Skip to content

Commit

Permalink
Implement properties that recursively disables children's focus & mou…
Browse files Browse the repository at this point in the history
…se filter.
  • Loading branch information
Delsin-Yu committed Dec 21, 2024
1 parent a7a2a12 commit e1b6e7b
Show file tree
Hide file tree
Showing 8 changed files with 211 additions and 20 deletions.
27 changes: 27 additions & 0 deletions doc/classes/Control.xml
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,12 @@
Returns [member offset_right] and [member offset_bottom].
</description>
</method>
<method name="get_focus_mode_with_recursive" qualifiers="const">
<return type="int" enum="Control.FocusMode" />
<description>
Similar to the getter of [member focus_mode], but takes parent's [member focus_recursive_behavior] into account.
</description>
</method>
<method name="get_focus_neighbor" qualifiers="const">
<return type="NodePath" />
<param index="0" name="side" type="int" enum="Side" />
Expand All @@ -416,6 +422,12 @@
Returns the minimum size for this control. See [member custom_minimum_size].
</description>
</method>
<method name="get_mouse_filter_with_recursive" qualifiers="const">
<return type="int" enum="Control.MouseFilter" />
<description>
Similar to to the getter of [member mouse_filter], but takes parent's [member mouse_recursive_behavior] into account.
</description>
</method>
<method name="get_offset" qualifiers="const">
<return type="float" />
<param index="0" name="offset" type="int" enum="Side" />
Expand Down Expand Up @@ -973,6 +985,9 @@
Tells Godot which node it should give focus to if the user presses [kbd]Shift + Tab[/kbd] on a keyboard by default. You can change the key by editing the [member ProjectSettings.input/ui_focus_prev] input action.
If this property is not set, Godot will select a "best guess" based on surrounding nodes in the scene tree.
</member>
<member name="focus_recursive_behavior" type="int" setter="set_focus_recursive_behavior" getter="get_focus_recursive_behavior" enum="Control.RecursiveBehavior" default="0">
Controls whether the recursive child nodes should have their [member focus_mode] overridden to [constant FOCUS_NONE] when evaluating input.
</member>
<member name="global_position" type="Vector2" setter="_set_global_position" getter="get_global_position">
The node's global position, relative to the world (usually to the [CanvasLayer]).
</member>
Expand Down Expand Up @@ -1001,6 +1016,9 @@
You should disable it on the root of your UI if you do not want scroll events to go to the [method Node._unhandled_input] processing.
[b]Note:[/b] Because this property defaults to [code]true[/code], this allows nested scrollable containers to work out of the box.
</member>
<member name="mouse_recursive_behavior" type="int" setter="set_mouse_recursive_behavior" getter="get_mouse_recursive_behavior" enum="Control.RecursiveBehavior" default="0">
Controls whether the recursive child nodes should have their [member mouse_filter] overridden to [constant MOUSE_FILTER_IGNORE] when evaluating input.
</member>
<member name="offset_bottom" type="float" setter="set_offset" getter="get_offset" default="0.0">
Distance between the node's bottom edge and its parent control, based on [member anchor_bottom].
Offsets are often controlled by one or multiple parent [Container] nodes, so you should not modify them manually if your node is a direct child of a [Container]. Offsets update automatically when you move or resize the node.
Expand Down Expand Up @@ -1156,6 +1174,15 @@
<constant name="FOCUS_ALL" value="2" enum="FocusMode">
The node can grab focus on mouse click, using the arrows and the Tab keys on the keyboard, or using the D-pad buttons on a gamepad. Use with [member focus_mode].
</constant>
<constant name="RECURSIVE_BEHAVIOR_INHERITED" value="0" enum="RecursiveBehavior">
Inherits the associated behavior from the control's parent. This is the default for any newly created control.
</constant>
<constant name="RECURSIVE_BEHAVIOR_DISABLED" value="1" enum="RecursiveBehavior">
The current control and all its recursive child controls have their associated behavior disabled, regardless of the parent control's configuration.
</constant>
<constant name="RECURSIVE_BEHAVIOR_ENABLED" value="2" enum="RecursiveBehavior">
The current control and all its recursive child controls have their associated behavior enabled, regardless of the parent control's configuration.
</constant>
<constant name="NOTIFICATION_RESIZED" value="40">
Sent when the node changes size. Use [member size] to get the new size.
</constant>
Expand Down
2 changes: 1 addition & 1 deletion editor/plugins/tiles/tile_set_atlas_source_editor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2758,7 +2758,7 @@ TileSetAtlasSourceEditor::~TileSetAtlasSourceEditor() {

void EditorPropertyTilePolygon::_add_focusable_children(Node *p_node) {
Control *control = Object::cast_to<Control>(p_node);
if (control && control->get_focus_mode() != Control::FOCUS_NONE) {
if (control && control->get_focus_mode_with_recursive() != Control::FOCUS_NONE) {
add_focusable(control);
}
for (int i = 0; i < p_node->get_child_count(); i++) {
Expand Down
155 changes: 147 additions & 8 deletions scene/gui/control.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1866,6 +1866,40 @@ Control::MouseFilter Control::get_mouse_filter() const {
return data.mouse_filter;
}

Control::MouseFilter Control::get_mouse_filter_with_recursive() const {
ERR_READ_THREAD_GUARD_V(MOUSE_FILTER_IGNORE);
if (_is_parent_mouse_disabled()) {
return MOUSE_FILTER_IGNORE;
}
return data.mouse_filter;
}

void Control::set_mouse_recursive_behavior(const RecursiveBehavior p_recursive_mouse_behavior) {
ERR_MAIN_THREAD_GUARD;
ERR_FAIL_INDEX((int)p_recursive_mouse_behavior, 4);
data.mouse_recursive_behavior = p_recursive_mouse_behavior;
if (p_recursive_mouse_behavior == RECURSIVE_BEHAVIOR_INHERITED) {
Control *parent = get_parent_control();
if(parent){
_apply_mouse_behavior_recursively(parent->data.parent_mouse_recursive_behavior, false);
}
else{
_apply_mouse_behavior_recursively(RECURSIVE_BEHAVIOR_ENABLED, false);
}
} else {
_apply_mouse_behavior_recursively(p_recursive_mouse_behavior, false);
}

if (get_viewport()) {
get_viewport()->_gui_update_mouse_over();
}
}

Control::RecursiveBehavior Control::get_mouse_recursive_behavior() const {
ERR_READ_THREAD_GUARD_V(RECURSIVE_BEHAVIOR_INHERITED);
return data.mouse_recursive_behavior;
}

void Control::set_force_pass_scroll_events(bool p_force_pass_scroll_events) {
ERR_MAIN_THREAD_GUARD;
data.force_pass_scroll_events = p_force_pass_scroll_events;
Expand Down Expand Up @@ -2014,6 +2048,36 @@ Control::FocusMode Control::get_focus_mode() const {
return data.focus_mode;
}

Control::FocusMode Control::get_focus_mode_with_recursive() const {
ERR_READ_THREAD_GUARD_V(FOCUS_NONE);
if (_is_focus_disabled_recursively()) {
return FOCUS_NONE;
}
return data.focus_mode;
}

void Control::set_focus_recursive_behavior(RecursiveBehavior p_recursive_focus_behavior) {
ERR_MAIN_THREAD_GUARD;
ERR_FAIL_INDEX((int)p_recursive_focus_behavior, 4);
data.focus_recursive_behavior = p_recursive_focus_behavior;
if (p_recursive_focus_behavior == RECURSIVE_BEHAVIOR_INHERITED) {
Control *parent = get_parent_control();
if(parent){
_apply_focus_behavior_recursively(parent->data.parent_focus_recursive_behavior, false);
}
else{
_apply_focus_behavior_recursively(RECURSIVE_BEHAVIOR_ENABLED, false);
}
} else {
_apply_focus_behavior_recursively(p_recursive_focus_behavior, false);
}
}

Control::RecursiveBehavior Control::get_focus_recursive_behavior() const {
ERR_READ_THREAD_GUARD_V(RECURSIVE_BEHAVIOR_INHERITED);
return data.focus_recursive_behavior;
}

bool Control::has_focus() const {
ERR_READ_THREAD_GUARD_V(false);
return is_inside_tree() && get_viewport()->_gui_control_has_focus(this);
Expand Down Expand Up @@ -2087,7 +2151,7 @@ Control *Control::find_next_valid_focus() const {
ERR_FAIL_NULL_V_MSG(n, nullptr, "Next focus node path is invalid: '" + data.focus_next + "'.");
Control *c = Object::cast_to<Control>(n);
ERR_FAIL_NULL_V_MSG(c, nullptr, "Next focus node is not a control: '" + n->get_name() + "'.");
if (c->is_visible() && c->get_focus_mode() != FOCUS_NONE) {
if (c->is_visible() && c->get_focus_mode_with_recursive() != FOCUS_NONE) {
return c;
}
}
Expand Down Expand Up @@ -2127,10 +2191,10 @@ Control *Control::find_next_valid_focus() const {
}

if (next_child == from || next_child == this) { // No next control.
return (get_focus_mode() == FOCUS_ALL) ? next_child : nullptr;
return (get_focus_mode_with_recursive() == FOCUS_ALL) ? next_child : nullptr;
}
if (next_child) {
if (next_child->get_focus_mode() == FOCUS_ALL) {
if (next_child->get_focus_mode_with_recursive() == FOCUS_ALL) {
return next_child;
}
from = next_child;
Expand Down Expand Up @@ -2174,7 +2238,7 @@ Control *Control::find_prev_valid_focus() const {
ERR_FAIL_NULL_V_MSG(n, nullptr, "Previous focus node path is invalid: '" + data.focus_prev + "'.");
Control *c = Object::cast_to<Control>(n);
ERR_FAIL_NULL_V_MSG(c, nullptr, "Previous focus node is not a control: '" + n->get_name() + "'.");
if (c->is_visible() && c->get_focus_mode() != FOCUS_NONE) {
if (c->is_visible() && c->get_focus_mode_with_recursive() != FOCUS_NONE) {
return c;
}
}
Expand Down Expand Up @@ -2208,10 +2272,10 @@ Control *Control::find_prev_valid_focus() const {
}

if (prev_child == from || prev_child == this) { // No prev control.
return (get_focus_mode() == FOCUS_ALL) ? prev_child : nullptr;
return (get_focus_mode_with_recursive() == FOCUS_ALL) ? prev_child : nullptr;
}

if (prev_child->get_focus_mode() == FOCUS_ALL) {
if (prev_child->get_focus_mode_with_recursive() == FOCUS_ALL) {
return prev_child;
}

Expand Down Expand Up @@ -2270,7 +2334,7 @@ Control *Control::_get_focus_neighbor(Side p_side, int p_count) {
if (!c->is_visible()) {
valid = false;
}
if (c->get_focus_mode() == FOCUS_NONE) {
if (c->get_focus_mode_with_recursive() == FOCUS_NONE) {
valid = false;
}
if (valid) {
Expand Down Expand Up @@ -2332,6 +2396,65 @@ Control *Control::_get_focus_neighbor(Side p_side, int p_count) {
return result;
}

bool Control::_is_focus_disabled_recursively() const {
switch (data.focus_recursive_behavior) {
case RECURSIVE_BEHAVIOR_INHERITED:
return data.parent_focus_recursive_behavior == RECURSIVE_BEHAVIOR_DISABLED;
case RECURSIVE_BEHAVIOR_DISABLED:
return true;
case RECURSIVE_BEHAVIOR_ENABLED:
return false;
}
return false;
}

void Control::_apply_focus_behavior_recursively(const RecursiveBehavior p_focus_recursive_behavior, bool p_skip_non_inherited) {
if (is_inside_tree() && (data.focus_recursive_behavior == RECURSIVE_BEHAVIOR_DISABLED || (data.focus_recursive_behavior == RECURSIVE_BEHAVIOR_INHERITED && p_focus_recursive_behavior == RECURSIVE_BEHAVIOR_DISABLED)) && has_focus()) {
release_focus();
}

if (p_skip_non_inherited && data.focus_recursive_behavior != RECURSIVE_BEHAVIOR_INHERITED) {
return;
}

data.parent_focus_recursive_behavior = p_focus_recursive_behavior;

for (int i = 0; i < get_child_count(); i++) {
Control *control = Object::cast_to<Control>(get_child(i));
if (control) {
control->_apply_focus_behavior_recursively(p_focus_recursive_behavior, true);
}
}
}

bool Control::_is_parent_mouse_disabled() const {
switch (data.mouse_recursive_behavior) {
case RECURSIVE_BEHAVIOR_INHERITED:
return data.parent_mouse_recursive_behavior == RECURSIVE_BEHAVIOR_DISABLED;
case RECURSIVE_BEHAVIOR_DISABLED:
return true;
case RECURSIVE_BEHAVIOR_ENABLED:
return false;
break;
}
return false;
}

void Control::_apply_mouse_behavior_recursively(const RecursiveBehavior p_mouse_recursive_behavior, bool p_skip_non_inherited) {
if (p_skip_non_inherited && data.mouse_recursive_behavior != RECURSIVE_BEHAVIOR_INHERITED) {
return;
}

data.parent_mouse_recursive_behavior = p_mouse_recursive_behavior;

for (int i = 0; i < get_child_count(); i++) {
Control *control = Object::cast_to<Control>(get_child(i));
if (control) {
control->_apply_mouse_behavior_recursively(p_mouse_recursive_behavior, true);
}
}
}

Control *Control::find_valid_focus_neighbor(Side p_side) const {
return const_cast<Control *>(this)->_get_focus_neighbor(p_side);
}
Expand All @@ -2343,7 +2466,7 @@ void Control::_window_find_focus_neighbor(const Vector2 &p_dir, Node *p_at, cons

Control *c = Object::cast_to<Control>(p_at);

if (c && c != this && c->get_focus_mode() == FOCUS_ALL && c->is_visible_in_tree()) {
if (c && c != this && c->get_focus_mode_with_recursive() == FOCUS_ALL && c->is_visible_in_tree()) {
Point2 points[4];

Transform2D xform = c->get_global_transform();
Expand Down Expand Up @@ -3233,6 +3356,9 @@ void Control::_notification(int p_notification) {
data.theme_owner->assign_theme_on_parented(this);

_update_layout_mode();

set_focus_recursive_behavior(data.focus_recursive_behavior);
set_mouse_recursive_behavior(data.mouse_recursive_behavior);
} break;

case NOTIFICATION_UNPARENTED: {
Expand Down Expand Up @@ -3453,6 +3579,9 @@ void Control::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_global_rect"), &Control::get_global_rect);
ClassDB::bind_method(D_METHOD("set_focus_mode", "mode"), &Control::set_focus_mode);
ClassDB::bind_method(D_METHOD("get_focus_mode"), &Control::get_focus_mode);
ClassDB::bind_method(D_METHOD("get_focus_mode_with_recursive"), &Control::get_focus_mode_with_recursive);
ClassDB::bind_method(D_METHOD("set_focus_recursive_behavior", "focus_recursive_behavior"), &Control::set_focus_recursive_behavior);
ClassDB::bind_method(D_METHOD("get_focus_recursive_behavior"), &Control::get_focus_recursive_behavior);
ClassDB::bind_method(D_METHOD("has_focus"), &Control::has_focus);
ClassDB::bind_method(D_METHOD("grab_focus"), &Control::grab_focus);
ClassDB::bind_method(D_METHOD("release_focus"), &Control::release_focus);
Expand Down Expand Up @@ -3548,6 +3677,10 @@ void Control::_bind_methods() {

ClassDB::bind_method(D_METHOD("set_mouse_filter", "filter"), &Control::set_mouse_filter);
ClassDB::bind_method(D_METHOD("get_mouse_filter"), &Control::get_mouse_filter);
ClassDB::bind_method(D_METHOD("get_mouse_filter_with_recursive"), &Control::get_mouse_filter_with_recursive);

ClassDB::bind_method(D_METHOD("set_mouse_recursive_behavior", "mouse_recursive_behavior"), &Control::set_mouse_recursive_behavior);
ClassDB::bind_method(D_METHOD("get_mouse_recursive_behavior"), &Control::get_mouse_recursive_behavior);

ClassDB::bind_method(D_METHOD("set_force_pass_scroll_events", "force_pass_scroll_events"), &Control::set_force_pass_scroll_events);
ClassDB::bind_method(D_METHOD("is_force_pass_scroll_events"), &Control::is_force_pass_scroll_events);
Expand Down Expand Up @@ -3644,9 +3777,11 @@ void Control::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "focus_next", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Control"), "set_focus_next", "get_focus_next");
ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "focus_previous", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Control"), "set_focus_previous", "get_focus_previous");
ADD_PROPERTY(PropertyInfo(Variant::INT, "focus_mode", PROPERTY_HINT_ENUM, "None,Click,All"), "set_focus_mode", "get_focus_mode");
ADD_PROPERTY(PropertyInfo(Variant::INT, "focus_recursive_behavior", PROPERTY_HINT_ENUM, "Inherited,Disabled,Enabled"), "set_focus_recursive_behavior", "get_focus_recursive_behavior");

ADD_GROUP("Mouse", "mouse_");
ADD_PROPERTY(PropertyInfo(Variant::INT, "mouse_filter", PROPERTY_HINT_ENUM, "Stop,Pass (Propagate Up),Ignore"), "set_mouse_filter", "get_mouse_filter");
ADD_PROPERTY(PropertyInfo(Variant::INT, "mouse_recursive_behavior", PROPERTY_HINT_ENUM, "Inherited,Disabled,Enabled"), "set_mouse_recursive_behavior", "get_mouse_recursive_behavior");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "mouse_force_pass_scroll_events"), "set_force_pass_scroll_events", "is_force_pass_scroll_events");
ADD_PROPERTY(PropertyInfo(Variant::INT, "mouse_default_cursor_shape", PROPERTY_HINT_ENUM, "Arrow,I-Beam,Pointing Hand,Cross,Wait,Busy,Drag,Can Drop,Forbidden,Vertical Resize,Horizontal Resize,Secondary Diagonal Resize,Main Diagonal Resize,Move,Vertical Split,Horizontal Split,Help"), "set_default_cursor_shape", "get_default_cursor_shape");

Expand All @@ -3661,6 +3796,10 @@ void Control::_bind_methods() {
BIND_ENUM_CONSTANT(FOCUS_CLICK);
BIND_ENUM_CONSTANT(FOCUS_ALL);

BIND_ENUM_CONSTANT(RECURSIVE_BEHAVIOR_INHERITED);
BIND_ENUM_CONSTANT(RECURSIVE_BEHAVIOR_DISABLED);
BIND_ENUM_CONSTANT(RECURSIVE_BEHAVIOR_ENABLED);

BIND_CONSTANT(NOTIFICATION_RESIZED);
BIND_CONSTANT(NOTIFICATION_MOUSE_ENTER);
BIND_CONSTANT(NOTIFICATION_MOUSE_EXIT);
Expand Down
Loading

0 comments on commit e1b6e7b

Please sign in to comment.