diff options
author | Tim Jenssen <tim.jenssen@qt.io> | 2023-10-05 15:58:09 +0200 |
---|---|---|
committer | Tim Jenssen <tim.jenssen@qt.io> | 2023-10-05 13:59:07 +0000 |
commit | e02f87da5854a90d3a74255932b041a8173e0e31 (patch) | |
tree | 55ff185c5fd8a33064854d66f69c7003ff08597a /src/plugins/qmldesigner | |
parent | 0a5f48a9493d280855821b6f503ecccbba298cea (diff) | |
parent | 05aa1e76dad4fd853131c2ab75eae72deaf34aae (diff) |
Merge remote-tracking branch 'origin/qds/dev' into 12.0
Change-Id: I36d7bf02cd4a9e2214877dc286a88d12685dd12f
Diffstat (limited to 'src/plugins/qmldesigner')
7 files changed, 383 insertions, 58 deletions
diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index 97dbc9eac3..a2f0dba38e 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -792,6 +792,7 @@ extend_qtc_plugin(QmlDesigner extend_qtc_plugin(QmlDesigner SOURCES_PREFIX components/collectioneditor SOURCES + collectiondetails.cpp collectiondetails.h collectioneditorconstants.h collectionlistmodel.cpp collectionlistmodel.h collectionsourcemodel.cpp collectionsourcemodel.h diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp new file mode 100644 index 0000000000..4b8a297140 --- /dev/null +++ b/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp @@ -0,0 +1,199 @@ +// 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 "collectiondetails.h" + +#include <QJsonObject> +#include <QVariant> + +namespace QmlDesigner { + +class CollectionDetails::Private +{ + using SourceFormat = CollectionEditor::SourceFormat; + +public: + QStringList headers; + QList<QJsonObject> elements; + SourceFormat sourceFormat = SourceFormat::Unknown; + CollectionReference reference; + bool isChanged = false; + + bool isValidColumnId(int column) const { return column > -1 && column < headers.size(); } + + bool isValidRowId(int row) const { return row > -1 && row < elements.size(); } +}; + +CollectionDetails::CollectionDetails() + : d(new Private()) +{} + +CollectionDetails::CollectionDetails(const CollectionReference &reference) + : CollectionDetails() +{ + d->reference = reference; +} + +CollectionDetails::CollectionDetails(const CollectionDetails &other) = default; + +CollectionDetails::~CollectionDetails() = default; + +void CollectionDetails::resetDetails(const QStringList &headers, + const QList<QJsonObject> &elements, + CollectionEditor::SourceFormat format) +{ + if (!isValid()) + return; + + d->headers = headers; + d->elements = elements; + d->sourceFormat = format; + + markSaved(); +} + +void CollectionDetails::insertHeader(const QString &header, int place, const QVariant &defaultValue) +{ + if (!isValid()) + return; + + if (d->headers.contains(header)) + return; + + if (d->isValidColumnId(place)) + d->headers.insert(place, header); + else + d->headers.append(header); + + QJsonValue defaultJsonValue = QJsonValue::fromVariant(defaultValue); + for (QJsonObject &element : d->elements) + element.insert(header, defaultJsonValue); + + markChanged(); +} + +void CollectionDetails::removeHeader(int place) +{ + if (!isValid()) + return; + + if (!d->isValidColumnId(place)) + return; + + const QString header = d->headers.takeAt(place); + + for (QJsonObject &element : d->elements) + element.remove(header); + + markChanged(); +} + +void CollectionDetails::insertElementAt(std::optional<QJsonObject> object, int row) +{ + if (!isValid()) + return; + + auto insertJson = [this, row](const QJsonObject &jsonObject) { + if (d->isValidRowId(row)) + d->elements.insert(row, jsonObject); + else + d->elements.append(jsonObject); + }; + + if (object.has_value()) { + insertJson(object.value()); + } else { + QJsonObject defaultObject; + for (const QString &header : std::as_const(d->headers)) + defaultObject.insert(header, {}); + insertJson(defaultObject); + } + + markChanged(); +} + +CollectionReference CollectionDetails::reference() const +{ + return d->reference; +} + +CollectionEditor::SourceFormat CollectionDetails::sourceFormat() const +{ + return d->sourceFormat; +} + +QVariant CollectionDetails::data(int row, int column) const +{ + if (!isValid()) + return {}; + + if (!d->isValidRowId(row)) + return {}; + + if (!d->isValidColumnId(column)) + return {}; + + const QString &propertyName = d->headers.at(column); + const QJsonObject &elementNode = d->elements.at(row); + + if (elementNode.contains(propertyName)) + return elementNode.value(propertyName).toVariant(); + + return {}; +} + +QString CollectionDetails::headerAt(int column) const +{ + if (!d->isValidColumnId(column)) + return {}; + + return d->headers.at(column); +} + +bool CollectionDetails::isValid() const +{ + return d->reference.node.isValid() && d->reference.name.size(); +} + +bool CollectionDetails::isChanged() const +{ + return d->isChanged; +} + +int CollectionDetails::columns() const +{ + return d->headers.size(); +} + +int CollectionDetails::rows() const +{ + return d->elements.size(); +} + +bool CollectionDetails::markSaved() +{ + if (d->isChanged) { + d->isChanged = false; + return true; + } + return false; +} + +void CollectionDetails::swap(CollectionDetails &other) +{ + d.swap(other.d); +} + +CollectionDetails &CollectionDetails::operator=(const CollectionDetails &other) +{ + CollectionDetails value(other); + swap(value); + return *this; +} + +void CollectionDetails::markChanged() +{ + d->isChanged = true; +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.h b/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.h new file mode 100644 index 0000000000..c3ec59f5d8 --- /dev/null +++ b/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.h @@ -0,0 +1,76 @@ +// 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 "collectioneditorconstants.h" +#include "modelnode.h" + +#include <QSharedPointer> + +QT_BEGIN_NAMESPACE +class QJsonObject; +class QVariant; +QT_END_NAMESPACE + +namespace QmlDesigner { + +struct CollectionReference +{ + ModelNode node; + QString name; + + friend auto qHash(const CollectionReference &collection) + { + return qHash(collection.node) ^ ::qHash(collection.name); + } + + bool operator==(const CollectionReference &other) const + { + return node == other.node && name == other.name; + } + + bool operator!=(const CollectionReference &other) const { return !(*this == other); } +}; + +class CollectionDetails +{ +public: + explicit CollectionDetails(); + CollectionDetails(const CollectionReference &reference); + CollectionDetails(const CollectionDetails &other); + ~CollectionDetails(); + + void resetDetails(const QStringList &headers, + const QList<QJsonObject> &elements, + CollectionEditor::SourceFormat format); + void insertHeader(const QString &header, int place = -1, const QVariant &defaultValue = {}); + void removeHeader(int place); + + void insertElementAt(std::optional<QJsonObject> object, int row = -1); + + CollectionReference reference() const; + CollectionEditor::SourceFormat sourceFormat() const; + QVariant data(int row, int column) const; + QString headerAt(int column) const; + + bool isValid() const; + bool isChanged() const; + + int columns() const; + int rows() const; + + bool markSaved(); + + void swap(CollectionDetails &other); + CollectionDetails &operator=(const CollectionDetails &other); + +private: + void markChanged(); + + // The private data is supposed to be shared between the copies + class Private; + QSharedPointer<Private> d; +}; + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorconstants.h b/src/plugins/qmldesigner/components/collectioneditor/collectioneditorconstants.h index d75fe221e9..f5e18a93ec 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorconstants.h +++ b/src/plugins/qmldesigner/components/collectioneditor/collectioneditorconstants.h @@ -5,6 +5,8 @@ namespace QmlDesigner::CollectionEditor { +enum class SourceFormat { Unknown, Json, Csv }; + inline constexpr char SOURCEFILE_PROPERTY[] = "sourceFile"; inline constexpr char COLLECTIONMODEL_IMPORT[] = "QtQuick.Studio.Models"; diff --git a/src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.cpp b/src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.cpp index 62a95474a5..a86b42cd16 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.cpp @@ -11,6 +11,7 @@ #include <QFile> #include <QJsonArray> +#include <QJsonObject> #include <QJsonParseError> namespace { @@ -39,26 +40,19 @@ SingleCollectionModel::SingleCollectionModel(QObject *parent) int SingleCollectionModel::rowCount([[maybe_unused]] const QModelIndex &parent) const { - return m_elements.count(); + return m_currentCollection.rows(); } int SingleCollectionModel::columnCount([[maybe_unused]] const QModelIndex &parent) const { - return m_headers.count(); + return m_currentCollection.columns(); } QVariant SingleCollectionModel::data(const QModelIndex &index, int) const { if (!index.isValid()) return {}; - - const QString &propertyName = m_headers.at(index.column()); - const QJsonObject &elementNode = m_elements.at(index.row()); - - if (elementNode.contains(propertyName)) - return elementNode.value(propertyName).toVariant(); - - return {}; + return m_currentCollection.data(index.row(), index.column()); } bool SingleCollectionModel::setData(const QModelIndex &, const QVariant &, int) @@ -79,7 +73,7 @@ QVariant SingleCollectionModel::headerData(int section, [[maybe_unused]] int role) const { if (orientation == Qt::Horizontal) - return m_headers.at(section); + return m_currentCollection.headerAt(section); return {}; } @@ -88,16 +82,62 @@ void SingleCollectionModel::loadCollection(const ModelNode &sourceNode, const QS { QString fileName = sourceNode.variantProperty(CollectionEditor::SOURCEFILE_PROPERTY).value().toString(); - if (sourceNode.type() == CollectionEditor::JSONCOLLECTIONMODEL_TYPENAME) - loadJsonCollection(fileName, collection); - else if (sourceNode.type() == CollectionEditor::CSVCOLLECTIONMODEL_TYPENAME) - loadCsvCollection(fileName, collection); + CollectionReference newReference{sourceNode, collection}; + bool alreadyOpen = m_openedCollections.contains(newReference); + + if (alreadyOpen) { + if (m_currentCollection.reference() != newReference) { + beginResetModel(); + switchToCollection(newReference); + endResetModel(); + } + } else { + switchToCollection(newReference); + if (sourceNode.type() == CollectionEditor::JSONCOLLECTIONMODEL_TYPENAME) + loadJsonCollection(fileName, collection); + else if (sourceNode.type() == CollectionEditor::CSVCOLLECTIONMODEL_TYPENAME) + loadCsvCollection(fileName, collection); + } +} + +void SingleCollectionModel::switchToCollection(const CollectionReference &collection) +{ + if (m_currentCollection.reference() == collection) + return; + + closeCurrentCollectionIfSaved(); + + if (!m_openedCollections.contains(collection)) + m_openedCollections.insert(collection, CollectionDetails(collection)); + + m_currentCollection = m_openedCollections.value(collection); + + setCollectionName(collection.name); +} + +void SingleCollectionModel::closeCollectionIfSaved(const CollectionReference &collection) +{ + if (!m_openedCollections.contains(collection)) + return; + + const CollectionDetails &collectionDetails = m_openedCollections.value(collection); + + if (!collectionDetails.isChanged()) + m_openedCollections.remove(collection); + + m_currentCollection = CollectionDetails{}; +} + +void SingleCollectionModel::closeCurrentCollectionIfSaved() +{ + if (m_currentCollection.isValid()) + closeCollectionIfSaved(m_currentCollection.reference()); } void SingleCollectionModel::loadJsonCollection(const QString &source, const QString &collection) { - beginResetModel(); - setCollectionName(collection); + using CollectionEditor::SourceFormat; + QFile sourceFile(source); QJsonArray collectionNodes; bool jsonFileIsOk = false; @@ -119,62 +159,64 @@ void SingleCollectionModel::loadJsonCollection(const QString &source, const QStr } } - setCollectionSourceFormat(jsonFileIsOk ? SourceFormat::Json : SourceFormat::Unknown); - if (collectionNodes.isEmpty()) { - m_headers.clear(); - m_elements.clear(); + closeCurrentCollectionIfSaved(); endResetModel(); return; - } - - m_headers = getJsonHeaders(collectionNodes); + }; - m_elements.clear(); + QList<QJsonObject> elements; for (const QJsonValue &value : std::as_const(collectionNodes)) { if (value.isObject()) { QJsonObject object = value.toObject(); - m_elements.append(object); + elements.append(object); } } + SourceFormat sourceFormat = jsonFileIsOk ? SourceFormat::Json : SourceFormat::Unknown; + + beginResetModel(); + m_currentCollection.resetDetails(getJsonHeaders(collectionNodes), elements, sourceFormat); endResetModel(); } -void SingleCollectionModel::loadCsvCollection(const QString &source, const QString &collectionName) +void SingleCollectionModel::loadCsvCollection(const QString &source, + [[maybe_unused]] const QString &collectionName) { - beginResetModel(); + using CollectionEditor::SourceFormat; - setCollectionName(collectionName); QFile sourceFile(source); - m_headers.clear(); - m_elements.clear(); + QStringList headers; + QList<QJsonObject> elements; bool csvFileIsOk = false; if (sourceFile.open(QFile::ReadOnly)) { QTextStream stream(&sourceFile); if (!stream.atEnd()) - m_headers = stream.readLine().split(','); + headers = stream.readLine().split(','); - if (!m_headers.isEmpty()) { + if (!headers.isEmpty()) { while (!stream.atEnd()) { const QStringList recordDataList = stream.readLine().split(','); int column = -1; QJsonObject recordData; for (const QString &cellData : recordDataList) { - if (++column == m_headers.size()) + if (++column == headers.size()) break; - recordData.insert(m_headers.at(column), cellData); + recordData.insert(headers.at(column), cellData); } if (recordData.count()) - m_elements.append(recordData); + elements.append(recordData); } csvFileIsOk = true; } } - setCollectionSourceFormat(csvFileIsOk ? SourceFormat::Csv : SourceFormat::Unknown); + SourceFormat sourceFormat = csvFileIsOk ? SourceFormat::Csv : SourceFormat::Unknown; + + beginResetModel(); + m_currentCollection.resetDetails(headers, elements, sourceFormat); endResetModel(); } @@ -186,8 +228,4 @@ void SingleCollectionModel::setCollectionName(const QString &newCollectionName) } } -void SingleCollectionModel::setCollectionSourceFormat(SourceFormat sourceFormat) -{ - m_sourceFormat = sourceFormat; -} } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.h b/src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.h index 471e43b967..a545f4b0e7 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.h +++ b/src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.h @@ -3,8 +3,10 @@ #pragma once +#include "collectiondetails.h" + #include <QAbstractTableModel> -#include <QJsonObject> +#include <QHash> namespace QmlDesigner { @@ -17,7 +19,6 @@ class SingleCollectionModel : public QAbstractTableModel Q_PROPERTY(QString collectionName MEMBER m_collectionName NOTIFY collectionNameChanged) public: - enum class SourceFormat { Unknown, Json, Csv }; explicit SingleCollectionModel(QObject *parent = nullptr); int rowCount(const QModelIndex &parent) const override; @@ -35,15 +36,17 @@ signals: void collectionNameChanged(const QString &collectionName); private: + void switchToCollection(const CollectionReference &collection); + void closeCollectionIfSaved(const CollectionReference &collection); + void closeCurrentCollectionIfSaved(); void setCollectionName(const QString &newCollectionName); - void setCollectionSourceFormat(SourceFormat sourceFormat); void loadJsonCollection(const QString &source, const QString &collection); void loadCsvCollection(const QString &source, const QString &collectionName); - QStringList m_headers; - QList<QJsonObject> m_elements; + QHash<CollectionReference, CollectionDetails> m_openedCollections; + CollectionDetails m_currentCollection; + QString m_collectionName; - SourceFormat m_sourceFormat = SourceFormat::Unknown; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp b/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp index ef7eca0d8f..e78a6d1a37 100644 --- a/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp +++ b/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp @@ -744,8 +744,9 @@ std::shared_ptr<NodeMetaInfoPrivate> NodeMetaInfoPrivate::create(Model *model, newData->minorVersion()); if (auto found = cache.find(stringfiedQualifiedType); found != cache.end()) { - cache.insert(stringfiedType, *found); - return *found; + newData = *found; + cache.insert(stringfiedType, newData); + return newData; } if (stringfiedQualifiedType != stringfiedType) @@ -1709,10 +1710,12 @@ std::vector<NodeMetaInfo> NodeMetaInfo::selfAndPrototypes() const NodeMetaInfos hierarchy = {*this}; Model *model = m_privateData->model(); for (const TypeDescription &type : m_privateData->prototypes()) { - hierarchy.emplace_back(model, - type.className.toUtf8(), - type.majorVersion, - type.minorVersion); + auto &last = hierarchy.emplace_back(model, + type.className.toUtf8(), + type.majorVersion, + type.minorVersion); + if (!last.isValid()) + hierarchy.pop_back(); } return hierarchy; @@ -1735,11 +1738,14 @@ NodeMetaInfos NodeMetaInfo::prototypes() const if (isValid()) { NodeMetaInfos hierarchy; Model *model = m_privateData->model(); - for (const TypeDescription &type : m_privateData->prototypes()) - hierarchy.emplace_back(model, - type.className.toUtf8(), - type.majorVersion, - type.minorVersion); + for (const TypeDescription &type : m_privateData->prototypes()) { + auto &last = hierarchy.emplace_back(model, + type.className.toUtf8(), + type.majorVersion, + type.minorVersion); + if (!last.isValid()) + hierarchy.pop_back(); + } return hierarchy; } |