QmlDesigner: Add a new collection to the collection editor

Task-number: QDS-11059
Change-Id: Iad622098ac7a95cbaf543d88f714e79cd5b3c153
Reviewed-by: Miikka Heikkinen <miikka.heikkinen@qt.io>
Reviewed-by: Qt CI Patch Build Bot <ci_patchbuild_bot@qt.io>
Reviewed-by: Mahmoud Badri <mahmoud.badri@qt.io>
This commit is contained in:
Ali Kianian
2023-10-27 15:21:50 +03:00
parent 30941d228e
commit afef8afc39
10 changed files with 475 additions and 36 deletions

View File

@@ -41,6 +41,7 @@ Item {
id: newCollection id: newCollection
backendValue: root.rootView backendValue: root.rootView
sourceModel: root.model
anchors.centerIn: parent anchors.centerIn: parent
} }

View File

@@ -3,23 +3,37 @@
import QtQuick import QtQuick
import QtQuick.Controls import QtQuick.Controls
import QtQuick.Layouts
import QtQuickDesignerTheme 1.0 import QtQuickDesignerTheme 1.0
import Qt.labs.platform as PlatformWidgets
import HelperWidgets 2.0 as HelperWidgets import HelperWidgets 2.0 as HelperWidgets
import StudioControls 1.0 as StudioControls import StudioControls 1.0 as StudioControls
import StudioTheme as StudioTheme import StudioTheme as StudioTheme
import CollectionEditor 1.0
StudioControls.Dialog { StudioControls.Dialog {
id: root id: root
enum SourceType { NewJson, NewCsv, ExistingCollection, NewCollectionToJson }
required property var backendValue
required property var sourceModel
readonly property alias collectionType: typeMode.collectionType
readonly property bool isValid: collectionName.isValid
&& jsonCollections.isValid
&& newCollectionPath.isValid
title: qsTr("Add a new Collection") title: qsTr("Add a new Collection")
anchors.centerIn: parent anchors.centerIn: parent
closePolicy: Popup.CloseOnEscape closePolicy: Popup.CloseOnEscape
modal: true modal: true
required property var backendValue
onOpened: { onOpened: {
collectionName.text = "Collection" collectionName.text = qsTr("Collection")
updateType()
updateJsonSourceIndex()
updateCollectionExists()
} }
onRejected: { onRejected: {
@@ -27,24 +41,197 @@ StudioControls.Dialog {
} }
onAccepted: { onAccepted: {
if (collectionName.text !== "") if (root.isValid) {
root.backendValue.addCollection(collectionName.text) root.backendValue.addCollection(collectionName.text,
root.collectionType,
newCollectionPath.text,
jsonCollections.currentValue)
}
} }
contentItem: Column { function updateType() {
spacing: 10 newCollectionPath.text = ""
Row { if (typeMode.currentValue === NewCollectionDialog.SourceType.NewJson) {
spacing: 10 newCollectionFileDialog.nameFilters = ["Json Files (*.json)"]
Text { newCollectionFileDialog.fileMode = PlatformWidgets.FileDialog.SaveFile
text: qsTr("Collection name: ") newCollectionPath.enabled = true
anchors.verticalCenter: parent.verticalCenter jsonCollections.enabled = false
typeMode.collectionType = "json"
} else if (typeMode.currentValue === NewCollectionDialog.SourceType.NewCsv) {
newCollectionFileDialog.nameFilters = ["Comma-Separated Values (*.csv)"]
newCollectionFileDialog.fileMode = PlatformWidgets.FileDialog.SaveFile
newCollectionPath.enabled = true
jsonCollections.enabled = false
typeMode.collectionType = "csv"
} else if (typeMode.currentValue === NewCollectionDialog.SourceType.ExistingCollection) {
newCollectionFileDialog.nameFilters = ["All Collection Files (*.json *.csv)",
"Json Files (*.json)",
"Comma-Separated Values (*.csv)"]
newCollectionFileDialog.fileMode = PlatformWidgets.FileDialog.OpenFile
newCollectionPath.enabled = true
jsonCollections.enabled = false
typeMode.collectionType = "existing"
} else if (typeMode.currentValue === NewCollectionDialog.SourceType.NewCollectionToJson) {
newCollectionFileDialog.nameFilters = [""]
newCollectionPath.enabled = false
jsonCollections.enabled = true
typeMode.collectionType = "json"
}
}
function updateJsonSourceIndex() {
if (!jsonCollections.enabled) {
jsonCollections.currentIndex = -1
return
}
if (jsonCollections.currentIndex === -1 && jsonCollections.model.rowCount())
jsonCollections.currentIndex = 0
}
function updateCollectionExists() {
collectionName.alreadyExists = sourceModel.collectionExists(jsonCollections.currentValue,
collectionName.text)
}
component NameField: Text {
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
horizontalAlignment: Qt.AlignRight
verticalAlignment: Qt.AlignCenter
color: StudioTheme.Values.themeTextColor color: StudioTheme.Values.themeTextColor
font.family: StudioTheme.Constants.font.family
font.pixelSize: StudioTheme.Values.baseIconFontSize
}
component ErrorField: Text {
Layout.columnSpan: 2
color: StudioTheme.Values.themeError
text: qsTr("Collection name can not be empty")
font.family: StudioTheme.Constants.font.family
font.pixelSize: StudioTheme.Values.baseIconFontSize
}
contentItem: ColumnLayout {
spacing: 10
GridLayout {
columns: 2
rowSpacing: 10
NameField {
text: qsTr("Type")
}
StudioControls.ComboBox {
id: typeMode
property string collectionType
Layout.minimumWidth: 300
Layout.fillWidth: true
model: ListModel {
ListElement { text: qsTr("New Json collection"); value: NewCollectionDialog.SourceType.NewJson}
ListElement { text: qsTr("New CSV collection"); value: NewCollectionDialog.SourceType.NewCsv}
ListElement { text: qsTr("Import an existing collection"); value: NewCollectionDialog.SourceType.ExistingCollection}
ListElement { text: qsTr("Add collection to an available JSON"); value: NewCollectionDialog.SourceType.NewCollectionToJson}
}
textRole: "text"
valueRole: "value"
actionIndicatorVisible: false
onCurrentValueChanged: root.updateType()
}
NameField {
text: qsTr("File location")
visible: newCollectionPath.enabled
}
RowLayout {
visible: newCollectionPath.enabled
Text {
id: newCollectionPath
readonly property bool isValid: !enabled || text !== ""
Layout.fillWidth: true
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
elide: Text.ElideRight
font.family: StudioTheme.Constants.font.family
font.pixelSize: StudioTheme.Values.baseIconFontSize
color: StudioTheme.Values.themePlaceholderTextColor
}
HelperWidgets.Button {
Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
text: qsTr("Select")
onClicked: newCollectionFileDialog.open()
PlatformWidgets.FileDialog {
id: newCollectionFileDialog
title: "Select source file"
fileMode: PlatformWidgets.FileDialog.OpenFile
acceptLabel: fileMode === PlatformWidgets.FileDialog.OpenFile ? qsTr("Open") : qsTr("Add")
onAccepted: newCollectionPath.text = newCollectionFileDialog.currentFile
}
}
}
ErrorField {
visible: !newCollectionPath.isValid
text: qsTr("Select a file to continue")
}
NameField {
text: qsTr("Json Collection")
visible: jsonCollections.enabled
}
StudioControls.ComboBox {
id: jsonCollections
readonly property bool isValid: !enabled || currentIndex !== -1
implicitWidth: 300
textRole: "sourceName"
valueRole: "sourceNode"
visible: enabled
actionIndicatorVisible: false
model: CollectionJsonSourceFilterModel {
sourceModel: root.sourceModel
onRowsInserted: root.updateJsonSourceIndex()
onModelReset: root.updateJsonSourceIndex()
onRowsRemoved: root.updateJsonSourceIndex()
}
onEnabledChanged: root.updateJsonSourceIndex()
onCurrentValueChanged: root.updateCollectionExists()
}
ErrorField {
visible: !jsonCollections.isValid
text: qsTr("Add a json resource to continue")
}
NameField {
text: qsTr("Collection name")
visible: collectionName.enabled
} }
StudioControls.TextField { StudioControls.TextField {
id: collectionName id: collectionName
anchors.verticalCenter: parent.verticalCenter readonly property bool isValid: !enabled || (text !== "" && !alreadyExists)
property bool alreadyExists
visible: enabled
actionIndicator.visible: false actionIndicator.visible: false
translationIndicator.visible: false translationIndicator.visible: false
validator: HelperWidgets.RegExpValidator { validator: HelperWidgets.RegExpValidator {
@@ -54,38 +241,42 @@ StudioControls.Dialog {
Keys.onEnterPressed: btnCreate.onClicked() Keys.onEnterPressed: btnCreate.onClicked()
Keys.onReturnPressed: btnCreate.onClicked() Keys.onReturnPressed: btnCreate.onClicked()
Keys.onEscapePressed: root.reject() Keys.onEscapePressed: root.reject()
}
onTextChanged: root.updateCollectionExists()
} }
Text { ErrorField {
id: fieldErrorText
color: StudioTheme.Values.themeTextColor
anchors.right: parent.right
text: qsTr("Collection name can not be empty") text: qsTr("Collection name can not be empty")
visible: collectionName.text === "" visible: collectionName.enabled && collectionName.text === ""
}
ErrorField {
text: qsTr("Collection name already exists %1").arg(collectionName.text)
visible: collectionName.enabled && collectionName.alreadyExists
}
} }
Item { // spacer Item { // spacer
width: 1 Layout.fillHeight: true
height: 20 Layout.preferredWidth: 1
} }
Row { RowLayout {
anchors.right: parent.right
spacing: 10 spacing: 10
Layout.alignment: Qt.AlignRight | Qt.AlignBottom
HelperWidgets.Button { HelperWidgets.Button {
id: btnCreate id: btnCreate
anchors.verticalCenter: parent.verticalCenter
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
text: qsTr("Create") text: qsTr("Create")
enabled: collectionName.text !== "" enabled: root.isValid
onClicked: root.accept() onClicked: root.accept()
} }
HelperWidgets.Button { HelperWidgets.Button {
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
text: qsTr("Cancel") text: qsTr("Cancel")
anchors.verticalCenter: parent.verticalCenter
onClicked: root.reject() onClicked: root.reject()
} }
} }

View File

@@ -485,7 +485,6 @@ void CollectionDetailsModel::loadJsonCollection(const QString &source, const QSt
} }
if (collectionNodes.isEmpty()) { if (collectionNodes.isEmpty()) {
closeCurrentCollectionIfSaved();
endResetModel(); endResetModel();
return; return;
}; };

View File

@@ -94,6 +94,11 @@ QString CollectionListModel::sourceAddress() const
return m_sourceNode.variantProperty(CollectionEditor::SOURCEFILE_PROPERTY).value().toString(); return m_sourceNode.variantProperty(CollectionEditor::SOURCEFILE_PROPERTY).value().toString();
} }
bool CollectionListModel::contains(const QString &collectionName) const
{
return stringList().contains(collectionName);
}
void CollectionListModel::selectCollectionIndex(int idx, bool selectAtLeastOne) void CollectionListModel::selectCollectionIndex(int idx, bool selectAtLeastOne)
{ {
int collectionCount = stringList().size(); int collectionCount = stringList().size();
@@ -108,6 +113,13 @@ void CollectionListModel::selectCollectionIndex(int idx, bool selectAtLeastOne)
setSelectedIndex(preferredIndex); setSelectedIndex(preferredIndex);
} }
void CollectionListModel::selectCollectionName(const QString &collectionName)
{
int idx = stringList().indexOf(collectionName);
if (idx > -1)
selectCollectionIndex(idx);
}
QString CollectionListModel::collectionNameAt(int idx) const QString CollectionListModel::collectionNameAt(int idx) const
{ {
return index(idx).data(NameRole).toString(); return index(idx).data(NameRole).toString();

View File

@@ -28,13 +28,16 @@ public:
Q_INVOKABLE int selectedIndex() const; Q_INVOKABLE int selectedIndex() const;
Q_INVOKABLE ModelNode sourceNode() const; Q_INVOKABLE ModelNode sourceNode() const;
Q_INVOKABLE QString sourceAddress() const; Q_INVOKABLE QString sourceAddress() const;
Q_INVOKABLE bool contains(const QString &collectionName) const;
void selectCollectionIndex(int idx, bool selectAtLeastOne = false); void selectCollectionIndex(int idx, bool selectAtLeastOne = false);
void selectCollectionName(const QString &collectionName);
QString collectionNameAt(int idx) const; QString collectionNameAt(int idx) const;
signals: signals:
void selectedIndexChanged(int idx); void selectedIndexChanged(int idx);
void isEmptyChanged(bool); void isEmptyChanged(bool);
void collectionNameChanged(const QString &oldName, const QString &newName);
private: private:
void setSelectedIndex(int idx); void setSelectedIndex(int idx);

View File

@@ -9,8 +9,11 @@
#include "variantproperty.h" #include "variantproperty.h"
#include <utils/qtcassert.h> #include <utils/qtcassert.h>
#include <qqml.h>
#include <QFile> #include <QFile>
#include <QFileInfo>
#include <QJsonArray>
#include <QJsonDocument> #include <QJsonDocument>
#include <QJsonObject> #include <QJsonObject>
#include <QJsonParseError> #include <QJsonParseError>
@@ -58,11 +61,26 @@ QSharedPointer<QmlDesigner::CollectionListModel> loadCollection(
} }
return collectionsList; return collectionsList;
} }
QString getSourceCollectionType(const QmlDesigner::ModelNode &node)
{
using namespace QmlDesigner;
if (node.type() == CollectionEditor::JSONCOLLECTIONMODEL_TYPENAME)
return "json";
if (node.type() == CollectionEditor::CSVCOLLECTIONMODEL_TYPENAME)
return "csv";
return {};
}
} // namespace } // namespace
namespace QmlDesigner { namespace QmlDesigner {
CollectionSourceModel::CollectionSourceModel() {} CollectionSourceModel::CollectionSourceModel(QObject *parent)
: Super(parent)
{}
int CollectionSourceModel::rowCount(const QModelIndex &) const int CollectionSourceModel::rowCount(const QModelIndex &) const
{ {
@@ -80,6 +98,10 @@ QVariant CollectionSourceModel::data(const QModelIndex &index, int role) const
return collectionSource->id(); return collectionSource->id();
case NameRole: case NameRole:
return collectionSource->variantProperty("objectName").value(); return collectionSource->variantProperty("objectName").value();
case NodeRole:
return QVariant::fromValue(*collectionSource);
case CollectionTypeRole:
return getSourceCollectionType(*collectionSource);
case SourceRole: case SourceRole:
return collectionSource->variantProperty(CollectionEditor::SOURCEFILE_PROPERTY).value(); return collectionSource->variantProperty(CollectionEditor::SOURCEFILE_PROPERTY).value();
case SelectedRole: case SelectedRole:
@@ -188,6 +210,8 @@ QHash<int, QByteArray> CollectionSourceModel::roleNames() const
roles.insert(Super::roleNames()); roles.insert(Super::roleNames());
roles.insert({{IdRole, "sourceId"}, roles.insert({{IdRole, "sourceId"},
{NameRole, "sourceName"}, {NameRole, "sourceName"},
{NodeRole, "sourceNode"},
{CollectionTypeRole, "sourceCollectionType"},
{SelectedRole, "sourceIsSelected"}, {SelectedRole, "sourceIsSelected"},
{SourceRole, "sourceAddress"}, {SourceRole, "sourceAddress"},
{CollectionsRole, "collections"}}); {CollectionsRole, "collections"}});
@@ -265,6 +289,83 @@ void CollectionSourceModel::selectSource(const ModelNode &node)
selectSourceIndex(nodePlace, true); selectSourceIndex(nodePlace, true);
} }
bool CollectionSourceModel::collectionExists(const ModelNode &node, const QString &collectionName) const
{
int idx = sourceIndex(node);
if (idx < 0)
return false;
auto collections = m_collectionList.at(idx);
if (collections.isNull())
return false;
return collections->contains(collectionName);
}
bool CollectionSourceModel::addCollectionToSource(const ModelNode &node,
const QString &collectionName,
QString *errorString)
{
auto returnError = [errorString](const QString &msg) -> bool {
if (errorString)
*errorString = msg;
return false;
};
int idx = sourceIndex(node);
if (idx < 0)
return returnError(tr("Node is not indexed in the collections model."));
if (node.type() != CollectionEditor::JSONCOLLECTIONMODEL_TYPENAME)
return returnError(tr("Node should be a json collection model."));
if (collectionExists(node, collectionName))
return returnError(tr("Collection does not exist."));
QString sourceFileAddress = node.variantProperty(CollectionEditor::SOURCEFILE_PROPERTY)
.value()
.toString();
QFileInfo sourceFileInfo(sourceFileAddress);
if (!sourceFileInfo.isFile())
return returnError(tr("Selected node should have a valid source file address"));
QFile jsonFile(sourceFileAddress);
if (!jsonFile.open(QFile::ReadWrite))
return returnError(tr("Can't open the file to read.\n") + jsonFile.errorString());
QJsonParseError parseError;
QJsonDocument document = QJsonDocument::fromJson(jsonFile.readAll(), &parseError);
if (parseError.error != QJsonParseError::NoError)
return returnError(tr("Saved json file is messy.\n") + parseError.errorString());
if (document.isObject()) {
QJsonObject sourceObject = document.object();
sourceObject.insert(collectionName, QJsonArray{});
document.setObject(sourceObject);
if (!jsonFile.resize(0))
return returnError(tr("Can't clean the json file."));
QByteArray jsonData = document.toJson();
auto writtenBytes = jsonFile.write(jsonData);
jsonFile.close();
if (writtenBytes != jsonData.size())
return returnError(tr("Can't write to the json file."));
updateCollectionList(index(idx));
auto collections = m_collectionList.at(idx);
if (collections.isNull())
return returnError(tr("No collection is available for the json file."));
collections->selectCollectionName(collectionName);
return true;
} else {
return returnError(tr("Json document type should be an object containing collections."));
}
}
QmlDesigner::ModelNode CollectionSourceModel::sourceNodeAt(int idx) QmlDesigner::ModelNode CollectionSourceModel::sourceNodeAt(int idx)
{ {
QModelIndex data = index(idx); QModelIndex data = index(idx);
@@ -309,6 +410,11 @@ void CollectionSourceModel::updateSelectedSource(bool selectAtLeastOne)
selectSourceIndex(idx, selectAtLeastOne); selectSourceIndex(idx, selectAtLeastOne);
} }
bool CollectionSourceModel::collectionExists(const QVariant &node, const QString &collectionName) const
{
return collectionExists(node.value<ModelNode>(), collectionName);
}
void CollectionSourceModel::updateNodeName(const ModelNode &node) void CollectionSourceModel::updateNodeName(const ModelNode &node)
{ {
QModelIndex index = indexOfNode(node); QModelIndex index = indexOfNode(node);
@@ -412,4 +518,21 @@ QModelIndex CollectionSourceModel::indexOfNode(const ModelNode &node) const
{ {
return index(m_sourceIndexHash.value(node.internalId(), -1)); return index(m_sourceIndexHash.value(node.internalId(), -1));
} }
void CollectionJsonSourceFilterModel::registerDeclarativeType()
{
qmlRegisterType<CollectionJsonSourceFilterModel>("CollectionEditor",
1,
0,
"CollectionJsonSourceFilterModel");
}
bool CollectionJsonSourceFilterModel::filterAcceptsRow(int source_row, const QModelIndex &) const
{
if (!sourceModel())
return false;
QModelIndex sourceItem = sourceModel()->index(source_row, 0, {});
return sourceItem.data(CollectionSourceModel::Roles::CollectionTypeRole).toString() == "json";
}
} // namespace QmlDesigner } // namespace QmlDesigner

View File

@@ -7,9 +7,13 @@
#include <QAbstractListModel> #include <QAbstractListModel>
#include <QHash> #include <QHash>
#include <QSortFilterProxyModel>
namespace QmlDesigner { namespace QmlDesigner {
class CollectionJsonSourceFilterModel;
class CollectionListModel; class CollectionListModel;
class CollectionSourceModel : public QAbstractListModel class CollectionSourceModel : public QAbstractListModel
{ {
Q_OBJECT Q_OBJECT
@@ -18,9 +22,17 @@ class CollectionSourceModel : public QAbstractListModel
Q_PROPERTY(bool isEmpty MEMBER m_isEmpty NOTIFY isEmptyChanged) Q_PROPERTY(bool isEmpty MEMBER m_isEmpty NOTIFY isEmptyChanged)
public: public:
enum Roles { IdRole = Qt::UserRole + 1, NameRole, SourceRole, SelectedRole, CollectionsRole }; enum Roles {
IdRole = Qt::UserRole + 1,
NameRole,
NodeRole,
CollectionTypeRole,
SourceRole,
SelectedRole,
CollectionsRole
};
explicit CollectionSourceModel(); explicit CollectionSourceModel(QObject *parent = nullptr);
virtual int rowCount(const QModelIndex &parent = QModelIndex()) const override; virtual int rowCount(const QModelIndex &parent = QModelIndex()) const override;
virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
@@ -40,12 +52,19 @@ public:
void addSource(const ModelNode &node); void addSource(const ModelNode &node);
void selectSource(const ModelNode &node); void selectSource(const ModelNode &node);
bool collectionExists(const ModelNode &node, const QString &collectionName) const;
bool addCollectionToSource(const ModelNode &node,
const QString &collectionName,
QString *errorString = nullptr);
ModelNode sourceNodeAt(int idx); ModelNode sourceNodeAt(int idx);
CollectionListModel *selectedCollectionList(); CollectionListModel *selectedCollectionList();
Q_INVOKABLE void selectSourceIndex(int idx, bool selectAtLeastOne = false); Q_INVOKABLE void selectSourceIndex(int idx, bool selectAtLeastOne = false);
Q_INVOKABLE void deselect(); Q_INVOKABLE void deselect();
Q_INVOKABLE void updateSelectedSource(bool selectAtLeastOne = false); Q_INVOKABLE void updateSelectedSource(bool selectAtLeastOne = false);
Q_INVOKABLE bool collectionExists(const QVariant &node, const QString &collectionName) const;
void updateNodeName(const ModelNode &node); void updateNodeName(const ModelNode &node);
void updateNodeSource(const ModelNode &node); void updateNodeSource(const ModelNode &node);
void updateNodeId(const ModelNode &node); void updateNodeId(const ModelNode &node);
@@ -64,10 +83,10 @@ private:
void setSelectedIndex(int idx); void setSelectedIndex(int idx);
void updateEmpty(); void updateEmpty();
void updateCollectionList(QModelIndex index); void updateCollectionList(QModelIndex index);
QModelIndex indexOfNode(const ModelNode &node) const;
using Super = QAbstractListModel; using Super = QAbstractListModel;
QModelIndex indexOfNode(const ModelNode &node) const;
ModelNodes m_collectionSources; ModelNodes m_collectionSources;
QHash<qint32, int> m_sourceIndexHash; // internalId -> index QHash<qint32, int> m_sourceIndexHash; // internalId -> index
QList<QSharedPointer<CollectionListModel>> m_collectionList; QList<QSharedPointer<CollectionListModel>> m_collectionList;
@@ -76,4 +95,15 @@ private:
bool m_isEmpty = true; bool m_isEmpty = true;
}; };
class CollectionJsonSourceFilterModel : public QSortFilterProxyModel
{
Q_OBJECT
public:
static void registerDeclarativeType();
protected:
bool filterAcceptsRow(int source_row, const QModelIndex &) const override;
};
} // namespace QmlDesigner } // namespace QmlDesigner

View File

@@ -156,6 +156,7 @@ void CollectionView::addResource(const QUrl &url, const QString &name, const QSt
void CollectionView::registerDeclarativeType() void CollectionView::registerDeclarativeType()
{ {
CollectionDetails::registerDeclarativeType(); CollectionDetails::registerDeclarativeType();
CollectionJsonSourceFilterModel::registerDeclarativeType();
} }
void CollectionView::refreshModel() void CollectionView::refreshModel()

View File

@@ -16,7 +16,9 @@
#include <QFile> #include <QFile>
#include <QFileInfo> #include <QFileInfo>
#include <QJsonArray>
#include <QJsonDocument> #include <QJsonDocument>
#include <QJsonObject>
#include <QJsonParseError> #include <QJsonParseError>
#include <QMetaObject> #include <QMetaObject>
#include <QQmlEngine> #include <QQmlEngine>
@@ -25,6 +27,7 @@
#include <QVBoxLayout> #include <QVBoxLayout>
namespace { namespace {
QString collectionViewResourcesPath() QString collectionViewResourcesPath()
{ {
#ifdef SHARE_QML_PATH #ifdef SHARE_QML_PATH
@@ -33,6 +36,22 @@ QString collectionViewResourcesPath()
#endif #endif
return Core::ICore::resourcePath("qmldesigner/collectionEditorQmlSource").toString(); return Core::ICore::resourcePath("qmldesigner/collectionEditorQmlSource").toString();
} }
static QString urlToLocalPath(const QUrl &url)
{
QString localPath;
if (url.isLocalFile())
localPath = url.toLocalFile();
if (url.scheme() == QLatin1String("qrc")) {
const QString &path = url.path();
localPath = QStringLiteral(":") + path;
}
return localPath;
}
} // namespace } // namespace
namespace QmlDesigner { namespace QmlDesigner {
@@ -161,9 +180,65 @@ bool CollectionWidget::isCsvFile(const QString &csvFileAddress) const
return true; return true;
} }
bool CollectionWidget::addCollection([[maybe_unused]] const QString &collectionName) const bool CollectionWidget::addCollection(const QString &collectionName,
const QString &collectionType,
const QString &sourceAddress,
const QVariant &sourceNode)
{ {
// TODO const ModelNode node = sourceNode.value<ModelNode>();
bool isNewCollection = !node.isValid();
if (isNewCollection) {
QString sourcePath = ::urlToLocalPath(sourceAddress);
if (collectionType == "json") {
QJsonObject jsonObject;
jsonObject.insert(collectionName, QJsonArray());
QFile sourceFile(sourcePath);
if (!sourceFile.open(QFile::WriteOnly)) {
warn(tr("File error"),
tr("Can not open the file to write.\n") + sourceFile.errorString());
return false;
}
sourceFile.write(QJsonDocument(jsonObject).toJson());
sourceFile.close();
bool loaded = loadJsonFile(sourcePath);
if (!loaded)
sourceFile.remove();
return loaded;
} else if (collectionType == "csv") {
QFile sourceFile(sourcePath);
if (!sourceFile.open(QFile::WriteOnly)) {
warn(tr("File error"),
tr("Can not open the file to write.\n") + sourceFile.errorString());
return false;
}
sourceFile.close();
bool loaded = loadCsvFile(collectionName, sourcePath);
if (!loaded)
sourceFile.remove();
return loaded;
} else if (collectionType == "existing") {
QFileInfo fileInfo(sourcePath);
if (fileInfo.suffix() == "json")
return loadJsonFile(sourcePath);
else if (fileInfo.suffix() == "csv")
return loadCsvFile(collectionName, sourcePath);
}
} else if (collectionType == "json") {
QString errorMsg;
bool added = m_sourceModel->addCollectionToSource(node, collectionName, &errorMsg);
if (!added)
warn(tr("Can not add a collection to the json file"), errorMsg);
return added;
}
return false; return false;
} }

View File

@@ -15,6 +15,7 @@ class CollectionDetailsModel;
class CollectionDetailsSortFilterModel; class CollectionDetailsSortFilterModel;
class CollectionSourceModel; class CollectionSourceModel;
class CollectionView; class CollectionView;
class ModelNode;
class CollectionWidget : public QFrame class CollectionWidget : public QFrame
{ {
@@ -33,7 +34,10 @@ public:
Q_INVOKABLE bool loadCsvFile(const QString &collectionName, const QString &csvFileAddress); Q_INVOKABLE bool loadCsvFile(const QString &collectionName, const QString &csvFileAddress);
Q_INVOKABLE bool isJsonFile(const QString &jsonFileAddress) const; Q_INVOKABLE bool isJsonFile(const QString &jsonFileAddress) const;
Q_INVOKABLE bool isCsvFile(const QString &csvFileAddress) const; Q_INVOKABLE bool isCsvFile(const QString &csvFileAddress) const;
Q_INVOKABLE bool addCollection(const QString &collectionName) const; Q_INVOKABLE bool addCollection(const QString &collectionName,
const QString &collectionType,
const QString &sourceAddress,
const QVariant &sourceNode);
void warn(const QString &title, const QString &body); void warn(const QString &title, const QString &body);