// Copyright (C) 2017 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace QQuickVisualTestUtils; using namespace QQuickControlsTestUtils; class tst_QQuickApplicationWindow : public QQmlDataTest { Q_OBJECT public: tst_QQuickApplicationWindow(); private slots: void qmlCreation(); void activeFocusOnTab1(); void activeFocusOnTab2(); void defaultFocus(); void implicitFill(); void attachedProperties(); void font(); void defaultFont(); void locale(); void activeFocusControl_data(); void activeFocusControl(); void focusAfterPopupClosed(); void clearFocusOnDestruction(); void layout(); void layoutLayout(); void componentComplete(); void opacity(); void backgroundSize(); void explicitBackgroundSizeBinding(); void safeArea(); #if QT_CONFIG(quicktemplates2_hover) void hoverInBackground(); #endif }; tst_QQuickApplicationWindow::tst_QQuickApplicationWindow() : QQmlDataTest(QT_QMLTEST_DATADIR) { QCoreApplication::setAttribute(Qt::AA_DontUseNativeMenuWindows); QCoreApplication::setAttribute(Qt::AA_DontUseNativeMenuBar); QQuickStyle::setStyle("Basic"); } void tst_QQuickApplicationWindow::qmlCreation() { QQmlEngine engine; QQmlComponent component(&engine); component.loadUrl(testFileUrl("basicapplicationwindow.qml")); QObject* created = component.create(); QScopedPointer cleanup(created); QVERIFY(created); QQuickWindow* window = qobject_cast(created); QVERIFY(window); QVERIFY(!window->isVisible()); QCOMPARE(created->property("title"), QVariant("Test Application Window")); QQuickItem* statusBar = qvariant_cast(created->property("statusBar")); QVERIFY(!statusBar); QQuickItem* header = qvariant_cast(created->property("header")); QVERIFY(!header); QQuickItem* footer = qvariant_cast(created->property("footer")); QVERIFY(!footer); } void tst_QQuickApplicationWindow::activeFocusOnTab1() { QQmlEngine engine; QQmlComponent component(&engine); component.loadUrl(testFileUrl("activefocusontab.qml")); QObject* created = component.create(); QScopedPointer cleanup(created); QVERIFY(created); QQuickWindow* window = qobject_cast(created); QVERIFY(window); window->show(); window->requestActivate(); QVERIFY(QTest::qWaitForWindowActive(window)); QVERIFY(QGuiApplication::focusWindow() == window); QQuickItem* contentItem = window->contentItem(); QVERIFY(contentItem); QVERIFY_ACTIVE_FOCUS(contentItem); QQuickItem* item = findItem(window->contentItem(), "sub1"); QVERIFY(item); QVERIFY(!item->hasActiveFocus()); // Tab: contentItem->sub1 { QKeyEvent key(QEvent::KeyPress, Qt::Key_Tab, Qt::NoModifier, "", false, 1); QGuiApplication::sendEvent(window, &key); QVERIFY(key.isAccepted()); item = findItem(window->contentItem(), "sub1"); QVERIFY(item); QVERIFY_ACTIVE_FOCUS(item); } // Tab: sub1->sub2 { QKeyEvent key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::NoModifier, "", false, 1); QGuiApplication::sendEvent(window, &key); QVERIFY(key.isAccepted()); item = findItem(window->contentItem(), "sub2"); QVERIFY(item); QVERIFY_ACTIVE_FOCUS(item); } // Tab: sub2->sub1 { QKeyEvent key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::NoModifier, "", false, 1); QGuiApplication::sendEvent(window, &key); QVERIFY(key.isAccepted()); item = findItem(window->contentItem(), "sub1"); QVERIFY(item); QVERIFY_ACTIVE_FOCUS(item); } } void tst_QQuickApplicationWindow::activeFocusOnTab2() { QQmlEngine engine; QQmlComponent component(&engine); component.loadUrl(testFileUrl("activefocusontab.qml")); QObject* created = component.create(); QScopedPointer cleanup(created); QVERIFY(created); QQuickWindow* window = qobject_cast(created); QVERIFY(window); window->show(); window->requestActivate(); QVERIFY(QTest::qWaitForWindowActive(window)); QVERIFY(QGuiApplication::focusWindow() == window); QQuickItem* contentItem = window->contentItem(); QVERIFY(contentItem); QVERIFY_ACTIVE_FOCUS(contentItem); QQuickItem* item = findItem(window->contentItem(), "sub2"); QVERIFY(item); QVERIFY(!item->hasActiveFocus()); // BackTab: contentItem->sub2 { QKeyEvent key(QEvent::KeyPress, Qt::Key_Tab, Qt::ShiftModifier, "", false, 1); QGuiApplication::sendEvent(window, &key); QVERIFY(key.isAccepted()); item = findItem(window->contentItem(), "sub2"); QVERIFY(item); QVERIFY_ACTIVE_FOCUS(item); } // BackTab: sub2->sub1 { QKeyEvent key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::ShiftModifier, "", false, 1); QGuiApplication::sendEvent(window, &key); QVERIFY(key.isAccepted()); item = findItem(window->contentItem(), "sub1"); QVERIFY(item); QVERIFY_ACTIVE_FOCUS(item); } // BackTab: sub1->sub2 { QKeyEvent key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::ShiftModifier, "", false, 1); QGuiApplication::sendEvent(window, &key); QVERIFY(key.isAccepted()); item = findItem(window->contentItem(), "sub2"); QVERIFY(item); QVERIFY_ACTIVE_FOCUS(item); } } void tst_QQuickApplicationWindow::defaultFocus() { QQmlEngine engine; QQmlComponent component(&engine); component.loadUrl(testFileUrl("defaultFocus.qml")); QObject* created = component.create(); QScopedPointer cleanup(created); Q_UNUSED(cleanup); QVERIFY(created); QQuickWindow* window = qobject_cast(created); QVERIFY(window); window->show(); window->requestActivate(); QVERIFY(QTest::qWaitForWindowActive(window)); QVERIFY(QGuiApplication::focusWindow() == window); QQuickItem* contentItem = window->contentItem(); QVERIFY(contentItem); QVERIFY_ACTIVE_FOCUS(contentItem); // A single item in an ApplicationWindow with focus: true should receive focus. QQuickItem* item = findItem(window->contentItem(), "item"); QVERIFY(item); QVERIFY(item->hasFocus()); QVERIFY_ACTIVE_FOCUS(item); } static QSizeF getExpectedElementSize() { #ifndef Q_OS_ANDROID // These values are taken from the QML file. return QSizeF(400.0, 400.0); #else // On Android we have to query screen parameters at runtime, because on // Android the Quick element will take the whole screen size. const QSize size = QGuiApplication::primaryScreen()->availableSize(); return QSizeF(size); #endif } void tst_QQuickApplicationWindow::implicitFill() { QQmlEngine engine; QQmlComponent component(&engine); component.loadUrl(testFileUrl("fill.qml")); QObject* created = component.create(); QScopedPointer cleanup(created); QVERIFY(created); QQuickWindow* window = qobject_cast(created); QVERIFY(window); QVERIFY(!window->isVisible()); QCOMPARE(window->width(), 400); QCOMPARE(window->height(), 400); window->show(); window->requestActivate(); QVERIFY(QTest::qWaitForWindowActive(window)); const QSizeF expectedSize = getExpectedElementSize(); QQuickItem *stackView = window->property("stackView").value(); QVERIFY(stackView); QCOMPARE(stackView->width(), expectedSize.width()); QCOMPARE(stackView->height(), expectedSize.height()); QQuickItem *nextItem = window->property("nextItem").value(); QVERIFY(nextItem); QVERIFY(QMetaObject::invokeMethod(window, "pushNextItem")); QCOMPARE(nextItem->width(), expectedSize.width()); QCOMPARE(nextItem->height(), expectedSize.height()); } void tst_QQuickApplicationWindow::attachedProperties() { if (QGuiApplication::platformName().startsWith(QLatin1String("eglfs"), Qt::CaseInsensitive)) { QSKIP("This test uses multiple windows and it crashes on EGLFS because of that"); } QQmlEngine engine; QQmlComponent component(&engine); component.loadUrl(testFileUrl("attachedProperties.qml")); QScopedPointer object(component.create()); QVERIFY2(!object.isNull(), qPrintable(component.errorString())); QQuickApplicationWindow *window = qobject_cast(object.data()); QVERIFY(window); QQuickItem *childControl = object->property("childControl").value(); QVERIFY(childControl); QCOMPARE(childControl->property("attached_window").value(), window); QCOMPARE(childControl->property("attached_contentItem").value(), window->contentItem()); QCOMPARE(childControl->property("attached_activeFocusControl").value(), window->activeFocusControl()); QCOMPARE(childControl->property("attached_header").value(), window->header()); QCOMPARE(childControl->property("attached_footer").value(), window->footer()); QQuickItem *childItem = object->property("childItem").value(); QVERIFY(childItem); QCOMPARE(childItem->property("attached_window").value(), window); QCOMPARE(childItem->property("attached_contentItem").value(), window->contentItem()); QCOMPARE(childItem->property("attached_activeFocusControl").value(), window->activeFocusControl()); QCOMPARE(childItem->property("attached_header").value(), window->header()); QCOMPARE(childItem->property("attached_footer").value(), window->footer()); QObject *childObject = object->property("childObject").value(); QVERIFY(childObject); QVERIFY(!childObject->property("attached_window").value()); QVERIFY(!childObject->property("attached_contentItem").value()); QVERIFY(!childObject->property("attached_activeFocusControl").value()); QVERIFY(!childObject->property("attached_header").value()); QVERIFY(!childObject->property("attached_footer").value()); QQuickWindow *childWindow = object->property("childWindow").value(); QVERIFY(childWindow); QVERIFY(!childWindow->property("attached_window").value()); QVERIFY(!childWindow->property("attached_contentItem").value()); QVERIFY(!childWindow->property("attached_activeFocusControl").value()); QVERIFY(!childWindow->property("attached_header").value()); QVERIFY(!childWindow->property("attached_footer").value()); QQuickItem *childWindowControl = object->property("childWindowControl").value(); QVERIFY(childWindowControl); QVERIFY(!childWindowControl->property("attached_window").value()); QVERIFY(!childWindowControl->property("attached_contentItem").value()); QVERIFY(!childWindowControl->property("attached_activeFocusControl").value()); QVERIFY(!childWindowControl->property("attached_header").value()); QVERIFY(!childWindowControl->property("attached_footer").value()); QQuickItem *childWindowItem = object->property("childWindowItem").value(); QVERIFY(childWindowItem); QVERIFY(!childWindowItem->property("attached_window").value()); QVERIFY(!childWindowItem->property("attached_contentItem").value()); QVERIFY(!childWindowItem->property("attached_activeFocusControl").value()); QVERIFY(!childWindowItem->property("attached_header").value()); QVERIFY(!childWindowItem->property("attached_footer").value()); QObject *childWindowObject = object->property("childWindowObject").value(); QVERIFY(childWindowObject); QVERIFY(!childWindowObject->property("attached_window").value()); QVERIFY(!childWindowObject->property("attached_contentItem").value()); QVERIFY(!childWindowObject->property("attached_activeFocusControl").value()); QVERIFY(!childWindowObject->property("attached_header").value()); QVERIFY(!childWindowObject->property("attached_footer").value()); QQuickApplicationWindow *childAppWindow = object->property("childAppWindow").value(); QVERIFY(childAppWindow); QVERIFY(!childAppWindow->property("attached_window").value()); QVERIFY(!childAppWindow->property("attached_contentItem").value()); QVERIFY(!childAppWindow->property("attached_activeFocusControl").value()); QVERIFY(!childAppWindow->property("attached_header").value()); QVERIFY(!childAppWindow->property("attached_footer").value()); QQuickItem *childAppWindowControl = object->property("childAppWindowControl").value(); QVERIFY(childAppWindowControl); QCOMPARE(childAppWindowControl->property("attached_window").value(), childAppWindow); QCOMPARE(childAppWindowControl->property("attached_contentItem").value(), childAppWindow->contentItem()); QCOMPARE(childAppWindowControl->property("attached_activeFocusControl").value(), childAppWindow->activeFocusControl()); QCOMPARE(childAppWindowControl->property("attached_header").value(), childAppWindow->header()); QCOMPARE(childAppWindowControl->property("attached_footer").value(), childAppWindow->footer()); QQuickItem *childAppWindowItem = object->property("childAppWindowItem").value(); QVERIFY(childAppWindowItem); QCOMPARE(childAppWindowItem->property("attached_window").value(), childAppWindow); QCOMPARE(childAppWindowItem->property("attached_contentItem").value(), childAppWindow->contentItem()); QCOMPARE(childAppWindowItem->property("attached_activeFocusControl").value(), childAppWindow->activeFocusControl()); QCOMPARE(childAppWindowItem->property("attached_header").value(), childAppWindow->header()); QCOMPARE(childAppWindowItem->property("attached_footer").value(), childAppWindow->footer()); QObject *childAppWindowObject = object->property("childAppWindowObject").value(); QVERIFY(childAppWindowObject); QVERIFY(!childAppWindowObject->property("attached_window").value()); QVERIFY(!childAppWindowObject->property("attached_contentItem").value()); QVERIFY(!childAppWindowObject->property("attached_activeFocusControl").value()); QVERIFY(!childAppWindowObject->property("attached_header").value()); QVERIFY(!childAppWindowObject->property("attached_footer").value()); window->show(); window->requestActivate(); QVERIFY(QTest::qWaitForWindowActive(window)); QVERIFY(!childControl->hasActiveFocus()); childControl->forceActiveFocus(); QTRY_VERIFY_ACTIVE_FOCUS(childControl); QCOMPARE(window->activeFocusItem(), childControl); QCOMPARE(childControl->property("attached_activeFocusControl").value(), childControl); QQuickItem *header = new QQuickItem; window->setHeader(header); QCOMPARE(window->header(), header); QCOMPARE(childControl->property("attached_header").value(), header); QQuickItem *footer = new QQuickItem; window->setFooter(footer); QCOMPARE(window->footer(), footer); QCOMPARE(childControl->property("attached_footer").value(), footer); childAppWindow->show(); childAppWindow->requestActivate(); QVERIFY(QTest::qWaitForWindowActive(childAppWindow)); QVERIFY(!childAppWindowControl->hasActiveFocus()); childAppWindowControl->forceActiveFocus(); QTRY_VERIFY_ACTIVE_FOCUS(childAppWindowControl); QCOMPARE(childAppWindow->activeFocusItem(), childAppWindowControl); QCOMPARE(childAppWindowControl->property("attached_activeFocusControl").value(), childAppWindowControl); childControl->setParentItem(childAppWindow->contentItem()); QCOMPARE(childControl->window(), childAppWindow); QCOMPARE(childControl->property("attached_window").value(), childAppWindow); QCOMPARE(childControl->property("attached_contentItem").value(), childAppWindow->contentItem()); QCOMPARE(childControl->property("attached_activeFocusControl").value(), childAppWindowControl); QCOMPARE(childControl->property("attached_header").value(), childAppWindow->header()); QCOMPARE(childControl->property("attached_footer").value(), childAppWindow->footer()); childItem->setParentItem(childAppWindow->contentItem()); QCOMPARE(childItem->window(), childAppWindow); QCOMPARE(childItem->property("attached_window").value(), childAppWindow); QCOMPARE(childItem->property("attached_contentItem").value(), childAppWindow->contentItem()); QCOMPARE(childItem->property("attached_activeFocusControl").value(), childAppWindowControl); QCOMPARE(childItem->property("attached_header").value(), childAppWindow->header()); QCOMPARE(childItem->property("attached_footer").value(), childAppWindow->footer()); childControl->setParentItem(nullptr); QVERIFY(!childControl->window()); QVERIFY(!childControl->property("attached_window").value()); QVERIFY(!childControl->property("attached_contentItem").value()); QVERIFY(!childControl->property("attached_activeFocusControl").value()); QVERIFY(!childControl->property("attached_header").value()); QVERIFY(!childControl->property("attached_footer").value()); childItem->setParentItem(nullptr); QVERIFY(!childItem->window()); QVERIFY(!childItem->property("attached_window").value()); QVERIFY(!childItem->property("attached_contentItem").value()); QVERIFY(!childItem->property("attached_activeFocusControl").value()); QVERIFY(!childItem->property("attached_header").value()); QVERIFY(!childItem->property("attached_footer").value()); childAppWindow->close(); qApp->processEvents(); childWindow->show(); childWindow->requestActivate(); QVERIFY(QTest::qWaitForWindowActive(childWindow)); QVERIFY(!childWindowControl->hasActiveFocus()); childWindowControl->forceActiveFocus(); QTRY_VERIFY_ACTIVE_FOCUS(childWindowControl); QCOMPARE(childWindow->activeFocusItem(), childWindowControl); QCOMPARE(childWindowControl->property("attached_activeFocusControl").value(), childWindowControl); childControl->setParentItem(childWindow->contentItem()); QCOMPARE(childControl->window(), childWindow); QVERIFY(!childControl->property("attached_window").value()); QCOMPARE(childControl->property("attached_activeFocusControl").value(), childWindowControl); QVERIFY(!childControl->property("attached_contentItem").value()); QVERIFY(!childControl->property("attached_header").value()); QVERIFY(!childControl->property("attached_footer").value()); childItem->setParentItem(childWindow->contentItem()); QCOMPARE(childItem->window(), childWindow); QVERIFY(!childControl->property("attached_window").value()); QCOMPARE(childControl->property("attached_activeFocusControl").value(), childWindowControl); QVERIFY(!childControl->property("attached_contentItem").value()); QVERIFY(!childControl->property("attached_header").value()); QVERIFY(!childControl->property("attached_footer").value()); childControl->setParentItem(nullptr); QVERIFY(!childControl->window()); QVERIFY(!childControl->property("attached_window").value()); QVERIFY(!childControl->property("attached_contentItem").value()); QVERIFY(!childControl->property("attached_activeFocusControl").value()); QVERIFY(!childControl->property("attached_header").value()); QVERIFY(!childControl->property("attached_footer").value()); childItem->setParentItem(nullptr); QVERIFY(!childItem->window()); QVERIFY(!childItem->property("attached_window").value()); QVERIFY(!childItem->property("attached_contentItem").value()); QVERIFY(!childItem->property("attached_activeFocusControl").value()); QVERIFY(!childItem->property("attached_header").value()); QVERIFY(!childItem->property("attached_footer").value()); childWindow->close(); } void tst_QQuickApplicationWindow::font() { QQmlEngine engine; QQmlComponent component(&engine); component.loadUrl(testFileUrl("font.qml")); QObject* created = component.create(); QScopedPointer cleanup(created); QVERIFY(created); QQuickApplicationWindow* window = qobject_cast(created); QVERIFY(window); QVERIFY(!window->isVisible()); QCOMPARE(window->width(), 400); QCOMPARE(window->height(), 400); window->show(); window->requestActivate(); QVERIFY(QTest::qWaitForWindowActive(window)); QFont font = window->font(); const QSizeF expectedSize = getExpectedElementSize(); QQuickControl *mainItem = window->property("mainItem").value(); QVERIFY(mainItem); QCOMPARE(mainItem->width(), expectedSize.width()); QCOMPARE(mainItem->height(), expectedSize.height()); QCOMPARE(mainItem->font(), font); QQuickControl *item2 = mainItem->property("item_2").value(); QVERIFY(item2); QQuickControl *item3 = mainItem->property("item_3").value(); QVERIFY(item3); QQuickTextArea *item4 = mainItem->property("item_4").value(); QVERIFY(item4); QQuickTextField *item5 = mainItem->property("item_5").value(); QVERIFY(item5); QQuickLabel *item6 = mainItem->property("item_6").value(); QVERIFY(item6); QCOMPARE(item2->font(), font); QCOMPARE(item3->font(), font); QCOMPARE(item4->font(), font); QCOMPARE(item5->font(), font); QCOMPARE(item6->font(), font); int pointSize = font.pointSize(); font.setPixelSize(pointSize + 5); window->setFont(font); QCOMPARE(window->font(), font); QCOMPARE(mainItem->font(), font); QCOMPARE(item2->font(), font); QCOMPARE(item3->font(), font); QCOMPARE(item4->font(), font); QCOMPARE(item5->font(), font); QCOMPARE(item6->font(), font); } class TestTheme : public QQuickTheme { public: TestTheme() { setFont(System, QFont("Courier")); } }; void tst_QQuickApplicationWindow::defaultFont() { QQuickThemePrivate::instance.reset(new TestTheme); QQmlEngine engine; QQmlComponent component(&engine); component.setData("import QtQuick.Controls; ApplicationWindow { }", QUrl()); QScopedPointer window; window.reset(static_cast(component.create())); QVERIFY(!window.isNull()); QCOMPARE(window->font(), QQuickTheme::font(QQuickTheme::System)); } void tst_QQuickApplicationWindow::locale() { QQmlEngine engine; QQmlComponent component(&engine); component.loadUrl(testFileUrl("locale.qml")); QObject* created = component.create(); QScopedPointer cleanup(created); QVERIFY(created); QQuickApplicationWindow* window = qobject_cast(created); QVERIFY(window); QVERIFY(!window->isVisible()); QCOMPARE(window->width(), 400); QCOMPARE(window->height(), 400); window->show(); window->requestActivate(); QVERIFY(QTest::qWaitForWindowActive(window)); QLocale l = window->locale(); const QSizeF expectedSize = getExpectedElementSize(); QQuickControl *mainItem = window->property("mainItem").value(); QVERIFY(mainItem); QCOMPARE(mainItem->width(), expectedSize.width()); QCOMPARE(mainItem->height(), expectedSize.height()); QCOMPARE(mainItem->locale(), l); QQuickControl *item2 = mainItem->property("item_2").value(); QVERIFY(item2); QQuickControl *item3 = mainItem->property("item_3").value(); QVERIFY(item3); QCOMPARE(item2->locale(), l); QCOMPARE(item3->locale(), l); l = QLocale("en_US"); window->setLocale(l); QCOMPARE(window->locale(), l); QCOMPARE(mainItem->locale(), l); QCOMPARE(item2->locale(), l); QCOMPARE(item3->locale(), l); l = QLocale("ar_EG"); window->setLocale(l); QCOMPARE(window->locale(), l); QCOMPARE(mainItem->locale(), l); QCOMPARE(item2->locale(), l); QCOMPARE(item3->locale(), l); } void tst_QQuickApplicationWindow::activeFocusControl_data() { QTest::addColumn("containerName"); QTest::addColumn("activeFocusItemName"); QTest::addColumn("activeFocusControlName"); QTest::newRow("Column:TextInput") << QByteArray("container_column") << QByteArray("textInput_column") << QByteArray(); QTest::newRow("Column:TextEdit") << QByteArray("container_column") << QByteArray("textEdit_column") << QByteArray(); QTest::newRow("Column:TextField") << QByteArray("container_column") << QByteArray("textField_column") << QByteArray("textField_column"); QTest::newRow("Column:TextArea") << QByteArray("container_column") << QByteArray("textArea_column") << QByteArray("textArea_column"); QTest::newRow("Column:SpinBox") << QByteArray("container_column") << QByteArray("spinContent_column") << QByteArray("spinBox_column"); QTest::newRow("Frame:TextInput") << QByteArray("container_frame") << QByteArray("textInput_frame") << QByteArray("container_frame"); QTest::newRow("Frame:TextEdit") << QByteArray("container_frame") << QByteArray("textEdit_frame") << QByteArray("container_frame"); QTest::newRow("Frame:TextField") << QByteArray("container_frame") << QByteArray("textField_frame") << QByteArray("textField_frame"); QTest::newRow("Frame:TextArea") << QByteArray("container_frame") << QByteArray("textArea_frame") << QByteArray("textArea_frame"); QTest::newRow("Frame:SpinBox") << QByteArray("container_frame") << QByteArray("spinContent_frame") << QByteArray("spinBox_frame"); } void tst_QQuickApplicationWindow::activeFocusControl() { QFETCH(QByteArray, containerName); QFETCH(QByteArray, activeFocusItemName); QFETCH(QByteArray, activeFocusControlName); QQmlEngine engine; QQmlComponent component(&engine); component.loadUrl(testFileUrl("activeFocusControl.qml")); QScopedPointer object(component.create()); QVERIFY(!object.isNull()); QQuickApplicationWindow* window = qobject_cast(object.data()); QVERIFY(window); QVERIFY(!window->isVisible()); QCOMPARE(window->width(), 400); QCOMPARE(window->height(), 400); window->show(); window->requestActivate(); QVERIFY(QTest::qWaitForWindowActive(window)); QQuickItem *container = window->property(containerName).value(); QVERIFY(container); QQuickItem *activeFocusItem = window->property(activeFocusItemName).value(); QVERIFY(activeFocusItem); activeFocusItem->forceActiveFocus(); QVERIFY_ACTIVE_FOCUS(activeFocusItem); QCOMPARE(window->activeFocusItem(), activeFocusItem); QQuickItem *activeFocusControl = window->property(activeFocusControlName).value(); if (activeFocusControlName.isEmpty()) { QVERIFY(!activeFocusControl); } else { QVERIFY(activeFocusControl); QVERIFY_ACTIVE_FOCUS(activeFocusControl); } QCOMPARE(window->activeFocusControl(), activeFocusControl); } void tst_QQuickApplicationWindow::focusAfterPopupClosed() { #ifdef Q_OS_ANDROID QSKIP("This test crashes in Android emulator because of GLES issues (QTBUG-100991)"); #endif QQmlEngine engine; QQmlComponent component(&engine); component.loadUrl(testFileUrl("focusAfterPopupClosed.qml")); QScopedPointer window(qobject_cast(component.create())); QVERIFY(window); window->show(); window->requestActivate(); QVERIFY(QTest::qWaitForWindowActive(window.data())); QVERIFY(QGuiApplication::focusWindow() == window.data()); QQuickItem* contentItem = window->contentItem(); QVERIFY(contentItem); QVERIFY_ACTIVE_FOCUS(contentItem); QQuickItem* focusScope = window->property("focusScope").value(); QVERIFY(focusScope); QVERIFY_ACTIVE_FOCUS(focusScope); QSignalSpy focusScopeSpy(window.data(), SIGNAL(focusScopeKeyPressed())); QTest::keyClick(window.data(), Qt::Key_Space); QCOMPARE(focusScopeSpy.size(), 1); // Open the menu. QQuickItem* toolButton = window->property("toolButton").value(); QVERIFY(toolButton); QTest::mouseClick(window.data(), Qt::LeftButton, Qt::NoModifier, toolButton->mapFromScene(QPointF(toolButton->width() / 2, toolButton->height() / 2)).toPoint()); QVERIFY(!focusScope->hasActiveFocus()); // The FocusScope shouldn't receive any key events while the menu is open. QTest::keyClick(window.data(), Qt::Key_Space); QCOMPARE(focusScopeSpy.size(), 1); // Close the menu. The FocusScope should regain focus. QTest::keyClick(window.data(), Qt::Key_Escape); QVERIFY_ACTIVE_FOCUS(focusScope); QTest::keyClick(window.data(), Qt::Key_Space); QCOMPARE(focusScopeSpy.size(), 2); QQuickPopup *focusPopup = window->property("focusPopup").value(); QVERIFY(focusPopup); QVERIFY(!focusPopup->hasActiveFocus()); focusPopup->open(); QVERIFY(focusPopup->isVisible()); QSignalSpy focusPopupSpy(window.data(), SIGNAL(focusPopupKeyPressed())); QTest::keyClick(window.data(), Qt::Key_Space); QCOMPARE(focusPopupSpy.size(), 1); QQuickMenu *fileMenu = window->property("fileMenu").value(); QVERIFY(fileMenu); fileMenu->open(); QVERIFY(fileMenu->isVisible()); // The Popup shouldn't receive any key events while the menu is open. QTest::keyClick(window.data(), Qt::Key_Space); QCOMPARE(focusPopupSpy.size(), 1); // Close the menu. The Popup should regain focus. QTest::keyClick(window.data(), Qt::Key_Escape); QVERIFY_ACTIVE_FOCUS(focusPopup); QTest::keyClick(window.data(), Qt::Key_Space); QCOMPARE(focusPopupSpy.size(), 2); // Close the popup. The FocusScope should regain focus. QTest::keyClick(window.data(), Qt::Key_Escape); QVERIFY_ACTIVE_FOCUS(focusScope); QTest::keyClick(window.data(), Qt::Key_Space); QCOMPARE(focusScopeSpy.size(), 3); } void tst_QQuickApplicationWindow::clearFocusOnDestruction() { if (!canImportModule("import QtGraphicalEffects; DropShadow {}")) QSKIP("Test requires QtGraphicalEffects"); QQmlEngine engine; QQmlComponent component(&engine); component.loadUrl(testFileUrl("clearfocusondestruction.qml")); QScopedPointer window(qobject_cast(component.create())); QVERIFY(window); window->show(); window->requestActivate(); QVERIFY(QTest::qWaitForWindowActive(window.data())); QVERIFY(QGuiApplication::focusWindow() == window.data()); QQuickItem* contentItem = window->contentItem(); QVERIFY(contentItem); QVERIFY_ACTIVE_FOCUS(contentItem); QQuickItem* focusScope = window->property("textfield").value(); QVERIFY(focusScope); QVERIFY_ACTIVE_FOCUS(focusScope); QSignalSpy spy(window.data(), SIGNAL(activeFocusControlChanged())); // destroy the window, do not crash window.reset(); /* QQuickWindow::activeFocusItemChanged() is emitted inconsistently and only for certain use cases. Ideally it should be emitted whenever a QQuickWindow with a focus item is destroyed, but it doesn't... It might also be favorable to not emit it for performance reason. However, activeFocusControlChanged() is emitted more consistently, which of course makes it inconsistent with the emission of activeFocusItemChanged().... Therefore, if you have good reasons to change the behavior (and not emit it) take the test below with a grain of salt. */ QCOMPARE(spy.size(), 1); } void tst_QQuickApplicationWindow::layout() { QQmlEngine engine; QQmlComponent component(&engine); component.loadUrl(testFileUrl("layout.qml")); QScopedPointer object(component.create()); QVERIFY(!object.isNull()); QQuickApplicationWindow* window = qobject_cast(object.data()); QVERIFY(window); QVERIFY(QTest::qWaitForWindowExposed(window)); QQuickItem *content = window->contentItem(); QVERIFY(content); QQuickItem *menuBar = window->menuBar(); QVERIFY(menuBar); QQuickItem *header = window->header(); QVERIFY(header); QQuickItem *footer = window->footer(); QVERIFY(footer); QCOMPARE(menuBar->x(), 0.0); QCOMPARE(menuBar->y(), 0.0); QCOMPARE(menuBar->width(), qreal(window->width())); QVERIFY(menuBar->height() > 0); QCOMPARE(header->x(), 0.0); QCOMPARE(header->y(), menuBar->height()); QCOMPARE(header->width(), qreal(window->width())); QVERIFY(header->height() > 0); QCOMPARE(footer->x(), 0.0); QCOMPARE(footer->y(), window->height() - footer->height()); QCOMPARE(footer->width(), qreal(window->width())); QVERIFY(footer->height() > 0.0); QCOMPARE(content->x(), 0.0); QCOMPARE(content->y(), menuBar->height() + header->height()); QCOMPARE(content->width(), qreal(window->width())); QCOMPARE(content->height(), window->height() - menuBar->height() - header->height() - footer->height()); menuBar->setVisible(false); QCOMPARE(content->x(), 0.0); QCOMPARE(content->y(), header->height()); QCOMPARE(content->width(), qreal(window->width())); QCOMPARE(content->height(), window->height() - header->height() - footer->height()); header->setVisible(false); QCOMPARE(content->x(), 0.0); QCOMPARE(content->y(), 0.0); QCOMPARE(content->width(), qreal(window->width())); QCOMPARE(content->height(), window->height() - footer->height()); footer->setVisible(false); QCOMPARE(content->x(), 0.0); QCOMPARE(content->y(), 0.0); QCOMPARE(content->width(), qreal(window->width())); QCOMPARE(content->height(), qreal(window->height())); } void tst_QQuickApplicationWindow::layoutLayout() { QQmlEngine engine; QQmlComponent component(&engine); component.loadUrl(testFileUrl("layoutLayout.qml")); QScopedPointer object(component.create()); QVERIFY2(!object.isNull(), qPrintable(component.errorString())); QQuickApplicationWindow* window = qobject_cast(object.data()); QVERIFY(window); QVERIFY(QTest::qWaitForWindowExposed(window)); QQuickItem *content = window->contentItem(); QVERIFY(content); QQuickItem *header = window->header(); QVERIFY(header); QQuickItem *footer = window->footer(); QVERIFY(footer); QQuickItem *headerChild = header->findChild(); QVERIFY(headerChild); QCOMPARE(header->x(), 0.0); QCOMPARE(header->y(), 0.0); QCOMPARE(header->width(), qreal(window->width())); QCOMPARE(headerChild->width(), qreal(window->width())); QVERIFY(header->height() > 0); QQuickItem *footerChild = footer->findChild(); QVERIFY(footerChild); QCOMPARE(footer->x(), 0.0); QCOMPARE(footer->y(), window->height() - footer->height()); QCOMPARE(footer->width(), qreal(window->width())); QCOMPARE(footerChild->width(), qreal(window->width())); QVERIFY(footer->height() > 0.0); } class FriendlyApplicationWindow : public QQuickApplicationWindow { friend class tst_QQuickApplicationWindow; }; void tst_QQuickApplicationWindow::componentComplete() { FriendlyApplicationWindow cppWindow; QVERIFY(cppWindow.isComponentComplete()); QQmlEngine engine; QQmlComponent component(&engine); component.setData("import QtQuick.Controls; ApplicationWindow { }", QUrl()); FriendlyApplicationWindow *qmlWindow = static_cast(component.beginCreate(engine.rootContext())); QVERIFY(qmlWindow); QVERIFY(!qmlWindow->isComponentComplete()); component.completeCreate(); QVERIFY(qmlWindow->isComponentComplete()); } void tst_QQuickApplicationWindow::opacity() { QQmlEngine engine; QQmlComponent component(&engine); component.loadUrl(testFileUrl("opacity.qml")); QScopedPointer object(component.create()); QVERIFY2(!object.isNull(), qPrintable(component.errorString())); QQuickApplicationWindow *window = qobject_cast(object.data()); QVERIFY(window); } void tst_QQuickApplicationWindow::backgroundSize() { QQuickControlsApplicationHelper helper(this, QLatin1String("backgroundSize.qml")); QVERIFY2(helper.ready, helper.failureMessage()); QQuickApplicationWindow *window = helper.appWindow; QCOMPARE(window->width(), 600); QCOMPARE(window->height(), 400); auto *background = window->background(); QCOMPARE(background->implicitWidth(), 123); QCOMPARE(background->implicitHeight(), 456); QCOMPARE(background->width(), window->width()); QCOMPARE(background->height(), window->height()); // Changing the implicit size of the background shouldn't have any effect // on its size if it was never explicitly set. background->setImplicitWidth(234); QCOMPARE(background->implicitWidth(), 234); QCOMPARE(window->width(), 600); QCOMPARE(background->width(), window->width()); background->setImplicitHeight(567); QCOMPARE(background->implicitHeight(), 567); QCOMPARE(window->height(), 400); QCOMPARE(background->height(), window->height()); // Explicitly setting the size of the background should ensure // that it's respected from that point onwards. background->setWidth(345); QCOMPARE(background->implicitWidth(), 234); QCOMPARE(window->width(), 600); QCOMPARE(background->width(), 345); window->setWidth(610); QCOMPARE(background->width(), 345); background->setHeight(678); QCOMPARE(background->implicitHeight(), 567); QCOMPARE(window->height(), 400); QCOMPARE(background->height(), 678); window->setHeight(410); QCOMPARE(background->height(), 678); } void tst_QQuickApplicationWindow::explicitBackgroundSizeBinding() { QQuickControlsApplicationHelper helper(this, QLatin1String("explicitBackgroundSizeBinding.qml")); QVERIFY2(helper.ready, helper.failureMessage()); QQuickApplicationWindow *window = helper.appWindow; window->show(); window->requestActivate(); QVERIFY(QTest::qWaitForWindowExposed(window)); auto *background = window->background(); QCOMPARE(background->width(), window->width()); QCOMPARE(background->height(), window->height()); window->setProperty("scaleFactor", 0.5); QCOMPARE(background->width(), window->width() / 2); QCOMPARE(background->height(), window->height() / 2); } void tst_QQuickApplicationWindow::safeArea() { QQuickControlsApplicationHelper helper(this, QLatin1String("safeArea.qml")); QVERIFY2(helper.ready, helper.failureMessage()); QQuickApplicationWindow *window = helper.appWindow; window->show(); window->requestActivate(); QVERIFY(QTest::qWaitForWindowExposed(window)); auto menuBarRect = window->menuBar()->mapRectToScene(window->menuBar()->boundingRect()); auto headerRect = window->header()->mapRectToScene(window->header()->boundingRect()); auto contentRect = window->contentItem()->mapRectToScene(window->contentItem()->boundingRect()); auto footerRect = window->footer()->mapRectToScene(window->footer()->boundingRect()); // No item should overlap by default when there are no margins QCOMPARE(menuBarRect.intersected(headerRect), QRectF()); QCOMPARE(menuBarRect.intersected(contentRect), QRectF()); QCOMPARE(menuBarRect.intersected(footerRect), QRectF()); QCOMPARE(headerRect.intersected(contentRect), QRectF()); QCOMPARE(headerRect.intersected(footerRect), QRectF()); QCOMPARE(footerRect.intersected(contentRect), QRectF()); // And content item should not report any safe area margins auto *contentSafeArea = qobject_cast( qmlAttachedPropertiesObject(window->contentItem())); QCOMPARE(contentSafeArea->margins(), QMarginsF()); auto *windowSafeArea = qobject_cast( qmlAttachedPropertiesObject(window)); windowSafeArea->setAdditionalMargins(QMarginsF(100, 100, 100, 100)); QTRY_COMPARE(window->property("margins").value(), windowSafeArea->additionalMargins()); menuBarRect = window->menuBar()->mapRectToScene(window->menuBar()->boundingRect()); headerRect = window->header()->mapRectToScene(window->header()->boundingRect()); contentRect = window->contentItem()->mapRectToScene(window->contentItem()->boundingRect()); footerRect = window->footer()->mapRectToScene(window->footer()->boundingRect()); // No item should overlap by default when there are margins QCOMPARE(menuBarRect.intersected(headerRect), QRectF()); QCOMPARE(menuBarRect.intersected(contentRect), QRectF()); QCOMPARE(menuBarRect.intersected(footerRect), QRectF()); QCOMPARE(headerRect.intersected(contentRect), QRectF()); QCOMPARE(headerRect.intersected(footerRect), QRectF()); QCOMPARE(footerRect.intersected(contentRect), QRectF()); // And content item should not report any safe area margins QCOMPARE(contentSafeArea->margins(), QMarginsF()); QQmlProperty::write(window, "leftPadding", 0); QQmlProperty::write(window, "topPadding", 0); QQmlProperty::write(window, "rightPadding", 0); QQmlProperty::write(window, "bottomPadding", 0); // Removing the automatic padding should reflect the window's safe area margins QCOMPARE(contentSafeArea->margins(), windowSafeArea->additionalMargins()); // And the content item should fill the entire window QCOMPARE(window->contentItem()->position(), QPoint()); QCOMPARE(window->contentItem()->size(), window->size()); // Having no window safe area marings should still reflect the // menuBar, header, and footer as safe areas windowSafeArea->setAdditionalMargins(QMarginsF()); QTRY_COMPARE(window->property("margins").value(), windowSafeArea->additionalMargins()); QCOMPARE(contentSafeArea->margins(), QMarginsF(0, window->menuBar()->height() + window->header()->height(), 0, window->footer()->height())); // And the content item still should fill the entire window QCOMPARE(window->contentItem()->position(), QPoint()); QCOMPARE(window->contentItem()->size(), window->size()); } #if QT_CONFIG(quicktemplates2_hover) void tst_QQuickApplicationWindow::hoverInBackground() { QQuickControlsApplicationHelper helper(this, QLatin1String("hoverInBackground.qml")); QVERIFY2(helper.ready, helper.failureMessage()); QQuickApplicationWindow *window = helper.appWindow; window->show(); QVERIFY(QTest::qWaitForWindowExposed(window)); auto *mouseArea = window->findChild(); QVERIFY(mouseArea); const QPoint windowCenter = mapCenterToWindow(window->contentItem()); PointLerper pointLerper(window, windowCenter - QPoint(100, 100)); pointLerper.move(windowCenter, 2); QVERIFY(mouseArea->hovered()); auto *button = window->findChild(); QVERIFY(button); pointLerper.move(mapCenterToWindow(button), 2); QCOMPARE(button->isHovered(), QQuickControlPrivate::calcHoverEnabled(window->contentItem())); } #endif // QT_CONFIG(quicktemplates2_hover) QTEST_MAIN(tst_QQuickApplicationWindow) #include "tst_qquickapplicationwindow.moc"