Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix subviewports receiving gui input too early #55300

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 20 additions & 4 deletions doc/classes/Viewport.xml
Original file line number Diff line number Diff line change
Expand Up @@ -134,22 +134,38 @@
<argument index="0" name="event" type="InputEvent" />
<argument index="1" name="in_local_coords" type="bool" default="false" />
<description>
Pushes an input event.
</description>
zhehangd marked this conversation as resolved.
Show resolved Hide resolved
</method>
<method name="push_text_input">
<method name="push_local_gui_input">
<return type="void" />
<argument index="0" name="text" type="String" />
<argument index="0" name="event" type="InputEvent" />
<argument index="1" name="in_local_coords" type="bool" default="false" />
<description>
Returns [code]true[/code] if the viewport is currently embedding windows.
</description>
</method>
<method name="push_unhandled_input">
<method name="push_local_input">
<return type="void" />
<argument index="0" name="event" type="InputEvent" />
<argument index="1" name="in_local_coords" type="bool" default="false" />
<description>
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.
</description>
</method>
<method name="push_local_unhandled_input">
<return type="void" />
<argument index="0" name="event" type="InputEvent" />
<argument index="1" name="in_local_coords" type="bool" default="false" />
<description>
</description>
</method>
<method name="push_text_input">
<return type="void" />
<argument index="0" name="text" type="String" />
<description>
Returns [code]true[/code] if the viewport is currently embedding windows.
</description>
</method>
<method name="set_input_as_handled">
<return type="void" />
<description>
Expand Down
4 changes: 2 additions & 2 deletions editor/editor_command_palette.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -260,7 +260,7 @@ Ref<Shortcut> 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);
Expand Down
31 changes: 29 additions & 2 deletions scene/gui/subviewport_container.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,34 @@ void SubViewportContainer::input(const Ref<InputEvent> &p_event) {
continue;
}

c->push_input(ev);
c->push_local_input(ev);
}
}

void SubViewportContainer::gui_input(const Ref<InputEvent> &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<InputEvent> ev = p_event; // already transformed

for (int i = 0; i < get_child_count(); i++) {
SubViewport *c = Object::cast_to<SubViewport>(get_child(i));
if (!c || c->is_input_disabled()) {
continue;
}

c->push_local_gui_input(ev);
}
}

Expand All @@ -189,7 +216,7 @@ void SubViewportContainer::unhandled_input(const Ref<InputEvent> &p_event) {
continue;
}

c->push_unhandled_input(ev);
c->push_local_unhandled_input(ev);
}
}

Expand Down
1 change: 1 addition & 0 deletions scene/gui/subviewport_container.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ class SubViewportContainer : public Container {
bool is_stretch_enabled() const;

virtual void input(const Ref<InputEvent> &p_event) override;
virtual void gui_input(const Ref<InputEvent> &p_event) override;
virtual void unhandled_input(const Ref<InputEvent> &p_event) override;
void set_stretch_shrink(int p_shrink);
int get_stretch_shrink() const;
Expand Down
118 changes: 102 additions & 16 deletions scene/main/viewport.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1973,23 +1973,52 @@ void Viewport::_gui_input_event(Ref<InputEvent> 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<Node *> vps;
get_tree()->get_nodes_in_group("_viewports", &vps);
for (Node *node : vps) {
Viewport *vp = Object::cast_to<Viewport>(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;
Expand Down Expand Up @@ -2682,22 +2711,77 @@ void Viewport::push_input(const Ref<InputEvent> &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<InputEvent> &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<InputEvent> 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<InputEvent> &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<InputEvent> 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<InputEvent> &p_event, bool p_local_coords) {
void Viewport::push_local_unhandled_input(const Ref<InputEvent> &p_event, bool p_local_coords) {
ERR_FAIL_COND(p_event.is_null());
ERR_FAIL_COND(!is_inside_tree());
local_input_handled = false;
Expand Down Expand Up @@ -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);
Expand Down
4 changes: 3 additions & 1 deletion scene/main/viewport.h
Original file line number Diff line number Diff line change
Expand Up @@ -541,7 +541,9 @@ class Viewport : public Node {

void push_text_input(const String &p_text);
void push_input(const Ref<InputEvent> &p_event, bool p_local_coords = false);
void push_unhandled_input(const Ref<InputEvent> &p_event, bool p_local_coords = false);
void push_local_input(const Ref<InputEvent> &p_event, bool p_local_coords = false);
void push_local_gui_input(const Ref<InputEvent> &p_event, bool p_local_coords = false);
void push_local_unhandled_input(const Ref<InputEvent> &p_event, bool p_local_coords = false);

void set_disable_input(bool p_disable);
bool is_input_disabled() const;
Expand Down
5 changes: 1 addition & 4 deletions scene/main/window.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -920,10 +920,7 @@ void Window::_window_input(const Ref<InputEvent> &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) {
Expand Down
4 changes: 2 additions & 2 deletions tests/test_macros.h
Original file line number Diff line number Diff line change
Expand Up @@ -166,15 +166,15 @@ 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(); \
}

#define SEND_GUI_DOUBLE_CLICK(m_object, m_local_pos) \
{ \
_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(); \
}

Expand Down