Behavior fix when creating QML windows
authorFabian Bumberger <fbumberger@rim.com>
Sat, 18 Jan 2014 15:45:33 +0000 (16:45 +0100)
committerThe Qt Project <gerrit-noreply@qt-project.org>
Wed, 19 Mar 2014 18:13:35 +0000 (19:13 +0100)
When a QML window is created it is set visible after the QQuickWindowQmlImpl component is complete.
This works fine for a single window, but because componentCompleted is called first for the last
created windows, the behavior is not as the user might expect (and different compared to
version 2.0 of the QML Window API). One of the results is e.g. that a  window which is created
as a child object in QML will have a lower z-Order than the parent. On some platforms (e.g. BlackBerry)
an even bigger problem arises because the first created window acts as a container for
the whole application and is always shown fullscreen. On other platforms (Linux) the initial window
position and the window focus ares not set correctly.

This patch postpones showing windows until the "transientParent" is visible.

[Changelog][QtQuick] Making a QtQuick Window visible is postponed till its transient parent is visible

Task-number: QTBUG-37440

Change-Id: I09a94ff038c066a5d3298c6c103dafde50bef1fa
Reviewed-by: Shawn Rutledge <shawn.rutledge@digia.com>
src/quick/items/qquickwindowmodule.cpp
tests/auto/quick/qquickwindow/data/active.qml
tests/auto/quick/qquickwindow/data/windoworder.qml [new file with mode: 0644]
tests/auto/quick/qquickwindow/qquickwindow.pro
tests/auto/quick/qquickwindow/tst_qquickwindow.cpp

index f6c32dcac366002f377c112d800b9e52ff53dd93..3bd62d21db069bec7e319827d6fcfc4e944e5f46 100644 (file)
@@ -73,7 +73,7 @@ public:
     void setVisible(bool visible) {
         if (!m_complete)
             m_visible = visible;
-        else
+        else if (!transientParent() || transientParent()->isVisible())
             QQuickWindow::setVisible(visible);
     }
 
