diff --git a/src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.cpp b/src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.cpp index bebc60f1de1..07f2116b6b9 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.cpp @@ -7,6 +7,10 @@ #include "collectioneditorconstants.h" #include "collectioneditorutils.h" #include "model/qmltextgenerator.h" +#include "plaintexteditmodifier.h" +#include "qmldesignerbase/qmldesignerbaseplugin.h" +#include "qmldesignerexternaldependencies.h" +#include "rewriterview.h" #include #include @@ -23,11 +27,15 @@ #include #include +#include #include #include +#include namespace { +inline constexpr char CHILDLISTMODEL_TYPENAME[] = "ChildListModel"; + QmlDesigner::PropertyNameList createNameList(const QmlDesigner::ModelNode &node) { using QmlDesigner::AbstractProperty; @@ -59,6 +67,57 @@ bool isValidCollectionPropertyName(const QString &collectionId) && !reservedKeywords.contains(collectionId.toLatin1()); } +QMap getModelIdMap(const QmlDesigner::ModelNode &rootNode) +{ + using namespace QmlDesigner; + QMap modelNameForId; + + const QList propertyNames = rootNode.dynamicProperties(); + + for (const AbstractProperty &property : std::as_const(propertyNames)) { + if (!property.isNodeProperty()) + continue; + + NodeProperty nodeProperty = property.toNodeProperty(); + if (!nodeProperty.hasDynamicTypeName(CHILDLISTMODEL_TYPENAME)) + continue; + + ModelNode childNode = nodeProperty.modelNode(); + if (childNode.hasProperty(CollectionEditor::JSONCHILDMODELNAME_PROPERTY)) { + QString modelName = childNode.property(CollectionEditor::JSONCHILDMODELNAME_PROPERTY) + .toVariantProperty() + .value() + .toString(); + + if (!modelName.isEmpty()) + modelNameForId.insert(modelName, property.name()); + } + } + return modelNameForId; +} + +void setQmlContextToModel(QmlDesigner::Model *model, const QString &qmlContext) +{ + using namespace QmlDesigner; + Q_ASSERT(model); + + QScopedPointer textEdit(new QPlainTextEdit); + QScopedPointer modifier( + new NotIndentingTextEditModifier(textEdit.data())); + textEdit->hide(); + textEdit->setPlainText(qmlContext); + QmlDesigner::ExternalDependencies externalDependencies{QmlDesignerBasePlugin::settings()}; + QScopedPointer rewriter( + new RewriterView(externalDependencies, QmlDesigner::RewriterView::Validate)); + + rewriter->setParent(model); + rewriter->setTextModifier(modifier.get()); + rewriter->setCheckSemanticErrors(false); + + model->attachView(rewriter.get()); + model->detachView(rewriter.get()); +} + } // namespace namespace QmlDesigner { @@ -102,8 +161,10 @@ void DataStoreModelNode::reloadModel() m_dataRelativePath = dataStoreJsonPath.relativePathFrom(dataStoreQmlPath).toFSPathString(); - if (forceUpdate) + if (forceUpdate) { + preloadFile(); update(); + } } QStringList DataStoreModelNode::collectionNames() const @@ -143,6 +204,25 @@ void DataStoreModelNode::reset() setCollectionNames({}); } +void DataStoreModelNode::preloadFile() +{ + using Utils::FilePath; + using Utils::FileReader; + + if (!m_model) + return; + + const FilePath dataStoreQmlPath = dataStoreQmlFilePath(); + FileReader dataStoreQmlFile; + QString sourceQmlContext; + + if (dataStoreQmlFile.fetch(dataStoreQmlPath)) + sourceQmlContext = QString::fromLatin1(dataStoreQmlFile.data()); + + setQmlContextToModel(m_model.get(), sourceQmlContext); + m_collectionPropertyNames = getModelIdMap(m_model->rootModelNode()); +} + void DataStoreModelNode::updateDataStoreProperties() { QTC_ASSERT(model(), return); @@ -150,8 +230,6 @@ void DataStoreModelNode::updateDataStoreProperties() ModelNode rootNode = modelNode(); QTC_ASSERT(rootNode.isValid(), return); - static TypeName childNodeTypename = "ChildListModel"; - QSet collectionNamesToBeAdded; const QStringList allCollectionNames = m_collectionPropertyNames.keys(); for (const QString &collectionName : allCollectionNames) @@ -165,7 +243,7 @@ void DataStoreModelNode::updateDataStoreProperties() continue; NodeProperty nodeProprty = property.toNodeProperty(); - if (!nodeProprty.hasDynamicTypeName(childNodeTypename)) + if (!nodeProprty.hasDynamicTypeName(CHILDLISTMODEL_TYPENAME)) continue; ModelNode childNode = nodeProprty.modelNode(); @@ -189,24 +267,8 @@ void DataStoreModelNode::updateDataStoreProperties() QStringList collectionNamesLeft = collectionNamesToBeAdded.values(); Utils::sort(collectionNamesLeft); - for (const QString &collectionName : std::as_const(collectionNamesLeft)) { - PropertyName newPropertyName = getUniquePropertyName(collectionName); - if (newPropertyName.isEmpty()) { - qWarning() << __FUNCTION__ << __LINE__ - << QString("The property name cannot be generated from \"%1\"").arg(collectionName); - continue; - } - - ModelNode collectionNode = model()->createModelNode(childNodeTypename); - VariantProperty modelNameProperty = collectionNode.variantProperty( - CollectionEditor::JSONCHILDMODELNAME_PROPERTY); - modelNameProperty.setValue(collectionName); - - NodeProperty nodeProp = rootNode.nodeProperty(newPropertyName); - nodeProp.setDynamicTypeNameAndsetModelNode(childNodeTypename, collectionNode); - - m_collectionPropertyNames.insert(collectionName, newPropertyName); - } + for (const QString &collectionName : std::as_const(collectionNamesLeft)) + addCollectionNameToTheModel(collectionName, getUniquePropertyName(collectionName)); // Backend Property ModelNode backendNode = model()->createModelNode(CollectionEditor::JSONBACKEND_TYPENAME); @@ -231,19 +293,50 @@ void DataStoreModelNode::updateSingletonFile() 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())); + FileSaver file(dataStoreQmlFilePath()); file.write(content.toLatin1()); file.finalize(); } void DataStoreModelNode::update() { + if (!m_model.get()) + return; + updateDataStoreProperties(); updateSingletonFile(); } +void DataStoreModelNode::addCollectionNameToTheModel(const QString &collectionName, + const PropertyName &dataStorePropertyName) +{ + ModelNode rootNode = modelNode(); + QTC_ASSERT(rootNode.isValid(), return); + + if (dataStorePropertyName.isEmpty()) { + qWarning() << __FUNCTION__ << __LINE__ + << QString("The property name cannot be generated from \"%1\"").arg(collectionName); + return; + } + + ModelNode collectionNode = model()->createModelNode(CHILDLISTMODEL_TYPENAME); + VariantProperty modelNameProperty = collectionNode.variantProperty( + CollectionEditor::JSONCHILDMODELNAME_PROPERTY); + modelNameProperty.setValue(collectionName); + + NodeProperty nodeProp = rootNode.nodeProperty(dataStorePropertyName); + nodeProp.setDynamicTypeNameAndsetModelNode(CHILDLISTMODEL_TYPENAME, collectionNode); + + m_collectionPropertyNames.insert(collectionName, dataStorePropertyName); +} + +Utils::FilePath DataStoreModelNode::dataStoreQmlFilePath() const +{ + QUrl modelUrl = m_model->fileUrl(); + return Utils::FilePath::fromUserInput(modelUrl.isLocalFile() ? modelUrl.toLocalFile() + : modelUrl.toString()); +} + PropertyName DataStoreModelNode::getUniquePropertyName(const QString &collectionName) { ModelNode dataStoreNode = modelNode(); diff --git a/src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.h b/src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.h index 3048fc4fc9c..1c855bca7a2 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.h +++ b/src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.h @@ -7,6 +7,10 @@ #include +namespace Utils { +class FilePath; +} + namespace QmlDesigner { class Model; @@ -34,9 +38,14 @@ private: QString getModelQmlText(); void reset(); + void preloadFile(); void updateDataStoreProperties(); void updateSingletonFile(); void update(); + void addCollectionNameToTheModel(const QString &collectionName, + const PropertyName &dataStorePropertyName); + Utils::FilePath dataStoreQmlFilePath() const; + PropertyName getUniquePropertyName(const QString &collectionName); ModelPointer m_model;