forked from qt-creator/qt-creator
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:
@@ -26,14 +26,15 @@
|
|||||||
#include "designeractionmanager.h"
|
#include "designeractionmanager.h"
|
||||||
|
|
||||||
#include "changestyleaction.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 "designeractionmanagerview.h"
|
||||||
|
#include "modelnodecontextmenu_helper.h"
|
||||||
#include "qmldesignerconstants.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>
|
#include <formeditortoolbutton.h>
|
||||||
|
|
||||||
@@ -44,8 +45,6 @@
|
|||||||
#include <listmodeleditor/listmodeleditordialog.h>
|
#include <listmodeleditor/listmodeleditordialog.h>
|
||||||
#include <listmodeleditor/listmodeleditormodel.h>
|
#include <listmodeleditor/listmodeleditormodel.h>
|
||||||
|
|
||||||
#include <QHBoxLayout>
|
|
||||||
#include <QGraphicsLinearLayout>
|
|
||||||
|
|
||||||
#include <coreplugin/actionmanager/actionmanager.h>
|
#include <coreplugin/actionmanager/actionmanager.h>
|
||||||
#include <coreplugin/icore.h>
|
#include <coreplugin/icore.h>
|
||||||
@@ -53,6 +52,12 @@
|
|||||||
#include <utils/qtcassert.h>
|
#include <utils/qtcassert.h>
|
||||||
#include <utils/utilsicons.h>
|
#include <utils/utilsicons.h>
|
||||||
|
|
||||||
|
#include <QGraphicsLinearLayout>
|
||||||
|
#include <QHBoxLayout>
|
||||||
|
#include <QMessageBox>
|
||||||
|
|
||||||
|
#include <exception>
|
||||||
|
|
||||||
namespace QmlDesigner {
|
namespace QmlDesigner {
|
||||||
|
|
||||||
static inline QString captionForModelNode(const ModelNode &modelNode)
|
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
|
class EditListModelAction final : public ModelNodeContextMenuAction
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -384,6 +395,24 @@ public:
|
|||||||
return view->createModelNode(elementMetaInfo.typeName(),
|
return view->createModelNode(elementMetaInfo.typeName(),
|
||||||
elementMetaInfo.majorVersion(),
|
elementMetaInfo.majorVersion(),
|
||||||
elementMetaInfo.minorVersion());
|
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);
|
model.setListView(targetNode);
|
||||||
@@ -391,7 +420,22 @@ public:
|
|||||||
ListModelEditorDialog dialog{Core::ICore::mainWindow()};
|
ListModelEditorDialog dialog{Core::ICore::mainWindow()};
|
||||||
dialog.setModel(&model);
|
dialog.setModel(&model);
|
||||||
|
|
||||||
|
try {
|
||||||
dialog.exec();
|
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."));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -127,9 +127,9 @@ static void setUpperLeftPostionToNode(const ModelNode &layoutNode, const QList<M
|
|||||||
|
|
||||||
namespace ModelNodeOperations {
|
namespace ModelNodeOperations {
|
||||||
|
|
||||||
void goIntoComponent(const ModelNode &modelNode)
|
bool goIntoComponent(const ModelNode &modelNode)
|
||||||
{
|
{
|
||||||
DocumentManager::goIntoComponent(modelNode);
|
return DocumentManager::goIntoComponent(modelNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
void select(const SelectionContext &selectionState)
|
void select(const SelectionContext &selectionState)
|
||||||
|
@@ -30,7 +30,7 @@
|
|||||||
namespace QmlDesigner {
|
namespace QmlDesigner {
|
||||||
namespace ModelNodeOperations {
|
namespace ModelNodeOperations {
|
||||||
|
|
||||||
void goIntoComponent(const ModelNode &modelNode);
|
bool goIntoComponent(const ModelNode &modelNode);
|
||||||
|
|
||||||
void select(const SelectionContext &selectionState);
|
void select(const SelectionContext &selectionState);
|
||||||
void deSelect(const SelectionContext &selectionState);
|
void deSelect(const SelectionContext &selectionState);
|
||||||
|
@@ -199,13 +199,15 @@ void renameProperties(const QStandardItemModel *model,
|
|||||||
}
|
}
|
||||||
|
|
||||||
ModelNode listModelNode(const ModelNode &listViewNode,
|
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.hasProperty("model")) {
|
||||||
if (listViewNode.hasBindingProperty("model"))
|
if (listViewNode.hasBindingProperty("model")) {
|
||||||
return listViewNode.bindingProperty("model").resolveToModelNode();
|
return goIntoComponentCallback(listViewNode.bindingProperty("model").resolveToModelNode());
|
||||||
else if (listViewNode.hasNodeProperty("model"))
|
} else if (listViewNode.hasNodeProperty("model")) {
|
||||||
return listViewNode.nodeProperty("model").modelNode();
|
return goIntoComponentCallback(listViewNode.nodeProperty("model").modelNode());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ModelNode newModel = createModelCallback();
|
ModelNode newModel = createModelCallback();
|
||||||
@@ -251,7 +253,7 @@ void ListModelEditorModel::setListModel(ModelNode node)
|
|||||||
|
|
||||||
void ListModelEditorModel::setListView(ModelNode listView)
|
void ListModelEditorModel::setListView(ModelNode listView)
|
||||||
{
|
{
|
||||||
setListModel(listModelNode(listView, m_createModelCallback));
|
setListModel(listModelNode(listView, m_createModelCallback, m_goIntoComponentCallback));
|
||||||
}
|
}
|
||||||
|
|
||||||
void ListModelEditorModel::addRow()
|
void ListModelEditorModel::addRow()
|
||||||
|
@@ -39,9 +39,11 @@ class ListModelEditorModel : public QStandardItemModel
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
ListModelEditorModel(std::function<ModelNode()> createModelCallback,
|
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_createModelCallback(std::move(createModelCallback))
|
||||||
, m_createElementCallback(std::move(createElementCallback))
|
, m_createElementCallback(std::move(createElementCallback))
|
||||||
|
, m_goIntoComponentCallback(std::move(goIntoComponentCallback))
|
||||||
{}
|
{}
|
||||||
|
|
||||||
void setListModel(ModelNode node);
|
void setListModel(ModelNode node);
|
||||||
@@ -76,6 +78,7 @@ private:
|
|||||||
QList<QmlDesigner::PropertyName> m_propertyNames;
|
QList<QmlDesigner::PropertyName> m_propertyNames;
|
||||||
std::function<ModelNode()> m_createModelCallback;
|
std::function<ModelNode()> m_createModelCallback;
|
||||||
std::function<ModelNode()> m_createElementCallback;
|
std::function<ModelNode()> m_createElementCallback;
|
||||||
|
std::function<ModelNode(const ModelNode &)> m_goIntoComponentCallback;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace QmlDesigner
|
} // namespace QmlDesigner
|
||||||
|
@@ -271,7 +271,7 @@ void DocumentManager::removeEditors(const QList<Core::IEditor *> &editors)
|
|||||||
delete m_designDocumentHash.take(editor).data();
|
delete m_designDocumentHash.take(editor).data();
|
||||||
}
|
}
|
||||||
|
|
||||||
void DocumentManager::goIntoComponent(const ModelNode &modelNode)
|
bool DocumentManager::goIntoComponent(const ModelNode &modelNode)
|
||||||
{
|
{
|
||||||
if (modelNode.isValid() && modelNode.isComponent() && designDocument()) {
|
if (modelNode.isValid() && modelNode.isComponent() && designDocument()) {
|
||||||
QmlDesignerPlugin::instance()->viewManager().setComponentNode(modelNode);
|
QmlDesignerPlugin::instance()->viewManager().setComponentNode(modelNode);
|
||||||
@@ -286,9 +286,14 @@ void DocumentManager::goIntoComponent(const ModelNode &modelNode)
|
|||||||
openComponentSourcePropertyOfLoader(modelNode);
|
openComponentSourcePropertyOfLoader(modelNode);
|
||||||
else
|
else
|
||||||
openInlineComponent(modelNode);
|
openInlineComponent(modelNode);
|
||||||
|
|
||||||
ModelNode rootModelNode = designDocument()->rewriterView()->rootModelNode();
|
ModelNode rootModelNode = designDocument()->rewriterView()->rootModelNode();
|
||||||
applyProperties(rootModelNode, oldProperties);
|
applyProperties(rootModelNode, oldProperties);
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DocumentManager::createFile(const QString &filePath, const QString &contents)
|
bool DocumentManager::createFile(const QString &filePath, const QString &contents)
|
||||||
|
@@ -51,7 +51,7 @@ public:
|
|||||||
|
|
||||||
void removeEditors(const QList<Core::IEditor *> &editors);
|
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 bool createFile(const QString &filePath, const QString &contents);
|
||||||
static void addFileToVersionControl(const QString &directoryPath, const QString &newFilePath);
|
static void addFileToVersionControl(const QString &directoryPath, const QString &newFilePath);
|
||||||
|
@@ -63,6 +63,7 @@ using testing::Pair;
|
|||||||
using testing::PrintToString;
|
using testing::PrintToString;
|
||||||
using testing::Property;
|
using testing::Property;
|
||||||
using testing::Return;
|
using testing::Return;
|
||||||
|
using testing::ReturnArg;
|
||||||
using testing::ReturnRef;
|
using testing::ReturnRef;
|
||||||
using testing::SafeMatcherCast;
|
using testing::SafeMatcherCast;
|
||||||
using testing::SaveArg;
|
using testing::SaveArg;
|
||||||
@@ -74,3 +75,4 @@ using testing::Throw;
|
|||||||
using testing::TypedEq;
|
using testing::TypedEq;
|
||||||
using testing::UnorderedElementsAre;
|
using testing::UnorderedElementsAre;
|
||||||
using testing::VariantWith;
|
using testing::VariantWith;
|
||||||
|
using testing::WithArg;
|
||||||
|
@@ -99,17 +99,31 @@ public:
|
|||||||
listViewNode = mockView.createModelNode("QtQuick.ListView", 2, 15);
|
listViewNode = mockView.createModelNode("QtQuick.ListView", 2, 15);
|
||||||
listModelNode = mockView.createModelNode("QtQml.Models.ListModel", 2, 15);
|
listModelNode = mockView.createModelNode("QtQml.Models.ListModel", 2, 15);
|
||||||
mockView.rootModelNode().defaultNodeListProperty().reparentHere(listModelNode);
|
mockView.rootModelNode().defaultNodeListProperty().reparentHere(listModelNode);
|
||||||
element1 = createElement({{"name", "foo"}, {"value", 1}, {"value2", 42}});
|
element1 = createElement({{"name", "foo"}, {"value", 1}, {"value2", 42}},
|
||||||
element2 = createElement({{"value", 4}, {"name", "bar"}, {"image", "pic.png"}});
|
mockView,
|
||||||
element3 = createElement({{"image", "pic.png"}, {"name", "poo"}, {"value", 111}});
|
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>;
|
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);
|
auto element = view.createModelNode("QtQml.Models/ListElement", 2, 15);
|
||||||
listModelNode.defaultNodeListProperty().reparentHere(element);
|
listModel.defaultNodeListProperty().reparentHere(element);
|
||||||
|
|
||||||
for (const auto &entry : entries) {
|
for (const auto &entry : entries) {
|
||||||
element.variantProperty(entry.first).setValue(entry.second);
|
element.variantProperty(entry.first).setValue(entry.second);
|
||||||
@@ -184,17 +198,23 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
MockFunction<ModelNode(const ModelNode &)> mockGoIntoComponent;
|
||||||
std::unique_ptr<QmlDesigner::Model> designerModel{QmlDesigner::Model::create("QtQuick.Item", 1, 1)};
|
std::unique_ptr<QmlDesigner::Model> designerModel{QmlDesigner::Model::create("QtQuick.Item", 1, 1)};
|
||||||
NiceMock<MockListModelEditorView> mockView;
|
NiceMock<MockListModelEditorView> mockView;
|
||||||
QmlDesigner::ListModelEditorModel model{
|
QmlDesigner::ListModelEditorModel model{
|
||||||
[&] { return mockView.createModelNode("QtQml.Models.ListModel", 2, 15); },
|
[&] { 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 listViewNode;
|
||||||
ModelNode listModelNode;
|
ModelNode listModelNode;
|
||||||
ModelNode emptyListModelNode;
|
ModelNode emptyListModelNode;
|
||||||
ModelNode element1;
|
ModelNode element1;
|
||||||
ModelNode element2;
|
ModelNode element2;
|
||||||
ModelNode element3;
|
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)
|
TEST_F(ListModelEditor, CreatePropertyNameSet)
|
||||||
@@ -1376,4 +1396,27 @@ TEST_F(ListModelEditor, AddFalseAsStringProperties)
|
|||||||
IsVariantProperty("value", 111))));
|
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
|
} // namespace
|
||||||
|
Reference in New Issue
Block a user