From 1c76217a7055b4fba1e04633fcc0ceb5f7b1b785 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Tue, 3 Oct 2023 17:01:44 +0300 Subject: [PATCH 1/4] QmlDesigner: Fix helper grid rendering issues We can't use SCREEN_TEXTURE to apply fade as it doesn't contain transparent objects. However, it turns out it's not actually necessary if the grid itself is also transparent (opacity 0.99 is sufficient), so SCREEN_TEXTURE use was removed. Fixed all components of the helper grid to have same bounding boxes, as it appears bounding box of the mesh is relevant to alpha calculations. Now subdiv lines of the grid no longer randomly change their alpha values when just orbiting the camera. Orbiting the camera also caused constant property updates on the grid material due to float math rounding errors, so round the result to five significant digits there. Limited the grid area to 30000 units as there is not much point in bigger grid due to the fade and very large objects can cause other issues. Fixes: QDS-10821 Change-Id: I65c31e99a45fd30186dcdcb185f9978b5c1b322e Reviewed-by: Reviewed-by: Mahmoud Badri Reviewed-by: Qt CI Patch Build Bot --- .../qml2puppet/mockfiles/qt6/GridMaterial.qml | 1 - .../qml2puppet/mockfiles/qt6/HelperGrid.qml | 21 ++++++++++++++----- .../qml2puppet/mockfiles/qt6/SceneView3D.qml | 8 +++++-- .../mockfiles/shaders/gridmaterial.frag | 12 +++-------- .../qml2puppet/editor3d/gridgeometry.cpp | 9 +++++--- 5 files changed, 31 insertions(+), 20 deletions(-) diff --git a/src/tools/qml2puppet/mockfiles/qt6/GridMaterial.qml b/src/tools/qml2puppet/mockfiles/qt6/GridMaterial.qml index 37c23806b2b..11ebbfd411b 100644 --- a/src/tools/qml2puppet/mockfiles/qt6/GridMaterial.qml +++ b/src/tools/qml2puppet/mockfiles/qt6/GridMaterial.qml @@ -17,6 +17,5 @@ CustomMaterial { sourceBlend: CustomMaterial.NoBlend destinationBlend: CustomMaterial.NoBlend shadingMode: CustomMaterial.Unshaded - depthDrawMode: Material.AlwaysDepthDraw cullMode: Material.NoCulling } diff --git a/src/tools/qml2puppet/mockfiles/qt6/HelperGrid.qml b/src/tools/qml2puppet/mockfiles/qt6/HelperGrid.qml index e189d064014..c82303d7799 100644 --- a/src/tools/qml2puppet/mockfiles/qt6/HelperGrid.qml +++ b/src/tools/qml2puppet/mockfiles/qt6/HelperGrid.qml @@ -14,7 +14,18 @@ Node { property double distance: 500 readonly property int maxGridStep: 32 * _generalHelper.minGridStep - readonly property int gridArea: _generalHelper.minGridStep * 512 + + readonly property int gridArea: { + let newArea = _generalHelper.minGridStep * 512 + + // Let's limit the grid size to something sensible + while (newArea > 30000) + newArea -= gridStep + + return newArea + } + + readonly property double gridOpacity: 0.99 // Step of the main lines of the grid, between those is always one subdiv line property int gridStep: 100 @@ -71,7 +82,7 @@ Node { orthoMode: grid.orthoMode } ] - opacity: 0.99 + opacity: grid.gridOpacity } Model { // Subdivision lines @@ -91,7 +102,7 @@ Node { orthoMode: grid.orthoMode } ] - opacity: 0.99 + opacity: grid.gridOpacity } Model { // Z Axis @@ -110,7 +121,7 @@ Node { orthoMode: grid.orthoMode } ] - opacity: 0.99 + opacity: grid.gridOpacity } Model { // X Axis readonly property bool _edit3dLocked: true // Make this non-pickable @@ -129,6 +140,6 @@ Node { orthoMode: grid.orthoMode } ] - opacity: 0.99 + opacity: grid.gridOpacity } } diff --git a/src/tools/qml2puppet/mockfiles/qt6/SceneView3D.qml b/src/tools/qml2puppet/mockfiles/qt6/SceneView3D.qml index 0eafeeb816e..6945579bf12 100644 --- a/src/tools/qml2puppet/mockfiles/qt6/SceneView3D.qml +++ b/src/tools/qml2puppet/mockfiles/qt6/SceneView3D.qml @@ -22,8 +22,12 @@ View3D { // gives a reasonable grid spacing in most cases while keeping spacing constant when // orbiting the camera. readonly property double cameraDistance: { - if (usePerspective) - return cameraLookAt.minus(camera.position).length() + Math.abs(cameraLookAt.y) + if (usePerspective) { + // Round to five decimals to avoid rounding errors causing constant property updates + // on the material when simply orbiting. + let dist = cameraLookAt.minus(camera.position).length() + Math.abs(cameraLookAt.y) + return Number(dist.toPrecision(5)); + } // Orthocamera should only care about camera magnification, // as grid will be same size regardless of distance, so setting steps based on distance diff --git a/src/tools/qml2puppet/mockfiles/shaders/gridmaterial.frag b/src/tools/qml2puppet/mockfiles/shaders/gridmaterial.frag index 7d785bc5312..20738ff46b4 100644 --- a/src/tools/qml2puppet/mockfiles/shaders/gridmaterial.frag +++ b/src/tools/qml2puppet/mockfiles/shaders/gridmaterial.frag @@ -19,15 +19,9 @@ void MAIN() if (depth > 90000.0) alpha *= clamp((100000.0 - depth) / 10000.0, 0, 1); - if (alpha > 0.01) { - vec2 uv = FRAGCOORD.xy / vec2(textureSize(SCREEN_TEXTURE, 0)); - vec4 sc = texture(SCREEN_TEXTURE, uv); - if (sc.a == 0.0) - FRAGCOLOR = vec4(color.xyz * alpha, alpha); - else - FRAGCOLOR = vec4((color.xyz * alpha + sc.xyz * (1.0 - alpha)) * alpha, alpha); - } else { + if (alpha > 0.01) + FRAGCOLOR = vec4(color.xyz * alpha, alpha); + else discard; - } } } diff --git a/src/tools/qml2puppet/qml2puppet/editor3d/gridgeometry.cpp b/src/tools/qml2puppet/qml2puppet/editor3d/gridgeometry.cpp index 1ef951ca3b7..4af31d072f0 100644 --- a/src/tools/qml2puppet/qml2puppet/editor3d/gridgeometry.cpp +++ b/src/tools/qml2puppet/qml2puppet/editor3d/gridgeometry.cpp @@ -81,9 +81,12 @@ void GridGeometry::doUpdateGeometry() setVertexData(vertexData); int lastIndex = (vertexData.size() - 1) / int(sizeof(QVector3D)); - auto vertexPtr = reinterpret_cast(vertexData.data()); - setBounds(QVector3D(vertexPtr[0][0], vertexPtr[0][1], 0.0), - QVector3D(vertexPtr[lastIndex][0], vertexPtr[lastIndex][1], 0.0)); + + // Set bounds based on main grid size instead of actual mesh size to make all parts of the + // grid have consistent bounds. + const float extent = float(m_lines) * m_step; + setBounds(QVector3D(-extent, -extent, 0.0), + QVector3D(extent, extent, 0.0)); } #if QT_VERSION_MAJOR == 6 && QT_VERSION_MINOR == 4 From be84072066b345b4b93b910c22abb04d62b61a98 Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Mon, 2 Oct 2023 17:12:29 +0300 Subject: [PATCH 2/4] QmlDesigner: Keep the unsaved collections open while switching Task-number: QDS-10813 Change-Id: Ia61260eb6ab23036142b5645a1288baf25f2eaf8 Reviewed-by: Miikka Heikkinen Reviewed-by: Reviewed-by: Qt CI Patch Build Bot --- src/plugins/qmldesigner/CMakeLists.txt | 1 + .../collectioneditor/collectiondetails.cpp | 199 ++++++++++++++++++ .../collectioneditor/collectiondetails.h | 76 +++++++ .../collectioneditorconstants.h | 2 + .../singlecollectionmodel.cpp | 120 +++++++---- .../collectioneditor/singlecollectionmodel.h | 15 +- 6 files changed, 366 insertions(+), 47 deletions(-) create mode 100644 src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp create mode 100644 src/plugins/qmldesigner/components/collectioneditor/collectiondetails.h diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index 0d7c39784c9..d2b091d1e4c 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 00000000000..4b8a297140f --- /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 +#include + +namespace QmlDesigner { + +class CollectionDetails::Private +{ + using SourceFormat = CollectionEditor::SourceFormat; + +public: + QStringList headers; + QList 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 &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 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 00000000000..c3ec59f5d8f --- /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 + +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 &elements, + CollectionEditor::SourceFormat format); + void insertHeader(const QString &header, int place = -1, const QVariant &defaultValue = {}); + void removeHeader(int place); + + void insertElementAt(std::optional 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 d; +}; + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorconstants.h b/src/plugins/qmldesigner/components/collectioneditor/collectioneditorconstants.h index d75fe221e91..f5e18a93ec3 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 62a95474a55..a86b42cd169 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.cpp @@ -11,6 +11,7 @@ #include #include +#include #include 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 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 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 471e43b9674..a545f4b0e78 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 -#include +#include 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 m_elements; + QHash m_openedCollections; + CollectionDetails m_currentCollection; + QString m_collectionName; - SourceFormat m_sourceFormat = SourceFormat::Unknown; }; } // namespace QmlDesigner From 0775bf3339902c3822349f28c8274d80450b9199 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Thu, 5 Oct 2023 11:05:42 +0200 Subject: [PATCH 3/4] QmlDesigner: Fix crash in NodeMetaInfoPrivate::create We changed the QHash and then accessed the invalid iterator. Change-Id: Ia6f4536cff92776b8d74f96224dee84e3b28fac3 Reviewed-by: Miikka Heikkinen Reviewed-by: Qt CI Patch Build Bot --- .../qmldesigner/designercore/metainfo/nodemetainfo.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp b/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp index fcb66766797..2408c4d94b9 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::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) From 05aa1e76dad4fd853131c2ab75eae72deaf34aae Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Thu, 5 Oct 2023 11:42:57 +0200 Subject: [PATCH 4/4] QmlDesigner: Don't add invalid nodes in the prototype chain Change-Id: Iab74a079c5ab507a370c5ab7014c81061eb3d878 Reviewed-by: Miikka Heikkinen --- .../designercore/metainfo/nodemetainfo.cpp | 23 +++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp b/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp index 2408c4d94b9..5ed270bf8cb 100644 --- a/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp +++ b/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp @@ -1710,10 +1710,12 @@ std::vector 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; @@ -1736,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; }