diff options
author | Volker Hilsheimer <volker.hilsheimer@qt.io> | 2021-10-04 21:39:31 +0200 |
---|---|---|
committer | Volker Hilsheimer <volker.hilsheimer@qt.io> | 2021-10-16 09:46:22 +0200 |
commit | 32edae5e268b968aff82f0713612eff2feffb4e1 (patch) | |
tree | fe97e538232807fdd5e87a0312f7cf94ffa81087 /tests | |
parent | 553a1c48fd7a9078c1a33ad0829eaa84f0afb670 (diff) |
Fix restoring main window state for maximized/fullscreen windows
On systems that asynchronously resize the window to maximized or full
screen state, the window will become visible in its normal geometry
before it gets the final size by the windowing system. This might cause
multiple resize events, to each of which the widget's layout responds
with a call to its setGeometry implementation.
The QMainWindowLayout is special in that it will shrink dock widgets if
there is not enough space for them, but it doesn't grow them back once
there is. With the initial resize event being for a smaller size than
what was restored, the state is not restored correctly, but remains in
the state that fit into the smallest size with which setGeometry got
called.
To fix this, we have to keep the restored state around until the window
either gets a size that is large enough for it to fit, or until we can
be reasonably certain that the windowing system is done resizing the
window while transitioning it to the maximized or full screen state.
Since across the various platforms and windowing systems there is no
reliable way to know when the window reaches its final size, we have
to use a timer that we (re)start for each call to setGeometry with a
size that's not large enough. Once the timer times out, we have to
give up; then the last layout state calculated is the final state.
To calculate the size of the layout, introduce a function to the
QDockAreaLayout that returns the size required for the current sizes
of the docks. Refactor sizeHint and minimumSize (which were identical)
into a helper template that takes member-function pointers to call the
respective method from the dock area layout's content items.
Add a test case for various permutations of the scenario. The timeout
of 150ms is based on running this test case repeatedly on various
desktop platforms and X11 window managers.
Fixes: QTBUG-46620
Change-Id: I489675c2c40d3308ac8194aeb4267172b2fb38be
Reviewed-by: Albert Astals Cid <albert.astals.cid@kdab.com>
Reviewed-by: Lars Knoll <lars.knoll@qt.io>
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Diffstat (limited to 'tests')
-rw-r--r-- | tests/auto/widgets/widgets/qmainwindow/tst_qmainwindow.cpp | 65 |
1 files changed, 65 insertions, 0 deletions
diff --git a/tests/auto/widgets/widgets/qmainwindow/tst_qmainwindow.cpp b/tests/auto/widgets/widgets/qmainwindow/tst_qmainwindow.cpp index b31acb2b7a..e30df32d5d 100644 --- a/tests/auto/widgets/widgets/qmainwindow/tst_qmainwindow.cpp +++ b/tests/auto/widgets/widgets/qmainwindow/tst_qmainwindow.cpp @@ -126,6 +126,8 @@ private slots: void dockWidgetArea(); void restoreState(); void restoreStateFromPreviousVersion(); + void restoreStateSizeChanged_data(); + void restoreStateSizeChanged(); void createPopupMenu(); void hideBeforeLayout(); #ifdef QT_BUILD_INTERNAL @@ -1391,6 +1393,67 @@ void tst_QMainWindow::restoreStateFromPreviousVersion() } +void tst_QMainWindow::restoreStateSizeChanged_data() +{ + QTest::addColumn<Qt::WindowState>("saveState"); + QTest::addColumn<Qt::WindowState>("showState"); + QTest::addColumn<bool>("sameSize"); + + QTest::addRow("fullscreen") << Qt::WindowFullScreen << Qt::WindowFullScreen << true; + QTest::addRow("maximized") << Qt::WindowMaximized << Qt::WindowMaximized << true; + QTest::addRow("maximized->normal") << Qt::WindowMaximized << Qt::WindowNoState << false; + QTest::addRow("fullscreen->normal") << Qt::WindowFullScreen << Qt::WindowNoState << false; + QTest::addRow("fullscreen->maximized") << Qt::WindowFullScreen << Qt::WindowMaximized << false; + QTest::addRow("maximized->fullscreen") << Qt::WindowMaximized << Qt::WindowFullScreen << true; +} + +void tst_QMainWindow::restoreStateSizeChanged() +{ + QFETCH(Qt::WindowState, saveState); + QFETCH(Qt::WindowState, showState); + QFETCH(bool, sameSize); + + auto createMainWindow = []{ + QMainWindow *mainWindow = new QMainWindow; + mainWindow->move(QGuiApplication::primaryScreen()->availableGeometry().topLeft()); + mainWindow->setCentralWidget(new QLabel("X")); + QDockWidget *dockWidget = new QDockWidget; + dockWidget->setObjectName("Dock Widget"); + mainWindow->addDockWidget(Qt::LeftDockWidgetArea, dockWidget); + return mainWindow; + }; + + QByteArray geometryData; + QByteArray stateData; + int dockWidgetWidth = 0; + QRect normalGeometry; + + { + auto mainWindow = QScopedPointer<QMainWindow>(createMainWindow()); + mainWindow->setWindowState(saveState); + mainWindow->show(); + QVERIFY(QTest::qWaitForWindowExposed(mainWindow.data())); + dockWidgetWidth = mainWindow->width() - 100; + QDockWidget *dockWidget = mainWindow->findChild<QDockWidget*>("Dock Widget"); + mainWindow->resizeDocks({dockWidget}, {dockWidgetWidth}, Qt::Horizontal); + geometryData = mainWindow->saveGeometry(); + stateData = mainWindow->saveState(); + normalGeometry = mainWindow->normalGeometry(); + } + + auto mainWindow = QScopedPointer<QMainWindow>(createMainWindow()); + mainWindow->restoreGeometry(geometryData); + mainWindow->restoreState(stateData); + mainWindow->setWindowState(showState); + mainWindow->show(); + QVERIFY(QTest::qWaitForWindowExposed(mainWindow.data())); + + QDockWidget *dockWidget = mainWindow->findChild<QDockWidget*>("Dock Widget"); + QVERIFY(dockWidget); + QCOMPARE(mainWindow->normalGeometry().size(), normalGeometry.size()); + if (sameSize) + QTRY_COMPARE(dockWidget->width(), dockWidgetWidth); +} void tst_QMainWindow::createPopupMenu() { @@ -1692,6 +1755,7 @@ void tst_QMainWindow::saveRestore() adw.apply(&mainWindow); mainWindow.show(); + mainWindow.restoreState(stateData); COMPARE_DOCK_WIDGET_GEOS(dockWidgetGeos, dockWidgetGeometries(&mainWindow)); @@ -1710,6 +1774,7 @@ void tst_QMainWindow::saveRestore() mainWindow.restoreState(stateData); mainWindow.show(); + QVERIFY(QTest::qWaitForWindowExposed(&mainWindow)); COMPARE_DOCK_WIDGET_GEOS(dockWidgetGeos, dockWidgetGeometries(&mainWindow)); } } |