aboutsummaryrefslogtreecommitdiffstats
path: root/src/quick
diff options
context:
space:
mode:
authorDoris Verria <doris.verria@qt.io>2024-03-05 09:23:35 +0100
committerDoris Verria <doris.verria@qt.io>2024-03-16 19:10:56 +0100
commit1863c068191601beba0bcdc970185703c8296e3d (patch)
tree756fd8f17f6b9cea961472eed1b7811e938a561e /src/quick
parenta67e6bd1ad2358deebcee5bfe7d5991980693032 (diff)
Prevent infinite loop when setting activeFocusItem of the scene's root
When we forceActiveFocus on a QQuickItem we set focus to all ancestor focus scopes, in their parent scope. That involves setting 'item' as the subFocusItem of its immediate focus ancestor. In Qt Quick 3D, where we have different subscenes, we follow the same approach as mentioned above, but at the same time, we would set the item that is getting focus as the activeFocusItem of the scene’s root item and by so doing, we would set the subFocusItem of all focus ancestors of that item to 'item'. This is not entirely correct since each focus ancestor's subFocusItem must be its nearest descendant with focus. But we would then proceed to go up the focus hierarchy of the item and this time try to set its ancestor as the activeFocusItem of the scene’s root, "undoing" what we had done in the previous step. This would cause a lot of unnecessary clearing and setting of the subFocusItem as well as focusIn/out events, and sometimes even infinite loops like in the case of compound controls, like the spinbox, which gives active focus to its textField content once it gains focus. To fix, don't change the subFocusItem of the scene's root item unless we have traversed up all the focus hierarchy. Fixes: QTBUG-120542 Pick-to: 6.5 6.6 6.7 Change-Id: Icf08edd2ff8b64662011f66c7137b44750cda9f2 Reviewed-by: Mitch Curtis <mitch.curtis@qt.io> Reviewed-by: Shawn Rutledge <shawn.rutledge@qt.io>
Diffstat (limited to 'src/quick')
-rw-r--r--src/quick/util/qquickdeliveryagent.cpp20
1 files changed, 20 insertions, 0 deletions
diff --git a/src/quick/util/qquickdeliveryagent.cpp b/src/quick/util/qquickdeliveryagent.cpp
index baccd770fb..658a90182f 100644
--- a/src/quick/util/qquickdeliveryagent.cpp
+++ b/src/quick/util/qquickdeliveryagent.cpp
@@ -315,6 +315,16 @@ static inline bool windowHasFocus(QQuickWindow *win)
return win == focusWindow || QQuickRenderControlPrivate::isRenderWindowFor(win, focusWindow) || !focusWindow;
}
+static QQuickItem *findFurthestFocusScopeAncestor(QQuickItem *item)
+{
+ QQuickItem *parentItem = item->parentItem();
+
+ if (parentItem && parentItem->flags() & QQuickItem::ItemIsFocusScope)
+ return findFurthestFocusScopeAncestor(parentItem);
+
+ return item;
+}
+
#ifdef Q_OS_WEBOS
// Temporary fix for webOS until multi-seat is implemented see QTBUG-85272
static inline bool singleWindowOnScreen(QQuickWindow *win)
@@ -457,6 +467,16 @@ void QQuickDeliveryAgentPrivate::setFocusInScope(QQuickItem *scope, QQuickItem *
if (isSubsceneAgent) {
auto da = QQuickWindowPrivate::get(rootItem->window())->deliveryAgent;
qCDebug(lcFocus) << " delegating setFocusInScope to" << da;
+
+ // When setting subFocusItem, hierarchy is important. Each focus ancestor's
+ // subFocusItem must be its nearest descendant with focus. Changing the rootItem's
+ // subFocusItem to 'item' here would make 'item' the subFocusItem of all ancestor
+ // focus scopes up until root item.
+ // That is why we should avoid altering subFocusItem until having traversed
+ // all the focus hierarchy.
+ QQuickItem *ancestorFS = findFurthestFocusScopeAncestor(item);
+ if (ancestorFS != item)
+ options |= QQuickDeliveryAgentPrivate::DontChangeSubFocusItem;
QQuickWindowPrivate::get(rootItem->window())->deliveryAgentPrivate()->setFocusInScope(da->rootItem(), item, reason, options);
}
if (oldActiveFocusItem == activeFocusItem)