From 5a746525693d63866ef79884e25a84f687283704 Mon Sep 17 00:00:00 2001 From: Rindbee Date: Sat, 16 Jul 2022 13:23:33 +0800 Subject: [PATCH] Fix getting wrong focus neighbor when the control is in `ScrollContainer` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Exclude controls inside a `ScrollContainer` that are outside the visible area of ​​ the `ScrollContainer` when looking for focus neighbors. --- scene/gui/control.cpp | 30 ++++++++++++++++++++++-------- scene/gui/control.h | 2 +- 2 files changed, 23 insertions(+), 9 deletions(-) diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp index c8318e1d9db1..e81e3c552616 100644 --- a/scene/gui/control.cpp +++ b/scene/gui/control.cpp @@ -35,6 +35,7 @@ #include "core/math/geometry_2d.h" #include "core/os/os.h" #include "core/string/translation_server.h" +#include "scene/gui/scroll_container.h" #include "scene/main/canvas_layer.h" #include "scene/main/window.h" #include "scene/theme/theme_db.h" @@ -2327,7 +2328,9 @@ Control *Control::_get_focus_neighbor(Side p_side, int p_count) { return nullptr; } - _window_find_focus_neighbor(vdir, base, points, maxd, dist, &result); + Rect2 clamp = Rect2(Point2(-1e7, -1e7), Size2(2e7, 2e7)); + + _window_find_focus_neighbor(vdir, base, points, clamp, maxd, dist, &result); return result; } @@ -2336,14 +2339,14 @@ Control *Control::find_valid_focus_neighbor(Side p_side) const { return const_cast(this)->_get_focus_neighbor(p_side); } -void Control::_window_find_focus_neighbor(const Vector2 &p_dir, Node *p_at, const Point2 *p_points, real_t p_min, real_t &r_closest_dist, Control **r_closest) { +void Control::_window_find_focus_neighbor(const Vector2 &p_dir, Node *p_at, const Point2 *p_points, Rect2 &p_clamp, real_t p_min, real_t &r_closest_dist, Control **r_closest) { if (Object::cast_to(p_at)) { return; //bye } Control *c = Object::cast_to(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() == FOCUS_ALL && c->is_visible_in_tree() && p_clamp.intersects(c->get_global_rect())) { Point2 points[4]; Transform2D xform = c->get_global_transform(); @@ -2371,16 +2374,16 @@ void Control::_window_find_focus_neighbor(const Vector2 &p_dir, Node *p_at, cons closest_center = closest_xform.xform(0.5 * closest->get_size()); } - real_t min = 1e7; + real_t max = -1e7; for (int i = 0; i < 4; i++) { real_t d = p_dir.dot(points[i]); - if (d < min) { - min = d; + if (d > max) { + max = d; } } - if (min > (p_min - CMP_EPSILON)) { + if (max > (p_min + CMP_EPSILON)) { for (int i = 0; i < 4; i++) { Vector2 la = p_points[i]; Vector2 lb = p_points[(i + 1) % 4]; @@ -2406,13 +2409,24 @@ void Control::_window_find_focus_neighbor(const Vector2 &p_dir, Node *p_at, cons } } + ScrollContainer *sc = Object::cast_to(c); + Rect2 intersection = p_clamp; + if (sc) { + if (!sc->is_following_focus() || !sc->is_ancestor_of(this)) { + intersection = p_clamp.intersection(sc->get_global_rect()); + if (!intersection.has_area()) { + return; + } + } + } + for (int i = 0; i < p_at->get_child_count(); i++) { Node *child = p_at->get_child(i); Control *childc = Object::cast_to(child); if (childc && childc->data.RI) { continue; //subwindow, ignore } - _window_find_focus_neighbor(p_dir, p_at->get_child(i), p_points, p_min, r_closest_dist, r_closest); + _window_find_focus_neighbor(p_dir, p_at->get_child(i), p_points, intersection, p_min, r_closest_dist, r_closest); } } diff --git a/scene/gui/control.h b/scene/gui/control.h index 23190381f3f4..970f5740ca67 100644 --- a/scene/gui/control.h +++ b/scene/gui/control.h @@ -314,7 +314,7 @@ class Control : public CanvasItem { // Focus. - void _window_find_focus_neighbor(const Vector2 &p_dir, Node *p_at, const Point2 *p_points, real_t p_min, real_t &r_closest_dist, Control **r_closest); + void _window_find_focus_neighbor(const Vector2 &p_dir, Node *p_at, const Point2 *p_points, Rect2 &p_clamp, real_t p_min, real_t &r_closest_dist, Control **r_closest); Control *_get_focus_neighbor(Side p_side, int p_count = 0); // Theming.