aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDoris Verria <doris.verria@qt.io>2024-04-10 19:10:21 +0200
committerQt Cherry-pick Bot <cherrypick_bot@qt-project.org>2024-04-12 11:55:47 +0000
commite31ae115297bfff9f125774aec0a52d2270330fe (patch)
tree71ac374d0944d757bd720d06aea303ae6b63aeb4
parent09c4e111eb65b22ed3479ebf89ccfaaef1cb549c (diff)
Take into account inactive window when restoring focus after popup close
When popup is closing we try to restore the focus to the last activeFocusItem of the window if the popup(item) has active focus. However, it might happen that the window is inactive when the popup closes, and the poup/popupItem don't have activeFocus. Take this into account when deciding if popup should restore focus. Since QQuickWindow doesn't store the focusObject when window loses focus, check if popupItem is focusObject by going down the window's subFocusItem hierarchy. Ideally the window wouldn't clear the focus object when it loses focus but that's a bigger change that requires further investigation. Fixes: QTBUG-84976 Pick-to: 6.5 Change-Id: Ib896cb74fa85d3b6f894a80b84f3ffba0fb9c72e Reviewed-by: Shawn Rutledge <shawn.rutledge@qt.io> Reviewed-by: Mitch Curtis <mitch.curtis@qt.io> Reviewed-by: Santhosh Kumar <santhosh.kumar.selvaraj@qt.io> (cherry picked from commit 2e520c6cae19f472b1053bd572bc488e8e048559) Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
-rw-r--r--src/quicktemplates/qquickpopup.cpp23
-rw-r--r--tests/auto/quickcontrols/qquickpopup/data/activeFocusAfterWindowInactive.qml27
-rw-r--r--tests/auto/quickcontrols/qquickpopup/tst_qquickpopup.cpp48
3 files changed, 96 insertions, 2 deletions
diff --git a/src/quicktemplates/qquickpopup.cpp b/src/quicktemplates/qquickpopup.cpp
index 43bf1f447a..f5db64bcfb 100644
--- a/src/quicktemplates/qquickpopup.cpp
+++ b/src/quicktemplates/qquickpopup.cpp
@@ -633,8 +633,27 @@ bool QQuickPopupPrivate::prepareExitTransition()
if (transitionState != ExitTransition) {
// The setFocus(false) call below removes any active focus before we're
// able to check it in finalizeExitTransition.
- if (!hadActiveFocusBeforeExitTransition)
- hadActiveFocusBeforeExitTransition = popupItem->hasActiveFocus();
+ if (!hadActiveFocusBeforeExitTransition) {
+ const auto hasFocusInRoot = [](QQuickItem *item) {
+ Q_ASSERT(item);
+ if (!item->window() || item->window()->isActive())
+ return item->hasActiveFocus();
+
+ // fallback for when there's no active window
+ const auto *da = QQuickItemPrivate::get(item)->deliveryAgentPrivate();
+ if (!da || !da->rootItem)
+ return false;
+
+ QQuickItem *focusItem = da->rootItem;
+ while (focusItem->isFocusScope() && focusItem->scopedFocusItem())
+ focusItem = focusItem->scopedFocusItem();
+
+ return focusItem == item;
+ };
+
+ hadActiveFocusBeforeExitTransition = hasFocusInRoot(popupItem);
+ }
+
if (focus)
popupItem->setFocus(false, Qt::PopupFocusReason);
transitionState = ExitTransition;
diff --git a/tests/auto/quickcontrols/qquickpopup/data/activeFocusAfterWindowInactive.qml b/tests/auto/quickcontrols/qquickpopup/data/activeFocusAfterWindowInactive.qml
new file mode 100644
index 0000000000..1d4dc87e41
--- /dev/null
+++ b/tests/auto/quickcontrols/qquickpopup/data/activeFocusAfterWindowInactive.qml
@@ -0,0 +1,27 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Controls
+
+ApplicationWindow {
+ width: 400
+ height: 400
+
+ property alias popup: popup
+ property alias button: button
+
+ Button {
+ id: button
+ text: "button"
+ focus: true
+ }
+
+ Popup {
+ id: popup
+ focus: true
+ width: 100
+ height: 100
+ anchors.centerIn: Overlay.overlay
+ }
+}
diff --git a/tests/auto/quickcontrols/qquickpopup/tst_qquickpopup.cpp b/tests/auto/quickcontrols/qquickpopup/tst_qquickpopup.cpp
index aec7391f63..e774d055e1 100644
--- a/tests/auto/quickcontrols/qquickpopup/tst_qquickpopup.cpp
+++ b/tests/auto/quickcontrols/qquickpopup/tst_qquickpopup.cpp
@@ -65,6 +65,7 @@ private slots:
void activeFocusAfterExit();
void activeFocusOnDelayedEnter();
void activeFocusDespiteLowerStackingOrder();
+ void activeFocusItemAfterWindowInactive();
void hover_data();
void hover();
void wheel_data();
@@ -954,6 +955,53 @@ void tst_QQuickPopup::activeFocusDespiteLowerStackingOrder()
QVERIFY(!popup1->hasActiveFocus());
}
+void tst_QQuickPopup::activeFocusItemAfterWindowInactive()
+{
+ if (!hasWindowActivation())
+ QSKIP("Window activation is not supported");
+
+ QQuickControlsApplicationHelper helper(this, QStringLiteral("activeFocusAfterWindowInactive.qml"));
+ QVERIFY2(helper.ready, helper.failureMessage());
+ QQuickApplicationWindow *window = helper.appWindow;
+ window->show();
+ window->requestActivate();
+ QVERIFY(QTest::qWaitForWindowFocused(window));
+
+ QQuickPopup *popup = helper.appWindow->property("popup").value<QQuickPopup*>();
+ QQuickButton *button = helper.appWindow->property("button").value<QQuickButton*>();
+ QVERIFY(popup);
+ QVERIFY(button);
+
+ popup->open();
+ QVERIFY(popup->isVisible());
+ QTRY_VERIFY(popup->isOpened());
+ QVERIFY(popup->hasActiveFocus());
+ QVERIFY(!button->hasActiveFocus());
+
+ popup->close();
+ QVERIFY(!popup->isVisible());
+ QTRY_VERIFY(!popup->isOpened());
+ QVERIFY(button->hasActiveFocus());
+ QCOMPARE(window->activeFocusItem(), button);
+
+ popup->open();
+ QVERIFY(popup->isVisible());
+ QTRY_VERIFY(popup->isOpened());
+
+ QQuickWindow newWindow;
+ newWindow.setTitle("newFocusWindow");
+ newWindow.show();
+ newWindow.requestActivate();
+ QVERIFY(QTest::qWaitForWindowFocused(&newWindow));
+
+ popup->close();
+ QCOMPARE(QGuiApplication::focusWindow(), &newWindow);
+
+ window->requestActivate();
+ QVERIFY(QTest::qWaitForWindowFocused(window));
+ QCOMPARE(window->activeFocusItem(), button);
+}
+
void tst_QQuickPopup::hover_data()
{
QTest::addColumn<QString>("source");