QmlDesigner: Go into components

Task-nubmer: QDS-2696
Change-Id: I2f0ee71deea593da08fa4a754d783b53867473ed
Reviewed-by: Michael Winkelmann <michael.winkelmann@qt.io>
Reviewed-by: Leena Miettinen <riitta-leena.miettinen@qt.io>
Reviewed-by: Thomas Hartmann <thomas.hartmann@qt.io>
This commit is contained in:
Marco Bubke
2020-09-01 16:36:56 +02:00
parent af88afe943
commit 7c8847333b
9 changed files with 127 additions and 28 deletions

View File

@@ -26,14 +26,15 @@
#include "designeractionmanager.h"
#include "changestyleaction.h"
#include "modelnodecontextmenu_helper.h"
#include <bindingproperty.h>
#include <nodeproperty.h>
#include <nodelistproperty.h>
#include <nodehints.h>
#include <nodemetainfo.h>
#include "designeractionmanagerview.h"
#include "modelnodecontextmenu_helper.h"
#include "qmldesignerconstants.h"
#include "rewritingexception.h"
#include <bindingproperty.h>
#include <nodehints.h>
#include <nodelistproperty.h>
#include <nodemetainfo.h>
#include <nodeproperty.h>
#include <formeditortoolbutton.h>
@@ -44,8 +45,6 @@
#include <listmodeleditor/listmodeleditordialog.h>
#include <listmodeleditor/listmodeleditormodel.h>
#include <QHBoxLayout>
#include <QGraphicsLinearLayout>
#include <coreplugin/actionmanager/actionmanager.h>
#include <coreplugin/icore.h>
@@ -53,6 +52,12 @@
#include <utils/qtcassert.h>
#include <utils/utilsicons.h>
#include <QGraphicsLinearLayout>
#include <QHBoxLayout>
#include <QMessageBox>
#include <exception>
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);
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."));
}
}
};

View File

@@ -127,9 +127,9 @@ static void setUpperLeftPostionToNode(const ModelNode &layoutNode, const QList<M
namespace ModelNodeOperations {
void goIntoComponent(const ModelNode &modelNode)
bool goIntoComponent(const ModelNode &modelNode)
{
DocumentManager::goIntoComponent(modelNode);
return DocumentManager::goIntoComponent(modelNode);
}
void select(const SelectionContext &selectionState)

View File

@@ -30,7 +30,7 @@
namespace QmlDesigner {
namespace ModelNodeOperations {
void goIntoComponent(const ModelNode &modelNode);
bool goIntoComponent(const ModelNode &modelNode);
void select(const SelectionContext &selectionState);
void deSelect(const SelectionContext &selectionState);

View File

@@ -199,13 +199,15 @@ void renameProperties(const QStandardItemModel *model,
}
ModelNode listModelNode(const ModelNode &listViewNode,
const std::function<ModelNode()> &createModelCallback)
const std::function<ModelNode()> &createModelCallback,
const std::function<ModelNode(const ModelNode &)> &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()

View File

@@ -39,9 +39,11 @@ class ListModelEditorModel : public QStandardItemModel
public:
ListModelEditorModel(std::function<ModelNode()> createModelCallback,
std::function<ModelNode()> createElementCallback)
std::function<ModelNode()> createElementCallback,
std::function<ModelNode(const ModelNode &)> 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<QmlDesigner::PropertyName> m_propertyNames;
std::function<ModelNode()> m_createModelCallback;
std::function<ModelNode()> m_createElementCallback;
std::function<ModelNode(const ModelNode &)> m_goIntoComponentCallback;
};
} // namespace QmlDesigner

View File

@@ -271,7 +271,7 @@ void DocumentManager::removeEditors(const QList<Core::IEditor *> &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)

View File

@@ -51,7 +51,7 @@ public:
void removeEditors(const QList<Core::IEditor *> &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);

View File

@@ -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;

View File

@@ -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<QmlDesigner::PropertyName, QVariant>;
ModelNode createElement(std::initializer_list<Entry> entries)
ModelNode createElement(std::initializer_list<Entry> 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<ModelNode(const ModelNode &)> mockGoIntoComponent;
std::unique_ptr<QmlDesigner::Model> designerModel{QmlDesigner::Model::create("QtQuick.Item", 1, 1)};
NiceMock<MockListModelEditorView> 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<QmlDesigner::Model> componentModel{
QmlDesigner::Model::create("QtQml.Models.ListModel", 1, 1)};
NiceMock<MockListModelEditorView> 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