aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/quicklayouts/qquicklayout.cpp20
-rw-r--r--src/quicktemplates/qquickapplicationwindow.cpp21
-rw-r--r--tests/auto/quickcontrols/qquickapplicationwindow/data/safeAreaLayout.qml28
-rw-r--r--tests/auto/quickcontrols/qquickapplicationwindow/tst_qquickapplicationwindow.cpp25
4 files changed, 85 insertions, 9 deletions
diff --git a/src/quicklayouts/qquicklayout.cpp b/src/quicklayouts/qquicklayout.cpp
index 3da13ba10a..0dd0d27eed 100644
--- a/src/quicklayouts/qquicklayout.cpp
+++ b/src/quicklayouts/qquicklayout.cpp
@@ -948,12 +948,30 @@ void QQuickLayout::itemChange(ItemChange change, const ItemChangeData &value)
void QQuickLayout::geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry)
{
Q_D(QQuickLayout);
+ qCDebug(lcQuickLayouts) << "QQuickLayout::geometryChange"
+ << oldGeometry << "-->" << newGeometry;
+
QQuickItem::geometryChange(newGeometry, oldGeometry);
+
if ((invalidated() && !qobject_cast<QQuickLayout *>(parentItem())) ||
d->m_disableRearrange || !isReady())
return;
- qCDebug(lcQuickLayouts) << "QQuickLayout::geometryChange" << newGeometry << oldGeometry;
+ // The geometryChange call above might recursively update the
+ // geometry of this layout, via item change listeners, in which
+ // case the recursive call has already rearranged the layout for
+ // the new size. We don't want to rearrange here based on the old
+ // 'new geometry', as that would revert the most up to date layout.
+ const qreal w = d->width.valueBypassingBindings();
+ const qreal h = d->height.valueBypassingBindings();
+ const QSizeF currentSize(w, h);
+ if (currentSize != newGeometry.size()) {
+ qCDebug(lcQuickLayouts) << "QQuickItem::geometryChange resulted"
+ << "in size change from" << newGeometry.size() << "to"
+ << currentSize << "; layout should already be up to date.";
+ return;
+ }
+
rearrange(newGeometry.size());
}
diff --git a/src/quicktemplates/qquickapplicationwindow.cpp b/src/quicktemplates/qquickapplicationwindow.cpp
index 489dd48f47..871ae593ed 100644
--- a/src/quicktemplates/qquickapplicationwindow.cpp
+++ b/src/quicktemplates/qquickapplicationwindow.cpp
@@ -139,20 +139,25 @@ void QQuickApplicationWindowPrivate::updateHasBackgroundFlags()
void QQuickApplicationWindowPrivate::relayout()
{
Q_Q(QQuickApplicationWindow);
- if (!componentComplete || insideRelayout)
+ if (!componentComplete)
return;
+ // Note: We track whether we are inside relayout, but we do
+ // allow nested relayouts, as those are necessary to compute
+ // the height and position of footers when using safe areas.
QScopedValueRollback<bool> guard(insideRelayout, true);
- qreal menuBarHeight = menuBar && menuBar->isVisible() ? menuBar->height() : 0;
- qreal headerheight = header && header->isVisible() ? header->height() : 0;
- qreal footerHeight = footer && footer->isVisible() ? footer->height() : 0;
+ // Re-evaluate component heights for each use, as they
+ // may change between each use due to recursive layouts.
+ auto menuBarHeight = [this]{ return menuBar && menuBar->isVisible() ? menuBar->height() : 0; };
+ auto headerheight = [this]{ return header && header->isVisible() ? header->height() : 0; };
+ auto footerHeight = [this]{ return footer && footer->isVisible() ? footer->height() : 0; };
control->setSize(q->size());
layoutItem(menuBar, 0, q->width());
- layoutItem(header, menuBarHeight, q->width());
- layoutItem(footer, control->height() - footerHeight, q->width());
+ layoutItem(header, menuBarHeight(), q->width());
+ layoutItem(footer, control->height() - footerHeight(), q->width());
if (background) {
if (!hasBackgroundWidth && qFuzzyIsNull(background->x()))
@@ -168,8 +173,8 @@ void QQuickApplicationWindowPrivate::relayout()
auto *windowSafeArea = static_cast<QQuickSafeArea*>(qmlAttachedPropertiesObject<QQuickSafeArea>(q));
const auto inheritedMargins = windowSafeArea->margins();
controlSafeArea->setAdditionalMargins(QMarginsF(
- 0, (menuBarHeight + headerheight) - inheritedMargins.top(),
- 0, footerHeight - inheritedMargins.bottom()));
+ 0, (menuBarHeight() + headerheight()) - inheritedMargins.top(),
+ 0, footerHeight() - inheritedMargins.bottom()));
}
void QQuickApplicationWindowPrivate::itemGeometryChanged(QQuickItem *item, QQuickGeometryChange change, const QRectF &diff)
diff --git a/tests/auto/quickcontrols/qquickapplicationwindow/data/safeAreaLayout.qml b/tests/auto/quickcontrols/qquickapplicationwindow/data/safeAreaLayout.qml
new file mode 100644
index 0000000000..07e58de9aa
--- /dev/null
+++ b/tests/auto/quickcontrols/qquickapplicationwindow/data/safeAreaLayout.qml
@@ -0,0 +1,28 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Controls
+
+ApplicationWindow {
+ id: window
+ width: 600
+ height: 400
+
+ SafeArea.additionalMargins.bottom: 20
+
+ Rectangle {
+ anchors.fill: parent
+ color: "red"
+ }
+
+ footer: ToolBar {
+ visible: false
+
+ Rectangle {
+ anchors.fill: parent
+ color: "yellow"
+ implicitHeight: 50;
+ }
+ }
+}
diff --git a/tests/auto/quickcontrols/qquickapplicationwindow/tst_qquickapplicationwindow.cpp b/tests/auto/quickcontrols/qquickapplicationwindow/tst_qquickapplicationwindow.cpp
index e9fa4494dc..49f810d1eb 100644
--- a/tests/auto/quickcontrols/qquickapplicationwindow/tst_qquickapplicationwindow.cpp
+++ b/tests/auto/quickcontrols/qquickapplicationwindow/tst_qquickapplicationwindow.cpp
@@ -59,6 +59,7 @@ private slots:
void backgroundSize();
void explicitBackgroundSizeBinding();
void safeArea();
+ void safeAreaLayout();
void paintOrderChildItems();
#if QT_CONFIG(quicktemplates2_hover)
void hoverInBackground();
@@ -1148,6 +1149,30 @@ void tst_QQuickApplicationWindow::safeArea()
}
+void tst_QQuickApplicationWindow::safeAreaLayout()
+{
+ QQuickControlsApplicationHelper helper(this, QLatin1String("safeAreaLayout.qml"));
+ QVERIFY2(helper.ready, helper.failureMessage());
+ QQuickApplicationWindow *window = helper.appWindow;
+ window->show();
+ window->requestActivate();
+ QVERIFY(QTest::qWaitForWindowExposed(window));
+
+ auto *windowSafeArea = qobject_cast<QQuickSafeArea*>(
+ qmlAttachedPropertiesObject<QQuickSafeArea>(window));
+ QVERIFY(windowSafeArea);
+
+ // Initially the footer is hidden, so the safe areas margins
+ // are not applied by ToolBar in computing the final height.
+ QCOMPARE(window->footer()->height(), 50);
+
+ // However once it's made visible, the safe area margins of
+ // the window should result in a taller footer, to account
+ // for both the implicit height and the margins.
+ window->footer()->setVisible(true);
+ QCOMPARE(window->footer()->height(), 50 + windowSafeArea->margins().bottom());
+}
+
void tst_QQuickApplicationWindow::paintOrderChildItems()
{
QQmlEngine engine;