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 <optional>
#include "modelsyncedcontainer.h"
class QQmlComponent;
class QQmlEngine;
@ -69,4 +71,6 @@ private:
QHash<int, QByteArray> m_roleNames;
QHash<QString, int> m_additionalRolesMap;
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
{
static constexpr auto attachementPropertyName = "_attachement";
if (!checkIndex(index, CheckIndexOption::IndexIsValid))
return {};
@ -41,31 +39,34 @@ QVariant SubmodelProxyModel::data(const QModelIndex& index, int role) const
return submodel;
}
QVariant attachement = submodelObj->property(attachementPropertyName);
QVariant proxyVariant;
if (attachement.isValid())
return attachement;
auto& entry = m_container[index.row()];
// 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 (entry) {
proxyVariant = QVariant::fromValue(entry.get());
}
if (attachement.isValid())
delete attachement.value<QObject*>();
});
if (proxyVariant.isValid())
return proxyVariant;
auto creationContext = m_delegateModel->creationContext();
auto parentContext = creationContext
? creationContext : m_delegateModel->engine()->rootContext();
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);
QObject* instance = m_delegateModel->create(context);
instance->setParent(submodelObj);
QVariant wrappedInstance = QVariant::fromValue(instance);
if (m_additionalRolesMap.size()) {
@ -77,8 +78,7 @@ QVariant SubmodelProxyModel::data(const QModelIndex& index, int role) const
this, SLOT(onCustomRoleChanged(QObject*,int)));
}
submodelObj->setProperty(attachementPropertyName, wrappedInstance);
m_container[index.row()].reset(instance);
return wrappedInstance;
}
@ -110,6 +110,8 @@ void SubmodelProxyModel::setSourceModel(QAbstractItemModel* model)
if (sourceModel() == model)
return;
m_container.setModel(model);
// Workaround for QTBUG-57971
if (model->roleNames().isEmpty())
connect(model, &QAbstractItemModel::rowsInserted,

View File

@ -663,9 +663,6 @@ private slots:
}
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;
auto delegate1 = std::make_unique<QQmlComponent>(&engine);