fix(SubmodelProxyModel): model reset emitted properly when roles change
Closes: #14650
This commit is contained in:
parent
4dfa0a1b05
commit
ed33f21828
|
@ -4,8 +4,10 @@
|
|||
#include <QPointer>
|
||||
|
||||
#include <limits>
|
||||
#include <optional>
|
||||
|
||||
class QQmlComponent;
|
||||
class QQmlEngine;
|
||||
|
||||
class SubmodelProxyModel : public QIdentityProxyModel
|
||||
{
|
||||
|
@ -34,27 +36,36 @@ signals:
|
|||
void delegateModelChanged();
|
||||
void submodelRoleNameChanged();
|
||||
|
||||
protected slots:
|
||||
void resetInternalData();
|
||||
|
||||
private slots:
|
||||
void onCustomRoleChanged(QObject* source, int role);
|
||||
void emitAllDataChanged();
|
||||
|
||||
private:
|
||||
void initializeIfReady();
|
||||
void initialize();
|
||||
void initRoles();
|
||||
void updateRoleNames();
|
||||
|
||||
void onDelegateChanged();
|
||||
QStringList fetchAdditionalRoles(QQmlComponent* delegateComponent);
|
||||
QQmlComponent* buildConnectorComponent(
|
||||
const QHash<QString, int>& additionalRoles,
|
||||
QQmlEngine* engine, QObject* parent);
|
||||
|
||||
std::optional<int> findSubmodelRole(const QHash<int, QByteArray>& roleNames,
|
||||
const QString& submodelRoleName);
|
||||
|
||||
QPointer<QQmlComponent> m_delegateModel;
|
||||
QPointer<QQmlComponent> m_connector;
|
||||
|
||||
QString m_submodelRoleName;
|
||||
|
||||
bool m_initialized = false;
|
||||
bool m_sourceModelDeleted = false;
|
||||
int m_submodelRole = 0;
|
||||
bool m_dataChangedQueued = false;
|
||||
|
||||
std::optional<int> m_submodelRole = 0;
|
||||
|
||||
QStringList m_additionalRoles;
|
||||
QHash<int, QByteArray> m_roleNames;
|
||||
QHash<QString, int> m_additionalRolesMap;
|
||||
int m_additionalRolesOffset = std::numeric_limits<int>::max();
|
||||
|
|
|
@ -24,14 +24,14 @@ 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))
|
||||
return {};
|
||||
|
||||
if (m_initialized && m_delegateModel && role == m_submodelRole) {
|
||||
if (m_delegateModel && m_submodelRole && role == m_submodelRole) {
|
||||
auto submodel = QIdentityProxyModel::data(index, role);
|
||||
|
||||
QObject* submodelObj = submodel.value<QObject*>();
|
||||
|
@ -82,10 +82,10 @@ QVariant SubmodelProxyModel::data(const QModelIndex &index, int role) const
|
|||
return wrappedInstance;
|
||||
}
|
||||
|
||||
if (role >= m_additionalRolesOffset
|
||||
if (m_submodelRole && role >= m_additionalRolesOffset
|
||||
&& role < m_additionalRolesOffset + m_additionalRolesMap.size())
|
||||
{
|
||||
auto submodel = data(index, m_submodelRole);
|
||||
auto submodel = data(index, *m_submodelRole);
|
||||
|
||||
auto submodelObj = submodel.value<QObject*>();
|
||||
|
||||
|
@ -107,8 +107,11 @@ void SubmodelProxyModel::setSourceModel(QAbstractItemModel* model)
|
|||
return;
|
||||
}
|
||||
|
||||
if (sourceModel() == model)
|
||||
return;
|
||||
|
||||
// Workaround for QTBUG-57971
|
||||
if (model && model->roleNames().isEmpty())
|
||||
if (model->roleNames().isEmpty())
|
||||
connect(model, &QAbstractItemModel::rowsInserted,
|
||||
this, &SubmodelProxyModel::initRoles);
|
||||
|
||||
|
@ -116,13 +119,16 @@ void SubmodelProxyModel::setSourceModel(QAbstractItemModel* model)
|
|||
this->m_sourceModelDeleted = true;
|
||||
});
|
||||
|
||||
if (m_delegateModel)
|
||||
m_additionalRoles = fetchAdditionalRoles(m_delegateModel);
|
||||
|
||||
QIdentityProxyModel::setSourceModel(model);
|
||||
initializeIfReady();
|
||||
}
|
||||
|
||||
QHash<int, QByteArray> SubmodelProxyModel::roleNames() const
|
||||
{
|
||||
return m_roleNames;
|
||||
return m_roleNames.isEmpty() && sourceModel()
|
||||
? sourceModel()->roleNames() : m_roleNames;;
|
||||
}
|
||||
|
||||
QQmlComponent* SubmodelProxyModel::delegateModel() const
|
||||
|
@ -132,22 +138,36 @@ QQmlComponent* SubmodelProxyModel::delegateModel() const
|
|||
|
||||
void SubmodelProxyModel::setDelegateModel(QQmlComponent* delegateModel)
|
||||
{
|
||||
if (m_delegateModel != nullptr) {
|
||||
qWarning("Changing delegate model is not supported!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_delegateModel == delegateModel)
|
||||
return;
|
||||
|
||||
if (m_delegateModel)
|
||||
disconnect(delegateModel, &QObject::destroyed,
|
||||
this, &SubmodelProxyModel::onDelegateChanged);
|
||||
if (sourceModel() != nullptr) {
|
||||
QStringList additionalRoles = fetchAdditionalRoles(delegateModel);
|
||||
|
||||
if (delegateModel)
|
||||
connect(delegateModel, &QObject::destroyed,
|
||||
this, &SubmodelProxyModel::onDelegateChanged);
|
||||
if (m_additionalRoles == additionalRoles) {
|
||||
m_delegateModel = delegateModel;
|
||||
|
||||
m_delegateModel = delegateModel;
|
||||
if (m_submodelRole && rowCount() && columnCount()) {
|
||||
emit dataChanged(index(0, 0),
|
||||
index(rowCount() - 1, columnCount() - 1),
|
||||
{ *m_submodelRole });
|
||||
}
|
||||
} else {
|
||||
beginResetModel();
|
||||
m_delegateModel = delegateModel;
|
||||
m_additionalRoles = additionalRoles;
|
||||
endResetModel();
|
||||
}
|
||||
} else {
|
||||
m_delegateModel = delegateModel;
|
||||
}
|
||||
|
||||
onDelegateChanged();
|
||||
|
||||
initializeIfReady();
|
||||
emit delegateModelChanged();
|
||||
}
|
||||
|
||||
const QString& SubmodelProxyModel::submodelRoleName() const
|
||||
|
@ -166,9 +186,25 @@ void SubmodelProxyModel::setSubmodelRoleName(const QString& sumodelRoleName)
|
|||
}
|
||||
|
||||
m_submodelRoleName = sumodelRoleName;
|
||||
emit submodelRoleNameChanged();
|
||||
|
||||
initializeIfReady();
|
||||
if (sourceModel()) {
|
||||
m_submodelRole = findSubmodelRole(sourceModel()->roleNames(),
|
||||
sumodelRoleName);
|
||||
|
||||
if (rowCount() && columnCount()) {
|
||||
emit dataChanged(index(0, 0),
|
||||
index(rowCount() - 1, columnCount() - 1),
|
||||
{ *m_submodelRole });
|
||||
}
|
||||
}
|
||||
|
||||
emit submodelRoleNameChanged();
|
||||
}
|
||||
|
||||
void SubmodelProxyModel::resetInternalData()
|
||||
{
|
||||
QIdentityProxyModel::resetInternalData();
|
||||
updateRoleNames();
|
||||
}
|
||||
|
||||
void SubmodelProxyModel::onCustomRoleChanged(QObject* source, int role)
|
||||
|
@ -193,61 +229,25 @@ void SubmodelProxyModel::emitAllDataChanged()
|
|||
emit this->dataChanged(index(0, 0), index(count - 1, 0), roles);
|
||||
}
|
||||
|
||||
void SubmodelProxyModel::initializeIfReady()
|
||||
void SubmodelProxyModel::initRoles()
|
||||
{
|
||||
if (!m_submodelRoleName.isEmpty() && sourceModel()
|
||||
&& !sourceModel()->roleNames().empty() && m_delegateModel)
|
||||
initialize();
|
||||
disconnect(sourceModel(), &QAbstractItemModel::rowsInserted,
|
||||
this, &SubmodelProxyModel::initRoles);
|
||||
|
||||
resetInternalData();
|
||||
}
|
||||
|
||||
void SubmodelProxyModel::initialize()
|
||||
void SubmodelProxyModel::updateRoleNames()
|
||||
{
|
||||
if (sourceModel() == nullptr)
|
||||
return;
|
||||
|
||||
auto roles = sourceModel()->roleNames();
|
||||
auto submodelKeys = roles.keys(m_submodelRoleName.toUtf8());
|
||||
auto submodelKeysCount = submodelKeys.size();
|
||||
|
||||
if (submodelKeysCount == 1) {
|
||||
m_submodelRole = submodelKeys.first();
|
||||
} else if (submodelKeysCount == 0){
|
||||
qWarning() << "Submodel role not found!";
|
||||
if (roles.empty())
|
||||
return;
|
||||
} else {
|
||||
qWarning() << "Malformed source model - multiple roles found for given "
|
||||
"submodel role name!";
|
||||
return;
|
||||
}
|
||||
|
||||
auto creationContext = m_delegateModel->creationContext();
|
||||
auto parentContext = creationContext
|
||||
? creationContext : m_delegateModel->engine()->rootContext();
|
||||
|
||||
QIdentityProxyModel emptyModel;
|
||||
|
||||
auto context = std::make_unique<QQmlContext>(parentContext);
|
||||
|
||||
// The delegate object is created in order to inspect properties. It may
|
||||
// be not properly initialized because of e.g. lack of context properties
|
||||
// containing submodel. To avoid warnings, they are muted by setting empty
|
||||
// message handler temporarily.
|
||||
QtMessageHandler originalHandler = qInstallMessageHandler(
|
||||
emptyMessageHandler);
|
||||
std::unique_ptr<QObject> instance(m_delegateModel->create(context.get()));
|
||||
qInstallMessageHandler(originalHandler);
|
||||
|
||||
const QMetaObject* meta = instance->metaObject();
|
||||
|
||||
QStringList additionalRoles;
|
||||
|
||||
for (auto i = meta->propertyOffset(); i < meta->propertyCount(); ++i) {
|
||||
const QLatin1String propertyName(meta->property(i).name());
|
||||
|
||||
bool isRole = propertyName.endsWith(QLatin1String(roleSuffix));
|
||||
|
||||
if (!isRole)
|
||||
continue;
|
||||
|
||||
additionalRoles << propertyName.chopped(qstrlen(roleSuffix));
|
||||
}
|
||||
m_submodelRole = findSubmodelRole(roles, m_submodelRoleName);
|
||||
|
||||
const auto keys = roles.keys();
|
||||
const auto maxElementIt = std::max_element(keys.begin(), keys.end());
|
||||
|
@ -257,7 +257,9 @@ void SubmodelProxyModel::initialize()
|
|||
auto maxRoleKey = *maxElementIt;
|
||||
m_additionalRolesOffset = maxRoleKey + 1;
|
||||
|
||||
for (auto& additionalRole : qAsConst(additionalRoles)) {
|
||||
m_additionalRolesMap.clear();
|
||||
|
||||
for (auto& additionalRole : qAsConst(m_additionalRoles)) {
|
||||
auto roleKey = ++maxRoleKey;
|
||||
|
||||
roles.insert(roleKey, additionalRole.toUtf8());
|
||||
|
@ -266,6 +268,53 @@ void SubmodelProxyModel::initialize()
|
|||
|
||||
m_roleNames = roles;
|
||||
|
||||
if (m_delegateModel == nullptr)
|
||||
return;
|
||||
|
||||
m_connector = buildConnectorComponent(m_additionalRolesMap,
|
||||
m_delegateModel->engine(),
|
||||
m_delegateModel);
|
||||
}
|
||||
|
||||
QStringList SubmodelProxyModel::fetchAdditionalRoles(
|
||||
QQmlComponent* delegateComponent)
|
||||
{
|
||||
auto creationContext = delegateComponent->creationContext();
|
||||
auto parentContext = creationContext
|
||||
? creationContext : delegateComponent->engine()->rootContext();
|
||||
|
||||
auto context = std::make_unique<QQmlContext>(parentContext);
|
||||
|
||||
// The delegate object is created in order to inspect properties. It may
|
||||
// be not properly initialized because of e.g. lack of context properties
|
||||
// containing submodel. To avoid warnings, they are muted by setting empty
|
||||
// message handler temporarily.
|
||||
QtMessageHandler originalHandler = qInstallMessageHandler(
|
||||
emptyMessageHandler);
|
||||
std::unique_ptr<QObject> instance(delegateComponent->create(context.get()));
|
||||
qInstallMessageHandler(originalHandler);
|
||||
|
||||
const QMetaObject* meta = instance->metaObject();
|
||||
|
||||
QStringList additionalRoles;
|
||||
|
||||
for (auto i = meta->propertyOffset(); i < meta->propertyCount(); ++i) {
|
||||
const QLatin1String propertyName(meta->property(i).name());
|
||||
bool isRole = propertyName.endsWith(QLatin1String(roleSuffix));
|
||||
|
||||
if (!isRole)
|
||||
continue;
|
||||
|
||||
additionalRoles << propertyName.chopped(qstrlen(roleSuffix));
|
||||
}
|
||||
|
||||
return additionalRoles;
|
||||
}
|
||||
|
||||
QQmlComponent* SubmodelProxyModel::buildConnectorComponent(
|
||||
const QHash<QString, int>& additionalRoles, QQmlEngine* engine,
|
||||
QObject* parent)
|
||||
{
|
||||
QString connectorCode = R"(
|
||||
import QtQml 2.15
|
||||
|
||||
|
@ -273,10 +322,10 @@ void SubmodelProxyModel::initialize()
|
|||
signal customRoleChanged(source: QtObject, role: int)
|
||||
)";
|
||||
|
||||
for (auto& additionalRole : qAsConst(additionalRoles)) {
|
||||
int role = m_additionalRolesMap[additionalRole];
|
||||
for (auto i = additionalRoles.cbegin(); i != additionalRoles.cend(); ++i) {
|
||||
int role = i.value();
|
||||
|
||||
auto upperCaseRole = additionalRole;
|
||||
auto upperCaseRole = i.key();
|
||||
upperCaseRole[0] = upperCaseRole[0].toUpper();
|
||||
|
||||
connectorCode += QString(R"(
|
||||
|
@ -286,27 +335,30 @@ void SubmodelProxyModel::initialize()
|
|||
|
||||
connectorCode += "}";
|
||||
|
||||
m_connector = new QQmlComponent(m_delegateModel->engine(), m_delegateModel);
|
||||
m_connector->setData(connectorCode.toUtf8(), {});
|
||||
auto connector = new QQmlComponent(engine, parent);
|
||||
connector->setData(connectorCode.toUtf8(), {});
|
||||
|
||||
m_initialized = true;
|
||||
return connector;
|
||||
}
|
||||
|
||||
void SubmodelProxyModel::initRoles()
|
||||
std::optional<int> SubmodelProxyModel::findSubmodelRole(
|
||||
const QHash<int, QByteArray>& roleNames,
|
||||
const QString& submodelRoleName)
|
||||
{
|
||||
disconnect(sourceModel(), &QAbstractItemModel::rowsInserted,
|
||||
this, &SubmodelProxyModel::initRoles);
|
||||
if (roleNames.empty() || submodelRoleName.isEmpty())
|
||||
return {};
|
||||
|
||||
resetInternalData();
|
||||
initializeIfReady();
|
||||
}
|
||||
auto submodelKeys = roleNames.keys(m_submodelRoleName.toUtf8());
|
||||
auto submodelKeysCount = submodelKeys.size();
|
||||
|
||||
void SubmodelProxyModel::onDelegateChanged()
|
||||
{
|
||||
emit delegateModelChanged();
|
||||
|
||||
if (m_initialized && rowCount() && columnCount()) {
|
||||
emit dataChanged(index(0, 0), index(rowCount() - 1, columnCount() - 1),
|
||||
{ m_submodelRole });
|
||||
if (submodelKeysCount == 1) {
|
||||
return submodelKeys.first();
|
||||
} else if (submodelKeysCount == 0) {
|
||||
qWarning() << "Submodel role not found!";
|
||||
return {};
|
||||
} else {
|
||||
qWarning() << "Malformed source model - multiple roles found for given "
|
||||
"submodel role name!";
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -204,8 +204,8 @@ private slots:
|
|||
|
||||
delegate.reset();
|
||||
|
||||
QCOMPARE(delegateModelChangedSpy.count(), 1);
|
||||
QCOMPARE(dataChangedSpy.count(), 1);
|
||||
QCOMPARE(delegateModelChangedSpy.count(), 0);
|
||||
QCOMPARE(dataChangedSpy.count(), 0);
|
||||
|
||||
QCOMPARE(model.rowCount(), 3);
|
||||
QCOMPARE(model.data(model.index(0, 0),
|
||||
|
@ -302,6 +302,7 @@ private slots:
|
|||
|
||||
model.setSourceModel(sourceModel);
|
||||
model.setDelegateModel(delegate.get());
|
||||
|
||||
model.setSubmodelRoleName(QStringLiteral("balances"));
|
||||
|
||||
ListModelWrapper expected(engine, R"([
|
||||
|
@ -400,6 +401,267 @@ private slots:
|
|||
QCOMPARE(signalsSpy.count(), 2);
|
||||
}
|
||||
|
||||
void modelResetWhenRoleChangedTest() {
|
||||
QQmlEngine engine;
|
||||
auto delegateWithRole = std::make_unique<QQmlComponent>(&engine);
|
||||
|
||||
delegateWithRole->setData(QByteArrayLiteral(R"(
|
||||
import QtQml.Models 2.15
|
||||
|
||||
ListModel {
|
||||
property int extraValueRole: 0
|
||||
}
|
||||
)"), QUrl());
|
||||
|
||||
auto delegateNoRole = std::make_unique<QQmlComponent>(&engine);
|
||||
|
||||
delegateNoRole->setData(QByteArrayLiteral(R"(
|
||||
import QtQml.Models 2.15
|
||||
|
||||
ListModel {}
|
||||
)"), QUrl());
|
||||
|
||||
ListModelWrapper sourceModel(engine, R"([
|
||||
{ "balances": [], "name": "name 1" }
|
||||
])");
|
||||
|
||||
// 1. set source, 2. set delegate model, 3. set submodel role name
|
||||
{
|
||||
SubmodelProxyModel model;
|
||||
|
||||
ModelSignalsSpy signalsSpy(&model);
|
||||
|
||||
model.setSourceModel(sourceModel);
|
||||
|
||||
QCOMPARE(signalsSpy.count(), 2);
|
||||
QCOMPARE(signalsSpy.modelAboutToBeResetSpy.count(), 1);
|
||||
QCOMPARE(signalsSpy.modelResetSpy.count(), 1);
|
||||
QCOMPARE(model.roleNames().count(), 2);
|
||||
|
||||
model.setDelegateModel(delegateWithRole.get());
|
||||
|
||||
QCOMPARE(signalsSpy.count(), 4);
|
||||
QCOMPARE(signalsSpy.modelAboutToBeResetSpy.count(), 2);
|
||||
QCOMPARE(signalsSpy.modelResetSpy.count(), 2);
|
||||
QCOMPARE(model.roleNames().count(), 3);
|
||||
|
||||
model.setSubmodelRoleName(QStringLiteral("balances"));
|
||||
|
||||
QCOMPARE(signalsSpy.count(), 5);
|
||||
QCOMPARE(signalsSpy.dataChangedSpy.count(), 1);
|
||||
QCOMPARE(signalsSpy.modelAboutToBeResetSpy.count(), 2);
|
||||
QCOMPARE(signalsSpy.modelResetSpy.count(), 2);
|
||||
QCOMPARE(model.roleNames().count(), 3);
|
||||
}
|
||||
|
||||
// 1. set delegate model, 2. set source, 3. set submodel role name
|
||||
{
|
||||
SubmodelProxyModel model;
|
||||
|
||||
ModelSignalsSpy signalsSpy(&model);
|
||||
|
||||
model.setDelegateModel(delegateWithRole.get());
|
||||
|
||||
QCOMPARE(signalsSpy.count(), 0);
|
||||
QCOMPARE(model.roleNames().count(), 0);
|
||||
|
||||
model.setSourceModel(sourceModel);
|
||||
|
||||
QCOMPARE(signalsSpy.count(), 2);
|
||||
QCOMPARE(signalsSpy.modelAboutToBeResetSpy.count(), 1);
|
||||
QCOMPARE(signalsSpy.modelResetSpy.count(), 1);
|
||||
QCOMPARE(model.roleNames().count(), 3);
|
||||
|
||||
model.setSubmodelRoleName(QStringLiteral("balances"));
|
||||
|
||||
QCOMPARE(signalsSpy.count(), 3);
|
||||
QCOMPARE(signalsSpy.dataChangedSpy.count(), 1);
|
||||
QCOMPARE(signalsSpy.modelAboutToBeResetSpy.count(), 1);
|
||||
QCOMPARE(signalsSpy.modelResetSpy.count(), 1);
|
||||
QCOMPARE(model.roleNames().count(), 3);
|
||||
}
|
||||
|
||||
// 1. set submodel role name, 2. set delegate model, 3. set source
|
||||
{
|
||||
SubmodelProxyModel model;
|
||||
|
||||
ModelSignalsSpy signalsSpy(&model);
|
||||
|
||||
model.setSubmodelRoleName(QStringLiteral("balances"));
|
||||
model.setDelegateModel(delegateWithRole.get());
|
||||
|
||||
QCOMPARE(signalsSpy.count(), 0);
|
||||
QCOMPARE(model.roleNames().count(), 0);
|
||||
|
||||
model.setSourceModel(sourceModel);
|
||||
|
||||
QCOMPARE(signalsSpy.count(), 2);
|
||||
QCOMPARE(signalsSpy.modelAboutToBeResetSpy.count(), 1);
|
||||
QCOMPARE(signalsSpy.modelResetSpy.count(), 1);
|
||||
QCOMPARE(model.roleNames().count(), 3);
|
||||
}
|
||||
|
||||
// 1. set source, 2. set delegate model (no extra roles),
|
||||
// 3. set submodel role name
|
||||
{
|
||||
SubmodelProxyModel model;
|
||||
|
||||
ModelSignalsSpy signalsSpy(&model);
|
||||
|
||||
model.setSourceModel(sourceModel);
|
||||
|
||||
QCOMPARE(signalsSpy.count(), 2);
|
||||
QCOMPARE(signalsSpy.modelAboutToBeResetSpy.count(), 1);
|
||||
QCOMPARE(signalsSpy.modelResetSpy.count(), 1);
|
||||
QCOMPARE(model.roleNames().count(), 2);
|
||||
|
||||
model.setDelegateModel(delegateNoRole.get());
|
||||
|
||||
QCOMPARE(signalsSpy.count(), 2);
|
||||
QCOMPARE(signalsSpy.modelAboutToBeResetSpy.count(), 1);
|
||||
QCOMPARE(signalsSpy.modelResetSpy.count(), 1);
|
||||
QCOMPARE(model.roleNames().count(), 2);
|
||||
|
||||
model.setSubmodelRoleName(QStringLiteral("balances"));
|
||||
|
||||
QCOMPARE(signalsSpy.count(), 3);
|
||||
QCOMPARE(signalsSpy.dataChangedSpy.count(), 1);
|
||||
QCOMPARE(signalsSpy.modelAboutToBeResetSpy.count(), 1);
|
||||
QCOMPARE(signalsSpy.modelResetSpy.count(), 1);
|
||||
QCOMPARE(model.roleNames().count(), 2);
|
||||
}
|
||||
}
|
||||
|
||||
// SubmodelProxyModel instantiates delegate model in order to inspect
|
||||
// extra roles. This instantiation must be deferred until model is,
|
||||
// available. Otherwise it may lead to accessing uninitialized external
|
||||
// data within a delegate instance.
|
||||
void deferredDelegateInstantiationTest() {
|
||||
QQmlEngine engine;
|
||||
|
||||
QObject controlObject;
|
||||
engine.rootContext()->setContextProperty("control", &controlObject);
|
||||
|
||||
auto delegate = std::make_unique<QQmlComponent>(&engine);
|
||||
|
||||
delegate->setData(QByteArrayLiteral(R"(
|
||||
import QtQml.Models 2.15
|
||||
import QtQml 2.15
|
||||
|
||||
ListModel {
|
||||
property int extraValueRole: 0
|
||||
|
||||
Component.onCompleted: control.objectName = "instantiated"
|
||||
}
|
||||
)"), QUrl());
|
||||
|
||||
ListModelWrapper sourceModel(engine, R"([
|
||||
{ "balances": [], "name": "name 1" }
|
||||
])");
|
||||
|
||||
{
|
||||
SubmodelProxyModel model;
|
||||
model.setSourceModel(sourceModel);
|
||||
QCOMPARE(controlObject.objectName(), "");
|
||||
|
||||
model.setDelegateModel(delegate.get());
|
||||
QCOMPARE(controlObject.objectName(), "instantiated");
|
||||
}
|
||||
|
||||
controlObject.setObjectName("");
|
||||
|
||||
{
|
||||
SubmodelProxyModel model;
|
||||
model.setDelegateModel(delegate.get());
|
||||
QCOMPARE(controlObject.objectName(), "");
|
||||
|
||||
model.setSourceModel(sourceModel);
|
||||
QCOMPARE(controlObject.objectName(), "instantiated");
|
||||
}
|
||||
}
|
||||
|
||||
void sourceModelResetTest() {
|
||||
class IdentityModel : public QIdentityProxyModel {};
|
||||
|
||||
QQmlEngine engine;
|
||||
auto delegate = std::make_unique<QQmlComponent>(&engine);
|
||||
|
||||
delegate->setData(QByteArrayLiteral(R"(
|
||||
import QtQml.Models 2.15
|
||||
|
||||
ListModel {
|
||||
property int extraValueRole: 0
|
||||
}
|
||||
)"), QUrl());
|
||||
|
||||
ListModelWrapper sourceModel1(engine, R"([
|
||||
{ "balances": [], "name": "name 1" }
|
||||
])");
|
||||
|
||||
ListModelWrapper sourceModel2(engine, R"([
|
||||
{ "key": "1", "balances": [], "name": "name 1", "color": "red" }
|
||||
])");
|
||||
|
||||
IdentityModel identity;
|
||||
identity.setSourceModel(sourceModel1);
|
||||
|
||||
SubmodelProxyModel model;
|
||||
model.setSourceModel(&identity);
|
||||
model.setDelegateModel(delegate.get());
|
||||
model.setSubmodelRoleName(QStringLiteral("balances"));
|
||||
|
||||
QCOMPARE(model.rowCount(), 1);
|
||||
auto roles = model.roleNames();
|
||||
QCOMPARE(roles.size(), 3);
|
||||
|
||||
ModelSignalsSpy signalsSpy(&model);
|
||||
|
||||
identity.setSourceModel(sourceModel2);
|
||||
|
||||
QCOMPARE(signalsSpy.count(), 2);
|
||||
QCOMPARE(signalsSpy.modelAboutToBeResetSpy.count(), 1);
|
||||
QCOMPARE(signalsSpy.modelResetSpy.count(), 1);
|
||||
|
||||
QCOMPARE(model.rowCount(), 1);
|
||||
roles = model.roleNames();
|
||||
QCOMPARE(roles.size(), 5);
|
||||
}
|
||||
|
||||
void sourceModelLateRolesInitTest() {
|
||||
QQmlEngine engine;
|
||||
auto delegate = std::make_unique<QQmlComponent>(&engine);
|
||||
|
||||
delegate->setData(QByteArrayLiteral(R"(
|
||||
import QtQml.Models 2.15
|
||||
|
||||
ListModel {
|
||||
property int extraValueRole: 0
|
||||
}
|
||||
)"), QUrl());
|
||||
|
||||
ListModelWrapper sourceModel(engine, R"([])");
|
||||
|
||||
SubmodelProxyModel model;
|
||||
model.setSourceModel(sourceModel);
|
||||
model.setDelegateModel(delegate.get());
|
||||
model.setSubmodelRoleName(QStringLiteral("balances"));
|
||||
|
||||
QCOMPARE(model.rowCount(), 0);
|
||||
auto roles = model.roleNames();
|
||||
QCOMPARE(roles.size(), 0);
|
||||
|
||||
ModelSignalsSpy signalsSpy(&model);
|
||||
|
||||
sourceModel.append(QJsonArray {
|
||||
QJsonObject {{ "name", "D"}, { "balances", "d1" }},
|
||||
QJsonObject {{ "name", "D"}, { "balances", "d2" }}
|
||||
});
|
||||
|
||||
QCOMPARE(model.rowCount(), 2);
|
||||
roles = model.roleNames();
|
||||
QCOMPARE(roles.size(), 3);
|
||||
}
|
||||
|
||||
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.");
|
||||
|
|
Loading…
Reference in New Issue