QmlDesigner: Add callbacks for model resource management

As a node or property is removed there are now callbacks to generate
node, properties and expressions which should removed or adapt too.

Task-number: QDS-9766
Change-Id: I6d842006a6282af00ff644ffaa0f3102e14f13fa
Reviewed-by: Thomas Hartmann <thomas.hartmann@qt.io>
Reviewed-by: <github-actions-qt-creator@cristianadam.eu>
This commit is contained in:
Marco Bubke
2023-05-04 19:01:58 +02:00
parent 90ea280b8e
commit 5eba056b73
25 changed files with 835 additions and 99 deletions

View File

@@ -341,6 +341,7 @@ extend_qtc_library(QmlDesignerCore
modelnodepositionrecalculator.cpp modelnodepositionrecalculator.cpp
modelnodepositionrecalculator.h modelnodepositionrecalculator.h
modelnodepositionstorage.cpp modelnodepositionstorage.cpp
modelresourcemanagementinterface.h
modeltotextmerger.cpp modeltotextmerger.cpp
modeltotextmerger.h modeltotextmerger.h
modelutils.cpp modelutils.cpp

View File

@@ -6,6 +6,7 @@
#include <qmldesignercorelib_global.h> #include <qmldesignercorelib_global.h>
#include <documentmessage.h> #include <documentmessage.h>
#include <model/modelresourcemanagementinterface.h>
#include <projectstorage/projectstoragefwd.h> #include <projectstorage/projectstoragefwd.h>
#include <QMimeData> #include <QMimeData>
@@ -40,7 +41,7 @@ class RewriterView;
class NodeInstanceView; class NodeInstanceView;
class TextModifier; class TextModifier;
using PropertyListType = QList<QPair<PropertyName, QVariant> >; using PropertyListType = QList<QPair<PropertyName, QVariant>>;
class QMLDESIGNERCORE_EXPORT Model : public QObject class QMLDESIGNERCORE_EXPORT Model : public QObject
{ {
@@ -61,25 +62,34 @@ public:
const TypeName &type, const TypeName &type,
int major = 1, int major = 1,
int minor = 1, int minor = 1,
Model *metaInfoProxyModel = nullptr); Model *metaInfoProxyModel = nullptr,
Model(const TypeName &typeName, int major = 1, int minor = 1, Model *metaInfoProxyModel = nullptr); std::unique_ptr<ModelResourceManagementInterface> resourceManagement = {});
Model(const TypeName &typeName,
int major = 1,
int minor = 1,
Model *metaInfoProxyModel = nullptr,
std::unique_ptr<ModelResourceManagementInterface> resourceManagement = {});
~Model(); ~Model();
static ModelPointer create(const TypeName &typeName, static ModelPointer create(const TypeName &typeName,
int major = 1, int major = 1,
int minor = 1, int minor = 1,
Model *metaInfoProxyModel = nullptr) Model *metaInfoProxyModel = nullptr,
std::unique_ptr<ModelResourceManagementInterface> resourceManagement = {})
{ {
return ModelPointer(new Model(typeName, major, minor, metaInfoProxyModel)); return ModelPointer(
new Model(typeName, major, minor, metaInfoProxyModel, std::move(resourceManagement)));
} }
static ModelPointer create(ProjectStorageType &projectStorage, static ModelPointer create(ProjectStorageType &projectStorage,
const TypeName &typeName, const TypeName &typeName,
int major = 1, int major = 1,
int minor = 1) int minor = 1,
std::unique_ptr<ModelResourceManagementInterface> resourceManagement = {})
{ {
return ModelPointer(new Model(projectStorage, typeName, major, minor)); return ModelPointer(
new Model(projectStorage, typeName, major, minor, nullptr, std::move(resourceManagement)));
} }
QUrl fileUrl() const; QUrl fileUrl() const;
@@ -120,6 +130,8 @@ public:
void attachView(AbstractView *view); void attachView(AbstractView *view);
void detachView(AbstractView *view, ViewNotification emitDetachNotify = NotifyView); void detachView(AbstractView *view, ViewNotification emitDetachNotify = NotifyView);
QList<ModelNode> allModelNodes() const;
// Editing sub-components: // Editing sub-components:
// Imports: // Imports:
@@ -177,4 +189,4 @@ private:
std::unique_ptr<Internal::ModelPrivate> d; std::unique_ptr<Internal::ModelPrivate> d;
}; };
} } // namespace QmlDesigner

View File

@@ -270,6 +270,8 @@ QMLDESIGNERCORE_EXPORT bool operator !=(const ModelNode &firstNode, const ModelN
QMLDESIGNERCORE_EXPORT bool operator <(const ModelNode &firstNode, const ModelNode &secondNode); QMLDESIGNERCORE_EXPORT bool operator <(const ModelNode &firstNode, const ModelNode &secondNode);
QMLDESIGNERCORE_EXPORT QDebug operator<<(QDebug debug, const ModelNode &modelNode); QMLDESIGNERCORE_EXPORT QDebug operator<<(QDebug debug, const ModelNode &modelNode);
QMLDESIGNERCORE_EXPORT QTextStream& operator<<(QTextStream &stream, const ModelNode &modelNode); QMLDESIGNERCORE_EXPORT QTextStream& operator<<(QTextStream &stream, const ModelNode &modelNode);
using ModelNodes = QList<ModelNode>;
} }
Q_DECLARE_METATYPE(QmlDesigner::ModelNode) Q_DECLARE_METATYPE(QmlDesigner::ModelNode)

View File

@@ -1619,16 +1619,20 @@ TypeName NodeMetaInfo::simplifiedTypeName() const
int NodeMetaInfo::majorVersion() const int NodeMetaInfo::majorVersion() const
{ {
if constexpr (!useProjectStorage()) {
if (isValid()) if (isValid())
return m_privateData->majorVersion(); return m_privateData->majorVersion();
}
return -1; return -1;
} }
int NodeMetaInfo::minorVersion() const int NodeMetaInfo::minorVersion() const
{ {
if constexpr (!useProjectStorage()) {
if (isValid()) if (isValid())
return m_privateData->minorVersion(); return m_privateData->minorVersion();
}
return -1; return -1;
} }

View File

@@ -39,7 +39,6 @@ AbstractProperty::AbstractProperty(const Internal::InternalPropertyPointer &prop
m_model(model), m_model(model),
m_view(view) m_view(view)
{ {
Q_ASSERT(!m_model || m_view);
} }
AbstractProperty::AbstractProperty(const AbstractProperty &property, AbstractView *view) AbstractProperty::AbstractProperty(const AbstractProperty &property, AbstractView *view)

View File

@@ -64,8 +64,12 @@ RewriterTransaction AbstractView::beginRewriterTransaction(const QByteArray &ide
ModelNode AbstractView::createModelNode(const TypeName &typeName) ModelNode AbstractView::createModelNode(const TypeName &typeName)
{ {
if constexpr (useProjectStorage()) {
return createModelNode(typeName, -1, -1);
} else {
const NodeMetaInfo metaInfo = model()->metaInfo(typeName); const NodeMetaInfo metaInfo = model()->metaInfo(typeName);
return createModelNode(typeName, metaInfo.majorVersion(), metaInfo.minorVersion()); return createModelNode(typeName, metaInfo.majorVersion(), metaInfo.minorVersion());
}
} }
ModelNode AbstractView::createModelNode(const TypeName &typeName, ModelNode AbstractView::createModelNode(const TypeName &typeName,

View File

@@ -56,7 +56,7 @@ void BindingProperty::setExpression(const QString &expression)
} }
if (internalNode()->hasProperty(name()) && !internalNode()->property(name())->isBindingProperty()) if (internalNode()->hasProperty(name()) && !internalNode()->property(name())->isBindingProperty())
privateModel()->removeProperty(internalNode()->property(name())); privateModel()->removePropertyAndRelatedResources(internalNode()->property(name()));
privateModel()->setBindingProperty(internalNode(), name(), expression); privateModel()->setBindingProperty(internalNode(), name(), expression);
} }
@@ -341,7 +341,7 @@ void BindingProperty::setDynamicTypeNameAndExpression(const TypeName &typeName,
} }
if (internalNode()->hasProperty(name()) && !internalNode()->property(name())->isBindingProperty()) if (internalNode()->hasProperty(name()) && !internalNode()->property(name())->isBindingProperty())
privateModel()->removeProperty(internalNode()->property(name())); privateModel()->removePropertyAndRelatedResources(internalNode()->property(name()));
privateModel()->setDynamicBindingProperty(internalNode(), name(), typeName, expression); privateModel()->setDynamicBindingProperty(internalNode(), name(), typeName, expression);
} }

