diff --git a/core/input/input_event.cpp b/core/input/input_event.cpp index 1a9f93c37..9568aa6e4 100644 --- a/core/input/input_event.cpp +++ b/core/input/input_event.cpp @@ -31,6 +31,7 @@ #include "input_event.h" #include "core/input/input_map.h" +#include "core/input/shortcut.h" #include "core/os/keyboard.h" const int InputEvent::DEVICE_ID_TOUCH_MOUSE = -1; @@ -1067,8 +1068,9 @@ String InputEventScreenDrag::as_text() const { bool InputEventScreenDrag::accumulate(const Ref &p_event) { Ref drag = p_event; - if (drag.is_null()) + if (drag.is_null()) { return false; + } if (get_index() != drag->get_index()) { return false; @@ -1378,3 +1380,39 @@ InputEventMIDI::InputEventMIDI() { controller_number = 0; controller_value = 0; } + +/////////////////////////////////// + +void InputEventShortCut::set_shortcut(Ref p_shortcut) { + shortcut = p_shortcut; + emit_changed(); +} + +Ref InputEventShortCut::get_shortcut() { + return shortcut; +} + +void InputEventShortCut::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_shortcut", "shortcut"), &InputEventShortCut::set_shortcut); + ClassDB::bind_method(D_METHOD("get_shortcut"), &InputEventShortCut::get_shortcut); + + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "shortcut", PROPERTY_HINT_RESOURCE_TYPE, "ShortCut"), "set_shortcut", "get_shortcut"); +} + +String InputEventShortCut::as_text() const { + ERR_FAIL_COND_V(shortcut.is_null(), "None"); + + return vformat(RTR("Input Event with ShortCut=%s"), shortcut->get_as_text()); +} + +String InputEventShortCut::to_string() { + ERR_FAIL_COND_V(shortcut.is_null(), "None"); + + return vformat("InputEventShortCut: shortcut=%s", shortcut->get_as_text()); +} + +InputEventShortCut::InputEventShortCut() { +} + +InputEventShortCut::~InputEventShortCut() { +} \ No newline at end of file diff --git a/core/input/input_event.h b/core/input/input_event.h index 458fd6a47..94107e49c 100644 --- a/core/input/input_event.h +++ b/core/input/input_event.h @@ -40,6 +40,8 @@ * The events are pretty obvious. */ +class ShortCut; + enum ButtonList { BUTTON_LEFT = 1, BUTTON_RIGHT = 2, @@ -651,4 +653,23 @@ public: InputEventMIDI(); }; +class InputEventShortCut : public InputEvent { + GDCLASS(InputEventShortCut, InputEvent); + + Ref shortcut; + +protected: + static void _bind_methods(); + +public: + void set_shortcut(Ref p_shortcut); + Ref get_shortcut(); + + virtual String as_text() const; + virtual String to_string(); + + InputEventShortCut(); + ~InputEventShortCut(); +}; + #endif diff --git a/core/register_core_types.cpp b/core/register_core_types.cpp index 8eb9e389c..2092d12db 100644 --- a/core/register_core_types.cpp +++ b/core/register_core_types.cpp @@ -159,6 +159,7 @@ void register_core_types() { ClassDB::register_class(); ClassDB::register_class(); ClassDB::register_class(); + ClassDB::register_class(); ClassDB::register_class(); ClassDB::register_virtual_class(); diff --git a/doc/classes/InputEventShortCut.xml b/doc/classes/InputEventShortCut.xml new file mode 100644 index 000000000..aadb20295 --- /dev/null +++ b/doc/classes/InputEventShortCut.xml @@ -0,0 +1,16 @@ + + + + Represents a triggered keyboard [ShortCut]. + + + InputEventShortCut is a special event that can be received in [method Node._unhandled_key_input]. It is typically sent by the editor's Command Palette to trigger actions, but can also be sent manually using [method Viewport.push_input]. + + + + + + The [ShortCut] represented by this event. Its [method ShortCut.matches_event] method will always return [code]true[/code] for this event. + + + \ No newline at end of file diff --git a/doc/classes/Node.xml b/doc/classes/Node.xml index 708310dc8..3e14a7145 100644 --- a/doc/classes/Node.xml +++ b/doc/classes/Node.xml @@ -95,6 +95,17 @@ [b]Note:[/b] [method _ready] may be called only once for each node. After removing a node from the scene tree and adding it again, [code]_ready[/code] will not be called a second time. This can be bypassed by requesting another call with [method request_ready], which may be called anywhere before adding the node again. + + + + + Called when an [InputEventKey] or [InputEventShortcut] hasn't been consumed by [method _input] or any GUI [Control] item. The input event propagates up through the node tree until a node consumes it. + It is only called if shortcut processing is enabled, which is done automatically if this method is overridden, and can be toggled with [method set_process_shortcut_input]. + To consume the input event and stop it propagating further to other nodes, [method Viewport.set_input_as_handled] can be called. + This method can be used to handle shortcuts. + [b]Note:[/b] This method is only called if the node is present in the scene tree (i.e. if it's not orphan). + + @@ -113,6 +124,7 @@ Called when an [InputEventKey] hasn't been consumed by [method _input] or any GUI [Control] item. The input event propagates up through the node tree until a node consumes it. It is only called if unhandled key input processing is enabled, which is done automatically if this method is overridden, and can be toggled with [method set_process_unhandled_key_input]. To consume the input event and stop it propagating further to other nodes, [method SceneTree.set_input_as_handled] can be called. + This method can be used to handle Unicode character input with [kbd]Alt[/kbd], [kbd]Alt + Ctrl[/kbd], and [kbd]Alt + Shift[/kbd] modifiers, after shortcuts were handled. For gameplay input, this and [method _unhandled_input] are usually a better fit than [method _input] as they allow the GUI to intercept the events first. [b]Note:[/b] This method is only called if the node is present in the scene tree (i.e. if it's not an orphan). @@ -493,6 +505,12 @@ Returns [code]true[/code] if internal processing is enabled (see [method set_process_internal]). + + + + Returns [code]true[/code] if the node is processing shortcuts (see [method set_process_shortcut_input]). + + @@ -798,6 +816,13 @@ [b]Warning:[/b] Built-in Nodes rely on the internal processing for their own logic, so changing this value from your code may lead to unexpected behavior. Script access to this internal logic is provided for specific advanced uses, but is unsafe and not supported. + + + + + Enables shortcut processing. Enabled automatically if [method _shortcut_input] is overridden. Any calls to this before [method _ready] will be ignored. + + diff --git a/editor/editor_file_dialog.cpp b/editor/editor_file_dialog.cpp index cd78d370d..35c3f7063 100644 --- a/editor/editor_file_dialog.cpp +++ b/editor/editor_file_dialog.cpp @@ -109,7 +109,7 @@ void EditorFileDialog::_notification(int p_what) { } } } else if (p_what == NOTIFICATION_POPUP_HIDE) { - set_process_unhandled_input(false); + set_process_shortcut_input(false); } else if (p_what == EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED) { bool is_showing_hidden = EditorSettings::get_singleton()->get("filesystem/file_dialog/show_hidden_files"); @@ -146,7 +146,7 @@ void EditorFileDialog::_notification(int p_what) { } } -void EditorFileDialog::_unhandled_input(const Ref &p_event) { +void EditorFileDialog::_shortcut_input(const Ref &p_event) { Ref k = p_event; if (k.is_valid() && is_window_modal_on_top()) { @@ -345,7 +345,7 @@ void EditorFileDialog::_post_popup() { _update_favorites(); } - set_process_unhandled_input(true); + set_process_shortcut_input(true); } void EditorFileDialog::_thumbnail_result(const String &p_path, const Ref &p_preview, const Ref &p_small_preview, const Variant &p_udata) { @@ -1397,7 +1397,7 @@ EditorFileDialog::DisplayMode EditorFileDialog::get_display_mode() const { } void EditorFileDialog::_bind_methods() { - ClassDB::bind_method(D_METHOD("_unhandled_input"), &EditorFileDialog::_unhandled_input); + ClassDB::bind_method(D_METHOD("_shortcut_input"), &EditorFileDialog::_shortcut_input); ClassDB::bind_method(D_METHOD("_item_selected"), &EditorFileDialog::_item_selected); ClassDB::bind_method(D_METHOD("_multi_selected"), &EditorFileDialog::_multi_selected); diff --git a/editor/editor_file_dialog.h b/editor/editor_file_dialog.h index d425c80e8..f3a3745e5 100644 --- a/editor/editor_file_dialog.h +++ b/editor/editor_file_dialog.h @@ -208,7 +208,7 @@ private: void _thumbnail_done(const String &p_path, const Ref &p_preview, const Ref &p_small_preview, const Variant &p_udata); void _request_single_thumbnail(const String &p_path); - void _unhandled_input(const Ref &p_event); + void _shortcut_input(const Ref &p_event); bool _is_open_should_be_disabled(); diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp index e3119e982..c33c1fcaf 100644 --- a/editor/editor_inspector.cpp +++ b/editor/editor_inspector.cpp @@ -579,7 +579,7 @@ void EditorProperty::_gui_input(const Ref &p_event) { } } -void EditorProperty::_unhandled_key_input(const Ref &p_event) { +void EditorProperty::_shortcut_input(const Ref &p_event) { if (!selected) { return; } @@ -755,7 +755,7 @@ void EditorProperty::_bind_methods() { ClassDB::bind_method(D_METHOD("_gui_input"), &EditorProperty::_gui_input); ClassDB::bind_method(D_METHOD("_menu_option", "option"), &EditorProperty::_menu_option); - ClassDB::bind_method(D_METHOD("_unhandled_key_input"), &EditorProperty::_unhandled_key_input); + ClassDB::bind_method(D_METHOD("_shortcut_input"), &EditorProperty::_shortcut_input); ClassDB::bind_method(D_METHOD("_focusable_focused"), &EditorProperty::_focusable_focused); ClassDB::bind_method(D_METHOD("add_focusable", "control"), &EditorProperty::add_focusable); @@ -812,7 +812,7 @@ EditorProperty::EditorProperty() { bottom_editor = nullptr; menu = nullptr; - set_process_unhandled_key_input(true); + set_process_shortcut_input(true); } void EditorProperty::_update_popup() { diff --git a/editor/editor_inspector.h b/editor/editor_inspector.h index e4162a9d9..71e93d06c 100644 --- a/editor/editor_inspector.h +++ b/editor/editor_inspector.h @@ -137,7 +137,7 @@ protected: static void _bind_methods(); void _gui_input(const Ref &p_event); - void _unhandled_key_input(const Ref &p_event); + void _shortcut_input(const Ref &p_event); public: void emit_changed(const StringName &p_property, const Variant &p_value, const StringName &p_field = StringName(), bool p_changing = false); diff --git a/editor/plugins/animation_player_editor_plugin.cpp b/editor/plugins/animation_player_editor_plugin.cpp index 342f005fb..ff12ff8f2 100644 --- a/editor/plugins/animation_player_editor_plugin.cpp +++ b/editor/plugins/animation_player_editor_plugin.cpp @@ -30,40 +30,38 @@ #include "animation_player_editor_plugin.h" +#include "core/config/project_settings.h" +#include "core/input/input.h" #include "core/io/resource_loader.h" #include "core/io/resource_saver.h" -#include "core/input/input.h" #include "core/os/keyboard.h" -#include "core/config/project_settings.h" #include "editor/animation_track_editor.h" #include "editor/editor_scale.h" #include "editor/editor_settings.h" // For onion skinning. -#include "editor/plugins/canvas_item_editor_plugin.h" -#include "editor/plugins/spatial_editor_plugin.h" -#include "scene/main/viewport.h" -#include "servers/rendering_server.h" -#include "core/variant/array.h" -#include "core/object/class_db.h" -#include "core/math/color.h" +#include "core/containers/list.h" +#include "core/containers/rid_handle.h" #include "core/error/error_list.h" #include "core/error/error_macros.h" +#include "core/input/input_event.h" #include "core/io/image.h" -#include "core/containers/list.h" +#include "core/math/color.h" #include "core/math/math_funcs.h" #include "core/math/rect2.h" -#include "core/input/input_event.h" -#include "core/os/memory.h" +#include "core/object/class_db.h" #include "core/object/resource.h" -#include "core/containers/rid_handle.h" +#include "core/object/undo_redo.h" +#include "core/os/memory.h" #include "core/string/string_name.h" #include "core/typedefs.h" -#include "core/object/undo_redo.h" +#include "core/variant/array.h" #include "core/variant/variant.h" #include "editor/editor_data.h" #include "editor/editor_file_dialog.h" #include "editor/editor_inspector.h" #include "editor/editor_node.h" +#include "editor/plugins/canvas_item_editor_plugin.h" +#include "editor/plugins/spatial_editor_plugin.h" #include "editor/scene_tree_dock.h" #include "editor/scene_tree_editor.h" #include "scene/animation/animation_player.h" @@ -80,8 +78,10 @@ #include "scene/gui/tree.h" #include "scene/main/node.h" #include "scene/main/scene_tree.h" +#include "scene/main/viewport.h" #include "scene/resources/animation.h" #include "scene/resources/texture.h" +#include "servers/rendering_server.h" void AnimationPlayerEditor::_node_removed(Node *p_node) { if (player && player == p_node) { @@ -1296,7 +1296,7 @@ void AnimationPlayerEditor::_onion_skinning_menu(int p_option) { } } -void AnimationPlayerEditor::_unhandled_key_input(const Ref &p_ev) { +void AnimationPlayerEditor::_shortcut_input(const Ref &p_ev) { ERR_FAIL_COND(p_ev.is_null()); Ref k = p_ev; @@ -1305,18 +1305,23 @@ void AnimationPlayerEditor::_unhandled_key_input(const Ref &p_ev) { case KEY_A: { if (!k->get_shift()) { _play_bw_from_pressed(); + accept_event(); } else { _play_bw_pressed(); + accept_event(); } } break; case KEY_S: { _stop_pressed(); + accept_event(); } break; case KEY_D: { if (!k->get_shift()) { _play_from_pressed(); + accept_event(); } else { _play_pressed(); + accept_event(); } } break; } @@ -1582,7 +1587,7 @@ void AnimationPlayerEditor::_bind_methods() { ClassDB::bind_method(D_METHOD("_animation_key_editor_anim_len_changed"), &AnimationPlayerEditor::_animation_key_editor_anim_len_changed); ClassDB::bind_method(D_METHOD("_animation_duplicate"), &AnimationPlayerEditor::_animation_duplicate); ClassDB::bind_method(D_METHOD("_blend_editor_next_changed"), &AnimationPlayerEditor::_blend_editor_next_changed); - ClassDB::bind_method(D_METHOD("_unhandled_key_input"), &AnimationPlayerEditor::_unhandled_key_input); + ClassDB::bind_method(D_METHOD("_shortcut_input"), &AnimationPlayerEditor::_shortcut_input); ClassDB::bind_method(D_METHOD("_animation_tool_menu"), &AnimationPlayerEditor::_animation_tool_menu); ClassDB::bind_method(D_METHOD("_onion_skinning_menu"), &AnimationPlayerEditor::_onion_skinning_menu); @@ -1789,7 +1794,7 @@ AnimationPlayerEditor::AnimationPlayerEditor(EditorNode *p_editor, AnimationPlay last_active = false; timeline_position = 0; - set_process_unhandled_key_input(true); + set_process_shortcut_input(true); add_child(track_editor); track_editor->set_v_size_flags(SIZE_EXPAND_FILL); diff --git a/editor/plugins/animation_player_editor_plugin.h b/editor/plugins/animation_player_editor_plugin.h index 72816b9df..5dad60b6a 100644 --- a/editor/plugins/animation_player_editor_plugin.h +++ b/editor/plugins/animation_player_editor_plugin.h @@ -225,7 +225,7 @@ class AnimationPlayerEditor : public VBoxContainer { void _animation_key_editor_seek(float p_pos, bool p_drag); void _animation_key_editor_anim_len_changed(float p_len); - void _unhandled_key_input(const Ref &p_ev); + void _shortcut_input(const Ref &p_ev); void _animation_tool_menu(int p_option); void _onion_skinning_menu(int p_option); diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp index 8eb6a49ce..e227589f7 100644 --- a/editor/plugins/canvas_item_editor_plugin.cpp +++ b/editor/plugins/canvas_item_editor_plugin.cpp @@ -514,7 +514,7 @@ float CanvasItemEditor::snap_angle(float p_target, float p_start) const { return (((smart_snap_active || snap_rotation) ^ Input::get_singleton()->is_key_pressed(KEY_CONTROL)) && snap_rotation_step != 0) ? Math::stepify(p_target - snap_rotation_offset, snap_rotation_step) + snap_rotation_offset : p_target; } -void CanvasItemEditor::_unhandled_key_input(const Ref &p_ev) { +void CanvasItemEditor::_shortcut_input(const Ref &p_ev) { ERR_FAIL_COND(p_ev.is_null()); Ref k = p_ev; @@ -532,6 +532,7 @@ void CanvasItemEditor::_unhandled_key_input(const Ref &p_ev) { // Multiply the grid size grid_step_multiplier = MIN(grid_step_multiplier + 1, 12); viewport->update(); + accept_event(); } else if ((grid_snap_active || show_grid) && divide_grid_step_shortcut.is_valid() && divide_grid_step_shortcut->is_shortcut(p_ev)) { // Divide the grid size Point2 new_grid_step = grid_step * Math::pow(2.0, grid_step_multiplier - 1); @@ -539,6 +540,7 @@ void CanvasItemEditor::_unhandled_key_input(const Ref &p_ev) { grid_step_multiplier--; } viewport->update(); + accept_event(); } } } @@ -5001,7 +5003,7 @@ void CanvasItemEditor::_bind_methods() { ClassDB::bind_method("_get_editor_data", &CanvasItemEditor::_get_editor_data); ClassDB::bind_method("_button_tool_select", &CanvasItemEditor::_button_tool_select); ClassDB::bind_method("_keying_changed", &CanvasItemEditor::_keying_changed); - ClassDB::bind_method("_unhandled_key_input", &CanvasItemEditor::_unhandled_key_input); + ClassDB::bind_method("_shortcut_input", &CanvasItemEditor::_shortcut_input); ClassDB::bind_method("_draw_viewport", &CanvasItemEditor::_draw_viewport); ClassDB::bind_method("_gui_input_viewport", &CanvasItemEditor::_gui_input_viewport); ClassDB::bind_method("_snap_changed", &CanvasItemEditor::_snap_changed); @@ -5772,7 +5774,7 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) { ED_SHORTCUT("canvas_item_editor/zoom_800_percent", TTR("Zoom to 800%"), KEY_4); ED_SHORTCUT("canvas_item_editor/zoom_1600_percent", TTR("Zoom to 1600%"), KEY_5); - set_process_unhandled_key_input(true); + set_process_shortcut_input(true); // Update the menus' checkboxes call_deferred("set_state", get_state()); diff --git a/editor/plugins/canvas_item_editor_plugin.h b/editor/plugins/canvas_item_editor_plugin.h index a8206e552..8b3550118 100644 --- a/editor/plugins/canvas_item_editor_plugin.h +++ b/editor/plugins/canvas_item_editor_plugin.h @@ -488,7 +488,7 @@ private: void _keying_changed(); - void _unhandled_key_input(const Ref &p_ev); + void _shortcut_input(const Ref &p_ev); void _draw_text_at_position(Point2 p_position, String p_string, Margin p_side); void _draw_margin_at_position(int p_value, Point2 p_position, Margin p_side); diff --git a/editor/plugins/spatial_editor_plugin.cpp b/editor/plugins/spatial_editor_plugin.cpp index c73aab929..7cfe5aa9d 100644 --- a/editor/plugins/spatial_editor_plugin.cpp +++ b/editor/plugins/spatial_editor_plugin.cpp @@ -6703,7 +6703,7 @@ void SpatialEditor::snap_selected_nodes_to_floor() { } } -void SpatialEditor::_unhandled_key_input(Ref p_event) { +void SpatialEditor::_shortcut_input(Ref p_event) { ERR_FAIL_COND(p_event.is_null()); if (!is_visible_in_tree() || get_viewport()->gui_has_modal_stack()) { @@ -6711,6 +6711,7 @@ void SpatialEditor::_unhandled_key_input(Ref p_event) { } snap_key_enabled = Input::get_singleton()->is_key_pressed(KEY_CONTROL); + // Don't accept event here } void SpatialEditor::_notification(int p_what) { if (p_what == NOTIFICATION_READY) { @@ -7057,7 +7058,7 @@ void SpatialEditor::_register_all_gizmos() { } void SpatialEditor::_bind_methods() { - ClassDB::bind_method("_unhandled_key_input", &SpatialEditor::_unhandled_key_input); + ClassDB::bind_method("_shortcut_input", &SpatialEditor::_shortcut_input); ClassDB::bind_method("_node_removed", &SpatialEditor::_node_removed); ClassDB::bind_method("_menu_item_pressed", &SpatialEditor::_menu_item_pressed); ClassDB::bind_method("_menu_gizmo_toggled", &SpatialEditor::_menu_gizmo_toggled); @@ -7483,7 +7484,7 @@ SpatialEditor::SpatialEditor(EditorNode *p_editor) { selected = nullptr; - set_process_unhandled_key_input(true); + set_process_shortcut_input(true); add_to_group("_spatial_editor_group"); EDITOR_DEF("editors/3d/manipulator_gizmo_size", 80); diff --git a/editor/plugins/spatial_editor_plugin.h b/editor/plugins/spatial_editor_plugin.h index 2f248dee0..2db6583a3 100644 --- a/editor/plugins/spatial_editor_plugin.h +++ b/editor/plugins/spatial_editor_plugin.h @@ -739,7 +739,7 @@ private: protected: void _notification(int p_what); //void _gui_input(InputEvent p_event); - void _unhandled_key_input(Ref p_event); + void _shortcut_input(Ref p_event); static void _bind_methods(); diff --git a/editor/project_manager.cpp b/editor/project_manager.cpp index a27f55422..4019f37bc 100644 --- a/editor/project_manager.cpp +++ b/editor/project_manager.cpp @@ -1806,7 +1806,7 @@ void ProjectManager::_notification(int p_what) { #endif } break; case NOTIFICATION_VISIBILITY_CHANGED: { - set_process_unhandled_input(is_visible_in_tree()); + set_process_shortcut_input(is_visible_in_tree()); } break; case NOTIFICATION_WM_QUIT_REQUEST: { _dim_window(); @@ -1848,7 +1848,7 @@ void ProjectManager::_update_project_buttons() { erase_missing_btn->set_disabled(!_project_list->is_any_project_missing()); } -void ProjectManager::_unhandled_input(const Ref &p_ev) { +void ProjectManager::_shortcut_input(const Ref &p_ev) { Ref k = p_ev; if (k.is_valid()) { @@ -2378,7 +2378,7 @@ void ProjectManager::_bind_methods() { ClassDB::bind_method("_on_tab_changed", &ProjectManager::_on_tab_changed); ClassDB::bind_method("_on_projects_updated", &ProjectManager::_on_projects_updated); ClassDB::bind_method("_on_project_created", &ProjectManager::_on_project_created); - ClassDB::bind_method("_unhandled_input", &ProjectManager::_unhandled_input); + ClassDB::bind_method("_shortcut_input", &ProjectManager::_shortcut_input); ClassDB::bind_method("_install_project", &ProjectManager::_install_project); ClassDB::bind_method("_files_dropped", &ProjectManager::_files_dropped); ClassDB::bind_method("_confirm_update_settings", &ProjectManager::_confirm_update_settings); diff --git a/editor/project_manager.h b/editor/project_manager.h index 387515bb5..eeabba95d 100644 --- a/editor/project_manager.h +++ b/editor/project_manager.h @@ -127,7 +127,7 @@ class ProjectManager : public Control { void _install_project(const String &p_zip_path, const String &p_title); void _dim_window(); - void _unhandled_input(const Ref &p_ev); + void _shortcut_input(const Ref &p_ev); void _files_dropped(PoolStringArray p_files, int p_screen); void _scan_multiple_folders(PoolStringArray p_files); diff --git a/editor/project_settings_editor.cpp b/editor/project_settings_editor.cpp index b11cc9380..12ad9c7b4 100644 --- a/editor/project_settings_editor.cpp +++ b/editor/project_settings_editor.cpp @@ -122,7 +122,7 @@ static const char *_axis_names[JOY_AXIS_MAX * 2] = { "", " (Right Trigger, Sony R2, Xbox RT)" }; -void ProjectSettingsEditor::_unhandled_input(const Ref &p_event) { +void ProjectSettingsEditor::_shortcut_input(const Ref &p_event) { const Ref k = p_event; if (k.is_valid() && is_window_modal_on_top() && k->is_pressed()) { @@ -192,7 +192,7 @@ void ProjectSettingsEditor::_notification(int p_what) { case NOTIFICATION_POPUP_HIDE: { EditorSettings::get_singleton()->set_project_metadata("dialog_bounds", "project_settings", get_rect()); - set_process_unhandled_input(false); + set_process_shortcut_input(false); } break; case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: { search_button->set_icon(get_theme_icon("Search", "EditorIcons")); @@ -945,7 +945,7 @@ void ProjectSettingsEditor::popup_project_settings() { autoload_settings->update_autoload(); plugin_settings->update_plugins(); import_defaults_editor->clear(); - set_process_unhandled_input(true); + set_process_shortcut_input(true); } void ProjectSettingsEditor::update_plugins() { @@ -1822,7 +1822,7 @@ void ProjectSettingsEditor::_update_theme() { } void ProjectSettingsEditor::_bind_methods() { - ClassDB::bind_method(D_METHOD("_unhandled_input"), &ProjectSettingsEditor::_unhandled_input); + ClassDB::bind_method(D_METHOD("_shortcut_input"), &ProjectSettingsEditor::_shortcut_input); ClassDB::bind_method(D_METHOD("_item_selected"), &ProjectSettingsEditor::_item_selected); ClassDB::bind_method(D_METHOD("_item_add"), &ProjectSettingsEditor::_item_add); ClassDB::bind_method(D_METHOD("_item_adds"), &ProjectSettingsEditor::_item_adds); diff --git a/editor/project_settings_editor.h b/editor/project_settings_editor.h index 0766c1f29..0edca73bf 100644 --- a/editor/project_settings_editor.h +++ b/editor/project_settings_editor.h @@ -206,7 +206,7 @@ class ProjectSettingsEditor : public AcceptDialog { void _update_theme(); protected: - void _unhandled_input(const Ref &p_event); + void _shortcut_input(const Ref &p_event); void _notification(int p_what); static void _bind_methods(); diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp index d5400aa34..876ccf5cc 100644 --- a/editor/scene_tree_dock.cpp +++ b/editor/scene_tree_dock.cpp @@ -127,7 +127,7 @@ void SceneTreeDock::_input(Ref p_event) { } } -void SceneTreeDock::_unhandled_key_input(Ref p_event) { +void SceneTreeDock::_shortcut_input(Ref p_event) { ERR_FAIL_COND(p_event.is_null()); if (get_viewport()->get_modal_stack_top()) { @@ -186,7 +186,11 @@ void SceneTreeDock::_unhandled_key_input(Ref p_event) { _tool_selected(TOOL_TOGGLE_SCENE_UNIQUE_NAME); } else if (ED_IS_SHORTCUT("scene_tree/delete", p_event)) { _tool_selected(TOOL_ERASE); + } else { + return; } + + accept_event(); } void SceneTreeDock::instance(const String &p_file) { @@ -3355,7 +3359,7 @@ void SceneTreeDock::_bind_methods() { ClassDB::bind_method(D_METHOD("_script_creation_closed"), &SceneTreeDock::_script_creation_closed); ClassDB::bind_method(D_METHOD("_load_request"), &SceneTreeDock::_load_request); ClassDB::bind_method(D_METHOD("_script_open_request"), &SceneTreeDock::_script_open_request); - ClassDB::bind_method(D_METHOD("_unhandled_key_input"), &SceneTreeDock::_unhandled_key_input); + ClassDB::bind_method(D_METHOD("_shortcut_input"), &SceneTreeDock::_shortcut_input); ClassDB::bind_method(D_METHOD("_input"), &SceneTreeDock::_input); ClassDB::bind_method(D_METHOD("_nodes_drag_begin"), &SceneTreeDock::_nodes_drag_begin); ClassDB::bind_method(D_METHOD("_delete_confirm"), &SceneTreeDock::_delete_confirm); @@ -3579,7 +3583,7 @@ SceneTreeDock::SceneTreeDock(EditorNode *p_editor, Node *p_scene_root, EditorSel quick_open = memnew(EditorQuickOpen); add_child(quick_open); quick_open->connect("quick_open", this, "_quick_open"); - set_process_unhandled_key_input(true); + set_process_shortcut_input(true); delete_dialog = memnew(ConfirmationDialog); add_child(delete_dialog); diff --git a/editor/scene_tree_dock.h b/editor/scene_tree_dock.h index d52300fbe..4dc84bf80 100644 --- a/editor/scene_tree_dock.h +++ b/editor/scene_tree_dock.h @@ -240,7 +240,7 @@ class SceneTreeDock : public VBoxContainer { void _nodes_drag_begin(); void _input(Ref p_event); - void _unhandled_key_input(Ref p_event); + void _shortcut_input(Ref p_event); void _import_subscene(); diff --git a/editor/settings_config_dialog.cpp b/editor/settings_config_dialog.cpp index 0a5ffc803..26a3dde0c 100644 --- a/editor/settings_config_dialog.cpp +++ b/editor/settings_config_dialog.cpp @@ -123,7 +123,7 @@ void EditorSettingsDialog::popup_edit_settings() { search_box->grab_focus(); _update_shortcuts(); - set_process_unhandled_input(true); + set_process_shortcut_input(true); // Restore valid window bounds or pop up at default size. Rect2 saved_size = EditorSettings::get_singleton()->get_project_metadata("dialog_bounds", "editor_settings", Rect2()); @@ -160,7 +160,7 @@ void EditorSettingsDialog::_notification(int p_what) { } break; case NOTIFICATION_POPUP_HIDE: { EditorSettings::get_singleton()->set_project_metadata("dialog_bounds", "editor_settings", get_rect()); - set_process_unhandled_input(false); + set_process_shortcut_input(false); } break; case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: { _update_icons(); @@ -171,7 +171,7 @@ void EditorSettingsDialog::_notification(int p_what) { } } -void EditorSettingsDialog::_unhandled_input(const Ref &p_event) { +void EditorSettingsDialog::_shortcut_input(const Ref &p_event) { const Ref k = p_event; if (k.is_valid() && is_window_modal_on_top() && k->is_pressed()) { @@ -415,7 +415,7 @@ void EditorSettingsDialog::_editor_restart_close() { } void EditorSettingsDialog::_bind_methods() { - ClassDB::bind_method(D_METHOD("_unhandled_input"), &EditorSettingsDialog::_unhandled_input); + ClassDB::bind_method(D_METHOD("_shortcut_input"), &EditorSettingsDialog::_shortcut_input); ClassDB::bind_method(D_METHOD("_settings_save"), &EditorSettingsDialog::_settings_save); ClassDB::bind_method(D_METHOD("_settings_changed"), &EditorSettingsDialog::_settings_changed); ClassDB::bind_method(D_METHOD("_settings_property_edited"), &EditorSettingsDialog::_settings_property_edited); diff --git a/editor/settings_config_dialog.h b/editor/settings_config_dialog.h index e221e5eb8..02343042d 100644 --- a/editor/settings_config_dialog.h +++ b/editor/settings_config_dialog.h @@ -81,7 +81,7 @@ class EditorSettingsDialog : public AcceptDialog { void _settings_property_edited(const String &p_name); void _settings_save(); - void _unhandled_input(const Ref &p_event); + void _shortcut_input(const Ref &p_event); void _notification(int p_what); void _update_icons(); diff --git a/editor_modules/editor_code_editor/editor_code_text_editor.cpp b/editor_modules/editor_code_editor/editor_code_text_editor.cpp index 253ef3b53..80d90b583 100644 --- a/editor_modules/editor_code_editor/editor_code_text_editor.cpp +++ b/editor_modules/editor_code_editor/editor_code_text_editor.cpp @@ -71,7 +71,7 @@ // This function should be used to handle shortcuts that could otherwise // be handled too late if they weren't handled here. -void EditorCodeTextEditor::_input(const Ref &event) { +void EditorCodeTextEditor::_shortcut_input(const Ref &event) { const Ref key_event = event; if (!key_event.is_valid() || !key_event->is_pressed()) { return; @@ -964,7 +964,7 @@ void EditorCodeTextEditor::_notification(int p_what) { if (toggle_scripts_button->is_visible()) { update_toggle_scripts_button(); } - set_process_input(is_visible_in_tree()); + set_process_shortcut_input(is_visible_in_tree()); } break; default: break; @@ -1045,7 +1045,7 @@ void EditorCodeTextEditor::remove_all_bookmarks() { } void EditorCodeTextEditor::_bind_methods() { - ClassDB::bind_method(D_METHOD("_input"), &EditorCodeTextEditor::_input); + ClassDB::bind_method(D_METHOD("_shortcut_input"), &EditorCodeTextEditor::_shortcut_input); ClassDB::bind_method("_text_editor_gui_input", &EditorCodeTextEditor::_text_editor_gui_input); ClassDB::bind_method("_line_col_changed", &EditorCodeTextEditor::_line_col_changed); ClassDB::bind_method("_text_changed", &EditorCodeTextEditor::_text_changed); diff --git a/editor_modules/editor_code_editor/editor_code_text_editor.h b/editor_modules/editor_code_editor/editor_code_text_editor.h index 8e4b9502c..3e5fca400 100644 --- a/editor_modules/editor_code_editor/editor_code_text_editor.h +++ b/editor_modules/editor_code_editor/editor_code_text_editor.h @@ -90,7 +90,7 @@ class EditorCodeTextEditor : public VBoxContainer { void _font_resize_timeout(); bool _add_font_size(int p_delta); - void _input(const Ref &event); + void _shortcut_input(const Ref &event); void _text_editor_gui_input(const Ref &p_event); void _zoom_in(); void _zoom_out(); diff --git a/editor_modules/editor_code_editor/editor_script_editor.cpp b/editor_modules/editor_code_editor/editor_script_editor.cpp index bf19c8d50..33c7b8d09 100644 --- a/editor_modules/editor_code_editor/editor_script_editor.cpp +++ b/editor_modules/editor_code_editor/editor_script_editor.cpp @@ -2593,7 +2593,7 @@ void EditorScriptEditor::_input(const Ref &p_event) { } } -void EditorScriptEditor::_unhandled_input(const Ref &p_event) { +void EditorScriptEditor::_shortcut_input(const Ref &p_event) { ERR_FAIL_COND(p_event.is_null()); if (!is_visible_in_tree() || !p_event->is_pressed() || p_event->is_echo()) { @@ -2606,6 +2606,7 @@ void EditorScriptEditor::_unhandled_input(const Ref &p_event) { _go_to_tab(script_list->get_item_metadata(next_tab)); _update_script_names(); } + accept_event(); } if (ED_IS_SHORTCUT("script_editor/prev_script", p_event)) { if (script_list->get_item_count() > 1) { @@ -2614,12 +2615,15 @@ void EditorScriptEditor::_unhandled_input(const Ref &p_event) { _go_to_tab(script_list->get_item_metadata(next_tab)); _update_script_names(); } + accept_event(); } if (ED_IS_SHORTCUT("script_editor/window_move_up", p_event)) { _menu_option(WINDOW_MOVE_UP); + accept_event(); } if (ED_IS_SHORTCUT("script_editor/window_move_down", p_event)) { _menu_option(WINDOW_MOVE_DOWN); + accept_event(); } } @@ -3165,7 +3169,7 @@ void EditorScriptEditor::_bind_methods() { ClassDB::bind_method("_history_back", &EditorScriptEditor::_history_back); ClassDB::bind_method("_live_auto_reload_running_scripts", &EditorScriptEditor::_live_auto_reload_running_scripts); ClassDB::bind_method("_input", &EditorScriptEditor::_input); - ClassDB::bind_method("_unhandled_input", &EditorScriptEditor::_unhandled_input); + ClassDB::bind_method("_shortcut_input", &EditorScriptEditor::_shortcut_input); ClassDB::bind_method("_script_list_gui_input", &EditorScriptEditor::_script_list_gui_input); ClassDB::bind_method("_toggle_members_overview_alpha_sort", &EditorScriptEditor::_toggle_members_overview_alpha_sort); ClassDB::bind_method("_update_members_overview", &EditorScriptEditor::_update_members_overview); @@ -3301,7 +3305,7 @@ EditorScriptEditor::EditorScriptEditor(EditorNode *p_editor) { ED_SHORTCUT("script_editor/next_script", TTR("Next Script"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_PERIOD); ED_SHORTCUT("script_editor/prev_script", TTR("Previous Script"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_COMMA); set_process_input(true); - set_process_unhandled_input(true); + set_process_shortcut_input(true); file_menu = memnew(MenuButton); menu_hb->add_child(file_menu); diff --git a/editor_modules/editor_code_editor/editor_script_editor.h b/editor_modules/editor_code_editor/editor_script_editor.h index c368a53f8..35cc451be 100644 --- a/editor_modules/editor_code_editor/editor_script_editor.h +++ b/editor_modules/editor_code_editor/editor_script_editor.h @@ -328,7 +328,7 @@ class EditorScriptEditor : public PanelContainer { void drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from); void _input(const Ref &p_event); - void _unhandled_input(const Ref &p_event); + void _shortcut_input(const Ref &p_event); void _script_list_gui_input(const Ref &ev); void _make_script_list_context_menu(); diff --git a/modules/ui_extensions/touch_button.cpp b/modules/ui_extensions/touch_button.cpp index fe9961d86..38c7a955b 100644 --- a/modules/ui_extensions/touch_button.cpp +++ b/modules/ui_extensions/touch_button.cpp @@ -98,13 +98,13 @@ void TouchButton::_gui_input(Ref p_event) { BaseButton::_gui_input(p_event); } -void TouchButton::_unhandled_input(Ref p_event) { +void TouchButton::_shortcut_input(Ref p_event) { if (p_event->get_device() == -1) { //accept_event(); return; } - Button::_unhandled_input(p_event); + Button::_shortcut_input(p_event); } void TouchButton::_bind_methods() { diff --git a/modules/ui_extensions/touch_button.h b/modules/ui_extensions/touch_button.h index 2d6e500a6..cebec9032 100644 --- a/modules/ui_extensions/touch_button.h +++ b/modules/ui_extensions/touch_button.h @@ -39,7 +39,7 @@ public: protected: void _notification(int p_what); virtual void _gui_input(Ref p_event); - virtual void _unhandled_input(Ref p_event); + virtual void _shortcut_input(Ref p_event); void _input(Ref p_event); static void _bind_methods(); }; diff --git a/scene/gui/base_button.cpp b/scene/gui/base_button.cpp index 4912b40db..5bf0e3ad3 100644 --- a/scene/gui/base_button.cpp +++ b/scene/gui/base_button.cpp @@ -342,14 +342,14 @@ bool BaseButton::is_keep_pressed_outside() const { void BaseButton::set_shortcut(const Ref &p_shortcut) { shortcut = p_shortcut; - set_process_unhandled_input(shortcut.is_valid()); + set_process_shortcut_input(shortcut.is_valid()); } Ref BaseButton::get_shortcut() const { return shortcut; } -void BaseButton::_unhandled_input(Ref p_event) { +void BaseButton::_shortcut_input(Ref p_event) { ERR_FAIL_COND(p_event.is_null()); if (!is_disabled() && is_visible_in_tree() && !p_event->is_echo() && shortcut.is_valid() && shortcut->is_shortcut(p_event)) { @@ -398,7 +398,7 @@ bool BaseButton::_was_pressed_by_mouse() const { void BaseButton::_bind_methods() { ClassDB::bind_method(D_METHOD("_gui_input"), &BaseButton::_gui_input); - ClassDB::bind_method(D_METHOD("_unhandled_input"), &BaseButton::_unhandled_input); + ClassDB::bind_method(D_METHOD("_shortcut_input"), &BaseButton::_shortcut_input); ClassDB::bind_method(D_METHOD("set_pressed", "pressed"), &BaseButton::set_pressed); ClassDB::bind_method(D_METHOD("is_pressed"), &BaseButton::is_pressed); ClassDB::bind_method(D_METHOD("set_pressed_no_signal", "pressed"), &BaseButton::set_pressed_no_signal); diff --git a/scene/gui/base_button.h b/scene/gui/base_button.h index 1f097e1b5..b31fe9641 100644 --- a/scene/gui/base_button.h +++ b/scene/gui/base_button.h @@ -77,7 +77,7 @@ protected: virtual void toggled(bool p_pressed); static void _bind_methods(); virtual void _gui_input(Ref p_event); - virtual void _unhandled_input(Ref p_event); + virtual void _shortcut_input(Ref p_event); void _notification(int p_what); bool _was_pressed_by_mouse() const; diff --git a/scene/gui/file_dialog.cpp b/scene/gui/file_dialog.cpp index f762709b0..205f8ea2c 100644 --- a/scene/gui/file_dialog.cpp +++ b/scene/gui/file_dialog.cpp @@ -82,11 +82,11 @@ void FileDialog::_notification(int p_what) { show_hidden->add_theme_color_override("icon_color_pressed", font_color_pressed); } else if (p_what == NOTIFICATION_POPUP_HIDE) { - set_process_unhandled_input(false); + set_process_shortcut_input(false); } } -void FileDialog::_unhandled_input(const Ref &p_event) { +void FileDialog::_shortcut_input(const Ref &p_event) { Ref k = p_event; if (k.is_valid() && is_window_modal_on_top()) { if (k->is_pressed()) { @@ -181,7 +181,7 @@ void FileDialog::_post_popup() { tree->grab_focus(); } - set_process_unhandled_input(true); + set_process_shortcut_input(true); // For open dir mode, deselect all items on file dialog open. if (mode == MODE_OPEN_DIR) { @@ -803,7 +803,7 @@ void FileDialog::_update_drives(bool p_select) { bool FileDialog::default_show_hidden_files = false; void FileDialog::_bind_methods() { - ClassDB::bind_method(D_METHOD("_unhandled_input"), &FileDialog::_unhandled_input); + ClassDB::bind_method(D_METHOD("_shortcut_input"), &FileDialog::_shortcut_input); ClassDB::bind_method(D_METHOD("_tree_multi_selected"), &FileDialog::_tree_multi_selected); ClassDB::bind_method(D_METHOD("_tree_selected"), &FileDialog::_tree_selected); diff --git a/scene/gui/file_dialog.h b/scene/gui/file_dialog.h index 4e5726b86..9ed76cf24 100644 --- a/scene/gui/file_dialog.h +++ b/scene/gui/file_dialog.h @@ -125,7 +125,7 @@ private: void _update_drives(bool p_select = true); - void _unhandled_input(const Ref &p_event); + void _shortcut_input(const Ref &p_event); bool _is_open_should_be_disabled(); diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp index b9fea6ce0..fbc7c849f 100644 --- a/scene/gui/line_edit.cpp +++ b/scene/gui/line_edit.cpp @@ -51,6 +51,27 @@ bool LineEdit::_is_text_char(CharType c) { return !is_symbol(c); } +void LineEdit::_unhandled_key_input(const Ref &p_event) { + Ref k = p_event; + + if (k.is_valid()) { + if (!k->is_pressed()) { + return; + } + // Handle Unicode (with modifiers active, process after shortcuts). + if (has_focus() && editable && (k->get_unicode() >= 32)) { + selection_delete(); + char32_t ucodestr[2] = { (char32_t)k->get_unicode(), 0 }; + int prev_len = text.length(); + append_at_cursor(ucodestr); + if (text.length() != prev_len) { + _text_changed(); + } + accept_event(); + } + } +} + void LineEdit::_gui_input(Ref p_event) { Ref b = p_event; @@ -1951,6 +1972,7 @@ void LineEdit::_bind_methods() { ClassDB::bind_method(D_METHOD("get_align"), &LineEdit::get_align); ClassDB::bind_method(D_METHOD("_gui_input"), &LineEdit::_gui_input); + ClassDB::bind_method(D_METHOD("_unhandled_key_input"), &LineEdit::_unhandled_key_input); ClassDB::bind_method(D_METHOD("clear"), &LineEdit::clear); ClassDB::bind_method(D_METHOD("select", "from", "to"), &LineEdit::select, DEFVAL(0), DEFVAL(-1)); ClassDB::bind_method(D_METHOD("select_all"), &LineEdit::select_all); @@ -2086,6 +2108,7 @@ LineEdit::LineEdit() { set_focus_mode(FOCUS_ALL); set_default_cursor_shape(CURSOR_IBEAM); set_mouse_filter(MOUSE_FILTER_STOP); + set_process_unhandled_key_input(true); draw_caret = true; caret_blink_enabled = false; diff --git a/scene/gui/line_edit.h b/scene/gui/line_edit.h index 4e1d5c737..278293e79 100644 --- a/scene/gui/line_edit.h +++ b/scene/gui/line_edit.h @@ -175,6 +175,7 @@ private: void _editor_settings_changed(); + void _unhandled_key_input(const Ref &p_event); void _gui_input(Ref p_event); void _notification(int p_what); diff --git a/scene/gui/menu_button.cpp b/scene/gui/menu_button.cpp index e621d03bc..addd27043 100644 --- a/scene/gui/menu_button.cpp +++ b/scene/gui/menu_button.cpp @@ -35,13 +35,15 @@ #include "core/input/shortcut.h" #include "scene/main/viewport.h" -void MenuButton::_unhandled_key_input(Ref p_event) { +void MenuButton::_shortcut_input(Ref p_event) { ERR_FAIL_COND(p_event.is_null()); if (disable_shortcuts) { return; } + BaseButton::_shortcut_input(p_event); + if (p_event->is_pressed() && !p_event->is_echo() && (Object::cast_to(p_event.ptr()) || Object::cast_to(p_event.ptr()) || Object::cast_to(*p_event))) { if (!get_parent() || !is_visible_in_tree() || is_disabled()) { return; @@ -112,7 +114,6 @@ void MenuButton::_notification(int p_what) { void MenuButton::_bind_methods() { ClassDB::bind_method(D_METHOD("get_popup"), &MenuButton::get_popup); - ClassDB::bind_method(D_METHOD("_unhandled_key_input"), &MenuButton::_unhandled_key_input); ClassDB::bind_method(D_METHOD("_set_items"), &MenuButton::_set_items); ClassDB::bind_method(D_METHOD("_get_items"), &MenuButton::_get_items); ClassDB::bind_method(D_METHOD("set_switch_on_hover", "enable"), &MenuButton::set_switch_on_hover); @@ -135,7 +136,7 @@ MenuButton::MenuButton() { set_toggle_mode(true); set_disable_shortcuts(false); set_focus_mode(FOCUS_NONE); - set_process_unhandled_key_input(true); + set_process_shortcut_input(true); set_action_mode(ACTION_MODE_BUTTON_PRESS); popup = memnew(PopupMenu); diff --git a/scene/gui/menu_button.h b/scene/gui/menu_button.h index fc7a2608a..007491b82 100644 --- a/scene/gui/menu_button.h +++ b/scene/gui/menu_button.h @@ -42,7 +42,7 @@ class MenuButton : public Button { bool disable_shortcuts; PopupMenu *popup; - void _unhandled_key_input(Ref p_event); + void _shortcut_input(Ref p_event); Array _get_items() const; void _set_items(const Array &p_items); diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index ab0dd3726..ee496b4df 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -2300,6 +2300,45 @@ void TextEdit::_get_minimap_mouse_row(const Point2i &p_mouse, int &r_row) const r_row = row; } +void TextEdit::_unhandled_key_input(const Ref &p_event) { + Ref k = p_event; + + if (k.is_valid()) { + if (!k->is_pressed()) { + return; + } + // Handle Unicode (with modifiers active, process after shortcuts). + if (has_focus() && !readonly && (k->get_unicode() >= 32)) { + //handle_unicode_input(k->get_unicode()); + + _reset_caret_blink_timer(); + + const CharType chr[2] = { (CharType)k->get_unicode(), 0 }; + if (auto_brace_completion_enabled && _is_pair_symbol(chr[0])) { + _consume_pair_symbol(chr[0]); + } else { + // Remove the old character if in insert mode. + if (insert_mode) { + begin_complex_operation(); + + // Make sure we don't try and remove empty space. + if (cursor.column < get_line(cursor.line).length()) { + _remove_text(cursor.line, cursor.column, cursor.line, cursor.column + 1); + } + } + + _insert_text_at_cursor(chr); + + if (insert_mode) { + end_complex_operation(); + } + } + _update_completion_candidates(); + accept_event(); + } + } +} + void TextEdit::_gui_input(const Ref &p_gui_input) { double prev_v_scroll = v_scroll->get_value(); double prev_h_scroll = h_scroll->get_value(); @@ -7155,6 +7194,7 @@ int TextEdit::get_line_height() const { void TextEdit::_bind_methods() { ClassDB::bind_method(D_METHOD("_gui_input"), &TextEdit::_gui_input); + ClassDB::bind_method(D_METHOD("_unhandled_key_input"), &TextEdit::_unhandled_key_input); ClassDB::bind_method(D_METHOD("_scroll_moved"), &TextEdit::_scroll_moved); ClassDB::bind_method(D_METHOD("_cursor_changed_emit"), &TextEdit::_cursor_changed_emit); ClassDB::bind_method(D_METHOD("_text_changed_emit"), &TextEdit::_text_changed_emit); @@ -7401,6 +7441,7 @@ TextEdit::TextEdit() { info_gutter_width = 0; cache.info_gutter_width = 0; set_default_cursor_shape(CURSOR_IBEAM); + set_process_unhandled_key_input(true); drag_action = false; drag_caret_force_displayed = false; diff --git a/scene/gui/text_edit.h b/scene/gui/text_edit.h index 4099866d2..63632e28a 100644 --- a/scene/gui/text_edit.h +++ b/scene/gui/text_edit.h @@ -515,6 +515,7 @@ protected: void _insert_text(int p_line, int p_char, const String &p_text, int *r_end_line = nullptr, int *r_end_char = nullptr); void _remove_text(int p_from_line, int p_from_column, int p_to_line, int p_to_column); void _insert_text_at_cursor(const String &p_text); + void _unhandled_key_input(const Ref &p_event); void _gui_input(const Ref &p_gui_input); void _notification(int p_what); diff --git a/scene/main/node.cpp b/scene/main/node.cpp index 404ced716..b87cd5539 100644 --- a/scene/main/node.cpp +++ b/scene/main/node.cpp @@ -131,6 +131,9 @@ void Node::_notification(int p_notification) { if (data.input) { add_to_group("_vp_input" + itos(get_viewport()->get_instance_id())); } + if (data.shortcut_input) { + add_to_group("_vp_shortcut_input" + itos(get_viewport()->get_instance_id())); + } if (data.unhandled_input) { add_to_group("_vp_unhandled_input" + itos(get_viewport()->get_instance_id())); } @@ -182,6 +185,9 @@ void Node::_notification(int p_notification) { if (data.input) { remove_from_group("_vp_input" + itos(get_viewport()->get_instance_id())); } + if (data.shortcut_input) { + remove_from_group("_vp_shortcut_input" + itos(get_viewport()->get_instance_id())); + } if (data.unhandled_input) { remove_from_group("_vp_unhandled_input" + itos(get_viewport()->get_instance_id())); } @@ -207,6 +213,10 @@ void Node::_notification(int p_notification) { set_process_input(true); } + if (get_script_instance()->has_method(SceneStringNames::get_singleton()->_shortcut_input)) { + set_process_shortcut_input(true); + } + if (get_script_instance()->has_method(SceneStringNames::get_singleton()->_unhandled_input)) { set_process_unhandled_input(true); } @@ -1402,6 +1412,27 @@ bool Node::is_processing_input() const { return data.input; } +void Node::set_process_shortcut_input(bool p_enable) { + if (p_enable == data.shortcut_input) { + return; + } + + data.shortcut_input = p_enable; + if (!is_inside_tree()) { + return; + } + + if (p_enable) { + add_to_group("_vp_shortcut_input" + itos(get_viewport()->get_instance_id())); + } else { + remove_from_group("_vp_shortcut_input" + itos(get_viewport()->get_instance_id())); + } +} + +bool Node::is_processing_shortcut_input() const { + return data.shortcut_input; +} + void Node::set_process_unhandled_input(bool p_enable) { if (p_enable == data.unhandled_input) { return; @@ -3501,6 +3532,8 @@ void Node::_bind_methods() { ClassDB::bind_method(D_METHOD("set_process_input", "enable"), &Node::set_process_input); ClassDB::bind_method(D_METHOD("is_processing_input"), &Node::is_processing_input); + ClassDB::bind_method(D_METHOD("set_process_shortcut_input", "enable"), &Node::set_process_shortcut_input); + ClassDB::bind_method(D_METHOD("is_processing_shortcut_input"), &Node::is_processing_shortcut_input); ClassDB::bind_method(D_METHOD("set_process_unhandled_input", "enable"), &Node::set_process_unhandled_input); ClassDB::bind_method(D_METHOD("is_processing_unhandled_input"), &Node::is_processing_unhandled_input); ClassDB::bind_method(D_METHOD("set_process_unhandled_key_input", "enable"), &Node::set_process_unhandled_key_input); @@ -3694,6 +3727,7 @@ void Node::_bind_methods() { BIND_VMETHOD(MethodInfo("_exit_tree")); BIND_VMETHOD(MethodInfo("_ready")); BIND_VMETHOD(MethodInfo("_input", PropertyInfo(Variant::OBJECT, "event", PROPERTY_HINT_RESOURCE_TYPE, "InputEvent"))); + BIND_VMETHOD(MethodInfo("_shortcut_input", PropertyInfo(Variant::OBJECT, "event", PROPERTY_HINT_RESOURCE_TYPE, "InputEvent"))); BIND_VMETHOD(MethodInfo("_unhandled_input", PropertyInfo(Variant::OBJECT, "event", PROPERTY_HINT_RESOURCE_TYPE, "InputEvent"))); BIND_VMETHOD(MethodInfo("_unhandled_key_input", PropertyInfo(Variant::OBJECT, "event", PROPERTY_HINT_RESOURCE_TYPE, "InputEventKey"))); BIND_VMETHOD(MethodInfo(Variant::STRING, "_get_configuration_warning")); @@ -3738,6 +3772,7 @@ Node::Node() { data.owner = nullptr; data.OW = nullptr; data.input = false; + data.shortcut_input = false; data.unhandled_input = false; data.unhandled_key_input = false; data.pause_mode = PAUSE_MODE_INHERIT; diff --git a/scene/main/node.h b/scene/main/node.h index b5c89d8e7..d8dbea4f1 100644 --- a/scene/main/node.h +++ b/scene/main/node.h @@ -153,6 +153,7 @@ private: bool idle_process_internal : 1; bool input : 1; + bool shortcut_input : 1; bool unhandled_input : 1; bool unhandled_key_input : 1; @@ -484,6 +485,9 @@ public: void set_process_input(bool p_enable); bool is_processing_input() const; + void set_process_shortcut_input(bool p_enable); + bool is_processing_shortcut_input() const; + void set_process_unhandled_input(bool p_enable); bool is_processing_unhandled_input() const; diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp index 7b1d3fc6b..5aeb78f84 100644 --- a/scene/main/scene_tree.cpp +++ b/scene/main/scene_tree.cpp @@ -1285,6 +1285,9 @@ void SceneTree::_call_input_pause(const StringName &p_group, const CallInputType case CALL_INPUT_TYPE_INPUT: method = SceneStringNames::get_singleton()->_input; break; + case CALL_INPUT_TYPE_SHORTCUT_INPUT: + method = SceneStringNames::get_singleton()->_shortcut_input; + break; case CALL_INPUT_TYPE_UNHANDLED_INPUT: method = SceneStringNames::get_singleton()->_unhandled_input; break; diff --git a/scene/main/scene_tree.h b/scene/main/scene_tree.h index 918642c6b..695f32f75 100644 --- a/scene/main/scene_tree.h +++ b/scene/main/scene_tree.h @@ -224,6 +224,7 @@ private: enum CallInputType { CALL_INPUT_TYPE_INPUT, + CALL_INPUT_TYPE_SHORTCUT_INPUT, CALL_INPUT_TYPE_UNHANDLED_INPUT, CALL_INPUT_TYPE_UNHANDLED_KEY_INPUT, }; diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index 498ab3245..fc6600393 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -2827,14 +2827,34 @@ void Viewport::input(const Ref &p_event) { void Viewport::unhandled_input(const Ref &p_event) { ERR_FAIL_COND(!is_inside_tree()); - get_tree()->_call_input_pause(unhandled_input_group, SceneTree::CALL_INPUT_TYPE_UNHANDLED_INPUT, p_event); - //call_group(GROUP_CALL_REVERSE|GROUP_CALL_REALTIME|GROUP_CALL_MULIILEVEL,"unhandled_input","_unhandled_input",ev); - if (!get_tree()->input_handled && Object::cast_to(*p_event) != nullptr) { - get_tree()->_call_input_pause(unhandled_key_input_group, SceneTree::CALL_INPUT_TYPE_UNHANDLED_KEY_INPUT, p_event); - //call_group(GROUP_CALL_REVERSE|GROUP_CALL_REALTIME|GROUP_CALL_MULIILEVEL,"unhandled_key_input","_unhandled_key_input",ev); + bool is_key_input = Object::cast_to(*p_event) != nullptr; + + // Shortcut Input. + if (is_key_input || Object::cast_to(*p_event) != nullptr) { + get_tree()->_call_input_pause(shortcut_input_group, SceneTree::CALL_INPUT_TYPE_SHORTCUT_INPUT, p_event); } - if (physics_object_picking && !get_tree()->input_handled) { + if (get_tree()->input_handled) { + return; + } + + // Unhandled Input. + get_tree()->_call_input_pause(unhandled_input_group, SceneTree::CALL_INPUT_TYPE_UNHANDLED_INPUT, p_event); + + if (get_tree()->input_handled) { + return; + } + + // Unhandled key Input - Used for performance reasons - This is called a lot less than _unhandled_input since it ignores MouseMotion, and to handle Unicode input with Alt / Ctrl modifiers after handling shortcuts. + if (is_key_input) { + get_tree()->_call_input_pause(unhandled_key_input_group, SceneTree::CALL_INPUT_TYPE_UNHANDLED_KEY_INPUT, p_event); + + if (get_tree()->input_handled) { + return; + } + } + + if (physics_object_picking) { if (Input::get_singleton()->get_mouse_mode() != Input::MOUSE_MODE_CAPTURED && (Object::cast_to(*p_event) || Object::cast_to(*p_event) || @@ -3393,6 +3413,7 @@ Viewport::Viewport() { String id = itos(get_instance_id()); input_group = "_vp_input" + id; gui_input_group = "_vp_gui_input" + id; + shortcut_input_group = "_vp_shortcut_input" + id; unhandled_input_group = "_vp_unhandled_input" + id; unhandled_key_input_group = "_vp_unhandled_key_input" + id; diff --git a/scene/main/viewport.h b/scene/main/viewport.h index 9cb062554..8bf56326a 100644 --- a/scene/main/viewport.h +++ b/scene/main/viewport.h @@ -416,6 +416,7 @@ private: StringName input_group; StringName gui_input_group; + StringName shortcut_input_group; StringName unhandled_input_group; StringName unhandled_key_input_group; diff --git a/scene/scene_string_names.cpp b/scene/scene_string_names.cpp index e0b482cdf..1b62fc99e 100644 --- a/scene/scene_string_names.cpp +++ b/scene/scene_string_names.cpp @@ -143,6 +143,8 @@ SceneStringNames::SceneStringNames() { gui_input = StaticCString::create("gui_input"); _gui_input = StaticCString::create("_gui_input"); + _shortcut_input = StaticCString::create("_shortcut_input"); + _unhandled_input = StaticCString::create("_unhandled_input"); _unhandled_key_input = StaticCString::create("_unhandled_key_input"); diff --git a/scene/scene_string_names.h b/scene/scene_string_names.h index 031fbfc93..b4fbc1f30 100644 --- a/scene/scene_string_names.h +++ b/scene/scene_string_names.h @@ -126,8 +126,9 @@ public: StringName _enter_tree; StringName _exit_tree; StringName _draw; - StringName _input; StringName _ready; + StringName _input; + StringName _shortcut_input; StringName _unhandled_input; StringName _unhandled_key_input;