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
|
||||
|
||||
backendValue: root.rootView
|
||||
sourceModel: root.model
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
|
||||
|
@@ -3,23 +3,37 @@
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import QtQuickDesignerTheme 1.0
|
||||
import Qt.labs.platform as PlatformWidgets
|
||||
import HelperWidgets 2.0 as HelperWidgets
|
||||
import StudioControls 1.0 as StudioControls
|
||||
import StudioTheme as StudioTheme
|
||||
import CollectionEditor 1.0
|
||||
|
||||
StudioControls.Dialog {
|
||||
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")
|
||||
anchors.centerIn: parent
|
||||
closePolicy: Popup.CloseOnEscape
|
||||
modal: true
|
||||
|
||||
required property var backendValue
|
||||
|
||||
onOpened: {
|
||||
collectionName.text = "Collection"
|
||||
collectionName.text = qsTr("Collection")
|
||||
updateType()
|
||||
updateJsonSourceIndex()
|
||||
updateCollectionExists()
|
||||
}
|
||||
|
||||
onRejected: {
|
||||
@@ -27,24 +41,197 @@ StudioControls.Dialog {
|
||||
}
|
||||
|
||||
onAccepted: {
|
||||
if (collectionName.text !== "")
|
||||
root.backendValue.addCollection(collectionName.text)
|
||||
if (root.isValid) {
|
||||
root.backendValue.addCollection(collectionName.text,
|
||||
root.collectionType,
|
||||
newCollectionPath.text,
|
||||
jsonCollections.currentValue)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
contentItem: Column {
|
||||
function updateType() {
|
||||
newCollectionPath.text = ""
|
||||
if (typeMode.currentValue === NewCollectionDialog.SourceType.NewJson) {
|
||||
newCollectionFileDialog.nameFilters = ["Json Files (*.json)"]
|
||||
newCollectionFileDialog.fileMode = PlatformWidgets.FileDialog.SaveFile
|
||||
newCollectionPath.enabled = true
|
||||
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
|
||||
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
|
||||
Row {
|
||||
spacing: 10
|
||||
Text {
|
||||
text: qsTr("Collection name: ")
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
color: StudioTheme.Values.themeTextColor
|
||||
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 {
|
||||
id: collectionName
|
||||
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
readonly property bool isValid: !enabled || (text !== "" && !alreadyExists)
|
||||
property bool alreadyExists
|
||||
|
||||
visible: enabled
|
||||
actionIndicator.visible: false
|
||||
translationIndicator.visible: false
|
||||
validator: HelperWidgets.RegExpValidator {
|
||||
@@ -54,38 +241,42 @@ StudioControls.Dialog {
|
||||
Keys.onEnterPressed: btnCreate.onClicked()
|
||||
Keys.onReturnPressed: btnCreate.onClicked()
|
||||
Keys.onEscapePressed: root.reject()
|
||||
|
||||
onTextChanged: root.updateCollectionExists()
|
||||
}
|
||||
|
||||
ErrorField {
|
||||
text: qsTr("Collection name can not be empty")
|
||||
visible: collectionName.enabled && collectionName.text === ""
|
||||
}
|
||||
|
||||
ErrorField {
|
||||
text: qsTr("Collection name already exists %1").arg(collectionName.text)
|
||||
visible: collectionName.enabled && collectionName.alreadyExists
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
id: fieldErrorText
|
||||
color: StudioTheme.Values.themeTextColor
|
||||
anchors.right: parent.right
|
||||
text: qsTr("Collection name can not be empty")
|
||||
visible: collectionName.text === ""
|
||||
}
|
||||
|
||||
Item { // spacer
|
||||
width: 1
|
||||
height: 20
|
||||
Layout.fillHeight: true
|
||||
Layout.preferredWidth: 1
|
||||
}
|
||||
|
||||
Row {
|
||||
anchors.right: parent.right
|
||||
RowLayout {
|
||||
spacing: 10
|
||||
Layout.alignment: Qt.AlignRight | Qt.AlignBottom
|
||||
|
||||
HelperWidgets.Button {
|
||||
id: btnCreate
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
|
||||
text: qsTr("Create")
|
||||
enabled: collectionName.text !== ""
|
||||
enabled: root.isValid
|
||||
onClicked: root.accept()
|
||||
}
|
||||
|
||||
HelperWidgets.Button {
|
||||
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
|
||||
text: qsTr("Cancel")
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
onClicked: root.reject()
|
||||
}
|
||||
}
|
||||
|
@@ -485,7 +485,6 @@ void CollectionDetailsModel::loadJsonCollection(const QString &source, const QSt
|
||||
}
|
||||
|
||||
if (collectionNodes.isEmpty()) {
|
||||
closeCurrentCollectionIfSaved();
|
||||
endResetModel();
|
||||
return;
|
||||
};
|
||||
|
@@ -94,6 +94,11 @@ QString CollectionListModel::sourceAddress() const
|
||||
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)
|
||||
{
|
||||
int collectionCount = stringList().size();
|
||||
@@ -108,6 +113,13 @@ void CollectionListModel::selectCollectionIndex(int idx, bool selectAtLeastOne)
|
||||
setSelectedIndex(preferredIndex);
|
||||
}
|
||||
|
||||
void CollectionListModel::selectCollectionName(const QString &collectionName)
|
||||
{
|
||||
int idx = stringList().indexOf(collectionName);
|
||||
if (idx > -1)
|
||||
selectCollectionIndex(idx);
|
||||
}
|
||||
|
||||
QString CollectionListModel::collectionNameAt(int idx) const
|
||||
{
|
||||
return index(idx).data(NameRole).toString();
|
||||
|
@@ -28,13 +28,16 @@ public:
|
||||
Q_INVOKABLE int selectedIndex() const;
|
||||
Q_INVOKABLE ModelNode sourceNode() const;
|
||||
Q_INVOKABLE QString sourceAddress() const;
|
||||
Q_INVOKABLE bool contains(const QString &collectionName) const;
|
||||
|
||||
void selectCollectionIndex(int idx, bool selectAtLeastOne = false);
|
||||
void selectCollectionName(const QString &collectionName);
|
||||
QString collectionNameAt(int idx) const;
|
||||
|
||||
signals:
|
||||
void selectedIndexChanged(int idx);
|
||||
void isEmptyChanged(bool);
|
||||
void collectionNameChanged(const QString &oldName, const QString &newName);
|
||||
|
||||
private:
|
||||
void setSelectedIndex(int idx);
|
||||
|
@@ -9,8 +9,11 @@
|
||||
#include "variantproperty.h"
|
||||
|
||||
#include <utils/qtcassert.h>
|
||||
#include <qqml.h>
|
||||
|
||||
#include <QFile>
|
||||
#include <QFileInfo>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QJsonParseError>
|
||||
@@ -58,11 +61,26 @@ QSharedPointer<QmlDesigner::CollectionListModel> loadCollection(
|
||||
}
|
||||
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 QmlDesigner {
|
||||
|
||||
CollectionSourceModel::CollectionSourceModel() {}
|
||||
CollectionSourceModel::CollectionSourceModel(QObject *parent)
|
||||
: Super(parent)
|
||||
{}
|
||||
|
||||
int CollectionSourceModel::rowCount(const QModelIndex &) const
|
||||
{
|
||||
@@ -80,6 +98,10 @@ QVariant CollectionSourceModel::data(const QModelIndex &index, int role) const
|
||||
return collectionSource->id();
|
||||
case NameRole:
|
||||
return collectionSource->variantProperty("objectName").value();
|
||||
case NodeRole:
|
||||
return QVariant::fromValue(*collectionSource);
|
||||
case CollectionTypeRole:
|
||||
return getSourceCollectionType(*collectionSource);
|
||||
case SourceRole:
|
||||
return collectionSource->variantProperty(CollectionEditor::SOURCEFILE_PROPERTY).value();
|
||||
case SelectedRole:
|
||||
@@ -188,6 +210,8 @@ QHash<int, QByteArray> CollectionSourceModel::roleNames() const
|
||||
roles.insert(Super::roleNames());
|
||||
roles.insert({{IdRole, "sourceId"},
|
||||
{NameRole, "sourceName"},
|
||||
{NodeRole, "sourceNode"},
|
||||
{CollectionTypeRole, "sourceCollectionType"},
|
||||
{SelectedRole, "sourceIsSelected"},
|
||||
{SourceRole, "sourceAddress"},
|
||||
{CollectionsRole, "collections"}});
|
||||
@@ -265,6 +289,83 @@ void CollectionSourceModel::selectSource(const ModelNode &node)
|
||||
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)
|
||||
{
|
||||
QModelIndex data = index(idx);
|
||||
@@ -309,6 +410,11 @@ void CollectionSourceModel::updateSelectedSource(bool 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)
|
||||
{
|
||||
QModelIndex index = indexOfNode(node);
|
||||
@@ -412,4 +518,21 @@ QModelIndex CollectionSourceModel::indexOfNode(const ModelNode &node) const
|
||||
{
|
||||
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
|
||||
|
@@ -7,9 +7,13 @@
|
||||
|
||||
#include <QAbstractListModel>
|
||||
#include <QHash>
|
||||
#include <QSortFilterProxyModel>
|
||||
|
||||
namespace QmlDesigner {
|
||||
|
||||
class CollectionJsonSourceFilterModel;
|
||||
class CollectionListModel;
|
||||
|
||||
class CollectionSourceModel : public QAbstractListModel
|
||||
{
|
||||
Q_OBJECT
|
||||
@@ -18,9 +22,17 @@ class CollectionSourceModel : public QAbstractListModel
|
||||
Q_PROPERTY(bool isEmpty MEMBER m_isEmpty NOTIFY isEmptyChanged)
|
||||
|
||||
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 QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
||||
@@ -40,12 +52,19 @@ public:
|
||||
void addSource(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);
|
||||
CollectionListModel *selectedCollectionList();
|
||||
|
||||
Q_INVOKABLE void selectSourceIndex(int idx, bool selectAtLeastOne = false);
|
||||
Q_INVOKABLE void deselect();
|
||||
Q_INVOKABLE void updateSelectedSource(bool selectAtLeastOne = false);
|
||||
Q_INVOKABLE bool collectionExists(const QVariant &node, const QString &collectionName) const;
|
||||
|
||||
void updateNodeName(const ModelNode &node);
|
||||
void updateNodeSource(const ModelNode &node);
|
||||
void updateNodeId(const ModelNode &node);
|
||||
@@ -64,10 +83,10 @@ private:
|
||||
void setSelectedIndex(int idx);
|
||||
void updateEmpty();
|
||||
void updateCollectionList(QModelIndex index);
|
||||
QModelIndex indexOfNode(const ModelNode &node) const;
|
||||
|
||||
using Super = QAbstractListModel;
|
||||
|
||||
QModelIndex indexOfNode(const ModelNode &node) const;
|
||||
ModelNodes m_collectionSources;
|
||||
QHash<qint32, int> m_sourceIndexHash; // internalId -> index
|
||||
QList<QSharedPointer<CollectionListModel>> m_collectionList;
|
||||
@@ -76,4 +95,15 @@ private:
|
||||
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
|
||||
|
@@ -156,6 +156,7 @@ void CollectionView::addResource(const QUrl &url, const QString &name, const QSt
|
||||
void CollectionView::registerDeclarativeType()
|
||||
{
|
||||
CollectionDetails::registerDeclarativeType();
|
||||
CollectionJsonSourceFilterModel::registerDeclarativeType();
|
||||
}
|
||||
|
||||
void CollectionView::refreshModel()
|
||||
|
@@ -16,7 +16,9 @@
|
||||
|
||||
#include <QFile>
|
||||
#include <QFileInfo>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QJsonParseError>
|
||||
#include <QMetaObject>
|
||||
#include <QQmlEngine>
|
||||
@@ -25,6 +27,7 @@
|
||||
#include <QVBoxLayout>
|
||||
|
||||
namespace {
|
||||
|
||||
QString collectionViewResourcesPath()
|
||||
{
|
||||
#ifdef SHARE_QML_PATH
|
||||
@@ -33,6 +36,22 @@ QString collectionViewResourcesPath()
|
||||
#endif
|
||||
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 QmlDesigner {
|
||||
@@ -161,9 +180,65 @@ bool CollectionWidget::isCsvFile(const QString &csvFileAddress) const
|
||||
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;
|
||||
}
|
||||
|
||||
|
@@ -15,6 +15,7 @@ class CollectionDetailsModel;
|
||||
class CollectionDetailsSortFilterModel;
|
||||
class CollectionSourceModel;
|
||||
class CollectionView;
|
||||
class ModelNode;
|
||||
|
||||
class CollectionWidget : public QFrame
|
||||
{
|
||||
@@ -33,7 +34,10 @@ public:
|
||||
Q_INVOKABLE bool loadCsvFile(const QString &collectionName, const QString &csvFileAddress);
|
||||
Q_INVOKABLE bool isJsonFile(const QString &jsonFileAddress) 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);
|
||||
|
||||
|
Reference in New Issue
Block a user