From 75f867b9bf6829c2ec58d7ac381563c61774d859 Mon Sep 17 00:00:00 2001 From: Tim Jenssen Date: Wed, 21 Dec 2016 12:06:25 +0100 Subject: [PATCH] QmlDesigner: Implementing changing the type of a node in the model The type of a ModelNode can now be changed in the model. The rewriter can already handle the case and the NodeInstanceView does a reset. We still have to expose this new feature in the UI. Change-Id: I9bc405d40b123f257324ba582a4451fbd395f24f Reviewed-by: Tim Jenssen --- .../components/navigator/navigatorview.cpp | 6 ++ .../components/navigator/navigatorview.h | 1 + .../propertyeditor/propertyeditorview.cpp | 6 ++ .../propertyeditor/propertyeditorview.h | 1 + .../designercore/include/abstractview.h | 1 + .../designercore/include/modelnode.h | 1 + .../designercore/include/nodeinstanceview.h | 1 + .../designercore/include/rewriterview.h | 1 + .../instances/nodeinstanceview.cpp | 5 ++ .../designercore/model/abstractview.cpp | 5 ++ .../qmldesigner/designercore/model/model.cpp | 47 ++++++++++ .../qmldesigner/designercore/model/model_p.h | 2 + .../designercore/model/modelnode.cpp | 11 +++ .../designercore/model/rewriterview.cpp | 12 +++ .../qmldesigner/coretests/tst_testcore.cpp | 89 +++++++++++++++++++ .../qml/qmldesigner/coretests/tst_testcore.h | 1 + 16 files changed, 190 insertions(+) diff --git a/src/plugins/qmldesigner/components/navigator/navigatorview.cpp b/src/plugins/qmldesigner/components/navigator/navigatorview.cpp index 136b150a81..3daf63312a 100644 --- a/src/plugins/qmldesigner/components/navigator/navigatorview.cpp +++ b/src/plugins/qmldesigner/components/navigator/navigatorview.cpp @@ -238,6 +238,12 @@ void NavigatorView::rootNodeTypeChanged(const QString & /*type*/, int /*majorVer m_treeModel->updateItemRow(rootModelNode()); } +void NavigatorView::nodeTypeChanged(const ModelNode &node, const TypeName &, int , int) +{ + if (m_treeModel->isInTree(node)) + m_treeModel->updateItemRow(node); +} + void NavigatorView::auxiliaryDataChanged(const ModelNode &modelNode, const PropertyName & name, const QVariant & /*data*/) { if (name == "invisible" && m_treeModel->isInTree(modelNode)) diff --git a/src/plugins/qmldesigner/components/navigator/navigatorview.h b/src/plugins/qmldesigner/components/navigator/navigatorview.h index 78fa3a92e0..03f0631c97 100644 --- a/src/plugins/qmldesigner/components/navigator/navigatorview.h +++ b/src/plugins/qmldesigner/components/navigator/navigatorview.h @@ -62,6 +62,7 @@ public: void nodeReparented(const ModelNode &node, const NodeAbstractProperty &newPropertyParent, const NodeAbstractProperty &oldPropertyParent, AbstractView::PropertyChangeFlags propertyChange) override; void rootNodeTypeChanged(const QString &type, int majorVersion, int minorVersion) override; + void nodeTypeChanged(const ModelNode& node, const TypeName &type, int majorVersion, int minorVersion) override; void nodeIdChanged(const ModelNode& node, const QString& newId, const QString& oldId) override; void propertiesAboutToBeRemoved(const QList& propertyList) override; void propertiesRemoved(const QList& propertyList) override; diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp index f55c7353b7..b43748a51d 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp @@ -720,6 +720,12 @@ void PropertyEditorView::rootNodeTypeChanged(const QString &/*type*/, int /*majo // TODO: we should react to this case } +void PropertyEditorView::nodeTypeChanged(const ModelNode &node, const TypeName &, int, int) +{ + if (node == m_selectedNode) + delayedResetView(); +} + void PropertyEditorView::nodeReparented(const ModelNode &node, const NodeAbstractProperty & /*newPropertyParent*/, const NodeAbstractProperty & /*oldPropertyParent*/, diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.h b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.h index b3158962ec..b6788d2257 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.h +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.h @@ -80,6 +80,7 @@ public: void instancePropertyChanged(const QList > &propertyList) override; void rootNodeTypeChanged(const QString &type, int majorVersion, int minorVersion) override; + void nodeTypeChanged(const ModelNode& node, const TypeName &type, int majorVersion, int minorVersion) override; void nodeReparented(const ModelNode &node, const NodeAbstractProperty &newPropertyParent, diff --git a/src/plugins/qmldesigner/designercore/include/abstractview.h b/src/plugins/qmldesigner/designercore/include/abstractview.h index 0b4e9563d5..4dd000e5fe 100644 --- a/src/plugins/qmldesigner/designercore/include/abstractview.h +++ b/src/plugins/qmldesigner/designercore/include/abstractview.h @@ -194,6 +194,7 @@ public: virtual void bindingPropertiesChanged(const QList& propertyList, PropertyChangeFlags propertyChange); virtual void signalHandlerPropertiesChanged(const QVector& propertyList, PropertyChangeFlags propertyChange); virtual void rootNodeTypeChanged(const QString &type, int majorVersion, int minorVersion); + virtual void nodeTypeChanged(const ModelNode& node, const TypeName &type, int majorVersion, int minorVersion); virtual void instancePropertyChanged(const QList > &propertyList); virtual void instanceErrorChanged(const QVector &errorNodeList); diff --git a/src/plugins/qmldesigner/designercore/include/modelnode.h b/src/plugins/qmldesigner/designercore/include/modelnode.h index 3b8b1fcf68..8611f15b30 100644 --- a/src/plugins/qmldesigner/designercore/include/modelnode.h +++ b/src/plugins/qmldesigner/designercore/include/modelnode.h @@ -102,6 +102,7 @@ public: NodeAbstractProperty parentProperty() const; void setParentProperty(NodeAbstractProperty parent); + void changeType(const TypeName &typeName, int majorVersion, int minorVersion); void setParentProperty(const ModelNode &newParentNode, const PropertyName &propertyName); bool hasParentProperty() const; diff --git a/src/plugins/qmldesigner/designercore/include/nodeinstanceview.h b/src/plugins/qmldesigner/designercore/include/nodeinstanceview.h index 7181df2845..49af489ff1 100644 --- a/src/plugins/qmldesigner/designercore/include/nodeinstanceview.h +++ b/src/plugins/qmldesigner/designercore/include/nodeinstanceview.h @@ -91,6 +91,7 @@ public: const NodeAbstractProperty &oldPropertyParent, AbstractView::PropertyChangeFlags propertyChange) override; void rootNodeTypeChanged(const QString &type, int majorVersion, int minorVersion) override; + void nodeTypeChanged(const ModelNode& node, const TypeName &type, int majorVersion, int minorVersion) override; void fileUrlChanged(const QUrl &oldUrl, const QUrl &newUrl) override; void nodeIdChanged(const ModelNode& node, const QString& newId, const QString& oldId) override; void nodeOrderChanged(const NodeListProperty &listProperty, const ModelNode &movedNode, int oldIndex) override; diff --git a/src/plugins/qmldesigner/designercore/include/rewriterview.h b/src/plugins/qmldesigner/designercore/include/rewriterview.h index a5f1866dda..fe7356366c 100644 --- a/src/plugins/qmldesigner/designercore/include/rewriterview.h +++ b/src/plugins/qmldesigner/designercore/include/rewriterview.h @@ -89,6 +89,7 @@ public: void nodeIdChanged(const ModelNode& node, const QString& newId, const QString& oldId) override; void nodeOrderChanged(const NodeListProperty &listProperty, const ModelNode &movedNode, int oldIndex) override; void rootNodeTypeChanged(const QString &type, int majorVersion, int minorVersion) override; + void nodeTypeChanged(const ModelNode& node, const TypeName &type, int majorVersion, int minorVersion) override; void customNotification(const AbstractView *view, const QString &identifier, const QList &nodeList, const QList &data) override; diff --git a/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp b/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp index 76408f59e3..0134e0e7b8 100644 --- a/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp +++ b/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp @@ -365,6 +365,11 @@ void NodeInstanceView::rootNodeTypeChanged(const QString &/*type*/, int /*majorV restartProcess(); } +void NodeInstanceView::nodeTypeChanged(const ModelNode &, const TypeName &, int, int) +{ + restartProcess(); +} + void NodeInstanceView::bindingPropertiesChanged(const QList& propertyList, PropertyChangeFlags /*propertyChange*/) { nodeInstanceServer()->changePropertyBindings(createChangeBindingCommand(propertyList)); diff --git a/src/plugins/qmldesigner/designercore/model/abstractview.cpp b/src/plugins/qmldesigner/designercore/model/abstractview.cpp index b7140f46e8..0d8c1ba789 100644 --- a/src/plugins/qmldesigner/designercore/model/abstractview.cpp +++ b/src/plugins/qmldesigner/designercore/model/abstractview.cpp @@ -325,6 +325,11 @@ void AbstractView::rootNodeTypeChanged(const QString &/*type*/, int /*majorVersi { } +void AbstractView::nodeTypeChanged(const ModelNode & /*node*/, const TypeName & /*type*/, int /*majorVersion*/, int /*minorVersion*/) +{ + +} + void AbstractView::importsChanged(const QList &/*addedImports*/, const QList &/*removedImports*/) { } diff --git a/src/plugins/qmldesigner/designercore/model/model.cpp b/src/plugins/qmldesigner/designercore/model/model.cpp index 48343b045a..9ca36c0f9a 100644 --- a/src/plugins/qmldesigner/designercore/model/model.cpp +++ b/src/plugins/qmldesigner/designercore/model/model.cpp @@ -186,6 +186,22 @@ void ModelPrivate::setFileUrl(const QUrl &fileUrl) } } +void ModelPrivate::changeNodeType(const InternalNodePointer &internalNodePointer, const TypeName &typeName, int majorVersion, int minorVersion) +{ + internalNodePointer->setType(typeName); + internalNodePointer->setMajorVersion(majorVersion); + internalNodePointer->setMinorVersion(minorVersion); + + try { + notifyNodeTypeChanged(internalNodePointer, typeName, majorVersion, minorVersion); + + } catch (const RewritingException &e) { + throw InvalidArgumentException(__LINE__, __FUNCTION__, __FILE__, e.description().toUtf8()); + + } + +} + InternalNode::Pointer ModelPrivate::createNode(const TypeName &typeName, int majorVersion, int minorVersion, @@ -953,6 +969,37 @@ void ModelPrivate::notifyNodeRemoved(const InternalNodePointer &internalNodePoin resetModelByRewriter(description); } +void ModelPrivate::notifyNodeTypeChanged(const InternalNodePointer &internalNodePointer, const TypeName &type, int majorVersion, int minorVersion) +{ + bool resetModel = false; + QString description; + + try { + if (rewriterView()) { + ModelNode modelNode(internalNodePointer, model(), rewriterView()); + rewriterView()->nodeTypeChanged(modelNode, type, majorVersion, minorVersion); + } + } catch (const RewritingException &e) { + description = e.description(); + resetModel = true; + } + + foreach (const QPointer &view, m_viewList) { + Q_ASSERT(view != 0); + ModelNode modelNode(internalNodePointer, model(), view.data()); + view->nodeTypeChanged(modelNode, type, majorVersion, minorVersion); + } + + if (nodeInstanceView()) { + ModelNode modelNode(internalNodePointer, model(), nodeInstanceView()); + nodeInstanceView()->nodeTypeChanged(modelNode, type, majorVersion, minorVersion); + } + + if (resetModel) + resetModelByRewriter(description); + +} + void ModelPrivate::notifyNodeIdChanged(const InternalNode::Pointer& internalNodePointer, const QString& newId, const QString& oldId) { bool resetModel = false; diff --git a/src/plugins/qmldesigner/designercore/model/model_p.h b/src/plugins/qmldesigner/designercore/model/model_p.h index 23b8c637d3..b5a6e97474 100644 --- a/src/plugins/qmldesigner/designercore/model/model_p.h +++ b/src/plugins/qmldesigner/designercore/model/model_p.h @@ -109,6 +109,7 @@ public: void removeNode(const InternalNodePointer &node); void changeNodeId(const InternalNodePointer& internalNodePointer, const QString& id); + void changeNodeType(const InternalNodePointer& internalNodePointer, const TypeName &typeName, int majorVersion, int minorVersion); InternalNodePointer rootNode() const; InternalNodePointer findNode(const QString &id) const; @@ -130,6 +131,7 @@ public: void notifyNodeAboutToBeRemoved(const InternalNodePointer &internalNodePointer); void notifyNodeRemoved(const InternalNodePointer &internalNodePointer, const InternalNodePointer &parentNodePointer, const PropertyName &parentPropertyName, AbstractView::PropertyChangeFlags propertyChange); void notifyNodeIdChanged(const InternalNodePointer& internalNodePointer, const QString& newId, const QString& oldId); + void notifyNodeTypeChanged(const InternalNodePointer& internalNodePointer, const TypeName &type, int majorVersion, int minorVersion); void notifyPropertiesRemoved(const QList &propertyList); void notifyPropertiesAboutToBeRemoved(const QList &internalPropertyList); diff --git a/src/plugins/qmldesigner/designercore/model/modelnode.cpp b/src/plugins/qmldesigner/designercore/model/modelnode.cpp index 5b4da5cbd8..66c69f8c88 100644 --- a/src/plugins/qmldesigner/designercore/model/modelnode.cpp +++ b/src/plugins/qmldesigner/designercore/model/modelnode.cpp @@ -408,6 +408,17 @@ void ModelNode::setParentProperty(NodeAbstractProperty parent) parent.reparentHere(*this); } +void ModelNode::changeType(const TypeName &typeName, int majorVersion, int minorVersion) +{ + if (!isValid()) { + Q_ASSERT_X(isValid(), Q_FUNC_INFO, "model node is invalid"); + throw InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__); + } + + model()->d->changeNodeType(internalNode(), typeName, majorVersion, minorVersion); + +} + void ModelNode::setParentProperty(const ModelNode &newParentNode, const PropertyName &propertyName) { setParentProperty(newParentNode.nodeAbstractProperty(propertyName)); diff --git a/src/plugins/qmldesigner/designercore/model/rewriterview.cpp b/src/plugins/qmldesigner/designercore/model/rewriterview.cpp index 208a16ac6e..40be35f9b1 100644 --- a/src/plugins/qmldesigner/designercore/model/rewriterview.cpp +++ b/src/plugins/qmldesigner/designercore/model/rewriterview.cpp @@ -285,6 +285,18 @@ void RewriterView::rootNodeTypeChanged(const QString &type, int majorVersion, in applyChanges(); } +void RewriterView::nodeTypeChanged(const ModelNode &node, const TypeName &type, int majorVersion, int minorVersion) +{ + Q_ASSERT(textModifier()); + if (textToModelMerger()->isActive()) + return; + + modelToTextMerger()->nodeTypeChanged(node, QString::fromLatin1(type), majorVersion, minorVersion); + + if (!isModificationGroupActive()) + applyChanges(); +} + void RewriterView::customNotification(const AbstractView * /*view*/, const QString &identifier, const QList & /* nodeList */, const QList & /*data */) { if (identifier == StartRewriterAmend || identifier == EndRewriterAmend) diff --git a/tests/auto/qml/qmldesigner/coretests/tst_testcore.cpp b/tests/auto/qml/qmldesigner/coretests/tst_testcore.cpp index 824adbc71d..b14912b4d6 100644 --- a/tests/auto/qml/qmldesigner/coretests/tst_testcore.cpp +++ b/tests/auto/qml/qmldesigner/coretests/tst_testcore.cpp @@ -6149,6 +6149,95 @@ void tst_TestCore::testModelNodeIsAncestorOf() QVERIFY(item3.isAncestorOf(item4)); } +void tst_TestCore::testModelChangeType() +{ + const QLatin1String qmlString("\n" + "import QtQuick 2.1\n" + "\n" + "Rectangle {\n" + " id: rootItem\n" + " Item {\n" + " id: firstItem\n" + " x: 10\n" + " }\n" + " Item {\n" + " id: secondItem\n" + " x: 20\n" + " }\n" + "}"); + QPlainTextEdit textEdit; + textEdit.setPlainText(qmlString); + NotIndentingTextEditModifier textModifier(&textEdit); + + QScopedPointer model(Model::create("QtQuick.Item", 2, 1)); + QVERIFY(model.data()); + + QScopedPointer view(new TestView(model.data())); + model->attachView(view.data()); + + // read in + QScopedPointer testRewriterView(new TestRewriterView()); + testRewriterView->setTextModifier(&textModifier); + model->attachView(testRewriterView.data()); + + ModelNode rootNode = view->rootModelNode(); + QVERIFY(rootNode.isValid()); + QCOMPARE(rootNode.type(), QmlDesigner::TypeName("QtQuick.Rectangle")); + QCOMPARE(rootNode.id(), QLatin1String("rootItem")); + + ModelNode childNode = rootNode.nodeListProperty(("data")).toModelNodeList().at(0); + QVERIFY(childNode.isValid()); + QCOMPARE(childNode.type(), QmlDesigner::TypeName("QtQuick.Item")); + QCOMPARE(childNode.id(), QLatin1String("firstItem")); + + childNode.changeType("QtQuick.Rectangle", 2, 0); + + QCOMPARE(childNode.type(), QmlDesigner::TypeName("QtQuick.Rectangle")); + + const QLatin1String expectedQmlCode1("\n" + "import QtQuick 2.1\n" + "\n" + "Rectangle {\n" + " id: rootItem\n" + " Rectangle {\n" + " id: firstItem\n" + " x: 10\n" + " }\n" + " Item {\n" + " id: secondItem\n" + " x: 20\n" + " }\n" + "}"); + + QCOMPARE(textEdit.toPlainText(), expectedQmlCode1); + + childNode = rootNode.nodeListProperty(("data")).toModelNodeList().at(1); + QVERIFY(childNode.isValid()); + QCOMPARE(childNode.type(), QmlDesigner::TypeName("QtQuick.Item")); + QCOMPARE(childNode.id(), QLatin1String("secondItem")); + + childNode.changeType("QtQuick.Rectangle", 2, 0); + + QCOMPARE(childNode.type(), QmlDesigner::TypeName("QtQuick.Rectangle")); + + const QLatin1String expectedQmlCode2("\n" + "import QtQuick 2.1\n" + "\n" + "Rectangle {\n" + " id: rootItem\n" + " Rectangle {\n" + " id: firstItem\n" + " x: 10\n" + " }\n" + " Rectangle {\n" + " id: secondItem\n" + " x: 20\n" + " }\n" + "}"); + + QCOMPARE(textEdit.toPlainText(), expectedQmlCode2); +} + void tst_TestCore::testModelDefaultProperties() { QScopedPointer model(createModel("QtQuick.Rectangle", 2, 0)); diff --git a/tests/auto/qml/qmldesigner/coretests/tst_testcore.h b/tests/auto/qml/qmldesigner/coretests/tst_testcore.h index f846c1963e..7693b58830 100644 --- a/tests/auto/qml/qmldesigner/coretests/tst_testcore.h +++ b/tests/auto/qml/qmldesigner/coretests/tst_testcore.h @@ -87,6 +87,7 @@ private slots: void testModelPropertyValueTypes(); void testModelNodeInHierarchy(); void testModelNodeIsAncestorOf(); + void testModelChangeType(); // // unit tests Rewriter -- GitLab