diff options
Diffstat (limited to 'tests/auto/widgets/kernel')
52 files changed, 3160 insertions, 664 deletions
diff --git a/tests/auto/widgets/kernel/CMakeLists.txt b/tests/auto/widgets/kernel/CMakeLists.txt index 2392997176..2d4880ea3c 100644 --- a/tests/auto/widgets/kernel/CMakeLists.txt +++ b/tests/auto/widgets/kernel/CMakeLists.txt @@ -1,4 +1,5 @@ -# Generated from kernel.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause add_subdirectory(qapplication) add_subdirectory(qboxlayout) diff --git a/tests/auto/widgets/kernel/qaction/CMakeLists.txt b/tests/auto/widgets/kernel/qaction/CMakeLists.txt index dd70d5448f..9d1985da0b 100644 --- a/tests/auto/widgets/kernel/qaction/CMakeLists.txt +++ b/tests/auto/widgets/kernel/qaction/CMakeLists.txt @@ -1,15 +1,23 @@ -# Generated from qaction.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qaction Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qaction LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qaction SOURCES tst_qaction.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::CorePrivate Qt::Gui Qt::GuiPrivate Qt::Widgets + Qt::WidgetsPrivate ) diff --git a/tests/auto/widgets/kernel/qaction/tst_qaction.cpp b/tests/auto/widgets/kernel/qaction/tst_qaction.cpp index 49123c33d5..2820fd710b 100644 --- a/tests/auto/widgets/kernel/qaction/tst_qaction.cpp +++ b/tests/auto/widgets/kernel/qaction/tst_qaction.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QDialog> #include <QMainWindow> @@ -17,6 +17,8 @@ #include <qpa/qplatformintegration.h> #include <private/qguiapplication_p.h> +#include <QtWidgets/private/qapplication_p.h> + class tst_QAction : public QObject { Q_OBJECT @@ -111,7 +113,6 @@ void tst_QAction::actionEvent() // add action MyWidget testWidget(this); testWidget.show(); - QApplication::setActiveWindow(&testWidget); testWidget.addAction(&a); qApp->processEvents(); @@ -141,7 +142,7 @@ void tst_QAction::alternateShortcuts() MyWidget testWidget(this); testWidget.show(); - QApplication::setActiveWindow(&testWidget); + QApplicationPrivate::setActiveWindow(&testWidget); { QAction act(&testWidget); @@ -153,11 +154,11 @@ void tst_QAction::alternateShortcuts() act.setAutoRepeat(true); QTest::keyClick(&testWidget, Qt::Key_A, Qt::ControlModifier); - QCOMPARE(spy.count(), 1); //act should have been triggered + QCOMPARE(spy.size(), 1); //act should have been triggered act.setAutoRepeat(false); QTest::keyClick(&testWidget, Qt::Key_A, Qt::ControlModifier); - QCOMPARE(spy.count(), 2); //act should have been triggered a 2nd time + QCOMPARE(spy.size(), 2); //act should have been triggered a 2nd time //end of the scope of the action, it will be destroyed and removed from wid //This action should also unregister its shortcuts @@ -172,7 +173,7 @@ void tst_QAction::keysequence() { MyWidget testWidget(this); testWidget.show(); - QApplication::setActiveWindow(&testWidget); + QApplicationPrivate::setActiveWindow(&testWidget); { QAction act(&testWidget); @@ -187,12 +188,12 @@ void tst_QAction::keysequence() act.setAutoRepeat(true); QTest::keySequence(&testWidget, ks); QCoreApplication::processEvents(); - QCOMPARE(spy.count(), 1); // act should have been triggered + QCOMPARE(spy.size(), 1); // act should have been triggered act.setAutoRepeat(false); QTest::keySequence(&testWidget, ks); QCoreApplication::processEvents(); - QCOMPARE(spy.count(), 2); //act should have been triggered a 2nd time + QCOMPARE(spy.size(), 2); //act should have been triggered a 2nd time // end of the scope of the action, it will be destroyed and removed from widget // This action should also unregister its shortcuts @@ -206,7 +207,7 @@ void tst_QAction::enabledVisibleInteraction() { MyWidget testWidget(this); testWidget.show(); - QApplication::setActiveWindow(&testWidget); + QApplicationPrivate::setActiveWindow(&testWidget); QAction act(nullptr); // check defaults @@ -228,15 +229,15 @@ void tst_QAction::enabledVisibleInteraction() act.setEnabled(true); act.setVisible(false); QTest::keyClick(&testWidget, Qt::Key_T, Qt::ControlModifier); - QCOMPARE(spy.count(), 0); //act is not visible, so don't trigger + QCOMPARE(spy.size(), 0); //act is not visible, so don't trigger act.setVisible(false); act.setEnabled(true); QTest::keyClick(&testWidget, Qt::Key_T, Qt::ControlModifier); - QCOMPARE(spy.count(), 0); //act is not visible, so don't trigger + QCOMPARE(spy.size(), 0); //act is not visible, so don't trigger act.setVisible(true); act.setEnabled(true); QTest::keyClick(&testWidget, Qt::Key_T, Qt::ControlModifier); - QCOMPARE(spy.count(), 1); //act is visible and enabled, so trigger + QCOMPARE(spy.size(), 1); //act is visible and enabled, so trigger } #endif // QT_CONFIG(shortcut) @@ -254,12 +255,12 @@ void tst_QAction::task229128TriggeredSignalWhenInActiongroup() QSignalSpy actionSpy(checkedAction, QOverload<bool>::of(&QAction::triggered)); QSignalSpy actionGroupSpy(&ag, QOverload<QAction*>::of(&QActionGroup::triggered)); - QCOMPARE(actionGroupSpy.count(), 0); - QCOMPARE(actionSpy.count(), 0); + QCOMPARE(actionGroupSpy.size(), 0); + QCOMPARE(actionSpy.size(), 0); checkedAction->trigger(); // check that both the group and the action have emitted the signal - QCOMPARE(actionGroupSpy.count(), 1); - QCOMPARE(actionSpy.count(), 1); + QCOMPARE(actionGroupSpy.size(), 1); + QCOMPARE(actionSpy.size(), 1); } #if QT_CONFIG(shortcut) @@ -271,7 +272,6 @@ void tst_QAction::repeat() MyWidget testWidget(this); testWidget.show(); - QApplication::setActiveWindow(&testWidget); QVERIFY(QTest::qWaitForWindowActive(&testWidget)); QAction act(&testWidget); @@ -282,7 +282,7 @@ void tst_QAction::repeat() act.setAutoRepeat(true); QTest::keyPress(&testWidget, Qt::Key_F); QTest::keyRelease(&testWidget, Qt::Key_F); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); spy.clear(); QTest::keyPress(&testWidget, Qt::Key_F); @@ -290,7 +290,7 @@ void tst_QAction::repeat() QTest::simulateEvent(&testWidget, true, Qt::Key_F, Qt::NoModifier, QString("f"), true); QTest::simulateEvent(&testWidget, true, Qt::Key_F, Qt::NoModifier, QString("f"), true); QTest::keyRelease(&testWidget, Qt::Key_F); - QCOMPARE(spy.count(), 3); + QCOMPARE(spy.size(), 3); spy.clear(); act.setAutoRepeat(false); @@ -298,14 +298,14 @@ void tst_QAction::repeat() QTest::simulateEvent(&testWidget, true, Qt::Key_F, Qt::NoModifier, QString("f"), true); QTest::simulateEvent(&testWidget, true, Qt::Key_F, Qt::NoModifier, QString("f"), true); QTest::keyRelease(&testWidget, Qt::Key_F); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); spy.clear(); act.setAutoRepeat(true); QTest::keyPress(&testWidget, Qt::Key_F); QTest::simulateEvent(&testWidget, true, Qt::Key_F, Qt::NoModifier, QString("f"), true); QTest::keyRelease(&testWidget, Qt::Key_F); - QCOMPARE(spy.count(), 2); + QCOMPARE(spy.size(), 2); } void tst_QAction::disableShortcutsWithBlockedWidgets_data() @@ -350,12 +350,11 @@ void tst_QAction::disableShortcutsWithBlockedWidgets() dialog.show(); QVERIFY(QTest::qWaitForWindowExposed(&dialog)); - QApplication::setActiveWindow(&window); QVERIFY(QTest::qWaitForWindowActive(&window)); QSignalSpy spy(&action, &QAction::triggered); QTest::keyPress(&window, Qt::Key_1); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); } class ShortcutOverrideWidget : public QWidget @@ -393,7 +392,7 @@ void tst_QAction::shortcutFromKeyEvent() // shortcut route for us QKeyEvent e(QEvent::KeyPress, Qt::Key_1, Qt::NoModifier); QApplication::sendEvent(&testWidget, &e); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); QCOMPARE(testWidget.shortcutOverrideCount, 1); } @@ -412,6 +411,9 @@ void tst_QAction::disableShortcutInMenuAction_data() void tst_QAction::disableShortcutInMenuAction() { + if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation)) + QSKIP("QWindow::requestActivate() is not supported."); + QFETCH(QByteArray, property); QMainWindow mw; @@ -429,42 +431,42 @@ void tst_QAction::disableShortcutInMenuAction() QKeyEvent event(QEvent::KeyPress, Qt::Key_A, Qt::ControlModifier); QApplication::sendEvent(&mw, &event); - QCOMPARE(spy.count(), ++expectedTriggerCount); + QCOMPARE(spy.size(), ++expectedTriggerCount); testMenu->menuAction()->setProperty(property, false); QApplication::sendEvent(&mw, &event); - QCOMPARE(spy.count(), expectedTriggerCount); + QCOMPARE(spy.size(), expectedTriggerCount); testMenu->menuAction()->setProperty(property, true); QApplication::sendEvent(&mw, &event); - QCOMPARE(spy.count(), ++expectedTriggerCount); + QCOMPARE(spy.size(), ++expectedTriggerCount); // If the action lives somewhere else, then keep firing even // if the menu has been hidden or disabled. toolBar->addAction(testAction); QApplication::sendEvent(&mw, &event); - QCOMPARE(spy.count(), ++expectedTriggerCount); + QCOMPARE(spy.size(), ++expectedTriggerCount); testMenu->menuAction()->setProperty(property, false); QApplication::sendEvent(&mw, &event); - QCOMPARE(spy.count(), ++expectedTriggerCount); + QCOMPARE(spy.size(), ++expectedTriggerCount); // unless all other widgets in which the action lives have // been hidden... toolBar->hide(); QApplication::sendEvent(&mw, &event); - QCOMPARE(spy.count(), expectedTriggerCount); + QCOMPARE(spy.size(), expectedTriggerCount); // ... or disabled toolBar->show(); toolBar->setEnabled(false); QApplication::sendEvent(&mw, &event); - QCOMPARE(spy.count(), expectedTriggerCount); + QCOMPARE(spy.size(), expectedTriggerCount); // back to normal toolBar->setEnabled(true); QApplication::sendEvent(&mw, &event); - QCOMPARE(spy.count(), ++expectedTriggerCount); + QCOMPARE(spy.size(), ++expectedTriggerCount); } #endif // QT_CONFIG(shortcut) diff --git a/tests/auto/widgets/kernel/qactiongroup/CMakeLists.txt b/tests/auto/widgets/kernel/qactiongroup/CMakeLists.txt index 927802103c..e26ee75bc1 100644 --- a/tests/auto/widgets/kernel/qactiongroup/CMakeLists.txt +++ b/tests/auto/widgets/kernel/qactiongroup/CMakeLists.txt @@ -1,13 +1,20 @@ -# Generated from qactiongroup.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qactiongroup Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qactiongroup LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qactiongroup SOURCES tst_qactiongroup.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::Gui Qt::Widgets ) diff --git a/tests/auto/widgets/kernel/qactiongroup/tst_qactiongroup.cpp b/tests/auto/widgets/kernel/qactiongroup/tst_qactiongroup.cpp index 56d2feee47..0d42340bfb 100644 --- a/tests/auto/widgets/kernel/qactiongroup/tst_qactiongroup.cpp +++ b/tests/auto/widgets/kernel/qactiongroup/tst_qactiongroup.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> diff --git a/tests/auto/widgets/kernel/qapplication/BLACKLIST b/tests/auto/widgets/kernel/qapplication/BLACKLIST index cce10602e4..c68c7d6b14 100644 --- a/tests/auto/widgets/kernel/qapplication/BLACKLIST +++ b/tests/auto/widgets/kernel/qapplication/BLACKLIST @@ -1,5 +1,3 @@ -[sendEventsOnProcessEvents] -ubuntu-20.04 [touchEventPropagation] # QTBUG-66745 opensuse-leap diff --git a/tests/auto/widgets/kernel/qapplication/CMakeLists.txt b/tests/auto/widgets/kernel/qapplication/CMakeLists.txt index 3bb7e31691..524db06560 100644 --- a/tests/auto/widgets/kernel/qapplication/CMakeLists.txt +++ b/tests/auto/widgets/kernel/qapplication/CMakeLists.txt @@ -1,5 +1,17 @@ -# Generated from qapplication.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qapplication LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() add_subdirectory(desktopsettingsaware) add_subdirectory(modal) add_subdirectory(test) + +add_dependencies(tst_qapplication + desktopsettingsaware_helper + modal_helper +) diff --git a/tests/auto/widgets/kernel/qapplication/desktopsettingsaware/CMakeLists.txt b/tests/auto/widgets/kernel/qapplication/desktopsettingsaware/CMakeLists.txt index a391d5b13d..f094a451d2 100644 --- a/tests/auto/widgets/kernel/qapplication/desktopsettingsaware/CMakeLists.txt +++ b/tests/auto/widgets/kernel/qapplication/desktopsettingsaware/CMakeLists.txt @@ -1,15 +1,16 @@ -# Generated from desktopsettingsaware.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## desktopsettingsaware Binary: ##################################################################### -qt_internal_add_executable(desktopsettingsaware_helper # special case +qt_internal_add_executable(desktopsettingsaware_helper SOURCES main.cpp - OUTPUT_DIRECTORY # special case - ${CMAKE_CURRENT_BINARY_DIR}/.. # special case - PUBLIC_LIBRARIES + OUTPUT_DIRECTORY + ${CMAKE_CURRENT_BINARY_DIR}/.. + LIBRARIES Qt::Gui Qt::Widgets ) diff --git a/tests/auto/widgets/kernel/qapplication/desktopsettingsaware/main.cpp b/tests/auto/widgets/kernel/qapplication/desktopsettingsaware/main.cpp index 0584dbeab8..1bf3eef55c 100644 --- a/tests/auto/widgets/kernel/qapplication/desktopsettingsaware/main.cpp +++ b/tests/auto/widgets/kernel/qapplication/desktopsettingsaware/main.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QApplication> diff --git a/tests/auto/widgets/kernel/qapplication/modal/CMakeLists.txt b/tests/auto/widgets/kernel/qapplication/modal/CMakeLists.txt index 95c429f936..79c5660650 100644 --- a/tests/auto/widgets/kernel/qapplication/modal/CMakeLists.txt +++ b/tests/auto/widgets/kernel/qapplication/modal/CMakeLists.txt @@ -1,16 +1,17 @@ -# Generated from modal.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## modal Binary: ##################################################################### -qt_internal_add_executable(modal_helper # special case +qt_internal_add_executable(modal_helper SOURCES base.cpp base.h main.cpp - OUTPUT_DIRECTORY # special case - ${CMAKE_CURRENT_BINARY_DIR}/.. # special case - PUBLIC_LIBRARIES + OUTPUT_DIRECTORY + ${CMAKE_CURRENT_BINARY_DIR}/.. + LIBRARIES Qt::Gui Qt::Widgets ) diff --git a/tests/auto/widgets/kernel/qapplication/modal/base.cpp b/tests/auto/widgets/kernel/qapplication/modal/base.cpp index 0108c9c789..49a90723dd 100644 --- a/tests/auto/widgets/kernel/qapplication/modal/base.cpp +++ b/tests/auto/widgets/kernel/qapplication/modal/base.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "base.h" diff --git a/tests/auto/widgets/kernel/qapplication/modal/base.h b/tests/auto/widgets/kernel/qapplication/modal/base.h index a78b2306ba..168da92f97 100644 --- a/tests/auto/widgets/kernel/qapplication/modal/base.h +++ b/tests/auto/widgets/kernel/qapplication/modal/base.h @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifndef BASE_H #define BASE_H diff --git a/tests/auto/widgets/kernel/qapplication/modal/main.cpp b/tests/auto/widgets/kernel/qapplication/modal/main.cpp index be38c41786..3f3496834d 100644 --- a/tests/auto/widgets/kernel/qapplication/modal/main.cpp +++ b/tests/auto/widgets/kernel/qapplication/modal/main.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QApplication> #include "base.h" diff --git a/tests/auto/widgets/kernel/qapplication/test/CMakeLists.txt b/tests/auto/widgets/kernel/qapplication/test/CMakeLists.txt index ad5209f9bf..fc80b8af7e 100644 --- a/tests/auto/widgets/kernel/qapplication/test/CMakeLists.txt +++ b/tests/auto/widgets/kernel/qapplication/test/CMakeLists.txt @@ -1,4 +1,5 @@ -# Generated from test.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## test Test: @@ -8,35 +9,23 @@ list(APPEND test_data "../tmp/README") list(APPEND test_data "../modal") -qt_internal_add_test(tst_qapplication # special case +qt_internal_add_test(tst_qapplication SOURCES ../tst_qapplication.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::CorePrivate Qt::Gui Qt::GuiPrivate Qt::Widgets Qt::WidgetsPrivate TESTDATA ${test_data} - OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/.." # special case + OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/.." ) ## Scopes: ##################################################################### -qt_internal_extend_target(tst_qapplication CONDITION builtin_testdata # special case +qt_internal_extend_target(tst_qapplication CONDITION builtin_testdata DEFINES BUILTIN_TESTDATA ) - -#### Keys ignored in scope 3:.:.:test.pro:NOT ANDROID: -# SUBPROGRAMS = "desktopsettingsaware" "modal" - -#### Keys ignored in scope 6:.:.:test.pro:NOT ANDROID: -# TEST_HELPER_INSTALLS = "../debug/helper" - -#### Keys ignored in scope 8:.:.:test.pro:NOT ANDROID: -# TEST_HELPER_INSTALLS = "../release/helper" - -#### Keys ignored in scope 10:.:.:test.pro:NOT ANDROID: -# TEST_HELPER_INSTALLS = "../helper" diff --git a/tests/auto/widgets/kernel/qapplication/tst_qapplication.cpp b/tests/auto/widgets/kernel/qapplication/tst_qapplication.cpp index 59cc7f0884..2eb2d84504 100644 --- a/tests/auto/widgets/kernel/qapplication/tst_qapplication.cpp +++ b/tests/auto/widgets/kernel/qapplication/tst_qapplication.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #define QT_STATICPLUGIN #include <QtWidgets/qstyleplugin.h> @@ -23,6 +23,7 @@ #include <QtGui/QFontDatabase> #include <QtGui/QClipboard> +#include <QtGui/QStyleHints> #include <QtWidgets/QApplication> #include <QtWidgets/QMessageBox> @@ -94,7 +95,9 @@ private slots: void libraryPaths_qt_plugin_path_2(); #endif +#ifdef QT_BUILD_INTERNAL void sendPostedEvents(); +#endif // ifdef QT_BUILD_INTERNAL void thread(); void desktopSettingsAware(); @@ -117,6 +120,7 @@ private slots: void style(); void applicationPalettePolish(); + void setColorScheme(); void allWidgets(); void topLevelWidgets(); @@ -128,6 +132,7 @@ private slots: void wheelEventPropagation(); void qtbug_12673(); + void qtbug_103611(); void noQuitOnHide(); void globalStaticObjectDestruction(); // run this last @@ -163,6 +168,21 @@ void tst_QApplication::sendEventsOnProcessEvents() QCoreApplication::postEvent(&app, new QEvent(QEvent::Type(QEvent::User + 1))); QCoreApplication::processEvents(); + +#ifdef Q_OS_LINUX + if ((QSysInfo::productType() == "rhel" && QSysInfo::productVersion().startsWith(u'9')) + || (QSysInfo::productType() == "ubuntu" && QSysInfo::productVersion().startsWith(u'2'))) + { + QFile f("/proc/self/maps"); + QVERIFY(f.open(QIODevice::ReadOnly)); + + QByteArray libs = f.readAll(); + if (libs.contains("libqgtk3.") || libs.contains("libqgtk3TestInfix.")) { + QEXPECT_FAIL("", "Fails if qgtk3 (Glib) is loaded, see QTBUG-87137", Abort); + } + } +#endif + QVERIFY(spy.recordedEvents.contains(QEvent::User + 1)); } @@ -215,10 +235,12 @@ void tst_QApplication::staticSetup() EventWatcher() { qApp->installEventFilter(this); +#if QT_DEPRECATED_SINCE(6, 0) QT_WARNING_PUSH QT_WARNING_DISABLE_DEPRECATED QObject::connect(qApp, &QApplication::paletteChanged, [&]{ ++palette_changed; }); QObject::connect(qApp, &QApplication::fontChanged, [&]{ ++font_changed; }); QT_WARNING_POP +#endif } protected: @@ -248,8 +270,13 @@ QT_WARNING_POP font.setBold(!font.bold()); qApp->setFont(font); QApplication::processEvents(); +#if QT_DEPRECATED_SINCE(6, 0) QCOMPARE(watcher.palette_changed, 2); QCOMPARE(watcher.font_changed, 2); +#else + QCOMPARE(watcher.palette_changed, 1); + QCOMPARE(watcher.font_changed, 1); +#endif } @@ -286,10 +313,8 @@ void tst_QApplication::alert() QApplication::alert(&widget, -1); QApplication::alert(&widget, 250); widget2.activateWindow(); - QApplication::setActiveWindow(&widget2); QApplication::alert(&widget, 0); widget.activateWindow(); - QApplication::setActiveWindow(&widget); QApplication::alert(&widget, 200); } @@ -478,7 +503,7 @@ static char **QString2cstrings(const QString &args) static QByteArrayList cache; const auto &list = QStringView{ args }.split(' '); - auto argarray = new char*[list.count() + 1]; + auto argarray = new char*[list.size() + 1]; int i = 0; for (; i < list.size(); ++i ) { @@ -567,7 +592,7 @@ void tst_QApplication::lastWindowClosed() QTimer::singleShot(1000, dialog.data(), &QDialog::accept); dialog->exec(); QVERIFY(dialog); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); QPointer<CloseWidget>widget = new CloseWidget; widget->setWindowTitle(QLatin1String(QTest::currentTestFunction()) + QLatin1String("CloseWidget")); @@ -576,7 +601,7 @@ void tst_QApplication::lastWindowClosed() QObject::connect(&app, &QGuiApplication::lastWindowClosed, widget.data(), &QObject::deleteLater); QCoreApplication::exec(); QVERIFY(!widget); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); spy.clear(); delete dialog; @@ -594,7 +619,7 @@ void tst_QApplication::lastWindowClosed() QTimer::singleShot(1000, &app, &QApplication::closeAllWindows); QCoreApplication::exec(); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); } class QuitOnLastWindowClosedDialog : public QDialog @@ -627,8 +652,8 @@ public slots: other.exec(); // verify that the eventloop ran and let the timer fire - QCOMPARE(spy.count(), 1); - QCOMPARE(appSpy.count(), 1); + QCOMPARE(spy.size(), 1); + QCOMPARE(appSpy.size(), 1); } private: @@ -653,7 +678,7 @@ public slots: timer1.setSingleShot(true); timer1.start(1000); dialog.exec(); - QCOMPARE(spy1.count(), 1); + QCOMPARE(spy1.size(), 1); show(); } @@ -674,7 +699,7 @@ void tst_QApplication::quitOnLastWindowClosed() QCoreApplication::exec(); // lastWindowClosed() signal should only be sent after the last dialog is closed - QCOMPARE(appSpy.count(), 2); + QCOMPARE(appSpy.size(), 2); } { int argc = 0; @@ -689,8 +714,8 @@ void tst_QApplication::quitOnLastWindowClosed() timer1.setSingleShot(true); timer1.start(1000); dialog.exec(); - QCOMPARE(spy1.count(), 1); - QCOMPARE(appSpy.count(), 0); + QCOMPARE(spy1.size(), 1); + QCOMPARE(appSpy.size(), 0); QTimer timer2; connect(&timer2, &QTimer::timeout, &app, &QCoreApplication::quit); @@ -699,8 +724,8 @@ void tst_QApplication::quitOnLastWindowClosed() timer2.start(1000); int returnValue = QCoreApplication::exec(); QCOMPARE(returnValue, 0); - QCOMPARE(spy2.count(), 1); - QCOMPARE(appSpy.count(), 0); + QCOMPARE(spy2.size(), 1); + QCOMPARE(appSpy.size(), 0); } { int argc = 0; @@ -731,8 +756,8 @@ void tst_QApplication::quitOnLastWindowClosed() QCoreApplication::exec(); - QCOMPARE(spy.count(), 1); - QVERIFY(spy2.count() < 15); // Should be around 10 if closing caused the quit + QCOMPARE(spy.size(), 1); + QVERIFY(spy2.size() < 15); // Should be around 10 if closing caused the quit } bool quitApplicationTriggered = false; @@ -762,7 +787,7 @@ void tst_QApplication::quitOnLastWindowClosed() QCoreApplication::exec(); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); QVERIFY(quitApplicationTriggered); } { @@ -784,8 +809,8 @@ void tst_QApplication::quitOnLastWindowClosed() QCOMPARE(returnValue, 0); // failure here means the timer above didn't fire, and the // quit was caused the dialog being closed (not the window) - QCOMPARE(timerSpy.count(), 1); - QCOMPARE(appSpy.count(), 2); + QCOMPARE(timerSpy.size(), 1); + QCOMPARE(appSpy.size(), 2); } { int argc = 0; @@ -834,7 +859,7 @@ void tst_QApplication::quitOnLastWindowClosed() QTimer::singleShot(100, &w1, &QWidget::close); QCoreApplication::exec(); - QVERIFY(timerSpy.count() < 10); + QVERIFY(timerSpy.size() < 10); } } @@ -878,6 +903,7 @@ void tst_QApplication::closeAllWindows() { int argc = 0; QApplication app(argc, nullptr); + app.setAttribute(Qt::AA_DontUseNativeDialogs, true); // create some windows new QWidget; @@ -886,7 +912,7 @@ void tst_QApplication::closeAllWindows() // show all windows auto topLevels = QApplication::topLevelWidgets(); - for (QWidget *w : qAsConst(topLevels)) { + for (QWidget *w : std::as_const(topLevels)) { w->show(); QVERIFY(QTest::qWaitForWindowExposed(w)); } @@ -903,14 +929,14 @@ void tst_QApplication::closeAllWindows() PromptOnCloseWidget *promptOnCloseWidget = new PromptOnCloseWidget; // show all windows topLevels = QApplication::topLevelWidgets(); - for (QWidget *w : qAsConst(topLevels)) { + for (QWidget *w : std::as_const(topLevels)) { w->show(); QVERIFY(QTest::qWaitForWindowExposed(w)); } // close the last window to open the prompt (eventloop recurses) promptOnCloseWidget->close(); // all windows should not be visible, except the one that opened the prompt - for (QWidget *w : qAsConst(topLevels)) { + for (QWidget *w : std::as_const(topLevels)) { if (w == promptOnCloseWidget) QVERIFY(w->isVisible()); else @@ -922,8 +948,8 @@ void tst_QApplication::closeAllWindows() bool isPathListIncluded(const QStringList &l, const QStringList &r) { - int size = r.count(); - if (size > l.count()) + int size = r.size(); + if (size > l.size()) return false; #if defined (Q_OS_WIN) Qt::CaseSensitivity cs = Qt::CaseInsensitive; @@ -931,13 +957,13 @@ bool isPathListIncluded(const QStringList &l, const QStringList &r) Qt::CaseSensitivity cs = Qt::CaseSensitive; #endif int i = 0, j = 0; - for ( ; i < l.count() && j < r.count(); ++i) { + for ( ; i < l.size() && j < r.size(); ++i) { if (QDir::toNativeSeparators(l[i]).compare(QDir::toNativeSeparators(r[j]), cs) == 0) { ++j; i = -1; } } - return j == r.count(); + return j == r.size(); } #if QT_CONFIG(library) @@ -997,7 +1023,7 @@ void tst_QApplication::libraryPaths() { qCDebug(lcTests) << "Initial library path:" << QApplication::libraryPaths(); - int count = QApplication::libraryPaths().count(); + int count = QApplication::libraryPaths().size(); #if 0 // this test doesn't work if KDE 4 is installed QCOMPARE(count, 1); // before creating QApplication, only the PluginsPath is in the libraryPaths() @@ -1006,9 +1032,9 @@ void tst_QApplication::libraryPaths() QApplication::addLibraryPath(installPathPlugins); qCDebug(lcTests) << "installPathPlugins" << installPathPlugins; qCDebug(lcTests) << "After adding plugins path:" << QApplication::libraryPaths(); - QCOMPARE(QApplication::libraryPaths().count(), count); + QCOMPARE(QApplication::libraryPaths().size(), count); QApplication::addLibraryPath(testDir); - QCOMPARE(QApplication::libraryPaths().count(), count + 1); + QCOMPARE(QApplication::libraryPaths().size(), count + 1); // creating QApplication adds the applicationDirPath to the libraryPath int argc = 1; @@ -1018,19 +1044,19 @@ void tst_QApplication::libraryPaths() // On Windows CE these are identical and might also be the case for other // systems too if (appDirPath != installPathPlugins) - QCOMPARE(QApplication::libraryPaths().count(), count + 2); + QCOMPARE(QApplication::libraryPaths().size(), count + 2); } { int argc = 1; QApplication app(argc, &argv0); qCDebug(lcTests) << "Initial library path:" << QCoreApplication::libraryPaths(); - int count = QCoreApplication::libraryPaths().count(); + int count = QCoreApplication::libraryPaths().size(); QString installPathPlugins = QLibraryInfo::path(QLibraryInfo::PluginsPath); QCoreApplication::addLibraryPath(installPathPlugins); qCDebug(lcTests) << "installPathPlugins" << installPathPlugins; qCDebug(lcTests) << "After adding plugins path:" << QCoreApplication::libraryPaths(); - QCOMPARE(QCoreApplication::libraryPaths().count(), count); + QCOMPARE(QCoreApplication::libraryPaths().size(), count); QString appDirPath = QCoreApplication::applicationDirPath(); @@ -1038,14 +1064,14 @@ void tst_QApplication::libraryPaths() QCoreApplication::addLibraryPath(appDirPath + "/.."); qCDebug(lcTests) << "appDirPath" << appDirPath; qCDebug(lcTests) << "After adding appDirPath && appDirPath + /..:" << QCoreApplication::libraryPaths(); - QCOMPARE(QCoreApplication::libraryPaths().count(), count + 1); + QCOMPARE(QCoreApplication::libraryPaths().size(), count + 1); #ifdef Q_OS_MACOS QCoreApplication::addLibraryPath(appDirPath + "/../MacOS"); #else QCoreApplication::addLibraryPath(appDirPath + "/tmp/.."); #endif qCDebug(lcTests) << "After adding appDirPath + /tmp/..:" << QCoreApplication::libraryPaths(); - QCOMPARE(QCoreApplication::libraryPaths().count(), count + 1); + QCOMPARE(QCoreApplication::libraryPaths().size(), count + 1); } } @@ -1112,11 +1138,12 @@ void tst_QApplication::libraryPaths_qt_plugin_path_2() << QCoreApplication::applicationDirPath(); QVERIFY(isPathListIncluded(QCoreApplication::libraryPaths(), expected)); - qputenv("QT_PLUGIN_PATH", QByteArray()); + qputenv("QT_PLUGIN_PATH", nullptr); } } #endif +#ifdef QT_BUILD_INTERNAL class SendPostedEventsTester : public QObject { Q_OBJECT @@ -1138,14 +1165,14 @@ void SendPostedEventsTester::doTest() QPointer<SendPostedEventsTester> p = this; QApplication::postEvent(this, new QEvent(QEvent::User)); // DeferredDelete should not be delivered until returning from this function - QApplication::postEvent(this, new QDeferredDeleteEvent()); + deleteLater(); QEventLoop eventLoop; QMetaObject::invokeMethod(&eventLoop, "quit", Qt::QueuedConnection); eventLoop.exec(); QVERIFY(p != nullptr); - QCOMPARE(eventSpy.count(), 2); + QCOMPARE(eventSpy.size(), 2); QCOMPARE(eventSpy.at(0), int(QEvent::MetaCall)); QCOMPARE(eventSpy.at(1), int(QEvent::User)); eventSpy.clear(); @@ -1162,6 +1189,7 @@ void tst_QApplication::sendPostedEvents() (void) QCoreApplication::exec(); QVERIFY(p.isNull()); } +#endif void tst_QApplication::thread() { @@ -1306,9 +1334,6 @@ void DeleteLaterWidget::checkDeleteLater() void tst_QApplication::testDeleteLater() { -#ifdef Q_OS_MAC - QSKIP("This test fails and then hangs on OS X, see QTBUG-24318"); -#endif int argc = 0; QApplication app(argc, nullptr); connect(&app, &QGuiApplication::lastWindowClosed, &app, &QCoreApplication::quit); @@ -1534,7 +1559,7 @@ void tst_QApplication::setActiveWindow() delete pb2; w->show(); - QApplication::setActiveWindow(w); // needs this on twm (focus follows mouse) + QApplicationPrivate::setActiveWindow(w); // needs this on twm (focus follows mouse) QVERIFY(pb1->hasFocus()); delete w; } @@ -1570,6 +1595,9 @@ void tst_QApplication::activateDeactivateEvent() int argc = 0; QApplication app(argc, nullptr); + if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation)) + QSKIP("QWindow::requestActivate() is not supported."); + Window w1; Window w2; @@ -1596,7 +1624,6 @@ void tst_QApplication::focusWidget() QTextEdit te; te.show(); - QApplication::setActiveWindow(&te); QVERIFY(QTest::qWaitForWindowActive(&te)); const auto focusWidget = QApplication::focusWidget(); @@ -1612,7 +1639,6 @@ void tst_QApplication::focusWidget() QTextEdit te(&w); w.show(); - QApplication::setActiveWindow(&w); QVERIFY(QTest::qWaitForWindowActive(&w)); const auto focusWidget = QApplication::focusWidget(); @@ -1645,22 +1671,22 @@ void tst_QApplication::focusChanged() hbox1.addWidget(&le1); hbox1.addWidget(&pb1); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); parent1.show(); - QApplication::setActiveWindow(&parent1); // needs this on twm (focus follows mouse) - QCOMPARE(spy.count(), 1); - QCOMPARE(spy.at(0).count(), 2); + QApplicationPrivate::setActiveWindow(&parent1); // needs this on twm (focus follows mouse) + QCOMPARE(spy.size(), 1); + QCOMPARE(spy.at(0).size(), 2); old = qvariant_cast<QWidget*>(spy.at(0).at(0)); now = qvariant_cast<QWidget*>(spy.at(0).at(1)); QCOMPARE(now, &le1); QCOMPARE(now, QApplication::focusWidget()); QVERIFY(!old); spy.clear(); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); pb1.setFocus(); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); old = qvariant_cast<QWidget*>(spy.at(0).at(0)); now = qvariant_cast<QWidget*>(spy.at(0).at(1)); QCOMPARE(now, &pb1); @@ -1669,7 +1695,7 @@ void tst_QApplication::focusChanged() spy.clear(); lb1.setFocus(); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); old = qvariant_cast<QWidget*>(spy.at(0).at(0)); now = qvariant_cast<QWidget*>(spy.at(0).at(1)); QCOMPARE(now, &lb1); @@ -1678,7 +1704,7 @@ void tst_QApplication::focusChanged() spy.clear(); lb1.clearFocus(); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); old = qvariant_cast<QWidget*>(spy.at(0).at(0)); now = qvariant_cast<QWidget*>(spy.at(0).at(1)); QVERIFY(!now); @@ -1697,10 +1723,10 @@ void tst_QApplication::focusChanged() hbox2.addWidget(&pb2); parent2.show(); - QApplication::setActiveWindow(&parent2); // needs this on twm (focus follows mouse) - QVERIFY(spy.count() > 0); // one for deactivation, one for activation on Windows - old = qvariant_cast<QWidget*>(spy.at(spy.count()-1).at(0)); - now = qvariant_cast<QWidget*>(spy.at(spy.count()-1).at(1)); + QApplicationPrivate::setActiveWindow(&parent2); // needs this on twm (focus follows mouse) + QVERIFY(spy.size() > 0); // one for deactivation, one for activation on Windows + old = qvariant_cast<QWidget*>(spy.at(spy.size()-1).at(0)); + now = qvariant_cast<QWidget*>(spy.at(spy.size()-1).at(1)); QCOMPARE(now, &le2); QCOMPARE(now, QApplication::focusWidget()); QVERIFY(!old); @@ -1725,10 +1751,10 @@ void tst_QApplication::focusChanged() tab.simulate(now); if (!tabAllControls) { - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); QCOMPARE(now, QApplication::focusWidget()); } else { - QVERIFY(spy.count() > 0); + QVERIFY(spy.size() > 0); old = qvariant_cast<QWidget*>(spy.at(0).at(0)); now = qvariant_cast<QWidget*>(spy.at(0).at(1)); QCOMPARE(now, &pb2); @@ -1738,11 +1764,11 @@ void tst_QApplication::focusChanged() } if (!tabAllControls) { - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); QCOMPARE(now, QApplication::focusWidget()); } else { tab.simulate(now); - QVERIFY(spy.count() > 0); + QVERIFY(spy.size() > 0); old = qvariant_cast<QWidget*>(spy.at(0).at(0)); now = qvariant_cast<QWidget*>(spy.at(0).at(1)); QCOMPARE(now, &le2); @@ -1752,11 +1778,11 @@ void tst_QApplication::focusChanged() } if (!tabAllControls) { - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); QCOMPARE(now, QApplication::focusWidget()); } else { backtab.simulate(now); - QVERIFY(spy.count() > 0); + QVERIFY(spy.size() > 0); old = qvariant_cast<QWidget*>(spy.at(0).at(0)); now = qvariant_cast<QWidget*>(spy.at(0).at(1)); QCOMPARE(now, &pb2); @@ -1767,12 +1793,12 @@ void tst_QApplication::focusChanged() if (!tabAllControls) { - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); QCOMPARE(now, QApplication::focusWidget()); old = &pb2; } else { backtab.simulate(now); - QVERIFY(spy.count() > 0); + QVERIFY(spy.size() > 0); old = qvariant_cast<QWidget*>(spy.at(0).at(0)); now = qvariant_cast<QWidget*>(spy.at(0).at(1)); QCOMPARE(now, &le2); @@ -1783,10 +1809,10 @@ void tst_QApplication::focusChanged() click.simulate(old); if (!(pb2.focusPolicy() & Qt::ClickFocus)) { - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); QCOMPARE(now, QApplication::focusWidget()); } else { - QVERIFY(spy.count() > 0); + QVERIFY(spy.size() > 0); old = qvariant_cast<QWidget*>(spy.at(0).at(0)); now = qvariant_cast<QWidget*>(spy.at(0).at(1)); QCOMPARE(now, &pb2); @@ -1795,7 +1821,7 @@ void tst_QApplication::focusChanged() spy.clear(); click.simulate(old); - QVERIFY(spy.count() > 0); + QVERIFY(spy.size() > 0); old = qvariant_cast<QWidget*>(spy.at(0).at(0)); now = qvariant_cast<QWidget*>(spy.at(0).at(1)); QCOMPARE(now, &le2); @@ -1805,16 +1831,16 @@ void tst_QApplication::focusChanged() } parent1.activateWindow(); - QApplication::setActiveWindow(&parent1); // needs this on twm (focus follows mouse) - QVERIFY(spy.count() == 1 || spy.count() == 2); // one for deactivation, one for activation on Windows + QApplicationPrivate::setActiveWindow(&parent1); // needs this on twm (focus follows mouse) + QVERIFY(spy.size() == 1 || spy.size() == 2); // one for deactivation, one for activation on Windows //on windows, the change of focus is made in 2 steps //(the focusChanged SIGNAL is emitted twice) - if (spy.count()==1) - old = qvariant_cast<QWidget*>(spy.at(spy.count()-1).at(0)); + if (spy.size()==1) + old = qvariant_cast<QWidget*>(spy.at(spy.size()-1).at(0)); else - old = qvariant_cast<QWidget*>(spy.at(spy.count()-2).at(0)); - now = qvariant_cast<QWidget*>(spy.at(spy.count()-1).at(1)); + old = qvariant_cast<QWidget*>(spy.at(spy.size()-2).at(0)); + now = qvariant_cast<QWidget*>(spy.at(spy.size()-1).at(1)); QCOMPARE(now, &le1); QCOMPARE(now, QApplication::focusWidget()); QCOMPARE(old, &le2); @@ -2027,6 +2053,122 @@ void tst_QApplication::applicationPalettePolish() } } +void tst_QApplication::setColorScheme() +{ + int argc = 1; + QApplication app(argc, &argv0); + + if (QStringList{"minimal", "offscreen", "wayland", "xcb", "wasm", "webassembly"} + .contains(QGuiApplication::platformName(), Qt::CaseInsensitive)) { + QSKIP("Setting the colorScheme is not implemented on this platform."); + } + qDebug() << "Testing setColorScheme on platform" << QGuiApplication::platformName(); + + if (QByteArrayView(app.style()->metaObject()->className()) == "QWindowsVistaStyle") + QSKIP("Setting the colorScheme is not supported with the Windows Vista style."); + + const Qt::ColorScheme defaultColorScheme = QApplication::styleHints()->colorScheme(); + // if we implement setColorScheme, then we must be able to read it + QVERIFY(defaultColorScheme != Qt::ColorScheme::Unknown); + const Qt::ColorScheme newColorScheme = defaultColorScheme == Qt::ColorScheme::Light + ? Qt::ColorScheme::Dark : Qt::ColorScheme::Light; + + class TopLevelWidget : public QWidget + { + QList<QEvent::Type> events; + public: + TopLevelWidget() + { + setObjectName("colorScheme TopLevelWidget"); + } + + void clearEvents() + { + events.clear(); + } + qsizetype eventCount(QEvent::Type type) const + { + return events.count(type); + } + protected: + bool event(QEvent *event) override + { + switch (event->type()) { + case QEvent::ApplicationPaletteChange: + case QEvent::PaletteChange: + case QEvent::ThemeChange: + events << event->type(); + break; + default: + break; + } + + return QWidget::event(event); + } + } topLevelWidget; + topLevelWidget.show(); + QVERIFY(QTest::qWaitForWindowExposed(&topLevelWidget)); + + QSignalSpy colorSchemeChangedSpy(app.styleHints(), &QStyleHints::colorSchemeChanged); + + // always start with a clean list + topLevelWidget.clearEvents(); + const QPalette defaultPalette = topLevelWidget.palette(); + + bool oldPaletteWhenSchemeChanged = false; + connect(app.styleHints(), &QStyleHints::colorSchemeChanged, this, + [defaultPalette, &topLevelWidget, &oldPaletteWhenSchemeChanged]{ + oldPaletteWhenSchemeChanged = defaultPalette == topLevelWidget.palette(); + }); + + app.styleHints()->setColorScheme(newColorScheme); + QTRY_COMPARE(colorSchemeChangedSpy.count(), 1); + // We have not yet updated the palette when we emit the colorSchemeChanged + // signal, so the toplevel widget should still use the previous palette + QVERIFY(oldPaletteWhenSchemeChanged); + QCOMPARE(topLevelWidget.eventCount(QEvent::ThemeChange), 1); + // We can't guarantee that there is only one ApplicationPaletteChange, + // and they might arrive asynchronously in response to ThemeChange + QTRY_COMPARE_GE(topLevelWidget.eventCount(QEvent::ApplicationPaletteChange), 1); + // But we can guarantee a single PaletteChange event for the widget + QCOMPARE(topLevelWidget.eventCount(QEvent::PaletteChange), 1); + // The palette should have changed + QCOMPARE_NE(topLevelWidget.palette(), defaultPalette); + + topLevelWidget.clearEvents(); + colorSchemeChangedSpy.clear(); + + // verify that a widget shown with a color scheme override in place respect that + QWidget newWidget; + newWidget.show(); + QVERIFY(QTest::qWaitForWindowExposed(&newWidget)); + QCOMPARE(newWidget.palette(), topLevelWidget.palette()); + + // Setting to Unknown should follow the system preference again + app.styleHints()->setColorScheme(Qt::ColorScheme::Unknown); + QTRY_COMPARE(colorSchemeChangedSpy.count(), 1); + QCOMPARE(app.styleHints()->colorScheme(), defaultColorScheme); + QTRY_COMPARE(topLevelWidget.eventCount(QEvent::PaletteChange), 1); + + auto debugPalette = qScopeGuard([defaultPalette, &topLevelWidget]{ + qDebug() << "Inspecting palettes for differences"; + const QPalette palette = topLevelWidget.palette(); + for (int g = 0; g < QPalette::NColorGroups; ++g) { + for (int r = 0; r < QPalette::NColorRoles; ++r) { + const auto group = static_cast<QPalette::ColorGroup>(g); + const auto role = static_cast<QPalette::ColorRole>(r); + qDebug() << "...Checking" << group << role; + const auto actualBrush = palette.brush(group, role); + const auto expectedBrush = defaultPalette.brush(group, role); + if (palette.brush(group, role) != defaultPalette.brush(group, role)) + qWarning() << "...Difference in" << group << role << actualBrush << expectedBrush; + } + } + }); + QCOMPARE(topLevelWidget.palette(), defaultPalette); + debugPalette.dismiss(); +} + void tst_QApplication::allWidgets() { int argc = 1; @@ -2049,11 +2191,11 @@ void tst_QApplication::topLevelWidgets() #endif QCoreApplication::processEvents(); QVERIFY(QApplication::topLevelWidgets().contains(w)); - QCOMPARE(QApplication::topLevelWidgets().count(), 1); + QCOMPARE(QApplication::topLevelWidgets().size(), 1); delete w; w = nullptr; QCoreApplication::processEvents(); - QCOMPARE(QApplication::topLevelWidgets().count(), 0); + QCOMPARE(QApplication::topLevelWidgets().size(), 0); } @@ -2470,7 +2612,7 @@ void tst_QApplication::wheelEventPropagation() int vcount = 0; int hcount = 0; - for (const auto &event : qAsConst(events)) { + for (const auto &event : std::as_const(events)) { const QPoint pixelDelta = event.orientation == Qt::Vertical ? QPoint(0, -scrollStep) : QPoint(-scrollStep, 0); const QPoint angleDelta = event.orientation == Qt::Vertical ? QPoint(0, -120) : QPoint(-120, 0); QWindowSystemInterface::handleWheelEvent(outerArea.windowHandle(), center, global, @@ -2481,10 +2623,10 @@ void tst_QApplication::wheelEventPropagation() else ++hcount; QCoreApplication::processEvents(); - QCOMPARE(innerVSpy.count(), innerScrolls ? vcount : 0); - QCOMPARE(innerHSpy.count(), innerScrolls ? hcount : 0); - QCOMPARE(outerVSpy.count(), innerScrolls ? 0 : vcount); - QCOMPARE(outerHSpy.count(), innerScrolls ? 0 : hcount); + QCOMPARE(innerVSpy.size(), innerScrolls ? vcount : 0); + QCOMPARE(innerHSpy.size(), innerScrolls ? hcount : 0); + QCOMPARE(outerVSpy.size(), innerScrolls ? 0 : vcount); + QCOMPARE(outerHSpy.size(), innerScrolls ? 0 : hcount); } } @@ -2503,6 +2645,20 @@ void tst_QApplication::qtbug_12673() #endif } +void tst_QApplication::qtbug_103611() +{ + { + int argc = 0; + QApplication app(argc, nullptr); + auto ll = QLocale().uiLanguages(); + } + { + int argc = 0; + QApplication app(argc, nullptr); + auto ll = QLocale().uiLanguages(); + } +} + class NoQuitOnHideWidget : public QWidget { Q_OBJECT @@ -2532,8 +2688,26 @@ public: explicit ShowCloseShowWidget(bool showAgain, QWidget *parent = nullptr) : QWidget(parent), showAgain(showAgain) { + int timeout = 500; +#ifdef Q_OS_ANDROID + // On Android, CI Android emulator is not running HW accelerated graphics and can be slow, + // use a longer timeout to avoid flaky failures + timeout = 1000; +#endif + QTimer::singleShot(timeout, this, [] () { QCoreApplication::exit(1); }); + } + + bool shown = false; + +protected: + void showEvent(QShowEvent *) override + { QTimer::singleShot(0, this, &ShowCloseShowWidget::doClose); - QTimer::singleShot(500, this, [] () { QCoreApplication::exit(1); }); + shown = true; + } + void hideEvent(QHideEvent *) override + { + shown = false; } private slots: @@ -2549,16 +2723,21 @@ private: void tst_QApplication::abortQuitOnShow() { + if (QGuiApplication::platformName().startsWith(QLatin1String("wayland"), Qt::CaseInsensitive)) + QSKIP("Wayland: This crash, see QTBUG-123172."); + int argc = 0; QApplication app(argc, nullptr); ShowCloseShowWidget window1(false); window1.setWindowTitle(QLatin1String(QTest::currentTestFunction())); window1.show(); + QVERIFY(QTest::qWaitFor([&window1](){ return window1.shown; })); QCOMPARE(QCoreApplication::exec(), 0); ShowCloseShowWidget window2(true); window2.setWindowTitle(QLatin1String(QTest::currentTestFunction())); window2.show(); + QVERIFY(QTest::qWaitFor([&window2](){ return window2.shown; })); QCOMPARE(QCoreApplication::exec(), 1); } @@ -2573,7 +2752,7 @@ void tst_QApplication::staticFunctions() QApplication::activeModalWidget(); QApplication::focusWidget(); QApplication::activeWindow(); - QApplication::setActiveWindow(nullptr); + QApplicationPrivate::setActiveWindow(nullptr); QApplication::widgetAt(QPoint(0, 0)); QApplication::topLevelAt(QPoint(0, 0)); QTest::ignoreMessage(QtWarningMsg, "Must construct a QApplication first."); diff --git a/tests/auto/widgets/kernel/qboxlayout/CMakeLists.txt b/tests/auto/widgets/kernel/qboxlayout/CMakeLists.txt index 8fdc0d21a9..5b60382fba 100644 --- a/tests/auto/widgets/kernel/qboxlayout/CMakeLists.txt +++ b/tests/auto/widgets/kernel/qboxlayout/CMakeLists.txt @@ -1,13 +1,20 @@ -# Generated from qboxlayout.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qboxlayout Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qboxlayout LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qboxlayout SOURCES tst_qboxlayout.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::Gui Qt::TestPrivate Qt::Widgets diff --git a/tests/auto/widgets/kernel/qboxlayout/tst_qboxlayout.cpp b/tests/auto/widgets/kernel/qboxlayout/tst_qboxlayout.cpp index fc4302b1ff..4313d9891c 100644 --- a/tests/auto/widgets/kernel/qboxlayout/tst_qboxlayout.cpp +++ b/tests/auto/widgets/kernel/qboxlayout/tst_qboxlayout.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> @@ -116,7 +116,7 @@ void tst_QBoxLayout::insertLayout() QCOMPARE(dummyParentLayout->count(), 1); // add subLayout to another layout - QTest::ignoreMessage(QtWarningMsg, "QLayout::addChildLayout: layout \"\" already has a parent"); + QTest::ignoreMessage(QtWarningMsg, "QLayout::addChildLayout: layout QHBoxLayout \"\" already has a parent"); vbox->addLayout(subLayout); QCOMPARE((subLayout->parent() == vbox), (vbox->count() == 1)); } @@ -192,7 +192,7 @@ void tst_QBoxLayout::setStyleShouldChangeSpacing() window.setWindowTitle(QTest::currentTestFunction()); QHBoxLayout *hbox = new QHBoxLayout(&window); QPushButton *pb1 = new QPushButton(tr("The spacing between this")); - QPushButton *pb2 = new QPushButton(tr("and this button should depend on the style of the parent widget"));; + QPushButton *pb2 = new QPushButton(tr("and this button should depend on the style of the parent widget")); pb1->setAttribute(Qt::WA_LayoutUsesWidgetRect); pb2->setAttribute(Qt::WA_LayoutUsesWidgetRect); hbox->addWidget(pb1); @@ -517,14 +517,14 @@ void tst_QBoxLayout::testLayoutEngine() QHBoxLayout box; box.setSpacing(spacing); int i; - for (i = 0; i < itemDescriptions.count(); ++i) { + for (i = 0; i < itemDescriptions.size(); ++i) { Descr descr = itemDescriptions.at(i); LayoutItem *li = new LayoutItem(descr); box.addItem(li); box.setStretch(i, descr.stretch); } box.setGeometry(QRect(0,0,size,100)); - for (i = 0; i < expectedSizes.count(); ++i) { + for (i = 0; i < expectedSizes.size(); ++i) { int xSize = expectedSizes.at(i); int xPos = expectedPositions.at(i); QLayoutItem *item = box.itemAt(i); diff --git a/tests/auto/widgets/kernel/qformlayout/BLACKLIST b/tests/auto/widgets/kernel/qformlayout/BLACKLIST deleted file mode 100644 index e69de29bb2..0000000000 --- a/tests/auto/widgets/kernel/qformlayout/BLACKLIST +++ /dev/null diff --git a/tests/auto/widgets/kernel/qformlayout/CMakeLists.txt b/tests/auto/widgets/kernel/qformlayout/CMakeLists.txt index b084fe1634..9e1da4c6a3 100644 --- a/tests/auto/widgets/kernel/qformlayout/CMakeLists.txt +++ b/tests/auto/widgets/kernel/qformlayout/CMakeLists.txt @@ -1,13 +1,20 @@ -# Generated from qformlayout.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qformlayout Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qformlayout LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qformlayout SOURCES tst_qformlayout.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::Gui Qt::TestPrivate Qt::Widgets diff --git a/tests/auto/widgets/kernel/qformlayout/tst_qformlayout.cpp b/tests/auto/widgets/kernel/qformlayout/tst_qformlayout.cpp index bccd443092..9638823538 100644 --- a/tests/auto/widgets/kernel/qformlayout/tst_qformlayout.cpp +++ b/tests/auto/widgets/kernel/qformlayout/tst_qformlayout.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> @@ -112,6 +112,7 @@ private slots: void setLayout(); void hideShowRow(); void showWithHiddenRow(); + void hiddenRowAndStretch(); /* QLayoutItem *itemAt(int row, ItemRole role) const; @@ -1065,7 +1066,7 @@ void tst_QFormLayout::setLayout() // should be ignored and generate warnings QTest::ignoreMessage(QtWarningMsg, "QFormLayoutPrivate::setItem: Cell (3, 1) already occupied"); layout.setLayout(3, QFormLayout::FieldRole, &l4); - QTest::ignoreMessage(QtWarningMsg, "QLayout::addChildLayout: layout \"\" already has a parent"); + QTest::ignoreMessage(QtWarningMsg, "QLayout::addChildLayout: layout QHBoxLayout \"\" already has a parent"); layout.setLayout(-1, QFormLayout::FieldRole, &l4); QCOMPARE(layout.count(), 3); QCOMPARE(layout.rowCount(), 6); @@ -1253,6 +1254,49 @@ void tst_QFormLayout::showWithHiddenRow() topLevel.show(); } +/* + Test that hiding rows does not leave outdated layout data behind + in hidden items that results in out-of-bounds array access. See + QTBUG-109237. +*/ +void tst_QFormLayout::hiddenRowAndStretch() +{ + QWidget topLevel; + QFormLayout layout; + layout.setRowWrapPolicy(QFormLayout::WrapAllRows); + + // We need our own stretcher item so that QFormLayout doesn't insert + // it's own, as that would grow the size of the layout data array again. + QSpacerItem *stretch = new QSpacerItem(100, 100, QSizePolicy::Expanding, QSizePolicy::Expanding); + layout.setItem(0, QFormLayout::FieldRole, stretch); + + QLabel *lastLabel = nullptr; + QLineEdit *lastField = nullptr; + for (int row = 1; row < 4; ++row) { + QLabel *label = new QLabel(QString("Label %1").arg(row)); + label->setWordWrap(true); + QLineEdit *field = new QLineEdit; + layout.setWidget(row, QFormLayout::LabelRole, label); + layout.setWidget(row, QFormLayout::FieldRole, field); + if (row == 3) { + lastLabel = label; + lastField = field; + } + } + + Q_ASSERT(lastLabel); + Q_ASSERT(lastField); + + topLevel.setLayout(&layout); + topLevel.sizeHint(); + + lastLabel->setVisible(false); + lastField->setVisible(false); + + // should not assert here + topLevel.show(); +} + void tst_QFormLayout::itemAt() { QWidget topLevel; diff --git a/tests/auto/widgets/kernel/qgesturerecognizer/CMakeLists.txt b/tests/auto/widgets/kernel/qgesturerecognizer/CMakeLists.txt index 9dfd6cb20c..ffa54992d3 100644 --- a/tests/auto/widgets/kernel/qgesturerecognizer/CMakeLists.txt +++ b/tests/auto/widgets/kernel/qgesturerecognizer/CMakeLists.txt @@ -1,13 +1,20 @@ -# Generated from qgesturerecognizer.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qgesturerecognizer Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qgesturerecognizer LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qgesturerecognizer SOURCES tst_qgesturerecognizer.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::CorePrivate Qt::Gui Qt::GuiPrivate diff --git a/tests/auto/widgets/kernel/qgesturerecognizer/tst_qgesturerecognizer.cpp b/tests/auto/widgets/kernel/qgesturerecognizer/tst_qgesturerecognizer.cpp index 6d5282732a..cdab480d84 100644 --- a/tests/auto/widgets/kernel/qgesturerecognizer/tst_qgesturerecognizer.cpp +++ b/tests/auto/widgets/kernel/qgesturerecognizer/tst_qgesturerecognizer.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QtTest/QTest> @@ -71,7 +71,7 @@ TestWidget::TestWidget(const GestureTypeVector &gestureTypes) { setAttribute(Qt::WA_AcceptTouchEvents); - foreach (Qt::GestureType gestureType, gestureTypes) { + for (Qt::GestureType gestureType : gestureTypes) { grabGesture(gestureType); m_receivedGestures.insert(gestureType, false); } @@ -269,7 +269,7 @@ void tst_QGestureRecognizer::swipeGesture() // Press point #3 points.append(points.last() + fingerDistance); - swipeSequence.press(points.size() - 1, points.last(), &widget); + swipeSequence.stationary(0).stationary(1).press(points.size() - 1, points.last(), &widget); swipeSequence.commit(); Q_ASSERT(points.size() == swipePoints); diff --git a/tests/auto/widgets/kernel/qgridlayout/CMakeLists.txt b/tests/auto/widgets/kernel/qgridlayout/CMakeLists.txt index c9ac563f01..bf72bc0ae6 100644 --- a/tests/auto/widgets/kernel/qgridlayout/CMakeLists.txt +++ b/tests/auto/widgets/kernel/qgridlayout/CMakeLists.txt @@ -1,14 +1,21 @@ -# Generated from qgridlayout.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qgridlayout Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qgridlayout LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qgridlayout SOURCES sortdialog.ui tst_qgridlayout.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::CorePrivate Qt::Gui Qt::GuiPrivate diff --git a/tests/auto/widgets/kernel/qgridlayout/tst_qgridlayout.cpp b/tests/auto/widgets/kernel/qgridlayout/tst_qgridlayout.cpp index fda88fcd83..3c325699a7 100644 --- a/tests/auto/widgets/kernel/qgridlayout/tst_qgridlayout.cpp +++ b/tests/auto/widgets/kernel/qgridlayout/tst_qgridlayout.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> @@ -58,7 +58,8 @@ private slots: static inline int visibleTopLevelWidgetCount() { int result= 0; - foreach (const QWidget *topLevel, QApplication::topLevelWidgets()) { + const auto topLevels = QApplication::topLevelWidgets(); + for (const QWidget *topLevel : topLevels) { if (topLevel->isVisible()) ++result; } @@ -210,6 +211,9 @@ void tst_QGridLayout::badDistributionBug() void tst_QGridLayout::setMinAndMaxSize() { + if (QGuiApplication::platformName().startsWith(QLatin1String("wayland"), Qt::CaseInsensitive)) + QSKIP("This test crashes on Wayland, see also QTBUG-107184"); + QWidget widget; setFrameless(&widget); QGridLayout layout(&widget); @@ -658,7 +662,7 @@ void tst_QGridLayout::spacingsAndMargins() QSKIP("The screen is too small to run this test case"); // We are relying on the order here... - for (int pi = 0; pi < sizehinters.count(); ++pi) { + for (int pi = 0; pi < sizehinters.size(); ++pi) { QPoint pt = sizehinters.at(pi)->mapTo(&toplevel, QPoint(0, 0)); QCOMPARE(pt, expectedpositions.at(pi)); } @@ -828,7 +832,7 @@ void tst_QGridLayout::minMaxSize() QList<QPointer<SizeHinterFrame> > sizehinters; for (int i = 0; i < rows; ++i) { for (int j = 0; j < columns; ++j) { - SizeInfo si = sizeinfos.at(sizehinters.count()); + SizeInfo si = sizeinfos.at(sizehinters.size()); int numpixels = si.hfwNumPixels; if (pass == 1 && numpixels == -1) numpixels = -2; //### yuk, (and don't fake it if it already tests sizehint) @@ -857,7 +861,7 @@ void tst_QGridLayout::minMaxSize() QTRY_COMPARE(toplevel.size(), toplevel.sizeHint()); } // We are relying on the order here... - for (int pi = 0; pi < sizehinters.count(); ++pi) { + for (int pi = 0; pi < sizehinters.size(); ++pi) { QPoint pt = sizehinters.at(pi)->mapTo(&toplevel, QPoint(0, 0)); QCOMPARE(pt, sizeinfos.at(pi).expectedPos); } @@ -1027,7 +1031,7 @@ void tst_QGridLayout::styleDependentSpacingsAndMargins() widget.adjustSize(); QApplication::processEvents(); - for (int pi = 0; pi < expectedpositions.count(); ++pi) { + for (int pi = 0; pi < expectedpositions.size(); ++pi) { QCOMPARE(sizehinters.at(pi)->pos(), expectedpositions.at(pi)); } } @@ -1417,7 +1421,7 @@ void tst_QGridLayout::layoutSpacing() QLayout *layout = widget->layout(); QVERIFY(layout); - for (int pi = 0; pi < expectedpositions.count(); ++pi) { + for (int pi = 0; pi < expectedpositions.size(); ++pi) { QLayoutItem *item = layout->itemAt(pi); //qDebug() << item->widget()->pos(); QCOMPARE(item->widget()->pos(), expectedpositions.at(pi)); diff --git a/tests/auto/widgets/kernel/qlayout/CMakeLists.txt b/tests/auto/widgets/kernel/qlayout/CMakeLists.txt index 89cb2ce8e9..6bda750c0f 100644 --- a/tests/auto/widgets/kernel/qlayout/CMakeLists.txt +++ b/tests/auto/widgets/kernel/qlayout/CMakeLists.txt @@ -1,9 +1,16 @@ -# Generated from qlayout.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qlayout Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qlayout LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + # Collect test data file(GLOB_RECURSE test_data_glob RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} @@ -13,7 +20,7 @@ list(APPEND test_data ${test_data_glob}) qt_internal_add_test(tst_qlayout SOURCES tst_qlayout.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::Gui Qt::TestPrivate Qt::Widgets diff --git a/tests/auto/widgets/kernel/qlayout/tst_qlayout.cpp b/tests/auto/widgets/kernel/qlayout/tst_qlayout.cpp index 9d5f7940cc..bd170ca8ab 100644 --- a/tests/auto/widgets/kernel/qlayout/tst_qlayout.cpp +++ b/tests/auto/widgets/kernel/qlayout/tst_qlayout.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> @@ -378,10 +378,10 @@ void tst_QLayout::removeWidget() { QHBoxLayout layout; QCOMPARE(layout.count(), 0); - QWidget w; - layout.addWidget(&w); + std::unique_ptr<QWidget> w(new QWidget); + layout.addWidget(w.get()); QCOMPARE(layout.count(), 1); - layout.removeWidget(&w); + layout.removeWidget(w.get()); QCOMPARE(layout.count(), 0); QPointer<QLayout> childLayout(new QHBoxLayout); @@ -395,6 +395,12 @@ void tst_QLayout::removeWidget() QCOMPARE(layout.count(), 0); QVERIFY(!childLayout.isNull()); + + // Test inactive layout consumes ChildRemoved event (QTBUG-124151) + layout.addWidget(w.get()); + layout.setEnabled(false); + w.reset(); + layout.setEnabled(true); } QTEST_MAIN(tst_QLayout) diff --git a/tests/auto/widgets/kernel/qshortcut/CMakeLists.txt b/tests/auto/widgets/kernel/qshortcut/CMakeLists.txt index fd97a436d0..517286f324 100644 --- a/tests/auto/widgets/kernel/qshortcut/CMakeLists.txt +++ b/tests/auto/widgets/kernel/qshortcut/CMakeLists.txt @@ -1,16 +1,38 @@ -# Generated from qshortcut.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qshortcut Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qshortcut LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qshortcut SOURCES tst_qshortcut.cpp INCLUDE_DIRECTORIES .. - PUBLIC_LIBRARIES + LIBRARIES + Qt::Gui + Qt::GuiPrivate + Qt::Widgets + Qt::WidgetsPrivate +) + +qt_internal_add_test(tst_qguishortcut_with_qapplication + SOURCES + ../../../gui/kernel/qshortcut/tst_qshortcut.cpp + DEFINES + tst_QShortcut=tst_QGuiShortcutWithQApplication + INCLUDE_DIRECTORIES + .. + LIBRARIES Qt::Gui Qt::GuiPrivate Qt::Widgets + Qt::WidgetsPrivate ) diff --git a/tests/auto/widgets/kernel/qshortcut/tst_qshortcut.cpp b/tests/auto/widgets/kernel/qshortcut/tst_qshortcut.cpp index 2beecc7112..d34df43b01 100644 --- a/tests/auto/widgets/kernel/qshortcut/tst_qshortcut.cpp +++ b/tests/auto/widgets/kernel/qshortcut/tst_qshortcut.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> @@ -23,6 +23,8 @@ #include <QtGui/private/qguiapplication_p.h> #include <QtGui/qpa/qplatformintegration.h> +#include <QtWidgets/private/qapplication_p.h> + QT_BEGIN_NAMESPACE class QMainWindow; class QTextEdit; @@ -313,11 +315,11 @@ void tst_QShortcut::number_data() Shift + Qt::Key_Plus on Shift + Qt::Key_Pluss Qt::Key_Plus on Shift + Qt::Key_Pluss */ - QTest::newRow("N002 - slot1") << SetupAccel << TriggerSlot1 << QString() << int(Qt::SHIFT | Qt::Key_M) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; - QTest::newRow("N002:Shift+M - [Shift+M]") << TestAccel << NoWidget << QString() << int(Qt::SHIFT | Qt::Key_M) << int('M') << 0 << 0 << 0 << 0 << 0 << 0 << Slot1Triggered; + QTest::newRow("N002 - slot1") << SetupAccel << TriggerSlot1 << QString() << QKeyCombination(Qt::SHIFT, Qt::Key_M).toCombined() << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; + QTest::newRow("N002:Shift+M - [Shift+M]") << TestAccel << NoWidget << QString() << QKeyCombination(Qt::SHIFT, Qt::Key_M).toCombined() << int('M') << 0 << 0 << 0 << 0 << 0 << 0 << Slot1Triggered; QTest::newRow("N002:M - [Shift+M]") << TestAccel << NoWidget << QString() << int(Qt::Key_M) << int('m') << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; - QTest::newRow("N002 - slot2") << SetupAccel << TriggerSlot2 << QString() << int(Qt::SHIFT | Qt::Key_Plus) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; - QTest::newRow("N002:Shift++ [Shift++]") << TestAccel << NoWidget << QString() << int(Qt::SHIFT | Qt::Key_Plus) << int('+') << 0 << 0 << 0 << 0 << 0 << 0 << Slot2Triggered; + QTest::newRow("N002 - slot2") << SetupAccel << TriggerSlot2 << QString() << QKeyCombination(Qt::SHIFT, Qt::Key_Plus).toCombined() << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; + QTest::newRow("N002:Shift++ [Shift++]") << TestAccel << NoWidget << QString() << QKeyCombination(Qt::SHIFT, Qt::Key_Plus).toCombined() << int('+') << 0 << 0 << 0 << 0 << 0 << 0 << Slot2Triggered; QTest::newRow("N002:+ [Shift++]") << TestAccel << NoWidget << QString() << int(Qt::Key_Plus) << int('+') << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; QTest::newRow("N002 - clear") << ClearAll << NoWidget << QString() << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; // Clear all @@ -335,8 +337,8 @@ void tst_QShortcut::number_data() Qt::Key_F1 on Shift + Qt::Key_F1 */ - QTest::newRow("N004 - slot1") << SetupAccel << TriggerSlot1 << QString() << int(Qt::SHIFT | Qt::Key_F1) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; - QTest::newRow("N004:Shift+F1 - [Shift+F1]") << TestAccel << NoWidget << QString() << int(Qt::SHIFT | Qt::Key_F1) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << Slot1Triggered; + QTest::newRow("N004 - slot1") << SetupAccel << TriggerSlot1 << QString() << QKeyCombination(Qt::SHIFT, Qt::Key_F1).toCombined() << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; + QTest::newRow("N004:Shift+F1 - [Shift+F1]") << TestAccel << NoWidget << QString() << QKeyCombination(Qt::SHIFT, Qt::Key_F1).toCombined() << 0 << 0 << 0 << 0 << 0 << 0 << 0 << Slot1Triggered; QTest::newRow("N004:F1 - [Shift+F1]") << TestAccel << NoWidget << QString() << int(Qt::Key_F1) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; QTest::newRow("N004 - clear") << ClearAll << NoWidget << QString() << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; // Clear all @@ -352,7 +354,7 @@ void tst_QShortcut::number_data() //QTest::newRow("N005a:Shift+Tab - [Tab]") << TestAccel << NoWidget << QString() << int(Qt::SHIFT + Qt::Key_Tab) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << Slot1Triggered; // (Shift+)BackTab != Tab, but Shift+BackTab == Shift+Tab QTest::newRow("N005a:Backtab - [Tab]") << TestAccel << NoWidget << QString() << int(Qt::Key_Backtab) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; - QTest::newRow("N005a:Shift+Backtab - [Tab]") << TestAccel << NoWidget << QString() << int(Qt::SHIFT | Qt::Key_Backtab) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; + QTest::newRow("N005a:Shift+Backtab - [Tab]") << TestAccel << NoWidget << QString() << QKeyCombination(Qt::SHIFT, Qt::Key_Backtab).toCombined() << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; QTest::newRow("N005a - clear") << ClearAll << NoWidget << QString() << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; // Clear all /* Testing Single Sequences @@ -361,11 +363,11 @@ void tst_QShortcut::number_data() Qt::Key_Backtab on Shift + Qt::Key_Tab Shift + Qt::Key_Backtab on Shift + Qt::Key_Tab */ - QTest::newRow("N005b - slot1") << SetupAccel << TriggerSlot1 << QString() << int(Qt::SHIFT | Qt::Key_Tab) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; + QTest::newRow("N005b - slot1") << SetupAccel << TriggerSlot1 << QString() << QKeyCombination(Qt::SHIFT, Qt::Key_Tab).toCombined() << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; QTest::newRow("N005b:Tab - [Shift+Tab]") << TestAccel << NoWidget << QString() << int(Qt::Key_Tab) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; - QTest::newRow("N005b:Shift+Tab - [Shift+Tab]") << TestAccel << NoWidget << QString() << int(Qt::SHIFT | Qt::Key_Tab) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << Slot1Triggered; + QTest::newRow("N005b:Shift+Tab - [Shift+Tab]") << TestAccel << NoWidget << QString() << QKeyCombination(Qt::SHIFT, Qt::Key_Tab).toCombined() << 0 << 0 << 0 << 0 << 0 << 0 << 0 << Slot1Triggered; QTest::newRow("N005b:BackTab - [Shift+Tab]") << TestAccel << NoWidget << QString() << int(Qt::Key_Backtab) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; - QTest::newRow("N005b:Shift+BackTab - [Shift+Tab]") << TestAccel << NoWidget << QString() << int(Qt::SHIFT | Qt::Key_Backtab) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << Slot1Triggered; + QTest::newRow("N005b:Shift+BackTab - [Shift+Tab]") << TestAccel << NoWidget << QString() << QKeyCombination(Qt::SHIFT, Qt::Key_Backtab).toCombined() << 0 << 0 << 0 << 0 << 0 << 0 << 0 << Slot1Triggered; QTest::newRow("N005b - clear") << ClearAll << NoWidget << QString() << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; // Clear all /* Testing Single Sequences @@ -378,10 +380,10 @@ void tst_QShortcut::number_data() QTest::newRow("N006a:Tab - [BackTab]") << TestAccel << NoWidget << QString() << int(Qt::Key_Tab) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; // This should work, since platform dependent code will transform the // Shift+Tab into a Shift+BackTab, which should trigger the shortcut - QTest::newRow("N006a:Shift+Tab - [BackTab]") << TestAccel << NoWidget << QString() << int(Qt::SHIFT | Qt::Key_Tab) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << Slot1Triggered; //XFAIL + QTest::newRow("N006a:Shift+Tab - [BackTab]") << TestAccel << NoWidget << QString() << QKeyCombination(Qt::SHIFT, Qt::Key_Tab).toCombined() << 0 << 0 << 0 << 0 << 0 << 0 << 0 << Slot1Triggered; //XFAIL QTest::newRow("N006a:BackTab - [BackTab]") << TestAccel << NoWidget << QString() << int(Qt::Key_Backtab) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << Slot1Triggered; //commented out because the behaviour changed, those tests should be updated - //QTest::newRow("N006a:Shift+BackTab - [BackTab]") << TestAccel << NoWidget << QString() << int(Qt::SHIFT + Qt::Key_Backtab) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << Slot1Triggered; + //QTest::newRow("N006a:Shift+BackTab - [BackTab]") << TestAccel << NoWidget << QString() << QKeyCombination(Qt::SHIFT, Qt::Key_Backtab).toCombined() << 0 << 0 << 0 << 0 << 0 << 0 << 0 << Slot1Triggered; QTest::newRow("N006a - clear") << ClearAll << NoWidget<< QString() << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; // Clear all /* Testing Single Sequences @@ -390,11 +392,11 @@ void tst_QShortcut::number_data() Qt::Key_Backtab on Shift + Qt::Key_Backtab Shift + Qt::Key_Backtab on Shift + Qt::Key_Backtab */ - QTest::newRow("N006b - slot1") << SetupAccel << TriggerSlot1 << QString() << int(Qt::SHIFT | Qt::Key_Backtab) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; + QTest::newRow("N006b - slot1") << SetupAccel << TriggerSlot1 << QString() << QKeyCombination(Qt::SHIFT, Qt::Key_Backtab).toCombined() << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; QTest::newRow("N006b:Tab - [Shift+BackTab]") << TestAccel << NoWidget << QString() << int(Qt::Key_Tab) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; - QTest::newRow("N006b:Shift+Tab - [Shift+BackTab]") << TestAccel << NoWidget << QString() << int(Qt::SHIFT | Qt::Key_Tab) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << Slot1Triggered; + QTest::newRow("N006b:Shift+Tab - [Shift+BackTab]") << TestAccel << NoWidget << QString() << QKeyCombination(Qt::SHIFT, Qt::Key_Tab).toCombined() << 0 << 0 << 0 << 0 << 0 << 0 << 0 << Slot1Triggered; QTest::newRow("N006b:BackTab - [Shift+BackTab]") << TestAccel << NoWidget << QString() << int(Qt::Key_Backtab) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; - QTest::newRow("N006b:Shift+BackTab - [Shift+BackTab]") << TestAccel << NoWidget << QString() << int(Qt::SHIFT | Qt::Key_Backtab) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << Slot1Triggered; //XFAIL + QTest::newRow("N006b:Shift+BackTab - [Shift+BackTab]") << TestAccel << NoWidget << QString() << QKeyCombination(Qt::SHIFT, Qt::Key_Backtab).toCombined() << 0 << 0 << 0 << 0 << 0 << 0 << 0 << Slot1Triggered; //XFAIL QTest::newRow("N006b - clear") << ClearAll << NoWidget << QString() << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; // Clear all //=========================================== @@ -407,9 +409,9 @@ void tst_QShortcut::number_data() Shift + Qt::Key_F1 */ QTest::newRow("N007 - slot1") << SetupAccel << TriggerSlot1 << QString() << int(Qt::Key_F1) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; - QTest::newRow("N007 - slot2") << SetupAccel << TriggerSlot2 << QString() << int(Qt::SHIFT | Qt::Key_F1) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; + QTest::newRow("N007 - slot2") << SetupAccel << TriggerSlot2 << QString() << QKeyCombination(Qt::SHIFT, Qt::Key_F1).toCombined() << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; QTest::newRow("N007:F1") << TestAccel << NoWidget << QString() << int(Qt::Key_F1) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << Slot1Triggered; - QTest::newRow("N007:Shift + F1") << TestAccel << NoWidget << QString() << int(Qt::SHIFT | Qt::Key_F1) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << Slot2Triggered; + QTest::newRow("N007:Shift + F1") << TestAccel << NoWidget << QString() << QKeyCombination(Qt::SHIFT, Qt::Key_F1).toCombined() << 0 << 0 << 0 << 0 << 0 << 0 << 0 << Slot2Triggered; QTest::newRow("N007 - clear") << ClearAll << NoWidget << QString() << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; // Clear all /* Testing Single Sequences @@ -419,13 +421,13 @@ void tst_QShortcut::number_data() Alt + Qt::Key_M */ QTest::newRow("N01 - slot1") << SetupAccel << TriggerSlot1 << QString() << int(Qt::Key_M) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; - QTest::newRow("N02 - slot2") << SetupAccel << TriggerSlot2 << QString() << int(Qt::SHIFT | Qt::Key_M) << int('M') << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; - QTest::newRow("N03 - slot1") << SetupAccel << TriggerSlot1 << QString() << int(Qt::CTRL | Qt::Key_M) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; - QTest::newRow("N04 - slot2") << SetupAccel << TriggerSlot2 << QString() << int(Qt::ALT | Qt::Key_M) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; + QTest::newRow("N02 - slot2") << SetupAccel << TriggerSlot2 << QString() << QKeyCombination(Qt::SHIFT, Qt::Key_M).toCombined() << int('M') << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; + QTest::newRow("N03 - slot1") << SetupAccel << TriggerSlot1 << QString() << QKeyCombination(Qt::CTRL, Qt::Key_M).toCombined() << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; + QTest::newRow("N04 - slot2") << SetupAccel << TriggerSlot2 << QString() << QKeyCombination(Qt::ALT, Qt::Key_M).toCombined() << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; QTest::newRow("N:Qt::Key_M") << TestAccel << NoWidget << QString() << int(Qt::Key_M) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << Slot1Triggered; - QTest::newRow("N:Shift+Qt::Key_M") << TestAccel << NoWidget << QString() << int(Qt::SHIFT | Qt::Key_M) << int('M') << 0 << 0 << 0 << 0 << 0 << 0 << Slot2Triggered; - QTest::newRow("N:Ctrl+Qt::Key_M") << TestAccel << NoWidget << QString() << int(Qt::CTRL | Qt::Key_M) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << Slot1Triggered; - QTest::newRow("N:Alt+Qt::Key_M") << TestAccel << NoWidget << QString() << int(Qt::ALT | Qt::Key_M) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << Slot2Triggered; + QTest::newRow("N:Shift+Qt::Key_M") << TestAccel << NoWidget << QString() << QKeyCombination(Qt::SHIFT, Qt::Key_M).toCombined() << int('M') << 0 << 0 << 0 << 0 << 0 << 0 << Slot2Triggered; + QTest::newRow("N:Ctrl+Qt::Key_M") << TestAccel << NoWidget << QString() << QKeyCombination(Qt::CTRL, Qt::Key_M).toCombined() << 0 << 0 << 0 << 0 << 0 << 0 << 0 << Slot1Triggered; + QTest::newRow("N:Alt+Qt::Key_M") << TestAccel << NoWidget << QString() << QKeyCombination(Qt::ALT, Qt::Key_M).toCombined() << 0 << 0 << 0 << 0 << 0 << 0 << 0 << Slot2Triggered; /* Testing Single Sequence Ambiguity Qt::Key_M on shortcut2 @@ -440,11 +442,11 @@ void tst_QShortcut::number_data() Qt::Key_K */ QTest::newRow("N06 - slot1") << SetupAccel << TriggerSlot1 << QString() << int(Qt::Key_Aring) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; - QTest::newRow("N07 - slot2") << SetupAccel << TriggerSlot2 << QString() << int(Qt::SHIFT | Qt::Key_Aring) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; + QTest::newRow("N07 - slot2") << SetupAccel << TriggerSlot2 << QString() << QKeyCombination(Qt::SHIFT, Qt::Key_Aring).toCombined() << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; QTest::newRow("N08 - slot2") << SetupAccel << TriggerSlot1 << QString() << int(Qt::Key_K) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; QTest::newRow("N:Qt::Key_aring") << TestAccel << NoWidget << QString() << int(Qt::Key_Aring) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << Slot1Triggered; - QTest::newRow("N:Qt::Key_Aring") << TestAccel << NoWidget << QString() << int(Qt::SHIFT | Qt::Key_Aring) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << Slot2Triggered; + QTest::newRow("N:Qt::Key_Aring") << TestAccel << NoWidget << QString() << QKeyCombination(Qt::SHIFT, Qt::Key_Aring).toCombined() << 0 << 0 << 0 << 0 << 0 << 0 << 0 << Slot2Triggered; QTest::newRow("N:Qt::Key_aring - Text Form") << TestAccel << NoWidget << QString() << 0 << 0xC5 << 0 << 0 << 0 << 0 << 0 << 0 << Slot1Triggered; QTest::newRow("N:Qt::Key_Aring - Text Form") << TestAccel << NoWidget << QString() << int(Qt::SHIFT) << 0xC5 << 0 << 0 << 0 << 0 << 0 << 0 << Slot2Triggered; QTest::newRow("N:Qt::Qt::Key_K") << TestAccel << NoWidget << QString() << int(Qt::Key_K) << int('k') << 0 << 0 << 0 << 0 << 0 << 0 << Slot1Triggered; @@ -457,11 +459,11 @@ void tst_QShortcut::number_data() */ QTest::newRow("N10 - slot1") << SetupAccel << TriggerSlot1 << QString() << int(Qt::Key_M) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; QTest::newRow("N11 - slot2") << SetupAccel << TriggerSlot2 << QString() << int(Qt::Key_I) << 0 << int(Qt::Key_M) << 0 << 0 << 0 << 0 << 0 << NoResult; - QTest::newRow("N12 - slot1") << SetupAccel << TriggerSlot1 << QString() << int(Qt::SHIFT | Qt::Key_I) << 0 << int(Qt::Key_M) << 0 << 0 << 0 << 0 << 0 << NoResult; + QTest::newRow("N12 - slot1") << SetupAccel << TriggerSlot1 << QString() << QKeyCombination(Qt::SHIFT, Qt::Key_I).toCombined() << 0 << int(Qt::Key_M) << 0 << 0 << 0 << 0 << 0 << NoResult; QTest::newRow("N:Qt::Key_M (2)") << TestAccel << NoWidget << QString() << int(Qt::Key_M) << int('m') << 0 << 0 << 0 << 0 << 0 << 0 << Slot1Triggered; QTest::newRow("N:Qt::Key_I, Qt::Key_M") << TestAccel << NoWidget << QString() << int(Qt::Key_I) << int('i') << int(Qt::Key_M) << int('m') << 0 << 0 << 0 << 0 << Slot2Triggered; - QTest::newRow("N:Shift+Qt::Key_I, Qt::Key_M") << TestAccel << NoWidget << QString() << int(Qt::SHIFT | Qt::Key_I) << int('I') << int(Qt::Key_M) << int('m') << 0 << 0 << 0 << 0 << Slot1Triggered; + QTest::newRow("N:Shift+Qt::Key_I, Qt::Key_M") << TestAccel << NoWidget << QString() << QKeyCombination(Qt::SHIFT, Qt::Key_I).toCombined() << int('I') << int(Qt::Key_M) << int('m') << 0 << 0 << 0 << 0 << Slot1Triggered; QTest::newRow("N:end") << TestEnd << NoWidget << QString() << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; } @@ -503,10 +505,10 @@ void tst_QShortcut::text_data() Ctrl + Qt::Key_Plus on Ctrl + Qt::Key_Pluss */ QTest::newRow("T002 - slot1") << SetupAccel << TriggerSlot1 << QString("Shift+M") << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; - QTest::newRow("T002:Shift+M - [Shift+M]") << TestAccel << NoWidget << QString() << int(Qt::SHIFT | Qt::Key_M) << int('M') << 0 << 0 << 0 << 0 << 0 << 0 << Slot1Triggered; + QTest::newRow("T002:Shift+M - [Shift+M]") << TestAccel << NoWidget << QString() << QKeyCombination(Qt::SHIFT, Qt::Key_M).toCombined() << int('M') << 0 << 0 << 0 << 0 << 0 << 0 << Slot1Triggered; QTest::newRow("T002:M - [Shift+M]") << TestAccel << NoWidget << QString() << int(Qt::Key_M) << int('m') << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; QTest::newRow("T002 - slot2") << SetupAccel << TriggerSlot2 << QString("Shift++") << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; - QTest::newRow("T002:Shift++ [Shift++]") << TestAccel << NoWidget << QString() << int(Qt::SHIFT | Qt::Key_Plus) << int('+') << 0 << 0 << 0 << 0 << 0 << 0 << Slot2Triggered; + QTest::newRow("T002:Shift++ [Shift++]") << TestAccel << NoWidget << QString() << QKeyCombination(Qt::SHIFT, Qt::Key_Plus).toCombined() << int('+') << 0 << 0 << 0 << 0 << 0 << 0 << Slot2Triggered; QTest::newRow("T002:+ [Shift++]") << TestAccel << NoWidget << QString() << int(Qt::Key_Plus) << int('+') << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; QTest::newRow("T002 - clear") << ClearAll << NoWidget << QString() << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; // Clear all @@ -518,7 +520,7 @@ void tst_QShortcut::text_data() QTest::newRow("T002b - slot1") << SetupAccel << TriggerSlot1 << QString("Ctrl++") << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; //commented out because the behaviour changed, those tests should be updated //QTest::newRow("T002b:Shift+Ctrl++ [Ctrl++]")<< TestAccel << NoWidget << QString() << int(Qt::SHIFT + Qt::CTRL + Qt::Key_Plus) << int('+') << 0 << 0 << 0 << 0 << 0 << 0 << Slot1Triggered; - QTest::newRow("T002b:Ctrl++ [Ctrl++]") << TestAccel << NoWidget << QString() << int(Qt::CTRL | Qt::Key_Plus) << int('+') << 0 << 0 << 0 << 0 << 0 << 0 << Slot1Triggered; + QTest::newRow("T002b:Ctrl++ [Ctrl++]") << TestAccel << NoWidget << QString() << QKeyCombination(Qt::CTRL, Qt::Key_Plus).toCombined() << int('+') << 0 << 0 << 0 << 0 << 0 << 0 << Slot1Triggered; QTest::newRow("T002b:+ [Ctrl++]") << TestAccel << NoWidget << QString() << int(Qt::Key_Plus) << int('+') << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; QTest::newRow("T002b - clear") << ClearAll << NoWidget << QString() << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; // Clear all @@ -537,7 +539,7 @@ void tst_QShortcut::text_data() Qt::Key_F1 on Shift + Qt::Key_F1 */ QTest::newRow("T004 - slot1") << SetupAccel << TriggerSlot1 << QString("Shift+F1") << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; - QTest::newRow("T004:Shift+F1 - [Shift+F1]") << TestAccel << NoWidget << QString() << int(Qt::SHIFT | Qt::Key_F1) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << Slot1Triggered; + QTest::newRow("T004:Shift+F1 - [Shift+F1]") << TestAccel << NoWidget << QString() << QKeyCombination(Qt::SHIFT, Qt::Key_F1).toCombined() << 0 << 0 << 0 << 0 << 0 << 0 << 0 << Slot1Triggered; QTest::newRow("T004:F1 - [Shift+F1]") << TestAccel << NoWidget << QString() << int(Qt::Key_F1) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; QTest::newRow("T004 - clear") << ClearAll << NoWidget << QString() << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; // Clear all @@ -553,7 +555,7 @@ void tst_QShortcut::text_data() QTest::newRow("T007 - slot1") << SetupAccel << TriggerSlot1 << QString("F1") << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; QTest::newRow("T007 - slot2") << SetupAccel << TriggerSlot2 << QString("Shift+F1") << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; QTest::newRow("T007:F1") << TestAccel << NoWidget << QString() << int(Qt::Key_F1) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << Slot1Triggered; - QTest::newRow("T007:Shift + F1") << TestAccel << NoWidget << QString() << int(Qt::SHIFT | Qt::Key_F1) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << Slot2Triggered; + QTest::newRow("T007:Shift + F1") << TestAccel << NoWidget << QString() << QKeyCombination(Qt::SHIFT, Qt::Key_F1).toCombined() << 0 << 0 << 0 << 0 << 0 << 0 << 0 << Slot2Triggered; QTest::newRow("T007 - clear") << ClearAll << NoWidget << QString() << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; // Clear all /* Testing Single Sequences @@ -568,9 +570,9 @@ void tst_QShortcut::text_data() QTest::newRow("T04 - slot2") << SetupAccel << TriggerSlot2 << QString("Alt+M") << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; QTest::newRow("T:Qt::Key_M") << TestAccel << NoWidget << QString() << int(Qt::Key_M) << int('m') << 0 << 0 << 0 << 0 << 0 << 0 << Slot1Triggered; - QTest::newRow("T:Shift + Qt::Key_M") << TestAccel << NoWidget << QString() << int(Qt::SHIFT | Qt::Key_M) << int('M') << 0 << 0 << 0 << 0 << 0 << 0 << Slot2Triggered; - QTest::newRow("T:Ctrl + Qt::Key_M") << TestAccel << NoWidget << QString() << int(Qt::CTRL | Qt::Key_M) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << Slot1Triggered; - QTest::newRow("T:Alt + Qt::Key_M") << TestAccel << NoWidget << QString() << int(Qt::ALT | Qt::Key_M) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << Slot2Triggered; + QTest::newRow("T:Shift + Qt::Key_M") << TestAccel << NoWidget << QString() << QKeyCombination(Qt::SHIFT, Qt::Key_M).toCombined() << int('M') << 0 << 0 << 0 << 0 << 0 << 0 << Slot2Triggered; + QTest::newRow("T:Ctrl + Qt::Key_M") << TestAccel << NoWidget << QString() << QKeyCombination(Qt::CTRL, Qt::Key_M).toCombined() << 0 << 0 << 0 << 0 << 0 << 0 << 0 << Slot1Triggered; + QTest::newRow("T:Alt + Qt::Key_M") << TestAccel << NoWidget << QString() << QKeyCombination(Qt::ALT, Qt::Key_M).toCombined() << 0 << 0 << 0 << 0 << 0 << 0 << 0 << Slot2Triggered; /* Testing Single Sequence Ambiguity Qt::Key_M on shortcut2 @@ -589,7 +591,7 @@ void tst_QShortcut::text_data() QTest::newRow("T07 - slot2") << SetupAccel << TriggerSlot2 << QString::fromLatin1("Shift+\x0C5")<< 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; QTest::newRow("T08 - slot2") << SetupAccel << TriggerSlot1 << QString("K") << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; QTest::newRow("T:Qt::Key_aring") << TestAccel << NoWidget << QString() << int(Qt::Key_Aring) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << Slot1Triggered; - QTest::newRow("T:Qt::Key_Aring") << TestAccel << NoWidget << QString() << int(Qt::SHIFT | Qt::Key_Aring) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << Slot2Triggered; + QTest::newRow("T:Qt::Key_Aring") << TestAccel << NoWidget << QString() << QKeyCombination(Qt::SHIFT, Qt::Key_Aring).toCombined() << 0 << 0 << 0 << 0 << 0 << 0 << 0 << Slot2Triggered; QTest::newRow("T:Qt::Key_aring - Text Form") << TestAccel << NoWidget << QString() << 0 << 0xC5 << 0 << 0 << 0 << 0 << 0 << 0 << Slot1Triggered; QTest::newRow("T:Qt::Key_Aring - Text Form") << TestAccel << NoWidget << QString() << int(Qt::SHIFT) << 0xC5 << 0 << 0 << 0 << 0 << 0 << 0 << Slot2Triggered; QTest::newRow("T:Qt::Key_K") << TestAccel << NoWidget << QString() << int(Qt::Key_K) << int('k') << 0 << 0 << 0 << 0 << 0 << 0 << Slot1Triggered; @@ -605,7 +607,7 @@ void tst_QShortcut::text_data() QTest::newRow("T12 - slot1") << SetupAccel << TriggerSlot1 << QString("Shift+I, M")<< 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; QTest::newRow("T:Qt::Key_M (2)") << TestAccel << NoWidget << QString() << int(Qt::Key_M) << int('m') << 0 << 0 << 0 << 0 << 0 << 0 << Slot1Triggered; QTest::newRow("T:Qt::Key_I, Qt::Key_M") << TestAccel << NoWidget << QString() << int(Qt::Key_I) << int('i') << int(Qt::Key_M) << int('m') << 0 << 0 << 0 << 0 << Slot2Triggered; - QTest::newRow("T:Shift+Qt::Key_I, Qt::Key_M") << TestAccel << NoWidget << QString() << int(Qt::SHIFT | Qt::Key_I) << int('I') << int(Qt::Key_M) << int('m') << 0 << 0 << 0 << 0 << Slot1Triggered; + QTest::newRow("T:Shift+Qt::Key_I, Qt::Key_M") << TestAccel << NoWidget << QString() << QKeyCombination(Qt::SHIFT, Qt::Key_I).toCombined() << int('I') << int(Qt::Key_M) << int('m') << 0 << 0 << 0 << 0 << Slot1Triggered; QTest::newRow("T:end") << TestEnd << NoWidget << QString() << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; } @@ -1062,7 +1064,6 @@ void tst_QShortcut::context() // Focus on 'other1' edit, so Active Window context should trigger other1->activateWindow(); // <--- - QApplication::setActiveWindow(other1); QCOMPARE(QApplication::activeWindow(), other1->window()); QCOMPARE(QApplication::focusWidget(), static_cast<QWidget *>(other1)); @@ -1154,7 +1155,6 @@ void tst_QShortcut::duplicatedShortcutOverride() w.resize(200, 200); w.move(QGuiApplication::primaryScreen()->availableGeometry().center() - QPoint(100, 100)); w.show(); - QApplication::setActiveWindow(&w); QVERIFY(QTest::qWaitForWindowActive(&w)); QTest::keyPress(w.windowHandle(), Qt::Key_A); QCoreApplication::processEvents(); @@ -1350,10 +1350,10 @@ void tst_QShortcut::keys() QCOMPARE(QApplication::focusWidget(), &le); QTest::keyEvent(QTest::Press, QApplication::focusWidget(), Qt::Key_Enter); - QTRY_COMPARE(spy.count(), 1); + QTRY_COMPARE(spy.size(), 1); QTest::keyEvent(QTest::Press, QApplication::focusWidget(), Qt::Key_Return); - QTRY_COMPARE(spy.count(), 2); + QTRY_COMPARE(spy.size(), 2); } QTEST_MAIN(tst_QShortcut) diff --git a/tests/auto/widgets/kernel/qsizepolicy/CMakeLists.txt b/tests/auto/widgets/kernel/qsizepolicy/CMakeLists.txt index 36450d4f80..efdd72db73 100644 --- a/tests/auto/widgets/kernel/qsizepolicy/CMakeLists.txt +++ b/tests/auto/widgets/kernel/qsizepolicy/CMakeLists.txt @@ -1,13 +1,20 @@ -# Generated from qsizepolicy.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qsizepolicy Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qsizepolicy LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qsizepolicy SOURCES tst_qsizepolicy.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::Gui Qt::Widgets Qt::WidgetsPrivate diff --git a/tests/auto/widgets/kernel/qsizepolicy/tst_qsizepolicy.cpp b/tests/auto/widgets/kernel/qsizepolicy/tst_qsizepolicy.cpp index f8c6779a83..19d267f7e5 100644 --- a/tests/auto/widgets/kernel/qsizepolicy/tst_qsizepolicy.cpp +++ b/tests/auto/widgets/kernel/qsizepolicy/tst_qsizepolicy.cpp @@ -1,7 +1,6 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only -#include <QTest> #include <qsizepolicy.h> Q_DECLARE_METATYPE(Qt::Orientations) @@ -9,6 +8,8 @@ Q_DECLARE_METATYPE(QSizePolicy) Q_DECLARE_METATYPE(QSizePolicy::Policy) Q_DECLARE_METATYPE(QSizePolicy::ControlType) +#include <QTest> + class tst_QSizePolicy : public QObject { Q_OBJECT @@ -94,7 +95,7 @@ void tst_QSizePolicy::constExpr() { // QTBUG-69983: For ControlType != QSizePolicy::DefaultType, qCountTrailingZeroBits() // is used, which MSVC 15.8.1 does not consider constexpr due to built-ins -# if defined(QT_HAS_CONSTEXPR_BUILTINS) && (!defined(Q_CC_MSVC) || _MSC_VER < 1915) +# if defined(QT_HAS_CONSTEXPR_BITOPS) constexpr auto sp = QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed, QSizePolicy::CheckBox); # else constexpr auto sp = QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Expanding, QSizePolicy::DefaultType); diff --git a/tests/auto/widgets/kernel/qstackedlayout/CMakeLists.txt b/tests/auto/widgets/kernel/qstackedlayout/CMakeLists.txt index 2c1c3da277..5701f455b5 100644 --- a/tests/auto/widgets/kernel/qstackedlayout/CMakeLists.txt +++ b/tests/auto/widgets/kernel/qstackedlayout/CMakeLists.txt @@ -1,13 +1,21 @@ -# Generated from qstackedlayout.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qstackedlayout Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qstackedlayout LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qstackedlayout SOURCES tst_qstackedlayout.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::Gui Qt::Widgets + Qt::WidgetsPrivate ) diff --git a/tests/auto/widgets/kernel/qstackedlayout/tst_qstackedlayout.cpp b/tests/auto/widgets/kernel/qstackedlayout/tst_qstackedlayout.cpp index 06e97e69b6..c73b9725b2 100644 --- a/tests/auto/widgets/kernel/qstackedlayout/tst_qstackedlayout.cpp +++ b/tests/auto/widgets/kernel/qstackedlayout/tst_qstackedlayout.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> @@ -11,6 +11,8 @@ #include <QPushButton> #include <QSignalSpy> +#include <QtWidgets/private/qapplication_p.h> + class tst_QStackedLayout : public QObject { Q_OBJECT @@ -107,7 +109,7 @@ void tst_QStackedLayout::testCase() // One widget added to layout QWidget *w1 = new QWidget(testWidget); testLayout->addWidget(w1); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); QCOMPARE(spy.at(0).at(0).toInt(), 0); spy.clear(); QCOMPARE(testLayout->currentIndex(), 0); @@ -124,7 +126,7 @@ void tst_QStackedLayout::testCase() // Change the current index testLayout->setCurrentIndex(1); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); QCOMPARE(spy.at(0).at(0).toInt(), 1); spy.clear(); QCOMPARE(testLayout->currentIndex(), 1); @@ -138,7 +140,7 @@ void tst_QStackedLayout::testCase() // Second widget removed from layout; back to nothing testLayout->removeWidget(w2); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); QCOMPARE(spy.at(0).at(0).toInt(), -1); spy.clear(); QCOMPARE(testLayout->currentIndex(), -1); @@ -287,7 +289,6 @@ void tst_QStackedLayout::keepFocusAfterSetCurrent() stackLayout->setCurrentIndex(0); testWidget->show(); - QApplication::setActiveWindow(testWidget); QVERIFY(QTest::qWaitForWindowActive(testWidget)); edit1->setFocus(); diff --git a/tests/auto/widgets/kernel/qtooltip/CMakeLists.txt b/tests/auto/widgets/kernel/qtooltip/CMakeLists.txt index a07cd1aa65..af2de80e24 100644 --- a/tests/auto/widgets/kernel/qtooltip/CMakeLists.txt +++ b/tests/auto/widgets/kernel/qtooltip/CMakeLists.txt @@ -1,13 +1,21 @@ -# Generated from qtooltip.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qtooltip Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qtooltip LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qtooltip SOURCES tst_qtooltip.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::Gui Qt::Widgets + Qt::WidgetsPrivate ) diff --git a/tests/auto/widgets/kernel/qtooltip/tst_qtooltip.cpp b/tests/auto/widgets/kernel/qtooltip/tst_qtooltip.cpp index f1532ae3ac..bc0624c9ab 100644 --- a/tests/auto/widgets/kernel/qtooltip/tst_qtooltip.cpp +++ b/tests/auto/widgets/kernel/qtooltip/tst_qtooltip.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> @@ -11,6 +11,8 @@ #include <qwhatsthis.h> #include <qscreen.h> +#include <QtWidgets/private/qapplication_p.h> + class tst_QToolTip : public QObject { Q_OBJECT @@ -24,6 +26,7 @@ private slots: void setPalette(); void qtbug64550_stylesheet(); void dontCrashOutsideScreenGeometry(); + void marginSetWithStyleSheet(); }; void tst_QToolTip::init() @@ -95,7 +98,6 @@ void tst_QToolTip::keyEvent() widget.setWindowTitle(QLatin1String(QTest::currentTestFunction()) + QLatin1Char(' ') + QLatin1String(QTest::currentDataTag())); widget.show(); - QApplication::setActiveWindow(&widget); QVERIFY(QTest::qWaitForWindowActive(&widget)); widget.showDelayedToolTip(100); @@ -115,7 +117,8 @@ void tst_QToolTip::keyEvent() static QWidget *findWhatsThat() { - foreach (QWidget *widget, QApplication::topLevelWidgets()) { + const auto widgets = QApplication::topLevelWidgets(); + for (QWidget *widget : widgets) { if (widget->inherits("QWhatsThat")) return widget; } @@ -189,7 +192,6 @@ void tst_QToolTip::qtbug64550_stylesheet() Widget widget; widget.setStyleSheet(QStringLiteral("* { font-size: 48pt; }\n")); widget.show(); - QApplication::setActiveWindow(&widget); QVERIFY(QTest::qWaitForWindowActive(&widget)); widget.showDelayedToolTip(100); @@ -211,5 +213,30 @@ void tst_QToolTip::dontCrashOutsideScreenGeometry() { QToolTip::hideText(); } +void tst_QToolTip::marginSetWithStyleSheet() +{ + const char *toolTipText = "Test Tool Tip"; + + qApp->setStyleSheet("QToolTip {font-size: 8px; margin: 5px;}"); + QToolTip::showText(QGuiApplication::primaryScreen()->availableGeometry().topLeft(), toolTipText); + QTRY_VERIFY(QToolTip::isVisible()); + QWidget *toolTip = findToolTip(); + QVERIFY(toolTip); + QTRY_VERIFY(toolTip->isVisible()); + int toolTipHeight = toolTip->size().height(); + qApp->setStyleSheet(QString()); + QToolTip::hideText(); + + qApp->setStyleSheet("QToolTip {font-size: 8px; margin: 10px;}"); + QToolTip::showText(QGuiApplication::primaryScreen()->availableGeometry().topLeft(), toolTipText); + QTRY_VERIFY(QToolTip::isVisible()); + toolTip = findToolTip(); + QVERIFY(toolTip); + QTRY_VERIFY(toolTip->isVisible()); + QCOMPARE_LE(toolTip->size().height(), toolTipHeight + 10); + qApp->setStyleSheet(QString()); + QToolTip::hideText(); +} + QTEST_MAIN(tst_QToolTip) #include "tst_qtooltip.moc" diff --git a/tests/auto/widgets/kernel/qwidget/BLACKLIST b/tests/auto/widgets/kernel/qwidget/BLACKLIST index 10e9259c52..4c17af245b 100644 --- a/tests/auto/widgets/kernel/qwidget/BLACKLIST +++ b/tests/auto/widgets/kernel/qwidget/BLACKLIST @@ -1,42 +1,21 @@ -# OSX QTBUG-25300 QTBUG-45502 -[normalGeometry] -ubuntu-16.04 -[restoreVersion1Geometry] -ubuntu-16.04 -[focusProxyAndInputMethods] -rhel-7.6 -centos -opensuse-leap -ubuntu [raise] opensuse-leap -# QTBUG-68175 -opensuse-42.3 -[renderInvisible] -macos -[optimizedResizeMove] -osx [optimizedResize_topLevel] osx [render_windowOpacity] macos arm [render_systemClip] osx -[maskedUpdate] -opensuse -opensuse-leap -[moveInResizeEvent] -ubuntu-16.04 [multipleToplevelFocusCheck] -rhel-7.6 centos opensuse-leap ubuntu sles-15 - # QTBUG-87668 [showMinimizedKeepsFocus] android +macos-13 ci +macos-14 ci [normalGeometry] android [saveRestoreGeometry] @@ -63,3 +42,8 @@ android android [optimizedResize_topLevel] android +[hoverPosition] +macos-14 x86 +# QTBUG-124291 +[setParentChangesFocus:make dialog parentless, after] +android diff --git a/tests/auto/widgets/kernel/qwidget/CMakeLists.txt b/tests/auto/widgets/kernel/qwidget/CMakeLists.txt index e2370a011a..f18bc98bb3 100644 --- a/tests/auto/widgets/kernel/qwidget/CMakeLists.txt +++ b/tests/auto/widgets/kernel/qwidget/CMakeLists.txt @@ -1,9 +1,16 @@ -# Generated from qwidget.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qwidget Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qwidget LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + # Resources: set(qwidget_resource_files "geometry-fullscreen.dat" @@ -15,7 +22,7 @@ set(qwidget_resource_files qt_internal_add_test(tst_qwidget SOURCES tst_qwidget.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::CorePrivate Qt::Gui Qt::GuiPrivate @@ -26,9 +33,6 @@ qt_internal_add_test(tst_qwidget BUILTIN_TESTDATA ) -#### Keys ignored in scope 1:.:.:qwidget.pro:<TRUE>: -# testcase.timeout = "600" - ## Scopes: ##################################################################### @@ -37,16 +41,8 @@ qt_internal_extend_target(tst_qwidget CONDITION AIX -fpermissive ) -qt_internal_extend_target(tst_qwidget CONDITION APPLE - SOURCES - tst_qwidget_mac_helpers.mm - PUBLIC_LIBRARIES - ${FWAppKit} - ${FWSecurity} -) - qt_internal_extend_target(tst_qwidget CONDITION WIN32 - PUBLIC_LIBRARIES + LIBRARIES gdi32 user32 ) diff --git a/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp b/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp index ce46a45649..666cb70da9 100644 --- a/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp +++ b/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "../../../shared/highdpi.h" @@ -13,6 +13,7 @@ #include <qlineedit.h> #include <qlistview.h> #include <qmessagebox.h> +#include <qmimedata.h> #include <qpainter.h> #include <qpoint.h> #include <qpushbutton.h> @@ -37,6 +38,7 @@ #include <QtGui/qbackingstore.h> #include <QtGui/qguiapplication.h> #include <QtGui/qpa/qplatformwindow.h> +#include <QtGui/qpa/qplatformdrag.h> #include <QtGui/qscreen.h> #include <qmenubar.h> #include <qcompleter.h> @@ -49,10 +51,7 @@ #include <QtGui/qwindow.h> #include <qtimer.h> #include <QtWidgets/QDoubleSpinBox> - -#if defined(Q_OS_MACOS) -#include "tst_qwidget_mac_helpers.h" // Abstract the ObjC stuff out so not everyone must run an ObjC++ compile. -#endif +#include <QtWidgets/QComboBox> #include <QtTest/QTest> #include <QtTest/private/qtesthelpers_p.h> @@ -123,6 +122,34 @@ static QByteArray msgComparisonFailed(T v1, const char *op, T v2) return s.toLocal8Bit(); } +template<class T> class EventSpy : public QObject +{ +public: + EventSpy(T *widget, QEvent::Type event) + : m_widget(widget), eventToSpy(event) + { + if (m_widget) + m_widget->installEventFilter(this); + } + + T *widget() const { return m_widget; } + int count() const { return m_count; } + void clear() { m_count = 0; } + +protected: + bool eventFilter(QObject *object, QEvent *event) override + { + if (event->type() == eventToSpy) + ++m_count; + return QObject::eventFilter(object, event); + } + +private: + T *m_widget; + const QEvent::Type eventToSpy; + int m_count = 0; +}; + Q_LOGGING_CATEGORY(lcTests, "qt.widgets.tests") class tst_QWidget : public QObject @@ -136,7 +163,9 @@ public: public slots: void initTestCase(); void cleanup(); + private slots: + void nativeWindowAttribute(); void addActionOverloads(); void getSetCheck(); void fontPropagation(); @@ -162,11 +191,15 @@ private slots: void mapFromAndTo(); void focusChainOnHide(); void focusChainOnReparent(); + void focusAbstraction(); void defaultTabOrder(); void reverseTabOrder(); void tabOrderWithProxy(); void tabOrderWithProxyDisabled(); + void tabOrderWithProxyOutOfOrder(); void tabOrderWithCompoundWidgets(); + void tabOrderWithCompoundWidgetsInflection_data(); + void tabOrderWithCompoundWidgetsInflection(); void tabOrderWithCompoundWidgetsNoFocusPolicy(); void tabOrderNoChange(); void tabOrderNoChange2(); @@ -174,12 +207,16 @@ private slots: void appFocusWidgetWhenLosingFocusProxy(); void explicitTabOrderWithComplexWidget(); void explicitTabOrderWithSpinBox_QTBUG81097(); + void tabOrderList(); + void tabOrderComboBox_data(); + void tabOrderComboBox(); #if defined(Q_OS_WIN) void activation(); #endif void reparent(); void setScreen(); void windowState(); + void resizePropagation(); void showMaximized(); void showFullScreen(); void showMinimized(); @@ -197,6 +234,8 @@ private slots: void saveRestoreGeometry(); void restoreVersion1Geometry_data(); void restoreVersion1Geometry(); + void restoreGeometryAfterScreenChange_data(); + void restoreGeometryAfterScreenChange(); void widgetAt(); #ifdef Q_OS_MACOS @@ -217,6 +256,7 @@ private slots: void ensureCreated(); void createAndDestroy(); + void eventsAndAttributesOnDestroy(); void winIdChangeEvent(); void persistentWinId(); void showNativeChild(); @@ -279,6 +319,7 @@ private slots: void renderTargetOffset(); void renderInvisible(); void renderWithPainter(); + void renderRTL(); void render_task188133(); void render_task211796(); void render_task217815(); @@ -344,6 +385,7 @@ private slots: void enterLeaveOnWindowShowHide_data(); void enterLeaveOnWindowShowHide(); void taskQTBUG_4055_sendSyntheticEnterLeave(); + void hoverPosition(); void underMouse(); void taskQTBUG_27643_enterEvents(); #endif @@ -367,7 +409,8 @@ private slots: void openModal_taskQTBUG_5804(); void focusProxy(); - void focusProxyAndInputMethods(); + void imEnabledNotImplemented(); + #ifdef QT_BUILD_INTERNAL void scrollWithoutBackingStore(); #endif @@ -425,6 +468,19 @@ private slots: void showFullscreenAndroid(); #endif + void setVisibleDuringDestruction(); + + void explicitShowHide(); + + void dragEnterLeaveSymmetry(); + + void reparentWindowHandles_data(); + void reparentWindowHandles(); + +#ifndef QT_NO_CONTEXTMENU + void contextMenuTrigger(); +#endif + private: const QString m_platform; QSize m_testWidgetSize; @@ -433,6 +489,16 @@ private: const bool m_windowsAnimationsEnabled; QPointingDevice *m_touchScreen; const int m_fuzz; + QPalette simplePalette(); + +private: + enum class ScreenPosition { + OffAbove, + OffLeft, + OffBelow, + OffRight, + Contained + }; }; // Testing get/set functions @@ -591,7 +657,7 @@ void tst_QWidget::getSetCheck() #if defined (Q_OS_WIN) obj1.setWindowFlags(Qt::FramelessWindowHint | Qt::WindowSystemMenuHint); const HWND handle = reinterpret_cast<HWND>(obj1.winId()); // explicitly create window handle - QVERIFY(GetWindowLong(handle, GWL_STYLE) & LONG(WS_POPUP)); + QVERIFY(GetWindowLongPtr(handle, GWL_STYLE) & LONG_PTR(WS_POPUP)); #endif } @@ -628,6 +694,10 @@ tst_QWidget::~tst_QWidget() void tst_QWidget::initTestCase() { +#ifdef Q_OS_ANDROID + if (QNativeInterface::QAndroidApplication::sdkVersion() == 33) + QSKIP("Is flaky on Android 13 / RHEL 8.6 and 8.8 (QTQAINFRA-5606)"); +#endif // Size of reference widget, 200 for < 2000, scale up for larger screens // to avoid Windows warnings about minimum size for decorated windows. int width = 200; @@ -660,6 +730,24 @@ struct ImplicitlyConvertibleTo { void testFunction0() {} void testFunction1(bool) {} +void tst_QWidget::nativeWindowAttribute() +{ + QWidget parent; + QWidget child(&parent); + + QCOMPARE(parent.windowHandle(), nullptr); + QCOMPARE(child.windowHandle(), nullptr); + + // Setting WA_NativeWindow should create window handle + parent.setAttribute(Qt::WA_NativeWindow); + QVERIFY(parent.windowHandle() != nullptr); + // But not its child's window handle + QCOMPARE(child.windowHandle(), nullptr); + // Until the child also gains WA_NativeWindow + child.setAttribute(Qt::WA_NativeWindow); + QVERIFY(child.windowHandle() != nullptr); +} + void tst_QWidget::addActionOverloads() { // almost exhaustive check of addAction() overloads: @@ -1814,6 +1902,7 @@ void tst_QWidget::focusChainOnReparent() } QWidget window2; + child22->setParent(child21); child2->setParent(&window2); QWidget *expectedNewChain[5] = {&window2, child2, child21, child22, &window2}; @@ -1854,8 +1943,6 @@ void tst_QWidget::focusChainOnHide() QWidget::setTabOrder(child, parent.data()); parent->show(); - QApplication::setActiveWindow(parent->window()); - child->activateWindow(); child->setFocus(); QTRY_VERIFY(child->hasFocus()); @@ -1896,8 +1983,11 @@ public: setObjectName(name); lineEdit1 = new QLineEdit; + lineEdit1->setObjectName(name + "/lineEdit1"); lineEdit2 = new QLineEdit; + lineEdit2->setObjectName(name + "/lineEdit2"); lineEdit3 = new QLineEdit; + lineEdit3->setObjectName(name + "/lineEdit3"); lineEdit3->setEnabled(false); QHBoxLayout* hbox = new QHBoxLayout(this); @@ -1912,6 +2002,112 @@ public: QLineEdit *lineEdit3; }; +static QList<QWidget *> getFocusChain(QWidget *start, bool bForward) +{ + QList<QWidget *> ret; + QWidget *cur = start; + // detect infinite loop + int count = 100; + auto loopGuard = qScopeGuard([]{ + QFAIL("Inifinite loop detected in focus chain"); + }); + do { + ret += cur; + cur = bForward ? cur->nextInFocusChain() : cur->previousInFocusChain(); + if (!--count) + return ret; + } while (cur != start); + loopGuard.dismiss(); + return ret; +} + +void tst_QWidget::focusAbstraction() +{ + QLoggingCategory::setFilterRules("qt.widgets.focus=true"); + QWidget *widget1 = new QWidget; + widget1->setObjectName("Widget 1"); + QWidget *widget2 = new QWidget; + widget2->setObjectName("Widget 2"); + QWidget *widget3 = new QWidget; + widget3->setObjectName("Widget 3"); + QWidgetPrivate *priv1 = QWidgetPrivate::get(widget1); + QWidgetPrivate *priv2 = QWidgetPrivate::get(widget2); + QWidgetPrivate *priv3 = QWidgetPrivate::get(widget3); + + // Verify initialization + QVERIFY(!priv1->isInFocusChain()); + QVERIFY(!priv2->isInFocusChain()); + QVERIFY(!priv3->isInFocusChain()); + + // Verify, that parenting builds a focus chain. + QWidget parent; + parent.setObjectName("Parent"); + widget1->setParent(&parent); + widget2->setParent(&parent); + widget3->setParent(&parent); + QVERIFY(priv1->isInFocusChain()); + QVERIFY(priv2->isInFocusChain()); + QVERIFY(priv3->isInFocusChain()); + QWidgetList expected{widget1, widget2, widget3, &parent}; + QCOMPARE(getFocusChain(widget1, true), expected); + + // Verify, that reparented focus children end up behind parent. + widget1->setParent(widget2); + priv2->insertIntoFocusChainAfter(widget3); + priv2->reparentFocusChildren(QWidgetPrivate::FocusDirection::Next); + expected = {widget1, &parent, widget3, widget2}; + QCOMPARE(getFocusChain(widget1, true), expected); + QVERIFY(priv1->isInFocusChain()); + QVERIFY(priv2->isInFocusChain()); + QVERIFY(priv3->isInFocusChain()); + + // Check removal + priv3->removeFromFocusChain(QWidgetPrivate::FocusChainRemovalRule::AssertConsistency); + expected.removeOne(widget3); + QCOMPARE(getFocusChain(widget1, true), expected); + QVERIFY(priv1->isInFocusChain()); + QVERIFY(priv2->isInFocusChain()); + QVERIFY(!priv3->isInFocusChain()); + + // Check insert + priv3->insertIntoFocusChain(QWidgetPrivate::FocusDirection::Previous, widget1); + expected = {widget3, widget1, &parent, widget2}; + QCOMPARE(getFocusChain(widget3, true), expected); + + // Verify, that take doesn't break + const QWidgetList taken = QWidgetPrivate::takeFromFocusChain(widget1, widget2); + QVERIFY(priv1->isFocusChainConsistent()); + expected = {widget1, &parent, widget2}; + QCOMPARE(taken, expected); + QVERIFY(priv1->isInFocusChain()); + QVERIFY(priv2->isInFocusChain()); + QVERIFY(!priv3->isInFocusChain()); + + // Verify insertion of multiple widgets + QWidgetPrivate::insertIntoFocusChain(taken, QWidgetPrivate::FocusDirection::Next, widget3); + expected = {widget3, widget1, &parent, widget2}; + QCOMPARE(getFocusChain(widget3, true), expected); + QVERIFY(priv1->isInFocusChain()); + QVERIFY(priv2->isInFocusChain()); + QVERIFY(priv2->isInFocusChain()); + + // Verify broken chain identification + // d'tor asserts chain consistency => repair before going out of scope + auto guard = qScopeGuard([priv2, widget3]{ priv2->focus_next = widget3; }); + + // Nullptr is not allowed + priv2->focus_next = nullptr; + QVERIFY(!priv1->isFocusChainConsistent()); + + // Chain looping back in the middle + priv2->focus_next = widget1; + QVERIFY(!priv1->isFocusChainConsistent()); + + // "last" element pointing to itself + priv2->focus_next = widget2; + QVERIFY(!priv1->isFocusChainConsistent()); +} + void tst_QWidget::defaultTabOrder() { if (QGuiApplication::platformName().startsWith(QLatin1String("wayland"), Qt::CaseInsensitive)) @@ -1935,7 +2131,6 @@ void tst_QWidget::defaultTabOrder() container.setWindowTitle(QLatin1String(QTest::currentTestFunction())); container.show(); container.activateWindow(); - QApplication::setActiveWindow(&container); QVERIFY(QTest::qWaitForWindowActive(&container)); QTRY_VERIFY(firstEdit->hasFocus()); @@ -1971,23 +2166,29 @@ void tst_QWidget::defaultTabOrder() void tst_QWidget::reverseTabOrder() { - if (QGuiApplication::platformName().startsWith(QLatin1String("wayland"), Qt::CaseInsensitive)) + if (QGuiApplication::platformName().startsWith(QLatin1StringView("wayland"), Qt::CaseInsensitive)) QSKIP("Wayland: This fails. Figure out why."); const int compositeCount = 2; Container container; - container.setWindowTitle(QLatin1String(QTest::currentTestFunction())); + container.setObjectName(QLatin1StringView("Container")); + container.setWindowTitle(QLatin1StringView(QTest::currentTestFunction())); Composite* composite[compositeCount]; QLineEdit *firstEdit = new QLineEdit(); + firstEdit->setObjectName(QLatin1StringView("FirstEdit")); container.box->addWidget(firstEdit); + static constexpr QLatin1StringView comp("Composite-%1"); for (int i = 0; i < compositeCount; i++) { - composite[i] = new Composite(); + const QString name = QString(comp).arg(i); + composite[i] = new Composite(nullptr, name); + composite[i]->setObjectName(name); container.box->addWidget(composite[i]); } QLineEdit *lastEdit = new QLineEdit(); + lastEdit->setObjectName(QLatin1StringView("LastEdit")); container.box->addWidget(lastEdit); // Reverse tab order inside each composite @@ -1996,7 +2197,6 @@ void tst_QWidget::reverseTabOrder() container.show(); container.activateWindow(); - QApplication::setActiveWindow(&container); QVERIFY(QTest::qWaitForWindowActive(&container)); QTRY_VERIFY(firstEdit->hasFocus()); @@ -2031,6 +2231,139 @@ void tst_QWidget::reverseTabOrder() QVERIFY(firstEdit->hasFocus()); } +void tst_QWidget::tabOrderList() +{ + Composite c; + QCOMPARE(getFocusChain(&c, true), + QList<QWidget *>({&c, c.lineEdit1, c.lineEdit2, c.lineEdit3})); + QWidget::setTabOrder({c.lineEdit3, c.lineEdit2, c.lineEdit1}); + // not starting with 3 like one would maybe expect, but still 3, 2, 1 + QCOMPARE(getFocusChain(&c, true), + QList<QWidget *>({&c, c.lineEdit1, c.lineEdit3, c.lineEdit2})); +} + +void tst_QWidget::tabOrderComboBox_data() +{ + QTest::addColumn<const bool>("editableAtBeginning"); + QTest::addColumn<const QList<int>>("firstTabOrder"); + QTest::addColumn<const QList<int>>("secondTabOrder"); + + QTest::addRow("3 not editable") << false << QList<int>{2, 1, 0} << QList<int>{0, 1, 2}; + QTest::addRow("4 editable") << true << QList<int>{2, 1, 0, 3} << QList<int>{3, 0, 2, 1}; +} + +QWidgetList expectedFocusChain(const QList<QComboBox *> &boxes, const QList<int> &sequence) +{ + Q_ASSERT(boxes.count() == sequence.count()); + QWidgetList widgets; + for (int i : sequence) { + Q_ASSERT(i >= 0); + Q_ASSERT(i < boxes.count()); + QComboBox *box = boxes.at(i); + widgets.append(box); + if (box->lineEdit()) + widgets.append(box->lineEdit()); + } + + return widgets; +} + +QWidgetList realFocusChain(const QList<QComboBox *> &boxes, const QList<int> &sequence) +{ + const QWidgetList all = getFocusChain(boxes.at(sequence.at(0)), true); + QWidgetList chain; + // Filter everything with NoFocus + for (auto *widget : all) { + if (widget->focusPolicy() != Qt::NoFocus) + chain << widget; + } + return chain; +} + +void setTabOrder(const QList<QComboBox *> &boxes, const QList<int> &sequence) +{ + Q_ASSERT(boxes.count() == sequence.count()); + QWidget *previous = nullptr; + for (int i : sequence) { + Q_ASSERT(i >= 0); + Q_ASSERT(i < boxes.count()); + QWidget *box = boxes.at(i); + if (!previous) { + previous = box; + } else { + QWidget::setTabOrder(previous, box); + previous = box; + } + } +} + +void tst_QWidget::tabOrderComboBox() +{ + QFETCH(const bool, editableAtBeginning); + QFETCH(const QList<int>, firstTabOrder); + QFETCH(const QList<int>, secondTabOrder); + const int count = firstTabOrder.count(); + Q_ASSERT(count == secondTabOrder.count()); + Q_ASSERT(count > 1); + + QWidget w; + w.setObjectName("MainWidget"); + QVBoxLayout* layout = new QVBoxLayout(); + w.setLayout(layout); + + QList<QComboBox *> boxes; + for (int i = 0; i < count; ++i) { + auto box = new QComboBox; + box->setObjectName("ComboBox " + QString::number(i)); + if (editableAtBeginning) { + box->setEditable(true); + box->lineEdit()->setObjectName("LineEdit " + QString::number(i)); + } + boxes.append(box); + layout->addWidget(box); + } + layout->addStretch(); + +#define COMPARE(seq)\ + setTabOrder(boxes, seq);\ + QCOMPARE(realFocusChain(boxes, seq), expectedFocusChain(boxes, seq)) + + COMPARE(firstTabOrder); + + if (!editableAtBeginning) { + for (auto *box : boxes) + box->setEditable(box); + } + + COMPARE(secondTabOrder); + + // Remove the focus proxy of the first combobox's line edit. + QComboBox *box = boxes.at(0); + QLineEdit *lineEdit = box->lineEdit(); + const QWidget *prev = lineEdit->previousInFocusChain(); + const QWidget *next = lineEdit->nextInFocusChain(); + const QWidget *proxy = lineEdit->focusProxy(); + QCOMPARE(proxy, box); + lineEdit->setFocusProxy(nullptr); + QCOMPARE(lineEdit->focusProxy(), nullptr); + QCOMPARE(lineEdit->previousInFocusChain(), prev); + QCOMPARE(lineEdit->nextInFocusChain(), next); + + // Remove first item and check chain consistency + boxes.removeFirst(); + delete box; + + // Create new list with 0 removed and other indexes updated + QList<int> thirdTabOrder(secondTabOrder); + thirdTabOrder.removeIf([](int i){ return i == 0; }); + for (int &i : thirdTabOrder) + --i; + + COMPARE(thirdTabOrder); + +#undef COMPARE +} + void tst_QWidget::tabOrderWithProxy() { if (QGuiApplication::platformName().startsWith(QLatin1String("wayland"), Qt::CaseInsensitive)) @@ -2058,7 +2391,6 @@ void tst_QWidget::tabOrderWithProxy() container.show(); container.activateWindow(); - QApplication::setActiveWindow(&container); QVERIFY(QTest::qWaitForWindowActive(&container)); QTRY_VERIFY(firstEdit->hasFocus()); @@ -2092,6 +2424,13 @@ void tst_QWidget::tabOrderWithProxy() QVERIFY(firstEdit->hasFocus()); } +static QString focusWidgetName() +{ + return QApplication::focusWidget() != nullptr + ? QApplication::focusWidget()->objectName() + : QStringLiteral("No focus widget"); +} + void tst_QWidget::tabOrderWithProxyDisabled() { Container container; @@ -2119,28 +2458,45 @@ void tst_QWidget::tabOrderWithProxyDisabled() container.show(); container.activateWindow(); - QApplication::setActiveWindow(&container); if (!QTest::qWaitForWindowActive(&container)) QSKIP("Window failed to activate, skipping test"); QVERIFY2(lineEdit1.hasFocus(), - qPrintable(QApplication::focusWidget()->objectName())); + qPrintable(focusWidgetName())); container.tab(); QVERIFY2(!lineEdit2.hasFocus(), - qPrintable(QApplication::focusWidget()->objectName())); + qPrintable(focusWidgetName())); QVERIFY2(lineEdit3.hasFocus(), - qPrintable(QApplication::focusWidget()->objectName())); + qPrintable(focusWidgetName())); container.tab(); QVERIFY2(lineEdit1.hasFocus(), - qPrintable(QApplication::focusWidget()->objectName())); + qPrintable(focusWidgetName())); container.backTab(); QVERIFY2(lineEdit3.hasFocus(), - qPrintable(QApplication::focusWidget()->objectName())); + qPrintable(focusWidgetName())); container.backTab(); QVERIFY2(!lineEdit2.hasFocus(), - qPrintable(QApplication::focusWidget()->objectName())); + qPrintable(focusWidgetName())); QVERIFY2(lineEdit1.hasFocus(), - qPrintable(QApplication::focusWidget()->objectName())); + qPrintable(focusWidgetName())); +} + +//#define DEBUG_FOCUS_CHAIN +static void dumpFocusChain(QWidget *start, bool bForward, const char *desc = nullptr) +{ +#ifdef DEBUG_FOCUS_CHAIN + qDebug() << "Dump focus chain, start:" << start << "isForward:" << bForward << desc; + QWidget *cur = start; + do { + qDebug() << "-" << cur; + auto widgetPrivate = static_cast<QWidgetPrivate *>(qt_widget_private(cur)); + cur = bForward ? widgetPrivate->focus_next : widgetPrivate->focus_prev; + } while (cur != start); +#else + Q_UNUSED(start); + Q_UNUSED(bForward); + Q_UNUSED(desc); +#endif } void tst_QWidget::tabOrderWithCompoundWidgets() @@ -2183,7 +2539,6 @@ void tst_QWidget::tabOrderWithCompoundWidgets() container.show(); container.activateWindow(); - QApplication::setActiveWindow(&container); QVERIFY(QTest::qWaitForWindowActive(&container)); lastEdit->setFocus(); @@ -2234,34 +2589,180 @@ void tst_QWidget::tabOrderWithCompoundWidgets() QVERIFY(lastEdit->hasFocus()); } -static QList<QWidget *> getFocusChain(QWidget *start, bool bForward) +void tst_QWidget::tabOrderWithProxyOutOfOrder() { - QList<QWidget *> ret; - QWidget *cur = start; - do { - ret += cur; - auto widgetPrivate = static_cast<QWidgetPrivate *>(qt_widget_private(cur)); - cur = bForward ? widgetPrivate->focus_next : widgetPrivate->focus_prev; - } while (cur != start); - return ret; + Container container; + container.setWindowTitle(QLatin1String(QTest::currentTestFunction())); + container.setObjectName(QLatin1StringView("Container")); + + // important to create the widgets with parent so that they are + // added to the focus chain already now, and with the buttonBox + // before the outsideButton. + QWidget buttonBox(&container); + buttonBox.setObjectName(QLatin1StringView("buttonBox")); + QPushButton outsideButton(&container); + outsideButton.setObjectName(QLatin1StringView("outsideButton")); + + container.box->addWidget(&outsideButton); + container.box->addWidget(&buttonBox); + QCOMPARE(getFocusChain(&container, true), + QList<QWidget*>({&container, &buttonBox, &outsideButton})); + + // this now adds okButon and cancelButton to the focus chain, + // after the outsideButton - so the outsideButton is in between + // the buttonBox and the children of the buttonBox! + QPushButton okButton(&buttonBox); + okButton.setObjectName("okButton"); + QPushButton cancelButton(&buttonBox); + cancelButton.setObjectName("cancelButton"); + QCOMPARE(getFocusChain(&container, true), + QList<QWidget*>({&container, &buttonBox, &outsideButton, &okButton, &cancelButton})); + + // by setting the okButton as the focusProxy, the outsideButton becomes + // unreachable when navigating the focus chain as the buttonBox is in front + // of, and proxies to the okButton behind the outsideButton. setFocusProxy + // must fix that by moving the buttonBox in front of the first sibling of + // the proxy. + buttonBox.setFocusProxy(&okButton); + QCOMPARE(getFocusChain(&container, true), + QList<QWidget*>({&container, &outsideButton, &buttonBox, &okButton, &cancelButton})); + + container.show(); + container.activateWindow(); + if (!QTest::qWaitForWindowActive(&container)) + QSKIP("Window failed to activate, skipping test"); + + QCOMPARE(QApplication::focusWidget(), &outsideButton); + container.tab(); + QCOMPARE(QApplication::focusWidget(), &okButton); + container.tab(); + QCOMPARE(QApplication::focusWidget(), &cancelButton); + container.tab(); + QCOMPARE(QApplication::focusWidget(), &outsideButton); + + container.backTab(); + QCOMPARE(QApplication::focusWidget(), &cancelButton); + container.backTab(); + QCOMPARE(QApplication::focusWidget(), &okButton); + container.backTab(); + QCOMPARE(QApplication::focusWidget(), &outsideButton); + container.backTab(); + QCOMPARE(QApplication::focusWidget(), &cancelButton); } -//#define DEBUG_FOCUS_CHAIN -static void dumpFocusChain(QWidget *start, bool bForward, const char *desc = nullptr) +static bool isFocusChainConsistent(QWidget *widget) { -#ifdef DEBUG_FOCUS_CHAIN - qDebug() << "Dump focus chain, start:" << start << "isForward:" << bForward << desc; - QWidget *cur = start; - do { - qDebug() << cur; - auto widgetPrivate = static_cast<QWidgetPrivate *>(qt_widget_private(cur)); - cur = bForward ? widgetPrivate->focus_next : widgetPrivate->focus_prev; - } while (cur != start); -#else - Q_UNUSED(start); - Q_UNUSED(bForward); - Q_UNUSED(desc); -#endif + auto forward = getFocusChain(widget, true); + auto backward = getFocusChain(widget, false); + auto logger = qScopeGuard([=]{ + qCritical("Focus chain is not consistent!"); + qWarning() << forward.size() << "forwards: " << forward; + qWarning() << backward.size() << "backwards:" << backward; + }); + // both lists start with the same, the widget + if (forward.takeFirst() != backward.takeFirst()) + return false; + const qsizetype chainLength = forward.size(); + if (backward.size() != chainLength) + return false; + for (qsizetype i = 0; i < chainLength; ++i) { + if (forward.at(i) != backward.at(chainLength - i - 1)) + return false; + } + logger.dismiss(); + return true; +} + +/* + This tests that we end up with consistent and complete chains when we set + the tab order from a widget (the lineEdit) inside a compound (the tabWidget) + to the compound, or visa versa. In that case, QWidget::setTabOrder will walk + the focus chain to the focus child inside the compound to replace the compound + itself when manipulating the tab order. If that last focus child is then + however also the lineEdit, then we must not create an inconsistent or + incomplete loop. + + The tabWidget is seen as a compound because QTabWidget sets the tab bar as + the focus proxy, and it has more widgets inside, like pages, toolbuttons etc. +*/ +void tst_QWidget::tabOrderWithCompoundWidgetsInflection_data() +{ + QTest::addColumn<QByteArrayList>("tabOrder"); + + QTest::addRow("forward") + << QByteArrayList{"dialog", "tabWidget", "lineEdit", "compound", "okButton", "cancelButton"}; + QTest::addRow("backward") + << QByteArrayList{"dialog", "cancelButton", "okButton", "compound", "lineEdit", "tabWidget"}; +} + +void tst_QWidget::tabOrderWithCompoundWidgetsInflection() +{ + QFETCH(const QByteArrayList, tabOrder); + + QDialog dialog; + dialog.setObjectName("dialog"); + QTabWidget *tabWidget = new QTabWidget; + tabWidget->setObjectName("tabWidget"); + tabWidget->setFocusPolicy(Qt::TabFocus); + QWidget *page = new QWidget; + page->setObjectName("page"); + QLineEdit *lineEdit = new QLineEdit; + lineEdit->setObjectName("lineEdit"); + QWidget *compound = new QWidget; + compound->setObjectName("compound"); + compound->setFocusPolicy(Qt::TabFocus); + QPushButton *okButton = new QPushButton("Ok"); + okButton->setObjectName("okButton"); + okButton->setFocusPolicy(Qt::TabFocus); + QPushButton *cancelButton = new QPushButton("Cancel"); + cancelButton->setObjectName("cancelButton"); + cancelButton->setFocusPolicy(Qt::TabFocus); + + QVBoxLayout *pageLayout = new QVBoxLayout; + pageLayout->addWidget(lineEdit); + page->setLayout(pageLayout); + tabWidget->addTab(page, "Tab"); + + QHBoxLayout *compoundLayout = new QHBoxLayout; + compoundLayout->addStretch(); + compoundLayout->addWidget(cancelButton); + compoundLayout->addWidget(okButton); + compound->setFocusProxy(okButton); + compound->setLayout(compoundLayout); + + QVBoxLayout *dialogLayout = new QVBoxLayout; + dialogLayout->addWidget(tabWidget); + dialogLayout->addWidget(compound); + dialog.setLayout(dialogLayout); + + QVERIFY(isFocusChainConsistent(&dialog)); + + QList<QWidget *> expectedFocusChain; + for (qsizetype i = 0; i < tabOrder.size() - 1; ++i) { + QWidget *first = dialog.findChild<QWidget *>(tabOrder.at(i)); + if (!first && tabOrder.at(i) == dialog.objectName()) + first = &dialog; + QVERIFY(first); + if (i == 0) + expectedFocusChain.append(first); + QWidget *second = dialog.findChild<QWidget *>(tabOrder.at(i + 1)); + QVERIFY(second); + expectedFocusChain.append(second); + QWidget::setTabOrder(first, second); + QVERIFY(isFocusChainConsistent(&dialog)); + } + + const auto forwardChain = getFocusChain(&dialog, true); + auto logger = qScopeGuard([=]{ + qCritical("Order of widgets in focus chain not matching:"); + qCritical() << " Actual :" << forwardChain; + qCritical() << " Expected:" << expectedFocusChain; + }); + for (qsizetype i = 0; i < expectedFocusChain.size() - 2; ++i) { + QCOMPARE_LT(forwardChain.indexOf(expectedFocusChain.at(i)), + forwardChain.indexOf(expectedFocusChain.at(i + 1))); + } + logger.dismiss(); } void tst_QWidget::tabOrderWithCompoundWidgetsNoFocusPolicy() @@ -2285,28 +2786,27 @@ void tst_QWidget::tabOrderWithCompoundWidgetsNoFocusPolicy() container.show(); container.activateWindow(); - QApplication::setActiveWindow(&container); if (!QTest::qWaitForWindowActive(&container)) QSKIP("Window failed to activate, skipping test"); QVERIFY2(spinbox1.hasFocus(), - qPrintable(QApplication::focusWidget()->objectName())); + qPrintable(focusWidgetName())); container.tab(); QVERIFY2(!spinbox2.hasFocus(), - qPrintable(QApplication::focusWidget()->objectName())); + qPrintable(focusWidgetName())); QVERIFY2(spinbox3.hasFocus(), - qPrintable(QApplication::focusWidget()->objectName())); + qPrintable(focusWidgetName())); container.tab(); QVERIFY2(spinbox1.hasFocus(), - qPrintable(QApplication::focusWidget()->objectName())); + qPrintable(focusWidgetName())); container.backTab(); QVERIFY2(spinbox3.hasFocus(), - qPrintable(QApplication::focusWidget()->objectName())); + qPrintable(focusWidgetName())); container.backTab(); QVERIFY2(!spinbox2.hasFocus(), - qPrintable(QApplication::focusWidget()->objectName())); + qPrintable(focusWidgetName())); QVERIFY2(spinbox1.hasFocus(), - qPrintable(QApplication::focusWidget()->objectName())); + qPrintable(focusWidgetName())); } void tst_QWidget::tabOrderNoChange() @@ -2391,7 +2891,6 @@ void tst_QWidget::appFocusWidgetWithFocusProxyLater() QLineEdit *lineEdit = new QLineEdit(&window); lineEdit->setFocus(); window.show(); - QApplication::setActiveWindow(&window); QVERIFY(QTest::qWaitForWindowActive(&window)); QCOMPARE(QApplication::focusWidget(), lineEdit); @@ -2419,7 +2918,6 @@ void tst_QWidget::appFocusWidgetWhenLosingFocusProxy() lineEdit->setFocusProxy(lineEditFocusProxy); lineEdit->setFocus(); window.show(); - QApplication::setActiveWindow(&window); QVERIFY(QTest::qWaitForWindowActive(&window)); QCOMPARE(QApplication::focusWidget(), lineEditFocusProxy); QVERIFY(lineEdit->hasFocus()); @@ -2446,7 +2944,6 @@ void tst_QWidget::explicitTabOrderWithComplexWidget() QWidget::setTabOrder(lineEditOne, lineEditTwo); lineEditOne->setFocus(); window.show(); - QApplication::setActiveWindow(&window); QVERIFY(QTest::qWaitForWindowActive(&window)); QTRY_COMPARE(QApplication::focusWidget(), lineEditOne); @@ -2475,7 +2972,6 @@ void tst_QWidget::explicitTabOrderWithSpinBox_QTBUG81097() QWidget::setTabOrder(spinBoxTwo, lineEdit); spinBoxOne->setFocus(); window.show(); - QApplication::setActiveWindow(&window); QVERIFY(QTest::qWaitForWindowActive(&window)); QTRY_COMPARE(QApplication::focusWidget(), spinBoxOne); @@ -2675,6 +3171,119 @@ void tst_QWidget::windowState() QTRY_COMPARE(widget1.size(), size); } +// Test propagation of size and state from platform window to QWidget +// Windows and linux/XCB only +void tst_QWidget::resizePropagation() +{ +#if !defined(Q_OS_LINUX) && !defined(Q_OS_WIN) + QSKIP("resizePropagation test is designed for Linux/XCB and Windows only"); +#endif + const bool xcb = (m_platform == QStringLiteral("xcb")); +#ifdef Q_OS_LINUX + if (!xcb) + QSKIP("resizePropagation test is designed for XCB only"); +#endif + + // Windows: + // When a widget is maximized after it has been resized, the widget retains its original size, + // while the window shows maximum size. + // windowStateChanged signal gets fired on a no-op change from/to WindowNoState + + // Initialize widget and signal spy for window handle + QWidget widget; + widget.showMaximized(); + QVERIFY(QTest::qWaitForWindowExposed(&widget)); + QWindow *window = widget.windowHandle(); + QTRY_VERIFY(window); + QSignalSpy spy(window, &QWindow::windowStateChanged); + int count = 0; + + const QSize screenSize = QGuiApplication::primaryScreen()->size(); + const QSize size1 = QSize(screenSize.width() * 0.5, screenSize.height() * 0.5); + const QSize size2 = QSize(screenSize.width() * 0.625, screenSize.height() * 0.833); + + enum CountIncrementCheck {Equal, Greater}; + enum TargetSizeCheck {Fail, Warn}; + auto verifyResize = [&](const QSize &size, Qt::WindowState windowState, + CountIncrementCheck checkCountIncrement, + TargetSizeCheck checkTargetSize) + { + // Capture count of latest async signals + if (checkCountIncrement == Equal) + count = spy.count(); + + // Resize if required + if (size.isValid()) + widget.resize(size); + + // Wait for the widget anyway + QVERIFY(QTest::qWaitForWindowExposed(&widget)); + + // Check signal count and qDebug output for fail analysis + switch (checkCountIncrement) { + case Greater: { + auto logger = qScopeGuard([&](){ + qDebug() << "spy count:" << spy.count() << "previous count:" << count; + }); + QTRY_VERIFY(spy.count() > count); + logger.dismiss(); + count = spy.count(); + } + break; + case Equal: { + auto logger = qScopeGuard([&](){ + qDebug() << spy << widget.windowState() << window->windowState(); + }); + QCOMPARE(spy.count(), count); + logger.dismiss(); + } + break; + } + + // QTRY necessary because state changes are propagated async + QTRY_COMPARE(widget.windowState(), windowState); + QTRY_COMPARE(window->windowState(), windowState); + + // Check target size with fail or warning + switch (checkTargetSize) { + case Fail: + QCOMPARE(widget.size(), window->size()); + break; + case Warn: + if (widget.size() != window->size()) { + qWarning() << m_platform << "size mismtach tolerated. Widget:" + << widget.size() << "Window:" << window->size(); + } + break; + } + }; + + // test state and size consistency of maximized window + verifyResize(QSize(), Qt::WindowMaximized, Equal, Fail); + if (QTest::currentTestFailed()) + return; + + // test state transition, state and size consistency after resize + verifyResize(size1, Qt::WindowNoState, Greater, xcb ? Warn : Fail ); + if (QTest::currentTestFailed()) + return; + + // test unchanged state, state and size consistency after resize + verifyResize(size2, Qt::WindowNoState, Equal, xcb ? Warn : Fail); + if (QTest::currentTestFailed()) + return; + + // test state transition, state and size consistency after maximize + widget.showMaximized(); + verifyResize(QSize(), Qt::WindowMaximized, Greater, xcb ? Fail : Warn); + if (QTest::currentTestFailed()) + return; + +#ifdef Q_OS_WIN + QCOMPARE(widget.size(), size2); +#endif +} + void tst_QWidget::showMaximized() { QWidget plain; @@ -2966,7 +3575,7 @@ void tst_QWidget::showMinimizedKeepsFocus() child1.setFocusPolicy(Qt::StrongFocus); child2.setFocusPolicy(Qt::StrongFocus); window.show(); - QApplication::setActiveWindow(&window); + QApplicationPrivate::setActiveWindow(&window); QVERIFY(QTest::qWaitForWindowActive(&window)); child2.setFocus(); @@ -2990,7 +3599,6 @@ void tst_QWidget::showMinimizedKeepsFocus() QWidget *child = new QWidget(&window); child->setFocusPolicy(Qt::StrongFocus); window.show(); - QApplication::setActiveWindow(&window); QVERIFY(QTest::qWaitForWindowActive(&window)); child->setFocus(); QTRY_COMPARE(window.focusWidget(), child); @@ -3009,7 +3617,6 @@ void tst_QWidget::showMinimizedKeepsFocus() QWidget *child = new QWidget(&window); child->setFocusPolicy(Qt::StrongFocus); window.show(); - QApplication::setActiveWindow(&window); QVERIFY(QTest::qWaitForWindowActive(&window)); child->setFocus(); QTRY_COMPARE(window.focusWidget(), child); @@ -3029,7 +3636,6 @@ void tst_QWidget::showMinimizedKeepsFocus() QWidget *child = new QWidget(&window); child->setFocusPolicy(Qt::StrongFocus); window.show(); - QApplication::setActiveWindow(&window); QVERIFY(QTest::qWaitForWindowActive(&window)); child->setFocus(); QTRY_COMPARE(window.focusWidget(), child); @@ -3050,7 +3656,6 @@ void tst_QWidget::showMinimizedKeepsFocus() QWidget *child = new QWidget(&window); child->setFocusPolicy(Qt::StrongFocus); window.show(); - QApplication::setActiveWindow(&window); QVERIFY(QTest::qWaitForWindowActive(&window)); child->setFocus(); QTRY_COMPARE(window.focusWidget(), child); @@ -3067,7 +3672,6 @@ void tst_QWidget::showMinimizedKeepsFocus() QTRY_COMPARE(QApplication::focusWidget(), nullptr); window.showNormal(); - QApplication::setActiveWindow(&window); QVERIFY(QTest::qWaitForWindowActive(&window)); #ifdef Q_OS_MACOS if (!macHasAccessToWindowsServer()) @@ -3132,7 +3736,7 @@ void tst_QWidget::reparent() void tst_QWidget::setScreen() { const auto screens = QApplication::screens(); - if (screens.count() < 2) + if (screens.size() < 2) QSKIP("This test tests nothing on a machine with a single screen."); QScreen *screen0 = screens.at(0); @@ -3526,9 +4130,9 @@ void tst_QWidget::raise() QObjectList list1{child1, child2, child3, child4}; QCOMPARE(parentPtr->children(), list1); - QCOMPARE(allChildren.count(), list1.count()); + QCOMPARE(allChildren.size(), list1.size()); - for (UpdateWidget *child : qAsConst(allChildren)) { + for (UpdateWidget *child : std::as_const(allChildren)) { int expectedPaintEvents = child == child4 ? 1 : 0; if (expectedPaintEvents == 0) { QCOMPARE(child->numPaintEvents, 0); @@ -3542,9 +4146,10 @@ void tst_QWidget::raise() for (int i = 0; i < 5; ++i) child2->raise(); - QTest::qWait(50); + QVERIFY(QTest::qWaitForWindowExposed(child2)); + QApplication::processEvents(); // process events that could be triggered by raise(); - for (UpdateWidget *child : qAsConst(allChildren)) { + for (UpdateWidget *child : std::as_const(allChildren)) { int expectedPaintEvents = child == child2 ? 1 : 0; int expectedZOrderChangeEvents = child == child2 ? 1 : 0; QTRY_COMPARE(child->numPaintEvents, expectedPaintEvents); @@ -3571,15 +4176,17 @@ void tst_QWidget::raise() onTop->show(); QVERIFY(QTest::qWaitForWindowExposed(&topLevel)); QTRY_VERIFY(onTop->numPaintEvents > 0); + QApplication::processEvents(); // process remaining paint events if there's more than one onTop->reset(); // Reset all the children. - for (UpdateWidget *child : qAsConst(allChildren)) + for (UpdateWidget *child : std::as_const(allChildren)) child->reset(); for (int i = 0; i < 5; ++i) child3->raise(); - QTest::qWait(50); + QVERIFY(QTest::qWaitForWindowExposed(child3)); + QApplication::processEvents(); // process events that could be triggered by raise(); QCOMPARE(onTop->numPaintEvents, 0); QCOMPARE(onTop->numZOrderChangeEvents, 0); @@ -3587,7 +4194,7 @@ void tst_QWidget::raise() QObjectList list3{child1, child4, child2, child3}; QCOMPARE(parent->children(), list3); - for (UpdateWidget *child : qAsConst(allChildren)) { + for (UpdateWidget *child : std::as_const(allChildren)) { int expectedPaintEvents = 0; int expectedZOrderChangeEvents = child == child3 ? 1 : 0; QTRY_COMPARE(child->numPaintEvents, expectedPaintEvents); @@ -3625,9 +4232,9 @@ void tst_QWidget::lower() QObjectList list1{child1, child2, child3, child4}; QCOMPARE(parent->children(), list1); - QCOMPARE(allChildren.count(), list1.count()); + QCOMPARE(allChildren.size(), list1.size()); - for (UpdateWidget *child : qAsConst(allChildren)) { + for (UpdateWidget *child : std::as_const(allChildren)) { int expectedPaintEvents = child == child4 ? 1 : 0; if (expectedPaintEvents == 0) { QCOMPARE(child->numPaintEvents, 0); @@ -3644,7 +4251,7 @@ void tst_QWidget::lower() QTest::qWait(100); - for (UpdateWidget *child : qAsConst(allChildren)) { + for (UpdateWidget *child : std::as_const(allChildren)) { int expectedPaintEvents = child == child3 ? 1 : 0; int expectedZOrderChangeEvents = child == child4 ? 1 : 0; QTRY_COMPARE(child->numZOrderChangeEvents, expectedZOrderChangeEvents); @@ -3690,7 +4297,7 @@ void tst_QWidget::stackUnder() QObjectList list1{child1, child2, child3, child4}; QCOMPARE(parent->children(), list1); - for (UpdateWidget *child : qAsConst(allChildren)) { + for (UpdateWidget *child : std::as_const(allChildren)) { int expectedPaintEvents = child == child4 ? 1 : 0; #if defined(Q_OS_WIN) || defined(Q_OS_MACOS) if (expectedPaintEvents == 1 && child->numPaintEvents == 2) @@ -3708,7 +4315,7 @@ void tst_QWidget::stackUnder() QObjectList list2{child1, child4, child2, child3}; QCOMPARE(parent->children(), list2); - for (UpdateWidget *child : qAsConst(allChildren)) { + for (UpdateWidget *child : std::as_const(allChildren)) { int expectedPaintEvents = child == child3 ? 1 : 0; int expectedZOrderChangeEvents = child == child4 ? 1 : 0; QTRY_COMPARE(child->numPaintEvents, expectedPaintEvents); @@ -3723,7 +4330,7 @@ void tst_QWidget::stackUnder() QObjectList list3{child4, child2, child1, child3}; QCOMPARE(parent->children(), list3); - for (UpdateWidget *child : qAsConst(allChildren)) { + for (UpdateWidget *child : std::as_const(allChildren)) { int expectedZOrderChangeEvents = child == child1 ? 1 : 0; if (child == child3) { #ifndef Q_OS_MACOS @@ -3845,6 +4452,13 @@ void tst_QWidget::saveRestoreGeometry() QVERIFY(QTest::qWaitForWindowExposed(&widget)); QApplication::processEvents(); + + /* --------------------------------------------------------------------- + * This test function is likely to flake when debugged with Qt Creator. + * (29px offset making the following QTRY_VERIFY2 fail) + * --------------------------------------------------------------------- + */ + QTRY_VERIFY2(HighDpi::fuzzyCompare(widget.pos(), position, m_fuzz), qPrintable(HighDpi::msgPointMismatch(widget.pos(), position))); QCOMPARE(widget.size(), size); @@ -4000,8 +4614,7 @@ void tst_QWidget::restoreVersion1Geometry() const Qt::WindowStates WindowStateMask = Qt::WindowFullScreen | Qt::WindowMaximized | Qt::WindowMinimized; QFile f(fileName); - QVERIFY(f.exists()); - f.open(QIODevice::ReadOnly); + QVERIFY(f.open(QIODevice::ReadOnly)); const QByteArray savedGeometry = f.readAll(); QCOMPARE(savedGeometry.size(), 46); f.close(); @@ -4057,6 +4670,68 @@ void tst_QWidget::restoreVersion1Geometry() #endif } +void tst_QWidget::restoreGeometryAfterScreenChange_data() +{ + QTest::addColumn<ScreenPosition>("screenPosition"); + QTest::addColumn<int>("deltaWidth"); + QTest::addColumn<int>("deltaHeight"); + QTest::addColumn<int>("frameMargin"); + QTest::addColumn<bool>("outside"); + + QTest::newRow("offAboveLarge") << ScreenPosition::OffAbove << 200 << 250 << 20 << true; + QTest::newRow("fitting") << ScreenPosition::Contained << 80 << 80 << 20 << false; + QTest::newRow("offRightWide") << ScreenPosition::OffRight << 150 << 80 << 20 << false; + QTest::newRow("offLeftFitting") << ScreenPosition::OffLeft << 70 << 70 << 20 << true; + QTest::newRow("offBelowHigh") << ScreenPosition::OffBelow << 80 << 200 << 20 << false; +} + +void tst_QWidget::restoreGeometryAfterScreenChange() +{ + const QList<QScreen *> &screens = QApplication::screens(); + QVERIFY2(!screens.isEmpty(), "No screens found."); + const QRect screenGeometry = screens.at(0)->geometry(); + + QFETCH(ScreenPosition, screenPosition); + QFETCH(int, deltaWidth); + QFETCH(int, deltaHeight); + QFETCH(int, frameMargin); + QFETCH(bool, outside); + + QRect restoredGeometry = screenGeometry; + restoredGeometry.setHeight(screenGeometry.height() * deltaHeight / 100); + restoredGeometry.setWidth(screenGeometry.width() * deltaWidth / 100); + const float moveMargin = outside ? 1.2 : 0.75; + + switch (screenPosition) { + case ScreenPosition::OffLeft: + restoredGeometry.setLeft(restoredGeometry.width() * (-moveMargin)); + break; + case ScreenPosition::OffAbove: + restoredGeometry.setTop(restoredGeometry.height() * (-moveMargin)); + break; + case ScreenPosition::OffRight: + restoredGeometry.setRight(restoredGeometry.width() * moveMargin); + break; + case ScreenPosition::OffBelow: + restoredGeometry.setBottom(restoredGeometry.height() * moveMargin); + break; + case ScreenPosition::Contained: + break; + } + + // If restored geometry fits into screen and has not been moved, + // it is changed only by frame margin plus one pixel at each edge + const QRect originalGeometry = restoredGeometry.adjusted(1, frameMargin + 1, 1, frameMargin + 1); + + QWidgetPrivate::checkRestoredGeometry(screenGeometry, &restoredGeometry, frameMargin); + + if (deltaHeight < 100 && deltaWidth < 100 && screenPosition == ScreenPosition::Contained) + QCOMPARE(originalGeometry, restoredGeometry); + + // new geometry has to fit on the screen + QVERIFY(screenGeometry.contains(restoredGeometry)); +} + void tst_QWidget::widgetAt() { #ifdef Q_OS_MACOS @@ -4322,22 +4997,20 @@ class StaticWidget : public QWidget Q_OBJECT public: bool partial = false; - bool gotPaintEvent = false; QRegion paintedRegion; - explicit StaticWidget(QWidget *parent = nullptr) : QWidget(parent) + explicit StaticWidget(const QPalette &palette, QWidget *parent = nullptr) : QWidget(parent) { setAttribute(Qt::WA_StaticContents); setAttribute(Qt::WA_OpaquePaintEvent); - setPalette(Qt::red); // Make sure we have an opaque palette. + setPalette(palette); setAutoFillBackground(true); } void paintEvent(QPaintEvent *e) override { paintedRegion += e->region(); - gotPaintEvent = true; -// qDebug() << "paint" << e->region(); + ++paintEvents; // Look for a full update, set partial to false if found. for (QRect r : e->region()) { partial = (r != rect()); @@ -4345,107 +5018,114 @@ public: break; } } + + // Wait timeout ms until at least one paint event has been consumed + // and the counter is no longer increasing. + // => making sure to consume multiple paint events relating to one operation + // before returning true. + bool waitForPaintEvent(int timeout = 100) + { + QDeadlineTimer deadline(timeout); + int count = -1; + while (!deadline.hasExpired() && count != paintEvents) { + count = paintEvents; + QCoreApplication::processEvents(); + if (count == paintEvents && count > 0) { + paintEvents = 0; + return true; + } + } + paintEvents = 0; + return false; + } +private: + int paintEvents = 0; }; /* Test that widget resizes and moves can be done with minimal repaints when WA_StaticContents - and WA_OpaquePaintEvent is set. Test is mac-only for now. + and WA_OpaquePaintEvent is set. */ void tst_QWidget::optimizedResizeMove() { - if (m_platform == QStringLiteral("wayland")) - QSKIP("Wayland: This fails. Figure out why."); + const bool wayland = QGuiApplication::platformName().startsWith(QLatin1String("wayland"), Qt::CaseInsensitive); + QWidget parent; - parent.setWindowTitle(QLatin1String(QTest::currentTestFunction())); + parent.setPalette(simplePalette()); + parent.setWindowTitle(QTest::currentTestFunction()); parent.resize(400, 400); - StaticWidget staticWidget(&parent); - staticWidget.gotPaintEvent = false; + StaticWidget staticWidget(simplePalette(), &parent); staticWidget.move(150, 150); staticWidget.resize(150, 150); parent.show(); QVERIFY(QTest::qWaitForWindowExposed(&parent)); - QTRY_VERIFY(staticWidget.gotPaintEvent); + QVERIFY(staticWidget.waitForPaintEvent()); - staticWidget.gotPaintEvent = false; staticWidget.move(staticWidget.pos() + QPoint(10, 10)); - QTest::qWait(20); - QCOMPARE(staticWidget.gotPaintEvent, false); + if (!wayland) { + QVERIFY(!staticWidget.waitForPaintEvent()); + } else { + if (staticWidget.waitForPaintEvent()) + QSKIP("Wayland is not optimising paint events. Skipping test."); + } - staticWidget.gotPaintEvent = false; staticWidget.move(staticWidget.pos() + QPoint(-10, -10)); - QTest::qWait(20); - QCOMPARE(staticWidget.gotPaintEvent, false); + QVERIFY(!staticWidget.waitForPaintEvent()); - staticWidget.gotPaintEvent = false; staticWidget.move(staticWidget.pos() + QPoint(-10, 10)); - QTest::qWait(20); - QCOMPARE(staticWidget.gotPaintEvent, false); + QVERIFY(!staticWidget.waitForPaintEvent()); - staticWidget.gotPaintEvent = false; staticWidget.resize(staticWidget.size() + QSize(10, 10)); - QTRY_VERIFY(staticWidget.gotPaintEvent); + QVERIFY(staticWidget.waitForPaintEvent()); QCOMPARE(staticWidget.partial, true); - staticWidget.gotPaintEvent = false; staticWidget.resize(staticWidget.size() + QSize(-10, -10)); - QTest::qWait(20); - QCOMPARE(staticWidget.gotPaintEvent, false); + QVERIFY(!staticWidget.waitForPaintEvent()); - staticWidget.gotPaintEvent = false; staticWidget.resize(staticWidget.size() + QSize(10, -10)); - QTRY_VERIFY(staticWidget.gotPaintEvent); + QVERIFY(staticWidget.waitForPaintEvent()); QCOMPARE(staticWidget.partial, true); - staticWidget.gotPaintEvent = false; staticWidget.move(staticWidget.pos() + QPoint(10, 10)); staticWidget.resize(staticWidget.size() + QSize(-10, -10)); - QTest::qWait(20); - QCOMPARE(staticWidget.gotPaintEvent, false); + QVERIFY(!staticWidget.waitForPaintEvent()); - staticWidget.gotPaintEvent = false; staticWidget.move(staticWidget.pos() + QPoint(10, 10)); staticWidget.resize(staticWidget.size() + QSize(10, 10)); - QTRY_VERIFY(staticWidget.gotPaintEvent); + QVERIFY(staticWidget.waitForPaintEvent()); QCOMPARE(staticWidget.partial, true); - staticWidget.gotPaintEvent = false; staticWidget.move(staticWidget.pos() + QPoint(-10, -10)); staticWidget.resize(staticWidget.size() + QSize(-10, -10)); - QTest::qWait(20); - QCOMPARE(staticWidget.gotPaintEvent, false); + QVERIFY(!staticWidget.waitForPaintEvent()); staticWidget.setAttribute(Qt::WA_StaticContents, false); - staticWidget.gotPaintEvent = false; staticWidget.move(staticWidget.pos() + QPoint(-10, -10)); staticWidget.resize(staticWidget.size() + QSize(-10, -10)); - QTRY_VERIFY(staticWidget.gotPaintEvent); + QVERIFY(staticWidget.waitForPaintEvent()); QCOMPARE(staticWidget.partial, false); staticWidget.setAttribute(Qt::WA_StaticContents, true); staticWidget.setAttribute(Qt::WA_StaticContents, false); - staticWidget.gotPaintEvent = false; staticWidget.move(staticWidget.pos() + QPoint(10, 10)); - QTest::qWait(20); - QCOMPARE(staticWidget.gotPaintEvent, false); + QVERIFY(!staticWidget.waitForPaintEvent()); staticWidget.setAttribute(Qt::WA_StaticContents, true); } void tst_QWidget::optimizedResize_topLevel() { - if (QGuiApplication::platformName().startsWith(QLatin1String("wayland"), Qt::CaseInsensitive)) - QSKIP("Wayland: This fails. Figure out why."); + const bool wayland = QGuiApplication::platformName().startsWith(QLatin1String("wayland"), Qt::CaseInsensitive); if (QHighDpiScaling::isActive()) QSKIP("Skip due to rounding errors in the regions."); - StaticWidget topLevel; + StaticWidget topLevel(simplePalette()); + topLevel.setPalette(simplePalette()); topLevel.setWindowTitle(QLatin1String(QTest::currentTestFunction())); - topLevel.gotPaintEvent = false; topLevel.show(); QVERIFY(QTest::qWaitForWindowExposed(&topLevel)); - QTRY_VERIFY(topLevel.gotPaintEvent); + QVERIFY(topLevel.waitForPaintEvent()); - topLevel.gotPaintEvent = false; topLevel.partial = false; topLevel.paintedRegion = QRegion(); @@ -4470,10 +5150,15 @@ void tst_QWidget::optimizedResize_topLevel() QRegion expectedUpdateRegion(topLevel.rect()); expectedUpdateRegion -= QRect(QPoint(), topLevel.size() - QSize(10, 10)); - QTRY_VERIFY(topLevel.gotPaintEvent); + QVERIFY(topLevel.waitForPaintEvent()); if (m_platform == QStringLiteral("xcb") || m_platform == QStringLiteral("offscreen")) QSKIP("QTBUG-26424"); - QCOMPARE(topLevel.partial, true); + if (!wayland) { + QCOMPARE(topLevel.partial, true); + } else { + if (!topLevel.partial) + QSKIP("Wayland does repaint partially. Skipping test."); + } QCOMPARE(topLevel.paintedRegion, expectedUpdateRegion); } @@ -4643,7 +5328,7 @@ protected: } public: QList<WId> m_winIdList; - int winIdChangeEventCount() const { return m_winIdList.count(); } + int winIdChangeEventCount() const { return m_winIdList.size(); } }; class CreateDestroyWidget : public WinIdChangeWidget @@ -4698,6 +5383,84 @@ void tst_QWidget::createAndDestroy() QVERIFY(widget.internalWinId()); } +void tst_QWidget::eventsAndAttributesOnDestroy() +{ + // The events and attributes when destroying a widget should + // include those of hiding the widget. + + CreateDestroyWidget widget; + EventSpy<QWidget> showEventSpy(&widget, QEvent::Show); + EventSpy<QWidget> hideEventSpy(&widget, QEvent::Hide); + + QCOMPARE(widget.testAttribute(Qt::WA_WState_Created), false); + QCOMPARE(widget.testAttribute(Qt::WA_WState_Visible), false); + QCOMPARE(widget.testAttribute(Qt::WA_Mapped), false); + + widget.show(); + QCOMPARE(widget.testAttribute(Qt::WA_WState_Created), true); + QCOMPARE(widget.testAttribute(Qt::WA_WState_Visible), true); + QTRY_COMPARE(widget.testAttribute(Qt::WA_Mapped), true); + QCOMPARE(showEventSpy.count(), 1); + QCOMPARE(hideEventSpy.count(), 0); + + widget.hide(); + QCOMPARE(widget.testAttribute(Qt::WA_WState_Created), true); + QCOMPARE(widget.testAttribute(Qt::WA_WState_Visible), false); + QCOMPARE(widget.testAttribute(Qt::WA_Mapped), false); + QCOMPARE(showEventSpy.count(), 1); + QCOMPARE(hideEventSpy.count(), 1); + + widget.show(); + QCOMPARE(widget.testAttribute(Qt::WA_WState_Created), true); + QCOMPARE(widget.testAttribute(Qt::WA_WState_Visible), true); + QTRY_COMPARE(widget.testAttribute(Qt::WA_Mapped), true); + QCOMPARE(showEventSpy.count(), 2); + QCOMPARE(hideEventSpy.count(), 1); + + widget.destroy(); + QCOMPARE(widget.testAttribute(Qt::WA_WState_Created), false); + QCOMPARE(widget.testAttribute(Qt::WA_WState_Visible), false); + QCOMPARE(widget.testAttribute(Qt::WA_Mapped), false); + QCOMPARE(showEventSpy.count(), 2); + QCOMPARE(hideEventSpy.count(), 2); + + const int hideEventsAfterDestroy = hideEventSpy.count(); + + widget.create(); + QCOMPARE(widget.testAttribute(Qt::WA_WState_Created), true); + QCOMPARE(widget.testAttribute(Qt::WA_WState_Visible), false); + QCOMPARE(widget.testAttribute(Qt::WA_Mapped), false); + QCOMPARE(showEventSpy.count(), 2); + QCOMPARE(hideEventSpy.count(), hideEventsAfterDestroy); + + QWidgetPrivate::get(&widget)->setVisible(true); + QCOMPARE(widget.testAttribute(Qt::WA_WState_Created), true); + QCOMPARE(widget.testAttribute(Qt::WA_WState_Visible), true); + QTRY_COMPARE(widget.testAttribute(Qt::WA_Mapped), true); + QCOMPARE(showEventSpy.count(), 3); + QCOMPARE(hideEventSpy.count(), hideEventsAfterDestroy); + + // Make sure the destroy that happens when a top level + // is moved to being a child does not prevent the child + // being shown again. + + QWidget parent; + QWidget child; + parent.show(); + QVERIFY(QTest::qWaitForWindowExposed(&parent)); + child.show(); + QVERIFY(QTest::qWaitForWindowExposed(&child)); + + child.setParent(&parent); + QCOMPARE(child.testAttribute(Qt::WA_WState_Created), false); + QCOMPARE(child.testAttribute(Qt::WA_WState_Visible), false); + + child.show(); + QCOMPARE(child.testAttribute(Qt::WA_WState_Created), true); + QCOMPARE(child.testAttribute(Qt::WA_WState_Visible), true); + QVERIFY(QTest::qWaitForWindowExposed(&child)); +} + void tst_QWidget::winIdChangeEvent() { { @@ -4865,24 +5628,29 @@ void tst_QWidget::closeAndShowWithNativeChild() QApplication::setAttribute(Qt::AA_DontCreateNativeWidgetSiblings); QWidget topLevel; + topLevel.setObjectName("TopLevel"); QWidget *nativeChild = new QWidget; + nativeChild->setObjectName("NativeChild"); nativeChild->setFixedSize(200, 200); - QWidget *nativeHiddenChild = new QWidget; - nativeHiddenChild->setFixedSize(200, 200); QWidget *normalChild = new QWidget; + normalChild->setObjectName("NormalChild"); normalChild->setFixedSize(200, 200); QHBoxLayout *layout = new QHBoxLayout; layout->addWidget(nativeChild); - layout->addWidget(nativeHiddenChild); layout->addWidget(normalChild); topLevel.setLayout(layout); - nativeHiddenChild->hide(); + nativeChild->setAttribute(Qt::WA_NativeWindow); + + QCOMPARE(normalChild->testAttribute(Qt::WA_WState_Hidden), false); + QCOMPARE(normalChild->testAttribute(Qt::WA_WState_ExplicitShowHide), false); + + QCOMPARE(nativeChild->testAttribute(Qt::WA_WState_Hidden), false); + QCOMPARE(nativeChild->testAttribute(Qt::WA_WState_ExplicitShowHide), false); topLevel.show(); QVERIFY(QTest::qWaitForWindowExposed(&topLevel)); - nativeChild->winId(); const QSize originalSize = topLevel.size(); topLevel.close(); @@ -5050,6 +5818,7 @@ void tst_QWidget::update() Q_CHECK_PAINTEVENTS UpdateWidget w; + w.setPalette(simplePalette()); w.setWindowTitle(QLatin1String(QTest::currentTestFunction())); w.resize(100, 100); centerOnScreen(&w); @@ -5063,6 +5832,7 @@ void tst_QWidget::update() w.reset(); UpdateWidget child(&w); + child.setPalette(simplePalette()); child.setGeometry(10, 10, 80, 80); child.show(); @@ -5134,6 +5904,7 @@ void tst_QWidget::update() // overlapping sibling UpdateWidget sibling(&w); + sibling.setPalette(simplePalette()); child.setGeometry(10, 10, 20, 20); sibling.setGeometry(15, 15, 20, 20); sibling.show(); @@ -5213,9 +5984,11 @@ void tst_QWidget::isOpaque() { #ifndef Q_OS_MACOS QWidget w; + w.setPalette(simplePalette()); QVERIFY(::isOpaque(&w)); QWidget child(&w); + child.setPalette(simplePalette()); QVERIFY(!::isOpaque(&child)); child.setAutoFillBackground(true); @@ -5297,11 +6070,11 @@ void tst_QWidget::scroll() const int h = qMin(500, screen->availableGeometry().height() / 2); UpdateWidget updateWidget; + updateWidget.setPalette(simplePalette()); updateWidget.resize(w, h); updateWidget.reset(); updateWidget.move(m_availableTopLeft); updateWidget.showNormal(); - QApplication::setActiveWindow(&updateWidget); QVERIFY(QTest::qWaitForWindowActive(&updateWidget)); QVERIFY(updateWidget.numPaintEvents > 0); @@ -5415,7 +6188,7 @@ public: } QObjectCastChecker(QObjectCastChecker &&other) noexcept - : m_target(qExchange(other.m_target, nullptr)) + : m_target(std::exchange(other.m_target, nullptr)) {} QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_MOVE_AND_SWAP(QObjectCastChecker) @@ -5489,7 +6262,7 @@ void tst_QWidget::setWindowGeometry_data() const Qt::WindowFlags windowFlags[] = {Qt::WindowFlags(), Qt::FramelessWindowHint}; const bool skipEmptyRects = (m_platform == QStringLiteral("windows")); - for (Rects l : qAsConst(rects)) { + for (Rects l : std::as_const(rects)) { if (skipEmptyRects) l.removeIf([] (const QRect &r) { return r.isEmpty(); }); const QRect &rect = l.constFirst(); @@ -5508,8 +6281,8 @@ void tst_QWidget::setWindowGeometry_data() void tst_QWidget::setWindowGeometry() { - if (m_platform == QStringLiteral("xcb")) - QSKIP("X11: Skip this test due to Window manager positioning issues."); + if (m_platform == QStringLiteral("xcb") || m_platform.startsWith(QLatin1String("wayland"), Qt::CaseInsensitive)) + QSKIP("X11/Wayland: Skip this test due to Window manager positioning issues."); QFETCH(Rects, rects); QFETCH(int, windowFlags); @@ -5526,7 +6299,7 @@ void tst_QWidget::setWindowGeometry() QCOMPARE(widget.geometry(), rect); // setGeometry() without showing - for (const QRect &r : qAsConst(rects)) { + for (const QRect &r : std::as_const(rects)) { widget.setGeometry(r); QTest::qWait(100); QCOMPARE(widget.geometry(), r); @@ -5552,7 +6325,7 @@ void tst_QWidget::setWindowGeometry() QTRY_COMPARE(widget.geometry(), rect); // setGeometry() while shown - for (const QRect &r : qAsConst(rects)) { + for (const QRect &r : std::as_const(rects)) { widget.setGeometry(r); QTest::qWait(10); QTRY_COMPARE(widget.geometry(), r); @@ -5567,7 +6340,7 @@ void tst_QWidget::setWindowGeometry() QTRY_COMPARE(widget.geometry(), rect); // setGeometry() after hide() - for (const QRect &r : qAsConst(rects)) { + for (const QRect &r : std::as_const(rects)) { widget.setGeometry(r); QTest::qWait(10); QTRY_COMPARE(widget.geometry(), r); @@ -5603,7 +6376,7 @@ void tst_QWidget::setWindowGeometry() QTRY_COMPARE(widget.geometry(), rect); // setGeometry() while shown - for (const QRect &r : qAsConst(rects)) { + for (const QRect &r : std::as_const(rects)) { widget.setGeometry(r); QTest::qWait(10); QTRY_COMPARE(widget.geometry(), r); @@ -5618,7 +6391,7 @@ void tst_QWidget::setWindowGeometry() QTRY_COMPARE(widget.geometry(), rect); // setGeometry() after hide() - for (const QRect &r : qAsConst(rects)) { + for (const QRect &r : std::as_const(rects)) { widget.setGeometry(r); QTest::qWait(10); QTRY_COMPARE(widget.geometry(), r); @@ -5697,7 +6470,7 @@ void tst_QWidget::windowMoveResize() QTRY_COMPARE(widget.size(), rect.size()); // move() without showing - for (const QRect &r : qAsConst(rects)) { + for (const QRect &r : std::as_const(rects)) { widget.move(r.topLeft()); widget.resize(r.size()); QApplication::processEvents(); @@ -5727,7 +6500,7 @@ void tst_QWidget::windowMoveResize() QTRY_COMPARE(widget.size(), rect.size()); // move() while shown - for (const QRect &r : qAsConst(rects)) { + for (const QRect &r : std::as_const(rects)) { // XCB: First resize after show of zero-sized gets wrong win_gravity. const bool expectMoveFail = !windowFlags && ((widget.width() == 0 || widget.height() == 0) && r.width() != 0 && r.height() != 0) @@ -5756,7 +6529,7 @@ void tst_QWidget::windowMoveResize() QTRY_COMPARE(widget.size(), rect.size()); // move() after hide() - for (const QRect &r : qAsConst(rects)) { + for (const QRect &r : std::as_const(rects)) { widget.move(r.topLeft()); widget.resize(r.size()); QApplication::processEvents(); @@ -5807,7 +6580,7 @@ void tst_QWidget::windowMoveResize() QTRY_COMPARE(widget.size(), rect.size()); // move() while shown - for (const QRect &r : qAsConst(rects)) { + for (const QRect &r : std::as_const(rects)) { widget.move(r.topLeft()); widget.resize(r.size()); QApplication::processEvents(); @@ -5827,7 +6600,7 @@ void tst_QWidget::windowMoveResize() QTRY_COMPARE(widget.size(), rect.size()); // move() after hide() - for (const QRect &r : qAsConst(rects)) { + for (const QRect &r : std::as_const(rects)) { widget.move(r.topLeft()); widget.resize(r.size()); QApplication::processEvents(); @@ -6034,6 +6807,9 @@ void tst_QWidget::moveChild() void tst_QWidget::showAndMoveChild() { +#ifdef ANDROID + QSKIP("Fails on Android due to removed grabWindow(): QTBUG-118849"); +#endif if (m_platform == QStringLiteral("wayland")) QSKIP("Wayland: This fails. Figure out why."); QWidget parent(nullptr, Qt::Window | Qt::WindowStaysOnTopHint); @@ -6051,7 +6827,6 @@ void tst_QWidget::showAndMoveChild() parent.setGeometry(desktopDimensions); parent.setPalette(Qt::red); parent.show(); - QApplication::setActiveWindow(&parent); QVERIFY(QTest::qWaitForWindowActive(&parent)); QWidget child(&parent); @@ -6170,17 +6945,17 @@ void tst_QWidget::multipleToplevelFocusCheck() w2.show(); QVERIFY(QTest::qWaitForWindowExposed(&w2)); - QApplication::setActiveWindow(&w1); w1.activateWindow(); + QApplicationPrivate::setActiveWindow(&w1); QVERIFY(QTest::qWaitForWindowActive(&w1)); - QCOMPARE(QApplication::activeWindow(), static_cast<QWidget *>(&w1)); + QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget *>(&w1)); QTest::mouseDClick(&w1, Qt::LeftButton); QTRY_COMPARE(QApplication::focusWidget(), static_cast<QWidget *>(w1.edit)); w2.activateWindow(); - QApplication::setActiveWindow(&w2); + QApplicationPrivate::setActiveWindow(&w2); QVERIFY(QTest::qWaitForWindowActive(&w2)); - QCOMPARE(QApplication::activeWindow(), static_cast<QWidget *>(&w2)); + QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget *>(&w2)); QTest::mouseClick(&w2, Qt::LeftButton); QTRY_COMPARE(QApplication::focusWidget(), nullptr); @@ -6188,16 +6963,16 @@ void tst_QWidget::multipleToplevelFocusCheck() QTRY_COMPARE(QApplication::focusWidget(), static_cast<QWidget *>(w2.edit)); w1.activateWindow(); - QApplication::setActiveWindow(&w1); + QApplicationPrivate::setActiveWindow(&w1); QVERIFY(QTest::qWaitForWindowActive(&w1)); - QCOMPARE(QApplication::activeWindow(), static_cast<QWidget *>(&w1)); + QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget *>(&w1)); QTest::mouseDClick(&w1, Qt::LeftButton); QTRY_COMPARE(QApplication::focusWidget(), static_cast<QWidget *>(w1.edit)); w2.activateWindow(); - QApplication::setActiveWindow(&w2); + QApplicationPrivate::setActiveWindow(&w2); QVERIFY(QTest::qWaitForWindowActive(&w2)); - QCOMPARE(QApplication::activeWindow(), static_cast<QWidget *>(&w2)); + QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget *>(&w2)); QTest::mouseClick(&w2, Qt::LeftButton); QTRY_COMPARE(QApplication::focusWidget(), nullptr); } @@ -6260,7 +7035,7 @@ void tst_QWidget::setFocus() { // move focus to another window testWidget->activateWindow(); - QApplication::setActiveWindow(testWidget.data()); + QApplicationPrivate::setActiveWindow(testWidget.data()); if (testWidget->focusWidget()) testWidget->focusWidget()->clearFocus(); else @@ -6306,7 +7081,7 @@ void tst_QWidget::setFocus() // note: window may be active, but we don't want it to be testWidget->activateWindow(); - QApplication::setActiveWindow(testWidget.data()); + QApplicationPrivate::setActiveWindow(testWidget.data()); if (testWidget->focusWidget()) testWidget->focusWidget()->clearFocus(); else @@ -6482,34 +7257,6 @@ void tst_QWidget::setFocus() } } -template<class T> class EventSpy : public QObject -{ -public: - EventSpy(T *widget, QEvent::Type event) - : m_widget(widget), eventToSpy(event) - { - if (m_widget) - m_widget->installEventFilter(this); - } - - T *widget() const { return m_widget; } - int count() const { return m_count; } - void clear() { m_count = 0; } - -protected: - bool eventFilter(QObject *object, QEvent *event) override - { - if (event->type() == eventToSpy) - ++m_count; - return QObject::eventFilter(object, event); - } - -private: - T *m_widget; - const QEvent::Type eventToSpy; - int m_count = 0; -}; - #ifndef QT_NO_CURSOR void tst_QWidget::setCursor() { @@ -6713,7 +7460,7 @@ void tst_QWidget::testWindowIconChangeEventPropagation() QWidgetList widgets; widgets << &topLevelWidget << &topLevelChild << &dialog << &dialogChild; - QCOMPARE(widgets.count(), 4); + QCOMPARE(widgets.size(), 4); topLevelWidget.show(); dialog.show(); @@ -6727,13 +7474,13 @@ void tst_QWidget::testWindowIconChangeEventPropagation() // Create spy lists. QList <EventSpyPtr> applicationEventSpies; QList <EventSpyPtr> widgetEventSpies; - for (QWidget *widget : qAsConst(widgets)) { + for (QWidget *widget : std::as_const(widgets)) { applicationEventSpies.append(EventSpyPtr::create(widget, QEvent::ApplicationWindowIconChange)); widgetEventSpies.append(EventSpyPtr::create(widget, QEvent::WindowIconChange)); } QList <WindowEventSpyPtr> appWindowEventSpies; QList <WindowEventSpyPtr> windowEventSpies; - for (QWindow *window : qAsConst(windows)) { + for (QWindow *window : std::as_const(windows)) { appWindowEventSpies.append(WindowEventSpyPtr::create(window, QEvent::ApplicationWindowIconChange)); windowEventSpies.append(WindowEventSpyPtr::create(window, QEvent::WindowIconChange)); } @@ -6742,7 +7489,7 @@ void tst_QWidget::testWindowIconChangeEventPropagation() const QIcon windowIcon = qApp->style()->standardIcon(QStyle::SP_TitleBarMenuButton); qApp->setWindowIcon(windowIcon); - for (int i = 0; i < widgets.count(); ++i) { + for (int i = 0; i < widgets.size(); ++i) { // Check QEvent::ApplicationWindowIconChange EventSpyPtr spy = applicationEventSpies.at(i); QWidget *widget = spy->widget(); @@ -6759,7 +7506,7 @@ void tst_QWidget::testWindowIconChangeEventPropagation() QCOMPARE(spy->count(), 1); spy->clear(); } - for (int i = 0; i < windows.count(); ++i) { + for (int i = 0; i < windows.size(); ++i) { // Check QEvent::ApplicationWindowIconChange (sent to QWindow) // QWidgetWindows don't get this event, since the widget takes care of changing the icon WindowEventSpyPtr spy = appWindowEventSpies.at(i); @@ -6777,7 +7524,7 @@ void tst_QWidget::testWindowIconChangeEventPropagation() // Set icon on a top-level widget. topLevelWidget.setWindowIcon(QIcon()); - for (int i = 0; i < widgets.count(); ++i) { + for (int i = 0; i < widgets.size(); ++i) { // Check QEvent::ApplicationWindowIconChange EventSpyPtr spy = applicationEventSpies.at(i); QCOMPARE(spy->count(), 0); @@ -6934,7 +7681,6 @@ void tst_QWidget::clean_qt_x11_enforce_cursor() child->setAttribute(Qt::WA_SetCursor, true); window.show(); - QApplication::setActiveWindow(&window); QVERIFY(QTest::qWaitForWindowActive(&window)); QTest::qWait(100); QCursor::setPos(window.geometry().center()); @@ -7311,6 +8057,9 @@ private: void tst_QWidget::render() { +#ifdef Q_OS_ANDROID + QSKIP("QTBUG-118984: crashes on Android."); +#endif QCalendarWidget source; source.setWindowTitle(QLatin1String(QTest::currentTestFunction())); // disable anti-aliasing to eliminate potential differences when subpixel antialiasing @@ -7367,7 +8116,8 @@ void tst_QWidget::renderChildFillsBackground() #ifndef Q_OS_ANDROID // On Android all widgets are shown maximized, so the pixmaps // will be similar - QEXPECT_FAIL("", "This test fails on all platforms", Continue); + if (!m_platform.startsWith(QLatin1String("wayland"), Qt::CaseInsensitive)) + QEXPECT_FAIL("", "This test fails on all platforms", Continue); #endif QCOMPARE(childPixmap, windowPixmap); } @@ -7402,15 +8152,12 @@ void tst_QWidget::renderTargetOffset() QCOMPARE(image.pixel(120, 120), QColor(Qt::blue).rgb()); } -// On Windows the active palette is used instead of the inactive palette even -// though the widget is invisible. This is probably related to task 178507/168682, -// but for the renderInvisible test it doesn't matter, we're mostly interested -// in testing the geometry so just workaround the palette issue for now. +// On some platforms the active palette is used instead of the inactive palette even +// though the widget is invisible, but for the renderInvisible test it doesn't matter, +// as we're mostly interested in testing the geometry, so just workaround the palette +// issue for now. static void workaroundPaletteIssue(QWidget *widget) { -#ifndef Q_OS_WIN - return; -#endif if (!widget) return; @@ -7431,6 +8178,9 @@ void tst_QWidget::renderInvisible() if (m_platform == QStringLiteral("xcb")) QSKIP("QTBUG-26424"); + if (m_platform.startsWith(QLatin1String("wayland"), Qt::CaseInsensitive)) + QSKIP("Wayland: Skip this test, see also QTBUG-107157"); + QScopedPointer<QCalendarWidget> calendar(new QCalendarWidget); calendar->move(m_availableTopLeft + QPoint(100, 100)); calendar->setWindowTitle(QLatin1String(QTest::currentTestFunction())); @@ -7451,7 +8201,7 @@ void tst_QWidget::renderInvisible() // Create normal reference image. const QSize calendarSize = calendar->size(); - QImage referenceImage(calendarSize, QImage::Format_ARGB32); + QImage referenceImage(calendarSize, QImage::Format_ARGB32_Premultiplied); calendar->render(&referenceImage); #ifdef RENDER_DEBUG referenceImage.save("referenceImage.png"); @@ -7462,7 +8212,7 @@ void tst_QWidget::renderInvisible() const QSize calendarSizeResized = calendar->size() + QSize(50, 50); calendar->resize(calendarSizeResized); QTest::qWait(30); - QImage referenceImageResized(calendarSizeResized, QImage::Format_ARGB32); + QImage referenceImageResized(calendarSizeResized, QImage::Format_ARGB32_Premultiplied); calendar->render(&referenceImageResized); #ifdef RENDER_DEBUG referenceImageResized.save("referenceImageResized.png"); @@ -7475,7 +8225,7 @@ void tst_QWidget::renderInvisible() workaroundPaletteIssue(calendar.data()); { // Make sure we get the same image when the calendar is explicitly hidden. - QImage testImage(calendarSizeResized, QImage::Format_ARGB32); + QImage testImage(calendarSizeResized, QImage::Format_ARGB32_Premultiplied); calendar->render(&testImage); #ifdef RENDER_DEBUG testImage.save("explicitlyHiddenCalendarResized.png"); @@ -7491,7 +8241,7 @@ void tst_QWidget::renderInvisible() workaroundPaletteIssue(calendar.data()); { // Never been visible, created or laid out. - QImage testImage(calendarSize, QImage::Format_ARGB32); + QImage testImage(calendarSize, QImage::Format_ARGB32_Premultiplied); calendar->render(&testImage); #ifdef RENDER_DEBUG testImage.save("neverBeenVisibleCreatedOrLaidOut.png"); @@ -7503,7 +8253,7 @@ void tst_QWidget::renderInvisible() QTest::qWait(30); { // Calendar explicitly hidden. - QImage testImage(calendarSize, QImage::Format_ARGB32); + QImage testImage(calendarSize, QImage::Format_ARGB32_Premultiplied); calendar->render(&testImage); #ifdef RENDER_DEBUG testImage.save("explicitlyHiddenCalendar.png"); @@ -7517,7 +8267,7 @@ void tst_QWidget::renderInvisible() navigationBar->hide(); { // Check that the navigation bar isn't drawn when rendering the entire calendar. - QImage testImage(calendarSize, QImage::Format_ARGB32); + QImage testImage(calendarSize, QImage::Format_ARGB32_Premultiplied); calendar->render(&testImage); #ifdef RENDER_DEBUG testImage.save("calendarWithoutNavigationBar.png"); @@ -7526,7 +8276,7 @@ void tst_QWidget::renderInvisible() } { // Make sure the navigation bar renders correctly even though it's hidden. - QImage testImage(navigationBar->size(), QImage::Format_ARGB32); + QImage testImage(navigationBar->size(), QImage::Format_ARGB32_Premultiplied); navigationBar->render(&testImage); #ifdef RENDER_DEBUG testImage.save("explicitlyHiddenNavigationBar.png"); @@ -7540,7 +8290,7 @@ void tst_QWidget::renderInvisible() { // Render next month button. // Fill test image with correct background color. - QImage testImage(nextMonthButton->size(), QImage::Format_ARGB32); + QImage testImage(nextMonthButton->size(), QImage::Format_ARGB32_Premultiplied); navigationBar->render(&testImage, QPoint(), QRegion(), QWidget::RenderFlags()); #ifdef RENDER_DEBUG testImage.save("nextMonthButtonBackground.png"); @@ -7578,7 +8328,7 @@ void tst_QWidget::renderInvisible() QCoreApplication::processEvents(); { // Make sure we get an image equal to the resized reference image. - QImage testImage(calendarSizeResized, QImage::Format_ARGB32); + QImage testImage(calendarSizeResized, QImage::Format_ARGB32_Premultiplied); calendar->render(&testImage); #ifdef RENDER_DEBUG testImage.save("calendarResized.png"); @@ -7590,7 +8340,7 @@ void tst_QWidget::renderInvisible() QCalendarWidget calendar; const QSize calendarSize = calendar.sizeHint(); - QImage image(2 * calendarSize, QImage::Format_ARGB32); + QImage image(2 * calendarSize, QImage::Format_ARGB32_Premultiplied); image.fill(QColor(Qt::red).rgb()); calendar.render(&image); @@ -7730,6 +8480,47 @@ void tst_QWidget::renderWithPainter() QCOMPARE(painter.renderHints(), oldRenderHints); } +void tst_QWidget::renderRTL() +{ + QFont f; + f.setStyleStrategy(QFont::NoAntialias); + const QScopedPointer<QStyle> style(QStyleFactory::create(QLatin1String("Windows"))); + + QMenu menu; + menu.setMinimumWidth(200); + menu.setFont(f); + menu.setStyle(style.data()); + menu.addAction("I"); + menu.show(); + menu.setLayoutDirection(Qt::LeftToRight); + QVERIFY(QTest::qWaitForWindowExposed(&menu)); + + QImage imageLTR(menu.size(), QImage::Format_ARGB32); + menu.render(&imageLTR); + //imageLTR.save("/tmp/rendered_1.png"); + + menu.setLayoutDirection(Qt::RightToLeft); + QImage imageRTL(menu.size(), QImage::Format_ARGB32); + menu.render(&imageRTL); + imageRTL = imageRTL.mirrored(true, false); + //imageRTL.save("/tmp/rendered_2.png"); + + QCOMPARE(imageLTR.height(), imageRTL.height()); + QCOMPARE(imageLTR.width(), imageRTL.width()); + static constexpr auto border = 4; + for (int h = border; h < imageRTL.height() - border; ++h) { + // there should be no difference on the right (aka no text) + for (int w = imageRTL.width() / 2; w < imageRTL.width() - border; ++w) { + auto pixLTR = imageLTR.pixel(w, h); + auto pixRTL = imageRTL.pixel(w, h); + if (pixLTR != pixRTL) + qDebug() << "Pixel do not match at" << w << h << ":" + << Qt::hex << pixLTR << "<->" << pixRTL; + QCOMPARE(pixLTR, pixRTL); + } + } +} + void tst_QWidget::render_task188133() { QMainWindow mainWindow; @@ -8394,6 +9185,7 @@ void tst_QWidget::updateWhileMinimized() QSKIP("Platform does not support showMinimized()"); #endif UpdateWidget widget; + widget.setPalette(simplePalette()); widget.setWindowTitle(QLatin1String(QTest::currentTestFunction())); // Filter out activation change and focus events to avoid update() calls in QWidget. widget.updateOnActivationChangeAndFocusIn = false; @@ -8967,6 +9759,7 @@ void tst_QWidget::doubleRepaint() QSKIP("Not having window server access causes the wrong number of repaints to be issues"); #endif UpdateWidget widget; + widget.setPalette(simplePalette()); widget.setWindowTitle(QLatin1String(QTest::currentTestFunction())); centerOnScreen(&widget); widget.setFocusPolicy(Qt::StrongFocus); @@ -8998,9 +9791,10 @@ void tst_QWidget::resizeInPaintEvent() QWidget window; window.setWindowTitle(QLatin1String(QTest::currentTestFunction())); UpdateWidget widget(&window); + widget.setPalette(simplePalette()); window.resize(200, 200); window.show(); - QApplication::setActiveWindow(&window); + QApplicationPrivate::setActiveWindow(&window); QVERIFY(QTest::qWaitForWindowExposed(&window)); QTRY_VERIFY(widget.numPaintEvents > 0); @@ -9056,6 +9850,9 @@ void tst_QWidget::opaqueChildren() void tst_QWidget::dumpObjectTree() { + if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation)) + QSKIP("QWindow::requestActivate() is not supported."); + QWidget w; w.setWindowTitle(QLatin1String(QTest::currentTestFunction())); Q_SET_OBJECT_NAME(w); @@ -9078,7 +9875,6 @@ void tst_QWidget::dumpObjectTree() } QTestPrivate::androidCompatibleShow(&w); - QApplication::setActiveWindow(&w); QVERIFY(QTest::qWaitForWindowActive(&w)); { @@ -9126,6 +9922,7 @@ public slots: void tst_QWidget::setMaskInResizeEvent() { UpdateWidget w; + w.setPalette(simplePalette()); w.setWindowTitle(QLatin1String(QTest::currentTestFunction())); w.reset(); w.resize(200, 200); @@ -9205,6 +10002,7 @@ void tst_QWidget::immediateRepaintAfterInvalidateBackingStore() QSKIP("We don't support immediate repaint right after show on other platforms."); QScopedPointer<UpdateWidget> widget(new UpdateWidget); + widget->setPalette(simplePalette()); widget->setWindowTitle(QLatin1String(QTest::currentTestFunction())); centerOnScreen(widget.data()); widget->show(); @@ -9656,11 +10454,12 @@ void tst_QWidget::setClearAndResizeMask() QSKIP("Wayland: This fails. Figure out why."); UpdateWidget topLevel; + topLevel.setPalette(simplePalette()); topLevel.setWindowTitle(QLatin1String(QTest::currentTestFunction())); topLevel.resize(160, 160); centerOnScreen(&topLevel); topLevel.show(); - QApplication::setActiveWindow(&topLevel); + QApplicationPrivate::setActiveWindow(&topLevel); QVERIFY(QTest::qWaitForWindowExposed(&topLevel)); QTRY_VERIFY(topLevel.numPaintEvents > 0); topLevel.reset(); @@ -9690,6 +10489,7 @@ void tst_QWidget::setClearAndResizeMask() } UpdateWidget child(&topLevel); + child.setPalette(simplePalette()); child.setAutoFillBackground(true); // NB! Opaque child. child.setPalette(Qt::red); child.resize(100, 100); @@ -10083,6 +10883,9 @@ void tst_QWidget::enterLeaveOnWindowShowHide_data() */ void tst_QWidget::enterLeaveOnWindowShowHide() { + if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation)) + QSKIP("QWindow::requestActivate() is not supported."); + QFETCH(Qt::WindowType, windowType); class Widget : public QWidget { @@ -10135,7 +10938,7 @@ void tst_QWidget::enterLeaveOnWindowShowHide() if (!QTest::qWaitForWindowExposed(secondary)) QEXPECT_FAIL("", "Secondary window failed to show, test will fail", Abort); if (secondaryWindowType == Qt::Dialog && QGuiApplication::platformName() == "windows") - QTest::qWait(250); // on Windows, we have to wait for fade-in effects + QTest::qWait(1000); // on Windows, we have to wait for fade-in effects } }; @@ -10152,11 +10955,10 @@ void tst_QWidget::enterLeaveOnWindowShowHide() if (!QTest::qWaitFor([&]{ return widget.geometry().contains(QCursor::pos()); })) QSKIP("We can't move the cursor"); widget.show(); - QApplication::setActiveWindow(&widget); QVERIFY(QTest::qWaitForWindowActive(&widget)); ++expectedEnter; - QTRY_COMPARE_WITH_TIMEOUT(widget.numEnterEvents, expectedEnter, 250); + QTRY_COMPARE_WITH_TIMEOUT(widget.numEnterEvents, expectedEnter, 1000); QCOMPARE(widget.enterPosition, widget.mapFromGlobal(cursorPos)); QVERIFY(widget.underMouse()); @@ -10253,7 +11055,7 @@ void tst_QWidget::taskQTBUG_4055_sendSyntheticEnterLeave() QTRY_COMPARE(child.numEnterEvents, 1); QCOMPARE(child.numMouseMoveEvents, 0); - // Sending synthetic enter/leave trough the parent's mousePressEvent handler. + // Sending synthetic enter/leave through the parent's mousePressEvent handler. parent.child = &child; child.hide(); @@ -10282,6 +11084,110 @@ void tst_QWidget::taskQTBUG_4055_sendSyntheticEnterLeave() QTRY_COMPARE(child.numEnterEvents, 1); QCOMPARE(child.numMouseMoveEvents, 0); } + +void tst_QWidget::hoverPosition() +{ + if (m_platform == QStringLiteral("wayland")) + QSKIP("Wayland: Clients can't set cursor position on wayland."); + + class HoverWidget : public QWidget + { + public: + HoverWidget(QWidget *parent = nullptr) : QWidget(parent) { + setMouseTracking(true); + setAttribute(Qt::WA_Hover); + } + bool event(QEvent *ev) override { + switch (ev->type()) { + case QEvent::HoverMove: + // The docs say that WA_Hover will cause a paint event on enter and leave, but not on move. + update(); + Q_FALLTHROUGH(); + case QEvent::HoverEnter: + case QEvent::HoverLeave: { + qCDebug(lcTests) << ev; + lastHoverType = ev->type(); + ++hoverEventCount; + QHoverEvent *hov = static_cast<QHoverEvent *>(ev); + mousePos = hov->position().toPoint(); + mouseScenePos = hov->scenePosition().toPoint(); + if (ev->type() == QEvent::HoverEnter) + mouseEnterScenePos = hov->scenePosition().toPoint(); + break; + } + default: + break; + } + return QWidget::event(ev); + } + void paintEvent(QPaintEvent *) override { + ++paintEventCount; + QPainter painter(this); + if (mousePos.x() > 0) + painter.setPen(Qt::red); + painter.drawRect(0, 0, width(), height()); + painter.setPen(Qt::darkGreen); + painter.drawLine(mousePos - QPoint(crossHalfWidth, 0), mousePos + QPoint(crossHalfWidth, 0)); + painter.drawLine(mousePos - QPoint(0, crossHalfWidth), mousePos + QPoint(0, crossHalfWidth)); + } + + QEvent::Type lastHoverType = QEvent::None; + int hoverEventCount = 0; + int paintEventCount = 0; + QPoint mousePos; + QPoint mouseScenePos; + QPoint mouseEnterScenePos; + + private: + const int crossHalfWidth = 5; + }; + + QCursor::setPos(m_safeCursorPos); + if (!QTest::qWaitFor([this]{ return QCursor::pos() == m_safeCursorPos; })) + QSKIP("Can't move cursor"); + + QWidget root; + root.resize(300, 300); + HoverWidget h(&root); + h.setGeometry(100, 100, 100, 100); + root.show(); + QVERIFY(QTest::qWaitForWindowExposed(&root)); + + const QPoint middle(50, 50); + // wait until we got the correct global pos + QPoint expectedGlobalPos = root.geometry().topLeft() + QPoint(100, 100) + middle; + if (!QTest::qWaitFor([&]{ return expectedGlobalPos == h.mapToGlobal(middle); })) + QSKIP("Can't move cursor"); + QPoint curpos = h.mapToGlobal(middle); + QCursor::setPos(curpos); + if (!QTest::qWaitFor([curpos]{ return QCursor::pos() == curpos; })) + QSKIP("Can't move cursor"); + QTRY_COMPARE_GE(h.hoverEventCount, 1); // HoverEnter and then probably HoverMove, so usually 2 + QTRY_COMPARE_GE(h.paintEventCount, 2); + const int enterHoverEventCount = h.hoverEventCount; + qCDebug(lcTests) << "hover enter events:" << enterHoverEventCount << "last was" << h.lastHoverType + << "; paint events:" << h.paintEventCount; + QCOMPARE(h.mousePos, middle); + QCOMPARE(h.mouseEnterScenePos, h.mapToParent(middle)); + QCOMPARE(h.mouseScenePos, h.mapToParent(middle)); + QCOMPARE(h.lastHoverType, enterHoverEventCount == 1 ? QEvent::HoverEnter : QEvent::HoverMove); + + curpos += {10, 10}; + QCursor::setPos(curpos); + if (!QTest::qWaitFor([curpos]{ return QCursor::pos() == curpos; })) + QSKIP("Can't move cursor"); + QTRY_COMPARE(h.hoverEventCount, enterHoverEventCount + 1); + QCOMPARE(h.lastHoverType, QEvent::HoverMove); + QTRY_COMPARE_GE(h.paintEventCount, 3); + + curpos += {50, 50}; // in the outer widget, but leaving the inner widget + QCursor::setPos(curpos); + if (!QTest::qWaitFor([curpos]{ return QCursor::pos() == curpos; })) + QSKIP("Can't move cursor"); + QTRY_COMPARE(h.lastHoverType, QEvent::HoverLeave); + QCOMPARE_GE(h.hoverEventCount, enterHoverEventCount + 2); + QTRY_COMPARE_GE(h.paintEventCount, 4); +} #endif void tst_QWidget::windowFlags() @@ -10387,6 +11293,7 @@ void tst_QWidget::focusWidget_task254563() void tst_QWidget::destroyBackingStore() { UpdateWidget w; + w.setPalette(simplePalette()); w.setWindowTitle(QLatin1String(QTest::currentTestFunction())); centerOnScreen(&w); w.reset(); @@ -10857,32 +11764,42 @@ void tst_QWidget::focusProxy() QCOMPARE(container2->focusOutCount, 1); } -void tst_QWidget::focusProxyAndInputMethods() +void tst_QWidget::imEnabledNotImplemented() { if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation)) - QSKIP("Window activation is not supported."); - QScopedPointer<QWidget> toplevel(new QWidget(nullptr, Qt::X11BypassWindowManagerHint)); - toplevel->setWindowTitle(QLatin1String(QTest::currentTestFunction())); - toplevel->resize(200, 200); - toplevel->setAttribute(Qt::WA_InputMethodEnabled, true); + QSKIP("QWindow::requestActivate() is not supported."); - QWidget *child = new QWidget(toplevel.data()); - child->setFocusProxy(toplevel.data()); - child->setAttribute(Qt::WA_InputMethodEnabled, true); - - toplevel->setFocusPolicy(Qt::WheelFocus); - child->setFocusPolicy(Qt::WheelFocus); - - QVERIFY(!child->hasFocus()); - QVERIFY(!toplevel->hasFocus()); + // Check that a plain widget doesn't report that it supports IM. Only + // widgets that implements either Qt::ImEnabled, or the Qt4 backup + // solution, Qt::ImSurroundingText, should do so. + QWidget topLevel; + QWidget plain(&topLevel); + QLineEdit edit(&topLevel); + topLevel.show(); - toplevel->show(); - QVERIFY(QTest::qWaitForWindowExposed(toplevel.data())); - QApplication::setActiveWindow(toplevel.data()); - QVERIFY(QTest::qWaitForWindowActive(toplevel.data())); - QVERIFY(toplevel->hasFocus()); - QVERIFY(child->hasFocus()); - QCOMPARE(qApp->focusObject(), toplevel.data()); + QVERIFY(QTest::qWaitForWindowExposed(&topLevel)); + QVERIFY(QTest::qWaitForWindowActive(&topLevel)); + + // A plain widget should return false for ImEnabled + plain.setFocus(Qt::OtherFocusReason); + QCOMPARE(QApplication::focusWidget(), &plain); + QVariant imEnabled = QApplication::inputMethod()->queryFocusObject(Qt::ImEnabled, QVariant()); + QVERIFY(imEnabled.isValid()); + QVERIFY(!imEnabled.toBool()); + + // But a lineedit should return true + edit.setFocus(Qt::OtherFocusReason); + QCOMPARE(QApplication::focusWidget(), &edit); + imEnabled = QApplication::inputMethod()->queryFocusObject(Qt::ImEnabled, QVariant()); + QVERIFY(imEnabled.isValid()); + QVERIFY(imEnabled.toBool()); + + // ImEnabled should be false when a lineedit is read-only since + // ImEnabled indicates the widget accepts input method _input_. + edit.setReadOnly(true); + imEnabled = QApplication::inputMethod()->queryFocusObject(Qt::ImEnabled, QVariant()); + QVERIFY(imEnabled.isValid()); + QVERIFY(!imEnabled.toBool()); } #ifdef QT_BUILD_INTERNAL @@ -11241,7 +12158,6 @@ void tst_QWidget::grabMouse() layout->addWidget(grabber); centerOnScreen(&w); w.show(); - QApplication::setActiveWindow(&w); QVERIFY(QTest::qWaitForWindowActive(&w)); QStringList expectedLog; @@ -11278,7 +12194,6 @@ void tst_QWidget::grabKeyboard() layout->addWidget(nonGrabber); centerOnScreen(&w); w.show(); - QApplication::setActiveWindow(&w); QVERIFY(QTest::qWaitForWindowActive(&w)); nonGrabber->setFocus(); grabber->grabKeyboard(); @@ -11372,6 +12287,9 @@ public: void tst_QWidget::touchEventSynthesizedMouseEvent() { + if (m_platform.startsWith(QLatin1String("wayland"), Qt::CaseInsensitive)) + QSKIP("This test failed on Wayland. See also QTBUG-107157."); + { // Simple case, we ignore the touch events, we get mouse events instead TouchMouseWidget widget; @@ -12080,6 +12998,7 @@ void tst_QWidget::resizeStaticContentsChildWidget_QTBUG35282() widget.resize(200,200); UpdateWidget childWidget(&widget); + childWidget.setPalette(simplePalette()); childWidget.setAttribute(Qt::WA_StaticContents); childWidget.setAttribute(Qt::WA_OpaquePaintEvent); childWidget.setGeometry(250, 250, 500, 500); @@ -12178,60 +13097,54 @@ void tst_QWidget::testForOutsideWSRangeFlag() } } -class TabletWidget : public QWidget +void tst_QWidget::tabletTracking() { -public: - TabletWidget(QWidget *parent) : QWidget(parent) { } + class TabletWidget : public QWidget + { + public: + using QWidget::QWidget; - int tabletEventCount = 0; - int pressEventCount = 0; - int moveEventCount = 0; - int releaseEventCount = 0; - int trackingChangeEventCount = 0; - qint64 uid = -1; + int tabletEventCount = 0; + int pressEventCount = 0; + int moveEventCount = 0; + int releaseEventCount = 0; + int trackingChangeEventCount = 0; + qint64 uid = -1; -protected: - void tabletEvent(QTabletEvent *event) override { - ++tabletEventCount; - uid = event->pointingDevice()->uniqueId().numericId(); - switch (event->type()) { - case QEvent::TabletMove: - ++moveEventCount; - break; - case QEvent::TabletPress: - ++pressEventCount; - break; - case QEvent::TabletRelease: - ++releaseEventCount; - break; - default: - break; + protected: + void tabletEvent(QTabletEvent *event) override { + ++tabletEventCount; + uid = event->pointingDevice()->uniqueId().numericId(); + switch (event->type()) { + case QEvent::TabletMove: + ++moveEventCount; + break; + case QEvent::TabletPress: + ++pressEventCount; + break; + case QEvent::TabletRelease: + ++releaseEventCount; + break; + default: + break; + } } - } - - bool event(QEvent *ev) override { - if (ev->type() == QEvent::TabletTrackingChange) - ++trackingChangeEventCount; - return QWidget::event(ev); - } -}; -void tst_QWidget::tabletTracking() -{ - QWidget parent; - parent.setWindowTitle(QLatin1String(QTest::currentTestFunction())); - parent.resize(200,200); - // QWidgetWindow::handleTabletEvent doesn't deliver tablet events to the window's widget, only to a child. - // So it doesn't do any good to show a TabletWidget directly: it needs a parent. - TabletWidget widget(&parent); + bool event(QEvent *ev) override { + if (ev->type() == QEvent::TabletTrackingChange) + ++trackingChangeEventCount; + return QWidget::event(ev); + } + } widget; + widget.setWindowTitle(QLatin1String(QTest::currentTestFunction())); widget.resize(200,200); - parent.showNormal(); - QVERIFY(QTest::qWaitForWindowExposed(&parent)); + widget.showNormal(); + QVERIFY(QTest::qWaitForWindowExposed(&widget)); widget.setAttribute(Qt::WA_TabletTracking); QTRY_COMPARE(widget.trackingChangeEventCount, 1); QVERIFY(widget.hasTabletTracking()); - QWindow *window = parent.windowHandle(); + QWindow *window = widget.windowHandle(); QPointF local(10, 10); QPointF global = window->mapToGlobal(local.toPoint()); QPointF deviceLocal = QHighDpi::toNativeLocalPosition(local, window); @@ -12553,7 +13466,7 @@ void tst_QWidget::deleteWindowInCloseEvent() QApplication::exec(); // It should still result in a single lastWindowClosed emit - QCOMPARE(quitSpy.count(), 1); + QCOMPARE(quitSpy.size(), 1); } /*! @@ -12574,7 +13487,7 @@ void tst_QWidget::quitOnClose() widget->close(); }); QApplication::exec(); - QCOMPARE(quitSpy.count(), 1); + QCOMPARE(quitSpy.size(), 1); widget->show(); QVERIFY(QTest::qWaitForWindowExposed(widget.get())); @@ -12582,7 +13495,7 @@ void tst_QWidget::quitOnClose() widget.reset(); }); QApplication::exec(); - QCOMPARE(quitSpy.count(), 2); + QCOMPARE(quitSpy.size(), 2); } void tst_QWidget::setParentChangesFocus_data() @@ -12642,20 +13555,24 @@ void tst_QWidget::setParentChangesFocus() QCOMPARE(secondary->focusWidget()->objectName(), focusWidget); } secondary->show(); - QApplication::setActiveWindow(secondary.get()); + QApplicationPrivate::setActiveWindow(secondary.get()); QVERIFY(QTest::qWaitForWindowActive(secondary.get())); if (!reparentBeforeShow) { secondary->setParent(targetParent ? &window : nullptr, targetType); secondary->show(); // reparenting hides, so show again - QApplication::setActiveWindow(secondary.get()); + QApplicationPrivate::setActiveWindow(secondary.get()); QVERIFY(QTest::qWaitForWindowActive(secondary.get())); } + QVERIFY(QTest::qWaitFor([]{ return QApplication::focusWidget(); })); QCOMPARE(QApplication::focusWidget()->objectName(), focusWidget); } void tst_QWidget::activateWhileModalHidden() { + if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation)) + QSKIP("QWindow::requestActivate() is not supported."); + QDialog dialog; dialog.setWindowModality(Qt::ApplicationModal); dialog.show(); @@ -12673,6 +13590,35 @@ void tst_QWidget::activateWhileModalHidden() QCOMPARE(QApplication::activeWindow(), &window); } +// Create a simple palette to prevent multiple paint events +QPalette tst_QWidget::simplePalette() +{ + static QPalette simplePalette = []{ + const QColor windowText = Qt::black; + const QColor backGround = QColor(239, 239, 239); + const QColor light = backGround.lighter(150); + const QColor mid = (backGround.darker(130)); + const QColor midLight = mid.lighter(110); + const QColor base = Qt::white; + const QColor dark = backGround.darker(150); + const QColor text = Qt::black; + const QColor highlight = QColor(48, 140, 198); + const QColor hightlightedText = Qt::white; + const QColor button = backGround; + const QColor shadow = dark.darker(135); + + QPalette defaultPalette(windowText, backGround, light, dark, mid, text, base); + defaultPalette.setBrush(QPalette::Midlight, midLight); + defaultPalette.setBrush(QPalette::Button, button); + defaultPalette.setBrush(QPalette::Shadow, shadow); + defaultPalette.setBrush(QPalette::HighlightedText, hightlightedText); + defaultPalette.setBrush(QPalette::Active, QPalette::Highlight, highlight); + return defaultPalette; + }(); + + return simplePalette; +} + #ifdef Q_OS_ANDROID void tst_QWidget::showFullscreenAndroid() { @@ -12706,5 +13652,438 @@ void tst_QWidget::showFullscreenAndroid() } #endif // Q_OS_ANDROID +void tst_QWidget::setVisibleDuringDestruction() +{ + CreateDestroyWidget widget; + widget.create(); + QVERIFY(widget.windowHandle()); + + QSignalSpy signalSpy(widget.windowHandle(), &QWindow::visibleChanged); + EventSpy<QWindow> showEventSpy(widget.windowHandle(), QEvent::Show); + widget.show(); + QTRY_COMPARE(showEventSpy.count(), 1); + QTRY_COMPARE(signalSpy.count(), 1); + + EventSpy<QWindow> hideEventSpy(widget.windowHandle(), QEvent::Hide); + widget.hide(); + QTRY_COMPARE(hideEventSpy.count(), 1); + QTRY_COMPARE(signalSpy.count(), 2); + + widget.show(); + QTRY_COMPARE(showEventSpy.count(), 2); + QTRY_COMPARE(signalSpy.count(), 3); + + widget.destroy(); + QTRY_COMPARE(hideEventSpy.count(), 2); + QTRY_COMPARE(signalSpy.count(), 4); +} + +void tst_QWidget::explicitShowHide() +{ + { + QWidget parent; + parent.setObjectName("Parent"); + QWidget child(&parent); + child.setObjectName("Child"); + + QCOMPARE(parent.testAttribute(Qt::WA_WState_ExplicitShowHide), false); + QCOMPARE(parent.testAttribute(Qt::WA_WState_Hidden), true); + QCOMPARE(child.testAttribute(Qt::WA_WState_ExplicitShowHide), false); + QCOMPARE(child.testAttribute(Qt::WA_WState_Hidden), false); + + parent.show(); + QCOMPARE(parent.testAttribute(Qt::WA_WState_ExplicitShowHide), true); + QCOMPARE(parent.testAttribute(Qt::WA_WState_Hidden), false); + QCOMPARE(child.testAttribute(Qt::WA_WState_ExplicitShowHide), false); + QCOMPARE(child.testAttribute(Qt::WA_WState_Hidden), false); + + // Fix up earlier expected failure + child.setAttribute(Qt::WA_WState_ExplicitShowHide, false); + + parent.hide(); + QCOMPARE(parent.testAttribute(Qt::WA_WState_ExplicitShowHide), true); + QCOMPARE(parent.testAttribute(Qt::WA_WState_Hidden), true); + QCOMPARE(child.testAttribute(Qt::WA_WState_ExplicitShowHide), false); + QCOMPARE(child.testAttribute(Qt::WA_WState_Hidden), false); + } + + { + // Test what happens when a child is reparented after showing it + + QWidget parent; + parent.setObjectName("Parent"); + QWidget child; + child.setObjectName("Child"); + + child.show(); + QCOMPARE(child.isVisible(), true); + QCOMPARE(child.testAttribute(Qt::WA_WState_ExplicitShowHide), true); + QCOMPARE(child.testAttribute(Qt::WA_WState_Hidden), false); + + child.setParent(&parent); + // As documented, a widget becomes invisible as part of changing + // its parent, even if it was previously visible. The user must call + // show() to make the widget visible again. + QCOMPARE(child.isVisible(), false); + + // However, the widget does not end up with Qt::WA_WState_Hidden, + // as QWidget::setParent treats it as a child, which normally will + // not get Qt::WA_WState_Hidden out of the box. + QCOMPARE(child.testAttribute(Qt::WA_WState_Hidden), false); + + // For some reason we reset WA_WState_ExplicitShowHide, and it's + // not clear whether this is correct or not See QWidget::setParent() + // for a comment with more details. + QEXPECT_FAIL("", "We reset WA_WState_ExplicitShowHide on widget re-parent", Continue); + QCOMPARE(child.testAttribute(Qt::WA_WState_ExplicitShowHide), true); + + // The fact that the child doesn't have Qt::WA_WState_Hidden means + // it's sufficient to show the parent widget. We don't need to + // explicitly show the child. + parent.show(); + QCOMPARE(child.isVisible(), true); + QCOMPARE(child.testAttribute(Qt::WA_WState_Hidden), false); + } + + { + QWidget parent; + parent.setObjectName("Parent"); + QWidget child(&parent); + child.setObjectName("Child"); + + parent.show(); + + // If a non-native child ends up being closed, we will hide the + // widget, but do so via QWidget::hide(), which marks the widget + // as explicitly hidden. + + child.setAttribute(Qt::WA_WState_ExplicitShowHide, false); + QCOMPARE(child.close(), true); + QCOMPARE(child.testAttribute(Qt::WA_WState_ExplicitShowHide), true); + QCOMPARE(child.testAttribute(Qt::WA_WState_Hidden), true); + + child.show(); + child.setAttribute(Qt::WA_NativeWindow); + child.setAttribute(Qt::WA_WState_ExplicitShowHide, false); + QCOMPARE(child.close(), true); + QCOMPARE(child.testAttribute(Qt::WA_WState_ExplicitShowHide), true); + QCOMPARE(child.testAttribute(Qt::WA_WState_Hidden), true); + + child.show(); + child.setAttribute(Qt::WA_WState_ExplicitShowHide, false); + QCOMPARE(child.windowHandle()->close(), false); // Can't close non-top level QWindows + QCOMPARE(child.testAttribute(Qt::WA_WState_ExplicitShowHide), false); + QCOMPARE(child.testAttribute(Qt::WA_WState_Hidden), false); + + // If we end up in QWidgetPrivate::handleClose via QWidgetWindow::closeEvent, + // either through QWindow::close(), or via QWSI::handleCloseEvent, we'll still + // do the explicit hide. + + child.show(); + child.setAttribute(Qt::WA_WState_ExplicitShowHide, false); + QCOMPARE(QWindowSystemInterface::handleCloseEvent<QWindowSystemInterface::SynchronousDelivery>( + child.windowHandle()), true); + QEXPECT_FAIL("", "Closing a native child via QWSI is treated as an explicit hide", Continue); + QCOMPARE(child.testAttribute(Qt::WA_WState_ExplicitShowHide), false); + QCOMPARE(child.testAttribute(Qt::WA_WState_Hidden), true); + } + + { + QWidget widget; + widget.show(); + widget.hide(); + + QCOMPARE(widget.testAttribute(Qt::WA_WState_Visible), false); + QCOMPARE(widget.testAttribute(Qt::WA_WState_ExplicitShowHide), true); + QCOMPARE(widget.testAttribute(Qt::WA_WState_Hidden), true); + + // The widget is now explicitly hidden. Showing it again, via QWindow, + // should make the widget visible, and it should not stay hidden, as + // that's an invalid state for a widget. + + widget.windowHandle()->setVisible(true); + QCOMPARE(widget.testAttribute(Qt::WA_WState_Visible), true); + QCOMPARE(widget.testAttribute(Qt::WA_WState_ExplicitShowHide), true); + QCOMPARE(widget.testAttribute(Qt::WA_WState_Hidden), false); + } +} + +/*! + Verify that we deliver DragEnter/Leave events symmetrically, even if the + widget entered didn't accept the DragEnter event. +*/ +void tst_QWidget::dragEnterLeaveSymmetry() +{ + QWidget widget; + widget.setAcceptDrops(true); + QLineEdit lineEdit; + QLabel label("Hello world"); + label.setAcceptDrops(true); + + struct EventFilter : QObject + { + bool eventFilter(QObject *receiver, QEvent *event) override + { + switch (event->type()) { + case QEvent::DragEnter: + case QEvent::DragLeave: + receivers[event->type()] << receiver; + break; + + default: + break; + } + + return false; + } + + QMap<QEvent::Type, QList<QObject *>> receivers; + + void clear() { receivers.clear(); } + bool hasEntered(QWidget *widget) const + { + return receivers.value(QEvent::DragEnter).contains(widget); + } + bool hasLeft(QWidget *widget) const + { + return receivers.value(QEvent::DragLeave).contains(widget); + } + } filter; + + widget.installEventFilter(&filter); + lineEdit.installEventFilter(&filter); + label.installEventFilter(&filter); + + QVBoxLayout vbox; + vbox.setContentsMargins(10, 10, 10, 10); + vbox.addWidget(&lineEdit); + vbox.addWidget(&label); + widget.setLayout(&vbox); + + widget.show(); + QVERIFY(QTest::qWaitForWindowExposed(&widget)); + + QMimeData data; + data.setColorData(QVariant::fromValue(Qt::red)); + QWindowSystemInterface::handleDrag(widget.windowHandle(), &data, QPoint(1, 1), + Qt::ActionMask, Qt::LeftButton, {}); + QVERIFY(filter.hasEntered(&widget)); + QVERIFY(!filter.hasEntered(&lineEdit)); + QVERIFY(!filter.hasEntered(&label)); + QVERIFY(widget.underMouse()); + QVERIFY(!lineEdit.underMouse()); + filter.clear(); + + QWindowSystemInterface::handleDrag(widget.windowHandle(), &data, lineEdit.geometry().center(), + Qt::ActionMask, Qt::LeftButton, {}); + // DragEnter propagates as the lineEdit doesn't want it, so the widget + // sees both a Leave and an Enter event + QVERIFY(filter.hasLeft(&widget)); + QVERIFY(filter.hasEntered(&widget)); + QVERIFY(filter.hasEntered(&widget)); + // both have the UnderMouse attribute set + QVERIFY(lineEdit.underMouse()); + QVERIFY(widget.underMouse()); + + // The lineEdit didn't accept the DragEnter, but it should still has to + // get the DragLeave so that UnderMouse is cleared; the widget gets both + // Leave and Enter through propagation. + QWindowSystemInterface::handleDrag(widget.windowHandle(), &data, label.geometry().center(), + Qt::ActionMask, Qt::LeftButton, {}); + QVERIFY(filter.hasLeft(&lineEdit)); + QVERIFY(filter.hasLeft(&widget)); + QVERIFY(filter.hasEntered(&label)); + QVERIFY(filter.hasEntered(&widget)); + + QVERIFY(!lineEdit.underMouse()); + QVERIFY(label.underMouse()); + QVERIFY(widget.underMouse()); +} + +void tst_QWidget::reparentWindowHandles_data() +{ + QTest::addColumn<int>("stage"); + QTest::addRow("reparent child") << 1; + QTest::addRow("top level to child") << 2; + QTest::addRow("transient parent") << 3; + QTest::addRow("window container") << 4; +} + +void tst_QWidget::reparentWindowHandles() +{ + const bool nativeSiblingsOriginal = qApp->testAttribute(Qt::AA_DontCreateNativeWidgetSiblings); + qApp->setAttribute(Qt::AA_DontCreateNativeWidgetSiblings, true); + auto nativeSiblingGuard = qScopeGuard([&]{ + qApp->setAttribute(Qt::AA_DontCreateNativeWidgetSiblings, nativeSiblingsOriginal); + }); + + QFETCH(int, stage); + + switch (stage) { + case 1: { + // Reparent child widget + + QWidget topLevel; + topLevel.setAttribute(Qt::WA_NativeWindow); + QVERIFY(topLevel.windowHandle()); + QPointer<QWidget> child = new QWidget(&topLevel); + child->setAttribute(Qt::WA_DontCreateNativeAncestors); + child->setAttribute(Qt::WA_NativeWindow); + QVERIFY(child->windowHandle()); + + QWidget anotherTopLevel; + anotherTopLevel.setAttribute(Qt::WA_NativeWindow); + QVERIFY(anotherTopLevel.windowHandle()); + QPointer<QWidget> intermediate = new QWidget(&anotherTopLevel); + QPointer<QWidget> leaf = new QWidget(intermediate); + leaf->setAttribute(Qt::WA_DontCreateNativeAncestors); + leaf->setAttribute(Qt::WA_NativeWindow); + QVERIFY(leaf->windowHandle()); + QVERIFY(!intermediate->windowHandle()); + + // Reparenting a native widget should reparent the QWindow + child->setParent(leaf); + QCOMPARE(child->windowHandle()->parent(), leaf->windowHandle()); + QCOMPARE(child->windowHandle()->transientParent(), nullptr); + QVERIFY(!intermediate->windowHandle()); + + // So should reparenting a non-native widget with native children + intermediate->setParent(&topLevel); + QVERIFY(!intermediate->windowHandle()); + QCOMPARE(leaf->windowHandle()->parent(), topLevel.windowHandle()); + QCOMPARE(leaf->windowHandle()->transientParent(), nullptr); + QCOMPARE(child->windowHandle()->parent(), leaf->windowHandle()); + QCOMPARE(child->windowHandle()->transientParent(), nullptr); + } + break; + case 2: { + // Top level to child + + QWidget topLevel; + topLevel.setAttribute(Qt::WA_NativeWindow); + + // A regular top level loses its nativeness + QPointer<QWidget> regularToplevel = new QWidget; + regularToplevel->show(); + QVERIFY(QTest::qWaitForWindowExposed(regularToplevel)); + QVERIFY(regularToplevel->windowHandle()); + regularToplevel->setParent(&topLevel); + QVERIFY(!regularToplevel->windowHandle()); + + // A regular top level loses its nativeness + QPointer<QWidget> regularToplevelWithNativeChildren = new QWidget; + QPointer<QWidget> nativeChild = new QWidget(regularToplevelWithNativeChildren); + nativeChild->setAttribute(Qt::WA_DontCreateNativeAncestors); + nativeChild->setAttribute(Qt::WA_NativeWindow); + QVERIFY(nativeChild->windowHandle()); + regularToplevelWithNativeChildren->show(); + QVERIFY(QTest::qWaitForWindowExposed(regularToplevelWithNativeChildren)); + QVERIFY(regularToplevelWithNativeChildren->windowHandle()); + regularToplevelWithNativeChildren->setParent(&topLevel); + QVERIFY(!regularToplevelWithNativeChildren->windowHandle()); + // But the native child does not + QVERIFY(nativeChild->windowHandle()); + QCOMPARE(nativeChild->windowHandle()->parent(), topLevel.windowHandle()); + + // An explicitly native top level keeps its nativeness, and the window handle moves + QPointer<QWidget> nativeTopLevel = new QWidget; + nativeTopLevel->setAttribute(Qt::WA_NativeWindow); + QVERIFY(nativeTopLevel->windowHandle()); + nativeTopLevel->setParent(&topLevel); + QVERIFY(nativeTopLevel->windowHandle()); + QCOMPARE(nativeTopLevel->windowHandle()->parent(), topLevel.windowHandle()); + } + break; + case 3: { + // Transient parent + + QWidget topLevel; + topLevel.setAttribute(Qt::WA_NativeWindow); + QVERIFY(topLevel.windowHandle()); + QPointer<QWidget> child = new QWidget(&topLevel); + child->setAttribute(Qt::WA_NativeWindow); + QVERIFY(child->windowHandle()); + + QWidget anotherTopLevel; + anotherTopLevel.setAttribute(Qt::WA_NativeWindow); + QVERIFY(anotherTopLevel.windowHandle()); + + // Make transient child of top level + anotherTopLevel.setParent(&topLevel, Qt::Window); + QCOMPARE(anotherTopLevel.windowHandle()->parent(), nullptr); + QCOMPARE(anotherTopLevel.windowHandle()->transientParent(), topLevel.windowHandle()); + + // Make transient child of child + anotherTopLevel.setParent(child, Qt::Window); + QCOMPARE(anotherTopLevel.windowHandle()->parent(), nullptr); + QCOMPARE(anotherTopLevel.windowHandle()->transientParent(), topLevel.windowHandle()); + } + break; + case 4: { + // Window container + + QWidget topLevel; + topLevel.setAttribute(Qt::WA_NativeWindow); + QVERIFY(topLevel.windowHandle()); + + QPointer<QWidget> child = new QWidget(&topLevel); + QVERIFY(!child->windowHandle()); + + QWindow *window = new QWindow; + QWidget *container = QWidget::createWindowContainer(window); + container->setParent(child); + topLevel.show(); + QVERIFY(QTest::qWaitForWindowExposed(&topLevel)); + QCOMPARE(window->parent(), topLevel.windowHandle()); + + QWidget anotherTopLevel; + anotherTopLevel.setAttribute(Qt::WA_NativeWindow); + QVERIFY(anotherTopLevel.windowHandle()); + + child->setParent(&anotherTopLevel); + QCOMPARE(window->parent(), anotherTopLevel.windowHandle()); + } + break; + default: + Q_UNREACHABLE(); + } +} + +#ifndef QT_NO_CONTEXTMENU +void tst_QWidget::contextMenuTrigger() +{ + class ContextMenuWidget : public QWidget + { + public: + int events = 0; + + protected: + void contextMenuEvent(QContextMenuEvent *) override { ++events; } + }; + + const Qt::ContextMenuTrigger wasTrigger = QGuiApplication::styleHints()->contextMenuTrigger(); + auto restoreTriggerGuard = qScopeGuard([wasTrigger]{ + QGuiApplication::styleHints()->setContextMenuTrigger(wasTrigger); + }); + + ContextMenuWidget widget; + widget.show(); + QVERIFY(!qApp->topLevelWindows().empty()); + auto *window = qApp->topLevelWindows()[0]; + QVERIFY(window); + QCOMPARE(widget.events, 0); + QGuiApplication::styleHints()->setContextMenuTrigger(Qt::ContextMenuTrigger::Press); + QTest::mousePress(window, Qt::RightButton); + QCOMPARE(widget.events, 1); + QTest::mouseRelease(window, Qt::RightButton); + QCOMPARE(widget.events, 1); + QGuiApplication::styleHints()->setContextMenuTrigger(Qt::ContextMenuTrigger::Release); + QTest::mousePress(window, Qt::RightButton); + QCOMPARE(widget.events, 1); + QTest::mouseRelease(window, Qt::RightButton); + QCOMPARE(widget.events, 2); +} +#endif + QTEST_MAIN(tst_QWidget) #include "tst_qwidget.moc" diff --git a/tests/auto/widgets/kernel/qwidget/tst_qwidget_mac_helpers.h b/tests/auto/widgets/kernel/qwidget/tst_qwidget_mac_helpers.h deleted file mode 100644 index fec9706ef5..0000000000 --- a/tests/auto/widgets/kernel/qwidget/tst_qwidget_mac_helpers.h +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 -#include <QtCore/QString> -#include <QtCore/QPair> -#include <QtWidgets/QWidget> - -#pragma once // Yeah, it's deprecated in general, but it's standard practice for Mac OS X. - -QString nativeWindowTitle(QWidget *widget, Qt::WindowState state); -bool nativeWindowModified(QWidget *widget); diff --git a/tests/auto/widgets/kernel/qwidget/tst_qwidget_mac_helpers.mm b/tests/auto/widgets/kernel/qwidget/tst_qwidget_mac_helpers.mm deleted file mode 100644 index 19c5c0d714..0000000000 --- a/tests/auto/widgets/kernel/qwidget/tst_qwidget_mac_helpers.mm +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -// some versions of CALayer.h use 'slots' as an identifier -#define QT_NO_KEYWORDS - -#include "tst_qwidget_mac_helpers.h" -#include <QApplication> -#include <qpa/qplatformnativeinterface.h> -#include <private/qcore_mac_p.h> - -#include <AppKit/AppKit.h> - -QString nativeWindowTitle(QWidget *window, Qt::WindowState state) -{ - QWindow *qwindow = window->windowHandle(); - NSWindow *nswindow = (NSWindow *) qApp->platformNativeInterface()->nativeResourceForWindow("nswindow", qwindow); - QCFString macTitle; - if (state == Qt::WindowMinimized) { - macTitle = reinterpret_cast<CFStringRef>([[nswindow miniwindowTitle] retain]); - } else { - macTitle = reinterpret_cast<CFStringRef>([[nswindow title] retain]); - } - return macTitle; -} - -bool nativeWindowModified(QWidget *widget) -{ - QWindow *qwindow = widget->windowHandle(); - NSWindow *nswindow = (NSWindow *) qApp->platformNativeInterface()->nativeResourceForWindow("nswindow", qwindow); - return [nswindow isDocumentEdited]; -} diff --git a/tests/auto/widgets/kernel/qwidget_window/BLACKLIST b/tests/auto/widgets/kernel/qwidget_window/BLACKLIST index 0e1a2d58dc..77853a3e8c 100644 --- a/tests/auto/widgets/kernel/qwidget_window/BLACKLIST +++ b/tests/auto/widgets/kernel/qwidget_window/BLACKLIST @@ -1,7 +1,3 @@ -[tst_resize_count] -# QTBUG-66345 -opensuse-42.3 -ubuntu-16.04 # QTBUG-87412 [tst_move_show] android diff --git a/tests/auto/widgets/kernel/qwidget_window/CMakeLists.txt b/tests/auto/widgets/kernel/qwidget_window/CMakeLists.txt index 9bae267970..af60c92cbf 100644 --- a/tests/auto/widgets/kernel/qwidget_window/CMakeLists.txt +++ b/tests/auto/widgets/kernel/qwidget_window/CMakeLists.txt @@ -1,16 +1,24 @@ -# Generated from qwidget_window.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qwidget_window Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qwidget_window LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qwidget_window SOURCES tst_qwidget_window.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::CorePrivate Qt::Gui Qt::GuiPrivate Qt::TestPrivate Qt::Widgets + Qt::WidgetsPrivate ) diff --git a/tests/auto/widgets/kernel/qwidget_window/tst_qwidget_window.cpp b/tests/auto/widgets/kernel/qwidget_window/tst_qwidget_window.cpp index 689a65518e..1fab69fdcc 100644 --- a/tests/auto/widgets/kernel/qwidget_window/tst_qwidget_window.cpp +++ b/tests/auto/widgets/kernel/qwidget_window/tst_qwidget_window.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> @@ -28,6 +28,8 @@ #include <QtTest/private/qtesthelpers_p.h> +#include <QtWidgets/private/qapplication_p.h> + using namespace QTestPrivate; // Compare a window position that may go through scaling in the platform plugin with fuzz. @@ -51,6 +53,7 @@ public: tst_QWidget_window(); public slots: + void init(); void initTestCase(); void cleanupTestCase(); void cleanup(); @@ -82,6 +85,7 @@ private slots: void tst_dnd(); void tst_dnd_events(); void tst_dnd_propagation(); + void tst_dnd_destroyOnDrop(); #endif void tst_qtbug35600(); @@ -107,8 +111,13 @@ private slots: void mouseMoveWithPopup_data(); void mouseMoveWithPopup(); + void showHideWindowHandle_data(); + void showHideWindowHandle(); + void resetFocusObjectOnDestruction(); + void cleanupOnDestruction(); + private: QSize m_testWidgetSize; const int m_fuzz; @@ -126,6 +135,11 @@ void tst_QWidget_window::initTestCase() { } +void tst_QWidget_window::init() +{ + QTest::failOnWarning(QRegularExpression(".*No such slot.*")); +} + void tst_QWidget_window::cleanupTestCase() { } @@ -677,7 +691,6 @@ void tst_QWidget_window::tst_dnd() dndTestWidget.show(); QVERIFY(QTest::qWaitForWindowExposed(&dndTestWidget)); - qApp->setActiveWindow(&dndTestWidget); QVERIFY(QTest::qWaitForWindowActive(&dndTestWidget)); QMimeData mimeData; @@ -917,6 +930,78 @@ void tst_QWidget_window::tst_dnd_propagation() QCOMPARE(target.mDndEvents, "enter leave enter drop "); } + +class ReparentSelfOnDropWidget : public QWidget +{ +public: + ReparentSelfOnDropWidget(QWidget *newFutureParent) + : m_newFutureParent(newFutureParent) + { + setAcceptDrops(true); + + const QRect availableGeometry = QGuiApplication::primaryScreen()->availableGeometry(); + auto width = availableGeometry.width() / 6; + auto height = availableGeometry.height() / 4; + + setGeometry(availableGeometry.x() + 200, availableGeometry.y() + 200, width, height); + + QLabel *label = new QLabel(QStringLiteral("Test"), this); + label->setGeometry(40, 40, 60, 60); + label->setAcceptDrops(true); + } + + void dragEnterEvent(QDragEnterEvent *event) override + { + event->accept(); + } + + void dragMoveEvent(QDragMoveEvent *event) override + { + event->acceptProposedAction(); + } + + void dropEvent(QDropEvent *event) override + { + event->accept(); + // Turn 'this' from a top-level widget to a child widget. + // This destroys the QWidgetWindow since the widget is no longer top-level. + setParent(m_newFutureParent); + } + +private: + QWidget *m_newFutureParent; +}; + +void tst_QWidget_window::tst_dnd_destroyOnDrop() +{ + if (QGuiApplication::platformName().startsWith(QLatin1String("wayland"), Qt::CaseInsensitive)) + QSKIP("Wayland: This fails. Figure out why."); + + QMimeData mimeData; + mimeData.setText(QLatin1String("testmimetext")); + + QWidget newParent; + newParent.resize(400, 400); + newParent.show(); + QVERIFY(QTest::qWaitForWindowActive(&newParent)); + + ReparentSelfOnDropWidget *target = new ReparentSelfOnDropWidget(&newParent); + target->show(); + QVERIFY(QTest::qWaitForWindowActive(target)); + + Qt::DropActions supportedActions = Qt::DropAction::CopyAction; + QWindow *window = target->windowHandle(); + + auto posInsideDropTarget = QHighDpi::toNativePixels(QPoint(20, 20), window->screen()); + auto posInsideLabel = QHighDpi::toNativePixels(QPoint(60, 60), window->screen()); + + QWindowSystemInterface::handleDrag(window, &mimeData, posInsideDropTarget, supportedActions, {}, {}); + QWindowSystemInterface::handleDrag(window, &mimeData, posInsideLabel, supportedActions, {}, {}); + QWindowSystemInterface::handleDrop(window, &mimeData, posInsideLabel, supportedActions, {}, {}); + + QGuiApplication::processEvents(); +} + #endif void tst_QWidget_window::tst_qtbug35600() @@ -1377,6 +1462,9 @@ void tst_QWidget_window::mouseMoveWithPopup_data() void tst_QWidget_window::mouseMoveWithPopup() { + if (QGuiApplication::platformName().startsWith(QLatin1String("wayland"), Qt::CaseInsensitive)) + QSKIP("Wayland: Skip this test, see also QTBUG-107154"); + QFETCH(Qt::WindowType, windowType); class Window : public QWidget @@ -1546,14 +1634,164 @@ void tst_QWidget_window::mouseMoveWithPopup() // but the release event will still be delivered to the first popup - dialogs might not get it QCOMPARE(mouseAction(Qt::LeftButton), QEvent::MouseButtonRelease); - if (topLevel.popup->mouseReleaseCount != 1 - && !QGuiApplication::platformName().startsWith(QLatin1String("windows"), Qt::CaseInsensitive)) + if (topLevel.popup->mouseReleaseCount != 1) QEXPECT_FAIL("Dialog", "Platform specific behavior", Continue); QCOMPARE(topLevel.popup->mouseReleaseCount, 1); } +struct ShowHideEntry { + QEvent::Type action; + Qt::WindowType target; + using List = QList<ShowHideEntry>; +}; + +void tst_QWidget_window::showHideWindowHandle_data() +{ + QTest::addColumn<ShowHideEntry::List>("entries"); + + QTest::addRow("show/hide widget") << ShowHideEntry::List{ + { QEvent::Show, Qt::Widget }, { QEvent::Hide, Qt::Widget } + }; + QTest::addRow("show/hide window") << ShowHideEntry::List{ + { QEvent::Show, Qt::Window }, { QEvent::Hide, Qt::Window } + }; + QTest::addRow("show widget, hide window") << ShowHideEntry::List{ + { QEvent::Show, Qt::Widget }, { QEvent::Hide, Qt::Window } + }; + QTest::addRow("show window, hide widget") << ShowHideEntry::List{ + { QEvent::Show, Qt::Window }, { QEvent::Hide, Qt::Widget } + }; + QTest::addRow("show/hide widget, then show window, hide widget") << ShowHideEntry::List{ + { QEvent::Show, Qt::Widget }, { QEvent::Hide, Qt::Widget }, + { QEvent::Show, Qt::Window }, { QEvent::Hide, Qt::Widget } + }; + QTest::addRow("show widget, close widget, show widget") << ShowHideEntry::List{ + { QEvent::Show, Qt::Widget }, { QEvent::Close, Qt::Widget }, { QEvent::Show, Qt::Widget } + }; + QTest::addRow("show widget, close widget, show window") << ShowHideEntry::List{ + { QEvent::Show, Qt::Widget }, { QEvent::Close, Qt::Widget }, { QEvent::Show, Qt::Window } + }; + QTest::addRow("show widget, close window, show widget") << ShowHideEntry::List{ + { QEvent::Show, Qt::Widget }, { QEvent::Close, Qt::Window }, { QEvent::Show, Qt::Widget } + }; + QTest::addRow("show widget, close window, show window") << ShowHideEntry::List{ + { QEvent::Show, Qt::Widget }, { QEvent::Close, Qt::Window }, { QEvent::Show, Qt::Window } + }; +} + +void tst_QWidget_window::showHideWindowHandle() +{ + QWidget parent; + parent.setObjectName("Parent"); + QCOMPARE(parent.isVisible(), false); + QCOMPARE(parent.testAttribute(Qt::WA_WState_ExplicitShowHide), false); + QCOMPARE(parent.testAttribute(Qt::WA_WState_Visible), false); + QCOMPARE(parent.testAttribute(Qt::WA_WState_Hidden), true); + + QWidget child; + child.setObjectName("Child"); + QCOMPARE(child.isVisible(), false); + QCOMPARE(child.testAttribute(Qt::WA_WState_ExplicitShowHide), false); + QCOMPARE(child.testAttribute(Qt::WA_WState_Visible), false); + QCOMPARE(child.testAttribute(Qt::WA_WState_Hidden), true); + + child.setParent(&parent); + QCOMPARE(child.isVisible(), false); + QCOMPARE(child.testAttribute(Qt::WA_WState_ExplicitShowHide), false); + QCOMPARE(child.testAttribute(Qt::WA_WState_Visible), false); + QCOMPARE(child.testAttribute(Qt::WA_WState_Hidden), false); + + QFETCH(QList<ShowHideEntry>, entries); + for (const auto entry : entries) { + + if (entry.action == QEvent::Show) { + if (entry.target == Qt::Window && !parent.windowHandle()) { + parent.setAttribute(Qt::WA_NativeWindow); + QVERIFY(parent.windowHandle()); + + QCOMPARE(parent.isVisible(), false); + QCOMPARE(parent.testAttribute(Qt::WA_WState_ExplicitShowHide), false); + QCOMPARE(parent.testAttribute(Qt::WA_WState_Visible), false); + QCOMPARE(parent.testAttribute(Qt::WA_WState_Hidden), true); + } + + bool wasExplicitShowHide = parent.testAttribute(Qt::WA_WState_ExplicitShowHide); + + if (entry.target == Qt::Widget) + parent.show(); + else + parent.windowHandle()->show(); + + QVERIFY(QTest::qWaitForWindowActive(&parent)); + + QCOMPARE(parent.isVisible(), true); + QVERIFY(parent.windowHandle()); + QCOMPARE(parent.windowHandle()->isVisible(), true); + + QCOMPARE(parent.testAttribute(Qt::WA_WState_Visible), true); + QCOMPARE(parent.testAttribute(Qt::WA_WState_Hidden), false); + QCOMPARE(parent.testAttribute(Qt::WA_WState_ExplicitShowHide), + entry.target == Qt::Widget || wasExplicitShowHide); + + QCOMPARE(child.isVisible(), true); + QCOMPARE(child.testAttribute(Qt::WA_WState_ExplicitShowHide), false); + QCOMPARE(child.testAttribute(Qt::WA_WState_Visible), true); + QCOMPARE(child.testAttribute(Qt::WA_WState_Hidden), false); + + } else if (entry.action == QEvent::Hide) { + + bool wasExplicitShowHide = parent.testAttribute(Qt::WA_WState_ExplicitShowHide); + + if (entry.target == Qt::Widget) + parent.hide(); + else + parent.windowHandle()->hide(); + + QCOMPARE(parent.isVisible(), false); + QVERIFY(parent.windowHandle()); + QCOMPARE(parent.windowHandle()->isVisible(), false); + + QCOMPARE(parent.testAttribute(Qt::WA_WState_Visible), false); + QCOMPARE(parent.testAttribute(Qt::WA_WState_Hidden), true); + QCOMPARE(parent.testAttribute(Qt::WA_WState_ExplicitShowHide), + entry.target == Qt::Widget || wasExplicitShowHide); + + QCOMPARE(child.isVisible(), false); + QCOMPARE(child.testAttribute(Qt::WA_WState_ExplicitShowHide), false); + QCOMPARE(child.testAttribute(Qt::WA_WState_Visible), false); + QCOMPARE(child.testAttribute(Qt::WA_WState_Hidden), false); + + } else if (entry.action == QEvent::Close) { + + bool wasExplicitShowHide = parent.testAttribute(Qt::WA_WState_ExplicitShowHide); + + if (entry.target == Qt::Widget) + parent.close(); + else + parent.windowHandle()->close(); + + QCOMPARE(parent.isVisible(), false); + QVERIFY(parent.windowHandle()); + QCOMPARE(parent.windowHandle()->isVisible(), false); + + QCOMPARE(parent.testAttribute(Qt::WA_WState_Visible), false); + QCOMPARE(parent.testAttribute(Qt::WA_WState_Hidden), true); + QCOMPARE(parent.testAttribute(Qt::WA_WState_ExplicitShowHide), + entry.target == Qt::Widget || wasExplicitShowHide); + + QCOMPARE(child.isVisible(), false); + QCOMPARE(child.testAttribute(Qt::WA_WState_ExplicitShowHide), false); + QCOMPARE(child.testAttribute(Qt::WA_WState_Visible), false); + QCOMPARE(child.testAttribute(Qt::WA_WState_Hidden), false); + } + } +} + void tst_QWidget_window::resetFocusObjectOnDestruction() { + if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation)) + QSKIP("QWindow::requestActivate() is not supported."); + QSignalSpy focusObjectChangedSpy(qApp, &QGuiApplication::focusObjectChanged); // single top level widget that has focus @@ -1563,9 +1801,9 @@ void tst_QWidget_window::resetFocusObjectOnDestruction() widget->show(); QVERIFY(QTest::qWaitForWindowActive(widget.get())); - int activeCount = focusObjectChangedSpy.count(); + int activeCount = focusObjectChangedSpy.size(); widget.reset(); - QVERIFY(focusObjectChangedSpy.count() > activeCount); + QVERIFY(focusObjectChangedSpy.size() > activeCount); QCOMPARE(focusObjectChangedSpy.last().last().value<QObject*>(), nullptr); focusObjectChangedSpy.clear(); @@ -1578,12 +1816,37 @@ void tst_QWidget_window::resetFocusObjectOnDestruction() widget->show(); QVERIFY(QTest::qWaitForWindowActive(widget.get())); - activeCount = focusObjectChangedSpy.count(); + activeCount = focusObjectChangedSpy.size(); widget.reset(); // we might get more than one signal emission - QVERIFY(focusObjectChangedSpy.count() > activeCount); + QVERIFY(focusObjectChangedSpy.size() > activeCount); QCOMPARE(focusObjectChangedSpy.last().last().value<QObject*>(), nullptr); } +class CreateDestroyWidget : public QWidget +{ +public: + using QWidget::create; + using QWidget::destroy; +}; + +void tst_QWidget_window::cleanupOnDestruction() +{ + CreateDestroyWidget widget; + QWidget child(&widget); + + QWidget grandChild(&child); + // Ensure there's not a 1:1 native window hierarhcy that we could + // recurse during QWidget::destroy(), triggering the issue that + // we were failing to clean up when not destroyed via QWidget. + grandChild.setAttribute(Qt::WA_DontCreateNativeAncestors); + grandChild.winId(); + + widget.destroy(); + widget.create(); + + widget.show(); +} + QTEST_MAIN(tst_QWidget_window) #include "tst_qwidget_window.moc" diff --git a/tests/auto/widgets/kernel/qwidgetaction/CMakeLists.txt b/tests/auto/widgets/kernel/qwidgetaction/CMakeLists.txt index 58dae2d076..fb5409464d 100644 --- a/tests/auto/widgets/kernel/qwidgetaction/CMakeLists.txt +++ b/tests/auto/widgets/kernel/qwidgetaction/CMakeLists.txt @@ -1,13 +1,20 @@ -# Generated from qwidgetaction.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qwidgetaction Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qwidgetaction LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qwidgetaction SOURCES tst_qwidgetaction.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::Gui Qt::TestPrivate Qt::Widgets diff --git a/tests/auto/widgets/kernel/qwidgetaction/tst_qwidgetaction.cpp b/tests/auto/widgets/kernel/qwidgetaction/tst_qwidgetaction.cpp index 031856e0f9..a06e072b71 100644 --- a/tests/auto/widgets/kernel/qwidgetaction/tst_qwidgetaction.cpp +++ b/tests/auto/widgets/kernel/qwidgetaction/tst_qwidgetaction.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> @@ -222,7 +222,7 @@ void tst_QWidgetAction::customWidget() tb1.addAction(action); QList<QWidget *> combos = action->createdWidgets(); - QCOMPARE(combos.count(), 1); + QCOMPARE(combos.size(), 1); QPointer<QComboBox> combo1 = qobject_cast<QComboBox *>(combos.at(0)); QVERIFY(combo1); @@ -230,7 +230,7 @@ void tst_QWidgetAction::customWidget() tb2.addAction(action); combos = action->createdWidgets(); - QCOMPARE(combos.count(), 2); + QCOMPARE(combos.size(), 2); QCOMPARE(combos.at(0), combo1.data()); QPointer<QComboBox> combo2 = qobject_cast<QComboBox *>(combos.at(1)); diff --git a/tests/auto/widgets/kernel/qwidgetmetatype/CMakeLists.txt b/tests/auto/widgets/kernel/qwidgetmetatype/CMakeLists.txt index 3d8c3bbf4b..431a584a60 100644 --- a/tests/auto/widgets/kernel/qwidgetmetatype/CMakeLists.txt +++ b/tests/auto/widgets/kernel/qwidgetmetatype/CMakeLists.txt @@ -1,13 +1,20 @@ -# Generated from qwidgetmetatype.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qwidgetmetatype Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qwidgetmetatype LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qwidgetmetatype SOURCES tst_qwidgetmetatype.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::Gui Qt::Widgets ) diff --git a/tests/auto/widgets/kernel/qwidgetmetatype/tst_qwidgetmetatype.cpp b/tests/auto/widgets/kernel/qwidgetmetatype/tst_qwidgetmetatype.cpp index da417b377e..885c26a128 100644 --- a/tests/auto/widgets/kernel/qwidgetmetatype/tst_qwidgetmetatype.cpp +++ b/tests/auto/widgets/kernel/qwidgetmetatype/tst_qwidgetmetatype.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2012 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Stephen Kelly <stephen.kelly@kdab.com> -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> diff --git a/tests/auto/widgets/kernel/qwidgetrepaintmanager/CMakeLists.txt b/tests/auto/widgets/kernel/qwidgetrepaintmanager/CMakeLists.txt index a84c774beb..ae91af064c 100644 --- a/tests/auto/widgets/kernel/qwidgetrepaintmanager/CMakeLists.txt +++ b/tests/auto/widgets/kernel/qwidgetrepaintmanager/CMakeLists.txt @@ -1,7 +1,16 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qwidgetrepaintmanager LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qwidgetrepaintmanager SOURCES tst_qwidgetrepaintmanager.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::CorePrivate Qt::Gui Qt::GuiPrivate diff --git a/tests/auto/widgets/kernel/qwidgetrepaintmanager/tst_qwidgetrepaintmanager.cpp b/tests/auto/widgets/kernel/qwidgetrepaintmanager/tst_qwidgetrepaintmanager.cpp index fb959821bc..64ebeb08b0 100644 --- a/tests/auto/widgets/kernel/qwidgetrepaintmanager/tst_qwidgetrepaintmanager.cpp +++ b/tests/auto/widgets/kernel/qwidgetrepaintmanager/tst_qwidgetrepaintmanager.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> @@ -11,7 +11,9 @@ #include <private/qhighdpiscaling_p.h> #include <private/qwidget_p.h> #include <private/qwidgetrepaintmanager_p.h> +#include <qpa/qplatformintegration.h> #include <qpa/qplatformbackingstore.h> +#include <private/qguiapplication_p.h> //#define MANUAL_DEBUG @@ -33,14 +35,33 @@ public: void initialShow() { show(); - if (isWindow()) + if (isWindow()) { QVERIFY(QTest::qWaitForWindowExposed(this)); + QVERIFY(waitForPainted()); + } paintedRegions = {}; } bool waitForPainted(int timeout = 5000) { - return QTest::qWaitFor([this]{ return !paintedRegions.isEmpty(); }, timeout); + int remaining = timeout; + QDeadlineTimer deadline(remaining, Qt::PreciseTimer); + if (!QTest::qWaitFor([this]{ return !paintedRegions.isEmpty(); }, timeout)) + return false; + + // In case of multiple paint events: + // Process events and wait until all have been consumed, + // i.e. paintedRegions no longer changes. + QRegion reg; + while (remaining > 0 && reg != paintedRegions) { + reg = paintedRegions; + QCoreApplication::processEvents(QEventLoop::AllEvents, remaining); + if (reg == paintedRegions) + return true; + + remaining = int(deadline.remainingTime()); + } + return false; } QRegion takePaintedRegions() @@ -51,6 +72,17 @@ public: } QRegion paintedRegions; + bool event(QEvent *event) override + { + const auto type = event->type(); + if (type == QEvent::WindowActivate || type == QEvent::WindowDeactivate) + return true; + if (type == QEvent::UpdateRequest) + ++updateRequests; + return QWidget::event(event); + } + int updateRequests = 0; + protected: void paintEvent(QPaintEvent *event) override { @@ -71,6 +103,14 @@ public: setAttribute(Qt::WA_OpaquePaintEvent); } + bool event(QEvent *event) override + { + const auto type = event->type(); + if (type == QEvent::WindowActivate || type == QEvent::WindowDeactivate) + return true; + return QWidget::event(event); + } + protected: void paintEvent(QPaintEvent *e) override { @@ -186,6 +226,14 @@ public: QSize sizeHint() const override { return QSize(400, 400); } + bool event(QEvent *event) override + { + const auto type = event->type(); + if (type == QEvent::WindowActivate || type == QEvent::WindowDeactivate) + return true; + return QWidget::event(event); + } + protected: void resizeEvent(QResizeEvent *) override { @@ -211,6 +259,9 @@ private slots: void opaqueChildren(); void staticContents(); void scroll(); + void paintOnScreenUpdates(); + void evaluateRhi(); + #if defined(QT_BUILD_INTERNAL) void scrollWithOverlap(); void overlappedRegion(); @@ -226,13 +277,14 @@ protected: */ bool compareWidget(QWidget *w) { + QBackingStore *backingStore = w->window()->backingStore(); + Q_ASSERT(backingStore && backingStore->handle()); + QPlatformBackingStore *platformBackingStore = backingStore->handle(); + if (!waitForFlush(w)) { qWarning() << "Widget" << w << "failed to flush"; return false; } - QBackingStore *backingStore = w->window()->backingStore(); - Q_ASSERT(backingStore && backingStore->handle()); - QPlatformBackingStore *platformBackingStore = backingStore->handle(); QImage backingstoreContent = platformBackingStore->toImage(); if (!w->isWindow()) { @@ -259,7 +311,14 @@ protected: } bool waitForFlush(QWidget *widget) const { + if (!widget) + return true; + auto *repaintManager = QWidgetPrivate::get(widget->window())->maybeRepaintManager(); + + if (!repaintManager) + return true; + return QTest::qWaitFor([repaintManager]{ return !repaintManager->isDirty(); } ); }; #endif // QT_BUILD_INTERNAL @@ -282,7 +341,7 @@ void tst_QWidgetRepaintManager::initTestCase() QVERIFY(QTest::qWaitForWindowExposed(&widget)); m_implementsScroll = widget.backingStore()->handle()->scroll(QRegion(widget.rect()), 1, 1); - qDebug() << QGuiApplication::platformName() << "QPA backend implements scroll:" << m_implementsScroll; + qInfo() << QGuiApplication::platformName() << "QPA backend implements scroll:" << m_implementsScroll; } void tst_QWidgetRepaintManager::cleanup() @@ -321,6 +380,7 @@ void tst_QWidgetRepaintManager::children() TestWidget *child1 = new TestWidget(&widget); child1->move(20, 20); child1->show(); + QVERIFY(QTest::qWaitForWindowExposed(child1)); QVERIFY(child1->waitForPainted()); QCOMPARE(widget.takePaintedRegions(), QRegion(child1->geometry())); QCOMPARE(child1->takePaintedRegions(), QRegion(child1->rect())); @@ -377,16 +437,26 @@ void tst_QWidgetRepaintManager::opaqueChildren() */ void tst_QWidgetRepaintManager::staticContents() { + const auto *integration = QGuiApplicationPrivate::platformIntegration(); + if (!integration->hasCapability(QPlatformIntegration::BackingStoreStaticContents)) + QSKIP("Platform does not support static backingstore content"); + TestWidget widget; widget.setAttribute(Qt::WA_StaticContents); widget.initialShow(); - const QSize oldSize = widget.size(); + // Trigger resize via QWindow (similar to QWSI code path) + QVERIFY(widget.windowHandle()); + QSize oldSize = widget.size(); + widget.windowHandle()->resize(widget.width(), widget.height() + 10); + QVERIFY(widget.waitForPainted()); + QCOMPARE(widget.takePaintedRegions(), QRegion(0, oldSize.width(), widget.width(), 10)); + // Trigger resize via QWidget + oldSize = widget.size(); widget.resize(widget.width() + 10, widget.height()); - QVERIFY(widget.waitForPainted()); - QEXPECT_FAIL("", "This should just repaint the newly exposed region", Continue); + QEXPECT_FAIL("", "QWidgetPrivate::setGeometry_sys wrongly triggers full update", Continue); QCOMPARE(widget.takePaintedRegions(), QRegion(oldSize.width(), 0, 10, widget.height())); } @@ -427,6 +497,306 @@ void tst_QWidgetRepaintManager::scroll() QCOMPARE(widget.takePaintedRegions(), QRegion()); } +class PaintOnScreenWidget : public TestWidget +{ +public: + using TestWidget::TestWidget; + + // Explicit override to prevent noPaintOnScreen on Windows + QPaintEngine *paintEngine() const override + { + return nullptr; + } +}; + +void tst_QWidgetRepaintManager::paintOnScreenUpdates() +{ + { + TestWidget topLevel; + topLevel.setObjectName("TopLevel"); + topLevel.resize(500, 500); + TestWidget renderToTextureWidget(&topLevel); + renderToTextureWidget.setObjectName("RenderToTexture"); + renderToTextureWidget.setGeometry(0, 0, 200, 200); + QWidgetPrivate::get(&renderToTextureWidget)->setRenderToTexture(); + + PaintOnScreenWidget paintOnScreenWidget(&topLevel); + paintOnScreenWidget.setObjectName("PaintOnScreen"); + paintOnScreenWidget.setGeometry(200, 200, 300, 300); + + topLevel.initialShow(); + + // Updating before toggling WA_PaintOnScreen should work fine + paintOnScreenWidget.update(); + paintOnScreenWidget.waitForPainted(); + QVERIFY(paintOnScreenWidget.waitForPainted()); + +#ifdef Q_OS_ANDROID + QEXPECT_FAIL("", "This test fails on Android", Abort); +#endif + QCOMPARE(paintOnScreenWidget.takePaintedRegions(), paintOnScreenWidget.rect()); + + renderToTextureWidget.update(); + QVERIFY(renderToTextureWidget.waitForPainted()); + QCOMPARE(renderToTextureWidget.takePaintedRegions(), renderToTextureWidget.rect()); + + // Then toggle WA_PaintOnScreen + paintOnScreenWidget.setAttribute(Qt::WA_PaintOnScreen); + + // The render-to-texture widget updates fine + renderToTextureWidget.update(); + QVERIFY(renderToTextureWidget.waitForPainted()); + QCOMPARE(renderToTextureWidget.takePaintedRegions(), renderToTextureWidget.rect()); + + // Updating the paint-on-screen texture widget will not result + // in a paint event, but should result in an update request. + paintOnScreenWidget.updateRequests = 0; + paintOnScreenWidget.update(); + QVERIFY(QTest::qWaitFor([&]{ return paintOnScreenWidget.updateRequests > 0; })); + + // And should not prevent the render-to-texture widget from receiving updates + renderToTextureWidget.update(); + QVERIFY(renderToTextureWidget.waitForPainted()); + QCOMPARE(renderToTextureWidget.takePaintedRegions(), renderToTextureWidget.rect()); + } + + { + TestWidget paintOnScreenTopLevel; + paintOnScreenTopLevel.setObjectName("PaintOnScreenTopLevel"); + paintOnScreenTopLevel.setAttribute(Qt::WA_PaintOnScreen); + + paintOnScreenTopLevel.initialShow(); + + paintOnScreenTopLevel.updateRequests = 0; + paintOnScreenTopLevel.update(); + QVERIFY(QTest::qWaitFor([&]{ return paintOnScreenTopLevel.updateRequests > 0; })); + + // Turn off paint on screen and make it a render-to-texture widget. + // This will lead us into a QWidgetRepaintManager::markDirty() code + // path that checks updateRequestSent, which is still set from the + // update above since paint-on-screen handling doesn't reset it. + paintOnScreenTopLevel.setAttribute(Qt::WA_PaintOnScreen, false); + QWidgetPrivate::get(&paintOnScreenTopLevel)->setRenderToTexture(); + paintOnScreenTopLevel.update(); + QVERIFY(QTest::qWaitFor([&]{ return paintOnScreenTopLevel.updateRequests > 1; })); + } +} + +class RhiWidgetPrivate : public QWidgetPrivate +{ +public: + RhiWidgetPrivate(const QPlatformBackingStoreRhiConfig &config) + : config(config) + { + } + + QPlatformBackingStoreRhiConfig rhiConfig() const override + { + return config; + } + + QPlatformBackingStoreRhiConfig config = QPlatformBackingStoreRhiConfig::Null; +}; + +class RhiWidget : public QWidget +{ +public: + RhiWidget(const QPlatformBackingStoreRhiConfig &config = QPlatformBackingStoreRhiConfig::Null, QWidget *parent = nullptr) + : QWidget(*new RhiWidgetPrivate(config), parent, {}) + { + } +}; + +void tst_QWidgetRepaintManager::evaluateRhi() +{ + const auto *integration = QGuiApplicationPrivate::platformIntegration(); + if (!integration->hasCapability(QPlatformIntegration::RhiBasedRendering)) + QSKIP("Platform does not support RHI based rendering"); + + // We need full control over whether widgets are native or not + const bool nativeSiblingsOriginal = qApp->testAttribute(Qt::AA_DontCreateNativeWidgetSiblings); + qApp->setAttribute(Qt::AA_DontCreateNativeWidgetSiblings, true); + auto nativeSiblingGuard = qScopeGuard([&]{ + qApp->setAttribute(Qt::AA_DontCreateNativeWidgetSiblings, nativeSiblingsOriginal); + }); + + auto defaultSurfaceType = QSurface::RasterSurface; + bool usesRhiBackingStore = false; + + { + // Plain QWidget doesn't enable RHI + QWidget regularWidget; + regularWidget.show(); + QVERIFY(QTest::qWaitForWindowExposed(®ularWidget)); + QVERIFY(!QWidgetPrivate::get(®ularWidget)->usesRhiFlush); + + // The platform might use a non-raster surface type if it uses + // an RHI backingstore by default (e.g. Android, iOS, QNX). + defaultSurfaceType = regularWidget.windowHandle()->surfaceType(); + + // Record whether the platform uses an RHI backingstore, + // so we can opt out of some tests further down. + if (defaultSurfaceType != QSurface::RasterSurface) + usesRhiBackingStore = QWidgetPrivate::get(®ularWidget)->rhi(); + else + QVERIFY(!QWidgetPrivate::get(®ularWidget)->rhi()); + } + + { + // But a top level RHI widget does + RhiWidget rhiWidget; + rhiWidget.show(); + QVERIFY(QTest::qWaitForWindowExposed(&rhiWidget)); + QVERIFY(QWidgetPrivate::get(&rhiWidget)->usesRhiFlush); + QVERIFY(QWidgetPrivate::get(&rhiWidget)->rhi()); + } + +#if QT_CONFIG(opengl) + { + // Non-native child RHI widget enables RHI for top level regular widget + QWidget topLevel; + RhiWidget rhiWidget(QPlatformBackingStoreRhiConfig::OpenGL, &topLevel); + topLevel.show(); + QVERIFY(QTest::qWaitForWindowExposed(&topLevel)); + QCOMPARE(topLevel.windowHandle()->surfaceType(), QSurface::OpenGLSurface); + QVERIFY(QWidgetPrivate::get(&topLevel)->usesRhiFlush); + QVERIFY(QWidgetPrivate::get(&topLevel)->rhi()); + // Only the native widget that actually flushes will report usesRhiFlush + QVERIFY(!QWidgetPrivate::get(&rhiWidget)->usesRhiFlush); + // But it should have an RHI it can use + QVERIFY(QWidgetPrivate::get(&rhiWidget)->rhi()); + } + + { + // Native child RHI widget does not enable RHI for top level + QWidget topLevel; + RhiWidget nativeRhiWidget(QPlatformBackingStoreRhiConfig::OpenGL, &topLevel); + nativeRhiWidget.setAttribute(Qt::WA_NativeWindow); + topLevel.show(); + QVERIFY(QTest::qWaitForWindowExposed(&topLevel)); + QCOMPARE(nativeRhiWidget.windowHandle()->surfaceType(), QSurface::OpenGLSurface); + QVERIFY(QWidgetPrivate::get(&nativeRhiWidget)->usesRhiFlush); + QVERIFY(QWidgetPrivate::get(&nativeRhiWidget)->rhi()); + QCOMPARE(topLevel.windowHandle()->surfaceType(), defaultSurfaceType); + QVERIFY(!QWidgetPrivate::get(&topLevel)->usesRhiFlush); + + if (!usesRhiBackingStore) + QVERIFY(!QWidgetPrivate::get(&topLevel)->rhi()); + } + + { + // Non-native RHI child of native child enables RHI for native child, + // but not top level. + QWidget topLevel; + QWidget nativeChild(&topLevel); + nativeChild.setAttribute(Qt::WA_NativeWindow); + RhiWidget rhiWidget(QPlatformBackingStoreRhiConfig::OpenGL, &nativeChild); + topLevel.show(); + QVERIFY(QTest::qWaitForWindowExposed(&topLevel)); + + QCOMPARE(nativeChild.windowHandle()->surfaceType(), QSurface::OpenGLSurface); + QVERIFY(QWidgetPrivate::get(&nativeChild)->usesRhiFlush); + QVERIFY(QWidgetPrivate::get(&nativeChild)->rhi()); + QVERIFY(!QWidgetPrivate::get(&rhiWidget)->usesRhiFlush); + QVERIFY(QWidgetPrivate::get(&rhiWidget)->rhi()); + QCOMPARE(topLevel.windowHandle()->surfaceType(), defaultSurfaceType); + QVERIFY(!QWidgetPrivate::get(&topLevel)->usesRhiFlush); + if (!usesRhiBackingStore) + QVERIFY(!QWidgetPrivate::get(&topLevel)->rhi()); + } + + { + // Native child RHI widget does not prevent RHI for top level + // if non-native RHI child widget is also present. + QWidget topLevel; + RhiWidget rhiWidget(QPlatformBackingStoreRhiConfig::OpenGL, &topLevel); + RhiWidget nativeRhiWidget(QPlatformBackingStoreRhiConfig::OpenGL, &topLevel); + nativeRhiWidget.setAttribute(Qt::WA_NativeWindow); + topLevel.show(); + QVERIFY(QTest::qWaitForWindowExposed(&topLevel)); + + QCOMPARE(nativeRhiWidget.windowHandle()->surfaceType(), QSurface::OpenGLSurface); + QVERIFY(QWidgetPrivate::get(&nativeRhiWidget)->usesRhiFlush); + QVERIFY(QWidgetPrivate::get(&nativeRhiWidget)->rhi()); + QVERIFY(!QWidgetPrivate::get(&rhiWidget)->usesRhiFlush); + QVERIFY(QWidgetPrivate::get(&rhiWidget)->rhi()); + QCOMPARE(topLevel.windowHandle()->surfaceType(), QSurface::OpenGLSurface); + QVERIFY(QWidgetPrivate::get(&topLevel)->usesRhiFlush); + QVERIFY(QWidgetPrivate::get(&topLevel)->rhi()); + } + + { + // Reparenting into a window that already matches the required + // surface type should still mark the parent as flushing with RHI. + QWidget topLevel; + + RhiWidget rhiWidget(QPlatformBackingStoreRhiConfig::Null); + rhiWidget.show(); + QVERIFY(QTest::qWaitForWindowExposed(&rhiWidget)); + QVERIFY(QWidgetPrivate::get(&rhiWidget)->usesRhiFlush); + QVERIFY(QWidgetPrivate::get(&rhiWidget)->rhi()); + + topLevel.show(); + QVERIFY(QTest::qWaitForWindowExposed(&topLevel)); + rhiWidget.setParent(&topLevel); + QVERIFY(QWidgetPrivate::get(&topLevel)->usesRhiFlush); + QVERIFY(QWidgetPrivate::get(&topLevel)->rhi()); + } + + { + // Non-native RHI child of native child enables RHI for native child, + // but does not prevent top level from flushing with RHI. + QWidget topLevel; + QWidget nativeChild(&topLevel); + nativeChild.setAttribute(Qt::WA_NativeWindow); + RhiWidget rhiGranchild(QPlatformBackingStoreRhiConfig::OpenGL, &nativeChild); + RhiWidget rhiChild(QPlatformBackingStoreRhiConfig::OpenGL, &topLevel); + topLevel.show(); + QVERIFY(QTest::qWaitForWindowExposed(&topLevel)); + + QCOMPARE(nativeChild.windowHandle()->surfaceType(), QSurface::OpenGLSurface); + QVERIFY(QWidgetPrivate::get(&nativeChild)->usesRhiFlush); + QVERIFY(QWidgetPrivate::get(&nativeChild)->rhi()); + QVERIFY(!QWidgetPrivate::get(&rhiGranchild)->usesRhiFlush); + QVERIFY(QWidgetPrivate::get(&rhiGranchild)->rhi()); + QCOMPARE(topLevel.windowHandle()->surfaceType(), QSurface::OpenGLSurface); + QVERIFY(QWidgetPrivate::get(&topLevel)->usesRhiFlush); + QVERIFY(QWidgetPrivate::get(&topLevel)->rhi()); + QVERIFY(!QWidgetPrivate::get(&rhiChild)->usesRhiFlush); + QVERIFY(QWidgetPrivate::get(&rhiChild)->rhi()); + } + +#if QT_CONFIG(metal) + QRhiMetalInitParams metalParams; + if (QRhi::probe(QRhi::Metal, &metalParams)) { + // Native RHI childen allows mixing RHI backends + QWidget topLevel; + RhiWidget openglWidget(QPlatformBackingStoreRhiConfig::OpenGL, &topLevel); + openglWidget.setAttribute(Qt::WA_NativeWindow); + RhiWidget metalWidget(QPlatformBackingStoreRhiConfig::Metal, &topLevel); + metalWidget.setAttribute(Qt::WA_NativeWindow); + topLevel.show(); + QVERIFY(QTest::qWaitForWindowExposed(&topLevel)); + + QCOMPARE(topLevel.windowHandle()->surfaceType(), defaultSurfaceType); + QVERIFY(!QWidgetPrivate::get(&topLevel)->usesRhiFlush); + if (!usesRhiBackingStore) + QVERIFY(!QWidgetPrivate::get(&topLevel)->rhi()); + + QCOMPARE(openglWidget.windowHandle()->surfaceType(), QSurface::OpenGLSurface); + QVERIFY(QWidgetPrivate::get(&openglWidget)->usesRhiFlush); + QVERIFY(QWidgetPrivate::get(&openglWidget)->rhi()); + + QCOMPARE(metalWidget.windowHandle()->surfaceType(), QSurface::MetalSurface); + QVERIFY(QWidgetPrivate::get(&metalWidget)->usesRhiFlush); + QVERIFY(QWidgetPrivate::get(&metalWidget)->rhi()); + + QVERIFY(QWidgetPrivate::get(&openglWidget)->rhi() != QWidgetPrivate::get(&metalWidget)->rhi()); + } +#endif // QT_CONFIG(metal) + +#endif // QT_CONFIG(opengl) +} #if defined(QT_BUILD_INTERNAL) @@ -447,6 +817,8 @@ void tst_QWidgetRepaintManager::scrollWithOverlap() : QWidget(parent, Qt::WindowStaysOnTopHint) { m_scrollArea = new QScrollArea(this); + m_scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + m_scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); QWidget *w = new QWidget; w->setPalette(QPalette(Qt::gray)); w->setAutoFillBackground(true); @@ -594,6 +966,7 @@ void tst_QWidgetRepaintManager::fastMove() QCOMPARE(dirtyRegion(scene.yellowChild), QRect(0, 0, 100, 100)); } QCOMPARE(dirtyRegion(&scene), QRect(0, 0, 25, 100)); + QTRY_VERIFY(dirtyRegion(&scene).isEmpty()); QVERIFY(compareWidget(&scene)); } diff --git a/tests/auto/widgets/kernel/qwidgetsvariant/CMakeLists.txt b/tests/auto/widgets/kernel/qwidgetsvariant/CMakeLists.txt index f387be7306..bb3c1e2ad6 100644 --- a/tests/auto/widgets/kernel/qwidgetsvariant/CMakeLists.txt +++ b/tests/auto/widgets/kernel/qwidgetsvariant/CMakeLists.txt @@ -1,15 +1,22 @@ -# Generated from qwidgetsvariant.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qwidgetsvariant Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qwidgetsvariant LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qwidgetsvariant SOURCES tst_qwidgetsvariant.cpp INCLUDE_DIRECTORIES ../../../other/qvariant_common - PUBLIC_LIBRARIES + LIBRARIES Qt::Gui Qt::Widgets ) diff --git a/tests/auto/widgets/kernel/qwidgetsvariant/tst_qwidgetsvariant.cpp b/tests/auto/widgets/kernel/qwidgetsvariant/tst_qwidgetsvariant.cpp index bb30f3fd43..0a03fb9e1d 100644 --- a/tests/auto/widgets/kernel/qwidgetsvariant/tst_qwidgetsvariant.cpp +++ b/tests/auto/widgets/kernel/qwidgetsvariant/tst_qwidgetsvariant.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> diff --git a/tests/auto/widgets/kernel/qwindowcontainer/CMakeLists.txt b/tests/auto/widgets/kernel/qwindowcontainer/CMakeLists.txt index 63a3f9632d..787505972f 100644 --- a/tests/auto/widgets/kernel/qwindowcontainer/CMakeLists.txt +++ b/tests/auto/widgets/kernel/qwindowcontainer/CMakeLists.txt @@ -1,13 +1,20 @@ -# Generated from qwindowcontainer.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qwindowcontainer Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qwindowcontainer LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qwindowcontainer SOURCES tst_qwindowcontainer.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::Gui Qt::Widgets ) diff --git a/tests/auto/widgets/kernel/qwindowcontainer/tst_qwindowcontainer.cpp b/tests/auto/widgets/kernel/qwindowcontainer/tst_qwindowcontainer.cpp index 8d0f84ca60..52aaf094b4 100644 --- a/tests/auto/widgets/kernel/qwindowcontainer/tst_qwindowcontainer.cpp +++ b/tests/auto/widgets/kernel/qwindowcontainer/tst_qwindowcontainer.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> @@ -7,12 +7,14 @@ #include <qapplication.h> #include <qwindow.h> #include <qwidget.h> +#include <qlineedit.h> #include <qdockwidget.h> #include <qmainwindow.h> #include <qscreen.h> #include <qscopedpointer.h> #include <qevent.h> +#include <qboxlayout.h> class Window : public QWindow @@ -46,16 +48,20 @@ public: private slots: void testShow(); void testPositionAndSize(); + void testSizeHints(); void testExposeObscure(); void testOwnership(); void testBehindTheScenesDeletion(); void testUnparenting(); + void testReparenting(); void testUnparentReparent(); void testActivation(); void testAncestorChange(); void testDockWidget(); void testNativeContainerParent(); void testPlatformSurfaceEvent(); + void embedWidgetWindow(); + void testFocus(); void cleanup(); private: @@ -107,7 +113,29 @@ void tst_QWindowContainer::testPositionAndSize() QCOMPARE(window->height(), container->height()); } +void tst_QWindowContainer::testSizeHints() +{ + QScopedPointer<QWidget> tlw(new QWidget); + QWindow *window = new QWindow(); + window->setMinimumSize(QSize(200, 200)); + window->setGeometry(m_availableGeometry.x() + 300, m_availableGeometry.y() + 400, 500, 600); + + QScopedPointer<QWidget> container(QWidget::createWindowContainer(window)); + container->setWindowTitle(QTest::currentTestFunction()); + + QVBoxLayout *vbox = new QVBoxLayout(tlw.data()); + vbox->addWidget(container.data()); + vbox->setContentsMargins(0, 0, 0, 0); + // Size hints should work regardless of visibility + QCOMPARE(container->minimumSizeHint(), window->minimumSize()); + QCOMPARE(vbox->minimumSize(), window->minimumSize()); + + // Respect dynamic updates + window->setMinimumSize(QSize(210, 210)); + QCOMPARE(container->minimumSizeHint(), window->minimumSize()); + QCOMPARE(vbox->minimumSize(), window->minimumSize()); +} void tst_QWindowContainer::testExposeObscure() { @@ -206,12 +234,12 @@ void tst_QWindowContainer::testActivation() void tst_QWindowContainer::testUnparenting() { - QWindow *window = new QWindow(); + QPointer<QWindow> window(new QWindow()); QScopedPointer<QWidget> container(QWidget::createWindowContainer(window)); container->setWindowTitle(QTest::currentTestFunction()); container->setGeometry(m_availableGeometry.x() + 100, m_availableGeometry.y() + 100, 200, 100); - window->setParent(0); + window->setParent(nullptr); container->show(); @@ -219,6 +247,26 @@ void tst_QWindowContainer::testUnparenting() // Window should not be made visible by container.. QVERIFY(!window->isVisible()); + + container.reset(); + QVERIFY(window); + delete window; +} + +void tst_QWindowContainer::testReparenting() +{ + QPointer<QWindow> window1(new QWindow()); + QScopedPointer<QWindow> window2(new QWindow()); + QScopedPointer<QWidget> container(QWidget::createWindowContainer(window1)); + + window1->setParent(window2.data()); + + // Not deleted with container + container.reset(); + QVERIFY(window1); + // but deleted with new parent + window2.reset(); + QVERIFY(!window1); } void tst_QWindowContainer::testUnparentReparent() @@ -389,6 +437,99 @@ void tst_QWindowContainer::testPlatformSurfaceEvent() QVERIFY(ok); } +void tst_QWindowContainer::embedWidgetWindow() +{ + { + QWidget parent; + QWidget *widget = new QWidget; + widget->show(); + QVERIFY(QTest::qWaitForWindowExposed(widget)); + QVERIFY(widget->windowHandle()); + QPointer<QWindow> widgetWindow = widget->windowHandle(); + auto *container = QWidget::createWindowContainer(widgetWindow, &parent); + QCOMPARE(container, widget); + QCOMPARE(widget->parent(), &parent); + delete widget; + QTRY_VERIFY(widgetWindow.isNull()); + } + + QPointer<QWidget> widget = new QWidget; + QPointer<QWindow> widgetWindow; + { + QWidget parent; + widget->show(); + QVERIFY(QTest::qWaitForWindowExposed(widget)); + QVERIFY(widget->windowHandle()); + widgetWindow = widget->windowHandle(); + auto *container = QWidget::createWindowContainer(widgetWindow, &parent); + QCOMPARE(container, widget); + QCOMPARE(widget->parent(), &parent); + } + QTRY_VERIFY(widget.isNull()); + QTRY_VERIFY(widgetWindow.isNull()); + +} + +void tst_QWindowContainer::testFocus() +{ + QWidget root; + root.setGeometry(m_availableGeometry); + + QLineEdit *lineEdit = new QLineEdit(&root); + lineEdit->setGeometry(0, 0, m_availableGeometry.width() * 0.1, 17); + lineEdit->setFocusPolicy(Qt::FocusPolicy::StrongFocus); + + QWindow *embedded = new QWindow(); + QWidget *container = QWidget::createWindowContainer(embedded, &root); + container->setGeometry(0, lineEdit->height() + 10, m_availableGeometry.width() * 0.2, m_availableGeometry.height() - (lineEdit->height() + 10)); + container->setFocusPolicy(Qt::StrongFocus); + + root.show(); + QVERIFY(QTest::qWaitForWindowExposed(&root)); + lineEdit->setFocus(); + QTRY_VERIFY(lineEdit->hasFocus()); + QCOMPARE(QGuiApplication::focusWindow(), root.windowHandle()); + QCOMPARE(QApplication::focusWidget(), lineEdit); + + // embedded window gets focused because of mouse click + QPoint embeddedCenter = container->rect().center(); + QTest::mousePress(root.windowHandle(), Qt::LeftButton, {}, embeddedCenter); + QVERIFY(QTest::qWaitForWindowFocused(embedded)); + QVERIFY(container->hasFocus()); + QCOMPARE(QGuiApplication::focusWindow(), embedded); + QCOMPARE(QApplication::focusWidget(), container); + QVERIFY(!lineEdit->hasFocus()); + + QTest::mouseClick(lineEdit, Qt::LeftButton, {}); + QVERIFY(QTest::qWaitForWindowFocused(root.windowHandle())); + QCOMPARE(QGuiApplication::focusWindow(), root.windowHandle()); + QCOMPARE(QApplication::focusWidget(), lineEdit); + QVERIFY(lineEdit->hasFocus()); + + // embedded window gets focused because of Tab key event + QTest::keyClick(root.windowHandle(), Qt::Key_Tab); + QVERIFY(QTest::qWaitForWindowFocused(embedded)); + QVERIFY(container->hasFocus()); + QCOMPARE(QGuiApplication::focusWindow(), embedded); + QCOMPARE(QApplication::focusWidget(), container); + QVERIFY(!lineEdit->hasFocus()); + // A key tab event sent to the root window should cause + // the nextInFocusChain of the window container to get focused + QTest::keyClick(root.windowHandle(), Qt::Key_Tab); + QVERIFY(QTest::qWaitForWindowFocused(root.windowHandle())); + QCOMPARE(QGuiApplication::focusWindow(), root.windowHandle()); + QCOMPARE(QApplication::focusWidget(), lineEdit); + QVERIFY(lineEdit->hasFocus()); + + // embedded window gets focused programmatically + embedded->requestActivate(); + QVERIFY(QTest::qWaitForWindowFocused(embedded)); + QVERIFY(container->hasFocus()); + QCOMPARE(QGuiApplication::focusWindow(), embedded); + QCOMPARE(QApplication::focusWidget(), container); + QVERIFY(!lineEdit->hasFocus()); +} + QTEST_MAIN(tst_QWindowContainer) #include "tst_qwindowcontainer.moc" |