forked from qt-creator/qt-creator
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>
This commit is contained in:
@@ -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();
|
||||
|
@@ -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
|
||||
{
|
||||
|
@@ -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)
|
||||
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), model(), this);
|
||||
return ModelNode(model()->d->createNode(typeName, majorVersion, minorVersion, propertyList, auxPropertyList, nodeSource, nodeSourceType, behaviorPropertyName), model(), this);
|
||||
}
|
||||
|
||||
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@@ -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);
|
||||
|
@@ -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) {
|
||||
|
@@ -103,6 +103,7 @@ public:
|
||||
const QList<QPair<PropertyName, QVariant> > &auxPropertyList,
|
||||
const QString &nodeSource,
|
||||
ModelNode::NodeSourceType nodeSourceType,
|
||||
const QString &behaviorPropertyName,
|
||||
bool isRootNode = false);
|
||||
|
||||
|
||||
|
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -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;
|
||||
|
@@ -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;
|
||||
|
@@ -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"
|
||||
|
@@ -145,6 +145,7 @@ private slots:
|
||||
void testRewriterUnicodeChars();
|
||||
void testRewriterTransactionAddingAfterReparenting();
|
||||
void testRewriterReparentToNewNode();
|
||||
void testRewriterBehaivours();
|
||||
|
||||
//
|
||||
// unit tests QmlModelNodeFacade/QmlModelState
|
||||
|
Reference in New Issue
Block a user