diff options
author | Doris Verria <doris.verria@qt.io> | 2024-03-05 09:23:35 +0100 |
---|---|---|
committer | Doris Verria <doris.verria@qt.io> | 2024-03-16 19:10:56 +0100 |
commit | 1863c068191601beba0bcdc970185703c8296e3d (patch) | |
tree | 756fd8f17f6b9cea961472eed1b7811e938a561e /src/quick | |
parent | a67e6bd1ad2358deebcee5bfe7d5991980693032 (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.cpp | 20 |
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) |