View File

@@ -62,9 +62,11 @@ ModelPrivate::ModelPrivate(Model *model,
const TypeName &typeName, const TypeName &typeName,
int major, int major,
int minor, int minor,
Model *metaInfoProxyModel) Model *metaInfoProxyModel,
std::unique_ptr<ModelResourceManagementInterface> resourceManagement)
: projectStorage{&projectStorage} : projectStorage{&projectStorage}
, m_model{model} , m_model{model}
, m_resourceManagement{std::move(resourceManagement)}
{ {
m_metaInfoProxyModel = metaInfoProxyModel; m_metaInfoProxyModel = metaInfoProxyModel;
@@ -75,9 +77,14 @@ ModelPrivate::ModelPrivate(Model *model,
m_currentTimelineNode = m_rootInternalNode; m_currentTimelineNode = m_rootInternalNode;
} }
ModelPrivate::ModelPrivate( ModelPrivate::ModelPrivate(Model *model,
Model *model, const TypeName &typeName, int major, int minor, Model *metaInfoProxyModel) const TypeName &typeName,
int major,
int minor,
Model *metaInfoProxyModel,
std::unique_ptr<ModelResourceManagementInterface> resourceManagement)
: m_model(model) : m_model(model)
, m_resourceManagement{std::move(resourceManagement)}
{ {
m_metaInfoProxyModel = metaInfoProxyModel; m_metaInfoProxyModel = metaInfoProxyModel;
@@ -279,7 +286,9 @@ InternalNodePointer ModelPrivate::createNode(const TypeName &typeName,
notifyNodeCreated(newNode); notifyNodeCreated(newNode);
if (!newNode->propertyNameList().isEmpty()) if (!newNode->propertyNameList().isEmpty())
notifyVariantPropertiesChanged(newNode, newNode->propertyNameList(), AbstractView::PropertiesAdded); notifyVariantPropertiesChanged(newNode,
newNode->propertyNameList(),
AbstractView::PropertiesAdded);
return newNode; return newNode;
} }
@@ -303,16 +312,40 @@ EnabledViewRange ModelPrivate::enabledViews() const
return EnabledViewRange{m_viewList}; return EnabledViewRange{m_viewList};
} }
void ModelPrivate::handleResourceSet(const ModelResourceSet &resourceSet)
{
for (const ModelNode &node : resourceSet.removeModelNodes) {
if (node)
removeNode(node.m_internalNode);
}
for (const AbstractProperty &property : resourceSet.removeProperties) {
if (property)
removeProperty(property.m_internalNode->property(property.m_propertyName));
}
for (const auto &[property, expression] : resourceSet.setExpressions) {
if (property)
setBindingProperty(property.m_internalNode, property.m_propertyName, expression);
}
}
void ModelPrivate::removeAllSubNodes(const InternalNodePointer &node) void ModelPrivate::removeAllSubNodes(const InternalNodePointer &node)
{ {
for (const InternalNodePointer &subNode : node->allSubNodes()) for (const InternalNodePointer &subNode : node->allSubNodes())
removeNodeFromModel(subNode); removeNodeFromModel(subNode);
} }
void ModelPrivate::removeNodeAndRelatedResources(const InternalNodePointer &node)
{
if (m_resourceManagement)
handleResourceSet(m_resourceManagement->removeNode(ModelNode{node, m_model, nullptr}));
else
removeNode(node);
}
void ModelPrivate::removeNode(const InternalNodePointer &node) void ModelPrivate::removeNode(const InternalNodePointer &node)
{ {
Q_ASSERT(node);
AbstractView::PropertyChangeFlags propertyChangeFlags = AbstractView::NoAdditionalChanges; AbstractView::PropertyChangeFlags propertyChangeFlags = AbstractView::NoAdditionalChanges;
notifyNodeAboutToBeRemoved(node); notifyNodeAboutToBeRemoved(node);
@@ -1088,6 +1121,15 @@ static QList<PropertyPair> toPropertyPairList(const QList<InternalPropertyPointe
return propertyPairList; return propertyPairList;
} }
void ModelPrivate::removePropertyAndRelatedResources(const InternalPropertyPointer &property)
{
if (m_resourceManagement)
handleResourceSet(
m_resourceManagement->removeProperty(AbstractProperty{property, m_model, nullptr}));
else
removeProperty(property);
}
void ModelPrivate::removeProperty(const InternalPropertyPointer &property) void ModelPrivate::removeProperty(const InternalPropertyPointer &property)
{ {
notifyPropertiesAboutToBeRemoved({property}); notifyPropertiesAboutToBeRemoved({property});
@@ -1408,13 +1450,19 @@ Model::Model(ProjectStorageType &projectStorage,
const TypeName &typeName, const TypeName &typeName,
int major, int major,
int minor, int minor,
Model *metaInfoProxyModel) Model *metaInfoProxyModel,
std::unique_ptr<ModelResourceManagementInterface> resourceManagement)
: d(std::make_unique<Internal::ModelPrivate>( : d(std::make_unique<Internal::ModelPrivate>(
this, projectStorage, typeName, major, minor, metaInfoProxyModel)) this, projectStorage, typeName, major, minor, metaInfoProxyModel, std::move(resourceManagement)))
{} {}
Model::Model(const TypeName &typeName, int major, int minor, Model *metaInfoProxyModel) Model::Model(const TypeName &typeName,
: d(std::make_unique<Internal::ModelPrivate>(this, typeName, major, minor, metaInfoProxyModel)) int major,
int minor,
Model *metaInfoProxyModel,
std::unique_ptr<ModelResourceManagementInterface> resourceManagement)
: d(std::make_unique<Internal::ModelPrivate>(
this, typeName, major, minor, metaInfoProxyModel, std::move(resourceManagement)))
{} {}
Model::~Model() = default; Model::~Model() = default;
@@ -2156,4 +2204,9 @@ void Model::detachView(AbstractView *view, ViewNotification emitDetachNotify)
d->detachView(view, emitNotify); d->detachView(view, emitNotify);
} }
QList<ModelNode> Model::allModelNodes() const
{
return QmlDesigner::toModelNodeList(d->allNodes(), nullptr);
}
} // namespace QmlDesigner } // namespace QmlDesigner

View File

