feat(StatusQ/SubmodelProxyModel): persistent submodels proxies

So far submodel's proxies were created on the fly, on every data(...)
call. Now, once created the proxy is persistent and reused whenever
needed.

Closes: #14389
This commit is contained in:
Michał Cieślak 2024-04-12 00:03:19 +02:00 committed by Michał
parent 5b242c6dd8
commit 173a02f1fc
2 changed files with 78 additions and 13 deletions

View File

@ -12,21 +12,49 @@ SubmodelProxyModel::SubmodelProxyModel(QObject* parent)
QVariant SubmodelProxyModel::data(const QModelIndex &index, int role) const
{
static constexpr auto attachementPropertyName = "_attachement";
if (!checkIndex(index, CheckIndexOption::IndexIsValid))
return {};
if (m_initialized && m_delegateModel && role == m_submodelRole) {
auto submodel = QIdentityProxyModel::data(index, role);
QObject* submodelObj = submodel.value<QObject*>();
if (submodelObj == nullptr) {
qWarning("Submodel must be a QObject-based type!");
return submodel;
}
QVariant attachement = submodelObj->property(attachementPropertyName);
if (attachement.isValid())
return attachement;
// Make sure that wrapper is destroyed before it receives signal related
// to submodel's destruction. Otherwise injected context property may
// be cleared causing warnings related to accessing null from qml.
connect(submodelObj, &QObject::destroyed, this, [](auto obj) {
QVariant attachement = obj->property(attachementPropertyName);
if (attachement.isValid())
delete attachement.value<QObject*>();
});
auto creationContext = m_delegateModel->creationContext();
auto parentContext = creationContext
? creationContext : m_delegateModel->engine()->rootContext();
auto context = new QQmlContext(parentContext, parentContext);
auto context = new QQmlContext(parentContext, submodelObj);
context->setContextProperty(QStringLiteral("submodel"), submodel);
QObject* instance = m_delegateModel->create(context);
QQmlEngine::setObjectOwnership(instance, QQmlEngine::JavaScriptOwnership);
instance->setParent(submodelObj);
submodelObj->setProperty(attachementPropertyName,
QVariant::fromValue(QPointer(instance)));
return QVariant::fromValue(instance);
}

View File

@ -25,18 +25,21 @@ private slots:
auto delegateData = R"(
import QtQml 2.15
QtObject {
property var sub: submodel
property var count: submodel.count
}
)";
delegate.setData(delegateData, QUrl());
SubmodelProxyModel model;
ListModelWrapper sourceModel(engine, QJsonArray {
QJsonObject {{ "balances", 11 }, { "name", "name 1" }},
QJsonObject {{ "balances", 12 }, { "name", "name 2" }},
QJsonObject {{ "balances", 123}, { "name", "name 3" }},
});
auto source = R"([
{ balances: [ { balance: 4 } ], name: "name 1" },
{ balances: [ { balance: 4 }, {balance: 43} ], name: "name 2" },
{ balances: [], name: "name 3" }
])";
ListModelWrapper sourceModel(engine, source);
QSignalSpy sourceModelChangedSpy(
&model, &SubmodelProxyModel::sourceModelChanged);
@ -68,13 +71,47 @@ private slots:
QVERIFY(object);
auto context = QQmlEngine::contextForObject(object);
QCOMPARE(context->contextProperty("submodel"), 11);
QCOMPARE(object->property("sub"), 11);
QVERIFY(context->contextProperty("submodel").value<QObject*>() != nullptr);
QCOMPARE(object->property("count"), 1);
QCOMPARE(QQmlEngine::objectOwnership(object),
QQmlEngine::JavaScriptOwnership);
QQmlEngine::CppOwnership);
}
object->deleteLater();
void usingNonObjectSubmodelRoleTest() {
QQmlEngine engine;
QQmlComponent delegate(&engine);
auto delegateData = R"(
import QtQml 2.15
QtObject {
property var count: submodel.count
}
)";
delegate.setData(delegateData, QUrl());
SubmodelProxyModel model;
auto source = R"([
{ balances: 1, name: "name 1" },
{ balances: 2, name: "name 2" },
{ balances: 3, name: "name 3" }
])";
ListModelWrapper sourceModel(engine, source);
QTest::ignoreMessage(QtWarningMsg,
"Submodel must be a QObject-based type!");
model.setSourceModel(sourceModel);
model.setDelegateModel(&delegate);
model.setSubmodelRoleName(QStringLiteral("balances"));
QCOMPARE(model.rowCount(), 3);
QVERIFY(model.data(model.index(0, 0),
sourceModel.role("balances")).isValid());
}
void deletingDelegateTest() {
@ -149,7 +186,7 @@ private slots:
QCOMPARE(model.data(model.index(0, 0), 0), {});
}
void resetSubmodelRoleNameText() {
void settingUndefinedSubmodelRoleNameText() {
QQmlEngine engine;
auto delegate = std::make_unique<QQmlComponent>(&engine);