diff options
author | Ali Kianian <ali.kianian@qt.io> | 2024-02-29 23:46:39 +0200 |
---|---|---|
committer | Ali Kianian <ali.kianian@qt.io> | 2024-03-01 13:17:42 +0000 |
commit | f7530458c456e15d318578b1c18435409936d319 (patch) | |
tree | fc75c69ab800a42a573dca4736e3bb5d7a7d4b77 /src/plugins/qmldesigner | |
parent | 9f024eb5cd7abc95a462506ebea9274d0cdd3939 (diff) |
QmlDesigner: Remove CollectionSourceModel
Task-number: QDS-12032
Change-Id: Id0b999500a0a8cae16a90a59d051cd725417e431
Reviewed-by: Mahmoud Badri <mahmoud.badri@qt.io>
Reviewed-by: Miikka Heikkinen <miikka.heikkinen@qt.io>
Diffstat (limited to 'src/plugins/qmldesigner')
13 files changed, 434 insertions, 1022 deletions
diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index aa4750f81d..5498097c1d 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -838,7 +838,6 @@ extend_qtc_plugin(QmlDesigner collectioneditorconstants.h collectioneditorutils.cpp collectioneditorutils.h collectionlistmodel.cpp collectionlistmodel.h - collectionsourcemodel.cpp collectionsourcemodel.h collectionview.cpp collectionview.h collectionwidget.cpp collectionwidget.h datastoremodelnode.cpp datastoremodelnode.h diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.cpp index ecc2544bcc..e1a17c51a1 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.cpp @@ -176,15 +176,6 @@ bool variantIslessThan(const QVariant &a, const QVariant &b, DataType type) return std::visit(LessThanVisitor{}, valueToVariant(a, type), valueToVariant(b, type)); } -CollectionEditorConstants::SourceFormat getSourceCollectionFormat(const ModelNode &node) -{ - using namespace QmlDesigner; - if (node.type() == CollectionEditorConstants::JSONCOLLECTIONMODEL_TYPENAME) - return CollectionEditorConstants::SourceFormat::Json; - - return CollectionEditorConstants::SourceFormat::Unknown; -} - QString getSourceCollectionType(const ModelNode &node) { using namespace QmlDesigner; diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.h b/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.h index bd9e24d23c..5be1c51f00 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.h +++ b/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.h @@ -19,8 +19,6 @@ namespace QmlDesigner::CollectionEditorUtils { bool variantIslessThan(const QVariant &a, const QVariant &b, CollectionDetails::DataType type); -CollectionEditorConstants::SourceFormat getSourceCollectionFormat(const QmlDesigner::ModelNode &node); - QString getSourceCollectionType(const QmlDesigner::ModelNode &node); QString getSourceCollectionPath(const QmlDesigner::ModelNode &dataStoreNode); diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionlistmodel.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionlistmodel.cpp index 323f0e767f..d27a077d2a 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionlistmodel.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionlistmodel.cpp @@ -6,8 +6,13 @@ #include "collectioneditorutils.h" #include <utils/algorithm.h> +#include <utils/fileutils.h> #include <utils/qtcassert.h> +#include <QJsonDocument> +#include <QJsonObject> +#include <QJsonParseError> + namespace { template<typename ValueType> @@ -20,14 +25,23 @@ bool containsItem(const std::initializer_list<ValueType> &container, const Value return it != end; } +bool sameCollectionNames(QStringList a, QStringList b) +{ + if (a.size() != b.size()) + return false; + + a.sort(Qt::CaseSensitive); + b.sort(Qt::CaseSensitive); + + return a == b; +} + } // namespace namespace QmlDesigner { -CollectionListModel::CollectionListModel(const ModelNode &sourceModel) +CollectionListModel::CollectionListModel() : QAbstractListModel() - , m_sourceNode(sourceModel) - , m_sourceType(CollectionEditorUtils::getSourceCollectionType(sourceModel)) { connect(this, &CollectionListModel::modelReset, this, &CollectionListModel::updateEmpty); connect(this, &CollectionListModel::rowsRemoved, this, &CollectionListModel::updateEmpty); @@ -59,18 +73,26 @@ bool CollectionListModel::setData(const QModelIndex &index, const QVariant &valu return false; if (containsItem<int>({Qt::EditRole, Qt::DisplayRole, NameRole}, role)) { - if (contains(value.toString())) + if (collectionExists(value.toString())) return false; QString oldName = collectionNameAt(index.row()); bool nameChanged = value != data(index); if (nameChanged) { QString newName = value.toString(); - m_data.replace(index.row(), newName); - emit dataChanged(index, index, {Qt::DisplayRole, Qt::EditRole}); - emit this->collectionNameChanged(oldName, newName); + QString errorString; + if (renameCollectionInDataStore(oldName, newName, errorString)) { + m_data.replace(index.row(), newName); + emit dataChanged(index, index, {Qt::DisplayRole, Qt::EditRole, NameRole}); + emit this->collectionNameChanged(oldName, newName); + if (m_selectedCollectionName == oldName) + updateSelectedCollectionName(); + return true; + } else { + emit warning("Rename Model", errorString); + return false; + } } - return nameChanged; } else if (role == SelectedRole) { if (value.toBool() != index.data(SelectedRole).toBool()) { setSelectedIndex(value.toBool() ? index.row() : -1); @@ -92,17 +114,27 @@ bool CollectionListModel::removeRows(int row, int count, const QModelIndex &pare if (count < 1) return false; + QString errorString; QStringList removedCollections = m_data.mid(row, count); + if (removeCollectionsFromDataStore(removedCollections, errorString)) { + beginRemoveRows(parent, row, row + count - 1); + m_data.remove(row, count); + endRemoveRows(); + + emit collectionsRemoved(removedCollections); + if (m_selectedIndex >= row) { + int preferredIndex = m_selectedIndex - count; + if (preferredIndex < 0) // If the selected item is deleted, reset selection + selectCollectionIndex(-1); + selectCollectionIndex(preferredIndex, true); + } - beginRemoveRows(parent, row, row + count - 1); - m_data.remove(row, count); - endRemoveRows(); - - emit collectionsRemoved(removedCollections); - if (m_selectedIndex >= row) - selectCollectionIndex(m_selectedIndex - count, true); - - return true; + updateSelectedCollectionName(); + return true; + } else { + emit warning("Remove Model", errorString); + return false; + } } QVariant CollectionListModel::data(const QModelIndex &index, int role) const @@ -120,13 +152,10 @@ QVariant CollectionListModel::data(const QModelIndex &index, int role) const } } -void CollectionListModel::resetModelData(const QStringList &collectionsList) +void CollectionListModel::setDataStoreNode(const ModelNode &dataStoreNode) { - QString prevSelectedCollection = selectedIndex() > -1 ? m_data.at(selectedIndex()) : QString(); - beginResetModel(); - m_data = collectionsList; - endResetModel(); - selectCollectionName(prevSelectedCollection); + m_dataStoreNode = dataStoreNode; + update(); } int CollectionListModel::selectedIndex() const @@ -136,15 +165,10 @@ int CollectionListModel::selectedIndex() const ModelNode CollectionListModel::sourceNode() const { - return m_sourceNode; -} - -QString CollectionListModel::sourceAddress() const -{ - return CollectionEditorUtils::getSourceCollectionPath(m_sourceNode); + return m_dataStoreNode; } -bool CollectionListModel::contains(const QString &collectionName) const +bool CollectionListModel::collectionExists(const QString &collectionName) const { return m_data.contains(collectionName); } @@ -154,6 +178,19 @@ QStringList CollectionListModel::collections() const return m_data; } +QString CollectionListModel::getUniqueCollectionName(const QString &baseName) const +{ + QString name = baseName.isEmpty() ? "Model" : baseName; + QString nameTemplate = name + "%1"; + + int num = 0; + + while (collectionExists(name)) + name = nameTemplate.arg(++num, 2, 10, QChar('0')); + + return name; +} + void CollectionListModel::selectCollectionIndex(int idx, bool selectAtLeastOne) { int collectionCount = m_data.size(); @@ -168,11 +205,20 @@ void CollectionListModel::selectCollectionIndex(int idx, bool selectAtLeastOne) setSelectedIndex(preferredIndex); } -void CollectionListModel::selectCollectionName(const QString &collectionName) +void CollectionListModel::selectCollectionName(QString collectionName, bool selectAtLeastOne) { int idx = m_data.indexOf(collectionName); if (idx > -1) selectCollectionIndex(idx); + else + selectCollectionIndex(selectedIndex(), selectAtLeastOne); + + collectionName = collectionNameAt(selectedIndex()); + if (m_selectedCollectionName == collectionName) + return; + + m_selectedCollectionName = collectionName; + emit selectedCollectionNameChanged(m_selectedCollectionName); } QString CollectionListModel::collectionNameAt(int idx) const @@ -180,17 +226,76 @@ QString CollectionListModel::collectionNameAt(int idx) const return index(idx).data(NameRole).toString(); } -void CollectionListModel::addCollection(const QString &collectionName) +QString CollectionListModel::selectedCollectionName() const { - if (m_data.contains(collectionName)) - return; + return m_selectedCollectionName; +} - int row = rowCount(); - beginInsertRows({}, row, row); - m_data.append(collectionName); - endInsertRows(); +void CollectionListModel::update() +{ + using Utils::FilePath; + using Utils::FileReader; + + FileReader sourceFile; + QString sourceFileAddress = CollectionEditorUtils::getSourceCollectionPath(m_dataStoreNode); + FilePath path = FilePath::fromUserInput(sourceFileAddress); + bool fileRead = false; + if (path.exists()) { + fileRead = sourceFile.fetch(path); + if (!fileRead) + emit this->warning(tr("Model Editor"), + tr("Cannot read the dataStore file\n%1").arg(sourceFile.errorString())); + } - emit collectionAdded(collectionName); + QStringList collectionNames; + if (fileRead) { + QJsonParseError parseError; + QJsonDocument document = QJsonDocument::fromJson(sourceFile.data(), &parseError); + if (parseError.error != QJsonParseError::NoError) { + emit this->warning(tr("Model Editor"), + tr("There is an error in the JSON file.\n%1") + .arg(parseError.errorString())); + } else { + if (document.isObject()) + collectionNames = document.object().toVariantMap().keys(); + else + emit this->warning(tr("Model Editor"), tr("The JSON document be an object.")); + } + } + + if (!sameCollectionNames(m_data, collectionNames)) { + QString prevSelectedCollection = selectedIndex() > -1 ? m_data.at(selectedIndex()) + : QString(); + beginResetModel(); + m_data = collectionNames; + endResetModel(); + emit this->collectionNamesChanged(collections()); + selectCollectionName(prevSelectedCollection, true); + } +} + +bool CollectionListModel::addCollection(const QString &collectionName, + const QJsonObject &localCollection) +{ + if (collectionExists(collectionName)) { + emit warning(tr("Add Model"), tr("Model \"%1\" already exists.").arg(collectionName)); + return false; + } + + QString errorMessage; + if (addCollectionToDataStore(collectionName, localCollection, errorMessage)) { + int row = rowCount(); + beginInsertRows({}, row, row); + m_data.append(collectionName); + endInsertRows(); + + selectCollectionName(collectionName); + emit collectionAdded(collectionName); + return true; + } else { + emit warning(tr("Add Collection"), errorMessage); + } + return false; } void CollectionListModel::setSelectedIndex(int idx) @@ -210,6 +315,187 @@ void CollectionListModel::setSelectedIndex(int idx) emit dataChanged(newIndex, newIndex, {SelectedRole}); emit selectedIndexChanged(idx); + updateSelectedCollectionName(); + } +} + +bool CollectionListModel::removeCollectionsFromDataStore(const QStringList &removedCollections, + QString &error) const +{ + using Utils::FilePath; + using Utils::FileReader; + auto setErrorAndReturn = [&error](const QString &msg) -> bool { + error = msg; + return false; + }; + + if (m_dataStoreNode.type() != CollectionEditorConstants::JSONCOLLECTIONMODEL_TYPENAME) + return setErrorAndReturn(tr("Invalid node type")); + + QString sourceFileAddress = CollectionEditorUtils::getSourceCollectionPath(m_dataStoreNode); + + QFileInfo sourceFileInfo(sourceFileAddress); + if (!sourceFileInfo.isFile()) + return setErrorAndReturn(tr("The selected node has an invalid source address")); + + FilePath jsonPath = FilePath::fromUserInput(sourceFileAddress); + FileReader jsonFile; + if (!jsonFile.fetch(jsonPath)) { + return setErrorAndReturn(tr("Can't read file \"%1\".\n%2") + .arg(sourceFileInfo.absoluteFilePath(), jsonFile.errorString())); + } + + QJsonParseError parseError; + QJsonDocument document = QJsonDocument::fromJson(jsonFile.data(), &parseError); + if (parseError.error != QJsonParseError::NoError) { + return setErrorAndReturn(tr("\"%1\" is corrupted.\n%2") + .arg(sourceFileInfo.absoluteFilePath(), parseError.errorString())); + } + + if (document.isObject()) { + QJsonObject rootObject = document.object(); + + for (const QString &collectionName : removedCollections) { + bool sourceContainsCollection = rootObject.contains(collectionName); + if (sourceContainsCollection) { + rootObject.remove(collectionName); + } else { + setErrorAndReturn(tr("The model group doesn't contain the model name (%1).") + .arg(sourceContainsCollection)); + } + } + + document.setObject(rootObject); + + if (CollectionEditorUtils::writeToJsonDocument(jsonPath, document)) { + error.clear(); + return true; + } else { + return setErrorAndReturn( + tr("Can't write to \"%1\".").arg(sourceFileInfo.absoluteFilePath())); + } + } else { + return setErrorAndReturn(tr("Local Json Document should be an object")); + } + + return false; +} + +bool CollectionListModel::renameCollectionInDataStore(const QString &oldName, + const QString &newName, + QString &error) +{ + using Utils::FilePath; + using Utils::FileReader; + using Utils::FileSaver; + + auto setErrorAndReturn = [&error](const QString &msg) -> bool { + error = msg; + return false; + }; + + if (m_dataStoreNode.type() != CollectionEditorConstants::JSONCOLLECTIONMODEL_TYPENAME) + return setErrorAndReturn(tr("Invalid node type")); + + QString sourceFileAddress = CollectionEditorUtils::getSourceCollectionPath(m_dataStoreNode); + + QFileInfo sourceFileInfo(sourceFileAddress); + if (!sourceFileInfo.isFile()) + return setErrorAndReturn(tr("Selected node must have a valid source file address")); + + FilePath jsonPath = FilePath::fromUserInput(sourceFileAddress); + FileReader jsonFile; + if (!jsonFile.fetch(jsonPath)) { + return setErrorAndReturn( + tr("Can't read \"%1\".\n%2").arg(sourceFileInfo.absoluteFilePath(), jsonFile.errorString())); + } + + QJsonParseError parseError; + QJsonDocument document = QJsonDocument::fromJson(jsonFile.data(), &parseError); + if (parseError.error != QJsonParseError::NoError) { + return setErrorAndReturn(tr("\"%1\" is corrupted.\n%2") + .arg(sourceFileInfo.absoluteFilePath(), parseError.errorString())); + } + + if (document.isObject()) { + QJsonObject rootObject = document.object(); + + bool collectionContainsOldName = rootObject.contains(oldName); + bool collectionContainsNewName = rootObject.contains(newName); + + if (!collectionContainsOldName) { + return setErrorAndReturn( + tr("The model group doesn't contain the old model name (%1).").arg(oldName)); + } + + if (collectionContainsNewName) { + return setErrorAndReturn( + tr("The model name \"%1\" already exists in the model group.").arg(newName)); + } + + QJsonValue oldValue = rootObject.value(oldName); + rootObject.insert(newName, oldValue); + rootObject.remove(oldName); + + document.setObject(rootObject); + + if (CollectionEditorUtils::writeToJsonDocument(jsonPath, document)) { + error.clear(); + return true; + } else { + return setErrorAndReturn( + tr("Can't write to \"%1\".").arg(sourceFileInfo.absoluteFilePath())); + } + } else { + return setErrorAndReturn(tr("Local Json Document should be an object")); + } + return false; +} + +bool CollectionListModel::addCollectionToDataStore(const QString &collectionName, + const QJsonObject &localCollection, + QString &errorString) const +{ + using Utils::FilePath; + using Utils::FileReader; + auto returnError = [&errorString](const QString &msg) -> bool { + errorString = msg; + return false; + }; + + if (collectionExists(collectionName)) + return returnError(tr("A model with the identical name already exists.")); + + QString sourceFileAddress = CollectionEditorUtils::getSourceCollectionPath(m_dataStoreNode); + + QFileInfo sourceFileInfo(sourceFileAddress); + if (!sourceFileInfo.isFile()) + return returnError(tr("Selected node must have a valid source file address")); + + FilePath jsonPath = FilePath::fromUserInput(sourceFileAddress); + FileReader jsonFile; + if (!jsonFile.fetch(jsonPath)) { + return returnError( + tr("Can't read \"%1\".\n%2").arg(sourceFileInfo.absoluteFilePath(), jsonFile.errorString())); + } + + QJsonParseError parseError; + QJsonDocument document = QJsonDocument::fromJson(jsonFile.data(), &parseError); + if (parseError.error != QJsonParseError::NoError) + return returnError(tr("\"%1\" is corrupted.\n%2") + .arg(sourceFileInfo.absoluteFilePath(), parseError.errorString())); + + if (document.isObject()) { + QJsonObject sourceObject = document.object(); + sourceObject.insert(collectionName, localCollection); + document.setObject(sourceObject); + + if (CollectionEditorUtils::writeToJsonDocument(jsonPath, document)) + return true; + else + return returnError(tr("Can't write to \"%1\".").arg(sourceFileInfo.absoluteFilePath())); + } else { + return returnError(tr("JSON document type should be an object containing models.")); } } @@ -225,4 +511,11 @@ void CollectionListModel::updateEmpty() } } +void CollectionListModel::updateSelectedCollectionName() +{ + QString selectedCollectionByIndex = collectionNameAt(selectedIndex()); + if (selectedCollectionByIndex != selectedCollectionName()) + selectCollectionName(selectedCollectionByIndex); +} + } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionlistmodel.h b/src/plugins/qmldesigner/components/collectioneditor/collectionlistmodel.h index 092dd1d6dc..7902fd5909 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionlistmodel.h +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionlistmodel.h @@ -16,49 +16,63 @@ class CollectionListModel : public QAbstractListModel Q_PROPERTY(int selectedIndex MEMBER m_selectedIndex NOTIFY selectedIndexChanged) Q_PROPERTY(bool isEmpty MEMBER m_isEmpty NOTIFY isEmptyChanged) - Q_PROPERTY(QString sourceType MEMBER m_sourceType CONSTANT) + Q_PROPERTY(QString selectedCollectionName + READ selectedCollectionName + WRITE selectCollectionName + NOTIFY selectedCollectionNameChanged) public: - enum Roles { IdRole = Qt::UserRole + 1, NameRole, SourceRole, SelectedRole, CollectionsRole }; + enum Roles { IdRole = Qt::UserRole + 1, NameRole, SelectedRole }; - explicit CollectionListModel(const ModelNode &sourceModel); + explicit CollectionListModel(); QHash<int, QByteArray> roleNames() const override; int rowCount(const QModelIndex &parent = QModelIndex()) const override; bool setData(const QModelIndex &index, const QVariant &value, int role) override; bool removeRows(int row, int count, const QModelIndex &parent = {}) override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; - void resetModelData(const QStringList &collectionsList); + void setDataStoreNode(const ModelNode &dataStoreNode = {}); Q_INVOKABLE int selectedIndex() const; Q_INVOKABLE ModelNode sourceNode() const; - Q_INVOKABLE QString sourceAddress() const; - Q_INVOKABLE bool contains(const QString &collectionName) const; + Q_INVOKABLE bool collectionExists(const QString &collectionName) const; Q_INVOKABLE QStringList collections() const; + Q_INVOKABLE QString getUniqueCollectionName(const QString &baseName = {}) const; void selectCollectionIndex(int idx, bool selectAtLeastOne = false); - void selectCollectionName(const QString &collectionName); + void selectCollectionName(QString collectionName, bool selectAtLeastOne = false); QString collectionNameAt(int idx) const; - void addCollection(const QString &collectionName); + QString selectedCollectionName() const; + + void update(); + bool addCollection(const QString &collectionName, const QJsonObject &localCollection); signals: void selectedIndexChanged(int idx); void isEmptyChanged(bool); void collectionNameChanged(const QString &oldName, const QString &newName); + void collectionNamesChanged(const QStringList &collectionNames); void collectionsRemoved(const QStringList &names); void collectionAdded(const QString &name); + void selectedCollectionNameChanged(const QString &selectedCollectionName); + void warning(const QString &title, const QString &body); private: void setSelectedIndex(int idx); + bool removeCollectionsFromDataStore(const QStringList &removedCollections, QString &error) const; + bool renameCollectionInDataStore(const QString &oldName, const QString &newName, QString &error); + bool addCollectionToDataStore(const QString &collectionName, + const QJsonObject &localCollection, + QString &errorString) const; void updateEmpty(); + void updateSelectedCollectionName(); using Super = QAbstractListModel; int m_selectedIndex = -1; bool m_isEmpty = false; - const ModelNode m_sourceNode; - const QString m_sourceType; - + ModelNode m_dataStoreNode; + QString m_selectedCollectionName; QStringList m_data; }; diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.cpp deleted file mode 100644 index 7760f46ff1..0000000000 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.cpp +++ /dev/null @@ -1,738 +0,0 @@ -// Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "collectionsourcemodel.h" - -#include "abstractview.h" -#include "collectioneditorconstants.h" -#include "collectioneditorutils.h" -#include "collectionlistmodel.h" -#include "variantproperty.h" - -#include <utils/fileutils.h> -#include <utils/qtcassert.h> -#include <qqml.h> - -#include <QFile> -#include <QFileInfo> -#include <QJsonArray> -#include <QJsonDocument> -#include <QJsonObject> -#include <QJsonParseError> - -namespace { - -QSharedPointer<QmlDesigner::CollectionListModel> loadCollection( - const QmlDesigner::ModelNode &sourceNode, - QSharedPointer<QmlDesigner::CollectionListModel> initialCollection = {}) -{ - using namespace QmlDesigner::CollectionEditorConstants; - using namespace QmlDesigner::CollectionEditorUtils; - using Utils::FilePath; - using Utils::FileReader; - QString sourceFileAddress = getSourceCollectionPath(sourceNode); - - QSharedPointer<QmlDesigner::CollectionListModel> collectionsList; - auto setupCollectionList = [&sourceNode, &initialCollection, &collectionsList]() { - if (initialCollection.isNull()) - collectionsList.reset(new QmlDesigner::CollectionListModel(sourceNode)); - else if (initialCollection->sourceNode() == sourceNode) - collectionsList = initialCollection; - else - collectionsList.reset(new QmlDesigner::CollectionListModel(sourceNode)); - }; - - if (sourceNode.type() == JSONCOLLECTIONMODEL_TYPENAME) { - FileReader sourceFile; - if (!sourceFile.fetch(FilePath::fromUserInput(sourceFileAddress))) - return {}; - - QJsonParseError parseError; - QJsonDocument document = QJsonDocument::fromJson(sourceFile.data(), &parseError); - if (parseError.error != QJsonParseError::NoError) - return {}; - - setupCollectionList(); - - if (document.isObject()) { - const QJsonObject sourceObject = document.object(); - collectionsList->resetModelData(sourceObject.toVariantMap().keys()); - } - } - - return collectionsList; -} - -} // namespace - -namespace QmlDesigner { - -CollectionSourceModel::CollectionSourceModel(QObject *parent) - : Super(parent) -{} - -int CollectionSourceModel::rowCount(const QModelIndex &) const -{ - return m_collectionSources.size(); -} - -QVariant CollectionSourceModel::data(const QModelIndex &index, int role) const -{ - QTC_ASSERT(index.isValid(), return {}); - - const ModelNode *collectionSource = &m_collectionSources.at(index.row()); - - switch (role) { - case NameRole: // Not used, to be removed - return collectionSource->variantProperty("objectName").value().toString(); - case NodeRole: - return QVariant::fromValue(*collectionSource); - case CollectionTypeRole: - return CollectionEditorUtils::getSourceCollectionType(*collectionSource); - case SourceRole: - return collectionSource->variantProperty(CollectionEditorConstants::SOURCEFILE_PROPERTY).value(); - case SelectedRole: - return index.row() == m_selectedIndex; - case CollectionsRole: - return QVariant::fromValue(m_collectionList.at(index.row()).data()); - } - - return {}; -} - -bool CollectionSourceModel::setData(const QModelIndex &index, const QVariant &value, int role) -{ - if (!index.isValid()) - return false; - - ModelNode collectionSource = m_collectionSources.at(index.row()); - switch (role) { - case Qt::DisplayRole: - case NameRole: { - auto collectionName = collectionSource.variantProperty("objectName"); - if (collectionName.value() == value) - return false; - - collectionName.setValue(value.toString()); - } break; - case SourceRole: { - auto sourceAddress = collectionSource.variantProperty( - CollectionEditorConstants::SOURCEFILE_PROPERTY); - if (sourceAddress.value() == value) - return false; - - sourceAddress.setValue(value.toString()); - } break; - case SelectedRole: { - if (value.toBool() != index.data(SelectedRole).toBool()) - setSelectedIndex(value.toBool() ? index.row() : -1); - else - return false; - } break; - default: - return false; - } - - return true; -} - -bool CollectionSourceModel::removeRows(int row, int count, [[maybe_unused]] const QModelIndex &parent) -{ - const int rowMax = std::min(row + count, rowCount()); - - if (row >= rowMax || row < 0) - return false; - - AbstractView *view = m_collectionSources.at(row).view(); - if (!view) - return false; - - count = rowMax - row; - - bool selectionUpdateNeeded = m_selectedIndex >= row && m_selectedIndex < rowMax; - - // It's better to remove the group of nodes here because of the performance issue for the list, - // and update issue for the view - beginRemoveRows({}, row, rowMax - 1); - - view->executeInTransaction(Q_FUNC_INFO, [row, count, this]() { - for (ModelNode node : Utils::span<const ModelNode>(m_collectionSources).subspan(row, count)) { - m_sourceIndexHash.remove(node.internalId()); - node.destroy(); - } - m_collectionSources.remove(row, count); - m_collectionList.remove(row, count); - }); - - int idx = row; - for (const ModelNode &node : Utils::span<const ModelNode>(m_collectionSources).subspan(row)) - m_sourceIndexHash.insert(node.internalId(), ++idx); - - endRemoveRows(); - - if (selectionUpdateNeeded) - updateSelectedSource(); - - updateEmpty(); - return true; -} - -QHash<int, QByteArray> CollectionSourceModel::roleNames() const -{ - static QHash<int, QByteArray> roles; - if (roles.isEmpty()) { - roles.insert(Super::roleNames()); - roles.insert({{NameRole, "sourceName"}, - {NodeRole, "sourceNode"}, - {CollectionTypeRole, "sourceCollectionType"}, - {SelectedRole, "sourceIsSelected"}, - {SourceRole, "sourceAddress"}, - {CollectionsRole, "internalModels"}}); - } - return roles; -} - -void CollectionSourceModel::setSource(const ModelNode &source) -{ - beginResetModel(); - m_collectionSources = {source}; - m_sourceIndexHash.clear(); - m_collectionList.clear(); - - // TODO: change m_collectionSources to only contain 1 source node - m_sourceIndexHash.insert(source.internalId(), 0); - - auto loadedCollection = loadCollection(source); - m_collectionList.append(loadedCollection); - - registerCollectionList(loadedCollection); - - - updateEmpty(); - endResetModel(); - - updateSelectedSource(true); -} - -void CollectionSourceModel::reset() -{ - beginResetModel(); - m_collectionSources.clear(); - m_sourceIndexHash.clear(); - m_collectionList.clear(); - m_previousSelectedList.clear(); - setSelectedCollectionName({}); - - updateEmpty(); - endResetModel(); - updateSelectedSource(); -} - -int CollectionSourceModel::sourceIndex(const ModelNode &node) const -{ - return m_sourceIndexHash.value(node.internalId(), -1); -} - -void CollectionSourceModel::addSource(const ModelNode &node) -{ - int newRowId = m_collectionSources.count(); - beginInsertRows({}, newRowId, newRowId); - m_collectionSources.append(node); - m_sourceIndexHash.insert(node.internalId(), newRowId); - - auto loadedCollection = loadCollection(node); - m_collectionList.append(loadedCollection); - - registerCollectionList(loadedCollection); - - updateEmpty(); - endInsertRows(); - updateSelectedSource(true); -} - -void CollectionSourceModel::selectSource(const ModelNode &node) -{ - int nodePlace = m_sourceIndexHash.value(node.internalId(), -1); - if (nodePlace < 0) - return; - - selectSourceIndex(nodePlace, true); -} - -bool CollectionSourceModel::collectionExists(const QString &collectionName) const -{ - return m_collectionList.size() == 1 && m_collectionList.at(0)->contains(collectionName); -} - -bool CollectionSourceModel::addCollectionToSource(const ModelNode &node, - const QString &collectionName, - const QJsonObject &newCollection, - QString *errorString) -{ - using Utils::FilePath; - using Utils::FileReader; - 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 models.")); - - if (node.type() != CollectionEditorConstants::JSONCOLLECTIONMODEL_TYPENAME) - return returnError(tr("Node should be a JSON model.")); - - if (collectionExists(collectionName)) - return returnError(tr("A model with the identical name already exists.")); - - QString sourceFileAddress = CollectionEditorUtils::getSourceCollectionPath(node); - - QFileInfo sourceFileInfo(sourceFileAddress); - if (!sourceFileInfo.isFile()) - return returnError(tr("Selected node must have a valid source file address")); - - FilePath jsonPath = FilePath::fromUserInput(sourceFileAddress); - FileReader jsonFile; - if (!jsonFile.fetch(jsonPath)) { - return returnError( - tr("Can't read \"%1\".\n%2").arg(sourceFileInfo.absoluteFilePath(), jsonFile.errorString())); - } - - QJsonParseError parseError; - QJsonDocument document = QJsonDocument::fromJson(jsonFile.data(), &parseError); - if (parseError.error != QJsonParseError::NoError) - return returnError(tr("\"%1\" is corrupted.\n%2") - .arg(sourceFileInfo.absoluteFilePath(), parseError.errorString())); - - if (document.isObject()) { - QJsonObject sourceObject = document.object(); - sourceObject.insert(collectionName, newCollection); - document.setObject(sourceObject); - - if (!CollectionEditorUtils::writeToJsonDocument(jsonPath, document)) - return returnError(tr("Can't write to \"%1\".").arg(sourceFileInfo.absoluteFilePath())); - - updateCollectionList(index(idx)); - - auto collections = m_collectionList.at(idx); - if (collections.isNull()) - return returnError(tr("No model is available for the JSON model group.")); - - collections->selectCollectionName(collectionName); - setSelectedCollectionName(collectionName); - return true; - } else { - return returnError(tr("JSON document type should be an object containing models.")); - } -} - -QmlDesigner::ModelNode CollectionSourceModel::sourceNodeAt(int idx) -{ - QModelIndex data = index(idx); - if (!data.isValid()) - return {}; - - return m_collectionSources.at(idx); -} - -CollectionListModel *CollectionSourceModel::selectedCollectionList() -{ - QModelIndex idx = index(m_selectedIndex); - if (!idx.isValid()) - return {}; - - return idx.data(CollectionsRole).value<CollectionListModel *>(); -} - -void CollectionSourceModel::selectSourceIndex(int idx, bool selectAtLeastOne) -{ - int collectionCount = m_collectionSources.size(); - int preferredIndex = -1; - if (collectionCount) { - if (selectAtLeastOne) - preferredIndex = std::max(0, std::min(idx, collectionCount - 1)); - else if (idx > -1 && idx < collectionCount) - preferredIndex = idx; - } - - setSelectedIndex(preferredIndex); -} - -void CollectionSourceModel::selectCollection(const QVariant &node, const QString &collectionName) -{ - const ModelNode sourceNode = node.value<ModelNode>(); - const QModelIndex index = indexOfNode(sourceNode); - if (!index.isValid()) - return; - - selectSource(sourceNode); - auto collections = m_collectionList.at(index.row()); - if (collections.isNull()) - return; - - collections->selectCollectionName(collectionName); -} - -void CollectionSourceModel::deselect() -{ - setSelectedIndex(-1); -} - -void CollectionSourceModel::updateSelectedSource(bool selectAtLeastOne) -{ - int idx = m_selectedIndex; - m_selectedIndex = -1; - selectSourceIndex(idx, selectAtLeastOne); -} - -QString CollectionSourceModel::getUniqueCollectionName(const QString &baseName) const -{ - if (m_collectionList.isEmpty()) - return "Model01"; - - CollectionListModel *collectionModel = m_collectionList.at(0).data(); - - QString name = baseName.isEmpty() ? "Model" : baseName; - QString nameTemplate = name + "%1"; - - int num = 0; - - while (collectionModel->contains(name)) - name = nameTemplate.arg(++num, 2, 10, QChar('0')); - - return name; -} - -void CollectionSourceModel::updateNodeName(const ModelNode &node) -{ - QModelIndex index = indexOfNode(node); - emit dataChanged(index, index, {NameRole, Qt::DisplayRole}); - updateCollectionList(index); -} - -void CollectionSourceModel::updateNodeSource(const ModelNode &node) -{ - QModelIndex index = indexOfNode(node); - emit dataChanged(index, index, {SourceRole}); - updateCollectionList(index); -} - -void CollectionSourceModel::onSelectedCollectionChanged(CollectionListModel *collectionList, - int collectionIndex) -{ - if (collectionIndex > -1) { - if (m_previousSelectedList && m_previousSelectedList != collectionList) - m_previousSelectedList->selectCollectionIndex(-1); - - m_previousSelectedList = collectionList; - - setSelectedCollectionName(collectionList->collectionNameAt(collectionIndex)); - - selectSourceIndex(sourceIndex(collectionList->sourceNode())); - } else { - setSelectedCollectionName({}); - } -} - -void CollectionSourceModel::onCollectionNameChanged(CollectionListModel *collectionList, - const QString &oldName, - const QString &newName) -{ - using Utils::FilePath; - using Utils::FileReader; - using Utils::FileSaver; - - auto emitRenameWarning = [this](const QString &msg) -> void { - emit warning(tr("Rename Model"), msg); - }; - - const ModelNode node = collectionList->sourceNode(); - const QModelIndex nodeIndex = indexOfNode(node); - - if (!nodeIndex.isValid()) { - emitRenameWarning(tr("Invalid node")); - return; - } - - if (node.type() != CollectionEditorConstants::JSONCOLLECTIONMODEL_TYPENAME) { - emitRenameWarning(tr("Invalid node type")); - return; - } - - QString sourceFileAddress = CollectionEditorUtils::getSourceCollectionPath(node); - - QFileInfo sourceFileInfo(sourceFileAddress); - if (!sourceFileInfo.isFile()) { - emitRenameWarning(tr("Selected node must have a valid source file address")); - return; - } - - FilePath jsonPath = FilePath::fromUserInput(sourceFileAddress); - FileReader jsonFile; - if (!jsonFile.fetch(jsonPath)) { - emitRenameWarning( - tr("Can't read \"%1\".\n%2").arg(sourceFileInfo.absoluteFilePath(), jsonFile.errorString())); - return; - } - - QJsonParseError parseError; - QJsonDocument document = QJsonDocument::fromJson(jsonFile.data(), &parseError); - if (parseError.error != QJsonParseError::NoError) { - emitRenameWarning(tr("\"%1\" is corrupted.\n%2") - .arg(sourceFileInfo.absoluteFilePath(), parseError.errorString())); - return; - } - - if (document.isObject()) { - QJsonObject rootObject = document.object(); - - bool collectionContainsOldName = rootObject.contains(oldName); - bool collectionContainsNewName = rootObject.contains(newName); - - if (!collectionContainsOldName) { - emitRenameWarning( - tr("The model group doesn't contain the old model name (%1).").arg(oldName)); - return; - } - - if (collectionContainsNewName) { - emitRenameWarning( - tr("The model name \"%1\" already exists in the model group.").arg(newName)); - return; - } - - QJsonValue oldValue = rootObject.value(oldName); - rootObject.insert(newName, oldValue); - rootObject.remove(oldName); - - document.setObject(rootObject); - - if (!CollectionEditorUtils::writeToJsonDocument(jsonPath, document)) { - emitRenameWarning(tr("Can't write to \"%1\".").arg(sourceFileInfo.absoluteFilePath())); - return; - } - - CollectionListModel *list = m_collectionList.at(nodeIndex.row()).data(); - bool updateSelectedNames = list && list == m_previousSelectedList.data(); - emit collectionRenamed(oldName, newName); - updateCollectionList(nodeIndex); - - if (updateSelectedNames) { - list = m_collectionList.at(nodeIndex.row()).data(); - if (m_selectedCollectionName == oldName) { - list->selectCollectionName(newName); - setSelectedCollectionName(newName); - } else { - // reselect to update ID if it's changed due to renaming and order changes - list->selectCollectionName(m_selectedCollectionName); - } - } - } -} - -void CollectionSourceModel::onCollectionsRemoved(CollectionListModel *collectionList, - const QStringList &removedCollections) -{ - using Utils::FilePath; - using Utils::FileReader; - auto emitDeleteWarning = [this](const QString &msg) -> void { - emit warning(tr("Delete Model"), msg); - }; - - const ModelNode node = collectionList->sourceNode(); - const QModelIndex nodeIndex = indexOfNode(node); - - if (!nodeIndex.isValid()) { - emitDeleteWarning(tr("Invalid node")); - return; - } - - if (node.type() != CollectionEditorConstants::JSONCOLLECTIONMODEL_TYPENAME) { - emitDeleteWarning(tr("Invalid node type")); - return; - } - - QString sourceFileAddress = CollectionEditorUtils::getSourceCollectionPath(node); - - QFileInfo sourceFileInfo(sourceFileAddress); - if (!sourceFileInfo.isFile()) { - emitDeleteWarning(tr("The selected node has an invalid source address")); - return; - } - - FilePath jsonPath = FilePath::fromUserInput(sourceFileAddress); - FileReader jsonFile; - if (!jsonFile.fetch(jsonPath)) { - emitDeleteWarning(tr("Can't read or write \"%1\".\n%2") - .arg(sourceFileInfo.absoluteFilePath(), jsonFile.errorString())); - return; - } - - QJsonParseError parseError; - QJsonDocument document = QJsonDocument::fromJson(jsonFile.data(), &parseError); - if (parseError.error != QJsonParseError::NoError) { - emitDeleteWarning(tr("\"%1\" is corrupted.\n%2") - .arg(sourceFileInfo.absoluteFilePath(), parseError.errorString())); - return; - } - - if (document.isObject()) { - QJsonObject rootObject = document.object(); - - QStringList collectionsRemovedFromDocument; - for (const QString &collectionName : removedCollections) { - bool sourceContainsCollection = rootObject.contains(collectionName); - if (sourceContainsCollection) { - rootObject.remove(collectionName); - collectionsRemovedFromDocument << collectionName; - } else { - emitDeleteWarning(tr("The model group doesn't contain the model name (%1).") - .arg(sourceContainsCollection)); - } - } - - document.setObject(rootObject); - - if (!CollectionEditorUtils::writeToJsonDocument(jsonPath, document)) { - emitDeleteWarning(tr("Can't write to \"%1\".").arg(sourceFileInfo.absoluteFilePath())); - return; - } - - for (const QString &collectionName : std::as_const(collectionsRemovedFromDocument)) - emit collectionRemoved(collectionName); - - updateCollectionList(nodeIndex); - if (m_previousSelectedList == collectionList) - onSelectedCollectionChanged(collectionList, collectionList->selectedIndex()); - } -} - -void CollectionSourceModel::setSelectedIndex(int idx) -{ - idx = (idx > -1 && idx < m_collectionSources.count()) ? idx : -1; - - if (m_selectedIndex != idx) { - QModelIndex previousIndex = index(m_selectedIndex); - QModelIndex newIndex = index(idx); - - m_selectedIndex = idx; - - if (previousIndex.isValid()) - emit dataChanged(previousIndex, previousIndex, {SelectedRole}); - - if (newIndex.isValid()) - emit dataChanged(newIndex, newIndex, {SelectedRole}); - - emit selectedIndexChanged(idx); - - if (idx > -1) { - QPointer<CollectionListModel> relatedCollectionList = m_collectionList.at(idx).data(); - if (relatedCollectionList) { - if (relatedCollectionList->selectedIndex() < 0) - relatedCollectionList->selectCollectionIndex(0, true); - } else if (m_previousSelectedList) { - m_previousSelectedList->selectCollectionIndex(-1); - m_previousSelectedList = {}; - setSelectedCollectionName({}); - } - } - } -} - -void CollectionSourceModel::setSelectedCollectionName(const QString &collectionName) -{ - if (m_selectedCollectionName != collectionName) { - m_selectedCollectionName = collectionName; - emit collectionSelected(m_selectedCollectionName); - } -} - -void CollectionSourceModel::updateEmpty() -{ - bool isEmptyNow = m_collectionSources.isEmpty(); - if (m_isEmpty != isEmptyNow) { - m_isEmpty = isEmptyNow; - emit isEmptyChanged(m_isEmpty); - - if (m_isEmpty) - setSelectedIndex(-1); - } -} - -void CollectionSourceModel::updateCollectionList(QModelIndex index) -{ - if (!index.isValid()) - return; - - ModelNode sourceNode = sourceNodeAt(index.row()); - QSharedPointer<CollectionListModel> oldList = m_collectionList.at(index.row()); - QSharedPointer<CollectionListModel> newList = loadCollection(sourceNode, oldList); - if (oldList != newList) { - m_collectionList.replace(index.row(), newList); - emit dataChanged(index, index, {CollectionsRole}); - registerCollectionList(newList); - } -} - -void CollectionSourceModel::registerCollectionList( - const QSharedPointer<CollectionListModel> &sharedCollectionList) -{ - CollectionListModel *collectionList = sharedCollectionList.data(); - if (collectionList == nullptr) - return; - - if (!collectionList->property("_is_registered_in_sourceModel").toBool()) { - collectionList->setProperty("_is_registered_in_sourceModel", true); - - connect(collectionList, - &CollectionListModel::selectedIndexChanged, - this, - [this, collectionList](int idx) { onSelectedCollectionChanged(collectionList, idx); }); - - connect(collectionList, - &CollectionListModel::collectionNameChanged, - this, - [this, collectionList](const QString &oldName, const QString &newName) { - onCollectionNameChanged(collectionList, oldName, newName); - }); - - connect(collectionList, - &CollectionListModel::collectionsRemoved, - this, - [this, collectionList](const QStringList &removedCollections) { - onCollectionsRemoved(collectionList, removedCollections); - }); - - connect(collectionList, &CollectionListModel::modelReset, this, [this, collectionList]() { - emit collectionNamesInitialized(collectionList->collections()); - }); - } - - if (collectionList->sourceNode().isValid()) - emit collectionNamesInitialized(collectionList->collections()); -} - -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 diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.h b/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.h deleted file mode 100644 index f988935c98..0000000000 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.h +++ /dev/null @@ -1,119 +0,0 @@ -// Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0 - -#pragma once - -#include "modelnode.h" - -#include <QAbstractListModel> -#include <QHash> -#include <QSortFilterProxyModel> - -namespace QmlDesigner { - -class CollectionJsonSourceFilterModel; -class CollectionListModel; - -class CollectionSourceModel : public QAbstractListModel -{ - Q_OBJECT - - Q_PROPERTY(int selectedIndex MEMBER m_selectedIndex NOTIFY selectedIndexChanged) - Q_PROPERTY(bool isEmpty MEMBER m_isEmpty NOTIFY isEmptyChanged) - -public: - enum Roles { - NameRole = Qt::UserRole + 1, - NodeRole, - CollectionTypeRole, - SourceRole, - SelectedRole, - CollectionsRole - }; - - 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; - virtual bool setData(const QModelIndex &index, - const QVariant &value, - int role = Qt::EditRole) override; - - Q_INVOKABLE virtual bool removeRows(int row, - int count = 1, - const QModelIndex &parent = QModelIndex()) override; - - virtual QHash<int, QByteArray> roleNames() const override; - - void setSource(const ModelNode &source); - void reset(); - int sourceIndex(const ModelNode &node) const; - void addSource(const ModelNode &node); - void selectSource(const ModelNode &node); - - bool addCollectionToSource(const ModelNode &node, - const QString &collectionName, - const QJsonObject &newCollection, - QString *errorString = nullptr); - - ModelNode sourceNodeAt(int idx); - CollectionListModel *selectedCollectionList(); - - void updateNodeName(const ModelNode &node); - void updateNodeSource(const ModelNode &node); - - Q_INVOKABLE void selectSourceIndex(int idx, bool selectAtLeastOne = false); - Q_INVOKABLE void selectCollection(const QVariant &node, const QString &collectionName); - Q_INVOKABLE void deselect(); - Q_INVOKABLE void updateSelectedSource(bool selectAtLeastOne = false); - Q_INVOKABLE QString getUniqueCollectionName(const QString &baseName = {}) const; - Q_INVOKABLE bool collectionExists(const QString &collectionName) const; - -signals: - void selectedIndexChanged(int idx); - void collectionSelected(const QString &collectionName); - void collectionNamesInitialized(const QStringList &initialList); - void collectionRenamed(const QString &oldname, const QString &newName); - void collectionRemoved(const QString &collectionName); - - void isEmptyChanged(bool); - void warning(const QString &title, const QString &body); - -private slots: - void onSelectedCollectionChanged(CollectionListModel *collectionList, int collectionIndex); - void onCollectionNameChanged(CollectionListModel *collectionList, const QString &oldName, - const QString &newName); - void onCollectionsRemoved(CollectionListModel *collectionList, - const QStringList &removedCollections); - -private: - void setSelectedIndex(int idx); - void setSelectedCollectionName(const QString &collectionName); - void updateEmpty(); - void updateCollectionList(QModelIndex index); - void registerCollectionList(const QSharedPointer<CollectionListModel> &collectionList); - QModelIndex indexOfNode(const ModelNode &node) const; - - using Super = QAbstractListModel; - - ModelNodes m_collectionSources; - QHash<qint32, int> m_sourceIndexHash; // internalId -> index - QList<QSharedPointer<CollectionListModel>> m_collectionList; - QPointer<CollectionListModel> m_previousSelectedList; - QString m_selectedCollectionName; - int m_selectedIndex = -1; - 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 diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp index 442a79cbd5..723d0beca8 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp @@ -6,7 +6,7 @@ #include "collectiondetailsmodel.h" #include "collectioneditorconstants.h" #include "collectioneditorutils.h" -#include "collectionsourcemodel.h" +#include "collectionlistmodel.h" #include "collectionwidget.h" #include "datastoremodelnode.h" #include "designmodecontext.h" @@ -81,29 +81,33 @@ QmlDesigner::WidgetInfo CollectionView::widgetInfo() auto collectionEditorContext = new Internal::CollectionEditorContext(m_widget.data()); Core::ICore::addContextObject(collectionEditorContext); - CollectionSourceModel *sourceModel = m_widget->sourceModel().data(); + CollectionListModel *listModel = m_widget->listModel().data(); - connect(sourceModel, - &CollectionSourceModel::collectionSelected, + connect(listModel, + &CollectionListModel::selectedCollectionNameChanged, this, [this](const QString &collection) { m_widget->collectionDetailsModel()->loadCollection(dataStoreNode(), collection); }); - connect(sourceModel, &CollectionSourceModel::isEmptyChanged, this, [this](bool isEmpty) { + connect(listModel, &CollectionListModel::isEmptyChanged, this, [this](bool isEmpty) { if (isEmpty) m_widget->collectionDetailsModel()->loadCollection({}, {}); }); - connect(sourceModel, - &CollectionSourceModel::collectionNamesInitialized, + connect(listModel, &CollectionListModel::modelReset, this, [this] { + CollectionListModel *listModel = m_widget->listModel().data(); + if (listModel->sourceNode() == m_dataStore->modelNode()) + m_dataStore->setCollectionNames(listModel->collections()); + }); + + connect(listModel, + &CollectionListModel::collectionAdded, this, - [this](const QStringList &collectionNames) { - m_dataStore->setCollectionNames(collectionNames); - }); + [this](const QString &collectionName) { m_dataStore->addCollection(collectionName); }); - connect(sourceModel, - &CollectionSourceModel::collectionRenamed, + connect(listModel, + &CollectionListModel::collectionNameChanged, this, [this](const QString &oldName, const QString &newName) { m_dataStore->renameCollection(oldName, newName); @@ -112,13 +116,15 @@ QmlDesigner::WidgetInfo CollectionView::widgetInfo() newName); }); - connect(sourceModel, - &CollectionSourceModel::collectionRemoved, + connect(listModel, + &CollectionListModel::collectionsRemoved, this, - [this](const QString &collectionName) { - m_dataStore->removeCollection(collectionName); - m_widget->collectionDetailsModel()->removeCollection(dataStoreNode(), - collectionName); + [this](const QStringList &collectionNames) { + m_dataStore->removeCollections(collectionNames); + for (const QString &collectionName : collectionNames) { + m_widget->collectionDetailsModel()->removeCollection(dataStoreNode(), + collectionName); + } }); } @@ -144,29 +150,7 @@ void CollectionView::modelAboutToBeDetached([[maybe_unused]] Model *model) m_dataStoreTypeFound = false; disconnect(m_documentUpdateConnection); QTC_ASSERT(m_delayedTasks.isEmpty(), m_delayedTasks.clear()); - m_widget->sourceModel()->reset(); -} - -void CollectionView::nodeRemoved(const ModelNode &removedNode, - [[maybe_unused]] const NodeAbstractProperty &parentProperty, - [[maybe_unused]] PropertyChangeFlags propertyChange) -{ - if (isStudioCollectionModel(removedNode)) - m_widget->sourceModel()->updateSelectedSource(true); -} - -void CollectionView::variantPropertiesChanged(const QList<VariantProperty> &propertyList, - [[maybe_unused]] PropertyChangeFlags propertyChange) -{ - for (const VariantProperty &property : propertyList) { - ModelNode node(property.parentModelNode()); - if (isStudioCollectionModel(node)) { - if (property.name() == "objectName") - m_widget->sourceModel()->updateNodeName(node); - else if (property.name() == CollectionEditorConstants::SOURCEFILE_PROPERTY) - m_widget->sourceModel()->updateNodeSource(node); - } - } + m_widget->listModel()->setDataStoreNode(); } void CollectionView::selectedNodesChanged(const QList<ModelNode> &selectedNodeList, @@ -190,11 +174,6 @@ void CollectionView::selectedNodesChanged(const QList<ModelNode> &selectedNodeLi // More than one model is selected. So ignore them if (selectedCollectionNodes.size() > 1) return; - - if (selectedCollectionNodes.size() == 1) { // If exactly one model is selected - m_widget->sourceModel()->selectSource(selectedCollectionNodes.first()); - return; - } } void CollectionView::customNotification(const AbstractView *, @@ -298,7 +277,7 @@ void CollectionView::assignCollectionToSelectedNode(const QString &collectionNam void CollectionView::addNewCollection(const QString &collectionName, const QJsonObject &localCollection) { addTask(QSharedPointer<CollectionTask>( - new AddCollectionTask(this, m_widget->sourceModel(), localCollection, collectionName))); + new AddCollectionTask(this, m_widget->listModel(), localCollection, collectionName))); } void CollectionView::openCollection(const QString &collectionName) @@ -309,7 +288,6 @@ void CollectionView::openCollection(const QString &collectionName) void CollectionView::registerDeclarativeType() { CollectionDetails::registerDeclarativeType(); - CollectionJsonSourceFilterModel::registerDeclarativeType(); } void CollectionView::resetDataStoreNode() @@ -317,7 +295,7 @@ void CollectionView::resetDataStoreNode() m_dataStore->reloadModel(); ModelNode dataStore = m_dataStore->modelNode(); - if (!dataStore || m_widget->sourceModel()->sourceIndex(dataStore) > -1) + if (!dataStore || m_widget->listModel()->sourceNode() == dataStore) return; bool dataStoreSingletonFound = m_dataStoreTypeFound; @@ -336,7 +314,7 @@ void CollectionView::resetDataStoreNode() } if (dataStoreSingletonFound) { - m_widget->sourceModel()->setSource(dataStore); + m_widget->listModel()->setDataStoreNode(dataStore); m_dataStoreTypeFound = true; while (!m_delayedTasks.isEmpty()) @@ -415,7 +393,7 @@ void CollectionView::onItemLibraryNodeCreated(const ModelNode &node) { if (node.metaInfo().isQtQuickListView()) { addTask(QSharedPointer<CollectionTask>( - new DropListViewTask(this, m_widget->sourceModel(), node))); + new DropListViewTask(this, m_widget->listModel(), node))); } } @@ -439,61 +417,49 @@ void CollectionView::addTask(QSharedPointer<CollectionTask> task) m_delayedTasks << task; } -CollectionTask::CollectionTask(CollectionView *view, CollectionSourceModel *sourceModel) +CollectionTask::CollectionTask(CollectionView *view, CollectionListModel *listModel) : m_collectionView(view) - , m_sourceModel(sourceModel) + , m_listModel(listModel) {} DropListViewTask::DropListViewTask(CollectionView *view, - CollectionSourceModel *sourceModel, + CollectionListModel *listModel, const ModelNode &node) - : CollectionTask(view, sourceModel) + : CollectionTask(view, listModel) , m_node(node) {} void DropListViewTask::process() { AbstractView *view = m_node.view(); - if (!m_node || !m_collectionView || !m_sourceModel || !view) + if (!m_node || !m_collectionView || !m_listModel || !view) return; - const QString newCollectionName = m_sourceModel->getUniqueCollectionName("ListModel"); - m_sourceModel->addCollectionToSource(m_collectionView->dataStoreNode(), - newCollectionName, - CollectionEditorUtils::defaultColorCollection()); + const QString newCollectionName = m_listModel->getUniqueCollectionName("ListModel"); + m_listModel->addCollection(newCollectionName, CollectionEditorUtils::defaultColorCollection()); m_collectionView->openCollection(newCollectionName); m_collectionView->assignCollectionToNode(newCollectionName, m_node); } AddCollectionTask::AddCollectionTask(CollectionView *view, - CollectionSourceModel *sourceModel, + CollectionListModel *listModel, const QJsonObject &localJsonObject, const QString &collectionName) - : CollectionTask(view, sourceModel) + : CollectionTask(view, listModel) , m_localJsonObject(localJsonObject) , m_name(collectionName) {} void AddCollectionTask::process() { - if (!m_sourceModel) + if (!m_listModel) return; - QString errorMsg; - - const QString newCollectionName = m_sourceModel->collectionExists(m_name) - ? m_sourceModel->getUniqueCollectionName(m_name) + const QString newCollectionName = m_listModel->collectionExists(m_name) + ? m_listModel->getUniqueCollectionName(m_name) : m_name; - bool added = m_sourceModel->addCollectionToSource(m_collectionView->dataStoreNode(), - newCollectionName, - m_localJsonObject, - &errorMsg); - - if (!added) { - emit m_sourceModel->warning(m_sourceModel->tr("Can not add a model to the JSON file"), - errorMsg); - } + m_listModel->addCollection(newCollectionName, m_localJsonObject); } } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionview.h b/src/plugins/qmldesigner/components/collectioneditor/collectionview.h index 5606ddc6b4..a4b16c4c27 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionview.h +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionview.h @@ -16,7 +16,7 @@ class Document; namespace QmlDesigner { class CollectionDetails; -class CollectionSourceModel; +class CollectionListModel; class CollectionTask; class CollectionWidget; class DataStoreModelNode; @@ -34,13 +34,6 @@ public: void modelAttached(Model *model) override; void modelAboutToBeDetached(Model *model) override; - void nodeRemoved(const ModelNode &removedNode, - const NodeAbstractProperty &parentProperty, - PropertyChangeFlags propertyChange) override; - - void variantPropertiesChanged(const QList<VariantProperty> &propertyList, - PropertyChangeFlags propertyChange) override; - void selectedNodesChanged(const QList<ModelNode> &selectedNodeList, const QList<ModelNode> &lastSelectedNodeList) override; @@ -87,7 +80,7 @@ private: class CollectionTask { public: - CollectionTask(CollectionView *view, CollectionSourceModel *sourceModel); + CollectionTask(CollectionView *view, CollectionListModel *listModel); CollectionTask() = delete; virtual ~CollectionTask() = default; @@ -95,13 +88,13 @@ public: protected: QPointer<CollectionView> m_collectionView; - QPointer<CollectionSourceModel> m_sourceModel; + QPointer<CollectionListModel> m_listModel; }; class DropListViewTask : public CollectionTask { public: - DropListViewTask(CollectionView *view, CollectionSourceModel *sourceModel, const ModelNode &node); + DropListViewTask(CollectionView *view, CollectionListModel *listModel, const ModelNode &node); void process() override; @@ -113,7 +106,7 @@ class AddCollectionTask : public CollectionTask { public: AddCollectionTask(CollectionView *view, - CollectionSourceModel *sourceModel, + CollectionListModel *listModel, const QJsonObject &localJsonObject, const QString &collectionName); diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp index 3e1b2e0129..093729dc67 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp @@ -7,7 +7,7 @@ #include "collectiondetailsmodel.h" #include "collectiondetailssortfiltermodel.h" #include "collectioneditorutils.h" -#include "collectionsourcemodel.h" +#include "collectionlistmodel.h" #include "collectionview.h" #include "designmodewidget.h" #include "qmldesignerconstants.h" @@ -56,7 +56,7 @@ namespace QmlDesigner { CollectionWidget::CollectionWidget(CollectionView *view) : QFrame() , m_view(view) - , m_sourceModel(new CollectionSourceModel) + , m_listModel(new CollectionListModel) , m_collectionDetailsModel(new CollectionDetailsModel) , m_collectionDetailsSortFilterModel(std::make_unique<CollectionDetailsSortFilterModel>()) , m_quickWidget(new StudioQuickWidget(this)) @@ -69,7 +69,7 @@ CollectionWidget::CollectionWidget(CollectionView *view) icontext->setContext(context); icontext->setWidget(this); - connect(m_sourceModel, &CollectionSourceModel::warning, this, &CollectionWidget::warn); + connect(m_listModel, &CollectionListModel::warning, this, &CollectionWidget::warn); m_collectionDetailsSortFilterModel->setSourceModel(m_collectionDetailsModel); @@ -90,7 +90,7 @@ CollectionWidget::CollectionWidget(CollectionView *view) auto map = m_quickWidget->registerPropertyMap("CollectionEditorBackend"); map->setProperties({ {"rootView", QVariant::fromValue(this)}, - {"model", QVariant::fromValue(m_sourceModel.data())}, + {"model", QVariant::fromValue(m_listModel.data())}, {"collectionDetailsModel", QVariant::fromValue(m_collectionDetailsModel.data())}, {"collectionDetailsSortFilterModel", QVariant::fromValue(m_collectionDetailsSortFilterModel.get())}, @@ -112,9 +112,9 @@ void CollectionWidget::contextHelp(const Core::IContext::HelpCallback &callback) callback({}); } -QPointer<CollectionSourceModel> CollectionWidget::sourceModel() const +QPointer<CollectionListModel> CollectionWidget::listModel() const { - return m_sourceModel; + return m_listModel; } QPointer<CollectionDetailsModel> CollectionWidget::collectionDetailsModel() const @@ -262,7 +262,7 @@ void CollectionWidget::assignCollectionToSelectedNode(const QString collectionNa void CollectionWidget::openCollection(const QString &collectionName) { - m_sourceModel->selectCollection(QVariant::fromValue(m_view->dataStoreNode()), collectionName); + m_listModel->selectCollectionName(collectionName); QmlDesignerPlugin::instance()->mainWidget()->showDockWidget("CollectionEditor", true); } diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.h b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.h index a3218bd1bc..0957bd81e0 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.h +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.h @@ -13,7 +13,7 @@ namespace QmlDesigner { class CollectionDetailsModel; class CollectionDetailsSortFilterModel; -class CollectionSourceModel; +class CollectionListModel; class CollectionView; class ModelNode; @@ -27,7 +27,7 @@ public: CollectionWidget(CollectionView *view); void contextHelp(const Core::IContext::HelpCallback &callback) const; - QPointer<CollectionSourceModel> sourceModel() const; + QPointer<CollectionListModel> listModel() const; QPointer<CollectionDetailsModel> collectionDetailsModel() const; void reloadQmlSource(); @@ -61,7 +61,7 @@ private: QString generateUniqueCollectionName(const ModelNode &node, const QString &name); QPointer<CollectionView> m_view; - QPointer<CollectionSourceModel> m_sourceModel; + QPointer<CollectionListModel> m_listModel; QPointer<CollectionDetailsModel> m_collectionDetailsModel; std::unique_ptr<CollectionDetailsSortFilterModel> m_collectionDetailsSortFilterModel; QScopedPointer<StudioQuickWidget> m_quickWidget; diff --git a/src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.cpp b/src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.cpp index 7712252655..503b3f1ea3 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.cpp @@ -390,6 +390,14 @@ void DataStoreModelNode::setCollectionNames(const QStringList &newCollectionName update(); } +void DataStoreModelNode::addCollection(const QString &collectionName) +{ + if (!m_collectionPropertyNames.contains(collectionName)) { + m_collectionPropertyNames.insert(collectionName, {}); + update(); + } +} + void DataStoreModelNode::renameCollection(const QString &oldName, const QString &newName) { ModelNode dataStoreNode = modelNode(); @@ -420,12 +428,18 @@ void DataStoreModelNode::renameCollection(const QString &oldName, const QString << QString("There is no old collection name registered with this name \"%1\"").arg(oldName); } -void DataStoreModelNode::removeCollection(const QString &collectionName) +void DataStoreModelNode::removeCollections(const QStringList &collectionNames) { - if (m_collectionPropertyNames.contains(collectionName)) { - m_collectionPropertyNames.remove(collectionName); - update(); + bool updateRequired = false; + for (const QString &collectionName : collectionNames) { + if (m_collectionPropertyNames.contains(collectionName)) { + m_collectionPropertyNames.remove(collectionName); + updateRequired = true; + } } + + if (updateRequired) + update(); } void DataStoreModelNode::assignCollectionToNode(AbstractView *view, diff --git a/src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.h b/src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.h index d23908bc0c..6cd969edbe 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.h +++ b/src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.h @@ -31,8 +31,9 @@ public: ModelNode modelNode() const; void setCollectionNames(const QStringList &newCollectionNames); + void addCollection(const QString &collectionName); void renameCollection(const QString &oldName, const QString &newName); - void removeCollection(const QString &collectionName); + void removeCollections(const QStringList &collectionNames); void assignCollectionToNode(AbstractView *view, const ModelNode &targetNode, |