summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/widgets/widgets/qdockarealayout.cpp72
-rw-r--r--src/widgets/widgets/qdockarealayout_p.h3
-rw-r--r--src/widgets/widgets/qmainwindowlayout.cpp73
-rw-r--r--src/widgets/widgets/qmainwindowlayout_p.h6
-rw-r--r--tests/auto/widgets/widgets/qmainwindow/tst_qmainwindow.cpp65
5 files changed, 169 insertions, 50 deletions
diff --git a/src/widgets/widgets/qdockarealayout.cpp b/src/widgets/widgets/qdockarealayout.cpp
index 5a144224db..626710cd54 100644
--- a/src/widgets/widgets/qdockarealayout.cpp
+++ b/src/widgets/widgets/qdockarealayout.cpp
@@ -2914,7 +2914,8 @@ void QDockAreaLayout::clear()
centralWidgetRect = QRect();
}
-QSize QDockAreaLayout::sizeHint() const
+template<typename SizePMF, typename CenterPMF>
+QSize QDockAreaLayout::size_helper(SizePMF sizeFn, CenterPMF centerFn) const
{
int left_sep = 0;
int right_sep = 0;
@@ -2928,11 +2929,12 @@ QSize QDockAreaLayout::sizeHint() const
bottom_sep = docks[QInternal::BottomDock].isEmpty() ? 0 : sep;
}
- QSize left = docks[QInternal::LeftDock].sizeHint() + QSize(left_sep, 0);
- QSize right = docks[QInternal::RightDock].sizeHint() + QSize(right_sep, 0);
- QSize top = docks[QInternal::TopDock].sizeHint() + QSize(0, top_sep);
- QSize bottom = docks[QInternal::BottomDock].sizeHint() + QSize(0, bottom_sep);
- QSize center = centralWidgetItem == nullptr ? QSize(0, 0) : centralWidgetItem->sizeHint();
+ const QSize left = (docks[QInternal::LeftDock].*sizeFn)() + QSize(left_sep, 0);
+ const QSize right = (docks[QInternal::RightDock].*sizeFn)() + QSize(right_sep, 0);
+ const QSize top = (docks[QInternal::TopDock].*sizeFn)() + QSize(0, top_sep);
+ const QSize bottom = (docks[QInternal::BottomDock].*sizeFn)() + QSize(0, bottom_sep);
+ const QSize center = centralWidgetItem == nullptr
+ ? QSize(0, 0) : (centralWidgetItem->*centerFn)();
int row1 = top.width();
int row2 = left.width() + center.width() + right.width();
@@ -2964,54 +2966,24 @@ QSize QDockAreaLayout::sizeHint() const
return QSize(qMax(row1, row2, row3), qMax(col1, col2, col3));
}
-QSize QDockAreaLayout::minimumSize() const
+QSize QDockAreaLayout::sizeHint() const
{
- int left_sep = 0;
- int right_sep = 0;
- int top_sep = 0;
- int bottom_sep = 0;
-
- if (centralWidgetItem != nullptr) {
- left_sep = docks[QInternal::LeftDock].isEmpty() ? 0 : sep;
- right_sep = docks[QInternal::RightDock].isEmpty() ? 0 : sep;
- top_sep = docks[QInternal::TopDock].isEmpty() ? 0 : sep;
- bottom_sep = docks[QInternal::BottomDock].isEmpty() ? 0 : sep;
- }
-
- QSize left = docks[QInternal::LeftDock].minimumSize() + QSize(left_sep, 0);
- QSize right = docks[QInternal::RightDock].minimumSize() + QSize(right_sep, 0);
- QSize top = docks[QInternal::TopDock].minimumSize() + QSize(0, top_sep);
- QSize bottom = docks[QInternal::BottomDock].minimumSize() + QSize(0, bottom_sep);
- QSize center = centralWidgetItem == nullptr ? QSize(0, 0) : centralWidgetItem->minimumSize();
-
- int row1 = top.width();
- int row2 = left.width() + center.width() + right.width();
- int row3 = bottom.width();
- int col1 = left.height();
- int col2 = top.height() + center.height() + bottom.height();
- int col3 = right.height();
-
- if (corners[Qt::TopLeftCorner] == Qt::LeftDockWidgetArea)
- row1 += left.width();
- else
- col1 += top.height();
-
- if (corners[Qt::TopRightCorner] == Qt::RightDockWidgetArea)
- row1 += right.width();
- else
- col3 += top.height();
+ return size_helper(&QDockAreaLayoutInfo::sizeHint, &QLayoutItem::sizeHint);
+}
- if (corners[Qt::BottomLeftCorner] == Qt::LeftDockWidgetArea)
- row3 += left.width();
- else
- col1 += bottom.height();
+QSize QDockAreaLayout::minimumSize() const
+{
+ return size_helper(&QDockAreaLayoutInfo::minimumSize, &QLayoutItem::minimumSize);
+}
- if (corners[Qt::BottomRightCorner] == Qt::RightDockWidgetArea)
- row3 += right.width();
- else
- col3 += bottom.height();
+/*!
+ \internal
- return QSize(qMax(row1, row2, row3), qMax(col1, col2, col3));
+ Returns the smallest size that doesn't change the size of any of the dock areas.
+*/
+QSize QDockAreaLayout::minimumStableSize() const
+{
+ return size_helper(&QDockAreaLayoutInfo::size, &QLayoutItem::minimumSize);
}
/*! \internal
diff --git a/src/widgets/widgets/qdockarealayout_p.h b/src/widgets/widgets/qdockarealayout_p.h
index 171147c254..57572bcfa1 100644
--- a/src/widgets/widgets/qdockarealayout_p.h
+++ b/src/widgets/widgets/qdockarealayout_p.h
@@ -271,6 +271,9 @@ public:
QSize sizeHint() const;
QSize minimumSize() const;
+ QSize minimumStableSize() const;
+ template<typename SizePMF, typename CenterPMF>
+ QSize size_helper(SizePMF sizeFn, CenterPMF centerFn) const;
void addDockWidget(QInternal::DockPosition pos, QDockWidget *dockWidget, Qt::Orientation orientation);
bool restoreDockWidget(QDockWidget *dockWidget);
diff --git a/src/widgets/widgets/qmainwindowlayout.cpp b/src/widgets/widgets/qmainwindowlayout.cpp
index 18dd05e63b..60db982cb7 100644
--- a/src/widgets/widgets/qmainwindowlayout.cpp
+++ b/src/widgets/widgets/qmainwindowlayout.cpp
@@ -675,6 +675,31 @@ QSize QMainWindowLayoutState::minimumSize() const
return result;
}
+/*!
+ \internal
+
+ Returns whether the layout fits into the main window.
+*/
+bool QMainWindowLayoutState::fits() const
+{
+ Q_ASSERT(mainWindow);
+
+ QSize size;
+
+#if QT_CONFIG(dockwidget)
+ size = dockAreaLayout.minimumStableSize();
+#endif
+
+#if QT_CONFIG(toolbar)
+ size.rwidth() += toolBarAreaLayout.docks[QInternal::LeftDock].rect.width();
+ size.rwidth() += toolBarAreaLayout.docks[QInternal::RightDock].rect.width();
+ size.rheight() += toolBarAreaLayout.docks[QInternal::TopDock].rect.height();
+ size.rheight() += toolBarAreaLayout.docks[QInternal::BottomDock].rect.height();
+#endif
+
+ return size.width() <= mainWindow->width() && size.height() <= mainWindow->height();
+}
+
void QMainWindowLayoutState::apply(bool animated)
{
#if QT_CONFIG(toolbar)
@@ -1974,11 +1999,47 @@ void QMainWindowLayout::setGeometry(const QRect &_r)
r.setBottom(sbr.top() - 1);
}
+ if (restoredState) {
+ /*
+ The main window was hidden and was going to be maximized or full-screened when
+ the state was restored. The state might have been for a larger window size than
+ the current size (in _r), and the window might still be in the process of being
+ shown and transitioning to the final size (there's no reliable way of knowing
+ this across different platforms). Try again with the restored state.
+ */
+ layoutState = *restoredState;
+ if (restoredState->fits()) {
+ restoredState.reset();
+ discardRestoredStateTimer.stop();
+ } else {
+ /*
+ Try again in the next setGeometry call, but discard the restored state
+ after 150ms without any further tries. That's a reasonably short amount of
+ time during which we can expect the windowing system to either have completed
+ showing the window, or resized the window once more (which then restarts the
+ timer in timerEvent).
+ If the windowing system is done, then the user won't have had a chance to
+ change the layout interactively AND trigger another resize.
+ */
+ discardRestoredStateTimer.start(150, this);
+ }
+ }
+
layoutState.rect = r;
+
layoutState.fitLayout();
applyState(layoutState, false);
}
+void QMainWindowLayout::timerEvent(QTimerEvent *e)
+{
+ if (e->timerId() == discardRestoredStateTimer.timerId()) {
+ discardRestoredStateTimer.stop();
+ restoredState.reset();
+ }
+ QLayout::timerEvent(e);
+}
+
void QMainWindowLayout::addItem(QLayoutItem *)
{ qWarning("QMainWindowLayout::addItem: Please use the public QMainWindow API instead"); }
@@ -2781,6 +2842,18 @@ bool QMainWindowLayout::restoreState(QDataStream &stream)
if (parentWidget()->isVisible()) {
layoutState.fitLayout();
applyState(layoutState, false);
+ } else {
+ /*
+ The state might not fit into the size of the widget as it gets shown, but
+ if the window is expected to be maximized or full screened, then we might
+ get several resizes as part of that transition, at the end of which the
+ state might fit. So keep the restored state around for now and try again
+ later in setGeometry.
+ */
+ if ((parentWidget()->windowState() & (Qt::WindowFullScreen | Qt::WindowMaximized))
+ && !layoutState.fits()) {
+ restoredState.reset(new QMainWindowLayoutState(layoutState));
+ }
}
savedState.deleteAllLayoutItems();
diff --git a/src/widgets/widgets/qmainwindowlayout_p.h b/src/widgets/widgets/qmainwindowlayout_p.h
index 20518bc2c9..55385cc164 100644
--- a/src/widgets/widgets/qmainwindowlayout_p.h
+++ b/src/widgets/widgets/qmainwindowlayout_p.h
@@ -414,6 +414,7 @@ public:
QSize sizeHint() const;
QSize minimumSize() const;
+ bool fits() const;
void fitLayout();
QLayoutItem *itemAt(int index, int *x) const;
@@ -451,6 +452,7 @@ class Q_AUTOTEST_EXPORT QMainWindowLayout
public:
QMainWindowLayoutState layoutState, savedState;
+ std::unique_ptr<QMainWindowLayoutState> restoredState;
QMainWindowLayout(QMainWindow *mainwindow, QLayout *parentLayout);
~QMainWindowLayout();
@@ -546,6 +548,7 @@ public:
};
void saveState(QDataStream &stream) const;
bool restoreState(QDataStream &stream);
+ QBasicTimer discardRestoredStateTimer;
// QLayout interface
@@ -584,6 +587,9 @@ public:
void restore(bool keepSavedState = false);
void animationFinished(QWidget *widget);
+protected:
+ void timerEvent(QTimerEvent *e) override;
+
private Q_SLOTS:
void updateGapIndicator();
#if QT_CONFIG(dockwidget)
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));
}
}