Skip to content

Commit

Permalink
Propagate input handled state from SubViewport automatically.
Browse files Browse the repository at this point in the history
Deprecate `Viewport::handle_input_locally` in favor of
- an automatism in `SubViewportContainer`, that propagates the event
handled state to the parent viewport
- a parameter in `SubViewportContainer`, that decides, if input events
should be sent to additional child SubViewports, when the event is set to
handled in one of the SubViewports.
  • Loading branch information
Sauermann committed Dec 1, 2024
1 parent 893bbdf commit fdd8190
Show file tree
Hide file tree
Showing 6 changed files with 43 additions and 66 deletions.
5 changes: 4 additions & 1 deletion doc/classes/SubViewportContainer.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
A container used for displaying the contents of a [SubViewport].
</brief_description>
<description>
A container that displays the contents of underlying [SubViewport] child nodes. It uses the combined size of the [SubViewport]s as minimum size, unless [member stretch] is enabled.
An interface that displays the contents of underlying [SubViewport] child nodes and handles input event propagation. It uses the combined size of the [SubViewport]s as minimum size, unless [member stretch] is enabled.
[b]Note:[/b] Changing a [SubViewportContainer]'s [member Control.scale] will cause its contents to appear distorted. To change its visual size without causing distortion, adjust the node's margins instead (if it's not already in a container).
[b]Note:[/b] The [SubViewportContainer] forwards mouse-enter and mouse-exit notifications to its sub-viewports.
</description>
Expand All @@ -26,6 +26,9 @@
If [code]false[/code], the [Control] nodes inside its [SubViewport] children are considered as targets.
If [code]true[/code], the [SubViewportContainer] itself will be considered as a target.
</member>
<member name="send_input_to_all_viewports" type="bool" setter="set_send_input_to_all_viewports" getter="is_send_input_to_all_viewports_enabled" default="false">
If [code]true[/code], input events will be sent to all child [SubViewport] nodes. If [code]false[/code], when a child [SubViewport] sets an input event to handled, it will not be sent to additional child [SubViewport] nodes.
</member>
<member name="stretch" type="bool" setter="set_stretch" getter="is_stretch_enabled" default="false">
If [code]true[/code], the sub-viewport will be automatically resized to the control's size.
[b]Note:[/b] If [code]true[/code], this will prohibit changing [member SubViewport.size] of its children manually.
Expand Down
7 changes: 2 additions & 5 deletions doc/classes/Viewport.xml
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,6 @@
<description>
Returns whether the current [InputEvent] has been handled. Input events are not handled until [method set_input_as_handled] has been called during the lifetime of an [InputEvent].
This is usually done as part of input handling methods like [method Node._input], [method Control._gui_input] or others, as well as in corresponding signal handlers.
If [member handle_input_locally] is set to [code]false[/code], this method will try finding the first parent viewport that is set to handle input locally, and return its value for [method is_input_handled] instead.
</description>
</method>
<method name="push_input">
Expand Down Expand Up @@ -308,10 +307,8 @@
<member name="gui_snap_controls_to_pixels" type="bool" setter="set_snap_controls_to_pixels" getter="is_snap_controls_to_pixels_enabled" default="true">
If [code]true[/code], the GUI controls on the viewport will lay pixel perfectly.
</member>
<member name="handle_input_locally" type="bool" setter="set_handle_input_locally" getter="is_handling_input_locally" default="true">
If [code]true[/code], this viewport will mark incoming input events as handled by itself. If [code]false[/code], this is instead done by the first parent viewport that is set to handle input locally.
A [SubViewportContainer] will automatically set this property to [code]false[/code] for the [Viewport] contained inside of it.
See also [method set_input_as_handled] and [method is_input_handled].
<member name="handle_input_locally" type="bool" setter="set_handle_input_locally" getter="is_handling_input_locally" default="true" deprecated="Use [member SubViewportContainer.send_input_to_all_viewports] instead.">
This member is not used by the engine and always returns [code]true[/code].
</member>
<member name="mesh_lod_threshold" type="float" setter="set_mesh_lod_threshold" getter="get_mesh_lod_threshold" default="1.0">
The automatic LOD bias to use for meshes rendered within the [Viewport] (this is analogous to [member ReflectionProbe.mesh_lod_threshold]). Higher values will use less detailed versions of meshes that have LOD variations generated. If set to [code]0.0[/code], automatic LOD is disabled. Increase [member mesh_lod_threshold] to improve performance at the cost of geometry detail.
Expand Down
27 changes: 24 additions & 3 deletions scene/gui/subviewport_container.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,14 @@ void SubViewportContainer::set_stretch_shrink(int p_shrink) {
queue_redraw();
}

void SubViewportContainer::set_send_input_to_all_viewports(bool p_enable) {
send_input_event_to_all_viewports = p_enable;
}

bool SubViewportContainer::is_send_input_to_all_viewports_enabled() {
return send_input_event_to_all_viewports;
}

void SubViewportContainer::recalc_force_viewport_sizes() {
if (!stretch) {
return;
Expand Down Expand Up @@ -126,8 +134,6 @@ void SubViewportContainer::_notification(int p_what) {
} else {
c->set_update_mode(SubViewport::UPDATE_DISABLED);
}

c->set_handle_input_locally(false); //do not handle input locally here
}
} break;

Expand Down Expand Up @@ -227,13 +233,24 @@ void SubViewportContainer::gui_input(const Ref<InputEvent> &p_event) {
}

void SubViewportContainer::_send_event_to_viewports(const Ref<InputEvent> &p_event) {
for (int i = 0; i < get_child_count(); i++) {
bool is_handled = false;
// The input order is consistent with the display order, so that the topmost SubViewport receives the input events first.
for (int i = get_child_count() - 1; i >= 0; i--) {
SubViewport *c = Object::cast_to<SubViewport>(get_child(i));
if (!c || c->is_input_disabled()) {
continue;
}

c->push_input(p_event);
if (c->is_input_handled()) {
is_handled = true;
if (!send_input_event_to_all_viewports) {
break;
}
}
}
if (is_handled) {
get_viewport()->set_input_as_handled();
}
}

Expand Down Expand Up @@ -297,9 +314,13 @@ void SubViewportContainer::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_mouse_target", "amount"), &SubViewportContainer::set_mouse_target);
ClassDB::bind_method(D_METHOD("is_mouse_target_enabled"), &SubViewportContainer::is_mouse_target_enabled);

ClassDB::bind_method(D_METHOD("set_send_input_to_all_viewports", "enable"), &SubViewportContainer::set_send_input_to_all_viewports);
ClassDB::bind_method(D_METHOD("is_send_input_to_all_viewports_enabled"), &SubViewportContainer::is_send_input_to_all_viewports_enabled);

ADD_PROPERTY(PropertyInfo(Variant::BOOL, "stretch"), "set_stretch", "is_stretch_enabled");
ADD_PROPERTY(PropertyInfo(Variant::INT, "stretch_shrink", PROPERTY_HINT_RANGE, "1,32,1,or_greater"), "set_stretch_shrink", "get_stretch_shrink");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "mouse_target"), "set_mouse_target", "is_mouse_target_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "send_input_to_all_viewports"), "set_send_input_to_all_viewports", "is_send_input_to_all_viewports_enabled");

GDVIRTUAL_BIND(_propagate_input_event, "event");
}
Expand Down
4 changes: 4 additions & 0 deletions scene/gui/subviewport_container.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ class SubViewportContainer : public Container {
bool stretch = false;
int shrink = 1;
bool mouse_target = false;
bool send_input_event_to_all_viewports = false;

void _notify_viewports(int p_notification);
bool _is_propagated_in_gui_input(const Ref<InputEvent> &p_event);
Expand All @@ -58,6 +59,9 @@ class SubViewportContainer : public Container {
void set_stretch(bool p_enable);
bool is_stretch_enabled() const;

void set_send_input_to_all_viewports(bool p_enable);
bool is_send_input_to_all_viewports_enabled();

virtual void input(const Ref<InputEvent> &p_event) override;
virtual void unhandled_input(const Ref<InputEvent> &p_event) override;
virtual void gui_input(const Ref<InputEvent> &p_event) override;
Expand Down
63 changes: 7 additions & 56 deletions scene/main/viewport.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -750,14 +750,6 @@ void Viewport::_process_picking() {

while (physics_picking_events.size()) {
local_input_handled = false;
if (!handle_input_locally) {
Viewport *vp = this;
while (!Object::cast_to<Window>(vp) && vp->get_parent()) {
vp = vp->get_parent()->get_viewport();
}
vp->local_input_handled = false;
}

Ref<InputEvent> ev = physics_picking_events.front()->get();
physics_picking_events.pop_front();

Expand Down Expand Up @@ -3143,16 +3135,6 @@ void Viewport::push_input(const Ref<InputEvent> &p_event, bool p_local_coords) {
}

local_input_handled = false;
if (!handle_input_locally) {
Viewport *vp = this;
while (true) {
if (Object::cast_to<Window>(vp) || !vp->get_parent()) {
break;
}
vp = vp->get_parent()->get_viewport();
}
vp->local_input_handled = false;
}

Ref<InputEvent> ev;
if (!p_local_coords) {
Expand Down Expand Up @@ -3548,57 +3530,22 @@ void Viewport::gui_cancel_drag() {

void Viewport::set_input_as_handled() {
ERR_MAIN_THREAD_GUARD;
if (!handle_input_locally) {
ERR_FAIL_COND(!is_inside_tree());
Viewport *vp = this;
while (true) {
if (Object::cast_to<Window>(vp)) {
break;
}
if (!vp->get_parent()) {
break;
}
vp = vp->get_parent()->get_viewport();
}
if (vp != this) {
vp->set_input_as_handled();
return;
}
}

local_input_handled = true;
}

bool Viewport::is_input_handled() const {
ERR_READ_THREAD_GUARD_V(false);
if (!handle_input_locally) {
ERR_FAIL_COND_V(!is_inside_tree(), false);
const Viewport *vp = this;
while (true) {
if (Object::cast_to<Window>(vp)) {
break;
}
if (!vp->get_parent()) {
break;
}
vp = vp->get_parent()->get_viewport();
}
if (vp != this) {
return vp->is_input_handled();
}
}
return local_input_handled;
}

#ifndef DISABLE_DEPRECATED
void Viewport::set_handle_input_locally(bool p_enable) {
ERR_MAIN_THREAD_GUARD;
handle_input_locally = p_enable;
}

bool Viewport::is_handling_input_locally() const {
ERR_READ_THREAD_GUARD_V(false);
return handle_input_locally;
return true; // Now viewports always handle input locally.
}
#endif // DISABLE_DEPRECATED

void Viewport::set_default_canvas_item_texture_filter(DefaultCanvasItemTextureFilter p_filter) {
ERR_MAIN_THREAD_GUARD;
Expand Down Expand Up @@ -4719,8 +4666,10 @@ void Viewport::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_input_as_handled"), &Viewport::set_input_as_handled);
ClassDB::bind_method(D_METHOD("is_input_handled"), &Viewport::is_input_handled);

#ifndef DISABLE_DEPRECATED
ClassDB::bind_method(D_METHOD("set_handle_input_locally", "enable"), &Viewport::set_handle_input_locally);
ClassDB::bind_method(D_METHOD("is_handling_input_locally"), &Viewport::is_handling_input_locally);
#endif // DISABLE_DEPRECATED

ClassDB::bind_method(D_METHOD("set_default_canvas_item_texture_filter", "mode"), &Viewport::set_default_canvas_item_texture_filter);
ClassDB::bind_method(D_METHOD("get_default_canvas_item_texture_filter"), &Viewport::get_default_canvas_item_texture_filter);
Expand Down Expand Up @@ -4801,7 +4750,9 @@ void Viewport::_bind_methods() {
#endif // _3D_DISABLED
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "world_2d", PROPERTY_HINT_RESOURCE_TYPE, "World2D", PROPERTY_USAGE_NONE), "set_world_2d", "get_world_2d");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "transparent_bg"), "set_transparent_background", "has_transparent_background");
#ifndef DISABLE_DEPRECATED
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "handle_input_locally"), "set_handle_input_locally", "is_handling_input_locally");
#endif // DISABLE_DEPRECATED
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "snap_2d_transforms_to_pixel"), "set_snap_2d_transforms_to_pixel", "is_snap_2d_transforms_to_pixel_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "snap_2d_vertices_to_pixel"), "set_snap_2d_vertices_to_pixel", "is_snap_2d_vertices_to_pixel_enabled");
ADD_GROUP("Rendering", "");
Expand Down
3 changes: 2 additions & 1 deletion scene/main/viewport.h
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,6 @@ class Viewport : public Node {
Transform3D physics_last_camera_transform;
ObjectID physics_last_id;

bool handle_input_locally = true;
bool local_input_handled = false;

Ref<World2D> world_2d;
Expand Down Expand Up @@ -623,8 +622,10 @@ class Viewport : public Node {
void set_input_as_handled();
bool is_input_handled() const;

#ifndef DISABLE_DEPRECATED
void set_handle_input_locally(bool p_enable);
bool is_handling_input_locally() const;
#endif // DISABLE_DEPRECATED

bool gui_is_dragging() const;
bool gui_is_drag_successful() const;
Expand Down

0 comments on commit fdd8190

Please sign in to comment.