@@ -101,8 +101,14 @@ public:
const TypeName &type, const TypeName &type,
int major, int major,
int minor, int minor,
Model *metaInfoProxyModel); Model *metaInfoProxyModel,
ModelPrivate(Model *model, const TypeName &type, int major, int minor, Model *metaInfoProxyModel); std::unique_ptr<ModelResourceManagementInterface> resourceManagement);
ModelPrivate(Model *model,
const TypeName &type,
int major,
int minor,
Model *metaInfoProxyModel,
std::unique_ptr<ModelResourceManagementInterface> resourceManagement);
~ModelPrivate() override; ~ModelPrivate() override;
@@ -120,6 +126,7 @@ public:
bool isRootNode = false); bool isRootNode = false);
/*factory methods for internal use in model and rewriter*/ /*factory methods for internal use in model and rewriter*/
void removeNodeAndRelatedResources(const InternalNodePointer &node);
void removeNode(const InternalNodePointer &node); void removeNode(const InternalNodePointer &node);
void changeNodeId(const InternalNodePointer &node, const QString &id); void changeNodeId(const InternalNodePointer &node, const QString &id);
void changeNodeType(const InternalNodePointer &node, const TypeName &typeName, int majorVersion, int minorVersion); void changeNodeType(const InternalNodePointer &node, const TypeName &typeName, int majorVersion, int minorVersion);
@@ -235,6 +242,7 @@ public:
//node state property manipulation //node state property manipulation
void addProperty(const InternalNodePointer &node, const PropertyName &name); void addProperty(const InternalNodePointer &node, const PropertyName &name);
void setPropertyValue(const InternalNodePointer &node,const PropertyName &name, const QVariant &value); void setPropertyValue(const InternalNodePointer &node,const PropertyName &name, const QVariant &value);
void removePropertyAndRelatedResources(const InternalPropertyPointer &property);
void removeProperty(const InternalPropertyPointer &property); void removeProperty(const InternalPropertyPointer &property);
void setBindingProperty(const InternalNodePointer &node, const PropertyName &name, const QString &expression); void setBindingProperty(const InternalNodePointer &node, const PropertyName &name, const QString &expression);
@@ -282,6 +290,7 @@ private:
QVector<ModelNode> toModelNodeVector(const QVector<InternalNodePointer> &nodeVector, AbstractView *view) const; QVector<ModelNode> toModelNodeVector(const QVector<InternalNodePointer> &nodeVector, AbstractView *view) const;
QVector<InternalNodePointer> toInternalNodeVector(const QVector<ModelNode> &modelNodeVector) const; QVector<InternalNodePointer> toInternalNodeVector(const QVector<ModelNode> &modelNodeVector) const;
EnabledViewRange enabledViews() const; EnabledViewRange enabledViews() const;
void handleResourceSet(const ModelResourceSet &resourceSet);
public: public:
NotNullPointer<ProjectStorageType> projectStorage = nullptr; NotNullPointer<ProjectStorageType> projectStorage = nullptr;
@@ -300,6 +309,7 @@ private:
InternalNodePointer m_currentStateNode; InternalNodePointer m_currentStateNode;
InternalNodePointer m_rootInternalNode; InternalNodePointer m_rootInternalNode;
InternalNodePointer m_currentTimelineNode; InternalNodePointer m_currentTimelineNode;
std::unique_ptr<ModelResourceManagementInterface> m_resourceManagement;
QUrl m_fileUrl; QUrl m_fileUrl;
QPointer<RewriterView> m_rewriterView; QPointer<RewriterView> m_rewriterView;
QPointer<NodeInstanceView> m_nodeInstanceView; QPointer<NodeInstanceView> m_nodeInstanceView;

View File

