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:
parent
5b242c6dd8
commit
173a02f1fc
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
Loading…
Reference in New Issue