diff options
author | The Qt Project <gerrit-noreply@qt-project.org> | 2024-01-18 12:32:01 +0000 |
---|---|---|
committer | The Qt Project <gerrit-noreply@qt-project.org> | 2024-01-18 12:32:01 +0000 |
commit | 5772a85751b905fa070f9c11fc930cb256c231c5 (patch) | |
tree | e93dd6cb9e9a0d340f0ebbc8e833f0b601492c70 /src/plugins | |
parent | d51e116ac4219f90ce8ca35e032f4387fe581a6f (diff) | |
parent | 399f7a1968c7d64580bdd209c4747a6979e07c93 (diff) |
Merge "Merge remote-tracking branch 'origin/qds/dev'"
Diffstat (limited to 'src/plugins')
47 files changed, 931 insertions, 422 deletions
diff --git a/src/plugins/clangformat/clangformatbaseindenter.cpp b/src/plugins/clangformat/clangformatbaseindenter.cpp index ac788c8a4b..dbbacd48a9 100644 --- a/src/plugins/clangformat/clangformatbaseindenter.cpp +++ b/src/plugins/clangformat/clangformatbaseindenter.cpp @@ -389,6 +389,7 @@ Utils::ChangeSet convertReplacements(const QTextDocument *doc, .size(); QString replacementText = QString::fromStdString(replacement.getReplacementText().str()); + replacementText.replace("\r", ""); auto sameCharAt = [&](int replacementOffset) { if (replacementText.size() <= replacementOffset || replacementOffset < 0) return false; diff --git a/src/plugins/cppeditor/cpptoolsreuse.cpp b/src/plugins/cppeditor/cpptoolsreuse.cpp index c8ce0effea..d7e403a1cc 100644 --- a/src/plugins/cppeditor/cpptoolsreuse.cpp +++ b/src/plugins/cppeditor/cpptoolsreuse.cpp @@ -786,6 +786,7 @@ SearchResultItems symbolOccurrencesInDeclarationComments( QList<Text::Range> symbolOccurrencesInText(const QTextDocument &doc, QStringView text, int offset, const QString &symbolName) { + QTC_ASSERT(!symbolName.isEmpty(), return QList<Text::Range>()); QList<Text::Range> ranges; int index = 0; while (true) { diff --git a/src/plugins/effectmakernew/compositionnode.cpp b/src/plugins/effectmakernew/compositionnode.cpp index 9412153dde..811cdbcb8f 100644 --- a/src/plugins/effectmakernew/compositionnode.cpp +++ b/src/plugins/effectmakernew/compositionnode.cpp @@ -113,6 +113,9 @@ void CompositionNode::parse(const QString &effectName, const QString &qenPath, c m_fragmentCode = EffectUtils::codeFromJsonArray(json.value("fragmentCode").toArray()); m_vertexCode = EffectUtils::codeFromJsonArray(json.value("vertexCode").toArray()); + if (json.contains("enabled")) + m_isEnabled = json["enabled"].toBool(); + m_id = json.value("id").toString(); if (m_id.isEmpty() && !qenPath.isEmpty()) { QString fileName = qenPath.split('/').last(); diff --git a/src/plugins/effectmakernew/effectmakermodel.cpp b/src/plugins/effectmakernew/effectmakermodel.cpp index 4fcb6686a7..dc71418f1d 100644 --- a/src/plugins/effectmakernew/effectmakermodel.cpp +++ b/src/plugins/effectmakernew/effectmakermodel.cpp @@ -87,6 +87,7 @@ bool EffectMakerModel::setData(const QModelIndex &index, const QVariant &value, if (role == EnabledRole) { m_nodes.at(index.row())->setIsEnabled(value.toBool()); bakeShaders(); + setHasUnsavedChanges(true); emit dataChanged(index, index, {role}); } @@ -192,14 +193,17 @@ void EffectMakerModel::removeNode(int idx) emit nodesChanged(); } -void EffectMakerModel::clear() +void EffectMakerModel::clear(bool clearName) { beginResetModel(); qDeleteAll(m_nodes); m_nodes.clear(); endResetModel(); + + if (clearName) + setCurrentComposition(""); + setHasUnsavedChanges(!m_currentComposition.isEmpty()); - setCurrentComposition(""); setIsEmpty(true); emit nodesChanged(); @@ -413,6 +417,7 @@ void EffectMakerModel::setEffectError(const QString &errorMessage, int type, int QString additionalErrorInfo = detectErrorMessage(errorMessage); error.m_message = additionalErrorInfo + errorMessage; m_effectErrors.insert(type, error); + qWarning() << QString("Effect error (line: %2): %1").arg(error.m_message, error.m_line); Q_EMIT effectErrorChanged(); } @@ -556,13 +561,23 @@ QString EffectMakerModel::getQmlEffectString() { QString s; - s += QString("// Created with Qt Design Studio (version %1), %2\n\n") - .arg(qApp->applicationVersion(), QDateTime::currentDateTime().toString()); - s += "import QtQuick\n"; - s += '\n'; - s += "Item {\n"; - s += " id: rootItem\n"; - s += '\n'; + // _isEffectItem is type var to hide it from property view + QString header{ +R"( +// Created with Qt Design Studio (version %1), %2 + +import QtQuick + +Item { + id: rootItem + + property var _isEffectItem + property Item _oldParent: null +)" + }; + + s += header.arg(qApp->applicationVersion(), QDateTime::currentDateTime().toString()); + if (m_shaderFeatures.enabled(ShaderFeatures::Source)) { s += " // This is the main source for the effect\n"; s += " property Item source: null\n"; @@ -570,7 +585,7 @@ QString EffectMakerModel::getQmlEffectString() if (m_shaderFeatures.enabled(ShaderFeatures::Time) || m_shaderFeatures.enabled(ShaderFeatures::Frame)) { s += " // Enable this to animate iTime property\n"; - s += " property bool timeRunning: false\n"; + s += " property bool timeRunning: true\n"; } if (m_shaderFeatures.enabled(ShaderFeatures::Time)) { s += " // When timeRunning is false, this can be used to control iTime manually\n"; @@ -580,7 +595,33 @@ QString EffectMakerModel::getQmlEffectString() s += " // When timeRunning is false, this can be used to control iFrame manually\n"; s += " property int animatedFrame: frameAnimation.currentFrame\n"; } - s += '\n'; + + QString parentChanged{ +R"( + onParentChanged: { + if (_oldParent && _oldParent !== parent) { + _oldParent.layer.enabled = false + _oldParent.layer.effect = null + %2 + _oldParent.update() + _oldParent = null + } + if (parent) { + _oldParent = parent + parent.layer.enabled = true + parent.layer.effect = effectComponent + %1 + } + } +)" + }; + + parentChanged = parentChanged.arg(m_shaderFeatures.enabled(ShaderFeatures::Source) + ? QString("source = parent") : QString(), + m_shaderFeatures.enabled(ShaderFeatures::Source) + ? QString("source = null") : QString()); + s += parentChanged; + // Custom properties if (!m_exportedRootPropertiesString.isEmpty()) { s += m_exportedRootPropertiesString; @@ -595,19 +636,14 @@ QString EffectMakerModel::getQmlEffectString() s += '\n'; } - if (m_shaderFeatures.enabled(ShaderFeatures::BlurSources)) { - s += " BlurHelper {\n"; - s += " id: blurHelper\n"; - s += " anchors.fill: parent\n"; - int blurMax = 32; - if (g_propertyData.contains("BLUR_HELPER_MAX_LEVEL")) - blurMax = g_propertyData["BLUR_HELPER_MAX_LEVEL"].toInt(); - s += QString(" property int blurMax: %1\n").arg(blurMax); - s += " property real blurMultiplier: rootItem.blurMultiplier\n"; - s += " }\n"; - } + QString customImagesString = getQmlImagesString(true); + if (!customImagesString.isEmpty()) + s += customImagesString; + s += " Component {\n"; + s += " id: effectComponent\n"; s += getQmlComponentString(true); + s += " }\n"; s += "}\n"; return s; } @@ -651,7 +687,7 @@ void EffectMakerModel::saveComposition(const QString &name) void EffectMakerModel::openComposition(const QString &path) { - clear(); + clear(true); const QString effectName = QFileInfo(path).baseName(); setCurrentComposition(effectName); @@ -785,10 +821,10 @@ void EffectMakerModel::saveResources(const QString &name) for (int i = 1; i < qmlStringList.size(); i++) { QString line = qmlStringList.at(i).trimmed(); if (line.startsWith("vertexShader")) { - QString vsLine = " vertexShader: '" + vsFilename + "'"; + QString vsLine = " vertexShader: '" + vsFilename + "'"; qmlStringList[i] = vsLine; } else if (line.startsWith("fragmentShader")) { - QString fsLine = " fragmentShader: '" + fsFilename + "'"; + QString fsLine = " fragmentShader: '" + fsFilename + "'"; qmlStringList[i] = fsLine; } } @@ -1287,17 +1323,17 @@ void EffectMakerModel::updateCustomUniforms() if (!uniform->description().isEmpty()) { const QStringList descriptionLines = uniform->description().split('\n'); for (const QString &line : descriptionLines) - exportedEffectPropertiesString += QStringLiteral(" // ") + line + '\n'; + exportedEffectPropertiesString += QStringLiteral(" // ") + line + '\n'; } - exportedEffectPropertiesString += QStringLiteral(" ") + readOnly + exportedEffectPropertiesString += QStringLiteral(" ") + readOnly + "property " + propertyType + " " + propertyName + boundValueString + '\n'; } else { // Custom values are not added into root exportedRootPropertiesString += " property " + propertyType + " " + propertyName + valueString + '\n'; - exportedEffectPropertiesString += QStringLiteral(" ") - + readOnly + "property alias " + propertyName + exportedEffectPropertiesString += QStringLiteral(" ") + + readOnly + "property " + propertyType + " " + propertyName + ": rootItem." + uniform->name() + '\n'; } } @@ -1488,22 +1524,26 @@ QString EffectMakerModel::getQmlComponentString(bool localFiles) { if (localFiles) { const QString parent = blurHelper ? QString("blurHelper.") : QString("rootItem."); - return QString("readonly property alias %1: %2%3\n").arg(name, parent, var); + return QString("readonly property %1 %2: %3%4\n").arg(type, name, parent, var); } else { const QString parent = blurHelper ? "blurHelper." : QString(); return QString("readonly property %1 %2: %3%4\n").arg(type, name, parent, var); } }; - QString customImagesString = getQmlImagesString(localFiles); QString s; - QString l1 = localFiles ? QStringLiteral(" ") : QStringLiteral(""); - QString l2 = localFiles ? QStringLiteral(" ") : QStringLiteral(" "); - QString l3 = localFiles ? QStringLiteral(" ") : QStringLiteral(" "); + QString l1 = localFiles ? QStringLiteral(" ") : QStringLiteral(""); + QString l2 = localFiles ? QStringLiteral(" ") : QStringLiteral(" "); + QString l3 = localFiles ? QStringLiteral(" ") : QStringLiteral(" "); if (!localFiles) s += "import QtQuick\n"; s += l1 + "ShaderEffect {\n"; + + if (localFiles) { + // Explicit "source" property is required for render puppet to detect effect correctly + s += l2 + "property Item source: null\n"; + } if (m_shaderFeatures.enabled(ShaderFeatures::Source)) s += l2 + addProperty("iSource", "source", "Item"); if (m_shaderFeatures.enabled(ShaderFeatures::Time)) @@ -1529,15 +1569,18 @@ QString EffectMakerModel::getQmlComponentString(bool localFiles) // and when in exported component, property with binding to root value. s += localFiles ? m_exportedEffectPropertiesString : m_previewEffectPropertiesString; - if (!customImagesString.isEmpty()) - s += '\n' + customImagesString; + if (!localFiles) { + QString customImagesString = getQmlImagesString(false); + if (!customImagesString.isEmpty()) + s += '\n' + customImagesString; + } s += '\n'; const QString vertFile = localFiles ? m_vertexShaderFilename : m_vertexShaderPreviewFilename; const QString fragFile = localFiles ? m_fragmentShaderFilename : m_fragmentShaderPreviewFilename; s += l2 + "vertexShader: 'file:///" + vertFile + "'\n"; s += l2 + "fragmentShader: 'file:///" + fragFile + "'\n"; - s += l2 + "anchors.fill: parent\n"; + s += l2 + "anchors.fill: " + (localFiles ? "rootItem.source" : "parent") + "\n"; if (m_shaderFeatures.enabled(ShaderFeatures::GridMesh)) { QString gridSize = QString("%1, %2").arg(m_shaderFeatures.gridMeshWidth()) .arg(m_shaderFeatures.gridMeshHeight()); @@ -1545,6 +1588,18 @@ QString EffectMakerModel::getQmlComponentString(bool localFiles) s += l3 + QString("resolution: Qt.size(%1)\n").arg(gridSize); s += l2 + "}\n"; } + if (localFiles && m_shaderFeatures.enabled(ShaderFeatures::BlurSources)) { + s += l2 + "BlurHelper {\n"; + s += l3 + "id: blurHelper\n"; + s += l3 + "source: rootItem.source\n"; + int blurMax = 32; + if (g_propertyData.contains("BLUR_HELPER_MAX_LEVEL")) + blurMax = g_propertyData["BLUR_HELPER_MAX_LEVEL"].toInt(); + s += l3 + QString("property int blurMax: %1\n").arg(blurMax); + s += l3 + "property real blurMultiplier: rootItem.blurMultiplier\n"; + s += l2 + "}\n"; + } + s += l1 + "}\n"; return s; } diff --git a/src/plugins/effectmakernew/effectmakermodel.h b/src/plugins/effectmakernew/effectmakermodel.h index a25a4e4091..1bbac9cd55 100644 --- a/src/plugins/effectmakernew/effectmakermodel.h +++ b/src/plugins/effectmakernew/effectmakermodel.h @@ -66,7 +66,7 @@ public: Q_INVOKABLE void moveNode(int fromIdx, int toIdx); Q_INVOKABLE void removeNode(int idx); - Q_INVOKABLE void clear(); + Q_INVOKABLE void clear(bool clearName = false); Q_INVOKABLE void assignToSelected(); Q_INVOKABLE QString getUniqueEffectName() const; diff --git a/src/plugins/mcusupport/mcusupportimportprovider.cpp b/src/plugins/mcusupport/mcusupportimportprovider.cpp index b78baf8bd9..f83dec6cb7 100644 --- a/src/plugins/mcusupport/mcusupportimportprovider.cpp +++ b/src/plugins/mcusupport/mcusupportimportprovider.cpp @@ -119,6 +119,30 @@ void McuSupportImportProvider::loadBuiltins(ImportsPerDocument *importsPerDocume import.info = ImportInfo::moduleImport("qul", {1, 0}, QString()); getInterfacesImport(context->fileName(), importsPerDocument, import, valueOwner, snapshot); imports->append(import); +} + +FilePaths McuSupportImportProvider::prioritizeImportPaths(const Document *context, + const FilePaths &importPaths) +{ + if (!context) + return importPaths; + const std::optional<FilePath> cmakeFilesPathOpt = getTargetBuildFolder(context->fileName()); + if (!cmakeFilesPathOpt) + return importPaths; + FilePaths ret; + // qmltocpp uses an incomplete QtQuick folder present in the build folder + // to avoid taking precedence over the correct qul_install/include/*/StyleDefault + // move the import path to be last + std::copy_if(importPaths.cbegin(), + importPaths.cend(), + std::back_inserter(ret), + [cmakeFilesPathOpt](const FilePath &path) { return path != *cmakeFilesPathOpt; }); + + // nothing was removed + if (ret.size() == importPaths.size()) + return importPaths; + ret.push_back(*cmakeFilesPathOpt); + return ret; }; void McuSupportImportProvider::getInterfacesImport(const FilePath &path, @@ -156,6 +180,7 @@ std::optional<FilePath> McuSupportImportProvider::getFileModule(const FilePath & const FilePath &inputFile) const { const auto doc = QJsonDocument::fromJson(inputFile.fileContents().value_or("")); + if (!doc.isObject()) return {}; diff --git a/src/plugins/mcusupport/mcusupportimportprovider.h b/src/plugins/mcusupport/mcusupportimportprovider.h index c1a130ed3d..f7457751b0 100644 --- a/src/plugins/mcusupport/mcusupportimportprovider.h +++ b/src/plugins/mcusupport/mcusupportimportprovider.h @@ -29,6 +29,9 @@ public: ValueOwner *valueOwner, Snapshot *snapshot) override; + virtual Utils::FilePaths prioritizeImportPaths(const Document *context, + const Utils::FilePaths &importPaths) override; + // Add to the interfaces needed for a document // path: opened qml document // importsPerDocument: imports available in the document (considered imported) diff --git a/src/plugins/projectexplorer/abi.cpp b/src/plugins/projectexplorer/abi.cpp index 07365fc5dd..d1bd01b89b 100644 --- a/src/plugins/projectexplorer/abi.cpp +++ b/src/plugins/projectexplorer/abi.cpp @@ -2,6 +2,7 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "abi.h" +#include "projectexplorerconstants.h" #include <utils/algorithm.h> #include <utils/environment.h> @@ -437,6 +438,18 @@ static Abis abiOf(const QByteArray &data) return result; } +static QString androidAbiFromAbi(const Abi &abi) +{ + QString androidAbi; + if (abi.architecture() == Abi::Architecture::ArmArchitecture) + androidAbi = QLatin1String(abi.wordWidth() == 64 ? Constants::ANDROID_ABI_ARM64_V8A + : Constants::ANDROID_ABI_ARMEABI_V7A); + else + androidAbi = QLatin1String(abi.wordWidth() == 64 ? Constants::ANDROID_ABI_X86_64 + : Constants::ANDROID_ABI_X86); + return androidAbi; +} + // -------------------------------------------------------------------------- // Abi // -------------------------------------------------------------------------- @@ -908,7 +921,11 @@ Abi Abi::fromString(const QString &abiString) return Abi(architecture, os, flavor, format, 0); } - return Abi(architecture, os, flavor, format, wordWidth); + Abi abi(architecture, os, flavor, format, wordWidth); + if (abi.os() == LinuxOS && abi.osFlavor() == AndroidLinuxFlavor) + abi.m_param = androidAbiFromAbi(abi); + + return abi; } Abi::Architecture Abi::architectureFromString(const QString &a) diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index e167a83db3..b13841019c 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -839,7 +839,6 @@ extend_qtc_plugin(QmlDesigner collectiondetailssortfiltermodel.cpp collectiondetailssortfiltermodel.h collectioneditorconstants.h collectioneditorutils.cpp collectioneditorutils.h - collectionimporttools.cpp collectionimporttools.h collectionlistmodel.cpp collectionlistmodel.h collectionsourcemodel.cpp collectionsourcemodel.h collectionview.cpp collectionview.h diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp index 4cc6717d91..90f0061c87 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp @@ -28,7 +28,7 @@ const QMap<DataTypeWarning::Warning, QString> DataTypeWarning::dataTypeWarnings class CollectionDetails::Private { - using SourceFormat = CollectionEditor::SourceFormat; + using SourceFormat = CollectionEditorConstants::SourceFormat; public: QList<CollectionProperty> properties; @@ -85,6 +85,24 @@ static QVariant valueToVariant(const QJsonValue &value, CollectionDetails::DataT } } +static QJsonValue variantToJsonValue(const QVariant &variant) +{ + using VariantType = QVariant::Type; + + switch (variant.type()) { + case VariantType::Bool: + return variant.toBool(); + case VariantType::Double: + case VariantType::Int: + return variant.toDouble(); + case VariantType::String: + case VariantType::Color: + case VariantType::Url: + default: + return variant.toString(); + } +} + CollectionDetails::CollectionDetails() : d(new Private()) {} @@ -101,7 +119,7 @@ CollectionDetails::~CollectionDetails() = default; void CollectionDetails::resetDetails(const QStringList &propertyNames, const QList<QJsonObject> &elements, - CollectionEditor::SourceFormat format) + CollectionEditorConstants::SourceFormat format) { if (!isValid()) return; @@ -238,6 +256,7 @@ bool CollectionDetails::setPropertyValue(int row, int column, const QVariant &va return false; element.insert(d->properties.at(column).name, QJsonValue::fromVariant(value)); + markChanged(); return true; } @@ -277,8 +296,10 @@ bool CollectionDetails::setPropertyType(int column, DataType type) for (QJsonObject &element : d->elements) { if (element.contains(property.name)) { - QJsonValue value = element.value(property.name); - element.insert(property.name, valueToVariant(value, type).toJsonValue()); + const QJsonValue value = element.value(property.name); + const QVariant properTypedValue = valueToVariant(value, type); + const QJsonValue properTypedJsonValue = variantToJsonValue(properTypedValue); + element.insert(property.name, properTypedJsonValue); changed = true; } } @@ -294,7 +315,7 @@ CollectionReference CollectionDetails::reference() const return d->reference; } -CollectionEditor::SourceFormat CollectionDetails::sourceFormat() const +CollectionEditorConstants::SourceFormat CollectionDetails::sourceFormat() const { return d->sourceFormat; } @@ -354,8 +375,10 @@ DataTypeWarning::Warning CollectionDetails::cellWarningCheck(int row, int column const QString &propertyName = d->properties.at(column).name; const QJsonObject &element = d->elements.at(row); - if (element.isEmpty()) + if (typeAt(column) == DataType::Unknown || element.isEmpty() + || data(row, column) == QVariant::fromValue(nullptr)) { return DataTypeWarning::Warning::None; + } if (element.contains(propertyName) && typeAt(column) != typeAt(row, column)) return DataTypeWarning::Warning::CellDataTypeMismatch; @@ -458,16 +481,19 @@ void CollectionDetails::resetPropertyTypes() resetPropertyType(property); } -QString CollectionDetails::getCollectionAsJsonString() const +QJsonArray CollectionDetails::getCollectionAsJsonArray() const { QJsonArray collectionArray; for (const QJsonObject &element : std::as_const(d->elements)) collectionArray.push_back(element); - QString collectionString = QString::fromUtf8(QJsonDocument(collectionArray).toJson()); + return collectionArray; +} - return collectionString; +QString CollectionDetails::getCollectionAsJsonString() const +{ + return QString::fromUtf8(QJsonDocument(getCollectionAsJsonArray()).toJson()); } QString CollectionDetails::getCollectionAsCsvString() const diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.h b/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.h index c35068ce6f..9d8eb7eca0 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.h +++ b/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.h @@ -70,7 +70,7 @@ public: void resetDetails(const QStringList &propertyNames, const QList<QJsonObject> &elements, - CollectionEditor::SourceFormat format); + CollectionEditorConstants::SourceFormat format); void insertColumn(const QString &propertyName, int colIdx = -1, const QVariant &defaultValue = {}, @@ -86,7 +86,7 @@ public: bool setPropertyType(int column, DataType type); CollectionReference reference() const; - CollectionEditor::SourceFormat sourceFormat() const; + CollectionEditorConstants::SourceFormat sourceFormat() const; QVariant data(int row, int column) const; QString propertyAt(int column) const; DataType typeAt(int column) const; @@ -106,6 +106,8 @@ public: QString getCollectionAsJsonString() const; QString getCollectionAsCsvString() const; + QJsonArray getCollectionAsJsonArray() const; + static void registerDeclarativeType(); CollectionDetails &operator=(const CollectionDetails &other); diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.cpp index 2279b60d13..52d5a6e2ba 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.cpp @@ -7,11 +7,16 @@ #include "collectioneditorutils.h" #include "modelnode.h" +#include <coreplugin/editormanager/editormanager.h> + +#include <utils/fileutils.h> #include <utils/qtcassert.h> +#include <utils/textfileformat.h> #include <QFile> #include <QFileInfo> #include <QJsonArray> +#include <QJsonDocument> #include <QJsonObject> #include <QJsonParseError> @@ -19,17 +24,27 @@ namespace { QStringList getJsonHeaders(const QJsonArray &collectionArray) { - QSet<QString> result; + QSet<QString> resultSet; + QList<QString> result; + for (const QJsonValue &value : collectionArray) { if (value.isObject()) { const QJsonObject object = value.toObject(); - const QStringList headers = object.toVariantMap().keys(); - for (const QString &header : headers) - result.insert(header); + QJsonObject::ConstIterator element = object.constBegin(); + const QJsonObject::ConstIterator stopItem = object.constEnd(); + + while (element != stopItem) { + const QString property = element.key(); + if (!resultSet.contains(property)) { + result.append(property); + resultSet.insert(property); + } + ++element; + } } } - return result.values(); + return result; } class CollectionDataTypeHelper @@ -339,8 +354,10 @@ bool CollectionDetailsModel::setPropertyType(int column, const QString &newValue newValue)); if (changed) { emit headerDataChanged(Qt::Horizontal, column, column); - emit dataChanged(index(0, column), index(rowCount() - 1, column), - {Qt::DisplayRole, DataTypeRole, DataTypeWarningRole}); + emit dataChanged( + index(0, column), + index(rowCount() - 1, column), + {Qt::DisplayRole, Qt::EditRole, DataTypeRole, DataTypeWarningRole, ColumnDataTypeRole}); } return changed; @@ -353,8 +370,8 @@ bool CollectionDetailsModel::selectRow(int row) const int rows = rowCount(); - if (m_selectedRow >= rows) - return false; + if (row >= rows) + row = rows - 1; selectColumn(-1); @@ -388,7 +405,7 @@ QStringList CollectionDetailsModel::typesList() void CollectionDetailsModel::loadCollection(const ModelNode &sourceNode, const QString &collection) { - QString fileName = CollectionEditor::getSourceCollectionPath(sourceNode); + QString fileName = CollectionEditorUtils::getSourceCollectionPath(sourceNode); CollectionReference newReference{sourceNode, collection}; bool alreadyOpen = m_openedCollections.contains(newReference); @@ -403,21 +420,83 @@ void CollectionDetailsModel::loadCollection(const ModelNode &sourceNode, const Q } else { deselectAll(); switchToCollection(newReference); - if (sourceNode.type() == CollectionEditor::JSONCOLLECTIONMODEL_TYPENAME) + if (sourceNode.type() == CollectionEditorConstants::JSONCOLLECTIONMODEL_TYPENAME) loadJsonCollection(fileName, collection); - else if (sourceNode.type() == CollectionEditor::CSVCOLLECTIONMODEL_TYPENAME) + else if (sourceNode.type() == CollectionEditorConstants::CSVCOLLECTIONMODEL_TYPENAME) loadCsvCollection(fileName, collection); } } -bool CollectionDetailsModel::saveCurrentCollection() +bool CollectionDetailsModel::saveDataStoreCollections() { - return saveCollection({}, &m_currentCollection); + const ModelNode node = m_currentCollection.reference().node; + const Utils::FilePath path = CollectionEditorUtils::dataStoreJsonFilePath(); + Utils::FileReader fileData; + Utils::FileSaver sourceFile(path); + + if (!fileData.fetch(path)) { + qWarning() << Q_FUNC_INFO << "Cannot read the json file:" << fileData.errorString(); + return false; + } + + QJsonParseError jpe; + QJsonDocument document = QJsonDocument::fromJson(fileData.data(), &jpe); + + if (jpe.error == QJsonParseError::NoError) { + QJsonObject obj = document.object(); + + QList<CollectionDetails> collectionsToBeSaved; + for (CollectionDetails &openedCollection : m_openedCollections) { + const CollectionReference reference = openedCollection.reference(); + if (reference.node == node) { + obj.insert(reference.name, openedCollection.getCollectionAsJsonArray()); + collectionsToBeSaved << openedCollection; + } + } + + document.setObject(obj); + bool saved = sourceFile.write(document.toJson()); + saved &= sourceFile.finalize(); + + if (saved) { + const CollectionReference currentReference = m_currentCollection.reference(); + for (CollectionDetails &collection : collectionsToBeSaved) { + collection.markSaved(); + const CollectionReference reference = collection.reference(); + if (reference != currentReference) + closeCollectionIfSaved(reference); + } + return true; + } + } + return false; } -bool CollectionDetailsModel::exportCollection(const QString &filePath) +bool CollectionDetailsModel::exportCollection(const QUrl &url) { - return saveCollection(filePath, &m_currentCollection); + using Core::EditorManager; + using Utils::FilePath; + using Utils::TextFileFormat; + + QTC_ASSERT(m_currentCollection.isValid(), return false); + + bool saved = false; + const FilePath filePath = FilePath::fromUserInput(url.isLocalFile() ? url.toLocalFile() + : url.toString()); + const QString saveFormat = filePath.toFileInfo().suffix().toLower(); + const QString content = saveFormat == "csv" ? m_currentCollection.getCollectionAsCsvString() + : m_currentCollection.getCollectionAsJsonString(); + + TextFileFormat textFileFormat; + textFileFormat.codec = EditorManager::defaultTextCodec(); + textFileFormat.lineTerminationMode = EditorManager::defaultLineEnding(); + QString errorMessage; + saved = textFileFormat.writeFile(filePath, content, &errorMessage); + + if (!saved) + qWarning() << Q_FUNC_INFO << "Unable to write file" << errorMessage; + + return saved; } void CollectionDetailsModel::updateEmpty() @@ -453,19 +532,19 @@ void CollectionDetailsModel::closeCollectionIfSaved(const CollectionReference &c if (!collectionDetails.isChanged()) m_openedCollections.remove(collection); - - m_currentCollection = CollectionDetails{}; } void CollectionDetailsModel::closeCurrentCollectionIfSaved() { - if (m_currentCollection.isValid()) + if (m_currentCollection.isValid()) { closeCollectionIfSaved(m_currentCollection.reference()); + m_currentCollection = CollectionDetails{}; + } } void CollectionDetailsModel::loadJsonCollection(const QString &source, const QString &collection) { - using CollectionEditor::SourceFormat; + using CollectionEditorConstants::SourceFormat; QFile sourceFile(source); QJsonArray collectionNodes; @@ -510,7 +589,7 @@ void CollectionDetailsModel::loadJsonCollection(const QString &source, const QSt void CollectionDetailsModel::loadCsvCollection(const QString &source, [[maybe_unused]] const QString &collectionName) { - using CollectionEditor::SourceFormat; + using CollectionEditorConstants::SourceFormat; QFile sourceFile(source); QStringList headers; @@ -594,49 +673,6 @@ void CollectionDetailsModel::setCollectionName(const QString &newCollectionName) } } -bool CollectionDetailsModel::saveCollection(const QString &filePath, CollectionDetails *collection) -{ - bool saved = false; - - auto saveSingleCollection = [&](CollectionDetails &singleCollection) { - - const ModelNode node = singleCollection.reference().node; - QString path = CollectionEditor::getSourceCollectionPath(node); - QString saveFormat = CollectionEditor::getSourceCollectionType(node); - - if (!filePath.isEmpty()) { - QUrl url(filePath); - path = url.isLocalFile() ? QFileInfo(url.toLocalFile()).absoluteFilePath() : url.toString(); - saveFormat = url.isLocalFile() ? QFileInfo(url.toLocalFile()).suffix().toLower() : saveFormat; - } - - saved = saveCollectionFromString(path, (saveFormat == "json") ? singleCollection.getCollectionAsJsonString() : - (saveFormat == "csv") ? singleCollection.getCollectionAsCsvString() : QString()); - - if (saved && filePath.isEmpty()) - singleCollection.markSaved(); - }; - - if (!collection) { - for (CollectionDetails &openedCollection : m_openedCollections) - saveSingleCollection(openedCollection); - } else { - saveSingleCollection(*collection); - } - - return saved; -} - -bool CollectionDetailsModel::saveCollectionFromString(const QString &path, const QString &content) -{ - QFile file(path); - - if (file.open(QFile::WriteOnly) && file.write(content.toUtf8())) - return true; - - return false; -} - QString CollectionDetailsModel::warningToString(DataTypeWarning::Warning warning) const { return DataTypeWarning::getDataTypeWarningString(warning); diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.h b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.h index bf87d3838f..4fef84a3df 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.h +++ b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.h @@ -62,8 +62,8 @@ public: void loadCollection(const ModelNode &sourceNode, const QString &collection); - Q_INVOKABLE bool saveCurrentCollection(); - Q_INVOKABLE bool exportCollection(const QString &filePath); + Q_INVOKABLE bool saveDataStoreCollections(); + Q_INVOKABLE bool exportCollection(const QUrl &url); signals: void collectionNameChanged(const QString &collectionName); @@ -81,8 +81,6 @@ private: void setCollectionName(const QString &newCollectionName); void loadJsonCollection(const QString &source, const QString &collection); void loadCsvCollection(const QString &source, const QString &collectionName); - bool saveCollection(const QString &filePath = {}, CollectionDetails *collection = nullptr); - bool saveCollectionFromString(const QString &path, const QString &content); QVariant variantFromString(const QString &value); QHash<CollectionReference, CollectionDetails> m_openedCollections; diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailssortfiltermodel.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailssortfiltermodel.cpp index 50fcadd494..f56bb36e88 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailssortfiltermodel.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailssortfiltermodel.cpp @@ -80,7 +80,9 @@ bool CollectionDetailsSortFilterModel::lessThan(const QModelIndex &sourceleft, if (sourceleft.column() == sourceRight.column()) { int column = sourceleft.column(); CollectionDetails::DataType columnType = m_source->propertyDataType(column); - return CollectionEditor::variantIslessThan(sourceleft.data(), sourceRight.data(), columnType); + return CollectionEditorUtils::variantIslessThan(sourceleft.data(), + sourceRight.data(), + columnType); } return false; diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorconstants.h b/src/plugins/qmldesigner/components/collectioneditor/collectioneditorconstants.h index e914891de3..a591719d87 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorconstants.h +++ b/src/plugins/qmldesigner/components/collectioneditor/collectioneditorconstants.h @@ -3,7 +3,7 @@ #pragma once -namespace QmlDesigner::CollectionEditor { +namespace QmlDesigner::CollectionEditorConstants { enum class SourceFormat { Unknown, Json, Csv }; @@ -17,4 +17,4 @@ inline constexpr char CSVCOLLECTIONMODEL_TYPENAME[] = "QtQuick.Studio.Ut inline constexpr char JSONCOLLECTIONCHILDMODEL_TYPENAME[] = "QtQuick.Studio.Utils.ChildListModel"; inline constexpr char JSONBACKEND_TYPENAME[] = "JsonData"; -} // namespace QmlDesigner::CollectionEditor +} // namespace QmlDesigner::CollectionEditorConstants diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.cpp index f48b6547ae..33867228d5 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.cpp @@ -18,7 +18,10 @@ #include <QColor> #include <QJsonArray> +#include <QJsonDocument> #include <QJsonObject> +#include <QJsonParseError> +#include <QJsonValue> #include <QUrl> namespace { @@ -104,32 +107,32 @@ inline Utils::FilePath qmlDirFilePath() } // namespace -namespace QmlDesigner::CollectionEditor { +namespace QmlDesigner::CollectionEditorUtils { bool variantIslessThan(const QVariant &a, const QVariant &b, CollectionDetails::DataType type) { return std::visit(LessThanVisitor{}, valueToVariant(a, type), valueToVariant(b, type)); } -SourceFormat getSourceCollectionFormat(const ModelNode &node) +CollectionEditorConstants::SourceFormat getSourceCollectionFormat(const ModelNode &node) { using namespace QmlDesigner; - if (node.type() == CollectionEditor::JSONCOLLECTIONMODEL_TYPENAME) - return CollectionEditor::SourceFormat::Json; + if (node.type() == CollectionEditorConstants::JSONCOLLECTIONMODEL_TYPENAME) + return CollectionEditorConstants::SourceFormat::Json; - if (node.type() == CollectionEditor::CSVCOLLECTIONMODEL_TYPENAME) - return CollectionEditor::SourceFormat::Csv; + if (node.type() == CollectionEditorConstants::CSVCOLLECTIONMODEL_TYPENAME) + return CollectionEditorConstants::SourceFormat::Csv; - return CollectionEditor::SourceFormat::Unknown; + return CollectionEditorConstants::SourceFormat::Unknown; } QString getSourceCollectionType(const ModelNode &node) { using namespace QmlDesigner; - if (node.type() == CollectionEditor::JSONCOLLECTIONMODEL_TYPENAME) + if (node.type() == CollectionEditorConstants::JSONCOLLECTIONMODEL_TYPENAME) return "json"; - if (node.type() == CollectionEditor::CSVCOLLECTIONMODEL_TYPENAME) + if (node.type() == CollectionEditorConstants::CSVCOLLECTIONMODEL_TYPENAME) return "csv"; return {}; @@ -159,6 +162,20 @@ bool canAcceptCollectionAsModel(const ModelNode &node) && modelProperty.propertyType().isVariant(); } +bool hasTextRoleProperty(const ModelNode &node) +{ + const NodeMetaInfo nodeMetaInfo = node.metaInfo(); + if (!nodeMetaInfo.isValid()) + return false; + + const PropertyMetaInfo textRoleProperty = nodeMetaInfo.property("textRole"); + if (!textRoleProperty.isValid()) + return false; + + return textRoleProperty.isWritable() && !textRoleProperty.isPrivate() + && textRoleProperty.propertyType().isString(); +} + QString getSourceCollectionPath(const ModelNode &dataStoreNode) { using Utils::FilePath; @@ -280,4 +297,178 @@ bool ensureDataStoreExists(bool &justCreated) return false; } -} // namespace QmlDesigner::CollectionEditor +QJsonArray loadAsSingleJsonCollection(const QUrl &url) +{ + QFile file(url.isLocalFile() ? url.toLocalFile() : url.toString()); + QJsonArray collection; + QByteArray jsonData; + if (file.open(QFile::ReadOnly)) + jsonData = file.readAll(); + + file.close(); + if (jsonData.isEmpty()) + return {}; + + QJsonParseError parseError; + QJsonDocument document = QJsonDocument::fromJson(jsonData, &parseError); + if (parseError.error != QJsonParseError::NoError) + return {}; + + auto refineJsonArray = [](const QJsonArray &array) -> QJsonArray { + QJsonArray resultArray; + for (const QJsonValue &collectionData : array) { + if (collectionData.isObject()) { + QJsonObject rowObject = collectionData.toObject(); + const QStringList rowKeys = rowObject.keys(); + for (const QString &key : rowKeys) { + QJsonValue cellValue = rowObject.value(key); + if (cellValue.isArray()) + rowObject.remove(key); + } + resultArray.push_back(rowObject); + } + } + return resultArray; + }; + + if (document.isArray()) { + collection = refineJsonArray(document.array()); + } else if (document.isObject()) { + QJsonObject documentObject = document.object(); + const QStringList mainKeys = documentObject.keys(); + + bool arrayFound = false; + for (const QString &key : mainKeys) { + const QJsonValue &value = documentObject.value(key); + if (value.isArray()) { + arrayFound = true; + collection = refineJsonArray(value.toArray()); + break; + } + } + + if (!arrayFound) { + QJsonObject singleObject; + for (const QString &key : mainKeys) { + const QJsonValue value = documentObject.value(key); + + if (!value.isObject()) + singleObject.insert(key, value); + } + collection.push_back(singleObject); + } + } + return collection; +} + +QJsonArray loadAsCsvCollection(const QUrl &url) +{ + QFile sourceFile(url.isLocalFile() ? url.toLocalFile() : url.toString()); + QStringList headers; + QJsonArray elements; + + if (sourceFile.open(QFile::ReadOnly)) { + QTextStream stream(&sourceFile); + + if (!stream.atEnd()) + headers = stream.readLine().split(','); + + for (QString &header : headers) + header = header.trimmed(); + + if (!headers.isEmpty()) { + while (!stream.atEnd()) { + const QStringList recordDataList = stream.readLine().split(','); + int column = -1; + QJsonObject recordData; + for (const QString &cellData : recordDataList) { + if (++column == headers.size()) + break; + recordData.insert(headers.at(column), cellData); + } + elements.append(recordData); + } + } + } + + return elements; +} + +QString getFirstColumnName(const QString &collectionName) +{ + Utils::FilePath dataStorePath = CollectionEditorUtils::dataStoreJsonFilePath(); + + if (!dataStorePath.exists()) + return {}; + + Utils::FileReader dataStoreFile; + if (!dataStoreFile.fetch(dataStorePath)) + return {}; + + QJsonParseError jsonError; + QJsonDocument dataStoreDocument = QJsonDocument::fromJson(dataStoreFile.data(), &jsonError); + if (jsonError.error == QJsonParseError::NoError) { + QJsonObject rootObject = dataStoreDocument.object(); + if (rootObject.contains(collectionName)) { + QJsonArray collectionArray = rootObject.value(collectionName).toArray(); + for (const QJsonValue &elementValue : std::as_const(collectionArray)) { + const QJsonObject elementObject = elementValue.toObject(); + QJsonObject::ConstIterator element = elementObject.constBegin(); + if (element != elementObject.constEnd()) + return element.key(); + } + } else { + qWarning() << Q_FUNC_INFO << __LINE__ + << QString("Collection \"%1\" not found.").arg(collectionName); + } + } else { + qWarning() << Q_FUNC_INFO << __LINE__ << "Problem in reading json file." + << jsonError.errorString(); + } + + return {}; +} + +bool collectionHasColumn(const QString &collectionName, const QString &columnName) +{ + Utils::FilePath dataStorePath = CollectionEditorUtils::dataStoreJsonFilePath(); + + if (!dataStorePath.exists()) + return false; + + Utils::FileReader dataStoreFile; + if (!dataStoreFile.fetch(dataStorePath)) + return false; + + QJsonParseError jsonError; + QJsonDocument dataStoreDocument = QJsonDocument::fromJson(dataStoreFile.data(), &jsonError); + if (jsonError.error == QJsonParseError::NoError) { + QJsonObject rootObject = dataStoreDocument.object(); + if (rootObject.contains(collectionName)) { + QJsonArray collectionArray = rootObject.value(collectionName).toArray(); + for (const QJsonValue &elementValue : std::as_const(collectionArray)) { + const QJsonObject elementObject = elementValue.toObject(); + QJsonObject::ConstIterator element = elementObject.constBegin(); + const QJsonObject::ConstIterator stopItem = elementObject.constEnd(); + + while (element != stopItem) { + const QString keyName = element.key(); + ++element; + + if (columnName == keyName) + return true; + } + } + } else { + qWarning() << Q_FUNC_INFO << __LINE__ + << QString("Collection \"%1\" not found.").arg(collectionName); + } + } else { + qWarning() << Q_FUNC_INFO << __LINE__ << "Problem in reading json file." + << jsonError.errorString(); + } + + return false; +} + +} // namespace QmlDesigner::CollectionEditorUtils diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.h b/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.h index 036304a381..46429f04b6 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.h +++ b/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.h @@ -14,11 +14,11 @@ namespace Utils { class FilePath; } -namespace QmlDesigner::CollectionEditor { +namespace QmlDesigner::CollectionEditorUtils { bool variantIslessThan(const QVariant &a, const QVariant &b, CollectionDetails::DataType type); -SourceFormat getSourceCollectionFormat(const QmlDesigner::ModelNode &node); +CollectionEditorConstants::SourceFormat getSourceCollectionFormat(const QmlDesigner::ModelNode &node); QString getSourceCollectionType(const QmlDesigner::ModelNode &node); @@ -34,6 +34,16 @@ bool ensureDataStoreExists(bool &justCreated); bool canAcceptCollectionAsModel(const ModelNode &node); +bool hasTextRoleProperty(const ModelNode &node); + QJsonArray defaultCollectionArray(); -} // namespace QmlDesigner::CollectionEditor +QJsonArray loadAsSingleJsonCollection(const QUrl &url); + +QJsonArray loadAsCsvCollection(const QUrl &url); + +QString getFirstColumnName(const QString &collectionName); + +bool collectionHasColumn(const QString &collectionName, const QString &columnName); + +} // namespace QmlDesigner::CollectionEditorUtils diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionimporttools.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionimporttools.cpp deleted file mode 100644 index 183730873d..0000000000 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionimporttools.cpp +++ /dev/null @@ -1,113 +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 "collectionimporttools.h" - -#include <QFile> -#include <QJsonArray> -#include <QJsonDocument> -#include <QJsonObject> -#include <QJsonParseError> -#include <QJsonValue> -#include <QUrl> - -namespace QmlDesigner::CollectionEditor::ImportTools { - -QJsonArray loadAsSingleJsonCollection(const QUrl &url) -{ - QFile file(url.isLocalFile() ? url.toLocalFile() : url.toString()); - QJsonArray collection; - QByteArray jsonData; - if (file.open(QFile::ReadOnly)) - jsonData = file.readAll(); - - file.close(); - if (jsonData.isEmpty()) - return {}; - - QJsonParseError parseError; - QJsonDocument document = QJsonDocument::fromJson(jsonData, &parseError); - if (parseError.error != QJsonParseError::NoError) - return {}; - - auto refineJsonArray = [](const QJsonArray &array) -> QJsonArray { - QJsonArray resultArray; - for (const QJsonValue &collectionData : array) { - if (collectionData.isObject()) { - QJsonObject rowObject = collectionData.toObject(); - const QStringList rowKeys = rowObject.keys(); - for (const QString &key : rowKeys) { - QJsonValue cellValue = rowObject.value(key); - if (cellValue.isArray()) - rowObject.remove(key); - } - resultArray.push_back(rowObject); - } - } - return resultArray; - }; - - if (document.isArray()) { - collection = refineJsonArray(document.array()); - } else if (document.isObject()) { - QJsonObject documentObject = document.object(); - const QStringList mainKeys = documentObject.keys(); - - bool arrayFound = false; - for (const QString &key : mainKeys) { - const QJsonValue &value = documentObject.value(key); - if (value.isArray()) { - arrayFound = true; - collection = refineJsonArray(value.toArray()); - break; - } - } - - if (!arrayFound) { - QJsonObject singleObject; - for (const QString &key : mainKeys) { - const QJsonValue value = documentObject.value(key); - - if (!value.isObject()) - singleObject.insert(key, value); - } - collection.push_back(singleObject); - } - } - return collection; -} - -QJsonArray loadAsCsvCollection(const QUrl &url) -{ - QFile sourceFile(url.isLocalFile() ? url.toLocalFile() : url.toString()); - QStringList headers; - QJsonArray elements; - - if (sourceFile.open(QFile::ReadOnly)) { - QTextStream stream(&sourceFile); - - if (!stream.atEnd()) - headers = stream.readLine().split(','); - - for (QString &header : headers) - header = header.trimmed(); - - if (!headers.isEmpty()) { - while (!stream.atEnd()) { - const QStringList recordDataList = stream.readLine().split(','); - int column = -1; - QJsonObject recordData; - for (const QString &cellData : recordDataList) { - if (++column == headers.size()) - break; - recordData.insert(headers.at(column), cellData); - } - elements.append(recordData); - } - } - } - - return elements; -} - -} // namespace QmlDesigner::CollectionEditor::ImportTools diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionimporttools.h b/src/plugins/qmldesigner/components/collectioneditor/collectionimporttools.h deleted file mode 100644 index 6ee4f590f7..0000000000 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionimporttools.h +++ /dev/null @@ -1,18 +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 - -#pragma once - -#include <QtCore/qtconfigmacros.h> - -QT_BEGIN_NAMESPACE -class QJsonArray; -class QUrl; -QT_END_NAMESPACE - -namespace QmlDesigner::CollectionEditor::ImportTools { - -QJsonArray loadAsSingleJsonCollection(const QUrl &url); -QJsonArray loadAsCsvCollection(const QUrl &url); - -} // namespace QmlDesigner::CollectionEditor::ImportTools diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionlistmodel.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionlistmodel.cpp index d910569f0d..214ece078a 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionlistmodel.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionlistmodel.cpp @@ -29,7 +29,7 @@ namespace QmlDesigner { CollectionListModel::CollectionListModel(const ModelNode &sourceModel) : QStringListModel() , m_sourceNode(sourceModel) - , m_sourceType(CollectionEditor::getSourceCollectionType(sourceModel)) + , m_sourceType(CollectionEditorUtils::getSourceCollectionType(sourceModel)) { connect(this, &CollectionListModel::modelReset, this, &CollectionListModel::updateEmpty); connect(this, &CollectionListModel::rowsRemoved, this, &CollectionListModel::updateEmpty); @@ -87,8 +87,11 @@ bool CollectionListModel::removeRows(int row, int count, const QModelIndex &pare QStringList removedCollections = stringList().mid(row, count); bool itemsRemoved = Super::removeRows(row, count, parent); - if (itemsRemoved) + if (itemsRemoved) { emit collectionsRemoved(removedCollections); + if (m_selectedIndex >= row) + selectCollectionIndex(m_selectedIndex - count, true); + } return itemsRemoved; } @@ -121,7 +124,7 @@ ModelNode CollectionListModel::sourceNode() const QString CollectionListModel::sourceAddress() const { - return CollectionEditor::getSourceCollectionPath(m_sourceNode); + return CollectionEditorUtils::getSourceCollectionPath(m_sourceNode); } bool CollectionListModel::contains(const QString &collectionName) const diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.cpp index 0c57f18778..1f9c7aa5d7 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.cpp @@ -25,7 +25,8 @@ QSharedPointer<QmlDesigner::CollectionListModel> loadCollection( const QmlDesigner::ModelNode &sourceNode, QSharedPointer<QmlDesigner::CollectionListModel> initialCollection = {}) { - using namespace QmlDesigner::CollectionEditor; + using namespace QmlDesigner::CollectionEditorConstants; + using namespace QmlDesigner::CollectionEditorUtils; QString sourceFileAddress = getSourceCollectionPath(sourceNode); QSharedPointer<QmlDesigner::CollectionListModel> collectionsList; @@ -88,9 +89,9 @@ QVariant CollectionSourceModel::data(const QModelIndex &index, int role) const case NodeRole: return QVariant::fromValue(*collectionSource); case CollectionTypeRole: - return CollectionEditor::getSourceCollectionType(*collectionSource); + return CollectionEditorUtils::getSourceCollectionType(*collectionSource); case SourceRole: - return collectionSource->variantProperty(CollectionEditor::SOURCEFILE_PROPERTY).value(); + return collectionSource->variantProperty(CollectionEditorConstants::SOURCEFILE_PROPERTY).value(); case SelectedRole: return index.row() == m_selectedIndex; case CollectionsRole: @@ -116,7 +117,8 @@ bool CollectionSourceModel::setData(const QModelIndex &index, const QVariant &va collectionName.setValue(value.toString()); } break; case SourceRole: { - auto sourceAddress = collectionSource.variantProperty(CollectionEditor::SOURCEFILE_PROPERTY); + auto sourceAddress = collectionSource.variantProperty( + CollectionEditorConstants::SOURCEFILE_PROPERTY); if (sourceAddress.value() == value) return false; @@ -281,13 +283,13 @@ bool CollectionSourceModel::addCollectionToSource(const ModelNode &node, if (idx < 0) return returnError(tr("Node is not indexed in the models.")); - if (node.type() != CollectionEditor::JSONCOLLECTIONMODEL_TYPENAME) + if (node.type() != CollectionEditorConstants::JSONCOLLECTIONMODEL_TYPENAME) return returnError(tr("Node should be a JSON model.")); if (collectionExists(node, collectionName)) return returnError(tr("A model with the identical name already exists.")); - QString sourceFileAddress = CollectionEditor::getSourceCollectionPath(node); + QString sourceFileAddress = CollectionEditorUtils::getSourceCollectionPath(node); QFileInfo sourceFileInfo(sourceFileAddress); if (!sourceFileInfo.isFile()) @@ -325,6 +327,7 @@ bool CollectionSourceModel::addCollectionToSource(const ModelNode &node, 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.")); @@ -403,9 +406,11 @@ void CollectionSourceModel::onSelectedCollectionChanged(CollectionListModel *col m_previousSelectedList = collectionList; - emit collectionSelected(collectionList->collectionNameAt(collectionIndex)); + setSelectedCollectionName(collectionList->collectionNameAt(collectionIndex)); selectSourceIndex(sourceIndex(collectionList->sourceNode())); + } else { + setSelectedCollectionName({}); } } @@ -413,7 +418,7 @@ void CollectionSourceModel::onCollectionNameChanged(CollectionListModel *collect const QString &oldName, const QString &newName) { auto emitRenameWarning = [this](const QString &msg) -> void { - emit this->warning(tr("Rename Model"), msg); + emit warning(tr("Rename Model"), msg); }; const ModelNode node = collectionList->sourceNode(); @@ -424,16 +429,16 @@ void CollectionSourceModel::onCollectionNameChanged(CollectionListModel *collect return; } - if (node.type() == CollectionEditor::CSVCOLLECTIONMODEL_TYPENAME) { + if (node.type() == CollectionEditorConstants::CSVCOLLECTIONMODEL_TYPENAME) { if (!setData(nodeIndex, newName, NameRole)) emitRenameWarning(tr("Can't rename the node")); return; - } else if (node.type() != CollectionEditor::JSONCOLLECTIONMODEL_TYPENAME) { + } else if (node.type() != CollectionEditorConstants::JSONCOLLECTIONMODEL_TYPENAME) { emitRenameWarning(tr("Invalid node type")); return; } - QString sourceFileAddress = CollectionEditor::getSourceCollectionPath(node); + QString sourceFileAddress = CollectionEditorUtils::getSourceCollectionPath(node); QFileInfo sourceFileInfo(sourceFileAddress); if (!sourceFileInfo.isFile()) { @@ -513,15 +518,15 @@ void CollectionSourceModel::onCollectionsRemoved(CollectionListModel *collection return; } - if (node.type() == CollectionEditor::CSVCOLLECTIONMODEL_TYPENAME) { + if (node.type() == CollectionEditorConstants::CSVCOLLECTIONMODEL_TYPENAME) { removeSource(node); return; - } else if (node.type() != CollectionEditor::JSONCOLLECTIONMODEL_TYPENAME) { + } else if (node.type() != CollectionEditorConstants::JSONCOLLECTIONMODEL_TYPENAME) { emitDeleteWarning(tr("Invalid node type")); return; } - QString sourceFileAddress = CollectionEditor::getSourceCollectionPath(node); + QString sourceFileAddress = CollectionEditorUtils::getSourceCollectionPath(node); QFileInfo sourceFileInfo(sourceFileAddress); if (!sourceFileInfo.isFile()) { @@ -575,9 +580,11 @@ void CollectionSourceModel::onCollectionsRemoved(CollectionListModel *collection } for (const QString &collectionName : std::as_const(collectionsRemovedFromDocument)) - emit this->collectionRemoved(collectionName); + emit collectionRemoved(collectionName); updateCollectionList(nodeIndex); + if (m_previousSelectedList == collectionList) + onSelectedCollectionChanged(collectionList, collectionList->selectedIndex()); } } @@ -607,12 +614,20 @@ void CollectionSourceModel::setSelectedIndex(int idx) } else if (m_previousSelectedList) { m_previousSelectedList->selectCollectionIndex(-1); m_previousSelectedList = {}; - emit this->collectionSelected({}); + 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(); diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.h b/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.h index 487b616b97..5ab77f2a98 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.h +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.h @@ -87,6 +87,7 @@ private slots: private: void setSelectedIndex(int idx); + void setSelectedCollectionName(const QString &collectionName); void updateEmpty(); void updateCollectionList(QModelIndex index); void registerCollection(const QSharedPointer<CollectionListModel> &collection); @@ -98,6 +99,7 @@ private: 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; }; diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp index f17abf5d9f..7f3ee7192a 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp @@ -31,7 +31,7 @@ namespace { inline bool isStudioCollectionModel(const QmlDesigner::ModelNode &node) { - using namespace QmlDesigner::CollectionEditor; + using namespace QmlDesigner::CollectionEditorConstants; return node.metaInfo().typeName() == JSONCOLLECTIONMODEL_TYPENAME || node.metaInfo().typeName() == CSVCOLLECTIONMODEL_TYPENAME; } @@ -151,7 +151,7 @@ void CollectionView::variantPropertiesChanged(const QList<VariantProperty> &prop if (isStudioCollectionModel(node)) { if (property.name() == "objectName") m_widget->sourceModel()->updateNodeName(node); - else if (property.name() == CollectionEditor::SOURCEFILE_PROPERTY) + else if (property.name() == CollectionEditorConstants::SOURCEFILE_PROPERTY) m_widget->sourceModel()->updateNodeSource(node); } } @@ -169,7 +169,8 @@ void CollectionView::selectedNodesChanged(const QList<ModelNode> &selectedNodeLi bool singleSelectedHasModelProperty = false; if (singleNonCollectionNodeSelected) { const ModelNode selectedNode = selectedNodeList.first(); - singleSelectedHasModelProperty = CollectionEditor::canAcceptCollectionAsModel(selectedNode); + singleSelectedHasModelProperty = CollectionEditorUtils::canAcceptCollectionAsModel( + selectedNode); } m_widget->setTargetNodeSelected(singleSelectedHasModelProperty); @@ -204,7 +205,7 @@ void CollectionView::addResource(const QUrl &url, const QString &name, const QSt resourceMetaInfo.majorVersion(), resourceMetaInfo.minorVersion()); VariantProperty sourceProperty = resourceNode.variantProperty( - CollectionEditor::SOURCEFILE_PROPERTY); + CollectionEditorConstants::SOURCEFILE_PROPERTY); VariantProperty nameProperty = resourceNode.variantProperty("objectName"); sourceProperty.setValue(sourceAddress); nameProperty.setValue(name); @@ -252,18 +253,18 @@ void CollectionView::refreshModel() NodeMetaInfo CollectionView::jsonCollectionMetaInfo() const { - return model()->metaInfo(CollectionEditor::JSONCOLLECTIONMODEL_TYPENAME); + return model()->metaInfo(CollectionEditorConstants::JSONCOLLECTIONMODEL_TYPENAME); } NodeMetaInfo CollectionView::csvCollectionMetaInfo() const { - return model()->metaInfo(CollectionEditor::CSVCOLLECTIONMODEL_TYPENAME); + return model()->metaInfo(CollectionEditorConstants::CSVCOLLECTIONMODEL_TYPENAME); } void CollectionView::ensureStudioModelImport() { executeInTransaction(__FUNCTION__, [&] { - Import import = Import::createLibraryImport(CollectionEditor::COLLECTIONMODEL_IMPORT); + Import import = Import::createLibraryImport(CollectionEditorConstants::COLLECTIONMODEL_IMPORT); try { if (!model()->hasImport(import, true, true)) model()->changeImports({import}, {}); diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp index 9b14c2cd03..39097cbeb3 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp @@ -6,7 +6,6 @@ #include "collectiondetailsmodel.h" #include "collectiondetailssortfiltermodel.h" #include "collectioneditorutils.h" -#include "collectionimporttools.h" #include "collectionsourcemodel.h" #include "collectionview.h" #include "qmldesignerconstants.h" @@ -216,7 +215,7 @@ bool CollectionWidget::addCollection(const QString &collectionName, if (collectionType == "json") { QJsonObject jsonObject; - jsonObject.insert(collectionName, CollectionEditor::defaultCollectionArray()); + jsonObject.insert(collectionName, CollectionEditorUtils::defaultCollectionArray()); QFile sourceFile(sourcePath); if (!sourceFile.open(QFile::WriteOnly)) { @@ -258,10 +257,8 @@ bool CollectionWidget::addCollection(const QString &collectionName, } } else if (collectionType == "json") { QString errorMsg; - bool added = m_sourceModel->addCollectionToSource(node, - collectionName, - CollectionEditor::defaultCollectionArray(), - &errorMsg); + bool added = m_sourceModel->addCollectionToSource( + node, collectionName, CollectionEditorUtils::defaultCollectionArray(), &errorMsg); if (!added) warn(tr("Can not add a model to the JSON file"), errorMsg); return added; @@ -274,10 +271,10 @@ bool CollectionWidget::importToJson(const QVariant &sourceNode, const QString &collectionName, const QUrl &url) { - using CollectionEditor::SourceFormat; + using CollectionEditorConstants::SourceFormat; using Utils::FilePath; const ModelNode node = sourceNode.value<ModelNode>(); - const SourceFormat nodeFormat = CollectionEditor::getSourceCollectionFormat(node); + const SourceFormat nodeFormat = CollectionEditorUtils::getSourceCollectionFormat(node); QTC_ASSERT(node.isValid() && nodeFormat == SourceFormat::Json, return false); FilePath fileInfo = FilePath::fromUserInput(url.isLocalFile() ? url.toLocalFile() @@ -287,9 +284,9 @@ bool CollectionWidget::importToJson(const QVariant &sourceNode, QJsonArray loadedCollection; if (fileInfo.suffix() == "json") - loadedCollection = CollectionEditor::ImportTools::loadAsSingleJsonCollection(url); + loadedCollection = CollectionEditorUtils::loadAsSingleJsonCollection(url); else if (fileInfo.suffix() == "csv") - loadedCollection = CollectionEditor::ImportTools::loadAsCsvCollection(url); + loadedCollection = CollectionEditorUtils::loadAsCsvCollection(url); if (!loadedCollection.isEmpty()) { const QString newCollectionName = generateUniqueCollectionName(node, collectionName); @@ -327,7 +324,7 @@ bool CollectionWidget::addCollectionToDataStore(const QString &collectionName) bool added = m_sourceModel->addCollectionToSource(node, generateUniqueCollectionName(node, collectionName), - CollectionEditor::defaultCollectionArray(), + CollectionEditorUtils::defaultCollectionArray(), &errorMsg); if (!added) warn(tr("Failed to add a model to the default model group"), errorMsg); @@ -343,7 +340,7 @@ void CollectionWidget::assignCollectionToSelectedNode(const QString collectionNa void CollectionWidget::ensureDataStoreExists() { bool filesJustCreated = false; - bool filesExist = CollectionEditor::ensureDataStoreExists(filesJustCreated); + bool filesExist = CollectionEditorUtils::ensureDataStoreExists(filesJustCreated); if (filesExist && filesJustCreated) m_view->resetDataStoreNode(); } diff --git a/src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.cpp b/src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.cpp index bebc60f1de..a1f82bbc65 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 <model.h> #include <nodemetainfo.h> @@ -23,20 +27,25 @@ #include <utils/fileutils.h> #include <utils/qtcassert.h> +#include <QPlainTextEdit> #include <QRegularExpression> #include <QRegularExpressionMatch> +#include <QScopedPointer> namespace { +inline constexpr char CHILDLISTMODEL_TYPENAME[] = "ChildListModel"; + 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"}; + static PropertyNameList defaultsNodeProps = { + "id", + QmlDesigner::CollectionEditorConstants::SOURCEFILE_PROPERTY, + QmlDesigner::CollectionEditorConstants::JSONCHILDMODELNAME_PROPERTY, + "backend"}; PropertyNameList dynamicPropertyNames = Utils::transform( node.dynamicProperties(), [](const AbstractProperty &property) -> PropertyName { return property.name(); }); @@ -49,8 +58,8 @@ QmlDesigner::PropertyNameList createNameList(const QmlDesigner::ModelNode &node) bool isValidCollectionPropertyName(const QString &collectionId) { static const QmlDesigner::PropertyNameList reservedKeywords = { - QmlDesigner::CollectionEditor::SOURCEFILE_PROPERTY, - QmlDesigner::CollectionEditor::JSONBACKEND_TYPENAME, + QmlDesigner::CollectionEditorConstants::SOURCEFILE_PROPERTY, + QmlDesigner::CollectionEditorConstants::JSONBACKEND_TYPENAME, "backend", "models", }; @@ -59,6 +68,58 @@ bool isValidCollectionPropertyName(const QString &collectionId) && !reservedKeywords.contains(collectionId.toLatin1()); } +QMap<QString, QmlDesigner::PropertyName> getModelIdMap(const QmlDesigner::ModelNode &rootNode) +{ + using namespace QmlDesigner; + QMap<QString, PropertyName> modelNameForId; + + const QList<AbstractProperty> 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(CollectionEditorConstants::JSONCHILDMODELNAME_PROPERTY)) { + QString modelName = childNode + .property(CollectionEditorConstants::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<QPlainTextEdit> textEdit(new QPlainTextEdit); + QScopedPointer<NotIndentingTextEditModifier> modifier( + new NotIndentingTextEditModifier(textEdit.data())); + textEdit->hide(); + textEdit->setPlainText(qmlContext); + QmlDesigner::ExternalDependencies externalDependencies{QmlDesignerBasePlugin::settings()}; + QScopedPointer<RewriterView> 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 { @@ -77,15 +138,16 @@ void DataStoreModelNode::reloadModel() } bool forceUpdate = false; - const FilePath dataStoreQmlPath = CollectionEditor::dataStoreQmlFilePath(); - const FilePath dataStoreJsonPath = CollectionEditor::dataStoreJsonFilePath(); + const FilePath dataStoreQmlPath = CollectionEditorUtils::dataStoreQmlFilePath(); + const FilePath dataStoreJsonPath = CollectionEditorUtils::dataStoreJsonFilePath(); 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); + m_model = Model::create(CollectionEditorConstants::JSONCOLLECTIONMODEL_TYPENAME, 1, 1); forceUpdate = true; - Import import = Import::createLibraryImport(CollectionEditor::COLLECTIONMODEL_IMPORT); + Import import = Import::createLibraryImport( + CollectionEditorConstants::COLLECTIONMODEL_IMPORT); try { if (!m_model->hasImport(import, true, true)) m_model->changeImports({import}, {}); @@ -102,8 +164,10 @@ void DataStoreModelNode::reloadModel() m_dataRelativePath = dataStoreJsonPath.relativePathFrom(dataStoreQmlPath).toFSPathString(); - if (forceUpdate) + if (forceUpdate) { + preloadFile(); update(); + } } QStringList DataStoreModelNode::collectionNames() const @@ -143,6 +207,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 +233,6 @@ void DataStoreModelNode::updateDataStoreProperties() ModelNode rootNode = modelNode(); QTC_ASSERT(rootNode.isValid(), return); - static TypeName childNodeTypename = "ChildListModel"; - QSet<QString> collectionNamesToBeAdded; const QStringList allCollectionNames = m_collectionPropertyNames.keys(); for (const QString &collectionName : allCollectionNames) @@ -165,12 +246,13 @@ void DataStoreModelNode::updateDataStoreProperties() continue; NodeProperty nodeProprty = property.toNodeProperty(); - if (!nodeProprty.hasDynamicTypeName(childNodeTypename)) + if (!nodeProprty.hasDynamicTypeName(CHILDLISTMODEL_TYPENAME)) continue; ModelNode childNode = nodeProprty.modelNode(); - if (childNode.hasProperty(CollectionEditor::JSONCHILDMODELNAME_PROPERTY)) { - QString modelName = childNode.property(CollectionEditor::JSONCHILDMODELNAME_PROPERTY) + if (childNode.hasProperty(CollectionEditorConstants::JSONCHILDMODELNAME_PROPERTY)) { + QString modelName = childNode + .property(CollectionEditorConstants::JSONCHILDMODELNAME_PROPERTY) .toVariantProperty() .value() .toString(); @@ -189,32 +271,17 @@ 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); + ModelNode backendNode = model()->createModelNode(CollectionEditorConstants::JSONBACKEND_TYPENAME); NodeProperty backendProperty = rootNode.nodeProperty("backend"); - backendProperty.setDynamicTypeNameAndsetModelNode(CollectionEditor::JSONBACKEND_TYPENAME, + backendProperty.setDynamicTypeNameAndsetModelNode(CollectionEditorConstants::JSONBACKEND_TYPENAME, backendNode); // Source Property - VariantProperty sourceProp = rootNode.variantProperty(CollectionEditor::SOURCEFILE_PROPERTY); + VariantProperty sourceProp = rootNode.variantProperty( + CollectionEditorConstants::SOURCEFILE_PROPERTY); sourceProp.setValue(m_dataRelativePath); } @@ -231,19 +298,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( + CollectionEditorConstants::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(); @@ -300,7 +398,7 @@ void DataStoreModelNode::renameCollection(const QString &oldName, const QString NodeProperty collectionNode = dataStoreNode.property(oldPropertyName).toNodeProperty(); if (collectionNode.isValid()) { VariantProperty modelNameProperty = collectionNode.modelNode().variantProperty( - CollectionEditor::JSONCHILDMODELNAME_PROPERTY); + CollectionEditorConstants::JSONCHILDMODELNAME_PROPERTY); modelNameProperty.setValue(newName); m_collectionPropertyNames.remove(oldName); m_collectionPropertyNames.insert(newName, collectionNode.name()); @@ -333,7 +431,7 @@ void DataStoreModelNode::assignCollectionToNode(AbstractView *view, { QTC_ASSERT(targetNode.isValid(), return); - if (!CollectionEditor::canAcceptCollectionAsModel(targetNode)) + if (!CollectionEditorUtils::canAcceptCollectionAsModel(targetNode)) return; if (!m_collectionPropertyNames.contains(collectionName)) { @@ -352,12 +450,27 @@ void DataStoreModelNode::assignCollectionToNode(AbstractView *view, return; } - BindingProperty modelProperty = targetNode.bindingProperty("model"); - - QString identifier = QString("DataStore.%1").arg(QString::fromLatin1(sourceProperty.name())); - - view->executeInTransaction("assignCollectionToNode", [&modelProperty, &identifier]() { + view->executeInTransaction("assignCollectionToNode", [&]() { + QString identifier = QString("DataStore.%1").arg(QString::fromLatin1(sourceProperty.name())); + BindingProperty modelProperty = targetNode.bindingProperty("model"); modelProperty.setExpression(identifier); + if (CollectionEditorUtils::hasTextRoleProperty(targetNode)) { + VariantProperty textRoleProperty = targetNode.variantProperty("textRole"); + const QVariant currentTextRoleValue = textRoleProperty.value(); + + if (currentTextRoleValue.isValid() && !currentTextRoleValue.isNull()) { + if (currentTextRoleValue.type() == QVariant::String) { + const QString currentTextRole = currentTextRoleValue.toString(); + if (CollectionEditorUtils::collectionHasColumn(collectionName, currentTextRole)) + return; + } else { + return; + } + } + + QString textRoleValue = CollectionEditorUtils::getFirstColumnName(collectionName); + textRoleProperty.setValue(textRoleValue); + } }); } diff --git a/src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.h b/src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.h index 3048fc4fc9..1c855bca7a 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.h +++ b/src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.h @@ -7,6 +7,10 @@ #include <QMap> +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; diff --git a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp index 6ad7ed994b..6e4b1a1efd 100644 --- a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp +++ b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp @@ -11,6 +11,7 @@ #include "formatoperation.h" #include "groupitemaction.h" #include "modelnodecontextmenu_helper.h" +#include "propertytreemodel.h" #include "qmldesignerconstants.h" #include "qmleditormenu.h" #include "rewritingexception.h" @@ -475,35 +476,20 @@ QStringList getSignalsList(const ModelNode &node) if (!node.hasMetaInfo()) return {}; - QStringList signalsList; - NodeMetaInfo nodeMetaInfo = node.metaInfo(); - - for (const auto &signalName : nodeMetaInfo.signalNames()) { - signalsList << QString::fromUtf8(signalName); - } - - //on...Changed are the most regular signals, we assign them the lowest priority, - //we don't need them right now -// QStringList signalsWithChanged = signalsList.filter("Changed"); - - //these are item specific, like MouseArea.clicked, they have higher priority - QStringList signalsWithoutChanged = signalsList; - signalsWithoutChanged.removeIf([](QString str) { - if (str.endsWith("Changed")) - return true; - return false; - }); - - QStringList finalResult; - finalResult.append(signalsWithoutChanged); + QStringList signalList; + std::vector<PropertyName> signalVector = PropertyTreeModel::sortedAndFilteredSignalNames( + node.metaInfo()); - if (finalResult.isEmpty()) - finalResult = signalsList; + std::vector<QString> signalVectorString = Utils::transform(signalVector, + [](const PropertyName &name) { + return QString::fromUtf8(name); + }); - finalResult.removeDuplicates(); + signalList.reserve(Utils::ssize(signalVectorString)); + std::copy(signalVectorString.begin(), signalVectorString.end(), std::back_inserter(signalList)); - return finalResult; + return signalList; } struct SlotEntry diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp index 79ff364c40..dd00cd48d4 100644 --- a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp +++ b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp @@ -1731,7 +1731,7 @@ bool useLayerEffect() QtcSettings *settings = Core::ICore::settings(); const Key layerEffectEntry = "QML/Designer/UseLayerEffect"; - return settings->value(layerEffectEntry, true).toBool(); + return settings->value(layerEffectEntry, false).toBool(); } bool validateEffect(const QString &effectPath) diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp index e25f14411e..97919fa4fe 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp @@ -309,6 +309,13 @@ ModelNode ConnectionModel::getTargetNodeForConnection(const ModelNode &connectio static QString addOnToSignalName(const QString &signal) { + if (signal.isEmpty()) + return {}; + + static const QRegularExpression rx("^on[A-Z]"); + if (rx.match(signal).hasMatch()) + return signal; + QString ret = signal; ret[0] = ret.at(0).toUpper(); ret.prepend("on"); @@ -361,11 +368,10 @@ void ConnectionModel::addConnection(const PropertyName &signalName) ModelNode selectedNode = connectionView()->selectedModelNodes().constFirst(); PropertyName signalHandlerName = signalName; - if (signalHandlerName.isEmpty()) { - signalHandlerName = addOnToSignalName(QString::fromUtf8(getFirstSignalForTarget( - selectedNode.metaInfo()))) - .toUtf8(); - } + if (signalHandlerName.isEmpty()) + signalHandlerName = getFirstSignalForTarget(selectedNode.metaInfo()); + + signalHandlerName = addOnToSignalName(QString::fromUtf8(signalHandlerName)).toUtf8(); connectionView() ->executeInTransaction("ConnectionModel::addConnection", [=, &rootModelNode]() { @@ -855,7 +861,7 @@ int ConnectionModelBackendDelegate::currentRow() const return m_currentRow; } -QString removeOnFromSignalName(const QString &signal) +static QString removeOnFromSignalName(const QString &signal) { if (signal.isEmpty()) return {}; diff --git a/src/plugins/qmldesigner/components/connectioneditor/propertytreemodel.cpp b/src/plugins/qmldesigner/components/connectioneditor/propertytreemodel.cpp index 2b1f53243a..4189496316 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/propertytreemodel.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/propertytreemodel.cpp @@ -72,9 +72,7 @@ const std::vector<PropertyName> blockListSlots = {"childAt", "inputMethodQuery", "positionAt", "positionToRectangle", - "isRightToLeft" - -}; + "isRightToLeft"}; const std::vector<PropertyName> priorityListSignals = {"clicked", "doubleClicked", @@ -99,7 +97,8 @@ const std::vector<PropertyName> priorityListSignals = {"clicked", "enabledChanged", "visibleChanged", "opacityChanged", - "rotationChanged"}; + "rotationChanged", + "positionChanged"}; const std::vector<PropertyName> priorityListProperties = {"opacity", "checked", @@ -572,8 +571,7 @@ const std::vector<PropertyName> PropertyTreeModel::sortedAndFilteredPropertyName const PropertyName name = metaInfo.name(); - if (!m_includeDotPropertiesOnFirstLevel - && name.contains(".")) + if (!m_includeDotPropertiesOnFirstLevel && name.contains(".")) return false; return filterProperty(name, metaInfo, recursive); @@ -606,8 +604,8 @@ const std::vector<PropertyName> PropertyTreeModel::sortedAndFilteredPropertyName return checkedPriorityList; } -const std::vector<PropertyName> PropertyTreeModel::sortedAndFilteredSignalNames( - const NodeMetaInfo &metaInfo, bool recursive) const +std::vector<PropertyName> PropertyTreeModel::sortedAndFilteredSignalNames(const NodeMetaInfo &metaInfo, + bool recursive) { Q_UNUSED(recursive); @@ -627,15 +625,14 @@ const std::vector<PropertyName> PropertyTreeModel::sortedAndFilteredSignalNames( std::set<PropertyName> set(std::make_move_iterator(sorted.begin()), std::make_move_iterator(sorted.end())); - auto checkedPriorityList = Utils::filtered(priorityListSignals, - [&set](const PropertyName &name) { - auto it = set.find(name); - const bool b = it != set.end(); - if (b) - set.erase(it); + auto checkedPriorityList = Utils::filtered(priorityListSignals, [&set](const PropertyName &name) { + auto it = set.find(name); + const bool b = it != set.end(); + if (b) + set.erase(it); - return b; - }); + return b; + }); //const int priorityLength = checkedPriorityList.size(); We eventually require this to get the prioproperties @@ -648,8 +645,8 @@ const std::vector<PropertyName> PropertyTreeModel::sortedAndFilteredSignalNames( return checkedPriorityList; } -const std::vector<PropertyName> PropertyTreeModel::sortedAndFilteredSlotNames( - const NodeMetaInfo &metaInfo, bool recursive) const +std::vector<PropertyName> PropertyTreeModel::sortedAndFilteredSlotNames(const NodeMetaInfo &metaInfo, + bool recursive) { Q_UNUSED(recursive); diff --git a/src/plugins/qmldesigner/components/connectioneditor/propertytreemodel.h b/src/plugins/qmldesigner/components/connectioneditor/propertytreemodel.h index df17c112da..07e1401c4a 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/propertytreemodel.h +++ b/src/plugins/qmldesigner/components/connectioneditor/propertytreemodel.h @@ -84,6 +84,12 @@ public: QHash<int, QByteArray> roleNames() const override; + static std::vector<PropertyName> sortedAndFilteredSignalNames(const NodeMetaInfo &metaInfo, + bool recursive = false); + + static std::vector<PropertyName> sortedAndFilteredSlotNames(const NodeMetaInfo &metaInfo, + bool recursive = false); + private: QModelIndex ensureModelIndex(const ModelNode &node, int row) const; QModelIndex ensureModelIndex(const ModelNode &node, const PropertyName &name, int row) const; @@ -94,15 +100,10 @@ private: const std::vector<PropertyName> getDynamicProperties(const ModelNode &modelNode) const; const std::vector<PropertyName> getDynamicSignals(const ModelNode &modelNode) const; + const std::vector<PropertyName> sortedAndFilteredPropertyNames(const NodeMetaInfo &metaInfo, bool recursive = false) const; - const std::vector<PropertyName> sortedAndFilteredSignalNames(const NodeMetaInfo &metaInfo, - bool recursive = false) const; - - const std::vector<PropertyName> sortedAndFilteredSlotNames(const NodeMetaInfo &metaInfo, - bool recursive = false) const; - const std::vector<PropertyName> sortedDotPropertyNames(const NodeMetaInfo &metaInfo, const PropertyName &propertyName) const; diff --git a/src/plugins/qmldesigner/components/formeditor/formeditoritem.cpp b/src/plugins/qmldesigner/components/formeditor/formeditoritem.cpp index 404a5f5730..496f0e4b2c 100644 --- a/src/plugins/qmldesigner/components/formeditor/formeditoritem.cpp +++ b/src/plugins/qmldesigner/components/formeditor/formeditoritem.cpp @@ -262,6 +262,27 @@ void FormEditorItem::setFrameColor(const QColor &color) update(); } +void FormEditorItem::setHasEffect(bool hasEffect) +{ + m_hasEffect = hasEffect; +} + +bool FormEditorItem::hasEffect() const +{ + return m_hasEffect; +} + +bool FormEditorItem::parentHasEffect() const +{ + FormEditorItem *pi = parentItem(); + while (pi) { + if (pi->hasEffect()) + return true; + pi = pi->parentItem(); + } + return false; +} + FormEditorItem::~FormEditorItem() { scene()->removeItemFromHash(this); @@ -421,7 +442,7 @@ void FormEditorItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *, painter->setClipRegion(boundingRect().toRect()); painter->setClipping(true); - if (!hideCompletely) { + if (!hideCompletely && !parentHasEffect()) { if (showPlaceHolder) { if (scene()->showBoundingRects() && m_boundingRect.width() > 15 && m_boundingRect.height() > 15) paintPlaceHolderForInvisbleItem(painter); diff --git a/src/plugins/qmldesigner/components/formeditor/formeditoritem.h b/src/plugins/qmldesigner/components/formeditor/formeditoritem.h index 69b67d0006..b035699772 100644 --- a/src/plugins/qmldesigner/components/formeditor/formeditoritem.h +++ b/src/plugins/qmldesigner/components/formeditor/formeditoritem.h @@ -98,6 +98,10 @@ public: void setFrameColor(const QColor &color); + void setHasEffect(bool hasEffect); + bool hasEffect() const; + bool parentHasEffect() const; + protected: AbstractFormEditorTool* tool() const; void paintBoundingRect(QPainter *painter) const; @@ -129,6 +133,7 @@ private: // variables bool m_highlightBoundingRect; bool m_blurContent; bool m_isContentVisible; + bool m_hasEffect; }; class FormEditorFlowItem : public FormEditorItem diff --git a/src/plugins/qmldesigner/components/formeditor/formeditorview.cpp b/src/plugins/qmldesigner/components/formeditor/formeditorview.cpp index 4b63d9ced8..fcc04ed026 100644 --- a/src/plugins/qmldesigner/components/formeditor/formeditorview.cpp +++ b/src/plugins/qmldesigner/components/formeditor/formeditorview.cpp @@ -140,7 +140,7 @@ void FormEditorView::setupFormEditorItemTree(const QmlItemNode &qmlItemNode) setupFormEditorItemTree(childNode.toQmlItemNode()); } } - } else { + } else if (!qmlItemNode.isEffectItem()) { m_scene->addFormEditorItem(qmlItemNode, FormEditorScene::Default); for (const QmlObjectNode &nextNode : qmlItemNode.allDirectSubNodes()) //TODO instance children //If the node has source for components/custom parsers we ignore it. @@ -280,6 +280,13 @@ void FormEditorView::nodeAboutToBeRemoved(const ModelNode &removedNode) removeNodeFromScene(qmlItemNode); } +void FormEditorView::nodeRemoved(const ModelNode &/*removedNode*/, + const NodeAbstractProperty &/*parentProperty*/, + PropertyChangeFlags /*propertyChange*/) +{ + updateHasEffects(); +} + void FormEditorView::rootNodeTypeChanged(const QString &/*type*/, int /*majorVersion*/, int /*minorVersion*/) { const QList<FormEditorItem *> items = m_scene->allFormEditorItems(); @@ -343,6 +350,8 @@ static inline bool hasNodeSourceOrNonItemParent(const ModelNode &node) void FormEditorView::nodeReparented(const ModelNode &node, const NodeAbstractProperty &/*newPropertyParent*/, const NodeAbstractProperty &/*oldPropertyParent*/, AbstractView::PropertyChangeFlags /*propertyChange*/) { addOrRemoveFormEditorItem(node); + + updateHasEffects(); } void FormEditorView::nodeSourceChanged(const ModelNode &node, @@ -830,6 +839,8 @@ void FormEditorView::setupFormEditorWidget() m_formEditorWidget->showWarningMessageBox(rewriterView()->warnings()); checkRootModelNode(); + + updateHasEffects(); } QmlItemNode findRecursiveQmlItemNode(const QmlObjectNode &firstQmlObjectNode) @@ -991,6 +1002,23 @@ void FormEditorView::setupRootItemSize() } } +void FormEditorView::updateHasEffects() +{ + if (model()) { + const QList<ModelNode> nodes = allModelNodes(); + for (const auto &node : nodes) { + QmlItemNode qmlNode(node); + FormEditorItem *item = m_scene->itemForQmlItemNode(qmlNode); + if (item) + item->setHasEffect(false); + if (qmlNode.isEffectItem()) { + FormEditorItem *parentItem = m_scene->itemForQmlItemNode(qmlNode.modelParentItem()); + parentItem->setHasEffect(true); + } + } + } +} + void FormEditorView::reset() { QTimer::singleShot(200, this, &FormEditorView::delayedReset); diff --git a/src/plugins/qmldesigner/components/formeditor/formeditorview.h b/src/plugins/qmldesigner/components/formeditor/formeditorview.h index 1a9f15d016..d3c6cb21db 100644 --- a/src/plugins/qmldesigner/components/formeditor/formeditorview.h +++ b/src/plugins/qmldesigner/components/formeditor/formeditorview.h @@ -52,6 +52,8 @@ public: void nodeCreated(const ModelNode &createdNode) override; void nodeAboutToBeRemoved(const ModelNode &removedNode) override; + void nodeRemoved(const ModelNode &removedNode, const NodeAbstractProperty &parentProperty, + PropertyChangeFlags propertyChange) override; void nodeReparented(const ModelNode &node, const NodeAbstractProperty &newPropertyParent, const NodeAbstractProperty &oldPropertyParent, AbstractView::PropertyChangeFlags propertyChange) override; void nodeSourceChanged(const ModelNode &node, const QString &newNodeSource) override; void nodeIdChanged(const ModelNode& node, const QString& newId, const QString& oldId) override; @@ -138,6 +140,7 @@ private: void checkRootModelNode(); void setupFormEditor3DView(); void setupRootItemSize(); + void updateHasEffects(); QPointer<FormEditorWidget> m_formEditorWidget; QPointer<FormEditorScene> m_scene; diff --git a/src/plugins/qmldesigner/components/toolbar/toolbarbackend.cpp b/src/plugins/qmldesigner/components/toolbar/toolbarbackend.cpp index ff0fa2ab9f..67043f02d1 100644 --- a/src/plugins/qmldesigner/components/toolbar/toolbarbackend.cpp +++ b/src/plugins/qmldesigner/components/toolbar/toolbarbackend.cpp @@ -149,6 +149,14 @@ WorkspaceModel::WorkspaceModel(QObject *) }; if (!connectDockManager()) connect(designModeWidget(), &Internal::DesignModeWidget::initialized, this, connectDockManager); + + connect(ProjectExplorer::ProjectManager::instance(), + &ProjectExplorer::ProjectManager::projectFinishedParsing, + this, + [this]() { + beginResetModel(); + endResetModel(); + }); } int WorkspaceModel::rowCount(const QModelIndex &) const @@ -162,7 +170,8 @@ int WorkspaceModel::rowCount(const QModelIndex &) const QHash<int, QByteArray> WorkspaceModel::roleNames() const { static QHash<int, QByteArray> roleNames{{DisplayNameRole, "displayName"}, - {FileNameRole, "fileName"}}; + {FileNameRole, "fileName"}, + {Enabled, "enabled"}}; return roleNames; } @@ -176,6 +185,9 @@ QVariant WorkspaceModel::data(const QModelIndex &index, int role) const return workspace.name(); } else if (role == FileNameRole) { return workspace.fileName(); + } else if (role == Enabled) { + if (QmlProjectManager::QmlProject::isMCUs()) + return workspace.isMcusEnabled(); } else { qWarning() << Q_FUNC_INFO << "invalid role"; } diff --git a/src/plugins/qmldesigner/components/toolbar/toolbarbackend.h b/src/plugins/qmldesigner/components/toolbar/toolbarbackend.h index 307704d63a..eb258f9ab7 100644 --- a/src/plugins/qmldesigner/components/toolbar/toolbarbackend.h +++ b/src/plugins/qmldesigner/components/toolbar/toolbarbackend.h @@ -29,7 +29,8 @@ public: class WorkspaceModel : public QAbstractListModel { Q_OBJECT - enum { DisplayNameRole = Qt::DisplayRole, FileNameRole = Qt::UserRole }; + + enum { DisplayNameRole = Qt::DisplayRole, FileNameRole = Qt::UserRole, Enabled }; public: explicit WorkspaceModel(QObject *parent = nullptr); diff --git a/src/plugins/qmldesigner/designercore/include/qmlitemnode.h b/src/plugins/qmldesigner/designercore/include/qmlitemnode.h index 5816d60ce1..5948ec7ab1 100644 --- a/src/plugins/qmldesigner/designercore/include/qmlitemnode.h +++ b/src/plugins/qmldesigner/designercore/include/qmlitemnode.h @@ -139,6 +139,8 @@ public: bool isFlowActionArea() const; ModelNode rootModelNode() const; + bool isEffectItem() const; + friend auto qHash(const QmlItemNode &node) { return qHash(node.modelNode()); } }; diff --git a/src/plugins/qmldesigner/designercore/model/qmlitemnode.cpp b/src/plugins/qmldesigner/designercore/model/qmlitemnode.cpp index 0ce155ca6b..e01bdaaec3 100644 --- a/src/plugins/qmldesigner/designercore/model/qmlitemnode.cpp +++ b/src/plugins/qmldesigner/designercore/model/qmlitemnode.cpp @@ -182,14 +182,19 @@ QmlItemNode QmlItemNode::createQmlItemNodeForEffect(AbstractView *view, const QString effectName = QFileInfo(effectPath).baseName(); Import import = Import::createLibraryImport("Effects." + effectName, "1.0"); try { - if (!view->model()->hasImport(import, true, true)) + if (!view->model()->hasImport(import, true, true)) { view->model()->changeImports({import}, {}); + // Trigger async reset puppet to ensure full transaction is done before reset + view->resetPuppet(); + } } catch (const Exception &) { QTC_ASSERT(false, return); } TypeName type(effectName.toUtf8()); - newQmlItemNode = QmlItemNode(view->createModelNode(type, -1, -1)); + ModelNode newModelNode = view->createModelNode(type, -1, -1); + newModelNode.setIdWithoutRefactoring(view->model()->generateNewId(effectName)); + newQmlItemNode = QmlItemNode(newModelNode); placeEffectNode(parentProperty, newQmlItemNode, isLayerEffect); }; @@ -206,12 +211,8 @@ void QmlItemNode::placeEffectNode(NodeAbstractProperty &parentProperty, const Qm parentProperty.reparentHere(effectNode); - if (!isLayerEffect) { - effectNode.modelNode().bindingProperty("source").setExpression("parent"); - effectNode.modelNode().bindingProperty("anchors.fill").setExpression("parent"); - } else { + if (isLayerEffect) parentProperty.parentModelNode().variantProperty("layer.enabled").setValue(true); - } if (effectNode.modelNode().metaInfo().hasProperty("timeRunning")) effectNode.modelNode().variantProperty("timeRunning").setValue(true); @@ -617,6 +618,11 @@ ModelNode QmlItemNode::rootModelNode() const return {}; } +bool QmlItemNode::isEffectItem() const +{ + return modelNode().metaInfo().hasProperty("_isEffectItem"); +} + void QmlItemNode::setSize(const QSizeF &size) { if (!hasBindingProperty("width") && !(anchors().instanceHasAnchor(AnchorLineRight) diff --git a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp index 889f847197..037259e87e 100644 --- a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp +++ b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp @@ -388,6 +388,7 @@ bool smartVeryFuzzyCompare(const QVariant &value1, const QVariant &value2) void removeModelNode(const QmlDesigner::ModelNode &modelNode) { + QTC_ASSERT(modelNode.isValid(), return ); modelNode.model()->removeModelNodes({modelNode}, QmlDesigner::BypassModelResourceManagement::Yes); } @@ -400,6 +401,7 @@ bool smartColorCompare(const QVariant &value1, const QVariant &value2) void removeProperty(const QmlDesigner::AbstractProperty &modelProperty) { + QTC_ASSERT(modelProperty.isValid(), return ); modelProperty.model()->removeProperties({modelProperty}, QmlDesigner::BypassModelResourceManagement::Yes); } diff --git a/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.cpp b/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.cpp index b7f6d2ae55..f9eb8080f7 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.cpp +++ b/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.cpp @@ -294,10 +294,16 @@ Storage::Synchronization::Type QmlDocumentParser::parse(const QString &sourceCon QString filePath{m_pathCache.sourcePath(sourceId)}; environment.loadFile( +#if QT_VERSION < QT_VERSION_CHECK(6, 6, 0) filePath, filePath, sourceContent, QDateTime{}, +#else + QQmlJS::Dom::FileToLoad::fromMemory(environment.ownerAs<QQmlJS::Dom::DomEnvironment>(), + filePath, + sourceContent), +#endif [&](QmlDom::Path, const QmlDom::DomItem &, const QmlDom::DomItem &newItems) { items = newItems; }, diff --git a/src/plugins/qmldesigner/designmodewidget.cpp b/src/plugins/qmldesigner/designmodewidget.cpp index a8567dec38..a26eada911 100644 --- a/src/plugins/qmldesigner/designmodewidget.cpp +++ b/src/plugins/qmldesigner/designmodewidget.cpp @@ -34,6 +34,7 @@ #include <coreplugin/idocument.h> #include <coreplugin/inavigationwidgetfactory.h> +#include <projectexplorer/projectmanager.h> #include <qmlprojectmanager/qmlproject.h> #include <utils/algorithm.h> @@ -219,6 +220,13 @@ void DesignModeWidget::setup() QString sheet = QString::fromUtf8(Utils::FileReader::fetchQrc(":/qmldesigner/dockwidgets.css")); m_dockManager->setStyleSheet(Theme::replaceCssColors(sheet)); + connect(ProjectExplorer::ProjectManager::instance(), + &ProjectExplorer::ProjectManager::projectFinishedParsing, + m_dockManager, + [this]() { + this->m_dockManager->setMcusProject(QmlProjectManager::QmlProject::isMCUs()); + }); + // Setup icons const QString closeUnicode = Theme::getIconUnicode(Theme::Icon::close_small); const QString maximizeUnicode = Theme::getIconUnicode(Theme::Icon::maxBar_small); @@ -273,6 +281,7 @@ void DesignModeWidget::setup() Core::ActionContainer *mview = Core::ActionManager::actionContainer(Core::Constants::M_VIEW); // View > Views Core::ActionContainer *mviews = Core::ActionManager::createMenu(Core::Constants::M_VIEW_VIEWS); + connect(mviews->menu(), &QMenu::aboutToShow, this, &DesignModeWidget::aboutToShowViews); mviews->menu()->addSeparator(); // View > Workspaces Core::ActionContainer *mworkspaces = Core::ActionManager::createMenu(QmlDesigner::Constants::M_VIEW_WORKSPACES); @@ -482,6 +491,29 @@ void DesignModeWidget::setup() show(); } +static bool isMcuDisabledView(const QString viewId) +{ + static const QStringList mcuDisabledViews = {"Editor3D", "MaterialEditor", "MaterialBrowser", "TextureEditor"}; + return mcuDisabledViews.contains(viewId); +} + +void DesignModeWidget::aboutToShowViews() +{ + for (const WidgetInfo &widgetInfo : viewManager().widgetInfos()) { + QString id = widgetInfo.uniqueId; + ADS::DockWidget *dockWidget = m_dockManager->findDockWidget(id); + QAction *action = dockWidget->toggleViewAction(); + + bool isMcuProject = currentDesignDocument() && currentDesignDocument()->isQtForMCUsProject(); + if (isMcuProject && isMcuDisabledView(id) && action->isEnabled()) { + action->setChecked(false); + action->setEnabled(false); + } else if (!isMcuProject && !action->isEnabled()) { + action->setEnabled(true); + } + } +} + void DesignModeWidget::aboutToShowWorkspaces() { Core::ActionContainer *aci = Core::ActionManager::actionContainer( @@ -520,6 +552,9 @@ void DesignModeWidget::aboutToShowWorkspaces() action->setCheckable(true); if (workspace == *m_dockManager->activeWorkspace()) action->setChecked(true); + + if (currentDesignDocument() && currentDesignDocument()->isQtForMCUsProject()) + action->setEnabled(workspace.isMcusEnabled()); } menu->addActions(ag->actions()); } diff --git a/src/plugins/qmldesigner/designmodewidget.h b/src/plugins/qmldesigner/designmodewidget.h index 432549e694..464994b7e3 100644 --- a/src/plugins/qmldesigner/designmodewidget.h +++ b/src/plugins/qmldesigner/designmodewidget.h @@ -93,6 +93,7 @@ private: QWidget *createCenterWidget(); QWidget *createCrumbleBarFrame(); + void aboutToShowViews(); void aboutToShowWorkspaces(); QPointer<QWidget> m_bottomSideBar; diff --git a/src/plugins/qmlprojectmanager/qmlproject.cpp b/src/plugins/qmlprojectmanager/qmlproject.cpp index 0acba25ded..62a2d7bdfc 100644 --- a/src/plugins/qmlprojectmanager/qmlproject.cpp +++ b/src/plugins/qmlprojectmanager/qmlproject.cpp @@ -238,4 +238,17 @@ bool QmlProject::allowOnlySingleProject() return !settings->value(key, false).toBool(); } +bool QmlProject::isMCUs() +{ + if (!ProjectExplorer::ProjectManager::startupTarget()) + return false; + + const QmlProjectManager::QmlBuildSystem *buildSystem + = qobject_cast<QmlProjectManager::QmlBuildSystem *>( + ProjectExplorer::ProjectManager::startupTarget()->buildSystem()); + QTC_ASSERT(buildSystem, return false); + + return buildSystem && buildSystem->qtForMCUs(); +} + } // namespace QmlProjectManager diff --git a/src/plugins/qmlprojectmanager/qmlproject.h b/src/plugins/qmlprojectmanager/qmlproject.h index a41b05e57b..0091d77d6c 100644 --- a/src/plugins/qmlprojectmanager/qmlproject.h +++ b/src/plugins/qmlprojectmanager/qmlproject.h @@ -24,6 +24,8 @@ public: ProjectExplorer::Tasks projectIssues(const ProjectExplorer::Kit *k) const final; + static bool isMCUs(); + protected: RestoreResult fromMap(const Utils::Store &map, QString *errorMessage) override; diff --git a/src/plugins/qtsupport/baseqtversion.cpp b/src/plugins/qtsupport/baseqtversion.cpp index fb28d16aa3..da669423e3 100644 --- a/src/plugins/qtsupport/baseqtversion.cpp +++ b/src/plugins/qtsupport/baseqtversion.cpp @@ -77,7 +77,7 @@ class QtVersionData { public: // Update version if you add data members! - static const int version = 2; + static const int version = 3; bool installed = true; bool hasExamples = false; @@ -203,8 +203,11 @@ public: hostDataPath = FilePath::fromSettings(map.value("HostDataPath")); hostPrefixPath = FilePath::fromSettings(map.value("HostPrefixPath")); auto it = map.find("QtAbis"); - if (it != map.end()) - qtAbis = Utils::transform(it.value().toStringList(), &Abi::fromString); + if (it != map.end()) { + const auto qtAbisList = it.value().toStringList(); + if (!qtAbisList.isEmpty()) + qtAbis = Utils::transform(qtAbisList, &Abi::fromString); + } versionInfo = fromStore(map.value("VersionInfo").value<Store>()); } }; diff --git a/src/plugins/studiowelcome/studiowelcomeplugin.cpp b/src/plugins/studiowelcome/studiowelcomeplugin.cpp index b48d87121f..c80b64601b 100644 --- a/src/plugins/studiowelcome/studiowelcomeplugin.cpp +++ b/src/plugins/studiowelcome/studiowelcomeplugin.cpp @@ -759,7 +759,8 @@ WelcomeMode::WelcomeMode() m_dataModelDownloader->setForceDownload(true); connect(m_dataModelDownloader, &DataModelDownloader::progressChanged, this, [this](){ - m_quickWidget->rootObject()->setProperty("loadingProgress", m_dataModelDownloader->progress()); + if (m_quickWidget->rootObject()) + m_quickWidget->rootObject()->setProperty("loadingProgress", m_dataModelDownloader->progress()); }); connect(m_dataModelDownloader, &DataModelDownloader::finished, this, [this, welcomePagePath]() { |