summaryrefslogtreecommitdiffstats
path: root/tests/auto/widgets/widgets/qdockwidget/tst_qdockwidget.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'tests/auto/widgets/widgets/qdockwidget/tst_qdockwidget.cpp')
-rw-r--r--tests/auto/widgets/widgets/qdockwidget/tst_qdockwidget.cpp741
1 files changed, 567 insertions, 174 deletions
diff --git a/tests/auto/widgets/widgets/qdockwidget/tst_qdockwidget.cpp b/tests/auto/widgets/widgets/qdockwidget/tst_qdockwidget.cpp
index 2229f5da55..63a432578e 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,17 +10,16 @@
#include "private/qmainwindowlayout_p.h"
#include <QAbstractButton>
#include <qlineedit.h>
+#include <QtGui/qpa/qplatformwindow.h>
#include <qtabbar.h>
#include <QScreen>
#include <QTimer>
#include <QtGui/QPainter>
#include <QLabel>
-#ifdef QT_BUILD_INTERNAL
-QT_BEGIN_NAMESPACE
-Q_LOGGING_CATEGORY(lcQpaDockWidgets, "qt.widgets.dockwidgets");
-QT_END_NAMESPACE
-#endif
+Q_LOGGING_CATEGORY(lcTestDockWidget, "qt.widgets.tests.qdockwidget")
+
+#include <QtWidgets/private/qapplication_p.h>
bool hasFeature(QDockWidget *dockwidget, QDockWidget::DockWidgetFeature feature)
{ return (dockwidget->features() & feature) == feature; }
@@ -67,21 +66,36 @@ 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();
private:
// helpers and consts for dockPermissions, hideAndShow, closeAndDelete
#ifdef QT_BUILD_INTERNAL
- void createTestWidgets(QMainWindow* &MainWindow, QPointer<QWidget> &cent, QPointer<QDockWidget> &d1, QPointer<QDockWidget> &d2) const;
+ void createTestWidgets(QMainWindow* &MainWindow, QPointer<QWidget> &cent,
+ QPointer<QDockWidget> &d1, QPointer<QDockWidget> &d2) const;
+
void unplugAndResize(QMainWindow* MainWindow, QDockWidget* dw, QPoint home, QSize size) const;
+ void createFloatingTabs(QMainWindow* &MainWindow, QPointer<QWidget> &cent,
+ 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())); }
@@ -101,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
@@ -127,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()
{
@@ -271,12 +295,12 @@ void tst_QDockWidget::features()
QVERIFY(!hasFeature(&dw, QDockWidget::DockWidgetClosable));
QVERIFY(hasFeature(&dw, QDockWidget::DockWidgetMovable));
QVERIFY(hasFeature(&dw, QDockWidget::DockWidgetFloatable));
- QCOMPARE(spy.count(), 1);
+ QCOMPARE(spy.size(), 1);
QCOMPARE((int)*(static_cast<const QDockWidget::DockWidgetFeature *>(spy.at(0).value(0).constData())),
(int)dw.features());
spy.clear();
dw.setFeatures(dw.features());
- QCOMPARE(spy.count(), 0);
+ QCOMPARE(spy.size(), 0);
spy.clear();
setFeature(&dw, QDockWidget::DockWidgetClosable);
@@ -284,12 +308,12 @@ void tst_QDockWidget::features()
QVERIFY(hasFeature(&dw, QDockWidget::DockWidgetClosable));
QVERIFY(hasFeature(&dw, QDockWidget::DockWidgetMovable));
QVERIFY(hasFeature(&dw, QDockWidget::DockWidgetFloatable));
- QCOMPARE(spy.count(), 1);
+ QCOMPARE(spy.size(), 1);
QCOMPARE((int)*static_cast<const QDockWidget::DockWidgetFeature *>(spy.at(0).value(0).constData()),
(int)dw.features());
spy.clear();
dw.setFeatures(dw.features());
- QCOMPARE(spy.count(), 0);
+ QCOMPARE(spy.size(), 0);
spy.clear();
setFeature(&dw, QDockWidget::DockWidgetMovable, false);
@@ -297,12 +321,12 @@ void tst_QDockWidget::features()
QVERIFY(hasFeature(&dw, QDockWidget::DockWidgetClosable));
QVERIFY(!hasFeature(&dw, QDockWidget::DockWidgetMovable));
QVERIFY(hasFeature(&dw, QDockWidget::DockWidgetFloatable));
- QCOMPARE(spy.count(), 1);
+ QCOMPARE(spy.size(), 1);
QCOMPARE((int)*static_cast<const QDockWidget::DockWidgetFeature *>(spy.at(0).value(0).constData()),
(int)dw.features());
spy.clear();
dw.setFeatures(dw.features());
- QCOMPARE(spy.count(), 0);
+ QCOMPARE(spy.size(), 0);
spy.clear();
setFeature(&dw, QDockWidget::DockWidgetMovable);
@@ -310,12 +334,12 @@ void tst_QDockWidget::features()
QVERIFY(hasFeature(&dw, QDockWidget::DockWidgetClosable));
QVERIFY(hasFeature(&dw, QDockWidget::DockWidgetMovable));
QVERIFY(hasFeature(&dw, QDockWidget::DockWidgetFloatable));
- QCOMPARE(spy.count(), 1);
+ QCOMPARE(spy.size(), 1);
QCOMPARE((int)*static_cast<const QDockWidget::DockWidgetFeature *>(spy.at(0).value(0).constData()),
(int)dw.features());
spy.clear();
dw.setFeatures(dw.features());
- QCOMPARE(spy.count(), 0);
+ QCOMPARE(spy.size(), 0);
spy.clear();
setFeature(&dw, QDockWidget::DockWidgetFloatable, false);
@@ -323,12 +347,12 @@ void tst_QDockWidget::features()
QVERIFY(hasFeature(&dw, QDockWidget::DockWidgetClosable));
QVERIFY(hasFeature(&dw, QDockWidget::DockWidgetMovable));
QVERIFY(!hasFeature(&dw, QDockWidget::DockWidgetFloatable));
- QCOMPARE(spy.count(), 1);
+ QCOMPARE(spy.size(), 1);
QCOMPARE((int)*static_cast<const QDockWidget::DockWidgetFeature *>(spy.at(0).value(0).constData()),
(int)dw.features());
spy.clear();
dw.setFeatures(dw.features());
- QCOMPARE(spy.count(), 0);
+ QCOMPARE(spy.size(), 0);
spy.clear();
setFeature(&dw, QDockWidget::DockWidgetFloatable);
@@ -336,12 +360,12 @@ void tst_QDockWidget::features()
QVERIFY(hasFeature(&dw, QDockWidget::DockWidgetClosable));
QVERIFY(hasFeature(&dw, QDockWidget::DockWidgetMovable));
QVERIFY(hasFeature(&dw, QDockWidget::DockWidgetFloatable));
- QCOMPARE(spy.count(), 1);
+ QCOMPARE(spy.size(), 1);
QCOMPARE((int)*static_cast<const QDockWidget::DockWidgetFeature *>(spy.at(0).value(0).constData()),
(int)dw.features());
spy.clear();
dw.setFeatures(dw.features());
- QCOMPARE(spy.count(), 0);
+ QCOMPARE(spy.size(), 0);
spy.clear();
// set all at once
@@ -350,12 +374,12 @@ void tst_QDockWidget::features()
QVERIFY(hasFeature(&dw, QDockWidget::DockWidgetClosable));
QVERIFY(hasFeature(&dw, QDockWidget::DockWidgetMovable));
QVERIFY(!hasFeature(&dw, QDockWidget::DockWidgetFloatable));
- QCOMPARE(spy.count(), 1);
+ QCOMPARE(spy.size(), 1);
QCOMPARE((int)*static_cast<const QDockWidget::DockWidgetFeature *>(spy.at(0).value(0).constData()),
(int)dw.features());
spy.clear();
dw.setFeatures(dw.features());
- QCOMPARE(spy.count(), 0);
+ QCOMPARE(spy.size(), 0);
spy.clear();
dw.setFeatures(QDockWidget::DockWidgetClosable);
@@ -363,12 +387,12 @@ void tst_QDockWidget::features()
QVERIFY(hasFeature(&dw, QDockWidget::DockWidgetClosable));
QVERIFY(!hasFeature(&dw, QDockWidget::DockWidgetMovable));
QVERIFY(!hasFeature(&dw, QDockWidget::DockWidgetFloatable));
- QCOMPARE(spy.count(), 1);
+ QCOMPARE(spy.size(), 1);
QCOMPARE((int)*static_cast<const QDockWidget::DockWidgetFeature *>(spy.at(0).value(0).constData()),
(int)dw.features());
spy.clear();
dw.setFeatures(dw.features());
- QCOMPARE(spy.count(), 0);
+ QCOMPARE(spy.size(), 0);
spy.clear();
dw.setFeatures(allDockWidgetFeatures);
@@ -376,12 +400,12 @@ void tst_QDockWidget::features()
QVERIFY(hasFeature(&dw, QDockWidget::DockWidgetClosable));
QVERIFY(hasFeature(&dw, QDockWidget::DockWidgetMovable));
QVERIFY(hasFeature(&dw, QDockWidget::DockWidgetFloatable));
- QCOMPARE(spy.count(), 1);
+ QCOMPARE(spy.size(), 1);
QCOMPARE((int)*static_cast<const QDockWidget::DockWidgetFeature *>(spy.at(0).value(0).constData()),
(int)dw.features());
spy.clear();
dw.setFeatures(dw.features());
- QCOMPARE(spy.count(), 0);
+ QCOMPARE(spy.size(), 0);
spy.clear();
}
@@ -408,20 +432,20 @@ void tst_QDockWidget::setFloating()
QVERIFY((dockedPosition - floatingPosition).manhattanLength() < 50);
QVERIFY(dw.isFloating());
- QCOMPARE(spy.count(), 1);
+ QCOMPARE(spy.size(), 1);
QCOMPARE(spy.at(0).value(0).toBool(), dw.isFloating());
spy.clear();
dw.setFloating(dw.isFloating());
- QCOMPARE(spy.count(), 0);
+ QCOMPARE(spy.size(), 0);
spy.clear();
dw.setFloating(false);
QVERIFY(!dw.isFloating());
- QCOMPARE(spy.count(), 1);
+ QCOMPARE(spy.size(), 1);
QCOMPARE(spy.at(0).value(0).toBool(), dw.isFloating());
spy.clear();
dw.setFloating(dw.isFloating());
- QCOMPARE(spy.count(), 0);
+ QCOMPARE(spy.size(), 0);
spy.clear();
}
@@ -445,12 +469,12 @@ void tst_QDockWidget::allowedAreas()
QVERIFY(!dw.isAreaAllowed(Qt::RightDockWidgetArea));
QVERIFY(!dw.isAreaAllowed(Qt::TopDockWidgetArea));
QVERIFY(!dw.isAreaAllowed(Qt::BottomDockWidgetArea));
- QCOMPARE(spy.count(), 1);
+ QCOMPARE(spy.size(), 1);
QCOMPARE(*static_cast<const Qt::DockWidgetAreas *>(spy.at(0).value(0).constData()),
dw.allowedAreas());
spy.clear();
dw.setAllowedAreas(dw.allowedAreas());
- QCOMPARE(spy.count(), 0);
+ QCOMPARE(spy.size(), 0);
dw.setAllowedAreas(Qt::RightDockWidgetArea);
QCOMPARE(dw.allowedAreas(), Qt::RightDockWidgetArea);
@@ -458,12 +482,12 @@ void tst_QDockWidget::allowedAreas()
QVERIFY(dw.isAreaAllowed(Qt::RightDockWidgetArea));
QVERIFY(!dw.isAreaAllowed(Qt::TopDockWidgetArea));
QVERIFY(!dw.isAreaAllowed(Qt::BottomDockWidgetArea));
- QCOMPARE(spy.count(), 1);
+ QCOMPARE(spy.size(), 1);
QCOMPARE(*static_cast<const Qt::DockWidgetAreas *>(spy.at(0).value(0).constData()),
dw.allowedAreas());
spy.clear();
dw.setAllowedAreas(dw.allowedAreas());
- QCOMPARE(spy.count(), 0);
+ QCOMPARE(spy.size(), 0);
dw.setAllowedAreas(Qt::TopDockWidgetArea);
QCOMPARE(dw.allowedAreas(), Qt::TopDockWidgetArea);
@@ -471,12 +495,12 @@ void tst_QDockWidget::allowedAreas()
QVERIFY(!dw.isAreaAllowed(Qt::RightDockWidgetArea));
QVERIFY(dw.isAreaAllowed(Qt::TopDockWidgetArea));
QVERIFY(!dw.isAreaAllowed(Qt::BottomDockWidgetArea));
- QCOMPARE(spy.count(), 1);
+ QCOMPARE(spy.size(), 1);
QCOMPARE(*static_cast<const Qt::DockWidgetAreas *>(spy.at(0).value(0).constData()),
dw.allowedAreas());
spy.clear();
dw.setAllowedAreas(dw.allowedAreas());
- QCOMPARE(spy.count(), 0);
+ QCOMPARE(spy.size(), 0);
dw.setAllowedAreas(Qt::BottomDockWidgetArea);
QCOMPARE(dw.allowedAreas(), Qt::BottomDockWidgetArea);
@@ -484,12 +508,12 @@ void tst_QDockWidget::allowedAreas()
QVERIFY(!dw.isAreaAllowed(Qt::RightDockWidgetArea));
QVERIFY(!dw.isAreaAllowed(Qt::TopDockWidgetArea));
QVERIFY(dw.isAreaAllowed(Qt::BottomDockWidgetArea));
- QCOMPARE(spy.count(), 1);
+ QCOMPARE(spy.size(), 1);
QCOMPARE(*static_cast<const Qt::DockWidgetAreas *>(spy.at(0).value(0).constData()),
dw.allowedAreas());
spy.clear();
dw.setAllowedAreas(dw.allowedAreas());
- QCOMPARE(spy.count(), 0);
+ QCOMPARE(spy.size(), 0);
// multiple dock window areas
dw.setAllowedAreas(Qt::TopDockWidgetArea | Qt::BottomDockWidgetArea);
@@ -499,12 +523,12 @@ void tst_QDockWidget::allowedAreas()
QVERIFY(dw.isAreaAllowed(Qt::TopDockWidgetArea));
QVERIFY(dw.isAreaAllowed(Qt::BottomDockWidgetArea));
//QVERIFY(!dw.isAreaAllowed(Qt::FloatingDockWidgetArea));
- QCOMPARE(spy.count(), 1);
+ QCOMPARE(spy.size(), 1);
QCOMPARE(*static_cast<const Qt::DockWidgetAreas *>(spy.at(0).value(0).constData()),
dw.allowedAreas());
spy.clear();
dw.setAllowedAreas(dw.allowedAreas());
- QCOMPARE(spy.count(), 0);
+ QCOMPARE(spy.size(), 0);
dw.setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
QCOMPARE(dw.allowedAreas(), Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
@@ -513,12 +537,12 @@ void tst_QDockWidget::allowedAreas()
QVERIFY(!dw.isAreaAllowed(Qt::TopDockWidgetArea));
QVERIFY(!dw.isAreaAllowed(Qt::BottomDockWidgetArea));
//QVERIFY(!dw.isAreaAllowed(Qt::FloatingDockWidgetArea));
- QCOMPARE(spy.count(), 1);
+ QCOMPARE(spy.size(), 1);
QCOMPARE(*static_cast<const Qt::DockWidgetAreas *>(spy.at(0).value(0).constData()),
dw.allowedAreas());
spy.clear();
dw.setAllowedAreas(dw.allowedAreas());
- QCOMPARE(spy.count(), 0);
+ QCOMPARE(spy.size(), 0);
dw.setAllowedAreas(Qt::TopDockWidgetArea | Qt::LeftDockWidgetArea);
QCOMPARE(dw.allowedAreas(), Qt::TopDockWidgetArea | Qt::LeftDockWidgetArea);
@@ -527,12 +551,12 @@ void tst_QDockWidget::allowedAreas()
QVERIFY(dw.isAreaAllowed(Qt::TopDockWidgetArea));
QVERIFY(!dw.isAreaAllowed(Qt::BottomDockWidgetArea));
//QVERIFY(!dw.isAreaAllowed(Qt::FloatingDockWidgetArea));
- QCOMPARE(spy.count(), 1);
+ QCOMPARE(spy.size(), 1);
QCOMPARE(*static_cast<const Qt::DockWidgetAreas *>(spy.at(0).value(0).constData()),
dw.allowedAreas());
spy.clear();
dw.setAllowedAreas(dw.allowedAreas());
- QCOMPARE(spy.count(), 0);
+ QCOMPARE(spy.size(), 0);
//dw.setAllowedAreas(Qt::BottomDockWidgetArea | Qt::FloatingDockWidgetArea);
dw.setAllowedAreas(Qt::BottomDockWidgetArea);
@@ -542,12 +566,12 @@ void tst_QDockWidget::allowedAreas()
QVERIFY(!dw.isAreaAllowed(Qt::TopDockWidgetArea));
QVERIFY(dw.isAreaAllowed(Qt::BottomDockWidgetArea));
//QVERIFY(dw.isAreaAllowed(Qt::FloatingDockWidgetArea));
- QCOMPARE(spy.count(), 1);
+ QCOMPARE(spy.size(), 1);
QCOMPARE(*static_cast<const Qt::DockWidgetAreas *>(spy.at(0).value(0).constData()),
dw.allowedAreas());
spy.clear();
dw.setAllowedAreas(dw.allowedAreas());
- QCOMPARE(spy.count(), 0);
+ QCOMPARE(spy.size(), 0);
dw.setAllowedAreas(Qt::BottomDockWidgetArea | Qt::RightDockWidgetArea);
QCOMPARE(dw.allowedAreas(), Qt::BottomDockWidgetArea | Qt::RightDockWidgetArea);
@@ -556,12 +580,12 @@ void tst_QDockWidget::allowedAreas()
QVERIFY(!dw.isAreaAllowed(Qt::TopDockWidgetArea));
QVERIFY(dw.isAreaAllowed(Qt::BottomDockWidgetArea));
//QVERIFY(!dw.isAreaAllowed(Qt::FloatingDockWidgetArea));
- QCOMPARE(spy.count(), 1);
+ QCOMPARE(spy.size(), 1);
QCOMPARE(*static_cast<const Qt::DockWidgetAreas *>(spy.at(0).value(0).constData()),
dw.allowedAreas());
spy.clear();
dw.setAllowedAreas(dw.allowedAreas());
- QCOMPARE(spy.count(), 0);
+ QCOMPARE(spy.size(), 0);
}
void tst_QDockWidget::toggleViewAction()
@@ -589,65 +613,65 @@ void tst_QDockWidget::visibilityChanged()
mw.addDockWidget(Qt::LeftDockWidgetArea, &dw);
mw.show();
- QCOMPARE(spy.count(), 1);
+ QCOMPARE(spy.size(), 1);
QCOMPARE(spy.at(0).at(0).toBool(), true);
spy.clear();
dw.hide();
- QCOMPARE(spy.count(), 1);
+ QCOMPARE(spy.size(), 1);
QCOMPARE(spy.at(0).at(0).toBool(), false);
spy.clear();
dw.hide();
- QCOMPARE(spy.count(), 0);
+ QCOMPARE(spy.size(), 0);
dw.show();
- QCOMPARE(spy.count(), 1);
+ QCOMPARE(spy.size(), 1);
QCOMPARE(spy.at(0).at(0).toBool(), true);
spy.clear();
dw.show();
- QCOMPARE(spy.count(), 0);
+ QCOMPARE(spy.size(), 0);
QDockWidget dw2;
mw.tabifyDockWidget(&dw, &dw2);
dw2.show();
dw2.raise();
- QCOMPARE(spy.count(), 1);
+ QCOMPARE(spy.size(), 1);
QCOMPARE(spy.at(0).at(0).toBool(), false);
spy.clear();
dw2.hide();
qApp->processEvents();
- QCOMPARE(spy.count(), 1);
+ QCOMPARE(spy.size(), 1);
QCOMPARE(spy.at(0).at(0).toBool(), true);
spy.clear();
dw2.show();
dw2.raise();
qApp->processEvents();
- QCOMPARE(spy.count(), 1);
+ QCOMPARE(spy.size(), 1);
QCOMPARE(spy.at(0).at(0).toBool(), false);
spy.clear();
dw.raise();
- QCOMPARE(spy.count(), 1);
+ QCOMPARE(spy.size(), 1);
QCOMPARE(spy.at(0).at(0).toBool(), true);
spy.clear();
dw.raise();
- QCOMPARE(spy.count(), 0);
+ QCOMPARE(spy.size(), 0);
dw2.raise();
- QCOMPARE(spy.count(), 1);
+ QCOMPARE(spy.size(), 1);
QCOMPARE(spy.at(0).at(0).toBool(), false);
spy.clear();
dw2.raise();
- QCOMPARE(spy.count(), 0);
+ QCOMPARE(spy.size(), 0);
mw.addDockWidget(Qt::RightDockWidgetArea, &dw2);
- QTRY_COMPARE(spy.count(), 1);
+ QTRY_COMPARE(spy.size(), 1);
QCOMPARE(spy.at(0).at(0).toBool(), true);
}
@@ -674,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);
@@ -687,6 +714,8 @@ void tst_QDockWidget::updateTabBarOnVisibilityChanged()
dw1->hide();
QTRY_COMPARE(tabBar->count(), 2);
QCOMPARE(tabBar->currentIndex(), 0);
+
+ QCOMPARE(mw.tabifiedDockWidgets(dw2), {dw3});
}
Q_DECLARE_METATYPE(Qt::DockWidgetArea)
@@ -701,56 +730,56 @@ void tst_QDockWidget::dockLocationChanged()
QSignalSpy spy(&dw, SIGNAL(dockLocationChanged(Qt::DockWidgetArea)));
mw.addDockWidget(Qt::LeftDockWidgetArea, &dw);
- QCOMPARE(spy.count(), 1);
+ QCOMPARE(spy.size(), 1);
QCOMPARE(qvariant_cast<Qt::DockWidgetArea>(spy.at(0).at(0)),
Qt::LeftDockWidgetArea);
spy.clear();
mw.addDockWidget(Qt::LeftDockWidgetArea, &dw);
- QCOMPARE(spy.count(), 1);
+ QCOMPARE(spy.size(), 1);
QCOMPARE(qvariant_cast<Qt::DockWidgetArea>(spy.at(0).at(0)),
Qt::LeftDockWidgetArea);
spy.clear();
mw.addDockWidget(Qt::RightDockWidgetArea, &dw);
- QCOMPARE(spy.count(), 1);
+ QCOMPARE(spy.size(), 1);
QCOMPARE(qvariant_cast<Qt::DockWidgetArea>(spy.at(0).at(0)),
Qt::RightDockWidgetArea);
spy.clear();
mw.removeDockWidget(&dw);
- QCOMPARE(spy.count(), 0);
+ QCOMPARE(spy.size(), 0);
QDockWidget dw2;
dw2.setObjectName("dock2");
mw.addDockWidget(Qt::TopDockWidgetArea, &dw2);
mw.tabifyDockWidget(&dw2, &dw);
- QCOMPARE(spy.count(), 1);
+ QCOMPARE(spy.size(), 1);
QCOMPARE(qvariant_cast<Qt::DockWidgetArea>(spy.at(0).at(0)),
Qt::TopDockWidgetArea);
spy.clear();
mw.splitDockWidget(&dw2, &dw, Qt::Horizontal);
- QCOMPARE(spy.count(), 1);
+ QCOMPARE(spy.size(), 1);
QCOMPARE(qvariant_cast<Qt::DockWidgetArea>(spy.at(0).at(0)),
Qt::TopDockWidgetArea);
spy.clear();
dw.setFloating(true);
- QTRY_COMPARE(spy.count(), 1);
+ QTRY_COMPARE(spy.size(), 1);
QCOMPARE(qvariant_cast<Qt::DockWidgetArea>(spy.at(0).at(0)),
Qt::NoDockWidgetArea);
spy.clear();
dw.setFloating(false);
- QTRY_COMPARE(spy.count(), 1);
+ QTRY_COMPARE(spy.size(), 1);
QCOMPARE(qvariant_cast<Qt::DockWidgetArea>(spy.at(0).at(0)),
Qt::TopDockWidgetArea);
spy.clear();
QByteArray ba = mw.saveState();
mw.restoreState(ba);
- QCOMPARE(spy.count(), 1);
+ QCOMPARE(spy.size(), 1);
QCOMPARE(qvariant_cast<Qt::DockWidgetArea>(spy.at(0).at(0)),
Qt::TopDockWidgetArea);
}
@@ -1021,9 +1050,9 @@ void tst_QDockWidget::task258459_visibilityChanged()
QSignalSpy spy2(&dock2, SIGNAL(visibilityChanged(bool)));
win.show();
QVERIFY(QTest::qWaitForWindowActive(&win));
- QCOMPARE(spy1.count(), 1);
+ QCOMPARE(spy1.size(), 1);
QCOMPARE(spy1.first().first().toBool(), false); //dock1 is invisible
- QCOMPARE(spy2.count(), 1);
+ QCOMPARE(spy2.size(), 1);
QCOMPARE(spy2.first().first().toBool(), true); //dock1 is visible
}
@@ -1073,9 +1102,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);
@@ -1086,6 +1116,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));
@@ -1095,12 +1126,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);
@@ -1115,6 +1150,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
@@ -1141,13 +1182,17 @@ void tst_QDockWidget::createTestWidgets(QMainWindow* &mainWindow, QPointer<QWidg
mainWindow->setDockOptions(QMainWindow::AllowTabbedDocks | QMainWindow::GroupedDragging);
mainWindow->move(m_topLeft);
+ const int minWidth = QApplication::style()->pixelMetric(QStyle::PM_TitleBarHeight);
+ const QSize minSize(minWidth, 2 * minWidth);
d1 = new QDockWidget(mainWindow);
+ d1->setMinimumSize(minSize);
d1->setWindowTitle("I am D1");
d1->setObjectName("D1");
d1->setFeatures(QDockWidget::DockWidgetFeatureMask);
d1->setAllowedAreas(Qt::DockWidgetArea::AllDockWidgetAreas);
d2 = new QDockWidget(mainWindow);
+ d2->setMinimumSize(minSize);
d2->setWindowTitle("I am D2");
d2->setObjectName("D2");
d2->setFeatures(QDockWidget::DockWidgetFeatureMask);
@@ -1158,7 +1203,7 @@ void tst_QDockWidget::createTestWidgets(QMainWindow* &mainWindow, QPointer<QWidg
d1->show();
d2->show();
mainWindow->show();
- QApplication::setActiveWindow(mainWindow);
+ QApplicationPrivate::setActiveWindow(mainWindow);
}
@@ -1170,7 +1215,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);
@@ -1181,18 +1226,28 @@ void tst_QDockWidget::moveDockWidget(QDockWidget* dw, QPoint to, QPoint from) co
// move and log
const QPoint source = dw->mapFromGlobal(from);
const QPoint target = dw->mapFromGlobal(to);
- qCDebug(lcQpaDockWidgets) << "Move" << dw->objectName() << "from" << source;
- qCDebug(lcQpaDockWidgets) << "Move" << dw->objectName() << "from" << from;
+ qCDebug(lcTestDockWidget) << "Move" << dw->objectName() << "from" << source;
+ qCDebug(lcTestDockWidget) << "Move" << dw->objectName() << "from" << from;
QTest::mousePress(dw, Qt::LeftButton, Qt::KeyboardModifiers(), source);
QTest::mouseMove(dw, target);
- qCDebug(lcQpaDockWidgets) << "Move" << dw->objectName() << "to" << target;
- qCDebug(lcQpaDockWidgets) << "Move" << dw->objectName() << "to" << to;
- QTest::mouseRelease(dw, Qt::LeftButton, Qt::KeyboardModifiers(), target);
- QTest::qWait(waitingTime);
+ qCDebug(lcTestDockWidget) << "Move" << dw->objectName() << "to" << target;
+ qCDebug(lcTestDockWidget) << "Move" << dw->objectName() << "to" << to;
+ 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));
+ return;
+ }
+ qCDebug(lcTestDockWidget) << "Aborting move and dropping at origin";
- // Verify WindowActive only for floating dock widgets
- if (dw->isFloating())
- QTRY_VERIFY(QTest::qWaitForWindowActive(dw));
+ // 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
@@ -1229,21 +1284,33 @@ void tst_QDockWidget::unplugAndResize(QMainWindow* mainWindow, QDockWidget* dw,
return;
}
+ // Remember size for comparison with unplugged object
+#ifdef Q_OS_LINUX
+ const int pluggedWidth = dw->width();
+ const int pluggedHeight = dw->height();
+#endif
+
// unplug and resize a dock Widget
- qCDebug(lcQpaDockWidgets) << "*** unplug and resize" << dw->objectName();
+ qCDebug(lcTestDockWidget) << "*** unplug and resize" << dw->objectName();
QPoint pos1 = dw->mapToGlobal(dw->rect().center());
pos1.rx() += mx;
pos1.ry() += my;
- moveDockWidget(dw, pos1, dw->mapToGlobal(dw->rect().center()));
- //QTest::mousePress(dw, Qt::LeftButton, Qt::KeyboardModifiers(), dw->mapFromGlobal(pos1));
+ moveDockWidget(dw, pos1, dw->mapToGlobal(dw->rect().center()), MoveDockWidgetRule::Drop);
QTRY_VERIFY(dw->isFloating());
- qCDebug(lcQpaDockWidgets) << "Resizing" << dw->objectName() << "to" << size;
+ // Unplugged object's size may differ max. by 2x frame size
+#ifdef Q_OS_LINUX
+ const int xMargin = 2 * dw->frameSize().width();
+ const int yMargin = 2 * dw->frameSize().height();
+ QVERIFY(dw->height() - pluggedHeight <= xMargin);
+ QVERIFY(dw->width() - pluggedWidth <= yMargin);
+#endif
+
+ qCDebug(lcTestDockWidget) << "Resizing" << dw->objectName() << "to" << size;
dw->setFixedSize(size);
QTest::qWait(waitingTime);
- qCDebug(lcQpaDockWidgets) << "Move" << dw->objectName() << "to its home" << dw->mapFromGlobal(home);
+ qCDebug(lcTestDockWidget) << "Move" << dw->objectName() << "to its home" << dw->mapFromGlobal(home);
dw->move(home);
- //moveDockWidget(dw, home);
}
bool tst_QDockWidget::checkFloatingTabs(QMainWindow* mainWindow, QPointer<QDockWidgetGroupWindow> &ftabs, const QList<QDockWidget*> &dwList) const
@@ -1253,62 +1320,115 @@ bool tst_QDockWidget::checkFloatingTabs(QMainWindow* mainWindow, QPointer<QDockW
// Check if mainWindow has a floatingTab child
ftabs = mainWindow->findChild<QDockWidgetGroupWindow*>();
if (ftabs.isNull()) {
- qCDebug(lcQpaDockWidgets) << "MainWindow has no DockWidgetGroupWindow" << mainWindow;
+ qCDebug(lcTestDockWidget) << "MainWindow has no DockWidgetGroupWindow" << mainWindow;
return false;
}
QTabBar* tab = ftabs->findChild<QTabBar*>();
if (!tab) {
- qCDebug(lcQpaDockWidgets) << "DockWidgetGroupWindow has no tab bar" << ftabs;
+ qCDebug(lcTestDockWidget) << "DockWidgetGroupWindow has no tab bar" << ftabs;
return false;
}
// both dock widgets must be direct children of the main window
const QList<QDockWidget*> children = ftabs->findChildren<QDockWidget*>(QString(), Qt::FindDirectChildrenOnly);
- if (dwList.count() > 0)
+ if (dwList.size() > 0)
{
- if (dwList.count() != children.count()) {
- qCDebug(lcQpaDockWidgets) << "Expected DockWidgetGroupWindow children:" << dwList.count()
- << "Children found:" << children.count();
+ if (dwList.size() != children.size()) {
+ qCDebug(lcTestDockWidget) << "Expected DockWidgetGroupWindow children:" << dwList.size()
+ << "Children found:" << children.size();
- qCDebug(lcQpaDockWidgets) << "Expected:" << dwList;
- qCDebug(lcQpaDockWidgets) << "Found in" << ftabs << ":" << children.count();
+ qCDebug(lcTestDockWidget) << "Expected:" << dwList;
+ qCDebug(lcTestDockWidget) << "Found in" << ftabs << ":" << children.size();
return false;
}
for (const QDockWidget* child : dwList) {
if (!children.contains(child)) {
- qCDebug(lcQpaDockWidgets) << "Expected child" << child << "not found in" << children;
+ qCDebug(lcTestDockWidget) << "Expected child" << child << "not found in" << children;
return false;
}
}
}
// Always select first tab position
- qCDebug(lcQpaDockWidgets) << "click on first tab";
+ qCDebug(lcTestDockWidget) << "click on first tab";
QTest::mouseClick(tab, Qt::LeftButton, Qt::KeyboardModifiers(), tab->tabRect(0).center());
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> &cent,
+ 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
void tst_QDockWidget::floatingTabs()
{
+ 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
@@ -1318,33 +1438,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(lcQpaDockWidgets) << "*** move d1 dock over d2 dock ***";
- qCDebug(lcQpaDockWidgets) << "**********(test plugging)*************";
- qCDebug(lcQpaDockWidgets) << "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())
@@ -1352,24 +1459,38 @@ 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
*/
// limitation: QTest cannot handle drag to unplug.
// reason: Object under mouse mutates from QTabBar::tab to QDockWidget. QTest cannot handle that.
// => click float button to unplug
- qCDebug(lcQpaDockWidgets) << "*** test unplugging from floating dock ***";
+ qCDebug(lcTestDockWidget) << "*** test unplugging from floating dock ***";
// QDockWidget must have a QAbstractButton with object name "qt_dockwidget_floatbutton"
QAbstractButton* floatButton = d1->findChild<QAbstractButton*>("qt_dockwidget_floatbutton", Qt::FindDirectChildrenOnly);
QTRY_VERIFY(floatButton != nullptr);
QPoint pos1 = floatButton->rect().center();
- qCDebug(lcQpaDockWidgets) << "unplug d1" << pos1;
+ qCDebug(lcTestDockWidget) << "unplug d1" << pos1;
QTest::mouseClick(floatButton, Qt::LeftButton, Qt::KeyboardModifiers(), pos1);
QTest::qWait(waitingTime);
@@ -1378,14 +1499,13 @@ void tst_QDockWidget::floatingTabs()
QTRY_VERIFY(!d2->isFloating());
// Plug back into dock areas
- qCDebug(lcQpaDockWidgets) << "*** test plugging back to dock areas ***";
- qCDebug(lcQpaDockWidgets) << "Move d1 to left dock";
- //moveDockWidget(d1, d1->mapFrom(MainWindow, dockPoint(MainWindow, Qt::LeftDockWidgetArea)));
- moveDockWidget(d1, dockPoint(mainWindow, Qt::LeftDockWidgetArea));
- qCDebug(lcQpaDockWidgets) << "Move d2 to right dock";
- moveDockWidget(d2, dockPoint(mainWindow, Qt::RightDockWidgetArea));
-
- qCDebug(lcQpaDockWidgets) << "Waiting" << waitBeforeClose << "ms before plugging back.";
+ qCDebug(lcTestDockWidget) << "*** test plugging back to dock areas ***";
+ qCDebug(lcTestDockWidget) << "Move d1 to left dock";
+ moveDockWidget(d1, dockPoint(mainWindow, Qt::LeftDockWidgetArea), QPoint(), MoveDockWidgetRule::Drop);
+ qCDebug(lcTestDockWidget) << "Move d2 to right dock";
+ moveDockWidget(d2, dockPoint(mainWindow, Qt::RightDockWidgetArea), QPoint(), MoveDockWidgetRule::Drop);
+
+ qCDebug(lcTestDockWidget) << "Waiting" << waitBeforeClose << "ms before plugging back.";
QTest::qWait(waitBeforeClose);
// Both dock widgets must no longer be floating
@@ -1397,13 +1517,98 @@ void tst_QDockWidget::floatingTabs()
QTRY_VERIFY(ftabs.isNull());
// Check if paths are consistent
- qCDebug(lcQpaDockWidgets) << "Checking path consistency" << layout->layoutState.indexOf(d1) << layout->layoutState.indexOf(d2);
+ QMainWindowLayout* layout = qobject_cast<QMainWindowLayout *>(mainWindow->layout());
+ qCDebug(lcTestDockWidget) << "Checking path consistency" << layout->layoutState.indexOf(d1) << layout->layoutState.indexOf(d2);
+
+ // Paths must be identical
+ QTRY_COMPARE(layout->layoutState.indexOf(d1), path1);
+ QTRY_COMPARE(layout->layoutState.indexOf(d2), path2);
+
+ 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
+}
- // Path1 must be identical
- QTRY_VERIFY(path1 == layout->layoutState.indexOf(d1));
+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);
- // d1 must have a gap item due to size change
- QTRY_VERIFY(layout->layoutState.indexOf(d2) == QList<int>() << path2 << 0);
+ // 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
@@ -1412,6 +1617,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;
@@ -1427,14 +1634,14 @@ void tst_QDockWidget::hideAndShow()
std::unique_ptr<QMainWindow> up_mainWindow(mainWindow);
// Check hiding of docked widgets
- qCDebug(lcQpaDockWidgets) << "Hiding mainWindow with plugged dock widgets" << mainWindow;
+ qCDebug(lcTestDockWidget) << "Hiding mainWindow with plugged dock widgets" << mainWindow;
mainWindow->hide();
QXCBVERIFY(!mainWindow->isVisible());
QXCBVERIFY(!d1->isVisible());
QXCBVERIFY(!d2->isVisible());
// Check showing everything again
- qCDebug(lcQpaDockWidgets) << "Showing mainWindow with plugged dock widgets" << mainWindow;
+ qCDebug(lcTestDockWidget) << "Showing mainWindow with plugged dock widgets" << mainWindow;
mainWindow->show();
QXCBVERIFY(QTest::qWaitForWindowActive(mainWindow));
QXCBVERIFY(QTest::qWaitForWindowExposed(mainWindow));
@@ -1454,8 +1661,8 @@ void tst_QDockWidget::hideAndShow()
unplugAndResize(mainWindow, d1, home1(mainWindow), size1(mainWindow));
unplugAndResize(mainWindow, d2, home2(mainWindow), size2(mainWindow));
- // Check hiding of undocked widgets
- qCDebug(lcQpaDockWidgets) << "Hiding mainWindow with unplugged dock widgets" << mainWindow;
+ // Check hiding of undocked widgets
+ qCDebug(lcTestDockWidget) << "Hiding mainWindow with unplugged dock widgets" << mainWindow;
mainWindow->hide();
QTRY_VERIFY(!mainWindow->isVisible());
QTRY_VERIFY(d1->isVisible());
@@ -1465,7 +1672,17 @@ void tst_QDockWidget::hideAndShow()
QTRY_VERIFY(!d1->isVisible());
QTRY_VERIFY(!d2->isVisible());
- qCDebug(lcQpaDockWidgets) << "Waiting" << waitBeforeClose << "ms before closing.";
+
+ // 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
QSKIP("test requires -developer-build option");
@@ -1475,6 +1692,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;
@@ -1489,8 +1708,8 @@ void tst_QDockWidget::closeAndDelete()
unplugAndResize(mainWindow, d2, home2(mainWindow), size2(mainWindow));
// Create a floating tab and unplug it again
- qCDebug(lcQpaDockWidgets) << "Move d1 over d2";
- moveDockWidget(d1, d2->mapToGlobal(d2->rect().center()));
+ qCDebug(lcTestDockWidget) << "Move d1 over d2";
+ 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
@@ -1502,8 +1721,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());
@@ -1512,21 +1733,14 @@ 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(lcQpaDockWidgets) << "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(lcQpaDockWidgets) << "Deleting mainWindow";
+ qCDebug(lcTestDockWidget) << "Deleting mainWindow";
up_mainWindow.reset();
QTRY_VERIFY(d1.isNull());
QTRY_VERIFY(d2.isNull());
@@ -1536,9 +1750,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
@@ -1567,7 +1801,7 @@ void tst_QDockWidget::dockPermissions()
// both dock widgets must be direct children of the main window
{
const QList<QDockWidget*> children = mainWindow->findChildren<QDockWidget*>(QString(), Qt::FindDirectChildrenOnly);
- QTRY_VERIFY(children.count() == 2);
+ QTRY_VERIFY(children.size() == 2);
for (const QDockWidget* child : children)
QTRY_VERIFY(child == d1 || child == d2);
}
@@ -1576,28 +1810,187 @@ void tst_QDockWidget::dockPermissions()
QTRY_VERIFY(mainWindow->findChild<QDockWidgetGroupWindow*>() == nullptr);
// Test unpermitted dock areas with d2
- qCDebug(lcQpaDockWidgets) << "*** move d2 to forbidden docks ***";
+ qCDebug(lcTestDockWidget) << "*** move d2 to forbidden docks ***";
// Move d2 to non allowed dock areas and verify it remains floating
- qCDebug(lcQpaDockWidgets) << "Move d2 to top dock";
- moveDockWidget(d2, dockPoint(mainWindow, Qt::TopDockWidgetArea));
+ qCDebug(lcTestDockWidget) << "Move d2 to top dock";
+ moveDockWidget(d2, dockPoint(mainWindow, Qt::TopDockWidgetArea), QPoint(), MoveDockWidgetRule::Drop);
QTRY_VERIFY(d2->isFloating());
- qCDebug(lcQpaDockWidgets) << "Move d2 to left dock";
+ 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(lcQpaDockWidgets) << "Move d2 to bottom dock";
- moveDockWidget(d2, dockPoint(mainWindow, Qt::BottomDockWidgetArea));
+ qCDebug(lcTestDockWidget) << "Move d2 to bottom dock";
+ moveDockWidget(d2, dockPoint(mainWindow, Qt::BottomDockWidgetArea), QPoint(), MoveDockWidgetRule::Drop);
QTRY_VERIFY(d2->isFloating());
- qCDebug(lcQpaDockWidgets) << "Waiting" << waitBeforeClose << "ms before closing.";
+ qCDebug(lcTestDockWidget) << "Waiting" << waitBeforeClose << "ms before closing.";
QTest::qWait(waitBeforeClose);
#else
QSKIP("test requires -developer-build option");
#endif // QT_BUILD_INTERNAL
}
+/*!
+ \internal
+
+ This test checks consistency of QMainWindow::saveState() / QMainWindow::restoreState().
+ These methods (de)serialize dock widget properties via a QDataStream into a QByteArray.
+
+ If the logic of (de)serializing Qt datatypes and classes changes, old settings can fail
+ to restore properly without triggering warnings or assertions.
+
+ The test consists of two parts:
+ \list 1
+ \li Read properties from a hard coded byte array and check if it is deserialized correctly.
+ \li Serialize properties into a \a QByteArray and check if it is serialized correctly.
+ \endlist
+*/
+void tst_QDockWidget::saveAndRestore()
+{
+ 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
+#ifndef QT_BUILD_INTERNAL
+ QSKIP("test requires -developer-build option");
+#else
+
+ // Hard coded byte array for test initialization
+ const QByteArray testArray = QByteArrayLiteral(
+ "\x00\x00\x00\xFF\x00\x00\x00\x00\xFD\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x13\x00\x00\x05\xE8\xFC\x02\x00\x00\x00\x01\xFB\x00\x00\x00\x04\x00"
+ "D\x00"
+ "1\x03\x00\x00\x01\f\x00\x00\x00\x97\x00\x00\x02\x19\x00\x00\x01z\x00\x00\x00\x01\x00\x00\x00\x13\x00\x00\x05\xE8\xFC\x02\x00\x00\x00\x01\xFB\x00\x00\x00\x04\x00"
+ "D\x00"
+ "2\x03\x00\x00\x06L\x00\x00\x00\xFF\x00\x00\x01\f\x00\x00\x00\xE2\x00\x00\n\x80\x00\x00\x05\xE8\x00\x00\x00\x04\x00\x00\x00\x04\x00\x00\x00\b\x00\x00\x00\b\xFC\x00\x00\x00\x00"
+ );
+
+ QByteArray referenceArray; // Copy of testArray, corrected for current screen limits
+ QPoint topLeft1; // Top left point of dock widget d1
+ QPoint topLeft2; // Top left point of dock widget d2
+ QSize widgetSize1; // Size of dock widget d1
+ QSize widgetSize2; // Size of dock widget d2
+ bool isFloating1; // Floating status of dock widget d1
+ bool isFloating2; // Floating status of dock widget d2
+
+ // Create a mainwindow with a central widget and two dock widgets.
+ // Import properties from hard coded byte array.
+ // Use a scope to delete objects from screen after test.
+ {
+ QPointer<QDockWidget> d1;
+ QPointer<QDockWidget> d2;
+ QPointer<QWidget> cent;
+ QMainWindow* mainWindow;
+ createTestWidgets(mainWindow, cent, d1, d2);
+
+ // Failure to restore properties might lead to inconsistencies and crash.
+ // To leave a clean environment when the test inexpectedly goes out of scope,
+ // => store main window pointer in a std::unique_ptr
+ std::unique_ptr<QMainWindow> up_mainWindow(mainWindow);
+
+ // Restore, wait for events to be processed
+ mainWindow->restoreState(testArray);
+ QVERIFY(QTest::qWaitForWindowExposed(d1));
+ QVERIFY(QTest::qWaitForWindowExposed(d2));
+
+ // Serialized dock widget positions and sizes might be overridden due
+ // screen size limitations => do not check them here.
+ // If the test fails between here and scope end, serialization format/sequence have changed
+ QTRY_VERIFY(d1->isFloating());
+ QTRY_VERIFY(d2->isFloating());
+
+ // Hide main window and save their floating status.
+ // Reason:
+ // - KDE window managers do not take control over dock widgets.
+ // => They always close with the main window.
+ // - Some non KDE window managers do take control over dock widgets.
+ // => They prevent them from closing with the main window (QTBUG-103474).
+ // If properties are restored correctly, closing behavior must be consistent
+ // throughout this test.
+ mainWindow->hide();
+ // FIXME: No method exists in 6.5 to wait for a window to be hidden.
+ // => wait and hope the best, replace with qWaitForWindowHidden once implemented.
+ QTest::qWait(200);
+ isFloating1 = d1->isFloating();
+ isFloating2 = d2->isFloating();
+ }
+
+ // Create a mainwindow with a central widget and two dock widgets.
+ // Assign different properties to each dock widgets.
+ // Write properties to a byte array.
+ // Remember position and size properties for comparison.
+ // Use a scope to delete objects from screen after test.
+ {
+ QPointer<QDockWidget> d1;
+ QPointer<QDockWidget> d2;
+ QPointer<QWidget> cent;
+ QMainWindow* mainWindow;
+ createTestWidgets(mainWindow, cent, d1, d2);
+ std::unique_ptr<QMainWindow> up_mainWindow(mainWindow);
+
+ // unplug, position and resize both dock widgets relative to screen size
+ unplugAndResize(mainWindow, d1, home1(mainWindow), size1(mainWindow));
+ unplugAndResize(mainWindow, d2, home2(mainWindow), size2(mainWindow));
+
+ topLeft1 = d1->pos();
+ topLeft2 = d2->pos();
+ widgetSize1 = d1->size();
+ widgetSize2 = d2->size();
+
+ // save properties, potentially corrected for screen limits
+ referenceArray = mainWindow->saveState();
+
+ // Check closing behavior consistency
+ mainWindow->hide();
+ QTRY_VERIFY(d1->isFloating());
+ QTRY_VERIFY(d2->isFloating());
+ QCOMPARE(d1->isFloating(), isFloating1);
+ QCOMPARE(d2->isFloating(), isFloating2);
+ }
+
+ // Create a new main window, central window and two dock widgets.
+ QPointer<QDockWidget> d1;
+ QPointer<QDockWidget> d2;
+ QPointer<QWidget> cent;
+ QMainWindow* mainWindow;
+ createTestWidgets(mainWindow, cent, d1, d2);
+
+ // Failure to restore properties might lead to inconsistencies and crash.
+ // To leave a clean environment when the test inexpectedly goes out of scope,
+ // - store main window pointer in a std::unique_ptr
+ std::unique_ptr<QMainWindow> up_mainWindow(mainWindow);
+
+ // Restore properties and wait for events to be processed
+ mainWindow->restoreState(referenceArray);
+ QVERIFY(QTest::qWaitForWindowExposed(d1));
+ QVERIFY(QTest::qWaitForWindowExposed(d2));
+
+ // Compare positions, sizes and floating status
+ // If the test fails in the following 12 lines,
+ // the de-serialization format/sequence have changed
+ QCOMPARE(topLeft1, d1->pos());
+ QCOMPARE(topLeft2, d2->pos());
+ QCOMPARE(widgetSize1, d1->size());
+ QCOMPARE(widgetSize2, d2->size());
+ QVERIFY(d1->isFloating());
+ QVERIFY(d2->isFloating());
+
+ // Serialize again to compare all remaining properties
+ const QByteArray comparisonArray = mainWindow->saveState();
+ QCOMPARE(comparisonArray, referenceArray);
+
+ // Check closing behavior consistency
+ mainWindow->hide();
+ QTRY_VERIFY(d1->isFloating());
+ QTRY_VERIFY(d2->isFloating());
+ QCOMPARE(d1->isFloating(), isFloating1);
+ QCOMPARE(d2->isFloating(), isFloating2);
+
+#endif // QT_BUILD_INTERNAL
+}
+
QTEST_MAIN(tst_QDockWidget)
#include "tst_qdockwidget.moc"