Merge pull request #85331 from RobProductions/customize-navigation-controls

Add full customization of 3D navigation controls
This commit is contained in:
Rémi Verschelde 2024-08-28 00:10:19 +02:00
commit 109203d472
No known key found for this signature in database
GPG Key ID: C3336907360768E1
7 changed files with 287 additions and 80 deletions

View File

@ -324,7 +324,6 @@
<member name="editors/3d/navigation/emulate_3_button_mouse" type="bool" setter="" getter="">
If [code]true[/code], enables 3-button mouse emulation mode. This is useful on laptops when using a trackpad.
When 3-button mouse emulation mode is enabled, the pan, zoom and orbit modifiers can always be used in the 3D editor viewport, even when not holding down any mouse button.
[b]Note:[/b] No matter the orbit modifier configured in [member editors/3d/navigation/orbit_modifier], [kbd]Alt[/kbd] will always remain usable for orbiting in this mode to improve usability with graphics tablets.
</member>
<member name="editors/3d/navigation/emulate_numpad" type="bool" setter="" getter="">
If [code]true[/code], allows using the top row [kbd]0[/kbd]-[kbd]9[/kbd] keys to function as their equivalent numpad keys for 3D editor navigation. This should be enabled on keyboards that have no numeric keypad available.
@ -336,28 +335,25 @@
If [code]true[/code], invert the vertical mouse axis when panning, orbiting, or using freelook mode in the 3D editor.
</member>
<member name="editors/3d/navigation/navigation_scheme" type="int" setter="" getter="">
The navigation scheme to use in the 3D editor. Changing this setting will affect the mouse buttons that must be held down to perform certain operations in the 3D editor viewport.
- [b]Godot[/b] Middle mouse button to orbit, [kbd]Shift + Middle mouse button[/kbd] to pan. [kbd]Mouse wheel[/kbd] to zoom.
- [b]Maya:[/b] [kbd]Alt + Left mouse button[/kbd] to orbit. [kbd]Middle mouse button[/kbd] to pan, [kbd]Shift + Middle mouse button[/kbd] to pan 10 times faster. [kbd]Mouse wheel[/kbd] to zoom.
The navigation scheme preset to use in the 3D editor. Changing this setting will affect the mouse button and modifier controls used to navigate the 3D editor viewport.
All schemes can use [kbd]Mouse wheel[/kbd] to zoom.
- [b]Godot:[/b] [kbd]Middle mouse button[/kbd] to orbit. [kbd]Shift + Middle mouse button[/kbd] to pan. [kbd]Ctrl + Shift + Middle mouse button[/kbd] to zoom.
- [b]Maya:[/b] [kbd]Alt + Left mouse button[/kbd] to orbit. [kbd]Middle mouse button[/kbd] to pan, [kbd]Shift + Middle mouse button[/kbd] to pan 10 times faster. [kbd]Alt + Right mouse button[/kbd] to zoom.
- [b]Modo:[/b] [kbd]Alt + Left mouse button[/kbd] to orbit. [kbd]Alt + Shift + Left mouse button[/kbd] to pan. [kbd]Ctrl + Alt + Left mouse button[/kbd] to zoom.
See also [member editors/3d/freelook/freelook_navigation_scheme].
See also [member editors/3d/navigation/orbit_mouse_button], [member editors/3d/navigation/pan_mouse_button], [member editors/3d/navigation/zoom_mouse_button], and [member editors/3d/freelook/freelook_navigation_scheme].
[b]Note:[/b] On certain window managers on Linux, the [kbd]Alt[/kbd] key will be intercepted by the window manager when clicking a mouse button at the same time. This means Godot will not see the modifier key as being pressed.
</member>
<member name="editors/3d/navigation/orbit_modifier" type="int" setter="" getter="">
The modifier key that must be held to orbit in the 3D editor.
[b]Note:[/b] If [member editors/3d/navigation/emulate_3_button_mouse] is [code]true[/code], [kbd]Alt[/kbd] will always remain usable for orbiting to improve usability with graphics tablets.
[b]Note:[/b] On certain window managers on Linux, the [kbd]Alt[/kbd] key will be intercepted by the window manager when clicking a mouse button at the same time. This means Godot will not see the modifier key as being pressed.
<member name="editors/3d/navigation/orbit_mouse_button" type="int" setter="" getter="">
The mouse button that needs to be held down to orbit in the 3D editor viewport.
</member>
<member name="editors/3d/navigation/pan_modifier" type="int" setter="" getter="">
The modifier key that must be held to pan in the 3D editor.
[b]Note:[/b] On certain window managers on Linux, the [kbd]Alt[/kbd] key will be intercepted by the window manager when clicking a mouse button at the same time. This means Godot will not see the modifier key as being pressed.
<member name="editors/3d/navigation/pan_mouse_button" type="int" setter="" getter="">
The mouse button that needs to be held down to pan in the 3D editor viewport.
</member>
<member name="editors/3d/navigation/warped_mouse_panning" type="bool" setter="" getter="">
If [code]true[/code], warps the mouse around the 3D viewport while panning in the 3D editor. This makes it possible to pan over a large area without having to exit panning and adjust the mouse cursor.
</member>
<member name="editors/3d/navigation/zoom_modifier" type="int" setter="" getter="">
The modifier key that must be held to zoom in the 3D editor.
[b]Note:[/b] On certain window managers on Linux, the [kbd]Alt[/kbd] key will be intercepted by the window manager when clicking a mouse button at the same time. This means Godot will not see the modifier key as being pressed.
<member name="editors/3d/navigation/zoom_mouse_button" type="int" setter="" getter="">
The mouse button that needs to be held down to zoom in the 3D editor viewport.
</member>
<member name="editors/3d/navigation/zoom_style" type="int" setter="" getter="">
The mouse cursor movement direction to use when zooming by moving the mouse. This does not affect zooming with the mouse wheel.

