From 7c8847333b6e0dab509e1e06006d133ff773c4bf Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Tue, 1 Sep 2020 16:36:56 +0200 Subject: [PATCH] QmlDesigner: Go into components Task-nubmer: QDS-2696 Change-Id: I2f0ee71deea593da08fa4a754d783b53867473ed Reviewed-by: Michael Winkelmann Reviewed-by: Leena Miettinen Reviewed-by: Thomas Hartmann --- .../componentcore/designeractionmanager.cpp | 62 ++++++++++++++++--- .../componentcore/modelnodeoperations.cpp | 4 +- .../componentcore/modelnodeoperations.h | 2 +- .../listmodeleditor/listmodeleditormodel.cpp | 14 +++-- .../listmodeleditor/listmodeleditormodel.h | 5 +- src/plugins/qmldesigner/documentmanager.cpp | 7 ++- src/plugins/qmldesigner/documentmanager.h | 2 +- .../unit/unittest/google-using-declarations.h | 2 + tests/unit/unittest/listmodeleditor-test.cpp | 57 ++++++++++++++--- 9 files changed, 127 insertions(+), 28 deletions(-) diff --git a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp index d9cc1521816..5c961eb8aef 100644 --- a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp +++ b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp @@ -26,14 +26,15 @@ #include "designeractionmanager.h" #include "changestyleaction.h" -#include "modelnodecontextmenu_helper.h" -#include -#include -#include -#include -#include #include "designeractionmanagerview.h" +#include "modelnodecontextmenu_helper.h" #include "qmldesignerconstants.h" +#include "rewritingexception.h" +#include +#include +#include +#include +#include #include @@ -44,8 +45,6 @@ #include #include -#include -#include #include #include @@ -53,6 +52,12 @@ #include #include +#include +#include +#include + +#include + namespace QmlDesigner { static inline QString captionForModelNode(const ModelNode &modelNode) @@ -339,6 +344,12 @@ public: } }; +class DocumentError : public std::exception +{ +public: + const char *what() const override { return "Current document contains errors."; } +}; + class EditListModelAction final : public ModelNodeContextMenuAction { public: @@ -384,6 +395,24 @@ public: return view->createModelNode(elementMetaInfo.typeName(), elementMetaInfo.majorVersion(), elementMetaInfo.minorVersion()); + }, + [&](const ModelNode &node) { + bool isNowInComponent = ModelNodeOperations::goIntoComponent( + node); + + Model *currentModel = QmlDesignerPlugin::instance() + ->currentDesignDocument() + ->currentModel(); + + if (currentModel->rewriterView() + && currentModel->rewriterView()->inErrorState()) { + throw DocumentError{}; + } + + if (isNowInComponent) + return view->rootModelNode(); + + return node; }}; model.setListView(targetNode); @@ -391,7 +420,22 @@ public: ListModelEditorDialog dialog{Core::ICore::mainWindow()}; dialog.setModel(&model); - dialog.exec(); + try { + dialog.exec(); + } catch (const DocumentError &) { + QMessageBox::warning( + Core::ICore::mainWindow(), + QCoreApplication::translate("DesignerActionManager", "Document has errors"), + QCoreApplication::translate("DesignerActionManager", + "The document which contains the list model " + "contains errors. So we cannot edit it.")); + } catch (const RewritingException &) { + QMessageBox::warning( + Core::ICore::mainWindow(), + QCoreApplication::translate("DesignerActionManager", "Document cannot be written"), + QCoreApplication::translate("DesignerActionManager", + "An error occurred during a write attemp.")); + } } }; diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp index 7453807a835..0fbaca46795 100644 --- a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp +++ b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp @@ -127,9 +127,9 @@ static void setUpperLeftPostionToNode(const ModelNode &layoutNode, const QList &createModelCallback) + const std::function &createModelCallback, + const std::function &goIntoComponentCallback) { if (listViewNode.hasProperty("model")) { - if (listViewNode.hasBindingProperty("model")) - return listViewNode.bindingProperty("model").resolveToModelNode(); - else if (listViewNode.hasNodeProperty("model")) - return listViewNode.nodeProperty("model").modelNode(); + if (listViewNode.hasBindingProperty("model")) { + return goIntoComponentCallback(listViewNode.bindingProperty("model").resolveToModelNode()); + } else if (listViewNode.hasNodeProperty("model")) { + return goIntoComponentCallback(listViewNode.nodeProperty("model").modelNode()); + } } ModelNode newModel = createModelCallback(); @@ -251,7 +253,7 @@ void ListModelEditorModel::setListModel(ModelNode node) void ListModelEditorModel::setListView(ModelNode listView) { - setListModel(listModelNode(listView, m_createModelCallback)); + setListModel(listModelNode(listView, m_createModelCallback, m_goIntoComponentCallback)); } void ListModelEditorModel::addRow() diff --git a/src/plugins/qmldesigner/components/listmodeleditor/listmodeleditormodel.h b/src/plugins/qmldesigner/components/listmodeleditor/listmodeleditormodel.h index 97bd9c18d8b..5d3b87bbd71 100644 --- a/src/plugins/qmldesigner/components/listmodeleditor/listmodeleditormodel.h +++ b/src/plugins/qmldesigner/components/listmodeleditor/listmodeleditormodel.h @@ -39,9 +39,11 @@ class ListModelEditorModel : public QStandardItemModel public: ListModelEditorModel(std::function createModelCallback, - std::function createElementCallback) + std::function createElementCallback, + std::function goIntoComponentCallback) : m_createModelCallback(std::move(createModelCallback)) , m_createElementCallback(std::move(createElementCallback)) + , m_goIntoComponentCallback(std::move(goIntoComponentCallback)) {} void setListModel(ModelNode node); @@ -76,6 +78,7 @@ private: QList m_propertyNames; std::function m_createModelCallback; std::function m_createElementCallback; + std::function m_goIntoComponentCallback; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/documentmanager.cpp b/src/plugins/qmldesigner/documentmanager.cpp index 4550d8a4ea4..109b6577b4f 100644 --- a/src/plugins/qmldesigner/documentmanager.cpp +++ b/src/plugins/qmldesigner/documentmanager.cpp @@ -271,7 +271,7 @@ void DocumentManager::removeEditors(const QList &editors) delete m_designDocumentHash.take(editor).data(); } -void DocumentManager::goIntoComponent(const ModelNode &modelNode) +bool DocumentManager::goIntoComponent(const ModelNode &modelNode) { if (modelNode.isValid() && modelNode.isComponent() && designDocument()) { QmlDesignerPlugin::instance()->viewManager().setComponentNode(modelNode); @@ -286,9 +286,14 @@ void DocumentManager::goIntoComponent(const ModelNode &modelNode) openComponentSourcePropertyOfLoader(modelNode); else openInlineComponent(modelNode); + ModelNode rootModelNode = designDocument()->rewriterView()->rootModelNode(); applyProperties(rootModelNode, oldProperties); + + return true; } + + return false; } bool DocumentManager::createFile(const QString &filePath, const QString &contents) diff --git a/src/plugins/qmldesigner/documentmanager.h b/src/plugins/qmldesigner/documentmanager.h index bb3b13c9acc..8cd67eca9f6 100644 --- a/src/plugins/qmldesigner/documentmanager.h +++ b/src/plugins/qmldesigner/documentmanager.h @@ -51,7 +51,7 @@ public: void removeEditors(const QList &editors); - static void goIntoComponent(const ModelNode &modelNode); + static bool goIntoComponent(const ModelNode &modelNode); static bool createFile(const QString &filePath, const QString &contents); static void addFileToVersionControl(const QString &directoryPath, const QString &newFilePath); diff --git a/tests/unit/unittest/google-using-declarations.h b/tests/unit/unittest/google-using-declarations.h index de524834039..99514fe9b4a 100644 --- a/tests/unit/unittest/google-using-declarations.h +++ b/tests/unit/unittest/google-using-declarations.h @@ -63,6 +63,7 @@ using testing::Pair; using testing::PrintToString; using testing::Property; using testing::Return; +using testing::ReturnArg; using testing::ReturnRef; using testing::SafeMatcherCast; using testing::SaveArg; @@ -74,3 +75,4 @@ using testing::Throw; using testing::TypedEq; using testing::UnorderedElementsAre; using testing::VariantWith; +using testing::WithArg; diff --git a/tests/unit/unittest/listmodeleditor-test.cpp b/tests/unit/unittest/listmodeleditor-test.cpp index bdd5e9c0700..1bfa00646a1 100644 --- a/tests/unit/unittest/listmodeleditor-test.cpp +++ b/tests/unit/unittest/listmodeleditor-test.cpp @@ -99,17 +99,31 @@ public: listViewNode = mockView.createModelNode("QtQuick.ListView", 2, 15); listModelNode = mockView.createModelNode("QtQml.Models.ListModel", 2, 15); mockView.rootModelNode().defaultNodeListProperty().reparentHere(listModelNode); - element1 = createElement({{"name", "foo"}, {"value", 1}, {"value2", 42}}); - element2 = createElement({{"value", 4}, {"name", "bar"}, {"image", "pic.png"}}); - element3 = createElement({{"image", "pic.png"}, {"name", "poo"}, {"value", 111}}); + element1 = createElement({{"name", "foo"}, {"value", 1}, {"value2", 42}}, + mockView, + listModelNode); + element2 = createElement({{"value", 4}, {"name", "bar"}, {"image", "pic.png"}}, + mockView, + listModelNode); + element3 = createElement({{"image", "pic.png"}, {"name", "poo"}, {"value", 111}}, + mockView, + listModelNode); + + componentModel->attachView(&mockComponentView); + + componentElement = createElement({{"name", "com"}, {"value", 11}, {"value2", 55}}, + mockComponentView, + mockComponentView.rootModelNode()); + + ON_CALL(mockGoIntoComponent, Call(_)).WillByDefault([](ModelNode node) { return node; }); } using Entry = std::pair; - ModelNode createElement(std::initializer_list entries) + ModelNode createElement(std::initializer_list entries, AbstractView &view, ModelNode listModel) { - auto element = mockView.createModelNode("QtQml.Models/ListElement", 2, 15); - listModelNode.defaultNodeListProperty().reparentHere(element); + auto element = view.createModelNode("QtQml.Models/ListElement", 2, 15); + listModel.defaultNodeListProperty().reparentHere(element); for (const auto &entry : entries) { element.variantProperty(entry.first).setValue(entry.second); @@ -184,17 +198,23 @@ public: } protected: + MockFunction mockGoIntoComponent; std::unique_ptr designerModel{QmlDesigner::Model::create("QtQuick.Item", 1, 1)}; NiceMock mockView; QmlDesigner::ListModelEditorModel model{ [&] { return mockView.createModelNode("QtQml.Models.ListModel", 2, 15); }, - [&] { return mockView.createModelNode("QtQml.Models.ListElement", 2, 15); }}; + [&] { return mockView.createModelNode("QtQml.Models.ListElement", 2, 15); }, + mockGoIntoComponent.AsStdFunction()}; ModelNode listViewNode; ModelNode listModelNode; ModelNode emptyListModelNode; ModelNode element1; ModelNode element2; ModelNode element3; + std::unique_ptr componentModel{ + QmlDesigner::Model::create("QtQml.Models.ListModel", 1, 1)}; + NiceMock mockComponentView; + ModelNode componentElement; }; TEST_F(ListModelEditor, CreatePropertyNameSet) @@ -1376,4 +1396,27 @@ TEST_F(ListModelEditor, AddFalseAsStringProperties) IsVariantProperty("value", 111)))); } +TEST_F(ListModelEditor, GoIntoComponentForBinding) +{ + EXPECT_CALL(mockGoIntoComponent, Call(Eq(listModelNode))) + .WillRepeatedly(Return(mockComponentView.rootModelNode())); + listModelNode.setIdWithoutRefactoring("listModel"); + listViewNode.bindingProperty("model").setExpression("listModel"); + + model.setListView(listViewNode); + + ASSERT_THAT(displayValues(), ElementsAre(ElementsAre("com", 11, 55))); +} + +TEST_F(ListModelEditor, GoIntoComponentForModelNode) +{ + EXPECT_CALL(mockGoIntoComponent, Call(Eq(listModelNode))) + .WillRepeatedly(Return(mockComponentView.rootModelNode())); + listViewNode.nodeProperty("model").reparentHere(listModelNode); + + model.setListView(listViewNode); + + ASSERT_THAT(displayValues(), ElementsAre(ElementsAre("com", 11, 55))); +} + } // namespace