aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThomas Hartmann <thomas.hartmann@qt.io>2022-07-13 12:07:58 +0200
committerThomas Hartmann <thomas.hartmann@qt.io>2022-07-13 15:33:07 +0000
commitaeea22257c32ac06c71a9183c5964372fd7d01eb (patch)
tree0685133d14187823abd07673a7cf0c58f3ba69b4
parentc505d6a72e80114521f23105738f814585e36356 (diff)
QmlDesigner: Add support for Behaviours
A Behavior will be added as a normal ModelNode to the default property, but we store the property name in behaviorPropertyName. The value of behaviorPropertyName cannot be changed after the ModelNode was created, since I do not see any use case and it keeps things simple. Change-Id: I69ba1d4d706432cfbbd35b001238f623e6e0b4fd Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org> Reviewed-by: Marco Bubke <marco.bubke@qt.io> Reviewed-by: <github-actions-qt-creator@cristianadam.eu>
-rw-r--r--src/plugins/qmldesigner/designercore/include/abstractview.h13
-rw-r--r--src/plugins/qmldesigner/designercore/include/modelnode.h1
-rw-r--r--src/plugins/qmldesigner/designercore/model/abstractview.cpp17
-rw-r--r--src/plugins/qmldesigner/designercore/model/internalnode.cpp10
-rw-r--r--src/plugins/qmldesigner/designercore/model/internalnode_p.h5
-rw-r--r--src/plugins/qmldesigner/designercore/model/model.cpp7
-rw-r--r--src/plugins/qmldesigner/designercore/model/model_p.h1
-rw-r--r--src/plugins/qmldesigner/designercore/model/modelnode.cpp8
-rw-r--r--src/plugins/qmldesigner/designercore/model/qmltextgenerator.cpp4
-rw-r--r--src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp30
-rw-r--r--tests/auto/qml/qmldesigner/coretests/tst_testcore.cpp97
-rw-r--r--tests/auto/qml/qmldesigner/coretests/tst_testcore.h1
12 files changed, 173 insertions, 21 deletions
diff --git a/src/plugins/qmldesigner/designercore/include/abstractview.h b/src/plugins/qmldesigner/designercore/include/abstractview.h
index 3c4eca0441..e41e93960c 100644
--- a/src/plugins/qmldesigner/designercore/include/abstractview.h
+++ b/src/plugins/qmldesigner/designercore/include/abstractview.h
@@ -112,12 +112,13 @@ public:
ModelNode createModelNode(const TypeName &typeName);
ModelNode createModelNode(const TypeName &typeName,
- int majorVersion,
- int minorVersion,
- const PropertyListType &propertyList = PropertyListType(),
- const PropertyListType &auxPropertyList = PropertyListType(),
- const QString &nodeSource = QString(),
- ModelNode::NodeSourceType nodeSourceType = ModelNode::NodeWithoutSource);
+ int majorVersion,
+ int minorVersion,
+ const PropertyListType &propertyList = PropertyListType(),
+ const PropertyListType &auxPropertyList = PropertyListType(),
+ const QString &nodeSource = {},
+ ModelNode::NodeSourceType nodeSourceType = ModelNode::NodeWithoutSource,
+ const QString &behaviorPropertyName = {});
ModelNode rootModelNode() const;
ModelNode rootModelNode();
diff --git a/src/plugins/qmldesigner/designercore/include/modelnode.h b/src/plugins/qmldesigner/designercore/include/modelnode.h
index 92884a9710..d187332e61 100644
--- a/src/plugins/qmldesigner/designercore/include/modelnode.h
+++ b/src/plugins/qmldesigner/designercore/include/modelnode.h
@@ -238,6 +238,7 @@ public:
bool isComponent() const;
bool isSubclassOf(const TypeName &typeName, int majorVersion = -1, int minorVersion = -1) const;
QIcon typeIcon() const;
+ QString behaviorPropertyName() const;
friend void swap(ModelNode &first, ModelNode &second) noexcept
{
diff --git a/src/plugins/qmldesigner/designercore/model/abstractview.cpp b/src/plugins/qmldesigner/designercore/model/abstractview.cpp
index 310dcdcdab..f3c757e81d 100644
--- a/src/plugins/qmldesigner/designercore/model/abstractview.cpp
+++ b/src/plugins/qmldesigner/designercore/model/abstractview.cpp
@@ -97,14 +97,15 @@ ModelNode AbstractView::createModelNode(const TypeName &typeName)
}
ModelNode AbstractView::createModelNode(const TypeName &typeName,
- int majorVersion,
- int minorVersion,
- const QList<QPair<PropertyName, QVariant> > &propertyList,
- const QList<QPair<PropertyName, QVariant> > &auxPropertyList,
- const QString &nodeSource,
- ModelNode::NodeSourceType nodeSourceType)
-{
- return ModelNode(model()->d->createNode(typeName, majorVersion, minorVersion, propertyList, auxPropertyList, nodeSource, nodeSourceType), model(), this);
+ int majorVersion,
+ int minorVersion,
+ const QList<QPair<PropertyName, QVariant>> &propertyList,
+ const QList<QPair<PropertyName, QVariant>> &auxPropertyList,
+ const QString &nodeSource,
+ ModelNode::NodeSourceType nodeSourceType,
+ const QString &behaviorPropertyName)
+{
+ return ModelNode(model()->d->createNode(typeName, majorVersion, minorVersion, propertyList, auxPropertyList, nodeSource, nodeSourceType, behaviorPropertyName), model(), this);
}
diff --git a/src/plugins/qmldesigner/designercore/model/internalnode.cpp b/src/plugins/qmldesigner/designercore/model/internalnode.cpp
index 532a156082..37c1c21b40 100644
--- a/src/plugins/qmldesigner/designercore/model/internalnode.cpp
+++ b/src/plugins/qmldesigner/designercore/model/internalnode.cpp
@@ -389,5 +389,15 @@ void InternalNode::setNodeSourceType(int i)
m_nodeSourceType = i;
}
+QString InternalNode::behaviorPropertyName() const
+{
+ return m_behaviorPropertyName;
+}
+
+void InternalNode::setBehaviorPropertyName(const QString &name)
+{
+ m_behaviorPropertyName = name;
+}
+
}
}
diff --git a/src/plugins/qmldesigner/designercore/model/internalnode_p.h b/src/plugins/qmldesigner/designercore/model/internalnode_p.h
index 8770763b4e..d63fbed744 100644
--- a/src/plugins/qmldesigner/designercore/model/internalnode_p.h
+++ b/src/plugins/qmldesigner/designercore/model/internalnode_p.h
@@ -125,6 +125,9 @@ public:
int nodeSourceType() const;
void setNodeSourceType(int i);
+ QString behaviorPropertyName() const;
+ void setBehaviorPropertyName(const QString &name);
+
protected:
Pointer internalPointer() const;
void setInternalWeakPointer(const Pointer &pointer);
@@ -151,6 +154,8 @@ private:
QString m_nodeSource;
int m_nodeSourceType = 0;
+
+ QString m_behaviorPropertyName;
};
Utils::QHashValueType qHash(const InternalNodePointer& node);
diff --git a/src/plugins/qmldesigner/designercore/model/model.cpp b/src/plugins/qmldesigner/designercore/model/model.cpp
index 772149a33d..acea8e6169 100644
--- a/src/plugins/qmldesigner/designercore/model/model.cpp
+++ b/src/plugins/qmldesigner/designercore/model/model.cpp
@@ -97,9 +97,11 @@ ModelPrivate::ModelPrivate(Model *model)
0,
PropertyListType(),
PropertyListType(),
- QString(),
+ {},
ModelNode::NodeWithoutSource,
+ {},
true);
+
m_currentStateNode = m_rootInternalNode;
m_currentTimelineNode = m_rootInternalNode;
}
@@ -250,6 +252,7 @@ InternalNodePointer ModelPrivate::createNode(const TypeName &typeName,
const QList<QPair<PropertyName, QVariant>> &auxPropertyList,
const QString &nodeSource,
ModelNode::NodeSourceType nodeSourceType,
+ const QString &behaviorPropertyName,
bool isRootNode)
{
if (typeName.isEmpty())
@@ -263,6 +266,8 @@ InternalNodePointer ModelPrivate::createNode(const TypeName &typeName,
InternalNodePointer newNode = InternalNode::create(typeName, majorVersion, minorVersion, internalId);
newNode->setNodeSourceType(nodeSourceType);
+ newNode->setBehaviorPropertyName(behaviorPropertyName);
+
using PropertyPair = QPair<PropertyName, QVariant>;
for (const PropertyPair &propertyPair : propertyList) {
diff --git a/src/plugins/qmldesigner/designercore/model/model_p.h b/src/plugins/qmldesigner/designercore/model/model_p.h
index c6ed24f681..7e81ee3cd5 100644
--- a/src/plugins/qmldesigner/designercore/model/model_p.h
+++ b/src/plugins/qmldesigner/designercore/model/model_p.h
@@ -103,6 +103,7 @@ public:
const QList<QPair<PropertyName, QVariant> > &auxPropertyList,
const QString &nodeSource,
ModelNode::NodeSourceType nodeSourceType,
+ const QString &behaviorPropertyName,
bool isRootNode = false);
diff --git a/src/plugins/qmldesigner/designercore/model/modelnode.cpp b/src/plugins/qmldesigner/designercore/model/modelnode.cpp
index 2e31b1a824..7d65d0495f 100644
--- a/src/plugins/qmldesigner/designercore/model/modelnode.cpp
+++ b/src/plugins/qmldesigner/designercore/model/modelnode.cpp
@@ -1436,4 +1436,12 @@ QIcon ModelNode::typeIcon() const
return QIcon(QStringLiteral(":/ItemLibrary/images/item-invalid-icon.png"));
}
+QString ModelNode::behaviorPropertyName() const
+{
+ if (m_internalNode.isNull())
+ return {};
+
+ return m_internalNode->behaviorPropertyName();
+}
+
}
diff --git a/src/plugins/qmldesigner/designercore/model/qmltextgenerator.cpp b/src/plugins/qmldesigner/designercore/model/qmltextgenerator.cpp
index 3481724d7b..4c9311a4cf 100644
--- a/src/plugins/qmldesigner/designercore/model/qmltextgenerator.cpp
+++ b/src/plugins/qmldesigner/designercore/model/qmltextgenerator.cpp
@@ -218,6 +218,10 @@ QString QmlTextGenerator::toQml(const ModelNode &node, int indentDepth) const
result = alias + '.';
result += type;
+ if (!node.behaviorPropertyName().isEmpty()) {
+ result += " on " + node.behaviorPropertyName();
+ }
+
result += QStringLiteral(" {\n");
const int propertyIndentDepth = indentDepth + m_tabSettings.m_indentSize;
diff --git a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp
index 2cddff7dfd..d7e215fcd5 100644
--- a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp
+++ b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp
@@ -1183,6 +1183,15 @@ void TextToModelMerger::syncNode(ModelNode &modelNode,
ReadingContext *context,
DifferenceHandler &differenceHandler)
{
+ auto binding = AST::cast<AST::UiObjectBinding *>(astNode);
+
+ const bool hasOnToken = binding && binding->hasOnToken;
+
+ QString onTokenProperty;
+
+ if (hasOnToken)
+ onTokenProperty = toString(binding->qualifiedId);
+
AST::UiQualifiedId *astObjectType = qualifiedTypeNameId(astNode);
AST::UiObjectInitializer *astInitializer = initializerOfObject(astNode);
@@ -1219,10 +1228,10 @@ void TextToModelMerger::syncNode(ModelNode &modelNode,
bool isImplicitComponent = modelNode.hasParentProperty() && propertyIsComponentType(modelNode.parentProperty(), typeName, modelNode.model());
-
- if (modelNode.type() != typeName //If there is no valid parentProperty //the node has just been created. The type is correct then.
- || modelNode.majorVersion() != majorVersion
- || modelNode.minorVersion() != minorVersion) {
+ if (modelNode.type()
+ != typeName //If there is no valid parentProperty //the node has just been created. The type is correct then.
+ || modelNode.majorVersion() != majorVersion || modelNode.minorVersion() != minorVersion
+ || modelNode.behaviorPropertyName() != onTokenProperty) {
const bool isRootNode = m_rewriterView->rootModelNode() == modelNode;
differenceHandler.typeDiffers(isRootNode, modelNode, typeName,
majorVersion, minorVersion,
@@ -1289,7 +1298,8 @@ void TextToModelMerger::syncNode(ModelNode &modelNode,
} else if (auto binding = AST::cast<AST::UiObjectBinding *>(member)) {
const QString astPropertyName = toString(binding->qualifiedId);
if (binding->hasOnToken) {
- // skip value sources
+ // Store Behaviours in the default property
+ defaultPropertyItems.append(member);
} else {
const Value *propertyType = nullptr;
const ObjectValue *containingObject = nullptr;
@@ -1685,6 +1695,13 @@ ModelNode TextToModelMerger::createModelNode(const TypeName &typeName,
{
QString nodeSource;
+ auto binding = AST::cast<AST::UiObjectBinding *>(astNode);
+
+ const bool hasOnToken = binding && binding->hasOnToken;
+
+ QString onTokenProperty;
+ if (hasOnToken)
+ onTokenProperty = toString(binding->qualifiedId);
AST::UiQualifiedId *astObjectType = qualifiedTypeNameId(astNode);
@@ -1716,7 +1733,8 @@ ModelNode TextToModelMerger::createModelNode(const TypeName &typeName,
PropertyListType(),
PropertyListType(),
nodeSource,
- nodeSourceType);
+ nodeSourceType,
+ onTokenProperty);
syncNode(newNode, astNode, context, differenceHandler);
return newNode;
diff --git a/tests/auto/qml/qmldesigner/coretests/tst_testcore.cpp b/tests/auto/qml/qmldesigner/coretests/tst_testcore.cpp
index ef77342719..4813ac96f2 100644
--- a/tests/auto/qml/qmldesigner/coretests/tst_testcore.cpp
+++ b/tests/auto/qml/qmldesigner/coretests/tst_testcore.cpp
@@ -1248,6 +1248,103 @@ void tst_TestCore::testRewriterReparentToNewNode()
QCOMPARE(testRewriterView->allModelNodes().count(), 8);
}
+void tst_TestCore::testRewriterBehaivours()
+{
+ const QLatin1String qmlString("\n"
+ "import QtQuick 2.0\n"
+ "\n"
+ "Item {\n"
+ " Item {}\n"
+ " Item {}\n"
+ " Behavior on width {\n"
+ " NumberAnimation { duration: 1000 }\n"
+ " }\n"
+ " Item {}\n"
+ "}\n");
+
+
+ QPlainTextEdit textEdit;
+ textEdit.setPlainText(qmlString);
+ NotIndentingTextEditModifier modifier(&textEdit);
+
+ QScopedPointer<Model> model(Model::create("QtQuick.Rectangle"));
+
+ QScopedPointer<TestRewriterView> testRewriterView(new TestRewriterView(0, RewriterView::Amend));
+ testRewriterView->setTextModifier(&modifier);
+ model->attachView(testRewriterView.data());
+
+ QVERIFY(testRewriterView->errors().isEmpty());
+
+ ModelNode rootModelNode = testRewriterView->rootModelNode();
+ QVERIFY(rootModelNode.isValid());
+
+ const QList<ModelNode> children = rootModelNode.directSubModelNodes();
+
+ QCOMPARE(children.count(), 4);
+
+ ModelNode behavior;
+ for (const ModelNode &child : children) {
+ if (child.type() == "QtQuick.Behavior")
+ behavior = child;
+ }
+
+ QVERIFY(behavior.isValid());
+ QVERIFY(!behavior.behaviorPropertyName().isEmpty());
+ QCOMPARE(behavior.behaviorPropertyName(), "width");
+
+ QVERIFY(!behavior.directSubModelNodes().isEmpty());
+
+ ModelNode animation = behavior.directSubModelNodes().first();
+
+ QVERIFY(animation.isValid());
+
+ NodeMetaInfo metaInfo = behavior.metaInfo();
+
+ QVERIFY(metaInfo.isValid());
+
+ ModelNode newBehavior = testRewriterView->createModelNode("QtQuick.Behavior",
+ metaInfo.majorVersion(),
+ metaInfo.minorVersion(),
+ {},
+ {},
+ {},
+ ModelNode::NodeWithoutSource,
+ "height");
+
+ rootModelNode.defaultNodeListProperty().reparentHere(newBehavior);
+
+ QCOMPARE(newBehavior.behaviorPropertyName(), "height");
+
+ metaInfo = animation.metaInfo();
+ QVERIFY(metaInfo.isValid());
+ ModelNode newAnimation = testRewriterView->createModelNode(metaInfo.typeName(),
+ metaInfo.majorVersion(),
+ metaInfo.minorVersion());
+
+ newBehavior.defaultNodeListProperty().reparentHere(newAnimation);
+
+ newAnimation.variantProperty("duration").setValue(500);
+
+ const QLatin1String expextedQmlCode(
+ "\nimport QtQuick 2.0\n\n"
+ "Item {\n Item {}\n Item {}\n"
+ " Behavior on width {\n "
+ " NumberAnimation { duration: 1000 }\n"
+ " }\n"
+ " Item {}\n\n"
+ " Behavior on height {\n"
+ " NumberAnimation {\n"
+ " duration: 500\n"
+ " }\n }\n}\n");
+
+
+ QCOMPARE(textEdit.toPlainText(), expextedQmlCode);
+
+ newBehavior.destroy();
+
+ QVERIFY(!newBehavior.isValid());
+}
+
void tst_TestCore::testRewriterForGradientMagic()
{
const QLatin1String qmlString("\n"
diff --git a/tests/auto/qml/qmldesigner/coretests/tst_testcore.h b/tests/auto/qml/qmldesigner/coretests/tst_testcore.h
index 5248763fef..d55bb2a196 100644
--- a/tests/auto/qml/qmldesigner/coretests/tst_testcore.h
+++ b/tests/auto/qml/qmldesigner/coretests/tst_testcore.h
@@ -145,6 +145,7 @@ private slots:
void testRewriterUnicodeChars();
void testRewriterTransactionAddingAfterReparenting();
void testRewriterReparentToNewNode();
+ void testRewriterBehaivours();
//
// unit tests QmlModelNodeFacade/QmlModelState