@@ -290,7 +290,7 @@ A node might become invalid if e.g. it or one of its ancestors is deleted.
*/ */
bool ModelNode::isValid() const bool ModelNode::isValid() const
{ {
return !m_model.isNull() && !m_view.isNull() && m_internalNode && m_internalNode->isValid; return !m_model.isNull() && m_internalNode && m_internalNode->isValid;
} }
/*! /*!
@@ -652,7 +652,7 @@ void ModelNode::removeProperty(const PropertyName &name) const
return; return;
if (m_internalNode->hasProperty(name)) if (m_internalNode->hasProperty(name))
model()->d->removeProperty(m_internalNode->property(name)); model()->d->removePropertyAndRelatedResources(m_internalNode->property(name));
} }
/*! \brief removes this node from the node tree /*! \brief removes this node from the node tree
@@ -692,7 +692,7 @@ void ModelNode::destroy()
return; return;
removeModelNodeFromSelection(*this); removeModelNodeFromSelection(*this);
model()->d->removeNode(m_internalNode); model()->d->removeNodeAndRelatedResources(m_internalNode);
} }
//\} //\}

View File

@@ -0,0 +1,39 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#pragma once
#include <qmldesignercorelib_exports.h>
#include <bindingproperty.h>
namespace QmlDesigner {
struct ModelResourceSet
{
struct SetExpression
{
BindingProperty property;
QString expression;
};
QList<ModelNode> removeModelNodes;
QList<AbstractProperty> removeProperties;
QList<SetExpression> setExpressions;
};
class QMLDESIGNERCORE_EXPORT ModelResourceManagementInterface
{
public:
ModelResourceManagementInterface() = default;
virtual ~ModelResourceManagementInterface() = default;
ModelResourceManagementInterface(const ModelResourceManagementInterface &) = delete;
ModelResourceManagementInterface &operator=(const ModelResourceManagementInterface &) = delete;
ModelResourceManagementInterface(ModelResourceManagementInterface &&) = default;
ModelResourceManagementInterface &operator=(ModelResourceManagementInterface &&) = default;
virtual ModelResourceSet removeNode(const ModelNode &node) const = 0;
virtual ModelResourceSet removeProperty(const AbstractProperty &property) const = 0;
};
} // namespace QmlDesigner

View File

@@ -71,7 +71,7 @@ void NodeAbstractProperty::reparentHere(const ModelNode &modelNode, bool isNode
return; return;
if (internalNode()->hasProperty(name()) && !internalNode()->property(name())->isNodeAbstractProperty()) if (internalNode()->hasProperty(name()) && !internalNode()->property(name())->isNodeAbstractProperty())
privateModel()->removeProperty(internalNode()->property(name())); privateModel()->removePropertyAndRelatedResources(internalNode()->property(name()));
if (modelNode.hasParentProperty()) { if (modelNode.hasParentProperty()) {
Internal::InternalNodeAbstractProperty::Pointer oldParentProperty = modelNode.internalNode()->parentProperty(); Internal::InternalNodeAbstractProperty::Pointer oldParentProperty = modelNode.internalNode()->parentProperty();

View File

@@ -32,7 +32,7 @@ void NodeProperty::setModelNode(const ModelNode &modelNode)
} }
if (internalNode()->hasProperty(name()) && !internalNode()->property(name())->isNodeProperty()) if (internalNode()->hasProperty(name()) && !internalNode()->property(name())->isNodeProperty())
privateModel()->removeProperty(internalNode()->property(name())); privateModel()->removePropertyAndRelatedResources(internalNode()->property(name()));
privateModel()->reparentNode(internalNode(), name(), modelNode.internalNode(), false); //### we have to add a flag that this is not a list privateModel()->reparentNode(internalNode(), name(), modelNode.internalNode(), false); //### we have to add a flag that this is not a list
} }

View File

@@ -41,7 +41,7 @@ void SignalHandlerProperty::setSource(const QString &source)
} }
if (internalNode()->hasProperty(name()) && !internalNode()->property(name())->isSignalHandlerProperty()) if (internalNode()->hasProperty(name()) && !internalNode()->property(name())->isSignalHandlerProperty())
privateModel()->removeProperty(internalNode()->property(name())); privateModel()->removePropertyAndRelatedResources(internalNode()->property(name()));
privateModel()->setSignalHandlerProperty(internalNode(), name(), source); privateModel()->setSignalHandlerProperty(internalNode(), name(), source);
} }
@@ -118,7 +118,7 @@ void SignalDeclarationProperty::setSignature(const QString &signature)
} }
if (internalNode()->hasProperty(name()) && !internalNode()->property(name())->isSignalDeclarationProperty()) if (internalNode()->hasProperty(name()) && !internalNode()->property(name())->isSignalDeclarationProperty())
privateModel()->removeProperty(internalNode()->property(name())); privateModel()->removePropertyAndRelatedResources(internalNode()->property(name()));
privateModel()->setSignalDeclarationProperty(internalNode(), name(), signature); privateModel()->setSignalDeclarationProperty(internalNode(), name(), signature);
} }

View File

@@ -47,7 +47,7 @@ void VariantProperty::setValue(const QVariant &value)
} }
if (internalNode()->hasProperty(name()) && !internalNode()->property(name())->isVariantProperty()) if (internalNode()->hasProperty(name()) && !internalNode()->property(name())->isVariantProperty())
privateModel()->removeProperty(internalNode()->property(name())); privateModel()->removePropertyAndRelatedResources(internalNode()->property(name()));
privateModel()->setVariantProperty(internalNode(), name(), value); privateModel()->setVariantProperty(internalNode(), name(), value);
} }
@@ -96,7 +96,7 @@ void VariantProperty::setDynamicTypeNameAndValue(const TypeName &type, const QVa
} }
if (internalNode()->hasProperty(name()) && !internalNode()->property(name())->isVariantProperty()) if (internalNode()->hasProperty(name()) && !internalNode()->property(name())->isVariantProperty())
privateModel()->removeProperty(internalNode()->property(name())); privateModel()->removePropertyAndRelatedResources(internalNode()->property(name()));
privateModel()->setDynamicVariantProperty(internalNode(), name(), type, value); privateModel()->setDynamicVariantProperty(internalNode(), name(), type, value);
} }

View File

@@ -242,36 +242,37 @@ public:
return false; return false;
} }
bool isBasedOn(TypeId id0) const override { return isBasedOn_(id0); } bool isBasedOn(TypeId typeId) const { return isBasedOn_(typeId); }
bool isBasedOn(TypeId id0, TypeId id1) const override { return isBasedOn_(id0, id1); } bool isBasedOn(TypeId typeId, TypeId id1) const override { return isBasedOn_(typeId, id1); }
bool isBasedOn(TypeId id0, TypeId id1, TypeId id2) const override bool isBasedOn(TypeId typeId, TypeId id1, TypeId id2) const override
{ {
return isBasedOn_(id0, id1, id2); return isBasedOn_(typeId, id1, id2);
} }
bool isBasedOn(TypeId id0, TypeId id1, TypeId id2, TypeId id3) const override bool isBasedOn(TypeId typeId, TypeId id1, TypeId id2, TypeId id3) const override
{ {
return isBasedOn_(id0, id1, id2, id3); return isBasedOn_(typeId, id1, id2, id3);
} }
bool isBasedOn(TypeId id0, TypeId id1, TypeId id2, TypeId id3, TypeId id4) const override bool isBasedOn(TypeId typeId, TypeId id1, TypeId id2, TypeId id3, TypeId id4) const override
{ {
return isBasedOn_(id0, id1, id2, id3, id4); return isBasedOn_(typeId, id1, id2, id3, id4);
} }
bool isBasedOn(TypeId id0, TypeId id1, TypeId id2, TypeId id3, TypeId id4, TypeId id5) const override bool isBasedOn(TypeId typeId, TypeId id1, TypeId id2, TypeId id3, TypeId id4, TypeId id5) const override
{ {
return isBasedOn_(id0, id1, id2, id3, id4, id5); return isBasedOn_(typeId, id1, id2, id3, id4, id5);
} }
bool isBasedOn(TypeId id0, TypeId id1, TypeId id2, TypeId id3, TypeId id4, TypeId id5, TypeId id6) const override bool isBasedOn(
TypeId typeId, TypeId id1, TypeId id2, TypeId id3, TypeId id4, TypeId id5, TypeId id6) const override
{ {
return isBasedOn_(id0, id1, id2, id3, id4, id5, id6); return isBasedOn_(typeId, id1, id2, id3, id4, id5, id6);
} }
bool isBasedOn(TypeId id0, bool isBasedOn(TypeId typeId,
TypeId id1, TypeId id1,
TypeId id2, TypeId id2,
TypeId id3, TypeId id3,
@@ -280,7 +281,7 @@ public:
TypeId id6, TypeId id6,
TypeId id7) const override TypeId id7) const override
{ {
return isBasedOn_(id0, id1, id2, id3, id4, id5, id6, id7); return isBasedOn_(typeId, id1, id2, id3, id4, id5, id6, id7);
} }
TypeId fetchTypeIdByExportedName(Utils::SmallStringView name) const TypeId fetchTypeIdByExportedName(Utils::SmallStringView name) const

View File

@@ -36,7 +36,6 @@ public:
propertyName(PropertyDeclarationId propertyDeclarationId) const = 0; propertyName(PropertyDeclarationId propertyDeclarationId) const = 0;
virtual TypeIds prototypeAndSelfIds(TypeId type) const = 0; virtual TypeIds prototypeAndSelfIds(TypeId type) const = 0;
virtual TypeIds prototypeIds(TypeId type) const = 0; virtual TypeIds prototypeIds(TypeId type) const = 0;
virtual bool isBasedOn(TypeId) const = 0;
virtual bool isBasedOn(TypeId, TypeId) const = 0; virtual bool isBasedOn(TypeId, TypeId) const = 0;
virtual bool isBasedOn(TypeId, TypeId, TypeId) const = 0; virtual bool isBasedOn(TypeId, TypeId, TypeId) const = 0;
virtual bool isBasedOn(TypeId, TypeId, TypeId, TypeId) const = 0; virtual bool isBasedOn(TypeId, TypeId, TypeId, TypeId) const = 0;

View File

@@ -56,6 +56,8 @@ add_qtc_test(unittest GTEST
gtest-std-printing.h gtest-std-printing.h
lastchangedrowid-test.cpp lastchangedrowid-test.cpp
import-test.cpp import-test.cpp
model-test.cpp
modelresourcemanagementmock.h
matchingtext-test.cpp matchingtext-test.cpp
mockfutureinterface.h mockfutureinterface.h
mockmutex.h mockmutex.h
@@ -66,6 +68,7 @@ add_qtc_test(unittest GTEST
mocktimer.cpp mocktimer.h mocktimer.cpp mocktimer.h
nodelistproperty-test.cpp nodelistproperty-test.cpp
processevents-utilities.cpp processevents-utilities.h processevents-utilities.cpp processevents-utilities.h
projectstoragemock.cpp projectstoragemock.h
sizedarray-test.cpp sizedarray-test.cpp
smallstring-test.cpp smallstring-test.cpp
spydummy.cpp spydummy.h spydummy.cpp spydummy.h
@@ -258,6 +261,7 @@ extend_qtc_test(unittest
model/model.cpp model/model.cpp
model/model_p.h model/model_p.h
model/modelnode.cpp model/modelnode.cpp
model/modelresourcemanagementinterface.h
model/propertycontainer.cpp model/propertycontainer.cpp
model/propertyparser.cpp model/propertyparser.cpp
model/nodeabstractproperty.cpp model/nodeabstractproperty.cpp

View File

@@ -73,19 +73,10 @@ class ListModelEditor : public testing::Test
public: public:
ListModelEditor() ListModelEditor()
{ {
setModuleId("QtQuick", modelId_QtQuick);
setType(modelId_QtQuick, "Item", "data");
designerModel = QmlDesigner::Model::create(projectStorageMock, "QtQuick.Item", 1, 1);
setModuleId("QtQml.Models", modelId_QtQml_Models);
setType(modelId_QtQml_Models, "ListModel", "children");
setType(modelId_QtQml_Models, "ListElement", "children");
componentModel = QmlDesigner::Model::create(projectStorageMock, "QtQml.Models.ListModel", 1, 1);
designerModel->attachView(&mockView); designerModel->attachView(&mockView);
emptyListModelNode = mockView.createModelNode("QtQml.Models.ListModel", 2, 15); emptyListModelNode = mockView.createModelNode("QtQml.Models.ListModel", 2, 15);
setType(modelId_QtQuick, "ListView", "data");
listViewNode = mockView.createModelNode("QtQuick.ListView", 2, 15); listViewNode = mockView.createModelNode("QtQuick.ListView", 2, 15);
listModelNode = mockView.createModelNode("QtQml.Models.ListModel", 2, 15); listModelNode = mockView.createModelNode("QtQml.Models.ListModel", 2, 15);
mockView.rootModelNode().defaultNodeListProperty().reparentHere(listModelNode); mockView.rootModelNode().defaultNodeListProperty().reparentHere(listModelNode);
@@ -108,29 +99,6 @@ public:
ON_CALL(goIntoComponentMock, Call(_)).WillByDefault([](ModelNode node) { return node; }); ON_CALL(goIntoComponentMock, Call(_)).WillByDefault([](ModelNode node) { return node; });
} }
void setModuleId(Utils::SmallStringView moduleName, ModuleId moduleId)
{
ON_CALL(projectStorageMock, moduleId(Eq(moduleName))).WillByDefault(Return(moduleId));
}
void setType(ModuleId moduleId,
Utils::SmallStringView typeName,
Utils::SmallString defaultPeopertyName)
{
static int typeIdNumber = 0;
TypeId typeId = TypeId::create(++typeIdNumber);
static int defaultPropertyIdNumber = 0;
PropertyDeclarationId defaultPropertyId = PropertyDeclarationId::create(
++defaultPropertyIdNumber);
ON_CALL(projectStorageMock, typeId(Eq(moduleId), Eq(typeName), _)).WillByDefault(Return(typeId));
ON_CALL(projectStorageMock, type(Eq(typeId)))
.WillByDefault(Return(Info::Type{defaultPropertyId, {}}));
ON_CALL(projectStorageMock, propertyName(Eq(defaultPropertyId)))
.WillByDefault(Return(defaultPeopertyName));
}
using Entry = std::pair<QmlDesigner::PropertyName, QVariant>; using Entry = std::pair<QmlDesigner::PropertyName, QVariant>;
ModelNode createElement(std::initializer_list<Entry> entries, AbstractView &view, ModelNode listModel) ModelNode createElement(std::initializer_list<Entry> entries, AbstractView &view, ModelNode listModel)
@@ -211,9 +179,10 @@ public:
} }
protected: protected:
NiceMock<ProjectStorageMock> projectStorageMock; NiceMock<ProjectStorageMockWithQtQtuick> projectStorageMock;
NiceMock<MockFunction<ModelNode(const ModelNode &)>> goIntoComponentMock; NiceMock<MockFunction<ModelNode(const ModelNode &)>> goIntoComponentMock;
QmlDesigner::ModelPointer designerModel; QmlDesigner::ModelPointer designerModel{
QmlDesigner::Model::create(projectStorageMock, "QtQuick.Item", 1, 1)};
NiceMock<MockListModelEditorView> mockView; NiceMock<MockListModelEditorView> mockView;
QmlDesigner::ListModelEditorModel model{ QmlDesigner::ListModelEditorModel model{
[&] { return mockView.createModelNode("QtQml.Models.ListModel", 2, 15); }, [&] { return mockView.createModelNode("QtQml.Models.ListModel", 2, 15); },
@@ -225,11 +194,10 @@ protected:
ModelNode element1; ModelNode element1;
ModelNode element2; ModelNode element2;
ModelNode element3; ModelNode element3;
QmlDesigner::ModelPointer componentModel; QmlDesigner::ModelPointer componentModel{
QmlDesigner::Model::create(projectStorageMock, "QtQml.Models.ListModel", 1, 1)};
NiceMock<MockListModelEditorView> mockComponentView; NiceMock<MockListModelEditorView> mockComponentView;
ModelNode componentElement; ModelNode componentElement;
ModuleId modelId_QtQuick = ModuleId::create(1);
ModuleId modelId_QtQml_Models = ModuleId::create(2);
}; };
TEST_F(ListModelEditor, CreatePropertyNameSet) TEST_F(ListModelEditor, CreatePropertyNameSet)

View File

@@ -26,10 +26,25 @@ public:
const QmlDesigner::NodeAbstractProperty &oldPropertyParent, const QmlDesigner::NodeAbstractProperty &oldPropertyParent,
AbstractView::PropertyChangeFlags propertyChange), AbstractView::PropertyChangeFlags propertyChange),
(override)); (override));
MOCK_METHOD(void, MOCK_METHOD(void,
propertiesRemoved, propertiesRemoved,
(const QList<QmlDesigner::AbstractProperty> &propertyList), (const QList<QmlDesigner::AbstractProperty> &propertyList),
(override)); (override));
MOCK_METHOD(void,
propertiesAboutToBeRemoved,
(const QList<QmlDesigner::AbstractProperty> &propertyList),
(override));
MOCK_METHOD(void,
bindingPropertiesChanged,
(const QList<QmlDesigner::BindingProperty> &propertyList,
PropertyChangeFlags propertyChange),
(override));
MOCK_METHOD(void,
bindingPropertiesAboutToBeChanged,
(const QList<QmlDesigner::BindingProperty> &propertyList),
(override));
MOCK_METHOD(void, MOCK_METHOD(void,
nodeRemoved, nodeRemoved,
@@ -37,4 +52,5 @@ public:
const QmlDesigner::NodeAbstractProperty &parentProperty, const QmlDesigner::NodeAbstractProperty &parentProperty,
AbstractView::PropertyChangeFlags propertyChange), AbstractView::PropertyChangeFlags propertyChange),
(override)); (override));
MOCK_METHOD(void, nodeAboutToBeRemoved, (const QmlDesigner::ModelNode &removedNode), (override));
}; };

View File

@@ -0,0 +1,491 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "googletest.h"
#include "mocklistmodeleditorview.h"
#include "modelresourcemanagementmock.h"
#include "projectstoragemock.h"
#include <designercore/include/bindingproperty.h>
#include <designercore/include/model.h>
#include <designercore/include/modelnode.h>
#include <designercore/include/nodeabstractproperty.h>
#include <designercore/include/nodelistproperty.h>
#include <designercore/include/nodeproperty.h>
#include <designercore/include/signalhandlerproperty.h>
#include <designercore/include/variantproperty.h>
namespace {
using QmlDesigner::AbstractProperty;
using QmlDesigner::ModelNode;
using QmlDesigner::ModelNodes;
using QmlDesigner::ModelResourceSet;
template<typename Matcher>
auto HasPropertyName(const Matcher &matcher)
{
return Property(&AbstractProperty::name, matcher);
}
class Model : public ::testing::Test
{
protected:
Model()
{
model.attachView(&viewMock);
rootNode = viewMock.rootModelNode();
ON_CALL(resourceManagementMock, removeNode(_)).WillByDefault([](const auto &node) {
return ModelResourceSet{{node}, {}, {}};
});
ON_CALL(resourceManagementMock, removeProperty(_)).WillByDefault([](const auto &property) {
return ModelResourceSet{{}, {property}, {}};
});
}
~Model() { model.detachView(&viewMock); }
auto createNodeWithParent(const ModelNode &parentNode)
{
auto node = viewMock.createModelNode("QtQuick.Item");
parentNode.defaultNodeAbstractProperty().reparentHere(node);
return node;
}
auto createProperty(const ModelNode &parentNode, QmlDesigner::PropertyName name)
{
auto property = parentNode.variantProperty(name);
property.setValue(4);
return property;
}
protected:
NiceMock<MockListModelEditorView> viewMock;
NiceMock<ProjectStorageMockWithQtQtuick> projectStorageMock;
NiceMock<ModelResourceManagementMock> resourceManagementMock;
QmlDesigner::Model model{projectStorageMock,
"QtQuick.Item",
-1,
-1,
nullptr,
std::make_unique<ModelResourceManagementMockWrapper>(
resourceManagementMock)};
ModelNode rootNode;
};
TEST_F(Model, ModelNodeDestroyIsCallingModelResourceManagementRemoveNode)
{
auto node = createNodeWithParent(rootNode);
EXPECT_CALL(resourceManagementMock, removeNode(node));
node.destroy();
}
TEST_F(Model, ModelNodeRemoveProperyIsCallingModelResourceManagementRemoveProperty)
{
auto property = rootNode.variantProperty("foo");
property.setValue(4);
EXPECT_CALL(resourceManagementMock, removeProperty(Eq(property)));
rootNode.removeProperty("foo");
}
TEST_F(Model, NodeAbstractPropertyReparentHereIsCallingModelResourceManagementRemoveProperty)
{
auto node = createNodeWithParent(rootNode);
auto property = rootNode.variantProperty("foo");
property.setValue(4);
EXPECT_CALL(resourceManagementMock, removeProperty(Eq(property)));
rootNode.nodeListProperty("foo").reparentHere(node);
}
TEST_F(Model, NodePropertySetModelNodeIsCallingModelResourceManagementRemoveProperty)
{
auto node = createNodeWithParent(rootNode);
auto property = rootNode.variantProperty("foo");
property.setValue(4);
EXPECT_CALL(resourceManagementMock, removeProperty(Eq(property)));
rootNode.nodeProperty("foo").setModelNode(node);
}
TEST_F(Model, VariantPropertySetValueIsCallingModelResourceManagementRemoveProperty)
{
auto property = rootNode.bindingProperty("foo");
property.setExpression("blah");
EXPECT_CALL(resourceManagementMock, removeProperty(Eq(property)));
rootNode.variantProperty("foo").setValue(7);
}
TEST_F(Model,
VariantPropertySetDynamicTypeNameAndEnumerationIsCallingModelResourceManagementRemoveProperty)
{
auto property = rootNode.bindingProperty("foo");
property.setExpression("blah");
EXPECT_CALL(resourceManagementMock, removeProperty(Eq(property)));
rootNode.variantProperty("foo").setDynamicTypeNameAndEnumeration("int", "Ha");
}
TEST_F(Model, VariantPropertySetDynamicTypeNameAndValueIsCallingModelResourceManagementRemoveProperty)
{
auto property = rootNode.bindingProperty("foo");
property.setExpression("blah");
EXPECT_CALL(resourceManagementMock, removeProperty(Eq(property)));
rootNode.variantProperty("foo").setDynamicTypeNameAndValue("int", 7);
}
TEST_F(Model, BindingPropertySetExpressionIsCallingModelResourceManagementRemoveProperty)
{
auto property = rootNode.variantProperty("foo");
property.setValue(4);
EXPECT_CALL(resourceManagementMock, removeProperty(Eq(property)));
rootNode.bindingProperty("foo").setExpression("blah");
}
TEST_F(Model,
BindingPropertySetDynamicTypeNameAndExpressionIsCallingModelResourceManagementRemoveProperty)
{
auto property = rootNode.variantProperty("foo");
property.setValue(4);
EXPECT_CALL(resourceManagementMock, removeProperty(Eq(property)));
rootNode.bindingProperty("foo").setDynamicTypeNameAndExpression("int", "blah");
}
TEST_F(Model, SignalHandlerPropertySetSourceIsCallingModelResourceManagementRemoveProperty)
{
auto property = rootNode.bindingProperty("foo");
property.setExpression("blah");
EXPECT_CALL(resourceManagementMock, removeProperty(Eq(property)));
rootNode.signalHandlerProperty("foo").setSource("blah");
}
TEST_F(Model, SignalDeclarationPropertySetSignatureIsCallingModelResourceManagementRemoveProperty)
{
auto property = rootNode.bindingProperty("foo");
property.setExpression("blah");
EXPECT_CALL(resourceManagementMock, removeProperty(Eq(property)));
rootNode.signalDeclarationProperty("foo").setSignature("blah");
}
TEST_F(Model, ModelNodeDestroyIsCallingAbstractViewNodeAboutToBeRemoved)
{
auto node = createNodeWithParent(rootNode);
auto node2 = createNodeWithParent(rootNode);
ON_CALL(resourceManagementMock, removeNode(node))
.WillByDefault(Return(ModelResourceSet{{node, node2}, {}, {}}));
EXPECT_CALL(viewMock, nodeAboutToBeRemoved(Eq(node)));
EXPECT_CALL(viewMock, nodeAboutToBeRemoved(Eq(node2)));
node.destroy();
}
TEST_F(Model, ModelNodeDestroyIsCallingAbstractViewNodeRemoved)
{
auto node = createNodeWithParent(rootNode);
auto node2 = createNodeWithParent(rootNode);
ON_CALL(resourceManagementMock, removeNode(node))
.WillByDefault(Return(ModelResourceSet{{node, node2}, {}, {}}));
EXPECT_CALL(viewMock, nodeRemoved(Eq(node), _, _));
EXPECT_CALL(viewMock, nodeRemoved(Eq(node2), _, _));
node.destroy();
}
TEST_F(Model, ModelNodeDestroyIsCallingAbstractViewNodeRemovedWithValidNodes)
{
auto node = createNodeWithParent(rootNode);
auto node2 = createNodeWithParent(rootNode);
ON_CALL(resourceManagementMock, removeNode(node))
.WillByDefault(Return(ModelResourceSet{{node, node2, ModelNode{}}, {}, {}}));
EXPECT_CALL(viewMock, nodeRemoved(Eq(node), _, _));
EXPECT_CALL(viewMock, nodeRemoved(Eq(node2), _, _));
node.destroy();
}
TEST_F(Model, ModelNodeDestroyIsCallingAbstractViewPropertiesAboutToBeRemoved)
{
auto node = createNodeWithParent(rootNode);
auto property = createProperty(rootNode, "foo");
auto property2 = createProperty(rootNode, "bar");
ON_CALL(resourceManagementMock, removeNode(node))
.WillByDefault(Return(ModelResourceSet{{node}, {property, property2}, {}}));
EXPECT_CALL(viewMock, propertiesAboutToBeRemoved(ElementsAre(Eq(property))));
EXPECT_CALL(viewMock, propertiesAboutToBeRemoved(ElementsAre(Eq(property2))));
node.destroy();
}
TEST_F(Model, ModelNodeDestroyIsCallingAbstractViewPropertiesRemoved)
{
auto node = createNodeWithParent(rootNode);
auto property = createProperty(rootNode, "foo");
auto property2 = createProperty(rootNode, "bar");
ON_CALL(resourceManagementMock, removeNode(node))
.WillByDefault(Return(ModelResourceSet{{node}, {property, property2}, {}}));
EXPECT_CALL(viewMock, propertiesRemoved(ElementsAre(Eq(property))));
EXPECT_CALL(viewMock, propertiesRemoved(ElementsAre(Eq(property2))));
node.destroy();
}
TEST_F(Model, ModelNodeDestroyIsCallingAbstractViewPropertiesRemovedOnlyWithValidProperties)
{
auto node = createNodeWithParent(rootNode);
auto property = createProperty(rootNode, "foo");
auto property2 = createProperty(rootNode, "bar");
ON_CALL(resourceManagementMock, removeNode(node))
.WillByDefault(Return(ModelResourceSet{{node}, {property, property2, {}}, {}}));
EXPECT_CALL(viewMock, propertiesRemoved(ElementsAre(Eq(property))));
EXPECT_CALL(viewMock, propertiesRemoved(ElementsAre(Eq(property2))));
node.destroy();
}
TEST_F(Model, ModelNodeDestroyIsCallingAbstractViewBindingPropertiesAboutToBeChanged)
{
auto node = createNodeWithParent(rootNode);
auto property = rootNode.bindingProperty("foo");
auto property2 = rootNode.bindingProperty("bar");
ON_CALL(resourceManagementMock, removeNode(node))
.WillByDefault(Return(ModelResourceSet{{node}, {}, {{property, "yi"}, {property2, "er"}}}));
EXPECT_CALL(viewMock, bindingPropertiesAboutToBeChanged(ElementsAre(Eq(property))));
EXPECT_CALL(viewMock, bindingPropertiesAboutToBeChanged(ElementsAre(Eq(property2))));
node.destroy();
}
TEST_F(Model, ModelNodeDestroyIsCallingAbstractViewBindingPropertiesChanged)
{
auto node = createNodeWithParent(rootNode);
auto property = rootNode.bindingProperty("foo");
auto property2 = rootNode.bindingProperty("bar");
ON_CALL(resourceManagementMock, removeNode(node))
.WillByDefault(Return(ModelResourceSet{{node}, {}, {{property, "yi"}, {property2, "er"}}}));
EXPECT_CALL(viewMock, bindingPropertiesChanged(ElementsAre(Eq(property)), _));
EXPECT_CALL(viewMock, bindingPropertiesChanged(ElementsAre(Eq(property2)), _));
node.destroy();
}
TEST_F(Model, ModelNodeDestroyIsCallingAbstractViewBindingPropertiesChangedOnlyWithValidProperties)
{
auto node = createNodeWithParent(rootNode);
auto property = rootNode.bindingProperty("foo");
auto property2 = rootNode.bindingProperty("bar");
ON_CALL(resourceManagementMock, removeNode(node))
.WillByDefault(Return(
ModelResourceSet{{node}, {}, {{property, "yi"}, {property2, "er"}, {{}, "san"}}}));
EXPECT_CALL(viewMock, bindingPropertiesChanged(ElementsAre(Eq(property)), _));
EXPECT_CALL(viewMock, bindingPropertiesChanged(ElementsAre(Eq(property2)), _));
node.destroy();
}
TEST_F(Model, ModelNodeRemovePropertyIsCallingAbstractViewNodeAboutToBeRemoved)
{
auto property = createProperty(rootNode, "foo");
auto node = createNodeWithParent(rootNode);
auto node2 = createNodeWithParent(rootNode);
ON_CALL(resourceManagementMock, removeProperty(property))
.WillByDefault(Return(ModelResourceSet{{node, node2}, {property}, {}}));
EXPECT_CALL(viewMock, nodeAboutToBeRemoved(Eq(node)));
EXPECT_CALL(viewMock, nodeAboutToBeRemoved(Eq(node2)));
rootNode.removeProperty("foo");
}
TEST_F(Model, ModelNodeRemovePropertyIsCallingAbstractViewNodeRemoved)
{
auto property = createProperty(rootNode, "foo");
auto node = createNodeWithParent(rootNode);
auto node2 = createNodeWithParent(rootNode);
ON_CALL(resourceManagementMock, removeProperty(property))
.WillByDefault(Return(ModelResourceSet{{node, node2}, {property}, {}}));
EXPECT_CALL(viewMock, nodeRemoved(Eq(node), _, _));
EXPECT_CALL(viewMock, nodeRemoved(Eq(node2), _, _));
rootNode.removeProperty("foo");
}
TEST_F(Model, ModelNodeRemovePropertyIsCallingAbstractViewNodeRemovedWithValidNodes)
{
auto property = createProperty(rootNode, "foo");
auto node = createNodeWithParent(rootNode);
auto node2 = createNodeWithParent(rootNode);
ON_CALL(resourceManagementMock, removeProperty(property))
.WillByDefault(Return(ModelResourceSet{{node, node2, ModelNode{}}, {property}, {}}));
EXPECT_CALL(viewMock, nodeRemoved(Eq(node), _, _));
EXPECT_CALL(viewMock, nodeRemoved(Eq(node2), _, _));
rootNode.removeProperty("foo");
}
TEST_F(Model, ModelNodeRemovePropertyIsCallingAbstractViewPropertiesAboutToBeRemoved)
{
auto property = createProperty(rootNode, "yi");
auto property2 = createProperty(rootNode, "er");
ON_CALL(resourceManagementMock, removeProperty(property))
.WillByDefault(Return(ModelResourceSet{{}, {property, property2}, {}}));
EXPECT_CALL(viewMock, propertiesAboutToBeRemoved(ElementsAre(Eq(property))));
EXPECT_CALL(viewMock, propertiesAboutToBeRemoved(ElementsAre(Eq(property2))));
rootNode.removeProperty("yi");
}
TEST_F(Model, ModelNodeRemovePropertyIsCallingAbstractViewPropertiesRemoved)
{
auto property = createProperty(rootNode, "yi");
auto property2 = createProperty(rootNode, "er");
ON_CALL(resourceManagementMock, removeProperty(property))
.WillByDefault(Return(ModelResourceSet{{}, {property, property2}, {}}));
EXPECT_CALL(viewMock, propertiesRemoved(ElementsAre(Eq(property))));
EXPECT_CALL(viewMock, propertiesRemoved(ElementsAre(Eq(property2))));
rootNode.removeProperty("yi");
}
TEST_F(Model, ModelNodeRemovePropertyIsCallingAbstractViewPropertiesRemovedOnlyWithValidProperties)
{
auto property = createProperty(rootNode, "yi");
auto property2 = createProperty(rootNode, "er");
ON_CALL(resourceManagementMock, removeProperty(property))
.WillByDefault(Return(ModelResourceSet{{}, {property, property2, {}}, {}}));
EXPECT_CALL(viewMock, propertiesRemoved(ElementsAre(Eq(property))));
EXPECT_CALL(viewMock, propertiesRemoved(ElementsAre(Eq(property2))));
rootNode.removeProperty("yi");
}
TEST_F(Model, ModelNodeRemovePropertyIsCallingAbstractViewBindingPropertiesAboutToBeChanged)
{
auto property = createProperty(rootNode, "yi");
auto property1 = rootNode.bindingProperty("foo");
auto property2 = rootNode.bindingProperty("bar");
ON_CALL(resourceManagementMock, removeProperty(property))
.WillByDefault(
Return(ModelResourceSet{{}, {property}, {{property1, "yi"}, {property2, "er"}}}));
EXPECT_CALL(viewMock, bindingPropertiesAboutToBeChanged(ElementsAre(Eq(property1))));
EXPECT_CALL(viewMock, bindingPropertiesAboutToBeChanged(ElementsAre(Eq(property2))));
rootNode.removeProperty("yi");
}
TEST_F(Model, ModelNodeRemovePropertyIsCallingAbstractViewBindingPropertiesChanged)
{
auto property = createProperty(rootNode, "yi");
auto property1 = rootNode.bindingProperty("foo");
auto property2 = rootNode.bindingProperty("bar");
ON_CALL(resourceManagementMock, removeProperty(property))
.WillByDefault(
Return(ModelResourceSet{{}, {property}, {{property1, "yi"}, {property2, "er"}}}));
EXPECT_CALL(viewMock, bindingPropertiesChanged(ElementsAre(Eq(property1)), _));
EXPECT_CALL(viewMock, bindingPropertiesChanged(ElementsAre(Eq(property2)), _));
rootNode.removeProperty("yi");
}
TEST_F(Model,
ModelNodeRemovePropertyIsCallingAbstractViewBindingPropertiesChangedOnlyWithValidProperties)
{
auto property = createProperty(rootNode, "yi");
auto property1 = rootNode.bindingProperty("foo");
auto property2 = rootNode.bindingProperty("bar");
ON_CALL(resourceManagementMock, removeProperty(property))
.WillByDefault(
Return(ModelResourceSet{{}, {property}, {{property1, "yi"}, {property2, "er"}, {}}}));
EXPECT_CALL(viewMock, bindingPropertiesChanged(ElementsAre(Eq(property1)), _));
EXPECT_CALL(viewMock, bindingPropertiesChanged(ElementsAre(Eq(property2)), _));
rootNode.removeProperty("yi");
}
TEST_F(Model, ByDefaultRemoveModelNodeRemovesNode)
{
model.detachView(&viewMock);
QmlDesigner::Model newModel{projectStorageMock, "QtQuick.Item"};
newModel.attachView(&viewMock);
auto node = createNodeWithParent(viewMock.rootModelNode());
EXPECT_CALL(viewMock, nodeAboutToBeRemoved(Eq(node)));
node.destroy();
}
TEST_F(Model, ByDefaultRemovePropertiesRemovesProperty)
{
model.detachView(&viewMock);
QmlDesigner::Model newModel{projectStorageMock, "QtQuick.Item"};
newModel.attachView(&viewMock);
rootNode = viewMock.rootModelNode();
auto property = createProperty(rootNode, "yi");
EXPECT_CALL(viewMock, propertiesAboutToBeRemoved(ElementsAre(Eq(property))));
rootNode.removeProperty("yi");
}
TEST_F(Model, ByDefaultRemoveModelNodeInFactoryMethodCallsRemovesNode)
{
model.detachView(&viewMock);
auto newModel = QmlDesigner::Model::create(projectStorageMock, "QtQuick.Item");
newModel->attachView(&viewMock);
auto node = createNodeWithParent(viewMock.rootModelNode());
EXPECT_CALL(viewMock, nodeAboutToBeRemoved(Eq(node)));
node.destroy();
}
TEST_F(Model, ByDefaultRemovePropertiesInFactoryMethodCallsRemoveProperty)
{
model.detachView(&viewMock);
auto newModel = QmlDesigner::Model::create(projectStorageMock, "QtQuick.Item");
newModel->attachView(&viewMock);
rootNode = viewMock.rootModelNode();
auto property = createProperty(rootNode, "yi");
EXPECT_CALL(viewMock, propertiesAboutToBeRemoved(ElementsAre(Eq(property))));
rootNode.removeProperty("yi");
}
} // namespace

View File

@@ -0,0 +1,42 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#pragma once
#include "googletest.h"
#include <model/modelresourcemanagementinterface.h>
#include <modelnode.h>
class ModelResourceManagementMock : public QmlDesigner::ModelResourceManagementInterface
{
public:
MOCK_METHOD(QmlDesigner::ModelResourceSet,
removeNode,
(const QmlDesigner::ModelNode &),
(const, override));
MOCK_METHOD(QmlDesigner::ModelResourceSet,
removeProperty,
(const QmlDesigner::AbstractProperty &),
(const, override));
};
class ModelResourceManagementMockWrapper : public QmlDesigner::ModelResourceManagementInterface
{
public:
ModelResourceManagementMockWrapper(ModelResourceManagementMock &mock)
: mock{mock}
{}
QmlDesigner::ModelResourceSet removeNode(const QmlDesigner::ModelNode &node) const override
{
return mock.removeNode(node);
}
QmlDesigner::ModelResourceSet removeProperty(const QmlDesigner::AbstractProperty &property) const override
{
return mock.removeProperty(property);
}
ModelResourceManagementMock &mock;
};

View File

@@ -25,11 +25,6 @@ protected:
using iterator = QmlDesigner::NodeListProperty::iterator; using iterator = QmlDesigner::NodeListProperty::iterator;
NodeListProperty() NodeListProperty()
{ {
ModuleId modelId_QtQuick = ModuleId::create(1);
setModuleId("QtQuick", modelId_QtQuick);
setType(modelId_QtQuick, "Item", "data");
model = std::make_unique<QmlDesigner::Model>(projectStorageMock, "QtQuick.Item");
model->attachView(&abstractViewMock); model->attachView(&abstractViewMock);
nodeListProperty = abstractViewMock.rootModelNode().nodeListProperty("foo"); nodeListProperty = abstractViewMock.rootModelNode().nodeListProperty("foo");
@@ -82,8 +77,9 @@ protected:
} }
protected: protected:
NiceMock<ProjectStorageMock> projectStorageMock; NiceMock<ProjectStorageMockWithQtQtuick> projectStorageMock;
std::unique_ptr<QmlDesigner::Model> model; std::unique_ptr<QmlDesigner::Model> model{
std::make_unique<QmlDesigner::Model>(projectStorageMock, "QtQuick.Item")};
NiceMock<AbstractViewMock> abstractViewMock; NiceMock<AbstractViewMock> abstractViewMock;
QmlDesigner::NodeListProperty nodeListProperty; QmlDesigner::NodeListProperty nodeListProperty;
ModelNode node1; ModelNode node1;

View File

@@ -0,0 +1,89 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "projectstoragemock.h"
namespace QmlDesigner {
namespace {
template<typename BasicId>
void incrementBasicId(BasicId &id)
{
id = BasicId::create(id.internalId() + 1);
}
ModuleId createModule(ProjectStorageMock &mock, Utils::SmallStringView moduleName)
{
static ModuleId moduleId;
incrementBasicId(moduleId);
ON_CALL(mock, moduleId(Eq(moduleName))).WillByDefault(Return(moduleId));
return moduleId;
}
TypeId createType(ProjectStorageMock &mock,
ModuleId moduleId,
Utils::SmallStringView typeName,
Utils::SmallString defaultPropertyName,
Storage::TypeTraits typeTraits,
TypeId baseTypeId = TypeId{})
{
static TypeId typeId;
incrementBasicId(typeId);
static PropertyDeclarationId defaultPropertyId;
incrementBasicId(defaultPropertyId);
ON_CALL(mock, typeId(Eq(moduleId), Eq(typeName), _)).WillByDefault(Return(typeId));
ON_CALL(mock, type(Eq(typeId)))
.WillByDefault(Return(Storage::Info::Type{defaultPropertyId, typeTraits}));
ON_CALL(mock, propertyName(Eq(defaultPropertyId))).WillByDefault(Return(defaultPropertyName));
if (baseTypeId)
ON_CALL(mock, isBasedOn(Eq(typeId), Eq(baseTypeId))).WillByDefault(Return(true));
return typeId;
}
TypeId createObject(ProjectStorageMock &mock,
ModuleId moduleId,
Utils::SmallStringView typeName,
Utils::SmallString defaultPropertyName,
TypeId baseTypeId = TypeId{})
{
return createType(
mock, moduleId, typeName, defaultPropertyName, Storage::TypeTraits::Reference, baseTypeId);
}
void setupIsBasedOn(ProjectStorageMock &mock)
{
auto call = [&](TypeId typeId, auto... ids) -> bool {
return (mock.isBasedOn(typeId, ids) || ...);
};
ON_CALL(mock, isBasedOn(_, _, _)).WillByDefault(call);
ON_CALL(mock, isBasedOn(_, _, _, _)).WillByDefault(call);
ON_CALL(mock, isBasedOn(_, _, _, _, _)).WillByDefault(call);
ON_CALL(mock, isBasedOn(_, _, _, _, _, _)).WillByDefault(call);
ON_CALL(mock, isBasedOn(_, _, _, _, _, _, _)).WillByDefault(call);
ON_CALL(mock, isBasedOn(_, _, _, _, _, _, _, _)).WillByDefault(call);
}
} // namespace
} // namespace QmlDesigner
void ProjectStorageMock::setupQtQtuick()
{
QmlDesigner::setupIsBasedOn(*this);
auto qmlModuleId = QmlDesigner::createModule(*this, "QML");
auto qtQmlModelsModuleId = QmlDesigner::createModule(*this, "QtQml.Models");
auto qtQuickModuleId = QmlDesigner::createModule(*this, "QtQuick");
auto qtObjectId = QmlDesigner::createObject(*this, qmlModuleId, "QtObject", "children");
QmlDesigner::createObject(*this, qtQmlModelsModuleId, "ListModel", "children", qtObjectId);
QmlDesigner::createObject(*this, qtQmlModelsModuleId, "ListElement", "children", qtObjectId);
auto itemId = QmlDesigner::createObject(*this, qtQuickModuleId, "Item", "data", qtObjectId);
QmlDesigner::createObject(*this, qtQuickModuleId, "ListView", "data", itemId);
}

View File

@@ -1,4 +1,4 @@
// Copyright (C) 2017 The Qt Company Ltd. // Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#pragma once #pragma once
@@ -14,6 +14,8 @@
class ProjectStorageMock : public QmlDesigner::ProjectStorageInterface class ProjectStorageMock : public QmlDesigner::ProjectStorageInterface
{ {
public: public:
void setupQtQtuick();
MOCK_METHOD(void, MOCK_METHOD(void,
synchronize, synchronize,
(QmlDesigner::Storage::Synchronization::SynchronizationPackage package), (QmlDesigner::Storage::Synchronization::SynchronizationPackage package),
@@ -63,7 +65,6 @@ public:
(const, override)); (const, override));
MOCK_METHOD(QmlDesigner::TypeIds, prototypeAndSelfIds, (QmlDesigner::TypeId type), (const, override)); MOCK_METHOD(QmlDesigner::TypeIds, prototypeAndSelfIds, (QmlDesigner::TypeId type), (const, override));
MOCK_METHOD(QmlDesigner::TypeIds, prototypeIds, (QmlDesigner::TypeId type), (const, override)); MOCK_METHOD(QmlDesigner::TypeIds, prototypeIds, (QmlDesigner::TypeId type), (const, override));
MOCK_METHOD(bool, isBasedOn, (QmlDesigner::TypeId typeId), (const, override));
MOCK_METHOD(bool, isBasedOn, (QmlDesigner::TypeId typeId, QmlDesigner::TypeId), (const, override)); MOCK_METHOD(bool, isBasedOn, (QmlDesigner::TypeId typeId, QmlDesigner::TypeId), (const, override));
MOCK_METHOD(bool, MOCK_METHOD(bool,
isBasedOn, isBasedOn,
@@ -158,3 +159,8 @@ public:
MOCK_METHOD(std::vector<QmlDesigner::Cache::Source>, fetchAllSources, (), ()); MOCK_METHOD(std::vector<QmlDesigner::Cache::Source>, fetchAllSources, (), ());
}; };
class ProjectStorageMockWithQtQtuick : public ProjectStorageMock
{
public:
ProjectStorageMockWithQtQtuick() { setupQtQtuick(); }
};