aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/quick/items/qquicklistview.cpp4
-rw-r--r--tests/auto/quick/qquicklistview/data/orphanedSectionContext.qml42
-rw-r--r--tests/auto/quick/qquicklistview/tst_qquicklistview.cpp44
3 files changed, 90 insertions, 0 deletions
diff --git a/src/quick/items/qquicklistview.cpp b/src/quick/items/qquicklistview.cpp
index 29eec9cb8c..1f7943af7c 100644
--- a/src/quick/items/qquicklistview.cpp
+++ b/src/quick/items/qquicklistview.cpp
@@ -2113,6 +2113,10 @@ bool QQuickListViewPrivate::flick(AxisData &data, qreal minExtent, qreal maxExte
void QQuickListViewPrivate::setSectionHelper(QQmlContext *context, QQuickItem *sectionItem, const QString &section)
{
+ // The section item can outlive its context
+ // Skip the update rather than dereference the null context
+ if (!context)
+ return;
if (!QQmlContextData::get(context)->isInternal() && context->contextProperty(QLatin1String("section")).isValid())
context->setContextProperty(QLatin1String("section"), section);
else
diff --git a/tests/auto/quick/qquicklistview/data/orphanedSectionContext.qml b/tests/auto/quick/qquicklistview/data/orphanedSectionContext.qml
new file mode 100644
index 0000000000..e74cea70a3
--- /dev/null
+++ b/tests/auto/quick/qquicklistview/data/orphanedSectionContext.qml
@@ -0,0 +1,42 @@
+import QtQuick
+
+ListView {
+ id: listView
+
+ width: 320
+ height: 480
+
+ model: ListModel {
+ id: listModel
+ Component.onCompleted: {
+ for (let i = 0; i < 50; ++i)
+ listModel.append({ name: "item " + i, sectionStr: "section " + (i % 5) })
+ }
+ }
+
+ property int sectionCount: 0
+
+ section.property: "sectionStr"
+ section.criteria: ViewSection.FullString
+ section.labelPositioning: ViewSection.InlineLabels | ViewSection.CurrentLabelAtStart
+
+ // The section delegate is bound, so section items reuse the ListView's own
+ // context (see tst_QQuickListView::orphanedSectionContext()).
+ section.delegate: Rectangle {
+ width: listView.width
+ height: 30
+ color: "#E0E1D8"
+ Text {
+ anchors.centerIn: parent
+ font.pixelSize: 12
+ text: section
+ }
+ Component.onCompleted: ++listView.sectionCount
+ }
+
+ delegate: Item {
+ width: listView.width
+ height: 40
+ Text { text: name }
+ }
+}
diff --git a/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp b/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp
index c30f5e5660..fbf482e3d6 100644
--- a/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp
+++ b/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp
@@ -126,6 +126,7 @@ private slots:
void sectionsItemInsertion();
void sectionsFocusChain();
void removeSectionsOnNonvisibleItems();
+ void orphanedSectionContext();
void cacheBuffer();
void positionViewAtBeginningEnd();
void positionViewAtBeginningWithSections_data();
@@ -2887,6 +2888,49 @@ void tst_QQuickListView::removeSectionsOnNonvisibleItems()
verifySectionData(listView, ++verifySectionId);
}
+void tst_QQuickListView::orphanedSectionContext()
+{
+ // QTBUG-130819: when the section delegate is bound, section items reuse
+ // the ListView's own context. If that context's parent context is
+ // destroyed, the section items are left with a null parent context, and
+ // setSectionHelper() must not dereference it.
+ QQmlEngine engine;
+ QQmlContext *outerContext = new QQmlContext(engine.rootContext());
+ QQmlContext *innerContext = new QQmlContext(outerContext);
+
+ QQmlComponent component(&engine, testFileUrl("orphanedSectionContext.qml"));
+ QScopedPointer<QObject> root(component.create(innerContext));
+ QVERIFY2(root, qPrintable(component.errorString()));
+ auto *listView = qobject_cast<QQuickListView *>(root.data());
+ QVERIFY(listView != nullptr);
+
+ QQuickWindow window;
+ window.resize(listView->width(), listView->height());
+ listView->setParentItem(window.contentItem());
+ window.show();
+ QVERIFY(QTest::qWaitForWindowExposed(&window));
+
+ QTRY_COMPARE_GT(listView->property("sectionCount").toInt(), 0);
+
+ delete outerContext;
+
+ // Any relayout after the context was destroyed calls setSectionHelper()
+ // with a null parent context.
+ QQuickItem *firstSection = nullptr;
+ const QList<QQuickItem *> children = listView->contentItem()->childItems();
+ for (QQuickItem *child : children) {
+ if (qFuzzyCompare(child->y(), qreal(70.0)) && qFuzzyCompare(child->height(), qreal(30.0))
+ && child->isVisible()) {
+ firstSection = child;
+ break;
+ }
+ }
+ QVERIFY(firstSection != nullptr);
+
+ listView->setContentY(800);
+ QTRY_COMPARE(firstSection->isVisible(), false);
+}
+
void tst_QQuickListView::currentIndex_delayedItemCreation()
{
QFETCH(bool, setCurrentToZero);