aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAli Kianian <ali.kianian@qt.io>2023-11-23 12:43:29 +0200
committerAli Kianian <ali.kianian@qt.io>2023-11-24 20:14:54 +0000
commit24968dbabd05fafd9a8eabcfcb81ec0ec6b235d3 (patch)
treed21bf2ab8e789c6a7063f3cdcb038f1a12353741
parentf6b5afa8e403655810b9ab80d2c2889bafcfd5a2 (diff)
QmlDesigner: Update DataStore.qml when collections change
Task-number: QDS-11113 Change-Id: I27d49d40a36a22e9af447087e7e8f7995812f1e3 Reviewed-by: <github-actions-qt-creator@cristianadam.eu> Reviewed-by: Mahmoud Badri <mahmoud.badri@qt.io>
-rw-r--r--src/plugins/qmldesigner/CMakeLists.txt1
-rw-r--r--src/plugins/qmldesigner/components/collectioneditor/collectioneditorconstants.h12
-rw-r--r--src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.cpp27
-rw-r--r--src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.h2
-rw-r--r--src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.cpp10
-rw-r--r--src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.h1
-rw-r--r--src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp45
-rw-r--r--src/plugins/qmldesigner/components/collectioneditor/collectionview.h7
-rw-r--r--src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.cpp214
-rw-r--r--src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.h37
10 files changed, 331 insertions, 25 deletions
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 <utils/qtcassert.h>
+#include <projectexplorer/project.h>
+#include <projectexplorer/projectexplorer.h>
+#include <projectexplorer/projectmanager.h>
+
#include <QColor>
#include <QJsonArray>
#include <QJsonObject>
@@ -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<CollectionListModel> 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 QSharedPointer<CollectionLi
this,
&CollectionSourceModel::onCollectionsRemoved,
Qt::UniqueConnection);
+
+ if (collection.data() && collection->sourceNode())
+ 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 <projectexplorer/project.h>
+#include <projectexplorer/projectexplorer.h>
+#include <projectexplorer/projectmanager.h>
+
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
@@ -37,7 +42,15 @@ namespace QmlDesigner {
CollectionView::CollectionView(ExternalDependenciesInterface &externalDependencies)
: AbstractView(externalDependencies)
-{}
+ , m_dataStore(std::make_unique<DataStoreModelNode>())
+
+{
+ 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 <QDateTime>
-
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<CollectionWidget> m_widget;
+ std::unique_ptr<DataStoreModelNode> 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 <model.h>
+#include <nodemetainfo.h>
+#include <nodeproperty.h>
+#include <variantproperty.h>
+
+#include <qmljstools/qmljscodestylepreferences.h>
+#include <qmljstools/qmljstoolssettings.h>
+
+#include <projectexplorer/project.h>
+#include <projectexplorer/projectexplorer.h>
+#include <projectexplorer/projectmanager.h>
+
+#include <utils/fileutils.h>
+#include <utils/qtcassert.h>
+
+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<AbstractProperty> 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 <modelnode.h>
+
+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