diff options
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; |
