QQmlDelegateModelPrivate::itemsRemoved - keep persisted items in cache

QQmlDelegateModelPrivate::itemsRemoved removes unreferenced items from
the cache. If an item is persisted and unreferenced, it's disappearance
from the cache will prevent display models (e.g. qqmllistmodel) from
looping over all delegates. This can lead incomplete clear operations
and dangling screen artifacts.

This patch adds a condition to prevent persited items from being
removed from the cache.

It adds a test function in tst_QQmlDelegateModel.

Fixes: QTBUG-98365
Change-Id: I52e6344be85ca64eadbf44378de32cde126ff07a
Reviewed-by: Richard Moe Gustavsen <richard.gustavsen@qt.io>
(cherry picked from commit da38eaf433)
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
This commit is contained in:
Axel Spoerl 2023-06-20 14:21:18 +02:00 committed by Qt Cherry-pick Bot
parent 701cd0b069
commit 477edcb3aa
3 changed files with 80 additions and 1 deletions

View File

@ -1627,7 +1627,7 @@ void QQmlDelegateModelPrivate::itemsRemoved(
emitDestroyingItem(object);
cacheItem->scriptRef -= 1;
}
if (!cacheItem->isReferenced()) {
if (!cacheItem->isReferenced() && !remove.inGroup(Compositor::Persisted)) {
m_compositor.clearFlags(Compositor::Cache, cacheIndex, 1, Compositor::CacheFlag);
m_cache.removeAt(cacheIndex);
delete cacheItem;

View File

@ -0,0 +1,62 @@
import QtQuick
import QtQuick.Window
import QtQml.Models
Window {
id: win
visible: true
width: 640
height: 480
property int destroyCount : 0;
property int createCount : 0;
property alias testListModel: mdl
DelegateModel {
id: visualModel
model: ListModel {
id: mdl
ListElement {
name: "a"
hidden: false
}
ListElement {
name: "b"
hidden: true
}
ListElement {
name: "c"
hidden: false
}
}
filterOnGroup: "selected"
groups: [
DelegateModelGroup {
name: "selected"
includeByDefault: true
}
]
delegate: Text {
visible: DelegateModel.inSelected
property var idx
Component.onCompleted: {
++createCount
idx = index
DelegateModel.inPersistedItems = true
DelegateModel.inSelected = !model.hidden
}
Component.onDestruction: ++destroyCount
text: model.name
}
}
ListView {
id: listView
model: visualModel
anchors.fill: parent
focus: true
}
}

View File

@ -7,6 +7,7 @@
#include <QtQml/qqmlcomponent.h>
#include <QtQml/qqmlapplicationengine.h>
#include <QtQmlModels/private/qqmldelegatemodel_p.h>
#include <QtQmlModels/private/qqmllistmodel_p.h>
#include <QtQuick/qquickview.h>
#include <QtQuick/qquickitem.h>
#include <QtQuickTestUtils/private/qmlutils_p.h>
@ -31,6 +32,7 @@ private slots:
void universalModelData();
void typedModelData();
void deleteRace();
void persistedItemsStayInCache();
};
class AbstractItemModel : public QAbstractItemModel
@ -457,6 +459,21 @@ void tst_QQmlDelegateModel::deleteRace()
QTRY_COMPARE(o->property("count").toInt(), 0);
}
void tst_QQmlDelegateModel::persistedItemsStayInCache()
{
QQmlEngine engine;
QQmlComponent component(&engine, testFileUrl("persistedItemsCache.qml"));
QVERIFY2(component.isReady(), qPrintable(component.errorString()));
std::unique_ptr<QObject> object(component.create());
QVERIFY(object);
const QVariant properyListModel = object->property("testListModel");
QQmlListModel *listModel = qvariant_cast<QQmlListModel *>(properyListModel);
QVERIFY(listModel);
QTRY_COMPARE(object->property("createCount").toInt(), 3);
listModel->clear();
QTRY_COMPARE(object->property("destroyCount").toInt(), 3);
}
QTEST_MAIN(tst_QQmlDelegateModel)
#include "tst_qqmldelegatemodel.moc"