From 861a7921ed7833a90f77b5fca436c789a4f9517c Mon Sep 17 00:00:00 2001 From: Mitch Curtis Date: Tue, 22 Oct 2024 13:26:08 +0800 Subject: [PATCH] QQuickItemView: fix crash with zero-size SwipeView that uses Repeater When running the minimal example from the bug report, the order of events is: 1. deletables are iterated in QQuickRepeater::clear(), and deleteLater is called on the delegate item. 2. deletables are unparented. This causes Container (SwipeView) to be notified of the parent change and it removes the item. Part of this involves changing the currentIndex, since the removed item was current. 3. SwipeView's contentItem (ListView) has its currentIndex bound to the container's, so QQuickItemView::setCurrentIndex is called. 4. setCurrentIndex calls updateCurrent, which detects that the currentIndex (which is -1, because there was only one item) is out of range and so releases the item. This causes it to be added to unrequestedItems, even though it has been scheduled for deletion. This patch makes QQuickItemView detect that the item is going to be deleted and not add it to the list of deletables. Fixes: QTBUG-129622 Pick-to: 6.5 Change-Id: I999aedbdfafc61ff6d33eb6579331f470e9c1454 Reviewed-by: Richard Moe Gustavsen Reviewed-by: Fabian Kosmale (cherry picked from commit 18a6a658aaf25fa8c380daef8a72dee4c1661164) Reviewed-by: Qt Cherry-pick Bot --- src/quick/items/qquickitemview.cpp | 4 +- .../quickcontrols/controls/data/tst_swipeview.qml | 59 ++++++++++++++++++++++ 2 files changed, 62 insertions(+), 1 deletion(-) diff --git a/src/quick/items/qquickitemview.cpp b/src/quick/items/qquickitemview.cpp index 19e5b7f4029..dabd5630dbc 100644 --- a/src/quick/items/qquickitemview.cpp +++ b/src/quick/items/qquickitemview.cpp @@ -2499,7 +2499,9 @@ bool QQuickItemViewPrivate::releaseItem(FxViewItem *item, QQmlInstanceModel::Reu // One case where this can happen is moving an item out of one ObjectModel and into another. QQuickItemPrivate::get(item->item)->setCulled(true); } - if (!isClearing) + // If deleteLater was called, the item isn't long for this world and so we shouldn't store references to it. + // This can happen when a Repeater is used to populate items in SwipeView's ListView contentItem. + if (!isClearing && !QObjectPrivate::get(item->item)->deleteLaterCalled) unrequestedItems.insert(item->item, model->indexOf(item->item, q)); } else if (flags & QQmlInstanceModel::Destroyed) { item->item->setParentItem(nullptr); diff --git a/tests/auto/quickcontrols/controls/data/tst_swipeview.qml b/tests/auto/quickcontrols/controls/data/tst_swipeview.qml index 3a7558c0e4d..a3dd16c3044 100644 --- a/tests/auto/quickcontrols/controls/data/tst_swipeview.qml +++ b/tests/auto/quickcontrols/controls/data/tst_swipeview.qml @@ -4,6 +4,7 @@ import QtQuick import QtTest import QtQuick.Controls +import QtQuick.Layouts TestCase { id: testCase @@ -760,4 +761,62 @@ TestCase { tryCompare(swipeListView, "contentX", swipeListView.width, 1000) compare(item2.x, swipeListView.width) } + + Component { + id: zeroSizeSwipeViewWithRepeatersComponent + + Item { + objectName: "rootItem" + anchors.fill: parent + + property alias swipeView: swipeView + property int d + + Timer { + interval: 2 + running: true + repeat: false + onTriggered: d = 2 + } + + SwipeView { + id: swipeView + contentItem.objectName: "swipeViewListView" + + Repeater { + objectName: "swipeViewContentItemRepeater" + model: [ + { + title: d + } + ] + + delegate: GridLayout { + objectName: "gridLayoutDelegate" + + Repeater { + id: repeater + objectName: "delegateRepeater" + model: d + delegate: Item { + objectName: "delegate" + index + + required property int index + } + } + } + } + } + } + } + + // QTBUG-129622 + function test_zeroSizeSwipeViewWithRepeaters() { + let root = createTemporaryObject(zeroSizeSwipeViewWithRepeatersComponent, testCase) + verify(root) + + let swipeView = root.swipeView + tryCompare(root, "d", 2) + // Shouldn't crash when the model is changed. + } } -- 2.16.3