diff options
Diffstat (limited to 'tests/auto/widgets/widgets/qdockwidget/tst_qdockwidget.cpp')
-rw-r--r-- | tests/auto/widgets/widgets/qdockwidget/tst_qdockwidget.cpp | 348 |
1 files changed, 283 insertions, 65 deletions
diff --git a/tests/auto/widgets/widgets/qdockwidget/tst_qdockwidget.cpp b/tests/auto/widgets/widgets/qdockwidget/tst_qdockwidget.cpp index 35a05f768a..af62d0d089 100644 --- a/tests/auto/widgets/widgets/qdockwidget/tst_qdockwidget.cpp +++ b/tests/auto/widgets/widgets/qdockwidget/tst_qdockwidget.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> #include <QSignalSpy> @@ -10,6 +10,7 @@ #include "private/qmainwindowlayout_p.h" #include <QAbstractButton> #include <qlineedit.h> +#include <QtGui/qpa/qplatformwindow.h> #include <qtabbar.h> #include <QScreen> #include <QTimer> @@ -65,14 +66,20 @@ private slots: // Dock area permissions for DockWidgets and DockWidgetGroupWindows void dockPermissions(); - // test floating tabs and item_tree consistency + // test floating tabs, item_tree and window title consistency void floatingTabs(); + void hoverWithoutDrop(); + + // floating tab gets removed, when last child goes away + void deleteFloatingTabWithSingleDockWidget_data(); + void deleteFloatingTabWithSingleDockWidget(); // test hide & show void hideAndShow(); // test closing and deleting consistency void closeAndDelete(); + void closeUnclosable(); // test save and restore consistency void saveAndRestore(); @@ -80,9 +87,15 @@ private slots: private: // helpers and consts for dockPermissions, hideAndShow, closeAndDelete #ifdef QT_BUILD_INTERNAL - void createTestWidgets(QMainWindow* &MainWindow, QPointer<QWidget> ¢, QPointer<QDockWidget> &d1, QPointer<QDockWidget> &d2) const; + void createTestWidgets(QMainWindow* &MainWindow, QPointer<QWidget> ¢, + QPointer<QDockWidget> &d1, QPointer<QDockWidget> &d2) const; + void unplugAndResize(QMainWindow* MainWindow, QDockWidget* dw, QPoint home, QSize size) const; + void createFloatingTabs(QMainWindow* &MainWindow, QPointer<QWidget> ¢, + QPointer<QDockWidget> &d1, QPointer<QDockWidget> &d2, + QList<int> &path1, QList<int> &path2) const; + static inline QPoint dragPoint(QDockWidget* dockWidget); static inline QPoint home1(QMainWindow* MainWindow) { return MainWindow->mapToGlobal(MainWindow->rect().topLeft() + QPoint(0.1 * MainWindow->width(), 0.1 * MainWindow->height())); } @@ -102,12 +115,27 @@ private: bool checkFloatingTabs(QMainWindow* MainWindow, QPointer<QDockWidgetGroupWindow> &ftabs, const QList<QDockWidget*> &dwList = {}) const; // move a dock widget - void moveDockWidget(QDockWidget* dw, QPoint to, QPoint from = QPoint()) const; + enum class MoveDockWidgetRule { + Drop, + Abort + }; + + void moveDockWidget(QDockWidget* dw, QPoint to, QPoint from, MoveDockWidgetRule rule) const; +#ifdef QT_BUILD_INTERNAL // Message handling for xcb error QTBUG 82059 static void xcbMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg); + + enum class ChildRemovalReason { + Destroyed, + Closed, + Reparented + }; + public: bool xcbError = false; + bool platformSupportingRaise = true; +#endif private: #ifdef QT_DEBUG @@ -128,11 +156,6 @@ private: }; -// Statics for xcb error / msg handler -static tst_QDockWidget *qThis = nullptr; -static void (*oldMessageHandler)(QtMsgType, const QMessageLogContext&, const QString&); -#define QXCBVERIFY(cond) do { if (xcbError) QSKIP("Test skipped due to XCB error"); QVERIFY(cond); } while (0) - // Testing get/set functions void tst_QDockWidget::getSetCheck() { @@ -675,6 +698,9 @@ void tst_QDockWidget::updateTabBarOnVisibilityChanged() mw.tabifyDockWidget(dw1, dw2); mw.tabifyDockWidget(dw2, dw3); + const auto list1 = QList<QDockWidget *>{dw1, dw2, dw3}; + QCOMPARE(mw.tabifiedDockWidgets(dw0), list1); + QTabBar *tabBar = mw.findChild<QTabBar *>(); QVERIFY(tabBar); tabBar->setCurrentIndex(2); @@ -688,6 +714,11 @@ void tst_QDockWidget::updateTabBarOnVisibilityChanged() dw1->hide(); QTRY_COMPARE(tabBar->count(), 2); QCOMPARE(tabBar->currentIndex(), 0); + + QCOMPARE(mw.tabifiedDockWidgets(dw2), {dw3}); + + mw.removeDockWidget(dw3); + QCOMPARE(mw.tabifiedDockWidgets(dw2).count(), 0); } Q_DECLARE_METATYPE(Qt::DockWidgetArea) @@ -1074,9 +1105,10 @@ void tst_QDockWidget::setWindowTitle() QMainWindow window; QDockWidget dock1(&window); QDockWidget dock2(&window); - const QString dock1Title = QStringLiteral("&Window"); - const QString dock2Title = QStringLiteral("&Modifiable Window [*]"); + constexpr QLatin1StringView dock1Title("&Window"); + constexpr QLatin1StringView dock2Title("&Modifiable Window [*]"); + // Set title on docked dock widgets, before main window is shown dock1.setWindowTitle(dock1Title); dock2.setWindowTitle(dock2Title); window.addDockWidget(Qt::RightDockWidgetArea, &dock1); @@ -1087,6 +1119,7 @@ void tst_QDockWidget::setWindowTitle() QCOMPARE(dock1.windowTitle(), dock1Title); QCOMPARE(dock2.windowTitle(), dock2Title); + // Check if title remains unchanged when docking / undocking dock1.setFloating(true); dock1.show(); QVERIFY(QTest::qWaitForWindowExposed(&dock1)); @@ -1096,12 +1129,16 @@ void tst_QDockWidget::setWindowTitle() dock1.setFloating(true); dock1.show(); QVERIFY(QTest::qWaitForWindowExposed(&dock1)); - const QString changed = QStringLiteral("Changed "); + + // Change a floating dock widget's title and check remains unchanged when docking + constexpr QLatin1StringView changed("Changed "); dock1.setWindowTitle(QString(changed + dock1Title)); QCOMPARE(dock1.windowTitle(), QString(changed + dock1Title)); dock1.setFloating(false); + QVERIFY(QTest::qWaitFor([&dock1](){ return !dock1.windowHandle(); })); QCOMPARE(dock1.windowTitle(), QString(changed + dock1Title)); + // Test consistency after toggling modified and floating dock2.setWindowModified(true); QCOMPARE(dock2.windowTitle(), dock2Title); dock2.setFloating(true); @@ -1116,6 +1153,12 @@ void tst_QDockWidget::setWindowTitle() dock2.show(); QVERIFY(QTest::qWaitForWindowExposed(&dock2)); QCOMPARE(dock2.windowTitle(), dock2Title); + + // Test title change of a closed dock widget + static constexpr QLatin1StringView closedDock2("Closed D2"); + dock2.close(); + dock2.setWindowTitle(closedDock2); + QCOMPARE(dock2.windowTitle(), closedDock2); } // helpers for dockPermissions, hideAndShow, closeAndDelete @@ -1175,7 +1218,7 @@ QPoint tst_QDockWidget::dragPoint(QDockWidget* dockWidget) return dockWidget->mapToGlobal(dwlayout->titleArea().center()); } -void tst_QDockWidget::moveDockWidget(QDockWidget* dw, QPoint to, QPoint from) const +void tst_QDockWidget::moveDockWidget(QDockWidget* dw, QPoint to, QPoint from, MoveDockWidgetRule rule) const { Q_ASSERT(dw); @@ -1192,12 +1235,22 @@ void tst_QDockWidget::moveDockWidget(QDockWidget* dw, QPoint to, QPoint from) co QTest::mouseMove(dw, target); qCDebug(lcTestDockWidget) << "Move" << dw->objectName() << "to" << target; qCDebug(lcTestDockWidget) << "Move" << dw->objectName() << "to" << to; - QTest::mouseRelease(dw, Qt::LeftButton, Qt::KeyboardModifiers(), target); - QTest::qWait(waitingTime); + if (rule == MoveDockWidgetRule::Drop) { + QTest::mouseRelease(dw, Qt::LeftButton, Qt::KeyboardModifiers(), target); + QTest::qWait(waitingTime); - // Verify WindowActive only for floating dock widgets - if (dw->isFloating()) - QTRY_VERIFY(QTest::qWaitForWindowActive(dw)); + // Verify WindowActive only for floating dock widgets + if (dw->isFloating()) + QTRY_VERIFY(QTest::qWaitForWindowActive(dw)); + return; + } + qCDebug(lcTestDockWidget) << "Aborting move and dropping at origin"; + + // Give animations some time + QTest::qWait(waitingTime); + QTest::mouseMove(dw, from); + QTest::mouseRelease(dw, Qt::LeftButton, Qt::KeyboardModifiers(), from); + QTest::qWait(waitingTime); } void tst_QDockWidget::unplugAndResize(QMainWindow* mainWindow, QDockWidget* dw, QPoint home, QSize size) const @@ -1245,7 +1298,7 @@ void tst_QDockWidget::unplugAndResize(QMainWindow* mainWindow, QDockWidget* dw, QPoint pos1 = dw->mapToGlobal(dw->rect().center()); pos1.rx() += mx; pos1.ry() += my; - moveDockWidget(dw, pos1, dw->mapToGlobal(dw->rect().center())); + moveDockWidget(dw, pos1, dw->mapToGlobal(dw->rect().center()), MoveDockWidgetRule::Drop); QTRY_VERIFY(dw->isFloating()); // Unplugged object's size may differ max. by 2x frame size @@ -1307,20 +1360,71 @@ bool tst_QDockWidget::checkFloatingTabs(QMainWindow* mainWindow, QPointer<QDockW return true; } -// detect xcb error +#ifdef QT_BUILD_INTERNAL +// Statics for xcb error, raise() suppert / msg handler +static tst_QDockWidget *qThis = nullptr; +static void (*oldMessageHandler)(QtMsgType, const QMessageLogContext &, const QString &); +#define QXCBVERIFY(cond) do { if (xcbError) QSKIP("Test skipped due to XCB error"); QVERIFY(cond); } while (0) + +// detect xcb error and missing raise() support // qt.qpa.xcb: internal error: void QXcbWindow::setNetWmStateOnUnmappedWindow() called on mapped window void tst_QDockWidget::xcbMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg) { Q_ASSERT(oldMessageHandler); - if (type == QtWarningMsg && QString(context.category) == "qt.qpa.xcb" && msg.contains("internal error")) { + if (type == QtWarningMsg) { Q_ASSERT(qThis); - qThis->xcbError = true; + if (QString(context.category) == "qt.qpa.xcb" && msg.contains("internal error")) + qThis->xcbError = true; + if (msg.contains("does not support raise")) + qThis->platformSupportingRaise = false; } return oldMessageHandler(type, context, msg); } +#endif + +void tst_QDockWidget::createFloatingTabs(QMainWindow* &mainWindow, QPointer<QWidget> ¢, + QPointer<QDockWidget> &d1, QPointer<QDockWidget> &d2, + QList<int> &path1, QList<int> &path2) const +{ + createTestWidgets(mainWindow, cent, d1, d2); + +#ifdef QT_BUILD_INTERNAL + qThis = const_cast<tst_QDockWidget *>(this); + oldMessageHandler = qInstallMessageHandler(xcbMessageHandler); + auto resetMessageHandler = qScopeGuard([] { qInstallMessageHandler(oldMessageHandler); }); +#endif + + // Test will fail if platform doesn't support raise. + mainWindow->windowHandle()->handle()->raise(); + if (!platformSupportingRaise) + QSKIP("Platform not supporting raise(). Floating tab based tests will fail."); + // remember paths to d1 and d2 + QMainWindowLayout* layout = qobject_cast<QMainWindowLayout *>(mainWindow->layout()); + path1 = layout->layoutState.indexOf(d1); + path2 = layout->layoutState.indexOf(d2); + + // unplug and resize both dock widgets + unplugAndResize(mainWindow, d1, home1(mainWindow), size1(mainWindow)); + unplugAndResize(mainWindow, d2, home2(mainWindow), size2(mainWindow)); + + // docks must be parented to the main window, no group window must exist + QCOMPARE(d1->parentWidget(), mainWindow); + QCOMPARE(d2->parentWidget(), mainWindow); + QVERIFY(mainWindow->findChildren<QDockWidgetGroupWindow *>().isEmpty()); + + // Test plugging + qCDebug(lcTestDockWidget) << "*** move d1 dock over d2 dock ***"; + qCDebug(lcTestDockWidget) << "**********(test plugging)*************"; + qCDebug(lcTestDockWidget) << "Move d1 over d2"; + moveDockWidget(d1, d2->mapToGlobal(d2->rect().center()), QPoint(), MoveDockWidgetRule::Drop); + + // Now MainWindow has to have a floatingTab child + QPointer<QDockWidgetGroupWindow> ftabs; + QTRY_VERIFY(checkFloatingTabs(mainWindow, ftabs, QList<QDockWidget *>() << d1 << d2)); +} #endif // QT_BUILD_INTERNAL // test floating tabs and item_tree consistency @@ -1337,33 +1441,20 @@ void tst_QDockWidget::floatingTabs() QPointer<QDockWidget> d2; QPointer<QWidget> cent; QMainWindow* mainWindow; - createTestWidgets(mainWindow, cent, d1, d2); + QList<int> path1; + QList<int> path2; + createFloatingTabs(mainWindow, cent, d1, d2, path1, path2); std::unique_ptr<QMainWindow> up_mainWindow(mainWindow); + QCOMPARE(mainWindow->tabifiedDockWidgets(d1), {d2}); + QCOMPARE(mainWindow->tabifiedDockWidgets(d2), {d1}); + /* * unplug both dockwidgets, resize them and plug them into a joint floating tab * expected behavior: QDOckWidgetGroupWindow with both widgets is created */ - // remember paths to d1 and d2 - QMainWindowLayout* layout = qobject_cast<QMainWindowLayout*>(mainWindow->layout()); - const QList<int> path1 = layout->layoutState.indexOf(d1); - const QList<int> path2 = layout->layoutState.indexOf(d2); - - // unplug and resize both dock widgets - unplugAndResize(mainWindow, d1, home1(mainWindow), size1(mainWindow)); - unplugAndResize(mainWindow, d2, home2(mainWindow), size2(mainWindow)); - - // Test plugging - qCDebug(lcTestDockWidget) << "*** move d1 dock over d2 dock ***"; - qCDebug(lcTestDockWidget) << "**********(test plugging)*************"; - qCDebug(lcTestDockWidget) << "Move d1 over d2"; - moveDockWidget(d1, d2->mapToGlobal(d2->rect().center())); - - // Both dock widgets must no longer be floating // disabled due to flakiness on macOS and Windows - //QTRY_VERIFY(!d1->isFloating()); - //QTRY_VERIFY(!d2->isFloating()); if (d1->isFloating()) qWarning("OS flakiness: D1 is docked and reports being floating"); if (d2->isFloating()) @@ -1371,11 +1462,25 @@ void tst_QDockWidget::floatingTabs() // Now MainWindow has to have a floatingTab child QPointer<QDockWidgetGroupWindow> ftabs; - QTRY_VERIFY(checkFloatingTabs(mainWindow, ftabs, QList<QDockWidget*>() << d1 << d2)); + QTRY_VERIFY(checkFloatingTabs(mainWindow, ftabs, QList<QDockWidget *>() << d1 << d2)); + + // Hide both dock widgets. Verify that the group window is also hidden. + qCDebug(lcTestDockWidget) << "*** Hide and show tabbed dock widgets ***"; + d1->hide(); + d2->hide(); + QTRY_VERIFY(ftabs->isHidden()); + + // Show both dockwidgets again. Verify that the group window is visible. + d1->show(); + d2->show(); + QTRY_VERIFY(ftabs->isVisible()); /* * replug both dock widgets into their initial position - * expected behavior: both docks are plugged and no longer floating + * expected behavior: + - both docks are plugged + - both docks are no longer floating + - title changes have been propagated */ @@ -1399,10 +1504,9 @@ void tst_QDockWidget::floatingTabs() // Plug back into dock areas qCDebug(lcTestDockWidget) << "*** test plugging back to dock areas ***"; qCDebug(lcTestDockWidget) << "Move d1 to left dock"; - //moveDockWidget(d1, d1->mapFrom(MainWindow, dockPoint(MainWindow, Qt::LeftDockWidgetArea))); - moveDockWidget(d1, dockPoint(mainWindow, Qt::LeftDockWidgetArea)); + moveDockWidget(d1, dockPoint(mainWindow, Qt::LeftDockWidgetArea), QPoint(), MoveDockWidgetRule::Drop); qCDebug(lcTestDockWidget) << "Move d2 to right dock"; - moveDockWidget(d2, dockPoint(mainWindow, Qt::RightDockWidgetArea)); + moveDockWidget(d2, dockPoint(mainWindow, Qt::RightDockWidgetArea), QPoint(), MoveDockWidgetRule::Drop); qCDebug(lcTestDockWidget) << "Waiting" << waitBeforeClose << "ms before plugging back."; QTest::qWait(waitBeforeClose); @@ -1416,13 +1520,98 @@ void tst_QDockWidget::floatingTabs() QTRY_VERIFY(ftabs.isNull()); // Check if paths are consistent + QMainWindowLayout* layout = qobject_cast<QMainWindowLayout *>(mainWindow->layout()); qCDebug(lcTestDockWidget) << "Checking path consistency" << layout->layoutState.indexOf(d1) << layout->layoutState.indexOf(d2); - // Path1 must be identical - QTRY_VERIFY(path1 == layout->layoutState.indexOf(d1)); + // Paths must be identical + QTRY_COMPARE(layout->layoutState.indexOf(d1), path1); + QTRY_COMPARE(layout->layoutState.indexOf(d2), path2); - // d1 must have a gap item due to size change - QTRY_VERIFY(layout->layoutState.indexOf(d2) == QList<int>() << path2 << 0); + QCOMPARE(mainWindow->tabifiedDockWidgets(d1), {}); + QCOMPARE(mainWindow->tabifiedDockWidgets(d2), {}); +#else + QSKIP("test requires -developer-build option"); +#endif // QT_BUILD_INTERNAL +} + +void tst_QDockWidget::deleteFloatingTabWithSingleDockWidget_data() +{ +#ifdef QT_BUILD_INTERNAL + QTest::addColumn<int>("reason"); + QTest::addRow("Delete child") << static_cast<int>(ChildRemovalReason::Destroyed); + QTest::addRow("Close child") << static_cast<int>(ChildRemovalReason::Closed); + QTest::addRow("Reparent child") << static_cast<int>(ChildRemovalReason::Reparented); +#endif +} + +void tst_QDockWidget::deleteFloatingTabWithSingleDockWidget() +{ + if (QGuiApplication::platformName().startsWith(QLatin1String("wayland"), Qt::CaseInsensitive)) + QSKIP("Test skipped on Wayland."); +#ifdef Q_OS_WIN + QSKIP("Test skipped on Windows platforms"); +#endif // Q_OS_WIN +#ifdef QT_BUILD_INTERNAL + + QFETCH(int, reason); + const ChildRemovalReason removalReason = static_cast<ChildRemovalReason>(reason); + + QPointer<QDockWidget> d1; + QPointer<QDockWidget> d2; + QPointer<QWidget> cent; + QMainWindow* mainWindow; + QList<int> path1; + QList<int> path2; + createFloatingTabs(mainWindow, cent, d1, d2, path1, path2); + std::unique_ptr<QMainWindow> up_mainWindow(mainWindow); + + switch (removalReason) { + case ChildRemovalReason::Destroyed: + delete d1; + break; + case ChildRemovalReason::Closed: + d1->close(); + break; + case ChildRemovalReason::Reparented: + // This will create an invalid state, because setParent() doesn't fix the item_list. + // Testing this case anyway, because setParent() includig item_list fixup is executed, + // when the 2nd last dock widget is dragged out of a floating tab. + // => despite of the broken state, the group window has to be gone. + d1->setParent(mainWindow); + break; + } + + QTRY_VERIFY(!qobject_cast<QDockWidgetGroupWindow *>(d2->parentWidget())); + QTRY_VERIFY(mainWindow->findChildren<QDockWidgetGroupWindow *>().isEmpty()); +#endif // QT_BUILD_INTERNAL +} + +void tst_QDockWidget::hoverWithoutDrop() +{ + if (QGuiApplication::platformName().startsWith(QLatin1String("wayland"), Qt::CaseInsensitive)) + QSKIP("Test skipped on Wayland."); +#ifdef QT_BUILD_INTERNAL + + QPointer<QDockWidget> d1; + QPointer<QDockWidget> d2; + QPointer<QWidget> cent; + QMainWindow* mainWindow; + createTestWidgets(mainWindow, cent, d1, d2); + std::unique_ptr<QMainWindow> up_mainWindow(mainWindow); + + // unplug and resize both dock widgets + unplugAndResize(mainWindow, d1, home1(mainWindow), size1(mainWindow)); + unplugAndResize(mainWindow, d2, home2(mainWindow), size2(mainWindow)); + + // Test plugging + qCDebug(lcTestDockWidget) << "*** move d1 dock over d2 dock ***"; + qCDebug(lcTestDockWidget) << "*******(test hovering)***********"; + qCDebug(lcTestDockWidget) << "Move d1 over d2, wait and return to origin"; + const QPoint source = d1->mapToGlobal(d1->rect().center()); + const QPoint target = d2->mapToGlobal(d2->rect().center()); + moveDockWidget(d1, target, source, MoveDockWidgetRule::Abort); + auto *groupWindow = mainWindow->findChild<QDockWidgetGroupWindow *>(); + QCOMPARE(groupWindow, nullptr); #else QSKIP("test requires -developer-build option"); #endif // QT_BUILD_INTERNAL @@ -1431,6 +1620,8 @@ void tst_QDockWidget::floatingTabs() // test hide & show void tst_QDockWidget::hideAndShow() { + if (QGuiApplication::platformName().startsWith(QLatin1String("wayland"), Qt::CaseInsensitive)) + QSKIP("Test skipped on Wayland."); #ifdef QT_BUILD_INTERNAL // Skip test if xcb error is launched qThis = this; @@ -1473,7 +1664,7 @@ void tst_QDockWidget::hideAndShow() unplugAndResize(mainWindow, d1, home1(mainWindow), size1(mainWindow)); unplugAndResize(mainWindow, d2, home2(mainWindow), size2(mainWindow)); - // Check hiding of undocked widgets + // Check hiding of undocked widgets qCDebug(lcTestDockWidget) << "Hiding mainWindow with unplugged dock widgets" << mainWindow; mainWindow->hide(); QTRY_VERIFY(!mainWindow->isVisible()); @@ -1484,6 +1675,16 @@ void tst_QDockWidget::hideAndShow() QTRY_VERIFY(!d1->isVisible()); QTRY_VERIFY(!d2->isVisible()); + + // Check floating, hidden dock widgets remain hidden, when their state is restored + qCDebug(lcTestDockWidget) << "Restoring state of unplugged, hidden dock widgets" << mainWindow; + const QByteArray state = mainWindow->saveState(); + mainWindow->restoreState(state); + mainWindow->show(); + QVERIFY(QTest::qWaitForWindowExposed(mainWindow)); + QTRY_VERIFY(!d1->isVisible()); + QTRY_VERIFY(!d2->isVisible()); + qCDebug(lcTestDockWidget) << "Waiting" << waitBeforeClose << "ms before closing."; QTest::qWait(waitBeforeClose); #else @@ -1494,6 +1695,8 @@ void tst_QDockWidget::hideAndShow() // test closing and deleting consistency void tst_QDockWidget::closeAndDelete() { + if (QGuiApplication::platformName().startsWith(QLatin1String("wayland"), Qt::CaseInsensitive)) + QSKIP("Test skipped on Wayland."); #ifdef QT_BUILD_INTERNAL // Create a mainwindow with a central widget and two dock widgets QPointer<QDockWidget> d1; @@ -1509,7 +1712,7 @@ void tst_QDockWidget::closeAndDelete() // Create a floating tab and unplug it again qCDebug(lcTestDockWidget) << "Move d1 over d2"; - moveDockWidget(d1, d2->mapToGlobal(d2->rect().center())); + moveDockWidget(d1, d2->mapToGlobal(d2->rect().center()), QPoint(), MoveDockWidgetRule::Drop); // Both dock widgets must no longer be floating // disabled due to flakiness on macOS and Windows @@ -1521,8 +1724,10 @@ void tst_QDockWidget::closeAndDelete() qWarning("OS flakiness: D2 is docked and reports being floating"); // Close everything with a single shot. Expected behavior: Event loop stops - bool eventLoopStopped = true; - QTimer::singleShot(0, this, [mainWindow, d1, d2] { + QSignalSpy closeSpy(qApp, &QApplication::lastWindowClosed); + QObject localContext; + + QTimer::singleShot(0, &localContext, [&](){ mainWindow->close(); QTRY_VERIFY(!mainWindow->isVisible()); QTRY_VERIFY(d1->isVisible()); @@ -1531,19 +1736,12 @@ void tst_QDockWidget::closeAndDelete() d2->close(); QTRY_VERIFY(!d1->isVisible()); QTRY_VERIFY(!d2->isVisible()); - }); - - // Fallback timer to report event loop still running - QTimer::singleShot(100, this, [&eventLoopStopped] { - qCDebug(lcTestDockWidget) << "Last dock widget hasn't shout down event loop!"; - eventLoopStopped = false; + QTRY_COMPARE(closeSpy.count(), 1); QApplication::quit(); }); QApplication::exec(); - QTRY_VERIFY(eventLoopStopped); - // Check heap cleanup qCDebug(lcTestDockWidget) << "Deleting mainWindow"; up_mainWindow.reset(); @@ -1555,9 +1753,29 @@ void tst_QDockWidget::closeAndDelete() #endif // QT_BUILD_INTERNAL } +void tst_QDockWidget::closeUnclosable() +{ + QDockWidget *dockWidget = new QDockWidget("dock"); + dockWidget->setWidget(new QScrollArea); + dockWidget->setFeatures(QDockWidget::DockWidgetFloatable); + + QMainWindow mw; + mw.addDockWidget(Qt::TopDockWidgetArea, dockWidget); + mw.show(); + + QVERIFY(QTest::qWaitForWindowExposed(&mw)); + dockWidget->setFloating(true); + + QCOMPARE(dockWidget->close(), false); + mw.close(); + QCOMPARE(dockWidget->close(), true); +} + // Test dock area permissions void tst_QDockWidget::dockPermissions() { + if (QGuiApplication::platformName().startsWith(QLatin1String("wayland"), Qt::CaseInsensitive)) + QSKIP("Test skipped on Wayland."); #ifdef Q_OS_WIN QSKIP("Test skipped on Windows platforms"); #endif // Q_OS_WIN @@ -1599,16 +1817,16 @@ void tst_QDockWidget::dockPermissions() // Move d2 to non allowed dock areas and verify it remains floating qCDebug(lcTestDockWidget) << "Move d2 to top dock"; - moveDockWidget(d2, dockPoint(mainWindow, Qt::TopDockWidgetArea)); + moveDockWidget(d2, dockPoint(mainWindow, Qt::TopDockWidgetArea), QPoint(), MoveDockWidgetRule::Drop); QTRY_VERIFY(d2->isFloating()); qCDebug(lcTestDockWidget) << "Move d2 to left dock"; //moveDockWidget(d2, d2->mapFrom(MainWindow, dockPoint(MainWindow, Qt::LeftDockWidgetArea))); - moveDockWidget(d2, dockPoint(mainWindow, Qt::LeftDockWidgetArea)); + moveDockWidget(d2, dockPoint(mainWindow, Qt::LeftDockWidgetArea), QPoint(), MoveDockWidgetRule::Drop); QTRY_VERIFY(d2->isFloating()); qCDebug(lcTestDockWidget) << "Move d2 to bottom dock"; - moveDockWidget(d2, dockPoint(mainWindow, Qt::BottomDockWidgetArea)); + moveDockWidget(d2, dockPoint(mainWindow, Qt::BottomDockWidgetArea), QPoint(), MoveDockWidgetRule::Drop); QTRY_VERIFY(d2->isFloating()); qCDebug(lcTestDockWidget) << "Waiting" << waitBeforeClose << "ms before closing."; |