fix(SubmodelProxyModel): Proxy model made non-intrusive

Now SubmodelProxyModel doesn't affect source model therefore it's
possible to safely use multiple SubmodelProxyModels over the same source
model.

Closes: #14550
This commit is contained in:
Michał Cieślak 2024-05-23 21:57:40 +02:00 committed by Michał
parent d9707091d3
commit 2dfb61fe1c
3 changed files with 23 additions and 20 deletions

View File

@ -6,6 +6,8 @@
#include <limits> #include <limits>
#include <optional> #include <optional>
#include "modelsyncedcontainer.h"
class QQmlComponent; class QQmlComponent;
class QQmlEngine; class QQmlEngine;
@ -69,4 +71,6 @@ private:
QHash<int, QByteArray> m_roleNames; QHash<int, QByteArray> m_roleNames;
QHash<QString, int> m_additionalRolesMap; QHash<QString, int> m_additionalRolesMap;
int m_additionalRolesOffset = std::numeric_limits<int>::max(); int m_additionalRolesOffset = std::numeric_limits<int>::max();
mutable ModelSyncedContainer<std::unique_ptr<QObject>> m_container;
}; };

View File

@ -26,8 +26,6 @@ SubmodelProxyModel::SubmodelProxyModel(QObject* parent)
QVariant SubmodelProxyModel::data(const QModelIndex& index, int role) const QVariant SubmodelProxyModel::data(const QModelIndex& index, int role) const
{ {
static constexpr auto attachementPropertyName = "_attachement";
if (!checkIndex(index, CheckIndexOption::IndexIsValid)) if (!checkIndex(index, CheckIndexOption::IndexIsValid))
return {}; return {};
@ -41,31 +39,34 @@ QVariant SubmodelProxyModel::data(const QModelIndex& index, int role) const
return submodel; return submodel;
} }
QVariant attachement = submodelObj->property(attachementPropertyName); QVariant proxyVariant;
if (attachement.isValid()) auto& entry = m_container[index.row()];
return attachement;
// Make sure that wrapper is destroyed before it receives signal related if (entry) {
// to submodel's destruction. Otherwise injected context property may proxyVariant = QVariant::fromValue(entry.get());
// 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()) if (proxyVariant.isValid())
delete attachement.value<QObject*>(); return proxyVariant;
});
auto creationContext = m_delegateModel->creationContext(); auto creationContext = m_delegateModel->creationContext();
auto parentContext = creationContext auto parentContext = creationContext
? creationContext : m_delegateModel->engine()->rootContext(); ? creationContext : m_delegateModel->engine()->rootContext();
auto context = new QQmlContext(parentContext, submodelObj); auto context = new QQmlContext(parentContext, submodelObj);
// 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,
[context = QPointer(context)](auto obj) {
delete context.data();
});
context->setContextProperty(QStringLiteral("submodel"), submodel); context->setContextProperty(QStringLiteral("submodel"), submodel);
QObject* instance = m_delegateModel->create(context); QObject* instance = m_delegateModel->create(context);
instance->setParent(submodelObj);
QVariant wrappedInstance = QVariant::fromValue(instance); QVariant wrappedInstance = QVariant::fromValue(instance);
if (m_additionalRolesMap.size()) { if (m_additionalRolesMap.size()) {
@ -77,8 +78,7 @@ QVariant SubmodelProxyModel::data(const QModelIndex& index, int role) const
this, SLOT(onCustomRoleChanged(QObject*,int))); this, SLOT(onCustomRoleChanged(QObject*,int)));
} }
submodelObj->setProperty(attachementPropertyName, wrappedInstance); m_container[index.row()].reset(instance);
return wrappedInstance; return wrappedInstance;
} }
@ -110,6 +110,8 @@ void SubmodelProxyModel::setSourceModel(QAbstractItemModel* model)
if (sourceModel() == model) if (sourceModel() == model)
return; return;
m_container.setModel(model);
// Workaround for QTBUG-57971 // Workaround for QTBUG-57971
if (model->roleNames().isEmpty()) if (model->roleNames().isEmpty())
connect(model, &QAbstractItemModel::rowsInserted, connect(model, &QAbstractItemModel::rowsInserted,

View File

@ -663,9 +663,6 @@ private slots:
} }
void multipleProxiesTest() { void multipleProxiesTest() {
QSKIP("Not implemented yet. The goal is to make the proxy fully "
"non-intrusive what will fix the isse pointed in this test.");
QQmlEngine engine; QQmlEngine engine;
auto delegate1 = std::make_unique<QQmlComponent>(&engine); auto delegate1 = std::make_unique<QQmlComponent>(&engine);