@@ -101,6 +101,24 @@ protected:
 
     void componentComplete() {
         m_complete = true;
+        if (transientParent() && !transientParent()->isVisible()) {
+            connect(transientParent(), &QQuickWindow::visibleChanged, this,
+                    &QQuickWindowQmlImpl::setWindowVisibility, Qt::QueuedConnection);
+        } else {
+            setWindowVisibility();
+        }
+    }
+
+private Q_SLOTS:
+    void setWindowVisibility()
+    {
+        if (transientParent() && !transientParent()->isVisible())
+            return;
+
+        if (sender()) {
+            disconnect(transientParent(), &QWindow::visibleChanged, this,
+                       &QQuickWindowQmlImpl::setWindowVisibility);
+        }
 
         // We have deferred window creation until we have the full picture of what
         // the user wanted in terms of window state, geometry, visibility, etc.
index af0b7edeb2fca4c019689801852cc96ec5c8d499..4d47225b4ee3b70a13492b83f90937eb1e7ff284 100644 (file)
@@ -14,7 +14,6 @@ Window {
             anchors.fill: parent;
             onPressed: window2.requestActivate();
         }
-        Component.onCompleted: window2.show();
     }
 
     Window {
@@ -22,6 +21,7 @@ Window {
         objectName: "window2";
         color: "#FF0000";
         width: 100; height: 100;
+        visible: true
         Item {
             width: 100; height: 100;
         }
diff --git a/tests/auto/quick/qquickwindow/data/windoworder.qml b/tests/auto/quick/qquickwindow/data/windoworder.qml
new file mode 100644 (file)
index 0000000..33aea95
--- /dev/null
@@ -0,0 +1,42 @@
+import QtQuick 2.1
+import QtQuick.Window 2.1
+
+Window {
+    id: window1;
+    objectName: "window1";
+    width: 100; height: 100;
+    visible: true
+    color: "blue"
+    property alias win2: window2
+    property alias win3: window3
+    property alias win4: window4
+    property alias win5: window5
+    Window {
+        id: window2;
+        objectName: "window2";
+        width: 100; height: 100;
+        visible: true
+        color: "green"
+        Window {
+            id: window3;
+            objectName: "window3";
+            width: 100; height: 100;
+            visible: true
+        }
+
+        Window { //Is invisible by default
+            id: window4
+            objectName: "window4";
+            height: 200
+            width: 200
+            color: "black"
+            Window {
+                id: window5
+                objectName: "window5";
+                height: 200
+                width: 200
+                visible: true
+            }
+        }
+    }
+}
index 46cf96423ff901546d4c73badea43deb08781da9..6bce209df9a73ffc49f11ed3f4095e67bcdcf4d4 100644 (file)
@@ -14,6 +14,7 @@ OTHER_FILES += \
     data/active.qml \
     data/AnimationsWhileHidden.qml \
     data/Headless.qml \
-    data/showHideAnimate.qml
+    data/showHideAnimate.qml \
+    data/windoworder.qml
 
 DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0
index fcab0c7ef2bce31cbe226607537d8f3e5f13705a..b05146fa3a3ba8be9d66e54c9804bdfc83775f30 100644 (file)
@@ -341,6 +341,8 @@ private slots:
 
     void requestActivate();
 
+    void testWindowVisibilityOrder();
+
     void blockClosing();
 
     void crashWhenHoverItemDeleted();
@@ -1589,7 +1591,8 @@ void tst_qquickwindow::requestActivate()
     QVERIFY(windows.at(0)->objectName() == "window2");
 
     window1->show();
-    window1->requestActivate();
+    QVERIFY(QTest::qWaitForWindowExposed(windows.at(0))); //We wait till window 2 comes up
+    window1->requestActivate();                 // and then transfer the focus to window1
 
     QTRY_VERIFY(QGuiApplication::focusWindow() == window1);
     QVERIFY(window1->isActive() == true);
@@ -1615,6 +1618,54 @@ void tst_qquickwindow::requestActivate()
 
     QTRY_VERIFY(QGuiApplication::focusWindow() == windows.at(0));
     QVERIFY(windows.at(0)->isActive());
+    delete window1;
+}
+
+void tst_qquickwindow::testWindowVisibilityOrder()
+{
+    QQmlEngine engine;
+    QQmlComponent component(&engine);
+    component.loadUrl(testFileUrl("windoworder.qml"));
+    QQuickWindow *window1 = qobject_cast<QQuickWindow *>(component.create());
+    QQuickWindow *window2 = window1->property("win2").value<QQuickWindow*>();
+    QQuickWindow *window3 = window1->property("win3").value<QQuickWindow*>();
+    QQuickWindow *window4 = window1->property("win4").value<QQuickWindow*>();
+    QQuickWindow *window5 = window1->property("win5").value<QQuickWindow*>();
+    QVERIFY(window1);
+    QVERIFY(window2);
+    QVERIFY(window3);
+
+    QTest::qWaitForWindowExposed(window3);
+
+    QWindowList windows = QGuiApplication::topLevelWindows();
+    QTRY_COMPARE(windows.size(), 5);
+
+    QVERIFY(window3 == QGuiApplication::focusWindow());
+    QVERIFY(window1->isActive());
+    QVERIFY(window2->isActive());
+    QVERIFY(window3->isActive());
+
+    //Test if window4 is shown 2 seconds after the application startup
+    //with window4 visible window5 (transient child) should also become visible
+    QVERIFY(!window4->isVisible());
+    QVERIFY(!window5->isVisible());
+
+    window4->setVisible(true);
+
+    QTest::qWaitForWindowExposed(window5);
+    QVERIFY(window4->isVisible());
+    QVERIFY(window5->isVisible());
+    window4->hide();
+    window5->hide();
+
+    window3->hide();
+#if defined(Q_OS_OSX)
+    QEXPECT_FAIL("","Focus is not transferred to transient parent on window close (QTBUG-33423)", Continue);
+#endif
+    QTRY_COMPARE(window2 == QGuiApplication::focusWindow(), true);
+
+    window2->hide();
+    QTRY_COMPARE(window1 == QGuiApplication::focusWindow(), true);
 }
 
 void tst_qquickwindow::blockClosing()