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 | |
parent | 0a5f48a9493d280855821b6f503ecccbba298cea (diff) | |
parent | 05aa1e76dad4fd853131c2ab75eae72deaf34aae (diff) |
Merge remote-tracking branch 'origin/qds/dev' into 12.0
Change-Id: I36d7bf02cd4a9e2214877dc286a88d12685dd12f
Diffstat (limited to 'src')
12 files changed, 414 insertions, 78 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; } diff --git a/src/tools/qml2puppet/mockfiles/qt6/GridMaterial.qml b/src/tools/qml2puppet/mockfiles/qt6/GridMaterial.qml index 37c23806b2..11ebbfd411 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 e189d06401..c82303d779 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 0eafeeb816..6945579bf1 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 7d785bc531..20738ff46b 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 1ef951ca3b..4af31d072f 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<QVector3D *>(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 |