From 3f3787a6b4f54f2603598e49eeebb58532aa2afa Mon Sep 17 00:00:00 2001 From: Doris Verria Date: Wed, 21 Feb 2024 15:28:18 +0100 Subject: Give focus to parent window when focus chain wraps If the focus chain wraps when tabbing (prev == last && next == first), we need to give a chance to the parent window to gain focus so that the focus can be passed to the next/prev object in the focus chain. To give focus to the prev/next target, override new virtual setFocusToTarget for QQuickWindowPrivate. Task-number: QTBUG-121789 Change-Id: Ibe91af53ca622e7fe2b7fc662a95f1a5d7cb479b Reviewed-by: Axel Spoerl --- src/quick/items/qquickitem.cpp | 28 ++++++++++++++++++++++++++-- src/quick/items/qquickitem_p.h | 2 +- src/quick/items/qquickwindow.cpp | 29 +++++++++++++++++++++++++++++ src/quick/items/qquickwindow_p.h | 1 + 4 files changed, 57 insertions(+), 3 deletions(-) (limited to 'src/quick') diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp index a9a5c4c4a2..75037079fb 100644 --- a/src/quick/items/qquickitem.cpp +++ b/src/quick/items/qquickitem.cpp @@ -2470,11 +2470,27 @@ bool QQuickItemPrivate::canAcceptTabFocus(QQuickItem *item) */ bool QQuickItemPrivate::focusNextPrev(QQuickItem *item, bool forward) { - QQuickItem *next = QQuickItemPrivate::nextPrevItemInTabFocusChain(item, forward); + QQuickWindow *window = item->window(); + const bool wrap = !window || window->isTopLevel(); + + QQuickItem *next = QQuickItemPrivate::nextPrevItemInTabFocusChain(item, forward, wrap); if (next == item) return false; + if (!wrap && !next) { + // Focus chain wrapped and we are not top-level window + // Give focus to parent window + Q_ASSERT(window); + Q_ASSERT(window->parent()); + + qt_window_private(window->parent())->setFocusToTarget( + forward ? QWindowPrivate::FocusTarget::Next + : QWindowPrivate::FocusTarget::Prev); + window->parent()->requestActivate(); + return true; + } + next->forceActiveFocus(forward ? Qt::TabFocusReason : Qt::BacktabFocusReason); return true; @@ -2524,7 +2540,7 @@ QQuickItem *QQuickItemPrivate::prevTabChildItem(const QQuickItem *item, int star return nullptr; } -QQuickItem* QQuickItemPrivate::nextPrevItemInTabFocusChain(QQuickItem *item, bool forward) +QQuickItem* QQuickItemPrivate::nextPrevItemInTabFocusChain(QQuickItem *item, bool forward, bool wrap) { Q_ASSERT(item); qCDebug(lcFocus) << "QQuickItemPrivate::nextPrevItemInTabFocusChain: item:" << item << ", forward:" << forward; @@ -2618,6 +2634,14 @@ QQuickItem* QQuickItemPrivate::nextPrevItemInTabFocusChain(QQuickItem *item, boo } current = parent; } else if (hasChildren) { + if (!wrap && (forward || firstFromItem != from)) { + qCDebug(lcFocus) << "QQuickItemPrivate::nextPrevItemInTabFocusChain:" + << "Focus chain about to wrap."; + // If focus chain wraps, we should give the parent window + // a chance to get focus, so we should stop here + return nullptr; + } + // Wrap around after checking all items forward if (forward) { current = firstChild; diff --git a/src/quick/items/qquickitem_p.h b/src/quick/items/qquickitem_p.h index c2e014b72d..bb238904ca 100644 --- a/src/quick/items/qquickitem_p.h +++ b/src/quick/items/qquickitem_p.h @@ -575,7 +575,7 @@ public: static bool focusNextPrev(QQuickItem *item, bool forward); static QQuickItem *nextTabChildItem(const QQuickItem *item, int start); static QQuickItem *prevTabChildItem(const QQuickItem *item, int start); - static QQuickItem *nextPrevItemInTabFocusChain(QQuickItem *item, bool forward); + static QQuickItem *nextPrevItemInTabFocusChain(QQuickItem *item, bool forward, bool wrap = true); static bool canAcceptTabFocus(QQuickItem *item); diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index b8c7878e04..09f938a473 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -1853,6 +1853,35 @@ void QQuickWindowPrivate::clearFocusObject() da->clearFocusObject(); } +void QQuickWindowPrivate::setFocusToTarget(FocusTarget target) +{ + QQuickItem *newFocusItem = nullptr; + if (contentItem) { + switch (target) { + case FocusTarget::First: + newFocusItem = QQuickItemPrivate::nextPrevItemInTabFocusChain(contentItem, true); + break; + case FocusTarget::Last: + newFocusItem = QQuickItemPrivate::nextPrevItemInTabFocusChain(contentItem, false); + break; + case FocusTarget::Next: + case FocusTarget::Prev: { + auto da = deliveryAgentPrivate(); + Q_ASSERT(da); + QQuickItem *focusItem = da->focusTargetItem() ? da->focusTargetItem() : contentItem; + bool forward = (target == FocusTarget::Next); + newFocusItem = QQuickItemPrivate::nextPrevItemInTabFocusChain(focusItem, forward); + break; + } + default: + break; + } + } + + if (newFocusItem) + newFocusItem->setFocus(true, Qt::ActiveWindowFocusReason); +} + /*! \qmlproperty list Window::data \qmldefault diff --git a/src/quick/items/qquickwindow_p.h b/src/quick/items/qquickwindow_p.h index 1b1b12de65..3a70fda3d2 100644 --- a/src/quick/items/qquickwindow_p.h +++ b/src/quick/items/qquickwindow_p.h @@ -146,6 +146,7 @@ public: #endif void clearFocusObject() override; + void setFocusToTarget(QWindowPrivate::FocusTarget) override; void dirtyItem(QQuickItem *); void cleanup(QSGNode *); -- cgit v1.2.3