forked from qt-creator/qt-creator
QmlDesigner: Fix crash for list view model
There was a type, so it crashed. The code is now under tests so we cannot break it anymore. Task-number: QDS-2563 Change-Id: I81426a9f8a568b217b7bf9c8c261b24be14ff61a Reviewed-by: Thomas Hartmann <thomas.hartmann@qt.io>
This commit is contained in:
@@ -363,32 +363,30 @@ public:
|
|||||||
|
|
||||||
bool isEnabled(const SelectionContext &) const override { return true; }
|
bool isEnabled(const SelectionContext &) const override { return true; }
|
||||||
|
|
||||||
static ModelNode listModelNode(const ModelNode &listViewNode)
|
|
||||||
{
|
|
||||||
if (listViewNode.hasProperty("model")) {
|
|
||||||
if (listViewNode.hasBindingProperty("model"))
|
|
||||||
return listViewNode.bindingProperty("model").resolveToModelNode();
|
|
||||||
else if (listViewNode.hasNodeProperty("model"))
|
|
||||||
return listViewNode.nodeProperty("model").modelNode();
|
|
||||||
}
|
|
||||||
|
|
||||||
ModelNode newModel = listViewNode.view()->createModelNode("QtQml.Models.ListModel", 2, 15);
|
|
||||||
listViewNode.nodeProperty("mode").reparentHere(newModel);
|
|
||||||
|
|
||||||
return newModel;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void openDialog(const SelectionContext &selectionState)
|
static void openDialog(const SelectionContext &selectionState)
|
||||||
{
|
{
|
||||||
ListModelEditorModel model;
|
|
||||||
|
|
||||||
ModelNode targetNode = selectionState.targetNode();
|
ModelNode targetNode = selectionState.targetNode();
|
||||||
if (!targetNode.isValid())
|
if (!targetNode.isValid())
|
||||||
targetNode = selectionState.currentSingleSelectedNode();
|
targetNode = selectionState.currentSingleSelectedNode();
|
||||||
if (!targetNode.isValid())
|
if (!targetNode.isValid())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
model.setListModel(listModelNode(targetNode));
|
AbstractView *view = targetNode.view();
|
||||||
|
NodeMetaInfo modelMetaInfo = view->model()->metaInfo("ListModel");
|
||||||
|
NodeMetaInfo elementMetaInfo = view->model()->metaInfo("ListElement");
|
||||||
|
|
||||||
|
ListModelEditorModel model{[&] {
|
||||||
|
return view->createModelNode(modelMetaInfo.typeName(),
|
||||||
|
modelMetaInfo.majorVersion(),
|
||||||
|
modelMetaInfo.minorVersion());
|
||||||
|
},
|
||||||
|
[&] {
|
||||||
|
return view->createModelNode(elementMetaInfo.typeName(),
|
||||||
|
elementMetaInfo.majorVersion(),
|
||||||
|
elementMetaInfo.minorVersion());
|
||||||
|
}};
|
||||||
|
|
||||||
|
model.setListView(targetNode);
|
||||||
|
|
||||||
ListModelEditorDialog dialog{Core::ICore::mainWindow()};
|
ListModelEditorDialog dialog{Core::ICore::mainWindow()};
|
||||||
dialog.setModel(&model);
|
dialog.setModel(&model);
|
||||||
|
@@ -26,7 +26,9 @@
|
|||||||
#include "listmodeleditormodel.h"
|
#include "listmodeleditormodel.h"
|
||||||
|
|
||||||
#include <abstractview.h>
|
#include <abstractview.h>
|
||||||
|
#include <bindingproperty.h>
|
||||||
#include <nodelistproperty.h>
|
#include <nodelistproperty.h>
|
||||||
|
#include <nodeproperty.h>
|
||||||
#include <variantproperty.h>
|
#include <variantproperty.h>
|
||||||
|
|
||||||
#include <QVariant>
|
#include <QVariant>
|
||||||
@@ -185,6 +187,22 @@ void renameProperties(const QStandardItemModel *model,
|
|||||||
static_cast<ListModelItem *>(model->item(rowIndex, columnIndex))->renameProperty(newPropertyName);
|
static_cast<ListModelItem *>(model->item(rowIndex, columnIndex))->renameProperty(newPropertyName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ModelNode listModelNode(const ModelNode &listViewNode,
|
||||||
|
const std::function<ModelNode()> &createModelCallback)
|
||||||
|
{
|
||||||
|
if (listViewNode.hasProperty("model")) {
|
||||||
|
if (listViewNode.hasBindingProperty("model"))
|
||||||
|
return listViewNode.bindingProperty("model").resolveToModelNode();
|
||||||
|
else if (listViewNode.hasNodeProperty("model"))
|
||||||
|
return listViewNode.nodeProperty("model").modelNode();
|
||||||
|
}
|
||||||
|
|
||||||
|
ModelNode newModel = createModelCallback();
|
||||||
|
listViewNode.nodeProperty("model").reparentHere(newModel);
|
||||||
|
|
||||||
|
return newModel;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
void ListModelEditorModel::populateModel()
|
void ListModelEditorModel::populateModel()
|
||||||
@@ -214,9 +232,20 @@ void ListModelEditorModel::appendItems(const ModelNode &listElementNode)
|
|||||||
appendRow(row);
|
appendRow(row);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ListModelEditorModel::setListModel(ModelNode node)
|
||||||
|
{
|
||||||
|
m_listModelNode = node;
|
||||||
|
populateModel();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ListModelEditorModel::setListView(ModelNode listView)
|
||||||
|
{
|
||||||
|
setListModel(listModelNode(listView, m_createModelCallback));
|
||||||
|
}
|
||||||
|
|
||||||
void ListModelEditorModel::addRow()
|
void ListModelEditorModel::addRow()
|
||||||
{
|
{
|
||||||
auto newElement = m_listModelNode.view()->createModelNode("QtQml.Models.ListElement", 2, 15);
|
auto newElement = m_createElementCallback();
|
||||||
m_listModelNode.defaultNodeListProperty().reparentHere(newElement);
|
m_listModelNode.defaultNodeListProperty().reparentHere(newElement);
|
||||||
|
|
||||||
appendItems(newElement);
|
appendItems(newElement);
|
||||||
|
@@ -38,11 +38,15 @@ class ListModelEditorModel : public QStandardItemModel
|
|||||||
using QStandardItemModel::removeRows;
|
using QStandardItemModel::removeRows;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void setListModel(ModelNode node)
|
ListModelEditorModel(std::function<ModelNode()> createModelCallback,
|
||||||
{
|
std::function<ModelNode()> createElementCallback)
|
||||||
m_listModelNode = node;
|
: m_createModelCallback(std::move(createModelCallback))
|
||||||
populateModel();
|
, m_createElementCallback(std::move(createElementCallback))
|
||||||
}
|
{}
|
||||||
|
|
||||||
|
void setListModel(ModelNode node);
|
||||||
|
|
||||||
|
void setListView(ModelNode listView);
|
||||||
|
|
||||||
void addRow();
|
void addRow();
|
||||||
void addColumn(const QString &columnName);
|
void addColumn(const QString &columnName);
|
||||||
@@ -70,6 +74,8 @@ private:
|
|||||||
private:
|
private:
|
||||||
ModelNode m_listModelNode;
|
ModelNode m_listModelNode;
|
||||||
QList<QmlDesigner::PropertyName> m_propertyNames;
|
QList<QmlDesigner::PropertyName> m_propertyNames;
|
||||||
|
std::function<ModelNode()> m_createModelCallback;
|
||||||
|
std::function<ModelNode()> m_createElementCallback;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace QmlDesigner
|
} // namespace QmlDesigner
|
||||||
|
@@ -31,8 +31,10 @@
|
|||||||
|
|
||||||
#include <qmldesigner/components/listmodeleditor/listmodeleditormodel.h>
|
#include <qmldesigner/components/listmodeleditor/listmodeleditormodel.h>
|
||||||
#include <qmldesigner/designercore/include/abstractview.h>
|
#include <qmldesigner/designercore/include/abstractview.h>
|
||||||
|
#include <qmldesigner/designercore/include/bindingproperty.h>
|
||||||
#include <qmldesigner/designercore/include/model.h>
|
#include <qmldesigner/designercore/include/model.h>
|
||||||
#include <qmldesigner/designercore/include/nodelistproperty.h>
|
#include <qmldesigner/designercore/include/nodelistproperty.h>
|
||||||
|
#include <qmldesigner/designercore/include/nodeproperty.h>
|
||||||
#include <qmldesigner/designercore/include/variantproperty.h>
|
#include <qmldesigner/designercore/include/variantproperty.h>
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
@@ -94,6 +96,7 @@ public:
|
|||||||
|
|
||||||
emptyListModelNode = mockView.createModelNode("QtQml.Models.ListModel", 2, 15);
|
emptyListModelNode = mockView.createModelNode("QtQml.Models.ListModel", 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}});
|
||||||
@@ -183,7 +186,10 @@ public:
|
|||||||
protected:
|
protected:
|
||||||
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.ListElement", 2, 15); }};
|
||||||
|
ModelNode listViewNode;
|
||||||
ModelNode listModelNode;
|
ModelNode listModelNode;
|
||||||
ModelNode emptyListModelNode;
|
ModelNode emptyListModelNode;
|
||||||
ModelNode element1;
|
ModelNode element1;
|
||||||
@@ -1272,4 +1278,36 @@ TEST_F(ListModelEditor, SelectionAfterMoveRowsUp)
|
|||||||
index(2, 3)));
|
index(2, 3)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(ListModelEditor, ListViewHasNoModel)
|
||||||
|
{
|
||||||
|
model.setListView(listViewNode);
|
||||||
|
|
||||||
|
ASSERT_THAT(listViewNode.nodeProperty("model").modelNode().type(), Eq("QtQml.Models.ListModel"));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ListModelEditor, ListViewHasModelInside)
|
||||||
|
{
|
||||||
|
listViewNode.nodeProperty("model").reparentHere(listModelNode);
|
||||||
|
|
||||||
|
model.setListView(listViewNode);
|
||||||
|
|
||||||
|
ASSERT_THAT(displayValues(),
|
||||||
|
ElementsAre(ElementsAre(IsInvalid(), "foo", 1, 42),
|
||||||
|
ElementsAre("pic.png", "bar", 4, IsInvalid()),
|
||||||
|
ElementsAre("pic.png", "poo", 111, IsInvalid())));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ListModelEditor, ListViewHasModelBinding)
|
||||||
|
{
|
||||||
|
listModelNode.setIdWithoutRefactoring("listModel");
|
||||||
|
listViewNode.bindingProperty("model").setExpression("listModel");
|
||||||
|
|
||||||
|
model.setListView(listViewNode);
|
||||||
|
|
||||||
|
ASSERT_THAT(displayValues(),
|
||||||
|
ElementsAre(ElementsAre(IsInvalid(), "foo", 1, 42),
|
||||||
|
ElementsAre("pic.png", "bar", 4, IsInvalid()),
|
||||||
|
ElementsAre("pic.png", "poo", 111, IsInvalid())));
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
Reference in New Issue
Block a user