diff options
author | Wang Chuan <ouchuanm@outlook.com> | 2019-11-30 20:03:19 +0800 |
---|---|---|
committer | Wang Chuan <ouchuanm@outlook.com> | 2020-01-15 10:25:21 +0800 |
commit | a20132c326f6d2c5fec848efb98dd86afb320e2a (patch) | |
tree | e16bfef2d0b32fa3e164f7b9e28af3ac86109e3a | |
parent | c061719d2e63c45dc2e126c8e36e9fe906a0bdfe (diff) |
QQuickItemView: fix crash when changing model
When visible items become invisible, ListView will try to cache
them and redisplay these items if necessary. However, we can't
cache items when changing to a new model, since the old one will
be deleted later
Fix by adding a flag to let ListView know we are clearing items
and prevent cache unnecessary items
Fixes: QTBUG-80203
Change-Id: I50dcd3f0586c93496b143bdad0e59751360501a8
Reviewed-by: Richard Moe Gustavsen <richard.gustavsen@qt.io>
-rw-r--r-- | src/quick/items/qquickitemview.cpp | 8 | ||||
-rw-r--r-- | src/quick/items/qquickitemview_p_p.h | 1 | ||||
-rw-r--r-- | tests/auto/quick/qquicklistview/data/changeModelAndDestroyTheOldOne.qml | 34 | ||||
-rw-r--r-- | tests/auto/quick/qquicklistview/tst_qquicklistview.cpp | 16 |
4 files changed, 57 insertions, 2 deletions
diff --git a/src/quick/items/qquickitemview.cpp b/src/quick/items/qquickitemview.cpp index b5fb12fe89..a931abca58 100644 --- a/src/quick/items/qquickitemview.cpp +++ b/src/quick/items/qquickitemview.cpp @@ -1505,7 +1505,7 @@ QQuickItemViewPrivate::QQuickItemViewPrivate() , inLayout(false), inViewportMoved(false), forceLayout(false), currentIndexCleared(false) , haveHighlightRange(false), autoHighlight(true), highlightRangeStartValid(false), highlightRangeEndValid(false) , fillCacheBuffer(false), inRequest(false) - , runDelayedRemoveTransition(false), delegateValidated(false) + , runDelayedRemoveTransition(false), delegateValidated(false), isClearing(false) { bufferPause.addAnimationChangeListener(this, QAbstractAnimationJob::Completion); bufferPause.setLoopCount(1); @@ -1681,6 +1681,10 @@ void QQuickItemViewPrivate::updateCurrent(int modelIndex) void QQuickItemViewPrivate::clear(bool onDestruction) { Q_Q(QQuickItemView); + + isClearing = true; + auto cleanup = qScopeGuard([this] { isClearing = false; }); + currentChanges.reset(); bufferedChanges.reset(); timeline.clear(); @@ -2403,7 +2407,7 @@ bool QQuickItemViewPrivate::releaseItem(FxViewItem *item) QQmlInstanceModel::ReleaseFlags flags = {}; if (model && item->item) { flags = model->release(item->item); - if (!flags) { + if (!flags && !isClearing) { // item was not destroyed, and we no longer reference it. if (item->item->parentItem() == contentItem) { // Only cull the item if its parent item is still our contentItem. diff --git a/src/quick/items/qquickitemview_p_p.h b/src/quick/items/qquickitemview_p_p.h index b31f53b2c0..a448cf9a38 100644 --- a/src/quick/items/qquickitemview_p_p.h +++ b/src/quick/items/qquickitemview_p_p.h @@ -316,6 +316,7 @@ public: bool inRequest : 1; bool runDelayedRemoveTransition : 1; bool delegateValidated : 1; + bool isClearing : 1; protected: virtual Qt::Orientation layoutOrientation() const = 0; diff --git a/tests/auto/quick/qquicklistview/data/changeModelAndDestroyTheOldOne.qml b/tests/auto/quick/qquicklistview/data/changeModelAndDestroyTheOldOne.qml new file mode 100644 index 0000000000..6a33decde6 --- /dev/null +++ b/tests/auto/quick/qquicklistview/data/changeModelAndDestroyTheOldOne.qml @@ -0,0 +1,34 @@ +import QtQuick 2.13 +import QtQml 2.13 +import QtQml.Models 2.13 + +Rectangle { + width: 640 + height: 480 + property var model1: null + property var model2: null + Component { + id: m1 + ObjectModel { + Rectangle { height: 30; width: 80; color: "red" } + Rectangle { height: 30; width: 80; color: "green" } + Rectangle { height: 30; width: 80; color: "blue" } + } + } + Component { + id: m2 + ObjectModel { + Rectangle { height: 30; width: 80; color: "red" } + } + } + ListView { + anchors.fill: parent + Component.onCompleted: { + model1 = m1.createObject() + model = model1 + model2 = m2.createObject() + model = model2 + model1.destroy() + } + } +} diff --git a/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp b/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp index 3976dbc0f0..3687c9416e 100644 --- a/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp +++ b/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp @@ -281,6 +281,7 @@ private slots: void touchCancel(); void resizeAfterComponentComplete(); void moveObjectModelItemToAnotherObjectModel(); + void changeModelAndDestroyTheOldOne(); private: template <class T> void items(const QUrl &source); @@ -9158,6 +9159,21 @@ void tst_QQuickListView::moveObjectModelItemToAnotherObjectModel() QVERIFY(!QQuickItemPrivate::get(redRect)->culled); } +void tst_QQuickListView::changeModelAndDestroyTheOldOne() // QTBUG-80203 +{ + QScopedPointer<QQuickView> window(createView()); + window->setSource(testFileUrl("changeModelAndDestroyTheOldOne.qml")); + window->resize(640, 480); + window->show(); + QVERIFY(QTest::qWaitForWindowExposed(window.data())); + + QQuickItem *root = window->rootObject(); + QVERIFY(root); + + QVERIFY(QQuickTest::qWaitForItemPolished(root)); + // no crash +} + QTEST_MAIN(tst_QQuickListView) #include "tst_qquicklistview.moc" |