diff --git a/src/quick/items/qquicklistview.cpp b/src/quick/items/qquicklistview.cpp index 69fdeafd09..6b29ac1f1d 100644 --- a/src/quick/items/qquicklistview.cpp +++ b/src/quick/items/qquicklistview.cpp @@ -806,6 +806,10 @@ void QQuickListViewPrivate::removeItem(FxViewItem *item) #endif { qCDebug(lcItemViewDelegateLifecycle) << "\treleasing stationary item" << item->index << (QObject *)(item->item); + if (auto *att = static_cast(item->attached)) { + releaseSectionItem(att->m_sectionItem); + att->m_sectionItem = nullptr; + } releaseItem(item, reusableFlag); } } diff --git a/tests/auto/quick/qquicklistview/data/removeSectionsOnNonVisibleItems.qml b/tests/auto/quick/qquicklistview/data/removeSectionsOnNonVisibleItems.qml new file mode 100644 index 0000000000..671dba3391 --- /dev/null +++ b/tests/auto/quick/qquicklistview/data/removeSectionsOnNonVisibleItems.qml @@ -0,0 +1,67 @@ +import QtQuick + +ListView { + id: listView + + ListModel { + id: listModel + Component.onCompleted: reloadModel() + } + + property list sectionItems + property int sectionType: 0 + property int sectionCount: 0 + + function reloadModel() { + ++listView.sectionType + listModel.clear() + for (let i = 0; i < 50; ++i) { + listModel.append({sectionStr: listView.sectionType + ":" + listView.sectionCount++}) + } + listView.sectionCount = 0 + } + + width: 640 + height: 480 + spacing: 24 + boundsBehavior: Flickable.DragOverBounds + displayMarginBeginning: 100 + displayMarginEnd: 100 + contentWidth: listView.width + + model: listModel + + section.property: "sectionStr" + section.criteria: ViewSection.FullString + section.labelPositioning: ViewSection.InlineLabels | ViewSection.CurrentLabelAtStart + section.delegate: Item { + id: item + property string sectionData: section + width: listView.width + height: 30 + Rectangle { + anchors.horizontalCenter: parent.horizontalCenter + width: textDate.width + 20 + height: 16 + radius: 8 + color: "#E0E1D8" + Text { + id: textDate + anchors.centerIn: parent + font.pixelSize: 10 + text: item.sectionData + } + } + Component.onCompleted: listView.sectionItems.push(item) + Component.onDestruction: { + let newSectionItems = [] + for (let index = 0; index < listView.sectionItems.length; index++) { + if (listView.sectionItems[index] !== null && listView.sectionItems[index] != item) + newSectionItems.push(listView.sectionItems[index]) + } + listView.sectionItems = newSectionItems + } + } + + delegate: Item { width: listView.width; height: 10 + index } +} diff --git a/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp b/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp index 8eb6007184..1fb61a6a2e 100644 --- a/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp +++ b/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp @@ -122,6 +122,7 @@ private slots: void sectionPropertyChange(); void sectionDelegateChange(); void sectionsItemInsertion(); + void removeSectionsOnNonvisibleItems(); void cacheBuffer(); void positionViewAtBeginningEnd(); void positionViewAtIndex(); @@ -2783,6 +2784,52 @@ void tst_QQuickListView::sectionsSnap() QCOMPARE(listview->contentY(), qreal(-50)); } +void tst_QQuickListView::removeSectionsOnNonvisibleItems() +{ + QScopedPointer window(createView()); + window->setSource(testFileUrl("removeSectionsOnNonVisibleItems.qml")); + window->show(); + QVERIFY(QTest::qWaitForWindowExposed(window.data())); + + auto verifySectionData = [](QObject *object, int sectionId) { + auto sectionList = object->property("sectionItems").value>(); + const int length = sectionList.count(§ionList); + for (int index = 0; index < length; index++) { + QQuickItem *currentItem = sectionList.at(§ionList, index); + QString sectionData = currentItem->property("sectionData").value(); + QStringList sectionDataList = sectionData.split(":"); + QVERIFY(sectionDataList.at(0).toInt() == sectionId); + } + }; + + auto *listView = qobject_cast(window->rootObject()); + QTRY_VERIFY(listView != nullptr); + QVERIFY(listView->contentItem()); + + auto device = QPointingDevice::primaryPointingDevice(); + const int stopFlickCount = 15; + int flickIndex = 0; + // The issue is more apparent when the list view used in this test case + // been flicked to the contentY: 965. + const float contentYThreadhold = 965.; + do { + QQuickTest::pointerFlick(device, window.data(), 0, QPoint(100, 100), QPoint(100, 50), 125); + QTRY_VERIFY(listView->isMovingVertically()); + QVERIFY(listView->contentY() != qreal(0)); + if (listView->contentY() >= contentYThreadhold) { + listView->cancelFlick(); + break; + } + } while (++flickIndex <= stopFlickCount); + + int verifySectionId = 0; + verifySectionData(listView, ++verifySectionId); + // Refresh the section item with the new model data + QMetaObject::invokeMethod(listView, "reloadModel"); + QVERIFY(QQuickTest::qWaitForPolish(listView)); + verifySectionData(listView, ++verifySectionId); +} + void tst_QQuickListView::currentIndex_delayedItemCreation() { QFETCH(bool, setCurrentToZero);