diff --git a/doc/classes/Node.xml b/doc/classes/Node.xml index b1a6ed77401..f1e51b6f794 100644 --- a/doc/classes/Node.xml +++ b/doc/classes/Node.xml @@ -84,6 +84,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). + + @@ -102,6 +113,7 @@ 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 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 Viewport.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). @@ -462,6 +474,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]). + + @@ -673,6 +691,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 ab403c42125..179894915bb 100644 --- a/editor/editor_file_dialog.cpp +++ b/editor/editor_file_dialog.cpp @@ -106,7 +106,7 @@ void EditorFileDialog::_notification(int p_what) { case NOTIFICATION_VISIBILITY_CHANGED: { if (!is_visible()) { - set_process_unhandled_input(false); + set_process_shortcut_input(false); } } break; @@ -126,7 +126,7 @@ void EditorFileDialog::_notification(int p_what) { } } -void EditorFileDialog::unhandled_input(const Ref &p_event) { +void EditorFileDialog::shortcut_input(const Ref &p_event) { ERR_FAIL_COND(p_event.is_null()); Ref k = p_event; @@ -327,7 +327,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) { diff --git a/editor/editor_file_dialog.h b/editor/editor_file_dialog.h index 07af8c54354..fffe7ffcc56 100644 --- a/editor/editor_file_dialog.h +++ b/editor/editor_file_dialog.h @@ -201,7 +201,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); - virtual void unhandled_input(const Ref &p_event) override; + virtual void shortcut_input(const Ref &p_event) override; bool _is_open_should_be_disabled(); diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp index 1449edb58f9..d5085942c33 100644 --- a/editor/editor_inspector.cpp +++ b/editor/editor_inspector.cpp @@ -685,7 +685,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 || !p_event->is_pressed()) { return; } @@ -971,7 +971,7 @@ EditorProperty::EditorProperty() { label_reference = nullptr; 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 c9e8a1672e7..c5bcbe07258 100644 --- a/editor/editor_inspector.h +++ b/editor/editor_inspector.h @@ -128,7 +128,7 @@ protected: virtual void _set_read_only(bool p_read_only); virtual void gui_input(const Ref &p_event) override; - virtual void unhandled_key_input(const Ref &p_event) override; + virtual void shortcut_input(const Ref &p_event) override; const Color *_get_property_colors(); public: diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index 52f7366dd7e..0cd07333ea0 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -430,7 +430,7 @@ void EditorNode::_update_title() { DisplayServer::get_singleton()->window_set_title(title); } -void EditorNode::unhandled_input(const Ref &p_event) { +void EditorNode::shortcut_input(const Ref &p_event) { ERR_FAIL_COND(p_event.is_null()); Ref k = p_event; @@ -7168,7 +7168,7 @@ EditorNode::EditorNode() { _update_recent_scenes(); editor_data.restore_editor_global_states(); - set_process_unhandled_input(true); + set_process_shortcut_input(true); load_errors = memnew(RichTextLabel); load_error_dialog = memnew(AcceptDialog); diff --git a/editor/editor_node.h b/editor/editor_node.h index 5f09d4e518c..2e0fb19b87a 100644 --- a/editor/editor_node.h +++ b/editor/editor_node.h @@ -585,7 +585,7 @@ private: void _exit_editor(int p_exit_code); - virtual void unhandled_input(const Ref &p_event) override; + virtual void shortcut_input(const Ref &p_event) override; bool has_main_screen() const { return true; } diff --git a/editor/editor_settings_dialog.cpp b/editor/editor_settings_dialog.cpp index 285b909b664..dcbe91bd7f5 100644 --- a/editor/editor_settings_dialog.cpp +++ b/editor/editor_settings_dialog.cpp @@ -92,7 +92,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()); @@ -119,7 +119,7 @@ void EditorSettingsDialog::_notification(int p_what) { case NOTIFICATION_VISIBILITY_CHANGED: { if (!is_visible()) { EditorSettings::get_singleton()->set_project_metadata("dialog_bounds", "editor_settings", Rect2(get_position(), get_size())); - set_process_unhandled_input(false); + set_process_shortcut_input(false); } } break; @@ -148,7 +148,7 @@ void EditorSettingsDialog::_notification(int p_what) { } } -void EditorSettingsDialog::unhandled_input(const Ref &p_event) { +void EditorSettingsDialog::shortcut_input(const Ref &p_event) { ERR_FAIL_COND(p_event.is_null()); const Ref k = p_event; diff --git a/editor/editor_settings_dialog.h b/editor/editor_settings_dialog.h index 42831c9ebd3..9a34eac7ef1 100644 --- a/editor/editor_settings_dialog.h +++ b/editor/editor_settings_dialog.h @@ -82,7 +82,7 @@ class EditorSettingsDialog : public AcceptDialog { void _settings_property_edited(const String &p_name); void _settings_save(); - virtual void unhandled_input(const Ref &p_event) override; + virtual void shortcut_input(const Ref &p_event) override; void _notification(int p_what); void _update_icons(); diff --git a/editor/plugins/animation_player_editor_plugin.cpp b/editor/plugins/animation_player_editor_plugin.cpp index ab8e2ca54ac..af7c092d03a 100644 --- a/editor/plugins/animation_player_editor_plugin.cpp +++ b/editor/plugins/animation_player_editor_plugin.cpp @@ -1267,7 +1267,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; @@ -1750,7 +1750,7 @@ AnimationPlayerEditor::AnimationPlayerEditor(AnimationPlayerEditorPlugin *p_plug 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 850751e47a1..4f6a9c534f2 100644 --- a/editor/plugins/animation_player_editor_plugin.h +++ b/editor/plugins/animation_player_editor_plugin.h @@ -204,7 +204,7 @@ class AnimationPlayerEditor : public VBoxContainer { void _animation_key_editor_seek(float p_pos, bool p_drag, bool p_timeline_only = false); void _animation_key_editor_anim_len_changed(float p_len); - virtual void unhandled_key_input(const Ref &p_ev) override; + virtual void shortcut_input(const Ref &p_ev) override; void _animation_tool_menu(int p_option); void _onion_skinning_menu(int p_option); diff --git a/editor/plugins/asset_library_editor_plugin.cpp b/editor/plugins/asset_library_editor_plugin.cpp index 20bd145299a..405ece1471a 100644 --- a/editor/plugins/asset_library_editor_plugin.cpp +++ b/editor/plugins/asset_library_editor_plugin.cpp @@ -640,7 +640,7 @@ void EditorAssetLibrary::_update_repository_options() { } } -void EditorAssetLibrary::unhandled_key_input(const Ref &p_event) { +void EditorAssetLibrary::shortcut_input(const Ref &p_event) { ERR_FAIL_COND(p_event.is_null()); const Ref key = p_event; @@ -1541,7 +1541,7 @@ EditorAssetLibrary::EditorAssetLibrary(bool p_templates_only) { description = nullptr; set_process(true); - set_process_unhandled_key_input(true); // Global shortcuts since there is no main element to be focused. + set_process_shortcut_input(true); // Global shortcuts since there is no main element to be focused. downloads_scroll = memnew(ScrollContainer); downloads_scroll->set_vertical_scroll_mode(ScrollContainer::SCROLL_MODE_DISABLED); diff --git a/editor/plugins/asset_library_editor_plugin.h b/editor/plugins/asset_library_editor_plugin.h index 2a04935c473..96830c31fd0 100644 --- a/editor/plugins/asset_library_editor_plugin.h +++ b/editor/plugins/asset_library_editor_plugin.h @@ -304,7 +304,7 @@ class EditorAssetLibrary : public PanelContainer { protected: static void _bind_methods(); void _notification(int p_what); - virtual void unhandled_key_input(const Ref &p_event) override; + virtual void shortcut_input(const Ref &p_event) override; public: void disable_community_support(); diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp index 764f4671921..78307fbe7c3 100644 --- a/editor/plugins/canvas_item_editor_plugin.cpp +++ b/editor/plugins/canvas_item_editor_plugin.cpp @@ -475,7 +475,7 @@ real_t CanvasItemEditor::snap_angle(real_t p_target, real_t p_start) const { } } -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; @@ -5322,7 +5322,7 @@ CanvasItemEditor::CanvasItemEditor() { ED_SHORTCUT("canvas_item_editor/zoom_800_percent", TTR("Zoom to 800%"), Key::KEY_4); ED_SHORTCUT("canvas_item_editor/zoom_1600_percent", TTR("Zoom to 1600%"), Key::KEY_5); - set_process_unhandled_key_input(true); + set_process_shortcut_input(true); // Update the menus' checkboxes call_deferred(SNAME("set_state"), get_state()); diff --git a/editor/plugins/canvas_item_editor_plugin.h b/editor/plugins/canvas_item_editor_plugin.h index 1b61cdc6752..26852ea8ed5 100644 --- a/editor/plugins/canvas_item_editor_plugin.h +++ b/editor/plugins/canvas_item_editor_plugin.h @@ -413,7 +413,7 @@ private: void _keying_changed(); - virtual void unhandled_key_input(const Ref &p_ev) override; + virtual void shortcut_input(const Ref &p_ev) override; void _draw_text_at_position(Point2 p_position, String p_string, Side p_side); void _draw_margin_at_position(int p_value, Point2 p_position, Side p_side); diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp index eab477adb7e..090c1404560 100644 --- a/editor/plugins/node_3d_editor_plugin.cpp +++ b/editor/plugins/node_3d_editor_plugin.cpp @@ -6848,7 +6848,7 @@ void Node3DEditor::snap_selected_nodes_to_floor() { } } -void Node3DEditor::unhandled_key_input(const Ref &p_event) { +void Node3DEditor::shortcut_input(const Ref &p_event) { ERR_FAIL_COND(p_event.is_null()); if (!is_visible_in_tree()) { @@ -7893,7 +7893,7 @@ Node3DEditor::Node3DEditor() { 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/node_3d_editor_plugin.h b/editor/plugins/node_3d_editor_plugin.h index 5447befeddb..d5d50c743c5 100644 --- a/editor/plugins/node_3d_editor_plugin.h +++ b/editor/plugins/node_3d_editor_plugin.h @@ -770,7 +770,7 @@ private: protected: void _notification(int p_what); //void _gui_input(InputEvent p_event); - virtual void unhandled_key_input(const Ref &p_event) override; + virtual void shortcut_input(const Ref &p_event) override; static void _bind_methods(); diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp index 677b55bb88b..6f85b24d5e6 100644 --- a/editor/plugins/script_editor_plugin.cpp +++ b/editor/plugins/script_editor_plugin.cpp @@ -3019,7 +3019,7 @@ void ScriptEditor::input(const Ref &p_event) { } } -void ScriptEditor::unhandled_key_input(const Ref &p_event) { +void ScriptEditor::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()) { @@ -3740,7 +3740,7 @@ ScriptEditor::ScriptEditor() { ED_SHORTCUT("script_editor/next_script", TTR("Next Script"), KeyModifierMask::CMD | KeyModifierMask::SHIFT | Key::PERIOD); ED_SHORTCUT("script_editor/prev_script", TTR("Previous Script"), KeyModifierMask::CMD | KeyModifierMask::SHIFT | Key::COMMA); set_process_input(true); - set_process_unhandled_key_input(true); + set_process_shortcut_input(true); file_menu = memnew(MenuButton); file_menu->set_text(TTR("File")); diff --git a/editor/plugins/script_editor_plugin.h b/editor/plugins/script_editor_plugin.h index 5eeca7a3346..5b8d46089ee 100644 --- a/editor/plugins/script_editor_plugin.h +++ b/editor/plugins/script_editor_plugin.h @@ -426,7 +426,7 @@ class ScriptEditor : public PanelContainer { void drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from); virtual void input(const Ref &p_event) override; - virtual void unhandled_key_input(const Ref &p_event) override; + virtual void shortcut_input(const Ref &p_event) override; void _script_list_gui_input(const Ref &ev); void _make_script_list_context_menu(); diff --git a/editor/project_manager.cpp b/editor/project_manager.cpp index cd95f972867..cbc4bb03f63 100644 --- a/editor/project_manager.cpp +++ b/editor/project_manager.cpp @@ -1903,7 +1903,7 @@ void ProjectManager::_notification(int p_what) { } break; case NOTIFICATION_VISIBILITY_CHANGED: { - set_process_unhandled_key_input(is_visible_in_tree()); + set_process_shortcut_input(is_visible_in_tree()); } break; case NOTIFICATION_WM_CLOSE_REQUEST: { @@ -1962,7 +1962,7 @@ void ProjectManager::_update_project_buttons() { erase_missing_btn->set_disabled(!_project_list->is_any_project_missing()); } -void ProjectManager::unhandled_key_input(const Ref &p_ev) { +void ProjectManager::shortcut_input(const Ref &p_ev) { ERR_FAIL_COND(p_ev.is_null()); Ref k = p_ev; diff --git a/editor/project_manager.h b/editor/project_manager.h index 4f053793fda..9cea6e163f3 100644 --- a/editor/project_manager.h +++ b/editor/project_manager.h @@ -126,7 +126,7 @@ class ProjectManager : public Control { void _install_project(const String &p_zip_path, const String &p_title); void _dim_window(); - virtual void unhandled_key_input(const Ref &p_ev) override; + virtual void shortcut_input(const Ref &p_ev) override; void _files_dropped(PackedStringArray p_files, int p_screen); void _version_button_pressed(); diff --git a/editor/project_settings_editor.cpp b/editor/project_settings_editor.cpp index 1318287dc85..8aaf3ddbefb 100644 --- a/editor/project_settings_editor.cpp +++ b/editor/project_settings_editor.cpp @@ -49,7 +49,7 @@ void ProjectSettingsEditor::popup_project_settings() { _add_feature_overrides(); general_settings_inspector->update_category_list(); - set_process_unhandled_input(true); + set_process_shortcut_input(true); localization_editor->update_translations(); autoload_settings->update_autoload(); @@ -202,7 +202,7 @@ void ProjectSettingsEditor::_select_type(Variant::Type p_type) { type_box->select(type_box->get_item_index(p_type)); } -void ProjectSettingsEditor::unhandled_input(const Ref &p_event) { +void ProjectSettingsEditor::shortcut_input(const Ref &p_event) { ERR_FAIL_COND(p_event.is_null()); const Ref k = p_event; diff --git a/editor/project_settings_editor.h b/editor/project_settings_editor.h index c9d849abec3..24d61db4432 100644 --- a/editor/project_settings_editor.h +++ b/editor/project_settings_editor.h @@ -82,7 +82,7 @@ class ProjectSettingsEditor : public AcceptDialog { void _feature_selected(int p_index); void _select_type(Variant::Type p_type); - virtual void unhandled_input(const Ref &p_event) override; + virtual void shortcut_input(const Ref &p_event) override; String _get_setting_name() const; void _setting_edited(const String &p_name); diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp index 551a0ff9c80..32236b01edd 100644 --- a/editor/scene_tree_dock.cpp +++ b/editor/scene_tree_dock.cpp @@ -79,7 +79,7 @@ void SceneTreeDock::input(const Ref &p_event) { } } -void SceneTreeDock::unhandled_key_input(const Ref &p_event) { +void SceneTreeDock::shortcut_input(const Ref &p_event) { ERR_FAIL_COND(p_event.is_null()); if (get_viewport()->gui_get_focus_owner() && get_viewport()->gui_get_focus_owner()->is_text_field()) { @@ -3476,7 +3476,7 @@ SceneTreeDock::SceneTreeDock(Node *p_scene_root, EditorSelection *p_editor_selec add_child(quick_open); quick_open->connect("quick_open", callable_mp(this, &SceneTreeDock::_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 711ac5bc53f..599fb01203d 100644 --- a/editor/scene_tree_dock.h +++ b/editor/scene_tree_dock.h @@ -220,7 +220,7 @@ class SceneTreeDock : public VBoxContainer { void _nodes_drag_begin(); virtual void input(const Ref &p_event) override; - virtual void unhandled_key_input(const Ref &p_event) override; + virtual void shortcut_input(const Ref &p_event) override; void _import_subscene(); diff --git a/scene/gui/base_button.cpp b/scene/gui/base_button.cpp index ab86face7e3..789c01adf3c 100644 --- a/scene/gui/base_button.cpp +++ b/scene/gui/base_button.cpp @@ -339,14 +339,14 @@ bool BaseButton::is_keep_pressed_outside() const { void BaseButton::set_shortcut(const Ref &p_shortcut) { shortcut = p_shortcut; - set_process_unhandled_key_input(shortcut.is_valid()); + set_process_shortcut_input(shortcut.is_valid()); } Ref BaseButton::get_shortcut() const { return shortcut; } -void BaseButton::unhandled_key_input(const Ref &p_event) { +void BaseButton::shortcut_input(const Ref &p_event) { ERR_FAIL_COND(p_event.is_null()); if (!_is_focus_owner_in_shortcut_context()) { diff --git a/scene/gui/base_button.h b/scene/gui/base_button.h index a2b6ee0845c..f4f9b888688 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(const Ref &p_event) override; - virtual void unhandled_key_input(const Ref &p_event) override; + virtual void shortcut_input(const Ref &p_event) override; void _notification(int p_what); bool _is_focus_owner_in_shortcut_context() const; diff --git a/scene/gui/file_dialog.cpp b/scene/gui/file_dialog.cpp index e71ab64535e..5e746584707 100644 --- a/scene/gui/file_dialog.cpp +++ b/scene/gui/file_dialog.cpp @@ -95,7 +95,7 @@ void FileDialog::_notification(int p_what) { switch (p_what) { case NOTIFICATION_VISIBILITY_CHANGED: { if (!is_visible()) { - set_process_unhandled_input(false); + set_process_shortcut_input(false); } } break; @@ -119,7 +119,7 @@ void FileDialog::_notification(int p_what) { } } -void FileDialog::unhandled_input(const Ref &p_event) { +void FileDialog::shortcut_input(const Ref &p_event) { ERR_FAIL_COND(p_event.is_null()); Ref k = p_event; @@ -217,7 +217,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 == FILE_MODE_OPEN_DIR) { diff --git a/scene/gui/file_dialog.h b/scene/gui/file_dialog.h index 9bba93bb898..b41a08c6c74 100644 --- a/scene/gui/file_dialog.h +++ b/scene/gui/file_dialog.h @@ -133,7 +133,7 @@ private: void _update_drives(bool p_select = true); - virtual void unhandled_input(const Ref &p_event) override; + virtual void shortcut_input(const Ref &p_event) override; bool _is_open_should_be_disabled(); diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp index e063d3aeba6..b3f051bb754 100644 --- a/scene/gui/line_edit.cpp +++ b/scene/gui/line_edit.cpp @@ -215,6 +215,27 @@ void LineEdit::_delete(bool p_word, bool p_all_to_right) { } } +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(); + insert_text_at_caret(ucodestr); + if (text.length() != prev_len) { + _text_changed(); + } + accept_event(); + } + } +} + void LineEdit::gui_input(const Ref &p_event) { ERR_FAIL_COND(p_event.is_null()); @@ -2445,6 +2466,7 @@ LineEdit::LineEdit(const String &p_placeholder) { set_focus_mode(FOCUS_ALL); set_default_cursor_shape(CURSOR_IBEAM); set_mouse_filter(MOUSE_FILTER_STOP); + set_process_unhandled_key_input(true); caret_blink_timer = memnew(Timer); add_child(caret_blink_timer, false, INTERNAL_MODE_FRONT); diff --git a/scene/gui/line_edit.h b/scene/gui/line_edit.h index 444c9a1c50e..b86ccd6421b 100644 --- a/scene/gui/line_edit.h +++ b/scene/gui/line_edit.h @@ -202,6 +202,7 @@ private: protected: void _notification(int p_what); static void _bind_methods(); + virtual void unhandled_key_input(const Ref &p_event) override; virtual void gui_input(const Ref &p_event) override; bool _set(const StringName &p_name, const Variant &p_value); diff --git a/scene/gui/menu_button.cpp b/scene/gui/menu_button.cpp index 7e724e4d713..1feee017c26 100644 --- a/scene/gui/menu_button.cpp +++ b/scene/gui/menu_button.cpp @@ -33,7 +33,7 @@ #include "core/os/keyboard.h" #include "scene/main/window.h" -void MenuButton::unhandled_key_input(const Ref &p_event) { +void MenuButton::shortcut_input(const Ref &p_event) { ERR_FAIL_COND(p_event.is_null()); if (!_is_focus_owner_in_shortcut_context()) { @@ -232,7 +232,7 @@ MenuButton::MenuButton(const String &p_text) : set_flat(true); set_toggle_mode(true); set_disable_shortcuts(false); - set_process_unhandled_key_input(true); + set_process_shortcut_input(true); set_focus_mode(FOCUS_NONE); set_action_mode(ACTION_MODE_BUTTON_PRESS); diff --git a/scene/gui/menu_button.h b/scene/gui/menu_button.h index e3ead9c291d..0a6b46c7967 100644 --- a/scene/gui/menu_button.h +++ b/scene/gui/menu_button.h @@ -54,7 +54,7 @@ protected: bool _get(const StringName &p_name, Variant &r_ret) const; void _get_property_list(List *p_list) const; static void _bind_methods(); - virtual void unhandled_key_input(const Ref &p_event) override; + virtual void shortcut_input(const Ref &p_event) override; public: virtual void pressed() override; diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index 1f49491eb84..86969e3ef41 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -1542,6 +1542,21 @@ void TextEdit::_notification(int p_what) { } } +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() && editable && (k->get_unicode() >= 32)) { + handle_unicode_input(k->get_unicode()); + accept_event(); + } + } +} + void TextEdit::gui_input(const Ref &p_gui_input) { ERR_FAIL_COND(p_gui_input.is_null()); @@ -6620,6 +6635,7 @@ TextEdit::TextEdit(const String &p_placeholder) { set_focus_mode(FOCUS_ALL); _update_caches(); set_default_cursor_shape(CURSOR_IBEAM); + set_process_unhandled_key_input(true); text.set_tab_size(text.get_tab_size()); diff --git a/scene/gui/text_edit.h b/scene/gui/text_edit.h index 7d36051a379..b365e9c61c7 100644 --- a/scene/gui/text_edit.h +++ b/scene/gui/text_edit.h @@ -623,6 +623,7 @@ protected: public: /* General overrides. */ + virtual void unhandled_key_input(const Ref &p_event) override; virtual void gui_input(const Ref &p_gui_input) override; virtual Size2 get_minimum_size() const override; virtual bool is_text_field() const override; diff --git a/scene/main/node.cpp b/scene/main/node.cpp index 7933045c5f4..34bb1cde05a 100644 --- a/scene/main/node.cpp +++ b/scene/main/node.cpp @@ -79,6 +79,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())); } @@ -100,6 +103,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())); } @@ -126,6 +132,10 @@ void Node::_notification(int p_notification) { set_process_input(true); } + if (GDVIRTUAL_IS_OVERRIDDEN(_shortcut_input)) { + set_process_shortcut_input(true); + } + if (GDVIRTUAL_IS_OVERRIDDEN(_unhandled_input)) { set_process_unhandled_input(true); } @@ -835,6 +845,26 @@ 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; @@ -2615,6 +2645,15 @@ void Node::_call_input(const Ref &p_event) { } input(p_event); } + +void Node::_call_shortcut_input(const Ref &p_event) { + GDVIRTUAL_CALL(_shortcut_input, p_event); + if (!is_inside_tree() || !get_viewport() || get_viewport()->is_input_handled()) { + return; + } + shortcut_input(p_event); +} + void Node::_call_unhandled_input(const Ref &p_event) { GDVIRTUAL_CALL(_unhandled_input, p_event); if (!is_inside_tree() || !get_viewport() || get_viewport()->is_input_handled()) { @@ -2622,6 +2661,7 @@ void Node::_call_unhandled_input(const Ref &p_event) { } unhandled_input(p_event); } + void Node::_call_unhandled_key_input(const Ref &p_event) { GDVIRTUAL_CALL(_unhandled_key_input, p_event); if (!is_inside_tree() || !get_viewport() || get_viewport()->is_input_handled()) { @@ -2633,6 +2673,9 @@ void Node::_call_unhandled_key_input(const Ref &p_event) { void Node::input(const Ref &p_event) { } +void Node::shortcut_input(const Ref &p_key_event) { +} + void Node::unhandled_input(const Ref &p_event) { } @@ -2694,6 +2737,8 @@ void Node::_bind_methods() { ClassDB::bind_method(D_METHOD("is_processing"), &Node::is_processing); 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); @@ -2854,6 +2899,7 @@ void Node::_bind_methods() { GDVIRTUAL_BIND(_ready); GDVIRTUAL_BIND(_get_configuration_warnings); GDVIRTUAL_BIND(_input, "event"); + GDVIRTUAL_BIND(_shortcut_input, "event"); GDVIRTUAL_BIND(_unhandled_input, "event"); GDVIRTUAL_BIND(_unhandled_key_input, "event"); } diff --git a/scene/main/node.h b/scene/main/node.h index 1f973a0cb4f..57b150e29a9 100644 --- a/scene/main/node.h +++ b/scene/main/node.h @@ -136,6 +136,7 @@ private: bool process_internal = false; bool input = false; + bool shortcut_input = false; bool unhandled_input = false; bool unhandled_key_input = false; @@ -215,11 +216,13 @@ protected: //call from SceneTree void _call_input(const Ref &p_event); + void _call_shortcut_input(const Ref &p_event); void _call_unhandled_input(const Ref &p_event); void _call_unhandled_key_input(const Ref &p_event); protected: virtual void input(const Ref &p_event); + virtual void shortcut_input(const Ref &p_key_event); virtual void unhandled_input(const Ref &p_event); virtual void unhandled_key_input(const Ref &p_key_event); @@ -231,6 +234,7 @@ protected: GDVIRTUAL0RC(Vector, _get_configuration_warnings) GDVIRTUAL1(_input, Ref) + GDVIRTUAL1(_shortcut_input, Ref) GDVIRTUAL1(_unhandled_input, Ref) GDVIRTUAL1(_unhandled_key_input, Ref) @@ -395,6 +399,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 8eeedf51a91..d005633bb54 100644 --- a/scene/main/scene_tree.cpp +++ b/scene/main/scene_tree.cpp @@ -897,6 +897,9 @@ void SceneTree::_call_input_pause(const StringName &p_group, CallInputType p_cal case CALL_INPUT_TYPE_INPUT: n->_call_input(p_input); break; + case CALL_INPUT_TYPE_SHORTCUT_INPUT: + n->_call_shortcut_input(p_input); + break; case CALL_INPUT_TYPE_UNHANDLED_INPUT: n->_call_unhandled_input(p_input); break; diff --git a/scene/main/scene_tree.h b/scene/main/scene_tree.h index 0f8cbedb414..705ca6ebd30 100644 --- a/scene/main/scene_tree.h +++ b/scene/main/scene_tree.h @@ -204,6 +204,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 a38bed71d13..e20287c8752 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -2744,11 +2744,18 @@ void Viewport::push_unhandled_input(const Ref &p_event, bool p_local ev = p_event; } - // Unhandled Input. - get_tree()->_call_input_pause(unhandled_input_group, SceneTree::CALL_INPUT_TYPE_UNHANDLED_INPUT, ev, this); + // Shortcut Input. + if (Object::cast_to(*ev) != nullptr || Object::cast_to(*ev) != nullptr) { + get_tree()->_call_input_pause(shortcut_input_group, SceneTree::CALL_INPUT_TYPE_SHORTCUT_INPUT, ev, this); + } - // Unhandled key Input - used for performance reasons - This is called a lot less than _unhandled_input since it ignores MouseMotion, etc. - if (!is_input_handled() && (Object::cast_to(*ev) != nullptr || Object::cast_to(*ev) != nullptr)) { + // Unhandled Input. + if (!is_input_handled()) { + get_tree()->_call_input_pause(unhandled_input_group, SceneTree::CALL_INPUT_TYPE_UNHANDLED_INPUT, ev, this); + } + + // 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_input_handled() && (Object::cast_to(*ev) != nullptr)) { get_tree()->_call_input_pause(unhandled_key_input_group, SceneTree::CALL_INPUT_TYPE_UNHANDLED_KEY_INPUT, ev, this); } @@ -3879,6 +3886,7 @@ Viewport::Viewport() { input_group = "_vp_input" + id; gui_input_group = "_vp_gui_input" + id; unhandled_input_group = "_vp_unhandled_input" + id; + shortcut_input_group = "_vp_shortcut_input" + id; unhandled_key_input_group = "_vp_unhandled_key_input" + id; // Window tooltip. diff --git a/scene/main/viewport.h b/scene/main/viewport.h index 80be53fab8d..32882fbb687 100644 --- a/scene/main/viewport.h +++ b/scene/main/viewport.h @@ -270,6 +270,7 @@ private: Rect2i to_screen_rect; StringName input_group; StringName gui_input_group; + StringName shortcut_input_group; StringName unhandled_input_group; StringName unhandled_key_input_group;