forked from qt-creator/qt-creator
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:
@@ -41,6 +41,7 @@ Item {
|
|||||||
id: newCollection
|
id: newCollection
|
||||||
|
|
||||||
backendValue: root.rootView
|
backendValue: root.rootView
|
||||||
|
sourceModel: root.model
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -485,7 +485,6 @@ void CollectionDetailsModel::loadJsonCollection(const QString &source, const QSt
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (collectionNodes.isEmpty()) {
|
if (collectionNodes.isEmpty()) {
|
||||||
closeCurrentCollectionIfSaved();
|
|
||||||
endResetModel();
|
endResetModel();
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
@@ -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();
|
||||||
|
@@ -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);
|
||||||
|
@@ -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
|
||||||
|
@@ -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
|
||||||
|
@@ -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()
|
||||||
|
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -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);
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user