From 24968dbabd05fafd9a8eabcfcb81ec0ec6b235d3 Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Thu, 23 Nov 2023 12:43:29 +0200 Subject: QmlDesigner: Update DataStore.qml when collections change Task-number: QDS-11113 Change-Id: I27d49d40a36a22e9af447087e7e8f7995812f1e3 Reviewed-by: Reviewed-by: Mahmoud Badri --- src/plugins/qmldesigner/CMakeLists.txt | 1 + .../collectioneditor/collectioneditorconstants.h | 12 +- .../collectioneditor/collectioneditorutils.cpp | 27 ++- .../collectioneditor/collectioneditorutils.h | 2 +- .../collectioneditor/collectionsourcemodel.cpp | 10 +- .../collectioneditor/collectionsourcemodel.h | 1 + .../components/collectioneditor/collectionview.cpp | 45 ++++- .../components/collectioneditor/collectionview.h | 7 +- .../collectioneditor/datastoremodelnode.cpp | 214 +++++++++++++++++++++ .../collectioneditor/datastoremodelnode.h | 37 ++++ 10 files changed, 331 insertions(+), 25 deletions(-) create mode 100644 src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.cpp create mode 100644 src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.h diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index 089d430302..e167a83db3 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -844,6 +844,7 @@ extend_qtc_plugin(QmlDesigner collectionsourcemodel.cpp collectionsourcemodel.h collectionview.cpp collectionview.h collectionwidget.cpp collectionwidget.h + datastoremodelnode.cpp datastoremodelnode.h ) extend_qtc_plugin(QmlDesigner diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorconstants.h b/src/plugins/qmldesigner/components/collectioneditor/collectioneditorconstants.h index 11ceb034fa..e914891de3 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorconstants.h +++ b/src/plugins/qmldesigner/components/collectioneditor/collectioneditorconstants.h @@ -7,10 +7,14 @@ namespace QmlDesigner::CollectionEditor { enum class SourceFormat { Unknown, Json, Csv }; -inline constexpr char SOURCEFILE_PROPERTY[] = "source"; +inline constexpr char SOURCEFILE_PROPERTY[] = "source"; +inline constexpr char ALLMODELS_PROPERTY[] = "allModels"; +inline constexpr char JSONCHILDMODELNAME_PROPERTY[] = "modelName"; -inline constexpr char COLLECTIONMODEL_IMPORT[] = "QtQuick.Studio.Utils"; -inline constexpr char JSONCOLLECTIONMODEL_TYPENAME[] = "QtQuick.Studio.Utils.JsonListModel"; -inline constexpr char CSVCOLLECTIONMODEL_TYPENAME[] = "QtQuick.Studio.Utils.CsvTableModel"; +inline constexpr char COLLECTIONMODEL_IMPORT[] = "QtQuick.Studio.Utils"; +inline constexpr char JSONCOLLECTIONMODEL_TYPENAME[] = "QtQuick.Studio.Utils.JsonListModel"; +inline constexpr char CSVCOLLECTIONMODEL_TYPENAME[] = "QtQuick.Studio.Utils.CsvTableModel"; +inline constexpr char JSONCOLLECTIONCHILDMODEL_TYPENAME[] = "QtQuick.Studio.Utils.ChildListModel"; +inline constexpr char JSONBACKEND_TYPENAME[] = "JsonData"; } // namespace QmlDesigner::CollectionEditor diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.cpp index d4e4e7654f..9abe498b91 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.cpp @@ -14,6 +14,10 @@ #include +#include +#include +#include + #include #include #include @@ -127,16 +131,21 @@ bool canAcceptCollectionAsModel(const ModelNode &node) && modelProperty.propertyType().isVariant(); } -QString getSourceCollectionPath(const ModelNode &node) +QString getSourceCollectionPath(const ModelNode &dataStoreNode) { - QUrl nodeSource = node.variantProperty(CollectionEditor::SOURCEFILE_PROPERTY).value().toUrl(); - QString sourcePath = nodeSource.isLocalFile() ? nodeSource.toLocalFile() : nodeSource.toString(); - return QmlDesignerPlugin::instance() - ->currentDesignDocument() - ->fileName() - .parentDir() - .resolvePath(sourcePath) - .toFSPathString(); + using Utils::FilePath; + ProjectExplorer::Project *currentProject = ProjectExplorer::ProjectManager::startupProject(); + + if (!currentProject || !dataStoreNode.isValid()) + return {}; + + const FilePath expectedFile = currentProject->projectDirectory().pathAppended( + "/imports/" + currentProject->displayName() + "/DataStore.json"); + + if (expectedFile.exists()) + return expectedFile.toFSPathString(); + + return {}; } QJsonArray defaultCollectionArray() diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.h b/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.h index 65806ab3b6..a9022ed099 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.h +++ b/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.h @@ -18,7 +18,7 @@ SourceFormat getSourceCollectionFormat(const QmlDesigner::ModelNode &node); QString getSourceCollectionType(const QmlDesigner::ModelNode &node); -QString getSourceCollectionPath(const QmlDesigner::ModelNode &node); +QString getSourceCollectionPath(const QmlDesigner::ModelNode &dataStoreNode); void assignCollectionSourceToNode(AbstractView *view, const ModelNode &modelNode, diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.cpp index 098bbdaaa7..4d7773ade3 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.cpp @@ -83,8 +83,8 @@ QVariant CollectionSourceModel::data(const QModelIndex &index, int role) const const ModelNode *collectionSource = &m_collectionSources.at(index.row()); switch (role) { - case NameRole: - return collectionSource->variantProperty("objectName").value(); + case NameRole: // Not used, to be removed + return collectionSource->variantProperty("objectName").value().toString(); case NodeRole: return QVariant::fromValue(*collectionSource); case CollectionTypeRole: @@ -634,7 +634,8 @@ void CollectionSourceModel::updateCollectionList(QModelIndex index) QSharedPointer newList = loadCollection(sourceNode, currentList); if (currentList != newList) { m_collectionList.replace(index.row(), newList); - emit this->dataChanged(index, index, {CollectionsRole}); + emit dataChanged(index, index, {CollectionsRole}); + emit collectionNamesChanged(sourceNode, newList->stringList()); } } @@ -657,6 +658,9 @@ void CollectionSourceModel::registerCollection(const QSharedPointersourceNode()) + emit collectionNamesChanged(collection->sourceNode(), collection->stringList()); } QModelIndex CollectionSourceModel::indexOfNode(const ModelNode &node) const diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.h b/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.h index f0dd517ee1..36226138c3 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.h +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.h @@ -71,6 +71,7 @@ public: signals: void selectedIndexChanged(int idx); void collectionSelected(const ModelNode &sourceNode, const QString &collectionName); + void collectionNamesChanged(const ModelNode &sourceNode, QStringList collections); void isEmptyChanged(bool); void warning(const QString &title, const QString &body); diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp index de82734b35..8a470c4915 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp @@ -8,12 +8,17 @@ #include "collectioneditorutils.h" #include "collectionsourcemodel.h" #include "collectionwidget.h" +#include "datastoremodelnode.h" #include "designmodecontext.h" #include "nodeabstractproperty.h" #include "nodemetainfo.h" #include "qmldesignerplugin.h" #include "variantproperty.h" +#include +#include +#include + #include #include #include @@ -37,7 +42,15 @@ namespace QmlDesigner { CollectionView::CollectionView(ExternalDependenciesInterface &externalDependencies) : AbstractView(externalDependencies) -{} + , m_dataStore(std::make_unique()) + +{ + connect(ProjectExplorer::ProjectManager::instance(), + &ProjectExplorer::ProjectManager::startupProjectChanged, + this, + &CollectionView::resetDataStoreNode); + resetDataStoreNode(); +} bool CollectionView::hasWidget() const { @@ -59,6 +72,19 @@ QmlDesigner::WidgetInfo CollectionView::widgetInfo() [this](const ModelNode &sourceNode, const QString &collection) { m_widget->collectionDetailsModel()->loadCollection(sourceNode, collection); }); + + connect(sourceModel, &CollectionSourceModel::isEmptyChanged, this, [this](bool isEmpty) { + if (isEmpty) + m_widget->collectionDetailsModel()->loadCollection({}, {}); + }); + + connect(sourceModel, + &CollectionSourceModel::collectionNamesChanged, + this, + [this](const ModelNode &sourceNode, const QStringList &collectionNames) { + if (sourceNode == m_dataStore->modelNode()) + m_dataStore->setCollectionNames(collectionNames); + }); } return createWidgetInfo(m_widget.data(), @@ -72,7 +98,7 @@ QmlDesigner::WidgetInfo CollectionView::widgetInfo() void CollectionView::modelAttached(Model *model) { AbstractView::modelAttached(model); - refreshModel(); + resetDataStoreNode(); } void CollectionView::nodeReparented(const ModelNode &node, @@ -179,16 +205,23 @@ void CollectionView::registerDeclarativeType() CollectionJsonSourceFilterModel::registerDeclarativeType(); } +void CollectionView::resetDataStoreNode() +{ + m_dataStore->reloadModel(); + refreshModel(); +} + void CollectionView::refreshModel() { if (!model()) return; // Load Model Groups - const ModelNodes collectionSourceNodes = rootModelNode().subModelNodesOfType( - jsonCollectionMetaInfo()) - + rootModelNode().subModelNodesOfType( - csvCollectionMetaInfo()); + ModelNodes collectionSourceNodes; + + if (ModelNode dataStore = m_dataStore->modelNode()) + collectionSourceNodes << dataStore; + m_widget->sourceModel()->setSources(collectionSourceNodes); } diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionview.h b/src/plugins/qmldesigner/components/collectioneditor/collectionview.h index 994105f974..bb52f82ac0 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionview.h +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionview.h @@ -4,13 +4,13 @@ #pragma once #include "abstractview.h" +#include "datastoremodelnode.h" #include "modelnode.h" -#include - namespace QmlDesigner { class CollectionWidget; +class DataStoreModelNode; class CollectionView : public AbstractView { @@ -45,6 +45,8 @@ public: static void registerDeclarativeType(); + void resetDataStoreNode(); + private: void refreshModel(); NodeMetaInfo jsonCollectionMetaInfo() const; @@ -52,5 +54,6 @@ private: void ensureStudioModelImport(); QPointer m_widget; + std::unique_ptr m_dataStore; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.cpp b/src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.cpp new file mode 100644 index 0000000000..4b2ab6edbe --- /dev/null +++ b/src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.cpp @@ -0,0 +1,214 @@ +// 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 "datastoremodelnode.h" + +#include "collectioneditorconstants.h" +#include "model/qmltextgenerator.h" + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include +#include + +namespace { + +Utils::FilePath findFile(const Utils::FilePath &path, const QString &fileName) +{ + QDirIterator it(path.toString(), QDirIterator::Subdirectories); + + while (it.hasNext()) { + QFileInfo file(it.next()); + if (file.isDir()) + continue; + + if (file.fileName() == fileName) + return Utils::FilePath::fromFileInfo(file); + } + return {}; +} + +QmlDesigner::PropertyNameList createNameList(const QmlDesigner::ModelNode &node) +{ + using QmlDesigner::AbstractProperty; + using QmlDesigner::PropertyName; + using QmlDesigner::PropertyNameList; + static PropertyNameList defaultsNodeProps = {"id", + QmlDesigner::CollectionEditor::SOURCEFILE_PROPERTY, + QmlDesigner::CollectionEditor::JSONCHILDMODELNAME_PROPERTY, + "backend"}; + PropertyNameList dynamicPropertyNames = Utils::transform( + node.dynamicProperties(), + [](const AbstractProperty &property) -> PropertyName { return property.name(); }); + + Utils::sort(dynamicPropertyNames); + + return defaultsNodeProps + dynamicPropertyNames; +} + +} // namespace + +namespace QmlDesigner { + +DataStoreModelNode::DataStoreModelNode() +{ + reloadModel(); +} + +void DataStoreModelNode::reloadModel() +{ + using Utils::FilePath; + if (!ProjectExplorer::ProjectManager::startupProject()) { + reset(); + return; + } + bool forceUpdate = false; + + const FilePath projectFilePath = ProjectExplorer::ProjectManager::startupProject()->projectDirectory(); + const FilePath importsPath = FilePath::fromString(projectFilePath.path() + "/imports"); + FilePath dataStoreQmlPath = findFile(importsPath, "DataStore.qml"); + FilePath dataStoreJsonPath = findFile(importsPath, "DataStore.json"); + QUrl dataStoreQmlUrl = dataStoreQmlPath.toUrl(); + + if (dataStoreQmlPath.exists() && dataStoreJsonPath.exists()) { + if (!m_model.get() || m_model->fileUrl() != dataStoreQmlUrl) { + m_model = Model::create(CollectionEditor::JSONCOLLECTIONMODEL_TYPENAME, 1, 1); + forceUpdate = true; + Import import = Import::createLibraryImport(CollectionEditor::COLLECTIONMODEL_IMPORT); + try { + if (!m_model->hasImport(import, true, true)) + m_model->changeImports({import}, {}); + } catch (const Exception &) { + QTC_ASSERT(false, return); + } + } + } else { + reset(); + } + + QTC_ASSERT(m_model.get(), return); + m_model->setFileUrl(dataStoreQmlUrl); + + m_dataRelativePath = dataStoreJsonPath.relativePathFrom(dataStoreQmlPath).toFSPathString(); + + if (forceUpdate) { + updateDataStoreProperties(); + updateSingletonFile(); + } +} + +QStringList DataStoreModelNode::collectionNames() const +{ + return m_collectionNames; +} + +Model *DataStoreModelNode::model() const +{ + return m_model.get(); +} + +ModelNode DataStoreModelNode::modelNode() const +{ + QTC_ASSERT(m_model.get(), return {}); + return m_model->rootModelNode(); +} + +QString DataStoreModelNode::getModelQmlText() +{ + ModelNode node = modelNode(); + QTC_ASSERT(node, return {}); + + Internal::QmlTextGenerator textGen(createNameList(node), + QmlJSTools::QmlJSToolsSettings::globalCodeStyle()->tabSettings()); + + QString genText = textGen(node); + return genText; +} + +void DataStoreModelNode::reset() +{ + if (m_model) + m_model.reset(); + + m_dataRelativePath.clear(); + setCollectionNames({}); +} + +void DataStoreModelNode::updateDataStoreProperties() +{ + QTC_ASSERT(model(), return); + + ModelNode rootNode = modelNode(); + QTC_ASSERT(rootNode.isValid(), return); + + static TypeName childNodeTypename = "ChildListModel"; + + const QList formerPropertyNames = rootNode.dynamicProperties(); + for (const AbstractProperty &property : formerPropertyNames) + rootNode.removeProperty(property.name()); + + rootNode.setIdWithoutRefactoring("models"); + + for (const QString &collectionName : std::as_const(m_collectionNames)) { + PropertyName newName = collectionName.toLatin1(); + + ModelNode collectionNode = model()->createModelNode(childNodeTypename); + + VariantProperty modelNameProperty = collectionNode.variantProperty( + CollectionEditor::JSONCHILDMODELNAME_PROPERTY); + modelNameProperty.setValue(newName); + + NodeProperty nodeProp = rootNode.nodeProperty(newName); + nodeProp.setDynamicTypeNameAndsetModelNode(childNodeTypename, collectionNode); + } + + // Backend Property + ModelNode backendNode = model()->createModelNode(CollectionEditor::JSONBACKEND_TYPENAME); + NodeProperty backendProperty = rootNode.nodeProperty("backend"); + backendProperty.setDynamicTypeNameAndsetModelNode(CollectionEditor::JSONBACKEND_TYPENAME, + backendNode); + // Source Property + VariantProperty sourceProp = rootNode.variantProperty(CollectionEditor::SOURCEFILE_PROPERTY); + sourceProp.setValue(m_dataRelativePath); +} + +void DataStoreModelNode::updateSingletonFile() +{ + using Utils::FilePath; + using Utils::FileSaver; + QTC_ASSERT(m_model.get(), return); + + const QString pragmaSingleTone = "pragma Singleton\n"; + QString imports; + + for (const Import &import : m_model->imports()) + imports += QStringLiteral("import %1\n").arg(import.toString(true)); + + QString content = pragmaSingleTone + imports + getModelQmlText(); + QUrl modelUrl = m_model->fileUrl(); + FileSaver file(FilePath::fromUserInput(modelUrl.isLocalFile() ? modelUrl.toLocalFile() + : modelUrl.toString())); + file.write(content.toLatin1()); + file.finalize(); +} + +void DataStoreModelNode::setCollectionNames(const QStringList &newCollectionNames) +{ + if (m_collectionNames != newCollectionNames) { + m_collectionNames = newCollectionNames; + updateDataStoreProperties(); + updateSingletonFile(); + } +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.h b/src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.h new file mode 100644 index 0000000000..e76d7f50e4 --- /dev/null +++ b/src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.h @@ -0,0 +1,37 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include + +namespace QmlDesigner { + +class Model; + +class DataStoreModelNode +{ +public: + DataStoreModelNode(); + + void reloadModel(); + QStringList collectionNames() const; + + Model *model() const; + ModelNode modelNode() const; + + void setCollectionNames(const QStringList &newCollectionNames); + +private: + QString getModelQmlText(); + + void reset(); + void updateDataStoreProperties(); + void updateSingletonFile(); + + ModelPointer m_model; + QStringList m_collectionNames; + QString m_dataRelativePath; +}; + +} // namespace QmlDesigner -- cgit v1.2.3