diff --git a/doc/classes/Viewport.xml b/doc/classes/Viewport.xml index 0418f2980832..1caf2a6219c4 100644 --- a/doc/classes/Viewport.xml +++ b/doc/classes/Viewport.xml @@ -134,22 +134,38 @@ + Pushes an input event. - + - + + - Returns [code]true[/code] if the viewport is currently embedding windows. - + + + + + + The three push_local_*_input functions push an event to the three event passes respectively. Unlike push_input, they do not forward events to subwindows. + + + + + + + + Returns [code]true[/code] if the viewport is currently embedding windows. + + diff --git a/editor/editor_command_palette.cpp b/editor/editor_command_palette.cpp index 52e55de84cea..33cf3c0b416f 100644 --- a/editor/editor_command_palette.cpp +++ b/editor/editor_command_palette.cpp @@ -238,7 +238,7 @@ void EditorCommandPalette::register_shortcuts_as_command() { ev.instantiate(); ev->set_shortcut(shortcut); String shortcut_text = String(shortcut->get_as_text()); - add_command(command_name, *key, callable_mp(EditorNode::get_singleton()->get_viewport(), &Viewport::push_unhandled_input), varray(ev, false), shortcut_text); + add_command(command_name, *key, callable_mp(EditorNode::get_singleton()->get_viewport(), &Viewport::push_local_unhandled_input), varray(ev, false), shortcut_text); key = unregistered_shortcuts.next(key); } unregistered_shortcuts.clear(); @@ -260,7 +260,7 @@ Ref EditorCommandPalette::add_shortcut_command(const String &p_command ev.instantiate(); ev->set_shortcut(p_shortcut); String shortcut_text = String(p_shortcut->get_as_text()); - add_command(p_command, p_key, callable_mp(EditorNode::get_singleton()->get_viewport(), &Viewport::push_unhandled_input), varray(ev, false), shortcut_text); + add_command(p_command, p_key, callable_mp(EditorNode::get_singleton()->get_viewport(), &Viewport::push_local_unhandled_input), varray(ev, false), shortcut_text); } else { const String key_name = String(p_key); const String command_name = String(p_command); diff --git a/scene/gui/subviewport_container.cpp b/scene/gui/subviewport_container.cpp index 53ea32e1b7c4..750ace1403ed 100644 --- a/scene/gui/subviewport_container.cpp +++ b/scene/gui/subviewport_container.cpp @@ -162,7 +162,34 @@ void SubViewportContainer::input(const Ref &p_event) { continue; } - c->push_input(ev); + c->push_local_input(ev); + } +} + +void SubViewportContainer::gui_input(const Ref &p_event) { + ERR_FAIL_COND(p_event.is_null()); + + if (Engine::get_singleton()->is_editor_hint()) { + return; + } + + Transform2D xform = get_global_transform(); + + if (stretch) { + Transform2D scale_xf; + scale_xf.scale(Vector2(shrink, shrink)); + xform *= scale_xf; + } + + Ref ev = p_event; // already transformed + + for (int i = 0; i < get_child_count(); i++) { + SubViewport *c = Object::cast_to(get_child(i)); + if (!c || c->is_input_disabled()) { + continue; + } + + c->push_local_gui_input(ev); } } @@ -189,7 +216,7 @@ void SubViewportContainer::unhandled_input(const Ref &p_event) { continue; } - c->push_unhandled_input(ev); + c->push_local_unhandled_input(ev); } } diff --git a/scene/gui/subviewport_container.h b/scene/gui/subviewport_container.h index 7853f1590ea8..2d441b4f6c04 100644 --- a/scene/gui/subviewport_container.h +++ b/scene/gui/subviewport_container.h @@ -48,6 +48,7 @@ class SubViewportContainer : public Container { bool is_stretch_enabled() const; virtual void input(const Ref &p_event) override; + virtual void gui_input(const Ref &p_event) override; virtual void unhandled_input(const Ref &p_event) override; void set_stretch_shrink(int p_shrink); int get_stretch_shrink() const; diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index f9e96a078490..38f351f910f2 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -1973,23 +1973,52 @@ void Viewport::_gui_input_event(Ref p_event) { } if (mm.is_null() && mb.is_null() && p_event->is_action_type()) { - if (gui.key_focus && !gui.key_focus->is_visible_in_tree()) { - gui.key_focus->release_focus(); - } + // Search all qualified subviewports (self-included) for a + // key_focus control. + Viewport *target_vp = this; + List vps; + get_tree()->get_nodes_in_group("_viewports", &vps); + for (Node *node : vps) { + Viewport *vp = Object::cast_to(node); + if (vp == nullptr) { + continue; + } + if (vp->gui.key_focus == nullptr) { + continue; + } - if (gui.key_focus) { - gui.key_event_accepted = false; - if (gui.key_focus->can_process()) { - gui.key_focus->_call_gui_input(p_event); + Viewport *vp_parent = vp; + while (vp_parent && (vp_parent != this)) { + vp_parent = vp_parent->get_parent_viewport(); + } + if (vp_parent != this) { + continue; } - if (gui.key_event_accepted) { - set_input_as_handled(); - return; + target_vp = vp; + break; + } + if (target_vp->gui.key_focus) { + Viewport::GUI &target_gui = target_vp->gui; + Control *&target_key_focus = target_vp->gui.key_focus; + if (target_key_focus && !target_key_focus->is_visible_in_tree()) { + target_key_focus->release_focus(); + } + + if (target_key_focus) { + target_gui.key_event_accepted = false; + if (target_key_focus->can_process()) { + target_key_focus->_call_gui_input(p_event); + } + + if (target_gui.key_event_accepted) { + set_input_as_handled(); + return; + } } } - Control *from = gui.key_focus ? gui.key_focus : nullptr; + Control *from = target_vp->gui.key_focus; if (from && p_event->is_pressed()) { Control *next = nullptr; @@ -2682,22 +2711,77 @@ void Viewport::push_input(const Ref &p_event, bool p_local_coords) { return; } + if (!is_input_handled()) { + push_local_input(ev); + } + if (!is_input_handled()) { + push_local_gui_input(ev); + } + if (!is_input_handled()) { + push_local_unhandled_input(ev); + } + event_count++; +} + +void Viewport::push_local_input(const Ref &p_event, bool p_local_coords) { + ERR_FAIL_COND(!is_inside_tree()); + + if (disable_input) { + return; + } + + if (Engine::get_singleton()->is_editor_hint() && get_tree()->get_edited_scene_root() && get_tree()->get_edited_scene_root()->is_ancestor_of(this)) { + return; + } + + local_input_handled = false; + + Ref ev; + if (!p_local_coords) { + ev = _make_input_local(p_event); + } else { + ev = p_event; + } + if (!_can_consume_input_events()) { return; } if (!is_input_handled()) { - get_tree()->_call_input_pause(input_group, SceneTree::CALL_INPUT_TYPE_INPUT, ev, this); //not a bug, must happen before GUI, order is _input -> gui input -> _unhandled input + get_tree()->_call_input_pause(input_group, SceneTree::CALL_INPUT_TYPE_INPUT, ev, this); + } +} + +void Viewport::push_local_gui_input(const Ref &p_event, bool p_local_coords) { + ERR_FAIL_COND(!is_inside_tree()); + + if (disable_input) { + return; + } + + if (Engine::get_singleton()->is_editor_hint() && get_tree()->get_edited_scene_root() && get_tree()->get_edited_scene_root()->is_ancestor_of(this)) { + return; + } + + local_input_handled = false; + + Ref ev; + if (!p_local_coords) { + ev = _make_input_local(p_event); + } else { + ev = p_event; + } + + if (!_can_consume_input_events()) { + return; } if (!is_input_handled()) { _gui_input_event(ev); } - - event_count++; } -void Viewport::push_unhandled_input(const Ref &p_event, bool p_local_coords) { +void Viewport::push_local_unhandled_input(const Ref &p_event, bool p_local_coords) { ERR_FAIL_COND(p_event.is_null()); ERR_FAIL_COND(!is_inside_tree()); local_input_handled = false; @@ -3573,7 +3657,9 @@ void Viewport::_bind_methods() { ClassDB::bind_method(D_METHOD("get_viewport_rid"), &Viewport::get_viewport_rid); ClassDB::bind_method(D_METHOD("push_text_input", "text"), &Viewport::push_text_input); ClassDB::bind_method(D_METHOD("push_input", "event", "in_local_coords"), &Viewport::push_input, DEFVAL(false)); - ClassDB::bind_method(D_METHOD("push_unhandled_input", "event", "in_local_coords"), &Viewport::push_unhandled_input, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("push_local_input", "event", "in_local_coords"), &Viewport::push_local_gui_input, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("push_local_gui_input", "event", "in_local_coords"), &Viewport::push_local_gui_input, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("push_local_unhandled_input", "event", "in_local_coords"), &Viewport::push_local_unhandled_input, DEFVAL(false)); ClassDB::bind_method(D_METHOD("get_camera_2d"), &Viewport::get_camera_2d); ClassDB::bind_method(D_METHOD("set_as_audio_listener_2d", "enable"), &Viewport::set_as_audio_listener_2d); diff --git a/scene/main/viewport.h b/scene/main/viewport.h index 38d43e1e5970..647fc222534e 100644 --- a/scene/main/viewport.h +++ b/scene/main/viewport.h @@ -541,7 +541,9 @@ class Viewport : public Node { void push_text_input(const String &p_text); void push_input(const Ref &p_event, bool p_local_coords = false); - void push_unhandled_input(const Ref &p_event, bool p_local_coords = false); + void push_local_input(const Ref &p_event, bool p_local_coords = false); + void push_local_gui_input(const Ref &p_event, bool p_local_coords = false); + void push_local_unhandled_input(const Ref &p_event, bool p_local_coords = false); void set_disable_input(bool p_disable); bool is_input_disabled() const; diff --git a/scene/main/window.cpp b/scene/main/window.cpp index 20f8b30dc6d9..dc8b1edaccf8 100644 --- a/scene/main/window.cpp +++ b/scene/main/window.cpp @@ -920,10 +920,7 @@ void Window::_window_input(const Ref &p_ev) { emit_signal(SceneStringNames::get_singleton()->window_input, p_ev); - push_input(p_ev); - if (!is_input_handled()) { - push_unhandled_input(p_ev); - } + push_input(p_ev, false); } void Window::_window_input_text(const String &p_text) { diff --git a/tests/test_macros.h b/tests/test_macros.h index b04c2117b7e1..5f203b243bb9 100644 --- a/tests/test_macros.h +++ b/tests/test_macros.h @@ -166,7 +166,7 @@ int register_test_command(String p_command, TestFunc p_function); #define SEND_GUI_MOUSE_EVENT(m_object, m_local_pos, m_input, m_mask) \ { \ _CREATE_GUI_MOUSE_EVENT(m_object, m_local_pos, m_input, m_mask); \ - m_object->get_viewport()->push_input(event); \ + m_object->get_viewport()->push_local_gui_input(event); \ MessageQueue::get_singleton()->flush(); \ } @@ -174,7 +174,7 @@ int register_test_command(String p_command, TestFunc p_function); { \ _CREATE_GUI_MOUSE_EVENT(m_object, m_local_pos, MouseButton::LEFT, MouseButton::LEFT); \ event->set_double_click(true); \ - m_object->get_viewport()->push_input(event); \ + m_object->get_viewport()->push_local_gui_input(event); \ MessageQueue::get_singleton()->flush(); \ }