From 8ff4f09b02e274c997d5083ce7baf3e45036133b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Arve=20S=C3=A6ther?= Date: Fri, 10 Mar 2023 13:52:42 +0100 Subject: [PATCH] Do not crash if resizing a layout that got its child destroyed Normally updatePolish() would call ensureLayoutItemsUpdated(), but in some cases the layout might be resized before the updatePolish() is processed. We therefore have to also call ensureLayoutItemsUpdated() from rearrange() Fixes: QTBUG-111792 Change-Id: Iab24dafc26dfa86975348c92244034f7ff825e5f Reviewed-by: Oliver Eftevaag (cherry picked from commit 24f5695d35f0ef7a8f48502de3d4810d21fb3d3f) --- src/quicklayouts/qquicklinearlayout.cpp | 4 ++ .../qquicklayouts/data/tst_rowlayout.qml | 39 ++++++++++++++++++- 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/src/quicklayouts/qquicklinearlayout.cpp b/src/quicklayouts/qquicklinearlayout.cpp index 116678c1d0..b34815e449 100644 --- a/src/quicklayouts/qquicklinearlayout.cpp +++ b/src/quicklayouts/qquicklinearlayout.cpp @@ -457,6 +457,10 @@ void QQuickGridLayoutBase::rearrange(const QSizeF &size) return; } + // Should normally not be needed, but there might be an incoming window resize event that we + // will process before we process updatePolish() + ensureLayoutItemsUpdated(QQuickLayout::ApplySizeHints | QQuickLayout::Recursive); + d->m_rearranging = true; qCDebug(lcQuickLayouts) << objectName() << "QQuickGridLayoutBase::rearrange()" << size; Qt::LayoutDirection visualDir = effectiveLayoutDirection(); diff --git a/tests/auto/quick/qquicklayouts/data/tst_rowlayout.qml b/tests/auto/quick/qquicklayouts/data/tst_rowlayout.qml index 506893b631..7f7352e25b 100644 --- a/tests/auto/quick/qquicklayouts/data/tst_rowlayout.qml +++ b/tests/auto/quick/qquicklayouts/data/tst_rowlayout.qml @@ -1470,6 +1470,43 @@ Item { verify(!BindingLoopDetector.bindingLoopDetected, "Detected binding loop") BindingLoopDetector.reset() } - } + + //--------------------------- + // QTBUG-111792 + Component { + id: rowlayoutCrashes_Component + RowLayout { + spacing: 5 + Rectangle { + color: "red" + implicitWidth: 10 + implicitHeight: 10 + } + Rectangle { + color: "green" + implicitWidth: 10 + implicitHeight: 10 + } + } + } + + function test_dontCrashAfterDestroyingChildren_data() { + return [ + { tag: "setWidth", func: function (layout) { layout.width = 42 } }, + { tag: "setHeight", func: function (layout) { layout.height = 42 } }, + { tag: "getImplicitWidth", func: function (layout) { let x = layout.implicitWidth } }, + { tag: "getImplicitHeight", func: function (layout) { let x = layout.implicitHeight } }, + ] + } + + function test_dontCrashAfterDestroyingChildren(data) { + var layout = createTemporaryObject(rowlayoutCrashes_Component, container) + waitForRendering(layout) + compare(layout.implicitWidth, 25) + layout.children[0].destroy() // deleteLater() + wait(0) // process the scheduled delete and actually invoke the dtor + data.func(layout) // call a function that might ultimately access the deleted item (but shouldn't) + } + } }