View File

@ -7897,6 +7897,9 @@ EditorNode::EditorNode() {
ED_SHORTCUT_AND_COMMAND("editor/editor_next", TTR("Open the next Editor"));
ED_SHORTCUT_AND_COMMAND("editor/editor_prev", TTR("Open the previous Editor"));
// Apply setting presets in case the editor_settings file is missing values.
EditorSettingsDialog::update_navigation_preset();
screenshot_timer = memnew(Timer);
screenshot_timer->set_one_shot(true);
screenshot_timer->set_wait_time(settings_menu->get_submenu_popup_delay() + 0.1f);

View File

@ -731,14 +731,14 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) {
// 3D: Navigation
_initial_set("editors/3d/navigation/invert_x_axis", false);
_initial_set("editors/3d/navigation/invert_y_axis", false);
EDITOR_SETTING(Variant::INT, PROPERTY_HINT_ENUM, "editors/3d/navigation/navigation_scheme", 0, "Godot,Maya,Modo")
EDITOR_SETTING(Variant::INT, PROPERTY_HINT_ENUM, "editors/3d/navigation/navigation_scheme", 0, "Godot,Maya,Modo,Custom")
EDITOR_SETTING(Variant::INT, PROPERTY_HINT_ENUM, "editors/3d/navigation/orbit_mouse_button", 1, "Left Mouse,Middle Mouse,Right Mouse")
EDITOR_SETTING(Variant::INT, PROPERTY_HINT_ENUM, "editors/3d/navigation/pan_mouse_button", 1, "Left Mouse,Middle Mouse,Right Mouse")
EDITOR_SETTING(Variant::INT, PROPERTY_HINT_ENUM, "editors/3d/navigation/zoom_mouse_button", 1, "Left Mouse,Middle Mouse,Right Mouse")
EDITOR_SETTING(Variant::INT, PROPERTY_HINT_ENUM, "editors/3d/navigation/zoom_style", 0, "Vertical,Horizontal")
_initial_set("editors/3d/navigation/emulate_numpad", false);
_initial_set("editors/3d/navigation/emulate_3_button_mouse", false);
EDITOR_SETTING(Variant::INT, PROPERTY_HINT_ENUM, "editors/3d/navigation/orbit_modifier", 0, "None,Shift,Alt,Meta,Ctrl")
EDITOR_SETTING(Variant::INT, PROPERTY_HINT_ENUM, "editors/3d/navigation/pan_modifier", 1, "None,Shift,Alt,Meta,Ctrl")
EDITOR_SETTING(Variant::INT, PROPERTY_HINT_ENUM, "editors/3d/navigation/zoom_modifier", 4, "None,Shift,Alt,Meta,Ctrl")
_initial_set("editors/3d/navigation/warped_mouse_panning", true);
// 3D: Navigation feel

View File

@ -42,6 +42,7 @@
#include "editor/editor_undo_redo_manager.h"
#include "editor/event_listener_line_edit.h"
#include "editor/input_event_configuration_dialog.h"
#include "editor/plugins/node_3d_editor_plugin.h"
#include "editor/themes/editor_scale.h"
#include "editor/themes/editor_theme_manager.h"
#include "scene/gui/panel_container.h"
@ -74,9 +75,84 @@ void EditorSettingsDialog::_settings_property_edited(const String &p_name) {
EditorSettings::get_singleton()->set_manually("text_editor/theme/color_theme", "Custom");
} else if (full_name.begins_with("editors/visual_editors/connection_colors") || full_name.begins_with("editors/visual_editors/category_colors")) {
EditorSettings::get_singleton()->set_manually("editors/visual_editors/color_theme", "Custom");
} else if (full_name == "editors/3d/navigation/orbit_mouse_button" || full_name == "editors/3d/navigation/pan_mouse_button" || full_name == "editors/3d/navigation/zoom_mouse_button") {
EditorSettings::get_singleton()->set_manually("editors/3d/navigation/navigation_scheme", (int)Node3DEditorViewport::NAVIGATION_CUSTOM);
} else if (full_name == "editors/3d/navigation/navigation_scheme") {
update_navigation_preset();
}
}
void EditorSettingsDialog::update_navigation_preset() {
Node3DEditorViewport::NavigationScheme nav_scheme = (Node3DEditorViewport::NavigationScheme)EDITOR_GET("editors/3d/navigation/navigation_scheme").operator int();
Node3DEditorViewport::ViewportNavMouseButton set_orbit_mouse_button = Node3DEditorViewport::NAVIGATION_LEFT_MOUSE;
Node3DEditorViewport::ViewportNavMouseButton set_pan_mouse_button = Node3DEditorViewport::NAVIGATION_LEFT_MOUSE;
Node3DEditorViewport::ViewportNavMouseButton set_zoom_mouse_button = Node3DEditorViewport::NAVIGATION_LEFT_MOUSE;
Ref<InputEventKey> orbit_mod_key_1;
Ref<InputEventKey> orbit_mod_key_2;
Ref<InputEventKey> pan_mod_key_1;
Ref<InputEventKey> pan_mod_key_2;
Ref<InputEventKey> zoom_mod_key_1;
Ref<InputEventKey> zoom_mod_key_2;
bool set_preset = false;
if (nav_scheme == Node3DEditorViewport::NAVIGATION_GODOT) {
set_preset = true;
set_orbit_mouse_button = Node3DEditorViewport::NAVIGATION_MIDDLE_MOUSE;
set_pan_mouse_button = Node3DEditorViewport::NAVIGATION_MIDDLE_MOUSE;
set_zoom_mouse_button = Node3DEditorViewport::NAVIGATION_MIDDLE_MOUSE;
orbit_mod_key_1 = InputEventKey::create_reference(Key::NONE);
orbit_mod_key_2 = InputEventKey::create_reference(Key::NONE);
pan_mod_key_1 = InputEventKey::create_reference(Key::SHIFT);
pan_mod_key_2 = InputEventKey::create_reference(Key::NONE);
zoom_mod_key_1 = InputEventKey::create_reference(Key::SHIFT);
zoom_mod_key_2 = InputEventKey::create_reference(Key::CTRL);
} else if (nav_scheme == Node3DEditorViewport::NAVIGATION_MAYA) {
set_preset = true;
set_orbit_mouse_button = Node3DEditorViewport::NAVIGATION_LEFT_MOUSE;
set_pan_mouse_button = Node3DEditorViewport::NAVIGATION_MIDDLE_MOUSE;
set_zoom_mouse_button = Node3DEditorViewport::NAVIGATION_RIGHT_MOUSE;
orbit_mod_key_1 = InputEventKey::create_reference(Key::ALT);
orbit_mod_key_2 = InputEventKey::create_reference(Key::NONE);
pan_mod_key_1 = InputEventKey::create_reference(Key::NONE);
pan_mod_key_2 = InputEventKey::create_reference(Key::NONE);
zoom_mod_key_1 = InputEventKey::create_reference(Key::ALT);
zoom_mod_key_2 = InputEventKey::create_reference(Key::NONE);
} else if (nav_scheme == Node3DEditorViewport::NAVIGATION_MODO) {
set_preset = true;
set_orbit_mouse_button = Node3DEditorViewport::NAVIGATION_LEFT_MOUSE;
set_pan_mouse_button = Node3DEditorViewport::NAVIGATION_LEFT_MOUSE;
set_zoom_mouse_button = Node3DEditorViewport::NAVIGATION_LEFT_MOUSE;
orbit_mod_key_1 = InputEventKey::create_reference(Key::ALT);
orbit_mod_key_2 = InputEventKey::create_reference(Key::NONE);
pan_mod_key_1 = InputEventKey::create_reference(Key::SHIFT);
pan_mod_key_2 = InputEventKey::create_reference(Key::ALT);
zoom_mod_key_1 = InputEventKey::create_reference(Key::ALT);
zoom_mod_key_2 = InputEventKey::create_reference(Key::CTRL);
}
// Set settings to the desired preset values.
if (set_preset) {
EditorSettings::get_singleton()->set_manually("editors/3d/navigation/orbit_mouse_button", (int)set_orbit_mouse_button);
EditorSettings::get_singleton()->set_manually("editors/3d/navigation/pan_mouse_button", (int)set_pan_mouse_button);
EditorSettings::get_singleton()->set_manually("editors/3d/navigation/zoom_mouse_button", (int)set_zoom_mouse_button);
_set_shortcut_input("spatial_editor/viewport_orbit_modifier_1", orbit_mod_key_1);
_set_shortcut_input("spatial_editor/viewport_orbit_modifier_2", orbit_mod_key_2);
_set_shortcut_input("spatial_editor/viewport_pan_modifier_1", pan_mod_key_1);
_set_shortcut_input("spatial_editor/viewport_pan_modifier_2", pan_mod_key_2);
_set_shortcut_input("spatial_editor/viewport_zoom_modifier_1", zoom_mod_key_1);
_set_shortcut_input("spatial_editor/viewport_zoom_modifier_2", zoom_mod_key_2);
}
}
void EditorSettingsDialog::_set_shortcut_input(const String &p_name, Ref<InputEventKey> &p_event) {
Array sc_events;
if (p_event->get_keycode() != Key::NONE) {
sc_events.push_back((Variant)p_event);
}
Ref<Shortcut> sc = EditorSettings::get_singleton()->get_shortcut(p_name);
sc->set_events(sc_events);
}
void EditorSettingsDialog::_settings_save() {
EditorSettings::get_singleton()->notify_changes();
EditorSettings::get_singleton()->save();
@ -97,6 +173,8 @@ void EditorSettingsDialog::popup_edit_settings() {
EditorSettings::get_singleton()->list_text_editor_themes(); // make sure we have an up to date list of themes
_update_dynamic_property_hints();
inspector->edit(EditorSettings::get_singleton());
inspector->get_inspector()->update_tree();
@ -160,6 +238,12 @@ void EditorSettingsDialog::_notification(int p_what) {
_update_shortcuts();
}
if (EditorSettings::get_singleton()->check_changed_settings_in_group("editors/3d/navigation")) {
// Shortcuts may have changed, so dynamic hint values must update.
_update_dynamic_property_hints();
inspector->get_inspector()->update_tree();
}
if (EditorSettings::get_singleton()->check_changed_settings_in_group("interface/editor/localize_settings")) {
inspector->update_category_list();
}
@ -256,6 +340,13 @@ void EditorSettingsDialog::_update_shortcut_events(const String &p_path, const A
undo_redo->add_do_method(this, "_settings_changed");
undo_redo->add_undo_method(this, "_settings_changed");
undo_redo->commit_action();
bool path_is_orbit_mod = p_path == "spatial_editor/viewport_orbit_modifier_1" || p_path == "spatial_editor/viewport_orbit_modifier_2";
bool path_is_pan_mod = p_path == "spatial_editor/viewport_pan_modifier_1" || p_path == "spatial_editor/viewport_pan_modifier_2";
bool path_is_zoom_mod = p_path == "spatial_editor/viewport_zoom_modifier_1" || p_path == "spatial_editor/viewport_zoom_modifier_2";
if (path_is_orbit_mod || path_is_pan_mod || path_is_zoom_mod) {
EditorSettings::get_singleton()->set_manually("editors/3d/navigation/navigation_scheme", (int)Node3DEditorViewport::NAVIGATION_CUSTOM);
}
}
Array EditorSettingsDialog::_event_list_to_array_helper(const List<Ref<InputEvent>> &p_events) {
@ -661,6 +752,40 @@ void EditorSettingsDialog::drop_data_fw(const Point2 &p_point, const Variant &p_
void EditorSettingsDialog::_tabs_tab_changed(int p_tab) {
_focus_current_search_box();
// When tab has switched, shortcuts may have changed.
_update_dynamic_property_hints();
inspector->get_inspector()->update_tree();
}
void EditorSettingsDialog::_update_dynamic_property_hints() {
// Calling add_property_hint overrides the existing hint.
EditorSettings *settings = EditorSettings::get_singleton();
settings->add_property_hint(_create_mouse_shortcut_property_info("editors/3d/navigation/orbit_mouse_button", "spatial_editor/viewport_orbit_modifier_1", "spatial_editor/viewport_orbit_modifier_2"));
settings->add_property_hint(_create_mouse_shortcut_property_info("editors/3d/navigation/pan_mouse_button", "spatial_editor/viewport_pan_modifier_1", "spatial_editor/viewport_pan_modifier_2"));
settings->add_property_hint(_create_mouse_shortcut_property_info("editors/3d/navigation/zoom_mouse_button", "spatial_editor/viewport_zoom_modifier_1", "spatial_editor/viewport_zoom_modifier_2"));
}
PropertyInfo EditorSettingsDialog::_create_mouse_shortcut_property_info(const String &p_property_name, const String &p_shortcut_1_name, const String &p_shortcut_2_name) {
String hint_string;
hint_string += _get_shortcut_button_string(p_shortcut_1_name) + _get_shortcut_button_string(p_shortcut_2_name);
hint_string += "Left Mouse,";
hint_string += _get_shortcut_button_string(p_shortcut_1_name) + _get_shortcut_button_string(p_shortcut_2_name);
hint_string += "Middle Mouse,";
hint_string += _get_shortcut_button_string(p_shortcut_1_name) + _get_shortcut_button_string(p_shortcut_2_name);
hint_string += "Right Mouse";
return PropertyInfo(Variant::INT, p_property_name, PROPERTY_HINT_ENUM, hint_string);
}
String EditorSettingsDialog::_get_shortcut_button_string(const String &p_shortcut_name) {
String button_string;
Ref<Shortcut> shortcut_ref = EditorSettings::get_singleton()->get_shortcut(p_shortcut_name);
Array events = shortcut_ref->get_events();
for (Ref<InputEvent> input_event : events) {
button_string += input_event->as_text() + " + ";
}
return button_string;
}
void EditorSettingsDialog::_focus_current_search_box() {

View File

@ -100,6 +100,10 @@ class EditorSettingsDialog : public AcceptDialog {
void _tabs_tab_changed(int p_tab);
void _focus_current_search_box();
void _update_dynamic_property_hints();
PropertyInfo _create_mouse_shortcut_property_info(const String &p_property_name, const String &p_shortcut_1_name, const String &p_shortcut_2_name);
String _get_shortcut_button_string(const String &p_shortcut_name);
void _filter_shortcuts(const String &p_filter);
void _filter_shortcuts_by_event(const Ref<InputEvent> &p_event);
bool _should_display_shortcut(const String &p_name, const Array &p_events) const;
@ -107,6 +111,7 @@ class EditorSettingsDialog : public AcceptDialog {
void _update_shortcuts();
void _shortcut_button_pressed(Object *p_item, int p_column, int p_idx, MouseButton p_button = MouseButton::LEFT);
void _shortcut_cell_double_clicked();
static void _set_shortcut_input(const String &p_name, Ref<InputEventKey> &p_event);
static void _undo_redo_callback(void *p_self, const String &p_name);
@ -124,6 +129,7 @@ protected:
public:
void popup_edit_settings();
static void update_navigation_preset();
EditorSettingsDialog();
~EditorSettingsDialog();

View File

@ -1692,6 +1692,10 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
if (b.is_valid()) {
emit_signal(SNAME("clicked"), this);
ViewportNavMouseButton orbit_mouse_preference = (ViewportNavMouseButton)EDITOR_GET("editors/3d/navigation/orbit_mouse_button").operator int();
ViewportNavMouseButton pan_mouse_preference = (ViewportNavMouseButton)EDITOR_GET("editors/3d/navigation/pan_mouse_button").operator int();
ViewportNavMouseButton zoom_mouse_preference = (ViewportNavMouseButton)EDITOR_GET("editors/3d/navigation/zoom_mouse_button").operator int();
const real_t zoom_factor = 1 + (ZOOM_FREELOOK_MULTIPLIER - 1) * b->get_factor();
switch (b->get_button_index()) {
case MouseButton::WHEEL_UP: {
@ -1709,8 +1713,6 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
}
} break;
case MouseButton::RIGHT: {
NavigationScheme nav_scheme = (NavigationScheme)EDITOR_GET("editors/3d/navigation/navigation_scheme").operator int();
if (b->is_pressed() && _edit.gizmo.is_valid()) {
//restore
_edit.gizmo->commit_handle(_edit.gizmo_handle, _edit.gizmo_handle_secondary, _edit.gizmo_initial_value, true);
@ -1718,11 +1720,15 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
}
if (_edit.mode == TRANSFORM_NONE && b->is_pressed()) {
if (b->is_alt_pressed()) {
if (nav_scheme == NAVIGATION_MAYA) {
break;
}
if (orbit_mouse_preference == NAVIGATION_RIGHT_MOUSE && _is_nav_modifier_pressed("spatial_editor/viewport_orbit_modifier_1") && _is_nav_modifier_pressed("spatial_editor/viewport_orbit_modifier_2")) {
break;
} else if (pan_mouse_preference == NAVIGATION_RIGHT_MOUSE && _is_nav_modifier_pressed("spatial_editor/viewport_pan_modifier_1") && _is_nav_modifier_pressed("spatial_editor/viewport_pan_modifier_2")) {
break;
} else if (zoom_mouse_preference == NAVIGATION_RIGHT_MOUSE && _is_nav_modifier_pressed("spatial_editor/viewport_zoom_modifier_1") && _is_nav_modifier_pressed("spatial_editor/viewport_zoom_modifier_2")) {
break;
}
if (b->is_alt_pressed()) {
_list_select(b);
return;
}
@ -1753,6 +1759,14 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
} break;
case MouseButton::MIDDLE: {
if (b->is_pressed() && _edit.mode != TRANSFORM_NONE) {
if (orbit_mouse_preference == NAVIGATION_MIDDLE_MOUSE && _is_nav_modifier_pressed("spatial_editor/viewport_orbit_modifier_1") && _is_nav_modifier_pressed("spatial_editor/viewport_orbit_modifier_2")) {
break;
} else if (pan_mouse_preference == NAVIGATION_MIDDLE_MOUSE && _is_nav_modifier_pressed("spatial_editor/viewport_pan_modifier_1") && _is_nav_modifier_pressed("spatial_editor/viewport_pan_modifier_2")) {
break;
} else if (zoom_mouse_preference == NAVIGATION_MIDDLE_MOUSE && _is_nav_modifier_pressed("spatial_editor/viewport_zoom_modifier_1") && _is_nav_modifier_pressed("spatial_editor/viewport_zoom_modifier_2")) {
break;
}
switch (_edit.plane) {
case TRANSFORM_VIEW: {
_edit.plane = TRANSFORM_X_AXIS;
@ -1791,8 +1805,11 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
commit_transform();
break; // just commit the edit, stop processing the event so we don't deselect the object
}
NavigationScheme nav_scheme = (NavigationScheme)EDITOR_GET("editors/3d/navigation/navigation_scheme").operator int();
if ((nav_scheme == NAVIGATION_MAYA || nav_scheme == NAVIGATION_MODO) && b->is_alt_pressed()) {
if (orbit_mouse_preference == NAVIGATION_LEFT_MOUSE && _is_nav_modifier_pressed("spatial_editor/viewport_orbit_modifier_1") && _is_nav_modifier_pressed("spatial_editor/viewport_orbit_modifier_2")) {
break;
} else if (pan_mouse_preference == NAVIGATION_LEFT_MOUSE && _is_nav_modifier_pressed("spatial_editor/viewport_pan_modifier_1") && _is_nav_modifier_pressed("spatial_editor/viewport_pan_modifier_2")) {
break;
} else if (zoom_mouse_preference == NAVIGATION_LEFT_MOUSE && _is_nav_modifier_pressed("spatial_editor/viewport_zoom_modifier_1") && _is_nav_modifier_pressed("spatial_editor/viewport_zoom_modifier_2")) {
break;
}
@ -1988,6 +2005,24 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
}
}
ViewportNavMouseButton orbit_mouse_preference = (ViewportNavMouseButton)EDITOR_GET("editors/3d/navigation/orbit_mouse_button").operator int();
ViewportNavMouseButton pan_mouse_preference = (ViewportNavMouseButton)EDITOR_GET("editors/3d/navigation/pan_mouse_button").operator int();
ViewportNavMouseButton zoom_mouse_preference = (ViewportNavMouseButton)EDITOR_GET("editors/3d/navigation/zoom_mouse_button").operator int();
bool orbit_mod_pressed = _is_nav_modifier_pressed("spatial_editor/viewport_orbit_modifier_1") && _is_nav_modifier_pressed("spatial_editor/viewport_orbit_modifier_2");
bool pan_mod_pressed = _is_nav_modifier_pressed("spatial_editor/viewport_pan_modifier_1") && _is_nav_modifier_pressed("spatial_editor/viewport_pan_modifier_2");
bool zoom_mod_pressed = _is_nav_modifier_pressed("spatial_editor/viewport_zoom_modifier_1") && _is_nav_modifier_pressed("spatial_editor/viewport_zoom_modifier_2");
int orbit_mod_input_count = _get_shortcut_input_count("spatial_editor/viewport_orbit_modifier_1") + _get_shortcut_input_count("spatial_editor/viewport_orbit_modifier_2");
int pan_mod_input_count = _get_shortcut_input_count("spatial_editor/viewport_pan_modifier_1") + _get_shortcut_input_count("spatial_editor/viewport_pan_modifier_2");
int zoom_mod_input_count = _get_shortcut_input_count("spatial_editor/viewport_zoom_modifier_1") + _get_shortcut_input_count("spatial_editor/viewport_zoom_modifier_2");
bool orbit_not_empty = !_is_shortcut_empty("spatial_editor/viewport_zoom_modifier_1") || !_is_shortcut_empty("spatial_editor/viewport_zoom_modifier_2");
bool pan_not_empty = !_is_shortcut_empty("spatial_editor/viewport_pan_modifier_1") || !_is_shortcut_empty("spatial_editor/viewport_pan_modifier_2");
bool zoom_not_empty = !_is_shortcut_empty("spatial_editor/viewport_orbit_modifier_1") || !_is_shortcut_empty("spatial_editor/viewport_orbit_modifier_2");
Vector<ShortcutCheckSet> shortcut_check_sets;
shortcut_check_sets.push_back(ShortcutCheckSet(orbit_mod_pressed, orbit_not_empty, orbit_mod_input_count, orbit_mouse_preference, NAVIGATION_ORBIT));
shortcut_check_sets.push_back(ShortcutCheckSet(pan_mod_pressed, pan_not_empty, pan_mod_input_count, pan_mouse_preference, NAVIGATION_PAN));
shortcut_check_sets.push_back(ShortcutCheckSet(zoom_mod_pressed, zoom_not_empty, zoom_mod_input_count, zoom_mouse_preference, NAVIGATION_ZOOM));
shortcut_check_sets.sort_custom<ShortcutCheckSetComparator>();
Ref<InputEventMouseMotion> m = p_event;
// Instant transforms process mouse motion in input() to handle wrapping.
@ -2032,7 +2067,6 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
_transform_gizmo_select(_edit.mouse_pos, true);
}
NavigationScheme nav_scheme = (NavigationScheme)EDITOR_GET("editors/3d/navigation/navigation_scheme").operator int();
NavigationMode nav_mode = NAVIGATION_NONE;
if (_edit.gizmo.is_valid()) {
@ -2042,14 +2076,9 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
set_message(n + ": " + String(v));
} else if (m->get_button_mask().has_flag(MouseButtonMask::LEFT)) {
if (nav_scheme == NAVIGATION_MAYA && m->is_alt_pressed()) {
nav_mode = NAVIGATION_ORBIT;
} else if (nav_scheme == NAVIGATION_MODO && m->is_alt_pressed() && m->is_shift_pressed()) {
nav_mode = NAVIGATION_PAN;
} else if (nav_scheme == NAVIGATION_MODO && m->is_alt_pressed() && m->is_command_or_control_pressed()) {
nav_mode = NAVIGATION_ZOOM;
} else if (nav_scheme == NAVIGATION_MODO && m->is_alt_pressed()) {
nav_mode = NAVIGATION_ORBIT;
NavigationMode change_nav_from_shortcut = _get_nav_mode_from_shortcut_check(NAVIGATION_LEFT_MOUSE, shortcut_check_sets, false);
if (change_nav_from_shortcut != NAVIGATION_NONE) {
nav_mode = change_nav_from_shortcut;
} else {
const bool movement_threshold_passed = _edit.original_mouse_pos.distance_to(_edit.mouse_pos) > 8 * EDSCALE;
@ -2080,8 +2109,9 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
update_transform(_get_key_modifier(m) == Key::SHIFT);
}
} else if (m->get_button_mask().has_flag(MouseButtonMask::RIGHT) || freelook_active) {
if (nav_scheme == NAVIGATION_MAYA && m->is_alt_pressed()) {
nav_mode = NAVIGATION_ZOOM;
NavigationMode change_nav_from_shortcut = _get_nav_mode_from_shortcut_check(NAVIGATION_RIGHT_MOUSE, shortcut_check_sets, false);
if (m->get_button_mask().has_flag(MouseButtonMask::RIGHT) && change_nav_from_shortcut != NAVIGATION_NONE) {
nav_mode = change_nav_from_shortcut;
} else if (freelook_active) {
nav_mode = NAVIGATION_LOOK;
} else if (orthogonal) {
@ -2089,34 +2119,16 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
}
} else if (m->get_button_mask().has_flag(MouseButtonMask::MIDDLE)) {
const Key mod = _get_key_modifier(m);
if (nav_scheme == NAVIGATION_GODOT) {
if (mod == _get_key_modifier_setting("editors/3d/navigation/pan_modifier")) {
nav_mode = NAVIGATION_PAN;
} else if (mod == _get_key_modifier_setting("editors/3d/navigation/zoom_modifier")) {
nav_mode = NAVIGATION_ZOOM;
} else if (mod == Key::ALT || mod == _get_key_modifier_setting("editors/3d/navigation/orbit_modifier")) {
// Always allow Alt as a modifier to better support graphic tablets.
nav_mode = NAVIGATION_ORBIT;
}
} else if (nav_scheme == NAVIGATION_MAYA) {
if (mod == _get_key_modifier_setting("editors/3d/navigation/pan_modifier")) {
nav_mode = NAVIGATION_PAN;
}
NavigationMode change_nav_from_shortcut = _get_nav_mode_from_shortcut_check(NAVIGATION_MIDDLE_MOUSE, shortcut_check_sets, false);
if (change_nav_from_shortcut != NAVIGATION_NONE) {
nav_mode = change_nav_from_shortcut;
}
} else if (EDITOR_GET("editors/3d/navigation/emulate_3_button_mouse")) {
// Handle trackpad (no external mouse) use case
const Key mod = _get_key_modifier(m);
if (mod != Key::NONE) {
if (mod == _get_key_modifier_setting("editors/3d/navigation/pan_modifier")) {
nav_mode = NAVIGATION_PAN;
} else if (mod == _get_key_modifier_setting("editors/3d/navigation/zoom_modifier")) {
nav_mode = NAVIGATION_ZOOM;
} else if (mod == Key::ALT || mod == _get_key_modifier_setting("editors/3d/navigation/orbit_modifier")) {
// Always allow Alt as a modifier to better support graphic tablets.
nav_mode = NAVIGATION_ORBIT;
}
NavigationMode change_nav_from_shortcut = _get_nav_mode_from_shortcut_check(NAVIGATION_LEFT_MOUSE, shortcut_check_sets, true);
if (change_nav_from_shortcut != NAVIGATION_NONE) {
nav_mode = change_nav_from_shortcut;
}
}
@ -2157,25 +2169,11 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
Ref<InputEventPanGesture> pan_gesture = p_event;
if (pan_gesture.is_valid()) {
NavigationScheme nav_scheme = (NavigationScheme)EDITOR_GET("editors/3d/navigation/navigation_scheme").operator int();
NavigationMode nav_mode = NAVIGATION_NONE;
if (nav_scheme == NAVIGATION_GODOT) {
const Key mod = _get_key_modifier(pan_gesture);
if (mod == _get_key_modifier_setting("editors/3d/navigation/pan_modifier")) {
nav_mode = NAVIGATION_PAN;
} else if (mod == _get_key_modifier_setting("editors/3d/navigation/zoom_modifier")) {
nav_mode = NAVIGATION_ZOOM;
} else if (mod == Key::ALT || mod == _get_key_modifier_setting("editors/3d/navigation/orbit_modifier")) {
// Always allow Alt as a modifier to better support graphic tablets.
nav_mode = NAVIGATION_ORBIT;
}
} else if (nav_scheme == NAVIGATION_MAYA) {
if (pan_gesture->is_alt_pressed()) {
nav_mode = NAVIGATION_PAN;
}
NavigationMode change_nav_from_shortcut = _get_nav_mode_from_shortcut_check(NAVIGATION_LEFT_MOUSE, shortcut_check_sets, true);
if (change_nav_from_shortcut != NAVIGATION_NONE) {
nav_mode = change_nav_from_shortcut;
}
switch (nav_mode) {
@ -2445,6 +2443,32 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
}
}
int Node3DEditorViewport::_get_shortcut_input_count(const String &p_name) {
Ref<Shortcut> check_shortcut = ED_GET_SHORTCUT(p_name);
ERR_FAIL_COND_V_MSG(check_shortcut.is_null(), 0, "The Shortcut was null, possible name mismatch.");
return check_shortcut->get_events().size();
}
Node3DEditorViewport::NavigationMode Node3DEditorViewport::_get_nav_mode_from_shortcut_check(ViewportNavMouseButton p_mouse_button, Vector<ShortcutCheckSet> p_shortcut_check_sets, bool p_use_not_empty) {
if (p_use_not_empty) {
for (const ShortcutCheckSet &shortcut_check_set : p_shortcut_check_sets) {
if (shortcut_check_set.mod_pressed && shortcut_check_set.shortcut_not_empty) {
return shortcut_check_set.result_nav_mode;
}
}
} else {
for (const ShortcutCheckSet &shortcut_check_set : p_shortcut_check_sets) {
if (shortcut_check_set.mouse_preference == p_mouse_button && shortcut_check_set.mod_pressed) {
return shortcut_check_set.result_nav_mode;
}
}
}
return NAVIGATION_NONE;
}
void Node3DEditorViewport::_nav_pan(Ref<InputEventWithModifiers> p_event, const Vector2 &p_relative) {
const NavigationScheme nav_scheme = (NavigationScheme)EDITOR_GET("editors/3d/navigation/navigation_scheme").operator int();
@ -2644,6 +2668,18 @@ void Node3DEditorViewport::scale_freelook_speed(real_t scale) {
surface->queue_redraw();
}
bool Node3DEditorViewport::_is_nav_modifier_pressed(const String &p_name) {
return _is_shortcut_empty(p_name) || Input::get_singleton()->is_action_pressed(p_name);
}
bool Node3DEditorViewport::_is_shortcut_empty(const String &p_name) {
Ref<Shortcut> check_shortcut = ED_GET_SHORTCUT(p_name);
ERR_FAIL_COND_V_MSG(check_shortcut.is_null(), true, "The Shortcut was null, possible name mismatch.");
return check_shortcut->get_events().is_empty();
}
Point2 Node3DEditorViewport::_get_warped_mouse_motion(const Ref<InputEventMouseMotion> &p_ev_mouse_motion) const {
Point2 relative;
if (bool(EDITOR_GET("editors/3d/navigation/warped_mouse_panning"))) {
@ -5374,6 +5410,14 @@ Node3DEditorViewport::Node3DEditorViewport(Node3DEditor *p_spatial_editor, int p
view_menu->get_popup()->set_item_tooltip(shadeless_idx, unsupported_tooltip);
}
// Registering with Key::NONE intentionally creates an empty Array.
register_shortcut_action("spatial_editor/viewport_orbit_modifier_1", TTR("Viewport Orbit Modifier 1"), Key::NONE);
register_shortcut_action("spatial_editor/viewport_orbit_modifier_2", TTR("Viewport Orbit Modifier 2"), Key::NONE);
register_shortcut_action("spatial_editor/viewport_pan_modifier_1", TTR("Viewport Pan Modifier 1"), Key::SHIFT);
register_shortcut_action("spatial_editor/viewport_pan_modifier_2", TTR("Viewport Pan Modifier 2"), Key::NONE);
register_shortcut_action("spatial_editor/viewport_zoom_modifier_1", TTR("Viewport Zoom Modifier 1"), Key::SHIFT);
register_shortcut_action("spatial_editor/viewport_zoom_modifier_2", TTR("Viewport Zoom Modifier 2"), Key::CTRL);
register_shortcut_action("spatial_editor/freelook_left", TTR("Freelook Left"), Key::A, true);
register_shortcut_action("spatial_editor/freelook_right", TTR("Freelook Right"), Key::D, true);
register_shortcut_action("spatial_editor/freelook_forward", TTR("Freelook Forward"), Key::W, true);

View File

@ -192,6 +192,7 @@ public:
NAVIGATION_GODOT,
NAVIGATION_MAYA,
NAVIGATION_MODO,
NAVIGATION_CUSTOM,
};
enum FreelookNavigationScheme {
@ -200,6 +201,12 @@ public:
FREELOOK_FULLY_AXIS_LOCKED,
};
enum ViewportNavMouseButton {
NAVIGATION_LEFT_MOUSE,
NAVIGATION_MIDDLE_MOUSE,
NAVIGATION_RIGHT_MOUSE,
};
private:
double cpu_time_history[FRAME_TIME_HISTORY];
int cpu_time_history_index;
@ -294,6 +301,10 @@ private:
void _nav_orbit(Ref<InputEventWithModifiers> p_event, const Vector2 &p_relative);
void _nav_look(Ref<InputEventWithModifiers> p_event, const Vector2 &p_relative);
bool _is_shortcut_empty(const String &p_name);
bool _is_nav_modifier_pressed(const String &p_name);
int _get_shortcut_input_count(const String &p_name);
float get_znear() const;
float get_zfar() const;
float get_fov() const;
@ -390,6 +401,28 @@ private:
void reset_fov();
void scale_cursor_distance(real_t scale);
struct ShortcutCheckSet {
bool mod_pressed = false;
bool shortcut_not_empty = true;
int input_count = 0;
ViewportNavMouseButton mouse_preference = NAVIGATION_LEFT_MOUSE;
NavigationMode result_nav_mode = NAVIGATION_NONE;
ShortcutCheckSet() {}
ShortcutCheckSet(bool p_mod_pressed, bool p_shortcut_not_empty, int p_input_count, const ViewportNavMouseButton &p_mouse_preference, const NavigationMode &p_result_nav_mode) :
mod_pressed(p_mod_pressed), shortcut_not_empty(p_shortcut_not_empty), input_count(p_input_count), mouse_preference(p_mouse_preference), result_nav_mode(p_result_nav_mode) {
}
};
struct ShortcutCheckSetComparator {
_FORCE_INLINE_ bool operator()(const ShortcutCheckSet &A, const ShortcutCheckSet &B) const {
return A.input_count > B.input_count;
}
};
NavigationMode _get_nav_mode_from_shortcut_check(ViewportNavMouseButton p_mouse_button, Vector<ShortcutCheckSet> p_shortcut_check_sets, bool p_use_not_empty);
void set_freelook_active(bool active_now);
void scale_freelook_speed(real_t scale);