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(); \
}