From f7cda6d1739ba99bb47cd71c66fdfb0154634d2f Mon Sep 17 00:00:00 2001 From: Shrief Gabr Date: Thu, 5 Oct 2023 16:22:56 +0300 Subject: [PATCH 001/242] QmlDesigner: Export a collection as Json Task-number: QDS-10617 Change-Id: I08be4cc77bf11e93a395a82aef1522656807a8c1 Reviewed-by: Mahmoud Badri Reviewed-by: Miikka Heikkinen Reviewed-by: Qt CI Patch Build Bot --- .../collectioneditor/collectiondetails.cpp | 10 +++++++++ .../collectioneditor/collectiondetails.h | 1 + .../singlecollectionmodel.cpp | 21 +++++++++++++++++++ .../collectioneditor/singlecollectionmodel.h | 1 + 4 files changed, 33 insertions(+) diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp index 4b8a297140f..c2b29d32f08 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp @@ -3,6 +3,7 @@ #include "collectiondetails.h" +#include #include #include @@ -196,4 +197,13 @@ void CollectionDetails::markChanged() d->isChanged = true; } +QJsonArray CollectionDetails::getJsonCollection() const +{ + QJsonArray collectionArray; + for (const QJsonObject &element : std::as_const(d->elements)) + collectionArray.push_back(element); + + return collectionArray; +} + } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.h b/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.h index c3ec59f5d8f..0362fbfe5bc 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.h +++ b/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.h @@ -63,6 +63,7 @@ public: bool markSaved(); void swap(CollectionDetails &other); + QJsonArray getJsonCollection() const; CollectionDetails &operator=(const CollectionDetails &other); private: diff --git a/src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.cpp b/src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.cpp index a86b42cd169..35219a98da7 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.cpp @@ -228,4 +228,25 @@ void SingleCollectionModel::setCollectionName(const QString &newCollectionName) } } +bool SingleCollectionModel::saveCollectionAsJson(const QString &collection, const QJsonArray &content, const QString &source) +{ + QFile sourceFile(source); + if (sourceFile.open(QFile::ReadWrite)) { + QJsonParseError jpe; + QJsonDocument document = QJsonDocument::fromJson(sourceFile.readAll(), &jpe); + + if (jpe.error == QJsonParseError::NoError) { + QJsonObject collectionMap = document.object(); + collectionMap[collection] = content; + document.setObject(collectionMap); + } + + sourceFile.resize(0); + if (sourceFile.write(document.toJson())) + return true; + } + + return false; +} + } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.h b/src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.h index a545f4b0e78..250f1dae5d5 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.h +++ b/src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.h @@ -42,6 +42,7 @@ private: void setCollectionName(const QString &newCollectionName); void loadJsonCollection(const QString &source, const QString &collection); void loadCsvCollection(const QString &source, const QString &collectionName); + bool saveCollectionAsJson(const QString &collection, const QJsonArray &content, const QString &source); QHash m_openedCollections; CollectionDetails m_currentCollection; From 793323cc162538b359ec7a2448561f4a0653f6d9 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Fri, 6 Oct 2023 11:32:38 +0200 Subject: [PATCH 002/242] QmlDesigner: Add .qad filter to project template Task-number: QDS-10815 Change-Id: I5a83e27197f33b39469bcfe5d5d94cccca79d14e Reviewed-by: Miikka Heikkinen --- .../studio_templates/projects/common/app.qmlproject.tpl | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/common/app.qmlproject.tpl b/share/qtcreator/qmldesigner/studio_templates/projects/common/app.qmlproject.tpl index 50840bae070..ab407e22f09 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/common/app.qmlproject.tpl +++ b/share/qtcreator/qmldesigner/studio_templates/projects/common/app.qmlproject.tpl @@ -68,6 +68,11 @@ Project { directory: "asset_imports" } + Files { + filter: "*.qad" + directory: "asset_imports" + } + Files { filter: "*.qml" directory: "asset_imports" From 3dcfd9335bc77d9fddb820c8e79e937a28cfda1b Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Fri, 6 Oct 2023 12:39:33 +0300 Subject: [PATCH 003/242] QmlDesigner: Fix the bug for filtering csv files Change-Id: I932ab0e297aee37e58c910790fdfb72ac8e430fb Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Miikka Heikkinen --- .../components/collectioneditor/collectionview.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp index 53cf76312d4..6c88fe2081d 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp @@ -158,9 +158,12 @@ void CollectionView::refreshModel() if (!model()) return; - // Load Json Collections - const ModelNodes jsonSourceNodes = rootModelNode().subModelNodesOfType(jsonCollectionMetaInfo()); - m_widget->sourceModel()->setSources(jsonSourceNodes); + // Load Collections + const ModelNodes collectionSourceNodes = rootModelNode().subModelNodesOfType( + jsonCollectionMetaInfo()) + + rootModelNode().subModelNodesOfType( + csvCollectionMetaInfo()); + m_widget->sourceModel()->setSources(collectionSourceNodes); } NodeMetaInfo CollectionView::jsonCollectionMetaInfo() const From 7f2b721dbfc33cb7ffd291e3baf27ba5891b3714 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Fri, 6 Oct 2023 14:47:51 +0200 Subject: [PATCH 004/242] QmlDesigner: Fix id in StackView template MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Task-number: QDS-10749 Change-Id: Ic611297f9da64461351a4f29374fdac3709494d2 Reviewed-by: Reviewed-by: Henning Gründl Reviewed-by: Peter Rohles --- .../studio_templates/files/swipeview/file.qml.tpl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/share/qtcreator/qmldesigner/studio_templates/files/swipeview/file.qml.tpl b/share/qtcreator/qmldesigner/studio_templates/files/swipeview/file.qml.tpl index 8da593bd703..b0554bd0760 100644 --- a/share/qtcreator/qmldesigner/studio_templates/files/swipeview/file.qml.tpl +++ b/share/qtcreator/qmldesigner/studio_templates/files/swipeview/file.qml.tpl @@ -8,7 +8,7 @@ Item { height: Constants.height SwipeView { - id: stackLayout + id: stackView width: 100 anchors.top: tabBar.bottom anchors.right: parent.right @@ -16,7 +16,7 @@ Item { anchors.bottom: parent.bottom currentIndex: tabBar.currentIndex - onCurrentIndexChanged: tabBar.currentIndex = stackLayout.currentIndex + onCurrentIndexChanged: tabBar.currentIndex = stackView.currentIndex Item { Label { @@ -47,8 +47,8 @@ Item { id: tabBar currentIndex: 0 anchors.top: parent.top - anchors.right: stackLayout.right - anchors.left: stackLayout.left + anchors.right: stackView.right + anchors.left: stackView.left TabButton { text: qsTr("Tab 1") From e10e0b57674bf5fc31dee8229824671503c0dae6 Mon Sep 17 00:00:00 2001 From: Knud Dollereder Date: Fri, 29 Sep 2023 14:15:00 +0200 Subject: [PATCH 005/242] Use Control or Command and mouse wheel to zoom on mac Fixes: QDS-10791 Change-Id: Iabe9199e4d1e04b8ac2daa94f4926e66ddaf5033 Reviewed-by: Reviewed-by: Thomas Hartmann Reviewed-by: Qt CI Patch Build Bot --- .../components/componentcore/navigation2d.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/plugins/qmldesigner/components/componentcore/navigation2d.cpp b/src/plugins/qmldesigner/components/componentcore/navigation2d.cpp index 0fe7e534acb..27dd56a685b 100644 --- a/src/plugins/qmldesigner/components/componentcore/navigation2d.cpp +++ b/src/plugins/qmldesigner/components/componentcore/navigation2d.cpp @@ -63,7 +63,13 @@ bool Navigation2dFilter::gestureEvent(QGestureEvent *event) bool Navigation2dFilter::wheelEvent(QWheelEvent *event) { - if (!event->modifiers().testFlag(Qt::ControlModifier)) { + bool isMac = Utils::HostOsInfo::isMacHost(); + bool controlPressed = event->modifiers().testFlag(Qt::ControlModifier); + + if (isMac) + controlPressed = controlPressed || event->modifiers().testFlag(Qt::MetaModifier); + + if (!controlPressed) { if (event->source() == Qt::MouseEventSynthesizedBySystem) { emit panChanged(QPointF(event->pixelDelta())); event->accept(); @@ -77,7 +83,6 @@ bool Navigation2dFilter::wheelEvent(QWheelEvent *event) if (zoomChangedConnected) { double speed = 1.0 / 200.0; - bool isMac = Utils::HostOsInfo::isMacHost(); if (QPointF delta = event->pixelDelta(); !delta.isNull() && isMac) { double dist = std::abs(delta.x()) > std::abs(delta.y()) ? -delta.x() : delta.y(); emit zoomChanged(dist * speed, event->position()); From 6b3a278f1358b116f891dde5d280bba6cd418a68 Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Wed, 4 Oct 2023 12:15:41 +0300 Subject: [PATCH 006/242] QmlDesigner: Make the Collection header modifiable Task-number: QDS-10620 Change-Id: I33c7beaf72decebe93a7e4d64d0ca6d6b3590af6 Reviewed-by: Mahmoud Badri --- .../EditPropertyDialog.qml | 100 ++++++++++++++++++ .../SingleCollectionView.qml | 41 ++++++- .../collectioneditor/collectiondetails.cpp | 21 ++++ .../collectioneditor/collectiondetails.h | 2 + .../singlecollectionmodel.cpp | 20 ++++ .../collectioneditor/singlecollectionmodel.h | 7 ++ 6 files changed, 189 insertions(+), 2 deletions(-) create mode 100644 share/qtcreator/qmldesigner/collectionEditorQmlSource/EditPropertyDialog.qml diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/EditPropertyDialog.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/EditPropertyDialog.qml new file mode 100644 index 00000000000..9aa377ea513 --- /dev/null +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/EditPropertyDialog.qml @@ -0,0 +1,100 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import QtQuick +import StudioTheme 1.0 as StudioTheme +import StudioControls 1.0 as StudioControls +import HelperWidgets 2.0 as HelperWidgets + +StudioControls.Dialog { + id: root + + required property var model + property int __propertyIndex: -1 + + title: qsTr("Edit Property") + + function editProperty(index) { + root.__propertyIndex = index + + if (root.__propertyIndex < 0) + return + + let previousName = root.model.headerData(root.__propertyIndex, Qt.Horizontal) + + oldName.text = previousName + newNameField.text = previousName + + root.open() + } + + onAccepted: { + if (newNameField.text !== "" && newNameField.text !== oldName.text) + root.model.renameColumn(root.__propertyIndex, newNameField.text) + } + + contentItem: Column { + spacing: 2 + + Grid { + spacing: 10 + columns: 2 + + Text { + text: qsTr("Previous name") + color: StudioTheme.Values.themeTextColor + } + + Text { + id: oldName + color: StudioTheme.Values.themeTextColor + } + + Text { + text: qsTr("New name") + color: StudioTheme.Values.themeTextColor + } + + StudioControls.TextField { + id: newNameField + + actionIndicator.visible: false + translationIndicator.visible: false + + Keys.onEnterPressed: root.accept() + Keys.onReturnPressed: root.accept() + Keys.onEscapePressed: root.reject() + + validator: HelperWidgets.RegExpValidator { + regExp: /^\w+$/ + } + + onTextChanged: { + editButton.enabled = newNameField.text !== "" + } + } + } + + Item { // spacer + width: 1 + height: 20 + } + + Row { + anchors.right: parent.right + spacing: 10 + + HelperWidgets.Button { + id: editButton + + text: qsTr("Edit") + onClicked: root.accept() + } + + HelperWidgets.Button { + text: qsTr("Cancel") + onClicked: root.reject() + } + } + } +} diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/SingleCollectionView.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/SingleCollectionView.qml index 8ad8a63e916..6bc9e942b6c 100644 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/SingleCollectionView.qml +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/SingleCollectionView.qml @@ -4,6 +4,7 @@ import QtQuick import QtQuick.Controls import StudioTheme 1.0 as StudioTheme +import StudioControls 1.0 as StudioControls Rectangle { id: root @@ -76,13 +77,22 @@ Rectangle { leftPadding: 5 rightPadding: 5 text: display - font.pixelSize: headerMetrics.font + font: headerMetrics.font color: StudioTheme.Values.themeIdleGreen horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter anchors.centerIn: parent elide: Text.ElideRight } + + MouseArea { + anchors.fill: parent + acceptedButtons: Qt.RightButton + onClicked: (event) => { + headerMenu.popIndex(index) + event.accepted = true + } + } } TextMetrics { @@ -91,6 +101,27 @@ Rectangle { font.pixelSize: StudioTheme.Values.baseFontSize text: "Xq" } + + StudioControls.Menu { + id: headerMenu + + property int clickedHeader: -1 + + function popIndex(clickedIndex) + { + headerMenu.clickedHeader = clickedIndex + headerMenu.popup() + } + + onClosed: { + headerMenu.clickedHeader = -1 + } + + StudioControls.MenuItem { + text: qsTr("Edit") + onTriggered: editProperyDialog.editProperty(headerMenu.clickedHeader) + } + } } } @@ -118,7 +149,8 @@ Rectangle { Text { id: itemText - text: display + text: display ? display : "" + width: parent.width leftPadding: 5 topPadding: 3 @@ -131,4 +163,9 @@ Rectangle { } } } + + EditPropertyDialog { + id: editProperyDialog + model: root.model + } } diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp index c2b29d32f08..d4c7dff9105 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp @@ -113,6 +113,27 @@ void CollectionDetails::insertElementAt(std::optional object, int r markChanged(); } +bool CollectionDetails::setHeader(int column, const QString &value) +{ + if (!d->isValidColumnId(column)) + return false; + + const QString oldColumnName = headerAt(column); + if (oldColumnName == value) + return false; + + d->headers.replace(column, value); + for (QJsonObject &element : d->elements) { + if (element.contains(oldColumnName)) { + element.insert(value, element.value(oldColumnName)); + element.remove(oldColumnName); + } + } + + markChanged(); + return true; +} + CollectionReference CollectionDetails::reference() const { return d->reference; diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.h b/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.h index 0362fbfe5bc..a949f45475f 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.h +++ b/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.h @@ -49,6 +49,8 @@ public: void insertElementAt(std::optional object, int row = -1); + bool setHeader(int column, const QString &value); + CollectionReference reference() const; CollectionEditor::SourceFormat sourceFormat() const; QVariant data(int row, int column) const; diff --git a/src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.cpp b/src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.cpp index 35219a98da7..75f52137439 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.cpp @@ -60,6 +60,21 @@ bool SingleCollectionModel::setData(const QModelIndex &, const QVariant &, int) return false; } +bool SingleCollectionModel::setHeaderData(int section, + Qt::Orientation orientation, + const QVariant &value, + [[maybe_unused]] int role) +{ + if (orientation == Qt::Vertical) + return false; + + bool headerChanged = m_currentCollection.setHeader(section, value.toString()); + if (headerChanged) + emit this->headerDataChanged(orientation, section, section); + + return headerChanged; +} + Qt::ItemFlags SingleCollectionModel::flags(const QModelIndex &index) const { if (!index.isValid()) @@ -78,6 +93,11 @@ QVariant SingleCollectionModel::headerData(int section, return {}; } +bool SingleCollectionModel::renameColumn(int section, const QString &newValue) +{ + return setHeaderData(section, Qt::Horizontal, newValue); +} + void SingleCollectionModel::loadCollection(const ModelNode &sourceNode, const QString &collection) { QString fileName = sourceNode.variantProperty(CollectionEditor::SOURCEFILE_PROPERTY).value().toString(); diff --git a/src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.h b/src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.h index 250f1dae5d5..7e09aff0bba 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.h +++ b/src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.h @@ -25,11 +25,18 @@ public: int columnCount(const QModelIndex &parent) const override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override; + bool setHeaderData(int section, + Qt::Orientation orientation, + const QVariant &value, + int role = Qt::EditRole) override; + Qt::ItemFlags flags(const QModelIndex &index) const override; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; + Q_INVOKABLE bool renameColumn(int section, const QString &newValue); + void loadCollection(const ModelNode &sourceNode, const QString &collection); signals: From df8b6310c476697bb8ebc5e26b664a39adebd9bb Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Wed, 4 Oct 2023 18:22:35 +0300 Subject: [PATCH 007/242] QmlDesigner: Select and delete a column of the collection Task-number: QDS-10620 Change-Id: Ieba6ab7e4bf366cffad0c020f4d786096150eb0c Reviewed-by: Mahmoud Badri --- .../SingleCollectionView.qml | 56 +++++++++++++++++++ .../collectioneditor/collectiondetails.cpp | 31 ++++++---- .../collectioneditor/collectiondetails.h | 4 +- .../singlecollectionmodel.cpp | 13 +++++ .../collectioneditor/singlecollectionmodel.h | 1 + 5 files changed, 93 insertions(+), 12 deletions(-) diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/SingleCollectionView.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/SingleCollectionView.qml index 6bc9e942b6c..bf62ebbf5cb 100644 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/SingleCollectionView.qml +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/SingleCollectionView.qml @@ -3,6 +3,7 @@ import QtQuick import QtQuick.Controls +import HelperWidgets 2.0 as HelperWidgets import StudioTheme 1.0 as StudioTheme import StudioControls 1.0 as StudioControls @@ -121,6 +122,11 @@ Rectangle { text: qsTr("Edit") onTriggered: editProperyDialog.editProperty(headerMenu.clickedHeader) } + + StudioControls.MenuItem { + text: qsTr("Delete") + onTriggered: deleteColumnDialog.popUp(headerMenu.clickedHeader) + } } } } @@ -168,4 +174,54 @@ Rectangle { id: editProperyDialog model: root.model } + + StudioControls.Dialog { + id: deleteColumnDialog + + property int clickedIndex: -1 + + title: qsTr("Delete Column") + width: 400 + + onAccepted: { + root.model.removeColumn(clickedIndex) + } + + function popUp(index) + { + deleteColumnDialog.clickedIndex = index + deleteColumnDialog.open() + } + + contentItem: Column { + spacing: 2 + + Text { + text: qsTr("Are you sure that you want to delete column \"%1\"?").arg( + root.model.headerData( + deleteColumnDialog.clickedIndex, Qt.Horizontal)) + color: StudioTheme.Values.themeTextColor + } + + Item { // spacer + width: 1 + height: 20 + } + + Row { + anchors.right: parent.right + spacing: 10 + + HelperWidgets.Button { + text: qsTr("Delete") + onClicked: deleteColumnDialog.accept() + } + + HelperWidgets.Button { + text: qsTr("Cancel") + onClicked: deleteColumnDialog.reject() + } + } + } + } } diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp index d4c7dff9105..a83a0168407 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp @@ -53,7 +53,7 @@ void CollectionDetails::resetDetails(const QStringList &headers, markSaved(); } -void CollectionDetails::insertHeader(const QString &header, int place, const QVariant &defaultValue) +void CollectionDetails::insertColumn(const QString &header, int colIdx, const QVariant &defaultValue) { if (!isValid()) return; @@ -61,8 +61,8 @@ void CollectionDetails::insertHeader(const QString &header, int place, const QVa if (d->headers.contains(header)) return; - if (d->isValidColumnId(place)) - d->headers.insert(place, header); + if (d->isValidColumnId(colIdx)) + d->headers.insert(colIdx, header); else d->headers.append(header); @@ -73,20 +73,31 @@ void CollectionDetails::insertHeader(const QString &header, int place, const QVa markChanged(); } -void CollectionDetails::removeHeader(int place) +bool CollectionDetails::removeColumns(int colIdx, int count) { + if (count < 1) + return false; + if (!isValid()) - return; + return false; - if (!d->isValidColumnId(place)) - return; + if (!d->isValidColumnId(colIdx)) + return false; - const QString header = d->headers.takeAt(place); + int maxPossibleCount = d->headers.count() - colIdx; + count = std::min(maxPossibleCount, count); - for (QJsonObject &element : d->elements) - element.remove(header); + const QStringList removedHeaders = d->headers.mid(colIdx, count); + d->headers.remove(colIdx, count); + + for (const QString &header : std::as_const(removedHeaders)) { + for (QJsonObject &element : d->elements) + element.remove(header); + } markChanged(); + + return true; } void CollectionDetails::insertElementAt(std::optional object, int row) diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.h b/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.h index a949f45475f..2dbfeeb13e2 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.h +++ b/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.h @@ -44,8 +44,8 @@ public: 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 insertColumn(const QString &header, int colIdx = -1, const QVariant &defaultValue = {}); + bool removeColumns(int colIdx, int count = 1); void insertElementAt(std::optional object, int row = -1); diff --git a/src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.cpp b/src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.cpp index 75f52137439..622c1af0557 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.cpp @@ -75,6 +75,19 @@ bool SingleCollectionModel::setHeaderData(int section, return headerChanged; } +bool SingleCollectionModel::removeColumns(int column, int count, const QModelIndex &parent) +{ + if (column < 0 || column >= columnCount(parent) || count < 1) + return false; + + count = std::min(count, columnCount(parent) - column); + beginRemoveColumns(parent, column, column + count); + bool columnsRemoved = m_currentCollection.removeColumns(column, count); + endRemoveColumns(); + + return columnsRemoved; +} + Qt::ItemFlags SingleCollectionModel::flags(const QModelIndex &index) const { if (!index.isValid()) diff --git a/src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.h b/src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.h index 7e09aff0bba..729826bda0e 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.h +++ b/src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.h @@ -29,6 +29,7 @@ public: Qt::Orientation orientation, const QVariant &value, int role = Qt::EditRole) override; + bool removeColumns(int column, int count, const QModelIndex &parent = QModelIndex()) override; Qt::ItemFlags flags(const QModelIndex &index) const override; QVariant headerData(int section, From d9299272bc7ee0e7aaf6a36a4677f5141dc6a7a1 Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Fri, 6 Oct 2023 11:09:41 +0300 Subject: [PATCH 008/242] QmlDesigner: Make collection column selectable Task-number: QDS-10895 Change-Id: Ibf047c5d3f38f93a6a58b692974c843467873610 Reviewed-by: Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Miikka Heikkinen Reviewed-by: Mahmoud Badri --- .../SingleCollectionView.qml | 79 +++++++++++++++++-- .../singlecollectionmodel.cpp | 53 ++++++++++++- .../collectioneditor/singlecollectionmodel.h | 12 ++- 3 files changed, 133 insertions(+), 11 deletions(-) diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/SingleCollectionView.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/SingleCollectionView.qml index bf62ebbf5cb..12706e2cc75 100644 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/SingleCollectionView.qml +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/SingleCollectionView.qml @@ -63,9 +63,9 @@ Rectangle { clip: true delegate: Rectangle { + id: headerItem implicitWidth: 100 implicitHeight: headerText.height - color: StudioTheme.Values.themeControlBackground border.width: 2 border.color: StudioTheme.Values.themeControlOutline clip: true @@ -79,7 +79,6 @@ Rectangle { rightPadding: 5 text: display font: headerMetrics.font - color: StudioTheme.Values.themeIdleGreen horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter anchors.centerIn: parent @@ -88,12 +87,46 @@ Rectangle { MouseArea { anchors.fill: parent - acceptedButtons: Qt.RightButton - onClicked: (event) => { - headerMenu.popIndex(index) - event.accepted = true + acceptedButtons: Qt.LeftButton | Qt.RightButton + onClicked: (mouse) => { + root.model.selectColumn(index) + + if (mouse.button === Qt.RightButton) + headerMenu.popIndex(index) + + mouse.accepted = true } } + + states: [ + State { + name: "default" + when: index !== root.model.selectedColumn + PropertyChanges { + target: headerItem + color: StudioTheme.Values.themeControlBackground + } + + PropertyChanges { + target: headerText + color: StudioTheme.Values.themeIdleGreen + } + }, + State { + name: "selected" + when: index === root.model.selectedColumn + + PropertyChanges { + target: headerItem + color: StudioTheme.Values.themeControlBackgroundInteraction + } + + PropertyChanges { + target: headerText + color: StudioTheme.Values.themeRunningGreen + } + } + ] } TextMetrics { @@ -146,9 +179,9 @@ Rectangle { model: root.model delegate: Rectangle { + id: itemCell implicitWidth: 100 implicitHeight: itemText.height - color: StudioTheme.Values.themeControlBackground border.width: 1 border.color: StudioTheme.Values.themeControlOutline @@ -162,11 +195,41 @@ Rectangle { topPadding: 3 bottomPadding: 3 font.pixelSize: StudioTheme.Values.baseFontSize - color: StudioTheme.Values.themeTextColor horizontalAlignment: Text.AlignLeft verticalAlignment: Text.AlignVCenter elide: Text.ElideRight } + + states: [ + State { + name: "default" + when: !itemSelected + + PropertyChanges { + target: itemCell + color: StudioTheme.Values.themeControlBackground + } + + PropertyChanges { + target: itemText + color: StudioTheme.Values.themeTextColor + } + }, + State { + name: "selected" + when: itemSelected + + PropertyChanges { + target: itemCell + color: StudioTheme.Values.themeControlBackgroundInteraction + } + + PropertyChanges { + target: itemText + color: StudioTheme.Values.themeInteraction + } + } + ] } } diff --git a/src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.cpp b/src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.cpp index 622c1af0557..669044486a2 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.cpp @@ -38,6 +38,16 @@ SingleCollectionModel::SingleCollectionModel(QObject *parent) : QAbstractTableModel(parent) {} +QHash SingleCollectionModel::roleNames() const +{ + static QHash roles; + if (roles.isEmpty()) { + roles.insert(QAbstractTableModel::roleNames()); + roles.insert(SelectedRole, "itemSelected"); + } + return roles; +} + int SingleCollectionModel::rowCount([[maybe_unused]] const QModelIndex &parent) const { return m_currentCollection.rows(); @@ -48,10 +58,14 @@ int SingleCollectionModel::columnCount([[maybe_unused]] const QModelIndex &paren return m_currentCollection.columns(); } -QVariant SingleCollectionModel::data(const QModelIndex &index, int) const +QVariant SingleCollectionModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) return {}; + + if (role == SelectedRole) + return index.column() == m_selectedColumn; + return m_currentCollection.data(index.row(), index.column()); } @@ -106,6 +120,41 @@ QVariant SingleCollectionModel::headerData(int section, return {}; } +int SingleCollectionModel::selectedColumn() const +{ + return m_selectedColumn; +} + +bool SingleCollectionModel::selectColumn(int section) +{ + if (m_selectedColumn == section) + return false; + + const int columns = columnCount(); + + if (m_selectedColumn >= columns) + return false; + + const int rows = rowCount(); + const int previousColumn = m_selectedColumn; + + m_selectedColumn = section; + emit this->selectedColumnChanged(m_selectedColumn); + + auto notifySelectedDataChanged = [this, columns, rows](int notifyingColumn) { + if (notifyingColumn > -1 && notifyingColumn < columns && rows) { + emit dataChanged(index(0, notifyingColumn), + index(rows - 1, notifyingColumn), + {SelectedRole}); + } + }; + + notifySelectedDataChanged(previousColumn); + notifySelectedDataChanged(m_selectedColumn); + + return true; +} + bool SingleCollectionModel::renameColumn(int section, const QString &newValue) { return setHeaderData(section, Qt::Horizontal, newValue); @@ -120,11 +169,13 @@ void SingleCollectionModel::loadCollection(const ModelNode &sourceNode, const QS if (alreadyOpen) { if (m_currentCollection.reference() != newReference) { + selectColumn(-1); beginResetModel(); switchToCollection(newReference); endResetModel(); } } else { + selectColumn(-1); switchToCollection(newReference); if (sourceNode.type() == CollectionEditor::JSONCOLLECTIONMODEL_TYPENAME) loadJsonCollection(fileName, collection); diff --git a/src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.h b/src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.h index 729826bda0e..9e89a948d2a 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.h +++ b/src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.h @@ -17,12 +17,16 @@ class SingleCollectionModel : public QAbstractTableModel Q_OBJECT Q_PROPERTY(QString collectionName MEMBER m_collectionName NOTIFY collectionNameChanged) + Q_PROPERTY(int selectedColumn READ selectedColumn WRITE selectColumn NOTIFY selectedColumnChanged) public: + enum DataRoles { SelectedRole = Qt::UserRole + 1 }; + explicit SingleCollectionModel(QObject *parent = nullptr); - int rowCount(const QModelIndex &parent) const override; - int columnCount(const QModelIndex &parent) const override; + QHash roleNames() const override; + int rowCount(const QModelIndex &parent = {}) const override; + int columnCount(const QModelIndex &parent = {}) const override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override; bool setHeaderData(int section, @@ -35,13 +39,16 @@ public: QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; + int selectedColumn() const; + Q_INVOKABLE bool selectColumn(int section); Q_INVOKABLE bool renameColumn(int section, const QString &newValue); void loadCollection(const ModelNode &sourceNode, const QString &collection); signals: void collectionNameChanged(const QString &collectionName); + void selectedColumnChanged(int); private: void switchToCollection(const CollectionReference &collection); @@ -54,6 +61,7 @@ private: QHash m_openedCollections; CollectionDetails m_currentCollection; + int m_selectedColumn = -1; QString m_collectionName; }; From 9236b1b01980e5fba1c19e46ef7214a0eed07cd7 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Wed, 4 Oct 2023 22:42:30 +0200 Subject: [PATCH 009/242] QmlDesigner: Modernize the code a little bit Change-Id: I24dffa459ad8a948fd58d83249cb07c827a6343f Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Tim Jenssen --- src/libs/utils/algorithm.h | 4 +- src/libs/utils/array.h | 2 + .../designercore/include/modelfwd.h | 1 + .../designercore/model/modelnode.cpp | 50 +++++++------- .../designercore/model/modelutils.h | 10 +++ .../designercore/model/texttomodelmerger.cpp | 67 ++++++++++--------- 6 files changed, 77 insertions(+), 57 deletions(-) diff --git a/src/libs/utils/algorithm.h b/src/libs/utils/algorithm.h index 496a2f06298..3c0a4b45c1f 100644 --- a/src/libs/utils/algorithm.h +++ b/src/libs/utils/algorithm.h @@ -1410,13 +1410,13 @@ OutputContainer setUnionMerge(InputContainer1 &&input1, } template -auto usize(const Container &container) +constexpr auto usize(const Container &container) { return static_cast>(std::size(container)); } template -auto ssize(const Container &container) +constexpr auto ssize(const Container &container) { return static_cast>(std::size(container)); } diff --git a/src/libs/utils/array.h b/src/libs/utils/array.h index 0d9a6047281..c89778871ca 100644 --- a/src/libs/utils/array.h +++ b/src/libs/utils/array.h @@ -3,6 +3,8 @@ #pragma once +#include + namespace Utils { namespace Internal { diff --git a/src/plugins/qmldesigner/designercore/include/modelfwd.h b/src/plugins/qmldesigner/designercore/include/modelfwd.h index eedc46e3b56..0a062289fd0 100644 --- a/src/plugins/qmldesigner/designercore/include/modelfwd.h +++ b/src/plugins/qmldesigner/designercore/include/modelfwd.h @@ -48,6 +48,7 @@ public: /* */ using TypeName = QByteArray; +using TypeNameView = QByteArrayView; using PropertyTypeList = QList; using IdName = QByteArray; class Model; diff --git a/src/plugins/qmldesigner/designercore/model/modelnode.cpp b/src/plugins/qmldesigner/designercore/model/modelnode.cpp index fe973f04abd..56f50223c9e 100644 --- a/src/plugins/qmldesigner/designercore/model/modelnode.cpp +++ b/src/plugins/qmldesigner/designercore/model/modelnode.cpp @@ -7,6 +7,7 @@ #include "bindingproperty.h" #include "internalnode_p.h" #include "model_p.h" +#include "modelutils.h" #include "nodeabstractproperty.h" #include "nodelistproperty.h" #include "nodeproperty.h" @@ -19,12 +20,15 @@ #include #include +#include #include #include #include #include +#include + namespace QmlDesigner { using namespace QmlDesigner::Internal; @@ -84,44 +88,42 @@ QString ModelNode::validId() return id(); } -static bool idIsQmlKeyWord(const QString &id) +namespace { +bool isQmlKeyWord(QStringView id) { - static const QSet keywords = {"as", "break", "case", "catch", - "continue", "debugger", "default", "delete", - "do", "else", "finally", "for", - "function", "if", "import", "in", - "instanceof", "new", "print", "return", - "switch", "this", "throw", "try", - "typeof", "var", "void", "while", - "with"}; + static constexpr auto keywords = Utils::to_array( + {u"as", u"break", u"case", u"catch", u"continue", u"debugger", + u"default", u"delete", u"do", u"else", u"finally", u"for", + u"function", u"if", u"import", u"in", u"instanceof", u"new", + u"print", u"return", u"switch", u"this", u"throw", u"try", + u"typeof", u"var", u"void", u"while", u"with"}); - return keywords.contains(id); + return std::binary_search(keywords.begin(), keywords.end(), ModelUtils::toStdStringView(id)); } -static bool isIdToAvoid(const QString &id) +bool isIdToAvoid(QStringView id) { - static const QSet ids = {"top", "bottom", "left", "right", - "width", "height", "x", "y", - "opacity", "parent", "item", "flow", - "color", "margin", "padding", "border", - "font", "text", "source", "state", - "visible", "focus", "data", "clip", - "layer", "scale", "enabled", "anchors", - "texture", "shaderInfo", "sprite", "spriteSequence", - "baseState", "rect"}; + static constexpr auto token = Utils::to_array( + {u"anchors", u"baseState", u"border", u"bottom", u"clip", u"color", + u"data", u"enabled", u"flow", u"focus", u"font", u"height", + u"item", u"layer", u"left", u"margin", u"opacity", u"padding", + u"parent", u"rect", u"right", u"scale", u"shaderInfo", u"source", + u"sprite", u"spriteSequence", u"state", u"text", u"texture", u"top", + u"visible", u"width", u"x", u"y"}); - return ids.contains(id); + return std::binary_search(token.begin(), token.end(), ModelUtils::toStdStringView(id)); } -static bool idContainsWrongLetter(const QString &id) +bool idContainsWrongLetter(const QString &id) { static QRegularExpression idExpr(QStringLiteral("^[a-z_][a-zA-Z0-9_]*$")); return !id.contains(idExpr); } +} // namespace bool ModelNode::isValidId(const QString &id) { - return id.isEmpty() || (!idContainsWrongLetter(id) && !idIsQmlKeyWord(id) && !isIdToAvoid(id)); + return id.isEmpty() || (!idContainsWrongLetter(id) && !isQmlKeyWord(id) && !isIdToAvoid(id)); } QString ModelNode::getIdValidityErrorMessage(const QString &id) @@ -138,7 +140,7 @@ QString ModelNode::getIdValidityErrorMessage(const QString &id) if (id.contains(' ')) return QObject::tr("ID cannot include whitespace (%1).").arg(id); - if (idIsQmlKeyWord(id)) + if (isQmlKeyWord(id)) return QObject::tr("%1 is a reserved QML keyword.").arg(id); if (isIdToAvoid(id)) diff --git a/src/plugins/qmldesigner/designercore/model/modelutils.h b/src/plugins/qmldesigner/designercore/model/modelutils.h index 3f8ddeab56b..2cef2e1fb26 100644 --- a/src/plugins/qmldesigner/designercore/model/modelutils.h +++ b/src/plugins/qmldesigner/designercore/model/modelutils.h @@ -5,10 +5,15 @@ #include "qmldesignercorelib_global.h" +#include + #include #include +#include + #include +#include namespace QmlDesigner { class PropertyMetaInfo; @@ -41,4 +46,9 @@ QMLDESIGNERCORE_EXPORT QList allModelNodesWithId(AbstractView *view); QMLDESIGNERCORE_EXPORT bool isThisOrAncestorLocked(const ModelNode &node); QMLDESIGNERCORE_EXPORT ModelNode lowestCommonAncestor(Utils::span nodes); +constexpr std::u16string_view toStdStringView(QStringView view) +{ + return {view.utf16(), Utils::usize(view)}; +} + } // namespace QmlDesigner::ModelUtils diff --git a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp index def00f395d3..75a272931de 100644 --- a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp +++ b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp @@ -22,6 +22,7 @@ #include #include +#include #include #include @@ -37,6 +38,7 @@ #include #include +#include #include #include @@ -78,43 +80,46 @@ bool isSupportedVersion(QmlDesigner::Version version) bool isGlobalQtEnums(QStringView value) { - static constexpr QLatin1StringView list[] = { - "Horizontal"_L1, "Vertical"_L1, "AlignVCenter"_L1, "AlignLeft"_L1, - "LeftToRight"_L1, "RightToLeft"_L1, "AlignHCenter"_L1, "AlignRight"_L1, - "AlignBottom"_L1, "AlignBaseline"_L1, "AlignTop"_L1, "BottomLeft"_L1, - "LeftEdge"_L1, "RightEdge"_L1, "BottomEdge"_L1, "TopEdge"_L1, - "TabFocus"_L1, "ClickFocus"_L1, "StrongFocus"_L1, "WheelFocus"_L1, - "NoFocus"_L1, "ArrowCursor"_L1, "UpArrowCursor"_L1, "CrossCursor"_L1, - "WaitCursor"_L1, "IBeamCursor"_L1, "SizeVerCursor"_L1, "SizeHorCursor"_L1, - "SizeBDiagCursor"_L1, "SizeFDiagCursor"_L1, "SizeAllCursor"_L1, "BlankCursor"_L1, - "SplitVCursor"_L1, "SplitHCursor"_L1, "PointingHandCursor"_L1, "ForbiddenCursor"_L1, - "WhatsThisCursor"_L1, "BusyCursor"_L1, "OpenHandCursor"_L1, "ClosedHandCursor"_L1, - "DragCopyCursor"_L1, "DragMoveCursor"_L1, "DragLinkCursor"_L1, "TopToBottom"_L1, - "LeftButton"_L1, "RightButton"_L1, "MiddleButton"_L1, "BackButton"_L1, - "ForwardButton"_L1, "AllButtons"_L1}; + static constexpr auto list = Utils::to_array( + {u"AlignBaseline", u"AlignBottom", u"AlignHCenter", u"AlignLeft", + u"AlignRight", u"AlignTop", u"AlignVCenter", u"AllButtons", + u"ArrowCursor", u"BackButton", u"BlankCursor", u"BottomEdge", + u"BottomLeft", u"BusyCursor", u"ClickFocus", u"ClosedHandCursor", + u"CrossCursor", u"DragCopyCursor", u"DragLinkCursor", u"DragMoveCursor", + u"ForbiddenCursor", u"ForwardButton", u"Horizontal", u"IBeamCursor", + u"LeftButton", u"LeftEdge", u"LeftToRight", u"MiddleButton", + u"NoFocus", u"OpenHandCursor", u"PointingHandCursor", u"RightButton", + u"RightEdge", u"RightToLeft", u"SizeAllCursor", u"SizeBDiagCursor", + u"SizeFDiagCursor", u"SizeHorCursor", u"SizeVerCursor", u"SplitHCursor", + u"SplitVCursor", u"StrongFocus", u"TabFocus", u"TopEdge", + u"TopToBottom", u"UpArrowCursor", u"Vertical", u"WaitCursor", + u"WhatsThisCursor", u"WheelFocus"}); - return std::find(std::begin(list), std::end(list), value) != std::end(list); + return std::binary_search(std::begin(list), + std::end(list), + QmlDesigner::ModelUtils::toStdStringView(value)); } bool isKnownEnumScopes(QStringView value) { - static constexpr QLatin1StringView list[] = {"TextInput"_L1, - "TextEdit"_L1, - "Material"_L1, - "Universal"_L1, - "Font"_L1, - "Shape"_L1, - "ShapePath"_L1, - "AbstractButton"_L1, - "Text"_L1, - "ShaderEffectSource"_L1, - "Grid"_L1, - "ItemLayer"_L1, - "ImageLayer"_L1, - "SpriteLayer"_L1, - "Light"_L1}; + static constexpr auto list = Utils::to_array({u"TextInput", + u"TextEdit", + u"Material", + u"Universal", + u"Font", + u"Shape", + u"ShapePath", + u"AbstractButton", + u"Text", + u"ShaderEffectSource", + u"Grid", + u"ItemLayer", + u"ImageLayer", + u"SpriteLayer", + u"Light"}); - return std::find(std::begin(list), std::end(list), value) != std::end(list); + return std::find(std::begin(list), std::end(list), QmlDesigner::ModelUtils::toStdStringView(value)) + != std::end(list); } bool supportedQtQuickVersion(const QmlDesigner::Import &import) From 740e1edb98df7ebacad2a45bfa31465c9bca8fa5 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Wed, 4 Oct 2023 21:51:33 +0200 Subject: [PATCH 010/242] Sqlite: Remove default constructor Because it holds a reference it is not possible to have a default constructor. Change-Id: I442bedf5fb2abfd79ad64520c74636340dabf741 Reviewed-by: Tim Jenssen Reviewed-by: Qt CI Patch Build Bot Reviewed-by: --- tests/unit/tests/mocks/sqlitereadstatementmock.h | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/unit/tests/mocks/sqlitereadstatementmock.h b/tests/unit/tests/mocks/sqlitereadstatementmock.h index 615e3d23b14..ed23409b97d 100644 --- a/tests/unit/tests/mocks/sqlitereadstatementmock.h +++ b/tests/unit/tests/mocks/sqlitereadstatementmock.h @@ -28,7 +28,6 @@ class SqliteDatabaseMock; class SqliteReadStatementMockBase { public: - SqliteReadStatementMockBase() = default; SqliteReadStatementMockBase(Utils::SmallStringView sqlStatement, SqliteDatabaseMock &databaseMock); MOCK_METHOD(std::vector, valuesReturnStringVector, (std::size_t), ()); From 55039d69750396086eb7e9b50833a984c8fb3cf9 Mon Sep 17 00:00:00 2001 From: Samuel Jose Raposo Vieira Mira Date: Wed, 27 Sep 2023 18:18:09 +0300 Subject: [PATCH 011/242] Update supported feature list for MCU in QDS 4.3 docs . Connections to mention Item Qt as Call functions is not filtered . Translations to Partially Supported but using regular Qt . Curves to that QtForMcus 2.6 supports easing.bezierCurve Task-number: QDS-9959 Change-Id: Ib60223609f90f108e1a029d093f9fbeda3c82b64 Reviewed-by: Johanna Vanhatapio Reviewed-by: Yoann Lopes Reviewed-by: Thomas Hartmann Reviewed-by: Qt CI Patch Build Bot --- ...designstudio-features-on-mcu-projects.qdoc | 23 ++++++++++++------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/doc/qtdesignstudio/src/mcus/qtdesignstudio-features-on-mcu-projects.qdoc b/doc/qtdesignstudio/src/mcus/qtdesignstudio-features-on-mcu-projects.qdoc index 89c1051cf90..196edd941bc 100644 --- a/doc/qtdesignstudio/src/mcus/qtdesignstudio-features-on-mcu-projects.qdoc +++ b/doc/qtdesignstudio/src/mcus/qtdesignstudio-features-on-mcu-projects.qdoc @@ -25,7 +25,8 @@ \li \b - \li A scene in the \uicontrol 2D view is rendered by the regular Qt Quick and QML, and not as \QUL and \QMCU, so some imperfections or inaccuracies - can occur. + can occur. Note that the default font used in \QDS preview and \QUL are + different, and the developer must confirm both fonts are the same. \row \li \l 3D \li \b - @@ -71,15 +72,19 @@ \li \b - \li \b - \li The \uicontrol Connections view displays all signal handlers in the - current file but it doesn't filter available signals, so you can still - see and select signals that are available in Qt Quick, but not in \QUL. + current file, but it doesn't filter available signals, so you can still + see and select signals available in Qt Quick, but not in \QUL. + The same also applies if \uicontrol Action is set to \uicontrol{Call Function} + and \uicontrol Item is set to \uicontrol Qt. See the component documentation + to filter available signal/function. \row \li \l {States} \li \b X \li \b - \li \b - \li The feature is fully supported as such, but there are some - limitations listed in \l {\QMCU Known Issues or Limitations}. + limitations such as StateGroup and the ones listed in + \l {\QMCU Known Issues or Limitations}. \row \li \l {Transitions} \li \b X @@ -89,9 +94,11 @@ \row \li \l {Translations} \li \b - - \li \b - \li \b X \li \b - + \li The \uicontrol Translations view previews with regular Qt Quick instead + of \QUL, and it can be inaccurate in calculating the text overflow in some translations. + Also, the developer needs to configure the \QUL project to use \QDS translations (.ts) files. \row \li \l {Timeline} \li \b X @@ -100,11 +107,11 @@ \li \b - \row \li \l {Curves} - \li \b - \li \b X \li \b - - \li Linear interpolation works, but \QMCU does not support the - \c easing.bezierCurve property of a keyframe. + \li \b - + \li Linear interpolation works, and \QMCU supports the \c easing.bezierCurve property + of a keyframe in \QMCU 2.6 or higher. \row \li \l Code \li \b X From 32b68b296a862bcfd1c17f2bf1a5765043dd6fe3 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Fri, 6 Oct 2023 17:46:25 +0300 Subject: [PATCH 012/242] QmlDesigner: Add enter and leave event support to 3D view Fixes: QDS-10917 Change-Id: Iefcf92bd4a747d35f44e47c438548338fccfc4a2 Reviewed-by: Mahmoud Badri Reviewed-by: Qt CI Patch Build Bot --- .../commands/inputeventcommand.cpp | 17 ++++++++++++++--- .../commands/inputeventcommand.h | 2 +- .../components/edit3d/edit3dcanvas.cpp | 12 ++++++++++++ .../components/edit3d/edit3dcanvas.h | 2 ++ .../components/edit3d/edit3dview.cpp | 2 +- .../qmldesigner/components/edit3d/edit3dview.h | 2 +- .../designercore/include/nodeinstanceview.h | 2 +- .../designercore/instances/nodeinstanceview.cpp | 2 +- .../qt5informationnodeinstanceserver.cpp | 8 +++++++- 9 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/libs/qmlpuppetcommunication/commands/inputeventcommand.cpp b/src/libs/qmlpuppetcommunication/commands/inputeventcommand.cpp index 0dc669406af..2ba1a651228 100644 --- a/src/libs/qmlpuppetcommunication/commands/inputeventcommand.cpp +++ b/src/libs/qmlpuppetcommunication/commands/inputeventcommand.cpp @@ -10,10 +10,16 @@ namespace QmlDesigner { InputEventCommand::InputEventCommand() = default; -InputEventCommand::InputEventCommand(QInputEvent *e) - : m_type(e->type()), - m_modifiers(e->modifiers()) +InputEventCommand::InputEventCommand(QEvent *e) + : m_type(e->type()) { + // Leave events are not actual input events + if (m_type == QEvent::Leave) + return; + + auto ie = static_cast(e); + m_modifiers = ie->modifiers(); + if (m_type == QEvent::Wheel) { auto we = static_cast(e); #if QT_VERSION <= QT_VERSION_CHECK(5, 15, 0) @@ -28,6 +34,11 @@ InputEventCommand::InputEventCommand(QInputEvent *e) m_key = ke->key(); m_count = ke->count(); m_autoRepeat = ke->isAutoRepeat(); + } else if (m_type == QEvent::Enter) { + auto spe = static_cast(e); + m_pos = spe->position().toPoint(); + m_button = spe->button(); + m_buttons = spe->buttons(); } else { auto me = static_cast(e); m_pos = me->pos(); diff --git a/src/libs/qmlpuppetcommunication/commands/inputeventcommand.h b/src/libs/qmlpuppetcommunication/commands/inputeventcommand.h index e0a220cd693..7cd2c898fe4 100644 --- a/src/libs/qmlpuppetcommunication/commands/inputeventcommand.h +++ b/src/libs/qmlpuppetcommunication/commands/inputeventcommand.h @@ -18,7 +18,7 @@ class InputEventCommand public: InputEventCommand(); - explicit InputEventCommand(QInputEvent *e); + explicit InputEventCommand(QEvent *e); QEvent::Type type() const { return m_type; } QPoint pos() const { return m_pos; } diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dcanvas.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dcanvas.cpp index 60cba025840..6afe4fa8164 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dcanvas.cpp +++ b/src/plugins/qmldesigner/components/edit3d/edit3dcanvas.cpp @@ -155,6 +155,18 @@ void Edit3DCanvas::focusInEvent(QFocusEvent *focusEvent) QWidget::focusInEvent(focusEvent); } +void Edit3DCanvas::enterEvent(QEnterEvent *e) +{ + m_parent->view()->sendInputEvent(e); + QWidget::enterEvent(e); +} + +void Edit3DCanvas::leaveEvent(QEvent *e) +{ + m_parent->view()->sendInputEvent(e); + QWidget::leaveEvent(e); +} + void Edit3DCanvas::positionBusyInidicator() { m_busyIndicator->move(width() / 2 - 32, height() / 2 - 32); diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dcanvas.h b/src/plugins/qmldesigner/components/edit3d/edit3dcanvas.h index d575e68c139..810e246f8a3 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dcanvas.h +++ b/src/plugins/qmldesigner/components/edit3d/edit3dcanvas.h @@ -38,6 +38,8 @@ protected: void resizeEvent(QResizeEvent *e) override; void focusOutEvent(QFocusEvent *focusEvent) override; void focusInEvent(QFocusEvent *focusEvent) override; + void enterEvent(QEnterEvent *e) override; + void leaveEvent(QEvent *e) override; private: void positionBusyInidicator(); diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp index fc81eef4797..9831ee18467 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp +++ b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp @@ -431,7 +431,7 @@ void Edit3DView::nodeRemoved(const ModelNode &, updateAlignActionStates(); } -void Edit3DView::sendInputEvent(QInputEvent *e) const +void Edit3DView::sendInputEvent(QEvent *e) const { if (nodeInstanceView()) nodeInstanceView()->sendInputEvent(e); diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dview.h b/src/plugins/qmldesigner/components/edit3d/edit3dview.h index 2fb1e0451cd..e5d41614958 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dview.h +++ b/src/plugins/qmldesigner/components/edit3d/edit3dview.h @@ -52,7 +52,7 @@ public: void nodeRemoved(const ModelNode &removedNode, const NodeAbstractProperty &parentProperty, PropertyChangeFlags propertyChange) override; - void sendInputEvent(QInputEvent *e) const; + void sendInputEvent(QEvent *e) const; void edit3DViewResized(const QSize &size) const; QSize canvasSize() const; diff --git a/src/plugins/qmldesigner/designercore/include/nodeinstanceview.h b/src/plugins/qmldesigner/designercore/include/nodeinstanceview.h index fd7f21d090b..80727bf5a9e 100644 --- a/src/plugins/qmldesigner/designercore/include/nodeinstanceview.h +++ b/src/plugins/qmldesigner/designercore/include/nodeinstanceview.h @@ -132,7 +132,7 @@ public: void selectedNodesChanged(const QList &selectedNodeList, const QList &lastSelectedNodeList) override; - void sendInputEvent(QInputEvent *e) const; + void sendInputEvent(QEvent *e) const; void view3DAction(View3DActionType type, const QVariant &value) override; void requestModelNodePreviewImage(const ModelNode &node, const ModelNode &renderNode) const; void edit3DViewResized(const QSize &size) const; diff --git a/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp b/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp index 23146996afc..641455910a0 100644 --- a/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp +++ b/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp @@ -1764,7 +1764,7 @@ void NodeInstanceView::selectedNodesChanged(const QList &selectedNode m_rotBlockTimer.start(); } -void NodeInstanceView::sendInputEvent(QInputEvent *e) const +void NodeInstanceView::sendInputEvent(QEvent *e) const { m_nodeInstanceServer->inputEvent(InputEventCommand(e)); } diff --git a/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp b/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp index bff0732d0c1..7647fd8ddbd 100644 --- a/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp +++ b/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp @@ -213,7 +213,7 @@ void Qt5InformationNodeInstanceServer::handleInputEvents() // Peek at next command. If that is also a wheel with same button/modifiers // state, skip this event and add the angle delta to the next one. auto nextCommand = m_pendingInputEventCommands[i + 1]; - if (nextCommand.type() == QEvent::MouseMove + if (nextCommand.type() == QEvent::Wheel && nextCommand.button() == command.button() && nextCommand.buttons() == command.buttons() && nextCommand.modifiers() == command.modifiers()) { @@ -232,6 +232,12 @@ void Qt5InformationNodeInstanceServer::handleInputEvents() QKeyEvent *ke = new QKeyEvent(command.type(), command.key(), command.modifiers(), QString(), command.autoRepeat(), command.count()); QGuiApplication::sendEvent(m_editView3DData.window, ke); + } else if (command.type() == QEvent::Enter) { + QEnterEvent *ee = new QEnterEvent(command.pos(), {}, {}); + QGuiApplication::sendEvent(m_editView3DData.window, ee); + } else if (command.type() == QEvent::Leave) { + QEvent *e = new QEvent(command.type()); + QGuiApplication::sendEvent(m_editView3DData.window, e); } else { if (command.type() == QEvent::MouseMove && i < m_pendingInputEventCommands.size() - 1) { // Peek at next command. If that is also a move with only difference being From 9712d4ca04c2e894dc6db76137d17564e62944ba Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Fri, 6 Oct 2023 17:40:23 +0300 Subject: [PATCH 013/242] QmlDesigner: Replace AxisHelper with OriginGizmo The new gizmo is more intuitive. Fixes: QDS-10880 Change-Id: I8d3f1723ae0fb748d1b96c31b10e6753e0e2c709 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Reviewed-by: Mahmoud Badri --- src/tools/qml2puppet/editor3d_qt6.qrc | 4 +- .../mockfiles/meshes/axishelper.mesh | Bin 158516 -> 0 bytes .../qml2puppet/mockfiles/qt6/AxisHelper.qml | 111 ------- .../mockfiles/qt6/AxisHelperArm.qml | 48 ---- .../mockfiles/qt6/EditCameraController.qml | 8 + .../qml2puppet/mockfiles/qt6/EditView3D.qml | 15 +- .../qml2puppet/mockfiles/qt6/OriginGizmo.qml | 270 ++++++++++++++++++ .../qml2puppet/editor3d/generalhelper.cpp | 13 + .../qml2puppet/editor3d/generalhelper.h | 3 + 9 files changed, 304 insertions(+), 168 deletions(-) delete mode 100644 src/tools/qml2puppet/mockfiles/meshes/axishelper.mesh delete mode 100644 src/tools/qml2puppet/mockfiles/qt6/AxisHelper.qml delete mode 100644 src/tools/qml2puppet/mockfiles/qt6/AxisHelperArm.qml create mode 100644 src/tools/qml2puppet/mockfiles/qt6/OriginGizmo.qml diff --git a/src/tools/qml2puppet/editor3d_qt6.qrc b/src/tools/qml2puppet/editor3d_qt6.qrc index df498e643cf..c99a586fd9c 100644 --- a/src/tools/qml2puppet/editor3d_qt6.qrc +++ b/src/tools/qml2puppet/editor3d_qt6.qrc @@ -4,7 +4,6 @@ mockfiles/meshes/scalerod.mesh mockfiles/meshes/ring.mesh mockfiles/meshes/ringselect.mesh - mockfiles/meshes/axishelper.mesh mockfiles/images/editor_camera.png mockfiles/images/editor_camera@2x.png mockfiles/images/editor_particlesystem.png @@ -21,8 +20,6 @@ mockfiles/qt6/AdjustableArrow.qml mockfiles/qt6/Arrow.qml mockfiles/qt6/AutoScaleHelper.qml - mockfiles/qt6/AxisHelper.qml - mockfiles/qt6/AxisHelperArm.qml mockfiles/qt6/CameraFrustum.qml mockfiles/qt6/CameraGizmo.qml mockfiles/qt6/DirectionalDraggable.qml @@ -41,6 +38,7 @@ mockfiles/qt6/ModelNodeView.qml mockfiles/qt6/MoveGizmo.qml mockfiles/qt6/NodeNodeView.qml + mockfiles/qt6/OriginGizmo.qml mockfiles/qt6/Overlay2D.qml mockfiles/qt6/ParticleSystemGizmo.qml mockfiles/qt6/ParticleEmitterGizmo.qml diff --git a/src/tools/qml2puppet/mockfiles/meshes/axishelper.mesh b/src/tools/qml2puppet/mockfiles/meshes/axishelper.mesh deleted file mode 100644 index 3e9e4958e4b694eb6bbd34185ff7a5e703866572..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 158516 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

Q&Ic9{)-CAfm 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 { - pick(mouse); - } - - onPressed: (mouse)=> { - pick(mouse); - if (pickObj) { - axisHelperView.editCameraCtrl.focusObject(axisHelperView.selectedNode, - pickObj.cameraRotation, false, false); - } else { - mouse.accepted = false; - } - } - - onExited: cancelHover() - onCanceled: cancelHover() - } -} diff --git a/src/tools/qml2puppet/mockfiles/qt6/AxisHelperArm.qml b/src/tools/qml2puppet/mockfiles/qt6/AxisHelperArm.qml deleted file mode 100644 index 683075c4c7f..00000000000 --- a/src/tools/qml2puppet/mockfiles/qt6/AxisHelperArm.qml +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright (C) 2019 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -import QtQuick 6.0 -import QtQuick3D 6.0 - -Node { - id: armRoot - property alias posModel: posModel - property alias negModel: negModel - property View3D view3D - property color hoverColor - property color color - property vector3d camRotPos - property vector3d camRotNeg - - Model { - id: posModel - - property bool hovering: false - property vector3d cameraRotation: armRoot.camRotPos - - source: "../meshes/axishelper.mesh" - materials: DefaultMaterial { - id: posMat - diffuseColor: posModel.hovering ? armRoot.hoverColor : armRoot.color - lighting: DefaultMaterial.NoLighting - } - pickable: true - } - - Model { - id: negModel - - property bool hovering: false - property vector3d cameraRotation: armRoot.camRotNeg - - source: "#Sphere" - y: -6 - scale: Qt.vector3d(0.025, 0.025, 0.025) - materials: DefaultMaterial { - id: negMat - diffuseColor: negModel.hovering ? armRoot.hoverColor : armRoot.color - lighting: DefaultMaterial.NoLighting - } - pickable: true - } -} diff --git a/src/tools/qml2puppet/mockfiles/qt6/EditCameraController.qml b/src/tools/qml2puppet/mockfiles/qt6/EditCameraController.qml index 42712d56a5d..7f54f4a09d6 100644 --- a/src/tools/qml2puppet/mockfiles/qt6/EditCameraController.qml +++ b/src/tools/qml2puppet/mockfiles/qt6/EditCameraController.qml @@ -88,6 +88,14 @@ Item { storeCameraState(0); } + function jumpToRotation(rotation) { + let distance = camera.scenePosition.minus(_lookAtPoint).length() + let direction = _generalHelper.dirForRotation(rotation) + + camera.rotation = rotation + camera.position = _lookAtPoint.plus(direction.times(distance)) + } + function alignCameras(targetNodes) { if (!camera) diff --git a/src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml b/src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml index a44a2a75d2d..4e2f4ea91d7 100644 --- a/src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml +++ b/src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml @@ -1135,14 +1135,17 @@ Item { } } - AxisHelper { + OriginGizmo { anchors.right: parent.right anchors.top: parent.top - width: 100 - height: width - editCameraCtrl: cameraControl - selectedNode: viewRoot.selectedNodes.length === 1 ? viewRoot.selectionBoxes[0].model - : viewRoot.selectedNode + anchors.margins: 10 + width: 120 + height: 120 + targetNode: cameraControl.camera + + onAxisClicked: (axis) => { + cameraControl.jumpToRotation(quaternionForAxis(axis)); + } } Text { diff --git a/src/tools/qml2puppet/mockfiles/qt6/OriginGizmo.qml b/src/tools/qml2puppet/mockfiles/qt6/OriginGizmo.qml new file mode 100644 index 00000000000..7b0ea16704d --- /dev/null +++ b/src/tools/qml2puppet/mockfiles/qt6/OriginGizmo.qml @@ -0,0 +1,270 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import QtQuick +import QtQuick3D + +Item { + id: root + required property Node targetNode + + enum Axis { + PositiveX, + PositiveY, + PositiveZ, + NegativeX, + NegativeY, + NegativeZ + } + + readonly property list rotations: [ + Qt.quaternion(Math.SQRT1_2, 0, Math.SQRT1_2, 0), // PositiveX + Qt.quaternion(Math.SQRT1_2, -Math.SQRT1_2, 0, 0), // PositiveY + Qt.quaternion(1, 0, 0, 0), // PositiveZ + Qt.quaternion(Math.SQRT1_2, 0, -Math.SQRT1_2, 0), // NegativeX + Qt.quaternion(Math.SQRT1_2, Math.SQRT1_2, 0, 0), // NegativeY + Qt.quaternion(0, 0, 1, 0) // NegativeZ + ] + + function quaternionForAxis(axis) { + return rotations[axis] + } + + signal axisClicked(int axis) + + QtObject { + id: stylePalette + property color brightBall: "#eeeeee" + property color dimBall: "#111111" + property color xAxis: "#ff0000" + property color yAxis: "#00aa00" + property color zAxis: "#1515ff" + property color background: "#aa303030" + } + + component LineRectangle : Rectangle { + property vector2d startPoint: Qt.vector2d(0, 0) + property vector2d endPoint: Qt.vector2d(0, 0) + property real lineWidth: 5 + transformOrigin: Item.Left + height: lineWidth + antialiasing: true + + readonly property vector2d offset: startPoint.plus(endPoint).times(0.5); + + width: startPoint.minus(endPoint).length() + rotation: Math.atan2(endPoint.y - startPoint.y, endPoint.x - startPoint.x) * 180 / Math.PI + } + + Rectangle { + id: ballBackground + anchors.centerIn: parent + width: parent.width > parent.height ? parent.height : parent.width + height: width + radius: width / 2 + color: ballBackgroundHoverHandler.hovered ? stylePalette.background : "transparent" + antialiasing: true + + readonly property real subBallWidth: width / 5 + readonly property real subBallHalfWidth: subBallWidth * 0.5 + readonly property real subBallOffset: radius - subBallWidth / 2 + + Item { + anchors.centerIn: parent + + component SubBall : Rectangle { + id: subBallRoot + required property Node targetNode + required property real offset + + property alias labelText: label.text + property alias labelColor: label.color + property alias labelVisible: label.visible + property alias hovered: subBallHoverHandler.hovered + property vector3d initialPosition: Qt.vector3d(0, 0, 0) + readonly property vector3d position: quaternionVectorMultiply(targetNode.sceneRotation.inverted(), initialPosition) + + signal tapped() + + function quaternionVectorMultiply(q, v) { + let qv = Qt.vector3d(q.x, q.y, q.z) + let uv = qv.crossProduct(v) + let uuv = qv.crossProduct(uv) + uv = uv.times(2.0 * q.scalar) + uuv = uuv.times(2.0) + return v.plus(uv).plus(uuv) + } + + antialiasing: true + height: width + radius: width / 2 + x: offset * position.x - width / 2 + y: offset * -position.y - height / 2 + z: position.z + + HoverHandler { + id: subBallHoverHandler + } + + MouseArea { + anchors.fill: parent + acceptedButtons: Qt.LeftButton + onClicked: (e) => { + subBallRoot.tapped() + e.accepted = true + } + } + + Text { + id: label + anchors.centerIn: parent + antialiasing: true + } + } + + SubBall { + id: positiveX + targetNode: root.targetNode + width: ballBackground.subBallWidth + offset: ballBackground.subBallOffset + labelText: qsTr("X") + labelColor: hovered ? stylePalette.brightBall : stylePalette.dimBall + color: stylePalette.xAxis + initialPosition: Qt.vector3d(1, 0, 0) + onTapped: { + let axis = OriginGizmo.Axis.PositiveX + if (_generalHelper.compareVectors(root.targetNode.sceneRotation, + root.quaternionForAxis(OriginGizmo.Axis.PositiveX))) { + axis = OriginGizmo.Axis.NegativeX + } + root.axisClicked(axis) + } + } + + LineRectangle { + endPoint: Qt.vector2d(positiveX.x + ballBackground.subBallHalfWidth, positiveX.y + ballBackground.subBallHalfWidth) + color: stylePalette.xAxis + z: positiveX.z - 0.001 + } + + SubBall { + id: positiveY + targetNode: root.targetNode + width: ballBackground.subBallWidth + offset: ballBackground.subBallOffset + labelText: qsTr("Y") + labelColor: hovered ? stylePalette.brightBall : stylePalette.dimBall + color: stylePalette.yAxis + initialPosition: Qt.vector3d(0, 1, 0) + onTapped: { + let axis = OriginGizmo.Axis.PositiveY + if (_generalHelper.compareVectors(root.targetNode.sceneRotation, + root.quaternionForAxis(OriginGizmo.Axis.PositiveY))) { + axis = OriginGizmo.Axis.NegativeY + } + root.axisClicked(axis) + } + } + + LineRectangle { + endPoint: Qt.vector2d(positiveY.x + ballBackground.subBallHalfWidth, positiveY.y + ballBackground.subBallHalfWidth) + color: stylePalette.yAxis + z: positiveY.z - 0.001 + } + + SubBall { + id: positiveZ + targetNode: root.targetNode + width: ballBackground.subBallWidth + offset: ballBackground.subBallOffset + labelText: qsTr("Z") + labelColor: hovered ? stylePalette.brightBall : stylePalette.dimBall + color: stylePalette.zAxis + initialPosition: Qt.vector3d(0, 0, 1) + onTapped: { + let axis = OriginGizmo.Axis.PositiveZ + if (_generalHelper.compareVectors(root.targetNode.sceneRotation, + root.quaternionForAxis(OriginGizmo.Axis.PositiveZ))) { + axis = OriginGizmo.Axis.NegativeZ + } + root.axisClicked(axis) + } + } + + LineRectangle { + endPoint: Qt.vector2d(positiveZ.x + ballBackground.subBallHalfWidth, positiveZ.y + ballBackground.subBallHalfWidth) + color: stylePalette.zAxis + z: positiveZ.z - 0.001 + } + + SubBall { + targetNode: root.targetNode + width: ballBackground.subBallWidth + offset: ballBackground.subBallOffset + labelText: qsTr("-X") + labelColor: stylePalette.brightBall + labelVisible: hovered + color: Qt.rgba(stylePalette.xAxis.r, stylePalette.xAxis.g, stylePalette.xAxis.b, z + 1 * 0.5) + border.color: stylePalette.xAxis + border.width: 2 + initialPosition: Qt.vector3d(-1, 0, 0) + onTapped: { + let axis = OriginGizmo.Axis.NegativeX + if (_generalHelper.compareVectors(root.targetNode.sceneRotation, + root.quaternionForAxis(OriginGizmo.Axis.NegativeX))) { + axis = OriginGizmo.Axis.PositiveX + } + root.axisClicked(axis) + } + } + + SubBall { + targetNode: root.targetNode + width: ballBackground.subBallWidth + offset: ballBackground.subBallOffset + labelText: qsTr("-Y") + labelColor: stylePalette.brightBall + labelVisible: hovered + color: Qt.rgba(stylePalette.yAxis.r, stylePalette.yAxis.g, stylePalette.yAxis.b, z + 1 * 0.5) + border.color: stylePalette.yAxis + border.width: 2 + initialPosition: Qt.vector3d(0, -1, 0) + onTapped: { + let axis = OriginGizmo.Axis.NegativeY + if (_generalHelper.compareVectors(root.targetNode.sceneRotation, + root.quaternionForAxis(OriginGizmo.Axis.NegativeY))) { + axis = OriginGizmo.Axis.PositiveY + } + root.axisClicked(axis) + } + } + + SubBall { + targetNode: root.targetNode + width: ballBackground.subBallWidth + offset: ballBackground.subBallOffset + labelText: qsTr("-Z") + labelColor: stylePalette.brightBall + labelVisible: hovered + color: Qt.rgba(stylePalette.zAxis.r, stylePalette.zAxis.g, stylePalette.zAxis.b, z + 1 * 0.5) + border.color: stylePalette.zAxis + border.width: 2 + initialPosition: Qt.vector3d(0, 0, -1) + onTapped: { + let axis = OriginGizmo.Axis.NegativeZ + if (_generalHelper.compareVectors(root.targetNode.sceneRotation, + root.quaternionForAxis(OriginGizmo.Axis.NegativeZ))) { + axis = OriginGizmo.Axis.PositiveZ + } + root.axisClicked(axis) + } + } + } + + HoverHandler { + id: ballBackgroundHoverHandler + acceptedDevices: PointerDevice.Mouse + cursorShape: Qt.PointingHandCursor + } + } +} diff --git a/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp b/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp index f1b4b795e47..075c32556cf 100644 --- a/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp +++ b/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp @@ -1059,6 +1059,14 @@ void GeneralHelper::setBgColor(const QVariant &colors) } } +QVector3D GeneralHelper::dirForRotation(const QQuaternion &rotation) +{ + QMatrix4x4 m; + m.rotate(rotation); + const float *dataPtr(m.data()); + return QVector3D(dataPtr[8], dataPtr[9], dataPtr[10]).normalized(); +} + void GeneralHelper::handlePendingToolStateUpdate() { m_toolStateUpdateTimer.stop(); @@ -1217,6 +1225,11 @@ bool GeneralHelper::getBounds(QQuick3DViewport *view3D, QQuick3DNode *node, QVec return hasModel; } +bool GeneralHelper::compareVectors(const QVector3D &v1, const QVector3D &v2) +{ + return qFuzzyCompare(v1[0], v2[0]) && qFuzzyCompare(v1[1], v2[1]) && qFuzzyCompare(v1[2], v2[2]); +} + } } diff --git a/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.h b/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.h index 9e54250201a..0818e7059a0 100644 --- a/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.h +++ b/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.h @@ -135,6 +135,9 @@ public: void setBgColor(const QVariant &colors); QVariant bgColor() const { return m_bgColor; } + Q_INVOKABLE QVector3D dirForRotation(const QQuaternion &rotation); + Q_INVOKABLE bool compareVectors(const QVector3D &v1, const QVector3D &v2); + signals: void overlayUpdateNeeded(); void toolStateChanged(const QString &sceneId, const QString &tool, const QVariant &toolState); From 8d4e103eac010c1dc6baad789ed96df0976cce97 Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Fri, 6 Oct 2023 13:15:07 +0300 Subject: [PATCH 014/242] QmlDesigner: Add columns to a collection Task-number: QDS-10619 Change-Id: Ifa38b2ec8210e7ca7260d3307d3906fd609f3c62 Reviewed-by: Mahmoud Badri Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Miikka Heikkinen --- .../CollectionDetailsToolbar.qml | 161 ++++++++++++++++++ .../SingleCollectionView.qml | 14 +- .../collectioneditor/collectiondetails.cpp | 8 + .../collectioneditor/collectiondetails.h | 1 + .../singlecollectionmodel.cpp | 19 +++ .../collectioneditor/singlecollectionmodel.h | 2 + 6 files changed, 204 insertions(+), 1 deletion(-) create mode 100644 share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsToolbar.qml diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsToolbar.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsToolbar.qml new file mode 100644 index 00000000000..a120b5c1503 --- /dev/null +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsToolbar.qml @@ -0,0 +1,161 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import QtQuick +import HelperWidgets 2.0 as HelperWidgets +import StudioControls 1.0 as StudioControls +import StudioTheme 1.0 as StudioTheme + +Item { + id: root + + property real iconHeight: 20 + required property var model + property int selectedRow: -1 + + implicitHeight: 30 + implicitWidth: leftSideToolbar.width + rightSideToolbar.width + + signal addPropertyRight() + + Row { + id: leftSideToolbar + + topPadding: (root.height - root.iconHeight) / 2 + leftPadding: 10 + spacing: 5 + + anchors { + left: parent.left + top: parent.top + bottom: parent.bottom + } + + IconButton { + icon: StudioTheme.Constants.addcolumnleft_medium + tooltip: qsTr("Add property left %1").arg(leftSideToolbar.topPadding) + enabled: root.model.selectedColumn > 0 + onClicked: addColumnDialog.popUp(root.model.selectedColumn - 1) + } + + IconButton { + icon: StudioTheme.Constants.addcolumnright_medium + tooltip: qsTr("Add property right") + enabled: root.model.selectedColumn > -1 + onClicked: addColumnDialog.popUp(root.model.selectedColumn + 1) + } + } + + Row { + id: rightSideToolbar + spacing: 5 + topPadding: (root.height - root.iconHeight) / 2 + rightPadding: 10 + + anchors { + right: parent.right + top: parent.top + bottom: parent.bottom + } + } + + Rectangle { + anchors.fill: parent + z: -1 + color: "grey" + opacity: 0.6 + radius: 5 + } + + component IconButton: HelperWidgets.IconButton { + height: root.iconHeight + width: root.iconHeight + } + + HelperWidgets.RegExpValidator { + id: nameValidator + regExp: /^\w+$/ + } + + StudioControls.Dialog { + id: addColumnDialog + + property int clickedIndex: -1 + property bool nameIsValid + + title: qsTr("Add Column") + width: 400 + + function popUp(index) + { + addColumnDialog.clickedIndex = index + addColumnDialog.open() + } + + function addColumnName() { + if (addColumnDialog.nameIsValid) { + root.model.addColumn(addColumnDialog.clickedIndex, columnName.text) + addColumnDialog.accept() + } else { + addColumnDialog.reject() + } + } + + contentItem: Column { + spacing: 2 + + Row { + spacing: 10 + + Text { + text: qsTr("Column name:") + color: StudioTheme.Values.themeTextColor + } + + StudioControls.TextField { + id: columnName + + actionIndicator.visible: false + translationIndicator.visible: false + validator: nameValidator + + Keys.onEnterPressed: addColumnDialog.addColumnName() + Keys.onReturnPressed: addColumnDialog.addColumnName() + Keys.onEscapePressed: addColumnDialog.reject() + + onTextChanged: { + addColumnDialog.nameIsValid = (columnName.text !== "" + && !root.model.isPropertyAvailable(columnName.text)) + } + } + } + + Text { + text: qsTr("The collection already contains \"%1\"!").arg(columnName.text) + visible: columnName.text !== "" && !addColumnDialog.nameIsValid + color: "red" + } + + Item { // spacer + width: 1 + height: 20 + } + + Row { + anchors.right: parent.right + spacing: 10 + + HelperWidgets.Button { + enabled: addColumnDialog.nameIsValid + text: qsTr("Add") + onClicked: addColumnDialog.addColumnName() + } + + HelperWidgets.Button { + text: qsTr("Cancel") + onClicked: addColumnDialog.reject() + } + } + } + } +} diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/SingleCollectionView.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/SingleCollectionView.qml index 12706e2cc75..134a5eeefd4 100644 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/SingleCollectionView.qml +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/SingleCollectionView.qml @@ -23,7 +23,7 @@ Rectangle { spacing: 0 width: parent.width leftPadding: 20 - rightPadding: 0 + rightPadding: 20 topPadding: 5 Text { @@ -51,6 +51,18 @@ Rectangle { height: 10 } + CollectionDetailsToolbar { + property real availableWidth: parent.width - parent.leftPadding - parent.rightPadding + + model: root.model + width: Math.max(availableWidth, implicitWidth) + } + + Item { // spacer + width: 1 + height: 5 + } + HorizontalHeaderView { id: headerView diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp index a83a0168407..350e9326bf8 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp @@ -183,6 +183,14 @@ QString CollectionDetails::headerAt(int column) const return d->headers.at(column); } +bool CollectionDetails::containsHeader(const QString &header) +{ + if (!isValid()) + return false; + + return d->headers.contains(header); +} + bool CollectionDetails::isValid() const { return d->reference.node.isValid() && d->reference.name.size(); diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.h b/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.h index 2dbfeeb13e2..61c9f963192 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.h +++ b/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.h @@ -55,6 +55,7 @@ public: CollectionEditor::SourceFormat sourceFormat() const; QVariant data(int row, int column) const; QString headerAt(int column) const; + bool containsHeader(const QString &header); bool isValid() const; bool isChanged() const; diff --git a/src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.cpp b/src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.cpp index 669044486a2..f7679e8917b 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.cpp @@ -125,6 +125,25 @@ int SingleCollectionModel::selectedColumn() const return m_selectedColumn; } +bool SingleCollectionModel::isPropertyAvailable(const QString &name) +{ + return m_currentCollection.containsHeader(name); +} + +bool SingleCollectionModel::addColumn(int column, const QString &name) +{ + if (m_currentCollection.containsHeader(name)) + return false; + + if (column < 0 || column > columnCount()) + column = columnCount(); + + beginInsertColumns({}, column, column); + m_currentCollection.insertColumn(name, column); + endInsertColumns(); + return m_currentCollection.containsHeader(name); +} + bool SingleCollectionModel::selectColumn(int section) { if (m_selectedColumn == section) diff --git a/src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.h b/src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.h index 9e89a948d2a..9d877502bb4 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.h +++ b/src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.h @@ -41,6 +41,8 @@ public: int role = Qt::DisplayRole) const override; int selectedColumn() const; + Q_INVOKABLE bool isPropertyAvailable(const QString &name); + Q_INVOKABLE bool addColumn(int column, const QString &name); Q_INVOKABLE bool selectColumn(int section); Q_INVOKABLE bool renameColumn(int section, const QString &newValue); From 82be17c2c66e5ae6c2432489c81e4a02875bdaf6 Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Fri, 6 Oct 2023 18:50:32 +0300 Subject: [PATCH 015/242] QmlDesigner: Add row id to the collection table Task-number: QDS-10621 Change-Id: Ib41f7b9a8ce8d19c24b7580887f7c44a7f87fbdc Reviewed-by: Miikka Heikkinen Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Mahmoud Badri --- .../SingleCollectionView.qml | 381 ++++++++++-------- .../singlecollectionmodel.cpp | 16 +- .../collectioneditor/singlecollectionmodel.h | 6 + 3 files changed, 236 insertions(+), 167 deletions(-) diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/SingleCollectionView.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/SingleCollectionView.qml index 134a5eeefd4..202e050cfc9 100644 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/SingleCollectionView.qml +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/SingleCollectionView.qml @@ -3,6 +3,7 @@ import QtQuick import QtQuick.Controls +import QtQuick.Layouts import HelperWidgets 2.0 as HelperWidgets import StudioTheme 1.0 as StudioTheme import StudioControls 1.0 as StudioControls @@ -12,19 +13,19 @@ Rectangle { required property var model - property alias leftPadding: topRow.leftPadding - property real rightPadding: topRow.rightPadding - color: StudioTheme.Values.themeBackgroundColorAlternate - Column { + ColumnLayout { id: topRow spacing: 0 - width: parent.width - leftPadding: 20 - rightPadding: 20 - topPadding: 5 + anchors { + fill: parent + topMargin: 10 + leftMargin: 15 + rightMargin: 15 + bottomMargin: 10 + } Text { id: collectionNameText @@ -52,10 +53,8 @@ Rectangle { } CollectionDetailsToolbar { - property real availableWidth: parent.width - parent.leftPadding - parent.rightPadding - model: root.model - width: Math.max(availableWidth, implicitWidth) + Layout.fillWidth: true } Item { // spacer @@ -63,186 +62,236 @@ Rectangle { height: 5 } - HorizontalHeaderView { - id: headerView + GridLayout { + columns: 2 + rowSpacing: 1 + columnSpacing: 1 - property real topPadding: 5 - property real bottomPadding: 5 + Layout.fillWidth: true + Layout.fillHeight: true - height: headerMetrics.height + topPadding + bottomPadding - - syncView: tableView - clip: true - - delegate: Rectangle { - id: headerItem - implicitWidth: 100 - implicitHeight: headerText.height - border.width: 2 - border.color: StudioTheme.Values.themeControlOutline + Rectangle { clip: true + visible: root.model.isEmpty === false + color: StudioTheme.Values.themeControlBackground + border.color: StudioTheme.Values.themeControlOutline + border.width: 2 + + Layout.preferredWidth: rowIdView.width + Layout.preferredHeight: headerView.height Text { - id: headerText - - topPadding: headerView.topPadding - bottomPadding: headerView.bottomPadding - leftPadding: 5 - rightPadding: 5 - text: display - font: headerMetrics.font + anchors.fill: parent + font: headerTextMetrics.font + text: "#" horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter - anchors.centerIn: parent - elide: Text.ElideRight + color: StudioTheme.Values.themeTextColor } - - MouseArea { - anchors.fill: parent - acceptedButtons: Qt.LeftButton | Qt.RightButton - onClicked: (mouse) => { - root.model.selectColumn(index) - - if (mouse.button === Qt.RightButton) - headerMenu.popIndex(index) - - mouse.accepted = true - } - } - - states: [ - State { - name: "default" - when: index !== root.model.selectedColumn - PropertyChanges { - target: headerItem - color: StudioTheme.Values.themeControlBackground - } - - PropertyChanges { - target: headerText - color: StudioTheme.Values.themeIdleGreen - } - }, - State { - name: "selected" - when: index === root.model.selectedColumn - - PropertyChanges { - target: headerItem - color: StudioTheme.Values.themeControlBackgroundInteraction - } - - PropertyChanges { - target: headerText - color: StudioTheme.Values.themeRunningGreen - } - } - ] } - TextMetrics { - id: headerMetrics + HorizontalHeaderView { + id: headerView - font.pixelSize: StudioTheme.Values.baseFontSize - text: "Xq" + property real topPadding: 5 + property real bottomPadding: 5 + + Layout.preferredHeight: headerTextMetrics.height + topPadding + bottomPadding + Layout.fillWidth: true + syncView: tableView + clip: true + + delegate: Rectangle { + id: headerItem + implicitWidth: 100 + implicitHeight: headerText.height + border.width: 2 + border.color: StudioTheme.Values.themeControlOutline + clip: true + + Text { + id: headerText + + topPadding: headerView.topPadding + bottomPadding: headerView.bottomPadding + leftPadding: 5 + rightPadding: 5 + text: display + font: headerTextMetrics.font + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + anchors.centerIn: parent + elide: Text.ElideRight + } + + MouseArea { + anchors.fill: parent + acceptedButtons: Qt.LeftButton | Qt.RightButton + onClicked: (mouse) => { + root.model.selectColumn(index) + + if (mouse.button === Qt.RightButton) + headerMenu.popIndex(index) + + mouse.accepted = true + } + } + + states: [ + State { + name: "default" + when: index !== root.model.selectedColumn + PropertyChanges { + target: headerItem + color: StudioTheme.Values.themeControlBackground + } + + PropertyChanges { + target: headerText + color: StudioTheme.Values.themeIdleGreen + } + }, + State { + name: "selected" + when: index === root.model.selectedColumn + + PropertyChanges { + target: headerItem + color: StudioTheme.Values.themeControlBackgroundInteraction + } + + PropertyChanges { + target: headerText + color: StudioTheme.Values.themeRunningGreen + } + } + ] + } + + StudioControls.Menu { + id: headerMenu + + property int clickedHeader: -1 + + function popIndex(clickedIndex) + { + headerMenu.clickedHeader = clickedIndex + headerMenu.popup() + } + + onClosed: { + headerMenu.clickedHeader = -1 + } + + StudioControls.MenuItem { + text: qsTr("Edit") + onTriggered: editProperyDialog.editProperty(headerMenu.clickedHeader) + } + + StudioControls.MenuItem { + text: qsTr("Delete") + onTriggered: deleteColumnDialog.popUp(headerMenu.clickedHeader) + } + } } - StudioControls.Menu { - id: headerMenu + VerticalHeaderView { + id: rowIdView - property int clickedHeader: -1 + syncView: tableView + clip: true - function popIndex(clickedIndex) - { - headerMenu.clickedHeader = clickedIndex - headerMenu.popup() + Layout.fillHeight: true + + delegate: Rectangle { + color: StudioTheme.Values.themeControlBackground + border.color: StudioTheme.Values.themeControlOutline + border.width: 2 + implicitWidth: idText.width + + Text { + id: idText + text: display + leftPadding: 5 + rightPadding: 5 + topPadding: 5 + color: StudioTheme.Values.themeTextColor + font: headerTextMetrics.font + } } + } - onClosed: { - headerMenu.clickedHeader = -1 - } + TableView { + id: tableView - StudioControls.MenuItem { - text: qsTr("Edit") - onTriggered: editProperyDialog.editProperty(headerMenu.clickedHeader) - } + model: root.model + clip: true - StudioControls.MenuItem { - text: qsTr("Delete") - onTriggered: deleteColumnDialog.popUp(headerMenu.clickedHeader) + Layout.fillWidth: true + Layout.fillHeight: true + + delegate: Rectangle { + id: itemCell + implicitWidth: 100 + implicitHeight: itemText.height + border.width: 1 + border.color: StudioTheme.Values.themeControlOutline + + Text { + id: itemText + + text: display ? display : "" + + width: parent.width + leftPadding: 5 + topPadding: 3 + bottomPadding: 3 + font.pixelSize: StudioTheme.Values.baseFontSize + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + elide: Text.ElideRight + } + + states: [ + State { + name: "default" + when: !itemSelected + + PropertyChanges { + target: itemCell + color: StudioTheme.Values.themeControlBackground + } + + PropertyChanges { + target: itemText + color: StudioTheme.Values.themeTextColor + } + }, + State { + name: "selected" + when: itemSelected + + PropertyChanges { + target: itemCell + color: StudioTheme.Values.themeControlBackgroundInteraction + } + + PropertyChanges { + target: itemText + color: StudioTheme.Values.themeInteraction + } + } + ] } } } } - TableView { - id: tableView + TextMetrics { + id: headerTextMetrics - anchors { - top: topRow.bottom - left: parent.left - right: parent.right - bottom: parent.bottom - leftMargin: root.leftPadding - rightMargin: root.rightPadding - } - - model: root.model - - delegate: Rectangle { - id: itemCell - implicitWidth: 100 - implicitHeight: itemText.height - border.width: 1 - border.color: StudioTheme.Values.themeControlOutline - - Text { - id: itemText - - text: display ? display : "" - - width: parent.width - leftPadding: 5 - topPadding: 3 - bottomPadding: 3 - font.pixelSize: StudioTheme.Values.baseFontSize - horizontalAlignment: Text.AlignLeft - verticalAlignment: Text.AlignVCenter - elide: Text.ElideRight - } - - states: [ - State { - name: "default" - when: !itemSelected - - PropertyChanges { - target: itemCell - color: StudioTheme.Values.themeControlBackground - } - - PropertyChanges { - target: itemText - color: StudioTheme.Values.themeTextColor - } - }, - State { - name: "selected" - when: itemSelected - - PropertyChanges { - target: itemCell - color: StudioTheme.Values.themeControlBackgroundInteraction - } - - PropertyChanges { - target: itemText - color: StudioTheme.Values.themeInteraction - } - } - ] - } + font.pixelSize: StudioTheme.Values.baseFontSize + text: "Xq" } EditPropertyDialog { diff --git a/src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.cpp b/src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.cpp index f7679e8917b..6fa2ac426db 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.cpp @@ -36,7 +36,9 @@ namespace QmlDesigner { SingleCollectionModel::SingleCollectionModel(QObject *parent) : QAbstractTableModel(parent) -{} +{ + connect(this, &SingleCollectionModel::modelReset, this, &SingleCollectionModel::updateEmpty); +} QHash SingleCollectionModel::roleNames() const { @@ -117,6 +119,9 @@ QVariant SingleCollectionModel::headerData(int section, if (orientation == Qt::Horizontal) return m_currentCollection.headerAt(section); + if (orientation == Qt::Vertical) + return section + 1; + return {}; } @@ -203,6 +208,15 @@ void SingleCollectionModel::loadCollection(const ModelNode &sourceNode, const QS } } +void SingleCollectionModel::updateEmpty() +{ + bool isEmptyNow = rowCount() == 0; + if (m_isEmpty != isEmptyNow) { + m_isEmpty = isEmptyNow; + emit isEmptyChanged(m_isEmpty); + } +} + void SingleCollectionModel::switchToCollection(const CollectionReference &collection) { if (m_currentCollection.reference() == collection) diff --git a/src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.h b/src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.h index 9d877502bb4..95154b5c55d 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.h +++ b/src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.h @@ -18,6 +18,7 @@ class SingleCollectionModel : public QAbstractTableModel Q_PROPERTY(QString collectionName MEMBER m_collectionName NOTIFY collectionNameChanged) Q_PROPERTY(int selectedColumn READ selectedColumn WRITE selectColumn NOTIFY selectedColumnChanged) + Q_PROPERTY(bool isEmpty MEMBER m_isEmpty NOTIFY isEmptyChanged) public: enum DataRoles { SelectedRole = Qt::UserRole + 1 }; @@ -51,6 +52,10 @@ public: signals: void collectionNameChanged(const QString &collectionName); void selectedColumnChanged(int); + void isEmptyChanged(bool); + +private slots: + void updateEmpty(); private: void switchToCollection(const CollectionReference &collection); @@ -63,6 +68,7 @@ private: QHash m_openedCollections; CollectionDetails m_currentCollection; + bool m_isEmpty = true; int m_selectedColumn = -1; QString m_collectionName; From 93ba99f019cbe4e4eba1858a501b38060a651ea3 Mon Sep 17 00:00:00 2001 From: Amr Essam Date: Mon, 9 Oct 2023 13:46:08 +0300 Subject: [PATCH 016/242] QmlDesigner: Prepare the process to compile shaders externally Also added customValue for Uniform Task-number: QDS-10811 Change-Id: Ie47ad41d0c80da149bdab9cae542297d59abcee6 Reviewed-by: Miikka Heikkinen --- .../EffectMakerPreview.qml | 51 +++++++++---------- src/plugins/effectmakernew/CMakeLists.txt | 2 +- .../effectmakernew/effectmakermodel.cpp | 30 ++++++++++- src/plugins/effectmakernew/effectmakermodel.h | 18 +++++-- .../effectmakernew/effectmakerwidget.cpp | 2 + src/plugins/effectmakernew/uniform.cpp | 3 ++ 6 files changed, 73 insertions(+), 33 deletions(-) diff --git a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMakerPreview.qml b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMakerPreview.qml index c23e95e5628..c6ffe83a168 100644 --- a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMakerPreview.qml +++ b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMakerPreview.qml @@ -20,6 +20,31 @@ Column { // The delay in ms to wait until updating the effect readonly property int updateDelay: 200 + // Create a dummy parent to host the effect qml object + function createNewComponent() { + var oldComponent = componentParent.children[0]; + if (oldComponent) + oldComponent.destroy(); + try { + const newObject = Qt.createQmlObject( + effectMakerModel.qmlComponentString, + componentParent, + "" + ); + effectMakerModel.resetEffectError(0); + } catch (error) { + let errorString = "QML: ERROR: "; + let errorLine = -1; + if (error.qmlErrors.length > 0) { + // Show the first QML error + let e = error.qmlErrors[0]; + errorString += e.lineNumber + ": " + e.message; + errorLine = e.lineNumber; + } + effectMakerModel.setEffectError(errorString, 0, errorLine); + } + } + Rectangle { // toolbar width: parent.width height: StudioTheme.Values.toolbarHeight @@ -154,32 +179,6 @@ Column { layer.smooth: true } - // Create a dummy parent to host the effect qml object - function createNewComponent() { - var oldComponent = componentParent.children[0]; - if (oldComponent) - oldComponent.destroy(); - - try { - const newObject = Qt.createQmlObject( - effectMakerModel.qmlComponentString, - componentParent, - "" - ); - effectMakerModel.resetEffectError(0); - } catch (error) { - let errorString = "QML: ERROR: "; - let errorLine = -1; - if (error.qmlErrors.length > 0) { - // Show the first QML error - let e = error.qmlErrors[0]; - errorString += e.lineNumber + ": " + e.message; - errorLine = e.lineNumber; - } - effectMakerModel.setEffectError(errorString, 0, errorLine); - } - } - Connections { target: effectMakerModel function onShadersBaked() { diff --git a/src/plugins/effectmakernew/CMakeLists.txt b/src/plugins/effectmakernew/CMakeLists.txt index b8842687e5e..d01c97618c7 100644 --- a/src/plugins/effectmakernew/CMakeLists.txt +++ b/src/plugins/effectmakernew/CMakeLists.txt @@ -3,7 +3,7 @@ find_package(Qt6 OPTIONAL_COMPONENTS Gui Quick ShaderTools) add_qtc_plugin(EffectMakerNew CONDITION TARGET QmlDesigner AND TARGET Qt::ShaderTools PLUGIN_DEPENDS - QtCreator::Core QtCreator::QmlDesigner + QtCreator::Core QtCreator::QmlDesigner QtCreator::ProjectExplorer DEPENDS Qt::Core QtCreator::Utils Qt::CorePrivate Qt::Widgets Qt::Qml Qt::QmlPrivate Qt::Quick Qt::ShaderTools Qt::ShaderToolsPrivate diff --git a/src/plugins/effectmakernew/effectmakermodel.cpp b/src/plugins/effectmakernew/effectmakermodel.cpp index 9da7d97ef60..364a012fe30 100644 --- a/src/plugins/effectmakernew/effectmakermodel.cpp +++ b/src/plugins/effectmakernew/effectmakermodel.cpp @@ -10,6 +10,12 @@ #include #include +#include +#include +#include + +#include + #include namespace EffectMaker { @@ -758,7 +764,7 @@ void EffectMakerModel::updateCustomUniforms() // Custom values are not readonly, others inside the effect can be QString readOnly = uniform->useCustomValue() ? QString() : QStringLiteral("readonly "); previewEffectPropertiesString += " " + readOnly + "property " + type + " " - + propertyName + bindedValueString + '\n'; + + propertyName + valueString + '\n'; // Define type properties are not added into exports if (!isDefine) { if (uniform->useCustomValue()) { @@ -783,7 +789,9 @@ void EffectMakerModel::updateCustomUniforms() } // See if any of the properties changed - // TODO + m_exportedRootPropertiesString = exportedRootPropertiesString; + m_previewEffectPropertiesString = previewEffectPropertiesString; + m_exportedEffectPropertiesString = exportedEffectPropertiesString; } void EffectMakerModel::bakeShaders() @@ -807,6 +815,7 @@ void EffectMakerModel::bakeShaders() QString vs = m_vertexShader; m_baker.setSourceString(vs.toUtf8(), QShader::VertexStage); QShader vertShader = m_baker.bake(); + if (!vertShader.isValid()) { qWarning() << "Shader baking failed:" << qPrintable(m_baker.errorMessage()); setEffectError(m_baker.errorMessage().split('\n').first(), ErrorVert); @@ -821,6 +830,7 @@ void EffectMakerModel::bakeShaders() m_baker.setSourceString(fs.toUtf8(), QShader::FragmentStage); QShader fragShader = m_baker.bake(); + if (!fragShader.isValid()) { qWarning() << "Shader baking failed:" << qPrintable(m_baker.errorMessage()); setEffectError(m_baker.errorMessage().split('\n').first(), ErrorFrag); @@ -908,6 +918,7 @@ QString EffectMakerModel::getQmlComponentString(bool localFiles) // When used in preview component, we need property with value // and when in exported component, property with binding to root value. s += localFiles ? m_exportedEffectPropertiesString : m_previewEffectPropertiesString; + if (!customImagesString.isEmpty()) s += '\n' + customImagesString; @@ -925,6 +936,21 @@ QString EffectMakerModel::getQmlComponentString(bool localFiles) return s; } +Utils::FilePath EffectMakerModel::qsbPath() const +{ + const ProjectExplorer::Target *target = ProjectExplorer::ProjectTree::currentTarget(); + if (target) { + if (QtSupport::QtVersion *qtVer = QtSupport::QtKitAspect::qtVersion(target->kit())) { + Utils::FilePath path = qtVer->binPath().pathAppended("qsb").withExecutableSuffix(); + if (path.exists()) + return path; + } + } + + qWarning() << "Shader baking failed, QSB not found."; + return {}; +} + void EffectMakerModel::updateQmlComponent() { // Clear possible QML runtime errors diff --git a/src/plugins/effectmakernew/effectmakermodel.h b/src/plugins/effectmakernew/effectmakermodel.h index 395a9063c53..ad315a9c8d1 100644 --- a/src/plugins/effectmakernew/effectmakermodel.h +++ b/src/plugins/effectmakernew/effectmakermodel.h @@ -5,6 +5,8 @@ #include "shaderfeatures.h" +#include + #include #include #include @@ -12,6 +14,10 @@ #include +namespace Utils { +class Process; +} + namespace EffectMaker { class CompositionNode; @@ -60,14 +66,17 @@ public: QString fragmentShader() const; void setFragmentShader(const QString &newFragmentShader); + QString vertexShader() const; void setVertexShader(const QString &newVertexShader); const QString &qmlComponentString() const; - void setQmlComponentString(const QString &string); Q_INVOKABLE void updateQmlComponent(); + Q_INVOKABLE void resetEffectError(int type); + Q_INVOKABLE void setEffectError(const QString &errorMessage, int type = -1, int lineNumber = -1); + signals: void isEmptyChanged(); void selectedIndexChanged(int idx); @@ -102,8 +111,6 @@ private: void updateBakedShaderVersions(); QString detectErrorMessage(const QString &errorMessage); EffectError effectError() const; - void setEffectError(const QString &errorMessage, int type = -1, int lineNumber = -1); - void resetEffectError(int type); QString valueAsString(const Uniform &uniform); QString valueAsBinding(const Uniform &uniform); @@ -121,6 +128,9 @@ private: QString getCustomShaderVaryings(bool outState); QString generateVertexShader(bool includeUniforms = true); QString generateFragmentShader(bool includeUniforms = true); + + Utils::FilePath qsbPath() const; + void updateCustomUniforms(); void bakeShaders(); @@ -140,7 +150,7 @@ private: QString m_vertexShader; QStringList m_defaultRootVertexShader; QStringList m_defaultRootFragmentShader; - QShaderBaker m_baker; + QShaderBaker m_baker; // TODO: Remove QTemporaryFile m_fragmentShaderFile; QTemporaryFile m_vertexShaderFile; // Used in exported QML, at root of the file diff --git a/src/plugins/effectmakernew/effectmakerwidget.cpp b/src/plugins/effectmakernew/effectmakerwidget.cpp index 72d4c1b2cd6..d0114584932 100644 --- a/src/plugins/effectmakernew/effectmakerwidget.cpp +++ b/src/plugins/effectmakernew/effectmakerwidget.cpp @@ -7,6 +7,8 @@ #include "effectmakermodel.h" #include "effectmakernodesmodel.h" #include "effectmakerview.h" + +#include "nodeinstanceview.h" #include "qmldesignerconstants.h" #include "qmldesignerplugin.h" #include "qqmlcontext.h" diff --git a/src/plugins/effectmakernew/uniform.cpp b/src/plugins/effectmakernew/uniform.cpp index 5cad43e0c95..4901b6bbb6e 100644 --- a/src/plugins/effectmakernew/uniform.cpp +++ b/src/plugins/effectmakernew/uniform.cpp @@ -40,6 +40,9 @@ Uniform::Uniform(const QJsonObject &propObj) // QEN files don't store the current value, so with those use default value value = defaultValue; } + m_customValue = propObj.value("customValue").toString(); + m_useCustomValue = getBoolValue(propObj.value("useCustomValue"), false); + minValue = propObj.value("minValue").toString(); maxValue = propObj.value("maxValue").toString(); From e3159b3ec428fe82a0dd930383f2d892382ab842 Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Mon, 9 Oct 2023 10:51:01 +0300 Subject: [PATCH 017/242] QmlDesigner: Make collection row selectable Task-number: QDS-10920 Change-Id: I8d3f34b62b03b0890183c325e89818d578fe449e Reviewed-by: Miikka Heikkinen Reviewed-by: Mahmoud Badri Reviewed-by: Qt CI Patch Build Bot --- .../SingleCollectionView.qml | 137 +++++++++--------- .../singlecollectionmodel.cpp | 48 +++++- .../collectioneditor/singlecollectionmodel.h | 8 + 3 files changed, 123 insertions(+), 70 deletions(-) diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/SingleCollectionView.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/SingleCollectionView.qml index 202e050cfc9..c95f1712778 100644 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/SingleCollectionView.qml +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/SingleCollectionView.qml @@ -101,71 +101,20 @@ Rectangle { syncView: tableView clip: true - delegate: Rectangle { - id: headerItem - implicitWidth: 100 - implicitHeight: headerText.height - border.width: 2 - border.color: StudioTheme.Values.themeControlOutline - clip: true - - Text { - id: headerText - - topPadding: headerView.topPadding - bottomPadding: headerView.bottomPadding - leftPadding: 5 - rightPadding: 5 - text: display - font: headerTextMetrics.font - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter - anchors.centerIn: parent - elide: Text.ElideRight - } + delegate: HeaderDelegate { + selectedItem: root.model.selectedColumn MouseArea { anchors.fill: parent + anchors.margins: 5 acceptedButtons: Qt.LeftButton | Qt.RightButton onClicked: (mouse) => { root.model.selectColumn(index) if (mouse.button === Qt.RightButton) headerMenu.popIndex(index) - - mouse.accepted = true } } - - states: [ - State { - name: "default" - when: index !== root.model.selectedColumn - PropertyChanges { - target: headerItem - color: StudioTheme.Values.themeControlBackground - } - - PropertyChanges { - target: headerText - color: StudioTheme.Values.themeIdleGreen - } - }, - State { - name: "selected" - when: index === root.model.selectedColumn - - PropertyChanges { - target: headerItem - color: StudioTheme.Values.themeControlBackgroundInteraction - } - - PropertyChanges { - target: headerText - color: StudioTheme.Values.themeRunningGreen - } - } - ] } StudioControls.Menu { @@ -203,21 +152,16 @@ Rectangle { Layout.fillHeight: true - delegate: Rectangle { - color: StudioTheme.Values.themeControlBackground - border.color: StudioTheme.Values.themeControlOutline - border.width: 2 - implicitWidth: idText.width + delegate: HeaderDelegate { + selectedItem: root.model.selectedRow - Text { - id: idText - text: display - leftPadding: 5 - rightPadding: 5 - topPadding: 5 - color: StudioTheme.Values.themeTextColor - font: headerTextMetrics.font + MouseArea { + anchors.fill: parent + anchors.margins: 5 + acceptedButtons: Qt.LeftButton + onClicked: root.model.selectRow(index) } + } } @@ -294,6 +238,65 @@ Rectangle { text: "Xq" } + component HeaderDelegate: Rectangle { + id: headerItem + + required property int selectedItem + property alias horizontalAlignment: headerText.horizontalAlignment + property alias verticalAlignment: headerText.verticalAlignment + + implicitWidth: headerText.width + implicitHeight: headerText.height + border.width: 2 + border.color: StudioTheme.Values.themeControlOutline + clip: true + + Text { + id: headerText + + topPadding: headerView.topPadding + bottomPadding: headerView.bottomPadding + leftPadding: 5 + rightPadding: 5 + text: display + font: headerTextMetrics.font + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + anchors.centerIn: parent + elide: Text.ElideRight + } + + states: [ + State { + name: "default" + when: index !== selectedItem + PropertyChanges { + target: headerItem + color: StudioTheme.Values.themeControlBackground + } + + PropertyChanges { + target: headerText + color: StudioTheme.Values.themeIdleGreen + } + }, + State { + name: "selected" + when: index === selectedItem + + PropertyChanges { + target: headerItem + color: StudioTheme.Values.themeControlBackgroundInteraction + } + + PropertyChanges { + target: headerText + color: StudioTheme.Values.themeRunningGreen + } + } + ] + } + EditPropertyDialog { id: editProperyDialog model: root.model diff --git a/src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.cpp b/src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.cpp index 6fa2ac426db..00d20d51a3b 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.cpp @@ -66,7 +66,7 @@ QVariant SingleCollectionModel::data(const QModelIndex &index, int role) const return {}; if (role == SelectedRole) - return index.column() == m_selectedColumn; + return (index.column() == m_selectedColumn || index.row() == m_selectedRow); return m_currentCollection.data(index.row(), index.column()); } @@ -130,6 +130,11 @@ int SingleCollectionModel::selectedColumn() const return m_selectedColumn; } +int SingleCollectionModel::selectedRow() const +{ + return m_selectedRow; +} + bool SingleCollectionModel::isPropertyAvailable(const QString &name) { return m_currentCollection.containsHeader(name); @@ -159,6 +164,8 @@ bool SingleCollectionModel::selectColumn(int section) if (m_selectedColumn >= columns) return false; + selectRow(-1); + const int rows = rowCount(); const int previousColumn = m_selectedColumn; @@ -184,6 +191,41 @@ bool SingleCollectionModel::renameColumn(int section, const QString &newValue) return setHeaderData(section, Qt::Horizontal, newValue); } +bool SingleCollectionModel::selectRow(int row) +{ + if (m_selectedRow == row) + return false; + + const int rows = rowCount(); + + if (m_selectedRow >= rows) + return false; + + selectColumn(-1); + + const int columns = columnCount(); + const int previousRow = m_selectedRow; + + m_selectedRow = row; + emit this->selectedRowChanged(m_selectedRow); + + auto notifySelectedDataChanged = [this, rows, columns](int notifyingRow) { + if (notifyingRow > -1 && notifyingRow < rows && columns) + emit dataChanged(index(notifyingRow, 0), index(notifyingRow, columns - 1), {SelectedRole}); + }; + + notifySelectedDataChanged(previousRow); + notifySelectedDataChanged(m_selectedRow); + + return true; +} + +void SingleCollectionModel::deselectAll() +{ + selectColumn(-1); + selectRow(-1); +} + void SingleCollectionModel::loadCollection(const ModelNode &sourceNode, const QString &collection) { QString fileName = sourceNode.variantProperty(CollectionEditor::SOURCEFILE_PROPERTY).value().toString(); @@ -193,13 +235,13 @@ void SingleCollectionModel::loadCollection(const ModelNode &sourceNode, const QS if (alreadyOpen) { if (m_currentCollection.reference() != newReference) { - selectColumn(-1); + deselectAll(); beginResetModel(); switchToCollection(newReference); endResetModel(); } } else { - selectColumn(-1); + deselectAll(); switchToCollection(newReference); if (sourceNode.type() == CollectionEditor::JSONCOLLECTIONMODEL_TYPENAME) loadJsonCollection(fileName, collection); diff --git a/src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.h b/src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.h index 95154b5c55d..0972a169ab5 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.h +++ b/src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.h @@ -18,6 +18,7 @@ class SingleCollectionModel : public QAbstractTableModel Q_PROPERTY(QString collectionName MEMBER m_collectionName NOTIFY collectionNameChanged) Q_PROPERTY(int selectedColumn READ selectedColumn WRITE selectColumn NOTIFY selectedColumnChanged) + Q_PROPERTY(int selectedRow READ selectedRow WRITE selectRow NOTIFY selectedRowChanged) Q_PROPERTY(bool isEmpty MEMBER m_isEmpty NOTIFY isEmptyChanged) public: @@ -41,17 +42,23 @@ public: Qt::Orientation orientation, int role = Qt::DisplayRole) const override; int selectedColumn() const; + int selectedRow() const; Q_INVOKABLE bool isPropertyAvailable(const QString &name); Q_INVOKABLE bool addColumn(int column, const QString &name); Q_INVOKABLE bool selectColumn(int section); Q_INVOKABLE bool renameColumn(int section, const QString &newValue); + Q_INVOKABLE bool selectRow(int row); + + Q_INVOKABLE void deselectAll(); + void loadCollection(const ModelNode &sourceNode, const QString &collection); signals: void collectionNameChanged(const QString &collectionName); void selectedColumnChanged(int); + void selectedRowChanged(int); void isEmptyChanged(bool); private slots: @@ -70,6 +77,7 @@ private: CollectionDetails m_currentCollection; bool m_isEmpty = true; int m_selectedColumn = -1; + int m_selectedRow = -1; QString m_collectionName; }; From c12fce75709bddc084d89e765e3939c618ed1b53 Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Mon, 9 Oct 2023 13:05:06 +0300 Subject: [PATCH 018/242] QmlDesigner: Add rows to a collection Task-number: QDS-10619 Change-Id: I37a2b7ebdc3abd8f72a5fe3e7e5fd82e173cd9ea Reviewed-by: Miikka Heikkinen Reviewed-by: Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Mahmoud Badri --- .../CollectionDetailsToolbar.qml | 19 +++++++++++++++++++ .../collectioneditor/collectiondetails.cpp | 14 ++++++++++++++ .../collectioneditor/collectiondetails.h | 1 + .../singlecollectionmodel.cpp | 14 ++++++++++++++ .../collectioneditor/singlecollectionmodel.h | 1 + 5 files changed, 49 insertions(+) diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsToolbar.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsToolbar.qml index a120b5c1503..25ad17ae85b 100644 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsToolbar.qml +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsToolbar.qml @@ -44,6 +44,25 @@ Item { enabled: root.model.selectedColumn > -1 onClicked: addColumnDialog.popUp(root.model.selectedColumn + 1) } + + Item { // spacer + width: 20 + height: 1 + } + + IconButton { + icon: StudioTheme.Constants.addrowbelow_medium + tooltip: qsTr("Insert record below") + enabled: root.model.selectedRow > -1 + onClicked: root.model.insertRow(root.model.selectedRow + 1) + } + + IconButton { + icon: StudioTheme.Constants.addrowabove_medium + tooltip: qsTr("Insert record above") + enabled: root.model.selectedRow > -1 + onClicked: root.model.insertRow(root.model.selectedRow) + } } Row { diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp index 350e9326bf8..d04d1e80cfd 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp @@ -124,6 +124,20 @@ void CollectionDetails::insertElementAt(std::optional object, int r markChanged(); } +void CollectionDetails::insertEmptyElements(int row, int count) +{ + if (!isValid()) + return; + + if (count < 1) + return; + + row = qBound(0, row, rows() - 1); + d->elements.insert(row, count, {}); + + markChanged(); +} + bool CollectionDetails::setHeader(int column, const QString &value) { if (!d->isValidColumnId(column)) diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.h b/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.h index 61c9f963192..61653bf0a08 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.h +++ b/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.h @@ -48,6 +48,7 @@ public: bool removeColumns(int colIdx, int count = 1); void insertElementAt(std::optional object, int row = -1); + void insertEmptyElements(int row = 0, int count = 1); bool setHeader(int column, const QString &value); diff --git a/src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.cpp b/src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.cpp index 00d20d51a3b..144904181b7 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.cpp @@ -91,6 +91,20 @@ bool SingleCollectionModel::setHeaderData(int section, return headerChanged; } +bool SingleCollectionModel::insertRows(int row, int count, const QModelIndex &parent) +{ + if (count < 1) + return false; + + row = qBound(0, row, rowCount()); + beginInsertRows(parent, row, row + count); + m_currentCollection.insertEmptyElements(row, count); + endInsertRows(); + + updateEmpty(); + return true; +} + bool SingleCollectionModel::removeColumns(int column, int count, const QModelIndex &parent) { if (column < 0 || column >= columnCount(parent) || count < 1) diff --git a/src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.h b/src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.h index 0972a169ab5..2ce960ad167 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.h +++ b/src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.h @@ -35,6 +35,7 @@ public: Qt::Orientation orientation, const QVariant &value, int role = Qt::EditRole) override; + bool insertRows(int row, int count, const QModelIndex &parent = {}) override; bool removeColumns(int column, int count, const QModelIndex &parent = QModelIndex()) override; Qt::ItemFlags flags(const QModelIndex &index) const override; From c63caa5a39d6e9e3b8f6791c9e7280fb35a1d296 Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Tue, 10 Oct 2023 10:21:15 +0300 Subject: [PATCH 019/242] QmlDesigner: Remove the selected row/column Task-number: QDS-10948 Change-Id: Iefaa658bef20bed5f8ecb9f7f288871b7d0ee83a Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Mahmoud Badri Reviewed-by: Miikka Heikkinen --- .../CollectionDetailsToolbar.qml | 18 +++++++++++-- .../collectioneditor/collectiondetails.cpp | 27 ++++++++++++------- .../collectioneditor/collectiondetails.h | 1 + .../singlecollectionmodel.cpp | 18 +++++++++++-- .../collectioneditor/singlecollectionmodel.h | 3 ++- 5 files changed, 53 insertions(+), 14 deletions(-) diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsToolbar.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsToolbar.qml index 25ad17ae85b..97c218c6417 100644 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsToolbar.qml +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsToolbar.qml @@ -45,6 +45,13 @@ Item { onClicked: addColumnDialog.popUp(root.model.selectedColumn + 1) } + IconButton { + icon: StudioTheme.Constants.deletecolumn_medium + tooltip: qsTr("Delete selected property") + enabled: root.model.selectedColumn > -1 + onClicked: root.model.removeColumn(root.model.selectedColumn) + } + Item { // spacer width: 20 height: 1 @@ -52,17 +59,24 @@ Item { IconButton { icon: StudioTheme.Constants.addrowbelow_medium - tooltip: qsTr("Insert record below") + tooltip: qsTr("Insert row below") enabled: root.model.selectedRow > -1 onClicked: root.model.insertRow(root.model.selectedRow + 1) } IconButton { icon: StudioTheme.Constants.addrowabove_medium - tooltip: qsTr("Insert record above") + tooltip: qsTr("Insert row above") enabled: root.model.selectedRow > -1 onClicked: root.model.insertRow(root.model.selectedRow) } + + IconButton { + icon: StudioTheme.Constants.deleterow_medium + tooltip: qsTr("Delete selected row") + enabled: root.model.selectedRow > -1 + onClicked: root.model.removeRow(root.model.selectedRow) + } } Row { diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp index d04d1e80cfd..b22852e446b 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp @@ -75,17 +75,11 @@ void CollectionDetails::insertColumn(const QString &header, int colIdx, const QV bool CollectionDetails::removeColumns(int colIdx, int count) { - if (count < 1) + if (count < 1 || !isValid() || !d->isValidColumnId(colIdx)) return false; - if (!isValid()) - return false; - - if (!d->isValidColumnId(colIdx)) - return false; - - int maxPossibleCount = d->headers.count() - colIdx; - count = std::min(maxPossibleCount, count); + int maxCount = d->headers.count() - colIdx; + count = std::min(maxCount, count); const QStringList removedHeaders = d->headers.mid(colIdx, count); d->headers.remove(colIdx, count); @@ -138,6 +132,21 @@ void CollectionDetails::insertEmptyElements(int row, int count) markChanged(); } +bool CollectionDetails::removeElements(int row, int count) +{ + if (count < 1 || !isValid() || !d->isValidRowId(row)) + return false; + + int maxCount = d->elements.count() - row; + count = std::min(maxCount, count); + + d->elements.remove(row, count); + + markChanged(); + + return true; +} + bool CollectionDetails::setHeader(int column, const QString &value) { if (!d->isValidColumnId(column)) diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.h b/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.h index 61653bf0a08..a26ffb558f3 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.h +++ b/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.h @@ -49,6 +49,7 @@ public: void insertElementAt(std::optional object, int row = -1); void insertEmptyElements(int row = 0, int count = 1); + bool removeElements(int row, int count = 1); bool setHeader(int column, const QString &value); diff --git a/src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.cpp b/src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.cpp index 144904181b7..fb46bd33ce2 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.cpp @@ -38,6 +38,8 @@ SingleCollectionModel::SingleCollectionModel(QObject *parent) : QAbstractTableModel(parent) { connect(this, &SingleCollectionModel::modelReset, this, &SingleCollectionModel::updateEmpty); + connect(this, &SingleCollectionModel::rowsInserted, this, &SingleCollectionModel::updateEmpty); + connect(this, &SingleCollectionModel::rowsRemoved, this, &SingleCollectionModel::updateEmpty); } QHash SingleCollectionModel::roleNames() const @@ -101,7 +103,6 @@ bool SingleCollectionModel::insertRows(int row, int count, const QModelIndex &pa m_currentCollection.insertEmptyElements(row, count); endInsertRows(); - updateEmpty(); return true; } @@ -111,13 +112,26 @@ bool SingleCollectionModel::removeColumns(int column, int count, const QModelInd return false; count = std::min(count, columnCount(parent) - column); - beginRemoveColumns(parent, column, column + count); + beginRemoveColumns(parent, column, column + count - 1); bool columnsRemoved = m_currentCollection.removeColumns(column, count); endRemoveColumns(); return columnsRemoved; } +bool SingleCollectionModel::removeRows(int row, int count, const QModelIndex &parent) +{ + if (row < 0 || row >= rowCount(parent) || count < 1) + return false; + + count = std::min(count, rowCount(parent) - row); + beginRemoveRows(parent, row, row + count - 1); + bool rowsRemoved = m_currentCollection.removeElements(row, count); + endRemoveRows(); + + return rowsRemoved; +} + Qt::ItemFlags SingleCollectionModel::flags(const QModelIndex &index) const { if (!index.isValid()) diff --git a/src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.h b/src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.h index 2ce960ad167..75654204460 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.h +++ b/src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.h @@ -36,7 +36,8 @@ public: const QVariant &value, int role = Qt::EditRole) override; bool insertRows(int row, int count, const QModelIndex &parent = {}) override; - bool removeColumns(int column, int count, const QModelIndex &parent = QModelIndex()) override; + bool removeColumns(int column, int count, const QModelIndex &parent = {}) override; + bool removeRows(int row, int count, const QModelIndex &parent = {}) override; Qt::ItemFlags flags(const QModelIndex &index) const override; QVariant headerData(int section, From 5cfc9734d6e7ff940ee11c6d23866ef7208fa64b Mon Sep 17 00:00:00 2001 From: Amr Essam Date: Tue, 10 Oct 2023 14:02:01 +0300 Subject: [PATCH 020/242] QmlDesigner: Create temp files for generated shaders Also some cleanups regarding usage of QShaderTools Task-number: QDS-10811 Change-Id: I655189e53a39e8342d849d3ffff0f89de9beb5e5 Reviewed-by: Mahmoud Badri Reviewed-by: Miikka Heikkinen Reviewed-by: Qt CI Patch Build Bot --- .../effectmakernew/effectmakermodel.cpp | 60 ++++--------------- src/plugins/effectmakernew/effectmakermodel.h | 7 +-- 2 files changed, 13 insertions(+), 54 deletions(-) diff --git a/src/plugins/effectmakernew/effectmakermodel.cpp b/src/plugins/effectmakernew/effectmakermodel.cpp index 364a012fe30..293d1311b7d 100644 --- a/src/plugins/effectmakernew/effectmakermodel.cpp +++ b/src/plugins/effectmakernew/effectmakermodel.cpp @@ -44,17 +44,13 @@ static bool writeToFile(const QByteArray &buf, const QString &filename, FileType EffectMakerModel::EffectMakerModel(QObject *parent) : QAbstractListModel{parent} { + m_vertexSourceFile.setFileTemplate(QDir::tempPath() + "/dsem_XXXXXX.vert"); + m_fragmentSourceFile.setFileTemplate(QDir::tempPath() + "/dsem_XXXXXX.frag"); m_vertexShaderFile.setFileTemplate(QDir::tempPath() + "/dsem_XXXXXX.vert.qsb"); m_fragmentShaderFile.setFileTemplate(QDir::tempPath() + "/dsem_XXXXXX.frag.qsb"); - // TODO: Will be revisted later when saving output files - if (m_vertexShaderFile.open()) - qInfo() << "Using temporary vs file:" << m_vertexShaderFile.fileName(); - if (m_fragmentShaderFile.open()) - qInfo() << "Using temporary fs file:" << m_fragmentShaderFile.fileName(); - - // Prepare baker - m_baker.setGeneratedShaderVariants({ QShader::StandardShader }); - updateBakedShaderVersions(); + if (!m_vertexSourceFile.open() || !m_fragmentSourceFile.open() + || !m_vertexShaderFile.open() || !m_fragmentShaderFile.open()) + qWarning() << "Unable to open temporary files"; } QHash EffectMakerModel::roleNames() const @@ -141,21 +137,6 @@ void EffectMakerModel::removeNode(int idx) bakeShaders(); } -void EffectMakerModel::updateBakedShaderVersions() -{ - QList targets; - targets.append({ QShader::SpirvShader, QShaderVersion(100) }); // Vulkan 1.0 - targets.append({ QShader::HlslShader, QShaderVersion(50) }); // Shader Model 5.0 - targets.append({ QShader::MslShader, QShaderVersion(12) }); // Metal 1.2 - targets.append({ QShader::GlslShader, QShaderVersion(300, QShaderVersion::GlslEs) }); // GLES 3.0+ - targets.append({ QShader::GlslShader, QShaderVersion(410) }); // OpenGL 4.1+ - targets.append({ QShader::GlslShader, QShaderVersion(330) }); // OpenGL 3.3 - targets.append({ QShader::GlslShader, QShaderVersion(140) }); // OpenGL 3.1 - //TODO: Do we need support for legacy shaders 100, 120? - - m_baker.setGeneratedShaders(targets); -} - QString EffectMakerModel::fragmentShader() const { return m_fragmentShader; @@ -813,37 +794,16 @@ void EffectMakerModel::bakeShaders() setVertexShader(generateVertexShader()); QString vs = m_vertexShader; - m_baker.setSourceString(vs.toUtf8(), QShader::VertexStage); - QShader vertShader = m_baker.bake(); - - if (!vertShader.isValid()) { - qWarning() << "Shader baking failed:" << qPrintable(m_baker.errorMessage()); - setEffectError(m_baker.errorMessage().split('\n').first(), ErrorVert); - } else { - QString filename = m_vertexShaderFile.fileName(); - writeToFile(vertShader.serialized(), filename, FileType::Binary); - resetEffectError(ErrorVert); - } + writeToFile(vs.toUtf8(), m_vertexSourceFile.fileName(), FileType::Text); setFragmentShader(generateFragmentShader()); QString fs = m_fragmentShader; - m_baker.setSourceString(fs.toUtf8(), QShader::FragmentStage); + writeToFile(fs.toUtf8(), m_fragmentSourceFile.fileName(), FileType::Text); - QShader fragShader = m_baker.bake(); + //TODO: Compile shaders using external qsb tools - if (!fragShader.isValid()) { - qWarning() << "Shader baking failed:" << qPrintable(m_baker.errorMessage()); - setEffectError(m_baker.errorMessage().split('\n').first(), ErrorFrag); - } else { - QString filename = m_fragmentShaderFile.fileName(); - writeToFile(fragShader.serialized(), filename, FileType::Binary); - resetEffectError(ErrorFrag); - } - - if (vertShader.isValid() && fragShader.isValid()) { - Q_EMIT shadersBaked(); - setShadersUpToDate(true); - } + Q_EMIT shadersBaked(); + setShadersUpToDate(true); // TODO: Mark shaders as baked, required by export later } diff --git a/src/plugins/effectmakernew/effectmakermodel.h b/src/plugins/effectmakernew/effectmakermodel.h index ad315a9c8d1..223656d382e 100644 --- a/src/plugins/effectmakernew/effectmakermodel.h +++ b/src/plugins/effectmakernew/effectmakermodel.h @@ -12,8 +12,6 @@ #include #include -#include - namespace Utils { class Process; } @@ -108,7 +106,6 @@ private: const QString getVSUniforms(); const QString getFSUniforms(); - void updateBakedShaderVersions(); QString detectErrorMessage(const QString &errorMessage); EffectError effectError() const; @@ -150,7 +147,9 @@ private: QString m_vertexShader; QStringList m_defaultRootVertexShader; QStringList m_defaultRootFragmentShader; - QShaderBaker m_baker; // TODO: Remove + // Temp files to store shaders sources and binary data + QTemporaryFile m_fragmentSourceFile; + QTemporaryFile m_vertexSourceFile; QTemporaryFile m_fragmentShaderFile; QTemporaryFile m_vertexShaderFile; // Used in exported QML, at root of the file From fed8f8c8457e2e05667cdc9ccc9d745c86fd9e63 Mon Sep 17 00:00:00 2001 From: Henning Gruendl Date: Thu, 5 Oct 2023 20:12:33 +0200 Subject: [PATCH 021/242] QmlDesigner: Fix id mismatch Change-Id: I4262d2e0efd167dec987fd48574519e0e156c4ff Reviewed-by: Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Thomas Hartmann --- .../imports/StudioControls/FilterComboBox.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/FilterComboBox.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/FilterComboBox.qml index 8531d791348..f17ef05a8f6 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/FilterComboBox.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/FilterComboBox.qml @@ -694,7 +694,7 @@ Item { ScrollBar.vertical: TransientScrollBar { id: popupScrollBar parent: listView - x: listView.width - verticalScrollBar.width + x: listView.width - popupScrollBar.width y: 0 height: listView.availableHeight orientation: Qt.Vertical From 7ffa49f217b60e69b7ecb34ea0855359909de655 Mon Sep 17 00:00:00 2001 From: Henning Gruendl Date: Tue, 10 Oct 2023 15:25:50 +0200 Subject: [PATCH 022/242] QmlDesigner: Make material browser responsive Change-Id: Ic6b2b9583dc2190974de7c74f8b39e22aa59226d Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Mahmoud Badri Reviewed-by: Miikka Heikkinen --- .../MaterialBrowser.qml | 239 +++++++++--------- .../materialBrowserQmlSource/MaterialItem.qml | 34 +-- .../materialBrowserQmlSource/TextureItem.qml | 31 ++- 3 files changed, 160 insertions(+), 144 deletions(-) diff --git a/share/qtcreator/qmldesigner/materialBrowserQmlSource/MaterialBrowser.qml b/share/qtcreator/qmldesigner/materialBrowserQmlSource/MaterialBrowser.qml index 2d7e8fe8fef..e0dacef36be 100644 --- a/share/qtcreator/qmldesigner/materialBrowserQmlSource/MaterialBrowser.qml +++ b/share/qtcreator/qmldesigner/materialBrowserQmlSource/MaterialBrowser.qml @@ -12,8 +12,8 @@ Item { id: root focus: true - readonly property int cellWidth: 100 - readonly property int cellHeight: 120 + readonly property real cellWidth: root.thumbnailSize + readonly property real cellHeight: root.thumbnailSize + 20 readonly property bool enableUiElements: materialBrowserModel.hasMaterialLibrary && materialBrowserModel.hasQuick3DImport @@ -22,30 +22,60 @@ Item { property var materialBrowserModel: MaterialBrowserBackend.materialBrowserModel property var materialBrowserTexturesModel: MaterialBrowserBackend.materialBrowserTexturesModel + property int numColumns: 0 + property real thumbnailSize: 100 + + readonly property int minThumbSize: 100 + readonly property int maxThumbSize: 150 + + function responsiveResize(width: int, height: int) { + width -= 2 * StudioTheme.Values.sectionPadding + + let numColumns = Math.floor(width / root.minThumbSize) + let remainder = width % root.minThumbSize + let space = (numColumns - 1) * StudioTheme.Values.sectionGridSpacing + + if (remainder < space) + numColumns -= 1 + + if (numColumns < 1) + return + + let maxItems = Math.max(texturesRepeater.count, materialRepeater.count) + + if (numColumns > maxItems) + numColumns = maxItems + + let rest = width - (numColumns * root.minThumbSize) + - ((numColumns - 1) * StudioTheme.Values.sectionGridSpacing) + + root.thumbnailSize = Math.min(root.minThumbSize + (rest / numColumns), + root.maxThumbSize) + root.numColumns = numColumns + } + + onWidthChanged: root.responsiveResize(root.width, root.height) + // Called also from C++ to close context menu on focus out - function closeContextMenu() - { + function closeContextMenu() { ctxMenu.close() ctxMenuTextures.close() HelperWidgets.Controller.closeContextMenu() } // Called from C++ to refresh a preview material after it changes - function refreshPreview(idx) - { + function refreshPreview(idx) { var item = materialRepeater.itemAt(idx); if (item) - item.refreshPreview(); + item.refreshPreview() } // Called from C++ - function clearSearchFilter() - { - searchBox.clear(); + function clearSearchFilter() { + searchBox.clear() } - function nextVisibleItem(idx, count, itemModel) - { + function nextVisibleItem(idx, count, itemModel) { if (count === 0) return idx @@ -66,8 +96,7 @@ Item { return newIdx } - function visibleItemCount(itemModel) - { + function visibleItemCount(itemModel) { let curIdx = 0 let count = 0 @@ -79,8 +108,7 @@ Item { return count } - function rowIndexOfItem(idx, rowSize, itemModel) - { + function rowIndexOfItem(idx, rowSize, itemModel) { if (rowSize === 1) return 1 @@ -98,8 +126,7 @@ Item { return count % rowSize } - function selectNextVisibleItem(delta) - { + function selectNextVisibleItem(delta) { if (searchBox.activeFocus) return @@ -112,36 +139,36 @@ Item { if (delta < 0) { if (matSecFocused) { - targetIdx = nextVisibleItem(materialBrowserModel.selectedIndex, - delta, materialBrowserModel) + targetIdx = root.nextVisibleItem(materialBrowserModel.selectedIndex, + delta, materialBrowserModel) if (targetIdx >= 0) materialBrowserModel.selectMaterial(targetIdx) } else if (texSecFocused) { - targetIdx = nextVisibleItem(materialBrowserTexturesModel.selectedIndex, - delta, materialBrowserTexturesModel) + targetIdx = root.nextVisibleItem(materialBrowserTexturesModel.selectedIndex, + delta, materialBrowserTexturesModel) if (targetIdx >= 0) { materialBrowserTexturesModel.selectTexture(targetIdx) } else if (!materialBrowserModel.isEmpty && materialsSection.expanded) { - targetIdx = nextVisibleItem(materialBrowserModel.rowCount(), -1, materialBrowserModel) + targetIdx = root.nextVisibleItem(materialBrowserModel.rowCount(), -1, materialBrowserModel) if (targetIdx >= 0) { if (delta !== -1) { // Try to match column when switching between materials/textures - origRowIdx = rowIndexOfItem(materialBrowserTexturesModel.selectedIndex, - -delta, materialBrowserTexturesModel) - if (visibleItemCount(materialBrowserModel) > origRowIdx) { - rowIdx = rowIndexOfItem(targetIdx, -delta, materialBrowserModel) + origRowIdx = root.rowIndexOfItem(materialBrowserTexturesModel.selectedIndex, + -delta, materialBrowserTexturesModel) + if (root.visibleItemCount(materialBrowserModel) > origRowIdx) { + rowIdx = root.rowIndexOfItem(targetIdx, -delta, materialBrowserModel) if (rowIdx >= origRowIdx) { - newTargetIdx = nextVisibleItem(targetIdx, - -(rowIdx - origRowIdx), - materialBrowserModel) + newTargetIdx = root.nextVisibleItem(targetIdx, + -(rowIdx - origRowIdx), + materialBrowserModel) } else { - newTargetIdx = nextVisibleItem(targetIdx, - -(-delta - origRowIdx + rowIdx), - materialBrowserModel) + newTargetIdx = root.nextVisibleItem(targetIdx, + -(-delta - origRowIdx + rowIdx), + materialBrowserModel) } } else { - newTargetIdx = nextVisibleItem(materialBrowserModel.rowCount(), - -1, materialBrowserModel) + newTargetIdx = root.nextVisibleItem(materialBrowserModel.rowCount(), + -1, materialBrowserModel) } if (newTargetIdx >= 0) targetIdx = newTargetIdx @@ -153,25 +180,25 @@ Item { } } else if (delta > 0) { if (matSecFocused) { - targetIdx = nextVisibleItem(materialBrowserModel.selectedIndex, - delta, materialBrowserModel) + targetIdx = root.nextVisibleItem(materialBrowserModel.selectedIndex, + delta, materialBrowserModel) if (targetIdx >= 0) { materialBrowserModel.selectMaterial(targetIdx) } else if (!materialBrowserTexturesModel.isEmpty && texturesSection.expanded) { - targetIdx = nextVisibleItem(-1, 1, materialBrowserTexturesModel) + targetIdx = root.nextVisibleItem(-1, 1, materialBrowserTexturesModel) if (targetIdx >= 0) { if (delta !== 1) { // Try to match column when switching between materials/textures - origRowIdx = rowIndexOfItem(materialBrowserModel.selectedIndex, - delta, materialBrowserModel) - if (visibleItemCount(materialBrowserTexturesModel) > origRowIdx) { + origRowIdx = root.rowIndexOfItem(materialBrowserModel.selectedIndex, + delta, materialBrowserModel) + if (root.visibleItemCount(materialBrowserTexturesModel) > origRowIdx) { if (origRowIdx > 0) { - newTargetIdx = nextVisibleItem(targetIdx, origRowIdx, - materialBrowserTexturesModel) + newTargetIdx = root.nextVisibleItem(targetIdx, origRowIdx, + materialBrowserTexturesModel) } } else { - newTargetIdx = nextVisibleItem(materialBrowserTexturesModel.rowCount(), - -1, materialBrowserTexturesModel) + newTargetIdx = root.nextVisibleItem(materialBrowserTexturesModel.rowCount(), + -1, materialBrowserTexturesModel) } if (newTargetIdx >= 0) targetIdx = newTargetIdx @@ -181,8 +208,8 @@ Item { } } } else if (texSecFocused) { - targetIdx = nextVisibleItem(materialBrowserTexturesModel.selectedIndex, - delta, materialBrowserTexturesModel) + targetIdx = root.nextVisibleItem(materialBrowserTexturesModel.selectedIndex, + delta, materialBrowserTexturesModel) if (targetIdx >= 0) materialBrowserTexturesModel.selectTexture(targetIdx) } @@ -190,24 +217,12 @@ Item { } Keys.enabled: true - Keys.onDownPressed: { - selectNextVisibleItem(gridMaterials.columns) - } + Keys.onDownPressed: root.selectNextVisibleItem(gridMaterials.columns) + Keys.onUpPressed: root.selectNextVisibleItem(-gridMaterials.columns) + Keys.onLeftPressed: root.selectNextVisibleItem(-1) + Keys.onRightPressed: root.selectNextVisibleItem(1) - Keys.onUpPressed: { - selectNextVisibleItem(-gridMaterials.columns) - } - - Keys.onLeftPressed: { - selectNextVisibleItem(-1) - } - - Keys.onRightPressed: { - selectNextVisibleItem(1) - } - - function handleEnterPress() - { + function handleEnterPress() { if (searchBox.activeFocus) return @@ -217,13 +232,8 @@ Item { materialBrowserTexturesModel.openTextureEditor() } - Keys.onEnterPressed: { - handleEnterPress() - } - - Keys.onReturnPressed: { - handleEnterPress() - } + Keys.onEnterPressed: root.handleEnterPress() + Keys.onReturnPressed: root.handleEnterPress() MouseArea { id: focusGrabber @@ -249,10 +259,10 @@ Item { onClicked: (mouse) => { if (!root.enableUiElements) - return; + return var matsSecBottom = mapFromItem(materialsSection, 0, materialsSection.y).y - + materialsSection.height; + + materialsSection.height if (mouse.y < matsSecBottom) ctxMenu.popupMenu() @@ -261,8 +271,7 @@ Item { } } - function ensureVisible(yPos, itemHeight) - { + function ensureVisible(yPos, itemHeight) { let currentY = contentYBehavior.targetValue && scrollViewAnim.running ? contentYBehavior.targetValue : scrollView.contentY @@ -286,18 +295,18 @@ Item { return false } - function ensureSelectedVisible() - { + function ensureSelectedVisible() { if (rootView.materialSectionFocused && materialsSection.expanded && root.currMaterialItem && materialBrowserModel.isVisible(materialBrowserModel.selectedIndex)) { - return ensureVisible(root.currMaterialItem.mapToItem(scrollView.contentItem, 0, 0).y, - root.currMaterialItem.height) + return root.ensureVisible(root.currMaterialItem.mapToItem(scrollView.contentItem, 0, 0).y, + root.currMaterialItem.height) } else if (!rootView.materialSectionFocused && texturesSection.expanded) { let currItem = texturesRepeater.itemAt(materialBrowserTexturesModel.selectedIndex) if (currItem && materialBrowserTexturesModel.isVisible(materialBrowserTexturesModel.selectedIndex)) - return ensureVisible(currItem.mapToItem(scrollView.contentItem, 0, 0).y, currItem.height) + return root.ensureVisible(currItem.mapToItem(scrollView.contentItem, 0, 0).y, + currItem.height) } else { - return ensureVisible(0, 90) + return root.ensureVisible(0, 90) } } @@ -310,15 +319,14 @@ Item { onTriggered: { // Redo until ensuring didn't change things if (!root.ensureSelectedVisible()) { - stop() - interval = 20 - triggeredOnStart = true + ensureTimer.stop() + ensureTimer.interval = 20 + ensureTimer.triggeredOnStart = true } } } - function startDelayedEnsureTimer(delay) - { + function startDelayedEnsureTimer(delay) { // Ensuring visibility immediately in some cases like before new search results are rendered // causes mapToItem return incorrect values, leading to undesirable flicker, // so delay ensuring visibility a bit. @@ -330,8 +338,7 @@ Item { Connections { target: materialBrowserModel - function onSelectedIndexChanged() - { + function onSelectedIndexChanged() { // commit rename upon changing selection if (root.currMaterialItem) root.currMaterialItem.forceFinishEditing(); @@ -341,8 +348,7 @@ Item { ensureTimer.start() } - function onIsEmptyChanged() - { + function onIsEmptyChanged() { ensureTimer.start() } } @@ -350,13 +356,11 @@ Item { Connections { target: materialBrowserTexturesModel - function onSelectedIndexChanged() - { + function onSelectedIndexChanged() { ensureTimer.start() } - function onIsEmptyChanged() - { + function onIsEmptyChanged() { ensureTimer.start() } } @@ -364,8 +368,7 @@ Item { Connections { target: rootView - function onMaterialSectionFocusedChanged() - { + function onMaterialSectionFocusedChanged() { ensureTimer.start() } } @@ -614,9 +617,10 @@ Item { id: scrollView width: root.width - height: root.height - toolbar.height + height: root.height - toolbar.height - col.spacing clip: true visible: root.enableUiElements + hideHorizontalScrollBar: true interactive: !ctxMenu.opened && !ctxMenuTextures.opened && !rootView.isDragging && !HelperWidgets.Controller.contextMenuOpened @@ -637,6 +641,12 @@ Item { id: materialsSection width: root.width + + leftPadding: StudioTheme.Values.sectionPadding + rightPadding: StudioTheme.Values.sectionPadding + topPadding: StudioTheme.Values.sectionPadding + bottomPadding: StudioTheme.Values.sectionPadding + caption: qsTr("Materials") dropEnabled: true category: "MaterialBrowser" @@ -671,11 +681,10 @@ Item { Grid { id: gridMaterials - width: scrollView.width - leftPadding: 5 - rightPadding: 5 - bottomPadding: 5 - columns: root.width / root.cellWidth + width: scrollView.width - materialsSection.leftPadding + - materialsSection.rightPadding + spacing: StudioTheme.Values.sectionGridSpacing + columns: root.numColumns Repeater { id: materialRepeater @@ -691,10 +700,10 @@ Item { width: root.cellWidth height: root.cellHeight - onShowContextMenu: { - ctxMenu.popupMenu(this, model) - } + onShowContextMenu: ctxMenu.popupMenu(this, model) } + + onCountChanged: root.responsiveResize(root.width, root.height) } } @@ -727,6 +736,11 @@ Item { id: texturesSection width: root.width + leftPadding: StudioTheme.Values.sectionPadding + rightPadding: StudioTheme.Values.sectionPadding + topPadding: StudioTheme.Values.sectionPadding + bottomPadding: StudioTheme.Values.sectionPadding + caption: qsTr("Textures") category: "MaterialBrowser" @@ -768,11 +782,10 @@ Item { Grid { id: gridTextures - width: scrollView.width - leftPadding: 5 - rightPadding: 5 - bottomPadding: 5 - columns: root.width / root.cellWidth + width: scrollView.width - texturesSection.leftPadding + - texturesSection.rightPadding + spacing: StudioTheme.Values.sectionGridSpacing + columns: root.numColumns Repeater { id: texturesRepeater @@ -782,10 +795,10 @@ Item { width: root.cellWidth height: root.cellHeight - onShowContextMenu: { - ctxMenuTextures.popupMenu(model) - } + onShowContextMenu: ctxMenuTextures.popupMenu(model) } + + onCountChanged: root.responsiveResize(root.width, root.height) } } diff --git a/share/qtcreator/qmldesigner/materialBrowserQmlSource/MaterialItem.qml b/share/qtcreator/qmldesigner/materialBrowserQmlSource/MaterialItem.qml index f055d1d665d..ce1b12aeaeb 100644 --- a/share/qtcreator/qmldesigner/materialBrowserQmlSource/MaterialItem.qml +++ b/share/qtcreator/qmldesigner/materialBrowserQmlSource/MaterialItem.qml @@ -8,32 +8,24 @@ import HelperWidgets 2.0 import StudioTheme 1.0 as StudioTheme import MaterialBrowserBackend -Rectangle { +Item { id: root signal showContextMenu() - function refreshPreview() - { + function refreshPreview() { img.source = "" img.source = "image://materialBrowser/" + materialInternalId } - function forceFinishEditing() - { + function forceFinishEditing() { matName.commitRename() } - function startRename() - { + function startRename() { matName.startRename() } - border.width: MaterialBrowserBackend.materialBrowserModel.selectedIndex === index ? MaterialBrowserBackend.rootView.materialSectionFocused ? 3 : 1 : 0 - border.color: MaterialBrowserBackend.materialBrowserModel.selectedIndex === index - ? StudioTheme.Values.themeControlOutlineInteraction - : "transparent" - color: "transparent" visible: materialVisible DropArea { @@ -81,12 +73,10 @@ Rectangle { anchors.fill: parent spacing: 1 - Item { width: 1; height: 5 } // spacer - Image { id: img - width: root.width - 10 + width: root.width height: img.width anchors.horizontalCenter: parent.horizontalCenter source: "image://materialBrowser/" + materialInternalId @@ -94,8 +84,8 @@ Rectangle { } // Eat keys so they are not passed to parent while editing name - Keys.onPressed: (e) => { - e.accepted = true; + Keys.onPressed: (event) => { + event.accepted = true } MaterialBrowserItemName { @@ -116,4 +106,14 @@ Rectangle { } } } + + Rectangle { + id: marker + anchors.fill: parent + border.width: MaterialBrowserBackend.materialBrowserModel.selectedIndex === index ? MaterialBrowserBackend.rootView.materialSectionFocused ? 3 : 1 : 0 + border.color: MaterialBrowserBackend.materialBrowserModel.selectedIndex === index + ? StudioTheme.Values.themeControlOutlineInteraction + : "transparent" + color: "transparent" + } } diff --git a/share/qtcreator/qmldesigner/materialBrowserQmlSource/TextureItem.qml b/share/qtcreator/qmldesigner/materialBrowserQmlSource/TextureItem.qml index b0a5e138099..4eaa449c62a 100644 --- a/share/qtcreator/qmldesigner/materialBrowserQmlSource/TextureItem.qml +++ b/share/qtcreator/qmldesigner/materialBrowserQmlSource/TextureItem.qml @@ -9,22 +9,14 @@ import HelperWidgets import StudioTheme as StudioTheme import MaterialBrowserBackend -Rectangle { +Item { id: root visible: textureVisible - color: "transparent" - border.width: MaterialBrowserBackend.materialBrowserTexturesModel.selectedIndex === index - ? !MaterialBrowserBackend.rootView.materialSectionFocused ? 3 : 1 : 0 - border.color: MaterialBrowserBackend.materialBrowserTexturesModel.selectedIndex === index - ? StudioTheme.Values.themeControlOutlineInteraction - : "transparent" - signal showContextMenu() - function forceFinishEditing() - { + function forceFinishEditing() { txtId.commitRename() } @@ -68,12 +60,11 @@ Rectangle { anchors.fill: parent spacing: 1 - Item { width: 1; height: 5 } // spacer Image { id: img source: "image://materialBrowserTex/" + textureSource asynchronous: true - width: root.width - 10 + width: root.width height: img.width anchors.horizontalCenter: parent.horizontalCenter smooth: true @@ -81,8 +72,8 @@ Rectangle { } // Eat keys so they are not passed to parent while editing name - Keys.onPressed: (e) => { - e.accepted = true; + Keys.onPressed: (event) => { + event.accepted = true } MaterialBrowserItemName { @@ -103,4 +94,16 @@ Rectangle { } } } + + Rectangle { + id: marker + anchors.fill: parent + + color: "transparent" + border.width: MaterialBrowserBackend.materialBrowserTexturesModel.selectedIndex === index + ? !MaterialBrowserBackend.rootView.materialSectionFocused ? 3 : 1 : 0 + border.color: MaterialBrowserBackend.materialBrowserTexturesModel.selectedIndex === index + ? StudioTheme.Values.themeControlOutlineInteraction + : "transparent" + } } From c54c62e5bd697010681a72af3f3ab85f610add62 Mon Sep 17 00:00:00 2001 From: Henning Gruendl Date: Thu, 5 Oct 2023 20:13:19 +0200 Subject: [PATCH 023/242] QmlDesigner: Make content library responsive Task-number: QDS-10769 Change-Id: I5f2b74b11535583c0e7d4ee8408b75d490e88fc2 Reviewed-by: Miikka Heikkinen Reviewed-by: Qt CI Patch Build Bot --- .../ContentLibrary.qml | 86 ++++++++++++++++--- .../ContentLibraryEffect.qml | 11 +-- .../ContentLibraryEffectsView.qml | 44 +++++++--- .../ContentLibraryMaterial.qml | 42 ++++----- .../ContentLibraryMaterialsView.qml | 46 +++++++--- .../ContentLibraryTexture.qml | 35 ++++---- .../ContentLibraryTexturesView.qml | 48 +++++++---- .../imports/HelperWidgets/ScrollView.qml | 7 ++ .../imports/StudioTheme/Values.qml | 3 + 9 files changed, 216 insertions(+), 106 deletions(-) diff --git a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibrary.qml b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibrary.qml index 306f5405b30..63ac2517aed 100644 --- a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibrary.qml +++ b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibrary.qml @@ -19,8 +19,7 @@ Item { objectName: "__mainSrollView" // Called also from C++ to close context menu on focus out - function closeContextMenu() - { + function closeContextMenu() { materialsView.closeContextMenu() texturesView.closeContextMenu() environmentsView.closeContextMenu() @@ -29,9 +28,43 @@ Item { } // Called from C++ - function clearSearchFilter() - { - searchBox.clear(); + function clearSearchFilter() { + searchBox.clear() + } + + property int numColumns: 4 + property real thumbnailSize: 100 + + readonly property int minThumbSize: 100 + readonly property int maxThumbSize: 150 + + function responsiveResize(width: int, height: int) { + width -= 2 * StudioTheme.Values.sectionPadding + + let numColumns = Math.floor(width / root.minThumbSize) + let remainder = width % root.minThumbSize + let space = (numColumns - 1) * StudioTheme.Values.sectionGridSpacing + + if (remainder < space) + numColumns -= 1 + + if (numColumns < 1) + return + + let maxItems = Math.max(materialsView.count, + texturesView.count, + environmentsView.count, + effectsView.count) + + if (numColumns > maxItems) + numColumns = maxItems + + let rest = width - (numColumns * root.minThumbSize) + - ((numColumns - 1) * StudioTheme.Values.sectionGridSpacing) + + root.thumbnailSize = Math.min(root.minThumbSize + (rest / numColumns), + root.maxThumbSize) + root.numColumns = numColumns } Column { @@ -46,11 +79,11 @@ Item { Column { anchors.fill: parent - anchors.topMargin: 6 - anchors.bottomMargin: 6 - anchors.leftMargin: 10 - anchors.rightMargin: 10 - spacing: 12 + anchors.topMargin: StudioTheme.Values.toolbarVerticalMargin + anchors.bottomMargin: StudioTheme.Values.toolbarVerticalMargin + anchors.leftMargin: StudioTheme.Values.toolbarHorizontalMargin + anchors.rightMargin: StudioTheme.Values.toolbarHorizontalMargin + spacing: StudioTheme.Values.toolbarColumnSpacing StudioControls.SearchBox { id: searchBox @@ -94,16 +127,24 @@ Item { } StackLayout { + id: stackLayout width: root.width height: root.height - y currentIndex: tabBar.currIndex + onWidthChanged: root.responsiveResize(stackLayout.width, stackLayout.height) + ContentLibraryMaterialsView { id: materialsView adsFocus: root.adsFocus width: root.width + cellWidth: root.thumbnailSize + cellHeight: root.thumbnailSize + 20 + numColumns: root.numColumns + hideHorizontalScrollBar: true + searchBox: searchBox onUnimport: (bundleMat) => { @@ -111,6 +152,8 @@ Item { confirmUnimportDialog.targetBundleType = "material" confirmUnimportDialog.open() } + + onCountChanged: root.responsiveResize(stackLayout.width, stackLayout.height) } ContentLibraryTexturesView { @@ -118,10 +161,18 @@ Item { adsFocus: root.adsFocus width: root.width + + cellWidth: root.thumbnailSize + cellHeight: root.thumbnailSize + numColumns: root.numColumns + hideHorizontalScrollBar: true + model: ContentLibraryBackend.texturesModel sectionCategory: "ContentLib_Tex" searchBox: searchBox + + onCountChanged: root.responsiveResize(stackLayout.width, stackLayout.height) } ContentLibraryTexturesView { @@ -129,10 +180,18 @@ Item { adsFocus: root.adsFocus width: root.width + + cellWidth: root.thumbnailSize + cellHeight: root.thumbnailSize + numColumns: root.numColumns + hideHorizontalScrollBar: true + model: ContentLibraryBackend.environmentsModel sectionCategory: "ContentLib_Env" searchBox: searchBox + + onCountChanged: root.responsiveResize(stackLayout.width, stackLayout.height) } ContentLibraryEffectsView { @@ -141,6 +200,11 @@ Item { adsFocus: root.adsFocus width: root.width + cellWidth: root.thumbnailSize + cellHeight: root.thumbnailSize + 20 + numColumns: root.numColumns + hideHorizontalScrollBar: true + searchBox: searchBox onUnimport: (bundleItem) => { @@ -148,6 +212,8 @@ Item { confirmUnimportDialog.targetBundleType = "effect" confirmUnimportDialog.open() } + + onCountChanged: root.responsiveResize(stackLayout.width, stackLayout.height) } } } diff --git a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryEffect.qml b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryEffect.qml index 6230d38974e..d418cab0dbe 100644 --- a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryEffect.qml +++ b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryEffect.qml @@ -36,12 +36,10 @@ Item { anchors.fill: parent spacing: 1 - Item { width: 1; height: 5 } // spacer - Image { id: img - width: root.width - 10 + width: root.width height: img.width anchors.horizontalCenter: parent.horizontalCenter source: modelData.bundleItemIcon @@ -74,16 +72,13 @@ Item { } Text { - text: modelData.bundleItemName - width: img.width - clip: true - anchors.horizontalCenter: parent.horizontalCenter horizontalAlignment: TextInput.AlignHCenter + text: modelData.bundleItemName + elide: Text.ElideRight font.pixelSize: StudioTheme.Values.myFontSize - color: StudioTheme.Values.themeTextColor } } // Column diff --git a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryEffectsView.qml b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryEffectsView.qml index 7cb9a4721d8..f6df99156bf 100644 --- a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryEffectsView.qml +++ b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryEffectsView.qml @@ -14,20 +14,28 @@ HelperWidgets.ScrollView { interactive: !ctxMenu.opened && !ContentLibraryBackend.rootView.isDragging && !HelperWidgets.Controller.contextMenuOpened - readonly property int cellWidth: 100 - readonly property int cellHeight: 120 + property real cellWidth: 100 + property real cellHeight: 120 + property int numColumns: 4 + + property int count: 0 + function assignMaxCount() { + let c = 0 + for (let i = 0; i < categoryRepeater.count; ++i) + c = Math.max(c, categoryRepeater.itemAt(i)?.count ?? 0) + + root.count = c + } required property var searchBox signal unimport(var bundleItem); - function closeContextMenu() - { + function closeContextMenu() { ctxMenu.close() } - function expandVisibleSections() - { + function expandVisibleSections() { for (let i = 0; i < categoryRepeater.count; ++i) { let cat = categoryRepeater.itemAt(i) if (cat.visible && !cat.expanded) @@ -48,10 +56,15 @@ HelperWidgets.ScrollView { model: ContentLibraryBackend.effectsModel delegate: HelperWidgets.Section { + id: section + width: root.width + leftPadding: StudioTheme.Values.sectionPadding + rightPadding: StudioTheme.Values.sectionPadding + topPadding: StudioTheme.Values.sectionPadding + bottomPadding: StudioTheme.Values.sectionPadding + caption: bundleCategoryName - addTopPadding: false - sectionBackgroundColor: "transparent" visible: bundleCategoryVisible && !ContentLibraryBackend.effectsModel.isEmpty expanded: bundleCategoryExpanded expandOnClick: false @@ -65,14 +78,17 @@ HelperWidgets.ScrollView { bundleCategoryExpanded = true } + property alias count: repeater.count + + onCountChanged: root.assignMaxCount() + Grid { - width: root.width - leftPadding: 5 - rightPadding: 5 - bottomPadding: 5 - columns: root.width / root.cellWidth + width: section.width - section.leftPadding - section.rightPadding + spacing: StudioTheme.Values.sectionGridSpacing + columns: root.numColumns Repeater { + id: repeater model: bundleCategoryItems delegate: ContentLibraryEffect { @@ -81,6 +97,8 @@ HelperWidgets.ScrollView { onShowContextMenu: ctxMenu.popupMenu(modelData) } + + onCountChanged: root.assignMaxCount() } } } diff --git a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryMaterial.qml b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryMaterial.qml index 9d723dc0ef8..9160c916064 100644 --- a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryMaterial.qml +++ b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryMaterial.qml @@ -1,16 +1,15 @@ // Copyright (C) 2022 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 -import QtQuick 2.15 -import QtQuick.Layouts 1.15 -import QtQuickDesignerTheme 1.0 -import HelperWidgets 2.0 +import QtQuick import QtQuick.Controls +import QtQuick.Layouts +import QtQuickDesignerTheme +import HelperWidgets +import StudioTheme as StudioTheme -import StudioTheme 1.0 as StudioTheme import ContentLibraryBackend - -import WebFetcher 1.0 +import WebFetcher Item { id: root @@ -45,8 +44,6 @@ Item { anchors.fill: parent spacing: 1 - Item { width: 1; height: 5 } // spacer - DownloadPane { id: downloadPane width: root.width - 10 @@ -59,7 +56,7 @@ Item { Image { id: img - width: root.width - 10 + width: root.width height: img.width anchors.horizontalCenter: parent.horizontalCenter source: modelData.bundleMaterialIcon @@ -108,7 +105,7 @@ Item { onClicked: { ContentLibraryBackend.materialsModel.addToProject(modelData) } - } // IconButton + } IconButton { id: downloadIcon @@ -154,29 +151,22 @@ Item { root.downloadState = "" downloader.start() } - } // IconButton - } // Image + } + } - TextInput { + Text { id: matName - text: modelData.bundleMaterialName - width: img.width - clip: true anchors.horizontalCenter: parent.horizontalCenter horizontalAlignment: TextInput.AlignHCenter + text: modelData.bundleMaterialName + elide: Text.ElideRight font.pixelSize: StudioTheme.Values.myFontSize - - readOnly: true - selectByMouse: !matName.readOnly - color: StudioTheme.Values.themeTextColor - selectionColor: StudioTheme.Values.themeTextSelectionColor - selectedTextColor: StudioTheme.Values.themeTextSelectedTextColor } - } // Column + } Timer { id: delayedFinish @@ -223,6 +213,6 @@ Item { probeUrl: false downloadEnabled: true targetFilePath: downloader.nextTargetPath - } // FileDownloader - } // MultiFileDownloader + } + } } diff --git a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryMaterialsView.qml b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryMaterialsView.qml index f9678dcad80..c21baf4c580 100644 --- a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryMaterialsView.qml +++ b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryMaterialsView.qml @@ -14,8 +14,18 @@ HelperWidgets.ScrollView { interactive: !ctxMenu.opened && !ContentLibraryBackend.rootView.isDragging && !HelperWidgets.Controller.contextMenuOpened - readonly property int cellWidth: 100 - readonly property int cellHeight: 120 + property real cellWidth: 100 + property real cellHeight: 120 + property int numColumns: 4 + + property int count: 0 + function assignMaxCount() { + let c = 0 + for (let i = 0; i < categoryRepeater.count; ++i) + c = Math.max(c, categoryRepeater.itemAt(i)?.count ?? 0) + + root.count = c + } property var currMaterialItem: null property var rootItem: null @@ -23,15 +33,13 @@ HelperWidgets.ScrollView { required property var searchBox - signal unimport(var bundleMat); + signal unimport(var bundleMat) - function closeContextMenu() - { + function closeContextMenu() { ctxMenu.close() } - function expandVisibleSections() - { + function expandVisibleSections() { for (let i = 0; i < categoryRepeater.count; ++i) { let cat = categoryRepeater.itemAt(i) if (cat.visible && !cat.expanded) @@ -56,10 +64,15 @@ HelperWidgets.ScrollView { model: materialsModel delegate: HelperWidgets.Section { + id: section + width: root.width + leftPadding: StudioTheme.Values.sectionPadding + rightPadding: StudioTheme.Values.sectionPadding + topPadding: StudioTheme.Values.sectionPadding + bottomPadding: StudioTheme.Values.sectionPadding + caption: bundleCategoryName - addTopPadding: false - sectionBackgroundColor: "transparent" visible: bundleCategoryVisible && !materialsModel.isEmpty expanded: bundleCategoryExpanded expandOnClick: false @@ -73,14 +86,17 @@ HelperWidgets.ScrollView { bundleCategoryExpanded = true } + property alias count: repeater.count + + onCountChanged: root.assignMaxCount() + Grid { - width: root.width - leftPadding: 5 - rightPadding: 5 - bottomPadding: 5 - columns: root.width / root.cellWidth + width: section.width - section.leftPadding - section.rightPadding + spacing: StudioTheme.Values.sectionGridSpacing + columns: root.numColumns Repeater { + id: repeater model: bundleCategoryMaterials delegate: ContentLibraryMaterial { @@ -89,6 +105,8 @@ HelperWidgets.ScrollView { onShowContextMenu: ctxMenu.popupMenu(modelData) } + + onCountChanged: root.assignMaxCount() } } } diff --git a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryTexture.qml b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryTexture.qml index 6a938ed14d6..74ece1c015b 100644 --- a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryTexture.qml +++ b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryTexture.qml @@ -1,16 +1,15 @@ // Copyright (C) 2022 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 -import QtQuick 2.15 -import QtQuick.Layouts 1.15 -import QtQuickDesignerTheme 1.0 -import HelperWidgets 2.0 +import QtQuick import QtQuick.Controls +import QtQuick.Layouts +import QtQuickDesignerTheme +import HelperWidgets +import StudioTheme as StudioTheme -import StudioTheme 1.0 as StudioTheme - -import WebFetcher 1.0 import ContentLibraryBackend +import WebFetcher Item { id: root @@ -30,8 +29,7 @@ Item { signal showContextMenu() - function statusText() - { + function statusText() { if (root.downloadState === "downloaded") return qsTr("Texture was already downloaded.") if (root.downloadState === "unavailable") @@ -42,16 +40,14 @@ Item { return qsTr("Click to download the texture.") } - function startDownload(message) - { + function startDownload(message) { if (root.downloadState !== "" && root.downloadState !== "failed") return root._startDownload(textureDownloader, message) } - function updateTexture() - { + function updateTexture() { if (root.downloadState !== "downloaded") return @@ -59,8 +55,7 @@ Item { root._startDownload(textureDownloader, qsTr("Updating...")) } - function _startDownload(downloader, message) - { + function _startDownload(downloader, message) { progressBar.visible = true tooltip.visible = false root.progressText = message @@ -144,8 +139,8 @@ Item { font.pixelSize: 12 } } - } // TextureProgressBar - } // Rectangle + } + } Image { id: image @@ -197,7 +192,7 @@ Item { onClicked: { root.startDownload(qsTr("Downloading...")) } - } // IconButton + } IconButton { id: updateButton @@ -233,7 +228,7 @@ Item { scale: updateButton.containsMouse ? 1.2 : 1 } - } // Update IconButton + } Rectangle { id: isNewFlag @@ -282,7 +277,7 @@ Item { visible: false } } - } // Image + } FileDownloader { id: textureDownloader diff --git a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryTexturesView.qml b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryTexturesView.qml index 7471a22cfb7..1fac9f2234e 100644 --- a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryTexturesView.qml +++ b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryTexturesView.qml @@ -14,8 +14,18 @@ HelperWidgets.ScrollView { interactive: !ctxMenu.opened && !ContentLibraryBackend.rootView.isDragging && !HelperWidgets.Controller.contextMenuOpened - readonly property int cellWidth: 100 - readonly property int cellHeight: 100 + property int cellWidth: 100 + property int cellHeight: 100 + property int numColumns: 4 + + property int count: 0 + function assignMaxCount() { + let c = 0 + for (let i = 0; i < categoryRepeater.count; ++i) + c = Math.max(c, categoryRepeater.itemAt(i)?.count ?? 0) + + root.count = c + } property var currMaterialItem: null property var rootItem: null @@ -24,21 +34,20 @@ HelperWidgets.ScrollView { required property var model required property string sectionCategory - signal unimport(var bundleMat); + signal unimport(var bundleMat) - function closeContextMenu() - { + function closeContextMenu() { ctxMenu.close() } - function expandVisibleSections() - { + function expandVisibleSections() { for (let i = 0; i < categoryRepeater.count; ++i) { let cat = categoryRepeater.itemAt(i) if (cat.visible && !cat.expanded) cat.expandSection() } } + Column { ContentLibraryTextureContextMenu { id: ctxMenu @@ -52,10 +61,15 @@ HelperWidgets.ScrollView { model: root.model delegate: HelperWidgets.Section { + id: section + width: root.width + leftPadding: StudioTheme.Values.sectionPadding + rightPadding: StudioTheme.Values.sectionPadding + topPadding: StudioTheme.Values.sectionPadding + bottomPadding: StudioTheme.Values.sectionPadding + caption: bundleCategoryName - addTopPadding: false - sectionBackgroundColor: "transparent" visible: bundleCategoryVisible && !root.model.isEmpty expanded: bundleCategoryExpanded expandOnClick: false @@ -69,15 +83,17 @@ HelperWidgets.ScrollView { bundleCategoryExpanded = true } + property alias count: repeater.count + + onCountChanged: root.assignMaxCount() + Grid { - width: root.width - leftPadding: 5 - rightPadding: 5 - bottomPadding: 5 - spacing: 5 - columns: root.width / root.cellWidth + width: section.width - section.leftPadding - section.rightPadding + spacing: StudioTheme.Values.sectionGridSpacing + columns: root.numColumns Repeater { + id: repeater model: bundleCategoryTextures delegate: ContentLibraryTexture { @@ -86,6 +102,8 @@ HelperWidgets.ScrollView { onShowContextMenu: ctxMenu.popupMenu(modelData) } + + onCountChanged: root.assignMaxCount() } } } diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ScrollView.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ScrollView.qml index f17d2025aa2..abe62052e86 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ScrollView.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ScrollView.qml @@ -16,6 +16,9 @@ Flickable { readonly property bool bothVisible: flickable.verticalScrollBarVisible && flickable.horizontalScrollBarVisible + property bool hideVerticalScrollBar: false + property bool hideHorizontalScrollBar: false + property real temporaryHeight: 0 default property alias content: areaItem.children @@ -36,6 +39,8 @@ Flickable { width: flickable.availableWidth - (verticalScrollBar.isNeeded ? verticalScrollBar.thickness : 0) orientation: Qt.Horizontal + visible: !flickable.hideHorizontalScrollBar + show: (hoverHandler.hovered || flickable.focus || flickable.adsFocus || horizontalScrollBar.inUse || horizontalScrollBar.otherInUse) && horizontalScrollBar.isNeeded @@ -51,6 +56,8 @@ Flickable { height: flickable.availableHeight - (horizontalScrollBar.isNeeded ? horizontalScrollBar.thickness : 0) orientation: Qt.Vertical + visible: !flickable.hideVerticalScrollBar + show: (hoverHandler.hovered || flickable.focus || flickable.adsFocus || horizontalScrollBar.inUse || horizontalScrollBar.otherInUse) && verticalScrollBar.isNeeded diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/Values.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/Values.qml index 0aa9a1b7217..88961be3407 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/Values.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/Values.qml @@ -118,6 +118,9 @@ QtObject { property real sectionHeadHeight: 21 // tab and section property real sectionHeadSpacerHeight: 10 + property real sectionPadding: 10 + property real sectionGridSpacing: 5 + property real controlLabelWidth: 15 property real controlLabelGap: 5 From ea44631300c02df6dcfb5ce59971f4f01adf8b73 Mon Sep 17 00:00:00 2001 From: Henning Gruendl Date: Tue, 10 Oct 2023 16:50:58 +0200 Subject: [PATCH 024/242] QmlDesigner: Adapt spacing to align with the rest Change-Id: I31e57f144c2aed32b50398790458dc8a6db038ed Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Thomas Hartmann --- share/qtcreator/qmldesigner/connectionseditor/Main.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/qtcreator/qmldesigner/connectionseditor/Main.qml b/share/qtcreator/qmldesigner/connectionseditor/Main.qml index 0b3752ac812..5bbb9c8ce61 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/Main.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/Main.qml @@ -22,7 +22,7 @@ Rectangle { Column { id: column anchors.fill: parent - spacing: 8 // TODO + spacing: 5 // TODO Rectangle { id: toolbar From 100ea3406064644ec4430a5415c74ba7b4ad5071 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Tue, 10 Oct 2023 16:09:39 +0200 Subject: [PATCH 025/242] Sqlite: Make SmallString::number locale independent std::to_string is locale dependent. std::to_chars it not. Because some of our supported compiler(libC++) don't support std::to_chars for float we use the QString. Change-Id: I9a8f5d1d9b769faca0a7ac2d0798b91ee6b814d2 Reviewed-by: Burak Hancerli Reviewed-by: Tim Jenssen Reviewed-by: Qt CI Patch Build Bot --- src/libs/utils/smallstring.h | 19 ++++++++++++++++--- .../createtablesqlstatementbuilder-test.cpp | 4 ++-- .../unittests/utils/smallstring-test.cpp | 4 ++-- 3 files changed, 20 insertions(+), 7 deletions(-) diff --git a/src/libs/utils/smallstring.h b/src/libs/utils/smallstring.h index 54318795a71..44f3910dc0f 100644 --- a/src/libs/utils/smallstring.h +++ b/src/libs/utils/smallstring.h @@ -11,6 +11,7 @@ #include "smallstringmemory.h" #include +#include #include #include @@ -562,7 +563,7 @@ public: { // 2 bytes for the sign and because digits10 returns the floor char buffer[std::numeric_limits::digits10 + 2]; - auto result = std::to_chars(buffer, buffer + sizeof(buffer), number, 10); + auto result = std::to_chars(buffer, buffer + sizeof(buffer), number); auto endOfConversionString = result.ptr; return BasicSmallString(buffer, endOfConversionString); } @@ -571,12 +572,24 @@ public: { // 2 bytes for the sign and because digits10 returns the floor char buffer[std::numeric_limits::digits10 + 2]; - auto result = std::to_chars(buffer, buffer + sizeof(buffer), number, 10); + auto result = std::to_chars(buffer, buffer + sizeof(buffer), number); auto endOfConversionString = result.ptr; return BasicSmallString(buffer, endOfConversionString); } - static BasicSmallString number(double number) noexcept { return std::to_string(number); } + static BasicSmallString number(double number) noexcept + { +#if defined(__cpp_lib_to_chars) && (__cpp_lib_to_chars >= 201611L) + // 2 bytes for the sign and because digits10 returns the floor + char buffer[std::numeric_limits::digits10 + 2]; + auto result = std::to_chars(buffer, buffer + sizeof(buffer), number); + auto endOfConversionString = result.ptr; + return BasicSmallString(buffer, endOfConversionString); +#else + QLocale locale{QLocale::Language::C}; + return BasicSmallString{locale.toString(number)}; +#endif + } char &operator[](std::size_t index) noexcept { return *(data() + index); } diff --git a/tests/unit/tests/unittests/sqlite/createtablesqlstatementbuilder-test.cpp b/tests/unit/tests/unittests/sqlite/createtablesqlstatementbuilder-test.cpp index f9ae5bcfdbc..8dcf71c2746 100644 --- a/tests/unit/tests/unittests/sqlite/createtablesqlstatementbuilder-test.cpp +++ b/tests/unit/tests/unittests/sqlite/createtablesqlstatementbuilder-test.cpp @@ -400,7 +400,7 @@ TEST_F(CreateTableSqlStatementBuilder, default_value_float) builder.addColumn("id", ColumnType::Real, {Sqlite::DefaultValue{1.1}}); - ASSERT_THAT(builder.sqlStatement(), "CREATE TABLE test(id REAL DEFAULT 1.100000)"); + ASSERT_THAT(builder.sqlStatement(), "CREATE TABLE test(id REAL DEFAULT 1.1)"); } TEST_F(CreateTableSqlStatementBuilder, default_value_string) @@ -928,7 +928,7 @@ TEST_F(CreateStrictTableSqlStatementBuilder, default_value_float) builder.addColumn("id", StrictColumnType::Real, {Sqlite::DefaultValue{1.1}}); - ASSERT_THAT(builder.sqlStatement(), "CREATE TABLE test(id REAL DEFAULT 1.100000) STRICT"); + ASSERT_THAT(builder.sqlStatement(), "CREATE TABLE test(id REAL DEFAULT 1.1) STRICT"); } TEST_F(CreateStrictTableSqlStatementBuilder, default_value_string) diff --git a/tests/unit/tests/unittests/utils/smallstring-test.cpp b/tests/unit/tests/unittests/utils/smallstring-test.cpp index 17124b1373b..bdcdb44019f 100644 --- a/tests/unit/tests/unittests/utils/smallstring-test.cpp +++ b/tests/unit/tests/unittests/utils/smallstring-test.cpp @@ -1837,8 +1837,8 @@ TEST(SmallString, number_to_string) ASSERT_THAT(SmallString::number(std::numeric_limits::min()), "-2147483648"); ASSERT_THAT(SmallString::number(std::numeric_limits::max()), "9223372036854775807"); ASSERT_THAT(SmallString::number(std::numeric_limits::min()), "-9223372036854775808"); - ASSERT_THAT(SmallString::number(1.2), "1.200000"); - ASSERT_THAT(SmallString::number(-1.2), "-1.200000"); + ASSERT_THAT(SmallString::number(1.2), "1.2"); + ASSERT_THAT(SmallString::number(-1.2), "-1.2"); } TEST(SmallString, string_view_plus_operator) From 5671bd3cd02a1da6b5f0f34150cb6eceb51f47df Mon Sep 17 00:00:00 2001 From: Shrief Gabr Date: Mon, 9 Oct 2023 13:16:59 +0300 Subject: [PATCH 026/242] QmlDesigner: Export collection as CSV Task-number: QDS-10618 Change-Id: Iaae95dd920b453e53c22fdf65c2f6d80652d5be3 Reviewed-by: Mahmoud Badri Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Ali Kianian --- .../collectioneditor/collectiondetails.cpp | 26 +++++++++++++++++++ .../collectioneditor/collectiondetails.h | 2 ++ .../singlecollectionmodel.cpp | 12 +++++++-- .../collectioneditor/singlecollectionmodel.h | 1 + 4 files changed, 39 insertions(+), 2 deletions(-) diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp index b22852e446b..e6aeaf23011 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp @@ -5,6 +5,7 @@ #include #include +#include #include namespace QmlDesigner { @@ -269,4 +270,29 @@ QJsonArray CollectionDetails::getJsonCollection() const return collectionArray; } +QString CollectionDetails::getCsvCollection() const +{ + QString content; + if (d->headers.count() <= 0) + return ""; + + for (const QString &header : std::as_const(d->headers)) + content += header + ','; + + content.back() = '\n'; + + for (const QJsonObject &elementsRow : std::as_const(d->elements)) { + for (const QString &header : std::as_const(d->headers)) { + const QJsonValue &value = elementsRow.value(header); + + if (value.isDouble()) + content += QString::number(value.toDouble()) + ','; + else + content += value.toString() + ','; + } + content.back() = '\n'; + } + + return content; +} } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.h b/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.h index a26ffb558f3..9e08adecbbd 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.h +++ b/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.h @@ -69,6 +69,8 @@ public: void swap(CollectionDetails &other); QJsonArray getJsonCollection() const; + QString getCsvCollection() const; + CollectionDetails &operator=(const CollectionDetails &other); private: diff --git a/src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.cpp b/src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.cpp index fb46bd33ce2..989027d9107 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.cpp @@ -361,7 +361,6 @@ void SingleCollectionModel::loadJsonCollection(const QString &source, const QStr } SourceFormat sourceFormat = jsonFileIsOk ? SourceFormat::Json : SourceFormat::Unknown; - beginResetModel(); m_currentCollection.resetDetails(getJsonHeaders(collectionNodes), elements, sourceFormat); endResetModel(); @@ -401,7 +400,6 @@ void SingleCollectionModel::loadCsvCollection(const QString &source, } SourceFormat sourceFormat = csvFileIsOk ? SourceFormat::Csv : SourceFormat::Unknown; - beginResetModel(); m_currentCollection.resetDetails(headers, elements, sourceFormat); endResetModel(); @@ -436,4 +434,14 @@ bool SingleCollectionModel::saveCollectionAsJson(const QString &collection, cons return false; } +bool SingleCollectionModel::saveCollectionAsCsv(const QString &path, const QString &content) +{ + QFile file(path); + + if (file.open(QFile::WriteOnly) && file.write(content.toUtf8())) + return true; + + return false; +} + } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.h b/src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.h index 75654204460..b1d5d0308af 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.h +++ b/src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.h @@ -74,6 +74,7 @@ private: void loadJsonCollection(const QString &source, const QString &collection); void loadCsvCollection(const QString &source, const QString &collectionName); bool saveCollectionAsJson(const QString &collection, const QJsonArray &content, const QString &source); + bool saveCollectionAsCsv(const QString &path, const QString &content); QHash m_openedCollections; CollectionDetails m_currentCollection; From 26a0199c36baa58e4b5ec1efd93e0d4d7387411c Mon Sep 17 00:00:00 2001 From: Mats Honkamaa Date: Wed, 11 Oct 2023 12:43:58 +0300 Subject: [PATCH 027/242] Doc: Add open project instructions to tutorials MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added a small section that descibes how to open the tutorial project. Task-number: QDS-10127 Change-Id: I1f3de12e1928246ad505391dd24d3f57c21d3be9 Reviewed-by: Johanna Vanhatapio Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Esa Törmänen --- doc/qtdesignstudio/examples/doc/3DsceneTutorial.qdoc | 2 ++ doc/qtdesignstudio/examples/doc/StateTransitions.qdoc | 2 ++ doc/qtdesignstudio/examples/doc/animationTutorial.qdoc | 2 ++ doc/qtdesignstudio/examples/doc/multilanguage.qdoc | 5 ++--- doc/qtdesignstudio/src/run-tutorial-project.qdocinc | 4 ++++ 5 files changed, 12 insertions(+), 3 deletions(-) create mode 100644 doc/qtdesignstudio/src/run-tutorial-project.qdocinc diff --git a/doc/qtdesignstudio/examples/doc/3DsceneTutorial.qdoc b/doc/qtdesignstudio/examples/doc/3DsceneTutorial.qdoc index 78a3490113f..37340999157 100644 --- a/doc/qtdesignstudio/examples/doc/3DsceneTutorial.qdoc +++ b/doc/qtdesignstudio/examples/doc/3DsceneTutorial.qdoc @@ -41,6 +41,8 @@ Besides the 3D model, the 3D scene also has the default camera and the default directional light. + \include run-tutorial-project.qdocinc + \section1 Adding Materials to the 3D Models First, use materials from \uicontrol {Content Library} on the ball bearing. diff --git a/doc/qtdesignstudio/examples/doc/StateTransitions.qdoc b/doc/qtdesignstudio/examples/doc/StateTransitions.qdoc index 2c2729d77ad..ad8ae0005e0 100644 --- a/doc/qtdesignstudio/examples/doc/StateTransitions.qdoc +++ b/doc/qtdesignstudio/examples/doc/StateTransitions.qdoc @@ -30,6 +30,8 @@ All assets you need for this tutorial are included in the Car Demo project. + \include run-tutorial-project.qdocinc + \section1 Creating States First, you create the different states. In this tutorial, you create four diff --git a/doc/qtdesignstudio/examples/doc/animationTutorial.qdoc b/doc/qtdesignstudio/examples/doc/animationTutorial.qdoc index d5b71b91bdb..66fb2009670 100644 --- a/doc/qtdesignstudio/examples/doc/animationTutorial.qdoc +++ b/doc/qtdesignstudio/examples/doc/animationTutorial.qdoc @@ -52,6 +52,8 @@ This tutorial requires that you know the basics of \QDS, see \l{Getting Started}. + \include run-tutorial-project.qdocinc + \section1 Creating a Timeline Animation First, you create an animation where the ball bearing continuously rotates diff --git a/doc/qtdesignstudio/examples/doc/multilanguage.qdoc b/doc/qtdesignstudio/examples/doc/multilanguage.qdoc index 5bb3dd56450..8689960cbb8 100644 --- a/doc/qtdesignstudio/examples/doc/multilanguage.qdoc +++ b/doc/qtdesignstudio/examples/doc/multilanguage.qdoc @@ -20,14 +20,13 @@ \l{https://git.qt.io/public-demos/qtdesign-studio/-/tree/master/tutorial%20projects/multi-language%20tutorial/Loginui2}{here} before you start. - Download the project and open the \e loginui2.qmlproject file in \QDS - to get started. - This project consists of a login page with a couple of text elements. Additionally, you will use a JSON translation file in this tutorial. Download it from \l{https://git.qt.io/public-demos/qtdesign-studio/-/tree/master/tutorial%20projects/multi-language}{here}. + \include run-tutorial-project.qdocinc + \section1 JSON Translation File The JSON translation file you are using in this project has the following diff --git a/doc/qtdesignstudio/src/run-tutorial-project.qdocinc b/doc/qtdesignstudio/src/run-tutorial-project.qdocinc new file mode 100644 index 00000000000..3be68b2d69f --- /dev/null +++ b/doc/qtdesignstudio/src/run-tutorial-project.qdocinc @@ -0,0 +1,4 @@ +\section1 Running the Tutorial Project + +To open the tutorial project in \QDS, open the \e{.qmlproject} file located +in the root folder of the downloaded project. From daa4419d7c3858c0652ead7038fbdff968617c1f Mon Sep 17 00:00:00 2001 From: Tim Jenssen Date: Wed, 11 Oct 2023 18:27:50 +0200 Subject: [PATCH 028/242] QmlDesigner: fix typeName for root node Change-Id: I832c5888228150dcc36fd1002b9d495a23fcc305 Reviewed-by: Marco Bubke --- .../designercore/model/texttomodelmerger.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp index 75a272931de..dac9e2f6924 100644 --- a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp +++ b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp @@ -1240,8 +1240,14 @@ void TextToModelMerger::syncNode(ModelNode &modelNode, return; } - int majorVersion = info.majorVersion(); - int minorVersion = info.minorVersion(); + int majorVersion = -1; + int minorVersion = -1; + + if constexpr (!useProjectStorage()) { + typeName = info.typeName(); + majorVersion = info.majorVersion(); + minorVersion = info.minorVersion(); + } if (modelNode.isRootNode() && !m_rewriterView->allowComponentRoot() && info.isQmlComponent()) { for (AST::UiObjectMemberList *iter = astInitializer->members; iter; iter = iter->next) { From cd4cca907b3142f05f55cab4a155549af990660a Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Thu, 21 Sep 2023 13:00:40 +0200 Subject: [PATCH 029/242] QmlDesigner: Move item library entries to project storage The subcomponent manager is synchronizing some meta files with the item library. The project storage is synchronizing types. Synchronizing both is quite complicated. Moving the the functionality to the project storage removes that synchronization task. Task-number: QDS-10266 Change-Id: Icdf14fbe85d5c4891542acae85ebecf0ba77b45d Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Vikas Pachdha Reviewed-by: --- .../propertyEditorQmlSources/quick.metainfo | 817 +++++++ src/libs/sqlite/sqlitedatabase.cpp | 23 + src/libs/sqlite/sqlitedatabase.h | 3 + src/libs/sqlite/sqlitedatabasebackend.cpp | 21 +- src/libs/sqlite/sqlitedatabasebackend.h | 4 + src/libs/sqlite/sqlitevalue.h | 19 +- src/plugins/qmldesigner/CMakeLists.txt | 29 +- .../components/bindingeditor/actioneditor.cpp | 1 - .../bindingeditor/bindingeditor.cpp | 1 - .../components/bindingeditor/signallist.cpp | 2 - .../componentcore/svgpasteaction.cpp | 4 +- .../components/edit3d/edit3dview.cpp | 40 +- .../components/edit3d/edit3dview.h | 2 +- .../components/edit3d/edit3dwidget.cpp | 35 +- .../components/edit3d/edit3dwidget.h | 2 +- .../components/eventlist/eventlist.cpp | 2 +- .../components/formeditor/dragtool.cpp | 8 +- .../components/integration/designdocument.cpp | 10 +- .../components/integration/designdocument.h | 10 +- .../components/itemlibrary/itemlibraryitem.h | 2 +- .../itemlibrary/itemlibrarymodel.cpp | 15 +- .../itemlibrary/itemlibraryview.cpp | 4 + .../itemlibrary/itemlibrarywidget.cpp | 16 +- .../itemlibrary/itemlibrarywidget.h | 9 +- .../materialeditor/materialeditorview.cpp | 21 +- .../navigator/iconcheckboxitemdelegate.cpp | 1 - .../components/navigator/nameitemdelegate.cpp | 1 - .../navigator/navigatortreemodel.cpp | 2 +- .../navigator/navigatortreeview.cpp | 1 - .../components/navigator/navigatorview.cpp | 8 +- .../components/pathtool/pathtoolview.cpp | 1 - .../textureeditor/textureeditorview.cpp | 1 - .../textureeditor/textureeditorview.h | 2 - .../exceptions/invalidmetainfoexception.cpp | 6 +- .../imagecache/imagecachecollector.cpp | 4 +- .../designercore/include/abstractview.h | 2 +- .../designercore/include/itemlibraryentry.h | 93 + .../designercore/include/itemlibraryinfo.h | 71 +- .../designercore/include/metainfo.h | 13 +- .../designercore/include/metainforeader.h | 14 +- .../qmldesigner/designercore/include/model.h | 16 +- .../designercore/include/nodehints.h | 3 +- .../designercore/include/nodemetainfo.h | 25 +- .../include/subcomponentmanager.h | 23 +- .../instances/nodeinstanceview.cpp | 1 - .../metainfo/itemlibraryentry.cpp | 306 +++ .../designercore/metainfo/itemlibraryinfo.cpp | 270 +- .../designercore/metainfo/nodehints.cpp | 123 +- .../designercore/metainfo/nodemetainfo.cpp | 217 +- .../designercore/model/abstractview.cpp | 6 - .../qmldesigner/designercore/model/model.cpp | 118 +- .../qmldesigner/designercore/model/model_p.h | 17 +- .../designercore/model/modelnode.cpp | 10 + .../designercore/model/propertyparser.cpp | 1 - .../designercore/model/qml3dnode.cpp | 2 - .../designercore/model/qmlchangeset.cpp | 1 - .../designercore/model/qmlconnections.cpp | 1 - .../designercore/model/qmlitemnode.cpp | 2 - .../designercore/model/qmlstate.cpp | 1 - .../designercore/model/qmltimeline.cpp | 1 - .../model/qmltimelinekeyframegroup.cpp | 1 - .../designercore/model/qmlvisualnode.cpp | 11 +- .../projectstorage/commontypecache.h | 24 +- .../projectstorage/projectstorage.h | 368 ++- .../projectstorageexceptions.cpp | 5 + .../projectstorage/projectstorageexceptions.h | 6 + .../projectstorage/projectstorageinfotypes.h | 190 +- .../projectstorage/projectstorageinterface.h | 12 +- .../projectstorage/projectstorageobserver.h | 15 + .../projectstorage/projectstoragetypes.h | 58 +- .../projectstorage/projectstorageupdater.cpp | 12 +- .../projectstorage/projectstorageupdater.h | 3 + .../projectstorage/qmltypesparser.cpp | 16 +- .../projectstorage/storagecache.h | 12 +- .../projectstorage/typeannotationreader.cpp | 490 ++++ .../projectstorage/typeannotationreader.h | 129 + src/plugins/qmldesigner/qmldesignerplugin.cpp | 8 +- .../qml/qmldesigner/coretests/CMakeLists.txt | 1 + .../qmldesigner/coretests/tst_testcore.cpp | 15 +- .../qml/qmldesigner/coretests/tst_testcore.h | 2 + tests/unit/tests/matchers/CMakeLists.txt | 1 + .../tests/matchers/projectstorage-matcher.h | 55 + .../tests/matchers/strippedstring-matcher.h | 11 + tests/unit/tests/mocks/CMakeLists.txt | 1 + tests/unit/tests/mocks/projectstoragemock.cpp | 34 +- tests/unit/tests/mocks/projectstoragemock.h | 43 +- .../tests/mocks/projectstorageobservermock.h | 14 + .../tests/printers/gtest-creator-printing.cpp | 153 +- .../tests/printers/gtest-creator-printing.h | 15 +- tests/unit/tests/printers/gtest-qt-printing.h | 19 + .../tests/testdesignercore/CMakeLists.txt | 11 +- .../unittests/metainfo/nodemetainfo-test.cpp | 603 ++++- .../metainfo/propertymetainfo-test.cpp | 12 +- .../unit/tests/unittests/model/model-test.cpp | 90 +- .../tests/unittests/model/modelutils-test.cpp | 24 +- .../unittests/projectstorage/CMakeLists.txt | 1 + .../projectstorage/projectstorage-test.cpp | 2178 ++++++++++------- .../projectstoragepathwatcher-test.cpp | 23 +- .../projectstorageupdater-test.cpp | 196 +- .../projectstorage/qmltypesparser-test.cpp | 47 +- .../typeannotationreader-test.cpp | 758 ++++++ 101 files changed, 6498 insertions(+), 1633 deletions(-) create mode 100644 share/qtcreator/qmldesigner/propertyEditorQmlSources/quick.metainfo create mode 100644 src/plugins/qmldesigner/designercore/include/itemlibraryentry.h create mode 100644 src/plugins/qmldesigner/designercore/metainfo/itemlibraryentry.cpp create mode 100644 src/plugins/qmldesigner/designercore/projectstorage/projectstorageobserver.h create mode 100644 src/plugins/qmldesigner/designercore/projectstorage/typeannotationreader.cpp create mode 100644 src/plugins/qmldesigner/designercore/projectstorage/typeannotationreader.h create mode 100644 tests/unit/tests/matchers/projectstorage-matcher.h create mode 100644 tests/unit/tests/mocks/projectstorageobservermock.h create mode 100644 tests/unit/tests/unittests/projectstorage/typeannotationreader-test.cpp diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/quick.metainfo b/share/qtcreator/qmldesigner/propertyEditorQmlSources/quick.metainfo new file mode 100644 index 00000000000..f1aeaa9ebbd --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/quick.metainfo @@ -0,0 +1,817 @@ +MetaInfo { + + Type { + name: "QtQuick.Item" + icon: ":/qtquickplugin/images/item-icon16.png" + + Hints { + visibleNonDefaultProperties: "layer.effect" + } + + ItemLibraryEntry { + name: "Item" + category: "a.Qt Quick - Basic" + libraryIcon: ":/qtquickplugin/images/item-icon.png" + version: "2.0" + + Property { name: "width"; type: "int"; value: 200; } + Property { name: "height"; type: "int"; value: 200; } + toolTip: qsTr("Groups several visual items.") + } + } + + Type { + name: "QtQuick.Rectangle" + icon: ":/qtquickplugin/images/rect-icon16.png" + + ItemLibraryEntry { + name: "Rectangle" + category: "a.Qt Quick - Basic" + libraryIcon: ":/qtquickplugin/images/rect-icon.png" + version: "2.0" + + Property { name: "width"; type: "int"; value: 200; } + Property { name: "height"; type: "int"; value: 200; } + Property { name: "color"; type: "QColor"; value: "#ffffff"; } + toolTip: qsTr("A rectangle with an optional border.") + } + } + + Type { + name: "QtQuick.Text" + icon: ":/qtquickplugin/images/text-icon16.png" + + ItemLibraryEntry { + name: "Text" + category: "a.Qt Quick - Basic" + libraryIcon: ":/qtquickplugin/images/text-icon.png" + version: "2.0" + + Property { name: "font.pixelSize"; type: "int"; value: 12; } + Property { name: "text"; type: "binding"; value: "qsTr(\"Text\")"; } + toolTip: qsTr("A read-only text label.") + } + } + + Type { + name: "QtQuick.TextEdit" + icon: ":/qtquickplugin/images/text-edit-icon16.png" + + ItemLibraryEntry { + name: "Text Edit" + category: "a.Qt Quick - Basic" + libraryIcon: ":/qtquickplugin/images/text-edit-icon.png" + version: "2.0" + + Property { name: "width"; type: "int"; value: 80; } + Property { name: "height"; type: "int"; value: 20; } + Property { name: "font.pixelSize"; type: "int"; value: 12; } + Property { name: "text"; type: "binding"; value: "qsTr(\"Text Edit\")"; } + toolTip: qsTr("A multi-line block of editable text.") + } + } + + Type { + name: "QtQuick.TextInput" + icon: ":/qtquickplugin/images/text-input-icon16.png" + + ItemLibraryEntry { + name: "Text Input" + category: "a.Qt Quick - Basic" + libraryIcon: ":/qtquickplugin/images/text-input-icon.png" + version: "2.0" + + Property { name: "width"; type: "int"; value: 80; } + Property { name: "height"; type: "int"; value: 20; } + Property { name: "font.pixelSize"; type: "int"; value: 12; } + Property { name: "text"; type: "binding"; value: "qsTr(\"Text Input\")"; } + toolTip: qsTr("An editable line of text.") + } + } + + Type { + name: "QtQuick.MouseArea" + icon: ":/qtquickplugin/images/mouse-area-icon16.png" + + ItemLibraryEntry { + name: "Mouse Area" + category: "a.Qt Quick - Basic" + libraryIcon: ":/qtquickplugin/images/mouse-area-icon.png" + version: "2.0" + + Property { name: "width"; type: "int"; value: 100; } + Property { name: "height"; type: "int"; value: 100; } + toolTip: qsTr("An area with mouse functionality.") + } + } + + Type { + name: "QtQuick.Image" + icon: ":/qtquickplugin/images/image-icon16.png" + + ItemLibraryEntry { + name: "Image" + category: "a.Qt Quick - Basic" + libraryIcon: ":/qtquickplugin/images/image-icon.png" + version: "2.0" + + Property { name: "width"; type: "int"; value: 100; } + Property { name: "height"; type: "int"; value: 100; } + Property { name: "source"; type: "QUrl"; value:"qrc:/qtquickplugin/images/template_image.png"; } + Property { name: "fillMode"; type: "enum"; value: "Image.PreserveAspectFit"; } + toolTip: qsTr("Displays an image.") + } + } + + Type { + name: "QtQuick.AnimatedImage" + icon: ":/qtquickplugin/images/animated-image-icon16.png" + + ItemLibraryEntry { + name: "Animated Image" + category: "a.Qt Quick - Basic" + libraryIcon: ":/qtquickplugin/images/animated-image-icon.png" + version: "2.0" + + Property { name: "width"; type: "int"; value: 100; } + Property { name: "height"; type: "int"; value: 100; } + Property { name: "source"; type: "QUrl"; value:"qrc:/qtquickplugin/images/template_image.png"; } + toolTip: qsTr("Animates a series of images.") + } + } + + Type { + name: "QtQuick.BorderImage" + icon: ":/qtquickplugin/images/border-image-icon16.png" + + ItemLibraryEntry { + name: "Border Image" + category: "a.Qt Quick - Basic" + libraryIcon: ":/qtquickplugin/images/border-image-icon.png" + version: "2.0" + + Property { name: "width"; type: "int"; value: 100; } + Property { name: "height"; type: "int"; value: 100; } + Property { name: "source"; type: "QUrl"; value:"qrc:/qtquickplugin/images/template_image.png"; } + toolTip: qsTr("A responsive border based on an image.") + } + } + + Type { + name: "QtQuick.Flickable" + icon: ":/qtquickplugin/images/flickable-icon16.png" + + ItemLibraryEntry { + name: "Flickable" + category: "a.Qt Quick - Basic" + libraryIcon: ":/qtquickplugin/images/flickable-icon.png" + version: "2.0" + + Property { name: "width"; type: "int"; value: 300; } + Property { name: "height"; type: "int"; value: 300; } + toolTip: qsTr("An area for keeping dragable objects.") + } + } + + Type { + name: "QtQuick.GridView" + icon: ":/qtquickplugin/images/gridview-icon16.png" + + ItemLibraryEntry { + name: "Grid View" + category: "b.Qt Quick - Views" + libraryIcon: ":/qtquickplugin/images/gridview-icon.png" + version: "2.0" + + QmlSource { source: ":/qtquickplugin/source/gridviewv2.qml" } + toolTip: qsTr("Organizes dynamic data sets in a grid.") + } + } + + Type { + name: "QtQuick.ListView" + icon: ":/qtquickplugin/images/listview-icon16.png" + + ItemLibraryEntry { + name: "List View" + category: "b.Qt Quick - Views" + libraryIcon: ":/qtquickplugin/images/listview-icon.png" + version: "2.0" + + QmlSource { source: ":/qtquickplugin/source/listviewv2.qml" } + toolTip: qsTr("Organizes dynamic data sets in a list.") + } + } + + Type { + name: "QtQuick.PathView" + icon: ":/qtquickplugin/images/pathview-icon16.png" + + ItemLibraryEntry { + name: "Path View" + category: "b.Qt Quick - Views" + libraryIcon: ":/qtquickplugin/images/pathview-icon.png" + version: "2.0" + + QmlSource { source: ":/qtquickplugin/source/pathviewv2.qml" } + toolTip: qsTr("Organizes dynamic data sets along a path.") + } + } + + Type { + name: "QtQuick.FocusScope" + icon: ":/qtquickplugin/images/focusscope-icon16.png" + + ItemLibraryEntry { + name: "Focus Scope" + category: "a.Qt Quick - Basic" + libraryIcon: ":/qtquickplugin/images/focusscope-icon.png" + version: "2.0" + + Property { name: "width"; type: "int"; value: 100; } + Property { name: "height"; type: "int"; value: 100; } + toolTip: qsTr("A scope to focus on a specific text element.") + } + } + + Type { + name: "QtQuick.Column" + icon: ":/qtquickplugin/images/column-positioner-icon-16px.png" + + ItemLibraryEntry { + name: "Column" + category: "c.Qt Quick - Positioner" + libraryIcon: ":/qtquickplugin/images/column-positioner-icon.png" + version: "2.0" + + Property { name: "width"; type: "int"; value: 200; } + Property { name: "height"; type: "int"; value: 400; } + + toolTip: qsTr("Organizes items in a column.") + } + } + + Type { + name: "QtQuick.Row" + icon: ":/qtquickplugin/images/row-positioner-icon-16px.png" + + ItemLibraryEntry { + name: "Row" + category: "c.Qt Quick - Positioner" + libraryIcon: ":/qtquickplugin/images/row-positioner-icon.png" + version: "2.0" + toolTip: qsTr("Organizes items in a row.") + + Property { name: "width"; type: "int"; value: 200; } + Property { name: "height"; type: "int"; value: 400; } + } + } + + Type { + name: "QtQuick.Grid" + icon: ":/qtquickplugin/images/grid-positioner-icon-16px.png" + + ItemLibraryEntry { + name: "Grid" + category: "c.Qt Quick - Positioner" + libraryIcon: ":/qtquickplugin/images/grid-positioner-icon.png" + version: "2.0" + + Property { name: "width"; type: "int"; value: 400; } + Property { name: "height"; type: "int"; value: 400; } + toolTip: qsTr("Organizes items in a fixed grid.") + } + } + + Type { + name: "QtQuick.Flow" + icon: ":/qtquickplugin/images/flow-positioner-icon-16px.png" + + ItemLibraryEntry { + name: "Flow" + category: "c.Qt Quick - Positioner" + libraryIcon: ":/qtquickplugin/images/flow-positioner-icon.png" + version: "2.0" + + Property { name: "width"; type: "int"; value: 400; } + Property { name: "height"; type: "int"; value: 400; } + toolTip: qsTr("Organizes items in free-flowing rows.") + } + } + + Type { + name: "QtQuick.Timeline.Timeline" + icon: ":/qtquickplugin/images/timeline-16px.png" + + Hints { + visibleNonDefaultProperties: "animations" + visibleInLibrary: false + visibleInNavigator: true + } + ItemLibraryEntry { + name: "Timeline" + category: "none" + version: "1.0" + } + } + + Type { + name: "QtQuick.Timeline.TimelineAnimation" + icon: ":/qtquickplugin/images/timeline-animation-16px.png" + + Hints { + visibleInLibrary: false + visibleInNavigator: true + } + ItemLibraryEntry { + name: "Animation" + category: "none" + version: "1.0" + } + } + + Type { + name: "QtQuick.Timeline.Keyframe" + icon: ":/qtquickplugin/images/keyframe-16px.png" + + ItemLibraryEntry { + name: "Keyframe" + category: "none" + version: "1.0" + requiredImport: "none" + } + } + + Type { + name: "QtQuick.Timeline.KeyframeGroup" + icon: ":/qtquickplugin/images/keyframe-16px.png" + + ItemLibraryEntry { + name: "KeyframeGroup" + category: "none" + version: "1.0" + requiredImport: "none" + } + } + + Type { + name: "QtQuick.PropertyAnimation" + icon: ":/qtquickplugin/images/item-icon16.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeContainer: false + } + + ItemLibraryEntry { + name: "Property Animation" + category: "d.Qt Quick - Animation" + libraryIcon: ":/qtquickplugin/images/item-icon.png" + version: "2.0" + toolTip: qsTr("Animates changes in property values.") + } + } + + Type { + name: "QtQuick.PauseAnimation" + icon: ":/qtquickplugin/images/item-icon16.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeContainer: false + } + + ItemLibraryEntry { + name: "Pause Animation" + category: "d.Qt Quick - Animation" + libraryIcon: ":/qtquickplugin/images/item-icon.png" + version: "2.0" + toolTip: qsTr("Provides a pause between animations.") + } + } + + Type { + name: "QtQuick.SequentialAnimation" + icon: ":/qtquickplugin/images/item-icon16.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + } + + ItemLibraryEntry { + name: "Sequential Animation" + category: "d.Qt Quick - Animation" + libraryIcon: ":/qtquickplugin/images/item-icon.png" + version: "2.0" + toolTip: qsTr("Runs animations one after the other.") + } + } + + Type { + name: "QtQuick.ParallelAnimation" + icon: ":/qtquickplugin/images/item-icon16.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + } + + ItemLibraryEntry { + name: "Parallel Animation" + category: "d.Qt Quick - Animation" + libraryIcon: ":/qtquickplugin/images/item-icon.png" + version: "2.0" + toolTip: qsTr("Runs animations together at the same time.") + } + } + + Type { + name: "QtQuick.PropertyAction" + icon: ":/qtquickplugin/images/item-icon16.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeContainer: false + } + + ItemLibraryEntry { + name: "Property Action" + category: "d.Qt Quick - Animation" + libraryIcon: ":/qtquickplugin/images/item-icon.png" + version: "2.0" + toolTip: qsTr("Provides an immediate property change during animations.") + } + } + + Type { + name: "QtQuick.ScriptAction" + icon: ":/qtquickplugin/images/item-icon16.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeContainer: false + } + + ItemLibraryEntry { + name: "Script Action" + category: "d.Qt Quick - Animation" + libraryIcon: ":/qtquickplugin/images/item-icon.png" + version: "2.0" + toolTip: qsTr("Runs a script during animation.") + } + } + + Type { + name: "QtQuick.ColorAnimation" + icon: ":/qtquickplugin/images/item-icon16.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeContainer: false + } + + ItemLibraryEntry { + name: "Color Animation" + category: "d.Qt Quick - Animation" + libraryIcon: ":/qtquickplugin/images/item-icon.png" + version: "2.0" + toolTip: qsTr("Animates the color of an item.") + } + } + + Type { + name: "QtQuick.NumberAnimation" + icon: ":/qtquickplugin/images/item-icon16.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeContainer: false + } + + ItemLibraryEntry { + name: "Number Animation" + category: "d.Qt Quick - Animation" + libraryIcon: ":/qtquickplugin/images/item-icon.png" + version: "2.0" + Property { name: "to"; type: "int"; value: 0; } + Property { name: "from"; type: "int"; value: 0; } + toolTip: qsTr("Animates a numerical property of an item.") + } + } + + Type { + name: "QtQml.Timer" + icon: ":/qtquickplugin/images/timer-16px.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeContainer: false + } + + ItemLibraryEntry { + name: "Timer" + category: "d.Qt Quick - Animation" + libraryIcon: ":/qtquickplugin/images/timer-24px.png" + version: "2.0" + toolTip: qsTr(" Triggers an action at a given time.") + } + } + + Type { + name: "QML.Component" + icon: ":/qtquickplugin/images/component-icon16.png" + + Hints { + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + } + + ItemLibraryEntry { + name: "Component" + category: "e.Qt Quick - Instancers" + libraryIcon: ":/qtquickplugin/images/component-icon.png" + version: "1.0" + + QmlSource { source: ":/qtquickplugin/source/component.qml" } + toolTip: qsTr("Allows you to define components inline, within a QML document.") + } + } + + Type { + name: "QML.Component" + icon: ":/qtquickplugin/images/component-icon16.png" + + Hints { + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + } + + ItemLibraryEntry { + name: "Component 3D" + category: "Instancers" + libraryIcon: ":/qtquickplugin/images/component-icon.png" + version: "1.0" + requiredImport: "QtQuick3D" + + QmlSource { source: ":/qtquickplugin/source/component3d.qml" } + toolTip: qsTr("Allows you to define 3D components inline, within a QML document.") + } + } + + Type { + name: "QtQuick.Loader" + icon: ":/qtquickplugin/images/loader-icon16.png" + + ItemLibraryEntry { + name: "Loader" + category: "e.Qt Quick - Instancers" + libraryIcon: ":/qtquickplugin/images/loader-icon.png" + version: "2.0" + Property { name: "width"; type: "int"; value: 200; } + Property { name: "height"; type: "int"; value: 200; } + toolTip: qsTr("Allows you to load components dynamically.") + } + } + + Type { + name: "QtQuick.Repeater" + icon: ":/qtquickplugin/images/repeater-icon16.png" + + Hints { + canBeDroppedInFormEditor: false + hasFormEditorItem: false + } + + ItemLibraryEntry { + name: "Repeater" + category: "e.Qt Quick - Instancers" + libraryIcon: ":/qtquickplugin/images/repeater-icon.png" + version: "2.0" + toolTip: qsTr("Creates a number of copies of the same item.") + } + } + + Type { + name: "QtMultimedia.MediaPlayer" + icon: ":/qtquickplugin/images/media-player-16px.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeContainer: false + } + + ItemLibraryEntry { + name: "Media Player" + category: "f.Qt Quick - Multimedia" + libraryIcon: ":/qtquickplugin/images/media-player-24px.png" + version: "6.0" + requiredImport: "QtMultimedia" + } + } + + Type { + name: "QtMultimedia.AudioOutput" + icon: ":/qtquickplugin/images/audio-output-16px.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeContainer: false + } + + ItemLibraryEntry { + name: "Audio Output" + category: "f.Qt Quick - Multimedia" + libraryIcon: ":/qtquickplugin/images/audio-output-24px.png" + version: "6.0" + requiredImport: "QtMultimedia" + } + } + + Type { + name: "QtMultimedia.VideoOutput" + icon: ":/qtquickplugin/images/video-output-16px.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeContainer: false + } + + ItemLibraryEntry { + name: "Video Output" + category: "f.Qt Quick - Multimedia" + libraryIcon: ":/qtquickplugin/images/video-output-24px.png" + version: "6.0" + requiredImport: "QtMultimedia" + } + } + + Type { + name: "QtMultimedia.Video" + icon: ":/qtquickplugin/images/video-16px.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: true + canBeContainer: false + } + + ItemLibraryEntry { + name: "Video" + category: "f.Qt Quick - Multimedia" + libraryIcon: ":/qtquickplugin/images/video-24px.png" + version: "6.0" + requiredImport: "QtMultimedia" + + Property { name: "width"; type: "int"; value: 200; } + Property { name: "height"; type: "int"; value: 200; } + } + } + + Type { + name: "QtQuick3D.SpatialAudio.AmbientSound" + icon: ":/qtquickplugin/images/ambient-sound-16.png" + + Hints { + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeDroppedInView3D: false + canBeContainer: false + } + + ItemLibraryEntry { + name: "Ambient Sound" + category: "Spatial Audio" + libraryIcon: ":/qtquickplugin/images/ambient-sound-24.png" + version: "6.0" + requiredImport: "QtQuick3D.SpatialAudio" + toolTip: qsTr("An ambient background sound.") + } + } + + Type { + name: "QtQuick3D.SpatialAudio.AudioEngine" + icon: ":/qtquickplugin/images/audio-engine-16.png" + + Hints { + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeDroppedInView3D: false + canBeContainer: false + } + + ItemLibraryEntry { + name: "Audio Engine" + category: "Spatial Audio" + libraryIcon: ":/qtquickplugin/images/audio-engine-24.png" + version: "6.0" + requiredImport: "QtQuick3D.SpatialAudio" + toolTip: qsTr("Manages sound objects inside a 3D scene.") + } + } + + Type { + name: "QtQuick3D.SpatialAudio.AudioListener" + icon: ":/qtquickplugin/images/audio-listener-16.png" + + Hints { + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeDroppedInView3D: true + } + + ItemLibraryEntry { + name: "Audio Listener" + category: "Spatial Audio" + libraryIcon: ":/qtquickplugin/images/audio-listener-24.png" + version: "6.0" + requiredImport: "QtQuick3D.SpatialAudio" + toolTip: qsTr("Sets the position and orientation of listening.") + } + } + + Type { + name: "QtQuick3D.SpatialAudio.AudioRoom" + icon: ":/qtquickplugin/images/audio-room-16.png" + + Hints { + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeDroppedInView3D: true + } + + ItemLibraryEntry { + name: "Audio Room" + category: "Spatial Audio" + libraryIcon: ":/qtquickplugin/images/audio-room-24.png" + version: "6.0" + requiredImport: "QtQuick3D.SpatialAudio" + toolTip: qsTr("Sets up a room for the spatial audio engine.") + } + } + + Type { + name: "QtQuick3D.SpatialAudio.SpatialSound" + icon: ":/qtquickplugin/images/spatial-audio-16.png" + + Hints { + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeDroppedInView3D: true + } + + ItemLibraryEntry { + name: "Spatial Sound" + category: "Spatial Audio" + libraryIcon: ":/qtquickplugin/images/spatial-audio-24.png" + version: "6.0" + requiredImport: "QtQuick3D.SpatialAudio" + toolTip: qsTr("A sound object in 3D space.") + } + } + + Type { + name: "QtQuick3D.BakedLightmap" + icon: ":/ItemLibrary/images/item-default-icon.png" + + Hints { + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeDroppedInView3D: false + } + + ItemLibraryEntry { + name: "Baked Lightmap" + category: "Components" + libraryIcon: ":/ItemLibrary/images/item-default-icon.png" + version: "6.5" + requiredImport: "QtQuick3D" + toolTip: qsTr("An object to specify details about baked lightmap of a model.") + + Property { name: "loadPrefix"; type: "string"; value: "lightmaps"; } + } + } +} diff --git a/src/libs/sqlite/sqlitedatabase.cpp b/src/libs/sqlite/sqlitedatabase.cpp index bb92943776b..12a122030c4 100644 --- a/src/libs/sqlite/sqlitedatabase.cpp +++ b/src/libs/sqlite/sqlitedatabase.cpp @@ -215,6 +215,29 @@ void Database::sessionRollback() m_statements->rollbackBegin.execute(); } +void Database::resetDatabaseForTestsOnly() +{ + m_databaseBackend.resetDatabaseForTestsOnly(); + setIsInitialized(false); +} + +void Database::clearAllTablesForTestsOnly() +{ + m_databaseBackend.disableForeignKeys(); + { + Sqlite::ImmediateTransaction transaction{*this}; + + ReadStatement<1> tablesStatement{"SELECT name FROM sqlite_schema WHERE type='table'", *this}; + auto tables = tablesStatement.template values(); + for (const auto &table : tables) + execute("DELETE FROM " + table); + + transaction.commit(); + } + + m_databaseBackend.enableForeignKeys(); +} + DatabaseBackend &Database::backend() { return m_databaseBackend; diff --git a/src/libs/sqlite/sqlitedatabase.h b/src/libs/sqlite/sqlitedatabase.h index 70be8f43057..0db256c8162 100644 --- a/src/libs/sqlite/sqlitedatabase.h +++ b/src/libs/sqlite/sqlitedatabase.h @@ -161,6 +161,9 @@ public: void sessionCommit() override; void sessionRollback() override; + void resetDatabaseForTestsOnly(); + void clearAllTablesForTestsOnly(); + private: void initializeTables(); void registerTransactionStatements(); diff --git a/src/libs/sqlite/sqlitedatabasebackend.cpp b/src/libs/sqlite/sqlitedatabasebackend.cpp index 1cca1f61791..2f55853dd03 100644 --- a/src/libs/sqlite/sqlitedatabasebackend.cpp +++ b/src/libs/sqlite/sqlitedatabasebackend.cpp @@ -248,11 +248,30 @@ int busyHandlerCallback(void *userData, int counter) void DatabaseBackend::registerBusyHandler() { - int resultCode = sqlite3_busy_handler(sqliteDatabaseHandle(), &busyHandlerCallback, &m_busyHandler); + int resultCode = sqlite3_busy_handler(sqliteDatabaseHandle(), + &busyHandlerCallback, + &m_busyHandler); checkIfBusyTimeoutWasSet(resultCode); } +void DatabaseBackend::resetDatabaseForTestsOnly() +{ + sqlite3_db_config(sqliteDatabaseHandle(), SQLITE_DBCONFIG_RESET_DATABASE, 1, 0); + sqlite3_exec(sqliteDatabaseHandle(), "VACUUM", nullptr, nullptr, nullptr); + sqlite3_db_config(sqliteDatabaseHandle(), SQLITE_DBCONFIG_RESET_DATABASE, 0, 0); +} + +void DatabaseBackend::enableForeignKeys() +{ + sqlite3_exec(sqliteDatabaseHandle(), "PRAGMA foreign_keys=ON", nullptr, nullptr, nullptr); +} + +void DatabaseBackend::disableForeignKeys() +{ + sqlite3_exec(sqliteDatabaseHandle(), "PRAGMA foreign_keys=OFF", nullptr, nullptr, nullptr); +} + void DatabaseBackend::checkForOpenDatabaseWhichCanBeClosed() { if (m_databaseHandle == nullptr) diff --git a/src/libs/sqlite/sqlitedatabasebackend.h b/src/libs/sqlite/sqlitedatabasebackend.h index 9a1caa92d67..39d690c4f73 100644 --- a/src/libs/sqlite/sqlitedatabasebackend.h +++ b/src/libs/sqlite/sqlitedatabasebackend.h @@ -84,6 +84,10 @@ public: void registerBusyHandler(); + void resetDatabaseForTestsOnly(); + void enableForeignKeys(); + void disableForeignKeys(); + protected: bool databaseIsOpen() const; diff --git a/src/libs/sqlite/sqlitevalue.h b/src/libs/sqlite/sqlitevalue.h index 49ab76d89f0..fe576f3fec9 100644 --- a/src/libs/sqlite/sqlitevalue.h +++ b/src/libs/sqlite/sqlitevalue.h @@ -234,9 +234,9 @@ public: {} template - static ValueView create(Type &&value) + static ValueView create(Type &&value_) { - return ValueView{ValueBase{value}}; + return ValueView{ValueBase{value_}}; } }; @@ -267,6 +267,21 @@ public: : ValueBase(Utils::SmallStringView(value)) {} + explicit Value(long long value) + : ValueBase(value) + {} + explicit Value(int value) + : ValueBase(static_cast(value)) + {} + + explicit Value(uint value) + : ValueBase(static_cast(value)) + {} + + explicit Value(double value) + : ValueBase(value) + {} + explicit Value(const QString &value) : ValueBase(VariantType{Utils::SmallString(value)}) {} diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index d2b091d1e4c..5b620323389 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -192,18 +192,26 @@ extend_qtc_library(QmlDesignerCore bytearraymodifier.h componenttextmodifier.h forwardview.h - itemlibraryinfo.h - metainforeader.h + itemlibraryentry.h model.h nodehints.h plaintexteditmodifier.h nodeinstanceview.h propertyparser.h rewriterview.h - subcomponentmanager.h textmodifier.h ) +extend_qtc_library(QmlDesignerCore + CONDITION NOT USE_PROJECTSTORAGE + SOURCES_PREFIX ${CMAKE_CURRENT_LIST_DIR}/designercore/include + SOURCES + itemlibraryinfo.h + metainforeader.h + subcomponentmanager.h + metainfo.h +) + extend_qtc_library(QmlDesignerCore SOURCES_PROPERTIES SKIP_AUTOGEN ON SOURCES_PREFIX ${CMAKE_CURRENT_LIST_DIR}/designercore/include @@ -233,7 +241,6 @@ extend_qtc_library(QmlDesignerCore invalidreparentingexception.h invalidslideindexexception.h mathutils.h - metainfo.h modelfwd.h modelmerger.h modelnode.h @@ -273,15 +280,23 @@ extend_qtc_library(QmlDesignerCore ${CMAKE_CURRENT_LIST_DIR}/designercore/metainfo SOURCES_PREFIX ${CMAKE_CURRENT_LIST_DIR}/designercore/metainfo DEFINES SHARE_QML_PATH="${CMAKE_CURRENT_SOURCE_DIR}/../../../share/qtcreator/qmldesigner" + SOURCES + itemlibraryentry.cpp + nodehints.cpp + nodemetainfo.cpp +) + +extend_qtc_library(QmlDesignerCore + CONDITION NOT USE_PROJECTSTORAGE + SOURCES_PREFIX ${CMAKE_CURRENT_LIST_DIR}/designercore/metainfo SOURCES itemlibraryinfo.cpp metainfo.cpp metainforeader.cpp - nodehints.cpp - nodemetainfo.cpp subcomponentmanager.cpp ) + extend_qtc_library(QmlDesignerCore PUBLIC_INCLUDES ${CMAKE_CURRENT_LIST_DIR}/designercore/instances SOURCES_PREFIX ${CMAKE_CURRENT_LIST_DIR}/designercore/instances @@ -405,6 +420,7 @@ extend_qtc_library(QmlDesignerCore projectstorageinterface.h projectstoragefwd.h projectstorageinfotypes.h + projectstorageobserver.h projectstoragepathwatcher.h projectstoragepathwatcherinterface.h projectstoragepathwatchernotifierinterface.h @@ -422,6 +438,7 @@ extend_qtc_library(QmlDesignerCore storagecache.h storagecacheentry.h storagecachefwd.h + typeannotationreader.cpp typeannotationreader.h qmldocumentparserinterface.h qmltypesparserinterface.h qmltypesparser.cpp qmltypesparser.h diff --git a/src/plugins/qmldesigner/components/bindingeditor/actioneditor.cpp b/src/plugins/qmldesigner/components/bindingeditor/actioneditor.cpp index 8480115a3b7..256bfac1e9f 100644 --- a/src/plugins/qmldesigner/components/bindingeditor/actioneditor.cpp +++ b/src/plugins/qmldesigner/components/bindingeditor/actioneditor.cpp @@ -8,7 +8,6 @@ #include #include -#include #include #include #include diff --git a/src/plugins/qmldesigner/components/bindingeditor/bindingeditor.cpp b/src/plugins/qmldesigner/components/bindingeditor/bindingeditor.cpp index 76f5cc7e211..b605a77191b 100644 --- a/src/plugins/qmldesigner/components/bindingeditor/bindingeditor.cpp +++ b/src/plugins/qmldesigner/components/bindingeditor/bindingeditor.cpp @@ -9,7 +9,6 @@ #include #include -#include #include #include #include diff --git a/src/plugins/qmldesigner/components/bindingeditor/signallist.cpp b/src/plugins/qmldesigner/components/bindingeditor/signallist.cpp index 82c37aa3c41..fa175123c79 100644 --- a/src/plugins/qmldesigner/components/bindingeditor/signallist.cpp +++ b/src/plugins/qmldesigner/components/bindingeditor/signallist.cpp @@ -8,8 +8,6 @@ #include #include -#include - #include #include #include diff --git a/src/plugins/qmldesigner/components/componentcore/svgpasteaction.cpp b/src/plugins/qmldesigner/components/componentcore/svgpasteaction.cpp index 738b1affedd..7bcd6050869 100644 --- a/src/plugins/qmldesigner/components/componentcore/svgpasteaction.cpp +++ b/src/plugins/qmldesigner/components/componentcore/svgpasteaction.cpp @@ -3,9 +3,9 @@ #include "svgpasteaction.h" -#include -#include +#include #include +#include #include diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp index 9831ee18467..e8ebf740397 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp +++ b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp @@ -257,9 +257,13 @@ void Edit3DView::modelAttached(Model *model) if (QtSupport::QtVersion *qtVer = QtSupport::QtKitAspect::qtVersion(target->kit())) m_isBakingLightsSupported = qtVer->qtVersion() >= QVersionNumber(6, 5, 0); } - - connect(model->metaInfo().itemLibraryInfo(), &ItemLibraryInfo::entriesChanged, this, - &Edit3DView::onEntriesChanged, Qt::UniqueConnection); +#ifndef QDS_USE_PROJECTSTORAGE + connect(model->metaInfo().itemLibraryInfo(), + &ItemLibraryInfo::entriesChanged, + this, + &Edit3DView::onEntriesChanged, + Qt::UniqueConnection); +#endif } void Edit3DView::onEntriesChanged() @@ -285,24 +289,43 @@ void Edit3DView::handleEntriesChanged() EK_importedModels }; - QMap entriesMap { + QMap entriesMap{ {EK_cameras, {tr("Cameras"), contextIcon(DesignerIcons::CameraIcon)}}, {EK_lights, {tr("Lights"), contextIcon(DesignerIcons::LightIcon)}}, {EK_primitives, {tr("Primitives"), contextIcon(DesignerIcons::PrimitivesIcon)}}, - {EK_importedModels, {tr("Imported Models"), contextIcon(DesignerIcons::ImportedModelsIcon)}} + {EK_importedModels, {tr("Imported Models"), contextIcon(DesignerIcons::ImportedModelsIcon)}}}; + +#ifdef QDS_USE_PROJECTSTORAGE + const auto &projectStorage = *model()->projectStorage(); + auto append = [&](const NodeMetaInfo &metaInfo, ItemLibraryEntryKeys key) { + auto entries = metaInfo.itemLibrariesEntries(); + if (entries.size()) + entriesMap[key].entryList.append(toItemLibraryEntries(entries, projectStorage)); }; + append(model()->qtQuick3DModelMetaInfo(), EK_primitives); + append(model()->qtQuick3DDirectionalLightMetaInfo(), EK_lights); + append(model()->qtQuick3DSpotLightMetaInfo(), EK_lights); + append(model()->qtQuick3DPointLightMetaInfo(), EK_lights); + append(model()->qtQuick3DOrthographicCameraMetaInfo(), EK_cameras); + append(model()->qtQuick3DPerspectiveCameraMetaInfo(), EK_cameras); + + auto assetsModule = model()->module("Quick3DAssets"); + + for (const auto &metaInfo : model()->metaInfosForModule(assetsModule)) + append(metaInfo, EK_importedModels); +#else const QList itemLibEntries = model()->metaInfo().itemLibraryInfo()->entries(); for (const ItemLibraryEntry &entry : itemLibEntries) { ItemLibraryEntryKeys entryKey; if (entry.typeName() == "QtQuick3D.Model" && entry.name() != "Empty") { entryKey = EK_primitives; } else if (entry.typeName() == "QtQuick3D.DirectionalLight" - || entry.typeName() == "QtQuick3D.PointLight" - || entry.typeName() == "QtQuick3D.SpotLight") { + || entry.typeName() == "QtQuick3D.PointLight" + || entry.typeName() == "QtQuick3D.SpotLight") { entryKey = EK_lights; } else if (entry.typeName() == "QtQuick3D.OrthographicCamera" - || entry.typeName() == "QtQuick3D.PerspectiveCamera") { + || entry.typeName() == "QtQuick3D.PerspectiveCamera") { entryKey = EK_cameras; } else if (entry.typeName().startsWith("Quick3DAssets.") && NodeHints::fromItemLibraryEntry(entry).canBeDroppedInView3D()) { @@ -312,6 +335,7 @@ void Edit3DView::handleEntriesChanged() } entriesMap[entryKey].entryList.append(entry); } +#endif m_edit3DWidget->updateCreateSubMenu(entriesMap.values()); } diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dview.h b/src/plugins/qmldesigner/components/edit3d/edit3dview.h index e5d41614958..c9d93381e05 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dview.h +++ b/src/plugins/qmldesigner/components/edit3d/edit3dview.h @@ -3,7 +3,7 @@ #pragma once #include "edit3dactions.h" -#include "itemlibraryinfo.h" +#include #include #include diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp index c054b712ce3..70de2d29367 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp +++ b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp @@ -618,20 +618,39 @@ void Edit3DWidget::dropEvent(QDropEvent *dropEvent) QHash addedAssets = actionManager.handleExternalAssetsDrop(dropEvent->mimeData()); view()->executeInTransaction("Edit3DWidget::dropEvent", [&] { - // add 3D assets to 3d editor (QtQuick3D import will be added if missing) - ItemLibraryInfo *itemLibInfo = m_view->model()->metaInfo().itemLibraryInfo(); - + // add 3D assets to 3d editor (QtQuick3D import will be added if missing) +#ifdef QDS_USE_PROJECTSTORAGE const QStringList added3DAssets = addedAssets.value(ComponentCoreConstants::add3DAssetsDisplayString); for (const QString &assetPath : added3DAssets) { QString fileName = QFileInfo(assetPath).baseName(); fileName = fileName.at(0).toUpper() + fileName.mid(1); // capitalize first letter - QString type = QString("Quick3DAssets.%1.%1").arg(fileName); - QList entriesForType = itemLibInfo->entriesForType(type.toLatin1()); - if (!entriesForType.isEmpty()) { // should always be true, but just in case - QmlVisualNode::createQml3DNode(view(), entriesForType.at(0), - m_canvas->activeScene(), {}, false).modelNode(); + auto model = m_view->model(); + auto metaInfo = model->metaInfo(model->module("Quick3DAssets"), fileName.toUtf8()); + if (auto entries = metaInfo.itemLibrariesEntries(); entries.size()) { + auto entry = ItemLibraryEntry{entries.front(), *model->projectStorage()}; + QmlVisualNode::createQml3DNode(view(), entry, m_canvas->activeScene(), {}, false); } } +#else + ItemLibraryInfo *itemLibInfo = m_view->model()->metaInfo().itemLibraryInfo(); + + const QStringList added3DAssets = addedAssets.value( + ComponentCoreConstants::add3DAssetsDisplayString); + for (const QString &assetPath : added3DAssets) { + QString fileName = QFileInfo(assetPath).baseName(); + fileName = fileName.at(0).toUpper() + fileName.mid(1); // capitalize first letter + QString type = QString("Quick3DAssets.%1.%1").arg(fileName); + QList entriesForType = itemLibInfo->entriesForType(type.toUtf8()); + if (!entriesForType.isEmpty()) { // should always be true, but just in case + QmlVisualNode::createQml3DNode(view(), + entriesForType.at(0), + m_canvas->activeScene(), + {}, + false) + .modelNode(); + } + } +#endif }); m_view->model()->endDrag(); diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.h b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.h index b9826ca07b3..7f119c205a5 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.h +++ b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.h @@ -9,7 +9,7 @@ #include #include -#include "itemlibraryinfo.h" +#include #include namespace QmlDesigner { diff --git a/src/plugins/qmldesigner/components/eventlist/eventlist.cpp b/src/plugins/qmldesigner/components/eventlist/eventlist.cpp index 24adad74192..919a74193a3 100644 --- a/src/plugins/qmldesigner/components/eventlist/eventlist.cpp +++ b/src/plugins/qmldesigner/components/eventlist/eventlist.cpp @@ -6,7 +6,6 @@ #include "nodelistview.h" #include "bindingproperty.h" -#include "metainfo.h" #include "projectexplorer/project.h" #include "projectexplorer/projectmanager.h" #include "qmldesignerplugin.h" @@ -14,6 +13,7 @@ #include "utils/fileutils.h" #include "utils/qtcassert.h" #include "variantproperty.h" +#include #include #include diff --git a/src/plugins/qmldesigner/components/formeditor/dragtool.cpp b/src/plugins/qmldesigner/components/formeditor/dragtool.cpp index 9a51c9f3722..e363e9bb11d 100644 --- a/src/plugins/qmldesigner/components/formeditor/dragtool.cpp +++ b/src/plugins/qmldesigner/components/formeditor/dragtool.cpp @@ -3,16 +3,16 @@ #include "dragtool.h" +#include "assetslibrarymodel.h" +#include "assetslibrarywidget.h" #include "formeditorscene.h" #include "formeditorview.h" -#include "assetslibrarywidget.h" -#include "assetslibrarymodel.h" #include "materialutils.h" -#include +#include "qmldesignerconstants.h" +#include #include #include #include -#include "qmldesignerconstants.h" #include diff --git a/src/plugins/qmldesigner/components/integration/designdocument.cpp b/src/plugins/qmldesigner/components/integration/designdocument.cpp index 01e37671a01..854a210565d 100644 --- a/src/plugins/qmldesigner/components/integration/designdocument.cpp +++ b/src/plugins/qmldesigner/components/integration/designdocument.cpp @@ -8,7 +8,9 @@ #include "qmlvisualnode.h" #include -#include +#ifndef QDS_USE_PROJECTSTORAGE +# include +#endif #include #include #include @@ -72,8 +74,8 @@ DesignDocument::DesignDocument(ProjectStorageDependencies projectStorageDependen #else : m_documentModel( Model::create("QtQuick.Item", 1, 0, nullptr, std::make_unique())) -#endif , m_subComponentManager(new SubComponentManager(m_documentModel.get(), externalDependencies)) +#endif , m_rewriterView(new RewriterView(externalDependencies, RewriterView::Amend)) , m_documentLoaded(false) , m_currentTarget(nullptr) @@ -165,7 +167,9 @@ ModelPointer DesignDocument::createInFileComponentModel() nullptr, std::make_unique()); model->setFileUrl(m_documentModel->fileUrl()); +#ifndef QDS_USE_PROJECTSTORAGE model->setMetaInfo(m_documentModel->metaInfo()); +#endif return model; } @@ -523,6 +527,7 @@ void DesignDocument::close() emit designDocumentClosed(); } +#ifndef QDS_USE_PROJECTSTORAGE void DesignDocument::updateSubcomponentManager() { Q_ASSERT(m_subComponentManager); @@ -534,6 +539,7 @@ void DesignDocument::addSubcomponentManagerImport(const Import &import) { m_subComponentManager->addAndParseImport(import); } +#endif void DesignDocument::deleteSelected() { diff --git a/src/plugins/qmldesigner/components/integration/designdocument.h b/src/plugins/qmldesigner/components/integration/designdocument.h index 7c0fe3941f8..c5c1bab27f9 100644 --- a/src/plugins/qmldesigner/components/integration/designdocument.h +++ b/src/plugins/qmldesigner/components/integration/designdocument.h @@ -9,7 +9,9 @@ #include #include #include -#include +#ifndef QDS_USE_PROJECTSTORAGE +# include +#endif #include #include @@ -49,9 +51,10 @@ public: void loadDocument(QPlainTextEdit *edit); void attachRewriterToModel(); void close(); +#ifndef QDS_USE_PROJECTSTORAGE void updateSubcomponentManager(); void addSubcomponentManagerImport(const Import &import); - +#endif bool isUndoAvailable() const; bool isRedoAvailable() const; @@ -138,8 +141,9 @@ private: // variables QPointer m_textEditor; QScopedPointer m_documentTextModifier; QScopedPointer m_inFileComponentTextModifier; +#ifndef QDS_USE_PROJECTSTORAGE QScopedPointer m_subComponentManager; - +#endif QScopedPointer m_rewriterView; bool m_documentLoaded; ProjectExplorer::Target *m_currentTarget; diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryitem.h b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryitem.h index 449103976b1..1bc091d0054 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryitem.h +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryitem.h @@ -8,7 +8,7 @@ #include #include -#include "itemlibraryinfo.h" +#include namespace QmlDesigner { diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp index 3f611a0dbba..c9aafc48fb7 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp @@ -3,10 +3,10 @@ #include "itemlibrarymodel.h" #include "itemlibrarycategoriesmodel.h" -#include "itemlibraryimport.h" #include "itemlibrarycategory.h" +#include "itemlibraryentry.h" +#include "itemlibraryimport.h" #include "itemlibraryitem.h" -#include "itemlibraryinfo.h" #include #include @@ -304,7 +304,7 @@ Import ItemLibraryModel::entryToImport(const ItemLibraryEntry &entry) } -void ItemLibraryModel::update(ItemLibraryInfo *itemLibraryInfo, Model *model) +void ItemLibraryModel::update([[maybe_unused]] ItemLibraryInfo *itemLibraryInfo, Model *model) { if (!model) return; @@ -365,9 +365,14 @@ void ItemLibraryModel::update(ItemLibraryInfo *itemLibraryInfo, Model *model) } const bool blockNewImports = document->inFileComponentModelActive(); - const QList itemLibEntries = itemLibraryInfo->entries(); + const QList itemLibEntries = model->itemLibraryEntries(); for (const ItemLibraryEntry &entry : itemLibEntries) { - NodeMetaInfo metaInfo = model->metaInfo(entry.typeName()); + NodeMetaInfo metaInfo; + + if constexpr (useProjectStorage()) + metaInfo = entry.metaInfo(); + else + metaInfo = model->metaInfo(entry.typeName()); bool valid = metaInfo.isValid() && (metaInfo.majorVersion() >= entry.majorVersion() diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp index 3082ee442ac..a6adc0d4f7d 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp @@ -76,9 +76,11 @@ void ItemLibraryView::modelAboutToBeDetached(Model *model) void ItemLibraryView::importsChanged(const Imports &addedImports, const Imports &removedImports) { +#ifndef QDS_USE_PROJECTSTORAGE DesignDocument *document = QmlDesignerPlugin::instance()->currentDesignDocument(); for (const auto &import : addedImports) document->addSubcomponentManagerImport(import); +#endif updateImports(); m_widget->updatePossibleImports(model()->possibleImports()); @@ -114,9 +116,11 @@ void ItemLibraryView::importsChanged(const Imports &addedImports, const Imports void ItemLibraryView::possibleImportsChanged(const Imports &possibleImports) { +#ifndef QDS_USE_PROJECTSTORAGE DesignDocument *document = QmlDesignerPlugin::instance()->currentDesignDocument(); for (const auto &import : possibleImports) document->addSubcomponentManagerImport(import); +#endif m_widget->updatePossibleImports(possibleImports); } diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp index 8d3603ea841..7703f352b0e 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp @@ -14,15 +14,20 @@ #include #include #include +#include #include -#include +#ifndef QDS_USE_PROJECTSTORAGE +# include +#endif #include -#include #include #include #include #include #include +#ifndef QDS_USE_PROJECTSTORAGE +# include +#endif #include #include @@ -176,6 +181,7 @@ ItemLibraryWidget::ItemLibraryWidget(AsynchronousImageCache &imageCache) ItemLibraryWidget::~ItemLibraryWidget() = default; +#ifndef QDS_USE_PROJECTSTORAGE void ItemLibraryWidget::setItemLibraryInfo(ItemLibraryInfo *itemLibraryInfo) { if (m_itemLibraryInfo.data() == itemLibraryInfo) @@ -192,6 +198,7 @@ void ItemLibraryWidget::setItemLibraryInfo(ItemLibraryInfo *itemLibraryInfo) } delayedUpdateModel(); } +#endif QList ItemLibraryWidget::createToolBarWidgets() { @@ -271,8 +278,9 @@ void ItemLibraryWidget::setModel(Model *model) m_itemToDrag = {}; return; } - +#ifndef QDS_USE_PROJECTSTORAGE setItemLibraryInfo(model->metaInfo().itemLibraryInfo()); +#endif if (DesignDocument *document = QmlDesignerPlugin::instance()->currentDesignDocument()) { const bool subCompEditMode = document->inFileComponentModelActive(); @@ -321,7 +329,9 @@ void ItemLibraryWidget::updateModel() m_compressionTimer.stop(); } +#ifndef QDS_USE_PROJECTSTORAGE m_itemLibraryModel->update(m_itemLibraryInfo.data(), m_model.data()); +#endif if (m_itemLibraryModel->rowCount() == 0 && !m_updateRetry) { m_updateRetry = true; // Only retry once to avoid endless loops diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.h b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.h index e791fceb698..b56532b2185 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.h +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.h @@ -3,7 +3,9 @@ #pragma once -#include "itemlibraryinfo.h" +#ifndef QDS_USE_PROJECTSTORAGE +# include +#endif #include "import.h" #include @@ -51,7 +53,9 @@ public: ItemLibraryWidget(AsynchronousImageCache &imageCache); ~ItemLibraryWidget(); +#ifndef QDS_USE_PROJECTSTORAGE void setItemLibraryInfo(ItemLibraryInfo *itemLibraryInfo); +#endif QList createToolBarWidgets(); static QString qmlSourcesPath(); @@ -97,8 +101,9 @@ private: QTimer m_compressionTimer; QSize m_itemIconSize; +#ifndef QDS_USE_PROJECTSTORAGE QPointer m_itemLibraryInfo; - +#endif QPointer m_itemLibraryModel; QPointer m_addModuleModel; diff --git a/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp b/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp index 487d300ef97..6d17a350f4f 100644 --- a/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp +++ b/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp @@ -4,16 +4,15 @@ #include "materialeditorview.h" #include "asset.h" -#include "bindingproperty.h" #include "auxiliarydataproperties.h" +#include "bindingproperty.h" #include "designdocument.h" #include "designmodewidget.h" #include "dynamicpropertiesmodel.h" #include "externaldependenciesinterface.h" -#include "itemlibraryinfo.h" -#include "materialeditorqmlbackend.h" #include "materialeditorcontextobject.h" #include "materialeditordynamicpropertiesproxymodel.h" +#include "materialeditorqmlbackend.h" #include "materialeditortransaction.h" #include "metainfo.h" #include "nodeinstanceview.h" @@ -25,6 +24,7 @@ #include "qmldesignerplugin.h" #include "qmltimeline.h" #include "variantproperty.h" +#include #include #include @@ -704,7 +704,7 @@ void MaterialEditorView::delayedTypeUpdate() m_typeUpdateTimer.start(); } -static Import entryToImport(const ItemLibraryEntry &entry) +[[maybe_unused]] static Import entryToImport(const ItemLibraryEntry &entry) { if (entry.majorVersion() == -1 && entry.minorVersion() == -1) return Import::createFileImport(entry.requiredImport()); @@ -721,7 +721,15 @@ void MaterialEditorView::updatePossibleTypes() if (!m_qmlBackEnd) return; - // Ensure basic types are always first +#ifdef QDS_USE_PROJECTSTORAGE + auto heirs = model()->qtQuick3DMaterialMetaInfo().heirs(); + heirs.push_back(model()->qtQuick3DMaterialMetaInfo()); + auto entries = Utils::transform(heirs, [&](const auto &heir) { + return toItemLibraryEntries(heir.itemLibrariesEntries(), *model()->projectStorage()); + }); + + // I am unsure about the code intention here +#else // Ensure basic types are always first QStringList nonQuick3dTypes; QStringList allTypes; @@ -755,6 +763,7 @@ void MaterialEditorView::updatePossibleTypes() allTypes.append(nonQuick3dTypes); m_qmlBackEnd->contextObject()->setPossibleTypes(allTypes); +#endif } void MaterialEditorView::modelAttached(Model *model) @@ -774,6 +783,7 @@ void MaterialEditorView::modelAttached(Model *model) m_ensureMatLibTimer.start(500); } +#ifndef QDS_USE_PROJECTSTORAGE if (m_itemLibraryInfo.data() != model->metaInfo().itemLibraryInfo()) { if (m_itemLibraryInfo) { disconnect(m_itemLibraryInfo.data(), &ItemLibraryInfo::entriesChanged, @@ -785,6 +795,7 @@ void MaterialEditorView::modelAttached(Model *model) this, &MaterialEditorView::delayedTypeUpdate); } } +#endif if (!m_setupCompleted) { reloadQml(); diff --git a/src/plugins/qmldesigner/components/navigator/iconcheckboxitemdelegate.cpp b/src/plugins/qmldesigner/components/navigator/iconcheckboxitemdelegate.cpp index d5701ac5691..c24aa1933bf 100644 --- a/src/plugins/qmldesigner/components/navigator/iconcheckboxitemdelegate.cpp +++ b/src/plugins/qmldesigner/components/navigator/iconcheckboxitemdelegate.cpp @@ -10,7 +10,6 @@ #include "navigatortreemodel.h" #include "qproxystyle.h" -#include #include #include diff --git a/src/plugins/qmldesigner/components/navigator/nameitemdelegate.cpp b/src/plugins/qmldesigner/components/navigator/nameitemdelegate.cpp index 1a3814f9618..aacaf6dc0d9 100644 --- a/src/plugins/qmldesigner/components/navigator/nameitemdelegate.cpp +++ b/src/plugins/qmldesigner/components/navigator/nameitemdelegate.cpp @@ -11,7 +11,6 @@ #include "choosefrompropertylistdialog.h" #include "qproxystyle.h" -#include #include #include #include diff --git a/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp b/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp index 6abda00dbac..33ed88ed722 100644 --- a/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp +++ b/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp @@ -16,8 +16,8 @@ #include #include #include +#include #include -#include #include #include #include diff --git a/src/plugins/qmldesigner/components/navigator/navigatortreeview.cpp b/src/plugins/qmldesigner/components/navigator/navigatortreeview.cpp index 6e1532155f5..8de0fb1b01c 100644 --- a/src/plugins/qmldesigner/components/navigator/navigatortreeview.cpp +++ b/src/plugins/qmldesigner/components/navigator/navigatortreeview.cpp @@ -12,7 +12,6 @@ #include -#include #include #include diff --git a/src/plugins/qmldesigner/components/navigator/navigatorview.cpp b/src/plugins/qmldesigner/components/navigator/navigatorview.cpp index 456f44e43c6..a4a7ead0426 100644 --- a/src/plugins/qmldesigner/components/navigator/navigatorview.cpp +++ b/src/plugins/qmldesigner/components/navigator/navigatorview.cpp @@ -13,18 +13,18 @@ #include #include #include -#include +#include #include #include #include #include +#include +#include +#include #include #include #include #include -#include -#include -#include #include #include diff --git a/src/plugins/qmldesigner/components/pathtool/pathtoolview.cpp b/src/plugins/qmldesigner/components/pathtool/pathtoolview.cpp index eeb1dbe88d6..6fcedfa7434 100644 --- a/src/plugins/qmldesigner/components/pathtool/pathtoolview.cpp +++ b/src/plugins/qmldesigner/components/pathtool/pathtoolview.cpp @@ -6,7 +6,6 @@ #include #include #include -#include #include "pathtool.h" diff --git a/src/plugins/qmldesigner/components/textureeditor/textureeditorview.cpp b/src/plugins/qmldesigner/components/textureeditor/textureeditorview.cpp index d088dd7a26a..47d85c4dbc3 100644 --- a/src/plugins/qmldesigner/components/textureeditor/textureeditorview.cpp +++ b/src/plugins/qmldesigner/components/textureeditor/textureeditorview.cpp @@ -14,7 +14,6 @@ #include #include #include -#include #include #include #include diff --git a/src/plugins/qmldesigner/components/textureeditor/textureeditorview.h b/src/plugins/qmldesigner/components/textureeditor/textureeditorview.h index 7baa07e9d30..dd5e0b0cc98 100644 --- a/src/plugins/qmldesigner/components/textureeditor/textureeditorview.h +++ b/src/plugins/qmldesigner/components/textureeditor/textureeditorview.h @@ -4,7 +4,6 @@ #pragma once #include -#include #include #include @@ -119,7 +118,6 @@ private: bool m_initializingPreviewData = false; QPointer m_colorDialog; - QPointer m_itemLibraryInfo; DynamicPropertiesModel *m_dynamicPropertiesModel = nullptr; }; diff --git a/src/plugins/qmldesigner/designercore/exceptions/invalidmetainfoexception.cpp b/src/plugins/qmldesigner/designercore/exceptions/invalidmetainfoexception.cpp index 3dc8c19d393..fbf32676896 100644 --- a/src/plugins/qmldesigner/designercore/exceptions/invalidmetainfoexception.cpp +++ b/src/plugins/qmldesigner/designercore/exceptions/invalidmetainfoexception.cpp @@ -18,9 +18,9 @@ namespace QmlDesigner { the __FILE__ macro. */ InvalidMetaInfoException::InvalidMetaInfoException(int line, - const QByteArray &function, - const QByteArray &file) - : Exception(line, function, file) + const QByteArray &function, + const QByteArray &file) + : Exception(line, function, file) { createWarning(); } diff --git a/src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.cpp b/src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.cpp index 344bd4a0191..4530290fbe3 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.cpp +++ b/src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.cpp @@ -4,9 +4,9 @@ #include "imagecachecollector.h" #include "imagecacheconnectionmanager.h" -#include #include #include +#include #include #include @@ -21,7 +21,7 @@ namespace QmlDesigner { namespace { -QByteArray fileToByteArray(QString const &filename) +QByteArray fileToByteArray(const QString &filename) { QFile file(filename); QFileInfo fleInfo(file); diff --git a/src/plugins/qmldesigner/designercore/include/abstractview.h b/src/plugins/qmldesigner/designercore/include/abstractview.h index 0a3350b693b..71c675fd3cb 100644 --- a/src/plugins/qmldesigner/designercore/include/abstractview.h +++ b/src/plugins/qmldesigner/designercore/include/abstractview.h @@ -79,7 +79,7 @@ public: ~AbstractView() override; - Model *model() const; + Model *model() const { return m_model.data(); } bool isAttached() const; RewriterTransaction beginRewriterTransaction(const QByteArray &identifier); diff --git a/src/plugins/qmldesigner/designercore/include/itemlibraryentry.h b/src/plugins/qmldesigner/designercore/include/itemlibraryentry.h new file mode 100644 index 00000000000..f88f9e35c6f --- /dev/null +++ b/src/plugins/qmldesigner/designercore/include/itemlibraryentry.h @@ -0,0 +1,93 @@ +// 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 "qmldesignercorelib_global.h" + +#include "propertycontainer.h" + +#include + +#include +#include +#include +#include + +namespace QmlDesigner { + +namespace Internal { + +class ItemLibraryEntryData; +class MetaInfoPrivate; +} // namespace Internal + +class ItemLibraryEntry; +class NodeMetaInfo; + +QMLDESIGNERCORE_EXPORT QDataStream &operator<<(QDataStream &stream, + const ItemLibraryEntry &itemLibraryEntry); +QMLDESIGNERCORE_EXPORT QDataStream &operator>>(QDataStream &stream, + ItemLibraryEntry &itemLibraryEntry); +QMLDESIGNERCORE_EXPORT QDebug operator<<(QDebug debug, const ItemLibraryEntry &itemLibraryEntry); + +class QMLDESIGNERCORE_EXPORT ItemLibraryEntry +{ + friend QMLDESIGNERCORE_EXPORT QDataStream &operator<<(QDataStream &stream, + const ItemLibraryEntry &itemLibraryEntry); + friend QMLDESIGNERCORE_EXPORT QDataStream &operator>>(QDataStream &stream, + ItemLibraryEntry &itemLibraryEntry); + friend QMLDESIGNERCORE_EXPORT QDebug operator<<(QDebug debug, + const ItemLibraryEntry &itemLibraryEntry); + +public: + ItemLibraryEntry(); + explicit ItemLibraryEntry(const Storage::Info::ItemLibraryEntry &entry, + const ProjectStorageType &projectStorage); + ~ItemLibraryEntry() = default; + + QString name() const; + TypeName typeName() const; + const NodeMetaInfo &metaInfo() const; + QIcon typeIcon() const; + QString libraryEntryIconPath() const; + int majorVersion() const; + int minorVersion() const; + QString category() const; + QString qmlSource() const; + QString requiredImport() const; + QString customComponentSource() const; + QStringList extraFilePaths() const; + QString toolTip() const; + + using Property = QmlDesigner::PropertyContainer; + + QList properties() const; + QHash hints() const; + + void setType(const TypeName &typeName, int majorVersion = -1, int minorVersion = -1); + void setName(const QString &name); + void setLibraryEntryIconPath(const QString &libraryEntryIconPath); + void addProperty(const Property &p); + void addProperty(PropertyName &name, QString &type, QVariant &value); + void setTypeIcon(const QIcon &typeIcon); + void setCategory(const QString &category); + void setQmlPath(const QString &qml); + void setRequiredImport(const QString &requiredImport); + void setToolTip(const QString &tooltip); + void addHints(const QHash &hints); + void setCustomComponentSource(const QString &source); + void addExtraFilePath(const QString &extraFile); + +private: + std::shared_ptr m_data; +}; + +using ItemLibraryEntries = QList; + +QMLDESIGNERCORE_EXPORT QList toItemLibraryEntries( + const Storage::Info::ItemLibraryEntries &entries, const ProjectStorageType &projectStorage); + +} // namespace QmlDesigner + +Q_DECLARE_METATYPE(QmlDesigner::ItemLibraryEntry) diff --git a/src/plugins/qmldesigner/designercore/include/itemlibraryinfo.h b/src/plugins/qmldesigner/designercore/include/itemlibraryinfo.h index 2974d58d0b5..99c10f11ed2 100644 --- a/src/plugins/qmldesigner/designercore/include/itemlibraryinfo.h +++ b/src/plugins/qmldesigner/designercore/include/itemlibraryinfo.h @@ -3,76 +3,12 @@ #pragma once -#include "qmldesignercorelib_global.h" +#ifndef QDS_USE_PROJECTSTORAGE -#include "propertycontainer.h" -#include -#include -#include -#include +# include "itemlibraryentry.h" namespace QmlDesigner { -namespace Internal { - -class ItemLibraryEntryData; -class MetaInfoPrivate; -} - -class ItemLibraryEntry; - -QMLDESIGNERCORE_EXPORT QDataStream& operator<<(QDataStream& stream, const ItemLibraryEntry &itemLibraryEntry); -QMLDESIGNERCORE_EXPORT QDataStream& operator>>(QDataStream& stream, ItemLibraryEntry &itemLibraryEntry); -QMLDESIGNERCORE_EXPORT QDebug operator<<(QDebug debug, const ItemLibraryEntry &itemLibraryEntry); - -class QMLDESIGNERCORE_EXPORT ItemLibraryEntry -{ - friend QMLDESIGNERCORE_EXPORT QDataStream& operator<<(QDataStream& stream, const ItemLibraryEntry &itemLibraryEntry); - friend QMLDESIGNERCORE_EXPORT QDataStream& operator>>(QDataStream& stream, ItemLibraryEntry &itemLibraryEntry); - friend QMLDESIGNERCORE_EXPORT QDebug operator<<(QDebug debug, const ItemLibraryEntry &itemLibraryEntry); - -public: - ItemLibraryEntry(); - ~ItemLibraryEntry() = default; - - QString name() const; - TypeName typeName() const; - QIcon typeIcon() const; - QString libraryEntryIconPath() const; - int majorVersion() const; - int minorVersion() const; - QString category() const; - QIcon dragIcon() const; - QString qmlPath() const; - QString qmlSource() const; - QString requiredImport() const; - QString customComponentSource() const; - QStringList extraFilePaths() const; - QString toolTip() const; - - using Property = QmlDesigner::PropertyContainer; - - QList properties() const; - QHash hints() const; - - void setType(const TypeName &typeName, int majorVersion = -1, int minorVersion = -1); - void setName(const QString &name); - void setLibraryEntryIconPath(const QString &libraryEntryIconPath); - void addProperty(const Property &p); - void addProperty(PropertyName &name, QString &type, QVariant &value); - void setTypeIcon(const QIcon &typeIcon); - void setCategory(const QString &category); - void setQmlPath(const QString &qml); - void setRequiredImport(const QString &requiredImport); - void setToolTip(const QString &tooltip); - void addHints(const QHash &hints); - void setCustomComponentSource(const QString &source); - void addExtraFilePath(const QString &extraFile); - -private: - std::shared_ptr m_data; -}; - class QMLDESIGNERCORE_EXPORT ItemLibraryInfo : public QObject { Q_OBJECT @@ -101,5 +37,4 @@ private: // variables }; } // namespace QmlDesigner - -Q_DECLARE_METATYPE(QmlDesigner::ItemLibraryEntry) +#endif diff --git a/src/plugins/qmldesigner/designercore/include/metainfo.h b/src/plugins/qmldesigner/designercore/include/metainfo.h index 914be96db7b..b176831eb6d 100644 --- a/src/plugins/qmldesigner/designercore/include/metainfo.h +++ b/src/plugins/qmldesigner/designercore/include/metainfo.h @@ -3,13 +3,15 @@ #pragma once -#include "qmldesignercorelib_global.h" +#ifndef QDS_USE_PROJECTSTORAGE -#include -#include +# include "qmldesignercorelib_global.h" -#include -#include "itemlibraryinfo.h" +# include +# include + +# include +# include "itemlibraryinfo.h" namespace QmlDesigner { @@ -63,3 +65,4 @@ private: }; } //namespace QmlDesigner +#endif diff --git a/src/plugins/qmldesigner/designercore/include/metainforeader.h b/src/plugins/qmldesigner/designercore/include/metainforeader.h index b10c7413c59..c12d507ed9c 100644 --- a/src/plugins/qmldesigner/designercore/include/metainforeader.h +++ b/src/plugins/qmldesigner/designercore/include/metainforeader.h @@ -3,13 +3,15 @@ #pragma once -#include "qmldesignercorelib_global.h" -#include +#ifndef QDS_USE_PROJECTSTORAGE -#include +# include "qmldesignercorelib_global.h" +# include -#include -#include +# include + +# include +# include namespace QmlDesigner { @@ -106,3 +108,5 @@ private: } } + +#endif diff --git a/src/plugins/qmldesigner/designercore/include/model.h b/src/plugins/qmldesigner/designercore/include/model.h index cc3b4aadf9c..9816857db65 100644 --- a/src/plugins/qmldesigner/designercore/include/model.h +++ b/src/plugins/qmldesigner/designercore/include/model.h @@ -10,11 +10,13 @@ #include #include #include +#include #include #include #include #include +#include #include @@ -44,6 +46,7 @@ class AbstractProperty; class RewriterView; class NodeInstanceView; class TextModifier; +class ItemLibraryEntry; using PropertyListType = QList>; @@ -125,15 +128,18 @@ public: SourceId fileUrlSourceId() const; void setFileUrl(const QUrl &url); +#ifndef QDS_USE_PROJECTSTORAGE const MetaInfo metaInfo() const; MetaInfo metaInfo(); + void setMetaInfo(const MetaInfo &metaInfo); +#endif + Module module(Utils::SmallStringView moduleName); NodeMetaInfo metaInfo(const TypeName &typeName, int majorVersion = -1, int minorVersion = -1) const; NodeMetaInfo metaInfo(Module module, Utils::SmallStringView typeName, Storage::Version version = Storage::Version{}) const; bool hasNodeMetaInfo(const TypeName &typeName, int majorVersion = -1, int minorVersion = -1) const; - void setMetaInfo(const MetaInfo &metaInfo); NodeMetaInfo boolMetaInfo() const; NodeMetaInfo doubleMetaInfo() const; @@ -148,10 +154,15 @@ public: NodeMetaInfo qtQmlModelsListElementMetaInfo() const; NodeMetaInfo qtQuick3DBakedLightmapMetaInfo() const; NodeMetaInfo qtQuick3DDefaultMaterialMetaInfo() const; + NodeMetaInfo qtQuick3DDirectionalLightMetaInfo() const; NodeMetaInfo qtQuick3DMaterialMetaInfo() const; NodeMetaInfo qtQuick3DModelMetaInfo() const; NodeMetaInfo qtQuick3DNodeMetaInfo() const; + NodeMetaInfo qtQuick3DOrthographicCameraMetaInfo() const; + NodeMetaInfo qtQuick3DPerspectiveCameraMetaInfo() const; + NodeMetaInfo qtQuick3DPointLightMetaInfo() const; NodeMetaInfo qtQuick3DPrincipledMaterialMetaInfo() const; + NodeMetaInfo qtQuick3DSpotLightMetaInfo() const; NodeMetaInfo qtQuick3DTextureMetaInfo() const; NodeMetaInfo qtQuickConnectionsMetaInfo() const; NodeMetaInfo qtQuickControlsTextAreaMetaInfo() const; @@ -169,6 +180,9 @@ public: NodeMetaInfo vector2dMetaInfo() const; NodeMetaInfo vector3dMetaInfo() const; NodeMetaInfo vector4dMetaInfo() const; + QVarLengthArray metaInfosForModule(Module module) const; + + QList itemLibraryEntries() const; void attachView(AbstractView *view); void detachView(AbstractView *view, ViewNotification emitDetachNotify = NotifyView); diff --git a/src/plugins/qmldesigner/designercore/include/nodehints.h b/src/plugins/qmldesigner/designercore/include/nodehints.h index 8bdb30f5a38..9e67c2d99b3 100644 --- a/src/plugins/qmldesigner/designercore/include/nodehints.h +++ b/src/plugins/qmldesigner/designercore/include/nodehints.h @@ -58,8 +58,9 @@ public: private: explicit NodeHints(const ModelNode &modelNode); + explicit NodeHints(const NodeMetaInfo &metaInfo); explicit NodeHints(const ItemLibraryEntry &entry); - ModelNode modelNode() const; + const ModelNode &modelNode() const; bool isValid() const; Model *model() const; bool evaluateBooleanExpression(const QString &hintName, bool defaultValue, const ModelNode potentialParent = ModelNode()) const; diff --git a/src/plugins/qmldesigner/designercore/include/nodemetainfo.h b/src/plugins/qmldesigner/designercore/include/nodemetainfo.h index 02a0c81ca4a..58cdeba228e 100644 --- a/src/plugins/qmldesigner/designercore/include/nodemetainfo.h +++ b/src/plugins/qmldesigner/designercore/include/nodemetainfo.h @@ -33,6 +33,8 @@ enum class MetaInfoType { None, Reference, Value, Sequence }; class QMLDESIGNERCORE_EXPORT NodeMetaInfo { + using NodeMetaInfos = std::vector; + public: NodeMetaInfo(); NodeMetaInfo(Model *model, const TypeName &typeName, int majorVersion, int minorVersion); @@ -59,6 +61,20 @@ public: bool isFileComponent() const; bool isProjectComponent() const; bool isInProjectModule() const; + FlagIs canBeContainer() const; + FlagIs forceClip() const; + FlagIs doesLayoutChildren() const; + FlagIs canBeDroppedInFormEditor() const; + FlagIs canBeDroppedInNavigator() const; + FlagIs canBeDroppedInView3D() const; + FlagIs isMovable() const; + FlagIs isResizable() const; + FlagIs hasFormEditorItem() const; + FlagIs isStackedContainer() const; + FlagIs takesOverRenderingOfChildren() const; + FlagIs visibleInNavigator() const; + FlagIs visibleInLibrary() const; + bool hasProperty(::Utils::SmallStringView propertyName) const; PropertyMetaInfos properties() const; PropertyMetaInfos localProperties() const; @@ -69,8 +85,9 @@ public: PropertyMetaInfo defaultProperty() const; bool hasDefaultProperty() const; - std::vector selfAndPrototypes() const; - std::vector prototypes() const; + NodeMetaInfos selfAndPrototypes() const; + NodeMetaInfos prototypes() const; + NodeMetaInfos heirs() const; NodeMetaInfo commonBase(const NodeMetaInfo &metaInfo) const; bool defaultPropertyIsComponent() const; @@ -83,6 +100,10 @@ public: Storage::Info::ExportedTypeNames allExportedTypeNames() const; Storage::Info::ExportedTypeNames exportedTypeNamesForSourceId(SourceId sourceId) const; + Storage::Info::TypeHints typeHints() const; + Utils::PathString iconPath() const; + Storage::Info::ItemLibraryEntries itemLibrariesEntries() const; + SourceId sourceId() const; QString componentFileName() const; diff --git a/src/plugins/qmldesigner/designercore/include/subcomponentmanager.h b/src/plugins/qmldesigner/designercore/include/subcomponentmanager.h index dbc89c1cccd..7fa23488543 100644 --- a/src/plugins/qmldesigner/designercore/include/subcomponentmanager.h +++ b/src/plugins/qmldesigner/designercore/include/subcomponentmanager.h @@ -3,18 +3,20 @@ #pragma once -#include "qmldesignercorelib_global.h" +#ifndef QDS_USE_PROJECTSTORAGE -#include +# include "qmldesignercorelib_global.h" -#include -#include -#include -#include -#include -#include -#include -#include +# include + +# include +# include +# include +# include +# include +# include +# include +# include namespace QmlDesigner { @@ -63,3 +65,4 @@ private: // variables }; } // namespace QmlDesigner +#endif diff --git a/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp b/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp index 641455910a0..e187d4986f4 100644 --- a/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp +++ b/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp @@ -57,7 +57,6 @@ #include #include #include -#include #include #include #include diff --git a/src/plugins/qmldesigner/designercore/metainfo/itemlibraryentry.cpp b/src/plugins/qmldesigner/designercore/metainfo/itemlibraryentry.cpp new file mode 100644 index 00000000000..806da7e7c4d --- /dev/null +++ b/src/plugins/qmldesigner/designercore/metainfo/itemlibraryentry.cpp @@ -0,0 +1,306 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "../include/itemlibraryentry.h" +#include "nodemetainfo.h" +#include "qregularexpression.h" + +#include +#include + +#include + +#include +#include + +namespace QmlDesigner { + +namespace Internal { + +class ItemLibraryEntryData +{ +public: + QString name; + TypeName typeName; + NodeMetaInfo metaInfo; + QString category; + int majorVersion{-1}; + int minorVersion{-1}; + QString libraryEntryIconPath; + QIcon typeIcon = QIcon(":/ItemLibrary/images/item-default-icon.png"); + QList properties; + QString qml; + QString qmlSource; + QString requiredImport; + QHash hints; + QString customComponentSource; + QStringList extraFilePaths; + QString toolTip; +}; + +} // namespace Internal + +void ItemLibraryEntry::setTypeIcon(const QIcon &icon) +{ + m_data->typeIcon = icon; +} + +void ItemLibraryEntry::addProperty(const Property &property) +{ + m_data->properties.append(property); +} + +QList ItemLibraryEntry::properties() const +{ + return m_data->properties; +} + +QHash ItemLibraryEntry::hints() const +{ + return m_data->hints; +} + +ItemLibraryEntry::ItemLibraryEntry() + : m_data(std::make_shared()) +{} + +ItemLibraryEntry::ItemLibraryEntry(const Storage::Info::ItemLibraryEntry &entry, + const ProjectStorageType &projectStorage) + : ItemLibraryEntry{} +{ + m_data->name = entry.name.toQString(); + m_data->metaInfo = {entry.typeId, &projectStorage}; + m_data->category = entry.category.toQString(); + if (entry.iconPath.size()) + m_data->libraryEntryIconPath = entry.iconPath.toQString(); + m_data->requiredImport = entry.import.toQString(); + m_data->toolTip = entry.toolTip.toQString(); + m_data->qmlSource = entry.templatePath.toQString(); + m_data->properties.reserve(Utils::ssize(entry.properties)); + for (const auto &property : entry.properties) { + m_data->properties.emplace_back(property.name.toQByteArray(), + property.type.toQString(), + QVariant{property.value}); + } + m_data->extraFilePaths.reserve(Utils::ssize(entry.extraFilePaths)); + for (const auto &extraFilePath : entry.extraFilePaths) + m_data->extraFilePaths.emplace_back(extraFilePath.toQString()); +} + +QString ItemLibraryEntry::name() const +{ + return m_data->name; +} + +TypeName ItemLibraryEntry::typeName() const +{ + return m_data->typeName; +} + +const NodeMetaInfo &ItemLibraryEntry::metaInfo() const +{ + return m_data->metaInfo; +} + +QString ItemLibraryEntry::qmlSource() const +{ + return m_data->qmlSource; +} + +QString ItemLibraryEntry::requiredImport() const +{ + return m_data->requiredImport; +} + +QString ItemLibraryEntry::customComponentSource() const +{ + return m_data->customComponentSource; +} + +QStringList ItemLibraryEntry::extraFilePaths() const +{ + return m_data->extraFilePaths; +} + +QString ItemLibraryEntry::toolTip() const +{ + return m_data->toolTip; +} + +int ItemLibraryEntry::majorVersion() const +{ + return m_data->majorVersion; +} + +int ItemLibraryEntry::minorVersion() const +{ + return m_data->minorVersion; +} + +QString ItemLibraryEntry::category() const +{ + return m_data->category; +} + +void ItemLibraryEntry::setCategory(const QString &category) +{ + m_data->category = category; +} + +QIcon ItemLibraryEntry::typeIcon() const +{ + return m_data->typeIcon; +} + +QString ItemLibraryEntry::libraryEntryIconPath() const +{ + return m_data->libraryEntryIconPath; +} + +void ItemLibraryEntry::setName(const QString &name) +{ + m_data->name = name; +} + +void ItemLibraryEntry::setType(const TypeName &typeName, int majorVersion, int minorVersion) +{ + m_data->typeName = typeName; + m_data->majorVersion = majorVersion; + m_data->minorVersion = minorVersion; +} + +void ItemLibraryEntry::setLibraryEntryIconPath(const QString &iconPath) +{ + m_data->libraryEntryIconPath = iconPath; +} + +static QByteArray getSourceForUrl(const QString &fileURl) +{ + Utils::FileReader fileReader; + + if (fileReader.fetch(Utils::FilePath::fromString(fileURl))) + return fileReader.data(); + else + return Utils::FileReader::fetchQrc(fileURl); +} + +void ItemLibraryEntry::setQmlPath(const QString &qml) +{ + m_data->qml = qml; + + m_data->qmlSource = QString::fromUtf8(getSourceForUrl(qml)); +} + +void ItemLibraryEntry::setRequiredImport(const QString &requiredImport) +{ + m_data->requiredImport = requiredImport; +} + +void ItemLibraryEntry::setToolTip(const QString &tooltip) +{ + static QRegularExpression regularExpressionPattern(QLatin1String("^qsTr\\(\"(.*)\"\\)$")); + const QRegularExpressionMatch match = regularExpressionPattern.match(tooltip); + if (match.hasMatch()) + m_data->toolTip = match.captured(1); + else + m_data->toolTip = tooltip; +} + +void ItemLibraryEntry::addHints(const QHash &hints) +{ + Utils::addToHash(&m_data->hints, hints); +} + +void ItemLibraryEntry::setCustomComponentSource(const QString &source) +{ + m_data->customComponentSource = source; +} + +void ItemLibraryEntry::addExtraFilePath(const QString &extraFile) +{ + m_data->extraFilePaths.append(extraFile); +} + +void ItemLibraryEntry::addProperty(PropertyName &name, QString &type, QVariant &value) +{ + Property property; + property.set(name, type, value); + addProperty(property); +} + +QDataStream &operator<<(QDataStream &stream, const ItemLibraryEntry &itemLibraryEntry) +{ + stream << itemLibraryEntry.name(); + stream << itemLibraryEntry.typeName(); + stream << itemLibraryEntry.majorVersion(); + stream << itemLibraryEntry.minorVersion(); + stream << itemLibraryEntry.typeIcon(); + stream << itemLibraryEntry.libraryEntryIconPath(); + stream << itemLibraryEntry.category(); + stream << itemLibraryEntry.requiredImport(); + stream << itemLibraryEntry.hints(); + + stream << itemLibraryEntry.m_data->properties; + stream << itemLibraryEntry.m_data->qml; + stream << itemLibraryEntry.m_data->qmlSource; + stream << itemLibraryEntry.m_data->customComponentSource; + stream << itemLibraryEntry.m_data->extraFilePaths; + + return stream; +} + +QDataStream &operator>>(QDataStream &stream, ItemLibraryEntry &itemLibraryEntry) +{ + // Clear containers so that we don't simply append to them in case the object is reused + itemLibraryEntry.m_data->hints.clear(); + itemLibraryEntry.m_data->properties.clear(); + + stream >> itemLibraryEntry.m_data->name; + stream >> itemLibraryEntry.m_data->typeName; + stream >> itemLibraryEntry.m_data->majorVersion; + stream >> itemLibraryEntry.m_data->minorVersion; + stream >> itemLibraryEntry.m_data->typeIcon; + stream >> itemLibraryEntry.m_data->libraryEntryIconPath; + stream >> itemLibraryEntry.m_data->category; + stream >> itemLibraryEntry.m_data->requiredImport; + stream >> itemLibraryEntry.m_data->hints; + + stream >> itemLibraryEntry.m_data->properties; + stream >> itemLibraryEntry.m_data->qml; + stream >> itemLibraryEntry.m_data->qmlSource; + stream >> itemLibraryEntry.m_data->customComponentSource; + stream >> itemLibraryEntry.m_data->extraFilePaths; + + return stream; +} + +QDebug operator<<(QDebug debug, const ItemLibraryEntry &itemLibraryEntry) +{ + debug << itemLibraryEntry.m_data->name; + debug << itemLibraryEntry.m_data->typeName; + debug << itemLibraryEntry.m_data->majorVersion; + debug << itemLibraryEntry.m_data->minorVersion; + debug << itemLibraryEntry.m_data->typeIcon; + debug << itemLibraryEntry.m_data->libraryEntryIconPath; + debug << itemLibraryEntry.m_data->category; + debug << itemLibraryEntry.m_data->requiredImport; + debug << itemLibraryEntry.m_data->hints; + + debug << itemLibraryEntry.m_data->properties; + debug << itemLibraryEntry.m_data->qml; + debug << itemLibraryEntry.m_data->qmlSource; + debug << itemLibraryEntry.m_data->customComponentSource; + debug << itemLibraryEntry.m_data->extraFilePaths; + + return debug.space(); +} + +QList toItemLibraryEntries(const Storage::Info::ItemLibraryEntries &entries, + const ProjectStorageType &projectStorage) +{ + return Utils::transform>(entries, [&](const auto &entry) { + return ItemLibraryEntry{entry, projectStorage}; + }); +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/metainfo/itemlibraryinfo.cpp b/src/plugins/qmldesigner/designercore/metainfo/itemlibraryinfo.cpp index 9f542826156..9f8b43f9209 100644 --- a/src/plugins/qmldesigner/designercore/metainfo/itemlibraryinfo.cpp +++ b/src/plugins/qmldesigner/designercore/metainfo/itemlibraryinfo.cpp @@ -2,280 +2,12 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "../include/itemlibraryinfo.h" -#include "nodemetainfo.h" -#include "qregularexpression.h" #include #include -#include - -#include -#include - namespace QmlDesigner { -namespace Internal { - -class ItemLibraryEntryData -{ -public: - QString name; - TypeName typeName; - QString category; - int majorVersion{-1}; - int minorVersion{-1}; - QString libraryEntryIconPath; - QIcon typeIcon = QIcon(":/ItemLibrary/images/item-default-icon.png"); - QList properties; - QString qml; - QString qmlSource; - QString requiredImport; - QHash hints; - QString customComponentSource; - QStringList extraFilePaths; - QString toolTip; -}; - -} // namespace Internal - -void ItemLibraryEntry::setTypeIcon(const QIcon &icon) -{ - m_data->typeIcon = icon; -} - -void ItemLibraryEntry::addProperty(const Property &property) -{ - m_data->properties.append(property); -} - -QList ItemLibraryEntry::properties() const -{ - return m_data->properties; -} - -QHash ItemLibraryEntry::hints() const -{ - return m_data->hints; -} - -ItemLibraryEntry::ItemLibraryEntry() : m_data(new Internal::ItemLibraryEntryData) -{ - m_data->name.clear(); -} - -QString ItemLibraryEntry::name() const -{ - return m_data->name; -} - -TypeName ItemLibraryEntry::typeName() const -{ - return m_data->typeName; -} - -QString ItemLibraryEntry::qmlPath() const -{ - return m_data->qml; -} - -QString ItemLibraryEntry::qmlSource() const -{ - return m_data->qmlSource; -} - -QString ItemLibraryEntry::requiredImport() const -{ - return m_data->requiredImport; -} - -QString ItemLibraryEntry::customComponentSource() const -{ - return m_data->customComponentSource; -} - -QStringList ItemLibraryEntry::extraFilePaths() const -{ - return m_data->extraFilePaths; -} - -QString ItemLibraryEntry::toolTip() const -{ - return m_data->toolTip; -} - -int ItemLibraryEntry::majorVersion() const -{ - return m_data->majorVersion; -} - -int ItemLibraryEntry::minorVersion() const -{ - return m_data->minorVersion; -} - -QString ItemLibraryEntry::category() const -{ - return m_data->category; -} - -void ItemLibraryEntry::setCategory(const QString &category) -{ - m_data->category = category; -} - -QIcon ItemLibraryEntry::typeIcon() const -{ - return m_data->typeIcon; -} - -QString ItemLibraryEntry::libraryEntryIconPath() const -{ - return m_data->libraryEntryIconPath; -} - -void ItemLibraryEntry::setName(const QString &name) -{ - m_data->name = name; -} - -void ItemLibraryEntry::setType(const TypeName &typeName, int majorVersion, int minorVersion) -{ - m_data->typeName = typeName; - m_data->majorVersion = majorVersion; - m_data->minorVersion = minorVersion; -} - -void ItemLibraryEntry::setLibraryEntryIconPath(const QString &iconPath) -{ - m_data->libraryEntryIconPath = iconPath; -} - -static QByteArray getSourceForUrl(const QString &fileURl) -{ - Utils::FileReader fileReader; - - if (fileReader.fetch(Utils::FilePath::fromString(fileURl))) - return fileReader.data(); - else - return Utils::FileReader::fetchQrc(fileURl); -} - -void ItemLibraryEntry::setQmlPath(const QString &qml) -{ - m_data->qml = qml; - - m_data->qmlSource = QString::fromUtf8(getSourceForUrl(qml)); -} - -void ItemLibraryEntry::setRequiredImport(const QString &requiredImport) -{ - m_data->requiredImport = requiredImport; -} - -void ItemLibraryEntry::setToolTip(const QString &tooltip) -{ - static QRegularExpression regularExpressionPattern(QLatin1String("^qsTr\\(\"(.*)\"\\)$")); - const QRegularExpressionMatch match = regularExpressionPattern.match(tooltip); - if (match.hasMatch()) - m_data->toolTip = match.captured(1); - else - m_data->toolTip = tooltip; -} - -void ItemLibraryEntry::addHints(const QHash &hints) -{ - Utils::addToHash(&m_data->hints, hints); -} - -void ItemLibraryEntry::setCustomComponentSource(const QString &source) -{ - m_data->customComponentSource = source; -} - -void ItemLibraryEntry::addExtraFilePath(const QString &extraFile) -{ - m_data->extraFilePaths.append(extraFile); -} - -void ItemLibraryEntry::addProperty(PropertyName &name, QString &type, QVariant &value) -{ - Property property; - property.set(name, type, value); - addProperty(property); -} - -QDataStream& operator<<(QDataStream& stream, const ItemLibraryEntry &itemLibraryEntry) -{ - stream << itemLibraryEntry.name(); - stream << itemLibraryEntry.typeName(); - stream << itemLibraryEntry.majorVersion(); - stream << itemLibraryEntry.minorVersion(); - stream << itemLibraryEntry.typeIcon(); - stream << itemLibraryEntry.libraryEntryIconPath(); - stream << itemLibraryEntry.category(); - stream << itemLibraryEntry.requiredImport(); - stream << itemLibraryEntry.hints(); - - stream << itemLibraryEntry.m_data->properties; - stream << itemLibraryEntry.m_data->qml; - stream << itemLibraryEntry.m_data->qmlSource; - stream << itemLibraryEntry.m_data->customComponentSource; - stream << itemLibraryEntry.m_data->extraFilePaths; - - return stream; -} - -QDataStream& operator>>(QDataStream& stream, ItemLibraryEntry &itemLibraryEntry) -{ - // Clear containers so that we don't simply append to them in case the object is reused - itemLibraryEntry.m_data->hints.clear(); - itemLibraryEntry.m_data->properties.clear(); - - stream >> itemLibraryEntry.m_data->name; - stream >> itemLibraryEntry.m_data->typeName; - stream >> itemLibraryEntry.m_data->majorVersion; - stream >> itemLibraryEntry.m_data->minorVersion; - stream >> itemLibraryEntry.m_data->typeIcon; - stream >> itemLibraryEntry.m_data->libraryEntryIconPath; - stream >> itemLibraryEntry.m_data->category; - stream >> itemLibraryEntry.m_data->requiredImport; - stream >> itemLibraryEntry.m_data->hints; - - stream >> itemLibraryEntry.m_data->properties; - stream >> itemLibraryEntry.m_data->qml; - stream >> itemLibraryEntry.m_data->qmlSource; - stream >> itemLibraryEntry.m_data->customComponentSource; - stream >> itemLibraryEntry.m_data->extraFilePaths; - - return stream; -} - -QDebug operator<<(QDebug debug, const ItemLibraryEntry &itemLibraryEntry) -{ - debug << itemLibraryEntry.m_data->name; - debug << itemLibraryEntry.m_data->typeName; - debug << itemLibraryEntry.m_data->majorVersion; - debug << itemLibraryEntry.m_data->minorVersion; - debug << itemLibraryEntry.m_data->typeIcon; - debug << itemLibraryEntry.m_data->libraryEntryIconPath; - debug << itemLibraryEntry.m_data->category; - debug << itemLibraryEntry.m_data->requiredImport; - debug << itemLibraryEntry.m_data->hints; - - debug << itemLibraryEntry.m_data->properties; - debug << itemLibraryEntry.m_data->qml; - debug << itemLibraryEntry.m_data->qmlSource; - debug << itemLibraryEntry.m_data->customComponentSource; - debug << itemLibraryEntry.m_data->extraFilePaths; - - return debug.space(); -} - -// -// ItemLibraryInfo -// - ItemLibraryInfo::ItemLibraryInfo(QObject *parent) : QObject(parent) { @@ -306,7 +38,7 @@ QList ItemLibraryInfo::entries() const return list; } -static inline QString keyForEntry(const ItemLibraryEntry &entry) +inline static QString keyForEntry(const ItemLibraryEntry &entry) { return entry.name() + entry.category() + QString::number(entry.majorVersion()); } diff --git a/src/plugins/qmldesigner/designercore/metainfo/nodehints.cpp b/src/plugins/qmldesigner/designercore/metainfo/nodehints.cpp index 8cdc590135f..149bb768777 100644 --- a/src/plugins/qmldesigner/designercore/metainfo/nodehints.cpp +++ b/src/plugins/qmldesigner/designercore/metainfo/nodehints.cpp @@ -6,10 +6,11 @@ #include "metainfo.h" #include -#include -#include +#include #include #include +#include +#include #include @@ -21,7 +22,9 @@ #include -#include +#ifndef QDS_USE_PROJECTSTORAGE +# include +#endif #include @@ -65,8 +68,15 @@ static QVariant evaluateExpression(const QString &expression, const ModelNode &m } //Internal -QmlDesigner::NodeHints::NodeHints(const ModelNode &node) : m_modelNode(node) +QmlDesigner::NodeHints::NodeHints(const ModelNode &node) +#ifdef QDS_USE_PROJECTSTORAGE + : NodeHints{node.metaInfo()} +#else + : m_modelNode(node) +#endif { +#ifndef QDS_USE_PROJECTSTORAGE + if (!isValid()) return; @@ -92,13 +102,31 @@ QmlDesigner::NodeHints::NodeHints(const ModelNode &node) : m_modelNode(node) } } +#endif +} + +NodeHints::NodeHints(const NodeMetaInfo &metaInfo) +{ + for (const auto &[name, expression] : metaInfo.typeHints()) + m_hints.insert(name.toQString(), expression.toQString()); } NodeHints::NodeHints(const ItemLibraryEntry &entry) +#ifdef QDS_USE_PROJECTSTORAGE + : NodeHints{entry.metaInfo()} +#endif { - m_hints = entry.hints(); + if constexpr (!useProjectStorage()) + m_hints = entry.hints(); } +namespace { +bool convert(FlagIs flagIs) +{ + return flagIs == FlagIs::True ? true : false; +} +} // namespace + bool NodeHints::canBeContainerFor(const ModelNode &potenialChild) const { /* The default is true for now to avoid confusion. Once our .metaInfo files in Qt @@ -107,6 +135,11 @@ bool NodeHints::canBeContainerFor(const ModelNode &potenialChild) const if (!isValid()) return true; + auto flagIs = m_modelNode.metaInfo().canBeContainer(); + + if (flagIs != FlagIs::Set) + return convert(flagIs); + return evaluateBooleanExpression("canBeContainer", true, potenialChild); } @@ -118,6 +151,11 @@ bool NodeHints::forceClip() const if (isSwipeView(modelNode())) return true; + auto flagIs = m_modelNode.metaInfo().forceClip(); + + if (flagIs != FlagIs::Set) + return convert(flagIs); + return evaluateBooleanExpression("forceClip", false); } @@ -129,21 +167,50 @@ bool NodeHints::doesLayoutChildren() const if (isSwipeView(modelNode())) return true; + auto flagIs = m_modelNode.metaInfo().doesLayoutChildren(); + + if (flagIs != FlagIs::Set) + return convert(flagIs); + return evaluateBooleanExpression("doesLayoutChildren", false); } bool NodeHints::canBeDroppedInFormEditor() const { + if (!isValid()) + return true; + + auto flagIs = m_modelNode.metaInfo().canBeDroppedInFormEditor(); + + if (flagIs != FlagIs::Set) + return convert(flagIs); + return evaluateBooleanExpression("canBeDroppedInFormEditor", true); } bool NodeHints::canBeDroppedInNavigator() const { + if (!isValid()) + return true; + + auto flagIs = m_modelNode.metaInfo().canBeDroppedInNavigator(); + + if (flagIs != FlagIs::Set) + return convert(flagIs); + return evaluateBooleanExpression("canBeDroppedInNavigator", true); } bool NodeHints::canBeDroppedInView3D() const { + if (!isValid()) + return false; + + auto flagIs = m_modelNode.metaInfo().canBeDroppedInView3D(); + + if (flagIs != FlagIs::Set) + return convert(flagIs); + return evaluateBooleanExpression("canBeDroppedInView3D", false); } @@ -152,16 +219,37 @@ bool NodeHints::isMovable() const if (!isValid()) return true; + auto flagIs = m_modelNode.metaInfo().isMovable(); + + if (flagIs != FlagIs::Set) + return convert(flagIs); + return evaluateBooleanExpression("isMovable", true); } bool NodeHints::isResizable() const { + if (!isValid()) + return true; + + auto flagIs = m_modelNode.metaInfo().isResizable(); + + if (flagIs != FlagIs::Set) + return convert(flagIs); + return evaluateBooleanExpression("isResizable", true); } bool NodeHints::hasFormEditorItem() const { + if (!isValid()) + return true; + + auto flagIs = m_modelNode.metaInfo().hasFormEditorItem(); + + if (flagIs != FlagIs::Set) + return convert(flagIs); + return evaluateBooleanExpression("hasFormEditorItem", true); } @@ -173,6 +261,11 @@ bool NodeHints::isStackedContainer() const if (isSwipeView(modelNode())) return true; + auto flagIs = m_modelNode.metaInfo().isStackedContainer(); + + if (flagIs != FlagIs::Set) + return convert(flagIs); + return evaluateBooleanExpression("isStackedContainer", false); } @@ -215,6 +308,11 @@ bool NodeHints::takesOverRenderingOfChildren() const if (!isValid()) return false; + auto flagIs = m_modelNode.metaInfo().takesOverRenderingOfChildren(); + + if (flagIs != FlagIs::Set) + return convert(flagIs); + return evaluateBooleanExpression("takesOverRenderingOfChildren", false); } @@ -223,11 +321,24 @@ bool NodeHints::visibleInNavigator() const if (!isValid()) return false; + auto flagIs = m_modelNode.metaInfo().visibleInNavigator(); + + if (flagIs != FlagIs::Set) + return convert(flagIs); + return evaluateBooleanExpression("visibleInNavigator", false); } bool NodeHints::visibleInLibrary() const { + if (!isValid()) + return true; + + auto flagIs = m_modelNode.metaInfo().visibleInLibrary(); + + if (flagIs != FlagIs::Set) + return convert(flagIs); + return evaluateBooleanExpression("visibleInLibrary", true); } @@ -297,7 +408,7 @@ NodeHints NodeHints::fromItemLibraryEntry(const ItemLibraryEntry &entry) return NodeHints(entry); } -ModelNode NodeHints::modelNode() const +const ModelNode &NodeHints::modelNode() const { return m_modelNode; } diff --git a/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp b/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp index 5ed270bf8cb..d21d8b44f86 100644 --- a/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp +++ b/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp @@ -1478,12 +1478,12 @@ MetaInfoType NodeMetaInfo::type() const { if constexpr (useProjectStorage()) { if (isValid()) { - switch (typeData().traits) { - case Storage::TypeTraits::Reference: + switch (typeData().traits.kind) { + case Storage::TypeTraitsKind::Reference: return MetaInfoType::Reference; - case Storage::TypeTraits::Value: + case Storage::TypeTraitsKind::Value: return MetaInfoType::Value; - case Storage::TypeTraits::Sequence: + case Storage::TypeTraitsKind::Sequence: return MetaInfoType::Sequence; default: break; @@ -1497,7 +1497,7 @@ MetaInfoType NodeMetaInfo::type() const bool NodeMetaInfo::isFileComponent() const { if constexpr (useProjectStorage()) - return isValid() && bool(typeData().traits & Storage::TypeTraits::IsFileComponent); + return isValid() && typeData().traits.isFileComponent; else return isValid() && m_privateData->isFileComponent(); } @@ -1505,7 +1505,7 @@ bool NodeMetaInfo::isFileComponent() const bool NodeMetaInfo::isProjectComponent() const { if constexpr (useProjectStorage()) { - return isValid() && bool(typeData().traits & Storage::TypeTraits::IsProjectComponent); + return isValid() && typeData().traits.isProjectComponent; } return false; @@ -1514,12 +1514,168 @@ bool NodeMetaInfo::isProjectComponent() const bool NodeMetaInfo::isInProjectModule() const { if constexpr (useProjectStorage()) { - return isValid() && bool(typeData().traits & Storage::TypeTraits::IsInProjectModule); + return isValid() && typeData().traits.isInProjectModule; } return false; } +FlagIs NodeMetaInfo::canBeContainer() const +{ + if constexpr (useProjectStorage()) { + if (isValid()) + return typeData().traits.canBeContainer; + + return FlagIs::False; + } + + return FlagIs::Set; +} + +FlagIs NodeMetaInfo::forceClip() const +{ + if constexpr (useProjectStorage()) { + if (isValid()) + return typeData().traits.forceClip; + + return FlagIs::False; + } + + return FlagIs::Set; +} + +FlagIs NodeMetaInfo::doesLayoutChildren() const +{ + if constexpr (useProjectStorage()) { + if (isValid()) + return typeData().traits.doesLayoutChildren; + + return FlagIs::False; + } + + return FlagIs::Set; +} + +FlagIs NodeMetaInfo::canBeDroppedInFormEditor() const +{ + if constexpr (useProjectStorage()) { + if (isValid()) + return typeData().traits.canBeDroppedInFormEditor; + + return FlagIs::False; + } + + return FlagIs::Set; +} + +FlagIs NodeMetaInfo::canBeDroppedInNavigator() const +{ + if constexpr (useProjectStorage()) { + if (isValid()) + return typeData().traits.canBeDroppedInNavigator; + + return FlagIs::False; + } + + return FlagIs::Set; +} + +FlagIs NodeMetaInfo::canBeDroppedInView3D() const +{ + if constexpr (useProjectStorage()) { + if (isValid()) + return typeData().traits.canBeDroppedInView3D; + + return FlagIs::False; + } + + return FlagIs::Set; +} + +FlagIs NodeMetaInfo::isMovable() const +{ + if constexpr (useProjectStorage()) { + if (isValid()) + return typeData().traits.isMovable; + + return FlagIs::False; + } + + return FlagIs::Set; +} + +FlagIs NodeMetaInfo::isResizable() const +{ + if constexpr (useProjectStorage()) { + if (isValid()) + return typeData().traits.isResizable; + + return FlagIs::False; + } + + return FlagIs::Set; +} + +FlagIs NodeMetaInfo::hasFormEditorItem() const +{ + if constexpr (useProjectStorage()) { + if (isValid()) + return typeData().traits.hasFormEditorItem; + + return FlagIs::False; + } + + return FlagIs::Set; +} + +FlagIs NodeMetaInfo::isStackedContainer() const +{ + if constexpr (useProjectStorage()) { + if (isValid()) + return typeData().traits.isStackedContainer; + + return FlagIs::False; + } + + return FlagIs::Set; +} + +FlagIs NodeMetaInfo::takesOverRenderingOfChildren() const +{ + if constexpr (useProjectStorage()) { + if (isValid()) + return typeData().traits.takesOverRenderingOfChildren; + + return FlagIs::False; + } + + return FlagIs::Set; +} + +FlagIs NodeMetaInfo::visibleInNavigator() const +{ + if constexpr (useProjectStorage()) { + if (isValid()) + return typeData().traits.visibleInNavigator; + + return FlagIs::False; + } + + return FlagIs::Set; +} + +FlagIs NodeMetaInfo::visibleInLibrary() const +{ + if constexpr (useProjectStorage()) { + if (isValid()) + return typeData().traits.visibleInLibrary; + + return FlagIs::False; + } + + return FlagIs::Set; +} + namespace { [[maybe_unused]] auto propertyId(const ProjectStorageType &projectStorage, @@ -1820,6 +1976,36 @@ Storage::Info::ExportedTypeNames NodeMetaInfo::exportedTypeNamesForSourceId(Sour return {}; } +Storage::Info::TypeHints NodeMetaInfo::typeHints() const +{ + if constexpr (useProjectStorage()) { + if (isValid()) + return m_projectStorage->typeHints(m_typeId); + } + + return {}; +} + +Utils::PathString NodeMetaInfo::iconPath() const +{ + if constexpr (useProjectStorage()) { + if (isValid()) + return m_projectStorage->typeIconPath(m_typeId); + } + + return {}; +} + +Storage::Info::ItemLibraryEntries NodeMetaInfo::itemLibrariesEntries() const +{ + if constexpr (useProjectStorage()) { + if (isValid()) + return m_projectStorage->itemLibraryEntries(m_typeId); + } + + return {}; +} + SourceId NodeMetaInfo::sourceId() const { if constexpr (useProjectStorage()) { @@ -2230,7 +2416,7 @@ bool NodeMetaInfo::isView() const bool NodeMetaInfo::usesCustomParser() const { if constexpr (useProjectStorage()) { - return isValid() && bool(typeData().traits & Storage::TypeTraits::UsesCustomParser); + return isValid() && typeData().traits.usesCustomParser; } else { if (!isValid()) return false; @@ -3129,7 +3315,7 @@ bool NodeMetaInfo::isQtQuick3DEffect() const bool NodeMetaInfo::isEnumeration() const { if constexpr (useProjectStorage()) - return isValid() && bool(typeData().traits & Storage::TypeTraits::IsEnum); + return isValid() && typeData().traits.isEnum; return false; } @@ -3402,6 +3588,19 @@ NodeMetaInfo NodeMetaInfo::commonBase(const NodeMetaInfo &metaInfo) const return {}; } +NodeMetaInfo::NodeMetaInfos NodeMetaInfo::heirs() const +{ + if constexpr (useProjectStorage()) { + if (isValid()) { + return Utils::transform(m_projectStorage->heirIds(m_typeId), [&](TypeId typeId) { + return NodeMetaInfo{typeId, m_projectStorage}; + }); + } + } + + return {}; +} + namespace { void addCompoundProperties(CompoundPropertyMetaInfos &inflatedProperties, diff --git a/src/plugins/qmldesigner/designercore/model/abstractview.cpp b/src/plugins/qmldesigner/designercore/model/abstractview.cpp index a982d8dc5e5..ff332ee3b2d 100644 --- a/src/plugins/qmldesigner/designercore/model/abstractview.cpp +++ b/src/plugins/qmldesigner/designercore/model/abstractview.cpp @@ -129,12 +129,6 @@ WidgetInfo AbstractView::createWidgetInfo(QWidget *widget, return widgetInfo; } -// Returns the model of the view. -Model* AbstractView::model() const -{ - return m_model.data(); -} - bool AbstractView::isAttached() const { return model(); diff --git a/src/plugins/qmldesigner/designercore/model/model.cpp b/src/plugins/qmldesigner/designercore/model/model.cpp index 7b5868d6cf5..b85a0d8b403 100644 --- a/src/plugins/qmldesigner/designercore/model/model.cpp +++ b/src/plugins/qmldesigner/designercore/model/model.cpp @@ -17,7 +17,10 @@ #include "internalproperty.h" #include "internalsignalhandlerproperty.h" #include "internalvariantproperty.h" -#include "metainfo.h" +#include "itemlibraryentry.h" +#ifndef QDS_USE_PROJECTSTORAGE +# include "metainfo.h" +#endif #include "nodeinstanceview.h" #include "nodemetainfo.h" @@ -84,7 +87,7 @@ ModelPrivate::ModelPrivate(Model *model, m_currentTimelineNode = m_rootInternalNode; if constexpr (useProjectStorage()) - projectStorage->addRefreshCallback(&m_metaInfoRefreshCallback); + projectStorage->addObserver(this); } ModelPrivate::ModelPrivate(Model *model, @@ -108,7 +111,7 @@ ModelPrivate::ModelPrivate(Model *model, m_currentTimelineNode = m_rootInternalNode; if constexpr (useProjectStorage()) - projectStorage->addRefreshCallback(&m_metaInfoRefreshCallback); + projectStorage->addObserver(this); } ModelPrivate::ModelPrivate(Model *model, @@ -132,14 +135,11 @@ ModelPrivate::ModelPrivate(Model *model, ModelPrivate::~ModelPrivate() { if constexpr (useProjectStorage()) - projectStorage->removeRefreshCallback(&m_metaInfoRefreshCallback); + projectStorage->removeObserver(this); }; void ModelPrivate::detachAllViews() { - if constexpr (useProjectStorage()) - projectStorage->removeRefreshCallback(&m_metaInfoRefreshCallback); - for (const QPointer &view : std::as_const(m_viewList)) detachView(view.data(), true); @@ -394,11 +394,6 @@ void ModelPrivate::setTypeId(InternalNode *node, Utils::SmallStringView typeName } } -void ModelPrivate::emitRefreshMetaInfos(const TypeIds &deletedTypeIds) -{ - notifyNodeInstanceViewLast([&](AbstractView *view) { view->refreshMetaInfos(deletedTypeIds); }); -} - void ModelPrivate::handleResourceSet(const ModelResourceSet &resourceSet) { for (const ModelNode &node : resourceSet.removeModelNodes) { @@ -411,6 +406,11 @@ void ModelPrivate::handleResourceSet(const ModelResourceSet &resourceSet) setBindingProperties(resourceSet.setExpressions); } +void ModelPrivate::removedTypeIds(const TypeIds &removedTypeIds) +{ + notifyNodeInstanceViewLast([&](AbstractView *view) { view->refreshMetaInfos(removedTypeIds); }); +} + void ModelPrivate::removeAllSubNodes(const InternalNodePointer &node) { for (const InternalNodePointer &subNode : node->allSubNodes()) @@ -459,6 +459,7 @@ InternalNodePointer ModelPrivate::rootNode() const return m_rootInternalNode; } +#ifndef QDS_USE_PROJECTSTORAGE MetaInfo ModelPrivate::metaInfo() const { return m_metaInfo; @@ -468,6 +469,7 @@ void ModelPrivate::setMetaInfo(const MetaInfo &metaInfo) { m_metaInfo = metaInfo; } +#endif void ModelPrivate::changeNodeId(const InternalNodePointer &node, const QString &id) { @@ -2061,23 +2063,21 @@ void Model::setFileUrl(const QUrl &url) d->setFileUrl(url); } -/*! - \brief Returns list of QML types available within the model. - */ -const MetaInfo Model::metaInfo() const -{ - return d->metaInfo(); -} - bool Model::hasNodeMetaInfo(const TypeName &typeName, int majorVersion, int minorVersion) const { return metaInfo(typeName, majorVersion, minorVersion).isValid(); } +#ifndef QDS_USE_PROJECTSTORAGE +const MetaInfo Model::metaInfo() const +{ + return d->metaInfo(); +} void Model::setMetaInfo(const MetaInfo &metaInfo) { d->setMetaInfo(metaInfo); } +#endif NodeMetaInfo Model::boolMetaInfo() const { @@ -2287,6 +2287,45 @@ NodeMetaInfo Model::qtQuick3DNodeMetaInfo() const } } +NodeMetaInfo Model::qtQuick3DPointLightMetaInfo() const +{ + if constexpr (useProjectStorage()) { + using namespace Storage::Info; + return createNodeMetaInfo(); + } else { + return metaInfo("QtQuick3D.PointLight"); + } +} + +NodeMetaInfo Model::qtQuick3DSpotLightMetaInfo() const +{ + if constexpr (useProjectStorage()) { + using namespace Storage::Info; + return createNodeMetaInfo(); + } else { + return metaInfo("QtQuick3D.SpotLight"); + } +} + +NodeMetaInfo Model::qtQuick3DOrthographicCameraMetaInfo() const +{ + if constexpr (useProjectStorage()) { + using namespace Storage::Info; + return createNodeMetaInfo(); + } else { + return metaInfo("QtQuick3D.OrthographicCamera"); + } +} + +NodeMetaInfo Model::qtQuick3DPerspectiveCameraMetaInfo() const +{ + if constexpr (useProjectStorage()) { + using namespace Storage::Info; + return createNodeMetaInfo(); + } else { + return metaInfo("QtQuick3D.PerspectiveCamera"); + } +} NodeMetaInfo Model::qtQuick3DTextureMetaInfo() const { if constexpr (useProjectStorage()) { @@ -2327,6 +2366,16 @@ NodeMetaInfo Model::qtQuick3DDefaultMaterialMetaInfo() const } } +NodeMetaInfo Model::qtQuick3DDirectionalLightMetaInfo() const +{ + if constexpr (useProjectStorage()) { + using namespace Storage::Info; + return createNodeMetaInfo(); + } else { + return metaInfo("QtQuick3D.DirectionalLight"); + } +} + NodeMetaInfo Model::qtQuick3DPrincipledMaterialMetaInfo() const { if constexpr (useProjectStorage()) { @@ -2407,6 +2456,30 @@ NodeMetaInfo Model::vector4dMetaInfo() const } } +QVarLengthArray Model::metaInfosForModule(Module module) const +{ + if constexpr (useProjectStorage()) { + using namespace Storage::Info; + return Utils::transform>( + d->projectStorage->typeIds(module.id()), [&](TypeId id) { + return NodeMetaInfo{id, d->projectStorage}; + }); + } else { + return {}; + } +} + +QList Model::itemLibraryEntries() const +{ +#ifdef QDS_USE_PROJECTSTORAGE + using namespace Storage::Info; + return toItemLibraryEntries(d->projectStorage->itemLibraryEntries(d->m_sourceId), + *d->projectStorage); +#else + return d->metaInfo().itemLibraryInfo()->entries(); +#endif +} + NodeMetaInfo Model::qtQuickTimelineKeyframeGroupMetaInfo() const { if constexpr (useProjectStorage()) { @@ -2460,13 +2533,12 @@ NodeMetaInfo Model::metaInfo(Module module, Utils::SmallStringView typeName, Sto } } -/*! - \brief Returns list of QML types available within the model. - */ +#ifndef QDS_USE_PROJECTSTORAGE MetaInfo Model::metaInfo() { return d->metaInfo(); } +#endif Module Model::module(Utils::SmallStringView moduleName) { diff --git a/src/plugins/qmldesigner/designercore/model/model_p.h b/src/plugins/qmldesigner/designercore/model/model_p.h index d09e4054ca2..d7743b028e2 100644 --- a/src/plugins/qmldesigner/designercore/model/model_p.h +++ b/src/plugins/qmldesigner/designercore/model/model_p.h @@ -6,7 +6,9 @@ #include "qmldesignercorelib_global.h" #include "abstractview.h" -#include "metainfo.h" +#ifndef QDS_USE_PROJECTSTORAGE +# include "metainfo.h" +#endif #include "modelnode.h" #include "skipiterator.h" @@ -82,7 +84,8 @@ public: {} }; -class ModelPrivate : public QObject +class ModelPrivate : public QObject, + protected ProjectStorageObserver { Q_OBJECT @@ -137,8 +140,10 @@ public: InternalNodePointer rootNode() const; InternalNodePointer findNode(const QString &id) const; +#ifndef QDS_USE_PROJECTSTORAGE MetaInfo metaInfo() const; void setMetaInfo(const MetaInfo &metaInfo); +#endif void attachView(AbstractView *view); void detachView(AbstractView *view, bool notifyView); @@ -303,6 +308,9 @@ public: return m_nodeMetaInfoCache; } +protected: + void removedTypeIds(const TypeIds &removedTypeIds) override; + private: void removePropertyWithoutNotification(InternalProperty *property); void removeAllSubNodes(const InternalNodePointer &node); @@ -317,7 +325,6 @@ private: EnabledViewRange enabledViews() const; ImportedTypeNameId importedTypeNameId(Utils::SmallStringView typeName); void setTypeId(InternalNode *node, Utils::SmallStringView typeName); - void emitRefreshMetaInfos(const TypeIds &deletedTypeIds); public: NotNullPointer projectStorage = nullptr; @@ -325,9 +332,9 @@ public: private: Model *m_model = nullptr; +#ifndef QDS_USE_PROJECTSTORAGE MetaInfo m_metaInfo; - std::function m_metaInfoRefreshCallback{ - [&](const TypeIds &deletedTypeIds) { emitRefreshMetaInfos(deletedTypeIds); }}; +#endif Imports m_imports; Imports m_possibleImportList; Imports m_usedImportList; diff --git a/src/plugins/qmldesigner/designercore/model/modelnode.cpp b/src/plugins/qmldesigner/designercore/model/modelnode.cpp index 56f50223c9e..94d431dda6f 100644 --- a/src/plugins/qmldesigner/designercore/model/modelnode.cpp +++ b/src/plugins/qmldesigner/designercore/model/modelnode.cpp @@ -1368,6 +1368,15 @@ bool ModelNode::isComponent() const QIcon ModelNode::typeIcon() const { +#ifdef QDS_USE_PROJECTSTORAGE + if (!isValid()) + return QIcon(QStringLiteral(":/ItemLibrary/images/item-invalid-icon.png")); + + if (auto iconPath = metaInfo().iconPath(); iconPath.size()) + return QIcon(iconPath.toQString()); + else + return QIcon(QStringLiteral(":/ItemLibrary/images/item-default-icon.png")); +#else if (isValid()) { // if node has no own icon, search for it in the itemlibrary const ItemLibraryInfo *libraryInfo = model()->metaInfo().itemLibraryInfo(); @@ -1381,6 +1390,7 @@ QIcon ModelNode::typeIcon() const } return QIcon(QStringLiteral(":/ItemLibrary/images/item-invalid-icon.png")); +#endif } QString ModelNode::behaviorPropertyName() const diff --git a/src/plugins/qmldesigner/designercore/model/propertyparser.cpp b/src/plugins/qmldesigner/designercore/model/propertyparser.cpp index 107cc41b07a..788530c291a 100644 --- a/src/plugins/qmldesigner/designercore/model/propertyparser.cpp +++ b/src/plugins/qmldesigner/designercore/model/propertyparser.cpp @@ -5,7 +5,6 @@ #include #include -#include #include #include diff --git a/src/plugins/qmldesigner/designercore/model/qml3dnode.cpp b/src/plugins/qmldesigner/designercore/model/qml3dnode.cpp index 4db2567e36d..12befa942f1 100644 --- a/src/plugins/qmldesigner/designercore/model/qml3dnode.cpp +++ b/src/plugins/qmldesigner/designercore/model/qml3dnode.cpp @@ -5,13 +5,11 @@ #include "auxiliarydataproperties.h" #include "bindingproperty.h" #include "invalidmodelnodeexception.h" -#include "itemlibraryinfo.h" #include "nodehints.h" #include "nodelistproperty.h" #include "qmlanchors.h" #include "qmlchangeset.h" #include "variantproperty.h" -#include #include "plaintexteditmodifier.h" #include "rewriterview.h" diff --git a/src/plugins/qmldesigner/designercore/model/qmlchangeset.cpp b/src/plugins/qmldesigner/designercore/model/qmlchangeset.cpp index 889a5a38650..134a9a91375 100644 --- a/src/plugins/qmldesigner/designercore/model/qmlchangeset.cpp +++ b/src/plugins/qmldesigner/designercore/model/qmlchangeset.cpp @@ -6,7 +6,6 @@ #include "rewritertransaction.h" #include "variantproperty.h" #include "abstractview.h" -#include #include diff --git a/src/plugins/qmldesigner/designercore/model/qmlconnections.cpp b/src/plugins/qmldesigner/designercore/model/qmlconnections.cpp index 9c98399cb30..7a63e73d629 100644 --- a/src/plugins/qmldesigner/designercore/model/qmlconnections.cpp +++ b/src/plugins/qmldesigner/designercore/model/qmlconnections.cpp @@ -3,7 +3,6 @@ #include "qmlconnections.h" -#include #include #include #include diff --git a/src/plugins/qmldesigner/designercore/model/qmlitemnode.cpp b/src/plugins/qmldesigner/designercore/model/qmlitemnode.cpp index 175d3291183..0ce155ca6b9 100644 --- a/src/plugins/qmldesigner/designercore/model/qmlitemnode.cpp +++ b/src/plugins/qmldesigner/designercore/model/qmlitemnode.cpp @@ -2,14 +2,12 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "qmlitemnode.h" -#include #include "nodelistproperty.h" #include "nodehints.h" #include "nodeproperty.h" #include "variantproperty.h" #include "bindingproperty.h" #include "qmlanchors.h" -#include "itemlibraryinfo.h" #include #include diff --git a/src/plugins/qmldesigner/designercore/model/qmlstate.cpp b/src/plugins/qmldesigner/designercore/model/qmlstate.cpp index 7d681071088..55864745a96 100644 --- a/src/plugins/qmldesigner/designercore/model/qmlstate.cpp +++ b/src/plugins/qmldesigner/designercore/model/qmlstate.cpp @@ -5,7 +5,6 @@ #include "abstractview.h" #include #include -#include #include #include "bindingproperty.h" #include "qmlchangeset.h" diff --git a/src/plugins/qmldesigner/designercore/model/qmltimeline.cpp b/src/plugins/qmldesigner/designercore/model/qmltimeline.cpp index fc0e2d4f988..ae0f675d793 100644 --- a/src/plugins/qmldesigner/designercore/model/qmltimeline.cpp +++ b/src/plugins/qmldesigner/designercore/model/qmltimeline.cpp @@ -9,7 +9,6 @@ #include #include -#include #include #include diff --git a/src/plugins/qmldesigner/designercore/model/qmltimelinekeyframegroup.cpp b/src/plugins/qmldesigner/designercore/model/qmltimelinekeyframegroup.cpp index d40616a0226..7cee6095bb4 100644 --- a/src/plugins/qmldesigner/designercore/model/qmltimelinekeyframegroup.cpp +++ b/src/plugins/qmldesigner/designercore/model/qmltimelinekeyframegroup.cpp @@ -8,7 +8,6 @@ #include #include -#include #include #include diff --git a/src/plugins/qmldesigner/designercore/model/qmlvisualnode.cpp b/src/plugins/qmldesigner/designercore/model/qmlvisualnode.cpp index 6ccab365bd1..75db18b5a22 100644 --- a/src/plugins/qmldesigner/designercore/model/qmlvisualnode.cpp +++ b/src/plugins/qmldesigner/designercore/model/qmlvisualnode.cpp @@ -2,14 +2,13 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "qmlvisualnode.h" -#include -#include "qmlchangeset.h" -#include "nodelistproperty.h" -#include "nodehints.h" -#include "variantproperty.h" #include "bindingproperty.h" +#include "itemlibraryentry.h" +#include "nodehints.h" +#include "nodelistproperty.h" #include "qmlanchors.h" -#include "itemlibraryinfo.h" +#include "qmlchangeset.h" +#include "variantproperty.h" #include diff --git a/src/plugins/qmldesigner/designercore/projectstorage/commontypecache.h b/src/plugins/qmldesigner/designercore/projectstorage/commontypecache.h index e7b41c559e3..41f894356ab 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/commontypecache.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/commontypecache.h @@ -38,6 +38,7 @@ inline constexpr char Control[] = "Control"; inline constexpr char CubeMapTexture[] = "CubeMapTexture"; inline constexpr char DefaultMaterial[] = "DefaultMaterial"; inline constexpr char Dialog[] = "Dialog"; +inline constexpr char DirectionalLight[] = "DirectionalLight"; inline constexpr char DoubleType[] = "double"; inline constexpr char Effect[] = "Effect"; inline constexpr char FloatType[] = "float"; @@ -52,12 +53,12 @@ inline constexpr char GroupItem[] = "GroupItem"; inline constexpr char Image[] = "Image"; inline constexpr char InstanceListEntry[] = "InstanceListEntry"; inline constexpr char InstanceList[] = "InstanceList"; -inline constexpr char Light[] = "Light"; inline constexpr char IntType[] = "int"; inline constexpr char Item[] = "Item"; inline constexpr char KeyframeGroup[] = "KeyframeGroup"; inline constexpr char Keyframe[] = "Keyframe"; inline constexpr char Layout[] = "Layout"; +inline constexpr char Light[] = "Light"; inline constexpr char ListElement[] = "ListElement"; inline constexpr char ListModel[] = "ListModel"; inline constexpr char ListView[] = "ListView"; @@ -66,17 +67,19 @@ inline constexpr char Material[] = "Material"; inline constexpr char Model[] = "Model"; inline constexpr char MouseArea[] = "MouseArea"; inline constexpr char Node[] = "Node"; +inline constexpr char OrthographicCamera[] = "OrthographicCamera"; inline constexpr char Particle3D[] = "Particle3D"; inline constexpr char ParticleEmitter3D[] = "ParticleEmitter3D"; inline constexpr char Pass[] = "Pass"; inline constexpr char PathView[] = "PathView"; inline constexpr char Path[] = "Path"; inline constexpr char PauseAnimation[] = "PauseAnimation"; +inline constexpr char PerspectiveCamera[] = "PerspectiveCamera"; inline constexpr char Picture[] = "Picture"; +inline constexpr char PointLight[] = "PointLight"; inline constexpr char Popup[] = "Popup"; inline constexpr char Positioner[] = "Positioner"; inline constexpr char PrincipledMaterial[] = "PrincipledMaterial"; -inline constexpr char SpecularGlossyMaterial[] = "SpecularGlossyMaterial"; inline constexpr char PropertyAnimation[] = "PropertyAnimation"; inline constexpr char PropertyChanges[] = "PropertyChanges"; inline constexpr char QML[] = "QML"; @@ -91,7 +94,6 @@ inline constexpr char QtQuick3D[] = "QtQuick3D"; inline constexpr char QtQuick3D_Particles3D[] = "QtQuick3D.Particles3D"; inline constexpr char QtQuick3D_Particles3D_cppnative[] = "QtQuick3D.Particles3D-cppnative"; inline constexpr char QtQuick[] = "QtQuick"; -inline constexpr char QtQuick_cppnative[] = "QtQuick-cppnative"; inline constexpr char QtQuick_Controls[] = "QtQuick.Controls"; inline constexpr char QtQuick_Dialogs[] = "QtQuick.Dialogs"; inline constexpr char QtQuick_Extras[] = "QtQuick.Extras"; @@ -100,6 +102,7 @@ inline constexpr char QtQuick_Studio_Components[] = "QtQuick.Studio.Components"; inline constexpr char QtQuick_Templates[] = "QtQuick.Templates"; inline constexpr char QtQuick_Timeline[] = "QtQuick.Timeline"; inline constexpr char QtQuick_Window[] = "QtQuick.Window"; +inline constexpr char QtQuick_cppnative[] = "QtQuick-cppnative"; inline constexpr char Qt_SafeRenderer[] = "Qt.SafeRenderer"; inline constexpr char Rectangle[] = "Rectangle"; inline constexpr char Repeater[] = "Repeater"; @@ -108,7 +111,9 @@ inline constexpr char SafeRendererPicture[] = "SafeRendererPicture"; inline constexpr char SceneEnvironment[] = "SceneEnvironment"; inline constexpr char Shader[] = "Shader"; inline constexpr char SoundEffect[] = "SoundEffect"; +inline constexpr char SpecularGlossyMaterial[] = "SpecularGlossyMaterial"; inline constexpr char SplitView[] = "SplitView"; +inline constexpr char SpotLight[] = "SpotLight"; inline constexpr char SpriteParticle3D[] = "SpriteParticle3D"; inline constexpr char StateGroup[] = "StateGroup"; inline constexpr char State[] = "State"; @@ -201,6 +206,7 @@ class CommonTypeCache CacheType, CacheType, CacheType, + CacheType, CacheType, CacheType, CacheType, @@ -208,11 +214,15 @@ class CommonTypeCache CacheType, CacheType, CacheType, + CacheType, CacheType, + CacheType, + CacheType, CacheType, CacheType, - CacheType, CacheType, + CacheType, + CacheType, CacheType, CacheType, CacheType, @@ -262,6 +272,12 @@ public: updateTypeIdsWithoutProperties(); } + void clearForTestsOnly() + { + std::apply([](auto &...type) { ((type.typeId = QmlDesigner::TypeId{}), ...); }, m_types); + std::fill(std::begin(m_typesWithoutProperties), std ::end(m_typesWithoutProperties), TypeId{}); + } + template TypeId typeId() const { diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h index cc4de52663e..f2bdaa4a26c 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h @@ -86,6 +86,8 @@ public: relinkablePrototypes, relinkableExtensions, package.updatedSourceIds); + synchronizeTypeAnnotations(package.typeAnnotations, + package.updatedTypeAnnotationSourceIds); synchronizePropertyEditorQmlPaths(package.propertyEditorQmlPaths, package.updatedPropertyEditorQmlPathSourceIds); @@ -123,15 +125,11 @@ public: }); } - void addRefreshCallback(std::function *callback) override - { - m_refreshCallbacks.push_back(callback); - } + void addObserver(ProjectStorageObserver *observer) override { observers.push_back(observer); } - void removeRefreshCallback(std::function *callback) override + void removeObserver(ProjectStorageObserver *observer) override { - m_refreshCallbacks.erase( - std::find(m_refreshCallbacks.begin(), m_refreshCallbacks.end(), callback)); + observers.removeOne(observer); } ModuleId moduleId(Utils::SmallStringView moduleName) const override @@ -171,6 +169,12 @@ public: return Sqlite::withDeferredTransaction(database, [&] { return fetchTypeId(typeNameId); }); } + QVarLengthArray typeIds(ModuleId moduleId) const override + { + return selectTypeIdsByModuleIdStatement + .template valuesWithTransaction>(moduleId); + } + Storage::Info::ExportedTypeNames exportedTypeNames(TypeId typeId) const override { return selectExportedTypesByTypeIdStatement @@ -251,6 +255,98 @@ public: typeId); } + Utils::PathString typeIconPath(TypeId typeId) const override + { + return selectTypeIconPathStatement.template valueWithTransaction(typeId); + } + + Storage::Info::TypeHints typeHints(TypeId typeId) const override + { + return selectTypeHintsStatement.template valuesWithTransaction( + typeId); + } + + Storage::Info::ItemLibraryEntries itemLibraryEntries(TypeId typeId) const override + { + using Storage::Info::ItemLibraryProperties; + Storage::Info::ItemLibraryEntries entries; + + auto callback = [&](TypeId typeId_, + Utils::SmallStringView name, + Utils::SmallStringView iconPath, + Utils::SmallStringView category, + Utils::SmallStringView import, + Utils::SmallStringView toolTip, + Utils::SmallStringView properties, + Utils::SmallStringView extraFilePaths, + Utils::SmallStringView templatePath) { + auto &last = entries.emplace_back( + typeId_, name, iconPath, category, import, toolTip, templatePath); + if (properties.size()) + selectItemLibraryPropertiesStatement.readTo(last.properties, properties); + if (extraFilePaths.size()) + selectItemLibraryExtraFilePathsStatement.readTo(last.extraFilePaths, extraFilePaths); + }; + + selectItemLibraryEntriesByTypeIdStatement.readCallbackWithTransaction(callback, typeId); + + return entries; + } + + Storage::Info::ItemLibraryEntries itemLibraryEntries(SourceId sourceId) const override + { + using Storage::Info::ItemLibraryProperties; + Storage::Info::ItemLibraryEntries entries; + + auto callback = [&](TypeId typeId, + Utils::SmallStringView name, + Utils::SmallStringView iconPath, + Utils::SmallStringView category, + Utils::SmallStringView import, + Utils::SmallStringView toolTip, + Utils::SmallStringView properties, + Utils::SmallStringView extraFilePaths, + Utils::SmallStringView templatePath) { + auto &last = entries.emplace_back( + typeId, name, iconPath, category, import, toolTip, templatePath); + if (properties.size()) + selectItemLibraryPropertiesStatement.readTo(last.properties, properties); + if (extraFilePaths.size()) + selectItemLibraryExtraFilePathsStatement.readTo(last.extraFilePaths, extraFilePaths); + }; + + selectItemLibraryEntriesBySourceIdStatement.readCallbackWithTransaction(callback, sourceId); + + return entries; + } + + Storage::Info::ItemLibraryEntries allItemLibraryEntries() const override + { + using Storage::Info::ItemLibraryProperties; + Storage::Info::ItemLibraryEntries entries; + + auto callback = [&](TypeId typeId, + Utils::SmallStringView name, + Utils::SmallStringView iconPath, + Utils::SmallStringView category, + Utils::SmallStringView import, + Utils::SmallStringView toolTip, + Utils::SmallStringView properties, + Utils::SmallStringView extraFilePaths, + Utils::SmallStringView templatePath) { + auto &last = entries.emplace_back( + typeId, name, iconPath, category, import, toolTip, templatePath); + if (properties.size()) + selectItemLibraryPropertiesStatement.readTo(last.properties, properties); + if (extraFilePaths.size()) + selectItemLibraryExtraFilePathsStatement.readTo(last.extraFilePaths, extraFilePaths); + }; + + selectItemLibraryEntriesStatement.readCallbackWithTransaction(callback); + + return entries; + } + std::vector signalDeclarationNames(TypeId typeId) const override { return selectSignalDeclarationNamesForTypeStatement @@ -304,6 +400,11 @@ public: .template valuesWithTransaction(type); } + TypeIds heirIds(TypeId typeId) const override + { + return selectHeirTypeIdsStatement.template valuesWithTransaction(typeId); + } + template bool isBasedOn_(TypeId typeId, TypeIds... baseTypeIds) const { @@ -565,6 +666,13 @@ public: .template valuesWithTransaction(); } + void resetForTestsOnly() + { + database.clearAllTablesForTestsOnly(); + commonTypeCache_.clearForTestsOnly(); + moduleCache.clearForTestOnly(); + } + private: class ModuleStorageAdapter { @@ -624,8 +732,10 @@ private: void callRefreshMetaInfoCallback(const TypeIds &deletedTypeIds) { - for (auto *callback : m_refreshCallbacks) - (*callback)(deletedTypeIds); + if (deletedTypeIds.size()) { + for (ProjectStorageObserver *observer : observers) + observer->removedTypeIds(deletedTypeIds); + } } class AliasPropertyDeclaration @@ -768,6 +878,99 @@ private: sourceIds.erase(newEnd, sourceIds.end()); } + void synchronizeTypeTraits(TypeId typeId, Storage::TypeTraits traits) + { + updateTypeAnnotationTraitStatement.write(typeId, traits.annotation); + } + + class TypeAnnotationView + { + public: + TypeAnnotationView(TypeId typeId, + Utils::SmallStringView iconPath, + Utils::SmallStringView itemLibraryJson, + Utils::SmallStringView hintsJson) + : typeId{typeId} + , iconPath{iconPath} + , itemLibraryJson{itemLibraryJson} + , hintsJson{hintsJson} + {} + + public: + TypeId typeId; + Utils::SmallStringView iconPath; + Utils::SmallStringView itemLibraryJson; + Utils::PathString hintsJson; + }; + + void updateTypeIdInTypeAnnotations(Storage::Synchronization::TypeAnnotations &typeAnnotations) + { + for (auto &annotation : typeAnnotations) { + annotation.typeId = fetchTypeIdByModuleIdAndExportedName(annotation.moduleId, + annotation.typeName); + } + } + + void synchronizeTypeAnnotations(Storage::Synchronization::TypeAnnotations &typeAnnotations, + const SourceIds &updatedTypeAnnotationSourceIds) + { + using Storage::Synchronization::TypeAnnotation; + + updateTypeIdInTypeAnnotations(typeAnnotations); + + auto compareKey = [](auto &&first, auto &&second) { return first.typeId - second.typeId; }; + + std::sort(typeAnnotations.begin(), typeAnnotations.end(), [&](auto &&first, auto &&second) { + return first.typeId < second.typeId; + }); + + auto range = selectTypeAnnotationsForSourceIdsStatement.template range( + toIntegers(updatedTypeAnnotationSourceIds)); + + auto insert = [&](const TypeAnnotation &annotation) { + if (!annotation.sourceId) + throw TypeAnnotationHasInvalidSourceId{}; + + synchronizeTypeTraits(annotation.typeId, annotation.traits); + + insertTypeAnnotationStatement.write(annotation.typeId, + annotation.sourceId, + annotation.iconPath, + annotation.itemLibraryJson, + annotation.hintsJson); + }; + + auto update = [&](const TypeAnnotationView &annotationFromDatabase, + const TypeAnnotation &annotation) { + synchronizeTypeTraits(annotation.typeId, annotation.traits); + + if (annotationFromDatabase.iconPath != annotation.iconPath + || annotationFromDatabase.itemLibraryJson != annotation.itemLibraryJson + || annotationFromDatabase.hintsJson != annotation.hintsJson) { + updateTypeAnnotationStatement.write(annotation.typeId, + annotation.iconPath, + annotation.itemLibraryJson, + annotation.hintsJson); + return Sqlite::UpdateChange::Update; + } + + return Sqlite::UpdateChange::No; + }; + + auto remove = [&](const TypeAnnotationView &annotationFromDatabase) { + synchronizeTypeTraits(annotationFromDatabase.typeId, Storage::TypeTraits{}); + + deleteTypeAnnotationStatement.write(annotationFromDatabase.typeId); + }; + + Sqlite::insertUpdateDelete(range, typeAnnotations, compareKey, insert, update, remove); + } + + void synchronizeTypeTrait(const Storage::Synchronization::Type &type) + { + updateTypeTraitStatement.write(type.typeId, type.traits.type); + } + void synchronizeTypes(Storage::Synchronization::Types &types, TypeIds &updatedTypeIds, AliasPropertyDeclarations &insertedAliasPropertyDeclarations, @@ -792,6 +995,7 @@ private: throw TypeHasInvalidSourceId{}; TypeId typeId = declareType(type); + synchronizeTypeTrait(type); sourceIdsOfTypes.push_back(type.sourceId); updatedTypeIds.push_back(typeId); if (type.changeLevel != Storage::Synchronization::ChangeLevel::ExcludeExportedTypes) { @@ -1016,7 +1220,7 @@ private: void handleAliasPropertyDeclarationsWithPropertyType( TypeId typeId, AliasPropertyDeclarations &relinkableAliasPropertyDeclarations) { - auto callback = [&](TypeId typeId, + auto callback = [&](TypeId typeId_, PropertyDeclarationId propertyDeclarationId, ImportedTypeNameId propertyImportedTypeNameId, PropertyDeclarationId aliasPropertyDeclarationId, @@ -1029,7 +1233,7 @@ private: aliasPropertyDeclarationTailId); relinkableAliasPropertyDeclarations - .emplace_back(TypeId{typeId}, + .emplace_back(TypeId{typeId_}, PropertyDeclarationId{propertyDeclarationId}, ImportedTypeNameId{propertyImportedTypeNameId}, std::move(aliasPropertyName), @@ -1965,9 +2169,7 @@ private: return type.typeId; } - type.typeId = upsertTypeStatement.template value(type.sourceId, - type.typeName, - type.traits); + type.typeId = insertTypeStatement.template value(type.sourceId, type.typeName); if (!type.typeId) type.typeId = selectTypeIdBySourceIdAndNameStatement.template value(type.sourceId, @@ -2142,7 +2344,7 @@ private: { TypeId typeId; ImportedTypeNameId typeNameId; - if (!std::visit([](auto &&typeName) -> bool { return typeName.name.isEmpty(); }, typeName)) { + if (!std::visit([](auto &&typeName_) -> bool { return typeName_.name.isEmpty(); }, typeName)) { typeNameId = fetchImportedTypeNameId(typeName, sourceId); typeId = fetchTypeId(typeNameId); @@ -2430,6 +2632,7 @@ private: createFileStatusesTable(database); createProjectDatasTable(database); createPropertyEditorPathsTable(database); + createTypeAnnotionsTable(database); } database.setIsInitialized(true); } @@ -2478,21 +2681,23 @@ private: auto &sourceIdColumn = typesTable.addColumn("sourceId", Sqlite::StrictColumnType::Integer); auto &typesNameColumn = typesTable.addColumn("name", Sqlite::StrictColumnType::Text); typesTable.addColumn("traits", Sqlite::StrictColumnType::Integer); - typesTable.addForeignKeyColumn("prototypeId", - typesTable, - Sqlite::ForeignKeyAction::NoAction, - Sqlite::ForeignKeyAction::Restrict); + auto &prototypeIdColumn = typesTable.addForeignKeyColumn("prototypeId", + typesTable, + Sqlite::ForeignKeyAction::NoAction, + Sqlite::ForeignKeyAction::Restrict); typesTable.addColumn("prototypeNameId", Sqlite::StrictColumnType::Integer); - typesTable.addForeignKeyColumn("extensionId", - typesTable, - Sqlite::ForeignKeyAction::NoAction, - Sqlite::ForeignKeyAction::Restrict); + auto &extensionIdColumn = typesTable.addForeignKeyColumn("extensionId", + typesTable, + Sqlite::ForeignKeyAction::NoAction, + Sqlite::ForeignKeyAction::Restrict); typesTable.addColumn("extensionNameId", Sqlite::StrictColumnType::Integer); auto &defaultPropertyIdColumn = typesTable.addColumn("defaultPropertyId", Sqlite::StrictColumnType::Integer); - + typesTable.addColumn("annotationTraits", Sqlite::StrictColumnType::Integer); typesTable.addUniqueIndex({sourceIdColumn, typesNameColumn}); typesTable.addIndex({defaultPropertyIdColumn}); + typesTable.addIndex({prototypeIdColumn}); + typesTable.addIndex({extensionIdColumn}); typesTable.initialize(database); @@ -2779,6 +2984,25 @@ private: table.initialize(database); } + + void createTypeAnnotionsTable(Database &database) + { + Sqlite::StrictTable table; + table.setUseIfNotExists(true); + table.setUseWithoutRowId(true); + table.setName("typeAnnotations"); + auto &typeIdColumn = table.addColumn("typeId", + Sqlite::StrictColumnType::Integer, + {Sqlite::PrimaryKey{}}); + auto &sourceIdColumn = table.addColumn("sourceId", Sqlite::StrictColumnType::Integer); + table.addColumn("iconPath", Sqlite::StrictColumnType::Text); + table.addColumn("itemLibrary", Sqlite::StrictColumnType::Text); + table.addColumn("hints", Sqlite::StrictColumnType::Text); + + table.addUniqueIndex({sourceIdColumn, typeIdColumn}); + + table.initialize(database); + } }; public: @@ -2787,12 +3011,9 @@ public: Initializer initializer; mutable ModuleCache moduleCache{ModuleStorageAdapter{*this}}; Storage::Info::CommonTypeCache commonTypeCache_{*this}; - std::vector *> m_refreshCallbacks; - ReadWriteStatement<1, 3> upsertTypeStatement{ - "INSERT INTO types(sourceId, name, traits) VALUES(?1, ?2, ?3) ON CONFLICT DO " - "UPDATE SET traits=excluded.traits WHERE traits IS NOT " - "excluded.traits RETURNING typeId", - database}; + QVarLengthArray observers; + ReadWriteStatement<1, 2> insertTypeStatement{ + "INSERT OR IGNORE INTO types(sourceId, name) VALUES(?1, ?2) RETURNING typeId", database}; WriteStatement<5> updatePrototypeAndExtensionStatement{ "UPDATE types SET prototypeId=?2, prototypeNameId=?3, extensionId=?4, extensionNameId=?5 " "WHERE typeId=?1 AND (prototypeId IS NOT ?2 OR extensionId IS NOT ?3 AND prototypeId " @@ -2891,10 +3112,12 @@ public: "INSERT INTO sources(sourceContextId, sourceName) VALUES (?,?)", database}; mutable ReadStatement<3> selectAllSourcesStatement{ "SELECT sourceName, sourceContextId, sourceId FROM sources", database}; - mutable ReadStatement<7, 1> selectTypeByTypeIdStatement{ - "SELECT sourceId, t.name, t.typeId, prototypeId, extensionId, traits, pd.name " - "FROM types AS t LEFT JOIN propertyDeclarations AS pd " - " ON defaultPropertyId=propertyDeclarationId WHERE t.typeId=?", + mutable ReadStatement<8, 1> selectTypeByTypeIdStatement{ + "SELECT sourceId, t.name, t.typeId, prototypeId, extensionId, traits, annotationTraits, " + "pd.name " + "FROM types AS t LEFT JOIN propertyDeclarations AS pd ON " + "defaultPropertyId=propertyDeclarationId " + "WHERE t.typeId=?", database}; mutable ReadStatement<4, 1> selectExportedTypesByTypeIdStatement{ "SELECT moduleId, name, ifnull(majorVersion, -1), ifnull(minorVersion, -1) FROM " @@ -2905,11 +3128,16 @@ public: "FROM exportedTypeNames AS etn JOIN documentImports USING(moduleId) WHERE typeId=?1 AND " "sourceId=?2", database}; - mutable ReadStatement<7> selectTypesStatement{ - "SELECT sourceId, t.name, t.typeId, prototypeId, extensionId, traits, pd.name " - "FROM types AS t LEFT JOIN propertyDeclarations AS pd " - " ON defaultPropertyId=propertyDeclarationId", + mutable ReadStatement<8> selectTypesStatement{ + "SELECT sourceId, t.name, t.typeId, prototypeId, extensionId, traits, annotationTraits, " + "pd.name " + "FROM types AS t LEFT JOIN propertyDeclarations AS pd ON " + "defaultPropertyId=propertyDeclarationId", database}; + WriteStatement<2> updateTypeTraitStatement{"UPDATE types SET traits = ?2 WHERE typeId=?1", + database}; + WriteStatement<2> updateTypeAnnotationTraitStatement{ + "UPDATE types SET annotationTraits = ?2 WHERE typeId=?1", database}; ReadStatement<1, 2> selectNotUpdatedTypesInSourcesStatement{ "SELECT DISTINCT typeId FROM types WHERE (sourceId IN carray(?1) AND typeId NOT IN " "carray(?2))", @@ -3430,8 +3658,9 @@ public: "UPDATE types SET defaultPropertyId=?2 WHERE typeId=?1", database}; WriteStatement<1> updateDefaultPropertyIdToNullStatement{ "UPDATE types SET defaultPropertyId=NULL WHERE defaultPropertyId=?1", database}; - mutable ReadStatement<3, 1> selectInfoTypeByTypeIdStatement{ - "SELECT defaultPropertyId, sourceId, traits FROM types WHERE typeId=?", database}; + mutable ReadStatement<4, 1> selectInfoTypeByTypeIdStatement{ + "SELECT defaultPropertyId, sourceId, traits, annotationTraits FROM types WHERE typeId=?", + database}; mutable ReadStatement<1, 1> selectPrototypeIdsForTypeIdInOrderStatement{ "WITH RECURSIVE " " all_prototype_and_extension(typeId, prototypeId) AS (" @@ -3493,6 +3722,65 @@ public: database}; WriteStatement<1> deletePropertyEditorPathStatement{ "DELETE FROM propertyEditorPaths WHERE typeId=?1", database}; + mutable ReadStatement<4, 1> selectTypeAnnotationsForSourceIdsStatement{ + "SELECT typeId, iconPath, itemLibrary, hints FROM typeAnnotations WHERE " + "sourceId IN carray(?1) ORDER BY typeId", + database}; + WriteStatement<5> insertTypeAnnotationStatement{ + "INSERT INTO typeAnnotations(typeId, sourceId, iconPath, itemLibrary, hints) VALUES(?1, " + "?2, ?3, ?4, ?5)", + database}; + WriteStatement<4> updateTypeAnnotationStatement{ + "UPDATE typeAnnotations SET iconPath=?2, itemLibrary=?3, hints=?4 WHERE typeId=?1", database}; + WriteStatement<1> deleteTypeAnnotationStatement{"DELETE FROM typeAnnotations WHERE typeId=?1", + database}; + mutable ReadStatement<1, 1> selectTypeIconPathStatement{ + "SELECT iconPath FROM typeAnnotations WHERE typeId=?1", database}; + mutable ReadStatement<2, 1> selectTypeHintsStatement{ + "SELECT hints.key, hints.value " + "FROM typeAnnotations, json_each(typeAnnotations.hints) AS hints " + "WHERE typeId=?1", + database}; + mutable ReadStatement<9> selectItemLibraryEntriesStatement{ + "SELECT typeId, i.value->>'$.name', i.value->>'$.iconPath', i.value->>'$.category', " + " i.value->>'$.import', i.value->>'$.toolTip', i.value->>'$.properties', " + " i.value->>'$.extraFilePaths', i.value->>'$.templatePath' " + "FROM typeAnnotations, json_each(typeAnnotations.itemLibrary) AS i", + database}; + mutable ReadStatement<9, 1> selectItemLibraryEntriesByTypeIdStatement{ + "SELECT typeId, i.value->>'$.name', i.value->>'$.iconPath', i.value->>'$.category', " + " i.value->>'$.import', i.value->>'$.toolTip', i.value->>'$.properties', " + " i.value->>'$.extraFilePaths', i.value->>'$.templatePath' " + "FROM typeAnnotations, json_each(typeAnnotations.itemLibrary) AS i " + "WHERE typeId=?1", + database}; + mutable ReadStatement<9, 1> selectItemLibraryEntriesBySourceIdStatement{ + "SELECT typeId, i.value->>'$.name', i.value->>'$.iconPath', i.value->>'$.category', " + " i.value->>'$.import', i.value->>'$.toolTip', i.value->>'$.properties', " + " i.value->>'$.extraFilePaths', i.value->>'$.templatePath' " + "FROM typeAnnotations, json_each(typeAnnotations.itemLibrary) AS i " + "WHERE typeId IN (SELECT DISTINCT typeId " + " FROM documentImports AS di JOIN exportedTypeNames USING(moduleId) " + " WHERE di.sourceId=?)", + database}; + mutable ReadStatement<3, 1> selectItemLibraryPropertiesStatement{ + "SELECT p.value->>0, p.value->>1, p.value->>2 FROM json_each(?1) AS p", database}; + mutable ReadStatement<1, 1> selectItemLibraryExtraFilePathsStatement{ + "SELECT p.value FROM json_each(?1) AS p", database}; + mutable ReadStatement<1, 1> selectTypeIdsByModuleIdStatement{ + "SELECT DISTINCT typeId FROM exportedTypeNames WHERE moduleId=?", database}; + mutable ReadStatement<1, 1> selectHeirTypeIdsStatement{ + "WITH RECURSIVE " + " typeSelection(typeId) AS (" + " SELECT typeId FROM types WHERE prototypeId=?1 OR extensionId=?1" + " UNION ALL " + " SELECT t.typeId " + " FROM types AS t JOIN typeSelection AS ts " + " WHERE prototypeId=ts.typeId OR extensionId=ts.typeId)" + "SELECT typeId FROM typeSelection", + database}; }; + extern template class ProjectStorage; + } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.cpp b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.cpp index 9f7e72784b8..efe9bc58f5d 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.cpp +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.cpp @@ -112,4 +112,9 @@ ExportedTypeCannotBeInserted::ExportedTypeCannotBeInserted(std::string_view erro : ProjectStorageErrorWithMessage{"ExportedTypeCannotBeInserted"sv, errorMessage} {} +const char *TypeAnnotationHasInvalidSourceId::what() const noexcept +{ + return "The source id in a type annotation is invalid!"; +} + } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.h index a9a9a5c8856..412dd4a9ff9 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.h @@ -136,4 +136,10 @@ public: const char *what() const noexcept override; }; +class QMLDESIGNERCORE_EXPORT TypeAnnotationHasInvalidSourceId : public ProjectStorageError +{ +public: + const char *what() const noexcept override; +}; + } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinfotypes.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinfotypes.h index bf73a3c724c..427c0ff8d61 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinfotypes.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinfotypes.h @@ -5,6 +5,7 @@ #include "projectstorageids.h" +#include #include #include @@ -21,15 +22,19 @@ constexpr std::underlying_type_t to_underlying(Enumeration enumerat return static_cast>(enumeration); } +enum class FlagIs : unsigned int { False, Set, True }; + } // namespace QmlDesigner namespace QmlDesigner::Storage { + enum class PropertyDeclarationTraits : int { None = 0, IsReadOnly = 1 << 0, IsPointer = 1 << 1, IsList = 1 << 2 }; + constexpr PropertyDeclarationTraits operator|(PropertyDeclarationTraits first, PropertyDeclarationTraits second) { @@ -41,27 +46,100 @@ constexpr bool operator&(PropertyDeclarationTraits first, PropertyDeclarationTra return static_cast(first) & static_cast(second); } -enum class TypeTraits : int { +enum class TypeTraitsKind : unsigned int { None, Reference, Value, Sequence, - IsEnum = 1 << 8, - IsFileComponent = 1 << 9, - IsProjectComponent = 1 << 10, - IsInProjectModule = 1 << 11, - UsesCustomParser = 1 << 12 }; -constexpr TypeTraits operator|(TypeTraits first, TypeTraits second) +struct TypeTraits { - return static_cast(static_cast(first) | static_cast(second)); -} + constexpr TypeTraits() + : kind{TypeTraitsKind::None} + , isEnum{false} + , isFileComponent{false} + , isProjectComponent{false} + , isInProjectModule{false} + , usesCustomParser{false} + , dummy{0U} + , canBeContainer{FlagIs::False} + , forceClip{FlagIs::False} + , doesLayoutChildren{FlagIs::False} + , canBeDroppedInFormEditor{FlagIs::False} + , canBeDroppedInNavigator{FlagIs::False} + , canBeDroppedInView3D{FlagIs::False} + , isMovable{FlagIs::False} + , isResizable{FlagIs::False} + , hasFormEditorItem{FlagIs::False} + , isStackedContainer{FlagIs::False} + , takesOverRenderingOfChildren{FlagIs::False} + , visibleInNavigator{FlagIs::False} + , visibleInLibrary{FlagIs::False} + , dummy2{0U} + {} -constexpr TypeTraits operator&(TypeTraits first, TypeTraits second) -{ - return static_cast(static_cast(first) & static_cast(second)); -} + constexpr TypeTraits(TypeTraitsKind aKind) + : TypeTraits{} + { + kind = aKind; + } + + explicit constexpr TypeTraits(long long type, long long int annotation) + : type{static_cast(type)} + , annotation{static_cast(annotation)} + {} + + explicit constexpr TypeTraits(unsigned int type, unsigned int annotation) + : type{type} + , annotation{annotation} + {} + + friend bool operator==(TypeTraits first, TypeTraits second) + { + return first.type == second.type && first.annotation == second.annotation; + } + + union { + struct + { + TypeTraitsKind kind : 4; + unsigned int isEnum : 1; + unsigned int isFileComponent : 1; + unsigned int isProjectComponent : 1; + unsigned int isInProjectModule : 1; + unsigned int usesCustomParser : 1; + unsigned int dummy : 23; + }; + + unsigned int type; + }; + + union { + struct + { + FlagIs canBeContainer : 2; + FlagIs forceClip : 2; + FlagIs doesLayoutChildren : 2; + FlagIs canBeDroppedInFormEditor : 2; + FlagIs canBeDroppedInNavigator : 2; + FlagIs canBeDroppedInView3D : 2; + FlagIs isMovable : 2; + FlagIs isResizable : 2; + FlagIs hasFormEditorItem : 2; + FlagIs isStackedContainer : 2; + FlagIs takesOverRenderingOfChildren : 2; + FlagIs visibleInNavigator : 2; + FlagIs visibleInLibrary : 2; + unsigned int dummy2 : 6; + }; + + unsigned int annotation; + }; +}; + +static_assert(sizeof(TypeTraits) == sizeof(unsigned int) * 2, + "TypeTraits must be of size unsigned long long!"); using TypeNameString = ::Utils::BasicSmallString<63>; @@ -132,6 +210,83 @@ public: namespace QmlDesigner::Storage::Info { +struct TypeHint +{ + TypeHint(Utils::SmallStringView name, Utils::SmallStringView expression) + : name{name} + , expression{expression} + {} + + Utils::SmallString name; + Utils::PathString expression; +}; + +using TypeHints = QVarLengthArray; + +struct ItemLibraryProperty +{ + ItemLibraryProperty(Utils::SmallStringView name, Utils::SmallStringView type, Sqlite::ValueView value) + : name{name} + , type{type} + , value{value} + {} + + Utils::SmallString name; + Utils::SmallString type; + Sqlite::Value value; +}; + +using ItemLibraryProperties = QVarLengthArray; + +using ToolTipString = Utils::BasicSmallString<94>; + +struct ItemLibraryEntry +{ + ItemLibraryEntry(TypeId typeId, + Utils::SmallStringView name, + Utils::SmallStringView iconPath, + Utils::SmallStringView category, + Utils::SmallStringView import, + Utils::SmallStringView toolTip, + Utils::SmallStringView templatePath) + : typeId{typeId} + , name{name} + , iconPath{iconPath} + , category{category} + , import{import} + , toolTip{toolTip} + , templatePath{templatePath} + {} + + ItemLibraryEntry(TypeId typeId, + Utils::SmallStringView name, + Utils::SmallStringView iconPath, + Utils::SmallStringView category, + Utils::SmallStringView import, + Utils::SmallStringView toolTip, + ItemLibraryProperties properties) + : typeId{typeId} + , name{name} + , iconPath{iconPath} + , category{category} + , import{import} + , toolTip{toolTip} + , properties{std::move(properties)} + {} + + TypeId typeId; + Utils::SmallString name; + Utils::PathString iconPath; + Utils::SmallString category; + Utils::SmallString import; + ToolTipString toolTip; + Utils::PathString templatePath; + ItemLibraryProperties properties; + std::vector extraFilePaths; +}; + +using ItemLibraryEntries = QVarLengthArray; + class ExportedTypeName { public: @@ -196,6 +351,15 @@ public: class Type { public: + Type(PropertyDeclarationId defaultPropertyId, + SourceId sourceId, + long long typeTraits, + long long typeAnnotationTraits) + : defaultPropertyId{defaultPropertyId} + , sourceId{sourceId} + , traits{typeTraits, typeAnnotationTraits} + {} + Type(PropertyDeclarationId defaultPropertyId, SourceId sourceId, TypeTraits traits) : defaultPropertyId{defaultPropertyId} , sourceId{sourceId} diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinterface.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinterface.h index e6962602b38..266c6ee7caa 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinterface.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinterface.h @@ -6,6 +6,7 @@ #include "commontypecache.h" #include "filestatus.h" #include "projectstorageids.h" +#include "projectstorageobserver.h" #include "projectstoragetypes.h" #include @@ -27,8 +28,8 @@ public: virtual void synchronize(Storage::Synchronization::SynchronizationPackage package) = 0; virtual void synchronizeDocumentImports(const Storage::Imports imports, SourceId sourceId) = 0; - virtual void addRefreshCallback(std::function *callback) = 0; - virtual void removeRefreshCallback(std::function *callback) = 0; + virtual void addObserver(ProjectStorageObserver *observer) = 0; + virtual void removeObserver(ProjectStorageObserver *observer) = 0; virtual ModuleId moduleId(::Utils::SmallStringView name) const = 0; virtual std::optional @@ -38,6 +39,7 @@ public: Storage::Version version) const = 0; virtual TypeId typeId(ImportedTypeNameId typeNameId) const = 0; + virtual QVarLengthArray typeIds(ModuleId moduleId) const = 0; virtual Storage::Info::ExportedTypeNames exportedTypeNames(TypeId typeId) const = 0; virtual Storage::Info::ExportedTypeNames exportedTypeNames(TypeId typeId, SourceId sourceId) const @@ -53,12 +55,18 @@ public: ::Utils::SmallStringView propertyName) const = 0; virtual std::optional type(TypeId typeId) const = 0; + virtual Utils::PathString typeIconPath(TypeId typeId) const = 0; + virtual Storage::Info::TypeHints typeHints(TypeId typeId) const = 0; + virtual Storage::Info::ItemLibraryEntries itemLibraryEntries(TypeId typeId) const = 0; + virtual Storage::Info::ItemLibraryEntries itemLibraryEntries(SourceId sourceId) const = 0; + virtual Storage::Info::ItemLibraryEntries allItemLibraryEntries() const = 0; virtual std::vector<::Utils::SmallString> signalDeclarationNames(TypeId typeId) const = 0; virtual std::vector<::Utils::SmallString> functionDeclarationNames(TypeId typeId) const = 0; virtual std::optional<::Utils::SmallString> propertyName(PropertyDeclarationId propertyDeclarationId) const = 0; virtual TypeIds prototypeAndSelfIds(TypeId type) const = 0; virtual TypeIds prototypeIds(TypeId type) const = 0; + virtual TypeIds heirIds(TypeId typeId) const = 0; virtual bool isBasedOn(TypeId, TypeId) const = 0; virtual bool isBasedOn(TypeId, TypeId, TypeId) const = 0; virtual bool isBasedOn(TypeId, TypeId, TypeId, TypeId) const = 0; diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageobserver.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageobserver.h new file mode 100644 index 00000000000..c3393c91d4c --- /dev/null +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageobserver.h @@ -0,0 +1,15 @@ +// 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 "projectstorageids.h" + +namespace QmlDesigner { + +class ProjectStorageObserver +{ +public: + virtual void removedTypeIds(const TypeIds &removedTypeIds) = 0; +}; +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstoragetypes.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstoragetypes.h index 7c7cab80e27..18c39312496 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstoragetypes.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstoragetypes.h @@ -7,6 +7,7 @@ #include "projectstorageids.h" #include "projectstorageinfotypes.h" +#include #include #include @@ -637,6 +638,19 @@ public: , changeLevel{changeLevel} {} + explicit Type(::Utils::SmallStringView typeName, + TypeId prototypeId, + TypeId extensionId, + long long typeTraits, + long long typeAnnotationTraits, + SourceId sourceId) + : typeName{typeName} + , traits{typeTraits, typeAnnotationTraits} + , sourceId{sourceId} + , prototypeId{prototypeId} + , extensionId{extensionId} + {} + explicit Type(::Utils::SmallStringView typeName, TypeId prototypeId, TypeId extensionId, @@ -679,11 +693,12 @@ public: TypeId typeId, TypeId prototypeId, TypeId extensionId, - TypeTraits traits, + long long typeTraits, + long long typeAnnotationTraits, ::Utils::SmallStringView defaultPropertyName) : typeName{typeName} , defaultPropertyName{defaultPropertyName} - , traits{traits} + , traits{typeTraits, typeAnnotationTraits} , sourceId{sourceId} , typeId{typeId} , prototypeId{prototypeId} @@ -712,7 +727,7 @@ public: FunctionDeclarations functionDeclarations; SignalDeclarations signalDeclarations; EnumerationDeclarations enumerationDeclarations; - TypeTraits traits = TypeTraits::None; + TypeTraits traits; SourceId sourceId; TypeId typeId; TypeId prototypeId; @@ -768,6 +783,41 @@ public: using ProjectDatas = std::vector; +class TypeAnnotation +{ +public: + TypeAnnotation(SourceId sourceId) + : sourceId{sourceId} + {} + TypeAnnotation(SourceId sourceId, + Utils::SmallStringView typeName, + ModuleId moduleId, + Utils::SmallStringView iconPath, + TypeTraits traits, + Utils::SmallStringView hintsJson, + Utils::SmallStringView itemLibraryJson) + : typeName{typeName} + , iconPath{iconPath} + , itemLibraryJson{itemLibraryJson} + , hintsJson{hintsJson} + , sourceId{sourceId} + , moduleId{moduleId} + , traits{traits} + {} + +public: + TypeNameString typeName; + Utils::PathString iconPath; + Utils::PathString itemLibraryJson; + Utils::PathString hintsJson; + TypeId typeId; + SourceId sourceId; + ModuleId moduleId; + TypeTraits traits; +}; + +using TypeAnnotations = std::vector; + class SynchronizationPackage { public: @@ -822,6 +872,8 @@ public: ModuleIds updatedModuleIds; PropertyEditorQmlPaths propertyEditorQmlPaths; SourceIds updatedPropertyEditorQmlPathSourceIds; + TypeAnnotations typeAnnotations; + SourceIds updatedTypeAnnotationSourceIds; }; } // namespace Synchronization diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.cpp b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.cpp index 3759736cfaf..62fcf310f65 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.cpp +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.cpp @@ -373,6 +373,14 @@ void ProjectStorageUpdater::updatePropertyEditorPaths( } } +void ProjectStorageUpdater::updateTypeAnnotations( + const QString & /*propertyEditorResourcesPath*/, + Storage::Synchronization::SynchronizationPackage & /*package*/, + NotUpdatedSourceIds & /*notUpdatedSourceIds*/) +{ + // const auto typeAnnotations = dir.entryInfoList({"*.metainfo"}, QDir::Files); +} + void ProjectStorageUpdater::updatePropertyEditorPath( const QString &directoryPath, Storage::Synchronization::SynchronizationPackage &package, @@ -649,7 +657,7 @@ void ProjectStorageUpdater::parseQmlComponent(Utils::SmallStringView relativeFil package.updatedSourceIds.push_back(sourceId); type.typeName = SourcePath{qmlFilePath}.name(); - type.traits = Storage::TypeTraits::Reference; + type.traits = Storage::TypeTraitsKind::Reference; type.sourceId = sourceId; type.exportedTypes = std::move(exportedTypes); @@ -675,7 +683,7 @@ void ProjectStorageUpdater::parseQmlComponent(SourceId sourceId, auto type = m_qmlDocumentParser.parse(content, package.imports, sourceId, sourcePath.directory()); type.typeName = sourcePath.name(); - type.traits = Storage::TypeTraits::Reference; + type.traits = Storage::TypeTraitsKind::Reference; type.sourceId = sourceId; type.changeLevel = Storage::Synchronization::ChangeLevel::ExcludeExportedTypes; diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.h index 6a77f353e20..187a2219d09 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.h @@ -145,6 +145,9 @@ private: void updatePropertyEditorPaths(const QString &propertyEditorResourcesPath, Storage::Synchronization::SynchronizationPackage &package, NotUpdatedSourceIds ¬UpdatedSourceIds); + void updateTypeAnnotations(const QString &propertyEditorResourcesPath, + Storage::Synchronization::SynchronizationPackage &package, + NotUpdatedSourceIds ¬UpdatedSourceIds); void updatePropertyEditorPath(const QString &path, Storage::Synchronization::SynchronizationPackage &package, SourceId directorySourceId); diff --git a/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.cpp b/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.cpp index 41f568e66d2..3768535299a 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.cpp +++ b/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.cpp @@ -84,16 +84,16 @@ Storage::TypeTraits createAccessTypeTraits(QQmlJSScope::AccessSemantics accessSe { switch (accessSematics) { case QQmlJSScope::AccessSemantics::Reference: - return Storage::TypeTraits::Reference; + return Storage::TypeTraitsKind::Reference; case QQmlJSScope::AccessSemantics::Value: - return Storage::TypeTraits::Value; + return Storage::TypeTraitsKind::Value; case QQmlJSScope::AccessSemantics::None: - return Storage::TypeTraits::None; + return Storage::TypeTraitsKind::None; case QQmlJSScope::AccessSemantics::Sequence: - return Storage::TypeTraits::Sequence; + return Storage::TypeTraitsKind::Sequence; } - return Storage::TypeTraits::None; + return Storage::TypeTraitsKind::None; } Storage::TypeTraits createTypeTraits(QQmlJSScope::AccessSemantics accessSematics, bool hasCustomParser) @@ -101,7 +101,7 @@ Storage::TypeTraits createTypeTraits(QQmlJSScope::AccessSemantics accessSematics auto typeTrait = createAccessTypeTraits(accessSematics); if (hasCustomParser) - typeTrait = typeTrait | Storage::TypeTraits::UsesCustomParser; + typeTrait.usesCustomParser = true; return typeTrait; } @@ -348,10 +348,12 @@ void addEnumerationType(EnumerationTypes &enumerationTypes, Utils::SmallStringView enumerationAlias) { auto fullTypeName = addEnumerationType(enumerationTypes, typeName, enumerationName); + Storage::TypeTraits typeTraits{Storage::TypeTraitsKind::Value}; + typeTraits.isEnum = true; types.emplace_back(fullTypeName, Storage::Synchronization::ImportedType{TypeNameString{}}, Storage::Synchronization::ImportedType{}, - Storage::TypeTraits::Value | Storage::TypeTraits::IsEnum, + typeTraits, sourceId, createCppEnumerationExports(typeName, cppModuleId, diff --git a/src/plugins/qmldesigner/designercore/projectstorage/storagecache.h b/src/plugins/qmldesigner/designercore/projectstorage/storagecache.h index f2d6841052d..85c6147d2c7 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/storagecache.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/storagecache.h @@ -62,17 +62,17 @@ class StorageCache return StorageCacheIndex{id + amount}; } - constexpr friend bool operator==(StorageCacheIndex first, StorageCacheIndex second) noexcept + friend constexpr bool operator==(StorageCacheIndex first, StorageCacheIndex second) noexcept { return first.id == second.id && first.isValid() && second.isValid(); } - constexpr friend bool operator<(StorageCacheIndex first, StorageCacheIndex second) noexcept + friend constexpr bool operator<(StorageCacheIndex first, StorageCacheIndex second) noexcept { return first.id < second.id; } - constexpr friend bool operator>=(StorageCacheIndex first, StorageCacheIndex second) noexcept + friend constexpr bool operator>=(StorageCacheIndex first, StorageCacheIndex second) noexcept { return first.id >= second.id; } @@ -286,6 +286,12 @@ public: Mutex &mutex() const { return m_mutex; } + void clearForTestOnly() + { + m_entries.clear(); + m_indices.clear(); + } + private: void updateIndices() { diff --git a/src/plugins/qmldesigner/designercore/projectstorage/typeannotationreader.cpp b/src/plugins/qmldesigner/designercore/projectstorage/typeannotationreader.cpp new file mode 100644 index 00000000000..b829e9db36d --- /dev/null +++ b/src/plugins/qmldesigner/designercore/projectstorage/typeannotationreader.cpp @@ -0,0 +1,490 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "typeannotationreader.h" + +#include "projectstorage.h" + +#include + +#include + +#include +#include +#include +#include + +namespace QmlDesigner::Storage { +using namespace Qt::StringLiterals; + +namespace { +constexpr auto rootElementName = "MetaInfo"_L1; +constexpr auto typeElementName = "Type"_L1; +constexpr auto itemLibraryEntryElementName = "ItemLibraryEntry"_L1; +constexpr auto hintsElementName = "Hints"_L1; +constexpr auto qmlSourceElementNamentName = "QmlSource"_L1; +constexpr auto propertyElementName = "Property"_L1; +constexpr auto extraFileElementName = "ExtraFile"_L1; +} // namespace + +Synchronization::TypeAnnotations TypeAnnotationReader::parseTypeAnnotation(const QString &content, + const QString &directoryPath, + SourceId sourceId) +{ + m_sourceId = sourceId; + m_directoryPath = directoryPath; + m_parserState = ParsingDocument; + if (!SimpleAbstractStreamReader::readFromSource(content)) { + m_parserState = Error; + throw TypeAnnoationParsingError(errors()); + } + + if (!errors().isEmpty()) { + m_parserState = Error; + throw TypeAnnoationParsingError(errors()); + } + + return takeTypeAnnotations(); +} + +QStringList TypeAnnotationReader::errors() +{ + return QmlJS::SimpleAbstractStreamReader::errors(); +} + +void TypeAnnotationReader::elementStart(const QString &name, + [[maybe_unused]] const QmlJS::SourceLocation &nameLocation) +{ + switch (parserState()) { + case ParsingDocument: + setParserState(readDocument(name)); + break; + case ParsingMetaInfo: + setParserState(readMetaInfoRootElement(name)); + break; + case ParsingType: + setParserState(readTypeElement(name)); + break; + case ParsingItemLibrary: + setParserState(readItemLibraryEntryElement(name)); + break; + case ParsingProperty: + setParserState(readPropertyElement(name)); + break; + case ParsingQmlSource: + setParserState(readQmlSourceElement(name)); + break; + case ParsingExtraFile: + setParserState(readExtraFileElement(name)); + break; + case ParsingHints: + case Finished: + case Undefined: + setParserState(Error); + addError(TypeAnnotationReader::tr("Illegal state while parsing."), currentSourceLocation()); + [[fallthrough]]; + case Error: + break; + } +} + +void TypeAnnotationReader::elementEnd() +{ + switch (parserState()) { + case ParsingMetaInfo: + setParserState(Finished); + break; + case ParsingType: + if (m_itemLibraryEntries.size()) + m_typeAnnotations.back().itemLibraryJson = to_string(m_itemLibraryEntries); + setParserState(ParsingMetaInfo); + break; + case ParsingItemLibrary: + setParserState((ParsingType)); + break; + case ParsingHints: + addHints(); + setParserState(ParsingType); + break; + case ParsingProperty: + insertProperty(); + setParserState(ParsingItemLibrary); + break; + case ParsingQmlSource: + setParserState(ParsingItemLibrary); + break; + case ParsingExtraFile: + setParserState(ParsingItemLibrary); + break; + case ParsingDocument: + case Finished: + case Undefined: + setParserState(Error); + addError(TypeAnnotationReader::tr("Illegal state while parsing."), currentSourceLocation()); + [[fallthrough]]; + case Error: + break; + } +} + +void TypeAnnotationReader::propertyDefinition(const QString &name, + [[maybe_unused]] const QmlJS::SourceLocation &nameLocation, + const QVariant &value, + [[maybe_unused]] const QmlJS::SourceLocation &valueLocation) +{ + switch (parserState()) { + case ParsingType: + readTypeProperty(name, value); + break; + case ParsingItemLibrary: + readItemLibraryEntryProperty(name, value); + break; + case ParsingProperty: + readPropertyProperty(name, value); + break; + case ParsingQmlSource: + readQmlSourceProperty(name, value); + break; + case ParsingExtraFile: + readExtraFileProperty(name, value); + break; + case ParsingMetaInfo: + addError(TypeAnnotationReader::tr("No property definition allowed."), currentSourceLocation()); + break; + case ParsingDocument: + case ParsingHints: + readHint(name, value); + break; + case Finished: + case Undefined: + setParserState(Error); + addError(TypeAnnotationReader::tr("Illegal state while parsing."), currentSourceLocation()); + [[fallthrough]]; + case Error: + break; + } +} + +TypeAnnotationReader::ParserSate TypeAnnotationReader::readDocument(const QString &name) +{ + if (name == rootElementName) { + return ParsingMetaInfo; + } else { + addErrorInvalidType(name); + return Error; + } +} + +TypeAnnotationReader::ParserSate TypeAnnotationReader::readMetaInfoRootElement(const QString &name) +{ + if (name == typeElementName) { + m_typeAnnotations.emplace_back(m_sourceId); + m_itemLibraryEntries = json::array(); + return ParsingType; + } else { + addErrorInvalidType(name); + return Error; + } +} + +TypeAnnotationReader::ParserSate TypeAnnotationReader::readTypeElement(const QString &name) +{ + if (name == itemLibraryEntryElementName) { + m_itemLibraryEntries.push_back({}); + + return ParsingItemLibrary; + } else if (name == hintsElementName) { + return ParsingHints; + } else { + addErrorInvalidType(name); + return Error; + } +} + +TypeAnnotationReader::ParserSate TypeAnnotationReader::readItemLibraryEntryElement(const QString &name) +{ + if (name == qmlSourceElementNamentName) { + return ParsingQmlSource; + } else if (name == propertyElementName) { + m_currentProperty = {}; + return ParsingProperty; + } else if (name == extraFileElementName) { + return ParsingExtraFile; + } else { + addError(TypeAnnotationReader::tr("Invalid type %1").arg(name), currentSourceLocation()); + return Error; + } +} + +TypeAnnotationReader::ParserSate TypeAnnotationReader::readPropertyElement(const QString &name) +{ + addError(TypeAnnotationReader::tr("Invalid type %1").arg(name), currentSourceLocation()); + return Error; +} + +TypeAnnotationReader::ParserSate TypeAnnotationReader::readQmlSourceElement(const QString &name) +{ + addError(TypeAnnotationReader::tr("Invalid type %1").arg(name), currentSourceLocation()); + return Error; +} + +TypeAnnotationReader::ParserSate TypeAnnotationReader::readExtraFileElement(const QString &name) +{ + addError(TypeAnnotationReader::tr("Invalid type %1").arg(name), currentSourceLocation()); + return Error; +} + +namespace { +QT_WARNING_PUSH +QT_WARNING_DISABLE_CLANG("-Wunneeded-internal-declaration") + +std::pair decomposeTypePath(Utils::SmallStringView typeName) +{ + auto found = std::find(typeName.rbegin(), typeName.rend(), '.'); + + if (found == typeName.rend()) + return {{}, typeName}; + + return {{typeName.begin(), std::prev(found.base())}, {found.base(), typeName.end()}}; +} + +QT_WARNING_POP +} // namespace + +void TypeAnnotationReader::readTypeProperty(QStringView name, const QVariant &value) +{ + if (name == "name"_L1) { + Utils::PathString fullTypeName = value.toString(); + auto [moduleName, typeName] = decomposeTypePath(fullTypeName); + + m_typeAnnotations.back().typeName = typeName; + m_typeAnnotations.back().moduleId = m_projectStorage.moduleId(moduleName); + + } else if (name == "icon"_L1) { + m_typeAnnotations.back().iconPath = absoluteFilePathForDocument(value.toString()); + } else { + addError(TypeAnnotationReader::tr("Unknown property for Type %1").arg(name), + currentSourceLocation()); + setParserState(Error); + } +} + +void TypeAnnotationReader::readItemLibraryEntryProperty(QStringView name, const QVariant &variant) +{ + auto value = variant.toString().toStdString(); + if (name == "name"_L1) { + m_itemLibraryEntries.back()["name"] = value; + } else if (name == "category"_L1) { + m_itemLibraryEntries.back()["category"] = value; + } else if (name == "libraryIcon"_L1) { + m_itemLibraryEntries.back()["iconPath"] = value; + } else if (name == "version"_L1) { + // setVersion(value.toString()); + } else if (name == "requiredImport"_L1) { + m_itemLibraryEntries.back()["import"] = value; + } else if (name == "toolTip"_L1) { + m_itemLibraryEntries.back()["toolTip"] = value; + } else { + addError(TypeAnnotationReader::tr("Unknown property for ItemLibraryEntry %1").arg(name), + currentSourceLocation()); + setParserState(Error); + } +} + +namespace { +QString deEscape(const QString &value) +{ + QString result = value; + + result.replace(R"(\")"_L1, R"(")"_L1); + result.replace(R"(\\")"_L1, R"(\)"_L1); + + return result; +} + +QVariant deEscapeVariant(const QVariant &value) +{ + if (value.typeId() == QVariant::String) + return deEscape(value.toString()); + return value; +} +} // namespace + +void TypeAnnotationReader::readPropertyProperty(QStringView name, const QVariant &value) +{ + if (name == "name"_L1) { + m_currentProperty.name = value.toByteArray(); + } else if (name == "type"_L1) { + m_currentProperty.type = value.toString(); + } else if (name == "value"_L1) { + m_currentProperty.value = deEscapeVariant(value); + } else { + addError(TypeAnnotationReader::tr("Unknown property for Property %1").arg(name), + currentSourceLocation()); + setParserState(Error); + } +} + +void TypeAnnotationReader::readQmlSourceProperty(QStringView name, const QVariant &value) +{ + if (name == "source"_L1) { + m_itemLibraryEntries.back()["templatePath"] = absoluteFilePathForDocument(value.toString()); + } else { + addError(TypeAnnotationReader::tr("Unknown property for QmlSource %1").arg(name), + currentSourceLocation()); + setParserState(Error); + } +} + +void TypeAnnotationReader::readExtraFileProperty(QStringView name, const QVariant &value) +{ + if (name == "source"_L1) { + m_itemLibraryEntries.back()["extraFilePaths"].push_back( + absoluteFilePathForDocument(value.toString())); + } else { + addError(TypeAnnotationReader::tr("Unknown property for ExtraFile %1").arg(name), + currentSourceLocation()); + setParserState(Error); + } +} + +namespace { +FlagIs createFlag(QStringView expression) +{ + using namespace Qt::StringLiterals; + if (expression == "true"_L1) + return FlagIs::True; + + if (expression == "false"_L1) + return FlagIs::False; + + return FlagIs::Set; +} + +template +void setTrait(QStringView name, FlagIs flag, Storage::TypeTraits &traits) +{ + using namespace Qt::StringLiterals; + + if (name == "canBeContainer"_L1) { + traits.canBeContainer = flag; + } else if (name == "forceClip"_L1) { + traits.forceClip = flag; + } else if (name == "doesLayoutChildren"_L1) { + traits.doesLayoutChildren = flag; + } else if (name == "canBeDroppedInFormEditor"_L1) { + traits.canBeDroppedInFormEditor = flag; + } else if (name == "canBeDroppedInNavigator"_L1) { + traits.canBeDroppedInNavigator = flag; + } else if (name == "canBeDroppedInView3D"_L1) { + traits.canBeDroppedInView3D = flag; + } else if (name == "isMovable"_L1) { + traits.isMovable = flag; + } else if (name == "isResizable"_L1) { + traits.isResizable = flag; + } else if (name == "hasFormEditorItem"_L1) { + traits.hasFormEditorItem = flag; + } else if (name == "isStackedContainer"_L1) { + traits.isStackedContainer = flag; + } else if (name == "takesOverRenderingOfChildren"_L1) { + traits.takesOverRenderingOfChildren = flag; + } else if (name == "visibleInNavigator"_L1) { + traits.visibleInNavigator = flag; + } else if (name == "visibleInLibrary"_L1) { + traits.visibleInLibrary = flag; + } +} + +void setComplexHint(nlohmann::json &hints, const QString &name, const QString &expression) +{ + hints[name.toStdString()] = expression.toStdString(); +} + +} // namespace + +void TypeAnnotationReader::readHint(const QString &name, const QVariant &value) +{ + auto expression = value.toString(); + + auto flag = createFlag(expression); + setTrait(name, flag, m_typeAnnotations.back().traits); + if (flag == FlagIs::Set) + setComplexHint(m_hints, name, expression); +} + +void TypeAnnotationReader::addHints() +{ + if (m_hints.size()) { + m_typeAnnotations.back().hintsJson = to_string(m_hints); + m_hints.clear(); + } +} + +void TypeAnnotationReader::setVersion(const QString &versionNumber) +{ + // const TypeName typeName = m_currentEntry.typeName(); + int major = 1; + int minor = 0; + + if (!versionNumber.isEmpty()) { + int val; + bool ok; + if (versionNumber.contains('.'_L1)) { + val = versionNumber.split('.'_L1).constFirst().toInt(&ok); + major = ok ? val : major; + val = versionNumber.split('.'_L1).constLast().toInt(&ok); + minor = ok ? val : minor; + } else { + val = versionNumber.toInt(&ok); + major = ok ? val : major; + } + } + // m_currentEntry.setType(typeName, major, minor); +} + +TypeAnnotationReader::ParserSate TypeAnnotationReader::parserState() const +{ + return m_parserState; +} + +void TypeAnnotationReader::setParserState(ParserSate newParserState) +{ + m_parserState = newParserState; +} + +using json = nlohmann::json; + +[[maybe_unused]] static void to_json(json &out, const TypeAnnotationReader::Property &property) +{ + out = json::array({}); + out.push_back(property.name); + out.push_back(property.type); + if (property.value.type() == QVariant::String) + out.push_back(Utils::PathString{property.value.toString()}); + else if (property.value.type() == QVariant::Int || property.value.type() == QVariant::LongLong) + out.push_back(property.value.toLongLong()); + else + out.push_back(property.value.toDouble()); +} + +void TypeAnnotationReader::insertProperty() +{ + m_itemLibraryEntries.back()["properties"].push_back(m_currentProperty); +} + +void TypeAnnotationReader::addErrorInvalidType(const QString &typeName) +{ + addError(TypeAnnotationReader::tr("Invalid type %1").arg(typeName), currentSourceLocation()); +} + +Utils::PathString TypeAnnotationReader::absoluteFilePathForDocument(Utils::PathString relativeFilePath) +{ + return Utils::PathString::join({m_directoryPath, "/", relativeFilePath}); +} + +const char *TypeAnnoationParsingError::what() const noexcept +{ + return "TypeAnnoationParsingError"; +} + +} // namespace QmlDesigner::Storage diff --git a/src/plugins/qmldesigner/designercore/projectstorage/typeannotationreader.h b/src/plugins/qmldesigner/designercore/projectstorage/typeannotationreader.h new file mode 100644 index 00000000000..9332d5bed94 --- /dev/null +++ b/src/plugins/qmldesigner/designercore/projectstorage/typeannotationreader.h @@ -0,0 +1,129 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include "qmldesignercorelib_global.h" + +#include "projectstoragetypes.h" + +#include + +#include <3rdparty/json/json.hpp> +#include +#include + +#include +#include +#include + +#include +#include + +namespace QmlDesigner::Storage { + +class ItemLibraryEntry; + +class TypeAnnoationParsingError : public std::exception +{ +public: + TypeAnnoationParsingError(QStringList errors) + : errors{std::move(errors)} + {} + + const char *what() const noexcept override; + + QStringList errors; +}; + +class TypeAnnotationReader : protected QmlJS::SimpleAbstractStreamReader +{ + Q_DECLARE_TR_FUNCTIONS(QmlDesigner::Internal::TypeAnnotationReader) + + using json = nlohmann::json; + +public: + TypeAnnotationReader(ProjectStorageType &projectStorage) + : m_projectStorage{projectStorage} + {} + + Synchronization::TypeAnnotations parseTypeAnnotation(const QString &content, + const QString &directoryPath, + SourceId sourceId); + + QStringList errors(); + + void setQualifcation(const TypeName &qualification); + + struct Property + { + Utils::SmallString name; + Utils::SmallString type; + QVariant value; + }; + +protected: + Synchronization::TypeAnnotations takeTypeAnnotations() { return std::move(m_typeAnnotations); } + + void elementStart(const QString &name, const QmlJS::SourceLocation &nameLocation) override; + void elementEnd() override; + void propertyDefinition(const QString &name, + const QmlJS::SourceLocation &nameLocation, + const QVariant &value, + const QmlJS::SourceLocation &valueLocation) override; + +private: + enum ParserSate { + Error, + Finished, + Undefined, + ParsingDocument, + ParsingMetaInfo, + ParsingType, + ParsingItemLibrary, + ParsingHints, + ParsingProperty, + ParsingQmlSource, + ParsingExtraFile + }; + + ParserSate readDocument(const QString &name); + + ParserSate readMetaInfoRootElement(const QString &name); + ParserSate readTypeElement(const QString &name); + ParserSate readItemLibraryEntryElement(const QString &name); + ParserSate readPropertyElement(const QString &name); + ParserSate readQmlSourceElement(const QString &name); + ParserSate readExtraFileElement(const QString &name); + + void readTypeProperty(QStringView name, const QVariant &value); + void readItemLibraryEntryProperty(QStringView name, const QVariant &value); + void readPropertyProperty(QStringView name, const QVariant &value); + void readQmlSourceProperty(QStringView name, const QVariant &value); + void readExtraFileProperty(QStringView name, const QVariant &value); + void readHint(const QString &name, const QVariant &value); + void addHints(); + + void setVersion(const QString &versionNumber); + + ParserSate parserState() const; + void setParserState(ParserSate newParserState); + + void insertProperty(); + + void addErrorInvalidType(const QString &typeName); + + Utils::PathString absoluteFilePathForDocument(Utils::PathString relativeFilePath); + +private: + ProjectStorageType &m_projectStorage; + Utils::PathString m_directoryPath; + ParserSate m_parserState = Undefined; + Synchronization::TypeAnnotations m_typeAnnotations; + json m_hints; + json m_itemLibraryEntries; + Property m_currentProperty; + SourceId m_sourceId; +}; + +} // namespace QmlDesigner::Storage diff --git a/src/plugins/qmldesigner/qmldesignerplugin.cpp b/src/plugins/qmldesigner/qmldesignerplugin.cpp index f3b01c6bb78..40e3b681e48 100644 --- a/src/plugins/qmldesigner/qmldesignerplugin.cpp +++ b/src/plugins/qmldesigner/qmldesignerplugin.cpp @@ -25,7 +25,9 @@ #include #include #include -#include +#ifndef QDS_USE_PROJECTSTORAGE +# include +#endif #include #include #include @@ -545,7 +547,9 @@ void QmlDesignerPlugin::activateAutoSynchronization() d->mainWidget.setupNavigatorHistory(currentDesignDocument()->textEditor()); +#ifndef QDS_USE_PROJECTSTORAGE currentDesignDocument()->updateSubcomponentManager(); +#endif } void QmlDesignerPlugin::deactivateAutoSynchronization() @@ -607,7 +611,9 @@ void QmlDesignerPlugin::enforceDelayedInitialize() return QString(p + postfix); }); +#ifndef QDS_USE_PROJECTSTORAGE MetaInfo::initializeGlobal(pluginPaths, d->externalDependencies); +#endif d->viewManager.registerView(std::make_unique(d->externalDependencies)); diff --git a/tests/auto/qml/qmldesigner/coretests/CMakeLists.txt b/tests/auto/qml/qmldesigner/coretests/CMakeLists.txt index e80627a00e1..66ac125a450 100644 --- a/tests/auto/qml/qmldesigner/coretests/CMakeLists.txt +++ b/tests/auto/qml/qmldesigner/coretests/CMakeLists.txt @@ -12,6 +12,7 @@ add_qtc_test(tst_qml_testcore IDE_DATA_PATH="${PROJECT_BINARY_DIR}/${IDE_DATA_PATH}" TESTSRCDIR="${CMAKE_CURRENT_SOURCE_DIR}" QMLDESIGNERCORE_STATIC_LIBRARY QMLDESIGNERUTILS_STATIC_LIBRARY + $<$:QDS_USE_PROJECTSTORAGE> INCLUDES "${CMAKE_CURRENT_LIST_DIR}/../../../../../share/qtcreator/qml/qmlpuppet/commands" SOURCES diff --git a/tests/auto/qml/qmldesigner/coretests/tst_testcore.cpp b/tests/auto/qml/qmldesigner/coretests/tst_testcore.cpp index ed2b110e967..8d80f7e547a 100644 --- a/tests/auto/qml/qmldesigner/coretests/tst_testcore.cpp +++ b/tests/auto/qml/qmldesigner/coretests/tst_testcore.cpp @@ -14,7 +14,6 @@ #include #include #include -#include #include #include #include @@ -22,11 +21,15 @@ #include #include #include -#include #include #include #include +#ifndef QDS_USE_PROJECTSTORAGE +#include +#include +#endif + #include "../testconnectionmanager.h" #include "../testview.h" #include @@ -230,7 +233,9 @@ tst_TestCore::~tst_TestCore() = default; void tst_TestCore::initTestCase() { QmlModelNodeFacade::enableUglyWorkaroundForIsValidQmlModelNodeFacadeInTests(); +#ifndef QDS_USE_PROJECTSTORAGE MetaInfo::disableParseItemLibraryDescriptionsUgly(); +#endif Exception::setShouldAssert(false); if (!QmlJS::ModelManagerInterface::instance()) @@ -257,7 +262,9 @@ void tst_TestCore::initTestCase() qDebug() << pluginPath; Q_ASSERT(QFileInfo::exists(pluginPath)); +#ifndef QDS_USE_PROJECTSTORAGE MetaInfo::initializeGlobal({pluginPath}, *externalDependencies); +#endif QFileInfo builtins(IDE_DATA_PATH "/qml-type-descriptions/builtins.qmltypes"); QStringList errors, warnings; @@ -4497,6 +4504,7 @@ bool contains(const QmlDesigner::PropertyMetaInfos &properties, QUtf8StringView } } // namespace +#ifndef QDS_USE_PROJECTSTORAGE void tst_TestCore::testSubComponentManager() { const QString qmlString("import QtQuick 2.15\n" @@ -4529,10 +4537,10 @@ void tst_TestCore::testSubComponentManager() auto model(createModel("QtQuick.Rectangle", 2, 15)); model->setFileUrl(QUrl::fromLocalFile(fileName)); ExternalDependenciesFake externalDependenciesFake{model.get()}; + QScopedPointer subComponentManager( new SubComponentManager(model.get(), externalDependenciesFake)); subComponentManager->update(QUrl::fromLocalFile(fileName), model->imports()); - QVERIFY(model->hasNodeMetaInfo("QtQuick.Rectangle", 2, 15)); QVERIFY(contains(model->metaInfo("QtQuick.Rectangle").properties(), "border.width")); @@ -4553,6 +4561,7 @@ void tst_TestCore::testSubComponentManager() QVERIFY(contains(myButtonMetaInfo.properties(), "border.width")); QVERIFY(myButtonMetaInfo.hasProperty("border.width")); } +#endif void tst_TestCore::testAnchorsAndRewriting() { diff --git a/tests/auto/qml/qmldesigner/coretests/tst_testcore.h b/tests/auto/qml/qmldesigner/coretests/tst_testcore.h index 1304f4aa654..a38bf36faf7 100644 --- a/tests/auto/qml/qmldesigner/coretests/tst_testcore.h +++ b/tests/auto/qml/qmldesigner/coretests/tst_testcore.h @@ -171,7 +171,9 @@ private slots: void testCopyModelRewriter2(); void testMergeModelRewriter1_data(); void testMergeModelRewriter1(); +#ifndef QDS_USE_PROJECTSTORAGE void testSubComponentManager(); +#endif void testAnchorsAndRewriting(); void testAnchorsAndRewritingCenter(); diff --git a/tests/unit/tests/matchers/CMakeLists.txt b/tests/unit/tests/matchers/CMakeLists.txt index 32a7dad99c3..1eba9d4747e 100644 --- a/tests/unit/tests/matchers/CMakeLists.txt +++ b/tests/unit/tests/matchers/CMakeLists.txt @@ -7,6 +7,7 @@ add_qtc_library(TestMatchers OBJECT SOURCES info_exportedtypenames-matcher.h import-matcher.h + projectstorage-matcher.h strippedstring-matcher.h unittest-matchers.h version-matcher.h diff --git a/tests/unit/tests/matchers/projectstorage-matcher.h b/tests/unit/tests/matchers/projectstorage-matcher.h new file mode 100644 index 00000000000..5ce6512c14e --- /dev/null +++ b/tests/unit/tests/matchers/projectstorage-matcher.h @@ -0,0 +1,55 @@ +// 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 "../utils/googletest.h" + +#include + +MATCHER_P2(IsTypeHint, + name, + expression, + std::string(negation ? "isn't " : "is ") + + PrintToString(QmlDesigner::Storage::Info::TypeHint{name, expression})) +{ + const QmlDesigner::Storage::Info::TypeHint &typeHint = arg; + + return typeHint.name == name && typeHint.expression == expression; +} + +template +auto IsItemLibraryEntry(QmlDesigner::TypeId typeId, + Utils::SmallStringView name, + Utils::SmallStringView iconPath, + Utils::SmallStringView category, + Utils::SmallStringView import, + Utils::SmallStringView toolTip, + Utils::SmallStringView templatePath, + PropertiesMatcher propertiesMatcher, + ExtraFilePathsMatcher extraFilePathsMatcher) +{ + using QmlDesigner::Storage::Info::ItemLibraryEntry; + return AllOf(Field("typeId", &ItemLibraryEntry::typeId, typeId), + Field("name", &ItemLibraryEntry::name, name), + Field("iconPath", &ItemLibraryEntry::iconPath, iconPath), + Field("category", &ItemLibraryEntry::category, category), + Field("import", &ItemLibraryEntry::import, import), + Field("toolTip", &ItemLibraryEntry::toolTip, toolTip), + Field("templatePath", &ItemLibraryEntry::templatePath, templatePath), + Field("properties", &ItemLibraryEntry::properties, propertiesMatcher), + Field("extraFilePaths", &ItemLibraryEntry::extraFilePaths, extraFilePathsMatcher)); +} + +MATCHER_P3(IsItemLibraryProperty, + name, + type, + value, + std::string(negation ? "isn't " : "is ") + + PrintToString(QmlDesigner::Storage::Info::ItemLibraryProperty( + name, type, Sqlite::ValueView::create(value)))) +{ + const QmlDesigner::Storage::Info::ItemLibraryProperty &property = arg; + + return property.name == name && property.type == type && property.value == value; +} diff --git a/tests/unit/tests/matchers/strippedstring-matcher.h b/tests/unit/tests/matchers/strippedstring-matcher.h index 3c491cba8f8..f3acbfb09e5 100644 --- a/tests/unit/tests/matchers/strippedstring-matcher.h +++ b/tests/unit/tests/matchers/strippedstring-matcher.h @@ -19,6 +19,11 @@ public: : m_content(strip(std::move(content))) {} + bool MatchAndExplain(std::string_view s, testing::MatchResultListener *listener) const + { + return MatchAndExplain(QString::fromUtf8(s.data(), Utils::ssize(s)), listener); + } + bool MatchAndExplain(const QString &s, testing::MatchResultListener *listener) const { auto strippedContent = strip(s); @@ -79,3 +84,9 @@ inline auto StrippedStringEq(const QStringView &content) { return ::testing::PolymorphicMatcher(Internal::StippedStringEqMatcher(content.toString())); } + +inline auto StrippedStringEq(const std::string_view &content) +{ + return ::testing::PolymorphicMatcher( + Internal::StippedStringEqMatcher(QString::fromUtf8(content.data(), Utils::ssize(content)))); +} diff --git a/tests/unit/tests/mocks/CMakeLists.txt b/tests/unit/tests/mocks/CMakeLists.txt index bd34e15c68c..d2090432626 100644 --- a/tests/unit/tests/mocks/CMakeLists.txt +++ b/tests/unit/tests/mocks/CMakeLists.txt @@ -24,6 +24,7 @@ add_qtc_library(TestMocks OBJECT propertycomponentgeneratormock.h projectstoragemock.cpp projectstoragemock.h + projectstorageobservermock.h projectstoragepathwatchermock.h projectstoragepathwatchernotifiermock.h qmldocumentparsermock.h diff --git a/tests/unit/tests/mocks/projectstoragemock.cpp b/tests/unit/tests/mocks/projectstoragemock.cpp index ac829d21a3f..83ff85fe9a3 100644 --- a/tests/unit/tests/mocks/projectstoragemock.cpp +++ b/tests/unit/tests/mocks/projectstoragemock.cpp @@ -203,6 +203,29 @@ void ProjectStorageMock::setPropertyEditorPathId(QmlDesigner::TypeId typeId, ON_CALL(*this, propertyEditorPathId(Eq(typeId))).WillByDefault(Return(sourceId)); } +void ProjectStorageMock::setTypeHints(QmlDesigner::TypeId typeId, + const Storage::Info::TypeHints &typeHints) +{ + ON_CALL(*this, typeHints(Eq(typeId))).WillByDefault(Return(typeHints)); +} + +void ProjectStorageMock::setTypeIconPath(QmlDesigner::TypeId typeId, Utils::SmallStringView path) +{ + ON_CALL(*this, typeIconPath(Eq(typeId))).WillByDefault(Return(path)); +} + +void ProjectStorageMock::setItemLibraryEntries( + QmlDesigner::TypeId typeId, const QmlDesigner::Storage::Info::ItemLibraryEntries &entries) +{ + ON_CALL(*this, itemLibraryEntries(TypedEq(typeId))).WillByDefault(Return(entries)); +} + +void ProjectStorageMock::setItemLibraryEntries( + QmlDesigner::SourceId sourceId, const QmlDesigner::Storage::Info::ItemLibraryEntries &entries) +{ + ON_CALL(*this, itemLibraryEntries(TypedEq(sourceId))).WillByDefault(Return(entries)); +} + namespace { void addBaseProperties(TypeId typeId, TypeIds baseTypeIds, ProjectStorageMock &storage) { @@ -310,7 +333,7 @@ TypeId ProjectStorageMock::createObject(ModuleId moduleId, defaultPropertyName, defaultPropertyTraits, defaultPropertyTypeId, - Storage::TypeTraits::Reference, + Storage::TypeTraitsKind::Reference, baseTypeIds, sourceId); } @@ -319,14 +342,19 @@ TypeId ProjectStorageMock::createObject(ModuleId moduleId, Utils::SmallStringView typeName, TypeIds baseTypeIds) { - return createType(moduleId, typeName, Storage::TypeTraits::Reference, baseTypeIds); + return createType(moduleId, typeName, Storage::TypeTraitsKind::Reference, baseTypeIds); } QmlDesigner::TypeId ProjectStorageMock::createValue(QmlDesigner::ModuleId moduleId, Utils::SmallStringView typeName, QmlDesigner::TypeIds baseTypeIds) { - return createType(moduleId, typeName, Storage::TypeTraits::Value, baseTypeIds); + return createType(moduleId, typeName, Storage::TypeTraitsKind::Value, baseTypeIds); +} + +void ProjectStorageMock::setHeirs(QmlDesigner::TypeId typeId, QmlDesigner::TypeIds heirIds) +{ + ON_CALL(*this, heirIds(typeId)).WillByDefault(Return(heirIds)); } ProjectStorageMock::ProjectStorageMock() diff --git a/tests/unit/tests/mocks/projectstoragemock.h b/tests/unit/tests/mocks/projectstoragemock.h index e55841f4786..198e54b370b 100644 --- a/tests/unit/tests/mocks/projectstoragemock.h +++ b/tests/unit/tests/mocks/projectstoragemock.h @@ -84,6 +84,8 @@ public: Utils::SmallStringView typeName, QmlDesigner::TypeIds baseTypeIds = {}); + void setHeirs(QmlDesigner::TypeId typeId, QmlDesigner::TypeIds heirIds); + QmlDesigner::PropertyDeclarationId createProperty( QmlDesigner::TypeId typeId, Utils::SmallString name, @@ -100,6 +102,14 @@ public: void createFunction(QmlDesigner::TypeId typeId, Utils::SmallString name); void setPropertyEditorPathId(QmlDesigner::TypeId typeId, QmlDesigner::SourceId sourceId); + void setTypeHints(QmlDesigner::TypeId typeId, + const QmlDesigner::Storage::Info::TypeHints &typeHints); + void setTypeIconPath(QmlDesigner::TypeId typeId, Utils::SmallStringView path); + void setItemLibraryEntries(QmlDesigner::TypeId typeId, + const QmlDesigner::Storage::Info::ItemLibraryEntries &entries); + void setItemLibraryEntries(QmlDesigner::SourceId sourceId, + const QmlDesigner::Storage::Info::ItemLibraryEntries &entries); + MOCK_METHOD(void, synchronize, (QmlDesigner::Storage::Synchronization::SynchronizationPackage package), @@ -109,14 +119,8 @@ public: (const QmlDesigner::Storage::Imports imports, QmlDesigner::SourceId sourceId), (override)); - MOCK_METHOD(void, - addRefreshCallback, - (std::function * callback), - (override)); - MOCK_METHOD(void, - removeRefreshCallback, - (std::function * callback), - (override)); + MOCK_METHOD(void, addObserver, (QmlDesigner::ProjectStorageObserver *), (override)); + MOCK_METHOD(void, removeObserver, (QmlDesigner::ProjectStorageObserver *), (override)); MOCK_METHOD(QmlDesigner::ModuleId, moduleId, (::Utils::SmallStringView), (const, override)); @@ -140,7 +144,10 @@ public: ::Utils::SmallStringView exportedTypeName, QmlDesigner::Storage::Version version), (const, override)); - + MOCK_METHOD((QVarLengthArray), + typeIds, + (QmlDesigner::ModuleId moduleId), + (const, override)); MOCK_METHOD(QmlDesigner::Storage::Info::ExportedTypeNames, exportedTypeNames, (QmlDesigner::TypeId), @@ -179,6 +186,23 @@ public: type, (QmlDesigner::TypeId typeId), (const, override)); + MOCK_METHOD(Utils::PathString, typeIconPath, (QmlDesigner::TypeId typeId), (const, override)); + MOCK_METHOD(QmlDesigner::Storage::Info::TypeHints, + typeHints, + (QmlDesigner::TypeId typeId), + (const, override)); + MOCK_METHOD(QmlDesigner::Storage::Info::ItemLibraryEntries, + itemLibraryEntries, + (QmlDesigner::TypeId typeId), + (const, override)); + MOCK_METHOD(QmlDesigner::Storage::Info::ItemLibraryEntries, + itemLibraryEntries, + (QmlDesigner::SourceId sourceId), + (const, override)); + MOCK_METHOD(QmlDesigner::Storage::Info::ItemLibraryEntries, + allItemLibraryEntries, + (), + (const, override)); MOCK_METHOD(std::vector<::Utils::SmallString>, signalDeclarationNames, (QmlDesigner::TypeId typeId), @@ -193,6 +217,7 @@ public: (const, override)); MOCK_METHOD(QmlDesigner::TypeIds, prototypeAndSelfIds, (QmlDesigner::TypeId type), (const, override)); MOCK_METHOD(QmlDesigner::TypeIds, prototypeIds, (QmlDesigner::TypeId type), (const, override)); + MOCK_METHOD(QmlDesigner::TypeIds, heirIds, (QmlDesigner::TypeId type), (const, override)); MOCK_METHOD(bool, isBasedOn, (QmlDesigner::TypeId typeId, QmlDesigner::TypeId), (const, override)); MOCK_METHOD(bool, isBasedOn, diff --git a/tests/unit/tests/mocks/projectstorageobservermock.h b/tests/unit/tests/mocks/projectstorageobservermock.h new file mode 100644 index 00000000000..93a14bc09d0 --- /dev/null +++ b/tests/unit/tests/mocks/projectstorageobservermock.h @@ -0,0 +1,14 @@ +// 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 "../utils/googletest.h" + +#include + +class ProjectStorageObserverMock : public QmlDesigner::ProjectStorageObserver +{ +public: + MOCK_METHOD(void, removedTypeIds, (const QmlDesigner::TypeIds &), (override)); +}; diff --git a/tests/unit/tests/printers/gtest-creator-printing.cpp b/tests/unit/tests/printers/gtest-creator-printing.cpp index 81572a7b359..f8a9fc48a68 100644 --- a/tests/unit/tests/printers/gtest-creator-printing.cpp +++ b/tests/unit/tests/printers/gtest-creator-printing.cpp @@ -157,6 +157,11 @@ void PrintTo(const Utils::SmallString &text, ::std::ostream *os) *os << "\"" << text << "\""; } +void PrintTo(const Utils::BasicSmallString<94> &text, ::std::ostream *os) +{ + *os << "\"" << text << "\""; +} + void PrintTo(const Utils::PathString &text, ::std::ostream *os) { *os << "\"" << text << "\""; @@ -511,6 +516,23 @@ std::ostream &operator<<(std::ostream &out, const ModelResourceSet &set) << set.setExpressions << ")"; } +std::ostream &operator<<(std::ostream &out, FlagIs flagIs) +{ + switch (flagIs) { + case QmlDesigner::FlagIs::False: + out << "False"; + break; + case QmlDesigner::FlagIs::True: + out << "True"; + break; + case QmlDesigner::FlagIs::Set: + out << "Set"; + break; + } + + return out; +} + namespace Cache { std::ostream &operator<<(std::ostream &out, const SourceContext &sourceContext) @@ -521,45 +543,87 @@ std::ostream &operator<<(std::ostream &out, const SourceContext &sourceContext) namespace Storage { -namespace { -TypeTraits cleanFlags(TypeTraits traits) +std::ostream &operator<<(std::ostream &out, TypeTraitsKind kind) { - auto data = static_cast(traits); - data &= ~static_cast(TypeTraits::IsEnum); - return static_cast(data); -} - -const char *typeTraitsToString(TypeTraits traits) -{ - switch (cleanFlags(traits)) { - case TypeTraits::None: - return "None"; - case TypeTraits::Reference: - return "Reference"; - case TypeTraits::Sequence: - return "Sequence"; - case TypeTraits::Value: - return "Value"; + switch (kind) { + case TypeTraitsKind::None: + out << "None"; + break; + case TypeTraitsKind::Reference: + out << "Reference"; + break; + case TypeTraitsKind::Sequence: + out << "Sequence"; + break; + case TypeTraitsKind::Value: + out << "Value"; + break; default: break; } - return ""; + return out; } -const char *typeTraitsFlagsToString(TypeTraits traits) -{ - if (bool(traits & TypeTraits::IsEnum)) - return "(IsEnum)"; - - return ""; -} - -} // namespace - std::ostream &operator<<(std::ostream &out, TypeTraits traits) { - return out << typeTraitsToString(traits) << typeTraitsFlagsToString(traits); + out << "(" << traits.kind; + + if (traits.isEnum) + out << " | isEnum"; + + if (traits.isFileComponent) + out << " | isFileComponent"; + + if (traits.isProjectComponent) + out << " | isProjectComponent"; + + if (traits.isInProjectModule) + out << " | isInProjectModule"; + + if (traits.usesCustomParser) + out << " | usesCustomParser"; + + if (traits.canBeContainer != QmlDesigner::FlagIs::False) + out << " | canBeContainer(" << traits.canBeContainer << ")"; + + if (traits.forceClip != QmlDesigner::FlagIs::False) + out << " | forceClip(" << traits.forceClip << ")"; + + if (traits.doesLayoutChildren != QmlDesigner::FlagIs::False) + out << " | doesLayoutChildren(" << traits.doesLayoutChildren << ")"; + + if (traits.canBeDroppedInFormEditor != QmlDesigner::FlagIs::False) + out << " | canBeDroppedInFormEditor(" << traits.canBeDroppedInFormEditor << ")"; + + if (traits.canBeDroppedInNavigator != QmlDesigner::FlagIs::False) + out << " | canBeDroppedInNavigator(" << traits.canBeDroppedInNavigator << ")"; + + if (traits.canBeDroppedInView3D != QmlDesigner::FlagIs::False) + out << " | canBeDroppedInView3D(" << traits.canBeDroppedInView3D << ")"; + + if (traits.isMovable != QmlDesigner::FlagIs::False) + out << " | isMovable(" << traits.isMovable << ")"; + + if (traits.isResizable != QmlDesigner::FlagIs::False) + out << " | isResizable(" << traits.isResizable << ")"; + + if (traits.hasFormEditorItem != QmlDesigner::FlagIs::False) + out << " | hasFormEditorItem(" << traits.hasFormEditorItem << ")"; + + if (traits.isStackedContainer != QmlDesigner::FlagIs::False) + out << " | isStackedContainer(" << traits.isStackedContainer << ")"; + + if (traits.takesOverRenderingOfChildren != QmlDesigner::FlagIs::False) + out << " | takesOverRenderingOfChildren(" << traits.takesOverRenderingOfChildren << ")"; + + if (traits.visibleInNavigator != QmlDesigner::FlagIs::False) + out << " | visibleInNavigator(" << traits.visibleInNavigator << ")"; + + if (traits.visibleInLibrary != QmlDesigner::FlagIs::False) + out << " | visibleInLibrary(" << traits.visibleInLibrary << ")"; + + return out << ")"; } std::ostream &operator<<(std::ostream &out, PropertyDeclarationTraits traits) @@ -615,8 +679,27 @@ std::ostream &operator<<(std::ostream &out, const Type &type) std::ostream &operator<<(std::ostream &out, const ExportedTypeName &name) { - return out << "(\"" << name.name << "\"," << name.moduleId << ", " << name.version << ")"; + return out << "(\"" << name.name << "\", " << name.moduleId << ", " << name.version << ")"; } + +std::ostream &operator<<(std::ostream &out, const TypeHint &hint) +{ + return out << "(\"" << hint.name << "\", \"" << hint.expression << "\")"; +} + +std::ostream &operator<<(std::ostream &out, const ItemLibraryProperty &property) +{ + return out << "(\"" << property.name << "\"," << property.type << "," << property.value << ")"; +} + +std::ostream &operator<<(std::ostream &out, const ItemLibraryEntry &entry) +{ + return out << R"((")" << entry.name << R"(", ")" << entry.iconPath << R"(", ")" + << entry.category << R"(", ")" << entry.import << R"(", ")" << entry.toolTip + << R"(", ")" << entry.templatePath << R"(", )" << entry.properties << ", " + << entry.extraFilePaths << ")"; +} + } // namespace Storage::Info namespace Storage::Synchronization { @@ -815,6 +898,14 @@ std::ostream &operator<<(std::ostream &out, const PropertyEditorQmlPath &path) return out << "(" << path.moduleId << ", " << path.typeName << ", " << path.pathId << ")"; } +std::ostream &operator<<(std::ostream &out, const TypeAnnotation &annotation) +{ + return out << R"x((")x" << annotation.typeName << R"(", )" << annotation.moduleId << ", " + << annotation.sourceId << R"(, ")" << annotation.iconPath << R"(", )" + << annotation.traits << R"(, ")" << annotation.hintsJson << R"(", ")" + << annotation.itemLibraryJson << R"x("))x"; +} + } // namespace Storage::Synchronization namespace ImageCache { diff --git a/tests/unit/tests/printers/gtest-creator-printing.h b/tests/unit/tests/printers/gtest-creator-printing.h index b01bbf1fb8d..8d0c77888ec 100644 --- a/tests/unit/tests/printers/gtest-creator-printing.h +++ b/tests/unit/tests/printers/gtest-creator-printing.h @@ -93,6 +93,7 @@ void PrintTo(const std::optional &optional, ::std::ostream *os) void PrintTo(Utils::SmallStringView text, ::std::ostream *os); void PrintTo(const Utils::SmallString &text, ::std::ostream *os); +void PrintTo(const Utils::BasicSmallString<94> &text, ::std::ostream *os); void PrintTo(const Utils::PathString &text, ::std::ostream *os); } // namespace Utils @@ -124,6 +125,7 @@ class Import; class NodeMetaInfo; class PropertyMetaInfo; struct CompoundPropertyMetaInfo; +enum class FlagIs : unsigned int; std::ostream &operator<<(std::ostream &out, const ModelNode &node); std::ostream &operator<<(std::ostream &out, const VariantProperty &property); @@ -139,6 +141,7 @@ std::ostream &operator<<(std::ostream &out, const ModelResourceSet &modelResourc std::ostream &operator<<(std::ostream &out, const NodeMetaInfo &metaInfo); std::ostream &operator<<(std::ostream &out, const PropertyMetaInfo &metaInfo); std::ostream &operator<<(std::ostream &out, const CompoundPropertyMetaInfo &metaInfo); +std::ostream &operator<<(std::ostream &out, FlagIs flagIs); namespace Cache { class SourceContext; @@ -158,12 +161,14 @@ std::ostream &operator<<(std::ostream &out, const FontCollectorSizesAuxiliaryDat namespace Storage { enum class PropertyDeclarationTraits : int; -enum class TypeTraits : int; +enum class TypeTraitsKind : unsigned int; +struct TypeTraits; class Import; class Version; class VersionNumber; std::ostream &operator<<(std::ostream &out, PropertyDeclarationTraits traits); +std::ostream &operator<<(std::ostream &out, TypeTraitsKind kind); std::ostream &operator<<(std::ostream &out, TypeTraits traits); std::ostream &operator<<(std::ostream &out, const Import &import); std::ostream &operator<<(std::ostream &out, VersionNumber versionNumber); @@ -175,10 +180,16 @@ namespace Storage::Info { class ProjectDeclaration; class Type; class ExportedTypeName; +struct TypeHint; +struct ItemLibraryProperty; +struct ItemLibraryEntry; std::ostream &operator<<(std::ostream &out, const ProjectDeclaration &declaration); std::ostream &operator<<(std::ostream &out, const Type &type); std::ostream &operator<<(std::ostream &out, const ExportedTypeName &name); +std::ostream &operator<<(std::ostream &out, const TypeHint &hint); +std::ostream &operator<<(std::ostream &out, const ItemLibraryProperty &property); +std::ostream &operator<<(std::ostream &out, const ItemLibraryEntry &entry); } // namespace Storage::Info @@ -202,6 +213,7 @@ enum class FileType : char; enum class ChangeLevel : char; class ModuleExportedImport; class PropertyEditorQmlPath; +class TypeAnnotation; std::ostream &operator<<(std::ostream &out, const Type &type); std::ostream &operator<<(std::ostream &out, const ExportedType &exportedType); @@ -221,6 +233,7 @@ std::ostream &operator<<(std::ostream &out, FileType fileType); std::ostream &operator<<(std::ostream &out, ChangeLevel changeLevel); std::ostream &operator<<(std::ostream &out, const ModuleExportedImport &import); std::ostream &operator<<(std::ostream &out, const PropertyEditorQmlPath &path); +std::ostream &operator<<(std::ostream &out, const TypeAnnotation &annotation); } // namespace Storage::Synchronization diff --git a/tests/unit/tests/printers/gtest-qt-printing.h b/tests/unit/tests/printers/gtest-qt-printing.h index ceec63817cc..97e6822a7bd 100644 --- a/tests/unit/tests/printers/gtest-qt-printing.h +++ b/tests/unit/tests/printers/gtest-qt-printing.h @@ -6,6 +6,7 @@ #include #include +#include QT_BEGIN_NAMESPACE @@ -15,6 +16,24 @@ class QStringView; class QTextCharFormat; class QImage; class QIcon; +template +class QVarLengthArray; + +template +std::ostream &operator<<(std::ostream &out, const QVarLengthArray &array) +{ + out << "["; + + int i = 0; + for (auto &&value : array) { + i++; + out << value; + if (i < array.size()) + out << ", "; + } + + return out << "]"; +} std::ostream &operator<<(std::ostream &out, const QVariant &QVariant); std::ostream &operator<<(std::ostream &out, const QString &text); diff --git a/tests/unit/tests/testdesignercore/CMakeLists.txt b/tests/unit/tests/testdesignercore/CMakeLists.txt index 0847caa3230..df91b656eb5 100644 --- a/tests/unit/tests/testdesignercore/CMakeLists.txt +++ b/tests/unit/tests/testdesignercore/CMakeLists.txt @@ -61,9 +61,7 @@ add_qtc_library(TestDesignerCore OBJECT include/bindingproperty.h include/imagecacheauxiliarydata.h include/import.h - include/itemlibraryinfo.h - include/metainfo.h - include/metainforeader.h + include/itemlibraryentry.h include/modelnode.h include/module.h include/nodeabstractproperty.h @@ -78,9 +76,7 @@ add_qtc_library(TestDesignerCore OBJECT include/signalhandlerproperty.h include/synchronousimagecache.h include/variantproperty.h - metainfo/itemlibraryinfo.cpp - metainfo/metainfo.cpp - metainfo/metainforeader.cpp + metainfo/itemlibraryentry.cpp metainfo/nodemetainfo.cpp model/abstractproperty.cpp model/abstractview.cpp @@ -127,6 +123,7 @@ add_qtc_library(TestDesignerCore OBJECT projectstorage/nonlockingmutex.h projectstorage/projectstorageexceptions.cpp projectstorage/projectstorageexceptions.h projectstorage/projectstorageinterface.h + projectstorage/projectstorageobserver.h projectstorage/projectstorage.cpp projectstorage/projectstorage.h projectstorage/projectstoragepathwatcher.h projectstorage/projectstoragepathwatcherinterface.h @@ -143,6 +140,8 @@ add_qtc_library(TestDesignerCore OBJECT projectstorage/storagecache.h projectstorage/storagecacheentry.h projectstorage/storagecachefwd.h + projectstorage/typeannotationreader.cpp + projectstorage/typeannotationreader.h projectstorage/qmldocumentparserinterface.h projectstorage/qmltypesparserinterface.h rewritertransaction.cpp diff --git a/tests/unit/tests/unittests/metainfo/nodemetainfo-test.cpp b/tests/unit/tests/unittests/metainfo/nodemetainfo-test.cpp index c21d3259365..af8c4bd2209 100644 --- a/tests/unit/tests/unittests/metainfo/nodemetainfo-test.cpp +++ b/tests/unit/tests/unittests/metainfo/nodemetainfo-test.cpp @@ -4,6 +4,7 @@ #include "../utils/googletest.h" #include +#include #include #include @@ -13,8 +14,11 @@ namespace { +using QmlDesigner::FlagIs; using QmlDesigner::ModelNode; using QmlDesigner::ModelNodes; +using QmlDesigner::Storage::TypeTraits; +using QmlDesigner::Storage::TypeTraitsKind; template auto PropertyId(const Matcher &matcher) @@ -211,11 +215,10 @@ TEST_F(NodeMetaInfo, invalid_is_not_file_component) TEST_F(NodeMetaInfo, component_is_file_component) { - using QmlDesigner::Storage::TypeTraits; auto moduleId = projectStorageMock.createModule("/path/to/project"); - auto typeId = projectStorageMock.createType(moduleId, - "Foo", - TypeTraits::IsFileComponent | TypeTraits::Reference); + TypeTraits traits{TypeTraitsKind::Reference}; + traits.isFileComponent = true; + auto typeId = projectStorageMock.createType(moduleId, "Foo", traits); QmlDesigner::NodeMetaInfo metaInfo{typeId, &projectStorageMock}; bool isFileComponent = metaInfo.isFileComponent(); @@ -225,9 +228,10 @@ TEST_F(NodeMetaInfo, component_is_file_component) TEST_F(NodeMetaInfo, is_project_component) { - using QmlDesigner::Storage::TypeTraits; auto moduleId = projectStorageMock.createModule("/path/to/project"); - auto typeId = projectStorageMock.createType(moduleId, "Foo", TypeTraits::IsProjectComponent); + TypeTraits traits{TypeTraitsKind::Reference}; + traits.isProjectComponent = true; + auto typeId = projectStorageMock.createType(moduleId, "Foo", traits); QmlDesigner::NodeMetaInfo metaInfo{typeId, &projectStorageMock}; bool isProjectComponent = metaInfo.isProjectComponent(); @@ -258,9 +262,10 @@ TEST_F(NodeMetaInfo, invalid_is_not_project_component) TEST_F(NodeMetaInfo, is_in_project_module) { - using QmlDesigner::Storage::TypeTraits; auto moduleId = projectStorageMock.createModule("/path/to/project"); - auto typeId = projectStorageMock.createType(moduleId, "Foo", TypeTraits::IsInProjectModule); + TypeTraits traits{TypeTraitsKind::Reference}; + traits.isInProjectModule = true; + auto typeId = projectStorageMock.createType(moduleId, "Foo", traits); QmlDesigner::NodeMetaInfo metaInfo{typeId, &projectStorageMock}; bool isInProjectModule = metaInfo.isInProjectModule(); @@ -793,6 +798,25 @@ TEST_F(NodeMetaInfo, prototypes_returns_empty_container_for_default) ASSERT_THAT(prototypes, IsEmpty()); } +TEST_F(NodeMetaInfo, heirs) +{ + auto metaInfo = model.qmlQtObjectMetaInfo(); + projectStorageMock.setHeirs(metaInfo.id(), {model.qtQuickItemMetaInfo().id()}); + + auto heirs = metaInfo.heirs(); + + ASSERT_THAT(heirs, ElementsAre(model.qtQuickItemMetaInfo())); +} + +TEST_F(NodeMetaInfo, heirs_returns_empty_container_for_default) +{ + auto metaInfo = QmlDesigner::NodeMetaInfo(); + + auto heirs = metaInfo.heirs(); + + ASSERT_THAT(heirs, IsEmpty()); +} + TEST_F(NodeMetaInfo, common_base_is_root) { auto metaInfo = model.flowViewFlowActionAreaMetaInfo(); @@ -2402,7 +2426,9 @@ TEST_F(NodeMetaInfo, default_is_not_view) TEST_F(NodeMetaInfo, is_enumeration) { - auto metaInfo = createMetaInfo("QML", "Foo", QmlDesigner::Storage::TypeTraits::IsEnum); + TypeTraits traits; + traits.isEnum = true; + auto metaInfo = createMetaInfo("QML", "Foo", traits); bool isType = metaInfo.isEnumeration(); @@ -2562,7 +2588,7 @@ TEST_F(NodeMetaInfo, default_property_editor_specifics_path_is_empty) TEST_F(NodeMetaInfo, is_reference) { - auto metaInfo = createMetaInfo("QtQuick", "Item", QmlDesigner::Storage::TypeTraits::Reference); + auto metaInfo = createMetaInfo("QtQuick", "Item", TypeTraitsKind::Reference); auto type = metaInfo.type(); @@ -2571,7 +2597,7 @@ TEST_F(NodeMetaInfo, is_reference) TEST_F(NodeMetaInfo, is_value) { - auto metaInfo = createMetaInfo("QML", "bool", QmlDesigner::Storage::TypeTraits::Value); + auto metaInfo = createMetaInfo("QML", "bool", TypeTraitsKind::Value); auto type = metaInfo.type(); @@ -2580,7 +2606,7 @@ TEST_F(NodeMetaInfo, is_value) TEST_F(NodeMetaInfo, is_sequence) { - auto metaInfo = createMetaInfo("QML", "QObjectList", QmlDesigner::Storage::TypeTraits::Sequence); + auto metaInfo = createMetaInfo("QML", "QObjectList", TypeTraitsKind::Sequence); auto type = metaInfo.type(); @@ -2589,7 +2615,7 @@ TEST_F(NodeMetaInfo, is_sequence) TEST_F(NodeMetaInfo, is_none) { - auto metaInfo = createMetaInfo("QML", "void", QmlDesigner::Storage::TypeTraits::None); + auto metaInfo = createMetaInfo("QML", "void", TypeTraitsKind::None); auto type = metaInfo.type(); @@ -2605,4 +2631,555 @@ TEST_F(NodeMetaInfo, default_is_none) ASSERT_THAT(type, QmlDesigner::MetaInfoType::None); } +TEST_F(NodeMetaInfo, object_can_not_be_container) +{ + auto canBeContainer = objectMetaInfo.canBeContainer(); + + ASSERT_THAT(canBeContainer, FlagIs::False); +} + +TEST_F(NodeMetaInfo, default_can_not_be_container) +{ + auto canBeContainer = QmlDesigner::NodeMetaInfo{}.canBeContainer(); + + ASSERT_THAT(canBeContainer, FlagIs::False); +} + +TEST_F(NodeMetaInfo, invalid_can_not_be_container) +{ + auto node = model.createModelNode("Foo"); + auto metaInfo = node.metaInfo(); + + auto canBeContainer = metaInfo.canBeContainer(); + + ASSERT_THAT(canBeContainer, FlagIs::False); +} + +TEST_F(NodeMetaInfo, component_can_be_container) +{ + auto moduleId = projectStorageMock.createModule("/path/to/project"); + TypeTraits traits{TypeTraitsKind::Reference}; + traits.canBeContainer = FlagIs::True; + auto typeId = projectStorageMock.createType(moduleId, "Foo", traits); + QmlDesigner::NodeMetaInfo metaInfo{typeId, &projectStorageMock}; + + auto canBeContainer = metaInfo.canBeContainer(); + + ASSERT_THAT(canBeContainer, FlagIs::True); +} + +TEST_F(NodeMetaInfo, object_do_no_forces_clipping) +{ + auto forceClip = objectMetaInfo.forceClip(); + + ASSERT_THAT(forceClip, FlagIs::False); +} + +TEST_F(NodeMetaInfo, default_do_no_forces_clipping) +{ + auto forceClip = QmlDesigner::NodeMetaInfo{}.forceClip(); + + ASSERT_THAT(forceClip, FlagIs::False); +} + +TEST_F(NodeMetaInfo, invalid_do_no_forces_clipping) +{ + auto node = model.createModelNode("Foo"); + auto metaInfo = node.metaInfo(); + + auto forceClip = metaInfo.forceClip(); + + ASSERT_THAT(forceClip, FlagIs::False); +} + +TEST_F(NodeMetaInfo, component_forces_clipping) +{ + auto moduleId = projectStorageMock.createModule("/path/to/project"); + TypeTraits traits{TypeTraitsKind::Reference}; + traits.forceClip = FlagIs::True; + auto typeId = projectStorageMock.createType(moduleId, "Foo", traits); + QmlDesigner::NodeMetaInfo metaInfo{typeId, &projectStorageMock}; + + auto forceClip = metaInfo.forceClip(); + + ASSERT_THAT(forceClip, FlagIs::True); +} + +TEST_F(NodeMetaInfo, object_does_not_layout_children) +{ + auto doesLayoutChildren = objectMetaInfo.doesLayoutChildren(); + + ASSERT_THAT(doesLayoutChildren, FlagIs::False); +} + +TEST_F(NodeMetaInfo, default_does_not_layout_children) +{ + auto doesLayoutChildren = QmlDesigner::NodeMetaInfo{}.doesLayoutChildren(); + + ASSERT_THAT(doesLayoutChildren, FlagIs::False); +} + +TEST_F(NodeMetaInfo, invalid_does_not_layout_children) +{ + auto node = model.createModelNode("Foo"); + auto metaInfo = node.metaInfo(); + + auto doesLayoutChildren = metaInfo.doesLayoutChildren(); + + ASSERT_THAT(doesLayoutChildren, FlagIs::False); +} + +TEST_F(NodeMetaInfo, component_layouts_children) +{ + auto moduleId = projectStorageMock.createModule("/path/to/project"); + TypeTraits traits{TypeTraitsKind::Reference}; + traits.doesLayoutChildren = FlagIs::True; + auto typeId = projectStorageMock.createType(moduleId, "Foo", traits); + QmlDesigner::NodeMetaInfo metaInfo{typeId, &projectStorageMock}; + + auto doesLayoutChildren = metaInfo.doesLayoutChildren(); + + ASSERT_THAT(doesLayoutChildren, FlagIs::True); +} + +TEST_F(NodeMetaInfo, object_cannot_be_dropped_in_form_editor) +{ + auto canBeDroppedInFormEditor = objectMetaInfo.canBeDroppedInFormEditor(); + + ASSERT_THAT(canBeDroppedInFormEditor, FlagIs::False); +} + +TEST_F(NodeMetaInfo, default_cannot_be_dropped_in_form_editor) +{ + auto canBeDroppedInFormEditor = QmlDesigner::NodeMetaInfo{}.canBeDroppedInFormEditor(); + + ASSERT_THAT(canBeDroppedInFormEditor, FlagIs::False); +} + +TEST_F(NodeMetaInfo, invalid_cannot_be_dropped_in_form_editor) +{ + auto node = model.createModelNode("Foo"); + auto metaInfo = node.metaInfo(); + + auto canBeDroppedInFormEditor = metaInfo.canBeDroppedInFormEditor(); + + ASSERT_THAT(canBeDroppedInFormEditor, FlagIs::False); +} + +TEST_F(NodeMetaInfo, component_can_be_dropped_in_form_editor) +{ + auto moduleId = projectStorageMock.createModule("/path/to/project"); + TypeTraits traits{TypeTraitsKind::Reference}; + traits.canBeDroppedInFormEditor = FlagIs::True; + auto typeId = projectStorageMock.createType(moduleId, "Foo", traits); + QmlDesigner::NodeMetaInfo metaInfo{typeId, &projectStorageMock}; + + auto canBeDroppedInFormEditor = metaInfo.canBeDroppedInFormEditor(); + + ASSERT_THAT(canBeDroppedInFormEditor, FlagIs::True); +} + +TEST_F(NodeMetaInfo, object_cannot_be_dropped_in_navigator) +{ + auto canBeDroppedInNavigator = objectMetaInfo.canBeDroppedInNavigator(); + + ASSERT_THAT(canBeDroppedInNavigator, FlagIs::False); +} + +TEST_F(NodeMetaInfo, default_cannot_be_dropped_in_navigator) +{ + auto canBeDroppedInNavigator = QmlDesigner::NodeMetaInfo{}.canBeDroppedInNavigator(); + + ASSERT_THAT(canBeDroppedInNavigator, FlagIs::False); +} + +TEST_F(NodeMetaInfo, invalid_cannot_be_dropped_in_navigator) +{ + auto node = model.createModelNode("Foo"); + auto metaInfo = node.metaInfo(); + + auto canBeDroppedInNavigator = metaInfo.canBeDroppedInNavigator(); + + ASSERT_THAT(canBeDroppedInNavigator, FlagIs::False); +} + +TEST_F(NodeMetaInfo, component_can_be_dropped_in_navigator) +{ + auto moduleId = projectStorageMock.createModule("/path/to/project"); + TypeTraits traits{TypeTraitsKind::Reference}; + traits.canBeDroppedInNavigator = FlagIs::True; + auto typeId = projectStorageMock.createType(moduleId, "Foo", traits); + QmlDesigner::NodeMetaInfo metaInfo{typeId, &projectStorageMock}; + + auto canBeDroppedInNavigator = metaInfo.canBeDroppedInNavigator(); + + ASSERT_THAT(canBeDroppedInNavigator, FlagIs::True); +} + +TEST_F(NodeMetaInfo, object_cannot_be_dropped_in_3d_view) +{ + auto canBeDroppedInView3D = objectMetaInfo.canBeDroppedInView3D(); + + ASSERT_THAT(canBeDroppedInView3D, FlagIs::False); +} + +TEST_F(NodeMetaInfo, default_cannot_be_dropped_in_3d_view) +{ + auto canBeDroppedInView3D = QmlDesigner::NodeMetaInfo{}.canBeDroppedInView3D(); + + ASSERT_THAT(canBeDroppedInView3D, FlagIs::False); +} + +TEST_F(NodeMetaInfo, invalid_cannot_be_dropped_in_3d_view) +{ + auto node = model.createModelNode("Foo"); + auto metaInfo = node.metaInfo(); + + auto canBeDroppedInView3D = metaInfo.canBeDroppedInView3D(); + + ASSERT_THAT(canBeDroppedInView3D, FlagIs::False); +} + +TEST_F(NodeMetaInfo, component_can_be_dropped_in_3d_view) +{ + auto moduleId = projectStorageMock.createModule("/path/to/project"); + TypeTraits traits{TypeTraitsKind::Reference}; + traits.canBeDroppedInView3D = FlagIs::True; + auto typeId = projectStorageMock.createType(moduleId, "Foo", traits); + QmlDesigner::NodeMetaInfo metaInfo{typeId, &projectStorageMock}; + + auto canBeDroppedInView3D = metaInfo.canBeDroppedInView3D(); + + ASSERT_THAT(canBeDroppedInView3D, FlagIs::True); +} + +TEST_F(NodeMetaInfo, object_is_not_movable) +{ + auto isMovable = objectMetaInfo.isMovable(); + + ASSERT_THAT(isMovable, FlagIs::False); +} + +TEST_F(NodeMetaInfo, default_is_not_movable) +{ + auto isMovable = QmlDesigner::NodeMetaInfo{}.isMovable(); + + ASSERT_THAT(isMovable, FlagIs::False); +} + +TEST_F(NodeMetaInfo, invalid_is_not_movable) +{ + auto node = model.createModelNode("Foo"); + auto metaInfo = node.metaInfo(); + + auto isMovable = metaInfo.isMovable(); + + ASSERT_THAT(isMovable, FlagIs::False); +} + +TEST_F(NodeMetaInfo, component_is_movable) +{ + auto moduleId = projectStorageMock.createModule("/path/to/project"); + TypeTraits traits{TypeTraitsKind::Reference}; + traits.isMovable = FlagIs::True; + auto typeId = projectStorageMock.createType(moduleId, "Foo", traits); + QmlDesigner::NodeMetaInfo metaInfo{typeId, &projectStorageMock}; + + auto isMovable = metaInfo.isMovable(); + + ASSERT_THAT(isMovable, FlagIs::True); +} + +TEST_F(NodeMetaInfo, object_is_not_resizable) +{ + auto isResizable = objectMetaInfo.isResizable(); + + ASSERT_THAT(isResizable, FlagIs::False); +} + +TEST_F(NodeMetaInfo, default_is_not_resizable) +{ + auto isResizable = QmlDesigner::NodeMetaInfo{}.isResizable(); + + ASSERT_THAT(isResizable, FlagIs::False); +} + +TEST_F(NodeMetaInfo, invalid_is_not_resizable) +{ + auto node = model.createModelNode("Foo"); + auto metaInfo = node.metaInfo(); + + auto isResizable = metaInfo.isResizable(); + + ASSERT_THAT(isResizable, FlagIs::False); +} + +TEST_F(NodeMetaInfo, component_is_resizable) +{ + auto moduleId = projectStorageMock.createModule("/path/to/project"); + TypeTraits traits{TypeTraitsKind::Reference}; + traits.isResizable = FlagIs::True; + auto typeId = projectStorageMock.createType(moduleId, "Foo", traits); + QmlDesigner::NodeMetaInfo metaInfo{typeId, &projectStorageMock}; + + auto isResizable = metaInfo.isResizable(); + + ASSERT_THAT(isResizable, FlagIs::True); +} + +TEST_F(NodeMetaInfo, object_has_not_form_editor_item) +{ + auto hasFormEditorItem = objectMetaInfo.hasFormEditorItem(); + + ASSERT_THAT(hasFormEditorItem, FlagIs::False); +} + +TEST_F(NodeMetaInfo, default_has_not_form_editor_item) +{ + auto hasFormEditorItem = QmlDesigner::NodeMetaInfo{}.hasFormEditorItem(); + + ASSERT_THAT(hasFormEditorItem, FlagIs::False); +} + +TEST_F(NodeMetaInfo, invalid_has_not_form_editor_item) +{ + auto node = model.createModelNode("Foo"); + auto metaInfo = node.metaInfo(); + + auto hasFormEditorItem = metaInfo.hasFormEditorItem(); + + ASSERT_THAT(hasFormEditorItem, FlagIs::False); +} + +TEST_F(NodeMetaInfo, component_has_form_editor_item) +{ + auto moduleId = projectStorageMock.createModule("/path/to/project"); + TypeTraits traits{TypeTraitsKind::Reference}; + traits.hasFormEditorItem = FlagIs::True; + auto typeId = projectStorageMock.createType(moduleId, "Foo", traits); + QmlDesigner::NodeMetaInfo metaInfo{typeId, &projectStorageMock}; + + auto hasFormEditorItem = metaInfo.hasFormEditorItem(); + + ASSERT_THAT(hasFormEditorItem, FlagIs::True); +} + +TEST_F(NodeMetaInfo, object_is_not_stacked_container) +{ + auto isStackedContainer = objectMetaInfo.isStackedContainer(); + + ASSERT_THAT(isStackedContainer, FlagIs::False); +} + +TEST_F(NodeMetaInfo, default_is_not_stacked_container) +{ + auto isStackedContainer = QmlDesigner::NodeMetaInfo{}.isStackedContainer(); + + ASSERT_THAT(isStackedContainer, FlagIs::False); +} + +TEST_F(NodeMetaInfo, invalid_is_not_stacked_container) +{ + auto node = model.createModelNode("Foo"); + auto metaInfo = node.metaInfo(); + + auto isStackedContainer = metaInfo.isStackedContainer(); + + ASSERT_THAT(isStackedContainer, FlagIs::False); +} + +TEST_F(NodeMetaInfo, component_is_stacked_container) +{ + auto moduleId = projectStorageMock.createModule("/path/to/project"); + TypeTraits traits{TypeTraitsKind::Reference}; + traits.isStackedContainer = FlagIs::True; + auto typeId = projectStorageMock.createType(moduleId, "Foo", traits); + QmlDesigner::NodeMetaInfo metaInfo{typeId, &projectStorageMock}; + + auto isStackedContainer = metaInfo.isStackedContainer(); + + ASSERT_THAT(isStackedContainer, FlagIs::True); +} + +TEST_F(NodeMetaInfo, object_dont_takes_over_rendering_of_children) +{ + auto takesOverRenderingOfChildren = objectMetaInfo.takesOverRenderingOfChildren(); + + ASSERT_THAT(takesOverRenderingOfChildren, FlagIs::False); +} + +TEST_F(NodeMetaInfo, default_dont_takes_over_rendering_of_children) +{ + auto takesOverRenderingOfChildren = QmlDesigner::NodeMetaInfo{}.takesOverRenderingOfChildren(); + + ASSERT_THAT(takesOverRenderingOfChildren, FlagIs::False); +} + +TEST_F(NodeMetaInfo, invalid_dont_takes_over_rendering_of_children) +{ + auto node = model.createModelNode("Foo"); + auto metaInfo = node.metaInfo(); + + auto takesOverRenderingOfChildren = metaInfo.takesOverRenderingOfChildren(); + + ASSERT_THAT(takesOverRenderingOfChildren, FlagIs::False); +} + +TEST_F(NodeMetaInfo, component_takes_over_rendering_of_children) +{ + auto moduleId = projectStorageMock.createModule("/path/to/project"); + TypeTraits traits{TypeTraitsKind::Reference}; + traits.takesOverRenderingOfChildren = FlagIs::True; + auto typeId = projectStorageMock.createType(moduleId, "Foo", traits); + QmlDesigner::NodeMetaInfo metaInfo{typeId, &projectStorageMock}; + + auto takesOverRenderingOfChildren = metaInfo.takesOverRenderingOfChildren(); + + ASSERT_THAT(takesOverRenderingOfChildren, FlagIs::True); +} + +TEST_F(NodeMetaInfo, object_is_not_visible_in_navigator) +{ + auto visibleInNavigator = objectMetaInfo.visibleInNavigator(); + + ASSERT_THAT(visibleInNavigator, FlagIs::False); +} + +TEST_F(NodeMetaInfo, default_is_not_visible_in_navigator) +{ + auto visibleInNavigator = QmlDesigner::NodeMetaInfo{}.visibleInNavigator(); + + ASSERT_THAT(visibleInNavigator, FlagIs::False); +} + +TEST_F(NodeMetaInfo, invalid_is_not_visible_in_navigator) +{ + auto node = model.createModelNode("Foo"); + auto metaInfo = node.metaInfo(); + + auto visibleInNavigator = metaInfo.visibleInNavigator(); + + ASSERT_THAT(visibleInNavigator, FlagIs::False); +} + +TEST_F(NodeMetaInfo, component_is_visible_in_navigator) +{ + auto moduleId = projectStorageMock.createModule("/path/to/project"); + TypeTraits traits{TypeTraitsKind::Reference}; + traits.visibleInNavigator = FlagIs::True; + auto typeId = projectStorageMock.createType(moduleId, "Foo", traits); + QmlDesigner::NodeMetaInfo metaInfo{typeId, &projectStorageMock}; + + auto visibleInNavigator = metaInfo.visibleInNavigator(); + + ASSERT_THAT(visibleInNavigator, FlagIs::True); +} + +TEST_F(NodeMetaInfo, object_is_not_visible_in_library) +{ + auto visibleInLibrary = objectMetaInfo.visibleInLibrary(); + + ASSERT_THAT(visibleInLibrary, FlagIs::False); +} + +TEST_F(NodeMetaInfo, default_is_not_visible_in_library) +{ + auto visibleInLibrary = QmlDesigner::NodeMetaInfo{}.visibleInLibrary(); + + ASSERT_THAT(visibleInLibrary, FlagIs::False); +} + +TEST_F(NodeMetaInfo, invalid_is_not_visible_in_library) +{ + auto node = model.createModelNode("Foo"); + auto metaInfo = node.metaInfo(); + + auto visibleInLibrary = metaInfo.visibleInLibrary(); + + ASSERT_THAT(visibleInLibrary, FlagIs::False); +} + +TEST_F(NodeMetaInfo, component_is_visible_in_library) +{ + auto moduleId = projectStorageMock.createModule("/path/to/project"); + TypeTraits traits{TypeTraitsKind::Reference}; + traits.visibleInLibrary = FlagIs::True; + auto typeId = projectStorageMock.createType(moduleId, "Foo", traits); + QmlDesigner::NodeMetaInfo metaInfo{typeId, &projectStorageMock}; + + auto visibleInLibrary = metaInfo.visibleInLibrary(); + + ASSERT_THAT(visibleInLibrary, FlagIs::True); +} + +TEST_F(NodeMetaInfo, type_hints) +{ + projectStorageMock.setTypeHints(objectMetaInfo.id(), {{"inContainer", "true"}}); + + auto typeHints = objectMetaInfo.typeHints(); + + ASSERT_THAT(typeHints, ElementsAre(IsTypeHint("inContainer", "true"))); +} + +TEST_F(NodeMetaInfo, no_type_hints_for_default) +{ + QmlDesigner::NodeMetaInfo metaInfo; + + auto typeHints = metaInfo.typeHints(); + + ASSERT_THAT(typeHints, IsEmpty()); +} + +TEST_F(NodeMetaInfo, icon_path) +{ + projectStorageMock.setTypeIconPath(objectMetaInfo.id(), "/icon/path"); + + auto path = objectMetaInfo.iconPath(); + + ASSERT_THAT(path, Eq("/icon/path")); +} + +TEST_F(NodeMetaInfo, no_icon_path_for_default) +{ + QmlDesigner::NodeMetaInfo metaInfo; + + auto path = metaInfo.iconPath(); + + ASSERT_THAT(path, IsEmpty()); +} + +TEST_F(NodeMetaInfo, item_library_entries) +{ + projectStorageMock.setItemLibraryEntries(objectMetaInfo.id(), + {{objectMetaInfo.id(), + "Object", + "/icon/path", + "Basic", + "QtQuick", + "An object", + {{"x", "double", Sqlite::ValueView::create(1)}}}}); + + auto entries = objectMetaInfo.itemLibrariesEntries(); + + ASSERT_THAT(entries, + ElementsAre(IsItemLibraryEntry(objectMetaInfo.id(), + "Object", + "/icon/path", + "Basic", + "QtQuick", + "An object", + "", + ElementsAre(IsItemLibraryProperty("x", "double", 1)), + IsEmpty()))); +} + +TEST_F(NodeMetaInfo, no_item_library_entries_for_default) +{ + QmlDesigner::NodeMetaInfo metaInfo; + + auto entries = metaInfo.itemLibrariesEntries(); + + ASSERT_THAT(entries, IsEmpty()); +} + } // namespace diff --git a/tests/unit/tests/unittests/metainfo/propertymetainfo-test.cpp b/tests/unit/tests/unittests/metainfo/propertymetainfo-test.cpp index 923743799e3..25436264aee 100644 --- a/tests/unit/tests/unittests/metainfo/propertymetainfo-test.cpp +++ b/tests/unit/tests/unittests/metainfo/propertymetainfo-test.cpp @@ -179,7 +179,9 @@ TEST_F(PropertyMetaInfo, default_is_not_list) TEST_F(PropertyMetaInfo, is_enumeration) { - auto enumInfo = createNodeMetaInfo("QtQuick", "MyEnum", TypeTraits::IsEnum); + TypeTraits traits; + traits.isEnum = true; + auto enumInfo = createNodeMetaInfo("QtQuick", "MyEnum", traits); projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, enumInfo.id()); auto propertyInfo = nodeInfo.property("bar"); @@ -271,7 +273,9 @@ TEST_F(PropertyMetaInfo, default_is_not_pointer) TEST_F(PropertyMetaInfo, cast_to_enumeration) { - auto propertyTypeInfo = createNodeMetaInfo("QtQuick", "MyEnum", TypeTraits::IsEnum); + TypeTraits traits; + traits.isEnum = true; + auto propertyTypeInfo = createNodeMetaInfo("QtQuick", "MyEnum", traits); projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); auto propertyInfo = nodeInfo.property("bar"); Enumeration enumeration{"MyEnum.Foo"}; @@ -297,7 +301,9 @@ TEST_F(PropertyMetaInfo, dont_to_cast_enumeration_if_property_type_is_not_enumer TEST_F(PropertyMetaInfo, dont_to_cast_enumeration_if_value_is_not_Enumeration) { - auto propertyTypeInfo = createNodeMetaInfo("QtQuick", "MyEnum", TypeTraits::IsEnum); + TypeTraits traits; + traits.isEnum = true; + auto propertyTypeInfo = createNodeMetaInfo("QtQuick", "MyEnum", traits); projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); auto propertyInfo = nodeInfo.property("bar"); auto value = QVariant::fromValue(QString{"enumeration"}); diff --git a/tests/unit/tests/unittests/model/model-test.cpp b/tests/unit/tests/unittests/model/model-test.cpp index 71f6462c7cc..949efb3ee0a 100644 --- a/tests/unit/tests/unittests/model/model-test.cpp +++ b/tests/unit/tests/unittests/model/model-test.cpp @@ -7,9 +7,11 @@ #include #include #include +#include #include #include +#include #include #include #include @@ -32,6 +34,40 @@ MATCHER(IsSorted, std::string(negation ? "isn't sorted" : "is sorted")) return std::is_sorted(begin(arg), end(arg)); } +template +auto IsItemLibraryEntry(const QmlDesigner::NodeMetaInfo &metaInfo, + QStringView name, + QStringView iconPath, + QStringView category, + QStringView import, + QStringView toolTip, + QStringView templatePath, + PropertiesMatcher propertiesMatcher, + ExtraFilePathsMatcher extraFilePathsMatcher) +{ + using QmlDesigner::ItemLibraryEntry; + return AllOf(Property("metaInfo", &ItemLibraryEntry::metaInfo, metaInfo), + Property("name", &ItemLibraryEntry::name, name), + Property("libraryEntryIconPath", &ItemLibraryEntry::libraryEntryIconPath, iconPath), + Property("category", &ItemLibraryEntry::category, category), + Property("requiredImport", &ItemLibraryEntry::requiredImport, import), + Property("toolTip", &ItemLibraryEntry::toolTip, toolTip), + Property("qmlSource", &ItemLibraryEntry::qmlSource, templatePath), + Property("properties", &ItemLibraryEntry::properties, propertiesMatcher), + Property("extraFilePath", &ItemLibraryEntry::extraFilePaths, extraFilePathsMatcher)); +} + +MATCHER_P3(IsItemLibraryProperty, + name, + type, + value, + std::string(negation ? "isn't " : "is ") + + PrintToString(QmlDesigner::PropertyContainer(name, type, value))) +{ + const QmlDesigner::PropertyContainer &property = arg; + + return property.name() == name && property.type() == type && property.value() == value; +} class Model : public ::testing::Test { protected: @@ -912,16 +948,16 @@ TEST_F(Model, get_invalid_meta_info_by_module_for_wrong_module) ASSERT_THAT(metaInfo, IsFalse()); } -TEST_F(Model, add_refresh_callback_to_project_storage) +TEST_F(Model, add_project_storage_observer_to_project_storage) { - EXPECT_CALL(projectStorageMock, addRefreshCallback(_)); + EXPECT_CALL(projectStorageMock, addObserver(_)); QmlDesigner::Model model{{projectStorageMock, pathCacheMock}, "Item", -1, -1, nullptr, {}}; } -TEST_F(Model, remove_refresh_callback_from_project_storage) +TEST_F(Model, remove_project_storage_observer_from_project_storage) { - EXPECT_CALL(projectStorageMock, removeRefreshCallback(_)).Times(2); // there is a model in the fixture + EXPECT_CALL(projectStorageMock, removeObserver(_)).Times(2); // the fixture model is calling it too QmlDesigner::Model model{{projectStorageMock, pathCacheMock}, "Item", -1, -1, nullptr, {}}; } @@ -930,14 +966,54 @@ TEST_F(Model, refresh_callback_is_calling_abstract_view) { const QmlDesigner::TypeIds typeIds = {QmlDesigner::TypeId::create(3), QmlDesigner::TypeId::create(1)}; - std::function *callback = nullptr; - ON_CALL(projectStorageMock, addRefreshCallback(_)).WillByDefault([&](auto *c) { callback = c; }); + ProjectStorageObserverMock observerMock; + QmlDesigner::ProjectStorageObserver *observer = nullptr; + ON_CALL(projectStorageMock, addObserver(_)).WillByDefault([&](auto *o) { observer = o; }); + QmlDesigner::Model model{{projectStorageMock, pathCacheMock}, "Item", -1, -1, nullptr, {}}; model.attachView(&viewMock); EXPECT_CALL(viewMock, refreshMetaInfos(typeIds)); - (*callback)(typeIds); + observer->removedTypeIds(typeIds); +} + +TEST_F(Model, meta_infos_for_mdoule) +{ + projectStorageMock.createModule("Foo"); + auto module = model.module("Foo"); + auto typeId = projectStorageMock.createObject(module.id(), "Bar"); + ON_CALL(projectStorageMock, typeIds(module.id())) + .WillByDefault(Return(QVarLengthArray{typeId})); + + auto types = model.metaInfosForModule(module); + + ASSERT_THAT(types, ElementsAre(Eq(QmlDesigner::NodeMetaInfo{typeId, &projectStorageMock}))); +} + +TEST_F(Model, item_library_entries) +{ + using namespace Qt::StringLiterals; + QmlDesigner::Storage::Info::ItemLibraryEntries storageEntries{ + {itemTypeId, "Item", "/path/to/icon", "basic category", "QtQuick", "It's a item", "/path/to/template"}}; + storageEntries.front().properties.emplace_back("x", "double", Sqlite::ValueView::create(1)); + storageEntries.front().extraFilePaths.emplace_back("/extra/file/path"); + projectStorageMock.setItemLibraryEntries(pathCacheMock.sourceId, storageEntries); + QmlDesigner::NodeMetaInfo metaInfo{itemTypeId, &projectStorageMock}; + + auto entries = model.itemLibraryEntries(); + + ASSERT_THAT(entries, + ElementsAre( + IsItemLibraryEntry(metaInfo, + u"Item", + u"/path/to/icon", + u"basic category", + u"QtQuick", + u"It's a item", + u"/path/to/template", + ElementsAre(IsItemLibraryProperty("x", "double"_L1, QVariant{1})), + ElementsAre(u"/extra/file/path")))); } } // namespace diff --git a/tests/unit/tests/unittests/model/modelutils-test.cpp b/tests/unit/tests/unittests/model/modelutils-test.cpp index 8d69e69aba8..c7a70cfbdb3 100644 --- a/tests/unit/tests/unittests/model/modelutils-test.cpp +++ b/tests/unit/tests/unittests/model/modelutils-test.cpp @@ -31,11 +31,9 @@ protected: TEST_F(ModelUtils, component_file_path) { - auto typeId = projectStorageMock.createType(moduleId, - "Foo", - QmlDesigner::Storage::TypeTraits::IsFileComponent, - {}, - sourceId); + QmlDesigner::Storage::TypeTraits traits{QmlDesigner::Storage::TypeTraitsKind::Reference}; + traits.isFileComponent = true; + auto typeId = projectStorageMock.createType(moduleId, "Foo", traits, {}, sourceId); QmlDesigner::NodeMetaInfo metaInfo{typeId, &projectStorageMock}; auto path = QmlDesigner::ModelUtils::componentFilePath(pathCacheMock, metaInfo); @@ -64,11 +62,9 @@ TEST_F(ModelUtils, empty_component_file_path_for_invalid_meta_info) TEST_F(ModelUtils, component_file_path_for_node) { - auto typeId = projectStorageMock.createType(moduleId, - "Foo", - QmlDesigner::Storage::TypeTraits::IsFileComponent, - {}, - sourceId); + QmlDesigner::Storage::TypeTraits traits{QmlDesigner::Storage::TypeTraitsKind::Reference}; + traits.isFileComponent = true; + auto typeId = projectStorageMock.createType(moduleId, "Foo", traits, {}, sourceId); projectStorageMock.createImportedTypeNameId(pathCacheMock.sourceId, "Foo", typeId); auto node = model.createModelNode("Foo"); @@ -86,11 +82,9 @@ TEST_F(ModelUtils, component_file_path_for_invalid_node_is_empty) TEST_F(ModelUtils, component_file_path_for_node_without_metainfo_is_empty) { - projectStorageMock.createType(moduleId, - "Foo", - QmlDesigner::Storage::TypeTraits::IsFileComponent, - {}, - sourceId); + QmlDesigner::Storage::TypeTraits traits{QmlDesigner::Storage::TypeTraitsKind::Reference}; + traits.isFileComponent = true; + projectStorageMock.createType(moduleId, "Foo", traits, {}, sourceId); auto node = model.createModelNode("Foo"); auto path = QmlDesigner::ModelUtils::componentFilePath(node); diff --git a/tests/unit/tests/unittests/projectstorage/CMakeLists.txt b/tests/unit/tests/unittests/projectstorage/CMakeLists.txt index 5c3fd4011e2..a2374ae265f 100644 --- a/tests/unit/tests/unittests/projectstorage/CMakeLists.txt +++ b/tests/unit/tests/unittests/projectstorage/CMakeLists.txt @@ -11,6 +11,7 @@ extend_qtc_test(unittest sourcepathcache-test.cpp sourcepathview-test.cpp storagecache-test.cpp + typeannotationreader-test.cpp ) extend_qtc_test(unittest diff --git a/tests/unit/tests/unittests/projectstorage/projectstorage-test.cpp b/tests/unit/tests/unittests/projectstorage/projectstorage-test.cpp index f6ea7a015a7..4b3f0a18692 100644 --- a/tests/unit/tests/unittests/projectstorage/projectstorage-test.cpp +++ b/tests/unit/tests/unittests/projectstorage/projectstorage-test.cpp @@ -4,6 +4,8 @@ #include "../utils/googletest.h" #include +#include +#include #include #include @@ -16,18 +18,21 @@ namespace { +using QmlDesigner::Cache::Source; +using QmlDesigner::Cache::SourceContext; using QmlDesigner::FileStatus; using QmlDesigner::FileStatuses; +using QmlDesigner::FlagIs; using QmlDesigner::ModuleId; using QmlDesigner::PropertyDeclarationId; using QmlDesigner::SourceContextId; using QmlDesigner::SourceId; using QmlDesigner::SourceIds; -using QmlDesigner::TypeId; -using QmlDesigner::Cache::Source; -using QmlDesigner::Cache::SourceContext; -using QmlDesigner::Storage::TypeTraits; using QmlDesigner::Storage::Synchronization::SynchronizationPackage; +using QmlDesigner::Storage::Synchronization::TypeAnnotations; +using QmlDesigner::Storage::TypeTraits; +using QmlDesigner::Storage::TypeTraitsKind; +using QmlDesigner::TypeId; namespace Storage = QmlDesigner::Storage; @@ -264,6 +269,22 @@ MATCHER_P3(IsInfoType, class ProjectStorage : public testing::Test { protected: + static void SetUpTestSuite() + { + static_database = std::make_unique(":memory:", Sqlite::JournalMode::Memory); + + static_projectStorage = std::make_unique>( + *static_database, static_database->isInitialized()); + } + + static void TearDownTestSuite() + { + static_projectStorage.reset(); + static_database.reset(); + } + + ~ProjectStorage() { static_projectStorage->resetForTestsOnly(); } + template static auto toValues(Range &&range) { @@ -295,37 +316,25 @@ protected: package.imports.emplace_back(qmlModuleId, Storage::Version{}, sourceId1); package.imports.emplace_back(qtQuickModuleId, Storage::Version{}, sourceId1); package.imports.emplace_back(qmlModuleId, Storage::Version{}, sourceId2); - package.moduleDependencies.emplace_back(qmlNativeModuleId, - Storage::Version{}, - sourceId1); - package.moduleDependencies.emplace_back(qtQuickNativeModuleId, - Storage::Version{}, - sourceId1); - package.moduleDependencies.emplace_back(qmlNativeModuleId, - Storage::Version{}, - sourceId2); + package.moduleDependencies.emplace_back(qmlNativeModuleId, Storage::Version{}, sourceId1); + package.moduleDependencies.emplace_back(qtQuickNativeModuleId, Storage::Version{}, sourceId1); + package.moduleDependencies.emplace_back(qmlNativeModuleId, Storage::Version{}, sourceId2); package.updatedModuleDependencySourceIds.push_back(sourceId1); package.updatedModuleDependencySourceIds.push_back(sourceId2); importsSourceId1.emplace_back(qmlModuleId, Storage::Version{}, sourceId1); importsSourceId1.emplace_back(qtQuickModuleId, Storage::Version{}, sourceId1); - moduleDependenciesSourceId1.emplace_back(qmlNativeModuleId, - Storage::Version{}, - sourceId1); - moduleDependenciesSourceId1.emplace_back(qtQuickNativeModuleId, - Storage::Version{}, - sourceId1); + moduleDependenciesSourceId1.emplace_back(qmlNativeModuleId, Storage::Version{}, sourceId1); + moduleDependenciesSourceId1.emplace_back(qtQuickNativeModuleId, Storage::Version{}, sourceId1); importsSourceId2.emplace_back(qmlModuleId, Storage::Version{}, sourceId2); - moduleDependenciesSourceId2.emplace_back(qmlNativeModuleId, - Storage::Version{}, - sourceId2); + moduleDependenciesSourceId2.emplace_back(qmlNativeModuleId, Storage::Version{}, sourceId2); package.types.push_back(Storage::Synchronization::Type{ "QQuickItem", Storage::Synchronization::ImportedType{"QObject"}, Storage::Synchronization::ImportedType{}, - TypeTraits::Reference, + TypeTraitsKind::Reference, sourceId1, {Storage::Synchronization::ExportedType{qtQuickModuleId, "Item"}, Storage::Synchronization::ExportedType{qtQuickNativeModuleId, "QQuickItem"}}, @@ -369,14 +378,10 @@ protected: "QObject", Storage::Synchronization::ImportedType{}, Storage::Synchronization::ImportedType{}, - TypeTraits::Reference, + TypeTraitsKind::Reference, sourceId2, - {Storage::Synchronization::ExportedType{qmlModuleId, - "Object", - Storage::Version{2}}, - Storage::Synchronization::ExportedType{qmlModuleId, - "Obj", - Storage::Version{2}}, + {Storage::Synchronization::ExportedType{qmlModuleId, "Object", Storage::Version{2}}, + Storage::Synchronization::ExportedType{qmlModuleId, "Obj", Storage::Version{2}}, Storage::Synchronization::ExportedType{qmlNativeModuleId, "QObject"}}}); package.updatedSourceIds = {sourceId1, sourceId2}; @@ -389,9 +394,7 @@ protected: SynchronizationPackage package; package.imports.emplace_back(QMLModuleId, Storage::Version{}, sourceId1); - package.moduleDependencies.emplace_back(QMLModuleId, - Storage::Version{}, - sourceId1); + package.moduleDependencies.emplace_back(QMLModuleId, Storage::Version{}, sourceId1); package.updatedModuleDependencySourceIds.push_back(sourceId1); importsSourceId1.emplace_back(QMLModuleId, Storage::Version{}, sourceId1); @@ -401,7 +404,7 @@ protected: Storage::Synchronization::Type{"bool", Storage::Synchronization::ImportedType{}, Storage::Synchronization::ImportedType{}, - TypeTraits::Value, + TypeTraitsKind::Value, sourceId1, {Storage::Synchronization::ExportedType{QMLModuleId, "bool"}}}); @@ -409,7 +412,7 @@ protected: Storage::Synchronization::Type{"int", Storage::Synchronization::ImportedType{}, Storage::Synchronization::ImportedType{}, - TypeTraits::Value, + TypeTraitsKind::Value, sourceId1, {Storage::Synchronization::ExportedType{QMLModuleId, "int"}}}); @@ -417,23 +420,23 @@ protected: Storage::Synchronization::Type{"uint", Storage::Synchronization::ImportedType{}, Storage::Synchronization::ImportedType{}, - TypeTraits::Value, + TypeTraitsKind::Value, sourceId1, {Storage::Synchronization::ExportedType{QMLNativeModuleId, "uint"}}}); - package.types.push_back( - Storage::Synchronization::Type{"double", - Storage::Synchronization::ImportedType{}, - Storage::Synchronization::ImportedType{}, - TypeTraits::Value, - sourceId1, - {Storage::Synchronization::ExportedType{QMLModuleId, - "double"}}}); + package.types.push_back(Storage::Synchronization::Type{ + "double", + Storage::Synchronization::ImportedType{}, + Storage::Synchronization::ImportedType{}, + TypeTraitsKind::Value, + sourceId1, + {Storage::Synchronization::ExportedType{QMLModuleId, "double"}, + Storage::Synchronization::ExportedType{QMLModuleId, "real"}}}); package.types.push_back( Storage::Synchronization::Type{"float", Storage::Synchronization::ImportedType{}, Storage::Synchronization::ImportedType{}, - TypeTraits::Value, + TypeTraitsKind::Value, sourceId1, {Storage::Synchronization::ExportedType{QMLNativeModuleId, "float"}}}); @@ -441,7 +444,7 @@ protected: Storage::Synchronization::Type{"date", Storage::Synchronization::ImportedType{}, Storage::Synchronization::ImportedType{}, - TypeTraits::Value, + TypeTraitsKind::Value, sourceId1, {Storage::Synchronization::ExportedType{QMLModuleId, "date"}}}); @@ -449,7 +452,7 @@ protected: Storage::Synchronization::Type{"string", Storage::Synchronization::ImportedType{}, Storage::Synchronization::ImportedType{}, - TypeTraits::Value, + TypeTraitsKind::Value, sourceId1, {Storage::Synchronization::ExportedType{QMLModuleId, "string"}}}); @@ -457,7 +460,7 @@ protected: Storage::Synchronization::Type{"url", Storage::Synchronization::ImportedType{}, Storage::Synchronization::ImportedType{}, - TypeTraits::Value, + TypeTraitsKind::Value, sourceId1, {Storage::Synchronization::ExportedType{QMLModuleId, "url"}}}); @@ -465,7 +468,7 @@ protected: Storage::Synchronization::Type{"var", Storage::Synchronization::ImportedType{}, Storage::Synchronization::ImportedType{}, - TypeTraits::Value, + TypeTraitsKind::Value, sourceId1, {Storage::Synchronization::ExportedType{QMLModuleId, "var"}}}); @@ -481,35 +484,23 @@ protected: package.imports.emplace_back(qmlModuleId, Storage::Version{}, sourceId3); package.imports.emplace_back(qtQuickModuleId, Storage::Version{}, sourceId3); - package.moduleDependencies.emplace_back(qmlNativeModuleId, - Storage::Version{}, - sourceId3); - package.moduleDependencies.emplace_back(qtQuickNativeModuleId, - Storage::Version{}, - sourceId3); + package.moduleDependencies.emplace_back(qmlNativeModuleId, Storage::Version{}, sourceId3); + package.moduleDependencies.emplace_back(qtQuickNativeModuleId, Storage::Version{}, sourceId3); package.updatedModuleDependencySourceIds.push_back(sourceId3); package.imports.emplace_back(qmlModuleId, Storage::Version{}, sourceId4); package.imports.emplace_back(pathToModuleId, Storage::Version{}, sourceId4); - package.moduleDependencies.emplace_back(qmlNativeModuleId, - Storage::Version{}, - sourceId4); + package.moduleDependencies.emplace_back(qmlNativeModuleId, Storage::Version{}, sourceId4); package.updatedModuleDependencySourceIds.push_back(sourceId4); importsSourceId3.emplace_back(qmlModuleId, Storage::Version{}, sourceId3); importsSourceId3.emplace_back(qtQuickModuleId, Storage::Version{}, sourceId3); - moduleDependenciesSourceId3.emplace_back(qmlNativeModuleId, - Storage::Version{}, - sourceId3); - moduleDependenciesSourceId3.emplace_back(qtQuickNativeModuleId, - Storage::Version{}, - sourceId3); + moduleDependenciesSourceId3.emplace_back(qmlNativeModuleId, Storage::Version{}, sourceId3); + moduleDependenciesSourceId3.emplace_back(qtQuickNativeModuleId, Storage::Version{}, sourceId3); importsSourceId4.emplace_back(qmlModuleId, Storage::Version{}, sourceId4); importsSourceId4.emplace_back(pathToModuleId, Storage::Version{}, sourceId4); - moduleDependenciesSourceId4.emplace_back(qmlNativeModuleId, - Storage::Version{}, - sourceId4); + moduleDependenciesSourceId4.emplace_back(qmlNativeModuleId, Storage::Version{}, sourceId4); package.types[1].propertyDeclarations.push_back( Storage::Synchronization::PropertyDeclaration{"objects", @@ -520,7 +511,7 @@ protected: "QAliasItem", Storage::Synchronization::ImportedType{"Item"}, Storage::Synchronization::ImportedType{}, - TypeTraits::Reference, + TypeTraitsKind::Reference, sourceId3, {Storage::Synchronization::ExportedType{qtQuickModuleId, "AliasItem"}, Storage::Synchronization::ExportedType{qtQuickNativeModuleId, "QAliasItem"}}}); @@ -540,7 +531,7 @@ protected: "QObject2", Storage::Synchronization::ImportedType{}, Storage::Synchronization::ImportedType{}, - TypeTraits::Reference, + TypeTraitsKind::Reference, sourceId4, {Storage::Synchronization::ExportedType{pathToModuleId, "Object2"}, Storage::Synchronization::ExportedType{pathToModuleId, "Obj2"}}}); @@ -564,38 +555,26 @@ protected: package.imports.emplace_back(qtQuickModuleId, Storage::Version{}, sourceId1); package.imports.emplace_back(qmlModuleId, Storage::Version{}, sourceId2); - package.moduleDependencies.emplace_back(qmlNativeModuleId, - Storage::Version{}, - sourceId1); - package.moduleDependencies.emplace_back(qtQuickNativeModuleId, - Storage::Version{}, - sourceId1); - package.moduleDependencies.emplace_back(qmlNativeModuleId, - Storage::Version{}, - sourceId2); + package.moduleDependencies.emplace_back(qmlNativeModuleId, Storage::Version{}, sourceId1); + package.moduleDependencies.emplace_back(qtQuickNativeModuleId, Storage::Version{}, sourceId1); + package.moduleDependencies.emplace_back(qmlNativeModuleId, Storage::Version{}, sourceId2); package.updatedModuleDependencySourceIds.push_back(sourceId1); package.updatedModuleDependencySourceIds.push_back(sourceId2); importsSourceId1.emplace_back(qmlModuleId, Storage::Version{}, sourceId1); importsSourceId1.emplace_back(qtQuickModuleId, Storage::Version{}, sourceId1); - moduleDependenciesSourceId1.emplace_back(qmlNativeModuleId, - Storage::Version{}, - sourceId1); - moduleDependenciesSourceId1.emplace_back(qtQuickNativeModuleId, - Storage::Version{}, - sourceId1); + moduleDependenciesSourceId1.emplace_back(qmlNativeModuleId, Storage::Version{}, sourceId1); + moduleDependenciesSourceId1.emplace_back(qtQuickNativeModuleId, Storage::Version{}, sourceId1); importsSourceId2.emplace_back(qmlModuleId, Storage::Version{}, sourceId2); - moduleDependenciesSourceId2.emplace_back(qmlNativeModuleId, - Storage::Version{}, - sourceId2); + moduleDependenciesSourceId2.emplace_back(qmlNativeModuleId, Storage::Version{}, sourceId2); package.types.push_back(Storage::Synchronization::Type{ "QQuickItem", Storage::Synchronization::ImportedType{"QObject"}, Storage::Synchronization::ImportedType{}, - TypeTraits::Reference, + TypeTraitsKind::Reference, sourceId1, {Storage::Synchronization::ExportedType{qtQuickModuleId, "Item"}, Storage::Synchronization::ExportedType{qtQuickNativeModuleId, "QQuickItem"}}, @@ -612,57 +591,37 @@ protected: "QObject", Storage::Synchronization::ImportedType{}, Storage::Synchronization::ImportedType{}, - TypeTraits::Reference, + TypeTraitsKind::Reference, sourceId2, - {Storage::Synchronization::ExportedType{qmlModuleId, - "Object", - Storage::Version{2}}, - Storage::Synchronization::ExportedType{qmlModuleId, - "Obj", - Storage::Version{2}}, + {Storage::Synchronization::ExportedType{qmlModuleId, "Object", Storage::Version{2}}, + Storage::Synchronization::ExportedType{qmlModuleId, "Obj", Storage::Version{2}}, Storage::Synchronization::ExportedType{qmlNativeModuleId, "QObject"}}}); package.updatedSourceIds = {sourceId1, sourceId2}; package.imports.emplace_back(qmlModuleId, Storage::Version{}, sourceId3); package.imports.emplace_back(qtQuickModuleId, Storage::Version{}, sourceId3); - package.moduleDependencies.emplace_back(qmlNativeModuleId, - Storage::Version{}, - sourceId3); - package.moduleDependencies.emplace_back(qtQuickNativeModuleId, - Storage::Version{}, - sourceId3); + package.moduleDependencies.emplace_back(qmlNativeModuleId, Storage::Version{}, sourceId3); + package.moduleDependencies.emplace_back(qtQuickNativeModuleId, Storage::Version{}, sourceId3); package.updatedModuleDependencySourceIds.push_back(sourceId3); package.imports.emplace_back(qmlModuleId, Storage::Version{}, sourceId4); package.imports.emplace_back(pathToModuleId, Storage::Version{}, sourceId4); package.imports.emplace_back(qtQuickModuleId, Storage::Version{}, sourceId4); - package.moduleDependencies.emplace_back(qmlNativeModuleId, - Storage::Version{}, - sourceId4); - package.moduleDependencies.emplace_back(qtQuickNativeModuleId, - Storage::Version{}, - sourceId4); + package.moduleDependencies.emplace_back(qmlNativeModuleId, Storage::Version{}, sourceId4); + package.moduleDependencies.emplace_back(qtQuickNativeModuleId, Storage::Version{}, sourceId4); package.updatedModuleDependencySourceIds.push_back(sourceId4); importsSourceId3.emplace_back(qmlModuleId, Storage::Version{}, sourceId3); importsSourceId3.emplace_back(qtQuickModuleId, Storage::Version{}, sourceId3); - moduleDependenciesSourceId3.emplace_back(qmlNativeModuleId, - Storage::Version{}, - sourceId3); - moduleDependenciesSourceId3.emplace_back(qtQuickNativeModuleId, - Storage::Version{}, - sourceId3); + moduleDependenciesSourceId3.emplace_back(qmlNativeModuleId, Storage::Version{}, sourceId3); + moduleDependenciesSourceId3.emplace_back(qtQuickNativeModuleId, Storage::Version{}, sourceId3); importsSourceId4.emplace_back(qmlModuleId, Storage::Version{}, sourceId4); importsSourceId4.emplace_back(pathToModuleId, Storage::Version{}, sourceId4); importsSourceId4.emplace_back(qtQuickModuleId, Storage::Version{}, sourceId4); - moduleDependenciesSourceId4.emplace_back(qmlNativeModuleId, - Storage::Version{}, - sourceId4); - moduleDependenciesSourceId4.emplace_back(qtQuickNativeModuleId, - Storage::Version{}, - sourceId4); + moduleDependenciesSourceId4.emplace_back(qmlNativeModuleId, Storage::Version{}, sourceId4); + moduleDependenciesSourceId4.emplace_back(qtQuickNativeModuleId, Storage::Version{}, sourceId4); package.types[1].propertyDeclarations.push_back( Storage::Synchronization::PropertyDeclaration{"objects", @@ -673,7 +632,7 @@ protected: "QAliasItem", Storage::Synchronization::ImportedType{"Item"}, Storage::Synchronization::ImportedType{}, - TypeTraits::Reference, + TypeTraitsKind::Reference, sourceId3, {Storage::Synchronization::ExportedType{qtQuickModuleId, "AliasItem"}, Storage::Synchronization::ExportedType{qtQuickNativeModuleId, "QAliasItem"}}}); @@ -688,7 +647,7 @@ protected: "QObject2", Storage::Synchronization::ImportedType{}, Storage::Synchronization::ImportedType{}, - TypeTraits::Reference, + TypeTraitsKind::Reference, sourceId4, {Storage::Synchronization::ExportedType{pathToModuleId, "Object2"}, Storage::Synchronization::ExportedType{pathToModuleId, "Obj2"}}}); @@ -704,11 +663,9 @@ protected: "QChildren", Storage::Synchronization::ImportedType{}, Storage::Synchronization::ImportedType{}, - TypeTraits::Reference, + TypeTraitsKind::Reference, sourceId5, - {Storage::Synchronization::ExportedType{qtQuickModuleId, - "Children", - Storage::Version{2}}, + {Storage::Synchronization::ExportedType{qtQuickModuleId, "Children", Storage::Version{2}}, Storage::Synchronization::ExportedType{qtQuickNativeModuleId, "QChildren"}}, {Storage::Synchronization::PropertyDeclaration{"items", Storage::Synchronization::ImportedType{ @@ -722,20 +679,12 @@ protected: package.imports.emplace_back(qmlModuleId, Storage::Version{}, sourceId5); package.imports.emplace_back(qtQuickModuleId, Storage::Version{}, sourceId5); - package.moduleDependencies.emplace_back(qmlNativeModuleId, - Storage::Version{}, - sourceId5); - package.moduleDependencies.emplace_back(qtQuickNativeModuleId, - Storage::Version{}, - sourceId5); + package.moduleDependencies.emplace_back(qmlNativeModuleId, Storage::Version{}, sourceId5); + package.moduleDependencies.emplace_back(qtQuickNativeModuleId, Storage::Version{}, sourceId5); importsSourceId5.emplace_back(qmlModuleId, Storage::Version{}, sourceId5); importsSourceId5.emplace_back(qtQuickModuleId, Storage::Version{}, sourceId5); - moduleDependenciesSourceId5.emplace_back(qmlNativeModuleId, - Storage::Version{}, - sourceId5); - moduleDependenciesSourceId5.emplace_back(qtQuickNativeModuleId, - Storage::Version{}, - sourceId5); + moduleDependenciesSourceId5.emplace_back(qmlNativeModuleId, Storage::Version{}, sourceId5); + moduleDependenciesSourceId5.emplace_back(qtQuickNativeModuleId, Storage::Version{}, sourceId5); package.updatedModuleDependencySourceIds.push_back(sourceId5); package.updatedSourceIds.push_back(sourceId5); @@ -743,11 +692,9 @@ protected: "QChildren2", Storage::Synchronization::ImportedType{}, Storage::Synchronization::ImportedType{}, - TypeTraits::Reference, + TypeTraitsKind::Reference, sourceId6, - {Storage::Synchronization::ExportedType{qtQuickModuleId, - "Children2", - Storage::Version{2}}, + {Storage::Synchronization::ExportedType{qtQuickModuleId, "Children2", Storage::Version{2}}, Storage::Synchronization::ExportedType{qtQuickNativeModuleId, "QChildren2"}}, {Storage::Synchronization::PropertyDeclaration{"items", Storage::Synchronization::ImportedType{ @@ -762,12 +709,8 @@ protected: package.imports.emplace_back(qmlModuleId, Storage::Version{}, sourceId6); package.imports.emplace_back(pathToModuleId, Storage::Version{}, sourceId6); package.imports.emplace_back(qtQuickModuleId, Storage::Version{}, sourceId6); - package.moduleDependencies.emplace_back(qmlNativeModuleId, - Storage::Version{}, - sourceId6); - package.moduleDependencies.emplace_back(qtQuickNativeModuleId, - Storage::Version{}, - sourceId6); + package.moduleDependencies.emplace_back(qmlNativeModuleId, Storage::Version{}, sourceId6); + package.moduleDependencies.emplace_back(qtQuickNativeModuleId, Storage::Version{}, sourceId6); package.updatedModuleDependencySourceIds.push_back(sourceId6); package.updatedSourceIds.push_back(sourceId6); @@ -796,7 +739,7 @@ protected: "QAliasItem2", Storage::Synchronization::ImportedType{"Object"}, Storage::Synchronization::ImportedType{}, - TypeTraits::Reference, + TypeTraitsKind::Reference, sourceId5, {Storage::Synchronization::ExportedType{qtQuickModuleId, "AliasItem2"}, Storage::Synchronization::ExportedType{qtQuickNativeModuleId, "QAliasItem2"}}}); @@ -827,57 +770,39 @@ protected: "QObject", Storage::Synchronization::ImportedType{}, Storage::Synchronization::ImportedType{}, - TypeTraits::Reference, + TypeTraitsKind::Reference, sourceId1, - {Storage::Synchronization::ExportedType{qmlModuleId, - "Object", - Storage::Version{1}}, - Storage::Synchronization::ExportedType{qmlModuleId, - "Obj", - Storage::Version{1, 2}}, + {Storage::Synchronization::ExportedType{qmlModuleId, "Object", Storage::Version{1}}, + Storage::Synchronization::ExportedType{qmlModuleId, "Obj", Storage::Version{1, 2}}, Storage::Synchronization::ExportedType{qmlModuleId, "BuiltInObj"}, Storage::Synchronization::ExportedType{qmlNativeModuleId, "QObject"}}}); package.types.push_back(Storage::Synchronization::Type{ "QObject2", Storage::Synchronization::ImportedType{}, Storage::Synchronization::ImportedType{}, - TypeTraits::Reference, + TypeTraitsKind::Reference, sourceId1, - {Storage::Synchronization::ExportedType{qmlModuleId, - "Object", - Storage::Version{2, 0}}, - Storage::Synchronization::ExportedType{qmlModuleId, - "Obj", - Storage::Version{2, 3}}, + {Storage::Synchronization::ExportedType{qmlModuleId, "Object", Storage::Version{2, 0}}, + Storage::Synchronization::ExportedType{qmlModuleId, "Obj", Storage::Version{2, 3}}, Storage::Synchronization::ExportedType{qmlNativeModuleId, "QObject2"}}}); package.types.push_back(Storage::Synchronization::Type{ "QObject3", Storage::Synchronization::ImportedType{}, Storage::Synchronization::ImportedType{}, - TypeTraits::Reference, + TypeTraitsKind::Reference, sourceId1, - {Storage::Synchronization::ExportedType{qmlModuleId, - "Object", - Storage::Version{2, 11}}, - Storage::Synchronization::ExportedType{qmlModuleId, - "Obj", - Storage::Version{2, 11}}, + {Storage::Synchronization::ExportedType{qmlModuleId, "Object", Storage::Version{2, 11}}, + Storage::Synchronization::ExportedType{qmlModuleId, "Obj", Storage::Version{2, 11}}, Storage::Synchronization::ExportedType{qmlNativeModuleId, "QObject3"}}}); package.types.push_back(Storage::Synchronization::Type{ "QObject4", Storage::Synchronization::ImportedType{}, Storage::Synchronization::ImportedType{}, - TypeTraits::Reference, + TypeTraitsKind::Reference, sourceId1, - {Storage::Synchronization::ExportedType{qmlModuleId, - "Object", - Storage::Version{3, 4}}, - Storage::Synchronization::ExportedType{qmlModuleId, - "Obj", - Storage::Version{3, 4}}, - Storage::Synchronization::ExportedType{qmlModuleId, - "BuiltInObj", - Storage::Version{3, 4}}, + {Storage::Synchronization::ExportedType{qmlModuleId, "Object", Storage::Version{3, 4}}, + Storage::Synchronization::ExportedType{qmlModuleId, "Obj", Storage::Version{3, 4}}, + Storage::Synchronization::ExportedType{qmlModuleId, "BuiltInObj", Storage::Version{3, 4}}, Storage::Synchronization::ExportedType{qmlNativeModuleId, "QObject4"}}}); package.updatedSourceIds.push_back(sourceId1); @@ -897,11 +822,9 @@ protected: "QObject", Storage::Synchronization::ImportedType{}, Storage::Synchronization::ImportedType{}, - TypeTraits::Reference, + TypeTraitsKind::Reference, sourceId1, - {Storage::Synchronization::ExportedType{qmlModuleId, - "Object", - Storage::Version{}}}, + {Storage::Synchronization::ExportedType{qmlModuleId, "Object", Storage::Version{}}}, {Storage::Synchronization::PropertyDeclaration{"data", Storage::Synchronization::ImportedType{"Object"}, Storage::PropertyDeclarationTraits::IsList}, @@ -916,11 +839,9 @@ protected: "QObject2", Storage::Synchronization::ImportedType{"Object"}, Storage::Synchronization::ImportedType{}, - TypeTraits::Reference, + TypeTraitsKind::Reference, sourceId1, - {Storage::Synchronization::ExportedType{qmlModuleId, - "Object2", - Storage::Version{}}}, + {Storage::Synchronization::ExportedType{qmlModuleId, "Object2", Storage::Version{}}}, {Storage::Synchronization::PropertyDeclaration{ "data2", Storage::Synchronization::ImportedType{"Object3"}, @@ -936,11 +857,9 @@ protected: "QObject3", Storage::Synchronization::ImportedType{"Object2"}, Storage::Synchronization::ImportedType{}, - TypeTraits::Reference, + TypeTraitsKind::Reference, sourceId1, - {Storage::Synchronization::ExportedType{qmlModuleId, - "Object3", - Storage::Version{}}}, + {Storage::Synchronization::ExportedType{qmlModuleId, "Object3", Storage::Version{}}}, {Storage::Synchronization::PropertyDeclaration{"data3", Storage::Synchronization::ImportedType{ "Object2"}, @@ -960,6 +879,34 @@ protected: return package; } + auto createHeirPackage() + { + auto package = createPackageWithProperties(); + + package.types.push_back( + Storage::Synchronization::Type{"QObject4", + Storage::Synchronization::ImportedType{}, + Storage::Synchronization::ImportedType{"Object2"}, + TypeTraitsKind::Reference, + sourceId1, + {}, + {}, + {}, + {}}); + package.types.push_back( + Storage::Synchronization::Type{"QObject5", + Storage::Synchronization::ImportedType{}, + Storage::Synchronization::ImportedType{"Object2"}, + TypeTraitsKind::Reference, + sourceId1, + {}, + {}, + {}, + {}}); + + return package; + } + auto createModuleExportedImportSynchronizationPackage() { SynchronizationPackage package; @@ -970,11 +917,9 @@ protected: "QQuickItem", Storage::Synchronization::ImportedType{"Object"}, Storage::Synchronization::ImportedType{}, - TypeTraits::Reference, + TypeTraitsKind::Reference, sourceId1, - {Storage::Synchronization::ExportedType{qtQuickModuleId, - "Item", - Storage::Version{1, 0}}}}); + {Storage::Synchronization::ExportedType{qtQuickModuleId, "Item", Storage::Version{1, 0}}}}); package.updatedModuleIds.push_back(qmlModuleId); package.moduleExportedImports.emplace_back(qtQuickModuleId, @@ -985,11 +930,9 @@ protected: "QObject", Storage::Synchronization::ImportedType{}, Storage::Synchronization::ImportedType{}, - TypeTraits::Reference, + TypeTraitsKind::Reference, sourceId2, - {Storage::Synchronization::ExportedType{qmlModuleId, - "Object", - Storage::Version{1, 0}}}}); + {Storage::Synchronization::ExportedType{qmlModuleId, "Object", Storage::Version{1, 0}}}}); package.imports.emplace_back(qtQuickModuleId, Storage::Version{1}, sourceId3); package.moduleExportedImports.emplace_back(qtQuick3DModuleId, @@ -997,26 +940,24 @@ protected: Storage::Version{}, Storage::Synchronization::IsAutoVersion::Yes); package.updatedModuleIds.push_back(qtQuick3DModuleId); - package.types.push_back(Storage::Synchronization::Type{ - "QQuickItem3d", - Storage::Synchronization::ImportedType{"Item"}, - Storage::Synchronization::ImportedType{}, - TypeTraits::Reference, - sourceId3, - {Storage::Synchronization::ExportedType{qtQuick3DModuleId, - "Item3D", - Storage::Version{1, 0}}}}); + package.types.push_back( + Storage::Synchronization::Type{"QQuickItem3d", + Storage::Synchronization::ImportedType{"Item"}, + Storage::Synchronization::ImportedType{}, + TypeTraitsKind::Reference, + sourceId3, + {Storage::Synchronization::ExportedType{ + qtQuick3DModuleId, "Item3D", Storage::Version{1, 0}}}}); package.imports.emplace_back(qtQuick3DModuleId, Storage::Version{1}, sourceId4); - package.types.push_back(Storage::Synchronization::Type{ - "MyItem", - Storage::Synchronization::ImportedType{"Object"}, - Storage::Synchronization::ImportedType{}, - TypeTraits::Reference, - sourceId4, - {Storage::Synchronization::ExportedType{myModuleModuleId, - "MyItem", - Storage::Version{1, 0}}}}); + package.types.push_back( + Storage::Synchronization::Type{"MyItem", + Storage::Synchronization::ImportedType{"Object"}, + Storage::Synchronization::ImportedType{}, + TypeTraitsKind::Reference, + sourceId4, + {Storage::Synchronization::ExportedType{ + myModuleModuleId, "MyItem", Storage::Version{1, 0}}}}); package.updatedSourceIds = {sourceId1, sourceId2, sourceId3, sourceId4}; @@ -1032,14 +973,14 @@ protected: "QQuickItem", Storage::Synchronization::ImportedType{}, Storage::Synchronization::ImportedType{}, - TypeTraits::Reference, + TypeTraitsKind::Reference, sourceId1, {Storage::Synchronization::ExportedType{qtQuickModuleId, "Item", Storage::Version{1, 0}}}}); package.types.push_back( Storage::Synchronization::Type{"QObject", Storage::Synchronization::ImportedType{}, Storage::Synchronization::ImportedType{}, - TypeTraits::Reference, + TypeTraitsKind::Reference, sourceId2, {Storage::Synchronization::ExportedType{ qtQuickModuleId, "QtObject", Storage::Version{1, 0}}}}); @@ -1048,7 +989,7 @@ protected: Storage::Synchronization::Type{"QQuickItem3d", Storage::Synchronization::ImportedType{}, Storage::Synchronization::ImportedType{}, - TypeTraits::Reference, + TypeTraitsKind::Reference, sourceId3, {Storage::Synchronization::ExportedType{ qtQuickModuleId, "Item3D", Storage::Version{1, 0}}}}); @@ -1065,6 +1006,58 @@ protected: return package; } + auto createTypeAnnotions() const + { + TypeAnnotations annotations; + + TypeTraits traits{TypeTraitsKind::Reference}; + traits.canBeContainer = FlagIs::True; + traits.visibleInLibrary = FlagIs::True; + + annotations.emplace_back(sourceId4, + "Object", + qmlModuleId, + "/path/to/icon.png", + traits, + R"xy({"canBeContainer": "true", "hints": "false"})xy", + R"xy([{"name":"Foo", + "iconPath":"/path/icon", + "category":"Basic Items", + "import":"QtQuick", + "toolTip":"Foo is a Item", + "templatePath":"/path/templates/item.qml", + "properties":[["x", "double", 32.1],["y", "double", 12.3]], + "extraFilePaths":["/path/templates/frame.png", "/path/templates/frame.frag"]}, + {"name":"Bar", + "iconPath":"/path/icon2", + "category":"Basic Items", + "import":"QtQuick", + "toolTip":"Bar is a Item", + "properties":[["color", "color", "#blue"]]}])xy"); + + annotations.emplace_back(sourceId5, + "Item", + qtQuickModuleId, + "/path/to/quick.png", + traits, + R"xy({"canBeContainer": "true", "forceClip": "false"})xy", + R"xy([{"name":"Item", + "iconPath":"/path/icon3", + "category":"Advanced Items", + "import":"QtQuick", + "toolTip":"Item is an Object", + "properties":[["x", "double", 1], ["y", "double", 2]]}])xy"); + + return annotations; + } + + static auto createUpdatedTypeAnnotionSourceIds(const TypeAnnotations &annotations) + { + return Utils::transform(annotations, [](const auto &annotation) { + return annotation.sourceId; + }); + } + template static FileStatuses convert(const Range &range) { @@ -1116,9 +1109,11 @@ protected: } protected: - Sqlite::Database database{":memory:", Sqlite::JournalMode::Memory}; + inline static std::unique_ptr static_database; + Sqlite::Database &database = *static_database; //Sqlite::Database database{"/tmp/aaaaa.db", Sqlite::JournalMode::Wal}; - QmlDesigner::ProjectStorage storage{database, database.isInitialized()}; + inline static std::unique_ptr> static_projectStorage; + QmlDesigner::ProjectStorage &storage = *static_projectStorage; QmlDesigner::SourcePathCache> sourcePathCache{ storage}; QmlDesigner::SourcePathView path1{"/path1/to"}; @@ -1370,7 +1365,7 @@ TEST_F(ProjectStorage, synchronize_types_adds_new_types) ASSERT_THAT(storage.fetchTypes(), UnorderedElementsAre( - AllOf(IsStorageType(sourceId2, "QObject", TypeId{}, TypeTraits::Reference), + AllOf(IsStorageType(sourceId2, "QObject", TypeId{}, TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::exportedTypes, UnorderedElementsAre(IsExportedType(qmlModuleId, "Object"), IsExportedType(qmlModuleId, "Obj"), @@ -1378,7 +1373,7 @@ TEST_F(ProjectStorage, synchronize_types_adds_new_types) AllOf(IsStorageType(sourceId1, "QQuickItem", fetchTypeId(sourceId2, "QObject"), - TypeTraits::Reference), + TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::exportedTypes, UnorderedElementsAre(IsExportedType(qtQuickModuleId, "Item"), IsExportedType(qtQuickNativeModuleId, @@ -1394,7 +1389,7 @@ TEST_F(ProjectStorage, synchronize_types_adds_new_types_with_extension_chain) ASSERT_THAT(storage.fetchTypes(), UnorderedElementsAre( - AllOf(IsStorageType(sourceId2, "QObject", TypeId{}, TypeTraits::Reference), + AllOf(IsStorageType(sourceId2, "QObject", TypeId{}, TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::exportedTypes, UnorderedElementsAre(IsExportedType(qmlModuleId, "Object"), IsExportedType(qmlModuleId, "Obj"), @@ -1403,7 +1398,7 @@ TEST_F(ProjectStorage, synchronize_types_adds_new_types_with_extension_chain) "QQuickItem", TypeId{}, fetchTypeId(sourceId2, "QObject"), - TypeTraits::Reference), + TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::exportedTypes, UnorderedElementsAre(IsExportedType(qtQuickModuleId, "Item"), IsExportedType(qtQuickNativeModuleId, @@ -1420,7 +1415,7 @@ TEST_F(ProjectStorage, synchronize_types_adds_new_types_with_exported_prototype_ ASSERT_THAT( storage.fetchTypes(), UnorderedElementsAre( - AllOf(IsStorageType(sourceId2, "QObject", TypeId{}, TypeId{}, TypeTraits::Reference), + AllOf(IsStorageType(sourceId2, "QObject", TypeId{}, TypeId{}, TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::exportedTypes, UnorderedElementsAre(IsExportedType(qmlModuleId, "Object"), IsExportedType(qmlModuleId, "Obj"), @@ -1429,7 +1424,7 @@ TEST_F(ProjectStorage, synchronize_types_adds_new_types_with_exported_prototype_ "QQuickItem", fetchTypeId(sourceId2, "QObject"), TypeId{}, - TypeTraits::Reference), + TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::exportedTypes, UnorderedElementsAre(IsExportedType(qtQuickModuleId, "Item"), IsExportedType(qtQuickNativeModuleId, "QQuickItem")))))); @@ -1445,7 +1440,7 @@ TEST_F(ProjectStorage, synchronize_types_adds_new_types_with_exported_extension_ ASSERT_THAT(storage.fetchTypes(), UnorderedElementsAre( - AllOf(IsStorageType(sourceId2, "QObject", TypeId{}, TypeTraits::Reference), + AllOf(IsStorageType(sourceId2, "QObject", TypeId{}, TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::exportedTypes, UnorderedElementsAre(IsExportedType(qmlModuleId, "Object"), IsExportedType(qmlModuleId, "Obj"), @@ -1454,7 +1449,7 @@ TEST_F(ProjectStorage, synchronize_types_adds_new_types_with_exported_extension_ "QQuickItem", TypeId{}, fetchTypeId(sourceId2, "QObject"), - TypeTraits::Reference), + TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::exportedTypes, UnorderedElementsAre(IsExportedType(qtQuickModuleId, "Item"), IsExportedType(qtQuickNativeModuleId, @@ -1484,7 +1479,7 @@ TEST_F(ProjectStorage, synchronize_types_adds_new_types_with_missing_module) "QObject2", Storage::Synchronization::ImportedType{}, Storage::Synchronization::ImportedType{}, - TypeTraits::Reference, + TypeTraitsKind::Reference, sourceId3, {Storage::Synchronization::ExportedType{ModuleId::create(22), "Object2"}, Storage::Synchronization::ExportedType{pathToModuleId, "Obj2"}}}); @@ -1501,7 +1496,7 @@ TEST_F(ProjectStorage, synchronize_types_adds_new_types_reverse_order) ASSERT_THAT(storage.fetchTypes(), UnorderedElementsAre( - AllOf(IsStorageType(sourceId2, "QObject", TypeId{}, TypeTraits::Reference), + AllOf(IsStorageType(sourceId2, "QObject", TypeId{}, TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::exportedTypes, UnorderedElementsAre(IsExportedType(qmlModuleId, "Object"), IsExportedType(qmlModuleId, "Obj"), @@ -1509,7 +1504,7 @@ TEST_F(ProjectStorage, synchronize_types_adds_new_types_reverse_order) AllOf(IsStorageType(sourceId1, "QQuickItem", fetchTypeId(sourceId2, "QObject"), - TypeTraits::Reference), + TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::exportedTypes, UnorderedElementsAre(IsExportedType(qtQuickModuleId, "Item"), IsExportedType(qtQuickNativeModuleId, @@ -1520,23 +1515,26 @@ TEST_F(ProjectStorage, synchronize_types_overwrites_type_traits) { auto package{createSimpleSynchronizationPackage()}; storage.synchronize(package); - package.types[0].traits = TypeTraits::Value; - package.types[1].traits = TypeTraits::Value; + package.types[0].traits = TypeTraitsKind::Value; + package.types[1].traits = TypeTraitsKind::Value; storage.synchronize(SynchronizationPackage{package.imports, package.types, {sourceId1, sourceId2}}); - ASSERT_THAT( - storage.fetchTypes(), - UnorderedElementsAre( - AllOf(IsStorageType(sourceId2, "QObject", TypeId{}, TypeTraits::Value), - Field(&Storage::Synchronization::Type::exportedTypes, - UnorderedElementsAre(IsExportedType(qmlModuleId, "Object"), - IsExportedType(qmlModuleId, "Obj"), - IsExportedType(qmlNativeModuleId, "QObject")))), - AllOf(IsStorageType(sourceId1, "QQuickItem", fetchTypeId(sourceId2, "QObject"), TypeTraits::Value), - Field(&Storage::Synchronization::Type::exportedTypes, - UnorderedElementsAre(IsExportedType(qtQuickModuleId, "Item"), - IsExportedType(qtQuickNativeModuleId, "QQuickItem")))))); + ASSERT_THAT(storage.fetchTypes(), + UnorderedElementsAre( + AllOf(IsStorageType(sourceId2, "QObject", TypeId{}, TypeTraitsKind::Value), + Field(&Storage::Synchronization::Type::exportedTypes, + UnorderedElementsAre(IsExportedType(qmlModuleId, "Object"), + IsExportedType(qmlModuleId, "Obj"), + IsExportedType(qmlNativeModuleId, "QObject")))), + AllOf(IsStorageType(sourceId1, + "QQuickItem", + fetchTypeId(sourceId2, "QObject"), + TypeTraitsKind::Value), + Field(&Storage::Synchronization::Type::exportedTypes, + UnorderedElementsAre(IsExportedType(qtQuickModuleId, "Item"), + IsExportedType(qtQuickNativeModuleId, + "QQuickItem")))))); } TEST_F(ProjectStorage, synchronize_types_overwrites_sources) @@ -1559,7 +1557,7 @@ TEST_F(ProjectStorage, synchronize_types_overwrites_sources) ASSERT_THAT(storage.fetchTypes(), UnorderedElementsAre( - AllOf(IsStorageType(sourceId4, "QObject", TypeId{}, TypeTraits::Reference), + AllOf(IsStorageType(sourceId4, "QObject", TypeId{}, TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::exportedTypes, UnorderedElementsAre(IsExportedType(qmlModuleId, "Object"), IsExportedType(qmlModuleId, "Obj"), @@ -1567,7 +1565,7 @@ TEST_F(ProjectStorage, synchronize_types_overwrites_sources) AllOf(IsStorageType(sourceId3, "QQuickItem", fetchTypeId(sourceId4, "QObject"), - TypeTraits::Reference), + TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::exportedTypes, UnorderedElementsAre(IsExportedType(qtQuickModuleId, "Item"), IsExportedType(qtQuickNativeModuleId, @@ -1583,7 +1581,7 @@ TEST_F(ProjectStorage, synchronize_types_insert_type_into_prototype_chain) "QQuickObject", Storage::Synchronization::ImportedType{"QObject"}, Storage::Synchronization::ImportedType{}, - TypeTraits::Reference, + TypeTraitsKind::Reference, sourceId1, {Storage::Synchronization::ExportedType{qtQuickModuleId, "Object"}, Storage::Synchronization::ExportedType{qtQuickNativeModuleId, "QQuickObject"}}}); @@ -1594,7 +1592,7 @@ TEST_F(ProjectStorage, synchronize_types_insert_type_into_prototype_chain) ASSERT_THAT( storage.fetchTypes(), UnorderedElementsAre( - AllOf(IsStorageType(sourceId2, "QObject", TypeId{}, TypeId{}, TypeTraits::Reference), + AllOf(IsStorageType(sourceId2, "QObject", TypeId{}, TypeId{}, TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::exportedTypes, UnorderedElementsAre(IsExportedType(qmlModuleId, "Object"), IsExportedType(qmlModuleId, "Obj"), @@ -1603,7 +1601,7 @@ TEST_F(ProjectStorage, synchronize_types_insert_type_into_prototype_chain) "QQuickObject", fetchTypeId(sourceId2, "QObject"), TypeId{}, - TypeTraits::Reference), + TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::exportedTypes, UnorderedElementsAre(IsExportedType(qtQuickModuleId, "Object"), IsExportedType(qtQuickNativeModuleId, "QQuickObject")))), @@ -1611,7 +1609,7 @@ TEST_F(ProjectStorage, synchronize_types_insert_type_into_prototype_chain) "QQuickItem", fetchTypeId(sourceId1, "QQuickObject"), TypeId{}, - TypeTraits::Reference), + TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::exportedTypes, UnorderedElementsAre(IsExportedType(qtQuickModuleId, "Item"), IsExportedType(qtQuickNativeModuleId, "QQuickItem")))))); @@ -1627,7 +1625,7 @@ TEST_F(ProjectStorage, synchronize_types_insert_type_into_extension_chain) "QQuickObject", Storage::Synchronization::ImportedType{}, Storage::Synchronization::ImportedType{"QObject"}, - TypeTraits::Reference, + TypeTraitsKind::Reference, sourceId1, {Storage::Synchronization::ExportedType{qtQuickModuleId, "Object"}, Storage::Synchronization::ExportedType{qtQuickNativeModuleId, "QQuickObject"}}}); @@ -1638,7 +1636,7 @@ TEST_F(ProjectStorage, synchronize_types_insert_type_into_extension_chain) ASSERT_THAT( storage.fetchTypes(), UnorderedElementsAre( - AllOf(IsStorageType(sourceId2, "QObject", TypeId{}, TypeId{}, TypeTraits::Reference), + AllOf(IsStorageType(sourceId2, "QObject", TypeId{}, TypeId{}, TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::exportedTypes, UnorderedElementsAre(IsExportedType(qmlModuleId, "Object"), IsExportedType(qmlModuleId, "Obj"), @@ -1647,7 +1645,7 @@ TEST_F(ProjectStorage, synchronize_types_insert_type_into_extension_chain) "QQuickObject", TypeId{}, fetchTypeId(sourceId2, "QObject"), - TypeTraits::Reference), + TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::exportedTypes, UnorderedElementsAre(IsExportedType(qtQuickModuleId, "Object"), IsExportedType(qtQuickNativeModuleId, "QQuickObject")))), @@ -1655,7 +1653,7 @@ TEST_F(ProjectStorage, synchronize_types_insert_type_into_extension_chain) "QQuickItem", TypeId{}, fetchTypeId(sourceId1, "QQuickObject"), - TypeTraits::Reference), + TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::exportedTypes, UnorderedElementsAre(IsExportedType(qtQuickModuleId, "Item"), IsExportedType(qtQuickNativeModuleId, "QQuickItem")))))); @@ -1670,7 +1668,7 @@ TEST_F(ProjectStorage, synchronize_types_add_qualified_prototype) "QQuickObject", Storage::Synchronization::ImportedType{"QObject"}, Storage::Synchronization::ImportedType{}, - TypeTraits::Reference, + TypeTraitsKind::Reference, sourceId1, {Storage::Synchronization::ExportedType{qtQuickModuleId, "Object"}, Storage::Synchronization::ExportedType{qtQuickNativeModuleId, "QQuickObject"}}}); @@ -1680,7 +1678,7 @@ TEST_F(ProjectStorage, synchronize_types_add_qualified_prototype) ASSERT_THAT( storage.fetchTypes(), UnorderedElementsAre( - AllOf(IsStorageType(sourceId2, "QObject", TypeId{}, TypeId{}, TypeTraits::Reference), + AllOf(IsStorageType(sourceId2, "QObject", TypeId{}, TypeId{}, TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::exportedTypes, UnorderedElementsAre(IsExportedType(qmlModuleId, "Object"), IsExportedType(qmlModuleId, "Obj"), @@ -1689,7 +1687,7 @@ TEST_F(ProjectStorage, synchronize_types_add_qualified_prototype) "QQuickObject", fetchTypeId(sourceId2, "QObject"), TypeId{}, - TypeTraits::Reference), + TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::exportedTypes, UnorderedElementsAre(IsExportedType(qtQuickModuleId, "Object"), IsExportedType(qtQuickNativeModuleId, "QQuickObject")))), @@ -1697,7 +1695,7 @@ TEST_F(ProjectStorage, synchronize_types_add_qualified_prototype) "QQuickItem", fetchTypeId(sourceId1, "QQuickObject"), TypeId{}, - TypeTraits::Reference), + TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::exportedTypes, UnorderedElementsAre(IsExportedType(qtQuickModuleId, "Item"), IsExportedType(qtQuickNativeModuleId, "QQuickItem")))))); @@ -1713,7 +1711,7 @@ TEST_F(ProjectStorage, synchronize_types_add_qualified_extension) "QQuickObject", Storage::Synchronization::ImportedType{}, Storage::Synchronization::ImportedType{"QObject"}, - TypeTraits::Reference, + TypeTraitsKind::Reference, sourceId1, {Storage::Synchronization::ExportedType{qtQuickModuleId, "Object"}, Storage::Synchronization::ExportedType{qtQuickNativeModuleId, "QQuickObject"}}}); @@ -1723,7 +1721,7 @@ TEST_F(ProjectStorage, synchronize_types_add_qualified_extension) ASSERT_THAT( storage.fetchTypes(), UnorderedElementsAre( - AllOf(IsStorageType(sourceId2, "QObject", TypeId{}, TypeId{}, TypeTraits::Reference), + AllOf(IsStorageType(sourceId2, "QObject", TypeId{}, TypeId{}, TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::exportedTypes, UnorderedElementsAre(IsExportedType(qmlModuleId, "Object"), IsExportedType(qmlModuleId, "Obj"), @@ -1732,7 +1730,7 @@ TEST_F(ProjectStorage, synchronize_types_add_qualified_extension) "QQuickObject", TypeId{}, fetchTypeId(sourceId2, "QObject"), - TypeTraits::Reference), + TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::exportedTypes, UnorderedElementsAre(IsExportedType(qtQuickModuleId, "Object"), IsExportedType(qtQuickNativeModuleId, "QQuickObject")))), @@ -1740,7 +1738,7 @@ TEST_F(ProjectStorage, synchronize_types_add_qualified_extension) "QQuickItem", TypeId{}, fetchTypeId(sourceId1, "QQuickObject"), - TypeTraits::Reference), + TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::exportedTypes, UnorderedElementsAre(IsExportedType(qtQuickModuleId, "Item"), IsExportedType(qtQuickNativeModuleId, "QQuickItem")))))); @@ -1753,7 +1751,7 @@ TEST_F(ProjectStorage, synchronize_types_throws_for_missing_prototype) "QQuickItem", Storage::Synchronization::ImportedType{"QObject"}, Storage::Synchronization::ImportedType{}, - TypeTraits::Reference, + TypeTraitsKind::Reference, sourceId1, {Storage::Synchronization::ExportedType{qtQuickModuleId, "Item"}, Storage::Synchronization::ExportedType{qtQuickNativeModuleId, "QQuickItem"}}}}; @@ -1768,7 +1766,7 @@ TEST_F(ProjectStorage, synchronize_types_throws_for_missing_extension) "QQuickItem", Storage::Synchronization::ImportedType{}, Storage::Synchronization::ImportedType{"QObject"}, - TypeTraits::Reference, + TypeTraitsKind::Reference, sourceId1, {Storage::Synchronization::ExportedType{qtQuickModuleId, "Item"}, Storage::Synchronization::ExportedType{qtQuickNativeModuleId, "QQuickItem"}}}}; @@ -1783,7 +1781,7 @@ TEST_F(ProjectStorage, synchronize_types_throws_for_invalid_module) Storage::Synchronization::Type{"QQuickItem", Storage::Synchronization::ImportedType{"QObject"}, Storage::Synchronization::ImportedType{}, - TypeTraits::Reference, + TypeTraitsKind::Reference, sourceId1, {Storage::Synchronization::ExportedType{ModuleId{}, "Item"}}}}; @@ -1797,7 +1795,7 @@ TEST_F(ProjectStorage, type_with_invalid_source_id_throws) Storage::Synchronization::Type{"QQuickItem", Storage::Synchronization::ImportedType{""}, Storage::Synchronization::ImportedType{}, - TypeTraits::Reference, + TypeTraitsKind::Reference, SourceId{}, {Storage::Synchronization::ExportedType{qtQuickModuleId, "Item"}}}}; @@ -1815,7 +1813,7 @@ TEST_F(ProjectStorage, delete_type_if_source_id_is_synchronized) ASSERT_THAT(storage.fetchTypes(), UnorderedElementsAre( - AllOf(IsStorageType(sourceId2, "QObject", TypeId{}, TypeTraits::Reference), + AllOf(IsStorageType(sourceId2, "QObject", TypeId{}, TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::exportedTypes, UnorderedElementsAre(IsExportedType(qmlModuleId, "Object"), IsExportedType(qmlModuleId, "Obj"), @@ -1831,7 +1829,7 @@ TEST_F(ProjectStorage, dont_delete_type_if_source_id_is_not_synchronized) ASSERT_THAT(storage.fetchTypes(), UnorderedElementsAre( - AllOf(IsStorageType(sourceId2, "QObject", TypeId{}, TypeTraits::Reference), + AllOf(IsStorageType(sourceId2, "QObject", TypeId{}, TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::exportedTypes, UnorderedElementsAre(IsExportedType(qmlModuleId, "Object"), IsExportedType(qmlModuleId, "Obj"), @@ -1839,7 +1837,7 @@ TEST_F(ProjectStorage, dont_delete_type_if_source_id_is_not_synchronized) AllOf(IsStorageType(sourceId1, "QQuickItem", fetchTypeId(sourceId2, "QObject"), - TypeTraits::Reference), + TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::exportedTypes, UnorderedElementsAre(IsExportedType(qtQuickModuleId, "Item"), IsExportedType(qtQuickNativeModuleId, @@ -1856,7 +1854,7 @@ TEST_F(ProjectStorage, update_exported_types_if_type_name_changes) ASSERT_THAT(storage.fetchTypes(), UnorderedElementsAre( - AllOf(IsStorageType(sourceId2, "QObject", TypeId{}, TypeTraits::Reference), + AllOf(IsStorageType(sourceId2, "QObject", TypeId{}, TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::exportedTypes, UnorderedElementsAre(IsExportedType(qmlModuleId, "Object"), IsExportedType(qmlModuleId, "Obj"), @@ -1864,7 +1862,7 @@ TEST_F(ProjectStorage, update_exported_types_if_type_name_changes) AllOf(IsStorageType(sourceId1, "QQuickItem2", fetchTypeId(sourceId2, "QObject"), - TypeTraits::Reference), + TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::exportedTypes, UnorderedElementsAre(IsExportedType(qtQuickModuleId, "Item"), IsExportedType(qtQuickNativeModuleId, @@ -1900,19 +1898,22 @@ TEST_F(ProjectStorage, synchronize_types_add_property_declarations) storage.synchronize(package); - ASSERT_THAT( - storage.fetchTypes(), - Contains(AllOf( - IsStorageType(sourceId1, "QQuickItem", fetchTypeId(sourceId2, "QObject"), TypeTraits::Reference), - Field(&Storage::Synchronization::Type::propertyDeclarations, - UnorderedElementsAre( - IsPropertyDeclaration("data", - fetchTypeId(sourceId2, "QObject"), - Storage::PropertyDeclarationTraits::IsList), - IsPropertyDeclaration("children", - fetchTypeId(sourceId1, "QQuickItem"), - Storage::PropertyDeclarationTraits::IsList - | Storage::PropertyDeclarationTraits::IsReadOnly)))))); + ASSERT_THAT(storage.fetchTypes(), + Contains( + AllOf(IsStorageType(sourceId1, + "QQuickItem", + fetchTypeId(sourceId2, "QObject"), + TypeTraitsKind::Reference), + Field(&Storage::Synchronization::Type::propertyDeclarations, + UnorderedElementsAre( + IsPropertyDeclaration("data", + fetchTypeId(sourceId2, "QObject"), + Storage::PropertyDeclarationTraits::IsList), + IsPropertyDeclaration( + "children", + fetchTypeId(sourceId1, "QQuickItem"), + Storage::PropertyDeclarationTraits::IsList + | Storage::PropertyDeclarationTraits::IsReadOnly)))))); } TEST_F(ProjectStorage, synchronize_types_add_property_declarations_with_missing_imports) @@ -1932,26 +1933,29 @@ TEST_F(ProjectStorage, synchronize_types_add_property_declaration_qualified_type Storage::Synchronization::Type{"QQuickObject", Storage::Synchronization::ImportedType{"QObject"}, Storage::Synchronization::ImportedType{}, - TypeTraits::Reference, + TypeTraitsKind::Reference, sourceId1, {Storage::Synchronization::ExportedType{qtQuickModuleId, "Object"}}}); storage.synchronize(package); - ASSERT_THAT( - storage.fetchTypes(), - Contains(AllOf( - IsStorageType(sourceId1, "QQuickItem", fetchTypeId(sourceId2, "QObject"), TypeTraits::Reference), - Field(&Storage::Synchronization::Type::propertyDeclarations, - UnorderedElementsAre( - IsPropertyDeclaration("data", - fetchTypeId(sourceId1, "QQuickObject"), - Storage::PropertyDeclarationTraits::IsList), - IsPropertyDeclaration("children", - fetchTypeId(sourceId1, "QQuickItem"), - Storage::PropertyDeclarationTraits::IsList - | Storage::PropertyDeclarationTraits::IsReadOnly)))))); + ASSERT_THAT(storage.fetchTypes(), + Contains( + AllOf(IsStorageType(sourceId1, + "QQuickItem", + fetchTypeId(sourceId2, "QObject"), + TypeTraitsKind::Reference), + Field(&Storage::Synchronization::Type::propertyDeclarations, + UnorderedElementsAre( + IsPropertyDeclaration("data", + fetchTypeId(sourceId1, "QQuickObject"), + Storage::PropertyDeclarationTraits::IsList), + IsPropertyDeclaration( + "children", + fetchTypeId(sourceId1, "QQuickItem"), + Storage::PropertyDeclarationTraits::IsList + | Storage::PropertyDeclarationTraits::IsReadOnly)))))); } TEST_F(ProjectStorage, synchronize_types_changes_property_declaration_type) @@ -1963,19 +1967,22 @@ TEST_F(ProjectStorage, synchronize_types_changes_property_declaration_type) storage.synchronize(SynchronizationPackage{importsSourceId1, {package.types[0]}, {sourceId1}}); - ASSERT_THAT( - storage.fetchTypes(), - Contains(AllOf( - IsStorageType(sourceId1, "QQuickItem", fetchTypeId(sourceId2, "QObject"), TypeTraits::Reference), - Field(&Storage::Synchronization::Type::propertyDeclarations, - UnorderedElementsAre( - IsPropertyDeclaration("data", - fetchTypeId(sourceId1, "QQuickItem"), - Storage::PropertyDeclarationTraits::IsList), - IsPropertyDeclaration("children", - fetchTypeId(sourceId1, "QQuickItem"), - Storage::PropertyDeclarationTraits::IsList - | Storage::PropertyDeclarationTraits::IsReadOnly)))))); + ASSERT_THAT(storage.fetchTypes(), + Contains( + AllOf(IsStorageType(sourceId1, + "QQuickItem", + fetchTypeId(sourceId2, "QObject"), + TypeTraitsKind::Reference), + Field(&Storage::Synchronization::Type::propertyDeclarations, + UnorderedElementsAre( + IsPropertyDeclaration("data", + fetchTypeId(sourceId1, "QQuickItem"), + Storage::PropertyDeclarationTraits::IsList), + IsPropertyDeclaration( + "children", + fetchTypeId(sourceId1, "QQuickItem"), + Storage::PropertyDeclarationTraits::IsList + | Storage::PropertyDeclarationTraits::IsReadOnly)))))); } TEST_F(ProjectStorage, synchronize_types_changes_declaration_traits) @@ -1989,7 +1996,7 @@ TEST_F(ProjectStorage, synchronize_types_changes_declaration_traits) ASSERT_THAT( storage.fetchTypes(), Contains(AllOf( - IsStorageType(sourceId1, "QQuickItem", fetchTypeId(sourceId2, "QObject"), TypeTraits::Reference), + IsStorageType(sourceId1, "QQuickItem", fetchTypeId(sourceId2, "QObject"), TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::propertyDeclarations, UnorderedElementsAre( IsPropertyDeclaration("data", @@ -2014,7 +2021,7 @@ TEST_F(ProjectStorage, synchronize_types_changes_declaration_traits_and_type) ASSERT_THAT( storage.fetchTypes(), Contains(AllOf( - IsStorageType(sourceId1, "QQuickItem", fetchTypeId(sourceId2, "QObject"), TypeTraits::Reference), + IsStorageType(sourceId1, "QQuickItem", fetchTypeId(sourceId2, "QObject"), TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::propertyDeclarations, UnorderedElementsAre( IsPropertyDeclaration("data", @@ -2038,7 +2045,7 @@ TEST_F(ProjectStorage, synchronize_types_removes_a_property_declaration) Contains(AllOf(IsStorageType(sourceId1, "QQuickItem", fetchTypeId(sourceId2, "QObject"), - TypeTraits::Reference), + TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::propertyDeclarations, UnorderedElementsAre(IsPropertyDeclaration( "data", @@ -2061,7 +2068,7 @@ TEST_F(ProjectStorage, synchronize_types_adds_a_property_declaration) ASSERT_THAT( storage.fetchTypes(), Contains(AllOf( - IsStorageType(sourceId1, "QQuickItem", fetchTypeId(sourceId2, "QObject"), TypeTraits::Reference), + IsStorageType(sourceId1, "QQuickItem", fetchTypeId(sourceId2, "QObject"), TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::propertyDeclarations, UnorderedElementsAre( IsPropertyDeclaration("object", @@ -2084,19 +2091,22 @@ TEST_F(ProjectStorage, synchronize_types_rename_a_property_declaration) storage.synchronize(SynchronizationPackage{importsSourceId1, {package.types[0]}, {sourceId1}}); - ASSERT_THAT( - storage.fetchTypes(), - Contains(AllOf( - IsStorageType(sourceId1, "QQuickItem", fetchTypeId(sourceId2, "QObject"), TypeTraits::Reference), - Field(&Storage::Synchronization::Type::propertyDeclarations, - UnorderedElementsAre( - IsPropertyDeclaration("data", - fetchTypeId(sourceId2, "QObject"), - Storage::PropertyDeclarationTraits::IsList), - IsPropertyDeclaration("objects", - fetchTypeId(sourceId1, "QQuickItem"), - Storage::PropertyDeclarationTraits::IsList - | Storage::PropertyDeclarationTraits::IsReadOnly)))))); + ASSERT_THAT(storage.fetchTypes(), + Contains( + AllOf(IsStorageType(sourceId1, + "QQuickItem", + fetchTypeId(sourceId2, "QObject"), + TypeTraitsKind::Reference), + Field(&Storage::Synchronization::Type::propertyDeclarations, + UnorderedElementsAre( + IsPropertyDeclaration("data", + fetchTypeId(sourceId2, "QObject"), + Storage::PropertyDeclarationTraits::IsList), + IsPropertyDeclaration( + "objects", + fetchTypeId(sourceId1, "QQuickItem"), + Storage::PropertyDeclarationTraits::IsList + | Storage::PropertyDeclarationTraits::IsReadOnly)))))); } TEST_F(ProjectStorage, using_non_existing_property_type_throws) @@ -2154,7 +2164,7 @@ TEST_F(ProjectStorage, synchronize_types_add_function_declarations) AllOf(IsStorageType(sourceId1, "QQuickItem", fetchTypeId(sourceId2, "QObject"), - TypeTraits::Reference), + TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::functionDeclarations, UnorderedElementsAre(Eq(package.types[0].functionDeclarations[0]), Eq(package.types[0].functionDeclarations[1])))))); @@ -2173,7 +2183,7 @@ TEST_F(ProjectStorage, synchronize_types_changes_function_declaration_return_typ AllOf(IsStorageType(sourceId1, "QQuickItem", fetchTypeId(sourceId2, "QObject"), - TypeTraits::Reference), + TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::functionDeclarations, UnorderedElementsAre(Eq(package.types[0].functionDeclarations[0]), Eq(package.types[0].functionDeclarations[1])))))); @@ -2192,7 +2202,7 @@ TEST_F(ProjectStorage, synchronize_types_changes_function_declaration_name) AllOf(IsStorageType(sourceId1, "QQuickItem", fetchTypeId(sourceId2, "QObject"), - TypeTraits::Reference), + TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::functionDeclarations, UnorderedElementsAre(Eq(package.types[0].functionDeclarations[0]), Eq(package.types[0].functionDeclarations[1])))))); @@ -2211,7 +2221,7 @@ TEST_F(ProjectStorage, synchronize_types_changes_function_declaration_pop_parame AllOf(IsStorageType(sourceId1, "QQuickItem", fetchTypeId(sourceId2, "QObject"), - TypeTraits::Reference), + TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::functionDeclarations, UnorderedElementsAre(Eq(package.types[0].functionDeclarations[0]), Eq(package.types[0].functionDeclarations[1])))))); @@ -2231,7 +2241,7 @@ TEST_F(ProjectStorage, synchronize_types_changes_function_declaration_append_par AllOf(IsStorageType(sourceId1, "QQuickItem", fetchTypeId(sourceId2, "QObject"), - TypeTraits::Reference), + TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::functionDeclarations, UnorderedElementsAre(Eq(package.types[0].functionDeclarations[0]), Eq(package.types[0].functionDeclarations[1])))))); @@ -2250,7 +2260,7 @@ TEST_F(ProjectStorage, synchronize_types_changes_function_declaration_change_par AllOf(IsStorageType(sourceId1, "QQuickItem", fetchTypeId(sourceId2, "QObject"), - TypeTraits::Reference), + TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::functionDeclarations, UnorderedElementsAre(Eq(package.types[0].functionDeclarations[0]), Eq(package.types[0].functionDeclarations[1])))))); @@ -2269,7 +2279,7 @@ TEST_F(ProjectStorage, synchronize_types_changes_function_declaration_change_par AllOf(IsStorageType(sourceId1, "QQuickItem", fetchTypeId(sourceId2, "QObject"), - TypeTraits::Reference), + TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::functionDeclarations, UnorderedElementsAre(Eq(package.types[0].functionDeclarations[0]), Eq(package.types[0].functionDeclarations[1])))))); @@ -2289,7 +2299,7 @@ TEST_F(ProjectStorage, synchronize_types_changes_function_declaration_change_par AllOf(IsStorageType(sourceId1, "QQuickItem", fetchTypeId(sourceId2, "QObject"), - TypeTraits::Reference), + TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::functionDeclarations, UnorderedElementsAre(Eq(package.types[0].functionDeclarations[0]), Eq(package.types[0].functionDeclarations[1])))))); @@ -2308,7 +2318,7 @@ TEST_F(ProjectStorage, synchronize_types_removes_function_declaration) AllOf(IsStorageType(sourceId1, "QQuickItem", fetchTypeId(sourceId2, "QObject"), - TypeTraits::Reference), + TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::functionDeclarations, UnorderedElementsAre(Eq(package.types[0].functionDeclarations[0])))))); } @@ -2327,7 +2337,7 @@ TEST_F(ProjectStorage, synchronize_types_add_function_declaration) AllOf(IsStorageType(sourceId1, "QQuickItem", fetchTypeId(sourceId2, "QObject"), - TypeTraits::Reference), + TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::functionDeclarations, UnorderedElementsAre(Eq(package.types[0].functionDeclarations[0]), Eq(package.types[0].functionDeclarations[1]), @@ -2347,7 +2357,7 @@ TEST_F(ProjectStorage, synchronize_types_add_function_declarations_with_overload AllOf(IsStorageType(sourceId1, "QQuickItem", fetchTypeId(sourceId2, "QObject"), - TypeTraits::Reference), + TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::functionDeclarations, UnorderedElementsAre(Eq(package.types[0].functionDeclarations[0]), Eq(package.types[0].functionDeclarations[1]), @@ -2368,7 +2378,7 @@ TEST_F(ProjectStorage, synchronize_types_function_declarations_adding_overload) AllOf(IsStorageType(sourceId1, "QQuickItem", fetchTypeId(sourceId2, "QObject"), - TypeTraits::Reference), + TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::functionDeclarations, UnorderedElementsAre(Eq(package.types[0].functionDeclarations[0]), Eq(package.types[0].functionDeclarations[1]), @@ -2390,7 +2400,7 @@ TEST_F(ProjectStorage, synchronize_types_function_declarations_removing_overload AllOf(IsStorageType(sourceId1, "QQuickItem", fetchTypeId(sourceId2, "QObject"), - TypeTraits::Reference), + TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::functionDeclarations, UnorderedElementsAre(Eq(package.types[0].functionDeclarations[0]), Eq(package.types[0].functionDeclarations[1])))))); @@ -2407,7 +2417,7 @@ TEST_F(ProjectStorage, synchronize_types_add_signal_declarations) AllOf(IsStorageType(sourceId1, "QQuickItem", fetchTypeId(sourceId2, "QObject"), - TypeTraits::Reference), + TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::signalDeclarations, UnorderedElementsAre(Eq(package.types[0].signalDeclarations[0]), Eq(package.types[0].signalDeclarations[1])))))); @@ -2426,7 +2436,7 @@ TEST_F(ProjectStorage, synchronize_types_changes_signal_declaration_name) AllOf(IsStorageType(sourceId1, "QQuickItem", fetchTypeId(sourceId2, "QObject"), - TypeTraits::Reference), + TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::signalDeclarations, UnorderedElementsAre(Eq(package.types[0].signalDeclarations[0]), Eq(package.types[0].signalDeclarations[1])))))); @@ -2445,7 +2455,7 @@ TEST_F(ProjectStorage, synchronize_types_changes_signal_declaration_pop_paramete AllOf(IsStorageType(sourceId1, "QQuickItem", fetchTypeId(sourceId2, "QObject"), - TypeTraits::Reference), + TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::signalDeclarations, UnorderedElementsAre(Eq(package.types[0].signalDeclarations[0]), Eq(package.types[0].signalDeclarations[1])))))); @@ -2465,7 +2475,7 @@ TEST_F(ProjectStorage, synchronize_types_changes_signal_declaration_append_param AllOf(IsStorageType(sourceId1, "QQuickItem", fetchTypeId(sourceId2, "QObject"), - TypeTraits::Reference), + TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::signalDeclarations, UnorderedElementsAre(Eq(package.types[0].signalDeclarations[0]), Eq(package.types[0].signalDeclarations[1])))))); @@ -2484,7 +2494,7 @@ TEST_F(ProjectStorage, synchronize_types_changes_signal_declaration_change_param AllOf(IsStorageType(sourceId1, "QQuickItem", fetchTypeId(sourceId2, "QObject"), - TypeTraits::Reference), + TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::signalDeclarations, UnorderedElementsAre(Eq(package.types[0].signalDeclarations[0]), Eq(package.types[0].signalDeclarations[1])))))); @@ -2503,7 +2513,7 @@ TEST_F(ProjectStorage, synchronize_types_changes_signal_declaration_change_param AllOf(IsStorageType(sourceId1, "QQuickItem", fetchTypeId(sourceId2, "QObject"), - TypeTraits::Reference), + TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::signalDeclarations, UnorderedElementsAre(Eq(package.types[0].signalDeclarations[0]), Eq(package.types[0].signalDeclarations[1])))))); @@ -2522,7 +2532,7 @@ TEST_F(ProjectStorage, synchronize_types_changes_signal_declaration_change_param AllOf(IsStorageType(sourceId1, "QQuickItem", fetchTypeId(sourceId2, "QObject"), - TypeTraits::Reference), + TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::signalDeclarations, UnorderedElementsAre(Eq(package.types[0].signalDeclarations[0]), Eq(package.types[0].signalDeclarations[1])))))); @@ -2541,7 +2551,7 @@ TEST_F(ProjectStorage, synchronize_types_removes_signal_declaration) AllOf(IsStorageType(sourceId1, "QQuickItem", fetchTypeId(sourceId2, "QObject"), - TypeTraits::Reference), + TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::signalDeclarations, UnorderedElementsAre(Eq(package.types[0].signalDeclarations[0])))))); } @@ -2560,7 +2570,7 @@ TEST_F(ProjectStorage, synchronize_types_add_signal_declaration) AllOf(IsStorageType(sourceId1, "QQuickItem", fetchTypeId(sourceId2, "QObject"), - TypeTraits::Reference), + TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::signalDeclarations, UnorderedElementsAre(Eq(package.types[0].signalDeclarations[0]), Eq(package.types[0].signalDeclarations[1]), @@ -2580,7 +2590,7 @@ TEST_F(ProjectStorage, synchronize_types_add_signal_declarations_with_overloads) AllOf(IsStorageType(sourceId1, "QQuickItem", fetchTypeId(sourceId2, "QObject"), - TypeTraits::Reference), + TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::signalDeclarations, UnorderedElementsAre(Eq(package.types[0].signalDeclarations[0]), Eq(package.types[0].signalDeclarations[1]), @@ -2601,7 +2611,7 @@ TEST_F(ProjectStorage, synchronize_types_signal_declarations_adding_overload) AllOf(IsStorageType(sourceId1, "QQuickItem", fetchTypeId(sourceId2, "QObject"), - TypeTraits::Reference), + TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::signalDeclarations, UnorderedElementsAre(Eq(package.types[0].signalDeclarations[0]), Eq(package.types[0].signalDeclarations[1]), @@ -2623,7 +2633,7 @@ TEST_F(ProjectStorage, synchronize_types_signal_declarations_removing_overload) AllOf(IsStorageType(sourceId1, "QQuickItem", fetchTypeId(sourceId2, "QObject"), - TypeTraits::Reference), + TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::signalDeclarations, UnorderedElementsAre(Eq(package.types[0].signalDeclarations[0]), Eq(package.types[0].signalDeclarations[1])))))); @@ -2640,7 +2650,7 @@ TEST_F(ProjectStorage, synchronize_types_add_enumeration_declarations) IsStorageType(sourceId1, "QQuickItem", fetchTypeId(sourceId2, "QObject"), - TypeTraits::Reference), + TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::enumerationDeclarations, UnorderedElementsAre(Eq(package.types[0].enumerationDeclarations[0]), Eq(package.types[0].enumerationDeclarations[1])))))); @@ -2659,7 +2669,7 @@ TEST_F(ProjectStorage, synchronize_types_changes_enumeration_declaration_name) IsStorageType(sourceId1, "QQuickItem", fetchTypeId(sourceId2, "QObject"), - TypeTraits::Reference), + TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::enumerationDeclarations, UnorderedElementsAre(Eq(package.types[0].enumerationDeclarations[0]), Eq(package.types[0].enumerationDeclarations[1])))))); @@ -2678,7 +2688,7 @@ TEST_F(ProjectStorage, synchronize_types_changes_enumeration_declaration_pop_enu IsStorageType(sourceId1, "QQuickItem", fetchTypeId(sourceId2, "QObject"), - TypeTraits::Reference), + TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::enumerationDeclarations, UnorderedElementsAre(Eq(package.types[0].enumerationDeclarations[0]), Eq(package.types[0].enumerationDeclarations[1])))))); @@ -2698,13 +2708,14 @@ TEST_F(ProjectStorage, synchronize_types_changes_enumeration_declaration_append_ IsStorageType(sourceId1, "QQuickItem", fetchTypeId(sourceId2, "QObject"), - TypeTraits::Reference), + TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::enumerationDeclarations, UnorderedElementsAre(Eq(package.types[0].enumerationDeclarations[0]), Eq(package.types[0].enumerationDeclarations[1])))))); } -TEST_F(ProjectStorage, synchronize_types_changes_enumeration_declaration_change_enumerator_declaration_name) +TEST_F(ProjectStorage, + synchronize_types_changes_enumeration_declaration_change_enumerator_declaration_name) { auto package{createSimpleSynchronizationPackage()}; storage.synchronize(package); @@ -2717,13 +2728,14 @@ TEST_F(ProjectStorage, synchronize_types_changes_enumeration_declaration_change_ IsStorageType(sourceId1, "QQuickItem", fetchTypeId(sourceId2, "QObject"), - TypeTraits::Reference), + TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::enumerationDeclarations, UnorderedElementsAre(Eq(package.types[0].enumerationDeclarations[0]), Eq(package.types[0].enumerationDeclarations[1])))))); } -TEST_F(ProjectStorage, synchronize_types_changes_enumeration_declaration_change_enumerator_declaration_value) +TEST_F(ProjectStorage, + synchronize_types_changes_enumeration_declaration_change_enumerator_declaration_value) { auto package{createSimpleSynchronizationPackage()}; storage.synchronize(package); @@ -2736,7 +2748,7 @@ TEST_F(ProjectStorage, synchronize_types_changes_enumeration_declaration_change_ IsStorageType(sourceId1, "QQuickItem", fetchTypeId(sourceId2, "QObject"), - TypeTraits::Reference), + TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::enumerationDeclarations, UnorderedElementsAre(Eq(package.types[0].enumerationDeclarations[0]), Eq(package.types[0].enumerationDeclarations[1])))))); @@ -2757,7 +2769,7 @@ TEST_F(ProjectStorage, IsStorageType(sourceId1, "QQuickItem", fetchTypeId(sourceId2, "QObject"), - TypeTraits::Reference), + TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::enumerationDeclarations, UnorderedElementsAre(Eq(package.types[0].enumerationDeclarations[0]), Eq(package.types[0].enumerationDeclarations[1])))))); @@ -2777,7 +2789,7 @@ TEST_F(ProjectStorage, IsStorageType(sourceId1, "QQuickItem", fetchTypeId(sourceId2, "QObject"), - TypeTraits::Reference), + TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::enumerationDeclarations, UnorderedElementsAre(Eq(package.types[0].enumerationDeclarations[0]), Eq(package.types[0].enumerationDeclarations[1])))))); @@ -2795,7 +2807,7 @@ TEST_F(ProjectStorage, synchronize_types_removes_enumeration_declaration) Contains(AllOf(IsStorageType(sourceId1, "QQuickItem", fetchTypeId(sourceId2, "QObject"), - TypeTraits::Reference), + TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::enumerationDeclarations, UnorderedElementsAre( Eq(package.types[0].enumerationDeclarations[0])))))); @@ -2815,7 +2827,7 @@ TEST_F(ProjectStorage, synchronize_types_add_enumeration_declaration) IsStorageType(sourceId1, "QQuickItem", fetchTypeId(sourceId2, "QObject"), - TypeTraits::Reference), + TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::enumerationDeclarations, UnorderedElementsAre(Eq(package.types[0].enumerationDeclarations[0]), Eq(package.types[0].enumerationDeclarations[1]), @@ -2895,7 +2907,7 @@ TEST_F(ProjectStorage, synchronize_types_add_alias_declarations) IsStorageType(sourceId3, "QAliasItem", fetchTypeId(sourceId1, "QQuickItem"), - TypeTraits::Reference), + TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::propertyDeclarations, UnorderedElementsAre( IsPropertyDeclaration("items", @@ -2922,7 +2934,7 @@ TEST_F(ProjectStorage, synchronize_types_add_alias_declarations_again) IsStorageType(sourceId3, "QAliasItem", fetchTypeId(sourceId1, "QQuickItem"), - TypeTraits::Reference), + TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::propertyDeclarations, UnorderedElementsAre( IsPropertyDeclaration("items", @@ -2950,7 +2962,7 @@ TEST_F(ProjectStorage, synchronize_types_remove_alias_declarations) IsStorageType(sourceId3, "QAliasItem", fetchTypeId(sourceId1, "QQuickItem"), - TypeTraits::Reference), + TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::propertyDeclarations, UnorderedElementsAre( IsPropertyDeclaration("items", @@ -2975,6 +2987,7 @@ TEST_F(ProjectStorage, synchronize_types_add_alias_declarations_throws_for_wrong {sourceId4}}), QmlDesigner::TypeNameDoesNotExists); } + TEST_F(ProjectStorage, synchronize_types_add_alias_declarations_throws_for_wrong_property_name) { auto package{createSynchronizationPackageWithAliases()}; @@ -3003,7 +3016,7 @@ TEST_F(ProjectStorage, synchronize_types_change_alias_declarations_type_name) IsStorageType(sourceId3, "QAliasItem", fetchTypeId(sourceId1, "QQuickItem"), - TypeTraits::Reference), + TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::propertyDeclarations, UnorderedElementsAre( IsPropertyDeclaration("items", @@ -3028,21 +3041,24 @@ TEST_F(ProjectStorage, synchronize_types_change_alias_declarations_property_name ASSERT_THAT( storage.fetchTypes(), - Contains(AllOf( - IsStorageType(sourceId3, "QAliasItem", fetchTypeId(sourceId1, "QQuickItem"), TypeTraits::Reference), - Field(&Storage::Synchronization::Type::propertyDeclarations, - UnorderedElementsAre( - IsPropertyDeclaration("items", - fetchTypeId(sourceId1, "QQuickItem"), - Storage::PropertyDeclarationTraits::IsList - | Storage::PropertyDeclarationTraits::IsReadOnly), - IsPropertyDeclaration("objects", - fetchTypeId(sourceId1, "QQuickItem"), - Storage::PropertyDeclarationTraits::IsList - | Storage::PropertyDeclarationTraits::IsReadOnly), - IsPropertyDeclaration("data", - fetchTypeId(sourceId2, "QObject"), - Storage::PropertyDeclarationTraits::IsList)))))); + Contains( + AllOf(IsStorageType(sourceId3, + "QAliasItem", + fetchTypeId(sourceId1, "QQuickItem"), + TypeTraitsKind::Reference), + Field(&Storage::Synchronization::Type::propertyDeclarations, + UnorderedElementsAre( + IsPropertyDeclaration("items", + fetchTypeId(sourceId1, "QQuickItem"), + Storage::PropertyDeclarationTraits::IsList + | Storage::PropertyDeclarationTraits::IsReadOnly), + IsPropertyDeclaration("objects", + fetchTypeId(sourceId1, "QQuickItem"), + Storage::PropertyDeclarationTraits::IsList + | Storage::PropertyDeclarationTraits::IsReadOnly), + IsPropertyDeclaration("data", + fetchTypeId(sourceId2, "QObject"), + Storage::PropertyDeclarationTraits::IsList)))))); } TEST_F(ProjectStorage, synchronize_types_change_alias_declarations_to_property_declaration) @@ -3059,21 +3075,24 @@ TEST_F(ProjectStorage, synchronize_types_change_alias_declarations_to_property_d ASSERT_THAT( storage.fetchTypes(), - Contains(AllOf( - IsStorageType(sourceId3, "QAliasItem", fetchTypeId(sourceId1, "QQuickItem"), TypeTraits::Reference), - Field(&Storage::Synchronization::Type::propertyDeclarations, - UnorderedElementsAre( - IsPropertyDeclaration("items", - fetchTypeId(sourceId1, "QQuickItem"), - Storage::PropertyDeclarationTraits::IsList - | Storage::PropertyDeclarationTraits::IsReadOnly), - IsPropertyDeclaration("objects", - fetchTypeId(sourceId1, "QQuickItem"), - Storage::PropertyDeclarationTraits::IsList - | Storage::PropertyDeclarationTraits::IsReadOnly), - IsPropertyDeclaration("data", - fetchTypeId(sourceId2, "QObject"), - Storage::PropertyDeclarationTraits::IsList)))))); + Contains( + AllOf(IsStorageType(sourceId3, + "QAliasItem", + fetchTypeId(sourceId1, "QQuickItem"), + TypeTraitsKind::Reference), + Field(&Storage::Synchronization::Type::propertyDeclarations, + UnorderedElementsAre( + IsPropertyDeclaration("items", + fetchTypeId(sourceId1, "QQuickItem"), + Storage::PropertyDeclarationTraits::IsList + | Storage::PropertyDeclarationTraits::IsReadOnly), + IsPropertyDeclaration("objects", + fetchTypeId(sourceId1, "QQuickItem"), + Storage::PropertyDeclarationTraits::IsList + | Storage::PropertyDeclarationTraits::IsReadOnly), + IsPropertyDeclaration("data", + fetchTypeId(sourceId2, "QObject"), + Storage::PropertyDeclarationTraits::IsList)))))); } TEST_F(ProjectStorage, synchronize_types_change_property_declarations_to_alias_declaration) @@ -3094,7 +3113,7 @@ TEST_F(ProjectStorage, synchronize_types_change_property_declarations_to_alias_d IsStorageType(sourceId3, "QAliasItem", fetchTypeId(sourceId1, "QQuickItem"), - TypeTraits::Reference), + TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::propertyDeclarations, UnorderedElementsAre( IsPropertyDeclaration("items", @@ -3120,21 +3139,24 @@ TEST_F(ProjectStorage, synchronize_types_change_alias_target_property_declaratio ASSERT_THAT( storage.fetchTypes(), - Contains(AllOf( - IsStorageType(sourceId3, "QAliasItem", fetchTypeId(sourceId1, "QQuickItem"), TypeTraits::Reference), - Field(&Storage::Synchronization::Type::propertyDeclarations, - UnorderedElementsAre( - IsPropertyDeclaration("items", - fetchTypeId(sourceId1, "QQuickItem"), - Storage::PropertyDeclarationTraits::IsList - | Storage::PropertyDeclarationTraits::IsReadOnly), - IsPropertyDeclaration("objects", - fetchTypeId(sourceId2, "QObject"), - Storage::PropertyDeclarationTraits::IsList - | Storage::PropertyDeclarationTraits::IsReadOnly), - IsPropertyDeclaration("data", - fetchTypeId(sourceId2, "QObject"), - Storage::PropertyDeclarationTraits::IsList)))))); + Contains( + AllOf(IsStorageType(sourceId3, + "QAliasItem", + fetchTypeId(sourceId1, "QQuickItem"), + TypeTraitsKind::Reference), + Field(&Storage::Synchronization::Type::propertyDeclarations, + UnorderedElementsAre( + IsPropertyDeclaration("items", + fetchTypeId(sourceId1, "QQuickItem"), + Storage::PropertyDeclarationTraits::IsList + | Storage::PropertyDeclarationTraits::IsReadOnly), + IsPropertyDeclaration("objects", + fetchTypeId(sourceId2, "QObject"), + Storage::PropertyDeclarationTraits::IsList + | Storage::PropertyDeclarationTraits::IsReadOnly), + IsPropertyDeclaration("data", + fetchTypeId(sourceId2, "QObject"), + Storage::PropertyDeclarationTraits::IsList)))))); } TEST_F(ProjectStorage, synchronize_types_change_alias_target_property_declaration_type_name) @@ -3152,7 +3174,7 @@ TEST_F(ProjectStorage, synchronize_types_change_alias_target_property_declaratio IsStorageType(sourceId3, "QAliasItem", fetchTypeId(sourceId1, "QQuickItem"), - TypeTraits::Reference), + TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::propertyDeclarations, UnorderedElementsAre( IsPropertyDeclaration("items", @@ -3194,7 +3216,7 @@ TEST_F(ProjectStorage, synchronize_types_remove_property_declaration_and_alias) IsStorageType(sourceId3, "QAliasItem", fetchTypeId(sourceId1, "QQuickItem"), - TypeTraits::Reference), + TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::propertyDeclarations, UnorderedElementsAre( IsPropertyDeclaration("items", @@ -3236,7 +3258,7 @@ TEST_F(ProjectStorage, synchronize_types_remove_type_and_alias_property_declarat IsStorageType(sourceId3, "QAliasItem", fetchTypeId(sourceId1, "QQuickItem"), - TypeTraits::Reference), + TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::propertyDeclarations, UnorderedElementsAre( IsPropertyDeclaration("items", @@ -3261,21 +3283,24 @@ TEST_F(ProjectStorage, update_alias_property_if_property_is_overloaded) ASSERT_THAT( storage.fetchTypes(), - Contains(AllOf( - IsStorageType(sourceId3, "QAliasItem", fetchTypeId(sourceId1, "QQuickItem"), TypeTraits::Reference), - Field(&Storage::Synchronization::Type::propertyDeclarations, - UnorderedElementsAre( - IsPropertyDeclaration("items", - fetchTypeId(sourceId1, "QQuickItem"), - Storage::PropertyDeclarationTraits::IsList - | Storage::PropertyDeclarationTraits::IsReadOnly), - IsPropertyDeclaration("objects", - fetchTypeId(sourceId1, "QQuickItem"), - Storage::PropertyDeclarationTraits::IsList - | Storage::PropertyDeclarationTraits::IsReadOnly), - IsPropertyDeclaration("data", - fetchTypeId(sourceId2, "QObject"), - Storage::PropertyDeclarationTraits::IsList)))))); + Contains( + AllOf(IsStorageType(sourceId3, + "QAliasItem", + fetchTypeId(sourceId1, "QQuickItem"), + TypeTraitsKind::Reference), + Field(&Storage::Synchronization::Type::propertyDeclarations, + UnorderedElementsAre( + IsPropertyDeclaration("items", + fetchTypeId(sourceId1, "QQuickItem"), + Storage::PropertyDeclarationTraits::IsList + | Storage::PropertyDeclarationTraits::IsReadOnly), + IsPropertyDeclaration("objects", + fetchTypeId(sourceId1, "QQuickItem"), + Storage::PropertyDeclarationTraits::IsList + | Storage::PropertyDeclarationTraits::IsReadOnly), + IsPropertyDeclaration("data", + fetchTypeId(sourceId2, "QObject"), + Storage::PropertyDeclarationTraits::IsList)))))); } TEST_F(ProjectStorage, alias_property_is_overloaded) @@ -3290,21 +3315,24 @@ TEST_F(ProjectStorage, alias_property_is_overloaded) ASSERT_THAT( storage.fetchTypes(), - Contains(AllOf( - IsStorageType(sourceId3, "QAliasItem", fetchTypeId(sourceId1, "QQuickItem"), TypeTraits::Reference), - Field(&Storage::Synchronization::Type::propertyDeclarations, - UnorderedElementsAre( - IsPropertyDeclaration("items", - fetchTypeId(sourceId1, "QQuickItem"), - Storage::PropertyDeclarationTraits::IsList - | Storage::PropertyDeclarationTraits::IsReadOnly), - IsPropertyDeclaration("objects", - fetchTypeId(sourceId1, "QQuickItem"), - Storage::PropertyDeclarationTraits::IsList - | Storage::PropertyDeclarationTraits::IsReadOnly), - IsPropertyDeclaration("data", - fetchTypeId(sourceId2, "QObject"), - Storage::PropertyDeclarationTraits::IsList)))))); + Contains( + AllOf(IsStorageType(sourceId3, + "QAliasItem", + fetchTypeId(sourceId1, "QQuickItem"), + TypeTraitsKind::Reference), + Field(&Storage::Synchronization::Type::propertyDeclarations, + UnorderedElementsAre( + IsPropertyDeclaration("items", + fetchTypeId(sourceId1, "QQuickItem"), + Storage::PropertyDeclarationTraits::IsList + | Storage::PropertyDeclarationTraits::IsReadOnly), + IsPropertyDeclaration("objects", + fetchTypeId(sourceId1, "QQuickItem"), + Storage::PropertyDeclarationTraits::IsList + | Storage::PropertyDeclarationTraits::IsReadOnly), + IsPropertyDeclaration("data", + fetchTypeId(sourceId2, "QObject"), + Storage::PropertyDeclarationTraits::IsList)))))); } TEST_F(ProjectStorage, update_alias_property_if_overloaded_property_is_removed) @@ -3324,7 +3352,7 @@ TEST_F(ProjectStorage, update_alias_property_if_overloaded_property_is_removed) IsStorageType(sourceId3, "QAliasItem", fetchTypeId(sourceId1, "QQuickItem"), - TypeTraits::Reference), + TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::propertyDeclarations, UnorderedElementsAre( IsPropertyDeclaration("items", @@ -3356,7 +3384,7 @@ TEST_F(ProjectStorage, relink_alias_property) IsStorageType(sourceId3, "QAliasItem", fetchTypeId(sourceId1, "QQuickItem"), - TypeTraits::Reference), + TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::propertyDeclarations, UnorderedElementsAre( IsPropertyDeclaration("items", @@ -3397,7 +3425,7 @@ TEST_F(ProjectStorage, "QObject2", Storage::Synchronization::ImportedType{}, Storage::Synchronization::ImportedType{}, - TypeTraits::Reference, + TypeTraitsKind::Reference, sourceId5, {Storage::Synchronization::ExportedType{qtQuickModuleId, "Object2"}, Storage::Synchronization::ExportedType{qtQuickModuleId, "Obj2"}}}); @@ -3410,7 +3438,7 @@ TEST_F(ProjectStorage, IsStorageType(sourceId3, "QAliasItem", fetchTypeId(sourceId1, "QQuickItem"), - TypeTraits::Reference), + TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::propertyDeclarations, UnorderedElementsAre( IsPropertyDeclaration("items", @@ -3435,22 +3463,24 @@ TEST_F(ProjectStorage, relink_alias_property_react_to_type_name_change) storage.synchronize(SynchronizationPackage{importsSourceId1, {package.types[0]}, {sourceId1}}); - ASSERT_THAT( - storage.fetchTypes(), - Contains(AllOf( - IsStorageType(sourceId3, "QAliasItem", fetchTypeId(sourceId2, "QObject"), TypeTraits::Reference), - Field(&Storage::Synchronization::Type::propertyDeclarations, - UnorderedElementsAre( - IsPropertyDeclaration("items", - fetchTypeId(sourceId1, "QQuickItem2"), - Storage::PropertyDeclarationTraits::IsList - | Storage::PropertyDeclarationTraits::IsReadOnly), - IsPropertyDeclaration("objects", - fetchTypeId(sourceId2, "QObject"), - Storage::PropertyDeclarationTraits::IsList), - IsPropertyDeclaration("data", - fetchTypeId(sourceId2, "QObject"), - Storage::PropertyDeclarationTraits::IsList)))))); + ASSERT_THAT(storage.fetchTypes(), + Contains(AllOf( + IsStorageType(sourceId3, + "QAliasItem", + fetchTypeId(sourceId2, "QObject"), + TypeTraitsKind::Reference), + Field(&Storage::Synchronization::Type::propertyDeclarations, + UnorderedElementsAre( + IsPropertyDeclaration("items", + fetchTypeId(sourceId1, "QQuickItem2"), + Storage::PropertyDeclarationTraits::IsList + | Storage::PropertyDeclarationTraits::IsReadOnly), + IsPropertyDeclaration("objects", + fetchTypeId(sourceId2, "QObject"), + Storage::PropertyDeclarationTraits::IsList), + IsPropertyDeclaration("data", + fetchTypeId(sourceId2, "QObject"), + Storage::PropertyDeclarationTraits::IsList)))))); } TEST_F(ProjectStorage, do_not_relink_alias_property_for_deleted_type) @@ -3470,7 +3500,7 @@ TEST_F(ProjectStorage, do_not_relink_alias_property_for_deleted_type) Not(Contains(IsStorageType(sourceId3, "QAliasItem", fetchTypeId(sourceId1, "QQuickItem"), - TypeTraits::Reference)))); + TypeTraitsKind::Reference)))); } TEST_F(ProjectStorage, do_not_relink_alias_property_for_deleted_type_and_property_type) @@ -3516,7 +3546,7 @@ TEST_F(ProjectStorage, do_not_relink_alias_property_for_deleted_type_and_propert Not(Contains(IsStorageType(sourceId3, "QAliasItem", fetchTypeId(sourceId1, "QQuickItem"), - TypeTraits::Reference)))); + TypeTraitsKind::Reference)))); } TEST_F(ProjectStorage, do_not_relink_property_type_does_not_exists) @@ -3557,7 +3587,7 @@ TEST_F(ProjectStorage, change_prototype_type_name) "QQuickItem", fetchTypeId(sourceId2, "QObject3"), TypeId{}, - TypeTraits::Reference))); + TypeTraitsKind::Reference))); } TEST_F(ProjectStorage, change_extension_type_name) @@ -3574,7 +3604,7 @@ TEST_F(ProjectStorage, change_extension_type_name) "QQuickItem", TypeId{}, fetchTypeId(sourceId2, "QObject3"), - TypeTraits::Reference))); + TypeTraitsKind::Reference))); } TEST_F(ProjectStorage, change_prototype_type_module_id) @@ -3590,7 +3620,7 @@ TEST_F(ProjectStorage, change_prototype_type_module_id) "QQuickItem", fetchTypeId(sourceId2, "QObject"), TypeId{}, - TypeTraits::Reference))); + TypeTraitsKind::Reference))); } TEST_F(ProjectStorage, change_extension_type_module_id) @@ -3607,7 +3637,7 @@ TEST_F(ProjectStorage, change_extension_type_module_id) "QQuickItem", TypeId{}, fetchTypeId(sourceId2, "QObject"), - TypeTraits::Reference))); + TypeTraitsKind::Reference))); } TEST_F(ProjectStorage, change_qualified_prototype_type_module_id_throws) @@ -3656,7 +3686,7 @@ TEST_F(ProjectStorage, change_qualified_prototype_type_module_id) "QQuickItem", fetchTypeId(sourceId2, "QObject"), TypeId{}, - TypeTraits::Reference))); + TypeTraitsKind::Reference))); } TEST_F(ProjectStorage, change_qualified_extension_type_module_id) @@ -3679,7 +3709,7 @@ TEST_F(ProjectStorage, change_qualified_extension_type_module_id) "QQuickItem", TypeId{}, fetchTypeId(sourceId2, "QObject"), - TypeTraits::Reference))); + TypeTraitsKind::Reference))); } TEST_F(ProjectStorage, change_prototype_type_name_and_module_id) @@ -3702,7 +3732,7 @@ TEST_F(ProjectStorage, change_prototype_type_name_and_module_id) "QQuickItem", fetchTypeId(sourceId2, "QObject3"), TypeId{}, - TypeTraits::Reference))); + TypeTraitsKind::Reference))); } TEST_F(ProjectStorage, change_extension_type_name_and_module_id) @@ -3726,7 +3756,7 @@ TEST_F(ProjectStorage, change_extension_type_name_and_module_id) "QQuickItem", TypeId{}, fetchTypeId(sourceId2, "QObject3"), - TypeTraits::Reference))); + TypeTraitsKind::Reference))); } TEST_F(ProjectStorage, change_prototype_type_name_throws_for_wrong_native_prototupe_type_name) @@ -3766,7 +3796,7 @@ TEST_F(ProjectStorage, throw_for_prototype_chain_cycles) "QObject2", Storage::Synchronization::ImportedType{"Item"}, Storage::Synchronization::ImportedType{}, - TypeTraits::Reference, + TypeTraitsKind::Reference, sourceId3, {Storage::Synchronization::ExportedType{pathToModuleId, "Object2"}, Storage::Synchronization::ExportedType{pathToModuleId, "Obj2"}}}); @@ -3791,7 +3821,7 @@ TEST_F(ProjectStorage, throw_for_extension_chain_cycles) "QObject2", Storage::Synchronization::ImportedType{"Item"}, Storage::Synchronization::ImportedType{}, - TypeTraits::Reference, + TypeTraitsKind::Reference, sourceId3, {Storage::Synchronization::ExportedType{pathToModuleId, "Object2"}, Storage::Synchronization::ExportedType{pathToModuleId, "Obj2"}}}); @@ -3861,7 +3891,7 @@ TEST_F(ProjectStorage, recursive_aliases) Contains(AllOf(IsStorageType(sourceId5, "QAliasItem2", fetchTypeId(sourceId2, "QObject"), - TypeTraits::Reference), + TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::propertyDeclarations, ElementsAre(IsPropertyDeclaration( "objects", @@ -3883,7 +3913,7 @@ TEST_F(ProjectStorage, recursive_aliases_change_property_type) Contains(AllOf(IsStorageType(sourceId5, "QAliasItem2", fetchTypeId(sourceId2, "QObject"), - TypeTraits::Reference), + TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::propertyDeclarations, ElementsAre(IsPropertyDeclaration( "objects", @@ -3906,7 +3936,7 @@ TEST_F(ProjectStorage, update_aliases_after_injecting_property) Contains(AllOf(IsStorageType(sourceId5, "QAliasItem2", fetchTypeId(sourceId2, "QObject"), - TypeTraits::Reference), + TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::propertyDeclarations, ElementsAre(IsPropertyDeclaration( "objects", @@ -3931,7 +3961,7 @@ TEST_F(ProjectStorage, update_aliases_after_change_alias_to_property) AllOf(Contains(AllOf(IsStorageType(sourceId5, "QAliasItem2", fetchTypeId(sourceId2, "QObject"), - TypeTraits::Reference), + TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::propertyDeclarations, ElementsAre(IsPropertyDeclaration( "objects", @@ -3942,7 +3972,7 @@ TEST_F(ProjectStorage, update_aliases_after_change_alias_to_property) Contains(AllOf(IsStorageType(sourceId3, "QAliasItem", fetchTypeId(sourceId1, "QQuickItem"), - TypeTraits::Reference), + TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::propertyDeclarations, ElementsAre(IsPropertyDeclaration( "objects", @@ -3969,7 +3999,7 @@ TEST_F(ProjectStorage, update_aliases_after_change_property_to_alias) Contains(AllOf(IsStorageType(sourceId5, "QAliasItem2", fetchTypeId(sourceId2, "QObject"), - TypeTraits::Reference), + TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::propertyDeclarations, ElementsAre(IsPropertyDeclaration( "objects", @@ -4013,7 +4043,7 @@ TEST_F(ProjectStorage, qualified_prototype) Storage::Synchronization::Type{"QQuickObject", Storage::Synchronization::ImportedType{}, Storage::Synchronization::ImportedType{}, - TypeTraits::Reference, + TypeTraitsKind::Reference, sourceId3, {Storage::Synchronization::ExportedType{qtQuickModuleId, "Object"}}}); @@ -4027,7 +4057,7 @@ TEST_F(ProjectStorage, qualified_prototype) "QQuickItem", fetchTypeId(sourceId2, "QObject"), TypeId{}, - TypeTraits::Reference))); + TypeTraitsKind::Reference))); } TEST_F(ProjectStorage, qualified_extension) @@ -4040,7 +4070,7 @@ TEST_F(ProjectStorage, qualified_extension) Storage::Synchronization::Type{"QQuickObject", Storage::Synchronization::ImportedType{}, Storage::Synchronization::ImportedType{}, - TypeTraits::Reference, + TypeTraitsKind::Reference, sourceId3, {Storage::Synchronization::ExportedType{qtQuickModuleId, "Object"}}}); @@ -4054,7 +4084,7 @@ TEST_F(ProjectStorage, qualified_extension) "QQuickItem", TypeId{}, fetchTypeId(sourceId2, "QObject"), - TypeTraits::Reference))); + TypeTraitsKind::Reference))); } TEST_F(ProjectStorage, qualified_prototype_upper_down_the_module_chain_throws) @@ -4085,7 +4115,7 @@ TEST_F(ProjectStorage, qualified_prototype_upper_in_the_module_chain) Storage::Synchronization::Type{"QQuickObject", Storage::Synchronization::ImportedType{}, Storage::Synchronization::ImportedType{}, - TypeTraits::Reference, + TypeTraitsKind::Reference, sourceId3, {Storage::Synchronization::ExportedType{qtQuickModuleId, "Object"}}}); @@ -4099,7 +4129,7 @@ TEST_F(ProjectStorage, qualified_prototype_upper_in_the_module_chain) "QQuickItem", fetchTypeId(sourceId3, "QQuickObject"), TypeId{}, - TypeTraits::Reference))); + TypeTraitsKind::Reference))); } TEST_F(ProjectStorage, qualified_extension_upper_in_the_module_chain) @@ -4112,7 +4142,7 @@ TEST_F(ProjectStorage, qualified_extension_upper_in_the_module_chain) Storage::Synchronization::Type{"QQuickObject", Storage::Synchronization::ImportedType{}, Storage::Synchronization::ImportedType{}, - TypeTraits::Reference, + TypeTraitsKind::Reference, sourceId3, {Storage::Synchronization::ExportedType{qtQuickModuleId, "Object"}}}); @@ -4126,7 +4156,7 @@ TEST_F(ProjectStorage, qualified_extension_upper_in_the_module_chain) "QQuickItem", TypeId{}, fetchTypeId(sourceId3, "QQuickObject"), - TypeTraits::Reference))); + TypeTraitsKind::Reference))); } TEST_F(ProjectStorage, qualified_prototype_with_wrong_version_throws) @@ -4138,7 +4168,7 @@ TEST_F(ProjectStorage, qualified_prototype_with_wrong_version_throws) Storage::Synchronization::Type{"QQuickObject", Storage::Synchronization::ImportedType{}, Storage::Synchronization::ImportedType{}, - TypeTraits::Reference, + TypeTraitsKind::Reference, sourceId3, {Storage::Synchronization::ExportedType{qtQuickModuleId, "Object"}}}); @@ -4158,7 +4188,7 @@ TEST_F(ProjectStorage, qualified_extension_with_wrong_version_throws) Storage::Synchronization::Type{"QQuickObject", Storage::Synchronization::ImportedType{}, Storage::Synchronization::ImportedType{}, - TypeTraits::Reference, + TypeTraitsKind::Reference, sourceId3, {Storage::Synchronization::ExportedType{qtQuickModuleId, "Object"}}}); @@ -4178,7 +4208,7 @@ TEST_F(ProjectStorage, qualified_prototype_with_version) Storage::Synchronization::Type{"QQuickObject", Storage::Synchronization::ImportedType{}, Storage::Synchronization::ImportedType{}, - TypeTraits::Reference, + TypeTraitsKind::Reference, sourceId3, {Storage::Synchronization::ExportedType{qtQuickModuleId, "Object"}}}); @@ -4192,7 +4222,7 @@ TEST_F(ProjectStorage, qualified_prototype_with_version) "QQuickItem", fetchTypeId(sourceId2, "QObject"), TypeId{}, - TypeTraits::Reference))); + TypeTraitsKind::Reference))); } TEST_F(ProjectStorage, qualified_extension_with_version) @@ -4206,7 +4236,7 @@ TEST_F(ProjectStorage, qualified_extension_with_version) Storage::Synchronization::Type{"QQuickObject", Storage::Synchronization::ImportedType{}, Storage::Synchronization::ImportedType{}, - TypeTraits::Reference, + TypeTraitsKind::Reference, sourceId3, {Storage::Synchronization::ExportedType{qtQuickModuleId, "Object"}}}); @@ -4220,7 +4250,7 @@ TEST_F(ProjectStorage, qualified_extension_with_version) "QQuickItem", TypeId{}, fetchTypeId(sourceId2, "QObject"), - TypeTraits::Reference))); + TypeTraitsKind::Reference))); } TEST_F(ProjectStorage, qualified_prototype_with_version_in_the_proto_type_chain) @@ -4234,11 +4264,9 @@ TEST_F(ProjectStorage, qualified_prototype_with_version_in_the_proto_type_chain) "QQuickObject", Storage::Synchronization::ImportedType{}, Storage::Synchronization::ImportedType{}, - TypeTraits::Reference, + TypeTraitsKind::Reference, sourceId3, - {Storage::Synchronization::ExportedType{qtQuickModuleId, - "Object", - Storage::Version{2}}}}); + {Storage::Synchronization::ExportedType{qtQuickModuleId, "Object", Storage::Version{2}}}}); package.imports.emplace_back(qtQuickModuleId, Storage::Version{}, sourceId3); package.updatedSourceIds.push_back(sourceId3); @@ -4249,7 +4277,7 @@ TEST_F(ProjectStorage, qualified_prototype_with_version_in_the_proto_type_chain) "QQuickItem", fetchTypeId(sourceId3, "QQuickObject"), TypeId{}, - TypeTraits::Reference))); + TypeTraitsKind::Reference))); } TEST_F(ProjectStorage, qualified_extension_with_version_in_the_proto_type_chain) @@ -4264,11 +4292,9 @@ TEST_F(ProjectStorage, qualified_extension_with_version_in_the_proto_type_chain) "QQuickObject", Storage::Synchronization::ImportedType{}, Storage::Synchronization::ImportedType{}, - TypeTraits::Reference, + TypeTraitsKind::Reference, sourceId3, - {Storage::Synchronization::ExportedType{qtQuickModuleId, - "Object", - Storage::Version{2}}}}); + {Storage::Synchronization::ExportedType{qtQuickModuleId, "Object", Storage::Version{2}}}}); package.imports.emplace_back(qtQuickModuleId, Storage::Version{}, sourceId3); package.updatedSourceIds.push_back(sourceId3); @@ -4279,7 +4305,7 @@ TEST_F(ProjectStorage, qualified_extension_with_version_in_the_proto_type_chain) "QQuickItem", TypeId{}, fetchTypeId(sourceId3, "QQuickObject"), - TypeTraits::Reference))); + TypeTraitsKind::Reference))); } TEST_F(ProjectStorage, qualified_prototype_with_version_down_the_proto_type_chain_throws) @@ -4310,7 +4336,7 @@ TEST_F(ProjectStorage, qualified_property_declaration_type_name) Storage::Synchronization::Type{"QQuickObject", Storage::Synchronization::ImportedType{}, Storage::Synchronization::ImportedType{}, - TypeTraits::Reference, + TypeTraitsKind::Reference, sourceId3, {Storage::Synchronization::ExportedType{qtQuickModuleId, "Object"}}}); @@ -4345,7 +4371,7 @@ TEST_F(ProjectStorage, qualified_property_declaration_type_name_in_the_module_ch Storage::Synchronization::Type{"QQuickObject", Storage::Synchronization::ImportedType{}, Storage::Synchronization::ImportedType{}, - TypeTraits::Reference, + TypeTraitsKind::Reference, sourceId3, {Storage::Synchronization::ExportedType{qtQuickModuleId, "Object"}}}); @@ -4405,14 +4431,16 @@ TEST_F(ProjectStorage, change_property_type_module_id_with_qualified_type) storage.synchronize(package); - ASSERT_THAT( - storage.fetchTypes(), - Contains(AllOf( - IsStorageType(sourceId1, "QQuickItem", fetchTypeId(sourceId2, "QObject"), TypeTraits::Reference), - Field(&Storage::Synchronization::Type::propertyDeclarations, - Contains(IsPropertyDeclaration("data", - fetchTypeId(sourceId2, "QObject"), - Storage::PropertyDeclarationTraits::IsList)))))); + ASSERT_THAT(storage.fetchTypes(), + Contains(AllOf(IsStorageType(sourceId1, + "QQuickItem", + fetchTypeId(sourceId2, "QObject"), + TypeTraitsKind::Reference), + Field(&Storage::Synchronization::Type::propertyDeclarations, + Contains(IsPropertyDeclaration( + "data", + fetchTypeId(sourceId2, "QObject"), + Storage::PropertyDeclarationTraits::IsList)))))); } TEST_F(ProjectStorage, add_file_statuses) @@ -4504,7 +4532,7 @@ TEST_F(ProjectStorage, synchronize_types_without_type_name) Contains(AllOf(IsStorageType(sourceId4, "QObject2", fetchTypeId(sourceId2, "QObject"), - TypeTraits::Reference), + TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::exportedTypes, UnorderedElementsAre(IsExportedType("Object2"), IsExportedType("Obj2")))))); @@ -4514,22 +4542,23 @@ TEST_F(ProjectStorage, fetch_by_major_version_for_imported_type) { auto package{createSynchronizationPackageWithVersions()}; storage.synchronize(package); - Storage::Synchronization::Type type{ - "Item", - Storage::Synchronization::ImportedType{"Object"}, - Storage::Synchronization::ImportedType{}, - TypeTraits::Reference, - sourceId2, - {Storage::Synchronization::ExportedType{qtQuickModuleId, - "Item", - Storage::Version{}}}}; + Storage::Synchronization::Type type{"Item", + Storage::Synchronization::ImportedType{"Object"}, + Storage::Synchronization::ImportedType{}, + TypeTraitsKind::Reference, + sourceId2, + {Storage::Synchronization::ExportedType{qtQuickModuleId, + "Item", + Storage::Version{}}}}; Storage::Import import{qmlModuleId, Storage::Version{1}, sourceId2}; storage.synchronize(SynchronizationPackage{{import}, {type}, {sourceId2}}); ASSERT_THAT(storage.fetchTypes(), - Contains(IsStorageType( - sourceId2, "Item", fetchTypeId(sourceId1, "QObject"), TypeTraits::Reference))); + Contains(IsStorageType(sourceId2, + "Item", + fetchTypeId(sourceId1, "QObject"), + TypeTraitsKind::Reference))); } TEST_F(ProjectStorage, fetch_by_major_version_for_qualified_imported_type) @@ -4541,39 +4570,40 @@ TEST_F(ProjectStorage, fetch_by_major_version_for_qualified_imported_type) "Item", Storage::Synchronization::QualifiedImportedType{"Object", import}, Storage::Synchronization::ImportedType{}, - TypeTraits::Reference, + TypeTraitsKind::Reference, sourceId2, - {Storage::Synchronization::ExportedType{qtQuickModuleId, - "Item", - Storage::Version{}}}}; + {Storage::Synchronization::ExportedType{qtQuickModuleId, "Item", Storage::Version{}}}}; storage.synchronize(SynchronizationPackage{{import}, {type}, {sourceId2}}); ASSERT_THAT(storage.fetchTypes(), - Contains(IsStorageType( - sourceId2, "Item", fetchTypeId(sourceId1, "QObject"), TypeTraits::Reference))); + Contains(IsStorageType(sourceId2, + "Item", + fetchTypeId(sourceId1, "QObject"), + TypeTraitsKind::Reference))); } TEST_F(ProjectStorage, fetch_by_major_version_and_minor_version_for_imported_type) { auto package{createSynchronizationPackageWithVersions()}; storage.synchronize(package); - Storage::Synchronization::Type type{ - "Item", - Storage::Synchronization::ImportedType{"Obj"}, - Storage::Synchronization::ImportedType{}, - TypeTraits::Reference, - sourceId2, - {Storage::Synchronization::ExportedType{qtQuickModuleId, - "Item", - Storage::Version{}}}}; + Storage::Synchronization::Type type{"Item", + Storage::Synchronization::ImportedType{"Obj"}, + Storage::Synchronization::ImportedType{}, + TypeTraitsKind::Reference, + sourceId2, + {Storage::Synchronization::ExportedType{qtQuickModuleId, + "Item", + Storage::Version{}}}}; Storage::Import import{qmlModuleId, Storage::Version{1, 2}, sourceId2}; storage.synchronize(SynchronizationPackage{{import}, {type}, {sourceId2}}); ASSERT_THAT(storage.fetchTypes(), - Contains(IsStorageType( - sourceId2, "Item", fetchTypeId(sourceId1, "QObject"), TypeTraits::Reference))); + Contains(IsStorageType(sourceId2, + "Item", + fetchTypeId(sourceId1, "QObject"), + TypeTraitsKind::Reference))); } TEST_F(ProjectStorage, fetch_by_major_version_and_minor_version_for_qualified_imported_type) @@ -4585,17 +4615,17 @@ TEST_F(ProjectStorage, fetch_by_major_version_and_minor_version_for_qualified_im "Item", Storage::Synchronization::QualifiedImportedType{"Obj", import}, Storage::Synchronization::ImportedType{}, - TypeTraits::Reference, + TypeTraitsKind::Reference, sourceId2, - {Storage::Synchronization::ExportedType{qtQuickModuleId, - "Item", - Storage::Version{}}}}; + {Storage::Synchronization::ExportedType{qtQuickModuleId, "Item", Storage::Version{}}}}; storage.synchronize(SynchronizationPackage{{import}, {type}, {sourceId2}}); ASSERT_THAT(storage.fetchTypes(), - Contains(IsStorageType( - sourceId2, "Item", fetchTypeId(sourceId1, "QObject"), TypeTraits::Reference))); + Contains(IsStorageType(sourceId2, + "Item", + fetchTypeId(sourceId1, "QObject"), + TypeTraitsKind::Reference))); } TEST_F(ProjectStorage, @@ -4603,15 +4633,14 @@ TEST_F(ProjectStorage, { auto package{createSynchronizationPackageWithVersions()}; storage.synchronize(package); - Storage::Synchronization::Type type{ - "Item", - Storage::Synchronization::ImportedType{"Object"}, - Storage::Synchronization::ImportedType{}, - TypeTraits::Reference, - sourceId2, - {Storage::Synchronization::ExportedType{qtQuickModuleId, - "Item", - Storage::Version{}}}}; + Storage::Synchronization::Type type{"Item", + Storage::Synchronization::ImportedType{"Object"}, + Storage::Synchronization::ImportedType{}, + TypeTraitsKind::Reference, + sourceId2, + {Storage::Synchronization::ExportedType{qtQuickModuleId, + "Item", + Storage::Version{}}}}; Storage::Import import{qmlModuleId, Storage::Version{1, 1}, sourceId2}; ASSERT_THROW(storage.synchronize(SynchronizationPackage{{import}, {type}, {sourceId2}}), @@ -4628,11 +4657,9 @@ TEST_F(ProjectStorage, "Item", Storage::Synchronization::QualifiedImportedType{"Object", import}, Storage::Synchronization::ImportedType{}, - TypeTraits::Reference, + TypeTraitsKind::Reference, sourceId2, - {Storage::Synchronization::ExportedType{qtQuickModuleId, - "Item", - Storage::Version{}}}}; + {Storage::Synchronization::ExportedType{qtQuickModuleId, "Item", Storage::Version{}}}}; ASSERT_THROW(storage.synchronize(SynchronizationPackage{{import}, {type}, {sourceId2}}), QmlDesigner::TypeNameDoesNotExists); @@ -4642,15 +4669,14 @@ TEST_F(ProjectStorage, fetch_low_minor_version_for_imported_type_throws) { auto package{createSynchronizationPackageWithVersions()}; storage.synchronize(package); - Storage::Synchronization::Type type{ - "Item", - Storage::Synchronization::ImportedType{"Obj"}, - Storage::Synchronization::ImportedType{}, - TypeTraits::Reference, - sourceId2, - {Storage::Synchronization::ExportedType{qtQuickModuleId, - "Item", - Storage::Version{}}}}; + Storage::Synchronization::Type type{"Item", + Storage::Synchronization::ImportedType{"Obj"}, + Storage::Synchronization::ImportedType{}, + TypeTraitsKind::Reference, + sourceId2, + {Storage::Synchronization::ExportedType{qtQuickModuleId, + "Item", + Storage::Version{}}}}; Storage::Import import{qmlModuleId, Storage::Version{1, 1}, sourceId2}; ASSERT_THROW(storage.synchronize(SynchronizationPackage{{import}, {type}, {sourceId2}}), @@ -4666,11 +4692,9 @@ TEST_F(ProjectStorage, fetch_low_minor_version_for_qualified_imported_type_throw "Item", Storage::Synchronization::QualifiedImportedType{"Obj", import}, Storage::Synchronization::ImportedType{}, - TypeTraits::Reference, + TypeTraitsKind::Reference, sourceId2, - {Storage::Synchronization::ExportedType{qtQuickModuleId, - "Item", - Storage::Version{}}}}; + {Storage::Synchronization::ExportedType{qtQuickModuleId, "Item", Storage::Version{}}}}; ASSERT_THROW(storage.synchronize(SynchronizationPackage{{import}, {type}, {sourceId2}}), QmlDesigner::TypeNameDoesNotExists); @@ -4680,22 +4704,23 @@ TEST_F(ProjectStorage, fetch_higher_minor_version_for_imported_type) { auto package{createSynchronizationPackageWithVersions()}; storage.synchronize(package); - Storage::Synchronization::Type type{ - "Item", - Storage::Synchronization::ImportedType{"Obj"}, - Storage::Synchronization::ImportedType{}, - TypeTraits::Reference, - sourceId2, - {Storage::Synchronization::ExportedType{qtQuickModuleId, - "Item", - Storage::Version{}}}}; + Storage::Synchronization::Type type{"Item", + Storage::Synchronization::ImportedType{"Obj"}, + Storage::Synchronization::ImportedType{}, + TypeTraitsKind::Reference, + sourceId2, + {Storage::Synchronization::ExportedType{qtQuickModuleId, + "Item", + Storage::Version{}}}}; Storage::Import import{qmlModuleId, Storage::Version{1, 3}, sourceId2}; storage.synchronize(SynchronizationPackage{{import}, {type}, {sourceId2}}); ASSERT_THAT(storage.fetchTypes(), - Contains(IsStorageType( - sourceId2, "Item", fetchTypeId(sourceId1, "QObject"), TypeTraits::Reference))); + Contains(IsStorageType(sourceId2, + "Item", + fetchTypeId(sourceId1, "QObject"), + TypeTraitsKind::Reference))); } TEST_F(ProjectStorage, fetch_higher_minor_version_for_qualified_imported_type) @@ -4707,32 +4732,31 @@ TEST_F(ProjectStorage, fetch_higher_minor_version_for_qualified_imported_type) "Item", Storage::Synchronization::QualifiedImportedType{"Obj", import}, Storage::Synchronization::ImportedType{}, - TypeTraits::Reference, + TypeTraitsKind::Reference, sourceId2, - {Storage::Synchronization::ExportedType{qtQuickModuleId, - "Item", - Storage::Version{}}}}; + {Storage::Synchronization::ExportedType{qtQuickModuleId, "Item", Storage::Version{}}}}; storage.synchronize(SynchronizationPackage{{import}, {type}, {sourceId2}}); ASSERT_THAT(storage.fetchTypes(), - Contains(IsStorageType( - sourceId2, "Item", fetchTypeId(sourceId1, "QObject"), TypeTraits::Reference))); + Contains(IsStorageType(sourceId2, + "Item", + fetchTypeId(sourceId1, "QObject"), + TypeTraitsKind::Reference))); } TEST_F(ProjectStorage, fetch_different_major_version_for_imported_type_throws) { auto package{createSynchronizationPackageWithVersions()}; storage.synchronize(package); - Storage::Synchronization::Type type{ - "Item", - Storage::Synchronization::ImportedType{"Obj"}, - Storage::Synchronization::ImportedType{}, - TypeTraits::Reference, - sourceId2, - {Storage::Synchronization::ExportedType{qtQuickModuleId, - "Item", - Storage::Version{}}}}; + Storage::Synchronization::Type type{"Item", + Storage::Synchronization::ImportedType{"Obj"}, + Storage::Synchronization::ImportedType{}, + TypeTraitsKind::Reference, + sourceId2, + {Storage::Synchronization::ExportedType{qtQuickModuleId, + "Item", + Storage::Version{}}}}; Storage::Import import{qmlModuleId, Storage::Version{3, 1}, sourceId2}; ASSERT_THROW(storage.synchronize(SynchronizationPackage{{import}, {type}, {sourceId2}}), @@ -4748,11 +4772,9 @@ TEST_F(ProjectStorage, fetch_different_major_version_for_qualified_imported_type "Item", Storage::Synchronization::QualifiedImportedType{"Obj", import}, Storage::Synchronization::ImportedType{}, - TypeTraits::Reference, + TypeTraitsKind::Reference, sourceId2, - {Storage::Synchronization::ExportedType{qtQuickModuleId, - "Item", - Storage::Version{}}}}; + {Storage::Synchronization::ExportedType{qtQuickModuleId, "Item", Storage::Version{}}}}; ASSERT_THROW(storage.synchronize(SynchronizationPackage{{import}, {type}, {sourceId2}}), QmlDesigner::TypeNameDoesNotExists); @@ -4762,22 +4784,23 @@ TEST_F(ProjectStorage, fetch_other_type_by_different_version_for_imported_type) { auto package{createSynchronizationPackageWithVersions()}; storage.synchronize(package); - Storage::Synchronization::Type type{ - "Item", - Storage::Synchronization::ImportedType{"Obj"}, - Storage::Synchronization::ImportedType{}, - TypeTraits::Reference, - sourceId2, - {Storage::Synchronization::ExportedType{qtQuickModuleId, - "Item", - Storage::Version{}}}}; + Storage::Synchronization::Type type{"Item", + Storage::Synchronization::ImportedType{"Obj"}, + Storage::Synchronization::ImportedType{}, + TypeTraitsKind::Reference, + sourceId2, + {Storage::Synchronization::ExportedType{qtQuickModuleId, + "Item", + Storage::Version{}}}}; Storage::Import import{qmlModuleId, Storage::Version{2, 3}, sourceId2}; storage.synchronize(SynchronizationPackage{{import}, {type}, {sourceId2}}); ASSERT_THAT(storage.fetchTypes(), - Contains(IsStorageType( - sourceId2, "Item", fetchTypeId(sourceId1, "QObject2"), TypeTraits::Reference))); + Contains(IsStorageType(sourceId2, + "Item", + fetchTypeId(sourceId1, "QObject2"), + TypeTraitsKind::Reference))); } TEST_F(ProjectStorage, fetch_other_type_by_different_version_for_qualified_imported_type) @@ -4789,39 +4812,40 @@ TEST_F(ProjectStorage, fetch_other_type_by_different_version_for_qualified_impor "Item", Storage::Synchronization::QualifiedImportedType{"Obj", import}, Storage::Synchronization::ImportedType{}, - TypeTraits::Reference, + TypeTraitsKind::Reference, sourceId2, - {Storage::Synchronization::ExportedType{qtQuickModuleId, - "Item", - Storage::Version{}}}}; + {Storage::Synchronization::ExportedType{qtQuickModuleId, "Item", Storage::Version{}}}}; storage.synchronize(SynchronizationPackage{{import}, {type}, {sourceId2}}); ASSERT_THAT(storage.fetchTypes(), - Contains(IsStorageType( - sourceId2, "Item", fetchTypeId(sourceId1, "QObject2"), TypeTraits::Reference))); + Contains(IsStorageType(sourceId2, + "Item", + fetchTypeId(sourceId1, "QObject2"), + TypeTraitsKind::Reference))); } TEST_F(ProjectStorage, fetch_highest_version_for_import_without_version_for_imported_type) { auto package{createSynchronizationPackageWithVersions()}; storage.synchronize(package); - Storage::Synchronization::Type type{ - "Item", - Storage::Synchronization::ImportedType{"Obj"}, - Storage::Synchronization::ImportedType{}, - TypeTraits::Reference, - sourceId2, - {Storage::Synchronization::ExportedType{qtQuickModuleId, - "Item", - Storage::Version{}}}}; + Storage::Synchronization::Type type{"Item", + Storage::Synchronization::ImportedType{"Obj"}, + Storage::Synchronization::ImportedType{}, + TypeTraitsKind::Reference, + sourceId2, + {Storage::Synchronization::ExportedType{qtQuickModuleId, + "Item", + Storage::Version{}}}}; Storage::Import import{qmlModuleId, Storage::Version{}, sourceId2}; storage.synchronize(SynchronizationPackage{{import}, {type}, {sourceId2}}); ASSERT_THAT(storage.fetchTypes(), - Contains(IsStorageType( - sourceId2, "Item", fetchTypeId(sourceId1, "QObject4"), TypeTraits::Reference))); + Contains(IsStorageType(sourceId2, + "Item", + fetchTypeId(sourceId1, "QObject4"), + TypeTraitsKind::Reference))); } TEST_F(ProjectStorage, fetch_highest_version_for_import_without_version_for_qualified_imported_type) @@ -4833,39 +4857,40 @@ TEST_F(ProjectStorage, fetch_highest_version_for_import_without_version_for_qual "Item", Storage::Synchronization::QualifiedImportedType{"Obj", import}, Storage::Synchronization::ImportedType{}, - TypeTraits::Reference, + TypeTraitsKind::Reference, sourceId2, - {Storage::Synchronization::ExportedType{qtQuickModuleId, - "Item", - Storage::Version{}}}}; + {Storage::Synchronization::ExportedType{qtQuickModuleId, "Item", Storage::Version{}}}}; storage.synchronize(SynchronizationPackage{{import}, {type}, {sourceId2}}); ASSERT_THAT(storage.fetchTypes(), - Contains(IsStorageType( - sourceId2, "Item", fetchTypeId(sourceId1, "QObject4"), TypeTraits::Reference))); + Contains(IsStorageType(sourceId2, + "Item", + fetchTypeId(sourceId1, "QObject4"), + TypeTraitsKind::Reference))); } TEST_F(ProjectStorage, fetch_highest_version_for_import_with_major_version_for_imported_type) { auto package{createSynchronizationPackageWithVersions()}; storage.synchronize(package); - Storage::Synchronization::Type type{ - "Item", - Storage::Synchronization::ImportedType{"Obj"}, - Storage::Synchronization::ImportedType{}, - TypeTraits::Reference, - sourceId2, - {Storage::Synchronization::ExportedType{qtQuickModuleId, - "Item", - Storage::Version{}}}}; + Storage::Synchronization::Type type{"Item", + Storage::Synchronization::ImportedType{"Obj"}, + Storage::Synchronization::ImportedType{}, + TypeTraitsKind::Reference, + sourceId2, + {Storage::Synchronization::ExportedType{qtQuickModuleId, + "Item", + Storage::Version{}}}}; Storage::Import import{qmlModuleId, Storage::Version{2}, sourceId2}; storage.synchronize(SynchronizationPackage{{import}, {type}, {sourceId2}}); ASSERT_THAT(storage.fetchTypes(), - Contains(IsStorageType( - sourceId2, "Item", fetchTypeId(sourceId1, "QObject3"), TypeTraits::Reference))); + Contains(IsStorageType(sourceId2, + "Item", + fetchTypeId(sourceId1, "QObject3"), + TypeTraitsKind::Reference))); } TEST_F(ProjectStorage, fetch_highest_version_for_import_with_major_version_for_qualified_imported_type) @@ -4877,39 +4902,40 @@ TEST_F(ProjectStorage, fetch_highest_version_for_import_with_major_version_for_q "Item", Storage::Synchronization::QualifiedImportedType{"Obj", import}, Storage::Synchronization::ImportedType{}, - TypeTraits::Reference, + TypeTraitsKind::Reference, sourceId2, - {Storage::Synchronization::ExportedType{qtQuickModuleId, - "Item", - Storage::Version{}}}}; + {Storage::Synchronization::ExportedType{qtQuickModuleId, "Item", Storage::Version{}}}}; storage.synchronize(SynchronizationPackage{{import}, {type}, {sourceId2}}); ASSERT_THAT(storage.fetchTypes(), - Contains(IsStorageType( - sourceId2, "Item", fetchTypeId(sourceId1, "QObject3"), TypeTraits::Reference))); + Contains(IsStorageType(sourceId2, + "Item", + fetchTypeId(sourceId1, "QObject3"), + TypeTraitsKind::Reference))); } TEST_F(ProjectStorage, fetch_exported_type_without_version_first_for_imported_type) { auto package{createSynchronizationPackageWithVersions()}; storage.synchronize(package); - Storage::Synchronization::Type type{ - "Item", - Storage::Synchronization::ImportedType{"BuiltInObj"}, - Storage::Synchronization::ImportedType{}, - TypeTraits::Reference, - sourceId2, - {Storage::Synchronization::ExportedType{qtQuickModuleId, - "Item", - Storage::Version{}}}}; + Storage::Synchronization::Type type{"Item", + Storage::Synchronization::ImportedType{"BuiltInObj"}, + Storage::Synchronization::ImportedType{}, + TypeTraitsKind::Reference, + sourceId2, + {Storage::Synchronization::ExportedType{qtQuickModuleId, + "Item", + Storage::Version{}}}}; Storage::Import import{qmlModuleId, Storage::Version{}, sourceId2}; storage.synchronize(SynchronizationPackage{{import}, {type}, {sourceId2}}); ASSERT_THAT(storage.fetchTypes(), - Contains(IsStorageType( - sourceId2, "Item", fetchTypeId(sourceId1, "QObject"), TypeTraits::Reference))); + Contains(IsStorageType(sourceId2, + "Item", + fetchTypeId(sourceId1, "QObject"), + TypeTraitsKind::Reference))); } TEST_F(ProjectStorage, fetch_exported_type_without_version_first_for_qualified_imported_type) @@ -4921,17 +4947,17 @@ TEST_F(ProjectStorage, fetch_exported_type_without_version_first_for_qualified_i "Item", Storage::Synchronization::QualifiedImportedType{"BuiltInObj", import}, Storage::Synchronization::ImportedType{}, - TypeTraits::Reference, + TypeTraitsKind::Reference, sourceId2, - {Storage::Synchronization::ExportedType{qtQuickModuleId, - "Item", - Storage::Version{}}}}; + {Storage::Synchronization::ExportedType{qtQuickModuleId, "Item", Storage::Version{}}}}; storage.synchronize(SynchronizationPackage{{import}, {type}, {sourceId2}}); ASSERT_THAT(storage.fetchTypes(), - Contains(IsStorageType( - sourceId2, "Item", fetchTypeId(sourceId1, "QObject"), TypeTraits::Reference))); + Contains(IsStorageType(sourceId2, + "Item", + fetchTypeId(sourceId1, "QObject"), + TypeTraitsKind::Reference))); } TEST_F(ProjectStorage, ensure_that_properties_for_removed_types_are_not_anymore_relinked) @@ -4940,11 +4966,9 @@ TEST_F(ProjectStorage, ensure_that_properties_for_removed_types_are_not_anymore_ "QObject", Storage::Synchronization::ImportedType{""}, Storage::Synchronization::ImportedType{}, - TypeTraits::Reference, + TypeTraitsKind::Reference, sourceId1, - {Storage::Synchronization::ExportedType{qmlModuleId, - "Object", - Storage::Version{}}}, + {Storage::Synchronization::ExportedType{qmlModuleId, "Object", Storage::Version{}}}, {Storage::Synchronization::PropertyDeclaration{"data", Storage::Synchronization::ImportedType{ "Object"}, @@ -4980,11 +5004,9 @@ TEST_F(ProjectStorage, minimal_updates) "QQuickItem", {}, Storage::Synchronization::ImportedType{}, - TypeTraits::Reference, + TypeTraitsKind::Reference, sourceId1, - {Storage::Synchronization::ExportedType{qtQuickModuleId, - "Item", - Storage::Version{2, 0}}, + {Storage::Synchronization::ExportedType{qtQuickModuleId, "Item", Storage::Version{2, 0}}, Storage::Synchronization::ExportedType{qtQuickNativeModuleId, "QQuickItem"}}, {}, {}, @@ -4997,7 +5019,7 @@ TEST_F(ProjectStorage, minimal_updates) ASSERT_THAT( storage.fetchTypes(), UnorderedElementsAre( - AllOf(IsStorageType(sourceId2, "QObject", TypeId{}, TypeTraits::Reference), + AllOf(IsStorageType(sourceId2, "QObject", TypeId{}, TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::exportedTypes, UnorderedElementsAre(IsExportedType(qmlModuleId, "Object"), IsExportedType(qmlModuleId, "Obj"), @@ -5005,7 +5027,7 @@ TEST_F(ProjectStorage, minimal_updates) AllOf(IsStorageType(sourceId1, "QQuickItem", fetchTypeId(sourceId2, "QObject"), - TypeTraits::Reference), + TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::exportedTypes, UnorderedElementsAre(IsExportedType(qtQuickModuleId, "Item", 2, 0), IsExportedType(qtQuickNativeModuleId, "QQuickItem"))), @@ -5286,7 +5308,7 @@ TEST_F(ProjectStorage, exclude_exported_types) ASSERT_THAT(storage.fetchTypes(), UnorderedElementsAre( - AllOf(IsStorageType(sourceId2, "QObject", TypeId{}, TypeTraits::Reference), + AllOf(IsStorageType(sourceId2, "QObject", TypeId{}, TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::exportedTypes, UnorderedElementsAre(IsExportedType(qmlModuleId, "Object"), IsExportedType(qmlModuleId, "Obj"), @@ -5294,7 +5316,7 @@ TEST_F(ProjectStorage, exclude_exported_types) AllOf(IsStorageType(sourceId1, "QQuickItem", fetchTypeId(sourceId2, "QObject"), - TypeTraits::Reference), + TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::exportedTypes, UnorderedElementsAre(IsExportedType(qtQuickModuleId, "Item"), IsExportedType(qtQuickNativeModuleId, @@ -5312,22 +5334,22 @@ TEST_F(ProjectStorage, module_exported_import) AllOf(IsStorageType(sourceId1, "QQuickItem", fetchTypeId(sourceId2, "QObject"), - TypeTraits::Reference), + TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::exportedTypes, UnorderedElementsAre(IsExportedType(qtQuickModuleId, "Item")))), - AllOf(IsStorageType(sourceId2, "QObject", TypeId{}, TypeTraits::Reference), + AllOf(IsStorageType(sourceId2, "QObject", TypeId{}, TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::exportedTypes, UnorderedElementsAre(IsExportedType(qmlModuleId, "Object")))), AllOf(IsStorageType(sourceId3, "QQuickItem3d", fetchTypeId(sourceId1, "QQuickItem"), - TypeTraits::Reference), + TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::exportedTypes, UnorderedElementsAre(IsExportedType(qtQuick3DModuleId, "Item3D")))), AllOf(IsStorageType(sourceId4, "MyItem", fetchTypeId(sourceId2, "QObject"), - TypeTraits::Reference), + TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::exportedTypes, UnorderedElementsAre(IsExportedType(myModuleModuleId, "MyItem")))))); } @@ -5360,22 +5382,22 @@ TEST_F(ProjectStorage, module_exported_import_with_different_versions) AllOf(IsStorageType(sourceId1, "QQuickItem", fetchTypeId(sourceId2, "QObject"), - TypeTraits::Reference), + TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::exportedTypes, UnorderedElementsAre(IsExportedType(qtQuickModuleId, "Item")))), - AllOf(IsStorageType(sourceId2, "QObject", TypeId{}, TypeTraits::Reference), + AllOf(IsStorageType(sourceId2, "QObject", TypeId{}, TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::exportedTypes, UnorderedElementsAre(IsExportedType(qmlModuleId, "Object")))), AllOf(IsStorageType(sourceId3, "QQuickItem3d", fetchTypeId(sourceId1, "QQuickItem"), - TypeTraits::Reference), + TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::exportedTypes, UnorderedElementsAre(IsExportedType(qtQuick3DModuleId, "Item3D")))), AllOf(IsStorageType(sourceId4, "MyItem", fetchTypeId(sourceId2, "QObject"), - TypeTraits::Reference), + TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::exportedTypes, UnorderedElementsAre(IsExportedType(myModuleModuleId, "MyItem")))))); } @@ -5397,27 +5419,28 @@ TEST_F(ProjectStorage, module_exported_import_with_indirect_different_versions) AllOf(IsStorageType(sourceId1, "QQuickItem", fetchTypeId(sourceId2, "QObject"), - TypeTraits::Reference), + TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::exportedTypes, UnorderedElementsAre(IsExportedType(qtQuickModuleId, "Item")))), - AllOf(IsStorageType(sourceId2, "QObject", TypeId{}, TypeTraits::Reference), + AllOf(IsStorageType(sourceId2, "QObject", TypeId{}, TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::exportedTypes, UnorderedElementsAre(IsExportedType(qmlModuleId, "Object")))), AllOf(IsStorageType(sourceId3, "QQuickItem3d", fetchTypeId(sourceId1, "QQuickItem"), - TypeTraits::Reference), + TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::exportedTypes, UnorderedElementsAre(IsExportedType(qtQuick3DModuleId, "Item3D")))), AllOf(IsStorageType(sourceId4, "MyItem", fetchTypeId(sourceId2, "QObject"), - TypeTraits::Reference), + TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::exportedTypes, UnorderedElementsAre(IsExportedType(myModuleModuleId, "MyItem")))))); } -TEST_F(ProjectStorage, module_exported_import_prevent_collision_if_module_is_indirectly_reexported_multiple_times) +TEST_F(ProjectStorage, + module_exported_import_prevent_collision_if_module_is_indirectly_reexported_multiple_times) { ModuleId qtQuick4DModuleId{storage.moduleId("QtQuick4D")}; auto package{createModuleExportedImportSynchronizationPackage()}; @@ -5435,11 +5458,9 @@ TEST_F(ProjectStorage, module_exported_import_prevent_collision_if_module_is_ind "QQuickItem4d", Storage::Synchronization::ImportedType{"Item"}, Storage::Synchronization::ImportedType{}, - TypeTraits::Reference, + TypeTraitsKind::Reference, sourceId5, - {Storage::Synchronization::ExportedType{qtQuick4DModuleId, - "Item4D", - Storage::Version{1, 0}}}}); + {Storage::Synchronization::ExportedType{qtQuick4DModuleId, "Item4D", Storage::Version{1, 0}}}}); package.imports.emplace_back(qtQuick4DModuleId, Storage::Version{1}, sourceId4); storage.synchronize(std::move(package)); @@ -5449,28 +5470,28 @@ TEST_F(ProjectStorage, module_exported_import_prevent_collision_if_module_is_ind AllOf(IsStorageType(sourceId1, "QQuickItem", fetchTypeId(sourceId2, "QObject"), - TypeTraits::Reference), + TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::exportedTypes, UnorderedElementsAre(IsExportedType(qtQuickModuleId, "Item")))), - AllOf(IsStorageType(sourceId2, "QObject", TypeId{}, TypeTraits::Reference), + AllOf(IsStorageType(sourceId2, "QObject", TypeId{}, TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::exportedTypes, UnorderedElementsAre(IsExportedType(qmlModuleId, "Object")))), AllOf(IsStorageType(sourceId3, "QQuickItem3d", fetchTypeId(sourceId1, "QQuickItem"), - TypeTraits::Reference), + TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::exportedTypes, UnorderedElementsAre(IsExportedType(qtQuick3DModuleId, "Item3D")))), AllOf(IsStorageType(sourceId5, "QQuickItem4d", fetchTypeId(sourceId1, "QQuickItem"), - TypeTraits::Reference), + TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::exportedTypes, UnorderedElementsAre(IsExportedType(qtQuick4DModuleId, "Item4D")))), AllOf(IsStorageType(sourceId4, "MyItem", fetchTypeId(sourceId2, "QObject"), - TypeTraits::Reference), + TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::exportedTypes, UnorderedElementsAre(IsExportedType(myModuleModuleId, "MyItem")))))); } @@ -5481,20 +5502,16 @@ TEST_F(ProjectStorage, distinguish_between_import_kinds) ModuleId qml11ModuleId{storage.moduleId("Qml11")}; auto package{createSimpleSynchronizationPackage()}; package.moduleDependencies.emplace_back(qmlModuleId, Storage::Version{}, sourceId1); - package.moduleDependencies.emplace_back(qml1ModuleId, - Storage::Version{1}, - sourceId1); + package.moduleDependencies.emplace_back(qml1ModuleId, Storage::Version{1}, sourceId1); package.imports.emplace_back(qml1ModuleId, Storage::Version{}, sourceId1); - package.moduleDependencies.emplace_back(qml11ModuleId, - Storage::Version{1, 1}, - sourceId1); + package.moduleDependencies.emplace_back(qml11ModuleId, Storage::Version{1, 1}, sourceId1); package.imports.emplace_back(qml11ModuleId, Storage::Version{1, 1}, sourceId1); storage.synchronize(std::move(package)); ASSERT_THAT(storage.fetchTypes(), UnorderedElementsAre( - AllOf(IsStorageType(sourceId2, "QObject", TypeId{}, TypeTraits::Reference), + AllOf(IsStorageType(sourceId2, "QObject", TypeId{}, TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::exportedTypes, UnorderedElementsAre(IsExportedType(qmlModuleId, "Object"), IsExportedType(qmlModuleId, "Obj"), @@ -5502,7 +5519,7 @@ TEST_F(ProjectStorage, distinguish_between_import_kinds) AllOf(IsStorageType(sourceId1, "QQuickItem", fetchTypeId(sourceId2, "QObject"), - TypeTraits::Reference), + TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::exportedTypes, UnorderedElementsAre(IsExportedType(qtQuickModuleId, "Item"), IsExportedType(qtQuickNativeModuleId, @@ -5512,9 +5529,7 @@ TEST_F(ProjectStorage, distinguish_between_import_kinds) TEST_F(ProjectStorage, module_exported_import_distinguish_between_dependency_and_import_re_exports) { auto package{createModuleExportedImportSynchronizationPackage()}; - package.moduleDependencies.emplace_back(qtQuick3DModuleId, - Storage::Version{1}, - sourceId4); + package.moduleDependencies.emplace_back(qtQuick3DModuleId, Storage::Version{1}, sourceId4); storage.synchronize(std::move(package)); @@ -5523,22 +5538,22 @@ TEST_F(ProjectStorage, module_exported_import_distinguish_between_dependency_and AllOf(IsStorageType(sourceId1, "QQuickItem", fetchTypeId(sourceId2, "QObject"), - TypeTraits::Reference), + TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::exportedTypes, UnorderedElementsAre(IsExportedType(qtQuickModuleId, "Item")))), - AllOf(IsStorageType(sourceId2, "QObject", TypeId{}, TypeTraits::Reference), + AllOf(IsStorageType(sourceId2, "QObject", TypeId{}, TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::exportedTypes, UnorderedElementsAre(IsExportedType(qmlModuleId, "Object")))), AllOf(IsStorageType(sourceId3, "QQuickItem3d", fetchTypeId(sourceId1, "QQuickItem"), - TypeTraits::Reference), + TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::exportedTypes, UnorderedElementsAre(IsExportedType(qtQuick3DModuleId, "Item3D")))), AllOf(IsStorageType(sourceId4, "MyItem", fetchTypeId(sourceId2, "QObject"), - TypeTraits::Reference), + TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::exportedTypes, UnorderedElementsAre(IsExportedType(myModuleModuleId, "MyItem")))))); } @@ -5556,22 +5571,22 @@ TEST_F(ProjectStorage, module_exported_import_with_qualified_imported_type) AllOf(IsStorageType(sourceId1, "QQuickItem", fetchTypeId(sourceId2, "QObject"), - TypeTraits::Reference), + TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::exportedTypes, UnorderedElementsAre(IsExportedType(qtQuickModuleId, "Item")))), - AllOf(IsStorageType(sourceId2, "QObject", TypeId{}, TypeTraits::Reference), + AllOf(IsStorageType(sourceId2, "QObject", TypeId{}, TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::exportedTypes, UnorderedElementsAre(IsExportedType(qmlModuleId, "Object")))), AllOf(IsStorageType(sourceId3, "QQuickItem3d", fetchTypeId(sourceId1, "QQuickItem"), - TypeTraits::Reference), + TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::exportedTypes, UnorderedElementsAre(IsExportedType(qtQuick3DModuleId, "Item3D")))), AllOf(IsStorageType(sourceId4, "MyItem", fetchTypeId(sourceId2, "QObject"), - TypeTraits::Reference), + TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::exportedTypes, UnorderedElementsAre(IsExportedType(myModuleModuleId, "MyItem")))))); } @@ -5582,19 +5597,22 @@ TEST_F(ProjectStorage, synchronize_types_add_indirect_alias_declarations) storage.synchronize(package); - ASSERT_THAT( - storage.fetchTypes(), - Contains(AllOf( - IsStorageType(sourceId3, "QAliasItem", fetchTypeId(sourceId1, "QQuickItem"), TypeTraits::Reference), - Field(&Storage::Synchronization::Type::propertyDeclarations, - UnorderedElementsAre( - IsPropertyDeclaration("items", - fetchTypeId(sourceId1, "QQuickItem"), - Storage::PropertyDeclarationTraits::IsList), - IsPropertyDeclaration("objects", - fetchTypeId(sourceId2, "QObject"), - Storage::PropertyDeclarationTraits::IsList - | Storage::PropertyDeclarationTraits::IsReadOnly)))))); + ASSERT_THAT(storage.fetchTypes(), + Contains( + AllOf(IsStorageType(sourceId3, + "QAliasItem", + fetchTypeId(sourceId1, "QQuickItem"), + TypeTraitsKind::Reference), + Field(&Storage::Synchronization::Type::propertyDeclarations, + UnorderedElementsAre( + IsPropertyDeclaration("items", + fetchTypeId(sourceId1, "QQuickItem"), + Storage::PropertyDeclarationTraits::IsList), + IsPropertyDeclaration( + "objects", + fetchTypeId(sourceId2, "QObject"), + Storage::PropertyDeclarationTraits::IsList + | Storage::PropertyDeclarationTraits::IsReadOnly)))))); } TEST_F(ProjectStorage, synchronize_types_add_indirect_alias_declarations_again) @@ -5604,19 +5622,22 @@ TEST_F(ProjectStorage, synchronize_types_add_indirect_alias_declarations_again) storage.synchronize(package); - ASSERT_THAT( - storage.fetchTypes(), - Contains(AllOf( - IsStorageType(sourceId3, "QAliasItem", fetchTypeId(sourceId1, "QQuickItem"), TypeTraits::Reference), - Field(&Storage::Synchronization::Type::propertyDeclarations, - UnorderedElementsAre( - IsPropertyDeclaration("items", - fetchTypeId(sourceId1, "QQuickItem"), - Storage::PropertyDeclarationTraits::IsList), - IsPropertyDeclaration("objects", - fetchTypeId(sourceId2, "QObject"), - Storage::PropertyDeclarationTraits::IsList - | Storage::PropertyDeclarationTraits::IsReadOnly)))))); + ASSERT_THAT(storage.fetchTypes(), + Contains( + AllOf(IsStorageType(sourceId3, + "QAliasItem", + fetchTypeId(sourceId1, "QQuickItem"), + TypeTraitsKind::Reference), + Field(&Storage::Synchronization::Type::propertyDeclarations, + UnorderedElementsAre( + IsPropertyDeclaration("items", + fetchTypeId(sourceId1, "QQuickItem"), + Storage::PropertyDeclarationTraits::IsList), + IsPropertyDeclaration( + "objects", + fetchTypeId(sourceId2, "QObject"), + Storage::PropertyDeclarationTraits::IsList + | Storage::PropertyDeclarationTraits::IsReadOnly)))))); } TEST_F(ProjectStorage, synchronize_types_remove_indirect_alias_declaration) @@ -5631,7 +5652,7 @@ TEST_F(ProjectStorage, synchronize_types_remove_indirect_alias_declaration) Contains(AllOf(IsStorageType(sourceId3, "QAliasItem", fetchTypeId(sourceId1, "QQuickItem"), - TypeTraits::Reference), + TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::propertyDeclarations, UnorderedElementsAre(IsPropertyDeclaration( "items", @@ -5668,7 +5689,8 @@ TEST_F(ProjectStorage, synchronize_types_add_indirect_alias_declarations_throws_ QmlDesigner::PropertyNameDoesNotExists); } -TEST_F(ProjectStorage, synchronize_types_add_indirect_alias_declarations_throws_for_wrong_property_name_tail) +TEST_F(ProjectStorage, + synchronize_types_add_indirect_alias_declarations_throws_for_wrong_property_name_tail) { auto package{createSynchronizationPackageWithIndirectAliases()}; storage.synchronize(package); @@ -5693,19 +5715,22 @@ TEST_F(ProjectStorage, synchronize_types_change_indirect_alias_declaration_type_ storage.synchronize(SynchronizationPackage{ importsSourceId3, {package.types[2]}, {sourceId3}, moduleDependenciesSourceId3, {sourceId3}}); - ASSERT_THAT( - storage.fetchTypes(), - Contains(AllOf( - IsStorageType(sourceId3, "QAliasItem", fetchTypeId(sourceId1, "QQuickItem"), TypeTraits::Reference), - Field(&Storage::Synchronization::Type::propertyDeclarations, - UnorderedElementsAre( - IsPropertyDeclaration("items", - fetchTypeId(sourceId1, "QQuickItem"), - Storage::PropertyDeclarationTraits::IsList), - IsPropertyDeclaration("objects", - fetchTypeId(sourceId4, "QObject2"), - Storage::PropertyDeclarationTraits::IsList - | Storage::PropertyDeclarationTraits::IsReadOnly)))))); + ASSERT_THAT(storage.fetchTypes(), + Contains( + AllOf(IsStorageType(sourceId3, + "QAliasItem", + fetchTypeId(sourceId1, "QQuickItem"), + TypeTraitsKind::Reference), + Field(&Storage::Synchronization::Type::propertyDeclarations, + UnorderedElementsAre( + IsPropertyDeclaration("items", + fetchTypeId(sourceId1, "QQuickItem"), + Storage::PropertyDeclarationTraits::IsList), + IsPropertyDeclaration( + "objects", + fetchTypeId(sourceId4, "QObject2"), + Storage::PropertyDeclarationTraits::IsList + | Storage::PropertyDeclarationTraits::IsReadOnly)))))); } TEST_F(ProjectStorage, synchronize_types_change_indirect_alias_declaration_tails_type_name) @@ -5719,19 +5744,22 @@ TEST_F(ProjectStorage, synchronize_types_change_indirect_alias_declaration_tails storage.synchronize(SynchronizationPackage{ importsSourceId5, {package.types[4]}, {sourceId5}, moduleDependenciesSourceId5, {sourceId5}}); - ASSERT_THAT( - storage.fetchTypes(), - Contains(AllOf( - IsStorageType(sourceId3, "QAliasItem", fetchTypeId(sourceId1, "QQuickItem"), TypeTraits::Reference), - Field(&Storage::Synchronization::Type::propertyDeclarations, - UnorderedElementsAre( - IsPropertyDeclaration("items", - fetchTypeId(sourceId1, "QQuickItem"), - Storage::PropertyDeclarationTraits::IsList), - IsPropertyDeclaration("objects", - fetchTypeId(sourceId4, "QObject2"), - Storage::PropertyDeclarationTraits::IsList - | Storage::PropertyDeclarationTraits::IsReadOnly)))))); + ASSERT_THAT(storage.fetchTypes(), + Contains( + AllOf(IsStorageType(sourceId3, + "QAliasItem", + fetchTypeId(sourceId1, "QQuickItem"), + TypeTraitsKind::Reference), + Field(&Storage::Synchronization::Type::propertyDeclarations, + UnorderedElementsAre( + IsPropertyDeclaration("items", + fetchTypeId(sourceId1, "QQuickItem"), + Storage::PropertyDeclarationTraits::IsList), + IsPropertyDeclaration( + "objects", + fetchTypeId(sourceId4, "QObject2"), + Storage::PropertyDeclarationTraits::IsList + | Storage::PropertyDeclarationTraits::IsReadOnly)))))); } TEST_F(ProjectStorage, synchronize_types_change_indirect_alias_declarations_property_name) @@ -5743,19 +5771,22 @@ TEST_F(ProjectStorage, synchronize_types_change_indirect_alias_declarations_prop storage.synchronize(SynchronizationPackage{ importsSourceId3, {package.types[2]}, {sourceId3}, moduleDependenciesSourceId3, {sourceId3}}); - ASSERT_THAT( - storage.fetchTypes(), - Contains(AllOf( - IsStorageType(sourceId3, "QAliasItem", fetchTypeId(sourceId1, "QQuickItem"), TypeTraits::Reference), - Field(&Storage::Synchronization::Type::propertyDeclarations, - UnorderedElementsAre( - IsPropertyDeclaration("items", - fetchTypeId(sourceId1, "QQuickItem"), - Storage::PropertyDeclarationTraits::IsList), - IsPropertyDeclaration("objects", - fetchTypeId(sourceId4, "QObject2"), - Storage::PropertyDeclarationTraits::IsList - | Storage::PropertyDeclarationTraits::IsReadOnly)))))); + ASSERT_THAT(storage.fetchTypes(), + Contains( + AllOf(IsStorageType(sourceId3, + "QAliasItem", + fetchTypeId(sourceId1, "QQuickItem"), + TypeTraitsKind::Reference), + Field(&Storage::Synchronization::Type::propertyDeclarations, + UnorderedElementsAre( + IsPropertyDeclaration("items", + fetchTypeId(sourceId1, "QQuickItem"), + Storage::PropertyDeclarationTraits::IsList), + IsPropertyDeclaration( + "objects", + fetchTypeId(sourceId4, "QObject2"), + Storage::PropertyDeclarationTraits::IsList + | Storage::PropertyDeclarationTraits::IsReadOnly)))))); } TEST_F(ProjectStorage, synchronize_types_change_indirect_alias_declarations_property_name_tail) @@ -5772,7 +5803,7 @@ TEST_F(ProjectStorage, synchronize_types_change_indirect_alias_declarations_prop IsStorageType(sourceId3, "QAliasItem", fetchTypeId(sourceId1, "QQuickItem"), - TypeTraits::Reference), + TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::propertyDeclarations, UnorderedElementsAre( IsPropertyDeclaration("items", @@ -5796,19 +5827,22 @@ TEST_F(ProjectStorage, synchronize_types_change_indirect_alias_declarations_to_p storage.synchronize(SynchronizationPackage{ importsSourceId3, {package.types[2]}, {sourceId3}, moduleDependenciesSourceId3, {sourceId3}}); - ASSERT_THAT( - storage.fetchTypes(), - Contains(AllOf( - IsStorageType(sourceId3, "QAliasItem", fetchTypeId(sourceId1, "QQuickItem"), TypeTraits::Reference), - Field(&Storage::Synchronization::Type::propertyDeclarations, - UnorderedElementsAre( - IsPropertyDeclaration("items", - fetchTypeId(sourceId1, "QQuickItem"), - Storage::PropertyDeclarationTraits::IsList), - IsPropertyDeclaration("objects", - fetchTypeId(sourceId1, "QQuickItem"), - Storage::PropertyDeclarationTraits::IsList - | Storage::PropertyDeclarationTraits::IsReadOnly)))))); + ASSERT_THAT(storage.fetchTypes(), + Contains( + AllOf(IsStorageType(sourceId3, + "QAliasItem", + fetchTypeId(sourceId1, "QQuickItem"), + TypeTraitsKind::Reference), + Field(&Storage::Synchronization::Type::propertyDeclarations, + UnorderedElementsAre( + IsPropertyDeclaration("items", + fetchTypeId(sourceId1, "QQuickItem"), + Storage::PropertyDeclarationTraits::IsList), + IsPropertyDeclaration( + "objects", + fetchTypeId(sourceId1, "QQuickItem"), + Storage::PropertyDeclarationTraits::IsList + | Storage::PropertyDeclarationTraits::IsReadOnly)))))); } TEST_F(ProjectStorage, synchronize_types_change_property_declarations_to_indirect_alias_declaration) @@ -5830,19 +5864,22 @@ TEST_F(ProjectStorage, synchronize_types_change_property_declarations_to_indirec storage.synchronize(SynchronizationPackage{ importsSourceId3, {package.types[2]}, {sourceId3}, moduleDependenciesSourceId3, {sourceId3}}); - ASSERT_THAT( - storage.fetchTypes(), - Contains(AllOf( - IsStorageType(sourceId3, "QAliasItem", fetchTypeId(sourceId1, "QQuickItem"), TypeTraits::Reference), - Field(&Storage::Synchronization::Type::propertyDeclarations, - UnorderedElementsAre( - IsPropertyDeclaration("items", - fetchTypeId(sourceId1, "QQuickItem"), - Storage::PropertyDeclarationTraits::IsList), - IsPropertyDeclaration("objects", - fetchTypeId(sourceId2, "QObject"), - Storage::PropertyDeclarationTraits::IsList - | Storage::PropertyDeclarationTraits::IsReadOnly)))))); + ASSERT_THAT(storage.fetchTypes(), + Contains( + AllOf(IsStorageType(sourceId3, + "QAliasItem", + fetchTypeId(sourceId1, "QQuickItem"), + TypeTraitsKind::Reference), + Field(&Storage::Synchronization::Type::propertyDeclarations, + UnorderedElementsAre( + IsPropertyDeclaration("items", + fetchTypeId(sourceId1, "QQuickItem"), + Storage::PropertyDeclarationTraits::IsList), + IsPropertyDeclaration( + "objects", + fetchTypeId(sourceId2, "QObject"), + Storage::PropertyDeclarationTraits::IsList + | Storage::PropertyDeclarationTraits::IsReadOnly)))))); } TEST_F(ProjectStorage, synchronize_types_change_indirect_alias_target_property_declaration_traits) @@ -5858,7 +5895,10 @@ TEST_F(ProjectStorage, synchronize_types_change_indirect_alias_target_property_d ASSERT_THAT( storage.fetchTypes(), Contains(AllOf( - IsStorageType(sourceId3, "QAliasItem", fetchTypeId(sourceId1, "QQuickItem"), TypeTraits::Reference), + IsStorageType(sourceId3, + "QAliasItem", + fetchTypeId(sourceId1, "QQuickItem"), + TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::propertyDeclarations, UnorderedElementsAre( IsPropertyDeclaration("items", @@ -5881,19 +5921,22 @@ TEST_F(ProjectStorage, synchronize_types_change_indirect_alias_target_property_d storage.synchronize(SynchronizationPackage{ importsSourceId5, {package.types[4]}, {sourceId5}, moduleDependenciesSourceId5, {sourceId5}}); - ASSERT_THAT( - storage.fetchTypes(), - Contains(AllOf( - IsStorageType(sourceId3, "QAliasItem", fetchTypeId(sourceId1, "QQuickItem"), TypeTraits::Reference), - Field(&Storage::Synchronization::Type::propertyDeclarations, - UnorderedElementsAre( - IsPropertyDeclaration("items", - fetchTypeId(sourceId1, "QQuickItem"), - Storage::PropertyDeclarationTraits::IsList), - IsPropertyDeclaration("objects", - fetchTypeId(sourceId1, "QQuickItem"), - Storage::PropertyDeclarationTraits::IsList - | Storage::PropertyDeclarationTraits::IsReadOnly)))))); + ASSERT_THAT(storage.fetchTypes(), + Contains( + AllOf(IsStorageType(sourceId3, + "QAliasItem", + fetchTypeId(sourceId1, "QQuickItem"), + TypeTraitsKind::Reference), + Field(&Storage::Synchronization::Type::propertyDeclarations, + UnorderedElementsAre( + IsPropertyDeclaration("items", + fetchTypeId(sourceId1, "QQuickItem"), + Storage::PropertyDeclarationTraits::IsList), + IsPropertyDeclaration( + "objects", + fetchTypeId(sourceId1, "QQuickItem"), + Storage::PropertyDeclarationTraits::IsList + | Storage::PropertyDeclarationTraits::IsReadOnly)))))); } TEST_F(ProjectStorage, synchronize_types_remove_property_declaration_with_an_indirect_alias_throws) @@ -5910,7 +5953,8 @@ TEST_F(ProjectStorage, synchronize_types_remove_property_declaration_with_an_ind Sqlite::ConstraintPreventsModification); } -TEST_F(ProjectStorage, DISABLED_synchronize_types_remove_stem_property_declaration_with_an_indirect_alias_throws) +TEST_F(ProjectStorage, + DISABLED_synchronize_types_remove_stem_property_declaration_with_an_indirect_alias_throws) { auto package{createSynchronizationPackageWithIndirectAliases()}; storage.synchronize(package); @@ -5941,7 +5985,7 @@ TEST_F(ProjectStorage, synchronize_types_remove_property_declaration_and_indirec Contains(AllOf(IsStorageType(sourceId3, "QAliasItem", fetchTypeId(sourceId1, "QQuickItem"), - TypeTraits::Reference), + TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::propertyDeclarations, UnorderedElementsAre(IsPropertyDeclaration( "items", @@ -5967,7 +6011,7 @@ TEST_F(ProjectStorage, synchronize_types_remove_property_declaration_and_indirec AllOf(IsStorageType(sourceId3, "QAliasItem", fetchTypeId(sourceId1, "QQuickItem"), - TypeTraits::Reference), + TypeTraitsKind::Reference), Field(&Storage::Synchronization::Type::propertyDeclarations, IsEmpty())))); } @@ -6644,7 +6688,7 @@ TEST_F(ProjectStorage, get_type) auto type = storage.type(typeId); - ASSERT_THAT(type, Optional(IsInfoType(defaultPropertyId, sourceId1, TypeTraits::Reference))); + ASSERT_THAT(type, Optional(IsInfoType(defaultPropertyId, sourceId1, TypeTraitsKind::Reference))); } TEST_F(ProjectStorage, dont_get_type_for_invalid_id) @@ -7016,16 +7060,14 @@ TEST_F(ProjectStorage, storage.synchronize(package); auto importId = storage.importId(Storage::Import{qmlModuleId, Storage::Version{}, sourceId1}); auto expectedImportedTypeNameId = storage.importedTypeNameId(importId, "Item"); - auto importId2 = storage.importId( - Storage::Import{qtQuickModuleId, Storage::Version{}, sourceId1}); + auto importId2 = storage.importId(Storage::Import{qtQuickModuleId, Storage::Version{}, sourceId1}); auto importedTypeNameId = storage.importedTypeNameId(importId2, "Item"); ASSERT_THAT(importedTypeNameId, Not(expectedImportedTypeNameId)); } -TEST_F(ProjectStorage, - get_imported_type_name_id_for_import_id_returns_different_id_for_different_name) +TEST_F(ProjectStorage, get_imported_type_name_id_for_import_id_returns_different_id_for_different_name) { auto package{createSimpleSynchronizationPackage()}; storage.synchronize(package); @@ -7205,16 +7247,440 @@ TEST_F(ProjectStorage, synchronize_property_editor_with_non_existing_type_name) ASSERT_THAT(storage.propertyEditorPathId(fetchTypeId(sourceId4, "Item4D")), IsFalse()); } -TEST_F(ProjectStorage, call_refresh_callback_after_synchronization) +TEST_F(ProjectStorage, call_remove_type_ids_in_observer_after_synchronization) { auto package{createSimpleSynchronizationPackage()}; - MockFunction callbackMock; - auto callback = callbackMock.AsStdFunction(); - storage.addRefreshCallback(&callback); + ProjectStorageObserverMock observerMock; + storage.addObserver(&observerMock); + storage.synchronize(package); + package.types.clear(); - EXPECT_CALL(callbackMock, Call(_)); + EXPECT_CALL(observerMock, removedTypeIds(_)); storage.synchronize(package); } +TEST_F(ProjectStorage, synchronize_type_annotation_type_traits) +{ + auto package{createSimpleSynchronizationPackage()}; + package.typeAnnotations = createTypeAnnotions(); + package.updatedTypeAnnotationSourceIds = createUpdatedTypeAnnotionSourceIds( + package.typeAnnotations); + TypeTraits traits{TypeTraitsKind::Reference}; + traits.canBeContainer = FlagIs::True; + traits.visibleInLibrary = FlagIs::True; + + storage.synchronize(package); + + ASSERT_THAT(storage.type(fetchTypeId(sourceId2, "QObject"))->traits, traits); +} + +TEST_F(ProjectStorage, synchronize_updates_type_annotation_type_traits) +{ + auto package{createSimpleSynchronizationPackage()}; + storage.synchronize(package); + SynchronizationPackage annotationPackage; + annotationPackage.typeAnnotations = createTypeAnnotions(); + package.updatedTypeAnnotationSourceIds = createUpdatedTypeAnnotionSourceIds( + package.typeAnnotations); + TypeTraits traits{TypeTraitsKind::Reference}; + traits.canBeContainer = FlagIs::True; + traits.visibleInLibrary = FlagIs::True; + + storage.synchronize(annotationPackage); + + ASSERT_THAT(storage.type(fetchTypeId(sourceId2, "QObject"))->traits, traits); +} + +TEST_F(ProjectStorage, synchronize_clears_annotation_type_traits_if_annotation_was_removed) +{ + auto package{createSimpleSynchronizationPackage()}; + package.typeAnnotations = createTypeAnnotions(); + package.updatedTypeAnnotationSourceIds = createUpdatedTypeAnnotionSourceIds( + package.typeAnnotations); + storage.synchronize(package); + package.typeAnnotations[0].traits.isStackedContainer = FlagIs::True; + TypeTraits traits{TypeTraitsKind::Reference}; + traits.canBeContainer = FlagIs::True; + traits.visibleInLibrary = FlagIs::True; + traits.isStackedContainer = FlagIs::True; + + storage.synchronize(package); + + ASSERT_THAT(storage.type(fetchTypeId(sourceId2, "QObject"))->traits, traits); +} + +TEST_F(ProjectStorage, synchronize_updatesannotation_type_traits) +{ + auto package{createSimpleSynchronizationPackage()}; + package.typeAnnotations = createTypeAnnotions(); + package.updatedTypeAnnotationSourceIds = createUpdatedTypeAnnotionSourceIds( + package.typeAnnotations); + storage.synchronize(package); + TypeTraits traits{TypeTraitsKind::Value}; + package.types[1].traits.kind = TypeTraitsKind::Value; + package.typeAnnotations.clear(); + + storage.synchronize(package); + + ASSERT_THAT(storage.type(fetchTypeId(sourceId2, "QObject"))->traits, traits); +} + +TEST_F(ProjectStorage, synchronize_type_annotation_type_icon_path) +{ + auto package{createSimpleSynchronizationPackage()}; + package.typeAnnotations = createTypeAnnotions(); + package.updatedTypeAnnotationSourceIds = createUpdatedTypeAnnotionSourceIds( + package.typeAnnotations); + + storage.synchronize(package); + + ASSERT_THAT(storage.typeIconPath(fetchTypeId(sourceId2, "QObject")), Eq("/path/to/icon.png")); +} + +TEST_F(ProjectStorage, synchronize_removes_type_annotation_type_icon_path) +{ + auto package{createSimpleSynchronizationPackage()}; + package.typeAnnotations = createTypeAnnotions(); + package.updatedTypeAnnotationSourceIds = createUpdatedTypeAnnotionSourceIds( + package.typeAnnotations); + storage.synchronize(package); + package.typeAnnotations.clear(); + + storage.synchronize(package); + + ASSERT_THAT(storage.typeIconPath(fetchTypeId(sourceId2, "QObject")), IsEmpty()); +} + +TEST_F(ProjectStorage, synchronize_updates_type_annotation_type_icon_path) +{ + auto package{createSimpleSynchronizationPackage()}; + package.typeAnnotations = createTypeAnnotions(); + package.updatedTypeAnnotationSourceIds = createUpdatedTypeAnnotionSourceIds( + package.typeAnnotations); + storage.synchronize(package); + package.typeAnnotations[0].iconPath = "/path/to/icon2.png"; + + storage.synchronize(package); + + ASSERT_THAT(storage.typeIconPath(fetchTypeId(sourceId2, "QObject")), Eq("/path/to/icon2.png")); +} + +TEST_F(ProjectStorage, return_empty_path_if_no_type_icon_exists) +{ + auto package{createSimpleSynchronizationPackage()}; + storage.synchronize(package); + + auto iconPath = storage.typeIconPath(fetchTypeId(sourceId2, "QObject")); + + ASSERT_THAT(iconPath, IsEmpty()); +} + +TEST_F(ProjectStorage, return_empty_path_if_that_type_does_not_exists) +{ + auto iconPath = storage.typeIconPath(fetchTypeId(sourceId2, "QObject")); + + ASSERT_THAT(iconPath, IsEmpty()); +} + +TEST_F(ProjectStorage, synchronize_type_hints) +{ + auto package{createSimpleSynchronizationPackage()}; + package.typeAnnotations = createTypeAnnotions(); + package.updatedTypeAnnotationSourceIds = createUpdatedTypeAnnotionSourceIds( + package.typeAnnotations); + + storage.synchronize(package); + + ASSERT_THAT(storage.typeHints(fetchTypeId(sourceId1, "QQuickItem")), + UnorderedElementsAre(IsTypeHint("canBeContainer", "true"), + IsTypeHint("forceClip", "false"))); +} + +TEST_F(ProjectStorage, synchronize_removes_type_hints) +{ + auto package{createSimpleSynchronizationPackage()}; + package.typeAnnotations = createTypeAnnotions(); + package.updatedTypeAnnotationSourceIds = createUpdatedTypeAnnotionSourceIds( + package.typeAnnotations); + storage.synchronize(package); + package.typeAnnotations.clear(); + + storage.synchronize(package); + + ASSERT_THAT(storage.typeHints(fetchTypeId(sourceId2, "QObject")), IsEmpty()); +} + +TEST_F(ProjectStorage, return_empty_type_hints_if_no_type_hints_exists) +{ + auto package{createSimpleSynchronizationPackage()}; + storage.synchronize(package); + + auto typeHints = storage.typeHints(fetchTypeId(sourceId2, "QObject")); + + ASSERT_THAT(typeHints, IsEmpty()); +} + +TEST_F(ProjectStorage, return_empty_type_hints_if_type_does_not_exists) +{ + auto typeHints = storage.typeHints(fetchTypeId(sourceId2, "QObject")); + + ASSERT_THAT(typeHints, IsEmpty()); +} + +TEST_F(ProjectStorage, synchronize_item_library_entries) +{ + auto package{createSimpleSynchronizationPackage()}; + package.typeAnnotations = createTypeAnnotions(); + package.updatedTypeAnnotationSourceIds = createUpdatedTypeAnnotionSourceIds( + package.typeAnnotations); + + storage.synchronize(package); + + ASSERT_THAT( + storage.allItemLibraryEntries(), + UnorderedElementsAre( + IsItemLibraryEntry(fetchTypeId(sourceId2, "QObject"), + "Foo", + "/path/icon", + "Basic Items", + "QtQuick", + "Foo is a Item", + "/path/templates/item.qml", + UnorderedElementsAre(IsItemLibraryProperty("x", "double", 32.1), + IsItemLibraryProperty("y", "double", 12.3)), + UnorderedElementsAre("/path/templates/frame.png", + "/path/templates/frame.frag")), + IsItemLibraryEntry(fetchTypeId(sourceId2, "QObject"), + "Bar", + "/path/icon2", + "Basic Items", + "QtQuick", + "Bar is a Item", + "", + UnorderedElementsAre(IsItemLibraryProperty("color", "color", "#blue")), + IsEmpty()), + IsItemLibraryEntry(fetchTypeId(sourceId1, "QQuickItem"), + "Item", + "/path/icon3", + "Advanced Items", + "QtQuick", + "Item is an Object", + "", + UnorderedElementsAre(IsItemLibraryProperty("x", "double", 1), + IsItemLibraryProperty("y", "double", 2)), + IsEmpty()))); +} + +TEST_F(ProjectStorage, synchronize_removes_item_library_entries) +{ + auto package{createSimpleSynchronizationPackage()}; + package.typeAnnotations = createTypeAnnotions(); + package.updatedTypeAnnotationSourceIds = createUpdatedTypeAnnotionSourceIds( + package.typeAnnotations); + storage.synchronize(package); + package.typeAnnotations.clear(); + + storage.synchronize(package); + + ASSERT_THAT(storage.allItemLibraryEntries(), IsEmpty()); +} + +TEST_F(ProjectStorage, synchronize_udpates_item_library_entries) +{ + auto package{createSimpleSynchronizationPackage()}; + package.typeAnnotations = createTypeAnnotions(); + package.updatedTypeAnnotationSourceIds = createUpdatedTypeAnnotionSourceIds( + package.typeAnnotations); + storage.synchronize(package); + package.typeAnnotations[0].itemLibraryJson + = R"xy([{"name":"Foo","iconPath":"/path/icon","category":"Basic Items", "import":"QtQuick","toolTip":"Foo is a Item", "properties":[["x", "double", 32.1], ["y", "double", 12.3]]}])xy"; + + storage.synchronize(package); + + ASSERT_THAT(storage.itemLibraryEntries(fetchTypeId(sourceId2, "QObject")), + ElementsAre( + IsItemLibraryEntry(fetchTypeId(sourceId2, "QObject"), + "Foo", + "/path/icon", + "Basic Items", + "QtQuick", + "Foo is a Item", + "", + UnorderedElementsAre(IsItemLibraryProperty("x", "double", 32.1), + IsItemLibraryProperty("y", "double", 12.3)), + IsEmpty()))); +} + +TEST_F(ProjectStorage, get_all_item_library_entries) +{ + auto package{createSimpleSynchronizationPackage()}; + package.typeAnnotations = createTypeAnnotions(); + package.updatedTypeAnnotationSourceIds = createUpdatedTypeAnnotionSourceIds( + package.typeAnnotations); + storage.synchronize(package); + + auto entries = storage.allItemLibraryEntries(); + + ASSERT_THAT( + entries, + UnorderedElementsAre( + IsItemLibraryEntry(fetchTypeId(sourceId2, "QObject"), + "Foo", + "/path/icon", + "Basic Items", + "QtQuick", + "Foo is a Item", + "/path/templates/item.qml", + UnorderedElementsAre(IsItemLibraryProperty("x", "double", 32.1), + IsItemLibraryProperty("y", "double", 12.3)), + UnorderedElementsAre("/path/templates/frame.png", + "/path/templates/frame.frag")), + IsItemLibraryEntry(fetchTypeId(sourceId2, "QObject"), + "Bar", + "/path/icon2", + "Basic Items", + "QtQuick", + "Bar is a Item", + "", + UnorderedElementsAre(IsItemLibraryProperty("color", "color", "#blue")), + IsEmpty()), + IsItemLibraryEntry(fetchTypeId(sourceId1, "QQuickItem"), + "Item", + "/path/icon3", + "Advanced Items", + "QtQuick", + "Item is an Object", + "", + UnorderedElementsAre(IsItemLibraryProperty("x", "double", 1), + IsItemLibraryProperty("y", "double", 2)), + IsEmpty()))); +} + +TEST_F(ProjectStorage, get_item_library_entries_by_type_id) +{ + auto package{createSimpleSynchronizationPackage()}; + package.typeAnnotations = createTypeAnnotions(); + package.updatedTypeAnnotationSourceIds = createUpdatedTypeAnnotionSourceIds( + package.typeAnnotations); + storage.synchronize(package); + auto typeId = fetchTypeId(sourceId2, "QObject"); + + auto entries = storage.itemLibraryEntries(typeId); + + ASSERT_THAT( + entries, + UnorderedElementsAre( + IsItemLibraryEntry(fetchTypeId(sourceId2, "QObject"), + "Foo", + "/path/icon", + "Basic Items", + "QtQuick", + "Foo is a Item", + "/path/templates/item.qml", + UnorderedElementsAre(IsItemLibraryProperty("x", "double", 32.1), + IsItemLibraryProperty("y", "double", 12.3)), + UnorderedElementsAre("/path/templates/frame.png", + "/path/templates/frame.frag")), + IsItemLibraryEntry(fetchTypeId(sourceId2, "QObject"), + "Bar", + "/path/icon2", + "Basic Items", + "QtQuick", + "Bar is a Item", + "", + UnorderedElementsAre(IsItemLibraryProperty("color", "color", "#blue")), + IsEmpty()))); +} + +TEST_F(ProjectStorage, get_no_item_library_entries_if_type_id_is_invalid) +{ + auto package{createSimpleSynchronizationPackage()}; + package.typeAnnotations = createTypeAnnotions(); + package.updatedTypeAnnotationSourceIds = createUpdatedTypeAnnotionSourceIds( + package.typeAnnotations); + storage.synchronize(package); + + auto entries = storage.itemLibraryEntries(TypeId()); + + ASSERT_THAT(entries, IsEmpty()); +} + +TEST_F(ProjectStorage, get_item_library_entries_by_source_id) +{ + auto package{createSimpleSynchronizationPackage()}; + package.typeAnnotations = createTypeAnnotions(); + package.updatedTypeAnnotationSourceIds = createUpdatedTypeAnnotionSourceIds( + package.typeAnnotations); + storage.synchronize(package); + + auto entries = storage.itemLibraryEntries(sourceId2); + + ASSERT_THAT( + entries, + UnorderedElementsAre( + IsItemLibraryEntry(fetchTypeId(sourceId2, "QObject"), + "Foo", + "/path/icon", + "Basic Items", + "QtQuick", + "Foo is a Item", + "/path/templates/item.qml", + UnorderedElementsAre(IsItemLibraryProperty("x", "double", 32.1), + IsItemLibraryProperty("y", "double", 12.3)), + UnorderedElementsAre("/path/templates/frame.png", + "/path/templates/frame.frag")), + IsItemLibraryEntry(fetchTypeId(sourceId2, "QObject"), + "Bar", + "/path/icon2", + "Basic Items", + "QtQuick", + "Bar is a Item", + "", + UnorderedElementsAre(IsItemLibraryProperty("color", "color", "#blue")), + IsEmpty()))); +} + +TEST_F(ProjectStorage, return_type_ids_for_module_id) +{ + auto package{createBuiltinSynchronizationPackage()}; + storage.synchronize(package); + + auto typeIds = storage.typeIds(QMLModuleId); + + ASSERT_THAT(typeIds, + UnorderedElementsAre(fetchTypeId(sourceId1, "bool"), + fetchTypeId(sourceId1, "int"), + fetchTypeId(sourceId1, "double"), + fetchTypeId(sourceId1, "date"), + fetchTypeId(sourceId1, "string"), + fetchTypeId(sourceId1, "url"), + fetchTypeId(sourceId1, "var"))); +} + +TEST_F(ProjectStorage, get_hair_ids) +{ + auto package{createHeirPackage()}; + storage.synchronize(package); + auto typeId = fetchTypeId(sourceId1, "QObject"); + + auto heirIds = storage.heirIds(typeId); + + ASSERT_THAT(heirIds, + UnorderedElementsAre(fetchTypeId(sourceId1, "QObject2"), + fetchTypeId(sourceId1, "QObject3"), + fetchTypeId(sourceId1, "QObject4"), + fetchTypeId(sourceId1, "QObject5"))); +} + +TEST_F(ProjectStorage, get_no_hair_ids_for_invalid_type_id) +{ + auto package{createHeirPackage()}; + storage.synchronize(package); + auto typeId = TypeId{}; + + auto heirIds = storage.heirIds(typeId); + + ASSERT_THAT(heirIds, IsEmpty()); +} } // namespace diff --git a/tests/unit/tests/unittests/projectstorage/projectstoragepathwatcher-test.cpp b/tests/unit/tests/unittests/projectstorage/projectstoragepathwatcher-test.cpp index 9fc9c30551b..d6ed96d0cfc 100644 --- a/tests/unit/tests/unittests/projectstorage/projectstoragepathwatcher-test.cpp +++ b/tests/unit/tests/unittests/projectstorage/projectstoragepathwatcher-test.cpp @@ -39,6 +39,22 @@ using QmlDesigner::WatcherEntry; class ProjectStoragePathWatcher : public testing::Test { protected: + static void SetUpTestSuite() + { + static_database = std::make_unique(":memory:", Sqlite::JournalMode::Memory); + + static_projectStorage = std::make_unique>( + *static_database, static_database->isInitialized()); + } + + static void TearDownTestSuite() + { + static_projectStorage.reset(); + static_database.reset(); + } + + ~ProjectStoragePathWatcher() { static_projectStorage->resetForTestsOnly(); } + ProjectStoragePathWatcher() { ON_CALL(mockFileSystem, fileStatus(_)).WillByDefault([](auto sourceId) { @@ -52,6 +68,7 @@ protected: ON_CALL(mockFileSystem, directoryEntries(Eq(sourceContextPath3))) .WillByDefault(Return(SourceIds{sourceIds[4]})); } + static WatcherEntries sorted(WatcherEntries &&entries) { std::stable_sort(entries.begin(), entries.end()); @@ -62,8 +79,10 @@ protected: protected: NiceMock notifier; NiceMock mockFileSystem; - Sqlite::Database database{":memory:", Sqlite::JournalMode::Memory}; - QmlDesigner::ProjectStorage storage{database, database.isInitialized()}; + inline static std::unique_ptr static_database; + Sqlite::Database &database = *static_database; + inline static std::unique_ptr> static_projectStorage; + QmlDesigner::ProjectStorage &storage = *static_projectStorage; SourcePathCache pathCache{storage}; Watcher watcher{pathCache, mockFileSystem, ¬ifier}; NiceMock &mockQFileSytemWatcher = watcher.fileSystemWatcher(); diff --git a/tests/unit/tests/unittests/projectstorage/projectstorageupdater-test.cpp b/tests/unit/tests/unittests/projectstorage/projectstorageupdater-test.cpp index c167e3eb8b5..d9578d2f1b6 100644 --- a/tests/unit/tests/unittests/projectstorage/projectstorageupdater-test.cpp +++ b/tests/unit/tests/unittests/projectstorage/projectstorageupdater-test.cpp @@ -31,6 +31,7 @@ using QmlDesigner::Storage::Synchronization::ModuleExportedImport; using QmlDesigner::Storage::Synchronization::ProjectData; using QmlDesigner::Storage::Synchronization::SynchronizationPackage; using QmlDesigner::Storage::TypeTraits; +using QmlDesigner::Storage::TypeTraitsKind; using QmlDesigner::Storage::Version; MATCHER_P5(IsStorageType, @@ -136,6 +137,20 @@ auto IsPropertyEditorQmlPath(const ModuleIdMatcher &moduleIdMatcher, class ProjectStorageUpdater : public testing::Test { public: + static void SetUpTestSuite() + { + static_database = std::make_unique(":memory:", Sqlite::JournalMode::Memory); + + static_projectStorage = std::make_unique>( + *static_database, static_database->isInitialized()); + } + + static void TearDownTestSuite() + { + static_projectStorage.reset(); + static_database.reset(); + } + ProjectStorageUpdater() { setFilesChanged({qmltypesPathSourceId, @@ -207,6 +222,8 @@ public: }); } + ~ProjectStorageUpdater() { static_projectStorage->resetForTestsOnly(); } + void setFilesDontChanged(const QmlDesigner::SourceIds &sourceIds) { for (auto sourceId : sourceIds) { @@ -287,8 +304,10 @@ protected: NiceMock qmlTypesParserMock; NiceMock qmlDocumentParserMock; QmlDesigner::FileStatusCache fileStatusCache{fileSystemMock}; - Sqlite::Database database{":memory:", Sqlite::JournalMode::Memory}; - QmlDesigner::ProjectStorage storage{database, database.isInitialized()}; + inline static std::unique_ptr static_database; + Sqlite::Database &database = *static_database; + inline static std::unique_ptr> static_projectStorage; + QmlDesigner::ProjectStorage &storage = *static_projectStorage; QmlDesigner::SourcePathCache> sourcePathCache{ storage}; NiceMock patchWatcherMock; @@ -323,14 +342,14 @@ protected: "QObject", Storage::Synchronization::ImportedType{}, Storage::Synchronization::ImportedType{}, - Storage::TypeTraits::Reference, + Storage::TypeTraitsKind::Reference, qmltypesPathSourceId, {Storage::Synchronization::ExportedType{exampleModuleId, "Object"}, Storage::Synchronization::ExportedType{exampleModuleId, "Obj"}}}; Storage::Synchronization::Type itemType{"QItem", Storage::Synchronization::ImportedType{}, Storage::Synchronization::ImportedType{}, - Storage::TypeTraits::Reference, + Storage::TypeTraitsKind::Reference, qmltypes2PathSourceId, {Storage::Synchronization::ExportedType{exampleModuleId, "Item"}}}; @@ -580,7 +599,7 @@ TEST_F(ProjectStorageUpdater, synchronize_qml_documents) UnorderedElementsAre( AllOf(IsStorageType("First.qml", Storage::Synchronization::ImportedType{"Object"}, - TypeTraits::Reference, + TypeTraitsKind::Reference, qmlDocumentSourceId1, Storage::Synchronization::ChangeLevel::Full), Field(&Storage::Synchronization::Type::exportedTypes, @@ -588,7 +607,7 @@ TEST_F(ProjectStorageUpdater, synchronize_qml_documents) IsExportedType(pathModuleId, "First", -1, -1)))), AllOf(IsStorageType("First2.qml", Storage::Synchronization::ImportedType{"Object2"}, - TypeTraits::Reference, + TypeTraitsKind::Reference, qmlDocumentSourceId2, Storage::Synchronization::ChangeLevel::Full), Field(&Storage::Synchronization::Type::exportedTypes, @@ -596,7 +615,7 @@ TEST_F(ProjectStorageUpdater, synchronize_qml_documents) IsExportedType(pathModuleId, "First2", -1, -1)))), AllOf(IsStorageType("Second.qml", Storage::Synchronization::ImportedType{"Object3"}, - TypeTraits::Reference, + TypeTraitsKind::Reference, qmlDocumentSourceId3, Storage::Synchronization::ChangeLevel::Full), Field(&Storage::Synchronization::Type::exportedTypes, @@ -655,7 +674,7 @@ TEST_F(ProjectStorageUpdater, synchronize_add_only_qml_document_in_directory) UnorderedElementsAre( AllOf(IsStorageType("First.qml", Storage::Synchronization::ImportedType{}, - TypeTraits::Reference, + TypeTraitsKind::Reference, qmlDocumentSourceId1, Storage::Synchronization::ChangeLevel::Minimal), Field(&Storage::Synchronization::Type::exportedTypes, @@ -664,7 +683,7 @@ TEST_F(ProjectStorageUpdater, synchronize_add_only_qml_document_in_directory) IsExportedType(pathModuleId, "First", -1, -1)))), AllOf(IsStorageType("First2.qml", Storage::Synchronization::ImportedType{"Object2"}, - TypeTraits::Reference, + TypeTraitsKind::Reference, qmlDocumentSourceId2, Storage::Synchronization::ChangeLevel::Full), Field(&Storage::Synchronization::Type::exportedTypes, @@ -715,7 +734,7 @@ TEST_F(ProjectStorageUpdater, synchronize_removes_qml_document) UnorderedElementsAre( AllOf(IsStorageType("First.qml", Storage::Synchronization::ImportedType{}, - TypeTraits::Reference, + TypeTraitsKind::Reference, qmlDocumentSourceId1, Storage::Synchronization::ChangeLevel::Minimal), Field(&Storage::Synchronization::Type::exportedTypes, @@ -724,7 +743,7 @@ TEST_F(ProjectStorageUpdater, synchronize_removes_qml_document) IsExportedType(pathModuleId, "First", -1, -1)))), AllOf(IsStorageType("First2.qml", Storage::Synchronization::ImportedType{}, - TypeTraits::Reference, + TypeTraitsKind::Reference, qmlDocumentSourceId2, Storage::Synchronization::ChangeLevel::Minimal), Field(&Storage::Synchronization::Type::exportedTypes, @@ -776,7 +795,7 @@ TEST_F(ProjectStorageUpdater, synchronize_removes_qml_document_in_qmldir_only) UnorderedElementsAre( AllOf(IsStorageType("First.qml", Storage::Synchronization::ImportedType{}, - TypeTraits::Reference, + TypeTraitsKind::Reference, qmlDocumentSourceId1, Storage::Synchronization::ChangeLevel::Minimal), Field(&Storage::Synchronization::Type::exportedTypes, @@ -784,7 +803,7 @@ TEST_F(ProjectStorageUpdater, synchronize_removes_qml_document_in_qmldir_only) IsExportedType(pathModuleId, "First", -1, -1)))), AllOf(IsStorageType("First2.qml", Storage::Synchronization::ImportedType{}, - TypeTraits::Reference, + TypeTraitsKind::Reference, qmlDocumentSourceId2, Storage::Synchronization::ChangeLevel::Minimal), Field(&Storage::Synchronization::Type::exportedTypes, @@ -833,7 +852,7 @@ TEST_F(ProjectStorageUpdater, synchronize_add_qml_document_to_qmldir) UnorderedElementsAre( AllOf(IsStorageType("First.qml", Storage::Synchronization::ImportedType{}, - TypeTraits::Reference, + TypeTraitsKind::Reference, qmlDocumentSourceId1, Storage::Synchronization::ChangeLevel::Minimal), Field(&Storage::Synchronization::Type::exportedTypes, @@ -841,7 +860,7 @@ TEST_F(ProjectStorageUpdater, synchronize_add_qml_document_to_qmldir) IsExportedType(pathModuleId, "First", -1, -1)))), AllOf(IsStorageType("First2.qml", Storage::Synchronization::ImportedType{}, - TypeTraits::Reference, + TypeTraitsKind::Reference, qmlDocumentSourceId2, Storage::Synchronization::ChangeLevel::Minimal), Field(&Storage::Synchronization::Type::exportedTypes, @@ -889,7 +908,7 @@ TEST_F(ProjectStorageUpdater, synchronize_remove_qml_document_from_qmldir) UnorderedElementsAre( AllOf(IsStorageType("First.qml", Storage::Synchronization::ImportedType{}, - TypeTraits::Reference, + TypeTraitsKind::Reference, qmlDocumentSourceId1, Storage::Synchronization::ChangeLevel::Minimal), Field(&Storage::Synchronization::Type::exportedTypes, @@ -897,7 +916,7 @@ TEST_F(ProjectStorageUpdater, synchronize_remove_qml_document_from_qmldir) IsExportedType(pathModuleId, "First", -1, -1)))), AllOf(IsStorageType("First2.qml", Storage::Synchronization::ImportedType{}, - TypeTraits::Reference, + TypeTraitsKind::Reference, qmlDocumentSourceId2, Storage::Synchronization::ChangeLevel::Minimal), Field(&Storage::Synchronization::Type::exportedTypes, @@ -941,7 +960,7 @@ TEST_F(ProjectStorageUpdater, synchronize_qml_documents_dont_update_if_up_to_dat UnorderedElementsAre( AllOf(IsStorageType("First.qml", Storage::Synchronization::ImportedType{"Object"}, - TypeTraits::Reference, + TypeTraitsKind::Reference, qmlDocumentSourceId1, Storage::Synchronization::ChangeLevel::Full), Field(&Storage::Synchronization::Type::exportedTypes, @@ -949,7 +968,7 @@ TEST_F(ProjectStorageUpdater, synchronize_qml_documents_dont_update_if_up_to_dat IsExportedType(pathModuleId, "First", -1, -1)))), AllOf(IsStorageType("First2.qml", Storage::Synchronization::ImportedType{"Object2"}, - TypeTraits::Reference, + TypeTraitsKind::Reference, qmlDocumentSourceId2, Storage::Synchronization::ChangeLevel::Full), Field(&Storage::Synchronization::Type::exportedTypes, @@ -957,7 +976,7 @@ TEST_F(ProjectStorageUpdater, synchronize_qml_documents_dont_update_if_up_to_dat IsExportedType(pathModuleId, "First2", -1, -1)))), AllOf(IsStorageType("Second.qml", Storage::Synchronization::ImportedType{}, - TypeTraits::Reference, + TypeTraitsKind::Reference, qmlDocumentSourceId3, Storage::Synchronization::ChangeLevel::Minimal), Field(&Storage::Synchronization::Type::exportedTypes, @@ -1014,13 +1033,13 @@ TEST_F(ProjectStorageUpdater, synchroniz_if_qmldir_file_has_not_changed) Eq(itemType), AllOf(IsStorageType("First.qml", Storage::Synchronization::ImportedType{"Object"}, - TypeTraits::Reference, + TypeTraitsKind::Reference, qmlDocumentSourceId1, Storage::Synchronization::ChangeLevel::ExcludeExportedTypes), Field(&Storage::Synchronization::Type::exportedTypes, IsEmpty())), AllOf(IsStorageType("First2.qml", Storage::Synchronization::ImportedType{"Object2"}, - TypeTraits::Reference, + TypeTraitsKind::Reference, qmlDocumentSourceId2, Storage::Synchronization::ChangeLevel::ExcludeExportedTypes), Field(&Storage::Synchronization::Type::exportedTypes, IsEmpty())))), @@ -1063,7 +1082,7 @@ TEST_F(ProjectStorageUpdater, synchroniz_if_qmldir_file_has_not_changed_and_some Eq(objectType), AllOf(IsStorageType("First.qml", Storage::Synchronization::ImportedType{"Object"}, - TypeTraits::Reference, + TypeTraitsKind::Reference, qmlDocumentSourceId1, Storage::Synchronization::ChangeLevel::ExcludeExportedTypes), Field(&Storage::Synchronization::Type::exportedTypes, IsEmpty())))), @@ -1118,7 +1137,7 @@ TEST_F(ProjectStorageUpdater, synchroniz_if_qmldir_file_has_changed_and_some_rem UnorderedElementsAre(AllOf( IsStorageType("First2.qml", Storage::Synchronization::ImportedType{}, - TypeTraits::Reference, + TypeTraitsKind::Reference, qmlDocumentSourceId2, Storage::Synchronization::ChangeLevel::Minimal), Field(&Storage::Synchronization::Type::exportedTypes, @@ -1230,7 +1249,7 @@ TEST_F(ProjectStorageUpdater, synchronize_qml_documents_with_different_version_b UnorderedElementsAre(AllOf( IsStorageType("First.qml", Storage::Synchronization::ImportedType{"Object"}, - TypeTraits::Reference, + TypeTraitsKind::Reference, qmlDocumentSourceId1, Storage::Synchronization::ChangeLevel::Full), Field(&Storage::Synchronization::Type::exportedTypes, @@ -1272,7 +1291,7 @@ TEST_F(ProjectStorageUpdater, synchronize_qml_documents_with_different_type_name UnorderedElementsAre(AllOf( IsStorageType("First.qml", Storage::Synchronization::ImportedType{"Object"}, - TypeTraits::Reference, + TypeTraitsKind::Reference, qmlDocumentSourceId1, Storage::Synchronization::ChangeLevel::Full), Field(&Storage::Synchronization::Type::exportedTypes, @@ -1820,21 +1839,21 @@ TEST_F(ProjectStorageUpdater, synchronize_qml_documents_without_qmldir) UnorderedElementsAre( AllOf(IsStorageType("First.qml", Storage::Synchronization::ImportedType{"Object"}, - TypeTraits::Reference, + TypeTraitsKind::Reference, qmlDocumentSourceId1, Storage::Synchronization::ChangeLevel::Full), Field(&Storage::Synchronization::Type::exportedTypes, UnorderedElementsAre(IsExportedType(pathModuleId, "First", -1, -1)))), AllOf(IsStorageType("First2.qml", Storage::Synchronization::ImportedType{"Object2"}, - TypeTraits::Reference, + TypeTraitsKind::Reference, qmlDocumentSourceId2, Storage::Synchronization::ChangeLevel::Full), Field(&Storage::Synchronization::Type::exportedTypes, UnorderedElementsAre(IsExportedType(pathModuleId, "First2", -1, -1)))), AllOf(IsStorageType("Second.qml", Storage::Synchronization::ImportedType{"Object3"}, - TypeTraits::Reference, + TypeTraitsKind::Reference, qmlDocumentSourceId3, Storage::Synchronization::ChangeLevel::Full), Field(&Storage::Synchronization::Type::exportedTypes, @@ -1930,7 +1949,7 @@ TEST_F(ProjectStorageUpdater, synchronize_qml_documents_without_qmldir_add_qml_d UnorderedElementsAre(AllOf( IsStorageType("Second.qml", Storage::Synchronization::ImportedType{"Object3"}, - TypeTraits::Reference, + TypeTraitsKind::Reference, qmlDocumentSourceId3, Storage::Synchronization::ChangeLevel::Full), Field(&Storage::Synchronization::Type::exportedTypes, @@ -2019,7 +2038,7 @@ TEST_F(ProjectStorageUpdater, watcher_updates_directories) UnorderedElementsAre( AllOf(IsStorageType("First.qml", Storage::Synchronization::ImportedType{"Object"}, - TypeTraits::Reference, + TypeTraitsKind::Reference, qmlDocumentSourceId1, Storage::Synchronization::ChangeLevel::Full), Field(&Storage::Synchronization::Type::exportedTypes, @@ -2027,7 +2046,7 @@ TEST_F(ProjectStorageUpdater, watcher_updates_directories) IsExportedType(pathModuleId, "First", -1, -1)))), AllOf(IsStorageType("First2.qml", Storage::Synchronization::ImportedType{"Object2"}, - TypeTraits::Reference, + TypeTraitsKind::Reference, qmlDocumentSourceId2, Storage::Synchronization::ChangeLevel::Full), Field(&Storage::Synchronization::Type::exportedTypes, @@ -2035,7 +2054,7 @@ TEST_F(ProjectStorageUpdater, watcher_updates_directories) IsExportedType(pathModuleId, "First2", -1, -1)))), AllOf(IsStorageType("Second.qml", Storage::Synchronization::ImportedType{"Object3"}, - TypeTraits::Reference, + TypeTraitsKind::Reference, qmlDocumentSourceId3, Storage::Synchronization::ChangeLevel::Full), Field(&Storage::Synchronization::Type::exportedTypes, @@ -2162,7 +2181,7 @@ TEST_F(ProjectStorageUpdater, watcher_updates_directories_and_qmldir) UnorderedElementsAre( AllOf(IsStorageType("First.qml", Storage::Synchronization::ImportedType{"Object"}, - TypeTraits::Reference, + TypeTraitsKind::Reference, qmlDocumentSourceId1, Storage::Synchronization::ChangeLevel::Full), Field(&Storage::Synchronization::Type::exportedTypes, @@ -2170,7 +2189,7 @@ TEST_F(ProjectStorageUpdater, watcher_updates_directories_and_qmldir) IsExportedType(pathModuleId, "First", -1, -1)))), AllOf(IsStorageType("First2.qml", Storage::Synchronization::ImportedType{"Object2"}, - TypeTraits::Reference, + TypeTraitsKind::Reference, qmlDocumentSourceId2, Storage::Synchronization::ChangeLevel::Full), Field(&Storage::Synchronization::Type::exportedTypes, @@ -2178,7 +2197,7 @@ TEST_F(ProjectStorageUpdater, watcher_updates_directories_and_qmldir) IsExportedType(pathModuleId, "First2", -1, -1)))), AllOf(IsStorageType("Second.qml", Storage::Synchronization::ImportedType{"Object3"}, - TypeTraits::Reference, + TypeTraitsKind::Reference, qmlDocumentSourceId3, Storage::Synchronization::ChangeLevel::Full), Field(&Storage::Synchronization::Type::exportedTypes, @@ -2276,7 +2295,7 @@ TEST_F(ProjectStorageUpdater, watcher_updates_add_only_qml_document_in_directory UnorderedElementsAre( AllOf(IsStorageType("First.qml", Storage::Synchronization::ImportedType{}, - TypeTraits::Reference, + TypeTraitsKind::Reference, qmlDocumentSourceId1, Storage::Synchronization::ChangeLevel::Minimal), Field(&Storage::Synchronization::Type::exportedTypes, @@ -2285,7 +2304,7 @@ TEST_F(ProjectStorageUpdater, watcher_updates_add_only_qml_document_in_directory IsExportedType(pathModuleId, "First", -1, -1)))), AllOf(IsStorageType("First2.qml", Storage::Synchronization::ImportedType{"Object2"}, - TypeTraits::Reference, + TypeTraitsKind::Reference, qmlDocumentSourceId2, Storage::Synchronization::ChangeLevel::Full), Field(&Storage::Synchronization::Type::exportedTypes, @@ -2336,7 +2355,7 @@ TEST_F(ProjectStorageUpdater, watcher_updates_removes_qml_document) UnorderedElementsAre( AllOf(IsStorageType("First.qml", Storage::Synchronization::ImportedType{}, - TypeTraits::Reference, + TypeTraitsKind::Reference, qmlDocumentSourceId1, Storage::Synchronization::ChangeLevel::Minimal), Field(&Storage::Synchronization::Type::exportedTypes, @@ -2345,7 +2364,7 @@ TEST_F(ProjectStorageUpdater, watcher_updates_removes_qml_document) IsExportedType(pathModuleId, "First", -1, -1)))), AllOf(IsStorageType("First2.qml", Storage::Synchronization::ImportedType{}, - TypeTraits::Reference, + TypeTraitsKind::Reference, qmlDocumentSourceId2, Storage::Synchronization::ChangeLevel::Minimal), Field(&Storage::Synchronization::Type::exportedTypes, @@ -2397,7 +2416,7 @@ TEST_F(ProjectStorageUpdater, watcher_updates_removes_qml_document_in_qmldir_onl UnorderedElementsAre( AllOf(IsStorageType("First.qml", Storage::Synchronization::ImportedType{}, - TypeTraits::Reference, + TypeTraitsKind::Reference, qmlDocumentSourceId1, Storage::Synchronization::ChangeLevel::Minimal), Field(&Storage::Synchronization::Type::exportedTypes, @@ -2405,7 +2424,7 @@ TEST_F(ProjectStorageUpdater, watcher_updates_removes_qml_document_in_qmldir_onl IsExportedType(pathModuleId, "First", -1, -1)))), AllOf(IsStorageType("First2.qml", Storage::Synchronization::ImportedType{}, - TypeTraits::Reference, + TypeTraitsKind::Reference, qmlDocumentSourceId2, Storage::Synchronization::ChangeLevel::Minimal), Field(&Storage::Synchronization::Type::exportedTypes, @@ -2454,7 +2473,7 @@ TEST_F(ProjectStorageUpdater, watcher_updates_directories_add_qml_document_to_qm UnorderedElementsAre( AllOf(IsStorageType("First.qml", Storage::Synchronization::ImportedType{}, - TypeTraits::Reference, + TypeTraitsKind::Reference, qmlDocumentSourceId1, Storage::Synchronization::ChangeLevel::Minimal), Field(&Storage::Synchronization::Type::exportedTypes, @@ -2462,7 +2481,7 @@ TEST_F(ProjectStorageUpdater, watcher_updates_directories_add_qml_document_to_qm IsExportedType(pathModuleId, "First", -1, -1)))), AllOf(IsStorageType("First2.qml", Storage::Synchronization::ImportedType{}, - TypeTraits::Reference, + TypeTraitsKind::Reference, qmlDocumentSourceId2, Storage::Synchronization::ChangeLevel::Minimal), Field(&Storage::Synchronization::Type::exportedTypes, @@ -2510,7 +2529,7 @@ TEST_F(ProjectStorageUpdater, watcher_updates_directories_remove_qml_document_fr UnorderedElementsAre( AllOf(IsStorageType("First.qml", Storage::Synchronization::ImportedType{}, - TypeTraits::Reference, + TypeTraitsKind::Reference, qmlDocumentSourceId1, Storage::Synchronization::ChangeLevel::Minimal), Field(&Storage::Synchronization::Type::exportedTypes, @@ -2518,7 +2537,7 @@ TEST_F(ProjectStorageUpdater, watcher_updates_directories_remove_qml_document_fr IsExportedType(pathModuleId, "First", -1, -1)))), AllOf(IsStorageType("First2.qml", Storage::Synchronization::ImportedType{}, - TypeTraits::Reference, + TypeTraitsKind::Reference, qmlDocumentSourceId2, Storage::Synchronization::ChangeLevel::Minimal), Field(&Storage::Synchronization::Type::exportedTypes, @@ -2562,7 +2581,7 @@ TEST_F(ProjectStorageUpdater, watcher_updates_directories_dont_update_qml_docume UnorderedElementsAre( AllOf(IsStorageType("First.qml", Storage::Synchronization::ImportedType{"Object"}, - TypeTraits::Reference, + TypeTraitsKind::Reference, qmlDocumentSourceId1, Storage::Synchronization::ChangeLevel::Full), Field(&Storage::Synchronization::Type::exportedTypes, @@ -2570,7 +2589,7 @@ TEST_F(ProjectStorageUpdater, watcher_updates_directories_dont_update_qml_docume IsExportedType(pathModuleId, "First", -1, -1)))), AllOf(IsStorageType("First2.qml", Storage::Synchronization::ImportedType{"Object2"}, - TypeTraits::Reference, + TypeTraitsKind::Reference, qmlDocumentSourceId2, Storage::Synchronization::ChangeLevel::Full), Field(&Storage::Synchronization::Type::exportedTypes, @@ -2578,7 +2597,7 @@ TEST_F(ProjectStorageUpdater, watcher_updates_directories_dont_update_qml_docume IsExportedType(pathModuleId, "First2", -1, -1)))), AllOf(IsStorageType("Second.qml", Storage::Synchronization::ImportedType{}, - TypeTraits::Reference, + TypeTraitsKind::Reference, qmlDocumentSourceId3, Storage::Synchronization::ChangeLevel::Minimal), Field(&Storage::Synchronization::Type::exportedTypes, @@ -2632,7 +2651,7 @@ TEST_F(ProjectStorageUpdater, watcher_updates_qmldirs_dont_update_qml_documents_ UnorderedElementsAre( AllOf(IsStorageType("First.qml", Storage::Synchronization::ImportedType{"Object"}, - TypeTraits::Reference, + TypeTraitsKind::Reference, qmlDocumentSourceId1, Storage::Synchronization::ChangeLevel::Full), Field(&Storage::Synchronization::Type::exportedTypes, @@ -2640,7 +2659,7 @@ TEST_F(ProjectStorageUpdater, watcher_updates_qmldirs_dont_update_qml_documents_ IsExportedType(pathModuleId, "First", -1, -1)))), AllOf(IsStorageType("First2.qml", Storage::Synchronization::ImportedType{"Object2"}, - TypeTraits::Reference, + TypeTraitsKind::Reference, qmlDocumentSourceId2, Storage::Synchronization::ChangeLevel::Full), Field(&Storage::Synchronization::Type::exportedTypes, @@ -2648,7 +2667,7 @@ TEST_F(ProjectStorageUpdater, watcher_updates_qmldirs_dont_update_qml_documents_ IsExportedType(pathModuleId, "First2", -1, -1)))), AllOf(IsStorageType("Second.qml", Storage::Synchronization::ImportedType{}, - TypeTraits::Reference, + TypeTraitsKind::Reference, qmlDocumentSourceId3, Storage::Synchronization::ChangeLevel::Minimal), Field(&Storage::Synchronization::Type::exportedTypes, @@ -2705,13 +2724,13 @@ TEST_F(ProjectStorageUpdater, watcher_updates_directory_but_not_qmldir) Eq(itemType), AllOf(IsStorageType("First.qml", Storage::Synchronization::ImportedType{"Object"}, - TypeTraits::Reference, + TypeTraitsKind::Reference, qmlDocumentSourceId1, Storage::Synchronization::ChangeLevel::ExcludeExportedTypes), Field(&Storage::Synchronization::Type::exportedTypes, IsEmpty())), AllOf(IsStorageType("First2.qml", Storage::Synchronization::ImportedType{"Object2"}, - TypeTraits::Reference, + TypeTraitsKind::Reference, qmlDocumentSourceId2, Storage::Synchronization::ChangeLevel::ExcludeExportedTypes), Field(&Storage::Synchronization::Type::exportedTypes, IsEmpty())))), @@ -2745,13 +2764,13 @@ TEST_F(ProjectStorageUpdater, watcher_updates_qml_documents) UnorderedElementsAre( AllOf(IsStorageType("First.qml", Storage::Synchronization::ImportedType{"Object"}, - TypeTraits::Reference, + TypeTraitsKind::Reference, qmlDocumentSourceId1, Storage::Synchronization::ChangeLevel::ExcludeExportedTypes), Field(&Storage::Synchronization::Type::exportedTypes, IsEmpty())), AllOf(IsStorageType("First2.qml", Storage::Synchronization::ImportedType{"Object2"}, - TypeTraits::Reference, + TypeTraitsKind::Reference, qmlDocumentSourceId2, Storage::Synchronization::ChangeLevel::ExcludeExportedTypes), Field(&Storage::Synchronization::Type::exportedTypes, IsEmpty())))), @@ -2779,7 +2798,7 @@ TEST_F(ProjectStorageUpdater, watcher_updates_removed_qml_documents) UnorderedElementsAre(AllOf( IsStorageType("First.qml", Storage::Synchronization::ImportedType{"Object"}, - TypeTraits::Reference, + TypeTraitsKind::Reference, qmlDocumentSourceId1, Storage::Synchronization::ChangeLevel::ExcludeExportedTypes), Field(&Storage::Synchronization::Type::exportedTypes, IsEmpty())))), @@ -2944,7 +2963,7 @@ TEST_F(ProjectStorageUpdater, watcher_updates_directories_and_but_not_included_q UnorderedElementsAre( AllOf(IsStorageType("First.qml", Storage::Synchronization::ImportedType{"Object"}, - TypeTraits::Reference, + TypeTraitsKind::Reference, qmlDocumentSourceId1, Storage::Synchronization::ChangeLevel::Full), Field(&Storage::Synchronization::Type::exportedTypes, @@ -2952,7 +2971,7 @@ TEST_F(ProjectStorageUpdater, watcher_updates_directories_and_but_not_included_q IsExportedType(pathModuleId, "First", -1, -1)))), AllOf(IsStorageType("First2.qml", Storage::Synchronization::ImportedType{"Object2"}, - TypeTraits::Reference, + TypeTraitsKind::Reference, qmlDocumentSourceId2, Storage::Synchronization::ChangeLevel::Full), Field(&Storage::Synchronization::Type::exportedTypes, @@ -2960,7 +2979,7 @@ TEST_F(ProjectStorageUpdater, watcher_updates_directories_and_but_not_included_q IsExportedType(pathModuleId, "First2", -1, -1)))), AllOf(IsStorageType("Second.qml", Storage::Synchronization::ImportedType{"Object3"}, - TypeTraits::Reference, + TypeTraitsKind::Reference, qmlDocumentSourceId3, Storage::Synchronization::ChangeLevel::Full), Field(&Storage::Synchronization::Type::exportedTypes, @@ -3018,7 +3037,7 @@ TEST_F(ProjectStorageUpdater, watcher_updates_qmldir_and_but_not_included_qml_do UnorderedElementsAre( AllOf(IsStorageType("First.qml", Storage::Synchronization::ImportedType{"Object"}, - TypeTraits::Reference, + TypeTraitsKind::Reference, qmlDocumentSourceId1, Storage::Synchronization::ChangeLevel::Full), Field(&Storage::Synchronization::Type::exportedTypes, @@ -3026,7 +3045,7 @@ TEST_F(ProjectStorageUpdater, watcher_updates_qmldir_and_but_not_included_qml_do IsExportedType(pathModuleId, "First", -1, -1)))), AllOf(IsStorageType("First2.qml", Storage::Synchronization::ImportedType{"Object2"}, - TypeTraits::Reference, + TypeTraitsKind::Reference, qmlDocumentSourceId2, Storage::Synchronization::ChangeLevel::Full), Field(&Storage::Synchronization::Type::exportedTypes, @@ -3034,7 +3053,7 @@ TEST_F(ProjectStorageUpdater, watcher_updates_qmldir_and_but_not_included_qml_do IsExportedType(pathModuleId, "First2", -1, -1)))), AllOf(IsStorageType("Second.qml", Storage::Synchronization::ImportedType{"Object3"}, - TypeTraits::Reference, + TypeTraitsKind::Reference, qmlDocumentSourceId3, Storage::Synchronization::ChangeLevel::Full), Field(&Storage::Synchronization::Type::exportedTypes, @@ -3111,7 +3130,7 @@ TEST_F(ProjectStorageUpdater, watcher_updates_qmldir_and_but_not_included_qmltyp Eq(itemType), AllOf(IsStorageType("First.qml", Storage::Synchronization::ImportedType{"Object"}, - TypeTraits::Reference, + TypeTraitsKind::Reference, qmlDocumentSourceId1, Storage::Synchronization::ChangeLevel::Full), Field(&Storage::Synchronization::Type::exportedTypes, @@ -3119,7 +3138,7 @@ TEST_F(ProjectStorageUpdater, watcher_updates_qmldir_and_but_not_included_qmltyp IsExportedType(pathModuleId, "First", -1, -1)))), AllOf(IsStorageType("First2.qml", Storage::Synchronization::ImportedType{"Object2"}, - TypeTraits::Reference, + TypeTraitsKind::Reference, qmlDocumentSourceId2, Storage::Synchronization::ChangeLevel::Full), Field(&Storage::Synchronization::Type::exportedTypes, @@ -3127,7 +3146,7 @@ TEST_F(ProjectStorageUpdater, watcher_updates_qmldir_and_but_not_included_qmltyp IsExportedType(pathModuleId, "First2", -1, -1)))), AllOf(IsStorageType("Second.qml", Storage::Synchronization::ImportedType{"Object3"}, - TypeTraits::Reference, + TypeTraitsKind::Reference, qmlDocumentSourceId3, Storage::Synchronization::ChangeLevel::Full), Field(&Storage::Synchronization::Type::exportedTypes, @@ -3224,13 +3243,13 @@ TEST_F(ProjectStorageUpdater, input_is_reused_next_call_if_an_error_happens) Eq(itemType), AllOf(IsStorageType("First.qml", Storage::Synchronization::ImportedType{"Object"}, - TypeTraits::Reference, + TypeTraitsKind::Reference, qmlDocumentSourceId1, Storage::Synchronization::ChangeLevel::ExcludeExportedTypes), Field(&Storage::Synchronization::Type::exportedTypes, IsEmpty())), AllOf(IsStorageType("First2.qml", Storage::Synchronization::ImportedType{"Object2"}, - TypeTraits::Reference, + TypeTraitsKind::Reference, qmlDocumentSourceId2, Storage::Synchronization::ChangeLevel::ExcludeExportedTypes), Field(&Storage::Synchronization::Type::exportedTypes, IsEmpty())))), @@ -3277,23 +3296,22 @@ TEST_F(ProjectStorageUpdater, input_is_reused_next_call_if_an_error_happens_and_ synchronize(AllOf( Field(&SynchronizationPackage::imports, UnorderedElementsAre(import1, import2, import4, import5)), - Field( - &SynchronizationPackage::types, - UnorderedElementsAre( - Eq(objectType), - Eq(itemType), - AllOf(IsStorageType("First.qml", - Storage::Synchronization::ImportedType{"Object"}, - TypeTraits::Reference, - qmlDocumentSourceId1, - Storage::Synchronization::ChangeLevel::ExcludeExportedTypes), - Field(&Storage::Synchronization::Type::exportedTypes, IsEmpty())), - AllOf(IsStorageType("First2.qml", - Storage::Synchronization::ImportedType{"Object2"}, - TypeTraits::Reference, - qmlDocumentSourceId2, - Storage::Synchronization::ChangeLevel::ExcludeExportedTypes), - Field(&Storage::Synchronization::Type::exportedTypes, IsEmpty())))), + Field(&SynchronizationPackage::types, + UnorderedElementsAre( + Eq(objectType), + Eq(itemType), + AllOf(IsStorageType("First.qml", + Storage::Synchronization::ImportedType{"Object"}, + TypeTraitsKind::Reference, + qmlDocumentSourceId1, + Storage::Synchronization::ChangeLevel::ExcludeExportedTypes), + Field(&Storage::Synchronization::Type::exportedTypes, IsEmpty())), + AllOf(IsStorageType("First2.qml", + Storage::Synchronization::ImportedType{"Object2"}, + TypeTraitsKind::Reference, + qmlDocumentSourceId2, + Storage::Synchronization::ChangeLevel::ExcludeExportedTypes), + Field(&Storage::Synchronization::Type::exportedTypes, IsEmpty())))), Field(&SynchronizationPackage::updatedSourceIds, UnorderedElementsAre(qmlDocumentSourceId1, qmlDocumentSourceId2, @@ -3346,13 +3364,13 @@ TEST_F(ProjectStorageUpdater, input_is_reused_next_call_if_an_error_happens_and_ Eq(itemType), AllOf(IsStorageType("First.qml", Storage::Synchronization::ImportedType{"Object"}, - TypeTraits::Reference, + TypeTraitsKind::Reference, qmlDocumentSourceId1, Storage::Synchronization::ChangeLevel::ExcludeExportedTypes), Field(&Storage::Synchronization::Type::exportedTypes, IsEmpty())), AllOf(IsStorageType("First2.qml", Storage::Synchronization::ImportedType{"Object2"}, - TypeTraits::Reference, + TypeTraitsKind::Reference, qmlDocumentSourceId2, Storage::Synchronization::ChangeLevel::ExcludeExportedTypes), Field(&Storage::Synchronization::Type::exportedTypes, IsEmpty())))), @@ -3401,13 +3419,13 @@ TEST_F(ProjectStorageUpdater, input_is_cleared_after_successful_update) UnorderedElementsAre( AllOf(IsStorageType("First.qml", Storage::Synchronization::ImportedType{"Object"}, - TypeTraits::Reference, + TypeTraitsKind::Reference, qmlDocumentSourceId1, Storage::Synchronization::ChangeLevel::ExcludeExportedTypes), Field(&Storage::Synchronization::Type::exportedTypes, IsEmpty())), AllOf(IsStorageType("First2.qml", Storage::Synchronization::ImportedType{"Object2"}, - TypeTraits::Reference, + TypeTraitsKind::Reference, qmlDocumentSourceId2, Storage::Synchronization::ChangeLevel::ExcludeExportedTypes), Field(&Storage::Synchronization::Type::exportedTypes, IsEmpty())))), diff --git a/tests/unit/tests/unittests/projectstorage/qmltypesparser-test.cpp b/tests/unit/tests/unittests/projectstorage/qmltypesparser-test.cpp index ba4b4865b86..78f8f41d6c4 100644 --- a/tests/unit/tests/unittests/projectstorage/qmltypesparser-test.cpp +++ b/tests/unit/tests/unittests/projectstorage/qmltypesparser-test.cpp @@ -58,6 +58,15 @@ MATCHER_P(HasFlag, flag, std::string(negation ? "hasn't " : "has ") + PrintToStr return bool(arg & flag); } +MATCHER_P(UsesCustomParser, + value, + std::string(negation ? "don't used custom parser " : "uses custom parser")) +{ + const Storage::TypeTraits &traits = arg; + + return traits.usesCustomParser == value; +} + template auto IsTypeTrait(const Matcher &matcher) { @@ -215,12 +224,12 @@ TEST_F(QmlTypesParser, types) UnorderedElementsAre(IsType("QObject", Synchronization::ImportedType{}, Synchronization::ImportedType{}, - Storage::TypeTraits::Reference, + Storage::TypeTraitsKind::Reference, qmltypesFileSourceId), IsType("QQmlComponent", Synchronization::ImportedType{}, Synchronization::ImportedType{}, - Storage::TypeTraits::Reference, + Storage::TypeTraitsKind::Reference, qmltypesFileSourceId))); } @@ -238,12 +247,12 @@ TEST_F(QmlTypesParser, prototype) UnorderedElementsAre(IsType("QObject", Synchronization::ImportedType{}, Synchronization::ImportedType{}, - Storage::TypeTraits::Reference, + Storage::TypeTraitsKind::Reference, qmltypesFileSourceId), IsType("QQmlComponent", Synchronization::ImportedType{"QObject"}, Synchronization::ImportedType{}, - Storage::TypeTraits::Reference, + Storage::TypeTraitsKind::Reference, qmltypesFileSourceId))); } @@ -261,12 +270,12 @@ TEST_F(QmlTypesParser, extension) UnorderedElementsAre(IsType("QObject", Synchronization::ImportedType{}, Synchronization::ImportedType{}, - Storage::TypeTraits::Reference, + Storage::TypeTraitsKind::Reference, qmltypesFileSourceId), IsType("QQmlComponent", Synchronization::ImportedType{}, Synchronization::ImportedType{"QObject"}, - Storage::TypeTraits::Reference, + Storage::TypeTraitsKind::Reference, qmltypesFileSourceId))); } @@ -588,6 +597,8 @@ TEST_F(QmlTypesParser, enumeration_is_exported_as_type) }})"}; parser.parse(source, imports, types, projectData); + QmlDesigner::Storage::TypeTraits traits{QmlDesigner::Storage::TypeTraitsKind::Value}; + traits.isEnum = true; ASSERT_THAT( types, @@ -595,7 +606,7 @@ TEST_F(QmlTypesParser, enumeration_is_exported_as_type) AllOf(IsType("QObject::NamedColorSpace", Synchronization::ImportedType{}, Synchronization::ImportedType{}, - Storage::TypeTraits::Value | Storage::TypeTraits::IsEnum, + traits, qmltypesFileSourceId), Field(&Synchronization::Type::exportedTypes, UnorderedElementsAre(IsExportedType(qtQmlNativeModuleId, @@ -604,7 +615,7 @@ TEST_F(QmlTypesParser, enumeration_is_exported_as_type) AllOf(IsType("QObject::VerticalLayoutDirection", Synchronization::ImportedType{}, Synchronization::ImportedType{}, - Storage::TypeTraits::Value | Storage::TypeTraits::IsEnum, + traits, qmltypesFileSourceId), Field(&Synchronization::Type::exportedTypes, UnorderedElementsAre(IsExportedType(qtQmlNativeModuleId, @@ -632,13 +643,15 @@ TEST_F(QmlTypesParser, enumeration_is_exported_as_type_with_alias) }})"}; parser.parse(source, imports, types, projectData); + QmlDesigner::Storage::TypeTraits traits{QmlDesigner::Storage::TypeTraitsKind::Value}; + traits.isEnum = true; ASSERT_THAT(types, UnorderedElementsAre( AllOf(IsType("QObject::NamedColorSpaces", Synchronization::ImportedType{}, Synchronization::ImportedType{}, - Storage::TypeTraits::Value | Storage::TypeTraits::IsEnum, + traits, qmltypesFileSourceId), Field(&Synchronization::Type::exportedTypes, UnorderedElementsAre(IsExportedType(qtQmlNativeModuleId, @@ -678,13 +691,15 @@ TEST_F(QmlTypesParser, enumeration_is_exported_as_type_with_alias_too) }})"}; parser.parse(source, imports, types, projectData); + QmlDesigner::Storage::TypeTraits traits{QmlDesigner::Storage::TypeTraitsKind::Value}; + traits.isEnum = true; ASSERT_THAT(types, UnorderedElementsAre( AllOf(IsType("QObject::NamedColorSpaces", Synchronization::ImportedType{}, Synchronization::ImportedType{}, - Storage::TypeTraits::Value | Storage::TypeTraits::IsEnum, + traits, qmltypesFileSourceId), Field(&Synchronization::Type::exportedTypes, UnorderedElementsAre(IsExportedType(qtQmlNativeModuleId, @@ -760,7 +775,7 @@ TEST_F(QmlTypesParser, access_type_is_reference) parser.parse(source, imports, types, projectData); - ASSERT_THAT(types, ElementsAre(IsTypeTrait(Storage::TypeTraits::Reference))); + ASSERT_THAT(types, ElementsAre(IsTypeTrait(Storage::TypeTraitsKind::Reference))); } TEST_F(QmlTypesParser, access_type_is_value) @@ -772,7 +787,7 @@ TEST_F(QmlTypesParser, access_type_is_value) parser.parse(source, imports, types, projectData); - ASSERT_THAT(types, ElementsAre(IsTypeTrait(Storage::TypeTraits::Value))); + ASSERT_THAT(types, ElementsAre(IsTypeTrait(Storage::TypeTraitsKind::Value))); } TEST_F(QmlTypesParser, access_type_is_sequence) @@ -784,7 +799,7 @@ TEST_F(QmlTypesParser, access_type_is_sequence) parser.parse(source, imports, types, projectData); - ASSERT_THAT(types, ElementsAre(IsTypeTrait(Storage::TypeTraits::Sequence))); + ASSERT_THAT(types, ElementsAre(IsTypeTrait(Storage::TypeTraitsKind::Sequence))); } TEST_F(QmlTypesParser, access_type_is_none) @@ -796,7 +811,7 @@ TEST_F(QmlTypesParser, access_type_is_none) parser.parse(source, imports, types, projectData); - ASSERT_THAT(types, ElementsAre(IsTypeTrait(Storage::TypeTraits::None))); + ASSERT_THAT(types, ElementsAre(IsTypeTrait(Storage::TypeTraitsKind::None))); } TEST_F(QmlTypesParser, uses_custom_parser) @@ -808,7 +823,7 @@ TEST_F(QmlTypesParser, uses_custom_parser) parser.parse(source, imports, types, projectData); - ASSERT_THAT(types, ElementsAre(IsTypeTrait(HasFlag(Storage::TypeTraits::UsesCustomParser)))); + ASSERT_THAT(types, ElementsAre(IsTypeTrait(UsesCustomParser(true)))); } TEST_F(QmlTypesParser, uses_no_custom_parser) @@ -820,7 +835,7 @@ TEST_F(QmlTypesParser, uses_no_custom_parser) parser.parse(source, imports, types, projectData); - ASSERT_THAT(types, ElementsAre(IsTypeTrait(Not(HasFlag(Storage::TypeTraits::UsesCustomParser))))); + ASSERT_THAT(types, ElementsAre(IsTypeTrait(UsesCustomParser(false)))); } } // namespace diff --git a/tests/unit/tests/unittests/projectstorage/typeannotationreader-test.cpp b/tests/unit/tests/unittests/projectstorage/typeannotationreader-test.cpp new file mode 100644 index 00000000000..ed5c1a0778b --- /dev/null +++ b/tests/unit/tests/unittests/projectstorage/typeannotationreader-test.cpp @@ -0,0 +1,758 @@ +// 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 "../utils/googletest.h" + +#include + +#include +#include +#include + +namespace { + +template +auto IsTypeAnnotation(QmlDesigner::SourceId sourceId, + Utils::SmallStringView typeName, + QmlDesigner::ModuleId moduleId, + Utils::SmallStringView iconPath, + QmlDesigner::Storage::TypeTraits traits, + HintsJsonMatcher hintsJsonMatcher, + ItemLibraryJsonMatcher itemLibraryJsonMatcher) +{ + using QmlDesigner::Storage::Synchronization::TypeAnnotation; + return AllOf(Field("sourceId", &TypeAnnotation::sourceId, sourceId), + Field("typeName", &TypeAnnotation::typeName, typeName), + Field("moduleId", &TypeAnnotation::moduleId, moduleId), + Field("iconPath", &TypeAnnotation::iconPath, iconPath), + Field("traits", &TypeAnnotation::traits, traits), + Field("hintsJson", &TypeAnnotation::hintsJson, hintsJsonMatcher), + Field("itemLibraryJson", &TypeAnnotation::itemLibraryJson, itemLibraryJsonMatcher)); +} + +class TypeAnnotationReader : public testing::Test +{ +protected: + static void SetUpTestSuite() + { + static_database = std::make_unique(":memory:", Sqlite::JournalMode::Memory); + + static_projectStorage = std::make_unique>( + *static_database, static_database->isInitialized()); + } + + static void TearDownTestSuite() + { + static_projectStorage.reset(); + static_database.reset(); + } + + auto moduleId(Utils::SmallStringView name) const { return storage.moduleId(name); } + +protected: + inline static std::unique_ptr static_database; + Sqlite::Database &database = *static_database; + inline static std::unique_ptr> static_projectStorage; + QmlDesigner::ProjectStorage &storage = *static_projectStorage; + QmlDesigner::Storage::TypeAnnotationReader reader{storage}; + QmlDesigner::SourceId sourceId = QmlDesigner::SourceId::create(33); +}; + +TEST_F(TypeAnnotationReader, parse_type) +{ + auto content = QString{R"xy( + MetaInfo { + Type { + name: "QtQuick.Controls.Frame" + icon: "images/frame-icon16.png" + } + Type { + name: "QtQuick.Item" + icon: "images/item-icon16.png" + } + })xy"}; + QmlDesigner::Storage::TypeTraits traits; + + auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId); + + ASSERT_THAT(annotations, + UnorderedElementsAre(IsTypeAnnotation(sourceId, + "Frame", + moduleId("QtQuick.Controls"), + "/path/images/frame-icon16.png", + traits, + IsEmpty(), + IsEmpty()), + IsTypeAnnotation(sourceId, + "Item", + moduleId("QtQuick"), + "/path/images/item-icon16.png", + traits, + IsEmpty(), + IsEmpty()))); +} + +TEST_F(TypeAnnotationReader, parse_true_canBeContainer) +{ + using QmlDesigner::FlagIs; + auto content = QString{R"xy( + MetaInfo { + Type { + name: "QtQuick.Controls.Frame" + icon: "images/frame-icon16.png" + + Hints { + canBeContainer: true + } + } + })xy"}; + QmlDesigner::Storage::TypeTraits traits; + traits.canBeContainer = FlagIs::True; + + auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId); + + ASSERT_THAT(annotations, + ElementsAre(IsTypeAnnotation(sourceId, + "Frame", + moduleId("QtQuick.Controls"), + "/path/images/frame-icon16.png", + traits, + IsEmpty(), + IsEmpty()))); +} + +TEST_F(TypeAnnotationReader, parse_true_forceClip) +{ + using QmlDesigner::FlagIs; + auto content = QString{R"xy( + MetaInfo { + Type { + name: "QtQuick.Controls.Frame" + icon: "images/frame-icon16.png" + + Hints { + forceClip: true + } + } + })xy"}; + QmlDesigner::Storage::TypeTraits traits; + traits.forceClip = FlagIs::True; + + auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId); + + ASSERT_THAT(annotations, + ElementsAre(IsTypeAnnotation(sourceId, + "Frame", + moduleId("QtQuick.Controls"), + "/path/images/frame-icon16.png", + traits, + IsEmpty(), + IsEmpty()))); +} + +TEST_F(TypeAnnotationReader, parse_true_doesLayoutChildren) +{ + using QmlDesigner::FlagIs; + auto content = QString{R"xy( + MetaInfo { + Type { + name: "QtQuick.Controls.Frame" + icon: "images/frame-icon16.png" + + Hints { + doesLayoutChildren: true + } + } + })xy"}; + QmlDesigner::Storage::TypeTraits traits; + traits.doesLayoutChildren = FlagIs::True; + + auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId); + + ASSERT_THAT(annotations, + ElementsAre(IsTypeAnnotation(sourceId, + "Frame", + moduleId("QtQuick.Controls"), + "/path/images/frame-icon16.png", + traits, + IsEmpty(), + IsEmpty()))); +} + +TEST_F(TypeAnnotationReader, parse_true_canBeDroppedInFormEditor) +{ + using QmlDesigner::FlagIs; + auto content = QString{R"xy( + MetaInfo { + Type { + name: "QtQuick.Controls.Frame" + icon: "images/frame-icon16.png" + + Hints { + canBeDroppedInFormEditor: true + } + } + })xy"}; + QmlDesigner::Storage::TypeTraits traits; + traits.canBeDroppedInFormEditor = FlagIs::True; + + auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId); + + ASSERT_THAT(annotations, + ElementsAre(IsTypeAnnotation(sourceId, + "Frame", + moduleId("QtQuick.Controls"), + "/path/images/frame-icon16.png", + traits, + IsEmpty(), + IsEmpty()))); +} + +TEST_F(TypeAnnotationReader, parse_true_canBeDroppedInNavigator) +{ + using QmlDesigner::FlagIs; + auto content = QString{R"xy( + MetaInfo { + Type { + name: "QtQuick.Controls.Frame" + icon: "images/frame-icon16.png" + + Hints { + canBeDroppedInNavigator: true + } + } + })xy"}; + QmlDesigner::Storage::TypeTraits traits; + traits.canBeDroppedInNavigator = FlagIs::True; + + auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId); + + ASSERT_THAT(annotations, + ElementsAre(IsTypeAnnotation(sourceId, + "Frame", + moduleId("QtQuick.Controls"), + "/path/images/frame-icon16.png", + traits, + IsEmpty(), + IsEmpty()))); +} + +TEST_F(TypeAnnotationReader, parse_true_canBeDroppedInView3D) +{ + using QmlDesigner::FlagIs; + auto content = QString{R"xy( + MetaInfo { + Type { + name: "QtQuick.Controls.Frame" + icon: "images/frame-icon16.png" + + Hints { + canBeDroppedInView3D: true + } + } + })xy"}; + QmlDesigner::Storage::TypeTraits traits; + traits.canBeDroppedInView3D = FlagIs::True; + + auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId); + + ASSERT_THAT(annotations, + ElementsAre(IsTypeAnnotation(sourceId, + "Frame", + moduleId("QtQuick.Controls"), + "/path/images/frame-icon16.png", + traits, + IsEmpty(), + IsEmpty()))); +} + +TEST_F(TypeAnnotationReader, parse_true_isMovable) +{ + using QmlDesigner::FlagIs; + auto content = QString{R"xy( + MetaInfo { + Type { + name: "QtQuick.Controls.Frame" + icon: "images/frame-icon16.png" + + Hints { + isMovable: true + } + } + })xy"}; + QmlDesigner::Storage::TypeTraits traits; + traits.isMovable = FlagIs::True; + + auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId); + + ASSERT_THAT(annotations, + ElementsAre(IsTypeAnnotation(sourceId, + "Frame", + moduleId("QtQuick.Controls"), + "/path/images/frame-icon16.png", + traits, + IsEmpty(), + IsEmpty()))); +} + +TEST_F(TypeAnnotationReader, parse_true_isResizable) +{ + using QmlDesigner::FlagIs; + auto content = QString{R"xy( + MetaInfo { + Type { + name: "QtQuick.Controls.Frame" + icon: "images/frame-icon16.png" + + Hints { + isResizable: true + } + } + })xy"}; + QmlDesigner::Storage::TypeTraits traits; + traits.isResizable = FlagIs::True; + + auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId); + + ASSERT_THAT(annotations, + ElementsAre(IsTypeAnnotation(sourceId, + "Frame", + moduleId("QtQuick.Controls"), + "/path/images/frame-icon16.png", + traits, + IsEmpty(), + IsEmpty()))); +} + +TEST_F(TypeAnnotationReader, parse_true_hasFormEditorItem) +{ + using QmlDesigner::FlagIs; + auto content = QString{R"xy( + MetaInfo { + Type { + name: "QtQuick.Controls.Frame" + icon: "images/frame-icon16.png" + + Hints { + hasFormEditorItem: true + } + } + })xy"}; + QmlDesigner::Storage::TypeTraits traits; + traits.hasFormEditorItem = FlagIs::True; + + auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId); + + ASSERT_THAT(annotations, + ElementsAre(IsTypeAnnotation(sourceId, + "Frame", + moduleId("QtQuick.Controls"), + "/path/images/frame-icon16.png", + traits, + IsEmpty(), + IsEmpty()))); +} + +TEST_F(TypeAnnotationReader, parse_true_isStackedContainer) +{ + using QmlDesigner::FlagIs; + auto content = QString{R"xy( + MetaInfo { + Type { + name: "QtQuick.Controls.Frame" + icon: "images/frame-icon16.png" + + Hints { + isStackedContainer: true + } + } + })xy"}; + QmlDesigner::Storage::TypeTraits traits; + traits.isStackedContainer = FlagIs::True; + + auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId); + + ASSERT_THAT(annotations, + ElementsAre(IsTypeAnnotation(sourceId, + "Frame", + moduleId("QtQuick.Controls"), + "/path/images/frame-icon16.png", + traits, + IsEmpty(), + IsEmpty()))); +} + +TEST_F(TypeAnnotationReader, parse_true_takesOverRenderingOfChildren) +{ + using QmlDesigner::FlagIs; + auto content = QString{R"xy( + MetaInfo { + Type { + name: "QtQuick.Controls.Frame" + icon: "images/frame-icon16.png" + + Hints { + takesOverRenderingOfChildren: true + } + } + })xy"}; + QmlDesigner::Storage::TypeTraits traits; + traits.takesOverRenderingOfChildren = FlagIs::True; + + auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId); + + ASSERT_THAT(annotations, + ElementsAre(IsTypeAnnotation(sourceId, + "Frame", + moduleId("QtQuick.Controls"), + "/path/images/frame-icon16.png", + traits, + IsEmpty(), + IsEmpty()))); +} + +TEST_F(TypeAnnotationReader, parse_true_visibleInNavigator) +{ + using QmlDesigner::FlagIs; + auto content = QString{R"xy( + MetaInfo { + Type { + name: "QtQuick.Controls.Frame" + icon: "images/frame-icon16.png" + + Hints { + visibleInNavigator: true + } + } + })xy"}; + QmlDesigner::Storage::TypeTraits traits; + traits.visibleInNavigator = FlagIs::True; + + auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId); + + ASSERT_THAT(annotations, + ElementsAre(IsTypeAnnotation(sourceId, + "Frame", + moduleId("QtQuick.Controls"), + "/path/images/frame-icon16.png", + traits, + IsEmpty(), + IsEmpty()))); +} + +TEST_F(TypeAnnotationReader, parse_true_visibleInLibrary) +{ + using QmlDesigner::FlagIs; + auto content = QString{R"xy( + MetaInfo { + Type { + name: "QtQuick.Controls.Frame" + icon: "images/frame-icon16.png" + + Hints { + visibleInLibrary: true + } + } + })xy"}; + QmlDesigner::Storage::TypeTraits traits; + traits.visibleInLibrary = FlagIs::True; + + auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId); + + ASSERT_THAT(annotations, + ElementsAre(IsTypeAnnotation(sourceId, + "Frame", + moduleId("QtQuick.Controls"), + "/path/images/frame-icon16.png", + traits, + IsEmpty(), + IsEmpty()))); +} + +TEST_F(TypeAnnotationReader, parse_false) +{ + using QmlDesigner::FlagIs; + auto content = QString{R"xy( + MetaInfo { + Type { + name: "QtQuick.Controls.Frame" + icon: "images/frame-icon16.png" + + Hints { + isMovable: false + } + } + })xy"}; + QmlDesigner::Storage::TypeTraits traits; + + auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId); + + ASSERT_THAT(annotations, + ElementsAre(IsTypeAnnotation(sourceId, + "Frame", + moduleId("QtQuick.Controls"), + "/path/images/frame-icon16.png", + traits, + IsEmpty(), + IsEmpty()))); +} + +TEST_F(TypeAnnotationReader, parse_complex_expression) +{ + using QmlDesigner::FlagIs; + auto content = QString{R"xy( + MetaInfo { + Type { + name: "QtQuick.Controls.Frame" + icon: "images/frame-icon16.png" + + Hints { + isMovable: true || false + visibleNonDefaultProperties: "layer.effect" + } + } + + Type { + name: "QtQuick.Item" + icon: "images/item-icon16.png" + + Hints { + canBeContainer: true + } + } + })xy"}; + QmlDesigner::Storage::TypeTraits frameTraits; + frameTraits.isMovable = QmlDesigner::FlagIs::Set; + QmlDesigner::Storage::TypeTraits itemTraits; + itemTraits.canBeContainer = QmlDesigner::FlagIs::True; + + auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId); + + ASSERT_THAT(annotations, + UnorderedElementsAre(IsTypeAnnotation(sourceId, + "Frame", + moduleId("QtQuick.Controls"), + "/path/images/frame-icon16.png", + frameTraits, + StrippedStringEq(R"xy({"isMovable":"true || false", + "visibleNonDefaultProperties":"layer.effect"})xy"), + IsEmpty()), + IsTypeAnnotation(sourceId, + "Item", + moduleId("QtQuick"), + "/path/images/item-icon16.png", + itemTraits, + IsEmpty(), + IsEmpty()))); +} + +TEST_F(TypeAnnotationReader, parse_item_library_entry) +{ + auto content = QString{R"xy( + MetaInfo { + Type { + name: "QtQuick.Controls.Frame" + icon: "images/frame-icon16.png" + + ItemLibraryEntry { + name: "Frame" + category: "Qt Quick - Controls 2" + libraryIcon: "images/frame-icon.png" + requiredImport: "QtQuick.Controls" + toolTip: qsTr("An untitled container for a group of controls.") + } + + ItemLibraryEntry { + name: "Large Frame" + category: "Qt Quick - Controls 2" + libraryIcon: "images/frame-icon.png" + requiredImport: "QtQuick.Controls" + toolTip: qsTr("An large container for a group of controls.") + } + } + })xy"}; + QmlDesigner::Storage::TypeTraits traits; + + auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId); + + ASSERT_THAT(annotations, + ElementsAre(IsTypeAnnotation(sourceId, + "Frame", + moduleId("QtQuick.Controls"), + "/path/images/frame-icon16.png", + traits, + IsEmpty(), + StrippedStringEq(R"xy([ + {"category":"Qt Quick - Controls 2", + "iconPath":"images/frame-icon.png", + "import":"QtQuick.Controls", + "name":"Frame", + "toolTip":"qsTr(\"An untitled container for a group of controls.\")"}, + {"category":"Qt Quick - Controls 2", + "iconPath":"images/frame-icon.png", + "import":"QtQuick.Controls", + "name":"Large Frame", + "toolTip":"qsTr(\"An large container for a group of controls.\")"}] + )xy")))); +} + +TEST_F(TypeAnnotationReader, parse_item_library_entry_with_properties) +{ + auto content = QString{R"xy( + MetaInfo { + Type { + name: "QtQuick.Controls.Frame" + icon: "images/frame-icon16.png" + + ItemLibraryEntry { + name: "Frame" + category: "Qt Quick - Controls 2" + libraryIcon: "images/frame-icon.png" + requiredImport: "QtQuick.Controls" + toolTip: qsTr("An untitled container for a group of controls.") + + Property { name: "width"; type: "int"; value: 200 } + Property { name: "height"; type: "int"; value: 100 } + } + + ItemLibraryEntry { + name: "Large Frame" + category: "Qt Quick - Controls 2" + libraryIcon: "images/frame-icon.png" + requiredImport: "QtQuick.Controls" + toolTip: qsTr("An large container for a group of controls.") + + Property { name: "width"; type: "int"; value: 2000 } + Property { name: "height"; type: "int"; value: 1000 } + } + } + })xy"}; + QmlDesigner::Storage::TypeTraits traits; + + auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId); + + ASSERT_THAT(annotations, + ElementsAre(IsTypeAnnotation(sourceId, + "Frame", + moduleId("QtQuick.Controls"), + "/path/images/frame-icon16.png", + traits, + IsEmpty(), + StrippedStringEq(R"xy([ + {"category":"Qt Quick - Controls 2", + "iconPath":"images/frame-icon.png", + "import":"QtQuick.Controls", + "name":"Frame", + "properties":[["width","int",200.0],["height","int",100.0]], + "toolTip":"qsTr(\"An untitled container for a group of controls.\")"}, + {"category":"Qt Quick - Controls 2", + "iconPath":"images/frame-icon.png", + "import":"QtQuick.Controls", + "name":"Large Frame", + "properties":[["width","int",2000.0],["height","int",1000.0]], + "toolTip":"qsTr(\"An large container for a group of controls.\")"}] + )xy")))); +} + +TEST_F(TypeAnnotationReader, parse_item_library_entry_template_path) +{ + auto content = QString{R"xy( + MetaInfo { + Type { + name: "QtQuick.Controls.Frame" + + ItemLibraryEntry { + name: "Frame" + + QmlSource{ source: "templates/frame.qml" } + } + } + Type { + name: "QtQuick.Item" + + ItemLibraryEntry { + name: "Item" + + QmlSource{ source: "templates/item.qml" } + } + } + })xy"}; + QmlDesigner::Storage::TypeTraits traits; + + auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId); + + ASSERT_THAT(annotations, + ElementsAre(IsTypeAnnotation(sourceId, + "Frame", + moduleId("QtQuick.Controls"), + {}, + traits, + IsEmpty(), + StrippedStringEq(R"xy([ + {"name":"Frame", + "templatePath":"/path/templates/frame.qml"}] + )xy")), + IsTypeAnnotation(sourceId, + "Item", + moduleId("QtQuick"), + {}, + traits, + IsEmpty(), + StrippedStringEq(R"xy([ + {"name":"Item", + "templatePath":"/path/templates/item.qml"}] + )xy")))); +} + +TEST_F(TypeAnnotationReader, parse_item_library_entry_extra_file_paths) +{ + auto content = QString{R"xy( + MetaInfo { + Type { + name: "QtQuick.Controls.Frame" + + ItemLibraryEntry { + name: "Frame" + + ExtraFile{ source: "templates/frame.png" } + ExtraFile{ source: "templates/frame.frag" } + } + } + Type { + name: "QtQuick.Item" + + ItemLibraryEntry { + name: "Item" + + ExtraFile{ source: "templates/item.png" } + } + } + })xy"}; + QmlDesigner::Storage::TypeTraits traits; + + auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId); + + ASSERT_THAT(annotations, + ElementsAre(IsTypeAnnotation(sourceId, + "Frame", + moduleId("QtQuick.Controls"), + {}, + traits, + IsEmpty(), + StrippedStringEq(R"xy([ + {"extraFilePaths":["/path/templates/frame.png", "/path/templates/frame.frag"], + "name":"Frame"}] + )xy")), + IsTypeAnnotation(sourceId, + "Item", + moduleId("QtQuick"), + {}, + traits, + IsEmpty(), + StrippedStringEq(R"xy([ + {"extraFilePaths":["/path/templates/item.png"], + "name":"Item"}] + )xy")))); +} + +} // namespace From 09649115291e63caa8f2812e20e8edf687a0e696 Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Wed, 11 Oct 2023 11:47:42 +0300 Subject: [PATCH 030/242] QmlDesigner: Consider property type for the collection properties Task-number: QDS-10964 Change-Id: I9759b772811fa44baa4f8a548bf04cadbd0807a4 Reviewed-by: Miikka Heikkinen --- .../collectioneditor/collectiondetails.cpp | 142 ++++++++++++++---- .../collectioneditor/collectiondetails.h | 22 ++- .../singlecollectionmodel.cpp | 10 +- 3 files changed, 137 insertions(+), 37 deletions(-) diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp index e6aeaf23011..e4cdea87c9c 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp @@ -3,29 +3,61 @@ #include "collectiondetails.h" +#include + #include #include -#include +#include #include namespace QmlDesigner { +struct CollectionProperty +{ + using DataType = CollectionDetails::DataType; + + QString name; + DataType type; +}; + class CollectionDetails::Private { using SourceFormat = CollectionEditor::SourceFormat; public: - QStringList headers; + QList properties; QList elements; SourceFormat sourceFormat = SourceFormat::Unknown; CollectionReference reference; bool isChanged = false; - bool isValidColumnId(int column) const { return column > -1 && column < headers.size(); } + bool isValidColumnId(int column) const { return column > -1 && column < properties.size(); } bool isValidRowId(int row) const { return row > -1 && row < elements.size(); } }; +static CollectionProperty::DataType collectionDataTypeFromJsonValue(const QJsonValue &value) +{ + using DataType = CollectionDetails::DataType; + using JsonType = QJsonValue::Type; + + switch (value.type()) { + case JsonType::Null: + case JsonType::Undefined: + return DataType::Unknown; + case JsonType::Bool: + return DataType::Boolean; + case JsonType::Double: + return DataType::Number; + case JsonType::String: { + // TODO: Image, Color, Url + return DataType::String; + } break; + default: + return DataType::Unknown; + } +} + CollectionDetails::CollectionDetails() : d(new Private()) {} @@ -40,36 +72,44 @@ CollectionDetails::CollectionDetails(const CollectionDetails &other) = default; CollectionDetails::~CollectionDetails() = default; -void CollectionDetails::resetDetails(const QStringList &headers, +void CollectionDetails::resetDetails(const QStringList &propertyNames, const QList &elements, CollectionEditor::SourceFormat format) { if (!isValid()) return; - d->headers = headers; + d->properties = Utils::transform(propertyNames, [](const QString &name) -> CollectionProperty { + return {name, DataType::Unknown}; + }); + d->elements = elements; d->sourceFormat = format; + resetPropertyTypes(); markSaved(); } -void CollectionDetails::insertColumn(const QString &header, int colIdx, const QVariant &defaultValue) +void CollectionDetails::insertColumn(const QString &propertyName, + int colIdx, + const QVariant &defaultValue, + DataType type) { if (!isValid()) return; - if (d->headers.contains(header)) + if (containsPropertyName(propertyName)) return; + CollectionProperty property = {propertyName, type}; if (d->isValidColumnId(colIdx)) - d->headers.insert(colIdx, header); + d->properties.insert(colIdx, property); else - d->headers.append(header); + d->properties.append(property); QJsonValue defaultJsonValue = QJsonValue::fromVariant(defaultValue); for (QJsonObject &element : d->elements) - element.insert(header, defaultJsonValue); + element.insert(propertyName, defaultJsonValue); markChanged(); } @@ -79,15 +119,15 @@ bool CollectionDetails::removeColumns(int colIdx, int count) if (count < 1 || !isValid() || !d->isValidColumnId(colIdx)) return false; - int maxCount = d->headers.count() - colIdx; + int maxCount = d->properties.count() - colIdx; count = std::min(maxCount, count); - const QStringList removedHeaders = d->headers.mid(colIdx, count); - d->headers.remove(colIdx, count); + const QList removedProperties = d->properties.mid(colIdx, count); + d->properties.remove(colIdx, count); - for (const QString &header : std::as_const(removedHeaders)) { + for (const CollectionProperty &property : removedProperties) { for (QJsonObject &element : d->elements) - element.remove(header); + element.remove(property.name); } markChanged(); @@ -111,8 +151,8 @@ void CollectionDetails::insertElementAt(std::optional object, int r insertJson(object.value()); } else { QJsonObject defaultObject; - for (const QString &header : std::as_const(d->headers)) - defaultObject.insert(header, {}); + for (const CollectionProperty &property : std::as_const(d->properties)) + defaultObject.insert(property.name, {}); insertJson(defaultObject); } @@ -141,23 +181,35 @@ bool CollectionDetails::removeElements(int row, int count) int maxCount = d->elements.count() - row; count = std::min(maxCount, count); + QSet removedProperties; + Utils::span elementsSpan{std::as_const(d->elements)}; + for (const QJsonObject &element : elementsSpan.subspan(row, count)) { + const QStringList elementPropertyNames = element.keys(); + for (const QString &removedProperty : elementPropertyNames) + removedProperties.insert(removedProperty); + } + d->elements.remove(row, count); + for (const QString &removedProperty : removedProperties) + resetPropertyType(removedProperty); + markChanged(); return true; } -bool CollectionDetails::setHeader(int column, const QString &value) +bool CollectionDetails::setPropertyName(int column, const QString &value) { if (!d->isValidColumnId(column)) return false; - const QString oldColumnName = headerAt(column); + const CollectionProperty &oldProperty = d->properties.at(column); + const QString oldColumnName = oldProperty.name; if (oldColumnName == value) return false; - d->headers.replace(column, value); + d->properties.replace(column, {value, oldProperty.type}); for (QJsonObject &element : d->elements) { if (element.contains(oldColumnName)) { element.insert(value, element.value(oldColumnName)); @@ -190,7 +242,7 @@ QVariant CollectionDetails::data(int row, int column) const if (!d->isValidColumnId(column)) return {}; - const QString &propertyName = d->headers.at(column); + const QString &propertyName = d->properties.at(column).name; const QJsonObject &elementNode = d->elements.at(row); if (elementNode.contains(propertyName)) @@ -199,20 +251,30 @@ QVariant CollectionDetails::data(int row, int column) const return {}; } -QString CollectionDetails::headerAt(int column) const +QString CollectionDetails::propertyAt(int column) const { if (!d->isValidColumnId(column)) return {}; - return d->headers.at(column); + return d->properties.at(column).name; } -bool CollectionDetails::containsHeader(const QString &header) +CollectionDetails::DataType CollectionDetails::typeAt(int column) const +{ + if (!d->isValidColumnId(column)) + return {}; + + return d->properties.at(column).type; +} + +bool CollectionDetails::containsPropertyName(const QString &propertyName) { if (!isValid()) return false; - return d->headers.contains(header); + return Utils::anyOf(d->properties, [&propertyName](const CollectionProperty &property) { + return property.name == propertyName; + }); } bool CollectionDetails::isValid() const @@ -227,7 +289,7 @@ bool CollectionDetails::isChanged() const int CollectionDetails::columns() const { - return d->headers.size(); + return d->properties.size(); } int CollectionDetails::rows() const @@ -261,6 +323,34 @@ void CollectionDetails::markChanged() d->isChanged = true; } +void CollectionDetails::resetPropertyType(const QString &propertyName) +{ + for (CollectionProperty &property : d->properties) { + if (property.name == propertyName) + resetPropertyType(property); + } +} + +void CollectionDetails::resetPropertyType(CollectionProperty &property) +{ + const QString &propertyName = property.name; + DataType type = DataType::Unknown; + for (const QJsonObject &element : std::as_const(d->elements)) { + if (element.contains(propertyName)) { + type = collectionDataTypeFromJsonValue(element.value(propertyName)); + if (type != DataType::Unknown) + break; + } + } + property.type = type; +} + +void CollectionDetails::resetPropertyTypes() +{ + for (CollectionProperty &property : d->properties) + resetPropertyType(property); +} + QJsonArray CollectionDetails::getJsonCollection() const { QJsonArray collectionArray; diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.h b/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.h index 9e08adecbbd..3ff788eaa7f 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.h +++ b/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.h @@ -33,31 +33,39 @@ struct CollectionReference bool operator!=(const CollectionReference &other) const { return !(*this == other); } }; +struct CollectionProperty; + class CollectionDetails { public: + enum class DataType { Unknown, String, Url, Number, Boolean, Image, Color }; + explicit CollectionDetails(); CollectionDetails(const CollectionReference &reference); CollectionDetails(const CollectionDetails &other); ~CollectionDetails(); - void resetDetails(const QStringList &headers, + void resetDetails(const QStringList &propertyNames, const QList &elements, CollectionEditor::SourceFormat format); - void insertColumn(const QString &header, int colIdx = -1, const QVariant &defaultValue = {}); + void insertColumn(const QString &propertyName, + int colIdx = -1, + const QVariant &defaultValue = {}, + DataType type = DataType::Unknown); bool removeColumns(int colIdx, int count = 1); void insertElementAt(std::optional object, int row = -1); void insertEmptyElements(int row = 0, int count = 1); bool removeElements(int row, int count = 1); - bool setHeader(int column, const QString &value); + bool setPropertyName(int column, const QString &value); CollectionReference reference() const; CollectionEditor::SourceFormat sourceFormat() const; QVariant data(int row, int column) const; - QString headerAt(int column) const; - bool containsHeader(const QString &header); + QString propertyAt(int column) const; + DataType typeAt(int column) const; + bool containsPropertyName(const QString &propertyName); bool isValid() const; bool isChanged() const; @@ -75,10 +83,12 @@ public: private: void markChanged(); + void resetPropertyType(const QString &propertyName); + void resetPropertyType(CollectionProperty &property); + void resetPropertyTypes(); // 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/singlecollectionmodel.cpp b/src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.cpp index 989027d9107..bd025b3645e 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.cpp @@ -86,7 +86,7 @@ bool SingleCollectionModel::setHeaderData(int section, if (orientation == Qt::Vertical) return false; - bool headerChanged = m_currentCollection.setHeader(section, value.toString()); + bool headerChanged = m_currentCollection.setPropertyName(section, value.toString()); if (headerChanged) emit this->headerDataChanged(orientation, section, section); @@ -145,7 +145,7 @@ QVariant SingleCollectionModel::headerData(int section, [[maybe_unused]] int role) const { if (orientation == Qt::Horizontal) - return m_currentCollection.headerAt(section); + return m_currentCollection.propertyAt(section); if (orientation == Qt::Vertical) return section + 1; @@ -165,12 +165,12 @@ int SingleCollectionModel::selectedRow() const bool SingleCollectionModel::isPropertyAvailable(const QString &name) { - return m_currentCollection.containsHeader(name); + return m_currentCollection.containsPropertyName(name); } bool SingleCollectionModel::addColumn(int column, const QString &name) { - if (m_currentCollection.containsHeader(name)) + if (m_currentCollection.containsPropertyName(name)) return false; if (column < 0 || column > columnCount()) @@ -179,7 +179,7 @@ bool SingleCollectionModel::addColumn(int column, const QString &name) beginInsertColumns({}, column, column); m_currentCollection.insertColumn(name, column); endInsertColumns(); - return m_currentCollection.containsHeader(name); + return m_currentCollection.containsPropertyName(name); } bool SingleCollectionModel::selectColumn(int section) From 9f6e6131037f6838a1d6bdc970aa0ef1b159db3a Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Thu, 12 Oct 2023 13:11:05 +0300 Subject: [PATCH 031/242] QmlDesigner: Fix the build error for the property name Change-Id: Ie9991a3f58d747f2ade2162b215f4236e0b3f639 Reviewed-by: Mahmoud Badri --- .../components/collectioneditor/collectiondetails.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp index e4cdea87c9c..8e2284d925a 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp @@ -363,17 +363,17 @@ QJsonArray CollectionDetails::getJsonCollection() const QString CollectionDetails::getCsvCollection() const { QString content; - if (d->headers.count() <= 0) + if (d->properties.count() <= 0) return ""; - for (const QString &header : std::as_const(d->headers)) - content += header + ','; + for (const CollectionProperty &property : std::as_const(d->properties)) + content += property.name + ','; content.back() = '\n'; for (const QJsonObject &elementsRow : std::as_const(d->elements)) { - for (const QString &header : std::as_const(d->headers)) { - const QJsonValue &value = elementsRow.value(header); + for (const CollectionProperty &property : std::as_const(d->properties)) { + const QJsonValue &value = elementsRow.value(property.name); if (value.isDouble()) content += QString::number(value.toDouble()) + ','; @@ -385,4 +385,5 @@ QString CollectionDetails::getCsvCollection() const return content; } + } // namespace QmlDesigner From 672fc4d2186f224978fa0afc3fef3c3f60b53e5e Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Sun, 1 Oct 2023 00:45:46 +0200 Subject: [PATCH 032/242] Nanotrace: Add high resolution low overhead tracer I want to use the trace in the project storage but I like reduce the overhead. If the constexpr activateTracer is set there is even not overhead at all. Uage would be: foo.h: extern thread_local EventQueue fooEventQueue; extern thread_local Category fooCategory; void foo() { Nanotrace::Tracer t{"Foo", fooCategory}; Nanotrace::Tracer t{"Foo", "fooCategory", fooEventQueue}; Nanotrace::Tracer t{"Foo", "fooCategory", R"xy("First":"Argument")xy", fooEventQueue}; } or you can use the GlobalTracer: void fooWithGlobal() { Nanotrace::GlobalTracer t{"Foo", "Category"}; Nanotrace::GlobalTracer t{"Foo", "Category", R"xy("First":"Argument")xy"}; } foo.cpp: namespace { Nanotrace::TraceFile fooTraceFile{"foo.json"}; thread_local auto fooEventQueueData = Nanotrace::makeEventQueueData<10000>("Foo", fooTraceFile); } // namespace thread_local EventQueue fooEventQueue = fooEventQueueData; thread_local EventQueue fooCategory{"foo"_t, fooEventQueue}; If nano trace is deactivated fooEventQueueData would be a null pointer, fooEventQueue would be disabled and the trace would generate no code. Change-Id: I1cad8570536806c462a61276eb142b8aa4932529 Reviewed-by: Reviewed-by: Eike Ziller Reviewed-by: Tim Jenssen Reviewed-by: Qt CI Patch Build Bot --- src/libs/nanotrace/CMakeLists.txt | 5 +- src/libs/nanotrace/nanotrace.h | 7 +- src/libs/nanotrace/nanotraceglobals.h | 16 ++ src/libs/nanotrace/nanotracehr.cpp | 111 ++++++++ src/libs/nanotrace/nanotracehr.h | 373 ++++++++++++++++++++++++++ 5 files changed, 505 insertions(+), 7 deletions(-) create mode 100644 src/libs/nanotrace/nanotraceglobals.h create mode 100644 src/libs/nanotrace/nanotracehr.cpp create mode 100644 src/libs/nanotrace/nanotracehr.h diff --git a/src/libs/nanotrace/CMakeLists.txt b/src/libs/nanotrace/CMakeLists.txt index 57f11af9970..fdfb137b683 100644 --- a/src/libs/nanotrace/CMakeLists.txt +++ b/src/libs/nanotrace/CMakeLists.txt @@ -2,7 +2,10 @@ add_qtc_library(Nanotrace BUILD_DEFAULT OFF DEFINES NANOTRACE_LIBRARY PUBLIC_DEFINES NANOTRACE_ENABLED - SOURCES nanotrace.cpp nanotrace.h + SOURCES + nanotraceglobals.h + nanotrace.cpp nanotrace.h + nanotracehr.cpp nanotracehr.h PUBLIC_DEPENDS Qt::Core PROPERTIES CXX_VISIBILITY_PRESET default diff --git a/src/libs/nanotrace/nanotrace.h b/src/libs/nanotrace/nanotrace.h index ab390519caf..fae1892b80b 100644 --- a/src/libs/nanotrace/nanotrace.h +++ b/src/libs/nanotrace/nanotrace.h @@ -3,13 +3,8 @@ #pragma once -#include +#include "nanotraceglobals.h" -#if defined(NANOTRACE_LIBRARY) -# define NANOTRACESHARED_EXPORT Q_DECL_EXPORT -#else -# define NANOTRACESHARED_EXPORT Q_DECL_IMPORT -#endif #include #include diff --git a/src/libs/nanotrace/nanotraceglobals.h b/src/libs/nanotrace/nanotraceglobals.h new file mode 100644 index 00000000000..649408d69c9 --- /dev/null +++ b/src/libs/nanotrace/nanotraceglobals.h @@ -0,0 +1,16 @@ +// 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 + +#if defined(NANOTRACE_LIBRARY) +#define NANOTRACE_EXPORT Q_DECL_EXPORT +#elif defined(NANOTRACE_STATIC_LIBRARY) +#define NANOTRACE_EXPORT +#else +#define NANOTRACE_EXPORT Q_DECL_IMPORT +#endif + +#define NANOTRACESHARED_EXPORT NANOTRACE_EXPORT diff --git a/src/libs/nanotrace/nanotracehr.cpp b/src/libs/nanotrace/nanotracehr.cpp new file mode 100644 index 00000000000..bd67ae14ea6 --- /dev/null +++ b/src/libs/nanotrace/nanotracehr.cpp @@ -0,0 +1,111 @@ +// 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 "nanotracehr.h" + +#include + +#include +#include +#include +#include +#include + +namespace NanotraceHR { + +namespace { + +template +void printEvent(std::ostream &out, const TraceEvent &event, qint64 processId, std::thread::id threadId) +{ + out << R"({"ph":"X","name":")" << event.name << R"(","cat":")" << event.category + << R"(","ts":")" << static_cast(event.start.time_since_epoch().count()) / 1000 + << R"(","dur":")" << static_cast(event.duration.count()) / 1000 << R"(","pid":")" + << processId << R"(","tid":")" << threadId << R"(","args":)" << event.arguments << "}"; +} +} // namespace + +template +void flushEvents(const Utils::span events, + std::thread::id threadId, + EventQueue &eventQueue) +{ + if (events.empty()) + return; + + std::lock_guard lock{eventQueue.file->fileMutex}; + auto &out = eventQueue.file->out; + + if (out.is_open()) { + auto processId = QCoreApplication::applicationPid(); + for (const auto &event : events) { + printEvent(out, event, processId, threadId); + out << ",\n"; + } + } +} + +template void flushEvents(const Utils::span events, + std::thread::id threadId, + EventQueue &eventQueue); +template void flushEvents(const Utils::span events, + std::thread::id threadId, + EventQueue &eventQueue); + +void openFile(class TraceFile &file) +{ + std::lock_guard lock{file.fileMutex}; + + if (file.out = std::ofstream{file.filePath, std::ios::trunc}; file.out.good()) + file.out << std::fixed << std::setprecision(3) << R"({"traceEvents": [)"; +} + +void finalizeFile(class TraceFile &file) +{ + std::lock_guard lock{file.fileMutex}; + auto &out = file.out; + + if (out.is_open()) { + out.seekp(-2, std::ios_base::cur); // removes last comma and new line + out << R"(],"displayTimeUnit":"ns","otherData":{"version": "Qt Creator )"; + out << QCoreApplication::applicationVersion().toStdString(); + out << R"("}})"; + out.close(); + } +} + +template +void flushInThread(EventQueue &eventQueue) +{ + if (eventQueue.file->processing.valid()) + eventQueue.file->processing.wait(); + + auto flush = [&](const Utils::span &events, std::thread::id threadId) { + flushEvents(events, threadId, eventQueue); + }; + + eventQueue.file->processing = std::async(std::launch::async, + flush, + eventQueue.currentEvents, + std::this_thread::get_id()); + eventQueue.currentEvents = eventQueue.currentEvents.data() == eventQueue.eventsOne.data() + ? eventQueue.eventsTwo + : eventQueue.eventsOne; + eventQueue.eventsIndex = 0; +} + +template void flushInThread(EventQueue &eventQueue); +template void flushInThread(EventQueue &eventQueue); + +namespace { +TraceFile globalTraceFile{"global.json"}; +thread_local auto globalEventQueueData = makeEventQueueData(globalTraceFile); +thread_local EventQueue s_globalEventQueue = globalEventQueueData; +} // namespace + +EventQueue &globalEventQueue() +{ + return s_globalEventQueue; +} + +} // namespace NanotraceHR diff --git a/src/libs/nanotrace/nanotracehr.h b/src/libs/nanotrace/nanotracehr.h new file mode 100644 index 00000000000..1adb6d586b2 --- /dev/null +++ b/src/libs/nanotrace/nanotracehr.h @@ -0,0 +1,373 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include "nanotraceglobals.h" + +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace NanotraceHR { +using Clock = std::chrono::steady_clock; +using TimePoint = std::chrono::time_point; +using Duration = std::chrono::nanoseconds; + +static_assert(Clock::is_steady, "clock should be steady"); +static_assert(std::is_same_v, + "the steady clock should have nano second resolution"); + +constexpr bool isTracerActive() +{ +#ifdef NANOTRACE_ENABLED + return true; +#else + return false; +#endif +} + +template +std::string_view toStringView(Utils::span string) +{ + return {string.data(), string.size()}; +} + +template +struct TraceEvent +{ + TraceEvent() = default; + TraceEvent(const TraceEvent &) = delete; + TraceEvent(TraceEvent &&) = delete; + TraceEvent &operator=(const TraceEvent &) = delete; + TraceEvent &operator=(TraceEvent &&) = delete; + ~TraceEvent() = default; + + String name; + String category; + String arguments; + TimePoint start; + Duration duration; +}; + +using StringViewTraceEvent = TraceEvent; +using StringTraceEvent = TraceEvent; + +enum class IsEnabled { No, Yes }; + +template +class EventQueue; + +template +void flushEvents(const Utils::span events, + std::thread::id threadId, + EventQueue &eventQueue); +extern template void flushEvents(const Utils::span events, + std::thread::id threadId, + EventQueue &eventQueue); +extern template void flushEvents(const Utils::span events, + std::thread::id threadId, + EventQueue &eventQueue); + +template +void flushInThread(EventQueue &eventQueue); +extern template void flushInThread(EventQueue &eventQueue); +extern template void flushInThread(EventQueue &eventQueue); + +void openFile(class TraceFile &file); +void finalizeFile(class TraceFile &file); + +class TraceFile +{ +public: + TraceFile([[maybe_unused]] std::string_view filePath) + : filePath{filePath} + { + openFile(*this); + } + + TraceFile(const TraceFile &) = delete; + TraceFile(TraceFile &&) = delete; + TraceFile &operator=(const TraceFile &) = delete; + TraceFile &operator=(TraceFile &&) = delete; + + ~TraceFile() { finalizeFile(*this); } + std::string filePath; + std::mutex fileMutex; + std::future processing; + std::ofstream out; +}; + +template +class EventQueue +{ + using TraceEventsSpan = Utils::span; + +public: + EventQueue() = default; + ~EventQueue() + { + if (isEnabled == IsEnabled::Yes) + flushEvents(currentEvents, std::this_thread::get_id(), *this); + } + + EventQueue(const EventQueue &) = delete; + EventQueue(EventQueue &&) = delete; + EventQueue &operator=(const EventQueue &) = delete; + EventQueue &operator=(EventQueue &&) = delete; + + TraceFile *file = nullptr; + TraceEventsSpan eventsOne; + TraceEventsSpan eventsTwo; + TraceEventsSpan currentEvents; + std::size_t eventsIndex = 0; + IsEnabled isEnabled = IsEnabled::No; +}; + +template +class EventQueueData +{ + using TraceEvents = std::array; + +public: + EventQueueData(TraceFile &file) + : file{file} + {} + + TraceFile &file; + TraceEvents eventsOne; + TraceEvents eventsTwo; +}; + +template +struct EventQueueDataPointer +{ + operator EventQueue() const + { + if constexpr (isTracerActive()) { + return {&data->file, data->eventsOne, data->eventsTwo, data->eventsOne, 0, IsEnabled::Yes}; + } else { + return {}; + } + } + + std::unique_ptr> data; +}; + +template +EventQueueDataPointer makeEventQueueData(TraceFile &file) +{ + if constexpr (isTracerActive()) { + return {std::make_unique>(file)}; + } else { + return {}; + } +} + +EventQueue &globalEventQueue(); + +template +TraceEvent &getTraceEvent(EventQueue &eventQueue) +{ + if (eventQueue.eventsIndex == eventQueue.currentEvents.size()) + flushInThread(eventQueue); + + return eventQueue.currentEvents[eventQueue.eventsIndex++]; +} + +namespace Literals { +struct TracerLiteral +{ + friend constexpr TracerLiteral operator""_t(const char *text, size_t size); + + constexpr operator std::string_view() const { return text; } + +private: + constexpr TracerLiteral(std::string_view text) + : text{text} + {} + + std::string_view text; +}; +constexpr TracerLiteral operator""_t(const char *text, size_t size) +{ + return {std::string_view{text, size}}; +} +} // namespace Literals + +using namespace Literals; + +template +class Category +{ +public: + using IsActive = std::true_type; + TracerLiteral name; + EventQueue &eventQueue; +}; + +using StringViewCategory = Category; +using StringCategory = Category; + +class DisabledCategory +{}; + +template +Category(TracerLiteral name, EventQueue &eventQueue) -> Category; + +template +class Tracer +{ +public: + constexpr Tracer(TracerLiteral, Category &, TracerLiteral) {} + constexpr Tracer(TracerLiteral, Category &) {} + + ~Tracer() {} +}; + +template +class BasicTracer +{}; + +template<> +class Tracer +{ +public: + constexpr Tracer(TracerLiteral name, StringViewCategory &category, TracerLiteral arguments) + : m_name{name} + , m_arguments{arguments} + , m_category{category} + { + if constexpr (isTracerActive()) { + if (category.eventQueue.isEnabled == IsEnabled::Yes) + m_start = Clock::now(); + } + } + + constexpr Tracer(TracerLiteral name, StringViewCategory &category) + : Tracer{name, category, "{}"_t} + { + if constexpr (isTracerActive()) { + if (category.eventQueue.isEnabled == IsEnabled::Yes) + m_start = Clock::now(); + } + } + + ~Tracer() + { + if constexpr (isTracerActive()) { + if (m_category.eventQueue.isEnabled == IsEnabled::Yes) { + auto duration = Clock::now() - m_start; + auto &traceEvent = getTraceEvent(m_category.eventQueue); + traceEvent.name = m_name; + traceEvent.category = m_category.name; + traceEvent.arguments = m_arguments; + traceEvent.start = m_start; + traceEvent.duration = duration; + } + } + } + +private: + TimePoint m_start; + std::string_view m_name; + std::string_view m_arguments; + StringViewCategory &m_category; +}; + +template<> +class Tracer +{ +public: + Tracer(std::string name, StringViewCategory &category, std::string arguments) + : m_name{std::move(name)} + , m_arguments{arguments} + , m_category{category} + { + if constexpr (isTracerActive()) { + if (category.eventQueue.isEnabled == IsEnabled::Yes) + m_start = Clock::now(); + } + } + + Tracer(std::string name, StringViewCategory &category) + : Tracer{std::move(name), category, "{}"} + { + if constexpr (isTracerActive()) { + if (category.eventQueue.isEnabled == IsEnabled::Yes) + m_start = Clock::now(); + } + } + + ~Tracer() + { + if constexpr (isTracerActive()) { + if (m_category.eventQueue.isEnabled == IsEnabled::Yes) { + auto duration = Clock::now() - m_start; + auto &traceEvent = getTraceEvent(m_category.eventQueue); + traceEvent.name = std::move(m_name); + traceEvent.category = m_category.name; + traceEvent.arguments = std::move(m_arguments); + traceEvent.start = m_start; + traceEvent.duration = duration; + } + } + } + +private: + TimePoint m_start; + std::string m_name; + std::string m_arguments; + StringViewCategory &m_category; +}; + +template +Tracer(TracerLiteral name, Category &category) -> Tracer; + +class GlobalTracer +{ +public: + GlobalTracer(std::string name, std::string category, std::string arguments) + : m_name{std::move(name)} + , m_category{std::move(category)} + , m_arguments{std::move(arguments)} + { + if constexpr (isTracerActive()) { + if (globalEventQueue().isEnabled == IsEnabled::Yes) + m_start = Clock::now(); + } + } + + GlobalTracer(std::string name, std::string category) + : GlobalTracer{std::move(name), std::move(category), "{}"} + {} + + ~GlobalTracer() + { + if constexpr (isTracerActive()) { + if (globalEventQueue().isEnabled == IsEnabled::Yes) { + auto duration = Clock::now() - m_start; + auto &traceEvent = getTraceEvent(globalEventQueue()); + traceEvent.name = std::move(m_name); + traceEvent.category = std::move(m_category); + traceEvent.arguments = std::move(m_arguments); + traceEvent.start = std::move(m_start); + traceEvent.duration = std::move(duration); + } + } + } + +private: + TimePoint m_start; + std::string m_name; + std::string m_category; + std::string m_arguments; +}; + +} // namespace NanotraceHR From 14476ee6062e17e4160714d53e55a5fcb8e95a8d Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Wed, 11 Oct 2023 15:44:45 +0200 Subject: [PATCH 033/242] Sqlite: Use nanotracer Change-Id: I32ff23b4115eaeda998810082b044f2b320c5124 Reviewed-by: Tim Jenssen Reviewed-by: Qt CI Patch Build Bot Reviewed-by: --- src/libs/sqlite/CMakeLists.txt | 11 ++++ src/libs/sqlite/sqlitebasestatement.cpp | 68 +++++++++++++++++++++++-- src/libs/sqlite/sqlitebasestatement.h | 28 ++++++++++ 3 files changed, 102 insertions(+), 5 deletions(-) diff --git a/src/libs/sqlite/CMakeLists.txt b/src/libs/sqlite/CMakeLists.txt index aa33171bab1..59654d6d5bd 100644 --- a/src/libs/sqlite/CMakeLists.txt +++ b/src/libs/sqlite/CMakeLists.txt @@ -1,3 +1,7 @@ +env_with_default("QTC_ENABLE_SQLITE_TRACING" ENV_QTC_ENABLE_SQLITE_TRACING OFF) +option(ENABLE_SQLITE_TRACING "Enable sqlite tarcing" ${ENV_QTC_ENABLE_SQLITE_TRACING}) +add_feature_info("Sqlite tracing" ${ENABLE_SQLITE_TRACING} "") + add_qtc_library(SqliteInternal OBJECT PROPERTIES AUTOMOC OFF AUTOUIC OFF QT_COMPILE_OPTIONS_DISABLE_WARNINGS ON DEFINES SQLITE_CORE SQLITE_CUSTOM_INCLUDE=config.h $<$:SQLITE_DEBUG> @@ -66,6 +70,13 @@ add_qtc_library(Sqlite sqliteids.h ) +extend_qtc_library(Sqlite + CONDITION TARGET Nanotrace + DEPENDS Nanotrace + DEFINES + $<$:ENABLE_SQLITE_TRACING> +) + extend_qtc_library(Sqlite CONDITION QTC_STATIC_BUILD PROPERTIES COMPILE_OPTIONS $,/FIsqlite_static_config.h,-includesqlite_static_config.h> diff --git a/src/libs/sqlite/sqlitebasestatement.cpp b/src/libs/sqlite/sqlitebasestatement.cpp index ae0a60913d1..6b1ad28cff9 100644 --- a/src/libs/sqlite/sqlitebasestatement.cpp +++ b/src/libs/sqlite/sqlitebasestatement.cpp @@ -26,6 +26,25 @@ extern "C" int sqlite3_carray_bind( namespace Sqlite { +#ifdef ENABLE_SQLITE_TRACING +namespace { +NanotraceHR::TraceFile traceFile{"sqlite.json"}; + +thread_local auto eventQueueData = NanotraceHR::makeEventQueueData( + traceFile); +thread_local NanotraceHR::EventQueue eventQueue = eventQueueData; + +NanotraceHR::Category sqliteLowLevelCategory{"sqlite low level"_t, + eventQueue}; +} // namespace + +NanotraceHR::Category sqliteHighLevelCategory{"sqlite high level"_t, + eventQueue}; + +#else +static NanotraceHR::DisabledCategory sqliteLowLevelCategory; +#endif + BaseStatement::BaseStatement(Utils::SmallStringView sqlStatement, Database &database) : m_database(database) { @@ -80,11 +99,14 @@ void BaseStatement::waitForUnlockNotify() const void BaseStatement::reset() const noexcept { + NanotraceHR::Tracer tracer{"reset"_t, sqliteLowLevelCategory}; + sqlite3_reset(m_compiledStatement.get()); } bool BaseStatement::next() const { + NanotraceHR::Tracer tracer{"next"_t, sqliteLowLevelCategory}; int resultCode; do { @@ -111,6 +133,8 @@ void BaseStatement::step() const void BaseStatement::bindNull(int index) { + NanotraceHR::Tracer tracer{"bind null"_t, sqliteLowLevelCategory}; + int resultCode = sqlite3_bind_null(m_compiledStatement.get(), index); if (resultCode != SQLITE_OK) Sqlite::throwError(resultCode, sqliteDatabaseHandle()); @@ -123,6 +147,8 @@ void BaseStatement::bind(int index, NullValue) void BaseStatement::bind(int index, int value) { + NanotraceHR::Tracer tracer{"bind int"_t, sqliteLowLevelCategory}; + int resultCode = sqlite3_bind_int(m_compiledStatement.get(), index, value); if (resultCode != SQLITE_OK) Sqlite::throwError(resultCode, sqliteDatabaseHandle()); @@ -130,6 +156,8 @@ void BaseStatement::bind(int index, int value) void BaseStatement::bind(int index, long long value) { + NanotraceHR::Tracer tracer{"bind long long"_t, sqliteLowLevelCategory}; + int resultCode = sqlite3_bind_int64(m_compiledStatement.get(), index, value); if (resultCode != SQLITE_OK) Sqlite::throwError(resultCode, sqliteDatabaseHandle()); @@ -137,6 +165,8 @@ void BaseStatement::bind(int index, long long value) void BaseStatement::bind(int index, double value) { + NanotraceHR::Tracer tracer{"bind double"_t, sqliteLowLevelCategory}; + int resultCode = sqlite3_bind_double(m_compiledStatement.get(), index, value); if (resultCode != SQLITE_OK) Sqlite::throwError(resultCode, sqliteDatabaseHandle()); @@ -144,17 +174,17 @@ void BaseStatement::bind(int index, double value) void BaseStatement::bind(int index, void *pointer) { - int resultCode = sqlite3_bind_pointer(m_compiledStatement.get(), - index, - pointer, - "carray", - nullptr); + NanotraceHR::Tracer tracer{"bind pointer"_t, sqliteLowLevelCategory}; + + int resultCode = sqlite3_bind_pointer(m_compiledStatement.get(), index, pointer, "carray", nullptr); if (resultCode != SQLITE_OK) Sqlite::throwError(resultCode, sqliteDatabaseHandle()); } void BaseStatement::bind(int index, Utils::span values) { + NanotraceHR::Tracer tracer{"bind int span"_t, sqliteLowLevelCategory}; + int resultCode = sqlite3_carray_bind(m_compiledStatement.get(), index, const_cast(values.data()), @@ -167,6 +197,8 @@ void BaseStatement::bind(int index, Utils::span values) void BaseStatement::bind(int index, Utils::span values) { + NanotraceHR::Tracer tracer{"bind long long span"_t, sqliteLowLevelCategory}; + int resultCode = sqlite3_carray_bind(m_compiledStatement.get(), index, const_cast(values.data()), @@ -179,6 +211,8 @@ void BaseStatement::bind(int index, Utils::span values) void BaseStatement::bind(int index, Utils::span values) { + NanotraceHR::Tracer tracer{"bind double span"_t, sqliteLowLevelCategory}; + int resultCode = sqlite3_carray_bind(m_compiledStatement.get(), index, const_cast(values.data()), @@ -191,6 +225,8 @@ void BaseStatement::bind(int index, Utils::span values) void BaseStatement::bind(int index, Utils::span values) { + NanotraceHR::Tracer tracer{"bind const char* span"_t, sqliteLowLevelCategory}; + int resultCode = sqlite3_carray_bind(m_compiledStatement.get(), index, values.data(), @@ -203,6 +239,8 @@ void BaseStatement::bind(int index, Utils::span values) void BaseStatement::bind(int index, Utils::SmallStringView text) { + NanotraceHR::Tracer tracer{"bind string"_t, sqliteLowLevelCategory}; + int resultCode = sqlite3_bind_text(m_compiledStatement.get(), index, text.data(), @@ -214,6 +252,8 @@ void BaseStatement::bind(int index, Utils::SmallStringView text) void BaseStatement::bind(int index, BlobView blobView) { + NanotraceHR::Tracer tracer{"bind blob"_t, sqliteLowLevelCategory}; + int resultCode = SQLITE_OK; if (blobView.empty()) { @@ -232,6 +272,8 @@ void BaseStatement::bind(int index, BlobView blobView) void BaseStatement::bind(int index, const Value &value) { + NanotraceHR::Tracer tracer{"bind value"_t, sqliteLowLevelCategory}; + switch (value.type()) { case ValueType::Integer: bind(index, value.toInteger()); @@ -253,6 +295,8 @@ void BaseStatement::bind(int index, const Value &value) void BaseStatement::bind(int index, ValueView value) { + NanotraceHR::Tracer tracer{"bind value"_t, sqliteLowLevelCategory}; + switch (value.type()) { case ValueType::Integer: bind(index, value.toInteger()); @@ -274,6 +318,8 @@ void BaseStatement::bind(int index, ValueView value) void BaseStatement::prepare(Utils::SmallStringView sqlStatement) { + NanotraceHR::Tracer tracer{"prepare"_t, sqliteLowLevelCategory}; + if (!m_database.isLocked()) throw DatabaseIsNotLocked{}; @@ -373,6 +419,8 @@ StringType convertToTextForColumn(sqlite3_stmt *sqlStatment, int column) Type BaseStatement::fetchType(int column) const { + NanotraceHR::Tracer tracer{"fetch type"_t, sqliteLowLevelCategory}; + auto dataType = sqlite3_column_type(m_compiledStatement.get(), column); switch (dataType) { @@ -393,6 +441,8 @@ Type BaseStatement::fetchType(int column) const int BaseStatement::fetchIntValue(int column) const { + NanotraceHR::Tracer tracer{"fetch int"_t, sqliteLowLevelCategory}; + return sqlite3_column_int(m_compiledStatement.get(), column); } @@ -415,6 +465,8 @@ long BaseStatement::fetchValue(int column) const long long BaseStatement::fetchLongLongValue(int column) const { + NanotraceHR::Tracer tracer{"fetch long long"_t, sqliteLowLevelCategory}; + return sqlite3_column_int64(m_compiledStatement.get(), column); } @@ -426,11 +478,15 @@ long long BaseStatement::fetchValue(int column) const double BaseStatement::fetchDoubleValue(int column) const { + NanotraceHR::Tracer tracer{"fetch double"_t, sqliteLowLevelCategory}; + return sqlite3_column_double(m_compiledStatement.get(), column); } BlobView BaseStatement::fetchBlobValue(int column) const { + NanotraceHR::Tracer tracer{"fetch blob"_t, sqliteLowLevelCategory}; + return convertToBlobForColumn(m_compiledStatement.get(), column); } @@ -479,6 +535,8 @@ ValueView BaseStatement::fetchValueView(int column) const void BaseStatement::Deleter::operator()(sqlite3_stmt *statement) { + NanotraceHR::Tracer tracer{"finalize"_t, sqliteLowLevelCategory}; + sqlite3_finalize(statement); } diff --git a/src/libs/sqlite/sqlitebasestatement.h b/src/libs/sqlite/sqlitebasestatement.h index 3c490293447..6c202625321 100644 --- a/src/libs/sqlite/sqlitebasestatement.h +++ b/src/libs/sqlite/sqlitebasestatement.h @@ -14,6 +14,7 @@ #include +#include #include #include @@ -29,6 +30,8 @@ using std::int64_t; namespace Sqlite { +using namespace NanotraceHR::Literals; + class Database; class DatabaseBackend; @@ -41,6 +44,11 @@ constexpr static std::underlying_type_t to_underlying(Enumeration e return static_cast>(enumeration); } +#ifdef ENABLE_SQLITE_TRACING +extern NanotraceHR::Category sqliteHighLevelCategory; +#else +inline NanotraceHR::DisabledCategory sqliteHighLevelCategory; +#endif class SQLITE_EXPORT BaseStatement { public: @@ -152,6 +160,8 @@ public: void execute() { + NanotraceHR::Tracer tracer{"execute"_t, sqliteHighLevelCategory}; + Resetter resetter{this}; BaseStatement::next(); } @@ -159,6 +169,8 @@ public: template void bindValues(const ValueType &...values) { + NanotraceHR::Tracer tracer{"bind"_t, sqliteHighLevelCategory}; + static_assert(BindParameterCount == sizeof...(values), "Wrong binding parameter count!"); int index = 0; @@ -168,6 +180,8 @@ public: template void write(const ValueType&... values) { + NanotraceHR::Tracer tracer{"write"_t, sqliteHighLevelCategory}; + Resetter resetter{this}; bindValues(values...); BaseStatement::next(); @@ -192,6 +206,8 @@ public: typename... QueryTypes> auto values(const QueryTypes &...queryValues) { + NanotraceHR::Tracer tracer{"values"_t, sqliteHighLevelCategory}; + Resetter resetter{this}; Container resultValues; resultValues.reserve(std::max(capacity, m_maximumResultCount)); @@ -219,6 +235,8 @@ public: template auto value(const QueryTypes &...queryValues) { + NanotraceHR::Tracer tracer{"values"_t, sqliteHighLevelCategory}; + Resetter resetter{this}; ResultType resultValue{}; @@ -233,6 +251,8 @@ public: template auto optionalValue(const QueryTypes &...queryValues) { + NanotraceHR::Tracer tracer{"optionalValue"_t, sqliteHighLevelCategory}; + Resetter resetter{this}; std::optional resultValue; @@ -247,6 +267,8 @@ public: template static auto toValue(Utils::SmallStringView sqlStatement, Database &database) { + NanotraceHR::Tracer tracer{"toValue"_t, sqliteHighLevelCategory}; + StatementImplementation statement(sqlStatement, database); statement.checkColumnCount(1); @@ -259,6 +281,8 @@ public: template void readCallback(Callable &&callable, const QueryTypes &...queryValues) { + NanotraceHR::Tracer tracer{"readCallback"_t, sqliteHighLevelCategory}; + Resetter resetter{this}; bindValues(queryValues...); @@ -274,6 +298,8 @@ public: template void readTo(Container &container, const QueryTypes &...queryValues) { + NanotraceHR::Tracer tracer{"readTo"_t, sqliteHighLevelCategory}; + Resetter resetter{this}; bindValues(queryValues...); @@ -369,6 +395,8 @@ public: const_iterator end() const & { return iterator{m_statement, false}; } private: + NanotraceHR::Tracer tracer{"range"_t, + sqliteHighLevelCategory}; StatementImplementation &m_statement; }; From c081c96bf0b99bc0c25943956a64014df65e7514 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Wed, 11 Oct 2023 20:20:59 +0200 Subject: [PATCH 034/242] QmlDesigner: Add tracing to project storage The tracing is by default disabled for the project storage category. So there is no overhead because the tracer instance is a empty class in that case. Change-Id: I7d3e91527871a946cadbbad2e11f0b2ab2825c59 Reviewed-by: Tim Jenssen Reviewed-by: Reviewed-by: Qt CI Patch Build Bot --- src/plugins/qmldesigner/CMakeLists.txt | 12 ++ .../projectstorage/projectstorage.cpp | 16 ++ .../projectstorage/projectstorage.h | 159 ++++++++++++++++++ 3 files changed, 187 insertions(+) diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index 5b620323389..6e8df880782 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -15,6 +15,11 @@ env_with_default("QDS_USE_PROJECTSTORAGE" ENV_QDS_USE_PROJECTSTORAGE OFF) option(USE_PROJECTSTORAGE "Use ProjectStorage" ${ENV_QDS_USE_PROJECTSTORAGE}) add_feature_info("ProjectStorage" ${USE_PROJECTSTORAGE} "") +env_with_default("QTC_ENABLE_PROJECT_STORAGE_TRACING" ENV_QTC_ENABLE_PROJECT_STORAGE_TRACING OFF) +option(ENABLE_PROJECT_STORAGE_TRACING "Enable sqlite tarcing" ${ENV_QTC_ENABLE_PROJECT_STORAGE_TRACING}) +add_feature_info("Sqlite tracing" ${ENABLE_PROJECT_STORAGE_TRACING} "") + + add_qtc_library(QmlDesignerUtils STATIC DEPENDS Qt::Gui Utils Qt::QmlPrivate Core @@ -83,6 +88,13 @@ add_qtc_library(QmlDesignerCore STATIC rewritertransaction.h ) +extend_qtc_library(QmlDesignerCore + CONDITION TARGET Nanotrace + DEPENDS Nanotrace + DEFINES + $<$:ENABLE_PROJECT_STORAGE_TRACING> +) + extend_qtc_library(QmlDesignerCore CONDITION ENABLE_COMPILE_WARNING_AS_ERROR PROPERTIES COMPILE_WARNING_AS_ERROR ON diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.cpp b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.cpp index 2bef2244d70..9819c2adcc4 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.cpp +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.cpp @@ -5,4 +5,20 @@ #include +namespace QmlDesigner { + +#ifdef ENABLE_PROJECT_STORAGE_TRACING +namespace { +NanotraceHR::TraceFile traceFile{"projectstorage.json"}; + +thread_local auto eventQueueData = NanotraceHR::makeEventQueueData( + traceFile); +thread_local NanotraceHR::EventQueue eventQueue = eventQueueData; +} // namespace + +NanotraceHR::Category projectStorageCategory{"project storage"_t, + eventQueue}; +#endif +} // namespace QmlDesigner + template class QmlDesigner::ProjectStorage; diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h index f2bdaa4a26c..d3963019440 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h @@ -26,6 +26,14 @@ namespace QmlDesigner { +using namespace NanotraceHR::Literals; + +#ifdef ENABLE_PROJECT_STORAGE_TRACING +extern NanotraceHR::Category projectStorageCategory; +#else +inline NanotraceHR::DisabledCategory projectStorageCategory; +#endif + template class ProjectStorage final : public ProjectStorageInterface { @@ -44,6 +52,8 @@ public: , exclusiveTransaction{database} , initializer{database, isInitialized} { + NanotraceHR::Tracer tracer{"initialize"_t, projectStorageCategory}; + exclusiveTransaction.commit(); database.walCheckpointFull(); @@ -53,6 +63,8 @@ public: void synchronize(Storage::Synchronization::SynchronizationPackage package) override { + NanotraceHR::Tracer tracer{"synchronize"_t, projectStorageCategory}; + TypeIds deletedTypeIds; Sqlite::withImmediateTransaction(database, [&] { AliasPropertyDeclarations insertedAliasPropertyDeclarations; @@ -118,6 +130,8 @@ public: void synchronizeDocumentImports(Storage::Imports imports, SourceId sourceId) override { + NanotraceHR::Tracer tracer{"synchronize document imports"_t, projectStorageCategory}; + Sqlite::withImmediateTransaction(database, [&] { synchronizeDocumentImports(imports, {sourceId}, @@ -134,11 +148,15 @@ public: ModuleId moduleId(Utils::SmallStringView moduleName) const override { + NanotraceHR::Tracer tracer{"get module id"_t, projectStorageCategory}; + return moduleCache.id(moduleName); } Utils::SmallString moduleName(ModuleId moduleId) const { + NanotraceHR::Tracer tracer{"get module name"_t, projectStorageCategory}; + if (!moduleId) throw ModuleDoesNotExists{}; @@ -149,6 +167,8 @@ public: Utils::SmallStringView exportedTypeName, Storage::Version version) const override { + NanotraceHR::Tracer tracer{"get type id by exported name"_t, projectStorageCategory}; + if (version.minor) return selectTypeIdByModuleIdAndExportedNameAndVersionStatement .template valueWithTransaction(moduleId, @@ -166,17 +186,23 @@ public: TypeId typeId(ImportedTypeNameId typeNameId) const override { + NanotraceHR::Tracer tracer{"get type id by imported type name"_t, projectStorageCategory}; + return Sqlite::withDeferredTransaction(database, [&] { return fetchTypeId(typeNameId); }); } QVarLengthArray typeIds(ModuleId moduleId) const override { + NanotraceHR::Tracer tracer{"get type ids by module id"_t, projectStorageCategory}; + return selectTypeIdsByModuleIdStatement .template valuesWithTransaction>(moduleId); } Storage::Info::ExportedTypeNames exportedTypeNames(TypeId typeId) const override { + NanotraceHR::Tracer tracer{"get exported type names by type id"_t, projectStorageCategory}; + return selectExportedTypesByTypeIdStatement .template valuesWithTransaction(typeId); } @@ -184,12 +210,16 @@ public: Storage::Info::ExportedTypeNames exportedTypeNames(TypeId typeId, SourceId sourceId) const override { + NanotraceHR::Tracer tracer{"get exported type names by source id"_t, projectStorageCategory}; + return selectExportedTypesByTypeIdAndSourceIdStatement .template valuesWithTransaction(typeId, sourceId); } ImportId importId(const Storage::Import &import) const override { + NanotraceHR::Tracer tracer{"get import id by import"_t, projectStorageCategory}; + return Sqlite::withDeferredTransaction(database, [&] { return fetchImportId(import.sourceId, import); }); @@ -198,6 +228,8 @@ public: ImportedTypeNameId importedTypeNameId(ImportId importId, Utils::SmallStringView typeName) override { + NanotraceHR::Tracer tracer{"get imported type name id by import id"_t, projectStorageCategory}; + return Sqlite::withDeferredTransaction(database, [&] { return fetchImportedTypeNameId(Storage::Synchronization::TypeNameKind::QualifiedExported, importId, @@ -208,6 +240,8 @@ public: ImportedTypeNameId importedTypeNameId(SourceId sourceId, Utils::SmallStringView typeName) override { + NanotraceHR::Tracer tracer{"get imported type name id by source id"_t, projectStorageCategory}; + return Sqlite::withDeferredTransaction(database, [&] { return fetchImportedTypeNameId(Storage::Synchronization::TypeNameKind::Exported, sourceId, @@ -217,12 +251,16 @@ public: QVarLengthArray propertyDeclarationIds(TypeId typeId) const override { + NanotraceHR::Tracer tracer{"get property declaration ids"_t, projectStorageCategory}; + return selectPropertyDeclarationIdsForTypeStatement .template valuesWithTransaction>(typeId); } QVarLengthArray localPropertyDeclarationIds(TypeId typeId) const override { + NanotraceHR::Tracer tracer{"get local property declaration ids"_t, projectStorageCategory}; + return selectLocalPropertyDeclarationIdsForTypeStatement .template valuesWithTransaction>(typeId); } @@ -230,6 +268,8 @@ public: PropertyDeclarationId propertyDeclarationId(TypeId typeId, Utils::SmallStringView propertyName) const override { + NanotraceHR::Tracer tracer{"get property declaration id"_t, projectStorageCategory}; + return selectPropertyDeclarationIdForTypeAndPropertyNameStatement .template valueWithTransaction(typeId, propertyName); } @@ -237,6 +277,8 @@ public: PropertyDeclarationId localPropertyDeclarationId(TypeId typeId, Utils::SmallStringView propertyName) const { + NanotraceHR::Tracer tracer{"get local property declaration id"_t, projectStorageCategory}; + return selectLocalPropertyDeclarationIdForTypeAndPropertyNameStatement .template valueWithTransaction(typeId, propertyName); } @@ -244,6 +286,8 @@ public: std::optional propertyDeclaration( PropertyDeclarationId propertyDeclarationId) const override { + NanotraceHR::Tracer tracer{"get property declaration"_t, projectStorageCategory}; + return selectPropertyDeclarationForPropertyDeclarationIdStatement .template optionalValueWithTransaction( propertyDeclarationId); @@ -251,23 +295,31 @@ public: std::optional type(TypeId typeId) const override { + NanotraceHR::Tracer tracer{"get type"_t, projectStorageCategory}; + return selectInfoTypeByTypeIdStatement.template optionalValueWithTransaction( typeId); } Utils::PathString typeIconPath(TypeId typeId) const override { + NanotraceHR::Tracer tracer{"get type icon path"_t, projectStorageCategory}; + return selectTypeIconPathStatement.template valueWithTransaction(typeId); } Storage::Info::TypeHints typeHints(TypeId typeId) const override { + NanotraceHR::Tracer tracer{"get type hints"_t, projectStorageCategory}; + return selectTypeHintsStatement.template valuesWithTransaction( typeId); } Storage::Info::ItemLibraryEntries itemLibraryEntries(TypeId typeId) const override { + NanotraceHR::Tracer tracer{"get item library entries by type id"_t, projectStorageCategory}; + using Storage::Info::ItemLibraryProperties; Storage::Info::ItemLibraryEntries entries; @@ -295,6 +347,8 @@ public: Storage::Info::ItemLibraryEntries itemLibraryEntries(SourceId sourceId) const override { + NanotraceHR::Tracer tracer{"get item library entries by source id"_t, projectStorageCategory}; + using Storage::Info::ItemLibraryProperties; Storage::Info::ItemLibraryEntries entries; @@ -322,6 +376,8 @@ public: Storage::Info::ItemLibraryEntries allItemLibraryEntries() const override { + NanotraceHR::Tracer tracer{"get all item library entries"_t, projectStorageCategory}; + using Storage::Info::ItemLibraryProperties; Storage::Info::ItemLibraryEntries entries; @@ -349,18 +405,24 @@ public: std::vector signalDeclarationNames(TypeId typeId) const override { + NanotraceHR::Tracer tracer{"get signal names"_t, projectStorageCategory}; + return selectSignalDeclarationNamesForTypeStatement .template valuesWithTransaction(typeId); } std::vector functionDeclarationNames(TypeId typeId) const override { + NanotraceHR::Tracer tracer{"get function names"_t, projectStorageCategory}; + return selectFuncionDeclarationNamesForTypeStatement .template valuesWithTransaction(typeId); } std::optional propertyName(PropertyDeclarationId propertyDeclarationId) const override { + NanotraceHR::Tracer tracer{"get property name"_t, projectStorageCategory}; + return selectPropertyNameStatement.template optionalValueWithTransaction( propertyDeclarationId); } @@ -373,41 +435,57 @@ public: template TypeId commonTypeId() const { + NanotraceHR::Tracer tracer{"get type id from common type cache"_t, projectStorageCategory}; + return commonTypeCache_.template typeId(); } template TypeId builtinTypeId() const { + NanotraceHR::Tracer tracer{"get builtin type id from common type cache"_t, + projectStorageCategory}; + return commonTypeCache_.template builtinTypeId(); } template TypeId builtinTypeId() const { + NanotraceHR::Tracer tracer{"get builtin type id from common type cache"_t, + projectStorageCategory}; + return commonTypeCache_.template builtinTypeId(); } TypeIds prototypeIds(TypeId type) const override { + NanotraceHR::Tracer tracer{"get prototypes"_t, projectStorageCategory}; + return selectPrototypeIdsForTypeIdInOrderStatement.template valuesWithTransaction( type); } TypeIds prototypeAndSelfIds(TypeId type) const override { + NanotraceHR::Tracer tracer{"get prototypes and self"_t, projectStorageCategory}; + return selectPrototypeAndSelfIdsForTypeIdInOrderStatement .template valuesWithTransaction(type); } TypeIds heirIds(TypeId typeId) const override { + NanotraceHR::Tracer tracer{"get heirs"_t, projectStorageCategory}; + return selectHeirTypeIdsStatement.template valuesWithTransaction(typeId); } template bool isBasedOn_(TypeId typeId, TypeIds... baseTypeIds) const { + NanotraceHR::Tracer tracer{"is based on"_t, projectStorageCategory}; + static_assert(((std::is_same_v) &&...), "Parameter must be a TypeId!"); if (((typeId == baseTypeIds) || ...)) @@ -467,6 +545,8 @@ public: TypeId fetchTypeIdByExportedName(Utils::SmallStringView name) const { + NanotraceHR::Tracer tracer{"is based on"_t, projectStorageCategory}; + return selectTypeIdByExportedNameStatement.template valueWithTransaction(name); } @@ -527,6 +607,8 @@ public: SourceContextId fetchSourceContextIdUnguarded(Utils::SmallStringView sourceContextPath) { + NanotraceHR::Tracer tracer{"fetch source context id unguarded"_t, projectStorageCategory}; + auto sourceContextId = readSourceContextId(sourceContextPath); return sourceContextId ? sourceContextId : writeSourceContextId(sourceContextPath); @@ -534,6 +616,8 @@ public: SourceContextId fetchSourceContextId(Utils::SmallStringView sourceContextPath) { + NanotraceHR::Tracer tracer{"fetch source context id"_t, projectStorageCategory}; + try { return Sqlite::withDeferredTransaction(database, [&] { return fetchSourceContextIdUnguarded(sourceContextPath); @@ -545,6 +629,8 @@ public: Utils::PathString fetchSourceContextPath(SourceContextId sourceContextId) const { + NanotraceHR::Tracer tracer{"fetch source context path"_t, projectStorageCategory}; + return Sqlite::withDeferredTransaction(database, [&] { auto optionalSourceContextPath = selectSourceContextPathFromSourceContextsBySourceContextIdStatement .template optionalValue( @@ -559,12 +645,16 @@ public: auto fetchAllSourceContexts() const { + NanotraceHR::Tracer tracer{"fetch all source contexts"_t, projectStorageCategory}; + return selectAllSourceContextsStatement .template valuesWithTransaction(); } SourceId fetchSourceId(SourceContextId sourceContextId, Utils::SmallStringView sourceName) { + NanotraceHR::Tracer tracer{"fetch source id"_t, projectStorageCategory}; + return Sqlite::withDeferredTransaction(database, [&] { return fetchSourceIdUnguarded(sourceContextId, sourceName); }); @@ -572,6 +662,9 @@ public: auto fetchSourceNameAndSourceContextId(SourceId sourceId) const { + NanotraceHR::Tracer tracer{"fetch source name and source context id"_t, + projectStorageCategory}; + auto value = selectSourceNameAndSourceContextIdFromSourcesBySourceIdStatement .template valueWithTransaction(sourceId); @@ -591,6 +684,8 @@ public: SourceContextId fetchSourceContextId(SourceId sourceId) const { + NanotraceHR::Tracer tracer{"fetch source context id"_t, projectStorageCategory}; + auto sourceContextId = selectSourceContextIdFromSourcesBySourceIdStatement .template valueWithTransaction(sourceId); @@ -602,11 +697,15 @@ public: auto fetchAllSources() const { + NanotraceHR::Tracer tracer{"fetch all sources"_t, projectStorageCategory}; + return selectAllSourcesStatement.template valuesWithTransaction(); } SourceId fetchSourceIdUnguarded(SourceContextId sourceContextId, Utils::SmallStringView sourceName) { + NanotraceHR::Tracer tracer{"fetch source id unguarded"_t, projectStorageCategory}; + auto sourceId = readSourceId(sourceContextId, sourceName); if (sourceId) @@ -617,23 +716,31 @@ public: auto fetchAllFileStatuses() const { + NanotraceHR::Tracer tracer{"fetch all file statuses"_t, projectStorageCategory}; + return selectAllFileStatusesStatement.template rangeWithTransaction(); } FileStatus fetchFileStatus(SourceId sourceId) const override { + NanotraceHR::Tracer tracer{"fetch file status"_t, projectStorageCategory}; + return selectFileStatusesForSourceIdStatement.template valueWithTransaction( sourceId); } std::optional fetchProjectData(SourceId sourceId) const override { + NanotraceHR::Tracer tracer{"fetch project data"_t, projectStorageCategory}; + return selectProjectDataForSourceIdStatement .template optionalValueWithTransaction(sourceId); } Storage::Synchronization::ProjectDatas fetchProjectDatas(SourceId projectSourceId) const override { + NanotraceHR::Tracer tracer{"fetch project datas by source id"_t, projectStorageCategory}; + return selectProjectDatasForSourceIdStatement .template valuesWithTransaction( projectSourceId); @@ -641,6 +748,8 @@ public: Storage::Synchronization::ProjectDatas fetchProjectDatas(const SourceIds &projectSourceIds) const { + NanotraceHR::Tracer tracer{"fetch project datas by source ids"_t, projectStorageCategory}; + return selectProjectDatasForSourceIdsStatement .template valuesWithTransaction( toIntegers(projectSourceIds)); @@ -662,6 +771,8 @@ public: Storage::Imports fetchDocumentImports() const { + NanotraceHR::Tracer tracer{"fetch document imports"_t, projectStorageCategory}; + return selectAllDocumentImportForSourceIdStatement .template valuesWithTransaction(); } @@ -914,6 +1025,8 @@ private: void synchronizeTypeAnnotations(Storage::Synchronization::TypeAnnotations &typeAnnotations, const SourceIds &updatedTypeAnnotationSourceIds) { + NanotraceHR::Tracer tracer{"synchronize type annotations"_t, projectStorageCategory}; + using Storage::Synchronization::TypeAnnotation; updateTypeIdInTypeAnnotations(typeAnnotations); @@ -981,6 +1094,8 @@ private: Prototypes &relinkableExtensions, const SourceIds &updatedSourceIds) { + NanotraceHR::Tracer tracer{"synchronize types"_t, projectStorageCategory}; + Storage::Synchronization::ExportedTypes exportedTypes; exportedTypes.reserve(types.size() * 3); SourceIds sourceIdsOfTypes; @@ -1036,6 +1151,8 @@ private: void synchronizeProjectDatas(Storage::Synchronization::ProjectDatas &projectDatas, const SourceIds &updatedProjectSourceIds) { + NanotraceHR::Tracer tracer{"synchronize project datas"_t, projectStorageCategory}; + auto compareKey = [](auto &&first, auto &&second) { auto projectSourceIdDifference = first.projectSourceId - second.projectSourceId; if (projectSourceIdDifference != 0) @@ -1088,6 +1205,8 @@ private: void synchronizeFileStatuses(FileStatuses &fileStatuses, const SourceIds &updatedSourceIds) { + NanotraceHR::Tracer tracer{"synchronize file statuses"_t, projectStorageCategory}; + auto compareKey = [](auto &&first, auto &&second) { return first.sourceId - second.sourceId; }; @@ -1133,6 +1252,8 @@ private: Storage::Synchronization::ModuleExportedImports &moduleExportedImports, const ModuleIds &updatedModuleIds) { + NanotraceHR::Tracer tracer{"synchronize imports"_t, projectStorageCategory}; + synchromizeModuleExportedImports(moduleExportedImports, updatedModuleIds); synchronizeDocumentImports(imports, updatedSourceIds, @@ -1292,6 +1413,8 @@ private: void relinkAliasPropertyDeclarations(AliasPropertyDeclarations &aliasPropertyDeclarations, const TypeIds &deletedTypeIds) { + NanotraceHR::Tracer tracer{"relink alias properties"_t, projectStorageCategory}; + std::sort(aliasPropertyDeclarations.begin(), aliasPropertyDeclarations.end()); Utils::set_greedy_difference( @@ -1320,6 +1443,8 @@ private: void relinkPropertyDeclarations(PropertyDeclarations &relinkablePropertyDeclaration, const TypeIds &deletedTypeIds) { + NanotraceHR::Tracer tracer{"relink properties"_t, projectStorageCategory}; + std::sort(relinkablePropertyDeclaration.begin(), relinkablePropertyDeclaration.end()); Utils::set_greedy_difference( @@ -1344,6 +1469,8 @@ private: const TypeIds &deletedTypeIds, Callable updateStatement) { + NanotraceHR::Tracer tracer{"relink prototypes"_t, projectStorageCategory}; + std::sort(relinkablePrototypes.begin(), relinkablePrototypes.end()); Utils::set_greedy_difference( @@ -1372,6 +1499,8 @@ private: Prototypes &relinkableExtensions, TypeIds &deletedTypeIds) { + NanotraceHR::Tracer tracer{"delete not updated types"_t, projectStorageCategory}; + auto callback = [&](TypeId typeId) { deletedTypeIds.push_back(typeId); deleteType(typeId, @@ -1394,6 +1523,8 @@ private: Prototypes &relinkableExtensions, TypeIds &deletedTypeIds) { + NanotraceHR::Tracer tracer{"relink"_t, projectStorageCategory}; + std::sort(deletedTypeIds.begin(), deletedTypeIds.end()); relinkPrototypes(relinkablePrototypes, deletedTypeIds, [&](TypeId typeId, TypeId prototypeId) { @@ -1460,6 +1591,8 @@ private: void linkAliases(const AliasPropertyDeclarations &insertedAliasPropertyDeclarations, const AliasPropertyDeclarations &updatedAliasPropertyDeclarations) { + NanotraceHR::Tracer tracer{"link aliases"_t, projectStorageCategory}; + linkAliasPropertyDeclarationAliasIds(insertedAliasPropertyDeclarations); linkAliasPropertyDeclarationAliasIds(updatedAliasPropertyDeclarations); @@ -1477,6 +1610,8 @@ private: Prototypes &relinkablePrototypes, Prototypes &relinkableExtensions) { + NanotraceHR::Tracer tracer{"synchronize exported types"_t, projectStorageCategory}; + std::sort(exportedTypes.begin(), exportedTypes.end(), [](auto &&first, auto &&second) { if (first.moduleId < second.moduleId) return true; @@ -1660,6 +1795,8 @@ private: AliasPropertyDeclarations &updatedAliasPropertyDeclarations, PropertyDeclarationIds &propertyDeclarationIds) { + NanotraceHR::Tracer tracer{"synchronize property declaration"_t, projectStorageCategory}; + std::sort(propertyDeclarations.begin(), propertyDeclarations.end(), [](auto &&first, auto &&second) { @@ -1776,6 +1913,8 @@ private: Storage::Synchronization::Types &types, AliasPropertyDeclarations &relinkableAliasPropertyDeclarations) { + NanotraceHR::Tracer tracer{"reset removed alias properties to null"_t, projectStorageCategory}; + PropertyDeclarationIds propertyDeclarationIds; propertyDeclarationIds.reserve(types.size()); @@ -1973,6 +2112,8 @@ private: void synchronizePropertyEditorQmlPaths(Storage::Synchronization::PropertyEditorQmlPaths &paths, SourceIds updatedPropertyEditorQmlPathsSourceIds) { + NanotraceHR::Tracer tracer{"synchronize property editor qml paths"_t, projectStorageCategory}; + addTypeIdToPropertyEditorQmlPaths(paths); synchronizePropertyEditorPaths(paths, updatedPropertyEditorQmlPathsSourceIds); } @@ -1980,6 +2121,8 @@ private: void synchronizeFunctionDeclarations( TypeId typeId, Storage::Synchronization::FunctionDeclarations &functionsDeclarations) { + NanotraceHR::Tracer tracer{"synchronize function declaration"_t, projectStorageCategory}; + std::sort(functionsDeclarations.begin(), functionsDeclarations.end(), [](auto &&first, auto &&second) { @@ -2037,6 +2180,8 @@ private: void synchronizeSignalDeclarations(TypeId typeId, Storage::Synchronization::SignalDeclarations &signalDeclarations) { + NanotraceHR::Tracer tracer{"synchronize signal declaration"_t, projectStorageCategory}; + std::sort(signalDeclarations.begin(), signalDeclarations.end(), [](auto &&first, auto &&second) { auto compare = Sqlite::compare(first.name, second.name); @@ -2111,6 +2256,8 @@ private: void synchronizeEnumerationDeclarations( TypeId typeId, Storage::Synchronization::EnumerationDeclarations &enumerationDeclarations) { + NanotraceHR::Tracer tracer{"synchronize enumeation declaration"_t, projectStorageCategory}; + std::sort(enumerationDeclarations.begin(), enumerationDeclarations.end(), [](auto &&first, auto &&second) { @@ -2183,6 +2330,8 @@ private: AliasPropertyDeclarations &updatedAliasPropertyDeclarations, PropertyDeclarationIds &propertyDeclarationIds) { + NanotraceHR::Tracer tracer{"synchronize declaration per type"_t, projectStorageCategory}; + if (type.changeLevel == Storage::Synchronization::ChangeLevel::Minimal) return; @@ -2200,6 +2349,8 @@ private: template void removeRelinkableEntries(std::vector &relinkables, Ids &ids, Compare compare) { + NanotraceHR::Tracer tracer{"remove relinkable entries"_t, projectStorageCategory}; + std::vector newRelinkables; newRelinkables.reserve(relinkables.size()); @@ -2222,6 +2373,8 @@ private: AliasPropertyDeclarations &updatedAliasPropertyDeclarations, PropertyDeclarations &relinkablePropertyDeclarations) { + NanotraceHR::Tracer tracer{"synchronize declaration"_t, projectStorageCategory}; + PropertyDeclarationIds propertyDeclarationIds; propertyDeclarationIds.reserve(types.size() * 10); @@ -2250,6 +2403,8 @@ private: void syncDefaultProperties(Storage::Synchronization::Types &types) { + NanotraceHR::Tracer tracer{"synchronize default properties"_t, projectStorageCategory}; + auto range = selectTypesWithDefaultPropertyStatement.template range(); auto compareKey = [](const TypeWithDefaultPropertyView &view, @@ -2284,6 +2439,8 @@ private: void resetDefaultPropertiesIfChanged(Storage::Synchronization::Types &types) { + NanotraceHR::Tracer tracer{"reset changed default properties"_t, projectStorageCategory}; + auto range = selectTypesWithDefaultPropertyStatement.template range(); auto compareKey = [](const TypeWithDefaultPropertyView &view, @@ -2382,6 +2539,8 @@ private: Prototypes &relinkablePrototypes, Prototypes &relinkableExtensions) { + NanotraceHR::Tracer tracer{"synchronize prototypes"_t, projectStorageCategory}; + TypeIds typeIds; typeIds.reserve(types.size()); From db59cdaf72d626438af8b056b1e334d4921f87f6 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Thu, 12 Oct 2023 10:04:26 +0200 Subject: [PATCH 035/242] NanoTrace: Reduce macros with template specialization The nano trace code gets more complicated but the usage get mor readable. Change-Id: I2b829455d1328dc330474abbb804c76232df3e92 Reviewed-by: Tim Jenssen Reviewed-by: Qt CI Patch Build Bot --- src/libs/nanotrace/nanotracehr.cpp | 22 +-- src/libs/nanotrace/nanotracehr.h | 142 ++++++++++++------ src/libs/sqlite/sqlitebasestatement.cpp | 19 +-- src/libs/sqlite/sqlitebasestatement.h | 10 +- .../projectstorage/projectstorage.cpp | 10 +- .../projectstorage/projectstorage.h | 9 +- 6 files changed, 137 insertions(+), 75 deletions(-) diff --git a/src/libs/nanotrace/nanotracehr.cpp b/src/libs/nanotrace/nanotracehr.cpp index bd67ae14ea6..7ed3a796dd0 100644 --- a/src/libs/nanotrace/nanotracehr.cpp +++ b/src/libs/nanotrace/nanotracehr.cpp @@ -28,7 +28,7 @@ void printEvent(std::ostream &out, const TraceEvent &event, qint64 processId, st template void flushEvents(const Utils::span events, std::thread::id threadId, - EventQueue &eventQueue) + EnabledEventQueue &eventQueue) { if (events.empty()) return; @@ -47,12 +47,12 @@ void flushEvents(const Utils::span events, template void flushEvents(const Utils::span events, std::thread::id threadId, - EventQueue &eventQueue); + EnabledEventQueue &eventQueue); template void flushEvents(const Utils::span events, std::thread::id threadId, - EventQueue &eventQueue); + EnabledEventQueue &eventQueue); -void openFile(class TraceFile &file) +void openFile(EnabledTraceFile &file) { std::lock_guard lock{file.fileMutex}; @@ -60,7 +60,7 @@ void openFile(class TraceFile &file) file.out << std::fixed << std::setprecision(3) << R"({"traceEvents": [)"; } -void finalizeFile(class TraceFile &file) +void finalizeFile(EnabledTraceFile &file) { std::lock_guard lock{file.fileMutex}; auto &out = file.out; @@ -75,7 +75,7 @@ void finalizeFile(class TraceFile &file) } template -void flushInThread(EventQueue &eventQueue) +void flushInThread(EnabledEventQueue &eventQueue) { if (eventQueue.file->processing.valid()) eventQueue.file->processing.wait(); @@ -94,16 +94,16 @@ void flushInThread(EventQueue &eventQueue) eventQueue.eventsIndex = 0; } -template void flushInThread(EventQueue &eventQueue); -template void flushInThread(EventQueue &eventQueue); +template void flushInThread(EnabledEventQueue &eventQueue); +template void flushInThread(EnabledEventQueue &eventQueue); namespace { -TraceFile globalTraceFile{"global.json"}; +EnabledTraceFile globalTraceFile{"global.json"}; thread_local auto globalEventQueueData = makeEventQueueData(globalTraceFile); -thread_local EventQueue s_globalEventQueue = globalEventQueueData; +thread_local EventQueue s_globalEventQueue = globalEventQueueData.createEventQueue(); } // namespace -EventQueue &globalEventQueue() +EnabledEventQueue &globalEventQueue() { return s_globalEventQueue; } diff --git a/src/libs/nanotrace/nanotracehr.h b/src/libs/nanotrace/nanotracehr.h index 1adb6d586b2..7520574a7c3 100644 --- a/src/libs/nanotrace/nanotracehr.h +++ b/src/libs/nanotrace/nanotracehr.h @@ -61,31 +61,51 @@ using StringTraceEvent = TraceEvent; enum class IsEnabled { No, Yes }; -template +template class EventQueue; +template +using EnabledEventQueue = EventQueue; + template void flushEvents(const Utils::span events, std::thread::id threadId, - EventQueue &eventQueue); + EnabledEventQueue &eventQueue); extern template void flushEvents(const Utils::span events, std::thread::id threadId, - EventQueue &eventQueue); + EnabledEventQueue &eventQueue); extern template void flushEvents(const Utils::span events, std::thread::id threadId, - EventQueue &eventQueue); + EnabledEventQueue &eventQueue); template -void flushInThread(EventQueue &eventQueue); -extern template void flushInThread(EventQueue &eventQueue); -extern template void flushInThread(EventQueue &eventQueue); +void flushInThread(EnabledEventQueue &eventQueue); +extern template void flushInThread(EnabledEventQueue &eventQueue); +extern template void flushInThread(EnabledEventQueue &eventQueue); -void openFile(class TraceFile &file); -void finalizeFile(class TraceFile &file); +template +class TraceFile; +using EnabledTraceFile = TraceFile; + +void openFile(EnabledTraceFile &file); +void finalizeFile(EnabledTraceFile &file); + +template class TraceFile { public: + using IsActive = std::false_type; + + TraceFile(std::string_view) {} +}; + +template<> +class TraceFile +{ +public: + using IsActive = std::true_type; + TraceFile([[maybe_unused]] std::string_view filePath) : filePath{filePath} { @@ -104,13 +124,18 @@ public: std::ofstream out; }; -template +template class EventQueue +{}; + +template +class EventQueue { using TraceEventsSpan = Utils::span; public: EventQueue() = default; + ~EventQueue() { if (isEnabled == IsEnabled::Yes) @@ -122,7 +147,7 @@ public: EventQueue &operator=(const EventQueue &) = delete; EventQueue &operator=(EventQueue &&) = delete; - TraceFile *file = nullptr; + EnabledTraceFile *file = nullptr; TraceEventsSpan eventsOne; TraceEventsSpan eventsTwo; TraceEventsSpan currentEvents; @@ -130,25 +155,44 @@ public: IsEnabled isEnabled = IsEnabled::No; }; -template +template class EventQueueData { using TraceEvents = std::array; public: - EventQueueData(TraceFile &file) + using IsActive = Enabled; + + EventQueueData(TraceFile &) {} +}; + +template +class EventQueueData +{ + using TraceEvents = std::array; + +public: + using IsActive = std::true_type; + + EventQueueData(EnabledTraceFile &file) : file{file} {} - TraceFile &file; + EnabledTraceFile &file; TraceEvents eventsOne; TraceEvents eventsTwo; }; -template +template struct EventQueueDataPointer { - operator EventQueue() const + EventQueue createEventQueue() const { return {}; } +}; + +template +struct EventQueueDataPointer +{ + EnabledEventQueue createEventQueue() const { if constexpr (isTracerActive()) { return {&data->file, data->eventsOne, data->eventsTwo, data->eventsOne, 0, IsEnabled::Yes}; @@ -157,23 +201,25 @@ struct EventQueueDataPointer } } - std::unique_ptr> data; + std::unique_ptr> data; }; -template -EventQueueDataPointer makeEventQueueData(TraceFile &file) +template +EventQueueDataPointer makeEventQueueData( + TraceFile &file) { - if constexpr (isTracerActive()) { - return {std::make_unique>(file)}; + if constexpr (isTracerActive() && std::is_same_v) { + return {std::make_unique>( + file)}; } else { return {}; } } -EventQueue &globalEventQueue(); +EnabledEventQueue &globalEventQueue(); template -TraceEvent &getTraceEvent(EventQueue &eventQueue) +TraceEvent &getTraceEvent(EnabledEventQueue &eventQueue) { if (eventQueue.eventsIndex == eventQueue.currentEvents.size()) flushInThread(eventQueue); @@ -195,6 +241,7 @@ private: std::string_view text; }; + constexpr TracerLiteral operator""_t(const char *text, size_t size) { return {std::string_view{text, size}}; @@ -203,43 +250,52 @@ constexpr TracerLiteral operator""_t(const char *text, size_t size) using namespace Literals; -template +template class Category { +public: + using IsActive = std::false_type; + + Category(TracerLiteral, EventQueue &) {} + + Category(TracerLiteral, EventQueue &) {} +}; + +template +class Category +{ public: using IsActive = std::true_type; TracerLiteral name; - EventQueue &eventQueue; + EnabledEventQueue &eventQueue; }; -using StringViewCategory = Category; -using StringCategory = Category; +template +using StringViewCategory = Category; +template +using StringCategory = Category; class DisabledCategory -{}; - -template -Category(TracerLiteral name, EventQueue &eventQueue) -> Category; +{ + using IsActive = std::false_type; +}; template class Tracer { public: constexpr Tracer(TracerLiteral, Category &, TracerLiteral) {} + constexpr Tracer(TracerLiteral, Category &) {} ~Tracer() {} }; -template -class BasicTracer -{}; - template<> -class Tracer +class Tracer> { public: - constexpr Tracer(TracerLiteral name, StringViewCategory &category, TracerLiteral arguments) + constexpr Tracer(TracerLiteral name, StringViewCategory &category, TracerLiteral arguments) : m_name{name} , m_arguments{arguments} , m_category{category} @@ -250,7 +306,7 @@ public: } } - constexpr Tracer(TracerLiteral name, StringViewCategory &category) + constexpr Tracer(TracerLiteral name, StringViewCategory &category) : Tracer{name, category, "{}"_t} { if constexpr (isTracerActive()) { @@ -278,14 +334,14 @@ private: TimePoint m_start; std::string_view m_name; std::string_view m_arguments; - StringViewCategory &m_category; + StringViewCategory &m_category; }; template<> -class Tracer +class Tracer> { public: - Tracer(std::string name, StringViewCategory &category, std::string arguments) + Tracer(std::string name, StringViewCategory &category, std::string arguments) : m_name{std::move(name)} , m_arguments{arguments} , m_category{category} @@ -296,7 +352,7 @@ public: } } - Tracer(std::string name, StringViewCategory &category) + Tracer(std::string name, StringViewCategory &category) : Tracer{std::move(name), category, "{}"} { if constexpr (isTracerActive()) { @@ -324,7 +380,7 @@ private: TimePoint m_start; std::string m_name; std::string m_arguments; - StringViewCategory &m_category; + StringViewCategory &m_category; }; template diff --git a/src/libs/sqlite/sqlitebasestatement.cpp b/src/libs/sqlite/sqlitebasestatement.cpp index 6b1ad28cff9..79e8cd6db6b 100644 --- a/src/libs/sqlite/sqlitebasestatement.cpp +++ b/src/libs/sqlite/sqlitebasestatement.cpp @@ -26,24 +26,21 @@ extern "C" int sqlite3_carray_bind( namespace Sqlite { -#ifdef ENABLE_SQLITE_TRACING namespace { -NanotraceHR::TraceFile traceFile{"sqlite.json"}; +using TraceFile = NanotraceHR::TraceFile; + +TraceFile traceFile{"sqlite.json"}; thread_local auto eventQueueData = NanotraceHR::makeEventQueueData( traceFile); -thread_local NanotraceHR::EventQueue eventQueue = eventQueueData; +thread_local NanotraceHR::EventQueue eventQueue = eventQueueData.createEventQueue(); -NanotraceHR::Category sqliteLowLevelCategory{"sqlite low level"_t, - eventQueue}; +NanotraceHR::StringViewCategory sqliteLowLevelCategory{"sqlite low level"_t, + eventQueue}; } // namespace -NanotraceHR::Category sqliteHighLevelCategory{"sqlite high level"_t, - eventQueue}; - -#else -static NanotraceHR::DisabledCategory sqliteLowLevelCategory; -#endif +NanotraceHR::StringViewCategory sqliteHighLevelCategory{"sqlite high level"_t, + eventQueue}; BaseStatement::BaseStatement(Utils::SmallStringView sqlStatement, Database &database) : m_database(database) diff --git a/src/libs/sqlite/sqlitebasestatement.h b/src/libs/sqlite/sqlitebasestatement.h index 6c202625321..84575ec4dbe 100644 --- a/src/libs/sqlite/sqlitebasestatement.h +++ b/src/libs/sqlite/sqlitebasestatement.h @@ -44,11 +44,17 @@ constexpr static std::underlying_type_t to_underlying(Enumeration e return static_cast>(enumeration); } +constexpr bool sqliteTracingIsEnabled() +{ #ifdef ENABLE_SQLITE_TRACING -extern NanotraceHR::Category sqliteHighLevelCategory; + return NanotraceHR::isTracerActive(); #else -inline NanotraceHR::DisabledCategory sqliteHighLevelCategory; + return false; #endif +} + +SQLITE_EXPORT extern NanotraceHR::StringViewCategory sqliteHighLevelCategory; + class SQLITE_EXPORT BaseStatement { public: diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.cpp b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.cpp index 9819c2adcc4..48e36206a49 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.cpp +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.cpp @@ -7,18 +7,16 @@ namespace QmlDesigner { -#ifdef ENABLE_PROJECT_STORAGE_TRACING namespace { -NanotraceHR::TraceFile traceFile{"projectstorage.json"}; +NanotraceHR::TraceFile traceFile{"projectstorage.json"}; thread_local auto eventQueueData = NanotraceHR::makeEventQueueData( traceFile); -thread_local NanotraceHR::EventQueue eventQueue = eventQueueData; +thread_local NanotraceHR::EventQueue eventQueue = eventQueueData.createEventQueue(); } // namespace -NanotraceHR::Category projectStorageCategory{"project storage"_t, - eventQueue}; -#endif +NanotraceHR::StringViewCategory projectStorageCategory{"project storage"_t, + eventQueue}; } // namespace QmlDesigner template class QmlDesigner::ProjectStorage; diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h index d3963019440..0db827e4fbc 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h @@ -28,11 +28,16 @@ namespace QmlDesigner { using namespace NanotraceHR::Literals; +constexpr bool projectStorageTracingIsEnabled() +{ #ifdef ENABLE_PROJECT_STORAGE_TRACING -extern NanotraceHR::Category projectStorageCategory; + return NanotraceHR::isTracerActive(); #else -inline NanotraceHR::DisabledCategory projectStorageCategory; + return false; #endif +} + +extern NanotraceHR::StringViewCategory projectStorageCategory; template class ProjectStorage final : public ProjectStorageInterface From 4cbd05c30d1493c1a360012deb818ddf3b4b7be0 Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Thu, 12 Oct 2023 13:16:24 +0300 Subject: [PATCH 036/242] QmlDesigner: Show and edit the property type Task-number: QDS-10964 Change-Id: I271a17a1bc64eaac8af914a5aa00a7a34317f68b Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Mahmoud Badri Reviewed-by: Miikka Heikkinen --- .../EditPropertyDialog.qml | 39 ++++++- .../collectioneditor/collectiondetails.cpp | 49 +++++++++ .../collectioneditor/collectiondetails.h | 1 + .../singlecollectionmodel.cpp | 103 +++++++++++++++++- .../collectioneditor/singlecollectionmodel.h | 7 +- 5 files changed, 192 insertions(+), 7 deletions(-) diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/EditPropertyDialog.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/EditPropertyDialog.qml index 9aa377ea513..da273808dab 100644 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/EditPropertyDialog.qml +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/EditPropertyDialog.qml @@ -20,17 +20,25 @@ StudioControls.Dialog { if (root.__propertyIndex < 0) return - let previousName = root.model.headerData(root.__propertyIndex, Qt.Horizontal) + let previousName = root.model.propertyName(root.__propertyIndex) + let previousType = root.model.propertyType(root.__propertyIndex) oldName.text = previousName newNameField.text = previousName + propertyType.initialType = previousType + + forceChangeType.checked = false + root.open() } onAccepted: { if (newNameField.text !== "" && newNameField.text !== oldName.text) root.model.renameColumn(root.__propertyIndex, newNameField.text) + + if (propertyType.changed || forceChangeType.checked) + root.model.setPropertyType(root.__propertyIndex, propertyType.currentText, forceChangeType.checked) } contentItem: Column { @@ -73,6 +81,35 @@ StudioControls.Dialog { editButton.enabled = newNameField.text !== "" } } + + Text { + text: qsTr("Type") + color: StudioTheme.Values.themeTextColor + } + + StudioControls.ComboBox { + id: propertyType + + property string initialType + readonly property bool changed: propertyType.initialType !== propertyType.currentText + + model: root.model.typesList() + + onInitialTypeChanged: { + let propertyIndex = propertyType.find(initialType) + propertyType.currentIndex = propertyIndex + } + } + + StudioControls.CheckBox { + id: forceChangeType + actionIndicatorVisible: false + } + + Text { + text: qsTr("Force update values") + color: StudioTheme.Values.themeTextColor + } } Item { // spacer diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp index 8e2284d925a..60ab6ab8442 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp @@ -58,6 +58,27 @@ static CollectionProperty::DataType collectionDataTypeFromJsonValue(const QJsonV } } +static QVariant valueToVariant(const QJsonValue &value, CollectionDetails::DataType type) +{ + using DataType = CollectionDetails::DataType; + QVariant variantValue = value.toVariant(); + + switch (type) { + case DataType::String: + return variantValue.toString(); + case DataType::Number: + return variantValue.toDouble(); + case DataType::Boolean: + return variantValue.toBool(); + case DataType::Color: + return variantValue.value(); + case DataType::Url: + return variantValue.value(); + default: + return variantValue; + } +} + CollectionDetails::CollectionDetails() : d(new Private()) {} @@ -221,6 +242,34 @@ bool CollectionDetails::setPropertyName(int column, const QString &value) return true; } +bool CollectionDetails::forcePropertyType(int column, DataType type, bool force) +{ + if (!isValid() || !d->isValidColumnId(column)) + return false; + + bool changed = false; + CollectionProperty &property = d->properties[column]; + if (property.type != type) + changed = true; + + property.type = type; + + if (force) { + for (QJsonObject &element : d->elements) { + if (element.contains(property.name)) { + QJsonValue value = element.value(property.name); + element.insert(property.name, valueToVariant(value, type).toJsonValue()); + changed = true; + } + } + } + + if (changed) + markChanged(); + + return changed; +} + CollectionReference CollectionDetails::reference() const { return d->reference; diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.h b/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.h index 3ff788eaa7f..20896c67923 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.h +++ b/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.h @@ -59,6 +59,7 @@ public: bool removeElements(int row, int count = 1); bool setPropertyName(int column, const QString &value); + bool forcePropertyType(int column, DataType type, bool force = false); CollectionReference reference() const; CollectionEditor::SourceFormat sourceFormat() const; diff --git a/src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.cpp b/src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.cpp index bd025b3645e..d35fb7303c5 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.cpp @@ -30,6 +30,57 @@ QStringList getJsonHeaders(const QJsonArray &collectionArray) return result.values(); } + +class CollectionDataTypeHelper +{ +public: + using DataType = QmlDesigner::CollectionDetails::DataType; + + static QString typeToString(DataType dataType) + { + static const QHash typeStringHash = typeToStringHash(); + return typeStringHash.value(dataType); + } + + static DataType typeFromString(const QString &dataType) + { + static const QHash stringTypeHash = stringToTypeHash(); + return stringTypeHash.value(dataType); + } + + static QStringList typesStringList() + { + static const QStringList typesList = typeToStringHash().values(); + return typesList; + } + +private: + CollectionDataTypeHelper() = delete; + + static QHash typeToStringHash() + { + return { + {DataType::Unknown, "Unknown"}, + {DataType::String, "String"}, + {DataType::Url, "Url"}, + {DataType::Number, "Number"}, + {DataType::Boolean, "Boolean"}, + {DataType::Image, "Image"}, + {DataType::Color, "Color"}, + }; + } + + static QHash stringToTypeHash() + { + QHash stringTypeHash; + const QHash typeStringHash = typeToStringHash(); + for (const auto &transferItem : typeStringHash.asKeyValueRange()) + stringTypeHash.insert(transferItem.second, transferItem.first); + + return stringTypeHash; + } +}; + } // namespace namespace QmlDesigner { @@ -48,6 +99,7 @@ QHash SingleCollectionModel::roleNames() const if (roles.isEmpty()) { roles.insert(QAbstractTableModel::roleNames()); roles.insert(SelectedRole, "itemSelected"); + roles.insert(DataTypeRole, "dataType"); } return roles; } @@ -140,12 +192,19 @@ Qt::ItemFlags SingleCollectionModel::flags(const QModelIndex &index) const return {Qt::ItemIsSelectable | Qt::ItemIsEnabled}; } -QVariant SingleCollectionModel::headerData(int section, - Qt::Orientation orientation, - [[maybe_unused]] int role) const +QVariant SingleCollectionModel::headerData(int section, Qt::Orientation orientation, int role) const { - if (orientation == Qt::Horizontal) - return m_currentCollection.propertyAt(section); + if (orientation == Qt::Horizontal) { + if (role == DataTypeRole) { + return CollectionDataTypeHelper::typeToString(m_currentCollection.typeAt(section)); + } else if (role == Qt::DisplayRole) { + return QString("%1 <%2>").arg(m_currentCollection.propertyAt(section), + CollectionDataTypeHelper::typeToString( + m_currentCollection.typeAt(section))); + } else { + return m_currentCollection.propertyAt(section); + } + } if (orientation == Qt::Vertical) return section + 1; @@ -163,6 +222,16 @@ int SingleCollectionModel::selectedRow() const return m_selectedRow; } +QString SingleCollectionModel::propertyName(int column) const +{ + return m_currentCollection.propertyAt(column); +} + +QString SingleCollectionModel::propertyType(int column) const +{ + return CollectionDataTypeHelper::typeToString(m_currentCollection.typeAt(column)); +} + bool SingleCollectionModel::isPropertyAvailable(const QString &name) { return m_currentCollection.containsPropertyName(name); @@ -219,6 +288,25 @@ bool SingleCollectionModel::renameColumn(int section, const QString &newValue) return setHeaderData(section, Qt::Horizontal, newValue); } +bool SingleCollectionModel::setPropertyType(int column, const QString &newValue, bool force) +{ + bool changed = m_currentCollection.forcePropertyType(column, + CollectionDataTypeHelper::typeFromString( + newValue), + force); + if (changed) { + emit headerDataChanged(Qt::Horizontal, column, column); + + if (force) { + emit dataChanged(index(0, column), + index(rowCount() - 1, column), + {Qt::DisplayRole, DataTypeRole}); + } + } + + return changed; +} + bool SingleCollectionModel::selectRow(int row) { if (m_selectedRow == row) @@ -254,6 +342,11 @@ void SingleCollectionModel::deselectAll() selectRow(-1); } +QStringList SingleCollectionModel::typesList() +{ + return CollectionDataTypeHelper::typesStringList(); +} + void SingleCollectionModel::loadCollection(const ModelNode &sourceNode, const QString &collection) { QString fileName = sourceNode.variantProperty(CollectionEditor::SOURCEFILE_PROPERTY).value().toString(); diff --git a/src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.h b/src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.h index b1d5d0308af..3327c8b01f5 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.h +++ b/src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.h @@ -22,7 +22,7 @@ class SingleCollectionModel : public QAbstractTableModel Q_PROPERTY(bool isEmpty MEMBER m_isEmpty NOTIFY isEmptyChanged) public: - enum DataRoles { SelectedRole = Qt::UserRole + 1 }; + enum DataRoles { SelectedRole = Qt::UserRole + 1, DataTypeRole }; explicit SingleCollectionModel(QObject *parent = nullptr); @@ -45,16 +45,21 @@ public: int role = Qt::DisplayRole) const override; int selectedColumn() const; int selectedRow() const; + Q_INVOKABLE QString propertyName(int column) const; + Q_INVOKABLE QString propertyType(int column) const; Q_INVOKABLE bool isPropertyAvailable(const QString &name); Q_INVOKABLE bool addColumn(int column, const QString &name); Q_INVOKABLE bool selectColumn(int section); Q_INVOKABLE bool renameColumn(int section, const QString &newValue); + Q_INVOKABLE bool setPropertyType(int column, const QString &newValue, bool force = false); Q_INVOKABLE bool selectRow(int row); Q_INVOKABLE void deselectAll(); + static Q_INVOKABLE QStringList typesList(); + void loadCollection(const ModelNode &sourceNode, const QString &collection); signals: From 5f227371773c108e7e62b17e978971fba4eb0e22 Mon Sep 17 00:00:00 2001 From: Burak Hancerli Date: Thu, 12 Oct 2023 14:39:33 +0200 Subject: [PATCH 037/242] 3rdparty: Add QrCodeGenerator library This library is required for QmlDesigner plugin to generate QR codes used in Design Viewer integration. Task-number: QDS-10485 Change-Id: Ic4b565fe56f682c8af310ad873f48caf74d05aef Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Eike Ziller Reviewed-by: Thomas Hartmann --- README.md | 28 + .../overview/creator-acknowledgements.qdoc | 12 + src/libs/3rdparty/CMakeLists.txt | 1 + src/libs/3rdparty/qrcodegen/CMakeLists.txt | 11 + src/libs/3rdparty/qrcodegen/LICENSE | 21 + src/libs/3rdparty/qrcodegen/README.md | 25 + src/libs/3rdparty/qrcodegen/src/qrcodegen.cpp | 863 ++++++++++++++++++ src/libs/3rdparty/qrcodegen/src/qrcodegen.h | 566 ++++++++++++ .../qrcodegen/src/qrcodeimageprovider.cpp | 89 ++ .../qrcodegen/src/qrcodeimageprovider.h | 36 + 10 files changed, 1652 insertions(+) create mode 100644 src/libs/3rdparty/qrcodegen/CMakeLists.txt create mode 100644 src/libs/3rdparty/qrcodegen/LICENSE create mode 100644 src/libs/3rdparty/qrcodegen/README.md create mode 100644 src/libs/3rdparty/qrcodegen/src/qrcodegen.cpp create mode 100644 src/libs/3rdparty/qrcodegen/src/qrcodegen.h create mode 100644 src/libs/3rdparty/qrcodegen/src/qrcodeimageprovider.cpp create mode 100644 src/libs/3rdparty/qrcodegen/src/qrcodeimageprovider.h diff --git a/README.md b/README.md index 7d6102f94f2..79a797fcfaa 100644 --- a/README.md +++ b/README.md @@ -934,6 +934,34 @@ SQLite (https://www.sqlite.org) is in the Public Domain. OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +### QrCodeGenerator + + The QML Designer plugin uses QR Code Generator for Design Viewer integration. + + https://github.com/alex-spataru/Qt-QrCodeGenerator + + MIT License + + Copyright (c) 2023 Alex Spataru + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + ### cmake The CMake project manager uses the CMake lexer code for parsing CMake files diff --git a/doc/qtcreator/src/overview/creator-acknowledgements.qdoc b/doc/qtcreator/src/overview/creator-acknowledgements.qdoc index 4120ff4b26b..37c105a22ad 100644 --- a/doc/qtcreator/src/overview/creator-acknowledgements.qdoc +++ b/doc/qtcreator/src/overview/creator-acknowledgements.qdoc @@ -889,6 +889,18 @@ \include license-mit.qdocinc + \li \b QrCodeGenerator + + The QML Designer plugin uses QR Code Generator for Design Viewer integration. + + \list + \li \l https://github.com/alex-spataru/Qt-QrCodeGenerator + \endlist + + Distributed under the MIT license. + + \include license-mit.qdocinc + \li \b cmake The CMake project manager uses the CMake lexer code for parsing CMake files. diff --git a/src/libs/3rdparty/CMakeLists.txt b/src/libs/3rdparty/CMakeLists.txt index 0cf9818ed54..60a90ddffe5 100644 --- a/src/libs/3rdparty/CMakeLists.txt +++ b/src/libs/3rdparty/CMakeLists.txt @@ -2,6 +2,7 @@ add_subdirectory(cplusplus) add_subdirectory(syntax-highlighting) add_subdirectory(libvterm) add_subdirectory(libptyqt) +add_subdirectory(qrcodegen) if(WIN32) add_subdirectory(winpty) diff --git a/src/libs/3rdparty/qrcodegen/CMakeLists.txt b/src/libs/3rdparty/qrcodegen/CMakeLists.txt new file mode 100644 index 00000000000..2fff8d04373 --- /dev/null +++ b/src/libs/3rdparty/qrcodegen/CMakeLists.txt @@ -0,0 +1,11 @@ +add_qtc_library(QrCodeGenerator STATIC + CONDITION BUILD_DESIGNSTUDIO + PUBLIC_INCLUDES ${CMAKE_CURRENT_SOURCE_DIR}/src + DEPENDS + Qt6::Qml Qt6::Quick Qt6::Svg Qt6::QuickWidgets + SOURCES + src/qrcodegen.cpp + src/qrcodegen.h + src/qrcodeimageprovider.cpp + src/qrcodeimageprovider.h +) diff --git a/src/libs/3rdparty/qrcodegen/LICENSE b/src/libs/3rdparty/qrcodegen/LICENSE new file mode 100644 index 00000000000..da7a8e0aae4 --- /dev/null +++ b/src/libs/3rdparty/qrcodegen/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 Alex Spataru + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/src/libs/3rdparty/qrcodegen/README.md b/src/libs/3rdparty/qrcodegen/README.md new file mode 100644 index 00000000000..dc811384775 --- /dev/null +++ b/src/libs/3rdparty/qrcodegen/README.md @@ -0,0 +1,25 @@ +# Qt QR Code Generator Library + +Qt QR Code Generator is a simple C++ class that uses the [qrcodegen](https://github.com/nayuki/QR-Code-generator) library to generate QR codes from QStrings in Qt applications. + +[![Screenshot](example/screenshot.png)](example/screenshot.png) + +## Usage + +1. Copy the *Qt-QrCodeGenerator* folder in your `lib` folder. +2. Include the *Qt-QrCodeGenerator* project include (pri) file using the `include()` qmake function. +3. Use the `QrCodeGenerator` class in your code: + +```cpp +#include + +QrCodeGenerator generator; +QString data = "https://www.example.com"; +QImage qrCodeImage = generator.generateQr(data); +``` + +4. That's all! Check the [example](example) project as a reference for your project if needed. + +## License + +This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details. diff --git a/src/libs/3rdparty/qrcodegen/src/qrcodegen.cpp b/src/libs/3rdparty/qrcodegen/src/qrcodegen.cpp new file mode 100644 index 00000000000..e594b8b6b75 --- /dev/null +++ b/src/libs/3rdparty/qrcodegen/src/qrcodegen.cpp @@ -0,0 +1,863 @@ +/* + * QR Code generator library (C++) + * + * Copyright (c) Project Nayuki. (MIT License) + * https://www.nayuki.io/page/qr-code-generator-library + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * - The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * - The Software is provided "as is", without warranty of any kind, express or + * implied, including but not limited to the warranties of merchantability, + * fitness for a particular purpose and noninfringement. In no event shall the + * authors or copyright holders be liable for any claim, damages or other + * liability, whether in an action of contract, tort or otherwise, arising from, + * out of or in connection with the Software or the use or other dealings in the + * Software. + */ + +#include "qrcodegen.h" +#include +#include +#include +#include +#include +#include +#include +#include + +using std::int8_t; +using std::size_t; +using std::uint8_t; +using std::vector; + +namespace qrcodegen { + +/*---- Class QrSegment ----*/ + +QrSegment::Mode::Mode(int mode, int cc0, int cc1, int cc2) + : modeBits(mode) +{ + numBitsCharCount[0] = cc0; + numBitsCharCount[1] = cc1; + numBitsCharCount[2] = cc2; +} + +int QrSegment::Mode::getModeBits() const +{ + return modeBits; +} + +int QrSegment::Mode::numCharCountBits(int ver) const +{ + return numBitsCharCount[(ver + 7) / 17]; +} + +const QrSegment::Mode QrSegment::Mode::NUMERIC(0x1, 10, 12, 14); +const QrSegment::Mode QrSegment::Mode::ALPHANUMERIC(0x2, 9, 11, 13); +const QrSegment::Mode QrSegment::Mode::BYTE(0x4, 8, 16, 16); +const QrSegment::Mode QrSegment::Mode::KANJI(0x8, 8, 10, 12); +const QrSegment::Mode QrSegment::Mode::ECI(0x7, 0, 0, 0); + +QrSegment QrSegment::makeBytes(const vector &data) +{ + if (data.size() > static_cast(INT_MAX)) + throw std::length_error("Data too long"); + BitBuffer bb; + for (uint8_t b : data) + bb.appendBits(b, 8); + return QrSegment(Mode::BYTE, static_cast(data.size()), std::move(bb)); +} + +QrSegment QrSegment::makeNumeric(const char *digits) +{ + BitBuffer bb; + int accumData = 0; + int accumCount = 0; + int charCount = 0; + for (; *digits != '\0'; digits++, charCount++) { + char c = *digits; + if (c < '0' || c > '9') + throw std::domain_error("String contains non-numeric characters"); + accumData = accumData * 10 + (c - '0'); + accumCount++; + if (accumCount == 3) { + bb.appendBits(static_cast(accumData), 10); + accumData = 0; + accumCount = 0; + } + } + if (accumCount > 0) // 1 or 2 digits remaining + bb.appendBits(static_cast(accumData), accumCount * 3 + 1); + return QrSegment(Mode::NUMERIC, charCount, std::move(bb)); +} + +QrSegment QrSegment::makeAlphanumeric(const char *text) +{ + BitBuffer bb; + int accumData = 0; + int accumCount = 0; + int charCount = 0; + for (; *text != '\0'; text++, charCount++) { + const char *temp = std::strchr(ALPHANUMERIC_CHARSET, *text); + if (temp == nullptr) + throw std::domain_error("String contains unencodable characters in alphanumeric mode"); + accumData = accumData * 45 + static_cast(temp - ALPHANUMERIC_CHARSET); + accumCount++; + if (accumCount == 2) { + bb.appendBits(static_cast(accumData), 11); + accumData = 0; + accumCount = 0; + } + } + if (accumCount > 0) // 1 character remaining + bb.appendBits(static_cast(accumData), 6); + return QrSegment(Mode::ALPHANUMERIC, charCount, std::move(bb)); +} + +vector QrSegment::makeSegments(const char *text) +{ + // Select the most efficient segment encoding automatically + vector result; + if (*text == '\0') + ; // Leave result empty + else if (isNumeric(text)) + result.push_back(makeNumeric(text)); + else if (isAlphanumeric(text)) + result.push_back(makeAlphanumeric(text)); + else { + vector bytes; + for (; *text != '\0'; text++) + bytes.push_back(static_cast(*text)); + result.push_back(makeBytes(bytes)); + } + return result; +} + +QrSegment QrSegment::makeEci(long assignVal) +{ + BitBuffer bb; + if (assignVal < 0) + throw std::domain_error("ECI assignment value out of range"); + else if (assignVal < (1 << 7)) + bb.appendBits(static_cast(assignVal), 8); + else if (assignVal < (1 << 14)) { + bb.appendBits(2, 2); + bb.appendBits(static_cast(assignVal), 14); + } else if (assignVal < 1000000L) { + bb.appendBits(6, 3); + bb.appendBits(static_cast(assignVal), 21); + } else + throw std::domain_error("ECI assignment value out of range"); + return QrSegment(Mode::ECI, 0, std::move(bb)); +} + +QrSegment::QrSegment(const Mode &md, int numCh, const std::vector &dt) + : mode(&md) + , numChars(numCh) + , data(dt) +{ + if (numCh < 0) + throw std::domain_error("Invalid value"); +} + +QrSegment::QrSegment(const Mode &md, int numCh, std::vector &&dt) + : mode(&md) + , numChars(numCh) + , data(std::move(dt)) +{ + if (numCh < 0) + throw std::domain_error("Invalid value"); +} + +int QrSegment::getTotalBits(const vector &segs, int version) +{ + int result = 0; + for (const QrSegment &seg : segs) { + int ccbits = seg.mode->numCharCountBits(version); + if (seg.numChars >= (1L << ccbits)) + return -1; // The segment's length doesn't fit the field's bit width + if (4 + ccbits > INT_MAX - result) + return -1; // The sum will overflow an int type + result += 4 + ccbits; + if (seg.data.size() > static_cast(INT_MAX - result)) + return -1; // The sum will overflow an int type + result += static_cast(seg.data.size()); + } + return result; +} + +bool QrSegment::isNumeric(const char *text) +{ + for (; *text != '\0'; text++) { + char c = *text; + if (c < '0' || c > '9') + return false; + } + return true; +} + +bool QrSegment::isAlphanumeric(const char *text) +{ + for (; *text != '\0'; text++) { + if (std::strchr(ALPHANUMERIC_CHARSET, *text) == nullptr) + return false; + } + return true; +} + +const QrSegment::Mode &QrSegment::getMode() const +{ + return *mode; +} + +int QrSegment::getNumChars() const +{ + return numChars; +} + +const std::vector &QrSegment::getData() const +{ + return data; +} + +const char *QrSegment::ALPHANUMERIC_CHARSET = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:"; + +/*---- Class QrCode ----*/ + +int QrCode::getFormatBits(Ecc ecl) +{ + switch (ecl) { + case Ecc::LOW: + return 1; + case Ecc::MEDIUM: + return 0; + case Ecc::QUARTILE: + return 3; + case Ecc::HIGH: + return 2; + default: + throw std::logic_error("Unreachable"); + } +} + +QrCode QrCode::encodeText(const char *text, Ecc ecl) +{ + vector segs = QrSegment::makeSegments(text); + return encodeSegments(segs, ecl); +} + +QrCode QrCode::encodeBinary(const vector &data, Ecc ecl) +{ + vector segs{QrSegment::makeBytes(data)}; + return encodeSegments(segs, ecl); +} + +QrCode QrCode::encodeSegments( + const vector &segs, Ecc ecl, int minVersion, int maxVersion, int mask, bool boostEcl) +{ + if (!(MIN_VERSION <= minVersion && minVersion <= maxVersion && maxVersion <= MAX_VERSION) + || mask < -1 || mask > 7) + throw std::invalid_argument("Invalid value"); + + // Find the minimal version number to use + int version, dataUsedBits; + for (version = minVersion;; version++) { + int dataCapacityBits = getNumDataCodewords(version, ecl) * 8; // Number of data bits available + dataUsedBits = QrSegment::getTotalBits(segs, version); + if (dataUsedBits != -1 && dataUsedBits <= dataCapacityBits) + break; // This version number is found to be suitable + if (version >= maxVersion) { // All versions in the range could not fit the given data + std::ostringstream sb; + if (dataUsedBits == -1) + sb << "Segment too long"; + else { + sb << "Data length = " << dataUsedBits << " bits, "; + sb << "Max capacity = " << dataCapacityBits << " bits"; + } + throw data_too_long(sb.str()); + } + } + assert(dataUsedBits != -1); + + // Increase the error correction level while the data still fits in the current version number + for (Ecc newEcl : {Ecc::MEDIUM, Ecc::QUARTILE, Ecc::HIGH}) { // From low to high + if (boostEcl && dataUsedBits <= getNumDataCodewords(version, newEcl) * 8) + ecl = newEcl; + } + + // Concatenate all segments to create the data bit string + BitBuffer bb; + for (const QrSegment &seg : segs) { + bb.appendBits(static_cast(seg.getMode().getModeBits()), 4); + bb.appendBits(static_cast(seg.getNumChars()), + seg.getMode().numCharCountBits(version)); + bb.insert(bb.end(), seg.getData().begin(), seg.getData().end()); + } + assert(bb.size() == static_cast(dataUsedBits)); + + // Add terminator and pad up to a byte if applicable + size_t dataCapacityBits = static_cast(getNumDataCodewords(version, ecl)) * 8; + assert(bb.size() <= dataCapacityBits); + bb.appendBits(0, std::min(4, static_cast(dataCapacityBits - bb.size()))); + bb.appendBits(0, (8 - static_cast(bb.size() % 8)) % 8); + assert(bb.size() % 8 == 0); + + // Pad with alternating bytes until data capacity is reached + for (uint8_t padByte = 0xEC; bb.size() < dataCapacityBits; padByte ^= 0xEC ^ 0x11) + bb.appendBits(padByte, 8); + + // Pack bits into bytes in big endian + vector dataCodewords(bb.size() / 8); + for (size_t i = 0; i < bb.size(); i++) + dataCodewords.at(i >> 3) |= (bb.at(i) ? 1 : 0) << (7 - (i & 7)); + + // Create the QR Code object + return QrCode(version, ecl, dataCodewords, mask); +} + +QrCode::QrCode(int ver, Ecc ecl, const vector &dataCodewords, int msk) + : // Initialize fields and check arguments + version(ver) + , errorCorrectionLevel(ecl) +{ + if (ver < MIN_VERSION || ver > MAX_VERSION) + throw std::domain_error("Version value out of range"); + if (msk < -1 || msk > 7) + throw std::domain_error("Mask value out of range"); + size = ver * 4 + 17; + size_t sz = static_cast(size); + modules = vector>(sz, vector(sz)); // Initially all light + isFunction = vector>(sz, vector(sz)); + + // Compute ECC, draw modules + drawFunctionPatterns(); + const vector allCodewords = addEccAndInterleave(dataCodewords); + drawCodewords(allCodewords); + + // Do masking + if (msk == -1) { // Automatically choose best mask + long minPenalty = LONG_MAX; + for (int i = 0; i < 8; i++) { + applyMask(i); + drawFormatBits(i); + long penalty = getPenaltyScore(); + if (penalty < minPenalty) { + msk = i; + minPenalty = penalty; + } + applyMask(i); // Undoes the mask due to XOR + } + } + assert(0 <= msk && msk <= 7); + mask = msk; + applyMask(msk); // Apply the final choice of mask + drawFormatBits(msk); // Overwrite old format bits + + isFunction.clear(); + isFunction.shrink_to_fit(); +} + +int QrCode::getVersion() const +{ + return version; +} + +int QrCode::getSize() const +{ + return size; +} + +QrCode::Ecc QrCode::getErrorCorrectionLevel() const +{ + return errorCorrectionLevel; +} + +int QrCode::getMask() const +{ + return mask; +} + +bool QrCode::getModule(int x, int y) const +{ + return 0 <= x && x < size && 0 <= y && y < size && module(x, y); +} + +void QrCode::drawFunctionPatterns() +{ + // Draw horizontal and vertical timing patterns + for (int i = 0; i < size; i++) { + setFunctionModule(6, i, i % 2 == 0); + setFunctionModule(i, 6, i % 2 == 0); + } + + // Draw 3 finder patterns (all corners except bottom right; overwrites some timing modules) + drawFinderPattern(3, 3); + drawFinderPattern(size - 4, 3); + drawFinderPattern(3, size - 4); + + // Draw numerous alignment patterns + const vector alignPatPos = getAlignmentPatternPositions(); + size_t numAlign = alignPatPos.size(); + for (size_t i = 0; i < numAlign; i++) { + for (size_t j = 0; j < numAlign; j++) { + // Don't draw on the three finder corners + if (!((i == 0 && j == 0) || (i == 0 && j == numAlign - 1) || (i == numAlign - 1 && j == 0))) + drawAlignmentPattern(alignPatPos.at(i), alignPatPos.at(j)); + } + } + + // Draw configuration data + drawFormatBits(0); // Dummy mask value; overwritten later in the constructor + drawVersion(); +} + +void QrCode::drawFormatBits(int msk) +{ + // Calculate error correction code and pack bits + int data = getFormatBits(errorCorrectionLevel) << 3 | msk; // errCorrLvl is uint2, msk is uint3 + int rem = data; + for (int i = 0; i < 10; i++) + rem = (rem << 1) ^ ((rem >> 9) * 0x537); + int bits = (data << 10 | rem) ^ 0x5412; // uint15 + assert(bits >> 15 == 0); + + // Draw first copy + for (int i = 0; i <= 5; i++) + setFunctionModule(8, i, getBit(bits, i)); + setFunctionModule(8, 7, getBit(bits, 6)); + setFunctionModule(8, 8, getBit(bits, 7)); + setFunctionModule(7, 8, getBit(bits, 8)); + for (int i = 9; i < 15; i++) + setFunctionModule(14 - i, 8, getBit(bits, i)); + + // Draw second copy + for (int i = 0; i < 8; i++) + setFunctionModule(size - 1 - i, 8, getBit(bits, i)); + for (int i = 8; i < 15; i++) + setFunctionModule(8, size - 15 + i, getBit(bits, i)); + setFunctionModule(8, size - 8, true); // Always dark +} + +void QrCode::drawVersion() +{ + if (version < 7) + return; + + // Calculate error correction code and pack bits + int rem = version; // version is uint6, in the range [7, 40] + for (int i = 0; i < 12; i++) + rem = (rem << 1) ^ ((rem >> 11) * 0x1F25); + long bits = static_cast(version) << 12 | rem; // uint18 + assert(bits >> 18 == 0); + + // Draw two copies + for (int i = 0; i < 18; i++) { + bool bit = getBit(bits, i); + int a = size - 11 + i % 3; + int b = i / 3; + setFunctionModule(a, b, bit); + setFunctionModule(b, a, bit); + } +} + +void QrCode::drawFinderPattern(int x, int y) +{ + for (int dy = -4; dy <= 4; dy++) { + for (int dx = -4; dx <= 4; dx++) { + int dist = std::max(std::abs(dx), std::abs(dy)); // Chebyshev/infinity norm + int xx = x + dx, yy = y + dy; + if (0 <= xx && xx < size && 0 <= yy && yy < size) + setFunctionModule(xx, yy, dist != 2 && dist != 4); + } + } +} + +void QrCode::drawAlignmentPattern(int x, int y) +{ + for (int dy = -2; dy <= 2; dy++) { + for (int dx = -2; dx <= 2; dx++) + setFunctionModule(x + dx, y + dy, std::max(std::abs(dx), std::abs(dy)) != 1); + } +} + +void QrCode::setFunctionModule(int x, int y, bool isDark) +{ + size_t ux = static_cast(x); + size_t uy = static_cast(y); + modules.at(uy).at(ux) = isDark; + isFunction.at(uy).at(ux) = true; +} + +bool QrCode::module(int x, int y) const +{ + return modules.at(static_cast(y)).at(static_cast(x)); +} + +vector QrCode::addEccAndInterleave(const vector &data) const +{ + if (data.size() != static_cast(getNumDataCodewords(version, errorCorrectionLevel))) + throw std::invalid_argument("Invalid argument"); + + // Calculate parameter numbers + int numBlocks = NUM_ERROR_CORRECTION_BLOCKS[static_cast(errorCorrectionLevel)][version]; + int blockEccLen = ECC_CODEWORDS_PER_BLOCK[static_cast(errorCorrectionLevel)][version]; + int rawCodewords = getNumRawDataModules(version) / 8; + int numShortBlocks = numBlocks - rawCodewords % numBlocks; + int shortBlockLen = rawCodewords / numBlocks; + + // Split data into blocks and append ECC to each block + vector> blocks; + const vector rsDiv = reedSolomonComputeDivisor(blockEccLen); + for (int i = 0, k = 0; i < numBlocks; i++) { + vector dat(data.cbegin() + k, + data.cbegin() + + (k + shortBlockLen - blockEccLen + (i < numShortBlocks ? 0 : 1))); + k += static_cast(dat.size()); + const vector ecc = reedSolomonComputeRemainder(dat, rsDiv); + if (i < numShortBlocks) + dat.push_back(0); + dat.insert(dat.end(), ecc.cbegin(), ecc.cend()); + blocks.push_back(std::move(dat)); + } + + // Interleave (not concatenate) the bytes from every block into a single sequence + vector result; + for (size_t i = 0; i < blocks.at(0).size(); i++) { + for (size_t j = 0; j < blocks.size(); j++) { + // Skip the padding byte in short blocks + if (i != static_cast(shortBlockLen - blockEccLen) + || j >= static_cast(numShortBlocks)) + result.push_back(blocks.at(j).at(i)); + } + } + assert(result.size() == static_cast(rawCodewords)); + return result; +} + +void QrCode::drawCodewords(const vector &data) +{ + if (data.size() != static_cast(getNumRawDataModules(version) / 8)) + throw std::invalid_argument("Invalid argument"); + + size_t i = 0; // Bit index into the data + // Do the funny zigzag scan + for (int right = size - 1; right >= 1; right -= 2) { // Index of right column in each column pair + if (right == 6) + right = 5; + for (int vert = 0; vert < size; vert++) { // Vertical counter + for (int j = 0; j < 2; j++) { + size_t x = static_cast(right - j); // Actual x coordinate + bool upward = ((right + 1) & 2) == 0; + size_t y = static_cast(upward ? size - 1 - vert : vert); // Actual y coordinate + if (!isFunction.at(y).at(x) && i < data.size() * 8) { + modules.at(y).at(x) = getBit(data.at(i >> 3), 7 - static_cast(i & 7)); + i++; + } + // If this QR Code has any remainder bits (0 to 7), they were assigned as + // 0/false/light by the constructor and are left unchanged by this method + } + } + } + assert(i == data.size() * 8); +} + +void QrCode::applyMask(int msk) +{ + if (msk < 0 || msk > 7) + throw std::domain_error("Mask value out of range"); + size_t sz = static_cast(size); + for (size_t y = 0; y < sz; y++) { + for (size_t x = 0; x < sz; x++) { + bool invert; + switch (msk) { + case 0: + invert = (x + y) % 2 == 0; + break; + case 1: + invert = y % 2 == 0; + break; + case 2: + invert = x % 3 == 0; + break; + case 3: + invert = (x + y) % 3 == 0; + break; + case 4: + invert = (x / 3 + y / 2) % 2 == 0; + break; + case 5: + invert = x * y % 2 + x * y % 3 == 0; + break; + case 6: + invert = (x * y % 2 + x * y % 3) % 2 == 0; + break; + case 7: + invert = ((x + y) % 2 + x * y % 3) % 2 == 0; + break; + default: + throw std::logic_error("Unreachable"); + } + modules.at(y).at(x) = modules.at(y).at(x) ^ (invert & !isFunction.at(y).at(x)); + } + } +} + +long QrCode::getPenaltyScore() const +{ + long result = 0; + + // Adjacent modules in row having same color, and finder-like patterns + for (int y = 0; y < size; y++) { + bool runColor = false; + int runX = 0; + std::array runHistory = {}; + for (int x = 0; x < size; x++) { + if (module(x, y) == runColor) { + runX++; + if (runX == 5) + result += PENALTY_N1; + else if (runX > 5) + result++; + } else { + finderPenaltyAddHistory(runX, runHistory); + if (!runColor) + result += finderPenaltyCountPatterns(runHistory) * PENALTY_N3; + runColor = module(x, y); + runX = 1; + } + } + result += finderPenaltyTerminateAndCount(runColor, runX, runHistory) * PENALTY_N3; + } + // Adjacent modules in column having same color, and finder-like patterns + for (int x = 0; x < size; x++) { + bool runColor = false; + int runY = 0; + std::array runHistory = {}; + for (int y = 0; y < size; y++) { + if (module(x, y) == runColor) { + runY++; + if (runY == 5) + result += PENALTY_N1; + else if (runY > 5) + result++; + } else { + finderPenaltyAddHistory(runY, runHistory); + if (!runColor) + result += finderPenaltyCountPatterns(runHistory) * PENALTY_N3; + runColor = module(x, y); + runY = 1; + } + } + result += finderPenaltyTerminateAndCount(runColor, runY, runHistory) * PENALTY_N3; + } + + // 2*2 blocks of modules having same color + for (int y = 0; y < size - 1; y++) { + for (int x = 0; x < size - 1; x++) { + bool color = module(x, y); + if (color == module(x + 1, y) && color == module(x, y + 1) + && color == module(x + 1, y + 1)) + result += PENALTY_N2; + } + } + + // Balance of dark and light modules + int dark = 0; + for (const vector &row : modules) { + for (bool color : row) { + if (color) + dark++; + } + } + int total = size * size; // Note that size is odd, so dark/total != 1/2 + // Compute the smallest integer k >= 0 such that (45-5k)% <= dark/total <= (55+5k)% + int k = static_cast((std::abs(dark * 20L - total * 10L) + total - 1) / total) - 1; + assert(0 <= k && k <= 9); + result += k * PENALTY_N4; + assert(0 <= result + && result <= 2568888L); // Non-tight upper bound based on default values of PENALTY_N1, ..., N4 + return result; +} + +vector QrCode::getAlignmentPatternPositions() const +{ + if (version == 1) + return vector(); + else { + int numAlign = version / 7 + 2; + int step = (version == 32) ? 26 : (version * 4 + numAlign * 2 + 1) / (numAlign * 2 - 2) * 2; + vector result; + for (int i = 0, pos = size - 7; i < numAlign - 1; i++, pos -= step) + result.insert(result.begin(), pos); + result.insert(result.begin(), 6); + return result; + } +} + +int QrCode::getNumRawDataModules(int ver) +{ + if (ver < MIN_VERSION || ver > MAX_VERSION) + throw std::domain_error("Version number out of range"); + int result = (16 * ver + 128) * ver + 64; + if (ver >= 2) { + int numAlign = ver / 7 + 2; + result -= (25 * numAlign - 10) * numAlign - 55; + if (ver >= 7) + result -= 36; + } + assert(208 <= result && result <= 29648); + return result; +} + +int QrCode::getNumDataCodewords(int ver, Ecc ecl) +{ + return getNumRawDataModules(ver) / 8 + - ECC_CODEWORDS_PER_BLOCK[static_cast(ecl)][ver] + * NUM_ERROR_CORRECTION_BLOCKS[static_cast(ecl)][ver]; +} + +vector QrCode::reedSolomonComputeDivisor(int degree) +{ + if (degree < 1 || degree > 255) + throw std::domain_error("Degree out of range"); + // Polynomial coefficients are stored from highest to lowest power, excluding the leading term which is always 1. + // For example the polynomial x^3 + 255x^2 + 8x + 93 is stored as the uint8 array {255, 8, 93}. + vector result(static_cast(degree)); + result.at(result.size() - 1) = 1; // Start off with the monomial x^0 + + // Compute the product polynomial (x - r^0) * (x - r^1) * (x - r^2) * ... * (x - r^{degree-1}), + // and drop the highest monomial term which is always 1x^degree. + // Note that r = 0x02, which is a generator element of this field GF(2^8/0x11D). + uint8_t root = 1; + for (int i = 0; i < degree; i++) { + // Multiply the current product by (x - r^i) + for (size_t j = 0; j < result.size(); j++) { + result.at(j) = reedSolomonMultiply(result.at(j), root); + if (j + 1 < result.size()) + result.at(j) ^= result.at(j + 1); + } + root = reedSolomonMultiply(root, 0x02); + } + return result; +} + +vector QrCode::reedSolomonComputeRemainder(const vector &data, + const vector &divisor) +{ + vector result(divisor.size()); + for (uint8_t b : data) { // Polynomial division + uint8_t factor = b ^ result.at(0); + result.erase(result.begin()); + result.push_back(0); + for (size_t i = 0; i < result.size(); i++) + result.at(i) ^= reedSolomonMultiply(divisor.at(i), factor); + } + return result; +} + +uint8_t QrCode::reedSolomonMultiply(uint8_t x, uint8_t y) +{ + // Russian peasant multiplication + int z = 0; + for (int i = 7; i >= 0; i--) { + z = (z << 1) ^ ((z >> 7) * 0x11D); + z ^= ((y >> i) & 1) * x; + } + assert(z >> 8 == 0); + return static_cast(z); +} + +int QrCode::finderPenaltyCountPatterns(const std::array &runHistory) const +{ + int n = runHistory.at(1); + assert(n <= size * 3); + bool core = n > 0 && runHistory.at(2) == n && runHistory.at(3) == n * 3 && runHistory.at(4) == n + && runHistory.at(5) == n; + return (core && runHistory.at(0) >= n * 4 && runHistory.at(6) >= n ? 1 : 0) + + (core && runHistory.at(6) >= n * 4 && runHistory.at(0) >= n ? 1 : 0); +} + +int QrCode::finderPenaltyTerminateAndCount(bool currentRunColor, + int currentRunLength, + std::array &runHistory) const +{ + if (currentRunColor) { // Terminate dark run + finderPenaltyAddHistory(currentRunLength, runHistory); + currentRunLength = 0; + } + currentRunLength += size; // Add light border to final run + finderPenaltyAddHistory(currentRunLength, runHistory); + return finderPenaltyCountPatterns(runHistory); +} + +void QrCode::finderPenaltyAddHistory(int currentRunLength, std::array &runHistory) const +{ + if (runHistory.at(0) == 0) + currentRunLength += size; // Add light border to initial run + std::copy_backward(runHistory.cbegin(), runHistory.cend() - 1, runHistory.end()); + runHistory.at(0) = currentRunLength; +} + +bool QrCode::getBit(long x, int i) +{ + return ((x >> i) & 1) != 0; +} + +/*---- Tables of constants ----*/ + +const int QrCode::PENALTY_N1 = 3; +const int QrCode::PENALTY_N2 = 3; +const int QrCode::PENALTY_N3 = 40; +const int QrCode::PENALTY_N4 = 10; + +const int8_t QrCode::ECC_CODEWORDS_PER_BLOCK[4][41] = { + // Version: (note that index 0 is for padding, and is set to an illegal value) + //0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40 Error correction level + {-1, 7, 10, 15, 20, 26, 18, 20, 24, 30, 18, 20, 24, 26, 30, 22, 24, 28, 30, 28, 28, + 28, 28, 30, 30, 26, 28, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30}, // Low + {-1, 10, 16, 26, 18, 24, 16, 18, 22, 22, 26, 30, 22, 22, 24, 24, 28, 28, 26, 26, 26, + 26, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28}, // Medium + {-1, 13, 22, 18, 26, 18, 24, 18, 22, 20, 24, 28, 26, 24, 20, 30, 24, 28, 28, 26, 30, + 28, 30, 30, 30, 30, 28, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30}, // Quartile + {-1, 17, 28, 22, 16, 22, 28, 26, 26, 24, 28, 24, 28, 22, 24, 24, 30, 28, 28, 26, 28, + 30, 24, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30}, // High +}; + +const int8_t QrCode::NUM_ERROR_CORRECTION_BLOCKS[4][41] = { + // Version: (note that index 0 is for padding, and is set to an illegal value) + //0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40 Error correction level + {-1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 4, 4, 4, 4, 4, 6, 6, 6, 6, 7, 8, + 8, 9, 9, 10, 12, 12, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 24, 25}, // Low + {-1, 1, 1, 1, 2, 2, 4, 4, 4, 5, 5, 5, 8, 9, 9, 10, 10, 11, 13, 14, 16, + 17, 17, 18, 20, 21, 23, 25, 26, 28, 29, 31, 33, 35, 37, 38, 40, 43, 45, 47, 49}, // Medium + {-1, 1, 1, 2, 2, 4, 4, 6, 6, 8, 8, 8, 10, 12, 16, 12, 17, 16, 18, 21, 20, + 23, 23, 25, 27, 29, 34, 34, 35, 38, 40, 43, 45, 48, 51, 53, 56, 59, 62, 65, 68}, // Quartile + {-1, 1, 1, 2, 4, 4, 4, 5, 6, 8, 8, 11, 11, 16, 16, 18, 16, 19, 21, 25, 25, + 25, 34, 30, 32, 35, 37, 40, 42, 45, 48, 51, 54, 57, 60, 63, 66, 70, 74, 77, 81}, // High +}; + +data_too_long::data_too_long(const std::string &msg) + : std::length_error(msg) +{} + +/*---- Class BitBuffer ----*/ + +BitBuffer::BitBuffer() + : std::vector() +{} + +void BitBuffer::appendBits(std::uint32_t val, int len) +{ + if (len < 0 || len > 31 || val >> len != 0) + throw std::domain_error("Value out of range"); + for (int i = len - 1; i >= 0; i--) // Append bit by bit + this->push_back(((val >> i) & 1) != 0); +} + +} // namespace qrcodegen diff --git a/src/libs/3rdparty/qrcodegen/src/qrcodegen.h b/src/libs/3rdparty/qrcodegen/src/qrcodegen.h new file mode 100644 index 00000000000..e2d285cf3e8 --- /dev/null +++ b/src/libs/3rdparty/qrcodegen/src/qrcodegen.h @@ -0,0 +1,566 @@ +/* + * QR Code generator library (C++) + * + * Copyright (c) Project Nayuki. (MIT License) + * https://www.nayuki.io/page/qr-code-generator-library + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * - The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * - The Software is provided "as is", without warranty of any kind, express or + * implied, including but not limited to the warranties of merchantability, + * fitness for a particular purpose and noninfringement. In no event shall the + * authors or copyright holders be liable for any claim, damages or other + * liability, whether in an action of contract, tort or otherwise, arising from, + * out of or in connection with the Software or the use or other dealings in the + * Software. + */ + +#pragma once + +#include +#include +#include +#include +#include + +namespace qrcodegen { + +/* + * A segment of character/binary/control data in a QR Code symbol. + * Instances of this class are immutable. + * The mid-level way to create a segment is to take the payload data + * and call a static factory function such as QrSegment::makeNumeric(). + * The low-level way to create a segment is to custom-make the bit buffer + * and call the QrSegment() constructor with appropriate values. + * This segment class imposes no length restrictions, but QR Codes have restrictions. + * Even in the most favorable conditions, a QR Code can only hold 7089 characters of data. + * Any segment longer than this is meaningless for the purpose of generating QR Codes. + */ +class QrSegment final +{ + /*---- Public helper enumeration ----*/ + + /* + * Describes how a segment's data bits are interpreted. Immutable. + */ +public: + class Mode final + { + /*-- Constants --*/ + + public: + static const Mode NUMERIC; + + public: + static const Mode ALPHANUMERIC; + + public: + static const Mode BYTE; + + public: + static const Mode KANJI; + + public: + static const Mode ECI; + + /*-- Fields --*/ + + // The mode indicator bits, which is a uint4 value (range 0 to 15). + private: + int modeBits; + + // Number of character count bits for three different version ranges. + private: + int numBitsCharCount[3]; + + /*-- Constructor --*/ + + private: + Mode(int mode, int cc0, int cc1, int cc2); + + /*-- Methods --*/ + + /* + * (Package-private) Returns the mode indicator bits, which is an unsigned 4-bit value (range 0 to 15). + */ + public: + int getModeBits() const; + + /* + * (Package-private) Returns the bit width of the character count field for a segment in + * this mode in a QR Code at the given version number. The result is in the range [0, 16]. + */ + public: + int numCharCountBits(int ver) const; + }; + + /*---- Static factory functions (mid level) ----*/ + + /* + * Returns a segment representing the given binary data encoded in + * byte mode. All input byte vectors are acceptable. Any text string + * can be converted to UTF-8 bytes and encoded as a byte mode segment. + */ +public: + static QrSegment makeBytes(const std::vector &data); + + /* + * Returns a segment representing the given string of decimal digits encoded in numeric mode. + */ +public: + static QrSegment makeNumeric(const char *digits); + + /* + * Returns a segment representing the given text string encoded in alphanumeric mode. + * The characters allowed are: 0 to 9, A to Z (uppercase only), space, + * dollar, percent, asterisk, plus, hyphen, period, slash, colon. + */ +public: + static QrSegment makeAlphanumeric(const char *text); + + /* + * Returns a list of zero or more segments to represent the given text string. The result + * may use various segment modes and switch modes to optimize the length of the bit stream. + */ +public: + static std::vector makeSegments(const char *text); + + /* + * Returns a segment representing an Extended Channel Interpretation + * (ECI) designator with the given assignment value. + */ +public: + static QrSegment makeEci(long assignVal); + + /*---- Public static helper functions ----*/ + + /* + * Tests whether the given string can be encoded as a segment in numeric mode. + * A string is encodable iff each character is in the range 0 to 9. + */ +public: + static bool isNumeric(const char *text); + + /* + * Tests whether the given string can be encoded as a segment in alphanumeric mode. + * A string is encodable iff each character is in the following set: 0 to 9, A to Z + * (uppercase only), space, dollar, percent, asterisk, plus, hyphen, period, slash, colon. + */ +public: + static bool isAlphanumeric(const char *text); + + /*---- Instance fields ----*/ + + /* The mode indicator of this segment. Accessed through getMode(). */ +private: + const Mode *mode; + + /* The length of this segment's unencoded data. Measured in characters for + * numeric/alphanumeric/kanji mode, bytes for byte mode, and 0 for ECI mode. + * Always zero or positive. Not the same as the data's bit length. + * Accessed through getNumChars(). */ +private: + int numChars; + + /* The data bits of this segment. Accessed through getData(). */ +private: + std::vector data; + + /*---- Constructors (low level) ----*/ + + /* + * Creates a new QR Code segment with the given attributes and data. + * The character count (numCh) must agree with the mode and the bit buffer length, + * but the constraint isn't checked. The given bit buffer is copied and stored. + */ +public: + QrSegment(const Mode &md, int numCh, const std::vector &dt); + + /* + * Creates a new QR Code segment with the given parameters and data. + * The character count (numCh) must agree with the mode and the bit buffer length, + * but the constraint isn't checked. The given bit buffer is moved and stored. + */ +public: + QrSegment(const Mode &md, int numCh, std::vector &&dt); + + /*---- Methods ----*/ + + /* + * Returns the mode field of this segment. + */ +public: + const Mode &getMode() const; + + /* + * Returns the character count field of this segment. + */ +public: + int getNumChars() const; + + /* + * Returns the data bits of this segment. + */ +public: + const std::vector &getData() const; + + // (Package-private) Calculates the number of bits needed to encode the given segments at + // the given version. Returns a non-negative number if successful. Otherwise returns -1 if a + // segment has too many characters to fit its length field, or the total bits exceeds INT_MAX. +public: + static int getTotalBits(const std::vector &segs, int version); + + /*---- Private constant ----*/ + + /* The set of all legal characters in alphanumeric mode, where + * each character value maps to the index in the string. */ +private: + static const char *ALPHANUMERIC_CHARSET; +}; + +/* + * A QR Code symbol, which is a type of two-dimension barcode. + * Invented by Denso Wave and described in the ISO/IEC 18004 standard. + * Instances of this class represent an immutable square grid of dark and light cells. + * The class provides static factory functions to create a QR Code from text or binary data. + * The class covers the QR Code Model 2 specification, supporting all versions (sizes) + * from 1 to 40, all 4 error correction levels, and 4 character encoding modes. + * + * Ways to create a QR Code object: + * - High level: Take the payload data and call QrCode::encodeText() or QrCode::encodeBinary(). + * - Mid level: Custom-make the list of segments and call QrCode::encodeSegments(). + * - Low level: Custom-make the array of data codeword bytes (including + * segment headers and final padding, excluding error correction codewords), + * supply the appropriate version number, and call the QrCode() constructor. + * (Note that all ways require supplying the desired error correction level.) + */ +class QrCode final +{ + /*---- Public helper enumeration ----*/ + + /* + * The error correction level in a QR Code symbol. + */ +public: + enum class Ecc { + LOW = 0, // The QR Code can tolerate about 7% erroneous codewords + MEDIUM, // The QR Code can tolerate about 15% erroneous codewords + QUARTILE, // The QR Code can tolerate about 25% erroneous codewords + HIGH, // The QR Code can tolerate about 30% erroneous codewords + }; + + // Returns a value in the range 0 to 3 (unsigned 2-bit integer). +private: + static int getFormatBits(Ecc ecl); + + /*---- Static factory functions (high level) ----*/ + + /* + * Returns a QR Code representing the given Unicode text string at the given error correction level. + * As a conservative upper bound, this function is guaranteed to succeed for strings that have 2953 or fewer + * UTF-8 code units (not Unicode code points) if the low error correction level is used. The smallest possible + * QR Code version is automatically chosen for the output. The ECC level of the result may be higher than + * the ecl argument if it can be done without increasing the version. + */ +public: + static QrCode encodeText(const char *text, Ecc ecl); + + /* + * Returns a QR Code representing the given binary data at the given error correction level. + * This function always encodes using the binary segment mode, not any text mode. The maximum number of + * bytes allowed is 2953. The smallest possible QR Code version is automatically chosen for the output. + * The ECC level of the result may be higher than the ecl argument if it can be done without increasing the version. + */ +public: + static QrCode encodeBinary(const std::vector &data, Ecc ecl); + + /*---- Static factory functions (mid level) ----*/ + + /* + * Returns a QR Code representing the given segments with the given encoding parameters. + * The smallest possible QR Code version within the given range is automatically + * chosen for the output. Iff boostEcl is true, then the ECC level of the result + * may be higher than the ecl argument if it can be done without increasing the + * version. The mask number is either between 0 to 7 (inclusive) to force that + * mask, or -1 to automatically choose an appropriate mask (which may be slow). + * This function allows the user to create a custom sequence of segments that switches + * between modes (such as alphanumeric and byte) to encode text in less space. + * This is a mid-level API; the high-level API is encodeText() and encodeBinary(). + */ +public: + static QrCode encodeSegments(const std::vector &segs, + Ecc ecl, + int minVersion = 1, + int maxVersion = 40, + int mask = -1, + bool boostEcl = true); // All optional parameters + + /*---- Instance fields ----*/ + + // Immutable scalar parameters: + + /* The version number of this QR Code, which is between 1 and 40 (inclusive). + * This determines the size of this barcode. */ +private: + int version; + + /* The width and height of this QR Code, measured in modules, between + * 21 and 177 (inclusive). This is equal to version * 4 + 17. */ +private: + int size; + + /* The error correction level used in this QR Code. */ +private: + Ecc errorCorrectionLevel; + + /* The index of the mask pattern used in this QR Code, which is between 0 and 7 (inclusive). + * Even if a QR Code is created with automatic masking requested (mask = -1), + * the resulting object still has a mask value between 0 and 7. */ +private: + int mask; + + // Private grids of modules/pixels, with dimensions of size*size: + + // The modules of this QR Code (false = light, true = dark). + // Immutable after constructor finishes. Accessed through getModule(). +private: + std::vector> modules; + + // Indicates function modules that are not subjected to masking. Discarded when constructor finishes. +private: + std::vector> isFunction; + + /*---- Constructor (low level) ----*/ + + /* + * Creates a new QR Code with the given version number, + * error correction level, data codeword bytes, and mask number. + * This is a low-level API that most users should not use directly. + * A mid-level API is the encodeSegments() function. + */ +public: + QrCode(int ver, Ecc ecl, const std::vector &dataCodewords, int msk); + + /*---- Public instance methods ----*/ + + /* + * Returns this QR Code's version, in the range [1, 40]. + */ +public: + int getVersion() const; + + /* + * Returns this QR Code's size, in the range [21, 177]. + */ +public: + int getSize() const; + + /* + * Returns this QR Code's error correction level. + */ +public: + Ecc getErrorCorrectionLevel() const; + + /* + * Returns this QR Code's mask, in the range [0, 7]. + */ +public: + int getMask() const; + + /* + * Returns the color of the module (pixel) at the given coordinates, which is false + * for light or true for dark. The top left corner has the coordinates (x=0, y=0). + * If the given coordinates are out of bounds, then false (light) is returned. + */ +public: + bool getModule(int x, int y) const; + + /*---- Private helper methods for constructor: Drawing function modules ----*/ + + // Reads this object's version field, and draws and marks all function modules. +private: + void drawFunctionPatterns(); + + // Draws two copies of the format bits (with its own error correction code) + // based on the given mask and this object's error correction level field. +private: + void drawFormatBits(int msk); + + // Draws two copies of the version bits (with its own error correction code), + // based on this object's version field, iff 7 <= version <= 40. +private: + void drawVersion(); + + // Draws a 9*9 finder pattern including the border separator, + // with the center module at (x, y). Modules can be out of bounds. +private: + void drawFinderPattern(int x, int y); + + // Draws a 5*5 alignment pattern, with the center module + // at (x, y). All modules must be in bounds. +private: + void drawAlignmentPattern(int x, int y); + + // Sets the color of a module and marks it as a function module. + // Only used by the constructor. Coordinates must be in bounds. +private: + void setFunctionModule(int x, int y, bool isDark); + + // Returns the color of the module at the given coordinates, which must be in range. +private: + bool module(int x, int y) const; + + /*---- Private helper methods for constructor: Codewords and masking ----*/ + + // Returns a new byte string representing the given data with the appropriate error correction + // codewords appended to it, based on this object's version and error correction level. +private: + std::vector addEccAndInterleave(const std::vector &data) const; + + // Draws the given sequence of 8-bit codewords (data and error correction) onto the entire + // data area of this QR Code. Function modules need to be marked off before this is called. +private: + void drawCodewords(const std::vector &data); + + // XORs the codeword modules in this QR Code with the given mask pattern. + // The function modules must be marked and the codeword bits must be drawn + // before masking. Due to the arithmetic of XOR, calling applyMask() with + // the same mask value a second time will undo the mask. A final well-formed + // QR Code needs exactly one (not zero, two, etc.) mask applied. +private: + void applyMask(int msk); + + // Calculates and returns the penalty score based on state of this QR Code's current modules. + // This is used by the automatic mask choice algorithm to find the mask pattern that yields the lowest score. +private: + long getPenaltyScore() const; + + /*---- Private helper functions ----*/ + + // Returns an ascending list of positions of alignment patterns for this version number. + // Each position is in the range [0,177), and are used on both the x and y axes. + // This could be implemented as lookup table of 40 variable-length lists of unsigned bytes. +private: + std::vector getAlignmentPatternPositions() const; + + // Returns the number of data bits that can be stored in a QR Code of the given version number, after + // all function modules are excluded. This includes remainder bits, so it might not be a multiple of 8. + // The result is in the range [208, 29648]. This could be implemented as a 40-entry lookup table. +private: + static int getNumRawDataModules(int ver); + + // Returns the number of 8-bit data (i.e. not error correction) codewords contained in any + // QR Code of the given version number and error correction level, with remainder bits discarded. + // This stateless pure function could be implemented as a (40*4)-cell lookup table. +private: + static int getNumDataCodewords(int ver, Ecc ecl); + + // Returns a Reed-Solomon ECC generator polynomial for the given degree. This could be + // implemented as a lookup table over all possible parameter values, instead of as an algorithm. +private: + static std::vector reedSolomonComputeDivisor(int degree); + + // Returns the Reed-Solomon error correction codeword for the given data and divisor polynomials. +private: + static std::vector reedSolomonComputeRemainder( + const std::vector &data, const std::vector &divisor); + + // Returns the product of the two given field elements modulo GF(2^8/0x11D). + // All inputs are valid. This could be implemented as a 256*256 lookup table. +private: + static std::uint8_t reedSolomonMultiply(std::uint8_t x, std::uint8_t y); + + // Can only be called immediately after a light run is added, and + // returns either 0, 1, or 2. A helper function for getPenaltyScore(). +private: + int finderPenaltyCountPatterns(const std::array &runHistory) const; + + // Must be called at the end of a line (row or column) of modules. A helper function for getPenaltyScore(). +private: + int finderPenaltyTerminateAndCount(bool currentRunColor, + int currentRunLength, + std::array &runHistory) const; + + // Pushes the given value to the front and drops the last value. A helper function for getPenaltyScore(). +private: + void finderPenaltyAddHistory(int currentRunLength, std::array &runHistory) const; + + // Returns true iff the i'th bit of x is set to 1. +private: + static bool getBit(long x, int i); + + /*---- Constants and tables ----*/ + + // The minimum version number supported in the QR Code Model 2 standard. +public: + static constexpr int MIN_VERSION = 1; + + // The maximum version number supported in the QR Code Model 2 standard. +public: + static constexpr int MAX_VERSION = 40; + + // For use in getPenaltyScore(), when evaluating which mask is best. +private: + static const int PENALTY_N1; + +private: + static const int PENALTY_N2; + +private: + static const int PENALTY_N3; + +private: + static const int PENALTY_N4; + +private: + static const std::int8_t ECC_CODEWORDS_PER_BLOCK[4][41]; + +private: + static const std::int8_t NUM_ERROR_CORRECTION_BLOCKS[4][41]; +}; + +/*---- Public exception class ----*/ + +/* + * Thrown when the supplied data does not fit any QR Code version. Ways to handle this exception include: + * - Decrease the error correction level if it was greater than Ecc::LOW. + * - If the encodeSegments() function was called with a maxVersion argument, then increase + * it if it was less than QrCode::MAX_VERSION. (This advice does not apply to the other + * factory functions because they search all versions up to QrCode::MAX_VERSION.) + * - Split the text data into better or optimal segments in order to reduce the number of bits required. + * - Change the text or binary data to be shorter. + * - Change the text to fit the character set of a particular segment mode (e.g. alphanumeric). + * - Propagate the error upward to the caller/user. + */ +class data_too_long : public std::length_error +{ +public: + explicit data_too_long(const std::string &msg); +}; + +/* + * An appendable sequence of bits (0s and 1s). Mainly used by QrSegment. + */ +class BitBuffer final : public std::vector +{ + /*---- Constructor ----*/ + + // Creates an empty bit buffer (length 0). +public: + BitBuffer(); + + /*---- Method ----*/ + + // Appends the given number of low-order bits of the given value + // to this buffer. Requires 0 <= len <= 31 and val < 2^len. +public: + void appendBits(std::uint32_t val, int len); +}; + +} // namespace qrcodegen diff --git a/src/libs/3rdparty/qrcodegen/src/qrcodeimageprovider.cpp b/src/libs/3rdparty/qrcodegen/src/qrcodeimageprovider.cpp new file mode 100644 index 00000000000..b889b677600 --- /dev/null +++ b/src/libs/3rdparty/qrcodegen/src/qrcodeimageprovider.cpp @@ -0,0 +1,89 @@ +/**************************************************************************** +** +** Copyright (C) 2022 The Qt Company Ltd +** All rights reserved. +** For any questions to The Qt Company, please use contact form at http://www.qt.io/contact-us +** +** This file is part of the Custom Merge QtDesignStudio plugin. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** If you have questions regarding the use of this file, please use +** contact form at http://www.qt.io/contact-us +** +******************************************************************************/ + +#include "qrcodeimageprovider.h" +#include "qrcodegen.h" +#include +#include + +QrCodeImageProvider::QrCodeImageProvider() + : QQuickImageProvider(QQuickImageProvider::Pixmap) +{ +} + + +static QString qrToSvgString(const qrcodegen::QrCode &qr, int border) { + if (border < 0) + throw std::domain_error("Border must be non-negative"); + if (border > INT_MAX / 2 || border * 2 > INT_MAX - qr.getSize()) + throw std::overflow_error("Border too large"); + + QString svgString; + QTextStream stream(&svgString); + + stream << "\n"; + stream << "\n"; + stream << "\n"; + stream << "\t\n"; + stream << "\t\n"; + stream << "\n"; + + return svgString; + +} + +QPixmap QrCodeImageProvider::requestPixmap(const QString &id, QSize *size, const QSize &requestedSize) +{ + int width = 1000; + int height = 1000; + + const qrcodegen::QrCode qr = qrcodegen::QrCode::encodeText(id.toLatin1(), qrcodegen::QrCode::Ecc::LOW); + + QString svgString = qrToSvgString(qr, 3); + QSvgRenderer svgRenderer(svgString.toLatin1()); + + + if (size) + *size = QSize(width, height); + + QPixmap pixmap(requestedSize.width() > 0 ? requestedSize.width() : width, + requestedSize.height() > 0 ? requestedSize.height() : height); + + QPainter painter(&pixmap); + + svgRenderer.render(&painter); + + return pixmap; + +} + + diff --git a/src/libs/3rdparty/qrcodegen/src/qrcodeimageprovider.h b/src/libs/3rdparty/qrcodegen/src/qrcodeimageprovider.h new file mode 100644 index 00000000000..1516d214bd8 --- /dev/null +++ b/src/libs/3rdparty/qrcodegen/src/qrcodeimageprovider.h @@ -0,0 +1,36 @@ +/**************************************************************************** +** +** Copyright (C) 2022 The Qt Company Ltd +** All rights reserved. +** For any questions to The Qt Company, please use contact form at http://www.qt.io/contact-us +** +** This file is part of the Custom Merge QtDesignStudio plugin. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** If you have questions regarding the use of this file, please use +** contact form at http://www.qt.io/contact-us +** +******************************************************************************/ + +#ifndef QRCODEIMAGEPROVIDER_H +#define QRCODEIMAGEPROVIDER_H + +#include +#include + +class QrCodeImageProvider : public QQuickImageProvider +{ +public: + QrCodeImageProvider(); + + QPixmap requestPixmap(const QString &id, QSize *size, const QSize &requestedSize); +}; + +#endif // QRCODEIMAGEPROVIDER_H From e18f0b50e69f555823f5315e35f3592d7460dcfe Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Thu, 12 Oct 2023 16:34:06 +0300 Subject: [PATCH 038/242] QmlDesigner: Rename SingleCollection to CollectionDetails * SingleCollectionModel is replaced by CollectionDetailsModel * SingleCollectionView is replaced by CollectionDetailsView Change-Id: Id3e7572b7b7d7e369684cde21b3cc6a34f819369 Reviewed-by: Miikka Heikkinen Reviewed-by: Mahmoud Badri --- ...tionView.qml => CollectionDetailsView.qml} | 0 .../CollectionView.qml | 6 +- src/plugins/qmldesigner/CMakeLists.txt | 2 +- ...onmodel.cpp => collectiondetailsmodel.cpp} | 76 +++++++++---------- ...ectionmodel.h => collectiondetailsmodel.h} | 4 +- .../collectioneditor/collectionview.cpp | 4 +- .../collectioneditor/collectionwidget.cpp | 11 +-- .../collectioneditor/collectionwidget.h | 6 +- 8 files changed, 55 insertions(+), 54 deletions(-) rename share/qtcreator/qmldesigner/collectionEditorQmlSource/{SingleCollectionView.qml => CollectionDetailsView.qml} (100%) rename src/plugins/qmldesigner/components/collectioneditor/{singlecollectionmodel.cpp => collectiondetailsmodel.cpp} (82%) rename src/plugins/qmldesigner/components/collectioneditor/{singlecollectionmodel.h => collectiondetailsmodel.h} (96%) diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/SingleCollectionView.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsView.qml similarity index 100% rename from share/qtcreator/qmldesigner/collectionEditorQmlSource/SingleCollectionView.qml rename to share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsView.qml diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionView.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionView.qml index e6fddf33ee4..141abaa90e6 100644 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionView.qml +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionView.qml @@ -14,7 +14,7 @@ Item { property var rootView: CollectionEditorBackend.rootView property var model: CollectionEditorBackend.model - property var singleCollectionModel: CollectionEditorBackend.singleCollectionModel + property var collectionDetailsModel: CollectionEditorBackend.collectionDetailsModel function showWarning(title, message) { warningDialog.title = title @@ -142,8 +142,8 @@ Item { } } - SingleCollectionView { - model: root.singleCollectionModel + CollectionDetailsView { + model: root.collectionDetailsModel anchors { left: collectionsRect.right right: parent.right diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index 6e8df880782..7b54937f181 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -822,12 +822,12 @@ extend_qtc_plugin(QmlDesigner SOURCES_PREFIX components/collectioneditor SOURCES collectiondetails.cpp collectiondetails.h + collectiondetailsmodel.cpp collectiondetailsmodel.h collectioneditorconstants.h collectionlistmodel.cpp collectionlistmodel.h collectionsourcemodel.cpp collectionsourcemodel.h collectionview.cpp collectionview.h collectionwidget.cpp collectionwidget.h - singlecollectionmodel.cpp singlecollectionmodel.h ) extend_qtc_plugin(QmlDesigner diff --git a/src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.cpp similarity index 82% rename from src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.cpp rename to src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.cpp index d35fb7303c5..896c584be8b 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.cpp @@ -1,7 +1,7 @@ // 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 "singlecollectionmodel.h" +#include "collectiondetailsmodel.h" #include "collectioneditorconstants.h" #include "modelnode.h" @@ -85,15 +85,15 @@ private: namespace QmlDesigner { -SingleCollectionModel::SingleCollectionModel(QObject *parent) +CollectionDetailsModel::CollectionDetailsModel(QObject *parent) : QAbstractTableModel(parent) { - connect(this, &SingleCollectionModel::modelReset, this, &SingleCollectionModel::updateEmpty); - connect(this, &SingleCollectionModel::rowsInserted, this, &SingleCollectionModel::updateEmpty); - connect(this, &SingleCollectionModel::rowsRemoved, this, &SingleCollectionModel::updateEmpty); + connect(this, &CollectionDetailsModel::modelReset, this, &CollectionDetailsModel::updateEmpty); + connect(this, &CollectionDetailsModel::rowsInserted, this, &CollectionDetailsModel::updateEmpty); + connect(this, &CollectionDetailsModel::rowsRemoved, this, &CollectionDetailsModel::updateEmpty); } -QHash SingleCollectionModel::roleNames() const +QHash CollectionDetailsModel::roleNames() const { static QHash roles; if (roles.isEmpty()) { @@ -104,17 +104,17 @@ QHash SingleCollectionModel::roleNames() const return roles; } -int SingleCollectionModel::rowCount([[maybe_unused]] const QModelIndex &parent) const +int CollectionDetailsModel::rowCount([[maybe_unused]] const QModelIndex &parent) const { return m_currentCollection.rows(); } -int SingleCollectionModel::columnCount([[maybe_unused]] const QModelIndex &parent) const +int CollectionDetailsModel::columnCount([[maybe_unused]] const QModelIndex &parent) const { return m_currentCollection.columns(); } -QVariant SingleCollectionModel::data(const QModelIndex &index, int role) const +QVariant CollectionDetailsModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) return {}; @@ -125,12 +125,12 @@ QVariant SingleCollectionModel::data(const QModelIndex &index, int role) const return m_currentCollection.data(index.row(), index.column()); } -bool SingleCollectionModel::setData(const QModelIndex &, const QVariant &, int) +bool CollectionDetailsModel::setData(const QModelIndex &, const QVariant &, int) { return false; } -bool SingleCollectionModel::setHeaderData(int section, +bool CollectionDetailsModel::setHeaderData(int section, Qt::Orientation orientation, const QVariant &value, [[maybe_unused]] int role) @@ -145,7 +145,7 @@ bool SingleCollectionModel::setHeaderData(int section, return headerChanged; } -bool SingleCollectionModel::insertRows(int row, int count, const QModelIndex &parent) +bool CollectionDetailsModel::insertRows(int row, int count, const QModelIndex &parent) { if (count < 1) return false; @@ -158,7 +158,7 @@ bool SingleCollectionModel::insertRows(int row, int count, const QModelIndex &pa return true; } -bool SingleCollectionModel::removeColumns(int column, int count, const QModelIndex &parent) +bool CollectionDetailsModel::removeColumns(int column, int count, const QModelIndex &parent) { if (column < 0 || column >= columnCount(parent) || count < 1) return false; @@ -171,7 +171,7 @@ bool SingleCollectionModel::removeColumns(int column, int count, const QModelInd return columnsRemoved; } -bool SingleCollectionModel::removeRows(int row, int count, const QModelIndex &parent) +bool CollectionDetailsModel::removeRows(int row, int count, const QModelIndex &parent) { if (row < 0 || row >= rowCount(parent) || count < 1) return false; @@ -184,7 +184,7 @@ bool SingleCollectionModel::removeRows(int row, int count, const QModelIndex &pa return rowsRemoved; } -Qt::ItemFlags SingleCollectionModel::flags(const QModelIndex &index) const +Qt::ItemFlags CollectionDetailsModel::flags(const QModelIndex &index) const { if (!index.isValid()) return {}; @@ -192,7 +192,7 @@ Qt::ItemFlags SingleCollectionModel::flags(const QModelIndex &index) const return {Qt::ItemIsSelectable | Qt::ItemIsEnabled}; } -QVariant SingleCollectionModel::headerData(int section, Qt::Orientation orientation, int role) const +QVariant CollectionDetailsModel::headerData(int section, Qt::Orientation orientation, int role) const { if (orientation == Qt::Horizontal) { if (role == DataTypeRole) { @@ -212,32 +212,32 @@ QVariant SingleCollectionModel::headerData(int section, Qt::Orientation orientat return {}; } -int SingleCollectionModel::selectedColumn() const +int CollectionDetailsModel::selectedColumn() const { return m_selectedColumn; } -int SingleCollectionModel::selectedRow() const +int CollectionDetailsModel::selectedRow() const { return m_selectedRow; } -QString SingleCollectionModel::propertyName(int column) const +QString CollectionDetailsModel::propertyName(int column) const { return m_currentCollection.propertyAt(column); } -QString SingleCollectionModel::propertyType(int column) const +QString CollectionDetailsModel::propertyType(int column) const { return CollectionDataTypeHelper::typeToString(m_currentCollection.typeAt(column)); } -bool SingleCollectionModel::isPropertyAvailable(const QString &name) +bool CollectionDetailsModel::isPropertyAvailable(const QString &name) { return m_currentCollection.containsPropertyName(name); } -bool SingleCollectionModel::addColumn(int column, const QString &name) +bool CollectionDetailsModel::addColumn(int column, const QString &name) { if (m_currentCollection.containsPropertyName(name)) return false; @@ -251,7 +251,7 @@ bool SingleCollectionModel::addColumn(int column, const QString &name) return m_currentCollection.containsPropertyName(name); } -bool SingleCollectionModel::selectColumn(int section) +bool CollectionDetailsModel::selectColumn(int section) { if (m_selectedColumn == section) return false; @@ -283,12 +283,12 @@ bool SingleCollectionModel::selectColumn(int section) return true; } -bool SingleCollectionModel::renameColumn(int section, const QString &newValue) +bool CollectionDetailsModel::renameColumn(int section, const QString &newValue) { return setHeaderData(section, Qt::Horizontal, newValue); } -bool SingleCollectionModel::setPropertyType(int column, const QString &newValue, bool force) +bool CollectionDetailsModel::setPropertyType(int column, const QString &newValue, bool force) { bool changed = m_currentCollection.forcePropertyType(column, CollectionDataTypeHelper::typeFromString( @@ -307,7 +307,7 @@ bool SingleCollectionModel::setPropertyType(int column, const QString &newValue, return changed; } -bool SingleCollectionModel::selectRow(int row) +bool CollectionDetailsModel::selectRow(int row) { if (m_selectedRow == row) return false; @@ -336,18 +336,18 @@ bool SingleCollectionModel::selectRow(int row) return true; } -void SingleCollectionModel::deselectAll() +void CollectionDetailsModel::deselectAll() { selectColumn(-1); selectRow(-1); } -QStringList SingleCollectionModel::typesList() +QStringList CollectionDetailsModel::typesList() { return CollectionDataTypeHelper::typesStringList(); } -void SingleCollectionModel::loadCollection(const ModelNode &sourceNode, const QString &collection) +void CollectionDetailsModel::loadCollection(const ModelNode &sourceNode, const QString &collection) { QString fileName = sourceNode.variantProperty(CollectionEditor::SOURCEFILE_PROPERTY).value().toString(); @@ -371,7 +371,7 @@ void SingleCollectionModel::loadCollection(const ModelNode &sourceNode, const QS } } -void SingleCollectionModel::updateEmpty() +void CollectionDetailsModel::updateEmpty() { bool isEmptyNow = rowCount() == 0; if (m_isEmpty != isEmptyNow) { @@ -380,7 +380,7 @@ void SingleCollectionModel::updateEmpty() } } -void SingleCollectionModel::switchToCollection(const CollectionReference &collection) +void CollectionDetailsModel::switchToCollection(const CollectionReference &collection) { if (m_currentCollection.reference() == collection) return; @@ -395,7 +395,7 @@ void SingleCollectionModel::switchToCollection(const CollectionReference &collec setCollectionName(collection.name); } -void SingleCollectionModel::closeCollectionIfSaved(const CollectionReference &collection) +void CollectionDetailsModel::closeCollectionIfSaved(const CollectionReference &collection) { if (!m_openedCollections.contains(collection)) return; @@ -408,13 +408,13 @@ void SingleCollectionModel::closeCollectionIfSaved(const CollectionReference &co m_currentCollection = CollectionDetails{}; } -void SingleCollectionModel::closeCurrentCollectionIfSaved() +void CollectionDetailsModel::closeCurrentCollectionIfSaved() { if (m_currentCollection.isValid()) closeCollectionIfSaved(m_currentCollection.reference()); } -void SingleCollectionModel::loadJsonCollection(const QString &source, const QString &collection) +void CollectionDetailsModel::loadJsonCollection(const QString &source, const QString &collection) { using CollectionEditor::SourceFormat; @@ -459,7 +459,7 @@ void SingleCollectionModel::loadJsonCollection(const QString &source, const QStr endResetModel(); } -void SingleCollectionModel::loadCsvCollection(const QString &source, +void CollectionDetailsModel::loadCsvCollection(const QString &source, [[maybe_unused]] const QString &collectionName) { using CollectionEditor::SourceFormat; @@ -498,7 +498,7 @@ void SingleCollectionModel::loadCsvCollection(const QString &source, endResetModel(); } -void SingleCollectionModel::setCollectionName(const QString &newCollectionName) +void CollectionDetailsModel::setCollectionName(const QString &newCollectionName) { if (m_collectionName != newCollectionName) { m_collectionName = newCollectionName; @@ -506,7 +506,7 @@ void SingleCollectionModel::setCollectionName(const QString &newCollectionName) } } -bool SingleCollectionModel::saveCollectionAsJson(const QString &collection, const QJsonArray &content, const QString &source) +bool CollectionDetailsModel::saveCollectionAsJson(const QString &collection, const QJsonArray &content, const QString &source) { QFile sourceFile(source); if (sourceFile.open(QFile::ReadWrite)) { @@ -527,7 +527,7 @@ bool SingleCollectionModel::saveCollectionAsJson(const QString &collection, cons return false; } -bool SingleCollectionModel::saveCollectionAsCsv(const QString &path, const QString &content) +bool CollectionDetailsModel::saveCollectionAsCsv(const QString &path, const QString &content) { QFile file(path); diff --git a/src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.h b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.h similarity index 96% rename from src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.h rename to src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.h index 3327c8b01f5..ee3805e34bb 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.h +++ b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.h @@ -12,7 +12,7 @@ namespace QmlDesigner { class ModelNode; -class SingleCollectionModel : public QAbstractTableModel +class CollectionDetailsModel : public QAbstractTableModel { Q_OBJECT @@ -24,7 +24,7 @@ class SingleCollectionModel : public QAbstractTableModel public: enum DataRoles { SelectedRole = Qt::UserRole + 1, DataTypeRole }; - explicit SingleCollectionModel(QObject *parent = nullptr); + explicit CollectionDetailsModel(QObject *parent = nullptr); QHash roleNames() const override; int rowCount(const QModelIndex &parent = {}) const override; diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp index 6c88fe2081d..a3ffd72250a 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp @@ -3,6 +3,7 @@ #include "collectionview.h" +#include "collectiondetailsmodel.h" #include "collectioneditorconstants.h" #include "collectionsourcemodel.h" #include "collectionwidget.h" @@ -10,7 +11,6 @@ #include "nodeabstractproperty.h" #include "nodemetainfo.h" #include "qmldesignerplugin.h" -#include "singlecollectionmodel.h" #include "variantproperty.h" #include @@ -55,7 +55,7 @@ QmlDesigner::WidgetInfo CollectionView::widgetInfo() &CollectionSourceModel::collectionSelected, this, [this](const ModelNode &sourceNode, const QString &collection) { - m_widget->singleCollectionModel()->loadCollection(sourceNode, collection); + m_widget->collectionDetailsModel()->loadCollection(sourceNode, collection); }); } diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp index c4d9631cefe..da0ece6ee27 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp @@ -2,11 +2,12 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "collectionwidget.h" + +#include "collectiondetailsmodel.h" #include "collectionsourcemodel.h" #include "collectionview.h" #include "qmldesignerconstants.h" #include "qmldesignerplugin.h" -#include "singlecollectionmodel.h" #include "theme.h" #include @@ -38,7 +39,7 @@ CollectionWidget::CollectionWidget(CollectionView *view) : QFrame() , m_view(view) , m_sourceModel(new CollectionSourceModel) - , m_singleCollectionModel(new SingleCollectionModel) + , m_collectionDetailsModel(new CollectionDetailsModel) , m_quickWidget(new StudioQuickWidget(this)) { setWindowTitle(tr("Collection View", "Title of collection view widget")); @@ -67,7 +68,7 @@ CollectionWidget::CollectionWidget(CollectionView *view) map->setProperties( {{"rootView", QVariant::fromValue(this)}, {"model", QVariant::fromValue(m_sourceModel.data())}, - {"singleCollectionModel", QVariant::fromValue(m_singleCollectionModel.data())}}); + {"collectionDetailsModel", QVariant::fromValue(m_collectionDetailsModel.data())}}); auto hotReloadShortcut = new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_F4), this); connect(hotReloadShortcut, &QShortcut::activated, this, &CollectionWidget::reloadQmlSource); @@ -88,9 +89,9 @@ QPointer CollectionWidget::sourceModel() const return m_sourceModel; } -QPointer CollectionWidget::singleCollectionModel() const +QPointer CollectionWidget::collectionDetailsModel() const { - return m_singleCollectionModel; + return m_collectionDetailsModel; } void CollectionWidget::reloadQmlSource() diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.h b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.h index de2b4d8d9fc..fd422fa5d99 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.h +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.h @@ -11,9 +11,9 @@ class StudioQuickWidget; namespace QmlDesigner { +class CollectionDetailsModel; class CollectionSourceModel; class CollectionView; -class SingleCollectionModel; class CollectionWidget : public QFrame { @@ -24,7 +24,7 @@ public: void contextHelp(const Core::IContext::HelpCallback &callback) const; QPointer sourceModel() const; - QPointer singleCollectionModel() const; + QPointer collectionDetailsModel() const; void reloadQmlSource(); @@ -39,7 +39,7 @@ public: private: QPointer m_view; QPointer m_sourceModel; - QPointer m_singleCollectionModel; + QPointer m_collectionDetailsModel; QScopedPointer m_quickWidget; }; From 5c68f4fa219ff41cb4a8c292fbc1a0a89f0b1ec4 Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Fri, 13 Oct 2023 14:42:44 +0300 Subject: [PATCH 039/242] EffectMaker: Ensure node categories sorting order across all OSs Fixes: QDS-10520 Change-Id: I24cc5c3a858be31acc3e72d563947cd991cef5a8 Reviewed-by: Miikka Heikkinen Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Amr Elsayed --- src/plugins/effectmakernew/effectmakernodesmodel.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/plugins/effectmakernew/effectmakernodesmodel.cpp b/src/plugins/effectmakernew/effectmakernodesmodel.cpp index 3d3bb9f95d6..dc028aef8e8 100644 --- a/src/plugins/effectmakernew/effectmakernodesmodel.cpp +++ b/src/plugins/effectmakernew/effectmakernodesmodel.cpp @@ -97,6 +97,11 @@ void EffectMakerNodesModel::loadModel() m_categories.push_back(category); } + std::sort(m_categories.begin(), m_categories.end(), + [](EffectNodesCategory *a, EffectNodesCategory *b) { + return a->name() < b->name(); + }); + resetModel(); } From c3e4258b78a1b1b72c935ff07d1ee99f8ded1522 Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Fri, 13 Oct 2023 15:11:43 +0300 Subject: [PATCH 040/242] EffectMaker: Clear composition nodes on view attach Fixes: QDS-10513 Change-Id: I3d2b1369c4e33fb6e21311c59a92625437382602 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Miikka Heikkinen --- src/plugins/effectmakernew/effectmakermodel.cpp | 13 +++++++++++++ src/plugins/effectmakernew/effectmakermodel.h | 2 ++ src/plugins/effectmakernew/effectmakerview.cpp | 4 +++- 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/plugins/effectmakernew/effectmakermodel.cpp b/src/plugins/effectmakernew/effectmakermodel.cpp index 293d1311b7d..da8403108f3 100644 --- a/src/plugins/effectmakernew/effectmakermodel.cpp +++ b/src/plugins/effectmakernew/effectmakermodel.cpp @@ -168,6 +168,19 @@ const QString &EffectMakerModel::qmlComponentString() const return m_qmlComponentString; } +void EffectMakerModel::clear() +{ + if (m_nodes.isEmpty()) + return; + + for (CompositionNode *node : std::as_const(m_nodes)) + delete node; + + m_nodes.clear(); + + setIsEmpty(true); +} + const QList EffectMakerModel::allUniforms() { QList uniforms = {}; diff --git a/src/plugins/effectmakernew/effectmakermodel.h b/src/plugins/effectmakernew/effectmakermodel.h index 223656d382e..8f34dc8fe43 100644 --- a/src/plugins/effectmakernew/effectmakermodel.h +++ b/src/plugins/effectmakernew/effectmakermodel.h @@ -70,6 +70,8 @@ public: const QString &qmlComponentString() const; + void clear(); + Q_INVOKABLE void updateQmlComponent(); Q_INVOKABLE void resetEffectError(int type); diff --git a/src/plugins/effectmakernew/effectmakerview.cpp b/src/plugins/effectmakernew/effectmakerview.cpp index 4bb68f358a4..40ba4987e67 100644 --- a/src/plugins/effectmakernew/effectmakerview.cpp +++ b/src/plugins/effectmakernew/effectmakerview.cpp @@ -3,8 +3,9 @@ #include "effectmakerview.h" -#include "effectmakerwidget.h" +#include "effectmakermodel.h" #include "effectmakernodesmodel.h" +#include "effectmakerwidget.h" #include "nodeinstanceview.h" #include "qmldesignerconstants.h" @@ -71,6 +72,7 @@ void EffectMakerView::modelAttached(QmlDesigner::Model *model) AbstractView::modelAttached(model); m_widget->effectMakerNodesModel()->loadModel(); + m_widget->effectMakerModel()->clear(); m_widget->initView(); } From dcbb07544eba185b16088376eea5a9d447fa4889 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Wed, 11 Oct 2023 20:26:08 +0200 Subject: [PATCH 041/242] QmlDesigner: Use raw string to improve readability Change-Id: Id829dde077dd95c7cfd10af0a0e6389db2701953 Reviewed-by: Thomas Hartmann Reviewed-by: Qt CI Patch Build Bot Reviewed-by: --- .../designercore/projectstorage/projectstorage.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h index 0db827e4fbc..00610ad83a7 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h @@ -2032,14 +2032,14 @@ private: for (const auto ¶meter : parameters) { json.append(comma); comma = ","; - json.append("{\"n\":\""); + json.append(R"({"n":")"); json.append(parameter.name); - json.append("\",\"tn\":\""); + json.append(R"(","tn":")"); json.append(parameter.typeName); if (parameter.traits == Storage::PropertyDeclarationTraits::None) { json.append("\"}"); } else { - json.append("\",\"tr\":"); + json.append(R"(","tr":)"); json.append(Utils::SmallString::number(to_underlying(parameter.traits))); json.append("}"); } From 077320d74c94d4c4da4f019342a53b38f63fd618 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Thu, 12 Oct 2023 13:22:47 +0200 Subject: [PATCH 042/242] QmlDesigner: Fix thread local tracing categories The macro was not exported to the category was not enabled in other targets for inline functions. Change-Id: I176df1bc6aaaf8b3c8851224e78e43238a98a3a8 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Tim Jenssen Reviewed-by: --- src/libs/nanotrace/nanotracehr.cpp | 1 + src/libs/sqlite/CMakeLists.txt | 2 +- src/libs/sqlite/sqlitebasestatement.cpp | 13 ++++++---- src/libs/sqlite/sqlitebasestatement.h | 24 +++++++++---------- .../projectstorage/projectstorage.cpp | 4 ++-- .../projectstorage/projectstorage.h | 2 +- 6 files changed, 26 insertions(+), 20 deletions(-) diff --git a/src/libs/nanotrace/nanotracehr.cpp b/src/libs/nanotrace/nanotracehr.cpp index 7ed3a796dd0..6e6c1fbe9d3 100644 --- a/src/libs/nanotrace/nanotracehr.cpp +++ b/src/libs/nanotrace/nanotracehr.cpp @@ -42,6 +42,7 @@ void flushEvents(const Utils::span events, printEvent(out, event, processId, threadId); out << ",\n"; } + out << std::flush; } } diff --git a/src/libs/sqlite/CMakeLists.txt b/src/libs/sqlite/CMakeLists.txt index 59654d6d5bd..8d690f3bd46 100644 --- a/src/libs/sqlite/CMakeLists.txt +++ b/src/libs/sqlite/CMakeLists.txt @@ -73,7 +73,7 @@ add_qtc_library(Sqlite extend_qtc_library(Sqlite CONDITION TARGET Nanotrace DEPENDS Nanotrace - DEFINES + PUBLIC_DEFINES $<$:ENABLE_SQLITE_TRACING> ) diff --git a/src/libs/sqlite/sqlitebasestatement.cpp b/src/libs/sqlite/sqlitebasestatement.cpp index 79e8cd6db6b..36247484d7e 100644 --- a/src/libs/sqlite/sqlitebasestatement.cpp +++ b/src/libs/sqlite/sqlitebasestatement.cpp @@ -35,12 +35,17 @@ thread_local auto eventQueueData = NanotraceHR::makeEventQueueData sqliteLowLevelCategory{"sqlite low level"_t, - eventQueue}; +thread_local NanotraceHR::StringViewCategory sqliteLowLevelCategory{ + "sqlite low level"_t, eventQueue}; + +thread_local NanotraceHR::StringViewCategory sqliteHighLevelCategory_{ + "sqlite high level"_t, eventQueue}; } // namespace -NanotraceHR::StringViewCategory sqliteHighLevelCategory{"sqlite high level"_t, - eventQueue}; +NanotraceHR::StringViewCategory &sqliteHighLevelCategory() +{ + return sqliteHighLevelCategory_; +} BaseStatement::BaseStatement(Utils::SmallStringView sqlStatement, Database &database) : m_database(database) diff --git a/src/libs/sqlite/sqlitebasestatement.h b/src/libs/sqlite/sqlitebasestatement.h index 84575ec4dbe..00bda5bce39 100644 --- a/src/libs/sqlite/sqlitebasestatement.h +++ b/src/libs/sqlite/sqlitebasestatement.h @@ -53,7 +53,7 @@ constexpr bool sqliteTracingIsEnabled() #endif } -SQLITE_EXPORT extern NanotraceHR::StringViewCategory sqliteHighLevelCategory; +SQLITE_EXPORT NanotraceHR::StringViewCategory &sqliteHighLevelCategory(); class SQLITE_EXPORT BaseStatement { @@ -166,7 +166,7 @@ public: void execute() { - NanotraceHR::Tracer tracer{"execute"_t, sqliteHighLevelCategory}; + NanotraceHR::Tracer tracer{"execute"_t, sqliteHighLevelCategory()}; Resetter resetter{this}; BaseStatement::next(); @@ -175,7 +175,7 @@ public: template void bindValues(const ValueType &...values) { - NanotraceHR::Tracer tracer{"bind"_t, sqliteHighLevelCategory}; + NanotraceHR::Tracer tracer{"bind"_t, sqliteHighLevelCategory()}; static_assert(BindParameterCount == sizeof...(values), "Wrong binding parameter count!"); @@ -186,7 +186,7 @@ public: template void write(const ValueType&... values) { - NanotraceHR::Tracer tracer{"write"_t, sqliteHighLevelCategory}; + NanotraceHR::Tracer tracer{"write"_t, sqliteHighLevelCategory()}; Resetter resetter{this}; bindValues(values...); @@ -212,7 +212,7 @@ public: typename... QueryTypes> auto values(const QueryTypes &...queryValues) { - NanotraceHR::Tracer tracer{"values"_t, sqliteHighLevelCategory}; + NanotraceHR::Tracer tracer{"values"_t, sqliteHighLevelCategory()}; Resetter resetter{this}; Container resultValues; @@ -241,7 +241,7 @@ public: template auto value(const QueryTypes &...queryValues) { - NanotraceHR::Tracer tracer{"values"_t, sqliteHighLevelCategory}; + NanotraceHR::Tracer tracer{"values"_t, sqliteHighLevelCategory()}; Resetter resetter{this}; ResultType resultValue{}; @@ -257,7 +257,7 @@ public: template auto optionalValue(const QueryTypes &...queryValues) { - NanotraceHR::Tracer tracer{"optionalValue"_t, sqliteHighLevelCategory}; + NanotraceHR::Tracer tracer{"optionalValue"_t, sqliteHighLevelCategory()}; Resetter resetter{this}; std::optional resultValue; @@ -273,7 +273,7 @@ public: template static auto toValue(Utils::SmallStringView sqlStatement, Database &database) { - NanotraceHR::Tracer tracer{"toValue"_t, sqliteHighLevelCategory}; + NanotraceHR::Tracer tracer{"toValue"_t, sqliteHighLevelCategory()}; StatementImplementation statement(sqlStatement, database); @@ -287,7 +287,7 @@ public: template void readCallback(Callable &&callable, const QueryTypes &...queryValues) { - NanotraceHR::Tracer tracer{"readCallback"_t, sqliteHighLevelCategory}; + NanotraceHR::Tracer tracer{"readCallback"_t, sqliteHighLevelCategory()}; Resetter resetter{this}; @@ -304,7 +304,7 @@ public: template void readTo(Container &container, const QueryTypes &...queryValues) { - NanotraceHR::Tracer tracer{"readTo"_t, sqliteHighLevelCategory}; + NanotraceHR::Tracer tracer{"readTo"_t, sqliteHighLevelCategory()}; Resetter resetter{this}; @@ -401,8 +401,8 @@ public: const_iterator end() const & { return iterator{m_statement, false}; } private: - NanotraceHR::Tracer tracer{"range"_t, - sqliteHighLevelCategory}; + NanotraceHR::Tracer tracer{"range"_t, + sqliteHighLevelCategory()}; StatementImplementation &m_statement; }; diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.cpp b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.cpp index 48e36206a49..29f732dea60 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.cpp +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.cpp @@ -15,8 +15,8 @@ thread_local auto eventQueueData = NanotraceHR::makeEventQueueData projectStorageCategory{"project storage"_t, - eventQueue}; +thread_local NanotraceHR::StringViewCategory projectStorageCategory{ + "project storage"_t, eventQueue}; } // namespace QmlDesigner template class QmlDesigner::ProjectStorage; diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h index 00610ad83a7..9070cfc2748 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h @@ -37,7 +37,7 @@ constexpr bool projectStorageTracingIsEnabled() #endif } -extern NanotraceHR::StringViewCategory projectStorageCategory; +extern thread_local NanotraceHR::StringViewCategory projectStorageCategory; template class ProjectStorage final : public ProjectStorageInterface From c4deafc555fcbc5ed29570d676dc5b855ddab58f Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Thu, 12 Oct 2023 18:44:00 +0200 Subject: [PATCH 043/242] Nanotrace: Export functions and add dependencies Under linux it was linking But I think it will not work under windows without exports. Change-Id: Idd27583defc1c00a6bb2426efb160e7d90abd3df Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Reviewed-by: Tim Jenssen --- src/libs/nanotrace/nanotracehr.cpp | 16 ++++++++-------- src/libs/nanotrace/nanotracehr.h | 22 +++++++++++----------- tests/unit/tests/unittests/CMakeLists.txt | 6 ++++++ 3 files changed, 25 insertions(+), 19 deletions(-) diff --git a/src/libs/nanotrace/nanotracehr.cpp b/src/libs/nanotrace/nanotracehr.cpp index 6e6c1fbe9d3..c79b16339a2 100644 --- a/src/libs/nanotrace/nanotracehr.cpp +++ b/src/libs/nanotrace/nanotracehr.cpp @@ -46,12 +46,12 @@ void flushEvents(const Utils::span events, } } -template void flushEvents(const Utils::span events, - std::thread::id threadId, - EnabledEventQueue &eventQueue); -template void flushEvents(const Utils::span events, - std::thread::id threadId, - EnabledEventQueue &eventQueue); +template NANOTRACE_EXPORT void flushEvents(const Utils::span events, + std::thread::id threadId, + EnabledEventQueue &eventQueue); +template NANOTRACE_EXPORT void flushEvents(const Utils::span events, + std::thread::id threadId, + EnabledEventQueue &eventQueue); void openFile(EnabledTraceFile &file) { @@ -95,8 +95,8 @@ void flushInThread(EnabledEventQueue &eventQueue) eventQueue.eventsIndex = 0; } -template void flushInThread(EnabledEventQueue &eventQueue); -template void flushInThread(EnabledEventQueue &eventQueue); +template NANOTRACE_EXPORT void flushInThread(EnabledEventQueue &eventQueue); +template NANOTRACE_EXPORT void flushInThread(EnabledEventQueue &eventQueue); namespace { EnabledTraceFile globalTraceFile{"global.json"}; diff --git a/src/libs/nanotrace/nanotracehr.h b/src/libs/nanotrace/nanotracehr.h index 7520574a7c3..297bcb74752 100644 --- a/src/libs/nanotrace/nanotracehr.h +++ b/src/libs/nanotrace/nanotracehr.h @@ -71,25 +71,25 @@ template void flushEvents(const Utils::span events, std::thread::id threadId, EnabledEventQueue &eventQueue); -extern template void flushEvents(const Utils::span events, - std::thread::id threadId, - EnabledEventQueue &eventQueue); -extern template void flushEvents(const Utils::span events, - std::thread::id threadId, - EnabledEventQueue &eventQueue); +extern template NANOTRACE_EXPORT void flushEvents(const Utils::span events, + std::thread::id threadId, + EnabledEventQueue &eventQueue); +extern template NANOTRACE_EXPORT void flushEvents(const Utils::span events, + std::thread::id threadId, + EnabledEventQueue &eventQueue); template void flushInThread(EnabledEventQueue &eventQueue); -extern template void flushInThread(EnabledEventQueue &eventQueue); -extern template void flushInThread(EnabledEventQueue &eventQueue); +extern template NANOTRACE_EXPORT void flushInThread(EnabledEventQueue &eventQueue); +extern template NANOTRACE_EXPORT void flushInThread(EnabledEventQueue &eventQueue); template class TraceFile; using EnabledTraceFile = TraceFile; -void openFile(EnabledTraceFile &file); -void finalizeFile(EnabledTraceFile &file); +NANOTRACE_EXPORT void openFile(EnabledTraceFile &file); +NANOTRACE_EXPORT void finalizeFile(EnabledTraceFile &file); template class TraceFile @@ -216,7 +216,7 @@ EventQueueDataPointer make } } -EnabledEventQueue &globalEventQueue(); +NANOTRACE_EXPORT EnabledEventQueue &globalEventQueue(); template TraceEvent &getTraceEvent(EnabledEventQueue &eventQueue) diff --git a/tests/unit/tests/unittests/CMakeLists.txt b/tests/unit/tests/unittests/CMakeLists.txt index 7088bedcc0a..77cf7a716cd 100644 --- a/tests/unit/tests/unittests/CMakeLists.txt +++ b/tests/unit/tests/unittests/CMakeLists.txt @@ -25,6 +25,12 @@ add_qtc_test(unittest GTEST unittests-main.cpp ) +extend_qtc_test(unittest + CONDITION TARGET Nanotrace + DEPENDS Nanotrace +) + + finalize_qtc_gtest(unittest EXCLUDE_SOURCES_REGEX ".c$" EXCLUDE_ALL_FROM_PRECHECK From d98fa1f173e35a78147e786fd50f2977ae4f33d4 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Thu, 12 Oct 2023 23:10:29 +0200 Subject: [PATCH 044/242] QmlDesigner: Fix typo Change-Id: I95d015bde565995d3c5429e0dbb94782e2dd0981 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Reviewed-by: Tim Jenssen --- src/libs/sqlite/sqlitebasestatement.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/sqlite/sqlitebasestatement.h b/src/libs/sqlite/sqlitebasestatement.h index 00bda5bce39..7774f9a23bc 100644 --- a/src/libs/sqlite/sqlitebasestatement.h +++ b/src/libs/sqlite/sqlitebasestatement.h @@ -241,7 +241,7 @@ public: template auto value(const QueryTypes &...queryValues) { - NanotraceHR::Tracer tracer{"values"_t, sqliteHighLevelCategory()}; + NanotraceHR::Tracer tracer{"value"_t, sqliteHighLevelCategory()}; Resetter resetter{this}; ResultType resultValue{}; From 33084f228e469f31a1d25307018ffa56ab86d1a8 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Fri, 13 Oct 2023 12:15:56 +0200 Subject: [PATCH 045/242] Nanotrace: Only print existing trace points Change-Id: Id65b4154eac7d3cfda247514b7a59abfd7f6d0a5 Reviewed-by: Tim Jenssen --- src/libs/nanotrace/nanotracehr.cpp | 2 +- src/libs/nanotrace/nanotracehr.h | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/libs/nanotrace/nanotracehr.cpp b/src/libs/nanotrace/nanotracehr.cpp index c79b16339a2..7be78cf6a4d 100644 --- a/src/libs/nanotrace/nanotracehr.cpp +++ b/src/libs/nanotrace/nanotracehr.cpp @@ -87,7 +87,7 @@ void flushInThread(EnabledEventQueue &eventQueue) eventQueue.file->processing = std::async(std::launch::async, flush, - eventQueue.currentEvents, + eventQueue.currentEvents.subspan(0, eventQueue.eventsIndex), std::this_thread::get_id()); eventQueue.currentEvents = eventQueue.currentEvents.data() == eventQueue.eventsOne.data() ? eventQueue.eventsTwo diff --git a/src/libs/nanotrace/nanotracehr.h b/src/libs/nanotrace/nanotracehr.h index 297bcb74752..ee8ff808dc8 100644 --- a/src/libs/nanotrace/nanotracehr.h +++ b/src/libs/nanotrace/nanotracehr.h @@ -138,8 +138,10 @@ public: ~EventQueue() { - if (isEnabled == IsEnabled::Yes) - flushEvents(currentEvents, std::this_thread::get_id(), *this); + if (isEnabled == IsEnabled::Yes && eventsIndex > 0) { + flushEvents(currentEvents.subspan(0, eventsIndex), std::this_thread::get_id(), *this); + eventsIndex = 0; + } } EventQueue(const EventQueue &) = delete; From 1faa8e29e298a0c8f76a78b7c95d929503ef20e4 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Fri, 13 Oct 2023 12:16:32 +0200 Subject: [PATCH 046/242] Nanotrace: Add asynchronous trace points With asynchronous traces you can follow complex tasks. Change-Id: Ia0fd20f34f3529c59eff5d222c8d87ac5dacd514 Reviewed-by: Tim Jenssen --- src/libs/nanotrace/nanotracehr.cpp | 101 +++++++- src/libs/nanotrace/nanotracehr.h | 236 +++++++++++++----- src/libs/sqlite/sqlitebasestatement.h | 4 +- src/plugins/qmldesigner/CMakeLists.txt | 7 +- .../imagecache/asynchronousimagecache.cpp | 62 ++++- .../include/asynchronousimagecache.h | 30 ++- 6 files changed, 358 insertions(+), 82 deletions(-) diff --git a/src/libs/nanotrace/nanotracehr.cpp b/src/libs/nanotrace/nanotracehr.cpp index 7be78cf6a4d..4837cfe9787 100644 --- a/src/libs/nanotrace/nanotracehr.cpp +++ b/src/libs/nanotrace/nanotracehr.cpp @@ -4,13 +4,17 @@ #include "nanotracehr.h" #include +#include #include #include -#include #include #include +#ifdef Q_OS_UNIX +# include +#endif + namespace NanotraceHR { namespace { @@ -18,11 +22,50 @@ namespace { template void printEvent(std::ostream &out, const TraceEvent &event, qint64 processId, std::thread::id threadId) { - out << R"({"ph":"X","name":")" << event.name << R"(","cat":")" << event.category - << R"(","ts":")" << static_cast(event.start.time_since_epoch().count()) / 1000 - << R"(","dur":")" << static_cast(event.duration.count()) / 1000 << R"(","pid":")" - << processId << R"(","tid":")" << threadId << R"(","args":)" << event.arguments << "}"; + out << R"({"ph":")" << event.type << R"(","name":")" << event.name << R"(","cat":")" + << event.category << R"(","ts":")" + << static_cast(event.time.time_since_epoch().count()) / 1000 << R"(","pid":")" + << processId << R"(","tid":")" << threadId << R"(")"; + + if (event.type == 'X') + out << R"(,"dur":)" << static_cast(event.duration.count()) / 1000; + + if (event.id != 0) + out << R"(,"id":)" << event.id; + + if (event.arguments.size()) + out << R"(,"args":)" << event.arguments; + + out << "}"; } + +void writeMetaEvent(TraceFile *file, std::string_view key, std::string_view value) +{ + std::lock_guard lock{file->fileMutex}; + auto &out = file->out; + + if (out.is_open()) { + file->out << R"({"name":")" << key << R"(","ph":"M", "pid":")" + << QCoreApplication::applicationPid() << R"(","tid":")" + << std::this_thread::get_id() << R"(","args":{"name":")" << value << R"("}})" + << ",\n"; + } +} + +std::string getThreadName() +{ + std::array buffer; + buffer[0] = 0; +#ifdef Q_OS_UNIX + auto rc = pthread_getname_np(pthread_self(), buffer.data(), buffer.size()); + if (rc != 0) + return {}; + +#endif + + return buffer.data(); +} + } // namespace template @@ -42,7 +85,6 @@ void flushEvents(const Utils::span events, printEvent(out, event, processId, threadId); out << ",\n"; } - out << std::flush; } } @@ -57,8 +99,12 @@ void openFile(EnabledTraceFile &file) { std::lock_guard lock{file.fileMutex}; - if (file.out = std::ofstream{file.filePath, std::ios::trunc}; file.out.good()) + if (file.out = std::ofstream{file.filePath, std::ios::trunc}; file.out.good()) { file.out << std::fixed << std::setprecision(3) << R"({"traceEvents": [)"; + file.out << R"({"name":"process_name","ph":"M", "pid":)" + << QCoreApplication::applicationPid() << R"(,"args":{"name":"QtCreator"}})" + << ",\n"; + } } void finalizeFile(EnabledTraceFile &file) @@ -109,4 +155,45 @@ EnabledEventQueue &globalEventQueue() return s_globalEventQueue; } +template +EventQueue::EventQueue(EnabledTraceFile *file, + TraceEventsSpan eventsOne, + TraceEventsSpan eventsTwo) + : file{file} + , eventsOne{eventsOne} + , eventsTwo{eventsTwo} + , currentEvents{eventsOne} +{ + if (auto thread = QThread::currentThread()) { + connection = QObject::connect(QCoreApplication::instance(), + &QCoreApplication::aboutToQuit, + thread, + [&] { flush(); }); + auto name = getThreadName(); + if (name.size()) { + writeMetaEvent(file, "thread_name", name); + } + } +} + +template +EventQueue::~EventQueue() +{ + flush(); + if (connection) + QObject::disconnect(connection); +} + +template +void EventQueue::flush() +{ + std::lock_guard lock{mutex}; + if (isEnabled == IsEnabled::Yes && eventsIndex > 0) { + flushEvents(currentEvents.subspan(0, eventsIndex), std::this_thread::get_id(), *this); + eventsIndex = 0; + } +} + +template class NANOTRACE_EXPORT EventQueue; +template class NANOTRACE_EXPORT EventQueue; } // namespace NanotraceHR diff --git a/src/libs/nanotrace/nanotracehr.h b/src/libs/nanotrace/nanotracehr.h index ee8ff808dc8..e2ec3823b9d 100644 --- a/src/libs/nanotrace/nanotracehr.h +++ b/src/libs/nanotrace/nanotracehr.h @@ -7,7 +7,10 @@ #include +#include + #include +#include #include #include #include @@ -33,15 +36,34 @@ constexpr bool isTracerActive() #endif } -template -std::string_view toStringView(Utils::span string) +namespace Literals { +struct TracerLiteral { - return {string.data(), string.size()}; -} + friend constexpr TracerLiteral operator""_t(const char *text, size_t size); + constexpr operator std::string_view() const { return text; } + +private: + constexpr TracerLiteral(std::string_view text) + : text{text} + {} + + std::string_view text; +}; + +constexpr TracerLiteral operator""_t(const char *text, size_t size) +{ + return {std::string_view{text, size}}; +} +} // namespace Literals + +using namespace Literals; template struct TraceEvent { + using StringType = String; + using ArgumentType = std::conditional_t, TracerLiteral, String>; + TraceEvent() = default; TraceEvent(const TraceEvent &) = delete; TraceEvent(TraceEvent &&) = delete; @@ -52,8 +74,10 @@ struct TraceEvent String name; String category; String arguments; - TimePoint start; + TimePoint time; Duration duration; + std::size_t id = 0; + char type = ' '; }; using StringViewTraceEvent = TraceEvent; @@ -126,7 +150,10 @@ public: template class EventQueue -{}; +{ +public: + using IsActive = std::false_type; +}; template class EventQueue @@ -134,15 +161,13 @@ class EventQueue using TraceEventsSpan = Utils::span; public: - EventQueue() = default; + using IsActive = std::true_type; - ~EventQueue() - { - if (isEnabled == IsEnabled::Yes && eventsIndex > 0) { - flushEvents(currentEvents.subspan(0, eventsIndex), std::this_thread::get_id(), *this); - eventsIndex = 0; - } - } + EventQueue(EnabledTraceFile *file, TraceEventsSpan eventsOne, TraceEventsSpan eventsTwo); + + ~EventQueue(); + + void flush(); EventQueue(const EventQueue &) = delete; EventQueue(EventQueue &&) = delete; @@ -154,14 +179,17 @@ public: TraceEventsSpan eventsTwo; TraceEventsSpan currentEvents; std::size_t eventsIndex = 0; - IsEnabled isEnabled = IsEnabled::No; + IsEnabled isEnabled = IsEnabled::Yes; + QMetaObject::Connection connection; + std::mutex mutex; }; +extern template class NANOTRACE_EXPORT EventQueue; +extern template class NANOTRACE_EXPORT EventQueue; + template class EventQueueData { - using TraceEvents = std::array; - public: using IsActive = Enabled; @@ -197,7 +225,7 @@ struct EventQueueDataPointer EnabledEventQueue createEventQueue() const { if constexpr (isTracerActive()) { - return {&data->file, data->eventsOne, data->eventsTwo, data->eventsOne, 0, IsEnabled::Yes}; + return {&data->file, data->eventsOne, data->eventsTwo}; } else { return {}; } @@ -229,38 +257,63 @@ TraceEvent &getTraceEvent(EnabledEventQueue &eventQueue) return eventQueue.currentEvents[eventQueue.eventsIndex++]; } -namespace Literals { -struct TracerLiteral +template +class Token { - friend constexpr TracerLiteral operator""_t(const char *text, size_t size); +public: + using IsActive = std::false_type; - constexpr operator std::string_view() const { return text; } + constexpr std::size_t operator*() const { return 0; } -private: - constexpr TracerLiteral(std::string_view text) - : text{text} - {} + constexpr explicit operator bool() const { return false; } - std::string_view text; + static constexpr bool isActive() { return false; } }; -constexpr TracerLiteral operator""_t(const char *text, size_t size) -{ - return {std::string_view{text, size}}; -} -} // namespace Literals +template +class Category; -using namespace Literals; +template<> +class Token +{ + friend Category; + friend Category; + + Token(std::size_t id) + : m_id{id} + {} + +public: + using IsActive = std::true_type; + + constexpr std::size_t operator*() const { return m_id; } + + constexpr explicit operator bool() const { return m_id; } + + static constexpr bool isActive() { return true; } + +private: + std::size_t m_id; +}; template class Category { public: using IsActive = std::false_type; + using ArgumentType = typename TraceEvent::ArgumentType; - Category(TracerLiteral, EventQueue &) {} + Category(ArgumentType, EventQueue &) {} - Category(TracerLiteral, EventQueue &) {} + Category(ArgumentType, EventQueue &) {} + + Token beginAsynchronous(ArgumentType) { return {}; } + + void tickAsynchronous(Token, ArgumentType) {} + + void endAsynchronous(Token, ArgumentType) {} + + static constexpr bool isActive() { return false; } }; template @@ -268,8 +321,67 @@ class Category { public: using IsActive = std::true_type; - TracerLiteral name; - EnabledEventQueue &eventQueue; + using ArgumentType = typename TraceEvent::ArgumentType; + using StringType = typename TraceEvent::StringType; + + template + Category(ArgumentType name, EventQueue &queue) + : m_name{std::move(name)} + , m_eventQueue{queue} + { + static_assert(std::is_same_v, + "A active category is not possible with an inactive event queue!"); + } + + Token beginAsynchronous(ArgumentType traceName) + { + auto id = ++idCounter; + auto &traceEvent = getTraceEvent(m_eventQueue); + traceEvent.name = std::move(traceName); + traceEvent.category = m_name; + traceEvent.time = Clock::now(); + traceEvent.type = 'b'; + traceEvent.id = id; + + return id; + } + + void tickAsynchronous(Token token, ArgumentType traceName) + { + if (!token) + return; + + auto &traceEvent = getTraceEvent(m_eventQueue); + traceEvent.name = std::move(traceName); + traceEvent.category = m_name; + traceEvent.time = Clock::now(); + traceEvent.type = 'n'; + traceEvent.id = *token; + } + + void endAsynchronous(Token token, ArgumentType traceName) + { + if (!token) + return; + + auto &traceEvent = getTraceEvent(m_eventQueue); + traceEvent.name = std::move(traceName); + traceEvent.category = m_name; + traceEvent.time = Clock::now(); + traceEvent.type = 'e'; + traceEvent.id = *token; + } + + EnabledEventQueue &eventQueue() const { return m_eventQueue; } + + std::string_view name() const { return m_name; } + + static constexpr bool isActive() { return true; } + +private: + StringType m_name; + EnabledEventQueue &m_eventQueue; + inline static std::atomic idCounter = 0; }; template @@ -277,18 +389,15 @@ using StringViewCategory = Category; template using StringCategory = Category; -class DisabledCategory -{ - using IsActive = std::false_type; -}; - template class Tracer { public: - constexpr Tracer(TracerLiteral, Category &, TracerLiteral) {} + using ArgumentType = typename Category::ArgumentType; - constexpr Tracer(TracerLiteral, Category &) {} + constexpr Tracer(ArgumentType, Category &, ArgumentType) {} + + constexpr Tracer(ArgumentType, Category &) {} ~Tracer() {} }; @@ -297,22 +406,22 @@ template<> class Tracer> { public: - constexpr Tracer(TracerLiteral name, StringViewCategory &category, TracerLiteral arguments) + Tracer(TracerLiteral name, StringViewCategory &category, TracerLiteral arguments) : m_name{name} , m_arguments{arguments} , m_category{category} { if constexpr (isTracerActive()) { - if (category.eventQueue.isEnabled == IsEnabled::Yes) + if (category.eventQueue().isEnabled == IsEnabled::Yes) m_start = Clock::now(); } } - constexpr Tracer(TracerLiteral name, StringViewCategory &category) - : Tracer{name, category, "{}"_t} + Tracer(TracerLiteral name, StringViewCategory &category) + : Tracer{name, category, ""_t} { if constexpr (isTracerActive()) { - if (category.eventQueue.isEnabled == IsEnabled::Yes) + if (category.eventQueue().isEnabled == IsEnabled::Yes) m_start = Clock::now(); } } @@ -320,14 +429,15 @@ public: ~Tracer() { if constexpr (isTracerActive()) { - if (m_category.eventQueue.isEnabled == IsEnabled::Yes) { + if (m_category.eventQueue().isEnabled == IsEnabled::Yes) { auto duration = Clock::now() - m_start; - auto &traceEvent = getTraceEvent(m_category.eventQueue); + auto &traceEvent = getTraceEvent(m_category.eventQueue()); traceEvent.name = m_name; - traceEvent.category = m_category.name; + traceEvent.category = m_category.name(); traceEvent.arguments = m_arguments; - traceEvent.start = m_start; + traceEvent.time = m_start; traceEvent.duration = duration; + traceEvent.type = 'X'; } } } @@ -349,16 +459,16 @@ public: , m_category{category} { if constexpr (isTracerActive()) { - if (category.eventQueue.isEnabled == IsEnabled::Yes) + if (category.eventQueue().isEnabled == IsEnabled::Yes) m_start = Clock::now(); } } Tracer(std::string name, StringViewCategory &category) - : Tracer{std::move(name), category, "{}"} + : Tracer{std::move(name), category, ""} { if constexpr (isTracerActive()) { - if (category.eventQueue.isEnabled == IsEnabled::Yes) + if (category.eventQueue().isEnabled == IsEnabled::Yes) m_start = Clock::now(); } } @@ -366,14 +476,15 @@ public: ~Tracer() { if constexpr (isTracerActive()) { - if (m_category.eventQueue.isEnabled == IsEnabled::Yes) { + if (m_category.eventQueue().isEnabled == IsEnabled::Yes) { auto duration = Clock::now() - m_start; - auto &traceEvent = getTraceEvent(m_category.eventQueue); + auto &traceEvent = getTraceEvent(m_category.eventQueue()); traceEvent.name = std::move(m_name); - traceEvent.category = m_category.name; + traceEvent.category = m_category.name(); traceEvent.arguments = std::move(m_arguments); - traceEvent.start = m_start; + traceEvent.time = m_start; traceEvent.duration = duration; + traceEvent.type = 'X'; } } } @@ -403,7 +514,7 @@ public: } GlobalTracer(std::string name, std::string category) - : GlobalTracer{std::move(name), std::move(category), "{}"} + : GlobalTracer{std::move(name), std::move(category), ""} {} ~GlobalTracer() @@ -415,8 +526,9 @@ public: traceEvent.name = std::move(m_name); traceEvent.category = std::move(m_category); traceEvent.arguments = std::move(m_arguments); - traceEvent.start = std::move(m_start); + traceEvent.time = std::move(m_start); traceEvent.duration = std::move(duration); + traceEvent.type = 'X'; } } } diff --git a/src/libs/sqlite/sqlitebasestatement.h b/src/libs/sqlite/sqlitebasestatement.h index 7774f9a23bc..c507b0b9d3b 100644 --- a/src/libs/sqlite/sqlitebasestatement.h +++ b/src/libs/sqlite/sqlitebasestatement.h @@ -401,8 +401,8 @@ public: const_iterator end() const & { return iterator{m_statement, false}; } private: - NanotraceHR::Tracer tracer{"range"_t, - sqliteHighLevelCategory()}; + NanotraceHR::Tracer> tracer{ + "range"_t, sqliteHighLevelCategory()}; StatementImplementation &m_statement; }; diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index 7b54937f181..e92dc29942f 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -16,9 +16,13 @@ option(USE_PROJECTSTORAGE "Use ProjectStorage" ${ENV_QDS_USE_PROJECTSTORAGE}) add_feature_info("ProjectStorage" ${USE_PROJECTSTORAGE} "") env_with_default("QTC_ENABLE_PROJECT_STORAGE_TRACING" ENV_QTC_ENABLE_PROJECT_STORAGE_TRACING OFF) -option(ENABLE_PROJECT_STORAGE_TRACING "Enable sqlite tarcing" ${ENV_QTC_ENABLE_PROJECT_STORAGE_TRACING}) +option(ENABLE_PROJECT_STORAGE_TRACING "Enable sqlite tracing" ${ENV_QTC_ENABLE_PROJECT_STORAGE_TRACING}) add_feature_info("Sqlite tracing" ${ENABLE_PROJECT_STORAGE_TRACING} "") +env_with_default("QTC_ENABLE_IMAGE_CACHE_TRACING" ENV_QTC_ENABLE_IMAGE_CACHE_TRACING OFF) +option(ENABLE_IMAGE_CACHE_TRACING "Enable image cache tracing" ${ENV_QTC_ENABLE_IMAGE_CACHE_TRACING}) +add_feature_info("Image cache tracing" ${ENABLE_IMAGE_CACHE_TRACING} "") + add_qtc_library(QmlDesignerUtils STATIC DEPENDS @@ -93,6 +97,7 @@ extend_qtc_library(QmlDesignerCore DEPENDS Nanotrace DEFINES $<$:ENABLE_PROJECT_STORAGE_TRACING> + $<$:ENABLE_IMAGE_CACHE_TRACING> ) extend_qtc_library(QmlDesignerCore diff --git a/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagecache.cpp b/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagecache.cpp index a18e051c364..4da56f18eae 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagecache.cpp +++ b/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagecache.cpp @@ -7,18 +7,39 @@ #include "imagecachestorage.h" #include "timestampprovider.h" +#include + #include namespace QmlDesigner { +using namespace NanotraceHR::Literals; + +namespace { +using TraceFile = NanotraceHR::TraceFile; + +TraceFile traceFile{"qml_designer.json"}; + +thread_local auto eventQueueData = NanotraceHR::makeEventQueueData( + traceFile); +thread_local NanotraceHR::EventQueue eventQueue = eventQueueData.createEventQueue(); + +thread_local NanotraceHR::StringViewCategory category{"image cache"_t, + eventQueue}; +} // namespace + +NanotraceHR::StringViewCategory &imageCacheCategory() +{ + return category; +} + AsynchronousImageCache::AsynchronousImageCache(ImageCacheStorageInterface &storage, ImageCacheGeneratorInterface &generator, TimeStampProviderInterface &timeStampProvider) : m_storage(storage) , m_generator(generator) , m_timeStampProvider(timeStampProvider) -{ -} +{} AsynchronousImageCache::~AsynchronousImageCache() { @@ -31,6 +52,7 @@ void AsynchronousImageCache::request(Utils::SmallStringView name, ImageCache::CaptureImageCallback captureCallback, ImageCache::AbortCallback abortCallback, ImageCache::AuxiliaryData auxiliaryData, + ImageCacheTraceToken traceToken, ImageCacheStorageInterface &storage, ImageCacheGeneratorInterface &generator, TimeStampProviderInterface &timeStampProvider) @@ -40,6 +62,11 @@ void AsynchronousImageCache::request(Utils::SmallStringView name, const auto timeStamp = timeStampProvider.timeStamp(name); auto requestImageFromStorage = [&](RequestType requestType) { + imageCacheCategory().tickAsynchronous(traceToken, "start fetching image from storage"_t); + + QScopeGuard finally{[=] { + imageCacheCategory().tickAsynchronous(traceToken, "end fetching image from storage"_t); + }}; switch (requestType) { case RequestType::Image: return storage.fetchImage(id, timeStamp); @@ -56,14 +83,19 @@ void AsynchronousImageCache::request(Utils::SmallStringView name, const auto entry = requestImageFromStorage(requestType); if (entry) { - if (entry->isNull()) + if (entry->isNull()) { abortCallback(ImageCache::AbortReason::Failed); - else + imageCacheCategory().endAsynchronous(traceToken, + "abort image request because entry in database is null"_t); + } else { captureCallback(*entry); + imageCacheCategory().endAsynchronous(traceToken, "image request delivered from storage"_t); + } } else { auto callback = [captureCallback = std::move(captureCallback), - requestType](const QImage &image, const QImage &midSizeImage, const QImage &smallImage) { + requestType, + traceToken](const QImage &image, const QImage &midSizeImage, const QImage &smallImage) { auto selectImage = [](RequestType requestType, const QImage &image, const QImage &midSizeImage, @@ -82,7 +114,12 @@ void AsynchronousImageCache::request(Utils::SmallStringView name, return image; }; captureCallback(selectImage(requestType, image, midSizeImage, smallImage)); + imageCacheCategory().endAsynchronous(traceToken, + "image request delivered from generation"_t); }; + + imageCacheCategory().tickAsynchronous(traceToken, "request image generation"_t); + generator.generateImage(name, extraId, timeStamp, @@ -98,12 +135,15 @@ void AsynchronousImageCache::requestImage(Utils::SmallStringView name, Utils::SmallStringView extraId, ImageCache::AuxiliaryData auxiliaryData) { + auto traceToken = imageCacheCategory().beginAsynchronous( + "request image in asynchornous image cache"_t); m_taskQueue.addTask(std::move(name), std::move(extraId), std::move(captureCallback), std::move(abortCallback), std::move(auxiliaryData), - RequestType::Image); + RequestType::Image, + traceToken); } void AsynchronousImageCache::requestMidSizeImage(Utils::SmallStringView name, @@ -112,12 +152,15 @@ void AsynchronousImageCache::requestMidSizeImage(Utils::SmallStringView name, Utils::SmallStringView extraId, ImageCache::AuxiliaryData auxiliaryData) { + auto traceToken = imageCacheCategory().beginAsynchronous( + "request mid size image in asynchornous image cache"_t); m_taskQueue.addTask(std::move(name), std::move(extraId), std::move(captureCallback), std::move(abortCallback), std::move(auxiliaryData), - RequestType::MidSizeImage); + RequestType::MidSizeImage, + traceToken); } void AsynchronousImageCache::requestSmallImage(Utils::SmallStringView name, @@ -126,12 +169,15 @@ void AsynchronousImageCache::requestSmallImage(Utils::SmallStringView name, Utils::SmallStringView extraId, ImageCache::AuxiliaryData auxiliaryData) { + auto traceToken = imageCacheCategory().beginAsynchronous( + "request small size image in asynchornous image cache"_t); m_taskQueue.addTask(std::move(name), std::move(extraId), std::move(captureCallback), std::move(abortCallback), std::move(auxiliaryData), - RequestType::SmallImage); + RequestType::SmallImage, + traceToken); } void AsynchronousImageCache::clean() diff --git a/src/plugins/qmldesigner/designercore/include/asynchronousimagecache.h b/src/plugins/qmldesigner/designercore/include/asynchronousimagecache.h index 3eed4a4487c..fc03cccce8e 100644 --- a/src/plugins/qmldesigner/designercore/include/asynchronousimagecache.h +++ b/src/plugins/qmldesigner/designercore/include/asynchronousimagecache.h @@ -6,6 +6,7 @@ #include "asynchronousimagecacheinterface.h" #include +#include #include #include @@ -21,6 +22,19 @@ class ImageCacheStorageInterface; class ImageCacheGeneratorInterface; class ImageCacheCollectorInterface; +constexpr bool imageCacheTracingIsEnabled() +{ +#ifdef ENABLE_IMAGE_CACHE_TRACING + return NanotraceHR::isTracerActive(); +#else + return false; +#endif +} + +using ImageCacheTraceToken = NanotraceHR::Token; + +NanotraceHR::StringViewCategory &imageCacheCategory(); + class AsynchronousImageCache final : public AsynchronousImageCacheInterface { public: @@ -53,18 +67,21 @@ private: struct Entry { Entry() = default; + Entry(Utils::PathString name, Utils::SmallString extraId, ImageCache::CaptureImageCallback &&captureCallback, ImageCache::AbortCallback &&abortCallback, ImageCache::AuxiliaryData &&auxiliaryData, - RequestType requestType) + RequestType requestType, + ImageCacheTraceToken traceToken) : name{std::move(name)} , extraId{std::move(extraId)} , captureCallback{std::move(captureCallback)} , abortCallback{std::move(abortCallback)} , auxiliaryData{std::move(auxiliaryData)} , requestType{requestType} + , traceToken{traceToken} {} Utils::PathString name; @@ -73,6 +90,7 @@ private: ImageCache::AbortCallback abortCallback; ImageCache::AuxiliaryData auxiliaryData; RequestType requestType = RequestType::Image; + ImageCacheTraceToken traceToken; }; static void request(Utils::SmallStringView name, @@ -81,6 +99,7 @@ private: ImageCache::CaptureImageCallback captureCallback, ImageCache::AbortCallback abortCallback, ImageCache::AuxiliaryData auxiliaryData, + ImageCacheTraceToken traceToken, ImageCacheStorageInterface &storage, ImageCacheGeneratorInterface &generator, TimeStampProviderInterface &timeStampProvider); @@ -95,6 +114,7 @@ private: std::move(entry.captureCallback), std::move(entry.abortCallback), std::move(entry.auxiliaryData), + entry.traceToken, storage, generator, timeStampProvider); @@ -107,7 +127,13 @@ private: struct Clean { - void operator()(Entry &entry) { entry.abortCallback(ImageCache::AbortReason::Abort); } + void operator()(Entry &entry) + { + using namespace NanotraceHR::Literals; + + entry.abortCallback(ImageCache::AbortReason::Abort); + imageCacheCategory().endAsynchronous(entry.traceToken, "aborted for cleanup"_t); + } }; private: From c5e40108420d6cd35432f2696741bdb85b985d3d Mon Sep 17 00:00:00 2001 From: Tim Jenssen Date: Tue, 26 Sep 2023 19:18:33 +0200 Subject: [PATCH 047/242] QmlDesigner: increase version to 4.4.0 Change-Id: I6c628d67162daad1921985c7f79676811df1647c Reviewed-by: Reviewed-by: Tim Jenssen Reviewed-by: Qt CI Patch Build Bot --- dist/branding/qtdesignstudio/QtCreatorIDEBranding.cmake | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dist/branding/qtdesignstudio/QtCreatorIDEBranding.cmake b/dist/branding/qtdesignstudio/QtCreatorIDEBranding.cmake index 5877838a481..59b9fdf3138 100644 --- a/dist/branding/qtdesignstudio/QtCreatorIDEBranding.cmake +++ b/dist/branding/qtdesignstudio/QtCreatorIDEBranding.cmake @@ -1,6 +1,6 @@ -set(IDE_VERSION "4.3.0") # The IDE version. -set(IDE_VERSION_COMPAT "4.3.0") # The IDE Compatibility version. -set(IDE_VERSION_DISPLAY "4.3.0") # The IDE display version. +set(IDE_VERSION "4.4.0") # The IDE version. +set(IDE_VERSION_COMPAT "4.4.0") # The IDE Compatibility version. +set(IDE_VERSION_DISPLAY "4.4.0") # The IDE display version. set(IDE_COPYRIGHT_YEAR "2023") # The IDE current copyright year. set(IDE_SETTINGSVARIANT "QtProject") # The IDE settings variation. From d8bbbd2494fba55b4cf781a47fe1a62653b1ff39 Mon Sep 17 00:00:00 2001 From: Amr Essam Date: Wed, 11 Oct 2023 18:59:03 +0300 Subject: [PATCH 048/242] QmlDesigner: Compile effect maker shaders using qsb tool Task-number: QDS-10811 Change-Id: I6028dea262b2658838b59156ac062657cc4ef3f0 Reviewed-by: Miikka Heikkinen Reviewed-by: Mahmoud Badri --- .../EffectMakerPreview.qml | 12 +-- src/plugins/effectmakernew/CMakeLists.txt | 2 +- .../effectmakernew/effectmakermodel.cpp | 92 +++++++++++++------ src/plugins/effectmakernew/effectmakermodel.h | 12 ++- 4 files changed, 83 insertions(+), 35 deletions(-) diff --git a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMakerPreview.qml b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMakerPreview.qml index c6ffe83a168..03d587e8195 100644 --- a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMakerPreview.qml +++ b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMakerPreview.qml @@ -18,7 +18,7 @@ Column { property var effectMakerModel: EffectMakerBackend.effectMakerModel property alias source: source // The delay in ms to wait until updating the effect - readonly property int updateDelay: 200 + readonly property int updateDelay: 100 // Create a dummy parent to host the effect qml object function createNewComponent() { @@ -171,7 +171,7 @@ Column { id: componentParent width: source.width height: source.height - anchors.centerIn: parent + anchors.centerIn: parent scale: 1 //TODO should come from toolbar // Cache the layer. This way heavy shaders rendering doesn't // slow down code editing & rest of the UI. @@ -183,16 +183,16 @@ Column { target: effectMakerModel function onShadersBaked() { console.log("Shaders Baked!") - //updateTimer.restart(); // Disable for now + updateTimer.restart() } } Timer { id: updateTimer - interval: updateDelay; + interval: updateDelay onTriggered: { - effectMakerModel.updateQmlComponent(); - createNewComponent(); + effectMakerModel.updateQmlComponent() + createNewComponent() } } } diff --git a/src/plugins/effectmakernew/CMakeLists.txt b/src/plugins/effectmakernew/CMakeLists.txt index d01c97618c7..21646e31322 100644 --- a/src/plugins/effectmakernew/CMakeLists.txt +++ b/src/plugins/effectmakernew/CMakeLists.txt @@ -3,7 +3,7 @@ find_package(Qt6 OPTIONAL_COMPONENTS Gui Quick ShaderTools) add_qtc_plugin(EffectMakerNew CONDITION TARGET QmlDesigner AND TARGET Qt::ShaderTools PLUGIN_DEPENDS - QtCreator::Core QtCreator::QmlDesigner QtCreator::ProjectExplorer + QtCreator::Core QtCreator::QmlDesigner QtCreator::ProjectExplorer QtCreator::QmlProjectManager DEPENDS Qt::Core QtCreator::Utils Qt::CorePrivate Qt::Widgets Qt::Qml Qt::QmlPrivate Qt::Quick Qt::ShaderTools Qt::ShaderToolsPrivate diff --git a/src/plugins/effectmakernew/effectmakermodel.cpp b/src/plugins/effectmakernew/effectmakermodel.cpp index da8403108f3..94de609c2e8 100644 --- a/src/plugins/effectmakernew/effectmakermodel.cpp +++ b/src/plugins/effectmakernew/effectmakermodel.cpp @@ -7,8 +7,7 @@ #include "syntaxhighlighterdata.h" #include "uniform.h" -#include -#include +#include #include #include @@ -17,6 +16,10 @@ #include #include +#include + +#include +#include namespace EffectMaker { @@ -49,8 +52,14 @@ EffectMakerModel::EffectMakerModel(QObject *parent) m_vertexShaderFile.setFileTemplate(QDir::tempPath() + "/dsem_XXXXXX.vert.qsb"); m_fragmentShaderFile.setFileTemplate(QDir::tempPath() + "/dsem_XXXXXX.frag.qsb"); if (!m_vertexSourceFile.open() || !m_fragmentSourceFile.open() - || !m_vertexShaderFile.open() || !m_fragmentShaderFile.open()) + || !m_vertexShaderFile.open() || !m_fragmentShaderFile.open()) { qWarning() << "Unable to open temporary files"; + } else { + m_vertexSourceFilename = m_vertexSourceFile.fileName(); + m_fragmentSourceFilename = m_fragmentSourceFile.fileName(); + m_vertexShaderFilename = m_vertexShaderFile.fileName(); + m_fragmentShaderFilename = m_fragmentShaderFile.fileName(); + } } QHash EffectMakerModel::roleNames() const @@ -720,6 +729,24 @@ QString EffectMakerModel::generateFragmentShader(bool includeUniforms) return s; } +void EffectMakerModel::handleQsbProcessExit(Utils::Process *qsbProcess, const QString &shader) +{ + --m_remainingQsbTargets; + + const QString errStr = qsbProcess->errorString(); + if (!errStr.isEmpty()) + qWarning() << QString("Failed to generate QSB file for: %1 %2").arg(shader, errStr); + + if (m_remainingQsbTargets <= 0) { + Q_EMIT shadersBaked(); + setShadersUpToDate(true); + + // TODO: Mark shaders as baked, required by export later + } + + qsbProcess->deleteLater(); +} + // Generates string of the custom properties (uniforms) into ShaderEffect component // Also generates QML images elements for samplers. void EffectMakerModel::updateCustomUniforms() @@ -790,6 +817,14 @@ void EffectMakerModel::updateCustomUniforms() void EffectMakerModel::bakeShaders() { + const QString failMessage = "Shader baking failed: "; + + const ProjectExplorer::Target *target = ProjectExplorer::ProjectTree::currentTarget(); + if (!target) { + qWarning() << failMessage << "Target not found"; + return; + } + resetEffectError(ErrorPreprocessor); if (m_vertexShader == generateVertexShader() && m_fragmentShader == generateFragmentShader()) { setShadersUpToDate(true); @@ -813,12 +848,34 @@ void EffectMakerModel::bakeShaders() QString fs = m_fragmentShader; writeToFile(fs.toUtf8(), m_fragmentSourceFile.fileName(), FileType::Text); - //TODO: Compile shaders using external qsb tools + QtSupport::QtVersion *qtVer = QtSupport::QtKitAspect::qtVersion(target->kit()); + if (!qtVer) { + qWarning() << failMessage << "Qt version not found"; + return; + } - Q_EMIT shadersBaked(); - setShadersUpToDate(true); + Utils::FilePath qsbPath = qtVer->binPath().pathAppended("qsb").withExecutableSuffix(); + if (!qsbPath.exists()) { + qWarning() << failMessage << "QSB tool not found"; + return; + } - // TODO: Mark shaders as baked, required by export later + m_remainingQsbTargets = 2; // We only have 2 shaders + const QStringList srcPaths = {m_vertexSourceFilename, m_fragmentSourceFilename}; + const QStringList outPaths = {m_vertexShaderFilename, m_fragmentShaderFilename}; + for (int i = 0; i < 2; ++i) { + const auto workDir = Utils::FilePath::fromString(outPaths[i]); + QStringList args = {"-s", "--glsl", "\"300 es,120,150,440\"", "--hlsl", "50", "--msl", "12"}; + args << "-o" << outPaths[i] << srcPaths[i]; + + auto qsbProcess = new Utils::Process(this); + connect(qsbProcess, &Utils::Process::done, this, [=] { + handleQsbProcessExit(qsbProcess, srcPaths[i]); + }); + qsbProcess->setWorkingDirectory(workDir.absolutePath()); + qsbProcess->setCommand({qsbPath, args}); + qsbProcess->start(); + } } bool EffectMakerModel::shadersUpToDate() const @@ -857,8 +914,6 @@ QString EffectMakerModel::getQmlComponentString(bool localFiles) }; QString customImagesString = getQmlImagesString(localFiles); - QString vertexShaderFilename = "file:///" + m_fragmentShaderFile.fileName(); - QString fragmentShaderFilename = "file:///" + m_vertexShaderFile.fileName(); QString s; QString l1 = localFiles ? QStringLiteral(" ") : QStringLiteral(""); QString l2 = localFiles ? QStringLiteral(" ") : QStringLiteral(" "); @@ -896,8 +951,8 @@ QString EffectMakerModel::getQmlComponentString(bool localFiles) s += '\n' + customImagesString; s += '\n'; - s += l2 + "vertexShader: '" + vertexShaderFilename + "'\n"; - s += l2 + "fragmentShader: '" + fragmentShaderFilename + "'\n"; + s += l2 + "vertexShader: 'file://" + m_vertexShaderFilename + "'\n"; + s += l2 + "fragmentShader: 'file://" + m_fragmentShaderFilename + "'\n"; s += l2 + "anchors.fill: parent\n"; if (m_shaderFeatures.enabled(ShaderFeatures::GridMesh)) { QString gridSize = QString("%1, %2").arg(m_shaderFeatures.gridMeshWidth()).arg(m_shaderFeatures.gridMeshHeight()); @@ -909,21 +964,6 @@ QString EffectMakerModel::getQmlComponentString(bool localFiles) return s; } -Utils::FilePath EffectMakerModel::qsbPath() const -{ - const ProjectExplorer::Target *target = ProjectExplorer::ProjectTree::currentTarget(); - if (target) { - if (QtSupport::QtVersion *qtVer = QtSupport::QtKitAspect::qtVersion(target->kit())) { - Utils::FilePath path = qtVer->binPath().pathAppended("qsb").withExecutableSuffix(); - if (path.exists()) - return path; - } - } - - qWarning() << "Shader baking failed, QSB not found."; - return {}; -} - void EffectMakerModel::updateQmlComponent() { // Clear possible QML runtime errors diff --git a/src/plugins/effectmakernew/effectmakermodel.h b/src/plugins/effectmakernew/effectmakermodel.h index 8f34dc8fe43..1cd68e70a72 100644 --- a/src/plugins/effectmakernew/effectmakermodel.h +++ b/src/plugins/effectmakernew/effectmakermodel.h @@ -12,6 +12,10 @@ #include #include +namespace ProjectExplorer { +class Target; +} + namespace Utils { class Process; } @@ -127,8 +131,7 @@ private: QString getCustomShaderVaryings(bool outState); QString generateVertexShader(bool includeUniforms = true); QString generateFragmentShader(bool includeUniforms = true); - - Utils::FilePath qsbPath() const; + void handleQsbProcessExit(Utils::Process *qsbProcess, const QString &shader); void updateCustomUniforms(); void bakeShaders(); @@ -142,6 +145,7 @@ private: bool m_isEmpty = true; // True when shaders haven't changed since last baking bool m_shadersUpToDate = true; + int m_remainingQsbTargets = 0; QMap m_effectErrors; ShaderFeatures m_shaderFeatures; QStringList m_shaderVaryingVariables; @@ -154,6 +158,10 @@ private: QTemporaryFile m_vertexSourceFile; QTemporaryFile m_fragmentShaderFile; QTemporaryFile m_vertexShaderFile; + QString m_fragmentSourceFilename; + QString m_vertexSourceFilename; + QString m_fragmentShaderFilename; + QString m_vertexShaderFilename; // Used in exported QML, at root of the file QString m_exportedRootPropertiesString; // Used in exported QML, at ShaderEffect component of the file From 3d8c740705a8edc29f99a6790d5d05cba6ef4206 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Fri, 13 Oct 2023 17:32:32 +0200 Subject: [PATCH 049/242] Nanotrace: Optimize the id counter We use the atomic only to get an offset and then use it as an normal counter. Change-Id: Ieef3c318107c26227efd2e493bf4a76b5744dff8 Reviewed-by: Tim Jenssen Reviewed-by: Reviewed-by: Qt CI Patch Build Bot --- src/libs/nanotrace/nanotracehr.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/libs/nanotrace/nanotracehr.h b/src/libs/nanotrace/nanotracehr.h index e2ec3823b9d..caa7f142d7e 100644 --- a/src/libs/nanotrace/nanotracehr.h +++ b/src/libs/nanotrace/nanotracehr.h @@ -331,6 +331,7 @@ public: { static_assert(std::is_same_v, "A active category is not possible with an inactive event queue!"); + idCounter = globalIdCounter += 1ULL << 32; } Token beginAsynchronous(ArgumentType traceName) @@ -381,7 +382,8 @@ public: private: StringType m_name; EnabledEventQueue &m_eventQueue; - inline static std::atomic idCounter = 0; + inline static std::atomic globalIdCounter; + std::size_t idCounter; }; template From 11bf705e26bec1db1f33304d2cdbd00559b91b9c Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Fri, 13 Oct 2023 22:06:05 +0200 Subject: [PATCH 050/242] QmlDesigner: Add move only trace token It is quite hard to define always an end to a trace. A move only token can do that. The drawback is that the token gets bigger but only compile them for an avtivated category. Change-Id: I88b47c5b47e4129b1cd402e89251b45fa126111c Reviewed-by: Reviewed-by: Tim Jenssen Reviewed-by: Qt CI Patch Build Bot --- src/libs/nanotrace/nanotracehr.cpp | 16 +- src/libs/nanotrace/nanotracehr.h | 170 +++++++--- .../imagecache/asynchronousimagecache.cpp | 92 +++-- .../imagecache/asynchronousimagefactory.cpp | 7 +- .../imagecache/imagecachecollector.cpp | 34 +- .../imagecache/imagecachecollector.h | 3 +- .../imagecache/imagecachecollectorinterface.h | 5 +- .../imagecache/imagecachedispatchcollector.h | 17 +- .../imagecache/imagecachefontcollector.cpp | 7 +- .../imagecache/imagecachefontcollector.h | 3 +- .../imagecache/imagecachegenerator.cpp | 59 +++- .../imagecache/imagecachegenerator.h | 14 +- .../imagecache/imagecachegeneratorinterface.h | 5 +- .../imagecache/meshimagecachecollector.cpp | 6 +- .../imagecache/meshimagecachecollector.h | 3 +- .../designercore/imagecache/taskqueue.h | 2 +- .../imagecache/textureimagecachecollector.cpp | 7 +- .../imagecache/textureimagecachecollector.h | 3 +- .../include/asynchronousimagecache.h | 46 +-- .../include/imagecacheauxiliarydata.h | 20 +- .../tests/mocks/imagecachecollectormock.h | 3 +- .../tests/mocks/mockimagecachegenerator.h | 5 +- .../asynchronousimagecache-test.cpp | 90 ++--- .../asynchronousimagefactory-test.cpp | 27 +- .../imagecachedispatchcollector-test.cpp | 44 +-- .../imagecache/imagecachegenerator-test.cpp | 315 +++++++++--------- 26 files changed, 588 insertions(+), 415 deletions(-) diff --git a/src/libs/nanotrace/nanotracehr.cpp b/src/libs/nanotrace/nanotracehr.cpp index 4837cfe9787..e8c9abd7747 100644 --- a/src/libs/nanotrace/nanotracehr.cpp +++ b/src/libs/nanotrace/nanotracehr.cpp @@ -24,14 +24,14 @@ void printEvent(std::ostream &out, const TraceEvent &event, qint64 processId, st { out << R"({"ph":")" << event.type << R"(","name":")" << event.name << R"(","cat":")" << event.category << R"(","ts":")" - << static_cast(event.time.time_since_epoch().count()) / 1000 << R"(","pid":")" - << processId << R"(","tid":")" << threadId << R"(")"; + << static_cast(event.time.time_since_epoch().count()) / 1000 << R"(","pid":)" + << processId << R"(,"tid":)" << threadId; if (event.type == 'X') out << R"(,"dur":)" << static_cast(event.duration.count()) / 1000; if (event.id != 0) - out << R"(,"id":)" << event.id; + out << R"(,"id":")" << event.id << R"(")"; if (event.arguments.size()) out << R"(,"args":)" << event.arguments; @@ -45,9 +45,9 @@ void writeMetaEvent(TraceFile *file, std::string_view key, std::string_vie auto &out = file->out; if (out.is_open()) { - file->out << R"({"name":")" << key << R"(","ph":"M", "pid":")" - << QCoreApplication::applicationPid() << R"(","tid":")" - << std::this_thread::get_id() << R"(","args":{"name":")" << value << R"("}})" + file->out << R"({"name":")" << key << R"(","ph":"M", "pid":)" + << QCoreApplication::applicationPid() << R"(,"tid":)" + << std::this_thread::get_id() << R"(,"args":{"name":")" << value << R"("}})" << ",\n"; } } @@ -194,6 +194,6 @@ void EventQueue::flush() } } -template class NANOTRACE_EXPORT EventQueue; -template class NANOTRACE_EXPORT EventQueue; +template class EventQueue; +template class EventQueue; } // namespace NanotraceHR diff --git a/src/libs/nanotrace/nanotracehr.h b/src/libs/nanotrace/nanotracehr.h index caa7f142d7e..d90f57b9e7f 100644 --- a/src/libs/nanotrace/nanotracehr.h +++ b/src/libs/nanotrace/nanotracehr.h @@ -58,6 +58,7 @@ constexpr TracerLiteral operator""_t(const char *text, size_t size) } // namespace Literals using namespace Literals; + template struct TraceEvent { @@ -142,6 +143,7 @@ public: TraceFile &operator=(TraceFile &&) = delete; ~TraceFile() { finalizeFile(*this); } + std::string filePath; std::mutex fileMutex; std::future processing; @@ -257,43 +259,108 @@ TraceEvent &getTraceEvent(EnabledEventQueue &eventQueue) return eventQueue.currentEvents[eventQueue.eventsIndex++]; } -template +template class Token { public: using IsActive = std::false_type; + using ArgumentType = typename Category::ArgumentType; - constexpr std::size_t operator*() const { return 0; } + Token() {} + + ~Token() {} + + constexpr Token(const Token &) = delete; + constexpr Token &operator=(const Token &) = delete; + + constexpr Token(Token &&other) noexcept = default; + + constexpr Token &operator=(Token &&other) noexcept = default; constexpr explicit operator bool() const { return false; } static constexpr bool isActive() { return false; } + + Token begin(ArgumentType) { return Token{}; } + + void tick(ArgumentType) {} + + void end() {} }; template class Category; -template<> -class Token +template +class Token { - friend Category; - friend Category; - - Token(std::size_t id) - : m_id{id} + Token(std::string_view name, std::size_t id, Category &category) + : m_name{name} + , m_id{id} + , m_category{&category} {} public: using IsActive = std::true_type; + using StringType = typename Category::StringType; + using ArgumentType = typename Category::ArgumentType; - constexpr std::size_t operator*() const { return m_id; } + friend Category; + + Token() = default; + + Token(const Token &) = delete; + Token &operator=(const Token &) = delete; + + Token(Token &&other) noexcept + : m_name{other.m_name} + , m_id{std::exchange(other.m_id, 0)} + , m_category{std::exchange(other.m_category, nullptr)} + {} + + Token &operator=(Token &&other) noexcept + { + if (&other != this) { + m_name = other.m_name; + m_id = std::exchange(other.m_id, 0); + m_category = std::exchange(other.m_category, nullptr); + } + + return *this; + } + + ~Token() { end(); } constexpr explicit operator bool() const { return m_id; } static constexpr bool isActive() { return true; } + Token begin(ArgumentType name) + { + if (m_id) + m_category->beginAsynchronous(m_id, name); + + return Token{m_name, m_id, *m_category}; + } + + void tick(ArgumentType name) + { + if (m_id) + m_category->tickAsynchronous(m_id, name); + } + + void end() + { + if (m_id) + m_category->endAsynchronous(m_id, m_name); + + m_id = 0; + } + private: - std::size_t m_id; + StringType m_name; + std::size_t m_id = 0; + Category *m_category = nullptr; }; template @@ -302,16 +369,15 @@ class Category public: using IsActive = std::false_type; using ArgumentType = typename TraceEvent::ArgumentType; + using TokenType = Token; Category(ArgumentType, EventQueue &) {} Category(ArgumentType, EventQueue &) {} - Token beginAsynchronous(ArgumentType) { return {}; } + TokenType beginAsynchronous(ArgumentType) { return {}; } - void tickAsynchronous(Token, ArgumentType) {} - - void endAsynchronous(Token, ArgumentType) {} + void tickAsynchronous(ArgumentType) {} static constexpr bool isActive() { return false; } }; @@ -323,6 +389,9 @@ public: using IsActive = std::true_type; using ArgumentType = typename TraceEvent::ArgumentType; using StringType = typename TraceEvent::StringType; + using TokenType = Token; + + friend TokenType; template Category(ArgumentType name, EventQueue &queue) @@ -334,43 +403,13 @@ public: idCounter = globalIdCounter += 1ULL << 32; } - Token beginAsynchronous(ArgumentType traceName) + TokenType beginAsynchronous(ArgumentType traceName) { - auto id = ++idCounter; - auto &traceEvent = getTraceEvent(m_eventQueue); - traceEvent.name = std::move(traceName); - traceEvent.category = m_name; - traceEvent.time = Clock::now(); - traceEvent.type = 'b'; - traceEvent.id = id; + std::size_t id = ++idCounter; - return id; - } + beginAsynchronous(id, traceName); - void tickAsynchronous(Token token, ArgumentType traceName) - { - if (!token) - return; - - auto &traceEvent = getTraceEvent(m_eventQueue); - traceEvent.name = std::move(traceName); - traceEvent.category = m_name; - traceEvent.time = Clock::now(); - traceEvent.type = 'n'; - traceEvent.id = *token; - } - - void endAsynchronous(Token token, ArgumentType traceName) - { - if (!token) - return; - - auto &traceEvent = getTraceEvent(m_eventQueue); - traceEvent.name = std::move(traceName); - traceEvent.category = m_name; - traceEvent.time = Clock::now(); - traceEvent.type = 'e'; - traceEvent.id = *token; + return {traceName, id, *this}; } EnabledEventQueue &eventQueue() const { return m_eventQueue; } @@ -379,6 +418,39 @@ public: static constexpr bool isActive() { return true; } +private: + void beginAsynchronous(std::size_t id, StringType traceName) + { + auto &traceEvent = getTraceEvent(m_eventQueue); + traceEvent.name = std::move(traceName); + traceEvent.category = m_name; + traceEvent.type = 'b'; + traceEvent.id = id; + traceEvent.time = Clock::now(); + } + + void tickAsynchronous(std::size_t id, StringType traceName) + { + auto time = Clock::now(); + auto &traceEvent = getTraceEvent(m_eventQueue); + traceEvent.name = std::move(traceName); + traceEvent.category = m_name; + traceEvent.time = time; + traceEvent.type = 'n'; + traceEvent.id = id; + } + + void endAsynchronous(std::size_t id, StringType traceName) + { + auto time = Clock::now(); + auto &traceEvent = getTraceEvent(m_eventQueue); + traceEvent.name = std::move(traceName); + traceEvent.category = m_name; + traceEvent.time = time; + traceEvent.type = 'e'; + traceEvent.id = id; + } + private: StringType m_name; EnabledEventQueue &m_eventQueue; diff --git a/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagecache.cpp b/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagecache.cpp index 4da56f18eae..19fba2e15c5 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagecache.cpp +++ b/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagecache.cpp @@ -15,8 +15,9 @@ namespace QmlDesigner { using namespace NanotraceHR::Literals; +namespace ImageCache { namespace { -using TraceFile = NanotraceHR::TraceFile; +using TraceFile = NanotraceHR::TraceFile; TraceFile traceFile{"qml_designer.json"}; @@ -24,14 +25,14 @@ thread_local auto eventQueueData = NanotraceHR::makeEventQueueData category{"image cache"_t, - eventQueue}; +thread_local Category category_{"image cache"_t, eventQueue}; } // namespace -NanotraceHR::StringViewCategory &imageCacheCategory() +Category &category() { - return category; + return category_; } +} // namespace ImageCache AsynchronousImageCache::AsynchronousImageCache(ImageCacheStorageInterface &storage, ImageCacheGeneratorInterface &generator, @@ -52,7 +53,7 @@ void AsynchronousImageCache::request(Utils::SmallStringView name, ImageCache::CaptureImageCallback captureCallback, ImageCache::AbortCallback abortCallback, ImageCache::AuxiliaryData auxiliaryData, - ImageCacheTraceToken traceToken, + ImageCache::TraceToken traceToken, ImageCacheStorageInterface &storage, ImageCacheGeneratorInterface &generator, TimeStampProviderInterface &timeStampProvider) @@ -62,11 +63,7 @@ void AsynchronousImageCache::request(Utils::SmallStringView name, const auto timeStamp = timeStampProvider.timeStamp(name); auto requestImageFromStorage = [&](RequestType requestType) { - imageCacheCategory().tickAsynchronous(traceToken, "start fetching image from storage"_t); - - QScopeGuard finally{[=] { - imageCacheCategory().tickAsynchronous(traceToken, "end fetching image from storage"_t); - }}; + auto storageTraceToken = traceToken.begin("fetching image from storage"_t); switch (requestType) { case RequestType::Image: return storage.fetchImage(id, timeStamp); @@ -80,22 +77,24 @@ void AsynchronousImageCache::request(Utils::SmallStringView name, return storage.fetchImage(id, timeStamp); }; + const auto entry = requestImageFromStorage(requestType); if (entry) { if (entry->isNull()) { + traceToken.tick("there was an null image in storage"_t); abortCallback(ImageCache::AbortReason::Failed); - imageCacheCategory().endAsynchronous(traceToken, - "abort image request because entry in database is null"_t); } else { captureCallback(*entry); - imageCacheCategory().endAsynchronous(traceToken, "image request delivered from storage"_t); } + traceToken.end(); } else { - auto callback = + auto imageGeneratedCallback = [captureCallback = std::move(captureCallback), - requestType, - traceToken](const QImage &image, const QImage &midSizeImage, const QImage &smallImage) { + requestType](const QImage &image, + const QImage &midSizeImage, + const QImage &smallImage, + ImageCache::TraceToken traceToken) { auto selectImage = [](RequestType requestType, const QImage &image, const QImage &midSizeImage, @@ -113,19 +112,27 @@ void AsynchronousImageCache::request(Utils::SmallStringView name, return image; }; + traceToken.end(); captureCallback(selectImage(requestType, image, midSizeImage, smallImage)); - imageCacheCategory().endAsynchronous(traceToken, - "image request delivered from generation"_t); }; - imageCacheCategory().tickAsynchronous(traceToken, "request image generation"_t); + auto imageGenerationAbortedCallback = + [abortCallback = std::move(abortCallback)](ImageCache::AbortReason reason, + ImageCache::TraceToken traceToken) { + traceToken.tick("image could not be created"_t); + traceToken.end(); + abortCallback(reason); + }; + + traceToken.tick("call the generator"_t); generator.generateImage(name, extraId, timeStamp, - std::move(callback), - std::move(abortCallback), - std::move(auxiliaryData)); + std::move(imageGeneratedCallback), + std::move(imageGenerationAbortedCallback), + std::move(auxiliaryData), + std::move(traceToken)); } } @@ -135,7 +142,7 @@ void AsynchronousImageCache::requestImage(Utils::SmallStringView name, Utils::SmallStringView extraId, ImageCache::AuxiliaryData auxiliaryData) { - auto traceToken = imageCacheCategory().beginAsynchronous( + auto traceToken = ImageCache::category().beginAsynchronous( "request image in asynchornous image cache"_t); m_taskQueue.addTask(std::move(name), std::move(extraId), @@ -143,7 +150,7 @@ void AsynchronousImageCache::requestImage(Utils::SmallStringView name, std::move(abortCallback), std::move(auxiliaryData), RequestType::Image, - traceToken); + std ::move(traceToken)); } void AsynchronousImageCache::requestMidSizeImage(Utils::SmallStringView name, @@ -152,7 +159,7 @@ void AsynchronousImageCache::requestMidSizeImage(Utils::SmallStringView name, Utils::SmallStringView extraId, ImageCache::AuxiliaryData auxiliaryData) { - auto traceToken = imageCacheCategory().beginAsynchronous( + auto traceToken = ImageCache::category().beginAsynchronous( "request mid size image in asynchornous image cache"_t); m_taskQueue.addTask(std::move(name), std::move(extraId), @@ -160,7 +167,7 @@ void AsynchronousImageCache::requestMidSizeImage(Utils::SmallStringView name, std::move(abortCallback), std::move(auxiliaryData), RequestType::MidSizeImage, - traceToken); + std ::move(traceToken)); } void AsynchronousImageCache::requestSmallImage(Utils::SmallStringView name, @@ -169,7 +176,7 @@ void AsynchronousImageCache::requestSmallImage(Utils::SmallStringView name, Utils::SmallStringView extraId, ImageCache::AuxiliaryData auxiliaryData) { - auto traceToken = imageCacheCategory().beginAsynchronous( + auto traceToken = ImageCache::category().beginAsynchronous( "request small size image in asynchornous image cache"_t); m_taskQueue.addTask(std::move(name), std::move(extraId), @@ -177,7 +184,7 @@ void AsynchronousImageCache::requestSmallImage(Utils::SmallStringView name, std::move(abortCallback), std::move(auxiliaryData), RequestType::SmallImage, - traceToken); + std ::move(traceToken)); } void AsynchronousImageCache::clean() @@ -186,4 +193,31 @@ void AsynchronousImageCache::clean() m_taskQueue.clean(); } +void AsynchronousImageCache::Dispatch::operator()(Entry &entry) +{ + using namespace NanotraceHR::Literals; + + request(entry.name, + entry.extraId, + entry.requestType, + std::move(entry.captureCallback), + std::move(entry.abortCallback), + std::move(entry.auxiliaryData), + std::move(entry.traceToken), + storage, + generator, + timeStampProvider); +} + +void AsynchronousImageCache::Clean::operator()(Entry &entry) +{ + using namespace NanotraceHR::Literals; + + entry.traceToken.tick("cleaning up in the cache"_t); + + entry.abortCallback(ImageCache::AbortReason::Abort); + + entry.traceToken.end(); +} + } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagefactory.cpp b/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagefactory.cpp index a711f1ad7d8..9ed3a34f7d6 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagefactory.cpp +++ b/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagefactory.cpp @@ -46,7 +46,10 @@ void AsynchronousImageFactory::request(Utils::SmallStringView name, if (currentModifiedTime < (storageModifiedTime + pause)) return; - auto capture = [&](const QImage &image, const QImage &midSizeImage, const QImage &smallImage) { + auto capture = [&](const QImage &image, + const QImage &midSizeImage, + const QImage &smallImage, + ImageCache::TraceToken) { storage.storeImage(id, currentModifiedTime, image, midSizeImage, smallImage); }; @@ -54,7 +57,7 @@ void AsynchronousImageFactory::request(Utils::SmallStringView name, extraId, std::move(auxiliaryData), std::move(capture), - ImageCache::AbortCallback{}); + ImageCache::InternalAbortCallback{}); } void AsynchronousImageFactory::clean() diff --git a/src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.cpp b/src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.cpp index 4530290fbe3..6b37940c072 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.cpp +++ b/src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.cpp @@ -73,8 +73,12 @@ void ImageCacheCollector::start(Utils::SmallStringView name, Utils::SmallStringView state, const ImageCache::AuxiliaryData &auxiliaryData, CaptureCallback captureCallback, - AbortCallback abortCallback) + AbortCallback abortCallback, + ImageCache::TraceToken traceToken) { + using namespace NanotraceHR::Literals; + auto collectorTraceToken = traceToken.begin("generate image in standard collector"_t); + RewriterView rewriterView{m_externalDependencies, RewriterView::Amend}; NodeInstanceView nodeInstanceView{m_connectionManager, m_externalDependencies}; nodeInstanceView.setCaptureImageMinimumAndMaximumSize(captureImageMinimumSize, @@ -101,7 +105,7 @@ void ImageCacheCollector::start(Utils::SmallStringView name, if (!rewriterView.errors().isEmpty() || (!rewriterView.rootModelNode().metaInfo().isGraphicalItem() && !is3DRoot)) { if (abortCallback) - abortCallback(ImageCache::AbortReason::Failed); + abortCallback(ImageCache::AbortReason::Failed, std::move(traceToken)); return; } @@ -117,21 +121,17 @@ void ImageCacheCollector::start(Utils::SmallStringView name, if (stateNode.isValid()) rewriterView.setCurrentStateNode(stateNode); - auto callback = [=, captureCallback = std::move(captureCallback)](const QImage &image) { - if (nullImageHandling == ImageCacheCollectorNullImageHandling::CaptureNullImage - || !image.isNull()) { - QImage midSizeImage = scaleImage(image, QSize{300, 300}); - QImage smallImage = scaleImage(midSizeImage, QSize{96, 96}); - captureCallback(image, midSizeImage, smallImage); - } - }; + QImage captureImage; + + auto callback = [&](const QImage &image) { captureImage = image; }; if (!m_target) return; nodeInstanceView.setTarget(m_target.data()); m_connectionManager.setCallback(std::move(callback)); - nodeInstanceView.setCrashCallback([=] { abortCallback(ImageCache::AbortReason::Failed); }); + bool isCrashed = false; + nodeInstanceView.setCrashCallback([&] { isCrashed = true; }); model->setNodeInstanceView(&nodeInstanceView); bool capturedDataArrived = m_connectionManager.waitForCapturedData(); @@ -142,8 +142,18 @@ void ImageCacheCollector::start(Utils::SmallStringView name, model->setNodeInstanceView({}); model->setRewriterView({}); + if (isCrashed) + abortCallback(ImageCache::AbortReason::Failed, std::move(traceToken)); + if (!capturedDataArrived && abortCallback) - abortCallback(ImageCache::AbortReason::Failed); + abortCallback(ImageCache::AbortReason::Failed, std::move(traceToken)); + + if (nullImageHandling == ImageCacheCollectorNullImageHandling::CaptureNullImage + || !captureImage.isNull()) { + QImage midSizeImage = scaleImage(captureImage, QSize{300, 300}); + QImage smallImage = scaleImage(midSizeImage, QSize{96, 96}); + captureCallback(captureImage, midSizeImage, smallImage, std::move(traceToken)); + } } ImageCacheCollectorInterface::ImageTuple ImageCacheCollector::createImage( diff --git a/src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.h b/src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.h index 87ebedba048..e5230ea2b23 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.h +++ b/src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.h @@ -41,7 +41,8 @@ public: Utils::SmallStringView state, const ImageCache::AuxiliaryData &auxiliaryData, CaptureCallback captureCallback, - AbortCallback abortCallback) override; + AbortCallback abortCallback, + ImageCache::TraceToken traceToken) override; ImageTuple createImage(Utils::SmallStringView filePath, Utils::SmallStringView state, diff --git a/src/plugins/qmldesigner/designercore/imagecache/imagecachecollectorinterface.h b/src/plugins/qmldesigner/designercore/imagecache/imagecachecollectorinterface.h index 057ab6f03e5..e6c70f31939 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/imagecachecollectorinterface.h +++ b/src/plugins/qmldesigner/designercore/imagecache/imagecachecollectorinterface.h @@ -15,14 +15,15 @@ class ImageCacheCollectorInterface { public: using CaptureCallback = ImageCache::CaptureImageWithScaledImagesCallback; - using AbortCallback = ImageCache::AbortCallback; + using AbortCallback = ImageCache::InternalAbortCallback; using ImageTuple = std::tuple; virtual void start(Utils::SmallStringView filePath, Utils::SmallStringView extraId, const ImageCache::AuxiliaryData &auxiliaryData, CaptureCallback captureCallback, - AbortCallback abortCallback) + AbortCallback abortCallback, + ImageCache::TraceToken traceToken = {}) = 0; virtual ImageTuple createImage(Utils::SmallStringView filePath, diff --git a/src/plugins/qmldesigner/designercore/imagecache/imagecachedispatchcollector.h b/src/plugins/qmldesigner/designercore/imagecache/imagecachedispatchcollector.h index 274cf72ad67..59cd77505ad 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/imagecachedispatchcollector.h +++ b/src/plugins/qmldesigner/designercore/imagecache/imagecachedispatchcollector.h @@ -12,13 +12,15 @@ class ImageCacheDispatchCollector final : public ImageCacheCollectorInterface { public: ImageCacheDispatchCollector(CollectorEntries collectors) - : m_collectors{std::move(collectors)} {}; + : m_collectors{std::move(collectors)} + {} void start(Utils::SmallStringView filePath, Utils::SmallStringView state, const ImageCache::AuxiliaryData &auxiliaryData, CaptureCallback captureCallback, - AbortCallback abortCallback) override + AbortCallback abortCallback, + ImageCache::TraceToken traceToken) override { std::apply( [&](const auto &...collectors) { @@ -27,6 +29,7 @@ public: auxiliaryData, std::move(captureCallback), std::move(abortCallback), + std::move(traceToken), collectors...); }, m_collectors); @@ -61,6 +64,7 @@ private: const ImageCache::AuxiliaryData &auxiliaryData, CaptureCallback captureCallback, AbortCallback abortCallback, + ImageCache::TraceToken traceToken, const Collector &collector, const Collectors &...collectors) { @@ -69,13 +73,15 @@ private: state, auxiliaryData, std::move(captureCallback), - std::move(abortCallback)); + std::move(abortCallback), + std::move(traceToken)); } else { dispatchStart(filePath, state, auxiliaryData, std::move(captureCallback), std::move(abortCallback), + std::move(traceToken), collectors...); } } @@ -84,10 +90,11 @@ private: Utils::SmallStringView, const ImageCache::AuxiliaryData &, CaptureCallback, - AbortCallback abortCallback) + AbortCallback abortCallback, + ImageCache::TraceToken traceToken) { qWarning() << "ImageCacheDispatchCollector: cannot handle file type."; - abortCallback(ImageCache::AbortReason::Failed); + abortCallback(ImageCache::AbortReason::Failed, std::move(traceToken)); } template diff --git a/src/plugins/qmldesigner/designercore/imagecache/imagecachefontcollector.cpp b/src/plugins/qmldesigner/designercore/imagecache/imagecachefontcollector.cpp index 2e0c4813ec8..5c10e2e2ce4 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/imagecachefontcollector.cpp +++ b/src/plugins/qmldesigner/designercore/imagecache/imagecachefontcollector.cpp @@ -94,7 +94,8 @@ void ImageCacheFontCollector::start(Utils::SmallStringView name, Utils::SmallStringView, const ImageCache::AuxiliaryData &auxiliaryDataValue, CaptureCallback captureCallback, - AbortCallback abortCallback) + AbortCallback abortCallback, + ImageCache::TraceToken traceToken) { QFont font; if (resolveFont(QString(name), font) >= 0) { @@ -107,12 +108,12 @@ void ImageCacheFontCollector::start(Utils::SmallStringView name, QImage image = createFontImage(text, textColor, font, size); if (!image.isNull()) { - captureCallback(std::move(image), {}, {}); + captureCallback(std::move(image), {}, {}, std::move(traceToken)); return; } } } - abortCallback(ImageCache::AbortReason::Failed); + abortCallback(ImageCache::AbortReason::Failed, std::move(traceToken)); } ImageCacheCollectorInterface::ImageTuple ImageCacheFontCollector::createImage( diff --git a/src/plugins/qmldesigner/designercore/imagecache/imagecachefontcollector.h b/src/plugins/qmldesigner/designercore/imagecache/imagecachefontcollector.h index ff7f624e223..104e48e9b2f 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/imagecachefontcollector.h +++ b/src/plugins/qmldesigner/designercore/imagecache/imagecachefontcollector.h @@ -18,7 +18,8 @@ public: Utils::SmallStringView extraId, const ImageCache::AuxiliaryData &auxiliaryData, CaptureCallback captureCallback, - AbortCallback abortCallback) override; + AbortCallback abortCallback, + ImageCache::TraceToken traceToken) override; ImageTuple createImage(Utils::SmallStringView filePath, Utils::SmallStringView extraId, diff --git a/src/plugins/qmldesigner/designercore/imagecache/imagecachegenerator.cpp b/src/plugins/qmldesigner/designercore/imagecache/imagecachegenerator.cpp index c61327ee087..93ddbd38015 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/imagecachegenerator.cpp +++ b/src/plugins/qmldesigner/designercore/imagecache/imagecachegenerator.cpp @@ -42,8 +42,9 @@ void ImageCacheGenerator::generateImage(Utils::SmallStringView name, Utils::SmallStringView extraId, Sqlite::TimeStamp timeStamp, ImageCache::CaptureImageWithScaledImagesCallback &&captureCallback, - ImageCache::AbortCallback &&abortCallback, - ImageCache::AuxiliaryData &&auxiliaryData) + ImageCache::InternalAbortCallback &&abortCallback, + ImageCache::AuxiliaryData &&auxiliaryData, + ImageCache::TraceToken traceToken) { { std::lock_guard lock{m_mutex}; @@ -64,7 +65,8 @@ void ImageCacheGenerator::generateImage(Utils::SmallStringView name, std::move(auxiliaryData), timeStamp, std::move(captureCallback), - std::move(abortCallback)); + std::move(abortCallback), + std::move(traceToken)); } } @@ -89,9 +91,14 @@ void callCallbacks(const Callbacks &callbacks, Argument &&...arguments) void ImageCacheGenerator::clean() { + using namespace NanotraceHR::Literals; + std::lock_guard lock{m_mutex}; - for (Task &task : m_tasks) - callCallbacks(task.abortCallbacks, ImageCache::AbortReason::Abort); + for (Task &task : m_tasks) { + task.traceToken.tick("cleaning up in the generator"_t); + task.traceToken.end(); + callCallbacks(task.abortCallbacks, ImageCache::AbortReason::Abort, std::move(task.traceToken)); + } m_tasks.clear(); } @@ -124,27 +131,43 @@ void ImageCacheGenerator::startGeneration() task.filePath, task.extraId, std::move(task.auxiliaryData), - [this, task](const QImage &image, const QImage &midSizeImage, const QImage &smallImage) { + [this, + abortCallbacks = task.abortCallbacks, + captureCallbacks = std::move(task.captureCallbacks), + filePath = task.filePath, + extraId = task.extraId, + timeStamp = task.timeStamp](const QImage &image, + const QImage &midSizeImage, + const QImage &smallImage, + ImageCache::TraceToken traceToken) { if (image.isNull() && midSizeImage.isNull() && smallImage.isNull()) - callCallbacks(task.abortCallbacks, ImageCache::AbortReason::Failed); + callCallbacks(abortCallbacks, + ImageCache::AbortReason::Failed, + std::move(traceToken)); else - callCallbacks(task.captureCallbacks, image, midSizeImage, smallImage); + callCallbacks(captureCallbacks, + image, + midSizeImage, + smallImage, + std::move(traceToken)); - m_storage.storeImage(createId(task.filePath, task.extraId), - task.timeStamp, + m_storage.storeImage(createId(filePath, extraId), + timeStamp, image, midSizeImage, smallImage); }, - [this, task](ImageCache::AbortReason abortReason) { - callCallbacks(task.abortCallbacks, abortReason); + [this, + abortCallbacks = task.abortCallbacks, + filePath = task.filePath, + extraId = task.extraId, + timeStamp = task.timeStamp](ImageCache::AbortReason abortReason, + ImageCache::TraceToken traceToken) { + callCallbacks(abortCallbacks, abortReason, std::move(traceToken)); if (abortReason != ImageCache::AbortReason::Abort) - m_storage.storeImage(createId(task.filePath, task.extraId), - task.timeStamp, - {}, - {}, - {}); - }); + m_storage.storeImage(createId(filePath, extraId), timeStamp, {}, {}, {}); + }, + std::move(task.traceToken)); std::lock_guard lock{m_mutex}; if (m_tasks.empty()) diff --git a/src/plugins/qmldesigner/designercore/imagecache/imagecachegenerator.h b/src/plugins/qmldesigner/designercore/imagecache/imagecachegenerator.h index cfac1e0ef05..e89e25bcb29 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/imagecachegenerator.h +++ b/src/plugins/qmldesigner/designercore/imagecache/imagecachegenerator.h @@ -34,8 +34,9 @@ public: Utils::SmallStringView extraId, Sqlite::TimeStamp timeStamp, ImageCache::CaptureImageWithScaledImagesCallback &&captureCallback, - ImageCache::AbortCallback &&abortCallback, - ImageCache::AuxiliaryData &&auxiliaryData) override; + ImageCache::InternalAbortCallback &&abortCallback, + ImageCache::AuxiliaryData &&auxiliaryData, + ImageCache::TraceToken traceToken = {}) override; void clean() override; void waitForFinished() override; @@ -44,26 +45,30 @@ private: struct Task { Task() = default; + Task(Utils::SmallStringView filePath, Utils::SmallStringView extraId, ImageCache::AuxiliaryData &&auxiliaryData, Sqlite::TimeStamp timeStamp, ImageCache::CaptureImageWithScaledImagesCallback &&captureCallback, - ImageCache::AbortCallback &&abortCallback) + ImageCache::InternalAbortCallback &&abortCallback, + ImageCache::TraceToken traceToken) : filePath(filePath) , extraId(std::move(extraId)) , auxiliaryData(std::move(auxiliaryData)) , captureCallbacks({std::move(captureCallback)}) , abortCallbacks({std::move(abortCallback)}) , timeStamp(timeStamp) + , traceToken{std::move(traceToken)} {} Utils::PathString filePath; Utils::SmallString extraId; ImageCache::AuxiliaryData auxiliaryData; std::vector captureCallbacks; - std::vector abortCallbacks; + std::vector abortCallbacks; Sqlite::TimeStamp timeStamp; + ImageCache::TraceToken traceToken; }; void startGeneration(); @@ -71,7 +76,6 @@ private: [[nodiscard]] std::tuple, bool> waitForEntries(); void stopThread(); -private: private: std::unique_ptr m_backgroundThread; mutable std::mutex m_mutex; diff --git a/src/plugins/qmldesigner/designercore/imagecache/imagecachegeneratorinterface.h b/src/plugins/qmldesigner/designercore/imagecache/imagecachegeneratorinterface.h index d18f0def323..3708a204d63 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/imagecachegeneratorinterface.h +++ b/src/plugins/qmldesigner/designercore/imagecache/imagecachegeneratorinterface.h @@ -18,8 +18,9 @@ public: Utils::SmallStringView extraId, Sqlite::TimeStamp timeStamp, ImageCache::CaptureImageWithScaledImagesCallback &&captureCallback, - ImageCache::AbortCallback &&abortCallback, - ImageCache::AuxiliaryData &&auxiliaryData) + ImageCache::InternalAbortCallback &&abortCallback, + ImageCache::AuxiliaryData &&auxiliaryData, + ImageCache::TraceToken = {}) = 0; virtual void clean() = 0; diff --git a/src/plugins/qmldesigner/designercore/imagecache/meshimagecachecollector.cpp b/src/plugins/qmldesigner/designercore/imagecache/meshimagecachecollector.cpp index 2063de8f3aa..726c049555d 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/meshimagecachecollector.cpp +++ b/src/plugins/qmldesigner/designercore/imagecache/meshimagecachecollector.cpp @@ -30,7 +30,8 @@ void MeshImageCacheCollector::start(Utils::SmallStringView name, Utils::SmallStringView state, const ImageCache::AuxiliaryData &auxiliaryData, CaptureCallback captureCallback, - AbortCallback abortCallback) + AbortCallback abortCallback, + ImageCache::TraceToken traceToken) { QTemporaryFile file(QDir::tempPath() + "/mesh-XXXXXX.qml"); if (file.open()) { @@ -63,7 +64,8 @@ void MeshImageCacheCollector::start(Utils::SmallStringView name, Utils::PathString path{file.fileName()}; - m_imageCacheCollector.start(path, state, auxiliaryData, captureCallback, abortCallback); + m_imageCacheCollector + .start(path, state, auxiliaryData, captureCallback, abortCallback, std::move(traceToken)); } ImageCacheCollectorInterface::ImageTuple MeshImageCacheCollector::createImage( diff --git a/src/plugins/qmldesigner/designercore/imagecache/meshimagecachecollector.h b/src/plugins/qmldesigner/designercore/imagecache/meshimagecachecollector.h index 17b1a412bc1..067c9033608 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/meshimagecachecollector.h +++ b/src/plugins/qmldesigner/designercore/imagecache/meshimagecachecollector.h @@ -29,7 +29,8 @@ public: Utils::SmallStringView state, const ImageCache::AuxiliaryData &auxiliaryData, CaptureCallback captureCallback, - AbortCallback abortCallback) override; + AbortCallback abortCallback, + ImageCache::TraceToken traceToken = {}) override; ImageTuple createImage(Utils::SmallStringView filePath, Utils::SmallStringView state, diff --git a/src/plugins/qmldesigner/designercore/imagecache/taskqueue.h b/src/plugins/qmldesigner/designercore/imagecache/taskqueue.h index 67b0904eed0..36c2d6bb7a5 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/taskqueue.h +++ b/src/plugins/qmldesigner/designercore/imagecache/taskqueue.h @@ -83,7 +83,7 @@ private: Task task = std::move(m_tasks.front()); m_tasks.pop_front(); - return {task}; + return {std::move(task)}; } void ensureThreadIsRunning() diff --git a/src/plugins/qmldesigner/designercore/imagecache/textureimagecachecollector.cpp b/src/plugins/qmldesigner/designercore/imagecache/textureimagecachecollector.cpp index af310ae6e7d..9ec83c0e5b3 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/textureimagecachecollector.cpp +++ b/src/plugins/qmldesigner/designercore/imagecache/textureimagecachecollector.cpp @@ -17,7 +17,8 @@ void TextureImageCacheCollector::start(Utils::SmallStringView name, Utils::SmallStringView, const ImageCache::AuxiliaryData &, CaptureCallback captureCallback, - AbortCallback abortCallback) + AbortCallback abortCallback, + ImageCache::TraceToken traceToken) { Asset asset {QString(name)}; QImage image; @@ -31,11 +32,11 @@ void TextureImageCacheCollector::start(Utils::SmallStringView name, } if (image.isNull()) - abortCallback(ImageCache::AbortReason::Failed); + abortCallback(ImageCache::AbortReason::Failed, std::move(traceToken)); else image = image.scaled(QSize{300, 300}, Qt::KeepAspectRatio); - captureCallback({}, image, {}); + captureCallback({}, image, {}, std::move(traceToken)); } ImageCacheCollectorInterface::ImageTuple TextureImageCacheCollector::createImage( diff --git a/src/plugins/qmldesigner/designercore/imagecache/textureimagecachecollector.h b/src/plugins/qmldesigner/designercore/imagecache/textureimagecachecollector.h index 67876d7641a..fceb06100bc 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/textureimagecachecollector.h +++ b/src/plugins/qmldesigner/designercore/imagecache/textureimagecachecollector.h @@ -17,7 +17,8 @@ public: Utils::SmallStringView state, const ImageCache::AuxiliaryData &auxiliaryData, CaptureCallback captureCallback, - AbortCallback abortCallback) override; + AbortCallback abortCallback, + ImageCache::TraceToken traceToken = {}) override; ImageTuple createImage(Utils::SmallStringView filePath, Utils::SmallStringView state, diff --git a/src/plugins/qmldesigner/designercore/include/asynchronousimagecache.h b/src/plugins/qmldesigner/designercore/include/asynchronousimagecache.h index fc03cccce8e..b1948f566bd 100644 --- a/src/plugins/qmldesigner/designercore/include/asynchronousimagecache.h +++ b/src/plugins/qmldesigner/designercore/include/asynchronousimagecache.h @@ -3,10 +3,11 @@ #pragma once +#include + #include "asynchronousimagecacheinterface.h" #include -#include #include #include @@ -22,19 +23,6 @@ class ImageCacheStorageInterface; class ImageCacheGeneratorInterface; class ImageCacheCollectorInterface; -constexpr bool imageCacheTracingIsEnabled() -{ -#ifdef ENABLE_IMAGE_CACHE_TRACING - return NanotraceHR::isTracerActive(); -#else - return false; -#endif -} - -using ImageCacheTraceToken = NanotraceHR::Token; - -NanotraceHR::StringViewCategory &imageCacheCategory(); - class AsynchronousImageCache final : public AsynchronousImageCacheInterface { public: @@ -74,14 +62,14 @@ private: ImageCache::AbortCallback &&abortCallback, ImageCache::AuxiliaryData &&auxiliaryData, RequestType requestType, - ImageCacheTraceToken traceToken) + ImageCache::TraceToken traceToken) : name{std::move(name)} , extraId{std::move(extraId)} , captureCallback{std::move(captureCallback)} , abortCallback{std::move(abortCallback)} , auxiliaryData{std::move(auxiliaryData)} , requestType{requestType} - , traceToken{traceToken} + , traceToken{std::move(traceToken)} {} Utils::PathString name; @@ -90,7 +78,7 @@ private: ImageCache::AbortCallback abortCallback; ImageCache::AuxiliaryData auxiliaryData; RequestType requestType = RequestType::Image; - ImageCacheTraceToken traceToken; + ImageCache::TraceToken traceToken; }; static void request(Utils::SmallStringView name, @@ -99,26 +87,14 @@ private: ImageCache::CaptureImageCallback captureCallback, ImageCache::AbortCallback abortCallback, ImageCache::AuxiliaryData auxiliaryData, - ImageCacheTraceToken traceToken, + ImageCache::TraceToken traceToken, ImageCacheStorageInterface &storage, ImageCacheGeneratorInterface &generator, TimeStampProviderInterface &timeStampProvider); struct Dispatch { - void operator()(Entry &entry) - { - request(entry.name, - entry.extraId, - entry.requestType, - std::move(entry.captureCallback), - std::move(entry.abortCallback), - std::move(entry.auxiliaryData), - entry.traceToken, - storage, - generator, - timeStampProvider); - } + QMLDESIGNERCORE_EXPORT void operator()(Entry &entry); ImageCacheStorageInterface &storage; ImageCacheGeneratorInterface &generator; @@ -127,13 +103,7 @@ private: struct Clean { - void operator()(Entry &entry) - { - using namespace NanotraceHR::Literals; - - entry.abortCallback(ImageCache::AbortReason::Abort); - imageCacheCategory().endAsynchronous(entry.traceToken, "aborted for cleanup"_t); - } + QMLDESIGNERCORE_EXPORT void operator()(Entry &entry); }; private: diff --git a/src/plugins/qmldesigner/designercore/include/imagecacheauxiliarydata.h b/src/plugins/qmldesigner/designercore/include/imagecacheauxiliarydata.h index c147fb0753a..b27816f0548 100644 --- a/src/plugins/qmldesigner/designercore/include/imagecacheauxiliarydata.h +++ b/src/plugins/qmldesigner/designercore/include/imagecacheauxiliarydata.h @@ -3,6 +3,7 @@ #pragma once +#include #include #include @@ -16,6 +17,19 @@ namespace QmlDesigner { namespace ImageCache { +constexpr bool tracingIsEnabled() +{ +#ifdef ENABLE_IMAGE_CACHE_TRACING + return NanotraceHR::isTracerActive(); +#else + return false; +#endif +} + +using Category = NanotraceHR::StringViewCategory; +using TraceToken = Category::TokenType; +Category &category(); + class FontCollectorSizeAuxiliaryData { public: @@ -46,9 +60,11 @@ using AuxiliaryData = std::variant; -using CaptureImageWithScaledImagesCallback = std::function< - void(const QImage &image, const QImage &midSizeImage, const QImage &smallImage)>; +using CaptureImageWithScaledImagesCallback = std::function; using AbortCallback = std::function; +using InternalAbortCallback = std::function; + } // namespace ImageCache } // namespace QmlDesigner diff --git a/tests/unit/tests/mocks/imagecachecollectormock.h b/tests/unit/tests/mocks/imagecachecollectormock.h index d8a8608faa6..0947df3f4d1 100644 --- a/tests/unit/tests/mocks/imagecachecollectormock.h +++ b/tests/unit/tests/mocks/imagecachecollectormock.h @@ -16,7 +16,8 @@ public: Utils::SmallStringView state, const QmlDesigner::ImageCache::AuxiliaryData &auxiliaryData, ImageCacheCollectorInterface::CaptureCallback captureCallback, - ImageCacheCollectorInterface::AbortCallback abortCallback), + ImageCacheCollectorInterface::AbortCallback abortCallback, + QmlDesigner::ImageCache::TraceToken), (override)); MOCK_METHOD(ImageTuple, diff --git a/tests/unit/tests/mocks/mockimagecachegenerator.h b/tests/unit/tests/mocks/mockimagecachegenerator.h index 30d322ac7f2..ee673c8ed4c 100644 --- a/tests/unit/tests/mocks/mockimagecachegenerator.h +++ b/tests/unit/tests/mocks/mockimagecachegenerator.h @@ -16,8 +16,9 @@ public: Utils::SmallStringView state, Sqlite::TimeStamp timeStamp, QmlDesigner::ImageCache::CaptureImageWithScaledImagesCallback &&captureCallback, - QmlDesigner::ImageCache::AbortCallback &&abortCallback, - QmlDesigner::ImageCache::AuxiliaryData &&auxiliaryData), + QmlDesigner::ImageCache::InternalAbortCallback &&abortCallback, + QmlDesigner::ImageCache::AuxiliaryData &&auxiliaryData, + QmlDesigner::ImageCache::TraceToken), (override)); MOCK_METHOD(void, clean, (), (override)); MOCK_METHOD(void, waitForFinished, (), (override)); diff --git a/tests/unit/tests/unittests/imagecache/asynchronousimagecache-test.cpp b/tests/unit/tests/unittests/imagecache/asynchronousimagecache-test.cpp index e7c51c40fc4..7dd283579d5 100644 --- a/tests/unit/tests/unittests/imagecache/asynchronousimagecache-test.cpp +++ b/tests/unit/tests/unittests/imagecache/asynchronousimagecache-test.cpp @@ -93,8 +93,8 @@ TEST_F(AsynchronousImageCache, request_image_request_image_from_generator) .WillByDefault(Return(Sqlite::TimeStamp{123})); EXPECT_CALL(mockGenerator, - generateImage(Eq("/path/to/Component.qml"), _, Eq(Sqlite::TimeStamp{123}), _, _, _)) - .WillRepeatedly([&](auto, auto, auto, auto, auto, auto) { notification.notify(); }); + generateImage(Eq("/path/to/Component.qml"), _, Eq(Sqlite::TimeStamp{123}), _, _, _, _)) + .WillRepeatedly([&](auto, auto, auto, auto, auto, auto, auto) { notification.notify(); }); cache.requestImage("/path/to/Component.qml", mockCaptureCallback.AsStdFunction(), @@ -104,9 +104,9 @@ TEST_F(AsynchronousImageCache, request_image_request_image_from_generator) TEST_F(AsynchronousImageCache, request_image_calls_capture_callback_with_image_from_generator) { - ON_CALL(mockGenerator, generateImage(Eq("/path/to/Component.qml"), _, _, _, _, _)) - .WillByDefault([&](auto, auto, auto, auto &&callback, auto, auto) { - callback(QImage{image1}, QImage{midSizeImage1}, QImage{smallImage1}); + ON_CALL(mockGenerator, generateImage(Eq("/path/to/Component.qml"), _, _, _, _, _, _)) + .WillByDefault([&](auto, auto, auto, auto &&callback, auto, auto, auto) { + callback(QImage{image1}, QImage{midSizeImage1}, QImage{smallImage1}, {}); notification.notify(); }); @@ -120,9 +120,9 @@ TEST_F(AsynchronousImageCache, request_image_calls_capture_callback_with_image_f TEST_F(AsynchronousImageCache, request_image_calls_abort_callback_from_generator) { - ON_CALL(mockGenerator, generateImage(Eq("/path/to/Component.qml"), _, _, _, _, _)) - .WillByDefault([&](auto, auto, auto, auto &&, auto &&abortCallback, auto) { - abortCallback(QmlDesigner::ImageCache::AbortReason::Failed); + ON_CALL(mockGenerator, generateImage(Eq("/path/to/Component.qml"), _, _, _, _, _, _)) + .WillByDefault([&](auto, auto, auto, auto &&, auto &&abortCallback, auto, auto) { + abortCallback(QmlDesigner::ImageCache::AbortReason::Failed, {}); notification.notify(); }); @@ -200,8 +200,8 @@ TEST_F(AsynchronousImageCache, request_mid_size_image_request_image_from_generat .WillByDefault(Return(Sqlite::TimeStamp{123})); EXPECT_CALL(mockGenerator, - generateImage(Eq("/path/to/Component.qml"), _, Eq(Sqlite::TimeStamp{123}), _, _, _)) - .WillRepeatedly([&](auto, auto, auto, auto, auto, auto) { notification.notify(); }); + generateImage(Eq("/path/to/Component.qml"), _, Eq(Sqlite::TimeStamp{123}), _, _, _, _)) + .WillRepeatedly([&](auto, auto, auto, auto, auto, auto, auto) { notification.notify(); }); cache.requestMidSizeImage("/path/to/Component.qml", mockCaptureCallback.AsStdFunction(), @@ -211,9 +211,9 @@ TEST_F(AsynchronousImageCache, request_mid_size_image_request_image_from_generat TEST_F(AsynchronousImageCache, request_mid_size_image_calls_capture_callback_with_image_from_generator) { - ON_CALL(mockGenerator, generateImage(Eq("/path/to/Component.qml"), _, _, _, _, _)) - .WillByDefault([&](auto, auto, auto, auto &&callback, auto, auto) { - callback(QImage{image1}, QImage{midSizeImage1}, QImage{smallImage1}); + ON_CALL(mockGenerator, generateImage(Eq("/path/to/Component.qml"), _, _, _, _, _, _)) + .WillByDefault([&](auto, auto, auto, auto &&callback, auto, auto, auto) { + callback(QImage{image1}, QImage{midSizeImage1}, QImage{smallImage1}, {}); notification.notify(); }); @@ -227,9 +227,9 @@ TEST_F(AsynchronousImageCache, request_mid_size_image_calls_capture_callback_wit TEST_F(AsynchronousImageCache, request_mid_size_image_calls_abort_callback_from_generator) { - ON_CALL(mockGenerator, generateImage(Eq("/path/to/Component.qml"), _, _, _, _, _)) - .WillByDefault([&](auto, auto, auto, auto &&, auto &&abortCallback, auto) { - abortCallback(QmlDesigner::ImageCache::AbortReason::Failed); + ON_CALL(mockGenerator, generateImage(Eq("/path/to/Component.qml"), _, _, _, _, _, _)) + .WillByDefault([&](auto, auto, auto, auto &&, auto &&abortCallback, auto, auto) { + abortCallback(QmlDesigner::ImageCache::AbortReason::Failed, {}); notification.notify(); }); @@ -306,8 +306,8 @@ TEST_F(AsynchronousImageCache, request_small_image_request_image_from_generator) .WillByDefault(Return(Sqlite::TimeStamp{123})); EXPECT_CALL(mockGenerator, - generateImage(Eq("/path/to/Component.qml"), _, Eq(Sqlite::TimeStamp{123}), _, _, _)) - .WillRepeatedly([&](auto, auto, auto, auto, auto, auto) { notification.notify(); }); + generateImage(Eq("/path/to/Component.qml"), _, Eq(Sqlite::TimeStamp{123}), _, _, _, _)) + .WillRepeatedly([&](auto, auto, auto, auto, auto, auto, auto) { notification.notify(); }); cache.requestSmallImage("/path/to/Component.qml", mockCaptureCallback.AsStdFunction(), @@ -317,9 +317,9 @@ TEST_F(AsynchronousImageCache, request_small_image_request_image_from_generator) TEST_F(AsynchronousImageCache, request_small_image_calls_capture_callback_with_image_from_generator) { - ON_CALL(mockGenerator, generateImage(Eq("/path/to/Component.qml"), _, _, _, _, _)) - .WillByDefault([&](auto, auto, auto, auto &&callback, auto, auto) { - callback(QImage{image1}, QImage{midSizeImage1}, QImage{smallImage1}); + ON_CALL(mockGenerator, generateImage(Eq("/path/to/Component.qml"), _, _, _, _, _, _)) + .WillByDefault([&](auto, auto, auto, auto &&callback, auto, auto, auto) { + callback(QImage{image1}, QImage{midSizeImage1}, QImage{smallImage1}, {}); notification.notify(); }); @@ -333,9 +333,9 @@ TEST_F(AsynchronousImageCache, request_small_image_calls_capture_callback_with_i TEST_F(AsynchronousImageCache, request_small_image_calls_abort_callback_from_generator) { - ON_CALL(mockGenerator, generateImage(Eq("/path/to/Component.qml"), _, _, _, _, _)) - .WillByDefault([&](auto, auto, auto, auto &&, auto &&abortCallback, auto) { - abortCallback(QmlDesigner::ImageCache::AbortReason::Failed); + ON_CALL(mockGenerator, generateImage(Eq("/path/to/Component.qml"), _, _, _, _, _, _)) + .WillByDefault([&](auto, auto, auto, auto &&, auto &&abortCallback, auto, auto) { + abortCallback(QmlDesigner::ImageCache::AbortReason::Failed, {}); notification.notify(); }); @@ -349,9 +349,9 @@ TEST_F(AsynchronousImageCache, request_small_image_calls_abort_callback_from_gen TEST_F(AsynchronousImageCache, clean_removes_entries) { - EXPECT_CALL(mockGenerator, generateImage(_, _, _, _, _, _)) - .WillRepeatedly([&](auto, auto, auto, auto &&captureCallback, auto &&, auto) { - captureCallback(QImage{}, QImage{}, QImage{}); + EXPECT_CALL(mockGenerator, generateImage(_, _, _, _, _, _, _)) + .WillRepeatedly([&](auto, auto, auto, auto &&captureCallback, auto &&, auto, auto) { + captureCallback(QImage{}, QImage{}, QImage{}, {}); waitInThread.wait(); }); cache.requestSmallImage("/path/to/Component1.qml", @@ -369,8 +369,8 @@ TEST_F(AsynchronousImageCache, clean_removes_entries) TEST_F(AsynchronousImageCache, clean_calls_abort) { - ON_CALL(mockGenerator, generateImage(_, _, _, _, _, _)) - .WillByDefault([&](auto, auto, auto, auto, auto &&, auto) { waitInThread.wait(); }); + ON_CALL(mockGenerator, generateImage(_, _, _, _, _, _, _)) + .WillByDefault([&](auto, auto, auto, auto, auto &&, auto, auto) { waitInThread.wait(); }); cache.requestSmallImage("/path/to/Component1.qml", mockCaptureCallback.AsStdFunction(), mockAbortCallback.AsStdFunction()); @@ -399,8 +399,9 @@ TEST_F(AsynchronousImageCache, after_clean_new_jobs_works) { cache.clean(); - EXPECT_CALL(mockGenerator, generateImage(Eq("/path/to/Component.qml"), _, _, _, _, _)) - .WillRepeatedly([&](auto, auto, auto, auto &&, auto &&, auto) { notification.notify(); }); + EXPECT_CALL(mockGenerator, generateImage(Eq("/path/to/Component.qml"), _, _, _, _, _, _)) + .WillRepeatedly( + [&](auto, auto, auto, auto &&, auto &&, auto, auto) { notification.notify(); }); cache.requestSmallImage("/path/to/Component.qml", mockCaptureCallback.AsStdFunction(), @@ -460,8 +461,8 @@ TEST_F(AsynchronousImageCache, request_image_with_extra_id_request_image_from_ge EXPECT_CALL(mockGenerator, generateImage( - Eq("/path/to/Component.qml"), Eq("extraId1"), Eq(Sqlite::TimeStamp{123}), _, _, _)) - .WillRepeatedly([&](auto, auto, auto, auto &&, auto, auto) { notification.notify(); }); + Eq("/path/to/Component.qml"), Eq("extraId1"), Eq(Sqlite::TimeStamp{123}), _, _, _, _)) + .WillRepeatedly([&](auto, auto, auto, auto &&, auto, auto, auto) { notification.notify(); }); cache.requestImage("/path/to/Component.qml", mockCaptureCallback.AsStdFunction(), @@ -477,8 +478,8 @@ TEST_F(AsynchronousImageCache, request_mid_size_image_with_extra_id_request_imag EXPECT_CALL(mockGenerator, generateImage( - Eq("/path/to/Component.qml"), Eq("extraId1"), Eq(Sqlite::TimeStamp{123}), _, _, _)) - .WillRepeatedly([&](auto, auto, auto, auto &&, auto, auto) { notification.notify(); }); + Eq("/path/to/Component.qml"), Eq("extraId1"), Eq(Sqlite::TimeStamp{123}), _, _, _, _)) + .WillRepeatedly([&](auto, auto, auto, auto &&, auto, auto, auto) { notification.notify(); }); cache.requestMidSizeImage("/path/to/Component.qml", mockCaptureCallback.AsStdFunction(), @@ -494,8 +495,8 @@ TEST_F(AsynchronousImageCache, request_small_image_with_extra_id_request_image_f EXPECT_CALL(mockGenerator, generateImage( - Eq("/path/to/Component.qml"), Eq("extraId1"), Eq(Sqlite::TimeStamp{123}), _, _, _)) - .WillRepeatedly([&](auto, auto, auto, auto &&, auto, auto) { notification.notify(); }); + Eq("/path/to/Component.qml"), Eq("extraId1"), Eq(Sqlite::TimeStamp{123}), _, _, _, _)) + .WillRepeatedly([&](auto, auto, auto, auto &&, auto, auto, auto) { notification.notify(); }); cache.requestSmallImage("/path/to/Component.qml", mockCaptureCallback.AsStdFunction(), @@ -521,8 +522,9 @@ TEST_F(AsynchronousImageCache, request_image_with_auxiliary_data_request_image_f AllOf(Field(&FontCollectorSizesAuxiliaryData::sizes, ElementsAre(QSize{20, 11})), Field(&FontCollectorSizesAuxiliaryData::colorName, - Eq(u"color")))))) - .WillRepeatedly([&](auto, auto, auto, auto &&, auto, auto) { notification.notify(); }); + Eq(u"color")))), + _)) + .WillRepeatedly([&](auto, auto, auto, auto &&, auto, auto, auto) { notification.notify(); }); cache.requestImage("/path/to/Component.qml", mockCaptureCallback.AsStdFunction(), @@ -549,8 +551,9 @@ TEST_F(AsynchronousImageCache, request_mid_size_image_with_auxiliary_data_reques AllOf(Field(&FontCollectorSizesAuxiliaryData::sizes, ElementsAre(QSize{20, 11})), Field(&FontCollectorSizesAuxiliaryData::colorName, - Eq(u"color")))))) - .WillRepeatedly([&](auto, auto, auto, auto &&, auto, auto) { notification.notify(); }); + Eq(u"color")))), + _)) + .WillRepeatedly([&](auto, auto, auto, auto &&, auto, auto, auto) { notification.notify(); }); cache.requestMidSizeImage("/path/to/Component.qml", mockCaptureCallback.AsStdFunction(), @@ -577,8 +580,9 @@ TEST_F(AsynchronousImageCache, request_small_image_with_auxiliary_data_request_i AllOf(Field(&FontCollectorSizesAuxiliaryData::sizes, ElementsAre(QSize{20, 11})), Field(&FontCollectorSizesAuxiliaryData::colorName, - Eq(u"color")))))) - .WillRepeatedly([&](auto, auto, auto, auto &&, auto, auto) { notification.notify(); }); + Eq(u"color")))), + _)) + .WillRepeatedly([&](auto, auto, auto, auto &&, auto, auto, auto) { notification.notify(); }); cache.requestSmallImage("/path/to/Component.qml", mockCaptureCallback.AsStdFunction(), diff --git a/tests/unit/tests/unittests/imagecache/asynchronousimagefactory-test.cpp b/tests/unit/tests/unittests/imagecache/asynchronousimagefactory-test.cpp index b26b4b7c708..966d18f95e6 100644 --- a/tests/unit/tests/unittests/imagecache/asynchronousimagefactory-test.cpp +++ b/tests/unit/tests/unittests/imagecache/asynchronousimagefactory-test.cpp @@ -43,8 +43,9 @@ TEST_F(AsynchronousImageFactory, request_image_request_image_from_collector) IsEmpty(), VariantWith(std::monostate{}), _, + _, _)) - .WillRepeatedly([&](auto, auto, auto, auto, auto) { notification.notify(); }); + .WillRepeatedly([&](auto, auto, auto, auto, auto, auto) { notification.notify(); }); factory.generate("/path/to/Component.qml"); notification.wait(); @@ -57,8 +58,9 @@ TEST_F(AsynchronousImageFactory, request_image_with_extra_id_request_image_from_ Eq("foo"), VariantWith(std::monostate{}), _, + _, _)) - .WillRepeatedly([&](auto, auto, auto, auto, auto) { notification.notify(); }); + .WillRepeatedly([&](auto, auto, auto, auto, auto, auto) { notification.notify(); }); factory.generate("/path/to/Component.qml", "foo"); notification.wait(); @@ -77,8 +79,9 @@ TEST_F(AsynchronousImageFactory, request_image_with_auxiliary_data_request_image Field(&FontCollectorSizesAuxiliaryData::colorName, Eq(u"color")), Field(&FontCollectorSizesAuxiliaryData::text, Eq(u"some text")))), _, + _, _)) - .WillRepeatedly([&](auto, auto, auto, auto, auto) { notification.notify(); }); + .WillRepeatedly([&](auto, auto, auto, auto, auto, auto) { notification.notify(); }); factory.generate("/path/to/Component.qml", "foo", @@ -99,7 +102,7 @@ TEST_F(AsynchronousImageFactory, dont_request_image_request_image_from_collector ON_CALL(timeStampProviderMock, timeStamp(Eq("/path/to/Component.qml"))) .WillByDefault(Return(Sqlite::TimeStamp{1})); - EXPECT_CALL(collectorMock, start(_, _, _, _, _)).Times(0); + EXPECT_CALL(collectorMock, start(_, _, _, _, _, _)).Times(0); factory.generate("/path/to/Component.qml"); notification.wait(); @@ -113,7 +116,7 @@ TEST_F(AsynchronousImageFactory, request_image_request_image_from_collector_if_f .WillByDefault(Return(Sqlite::TimeStamp{125})); ON_CALL(timeStampProviderMock, pause()).WillByDefault(Return(Sqlite::TimeStamp{1})); - EXPECT_CALL(collectorMock, start(_, _, _, _, _)).WillOnce([&](auto, auto, auto, auto, auto) { + EXPECT_CALL(collectorMock, start(_, _, _, _, _, _)).WillOnce([&](auto, auto, auto, auto, auto, auto) { notification.notify(); }); @@ -123,15 +126,15 @@ TEST_F(AsynchronousImageFactory, request_image_request_image_from_collector_if_f TEST_F(AsynchronousImageFactory, clean_removes_entries) { - EXPECT_CALL(collectorMock, start(Eq("/path/to/Component1.qml"), _, _, _, _)) - .WillRepeatedly([&](auto, auto, auto, auto, auto) { + EXPECT_CALL(collectorMock, start(Eq("/path/to/Component1.qml"), _, _, _, _, _)) + .WillRepeatedly([&](auto, auto, auto, auto, auto, auto) { notification.notify(); waitInThread.wait(); }); factory.generate("/path/to/Component1.qml"); notification.wait(); - EXPECT_CALL(collectorMock, start(Eq("/path/to/Component3.qml"), _, _, _, _)).Times(0); + EXPECT_CALL(collectorMock, start(Eq("/path/to/Component3.qml"), _, _, _, _, _)).Times(0); factory.generate("/path/to/Component3.qml"); factory.clean(); @@ -147,8 +150,9 @@ TEST_F(AsynchronousImageFactory, after_clean_new_jobs_works) IsEmpty(), VariantWith(std::monostate{}), _, + _, _)) - .WillRepeatedly([&](auto, auto, auto, auto, auto) { notification.notify(); }); + .WillRepeatedly([&](auto, auto, auto, auto, auto, auto) { notification.notify(); }); factory.generate("/path/to/Component.qml"); notification.wait(); @@ -166,9 +170,10 @@ TEST_F(AsynchronousImageFactory, capture_image_callback_stores_image) Eq("id"), VariantWith(std::monostate{}), _, + _, _)) - .WillByDefault([&](auto, auto, auto, auto capture, auto) { - capture(image1, midSizeImage1, smallImage1); + .WillByDefault([&](auto, auto, auto, auto capture, auto, auto) { + capture(image1, midSizeImage1, smallImage1, {}); }); EXPECT_CALL(storageMock, diff --git a/tests/unit/tests/unittests/imagecache/imagecachedispatchcollector-test.cpp b/tests/unit/tests/unittests/imagecache/imagecachedispatchcollector-test.cpp index 8d459916ffc..b463a5a698e 100644 --- a/tests/unit/tests/unittests/imagecache/imagecachedispatchcollector-test.cpp +++ b/tests/unit/tests/unittests/imagecache/imagecachedispatchcollector-test.cpp @@ -12,6 +12,7 @@ namespace { using QmlDesigner::ImageCache::FontCollectorSizesAuxiliaryData; +using QmlDesigner::ImageCache::TraceToken; MATCHER_P(IsIcon, icon, std::string(negation ? "isn't " : "is ") + PrintToString(icon)) { @@ -47,8 +48,8 @@ protected: protected: std::vector sizes{{20, 11}}; - NiceMock> captureCallbackMock; - NiceMock> abortCallbackMock; + NiceMock> captureCallbackMock; + NiceMock> abortCallbackMock; NiceMock collectorMock1; NiceMock collectorMock2; QImage image1{1, 1, QImage::Format_ARGB32}; @@ -83,8 +84,8 @@ TEST_F(ImageCacheDispatchCollector, call_qml_collector_start) }, &collectorMock2))}; - EXPECT_CALL(captureCallbackMock, Call(_, _, _)); - EXPECT_CALL(abortCallbackMock, Call(_)); + EXPECT_CALL(captureCallbackMock, Call(_, _, _, _)); + EXPECT_CALL(abortCallbackMock, Call(_, _)); EXPECT_CALL(collectorMock2, start(Eq("foo.qml"), Eq("state"), @@ -93,18 +94,20 @@ TEST_F(ImageCacheDispatchCollector, call_qml_collector_start) ElementsAre(QSize{20, 11})), Field(&FontCollectorSizesAuxiliaryData::colorName, Eq(u"color")))), _, + _, _)) - .WillRepeatedly([&](auto, auto, auto, auto captureCallback, auto abortCallback) { - captureCallback({}, {}, {}); - abortCallback(QmlDesigner::ImageCache::AbortReason::Abort); + .WillRepeatedly([&](auto, auto, auto, auto captureCallback, auto abortCallback, auto) { + captureCallback({}, {}, {}, {}); + abortCallback(QmlDesigner::ImageCache::AbortReason::Abort, {}); }); - EXPECT_CALL(collectorMock1, start(_, _, _, _, _)).Times(0); + EXPECT_CALL(collectorMock1, start(_, _, _, _, _, _)).Times(0); collector.start("foo.qml", "state", FontCollectorSizesAuxiliaryData{sizes, "color", "text"}, captureCallbackMock.AsStdFunction(), - abortCallbackMock.AsStdFunction()); + abortCallbackMock.AsStdFunction(), + {}); } TEST_F(ImageCacheDispatchCollector, call_ui_file_collector_start) @@ -119,8 +122,8 @@ TEST_F(ImageCacheDispatchCollector, call_ui_file_collector_start) const QmlDesigner::ImageCache::AuxiliaryData &) { return true; }, &collectorMock2))}; - EXPECT_CALL(captureCallbackMock, Call(_, _, _)); - EXPECT_CALL(abortCallbackMock, Call(_)); + EXPECT_CALL(captureCallbackMock, Call(_, _, _, _)); + EXPECT_CALL(abortCallbackMock, Call(_, _)); EXPECT_CALL(collectorMock1, start(Eq("foo.ui.qml"), Eq("state"), @@ -129,18 +132,20 @@ TEST_F(ImageCacheDispatchCollector, call_ui_file_collector_start) ElementsAre(QSize{20, 11})), Field(&FontCollectorSizesAuxiliaryData::colorName, Eq(u"color")))), _, + _, _)) - .WillRepeatedly([&](auto, auto, auto, auto captureCallback, auto abortCallback) { - captureCallback({}, {}, {}); - abortCallback(QmlDesigner::ImageCache::AbortReason::Abort); + .WillRepeatedly([&](auto, auto, auto, auto captureCallback, auto abortCallback, auto) { + captureCallback({}, {}, {}, {}); + abortCallback(QmlDesigner::ImageCache::AbortReason::Abort, {}); }); - EXPECT_CALL(collectorMock2, start(_, _, _, _, _)).Times(0); + EXPECT_CALL(collectorMock2, start(_, _, _, _, _, _)).Times(0); collector.start("foo.ui.qml", "state", FontCollectorSizesAuxiliaryData{sizes, "color", "text"}, captureCallbackMock.AsStdFunction(), - abortCallbackMock.AsStdFunction()); + abortCallbackMock.AsStdFunction(), + {}); } TEST_F(ImageCacheDispatchCollector, dont_call_collector_start_for_unknown_file) @@ -155,14 +160,15 @@ TEST_F(ImageCacheDispatchCollector, dont_call_collector_start_for_unknown_file) const QmlDesigner::ImageCache::AuxiliaryData &) { return false; }, &collectorMock2))}; - EXPECT_CALL(collectorMock2, start(_, _, _, _, _)).Times(0); - EXPECT_CALL(collectorMock1, start(_, _, _, _, _)).Times(0); + EXPECT_CALL(collectorMock2, start(_, _, _, _, _, _)).Times(0); + EXPECT_CALL(collectorMock1, start(_, _, _, _, _, _)).Times(0); collector.start("foo.mesh", "state", FontCollectorSizesAuxiliaryData{sizes, "color", "text"}, captureCallbackMock.AsStdFunction(), - abortCallbackMock.AsStdFunction()); + abortCallbackMock.AsStdFunction(), + {}); } TEST_F(ImageCacheDispatchCollector, call_first_collector_create_icon) diff --git a/tests/unit/tests/unittests/imagecache/imagecachegenerator-test.cpp b/tests/unit/tests/unittests/imagecache/imagecachegenerator-test.cpp index 878552964a8..15e31764ad7 100644 --- a/tests/unit/tests/unittests/imagecache/imagecachegenerator-test.cpp +++ b/tests/unit/tests/unittests/imagecache/imagecachegenerator-test.cpp @@ -13,6 +13,8 @@ namespace { +using QmlDesigner::ImageCache::TraceToken; + class ImageCacheGenerator : public testing::Test { protected: @@ -34,8 +36,8 @@ protected: QImage image1{10, 10, QImage::Format_ARGB32}; QImage midSizeImage1{5, 5, QImage::Format_ARGB32}; QImage smallImage1{1, 1, QImage::Format_ARGB32}; - NiceMock> imageCallbackMock; - NiceMock> abortCallbackMock; + NiceMock> imageCallbackMock; + NiceMock> abortCallbackMock; NiceMock collectorMock; NiceMock storageMock; QmlDesigner::ImageCacheGenerator generator{collectorMock, storageMock}; @@ -43,13 +45,13 @@ protected: TEST_F(ImageCacheGenerator, calls_collector_with_capture_callback) { - EXPECT_CALL(collectorMock, start(Eq("name"), _, _, _, _)) - .WillRepeatedly([&](auto, auto, auto, auto captureCallback, auto) { - captureCallback(QImage{image1}, QImage{midSizeImage1}, QImage{smallImage1}); + EXPECT_CALL(collectorMock, start(Eq("name"), _, _, _, _, _)) + .WillRepeatedly([&](auto, auto, auto, auto captureCallback, auto, auto) { + captureCallback(QImage{image1}, QImage{midSizeImage1}, QImage{smallImage1}, {}); }); - EXPECT_CALL(imageCallbackMock, Call(_, _, _)) + EXPECT_CALL(imageCallbackMock, Call(_, _, _, _)) .WillRepeatedly( - [&](const QImage &, const QImage &, const QImage &) { notification.notify(); }); + [&](const QImage &, const QImage &, const QImage &, auto) { notification.notify(); }); generator.generateImage("name", {}, {}, imageCallbackMock.AsStdFunction(), {}, {}); notification.wait(); @@ -57,8 +59,8 @@ TEST_F(ImageCacheGenerator, calls_collector_with_capture_callback) TEST_F(ImageCacheGenerator, calls_collector_only_if_not_processing) { - EXPECT_CALL(collectorMock, start(AnyOf(Eq("name"), Eq("name2")), _, _, _, _)) - .WillRepeatedly([&](auto, auto, auto, auto, auto) { notification.notify(); }); + EXPECT_CALL(collectorMock, start(AnyOf(Eq("name"), Eq("name2")), _, _, _, _, _)) + .WillRepeatedly([&](auto, auto, auto, auto, auto, auto) { notification.notify(); }); generator.generateImage("name", {}, {}, imageCallbackMock.AsStdFunction(), {}, {}); generator.generateImage("name2", {}, {}, imageCallbackMock.AsStdFunction(), {}, {}); @@ -67,16 +69,17 @@ TEST_F(ImageCacheGenerator, calls_collector_only_if_not_processing) TEST_F(ImageCacheGenerator, process_task_after_first_finished) { - ON_CALL(imageCallbackMock, Call(_, _, _)) - .WillByDefault([&](const QImage &, const QImage &, const QImage &) { notification.notify(); }); + ON_CALL(imageCallbackMock, Call(_, _, _, _)) + .WillByDefault( + [&](const QImage &, const QImage &, const QImage &, auto) { notification.notify(); }); - EXPECT_CALL(collectorMock, start(Eq("name"), _, _, _, _)) - .WillOnce([&](auto, auto, auto, auto captureCallback, auto) { - captureCallback(QImage{image1}, QImage{midSizeImage1}, QImage{smallImage1}); + EXPECT_CALL(collectorMock, start(Eq("name"), _, _, _, _, _)) + .WillOnce([&](auto, auto, auto, auto captureCallback, auto, auto) { + captureCallback(QImage{image1}, QImage{midSizeImage1}, QImage{smallImage1}, {}); }); - EXPECT_CALL(collectorMock, start(Eq("name2"), _, _, _, _)) - .WillOnce([&](auto, auto, auto, auto captureCallback, auto) { - captureCallback(QImage{image1}, QImage{midSizeImage1}, QImage{smallImage1}); + EXPECT_CALL(collectorMock, start(Eq("name2"), _, _, _, _, _)) + .WillOnce([&](auto, auto, auto, auto captureCallback, auto, auto) { + captureCallback(QImage{image1}, QImage{midSizeImage1}, QImage{smallImage1}, {}); }); generator.generateImage("name", {}, {}, imageCallbackMock.AsStdFunction(), {}, {}); @@ -86,13 +89,13 @@ TEST_F(ImageCacheGenerator, process_task_after_first_finished) TEST_F(ImageCacheGenerator, dont_crash_at_destructing_generator) { - ON_CALL(collectorMock, start(Eq("name"), _, _, _, _)) - .WillByDefault([&](auto, auto, auto, auto captureCallback, auto) { - captureCallback(QImage{image1}, QImage{midSizeImage1}, QImage{smallImage1}); + ON_CALL(collectorMock, start(Eq("name"), _, _, _, _, _)) + .WillByDefault([&](auto, auto, auto, auto captureCallback, auto, auto) { + captureCallback(QImage{image1}, QImage{midSizeImage1}, QImage{smallImage1}, {}); }); generator.generateImage( - "name", {}, {}, imageCallbackMock.AsStdFunction(), abortCallbackMock.AsStdFunction(), {}); + "name", {}, {}, imageCallbackMock.AsStdFunction(), abortCallbackMock.AsStdFunction(), {}, {}); generator.generateImage( "name2", {}, {}, imageCallbackMock.AsStdFunction(), abortCallbackMock.AsStdFunction(), {}); generator.generateImage( @@ -103,9 +106,9 @@ TEST_F(ImageCacheGenerator, dont_crash_at_destructing_generator) TEST_F(ImageCacheGenerator, store_image) { - ON_CALL(collectorMock, start(Eq("name"), _, _, _, _)) - .WillByDefault([&](auto, auto, auto, auto captureCallback, auto) { - captureCallback(QImage{image1}, QImage{midSizeImage1}, QImage{smallImage1}); + ON_CALL(collectorMock, start(Eq("name"), _, _, _, _, _)) + .WillByDefault([&](auto, auto, auto, auto captureCallback, auto, auto) { + captureCallback(QImage{image1}, QImage{midSizeImage1}, QImage{smallImage1}, {}); }); EXPECT_CALL(storageMock, @@ -122,9 +125,9 @@ TEST_F(ImageCacheGenerator, store_image) TEST_F(ImageCacheGenerator, store_image_with_extra_id) { - ON_CALL(collectorMock, start(Eq("name"), _, _, _, _)) - .WillByDefault([&](auto, auto, auto, auto captureCallback, auto) { - captureCallback(QImage{image1}, QImage{midSizeImage1}, QImage{smallImage1}); + ON_CALL(collectorMock, start(Eq("name"), _, _, _, _, _)) + .WillByDefault([&](auto, auto, auto, auto captureCallback, auto, auto) { + captureCallback(QImage{image1}, QImage{midSizeImage1}, QImage{smallImage1}, {}); }); EXPECT_CALL(storageMock, @@ -141,9 +144,9 @@ TEST_F(ImageCacheGenerator, store_image_with_extra_id) TEST_F(ImageCacheGenerator, store_null_image) { - ON_CALL(collectorMock, start(Eq("name"), _, _, _, _)) - .WillByDefault([&](auto, auto, auto, auto captureCallback, auto) { - captureCallback(QImage{}, QImage{}, QImage{}); + ON_CALL(collectorMock, start(Eq("name"), _, _, _, _, _)) + .WillByDefault([&](auto, auto, auto, auto captureCallback, auto, auto) { + captureCallback(QImage{}, QImage{}, QImage{}, {}); }); EXPECT_CALL( @@ -158,9 +161,9 @@ TEST_F(ImageCacheGenerator, store_null_image) TEST_F(ImageCacheGenerator, store_null_image_with_extra_id) { - ON_CALL(collectorMock, start(Eq("name"), _, _, _, _)) - .WillByDefault([&](auto, auto, auto, auto captureCallback, auto) { - captureCallback(QImage{}, QImage{}, QImage{}); + ON_CALL(collectorMock, start(Eq("name"), _, _, _, _, _)) + .WillByDefault([&](auto, auto, auto, auto captureCallback, auto, auto) { + captureCallback(QImage{}, QImage{}, QImage{}, {}); }); EXPECT_CALL(storageMock, @@ -182,19 +185,20 @@ TEST_F(ImageCacheGenerator, store_null_image_with_extra_id) TEST_F(ImageCacheGenerator, abort_callback) { - ON_CALL(collectorMock, start(Eq("name"), _, _, _, _)) - .WillByDefault([&](auto, auto, auto, auto captureCallback, auto) { - captureCallback(QImage{image1}, QImage{midSizeImage1}, QImage{smallImage1}); + ON_CALL(collectorMock, start(Eq("name"), _, _, _, _, _)) + .WillByDefault([&](auto, auto, auto, auto captureCallback, auto, auto) { + captureCallback(QImage{image1}, QImage{midSizeImage1}, QImage{smallImage1}, {}); }); - ON_CALL(collectorMock, start(Eq("name2"), _, _, _, _)) - .WillByDefault([&](auto, auto, auto, auto, auto abortCallback) { - abortCallback(QmlDesigner::ImageCache::AbortReason::Failed); + ON_CALL(collectorMock, start(Eq("name2"), _, _, _, _, _)) + .WillByDefault([&](auto, auto, auto, auto, auto abortCallback, auto) { + abortCallback(QmlDesigner::ImageCache::AbortReason::Failed, {}); }); - EXPECT_CALL(imageCallbackMock, Call(_, _, _)) - .WillOnce([&](const QImage &, const QImage &, const QImage &) { notification.notify(); }); - EXPECT_CALL(abortCallbackMock, Call(Eq(QmlDesigner::ImageCache::AbortReason::Failed))) - .WillOnce([&](auto) { notification.notify(); }); + EXPECT_CALL(imageCallbackMock, Call(_, _, _, _)) + .WillOnce( + [&](const QImage &, const QImage &, const QImage &, auto) { notification.notify(); }); + EXPECT_CALL(abortCallbackMock, Call(Eq(QmlDesigner::ImageCache::AbortReason::Failed), _)) + .WillOnce([&](auto, auto) { notification.notify(); }); generator.generateImage( "name", {}, {}, imageCallbackMock.AsStdFunction(), abortCallbackMock.AsStdFunction(), {}); @@ -205,12 +209,12 @@ TEST_F(ImageCacheGenerator, abort_callback) TEST_F(ImageCacheGenerator, store_null_image_for_abort_callback_abort) { - ON_CALL(collectorMock, start(Eq("name"), _, _, _, _)) - .WillByDefault([&](auto, auto, auto, auto, auto abortCallback) { - abortCallback(QmlDesigner::ImageCache::AbortReason::Abort); + ON_CALL(collectorMock, start(Eq("name"), _, _, _, _, _)) + .WillByDefault([&](auto, auto, auto, auto, auto abortCallback, auto) { + abortCallback(QmlDesigner::ImageCache::AbortReason::Abort, {}); }); - ON_CALL(collectorMock, start(Eq("dummyNotify"), _, _, _, _)) - .WillByDefault([&](auto, auto, auto, auto, auto) { notification.notify(); }); + ON_CALL(collectorMock, start(Eq("dummyNotify"), _, _, _, _, _)) + .WillByDefault([&](auto, auto, auto, auto, auto, auto) { notification.notify(); }); EXPECT_CALL(storageMock, storeImage(Eq("name"), _, _, _, _)).Times(0); @@ -222,9 +226,9 @@ TEST_F(ImageCacheGenerator, store_null_image_for_abort_callback_abort) TEST_F(ImageCacheGenerator, dont_store_null_image_for_abort_callback_failed) { - ON_CALL(collectorMock, start(_, _, _, _, _)) - .WillByDefault([&](auto, auto, auto, auto, auto abortCallback) { - abortCallback(QmlDesigner::ImageCache::AbortReason::Failed); + ON_CALL(collectorMock, start(_, _, _, _, _, _)) + .WillByDefault([&](auto, auto, auto, auto, auto abortCallback, auto) { + abortCallback(QmlDesigner::ImageCache::AbortReason::Failed, {}); }); EXPECT_CALL( @@ -239,13 +243,13 @@ TEST_F(ImageCacheGenerator, dont_store_null_image_for_abort_callback_failed) TEST_F(ImageCacheGenerator, abort_for_null_image) { - ON_CALL(collectorMock, start(Eq("name"), _, _, _, _)) - .WillByDefault([&](auto, auto, auto, auto captureCallback, auto) { - captureCallback(QImage{}, QImage{}, QImage{}); + ON_CALL(collectorMock, start(Eq("name"), _, _, _, _, _)) + .WillByDefault([&](auto, auto, auto, auto captureCallback, auto, auto) { + captureCallback(QImage{}, QImage{}, QImage{}, {}); }); - EXPECT_CALL(abortCallbackMock, Call(Eq(QmlDesigner::ImageCache::AbortReason::Failed))) - .WillOnce([&](auto) { notification.notify(); }); + EXPECT_CALL(abortCallbackMock, Call(Eq(QmlDesigner::ImageCache::AbortReason::Failed), _)) + .WillOnce([&](auto, auto) { notification.notify(); }); generator.generateImage( "name", {}, {}, imageCallbackMock.AsStdFunction(), abortCallbackMock.AsStdFunction(), {}); @@ -254,13 +258,13 @@ TEST_F(ImageCacheGenerator, abort_for_null_image) TEST_F(ImageCacheGenerator, call_image_callback_if_small_image_is_not_null) { - ON_CALL(collectorMock, start(Eq("name"), _, _, _, _)) - .WillByDefault([&](auto, auto, auto, auto captureCallback, auto) { - captureCallback({}, {}, smallImage1); + ON_CALL(collectorMock, start(Eq("name"), _, _, _, _, _)) + .WillByDefault([&](auto, auto, auto, auto captureCallback, auto, auto) { + captureCallback({}, {}, smallImage1, {}); }); - EXPECT_CALL(imageCallbackMock, Call(Eq(QImage()), Eq(QImage()), Eq(smallImage1))) - .WillOnce([&](auto, auto, auto) { notification.notify(); }); + EXPECT_CALL(imageCallbackMock, Call(Eq(QImage()), Eq(QImage()), Eq(smallImage1), _)) + .WillOnce([&](auto, auto, auto, auto) { notification.notify(); }); generator.generateImage( "name", {}, {}, imageCallbackMock.AsStdFunction(), abortCallbackMock.AsStdFunction(), {}); @@ -269,9 +273,9 @@ TEST_F(ImageCacheGenerator, call_image_callback_if_small_image_is_not_null) TEST_F(ImageCacheGenerator, store_image_if_small_image_is_not_null) { - ON_CALL(collectorMock, start(Eq("name"), _, _, _, _)) - .WillByDefault([&](auto, auto, auto, auto captureCallback, auto) { - captureCallback({}, {}, smallImage1); + ON_CALL(collectorMock, start(Eq("name"), _, _, _, _, _)) + .WillByDefault([&](auto, auto, auto, auto captureCallback, auto, auto) { + captureCallback({}, {}, smallImage1, {}); }); EXPECT_CALL(storageMock, storeImage(_, _, Eq(QImage()), Eq(QImage()), Eq(smallImage1))) @@ -284,13 +288,13 @@ TEST_F(ImageCacheGenerator, store_image_if_small_image_is_not_null) TEST_F(ImageCacheGenerator, call_image_callback_if_mid_size_image_is_not_null) { - ON_CALL(collectorMock, start(Eq("name"), _, _, _, _)) - .WillByDefault([&](auto, auto, auto, auto captureCallback, auto) { - captureCallback({}, midSizeImage1, {}); + ON_CALL(collectorMock, start(Eq("name"), _, _, _, _, _)) + .WillByDefault([&](auto, auto, auto, auto captureCallback, auto, auto) { + captureCallback({}, midSizeImage1, {}, {}); }); - EXPECT_CALL(imageCallbackMock, Call(Eq(QImage()), Eq(midSizeImage1), Eq(QImage{}))) - .WillOnce([&](auto, auto, auto) { notification.notify(); }); + EXPECT_CALL(imageCallbackMock, Call(Eq(QImage()), Eq(midSizeImage1), Eq(QImage{}), _)) + .WillOnce([&](auto, auto, auto, auto) { notification.notify(); }); generator.generateImage( "name", {}, {}, imageCallbackMock.AsStdFunction(), abortCallbackMock.AsStdFunction(), {}); @@ -299,9 +303,9 @@ TEST_F(ImageCacheGenerator, call_image_callback_if_mid_size_image_is_not_null) TEST_F(ImageCacheGenerator, store_image_if_mid_size_image_is_not_null) { - ON_CALL(collectorMock, start(Eq("name"), _, _, _, _)) - .WillByDefault([&](auto, auto, auto, auto captureCallback, auto) { - captureCallback({}, midSizeImage1, {}); + ON_CALL(collectorMock, start(Eq("name"), _, _, _, _, _)) + .WillByDefault([&](auto, auto, auto, auto captureCallback, auto, auto) { + captureCallback({}, midSizeImage1, {}, {}); }); EXPECT_CALL(storageMock, storeImage(_, _, Eq(QImage()), Eq(midSizeImage1), Eq(QImage()))) @@ -314,12 +318,13 @@ TEST_F(ImageCacheGenerator, store_image_if_mid_size_image_is_not_null) TEST_F(ImageCacheGenerator, call_image_callback_if_image_is_not_null) { - ON_CALL(collectorMock, start(Eq("name"), _, _, _, _)) - .WillByDefault( - [&](auto, auto, auto, auto captureCallback, auto) { captureCallback(image1, {}, {}); }); + ON_CALL(collectorMock, start(Eq("name"), _, _, _, _, _)) + .WillByDefault([&](auto, auto, auto, auto captureCallback, auto, auto) { + captureCallback(image1, {}, {}, {}); + }); - EXPECT_CALL(imageCallbackMock, Call(Eq(image1), Eq(QImage{}), Eq(QImage{}))) - .WillOnce([&](auto, auto, auto) { notification.notify(); }); + EXPECT_CALL(imageCallbackMock, Call(Eq(image1), Eq(QImage{}), Eq(QImage{}), _)) + .WillOnce([&](auto, auto, auto, auto) { notification.notify(); }); generator.generateImage( "name", {}, {}, imageCallbackMock.AsStdFunction(), abortCallbackMock.AsStdFunction(), {}); @@ -328,9 +333,10 @@ TEST_F(ImageCacheGenerator, call_image_callback_if_image_is_not_null) TEST_F(ImageCacheGenerator, store_image_if_image_is_not_null) { - ON_CALL(collectorMock, start(Eq("name"), _, _, _, _)) - .WillByDefault( - [&](auto, auto, auto, auto captureCallback, auto) { captureCallback(image1, {}, {}); }); + ON_CALL(collectorMock, start(Eq("name"), _, _, _, _, _)) + .WillByDefault([&](auto, auto, auto, auto captureCallback, auto, auto) { + captureCallback(image1, {}, {}, {}); + }); EXPECT_CALL(storageMock, storeImage(_, _, Eq(image1), Eq(QImage{}), Eq(QImage{}))) .WillOnce([&](auto, auto, auto, auto, auto) { notification.notify(); }); @@ -342,9 +348,10 @@ TEST_F(ImageCacheGenerator, store_image_if_image_is_not_null) TEST_F(ImageCacheGenerator, call_wal_checkpoint_full_if_queue_is_empty) { - ON_CALL(collectorMock, start(Eq("name"), _, _, _, _)) - .WillByDefault( - [&](auto, auto, auto, auto captureCallback, auto) { captureCallback({}, {}, {}); }); + ON_CALL(collectorMock, start(Eq("name"), _, _, _, _, _)) + .WillByDefault([&](auto, auto, auto, auto captureCallback, auto, auto) { + captureCallback({}, {}, {}, {}); + }); EXPECT_CALL(storageMock, walCheckpointFull()).WillRepeatedly([&]() { notification.notify(); }); @@ -357,17 +364,16 @@ TEST_F(ImageCacheGenerator, call_wal_checkpoint_full_if_queue_is_empty) TEST_F(ImageCacheGenerator, clean_is_calling_abort_callback) { - ON_CALL(collectorMock, start(_, _, _, _, _)).WillByDefault([&](auto, auto, auto, auto, auto) { - notification.wait(); - }); + ON_CALL(collectorMock, start(_, _, _, _, _, _)) + .WillByDefault([&](auto, auto, auto, auto, auto, auto) { notification.wait(); }); generator.generateImage( "name", {}, {11}, imageCallbackMock.AsStdFunction(), abortCallbackMock.AsStdFunction(), {}); generator.generateImage( "name2", {}, {11}, imageCallbackMock.AsStdFunction(), abortCallbackMock.AsStdFunction(), {}); - EXPECT_CALL(abortCallbackMock, Call(Eq(QmlDesigner::ImageCache::AbortReason::Abort))) + EXPECT_CALL(abortCallbackMock, Call(Eq(QmlDesigner::ImageCache::AbortReason::Abort), _)) .Times(AtLeast(1)) - .WillRepeatedly([&](auto) { waitInThread.notify(); }); + .WillRepeatedly([&](auto, auto) { waitInThread.notify(); }); generator.clean(); notification.notify(); @@ -376,14 +382,14 @@ TEST_F(ImageCacheGenerator, clean_is_calling_abort_callback) TEST_F(ImageCacheGenerator, wait_for_finished) { - ON_CALL(collectorMock, start(Eq("name"), _, _, _, _)) - .WillByDefault([&](auto, auto, auto, auto captureCallback, auto) { + ON_CALL(collectorMock, start(Eq("name"), _, _, _, _, _)) + .WillByDefault([&](auto, auto, auto, auto captureCallback, auto, auto) { waitInThread.wait(); - captureCallback(QImage{image1}, QImage{midSizeImage1}, QImage{smallImage1}); + captureCallback(QImage{image1}, QImage{midSizeImage1}, QImage{smallImage1}, {}); }); - ON_CALL(collectorMock, start(Eq("name2"), _, _, _, _)) - .WillByDefault([&](auto, auto, auto, auto captureCallback, auto) { - captureCallback(QImage{image1}, QImage{midSizeImage1}, QImage{smallImage1}); + ON_CALL(collectorMock, start(Eq("name2"), _, _, _, _, _)) + .WillByDefault([&](auto, auto, auto, auto captureCallback, auto, auto) { + captureCallback(QImage{image1}, QImage{midSizeImage1}, QImage{smallImage1}, {}); }); generator.generateImage( @@ -391,7 +397,7 @@ TEST_F(ImageCacheGenerator, wait_for_finished) generator.generateImage( "name2", {}, {11}, imageCallbackMock.AsStdFunction(), abortCallbackMock.AsStdFunction(), {}); - EXPECT_CALL(imageCallbackMock, Call(_, _, _)).Times(AtMost(2)); + EXPECT_CALL(imageCallbackMock, Call(_, _, _, _)).Times(AtMost(2)); waitInThread.notify(); generator.waitForFinished(); @@ -399,8 +405,8 @@ TEST_F(ImageCacheGenerator, wait_for_finished) TEST_F(ImageCacheGenerator, calls_collector_with_extra_id) { - EXPECT_CALL(collectorMock, start(Eq("name"), Eq("extraId1"), _, _, _)) - .WillRepeatedly([&](auto, auto, auto, auto, auto) { notification.notify(); }); + EXPECT_CALL(collectorMock, start(Eq("name"), Eq("extraId1"), _, _, _, _)) + .WillRepeatedly([&](auto, auto, auto, auto, auto, auto) { notification.notify(); }); generator.generateImage("name", "extraId1", {}, imageCallbackMock.AsStdFunction(), {}, {}); notification.wait(); @@ -419,8 +425,9 @@ TEST_F(ImageCacheGenerator, calls_collector_with_auxiliary_data) ElementsAre(QSize{20, 11})), Field(&FontCollectorSizesAuxiliaryData::colorName, Eq(u"color")))), _, + _, _)) - .WillRepeatedly([&](auto, auto, auto, auto, auto) { notification.notify(); }); + .WillRepeatedly([&](auto, auto, auto, auto, auto, auto) { notification.notify(); }); generator.generateImage("name", {}, @@ -433,12 +440,12 @@ TEST_F(ImageCacheGenerator, calls_collector_with_auxiliary_data) TEST_F(ImageCacheGenerator, merge_tasks) { - EXPECT_CALL(collectorMock, start(Eq("waitDummy"), _, _, _, _)) - .WillRepeatedly([&](auto, auto, auto, auto, auto) { waitInThread.wait(); }); - EXPECT_CALL(collectorMock, start(Eq("notificationDummy"), _, _, _, _)) - .WillRepeatedly([&](auto, auto, auto, auto, auto) { notification.notify(); }); + EXPECT_CALL(collectorMock, start(Eq("waitDummy"), _, _, _, _, _)) + .WillRepeatedly([&](auto, auto, auto, auto, auto, auto) { waitInThread.wait(); }); + EXPECT_CALL(collectorMock, start(Eq("notificationDummy"), _, _, _, _, _)) + .WillRepeatedly([&](auto, auto, auto, auto, auto, auto) { notification.notify(); }); - EXPECT_CALL(collectorMock, start(Eq("name"), _, _, _, _)); + EXPECT_CALL(collectorMock, start(Eq("name"), _, _, _, _, _)); generator.generateImage("waitDummy", {}, {}, {}, {}, {}); generator.generateImage("name", {}, {}, {}, {}, {}); @@ -450,13 +457,13 @@ TEST_F(ImageCacheGenerator, merge_tasks) TEST_F(ImageCacheGenerator, dont_merge_tasks_with_different_id) { - EXPECT_CALL(collectorMock, start(Eq("waitDummy"), _, _, _, _)) - .WillRepeatedly([&](auto, auto, auto, auto, auto) { waitInThread.wait(); }); + EXPECT_CALL(collectorMock, start(Eq("waitDummy"), _, _, _, _, _)) + .WillRepeatedly([&](auto, auto, auto, auto, auto, auto) { waitInThread.wait(); }); - EXPECT_CALL(collectorMock, start(Eq("name"), _, _, _, _)) - .WillRepeatedly([&](auto, auto, auto, auto, auto) { notification.notify(); }); - EXPECT_CALL(collectorMock, start(Eq("name2"), _, _, _, _)) - .WillRepeatedly([&](auto, auto, auto, auto, auto) { notification.notify(); }); + EXPECT_CALL(collectorMock, start(Eq("name"), _, _, _, _, _)) + .WillRepeatedly([&](auto, auto, auto, auto, auto, auto) { notification.notify(); }); + EXPECT_CALL(collectorMock, start(Eq("name2"), _, _, _, _, _)) + .WillRepeatedly([&](auto, auto, auto, auto, auto, auto) { notification.notify(); }); generator.generateImage("waitDummy", {}, {}, {}, {}, {}); generator.generateImage("name", {}, {}, {}, {}, {}); @@ -467,12 +474,12 @@ TEST_F(ImageCacheGenerator, dont_merge_tasks_with_different_id) TEST_F(ImageCacheGenerator, merge_tasks_with_same_extra_id) { - EXPECT_CALL(collectorMock, start(Eq("waitDummy"), _, _, _, _)) - .WillRepeatedly([&](auto, auto, auto, auto, auto) { waitInThread.wait(); }); - EXPECT_CALL(collectorMock, start(Eq("notificationDummy"), _, _, _, _)) - .WillRepeatedly([&](auto, auto, auto, auto, auto) { notification.notify(); }); + EXPECT_CALL(collectorMock, start(Eq("waitDummy"), _, _, _, _, _)) + .WillRepeatedly([&](auto, auto, auto, auto, auto, auto) { waitInThread.wait(); }); + EXPECT_CALL(collectorMock, start(Eq("notificationDummy"), _, _, _, _, _)) + .WillRepeatedly([&](auto, auto, auto, auto, auto, auto) { notification.notify(); }); - EXPECT_CALL(collectorMock, start(Eq("name"), _, _, _, _)); + EXPECT_CALL(collectorMock, start(Eq("name"), _, _, _, _, _)); generator.generateImage("waitDummy", {}, {}, {}, {}, {}); generator.generateImage("name", "id1", {}, {}, {}, {}); @@ -484,12 +491,12 @@ TEST_F(ImageCacheGenerator, merge_tasks_with_same_extra_id) TEST_F(ImageCacheGenerator, dont_merge_tasks_with_different_extra_id) { - EXPECT_CALL(collectorMock, start(Eq("waitDummy"), _, _, _, _)) - .WillRepeatedly([&](auto, auto, auto, auto, auto) { waitInThread.wait(); }); + EXPECT_CALL(collectorMock, start(Eq("waitDummy"), _, _, _, _, _)) + .WillRepeatedly([&](auto, auto, auto, auto, auto, auto) { waitInThread.wait(); }); - EXPECT_CALL(collectorMock, start(Eq("name"), _, _, _, _)) + EXPECT_CALL(collectorMock, start(Eq("name"), _, _, _, _, _)) .Times(2) - .WillRepeatedly([&](auto, auto, auto, auto, auto) { notification.notify(); }); + .WillRepeatedly([&](auto, auto, auto, auto, auto, auto) { notification.notify(); }); generator.generateImage("waitDummy", {}, {}, {}, {}, {}); generator.generateImage("name", "id1", {}, {}, {}, {}); @@ -500,13 +507,13 @@ TEST_F(ImageCacheGenerator, dont_merge_tasks_with_different_extra_id) TEST_F(ImageCacheGenerator, use_last_time_stamp_if_tasks_are_merged) { - ON_CALL(collectorMock, start(Eq("waitDummy"), _, _, _, _)) - .WillByDefault([&](auto, auto, auto, auto, auto) { waitInThread.wait(); }); - ON_CALL(collectorMock, start(Eq("notificationDummy"), _, _, _, _)) - .WillByDefault([&](auto, auto, auto, auto, auto) { notification.notify(); }); - ON_CALL(collectorMock, start(Eq("name"), _, _, _, _)) - .WillByDefault([&](auto, auto, auto, auto, auto abortCallback) { - abortCallback(QmlDesigner::ImageCache::AbortReason::Failed); + ON_CALL(collectorMock, start(Eq("waitDummy"), _, _, _, _, _)) + .WillByDefault([&](auto, auto, auto, auto, auto, auto) { waitInThread.wait(); }); + ON_CALL(collectorMock, start(Eq("notificationDummy"), _, _, _, _, _)) + .WillByDefault([&](auto, auto, auto, auto, auto, auto) { notification.notify(); }); + ON_CALL(collectorMock, start(Eq("name"), _, _, _, _, _)) + .WillByDefault([&](auto, auto, auto, auto, auto abortCallback, auto) { + abortCallback(QmlDesigner::ImageCache::AbortReason::Failed, {}); }); EXPECT_CALL(storageMock, storeImage(Eq("name"), Eq(Sqlite::TimeStamp{4}), _, _, _)); @@ -521,18 +528,18 @@ TEST_F(ImageCacheGenerator, use_last_time_stamp_if_tasks_are_merged) TEST_F(ImageCacheGenerator, merge_capture_callback_if_tasks_are_merged) { - NiceMock> newerImageCallbackMock; - ON_CALL(collectorMock, start(Eq("waitDummy"), _, _, _, _)) - .WillByDefault([&](auto, auto, auto, auto, auto) { waitInThread.wait(); }); - ON_CALL(collectorMock, start(Eq("notificationDummy"), _, _, _, _)) - .WillByDefault([&](auto, auto, auto, auto, auto) { notification.notify(); }); - ON_CALL(collectorMock, start(Eq("name"), _, _, _, _)) - .WillByDefault([&](auto, auto, auto, auto imageCallback, auto) { - imageCallback(QImage{image1}, QImage{midSizeImage1}, QImage{smallImage1}); + NiceMock> newerImageCallbackMock; + ON_CALL(collectorMock, start(Eq("waitDummy"), _, _, _, _, _)) + .WillByDefault([&](auto, auto, auto, auto, auto, auto) { waitInThread.wait(); }); + ON_CALL(collectorMock, start(Eq("notificationDummy"), _, _, _, _, _)) + .WillByDefault([&](auto, auto, auto, auto, auto, auto) { notification.notify(); }); + ON_CALL(collectorMock, start(Eq("name"), _, _, _, _, _)) + .WillByDefault([&](auto, auto, auto, auto imageCallback, auto, auto) { + imageCallback(QImage{image1}, QImage{midSizeImage1}, QImage{smallImage1}, {}); }); - EXPECT_CALL(imageCallbackMock, Call(_, _, _)); - EXPECT_CALL(newerImageCallbackMock, Call(_, _, _)); + EXPECT_CALL(imageCallbackMock, Call(_, _, _, _)); + EXPECT_CALL(newerImageCallbackMock, Call(_, _, _, _)); generator.generateImage("waitDummy", {}, {}, {}, {}, {}); generator.generateImage("name", {}, {}, imageCallbackMock.AsStdFunction(), {}, {}); @@ -544,18 +551,18 @@ TEST_F(ImageCacheGenerator, merge_capture_callback_if_tasks_are_merged) TEST_F(ImageCacheGenerator, merge_abort_callback_if_tasks_are_merged) { - NiceMock> newerAbortCallbackMock; - ON_CALL(collectorMock, start(Eq("waitDummy"), _, _, _, _)) - .WillByDefault([&](auto, auto, auto, auto, auto) { waitInThread.wait(); }); - ON_CALL(collectorMock, start(Eq("notificationDummy"), _, _, _, _)) - .WillByDefault([&](auto, auto, auto, auto, auto) { notification.notify(); }); - ON_CALL(collectorMock, start(Eq("name"), _, _, _, _)) - .WillByDefault([&](auto, auto, auto, auto, auto abortCallback) { - abortCallback(QmlDesigner::ImageCache::AbortReason::Failed); + NiceMock> newerAbortCallbackMock; + ON_CALL(collectorMock, start(Eq("waitDummy"), _, _, _, _, _)) + .WillByDefault([&](auto, auto, auto, auto, auto, auto) { waitInThread.wait(); }); + ON_CALL(collectorMock, start(Eq("notificationDummy"), _, _, _, _, _)) + .WillByDefault([&](auto, auto, auto, auto, auto, auto) { notification.notify(); }); + ON_CALL(collectorMock, start(Eq("name"), _, _, _, _, _)) + .WillByDefault([&](auto, auto, auto, auto, auto abortCallback, auto) { + abortCallback(QmlDesigner::ImageCache::AbortReason::Failed, {}); }); - EXPECT_CALL(abortCallbackMock, Call(Eq(QmlDesigner::ImageCache::AbortReason::Failed))); - EXPECT_CALL(newerAbortCallbackMock, Call(Eq(QmlDesigner::ImageCache::AbortReason::Failed))); + EXPECT_CALL(abortCallbackMock, Call(Eq(QmlDesigner::ImageCache::AbortReason::Failed), _)); + EXPECT_CALL(newerAbortCallbackMock, Call(Eq(QmlDesigner::ImageCache::AbortReason::Failed), _)); generator.generateImage("waitDummy", {}, {}, {}, {}, {}); generator.generateImage("name", {}, {}, {}, abortCallbackMock.AsStdFunction(), {}); @@ -567,9 +574,9 @@ TEST_F(ImageCacheGenerator, merge_abort_callback_if_tasks_are_merged) TEST_F(ImageCacheGenerator, dont_call_null_image_callback) { - EXPECT_CALL(collectorMock, start(_, _, _, _, _)) - .WillOnce([&](auto, auto, auto, auto captureCallback, auto) { - captureCallback(image1, midSizeImage1, smallImage1); + EXPECT_CALL(collectorMock, start(_, _, _, _, _, _)) + .WillOnce([&](auto, auto, auto, auto captureCallback, auto, auto) { + captureCallback(image1, midSizeImage1, smallImage1, {}); notification.notify(); }); @@ -579,9 +586,9 @@ TEST_F(ImageCacheGenerator, dont_call_null_image_callback) TEST_F(ImageCacheGenerator, dont_call_null_abort_callback_for_null_image) { - EXPECT_CALL(collectorMock, start(_, _, _, _, _)) - .WillOnce([&](auto, auto, auto, auto captureCallback, auto) { - captureCallback(QImage{}, QImage{}, QImage{}); + EXPECT_CALL(collectorMock, start(_, _, _, _, _, _)) + .WillOnce([&](auto, auto, auto, auto captureCallback, auto, auto) { + captureCallback(QImage{}, QImage{}, QImage{}, {}); notification.notify(); }); @@ -591,9 +598,9 @@ TEST_F(ImageCacheGenerator, dont_call_null_abort_callback_for_null_image) TEST_F(ImageCacheGenerator, dont_call_null_abort_callback) { - EXPECT_CALL(collectorMock, start(_, _, _, _, _)) - .WillOnce([&](auto, auto, auto, auto, auto abortCallback) { - abortCallback(QmlDesigner::ImageCache::AbortReason::Failed); + EXPECT_CALL(collectorMock, start(_, _, _, _, _, _)) + .WillOnce([&](auto, auto, auto, auto, auto abortCallback, auto) { + abortCallback(QmlDesigner::ImageCache::AbortReason::Failed, {}); notification.notify(); }); From ad12bbb6698a6c00ed127f79a0078f52b1e7ce7d Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Sat, 14 Oct 2023 15:56:03 +0200 Subject: [PATCH 051/242] QmlDesigner: Add object trace 'N' and 'D' phases are not supported by Perfetto. So I mapped it to async events. This patch is improving the interface for arguments too. Now you can select the type for you arguments. So the name can be a string_view but the arguments can be a string. The variadic template arguments are used to prevent any conversion code which could not be optimized out for an empty function. Change-Id: I1ad1927b5e3b63607a21df1351b1f5cfba50159c Reviewed-by: Tim Jenssen Reviewed-by: Qt CI Patch Build Bot Reviewed-by: --- src/libs/nanotrace/nanotracehr.cpp | 12 +- src/libs/nanotrace/nanotracehr.h | 401 +++++++++++++++--- src/plugins/qmldesigner/CMakeLists.txt | 13 + .../imagecache/asynchronousimagecache.cpp | 11 +- .../imagecache/imagecachegenerator.h | 2 +- .../include/asynchronousimagecache.h | 2 +- .../include/imagecacheauxiliarydata.h | 2 +- .../designercore/model/internalnode_p.h | 4 + .../designercore/model/internalproperty.h | 6 + .../qmldesigner/designercore/model/model.cpp | 5 + .../projectstorage/projectstorage.cpp | 13 +- .../tracing/qmldesignertracing.cpp | 45 ++ .../designercore/tracing/qmldesignertracing.h | 47 ++ .../tests/testdesignercore/CMakeLists.txt | 1 + 14 files changed, 481 insertions(+), 83 deletions(-) create mode 100644 src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.cpp create mode 100644 src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.h diff --git a/src/libs/nanotrace/nanotracehr.cpp b/src/libs/nanotrace/nanotracehr.cpp index e8c9abd7747..d97aa889391 100644 --- a/src/libs/nanotrace/nanotracehr.cpp +++ b/src/libs/nanotrace/nanotracehr.cpp @@ -23,8 +23,8 @@ template void printEvent(std::ostream &out, const TraceEvent &event, qint64 processId, std::thread::id threadId) { out << R"({"ph":")" << event.type << R"(","name":")" << event.name << R"(","cat":")" - << event.category << R"(","ts":")" - << static_cast(event.time.time_since_epoch().count()) / 1000 << R"(","pid":)" + << event.category << R"(","ts":)" + << static_cast(event.time.time_since_epoch().count()) / 1000 << R"(,"pid":)" << processId << R"(,"tid":)" << threadId; if (event.type == 'X') @@ -94,6 +94,10 @@ template NANOTRACE_EXPORT void flushEvents(const Utils::span events, std::thread::id threadId, EnabledEventQueue &eventQueue); +template NANOTRACE_EXPORT void flushEvents( + const Utils::span events, + std::thread::id threadId, + EnabledEventQueue &eventQueue); void openFile(EnabledTraceFile &file) { @@ -143,6 +147,8 @@ void flushInThread(EnabledEventQueue &eventQueue) template NANOTRACE_EXPORT void flushInThread(EnabledEventQueue &eventQueue); template NANOTRACE_EXPORT void flushInThread(EnabledEventQueue &eventQueue); +template NANOTRACE_EXPORT void flushInThread( + EnabledEventQueue &eventQueue); namespace { EnabledTraceFile globalTraceFile{"global.json"}; @@ -196,4 +202,6 @@ void EventQueue::flush() template class EventQueue; template class EventQueue; +template class EventQueue; + } // namespace NanotraceHR diff --git a/src/libs/nanotrace/nanotracehr.h b/src/libs/nanotrace/nanotracehr.h index d90f57b9e7f..cfe006cd6ba 100644 --- a/src/libs/nanotrace/nanotracehr.h +++ b/src/libs/nanotrace/nanotracehr.h @@ -5,9 +5,12 @@ #include "nanotraceglobals.h" +#include #include +#include #include +#include #include #include @@ -17,6 +20,7 @@ #include #include #include +#include namespace NanotraceHR { using Clock = std::chrono::steady_clock; @@ -36,6 +40,84 @@ constexpr bool isTracerActive() #endif } +#if __cplusplus >= 202002L && __has_cpp_attribute(msvc::no_unique_address) +# define NO_UNIQUE_ADDRESS [[msvc::no_unique_address]] +#elif __cplusplus >= 202002L && __has_cpp_attribute(no_unique_address) >= 201803L +# define NO_UNIQUE_ADDRESS [[no_unique_address]] +#else +# define NO_UNIQUE_ADDRESS +#endif + +namespace Internal { +inline std::string_view covertToString(std::string_view text) +{ + return text; +} + +template +inline std::string_view covertToString(const char (&text)[size]) +{ + return text; +} + +inline Utils::PathString covertToString(QStringView text) +{ + return text; +} + +inline Utils::PathString covertToString(QByteArrayView text) +{ + return text; +} + +inline Utils::SmallString covertToString(long long number) +{ + return Utils::SmallString::number(number); +} + +inline Utils::SmallString covertToString(double number) +{ + return Utils::SmallString::number(number); +} + +template +void toArgument(String &text, Argument &&argument) +{ + const auto &[key, value] = argument; + text.append(R"(")"); + text.append(covertToString(key)); + text.append(R"(":")"); + text.append(covertToString(value)); + text.append(R"(",)"); +} + +template +String toArguments(Arguments &&...arguments) +{ + if constexpr (isTracerActive()) { + String text; + constexpr auto argumentCount = sizeof...(Arguments); + text.reserve(sizeof...(Arguments) * 20); + text.append("{"); + (toArgument(text, arguments), ...); + if (argumentCount) + text.pop_back(); + + text.append("}"); + + return text; + } else { + return {}; + } +} + +inline std::string_view toArguments(std::string_view arguments) +{ + return arguments; +} + +} // namespace Internal + namespace Literals { struct TracerLiteral { @@ -43,6 +125,8 @@ struct TracerLiteral constexpr operator std::string_view() const { return text; } + operator std::string() const { return std::string{text}; } + private: constexpr TracerLiteral(std::string_view text) : text{text} @@ -59,11 +143,12 @@ constexpr TracerLiteral operator""_t(const char *text, size_t size) using namespace Literals; -template +template struct TraceEvent { using StringType = String; using ArgumentType = std::conditional_t, TracerLiteral, String>; + using ArgumentsStringType = ArgumentsString; TraceEvent() = default; TraceEvent(const TraceEvent &) = delete; @@ -74,15 +159,16 @@ struct TraceEvent String name; String category; - String arguments; + ArgumentsString arguments; TimePoint time; Duration duration; std::size_t id = 0; char type = ' '; }; -using StringViewTraceEvent = TraceEvent; -using StringTraceEvent = TraceEvent; +using StringViewTraceEvent = TraceEvent; +using StringViewWithStringArgumentsTraceEvent = TraceEvent; +using StringTraceEvent = TraceEvent; enum class IsEnabled { No, Yes }; @@ -102,11 +188,16 @@ extern template NANOTRACE_EXPORT void flushEvents(const Utils::span events, std::thread::id threadId, EnabledEventQueue &eventQueue); - +extern template NANOTRACE_EXPORT void flushEvents( + const Utils::span events, + std::thread::id threadId, + EnabledEventQueue &eventQueue); template void flushInThread(EnabledEventQueue &eventQueue); extern template NANOTRACE_EXPORT void flushInThread(EnabledEventQueue &eventQueue); extern template NANOTRACE_EXPORT void flushInThread(EnabledEventQueue &eventQueue); +extern template NANOTRACE_EXPORT void flushInThread( + EnabledEventQueue &eventQueue); template class TraceFile; @@ -188,6 +279,7 @@ public: extern template class NANOTRACE_EXPORT EventQueue; extern template class NANOTRACE_EXPORT EventQueue; +extern template class NANOTRACE_EXPORT EventQueue; template class EventQueueData @@ -259,69 +351,92 @@ TraceEvent &getTraceEvent(EnabledEventQueue &eventQueue) return eventQueue.currentEvents[eventQueue.eventsIndex++]; } -template -class Token +class BasicDisabledToken { public: using IsActive = std::false_type; - using ArgumentType = typename Category::ArgumentType; - Token() {} + BasicDisabledToken() {} - ~Token() {} + BasicDisabledToken(const BasicDisabledToken &) = default; + BasicDisabledToken &operator=(const BasicDisabledToken &) = default; + BasicDisabledToken(BasicDisabledToken &&other) noexcept = default; + BasicDisabledToken &operator=(BasicDisabledToken &&other) noexcept = default; - constexpr Token(const Token &) = delete; - constexpr Token &operator=(const Token &) = delete; - - constexpr Token(Token &&other) noexcept = default; - - constexpr Token &operator=(Token &&other) noexcept = default; + ~BasicDisabledToken() {} constexpr explicit operator bool() const { return false; } static constexpr bool isActive() { return false; } - - Token begin(ArgumentType) { return Token{}; } - - void tick(ArgumentType) {} - - void end() {} }; -template -class Category; +class BasicEnabledToken +{ +public: + using IsActive = std::true_type; + + BasicEnabledToken() {} + + BasicEnabledToken(const BasicEnabledToken &) = default; + BasicEnabledToken &operator=(const BasicEnabledToken &) = default; + BasicEnabledToken(BasicEnabledToken &&other) noexcept = default; + BasicEnabledToken &operator=(BasicEnabledToken &&other) noexcept = default; + ~BasicEnabledToken() {} + + constexpr explicit operator bool() const { return false; } + + static constexpr bool isActive() { return false; } +}; + +template +class ObjectToken : public BasicDisabledToken +{ +public: + using ArgumentType = typename Category::ArgumentType; + + ObjectToken() = default; + ObjectToken(const ObjectToken &) = delete; + ObjectToken &operator=(const ObjectToken &) = delete; + ObjectToken(ObjectToken &&other) noexcept = default; + ObjectToken &operator=(ObjectToken &&other) noexcept = default; + ~ObjectToken() = default; + + template + void change(ArgumentType, Arguments &&...) + {} +}; template -class Token +class ObjectToken : public BasicEnabledToken { - Token(std::string_view name, std::size_t id, Category &category) + ObjectToken(std::string_view name, std::size_t id, Category &category) : m_name{name} , m_id{id} , m_category{&category} {} public: - using IsActive = std::true_type; using StringType = typename Category::StringType; using ArgumentType = typename Category::ArgumentType; friend Category; - Token() = default; + ObjectToken() = default; - Token(const Token &) = delete; - Token &operator=(const Token &) = delete; + ObjectToken(const ObjectToken &other) = delete; - Token(Token &&other) noexcept - : m_name{other.m_name} + ObjectToken &operator=(const ObjectToken &other) = delete; + + ObjectToken(ObjectToken &&other) noexcept + : m_name{std::move(other.m_name)} , m_id{std::exchange(other.m_id, 0)} , m_category{std::exchange(other.m_category, nullptr)} {} - Token &operator=(Token &&other) noexcept + ObjectToken &operator=(ObjectToken &&other) noexcept { if (&other != this) { - m_name = other.m_name; + m_name = std::move(other.m_name); m_id = std::exchange(other.m_id, 0); m_category = std::exchange(other.m_category, nullptr); } @@ -329,30 +444,120 @@ public: return *this; } - ~Token() { end(); } - - constexpr explicit operator bool() const { return m_id; } - - static constexpr bool isActive() { return true; } - - Token begin(ArgumentType name) + ~ObjectToken() { if (m_id) - m_category->beginAsynchronous(m_id, name); + m_category->end('e', m_id, std::move(m_name)); - return Token{m_name, m_id, *m_category}; + m_id = 0; } - void tick(ArgumentType name) + template + void change(ArgumentType name, Arguments &&...arguments) { if (m_id) - m_category->tickAsynchronous(m_id, name); + m_category->tick('n', m_id, std::move(name), std::forward(arguments)...); } - void end() +private: + StringType m_name; + std::size_t m_id = 0; + Category *m_category = nullptr; +}; + +template +class AsynchronousToken : public BasicDisabledToken +{ +public: + using ArgumentType = typename Category::ArgumentType; + + AsynchronousToken() {} + + AsynchronousToken(const AsynchronousToken &) = delete; + AsynchronousToken &operator=(const AsynchronousToken &) = delete; + AsynchronousToken(AsynchronousToken &&other) noexcept = default; + AsynchronousToken &operator=(AsynchronousToken &&other) noexcept = default; + + ~AsynchronousToken() {} + + template + AsynchronousToken begin(ArgumentType, Arguments &&...) + { + return AsynchronousToken{}; + } + + template + void tick(Arguments &&...) + {} + + template + void end(Arguments &&...) + {} +}; + +template +class Category; + +template +class AsynchronousToken : public BasicEnabledToken +{ + AsynchronousToken(std::string_view name, std::size_t id, Category &category) + : m_name{name} + , m_id{id} + , m_category{&category} + {} + +public: + using StringType = typename Category::StringType; + using ArgumentType = typename Category::ArgumentType; + + friend Category; + + AsynchronousToken() = default; + + AsynchronousToken(const AsynchronousToken &) = delete; + AsynchronousToken &operator=(const AsynchronousToken &) = delete; + + AsynchronousToken(AsynchronousToken &&other) noexcept + : m_name{std::move(other.m_name)} + , m_id{std::exchange(other.m_id, 0)} + , m_category{std::exchange(other.m_category, nullptr)} + {} + + AsynchronousToken &operator=(AsynchronousToken &&other) noexcept + { + if (&other != this) { + m_name = std::move(other.m_name); + m_id = std::exchange(other.m_id, 0); + m_category = std::exchange(other.m_category, nullptr); + } + + return *this; + } + + ~AsynchronousToken() { end(); } + + template + AsynchronousToken begin(ArgumentType name, Arguments &&...arguments) { if (m_id) - m_category->endAsynchronous(m_id, m_name); + m_category->begin('b', m_id, std::move(name), std::forward(arguments)...); + + return AsynchronousToken{m_name, m_id, *m_category}; + } + + template + void tick(ArgumentType name, Arguments &&...arguments) + { + if (m_id) + m_category->tick('n', m_id, std::move(name), std::forward(arguments)...); + } + + template + void end(Arguments &&...arguments) + { + if (m_id) + m_category->end('e', m_id, std::move(m_name), std::forward(arguments)...); m_id = 0; } @@ -369,15 +574,25 @@ class Category public: using IsActive = std::false_type; using ArgumentType = typename TraceEvent::ArgumentType; - using TokenType = Token; + using ArgumentsStringType = typename TraceEvent::ArgumentsStringType; + using AsynchronousTokenType = AsynchronousToken; + using ObjectTokenType = ObjectToken; Category(ArgumentType, EventQueue &) {} Category(ArgumentType, EventQueue &) {} - TokenType beginAsynchronous(ArgumentType) { return {}; } + template + AsynchronousTokenType beginAsynchronous(ArgumentType, Arguments &&...) + { + return {}; + } - void tickAsynchronous(ArgumentType) {} + template + ObjectTokenType beginObject(ArgumentType, Arguments &&...) + { + return {}; + } static constexpr bool isActive() { return false; } }; @@ -388,10 +603,13 @@ class Category public: using IsActive = std::true_type; using ArgumentType = typename TraceEvent::ArgumentType; + using ArgumentsStringType = typename TraceEvent::ArgumentsStringType; using StringType = typename TraceEvent::StringType; - using TokenType = Token; + using AsynchronousTokenType = AsynchronousToken; + using ObjectTokenType = ObjectToken; - friend TokenType; + friend AsynchronousTokenType; + friend ObjectTokenType; template Category(ArgumentType name, EventQueue &queue) @@ -403,11 +621,33 @@ public: idCounter = globalIdCounter += 1ULL << 32; } - TokenType beginAsynchronous(ArgumentType traceName) + template + AsynchronousTokenType beginAsynchronous(ArgumentType traceName, Arguments &&...arguments) { std::size_t id = ++idCounter; - beginAsynchronous(id, traceName); + begin('b', id, std::move(traceName), std::forward(arguments)...); + + return {traceName, id, *this}; + } + + ObjectTokenType beginObject(ArgumentType traceName) + { + std::size_t id = ++idCounter; + + if (id) + begin('b', id, std::move(traceName)); + + return {traceName, id, *this}; + } + + template + ObjectTokenType beginObject(ArgumentType traceName, Arguments &&...arguments) + { + std::size_t id = ++idCounter; + + if (id) + begin('b', id, std::move(traceName), std::forward(arguments)...); return {traceName, id, *this}; } @@ -419,36 +659,65 @@ public: static constexpr bool isActive() { return true; } private: - void beginAsynchronous(std::size_t id, StringType traceName) + static void appendArguments(TraceEvent &) {} + + static void appendArguments(TraceEvent &traceEvent, TracerLiteral arguments) + { + traceEvent.arguments = arguments; + } + + template + static void appendArguments(LocalTraceEvent &traceEvent, Arguments &&...arguments) + { + using String = typename LocalTraceEvent::ArgumentsStringType; + static_assert( + !std::is_same_v, + R"(The arguments type of the tracing event queue is a string view. You can only provide trace token arguments as TracerLiteral (""_t).)"); + + traceEvent.arguments = Internal::toArguments(std::forward(arguments)...); + } + + template + void begin(char type, std::size_t id, StringType traceName, Arguments &&...arguments) { auto &traceEvent = getTraceEvent(m_eventQueue); + traceEvent.name = std::move(traceName); traceEvent.category = m_name; - traceEvent.type = 'b'; + traceEvent.type = type; traceEvent.id = id; + appendArguments(traceEvent, std::forward(arguments)...); traceEvent.time = Clock::now(); } - void tickAsynchronous(std::size_t id, StringType traceName) + template + void tick(char type, std::size_t id, StringType traceName, Arguments &&...arguments) { auto time = Clock::now(); + auto &traceEvent = getTraceEvent(m_eventQueue); + traceEvent.name = std::move(traceName); traceEvent.category = m_name; traceEvent.time = time; - traceEvent.type = 'n'; + traceEvent.type = type; traceEvent.id = id; + appendArguments(traceEvent, std::forward(arguments)...); } - void endAsynchronous(std::size_t id, StringType traceName) + template + void end(char type, std::size_t id, StringType traceName, Arguments &&...arguments) { auto time = Clock::now(); + auto &traceEvent = getTraceEvent(m_eventQueue); + traceEvent.name = std::move(traceName); traceEvent.category = m_name; traceEvent.time = time; - traceEvent.type = 'e'; + traceEvent.type = type; traceEvent.id = id; + appendArguments(traceEvent, std::forward(arguments)...); } private: @@ -462,6 +731,8 @@ template using StringViewCategory = Category; template using StringCategory = Category; +template +using StringViewWithStringArgumentsCategory = Category; template class Tracer @@ -473,6 +744,10 @@ public: constexpr Tracer(ArgumentType, Category &) {} + Tracer(const Tracer &) = delete; + Tracer &operator=(const Tracer &) = delete; + Tracer(Tracer &&other) noexcept = default; + Tracer &operator=(Tracer &&other) noexcept = delete; ~Tracer() {} }; @@ -500,6 +775,10 @@ public: } } + Tracer(const Tracer &) = delete; + Tracer &operator=(const Tracer &) = delete; + Tracer(Tracer &&other) noexcept = default; + Tracer &operator=(Tracer &&other) noexcept = delete; ~Tracer() { if constexpr (isTracerActive()) { @@ -529,7 +808,7 @@ class Tracer> public: Tracer(std::string name, StringViewCategory &category, std::string arguments) : m_name{std::move(name)} - , m_arguments{arguments} + , m_arguments{std::move(arguments)} , m_category{category} { if constexpr (isTracerActive()) { diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index e92dc29942f..275e75a8cff 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -23,6 +23,11 @@ env_with_default("QTC_ENABLE_IMAGE_CACHE_TRACING" ENV_QTC_ENABLE_IMAGE_CACHE_TRA option(ENABLE_IMAGE_CACHE_TRACING "Enable image cache tracing" ${ENV_QTC_ENABLE_IMAGE_CACHE_TRACING}) add_feature_info("Image cache tracing" ${ENABLE_IMAGE_CACHE_TRACING} "") +env_with_default("QTC_ENABLE_MODEL_TRACING" ENV_QTC_ENABLE_MODEL_TRACING OFF) +option(ENABLE_MODEL_TRACING "Enable model tracing" ${ENV_QTC_ENABLE_MODEL_TRACING}) +add_feature_info("Model tracing" ${ENABLE_MODEL_TRACING} "") + + add_qtc_library(QmlDesignerUtils STATIC DEPENDS @@ -96,8 +101,10 @@ extend_qtc_library(QmlDesignerCore CONDITION TARGET Nanotrace DEPENDS Nanotrace DEFINES + ENABLE_QMLDESIGNER_TRACING $<$:ENABLE_PROJECT_STORAGE_TRACING> $<$:ENABLE_IMAGE_CACHE_TRACING> + $<$:ENABLE_MODEL_TRACING> ) extend_qtc_library(QmlDesignerCore @@ -201,6 +208,12 @@ extend_qtc_library(QmlDesignerCore timestampproviderinterface.h ) +extend_qtc_library(QmlDesignerCore + SOURCES_PREFIX ${CMAKE_CURRENT_LIST_DIR}/designercore/tracing + SOURCES + qmldesignertracing.cpp qmldesignertracing.h +) + extend_qtc_library(QmlDesignerCore SOURCES_PREFIX ${CMAKE_CURRENT_LIST_DIR}/designercore/include SOURCES diff --git a/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagecache.cpp b/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagecache.cpp index 19fba2e15c5..37e3e05ac0f 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagecache.cpp +++ b/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagecache.cpp @@ -7,6 +7,8 @@ #include "imagecachestorage.h" #include "timestampprovider.h" +#include + #include #include @@ -17,15 +19,8 @@ using namespace NanotraceHR::Literals; namespace ImageCache { namespace { -using TraceFile = NanotraceHR::TraceFile; -TraceFile traceFile{"qml_designer.json"}; - -thread_local auto eventQueueData = NanotraceHR::makeEventQueueData( - traceFile); -thread_local NanotraceHR::EventQueue eventQueue = eventQueueData.createEventQueue(); - -thread_local Category category_{"image cache"_t, eventQueue}; +thread_local Category category_{"image cache"_t, QmlDesigner::Tracing::eventQueue()}; } // namespace Category &category() diff --git a/src/plugins/qmldesigner/designercore/imagecache/imagecachegenerator.h b/src/plugins/qmldesigner/designercore/imagecache/imagecachegenerator.h index e89e25bcb29..3b28c117fdb 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/imagecachegenerator.h +++ b/src/plugins/qmldesigner/designercore/imagecache/imagecachegenerator.h @@ -68,7 +68,7 @@ private: std::vector captureCallbacks; std::vector abortCallbacks; Sqlite::TimeStamp timeStamp; - ImageCache::TraceToken traceToken; + NO_UNIQUE_ADDRESS ImageCache::TraceToken traceToken; }; void startGeneration(); diff --git a/src/plugins/qmldesigner/designercore/include/asynchronousimagecache.h b/src/plugins/qmldesigner/designercore/include/asynchronousimagecache.h index b1948f566bd..c3fc7bcd153 100644 --- a/src/plugins/qmldesigner/designercore/include/asynchronousimagecache.h +++ b/src/plugins/qmldesigner/designercore/include/asynchronousimagecache.h @@ -78,7 +78,7 @@ private: ImageCache::AbortCallback abortCallback; ImageCache::AuxiliaryData auxiliaryData; RequestType requestType = RequestType::Image; - ImageCache::TraceToken traceToken; + NO_UNIQUE_ADDRESS ImageCache::TraceToken traceToken; }; static void request(Utils::SmallStringView name, diff --git a/src/plugins/qmldesigner/designercore/include/imagecacheauxiliarydata.h b/src/plugins/qmldesigner/designercore/include/imagecacheauxiliarydata.h index b27816f0548..96667624ad6 100644 --- a/src/plugins/qmldesigner/designercore/include/imagecacheauxiliarydata.h +++ b/src/plugins/qmldesigner/designercore/include/imagecacheauxiliarydata.h @@ -27,7 +27,7 @@ constexpr bool tracingIsEnabled() } using Category = NanotraceHR::StringViewCategory; -using TraceToken = Category::TokenType; +using TraceToken = Category::AsynchronousTokenType; Category &category(); class FontCollectorSizeAuxiliaryData diff --git a/src/plugins/qmldesigner/designercore/model/internalnode_p.h b/src/plugins/qmldesigner/designercore/model/internalnode_p.h index af5b43eec28..7d32b498a09 100644 --- a/src/plugins/qmldesigner/designercore/model/internalnode_p.h +++ b/src/plugins/qmldesigner/designercore/model/internalnode_p.h @@ -13,6 +13,7 @@ #include #include +#include #include #include @@ -30,6 +31,7 @@ namespace QmlDesigner { namespace Internal { +using namespace NanotraceHR::Literals; class InternalProperty; class InternalNode; @@ -221,6 +223,8 @@ public: ModuleId moduleId; ImportedTypeNameId importedTypeNameId; TypeId typeId; + NO_UNIQUE_ADDRESS ModelTracing::ObjectTraceToken traceToken = ModelTracing::category().beginObject( + "InternalNode"_t); private: AuxiliaryDatas m_auxiliaryDatas; diff --git a/src/plugins/qmldesigner/designercore/model/internalproperty.h b/src/plugins/qmldesigner/designercore/model/internalproperty.h index 66d0bcaa881..2dfcdd616bb 100644 --- a/src/plugins/qmldesigner/designercore/model/internalproperty.h +++ b/src/plugins/qmldesigner/designercore/model/internalproperty.h @@ -5,6 +5,8 @@ #include "qmldesignercorelib_global.h" +#include + #include #include @@ -14,6 +16,8 @@ namespace QmlDesigner { namespace Internal { +using namespace NanotraceHR::Literals; + class InternalBindingProperty; class InternalSignalHandlerProperty; class InternalSignalDeclarationProperty; @@ -185,6 +189,8 @@ private: TypeName m_dynamicType; std::weak_ptr m_propertyOwner; PropertyType m_propertyType = PropertyType::None; + NO_UNIQUE_ADDRESS ModelTracing::ObjectTraceToken traceToken = ModelTracing::category().beginObject( + "InternalProperty"_t, std::forward_as_tuple("name", m_name)); }; } // namespace Internal diff --git a/src/plugins/qmldesigner/designercore/model/model.cpp b/src/plugins/qmldesigner/designercore/model/model.cpp index b85a0d8b403..67001855757 100644 --- a/src/plugins/qmldesigner/designercore/model/model.cpp +++ b/src/plugins/qmldesigner/designercore/model/model.cpp @@ -46,6 +46,8 @@ #include #include +#include + /*! \defgroup CoreModel */ @@ -473,9 +475,12 @@ void ModelPrivate::setMetaInfo(const MetaInfo &metaInfo) void ModelPrivate::changeNodeId(const InternalNodePointer &node, const QString &id) { + using namespace NanotraceHR::Literals; + const QString oldId = node->id; node->id = id; + node->traceToken.change("id"_t, std::forward_as_tuple("id", id)); if (!oldId.isEmpty()) m_idNodeHash.remove(oldId); if (!id.isEmpty()) diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.cpp b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.cpp index 29f732dea60..c3b659f6453 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.cpp +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.cpp @@ -3,20 +3,15 @@ #include "projectstorage.h" +#include + #include namespace QmlDesigner { -namespace { -NanotraceHR::TraceFile traceFile{"projectstorage.json"}; - -thread_local auto eventQueueData = NanotraceHR::makeEventQueueData( - traceFile); -thread_local NanotraceHR::EventQueue eventQueue = eventQueueData.createEventQueue(); -} // namespace - thread_local NanotraceHR::StringViewCategory projectStorageCategory{ - "project storage"_t, eventQueue}; + "project storage"_t, Tracing::eventQueue()}; + } // namespace QmlDesigner template class QmlDesigner::ProjectStorage; diff --git a/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.cpp b/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.cpp new file mode 100644 index 00000000000..5912914f6e9 --- /dev/null +++ b/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.cpp @@ -0,0 +1,45 @@ +// 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 "qmldesignertracing.h" + +namespace QmlDesigner { +namespace Tracing { + +namespace { +using TraceFile = NanotraceHR::TraceFile; + +TraceFile traceFile{"qml_designer.json"}; + +thread_local auto strinViewEventQueueData = NanotraceHR::makeEventQueueData(traceFile); +thread_local NanotraceHR::EventQueue stringViewEventQueue_ = strinViewEventQueueData.createEventQueue(); + +thread_local auto stringViewWithStringArgumentsEventQueueData = NanotraceHR:: + makeEventQueueData(traceFile); +thread_local NanotraceHR::EventQueue stringViewEventWithStringArgumentsQueue_ = stringViewWithStringArgumentsEventQueueData + .createEventQueue(); +} // namespace + +EventQueue &eventQueue() +{ + return stringViewEventQueue_; +} + +} // namespace Tracing + +namespace ModelTracing { +namespace { +using namespace NanotraceHR::Literals; + +thread_local Category category_{"model"_t, Tracing::stringViewEventWithStringArgumentsQueue_}; + +} // namespace + +Category &category() +{ + return category_; +} + +} // namespace ModelTracing +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.h b/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.h new file mode 100644 index 00000000000..4a9c741cc4c --- /dev/null +++ b/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.h @@ -0,0 +1,47 @@ +// 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 + +#include + +#pragma once + +namespace QmlDesigner { +namespace Tracing { +#ifdef ENABLE_QMLDESIGNER_TRACING +using Enabled = std::true_type; +#else +using Enabled = std::false_type; +#endif + +constexpr bool tracingIsEnabled() +{ +#ifdef ENABLE_QMLDESIGNER_TRACING + return NanotraceHR::isTracerActive(); +#else + return false; +#endif +} + +using EventQueue = NanotraceHR::EventQueue; +QMLDESIGNERCORE_EXPORT EventQueue &eventQueue(); + +} // namespace Tracing + +namespace ModelTracing { +constexpr bool tracingIsEnabled() +{ +#ifdef ENABLE_MODEL_TRACING + return NanotraceHR::isTracerActive(); +#else + return false; +#endif +} + +using Category = NanotraceHR::StringViewWithStringArgumentsCategory; +using ObjectTraceToken = Category::ObjectTokenType; +QMLDESIGNERCORE_EXPORT Category &category(); + +} // namespace ModelTracing +} // namespace QmlDesigner diff --git a/tests/unit/tests/testdesignercore/CMakeLists.txt b/tests/unit/tests/testdesignercore/CMakeLists.txt index df91b656eb5..0bdf3452d6a 100644 --- a/tests/unit/tests/testdesignercore/CMakeLists.txt +++ b/tests/unit/tests/testdesignercore/CMakeLists.txt @@ -144,6 +144,7 @@ add_qtc_library(TestDesignerCore OBJECT projectstorage/typeannotationreader.h projectstorage/qmldocumentparserinterface.h projectstorage/qmltypesparserinterface.h + tracing/qmldesignertracing.cpp tracing/qmldesignertracing.h rewritertransaction.cpp rewritertransaction.h ) From 120f587d58b6045438f0f95764b000350e799206 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Sun, 15 Oct 2023 14:58:04 +0200 Subject: [PATCH 052/242] Nanotrace: Change event queue allocation The event queue data will be not filled with zero if it has internal linkage. Change-Id: Icb7798ac7ad8cc4c4f486b49e9d94f08625b818a Reviewed-by: Tim Jenssen Reviewed-by: Reviewed-by: Qt CI Patch Build Bot --- src/libs/nanotrace/nanotracehr.cpp | 23 +++--- src/libs/nanotrace/nanotracehr.h | 75 +++++++------------ src/libs/sqlite/sqlitebasestatement.cpp | 4 +- .../tracing/qmldesignertracing.cpp | 8 +- .../designercore/tracing/qmldesignertracing.h | 7 +- 5 files changed, 48 insertions(+), 69 deletions(-) diff --git a/src/libs/nanotrace/nanotracehr.cpp b/src/libs/nanotrace/nanotracehr.cpp index d97aa889391..364bca6ecc7 100644 --- a/src/libs/nanotrace/nanotracehr.cpp +++ b/src/libs/nanotrace/nanotracehr.cpp @@ -151,20 +151,21 @@ template NANOTRACE_EXPORT void flushInThread( EnabledEventQueue &eventQueue); namespace { -EnabledTraceFile globalTraceFile{"global.json"}; -thread_local auto globalEventQueueData = makeEventQueueData(globalTraceFile); +TraceFile globalTraceFile{"global.json"}; +thread_local EventQueueData globalEventQueueData{ + globalTraceFile}; thread_local EventQueue s_globalEventQueue = globalEventQueueData.createEventQueue(); } // namespace -EnabledEventQueue &globalEventQueue() +EventQueue &globalEventQueue() { return s_globalEventQueue; } template -EventQueue::EventQueue(EnabledTraceFile *file, - TraceEventsSpan eventsOne, - TraceEventsSpan eventsTwo) +EventQueue::EventQueue(EnabledTraceFile *file, + TraceEventsSpan eventsOne, + TraceEventsSpan eventsTwo) : file{file} , eventsOne{eventsOne} , eventsTwo{eventsTwo} @@ -183,7 +184,7 @@ EventQueue::EventQueue(EnabledTraceFile *file, } template -EventQueue::~EventQueue() +EventQueue::~EventQueue() { flush(); if (connection) @@ -191,7 +192,7 @@ EventQueue::~EventQueue() } template -void EventQueue::flush() +void EventQueue::flush() { std::lock_guard lock{mutex}; if (isEnabled == IsEnabled::Yes && eventsIndex > 0) { @@ -200,8 +201,8 @@ void EventQueue::flush() } } -template class EventQueue; -template class EventQueue; -template class EventQueue; +template class EventQueue; +template class EventQueue; +template class EventQueue; } // namespace NanotraceHR diff --git a/src/libs/nanotrace/nanotracehr.h b/src/libs/nanotrace/nanotracehr.h index cfe006cd6ba..f54058c2652 100644 --- a/src/libs/nanotrace/nanotracehr.h +++ b/src/libs/nanotrace/nanotracehr.h @@ -172,11 +172,11 @@ using StringTraceEvent = TraceEvent; enum class IsEnabled { No, Yes }; -template +template class EventQueue; template -using EnabledEventQueue = EventQueue; +using EnabledEventQueue = EventQueue; template void flushEvents(const Utils::span events, @@ -241,7 +241,7 @@ public: std::ofstream out; }; -template +template class EventQueue { public: @@ -249,7 +249,7 @@ public: }; template -class EventQueue +class EventQueue { using TraceEventsSpan = Utils::span; @@ -277,21 +277,23 @@ public: std::mutex mutex; }; -extern template class NANOTRACE_EXPORT EventQueue; -extern template class NANOTRACE_EXPORT EventQueue; -extern template class NANOTRACE_EXPORT EventQueue; +extern template class NANOTRACE_EXPORT EventQueue; +extern template class NANOTRACE_EXPORT EventQueue; +extern template class NANOTRACE_EXPORT EventQueue; -template +template class EventQueueData { public: - using IsActive = Enabled; + using IsActive = std::true_type; EventQueueData(TraceFile &) {} + + EventQueue createEventQueue() { return {}; } }; template -class EventQueueData +class EventQueueData { using TraceEvents = std::array; @@ -302,45 +304,14 @@ public: : file{file} {} + EventQueue createEventQueue() { return {&file, eventsOne, eventsTwo}; } + EnabledTraceFile &file; TraceEvents eventsOne; TraceEvents eventsTwo; }; -template -struct EventQueueDataPointer -{ - EventQueue createEventQueue() const { return {}; } -}; - -template -struct EventQueueDataPointer -{ - EnabledEventQueue createEventQueue() const - { - if constexpr (isTracerActive()) { - return {&data->file, data->eventsOne, data->eventsTwo}; - } else { - return {}; - } - } - - std::unique_ptr> data; -}; - -template -EventQueueDataPointer makeEventQueueData( - TraceFile &file) -{ - if constexpr (isTracerActive() && std::is_same_v) { - return {std::make_unique>( - file)}; - } else { - return {}; - } -} - -NANOTRACE_EXPORT EnabledEventQueue &globalEventQueue(); +NANOTRACE_EXPORT EventQueue &globalEventQueue(); template TraceEvent &getTraceEvent(EnabledEventQueue &eventQueue) @@ -578,9 +549,9 @@ public: using AsynchronousTokenType = AsynchronousToken; using ObjectTokenType = ObjectToken; - Category(ArgumentType, EventQueue &) {} + Category(ArgumentType, EventQueue &) {} - Category(ArgumentType, EventQueue &) {} + Category(ArgumentType, EventQueue &) {} template AsynchronousTokenType beginAsynchronous(ArgumentType, Arguments &&...) @@ -852,6 +823,7 @@ private: template Tracer(TracerLiteral name, Category &category) -> Tracer; +#ifdef NANOTRACE_ENABLED class GlobalTracer { public: @@ -892,5 +864,16 @@ private: std::string m_category; std::string m_arguments; }; +#else +class GlobalTracer +{ +public: + GlobalTracer(std::string_view, std::string_view, std::string_view) {} + + GlobalTracer(std::string_view, std::string_view) {} + + ~GlobalTracer() {} +}; +#endif } // namespace NanotraceHR diff --git a/src/libs/sqlite/sqlitebasestatement.cpp b/src/libs/sqlite/sqlitebasestatement.cpp index 36247484d7e..afd7656bca9 100644 --- a/src/libs/sqlite/sqlitebasestatement.cpp +++ b/src/libs/sqlite/sqlitebasestatement.cpp @@ -31,8 +31,8 @@ using TraceFile = NanotraceHR::TraceFile; TraceFile traceFile{"sqlite.json"}; -thread_local auto eventQueueData = NanotraceHR::makeEventQueueData( - traceFile); +thread_local NanotraceHR::EventQueueData + eventQueueData(traceFile); thread_local NanotraceHR::EventQueue eventQueue = eventQueueData.createEventQueue(); thread_local NanotraceHR::StringViewCategory sqliteLowLevelCategory{ diff --git a/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.cpp b/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.cpp index 5912914f6e9..d49f5c430f7 100644 --- a/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.cpp +++ b/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.cpp @@ -11,12 +11,12 @@ using TraceFile = NanotraceHR::TraceFile; TraceFile traceFile{"qml_designer.json"}; -thread_local auto strinViewEventQueueData = NanotraceHR::makeEventQueueData(traceFile); +thread_local NanotraceHR::EventQueueData + strinViewEventQueueData(traceFile); thread_local NanotraceHR::EventQueue stringViewEventQueue_ = strinViewEventQueueData.createEventQueue(); -thread_local auto stringViewWithStringArgumentsEventQueueData = NanotraceHR:: - makeEventQueueData(traceFile); +thread_local NanotraceHR::EventQueueData + stringViewWithStringArgumentsEventQueueData(traceFile); thread_local NanotraceHR::EventQueue stringViewEventWithStringArgumentsQueue_ = stringViewWithStringArgumentsEventQueueData .createEventQueue(); } // namespace diff --git a/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.h b/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.h index 4a9c741cc4c..ef8fd436dbc 100644 --- a/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.h +++ b/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.h @@ -9,11 +9,6 @@ namespace QmlDesigner { namespace Tracing { -#ifdef ENABLE_QMLDESIGNER_TRACING -using Enabled = std::true_type; -#else -using Enabled = std::false_type; -#endif constexpr bool tracingIsEnabled() { @@ -24,7 +19,7 @@ constexpr bool tracingIsEnabled() #endif } -using EventQueue = NanotraceHR::EventQueue; +using EventQueue = NanotraceHR::EventQueue; QMLDESIGNERCORE_EXPORT EventQueue &eventQueue(); } // namespace Tracing From f7314133fe7e1f563f5c3beafd79b026e4c00a43 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Sun, 15 Oct 2023 16:45:36 +0200 Subject: [PATCH 053/242] Nanotrace: Remove the magic booleans and use an enumeration Hopefully it will make more clear what is disabled or enabled. Change-Id: Ibc9b2329f5b664ba346bafeb27c54cf082758449 Reviewed-by: Tim Jenssen --- src/libs/nanotrace/nanotracehr.cpp | 24 ++-- src/libs/nanotrace/nanotracehr.h | 117 +++++++++--------- src/libs/sqlite/sqlitebasestatement.cpp | 10 +- src/libs/sqlite/sqlitebasestatement.h | 11 +- .../include/imagecacheauxiliarydata.h | 6 +- .../projectstorage/projectstorage.cpp | 2 +- .../projectstorage/projectstorage.h | 8 +- .../tracing/qmldesignertracing.cpp | 6 +- .../designercore/tracing/qmldesignertracing.h | 16 +-- 9 files changed, 103 insertions(+), 97 deletions(-) diff --git a/src/libs/nanotrace/nanotracehr.cpp b/src/libs/nanotrace/nanotracehr.cpp index 364bca6ecc7..b513d4887f3 100644 --- a/src/libs/nanotrace/nanotracehr.cpp +++ b/src/libs/nanotrace/nanotracehr.cpp @@ -39,7 +39,7 @@ void printEvent(std::ostream &out, const TraceEvent &event, qint64 processId, st out << "}"; } -void writeMetaEvent(TraceFile *file, std::string_view key, std::string_view value) +void writeMetaEvent(TraceFile *file, std::string_view key, std::string_view value) { std::lock_guard lock{file->fileMutex}; auto &out = file->out; @@ -151,21 +151,21 @@ template NANOTRACE_EXPORT void flushInThread( EnabledEventQueue &eventQueue); namespace { -TraceFile globalTraceFile{"global.json"}; -thread_local EventQueueData globalEventQueueData{ +TraceFile globalTraceFile{"global.json"}; +thread_local EventQueueData globalEventQueueData{ globalTraceFile}; thread_local EventQueue s_globalEventQueue = globalEventQueueData.createEventQueue(); } // namespace -EventQueue &globalEventQueue() +EventQueue &globalEventQueue() { return s_globalEventQueue; } template -EventQueue::EventQueue(EnabledTraceFile *file, - TraceEventsSpan eventsOne, - TraceEventsSpan eventsTwo) +EventQueue::EventQueue(EnabledTraceFile *file, + TraceEventsSpan eventsOne, + TraceEventsSpan eventsTwo) : file{file} , eventsOne{eventsOne} , eventsTwo{eventsTwo} @@ -184,7 +184,7 @@ EventQueue::EventQueue(EnabledTraceFile *file, } template -EventQueue::~EventQueue() +EventQueue::~EventQueue() { flush(); if (connection) @@ -192,7 +192,7 @@ EventQueue::~EventQueue() } template -void EventQueue::flush() +void EventQueue::flush() { std::lock_guard lock{mutex}; if (isEnabled == IsEnabled::Yes && eventsIndex > 0) { @@ -201,8 +201,8 @@ void EventQueue::flush() } } -template class EventQueue; -template class EventQueue; -template class EventQueue; +template class EventQueue; +template class EventQueue; +template class EventQueue; } // namespace NanotraceHR diff --git a/src/libs/nanotrace/nanotracehr.h b/src/libs/nanotrace/nanotracehr.h index f54058c2652..56e79d196f2 100644 --- a/src/libs/nanotrace/nanotracehr.h +++ b/src/libs/nanotrace/nanotracehr.h @@ -31,12 +31,14 @@ static_assert(Clock::is_steady, "clock should be steady"); static_assert(std::is_same_v, "the steady clock should have nano second resolution"); -constexpr bool isTracerActive() +enum class Tracing { IsDisabled, IsEnabled }; + +constexpr Tracing tracingStatus() { #ifdef NANOTRACE_ENABLED - return true; + return Tracing::IsEnabled; #else - return false; + return Tracing::IsDisabled; #endif } @@ -94,7 +96,7 @@ void toArgument(String &text, Argument &&argument) template String toArguments(Arguments &&...arguments) { - if constexpr (isTracerActive()) { + if constexpr (tracingStatus() == Tracing::IsEnabled) { String text; constexpr auto argumentCount = sizeof...(Arguments); text.reserve(sizeof...(Arguments) * 20); @@ -172,11 +174,11 @@ using StringTraceEvent = TraceEvent; enum class IsEnabled { No, Yes }; -template +template class EventQueue; template -using EnabledEventQueue = EventQueue; +using EnabledEventQueue = EventQueue; template void flushEvents(const Utils::span events, @@ -199,15 +201,15 @@ extern template NANOTRACE_EXPORT void flushInThread(EnabledEventQueue &eventQueue); -template +template class TraceFile; -using EnabledTraceFile = TraceFile; +using EnabledTraceFile = TraceFile; NANOTRACE_EXPORT void openFile(EnabledTraceFile &file); NANOTRACE_EXPORT void finalizeFile(EnabledTraceFile &file); -template +template class TraceFile { public: @@ -217,7 +219,7 @@ public: }; template<> -class TraceFile +class TraceFile { public: using IsActive = std::true_type; @@ -241,7 +243,7 @@ public: std::ofstream out; }; -template +template class EventQueue { public: @@ -249,7 +251,7 @@ public: }; template -class EventQueue +class EventQueue { using TraceEventsSpan = Utils::span; @@ -277,23 +279,23 @@ public: std::mutex mutex; }; -extern template class NANOTRACE_EXPORT EventQueue; -extern template class NANOTRACE_EXPORT EventQueue; -extern template class NANOTRACE_EXPORT EventQueue; +extern template class NANOTRACE_EXPORT EventQueue; +extern template class NANOTRACE_EXPORT EventQueue; +extern template class NANOTRACE_EXPORT EventQueue; -template +template class EventQueueData { public: using IsActive = std::true_type; - EventQueueData(TraceFile &) {} + EventQueueData(TraceFile &) {} - EventQueue createEventQueue() { return {}; } + EventQueue createEventQueue() { return {}; } }; template -class EventQueueData +class EventQueueData { using TraceEvents = std::array; @@ -304,14 +306,17 @@ public: : file{file} {} - EventQueue createEventQueue() { return {&file, eventsOne, eventsTwo}; } + EventQueue createEventQueue() + { + return {&file, eventsOne, eventsTwo}; + } EnabledTraceFile &file; TraceEvents eventsOne; TraceEvents eventsTwo; }; -NANOTRACE_EXPORT EventQueue &globalEventQueue(); +NANOTRACE_EXPORT EventQueue &globalEventQueue(); template TraceEvent &getTraceEvent(EnabledEventQueue &eventQueue) @@ -359,7 +364,7 @@ public: static constexpr bool isActive() { return false; } }; -template +template class ObjectToken : public BasicDisabledToken { public: @@ -378,7 +383,7 @@ public: }; template -class ObjectToken : public BasicEnabledToken +class ObjectToken : public BasicEnabledToken { ObjectToken(std::string_view name, std::size_t id, Category &category) : m_name{name} @@ -436,7 +441,7 @@ private: Category *m_category = nullptr; }; -template +template class AsynchronousToken : public BasicDisabledToken { public: @@ -466,11 +471,11 @@ public: {} }; -template +template class Category; template -class AsynchronousToken : public BasicEnabledToken +class AsynchronousToken : public BasicEnabledToken { AsynchronousToken(std::string_view name, std::size_t id, Category &category) : m_name{name} @@ -539,19 +544,19 @@ private: Category *m_category = nullptr; }; -template +template class Category { public: using IsActive = std::false_type; using ArgumentType = typename TraceEvent::ArgumentType; using ArgumentsStringType = typename TraceEvent::ArgumentsStringType; - using AsynchronousTokenType = AsynchronousToken; - using ObjectTokenType = ObjectToken; + using AsynchronousTokenType = AsynchronousToken; + using ObjectTokenType = ObjectToken; - Category(ArgumentType, EventQueue &) {} + Category(ArgumentType, EventQueue &) {} - Category(ArgumentType, EventQueue &) {} + Category(ArgumentType, EventQueue &) {} template AsynchronousTokenType beginAsynchronous(ArgumentType, Arguments &&...) @@ -569,15 +574,15 @@ public: }; template -class Category +class Category { public: using IsActive = std::true_type; using ArgumentType = typename TraceEvent::ArgumentType; using ArgumentsStringType = typename TraceEvent::ArgumentsStringType; using StringType = typename TraceEvent::StringType; - using AsynchronousTokenType = AsynchronousToken; - using ObjectTokenType = ObjectToken; + using AsynchronousTokenType = AsynchronousToken; + using ObjectTokenType = ObjectToken; friend AsynchronousTokenType; friend ObjectTokenType; @@ -698,12 +703,12 @@ private: std::size_t idCounter; }; -template -using StringViewCategory = Category; -template -using StringCategory = Category; -template -using StringViewWithStringArgumentsCategory = Category; +template +using StringViewCategory = Category; +template +using StringCategory = Category; +template +using StringViewWithStringArgumentsCategory = Category; template class Tracer @@ -723,24 +728,24 @@ public: }; template<> -class Tracer> +class Tracer> { public: - Tracer(TracerLiteral name, StringViewCategory &category, TracerLiteral arguments) + Tracer(TracerLiteral name, StringViewCategory &category, TracerLiteral arguments) : m_name{name} , m_arguments{arguments} , m_category{category} { - if constexpr (isTracerActive()) { + if constexpr (tracingStatus() == Tracing::IsEnabled) { if (category.eventQueue().isEnabled == IsEnabled::Yes) m_start = Clock::now(); } } - Tracer(TracerLiteral name, StringViewCategory &category) + Tracer(TracerLiteral name, StringViewCategory &category) : Tracer{name, category, ""_t} { - if constexpr (isTracerActive()) { + if constexpr (tracingStatus() == Tracing::IsEnabled) { if (category.eventQueue().isEnabled == IsEnabled::Yes) m_start = Clock::now(); } @@ -752,7 +757,7 @@ public: Tracer &operator=(Tracer &&other) noexcept = delete; ~Tracer() { - if constexpr (isTracerActive()) { + if constexpr (tracingStatus() == Tracing::IsEnabled) { if (m_category.eventQueue().isEnabled == IsEnabled::Yes) { auto duration = Clock::now() - m_start; auto &traceEvent = getTraceEvent(m_category.eventQueue()); @@ -770,28 +775,28 @@ private: TimePoint m_start; std::string_view m_name; std::string_view m_arguments; - StringViewCategory &m_category; + StringViewCategory &m_category; }; template<> -class Tracer> +class Tracer> { public: - Tracer(std::string name, StringViewCategory &category, std::string arguments) + Tracer(std::string name, StringViewCategory &category, std::string arguments) : m_name{std::move(name)} , m_arguments{std::move(arguments)} , m_category{category} { - if constexpr (isTracerActive()) { + if constexpr (tracingStatus() == Tracing::IsEnabled) { if (category.eventQueue().isEnabled == IsEnabled::Yes) m_start = Clock::now(); } } - Tracer(std::string name, StringViewCategory &category) + Tracer(std::string name, StringViewCategory &category) : Tracer{std::move(name), category, ""} { - if constexpr (isTracerActive()) { + if constexpr (tracingStatus() == Tracing::IsEnabled) { if (category.eventQueue().isEnabled == IsEnabled::Yes) m_start = Clock::now(); } @@ -799,7 +804,7 @@ public: ~Tracer() { - if constexpr (isTracerActive()) { + if constexpr (tracingStatus() == Tracing::IsEnabled) { if (m_category.eventQueue().isEnabled == IsEnabled::Yes) { auto duration = Clock::now() - m_start; auto &traceEvent = getTraceEvent(m_category.eventQueue()); @@ -817,7 +822,7 @@ private: TimePoint m_start; std::string m_name; std::string m_arguments; - StringViewCategory &m_category; + StringViewCategory &m_category; }; template @@ -832,7 +837,7 @@ public: , m_category{std::move(category)} , m_arguments{std::move(arguments)} { - if constexpr (isTracerActive()) { + if constexpr (tracingStatus() == Tracing::IsEnabled) { if (globalEventQueue().isEnabled == IsEnabled::Yes) m_start = Clock::now(); } @@ -844,7 +849,7 @@ public: ~GlobalTracer() { - if constexpr (isTracerActive()) { + if constexpr (tracingStatus() == Tracing::IsEnabled) { if (globalEventQueue().isEnabled == IsEnabled::Yes) { auto duration = Clock::now() - m_start; auto &traceEvent = getTraceEvent(globalEventQueue()); diff --git a/src/libs/sqlite/sqlitebasestatement.cpp b/src/libs/sqlite/sqlitebasestatement.cpp index afd7656bca9..efddc08e38c 100644 --- a/src/libs/sqlite/sqlitebasestatement.cpp +++ b/src/libs/sqlite/sqlitebasestatement.cpp @@ -27,22 +27,22 @@ extern "C" int sqlite3_carray_bind( namespace Sqlite { namespace { -using TraceFile = NanotraceHR::TraceFile; +using TraceFile = NanotraceHR::TraceFile; TraceFile traceFile{"sqlite.json"}; -thread_local NanotraceHR::EventQueueData +thread_local NanotraceHR::EventQueueData eventQueueData(traceFile); thread_local NanotraceHR::EventQueue eventQueue = eventQueueData.createEventQueue(); -thread_local NanotraceHR::StringViewCategory sqliteLowLevelCategory{ +thread_local NanotraceHR::StringViewCategory sqliteLowLevelCategory{ "sqlite low level"_t, eventQueue}; -thread_local NanotraceHR::StringViewCategory sqliteHighLevelCategory_{ +thread_local NanotraceHR::StringViewCategory sqliteHighLevelCategory_{ "sqlite high level"_t, eventQueue}; } // namespace -NanotraceHR::StringViewCategory &sqliteHighLevelCategory() +NanotraceHR::StringViewCategory &sqliteHighLevelCategory() { return sqliteHighLevelCategory_; } diff --git a/src/libs/sqlite/sqlitebasestatement.h b/src/libs/sqlite/sqlitebasestatement.h index c507b0b9d3b..d2306c67a5e 100644 --- a/src/libs/sqlite/sqlitebasestatement.h +++ b/src/libs/sqlite/sqlitebasestatement.h @@ -44,16 +44,16 @@ constexpr static std::underlying_type_t to_underlying(Enumeration e return static_cast>(enumeration); } -constexpr bool sqliteTracingIsEnabled() +constexpr NanotraceHR::Tracing sqliteTracingStatus() { #ifdef ENABLE_SQLITE_TRACING - return NanotraceHR::isTracerActive(); + return NanotraceHR::tracingStatus(); #else - return false; + return NanotraceHR::Tracing::IsDisabled; #endif } -SQLITE_EXPORT NanotraceHR::StringViewCategory &sqliteHighLevelCategory(); +SQLITE_EXPORT NanotraceHR::StringViewCategory &sqliteHighLevelCategory(); class SQLITE_EXPORT BaseStatement { @@ -401,7 +401,8 @@ public: const_iterator end() const & { return iterator{m_statement, false}; } private: - NanotraceHR::Tracer> tracer{ + using TracerCategory = std::decay_t; + NanotraceHR::Tracer tracer{ "range"_t, sqliteHighLevelCategory()}; StatementImplementation &m_statement; }; diff --git a/src/plugins/qmldesigner/designercore/include/imagecacheauxiliarydata.h b/src/plugins/qmldesigner/designercore/include/imagecacheauxiliarydata.h index 96667624ad6..5b7fa2624de 100644 --- a/src/plugins/qmldesigner/designercore/include/imagecacheauxiliarydata.h +++ b/src/plugins/qmldesigner/designercore/include/imagecacheauxiliarydata.h @@ -17,16 +17,16 @@ namespace QmlDesigner { namespace ImageCache { -constexpr bool tracingIsEnabled() +constexpr NanotraceHR::Tracing tracingStatus() { #ifdef ENABLE_IMAGE_CACHE_TRACING return NanotraceHR::isTracerActive(); #else - return false; + return NanotraceHR::Tracing::IsDisabled; #endif } -using Category = NanotraceHR::StringViewCategory; +using Category = NanotraceHR::StringViewCategory; using TraceToken = Category::AsynchronousTokenType; Category &category(); diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.cpp b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.cpp index c3b659f6453..2a6bdd01cbc 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.cpp +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.cpp @@ -9,7 +9,7 @@ namespace QmlDesigner { -thread_local NanotraceHR::StringViewCategory projectStorageCategory{ +thread_local NanotraceHR::StringViewCategory projectStorageCategory{ "project storage"_t, Tracing::eventQueue()}; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h index 9070cfc2748..47ca8b7c9a0 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h @@ -28,16 +28,16 @@ namespace QmlDesigner { using namespace NanotraceHR::Literals; -constexpr bool projectStorageTracingIsEnabled() +constexpr NanotraceHR::Tracing projectStorageTracingStatus() { #ifdef ENABLE_PROJECT_STORAGE_TRACING - return NanotraceHR::isTracerActive(); + return NanotraceHR::tracingStatus(); #else - return false; + return NanotraceHR::Tracing::IsDisabled; #endif } -extern thread_local NanotraceHR::StringViewCategory projectStorageCategory; +extern thread_local NanotraceHR::StringViewCategory projectStorageCategory; template class ProjectStorage final : public ProjectStorageInterface diff --git a/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.cpp b/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.cpp index d49f5c430f7..f780e813ddd 100644 --- a/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.cpp +++ b/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.cpp @@ -7,15 +7,15 @@ namespace QmlDesigner { namespace Tracing { namespace { -using TraceFile = NanotraceHR::TraceFile; +using TraceFile = NanotraceHR::TraceFile; TraceFile traceFile{"qml_designer.json"}; -thread_local NanotraceHR::EventQueueData +thread_local NanotraceHR::EventQueueData strinViewEventQueueData(traceFile); thread_local NanotraceHR::EventQueue stringViewEventQueue_ = strinViewEventQueueData.createEventQueue(); -thread_local NanotraceHR::EventQueueData +thread_local NanotraceHR::EventQueueData stringViewWithStringArgumentsEventQueueData(traceFile); thread_local NanotraceHR::EventQueue stringViewEventWithStringArgumentsQueue_ = stringViewWithStringArgumentsEventQueueData .createEventQueue(); diff --git a/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.h b/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.h index ef8fd436dbc..81e7e6f17c4 100644 --- a/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.h +++ b/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.h @@ -10,31 +10,31 @@ namespace QmlDesigner { namespace Tracing { -constexpr bool tracingIsEnabled() +constexpr NanotraceHR::Tracing tracingStatus() { #ifdef ENABLE_QMLDESIGNER_TRACING - return NanotraceHR::isTracerActive(); + return NanotraceHR::tracingStatus(); #else - return false; + return NanotraceHR::Tracing::IsDisabled; #endif } -using EventQueue = NanotraceHR::EventQueue; +using EventQueue = NanotraceHR::EventQueue; QMLDESIGNERCORE_EXPORT EventQueue &eventQueue(); } // namespace Tracing namespace ModelTracing { -constexpr bool tracingIsEnabled() +constexpr NanotraceHR::Tracing tracingStatus() { #ifdef ENABLE_MODEL_TRACING - return NanotraceHR::isTracerActive(); + return NanotraceHR::tracingStatus(); #else - return false; + return NanotraceHR::Tracing::IsDisabled; #endif } -using Category = NanotraceHR::StringViewWithStringArgumentsCategory; +using Category = NanotraceHR::StringViewWithStringArgumentsCategory; using ObjectTraceToken = Category::ObjectTokenType; QMLDESIGNERCORE_EXPORT Category &category(); From 707585da44f00928cca4a378e375acdd23e4f9b4 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Sun, 15 Oct 2023 20:42:20 +0200 Subject: [PATCH 054/242] Nanotrace: Print only ids for event types which have an id Change-Id: Idf41483b09a54f93f2d84f3ad9d5712b218e373c Reviewed-by: Tim Jenssen Reviewed-by: --- src/libs/nanotrace/nanotracehr.cpp | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/libs/nanotrace/nanotracehr.cpp b/src/libs/nanotrace/nanotracehr.cpp index b513d4887f3..5e04295920c 100644 --- a/src/libs/nanotrace/nanotracehr.cpp +++ b/src/libs/nanotrace/nanotracehr.cpp @@ -19,6 +19,18 @@ namespace NanotraceHR { namespace { +bool hasId(char phase) +{ + switch (phase) { + case 'b': + case 'n': + case 'e': + return true; + } + + return false; +} + template void printEvent(std::ostream &out, const TraceEvent &event, qint64 processId, std::thread::id threadId) { @@ -30,7 +42,7 @@ void printEvent(std::ostream &out, const TraceEvent &event, qint64 processId, st if (event.type == 'X') out << R"(,"dur":)" << static_cast(event.duration.count()) / 1000; - if (event.id != 0) + if (hasId(event.type)) out << R"(,"id":")" << event.id << R"(")"; if (event.arguments.size()) From b6f14a54214ff7ad7d61ce64576d27dc97219183 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Sun, 15 Oct 2023 23:42:08 +0200 Subject: [PATCH 055/242] Nanotracer: Simplifying tracer It is not only simpler but more flexible too. Change-Id: I9b4b0e522865270bcb84ea5d343d7f8ada708d3b Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Tim Jenssen --- src/libs/nanotrace/nanotracehr.h | 224 ++++++++++++------------------- 1 file changed, 86 insertions(+), 138 deletions(-) diff --git a/src/libs/nanotrace/nanotracehr.h b/src/libs/nanotrace/nanotracehr.h index 56e79d196f2..620c94e49be 100644 --- a/src/libs/nanotrace/nanotracehr.h +++ b/src/libs/nanotrace/nanotracehr.h @@ -50,6 +50,31 @@ constexpr Tracing tracingStatus() # define NO_UNIQUE_ADDRESS #endif +namespace Literals { +struct TracerLiteral +{ + friend constexpr TracerLiteral operator""_t(const char *text, size_t size); + + constexpr operator std::string_view() const { return text; } + + operator std::string() const { return std::string{text}; } + +private: + constexpr TracerLiteral(std::string_view text) + : text{text} + {} + + std::string_view text; +}; + +constexpr TracerLiteral operator""_t(const char *text, size_t size) +{ + return {std::string_view{text, size}}; +} +} // namespace Literals + +using namespace Literals; + namespace Internal { inline std::string_view covertToString(std::string_view text) { @@ -99,7 +124,7 @@ String toArguments(Arguments &&...arguments) if constexpr (tracingStatus() == Tracing::IsEnabled) { String text; constexpr auto argumentCount = sizeof...(Arguments); - text.reserve(sizeof...(Arguments) * 20); + text.reserve(sizeof...(Arguments) * 30); text.append("{"); (toArgument(text, arguments), ...); if (argumentCount) @@ -118,32 +143,29 @@ inline std::string_view toArguments(std::string_view arguments) return arguments; } -} // namespace Internal - -namespace Literals { -struct TracerLiteral +template +void appendArguments(String &eventArguments) { - friend constexpr TracerLiteral operator""_t(const char *text, size_t size); - - constexpr operator std::string_view() const { return text; } - - operator std::string() const { return std::string{text}; } - -private: - constexpr TracerLiteral(std::string_view text) - : text{text} - {} - - std::string_view text; -}; - -constexpr TracerLiteral operator""_t(const char *text, size_t size) -{ - return {std::string_view{text, size}}; + eventArguments = {}; } -} // namespace Literals -using namespace Literals; +template +void appendArguments(String &eventArguments, TracerLiteral arguments) +{ + eventArguments = arguments; +} + +template +[[maybe_unused]] void appendArguments(String &eventArguments, Arguments &&...arguments) +{ + static_assert( + !std::is_same_v, + R"(The arguments type of the tracing event queue is a string view. You can only provide trace token arguments as TracerLiteral (""_t).)"); + + eventArguments = Internal::toArguments(std::forward(arguments)...); +} + +} // namespace Internal template struct TraceEvent @@ -635,24 +657,6 @@ public: static constexpr bool isActive() { return true; } private: - static void appendArguments(TraceEvent &) {} - - static void appendArguments(TraceEvent &traceEvent, TracerLiteral arguments) - { - traceEvent.arguments = arguments; - } - - template - static void appendArguments(LocalTraceEvent &traceEvent, Arguments &&...arguments) - { - using String = typename LocalTraceEvent::ArgumentsStringType; - static_assert( - !std::is_same_v, - R"(The arguments type of the tracing event queue is a string view. You can only provide trace token arguments as TracerLiteral (""_t).)"); - - traceEvent.arguments = Internal::toArguments(std::forward(arguments)...); - } - template void begin(char type, std::size_t id, StringType traceName, Arguments &&...arguments) { @@ -662,7 +666,7 @@ private: traceEvent.category = m_name; traceEvent.type = type; traceEvent.id = id; - appendArguments(traceEvent, std::forward(arguments)...); + Internal::appendArguments(traceEvent.arguments, std::forward(arguments)...); traceEvent.time = Clock::now(); } @@ -678,7 +682,7 @@ private: traceEvent.time = time; traceEvent.type = type; traceEvent.id = id; - appendArguments(traceEvent, std::forward(arguments)...); + Internal::appendArguments(traceEvent.arguments, std::forward(arguments)...); } template @@ -693,7 +697,7 @@ private: traceEvent.time = time; traceEvent.type = type; traceEvent.id = id; - appendArguments(traceEvent, std::forward(arguments)...); + Internal::appendArguments(traceEvent.arguments, std::forward(arguments)...); } private: @@ -710,15 +714,15 @@ using StringCategory = Category; template using StringViewWithStringArgumentsCategory = Category; -template +template class Tracer { public: using ArgumentType = typename Category::ArgumentType; - constexpr Tracer(ArgumentType, Category &, ArgumentType) {} - - constexpr Tracer(ArgumentType, Category &) {} + template + Tracer(ArgumentType, Category &, Arguments &&...) + {} Tracer(const Tracer &) = delete; Tracer &operator=(const Tracer &) = delete; @@ -727,33 +731,29 @@ public: ~Tracer() {} }; -template<> -class Tracer> +template +class Tracer { + using ArgumentType = typename Category::ArgumentType; + using StringType = typename Category::StringType; + using ArgumentsStringType = typename Category::ArgumentsStringType; + public: - Tracer(TracerLiteral name, StringViewCategory &category, TracerLiteral arguments) + template + Tracer(ArgumentType name, Category &category, Arguments &&...arguments) : m_name{name} - , m_arguments{arguments} , m_category{category} { - if constexpr (tracingStatus() == Tracing::IsEnabled) { - if (category.eventQueue().isEnabled == IsEnabled::Yes) - m_start = Clock::now(); - } - } - - Tracer(TracerLiteral name, StringViewCategory &category) - : Tracer{name, category, ""_t} - { - if constexpr (tracingStatus() == Tracing::IsEnabled) { - if (category.eventQueue().isEnabled == IsEnabled::Yes) - m_start = Clock::now(); + if (category.eventQueue().isEnabled == IsEnabled::Yes) { + Internal::appendArguments(m_arguments, + std::forward(arguments)...); + m_start = Clock::now(); } } Tracer(const Tracer &) = delete; Tracer &operator=(const Tracer &) = delete; - Tracer(Tracer &&other) noexcept = default; + Tracer(Tracer &&other) noexcept = delete; Tracer &operator=(Tracer &&other) noexcept = delete; ~Tracer() { @@ -773,93 +773,41 @@ public: private: TimePoint m_start; - std::string_view m_name; - std::string_view m_arguments; - StringViewCategory &m_category; + StringType m_name; + StringType m_arguments; + Category &m_category; }; -template<> -class Tracer> -{ -public: - Tracer(std::string name, StringViewCategory &category, std::string arguments) - : m_name{std::move(name)} - , m_arguments{std::move(arguments)} - , m_category{category} - { - if constexpr (tracingStatus() == Tracing::IsEnabled) { - if (category.eventQueue().isEnabled == IsEnabled::Yes) - m_start = Clock::now(); - } - } - - Tracer(std::string name, StringViewCategory &category) - : Tracer{std::move(name), category, ""} - { - if constexpr (tracingStatus() == Tracing::IsEnabled) { - if (category.eventQueue().isEnabled == IsEnabled::Yes) - m_start = Clock::now(); - } - } - - ~Tracer() - { - if constexpr (tracingStatus() == Tracing::IsEnabled) { - if (m_category.eventQueue().isEnabled == IsEnabled::Yes) { - auto duration = Clock::now() - m_start; - auto &traceEvent = getTraceEvent(m_category.eventQueue()); - traceEvent.name = std::move(m_name); - traceEvent.category = m_category.name(); - traceEvent.arguments = std::move(m_arguments); - traceEvent.time = m_start; - traceEvent.duration = duration; - traceEvent.type = 'X'; - } - } - } - -private: - TimePoint m_start; - std::string m_name; - std::string m_arguments; - StringViewCategory &m_category; -}; - -template -Tracer(TracerLiteral name, Category &category) -> Tracer; +template +Tracer(typename Category::ArgumentType name, Category &category, Arguments &&...) + -> Tracer; #ifdef NANOTRACE_ENABLED class GlobalTracer { public: - GlobalTracer(std::string name, std::string category, std::string arguments) + template + GlobalTracer(std::string name, std::string category, Arguments &&...arguments) : m_name{std::move(name)} , m_category{std::move(category)} - , m_arguments{std::move(arguments)} { - if constexpr (tracingStatus() == Tracing::IsEnabled) { - if (globalEventQueue().isEnabled == IsEnabled::Yes) - m_start = Clock::now(); + if (globalEventQueue().isEnabled == IsEnabled::Yes) { + Internal::appendArguments(m_arguments, std::forward(arguments)...); + m_start = Clock::now(); } } - GlobalTracer(std::string name, std::string category) - : GlobalTracer{std::move(name), std::move(category), ""} - {} - ~GlobalTracer() { - if constexpr (tracingStatus() == Tracing::IsEnabled) { - if (globalEventQueue().isEnabled == IsEnabled::Yes) { - auto duration = Clock::now() - m_start; - auto &traceEvent = getTraceEvent(globalEventQueue()); - traceEvent.name = std::move(m_name); - traceEvent.category = std::move(m_category); - traceEvent.arguments = std::move(m_arguments); - traceEvent.time = std::move(m_start); - traceEvent.duration = std::move(duration); - traceEvent.type = 'X'; - } + if (globalEventQueue().isEnabled == IsEnabled::Yes) { + auto duration = Clock::now() - m_start; + auto &traceEvent = getTraceEvent(globalEventQueue()); + traceEvent.name = std::move(m_name); + traceEvent.category = std::move(m_category); + traceEvent.arguments = std::move(m_arguments); + traceEvent.time = std::move(m_start); + traceEvent.duration = std::move(duration); + traceEvent.type = 'X'; } } From 4aa7c89aad96f58fcd31e7a545c70c60034eabf7 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Mon, 16 Oct 2023 00:49:14 +0200 Subject: [PATCH 056/242] Nanotracer: Add copy for object token Change-Id: I8d11284db1b38eeff6f271495f3095b49415f78c Reviewed-by: Tim Jenssen Reviewed-by: Qt CI Patch Build Bot --- src/libs/nanotrace/nanotracehr.h | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/libs/nanotrace/nanotracehr.h b/src/libs/nanotrace/nanotracehr.h index 620c94e49be..22a4deb613f 100644 --- a/src/libs/nanotrace/nanotracehr.h +++ b/src/libs/nanotrace/nanotracehr.h @@ -421,9 +421,25 @@ public: ObjectToken() = default; - ObjectToken(const ObjectToken &other) = delete; + ObjectToken(const ObjectToken &other) + : m_name{other.m_name} + , m_category{other.m_category} + { + if (other.m_id) + m_id = m_category->beginObject(m_name).m_id; + } - ObjectToken &operator=(const ObjectToken &other) = delete; + ObjectToken &operator=(const ObjectToken &other) + { + if (this != &other) { + ~ObjectToken(); + if (other.m_id) { + m_category = other.m_category; + m_name = other.m_name; + m_id = other.m_category->beginObject(other.m_name).m_id; + } + } + } ObjectToken(ObjectToken &&other) noexcept : m_name{std::move(other.m_name)} From df8330963642561f4cc6b5667f247cb5911fc69b Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Mon, 16 Oct 2023 11:00:57 +0200 Subject: [PATCH 057/242] QmlDesigner: Trace thread creation and readyness Thats is creating a token without a name but it can be used later wo create a token with a name or to tick. Change-Id: I772b819392e660c4d550aa59edc45579e6e00a02 Reviewed-by: Tim Jenssen Reviewed-by: Qt CI Patch Build Bot Reviewed-by: --- src/libs/nanotrace/nanotracehr.h | 41 +++++++++---------- .../imagecache/asynchronousimagecache.cpp | 9 ++-- .../designercore/imagecache/taskqueue.h | 41 +++++++++++++------ .../include/imagecacheauxiliarydata.h | 2 +- 4 files changed, 54 insertions(+), 39 deletions(-) diff --git a/src/libs/nanotrace/nanotracehr.h b/src/libs/nanotrace/nanotracehr.h index 22a4deb613f..b31036d47a0 100644 --- a/src/libs/nanotrace/nanotracehr.h +++ b/src/libs/nanotrace/nanotracehr.h @@ -494,8 +494,10 @@ public: ~AsynchronousToken() {} + [[nodiscard]] AsynchronousToken create() { return {}; } + template - AsynchronousToken begin(ArgumentType, Arguments &&...) + [[nodiscard]] AsynchronousToken begin(ArgumentType, Arguments &&...) { return AsynchronousToken{}; } @@ -509,6 +511,8 @@ public: {} }; +using DisabledAsynchronousToken = AsynchronousToken; + template class Category; @@ -551,13 +555,15 @@ public: ~AsynchronousToken() { end(); } + [[nodiscard]] AsynchronousToken create() { return {{}, m_id, *m_category}; } + template - AsynchronousToken begin(ArgumentType name, Arguments &&...arguments) + [[nodiscard]] AsynchronousToken begin(ArgumentType name, Arguments &&...arguments) { if (m_id) - m_category->begin('b', m_id, std::move(name), std::forward(arguments)...); + m_category->begin('b', m_id, name, std::forward(arguments)...); - return AsynchronousToken{m_name, m_id, *m_category}; + return AsynchronousToken{std::move(name), m_id, *m_category}; } template @@ -570,7 +576,7 @@ public: template void end(Arguments &&...arguments) { - if (m_id) + if (m_id && m_name.size()) m_category->end('e', m_id, std::move(m_name), std::forward(arguments)...); m_id = 0; @@ -597,13 +603,13 @@ public: Category(ArgumentType, EventQueue &) {} template - AsynchronousTokenType beginAsynchronous(ArgumentType, Arguments &&...) + [[nodiscard]] AsynchronousTokenType beginAsynchronous(ArgumentType, Arguments &&...) { return {}; } template - ObjectTokenType beginObject(ArgumentType, Arguments &&...) + [[nodiscard]] ObjectTokenType beginObject(ArgumentType, Arguments &&...) { return {}; } @@ -636,7 +642,8 @@ public: } template - AsynchronousTokenType beginAsynchronous(ArgumentType traceName, Arguments &&...arguments) + [[nodiscard]] AsynchronousTokenType beginAsynchronous(ArgumentType traceName, + Arguments &&...arguments) { std::size_t id = ++idCounter; @@ -645,18 +652,8 @@ public: return {traceName, id, *this}; } - ObjectTokenType beginObject(ArgumentType traceName) - { - std::size_t id = ++idCounter; - - if (id) - begin('b', id, std::move(traceName)); - - return {traceName, id, *this}; - } - template - ObjectTokenType beginObject(ArgumentType traceName, Arguments &&...arguments) + [[nodiscard]] ObjectTokenType beginObject(ArgumentType traceName, Arguments &&...arguments) { std::size_t id = ++idCounter; @@ -737,7 +734,7 @@ public: using ArgumentType = typename Category::ArgumentType; template - Tracer(ArgumentType, Category &, Arguments &&...) + [[nodiscard]] Tracer(ArgumentType, Category &, Arguments &&...) {} Tracer(const Tracer &) = delete; @@ -756,7 +753,7 @@ class Tracer public: template - Tracer(ArgumentType name, Category &category, Arguments &&...arguments) + [[nodiscard]] Tracer(ArgumentType name, Category &category, Arguments &&...arguments) : m_name{name} , m_category{category} { @@ -803,7 +800,7 @@ class GlobalTracer { public: template - GlobalTracer(std::string name, std::string category, Arguments &&...arguments) + [[nodiscard]] GlobalTracer(std::string name, std::string category, Arguments &&...arguments) : m_name{std::move(name)} , m_category{std::move(category)} { diff --git a/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagecache.cpp b/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagecache.cpp index 37e3e05ac0f..6c009ec3cad 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagecache.cpp +++ b/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagecache.cpp @@ -139,7 +139,8 @@ void AsynchronousImageCache::requestImage(Utils::SmallStringView name, { auto traceToken = ImageCache::category().beginAsynchronous( "request image in asynchornous image cache"_t); - m_taskQueue.addTask(std::move(name), + m_taskQueue.addTask(traceToken.create(), + std::move(name), std::move(extraId), std::move(captureCallback), std::move(abortCallback), @@ -156,7 +157,8 @@ void AsynchronousImageCache::requestMidSizeImage(Utils::SmallStringView name, { auto traceToken = ImageCache::category().beginAsynchronous( "request mid size image in asynchornous image cache"_t); - m_taskQueue.addTask(std::move(name), + m_taskQueue.addTask(traceToken.create(), + std::move(name), std::move(extraId), std::move(captureCallback), std::move(abortCallback), @@ -173,7 +175,8 @@ void AsynchronousImageCache::requestSmallImage(Utils::SmallStringView name, { auto traceToken = ImageCache::category().beginAsynchronous( "request small size image in asynchornous image cache"_t); - m_taskQueue.addTask(std::move(name), + m_taskQueue.addTask(traceToken.create(), + std::move(name), std::move(extraId), std::move(captureCallback), std::move(abortCallback), diff --git a/src/plugins/qmldesigner/designercore/imagecache/taskqueue.h b/src/plugins/qmldesigner/designercore/imagecache/taskqueue.h index 36c2d6bb7a5..7ae32310188 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/taskqueue.h +++ b/src/plugins/qmldesigner/designercore/imagecache/taskqueue.h @@ -3,6 +3,8 @@ #pragma once +#include + #include #include #include @@ -23,19 +25,26 @@ public: ~TaskQueue() { destroy(); } - template - void addTask(Arguments &&...arguments) + template + void addTask(NanotraceHR::AsynchronousToken traceToken, + Arguments &&...arguments) { { std::unique_lock lock{m_mutex}; - ensureThreadIsRunning(); + ensureThreadIsRunning(std::move(traceToken)); m_tasks.emplace_back(std::forward(arguments)...); } m_condition.notify_all(); } + template + void addTask(Arguments &&...arguments) + { + addTask(NanotraceHR::DisabledAsynchronousToken{}, std::forward(arguments)...); + } + void clean() { Tasks oldTasks; @@ -86,8 +95,11 @@ private: return {std::move(task)}; } - void ensureThreadIsRunning() + template + void ensureThreadIsRunning(TraceTokend traceToken) { + using namespace NanotraceHR::Literals; + if (m_finishing || !m_sleeping) return; @@ -96,15 +108,18 @@ private: m_sleeping = false; - m_backgroundThread = std::thread{[this] { - while (true) { - auto [lock, abort] = waitForTasks(); - if (abort) - return; - if (auto task = getTask(std::move(lock)); task) - m_dispatchCallback(*task); - } - }}; + auto threadCreateToken = traceToken.begin("thread is created in the task queue"_t); + m_backgroundThread = std::thread{[this](auto traceToken) { + traceToken.tick("thread is ready"_t); + while (true) { + auto [lock, abort] = waitForTasks(); + if (abort) + return; + if (auto task = getTask(std::move(lock)); task) + m_dispatchCallback(*task); + } + }, + std::move(traceToken)}; } void clearTasks(Tasks &tasks) diff --git a/src/plugins/qmldesigner/designercore/include/imagecacheauxiliarydata.h b/src/plugins/qmldesigner/designercore/include/imagecacheauxiliarydata.h index 5b7fa2624de..fc33cb98e21 100644 --- a/src/plugins/qmldesigner/designercore/include/imagecacheauxiliarydata.h +++ b/src/plugins/qmldesigner/designercore/include/imagecacheauxiliarydata.h @@ -20,7 +20,7 @@ namespace ImageCache { constexpr NanotraceHR::Tracing tracingStatus() { #ifdef ENABLE_IMAGE_CACHE_TRACING - return NanotraceHR::isTracerActive(); + return NanotraceHR::tracingStatus(); #else return NanotraceHR::Tracing::IsDisabled; #endif From 629d246650e8ef29c83258717a8249734faea0b1 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Mon, 16 Oct 2023 13:35:47 +0200 Subject: [PATCH 058/242] Nanotrace: Generalize arguments You can now add array or dictonary arguments NO_UNIQUE_ADDRESS ModelTracing::ObjectTraceToken traceToken = ModelTracing::category().beginObject( "InternalProperty"_t, std::forward_as_tuple("name", m_name), std::forward_as_tuple("values", std::forward_as_tuple(NanotraceHR::isArray, 1, 2, 3))); NO_UNIQUE_ADDRESS ModelTracing::ObjectTraceToken traceToken = ModelTracing::category().beginObject( "InternalProperty"_t, std::forward_as_tuple("name", m_name), std::forward_as_tuple("values", std::forward_as_tuple(NanotraceHR::isDictonary, std::forward_as_tuple("x", "foo"), std::forward_as_tuple("y", "bar")))); Change-Id: I3c136827455194439980700155f4c43f4b2915ea Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Tim Jenssen Reviewed-by: --- src/libs/nanotrace/nanotracehr.h | 134 ++++++++++++++++++++++++++----- 1 file changed, 113 insertions(+), 21 deletions(-) diff --git a/src/libs/nanotrace/nanotracehr.h b/src/libs/nanotrace/nanotracehr.h index b31036d47a0..f58801ad0cd 100644 --- a/src/libs/nanotrace/nanotracehr.h +++ b/src/libs/nanotrace/nanotracehr.h @@ -75,47 +75,139 @@ constexpr TracerLiteral operator""_t(const char *text, size_t size) using namespace Literals; +struct IsArray +{}; + +inline constexpr IsArray isArray; + +struct IsDictonary +{}; + +inline constexpr IsDictonary isDictonary; + namespace Internal { -inline std::string_view covertToString(std::string_view text) + +template +void convertToString(String &string, std::string_view text) { - return text; + string.append(R"(")"); + string.append(text); + string.append(R"(")"); } -template -inline std::string_view covertToString(const char (&text)[size]) +template +void convertToString(String &string, const char (&text)[size]) { - return text; + string.append(R"(")"); + string.append(text); + string.append(R"(")"); } -inline Utils::PathString covertToString(QStringView text) +template +void convertToString(String &string, QStringView text) { - return text; + string.append(R"(")"); + string.append(Utils::PathString{text}); + string.append(R"(")"); } -inline Utils::PathString covertToString(QByteArrayView text) +template +void convertToString(String &string, QByteArrayView text) { - return text; + string.append(R"(")"); + string.append(std::string_view(text.data(), Utils::usize(text))); + string.append(R"(")"); } -inline Utils::SmallString covertToString(long long number) +template +void convertToString(String &string, int number) { - return Utils::SmallString::number(number); + string.append(Utils::SmallString::number(number)); } -inline Utils::SmallString covertToString(double number) +template +void convertToString(String &string, long long number) { - return Utils::SmallString::number(number); + string.append(Utils::SmallString::number(number)); } -template -void toArgument(String &text, Argument &&argument) +template +void convertToString(String &string, double number) +{ + string.append(Utils::SmallString::number(number)); +} + +template +void convertToString(String &string, const std::tuple &dictonary); + +template +void convertToString(String &string, const std::tuple &list); + +template typename Container, typename... Arguments> +void convertToString(String &string, const Container &container); + +template +void convertArrayEntryToString(String &string, const Value &value) +{ + convertToString(string, value); + string.append(","); +} + +template +void convertArrayToString(String &string, const IsArray &, Entries &...entries) +{ + string.append(R"([)"); + (convertArrayEntryToString(string, entries), ...); + if (sizeof...(entries)) + string.pop_back(); + string.append("]"); +} + +template +void convertToString(String &string, const std::tuple &list) +{ + std::apply([&](auto &&...entries) { convertArrayToString(string, entries...); }, list); +} + +template +void convertDictonaryEntryToString(String &string, const std::tuple &argument) { const auto &[key, value] = argument; - text.append(R"(")"); - text.append(covertToString(key)); - text.append(R"(":")"); - text.append(covertToString(value)); - text.append(R"(",)"); + convertToString(string, key); + string.append(":"); + convertToString(string, value); + string.append(","); +} + +template +void convertDictonaryToString(String &string, const IsDictonary &, Entries &...entries) +{ + string.append(R"({)"); + (convertDictonaryEntryToString(string, entries), ...); + if (sizeof...(entries)) + string.pop_back(); + string.append("}"); +} + +template +void convertToString(String &string, const std::tuple &dictonary) +{ + std::apply([&](auto &&...entries) { convertDictonaryToString(string, entries...); }, dictonary); +} + +template typename Container, typename... Arguments> +void convertToString(String &string, const Container &container) +{ + string.append("["); + for (const auto &entry : container) { + convertToString(string, entry); + string.append(","); + } + + if (container.size()) + string.pop_back(); + + string.append("]"); } template @@ -126,7 +218,7 @@ String toArguments(Arguments &&...arguments) constexpr auto argumentCount = sizeof...(Arguments); text.reserve(sizeof...(Arguments) * 30); text.append("{"); - (toArgument(text, arguments), ...); + (convertDictonaryEntryToString(text, arguments), ...); if (argumentCount) text.pop_back(); From a671014125104a589442d3e74df7c2a5ac14ee5a Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Mon, 16 Oct 2023 16:25:37 +0200 Subject: [PATCH 059/242] Nanotrace: Categories can be anabled and disabled at runtime Change-Id: I41ddd311da96f54db5b1525c5b0126528bf67744 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Reviewed-by: Tim Jenssen --- src/libs/nanotrace/nanotracehr.h | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/libs/nanotrace/nanotracehr.h b/src/libs/nanotrace/nanotracehr.h index f58801ad0cd..2f3552148c4 100644 --- a/src/libs/nanotrace/nanotracehr.h +++ b/src/libs/nanotrace/nanotracehr.h @@ -761,10 +761,16 @@ public: static constexpr bool isActive() { return true; } +public: + IsEnabled isEnabled = IsEnabled::Yes; + private: template void begin(char type, std::size_t id, StringType traceName, Arguments &&...arguments) { + if (isEnabled == IsEnabled::No) + return; + auto &traceEvent = getTraceEvent(m_eventQueue); traceEvent.name = std::move(traceName); @@ -778,6 +784,9 @@ private: template void tick(char type, std::size_t id, StringType traceName, Arguments &&...arguments) { + if (isEnabled == IsEnabled::No) + return; + auto time = Clock::now(); auto &traceEvent = getTraceEvent(m_eventQueue); @@ -793,6 +802,9 @@ private: template void end(char type, std::size_t id, StringType traceName, Arguments &&...arguments) { + if (isEnabled == IsEnabled::No) + return; + auto time = Clock::now(); auto &traceEvent = getTraceEvent(m_eventQueue); @@ -849,7 +861,7 @@ public: : m_name{name} , m_category{category} { - if (category.eventQueue().isEnabled == IsEnabled::Yes) { + if (category.isEnabled == IsEnabled::Yes) { Internal::appendArguments(m_arguments, std::forward(arguments)...); m_start = Clock::now(); @@ -863,7 +875,7 @@ public: ~Tracer() { if constexpr (tracingStatus() == Tracing::IsEnabled) { - if (m_category.eventQueue().isEnabled == IsEnabled::Yes) { + if (m_category.isEnabled == IsEnabled::Yes) { auto duration = Clock::now() - m_start; auto &traceEvent = getTraceEvent(m_category.eventQueue()); traceEvent.name = m_name; From a9a74992def0f95071361045ca47ad9a2eedda3a Mon Sep 17 00:00:00 2001 From: Yasser Grimes Date: Fri, 29 Sep 2023 11:43:28 +0300 Subject: [PATCH 060/242] McuSupport: Notify users to read QtMCUs on QDS documentation Make the documentation for using QtMCUs design studio easier to locate for new users by showing an InfoBar notification with the link to the online documentation. Task-number: QDS-10332 Change-Id: I9344216ef36369cdfb284ab18dba91cd0b5d4c92 Reviewed-by: hjk Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Thomas Hartmann Reviewed-by: --- src/plugins/mcusupport/mcubuildstep.cpp | 3 ++ src/plugins/mcusupport/mcusupportplugin.cpp | 39 +++++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/src/plugins/mcusupport/mcubuildstep.cpp b/src/plugins/mcusupport/mcubuildstep.cpp index 3b0c639df5e..0250eb82f30 100644 --- a/src/plugins/mcusupport/mcubuildstep.cpp +++ b/src/plugins/mcusupport/mcubuildstep.cpp @@ -119,6 +119,9 @@ DeployMcuProcessStep::DeployMcuProcessStep(ProjectExplorer::BuildStepList *bc, U cmdLine.addArg(directory); return cmdLine; }); + + if(target()) + target()->setNamedSettings("IS_MCU_QDS_PROJECT", true); } QString DeployMcuProcessStep::findKitInformation(ProjectExplorer::Kit *kit, const QString &key) diff --git a/src/plugins/mcusupport/mcusupportplugin.cpp b/src/plugins/mcusupport/mcusupportplugin.cpp index 21a993866b6..90bc2af509c 100644 --- a/src/plugins/mcusupport/mcusupportplugin.cpp +++ b/src/plugins/mcusupport/mcusupportplugin.cpp @@ -43,6 +43,7 @@ #include #include +#include #include using namespace Core; @@ -51,6 +52,7 @@ using namespace ProjectExplorer; namespace McuSupport::Internal { const char setupMcuSupportKits[] = "SetupMcuSupportKits"; +const char qdsMcuDocInfoEntry[] = "McuDocInfoEntry"; void printMessage(const QString &message, bool important) { @@ -114,6 +116,25 @@ McuSupportPlugin::~McuSupportPlugin() dd = nullptr; } +static bool isQtMCUsProject(ProjectExplorer::Project *p) +{ + if (!Core::ICore::isQtDesignStudio()) + // should be unreachable + printMessage("Testing if the QDS project is an MCU project outside the QDS", true); + + if (!p || !p->rootProjectNode()) + return false; + + ProjectExplorer::Target *target = p->activeTarget(); + if (!target) + return false; + + if (!target->namedSettings("IS_MCU_QDS_PROJECT").toBool()) + return false; + + return true; +} + void McuSupportPlugin::initialize() { setObjectName("McuSupportPlugin"); @@ -160,6 +181,23 @@ void McuSupportPlugin::initialize() ->action() ->trigger(); }); + } else { + // Only in design studio + connect(ProjectManager::instance(), + &ProjectManager::projectFinishedParsing, + [&](ProjectExplorer::Project *p) { + if (!isQtMCUsProject(p) || !ICore::infoBar()->canInfoBeAdded(qdsMcuDocInfoEntry)) + return; + Utils::InfoBarEntry docInfo(qdsMcuDocInfoEntry, + Tr::tr("Read about Using QtMCUs in the Qt Design Studio"), + Utils::InfoBarEntry::GlobalSuppression::Enabled); + docInfo.addCustomButton(Tr::tr("Go to the Documentation"), [] { + ICore::infoBar()->suppressInfo(qdsMcuDocInfoEntry); + QDesktopServices::openUrl( + QUrl("https://doc.qt.io/qtdesignstudio/studio-on-mcus.html")); + }); + ICore::infoBar()->addInfo(docInfo); + }); } dd->m_options.registerQchFiles(); @@ -270,3 +308,4 @@ void McuSupportPlugin::updateDeployStep(ProjectExplorer::Target *target, bool en } } // namespace McuSupport::Internal + From 12aeed97b38c076493917979064252602db49039 Mon Sep 17 00:00:00 2001 From: Amr Essam Date: Wed, 18 Oct 2023 18:33:42 +0300 Subject: [PATCH 061/242] QmlDesigner: Add effect maker runtime nodes binding mechanism Still some issues related to UI components because it's never tested Also quick fix for generated paths on windows (not tested) Task-number: QDS-10987 Change-Id: Ifed4b0f687e8da78206985e031692aa6c9faf947 Reviewed-by: Mahmoud Badri --- src/plugins/effectmakernew/CMakeLists.txt | 1 + src/plugins/effectmakernew/compositionnode.cpp | 8 ++++++-- src/plugins/effectmakernew/effectmakermodel.cpp | 10 +++++----- .../effectmakernew/effectmakeruniformsmodel.cpp | 5 ++++- src/plugins/effectmakernew/effectmakerwidget.cpp | 4 +++- src/plugins/effectmakernew/propertyhandler.cpp | 10 ++++++++++ src/plugins/effectmakernew/propertyhandler.h | 15 +++++++++++++++ 7 files changed, 44 insertions(+), 9 deletions(-) create mode 100644 src/plugins/effectmakernew/propertyhandler.cpp create mode 100644 src/plugins/effectmakernew/propertyhandler.h diff --git a/src/plugins/effectmakernew/CMakeLists.txt b/src/plugins/effectmakernew/CMakeLists.txt index 21646e31322..10cff8f2737 100644 --- a/src/plugins/effectmakernew/CMakeLists.txt +++ b/src/plugins/effectmakernew/CMakeLists.txt @@ -22,5 +22,6 @@ add_qtc_plugin(EffectMakerNew effectmakercontextobject.cpp effectmakercontextobject.h shaderfeatures.cpp shaderfeatures.h syntaxhighlighterdata.cpp syntaxhighlighterdata.h + propertyhandler.cpp propertyhandler.h BUILD_DEFAULT OFF ) diff --git a/src/plugins/effectmakernew/compositionnode.cpp b/src/plugins/effectmakernew/compositionnode.cpp index 74e43c76d56..8952a29f3ad 100644 --- a/src/plugins/effectmakernew/compositionnode.cpp +++ b/src/plugins/effectmakernew/compositionnode.cpp @@ -5,6 +5,7 @@ #include "effectutils.h" #include "effectmakeruniformsmodel.h" +#include "propertyhandler.h" #include "uniform.h" #include @@ -100,8 +101,11 @@ void CompositionNode::parse(const QString &qenPath) // parse properties QJsonArray jsonProps = json.value("properties").toArray(); - for (const auto /*QJsonValueRef*/ &prop : jsonProps) - m_unifomrsModel.addUniform(new Uniform(prop.toObject())); + for (const auto /*QJsonValueRef*/ &prop : jsonProps) { + const auto uniform = new Uniform(prop.toObject()); + m_unifomrsModel.addUniform(uniform); + g_propertyData.insert(uniform->name(), uniform->value()); + } // Seek through code to get tags QStringList shaderCodeLines; diff --git a/src/plugins/effectmakernew/effectmakermodel.cpp b/src/plugins/effectmakernew/effectmakermodel.cpp index 94de609c2e8..6dbe9a78674 100644 --- a/src/plugins/effectmakernew/effectmakermodel.cpp +++ b/src/plugins/effectmakernew/effectmakermodel.cpp @@ -781,11 +781,11 @@ void EffectMakerModel::updateCustomUniforms() } } QString valueString = value.isEmpty() ? QString() : QString(": %1").arg(value); - QString bindedValueString = bindedValue.isEmpty() ? QString() : QString(": %1").arg(bindedValue); + QString boundValueString = bindedValue.isEmpty() ? QString() : QString(": %1").arg(bindedValue); // Custom values are not readonly, others inside the effect can be QString readOnly = uniform->useCustomValue() ? QString() : QStringLiteral("readonly "); previewEffectPropertiesString += " " + readOnly + "property " + type + " " - + propertyName + valueString + '\n'; + + propertyName + boundValueString + '\n'; // Define type properties are not added into exports if (!isDefine) { if (uniform->useCustomValue()) { @@ -797,7 +797,7 @@ void EffectMakerModel::updateCustomUniforms() } exportedEffectPropertiesString += QStringLiteral(" ") + readOnly + "property " + type + " " + propertyName - + bindedValueString + '\n'; + + boundValueString + '\n'; } else { // Custom values are not added into root exportedRootPropertiesString += " property " + type + " " + propertyName @@ -951,8 +951,8 @@ QString EffectMakerModel::getQmlComponentString(bool localFiles) s += '\n' + customImagesString; s += '\n'; - s += l2 + "vertexShader: 'file://" + m_vertexShaderFilename + "'\n"; - s += l2 + "fragmentShader: 'file://" + m_fragmentShaderFilename + "'\n"; + s += l2 + "vertexShader: 'file:///" + m_vertexShaderFilename + "'\n"; + s += l2 + "fragmentShader: 'file:///" + m_fragmentShaderFilename + "'\n"; s += l2 + "anchors.fill: parent\n"; if (m_shaderFeatures.enabled(ShaderFeatures::GridMesh)) { QString gridSize = QString("%1, %2").arg(m_shaderFeatures.gridMeshWidth()).arg(m_shaderFeatures.gridMeshHeight()); diff --git a/src/plugins/effectmakernew/effectmakeruniformsmodel.cpp b/src/plugins/effectmakernew/effectmakeruniformsmodel.cpp index 9313b986404..d8aa312df5a 100644 --- a/src/plugins/effectmakernew/effectmakeruniformsmodel.cpp +++ b/src/plugins/effectmakernew/effectmakeruniformsmodel.cpp @@ -3,6 +3,7 @@ #include "effectmakeruniformsmodel.h" +#include "propertyhandler.h" #include "uniform.h" #include @@ -48,7 +49,9 @@ bool EffectMakerUniformsModel::setData(const QModelIndex &index, const QVariant if (!index.isValid() || !roleNames().contains(role)) return false; - m_uniforms.at(index.row())->setValue(value); + auto uniform = m_uniforms.at(index.row()); + uniform->setValue(value); + g_propertyData.insert(uniform->name(), value); emit dataChanged(index, index, {role}); return true; diff --git a/src/plugins/effectmakernew/effectmakerwidget.cpp b/src/plugins/effectmakernew/effectmakerwidget.cpp index d0114584932..ed396113080 100644 --- a/src/plugins/effectmakernew/effectmakerwidget.cpp +++ b/src/plugins/effectmakernew/effectmakerwidget.cpp @@ -7,8 +7,8 @@ #include "effectmakermodel.h" #include "effectmakernodesmodel.h" #include "effectmakerview.h" +#include "propertyhandler.h" -#include "nodeinstanceview.h" #include "qmldesignerconstants.h" #include "qmldesignerplugin.h" #include "qqmlcontext.h" @@ -65,6 +65,8 @@ EffectMakerWidget::EffectMakerWidget(EffectMakerView *view) QmlDesigner::QmlDesignerPlugin::trackWidgetFocusTime(this, QmlDesigner::Constants::EVENT_EFFECTMAKER_TIME); + m_quickWidget->rootContext()->setContextProperty("g_propertyData", &g_propertyData); + auto map = m_quickWidget->registerPropertyMap("EffectMakerBackend"); map->setProperties({{"effectMakerNodesModel", QVariant::fromValue(m_effectMakerNodesModel.data())}, {"effectMakerModel", QVariant::fromValue(m_effectMakerModel.data())}, diff --git a/src/plugins/effectmakernew/propertyhandler.cpp b/src/plugins/effectmakernew/propertyhandler.cpp new file mode 100644 index 00000000000..9382c9a1f67 --- /dev/null +++ b/src/plugins/effectmakernew/propertyhandler.cpp @@ -0,0 +1,10 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include "propertyhandler.h" + +namespace EffectMaker { + +QQmlPropertyMap g_propertyData; + +} diff --git a/src/plugins/effectmakernew/propertyhandler.h b/src/plugins/effectmakernew/propertyhandler.h new file mode 100644 index 00000000000..82e2f7212d0 --- /dev/null +++ b/src/plugins/effectmakernew/propertyhandler.h @@ -0,0 +1,15 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#pragma once + +#include + +namespace EffectMaker { + +// This will be used for binding dynamic properties +// changes between C++ and QML. +extern QQmlPropertyMap g_propertyData; + +} + From 87695e368ba20b8c36d62173d74b7ca41222cbfc Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Thu, 12 Oct 2023 11:35:57 +0300 Subject: [PATCH 062/242] QmlDesigner: Add toggle button for 3D split view The button and puppet communication is added for split view toggle. Task-number: QDS-10921 Change-Id: I4322dfff6772eec493a2f3ce1722cdefb69bc490 Reviewed-by: Mahmoud Badri Reviewed-by: Qt CI Patch Build Bot Reviewed-by: --- .../interfaces/nodeinstanceglobal.h | 3 ++- .../components/edit3d/edit3dview.cpp | 18 ++++++++++++++++++ .../qmldesigner/components/edit3d/edit3dview.h | 1 + src/plugins/qmldesigner/qmldesignerconstants.h | 1 + .../qml2puppet/mockfiles/qt6/EditView3D.qml | 8 ++++++++ .../qt5informationnodeinstanceserver.cpp | 3 +++ 6 files changed, 33 insertions(+), 1 deletion(-) diff --git a/src/libs/qmlpuppetcommunication/interfaces/nodeinstanceglobal.h b/src/libs/qmlpuppetcommunication/interfaces/nodeinstanceglobal.h index 58166516c80..791b285e66f 100644 --- a/src/libs/qmlpuppetcommunication/interfaces/nodeinstanceglobal.h +++ b/src/libs/qmlpuppetcommunication/interfaces/nodeinstanceglobal.h @@ -46,7 +46,8 @@ enum class View3DActionType { ParticlesSeek, SyncEnvBackground, GetNodeAtPos, - SetBakeLightsView3D + SetBakeLightsView3D, + SplitViewToggle }; constexpr bool isNanotraceEnabled() diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp index e8ebf740397..79765aa25f8 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp +++ b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp @@ -121,6 +121,7 @@ void Edit3DView::updateActiveScene3D(const QVariantMap &sceneState) const QString particleEmitterKey = QStringLiteral("showParticleEmitter"); const QString particlesPlayKey = QStringLiteral("particlePlay"); const QString syncEnvBgKey = QStringLiteral("syncEnvBackground"); + const QString splitViewKey = QStringLiteral("splitView"); if (sceneState.contains(sceneKey)) { qint32 newActiveScene = sceneState[sceneKey].value(); @@ -191,6 +192,11 @@ void Edit3DView::updateActiveScene3D(const QVariantMap &sceneState) else m_particlesPlayAction->action()->setChecked(true); + if (sceneState.contains(splitViewKey)) + m_splitViewAction->action()->setChecked(sceneState[splitViewKey].toBool()); + else + m_splitViewAction->action()->setChecked(false); + // Syncing background color only makes sense for children of View3D instances bool syncValue = false; bool syncEnabled = false; @@ -1015,6 +1021,17 @@ void Edit3DView::createEdit3DActions() this, snapConfigTrigger); + m_splitViewAction = std::make_unique( + QmlDesigner::Constants::EDIT3D_SPLIT_VIEW, + View3DActionType::SplitViewToggle, + QCoreApplication::translate("SplitViewToggleAction", + "Toggle Split View On/Off"), + QKeySequence(Qt::Key_4), + true, + false, + toolbarIcon(DesignerIcons::ScaleToolIcon), // TODO Placeholder, needs proper icon + this); + m_leftActions << m_selectionModeAction.get(); m_leftActions << nullptr; // Null indicates separator m_leftActions << nullptr; // Second null after separator indicates an exclusive group @@ -1036,6 +1053,7 @@ void Edit3DView::createEdit3DActions() m_leftActions << nullptr; m_leftActions << m_visibilityTogglesAction.get(); m_leftActions << m_backgroundColorMenuAction.get(); + m_leftActions << m_splitViewAction.get(); m_rightActions << m_particleViewModeAction.get(); m_rightActions << m_particlesPlayAction.get(); diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dview.h b/src/plugins/qmldesigner/components/edit3d/edit3dview.h index c9d93381e05..84742943a22 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dview.h +++ b/src/plugins/qmldesigner/components/edit3d/edit3dview.h @@ -139,6 +139,7 @@ private: std::unique_ptr m_selectBackgroundColorAction; std::unique_ptr m_selectGridColorAction; std::unique_ptr m_resetColorAction; + std::unique_ptr m_splitViewAction; // View3DActionType::Empty actions std::unique_ptr m_resetAction; diff --git a/src/plugins/qmldesigner/qmldesignerconstants.h b/src/plugins/qmldesigner/qmldesignerconstants.h index 4ca8b1ea923..56a4ebca95f 100644 --- a/src/plugins/qmldesigner/qmldesignerconstants.h +++ b/src/plugins/qmldesigner/qmldesignerconstants.h @@ -62,6 +62,7 @@ const char EDIT3D_PARTICLE_MODE[] = "QmlDesigner.Editor3D.ParticleViewModeTo const char EDIT3D_PARTICLES_PLAY[] = "QmlDesigner.Editor3D.ParticlesPlay"; const char EDIT3D_PARTICLES_SEEKER[] = "QmlDesigner.Editor3D.ParticlesSeeker"; const char EDIT3D_PARTICLES_RESTART[] = "QmlDesigner.Editor3D.ParticlesRestart"; +const char EDIT3D_SPLIT_VIEW[] = "QmlDesigner.Editor3D.SplitViewToggle"; const char EDIT3D_VISIBILITY_TOGGLES[] = "QmlDesigner.Editor3D.VisibilityToggles"; const char EDIT3D_BACKGROUND_COLOR_ACTIONS[] = "QmlDesigner.Editor3D.BackgroundColorActions"; const char EDIT3D_BAKE_LIGHTS[] = "QmlDesigner.Editor3D.BakeLights"; diff --git a/src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml b/src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml index 4e2f4ea91d7..fd80b26584f 100644 --- a/src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml +++ b/src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml @@ -28,6 +28,7 @@ Item { property color backgroundGradientColorEnd: "#999999" property color gridColor: "#cccccc" property bool syncEnvBackground: false + property bool splitView: false enum SelectionMode { Item, Group } enum TransformMode { Move, Rotate, Scale } @@ -65,6 +66,7 @@ Item { onShowParticleEmitterChanged: _generalHelper.storeToolState(sceneId, "showParticleEmitter", showParticleEmitter); onSelectionModeChanged: _generalHelper.storeToolState(sceneId, "selectionMode", selectionMode); onTransformModeChanged: _generalHelper.storeToolState(sceneId, "transformMode", transformMode); + onSplitViewChanged: _generalHelper.storeToolState(sceneId, "splitView", splitView) onActiveSceneChanged: updateActiveScene() @@ -294,6 +296,11 @@ Item { cameraControl.restoreCameraState(toolStates.editCamState); else if (resetToDefault) cameraControl.restoreDefaultState(); + + if ("splitView" in toolStates) + splitView = toolStates.splitView; + else if (resetToDefault) + splitView = false; } function storeCurrentToolStates() @@ -309,6 +316,7 @@ Item { _generalHelper.storeToolState(sceneId, "globalOrientation", globalOrientation) _generalHelper.storeToolState(sceneId, "selectionMode", selectionMode); _generalHelper.storeToolState(sceneId, "transformMode", transformMode); + _generalHelper.storeToolState(sceneId, "splitView", splitView) cameraControl.storeCameraState(0); } diff --git a/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp b/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp index 7647fd8ddbd..f39263f65fe 100644 --- a/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp +++ b/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp @@ -2497,6 +2497,9 @@ void Qt5InformationNodeInstanceServer::view3DAction(const View3DActionCommand &c return; } #endif + case View3DActionType::SplitViewToggle: + updatedToolState.insert("splitView", command.isEnabled()); + break; default: break; From 3991561fd8264051c3158148959842e2f43a4206 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Thu, 12 Oct 2023 17:50:07 +0300 Subject: [PATCH 063/242] QmlDesigner: Split 3D edit view into four separate views 3D edit view can now be split into four separately controllable views. Each view has its own edit camera. Only one split can be 'active' at a time. Split is activated by mouse press or wheel event when the cursor is above the split. Transform gizmos are visible only in the active split. Task-number: QDS-10921 Change-Id: I59772f64bab9bcbe469daee3a717c0e1a382f01b Reviewed-by: Mahmoud Badri --- src/tools/qml2puppet/editor3d_qt6.qrc | 3 +- .../mockfiles/qt6/AutoScaleHelper.qml | 6 + .../mockfiles/qt6/EditCameraController.qml | 44 +- .../qml2puppet/mockfiles/qt6/EditView3D.qml | 968 +++++++----------- .../qml2puppet/mockfiles/qt6/Overlay2D.qml | 12 +- .../mockfiles/qt6/OverlayView3D.qml | 541 ++++++++++ .../mockfiles/qt6/ParticleEmitterGizmo.qml | 3 +- .../qml2puppet/mockfiles/qt6/SceneView3D.qml | 20 +- .../qt5informationnodeinstanceserver.cpp | 4 +- 9 files changed, 999 insertions(+), 602 deletions(-) create mode 100644 src/tools/qml2puppet/mockfiles/qt6/OverlayView3D.qml diff --git a/src/tools/qml2puppet/editor3d_qt6.qrc b/src/tools/qml2puppet/editor3d_qt6.qrc index c99a586fd9c..59423beb91d 100644 --- a/src/tools/qml2puppet/editor3d_qt6.qrc +++ b/src/tools/qml2puppet/editor3d_qt6.qrc @@ -26,6 +26,7 @@ mockfiles/qt6/EditCameraController.qml mockfiles/qt6/EditView3D.qml mockfiles/qt6/FadeHandle.qml + mockfiles/qt6/GridMaterial.qml mockfiles/qt6/HelperGrid.qml mockfiles/qt6/IconGizmo.qml mockfiles/qt6/LightGizmo.qml @@ -40,6 +41,7 @@ mockfiles/qt6/NodeNodeView.qml mockfiles/qt6/OriginGizmo.qml mockfiles/qt6/Overlay2D.qml + mockfiles/qt6/OverlayView3D.qml mockfiles/qt6/ParticleSystemGizmo.qml mockfiles/qt6/ParticleEmitterGizmo.qml mockfiles/qt6/PlanarDraggable.qml @@ -52,7 +54,6 @@ mockfiles/qt6/SceneView3D.qml mockfiles/qt6/SelectionBox.qml mockfiles/qt6/SpotLightHandle.qml - mockfiles/qt6/GridMaterial.qml mockfiles/shaders/gridmaterial.frag mockfiles/shaders/gridmaterial.vert diff --git a/src/tools/qml2puppet/mockfiles/qt6/AutoScaleHelper.qml b/src/tools/qml2puppet/mockfiles/qt6/AutoScaleHelper.qml index 5c73716426a..c0e9b21f52b 100644 --- a/src/tools/qml2puppet/mockfiles/qt6/AutoScaleHelper.qml +++ b/src/tools/qml2puppet/mockfiles/qt6/AutoScaleHelper.qml @@ -30,6 +30,12 @@ Node { function onOverlayUpdateNeeded() { updateScale() } } + Connections { + target: view3D + function onWidthChanged() { updateScale() } + function onHeightChanged() { updateScale() } + } + function getScale(baseScale) { return Qt.vector3d(baseScale.x * relativeScale, baseScale.y * relativeScale, diff --git a/src/tools/qml2puppet/mockfiles/qt6/EditCameraController.qml b/src/tools/qml2puppet/mockfiles/qt6/EditCameraController.qml index 7f54f4a09d6..f8263be85a0 100644 --- a/src/tools/qml2puppet/mockfiles/qt6/EditCameraController.qml +++ b/src/tools/qml2puppet/mockfiles/qt6/EditCameraController.qml @@ -7,9 +7,11 @@ import QtQuick3D 6.0 Item { id: cameraCtrl - property Camera camera: null - property View3D view3d: null - property string sceneId + property var viewRoot: null + property int splitId: -1 + property Camera camera: view3d ? view3d.camera : null + property View3D view3d: viewRoot.editViews[splitId] + property string sceneId: viewRoot.sceneId property vector3d _lookAtPoint property vector3d _pressPoint property vector3d _prevPoint @@ -27,11 +29,15 @@ Item { readonly property real _keyPanAmount: 5 property bool ignoreToolState: false + z: 10 + anchors.fill: parent + function restoreCameraState(cameraState) { if (!camera || ignoreToolState) return; + // TODO: handle camera control state separately for each split _lookAtPoint = cameraState[0]; _zoomFactor = cameraState[1]; camera.position = cameraState[2]; @@ -45,6 +51,7 @@ Item { if (!camera) return; + // TODO: handle camera control state separately for each split _lookAtPoint = Qt.vector3d(0, 0, 0); _zoomFactor = 1; camera.position = _defaultCameraPosition; @@ -58,6 +65,7 @@ Item { if (!camera || ignoreToolState) return; + // TODO: handle camera control state separately for each split var cameraState = []; cameraState[0] = _lookAtPoint; cameraState[1] = _zoomFactor; @@ -66,7 +74,6 @@ Item { _generalHelper.storeToolState(sceneId, "editCamState", cameraState, delay); } - function focusObject(targetNodes, rotation, updateZoom, closeUp) { if (!camera) @@ -88,7 +95,8 @@ Item { storeCameraState(0); } - function jumpToRotation(rotation) { + function jumpToRotation(rotation) + { let distance = camera.scenePosition.minus(_lookAtPoint).length() let direction = _generalHelper.dirForRotation(rotation) @@ -157,7 +165,7 @@ Item { acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.MiddleButton hoverEnabled: false anchors.fill: parent - onPositionChanged: (mouse)=> { + onPositionChanged: (mouse) => { if (cameraCtrl.camera && mouse.modifiers === Qt.AltModifier && cameraCtrl._dragging) { var currentPoint = Qt.vector3d(mouse.x, mouse.y, 0); if (cameraCtrl._button == Qt.LeftButton) { @@ -175,7 +183,8 @@ Item { } } } - onPressed: (mouse)=> { + onPressed: (mouse) => { + viewRoot.activeSplit = cameraCtrl.splitId if (cameraCtrl.camera && mouse.modifiers === Qt.AltModifier) { cameraCtrl._dragging = true; cameraCtrl._startRotation = cameraCtrl.camera.eulerRotation; @@ -190,7 +199,8 @@ Item { } } - function handleRelease() { + function handleRelease() + { cameraCtrl._dragging = false; cameraCtrl.storeCameraState(0); } @@ -198,7 +208,8 @@ Item { onReleased: handleRelease() onCanceled: handleRelease() - onWheel: (wheel)=> { + onWheel: (wheel) => { + viewRoot.activeSplit = cameraCtrl.splitId if (cameraCtrl.camera) { // Empirically determined divisor for nice zoom cameraCtrl.zoomRelative(wheel.angleDelta.y / -40); @@ -207,7 +218,7 @@ Item { } } - Keys.onPressed: (event)=> { + Keys.onPressed: (event) => { var pressPoint = Qt.vector3d(view3d.width / 2, view3d.height / 2, 0); var currentPoint; @@ -236,4 +247,17 @@ Item { event.accepted = true; } } + + OriginGizmo { + anchors.right: parent.right + anchors.top: parent.top + anchors.margins: 10 + width: 120 + height: 120 + targetNode: cameraCtrl.camera + + onAxisClicked: (axis) => { + cameraCtrl.jumpToRotation(quaternionForAxis(axis)); + } + } } diff --git a/src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml b/src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml index fd80b26584f..01c35cb4e05 100644 --- a/src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml +++ b/src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml @@ -12,7 +12,13 @@ Item { visible: true property Node activeScene: null - property View3D editView: null + property int activeSplit: 0 + property var editViews: [null, null, null, null] + property var overlayViews: [overlayView0, overlayView1, overlayView2, overlayView3] + property var cameraControls: [cameraControl0, cameraControl1, cameraControl2, cameraControl3] + property var viewRects: [viewRect0, viewRect1, viewRect2, viewRect3] + property var activeEditView: editViews[activeSplit] + property var activeOverlayView: overlayViews[activeSplit] property string sceneId property bool showEditLight: false @@ -38,12 +44,8 @@ Item { property Node selectedNode: null // This is multiSelectionNode in multi-selection case property var selectedNodes: [] // All selected nodes + property int selectionBoxCount: 0 - property var lightIconGizmos: [] - property var cameraGizmos: [] - property var particleSystemIconGizmos: [] - property var particleEmitterGizmos: [] - property var selectionBoxes: [] property rect viewPortRect: Qt.rect(0, 0, 1000, 1000) property Node activeParticleSystem: null property bool shuttingDown: false @@ -75,26 +77,34 @@ Item { shuttingDown = true; } - function createEditView() + function createEditViews() { var component = Qt.createComponent("SceneView3D.qml"); if (component.status === Component.Ready) { - editView = component.createObject(viewRect, - {"usePerspective": usePerspective, - "showSceneLight": showEditLight, - "showGrid": showGrid, - "gridColor": gridColor, - "importScene": activeScene, - "cameraLookAt": cameraControl._lookAtPoint, - "z": 1}); - editView.usePerspective = Qt.binding(function() {return usePerspective;}); - editView.showSceneLight = Qt.binding(function() {return showEditLight;}); - editView.showGrid = Qt.binding(function() {return showGrid;}); - editView.gridColor = Qt.binding(function() {return gridColor;}); - editView.cameraLookAt = Qt.binding(function() {return cameraControl._lookAtPoint;}); + for (var i = 0; i < 4; ++i) { + editViews[i] = component.createObject(viewRects[i], + {"usePerspective": usePerspective, + "showSceneLight": showEditLight, + "showGrid": showGrid, + "gridColor": gridColor, + "importScene": activeScene, + "cameraLookAt": cameraControls[i]._lookAtPoint, + "z": 1}); + editViews[i].usePerspective = Qt.binding(function() {return usePerspective;}); + editViews[i].showSceneLight = Qt.binding(function() {return showEditLight;}); + editViews[i].showGrid = Qt.binding(function() {return showGrid;}); + editViews[i].gridColor = Qt.binding(function() {return gridColor;}); + } + editViews[0].cameraLookAt = Qt.binding(function() {return cameraControl0._lookAtPoint;}); + editViews[1].cameraLookAt = Qt.binding(function() {return cameraControl1._lookAtPoint;}); + editViews[2].cameraLookAt = Qt.binding(function() {return cameraControl2._lookAtPoint;}); + editViews[3].cameraLookAt = Qt.binding(function() {return cameraControl3._lookAtPoint;}); - selectionBoxes.length = 0; - cameraControl.forceActiveFocus(); + activeSplit = 0; + + selectionBoxCount = 0; + cameraControl0.forceActiveFocus(); + editViewsChanged(); return true; } return false; @@ -102,13 +112,18 @@ Item { function updateActiveScene() { - if (editView) { - editView.visible = false; - editView.destroy(); + let viewsDeleted = false; + for (var i = 0; i < editViews.length; ++i) { + if (editViews[i]) { + editViews[i].visible = false; + editViews[i].destroy(); + editViews[i] = null; + viewsDeleted = true; + } } // importScene cannot be updated after initial set, so we need to reconstruct entire View3D - if (createEditView()) { + if (createEditViews()) { if (activeScene) { var toolStates = _generalHelper.getToolStates(sceneId); if (Object.keys(toolStates).length > 0) { @@ -118,8 +133,8 @@ Item { // turn the edit light on for scenes that do not have any scene // lights, and turn it off for scenes that have. var hasSceneLight = false; - for (var i = 0; i < lightIconGizmos.length; ++i) { - if (lightIconGizmos[i].scene === activeScene) { + for (var j = 0; j < overlayView0.lightIconGizmos.length; ++j) { + if (overlayView0.lightIconGizmos[j].scene === activeScene) { hasSceneLight = true; break; } @@ -141,6 +156,8 @@ Item { updateEnvBackground(); notifyActiveSceneChange(); + } else if (viewsDeleted) { + editViewsChanged(); } } @@ -165,24 +182,25 @@ Item { function fitToView() { - if (editView) { + if (activeEditView) { var boxModels = []; if (selectedNodes.length > 1) { for (var i = 0; i < selectedNodes.length; ++i) { - if (selectionBoxes.length > i) - boxModels.push(selectionBoxes[i].model) + if (selectionBoxCount > i) + boxModels.push(activeEditView.selectionBoxes[i].model) } - } else if (selectedNodes.length > 0 && selectionBoxes.length > 0) { - boxModels.push(selectionBoxes[0].model); + } else if (selectedNodes.length > 0 && selectionBoxCount > 0) { + boxModels.push(activeEditView.selectionBoxes[0].model); } - cameraControl.focusObject(boxModels, editView.camera.eulerRotation, true, false); + cameraControls[activeSplit].focusObject( + boxModels, activeEditView.camera.eulerRotation, true, false); } } function alignCamerasToView(cameraNodes) { - if (editView) { - cameraControl.alignCameras(cameraNodes); + if (activeEditView) { + cameraControls[activeSplit].alignCameras(cameraNodes); var propertyNames = ["position", "eulerRotation"]; viewRoot.changeObjectProperty(cameraNodes, propertyNames); viewRoot.commitObjectProperty(cameraNodes, propertyNames); @@ -191,11 +209,12 @@ Item { function alignViewToCamera(cameraNodes) { - if (editView) - cameraControl.alignView(cameraNodes); + if (activeEditView) + cameraControls[activeSplit].alignView(cameraNodes); } - function updateBackgroundColors(colors) { + function updateBackgroundColors(colors) + { if (colors.length === 1) { backgroundGradientColorStart = colors[0]; backgroundGradientColorEnd = colors[0]; @@ -205,28 +224,31 @@ Item { } } - function updateEnvBackground() { + function updateEnvBackground() + { updateBackgroundColors(_generalHelper.bgColor); - if (!editView) + if (!editViews[0]) return; - if (syncEnvBackground) { - let bgMode = _generalHelper.sceneEnvironmentBgMode(sceneId); - if ((!_generalHelper.sceneEnvironmentLightProbe(sceneId) && bgMode === SceneEnvironment.SkyBox) - || (!_generalHelper.sceneEnvironmentSkyBoxCubeMap(sceneId) && bgMode === SceneEnvironment.SkyBoxCubeMap)) { - editView.sceneEnv.backgroundMode = SceneEnvironment.Color; + for (var i = 0; i < 4; ++i) { + if (syncEnvBackground) { + let bgMode = _generalHelper.sceneEnvironmentBgMode(sceneId); + if ((!_generalHelper.sceneEnvironmentLightProbe(sceneId) && bgMode === SceneEnvironment.SkyBox) + || (!_generalHelper.sceneEnvironmentSkyBoxCubeMap(sceneId) && bgMode === SceneEnvironment.SkyBoxCubeMap)) { + editViews[i].sceneEnv.backgroundMode = SceneEnvironment.Color; + } else { + editViews[i].sceneEnv.backgroundMode = bgMode; + } + editViews[i].sceneEnv.lightProbe = _generalHelper.sceneEnvironmentLightProbe(sceneId); + editViews[i].sceneEnv.skyBoxCubeMap = _generalHelper.sceneEnvironmentSkyBoxCubeMap(sceneId); + editViews[i].sceneEnv.clearColor = _generalHelper.sceneEnvironmentColor(sceneId); } else { - editView.sceneEnv.backgroundMode = bgMode; + editViews[i].sceneEnv.backgroundMode = SceneEnvironment.Transparent; + editViews[i].sceneEnv.lightProbe = null; + editViews[i].sceneEnv.skyBoxCubeMap = null; + editViews[i].sceneEnv.clearColor = "transparent"; } - editView.sceneEnv.lightProbe = _generalHelper.sceneEnvironmentLightProbe(sceneId); - editView.sceneEnv.skyBoxCubeMap = _generalHelper.sceneEnvironmentSkyBoxCubeMap(sceneId); - editView.sceneEnv.clearColor = _generalHelper.sceneEnvironmentColor(sceneId); - } else { - editView.sceneEnv.backgroundMode = SceneEnvironment.Transparent; - editView.sceneEnv.lightProbe = null; - editView.sceneEnv.skyBoxCubeMap = null; - editView.sceneEnv.clearColor = "transparent"; } } @@ -292,15 +314,20 @@ Item { else if (resetToDefault) transformMode = EditView3D.TransformMode.Move; - if ("editCamState" in toolStates) - cameraControl.restoreCameraState(toolStates.editCamState); - else if (resetToDefault) - cameraControl.restoreDefaultState(); + for (var i = 0; i < 4; ++i) { + if ("editCamState" in toolStates) + cameraControls[i].restoreCameraState(toolStates.editCamState); + else if (resetToDefault) + cameraControls[i].restoreDefaultState(); + } - if ("splitView" in toolStates) + if ("splitView" in toolStates) { splitView = toolStates.splitView; - else if (resetToDefault) + activeSplit = 0; + } else if (resetToDefault) { splitView = false; + activeSplit = 0; + } } function storeCurrentToolStates() @@ -318,28 +345,18 @@ Item { _generalHelper.storeToolState(sceneId, "transformMode", transformMode); _generalHelper.storeToolState(sceneId, "splitView", splitView) - cameraControl.storeCameraState(0); + for (var i = 0; i < 4; ++i) + cameraControls[i].storeCameraState(0); } function ensureSelectionBoxes(count) { - var needMore = count - selectionBoxes.length - if (needMore > 0) { - var component = Qt.createComponent("SelectionBox.qml"); - if (component.status === Component.Ready) { - for (var i = 0; i < needMore; ++i) { - var geometryName = _generalHelper.generateUniqueName("SelectionBoxGeometry"); - var boxParent = null; - if (editView) - boxParent = editView.sceneHelpers; - var box = component.createObject(boxParent, {"view3D": editView, - "geometryName": geometryName}); - selectionBoxes[selectionBoxes.length] = box; - box.view3D = Qt.binding(function() {return editView;}); - box.showBox = Qt.binding(function() {return showSelectionBox;}); - } - } + for (var i = 0; i < 4; ++i) { + if (editViews[i]) + editViews[i].ensureSelectionBoxes(count); } + + selectionBoxCount = count; } function selectObjects(objects) @@ -349,11 +366,15 @@ Item { // This fixes an occasional visual glitch when creating a new box. ensureSelectionBoxes(objects.length + 1) - var i; - for (i = 0; i < objects.length; ++i) - selectionBoxes[i].targetNode = objects[i]; - for (i = objects.length; i < selectionBoxes.length; ++i) - selectionBoxes[i].targetNode = null; + for (var idx = 0; idx < 4; ++idx) { + if (editViews[idx]) { + var i; + for (i = 0; i < objects.length; ++i) + editViews[idx].selectionBoxes[i].targetNode = objects[i]; + for (i = objects.length; i < editViews[idx].selectionBoxes.length; ++i) + editViews[idx].selectionBoxes[i].targetNode = null; + } + } selectedNodes = objects; if (objects.length === 0) { @@ -424,274 +445,117 @@ Item { function addLightGizmo(scene, obj) { - // Insert into first available gizmo if we don't already have gizmo for this object - var slotFound = -1; - for (var i = 0; i < lightIconGizmos.length; ++i) { - if (!lightIconGizmos[i].targetNode) { - slotFound = i; - } else if (lightIconGizmos[i].targetNode === obj) { - lightIconGizmos[i].scene = scene; - return; - } - } - - if (slotFound !== -1) { - lightIconGizmos[slotFound].scene = scene; - lightIconGizmos[slotFound].targetNode = obj; - lightIconGizmos[slotFound].locked = _generalHelper.isLocked(obj); - lightIconGizmos[slotFound].hidden = _generalHelper.isHidden(obj); - return; - } - - // No free gizmos available, create a new one - var gizmoComponent = Qt.createComponent("LightIconGizmo.qml"); - if (gizmoComponent.status === Component.Ready) { - var gizmo = gizmoComponent.createObject(overlayView, - {"view3D": overlayView, "targetNode": obj, - "selectedNodes": selectedNodes, "scene": scene, - "activeScene": activeScene, - "locked": _generalHelper.isLocked(obj), - "hidden": _generalHelper.isHidden(obj), - "globalShow": showIconGizmo}); - lightIconGizmos[lightIconGizmos.length] = gizmo; - gizmo.clicked.connect(handleObjectClicked); - gizmo.selectedNodes = Qt.binding(function() {return selectedNodes;}); - gizmo.activeScene = Qt.binding(function() {return activeScene;}); - gizmo.globalShow = Qt.binding(function() {return showIconGizmo;}); - } + for (var i = 0; i < 4; ++i) + overlayViews[i].addLightGizmo(scene, obj); } function addCameraGizmo(scene, obj) { - // Insert into first available gizmo if we don't already have gizmo for this object - var slotFound = -1; - for (var i = 0; i < cameraGizmos.length; ++i) { - if (!cameraGizmos[i].targetNode) { - slotFound = i; - } else if (cameraGizmos[i].targetNode === obj) { - cameraGizmos[i].scene = scene; - return; - } - } - - if (slotFound !== -1) { - cameraGizmos[slotFound].scene = scene; - cameraGizmos[slotFound].targetNode = obj; - cameraGizmos[slotFound].locked = _generalHelper.isLocked(obj); - cameraGizmos[slotFound].hidden = _generalHelper.isHidden(obj); - return; - } - - // No free gizmos available, create a new one - var gizmoComponent = Qt.createComponent("CameraGizmo.qml"); - var frustumComponent = Qt.createComponent("CameraFrustum.qml"); - if (gizmoComponent.status === Component.Ready && frustumComponent.status === Component.Ready) { - var geometryName = _generalHelper.generateUniqueName("CameraGeometry"); - var frustum = frustumComponent.createObject( - overlayScene, - {"geometryName": geometryName, "viewPortRect": viewPortRect}); - var gizmo = gizmoComponent.createObject( - overlayView, - {"view3D": overlayView, "targetNode": obj, - "selectedNodes": selectedNodes, "scene": scene, "activeScene": activeScene, - "locked": _generalHelper.isLocked(obj), "hidden": _generalHelper.isHidden(obj), - "globalShow": showIconGizmo, "globalShowFrustum": showCameraFrustum}); - - cameraGizmos[cameraGizmos.length] = gizmo; - gizmo.clicked.connect(handleObjectClicked); - gizmo.selectedNodes = Qt.binding(function() {return selectedNodes;}); - gizmo.activeScene = Qt.binding(function() {return activeScene;}); - gizmo.globalShow = Qt.binding(function() {return showIconGizmo;}); - gizmo.globalShowFrustum = Qt.binding(function() {return showCameraFrustum;}); - frustum.viewPortRect = Qt.binding(function() {return viewPortRect;}); - gizmo.connectFrustum(frustum); - } + for (var i = 0; i < 4; ++i) + overlayViews[i].addCameraGizmo(scene, obj); } function addParticleSystemGizmo(scene, obj) { - // Insert into first available gizmo if we don't already have gizmo for this object - var slotFound = -1; - for (var i = 0; i < particleSystemIconGizmos.length; ++i) { - if (!particleSystemIconGizmos[i].targetNode) { - slotFound = i; - } else if (particleSystemIconGizmos[i].targetNode === obj) { - particleSystemIconGizmos[i].scene = scene; - return; - } - } - - if (slotFound !== -1) { - particleSystemIconGizmos[slotFound].scene = scene; - particleSystemIconGizmos[slotFound].targetNode = obj; - particleSystemIconGizmos[slotFound].locked = _generalHelper.isLocked(obj); - particleSystemIconGizmos[slotFound].hidden = _generalHelper.isHidden(obj); - return; - } - - // No free gizmos available, create a new one - var gizmoComponent = Qt.createComponent("ParticleSystemGizmo.qml"); - if (gizmoComponent.status === Component.Ready) { - var gizmo = gizmoComponent.createObject(overlayView, - {"view3D": overlayView, "targetNode": obj, - "selectedNodes": selectedNodes, "scene": scene, - "activeScene": activeScene, - "locked": _generalHelper.isLocked(obj), - "hidden": _generalHelper.isHidden(obj), - "globalShow": showIconGizmo, - "activeParticleSystem": activeParticleSystem}); - particleSystemIconGizmos[particleSystemIconGizmos.length] = gizmo; - gizmo.clicked.connect(handleObjectClicked); - gizmo.selectedNodes = Qt.binding(function() {return selectedNodes;}); - gizmo.activeScene = Qt.binding(function() {return activeScene;}); - gizmo.globalShow = Qt.binding(function() {return showIconGizmo;}); - gizmo.activeParticleSystem = Qt.binding(function() {return activeParticleSystem;}); - } + for (var i = 0; i < 4; ++i) + overlayViews[i].addParticleSystemGizmo(scene, obj); } function addParticleEmitterGizmo(scene, obj) { - // Insert into first available gizmo if we don't already have gizmo for this object - var slotFound = -1; - for (var i = 0; i < particleEmitterGizmos.length; ++i) { - if (!particleEmitterGizmos[i].targetNode) { - slotFound = i; - } else if (particleEmitterGizmos[i].targetNode === obj) { - particleEmitterGizmos[i].scene = scene; - return; - } - } - - if (slotFound !== -1) { - particleEmitterGizmos[slotFound].scene = scene; - particleEmitterGizmos[slotFound].targetNode = obj; - particleEmitterGizmos[slotFound].hidden = _generalHelper.isHidden(obj); - particleEmitterGizmos[slotFound].systemHidden = _generalHelper.isHidden(obj.system); - return; - } - - // No free gizmos available, create a new one - var gizmoComponent = Qt.createComponent("ParticleEmitterGizmo.qml"); - if (gizmoComponent.status === Component.Ready) { - var gizmo = gizmoComponent.createObject( - overlayScene, - {"targetNode": obj, "selectedNodes": selectedNodes, - "activeParticleSystem": activeParticleSystem, "scene": scene, - "activeScene": activeScene, "hidden": _generalHelper.isHidden(obj), - "systemHidden": _generalHelper.isHidden(obj.system), - "globalShow": showParticleEmitter}); - - particleEmitterGizmos[particleEmitterGizmos.length] = gizmo; - gizmo.selectedNodes = Qt.binding(function() {return selectedNodes;}); - gizmo.activeParticleSystem = Qt.binding(function() {return activeParticleSystem;}); - gizmo.globalShow = Qt.binding(function() {return showParticleEmitter;}); - gizmo.activeScene = Qt.binding(function() {return activeScene;}); - } + for (var i = 0; i < 4; ++i) + overlayViews[i].addParticleEmitterGizmo(scene, obj); } function releaseLightGizmo(obj) { - for (var i = 0; i < lightIconGizmos.length; ++i) { - if (lightIconGizmos[i].targetNode === obj) { - lightIconGizmos[i].scene = null; - lightIconGizmos[i].targetNode = null; - return; - } - } + for (var i = 0; i < 4; ++i) + overlayViews[i].releaseLightGizmo(obj); } function releaseCameraGizmo(obj) { - for (var i = 0; i < cameraGizmos.length; ++i) { - if (cameraGizmos[i].targetNode === obj) { - cameraGizmos[i].scene = null; - cameraGizmos[i].targetNode = null; - return; - } - } + for (var i = 0; i < 4; ++i) + overlayViews[i].releaseCameraGizmo(obj); } function releaseParticleSystemGizmo(obj) { - for (var i = 0; i < particleSystemIconGizmos.length; ++i) { - if (particleSystemIconGizmos[i].targetNode === obj) { - particleSystemIconGizmos[i].scene = null; - particleSystemIconGizmos[i].targetNode = null; - return; - } - } + for (var i = 0; i < 4; ++i) + overlayViews[i].releaseParticleSystemGizmo(obj); } function releaseParticleEmitterGizmo(obj) { - for (var i = 0; i < particleEmitterGizmos.length; ++i) { - if (particleEmitterGizmos[i].targetNode === obj) { - particleEmitterGizmos[i].scene = null; - particleEmitterGizmos[i].targetNode = null; - return; - } - } + for (var i = 0; i < 4; ++i) + overlayViews[i].releaseParticleEmitterGizmo(obj); } function updateLightGizmoScene(scene, obj) { - for (var i = 0; i < lightIconGizmos.length; ++i) { - if (lightIconGizmos[i].targetNode === obj) { - lightIconGizmos[i].scene = scene; - return; - } - } + for (var i = 0; i < 4; ++i) + overlayViews[i].updateLightGizmoScene(scene, obj); } function updateCameraGizmoScene(scene, obj) { - for (var i = 0; i < cameraGizmos.length; ++i) { - if (cameraGizmos[i].targetNode === obj) { - cameraGizmos[i].scene = scene; - return; - } - } + for (var i = 0; i < 4; ++i) + overlayViews[i].updateCameraGizmoScene(scene, obj); } function updateParticleSystemGizmoScene(scene, obj) { - for (var i = 0; i < particleSystemIconGizmos.length; ++i) { - if (particleSystemIconGizmos[i].targetNode === obj) { - particleSystemIconGizmos[i].scene = scene; - return; - } - } + for (var i = 0; i < 4; ++i) + overlayViews[i].updateParticleSystemGizmoScene(scene, obj); } function updateParticleEmitterGizmoScene(scene, obj) { - for (var i = 0; i < particleEmitterGizmos.length; ++i) { - if (particleEmitterGizmos[i].targetNode === obj) { - particleEmitterGizmos[i].scene = scene; - return; + for (var i = 0; i < 4; ++i) + overlayViews[i].updateParticleEmitterGizmoScene(scene, obj); + } + + function resolveSplitPoint(x, y) + { + if (activeSplit === 0) + return Qt.point(x, y); + else if (activeSplit === 1) + return Qt.point(x - viewContainer.width / 2, y); + else if (activeSplit === 2) + return Qt.point(x, y - viewContainer.height / 2); + else + return Qt.point(x - viewContainer.width / 2, y - viewContainer.height / 2); + } + + function updateActiveSplit(x, y) + { + if (splitView) { + if (x <= viewContainer.width / 2) { + if (y <= viewContainer.height / 2) + activeSplit = 0; + else + activeSplit = 2; + } else { + if (y <= viewContainer.height / 2) + activeSplit = 1; + else + activeSplit = 3; } + } else { + activeSplit = 0; } } function gizmoAt(x, y) { - for (var i = 0; i < lightIconGizmos.length; ++i) { - if (lightIconGizmos[i].visible && lightIconGizmos[i].hasPoint(x, y)) - return lightIconGizmos[i].targetNode; - } - for (var i = 0; i < cameraGizmos.length; ++i) { - if (cameraGizmos[i].visible && cameraGizmos[i].hasPoint(x, y)) - return cameraGizmos[i].targetNode; - } - for (var i = 0; i < particleSystemIconGizmos.length; ++i) { - if (particleSystemIconGizmos[i].visible && particleSystemIconGizmos[i].hasPoint(x, y)) - return particleSystemIconGizmos[i].targetNode; - } - return null; + updateActiveSplit(x, y); + let splitPoint = resolveSplitPoint(x, y); + + return activeOverlayView.gizmoAt(splitPoint.x, splitPoint.y); } Component.onCompleted: { - createEditView(); + createEditViews(); selectObjects([]); // Work-around the fact that the projection matrix for the camera is not calculated until // the first frame is rendered, so any initial calls to mapFrom3DScene() will fail. @@ -705,55 +569,15 @@ Item { target: _generalHelper function onLockedStateChanged(node) { - for (var i = 0; i < cameraGizmos.length; ++i) { - if (cameraGizmos[i].targetNode === node) { - cameraGizmos[i].locked = _generalHelper.isLocked(node); - return; - } - } - for (var i = 0; i < lightIconGizmos.length; ++i) { - if (lightIconGizmos[i].targetNode === node) { - lightIconGizmos[i].locked = _generalHelper.isLocked(node); - return; - } - } - for (var i = 0; i < particleSystemIconGizmos.length; ++i) { - if (particleSystemIconGizmos[i].targetNode === node) { - particleSystemIconGizmos[i].locked = _generalHelper.isLocked(node); - return; - } - } + for (var i = 0; i < 4; ++i) + overlayViews[i].handleLockedStateChange(node); } function onHiddenStateChanged(node) { - for (var i = 0; i < cameraGizmos.length; ++i) { - if (cameraGizmos[i].targetNode === node) { - cameraGizmos[i].hidden = _generalHelper.isHidden(node); - return; - } - } - for (var i = 0; i < lightIconGizmos.length; ++i) { - if (lightIconGizmos[i].targetNode === node) { - lightIconGizmos[i].hidden = _generalHelper.isHidden(node); - return; - } - } - for (var i = 0; i < particleSystemIconGizmos.length; ++i) { - if (particleSystemIconGizmos[i].targetNode === node) { - particleSystemIconGizmos[i].hidden = _generalHelper.isHidden(node); - return; - } - } - for (var i = 0; i < particleEmitterGizmos.length; ++i) { - if (particleEmitterGizmos[i].targetNode === node) { - particleEmitterGizmos[i].hidden = _generalHelper.isHidden(node); - return; - } else if (particleEmitterGizmos[i].targetNode && particleEmitterGizmos[i].targetNode.system === node) { - particleEmitterGizmos[i].systemHidden = _generalHelper.isHidden(node); - return; - } - } + for (var i = 0; i < 4; ++i) + overlayViews[i].handleHiddenStateChange(node); + } function onUpdateDragTooltip() @@ -762,228 +586,211 @@ Item { rotateGizmoLabel.updateLabel(); } - function onSceneEnvDataChanged() { + function onSceneEnvDataChanged() + { updateEnvBackground(); } } + // Shared nodes of the overlay, set as importScene on all overlay views. + // Content here can be used as is on all splits. + // Nodes that utilize autoscaling or otherwise need to have different appearance on each split + // need to have separate copy on each split. Node { id: overlayScene - PerspectiveCamera { - id: overlayPerspectiveCamera - clipFar: viewRoot.editView ? viewRoot.editView.perspectiveCamera.clipFar : 1000 - clipNear: viewRoot.editView ? viewRoot.editView.perspectiveCamera.clipNear : 1 - position: viewRoot.editView ? viewRoot.editView.perspectiveCamera.position : Qt.vector3d(0, 0, 0) - rotation: viewRoot.editView ? viewRoot.editView.perspectiveCamera.rotation : Qt.quaternion(1, 0, 0, 0) - } - - OrthographicCamera { - id: overlayOrthoCamera - clipFar: viewRoot.editView ? viewRoot.editView.orthoCamera.clipFar : 1000 - clipNear: viewRoot.editView ? viewRoot.editView.orthoCamera.clipNear : 1 - position: viewRoot.editView ? viewRoot.editView.orthoCamera.position : Qt.vector3d(0, 0, 0) - rotation: viewRoot.editView ? viewRoot.editView.orthoCamera.rotation : Qt.quaternion(1, 0, 0, 0) - horizontalMagnification: viewRoot.editView ? viewRoot.editView.orthoCamera.horizontalMagnification : 1 - verticalMagnification: viewRoot.editView ? viewRoot.editView.orthoCamera.verticalMagnification : 1 - } - - MouseArea3D { - id: gizmoDragHelper - view3D: overlayView - } - Node { id: multiSelectionNode objectName: "multiSelectionNode" } - - MoveGizmo { - id: moveGizmo - scale: autoScale.getScale(Qt.vector3d(5, 5, 5)) - highlightOnHover: true - targetNode: viewRoot.selectedNode - globalOrientation: viewRoot.globalOrientation - visible: viewRoot.selectedNode && transformMode === EditView3D.TransformMode.Move - view3D: overlayView - dragHelper: gizmoDragHelper - property var propertyNames: ["position"] - - onPositionCommit: { - if (targetNode == multiSelectionNode) - viewRoot.commitObjectProperty(_generalHelper.multiSelectionTargets(), propertyNames); - else - viewRoot.commitObjectProperty([viewRoot.selectedNode], propertyNames); - } - onPositionMove: { - if (targetNode == multiSelectionNode) - viewRoot.changeObjectProperty(_generalHelper.multiSelectionTargets(), propertyNames); - else - viewRoot.changeObjectProperty([viewRoot.selectedNode], propertyNames); - } - } - - ScaleGizmo { - id: scaleGizmo - scale: autoScale.getScale(Qt.vector3d(5, 5, 5)) - highlightOnHover: true - targetNode: viewRoot.selectedNode - visible: viewRoot.selectedNode && transformMode === EditView3D.TransformMode.Scale - view3D: overlayView - dragHelper: gizmoDragHelper - property var propertyNames: ["scale"] - property var propertyNamesMulti: ["position", "scale"] - - onScaleCommit: { - if (targetNode == multiSelectionNode) - viewRoot.commitObjectProperty(_generalHelper.multiSelectionTargets(), propertyNamesMulti); - else - viewRoot.commitObjectProperty([viewRoot.selectedNode], propertyNames); - } - onScaleChange: { - if (targetNode == multiSelectionNode) - viewRoot.changeObjectProperty(_generalHelper.multiSelectionTargets(), propertyNamesMulti); - else - viewRoot.changeObjectProperty([viewRoot.selectedNode], propertyNames); - } - } - - RotateGizmo { - id: rotateGizmo - scale: autoScale.getScale(Qt.vector3d(7, 7, 7)) - highlightOnHover: true - targetNode: viewRoot.selectedNode - globalOrientation: viewRoot.globalOrientation - visible: viewRoot.selectedNode && transformMode === EditView3D.TransformMode.Rotate - view3D: overlayView - dragHelper: gizmoDragHelper - property var propertyNames: ["eulerRotation"] - property var propertyNamesMulti: ["position", "eulerRotation"] - - onRotateCommit: { - if (targetNode == multiSelectionNode) - viewRoot.commitObjectProperty(_generalHelper.multiSelectionTargets(), propertyNamesMulti); - else - viewRoot.commitObjectProperty([viewRoot.selectedNode], propertyNames); - } - onRotateChange: { - if (targetNode == multiSelectionNode) - viewRoot.changeObjectProperty(_generalHelper.multiSelectionTargets(), propertyNamesMulti); - else - viewRoot.changeObjectProperty([viewRoot.selectedNode], propertyNames); - } - onCurrentAngleChanged: rotateGizmoLabel.updateLabel() - } - - LightGizmo { - id: lightGizmo - targetNode: viewRoot.selectedNode != multiSelectionNode ? viewRoot.selectedNode : null - view3D: overlayView - dragHelper: gizmoDragHelper - - onPropertyValueCommit: (propName) => { - viewRoot.commitObjectProperty([targetNode], [propName]); - } - onPropertyValueChange: (propName) => { - viewRoot.changeObjectProperty([targetNode], [propName]); - } - } - - AutoScaleHelper { - id: autoScale - view3D: overlayView - position: moveGizmo.scenePosition - } - - AutoScaleHelper { - id: pivotAutoScale - view3D: overlayView - position: pivotLine.startPos - } - - Line3D { - id: pivotLine - visible: viewRoot.selectedNode && viewRoot.selectedNode != multiSelectionNode - name: "3D Edit View Pivot Line" - color: "#ddd600" - - startPos: viewRoot.selectedNode ? viewRoot.selectedNode.scenePosition - : Qt.vector3d(0, 0, 0) - Connections { - target: viewRoot - function onSelectedNodeChanged() - { - pivotLine.endPos = gizmoDragHelper.pivotScenePosition(viewRoot.selectedNode); - } - } - Connections { - target: viewRoot.selectedNode - function onSceneTransformChanged() - { - pivotLine.endPos = gizmoDragHelper.pivotScenePosition(viewRoot.selectedNode); - } - } - - Model { - id: pivotCap - readonly property bool _edit3dLocked: true // Make this non-pickable - source: "#Sphere" - scale: pivotAutoScale.getScale(Qt.vector3d(0.03, 0.03, 0.03)) - position: pivotLine.startPos - materials: [ - DefaultMaterial { - id: lineMat - lighting: DefaultMaterial.NoLighting - cullMode: Material.NoCulling - diffuseColor: pivotLine.color - } - ] - } - } } Item { id: contentItem anchors.fill: parent - Rectangle { - id: viewRect + Item { + id: viewContainer anchors.fill: parent - gradient: Gradient { + Gradient { + id: bgGradient GradientStop { position: 1.0; color: backgroundGradientColorStart } GradientStop { position: 0.0; color: backgroundGradientColorEnd } } + Rectangle { + id: viewRect0 + width: splitView ? parent.width / 2 : parent.width + height: splitView ? parent.height / 2 : parent.height + x: 0 + y: 0 + gradient: bgGradient + OverlayView3D { + id: overlayView0 + editView: viewRoot.editViews[0] + viewRoot: viewRoot + importScene: overlayScene + + + onChangeObjectProperty: (nodes, props) => { + viewRoot.changeObjectProperty(nodes, props); + } + onCommitObjectProperty: (nodes, props) => { + viewRoot.commitObjectProperty(nodes, props); + } + } + EditCameraController { + id: cameraControl0 + viewRoot: viewRoot + splitId: 0 + } + } + + Rectangle { + id: viewRect1 + width: parent.width / 2 + height: parent.height / 2 + x: parent.width / 2 + y: 0 + visible: splitView + gradient: bgGradient + OverlayView3D { + id: overlayView1 + editView: viewRoot.editViews[1] + viewRoot: viewRoot + importScene: overlayScene + + onChangeObjectProperty: (nodes, props) => { + viewRoot.changeObjectProperty(nodes, props); + } + onCommitObjectProperty: (nodes, props) => { + viewRoot.commitObjectProperty(nodes, props); + } + } + EditCameraController { + id: cameraControl1 + viewRoot: viewRoot + splitId: 1 + } + } + + Rectangle { + id: viewRect2 + width: parent.width / 2 + height: parent.height / 2 + x: 0 + y: parent.height / 2 + visible: splitView + gradient: bgGradient + OverlayView3D { + id: overlayView2 + editView: viewRoot.editViews[2] + viewRoot: viewRoot + importScene: overlayScene + + onChangeObjectProperty: (nodes, props) => { + viewRoot.changeObjectProperty(nodes, props); + } + onCommitObjectProperty: (nodes, props) => { + viewRoot.commitObjectProperty(nodes, props); + } + } + EditCameraController { + id: cameraControl2 + viewRoot: viewRoot + splitId: 2 + } + } + + Rectangle { + id: viewRect3 + width: parent.width / 2 + height: parent.height / 2 + x: parent.width / 2 + y: parent.height / 2 + visible: splitView + gradient: bgGradient + OverlayView3D { + id: overlayView3 + editView: viewRoot.editViews[3] + viewRoot: viewRoot + importScene: overlayScene + + onChangeObjectProperty: (nodes, props) => { + viewRoot.changeObjectProperty(nodes, props); + } + onCommitObjectProperty: (nodes, props) => { + viewRoot.commitObjectProperty(nodes, props); + } + } + EditCameraController { + id: cameraControl3 + viewRoot: viewRoot + splitId: 3 + } + } + + Rectangle { + // Vertical border between splits + visible: splitView + x: parent.width / 2 + y: 0 + width: 1 + height: parent.height + border.width: 1 + border.color: "#aaaaaa" + } + + Rectangle { + // Horizontal border between splits + visible: splitView + x: 0 + y: parent.height / 2 + height: 1 + width: parent.width + border.width: 1 + border.color: "#aaaaaa" + } + MouseArea { anchors.fill: parent acceptedButtons: Qt.LeftButton | Qt.RightButton hoverEnabled: false + z: -10 property MouseArea3D freeDraggerArea property point pressPoint property bool initialMoveBlock: false onPressed: (mouse) => { - if (viewRoot.editView) { + viewRoot.updateActiveSplit(mouse.x, mouse.y); + + let splitPoint = viewRoot.resolveSplitPoint(mouse.x, mouse.y); + + if (viewRoot.activeEditView) { // First pick overlay to check for hits there - var pickResult = _generalHelper.pickViewAt(overlayView, mouse.x, mouse.y); + var pickResult = _generalHelper.pickViewAt(activeOverlayView, + splitPoint.x, splitPoint.y); var resolvedResult = _generalHelper.resolvePick(pickResult.objectHit); + if (!resolvedResult) { // No hits from overlay view, pick the main scene - pickResult = _generalHelper.pickViewAt(viewRoot.editView, mouse.x, mouse.y); + pickResult = _generalHelper.pickViewAt(viewRoot.activeEditView, + splitPoint.x, splitPoint.y); resolvedResult = _generalHelper.resolvePick(pickResult.objectHit); } - handleObjectClicked(resolvedResult, mouse.button, mouse.modifiers & Qt.ControlModifier); + handleObjectClicked(resolvedResult, mouse.button, + mouse.modifiers & Qt.ControlModifier); if (pickResult.objectHit && pickResult.objectHit instanceof Node) { if (transformMode === EditView3D.TransformMode.Move) - freeDraggerArea = moveGizmo.freeDraggerArea; + freeDraggerArea = activeOverlayView.moveGizmo.freeDraggerArea; else if (transformMode === EditView3D.TransformMode.Rotate) - freeDraggerArea = rotateGizmo.freeDraggerArea; + freeDraggerArea = activeOverlayView.rotateGizmo.freeDraggerArea; else if (transformMode === EditView3D.TransformMode.Scale) - freeDraggerArea = scaleGizmo.freeDraggerArea; + freeDraggerArea = activeOverlayView.scaleGizmo.freeDraggerArea; pressPoint.x = mouse.x; pressPoint.y = mouse.y; initialMoveBlock = true; @@ -994,14 +801,17 @@ Item { } onPositionChanged: (mouse) => { if (freeDraggerArea) { - if (initialMoveBlock && Math.abs(pressPoint.x - mouse.x) + Math.abs(pressPoint.y - mouse.y) > 10) { + let splitPoint = viewRoot.resolveSplitPoint(mouse.x, mouse.y); + let splitPress = viewRoot.resolveSplitPoint(pressPoint.x, pressPoint.y); + if (initialMoveBlock && Math.abs(splitPress.x - splitPoint.x) + + Math.abs(splitPress.y - splitPoint.y) > 10) { // Don't force press event at actual press, as that puts the gizmo // in free-dragging state, which is bad UX if drag is not actually done - freeDraggerArea.forcePressEvent(pressPoint.x, pressPoint.y); - freeDraggerArea.forceMoveEvent(mouse.x, mouse.y); + freeDraggerArea.forcePressEvent(splitPress.x, splitPress.y); + freeDraggerArea.forceMoveEvent(splitPoint.x, splitPoint.y); initialMoveBlock = false; } else { - freeDraggerArea.forceMoveEvent(mouse.x, mouse.y); + freeDraggerArea.forceMoveEvent(splitPoint.x, splitPoint.y); } } } @@ -1009,10 +819,13 @@ Item { function handleRelease(mouse) { if (freeDraggerArea) { - if (initialMoveBlock) - freeDraggerArea.forceReleaseEvent(pressPoint.x, pressPoint.y); - else - freeDraggerArea.forceReleaseEvent(mouse.x, mouse.y); + if (initialMoveBlock) { + let splitPress = viewRoot.resolveSplitPoint(pressPoint.x, pressPoint.y); + freeDraggerArea.forceReleaseEvent(splitPress.x, splitPress.y); + } else { + let splitPoint = viewRoot.resolveSplitPoint(mouse.x, mouse.y); + freeDraggerArea.forceReleaseEvent(splitPoint.x, splitPoint.y); + } freeDraggerArea = null; } } @@ -1029,27 +842,13 @@ Item { anchors.fill: parent } - View3D { - id: overlayView - anchors.fill: parent - camera: viewRoot.usePerspective ? overlayPerspectiveCamera : overlayOrthoCamera - importScene: overlayScene - z: 2 - - environment: sceneEnv - SceneEnvironment { - id: sceneEnv - antialiasingMode: SceneEnvironment.MSAA - antialiasingQuality: SceneEnvironment.High - } - } - Overlay2D { id: gizmoLabel - targetNode: moveGizmo.visible ? moveGizmo : scaleGizmo - targetView: overlayView + targetNode: activeOverlayView.moveGizmo.visible ? activeOverlayView.moveGizmo + : activeOverlayView.scaleGizmo + targetView: activeOverlayView visible: targetNode.dragging - z: 3 + z: 300 function updateLabel() { @@ -1058,7 +857,7 @@ Item { if (!gizmoLabel.visible || !viewRoot.selectedNode || shuttingDown) return; var targetProperty; - if (gizmoLabel.targetNode === moveGizmo) + if (gizmoLabel.targetNode === activeOverlayView.moveGizmo) gizmoLabelText.text = _generalHelper.snapPositionDragTooltip(viewRoot.selectedNode.position); else gizmoLabelText.text = _generalHelper.snapScaleDragTooltip(viewRoot.selectedNode.scale); @@ -1089,26 +888,42 @@ Item { Rectangle { id: rotateGizmoLabel color: "white" - x: rotateGizmo.currentMousePos.x - (10 + width) - y: rotateGizmo.currentMousePos.y - (10 + height) width: rotateGizmoLabelText.width + 4 height: rotateGizmoLabelText.height + 4 border.width: 1 - visible: rotateGizmo.dragging - parent: rotateGizmo.view3D - z: 3 + visible: activeOverlayView.rotateGizmo.dragging + z: 300 - function updateLabel() { + Connections { + target: activeOverlayView.rotateGizmo + function onCurrentMousePosChanged() + { + let newPos = viewContainer.mapFromItem( + activeOverlayView, + activeOverlayView.rotateGizmo.currentMousePos.x, + activeOverlayView.rotateGizmo.currentMousePos.y); + rotateGizmoLabel.x = newPos.x - (10 + rotateGizmoLabel.width); + rotateGizmoLabel.y = newPos.y - (10 + rotateGizmoLabel.height); + } + } + + function updateLabel() + { // This is skipped during application shutdown, as calling QQuickText::setText() // during application shutdown can crash the application. - if (!rotateGizmoLabel.visible || !rotateGizmo.targetNode || shuttingDown) + if (!rotateGizmoLabel.visible || !activeOverlayView.rotateGizmo.targetNode || shuttingDown) return; - var degrees = rotateGizmo.currentAngle * (180 / Math.PI); + var degrees = activeOverlayView.rotateGizmo.currentAngle * (180 / Math.PI); rotateGizmoLabelText.text = _generalHelper.snapRotationDragTooltip(degrees); } onVisibleChanged: rotateGizmoLabel.updateLabel() + Connections { + target: activeOverlayView.rotateGizmo + function onCurrentAngleChanged() { rotateGizmoLabel.updateLabel() } + } + Text { id: rotateGizmoLabelText anchors.centerIn: parent @@ -1118,42 +933,31 @@ Item { Rectangle { id: lightGizmoLabel color: "white" - x: lightGizmo.currentMousePos.x - (10 + width) - y: lightGizmo.currentMousePos.y - (10 + height) width: lightGizmoLabelText.width + 4 height: lightGizmoLabelText.height + 4 border.width: 1 - visible: lightGizmo.dragging - parent: lightGizmo.view3D - z: 3 + visible: activeOverlayView.lightGizmo.dragging + z: 300 + + Connections { + target: activeOverlayView.lightGizmo + function onCurrentMousePosChanged() + { + let newPos = viewContainer.mapFromItem( + activeOverlayView, + activeOverlayView.lightGizmo.currentMousePos.x, + activeOverlayView.lightGizmo.currentMousePos.y); + lightGizmoLabel.x = newPos.x - (10 + lightGizmoLabel.width); + lightGizmoLabel.y = newPos.y - (10 + lightGizmoLabel.height); + } + } Text { id: lightGizmoLabelText - text: lightGizmo.currentLabel + text: activeOverlayView.lightGizmo.currentLabel anchors.centerIn: parent } } - - EditCameraController { - id: cameraControl - camera: viewRoot.editView ? viewRoot.editView.camera : null - anchors.fill: parent - view3d: viewRoot.editView - sceneId: viewRoot.sceneId - } - } - - OriginGizmo { - anchors.right: parent.right - anchors.top: parent.top - anchors.margins: 10 - width: 120 - height: 120 - targetNode: cameraControl.camera - - onAxisClicked: (axis) => { - cameraControl.jumpToRotation(quaternionForAxis(axis)); - } } Text { diff --git a/src/tools/qml2puppet/mockfiles/qt6/Overlay2D.qml b/src/tools/qml2puppet/mockfiles/qt6/Overlay2D.qml index affbb8f25c2..1ffd7d6be93 100644 --- a/src/tools/qml2puppet/mockfiles/qt6/Overlay2D.qml +++ b/src/tools/qml2puppet/mockfiles/qt6/Overlay2D.qml @@ -37,11 +37,13 @@ Item { var scenePosWithOffset = Qt.vector3d(scenePos.x + offset.x, scenePos.y + offset.y, scenePos.z + offset.z); - var viewPos = targetView ? targetView.mapFrom3DScene(scenePosWithOffset) - : Qt.vector3d(0, 0, 0); - root.x = viewPos.x; - root.y = viewPos.y; - isBehindCamera = viewPos.z <= 0; + if (targetView) { + var viewPos = targetView.mapFrom3DScene(scenePosWithOffset); + let newPos = parent.mapFromItem(targetView, viewPos.x, viewPos.y); + x = newPos.x; + y = newPos.y; + isBehindCamera = viewPos.z <= 0; + } } } diff --git a/src/tools/qml2puppet/mockfiles/qt6/OverlayView3D.qml b/src/tools/qml2puppet/mockfiles/qt6/OverlayView3D.qml new file mode 100644 index 00000000000..13a37f4f723 --- /dev/null +++ b/src/tools/qml2puppet/mockfiles/qt6/OverlayView3D.qml @@ -0,0 +1,541 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import QtQuick +import QtQuick3D +import MouseArea3D + +View3D { + id: overlayView + + property alias moveGizmo: moveGizmo + property alias rotateGizmo: rotateGizmo + property alias scaleGizmo: scaleGizmo + property alias lightGizmo: lightGizmo + + property var viewRoot: null + property View3D editView: null + property bool isActive: viewRoot.overlayViews[viewRoot.activeSplit] === overlayView + + property var lightIconGizmos: [] + property var cameraGizmos: [] + property var particleSystemIconGizmos: [] + property var particleEmitterGizmos: [] + + signal commitObjectProperty(var objects, var propNames) + signal changeObjectProperty(var objects, var propNames) + + camera: viewRoot.usePerspective ? overlayPerspectiveCamera : overlayOrthoCamera + + anchors.fill: parent + z: 2 + clip: true + + environment: sceneEnv + + function addLightGizmo(scene, obj) + { + // Insert into first available gizmo if we don't already have gizmo for this object + var slotFound = -1; + for (var i = 0; i < lightIconGizmos.length; ++i) { + if (!lightIconGizmos[i].targetNode) { + slotFound = i; + } else if (lightIconGizmos[i].targetNode === obj) { + lightIconGizmos[i].scene = scene; + return; + } + } + + if (slotFound !== -1) { + lightIconGizmos[slotFound].scene = scene; + lightIconGizmos[slotFound].targetNode = obj; + lightIconGizmos[slotFound].locked = _generalHelper.isLocked(obj); + lightIconGizmos[slotFound].hidden = _generalHelper.isHidden(obj); + return; + } + + // No free gizmos available, create a new one + var gizmoComponent = Qt.createComponent("LightIconGizmo.qml"); + if (gizmoComponent.status === Component.Ready) { + var gizmo = gizmoComponent.createObject(overlayView, + {"view3D": overlayView, + "targetNode": obj, + "selectedNodes": viewRoot.selectedNodes, + "scene": scene, + "activeScene": viewRoot.activeScene, + "locked": _generalHelper.isLocked(obj), + "hidden": _generalHelper.isHidden(obj), + "globalShow": viewRoot.showIconGizmo}); + lightIconGizmos[lightIconGizmos.length] = gizmo; + gizmo.clicked.connect(viewRoot.handleObjectClicked); + gizmo.selectedNodes = Qt.binding(function() {return viewRoot.selectedNodes;}); + gizmo.activeScene = Qt.binding(function() {return viewRoot.activeScene;}); + gizmo.globalShow = Qt.binding(function() {return viewRoot.showIconGizmo;}); + } + } + + function releaseLightGizmo(obj) + { + for (var i = 0; i < lightIconGizmos.length; ++i) { + if (lightIconGizmos[i].targetNode === obj) { + lightIconGizmos[i].scene = null; + lightIconGizmos[i].targetNode = null; + return; + } + } + } + + function updateLightGizmoScene(scene, obj) + { + for (var i = 0; i < lightIconGizmos.length; ++i) { + if (lightIconGizmos[i].targetNode === obj) { + lightIconGizmos[i].scene = scene; + return; + } + } + } + + function addCameraGizmo(scene, obj) + { + // Insert into first available gizmo if we don't already have gizmo for this object + var slotFound = -1; + for (var i = 0; i < cameraGizmos.length; ++i) { + if (!cameraGizmos[i].targetNode) { + slotFound = i; + } else if (cameraGizmos[i].targetNode === obj) { + cameraGizmos[i].scene = scene; + return; + } + } + + if (slotFound !== -1) { + cameraGizmos[slotFound].scene = scene; + cameraGizmos[slotFound].targetNode = obj; + cameraGizmos[slotFound].locked = _generalHelper.isLocked(obj); + cameraGizmos[slotFound].hidden = _generalHelper.isHidden(obj); + return; + } + + // No free gizmos available, create a new one + var gizmoComponent = Qt.createComponent("CameraGizmo.qml"); + var frustumComponent = Qt.createComponent("CameraFrustum.qml"); + if (gizmoComponent.status === Component.Ready && frustumComponent.status === Component.Ready) { + var geometryName = _generalHelper.generateUniqueName("CameraGeometry"); + var frustum = frustumComponent.createObject( + sceneNode, + {"geometryName": geometryName, "viewPortRect": viewRoot.viewPortRect}); + var gizmo = gizmoComponent.createObject( + overlayView, + {"view3D": overlayView, "targetNode": obj, + "selectedNodes": viewRoot.selectedNodes, "scene": scene, "activeScene": viewRoot.activeScene, + "locked": _generalHelper.isLocked(obj), "hidden": _generalHelper.isHidden(obj), + "globalShow": viewRoot.showIconGizmo, "globalShowFrustum": viewRoot.showCameraFrustum}); + + cameraGizmos[cameraGizmos.length] = gizmo; + gizmo.clicked.connect(viewRoot.handleObjectClicked); + gizmo.selectedNodes = Qt.binding(function() {return viewRoot.selectedNodes;}); + gizmo.activeScene = Qt.binding(function() {return viewRoot.activeScene;}); + gizmo.globalShow = Qt.binding(function() {return viewRoot.showIconGizmo;}); + gizmo.globalShowFrustum = Qt.binding(function() {return viewRoot.showCameraFrustum;}); + frustum.viewPortRect = Qt.binding(function() {return viewRoot.viewPortRect;}); + gizmo.connectFrustum(frustum); + } + } + + function releaseCameraGizmo(obj) + { + for (var i = 0; i < cameraGizmos.length; ++i) { + if (cameraGizmos[i].targetNode === obj) { + cameraGizmos[i].scene = null; + cameraGizmos[i].targetNode = null; + return; + } + } + } + + function updateCameraGizmoScene(scene, obj) + { + for (var i = 0; i < cameraGizmos.length; ++i) { + if (cameraGizmos[i].targetNode === obj) { + cameraGizmos[i].scene = scene; + return; + } + } + } + + function addParticleSystemGizmo(scene, obj) + { + // Insert into first available gizmo if we don't already have gizmo for this object + var slotFound = -1; + for (var i = 0; i < particleSystemIconGizmos.length; ++i) { + if (!particleSystemIconGizmos[i].targetNode) { + slotFound = i; + } else if (particleSystemIconGizmos[i].targetNode === obj) { + particleSystemIconGizmos[i].scene = scene; + return; + } + } + + if (slotFound !== -1) { + particleSystemIconGizmos[slotFound].scene = scene; + particleSystemIconGizmos[slotFound].targetNode = obj; + particleSystemIconGizmos[slotFound].locked = _generalHelper.isLocked(obj); + particleSystemIconGizmos[slotFound].hidden = _generalHelper.isHidden(obj); + return; + } + + // No free gizmos available, create a new one + var gizmoComponent = Qt.createComponent("ParticleSystemGizmo.qml"); + if (gizmoComponent.status === Component.Ready) { + var gizmo = gizmoComponent.createObject(overlayView, + {"view3D": overlayView, + "targetNode": obj, + "selectedNodes": viewRoot.selectedNodes, + "scene": scene, + "activeScene": viewRoot.activeScene, + "locked": _generalHelper.isLocked(obj), + "hidden": _generalHelper.isHidden(obj), + "globalShow": viewRoot.showIconGizmo, + "activeParticleSystem": viewRoot.activeParticleSystem}); + particleSystemIconGizmos[particleSystemIconGizmos.length] = gizmo; + gizmo.clicked.connect(viewRoot.handleObjectClicked); + gizmo.selectedNodes = Qt.binding(function() {return viewRoot.selectedNodes;}); + gizmo.activeScene = Qt.binding(function() {return viewRoot.activeScene;}); + gizmo.globalShow = Qt.binding(function() {return viewRoot.showIconGizmo;}); + gizmo.activeParticleSystem = Qt.binding(function() {return viewRoot.activeParticleSystem;}); + } + } + + function releaseParticleSystemGizmo(obj) + { + for (var i = 0; i < particleSystemIconGizmos.length; ++i) { + if (particleSystemIconGizmos[i].targetNode === obj) { + particleSystemIconGizmos[i].scene = null; + particleSystemIconGizmos[i].targetNode = null; + return; + } + } + } + + function updateParticleSystemGizmoScene(scene, obj) + { + for (var i = 0; i < particleSystemIconGizmos.length; ++i) { + if (particleSystemIconGizmos[i].targetNode === obj) { + particleSystemIconGizmos[i].scene = scene; + return; + } + } + } + + function addParticleEmitterGizmo(scene, obj) + { + // Insert into first available gizmo if we don't already have gizmo for this object + var slotFound = -1; + for (var i = 0; i < particleEmitterGizmos.length; ++i) { + if (!particleEmitterGizmos[i].targetNode) { + slotFound = i; + } else if (particleEmitterGizmos[i].targetNode === obj) { + particleEmitterGizmos[i].scene = scene; + return; + } + } + + if (slotFound !== -1) { + particleEmitterGizmos[slotFound].scene = scene; + particleEmitterGizmos[slotFound].targetNode = obj; + particleEmitterGizmos[slotFound].hidden = _generalHelper.isHidden(obj); + particleEmitterGizmos[slotFound].systemHidden = _generalHelper.isHidden(obj.system); + return; + } + + // No free gizmos available, create a new one + var gizmoComponent = Qt.createComponent("ParticleEmitterGizmo.qml"); + if (gizmoComponent.status === Component.Ready) { + var gizmo = gizmoComponent.createObject( + overlayScene, + {"view3d": overlayView, "targetNode": obj, "selectedNodes": viewRoot.selectedNodes, + "activeParticleSystem": viewRoot.activeParticleSystem, "scene": scene, + "activeScene": viewRoot.activeScene, "hidden": _generalHelper.isHidden(obj), + "systemHidden": _generalHelper.isHidden(obj.system), + "globalShow": viewRoot.showParticleEmitter}); + + particleEmitterGizmos[particleEmitterGizmos.length] = gizmo; + gizmo.selectedNodes = Qt.binding(function() {return viewRoot.selectedNodes;}); + gizmo.activeParticleSystem = Qt.binding(function() {return viewRoot.activeParticleSystem;}); + gizmo.globalShow = Qt.binding(function() {return viewRoot.showParticleEmitter;}); + gizmo.activeScene = Qt.binding(function() {return viewRoot.activeScene;}); + } + } + + function releaseParticleEmitterGizmo(obj) + { + for (var i = 0; i < particleEmitterGizmos.length; ++i) { + if (particleEmitterGizmos[i].targetNode === obj) { + particleEmitterGizmos[i].scene = null; + particleEmitterGizmos[i].targetNode = null; + return; + } + } + } + + function updateParticleEmitterGizmoScene(scene, obj) + { + for (var i = 0; i < particleEmitterGizmos.length; ++i) { + if (particleEmitterGizmos[i].targetNode === obj) { + particleEmitterGizmos[i].scene = scene; + return; + } + } + } + + function gizmoAt(x, y) + { + for (var i = 0; i < lightIconGizmos.length; ++i) { + if (lightIconGizmos[i].visible && lightIconGizmos[i].hasPoint(x, y)) + return lightIconGizmos[i].targetNode; + } + for (var i = 0; i < cameraGizmos.length; ++i) { + if (cameraGizmos[i].visible && cameraGizmos[i].hasPoint(x, y)) + return cameraGizmos[i].targetNode; + } + for (var i = 0; i < particleSystemIconGizmos.length; ++i) { + if (particleSystemIconGizmos[i].visible && particleSystemIconGizmos[i].hasPoint(x, y)) + return particleSystemIconGizmos[i].targetNode; + } + return null; + } + + function handleLockedStateChange(node) + { + for (var i = 0; i < lightIconGizmos.length; ++i) { + if (lightIconGizmos[i].targetNode === node) { + lightIconGizmos[i].locked = _generalHelper.isLocked(node); + return; + } + } + for (var i = 0; i < cameraGizmos.length; ++i) { + if (cameraGizmos[i].targetNode === node) { + cameraGizmos[i].locked = _generalHelper.isLocked(node); + return; + } + } + for (var i = 0; i < particleSystemIconGizmos.length; ++i) { + if (particleSystemIconGizmos[i].targetNode === node) { + particleSystemIconGizmos[i].locked = _generalHelper.isLocked(node); + return; + } + } + } + + function handleHiddenStateChange(node) + { + for (var i = 0; i < lightIconGizmos.length; ++i) { + if (lightIconGizmos[i].targetNode === node) { + lightIconGizmos[i].hidden = _generalHelper.isHidden(node); + return; + } + } + for (var i = 0; i < cameraGizmos.length; ++i) { + if (cameraGizmos[i].targetNode === node) { + cameraGizmos[i].hidden = _generalHelper.isHidden(node); + return; + } + } + for (var i = 0; i < particleSystemIconGizmos.length; ++i) { + if (particleSystemIconGizmos[i].targetNode === node) { + particleSystemIconGizmos[i].hidden = _generalHelper.isHidden(node); + return; + } + } + for (var i = 0; i < particleEmitterGizmos.length; ++i) { + if (particleEmitterGizmos[i].targetNode === node) { + particleEmitterGizmos[i].hidden = _generalHelper.isHidden(node); + return; + } else if (particleEmitterGizmos[i].targetNode && particleEmitterGizmos[i].targetNode.system === node) { + particleEmitterGizmos[i].systemHidden = _generalHelper.isHidden(node); + return; + } + } + } + + SceneEnvironment { + id: sceneEnv + antialiasingMode: SceneEnvironment.MSAA + antialiasingQuality: SceneEnvironment.High + } + + Node { + id: sceneNode + + PerspectiveCamera { + id: overlayPerspectiveCamera + clipFar: overlayView.editView ? overlayView.editView.perspectiveCamera.clipFar : 1000 + clipNear: overlayView.editView ? overlayView.editView.perspectiveCamera.clipNear : 1 + position: overlayView.editView ? overlayView.editView.perspectiveCamera.position : Qt.vector3d(0, 0, 0) + rotation: overlayView.editView ? overlayView.editView.perspectiveCamera.rotation : Qt.quaternion(1, 0, 0, 0) + } + + OrthographicCamera { + id: overlayOrthoCamera + clipFar: overlayView.editView ? overlayView.editView.orthoCamera.clipFar : 1000 + clipNear: overlayView.editView ? overlayView.editView.orthoCamera.clipNear : 1 + position: overlayView.editView ? overlayView.editView.orthoCamera.position : Qt.vector3d(0, 0, 0) + rotation: overlayView.editView ? overlayView.editView.orthoCamera.rotation : Qt.quaternion(1, 0, 0, 0) + horizontalMagnification: overlayView.editView ? overlayView.editView.orthoCamera.horizontalMagnification : 1 + verticalMagnification: overlayView.editView ? overlayView.editView.orthoCamera.verticalMagnification : 1 + } + + MouseArea3D { + id: gizmoDragHelper + view3D: overlayView + } + + AutoScaleHelper { + id: autoScale + view3D: overlayView + position: moveGizmo.scenePosition + } + + AutoScaleHelper { + id: pivotAutoScale + view3D: overlayView + position: pivotLine.startPos + } + + MoveGizmo { + id: moveGizmo + scale: autoScale.getScale(Qt.vector3d(5, 5, 5)) + highlightOnHover: true + targetNode: viewRoot.selectedNode + globalOrientation: viewRoot.globalOrientation + visible: viewRoot.selectedNode && viewRoot.transformMode === EditView3D.TransformMode.Move + && overlayView.isActive + view3D: overlayView + dragHelper: gizmoDragHelper + property var propertyNames: ["position"] + + onPositionCommit: { + if (targetNode === viewRoot.multiSelectionNode) + overlayView.commitObjectProperty(_generalHelper.multiSelectionTargets(), propertyNames); + else + overlayView.commitObjectProperty([viewRoot.selectedNode], propertyNames); + } + onPositionMove: { + if (targetNode === viewRoot.multiSelectionNode) + overlayView.changeObjectProperty(_generalHelper.multiSelectionTargets(), propertyNames); + else + overlayView.changeObjectProperty([viewRoot.selectedNode], propertyNames); + } + } + + ScaleGizmo { + id: scaleGizmo + scale: autoScale.getScale(Qt.vector3d(5, 5, 5)) + highlightOnHover: true + targetNode: viewRoot.selectedNode + visible: viewRoot.selectedNode && viewRoot.transformMode === EditView3D.TransformMode.Scale + && overlayView.isActive + view3D: overlayView + dragHelper: gizmoDragHelper + property var propertyNames: ["scale"] + property var propertyNamesMulti: ["position", "scale"] + + onScaleCommit: { + if (targetNode === viewRoot.multiSelectionNode) + overlayView.commitObjectProperty(_generalHelper.multiSelectionTargets(), propertyNamesMulti); + else + overlayView.commitObjectProperty([viewRoot.selectedNode], propertyNames); + } + onScaleChange: { + if (targetNode === viewRoot.multiSelectionNode) + overlayView.changeObjectProperty(_generalHelper.multiSelectionTargets(), propertyNamesMulti); + else + overlayView.changeObjectProperty([viewRoot.selectedNode], propertyNames); + } + } + + RotateGizmo { + id: rotateGizmo + scale: autoScale.getScale(Qt.vector3d(7, 7, 7)) + highlightOnHover: true + targetNode: viewRoot.selectedNode + globalOrientation: viewRoot.globalOrientation + visible: viewRoot.selectedNode && viewRoot.transformMode === EditView3D.TransformMode.Rotate + && overlayView.isActive + view3D: overlayView + dragHelper: gizmoDragHelper + property var propertyNames: ["eulerRotation"] + property var propertyNamesMulti: ["position", "eulerRotation"] + + onRotateCommit: { + if (targetNode === viewRoot.multiSelectionNode) + overlayView.commitObjectProperty(_generalHelper.multiSelectionTargets(), propertyNamesMulti); + else + overlayView.commitObjectProperty([viewRoot.selectedNode], propertyNames); + } + onRotateChange: { + if (targetNode === viewRoot.multiSelectionNode) + overlayView.changeObjectProperty(_generalHelper.multiSelectionTargets(), propertyNamesMulti); + else + overlayView.changeObjectProperty([viewRoot.selectedNode], propertyNames); + } + onCurrentAngleChanged: rotateGizmoLabel.updateLabel() + } + + LightGizmo { + id: lightGizmo + targetNode: viewRoot.selectedNode !== viewRoot.multiSelectionNode ? viewRoot.selectedNode : null + view3D: overlayView + dragHelper: gizmoDragHelper + + onPropertyValueCommit: (propName) => { + overlayView.commitObjectProperty([targetNode], [propName]); + } + onPropertyValueChange: (propName) => { + overlayView.changeObjectProperty([targetNode], [propName]); + } + } + + Line3D { + id: pivotLine + visible: viewRoot.selectedNode && viewRoot.selectedNode !== viewRoot.multiSelectionNode + && overlayView.isActive + name: "3D Edit View Pivot Line" + color: "#ddd600" + + startPos: viewRoot.selectedNode ? viewRoot.selectedNode.scenePosition + : Qt.vector3d(0, 0, 0) + + Connections { + target: viewRoot + function onSelectedNodeChanged() + { + pivotLine.endPos = gizmoDragHelper.pivotScenePosition(viewRoot.selectedNode); + } + } + Connections { + target: viewRoot.selectedNode + function onSceneTransformChanged() + { + pivotLine.endPos = gizmoDragHelper.pivotScenePosition(viewRoot.selectedNode); + } + } + + Model { + id: pivotCap + readonly property bool _edit3dLocked: true // Make this non-pickable + source: "#Sphere" + scale: pivotAutoScale.getScale(Qt.vector3d(0.03, 0.03, 0.03)) + position: pivotLine.startPos + materials: [ + DefaultMaterial { + id: lineMat + lighting: DefaultMaterial.NoLighting + cullMode: Material.NoCulling + diffuseColor: pivotLine.color + } + ] + } + } + } +} diff --git a/src/tools/qml2puppet/mockfiles/qt6/ParticleEmitterGizmo.qml b/src/tools/qml2puppet/mockfiles/qt6/ParticleEmitterGizmo.qml index 17e5123c101..c444e4ca9de 100644 --- a/src/tools/qml2puppet/mockfiles/qt6/ParticleEmitterGizmo.qml +++ b/src/tools/qml2puppet/mockfiles/qt6/ParticleEmitterGizmo.qml @@ -11,6 +11,7 @@ import QtQuick3D.Particles3D Node { id: root + property View3D view3d: null property Node targetNode: null property var selectedNodes: [] property Node activeParticleSystem: null @@ -97,7 +98,7 @@ Node { AutoScaleHelper { id: autoScale - view3D: overlayView + view3D: root.view3d } DefaultMaterial { diff --git a/src/tools/qml2puppet/mockfiles/qt6/SceneView3D.qml b/src/tools/qml2puppet/mockfiles/qt6/SceneView3D.qml index 6945579bf12..6b2cda1398d 100644 --- a/src/tools/qml2puppet/mockfiles/qt6/SceneView3D.qml +++ b/src/tools/qml2puppet/mockfiles/qt6/SceneView3D.qml @@ -12,11 +12,11 @@ View3D { property alias showSceneLight: sceneLight.visible property alias showGrid: helperGrid.visible property alias gridColor: helperGrid.gridColor - property alias sceneHelpers: sceneHelpers property alias perspectiveCamera: scenePerspectiveCamera property alias orthoCamera: sceneOrthoCamera property alias sceneEnv: sceneEnv property vector3d cameraLookAt + property var selectionBoxes: [] // Measuring the distance from camera to lookAt plus the distance of lookAt from grid plane // gives a reasonable grid spacing in most cases while keeping spacing constant when @@ -38,6 +38,24 @@ View3D { camera: usePerspective ? scenePerspectiveCamera : sceneOrthoCamera environment: sceneEnv + + function ensureSelectionBoxes(count) + { + var needMore = count - selectionBoxes.length + if (needMore > 0) { + var component = Qt.createComponent("SelectionBox.qml"); + if (component.status === Component.Ready) { + for (let i = 0; i < needMore; ++i) { + let geometryName = _generalHelper.generateUniqueName("SelectionBoxGeometry"); + let box = component.createObject(sceneHelpers, {"view3D": sceneView, + "geometryName": geometryName}); + selectionBoxes[selectionBoxes.length] = box; + box.showBox = Qt.binding(function() {return showSelectionBox;}); + } + } + } + } + SceneEnvironment { id: sceneEnv antialiasingMode: SceneEnvironment.MSAA diff --git a/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp b/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp index f39263f65fe..990e03fa014 100644 --- a/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp +++ b/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp @@ -428,7 +428,7 @@ void Qt5InformationNodeInstanceServer::getNodeAtPos([[maybe_unused]] const QPoin if (!helper) return; - QQmlProperty editViewProp(m_editView3DData.rootItem, "editView", context()); + QQmlProperty editViewProp(m_editView3DData.rootItem, "activeEditView", context()); QObject *obj = qvariant_cast(editViewProp.read()); QQuick3DViewport *editView = qobject_cast(obj); @@ -2220,7 +2220,7 @@ void Qt5InformationNodeInstanceServer::changeSelection(const ChangeSelectionComm // Ensure the UI has enough selection box items. If it doesn't yet have them, which can be the // case when the first selection processed is a multiselection, we wait a bit as // using the new boxes immediately leads to visual glitches. - int boxCount = m_editView3DData.rootItem->property("selectionBoxes").value().size(); + int boxCount = m_editView3DData.rootItem->property("selectionBoxCount").toInt(); if (boxCount < selectedObjs.size()) { QMetaObject::invokeMethod(m_editView3DData.rootItem, "ensureSelectionBoxes", Q_ARG(QVariant, QVariant::fromValue(selectedObjs.size()))); From ecba81097cc39b386ec53348659f5dba4c3fbd18 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Tue, 17 Oct 2023 19:10:32 +0200 Subject: [PATCH 064/242] Nanotrace: Add flows Change-Id: If9c6642449a495f2fcc94f843b7fcd7bf2ab4548 Reviewed-by: Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Thomas Hartmann --- src/libs/nanotrace/nanotracehr.cpp | 33 +- src/libs/nanotrace/nanotracehr.h | 653 ++++++++++++++++-- src/libs/sqlite/sqlitebasestatement.cpp | 57 +- .../imagecache/asynchronousimagecache.cpp | 8 +- .../imagecache/asynchronousimagefactory.cpp | 3 +- .../imagecache/imagecachecollectorinterface.h | 2 +- .../imagecache/imagecachegenerator.cpp | 64 +- .../imagecache/imagecachegenerator.h | 1 + .../imagecache/imagecachegeneratorinterface.h | 2 +- .../imagecache/meshimagecachecollector.h | 2 +- .../designercore/imagecache/taskqueue.h | 11 +- .../projectstorage/projectstorage.cpp | 12 +- .../projectstorage/projectstorage.h | 155 +++-- .../tracing/qmldesignertracing.cpp | 2 +- 14 files changed, 806 insertions(+), 199 deletions(-) diff --git a/src/libs/nanotrace/nanotracehr.cpp b/src/libs/nanotrace/nanotracehr.cpp index 5e04295920c..9e679e350c0 100644 --- a/src/libs/nanotrace/nanotracehr.cpp +++ b/src/libs/nanotrace/nanotracehr.cpp @@ -31,19 +31,38 @@ bool hasId(char phase) return false; } +template +unsigned int getUnsignedIntegerHash(Id id) +{ + return static_cast(id & 0xFFFFFFFF); +} + +unsigned int getUnsignedIntegerHash(std::thread::id id) +{ + return static_cast(std::hash{}(id) & 0xFFFFFFFF); +} + template void printEvent(std::ostream &out, const TraceEvent &event, qint64 processId, std::thread::id threadId) { out << R"({"ph":")" << event.type << R"(","name":")" << event.name << R"(","cat":")" << event.category << R"(","ts":)" << static_cast(event.time.time_since_epoch().count()) / 1000 << R"(,"pid":)" - << processId << R"(,"tid":)" << threadId; + << getUnsignedIntegerHash(processId) << R"(,"tid":)" << getUnsignedIntegerHash(threadId); if (event.type == 'X') out << R"(,"dur":)" << static_cast(event.duration.count()) / 1000; if (hasId(event.type)) - out << R"(,"id":")" << event.id << R"(")"; + out << R"(,"id":)" << event.id; + + if (event.bindId) { + out << R"(,"bind_id":)" << event.bindId; + if (event.flow & IsFlow::Out) + out << R"(,"flow_out":true)"; + if (event.flow & IsFlow::In) + out << R"(,"flow_in":true)"; + } if (event.arguments.size()) out << R"(,"args":)" << event.arguments; @@ -58,8 +77,9 @@ void writeMetaEvent(TraceFile *file, std::string_view key, s if (out.is_open()) { file->out << R"({"name":")" << key << R"(","ph":"M", "pid":)" - << QCoreApplication::applicationPid() << R"(,"tid":)" - << std::this_thread::get_id() << R"(,"args":{"name":")" << value << R"("}})" + << getUnsignedIntegerHash(QCoreApplication::applicationPid()) << R"(,"tid":)" + << getUnsignedIntegerHash(std::this_thread::get_id()) << R"(,"args":{"name":")" + << value << R"("}})" << ",\n"; } } @@ -150,7 +170,7 @@ void flushInThread(EnabledEventQueue &eventQueue) eventQueue.file->processing = std::async(std::launch::async, flush, eventQueue.currentEvents.subspan(0, eventQueue.eventsIndex), - std::this_thread::get_id()); + eventQueue.threadId); eventQueue.currentEvents = eventQueue.currentEvents.data() == eventQueue.eventsOne.data() ? eventQueue.eventsTwo : eventQueue.eventsOne; @@ -182,6 +202,7 @@ EventQueue::EventQueue(EnabledTraceFile *file, , eventsOne{eventsOne} , eventsTwo{eventsTwo} , currentEvents{eventsOne} + , threadId{std::this_thread::get_id()} { if (auto thread = QThread::currentThread()) { connection = QObject::connect(QCoreApplication::instance(), @@ -208,7 +229,7 @@ void EventQueue::flush() { std::lock_guard lock{mutex}; if (isEnabled == IsEnabled::Yes && eventsIndex > 0) { - flushEvents(currentEvents.subspan(0, eventsIndex), std::this_thread::get_id(), *this); + flushEvents(currentEvents.subspan(0, eventsIndex), threadId, *this); eventsIndex = 0; } } diff --git a/src/libs/nanotrace/nanotracehr.h b/src/libs/nanotrace/nanotracehr.h index 2f3552148c4..48aa61d37a1 100644 --- a/src/libs/nanotrace/nanotracehr.h +++ b/src/libs/nanotrace/nanotracehr.h @@ -259,6 +259,14 @@ template } // namespace Internal +enum class IsFlow : std::size_t { No = 0, Out = 1 << 0, In = 1 << 1, InOut = In | Out }; + +inline bool operator&(IsFlow first, IsFlow second) +{ + return static_cast>(first) + & static_cast>(second); +} + template struct TraceEvent { @@ -279,6 +287,8 @@ struct TraceEvent TimePoint time; Duration duration; std::size_t id = 0; + std::size_t bindId : 62; + IsFlow flow : 2; char type = ' '; }; @@ -391,6 +401,7 @@ public: IsEnabled isEnabled = IsEnabled::Yes; QMetaObject::Connection connection; std::mutex mutex; + std::thread::id threadId; }; extern template class NANOTRACE_EXPORT EventQueue; @@ -478,6 +489,151 @@ public: static constexpr bool isActive() { return false; } }; +template +class Tracer; + +template +class Token : public BasicDisabledToken +{ +public: + using ArgumentType = typename Category::ArgumentType; + using FlowTokenType = typename Category::FlowTokenType; + using AsynchronousTokenType = typename Category::AsynchronousTokenType; + using TracerType = typename Category::TracerType; + + Token() {} + + ~Token() {} + + template + [[nodiscard]] AsynchronousTokenType beginAsynchronous(ArgumentType, Arguments &&...) + { + return {}; + } + + template + [[nodiscard]] std::pair beginAsynchronousWithFlow( + ArgumentType, Arguments &&...) + { + return {}; + } + + template + [[nodiscard]] TracerType beginDuration(ArgumentType, Arguments &&...) + { + return {}; + } + + template + [[nodiscard]] std::pair beginDurationWithFlow(ArgumentType, + Arguments &&...) + { + return {}; + } + + template + void tick(ArgumentType, Arguments &&...) + {} +}; + +template +class Token : public BasicEnabledToken + +{ + using CategoryFunctionPointer = typename Category::CategoryFunctionPointer; + + Token(std::size_t id, CategoryFunctionPointer category) + : m_id{id} + , m_category{category} + {} + + using PrivateTag = typename Category::PrivateTag; + +public: + using ArgumentType = typename Category::ArgumentType; + using FlowTokenType = typename Category::FlowTokenType; + using AsynchronousTokenType = typename Category::AsynchronousTokenType; + using TracerType = typename Category::TracerType; + + Token(PrivateTag, std::size_t id, CategoryFunctionPointer category) + : Token{id, category} + {} + + friend TracerType; + friend AsynchronousTokenType; + + ~Token() {} + + template + [[nodiscard]] AsynchronousTokenType beginAsynchronous(ArgumentType name, Arguments &&...arguments) + { + if (m_id) + m_category().begin('b', m_id, name, 0, IsFlow::No, std::forward(arguments)...); + + return {std::move(name), m_id, m_category}; + } + + template + [[nodiscard]] std::pair beginAsynchronousWithFlow( + ArgumentType name, Arguments &&...arguments) + { + std::size_t bindId = 0; + + if (m_id) { + auto category = m_category(); + bindId = category.createBindId(); + category.begin('b', m_id, name, bindId, IsFlow::Out, std::forward(arguments)...); + } + + return {std::piecewise_construct, + std::forward_as_tuple(std::move(name), m_id, m_category), + std::forward_as_tuple(std::move(name), bindId, m_category)}; + } + + template + [[nodiscard]] TracerType beginDuration(ArgumentType traceName, Arguments &&...arguments) + { + return {traceName, m_category, std::forward(arguments)...}; + } + + template + [[nodiscard]] std::pair beginDurationWithFlow(ArgumentType traceName, + Arguments &&...arguments) + { + std::size_t bindId = m_category().createBindId(); + + return {std::piecewise_construct, + std::forward_as_tuple(PrivateTag{}, + bindId, + IsFlow::Out, + traceName, + m_category, + std::forward(arguments)...), + std::forward_as_tuple(PrivateTag{}, traceName, bindId, m_category)}; + } + + template + void tick(ArgumentType name, Arguments &&...arguments) + { + m_category().begin('i', 0, name, 0, IsFlow::No, std::forward(arguments)...); + } + +private: + std::size_t m_id = 0; + CategoryFunctionPointer m_category = nullptr; +}; +template +class Category; + +template +using StringViewCategory = Category; +template +using StringCategory = Category; +template +using StringViewWithStringArgumentsCategory = Category; + +using DisabledToken = Token, Tracing::IsDisabled>; + template class ObjectToken : public BasicDisabledToken { @@ -499,10 +655,12 @@ public: template class ObjectToken : public BasicEnabledToken { - ObjectToken(std::string_view name, std::size_t id, Category &category) + using CategoryFunctionPointer = typename Category::CategoryFunctionPointer; + + ObjectToken(std::string_view name, std::size_t id, CategoryFunctionPointer category) : m_name{name} , m_id{id} - , m_category{&category} + , m_category{category} {} public: @@ -511,14 +669,12 @@ public: friend Category; - ObjectToken() = default; - ObjectToken(const ObjectToken &other) : m_name{other.m_name} , m_category{other.m_category} { if (other.m_id) - m_id = m_category->beginObject(m_name).m_id; + m_id = m_category().beginObject(m_name).m_id; } ObjectToken &operator=(const ObjectToken &other) @@ -553,7 +709,7 @@ public: ~ObjectToken() { if (m_id) - m_category->end('e', m_id, std::move(m_name)); + m_category().end('e', m_id, std::move(m_name)); m_id = 0; } @@ -561,14 +717,16 @@ public: template void change(ArgumentType name, Arguments &&...arguments) { - if (m_id) - m_category->tick('n', m_id, std::move(name), std::forward(arguments)...); + if (m_id) { + m_category().tick( + 'n', m_id, std::move(name), 0, IsFlow::No, std::forward(arguments)...); + } } private: StringType m_name; std::size_t m_id = 0; - Category *m_category = nullptr; + CategoryFunctionPointer m_category = nullptr; }; template @@ -576,6 +734,8 @@ class AsynchronousToken : public BasicDisabledToken { public: using ArgumentType = typename Category::ArgumentType; + using FlowTokenType = typename Category::FlowTokenType; + using TokenType = typename Category::TokenType; AsynchronousToken() {} @@ -586,44 +746,71 @@ public: ~AsynchronousToken() {} - [[nodiscard]] AsynchronousToken create() { return {}; } + [[nodiscard]] TokenType createToken() { return {}; } template [[nodiscard]] AsynchronousToken begin(ArgumentType, Arguments &&...) { - return AsynchronousToken{}; + return {}; } template - void tick(Arguments &&...) + [[nodiscard]] std::pair beginWithFlow(ArgumentType, + Arguments &&...) + { + return {}; + } + + template + void tick(ArgumentType, Arguments &&...) {} + template + FlowTokenType tickWithFlow(ArgumentType, Arguments &&...) + { + return {}; + } + template void end(Arguments &&...) {} }; -using DisabledAsynchronousToken = AsynchronousToken; -template -class Category; +using DisabledAsynchronousToken = AsynchronousToken, + Tracing::IsDisabled>; + +template +class FlowToken; template class AsynchronousToken : public BasicEnabledToken { - AsynchronousToken(std::string_view name, std::size_t id, Category &category) + using CategoryFunctionPointer = typename Category::CategoryFunctionPointer; + + AsynchronousToken(std::string_view name, std::size_t id, CategoryFunctionPointer category) : m_name{name} , m_id{id} - , m_category{&category} + , m_category{category} {} + using PrivateTag = typename Category::PrivateTag; + public: using StringType = typename Category::StringType; using ArgumentType = typename Category::ArgumentType; + using FlowTokenType = typename Category::FlowTokenType; + using TokenType = typename Category::TokenType; friend Category; + friend FlowTokenType; + friend TokenType; - AsynchronousToken() = default; + AsynchronousToken(PrivateTag, std::string_view name, std::size_t id, CategoryFunctionPointer category) + : AsynchronousToken{name, id, category} + {} + + AsynchronousToken() {} AsynchronousToken(const AsynchronousToken &) = delete; AsynchronousToken &operator=(const AsynchronousToken &) = delete; @@ -647,29 +834,62 @@ public: ~AsynchronousToken() { end(); } - [[nodiscard]] AsynchronousToken create() { return {{}, m_id, *m_category}; } + [[nodiscard]] TokenType createToken() { return {m_id, m_category}; } template [[nodiscard]] AsynchronousToken begin(ArgumentType name, Arguments &&...arguments) { if (m_id) - m_category->begin('b', m_id, name, std::forward(arguments)...); + m_category().begin('b', m_id, name, 0, IsFlow::No, std::forward(arguments)...); - return AsynchronousToken{std::move(name), m_id, *m_category}; + return AsynchronousToken{std::move(name), m_id, m_category}; + } + + template + [[nodiscard]] std::pair beginWithFlow(ArgumentType name, + Arguments &&...arguments) + { + std::size_t bindId = 0; + + if (m_id) { + auto category = m_category(); + bindId = category.createBindId(); + category.begin('b', m_id, name, bindId, IsFlow::Out, std::forward(arguments)...); + } + + return {std::piecewise_construct, + std::forward_as_tuple(std::move(name), m_id, m_category), + std::forward_as_tuple(PrivateTag{}, std::move(name), bindId, m_category)}; } template void tick(ArgumentType name, Arguments &&...arguments) { - if (m_id) - m_category->tick('n', m_id, std::move(name), std::forward(arguments)...); + if (m_id) { + m_category().tick( + 'n', m_id, std::move(name), 0, IsFlow::No, std::forward(arguments)...); + } + } + + template + FlowTokenType tickWithFlow(ArgumentType name, Arguments &&...arguments) + { + std::size_t bindId = 0; + + if (m_id) { + auto category = m_category(); + bindId = category.createBindId(); + category.tick('n', m_id, name, bindId, IsFlow::Out, std::forward(arguments)...); + } + + return {std::move(name), bindId, m_category}; } template void end(Arguments &&...arguments) { if (m_id && m_name.size()) - m_category->end('e', m_id, std::move(m_name), std::forward(arguments)...); + m_category().end('e', m_id, std::move(m_name), std::forward(arguments)...); m_id = 0; } @@ -677,7 +897,174 @@ public: private: StringType m_name; std::size_t m_id = 0; - Category *m_category = nullptr; + CategoryFunctionPointer m_category = nullptr; +}; + +template +class FlowToken : public BasicDisabledToken +{ +public: + FlowToken() = default; + using AsynchronousTokenType = typename Category::AsynchronousTokenType; + using ArgumentType = typename Category::ArgumentType; + using TracerType = typename Category::TracerType; + using TokenType = typename Category::TokenType; + + friend TracerType; + friend AsynchronousTokenType; + friend TokenType; + friend Category; + + FlowToken(const FlowToken &) = delete; + FlowToken &operator=(const FlowToken &) = delete; + FlowToken(FlowToken &&other) noexcept = default; + FlowToken &operator=(FlowToken &&other) noexcept = default; + + ~FlowToken() {} + + template + [[nodiscard]] AsynchronousTokenType beginAsynchronous(ArgumentType, Arguments &&...) + { + return {}; + } + + template + [[nodiscard]] std::pair beginAsynchronousWithFlow(ArgumentType, + Arguments &&...) + { + return {}; + } + + template + void connectTo(const AsynchronousTokenType &, Arguments &&...) + {} + + template + [[nodiscard]] TracerType beginDuration(ArgumentType, Arguments &&...) + { + return {}; + } + + template + [[nodiscard]] std::pair beginDurationWithFlow(ArgumentType, Arguments &&...) + { + return std::pair(); + } + + template + void tick(ArgumentType, Arguments &&...) + {} +}; + +template +class FlowToken : public BasicDisabledToken +{ + using PrivateTag = typename Category::PrivateTag; + using CategoryFunctionPointer = typename Category::CategoryFunctionPointer; + +public: + FlowToken() = default; + + FlowToken(PrivateTag, std::string_view name, std::size_t bindId, CategoryFunctionPointer category) + : m_name{name} + , m_bindId{bindId} + , m_category{category} + {} + + using StringType = typename Category::StringType; + using AsynchronousTokenType = typename Category::AsynchronousTokenType; + using ArgumentType = typename Category::ArgumentType; + using TracerType = typename Category::TracerType; + using TokenType = typename Category::TokenType; + + friend AsynchronousTokenType; + friend TokenType; + friend TracerType; + friend Category; + + FlowToken(const FlowToken &) = delete; + FlowToken &operator=(const FlowToken &) = delete; + FlowToken(FlowToken &&other) noexcept = default; + FlowToken &operator=(FlowToken &&other) noexcept = default; + + ~FlowToken() {} + + template + [[nodiscard]] AsynchronousTokenType beginAsynchronous(ArgumentType name, Arguments &&...arguments) + { + std::size_t id = 0; + + if (m_bindId) { + auto category = m_category(); + id = category.createId(); + category->begin('b', id, name, m_bindId, IsFlow::In, std::forward(arguments)...); + } + + return {std::move(name), id, m_category}; + } + + template + [[nodiscard]] std::pair beginAsynchronousWithFlow( + ArgumentType name, Arguments &&...arguments) + { + std::size_t id = 0; + std::size_t bindId = 0; + + if (m_bindId) { + auto category = m_category(); + id = category.createId(); + bindId = category.createBindId(); + category->begin('b', id, name, bindId, IsFlow::InOut, std::forward(arguments)...); + } + + return {std::piecewise_construct, + std::forward_as_tuple(PrivateTag{}, std::move(name), id, m_category), + std::forward_as_tuple(PrivateTag{}, std::move(name), bindId, m_category)}; + } + + template + void connectTo(const AsynchronousTokenType &token, Arguments &&...arguments) + { + if (m_bindId && token.m_id) { + m_category().begin('b', + token.m_id, + token.m_name, + m_bindId, + IsFlow::In, + std::forward(arguments)...); + } + } + + template + [[nodiscard]] TracerType beginDuration(ArgumentType traceName, Arguments &&...arguments) + { + return {m_bindId, IsFlow::In, traceName, m_category, std::forward(arguments)...}; + } + + template + [[nodiscard]] std::pair beginDurationWithFlow(ArgumentType traceName, + Arguments &&...arguments) + { + return {std::piecewise_construct, + std::forward_as_tuple(PrivateTag{}, + m_bindId, + IsFlow::InOut, + traceName, + m_category, + std::forward(arguments)...), + std::forward_as_tuple(PrivateTag{}, traceName, m_bindId, m_category)}; + } + + template + void tick(ArgumentType name, Arguments &&...arguments) + { + m_category().tick('i', 0, name, m_bindId, IsFlow::In, std::forward(arguments)...); + } + +private: + StringType m_name; + std::size_t m_bindId = 0; + CategoryFunctionPointer m_category = nullptr; }; template @@ -689,10 +1076,15 @@ public: using ArgumentsStringType = typename TraceEvent::ArgumentsStringType; using AsynchronousTokenType = AsynchronousToken; using ObjectTokenType = ObjectToken; + using FlowTokenType = FlowToken; + using TracerType = Tracer; + using TokenType = Token; + using CategoryFunctionPointer = Category &(*) (); - Category(ArgumentType, EventQueue &) {} + Category(ArgumentType, EventQueue &, CategoryFunctionPointer) + {} - Category(ArgumentType, EventQueue &) {} + Category(ArgumentType, EventQueue &, CategoryFunctionPointer) {} template [[nodiscard]] AsynchronousTokenType beginAsynchronous(ArgumentType, Arguments &&...) @@ -700,18 +1092,39 @@ public: return {}; } + template + [[nodiscard]] std::pair beginAsynchronousWithFlow( + ArgumentType, Arguments &&...) + {} + template [[nodiscard]] ObjectTokenType beginObject(ArgumentType, Arguments &&...) { return {}; } + template + [[nodiscard]] TracerType beginDuration(ArgumentType, Arguments &&...) + { + return {}; + } + + template + [[nodiscard]] std::pair beginDurationWithFlow(ArgumentType, + Arguments &&...) + { + return std::pair(); + } + static constexpr bool isActive() { return false; } }; template class Category { + class PrivateTag + {}; + public: using IsActive = std::true_type; using ArgumentType = typename TraceEvent::ArgumentType; @@ -719,40 +1132,84 @@ public: using StringType = typename TraceEvent::StringType; using AsynchronousTokenType = AsynchronousToken; using ObjectTokenType = ObjectToken; + using FlowTokenType = FlowToken; + using TracerType = Tracer; + using TokenType = Token; + using CategoryFunctionPointer = Category &(*) (); friend AsynchronousTokenType; friend ObjectTokenType; + friend TokenType; + friend FlowTokenType; + friend TracerType; template - Category(ArgumentType name, EventQueue &queue) + Category(ArgumentType name, EventQueue &queue, CategoryFunctionPointer self) : m_name{std::move(name)} , m_eventQueue{queue} + , m_self{self} { static_assert(std::is_same_v, "A active category is not possible with an inactive event queue!"); - idCounter = globalIdCounter += 1ULL << 32; + m_idCounter = m_globalIdCounter += 1ULL << 32; + m_bindIdCounter = m_globalBindIdCounter += 1ULL << 32; } template [[nodiscard]] AsynchronousTokenType beginAsynchronous(ArgumentType traceName, Arguments &&...arguments) { - std::size_t id = ++idCounter; + std::size_t id = createId(); - begin('b', id, std::move(traceName), std::forward(arguments)...); + begin('b', id, std::move(traceName), 0, IsFlow::No, std::forward(arguments)...); - return {traceName, id, *this}; + return {traceName, id, m_self}; + } + + template + [[nodiscard]] std::pair beginAsynchronousWithFlow( + ArgumentType traceName, Arguments &&...arguments) + { + std::size_t id = createId(); + std::size_t bindId = createBindId(); + + begin('b', id, std::move(traceName), bindId, IsFlow::Out, std::forward(arguments)...); + + return {std::piecewise_construct, + std::forward_as_tuple(PrivateTag{}, traceName, id, m_self), + std::forward_as_tuple(PrivateTag{}, traceName, bindId, m_self)}; } template [[nodiscard]] ObjectTokenType beginObject(ArgumentType traceName, Arguments &&...arguments) { - std::size_t id = ++idCounter; + std::size_t id = createId(); - if (id) - begin('b', id, std::move(traceName), std::forward(arguments)...); + begin('b', id, std::move(traceName), 0, IsFlow::No, std::forward(arguments)...); - return {traceName, id, *this}; + return {traceName, id, m_self}; + } + + template + [[nodiscard]] TracerType beginDuration(ArgumentType traceName, Arguments &&...arguments) + { + return {traceName, m_self, std::forward(arguments)...}; + } + + template + [[nodiscard]] std::pair beginDurationWithFlow(ArgumentType traceName, + Arguments &&...arguments) + { + std::size_t bindId = createBindId(); + + return {std::piecewise_construct, + std::forward_as_tuple(PrivateTag{}, + bindId, + IsFlow::Out, + traceName, + m_self, + std::forward(arguments)...), + std::forward_as_tuple(PrivateTag{}, traceName, bindId, m_self)}; } EnabledEventQueue &eventQueue() const { return m_eventQueue; } @@ -761,12 +1218,21 @@ public: static constexpr bool isActive() { return true; } + std::size_t createBindId() { return ++m_bindIdCounter; } + + std::size_t createId() { return ++m_idCounter; } + public: IsEnabled isEnabled = IsEnabled::Yes; private: template - void begin(char type, std::size_t id, StringType traceName, Arguments &&...arguments) + void begin(char type, + std::size_t id, + StringType traceName, + std::size_t bindId, + IsFlow flow, + Arguments &&...arguments) { if (isEnabled == IsEnabled::No) return; @@ -777,12 +1243,19 @@ private: traceEvent.category = m_name; traceEvent.type = type; traceEvent.id = id; + traceEvent.bindId = bindId; + traceEvent.flow = flow; Internal::appendArguments(traceEvent.arguments, std::forward(arguments)...); traceEvent.time = Clock::now(); } template - void tick(char type, std::size_t id, StringType traceName, Arguments &&...arguments) + void tick(char type, + std::size_t id, + StringType traceName, + std::size_t bindId, + IsFlow flow, + Arguments &&...arguments) { if (isEnabled == IsEnabled::No) return; @@ -796,6 +1269,8 @@ private: traceEvent.time = time; traceEvent.type = type; traceEvent.id = id; + traceEvent.bindId = bindId; + traceEvent.flow = flow; Internal::appendArguments(traceEvent.arguments, std::forward(arguments)...); } @@ -814,14 +1289,19 @@ private: traceEvent.time = time; traceEvent.type = type; traceEvent.id = id; + traceEvent.bindId = 0; + traceEvent.flow = IsFlow::No; Internal::appendArguments(traceEvent.arguments, std::forward(arguments)...); } private: StringType m_name; EnabledEventQueue &m_eventQueue; - inline static std::atomic globalIdCounter; - std::size_t idCounter; + inline static std::atomic m_globalIdCounter; + std::size_t m_idCounter; + inline static std::atomic m_globalBindIdCounter; + std::size_t m_bindIdCounter; + CategoryFunctionPointer m_self; }; template @@ -835,7 +1315,14 @@ template class Tracer { public: + Tracer() = default; using ArgumentType = typename Category::ArgumentType; + using TokenType = typename Category::TokenType; + using FlowTokenType = typename Category::FlowTokenType; + + friend TokenType; + friend FlowTokenType; + friend Category; template [[nodiscard]] Tracer(ArgumentType, Category &, Arguments &&...) @@ -845,6 +1332,17 @@ public: Tracer &operator=(const Tracer &) = delete; Tracer(Tracer &&other) noexcept = default; Tracer &operator=(Tracer &&other) noexcept = delete; + + TokenType createToken() { return {}; } + + template + Tracer beginDuration(ArgumentType, Arguments &&...) + { + return {}; + } + + void end() {} + ~Tracer() {} }; @@ -854,14 +1352,50 @@ class Tracer using ArgumentType = typename Category::ArgumentType; using StringType = typename Category::StringType; using ArgumentsStringType = typename Category::ArgumentsStringType; + using TokenType = typename Category::TokenType; + using FlowTokenType = typename Category::FlowTokenType; + using PrivateTag = typename Category::PrivateTag; + using CategoryFunctionPointer = typename Category::CategoryFunctionPointer; + + friend FlowTokenType; + friend TokenType; + friend Category; + + template + [[nodiscard]] Tracer(std::size_t bindId, + IsFlow flow, + ArgumentType name, + CategoryFunctionPointer category, + Arguments &&...arguments) + : m_name{name} + , m_bindId{bindId} + , flow{flow} + , m_category{category} + { + if (category().isEnabled == IsEnabled::Yes) { + Internal::appendArguments(m_arguments, + std::forward(arguments)...); + m_start = Clock::now(); + } + } public: template - [[nodiscard]] Tracer(ArgumentType name, Category &category, Arguments &&...arguments) + [[nodiscard]] Tracer(PrivateTag, + std::size_t bindId, + IsFlow flow, + ArgumentType name, + CategoryFunctionPointer category, + Arguments &&...arguments) + : Tracer{bindId, flow, std::move(name), category, std::forward(arguments)...} + {} + + template + [[nodiscard]] Tracer(ArgumentType name, CategoryFunctionPointer category, Arguments &&...arguments) : m_name{name} , m_category{category} { - if (category.isEnabled == IsEnabled::Yes) { + if (category().isEnabled == IsEnabled::Yes) { Internal::appendArguments(m_arguments, std::forward(arguments)...); m_start = Clock::now(); @@ -872,17 +1406,38 @@ public: Tracer &operator=(const Tracer &) = delete; Tracer(Tracer &&other) noexcept = delete; Tracer &operator=(Tracer &&other) noexcept = delete; - ~Tracer() + + TokenType createToken() { return {0, m_category}; } + + ~Tracer() { sendTrace(); } + + template + Tracer beginDuration(ArgumentType name, Arguments &&...arguments) { - if constexpr (tracingStatus() == Tracing::IsEnabled) { - if (m_category.isEnabled == IsEnabled::Yes) { + return {std::move(name), m_category, std::forward(arguments)...}; + } + + void end() + { + sendTrace(); + m_name = {}; + } + +private: + void sendTrace() + { + if (m_name.size()) { + auto category = m_category(); + if (category.isEnabled == IsEnabled::Yes) { auto duration = Clock::now() - m_start; - auto &traceEvent = getTraceEvent(m_category.eventQueue()); + auto &traceEvent = getTraceEvent(category.eventQueue()); traceEvent.name = m_name; - traceEvent.category = m_category.name(); + traceEvent.category = category.name(); traceEvent.arguments = m_arguments; traceEvent.time = m_start; traceEvent.duration = duration; + traceEvent.bindId = m_bindId; + traceEvent.flow = flow; traceEvent.type = 'X'; } } @@ -892,7 +1447,9 @@ private: TimePoint m_start; StringType m_name; StringType m_arguments; - Category &m_category; + std::size_t m_bindId; + IsFlow flow; + CategoryFunctionPointer m_category; }; template diff --git a/src/libs/sqlite/sqlitebasestatement.cpp b/src/libs/sqlite/sqlitebasestatement.cpp index efddc08e38c..406d49d2489 100644 --- a/src/libs/sqlite/sqlitebasestatement.cpp +++ b/src/libs/sqlite/sqlitebasestatement.cpp @@ -35,11 +35,18 @@ thread_local NanotraceHR::EventQueueData sqliteLowLevelCategory{ - "sqlite low level"_t, eventQueue}; +NanotraceHR::StringViewCategory &sqliteLowLevelCategory(); + +thread_local NanotraceHR::StringViewCategory sqliteLowLevelCategory_{ + "sqlite low level"_t, eventQueue, sqliteLowLevelCategory}; + +NanotraceHR::StringViewCategory &sqliteLowLevelCategory() +{ + return sqliteLowLevelCategory_; +} thread_local NanotraceHR::StringViewCategory sqliteHighLevelCategory_{ - "sqlite high level"_t, eventQueue}; + "sqlite high level"_t, eventQueue, sqliteHighLevelCategory}; } // namespace NanotraceHR::StringViewCategory &sqliteHighLevelCategory() @@ -101,14 +108,14 @@ void BaseStatement::waitForUnlockNotify() const void BaseStatement::reset() const noexcept { - NanotraceHR::Tracer tracer{"reset"_t, sqliteLowLevelCategory}; + NanotraceHR::Tracer tracer{"reset"_t, sqliteLowLevelCategory()}; sqlite3_reset(m_compiledStatement.get()); } bool BaseStatement::next() const { - NanotraceHR::Tracer tracer{"next"_t, sqliteLowLevelCategory}; + NanotraceHR::Tracer tracer{"next"_t, sqliteLowLevelCategory()}; int resultCode; do { @@ -135,7 +142,7 @@ void BaseStatement::step() const void BaseStatement::bindNull(int index) { - NanotraceHR::Tracer tracer{"bind null"_t, sqliteLowLevelCategory}; + NanotraceHR::Tracer tracer{"bind null"_t, sqliteLowLevelCategory()}; int resultCode = sqlite3_bind_null(m_compiledStatement.get(), index); if (resultCode != SQLITE_OK) @@ -149,7 +156,7 @@ void BaseStatement::bind(int index, NullValue) void BaseStatement::bind(int index, int value) { - NanotraceHR::Tracer tracer{"bind int"_t, sqliteLowLevelCategory}; + NanotraceHR::Tracer tracer{"bind int"_t, sqliteLowLevelCategory()}; int resultCode = sqlite3_bind_int(m_compiledStatement.get(), index, value); if (resultCode != SQLITE_OK) @@ -158,7 +165,7 @@ void BaseStatement::bind(int index, int value) void BaseStatement::bind(int index, long long value) { - NanotraceHR::Tracer tracer{"bind long long"_t, sqliteLowLevelCategory}; + NanotraceHR::Tracer tracer{"bind long long"_t, sqliteLowLevelCategory()}; int resultCode = sqlite3_bind_int64(m_compiledStatement.get(), index, value); if (resultCode != SQLITE_OK) @@ -167,7 +174,7 @@ void BaseStatement::bind(int index, long long value) void BaseStatement::bind(int index, double value) { - NanotraceHR::Tracer tracer{"bind double"_t, sqliteLowLevelCategory}; + NanotraceHR::Tracer tracer{"bind double"_t, sqliteLowLevelCategory()}; int resultCode = sqlite3_bind_double(m_compiledStatement.get(), index, value); if (resultCode != SQLITE_OK) @@ -176,7 +183,7 @@ void BaseStatement::bind(int index, double value) void BaseStatement::bind(int index, void *pointer) { - NanotraceHR::Tracer tracer{"bind pointer"_t, sqliteLowLevelCategory}; + NanotraceHR::Tracer tracer{"bind pointer"_t, sqliteLowLevelCategory()}; int resultCode = sqlite3_bind_pointer(m_compiledStatement.get(), index, pointer, "carray", nullptr); if (resultCode != SQLITE_OK) @@ -185,7 +192,7 @@ void BaseStatement::bind(int index, void *pointer) void BaseStatement::bind(int index, Utils::span values) { - NanotraceHR::Tracer tracer{"bind int span"_t, sqliteLowLevelCategory}; + NanotraceHR::Tracer tracer{"bind int span"_t, sqliteLowLevelCategory()}; int resultCode = sqlite3_carray_bind(m_compiledStatement.get(), index, @@ -199,7 +206,7 @@ void BaseStatement::bind(int index, Utils::span values) void BaseStatement::bind(int index, Utils::span values) { - NanotraceHR::Tracer tracer{"bind long long span"_t, sqliteLowLevelCategory}; + NanotraceHR::Tracer tracer{"bind long long span"_t, sqliteLowLevelCategory()}; int resultCode = sqlite3_carray_bind(m_compiledStatement.get(), index, @@ -213,7 +220,7 @@ void BaseStatement::bind(int index, Utils::span values) void BaseStatement::bind(int index, Utils::span values) { - NanotraceHR::Tracer tracer{"bind double span"_t, sqliteLowLevelCategory}; + NanotraceHR::Tracer tracer{"bind double span"_t, sqliteLowLevelCategory()}; int resultCode = sqlite3_carray_bind(m_compiledStatement.get(), index, @@ -227,7 +234,7 @@ void BaseStatement::bind(int index, Utils::span values) void BaseStatement::bind(int index, Utils::span values) { - NanotraceHR::Tracer tracer{"bind const char* span"_t, sqliteLowLevelCategory}; + NanotraceHR::Tracer tracer{"bind const char* span"_t, sqliteLowLevelCategory()}; int resultCode = sqlite3_carray_bind(m_compiledStatement.get(), index, @@ -241,7 +248,7 @@ void BaseStatement::bind(int index, Utils::span values) void BaseStatement::bind(int index, Utils::SmallStringView text) { - NanotraceHR::Tracer tracer{"bind string"_t, sqliteLowLevelCategory}; + NanotraceHR::Tracer tracer{"bind string"_t, sqliteLowLevelCategory()}; int resultCode = sqlite3_bind_text(m_compiledStatement.get(), index, @@ -254,7 +261,7 @@ void BaseStatement::bind(int index, Utils::SmallStringView text) void BaseStatement::bind(int index, BlobView blobView) { - NanotraceHR::Tracer tracer{"bind blob"_t, sqliteLowLevelCategory}; + NanotraceHR::Tracer tracer{"bind blob"_t, sqliteLowLevelCategory()}; int resultCode = SQLITE_OK; @@ -274,7 +281,7 @@ void BaseStatement::bind(int index, BlobView blobView) void BaseStatement::bind(int index, const Value &value) { - NanotraceHR::Tracer tracer{"bind value"_t, sqliteLowLevelCategory}; + NanotraceHR::Tracer tracer{"bind value"_t, sqliteLowLevelCategory()}; switch (value.type()) { case ValueType::Integer: @@ -297,7 +304,7 @@ void BaseStatement::bind(int index, const Value &value) void BaseStatement::bind(int index, ValueView value) { - NanotraceHR::Tracer tracer{"bind value"_t, sqliteLowLevelCategory}; + NanotraceHR::Tracer tracer{"bind value"_t, sqliteLowLevelCategory()}; switch (value.type()) { case ValueType::Integer: @@ -320,7 +327,7 @@ void BaseStatement::bind(int index, ValueView value) void BaseStatement::prepare(Utils::SmallStringView sqlStatement) { - NanotraceHR::Tracer tracer{"prepare"_t, sqliteLowLevelCategory}; + NanotraceHR::Tracer tracer{"prepare"_t, sqliteLowLevelCategory()}; if (!m_database.isLocked()) throw DatabaseIsNotLocked{}; @@ -421,7 +428,7 @@ StringType convertToTextForColumn(sqlite3_stmt *sqlStatment, int column) Type BaseStatement::fetchType(int column) const { - NanotraceHR::Tracer tracer{"fetch type"_t, sqliteLowLevelCategory}; + NanotraceHR::Tracer tracer{"fetch type"_t, sqliteLowLevelCategory()}; auto dataType = sqlite3_column_type(m_compiledStatement.get(), column); @@ -443,7 +450,7 @@ Type BaseStatement::fetchType(int column) const int BaseStatement::fetchIntValue(int column) const { - NanotraceHR::Tracer tracer{"fetch int"_t, sqliteLowLevelCategory}; + NanotraceHR::Tracer tracer{"fetch int"_t, sqliteLowLevelCategory()}; return sqlite3_column_int(m_compiledStatement.get(), column); } @@ -467,7 +474,7 @@ long BaseStatement::fetchValue(int column) const long long BaseStatement::fetchLongLongValue(int column) const { - NanotraceHR::Tracer tracer{"fetch long long"_t, sqliteLowLevelCategory}; + NanotraceHR::Tracer tracer{"fetch long long"_t, sqliteLowLevelCategory()}; return sqlite3_column_int64(m_compiledStatement.get(), column); } @@ -480,14 +487,14 @@ long long BaseStatement::fetchValue(int column) const double BaseStatement::fetchDoubleValue(int column) const { - NanotraceHR::Tracer tracer{"fetch double"_t, sqliteLowLevelCategory}; + NanotraceHR::Tracer tracer{"fetch double"_t, sqliteLowLevelCategory()}; return sqlite3_column_double(m_compiledStatement.get(), column); } BlobView BaseStatement::fetchBlobValue(int column) const { - NanotraceHR::Tracer tracer{"fetch blob"_t, sqliteLowLevelCategory}; + NanotraceHR::Tracer tracer{"fetch blob"_t, sqliteLowLevelCategory()}; return convertToBlobForColumn(m_compiledStatement.get(), column); } @@ -537,7 +544,7 @@ ValueView BaseStatement::fetchValueView(int column) const void BaseStatement::Deleter::operator()(sqlite3_stmt *statement) { - NanotraceHR::Tracer tracer{"finalize"_t, sqliteLowLevelCategory}; + NanotraceHR::Tracer tracer{"finalize"_t, sqliteLowLevelCategory()}; sqlite3_finalize(statement); } diff --git a/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagecache.cpp b/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagecache.cpp index 6c009ec3cad..ad4e4aa40fb 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagecache.cpp +++ b/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagecache.cpp @@ -20,7 +20,7 @@ using namespace NanotraceHR::Literals; namespace ImageCache { namespace { -thread_local Category category_{"image cache"_t, QmlDesigner::Tracing::eventQueue()}; +thread_local Category category_{"image cache"_t, QmlDesigner::Tracing::eventQueue(), category}; } // namespace Category &category() @@ -139,7 +139,7 @@ void AsynchronousImageCache::requestImage(Utils::SmallStringView name, { auto traceToken = ImageCache::category().beginAsynchronous( "request image in asynchornous image cache"_t); - m_taskQueue.addTask(traceToken.create(), + m_taskQueue.addTask(traceToken.createToken(), std::move(name), std::move(extraId), std::move(captureCallback), @@ -157,7 +157,7 @@ void AsynchronousImageCache::requestMidSizeImage(Utils::SmallStringView name, { auto traceToken = ImageCache::category().beginAsynchronous( "request mid size image in asynchornous image cache"_t); - m_taskQueue.addTask(traceToken.create(), + m_taskQueue.addTask(traceToken.createToken(), std::move(name), std::move(extraId), std::move(captureCallback), @@ -175,7 +175,7 @@ void AsynchronousImageCache::requestSmallImage(Utils::SmallStringView name, { auto traceToken = ImageCache::category().beginAsynchronous( "request small size image in asynchornous image cache"_t); - m_taskQueue.addTask(traceToken.create(), + m_taskQueue.addTask(traceToken.createToken(), std::move(name), std::move(extraId), std::move(captureCallback), diff --git a/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagefactory.cpp b/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagefactory.cpp index 9ed3a34f7d6..f7c9d59e9cf 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagefactory.cpp +++ b/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagefactory.cpp @@ -57,7 +57,8 @@ void AsynchronousImageFactory::request(Utils::SmallStringView name, extraId, std::move(auxiliaryData), std::move(capture), - ImageCache::InternalAbortCallback{}); + ImageCache::InternalAbortCallback{}, + {}); } void AsynchronousImageFactory::clean() diff --git a/src/plugins/qmldesigner/designercore/imagecache/imagecachecollectorinterface.h b/src/plugins/qmldesigner/designercore/imagecache/imagecachecollectorinterface.h index e6c70f31939..34083f7b293 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/imagecachecollectorinterface.h +++ b/src/plugins/qmldesigner/designercore/imagecache/imagecachecollectorinterface.h @@ -23,7 +23,7 @@ public: const ImageCache::AuxiliaryData &auxiliaryData, CaptureCallback captureCallback, AbortCallback abortCallback, - ImageCache::TraceToken traceToken = {}) + ImageCache::TraceToken traceToken) = 0; virtual ImageTuple createImage(Utils::SmallStringView filePath, diff --git a/src/plugins/qmldesigner/designercore/imagecache/imagecachegenerator.cpp b/src/plugins/qmldesigner/designercore/imagecache/imagecachegenerator.cpp index 93ddbd38015..bf57766e5e6 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/imagecachegenerator.cpp +++ b/src/plugins/qmldesigner/designercore/imagecache/imagecachegenerator.cpp @@ -111,35 +111,43 @@ void ImageCacheGenerator::waitForFinished() m_backgroundThread->wait(); } +std::optional ImageCacheGenerator::getTask() +{ + { + auto [lock, abort] = waitForEntries(); + + if (abort) + return {}; + + std::optional task = std::move(m_tasks.front()); + + m_tasks.pop_front(); + + return task; + } +} + void ImageCacheGenerator::startGeneration() { while (true) { - Task task; + auto task = getTask(); - { - auto [lock, abort] = waitForEntries(); - - if (abort) - return; - - task = std::move(m_tasks.front()); - - m_tasks.pop_front(); - } + if (!task) + return; m_collector.start( - task.filePath, - task.extraId, - std::move(task.auxiliaryData), + task->filePath, + task->extraId, + std::move(task->auxiliaryData), [this, - abortCallbacks = task.abortCallbacks, - captureCallbacks = std::move(task.captureCallbacks), - filePath = task.filePath, - extraId = task.extraId, - timeStamp = task.timeStamp](const QImage &image, - const QImage &midSizeImage, - const QImage &smallImage, - ImageCache::TraceToken traceToken) { + abortCallbacks = task->abortCallbacks, + captureCallbacks = std::move(task->captureCallbacks), + filePath = task->filePath, + extraId = task->extraId, + timeStamp = task->timeStamp](const QImage &image, + const QImage &midSizeImage, + const QImage &smallImage, + ImageCache::TraceToken traceToken) { if (image.isNull() && midSizeImage.isNull() && smallImage.isNull()) callCallbacks(abortCallbacks, ImageCache::AbortReason::Failed, @@ -158,16 +166,16 @@ void ImageCacheGenerator::startGeneration() smallImage); }, [this, - abortCallbacks = task.abortCallbacks, - filePath = task.filePath, - extraId = task.extraId, - timeStamp = task.timeStamp](ImageCache::AbortReason abortReason, - ImageCache::TraceToken traceToken) { + abortCallbacks = task->abortCallbacks, + filePath = task->filePath, + extraId = task->extraId, + timeStamp = task->timeStamp](ImageCache::AbortReason abortReason, + ImageCache::TraceToken traceToken) { callCallbacks(abortCallbacks, abortReason, std::move(traceToken)); if (abortReason != ImageCache::AbortReason::Abort) m_storage.storeImage(createId(filePath, extraId), timeStamp, {}, {}, {}); }, - std::move(task.traceToken)); + std::move(task->traceToken)); std::lock_guard lock{m_mutex}; if (m_tasks.empty()) diff --git a/src/plugins/qmldesigner/designercore/imagecache/imagecachegenerator.h b/src/plugins/qmldesigner/designercore/imagecache/imagecachegenerator.h index 3b28c117fdb..1846eff557c 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/imagecachegenerator.h +++ b/src/plugins/qmldesigner/designercore/imagecache/imagecachegenerator.h @@ -72,6 +72,7 @@ private: }; void startGeneration(); + std::optional getTask(); void ensureThreadIsRunning(); [[nodiscard]] std::tuple, bool> waitForEntries(); void stopThread(); diff --git a/src/plugins/qmldesigner/designercore/imagecache/imagecachegeneratorinterface.h b/src/plugins/qmldesigner/designercore/imagecache/imagecachegeneratorinterface.h index 3708a204d63..62d1b374914 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/imagecachegeneratorinterface.h +++ b/src/plugins/qmldesigner/designercore/imagecache/imagecachegeneratorinterface.h @@ -20,7 +20,7 @@ public: ImageCache::CaptureImageWithScaledImagesCallback &&captureCallback, ImageCache::InternalAbortCallback &&abortCallback, ImageCache::AuxiliaryData &&auxiliaryData, - ImageCache::TraceToken = {}) + ImageCache::TraceToken) = 0; virtual void clean() = 0; diff --git a/src/plugins/qmldesigner/designercore/imagecache/meshimagecachecollector.h b/src/plugins/qmldesigner/designercore/imagecache/meshimagecachecollector.h index 067c9033608..98c2e84605e 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/meshimagecachecollector.h +++ b/src/plugins/qmldesigner/designercore/imagecache/meshimagecachecollector.h @@ -30,7 +30,7 @@ public: const ImageCache::AuxiliaryData &auxiliaryData, CaptureCallback captureCallback, AbortCallback abortCallback, - ImageCache::TraceToken traceToken = {}) override; + ImageCache::TraceToken traceToken) override; ImageTuple createImage(Utils::SmallStringView filePath, Utils::SmallStringView state, diff --git a/src/plugins/qmldesigner/designercore/imagecache/taskqueue.h b/src/plugins/qmldesigner/designercore/imagecache/taskqueue.h index 7ae32310188..ff17fef1cd8 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/taskqueue.h +++ b/src/plugins/qmldesigner/designercore/imagecache/taskqueue.h @@ -26,8 +26,7 @@ public: ~TaskQueue() { destroy(); } template - void addTask(NanotraceHR::AsynchronousToken traceToken, - Arguments &&...arguments) + void addTask(NanotraceHR::Token traceToken, Arguments &&...arguments) { { std::unique_lock lock{m_mutex}; @@ -42,7 +41,7 @@ public: template void addTask(Arguments &&...arguments) { - addTask(NanotraceHR::DisabledAsynchronousToken{}, std::forward(arguments)...); + addTask(NanotraceHR::DisabledToken{}, std::forward(arguments)...); } void clean() @@ -95,8 +94,8 @@ private: return {std::move(task)}; } - template - void ensureThreadIsRunning(TraceTokend traceToken) + template + void ensureThreadIsRunning(TraceToken traceToken) { using namespace NanotraceHR::Literals; @@ -108,7 +107,7 @@ private: m_sleeping = false; - auto threadCreateToken = traceToken.begin("thread is created in the task queue"_t); + auto threadCreateToken = traceToken.beginDuration("thread is created in the task queue"_t); m_backgroundThread = std::thread{[this](auto traceToken) { traceToken.tick("thread is ready"_t); while (true) { diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.cpp b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.cpp index 2a6bdd01cbc..700a85bc112 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.cpp +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.cpp @@ -9,8 +9,16 @@ namespace QmlDesigner { -thread_local NanotraceHR::StringViewCategory projectStorageCategory{ - "project storage"_t, Tracing::eventQueue()}; +namespace { + +thread_local NanotraceHR::StringViewCategory projectStorageCategory_{ + "project storage"_t, Tracing::eventQueue(), projectStorageCategory}; +} + +NanotraceHR::StringViewCategory &projectStorageCategory() +{ + return projectStorageCategory_; +} } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h index 47ca8b7c9a0..6244977ec79 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h @@ -37,7 +37,7 @@ constexpr NanotraceHR::Tracing projectStorageTracingStatus() #endif } -extern thread_local NanotraceHR::StringViewCategory projectStorageCategory; +NanotraceHR::StringViewCategory &projectStorageCategory(); template class ProjectStorage final : public ProjectStorageInterface @@ -57,7 +57,7 @@ public: , exclusiveTransaction{database} , initializer{database, isInitialized} { - NanotraceHR::Tracer tracer{"initialize"_t, projectStorageCategory}; + NanotraceHR::Tracer tracer{"initialize"_t, projectStorageCategory()}; exclusiveTransaction.commit(); @@ -68,7 +68,7 @@ public: void synchronize(Storage::Synchronization::SynchronizationPackage package) override { - NanotraceHR::Tracer tracer{"synchronize"_t, projectStorageCategory}; + NanotraceHR::Tracer tracer{"synchronize"_t, projectStorageCategory()}; TypeIds deletedTypeIds; Sqlite::withImmediateTransaction(database, [&] { @@ -135,7 +135,7 @@ public: void synchronizeDocumentImports(Storage::Imports imports, SourceId sourceId) override { - NanotraceHR::Tracer tracer{"synchronize document imports"_t, projectStorageCategory}; + NanotraceHR::Tracer tracer{"synchronize document imports"_t, projectStorageCategory()}; Sqlite::withImmediateTransaction(database, [&] { synchronizeDocumentImports(imports, @@ -153,14 +153,14 @@ public: ModuleId moduleId(Utils::SmallStringView moduleName) const override { - NanotraceHR::Tracer tracer{"get module id"_t, projectStorageCategory}; + NanotraceHR::Tracer tracer{"get module id"_t, projectStorageCategory()}; return moduleCache.id(moduleName); } Utils::SmallString moduleName(ModuleId moduleId) const { - NanotraceHR::Tracer tracer{"get module name"_t, projectStorageCategory}; + NanotraceHR::Tracer tracer{"get module name"_t, projectStorageCategory()}; if (!moduleId) throw ModuleDoesNotExists{}; @@ -172,7 +172,7 @@ public: Utils::SmallStringView exportedTypeName, Storage::Version version) const override { - NanotraceHR::Tracer tracer{"get type id by exported name"_t, projectStorageCategory}; + NanotraceHR::Tracer tracer{"get type id by exported name"_t, projectStorageCategory()}; if (version.minor) return selectTypeIdByModuleIdAndExportedNameAndVersionStatement @@ -191,14 +191,14 @@ public: TypeId typeId(ImportedTypeNameId typeNameId) const override { - NanotraceHR::Tracer tracer{"get type id by imported type name"_t, projectStorageCategory}; + NanotraceHR::Tracer tracer{"get type id by imported type name"_t, projectStorageCategory()}; return Sqlite::withDeferredTransaction(database, [&] { return fetchTypeId(typeNameId); }); } QVarLengthArray typeIds(ModuleId moduleId) const override { - NanotraceHR::Tracer tracer{"get type ids by module id"_t, projectStorageCategory}; + NanotraceHR::Tracer tracer{"get type ids by module id"_t, projectStorageCategory()}; return selectTypeIdsByModuleIdStatement .template valuesWithTransaction>(moduleId); @@ -206,7 +206,7 @@ public: Storage::Info::ExportedTypeNames exportedTypeNames(TypeId typeId) const override { - NanotraceHR::Tracer tracer{"get exported type names by type id"_t, projectStorageCategory}; + NanotraceHR::Tracer tracer{"get exported type names by type id"_t, projectStorageCategory()}; return selectExportedTypesByTypeIdStatement .template valuesWithTransaction(typeId); @@ -215,7 +215,7 @@ public: Storage::Info::ExportedTypeNames exportedTypeNames(TypeId typeId, SourceId sourceId) const override { - NanotraceHR::Tracer tracer{"get exported type names by source id"_t, projectStorageCategory}; + NanotraceHR::Tracer tracer{"get exported type names by source id"_t, projectStorageCategory()}; return selectExportedTypesByTypeIdAndSourceIdStatement .template valuesWithTransaction(typeId, sourceId); @@ -223,7 +223,7 @@ public: ImportId importId(const Storage::Import &import) const override { - NanotraceHR::Tracer tracer{"get import id by import"_t, projectStorageCategory}; + NanotraceHR::Tracer tracer{"get import id by import"_t, projectStorageCategory()}; return Sqlite::withDeferredTransaction(database, [&] { return fetchImportId(import.sourceId, import); @@ -233,7 +233,8 @@ public: ImportedTypeNameId importedTypeNameId(ImportId importId, Utils::SmallStringView typeName) override { - NanotraceHR::Tracer tracer{"get imported type name id by import id"_t, projectStorageCategory}; + NanotraceHR::Tracer tracer{"get imported type name id by import id"_t, + projectStorageCategory()}; return Sqlite::withDeferredTransaction(database, [&] { return fetchImportedTypeNameId(Storage::Synchronization::TypeNameKind::QualifiedExported, @@ -245,7 +246,8 @@ public: ImportedTypeNameId importedTypeNameId(SourceId sourceId, Utils::SmallStringView typeName) override { - NanotraceHR::Tracer tracer{"get imported type name id by source id"_t, projectStorageCategory}; + NanotraceHR::Tracer tracer{"get imported type name id by source id"_t, + projectStorageCategory()}; return Sqlite::withDeferredTransaction(database, [&] { return fetchImportedTypeNameId(Storage::Synchronization::TypeNameKind::Exported, @@ -256,7 +258,7 @@ public: QVarLengthArray propertyDeclarationIds(TypeId typeId) const override { - NanotraceHR::Tracer tracer{"get property declaration ids"_t, projectStorageCategory}; + NanotraceHR::Tracer tracer{"get property declaration ids"_t, projectStorageCategory()}; return selectPropertyDeclarationIdsForTypeStatement .template valuesWithTransaction>(typeId); @@ -264,7 +266,7 @@ public: QVarLengthArray localPropertyDeclarationIds(TypeId typeId) const override { - NanotraceHR::Tracer tracer{"get local property declaration ids"_t, projectStorageCategory}; + NanotraceHR::Tracer tracer{"get local property declaration ids"_t, projectStorageCategory()}; return selectLocalPropertyDeclarationIdsForTypeStatement .template valuesWithTransaction>(typeId); @@ -273,7 +275,7 @@ public: PropertyDeclarationId propertyDeclarationId(TypeId typeId, Utils::SmallStringView propertyName) const override { - NanotraceHR::Tracer tracer{"get property declaration id"_t, projectStorageCategory}; + NanotraceHR::Tracer tracer{"get property declaration id"_t, projectStorageCategory()}; return selectPropertyDeclarationIdForTypeAndPropertyNameStatement .template valueWithTransaction(typeId, propertyName); @@ -282,7 +284,7 @@ public: PropertyDeclarationId localPropertyDeclarationId(TypeId typeId, Utils::SmallStringView propertyName) const { - NanotraceHR::Tracer tracer{"get local property declaration id"_t, projectStorageCategory}; + NanotraceHR::Tracer tracer{"get local property declaration id"_t, projectStorageCategory()}; return selectLocalPropertyDeclarationIdForTypeAndPropertyNameStatement .template valueWithTransaction(typeId, propertyName); @@ -291,7 +293,7 @@ public: std::optional propertyDeclaration( PropertyDeclarationId propertyDeclarationId) const override { - NanotraceHR::Tracer tracer{"get property declaration"_t, projectStorageCategory}; + NanotraceHR::Tracer tracer{"get property declaration"_t, projectStorageCategory()}; return selectPropertyDeclarationForPropertyDeclarationIdStatement .template optionalValueWithTransaction( @@ -300,7 +302,7 @@ public: std::optional type(TypeId typeId) const override { - NanotraceHR::Tracer tracer{"get type"_t, projectStorageCategory}; + NanotraceHR::Tracer tracer{"get type"_t, projectStorageCategory()}; return selectInfoTypeByTypeIdStatement.template optionalValueWithTransaction( typeId); @@ -308,14 +310,14 @@ public: Utils::PathString typeIconPath(TypeId typeId) const override { - NanotraceHR::Tracer tracer{"get type icon path"_t, projectStorageCategory}; + NanotraceHR::Tracer tracer{"get type icon path"_t, projectStorageCategory()}; return selectTypeIconPathStatement.template valueWithTransaction(typeId); } Storage::Info::TypeHints typeHints(TypeId typeId) const override { - NanotraceHR::Tracer tracer{"get type hints"_t, projectStorageCategory}; + NanotraceHR::Tracer tracer{"get type hints"_t, projectStorageCategory()}; return selectTypeHintsStatement.template valuesWithTransaction( typeId); @@ -323,7 +325,7 @@ public: Storage::Info::ItemLibraryEntries itemLibraryEntries(TypeId typeId) const override { - NanotraceHR::Tracer tracer{"get item library entries by type id"_t, projectStorageCategory}; + NanotraceHR::Tracer tracer{"get item library entries by type id"_t, projectStorageCategory()}; using Storage::Info::ItemLibraryProperties; Storage::Info::ItemLibraryEntries entries; @@ -352,7 +354,8 @@ public: Storage::Info::ItemLibraryEntries itemLibraryEntries(SourceId sourceId) const override { - NanotraceHR::Tracer tracer{"get item library entries by source id"_t, projectStorageCategory}; + NanotraceHR::Tracer tracer{"get item library entries by source id"_t, + projectStorageCategory()}; using Storage::Info::ItemLibraryProperties; Storage::Info::ItemLibraryEntries entries; @@ -381,7 +384,7 @@ public: Storage::Info::ItemLibraryEntries allItemLibraryEntries() const override { - NanotraceHR::Tracer tracer{"get all item library entries"_t, projectStorageCategory}; + NanotraceHR::Tracer tracer{"get all item library entries"_t, projectStorageCategory()}; using Storage::Info::ItemLibraryProperties; Storage::Info::ItemLibraryEntries entries; @@ -410,7 +413,7 @@ public: std::vector signalDeclarationNames(TypeId typeId) const override { - NanotraceHR::Tracer tracer{"get signal names"_t, projectStorageCategory}; + NanotraceHR::Tracer tracer{"get signal names"_t, projectStorageCategory()}; return selectSignalDeclarationNamesForTypeStatement .template valuesWithTransaction(typeId); @@ -418,7 +421,7 @@ public: std::vector functionDeclarationNames(TypeId typeId) const override { - NanotraceHR::Tracer tracer{"get function names"_t, projectStorageCategory}; + NanotraceHR::Tracer tracer{"get function names"_t, projectStorageCategory()}; return selectFuncionDeclarationNamesForTypeStatement .template valuesWithTransaction(typeId); @@ -426,7 +429,7 @@ public: std::optional propertyName(PropertyDeclarationId propertyDeclarationId) const override { - NanotraceHR::Tracer tracer{"get property name"_t, projectStorageCategory}; + NanotraceHR::Tracer tracer{"get property name"_t, projectStorageCategory()}; return selectPropertyNameStatement.template optionalValueWithTransaction( propertyDeclarationId); @@ -440,7 +443,7 @@ public: template TypeId commonTypeId() const { - NanotraceHR::Tracer tracer{"get type id from common type cache"_t, projectStorageCategory}; + NanotraceHR::Tracer tracer{"get type id from common type cache"_t, projectStorageCategory()}; return commonTypeCache_.template typeId(); } @@ -449,7 +452,7 @@ public: TypeId builtinTypeId() const { NanotraceHR::Tracer tracer{"get builtin type id from common type cache"_t, - projectStorageCategory}; + projectStorageCategory()}; return commonTypeCache_.template builtinTypeId(); } @@ -458,14 +461,14 @@ public: TypeId builtinTypeId() const { NanotraceHR::Tracer tracer{"get builtin type id from common type cache"_t, - projectStorageCategory}; + projectStorageCategory()}; return commonTypeCache_.template builtinTypeId(); } TypeIds prototypeIds(TypeId type) const override { - NanotraceHR::Tracer tracer{"get prototypes"_t, projectStorageCategory}; + NanotraceHR::Tracer tracer{"get prototypes"_t, projectStorageCategory()}; return selectPrototypeIdsForTypeIdInOrderStatement.template valuesWithTransaction( type); @@ -473,7 +476,7 @@ public: TypeIds prototypeAndSelfIds(TypeId type) const override { - NanotraceHR::Tracer tracer{"get prototypes and self"_t, projectStorageCategory}; + NanotraceHR::Tracer tracer{"get prototypes and self"_t, projectStorageCategory()}; return selectPrototypeAndSelfIdsForTypeIdInOrderStatement .template valuesWithTransaction(type); @@ -481,7 +484,7 @@ public: TypeIds heirIds(TypeId typeId) const override { - NanotraceHR::Tracer tracer{"get heirs"_t, projectStorageCategory}; + NanotraceHR::Tracer tracer{"get heirs"_t, projectStorageCategory()}; return selectHeirTypeIdsStatement.template valuesWithTransaction(typeId); } @@ -489,7 +492,7 @@ public: template bool isBasedOn_(TypeId typeId, TypeIds... baseTypeIds) const { - NanotraceHR::Tracer tracer{"is based on"_t, projectStorageCategory}; + NanotraceHR::Tracer tracer{"is based on"_t, projectStorageCategory()}; static_assert(((std::is_same_v) &&...), "Parameter must be a TypeId!"); @@ -550,7 +553,7 @@ public: TypeId fetchTypeIdByExportedName(Utils::SmallStringView name) const { - NanotraceHR::Tracer tracer{"is based on"_t, projectStorageCategory}; + NanotraceHR::Tracer tracer{"is based on"_t, projectStorageCategory()}; return selectTypeIdByExportedNameStatement.template valueWithTransaction(name); } @@ -612,7 +615,7 @@ public: SourceContextId fetchSourceContextIdUnguarded(Utils::SmallStringView sourceContextPath) { - NanotraceHR::Tracer tracer{"fetch source context id unguarded"_t, projectStorageCategory}; + NanotraceHR::Tracer tracer{"fetch source context id unguarded"_t, projectStorageCategory()}; auto sourceContextId = readSourceContextId(sourceContextPath); @@ -621,7 +624,7 @@ public: SourceContextId fetchSourceContextId(Utils::SmallStringView sourceContextPath) { - NanotraceHR::Tracer tracer{"fetch source context id"_t, projectStorageCategory}; + NanotraceHR::Tracer tracer{"fetch source context id"_t, projectStorageCategory()}; try { return Sqlite::withDeferredTransaction(database, [&] { @@ -634,7 +637,7 @@ public: Utils::PathString fetchSourceContextPath(SourceContextId sourceContextId) const { - NanotraceHR::Tracer tracer{"fetch source context path"_t, projectStorageCategory}; + NanotraceHR::Tracer tracer{"fetch source context path"_t, projectStorageCategory()}; return Sqlite::withDeferredTransaction(database, [&] { auto optionalSourceContextPath = selectSourceContextPathFromSourceContextsBySourceContextIdStatement @@ -650,7 +653,7 @@ public: auto fetchAllSourceContexts() const { - NanotraceHR::Tracer tracer{"fetch all source contexts"_t, projectStorageCategory}; + NanotraceHR::Tracer tracer{"fetch all source contexts"_t, projectStorageCategory()}; return selectAllSourceContextsStatement .template valuesWithTransaction(); @@ -658,7 +661,7 @@ public: SourceId fetchSourceId(SourceContextId sourceContextId, Utils::SmallStringView sourceName) { - NanotraceHR::Tracer tracer{"fetch source id"_t, projectStorageCategory}; + NanotraceHR::Tracer tracer{"fetch source id"_t, projectStorageCategory()}; return Sqlite::withDeferredTransaction(database, [&] { return fetchSourceIdUnguarded(sourceContextId, sourceName); @@ -668,7 +671,7 @@ public: auto fetchSourceNameAndSourceContextId(SourceId sourceId) const { NanotraceHR::Tracer tracer{"fetch source name and source context id"_t, - projectStorageCategory}; + projectStorageCategory()}; auto value = selectSourceNameAndSourceContextIdFromSourcesBySourceIdStatement .template valueWithTransaction(sourceId); @@ -689,7 +692,7 @@ public: SourceContextId fetchSourceContextId(SourceId sourceId) const { - NanotraceHR::Tracer tracer{"fetch source context id"_t, projectStorageCategory}; + NanotraceHR::Tracer tracer{"fetch source context id"_t, projectStorageCategory()}; auto sourceContextId = selectSourceContextIdFromSourcesBySourceIdStatement .template valueWithTransaction(sourceId); @@ -702,14 +705,14 @@ public: auto fetchAllSources() const { - NanotraceHR::Tracer tracer{"fetch all sources"_t, projectStorageCategory}; + NanotraceHR::Tracer tracer{"fetch all sources"_t, projectStorageCategory()}; return selectAllSourcesStatement.template valuesWithTransaction(); } SourceId fetchSourceIdUnguarded(SourceContextId sourceContextId, Utils::SmallStringView sourceName) { - NanotraceHR::Tracer tracer{"fetch source id unguarded"_t, projectStorageCategory}; + NanotraceHR::Tracer tracer{"fetch source id unguarded"_t, projectStorageCategory()}; auto sourceId = readSourceId(sourceContextId, sourceName); @@ -721,14 +724,14 @@ public: auto fetchAllFileStatuses() const { - NanotraceHR::Tracer tracer{"fetch all file statuses"_t, projectStorageCategory}; + NanotraceHR::Tracer tracer{"fetch all file statuses"_t, projectStorageCategory()}; return selectAllFileStatusesStatement.template rangeWithTransaction(); } FileStatus fetchFileStatus(SourceId sourceId) const override { - NanotraceHR::Tracer tracer{"fetch file status"_t, projectStorageCategory}; + NanotraceHR::Tracer tracer{"fetch file status"_t, projectStorageCategory()}; return selectFileStatusesForSourceIdStatement.template valueWithTransaction( sourceId); @@ -736,7 +739,7 @@ public: std::optional fetchProjectData(SourceId sourceId) const override { - NanotraceHR::Tracer tracer{"fetch project data"_t, projectStorageCategory}; + NanotraceHR::Tracer tracer{"fetch project data"_t, projectStorageCategory()}; return selectProjectDataForSourceIdStatement .template optionalValueWithTransaction(sourceId); @@ -744,7 +747,7 @@ public: Storage::Synchronization::ProjectDatas fetchProjectDatas(SourceId projectSourceId) const override { - NanotraceHR::Tracer tracer{"fetch project datas by source id"_t, projectStorageCategory}; + NanotraceHR::Tracer tracer{"fetch project datas by source id"_t, projectStorageCategory()}; return selectProjectDatasForSourceIdStatement .template valuesWithTransaction( @@ -753,7 +756,7 @@ public: Storage::Synchronization::ProjectDatas fetchProjectDatas(const SourceIds &projectSourceIds) const { - NanotraceHR::Tracer tracer{"fetch project datas by source ids"_t, projectStorageCategory}; + NanotraceHR::Tracer tracer{"fetch project datas by source ids"_t, projectStorageCategory()}; return selectProjectDatasForSourceIdsStatement .template valuesWithTransaction( @@ -776,7 +779,7 @@ public: Storage::Imports fetchDocumentImports() const { - NanotraceHR::Tracer tracer{"fetch document imports"_t, projectStorageCategory}; + NanotraceHR::Tracer tracer{"fetch document imports"_t, projectStorageCategory()}; return selectAllDocumentImportForSourceIdStatement .template valuesWithTransaction(); @@ -1030,7 +1033,7 @@ private: void synchronizeTypeAnnotations(Storage::Synchronization::TypeAnnotations &typeAnnotations, const SourceIds &updatedTypeAnnotationSourceIds) { - NanotraceHR::Tracer tracer{"synchronize type annotations"_t, projectStorageCategory}; + NanotraceHR::Tracer tracer{"synchronize type annotations"_t, projectStorageCategory()}; using Storage::Synchronization::TypeAnnotation; @@ -1099,7 +1102,7 @@ private: Prototypes &relinkableExtensions, const SourceIds &updatedSourceIds) { - NanotraceHR::Tracer tracer{"synchronize types"_t, projectStorageCategory}; + NanotraceHR::Tracer tracer{"synchronize types"_t, projectStorageCategory()}; Storage::Synchronization::ExportedTypes exportedTypes; exportedTypes.reserve(types.size() * 3); @@ -1156,7 +1159,7 @@ private: void synchronizeProjectDatas(Storage::Synchronization::ProjectDatas &projectDatas, const SourceIds &updatedProjectSourceIds) { - NanotraceHR::Tracer tracer{"synchronize project datas"_t, projectStorageCategory}; + NanotraceHR::Tracer tracer{"synchronize project datas"_t, projectStorageCategory()}; auto compareKey = [](auto &&first, auto &&second) { auto projectSourceIdDifference = first.projectSourceId - second.projectSourceId; @@ -1210,7 +1213,7 @@ private: void synchronizeFileStatuses(FileStatuses &fileStatuses, const SourceIds &updatedSourceIds) { - NanotraceHR::Tracer tracer{"synchronize file statuses"_t, projectStorageCategory}; + NanotraceHR::Tracer tracer{"synchronize file statuses"_t, projectStorageCategory()}; auto compareKey = [](auto &&first, auto &&second) { return first.sourceId - second.sourceId; @@ -1257,7 +1260,7 @@ private: Storage::Synchronization::ModuleExportedImports &moduleExportedImports, const ModuleIds &updatedModuleIds) { - NanotraceHR::Tracer tracer{"synchronize imports"_t, projectStorageCategory}; + NanotraceHR::Tracer tracer{"synchronize imports"_t, projectStorageCategory()}; synchromizeModuleExportedImports(moduleExportedImports, updatedModuleIds); synchronizeDocumentImports(imports, @@ -1418,7 +1421,7 @@ private: void relinkAliasPropertyDeclarations(AliasPropertyDeclarations &aliasPropertyDeclarations, const TypeIds &deletedTypeIds) { - NanotraceHR::Tracer tracer{"relink alias properties"_t, projectStorageCategory}; + NanotraceHR::Tracer tracer{"relink alias properties"_t, projectStorageCategory()}; std::sort(aliasPropertyDeclarations.begin(), aliasPropertyDeclarations.end()); @@ -1448,7 +1451,7 @@ private: void relinkPropertyDeclarations(PropertyDeclarations &relinkablePropertyDeclaration, const TypeIds &deletedTypeIds) { - NanotraceHR::Tracer tracer{"relink properties"_t, projectStorageCategory}; + NanotraceHR::Tracer tracer{"relink properties"_t, projectStorageCategory()}; std::sort(relinkablePropertyDeclaration.begin(), relinkablePropertyDeclaration.end()); @@ -1474,7 +1477,7 @@ private: const TypeIds &deletedTypeIds, Callable updateStatement) { - NanotraceHR::Tracer tracer{"relink prototypes"_t, projectStorageCategory}; + NanotraceHR::Tracer tracer{"relink prototypes"_t, projectStorageCategory()}; std::sort(relinkablePrototypes.begin(), relinkablePrototypes.end()); @@ -1504,7 +1507,7 @@ private: Prototypes &relinkableExtensions, TypeIds &deletedTypeIds) { - NanotraceHR::Tracer tracer{"delete not updated types"_t, projectStorageCategory}; + NanotraceHR::Tracer tracer{"delete not updated types"_t, projectStorageCategory()}; auto callback = [&](TypeId typeId) { deletedTypeIds.push_back(typeId); @@ -1528,7 +1531,7 @@ private: Prototypes &relinkableExtensions, TypeIds &deletedTypeIds) { - NanotraceHR::Tracer tracer{"relink"_t, projectStorageCategory}; + NanotraceHR::Tracer tracer{"relink"_t, projectStorageCategory()}; std::sort(deletedTypeIds.begin(), deletedTypeIds.end()); @@ -1596,7 +1599,7 @@ private: void linkAliases(const AliasPropertyDeclarations &insertedAliasPropertyDeclarations, const AliasPropertyDeclarations &updatedAliasPropertyDeclarations) { - NanotraceHR::Tracer tracer{"link aliases"_t, projectStorageCategory}; + NanotraceHR::Tracer tracer{"link aliases"_t, projectStorageCategory()}; linkAliasPropertyDeclarationAliasIds(insertedAliasPropertyDeclarations); linkAliasPropertyDeclarationAliasIds(updatedAliasPropertyDeclarations); @@ -1615,7 +1618,7 @@ private: Prototypes &relinkablePrototypes, Prototypes &relinkableExtensions) { - NanotraceHR::Tracer tracer{"synchronize exported types"_t, projectStorageCategory}; + NanotraceHR::Tracer tracer{"synchronize exported types"_t, projectStorageCategory()}; std::sort(exportedTypes.begin(), exportedTypes.end(), [](auto &&first, auto &&second) { if (first.moduleId < second.moduleId) @@ -1800,7 +1803,7 @@ private: AliasPropertyDeclarations &updatedAliasPropertyDeclarations, PropertyDeclarationIds &propertyDeclarationIds) { - NanotraceHR::Tracer tracer{"synchronize property declaration"_t, projectStorageCategory}; + NanotraceHR::Tracer tracer{"synchronize property declaration"_t, projectStorageCategory()}; std::sort(propertyDeclarations.begin(), propertyDeclarations.end(), @@ -1918,7 +1921,8 @@ private: Storage::Synchronization::Types &types, AliasPropertyDeclarations &relinkableAliasPropertyDeclarations) { - NanotraceHR::Tracer tracer{"reset removed alias properties to null"_t, projectStorageCategory}; + NanotraceHR::Tracer tracer{"reset removed alias properties to null"_t, + projectStorageCategory()}; PropertyDeclarationIds propertyDeclarationIds; propertyDeclarationIds.reserve(types.size()); @@ -2117,7 +2121,8 @@ private: void synchronizePropertyEditorQmlPaths(Storage::Synchronization::PropertyEditorQmlPaths &paths, SourceIds updatedPropertyEditorQmlPathsSourceIds) { - NanotraceHR::Tracer tracer{"synchronize property editor qml paths"_t, projectStorageCategory}; + NanotraceHR::Tracer tracer{"synchronize property editor qml paths"_t, + projectStorageCategory()}; addTypeIdToPropertyEditorQmlPaths(paths); synchronizePropertyEditorPaths(paths, updatedPropertyEditorQmlPathsSourceIds); @@ -2126,7 +2131,7 @@ private: void synchronizeFunctionDeclarations( TypeId typeId, Storage::Synchronization::FunctionDeclarations &functionsDeclarations) { - NanotraceHR::Tracer tracer{"synchronize function declaration"_t, projectStorageCategory}; + NanotraceHR::Tracer tracer{"synchronize function declaration"_t, projectStorageCategory()}; std::sort(functionsDeclarations.begin(), functionsDeclarations.end(), @@ -2185,7 +2190,7 @@ private: void synchronizeSignalDeclarations(TypeId typeId, Storage::Synchronization::SignalDeclarations &signalDeclarations) { - NanotraceHR::Tracer tracer{"synchronize signal declaration"_t, projectStorageCategory}; + NanotraceHR::Tracer tracer{"synchronize signal declaration"_t, projectStorageCategory()}; std::sort(signalDeclarations.begin(), signalDeclarations.end(), [](auto &&first, auto &&second) { auto compare = Sqlite::compare(first.name, second.name); @@ -2261,7 +2266,7 @@ private: void synchronizeEnumerationDeclarations( TypeId typeId, Storage::Synchronization::EnumerationDeclarations &enumerationDeclarations) { - NanotraceHR::Tracer tracer{"synchronize enumeation declaration"_t, projectStorageCategory}; + NanotraceHR::Tracer tracer{"synchronize enumeation declaration"_t, projectStorageCategory()}; std::sort(enumerationDeclarations.begin(), enumerationDeclarations.end(), @@ -2335,7 +2340,7 @@ private: AliasPropertyDeclarations &updatedAliasPropertyDeclarations, PropertyDeclarationIds &propertyDeclarationIds) { - NanotraceHR::Tracer tracer{"synchronize declaration per type"_t, projectStorageCategory}; + NanotraceHR::Tracer tracer{"synchronize declaration per type"_t, projectStorageCategory()}; if (type.changeLevel == Storage::Synchronization::ChangeLevel::Minimal) return; @@ -2354,7 +2359,7 @@ private: template void removeRelinkableEntries(std::vector &relinkables, Ids &ids, Compare compare) { - NanotraceHR::Tracer tracer{"remove relinkable entries"_t, projectStorageCategory}; + NanotraceHR::Tracer tracer{"remove relinkable entries"_t, projectStorageCategory()}; std::vector newRelinkables; newRelinkables.reserve(relinkables.size()); @@ -2378,7 +2383,7 @@ private: AliasPropertyDeclarations &updatedAliasPropertyDeclarations, PropertyDeclarations &relinkablePropertyDeclarations) { - NanotraceHR::Tracer tracer{"synchronize declaration"_t, projectStorageCategory}; + NanotraceHR::Tracer tracer{"synchronize declaration"_t, projectStorageCategory()}; PropertyDeclarationIds propertyDeclarationIds; propertyDeclarationIds.reserve(types.size() * 10); @@ -2408,7 +2413,7 @@ private: void syncDefaultProperties(Storage::Synchronization::Types &types) { - NanotraceHR::Tracer tracer{"synchronize default properties"_t, projectStorageCategory}; + NanotraceHR::Tracer tracer{"synchronize default properties"_t, projectStorageCategory()}; auto range = selectTypesWithDefaultPropertyStatement.template range(); @@ -2444,7 +2449,7 @@ private: void resetDefaultPropertiesIfChanged(Storage::Synchronization::Types &types) { - NanotraceHR::Tracer tracer{"reset changed default properties"_t, projectStorageCategory}; + NanotraceHR::Tracer tracer{"reset changed default properties"_t, projectStorageCategory()}; auto range = selectTypesWithDefaultPropertyStatement.template range(); @@ -2544,7 +2549,7 @@ private: Prototypes &relinkablePrototypes, Prototypes &relinkableExtensions) { - NanotraceHR::Tracer tracer{"synchronize prototypes"_t, projectStorageCategory}; + NanotraceHR::Tracer tracer{"synchronize prototypes"_t, projectStorageCategory()}; TypeIds typeIds; typeIds.reserve(types.size()); diff --git a/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.cpp b/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.cpp index f780e813ddd..ea0fe73c0f4 100644 --- a/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.cpp +++ b/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.cpp @@ -32,7 +32,7 @@ namespace ModelTracing { namespace { using namespace NanotraceHR::Literals; -thread_local Category category_{"model"_t, Tracing::stringViewEventWithStringArgumentsQueue_}; +thread_local Category category_{"model"_t, Tracing::stringViewEventWithStringArgumentsQueue_, category}; } // namespace From 9026338ec3be3ffffe9e09b42c45dbe2037f935a Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Wed, 18 Oct 2023 13:14:58 +0200 Subject: [PATCH 065/242] QmlDesigner: Use flows Change-Id: I46f4f4eeccfb61fbc24bbe774cc04a62c0c4bce5 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Tim Jenssen Reviewed-by: Thomas Hartmann --- .../imagecache/asynchronousimagecache.cpp | 35 ++++++++++--------- .../imagecache/asynchronousimagefactory.cpp | 27 ++++++++++++-- .../imagecache/asynchronousimagefactory.h | 20 +++++------ .../imagecache/imagecachecollector.cpp | 11 +++--- .../imagecache/imagecachegenerator.cpp | 2 -- .../designercore/imagecache/taskqueue.h | 15 +++++--- .../imagecache/textureimagecachecollector.h | 2 +- .../include/imagecacheauxiliarydata.h | 6 ++-- 8 files changed, 74 insertions(+), 44 deletions(-) diff --git a/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagecache.cpp b/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagecache.cpp index ad4e4aa40fb..d7a089df684 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagecache.cpp +++ b/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagecache.cpp @@ -56,9 +56,15 @@ void AsynchronousImageCache::request(Utils::SmallStringView name, const auto id = extraId.empty() ? Utils::PathString{name} : Utils::PathString::join({name, "+", extraId}); + auto [durationToken, flowToken] = traceToken.beginDurationWithFlow( + "AsynchronousImageCache works on the image request"_t); + + auto timeStrampToken = durationToken.beginDuration("getting timestamp"_t); const auto timeStamp = timeStampProvider.timeStamp(name); + timeStrampToken.end(); + + auto storageTraceToken = durationToken.beginDuration("fetching image from storage"_t); auto requestImageFromStorage = [&](RequestType requestType) { - auto storageTraceToken = traceToken.begin("fetching image from storage"_t); switch (requestType) { case RequestType::Image: return storage.fetchImage(id, timeStamp); @@ -74,6 +80,7 @@ void AsynchronousImageCache::request(Utils::SmallStringView name, }; const auto entry = requestImageFromStorage(requestType); + storageTraceToken.end(); if (entry) { if (entry->isNull()) { @@ -82,7 +89,6 @@ void AsynchronousImageCache::request(Utils::SmallStringView name, } else { captureCallback(*entry); } - traceToken.end(); } else { auto imageGeneratedCallback = [captureCallback = std::move(captureCallback), @@ -90,6 +96,7 @@ void AsynchronousImageCache::request(Utils::SmallStringView name, const QImage &midSizeImage, const QImage &smallImage, ImageCache::TraceToken traceToken) { + auto token = traceToken.beginDuration("call capture callback"_t); auto selectImage = [](RequestType requestType, const QImage &image, const QImage &midSizeImage, @@ -107,7 +114,6 @@ void AsynchronousImageCache::request(Utils::SmallStringView name, return image; }; - traceToken.end(); captureCallback(selectImage(requestType, image, midSizeImage, smallImage)); }; @@ -115,7 +121,6 @@ void AsynchronousImageCache::request(Utils::SmallStringView name, [abortCallback = std::move(abortCallback)](ImageCache::AbortReason reason, ImageCache::TraceToken traceToken) { traceToken.tick("image could not be created"_t); - traceToken.end(); abortCallback(reason); }; @@ -137,16 +142,16 @@ void AsynchronousImageCache::requestImage(Utils::SmallStringView name, Utils::SmallStringView extraId, ImageCache::AuxiliaryData auxiliaryData) { - auto traceToken = ImageCache::category().beginAsynchronous( - "request image in asynchornous image cache"_t); - m_taskQueue.addTask(traceToken.createToken(), + auto [trace, flowToken] = ImageCache::category().beginDurationWithFlow( + "request image in asynchronous image cache"_t); + m_taskQueue.addTask(trace.createToken(), std::move(name), std::move(extraId), std::move(captureCallback), std::move(abortCallback), std::move(auxiliaryData), RequestType::Image, - std ::move(traceToken)); + std ::move(flowToken)); } void AsynchronousImageCache::requestMidSizeImage(Utils::SmallStringView name, @@ -155,8 +160,8 @@ void AsynchronousImageCache::requestMidSizeImage(Utils::SmallStringView name, Utils::SmallStringView extraId, ImageCache::AuxiliaryData auxiliaryData) { - auto traceToken = ImageCache::category().beginAsynchronous( - "request mid size image in asynchornous image cache"_t); + auto [traceToken, flowToken] = ImageCache::category().beginDurationWithFlow( + "request mid size image in asynchronous image cache"_t); m_taskQueue.addTask(traceToken.createToken(), std::move(name), std::move(extraId), @@ -164,7 +169,7 @@ void AsynchronousImageCache::requestMidSizeImage(Utils::SmallStringView name, std::move(abortCallback), std::move(auxiliaryData), RequestType::MidSizeImage, - std ::move(traceToken)); + std ::move(flowToken)); } void AsynchronousImageCache::requestSmallImage(Utils::SmallStringView name, @@ -173,8 +178,8 @@ void AsynchronousImageCache::requestSmallImage(Utils::SmallStringView name, Utils::SmallStringView extraId, ImageCache::AuxiliaryData auxiliaryData) { - auto traceToken = ImageCache::category().beginAsynchronous( - "request small size image in asynchornous image cache"_t); + auto [traceToken, flowtoken] = ImageCache::category().beginDurationWithFlow( + "request small size image in asynchronous image cache"_t); m_taskQueue.addTask(traceToken.createToken(), std::move(name), std::move(extraId), @@ -182,7 +187,7 @@ void AsynchronousImageCache::requestSmallImage(Utils::SmallStringView name, std::move(abortCallback), std::move(auxiliaryData), RequestType::SmallImage, - std ::move(traceToken)); + std ::move(flowtoken)); } void AsynchronousImageCache::clean() @@ -214,8 +219,6 @@ void AsynchronousImageCache::Clean::operator()(Entry &entry) entry.traceToken.tick("cleaning up in the cache"_t); entry.abortCallback(ImageCache::AbortReason::Abort); - - entry.traceToken.end(); } } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagefactory.cpp b/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagefactory.cpp index f7c9d59e9cf..6feaea8150d 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagefactory.cpp +++ b/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagefactory.cpp @@ -10,6 +10,8 @@ namespace QmlDesigner { +using namespace NanotraceHR::Literals; + AsynchronousImageFactory::AsynchronousImageFactory(ImageCacheStorageInterface &storage, TimeStampProviderInterface &timeStampProvider, ImageCacheCollectorInterface &collector) @@ -24,7 +26,13 @@ void AsynchronousImageFactory::generate(Utils::SmallStringView name, Utils::SmallStringView extraId, ImageCache::AuxiliaryData auxiliaryData) { - m_taskQueue.addTask(name, extraId, std::move(auxiliaryData)); + auto [trace, flowToken] = ImageCache::category().beginDurationWithFlow( + "request image in asynchronous image factory"_t); + m_taskQueue.addTask(trace.createToken(), + name, + extraId, + std::move(auxiliaryData), + std::move(flowToken)); } AsynchronousImageFactory::~AsynchronousImageFactory() {} @@ -34,8 +42,10 @@ void AsynchronousImageFactory::request(Utils::SmallStringView name, ImageCache::AuxiliaryData auxiliaryData, ImageCacheStorageInterface &storage, TimeStampProviderInterface &timeStampProvider, - ImageCacheCollectorInterface &collector) + ImageCacheCollectorInterface &collector, + ImageCache::TraceToken traceToken) { + auto [storageTracer, flowToken] = traceToken.beginDurationWithFlow("starte image generator"_t); const auto id = extraId.empty() ? Utils::PathString{name} : Utils::PathString::join({name, "+", extraId}); @@ -58,11 +68,22 @@ void AsynchronousImageFactory::request(Utils::SmallStringView name, std::move(auxiliaryData), std::move(capture), ImageCache::InternalAbortCallback{}, - {}); + std::move(flowToken)); } void AsynchronousImageFactory::clean() { m_taskQueue.clean(); } + +void AsynchronousImageFactory::Dispatch::operator()(Entry &entry) +{ + request(entry.name, + entry.extraId, + std::move(entry.auxiliaryData), + storage, + timeStampProvider, + collector, + std::move(entry.traceToken)); +} } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagefactory.h b/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagefactory.h index f53ccc18ed7..bfd76a5ccac 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagefactory.h +++ b/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagefactory.h @@ -39,17 +39,22 @@ private: struct Entry { Entry() = default; + Entry(Utils::PathString name, Utils::SmallString extraId, - ImageCache::AuxiliaryData &&auxiliaryData) + ImageCache::AuxiliaryData &&auxiliaryData, + ImageCache::TraceToken traceToken) : name{std::move(name)} , extraId{std::move(extraId)} , auxiliaryData{std::move(auxiliaryData)} + , traceToken{std::move(traceToken)} + {} Utils::PathString name; Utils::SmallString extraId; ImageCache::AuxiliaryData auxiliaryData; + NO_UNIQUE_ADDRESS ImageCache::TraceToken traceToken; }; static void request(Utils::SmallStringView name, @@ -57,19 +62,12 @@ private: ImageCache::AuxiliaryData auxiliaryData, ImageCacheStorageInterface &storage, TimeStampProviderInterface &timeStampProvider, - ImageCacheCollectorInterface &collector); + ImageCacheCollectorInterface &collector, + ImageCache::TraceToken traceToken); struct Dispatch { - void operator()(Entry &entry) - { - request(entry.name, - entry.extraId, - std::move(entry.auxiliaryData), - storage, - timeStampProvider, - collector); - } + void operator()(Entry &entry); ImageCacheStorageInterface &storage; TimeStampProviderInterface &timeStampProvider; diff --git a/src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.cpp b/src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.cpp index 6b37940c072..9d32b803cba 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.cpp +++ b/src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.cpp @@ -77,7 +77,8 @@ void ImageCacheCollector::start(Utils::SmallStringView name, ImageCache::TraceToken traceToken) { using namespace NanotraceHR::Literals; - auto collectorTraceToken = traceToken.begin("generate image in standard collector"_t); + auto [collectorTraceToken, flowtoken] = traceToken.beginDurationWithFlow( + "generate image in standard collector"_t); RewriterView rewriterView{m_externalDependencies, RewriterView::Amend}; NodeInstanceView nodeInstanceView{m_connectionManager, m_externalDependencies}; @@ -105,7 +106,7 @@ void ImageCacheCollector::start(Utils::SmallStringView name, if (!rewriterView.errors().isEmpty() || (!rewriterView.rootModelNode().metaInfo().isGraphicalItem() && !is3DRoot)) { if (abortCallback) - abortCallback(ImageCache::AbortReason::Failed, std::move(traceToken)); + abortCallback(ImageCache::AbortReason::Failed, std::move(flowtoken)); return; } @@ -143,16 +144,16 @@ void ImageCacheCollector::start(Utils::SmallStringView name, model->setRewriterView({}); if (isCrashed) - abortCallback(ImageCache::AbortReason::Failed, std::move(traceToken)); + abortCallback(ImageCache::AbortReason::Failed, std::move(flowtoken)); if (!capturedDataArrived && abortCallback) - abortCallback(ImageCache::AbortReason::Failed, std::move(traceToken)); + abortCallback(ImageCache::AbortReason::Failed, std::move(flowtoken)); if (nullImageHandling == ImageCacheCollectorNullImageHandling::CaptureNullImage || !captureImage.isNull()) { QImage midSizeImage = scaleImage(captureImage, QSize{300, 300}); QImage smallImage = scaleImage(midSizeImage, QSize{96, 96}); - captureCallback(captureImage, midSizeImage, smallImage, std::move(traceToken)); + captureCallback(captureImage, midSizeImage, smallImage, std::move(flowtoken)); } } diff --git a/src/plugins/qmldesigner/designercore/imagecache/imagecachegenerator.cpp b/src/plugins/qmldesigner/designercore/imagecache/imagecachegenerator.cpp index bf57766e5e6..60b51130312 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/imagecachegenerator.cpp +++ b/src/plugins/qmldesigner/designercore/imagecache/imagecachegenerator.cpp @@ -95,8 +95,6 @@ void ImageCacheGenerator::clean() std::lock_guard lock{m_mutex}; for (Task &task : m_tasks) { - task.traceToken.tick("cleaning up in the generator"_t); - task.traceToken.end(); callCallbacks(task.abortCallbacks, ImageCache::AbortReason::Abort, std::move(task.traceToken)); } m_tasks.clear(); diff --git a/src/plugins/qmldesigner/designercore/imagecache/taskqueue.h b/src/plugins/qmldesigner/designercore/imagecache/taskqueue.h index ff17fef1cd8..fac7e7d9bfe 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/taskqueue.h +++ b/src/plugins/qmldesigner/designercore/imagecache/taskqueue.h @@ -107,18 +107,25 @@ private: m_sleeping = false; - auto threadCreateToken = traceToken.beginDuration("thread is created in the task queue"_t); + auto [threadCreateToken, flowToken] = traceToken.beginDurationWithFlow( + "thread is created in the task queue"_t); m_backgroundThread = std::thread{[this](auto traceToken) { - traceToken.tick("thread is ready"_t); + auto duration = traceToken.beginDuration( + "thread is ready"_t); while (true) { auto [lock, abort] = waitForTasks(); + duration.end(); if (abort) return; - if (auto task = getTask(std::move(lock)); task) + auto getTaskToken = duration.beginDuration( + "get task from queue"_t); + if (auto task = getTask(std::move(lock)); task) { + getTaskToken.end(); m_dispatchCallback(*task); + } } }, - std::move(traceToken)}; + std::move(flowToken)}; } void clearTasks(Tasks &tasks) diff --git a/src/plugins/qmldesigner/designercore/imagecache/textureimagecachecollector.h b/src/plugins/qmldesigner/designercore/imagecache/textureimagecachecollector.h index fceb06100bc..fa8885b4d4a 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/textureimagecachecollector.h +++ b/src/plugins/qmldesigner/designercore/imagecache/textureimagecachecollector.h @@ -18,7 +18,7 @@ public: const ImageCache::AuxiliaryData &auxiliaryData, CaptureCallback captureCallback, AbortCallback abortCallback, - ImageCache::TraceToken traceToken = {}) override; + ImageCache::TraceToken traceToken) override; ImageTuple createImage(Utils::SmallStringView filePath, Utils::SmallStringView state, diff --git a/src/plugins/qmldesigner/designercore/include/imagecacheauxiliarydata.h b/src/plugins/qmldesigner/designercore/include/imagecacheauxiliarydata.h index fc33cb98e21..15ad35545af 100644 --- a/src/plugins/qmldesigner/designercore/include/imagecacheauxiliarydata.h +++ b/src/plugins/qmldesigner/designercore/include/imagecacheauxiliarydata.h @@ -27,8 +27,10 @@ constexpr NanotraceHR::Tracing tracingStatus() } using Category = NanotraceHR::StringViewCategory; -using TraceToken = Category::AsynchronousTokenType; -Category &category(); +using TraceToken = Category::FlowTokenType; +using FlowToken = Category::FlowTokenType; +using Token = Category::TokenType; +extern Category &category(); class FontCollectorSizeAuxiliaryData { From 4b0b8568efe62a1e88d575d57410e9941d9be3b8 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Wed, 18 Oct 2023 15:41:54 +0200 Subject: [PATCH 066/242] Nanotrace: Improve argument creation To avoid any overhead for non activated categories we only take references and if the the category is activated we create a string. But std::forward_as_tuple() is not very readable. So there is now keyValue(), dictonary() and array(). So you can now write keyValue("ids", array(foo, bar, hoo)) or keyValue("ids", dictonary(keyValue("foo", foo), keyValue("bar", bar))) and so on. Change-Id: I08d268764d43fcf137b628f5d42ec37aaadd3b1a Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Thomas Hartmann Reviewed-by: --- src/libs/nanotrace/nanotracehr.h | 18 ++++++++++++++++++ .../designercore/model/internalproperty.h | 6 +++++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/libs/nanotrace/nanotracehr.h b/src/libs/nanotrace/nanotracehr.h index 48aa61d37a1..04a0fd0d554 100644 --- a/src/libs/nanotrace/nanotracehr.h +++ b/src/libs/nanotrace/nanotracehr.h @@ -259,6 +259,24 @@ template } // namespace Internal +template +auto keyValue(Key &&key, Value &&value) +{ + return std::forward_as_tuple(std::forward(key), std::forward(value)); +} + +template +auto dictonary(Entries &&...entries) +{ + return std::forward_as_tuple(isDictonary, std::forward(entries)...); +} + +template +auto array(Entries &&...entries) +{ + return std::forward_as_tuple(isArray, std::forward(entries)...); +} + enum class IsFlow : std::size_t { No = 0, Out = 1 << 0, In = 1 << 1, InOut = In | Out }; inline bool operator&(IsFlow first, IsFlow second) diff --git a/src/plugins/qmldesigner/designercore/model/internalproperty.h b/src/plugins/qmldesigner/designercore/model/internalproperty.h index 2dfcdd616bb..f93b5e85dde 100644 --- a/src/plugins/qmldesigner/designercore/model/internalproperty.h +++ b/src/plugins/qmldesigner/designercore/model/internalproperty.h @@ -88,6 +88,10 @@ struct TypeLookup template using type_lookup_t = typename TypeLookup::Type; +using NanotraceHR::array; +using NanotraceHR::dictonary; +using NanotraceHR::keyValue; + class QMLDESIGNERCORE_EXPORT InternalProperty : public std::enable_shared_from_this { public: @@ -190,7 +194,7 @@ private: std::weak_ptr m_propertyOwner; PropertyType m_propertyType = PropertyType::None; NO_UNIQUE_ADDRESS ModelTracing::ObjectTraceToken traceToken = ModelTracing::category().beginObject( - "InternalProperty"_t, std::forward_as_tuple("name", m_name)); + "InternalProperty"_t, keyValue("name", m_name)); }; } // namespace Internal From 1a9a34ec9d0dcca4b1bef70e96ff6f7d5fc63de6 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Wed, 18 Oct 2023 16:33:41 +0200 Subject: [PATCH 067/242] Nanotrace: Improve event queue cleanup It seem that some of the event queue destructor never get called. QCoreApplocation finished was used for clean up but the signal comes quite early. Change-Id: I5f143d0754a704e47e617828c1b7f5b58aad31ed Reviewed-by: Thomas Hartmann --- src/libs/nanotrace/nanotracehr.cpp | 9 ++---- src/libs/nanotrace/nanotracehr.h | 49 ++++++++++++++++++++++++++++-- 2 files changed, 50 insertions(+), 8 deletions(-) diff --git a/src/libs/nanotrace/nanotracehr.cpp b/src/libs/nanotrace/nanotracehr.cpp index 9e679e350c0..c8160926a95 100644 --- a/src/libs/nanotrace/nanotracehr.cpp +++ b/src/libs/nanotrace/nanotracehr.cpp @@ -204,11 +204,8 @@ EventQueue::EventQueue(EnabledTraceFile *file, , currentEvents{eventsOne} , threadId{std::this_thread::get_id()} { + Internal::EventQueueTracker::get().addQueue(this); if (auto thread = QThread::currentThread()) { - connection = QObject::connect(QCoreApplication::instance(), - &QCoreApplication::aboutToQuit, - thread, - [&] { flush(); }); auto name = getThreadName(); if (name.size()) { writeMetaEvent(file, "thread_name", name); @@ -219,9 +216,9 @@ EventQueue::EventQueue(EnabledTraceFile *file, template EventQueue::~EventQueue() { + Internal::EventQueueTracker::get().removeQueue(this); + flush(); - if (connection) - QObject::disconnect(connection); } template diff --git a/src/libs/nanotrace/nanotracehr.h b/src/libs/nanotrace/nanotracehr.h index 04a0fd0d554..675557aa204 100644 --- a/src/libs/nanotrace/nanotracehr.h +++ b/src/libs/nanotrace/nanotracehr.h @@ -9,7 +9,6 @@ #include #include -#include #include #include @@ -392,6 +391,53 @@ public: using IsActive = std::false_type; }; +namespace Internal { + +template +class EventQueueTracker +{ + using Queue = EventQueue; + +public: + EventQueueTracker() = default; + EventQueueTracker(const EventQueueTracker &) = delete; + EventQueueTracker(EventQueueTracker &&) = delete; + EventQueueTracker &operator=(const EventQueueTracker &) = delete; + EventQueueTracker &operator=(EventQueueTracker &&) = delete; + + ~EventQueueTracker() + { + std::lock_guard lock{mutex}; + + for (auto queue : queues) + queue->flush(); + } + + void addQueue(Queue *queue) + { + std::lock_guard lock{mutex}; + queues.push_back(queue); + } + + void removeQueue(Queue *queue) + { + std::lock_guard lock{mutex}; + queues.erase(std::remove(queues.begin(), queues.end(), queue), queues.end()); + } + + static EventQueueTracker &get() + { + static EventQueueTracker tracker; + + return tracker; + } + +private: + std::mutex mutex; + std::vector queues; +}; +} // namespace Internal + template class EventQueue { @@ -417,7 +463,6 @@ public: TraceEventsSpan currentEvents; std::size_t eventsIndex = 0; IsEnabled isEnabled = IsEnabled::Yes; - QMetaObject::Connection connection; std::mutex mutex; std::thread::id threadId; }; From be6e043257730d28105f1dc580e5df5e1d84d8bf Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Wed, 18 Oct 2023 17:53:47 +0200 Subject: [PATCH 068/242] Nanotrace: Allow 1:N relationships for flows There can be more than one flow connection from a slice. So we can simply copy the FlowToken. That makes it possible to capture them with std::function too. Change-Id: I5c36de92e8eba8da3839d1d6670cdeb795d84554 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Reviewed-by: Tim Jenssen --- src/libs/nanotrace/nanotracehr.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libs/nanotrace/nanotracehr.h b/src/libs/nanotrace/nanotracehr.h index 675557aa204..944d796b247 100644 --- a/src/libs/nanotrace/nanotracehr.h +++ b/src/libs/nanotrace/nanotracehr.h @@ -978,8 +978,8 @@ public: friend TokenType; friend Category; - FlowToken(const FlowToken &) = delete; - FlowToken &operator=(const FlowToken &) = delete; + FlowToken(const FlowToken &) = default; + FlowToken &operator=(const FlowToken &) = default; FlowToken(FlowToken &&other) noexcept = default; FlowToken &operator=(FlowToken &&other) noexcept = default; @@ -1045,8 +1045,8 @@ public: friend TracerType; friend Category; - FlowToken(const FlowToken &) = delete; - FlowToken &operator=(const FlowToken &) = delete; + FlowToken(const FlowToken &) = default; + FlowToken &operator=(const FlowToken &) = default; FlowToken(FlowToken &&other) noexcept = default; FlowToken &operator=(FlowToken &&other) noexcept = default; From 7d9572615cce2db24bd9eafa865787ab5acd6509 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Thu, 19 Oct 2023 06:58:42 +0200 Subject: [PATCH 069/242] Nanotrace: Use large SmallString for arguments That is preventing almost all allocations for arguments. So they are cheaper now. The reserve is removed to prevent any accidental allocations. Change-Id: I6b6f57ead126f9f7d07f9c4d41f53b0488a80ae8 Reviewed-by: Tim Jenssen Reviewed-by: Qt CI Patch Build Bot --- src/libs/nanotrace/nanotracehr.cpp | 2 ++ src/libs/nanotrace/nanotracehr.h | 4 ++-- src/libs/utils/smallstring.h | 2 ++ 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/libs/nanotrace/nanotracehr.cpp b/src/libs/nanotrace/nanotracehr.cpp index c8160926a95..b621be7095b 100644 --- a/src/libs/nanotrace/nanotracehr.cpp +++ b/src/libs/nanotrace/nanotracehr.cpp @@ -3,6 +3,8 @@ #include "nanotracehr.h" +#include + #include #include diff --git a/src/libs/nanotrace/nanotracehr.h b/src/libs/nanotrace/nanotracehr.h index 944d796b247..6f7d1b70a4e 100644 --- a/src/libs/nanotrace/nanotracehr.h +++ b/src/libs/nanotrace/nanotracehr.h @@ -215,7 +215,6 @@ String toArguments(Arguments &&...arguments) if constexpr (tracingStatus() == Tracing::IsEnabled) { String text; constexpr auto argumentCount = sizeof...(Arguments); - text.reserve(sizeof...(Arguments) * 30); text.append("{"); (convertDictonaryEntryToString(text, arguments), ...); if (argumentCount) @@ -309,8 +308,9 @@ struct TraceEvent char type = ' '; }; +using ArgumentsString = Utils::BasicSmallString<510>; using StringViewTraceEvent = TraceEvent; -using StringViewWithStringArgumentsTraceEvent = TraceEvent; +using StringViewWithStringArgumentsTraceEvent = TraceEvent; using StringTraceEvent = TraceEvent; enum class IsEnabled { No, Yes }; diff --git a/src/libs/utils/smallstring.h b/src/libs/utils/smallstring.h index 44f3910dc0f..e5c303430ca 100644 --- a/src/libs/utils/smallstring.h +++ b/src/libs/utils/smallstring.h @@ -241,6 +241,8 @@ public: setSize(newSize); } + void pop_back() noexcept { setSize(size() - 1); } + void clear() noexcept { this->~BasicSmallString(); From 7de7836068772d655686a50440000461dfa8f32a Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Thu, 19 Oct 2023 10:09:54 +0200 Subject: [PATCH 070/242] QmlDesigner: add more tracing arguments Change-Id: Ia809308ce5f7814d751b7ccfcb0c8a65c9ebe272 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Tim Jenssen Reviewed-by: --- src/libs/nanotrace/nanotracehr.h | 61 ++++++++++++++++--- .../imagecache/asynchronousimagecache.cpp | 49 ++++++++++++--- .../include/imagecacheauxiliarydata.h | 2 +- .../tracing/qmldesignertracing.cpp | 5 ++ .../designercore/tracing/qmldesignertracing.h | 5 +- 5 files changed, 104 insertions(+), 18 deletions(-) diff --git a/src/libs/nanotrace/nanotracehr.h b/src/libs/nanotrace/nanotracehr.h index 6f7d1b70a4e..f74cde055f5 100644 --- a/src/libs/nanotrace/nanotracehr.h +++ b/src/libs/nanotrace/nanotracehr.h @@ -111,13 +111,28 @@ void convertToString(String &string, QStringView text) } template -void convertToString(String &string, QByteArrayView text) +void convertToString(String &string, const QByteArray &text) { string.append(R"(")"); string.append(std::string_view(text.data(), Utils::usize(text))); string.append(R"(")"); } +template +void convertToString(String &string, bool isTrue) +{ + if (isTrue) + string.append("true"); + else + string.append("false"); +} + +template>> +void convertToString(String &string, Callable &&callable) +{ + convertToString(string, callable()); +} + template void convertToString(String &string, int number) { @@ -308,9 +323,9 @@ struct TraceEvent char type = ' '; }; -using ArgumentsString = Utils::BasicSmallString<510>; +using SmallArgumentsString = Utils::BasicSmallString<510>; using StringViewTraceEvent = TraceEvent; -using StringViewWithStringArgumentsTraceEvent = TraceEvent; +using StringViewWithStringArgumentsTraceEvent = TraceEvent; using StringTraceEvent = TraceEvent; enum class IsEnabled { No, Yes }; @@ -467,6 +482,13 @@ public: std::thread::id threadId; }; +template +using StringViewEventQueue = EventQueue; +template +using StringViewWithStringArgumentsEventQueue = EventQueue; +template +using StringEventQueue = EventQueue; + extern template class NANOTRACE_EXPORT EventQueue; extern template class NANOTRACE_EXPORT EventQueue; extern template class NANOTRACE_EXPORT EventQueue; @@ -1404,7 +1426,13 @@ public: return {}; } - void end() {} + template + void tick(ArgumentType, Arguments &&...) + {} + + template + void end(Arguments &&...) + {} ~Tracer() {} }; @@ -1480,14 +1508,22 @@ public: return {std::move(name), m_category, std::forward(arguments)...}; } - void end() + template + void tick(ArgumentType name, Arguments &&...arguments) { - sendTrace(); + m_category().begin('i', 0, name, 0, IsFlow::No, std::forward(arguments)...); + } + + template + void end(Arguments &&...arguments) + { + sendTrace(std::forward(arguments)...); m_name = {}; } private: - void sendTrace() + template + void sendTrace(Arguments &&...arguments) { if (m_name.size()) { auto category = m_category(); @@ -1496,12 +1532,19 @@ private: auto &traceEvent = getTraceEvent(category.eventQueue()); traceEvent.name = m_name; traceEvent.category = category.name(); - traceEvent.arguments = m_arguments; traceEvent.time = m_start; traceEvent.duration = duration; traceEvent.bindId = m_bindId; traceEvent.flow = flow; traceEvent.type = 'X'; + if (sizeof...(arguments)) { + m_arguments.clear(); + Internal::appendArguments(traceEvent.arguments, + std::forward( + arguments)...); + } else { + traceEvent.arguments = m_arguments; + } } } } @@ -1509,7 +1552,7 @@ private: private: TimePoint m_start; StringType m_name; - StringType m_arguments; + ArgumentsStringType m_arguments; std::size_t m_bindId; IsFlow flow; CategoryFunctionPointer m_category; diff --git a/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagecache.cpp b/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagecache.cpp index d7a089df684..daa8e3e93a6 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagecache.cpp +++ b/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagecache.cpp @@ -20,7 +20,9 @@ using namespace NanotraceHR::Literals; namespace ImageCache { namespace { -thread_local Category category_{"image cache"_t, QmlDesigner::Tracing::eventQueue(), category}; +thread_local Category category_{"image cache"_t, + QmlDesigner::Tracing::eventQueueWithStringArguments(), + category}; } // namespace Category &category() @@ -56,14 +58,21 @@ void AsynchronousImageCache::request(Utils::SmallStringView name, const auto id = extraId.empty() ? Utils::PathString{name} : Utils::PathString::join({name, "+", extraId}); + using NanotraceHR::dictonary; + using NanotraceHR::keyValue; + using namespace std::literals::string_view_literals; + auto [durationToken, flowToken] = traceToken.beginDurationWithFlow( - "AsynchronousImageCache works on the image request"_t); + "AsynchronousImageCache works on the image request"_t, + keyValue("name", name), + keyValue("extra id", extraId)); auto timeStrampToken = durationToken.beginDuration("getting timestamp"_t); const auto timeStamp = timeStampProvider.timeStamp(name); - timeStrampToken.end(); + timeStrampToken.end(keyValue("time stamp", timeStamp.value)); - auto storageTraceToken = durationToken.beginDuration("fetching image from storage"_t); + auto storageTraceToken = durationToken.beginDuration("fetching image from storage"_t, + keyValue("storage id", id)); auto requestImageFromStorage = [&](RequestType requestType) { switch (requestType) { case RequestType::Image: @@ -80,16 +89,42 @@ void AsynchronousImageCache::request(Utils::SmallStringView name, }; const auto entry = requestImageFromStorage(requestType); - storageTraceToken.end(); if (entry) { if (entry->isNull()) { - traceToken.tick("there was an null image in storage"_t); + storageTraceToken.tick("there was an null image in storage"_t); abortCallback(ImageCache::AbortReason::Failed); } else { captureCallback(*entry); + storageTraceToken.end( + keyValue("storage id", id), + keyValue("image", + dictonary(keyValue("width", entry->width()), + keyValue("height", entry->height()), + keyValue("bytes", entry->sizeInBytes()), + keyValue("has alpha channel", entry->hasAlphaChannel()), + keyValue("is color", !entry->isGrayscale()), + keyValue("pixel format", + dictonary(keyValue("bits per pixel", + entry->pixelFormat().bitsPerPixel()), + keyValue("byte order", + [&] { + if (entry->pixelFormat().byteOrder() + == QPixelFormat::BigEndian) + return "big endian"sv; + else + return "little endian"sv; + }), + keyValue("premultiplied", [&] { + if (entry->pixelFormat().premultiplied() + == QPixelFormat::Premultiplied) + return "premultiplied"sv; + else + return "alpha premultiplied"sv; + })))))); } } else { + storageTraceToken.end(); auto imageGeneratedCallback = [captureCallback = std::move(captureCallback), requestType](const QImage &image, @@ -132,7 +167,7 @@ void AsynchronousImageCache::request(Utils::SmallStringView name, std::move(imageGeneratedCallback), std::move(imageGenerationAbortedCallback), std::move(auxiliaryData), - std::move(traceToken)); + std::move(flowToken)); } } diff --git a/src/plugins/qmldesigner/designercore/include/imagecacheauxiliarydata.h b/src/plugins/qmldesigner/designercore/include/imagecacheauxiliarydata.h index 15ad35545af..a96082ee0fe 100644 --- a/src/plugins/qmldesigner/designercore/include/imagecacheauxiliarydata.h +++ b/src/plugins/qmldesigner/designercore/include/imagecacheauxiliarydata.h @@ -26,7 +26,7 @@ constexpr NanotraceHR::Tracing tracingStatus() #endif } -using Category = NanotraceHR::StringViewCategory; +using Category = NanotraceHR::StringViewWithStringArgumentsCategory; using TraceToken = Category::FlowTokenType; using FlowToken = Category::FlowTokenType; using Token = Category::TokenType; diff --git a/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.cpp b/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.cpp index ea0fe73c0f4..be723a3dccc 100644 --- a/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.cpp +++ b/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.cpp @@ -26,6 +26,11 @@ EventQueue &eventQueue() return stringViewEventQueue_; } +EventQueueWithStringArguments &eventQueueWithStringArguments() +{ + return stringViewEventWithStringArgumentsQueue_; +} + } // namespace Tracing namespace ModelTracing { diff --git a/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.h b/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.h index 81e7e6f17c4..fb3f23d2f14 100644 --- a/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.h +++ b/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.h @@ -19,8 +19,11 @@ constexpr NanotraceHR::Tracing tracingStatus() #endif } -using EventQueue = NanotraceHR::EventQueue; +using EventQueue = NanotraceHR::StringViewEventQueue; +using EventQueueWithStringArguments = NanotraceHR::StringViewWithStringArgumentsEventQueue; + QMLDESIGNERCORE_EXPORT EventQueue &eventQueue(); +QMLDESIGNERCORE_EXPORT EventQueueWithStringArguments &eventQueueWithStringArguments(); } // namespace Tracing From 544bf815f41f7b088deec556a5f22336f547ec58 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Thu, 19 Oct 2023 12:14:27 +0200 Subject: [PATCH 071/242] Nanotrace: Add QImage argument converter So it is easier to use QImage as argument. Change-Id: Ia699e9317339c4cb4ae3b69432fc0e720f08fe95 Reviewed-by: Thomas Hartmann Reviewed-by: Qt CI Patch Build Bot --- src/libs/nanotrace/CMakeLists.txt | 2 +- src/libs/nanotrace/nanotracehr.cpp | 38 +++++++++++++++++++ src/libs/nanotrace/nanotracehr.h | 11 +++++- .../imagecache/asynchronousimagecache.cpp | 27 +------------ 4 files changed, 49 insertions(+), 29 deletions(-) diff --git a/src/libs/nanotrace/CMakeLists.txt b/src/libs/nanotrace/CMakeLists.txt index fdfb137b683..725210dcdec 100644 --- a/src/libs/nanotrace/CMakeLists.txt +++ b/src/libs/nanotrace/CMakeLists.txt @@ -6,7 +6,7 @@ add_qtc_library(Nanotrace nanotraceglobals.h nanotrace.cpp nanotrace.h nanotracehr.cpp nanotracehr.h - PUBLIC_DEPENDS Qt::Core + PUBLIC_DEPENDS Qt::Core Qt::Gui PROPERTIES CXX_VISIBILITY_PRESET default VISIBILITY_INLINES_HIDDEN OFF diff --git a/src/libs/nanotrace/nanotracehr.cpp b/src/libs/nanotrace/nanotracehr.cpp index b621be7095b..3f02bd527d3 100644 --- a/src/libs/nanotrace/nanotracehr.cpp +++ b/src/libs/nanotrace/nanotracehr.cpp @@ -6,6 +6,7 @@ #include #include +#include #include #include @@ -102,6 +103,43 @@ std::string getThreadName() } // namespace +namespace Internal { +template +void convertToString(String &string, const QImage &image) +{ + using namespace std::string_view_literals; + auto dict = dictonary(keyValue("width", image.width()), + keyValue("height", image.height()), + keyValue("bytes", image.sizeInBytes()), + keyValue("has alpha channel", image.hasAlphaChannel()), + keyValue("is color", !image.isGrayscale()), + keyValue("pixel format", + dictonary(keyValue("bits per pixel", + image.pixelFormat().bitsPerPixel()), + keyValue("byte order", + [&] { + if (image.pixelFormat().byteOrder() + == QPixelFormat::BigEndian) + return "big endian"sv; + else + return "little endian"sv; + }), + keyValue("premultiplied", [&] { + if (image.pixelFormat().premultiplied() + == QPixelFormat::Premultiplied) + return "premultiplied"sv; + else + return "alpha premultiplied"sv; + })))); + + Internal::convertToString(string, dict); +} + +template NANOTRACE_EXPORT void convertToString(std::string &string, const QImage &image); +template NANOTRACE_EXPORT void convertToString(ArgumentsString &string, const QImage &image); + +} // namespace Internal + template void flushEvents(const Utils::span events, std::thread::id threadId, diff --git a/src/libs/nanotrace/nanotracehr.h b/src/libs/nanotrace/nanotracehr.h index f74cde055f5..d49e12a87a0 100644 --- a/src/libs/nanotrace/nanotracehr.h +++ b/src/libs/nanotrace/nanotracehr.h @@ -49,6 +49,8 @@ constexpr Tracing tracingStatus() # define NO_UNIQUE_ADDRESS #endif +using ArgumentsString = Utils::BasicSmallString<510>; + namespace Literals { struct TracerLiteral { @@ -94,6 +96,12 @@ void convertToString(String &string, std::string_view text) string.append(R"(")"); } +template +void convertToString(String &string, const QImage &image); + +extern template NANOTRACE_EXPORT void convertToString(std::string &string, const QImage &image); +extern template NANOTRACE_EXPORT void convertToString(ArgumentsString &string, const QImage &image); + template void convertToString(String &string, const char (&text)[size]) { @@ -323,9 +331,8 @@ struct TraceEvent char type = ' '; }; -using SmallArgumentsString = Utils::BasicSmallString<510>; using StringViewTraceEvent = TraceEvent; -using StringViewWithStringArgumentsTraceEvent = TraceEvent; +using StringViewWithStringArgumentsTraceEvent = TraceEvent; using StringTraceEvent = TraceEvent; enum class IsEnabled { No, Yes }; diff --git a/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagecache.cpp b/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagecache.cpp index daa8e3e93a6..fdd06d19a16 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagecache.cpp +++ b/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagecache.cpp @@ -96,32 +96,7 @@ void AsynchronousImageCache::request(Utils::SmallStringView name, abortCallback(ImageCache::AbortReason::Failed); } else { captureCallback(*entry); - storageTraceToken.end( - keyValue("storage id", id), - keyValue("image", - dictonary(keyValue("width", entry->width()), - keyValue("height", entry->height()), - keyValue("bytes", entry->sizeInBytes()), - keyValue("has alpha channel", entry->hasAlphaChannel()), - keyValue("is color", !entry->isGrayscale()), - keyValue("pixel format", - dictonary(keyValue("bits per pixel", - entry->pixelFormat().bitsPerPixel()), - keyValue("byte order", - [&] { - if (entry->pixelFormat().byteOrder() - == QPixelFormat::BigEndian) - return "big endian"sv; - else - return "little endian"sv; - }), - keyValue("premultiplied", [&] { - if (entry->pixelFormat().premultiplied() - == QPixelFormat::Premultiplied) - return "premultiplied"sv; - else - return "alpha premultiplied"sv; - })))))); + storageTraceToken.end(keyValue("storage id", id), keyValue("image", *entry)); } } else { storageTraceToken.end(); From 709bcd640df645e768fb151abc9492cf9843e913 Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Wed, 18 Oct 2023 11:25:10 +0300 Subject: [PATCH 072/242] QmlDesigner: Hide CollectionDetails when no collection is selected Task-number: QDS-10621 Change-Id: Ifc5f16f4cffa1e0884394b94456da51505a18b2e Reviewed-by: Mahmoud Badri Reviewed-by: Miikka Heikkinen Reviewed-by: --- .../CollectionDetailsView.qml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsView.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsView.qml index c95f1712778..aaa4e1e8f2b 100644 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsView.qml +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsView.qml @@ -13,11 +13,16 @@ Rectangle { required property var model + implicitWidth: 600 + implicitHeight: 400 + color: StudioTheme.Values.themeBackgroundColorAlternate ColumnLayout { id: topRow + visible: collectionNameText.text !== "" + spacing: 0 anchors { fill: parent @@ -231,6 +236,17 @@ Rectangle { } } + Text { + anchors.fill: parent + text: qsTr("Select a collection to continue") + visible: !topRow.visible + textFormat: Text.RichText + color: StudioTheme.Values.themeTextColor + font.pixelSize: StudioTheme.Values.mediumFontSize + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + } + TextMetrics { id: headerTextMetrics From bf99d08ca15f2a91b5f4aaa2b7e66a0ca7b4762e Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Wed, 18 Oct 2023 11:41:18 +0300 Subject: [PATCH 073/242] QmlDesigner: Add edit delegate to the CollectionDetailsView Delegates are added for the Color, Bool, Number, ad String types. The default delegate is string delegate. Task-number: QDS-10989 Change-Id: I7dbaf5451d8be778691e72e18bd3124a754e47b7 Reviewed-by: Reviewed-by: Miikka Heikkinen Reviewed-by: Mahmoud Badri --- .../CollectionDetailsEditDelegate.qml | 154 ++++++++++++++++++ .../CollectionDetailsView.qml | 7 + .../collectioneditor/collectiondetails.cpp | 37 +++++ .../collectioneditor/collectiondetails.h | 7 + .../collectiondetailsmodel.cpp | 27 ++- .../collectioneditor/collectiondetailsmodel.h | 2 +- .../collectioneditor/collectionview.cpp | 5 + .../collectioneditor/collectionview.h | 2 + src/plugins/qmldesigner/qmldesignerplugin.cpp | 2 + 9 files changed, 239 insertions(+), 4 deletions(-) create mode 100644 share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsEditDelegate.qml diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsEditDelegate.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsEditDelegate.qml new file mode 100644 index 00000000000..d5f85ce49ae --- /dev/null +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsEditDelegate.qml @@ -0,0 +1,154 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import QtQuick +import CollectionDetails 1.0 as CollectionDetails +import HelperWidgets 2.0 as HelperWidgets +import StudioControls 1.0 as StudioControls +import StudioTheme 1.0 as StudioTheme + +Item { + id: root + required property var columnType + + property var __modifier : textEditor + + width: itemColumn.width + height: itemColumn.height + + TableView.onCommit: edit = __modifier.editValue + + Component.onCompleted: { + if (edit && edit !== "") + root.__modifier.editValue = edit + } + + onActiveFocusChanged: { + if (root.activeFocus) + root.__modifier.forceActiveFocus() + } + + Connections { + id: modifierFocusConnection + target: root.__modifier + function onActiveFocusChanged() { + if (!modifierFocusConnection.target.activeFocus) + TableView.commit() + } + } + + Column { + id: itemColumn + + StudioControls.TextField { + id: textEditor + + property alias editValue: textEditor.text + + actionIndicator.visible: false + translationIndicatorVisible: false + enabled: visible + visible: false + } + + StudioControls.RealSpinBox { + id: numberEditor + + property alias editValue: numberEditor.realValue + + actionIndicator.visible: false + enabled: visible + visible: false + + realFrom: -9e9 + realTo: 9e9 + realStepSize: 1.0 + decimals: 6 + } + + StudioControls.CheckBox { + id: boolEditor + + property alias editValue: boolEditor.checked + + actionIndicatorVisible: false + enabled: visible + visible: false + } + + HelperWidgets.ColorPicker { + id: colorEditor + + property alias editValue: colorEditor.color + + width: 100 + enabled: visible + visible: false + } + } + + states: [ + State { + name: "default" + when: columnType !== CollectionDetails.DataType.Boolean + && columnType !== CollectionDetails.DataType.Color + && columnType !== CollectionDetails.DataType.Number + + PropertyChanges { + target: root + __modifier: textEditor + } + + PropertyChanges { + target: textEditor + visible: true + focus: true + } + }, + State { + name: "number" + when: columnType === CollectionDetails.DataType.Number + + PropertyChanges { + target: root + __modifier: numberEditor + } + + PropertyChanges { + target: numberEditor + visible: true + focus: true + } + }, + State { + name: "bool" + when: columnType === CollectionDetails.DataType.Boolean + + PropertyChanges { + target: root + __modifier: boolEditor + } + + PropertyChanges { + target: boolEditor + visible: true + focus: true + } + }, + State { + name: "color" + when: columnType === CollectionDetails.DataType.Color + + PropertyChanges { + target: root + __modifier: colorEditor + } + + PropertyChanges { + target: colorEditor + visible: true + focus: true + } + } + ] +} diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsView.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsView.qml index aaa4e1e8f2b..d0ac4b0dfed 100644 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsView.qml +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsView.qml @@ -201,6 +201,13 @@ Rectangle { elide: Text.ElideRight } + TableView.editDelegate: CollectionDetailsEditDelegate { + anchors { + top: itemText.top + left: itemText.left + } + } + states: [ State { name: "default" diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp index 60ab6ab8442..5b84ec55710 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp @@ -4,6 +4,7 @@ #include "collectiondetails.h" #include +#include #include #include @@ -220,6 +221,21 @@ bool CollectionDetails::removeElements(int row, int count) return true; } +bool CollectionDetails::setPropertyValue(int row, int column, const QVariant &value) +{ + if (!d->isValidRowId(row) || !d->isValidColumnId(column)) + return false; + + QJsonObject &element = d->elements[row]; + QVariant currentValue = data(row, column); + + if (value == currentValue) + return false; + + element.insert(d->properties.at(column).name, QJsonValue::fromVariant(value)); + return true; +} + bool CollectionDetails::setPropertyName(int column, const QString &value) { if (!d->isValidColumnId(column)) @@ -316,6 +332,20 @@ CollectionDetails::DataType CollectionDetails::typeAt(int column) const return d->properties.at(column).type; } +CollectionDetails::DataType CollectionDetails::typeAt(int row, int column) const +{ + if (!d->isValidRowId(row) || !d->isValidColumnId(column)) + return {}; + + const QString &propertyName = d->properties.at(column).name; + const QJsonObject &element = d->elements.at(row); + + if (element.contains(propertyName)) + return collectionDataTypeFromJsonValue(element.value(propertyName)); + + return {}; +} + bool CollectionDetails::containsPropertyName(const QString &propertyName) { if (!isValid()) @@ -360,6 +390,13 @@ void CollectionDetails::swap(CollectionDetails &other) d.swap(other.d); } +void CollectionDetails::registerDeclarativeType() +{ + typedef CollectionDetails::DataType DataType; + qRegisterMetaType("DataType"); + qmlRegisterUncreatableType("CollectionDetails", 1, 0, "DataType", "Enum type"); +} + CollectionDetails &CollectionDetails::operator=(const CollectionDetails &other) { CollectionDetails value(other); diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.h b/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.h index 20896c67923..33e5552884f 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.h +++ b/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.h @@ -37,8 +37,11 @@ struct CollectionProperty; class CollectionDetails { + Q_GADGET + public: enum class DataType { Unknown, String, Url, Number, Boolean, Image, Color }; + Q_ENUM(DataType) explicit CollectionDetails(); CollectionDetails(const CollectionReference &reference); @@ -57,6 +60,7 @@ public: void insertElementAt(std::optional object, int row = -1); void insertEmptyElements(int row = 0, int count = 1); bool removeElements(int row, int count = 1); + bool setPropertyValue(int row, int column, const QVariant &value); bool setPropertyName(int column, const QString &value); bool forcePropertyType(int column, DataType type, bool force = false); @@ -66,6 +70,7 @@ public: QVariant data(int row, int column) const; QString propertyAt(int column) const; DataType typeAt(int column) const; + DataType typeAt(int row, int column) const; bool containsPropertyName(const QString &propertyName); bool isValid() const; @@ -80,6 +85,8 @@ public: QJsonArray getJsonCollection() const; QString getCsvCollection() const; + static void registerDeclarativeType(); + CollectionDetails &operator=(const CollectionDetails &other); private: diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.cpp index 896c584be8b..41fdd8102bb 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.cpp @@ -100,6 +100,7 @@ QHash CollectionDetailsModel::roleNames() const roles.insert(QAbstractTableModel::roleNames()); roles.insert(SelectedRole, "itemSelected"); roles.insert(DataTypeRole, "dataType"); + roles.insert(ColumnDataTypeRole, "columnType"); } return roles; } @@ -122,11 +123,31 @@ QVariant CollectionDetailsModel::data(const QModelIndex &index, int role) const if (role == SelectedRole) return (index.column() == m_selectedColumn || index.row() == m_selectedRow); - return m_currentCollection.data(index.row(), index.column()); + if (role == DataTypeRole) + return QVariant::fromValue(m_currentCollection.typeAt(index.row(), index.column())); + + if (role == ColumnDataTypeRole) + return QVariant::fromValue(m_currentCollection.typeAt(index.column())); + + if (role == Qt::EditRole) + return m_currentCollection.data(index.row(), index.column()); + + return m_currentCollection.data(index.row(), index.column()).toString(); } -bool CollectionDetailsModel::setData(const QModelIndex &, const QVariant &, int) +bool CollectionDetailsModel::setData(const QModelIndex &index, const QVariant &value, int role) { + if (!index.isValid()) + return {}; + + if (role == Qt::EditRole) { + bool changed = m_currentCollection.setPropertyValue(index.row(), index.column(), value); + if (changed) { + emit dataChanged(index, index, {Qt::DisplayRole, Qt::EditRole}); + return true; + } + } + return false; } @@ -189,7 +210,7 @@ Qt::ItemFlags CollectionDetailsModel::flags(const QModelIndex &index) const if (!index.isValid()) return {}; - return {Qt::ItemIsSelectable | Qt::ItemIsEnabled}; + return {Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsEditable}; } QVariant CollectionDetailsModel::headerData(int section, Qt::Orientation orientation, int role) const diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.h b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.h index ee3805e34bb..220865717a4 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.h +++ b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.h @@ -22,7 +22,7 @@ class CollectionDetailsModel : public QAbstractTableModel Q_PROPERTY(bool isEmpty MEMBER m_isEmpty NOTIFY isEmptyChanged) public: - enum DataRoles { SelectedRole = Qt::UserRole + 1, DataTypeRole }; + enum DataRoles { SelectedRole = Qt::UserRole + 1, DataTypeRole, ColumnDataTypeRole }; explicit CollectionDetailsModel(QObject *parent = nullptr); diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp index a3ffd72250a..6b0d063081f 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp @@ -153,6 +153,11 @@ void CollectionView::addResource(const QUrl &url, const QString &name, const QSt }); } +void CollectionView::registerDeclarativeType() +{ + CollectionDetails::registerDeclarativeType(); +} + void CollectionView::refreshModel() { if (!model()) diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionview.h b/src/plugins/qmldesigner/components/collectioneditor/collectionview.h index 6d81d00f064..994105f9745 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionview.h +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionview.h @@ -43,6 +43,8 @@ public: void addResource(const QUrl &url, const QString &name, const QString &type); + static void registerDeclarativeType(); + private: void refreshModel(); NodeMetaInfo jsonCollectionMetaInfo() const; diff --git a/src/plugins/qmldesigner/qmldesignerplugin.cpp b/src/plugins/qmldesigner/qmldesignerplugin.cpp index 40e3b681e48..8c94a53ce49 100644 --- a/src/plugins/qmldesigner/qmldesignerplugin.cpp +++ b/src/plugins/qmldesigner/qmldesignerplugin.cpp @@ -4,6 +4,7 @@ #include "qmldesignerplugin.h" #include "qmldesignertr.h" +#include "collectioneditor/collectionview.h" #include "coreplugin/iwizardfactory.h" #include "designmodecontext.h" #include "designmodewidget.h" @@ -282,6 +283,7 @@ bool QmlDesignerPlugin::initialize(const QStringList & /*arguments*/, QString *e //TODO Move registering those types out of the property editor, since they are used also in the states editor Quick2PropertyEditorView::registerQmlTypes(); + CollectionView::registerDeclarativeType(); if (checkEnterpriseLicense()) Core::IWizardFactory::registerFeatureProvider(new EnterpriseFeatureProvider); From 83c33233ff178d280dd5901eb3470b3d634fef2b Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Wed, 18 Oct 2023 16:25:14 +0300 Subject: [PATCH 074/242] QmlDesigner: Select relevant collection and source Task-number: QDS-10621 Change-Id: I836ca523469bce500d93b4ff0680c1ae0ef6c12c Reviewed-by: Miikka Heikkinen Reviewed-by: Mahmoud Badri Reviewed-by: Qt CI Patch Build Bot --- .../collectionsourcemodel.cpp | 20 ++++++++++++++++--- .../collectioneditor/collectionsourcemodel.h | 2 +- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.cpp index 999ebe449ba..19dce052852 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.cpp @@ -338,13 +338,15 @@ void CollectionSourceModel::onSelectedCollectionChanged(int collectionIndex) { CollectionListModel *collectionList = qobject_cast(sender()); if (collectionIndex > -1 && collectionList) { - if (_previousSelectedList && _previousSelectedList != collectionList) - _previousSelectedList->selectCollectionIndex(-1); + if (m_previousSelectedList && m_previousSelectedList != collectionList) + m_previousSelectedList->selectCollectionIndex(-1); + + m_previousSelectedList = collectionList; emit collectionSelected(collectionList->sourceNode(), collectionList->collectionNameAt(collectionIndex)); - _previousSelectedList = collectionList; + selectSourceIndex(sourceIndex(collectionList->sourceNode())); } } @@ -365,6 +367,18 @@ void CollectionSourceModel::setSelectedIndex(int idx) emit dataChanged(newIndex, newIndex, {SelectedRole}); emit selectedIndexChanged(idx); + + if (idx > -1) { + QPointer relatedCollectionList = m_collectionList.at(idx).data(); + if (relatedCollectionList) { + if (relatedCollectionList->selectedIndex() < 0) + relatedCollectionList->selectCollectionIndex(0, true); + } else if (m_previousSelectedList) { + m_previousSelectedList->selectCollectionIndex(-1); + m_previousSelectedList = {}; + emit this->collectionSelected(sourceNodeAt(idx), {}); + } + } } } diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.h b/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.h index bd22d833ad9..20a7933e633 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.h +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.h @@ -71,7 +71,7 @@ private: ModelNodes m_collectionSources; QHash m_sourceIndexHash; // internalId -> index QList> m_collectionList; - QPointer _previousSelectedList; + QPointer m_previousSelectedList; int m_selectedIndex = -1; bool m_isEmpty = true; }; From 4f893d0a7d72d730795061ef0e2cf867b8fdb18e Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Thu, 19 Oct 2023 16:41:51 +0300 Subject: [PATCH 075/242] QmlDesigner: Fix NodeHints::isValid() checks Evaluating some properties of NodeHints is needed even if there is no valid model node yet, i.e. when the hints have been created from ItemLibraryEntry. Fixes: QDS-10997 Change-Id: I321b89167530993d793b2c7beece7037093555f0 Reviewed-by: Marco Bubke --- .../qmldesigner/designercore/metainfo/nodehints.cpp | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/plugins/qmldesigner/designercore/metainfo/nodehints.cpp b/src/plugins/qmldesigner/designercore/metainfo/nodehints.cpp index 149bb768777..32e68a3cdc6 100644 --- a/src/plugins/qmldesigner/designercore/metainfo/nodehints.cpp +++ b/src/plugins/qmldesigner/designercore/metainfo/nodehints.cpp @@ -177,9 +177,6 @@ bool NodeHints::doesLayoutChildren() const bool NodeHints::canBeDroppedInFormEditor() const { - if (!isValid()) - return true; - auto flagIs = m_modelNode.metaInfo().canBeDroppedInFormEditor(); if (flagIs != FlagIs::Set) @@ -190,9 +187,6 @@ bool NodeHints::canBeDroppedInFormEditor() const bool NodeHints::canBeDroppedInNavigator() const { - if (!isValid()) - return true; - auto flagIs = m_modelNode.metaInfo().canBeDroppedInNavigator(); if (flagIs != FlagIs::Set) @@ -203,9 +197,6 @@ bool NodeHints::canBeDroppedInNavigator() const bool NodeHints::canBeDroppedInView3D() const { - if (!isValid()) - return false; - auto flagIs = m_modelNode.metaInfo().canBeDroppedInView3D(); if (flagIs != FlagIs::Set) @@ -331,9 +322,6 @@ bool NodeHints::visibleInNavigator() const bool NodeHints::visibleInLibrary() const { - if (!isValid()) - return true; - auto flagIs = m_modelNode.metaInfo().visibleInLibrary(); if (flagIs != FlagIs::Set) From 4fbff05a604e77206e8780037c657d2cb82e1790 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Thu, 19 Oct 2023 15:27:03 +0200 Subject: [PATCH 076/242] QmlDesigner: Add QtQuick3D.Effects to 3D wizard Change-Id: Id11d4232986c6b390e98740653cf465447d32c68 Reviewed-by: Miikka Heikkinen Reviewed-by: --- .../studio_templates/projects/application-3d/Screen01.ui.qml.tpl | 1 + 1 file changed, 1 insertion(+) diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/application-3d/Screen01.ui.qml.tpl b/share/qtcreator/qmldesigner/studio_templates/projects/application-3d/Screen01.ui.qml.tpl index 843185a029f..1c57554f38b 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/application-3d/Screen01.ui.qml.tpl +++ b/share/qtcreator/qmldesigner/studio_templates/projects/application-3d/Screen01.ui.qml.tpl @@ -8,6 +8,7 @@ Check out https://doc.qt.io/qtcreator/creator-quick-ui-forms.html for details on import QtQuick %{QtQuickVersion} import QtQuick.Controls %{QtQuickVersion} import QtQuick3D %{QtQuick3DVersion} +import QtQuick3D.Effects %{QtQuick3DVersion} import %{ImportModuleName} %{ImportModuleVersion} Rectangle { From ab2c25d52ac0bc8312c5adadee4fa64688551ec9 Mon Sep 17 00:00:00 2001 From: Burak Hancerli Date: Mon, 2 Oct 2023 13:20:02 +0200 Subject: [PATCH 077/242] QmlProject: Absolute files paths are ignored This patch fixes issues with adding files to the project by providing the absolute paths in the .qmlproject file. - If a directory is given without an explicit file list, then that directory wil be searched recursively. - If explicit file list given without a directory, then each file will be added to the project separately. - If both provided, then each file wil be prefixed with given directory, and added project separately. Task-number: QDS-10636 Change-Id: Ia13964ce2b3f6364d1def1aa71e20ec29f6f3542 Reviewed-by: Reviewed-by: Thomas Hartmann Reviewed-by: Qt CI Patch Build Bot --- .../buildsystem/projectitem/converters.cpp | 256 ++++++------------ .../projectitem/qmlprojectitem.cpp | 49 +++- .../converter/test-set-1/testfile.jsontoqml | 98 ++++--- .../converter/test-set-1/testfile.qmlproject | 5 +- .../converter/test-set-1/testfile.qmltojson | 153 ++++++----- .../converter/test-set-2/testfile.jsontoqml | 25 +- .../converter/test-set-2/testfile.qmlproject | 5 +- .../converter/test-set-2/testfile.qmltojson | 59 ++-- .../converter/test-set-3/testfile.jsontoqml | 98 ++++--- .../converter/test-set-3/testfile.qmlproject | 5 +- .../converter/test-set-3/testfile.qmltojson | 153 ++++++----- .../file-filters/MaterialBundle.qmlproject | 2 +- .../qmlprojectmanager/projectitem-test.cpp | 11 +- 13 files changed, 479 insertions(+), 440 deletions(-) diff --git a/src/plugins/qmlprojectmanager/buildsystem/projectitem/converters.cpp b/src/plugins/qmlprojectmanager/buildsystem/projectitem/converters.cpp index d380bedc757..a4be2d9ac28 100644 --- a/src/plugins/qmlprojectmanager/buildsystem/projectitem/converters.cpp +++ b/src/plugins/qmlprojectmanager/buildsystem/projectitem/converters.cpp @@ -7,25 +7,6 @@ namespace QmlProjectManager::Converters { -using PropsPair = QPair; -struct FileProps -{ - const PropsPair image{"image", - QStringList{"*.jpeg", "*.jpg", "*.png", "*.svg", "*.hdr", ".ktx"}}; - const PropsPair qml{"qml", QStringList{"*.qml"}}; - const PropsPair qmlDir{"qmldir", QStringList{"qmldir"}}; - const PropsPair javaScr{"javaScript", QStringList{"*.js", "*.ts"}}; - const PropsPair video{"video", QStringList{"*.mp4"}}; - const PropsPair sound{"sound", QStringList{"*.mp3", "*.wav"}}; - const PropsPair font{"font", QStringList{"*.ttf", "*.otf"}}; - const PropsPair config{"config", QStringList{"*.conf"}}; - const PropsPair styling{"styling", QStringList{"*.css"}}; - const PropsPair mesh{"meshes", QStringList{"*.mesh"}}; - const PropsPair - shader{"shader", - QStringList{"*.glsl", "*.glslv", "*.glslf", "*.vsh", "*.fsh", "*.vert", "*.frag"}}; -}; - QString jsonToQmlProject(const QJsonObject &rootObject) { QString qmlProjectString; @@ -37,7 +18,7 @@ QString jsonToQmlProject(const QJsonObject &rootObject) QJsonObject versionConfig = rootObject["versions"].toObject(); QJsonObject environmentConfig = rootObject["environment"].toObject(); QJsonObject deploymentConfig = rootObject["deployment"].toObject(); - QJsonObject filesConfig = rootObject["fileGroups"].toObject(); + QJsonArray filesConfig = rootObject["fileGroups"].toArray(); int indentationLevel = 0; @@ -82,42 +63,22 @@ QString jsonToQmlProject(const QJsonObject &rootObject) ts << QString(" ").repeated(indentationLevel * 4) << "}" << Qt::endl; }; - auto appendDirectories = - [&startObject, &endObject, &appendString, &filesConfig](const QString &jsonKey, + auto appendFileGroup = + [&startObject, &endObject, &appendString, &appendArray](const QJsonObject &fileGroup, const QString &qmlKey) { - QJsonValue dirsObj = filesConfig[jsonKey].toObject()["directories"]; - QStringList dirs = dirsObj.toVariant().toStringList(); - foreach (const QString &directory, dirs) { - startObject(qmlKey); - appendString("directory", directory); - endObject(); - } + startObject(qmlKey); + appendString("directory", fileGroup["directory"].toString()); + appendString("filter", fileGroup["filters"].toVariant().toStringList().join(";")); + appendArray("files", fileGroup["files"].toVariant().toStringList()); + endObject(); }; - auto appendFiles = [&startObject, - &endObject, - &appendString, - &appendArray, - &filesConfig](const QString &jsonKey, const QString &qmlKey) { - QJsonValue dirsObj = filesConfig[jsonKey].toObject()["directories"]; - QJsonValue filesObj = filesConfig[jsonKey].toObject()["files"]; - QJsonValue filtersObj = filesConfig[jsonKey].toObject()["filters"]; - - foreach (const QString &directory, dirsObj.toVariant().toStringList()) { - startObject(qmlKey); - appendString("directory", directory); - appendString("filters", filtersObj.toVariant().toStringList().join(";")); - - if (!filesObj.toArray().isEmpty()) { - QStringList fileList; - foreach (const QJsonValue &file, filesObj.toArray()) { - fileList.append(file.toObject()["name"].toString()); - } - appendArray("files", fileList); - } + auto appendQmlFileGroup = + [&startObject, &endObject, &appendString](const QJsonObject &fileGroup) { + startObject("QmlFiles"); + appendString("directory", fileGroup["directory"].toString()); endObject(); - } - }; + }; // start creating the file content appendComment("prop: json-converted"); @@ -127,54 +88,46 @@ QString jsonToQmlProject(const QJsonObject &rootObject) { startObject("Project"); - { // append non-object props - appendString("mainFile", runConfig["mainFile"].toString()); - appendString("mainUiFile", runConfig["mainUiFile"].toString()); - appendString("targetDirectory", deploymentConfig["targetDirectory"].toString()); - appendBool("widgetApp", runConfig["widgetApp"].toBool()); - appendArray("importPaths", rootObject["importPaths"].toVariant().toStringList()); - appendBreak(); - appendString("qdsVersion", versionConfig["designStudio"].toString()); - appendString("quickVersion", versionConfig["qtQuick"].toString()); - appendBool("qt6Project", versionConfig["qt"].toString() == "6"); - appendBool("qtForMCUs", !(rootObject["mcuConfig"].toObject().isEmpty())); - appendBreak(); - appendBool("multilanguageSupport", languageConfig["multiLanguageSupport"].toBool()); - appendString("primaryLanguage", languageConfig["primaryLanguage"].toString()); - appendArray("supportedLanguages", - languageConfig["supportedLanguages"].toVariant().toStringList()); - } + // append non-object props + appendString("mainFile", runConfig["mainFile"].toString()); + appendString("mainUiFile", runConfig["mainUiFile"].toString()); + appendString("targetDirectory", deploymentConfig["targetDirectory"].toString()); + appendBool("widgetApp", runConfig["widgetApp"].toBool()); + appendArray("importPaths", rootObject["importPaths"].toVariant().toStringList()); + appendBreak(); + appendString("qdsVersion", versionConfig["designStudio"].toString()); + appendString("quickVersion", versionConfig["qtQuick"].toString()); + appendBool("qt6Project", versionConfig["qt"].toString() == "6"); + appendBool("qtForMCUs", !(rootObject["mcuConfig"].toObject().isEmpty())); + appendBreak(); + appendBool("multilanguageSupport", languageConfig["multiLanguageSupport"].toBool()); + appendString("primaryLanguage", languageConfig["primaryLanguage"].toString()); + appendArray("supportedLanguages", + languageConfig["supportedLanguages"].toVariant().toStringList()); - { // append Environment object - startObject("Environment"); - foreach (const QString &key, environmentConfig.keys()) { - appendItem(key, environmentConfig[key].toString(), true); - } + // append Environment object + startObject("Environment"); + foreach (const QString &key, environmentConfig.keys()) { + appendItem(key, environmentConfig[key].toString(), true); + } + endObject(); + + // append ShaderTool object + if (!shaderConfig["args"].toVariant().toStringList().isEmpty()) { + startObject("ShaderTool"); + appendString("args", + shaderConfig["args"].toVariant().toStringList().join(" ").replace("\"", + "\\\"")); + appendArray("files", shaderConfig["files"].toVariant().toStringList()); endObject(); } - { // append ShaderTool object - if (!shaderConfig["args"].toVariant().toStringList().isEmpty()) { - startObject("ShaderTool"); - appendString("args", - shaderConfig["args"].toVariant().toStringList().join(" ").replace( - "\"", "\\\"")); - appendArray("files", shaderConfig["files"].toVariant().toStringList()); - endObject(); - } - } - - { // append files objects - appendDirectories("qml", "QmlFiles"); - appendDirectories("javaScript", "JavaScriptFiles"); - appendDirectories("image", "ImageFiles"); - appendFiles("config", "Files"); - appendFiles("font", "Files"); - appendFiles("meshes", "Files"); - appendFiles("qmldir", "Files"); - appendFiles("shader", "Files"); - appendFiles("sound", "Files"); - appendFiles("video", "Files"); + // append files objects + for (const QJsonValue &fileGroup : filesConfig) { + if (fileGroup["filters"].toArray().contains("*.qml")) + appendQmlFileGroup(fileGroup.toObject()); + else + appendFileGroup(fileGroup.toObject(), "Files"); } endObject(); // Closing 'Project' @@ -210,7 +163,7 @@ QJsonObject qmlProjectTojson(const Utils::FilePath &projectFile) auto toCamelCase = [](const QString &s) { return QString(s).replace(0, 1, s[0].toLower()); }; QJsonObject rootObject; // root object - QJsonObject fileGroupsObject; + QJsonArray fileGroupsObject; QJsonObject languageObject; QJsonObject versionObject; QJsonObject runConfigObject; @@ -270,82 +223,51 @@ QJsonObject qmlProjectTojson(const Utils::FilePath &projectFile) // convert the the object props for (const QmlJS::SimpleReaderNode::Ptr &childNode : rootNode->children()) { if (childNode->name().contains("files", Qt::CaseInsensitive)) { - PropsPair propsPair; - FileProps fileProps; const QString childNodeName = childNode->name().toLower().remove("qds."); - const QmlJS::SimpleReaderNode::Property childNodeFilter = childNode->property("filter"); - const QmlJS::SimpleReaderNode::Property childNodeDirectory = childNode->property( - "directory"); - const QmlJS::SimpleReaderNode::Property childNodeFiles = childNode->property("files"); - const QString childNodeFilterValue = childNodeFilter.value.toString(); + QJsonArray childNodeFiles = childNode->property("files").value.toJsonArray(); + QString childNodeDirectory = childNode->property("directory").value.toString(); + QStringList filters = childNode->property("filter").value.toString().split(";"); + filters.removeAll(""); + QJsonArray childNodeFilters = QJsonArray::fromStringList(filters); - if (childNodeName == "qmlfiles" || childNodeFilterValue.contains("*.qml")) { - propsPair = fileProps.qml; - } else if (childNodeName == "javascriptfiles") { - propsPair = fileProps.javaScr; - } else if (childNodeName == "imagefiles") { - propsPair = fileProps.image; - } else { - if (childNodeFilter.isValid()) { - if (childNodeFilterValue.contains(".conf")) - propsPair = fileProps.config; - else if (childNodeFilterValue.contains(".ttf")) - propsPair = fileProps.font; - else if (childNodeFilterValue.contains("qmldir")) - propsPair = fileProps.qmlDir; - else if (childNodeFilterValue.contains(".wav")) - propsPair = fileProps.sound; - else if (childNodeFilterValue.contains(".mp4")) - propsPair = fileProps.video; - else if (childNodeFilterValue.contains(".mesh")) - propsPair = fileProps.mesh; - else if (childNodeFilterValue.contains(".glsl")) - propsPair = fileProps.shader; - else if (childNodeFilterValue.contains(".css")) - propsPair = fileProps.styling; + // files have priority over filters + // if explicit files are given, then filters will be ignored + // and all files are prefixed such as "directory/". + // if directory is empty, then the files are prefixed with the project directory + if (childNodeFiles.empty()) { + auto inserter = [&childNodeFilters](const QStringList &filterSource) { + std::for_each(filterSource.begin(), + filterSource.end(), + [&childNodeFilters](const auto &value) { + childNodeFilters << value; + }); + }; + + // Those 3 file groups are the special ones + // that have a default set of filters. After the first + // conversion (QmlProject -> JSON) they are converted to + // the generic file group format ('Files' or 'QDS.Files') + if (childNodeName == "qmlfiles") { + inserter({QStringLiteral("*.qml")}); + } else if (childNodeName == "javascriptfiles") { + inserter({QStringLiteral("*.js"), QStringLiteral("*.ts")}); + } else if (childNodeName == "imagefiles") { + inserter({QStringLiteral("*.jpeg"), + QStringLiteral("*.jpg"), + QStringLiteral("*.png"), + QStringLiteral("*.svg"), + QStringLiteral("*.hdr"), + QStringLiteral(".ktx")}); } } - // get all objects we'll work on - QJsonObject targetObject = fileGroupsObject[propsPair.first].toObject(); - QJsonArray directories = targetObject["directories"].toArray(); - QJsonArray filters = targetObject["filters"].toArray(); - QJsonArray files = targetObject["files"].toArray(); + // create the file group object + QJsonObject targetObject; + targetObject.insert("directory", childNodeDirectory); + targetObject.insert("filters", childNodeFilters); + targetObject.insert("files", childNodeFiles); - // populate & update filters - if (filters.isEmpty()) { - filters = QJsonArray::fromStringList( - propsPair.second); // populate the filters with the predefined ones - } - - if (childNodeFilter.isValid()) { // append filters from qmlproject (merge) - const QStringList filtersFromProjectFile = childNodeFilterValue.split(";"); - for (const QString &filter : filtersFromProjectFile) { - if (!filters.contains(QJsonValue(filter))) { - filters.append(QJsonValue(filter)); - } - } - } - - // populate & update directories - if (childNodeDirectory.isValid()) { - directories.append(childNodeDirectory.value.toJsonValue()); - } - if (directories.isEmpty()) - directories.append("."); - - // populate & update files - if (childNodeFiles.isValid()) { - foreach (const QJsonValue &file, childNodeFiles.value.toJsonArray()) { - files.append(QJsonObject{{"name", file.toString()}}); - } - } - - // put everything back into the root object - targetObject.insert("directories", directories); - targetObject.insert("filters", filters); - targetObject.insert("files", files); - fileGroupsObject.insert(propsPair.first, targetObject); + fileGroupsObject.append(targetObject); } else if (childNode->name().contains("shadertool", Qt::CaseInsensitive)) { QStringList quotedArgs = childNode->property("args").value.toString().split('\"', Qt::SkipEmptyParts); diff --git a/src/plugins/qmlprojectmanager/buildsystem/projectitem/qmlprojectitem.cpp b/src/plugins/qmlprojectmanager/buildsystem/projectitem/qmlprojectitem.cpp index 5b56c8f37ca..6a90b79badb 100644 --- a/src/plugins/qmlprojectmanager/buildsystem/projectitem/qmlprojectitem.cpp +++ b/src/plugins/qmlprojectmanager/buildsystem/projectitem/qmlprojectitem.cpp @@ -65,20 +65,16 @@ bool QmlProjectItem::initProjectObject() void QmlProjectItem::setupFileFilters() { auto setupFileFilterItem = [this](const QJsonObject &fileGroup) { - for (const QString &directory : fileGroup["directories"].toVariant().toStringList()) { + // first we need to add all directories as a 'resource' path for the project and set them as + // recursive. + // if there're any files with the explicit absolute paths, we need to add them afterwards. + for (const QString &directory : fileGroup["directory"].toVariant().toStringList()) { std::unique_ptr fileFilterItem{new FileFilterItem}; - - QStringList filesArr; - for (const QJsonValue &file : fileGroup["files"].toArray()) { - filesArr.append(file["name"].toString()); - } - fileFilterItem->setDirectory(directory); fileFilterItem->setFilters(fileGroup["filters"].toVariant().toStringList()); - fileFilterItem->setRecursive(fileGroup["recursive"].toBool(true)); - fileFilterItem->setPathsProperty(fileGroup["directories"].toVariant().toStringList()); - fileFilterItem->setPathsProperty(filesArr); + fileFilterItem->setRecursive(true); fileFilterItem->setDefaultDirectory(m_projectFile.parentDir().toString()); + #ifndef TESTS_ENABLED_QMLPROJECTITEM connect(fileFilterItem.get(), &FileFilterItem::filesChanged, @@ -87,11 +83,38 @@ void QmlProjectItem::setupFileFilters() #endif m_content.push_back(std::move(fileFilterItem)); }; + + // here we begin to add files with the explicit absolute paths + QJsonArray files = fileGroup["files"].toArray(); + if (files.isEmpty()) + return; + + QStringList filesArr; + std::transform(files.begin(), + files.end(), + std::back_inserter(filesArr), + [](const QJsonValue &value) { return value.toString(); }); + + const QString directory = fileGroup["directory"].toString() == "" + ? m_projectFile.parentDir().toString() + : fileGroup["directory"].toString(); + Utils::FilePath groupDir = Utils::FilePath::fromString(directory); + std::unique_ptr fileFilterItem{new FileFilterItem}; + fileFilterItem->setRecursive(false); + fileFilterItem->setPathsProperty(filesArr); + fileFilterItem->setDefaultDirectory(m_projectFile.parentDir().toString()); + fileFilterItem->setDirectory(groupDir.toString()); +#ifndef TESTS_ENABLED_QMLPROJECTITEM + connect(fileFilterItem.get(), + &FileFilterItem::filesChanged, + this, + &QmlProjectItem::qmlFilesChanged); +#endif + m_content.push_back(std::move(fileFilterItem)); }; - QJsonObject fileGroups = m_project["fileGroups"].toObject(); - for (const QString &groupName : fileGroups.keys()) { - setupFileFilterItem(fileGroups[groupName].toObject()); + for (const QJsonValue &fileGroup : m_project["fileGroups"].toArray()) { + setupFileFilterItem(fileGroup.toObject()); } } diff --git a/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-1/testfile.jsontoqml b/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-1/testfile.jsontoqml index dbd6e4a91ac..4db64285c7e 100644 --- a/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-1/testfile.jsontoqml +++ b/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-1/testfile.jsontoqml @@ -40,59 +40,79 @@ Project { directory: "imports" } - QmlFiles { - directory: "asset_imports" - } - - JavaScriptFiles { + Files { directory: "content" - } - - JavaScriptFiles { - directory: "imports" - } - - ImageFiles { - directory: "content" - } - - ImageFiles { - directory: "asset_imports" + filter: "*.js;*.ts" + files: [ ] } Files { - directory: "." - filters: "*.conf" + directory: "imports" + filter: "*.js;*.ts" + files: [ ] + } + + Files { + directory: "content" + filter: "*.jpeg;*.jpg;*.png;*.svg;*.hdr;.ktx" + files: [ ] + } + + Files { + directory: "asset_imports" + filter: "*.jpeg;*.jpg;*.png;*.svg;*.hdr;.ktx" + files: [ ] + } + + Files { + directory: "" + filter: "" files: [ "qtquickcontrols2.conf" ] } + Files { + directory: "" + filter: "*.conf" + files: [ ] + } + Files { directory: "." - filters: "*.ttf;*.otf;*.ctf" + filter: "qmldir" + files: [ ] + } + + Files { + directory: "" + filter: "*.ttf;*.otf;*.ctf" + files: [ ] + } + + Files { + directory: "" + filter: "*.wav;*.mp3" + files: [ ] + } + + Files { + directory: "" + filter: "*.mp4" + files: [ ] + } + + Files { + directory: "" + filter: "*.glsl;*.glslv;*.glslf;*.vsh;*.fsh;*.vert;*.frag;*.trag" + files: [ ] } Files { directory: "asset_imports" - filters: "*.mesh" + filter: "*.mesh" + files: [ ] } - Files { - directory: "." - filters: "qmldir" - } - - Files { - directory: "." - filters: "*.glsl;*.glslv;*.glslf;*.vsh;*.fsh;*.vert;*.frag;*.trag" - } - - Files { - directory: "." - filters: "*.mp3;*.wav" - } - - Files { - directory: "." - filters: "*.mp4" + QmlFiles { + directory: "asset_imports" } } diff --git a/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-1/testfile.qmlproject b/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-1/testfile.qmlproject index d3e15d20be2..260938164a7 100644 --- a/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-1/testfile.qmlproject +++ b/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-1/testfile.qmlproject @@ -30,10 +30,13 @@ Project { } Files { - filter: "*.conf" files: ["qtquickcontrols2.conf"] } + Files { + filter: "*.conf" + } + Files { filter: "qmldir" directory: "." diff --git a/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-1/testfile.qmltojson b/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-1/testfile.qmltojson index 293b8e96524..4b039c2e1ed 100644 --- a/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-1/testfile.qmltojson +++ b/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-1/testfile.qmltojson @@ -9,37 +9,43 @@ "QT_LOGGING_RULES": "qt.qml.connections=false", "QT_QUICK_CONTROLS_CONF": "qtquickcontrols2.conf" }, - "fileGroups": { - "config": { - "directories": [ - "." - ], - "files": [ - { - "name": "qtquickcontrols2.conf" - } - ], - "filters": [ - "*.conf" - ] - }, - "font": { - "directories": [ - "." - ], + "fileGroups": [ + { + "directory": "content", "files": [ ], "filters": [ - "*.ttf", - "*.otf", - "*.ctf" + "*.qml" ] }, - "image": { - "directories": [ - "content", - "asset_imports" + { + "directory": "imports", + "files": [ ], + "filters": [ + "*.qml" + ] + }, + { + "directory": "content", + "files": [ + ], + "filters": [ + "*.js", + "*.ts" + ] + }, + { + "directory": "imports", + "files": [ + ], + "filters": [ + "*.js", + "*.ts" + ] + }, + { + "directory": "content", "files": [ ], "filters": [ @@ -51,54 +57,72 @@ ".ktx" ] }, - "javaScript": { - "directories": [ - "content", - "imports" - ], + { + "directory": "asset_imports", "files": [ ], "filters": [ - "*.js", - "*.ts" + "*.jpeg", + "*.jpg", + "*.png", + "*.svg", + "*.hdr", + ".ktx" ] }, - "meshes": { - "directories": [ - "asset_imports" + { + "directory": "", + "files": [ + "qtquickcontrols2.conf" ], + "filters": [ + ] + }, + { + "directory": "", "files": [ ], "filters": [ - "*.mesh" + "*.conf" ] }, - "qml": { - "directories": [ - "content", - "imports", - "asset_imports" - ], - "files": [ - ], - "filters": [ - "*.qml" - ] - }, - "qmldir": { - "directories": [ - "." - ], + { + "directory": ".", "files": [ ], "filters": [ "qmldir" ] }, - "shader": { - "directories": [ - "." + { + "directory": "", + "files": [ ], + "filters": [ + "*.ttf", + "*.otf", + "*.ctf" + ] + }, + { + "directory": "", + "files": [ + ], + "filters": [ + "*.wav", + "*.mp3" + ] + }, + { + "directory": "", + "files": [ + ], + "filters": [ + "*.mp4" + ] + }, + { + "directory": "", "files": [ ], "filters": [ @@ -112,28 +136,23 @@ "*.trag" ] }, - "sound": { - "directories": [ - "." - ], + { + "directory": "asset_imports", "files": [ ], "filters": [ - "*.mp3", - "*.wav" + "*.mesh" ] }, - "video": { - "directories": [ - "." - ], + { + "directory": "asset_imports", "files": [ ], "filters": [ - "*.mp4" + "*.qml" ] } - }, + ], "fileVersion": 1, "importPaths": [ "imports", diff --git a/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-2/testfile.jsontoqml b/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-2/testfile.jsontoqml index e1ec5b97566..484a425b91c 100644 --- a/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-2/testfile.jsontoqml +++ b/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-2/testfile.jsontoqml @@ -28,22 +28,33 @@ Project { directory: "." } - JavaScriptFiles { - directory: "." - } - - ImageFiles { + Files { directory: "." + filter: "*.js;*.ts" + files: [ ] } Files { directory: "." - filters: "*.conf" + filter: "*.jpeg;*.jpg;*.png;*.svg;*.hdr;.ktx" + files: [ ] + } + + Files { + directory: "" + filter: "" files: [ "qtquickcontrols2.conf" ] } + Files { + directory: "" + filter: "*.conf" + files: [ ] + } + Files { directory: "." - filters: "qmldir" + filter: "qmldir" + files: [ ] } } diff --git a/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-2/testfile.qmlproject b/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-2/testfile.qmlproject index 409b46bb7ff..3ceeda651af 100644 --- a/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-2/testfile.qmlproject +++ b/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-2/testfile.qmlproject @@ -19,10 +19,13 @@ Project { } Files { - filter: "*.conf" files: ["qtquickcontrols2.conf"] } + Files { + filter: "*.conf" + } + Files { filter: "qmldir" directory: "." diff --git a/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-2/testfile.qmltojson b/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-2/testfile.qmltojson index 33a478e2ca4..abe1b455e4b 100644 --- a/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-2/testfile.qmltojson +++ b/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-2/testfile.qmltojson @@ -6,24 +6,26 @@ "QT_AUTO_SCREEN_SCALE_FACTOR": "1", "QT_QUICK_CONTROLS_CONF": "qtquickcontrols2.conf" }, - "fileGroups": { - "config": { - "directories": [ - "." - ], + "fileGroups": [ + { + "directory": ".", "files": [ - { - "name": "qtquickcontrols2.conf" - } ], "filters": [ - "*.conf" + "*.qml" ] }, - "image": { - "directories": [ - "." + { + "directory": ".", + "files": [ ], + "filters": [ + "*.js", + "*.ts" + ] + }, + { + "directory": ".", "files": [ ], "filters": [ @@ -35,38 +37,31 @@ ".ktx" ] }, - "javaScript": { - "directories": [ - "." + { + "directory": "", + "files": [ + "qtquickcontrols2.conf" ], + "filters": [ + ] + }, + { + "directory": "", "files": [ ], "filters": [ - "*.js", - "*.ts" + "*.conf" ] }, - "qml": { - "directories": [ - "." - ], - "files": [ - ], - "filters": [ - "*.qml" - ] - }, - "qmldir": { - "directories": [ - "." - ], + { + "directory": ".", "files": [ ], "filters": [ "qmldir" ] } - }, + ], "fileVersion": 1, "importPaths": [ "imports" diff --git a/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-3/testfile.jsontoqml b/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-3/testfile.jsontoqml index dbd6e4a91ac..4db64285c7e 100644 --- a/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-3/testfile.jsontoqml +++ b/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-3/testfile.jsontoqml @@ -40,59 +40,79 @@ Project { directory: "imports" } - QmlFiles { - directory: "asset_imports" - } - - JavaScriptFiles { + Files { directory: "content" - } - - JavaScriptFiles { - directory: "imports" - } - - ImageFiles { - directory: "content" - } - - ImageFiles { - directory: "asset_imports" + filter: "*.js;*.ts" + files: [ ] } Files { - directory: "." - filters: "*.conf" + directory: "imports" + filter: "*.js;*.ts" + files: [ ] + } + + Files { + directory: "content" + filter: "*.jpeg;*.jpg;*.png;*.svg;*.hdr;.ktx" + files: [ ] + } + + Files { + directory: "asset_imports" + filter: "*.jpeg;*.jpg;*.png;*.svg;*.hdr;.ktx" + files: [ ] + } + + Files { + directory: "" + filter: "" files: [ "qtquickcontrols2.conf" ] } + Files { + directory: "" + filter: "*.conf" + files: [ ] + } + Files { directory: "." - filters: "*.ttf;*.otf;*.ctf" + filter: "qmldir" + files: [ ] + } + + Files { + directory: "" + filter: "*.ttf;*.otf;*.ctf" + files: [ ] + } + + Files { + directory: "" + filter: "*.wav;*.mp3" + files: [ ] + } + + Files { + directory: "" + filter: "*.mp4" + files: [ ] + } + + Files { + directory: "" + filter: "*.glsl;*.glslv;*.glslf;*.vsh;*.fsh;*.vert;*.frag;*.trag" + files: [ ] } Files { directory: "asset_imports" - filters: "*.mesh" + filter: "*.mesh" + files: [ ] } - Files { - directory: "." - filters: "qmldir" - } - - Files { - directory: "." - filters: "*.glsl;*.glslv;*.glslf;*.vsh;*.fsh;*.vert;*.frag;*.trag" - } - - Files { - directory: "." - filters: "*.mp3;*.wav" - } - - Files { - directory: "." - filters: "*.mp4" + QmlFiles { + directory: "asset_imports" } } diff --git a/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-3/testfile.qmlproject b/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-3/testfile.qmlproject index 721dea3d28e..1ec43b95d5a 100644 --- a/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-3/testfile.qmlproject +++ b/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-3/testfile.qmlproject @@ -30,10 +30,13 @@ Project { } QDS.Files { - filter: "*.conf" files: ["qtquickcontrols2.conf"] } + QDS.Files { + filter: "*.conf" + } + QDS.Files { filter: "qmldir" directory: "." diff --git a/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-3/testfile.qmltojson b/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-3/testfile.qmltojson index 293b8e96524..4b039c2e1ed 100644 --- a/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-3/testfile.qmltojson +++ b/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-3/testfile.qmltojson @@ -9,37 +9,43 @@ "QT_LOGGING_RULES": "qt.qml.connections=false", "QT_QUICK_CONTROLS_CONF": "qtquickcontrols2.conf" }, - "fileGroups": { - "config": { - "directories": [ - "." - ], - "files": [ - { - "name": "qtquickcontrols2.conf" - } - ], - "filters": [ - "*.conf" - ] - }, - "font": { - "directories": [ - "." - ], + "fileGroups": [ + { + "directory": "content", "files": [ ], "filters": [ - "*.ttf", - "*.otf", - "*.ctf" + "*.qml" ] }, - "image": { - "directories": [ - "content", - "asset_imports" + { + "directory": "imports", + "files": [ ], + "filters": [ + "*.qml" + ] + }, + { + "directory": "content", + "files": [ + ], + "filters": [ + "*.js", + "*.ts" + ] + }, + { + "directory": "imports", + "files": [ + ], + "filters": [ + "*.js", + "*.ts" + ] + }, + { + "directory": "content", "files": [ ], "filters": [ @@ -51,54 +57,72 @@ ".ktx" ] }, - "javaScript": { - "directories": [ - "content", - "imports" - ], + { + "directory": "asset_imports", "files": [ ], "filters": [ - "*.js", - "*.ts" + "*.jpeg", + "*.jpg", + "*.png", + "*.svg", + "*.hdr", + ".ktx" ] }, - "meshes": { - "directories": [ - "asset_imports" + { + "directory": "", + "files": [ + "qtquickcontrols2.conf" ], + "filters": [ + ] + }, + { + "directory": "", "files": [ ], "filters": [ - "*.mesh" + "*.conf" ] }, - "qml": { - "directories": [ - "content", - "imports", - "asset_imports" - ], - "files": [ - ], - "filters": [ - "*.qml" - ] - }, - "qmldir": { - "directories": [ - "." - ], + { + "directory": ".", "files": [ ], "filters": [ "qmldir" ] }, - "shader": { - "directories": [ - "." + { + "directory": "", + "files": [ ], + "filters": [ + "*.ttf", + "*.otf", + "*.ctf" + ] + }, + { + "directory": "", + "files": [ + ], + "filters": [ + "*.wav", + "*.mp3" + ] + }, + { + "directory": "", + "files": [ + ], + "filters": [ + "*.mp4" + ] + }, + { + "directory": "", "files": [ ], "filters": [ @@ -112,28 +136,23 @@ "*.trag" ] }, - "sound": { - "directories": [ - "." - ], + { + "directory": "asset_imports", "files": [ ], "filters": [ - "*.mp3", - "*.wav" + "*.mesh" ] }, - "video": { - "directories": [ - "." - ], + { + "directory": "asset_imports", "files": [ ], "filters": [ - "*.mp4" + "*.qml" ] } - }, + ], "fileVersion": 1, "importPaths": [ "imports", diff --git a/tests/unit/tests/unittests/qmlprojectmanager/data/file-filters/MaterialBundle.qmlproject b/tests/unit/tests/unittests/qmlprojectmanager/data/file-filters/MaterialBundle.qmlproject index 479c20456be..837e7bbd911 100644 --- a/tests/unit/tests/unittests/qmlprojectmanager/data/file-filters/MaterialBundle.qmlproject +++ b/tests/unit/tests/unittests/qmlprojectmanager/data/file-filters/MaterialBundle.qmlproject @@ -55,7 +55,7 @@ Project { Files { filter: "*.mesh" directory: "asset_imports" - } + } Files { filter: "*.mesh" diff --git a/tests/unit/tests/unittests/qmlprojectmanager/projectitem-test.cpp b/tests/unit/tests/unittests/qmlprojectmanager/projectitem-test.cpp index 976841b541a..49caf960746 100644 --- a/tests/unit/tests/unittests/qmlprojectmanager/projectitem-test.cpp +++ b/tests/unit/tests/unittests/qmlprojectmanager/projectitem-test.cpp @@ -1,6 +1,8 @@ // Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// clazy:excludeall=non-pod-global-static + #include "../utils/google-using-declarations.h" #include "../utils/googletest.h" // IWYU pragma: keep @@ -44,15 +46,14 @@ protected: } protected: - static inline std::unique_ptr projectItemEmpty; - static inline std::unique_ptr projectItemWithQdsPrefix; - static inline std::unique_ptr - projectItemWithoutQdsPrefix; + inline static std::unique_ptr projectItemEmpty; + inline static std::unique_ptr projectItemWithQdsPrefix; + inline static std::unique_ptr projectItemWithoutQdsPrefix; std::unique_ptr projectItemSetters = std::make_unique< QmlProjectManager::QmlProjectItem>(Utils::FilePath::fromString( localTestDataDir + "/getter-setter/empty.qmlproject"), true); - static inline std::unique_ptr projectItemFileFilters; + inline static std::unique_ptr projectItemFileFilters; }; auto createAbsoluteFilePaths(const QStringList &fileList) From 3d53bd6b80e9b8068aef0234e78df9909900d003 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Fri, 20 Oct 2023 12:26:31 +0300 Subject: [PATCH 078/242] QmlDesigner: Fix quaternion compare in OriginGizmo in 3D view Change-Id: Icefb7fb75e8dfd229a9b13028fa8f00f774be68b Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Mahmoud Badri --- .../qml2puppet/mockfiles/qt6/OriginGizmo.qml | 30 +++++++++++-------- .../qml2puppet/editor3d/generalhelper.cpp | 10 +++++-- .../qml2puppet/editor3d/generalhelper.h | 5 ++-- 3 files changed, 29 insertions(+), 16 deletions(-) diff --git a/src/tools/qml2puppet/mockfiles/qt6/OriginGizmo.qml b/src/tools/qml2puppet/mockfiles/qt6/OriginGizmo.qml index 7b0ea16704d..bb055fa29a9 100644 --- a/src/tools/qml2puppet/mockfiles/qt6/OriginGizmo.qml +++ b/src/tools/qml2puppet/mockfiles/qt6/OriginGizmo.qml @@ -133,8 +133,9 @@ Item { initialPosition: Qt.vector3d(1, 0, 0) onTapped: { let axis = OriginGizmo.Axis.PositiveX - if (_generalHelper.compareVectors(root.targetNode.sceneRotation, - root.quaternionForAxis(OriginGizmo.Axis.PositiveX))) { + if (_generalHelper.compareQuaternions( + root.targetNode.sceneRotation, + root.quaternionForAxis(OriginGizmo.Axis.PositiveX))) { axis = OriginGizmo.Axis.NegativeX } root.axisClicked(axis) @@ -158,8 +159,9 @@ Item { initialPosition: Qt.vector3d(0, 1, 0) onTapped: { let axis = OriginGizmo.Axis.PositiveY - if (_generalHelper.compareVectors(root.targetNode.sceneRotation, - root.quaternionForAxis(OriginGizmo.Axis.PositiveY))) { + if (_generalHelper.compareQuaternions( + root.targetNode.sceneRotation, + root.quaternionForAxis(OriginGizmo.Axis.PositiveY))) { axis = OriginGizmo.Axis.NegativeY } root.axisClicked(axis) @@ -183,8 +185,9 @@ Item { initialPosition: Qt.vector3d(0, 0, 1) onTapped: { let axis = OriginGizmo.Axis.PositiveZ - if (_generalHelper.compareVectors(root.targetNode.sceneRotation, - root.quaternionForAxis(OriginGizmo.Axis.PositiveZ))) { + if (_generalHelper.compareQuaternions( + root.targetNode.sceneRotation, + root.quaternionForAxis(OriginGizmo.Axis.PositiveZ))) { axis = OriginGizmo.Axis.NegativeZ } root.axisClicked(axis) @@ -210,8 +213,9 @@ Item { initialPosition: Qt.vector3d(-1, 0, 0) onTapped: { let axis = OriginGizmo.Axis.NegativeX - if (_generalHelper.compareVectors(root.targetNode.sceneRotation, - root.quaternionForAxis(OriginGizmo.Axis.NegativeX))) { + if (_generalHelper.compareQuaternions( + root.targetNode.sceneRotation, + root.quaternionForAxis(OriginGizmo.Axis.NegativeX))) { axis = OriginGizmo.Axis.PositiveX } root.axisClicked(axis) @@ -231,8 +235,9 @@ Item { initialPosition: Qt.vector3d(0, -1, 0) onTapped: { let axis = OriginGizmo.Axis.NegativeY - if (_generalHelper.compareVectors(root.targetNode.sceneRotation, - root.quaternionForAxis(OriginGizmo.Axis.NegativeY))) { + if (_generalHelper.compareQuaternions( + root.targetNode.sceneRotation, + root.quaternionForAxis(OriginGizmo.Axis.NegativeY))) { axis = OriginGizmo.Axis.PositiveY } root.axisClicked(axis) @@ -252,8 +257,9 @@ Item { initialPosition: Qt.vector3d(0, 0, -1) onTapped: { let axis = OriginGizmo.Axis.NegativeZ - if (_generalHelper.compareVectors(root.targetNode.sceneRotation, - root.quaternionForAxis(OriginGizmo.Axis.NegativeZ))) { + if (_generalHelper.compareQuaternions( + root.targetNode.sceneRotation, + root.quaternionForAxis(OriginGizmo.Axis.NegativeZ))) { axis = OriginGizmo.Axis.PositiveZ } root.axisClicked(axis) diff --git a/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp b/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp index 075c32556cf..b50ea4e932e 100644 --- a/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp +++ b/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp @@ -1059,7 +1059,7 @@ void GeneralHelper::setBgColor(const QVariant &colors) } } -QVector3D GeneralHelper::dirForRotation(const QQuaternion &rotation) +QVector3D GeneralHelper::dirForRotation(const QQuaternion &rotation) const { QMatrix4x4 m; m.rotate(rotation); @@ -1225,11 +1225,17 @@ bool GeneralHelper::getBounds(QQuick3DViewport *view3D, QQuick3DNode *node, QVec return hasModel; } -bool GeneralHelper::compareVectors(const QVector3D &v1, const QVector3D &v2) +bool GeneralHelper::compareVectors(const QVector3D &v1, const QVector3D &v2) const { return qFuzzyCompare(v1[0], v2[0]) && qFuzzyCompare(v1[1], v2[1]) && qFuzzyCompare(v1[2], v2[2]); } +bool GeneralHelper::compareQuaternions(const QQuaternion &q1, const QQuaternion &q2) const +{ + return qFuzzyCompare(q1.x(), q2.x()) && qFuzzyCompare(q1.y(), q2.y()) + && qFuzzyCompare(q1.z(), q2.z()) && qFuzzyCompare(q1.scalar(), q2.scalar()); +} + } } diff --git a/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.h b/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.h index 0818e7059a0..fd76cadcfc6 100644 --- a/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.h +++ b/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.h @@ -135,8 +135,9 @@ public: void setBgColor(const QVariant &colors); QVariant bgColor() const { return m_bgColor; } - Q_INVOKABLE QVector3D dirForRotation(const QQuaternion &rotation); - Q_INVOKABLE bool compareVectors(const QVector3D &v1, const QVector3D &v2); + Q_INVOKABLE QVector3D dirForRotation(const QQuaternion &rotation) const; + Q_INVOKABLE bool compareVectors(const QVector3D &v1, const QVector3D &v2) const; + Q_INVOKABLE bool compareQuaternions(const QQuaternion &q1, const QQuaternion &q2) const; signals: void overlayUpdateNeeded(); From 1d04d50c78669b3ee0df9440c6db8545da9c896b Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Thu, 19 Oct 2023 19:21:31 +0300 Subject: [PATCH 079/242] EffectMaker: Add save dialog Change-Id: Ic538e6c8c9d3ac19bd8726b95d93a9b1f292a437 Reviewed-by: Miikka Heikkinen --- .../effectMakerQmlSources/EffectMaker.qml | 8 +- .../EffectMakerTopBar.qml | 6 +- .../effectMakerQmlSources/SaveDialog.qml | 88 +++++++++++++++++++ 3 files changed, 98 insertions(+), 4 deletions(-) create mode 100644 share/qtcreator/qmldesigner/effectMakerQmlSources/SaveDialog.qml diff --git a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMaker.qml b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMaker.qml index 49731b37130..56fcdcf5a30 100644 --- a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMaker.qml +++ b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMaker.qml @@ -15,13 +15,19 @@ Item { property int moveFromIdx: 0 property int moveToIdx: 0 + SaveDialog { + id: saveDialog + anchors.centerIn: parent + onAccepted: print("TODO: export and save effect files") + } + Column { id: col anchors.fill: parent spacing: 1 EffectMakerTopBar { - + onSaveClicked: saveDialog.open() } EffectMakerPreview { diff --git a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMakerTopBar.qml b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMakerTopBar.qml index eb5463f3bdf..c8758ff2ebb 100644 --- a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMakerTopBar.qml +++ b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMakerTopBar.qml @@ -15,15 +15,15 @@ Rectangle { height: StudioTheme.Values.toolbarHeight color: StudioTheme.Values.themeToolbarBackground + signal saveClicked + HelperWidgets.Button { anchors.verticalCenter: parent.verticalCenter x: 5 text: qsTr("Save in Library") - onClicked: { - // TODO - } + onClicked: root.saveClicked() } HelperWidgets.AbstractButton { diff --git a/share/qtcreator/qmldesigner/effectMakerQmlSources/SaveDialog.qml b/share/qtcreator/qmldesigner/effectMakerQmlSources/SaveDialog.qml new file mode 100644 index 00000000000..309dd433395 --- /dev/null +++ b/share/qtcreator/qmldesigner/effectMakerQmlSources/SaveDialog.qml @@ -0,0 +1,88 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import QtQuick +import QtQuick.Controls +import HelperWidgets as HelperWidgets +import StudioControls as StudioControls +import StudioTheme as StudioTheme +import AssetsLibraryBackend + +StudioControls.Dialog { + id: root + + title: qsTr("Save Effect") + + closePolicy: Popup.CloseOnEscape + modal: true + implicitWidth: 250 + + onOpened: { + nameText.text = "" + emptyText.opacity = 0 + nameText.forceActiveFocus() + } + + HelperWidgets.RegExpValidator { + id: validator + regExp: /^(\w[^*/> Date: Thu, 19 Oct 2023 16:39:21 +0300 Subject: [PATCH 080/242] QmlDesigner: Fix some parsing issues in effect maker Task-number: QDS-10987 Change-Id: Ifeb42f9e04ba39ddaa45a03b60eb12ce1652c61a Reviewed-by: Mahmoud Badri Reviewed-by: Miikka Heikkinen --- .../effectMakerQmlSources/EffectMaker.qml | 6 ++ .../EffectMakerPreview.qml | 3 + .../effectmakernew/compositionnode.cpp | 2 +- .../effectmakernew/effectmakermodel.cpp | 88 ++++++++++++++++++- src/plugins/effectmakernew/effectmakermodel.h | 8 ++ src/plugins/effectmakernew/uniform.cpp | 21 +++-- src/plugins/effectmakernew/uniform.h | 3 +- 7 files changed, 117 insertions(+), 14 deletions(-) diff --git a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMaker.qml b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMaker.qml index 56fcdcf5a30..7bc23c9c1f3 100644 --- a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMaker.qml +++ b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMaker.qml @@ -32,6 +32,12 @@ Item { EffectMakerPreview { mainRoot: root + + FrameAnimation { + id: previewFrameTimer + running: true + paused: false // TODO: from the toolbar + } } Rectangle { diff --git a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMakerPreview.qml b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMakerPreview.qml index 03d587e8195..0e686964971 100644 --- a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMakerPreview.qml +++ b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMakerPreview.qml @@ -12,6 +12,9 @@ import EffectMakerBackend Column { id: root + property real animatedTime: 0 //TODO get from animator + property int animatedFrame: 0 //TODO get from animator + width: parent.width required property Item mainRoot diff --git a/src/plugins/effectmakernew/compositionnode.cpp b/src/plugins/effectmakernew/compositionnode.cpp index 8952a29f3ad..a57ac74e94f 100644 --- a/src/plugins/effectmakernew/compositionnode.cpp +++ b/src/plugins/effectmakernew/compositionnode.cpp @@ -102,7 +102,7 @@ void CompositionNode::parse(const QString &qenPath) // parse properties QJsonArray jsonProps = json.value("properties").toArray(); for (const auto /*QJsonValueRef*/ &prop : jsonProps) { - const auto uniform = new Uniform(prop.toObject()); + const auto uniform = new Uniform(prop.toObject(), qenPath); m_unifomrsModel.addUniform(uniform); g_propertyData.insert(uniform->name(), uniform->value()); } diff --git a/src/plugins/effectmakernew/effectmakermodel.cpp b/src/plugins/effectmakernew/effectmakermodel.cpp index 6dbe9a78674..e3d8017d01f 100644 --- a/src/plugins/effectmakernew/effectmakermodel.cpp +++ b/src/plugins/effectmakernew/effectmakermodel.cpp @@ -60,6 +60,20 @@ EffectMakerModel::EffectMakerModel(QObject *parent) m_vertexShaderFilename = m_vertexShaderFile.fileName(); m_fragmentShaderFilename = m_fragmentShaderFile.fileName(); } + + connect(&m_fileWatcher, &Utils::FileSystemWatcher::fileChanged, this, [this]() { + // Update component with images not set. + m_loadComponentImages = false; + updateQmlComponent(); + // Then enable component images with a longer delay than + // the component updating delay. This way Image elements + // will reload the changed image files. + const int enableImagesDelay = 200; + QTimer::singleShot(enableImagesDelay, this, [this]() { + m_loadComponentImages = true; + updateQmlComponent(); + } ); + }); } QHash EffectMakerModel::roleNames() const @@ -734,6 +748,7 @@ void EffectMakerModel::handleQsbProcessExit(Utils::Process *qsbProcess, const QS --m_remainingQsbTargets; const QString errStr = qsbProcess->errorString(); + auto std = qsbProcess->stdErr(); if (!errStr.isEmpty()) qWarning() << QString("Failed to generate QSB file for: %1 %2").arg(shader, errStr); @@ -891,12 +906,47 @@ void EffectMakerModel::setShadersUpToDate(bool UpToDate) emit shadersUpToDateChanged(); } +// Returns name for image mipmap property. +// e.g. "myImage" -> "myImageMipmap". +QString EffectMakerModel::mipmapPropertyName(const QString &name) const +{ + QString simplifiedName = name.simplified(); + simplifiedName = simplifiedName.remove(' '); + simplifiedName += "Mipmap"; + return simplifiedName; +} + QString EffectMakerModel::getQmlImagesString(bool localFiles) { - Q_UNUSED(localFiles) - - // TODO - return QString(); + QString imagesString; + const QList uniforms = allUniforms(); + for (Uniform *uniform : uniforms) { + if (uniform->type() == Uniform::Type::Sampler) { + QString imagePath = uniform->value().toString(); + if (imagePath.isEmpty()) + continue; + imagesString += " Image {\n"; + QString simplifiedName = getImageElementName(*uniform); + imagesString += QString(" id: %1\n").arg(simplifiedName); + imagesString += " anchors.fill: parent\n"; + // File paths are absolute, return as local when requested + if (localFiles) { + QFileInfo fi(imagePath); + imagePath = fi.fileName(); + } + if (m_loadComponentImages) + imagesString += QString(" source: \"%1\"\n").arg(imagePath); + if (!localFiles) { + QString mipmapProperty = mipmapPropertyName(uniform->name()); + imagesString += QString(" mipmap: g_propertyData.%1\n").arg(mipmapProperty); + } else if (uniform->enableMipmap()) { + imagesString += " mipmap: true\n"; + } + imagesString += " visible: false\n"; + imagesString += " }\n"; + } + } + return imagesString; } QString EffectMakerModel::getQmlComponentString(bool localFiles) @@ -971,5 +1021,35 @@ void EffectMakerModel::updateQmlComponent() m_qmlComponentString = getQmlComponentString(false); } +// Removes "file:" from the URL path. +// So e.g. "file:///C:/myimages/steel1.jpg" -> "C:/myimages/steel1.jpg" +QString EffectMakerModel::stripFileFromURL(const QString &urlString) const +{ + QUrl url(urlString); + QString filePath = (url.scheme() == QStringLiteral("file")) ? url.toLocalFile() : url.toString(); + return filePath; +} + +void EffectMakerModel::updateImageWatchers() +{ + const QList uniforms = allUniforms(); + for (Uniform *uniform : uniforms) { + if (uniform->type() == Uniform::Type::Sampler) { + // Watch all image properties files + QString imagePath = stripFileFromURL(uniform->value().toString()); + if (imagePath.isEmpty()) + continue; + m_fileWatcher.addFile(imagePath, Utils::FileSystemWatcher::WatchAllChanges); + } + } +} + +void EffectMakerModel::clearImageWatchers() +{ + const auto watchedFiles = m_fileWatcher.files(); + if (!watchedFiles.isEmpty()) + m_fileWatcher.removeFiles(watchedFiles); +} + } // namespace EffectMaker diff --git a/src/plugins/effectmakernew/effectmakermodel.h b/src/plugins/effectmakernew/effectmakermodel.h index 1cd68e70a72..77f3df5577e 100644 --- a/src/plugins/effectmakernew/effectmakermodel.h +++ b/src/plugins/effectmakernew/effectmakermodel.h @@ -6,7 +6,9 @@ #include "shaderfeatures.h" #include +#include +#include #include #include #include @@ -132,10 +134,14 @@ private: QString generateVertexShader(bool includeUniforms = true); QString generateFragmentShader(bool includeUniforms = true); void handleQsbProcessExit(Utils::Process *qsbProcess, const QString &shader); + QString stripFileFromURL(const QString &urlString) const; + void updateImageWatchers(); + void clearImageWatchers(); void updateCustomUniforms(); void bakeShaders(); + QString mipmapPropertyName(const QString &name) const; QString getQmlImagesString(bool localFiles); QString getQmlComponentString(bool localFiles); @@ -169,6 +175,8 @@ private: // Used in preview QML, at ShaderEffect component of the file QString m_previewEffectPropertiesString; QString m_qmlComponentString; + bool m_loadComponentImages = true; + Utils::FileSystemWatcher m_fileWatcher; const QRegularExpression m_spaceReg = QRegularExpression("\\s+"); }; diff --git a/src/plugins/effectmakernew/uniform.cpp b/src/plugins/effectmakernew/uniform.cpp index 4901b6bbb6e..d658383eeea 100644 --- a/src/plugins/effectmakernew/uniform.cpp +++ b/src/plugins/effectmakernew/uniform.cpp @@ -4,6 +4,7 @@ #include "uniform.h" #include +#include "propertyhandler.h" #include #include @@ -11,7 +12,8 @@ namespace EffectMaker { -Uniform::Uniform(const QJsonObject &propObj) +Uniform::Uniform(const QJsonObject &propObj, const QString &qenPath) : + m_qenPath(qenPath) { QString value, defaultValue, minValue, maxValue; @@ -31,6 +33,7 @@ Uniform::Uniform(const QJsonObject &propObj) m_enableMipmap = getBoolValue(propObj.value("enableMipmap"), false); // Update the mipmap property QString mipmapProperty = mipmapPropertyName(m_name); + g_propertyData[mipmapProperty] = m_enableMipmap; } if (propObj.contains("value")) { value = propObj.value("value").toString(); @@ -164,9 +167,13 @@ bool Uniform::getBoolValue(const QJsonValue &jsonValue, bool defaultValue) // Used with sampler types QString Uniform::getResourcePath(const QString &value) const { - Q_UNUSED(value) - //TODO - return {}; + QString filePath = value; + QDir dir(m_qenPath); + dir.cdUp(); + QString absPath = dir.absoluteFilePath(filePath); + absPath = QDir::cleanPath(absPath); + absPath = QUrl::fromLocalFile(absPath).toString(); + return absPath; } // Validation and setting values @@ -263,10 +270,8 @@ QString Uniform::stringFromType(Uniform::Type type) return "vec2"; else if (type == Type::Vec3) return "vec3"; - else if (type == Type::Vec4) + else if (type == Type::Vec4 || type == Type::Color) return "vec4"; - else if (type == Type::Color) - return "color"; else if (type == Type::Sampler) return "sampler2D"; else if (type == Type::Define) @@ -292,7 +297,7 @@ Uniform::Type Uniform::typeFromString(const QString &typeString) return Uniform::Type::Vec4; else if (typeString == "color") return Uniform::Type::Color; - else if (typeString == "sampler2D") + else if (typeString == "image") return Uniform::Type::Sampler; else if (typeString == "define") return Uniform::Type::Define; diff --git a/src/plugins/effectmakernew/uniform.h b/src/plugins/effectmakernew/uniform.h index 7bad706cb3e..1fe2530cacd 100644 --- a/src/plugins/effectmakernew/uniform.h +++ b/src/plugins/effectmakernew/uniform.h @@ -42,7 +42,7 @@ public: Define }; - Uniform(const QJsonObject &props); + Uniform(const QJsonObject &props, const QString &qenPath); Type type() const; QString typeName() const; @@ -87,6 +87,7 @@ private: QVariant getInitializedVariant(bool maxValue); QVariant valueStringToVariant(const QString &value); + QString m_qenPath; Type m_type; QVariant m_value; QVariant m_defaultValue; From da1ecb4b363f9a60e42217c0080ca36346316f46 Mon Sep 17 00:00:00 2001 From: Pranta Dastider Date: Fri, 13 Oct 2023 17:26:58 +0200 Subject: [PATCH 081/242] QmlDesigner: Update the Designer-Developer workflow doc This patch updates the Designer-Developer workflow document. Removes the old image and add new images to support the current workflow. Fixes: QDS-10916 Change-Id: I3e448b36bd634da4fede76343567ebd2282faffd Reviewed-by: Alessandro Portale Reviewed-by: Mats Honkamaa --- ...tudio-project-export-advanced-options.webp | Bin 0 -> 11752 bytes .../studio-project-export-advanced.webp | Bin 0 -> 2488 bytes .../images/studio-project-export.webp | Bin 0 -> 12244 bytes .../images/studio-project-structure.png | Bin 51475 -> 0 bytes .../studio-designer-developer-workflow.qdoc | 102 ++++++++---------- 5 files changed, 42 insertions(+), 60 deletions(-) create mode 100644 doc/qtdesignstudio/images/studio-project-export-advanced-options.webp create mode 100644 doc/qtdesignstudio/images/studio-project-export-advanced.webp create mode 100644 doc/qtdesignstudio/images/studio-project-export.webp delete mode 100644 doc/qtdesignstudio/images/studio-project-structure.png diff --git a/doc/qtdesignstudio/images/studio-project-export-advanced-options.webp b/doc/qtdesignstudio/images/studio-project-export-advanced-options.webp new file mode 100644 index 0000000000000000000000000000000000000000..33be8549a700de37828c98956aceaf8ae2c277c0 GIT binary patch literal 11752 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 X&<9S%#d3$%Oc8U6)PBC-MMe?;vA&DJ literal 0 HcmV?d00001 diff --git a/doc/qtdesignstudio/images/studio-project-export-advanced.webp b/doc/qtdesignstudio/images/studio-project-export-advanced.webp new file mode 100644 index 0000000000000000000000000000000000000000..d6f1189093e7e8a6d2360d94c79234638af17b1b GIT binary patch literal 2488 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 W@BgEzZx2?@whP<&e_z=_b_M{aw8azv literal 0 HcmV?d00001 diff --git a/doc/qtdesignstudio/images/studio-project-export.webp b/doc/qtdesignstudio/images/studio-project-export.webp new file mode 100644 index 0000000000000000000000000000000000000000..8847dd945f801b7fcd7896705ec08d0e5f4eecec GIT binary patch literal 12244 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 U?~7D+lmPKPUHx3vIVCg!0KrPJF#rGn diff --git a/doc/qtdesignstudio/src/developers/studio-designer-developer-workflow.qdoc b/doc/qtdesignstudio/src/developers/studio-designer-developer-workflow.qdoc index 146501328c2..74ffff54688 100644 --- a/doc/qtdesignstudio/src/developers/studio-designer-developer-workflow.qdoc +++ b/doc/qtdesignstudio/src/developers/studio-designer-developer-workflow.qdoc @@ -31,76 +31,58 @@ \e CMakeLists.txt file as the project file. This enables you to share your project as a fully working C++ application with developers. - If you add or remove QML files in \QDS, you have to regenerate the - \e CMakeLists.txt project configuration file by selecting \uicontrol File - > \uicontrol {Export Project} > \uicontrol {Generate CMake Build Files}. - If you use Git, you can clone an example project \l{https://git.qt.io/public-demos/qtdesign-studio/-/tree/master/playground/AuroraCluster0} {here}. - The following image shows the example project structure and contents in the - \l Projects and \l {File System} views in \QDS and Qt Creator: + \section1 Exporting a \QDS Project - \image studio-project-structure.png "\QDS project in \QDS and Qt Creator views" - - \section1 Converting Project Structure for CMake - - \QDS can generate \e CMakeLists.txt and other related files to use with - Qt Creator and to compile into an executable application but only if the - project has a certain folder structure. If you have a \QDS QML project that - doesn't have the CMake configuration, follow these steps to convert its - file structure to the correct format. + \QDS uses a different project format than Qt Creator. \QDS does not build the project, + it uses a pre-compiled \l{QML runtime} to run the project. To export a \QDS project for the + Qt Creator, follow the process: \list 1 - \li Create a folder named \e content in the project's folder. This folder contains the - application's main module. - \li Move all QML files of the project's main module to the \e content folder. If your project - has multiple modules, place the other modules in the \e imports or - \e asset_imports folder. - \li If your project's main module has resource folders such as \e fonts or \e {images}, move - them to the \e content folder. - \li Create a folder named \e src in the project's folder. This folder contains C++ code for - compiling the project. - \li If your project doesn't have an \e imports folder for other QML modules, create it - now even if you do not have other modules. The CMake file generator expects it. - \li In the project's \e .qmlproject file: - \list - \li Add \e "." in importPaths. For example: - \code - importPaths: [ "imports", "asset_imports", "." ] - \endcode - \li Change mainFile to \e "content/App.qml": - \code - mainFile: "content/App.qml" - \endcode - \endlist - \li In the \e content folder, create a file named \e App.qml and add the following content: + \li Open the project you want to export in \QDS. + \li Select \uicontrol {File} > \uicontrol {Export Project} > \uicontrol {Generate CMake Build Files}. + \image studio-project-export.webp "Export the \QDS project for Qt Creator" - \qml - import QtQuick - import QtQuick.Window - import YourImportModuleHere - Window { - width: Constants.width - height: Constants.height - visible: true - title: "YourWindowTitleHere" - { - } - } - \endqml + \li Select \uicontrol {Details} to access the \l {Advanced Options}. + \image studio-project-export-advanced.webp "Access Advanced Options in the project exporter" - \li In \e{App.qml}, modify imported modules, window dimensions, window title, and main QML - class appropriately. + \note The project exporter has default settings selected. This works better if the project + is combined with an existing Qt project. - \note This template assumes that your project has a module named \e YourImportModuleHere in - the \a imports folder containing a singleton class named \a Constants. - This isn't mandatory. - - \li Generate CMake files and C++ source files that are used to compile the application into - an executable file by selecting \uicontrol File > \uicontrol {Export Project} > - \uicontrol {Generate CMake Build Files}. + \li Select all the options here. This allows to export the + complete project. So, it can be compiled as a stand-alone application. + \image studio-project-export-advanced-options.webp "Select all the options in the project exporter" + \note If you copy this export on top of the existing Qt Creator project + it overwrites the existing project. Hence, the default selected options in + the exporter only exports the QML-specific items. You get a list of + warnings at the bottom part of the exporter that denotes exactly which parts + of the project gets overwritten. \endlist + + \section1 Using the Exported Project in Qt Creator + + After exporting the project from the \QDS, you have to open it from Qt Creator. + + If you have used any version before \QDS 4.0 to create the project, manually include this code + in the \l {CMakeLists.txt} file so the exported project works in Qt Creator. + + \code + set(BUILD_QDS_COMPONENTS ON CACHE BOOL "Build design studio components") + + set(CMAKE_INCLUDE_CURRENT_DIR ON) + + if (${BUILD_QDS_COMPONENTS}) + include(${CMAKE_CURRENT_SOURCE_DIR}/qmlcomponents) + endif () + + include(${CMAKE_CURRENT_SOURCE_DIR}/qmlmodules) + \endcode + + \note If you have created the project with the \QDS version 4.0 or above, you already have this code in + \l {CMakeLists.txt} by default. + */ From 957d8efe56d9afc3f9e14840957c55acd05d09d3 Mon Sep 17 00:00:00 2001 From: Pranta Dastider Date: Mon, 16 Oct 2023 14:21:23 +0200 Subject: [PATCH 082/242] QmlDesigner: Update the text arrangement and include tutorial video Some text were out of the place. This patch fixes that. It also includes a tutorial video that was later created for showcasing the connection view workflow. Fixes: QDS-10937 Change-Id: If0b6ddba89022db5bf817a16368c6e2747855355 Reviewed-by: Leena Miettinen Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Mats Honkamaa Reviewed-by: Johanna Vanhatapio Reviewed-by: Thomas Hartmann --- .../images/extraimages/images/KDxnMQzgmIY.jpg | Bin 0 -> 11538 bytes .../qtdesignstudio-extraimages.qdocconf | 3 ++- .../qtquick-connection-editor-signals.qdoc | 15 +++++++++------ 3 files changed, 11 insertions(+), 7 deletions(-) create mode 100644 doc/qtcreator/images/extraimages/images/KDxnMQzgmIY.jpg diff --git a/doc/qtcreator/images/extraimages/images/KDxnMQzgmIY.jpg b/doc/qtcreator/images/extraimages/images/KDxnMQzgmIY.jpg new file mode 100644 index 0000000000000000000000000000000000000000..de226c2b1a3d2703fb12fef5b0921d29cf97d83e GIT binary patch literal 11538 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 VLrMY|e4&L7*@H*JM8Ew1n*eaI1pEL1 literal 0 HcmV?d00001 diff --git a/doc/qtcreator/images/extraimages/qtdesignstudio-extraimages.qdocconf b/doc/qtcreator/images/extraimages/qtdesignstudio-extraimages.qdocconf index 7a4f2d4dcee..35f519210c0 100644 --- a/doc/qtcreator/images/extraimages/qtdesignstudio-extraimages.qdocconf +++ b/doc/qtcreator/images/extraimages/qtdesignstudio-extraimages.qdocconf @@ -13,4 +13,5 @@ images/V3Po15bNErw.jpg \ images/bMXeeQw6BYs.jpg \ images/u3kZJjlk3CY.jpg \ - images/9MqUCP6JLCQ.jpg + images/9MqUCP6JLCQ.jpg \ + images/KDxnMQzgmIY.jpg diff --git a/doc/qtdesignstudio/src/views/qtquick-connection-editor-signals.qdoc b/doc/qtdesignstudio/src/views/qtquick-connection-editor-signals.qdoc index e175a84d4fd..fe7e83141da 100644 --- a/doc/qtdesignstudio/src/views/qtquick-connection-editor-signals.qdoc +++ b/doc/qtdesignstudio/src/views/qtquick-connection-editor-signals.qdoc @@ -112,6 +112,13 @@ \li Open the \uicontrol {Manual Code Edit} window from the \uicontrol {Connections} view and write JavaScript expressions with components and logical expressions manually. + + \note If you create a conditional expression by selecting options from the + drop-down menus in the \uicontrol {Connection} view, you can only create a single + level \e {if-else} expression. For nested level \e {if-else} expressions, + use the \uicontrol {Manual Code Edit}. + + \image qmldesigner-connections-ConditionalAction-Manual.webp \endlist \section2 Action Properties @@ -152,11 +159,7 @@ \li N/A \endtable - \note If you create a conditional expression by selecting options from drop-down menus in - the \uicontrol {Connection} view, you can only create a single - level {if-else} expression. For nested level \e {if-elseif-else} expressions, - you have to use the \uicontrol {Manual Code Edit}. - - \image qmldesigner-connections-ConditionalAction-Manual.webp + Watch this video for practical examples of the \uicontrol {Connection} view workflow: + \youtube KDxnMQzgmIY */ From 104a45a4610eb2759c6a7f7ff8797e0f6afcdffe Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Thu, 19 Oct 2023 14:33:05 +0300 Subject: [PATCH 083/242] QmlDesigner: Add table buttons for adding rows and columns Task-number: QDS-11003 Change-Id: I573921ab6f57d7854658086a46051c8650e5b23a Reviewed-by: Mahmoud Badri Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Miikka Heikkinen --- .../CollectionDetailsToolbar.qml | 8 ++- .../CollectionDetailsView.qml | 52 +++++++++++++++++-- .../collectioneditor/collectiondetails.cpp | 2 +- 3 files changed, 55 insertions(+), 7 deletions(-) diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsToolbar.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsToolbar.qml index 97c218c6417..84cd0505046 100644 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsToolbar.qml +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsToolbar.qml @@ -16,7 +16,13 @@ Item { implicitHeight: 30 implicitWidth: leftSideToolbar.width + rightSideToolbar.width - signal addPropertyRight() + function addNewColumn() { + addColumnDialog.popUp(root.model.columnCount()) + } + + function addNewRow() { + root.model.insertRow(root.model.rowCount()) + } Row { id: leftSideToolbar diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsView.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsView.qml index d0ac4b0dfed..f12cacd97a5 100644 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsView.qml +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsView.qml @@ -58,6 +58,7 @@ Rectangle { } CollectionDetailsToolbar { + id: toolbar model: root.model Layout.fillWidth: true } @@ -68,7 +69,7 @@ Rectangle { } GridLayout { - columns: 2 + columns: 3 rowSpacing: 1 columnSpacing: 1 @@ -84,6 +85,8 @@ Rectangle { Layout.preferredWidth: rowIdView.width Layout.preferredHeight: headerView.height + Layout.minimumWidth: rowIdView.width + Layout.minimumHeight: headerView.height Text { anchors.fill: parent @@ -102,7 +105,7 @@ Rectangle { property real bottomPadding: 5 Layout.preferredHeight: headerTextMetrics.height + topPadding + bottomPadding - Layout.fillWidth: true + Layout.columnSpan: 2 syncView: tableView clip: true @@ -155,7 +158,9 @@ Rectangle { syncView: tableView clip: true - Layout.fillHeight: true + Layout.preferredHeight: tableView.height + Layout.rowSpan: 2 + Layout.alignment: Qt.AlignTop + Qt.AlignLeft delegate: HeaderDelegate { selectedItem: root.model.selectedRow @@ -176,8 +181,10 @@ Rectangle { model: root.model clip: true - Layout.fillWidth: true - Layout.fillHeight: true + Layout.preferredWidth: tableView.contentWidth + Layout.preferredHeight: tableView.contentHeight + Layout.minimumWidth: 10 + Layout.minimumHeight: 10 delegate: Rectangle { id: itemCell @@ -240,6 +247,41 @@ Rectangle { ] } } + + HelperWidgets.IconButton { + id: addColumnContainer + + iconSize:16 + Layout.preferredWidth: 24 + Layout.preferredHeight: tableView.height + Layout.minimumHeight: 24 + Layout.alignment: Qt.AlignLeft + Qt.AlignVCenter + + icon: StudioTheme.Constants.create_medium + tooltip: "Add Column" + + onClicked: toolbar.addNewColumn() + } + + HelperWidgets.IconButton { + id: addRowContainer + + iconSize:16 + Layout.preferredWidth: tableView.width + Layout.preferredHeight: 24 + Layout.minimumWidth: 24 + Layout.alignment: Qt.AlignTop + Qt.AlignHCenter + + icon: StudioTheme.Constants.create_medium + tooltip: "Add Row" + + onClicked: toolbar.addNewRow() + } + + Item { + Layout.fillWidth: true + Layout.fillHeight: true + } } } diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp index 5b84ec55710..5c05d685a79 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp @@ -189,7 +189,7 @@ void CollectionDetails::insertEmptyElements(int row, int count) if (count < 1) return; - row = qBound(0, row, rows() - 1); + row = qBound(0, row, rows()); d->elements.insert(row, count, {}); markChanged(); From ebab3e1c5a53a5ef9926c19ee0814ec89811517b Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Fri, 20 Oct 2023 11:14:59 +0300 Subject: [PATCH 084/242] QmlDesigner: Prevent collapsing the collection node while it's selected Task-number: QDS-11005 Change-Id: Iacc4494fac3b37f3bbeb71c99ab012d30b5a44da Reviewed-by: Miikka Heikkinen --- .../ModelSourceItem.qml | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/ModelSourceItem.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/ModelSourceItem.qml index 9496ee394a7..ef609e7f2be 100644 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/ModelSourceItem.qml +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/ModelSourceItem.qml @@ -21,6 +21,11 @@ Item { signal selectItem(int itemIndex) signal deleteItem() + function toggleExpanded() { + if (collectionListView.count > 0) + root.expanded = !root.expanded || sourceIsSelected; + } + Column { id: wholeColumn @@ -48,8 +53,7 @@ Item { } onDoubleClicked: (event) => { - if (collectionListView.count > 0) - root.expanded = !root.expanded; + root.toggleExpanded() } } @@ -78,6 +82,7 @@ Item { color: textColor rotation: root.expanded ? 90 : 0 + visible: collectionListView.count > 0 Behavior on rotation { SpringAnimation { spring: 2; damping: 0.2 } @@ -87,11 +92,10 @@ Item { anchors.fill: parent acceptedButtons: Qt.RightButton + Qt.LeftButton onClicked: (event) => { - root.expanded = !root.expanded + root.toggleExpanded() event.accepted = true } } - visible: collectionListView.count > 0 } Text { @@ -325,6 +329,12 @@ Item { PropertyChanges { target: root textColor: StudioTheme.Values.themeIconColorSelected + expanded: true + } + + PropertyChanges { + target: expandButton + enabled: false } } ] From 6fcd0debe4fdf79dad18aa2ca6410e567f0d874e Mon Sep 17 00:00:00 2001 From: Aleksei German Date: Wed, 18 Oct 2023 19:34:23 +0200 Subject: [PATCH 085/242] QmlDesigner: Improve percent Gradient support - Changes default gradient units to Percents - Adds percentage support to Radius properties - Moves instanceValue checks into PropertyData - Moves binding string assembly into PropertyData Task-number: QDS-10942 Change-Id: I952aafa60e3b2e3221f74e5571516c2d4cbe7c02 Reviewed-by: Qt CI Bot Reviewed-by: Thomas Hartmann Reviewed-by: Qt CI Patch Build Bot --- .../propertyeditor/gradientmodel.cpp | 281 +++++++++++------- 1 file changed, 172 insertions(+), 109 deletions(-) diff --git a/src/plugins/qmldesigner/components/propertyeditor/gradientmodel.cpp b/src/plugins/qmldesigner/components/propertyeditor/gradientmodel.cpp index eaf907e8f82..b435322c39d 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/gradientmodel.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/gradientmodel.cpp @@ -22,58 +22,42 @@ #include namespace { -constexpr auto defaultValueLinearX1 = [](const QmlDesigner::QmlItemNode &) -> qreal { return 0.0; }; -constexpr auto defaultValueLinearY1 = [](const QmlDesigner::QmlItemNode &) -> qreal { return 0.0; }; -constexpr auto defaultValueLinearX2 = [](const QmlDesigner::QmlItemNode &node) -> qreal { - return node.instanceValue("width").toReal(); +constexpr auto widthBinding = [](const QStringView nodeName) -> QString { + return QString("%1.width").arg(nodeName); }; -constexpr auto defaultValueLinearY2 = [](const QmlDesigner::QmlItemNode &node) -> qreal { - return node.instanceValue("height").toReal(); +constexpr auto heightBinding = [](const QStringView nodeName) -> QString { + return QString("%1.height").arg(nodeName); }; -constexpr auto defaultValueRadialCenterRadius = [](const QmlDesigner::QmlItemNode &node) -> qreal { - const qreal width = node.instanceValue("width").toReal(); - const qreal height = node.instanceValue("height").toReal(); - return qMin(width, height) / 2.0; +constexpr auto minBinding = [](const QStringView nodeName) -> QString { + return QString("Math.min(%1.width, %1.height)").arg(nodeName); }; -constexpr auto defaultValueRadialCenterX = [](const QmlDesigner::QmlItemNode &node) -> qreal { - return (node.instanceValue("width").toReal() / 2.0); -}; -constexpr auto defaultValueRadialCenterY = [](const QmlDesigner::QmlItemNode &node) -> qreal { - return (node.instanceValue("height").toReal() / 2.0); -}; -constexpr auto defaultValueRadialFocalRadius = [](const QmlDesigner::QmlItemNode &) -> qreal { - return 0.0; -}; -constexpr auto defaultValueRadialFocalX = [](const QmlDesigner::QmlItemNode &node) -> qreal { - return (node.instanceValue("width").toReal() / 2.0); -}; -constexpr auto defaultValueRadialFocalY = [](const QmlDesigner::QmlItemNode &node) -> qreal { - return (node.instanceValue("height").toReal() / 2.0); -}; -constexpr auto defaultValueConicalAngle = [](const QmlDesigner::QmlItemNode &) -> qreal { - return 0.0; -}; -constexpr auto defaultValueConicalCenterX = [](const QmlDesigner::QmlItemNode &node) -> qreal { - return (node.instanceValue("width").toReal() / 2.0); -}; -constexpr auto defaultValueConicalCenterY = [](const QmlDesigner::QmlItemNode &node) -> qreal { - return (node.instanceValue("height").toReal() / 2.0); +constexpr auto emptyBinding = [](const QStringView) -> QString { + return {}; }; -using DefaultValueFunctionVariant = std::variant; +using BindingStringFunctionVariant = std::variant; + +constexpr auto widthBindingValue = [](const QmlDesigner::QmlItemNode &node) -> qreal { + return node.instanceValue("width").toReal(); +}; +constexpr auto heightBindingValue = [](const QmlDesigner::QmlItemNode &node) -> qreal { + return node.instanceValue("height").toReal(); +}; +constexpr auto minBindingValue = [](const QmlDesigner::QmlItemNode &node) -> qreal { + return qMin(node.instanceValue("width").toReal(), node.instanceValue("height").toReal()); +}; +constexpr auto emptyBindingValue = [](const QmlDesigner::QmlItemNode &) -> qreal { return 0.0; }; + +using BindingValueFunctionVariant = std::variant; + } // namespace class ShapeGradientPropertyData @@ -84,34 +68,60 @@ public: constexpr ShapeGradientPropertyData() = default; constexpr ShapeGradientPropertyData(QmlDesigner::PropertyNameView name, - QmlDesigner::PropertyNameView bindingProperty, UsePercents canPercent, - DefaultValueFunctionVariant defVariant) + qreal defPercent, + BindingStringFunctionVariant binVariant, + BindingValueFunctionVariant binValueVariant) : name(name) - , bindingProperty(bindingProperty) , canUsePercentage(canPercent) - , m_defaultValue(defVariant) + , defaultPercent(defPercent) + , m_bindingString(binVariant) + , m_bindingValue(binValueVariant) {} QmlDesigner::PropertyNameView name; - QmlDesigner::PropertyNameView bindingProperty; UsePercents canUsePercentage = UsePercents::No; + qreal defaultPercent = 0.0; private: - DefaultValueFunctionVariant m_defaultValue; + BindingStringFunctionVariant m_bindingString; + BindingValueFunctionVariant m_bindingValue; public: - constexpr qreal getDefaultValue(const QmlDesigner::QmlItemNode &itemNode) const + QString getBindingString(const QStringView nodeId) const { return std::visit( - [&](auto &defValue) -> qreal { - using Type = std::decay_t; + [&](auto &func) -> QString { + using Type = std::decay_t; if constexpr (std::is_same_v) - return 0.0; + return {}; else - return defValue(itemNode); + return func(nodeId); }, - m_defaultValue); + m_bindingString); + } + + qreal getBindingValue(const QmlDesigner::QmlItemNode &node) const + { + return std::visit( + [&](auto &func) -> qreal { + using Type = std::decay_t; + if constexpr (std::is_same_v) + return {}; + else + return func(node); + }, + m_bindingValue); + } + + qreal getDefaultValue(const QmlDesigner::QmlItemNode &itemNode) const + { + return getBindingValue(itemNode) * defaultPercent; + } + + QString getDefaultPercentString(const QStringView nodeId) const + { + return QString("%1 * %2").arg(getBindingString(nodeId), QString::number(defaultPercent)); } }; @@ -132,26 +142,75 @@ constexpr QmlDesigner::PropertyNameView conicalCenterXStr = u8"centerX"; constexpr QmlDesigner::PropertyNameView conicalCenterYStr = u8"centerY"; constexpr ShapeGradientPropertyData defaultLinearShapeGradients[] = { - {linearX1Str, u8"width", ShapeGradientPropertyData::UsePercents::Yes, defaultValueLinearX1}, - {linearX2Str, u8"width", ShapeGradientPropertyData::UsePercents::Yes, defaultValueLinearX2}, - {linearY1Str, u8"height", ShapeGradientPropertyData::UsePercents::Yes, defaultValueLinearY1}, - {linearY2Str, u8"height", ShapeGradientPropertyData::UsePercents::Yes, defaultValueLinearY2}}; + {linearX1Str, + ShapeGradientPropertyData::UsePercents::Yes, + 0.0, + widthBinding, + widthBindingValue}, + {linearX2Str, + ShapeGradientPropertyData::UsePercents::Yes, + 1.0, + widthBinding, + widthBindingValue}, + {linearY1Str, + ShapeGradientPropertyData::UsePercents::Yes, + 0.0, + heightBinding, + heightBindingValue}, + {linearY2Str, + ShapeGradientPropertyData::UsePercents::Yes, + 1.0, + heightBinding, + heightBindingValue}}; constexpr ShapeGradientPropertyData defaultRadialShapeGradients[] = { - {radialCenterRadiusStr, u8"", ShapeGradientPropertyData::UsePercents::No, defaultValueRadialCenterRadius}, - {radialCenterXStr, u8"width", ShapeGradientPropertyData::UsePercents::Yes, defaultValueRadialCenterX}, - {radialCenterYStr, u8"height", ShapeGradientPropertyData::UsePercents::Yes, defaultValueRadialCenterY}, - {radialFocalRadiusStr, u8"", ShapeGradientPropertyData::UsePercents::No, defaultValueRadialFocalRadius}, - {radialFocalXStr, u8"width", ShapeGradientPropertyData::UsePercents::Yes, defaultValueRadialFocalX}, - {radialFocalYStr, u8"height", ShapeGradientPropertyData::UsePercents::Yes, defaultValueRadialFocalY}}; + {radialCenterRadiusStr, + ShapeGradientPropertyData::UsePercents::Yes, + 0.5, + minBinding, + minBindingValue}, + {radialCenterXStr, + ShapeGradientPropertyData::UsePercents::Yes, + 0.5, + widthBinding, + widthBindingValue}, + {radialCenterYStr, + ShapeGradientPropertyData::UsePercents::Yes, + 0.5, + heightBinding, + heightBindingValue}, + {radialFocalRadiusStr, + ShapeGradientPropertyData::UsePercents::Yes, + 0.0, + minBinding, + minBindingValue}, + {radialFocalXStr, + ShapeGradientPropertyData::UsePercents::Yes, + 0.5, + widthBinding, + widthBindingValue}, + {radialFocalYStr, + ShapeGradientPropertyData::UsePercents::Yes, + 0.5, + heightBinding, + heightBindingValue}}; constexpr ShapeGradientPropertyData defaultConicalShapeGradients[] = { - {conicalAngleStr, u8"", ShapeGradientPropertyData::UsePercents::No, defaultValueConicalAngle}, - {conicalCenterXStr, u8"width", ShapeGradientPropertyData::UsePercents::Yes, defaultValueConicalCenterX}, - {conicalCenterYStr, - u8"height", + {conicalAngleStr, + ShapeGradientPropertyData::UsePercents::No, + 0.0, + emptyBinding, + emptyBindingValue}, + {conicalCenterXStr, ShapeGradientPropertyData::UsePercents::Yes, - defaultValueConicalCenterY}}; + 0.5, + widthBinding, + widthBindingValue}, + {conicalCenterYStr, + ShapeGradientPropertyData::UsePercents::Yes, + 0.5, + heightBinding, + heightBindingValue}}; template const ShapeGradientPropertyData *findGradientInArray(const GradientArrayType &array, @@ -183,10 +242,17 @@ const ShapeGradientPropertyData *getDefaultGradientData(const QmlDesigner::Prope template void prepareGradient(const T &array, const QmlDesigner::ModelNode &gradient, - const QmlDesigner::QmlItemNode &node) + const QmlDesigner::QmlItemNode &node, + ShapeGradientPropertyData::UsePercents usePercents) { - std::for_each(std::begin(array), std::end(array), [&](auto &a) { - gradient.variantProperty(a.name.toByteArray()).setValue(a.getDefaultValue(node)); + std::for_each(std::begin(array), std::end(array), [&](const ShapeGradientPropertyData &a) { + if (a.canUsePercentage == ShapeGradientPropertyData::UsePercents::Yes + && usePercents == ShapeGradientPropertyData::UsePercents::Yes) { + gradient.bindingProperty(a.name.toByteArray()) + .setExpression(a.getDefaultPercentString(node.id())); + } else { + gradient.variantProperty(a.name.toByteArray()).setValue(a.getDefaultValue(node)); + } }); } @@ -593,11 +659,20 @@ void GradientModel::setupGradientProperties(const QmlDesigner::ModelNode &gradie if (m_gradientTypeName == u"Gradient") { gradient.variantProperty("orientation").setEnumeration("Gradient.Vertical"); } else if (m_gradientTypeName == u"LinearGradient") { - prepareGradient(defaultLinearShapeGradients, gradient, m_itemNode); + prepareGradient(defaultLinearShapeGradients, + gradient, + m_itemNode, + ShapeGradientPropertyData::UsePercents::Yes); } else if (m_gradientTypeName == u"RadialGradient") { - prepareGradient(defaultRadialShapeGradients, gradient, m_itemNode); + prepareGradient(defaultRadialShapeGradients, + gradient, + m_itemNode, + ShapeGradientPropertyData::UsePercents::Yes); } else if (m_gradientTypeName == u"ConicalGradient") { - prepareGradient(defaultConicalShapeGradients, gradient, m_itemNode); + prepareGradient(defaultConicalShapeGradients, + gradient, + m_itemNode, + ShapeGradientPropertyData::UsePercents::Yes); } } @@ -684,7 +759,10 @@ qreal GradientModel::getPercentageGradientProperty(const QmlDesigner::PropertyNa if (ok != nullptr) *ok = false; - if (!m_itemNode.isValid()) + if (!m_itemNode.isValid() || !m_itemNode.hasModelNode()) + return 0.; + + if (!m_itemNode.modelNode().hasId()) return 0.; //valid format is itemName1.property * 0.1 @@ -703,33 +781,20 @@ qreal GradientModel::getPercentageGradientProperty(const QmlDesigner::PropertyNa if (const auto bindingProperty = gradientModel.bindingProperty(propertyName.toByteArray())) { const auto defaultGradient = getDefaultGradientPropertyData(propertyName, m_gradientTypeName); - const auto expectedParentProperty = defaultGradient.bindingProperty; + const auto expectedBindingProperty = defaultGradient.getBindingString(m_itemNode.id()); const QString expression = bindingProperty.expression(); const QStringList splitExpression = expression.split("*", Qt::SkipEmptyParts); - if (splitExpression.length() == 2 && !expectedParentProperty.isEmpty()) { + if (splitExpression.length() == 2 && !expectedBindingProperty.isEmpty()) { const QString parentStr = splitExpression.at(0).trimmed(); const QString percentageStr = splitExpression.at(1).trimmed(); - bool validStatement = false; - - if (!parentStr.isEmpty()) { - const QStringList splitParent = parentStr.split(".", Qt::SkipEmptyParts); - if (splitParent.length() == 2) { - const QString itemId = splitParent.at(0).trimmed(); - const QString itemProp = splitParent.at(1).trimmed(); - const QString realParentId = m_itemNode.modelNode().hasId() ? m_itemNode.id() : ""; - if (!itemId.isEmpty() && !itemProp.isEmpty() && (itemId == realParentId) - && (itemProp.toUtf8() == expectedParentProperty)) { - validStatement = true; - } + if (!parentStr.isEmpty() && !percentageStr.isEmpty()) { + if (parentStr == expectedBindingProperty) { + const qreal percentage = percentageStr.toFloat(ok); + return percentage; } } - - if (!percentageStr.isEmpty() && validStatement) { - const qreal percentage = percentageStr.toFloat(ok); - return percentage; - } } } @@ -793,17 +858,15 @@ void GradientModel::setGradientPropertyPercentage(const QString &propertyName, q QTC_ASSERT(gradientDefaultData.canUsePercentage == ShapeGradientPropertyData::UsePercents::Yes, return); - const QmlDesigner::PropertyNameView parentPropertyStr = gradientDefaultData.bindingProperty; - QTC_ASSERT(!parentPropertyStr.isEmpty(), return); + const QString parentId = m_itemNode.validId(); + const QString bindingPropertyStr = gradientDefaultData.getBindingString(parentId); + QTC_ASSERT(!bindingPropertyStr.isEmpty(), return); - if (parentPropertyStr.isEmpty() + if (bindingPropertyStr.isEmpty() || (gradientDefaultData.canUsePercentage == ShapeGradientPropertyData::UsePercents::No)) { return; } - - const QString parentId = m_itemNode.validId(); - const QString leftBinding = parentId + "." + parentPropertyStr; - const QString expression = leftBinding + " * " + QString::number(value); + const QString expression = bindingPropertyStr + " * " + QString::number(value); try { gradient.setBindingProperty(propertyName.toUtf8(), expression); @@ -840,11 +903,11 @@ void GradientModel::setGradientPropertyUnits(const QString &propertyName, const auto defaultGradientData = getDefaultGradientPropertyData(propertyName.toUtf8(), m_gradientTypeName); - const QmlDesigner::PropertyNameView parentPropertyName = defaultGradientData.bindingProperty; + const QString parentPropertyName = defaultGradientData.getBindingString(m_itemNode.validId()); if (parentPropertyName.isEmpty()) return; - const qreal parentPropertyValue = m_itemNode.instanceValue(parentPropertyName.toByteArray()).toReal(); + const qreal parentPropertyValue = defaultGradientData.getBindingValue(m_itemNode); if (toPixels) { bool ok = false; From 09f13dea040b9275657a0f9312966f96380f09c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Jen=C3=9Fen?= Date: Sat, 21 Oct 2023 09:24:50 +0200 Subject: [PATCH 086/242] QrCodeGenerator: enable by default BUILD_DESIGNSTUDIO was introduced to have all commercial plugins as a developer while programming. In packages its depending on which plugin goes to which license and we did not had that for libraries. Change-Id: Ib02b166ea756c939e19f625c76207c07729f38bc Reviewed-by: Tim Jenssen --- src/libs/3rdparty/qrcodegen/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libs/3rdparty/qrcodegen/CMakeLists.txt b/src/libs/3rdparty/qrcodegen/CMakeLists.txt index 2fff8d04373..15a03147ee4 100644 --- a/src/libs/3rdparty/qrcodegen/CMakeLists.txt +++ b/src/libs/3rdparty/qrcodegen/CMakeLists.txt @@ -1,8 +1,8 @@ add_qtc_library(QrCodeGenerator STATIC - CONDITION BUILD_DESIGNSTUDIO + CONDITION TARGET Qt6::Quick AND TARGET Qt6::Svg PUBLIC_INCLUDES ${CMAKE_CURRENT_SOURCE_DIR}/src DEPENDS - Qt6::Qml Qt6::Quick Qt6::Svg Qt6::QuickWidgets + Qt6::Qml Qt6::Quick Qt6::Svg SOURCES src/qrcodegen.cpp src/qrcodegen.h From ea7aca28c70bb9fbda872edeace6a27312d88e34 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Mon, 23 Oct 2023 12:59:42 +0300 Subject: [PATCH 087/242] QmlDesigner: Store 3D split view camera angles per split Splits 1-3 default to axis oriented views. Fixes: QDS-10996 Change-Id: I111c9b76ae335f8630eeae5e51bbefb2e702f6df Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Mahmoud Badri --- .../mockfiles/qt6/EditCameraController.qml | 23 ++++++++----- .../qml2puppet/mockfiles/qt6/EditView3D.qml | 32 +++++++++++++------ 2 files changed, 37 insertions(+), 18 deletions(-) diff --git a/src/tools/qml2puppet/mockfiles/qt6/EditCameraController.qml b/src/tools/qml2puppet/mockfiles/qt6/EditCameraController.qml index f8263be85a0..04856bc7631 100644 --- a/src/tools/qml2puppet/mockfiles/qt6/EditCameraController.qml +++ b/src/tools/qml2puppet/mockfiles/qt6/EditCameraController.qml @@ -37,7 +37,6 @@ Item { if (!camera || ignoreToolState) return; - // TODO: handle camera control state separately for each split _lookAtPoint = cameraState[0]; _zoomFactor = cameraState[1]; camera.position = cameraState[2]; @@ -51,13 +50,21 @@ Item { if (!camera) return; - // TODO: handle camera control state separately for each split _lookAtPoint = Qt.vector3d(0, 0, 0); _zoomFactor = 1; - camera.position = _defaultCameraPosition; - camera.eulerRotation = _defaultCameraRotation; - _generalHelper.zoomCamera(view3d, camera, 0, _defaultCameraLookAtDistance, _lookAtPoint, - _zoomFactor, false); + + if (splitId === 1) { + jumpToRotation(originGizmo.quaternionForAxis(OriginGizmo.PositiveZ)); + } else if (splitId === 2) { + jumpToRotation(originGizmo.quaternionForAxis(OriginGizmo.NegativeY)); + } else if (splitId === 3) { + jumpToRotation(originGizmo.quaternionForAxis(OriginGizmo.NegativeX)); + } else { + camera.position = _defaultCameraPosition; + camera.eulerRotation = _defaultCameraRotation; + _generalHelper.zoomCamera(view3d, camera, 0, _defaultCameraLookAtDistance, _lookAtPoint, + _zoomFactor, false); + } } function storeCameraState(delay) @@ -65,13 +72,12 @@ Item { if (!camera || ignoreToolState) return; - // TODO: handle camera control state separately for each split var cameraState = []; cameraState[0] = _lookAtPoint; cameraState[1] = _zoomFactor; cameraState[2] = camera.position; cameraState[3] = camera.rotation; - _generalHelper.storeToolState(sceneId, "editCamState", cameraState, delay); + _generalHelper.storeToolState(sceneId, "editCamState" + splitId, cameraState, delay); } function focusObject(targetNodes, rotation, updateZoom, closeUp) @@ -249,6 +255,7 @@ Item { } OriginGizmo { + id: originGizmo anchors.right: parent.right anchors.top: parent.top anchors.margins: 10 diff --git a/src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml b/src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml index 01c35cb4e05..8a8efa1711b 100644 --- a/src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml +++ b/src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml @@ -12,7 +12,7 @@ Item { visible: true property Node activeScene: null - property int activeSplit: 0 + property int activeSplit: 0 property var editViews: [null, null, null, null] property var overlayViews: [overlayView0, overlayView1, overlayView2, overlayView3] property var cameraControls: [cameraControl0, cameraControl1, cameraControl2, cameraControl3] @@ -69,6 +69,10 @@ Item { onSelectionModeChanged: _generalHelper.storeToolState(sceneId, "selectionMode", selectionMode); onTransformModeChanged: _generalHelper.storeToolState(sceneId, "transformMode", transformMode); onSplitViewChanged: _generalHelper.storeToolState(sceneId, "splitView", splitView) + onActiveSplitChanged: { + _generalHelper.storeToolState(sceneId, "activeSplit", activeSplit); + cameraControls[activeSplit].forceActiveFocus(); + } onActiveSceneChanged: updateActiveScene() @@ -100,10 +104,7 @@ Item { editViews[2].cameraLookAt = Qt.binding(function() {return cameraControl2._lookAtPoint;}); editViews[3].cameraLookAt = Qt.binding(function() {return cameraControl3._lookAtPoint;}); - activeSplit = 0; - selectionBoxCount = 0; - cameraControl0.forceActiveFocus(); editViewsChanged(); return true; } @@ -140,6 +141,11 @@ Item { } } showEditLight = !hasSceneLight; + + // Don't inherit camera angles from the previous scene + for (let i = 0; i < 4; ++i) + cameraControls[i].restoreDefaultState(); + storeCurrentToolStates(); } } else { @@ -315,19 +321,24 @@ Item { transformMode = EditView3D.TransformMode.Move; for (var i = 0; i < 4; ++i) { - if ("editCamState" in toolStates) - cameraControls[i].restoreCameraState(toolStates.editCamState); + let propId = "editCamState" + i; + if (propId in toolStates) + cameraControls[i].restoreCameraState(toolStates[propId]); else if (resetToDefault) cameraControls[i].restoreDefaultState(); } - if ("splitView" in toolStates) { + if ("splitView" in toolStates) splitView = toolStates.splitView; - activeSplit = 0; - } else if (resetToDefault) { + else if (resetToDefault) splitView = false; + + if (!splitView) + activeSplit = 0; + else if ("activeSplit" in toolStates) + activeSplit = toolStates.activeSplit; + else if (resetToDefault) activeSplit = 0; - } } function storeCurrentToolStates() @@ -344,6 +355,7 @@ Item { _generalHelper.storeToolState(sceneId, "selectionMode", selectionMode); _generalHelper.storeToolState(sceneId, "transformMode", transformMode); _generalHelper.storeToolState(sceneId, "splitView", splitView) + _generalHelper.storeToolState(sceneId, "activeSplit", activeSplit) for (var i = 0; i < 4; ++i) cameraControls[i].storeCameraState(0); From 3caac7bc674ec56c807794f719f1fbf975a9fcb9 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Tue, 24 Oct 2023 12:36:19 +0300 Subject: [PATCH 088/242] QmlDesigner: Update icon gizmo positions on split toggle Fixes: QDS-11021 Change-Id: I40472bd7c5766e5cf12782b760618371fddc3cc8 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Mahmoud Badri --- src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml b/src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml index 8a8efa1711b..91ae5014f84 100644 --- a/src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml +++ b/src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml @@ -68,7 +68,10 @@ Item { onShowParticleEmitterChanged: _generalHelper.storeToolState(sceneId, "showParticleEmitter", showParticleEmitter); onSelectionModeChanged: _generalHelper.storeToolState(sceneId, "selectionMode", selectionMode); onTransformModeChanged: _generalHelper.storeToolState(sceneId, "transformMode", transformMode); - onSplitViewChanged: _generalHelper.storeToolState(sceneId, "splitView", splitView) + onSplitViewChanged: { + _generalHelper.storeToolState(sceneId, "splitView", splitView); + _generalHelper.requestOverlayUpdate(); + } onActiveSplitChanged: { _generalHelper.storeToolState(sceneId, "activeSplit", activeSplit); cameraControls[activeSplit].forceActiveFocus(); From 7613710727f4d93d611e0f1f8b82b1bff94a1a4d Mon Sep 17 00:00:00 2001 From: Henning Gruendl Date: Thu, 12 Oct 2023 20:53:39 +0200 Subject: [PATCH 089/242] ADS: Enable minimumSizeHint from content * Fix minimumSizeHint handling * Set DockWidget to not force scroll area * Set background color on dock widgets * Add setting to activate/deactivate minimumSizeHint handling * Fix missing ADS changes from last merge * Fix ADS comments Task-number: QDS-10925 Change-Id: I7e8e0d2e7c3f9ee6636a2dc0796f6bd1fff3b2ee Reviewed-by: Thomas Hartmann Reviewed-by: Miikka Heikkinen Reviewed-by: --- .../themes/design-light.creatortheme | 5 +- share/qtcreator/themes/design.creatortheme | 4 +- .../advanceddockingsystem/dockareawidget.cpp | 118 ++++++-------- .../dockcontainerwidget.cpp | 148 +++++++++-------- .../dockcontainerwidget.h | 2 + src/libs/advanceddockingsystem/dockwidget.cpp | 27 +--- .../floatingdockcontainer.cpp | 10 +- .../floatingdockcontainer.h | 2 + .../connectioneditor/connectionview.cpp | 3 +- .../materialeditor/materialeditorview.cpp | 23 +-- .../propertyeditor/propertyeditorview.cpp | 2 +- .../stateseditor/stateseditorwidget.cpp | 3 +- src/plugins/qmldesigner/designmodewidget.cpp | 36 ++++- src/plugins/qmldesigner/designmodewidget.h | 4 + src/plugins/qmldesigner/settingspage.cpp | 153 +++++++++--------- .../studio/studioquickwidget.cpp | 2 + .../utils/designersettings.cpp | 1 + .../qmldesignerbase/utils/designersettings.h | 1 + 18 files changed, 263 insertions(+), 281 deletions(-) diff --git a/share/qtcreator/themes/design-light.creatortheme b/share/qtcreator/themes/design-light.creatortheme index 5c9a0c6277a..d77feb6d969 100644 --- a/share/qtcreator/themes/design-light.creatortheme +++ b/share/qtcreator/themes/design-light.creatortheme @@ -211,9 +211,8 @@ DStableHeaderText=ff00ff00 DSdockContainerBackground=ff323232 DSdockContainerSplitter=ff323232 -DSdockAreaBackground=ff262728 - -DSdockWidgetBackground=ff00ff00 +DSdockAreaBackground=ffeaeaea +DSdockWidgetBackground=ffeaeaea DSdockWidgetSplitter=ff595959 DSdockWidgetTitleBar=ffeaeaea diff --git a/share/qtcreator/themes/design.creatortheme b/share/qtcreator/themes/design.creatortheme index f2a12766b9d..fbf5241fa83 100644 --- a/share/qtcreator/themes/design.creatortheme +++ b/share/qtcreator/themes/design.creatortheme @@ -228,8 +228,8 @@ DStableHeaderText=ff00ff00 DSdockContainerBackground=ff242424 DSdockContainerSplitter=ff323232 -DSdockAreaBackground=ff262728 -DSdockWidgetBackground=ff00ff00 +DSdockAreaBackground=dawnGrey +DSdockWidgetBackground=dawnGrey DStitleBarText=ffdadada DStitleBarIcon=ffffffff DStitleBarButtonHover=40ffffff diff --git a/src/libs/advanceddockingsystem/dockareawidget.cpp b/src/libs/advanceddockingsystem/dockareawidget.cpp index c4c74a38c1d..cc263895af6 100644 --- a/src/libs/advanceddockingsystem/dockareawidget.cpp +++ b/src/libs/advanceddockingsystem/dockareawidget.cpp @@ -46,11 +46,11 @@ static bool isAutoHideFeatureEnabled() } /** - * Internal dock area layout mimics stack layout but only inserts the current - * widget into the internal QLayout object. - * \warning Only the current widget has a parent. All other widgets - * do not have a parent. That means, a widget that is in this layout may - * return nullptr for its parent() function if it is not the current widget. + * Internal dock area layout mimics stack layout but only inserts the current widget into the + * internal QLayout object. + * \warning Only the current widget has a parent. All other widgets do not have a parent. That + * means, a widget that is in this layout may return nullptr for its parent() function if it is + * not the current widget. */ class DockAreaLayout { @@ -74,8 +74,7 @@ public: int count() const { return m_widgets.count(); } /** - * Inserts the widget at the given index position into the internal widget - * list + * Inserts the widget at the given index position into the internal widget list */ void insertWidget(int index, QWidget *widget) { @@ -195,8 +194,7 @@ struct DockAreaWidgetPrivate DockManager *m_dockManager = nullptr; AutoHideDockContainer *m_autoHideDockContainer = nullptr; bool m_updateTitleBarButtons = false; - DockWidgetAreas m_allowedAreas = AllDockAreas; - QSize m_minSizeHint; + DockWidgetAreas m_allowedAreas = DefaultAllowedAreas; DockAreaWidget::DockAreaFlags m_flags{DockAreaWidget::DefaultFlags}; /** @@ -252,20 +250,6 @@ struct DockAreaWidgetPrivate * Updates the visibility of the close and detach button */ void updateTitleBarButtonVisibility(bool isTopLevel); - - /** - * Scans all contained dock widgets for the max. minimum size hint - */ - void updateMinimumSizeHint() - { - m_minSizeHint = QSize(); - for (int i = 0; i < m_contentsLayout->count(); ++i) { - auto widget = m_contentsLayout->widget(i); - m_minSizeHint.setHeight( - qMax(m_minSizeHint.height(), widget->minimumSizeHint().height())); - m_minSizeHint.setWidth(qMax(m_minSizeHint.width(), widget->minimumSizeHint().width())); - } - } }; // struct DockAreaWidgetPrivate @@ -391,26 +375,24 @@ void DockAreaWidget::insertDockWidget(int index, DockWidget *dockWidget, bool ac dockWidget->setDockArea(this); dockWidget->tabWidget()->setDockAreaWidget(this); auto tabWidget = dockWidget->tabWidget(); - // Inserting the tab will change the current index which in turn will - // make the tab widget visible in the slot + // Inserting the tab will change the current index which in turn will make the tab widget + // visible in the slot d->tabBar()->blockSignals(true); d->tabBar()->insertTab(index, tabWidget); d->tabBar()->blockSignals(false); tabWidget->setVisible(!dockWidget->isClosed()); d->m_titleBar->autoHideTitleLabel()->setText(dockWidget->windowTitle()); dockWidget->setProperty(g_indexProperty, index); - d->m_minSizeHint.setHeight( - qMax(d->m_minSizeHint.height(), dockWidget->minimumSizeHint().height())); - d->m_minSizeHint.setWidth(qMax(d->m_minSizeHint.width(), dockWidget->minimumSizeHint().width())); if (activate) { setCurrentIndex(index); - dockWidget->setClosedState( - false); // Set current index can show the widget without changing the close state, added to keep the close state consistent + // Set current index can show the widget without changing the close state, added to keep + // the close state consistent + dockWidget->setClosedState(false); } - // If this dock area is hidden, then we need to make it visible again - // by calling dockWidget->toggleViewInternal(true); - if (!this->isVisible() && d->m_contentsLayout->count() > 1 && !dockManager()->isRestoringState()) + // If this dock area is hidden, then we need to make it visible again by calling + // dockWidget->toggleViewInternal(true); + if (!isVisible() && d->m_contentsLayout->count() > 1 && !dockManager()->isRestoringState()) dockWidget->toggleViewInternal(true); d->updateTitleBarButtonStates(); @@ -423,8 +405,7 @@ void DockAreaWidget::removeDockWidget(DockWidget *dockWidget) if (!dockWidget) return; - // If this dock area is in a auto hide container, then we can delete - // the auto hide container now + // If this dock area is in a auto hide container, then we can delete the auto hide container now if (isAutoHide()) { autoHideDockContainer()->cleanupAndDelete(); return; @@ -445,7 +426,7 @@ void DockAreaWidget::removeDockWidget(DockWidget *dockWidget) } else if (d->m_contentsLayout->isEmpty() && dockContainerWidget->dockAreaCount() >= 1) { qCInfo(adsLog) << "Dock Area empty"; dockContainerWidget->removeDockArea(this); - this->deleteLater(); + deleteLater(); if (dockContainerWidget->dockAreaCount() == 0) { if (FloatingDockContainer *floatingDockContainer = dockContainerWidget->floatingWidget()) { @@ -454,15 +435,13 @@ void DockAreaWidget::removeDockWidget(DockWidget *dockWidget) } } } else if (dockWidget == current) { - // if contents layout is not empty but there are no more open dock - // widgets, then we need to hide the dock area because it does not - // contain any visible content + // If contents layout is not empty but there are no more open dock widgets, then we need to + // hide the dock area because it does not contain any visible content hideAreaWithNoVisibleContent(); } d->updateTitleBarButtonStates(); updateTitleBarVisibility(); - d->updateMinimumSizeHint(); auto topLevelDockWidget = dockContainerWidget->topLevelDockWidget(); if (topLevelDockWidget) topLevelDockWidget->emitTopLevelChanged(true); @@ -474,16 +453,16 @@ void DockAreaWidget::removeDockWidget(DockWidget *dockWidget) void DockAreaWidget::hideAreaWithNoVisibleContent() { - this->toggleView(false); + toggleView(false); // Hide empty parent splitters auto splitter = internal::findParent(this); internal::hideEmptyParentSplitters(splitter); - //Hide empty floating widget - DockContainerWidget *container = this->dockContainer(); + // Hide empty floating widget + DockContainerWidget *container = dockContainer(); if (!container->isFloating() - && DockManager::testConfigFlag(DockManager::HideSingleCentralWidgetTitleBar)) + && !DockManager::testConfigFlag(DockManager::HideSingleCentralWidgetTitleBar)) return; updateTitleBarVisibility(); @@ -532,8 +511,9 @@ void DockAreaWidget::internalSetCurrentDockWidget(DockWidget *dockWidget) return; setCurrentIndex(index); - dockWidget->setClosedState( - false); // Set current index can show the widget without changing the close state, added to keep the close state consistent + // Set current index can show the widget without changing the close state, added to keep + // the close state consistent + dockWidget->setClosedState(false); } void DockAreaWidget::setCurrentIndex(int index) @@ -601,7 +581,7 @@ QList DockAreaWidget::openedDockWidgets() const for (int i = 0; i < d->m_contentsLayout->count(); ++i) { DockWidget *currentDockWidget = dockWidget(i); if (!currentDockWidget->isClosed()) - dockWidgetList.append(dockWidget(i)); + dockWidgetList.append(currentDockWidget); } return dockWidgetList; } @@ -672,8 +652,8 @@ void DockAreaWidget::updateTitleBarVisibility() if (isAutoHideFeatureEnabled()) { auto tabBar = d->m_titleBar->tabBar(); tabBar->setVisible(!autoHide); // Never show tab bar when auto hidden - d->m_titleBar->autoHideTitleLabel()->setVisible( - autoHide); // Always show when auto hidden, never otherwise + // Always show when auto hidden, never otherwise + d->m_titleBar->autoHideTitleLabel()->setVisible(autoHide); updateTitleBarButtonVisibility(container->topLevelDockArea() == this); } } @@ -786,8 +766,8 @@ bool DockAreaWidget::restoreState(DockingStateReader &stateReader, if (dockWidget->autoHideDockContainer()) dockWidget->autoHideDockContainer()->cleanupAndDelete(); - // We hide the DockArea here to prevent the short display (the flashing) - // of the dock areas during application startup + // We hide the DockArea here to prevent the short display (the flashing) of the dock + // areas during application startup dockArea->hide(); dockArea->addDockWidget(dockWidget); dockWidget->setToggleViewActionChecked(!closed); @@ -919,10 +899,10 @@ QAbstractButton *DockAreaWidget::titleBarButton(eTitleBarButton which) const void DockAreaWidget::closeArea() { - // If there is only one single dock widget and this widget has the - // DeleteOnClose feature or CustomCloseHandling, then we delete the dock widget now; - // in the case of CustomCloseHandling, the CDockWidget class will emit its - // closeRequested signal and not actually delete unless the signal is handled in a way that deletes it + // If there is only one single dock widget and this widget has the DeleteOnClose feature or + // CustomCloseHandling, then we delete the dock widget now; in the case of CustomCloseHandling, + // the DockWidget class will emit its closeRequested signal and not actually delete unless + // the signal is handled in a way that deletes it auto openDockWidgets = openedDockWidgets(); if (openDockWidgets.count() == 1 && (openDockWidgets[0]->features().testFlag(DockWidget::DockWidgetDeleteOnClose) @@ -983,27 +963,23 @@ SideBarLocation DockAreaWidget::calculateSideTabBarArea() const int distance = qAbs(contentRect.topLeft().y() - dockAreaRect.topLeft().y()); borderDistance[SideBarLocation::SideBarTop] = (distance < minBorderDistance) ? 0 : distance; - if (!borderDistance[SideBarLocation::SideBarTop]) { + if (!borderDistance[SideBarLocation::SideBarTop]) borders |= BorderTop; - } distance = qAbs(contentRect.bottomRight().y() - dockAreaRect.bottomRight().y()); borderDistance[SideBarLocation::SideBarBottom] = (distance < minBorderDistance) ? 0 : distance; - if (!borderDistance[SideBarLocation::SideBarBottom]) { + if (!borderDistance[SideBarLocation::SideBarBottom]) borders |= BorderBottom; - } distance = qAbs(contentRect.topLeft().x() - dockAreaRect.topLeft().x()); borderDistance[SideBarLocation::SideBarLeft] = (distance < minBorderDistance) ? 0 : distance; - if (!borderDistance[SideBarLocation::SideBarLeft]) { + if (!borderDistance[SideBarLocation::SideBarLeft]) borders |= BorderLeft; - } distance = qAbs(contentRect.bottomRight().x() - dockAreaRect.bottomRight().x()); borderDistance[SideBarLocation::SideBarRight] = (distance < minBorderDistance) ? 0 : distance; - if (!borderDistance[SideBarLocation::SideBarRight]) { + if (!borderDistance[SideBarLocation::SideBarRight]) borders |= BorderRight; - } auto sideTab = SideBarLocation::SideBarRight; switch (borders) { @@ -1145,13 +1121,19 @@ bool DockAreaWidget::containsCentralWidget() const QSize DockAreaWidget::minimumSizeHint() const { - if (!d->m_minSizeHint.isValid()) - return Super::minimumSizeHint(); + QSize s(0, 0); + + if (d->m_contentsLayout) { + for (int i = 0; i < d->m_contentsLayout->count(); ++i) { + auto widget = d->m_contentsLayout->widget(i); + s = s.expandedTo(widget->minimumSizeHint()); + } + } if (d->m_titleBar->isVisible()) - return d->m_minSizeHint + QSize(0, d->m_titleBar->minimumSizeHint().height()); - else - return d->m_minSizeHint; + s += QSize(0, d->m_titleBar->minimumSizeHint().height()); + + return s; } void DockAreaWidget::onDockWidgetFeaturesChanged() diff --git a/src/libs/advanceddockingsystem/dockcontainerwidget.cpp b/src/libs/advanceddockingsystem/dockcontainerwidget.cpp index 0b2a03d1348..c57e277eddb 100644 --- a/src/libs/advanceddockingsystem/dockcontainerwidget.cpp +++ b/src/libs/advanceddockingsystem/dockcontainerwidget.cpp @@ -227,8 +227,7 @@ public: eDropMode getDropMode(const QPoint &targetPosition); /** - * Initializes the visible dock area count variable if it is not initialized - * yet + * Initializes the visible dock area count variable if it is not initialized yet */ void initVisibleDockAreaCount() { @@ -245,15 +244,14 @@ public: */ int visibleDockAreaCount() { - // Lazy initialization - we initialize the m_visibleDockAreaCount variable - // on first use + // Lazy initialization - we initialize the m_visibleDockAreaCount variable on first use initVisibleDockAreaCount(); return m_visibleDockAreaCount; } /** - * The visible dock area count changes, if dock areas are remove, added or - * when its view is toggled + * The visible dock area count changes, if dock areas are removed, added or when its view + * is toggled */ void onVisibleDockAreaCountChanged(); @@ -274,15 +272,14 @@ public: */ DockSplitter *createSplitter(Qt::Orientation orientation, QWidget *parent = nullptr) { - auto *splitter = new DockSplitter(orientation, parent); + auto splitter = new DockSplitter(orientation, parent); splitter->setOpaqueResize(DockManager::testConfigFlag(DockManager::OpaqueSplitterResize)); splitter->setChildrenCollapsible(false); return splitter; } /** - * Ensures equal distribution of the sizes of a splitter if an dock widget - * is inserted from code + * Ensures equal distribution of the sizes of a splitter if a dock widget is inserted from code */ void adjustSplitterSizesOnInsertion(QSplitter *splitter, qreal lastRatio = 1.0) { @@ -299,15 +296,14 @@ public: } /** - * This function forces the dock container widget to update handles of splitters - * based if a central widget exists. + * This function forces the dock container widget to update handles of splitters based if + * a central widget exists. */ void updateSplitterHandles(QSplitter *splitter); /** - * If no central widget exists, the widgets resize with the container. - * If a central widget exists, the widgets surrounding the central widget - * do not resize its height or width. + * If no central widget exists, the widgets resize with the container. If a central widget + * exists, the widgets surrounding the central widget do not resize its height or width. */ bool widgetResizesWithContainer(QWidget *widget); @@ -388,12 +384,12 @@ void DockContainerWidgetPrivate::dropIntoContainer(FloatingDockContainer *floati auto newDockAreas = floatingDockContainer->findChildren(QString(), Qt::FindChildrenRecursively); - auto *splitter = m_rootSplitter; + auto splitter = m_rootSplitter; if (m_dockAreas.count() <= 1) { splitter->setOrientation(insertParam.orientation()); } else if (splitter->orientation() != insertParam.orientation()) { - auto *newSplitter = createSplitter(insertParam.orientation()); + auto newSplitter = createSplitter(insertParam.orientation()); QLayoutItem *layoutItem = m_layout->replaceWidget(splitter, newSplitter); newSplitter->addWidget(splitter); updateSplitterHandles(newSplitter); @@ -419,9 +415,8 @@ void DockContainerWidgetPrivate::dropIntoContainer(FloatingDockContainer *floati m_rootSplitter = splitter; addDockAreasToList(newDockAreas); - // If we dropped the floating widget into the main dock container that does - // not contain any dock widgets, then splitter is invisible and we need to - // show it to display the docked widgets + // If we dropped the floating widget into the main dock container that does not contain any dock + // widgets, then splitter is invisible and we need to show it to display the docked widgets if (!splitter->isVisible()) splitter->show(); @@ -467,7 +462,6 @@ void DockContainerWidgetPrivate::dropIntoCenterOfSection(FloatingDockContainer * } targetArea->setCurrentIndex(newCurrentIndex + tabIndex); targetArea->updateTitleBarVisibility(); - return; } void DockContainerWidgetPrivate::dropIntoSection(FloatingDockContainer *floatingWidget, @@ -489,7 +483,7 @@ void DockContainerWidgetPrivate::dropIntoSection(FloatingDockContainer *floating QSplitter *targetAreaSplitter = internal::findParent(targetArea); if (!targetAreaSplitter) { - QSplitter *splitter = createSplitter(insertParam.orientation()); + auto splitter = createSplitter(insertParam.orientation()); m_layout->replaceWidget(targetArea, splitter); splitter->addWidget(targetArea); targetAreaSplitter = splitter; @@ -523,7 +517,7 @@ void DockContainerWidgetPrivate::dropIntoSection(FloatingDockContainer *floating targetAreaSplitter->setSizes(sizes); } } else { - QSplitter *newSplitter = createSplitter(insertParam.orientation()); + auto newSplitter = createSplitter(insertParam.orientation()); int targetAreaSize = (insertParam.orientation() == Qt::Horizontal) ? targetArea->width() : targetArea->height(); bool adjustSplitterSizes = true; @@ -539,8 +533,7 @@ void DockContainerWidgetPrivate::dropIntoSection(FloatingDockContainer *floating } } - // Save the sizes before insertion and restore it later to prevent - // shrinking of existing area + // Save the sizes before insertion and restore it later to prevent shrinking of existing area auto sizes = targetAreaSplitter->sizes(); insertWidgetIntoSplitter(newSplitter, targetArea, !insertParam.append()); updateSplitterHandles(newSplitter); @@ -585,7 +578,7 @@ void DockContainerWidgetPrivate::moveToNewSection(QWidget *widget, } auto insertParam = internal::dockAreaInsertParameters(area); - QSplitter *targetAreaSplitter = internal::findParent(targetArea); + auto targetAreaSplitter = internal::findParent(targetArea); const int areaIndex = targetAreaSplitter->indexOf(targetArea); auto sizes = targetAreaSplitter->sizes(); if (targetAreaSplitter->orientation() == insertParam.orientation()) { @@ -601,7 +594,7 @@ void DockContainerWidgetPrivate::moveToNewSection(QWidget *widget, const int targetAreaSize = (insertParam.orientation() == Qt::Horizontal) ? targetArea->width() : targetArea->height(); - QSplitter *newSplitter = createSplitter(insertParam.orientation()); + auto newSplitter = createSplitter(insertParam.orientation()); newSplitter->addWidget(targetArea); insertWidgetIntoSplitter(newSplitter, newDockArea, insertParam.append()); updateSplitterHandles(newSplitter); @@ -629,10 +622,9 @@ void DockContainerWidgetPrivate::moveToContainer(QWidget *widget, DockWidgetArea newDockArea->addDockWidget(droppedDockWidget); } else { - // We check, if we insert the dropped widget into the same place that - // it already has and do nothing, if it is the same place. It would - // also work without this check, but it looks nicer with the check - // because there will be no layout updates + // We check, if we insert the dropped widget into the same place that it already has and do + // nothing, if it is the same place. It would also work without this check, but it looks + // nicer with the check because there will be no layout updates. auto splitter = internal::findParent(droppedDockArea); auto insertParam = internal::dockAreaInsertParameters(area); if (splitter == m_rootSplitter && insertParam.orientation() == splitter->orientation()) { @@ -679,7 +671,6 @@ void DockContainerWidgetPrivate::moveIntoCenterOfSection(QWidget *widget, } targetArea->updateTitleBarVisibility(); - return; } void DockContainerWidgetPrivate::moveToAutoHideSideBar(QWidget *widget, @@ -740,16 +731,16 @@ void DockContainerWidgetPrivate::addDockAreasToList(const QListtitleBarButton(TitleBarButtonUndock)->setVisible(true); dockArea->titleBarButton(TitleBarButtonClose)->setVisible(true); } - // We need to ensure, that the dock area title bar is visible. The title bar - // is invisible, if the dock are is a single dock area in a floating widget. + // We need to ensure, that the dock area title bar is visible. The title bar is invisible, + // if the dock area is a single dock area in a floating widget. if (1 == countBefore) m_dockAreas.at(0)->updateTitleBarVisibility(); @@ -892,6 +883,9 @@ bool DockContainerWidgetPrivate::restoreSplitter(DockingStateReader &stateReader visible |= childNode->isVisibleTo(splitter); } + if (!testing) + updateSplitterHandles(splitter); + if (sizes.count() != widgetCount) return false; @@ -995,7 +989,7 @@ void DockContainerWidgetPrivate::addDockArea(DockAreaWidget *newDockArea, DockWi if (m_dockAreas.count() <= 1) m_rootSplitter->setOrientation(insertParam.orientation()); - auto *splitter = m_rootSplitter; + auto splitter = m_rootSplitter; if (splitter->orientation() == insertParam.orientation()) { insertWidgetIntoSplitter(splitter, newDockArea, insertParam.append()); updateSplitterHandles(splitter); @@ -1003,7 +997,7 @@ void DockContainerWidgetPrivate::addDockArea(DockAreaWidget *newDockArea, DockWi splitter->show(); } else { - auto *newSplitter = createSplitter(insertParam.orientation()); + auto newSplitter = createSplitter(insertParam.orientation()); if (insertParam.append()) { QLayoutItem *layoutItem = m_layout->replaceWidget(splitter, newSplitter); newSplitter->addWidget(splitter); @@ -1083,18 +1077,19 @@ DockAreaWidget *DockContainerWidgetPrivate::addDockWidgetToDockArea(DockWidgetAr newDockArea->addDockWidget(dockWidget); auto insertParam = internal::dockAreaInsertParameters(area); - QSplitter *targetAreaSplitter = internal::findParent(targetDockArea); + auto targetAreaSplitter = internal::findParent(targetDockArea); int targetIndex = targetAreaSplitter->indexOf(targetDockArea); if (targetAreaSplitter->orientation() == insertParam.orientation()) { qCInfo(adsLog) << "TargetAreaSplitter->orientation() == InsertParam.orientation()"; targetAreaSplitter->insertWidget(targetIndex + insertParam.insertOffset(), newDockArea); + updateSplitterHandles(targetAreaSplitter); // do nothing, if flag is not enabled if (DockManager::testConfigFlag(DockManager::EqualSplitOnInsertion)) adjustSplitterSizesOnInsertion(targetAreaSplitter); } else { qCInfo(adsLog) << "TargetAreaSplitter->orientation() != InsertParam.orientation()"; auto targetAreaSizes = targetAreaSplitter->sizes(); - QSplitter *newSplitter = createSplitter(insertParam.orientation()); + auto newSplitter = createSplitter(insertParam.orientation()); newSplitter->addWidget(targetDockArea); insertWidgetIntoSplitter(newSplitter, newDockArea, insertParam.append()); updateSplitterHandles(newSplitter); @@ -1143,12 +1138,17 @@ DockContainerWidget::~DockContainerWidget() delete d; } +QSize DockContainerWidget::minimumSizeHint() const +{ + return d->m_layout->minimumSize(); +} + DockAreaWidget *DockContainerWidget::addDockWidget(DockWidgetArea area, DockWidget *dockWidget, DockAreaWidget *dockAreaWidget, int index) { - auto currentTopLevelDockWIdget = topLevelDockWidget(); + auto currentTopLevelDockWidget = topLevelDockWidget(); DockAreaWidget *oldDockArea = dockWidget->dockAreaWidget(); if (oldDockArea) oldDockArea->removeDockWidget(dockWidget); @@ -1160,12 +1160,12 @@ DockAreaWidget *DockContainerWidget::addDockWidget(DockWidgetArea area, else dockArea = d->addDockWidgetToContainer(area, dockWidget); - if (currentTopLevelDockWIdget) { + if (currentTopLevelDockWidget) { auto newTopLevelDockWidget = topLevelDockWidget(); // If the container contained only one visible dock widget, we need to emit a top level // event for this widget because it is not the one and only visible docked widget anymore. if (!newTopLevelDockWidget) - DockWidget::emitTopLevelEventForWidget(currentTopLevelDockWIdget, false); + DockWidget::emitTopLevelEventForWidget(currentTopLevelDockWidget, false); } return dockArea; @@ -1177,7 +1177,7 @@ AutoHideDockContainer *DockContainerWidget::createAndSetupAutoHideContainer(Side { if (!DockManager::testAutoHideConfigFlag(DockManager::AutoHideFeatureEnabled)) { Q_ASSERT_X(false, - "CDockContainerWidget::createAndInitializeDockWidgetOverlayContainer", + "DockContainerWidget::createAndInitializeDockWidgetOverlayContainer", "Requested area does not exist in config"); return nullptr; } @@ -1203,7 +1203,7 @@ unsigned int DockContainerWidget::zOrderIndex() const bool DockContainerWidget::isInFrontOf(DockContainerWidget *other) const { - return this->zOrderIndex() > other->zOrderIndex(); + return zOrderIndex() > other->zOrderIndex(); } bool DockContainerWidget::event(QEvent *event) @@ -1243,10 +1243,10 @@ void DockContainerWidget::removeDockArea(DockAreaWidget *area) area->disconnect(this); d->m_dockAreas.removeAll(area); - DockSplitter *splitter = internal::findParent(area); + auto splitter = internal::findParent(area); - // Remove area from parent splitter and recursively hide tree of parent - // splitters if it has no visible content + // Remove area from parent splitter and recursively hide tree of parent splitters if it has + // no visible content area->setParent(nullptr); internal::hideEmptyParentSplitters(splitter); @@ -1262,8 +1262,7 @@ void DockContainerWidget::removeDockArea(DockAreaWidget *area) return; } - // If this is the RootSplitter we need to remove empty splitters to - // avoid too many empty splitters + // If this is the RootSplitter we need to remove empty splitters to avoid too many empty splitters if (splitter == d->m_rootSplitter) { qCInfo(adsLog) << "Removed from RootSplitter"; // If splitter is empty, we are finished @@ -1275,9 +1274,8 @@ void DockContainerWidget::removeDockArea(DockAreaWidget *area) } QWidget *widget = splitter->widget(0); - auto *childSplitter = qobject_cast(widget); - // If the one and only content widget of the splitter is not a splitter - // then we are finished + auto childSplitter = qobject_cast(widget); + // If the one and only content widget of the splitter is not a splitter then we are finished if (!childSplitter) { updateSplitterHandles(splitter); emitAndExit(); @@ -1292,7 +1290,7 @@ void DockContainerWidget::removeDockArea(DockAreaWidget *area) qCInfo(adsLog) << "RootSplitter replaced by child splitter"; } else if (splitter->count() == 1) { qCInfo(adsLog) << "Replacing splitter with content"; - auto *parentSplitter = internal::findParent(splitter); + auto parentSplitter = internal::findParent(splitter); auto sizes = parentSplitter->sizes(); QWidget *widget = splitter->widget(0); widget->setParent(this); @@ -1312,8 +1310,8 @@ void DockContainerWidget::emitAndExit() const { DockWidget *topLevelWidget = topLevelDockWidget(); - // Updated the title bar visibility of the dock widget if there is only - // one single visible dock widget + // Updated the title bar visibility of the dock widget if there is only one single visible + // dock widget DockWidget::emitTopLevelEventForWidget(topLevelWidget, true); dumpLayout(); d->emitDockAreasRemoved(); @@ -1407,12 +1405,11 @@ void DockContainerWidget::dropFloatingWidget(FloatingDockContainer *floatingWidg // Fix https://github.com/githubuser0xFFFF/Qt-Advanced-Docking-System/issues/351 floatingWidget->hideAndDeleteLater(); - // If we dropped a floating widget with only one single dock widget, then we - // drop a top level widget that changes from floating to docked now + // If we dropped a floating widget with only one single dock widget, then we drop a top + // level widget that changes from floating to docked now DockWidget::emitTopLevelEventForWidget(singleDroppedDockWidget, false); - // If there was a top level widget before the drop, then it is not top - // level widget anymore + // If there was a top level widget before the drop, then it is not top level widget anymore DockWidget::emitTopLevelEventForWidget(singleDockWidget, false); } @@ -1436,8 +1433,7 @@ void DockContainerWidget::dropWidget(QWidget *widget, else d->moveToContainer(widget, dropArea); - // If there was a top level widget before the drop, then it is not top - // level widget anymore + // If there was a top level widget before the drop, then it is not top level widget anymore DockWidget::emitTopLevelEventForWidget(singleDockWidget, false); window()->activateWindow(); @@ -1458,7 +1454,7 @@ QList DockContainerWidget::openedDockAreas() const QList DockContainerWidget::openedDockWidgets() const { QList dockWidgetList; - for (auto dockArea : d->m_dockAreas) { + for (auto dockArea : std::as_const(d->m_dockAreas)) { if (!dockArea->isHidden()) dockWidgetList.append(dockArea->openedDockWidgets()); } @@ -1467,7 +1463,7 @@ QList DockContainerWidget::openedDockWidgets() const bool DockContainerWidget::hasOpenDockAreas() const { - for (auto dockArea : d->m_dockAreas) { + for (auto dockArea : std::as_const(d->m_dockAreas)) { if (!dockArea->isHidden()) return true; } @@ -1531,13 +1527,13 @@ bool DockContainerWidget::restoreState(DockingStateReader &stateReader, bool tes if (testing) return true; - // If the root splitter is empty, restoreChildNodes returns a nullptr - // and we need to create a new empty root splitter + // If the root splitter is empty, restoreChildNodes returns a nullptr and we need to create + // a new empty root splitter if (!newRootSplitter) newRootSplitter = d->createSplitter(Qt::Horizontal); d->m_layout->replaceWidget(d->m_rootSplitter, newRootSplitter); - QSplitter *oldRoot = d->m_rootSplitter; + auto oldRoot = d->m_rootSplitter; d->m_rootSplitter = qobject_cast(newRootSplitter); oldRoot->deleteLater(); @@ -1653,7 +1649,7 @@ void DockContainerWidget::updateSplitterHandles(QSplitter *splitter) void DockContainerWidget::registerAutoHideWidget(AutoHideDockContainer *autohideWidget) { d->m_autoHideWidgets.append(autohideWidget); - Q_EMIT autoHideWidgetCreated(autohideWidget); + emit autoHideWidgetCreated(autohideWidget); qCInfo(adsLog) << "d->AutoHideWidgets.count() " << d->m_autoHideWidgets.count(); } @@ -1699,14 +1695,6 @@ AutoHideSideBar *DockContainerWidget::autoHideSideBar(SideBarLocation area) cons } QRect DockContainerWidget::contentRect() const -{ - if (!d->m_rootSplitter) - return QRect(); - - return d->m_rootSplitter->geometry(); -} - -QRect DockContainerWidget::contentRectGlobal() const { if (!d->m_rootSplitter) return QRect(); @@ -1724,6 +1712,14 @@ QRect DockContainerWidget::contentRectGlobal() const } } +QRect DockContainerWidget::contentRectGlobal() const +{ + if (!d->m_rootSplitter) + return QRect(); + + return internal::globalGeometry(d->m_rootSplitter); +} + DockManager *DockContainerWidget::dockManager() const { return d->m_dockManager; diff --git a/src/libs/advanceddockingsystem/dockcontainerwidget.h b/src/libs/advanceddockingsystem/dockcontainerwidget.h index f2c6085a1d3..77205707bf6 100644 --- a/src/libs/advanceddockingsystem/dockcontainerwidget.h +++ b/src/libs/advanceddockingsystem/dockcontainerwidget.h @@ -193,6 +193,8 @@ public: */ ~DockContainerWidget() override; + QSize minimumSizeHint() const override; + /** * Adds dockwidget into the given area. * If DockAreaWidget is not null, then the area parameter indicates the area diff --git a/src/libs/advanceddockingsystem/dockwidget.cpp b/src/libs/advanceddockingsystem/dockwidget.cpp index f2e0e72249f..6c89c92c66f 100644 --- a/src/libs/advanceddockingsystem/dockwidget.cpp +++ b/src/libs/advanceddockingsystem/dockwidget.cpp @@ -557,9 +557,8 @@ void DockWidget::toggleViewInternal(bool open) if (open && topLevelDockWidgetBefore) DockWidget::emitTopLevelEventForWidget(topLevelDockWidgetBefore, false); - // Here we need to call the dockContainer() function again, because if - // this dock widget was unassigned before the call to showDockWidget() then - // it has a dock container now + // Here we need to call the dockContainer() function again, because if this dock widget was + // unassigned before the call to showDockWidget() then it has a dock container now const DockContainerWidget *const dockContainerWidget = dockContainer(); DockWidget *topLevelDockWidgetAfter = dockContainerWidget ? dockContainerWidget->topLevelDockWidget() @@ -614,18 +613,17 @@ bool DockWidget::event(QEvent *event) case QEvent::WindowTitleChange: { const auto title = windowTitle(); - if (d->m_tabWidget) { + if (d->m_tabWidget) d->m_tabWidget->setText(title); - } + if (d->m_sideTabWidget) d->m_sideTabWidget->setText(title); - if (d->m_toggleViewAction) { + if (d->m_toggleViewAction) d->m_toggleViewAction->setText(title); - } - if (d->m_dockArea) { + + if (d->m_dockArea) d->m_dockArea->markTitleBarMenuOutdated(); // update tabs menu - } auto floatingWidget = floatingDockContainer(); if (floatingWidget) @@ -771,14 +769,6 @@ QSize DockWidget::minimumSizeHint() const if (!d->m_widget) return QSize(60, 40); - // TODO - DockContainerWidget *container = this->dockContainer(); - if (!container || container->isFloating()) { - const QSize sh = d->m_widget->minimumSizeHint(); - const QSize s = d->m_widget->minimumSize(); - return {std::max(s.width(), sh.width()), std::max(s.height(), sh.height())}; - } - switch (d->m_minimumSizeHintMode) { case MinimumSizeHintFromDockWidget: return QSize(60, 40); @@ -837,8 +827,7 @@ bool DockWidget::closeDockWidgetInternal(bool forceClose) return false; if (features().testFlag(DockWidget::DockWidgetDeleteOnClose)) { - // If the dock widget is floating, then we check if we also need to - // delete the floating widget + // If the dock widget is floating, then check if we also need to delete the floating widget if (isFloating()) { FloatingDockContainer *floatingWidget = internal::findParent( this); diff --git a/src/libs/advanceddockingsystem/floatingdockcontainer.cpp b/src/libs/advanceddockingsystem/floatingdockcontainer.cpp index 83f203c5e57..be645ebbcd3 100644 --- a/src/libs/advanceddockingsystem/floatingdockcontainer.cpp +++ b/src/libs/advanceddockingsystem/floatingdockcontainer.cpp @@ -496,8 +496,8 @@ void FloatingDockContainerPrivate::updateDropOverlays(const QPoint &globalPositi if (m_dockContainer == containerWidget) continue; - QPoint mappedPos = containerWidget->mapFromGlobal(globalPosition); - if (containerWidget->rect().contains(mappedPos)) { + QPoint mappedPosition = containerWidget->mapFromGlobal(globalPosition); + if (containerWidget->rect().contains(mappedPosition)) { if (!topContainer || containerWidget->isInFrontOf(topContainer)) topContainer = containerWidget; } @@ -580,7 +580,6 @@ FloatingDockContainer::FloatingDockContainer(DockManager *dockManager) // on linux are missing from floating dock widgets. setWindowFlags(Qt::Window | Qt::WindowMinMaxButtonsHint | Qt::FramelessWindowHint | Qt::Tool); QDockWidget::setWidget(d->m_dockContainer); - //QDockWidget::setFloating(true); QDockWidget::setFeatures(QDockWidget::DockWidgetClosable | QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetFloatable); @@ -639,6 +638,11 @@ FloatingDockContainer::~FloatingDockContainer() delete d; } +QSize FloatingDockContainer::minimumSizeHint() const +{ + return d->m_dockContainer->minimumSizeHint(); +} + DockContainerWidget *FloatingDockContainer::dockContainer() const { return d->m_dockContainer; diff --git a/src/libs/advanceddockingsystem/floatingdockcontainer.h b/src/libs/advanceddockingsystem/floatingdockcontainer.h index da2fc42138a..02fd003f217 100644 --- a/src/libs/advanceddockingsystem/floatingdockcontainer.h +++ b/src/libs/advanceddockingsystem/floatingdockcontainer.h @@ -187,6 +187,8 @@ public: */ ~FloatingDockContainer() override; + virtual QSize minimumSizeHint() const override; + /** * Access function for the internal dock container */ diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionview.cpp b/src/plugins/qmldesigner/components/connectioneditor/connectionview.cpp index b96afd69665..75046f92bee 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectionview.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/connectionview.cpp @@ -98,8 +98,7 @@ public: Theme::setupTheme(engine()); - setMinimumWidth(195); - setMinimumHeight(195); + setMinimumSize(QSize(195, 195)); // init the first load of the QML UI elements reloadQmlSource(); diff --git a/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp b/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp index 6d17a350f4f..3c7f0e8af09 100644 --- a/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp +++ b/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp @@ -39,20 +39,6 @@ #include #include -namespace { -QSize maxSize(const std::initializer_list &sizeList) -{ - QSize result; - for (const QSize &size : sizeList) { - if (size.width() > result.width()) - result.setWidth(size.width()); - if (size.height() > result.height()) - result.setHeight(size.height()); - } - return result; -} -} - namespace QmlDesigner { MaterialEditorView::MaterialEditorView(ExternalDependenciesInterface &externalDependencies) @@ -612,14 +598,7 @@ void MaterialEditorView::setupQmlBackend() initPreviewData(); m_stackedWidget->setCurrentWidget(m_qmlBackEnd->widget()); - if (m_qmlBackEnd->widget()) { - m_stackedWidget->setMinimumSize(maxSize({m_qmlBackEnd->widget()->sizeHint(), - m_qmlBackEnd->widget()->initialSize(), - m_qmlBackEnd->widget()->minimumSizeHint(), - m_qmlBackEnd->widget()->minimumSize()})); - } else { - m_stackedWidget->setMinimumSize({400, 300}); - } + m_stackedWidget->setMinimumSize({400, 300}); } void MaterialEditorView::commitVariantValueToModel(const PropertyName &propertyName, const QVariant &value) diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp index 1fba74f2178..48be900851e 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp @@ -82,7 +82,7 @@ PropertyEditorView::PropertyEditorView(AsynchronousImageCache &imageCache, m_stackedWidget->setStyleSheet(Theme::replaceCssColors( QString::fromUtf8(Utils::FileReader::fetchQrc(":/qmldesigner/stylesheet.css")))); - m_stackedWidget->setMinimumWidth(340); + m_stackedWidget->setMinimumSize(340, 340); m_stackedWidget->move(0, 0); connect(m_stackedWidget, &PropertyEditorWidget::resized, this, &PropertyEditorView::updateSize); diff --git a/src/plugins/qmldesigner/components/stateseditor/stateseditorwidget.cpp b/src/plugins/qmldesigner/components/stateseditor/stateseditorwidget.cpp index d0d5e88f5c4..08c36778691 100644 --- a/src/plugins/qmldesigner/components/stateseditor/stateseditorwidget.cpp +++ b/src/plugins/qmldesigner/components/stateseditor/stateseditorwidget.cpp @@ -91,8 +91,7 @@ StatesEditorWidget::StatesEditorWidget(StatesEditorView *statesEditorView, Theme::setupTheme(engine()); setWindowTitle(tr("States", "Title of Editor widget")); - setMinimumWidth(195); - setMinimumHeight(195); + setMinimumSize(QSize(195, 195)); // init the first load of the QML UI elements reloadQmlSource(); diff --git a/src/plugins/qmldesigner/designmodewidget.cpp b/src/plugins/qmldesigner/designmodewidget.cpp index fe9fafde5c4..492402fe020 100644 --- a/src/plugins/qmldesigner/designmodewidget.cpp +++ b/src/plugins/qmldesigner/designmodewidget.cpp @@ -186,6 +186,10 @@ void DesignModeWidget::setup() //ADS::DockManager::setAutoHideConfigFlags(ADS::DockManager::DefaultAutoHideConfig); + auto designerSettings = DesignerSettings(settings); + if (designerSettings.value(DesignerSettingsKey::ENABLE_DOCKWIDGET_CONTENT_MIN_SIZE).toBool()) + m_minimumSizeHintMode = ADS::DockWidget::MinimumSizeHintFromContentMinimumSize; + m_dockManager = new ADS::DockManager(this); m_dockManager->setSettings(settings); m_dockManager->setWorkspacePresetsPath( @@ -306,8 +310,9 @@ void DesignModeWidget::setup() // Create DockWidget ADS::DockWidget *dockWidget = new ADS::DockWidget(uniqueId); - dockWidget->setWidget(navigationView.widget); + dockWidget->setWidget(navigationView.widget, ADS::DockWidget::ForceNoScrollArea); dockWidget->setWindowTitle(title); + dockWidget->setMinimumSizeHintMode(m_minimumSizeHintMode); m_dockManager->addDockWidget(ADS::NoDockWidgetArea, dockWidget); // Set unique id as object name @@ -325,8 +330,9 @@ void DesignModeWidget::setup() for (const WidgetInfo &widgetInfo : viewManager().widgetInfos()) { // Create DockWidget ADS::DockWidget *dockWidget = new ADS::DockWidget(widgetInfo.uniqueId); - dockWidget->setWidget(widgetInfo.widget); + dockWidget->setWidget(widgetInfo.widget, ADS::DockWidget::ForceNoScrollArea); dockWidget->setWindowTitle(widgetInfo.tabName); + dockWidget->setMinimumSizeHintMode(m_minimumSizeHintMode); m_dockManager->addDockWidget(ADS::NoDockWidgetArea, dockWidget); // Add to view widgets @@ -349,8 +355,9 @@ void DesignModeWidget::setup() const QString uniqueId = "OutputPane"; auto outputPanePlaceholder = new Core::OutputPanePlaceHolder(Core::Constants::MODE_DESIGN); m_outputPaneDockWidget = new ADS::DockWidget(uniqueId); - m_outputPaneDockWidget->setWidget(outputPanePlaceholder); + m_outputPaneDockWidget->setWidget(outputPanePlaceholder, ADS::DockWidget::ForceNoScrollArea); m_outputPaneDockWidget->setWindowTitle(tr("Output")); + m_outputPaneDockWidget->setMinimumSizeHintMode(m_minimumSizeHintMode); m_dockManager->addDockWidget(ADS::NoDockWidgetArea, m_outputPaneDockWidget); // Set unique id as object name @@ -541,6 +548,29 @@ GlobalAnnotationEditor &DesignModeWidget::globalAnnotationEditor() return m_globalAnnotationEditor; } +void DesignModeWidget::setMinimumSizeHintFromContentMinimumSize(bool value) +{ + // This is the default mode + ADS::DockWidget::eMinimumSizeHintMode newMode = ADS::DockWidget::MinimumSizeHintFromDockWidget; + + if (value) + newMode = ADS::DockWidget::MinimumSizeHintFromContentMinimumSize; + + if (newMode == m_minimumSizeHintMode) + return; + + m_minimumSizeHintMode = newMode; + + const auto &dockWidgets = m_dockManager->dockWidgetsMap(); + for (auto dockWidget : dockWidgets) + dockWidget->setMinimumSizeHintMode(m_minimumSizeHintMode); + + const auto &dockContainers = m_dockManager->dockContainers(); + // This still needs to be updated manually to show changes immediately after option was toggled + for (auto dockContainer : dockContainers) + dockContainer->layout()->update(); +} + void DesignModeWidget::dragEnterEvent(QDragEnterEvent *event) { event->accept(); diff --git a/src/plugins/qmldesigner/designmodewidget.h b/src/plugins/qmldesigner/designmodewidget.h index 1dfa79a0b3d..432549e694d 100644 --- a/src/plugins/qmldesigner/designmodewidget.h +++ b/src/plugins/qmldesigner/designmodewidget.h @@ -74,6 +74,8 @@ public: GlobalAnnotationEditor &globalAnnotationEditor(); + void setMinimumSizeHintFromContentMinimumSize(bool value); + signals: void navigationHistoryChanged(); void initialized(); @@ -113,6 +115,8 @@ private: bool m_canGoForward = false; bool m_canGoBack = false; + + ADS::DockWidget::eMinimumSizeHintMode m_minimumSizeHintMode = ADS::DockWidget::MinimumSizeHintFromDockWidget; }; } // namespace Internal diff --git a/src/plugins/qmldesigner/settingspage.cpp b/src/plugins/qmldesigner/settingspage.cpp index 473e1a341a5..4a1e855964e 100644 --- a/src/plugins/qmldesigner/settingspage.cpp +++ b/src/plugins/qmldesigner/settingspage.cpp @@ -4,6 +4,7 @@ #include "settingspage.h" #include "designersettings.h" +#include "designmodewidget.h" #include "qmldesignerexternaldependencies.h" #include "qmldesignerplugin.h" @@ -83,6 +84,7 @@ private: QCheckBox *m_askBeforeDeletingAssetCheckBox; QCheckBox *m_alwaysAutoFormatUICheckBox; QCheckBox *m_featureTimelineEditorCheckBox; + QCheckBox *m_featureDockWidgetContentMinSize; QGroupBox *m_debugGroupBox; QCheckBox *m_designerShowDebuggerCheckBox; QCheckBox *m_showPropertyEditorWarningsCheckBox; @@ -184,6 +186,7 @@ SettingsPageWidget::SettingsPageWidget(ExternalDependencies &externalDependencie m_askBeforeDeletingAssetCheckBox = new QCheckBox(tr("Ask for confirmation before deleting asset")); m_alwaysAutoFormatUICheckBox = new QCheckBox(tr("Always auto-format ui.qml files in Design mode")); m_featureTimelineEditorCheckBox = new QCheckBox(tr("Enable Timeline editor")); + m_featureDockWidgetContentMinSize = new QCheckBox(tr("Enable DockWidget content minimum size")); m_debugGroupBox = new QGroupBox(tr("Debugging")); m_designerShowDebuggerCheckBox = new QCheckBox(tr("Show the debugging view")); @@ -221,71 +224,51 @@ SettingsPageWidget::SettingsPageWidget(ExternalDependencies &externalDependencie Form { tr("Debug QML emulation layer:"), m_debugPuppetComboBox } }.attachTo(m_debugGroupBox); - Column { - Row { - Group { - title(tr("Snapping")), - Form { - tr("Parent component padding:"), m_spinSnapMargin, br, - tr("Sibling component spacing:"), m_spinItemSpacing - } - }, - Group { - title(tr("Canvas")), - Form { - tr("Width:"), m_spinCanvasWidth, br, - tr("Height:"), m_spinCanvasHeight, br, - tr("Smooth rendering:"), m_smoothRendering - } - }, - Group { - title(tr("Root Component Init Size")), - Form { - tr("Width:"), m_spinRootItemInitWidth, br, - tr("Height:"), m_spinRootItemInitHeight - } - }, - Group { - title(tr("Styling")), - Form { - tr("Controls style:"), m_styleLineEdit, resetStyle, br, - tr("Controls 2 style:"), m_controls2StyleComboBox - } - } - }, - m_emulationGroupBox, - Group { - title(tr("Subcomponents")), - Column { m_alwaysSaveSubcomponentsCheckBox } - }, - Row { - Group { - title(tr("Warnings")), - Column { - m_designerWarningsCheckBox, - m_designerWarningsInEditorCheckBox, - m_designerWarningsUiQmlfiles - } - }, - Group { - title(tr("Internationalization")), - Column { - m_useQsTrFunctionRadioButton, - m_useQsTrIdFunctionRadioButton, - m_useQsTranslateFunctionRadioButton - } - } - }, - Group { - title(tr("Features")), - Grid { - m_designerAlwaysDesignModeCheckBox, m_alwaysAutoFormatUICheckBox, br, - m_askBeforeDeletingAssetCheckBox, m_featureTimelineEditorCheckBox - } - }, - m_debugGroupBox, - st - }.attachTo(this); + Column{Row{Group{title(tr("Snapping")), + Form{tr("Parent component padding:"), + m_spinSnapMargin, + br, + tr("Sibling component spacing:"), + m_spinItemSpacing}}, + Group{title(tr("Canvas")), + Form{tr("Width:"), + m_spinCanvasWidth, + br, + tr("Height:"), + m_spinCanvasHeight, + br, + tr("Smooth rendering:"), + m_smoothRendering}}, + Group{title(tr("Root Component Init Size")), + Form{tr("Width:"), m_spinRootItemInitWidth, br, tr("Height:"), m_spinRootItemInitHeight}}, + Group{title(tr("Styling")), + Form{tr("Controls style:"), + m_styleLineEdit, + resetStyle, + br, + tr("Controls 2 style:"), + m_controls2StyleComboBox}}}, + m_emulationGroupBox, + Group{title(tr("Subcomponents")), Column{m_alwaysSaveSubcomponentsCheckBox}}, + Row{Group{title(tr("Warnings")), + Column{m_designerWarningsCheckBox, + m_designerWarningsInEditorCheckBox, + m_designerWarningsUiQmlfiles}}, + Group{title(tr("Internationalization")), + Column{m_useQsTrFunctionRadioButton, + m_useQsTrIdFunctionRadioButton, + m_useQsTranslateFunctionRadioButton}}}, + Group{title(tr("Features")), + Grid{m_designerAlwaysDesignModeCheckBox, + m_alwaysAutoFormatUICheckBox, + br, + m_askBeforeDeletingAssetCheckBox, + m_featureTimelineEditorCheckBox, + br, + m_featureDockWidgetContentMinSize}}, + m_debugGroupBox, + st} + .attachTo(this); connect(m_designerEnableDebuggerCheckBox, &QCheckBox::toggled, [=](bool checked) { if (checked && ! m_designerShowDebuggerCheckBox->isChecked()) @@ -309,9 +292,15 @@ SettingsPageWidget::SettingsPageWidget(ExternalDependencies &externalDependencie connect(resetStyle, &QPushButton::clicked, m_styleLineEdit, &QLineEdit::clear); connect(m_controls2StyleComboBox, &QComboBox::currentTextChanged, [=]() { - m_styleLineEdit->setText(m_controls2StyleComboBox->currentText()); - } - ); + m_styleLineEdit->setText(m_controls2StyleComboBox->currentText()); + }); + + connect(m_featureDockWidgetContentMinSize, &QCheckBox::toggled, this, [=](bool checked) { + if (checked && !m_featureDockWidgetContentMinSize->isChecked()) + m_featureDockWidgetContentMinSize->setChecked(true); + + QmlDesignerPlugin::instance()->mainWidget()->setMinimumSizeHintFromContentMinimumSize(checked); + }); m_forwardPuppetOutputComboBox->addItems(puppetModes()); m_debugPuppetComboBox->addItems(puppetModes()); @@ -391,6 +380,8 @@ QHash SettingsPageWidget::newSettings() const m_showWarnExceptionsCheckBox->isChecked()); settings.insert(DesignerSettingsKey::ENABLE_TIMELINEVIEW, m_featureTimelineEditorCheckBox->isChecked()); + settings.insert(DesignerSettingsKey::ENABLE_DOCKWIDGET_CONTENT_MIN_SIZE, + m_featureDockWidgetContentMinSize->isChecked()); settings.insert(DesignerSettingsKey::ALWAYS_DESIGN_MODE, m_designerAlwaysDesignModeCheckBox->isChecked()); settings.insert(DesignerSettingsKey::ASK_BEFORE_DELETING_ASSET, @@ -470,8 +461,11 @@ void SettingsPageWidget::setSettings(const DesignerSettings &settings) DesignerSettingsKey::ALWAYS_DESIGN_MODE).toBool()); m_featureTimelineEditorCheckBox->setChecked(settings.value( DesignerSettingsKey::ENABLE_TIMELINEVIEW).toBool()); - m_askBeforeDeletingAssetCheckBox->setChecked(settings.value( - DesignerSettingsKey::ASK_BEFORE_DELETING_ASSET).toBool()); + m_featureDockWidgetContentMinSize->setChecked( + settings.value(DesignerSettingsKey::ENABLE_DOCKWIDGET_CONTENT_MIN_SIZE).toBool()); + + m_askBeforeDeletingAssetCheckBox->setChecked( + settings.value(DesignerSettingsKey::ASK_BEFORE_DELETING_ASSET).toBool()); #ifdef QT_DEBUG const auto showDebugSettings = true; @@ -482,6 +476,7 @@ void SettingsPageWidget::setSettings(const DesignerSettings &settings) m_emulationGroupBox->setVisible(showAdvancedFeatures); m_debugGroupBox->setVisible(showAdvancedFeatures); m_featureTimelineEditorCheckBox->setVisible(Core::ICore::isQtDesignStudio()); + m_featureDockWidgetContentMinSize->setVisible(Core::ICore::isQtDesignStudio()); m_smoothRendering->setChecked(settings.value(DesignerSettingsKey::SMOOTH_RENDERING).toBool()); m_alwaysAutoFormatUICheckBox->setChecked( @@ -492,16 +487,14 @@ void SettingsPageWidget::apply() { auto settings = newSettings(); - const auto restartNecessaryKeys = { - DesignerSettingsKey::PUPPET_DEFAULT_DIRECTORY, - DesignerSettingsKey::PUPPET_TOPLEVEL_BUILD_DIRECTORY, - DesignerSettingsKey::ENABLE_MODEL_EXCEPTION_OUTPUT, - DesignerSettingsKey::PUPPET_KILL_TIMEOUT, - DesignerSettingsKey::FORWARD_PUPPET_OUTPUT, - DesignerSettingsKey::DEBUG_PUPPET, - DesignerSettingsKey::ENABLE_MODEL_EXCEPTION_OUTPUT, - DesignerSettingsKey::ENABLE_TIMELINEVIEW - }; + const auto restartNecessaryKeys = {DesignerSettingsKey::PUPPET_DEFAULT_DIRECTORY, + DesignerSettingsKey::PUPPET_TOPLEVEL_BUILD_DIRECTORY, + DesignerSettingsKey::ENABLE_MODEL_EXCEPTION_OUTPUT, + DesignerSettingsKey::PUPPET_KILL_TIMEOUT, + DesignerSettingsKey::FORWARD_PUPPET_OUTPUT, + DesignerSettingsKey::DEBUG_PUPPET, + DesignerSettingsKey::ENABLE_MODEL_EXCEPTION_OUTPUT, + DesignerSettingsKey::ENABLE_TIMELINEVIEW}; for (const char * const key : restartNecessaryKeys) { if (QmlDesignerPlugin::settings().value(key) != settings.value(key)) { diff --git a/src/plugins/qmldesignerbase/studio/studioquickwidget.cpp b/src/plugins/qmldesignerbase/studio/studioquickwidget.cpp index b2c55c5b61d..5da9aaec6e2 100644 --- a/src/plugins/qmldesignerbase/studio/studioquickwidget.cpp +++ b/src/plugins/qmldesignerbase/studio/studioquickwidget.cpp @@ -24,6 +24,8 @@ StudioQuickWidget::StudioQuickWidget(QWidget *parent) setLayout(layout); layout->setContentsMargins(0, 0, 0, 0); layout->addWidget(m_quickWidget); + + setMinimumSize(QSize(100, 100)); // sensible default } QQmlEngine *StudioQuickWidget::engine() const diff --git a/src/plugins/qmldesignerbase/utils/designersettings.cpp b/src/plugins/qmldesignerbase/utils/designersettings.cpp index 8157712437b..248df496081 100644 --- a/src/plugins/qmldesignerbase/utils/designersettings.cpp +++ b/src/plugins/qmldesignerbase/utils/designersettings.cpp @@ -75,6 +75,7 @@ void DesignerSettings::fromSettings(QSettings *settings) restoreValue(settings, DesignerSettingsKey::NAVIGATOR_SHOW_ONLY_VISIBLE_ITEMS, true); restoreValue(settings, DesignerSettingsKey::NAVIGATOR_REVERSE_ITEM_ORDER, false); restoreValue(settings, DesignerSettingsKey::ENABLE_TIMELINEVIEW, true); + restoreValue(settings, DesignerSettingsKey::ENABLE_DOCKWIDGET_CONTENT_MIN_SIZE, true); restoreValue(settings, DesignerSettingsKey::COLOR_PALETTE_RECENT, QStringList()); restoreValue(settings, DesignerSettingsKey::COLOR_PALETTE_FAVORITE, QStringList()); restoreValue(settings, DesignerSettingsKey::ALWAYS_DESIGN_MODE, true); diff --git a/src/plugins/qmldesignerbase/utils/designersettings.h b/src/plugins/qmldesignerbase/utils/designersettings.h index 850e2a43a06..93eab204397 100644 --- a/src/plugins/qmldesignerbase/utils/designersettings.h +++ b/src/plugins/qmldesignerbase/utils/designersettings.h @@ -59,6 +59,7 @@ inline constexpr char IGNORE_DEVICE_PIXEL_RATIO[] = "IgnoreDevicePixelRaio"; /* The settings can be used to turn off the feature, if there are serious issues */ inline constexpr char SHOW_DEBUG_SETTINGS[] = "ShowDebugSettings"; inline constexpr char ENABLE_TIMELINEVIEW[] = "EnableTimelineView"; +inline constexpr char ENABLE_DOCKWIDGET_CONTENT_MIN_SIZE[] = "EnableDockWidgetContentMinSize"; inline constexpr char COLOR_PALETTE_RECENT[] = "ColorPaletteRecent"; inline constexpr char COLOR_PALETTE_FAVORITE[] = "ColorPaletteFavorite"; inline constexpr char ALWAYS_DESIGN_MODE[] = "AlwaysDesignMode"; From 224a2564d6d55a7cdfa6832e08134734ef84e7af Mon Sep 17 00:00:00 2001 From: Henning Gruendl Date: Fri, 20 Oct 2023 16:39:11 +0200 Subject: [PATCH 090/242] QmlDesigner: Remove option from restart list Change-Id: I3bb28e8b65beddd96d757673f375021207a83ca2 Reviewed-by: Thomas Hartmann Reviewed-by: Reviewed-by: Qt CI Patch Build Bot --- src/plugins/qmldesigner/settingspage.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/plugins/qmldesigner/settingspage.cpp b/src/plugins/qmldesigner/settingspage.cpp index 4a1e855964e..2302883b0d9 100644 --- a/src/plugins/qmldesigner/settingspage.cpp +++ b/src/plugins/qmldesigner/settingspage.cpp @@ -490,7 +490,6 @@ void SettingsPageWidget::apply() const auto restartNecessaryKeys = {DesignerSettingsKey::PUPPET_DEFAULT_DIRECTORY, DesignerSettingsKey::PUPPET_TOPLEVEL_BUILD_DIRECTORY, DesignerSettingsKey::ENABLE_MODEL_EXCEPTION_OUTPUT, - DesignerSettingsKey::PUPPET_KILL_TIMEOUT, DesignerSettingsKey::FORWARD_PUPPET_OUTPUT, DesignerSettingsKey::DEBUG_PUPPET, DesignerSettingsKey::ENABLE_MODEL_EXCEPTION_OUTPUT, From 8e033db46f1c81d1503e5dc694c78fdfd2ca85d7 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Tue, 24 Oct 2023 16:00:39 +0300 Subject: [PATCH 091/242] QmlDesigner: Fix getNodeAtPos for 3D view when the view is split Fixes: QDS-11019 Change-Id: If18404204f9fd550c79cfa1f22d7adec5fac2595 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Mahmoud Badri --- .../qt5informationnodeinstanceserver.cpp | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp b/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp index 990e03fa014..63d4e79241b 100644 --- a/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp +++ b/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp @@ -428,23 +428,26 @@ void Qt5InformationNodeInstanceServer::getNodeAtPos([[maybe_unused]] const QPoin if (!helper) return; - QQmlProperty editViewProp(m_editView3DData.rootItem, "activeEditView", context()); - QObject *obj = qvariant_cast(editViewProp.read()); - QQuick3DViewport *editView = qobject_cast(obj); - - // Non-model nodes with icon gizmos are also valid results + // Non-model nodes with icon gizmos are also valid results. QVariant gizmoVar; QMetaObject::invokeMethod(m_editView3DData.rootItem, "gizmoAt", Qt::DirectConnection, Q_RETURN_ARG(QVariant, gizmoVar), Q_ARG(QVariant, pos.x()), Q_ARG(QVariant, pos.y())); QObject *gizmoObj = qvariant_cast(gizmoVar); + + // gizmoAt() call above will update the activeEditView + QQmlProperty editViewProp(m_editView3DData.rootItem, "activeEditView", context()); + QObject *obj = qvariant_cast(editViewProp.read()); + QQuick3DViewport *editView = qobject_cast(obj); + QPointF mappedPos = m_editView3DData.rootItem->mapToItem(editView, pos); + qint32 instanceId = -1; if (gizmoObj && hasInstanceForObject(gizmoObj)) { instanceId = instanceForObject(gizmoObj).instanceId(); } else { - QQuick3DModel *hitModel = helper->pickViewAt(editView, pos.x(), pos.y()).objectHit(); + QQuick3DModel *hitModel = helper->pickViewAt(editView, mappedPos.x(), mappedPos.y()).objectHit(); QObject *resolvedPick = helper->resolvePick(hitModel); if (hasInstanceForObject(resolvedPick)) instanceId = instanceForObject(resolvedPick).instanceId(); @@ -456,7 +459,7 @@ void Qt5InformationNodeInstanceServer::getNodeAtPos([[maybe_unused]] const QPoin Internal::MouseArea3D ma; ma.setView3D(editView); ma.setEulerRotation({90, 0, 0}); // Default grid plane (XZ plane) - QVector3D planePos = ma.getMousePosInPlane(nullptr, pos); + QVector3D planePos = ma.getMousePosInPlane(nullptr, mappedPos); const float limit = 10000000; // Remove extremes on nearly parallel plane if (!qFuzzyCompare(planePos.z(), -1.f) && qAbs(planePos.x()) < limit && qAbs(planePos.y()) < limit) pos3d = {planePos.x(), 0, planePos.y()}; From 6bfca09b9821dfc8d4d2db593405a6b68aeba426 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Tue, 24 Oct 2023 17:11:14 +0300 Subject: [PATCH 092/242] QmlDesigner: Clear selected material on model detach Retaining ModelNodes from detached model causes issues down the line. Change-Id: I2bd1969d014fef76210a727312eb8bb9da96ffa6 Reviewed-by: Knud Dollereder --- .../qmldesigner/components/materialeditor/materialeditorview.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp b/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp index 3c7f0e8af09..992ef2574f1 100644 --- a/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp +++ b/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp @@ -791,6 +791,7 @@ void MaterialEditorView::modelAboutToBeDetached(Model *model) m_dynamicPropertiesModel->reset(); m_qmlBackEnd->materialEditorTransaction()->end(); m_qmlBackEnd->contextObject()->setHasMaterialLibrary(false); + m_selectedMaterial = {}; } void MaterialEditorView::propertiesRemoved(const QList &propertyList) From 5d7bfd67bd695ea375d60a52e7332eeeac11f3cc Mon Sep 17 00:00:00 2001 From: Brook Cronin Date: Tue, 24 Oct 2023 15:53:40 +0200 Subject: [PATCH 093/242] QmlDesigner: Add jump to code icons Change-Id: Ied52b12c2a6855766819a9444b217b6386ffd28a Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Thomas Hartmann Reviewed-by: --- .../imports/StudioTheme/InternalConstants.qml | 376 +++++++++--------- .../imports/StudioTheme/icons.ttf | Bin 64484 -> 64788 bytes .../components/componentcore/theme.h | 2 + 3 files changed, 191 insertions(+), 187 deletions(-) diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/InternalConstants.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/InternalConstants.qml index 168f3c37908..b86ce391d20 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/InternalConstants.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/InternalConstants.qml @@ -178,193 +178,195 @@ QtObject { readonly property string infinity: "\u00C5" readonly property string invisible_medium: "\u00C6" readonly property string invisible_small: "\u00C7" - readonly property string keyframe: "\u00C8" - readonly property string languageList_medium: "\u00C9" - readonly property string layouts_small: "\u00CA" - readonly property string lights_small: "\u00CB" - readonly property string linear_medium: "\u00CC" - readonly property string linkTriangle: "\u00CD" - readonly property string linked: "\u00CE" - readonly property string listView: "\u00CF" - readonly property string list_medium: "\u00D0" - readonly property string localOrient_medium: "\u00D1" - readonly property string lockOff: "\u00D2" - readonly property string lockOn: "\u00D3" - readonly property string loopPlayback_medium: "\u00D4" - readonly property string materialBrowser_medium: "\u00D5" - readonly property string materialPreviewEnvironment: "\u00D6" - readonly property string materialPreviewModel: "\u00D7" - readonly property string material_medium: "\u00D8" - readonly property string maxBar_small: "\u00D9" - readonly property string mergeCells: "\u00DA" - readonly property string merge_small: "\u00DB" - readonly property string minus: "\u00DC" - readonly property string mirror: "\u00DD" - readonly property string more_medium: "\u00DE" - readonly property string mouseArea_small: "\u00DF" - readonly property string moveDown_medium: "\u00E0" - readonly property string moveInwards_medium: "\u00E1" - readonly property string moveUp_medium: "\u00E2" - readonly property string moveUpwards_medium: "\u00E3" - readonly property string move_medium: "\u00E4" - readonly property string newMaterial: "\u00E5" - readonly property string nextFile_large: "\u00E6" - readonly property string normalBar_small: "\u00E7" - readonly property string openLink: "\u00E8" - readonly property string openMaterialBrowser: "\u00E9" - readonly property string orientation: "\u00EA" - readonly property string orthCam_medium: "\u00EB" - readonly property string orthCam_small: "\u00EC" - readonly property string paddingEdge: "\u00ED" - readonly property string paddingFrame: "\u00EE" - readonly property string particleAnimation_medium: "\u00EF" - readonly property string pasteStyle: "\u00F0" - readonly property string paste_small: "\u00F1" - readonly property string pause: "\u00F2" - readonly property string perspectiveCam_medium: "\u00F3" - readonly property string perspectiveCam_small: "\u00F4" - readonly property string pin: "\u00F5" - readonly property string plane_medium: "\u00F6" - readonly property string plane_small: "\u00F7" - readonly property string play: "\u00F8" - readonly property string playFill_medium: "\u00F9" - readonly property string playOutline_medium: "\u00FA" - readonly property string plus: "\u00FB" - readonly property string pointLight_small: "\u00FC" - readonly property string positioners_small: "\u00FD" - readonly property string previewEnv_medium: "\u00FE" - readonly property string previousFile_large: "\u00FF" - readonly property string promote: "\u0100" - readonly property string properties_medium: "\u0101" - readonly property string readOnly: "\u0102" - readonly property string recordFill_medium: "\u0103" - readonly property string recordOutline_medium: "\u0104" - readonly property string redo: "\u0105" - readonly property string reload_medium: "\u0106" - readonly property string remove_medium: "\u0107" - readonly property string remove_small: "\u0108" - readonly property string rename_small: "\u0109" - readonly property string replace_small: "\u010A" - readonly property string resetView_small: "\u010B" - readonly property string restartParticles_medium: "\u010C" - readonly property string reverseOrder_medium: "\u010D" - readonly property string roatate_medium: "\u010E" - readonly property string rotationFill: "\u010F" - readonly property string rotationOutline: "\u0110" - readonly property string runProjFill_large: "\u0111" - readonly property string runProjOutline_large: "\u0112" - readonly property string s_anchors: "\u0113" - readonly property string s_annotations: "\u0114" - readonly property string s_arrange: "\u0115" - readonly property string s_boundingBox: "\u0116" - readonly property string s_component: "\u0117" - readonly property string s_connections: "\u0118" - readonly property string s_edit: "\u0119" - readonly property string s_enterComponent: "\u011A" - readonly property string s_eventList: "\u011B" - readonly property string s_group: "\u011C" - readonly property string s_layouts: "\u011D" - readonly property string s_merging: "\u011E" - readonly property string s_mouseArea: "\u011F" - readonly property string s_positioners: "\u0120" - readonly property string s_selection: "\u0121" - readonly property string s_snapping: "\u0122" - readonly property string s_timeline: "\u0123" - readonly property string s_visibility: "\u0124" - readonly property string saveLogs_medium: "\u0125" - readonly property string scale_medium: "\u0126" - readonly property string search: "\u0127" - readonly property string search_small: "\u0128" - readonly property string sectionToggle: "\u0129" - readonly property string selectFill_medium: "\u012A" - readonly property string selectOutline_medium: "\u012B" - readonly property string selectParent_small: "\u012C" - readonly property string selection_small: "\u012D" - readonly property string settings_medium: "\u012E" - readonly property string signal_small: "\u012F" - readonly property string snapping_conf_medium: "\u0130" - readonly property string snapping_medium: "\u0131" - readonly property string snapping_small: "\u0132" - readonly property string sortascending_medium: "\u0133" - readonly property string sortdescending_medium: "\u0134" - readonly property string sphere_medium: "\u0135" - readonly property string sphere_small: "\u0136" - readonly property string splitColumns: "\u0137" - readonly property string splitRows: "\u0138" - readonly property string spotLight_small: "\u0139" - readonly property string stackedContainer_small: "\u013A" - readonly property string startNode: "\u013B" - readonly property string step_medium: "\u013C" - readonly property string stop_medium: "\u013D" - readonly property string testIcon: "\u013E" - readonly property string textAlignBottom: "\u013F" - readonly property string textAlignCenter: "\u0140" - readonly property string textAlignJustified: "\u0141" - readonly property string textAlignLeft: "\u0142" - readonly property string textAlignMiddle: "\u0143" - readonly property string textAlignRight: "\u0144" - readonly property string textAlignTop: "\u0145" - readonly property string textBulletList: "\u0146" - readonly property string textFullJustification: "\u0147" - readonly property string textNumberedList: "\u0148" - readonly property string textures_medium: "\u0149" - readonly property string tickIcon: "\u014A" - readonly property string tickMark_small: "\u014B" - readonly property string timeline_small: "\u014C" - readonly property string toEndFrame_medium: "\u014D" - readonly property string toNextFrame_medium: "\u014E" - readonly property string toPrevFrame_medium: "\u014F" - readonly property string toStartFrame_medium: "\u0150" - readonly property string topToolbar_annotations: "\u0151" - readonly property string topToolbar_closeFile: "\u0152" - readonly property string topToolbar_designMode: "\u0153" - readonly property string topToolbar_enterComponent: "\u0154" - readonly property string topToolbar_home: "\u0155" - readonly property string topToolbar_makeComponent: "\u0156" - readonly property string topToolbar_navFile: "\u0157" - readonly property string topToolbar_runProject: "\u0158" - readonly property string translationCreateFiles: "\u0159" - readonly property string translationCreateReport: "\u015A" - readonly property string translationExport: "\u015B" - readonly property string translationImport: "\u015C" - readonly property string translationSelectLanguages: "\u015D" - readonly property string translationTest: "\u015E" - readonly property string transparent: "\u015F" - readonly property string triState: "\u0160" - readonly property string triangleArcA: "\u0161" - readonly property string triangleArcB: "\u0162" - readonly property string triangleCornerA: "\u0163" - readonly property string triangleCornerB: "\u0164" - readonly property string unLinked: "\u0165" - readonly property string undo: "\u0166" - readonly property string unify_medium: "\u0167" - readonly property string unpin: "\u0168" - readonly property string upDownIcon: "\u0169" - readonly property string upDownSquare2: "\u016A" - readonly property string updateAvailable_medium: "\u016B" - readonly property string updateContent_medium: "\u016C" - readonly property string uploadcsv_large: "\u016D" - readonly property string uploadcsv_medium: "\u016E" - readonly property string uploadjson_large: "\u016F" - readonly property string uploadjson_medium: "\u0170" - readonly property string visibilityOff: "\u0171" - readonly property string visibilityOn: "\u0172" - readonly property string visible_medium: "\u0173" - readonly property string visible_small: "\u0174" - readonly property string wildcard: "\u0175" - readonly property string wizardsAutomotive: "\u0176" - readonly property string wizardsDesktop: "\u0177" - readonly property string wizardsGeneric: "\u0178" - readonly property string wizardsMcuEmpty: "\u0179" - readonly property string wizardsMcuGraph: "\u017A" - readonly property string wizardsMobile: "\u017B" - readonly property string wizardsUnknown: "\u017C" - readonly property string zoomAll: "\u017D" - readonly property string zoomIn: "\u017E" - readonly property string zoomIn_medium: "\u017F" - readonly property string zoomOut: "\u0180" - readonly property string zoomOut_medium: "\u0181" - readonly property string zoomSelection: "\u0182" + readonly property string jumpToCode_medium: "\u00C8" + readonly property string jumpToCode_small: "\u00C9" + readonly property string keyframe: "\u00CA" + readonly property string languageList_medium: "\u00CB" + readonly property string layouts_small: "\u00CC" + readonly property string lights_small: "\u00CD" + readonly property string linear_medium: "\u00CE" + readonly property string linkTriangle: "\u00CF" + readonly property string linked: "\u00D0" + readonly property string listView: "\u00D1" + readonly property string list_medium: "\u00D2" + readonly property string localOrient_medium: "\u00D3" + readonly property string lockOff: "\u00D4" + readonly property string lockOn: "\u00D5" + readonly property string loopPlayback_medium: "\u00D6" + readonly property string materialBrowser_medium: "\u00D7" + readonly property string materialPreviewEnvironment: "\u00D8" + readonly property string materialPreviewModel: "\u00D9" + readonly property string material_medium: "\u00DA" + readonly property string maxBar_small: "\u00DB" + readonly property string mergeCells: "\u00DC" + readonly property string merge_small: "\u00DD" + readonly property string minus: "\u00DE" + readonly property string mirror: "\u00DF" + readonly property string more_medium: "\u00E0" + readonly property string mouseArea_small: "\u00E1" + readonly property string moveDown_medium: "\u00E2" + readonly property string moveInwards_medium: "\u00E3" + readonly property string moveUp_medium: "\u00E4" + readonly property string moveUpwards_medium: "\u00E5" + readonly property string move_medium: "\u00E6" + readonly property string newMaterial: "\u00E7" + readonly property string nextFile_large: "\u00E8" + readonly property string normalBar_small: "\u00E9" + readonly property string openLink: "\u00EA" + readonly property string openMaterialBrowser: "\u00EB" + readonly property string orientation: "\u00EC" + readonly property string orthCam_medium: "\u00ED" + readonly property string orthCam_small: "\u00EE" + readonly property string paddingEdge: "\u00EF" + readonly property string paddingFrame: "\u00F0" + readonly property string particleAnimation_medium: "\u00F1" + readonly property string pasteStyle: "\u00F2" + readonly property string paste_small: "\u00F3" + readonly property string pause: "\u00F4" + readonly property string perspectiveCam_medium: "\u00F5" + readonly property string perspectiveCam_small: "\u00F6" + readonly property string pin: "\u00F7" + readonly property string plane_medium: "\u00F8" + readonly property string plane_small: "\u00F9" + readonly property string play: "\u00FA" + readonly property string playFill_medium: "\u00FB" + readonly property string playOutline_medium: "\u00FC" + readonly property string plus: "\u00FD" + readonly property string pointLight_small: "\u00FE" + readonly property string positioners_small: "\u00FF" + readonly property string previewEnv_medium: "\u0100" + readonly property string previousFile_large: "\u0101" + readonly property string promote: "\u0102" + readonly property string properties_medium: "\u0103" + readonly property string readOnly: "\u0104" + readonly property string recordFill_medium: "\u0105" + readonly property string recordOutline_medium: "\u0106" + readonly property string redo: "\u0107" + readonly property string reload_medium: "\u0108" + readonly property string remove_medium: "\u0109" + readonly property string remove_small: "\u010A" + readonly property string rename_small: "\u010B" + readonly property string replace_small: "\u010C" + readonly property string resetView_small: "\u010D" + readonly property string restartParticles_medium: "\u010E" + readonly property string reverseOrder_medium: "\u010F" + readonly property string roatate_medium: "\u0110" + readonly property string rotationFill: "\u0111" + readonly property string rotationOutline: "\u0112" + readonly property string runProjFill_large: "\u0113" + readonly property string runProjOutline_large: "\u0114" + readonly property string s_anchors: "\u0115" + readonly property string s_annotations: "\u0116" + readonly property string s_arrange: "\u0117" + readonly property string s_boundingBox: "\u0118" + readonly property string s_component: "\u0119" + readonly property string s_connections: "\u011A" + readonly property string s_edit: "\u011B" + readonly property string s_enterComponent: "\u011C" + readonly property string s_eventList: "\u011D" + readonly property string s_group: "\u011E" + readonly property string s_layouts: "\u011F" + readonly property string s_merging: "\u0120" + readonly property string s_mouseArea: "\u0121" + readonly property string s_positioners: "\u0122" + readonly property string s_selection: "\u0123" + readonly property string s_snapping: "\u0124" + readonly property string s_timeline: "\u0125" + readonly property string s_visibility: "\u0126" + readonly property string saveLogs_medium: "\u0127" + readonly property string scale_medium: "\u0128" + readonly property string search: "\u0129" + readonly property string search_small: "\u012A" + readonly property string sectionToggle: "\u012B" + readonly property string selectFill_medium: "\u012C" + readonly property string selectOutline_medium: "\u012D" + readonly property string selectParent_small: "\u012E" + readonly property string selection_small: "\u012F" + readonly property string settings_medium: "\u0130" + readonly property string signal_small: "\u0131" + readonly property string snapping_conf_medium: "\u0132" + readonly property string snapping_medium: "\u0133" + readonly property string snapping_small: "\u0134" + readonly property string sortascending_medium: "\u0135" + readonly property string sortdescending_medium: "\u0136" + readonly property string sphere_medium: "\u0137" + readonly property string sphere_small: "\u0138" + readonly property string splitColumns: "\u0139" + readonly property string splitRows: "\u013A" + readonly property string spotLight_small: "\u013B" + readonly property string stackedContainer_small: "\u013C" + readonly property string startNode: "\u013D" + readonly property string step_medium: "\u013E" + readonly property string stop_medium: "\u013F" + readonly property string testIcon: "\u0140" + readonly property string textAlignBottom: "\u0141" + readonly property string textAlignCenter: "\u0142" + readonly property string textAlignJustified: "\u0143" + readonly property string textAlignLeft: "\u0144" + readonly property string textAlignMiddle: "\u0145" + readonly property string textAlignRight: "\u0146" + readonly property string textAlignTop: "\u0147" + readonly property string textBulletList: "\u0148" + readonly property string textFullJustification: "\u0149" + readonly property string textNumberedList: "\u014A" + readonly property string textures_medium: "\u014B" + readonly property string tickIcon: "\u014C" + readonly property string tickMark_small: "\u014D" + readonly property string timeline_small: "\u014E" + readonly property string toEndFrame_medium: "\u014F" + readonly property string toNextFrame_medium: "\u0150" + readonly property string toPrevFrame_medium: "\u0151" + readonly property string toStartFrame_medium: "\u0152" + readonly property string topToolbar_annotations: "\u0153" + readonly property string topToolbar_closeFile: "\u0154" + readonly property string topToolbar_designMode: "\u0155" + readonly property string topToolbar_enterComponent: "\u0156" + readonly property string topToolbar_home: "\u0157" + readonly property string topToolbar_makeComponent: "\u0158" + readonly property string topToolbar_navFile: "\u0159" + readonly property string topToolbar_runProject: "\u015A" + readonly property string translationCreateFiles: "\u015B" + readonly property string translationCreateReport: "\u015C" + readonly property string translationExport: "\u015D" + readonly property string translationImport: "\u015E" + readonly property string translationSelectLanguages: "\u015F" + readonly property string translationTest: "\u0160" + readonly property string transparent: "\u0161" + readonly property string triState: "\u0162" + readonly property string triangleArcA: "\u0163" + readonly property string triangleArcB: "\u0164" + readonly property string triangleCornerA: "\u0165" + readonly property string triangleCornerB: "\u0166" + readonly property string unLinked: "\u0167" + readonly property string undo: "\u0168" + readonly property string unify_medium: "\u0169" + readonly property string unpin: "\u016A" + readonly property string upDownIcon: "\u016B" + readonly property string upDownSquare2: "\u016C" + readonly property string updateAvailable_medium: "\u016D" + readonly property string updateContent_medium: "\u016E" + readonly property string uploadcsv_large: "\u016F" + readonly property string uploadcsv_medium: "\u0170" + readonly property string uploadjson_large: "\u0171" + readonly property string uploadjson_medium: "\u0172" + readonly property string visibilityOff: "\u0173" + readonly property string visibilityOn: "\u0174" + readonly property string visible_medium: "\u0175" + readonly property string visible_small: "\u0176" + readonly property string wildcard: "\u0177" + readonly property string wizardsAutomotive: "\u0178" + readonly property string wizardsDesktop: "\u0179" + readonly property string wizardsGeneric: "\u017A" + readonly property string wizardsMcuEmpty: "\u017B" + readonly property string wizardsMcuGraph: "\u017C" + readonly property string wizardsMobile: "\u017D" + readonly property string wizardsUnknown: "\u017E" + readonly property string zoomAll: "\u017F" + readonly property string zoomIn: "\u0180" + readonly property string zoomIn_medium: "\u0181" + readonly property string zoomOut: "\u0182" + readonly property string zoomOut_medium: "\u0183" + readonly property string zoomSelection: "\u0184" readonly property font iconFont: Qt.font({ "family": controlIcons.name, diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/icons.ttf b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/icons.ttf index 4d18734ddac41d554999461e30cf272872037ed2..ecf109170c9a6f5af4ca544c0bcff064fad3bc02 100644 GIT binary patch delta 1115 zcmaFzoq5VH=6VK31_lORh6V;^h5$FW5Z?s{QZz!<^6 zz>tufn^>R~uEWQ`z|zCOz!a8TR-(Y5$W+O|!1jcJfq^AGvAE#>e+Fg-hCd$|7#KLx zb1KvBte^Xlfq`=i1Ht#|e<>V(PzI-gF#=yY# zf`NhQOm1RD0pm_aRt5$}3kC)Tg}lVv)B|ekRT&uWnlLahT`tHkF4=x6N91;^V@u7;AVcoz`(%pFt&;pMo(h2WlWuH#~91lvUv_;HREIfR+i1; z%nKM9TQ=`tsb{W#V4z@NVerGy&Tx|91H*4dHb!%d-WaSj92%)uf(TiX479ia5GB*3>(0b3Eht#L3QSp3@m; zE9Wlf6V5+elw4d~s$AZyjOTX^U?F^@=f#O@!R7s;GgDyCBP+MUciGuzCfG613?Bsc0qkXhk_mi zn*}!op9zr&$qU&PS}zcq6S^ZTBy3;Uqi~+^fbgd9E#WUBq#|M>CPg+yeuxT)S`>9A znkm{NdQJ4J7>$@EG3R1!Vnbr{Vmo4&#NLXNhzp2279SN~7T*=WC;nd|OJY&dtYn$w zc_}<8UMUqRr&3p>DW!#^ok_P!pO?Xwk&v+{<4dMqWjR8WLXNzGKygjHNjgrAX3NzKHbQBg_F#LQSxUCqSIhId^kP2y%4(wc-&Jtufn^>R~uEWE?z|z6Mz!a8TR-(Y5$W+O|!1jQFfq^AGvAE#>e+Fg-hTjJm7#KLx zb1Ku`3)Wm`VBlQCz;LN2BQ-Ha@7H0#lXP! zgn@zSL~de50poT?Rt5$}3kC)Tg}lVv)V-J_iGX z%nq<07#SFpnm=^J^V@u7;AVcoz`(%p&>%bxMo(h2WlWxI#~91lw0RC=HREP!<^_z5 zO`G?y)HB!bGpI8-VklvlW>{l*$Vkbk$mo7ETs*7RxN2SXx;wvwUXdWwp*~m(@AzBuDLu|Wj zkJw(aePhRGXJfa|p2ozwIHxIW7Zr|La+su2o4IK z7yKo}A!J&}r_g%4&|6_HVHshQ!mfpLgj_ElMLQD5@jsT(nVi zLG-#9t(cIQLov5vzQppyYQ-kR&WpVk=M(oJ?oYfxyi{Eg9c3-58h{6d4#8wKnhJbc^5oEwiv#-IP%l z9GeUbEDVy2?NBx=g8<`UD4UHzl8Iw--(6kxEQXB?%nXbyoD3`sMvTEwHY Date: Tue, 24 Oct 2023 13:39:21 +0200 Subject: [PATCH 094/242] Prevent assert from triggering when trying to edit a 3D component Clicking on "Edit Component" of a 3D component in the Components view results in an assertion. This patch attempts to resolve this. Change-Id: I98ca474ee1c1ea4369ad9bc6c207a6c61e4e4e95 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Thomas Hartmann Reviewed-by: Reviewed-by: Miikka Heikkinen --- .../qmldesigner/designercore/instances/nodeinstanceview.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp b/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp index e187d4986f4..88aff952a3a 100644 --- a/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp +++ b/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp @@ -1776,7 +1776,7 @@ void NodeInstanceView::view3DAction(View3DActionType type, const QVariant &value void NodeInstanceView::requestModelNodePreviewImage(const ModelNode &node, const ModelNode &renderNode) const { - if (m_nodeInstanceServer && node.isValid()) { + if (m_nodeInstanceServer && node.isValid() && hasInstanceForModelNode(node)) { auto instance = instanceForModelNode(node); if (instance.isValid()) { qint32 renderItemId = -1; From d0201ad998fbe4a47b57580f9aa8aa552c0e033d Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Wed, 25 Oct 2023 14:26:27 +0200 Subject: [PATCH 095/242] QmlDesigner: Bump version in project template Change-Id: Ic0c2e37d40d219b550f57e178ee282c1e9d55609 Reviewed-by: Thomas Hartmann --- .../studio_templates/projects/common/app.qmlproject.tpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/common/app.qmlproject.tpl b/share/qtcreator/qmldesigner/studio_templates/projects/common/app.qmlproject.tpl index ab407e22f09..6169688f16b 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/common/app.qmlproject.tpl +++ b/share/qtcreator/qmldesigner/studio_templates/projects/common/app.qmlproject.tpl @@ -110,7 +110,7 @@ Project { /* Required for deployment */ targetDirectory: "/opt/%{ProjectName}" - qdsVersion: "4.3" + qdsVersion: "4.4" quickVersion: "%{QtQuickVersion}" From ea8561db8d54faf095aa20dfd73ef560a13eeb5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Jen=C3=9Fen?= Date: Tue, 24 Oct 2023 09:38:47 +0200 Subject: [PATCH 096/242] QmlDesigner: rename Qt5 only Default style to Basic Task-number: QDS-9980 Change-Id: Id3d511adb5c1cd5f680f91705cb2f7a721c7ad12 Reviewed-by: Thomas Hartmann Reviewed-by: Qt CI Patch Build Bot Reviewed-by: --- .../projects/application-3d/wizard.json | 8 +++----- .../studio_templates/projects/application/wizard.json | 10 ++++------ .../projects/desktop-launcher/wizard.json | 8 +++----- .../projects/mobile-scroll/wizard.json | 8 +++----- .../studio_templates/projects/mobile-stack/wizard.json | 8 +++----- .../studio_templates/projects/mobile-swipe/wizard.json | 8 +++----- .../components/componentcore/changestyleaction.cpp | 4 ---- 7 files changed, 19 insertions(+), 35 deletions(-) diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/application-3d/wizard.json b/share/qtcreator/qmldesigner/studio_templates/projects/application-3d/wizard.json index 4809b7d410b..d5b1fef1e45 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/application-3d/wizard.json +++ b/share/qtcreator/qmldesigner/studio_templates/projects/application-3d/wizard.json @@ -23,9 +23,7 @@ { "key": "UIClassFileName", "value": "%{JS: Util.fileName('%{UIClassName}', 'ui.qml')}" }, { "key": "QtQuickVersion", "value": "%{JS: %{TargetQtVersion}.TargetQuickVersion}" }, { "key": "QtQuickFeature", "value": "QtSupport.Wizards.FeatureQtQuick.%{QtQuickVersion}" }, - { "key": "QtQuickControlsStyleInternalQt5", "value": "%{JS: %{ControlsStyle}.QtQuickControlsStyle}" }, - { "key": "QtQuickControlsStyleInternalQt6", "value": "%{JS: value('QtQuickControlsStyleInternalQt5') === 'Default' ? 'Basic' : value('QtQuickControlsStyleInternalQt5')}" }, - { "key": "QtQuickControlsStyle", "value": "%{JS: value('IsQt6Project') === 'true' ? value('QtQuickControlsStyleInternalQt6') : value('QtQuickControlsStyleInternalQt5')}" }, + { "key": "QtQuickControlsStyle", "value": "Basic" }, { "key": "QtQuickControlsStyleTheme", "value": "%{JS: %{ControlsStyle}.QtQuickControlsStyleTheme}" }, { "key": "ApplicationImport", "value": "%{JS: value('IsQt6Project') === 'true' ? '%{ImportModuleName}' : '%{ImportModuleName} 1.0'}" }, { "key": "UseStandardResolution", "value": "%{JS: value('CustomScreenWidth') === '' || value('CustomScreenHeight') === ''}" }, @@ -134,10 +132,10 @@ "items": [ { - "trKey": "Default", + "trKey": "Basic", "value": "({ - 'QtQuickControlsStyle': 'Default', + 'QtQuickControlsStyle': 'Basic', 'QtQuickControlsStyleTheme': '' })" }, diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/application/wizard.json b/share/qtcreator/qmldesigner/studio_templates/projects/application/wizard.json index e1a25e23fb5..0708838a5ae 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/application/wizard.json +++ b/share/qtcreator/qmldesigner/studio_templates/projects/application/wizard.json @@ -23,16 +23,14 @@ { "key": "IsQt6Project", "value": "%{JS: value('QtQuickVersion') !== '2.15' }" }, { "key": "QtQuickVersion", "value": "%{JS: %{TargetQtVersion}.TargetQuickVersion}" }, { "key": "QtQuickFeature", "value": "QtSupport.Wizards.FeatureQtQuick.%{QtQuickVersion}" }, - { "key": "QtQuickControlsStyleInternalQt5", "value": "%{JS: %{ControlsStyle}.QtQuickControlsStyle}" }, - { "key": "QtQuickControlsStyleInternalQt6", "value": "%{JS: value('QtQuickControlsStyleInternalQt5') === 'Default' ? 'Basic' : value('QtQuickControlsStyleInternalQt5')}" }, - { "key": "QtQuickControlsStyle", "value": "%{JS: value('IsQt6Project') === 'true' ? value('QtQuickControlsStyleInternalQt6') : value('QtQuickControlsStyleInternalQt5')}" }, + { "key": "QtQuickControlsStyle", "value": "Basic" }, { "key": "QtQuickControlsStyleTheme", "value": "%{JS: %{ControlsStyle}.QtQuickControlsStyleTheme}" }, { "key": "ApplicationImport", "value": "%{JS: value('IsQt6Project') === 'true' ? '%{ImportModuleName}' : '%{ImportModuleName} 1.0'}" }, { "key": "UseStandardResolution", "value": "%{JS: value('CustomScreenWidth') === '' || value('CustomScreenHeight') === ''}" }, { "key": "ScreenWidth", "value": "%{JS: value('UseStandardResolution') === 'true' ? %{ScreenFactor}.ScreenWidth : value('CustomScreenWidth')}" }, { "key": "ScreenHeight", "value": "%{JS: value('UseStandardResolution') === 'true' ? %{ScreenFactor}.ScreenHeight : value('CustomScreenHeight')}" }, { "key": "UseVirtualKeyboardDefault", "value": "%{JS: false}" }, - { "key": "DefaultStyle", "value": "%{JS: value('IsQt6Project') === 'true' ? 'Basic' : 'Default'}" }, + { "key": "DefaultStyle", "value": "Basic" }, { "key": "ImportModuleVersion", "value": "%{JS: value('IsQt6Project') === 'true' ? '' : '1.0'}" } ], @@ -134,10 +132,10 @@ "items": [ { - "trKey": "Default", + "trKey": "Basic", "value": "({ - 'QtQuickControlsStyle': 'Default', + 'QtQuickControlsStyle': 'Basic', 'QtQuickControlsStyleTheme': '' })" }, diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/desktop-launcher/wizard.json b/share/qtcreator/qmldesigner/studio_templates/projects/desktop-launcher/wizard.json index 58fb3f30ef7..913eb0bf28b 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/desktop-launcher/wizard.json +++ b/share/qtcreator/qmldesigner/studio_templates/projects/desktop-launcher/wizard.json @@ -23,9 +23,7 @@ { "key": "IsQt6Project", "value": "%{JS: value('QtQuickVersion') !== '2.15' }" }, { "key": "QtQuickVersion", "value": "%{JS: %{TargetQtVersion}.TargetQuickVersion}" }, { "key": "QtQuickFeature", "value": "QtSupport.Wizards.FeatureQtQuick.%{QtQuickVersion}" }, - { "key": "QtQuickControlsStyleInternalQt5", "value": "%{JS: %{ControlsStyle}.QtQuickControlsStyle}" }, - { "key": "QtQuickControlsStyleInternalQt6", "value": "%{JS: value('QtQuickControlsStyleInternalQt5') === 'Default' ? 'Basic' : value('QtQuickControlsStyleInternalQt5')}" }, - { "key": "QtQuickControlsStyle", "value": "%{JS: value('IsQt6Project') === 'true' ? value('QtQuickControlsStyleInternalQt6') : value('QtQuickControlsStyleInternalQt5')}" }, + { "key": "QtQuickControlsStyle", "value": "Basic" }, { "key": "QtQuickControlsStyleTheme", "value": "%{JS: %{ControlsStyle}.QtQuickControlsStyleTheme}" }, { "key": "ApplicationImport", "value": "%{JS: value('IsQt6Project') === 'true' ? '%{ImportModuleName}' : '%{ImportModuleName} 1.0'}" }, { "key": "UseStandardResolution", "value": "%{JS: value('CustomScreenWidth') === '' || value('CustomScreenHeight') === ''}" }, @@ -132,10 +130,10 @@ "items": [ { - "trKey": "Default", + "trKey": "Basic", "value": "({ - 'QtQuickControlsStyle': 'Default', + 'QtQuickControlsStyle': 'Basic', 'QtQuickControlsStyleTheme': '' })" }, diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/mobile-scroll/wizard.json b/share/qtcreator/qmldesigner/studio_templates/projects/mobile-scroll/wizard.json index d79059a6a5a..944b6b6289c 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/mobile-scroll/wizard.json +++ b/share/qtcreator/qmldesigner/studio_templates/projects/mobile-scroll/wizard.json @@ -23,9 +23,7 @@ { "key": "IsQt6Project", "value": "%{JS: value('QtQuickVersion') !== '2.15' }" }, { "key": "QtQuickVersion", "value": "%{JS: %{TargetQtVersion}.TargetQuickVersion}" }, { "key": "QtQuickFeature", "value": "QtSupport.Wizards.FeatureQtQuick.%{QtQuickVersion}" }, - { "key": "QtQuickControlsStyleInternalQt5", "value": "%{JS: %{ControlsStyle}.QtQuickControlsStyle}" }, - { "key": "QtQuickControlsStyleInternalQt6", "value": "%{JS: value('QtQuickControlsStyleInternalQt5') === 'Default' ? 'Basic' : value('QtQuickControlsStyleInternalQt5')}" }, - { "key": "QtQuickControlsStyle", "value": "%{JS: value('IsQt6Project') === 'true' ? value('QtQuickControlsStyleInternalQt6') : value('QtQuickControlsStyleInternalQt5')}" }, + { "key": "QtQuickControlsStyle", "value": "Basic" }, { "key": "QtQuickControlsStyleTheme", "value": "%{JS: %{ControlsStyle}.QtQuickControlsStyleTheme}" }, { "key": "ApplicationImport", "value": "%{JS: value('IsQt6Project') === 'true' ? '%{ImportModuleName}' : '%{ImportModuleName} 1.0'}" }, { "key": "UseStandardResolution", "value": "%{JS: value('CustomScreenWidth') === '' || value('CustomScreenHeight') === ''}" }, @@ -100,10 +98,10 @@ "items": [ { - "trKey": "Default", + "trKey": "Basic", "value": "({ - 'QtQuickControlsStyle': 'Default', + 'QtQuickControlsStyle': 'Basic', 'QtQuickControlsStyleTheme': '' })" }, diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/mobile-stack/wizard.json b/share/qtcreator/qmldesigner/studio_templates/projects/mobile-stack/wizard.json index cd837cf5688..c8733770e07 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/mobile-stack/wizard.json +++ b/share/qtcreator/qmldesigner/studio_templates/projects/mobile-stack/wizard.json @@ -21,9 +21,7 @@ { "key": "IsQt6Project", "value": "%{JS: value('QtQuickVersion') !== '2.15' }" }, { "key": "QtQuickVersion", "value": "%{JS: %{TargetQtVersion}.TargetQuickVersion}" }, { "key": "QtQuickFeature", "value": "QtSupport.Wizards.FeatureQtQuick.%{QtQuickVersion}" }, - { "key": "QtQuickControlsStyleInternalQt5", "value": "%{JS: %{ControlsStyle}.QtQuickControlsStyle}" }, - { "key": "QtQuickControlsStyleInternalQt6", "value": "%{JS: value('QtQuickControlsStyleInternalQt5') === 'Default' ? 'Basic' : value('QtQuickControlsStyleInternalQt5')}" }, - { "key": "QtQuickControlsStyle", "value": "%{JS: value('IsQt6Project') === 'true' ? value('QtQuickControlsStyleInternalQt6') : value('QtQuickControlsStyleInternalQt5')}" }, + { "key": "QtQuickControlsStyle", "value": "Basic" }, { "key": "QtQuickControlsStyleTheme", "value": "%{JS: %{ControlsStyle}.QtQuickControlsStyleTheme}" }, { "key": "ApplicationImport", "value": "%{JS: value('IsQt6Project') === 'true' ? '%{ImportModuleName}' : '%{ImportModuleName} 1.0'}" }, { "key": "UseStandardResolution", "value": "%{JS: value('CustomScreenWidth') === '' || value('CustomScreenHeight') === ''}" }, @@ -98,10 +96,10 @@ "items": [ { - "trKey": "Default", + "trKey": "Basic", "value": "({ - 'QtQuickControlsStyle': 'Default', + 'QtQuickControlsStyle': 'Basic', 'QtQuickControlsStyleTheme': '' })" }, diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/mobile-swipe/wizard.json b/share/qtcreator/qmldesigner/studio_templates/projects/mobile-swipe/wizard.json index abef03752e2..295c85aa63a 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/mobile-swipe/wizard.json +++ b/share/qtcreator/qmldesigner/studio_templates/projects/mobile-swipe/wizard.json @@ -21,9 +21,7 @@ { "key": "IsQt6Project", "value": "%{JS: value('QtQuickVersion') !== '2.15' }" }, { "key": "QtQuickVersion", "value": "%{JS: %{TargetQtVersion}.TargetQuickVersion}" }, { "key": "QtQuickFeature", "value": "QtSupport.Wizards.FeatureQtQuick.%{QtQuickVersion}" }, - { "key": "QtQuickControlsStyleInternalQt5", "value": "%{JS: %{ControlsStyle}.QtQuickControlsStyle}" }, - { "key": "QtQuickControlsStyleInternalQt6", "value": "%{JS: value('QtQuickControlsStyleInternalQt5') === 'Default' ? 'Basic' : value('QtQuickControlsStyleInternalQt5')}" }, - { "key": "QtQuickControlsStyle", "value": "%{JS: value('IsQt6Project') === 'true' ? value('QtQuickControlsStyleInternalQt6') : value('QtQuickControlsStyleInternalQt5')}" }, + { "key": "QtQuickControlsStyle", "value": "Basic" }, { "key": "QtQuickControlsStyleTheme", "value": "%{JS: %{ControlsStyle}.QtQuickControlsStyleTheme}" }, { "key": "ApplicationImport", "value": "%{JS: value('IsQt6Project') === 'true' ? '%{ImportModuleName}' : '%{ImportModuleName} 1.0'}" }, { "key": "UseStandardResolution", "value": "%{JS: value('CustomScreenWidth') === '' || value('CustomScreenHeight') === ''}" }, @@ -98,10 +96,10 @@ "items": [ { - "trKey": "Default", + "trKey": "Basic", "value": "({ - 'QtQuickControlsStyle': 'Default', + 'QtQuickControlsStyle': 'Basic', 'QtQuickControlsStyleTheme': '' })" }, diff --git a/src/plugins/qmldesigner/components/componentcore/changestyleaction.cpp b/src/plugins/qmldesigner/components/componentcore/changestyleaction.cpp index 6ef8bbb6c94..4eb0ba175cf 100644 --- a/src/plugins/qmldesigner/components/componentcore/changestyleaction.cpp +++ b/src/plugins/qmldesigner/components/componentcore/changestyleaction.cpp @@ -29,9 +29,6 @@ static QString styleConfigFileName(const QString &qmlFileName) ChangeStyleWidgetAction::ChangeStyleWidgetAction(QObject *parent) : QWidgetAction(parent) { - // The Default style was renamed to Basic in Qt 6. In Qt 6, "Default" - // will result in a platform-specific style being chosen. - items = getAllStyleItems(); } @@ -48,7 +45,6 @@ const QList ChangeStyleWidgetAction::styleItems() const QList ChangeStyleWidgetAction::getAllStyleItems() { QList items = {{"Basic", "Basic", {}}, - {"Default", "Default", {}}, {"Fusion", "Fusion", {}}, {"Imagine", "Imagine", {}}, {"Material Light", "Material", "Light"}, From 3e626446b1a54948e6e4242e1413e993cfbd3117 Mon Sep 17 00:00:00 2001 From: Henning Gruendl Date: Mon, 23 Oct 2023 11:26:47 +0200 Subject: [PATCH 097/242] QmlDesigner: Add workspace lock feature * Add lock button in top toolbar * Add lock button in top toolbar flyout * Add action in workspace menu Task-number: QDS-10181 Change-Id: Iabc94889f936955c56fa3e9fb3de4dced05649e0 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Reviewed-by: Thomas Hartmann Reviewed-by: Brook Cronin --- share/qtcreator/qmldesigner/toolbar/Main.qml | 30 +++++++++++++++- .../advanceddockingsystem/dockmanager.cpp | 36 +++++++++++++++++++ src/libs/advanceddockingsystem/dockmanager.h | 6 ++++ src/libs/advanceddockingsystem/workspace.cpp | 10 ++++++ src/libs/advanceddockingsystem/workspace.h | 4 +++ .../components/toolbar/toolbarbackend.cpp | 20 +++++++++++ .../components/toolbar/toolbarbackend.h | 4 +++ src/plugins/qmldesigner/designmodewidget.cpp | 7 ++++ 8 files changed, 116 insertions(+), 1 deletion(-) diff --git a/share/qtcreator/qmldesigner/toolbar/Main.qml b/share/qtcreator/qmldesigner/toolbar/Main.qml index 1ddedd08b48..ddd63b0708f 100644 --- a/share/qtcreator/qmldesigner/toolbar/Main.qml +++ b/share/qtcreator/qmldesigner/toolbar/Main.qml @@ -215,7 +215,7 @@ Rectangle { ToolbarButton { id: enterComponent anchors.verticalCenter: parent.verticalCenter - anchors.right: workspaces.left + anchors.right: lockWorkspace.left anchors.rightMargin: 10 enabled: goIntoComponentBackend.available tooltip: goIntoComponentBackend.tooltip @@ -230,6 +230,21 @@ Rectangle { } } + ToolbarButton { + id: lockWorkspace + anchors.verticalCenter: parent.verticalCenter + anchors.right: workspaces.left + anchors.rightMargin: 10 + tooltip: qsTr("Sets the visible Views to immovable across the Workspaces.") + buttonIcon: backend.lockWorkspace ? StudioTheme.Constants.lockOn + : StudioTheme.Constants.lockOff + visible: !root.flyoutEnabled + checkable: true + checked: backend.lockWorkspace + + onClicked: backend.setLockWorkspace(lockWorkspace.checked) + } + StudioControls.TopLevelComboBox { id: workspaces style: StudioTheme.Values.toolbarStyle @@ -376,6 +391,19 @@ Rectangle { onClicked: backend.editGlobalAnnoation() } + ToolbarButton { + id: lockWorkspaceFlyout + style: StudioTheme.Values.statusbarButtonStyle + anchors.verticalCenter: parent.verticalCenter + tooltip: lockWorkspace.tooltip + buttonIcon: backend.lockWorkspace ? StudioTheme.Constants.lockOn + : StudioTheme.Constants.lockOff + checkable: true + checked: backend.lockWorkspace + + onClicked: backend.setLockWorkspace(lockWorkspaceFlyout.checked) + } + ToolbarButton { anchors.verticalCenter: parent.verticalCenter style: StudioTheme.Values.primaryToolbarStyle diff --git a/src/libs/advanceddockingsystem/dockmanager.cpp b/src/libs/advanceddockingsystem/dockmanager.cpp index b389a88bc9e..3b1f142be5b 100644 --- a/src/libs/advanceddockingsystem/dockmanager.cpp +++ b/src/libs/advanceddockingsystem/dockmanager.cpp @@ -89,6 +89,7 @@ public: QString m_workspacePresetsPath; QList m_workspaces; Workspace m_workspace; + bool m_workspaceLocked = false; QSettings *m_settings = nullptr; bool m_modeChangeState = false; @@ -365,6 +366,7 @@ DockManager::~DockManager() emit aboutToUnloadWorkspace(d->m_workspace.fileName()); save(); saveStartupWorkspace(); + saveLockWorkspace(); // Fix memory leaks, see https://github.com/githubuser0xFFFF/Qt-Advanced-Docking-System/issues/307 std::vector areas; @@ -488,6 +490,8 @@ void DockManager::initialize() } openWorkspace(workspace); + + lockWorkspace(d->m_settings->value(Constants::LOCK_WORKSPACE_SETTINGS_KEY, false).toBool()); } DockAreaWidget *DockManager::addDockWidget(DockWidgetArea area, @@ -1102,6 +1106,32 @@ void DockManager::showWorkspaceMananger() workspaceDialog.autoLoadWorkspace()); } +void DockManager::lockWorkspace(bool value) +{ + if (value == d->m_workspaceLocked) + return; + + d->m_workspaceLocked = value; + + DockWidget::DockWidgetFeatures features = DockWidget::DefaultDockWidgetFeatures; + + if (value) { + internal::setFlag(features, DockWidget::DockWidgetMovable, false); + internal::setFlag(features, DockWidget::DockWidgetFloatable, false); + } + + const auto &dockWidgets = dockWidgetsMap(); + for (auto dockWidget : dockWidgets) + dockWidget->setFeatures(features); + + emit lockWorkspaceChanged(); +} + +bool DockManager::isWorkspaceLocked() const +{ + return d->m_workspaceLocked; +} + expected_str DockManager::createWorkspace(const QString &workspaceName) { qCInfo(adsLog) << "Create workspace" << workspaceName; @@ -1614,4 +1644,10 @@ void DockManager::saveStartupWorkspace() activeWorkspace()->fileName()); } +void DockManager::saveLockWorkspace() +{ + QTC_ASSERT(d->m_settings, return); + d->m_settings->setValue(Constants::LOCK_WORKSPACE_SETTINGS_KEY, d->m_workspaceLocked); +} + } // namespace ADS diff --git a/src/libs/advanceddockingsystem/dockmanager.h b/src/libs/advanceddockingsystem/dockmanager.h index 195f634023b..31621449d58 100644 --- a/src/libs/advanceddockingsystem/dockmanager.h +++ b/src/libs/advanceddockingsystem/dockmanager.h @@ -32,6 +32,7 @@ namespace Constants { const char DEFAULT_WORKSPACE[] = "Basic.wrk"; // Needs to align with a shipped preset const char STARTUP_WORKSPACE_SETTINGS_KEY[] = "QML/Designer/StartupWorkspace"; const char AUTO_RESTORE_WORKSPACE_SETTINGS_KEY[] = "QML/Designer/AutoRestoreLastWorkspace"; +const char LOCK_WORKSPACE_SETTINGS_KEY[] = "QML/Designer/LockWorkspace"; } // namespace Constants class DockManagerPrivate; @@ -662,6 +663,9 @@ public: // Workspace management functionality void showWorkspaceMananger(); + void lockWorkspace(bool value); + bool isWorkspaceLocked() const; + /** * \brief Create a workspace. * @@ -734,6 +738,7 @@ signals: void workspaceLoaded(QString fileName); void workspaceReloaded(QString fileName); void aboutToSaveWorkspace(); + void lockWorkspaceChanged(); private: static Utils::expected_str write(const Utils::FilePath &filePath, const QByteArray &data); @@ -747,6 +752,7 @@ private: void prepareWorkspaces(); void saveStartupWorkspace(); + void saveLockWorkspace(); }; // class DockManager } // namespace ADS diff --git a/src/libs/advanceddockingsystem/workspace.cpp b/src/libs/advanceddockingsystem/workspace.cpp index d3b0785d90b..760e2c7dd1c 100644 --- a/src/libs/advanceddockingsystem/workspace.cpp +++ b/src/libs/advanceddockingsystem/workspace.cpp @@ -41,6 +41,16 @@ const QString &Workspace::name() const return m_name; } +void Workspace::setLocked(bool value) +{ + m_locked = value; +} + +bool Workspace::isLocked() const +{ + return m_locked; +} + const Utils::FilePath &Workspace::filePath() const { return m_filePath; diff --git a/src/libs/advanceddockingsystem/workspace.h b/src/libs/advanceddockingsystem/workspace.h index c23db55d6b6..5e96e5a9815 100644 --- a/src/libs/advanceddockingsystem/workspace.h +++ b/src/libs/advanceddockingsystem/workspace.h @@ -18,6 +18,9 @@ public: void setName(const QString &name); const QString &name() const; + void setLocked(bool value); + bool isLocked() const; + const Utils::FilePath &filePath() const; QString fileName() const; @@ -50,6 +53,7 @@ private: QString m_name; Utils::FilePath m_filePath; bool m_preset = false; + bool m_locked = false; }; } // namespace ADS diff --git a/src/plugins/qmldesigner/components/toolbar/toolbarbackend.cpp b/src/plugins/qmldesigner/components/toolbar/toolbarbackend.cpp index 1a8d3835fa7..2634629fbbb 100644 --- a/src/plugins/qmldesigner/components/toolbar/toolbarbackend.cpp +++ b/src/plugins/qmldesigner/components/toolbar/toolbarbackend.cpp @@ -308,6 +308,13 @@ ToolBarBackend::ToolBarBackend(QObject *parent) this, &ToolBarBackend::currentWorkspaceChanged); emit currentWorkspaceChanged(); + + connect(dockManager, + &ADS::DockManager::lockWorkspaceChanged, + this, + &ToolBarBackend::lockWorkspaceChanged); + emit lockWorkspaceChanged(); + return true; }; @@ -453,6 +460,11 @@ void ToolBarBackend::setCurrentWorkspace(const QString &workspace) designModeWidget()->dockManager()->openWorkspace(workspace); } +void ToolBarBackend::setLockWorkspace(bool value) +{ + designModeWidget()->dockManager()->lockWorkspace(value); +} + void ToolBarBackend::editGlobalAnnoation() { launchGlobalAnnotations(); @@ -588,6 +600,14 @@ QString ToolBarBackend::currentWorkspace() const return {}; } +bool ToolBarBackend::lockWorkspace() const +{ + if (designModeWidget() && designModeWidget()->dockManager()) + return designModeWidget()->dockManager()->isWorkspaceLocked(); + + return false; +} + QStringList ToolBarBackend::styles() const { const QList items = ChangeStyleWidgetAction::getAllStyleItems(); diff --git a/src/plugins/qmldesigner/components/toolbar/toolbarbackend.h b/src/plugins/qmldesigner/components/toolbar/toolbarbackend.h index 0091ab8a7f1..66572515fcb 100644 --- a/src/plugins/qmldesigner/components/toolbar/toolbarbackend.h +++ b/src/plugins/qmldesigner/components/toolbar/toolbarbackend.h @@ -83,6 +83,7 @@ class ToolBarBackend : public QObject Q_PROPERTY(QStringList documentModel READ documentModel NOTIFY openDocumentsChanged) Q_PROPERTY(int documentIndex READ documentIndex NOTIFY documentIndexChanged) Q_PROPERTY(QString currentWorkspace READ currentWorkspace NOTIFY currentWorkspaceChanged) + Q_PROPERTY(bool lockWorkspace READ lockWorkspace WRITE setLockWorkspace NOTIFY lockWorkspaceChanged) Q_PROPERTY(QStringList styles READ styles CONSTANT) Q_PROPERTY(bool isInDesignMode READ isInDesignMode NOTIFY isInDesignModeChanged) Q_PROPERTY(bool isInEditMode READ isInEditMode NOTIFY isInEditModeChanged) @@ -106,6 +107,7 @@ public: Q_INVOKABLE void closeCurrentDocument(); Q_INVOKABLE void shareApplicationOnline(); Q_INVOKABLE void setCurrentWorkspace(const QString &workspace); + Q_INVOKABLE void setLockWorkspace(bool value); Q_INVOKABLE void editGlobalAnnoation(); Q_INVOKABLE void showZoomMenu(int x, int y); Q_INVOKABLE void setCurrentStyle(int index); @@ -120,6 +122,7 @@ public: int documentIndex() const; QString currentWorkspace() const; + bool lockWorkspace() const; QStringList styles() const; @@ -143,6 +146,7 @@ signals: void openDocumentsChanged(); void documentIndexChanged(); void currentWorkspaceChanged(); + void lockWorkspaceChanged(); void isInDesignModeChanged(); void isInEditModeChanged(); void isDesignModeEnabledChanged(); diff --git a/src/plugins/qmldesigner/designmodewidget.cpp b/src/plugins/qmldesigner/designmodewidget.cpp index 492402fe020..1387f42f9c9 100644 --- a/src/plugins/qmldesigner/designmodewidget.cpp +++ b/src/plugins/qmldesigner/designmodewidget.cpp @@ -483,6 +483,13 @@ void DesignModeWidget::aboutToShowWorkspaces() QAction *action = menu->addAction(tr("Manage...")); connect(action, &QAction::triggered, m_dockManager, &ADS::DockManager::showWorkspaceMananger); + QAction *lockWorkspace = menu->addAction(tr("Lock Workspaces")); + lockWorkspace->setCheckable(true); + lockWorkspace->setChecked(m_dockManager->isWorkspaceLocked()); + connect(lockWorkspace, &QAction::triggered, this, [this](bool checked) { + m_dockManager->lockWorkspace(checked); + }); + QAction *resetWorkspace = menu->addAction(tr("Reset Active")); connect(resetWorkspace, &QAction::triggered, this, [this]() { if (m_dockManager->resetWorkspacePreset(m_dockManager->activeWorkspace()->fileName())) From 89502f5298921c86f826ee76d3a1055dfedd7077 Mon Sep 17 00:00:00 2001 From: Henning Gruendl Date: Tue, 24 Oct 2023 15:57:19 +0200 Subject: [PATCH 098/242] QmlDesigner: ADS code and comment clean up Change-Id: I2dadb9a46e58c066de072b7e4eabb17ec8d9a1cb Reviewed-by: Thomas Hartmann Reviewed-by: Qt CI Patch Build Bot --- .../advanceddockingsystem/dockareatabbar.h | 56 +++++++++---------- .../advanceddockingsystem/dockmanager.cpp | 30 +++++----- .../advanceddockingsystem/dockwidgettab.cpp | 15 +++-- 3 files changed, 46 insertions(+), 55 deletions(-) diff --git a/src/libs/advanceddockingsystem/dockareatabbar.h b/src/libs/advanceddockingsystem/dockareatabbar.h index c21b577947f..33f5b117e4a 100644 --- a/src/libs/advanceddockingsystem/dockareatabbar.h +++ b/src/libs/advanceddockingsystem/dockareatabbar.h @@ -17,13 +17,11 @@ class FloatingDockContainer; class AbstractFloatingWidget; /** - * Custom tabbar implementation for tab area that is shown on top of a - * dock area widget. - * The tabbar displays the tab widgets of the contained dock widgets. - * We cannot use QTabBar here because it does a lot of fancy animations - * that will crash the application if a tab is removed while the animation - * has not finished. And we need to remove a tab, if the user drags a - * a dock widget out of a group of tabbed widgets + * Custom tabbar implementation for tab area that is shown on top of a dock area widget. The tabbar + * displays the tab widgets of the contained dock widgets. We cannot use QTabBar here because it + * does a lot of fancy animations that will crash the application if a tab is removed while the + * animation has not finished. And we need to remove a tab, if the user drags a dock widget out + * of a group of tabbed widgets. */ class ADS_EXPORT DockAreaTabBar : public QScrollArea { @@ -55,24 +53,24 @@ public: ~DockAreaTabBar() override; /** - * Inserts the given dock widget tab at the given position. - * Inserting a new tab at an index less than or equal to the current index - * will increment the current index, but keep the current tab. + * Inserts the given dock widget tab at the given position. Inserting a new tab at an index + * less than or equal to the current index will increment the current index, but keep the + * current tab. */ void insertTab(int Index, DockWidgetTab *tab); /** - * Removes the given DockWidgetTab from the tabbar + * Removes the given DockWidgetTab from the tabbar. */ void removeTab(DockWidgetTab *tab); /** - * Returns the number of tabs in this tabbar + * Returns the number of tabs in this tabbar. */ int count() const; /** - * Returns the current index or -1 if no tab is selected + * Returns the current index or -1 if no tab is selected. */ int currentIndex() const; @@ -82,7 +80,7 @@ public: DockWidgetTab *currentTab() const; /** - * Returns the tab with the given index + * Returns the tab with the given index. */ DockWidgetTab *tab(int index) const; @@ -99,14 +97,13 @@ public: int tabInsertIndexAt(const QPoint &pos) const; /** - * Filters the tab widget events + * Filters the tab widget events. */ bool eventFilter(QObject *watched, QEvent *event) override; /** - * This function returns true if the tab is open, that means if it is - * visible to the user. If the function returns false, the tab is - * closed + * This function returns true if the tab is open, that means if it is visible to the user. + * If the function returns false, the tab is closed. */ bool isTabOpen(int index) const; @@ -120,19 +117,18 @@ public: QSize minimumSizeHint() const override; /** - * The function provides a sizeHint that matches the height of the - * internal viewport. + * The function provides a sizeHint that matches the height of the internal viewport. */ QSize sizeHint() const override; /** - * This property sets the index of the tab bar's visible tab + * This property sets the index of the tab bar's visible tab. */ void setCurrentIndex(int index); /** * This function will close the tab given in Index param. - * Closing a tab means, the tab will be hidden, it will not be removed + * Closing a tab means, the tab will be hidden, it will not be removed. */ void closeTab(int index); @@ -145,12 +141,12 @@ signals: /** * This signal is emitted when the tab bar's current tab changes. The new - * current has the given index, or -1 if there isn't a new one + * current has the given index, or -1 if there isn't a new one. */ void currentChanged(int index); /** - * This signal is emitted when user clicks on a tab + * This signal is emitted when user clicks on a tab. */ void tabBarClicked(int index); @@ -161,13 +157,12 @@ signals: void tabCloseRequested(int index); /** - * This signal is emitted if a tab has been closed + * This signal is emitted if a tab has been closed. */ void tabClosed(int index); /** - * This signal is emitted if a tab has been opened. - * A tab is opened if it has been made visible + * This signal is emitted if a tab has been opened. A tab is opened if it has been made visible. */ void tabOpened(int index); @@ -178,18 +173,17 @@ signals: void tabMoved(int from, int to); /** - * This signal is emitted, just before the tab with the given index is - * removed + * This signal is emitted, just before the tab with the given index is removed. */ void removingTab(int index); /** - * This signal is emitted if a tab has been inserted + * This signal is emitted if a tab has been inserted. */ void tabInserted(int index); /** - * This signal is emitted when a tab title elide state has been changed + * This signal is emitted when a tab title elide state has been changed. */ void elidedChanged(bool elided); }; // class DockAreaTabBar diff --git a/src/libs/advanceddockingsystem/dockmanager.cpp b/src/libs/advanceddockingsystem/dockmanager.cpp index 3b1f142be5b..088db38fbdf 100644 --- a/src/libs/advanceddockingsystem/dockmanager.cpp +++ b/src/libs/advanceddockingsystem/dockmanager.cpp @@ -469,19 +469,17 @@ void DockManager::initialize() if (!workspaceExists(lastWorkspace)) { // This is a fallback mechanism for pre 4.1 settings which stored the workspace name // instead of the file name. - QString minusVariant = lastWorkspace; - minusVariant.replace(" ", "-"); - minusVariant.append("." + workspaceFileExtension); - if (workspaceExists(minusVariant)) - workspace = minusVariant; + const std::vector separators = {"-", "_"}; - QString underscoreVariant = lastWorkspace; - underscoreVariant.replace(" ", "_"); - underscoreVariant.append("." + workspaceFileExtension); + for (const QString &separator : separators) { + QString workspaceVariant = lastWorkspace; + workspaceVariant.replace(" ", separator); + workspaceVariant.append("." + workspaceFileExtension); - if (workspaceExists(underscoreVariant)) - workspace = underscoreVariant; + if (workspaceExists(workspaceVariant)) + workspace = workspaceVariant; + } } else { workspace = lastWorkspace; } @@ -1140,7 +1138,7 @@ expected_str DockManager::createWorkspace(const QString &workspaceName) uniqueWorkspaceFileName(fileName); const FilePath filePath = userDirectory().pathAppended(fileName); - expected_str result = write(filePath, saveState(workspaceName)); + expected_str result = write(filePath, saveState(workspaceName)); // TODO utils if (!result) return make_unexpected(result.error()); @@ -1487,9 +1485,6 @@ QByteArray DockManager::loadFile(const FilePath &filePath) QString DockManager::readDisplayName(const FilePath &filePath) { - if (!filePath.exists()) - return {}; - auto data = loadFile(filePath); if (data.isEmpty()) @@ -1593,8 +1588,11 @@ void DockManager::syncWorkspacePresets() // If *.wrk file and displayName attribute is empty set the displayName. This // should fix old workspace files which don't have the displayName attribute. - if (userFile.suffix() == workspaceFileExtension && readDisplayName(userFile).isEmpty()) - writeDisplayName(userFile, readDisplayName(filePath)); + if (userFile.suffix() == workspaceFileExtension) { + const QString name = readDisplayName(userFile); + if (name.isEmpty()) + writeDisplayName(userFile, name); + } continue; } diff --git a/src/libs/advanceddockingsystem/dockwidgettab.cpp b/src/libs/advanceddockingsystem/dockwidgettab.cpp index 4efa339e34e..aa4aba6aee3 100644 --- a/src/libs/advanceddockingsystem/dockwidgettab.cpp +++ b/src/libs/advanceddockingsystem/dockwidgettab.cpp @@ -420,22 +420,21 @@ void DockWidgetTab::mouseMoveEvent(QMouseEvent *event) int dragDistanceY = qAbs(d->m_globalDragStartMousePosition.y() - event->globalPosition().toPoint().y()); if (dragDistanceY >= DockManager::startDragDistance() || mouseOutsideBar) { - // If this is the last dock area in a dock container with only - // one single dock widget it does not make sense to move it to a new - // floating widget and leave this one empty + // If this is the last dock area in a dock container with only one single dock widget it + // does not make sense to move it to a new floating widget and leave this one empty if (d->m_dockArea->dockContainer()->isFloating() && d->m_dockArea->openDockWidgetsCount() == 1 && d->m_dockArea->dockContainer()->visibleDockAreaCount() == 1) { return; } - // Floating is only allowed for widgets that are floatable - // We can create the drag preview if the widget is movable. + // Floating is only allowed for widgets that are floatable. We can create the drag preview + // if the widget is movable. auto features = d->m_dockWidget->features(); if (features.testFlag(DockWidget::DockWidgetFloatable) - || (features.testFlag(DockWidget::DockWidgetMovable))) { - // If we undock, we need to restore the initial position of this - // tab because it looks strange if it remains on its dragged position + || features.testFlag(DockWidget::DockWidgetMovable)) { + // If we undock, we need to restore the initial position of this tab because it looks + // strange if it remains on its dragged position if (d->isDraggingState(DraggingTab)) parentWidget()->layout()->update(); From efab1cbab1f4407a12fa0c3bca85cc7faf4f07f7 Mon Sep 17 00:00:00 2001 From: Pranta Dastider Date: Thu, 19 Oct 2023 22:17:33 +0200 Subject: [PATCH 099/242] QmlDesigner: Update the creating component instances doc Some of the images was outdated as the connection view format got changed in the latest version of Qt Design Studio. This patch will change the old images to the new ones. Fixes: QDS-10939 Change-Id: I62a3a342a1f2f38241a769ff96c25bb7d8f1e087 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Leena Miettinen --- .../images/qmldesigner-bindings.png | Bin 3514 -> 0 bytes .../images/qmldesigner-dynamicprops.png | Bin 3352 -> 0 bytes .../components/qtquick-component-instances.qdoc | 6 +++--- 3 files changed, 3 insertions(+), 3 deletions(-) delete mode 100644 doc/qtdesignstudio/images/qmldesigner-bindings.png delete mode 100644 doc/qtdesignstudio/images/qmldesigner-dynamicprops.png diff --git a/doc/qtdesignstudio/images/qmldesigner-bindings.png b/doc/qtdesignstudio/images/qmldesigner-bindings.png deleted file mode 100644 index 3ac0964f76e9d4ab9e456c882f4d893b5d164227..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3514 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 kCje3g%^dz>% diff --git a/doc/qtdesignstudio/images/qmldesigner-dynamicprops.png b/doc/qtdesignstudio/images/qmldesigner-dynamicprops.png deleted file mode 100644 index 565dc26b0e609583ea3ad25a40df1271a5e9b2bb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3352 zcmeAS@N?(olHy`uVBq!ia0y~yU}9omV3@+e%)r2K&3a=e0|V2P0G|+71`yC^UtWzoinM7Gij?J1A`!gz92)0AP6`L3T6wc zmkAnA5_HoSbek?1xLq)*Ot5yVVC`jbbp~;DeR1^=aW@8Ww=(f0eet9a@o8Jdr(Kqn zm6a0|loQOBGoB>p#vtdWFPF1jF6XA)`ss4(x5}-*EDr)M^75|o@~QIj>GCcu@~-Xj zsf*;(mnteMswC;FB!#G~pRTfgtIGPz8X6i}T3TAQAzIVQwAN48TEA6m{bg-IL2W@t zZNY5q_U+p3H?`aU=;-L^>+9>=xEdQ9n;8q589SO8XPYhfW0h2Aom^*~TyLGyV4c!v zliFyL+GLZ~Y?IM$o6%~Q-EEiC;}j_96qxOlEWeI?=J=ESi1E7;idQQudnxAU!T5y>EZRaZf)AMX@85${{6T1-@0|U-Su#L z`r)PNhnFq|8@%-J{rktJryrYMbL_~GW4CVIT9kTgf6J}?`)}P^bPJ^8)}3|rcaBWI zb8Fq5TSxBPx^;hP`u(M~_m{TcKiq!*@ZtOSm)^gB`2PL-#on8rGca)3d%8G=RNQ(y zr#C)2UG#YHgjK@L-qu^C%vPrrUx{*BpqQeU-Ec+oXxQr6>-H@OVvRqLm|Z(Fb;GuV zXw`XT~JYPOPm1$lLTdTr)0S~1up8c=y zT)%t!>&n9~SU+($F6Osi%Qs7B+IG9UxBvaK{d4Kd!@1LZgOoMgzr5KPVdrE&yU+IC zo$4cpmiO8HajmR8v1wzaAoKnE5lE6SEe^Q%&S$-c&<>l&@zT&k)Xx4$;|gQtGg*)5O~qMjy2X_ZrXd+yABHH=Wz!# zr)N%>*MIw92Fs4Si`Pw%mofRWZFSj(*lc#zMzwu1*NXjDOlPO$Q&W{^}(3vZ`49yrDE z3yi=9LJ?vNPR29?$ihiOci;o6}!{ zDj&u_E;j!CQ^s{(-T6HZ;q_K$3ZL{WjD2qYe6m{uPoY?Vn95fVu^V=N#lE+-+n(Qu z*cuTd%EZ1t-r8m7gHH!vAADfS^~3(>(qs*ZJ(Woc5_|qe?<(uv+%RXMqoHuqLcxr{ zz$a6D3SZ5(-d|g@>(im$$0hq_CC&(A{{YlOr1;@+;l1_7@qyQ)C;2hGdTbm&O-JC_Dp%)(S$!6V zFD!W89`47a{f%kQ*Q@-33;F&R>7DRB#BiOV^+5iWnG0`t9=O4gAsgw#GJ|hzYj-O{ ztwN#v&I?{&JcTX_Jc!!B!FcPRQAX;TuoFx{@1`p%?5-7L%KFdmF_VGm;wp({86p4r zly8aiO=x4dyzy^(H}}Cab($<(jK4RD>EF~*=yeofS|0Vo3=VIjQ|o@CC>A<~vee)+-L(`?$7vVp+hcgJ*>u6vE~Is(5L7+CH`vF+Ts;J5f#{ zVD68fA_51PlWY{&;AzgN$$>p3hmlbs;TErCG&Cj3Gca;+I5;pcF$oAXG%&ERC@3&6 zGIDUdxp(&lN56!0n_GFw@ zx)9Nvcm4r3C!o0Mu)>7a|9TbdIZxJ=K{MGQW)=ZCg@z9dj6qkAhW%Q+{`D%oR;Thu zH#4NquIk+upmFfQm)srCelpj;ntuK-BgcfsZIyEcBc}2`58a#V@g-LH+a8Ap+0&iY zv&y79mH(Di-p_T`dU+i8i-v~JSyzw7-+!C`xb}&}PfraK#odKh?0%fS5bwOOem>s~ z+2zbPH#u^AFe^Q~``@cT_0D*zc=#fHJ{^bdkb9HnDrj*&7If3 zRd%+a^b^|VQaYV(wwf+@G$UfQzXv;6sWr9i#aVzU)OTLk%nOEjArq-HMW@VNiz0ykr(*|gn$ zF-v!zv-{C~<(s>gnFQnW(60S$2U4=1<^HIzw>nTK+vB8=zUn{`%MO+l=Eedh*{#<9 z@7-5>YW~LRx_0>H^k%_=o;?y5*oyPc@8G>&uO7WpBw(pRdyrGX-K%cT6qw5FpK6^d z6#k{0@V!{8wC7LYr-k#YwaVredv#@f%kkLx?Ag7qr$oXTPF?@_Ys-|Jkl0xdyvyTL zSWfK!ulW8~`1jP3w{}upH-#+Kqc`U?UoP6CX0ZFUfelli@yczUD{mEe{-2)75)|LQ zQJ{fWfkE1V!K{dhQ68MWLA78F11u#YC!=XAwpsK~|Jt$kovzv4wDt81?mXA?y03OV zc=x>&tuG9WvH};1%lKl|a=4g|T25xox*rntLeqFAM;=r0+}=9TDd{&NatkHT#{569 zJM~woS$yrvl@D^a?fQK$G~k`-%riMVPdqo9xn||<71|Fw>$CRCPqL*uT~!^N|0WnSpMH{2T>T{+Re?W9c;L-hmZqQ@qy zJ~XSoX*hGg+=(SF|M8Qs_^hWT&vd`MXh?rsrG55K#>$(^Zz?pGgoGzeJ-gxQ-tUvG zv+uv~oNXe?XZ2;)>vy`>Rk;P2w)Aas>YvV(^(>gHvFX#V-?^KlqPj!cs#+Z+t*3|8 z%|HHp+p5HeP6=uJE2NhPzH2J_dDjvs={SCdzSKm{R&ZFPXioH#euC z+jmcrQe@`WC(9RY>DjS!n-+`8%BiXQE@g2PxF>wQx_9o|3w&W5Gj=MixcVbyW+IEm zyib<%yk$@ELS$-F@QEcf-CDW0AJ>>0i7Z`kvtPOD$9+^XX+ zapFGP$J&uy4beirB1~DPd7HIZSgf9w^D^q~Fcb_hmvcN29q{kJL&I<3%_d5dCK%u6 zyBF-V)9+ViYIBu&CVQxq&JDG{fmO8{zCW#Wxi9#97g_zy;Be9EGJ_t5a3)@B(dbBD zOZKVy7uJ`%Y+{%nmEO&>*-Zdsu!HZ$XL7tpZ}nAe$qx9Z@WAZl2G@l$ZWyJhgJs(R sz7sc?AZ76n1`gyz2`zCQ*jyjfpRLr`d;i73Mo Date: Tue, 24 Oct 2023 17:02:16 +0200 Subject: [PATCH 100/242] QmlDesigner: Update the Concepts and Terms doc This patch updates the Concepts and Terms document. Removes an old image and adds new images to support the current connection view update. Fixes: QDS-11000 Change-Id: I7d5d1bea4287e13c2344138af7cce5b8f9f4321d Reviewed-by: Leena Miettinen --- .../images/qtquick-component-signal.webp | Bin 0 -> 22038 bytes .../qtquick-connection-editor-assignment.png | Bin 4988 -> 0 bytes .../qtquick-connection-editor-assignment.webp | Bin 0 -> 11544 bytes .../src/qtdesignstudio-terms.qdoc | 8 ++++---- 4 files changed, 4 insertions(+), 4 deletions(-) create mode 100644 doc/qtdesignstudio/images/qtquick-component-signal.webp delete mode 100644 doc/qtdesignstudio/images/qtquick-connection-editor-assignment.png create mode 100644 doc/qtdesignstudio/images/qtquick-connection-editor-assignment.webp diff --git a/doc/qtdesignstudio/images/qtquick-component-signal.webp b/doc/qtdesignstudio/images/qtquick-component-signal.webp new file mode 100644 index 0000000000000000000000000000000000000000..7256ef97d635588cf8dc67d9d02bfa1e5e56df5e GIT binary patch literal 22038 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 literal 0 HcmV?d00001 diff --git a/doc/qtdesignstudio/images/qtquick-connection-editor-assignment.png b/doc/qtdesignstudio/images/qtquick-connection-editor-assignment.png deleted file mode 100644 index 60c9831184de1f1f004b223c0541c35b61c7648b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4988 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 diff --git a/doc/qtdesignstudio/images/qtquick-connection-editor-assignment.webp b/doc/qtdesignstudio/images/qtquick-connection-editor-assignment.webp new file mode 100644 index 0000000000000000000000000000000000000000..e2306339bf2d3200af8c190b0b3f009f96c0ca27 GIT binary patch literal 11544 zcmWIYbaNBXWnc(*bqWXzu<&8gWnj>Md&rU@l)HIbuZrr!^Ht^f@0bMyg&RIw{0b_F z)>6*fFn6tR9z)qmqXW`gBF=0z(=iXvn!0V(QZegwydUp9;QJF8l9zO4(juU8ogOcs!_pIWl_zx`9j z2!lrnD)R)VI&cab6scqW6Ad>6R`GiE1i8ofy z_@Tw)?I|(M!C}YR+~`g|eV3x;Ec!RC6qc~IPr1OEx+wB=ZqDshoAx;~#x?Ku{@*Ap zRDW9I!TB%u1#f$FJ+YIDlGT0xaqowS|NGCZU9$Gi{UA@(sRt(A4`086B{XUC44wFs zlTy8pX-Dr%J+11kHdEJl%L&yTMw?To#w4x$;-xxGD*SO!DCZij+gG=&?6Y!qpL?%( zOT)5`-Qq=?cQ?d2<}bMZV1;4%dd5ZXIE>%P-C{J>eXuUaVAct4iMRtg4Kovkdj!uf znOt;ij$yEs_lyY-oy=@ZAL=Y9H`vm3^Q_C{?@Nl+=I^ka&9S#RlC}8QlrjtM1#7}D z?%ce)+3tTy#k2Brv4_?;EMjQL654CJINITs65s1{3?e^DE(QlUy>YQ(^WeF0Ea=d- z-0VrVw_msgEp>jwm)1L9@6~gU&%!QCoU=R*H|_n~e#B$aVvSu@5B6-oyg=0d)YWaa z#gkK~OXdPX;gG(ymHB*78UfYh8)6;ZSHxx%dne^zCZQz01-Pf&3CT2ETy=bug zyLqX6n2u$7RUqS<37Y}y zrs>`4log%)Ql?b;jm+wK?-jD;4lZh+Jx%=2#usL1{yuyWIOCRD1a~m=p)+Y+U)A~9 zI#nMg{7gP`YuRGYh>5w00eNZ5*zT!i={;ZXt(nXf9Cnm*W6JR@N(ySJ>lXQh>L`_M zTXX8C=8Rq6>n=KfEZuScOf}cF`UP@p|NNX|Ipy3O%i@~Wu8l4Y+_Ba@A3sjDIsbX7 zOhI8ylWO*xgDeLZFfrb#>*JN=Z4wSxr!wv9hb?g-JMU_w3vIV}9sZZ=PuZ*LNfu$6 z%VRXtg?1Nw-*W#SyKdE{Sx%ChRU#Ip$JoUF4Lp+Hb~^3<*~O2OJwl{52^?^`zkc0M z>*%-HM-zTDP28M&#;<3;l1QK2=2ly;7g7?_3*K-v6)t64%eeV(TbIDJUssqbuAX@2 zc=6kkY&C&43;q@*)5)$(t_L2bs!1GwxHP86Tx6=A_L&&#@Pw0G&s7~)t8*|K2rc=a zr=qcTA#3A8xuou(Vg&(?q;4bQ8R@6F()kLvavP*y)#J+c_{6~B@nY#E$;94}JxwRQ zj!t(8=FL9lZDGOx!TXP>nS=G^?CUL)Kb^Z~w5e{1()`ece2fg7v)G@eE}3^H;@3L; zMM9P-wF-MU7z8%t?f>BC_q=HHzK<(aK1iEi&zm}Dk}1oR?)7}a=Y#^Eo{)PzKgGdA z8i0;_r3YzGi7^_P+}!SQhqAY;Nn@vUuW*cuU1??J~E`O-q`D)7onS zg4S#(#mc6qGY3GDID7Rmq)~wtds- z*W%Ase7PZesga4PYs>1%mYwyF-z!Jn4oo?)?7rOFsUJ8yMZCX>+)Zfu>=mMUsbPQQ zTfTj2PuuQDbStgZ{C=q3QR!oX-saHQq`T+67xGW!vpa5`$N1&WgMJU|xm$leH4pp2gcDm`{e5r1JRhS4t4@s1H|M;D0{P-}m^=H(|MqKbP-sINh>}>zL=v8G`Ti zPo8!Eag z=~ZbsyYab9>#j92`wJKU%B?iHxIweb?QO)SikV6IT>ncJ+IQ(NO#D8PDgKJfUUhBn zvR6lP8Qv+(7J8mKZ+=Kdr_k#a=6|M5sYv*F^q=+yl9MoKxi6p!eB`R&?!NaSHf z<`+4^5B`;nFJu)T&C~kew)2TzuvGh-zqWs0`Y-?TSp98@%S73#)+rrAbyw_l{ny^= zR}!_+)!zM)E${2;U7o51tJ9ZF)7fu1W9Aaux)o7&-!^~Xw-jF(|EzS~)5HAl7yS6L zyqY;>-TaKp`?&l{)2syJpHFyk=We<#kU3uis z%`h3x?~ggmy4J6$7Vv!VQ(ePZtoWd7!9Byjoqyli&fHjh@%16?iM8LV54HB}bjZAC zr~Ng<&^~9)hQ^g^?&Vcn`la{q41w^QR3q7Tk8&Gvj@);wm4_O5?ct{l9|M)gxwVOpXYe zBXB#KPx!mL&*}T?ZgtB|oXXqhz5LAc=u}Z-XD*`^Q%~PaSIb~|c=7zhY`=*-%q!oi z=RcS^qp$h=_sVSdQxj_bcAdDjEI!z~Xz87HGrMi}LTqP^ z(}L&zm?ZLZa**pJm$=~Wj@AiV^*Rr1Y}lI9F}r_lZ<7Aohea%sn%)2T85}euq!pMH z6hsa&2r=&KXH}m1JNi?L<*}F6@r`oIB7e60Q}$=xkn$&-F@noafg@tmksualAA<>n zVmeF`oJHOp>5AKIBeOGp@g8uP#3N!T+;lzr;ZK?0mJ%7$FG%OKUfr7-J#Y2YMH#1e zeOhw3T!_`z_)W{qz=?H{s@1=Wj!)eCUe&VaNr&*Ad+Q}Pu09)K>Dp0dtipJlq0i@q z!|G={j4xm5WOa(1e^c6ecKWmlQ9tS;y6x&OZp{5wbm(^EE^AjQhTXpA{N=CoE^cgN ze9$TT<}*{b0y|?1yWzoi@8&+7IX7=UqpHWW2jAx9Sa955KS^N6xjjecuUc~B`E!}$ zic>@{u8!U{;WWeS+|@T&s|tLanQF>X^)0`-DK%uCtXy%3|L^bBiAUTfDIdueHWd`y zy=VP4_2U{bY1a-NPd)t5KCbPftm=HPK%LK)$8~CU#MV~4-4k+>qe)mu!0_thq%U{x zoi5|n6=V`DKE%T1w)aTgQvSBK`IoyHSY8Wx?6(v6`0zF3&qr7OwH3`4b4gnn!ld7E zM`zZIZnx&5Qjc|fs~u9@_!Zw?n|6gM;q-|MTDqkS|J3$p2F;B1nXe|zYaKoDu>7ed zch8>>Y@TKq&0xk5$nceG{mQ}>3+o^Cu$(&5KevkQs#e6#uGK>SoW8xf>!5k-%Ju|n z-z|q<+!Bg=<#F*}>iNBs&bvI%Oer{0zQeETdF!*iD->fB_MT*1d+{Jd(;;`iDn zHtDyD*X`ThVdwiXpl7P`t%h+X}R!Lrm&;fNp(t~~h@$NsK!X8QW-(!L|pK7Q%bTP$)Yy)EPLrsoxJ)IML5 zFS+vat~J99r*GjipYbUud=u0+Q~xdzGwZAI{>>Zp@5RnMzVvd~N9W0ICu5gfc6qM< zSK37CAZyGAPTw-Ei8UJkzZHi}c@~=N_jy~F%AWnVFUqve6|2rjnX03jG(|d5D1ra5 z`Z|+2%Z-$mo&HgBZq_l@{=0VXbi4wOT#v05|HS;FV%2`1l>(e1#rtH8ue)bUZ&R8% zcjrWP&kfVAn}sY5UNhnTRe6W@#pSD}Cx%z)F5Vi%a^vT`L&+Pqt`WO7mwno#y+<^z zX`g?4;#Hdr5*Qsr}kMFsyKQ!#jZZfDEK=@X6}Q~MEqVt2Wx z>GZdT^@;JfH+ry4S)R@N)$m5H&<&fUMJ319I-j+xk=Pqq%UG6_^IBT0^N4<_Xs)ge zC(r49K9{dY6ngJI#>6^jr&#Kd4Hjv;XM7M9`1x6dO=W`r%1Z9cq}A(em2Mo})>P=2 zySAWJ{Kc(%jqB8QFAuWZv~Yg$otfJEnT~J#5S*&KZ1tHv>f6~fF0st@P>2q((=&YH zZgu1HjIFQqufCZaE)=-r`SvBV@^UuF+kW19?fJuxOiOp1`aJ9Ix3CS}^Do}Ye7G+r znR)rr{jWUj>|>-FCBEC-6qi}*>9Tj*mfiKb;t5aBN8eDB{xxw<@6T7qb2%K__8$<7 z1XM=NuKBQTMw$`*7YI6<_P(zSN0# z3S6dHJebRL`5mKx*XlKIYhE{qxJ>nW!@Dp`)VN6Z_EaCiPIbZ6_rKk#e#})@W4FBS zgNxI~-T&U$bo6z3F);19sCAxuujH$%ql>Q3mz!@lWorwQv+{v$b1vP_kp9}- zr(L#Z^NySdxqp&7ZiORlYFRP@a`g<2<{4YDzIYvwoQeALq*K5cq8I*?L`~%-_PuWu2GvGOOy!KL$>6 zX<(T(=T6&;FF(UiX)6y!L!`kOAYz2Htvc1dLwzt zyAO7+9v!gynY?9sMDgzJPYhfaHOTHz2!6=qe_DUd!AWL}jQzE$-MuDH4c?z#tN;76 ztD5leYFDJol&t3IujhTg`|I&}C5G%5zf_LiV`Q0gK4~GR=H~0)>wEn7 z)LiCKXp~$y+jO4vu0KH+3e9hFmHyWAUH9;}U_%yfiCMd-bXnNO4kOu%W* zHowGAC&!ED5~L5EpTgK$+b(@1JT9Xz{!_!>&P|FwOwNwyrc2cCbbno){-aRXzW(pq zcZ=#*p2*)crRLkVr1Yd;?50Jh%og06_<8x9W%`E`LqFB-fBQtxELHEH_Oz8=H zeIonK-RR9*4=;Wa@a^&L<%VUiY&5-pYPz^EeB1EVbJJpOj*i3U^j}*i+t>44oqJ%` zb=k|MuE%=My;Hrdut#X$EvvNm*Y~}<)hn$PC&CzUZocj5E?=?EWr0hRZf^SEbTK69 z{rz_l@*)aeJQc;uKJZJ)KDfUAoyx)5QyU#4T@P<=w)(w(x!&?_d(lNVmd~m_&+~Or z;JIM)ojmQKhf`g8J}sE6d9*3EGbRTt)-5?><$nC3 zlGatG^j}|}XK}b1XI`y;y}4L{rI4ZCBq8gcL&&PL4?nNHkdp6ucy_F=bdQ^Ch+c5! z%XX)Wuio_<{yQ3^^snI0B-d#cZ)1{#*1S1kDf}qE=GTU&i%p-bXwBcN`+TNoedjNQ z(^oxjZ9gJ0`M`oc-X|;fO?El86 zTC3ia9o;`Sw%YLSmV5Jh4!>Ed7_WDsuIAp~OxehJZ)P0Vza|ysbSLxO&)9;v9E0<( zzVsHUG@h4T$8B!!vA|b9$NOyS#<0X+CARl|dwS11WgY6Z#5n9I%eNEC-{tPvXJv2l zZtBhO*t~U{-aU^XwIw}eR_0s93f=Nf-MMU#Q6XQlyVdRJ8|ig{MxDkCEycyr2Ul9o zyfkCpvAnk*`dd$VMe+ua3q zLFpI1CLVEeJ97CcKcZU^c(*GV81@ zWu47d?{>QMZc>uDQF!)v)-|)Q-A{^su=(b%Sl+*zwZXKcw(d>h!(%*BQ`hdU)^~nh z*qG_@fN#lC_PQm7^O>ETUm7(e)NuuVG2rXa*6!8bd`+Oi+PYVFzgRb1_s!)nu{k~#Hn}oIDwWW8bGF9$8a;0o*zvlw+tzQK1X&pP? zyWX;RiR+E9+M3IjM?r1+u7EwoX2nOv)y>%aof!a_x5C+^D3Sh<`N*XcT4=TBL!^Y)k~_ji})&q z?Ry`gryXQby3l@p@zxxJKMzk&k-IfHNZ<``bz;&(7fH?~i-K|F-7sxaXVK zPJaLRvn5xr?)UrsXMdi}T61IK^1$$#ZH0mRU;OH<7Mr0T_8p5#RX^;lRa+LxBbJ-6#x&{ve7D7c@AoP?p4z=KGCt;yEE7lnEuK)v zWL-Hi<&#H$?5sLJGgYj)=~|Wk0ox}BGM!!SIqtsDR{VA2mlL-%??$4K5dHku2{Ej=t+})=?-u$ht!Kl~rKP~sL z>K%^%XN&z7N@jd$o7y!aOlq@cw65#kZ!f9D{>DhOqX~8AI9VSg|zaJLnesRmn zy}8`x=M1l>B5ZSvA}3wBZqjkG`b#SB*Q#|s`kIF7&P9$ao?F`db;?pD^*uGDrtY|C zrzOfgOL^ymOP=$kMels;&3U(uUo2*-2cVku)(hF zVb6qlwQ)%$F`B&#V^(*s-n-jBbGQBB!pIc8Yi?3omx!|z?5apM2u`~Mbg+q6LF{RQd#bi;r4`$Vq)I9Kjtxb}K8UuJcelg77* zt6KT>f>(bP%r*G7_xX`aJaP7)e{Rm}`S{AS*f73genO>N&RI_-JMAqO0)OzvTOa&Y z@`N?w^RX?RyJ{EceeyE%o%m*%YsWQ#+qD+wSYBQYPOF@9d)GC_lQ-7r@n6j6`7YsM z+`)Fj(TV$=){$o-F-(#rcY{<4Ce6<8lv%&8AZgN6v7XmO_3y7uULv$~mOz&1+&`)e z$u865^7>78Tkt(u?vwmq^<7nZt)8yY&P&GM+XR~W|Lzhpj!B;W>8x9N(8~*DCR)vt zduDBTr2BGy;Kiiwv-2YC;`e^$`Q+}lLA_Y$fM%NuTe;t>hkhRSG-d{^ioCOK62t8=`*N^sf5rcYIFC!lO_0>X=ronQYmbdT#%@1+Me{|H;_Ncv{}4H*BUVcewWK zD{aIUG<+bLw)wfIgO1H1R+ds9eyYTrNjVEiFCpi@59?gv8k6-0Klcvh4 zf7idJrv6B9)4L{`U80B5)y`aFo3B< zrGe4=_Q`xDP;XXSuKf!`lX95%-1u$p3V#1IU)KJ`#Js!Xc_T{>zuf*`rVS2G`K9q& zwoi(U;q@r3OP<4c_zv^+KoRRLH&S*tKHs_PgKcSNKc|IQPEm78+PO}zqe@D>Z0)(v zZZ+I^{VQZeJKyiIXC0g48>K?GgVmoIFS2&dNT0Im1JmM=#$OYxBXitD?pUNvnf`bGZh0G-ZTIZ*UTd2O zoqKpEN$rFFD$B#mUoc#GXK!koraH$Y?)KhWK8F_6J-Ri)t3+nvT(u>szh|xxJ7e^# z?qT`=7S&(c6}A6sbo(0IWc)>*$UUC+;rWb@UL_0c6V)so`?omU;q7&oF)#HoeB%pEyDhOo_BZ2x|ZbdZ#vf({k^c@^Sv2quiK9cG?X!C z&hP&_Z_9xQox<)7tv>#Bc}G`UEfjM3xccjoZkfNAIKHKvFsW-}xFYSbJo8yySZ1Yn zmEe&F`*U9C9OmE@Zogt@%K7|iW#Qwy!Cli|?$Fue-QXWE!@}V1)1!NMl)8E=Uh!X0 zm^n**=|}w^d%rXGcHPY=XT9|=CbmF#@x%a>I{M`!7occSS zT#~Uh+;RP>b*=km{W9w>pBG&DIsaRY*TtecoA!Twx5Y^{QJ`UA?dQ3A_I zUcJKKGs`GA_XWSAd|~794aw$K?%t(0 z5`5m?O>5uhzjRXW9=Xlj6OHbasdx*_c$UVqORnd|ET#6X=i-hA&(!_R+Wh|A%gzNC zI`*7+9&KdEXgK3v>jPWAhC8S8^=Ip}ed6#>oK#_RiD_x#=iK^yrD&1f)aBti>IaLK zn=bmAyY={)%dFD((EcPqtD99)=eymb%Nw9-b!k+(WlQ zYqMo=f##C=3*^r1Qa{rAjX_gSbTYTFMuqcIcJ1)>si_4^w@ER?-;`EbadMaSypy#@ zpFe)QfF;$+H%ano{N&V1r-QE-r)almO*J)c{*=3U$Npxg*r^Yoyp$kV0e~Y_b{(8tX<&N^6`0G2CbZD4qJ=T)&n=JkJ;cIr) zJ#nA<_;QR788xq6rSnZfMmE+QXJYVSfhSSU~TRZAZBfT`Vr!M!;FG9bDX*=cqCK!XjkLvsb>`pjUTg~ z-7oC5ehu3aF*)~7?yJ6QI>_AD_!V3JZc9i(h%{H0vT#{=SXyxGp`FE!jfTX>{(eM^HzuKA6I4^ zxfvq(!^e*2`za}dm7=U$g@XSS+pK41h@P@nFgox8*ZD)P4~n!JIhd~ovoH#DU5RnZ zVJVOBSi8m8wmp0o$BB^53}@HgNK#4v%qPispknEh&k{@LDY9&NA-B%SSmVn}?LX@n zbKUw`W}3~Eva#zlTDwwn^TJ#2C7d^};ALoOlW9A(VC6kduZb;TB3FXWrI)h4*I&i5 z?)`F~P?u|u^wo9dZ(H}Q)#Bck1LiHsExeHz)J^06+H45o&|G)4N7!67RaPa?MAqx7 zZQR9-e8KM`d9elUAp!fRbeT_X(~Dy_t^T=wtLjZg-dpSm_7c0~r`Y|e3OTABsU%X6 z9drF+{IzPH85>q>k=m4Y>#jEp90Hs3*InXU+`%9_#oc1w zRqOW}uJ0om1=XIf*fUqdh*4pI$8H-T)%iMN>SwAuZPFgh?mo$WsQs64(e_!oi)3!T z?-251nZ7J$viE=67h5=_rk@SD`16Oj&dayAxa?J?{#ST=Ag$n9$Xh+6-d zmj|1F@!xh?K;lM-P*B^KoTEiHb5n=3@ z)Kkci6ZrDb!?0O1C*Mg|C0^b z7$2LiGd~^r*|}+(q#pOb(1Xus=e$+!EQ>tsSI*tWeJ;6b`!&PMQ^YclxJk_}o>DQ7 z@7C5`uG3xq=Dez(WT<*pN!V%Q`!iO@Qzvm`{5bR^TF-O0jgOa|q}RVq(!MUgV$Vlb zb{A(K`ZBrkjYUOjkJsMUYu%5ZDW7Zi^2LJC;w_0C?K_-OK3|hHI-t5*|NGPqx_o=T$_7+vLX^_8fbsaKVh>w&uLt3d_#_xOn$lwzlBx35_F-8=o4-n1OQ{Wo*3Je~8cE;Q=jvh!UR*uJHj zbG|a4YEvuSp&K;QIF!sv@en_`Q=XYFX`KPe?Kg|bIm;HaG>Afcc%;YFTHa!aaOMQ z0&5P|WbH-&l>N2u+gRzarXN%i=sFU_pxk6W;bT0jflx}ALqcNP9`1Y}E8YW>mNK*U zbQf1$VBJ|-TEFY$6^_HHdSN%p-bM)iD+$xQZrf(&CO@IELq9G&V_%I;^82ZkYnGWO zC2G`MI^urYZq+qtJ)~*U1h%}@OZB?cU6VT ze-WW1RmL^vD~in<@0`}U5p?{_d6RGcXM+o-*kms zH4@+`o1vc#EP7^uiCbRoc^|a|AV;2t6H`+yFX!+u(C{YTR$x^{pwl^ zi>cwe4x7(qR1ley5_&k|@H}R#Hxj(A+`(62W{z*&bw^ra;=UnykEj-{|CjQsnx9hS#7jxS2Gwz-TRoX7C^V2%H z^U>Al_x_692Lk=BeJ$JDn0V~i9f7lD9DD70mqjOSvu8Z7rTTcX+Cxi*85?eIWz8#i z<$iQZ^ed@})=QeV+t1j#>Vay#aPjroi@u9D7r)-Mbb6>k=f?RP^veEQUtmAwuxZJ2 z=SRIik{yq?>soz_*Q}^U#M4HGm z&e&)lveZm^OPko$aP9OKuDDGPuUO{)PMqvKHR0yA-k@s*SI<>{-nCpewC-NnRq?61 z8MB>|BrRSYjXY^#!M)4Ij5(j{_Vy3`4DY6fZ0}Gtyn18N_5TlYGVabbR1eFns;QUG zao@Lfip|NHsWVL`ujR z2cv&~wteva!oKIe53QHo(_}gzy(^4w%gqmuYTSF1lS=Ovf1M+G@EGfh{)#1CJ>PZK zuQ1*(!nwcjuEC5a**{~slQ&Ys3-ulr|n+!bcpEvLAm*oou++4)B&nSI=9%hK2uaYvwIX?@Ydvg^MNYi~|z zeeM!ceawSH2#@}rWM0yV8U*D%1w;*kg)71~(*NV6w ztKBtstHIS%K2~jMuT!%hDyg41bX;}sTK86?S9Rrz1*U?#lv#Rap5Lye{p!k#?F|Y- ztF4P(p8mBU-E`-Q%z`B6lXo}vyv*6ocU$h{qQ)oDcV&1t`?l#Zec~^RuyWU!_U82h ziw}whoePs^_di!(CL)-BFY@%V6-Ucdze}bZwJrU1%I2S7f~D=MpIH~&WlQfL;f(QZ zImf}6a#1y_{WsrDMgfDTsm#L0o${HL*&ohH6`Xu^nmOg(@xl>J_+sW6pstLwcir4m;T4SAQT%GI*`I@3A!Z!mq`b#lpCHE~~g z)7>uI0vp6q_D(kHENu07_e|F4p!wS!1;}~Z-oF_L$d1QGMZ%?;B^oK)SYBgh8&BgO-&HE$&N%pLA z$=Vpx;XUJe!IZO0_Me>;uD|D7{`V)%J{paiSl*?Gu4xzdy??gj?NftdW;fe=9+iGS z%oqL(^HG4r>V(-djkewN-5eq0vQ6u{UAz3w+50a~vzw-8YUcS~fWuYv_uEsCrcb`J zH*4E6(e{}~Pj&_(n-_QHv@eIyJyB$4w z6HZ9J7QdUi>&kDALky2aep#tGCH&qteG${<-Sg@v)jR$0M}qm^%+~xauWj<@1OTjx BfUW=l literal 0 HcmV?d00001 diff --git a/doc/qtdesignstudio/src/qtdesignstudio-terms.qdoc b/doc/qtdesignstudio/src/qtdesignstudio-terms.qdoc index d4dcb9a75ee..9c517f9846c 100644 --- a/doc/qtdesignstudio/src/qtdesignstudio-terms.qdoc +++ b/doc/qtdesignstudio/src/qtdesignstudio-terms.qdoc @@ -57,7 +57,7 @@ component height is adjusted automatically. Similarly, the opacity of a component can be bound to the opacity of its parent component. - \image qtquick-connection-editor-assignment.png "Binding Editor" + \image qtquick-connection-editor-assignment.webp "Binding Editor" Property bindings are created implicitly whenever a property is assigned a JavaScript expression. @@ -115,7 +115,7 @@ is to create \l{glossary-binding}{bindings} between the values of their \l{glossary-property}{properties}. - \image qmldesigner-connections.png "The Connections view" + \image qmldesigner-connections.webp "The Connections view" Read more about connections: @@ -211,9 +211,9 @@ the application. For example, the \l {Mouse Area} component has a \c clicked signal that is emitted whenever the mouse is clicked within the area. Since the signal name is \c clicked, the signal handler for receiving this signal - is named \c onClicked. + is named \c onClicked. Then it performs the defined \uicontrol {Action}. - \image washingmachineui-connections.png "Connections view, Connections tab" + \image qtquick-component-signal.webp "Component signal" Further, a signal is automatically emitted when the value of a \l{glossary-property}{property} changes. From 259bade04edd8f1d141c597c62a9554330888784 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Wed, 25 Oct 2023 14:24:43 +0200 Subject: [PATCH 101/242] QmlDesigner: Implement Jump To Code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Task-number: QDS-10866 Task-number: QDS-11045 Task-number: QDS-10865 Change-Id: I94f76bb6319e2711cde88adba2307a7539ac8617 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Henning Gründl --- .../ConnectionsDialogForm.qml | 19 ++++++++++---- .../qtcreator/qmldesigner/designericons.json | 3 +++ .../componentcore/componentcore_constants.h | 3 +++ .../componentcore/designeractionmanager.cpp | 26 ++++++++++++------- .../components/componentcore/designericons.h | 1 + .../componentcore/modelnodeoperations.cpp | 16 +++++++++--- .../componentcore/modelnodeoperations.h | 2 ++ .../components/componentcore/viewmanager.cpp | 11 ++++++++ .../components/componentcore/viewmanager.h | 1 + .../connectioneditor/connectionmodel.cpp | 17 ++++++++++-- .../connectioneditor/connectionmodel.h | 2 ++ .../components/texteditor/texteditorview.cpp | 11 +++++++- .../components/texteditor/texteditorview.h | 2 ++ .../texteditor/texteditorwidget.cpp | 24 ++++++++++------- .../components/texteditor/texteditorwidget.h | 3 +++ 15 files changed, 112 insertions(+), 29 deletions(-) diff --git a/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialogForm.qml b/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialogForm.qml index 7f410f1c3ff..883468947ff 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialogForm.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialogForm.qml @@ -216,11 +216,20 @@ Column { && backend.hasCondition && backend.hasElse } - HelperWidgets.AbstractButton { - id: editorButton - buttonIcon: StudioTheme.Constants.codeEditor_medium - tooltip: qsTr("Write the conditions for the components and the signals manually.") - onClicked: expressionDialogLoader.show() + Row { + + HelperWidgets.AbstractButton { + id: editorButton + buttonIcon: StudioTheme.Constants.codeEditor_medium + tooltip: qsTr("Write the conditions for the components and the signals manually.") + onClicked: expressionDialogLoader.show() + } + HelperWidgets.AbstractButton { + id: jumpToCodeButton + buttonIcon: StudioTheme.Constants.jumpToCode_medium + tooltip: qsTr("Jump to Code Editor.") + onClicked: backend.jumpToCode() + } } // Editor diff --git a/share/qtcreator/qmldesigner/designericons.json b/share/qtcreator/qmldesigner/designericons.json index 36a60ea3b31..2422559aeb5 100644 --- a/share/qtcreator/qmldesigner/designericons.json +++ b/share/qtcreator/qmldesigner/designericons.json @@ -69,6 +69,9 @@ "EnterComponentIcon": { "iconName": "editComponent_small" }, + "JumpToCodeIcon": { + "iconName": "jumpToCode_small" + }, "EventListIcon": { "iconName": "events_small" }, diff --git a/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h b/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h index 5644648336f..3f8cbd21346 100644 --- a/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h +++ b/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h @@ -64,6 +64,7 @@ const char layoutGridLayoutCommandId[] = "LayoutGridLayout"; const char layoutFillWidthCommandId[] = "LayoutFillWidth"; const char layoutFillHeightCommandId[] = "LayoutFillHeight"; const char goIntoComponentCommandId[] = "GoIntoComponent"; +const char jumpToCodeCommandId[] = "JumpToCode"; const char mergeTemplateCommandId[] = "MergeTemplate"; const char goToImplementationCommandId[] = "GoToImplementation"; const char makeComponentCommandId[] = "MakeComponent"; @@ -120,6 +121,7 @@ const char copyFormatDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", const char applyFormatDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Apply Formatting"); const char enterComponentDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Edit Component"); +const char JumpToCodeDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Jump To Code"); const char mergeTemplateDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Merge with Template"); const char goToImplementationDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Go to Implementation"); const char makeComponentDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Create Component"); @@ -220,6 +222,7 @@ enum PrioritiesEnum : int { EventListCategory, /******** Section *****************************/ AdditionsSection = 4000, + JumpToCode, EditAnnotations, AddMouseArea, MergeWithTemplate, diff --git a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp index 4f800bf939f..cb38789b633 100644 --- a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp +++ b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp @@ -1876,15 +1876,23 @@ void DesignerActionManager::createDefaultDesignerActions() addDesignerAction(new SeparatorDesignerAction(rootCategory, Priorities::ViewOprionsSection)); addDesignerAction(new SeparatorDesignerAction(rootCategory, Priorities::CustomActionsSection)); - addDesignerAction(new ModelNodeContextMenuAction( - goIntoComponentCommandId, - enterComponentDisplayName, - contextIcon(DesignerIcons::EnterComponentIcon), - rootCategory, - QKeySequence(Qt::Key_F2), - Priorities::ComponentActions + 2, - &goIntoComponentOperation, - &selectionIsComponent)); + addDesignerAction(new ModelNodeContextMenuAction(goIntoComponentCommandId, + enterComponentDisplayName, + contextIcon(DesignerIcons::EnterComponentIcon), + rootCategory, + QKeySequence(Qt::Key_F2), + Priorities::ComponentActions + 2, + &goIntoComponentOperation, + &selectionIsComponent)); + + addDesignerAction(new ModelNodeContextMenuAction(jumpToCodeCommandId, + JumpToCodeDisplayName, + contextIcon(DesignerIcons::JumpToCodeIcon), + rootCategory, + QKeySequence(Qt::Key_F4), + Priorities::JumpToCode, + &jumpToCodeOperation, + &singleSelection)); addDesignerAction(new ModelNodeContextMenuAction( editAnnotationsCommandId, diff --git a/src/plugins/qmldesigner/components/componentcore/designericons.h b/src/plugins/qmldesigner/components/componentcore/designericons.h index 6539462f8f7..c2d33523c08 100644 --- a/src/plugins/qmldesigner/components/componentcore/designericons.h +++ b/src/plugins/qmldesigner/components/componentcore/designericons.h @@ -67,6 +67,7 @@ public: EditIcon, EditLightIcon, EnterComponentIcon, + JumpToCodeIcon, EventListIcon, FitSelectedIcon, FitToViewIcon, diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp index bedfdb7dd14..fa2d7f854c4 100644 --- a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp +++ b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp @@ -1722,9 +1722,19 @@ bool validateEffect(const QString &effectPath) Utils::FilePath getImagesDefaultDirectory() { - return Utils::FilePath::fromString( - getAssetDefaultDirectory( - "images", QmlDesignerPlugin::instance()->documentManager().currentProjectDirPath().toString())); + return Utils::FilePath::fromString(getAssetDefaultDirectory( + "images", + QmlDesignerPlugin::instance()->documentManager().currentProjectDirPath().toString())); +} + +void jumpToCode(const ModelNode &modelNode) +{ + QmlDesignerPlugin::instance()->viewManager().jumpToCodeInTextEditor(modelNode); +} + +void jumpToCodeOperation(const SelectionContext &selectionState) +{ + jumpToCode(selectionState.currentSingleSelectedNode()); } } // namespace ModelNodeOperations diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.h b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.h index 94cd54c1ea3..d30a69b6049 100644 --- a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.h +++ b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.h @@ -56,6 +56,7 @@ private: namespace ModelNodeOperations { bool goIntoComponent(const ModelNode &modelNode); +void jumpToCode(const ModelNode &modelNode); void select(const SelectionContext &selectionState); void deSelect(const SelectionContext &selectionState); @@ -75,6 +76,7 @@ void setFillHeight(const SelectionContext &selectionState); void resetSize(const SelectionContext &selectionState); void resetPosition(const SelectionContext &selectionState); void goIntoComponentOperation(const SelectionContext &selectionState); +void jumpToCodeOperation(const SelectionContext &selectionState); void setId(const SelectionContext &selectionState); void resetZ(const SelectionContext &selectionState); void reverse(const SelectionContext &selectionState); diff --git a/src/plugins/qmldesigner/components/componentcore/viewmanager.cpp b/src/plugins/qmldesigner/components/componentcore/viewmanager.cpp index 26f9a974211..58f766b9157 100644 --- a/src/plugins/qmldesigner/components/componentcore/viewmanager.cpp +++ b/src/plugins/qmldesigner/components/componentcore/viewmanager.cpp @@ -31,6 +31,8 @@ #include +#include + #include #include #include @@ -516,6 +518,15 @@ void ViewManager::enableStandardViews() attachViewsExceptRewriterAndComponetView(); } +void ViewManager::jumpToCodeInTextEditor(const ModelNode &modelNode) +{ + ADS::DockWidget *dockWidget = qobject_cast( + d->textEditorView.widgetInfo().widget->parentWidget()); + if (dockWidget) + dockWidget->toggleView(true); + d->textEditorView.jumpToModelNode(modelNode); +} + void ViewManager::addView(std::unique_ptr &&view) { d->additionalViews.push_back(std::move(view)); diff --git a/src/plugins/qmldesigner/components/componentcore/viewmanager.h b/src/plugins/qmldesigner/components/componentcore/viewmanager.h index a3cacbe907e..935ff54cfdb 100644 --- a/src/plugins/qmldesigner/components/componentcore/viewmanager.h +++ b/src/plugins/qmldesigner/components/componentcore/viewmanager.h @@ -89,6 +89,7 @@ public: void disableStandardViews(); void enableStandardViews(); + void jumpToCodeInTextEditor(const ModelNode &modelNode); QList views() const; private: // functions diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp index 5791b8ad6c6..55f9f5dcae8 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp @@ -9,16 +9,18 @@ #include #include #include +#include #include #include #include #include +#include +#include #include #include #include #include -#include -#include +#include #include @@ -918,6 +920,17 @@ void ConnectionModelBackendDelegate::update() QTC_ASSERT(model, return ); } +void ConnectionModelBackendDelegate::jumpToCode() +{ + ConnectionModel *model = qobject_cast(parent()); + + QTC_ASSERT(model, return ); + QTC_ASSERT(model->connectionView()->isAttached(), return ); + SignalHandlerProperty signalHandlerProperty = model->signalHandlerPropertyForRow(currentRow()); + + ModelNodeOperations::jumpToCode(signalHandlerProperty.parentModelNode()); +} + void ConnectionModelBackendDelegate::handleException() { QMessageBox::warning(nullptr, tr("Error"), m_exceptionError); diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.h b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.h index d25d5aa2d3b..1aa94f77337 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.h +++ b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.h @@ -293,6 +293,8 @@ public: void setCurrentRow(int i); void update(); + Q_INVOKABLE void jumpToCode(); + signals: void currentRowChanged(); void actionTypeChanged(); diff --git a/src/plugins/qmldesigner/components/texteditor/texteditorview.cpp b/src/plugins/qmldesigner/components/texteditor/texteditorview.cpp index 0826df54848..cdf09c14933 100644 --- a/src/plugins/qmldesigner/components/texteditor/texteditorview.cpp +++ b/src/plugins/qmldesigner/components/texteditor/texteditorview.cpp @@ -34,11 +34,12 @@ #include #include +#include #include #include +#include #include #include -#include namespace QmlDesigner { @@ -292,6 +293,14 @@ void TextEditorView::reformatFile() } } +void TextEditorView::jumpToModelNode(const ModelNode &modelNode) +{ + m_widget->jumpToModelNode(modelNode); + + m_widget->window()->windowHandle()->requestActivate(); + m_widget->textEditor()->widget()->setFocus(); +} + void TextEditorView::instancePropertyChanged(const QList > &/*propertyList*/) { } diff --git a/src/plugins/qmldesigner/components/texteditor/texteditorview.h b/src/plugins/qmldesigner/components/texteditor/texteditorview.h index e50372279d9..33a5ec8276c 100644 --- a/src/plugins/qmldesigner/components/texteditor/texteditorview.h +++ b/src/plugins/qmldesigner/components/texteditor/texteditorview.h @@ -81,6 +81,8 @@ public: void reformatFile(); + void jumpToModelNode(const ModelNode &modelNode); + private: QPointer m_widget; Internal::TextEditorContext *m_textEditorContext; diff --git a/src/plugins/qmldesigner/components/texteditor/texteditorwidget.cpp b/src/plugins/qmldesigner/components/texteditor/texteditorwidget.cpp index 122228f2086..2da33348f2c 100644 --- a/src/plugins/qmldesigner/components/texteditor/texteditorwidget.cpp +++ b/src/plugins/qmldesigner/components/texteditor/texteditorwidget.cpp @@ -98,6 +98,20 @@ void TextEditorWidget::updateSelectionByCursorPosition() m_blockRoundTrip = false; } +void TextEditorWidget::jumpToModelNode(const ModelNode &modelNode) +{ + RewriterView *rewriterView = m_textEditorView->model()->rewriterView(); + + m_blockCursorSelectionSynchronisation = true; + const int nodeOffset = rewriterView->nodeOffset(modelNode); + if (nodeOffset > 0) { + int line, column; + m_textEditor->editorWidget()->convertPosition(nodeOffset, &line, &column); + m_textEditor->editorWidget()->gotoLine(line + 1, column); + } + m_blockCursorSelectionSynchronisation = false; +} + void TextEditorWidget::jumpTextCursorToSelectedModelNode() { if (m_blockRoundTrip) @@ -115,15 +129,7 @@ void TextEditorWidget::jumpTextCursorToSelectedModelNode() selectedNode = m_textEditorView->selectedModelNodes().constFirst(); if (selectedNode.isValid()) { - RewriterView *rewriterView = m_textEditorView->model()->rewriterView(); - - const int nodeOffset = rewriterView->nodeOffset(selectedNode); - if (nodeOffset > 0) { - int line, column; - m_textEditor->editorWidget()->convertPosition(nodeOffset, &line, &column); - // line has to be 1 based, column 0 based! - m_textEditor->editorWidget()->gotoLine(line, column - 1); - } + jumpToModelNode(selectedNode); } m_updateSelectionTimer.stop(); } diff --git a/src/plugins/qmldesigner/components/texteditor/texteditorwidget.h b/src/plugins/qmldesigner/components/texteditor/texteditorwidget.h index 947826ab598..8c5f5b2dd86 100644 --- a/src/plugins/qmldesigner/components/texteditor/texteditorwidget.h +++ b/src/plugins/qmldesigner/components/texteditor/texteditorwidget.h @@ -5,6 +5,8 @@ #include #include +#include + #include #include #include @@ -43,6 +45,7 @@ public: int currentLine() const; void setBlockCursorSelectionSynchronisation(bool b); + void jumpToModelNode(const ModelNode &modelNode); protected: bool eventFilter(QObject *object, QEvent *event) override; From ff603fd1e0e5642bde283fcb48f36472c9543d11 Mon Sep 17 00:00:00 2001 From: Pranta Dastider Date: Wed, 25 Oct 2023 11:26:33 +0200 Subject: [PATCH 102/242] QmlDesigner: Update the LoginUI States Tutorial Doc This patch updates the LoginUI States Tutorial document. Removes an old image and adds a new image to support the current connection view update. Also, changes some texts to keep the process relevant. Fixes: QDS-10936 Change-Id: I52f403a1f4a4a7c36b5346069f50c1a633c437cc Reviewed-by: Leena Miettinen --- .../doc/images/loginui3-connections.png | Bin 4260 -> 0 bytes .../doc/images/loginui3-connections.webp | Bin 0 -> 21144 bytes doc/qtdesignstudio/examples/doc/loginui3.qdoc | 21 +++++++++++------- 3 files changed, 13 insertions(+), 8 deletions(-) delete mode 100644 doc/qtdesignstudio/examples/doc/images/loginui3-connections.png create mode 100644 doc/qtdesignstudio/examples/doc/images/loginui3-connections.webp diff --git a/doc/qtdesignstudio/examples/doc/images/loginui3-connections.png b/doc/qtdesignstudio/examples/doc/images/loginui3-connections.png deleted file mode 100644 index 6ebf2f0e4701318ad39c749e75718a55c2aec1b1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4260 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 O1B0ilpUXO@geCwWWMj(! diff --git a/doc/qtdesignstudio/examples/doc/images/loginui3-connections.webp b/doc/qtdesignstudio/examples/doc/images/loginui3-connections.webp new file mode 100644 index 0000000000000000000000000000000000000000..bc7199ec52a767d58590d40b07af23adab50640f GIT binary patch literal 21144 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 literal 0 HcmV?d00001 diff --git a/doc/qtdesignstudio/examples/doc/loginui3.qdoc b/doc/qtdesignstudio/examples/doc/loginui3.qdoc index 59501c199dd..c66d2b7e484 100644 --- a/doc/qtdesignstudio/examples/doc/loginui3.qdoc +++ b/doc/qtdesignstudio/examples/doc/loginui3.qdoc @@ -135,17 +135,22 @@ \uicontrol {Connections} to open the \uicontrol Connections view. \li Select \e createAccount in \uicontrol Navigator. \li In the \uicontrol Connections tab, select the \inlineimage icons/plus.png - button to add the action that the \c onClicked signal handler of - \e createAccount should apply. - \li Double-click the value \uicontrol Action column and select - \uicontrol {Change state to createAccount} in the drop-down menu. - \note Or, you can right-click the \e createAccount button in \l Navigator. - Then select \uicontrol {Connections} > \uicontrol {Add signal handler} > - \uicontrol {clicked} > \uicontrol {Change State to createAccount}. - \image loginui3-connections.png "Connections tab" + button to open the connection setup options. + \li Set \uicontrol Signal to \c clicked, \uicontrol Action to + \c {Change State}, \uicontrol {State Group} to \c rectangle and + \uicontrol State to \c createAccount in the respective + drop-down menus. + \li Select the \inlineimage icons/close.png + button to close the connection setup options. + + \image loginui3-connections.webp "Connections tab" + \li Select \uicontrol File > \uicontrol Save or press \key {Ctrl+S} to save your changes. + \note Or, you can right-click the \e createAccount button in \l Navigator. + Then select \uicontrol {Connections} > \uicontrol {Add signal handler} > + \uicontrol {clicked} > \uicontrol {Change State to createAccount}. \endlist In the live preview, you can now click the \uicontrol {Create Account} From 5f0908ea2b26aff43c5064f3db5aeebbed8788e5 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Wed, 25 Oct 2023 17:21:22 +0200 Subject: [PATCH 103/242] QmlDesigner: Sync with states and PropertyChanges MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If we are in a state, we sync the texteditor not with the node, but with the state or property change if it does exist. Task-number: QDS-7261 Change-Id: I71a7c53f4d83ebc4d6fcb95283012898505a1c2a Reviewed-by: Henning Gründl --- .../components/texteditor/texteditorwidget.cpp | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/plugins/qmldesigner/components/texteditor/texteditorwidget.cpp b/src/plugins/qmldesigner/components/texteditor/texteditorwidget.cpp index 2da33348f2c..ec284caf8d6 100644 --- a/src/plugins/qmldesigner/components/texteditor/texteditorwidget.cpp +++ b/src/plugins/qmldesigner/components/texteditor/texteditorwidget.cpp @@ -4,10 +4,11 @@ #include "texteditorwidget.h" #include "utils/uniqueobjectptr.h" +#include +#include +#include #include #include -#include -#include #include #include @@ -129,7 +130,17 @@ void TextEditorWidget::jumpTextCursorToSelectedModelNode() selectedNode = m_textEditorView->selectedModelNodes().constFirst(); if (selectedNode.isValid()) { - jumpToModelNode(selectedNode); + auto currentState = m_textEditorView->currentState(); + if (currentState.isBaseState()) { + jumpToModelNode(selectedNode); + } else { + if (currentState.affectsModelNode(selectedNode)) { + auto propertyChanges = currentState.propertyChanges(selectedNode); + jumpToModelNode(propertyChanges.modelNode()); + } else { + jumpToModelNode(currentState.modelNode()); + } + } } m_updateSelectionTimer.stop(); } From a8ee60048c30f33d7b44326955d023bcc3924975 Mon Sep 17 00:00:00 2001 From: Amr Essam Date: Wed, 25 Oct 2023 18:05:26 +0300 Subject: [PATCH 104/242] QmlDesigner: Fix important issues to Effect Maker A first shot of fixes Change-Id: I7c175edb601fc5880805cd6496e7c8c8a10df33d Reviewed-by: Mahmoud Badri --- .../EffectCompositionNodeUniform.qml | 2 +- src/plugins/effectmakernew/effectmakermodel.cpp | 8 ++++---- src/plugins/effectmakernew/uniform.cpp | 6 ++++-- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectCompositionNodeUniform.qml b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectCompositionNodeUniform.qml index 7c632730ccb..978fd95358c 100644 --- a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectCompositionNodeUniform.qml +++ b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectCompositionNodeUniform.qml @@ -28,7 +28,7 @@ Item { valueLoader.source = "ValueBool.qml" else if (uniformType === "color") valueLoader.source = "ValueColor.qml" - else if (uniformType === "image") + else if (uniformType === "sampler2D") valueLoader.source = "ValueImage.qml" else if (uniformType === "define") valueLoader.source = "ValueDefine.qml" diff --git a/src/plugins/effectmakernew/effectmakermodel.cpp b/src/plugins/effectmakernew/effectmakermodel.cpp index e3d8017d01f..1cdbcf94efb 100644 --- a/src/plugins/effectmakernew/effectmakermodel.cpp +++ b/src/plugins/effectmakernew/effectmakermodel.cpp @@ -774,7 +774,7 @@ void EffectMakerModel::updateCustomUniforms() for (Uniform *uniform : uniforms) { // TODO: Check if uniform is already added. const bool isDefine = uniform->type() == Uniform::Type::Define; - QString type = Uniform::typeToProperty(uniform->type()); + QString propertyType = Uniform::typeToProperty(uniform->type()); QString value = valueAsString(*uniform); QString bindedValue = valueAsBinding(*uniform); // When user has set custom uniform value, use it as-is @@ -799,7 +799,7 @@ void EffectMakerModel::updateCustomUniforms() QString boundValueString = bindedValue.isEmpty() ? QString() : QString(": %1").arg(bindedValue); // Custom values are not readonly, others inside the effect can be QString readOnly = uniform->useCustomValue() ? QString() : QStringLiteral("readonly "); - previewEffectPropertiesString += " " + readOnly + "property " + type + " " + previewEffectPropertiesString += " " + readOnly + "property " + propertyType + " " + propertyName + boundValueString + '\n'; // Define type properties are not added into exports if (!isDefine) { @@ -811,11 +811,11 @@ void EffectMakerModel::updateCustomUniforms() exportedEffectPropertiesString += QStringLiteral(" // ") + line + '\n'; } exportedEffectPropertiesString += QStringLiteral(" ") + readOnly - + "property " + type + " " + propertyName + + "property " + propertyType + " " + propertyName + boundValueString + '\n'; } else { // Custom values are not added into root - exportedRootPropertiesString += " property " + type + " " + propertyName + exportedRootPropertiesString += " property " + propertyType + " " + propertyName + valueString + '\n'; exportedEffectPropertiesString += QStringLiteral(" ") + readOnly + "property alias " + propertyName diff --git a/src/plugins/effectmakernew/uniform.cpp b/src/plugins/effectmakernew/uniform.cpp index d658383eeea..631421bc53b 100644 --- a/src/plugins/effectmakernew/uniform.cpp +++ b/src/plugins/effectmakernew/uniform.cpp @@ -1,4 +1,4 @@ -// Copyright (C) 2023 The Qt Company Ltd. +// 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 "uniform.h" @@ -270,8 +270,10 @@ QString Uniform::stringFromType(Uniform::Type type) return "vec2"; else if (type == Type::Vec3) return "vec3"; - else if (type == Type::Vec4 || type == Type::Color) + else if (type == Type::Vec4) return "vec4"; + else if (type == Type::Color) + return "color"; else if (type == Type::Sampler) return "sampler2D"; else if (type == Type::Define) From 117ae598617dd6a41a91ae571b1e09d04093c6c4 Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Wed, 25 Oct 2023 14:15:46 +0300 Subject: [PATCH 105/242] EffectMaker: Fix window appears in the back on macOS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes: QDS-11035 Change-Id: I266e663a85f779a9a1e5ad2040643b3a54425b3b Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Henning Gründl --- .../qmldesigner/effectMakerQmlSources/EffectNodesComboBox.qml | 2 +- .../qmldesigner/effectMakerQmlSources/PreviewImagesComboBox.qml | 2 +- .../imports/StudioControls/TopLevelComboBox.qml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectNodesComboBox.qml b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectNodesComboBox.qml index b076e152cc0..0827b20c1e3 100644 --- a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectNodesComboBox.qml +++ b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectNodesComboBox.qml @@ -47,7 +47,7 @@ StudioControls.ComboBox { width: row.width + 2 // 2: scrollView left and right 1px margins height: Math.min(800, Math.min(row.height + 2, Screen.height - y - 40)) // 40: some bottom margin to cover OS bottom toolbar - flags: Qt.Dialog | Qt.FramelessWindowHint + flags: Qt.Dialog | Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint onActiveFocusItemChanged: { if (!window.activeFocusItem && !root.indicator.hover && root.popup.opened) diff --git a/share/qtcreator/qmldesigner/effectMakerQmlSources/PreviewImagesComboBox.qml b/share/qtcreator/qmldesigner/effectMakerQmlSources/PreviewImagesComboBox.qml index a53a923ca6b..bcf4efbd4d9 100644 --- a/share/qtcreator/qmldesigner/effectMakerQmlSources/PreviewImagesComboBox.qml +++ b/share/qtcreator/qmldesigner/effectMakerQmlSources/PreviewImagesComboBox.qml @@ -67,7 +67,7 @@ StudioControls.ComboBox { width: col.width + 2 // 2: scrollView left and right 1px margins height: Math.min(800, Math.min(col.height + 2, Screen.height - y - 40)) // 40: some bottom margin to cover OS bottom toolbar - flags: Qt.Dialog | Qt.FramelessWindowHint + flags: Qt.Dialog | Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint onActiveFocusItemChanged: { if (!window.activeFocusItem && !root.indicator.hover && root.popup.opened) diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/TopLevelComboBox.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/TopLevelComboBox.qml index ea80abe7ea3..d2dc962c306 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/TopLevelComboBox.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/TopLevelComboBox.qml @@ -110,7 +110,7 @@ T.ComboBox { width: control.listView.width height: control.listView.height + 2 * control.style.borderWidth visible: false - flags: Qt.FramelessWindowHint | Qt.Dialog | Qt.NoDropShadowWindowHint + flags: Qt.FramelessWindowHint | Qt.Dialog | Qt.NoDropShadowWindowHint | Qt.WindowStaysOnTopHint modality: Qt.NonModal transientParent: null color: "transparent" From 9d00f046ce2da08b451482546ce5f4de148aae23 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Tue, 24 Oct 2023 17:26:29 +0300 Subject: [PATCH 106/242] QmlDesigner: Change the split view toggle hotkey Fixes: QDS-11040 Change-Id: Iaf9e41b4f7d7386706d3c095f20609c65da2e50f Reviewed-by: Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Mahmoud Badri --- src/plugins/qmldesigner/components/edit3d/edit3dview.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp index 79765aa25f8..96711162bcd 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp +++ b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp @@ -1026,7 +1026,7 @@ void Edit3DView::createEdit3DActions() View3DActionType::SplitViewToggle, QCoreApplication::translate("SplitViewToggleAction", "Toggle Split View On/Off"), - QKeySequence(Qt::Key_4), + QKeySequence(Qt::CTRL | Qt::ALT | Qt::Key_Q), true, false, toolbarIcon(DesignerIcons::ScaleToolIcon), // TODO Placeholder, needs proper icon From a983679e434c0c44d4f349bb59d9caf0dd48b1cf Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Tue, 24 Oct 2023 17:39:44 +0300 Subject: [PATCH 107/242] QmlDesigner: Show negative axis label when axis is pointing at camera Fixes: QDS-11016 Change-Id: Iea1db2413aabbb2f4a5e166533e4436273fc775b Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Reviewed-by: Mahmoud Badri --- .../qml2puppet/mockfiles/qt6/OriginGizmo.qml | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/tools/qml2puppet/mockfiles/qt6/OriginGizmo.qml b/src/tools/qml2puppet/mockfiles/qt6/OriginGizmo.qml index bb055fa29a9..b2658e32bf6 100644 --- a/src/tools/qml2puppet/mockfiles/qt6/OriginGizmo.qml +++ b/src/tools/qml2puppet/mockfiles/qt6/OriginGizmo.qml @@ -205,8 +205,10 @@ Item { width: ballBackground.subBallWidth offset: ballBackground.subBallOffset labelText: qsTr("-X") - labelColor: stylePalette.brightBall - labelVisible: hovered + labelColor: hovered ? stylePalette.brightBall : stylePalette.dimBall + labelVisible: hovered || _generalHelper.compareQuaternions( + root.targetNode.sceneRotation, + root.quaternionForAxis(OriginGizmo.Axis.NegativeX)) color: Qt.rgba(stylePalette.xAxis.r, stylePalette.xAxis.g, stylePalette.xAxis.b, z + 1 * 0.5) border.color: stylePalette.xAxis border.width: 2 @@ -227,8 +229,10 @@ Item { width: ballBackground.subBallWidth offset: ballBackground.subBallOffset labelText: qsTr("-Y") - labelColor: stylePalette.brightBall - labelVisible: hovered + labelColor: hovered ? stylePalette.brightBall : stylePalette.dimBall + labelVisible: hovered || _generalHelper.compareQuaternions( + root.targetNode.sceneRotation, + root.quaternionForAxis(OriginGizmo.Axis.NegativeY)) color: Qt.rgba(stylePalette.yAxis.r, stylePalette.yAxis.g, stylePalette.yAxis.b, z + 1 * 0.5) border.color: stylePalette.yAxis border.width: 2 @@ -249,8 +253,10 @@ Item { width: ballBackground.subBallWidth offset: ballBackground.subBallOffset labelText: qsTr("-Z") - labelColor: stylePalette.brightBall - labelVisible: hovered + labelColor: hovered ? stylePalette.brightBall : stylePalette.dimBall + labelVisible: hovered || _generalHelper.compareQuaternions( + root.targetNode.sceneRotation, + root.quaternionForAxis(OriginGizmo.Axis.NegativeZ)) color: Qt.rgba(stylePalette.zAxis.r, stylePalette.zAxis.g, stylePalette.zAxis.b, z + 1 * 0.5) border.color: stylePalette.zAxis border.width: 2 From 7713f35fcdfe8f99310fa059f328a51cd07701d2 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Wed, 22 Feb 2023 14:22:08 +0100 Subject: [PATCH 108/242] QmlDesigner: Add DropArea to component library MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Task-number: QDS-7282 Change-Id: I996cea7f21c557c3df41ab61bd9984862c243f29 Reviewed-by: Henning Gründl --- .../qtquickplugin/images/drop-area-16px.png | Bin 0 -> 205 bytes .../qtquickplugin/images/drop-area-24px.png | Bin 0 -> 490 bytes .../qtquickplugin/images/drop-area-24px@2x.png | Bin 0 -> 638 bytes .../qmldesigner/qtquickplugin/qtquickplugin.qrc | 3 +++ .../qmldesigner/qtquickplugin/quick.metainfo | 16 ++++++++++++++++ 5 files changed, 19 insertions(+) create mode 100644 src/plugins/qmldesigner/qtquickplugin/images/drop-area-16px.png create mode 100644 src/plugins/qmldesigner/qtquickplugin/images/drop-area-24px.png create mode 100644 src/plugins/qmldesigner/qtquickplugin/images/drop-area-24px@2x.png diff --git a/src/plugins/qmldesigner/qtquickplugin/images/drop-area-16px.png b/src/plugins/qmldesigner/qtquickplugin/images/drop-area-16px.png new file mode 100644 index 0000000000000000000000000000000000000000..278690f07f20fc8bd139360ae8ec63b7ccd46c37 GIT binary patch literal 205 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd7G?$phPQVgfdq5|d_r6q7#LQpTuCHyvufvO zU|`@c3GxeOVCLlIlhn}C);n_P!Rv3k&a8aTz`)?(>Eal|5uKdiH|L8Nm=oX=;>y6lumTONTnQ3@ zlUNxmSFV7d6)QjtkOmlrs7Dir2*S0%)PiV;Af|$qD>pO$v1edlFe(Z13ua(oWaE^O zkX2Ar(a_Q{FtM<5NJuPcXl-lnnJ{g^g2hYLZ`gg{$l3E3Z{2(F;?4UHzy7wmd|J-H zz;N2r#W6(VeCeduLQM(+Zr6>Bb%RYd>%3hQS=n3rzucQ$SpVbSga+e+gfhl87VP4N zS#8oT9ePhTvL=K^^f`7;Wi?FWThStMb^~Lxfan~arbzB=4aBg&KEzrQT@H&iAg2C)Bd3w!b!;G1*wQLAE$jx* dqHjN$&SohnwH8koVPIfj@O1TaS?83{1OUtXR2n1sYhn z5+n!_hmb2)pol;OAtXc!Vgj;8h&WsgL;-|^D1#_~h(Op7#c)}O6kHIj<_F{484L^z zmL)-c!3<2ytn54j0^$KR z=S!TMW7bG6rQ7h784ywYf! zmf7e3@@Ymtg3ewz+3bD(bV}>yM30121`p4`@GWa}v%MyAzmYs}g*DqNv~;4w;y6#s z&AbL%rrAD^zZq~~-3*Ofiv;gm`?;rSK24L%_L#`MiSfV%R%5TF-swII9;DuDT>I+G z%}9r{-}yfXA3VQeq0zA=0!yZ>5&h$AJ3%e+8fSHE|0V;4saUMeJf!|TEcoy zZ*N0@D$|F;J+D_zQYh&<6n&ZD{PRy&XIz=eu*dq=%`@dUUsq-phUbQ6mDDd{`Ct{q z)FJiY>9i$`MLRDaU-E<9V3%OSjJ-*Z?}`6j&i(QAhZ3v&-)Zbi{-tHrXfW4)`&EDO zI=_r;i|IS>@cZ|Fzi<3dze`r&)T`j&Yxk#DEm+|`tx99n@pV-jUib68I?VZ^&wl=g Yk_ks*xu#n%FfcH9y85}Sb4q9e03=aEZU6uP literal 0 HcmV?d00001 diff --git a/src/plugins/qmldesigner/qtquickplugin/qtquickplugin.qrc b/src/plugins/qmldesigner/qtquickplugin/qtquickplugin.qrc index 30d705ad980..2e2dbf7819f 100644 --- a/src/plugins/qmldesigner/qtquickplugin/qtquickplugin.qrc +++ b/src/plugins/qmldesigner/qtquickplugin/qtquickplugin.qrc @@ -119,5 +119,8 @@ images/spatial-audio-16.png images/spatial-audio-24.png images/spatial-audio-24@2x.png + images/drop-area-16px.png + images/drop-area-24px.png + images/drop-area-24px@2x.png diff --git a/src/plugins/qmldesigner/qtquickplugin/quick.metainfo b/src/plugins/qmldesigner/qtquickplugin/quick.metainfo index f1aeaa9ebbd..ce6f31e1940 100644 --- a/src/plugins/qmldesigner/qtquickplugin/quick.metainfo +++ b/src/plugins/qmldesigner/qtquickplugin/quick.metainfo @@ -105,6 +105,22 @@ MetaInfo { } } + Type { + name: "QtQuick.DropArea" + icon: ":/qtquickplugin/images/drop-area-16px.png" + + ItemLibraryEntry { + name: "Drop Area" + category: "a.Qt Quick - Basic" + libraryIcon: ":/qtquickplugin/drop-area-24px.png" + version: "2.0" + + Property { name: "width"; type: "int"; value: 100; } + Property { name: "height"; type: "int"; value: 100; } + toolTip: qsTr("Sets an area that serves as a drag target.") + } +} + Type { name: "QtQuick.Image" icon: ":/qtquickplugin/images/image-icon16.png" From e4301eccff4f4b48006b16d40c664c5c7d52a2cc Mon Sep 17 00:00:00 2001 From: Henning Gruendl Date: Thu, 26 Oct 2023 12:57:45 +0200 Subject: [PATCH 109/242] QmlDesigner: Fix DropArea item library icon path Change-Id: Ifdbda61f582d70cd26f50fdc7c8f4364eababdc0 Reviewed-by: Thomas Hartmann --- src/plugins/qmldesigner/qtquickplugin/quick.metainfo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/qmldesigner/qtquickplugin/quick.metainfo b/src/plugins/qmldesigner/qtquickplugin/quick.metainfo index ce6f31e1940..a25cdadf4ef 100644 --- a/src/plugins/qmldesigner/qtquickplugin/quick.metainfo +++ b/src/plugins/qmldesigner/qtquickplugin/quick.metainfo @@ -112,7 +112,7 @@ MetaInfo { ItemLibraryEntry { name: "Drop Area" category: "a.Qt Quick - Basic" - libraryIcon: ":/qtquickplugin/drop-area-24px.png" + libraryIcon: ":/qtquickplugin/images/drop-area-24px.png" version: "2.0" Property { name: "width"; type: "int"; value: 100; } From a81e90a7f4cabc31b81df96d68bc55c0afb529a0 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Wed, 25 Oct 2023 17:34:00 +0200 Subject: [PATCH 110/242] QmlDesigner: Implement JumpToCode in states editor MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Task-number: QDS-10867 Change-Id: I90975ad70567261899f047f2f02236bb993cfd2b Reviewed-by: Henning Gründl Reviewed-by: Reviewed-by: Qt CI Patch Build Bot --- share/qtcreator/qmldesigner/stateseditor/StateMenu.qml | 9 +++++++++ .../qmldesigner/stateseditor/StateThumbnail.qml | 3 +++ .../components/stateseditor/stateseditormodel.cpp | 6 ++++++ .../components/stateseditor/stateseditormodel.h | 1 + 4 files changed, 19 insertions(+) diff --git a/share/qtcreator/qmldesigner/stateseditor/StateMenu.qml b/share/qtcreator/qmldesigner/stateseditor/StateMenu.qml index d26572ebde6..c30538e31ed 100644 --- a/share/qtcreator/qmldesigner/stateseditor/StateMenu.qml +++ b/share/qtcreator/qmldesigner/stateseditor/StateMenu.qml @@ -44,6 +44,7 @@ StudioControls.Menu { signal remove() signal toggle() signal resetWhenCondition() + signal jumpToCode() signal editAnnotation() signal removeAnnotation() @@ -84,6 +85,14 @@ StudioControls.Menu { StudioControls.MenuSeparator {} + StudioControls.MenuItem { + enabled: !root.isBaseState + text: qsTr("Jump To Code") + onTriggered: root.jumpToCode() + } + + StudioControls.MenuSeparator {} + StudioControls.MenuItem { enabled: !root.isBaseState && root.hasWhenCondition text: qsTr("Reset when Condition") diff --git a/share/qtcreator/qmldesigner/stateseditor/StateThumbnail.qml b/share/qtcreator/qmldesigner/stateseditor/StateThumbnail.qml index 1d8147d0425..781ddc4601f 100644 --- a/share/qtcreator/qmldesigner/stateseditor/StateThumbnail.qml +++ b/share/qtcreator/qmldesigner/stateseditor/StateThumbnail.qml @@ -736,6 +736,9 @@ Item { onRemove: root.remove() onToggle: root.setPropertyChangesVisible(!root.propertyChangesVisible) onResetWhenCondition: statesEditorModel.resetWhenCondition(root.internalNodeId) + + onJumpToCode: StatesEditorBackend.statesEditorModel.jumpToCode() + onEditAnnotation: { StatesEditorBackend.statesEditorModel.setAnnotation(root.internalNodeId) stateMenu.hasAnnotation = root.checkAnnotation() diff --git a/src/plugins/qmldesigner/components/stateseditor/stateseditormodel.cpp b/src/plugins/qmldesigner/components/stateseditor/stateseditormodel.cpp index 0b53af121b8..2ec6f0e6a02 100644 --- a/src/plugins/qmldesigner/components/stateseditor/stateseditormodel.cpp +++ b/src/plugins/qmldesigner/components/stateseditor/stateseditormodel.cpp @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -246,6 +247,11 @@ bool StatesEditorModel::hasDefaultState() const return m_statesEditorView->hasDefaultState(); } +void StatesEditorModel::jumpToCode() +{ + ModelNodeOperations::jumpToCode(m_statesEditorView->currentState().modelNode()); +} + void StatesEditorModel::setAnnotation(int internalNodeId) { m_statesEditorView->setAnnotation(internalNodeId); diff --git a/src/plugins/qmldesigner/components/stateseditor/stateseditormodel.h b/src/plugins/qmldesigner/components/stateseditor/stateseditormodel.h index 8eff287c869..0e228e46bd8 100644 --- a/src/plugins/qmldesigner/components/stateseditor/stateseditormodel.h +++ b/src/plugins/qmldesigner/components/stateseditor/stateseditormodel.h @@ -50,6 +50,7 @@ public: Q_INVOKABLE void setStateAsDefault(int internalNodeId); Q_INVOKABLE void resetDefaultState(); Q_INVOKABLE bool hasDefaultState() const; + Q_INVOKABLE void jumpToCode(); Q_INVOKABLE void setAnnotation(int internalNodeId); Q_INVOKABLE void removeAnnotation(int internalNodeId); Q_INVOKABLE bool hasAnnotation(int internalNodeId) const; From 3564e645a26891a94de0a970b9d4dea344c57083 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Thu, 26 Oct 2023 14:17:17 +0200 Subject: [PATCH 111/242] QmlDesigner: Add QTC_ASSERT If the model is detached, because the application is shutdown, those could be already deleted. Change-Id: Ibee260da04ab19a3f0e5381996fe71e1884b9af2 Reviewed-by: Tim Jenssen Reviewed-by: Qt CI Patch Build Bot --- .../qmldesigner/components/formeditor/formeditorview.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/plugins/qmldesigner/components/formeditor/formeditorview.cpp b/src/plugins/qmldesigner/components/formeditor/formeditorview.cpp index 2a433e6764a..5d7afed39c3 100644 --- a/src/plugins/qmldesigner/components/formeditor/formeditorview.cpp +++ b/src/plugins/qmldesigner/components/formeditor/formeditorview.cpp @@ -233,6 +233,10 @@ void FormEditorView::nodeCreated(const ModelNode &node) void FormEditorView::cleanupToolsAndScene() { + QTC_ASSERT(m_scene, return ); + QTC_ASSERT(m_formEditorWidget, return ); + QTC_ASSERT(m_currentTool, return ); + m_currentTool->setItems(QList()); m_selectionTool->clear(); m_rotationTool->clear(); From d10f760a55b51ea6a8a7a34a9d79826467fbe562 Mon Sep 17 00:00:00 2001 From: Shrief Gabr Date: Tue, 24 Oct 2023 09:44:31 +0300 Subject: [PATCH 112/242] QmlDesigner: Connect collection save functionality to UI Task-number: QDS-10922 Change-Id: I9d2d930b8203b76d40e0ce1c83351e490509020e Reviewed-by: Mahmoud Badri Reviewed-by: Miikka Heikkinen Reviewed-by: Qt CI Patch Build Bot --- .../CollectionDetailsToolbar.qml | 61 +++++++++++++++++++ .../CollectionDetailsView.qml | 2 + .../CollectionView.qml | 1 + .../collectiondetailsmodel.cpp | 38 +++++++++--- .../collectioneditor/collectiondetailsmodel.h | 6 +- .../collectioneditor/collectionsourcemodel.h | 2 +- .../collectioneditor/collectionwidget.cpp | 1 + 7 files changed, 100 insertions(+), 11 deletions(-) diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsToolbar.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsToolbar.qml index 84cd0505046..f7c8ee1a7c8 100644 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsToolbar.qml +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsToolbar.qml @@ -2,15 +2,18 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 import QtQuick +import Qt.labs.platform as PlatformWidgets import HelperWidgets 2.0 as HelperWidgets import StudioControls 1.0 as StudioControls import StudioTheme 1.0 as StudioTheme +import CollectionEditorBackend Item { id: root property real iconHeight: 20 required property var model + required property var backend property int selectedRow: -1 implicitHeight: 30 @@ -96,6 +99,64 @@ Item { top: parent.top bottom: parent.bottom } + + IconButton { + icon: StudioTheme.Constants.updateContent_medium + tooltip: qsTr("Update existing file with changes") + enabled: root.model.collectionName !== "" + onClicked: + { + if (root.backend.selectedSourceAddress().indexOf("json") !== -1) + root.model.exportCollection(root.backend.selectedSourceAddress(), root.model.collectionName, "JSON") + else + root.model.exportCollection(root.backend.selectedSourceAddress(), root.model.collectionName, "CSV") + } + } + + IconButton { + icon: StudioTheme.Constants.export_medium + tooltip: qsTr("Export collection to a new file") + enabled: root.model.collectionName !== "" + onClicked: exportMenu.popup() + } + + StudioControls.Menu { + id: exportMenu + + StudioControls.MenuItem { + text: qsTr("Export as JSON") + onTriggered: + { + fileDialog.defaultSuffix = "json" + fileDialog.open() + } + } + + StudioControls.MenuItem { + text: qsTr("Export as CSV") + onTriggered: + { + fileDialog.defaultSuffix = "csv" + fileDialog.open() + } + } + } + } + + PlatformWidgets.FileDialog { + id: fileDialog + fileMode: PlatformWidgets.FileDialog.SaveFile + onAccepted: + { + var fileAddress = file.toString() + + if (fileAddress.indexOf("json") !== -1) + root.model.exportCollection(fileAddress, root.model.collectionName, "JSON") + else if (fileAddress.indexOf("csv") !== -1) + root.model.exportCollection(fileAddress, root.model.collectionName, "CSV") + + fileDialog.reject() + } } Rectangle { diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsView.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsView.qml index f12cacd97a5..ac57169905c 100644 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsView.qml +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsView.qml @@ -12,6 +12,7 @@ Rectangle { id: root required property var model + required property var backend implicitWidth: 600 implicitHeight: 400 @@ -60,6 +61,7 @@ Rectangle { CollectionDetailsToolbar { id: toolbar model: root.model + backend: root.backend Layout.fillWidth: true } diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionView.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionView.qml index 141abaa90e6..594930723a1 100644 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionView.qml +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionView.qml @@ -144,6 +144,7 @@ Item { CollectionDetailsView { model: root.collectionDetailsModel + backend: root.model anchors { left: collectionsRect.right right: parent.right diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.cpp index 41fdd8102bb..df69af21445 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.cpp @@ -392,6 +392,22 @@ void CollectionDetailsModel::loadCollection(const ModelNode &sourceNode, const Q } } +bool CollectionDetailsModel::exportCollection(const QString &path, const QString &collectionName, const QString &exportType) +{ + QUrl url(path); + QString fileAddress = url.isLocalFile() ? url.toLocalFile() : path; + + if (exportType == "JSON") { + QJsonArray content = m_currentCollection.getJsonCollection(); + return saveCollectionAsJson(fileAddress, content, collectionName); + } else if (exportType == "CSV") { + QString content = m_currentCollection.getCsvCollection(); + return saveCollectionAsCsv(fileAddress, content); + } + + return false; +} + void CollectionDetailsModel::updateEmpty() { bool isEmptyNow = rowCount() == 0; @@ -527,24 +543,32 @@ void CollectionDetailsModel::setCollectionName(const QString &newCollectionName) } } -bool CollectionDetailsModel::saveCollectionAsJson(const QString &collection, const QJsonArray &content, const QString &source) +bool CollectionDetailsModel::saveCollectionAsJson(const QString &path, const QJsonArray &content, const QString &collectionName) { - QFile sourceFile(source); - if (sourceFile.open(QFile::ReadWrite)) { + QFile sourceFile(path); + QJsonDocument document; + + if (sourceFile.exists() && sourceFile.open(QFile::ReadWrite)) { QJsonParseError jpe; - QJsonDocument document = QJsonDocument::fromJson(sourceFile.readAll(), &jpe); + document = QJsonDocument::fromJson(sourceFile.readAll(), &jpe); if (jpe.error == QJsonParseError::NoError) { QJsonObject collectionMap = document.object(); - collectionMap[collection] = content; + collectionMap[collectionName] = content; document.setObject(collectionMap); } sourceFile.resize(0); - if (sourceFile.write(document.toJson())) - return true; + + } else if (sourceFile.open(QFile::WriteOnly)) { + QJsonObject collection; + collection[collectionName] = content; + document.setObject(collection); } + if (sourceFile.write(document.toJson())) + return true; + return false; } diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.h b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.h index 220865717a4..92ec3850f37 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.h +++ b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.h @@ -53,15 +53,15 @@ public: Q_INVOKABLE bool selectColumn(int section); Q_INVOKABLE bool renameColumn(int section, const QString &newValue); Q_INVOKABLE bool setPropertyType(int column, const QString &newValue, bool force = false); - Q_INVOKABLE bool selectRow(int row); - Q_INVOKABLE void deselectAll(); static Q_INVOKABLE QStringList typesList(); void loadCollection(const ModelNode &sourceNode, const QString &collection); + Q_INVOKABLE bool exportCollection(const QString &path, const QString &collectionName, const QString &exportType); + signals: void collectionNameChanged(const QString &collectionName); void selectedColumnChanged(int); @@ -78,7 +78,7 @@ private: void setCollectionName(const QString &newCollectionName); void loadJsonCollection(const QString &source, const QString &collection); void loadCsvCollection(const QString &source, const QString &collectionName); - bool saveCollectionAsJson(const QString &collection, const QJsonArray &content, const QString &source); + bool saveCollectionAsJson(const QString &path, const QJsonArray &content, const QString &collectionName); bool saveCollectionAsCsv(const QString &path, const QString &content); QHash m_openedCollections; diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.h b/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.h index 20a7933e633..5a71635c874 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.h +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.h @@ -50,7 +50,7 @@ public: void updateNodeSource(const ModelNode &node); void updateNodeId(const ModelNode &node); - QString selectedSourceAddress() const; + Q_INVOKABLE QString selectedSourceAddress() const; signals: void selectedIndexChanged(int idx); diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp index da0ece6ee27..9ad61f71e5f 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp @@ -167,4 +167,5 @@ void CollectionWidget::warn(const QString &title, const QString &body) Q_ARG(QVariant, title), Q_ARG(QVariant, body)); } + } // namespace QmlDesigner From 37f00b47a94e0b04c2e0d9657573801ba248d4ab Mon Sep 17 00:00:00 2001 From: Aleksei German Date: Tue, 24 Oct 2023 17:54:50 +0200 Subject: [PATCH 113/242] QmlDesigner: Support AnimatedSprite type MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Task-number: QDS-11017 Change-Id: Icc7716a6b219e35a24b75633e7a67ad73240eee4 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Henning Gründl Reviewed-by: Thomas Hartmann --- .../QtQuick/AnimatedSpriteSpecifics.qml | 364 ++++++++++++++++++ .../propertyEditorQmlSources/quick.metainfo | 20 + .../images/animatedsprite-loading.png | Bin 0 -> 118 bytes .../qtquickplugin/qtquickplugin.qrc | 1 + .../qmldesigner/qtquickplugin/quick.metainfo | 20 + 5 files changed, 405 insertions(+) create mode 100644 share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/AnimatedSpriteSpecifics.qml create mode 100644 src/plugins/qmldesigner/qtquickplugin/images/animatedsprite-loading.png diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/AnimatedSpriteSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/AnimatedSpriteSpecifics.qml new file mode 100644 index 00000000000..1dbd0a343b7 --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/AnimatedSpriteSpecifics.qml @@ -0,0 +1,364 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import QtQuick +import QtQuick.Layouts +import HelperWidgets +import StudioControls as StudioControls +import StudioTheme as StudioTheme + +Column { + anchors.left: parent.left + anchors.right: parent.right + + Section { + caption: qsTr("Animated Sprite") + + anchors.left: parent.left + anchors.right: parent.right + + SectionLayout { + PropertyLabel { + text: qsTr("Source") + tooltip: qsTr("Adds an image from the local file system.") + } + + SecondColumnLayout { + UrlChooser { + backendValue: backendValues.source + } + + ExpandingSpacer {} + } + + PropertyLabel { + text: qsTr("Frame size") + tooltip: qsTr("Sets the width and height of the frame.") + blockedByTemplate: !(backendValues.frameWidth.isAvailable || backendValues.frameHeight.isAvailable) + } + + SecondColumnLayout { + SpinBox { + implicitWidth: StudioTheme.Values.twoControlColumnWidth + + StudioTheme.Values.actionIndicatorWidth + backendValue: backendValues.frameWidth + minimumValue: 0 + maximumValue: 8192 + decimals: 0 + enabled: backendValue.isAvailable + } + + Spacer { implicitWidth: StudioTheme.Values.controlLabelGap } + + ControlLabel { + //: The width of the animated sprite frame + text: qsTr("W", "width") + tooltip: qsTr("Width.") + enabled: backendValues.frameWidth.isAvailable + } + + Spacer { implicitWidth: StudioTheme.Values.controlGap } + + SpinBox { + implicitWidth: StudioTheme.Values.twoControlColumnWidth + + StudioTheme.Values.actionIndicatorWidth + backendValue: backendValues.frameHeight + minimumValue: 0 + maximumValue: 8192 + decimals: 0 + enabled: backendValue.isAvailable + } + + Spacer { implicitWidth: StudioTheme.Values.controlLabelGap } + + ControlLabel { + //: The height of the animated sprite frame + text: qsTr("H", "height") + tooltip: qsTr("Height.") + enabled: backendValues.frameHeight.isAvailable + } + /* + TODO QDS-4836 + Spacer { implicitWidth: StudioTheme.Values.controlGap } + + LinkIndicator2D {} + */ + ExpandingSpacer {} + } + + PropertyLabel { + text: qsTr("Frame coordinates") + tooltip: qsTr("Sets the coordinates of the first frame of the animated sprite.") + blockedByTemplate: !(backendValues.frameX.isAvailable || backendValues.frameY.isAvailable) + } + + SecondColumnLayout { + SpinBox { + implicitWidth: StudioTheme.Values.twoControlColumnWidth + + StudioTheme.Values.actionIndicatorWidth + backendValue: backendValues.frameX + minimumValue: 0 + maximumValue: 8192 + decimals: 0 + enabled: backendValue.isAvailable + } + + Spacer { implicitWidth: StudioTheme.Values.controlLabelGap } + + ControlLabel { + //: The width of the animated sprite frame + text: qsTr("X", "Frame X") + tooltip: qsTr("Frame X coordinate.") + enabled: backendValues.frameX.isAvailable + } + + Spacer { implicitWidth: StudioTheme.Values.controlGap } + + SpinBox { + implicitWidth: StudioTheme.Values.twoControlColumnWidth + + StudioTheme.Values.actionIndicatorWidth + backendValue: backendValues.frameY + minimumValue: 0 + maximumValue: 8192 + decimals: 0 + enabled: backendValue.isAvailable + } + + Spacer { implicitWidth: StudioTheme.Values.controlLabelGap } + + ControlLabel { + //: The height of the animated sprite frame + text: qsTr("Y", "Frame Y") + tooltip: qsTr("Frame Y coordinate.") + enabled: backendValues.frameY.isAvailable + } + /* + TODO QDS-4836 + Spacer { implicitWidth: StudioTheme.Values.controlGap } + + LinkIndicator2D {} + */ + ExpandingSpacer {} + } + + PropertyLabel { + text: qsTr("Frame count") + tooltip: qsTr("Sets the number of frames in this animated sprite.") + blockedByTemplate: !backendValues.frameCount.isAvailable + } + + SecondColumnLayout { + SpinBox { + implicitWidth: StudioTheme.Values.singleControlColumnWidth + + StudioTheme.Values.actionIndicatorWidth + backendValue: backendValues.frameCount + decimals: 0 + minimumValue: 0 + maximumValue: 10000 + enabled: backendValue.isAvailable + } + + ExpandingSpacer {} + } + + //frame rate OR frame duration OR frame sync should be used + //frame rate has priority over frame duration + //frame sync has priority over rate and duration + PropertyLabel { + text: qsTr("Frame rate") + tooltip: qsTr("Sets the number of frames per second to show in the animation.") + blockedByTemplate: !backendValues.frameRate.isAvailable + } + + SecondColumnLayout { + SpinBox { + implicitWidth: StudioTheme.Values.singleControlColumnWidth + + StudioTheme.Values.actionIndicatorWidth + backendValue: backendValues.frameRate + decimals: 2 + minimumValue: 0 + maximumValue: 1000 + enabled: backendValue.isAvailable + } + + ExpandingSpacer {} + } + + //frame duration OR frame rate OR frame sync should be used + //frame rate has priority over frame duration + //frame sync has priority over rate and duration + PropertyLabel { + text: qsTr("Frame duration") + tooltip: qsTr("Sets the duration of each frame of the animation in milliseconds.") + blockedByTemplate: !backendValues.frameDuration.isAvailable + } + + SecondColumnLayout { + SpinBox { + implicitWidth: StudioTheme.Values.singleControlColumnWidth + + StudioTheme.Values.actionIndicatorWidth + backendValue: backendValues.frameDuration + decimals: 0 + minimumValue: 0 + maximumValue: 100000 + enabled: backendValue.isAvailable + } + + ExpandingSpacer {} + } + + //frame sync OR frame rate OR frame duration should be used + //frame rate has priority over frame duration + //frame sync has priority over rate and duration + PropertyLabel { + text: qsTr("Frame sync") + tooltip: qsTr("Sets frame advancements one frame each time a frame is rendered to the screen.") + blockedByTemplate: !backendValues.frameSync.isAvailable + } + + SecondColumnLayout { + CheckBox { + text: backendValues.frameSync.valueToString + implicitWidth: StudioTheme.Values.twoControlColumnWidth + + StudioTheme.Values.actionIndicatorWidth + backendValue: backendValues.frameSync + enabled: backendValue.isAvailable + } + + ExpandingSpacer {} + } + + PropertyLabel { + text: qsTr("Loops") + tooltip: qsTr("After playing the animation this many times, the animation will automatically stop.") + blockedByTemplate: !backendValues.loops.isAvailable + } + + SecondColumnLayout { + SpinBox { + implicitWidth: StudioTheme.Values.singleControlColumnWidth + + StudioTheme.Values.actionIndicatorWidth + backendValue: backendValues.loops + decimals: 0 + minimumValue: 0 + maximumValue: 100000 + enabled: backendValue.isAvailable + } + + ExpandingSpacer {} + } + + PropertyLabel { + text: qsTr("Interpolate") + tooltip: qsTr("If true, interpolation will occur between sprite frames to make the animation appear smoother.") + blockedByTemplate: !backendValues.interpolate.isAvailable + } + + SecondColumnLayout { + CheckBox { + text: backendValues.interpolate.valueToString + implicitWidth: StudioTheme.Values.twoControlColumnWidth + + StudioTheme.Values.actionIndicatorWidth + backendValue: backendValues.interpolate + enabled: backendValue.isAvailable + } + + ExpandingSpacer {} + } + + PropertyLabel { + text: qsTr("Finish behavior") + tooltip: qsTr("Sets the behavior when the animation finishes on its own.") + blockedByTemplate: !backendValues.finishBehavior.isAvailable + } + + SecondColumnLayout { + ComboBox { + implicitWidth: StudioTheme.Values.singleControlColumnWidth + + StudioTheme.Values.actionIndicatorWidth + width: implicitWidth + scope: "AnimatedSprite" + model: ["FinishAtInitialFrame", "FinishAtFinalFrame"] + backendValue: backendValues.finishBehavior + enabled: backendValue.isAvailable + } + + ExpandingSpacer {} + } + + PropertyLabel { + text: qsTr("Reverse") + tooltip: qsTr("If true, the animation will be played in reverse.") + blockedByTemplate: !backendValues.reverse.isAvailable + } + + SecondColumnLayout { + CheckBox { + text: backendValues.reverse.valueToString + implicitWidth: StudioTheme.Values.twoControlColumnWidth + + StudioTheme.Values.actionIndicatorWidth + backendValue: backendValues.reverse + enabled: backendValue.isAvailable + } + + ExpandingSpacer {} + } + + PropertyLabel { + text: qsTr("Running") + tooltip: qsTr("Whether the sprite is animating or not.") + blockedByTemplate: !backendValues.running.isAvailable + } + + SecondColumnLayout { + CheckBox { + text: backendValues.running.valueToString + implicitWidth: StudioTheme.Values.twoControlColumnWidth + + StudioTheme.Values.actionIndicatorWidth + backendValue: backendValues.running + enabled: backendValue.isAvailable + } + + ExpandingSpacer {} + } + + PropertyLabel { + text: qsTr("Paused") + tooltip: qsTr("When paused, the current frame can be advanced manually.") + blockedByTemplate: !backendValues.paused.isAvailable + } + + SecondColumnLayout { + CheckBox { + text: backendValues.paused.valueToString + implicitWidth: StudioTheme.Values.twoControlColumnWidth + + StudioTheme.Values.actionIndicatorWidth + backendValue: backendValues.paused + enabled: backendValue.isAvailable + } + + ExpandingSpacer {} + } + + PropertyLabel { + text: qsTr("Current frame") + tooltip: qsTr("When paused, the current frame can be advanced manually by setting this property.") + blockedByTemplate: !backendValues.currentFrame.isAvailable + } + + SecondColumnLayout { + SpinBox { + implicitWidth: StudioTheme.Values.singleControlColumnWidth + + StudioTheme.Values.actionIndicatorWidth + backendValue: backendValues.currentFrame + decimals: 0 + minimumValue: 0 + maximumValue: 100000 + enabled: backendValue.isAvailable + } + + ExpandingSpacer {} + } + } + } +} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/quick.metainfo b/share/qtcreator/qmldesigner/propertyEditorQmlSources/quick.metainfo index f1aeaa9ebbd..f390f72260d 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/quick.metainfo +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/quick.metainfo @@ -140,6 +140,26 @@ MetaInfo { } } + Type { + name: "QtQuick.AnimatedSprite" + icon: ":/qtquickplugin/images/animated-image-icon16.png" + + ItemLibraryEntry { + name: "Animated Sprite" + category: "a.Qt Quick - Basic" + libraryIcon: ":/qtquickplugin/images/animated-image-icon.png" + version: "2.0" + + Property { name: "frameWidth"; type: "int"; value: 64; } + Property { name: "frameHeight"; type: "int"; value: 64; } + Property { name: "frameCount"; type: "int"; value: 4; } + Property { name: "frameDuration"; type: "int"; value: 500; } + Property { name: "source"; type: "QUrl"; value:"animatedsprite-loading.png"; } + ExtraFile { source: ":/qtquickplugin/images/animatedsprite-loading.png" } + toolTip: qsTr("Draws a sprite animation.") + } + } + Type { name: "QtQuick.BorderImage" icon: ":/qtquickplugin/images/border-image-icon16.png" diff --git a/src/plugins/qmldesigner/qtquickplugin/images/animatedsprite-loading.png b/src/plugins/qmldesigner/qtquickplugin/images/animatedsprite-loading.png new file mode 100644 index 0000000000000000000000000000000000000000..ff2bbbd140fc31c4de2eb530414d8153eb0715b4 GIT binary patch literal 118 zcmeAS@N?(olHy`uVBq!ia0y~yU}RumU~ph$W?*1AKiT>(0|NtFfKP~P>46Xb|No!p z`u8#e1B14wi(`n#@#G)=3= literal 0 HcmV?d00001 diff --git a/src/plugins/qmldesigner/qtquickplugin/qtquickplugin.qrc b/src/plugins/qmldesigner/qtquickplugin/qtquickplugin.qrc index 2e2dbf7819f..cb5346c0795 100644 --- a/src/plugins/qmldesigner/qtquickplugin/qtquickplugin.qrc +++ b/src/plugins/qmldesigner/qtquickplugin/qtquickplugin.qrc @@ -122,5 +122,6 @@ images/drop-area-16px.png images/drop-area-24px.png images/drop-area-24px@2x.png + images/animatedsprite-loading.png diff --git a/src/plugins/qmldesigner/qtquickplugin/quick.metainfo b/src/plugins/qmldesigner/qtquickplugin/quick.metainfo index a25cdadf4ef..d3fdd6f55f0 100644 --- a/src/plugins/qmldesigner/qtquickplugin/quick.metainfo +++ b/src/plugins/qmldesigner/qtquickplugin/quick.metainfo @@ -156,6 +156,26 @@ MetaInfo { } } + Type { + name: "QtQuick.AnimatedSprite" + icon: ":/qtquickplugin/images/animated-image-icon16.png" + + ItemLibraryEntry { + name: "Animated Sprite" + category: "a.Qt Quick - Basic" + libraryIcon: ":/qtquickplugin/images/animated-image-icon.png" + version: "2.0" + + Property { name: "frameWidth"; type: "int"; value: 64; } + Property { name: "frameHeight"; type: "int"; value: 64; } + Property { name: "frameCount"; type: "int"; value: 4; } + Property { name: "frameDuration"; type: "int"; value: 500; } + Property { name: "source"; type: "QUrl"; value:"animatedsprite-loading.png"; } + ExtraFile { source: ":/qtquickplugin/images/animatedsprite-loading.png" } + toolTip: qsTr("Draws a sprite animation.") + } + } + Type { name: "QtQuick.BorderImage" icon: ":/qtquickplugin/images/border-image-icon16.png" From 2473265541bb1982dfe4767428e860534c98eaa3 Mon Sep 17 00:00:00 2001 From: Amr Essam Date: Thu, 26 Oct 2023 17:41:54 +0300 Subject: [PATCH 114/242] QmlDesigner: Fix issues related to uniforms Some types were parsed wrong for shader values Change-Id: I4301d671dfa7bde1f23feacd64ceb5f0f9d247b9 Reviewed-by: Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Mahmoud Badri --- src/plugins/effectmakernew/effectmakermodel.cpp | 4 ++-- src/plugins/effectmakernew/uniform.cpp | 6 +++--- src/plugins/effectmakernew/uniform.h | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/plugins/effectmakernew/effectmakermodel.cpp b/src/plugins/effectmakernew/effectmakermodel.cpp index 1cdbcf94efb..4e4bcacbdea 100644 --- a/src/plugins/effectmakernew/effectmakermodel.cpp +++ b/src/plugins/effectmakernew/effectmakermodel.cpp @@ -230,7 +230,7 @@ const QString EffectMakerModel::getBufUniform() for (const auto uniform : uniforms) { // TODO: Check if uniform is already added. if (uniform->type() != Uniform::Type::Sampler && uniform->type() != Uniform::Type::Define) { - QString type = Uniform::stringFromType(uniform->type()); + QString type = Uniform::stringFromType(uniform->type(), true); QString props = " " + type + " " + uniform->name() + ";\n"; s += props; } @@ -480,7 +480,7 @@ const QString EffectMakerModel::getConstVariables() for (Uniform *uniform : uniforms) { // TODO: Check if uniform is already added. QString constValue = valueAsVariable(*uniform); - QString type = Uniform::stringFromType(uniform->type()); + QString type = Uniform::stringFromType(uniform->type(), true); s += QString("const %1 %2 = %3;\n").arg(type, uniform->name(), constValue); } if (!s.isEmpty()) diff --git a/src/plugins/effectmakernew/uniform.cpp b/src/plugins/effectmakernew/uniform.cpp index 631421bc53b..370adddc949 100644 --- a/src/plugins/effectmakernew/uniform.cpp +++ b/src/plugins/effectmakernew/uniform.cpp @@ -63,7 +63,7 @@ Uniform::Type Uniform::type() const // String representation of the type for qml QString Uniform::typeName() const { - return Uniform::stringFromType(m_type); + return Uniform::stringFromType(m_type, false); } QVariant Uniform::value() const @@ -258,7 +258,7 @@ QVariant Uniform::valueStringToVariant(const QString &value) return variant; } -QString Uniform::stringFromType(Uniform::Type type) +QString Uniform::stringFromType(Uniform::Type type, bool isShader) { if (type == Type::Bool) return "bool"; @@ -273,7 +273,7 @@ QString Uniform::stringFromType(Uniform::Type type) else if (type == Type::Vec4) return "vec4"; else if (type == Type::Color) - return "color"; + return isShader ? QString("vec4") : QString("color"); else if (type == Type::Sampler) return "sampler2D"; else if (type == Type::Define) diff --git a/src/plugins/effectmakernew/uniform.h b/src/plugins/effectmakernew/uniform.h index 1fe2530cacd..370c720410b 100644 --- a/src/plugins/effectmakernew/uniform.h +++ b/src/plugins/effectmakernew/uniform.h @@ -69,7 +69,7 @@ public: bool enableMipmap() const; - static QString stringFromType(Uniform::Type type); + static QString stringFromType(Uniform::Type type, bool isShader); static Uniform::Type typeFromString(const QString &typeString); static QString typeToProperty(Uniform::Type type); From fbaaa6ddb7771aec6b862357db7c9415a2e40cbe Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Thu, 26 Oct 2023 16:13:06 +0300 Subject: [PATCH 115/242] QmlDesigner: Highlight active split in 3D view Active split gets hightlight border. Same color used as for selection boxes. Fixes: QDS-11039 Change-Id: Ic63807d58649c441f0d44173e4cd9d981582b3e5 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Reviewed-by: Mahmoud Badri --- src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml b/src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml index 91ae5014f84..ad915b2804d 100644 --- a/src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml +++ b/src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml @@ -768,6 +768,18 @@ Item { border.color: "#aaaaaa" } + Rectangle { + // Active split highlight + visible: splitView + x: viewRects[activeSplit].x + y: viewRects[activeSplit].y + height: viewRects[activeSplit].height + (activeSplit === 0 || activeSplit === 1 ? 1 : 0) + width: viewRects[activeSplit].width + (activeSplit === 0 || activeSplit === 2 ? 1 : 0) + border.width: 1 + border.color: "#fff600" + color: "transparent" + } + MouseArea { anchors.fill: parent acceptedButtons: Qt.LeftButton | Qt.RightButton From 43782a1fcef7acaa88cc918d8910524507be0440 Mon Sep 17 00:00:00 2001 From: Pranta Dastider Date: Thu, 26 Oct 2023 17:34:57 +0200 Subject: [PATCH 116/242] QmlDesigner: Update the Washing Machine UI Doc This patch updates the Washing Machine UI Tutorial document. Removes old images and adds a new image to support the current connection view update. Also, changes some texts to keep the process relevant. Fixes: QDS-10938 Change-Id: I7ef161ddadb62eb943c508c7c4f17d2995af4337 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Leena Miettinen --- .../washingmachineui-connections-presets.png | Bin 7002 -> 0 bytes .../doc/images/washingmachineui-connections.png | Bin 3981 -> 0 bytes .../doc/images/washingmachineui-connections.webp | Bin 0 -> 7630 bytes .../examples/doc/washingMachineUI.qdoc | 8 +++++--- 4 files changed, 5 insertions(+), 3 deletions(-) delete mode 100644 doc/qtdesignstudio/examples/doc/images/washingmachineui-connections-presets.png delete mode 100644 doc/qtdesignstudio/examples/doc/images/washingmachineui-connections.png create mode 100644 doc/qtdesignstudio/examples/doc/images/washingmachineui-connections.webp diff --git a/doc/qtdesignstudio/examples/doc/images/washingmachineui-connections-presets.png b/doc/qtdesignstudio/examples/doc/images/washingmachineui-connections-presets.png deleted file mode 100644 index 59c5c61315fec09f37b8bbfae786057a38b2c387..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7002 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 diff --git a/doc/qtdesignstudio/examples/doc/images/washingmachineui-connections.png b/doc/qtdesignstudio/examples/doc/images/washingmachineui-connections.png deleted file mode 100644 index 04f3acad87edd49bc87edf61cd043c4a59a56f40..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3981 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 V@|z#Kv(8|86mryVp=tm~7yxrsy@db( literal 0 HcmV?d00001 diff --git a/doc/qtdesignstudio/examples/doc/washingMachineUI.qdoc b/doc/qtdesignstudio/examples/doc/washingMachineUI.qdoc index a003d922d47..29526ed0c26 100644 --- a/doc/qtdesignstudio/examples/doc/washingMachineUI.qdoc +++ b/doc/qtdesignstudio/examples/doc/washingMachineUI.qdoc @@ -193,10 +193,12 @@ Then, we select the mouse area for the start button, \e startMA, in \uicontrol Navigator. On the \uicontrol Connections tab in the \l {Connections} view, we select the \inlineimage icons/plus.png - (\uicontrol Add) button to connect the \c onClicked() signal handler - of the button to the \c startClicked() signal. + (\uicontrol Add) button. We set \uicontrol Signal to \c clicked, + \uicontrol Action to \c {Call Function} and \uicontrol Item to + \c startClicked. Next, we select the \inlineimage icons/close.png + button to close the connection setup options. - \image washingmachineui-connections.png "Connections view" + \image washingmachineui-connections.webp "Connections view" Then, in \e ApplicationView.qml, we specify that the \c startClicked() signal changes the application state to \e presets: From c021ad3b6196fa4b72d5d1c420ac2ad708296a68 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Fri, 27 Oct 2023 13:19:16 +0300 Subject: [PATCH 117/242] QmlDesigner: Make OriginGizmo nicer looking Reduce the size of the gizmo and adjust gizmo components accordingly. Colors were also adjusted. Fixes: QDS-11038 Change-Id: Ia1a01bf6e705cabf875dd5145d93af2a46196f0e Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Mahmoud Badri --- .../mockfiles/qt6/EditCameraController.qml | 6 +++--- .../qml2puppet/mockfiles/qt6/OriginGizmo.qml | 16 ++++++++-------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/tools/qml2puppet/mockfiles/qt6/EditCameraController.qml b/src/tools/qml2puppet/mockfiles/qt6/EditCameraController.qml index 04856bc7631..3878e26c14a 100644 --- a/src/tools/qml2puppet/mockfiles/qt6/EditCameraController.qml +++ b/src/tools/qml2puppet/mockfiles/qt6/EditCameraController.qml @@ -258,9 +258,9 @@ Item { id: originGizmo anchors.right: parent.right anchors.top: parent.top - anchors.margins: 10 - width: 120 - height: 120 + anchors.margins: 4 + width: 70 + height: 70 targetNode: cameraCtrl.camera onAxisClicked: (axis) => { diff --git a/src/tools/qml2puppet/mockfiles/qt6/OriginGizmo.qml b/src/tools/qml2puppet/mockfiles/qt6/OriginGizmo.qml index b2658e32bf6..5054caccb19 100644 --- a/src/tools/qml2puppet/mockfiles/qt6/OriginGizmo.qml +++ b/src/tools/qml2puppet/mockfiles/qt6/OriginGizmo.qml @@ -36,22 +36,19 @@ Item { id: stylePalette property color brightBall: "#eeeeee" property color dimBall: "#111111" - property color xAxis: "#ff0000" - property color yAxis: "#00aa00" - property color zAxis: "#1515ff" + property color xAxis: "#ed324d" + property color yAxis: "#489600" + property color zAxis: "#0075cc" property color background: "#aa303030" } component LineRectangle : Rectangle { property vector2d startPoint: Qt.vector2d(0, 0) property vector2d endPoint: Qt.vector2d(0, 0) - property real lineWidth: 5 transformOrigin: Item.Left - height: lineWidth + height: 2 antialiasing: true - readonly property vector2d offset: startPoint.plus(endPoint).times(0.5); - width: startPoint.minus(endPoint).length() rotation: Math.atan2(endPoint.y - startPoint.y, endPoint.x - startPoint.x) * 180 / Math.PI } @@ -117,8 +114,11 @@ Item { Text { id: label - anchors.centerIn: parent + anchors.fill: parent antialiasing: true + font.pixelSize: 8 + horizontalAlignment: Qt.AlignHCenter + verticalAlignment: Qt.AlignVCenter } } From e7daa748a6df33aaa2ed8d6ee56b6ca9752215f9 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Fri, 27 Oct 2023 14:30:21 +0300 Subject: [PATCH 118/242] QmlDesigner: Change active split when clicking on OriginGizmo in 3D view Fixes: QDS-11042 Change-Id: Id816f24a0a3f3116da00ff0a0c3ccfdc0f312c58 Reviewed-by: Mahmoud Badri --- src/tools/qml2puppet/mockfiles/qt6/EditCameraController.qml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/tools/qml2puppet/mockfiles/qt6/EditCameraController.qml b/src/tools/qml2puppet/mockfiles/qt6/EditCameraController.qml index 3878e26c14a..cb9276305a3 100644 --- a/src/tools/qml2puppet/mockfiles/qt6/EditCameraController.qml +++ b/src/tools/qml2puppet/mockfiles/qt6/EditCameraController.qml @@ -264,6 +264,7 @@ Item { targetNode: cameraCtrl.camera onAxisClicked: (axis) => { + viewRoot.activeSplit = cameraCtrl.splitId cameraCtrl.jumpToRotation(quaternionForAxis(axis)); } } From 26e13e57bdf37044cecedb645dda301387777ec1 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Fri, 27 Oct 2023 16:37:22 +0300 Subject: [PATCH 119/242] QmlDesigner: Fix 3D view active split highlight color Change-Id: I9062f4ebd67ddd849daf436b903ca471703953f9 Reviewed-by: Mahmoud Badri --- src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml b/src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml index ad915b2804d..347e0b99479 100644 --- a/src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml +++ b/src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml @@ -776,7 +776,7 @@ Item { height: viewRects[activeSplit].height + (activeSplit === 0 || activeSplit === 1 ? 1 : 0) width: viewRects[activeSplit].width + (activeSplit === 0 || activeSplit === 2 ? 1 : 0) border.width: 1 - border.color: "#fff600" + border.color: "#57B9FC" color: "transparent" } From 7e2bbd2c31c29e47114adb65c364288e2b4e7488 Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Fri, 27 Oct 2023 16:37:40 +0300 Subject: [PATCH 120/242] QmlDesigner: Update icon font Add splitScreen_medium and update colorSelection_medium icons Change-Id: Idd47aae14eef339511d2d79b8147a58e2204c4be Reviewed-by: Miikka Heikkinen --- .../imports/StudioTheme/InternalConstants.qml | 149 +++++++++--------- .../imports/StudioTheme/icons.ttf | Bin 64788 -> 64936 bytes .../components/componentcore/theme.h | 1 + 3 files changed, 76 insertions(+), 74 deletions(-) diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/InternalConstants.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/InternalConstants.qml index b86ce391d20..b35f383c9e0 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/InternalConstants.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/InternalConstants.qml @@ -293,80 +293,81 @@ QtObject { readonly property string sphere_small: "\u0138" readonly property string splitColumns: "\u0139" readonly property string splitRows: "\u013A" - readonly property string spotLight_small: "\u013B" - readonly property string stackedContainer_small: "\u013C" - readonly property string startNode: "\u013D" - readonly property string step_medium: "\u013E" - readonly property string stop_medium: "\u013F" - readonly property string testIcon: "\u0140" - readonly property string textAlignBottom: "\u0141" - readonly property string textAlignCenter: "\u0142" - readonly property string textAlignJustified: "\u0143" - readonly property string textAlignLeft: "\u0144" - readonly property string textAlignMiddle: "\u0145" - readonly property string textAlignRight: "\u0146" - readonly property string textAlignTop: "\u0147" - readonly property string textBulletList: "\u0148" - readonly property string textFullJustification: "\u0149" - readonly property string textNumberedList: "\u014A" - readonly property string textures_medium: "\u014B" - readonly property string tickIcon: "\u014C" - readonly property string tickMark_small: "\u014D" - readonly property string timeline_small: "\u014E" - readonly property string toEndFrame_medium: "\u014F" - readonly property string toNextFrame_medium: "\u0150" - readonly property string toPrevFrame_medium: "\u0151" - readonly property string toStartFrame_medium: "\u0152" - readonly property string topToolbar_annotations: "\u0153" - readonly property string topToolbar_closeFile: "\u0154" - readonly property string topToolbar_designMode: "\u0155" - readonly property string topToolbar_enterComponent: "\u0156" - readonly property string topToolbar_home: "\u0157" - readonly property string topToolbar_makeComponent: "\u0158" - readonly property string topToolbar_navFile: "\u0159" - readonly property string topToolbar_runProject: "\u015A" - readonly property string translationCreateFiles: "\u015B" - readonly property string translationCreateReport: "\u015C" - readonly property string translationExport: "\u015D" - readonly property string translationImport: "\u015E" - readonly property string translationSelectLanguages: "\u015F" - readonly property string translationTest: "\u0160" - readonly property string transparent: "\u0161" - readonly property string triState: "\u0162" - readonly property string triangleArcA: "\u0163" - readonly property string triangleArcB: "\u0164" - readonly property string triangleCornerA: "\u0165" - readonly property string triangleCornerB: "\u0166" - readonly property string unLinked: "\u0167" - readonly property string undo: "\u0168" - readonly property string unify_medium: "\u0169" - readonly property string unpin: "\u016A" - readonly property string upDownIcon: "\u016B" - readonly property string upDownSquare2: "\u016C" - readonly property string updateAvailable_medium: "\u016D" - readonly property string updateContent_medium: "\u016E" - readonly property string uploadcsv_large: "\u016F" - readonly property string uploadcsv_medium: "\u0170" - readonly property string uploadjson_large: "\u0171" - readonly property string uploadjson_medium: "\u0172" - readonly property string visibilityOff: "\u0173" - readonly property string visibilityOn: "\u0174" - readonly property string visible_medium: "\u0175" - readonly property string visible_small: "\u0176" - readonly property string wildcard: "\u0177" - readonly property string wizardsAutomotive: "\u0178" - readonly property string wizardsDesktop: "\u0179" - readonly property string wizardsGeneric: "\u017A" - readonly property string wizardsMcuEmpty: "\u017B" - readonly property string wizardsMcuGraph: "\u017C" - readonly property string wizardsMobile: "\u017D" - readonly property string wizardsUnknown: "\u017E" - readonly property string zoomAll: "\u017F" - readonly property string zoomIn: "\u0180" - readonly property string zoomIn_medium: "\u0181" - readonly property string zoomOut: "\u0182" - readonly property string zoomOut_medium: "\u0183" - readonly property string zoomSelection: "\u0184" + readonly property string splitScreen_medium: "\u013B" + readonly property string spotLight_small: "\u013C" + readonly property string stackedContainer_small: "\u013D" + readonly property string startNode: "\u013E" + readonly property string step_medium: "\u013F" + readonly property string stop_medium: "\u0140" + readonly property string testIcon: "\u0141" + readonly property string textAlignBottom: "\u0142" + readonly property string textAlignCenter: "\u0143" + readonly property string textAlignJustified: "\u0144" + readonly property string textAlignLeft: "\u0145" + readonly property string textAlignMiddle: "\u0146" + readonly property string textAlignRight: "\u0147" + readonly property string textAlignTop: "\u0148" + readonly property string textBulletList: "\u0149" + readonly property string textFullJustification: "\u014A" + readonly property string textNumberedList: "\u014B" + readonly property string textures_medium: "\u014C" + readonly property string tickIcon: "\u014D" + readonly property string tickMark_small: "\u014E" + readonly property string timeline_small: "\u014F" + readonly property string toEndFrame_medium: "\u0150" + readonly property string toNextFrame_medium: "\u0151" + readonly property string toPrevFrame_medium: "\u0152" + readonly property string toStartFrame_medium: "\u0153" + readonly property string topToolbar_annotations: "\u0154" + readonly property string topToolbar_closeFile: "\u0155" + readonly property string topToolbar_designMode: "\u0156" + readonly property string topToolbar_enterComponent: "\u0157" + readonly property string topToolbar_home: "\u0158" + readonly property string topToolbar_makeComponent: "\u0159" + readonly property string topToolbar_navFile: "\u015A" + readonly property string topToolbar_runProject: "\u015B" + readonly property string translationCreateFiles: "\u015C" + readonly property string translationCreateReport: "\u015D" + readonly property string translationExport: "\u015E" + readonly property string translationImport: "\u015F" + readonly property string translationSelectLanguages: "\u0160" + readonly property string translationTest: "\u0161" + readonly property string transparent: "\u0162" + readonly property string triState: "\u0163" + readonly property string triangleArcA: "\u0164" + readonly property string triangleArcB: "\u0165" + readonly property string triangleCornerA: "\u0166" + readonly property string triangleCornerB: "\u0167" + readonly property string unLinked: "\u0168" + readonly property string undo: "\u0169" + readonly property string unify_medium: "\u016A" + readonly property string unpin: "\u016B" + readonly property string upDownIcon: "\u016C" + readonly property string upDownSquare2: "\u016D" + readonly property string updateAvailable_medium: "\u016E" + readonly property string updateContent_medium: "\u016F" + readonly property string uploadcsv_large: "\u0170" + readonly property string uploadcsv_medium: "\u0171" + readonly property string uploadjson_large: "\u0172" + readonly property string uploadjson_medium: "\u0173" + readonly property string visibilityOff: "\u0174" + readonly property string visibilityOn: "\u0175" + readonly property string visible_medium: "\u0176" + readonly property string visible_small: "\u0177" + readonly property string wildcard: "\u0178" + readonly property string wizardsAutomotive: "\u0179" + readonly property string wizardsDesktop: "\u017A" + readonly property string wizardsGeneric: "\u017B" + readonly property string wizardsMcuEmpty: "\u017C" + readonly property string wizardsMcuGraph: "\u017D" + readonly property string wizardsMobile: "\u017E" + readonly property string wizardsUnknown: "\u017F" + readonly property string zoomAll: "\u0180" + readonly property string zoomIn: "\u0181" + readonly property string zoomIn_medium: "\u0182" + readonly property string zoomOut: "\u0183" + readonly property string zoomOut_medium: "\u0184" + readonly property string zoomSelection: "\u0185" readonly property font iconFont: Qt.font({ "family": controlIcons.name, diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/icons.ttf b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/icons.ttf index ecf109170c9a6f5af4ca544c0bcff064fad3bc02..5d166dd13cd632ed2d80937d743151b5059f8eec 100644 GIT binary patch delta 1394 zcmbR8i+ROwW)lWR1_lORh6V;^h5$FW5Z?tSnfw?S{`N31Fv$1^>l;NbNHb($V2og3 zU`R;LO)Ss~*WsUNqE!F4g@J*ABR!`w?f5a>Oa=zd9SjVYe`KU4rfB*~D=;uH++ko~ zFw4kDP2~FdzmVIAQV;T^(nM3_WeL}rLqiJlP?6KfLtCGI1>L&88} zi6on3lH?4@b5a6QZc-&u+oT1g-K1+|(qxXv7Ra{9&XL_BdrJ0$oRXZCT!LJQ+yuEb zatGwaH7>O{wL|J$>JjQC>N_-q zH0m@3G|eL0V>9C_;{zrtCdW)IOnc0X%wo)zn6sJ7nOm4AnRl7* zFn?qrU}0ym&Z7R6rI2NS77L)>hW%tpC|0*p%3Ov#qc_VEfI^z|PJt z!mi40g}s)2p8YBNFAi!BDGqNO*&NLr^Bh+=?sL56WZ*QxX`i!>bB*&Z=T|NwE@mz{ zF85r8Tsz#9+)CWGxczZYazEz&!^6R2j>i#C4$lP7O`hMpOzOQly#9C_cxQN*dC&5` z;iKSF;~U}o&u^VSlYfN&u>iAx2?3V^83OeJD+2!nUxZ(CT8Y#jK&7WMqkNmO0Susz+=GuEwD3{24PQ{> z~RS|w!MaD&*AQOvKK_*`H1esW*3NjH{e6xY>-@1BM28RC@ z%rBS~82A~)7-Sh#7_=D}7>&e41x47D6_wOX1x?J1SxwZG6$KR;mDofD6$KeZ#Ed~a zBQsM$MM365LpL|Wzic3q*;q;Z-y+88f6huMGEV=OsU*%A_b-##SVHk%CbI%e71J+6 zw{nmW<8&oqEb2FFuQ_bQXg>Mj~F*nIRpUXf35of delta 1265 zcmZ4Sn|aDFW)lWR1_lORh6V;^h5$FW5Z?s{QZz!<^6 zz>tufn^>R~uERIcM5+GI2L=WPj`W<$v^(qPeq> zgIPvKY9iOq|E&xRj3NvS3@RD9B^3(3g6<3qj5`<@m@ehyCnvsqET_i6!1jWHf$2pBf?g~MZ(8~e~Ac)1c@vY?GU{srXn^$oI^ZH{FsED#3o4* z$pXn`l6R!!q(Y>cq>f0-Nry;x$&|=klC6`SB)dxXkn9cFe{u$L9&&kdO>*<(cFCQS zSCP+=-=VNaQBN^XX^OIxa*Fa16&{rUl|`y*sx7J)>eVFFa?~!UOQ@%*H>n@fP|)bn zl+$$4oThn9^NyC9R)|)O)-`P-?F=0O9VeYjx(2!hx*K%=>B;EL)BB+BrN2!7fq{a7 zg~1O)JHtta4-CH<*%-|+dSmQj++lpq#K`1|shjC6GY7LQvrXnA<~rtX<^|?6%#WFW zu#mIxvDjl#|HD$jGR|^~m4a1*)e&nAYZGe^>pM0)HhDHpwk)=7w&(0v?Ck7(?9%Ky z?6%ok*w@(KaA0yUaVT>5Tc}{1Xt(?1@PdNW@QF3u{sd9Pa zs^B`!&A_e6?T|Z{dx85E4>k`!k5wL*JjFcoJP&xWcsbR3P4njRw(~CYZt-5>{me(t zr^`3ZkH>G1zkq+5|CIojfO!EA0{H@M0uKZk1la}k1sw`{5NsCQ6nrK`A|x+lSExW} zPUw!Xkg$DWkHUGv1Hzlaw}iilkcx9A^XK-c+W{79VXJB9y7c;VF zG*eSnRALiW*JHF}G*&c{V`SrJWK&XQXA_ZU6f-jwG*M$Uw_}uJ6crTVV^q^;R5KA) zS5x9=WD_%1G%{lrmt*8(R8~~dV>Gj8G&5o~H#5;^RAUnr6cJNp=VR1kG!-S5$T(!#;g{^2YU|o3(Ub*KJ<8 z=AaRy$>i+&_KYTzSKt4|n7Vn{gHueKiypg)Y(D>e2dfeThI diff --git a/src/plugins/qmldesigner/components/componentcore/theme.h b/src/plugins/qmldesigner/components/componentcore/theme.h index 512dfb9931f..21d4d6fc7fb 100644 --- a/src/plugins/qmldesigner/components/componentcore/theme.h +++ b/src/plugins/qmldesigner/components/componentcore/theme.h @@ -301,6 +301,7 @@ public: sphere_small, splitColumns, splitRows, + splitScreen_medium, spotLight_small, stackedContainer_small, startNode, From 8d95dc9dfd312656dffd3dd127d9dee9bafc1197 Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Fri, 27 Oct 2023 16:52:03 +0300 Subject: [PATCH 121/242] QmlDesigner: Update 3D Editor's split view icon Change-Id: Id5b9fe50d4524a9bd85f3472dba900d2fb361334 Reviewed-by: Miikka Heikkinen --- share/qtcreator/qmldesigner/designericons.json | 3 +++ .../qmldesigner/components/componentcore/designericons.h | 1 + src/plugins/qmldesigner/components/edit3d/edit3dview.cpp | 2 +- 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/share/qtcreator/qmldesigner/designericons.json b/share/qtcreator/qmldesigner/designericons.json index 2422559aeb5..715454bea22 100644 --- a/share/qtcreator/qmldesigner/designericons.json +++ b/share/qtcreator/qmldesigner/designericons.json @@ -267,6 +267,9 @@ "SnappingConfIcon": { "iconName": "snapping_conf_medium" }, + "SplitViewIcon": { + "iconName": "splitScreen_medium" + }, "ToggleGroupIcon": { "Off": { "iconName": "selectOutline_medium" diff --git a/src/plugins/qmldesigner/components/componentcore/designericons.h b/src/plugins/qmldesigner/components/componentcore/designericons.h index c2d33523c08..26d6ba76771 100644 --- a/src/plugins/qmldesigner/components/componentcore/designericons.h +++ b/src/plugins/qmldesigner/components/componentcore/designericons.h @@ -104,6 +104,7 @@ public: SimpleCheckIcon, SnappingIcon, SnappingConfIcon, + SplitViewIcon, TimelineIcon, ToggleGroupIcon, VisibilityIcon diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp index 96711162bcd..eb48b5a00b9 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp +++ b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp @@ -1029,7 +1029,7 @@ void Edit3DView::createEdit3DActions() QKeySequence(Qt::CTRL | Qt::ALT | Qt::Key_Q), true, false, - toolbarIcon(DesignerIcons::ScaleToolIcon), // TODO Placeholder, needs proper icon + toolbarIcon(DesignerIcons::SplitViewIcon), this); m_leftActions << m_selectionModeAction.get(); From 7cd9e332f362fb64220fe3f53edc74ae41454418 Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Mon, 16 Oct 2023 14:19:15 +0300 Subject: [PATCH 122/242] QmlDesigner: Implement sort for the Collection Details View Task-number: QDS-10986 Change-Id: Iba10830436d58cecb3bd2dba1afc1ac95779123d Reviewed-by: Mahmoud Badri --- .../CollectionDetailsView.qml | 24 +++-- .../CollectionView.qml | 2 + src/plugins/qmldesigner/CMakeLists.txt | 2 + .../collectiondetailsmodel.cpp | 5 + .../collectioneditor/collectiondetailsmodel.h | 3 + .../collectiondetailssortfiltermodel.cpp | 96 +++++++++++++++++++ .../collectiondetailssortfiltermodel.h | 51 ++++++++++ .../collectioneditorutils.cpp | 65 +++++++++++++ .../collectioneditor/collectioneditorutils.h | 12 +++ .../collectioneditor/collectionwidget.cpp | 15 ++- .../collectioneditor/collectionwidget.h | 2 + 11 files changed, 266 insertions(+), 11 deletions(-) create mode 100644 src/plugins/qmldesigner/components/collectioneditor/collectiondetailssortfiltermodel.cpp create mode 100644 src/plugins/qmldesigner/components/collectioneditor/collectiondetailssortfiltermodel.h create mode 100644 src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.cpp create mode 100644 src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.h diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsView.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsView.qml index ac57169905c..192d027e635 100644 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsView.qml +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsView.qml @@ -13,6 +13,7 @@ Rectangle { required property var model required property var backend + required property var sortedModel implicitWidth: 600 implicitHeight: 400 @@ -80,7 +81,7 @@ Rectangle { Rectangle { clip: true - visible: root.model.isEmpty === false + visible: !tableView.model.isEmpty color: StudioTheme.Values.themeControlBackground border.color: StudioTheme.Values.themeControlOutline border.width: 2 @@ -112,14 +113,14 @@ Rectangle { clip: true delegate: HeaderDelegate { - selectedItem: root.model.selectedColumn + selectedItem: tableView.model.selectedColumn MouseArea { anchors.fill: parent anchors.margins: 5 acceptedButtons: Qt.LeftButton | Qt.RightButton onClicked: (mouse) => { - root.model.selectColumn(index) + tableView.model.selectColumn(index) if (mouse.button === Qt.RightButton) headerMenu.popIndex(index) @@ -151,6 +152,16 @@ Rectangle { text: qsTr("Delete") onTriggered: deleteColumnDialog.popUp(headerMenu.clickedHeader) } + + StudioControls.MenuItem { + text: qsTr("Sort Ascending") + onTriggered: sortedModel.sort(headerMenu.clickedHeader, Qt.AscendingOrder) + } + + StudioControls.MenuItem { + text: qsTr("Sort Descending") + onTriggered: sortedModel.sort(headerMenu.clickedHeader, Qt.DescendingOrder) + } } } @@ -165,22 +176,21 @@ Rectangle { Layout.alignment: Qt.AlignTop + Qt.AlignLeft delegate: HeaderDelegate { - selectedItem: root.model.selectedRow + selectedItem: tableView.model.selectedRow MouseArea { anchors.fill: parent anchors.margins: 5 acceptedButtons: Qt.LeftButton - onClicked: root.model.selectRow(index) + onClicked: tableView.model.selectRow(index) } - } } TableView { id: tableView - model: root.model + model: root.sortedModel clip: true Layout.preferredWidth: tableView.contentWidth diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionView.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionView.qml index 594930723a1..beaaad71641 100644 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionView.qml +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionView.qml @@ -15,6 +15,7 @@ Item { property var rootView: CollectionEditorBackend.rootView property var model: CollectionEditorBackend.model property var collectionDetailsModel: CollectionEditorBackend.collectionDetailsModel + property var collectionDetailsSortFilterModel: CollectionEditorBackend.collectionDetailsSortFilterModel function showWarning(title, message) { warningDialog.title = title @@ -145,6 +146,7 @@ Item { CollectionDetailsView { model: root.collectionDetailsModel backend: root.model + sortedModel: root.collectionDetailsSortFilterModel anchors { left: collectionsRect.right right: parent.right diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index 275e75a8cff..eae9460cdce 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -841,7 +841,9 @@ extend_qtc_plugin(QmlDesigner SOURCES collectiondetails.cpp collectiondetails.h collectiondetailsmodel.cpp collectiondetailsmodel.h + collectiondetailssortfiltermodel.cpp collectiondetailssortfiltermodel.h collectioneditorconstants.h + collectioneditorutils.cpp collectioneditorutils.h collectionlistmodel.cpp collectionlistmodel.h collectionsourcemodel.cpp collectionsourcemodel.h collectionview.cpp collectionview.h diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.cpp index df69af21445..c1997db7532 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.cpp @@ -233,6 +233,11 @@ QVariant CollectionDetailsModel::headerData(int section, Qt::Orientation orienta return {}; } +CollectionDetails::DataType CollectionDetailsModel::propertyDataType(int column) const +{ + return m_currentCollection.typeAt(column); +} + int CollectionDetailsModel::selectedColumn() const { return m_selectedColumn; diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.h b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.h index 92ec3850f37..904ad57742f 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.h +++ b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.h @@ -43,6 +43,9 @@ public: QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; + + CollectionDetails::DataType propertyDataType(int column) const; + int selectedColumn() const; int selectedRow() const; Q_INVOKABLE QString propertyName(int column) const; diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailssortfiltermodel.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailssortfiltermodel.cpp new file mode 100644 index 00000000000..5fa82ace937 --- /dev/null +++ b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailssortfiltermodel.cpp @@ -0,0 +1,96 @@ +// 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 "collectiondetailssortfiltermodel.h" + +#include "collectiondetailsmodel.h" +#include "collectioneditorutils.h" + +#include + +namespace QmlDesigner { + +CollectionDetailsSortFilterModel::CollectionDetailsSortFilterModel(QObject *parent) + : QSortFilterProxyModel(parent) +{ + connect(this, &CollectionDetailsSortFilterModel::rowsInserted, + this, &CollectionDetailsSortFilterModel::updateEmpty); + connect(this, &CollectionDetailsSortFilterModel::rowsRemoved, + this, &CollectionDetailsSortFilterModel::updateEmpty); + connect(this, &CollectionDetailsSortFilterModel::modelReset, + this, &CollectionDetailsSortFilterModel::updateEmpty); +} + +void CollectionDetailsSortFilterModel::setSourceModel(CollectionDetailsModel *model) +{ + m_source = model; + Super::setSourceModel(model); + connect(m_source, &CollectionDetailsModel::selectedColumnChanged, this, [this](int sourceColumn) { + emit selectedColumnChanged(mapFromSource(m_source->index(0, sourceColumn)).column()); + }); + + connect(m_source, &CollectionDetailsModel::selectedRowChanged, this, [this](int sourceRow) { + emit selectedRowChanged(mapFromSource(m_source->index(sourceRow, 0)).row()); + }); +} + +int CollectionDetailsSortFilterModel::selectedRow() const +{ + QTC_ASSERT(m_source, return -1); + + return mapFromSource(m_source->index(m_source->selectedRow(), 0)).row(); +} + +int CollectionDetailsSortFilterModel::selectedColumn() const +{ + QTC_ASSERT(m_source, return -1); + + return mapFromSource(m_source->index(0, m_source->selectedColumn())).column(); +} + +bool CollectionDetailsSortFilterModel::selectRow(int row) +{ + QTC_ASSERT(m_source, return false); + + return m_source->selectRow(mapToSource(index(row, 0)).row()); +} + +bool CollectionDetailsSortFilterModel::selectColumn(int column) +{ + QTC_ASSERT(m_source, return false); + + return m_source->selectColumn(mapToSource(index(0, column)).column()); +} + +CollectionDetailsSortFilterModel::~CollectionDetailsSortFilterModel() = default; + +bool CollectionDetailsSortFilterModel::filterAcceptsRow(int sourceRow, + const QModelIndex &sourceParent) const +{ + return true; +} + +bool CollectionDetailsSortFilterModel::lessThan(const QModelIndex &sourceleft, + const QModelIndex &sourceRight) const +{ + QTC_ASSERT(m_source, return false); + + 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 false; +} + +void CollectionDetailsSortFilterModel::updateEmpty() +{ + bool newValue = rowCount() == 0; + if (m_isEmpty != newValue) { + m_isEmpty = newValue; + emit isEmptyChanged(m_isEmpty); + } +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailssortfiltermodel.h b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailssortfiltermodel.h new file mode 100644 index 00000000000..bb2c68126d7 --- /dev/null +++ b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailssortfiltermodel.h @@ -0,0 +1,51 @@ +// 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 + +namespace QmlDesigner { + +class CollectionDetailsModel; + +class CollectionDetailsSortFilterModel : public QSortFilterProxyModel +{ + Q_OBJECT + + Q_PROPERTY(int selectedColumn READ selectedColumn WRITE selectColumn NOTIFY selectedColumnChanged) + Q_PROPERTY(int selectedRow READ selectedRow WRITE selectRow NOTIFY selectedRowChanged) + Q_PROPERTY(bool isEmpty MEMBER m_isEmpty NOTIFY isEmptyChanged) + + using Super = QSortFilterProxyModel; + +public: + explicit CollectionDetailsSortFilterModel(QObject *parent = nullptr); + virtual ~CollectionDetailsSortFilterModel(); + + void setSourceModel(CollectionDetailsModel *model); + + int selectedRow() const; + int selectedColumn() const; + + Q_INVOKABLE bool selectRow(int row); + Q_INVOKABLE bool selectColumn(int column); + +signals: + void selectedColumnChanged(int); + void selectedRowChanged(int); + void isEmptyChanged(bool); + +protected: + using Super::setSourceModel; + bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override; + bool lessThan(const QModelIndex &sourceleft, const QModelIndex &sourceRight) const override; + +private: + void updateEmpty(); + + QPointer m_source; + bool m_isEmpty = true; +}; + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.cpp new file mode 100644 index 00000000000..b18ea366bb6 --- /dev/null +++ b/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.cpp @@ -0,0 +1,65 @@ +// 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 "collectioneditorutils.h" + +#include + +#include +#include + +namespace { + +using CollectionDataVariant = std::variant; + +inline CollectionDataVariant valueToVariant(const QVariant &value, + QmlDesigner::CollectionDetails::DataType type) +{ + using DataType = QmlDesigner::CollectionDetails::DataType; + switch (type) { + case DataType::String: + return value.toString(); + case DataType::Number: + return value.toDouble(); + case DataType::Boolean: + return value.toBool(); + case DataType::Color: + return value.value(); + case DataType::Url: + return value.value(); + default: + return false; + } +} + +struct LessThanVisitor +{ + template + bool operator()(const T1 &a, const T2 &b) const + { + return CollectionDataVariant(a).index() < CollectionDataVariant(b).index(); + } + + template + bool operator()(const T &a, const T &b) const + { + return a < b; + } + + template<> + bool operator()(const QColor &a, const QColor &b) const + { + return a.name(QColor::HexArgb) < b.name(QColor::HexArgb); + } +}; + +} // namespace + +namespace QmlDesigner::CollectionEditor { + +bool variantIslessThan(const QVariant &a, const QVariant &b, CollectionDetails::DataType type) +{ + return std::visit(LessThanVisitor{}, valueToVariant(a, type), valueToVariant(b, type)); +} + +} // namespace QmlDesigner::CollectionEditor diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.h b/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.h new file mode 100644 index 00000000000..1281197633c --- /dev/null +++ b/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.h @@ -0,0 +1,12 @@ +// 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 "collectiondetails.h" + +namespace QmlDesigner::CollectionEditor { + +bool variantIslessThan(const QVariant &a, const QVariant &b, CollectionDetails::DataType type); + +} // namespace QmlDesigner::CollectionEditor diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp index 9ad61f71e5f..12e9e9ee856 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp @@ -4,6 +4,7 @@ #include "collectionwidget.h" #include "collectiondetailsmodel.h" +#include "collectiondetailssortfiltermodel.h" #include "collectionsourcemodel.h" #include "collectionview.h" #include "qmldesignerconstants.h" @@ -40,6 +41,7 @@ CollectionWidget::CollectionWidget(CollectionView *view) , m_view(view) , m_sourceModel(new CollectionSourceModel) , m_collectionDetailsModel(new CollectionDetailsModel) + , m_collectionDetailsSortFilterModel(std::make_unique()) , m_quickWidget(new StudioQuickWidget(this)) { setWindowTitle(tr("Collection View", "Title of collection view widget")); @@ -50,6 +52,8 @@ CollectionWidget::CollectionWidget(CollectionView *view) icontext->setContext(context); icontext->setWidget(this); + m_collectionDetailsSortFilterModel->setSourceModel(m_collectionDetailsModel); + m_quickWidget->quickWidget()->setObjectName(Constants::OBJECT_NAME_COLLECTION_EDITOR); m_quickWidget->setResizeMode(QQuickWidget::SizeRootObjectToView); m_quickWidget->engine()->addImportPath(collectionViewResourcesPath() + "/imports"); @@ -65,10 +69,13 @@ CollectionWidget::CollectionWidget(CollectionView *view) qmlRegisterAnonymousType("CollectionEditorBackend", 1); auto map = m_quickWidget->registerPropertyMap("CollectionEditorBackend"); - map->setProperties( - {{"rootView", QVariant::fromValue(this)}, - {"model", QVariant::fromValue(m_sourceModel.data())}, - {"collectionDetailsModel", QVariant::fromValue(m_collectionDetailsModel.data())}}); + map->setProperties({ + {"rootView", QVariant::fromValue(this)}, + {"model", QVariant::fromValue(m_sourceModel.data())}, + {"collectionDetailsModel", QVariant::fromValue(m_collectionDetailsModel.data())}, + {"collectionDetailsSortFilterModel", + QVariant::fromValue(m_collectionDetailsSortFilterModel.get())}, + }); auto hotReloadShortcut = new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_F4), this); connect(hotReloadShortcut, &QShortcut::activated, this, &CollectionWidget::reloadQmlSource); diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.h b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.h index fd422fa5d99..21fddc0092b 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.h +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.h @@ -12,6 +12,7 @@ class StudioQuickWidget; namespace QmlDesigner { class CollectionDetailsModel; +class CollectionDetailsSortFilterModel; class CollectionSourceModel; class CollectionView; @@ -40,6 +41,7 @@ private: QPointer m_view; QPointer m_sourceModel; QPointer m_collectionDetailsModel; + std::unique_ptr m_collectionDetailsSortFilterModel; QScopedPointer m_quickWidget; }; From a8ac61328b57dafd52968cbbecf5de51940b514a Mon Sep 17 00:00:00 2001 From: Amr Essam Date: Fri, 27 Oct 2023 17:02:45 +0300 Subject: [PATCH 123/242] QmlDesigner: Fix effect maker colors not update shaders Also some minor fixes and cleanups Change-Id: I3db71f41a703c19e53e2b7014de053a7759a4628 Reviewed-by: Mahmoud Badri --- .../effectmakernew/effectmakermodel.cpp | 27 +++++++++---------- src/plugins/effectmakernew/uniform.cpp | 2 +- src/plugins/effectmakernew/uniform.h | 2 +- 3 files changed, 15 insertions(+), 16 deletions(-) diff --git a/src/plugins/effectmakernew/effectmakermodel.cpp b/src/plugins/effectmakernew/effectmakermodel.cpp index 4e4bcacbdea..0745536cf23 100644 --- a/src/plugins/effectmakernew/effectmakermodel.cpp +++ b/src/plugins/effectmakernew/effectmakermodel.cpp @@ -390,12 +390,9 @@ QString EffectMakerModel::valueAsString(const Uniform &uniform) } else if (uniform.type() == Uniform::Type::Vec4) { QVector4D v4 = uniform.value().value(); return QString("Qt.vector4d(%1, %2, %3, %4)").arg(v4.x(), v4.y(), v4.z(), v4.w()); - } else if (uniform.type() == Uniform::Type::Color) { - QColor c = uniform.value().value(); - return QString("Qt.rgba(%1, %2, %3, %4)").arg(c.redF(), c.greenF(), c.blueF(), c.alphaF()); } else if (uniform.type() == Uniform::Type::Sampler) { return getImageElementName(uniform); - } else if (uniform.type() == Uniform::Type::Define) { + } else if (uniform.type() == Uniform::Type::Define || uniform.type() == Uniform::Type::Color) { return uniform.value().toString(); } else { qWarning() << QString("Unhandled const variable type: %1").arg(int(uniform.type())).toLatin1(); @@ -406,8 +403,11 @@ QString EffectMakerModel::valueAsString(const Uniform &uniform) // Get value in QML binding that used for previews QString EffectMakerModel::valueAsBinding(const Uniform &uniform) { - if (uniform.type() == Uniform::Type::Bool || uniform.type() == Uniform::Type::Int - || uniform.type() == Uniform::Type::Float || uniform.type() == Uniform::Type::Define) { + if (uniform.type() == Uniform::Type::Bool + || uniform.type() == Uniform::Type::Int + || uniform.type() == Uniform::Type::Float + || uniform.type() == Uniform::Type::Color + || uniform.type() == Uniform::Type::Define) { return "g_propertyData." + uniform.name(); } else if (uniform.type() == Uniform::Type::Vec2) { QString sx = QString("g_propertyData.%1.x").arg(uniform.name()); @@ -424,12 +424,6 @@ QString EffectMakerModel::valueAsBinding(const Uniform &uniform) QString sz = QString("g_propertyData.%1.z").arg(uniform.name()); QString sw = QString("g_propertyData.%1.w").arg(uniform.name()); return QString("Qt.vector4d(%1, %2, %3, %4)").arg(sx, sy, sz, sw); - } else if (uniform.type() == Uniform::Type::Color) { - QString sr = QString("g_propertyData.%1.r").arg(uniform.name()); - QString sg = QString("g_propertyData.%1.g").arg(uniform.name()); - QString sb = QString("g_propertyData.%1.b").arg(uniform.name()); - QString sa = QString("g_propertyData.%1.a").arg(uniform.name()); - return QString("Qt.rgba(%1, %2, %3, %4)").arg(sr, sg, sb, sa); } else if (uniform.type() == Uniform::Type::Sampler) { return getImageElementName(uniform); } else { @@ -748,10 +742,14 @@ void EffectMakerModel::handleQsbProcessExit(Utils::Process *qsbProcess, const QS --m_remainingQsbTargets; const QString errStr = qsbProcess->errorString(); - auto std = qsbProcess->stdErr(); + const QByteArray errStd = qsbProcess->readAllRawStandardError(); if (!errStr.isEmpty()) qWarning() << QString("Failed to generate QSB file for: %1 %2").arg(shader, errStr); + if (!errStd.isEmpty()) + qWarning() << QString("Failed to generate QSB file for: %1 %2") + .arg(shader, QString::fromUtf8(errStd)); + if (m_remainingQsbTargets <= 0) { Q_EMIT shadersBaked(); setShadersUpToDate(true); @@ -1005,7 +1003,8 @@ QString EffectMakerModel::getQmlComponentString(bool localFiles) s += l2 + "fragmentShader: 'file:///" + m_fragmentShaderFilename + "'\n"; s += l2 + "anchors.fill: parent\n"; if (m_shaderFeatures.enabled(ShaderFeatures::GridMesh)) { - QString gridSize = QString("%1, %2").arg(m_shaderFeatures.gridMeshWidth()).arg(m_shaderFeatures.gridMeshHeight()); + QString gridSize = QString("%1, %2").arg(m_shaderFeatures.gridMeshWidth()) + .arg(m_shaderFeatures.gridMeshHeight()); s += l2 + "mesh: GridMesh {\n"; s += l3 + QString("resolution: Qt.size(%1)\n").arg(gridSize); s += l2 + "}\n"; diff --git a/src/plugins/effectmakernew/uniform.cpp b/src/plugins/effectmakernew/uniform.cpp index 370adddc949..a9729b574ef 100644 --- a/src/plugins/effectmakernew/uniform.cpp +++ b/src/plugins/effectmakernew/uniform.cpp @@ -63,7 +63,7 @@ Uniform::Type Uniform::type() const // String representation of the type for qml QString Uniform::typeName() const { - return Uniform::stringFromType(m_type, false); + return Uniform::stringFromType(m_type); } QVariant Uniform::value() const diff --git a/src/plugins/effectmakernew/uniform.h b/src/plugins/effectmakernew/uniform.h index 370c720410b..5146de6b67c 100644 --- a/src/plugins/effectmakernew/uniform.h +++ b/src/plugins/effectmakernew/uniform.h @@ -69,7 +69,7 @@ public: bool enableMipmap() const; - static QString stringFromType(Uniform::Type type, bool isShader); + static QString stringFromType(Uniform::Type type, bool isShader = false); static Uniform::Type typeFromString(const QString &typeString); static QString typeToProperty(Uniform::Type type); From 1204ce35b90dd7edf4f070325d7ab77e97b00b22 Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Fri, 20 Oct 2023 10:39:59 +0300 Subject: [PATCH 124/242] QmlDesigner: Select the property type while adding a column Task-number: QDS-11004 Change-Id: Ie2b8cb715efc2d530b2ce8dec2d7111b7fc318cc Reviewed-by: Miikka Heikkinen Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Reviewed-by: Mahmoud Badri --- .../CollectionDetailsToolbar.qml | 37 ++++++++++++++----- .../EditPropertyDialog.qml | 1 + .../collectiondetailsmodel.cpp | 9 +++-- .../collectioneditor/collectiondetailsmodel.h | 2 +- 4 files changed, 36 insertions(+), 13 deletions(-) diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsToolbar.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsToolbar.qml index f7c8ee1a7c8..7930a4a444c 100644 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsToolbar.qml +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsToolbar.qml @@ -2,6 +2,7 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 import QtQuick +import QtQuick.Layouts import Qt.labs.platform as PlatformWidgets import HelperWidgets 2.0 as HelperWidgets import StudioControls 1.0 as StudioControls @@ -43,7 +44,7 @@ Item { IconButton { icon: StudioTheme.Constants.addcolumnleft_medium tooltip: qsTr("Add property left %1").arg(leftSideToolbar.topPadding) - enabled: root.model.selectedColumn > 0 + enabled: root.model.selectedColumn > -1 onClicked: addColumnDialog.popUp(root.model.selectedColumn - 1) } @@ -189,12 +190,15 @@ Item { function popUp(index) { addColumnDialog.clickedIndex = index + columnName.text = "" + addedPropertyType.currentIndex = addedPropertyType.find("String") + addColumnDialog.open() } function addColumnName() { if (addColumnDialog.nameIsValid) { - root.model.addColumn(addColumnDialog.clickedIndex, columnName.text) + root.model.addColumn(addColumnDialog.clickedIndex, columnName.text, addedPropertyType.currentText) addColumnDialog.accept() } else { addColumnDialog.reject() @@ -204,8 +208,10 @@ Item { contentItem: Column { spacing: 2 - Row { - spacing: 10 + GridLayout { + rowSpacing: 10 + columnSpacing: 10 + columns: 2 Text { text: qsTr("Column name:") @@ -228,12 +234,25 @@ Item { && !root.model.isPropertyAvailable(columnName.text)) } } - } - Text { - text: qsTr("The collection already contains \"%1\"!").arg(columnName.text) - visible: columnName.text !== "" && !addColumnDialog.nameIsValid - color: "red" + Text { + text: qsTr("The collection already contains \"%1\"!").arg(columnName.text) + visible: columnName.text !== "" && !addColumnDialog.nameIsValid + color: StudioTheme.Values.themeRedLight + Layout.columnSpan: 2 + } + + Text { + text: qsTr("Type:") + color: StudioTheme.Values.themeTextColor + } + + StudioControls.ComboBox { + id: addedPropertyType + + model: root.model.typesList() + actionIndicatorVisible: false + } } Item { // spacer diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/EditPropertyDialog.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/EditPropertyDialog.qml index da273808dab..173e1a00b61 100644 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/EditPropertyDialog.qml +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/EditPropertyDialog.qml @@ -94,6 +94,7 @@ StudioControls.Dialog { readonly property bool changed: propertyType.initialType !== propertyType.currentText model: root.model.typesList() + actionIndicatorVisible: false onInitialTypeChanged: { let propertyIndex = propertyType.find(initialType) diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.cpp index c1997db7532..b8596f81928 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.cpp @@ -45,7 +45,7 @@ public: static DataType typeFromString(const QString &dataType) { static const QHash stringTypeHash = stringToTypeHash(); - return stringTypeHash.value(dataType); + return stringTypeHash.value(dataType, DataType::Unknown); } static QStringList typesStringList() @@ -263,7 +263,7 @@ bool CollectionDetailsModel::isPropertyAvailable(const QString &name) return m_currentCollection.containsPropertyName(name); } -bool CollectionDetailsModel::addColumn(int column, const QString &name) +bool CollectionDetailsModel::addColumn(int column, const QString &name, const QString &propertyType) { if (m_currentCollection.containsPropertyName(name)) return false; @@ -272,7 +272,10 @@ bool CollectionDetailsModel::addColumn(int column, const QString &name) column = columnCount(); beginInsertColumns({}, column, column); - m_currentCollection.insertColumn(name, column); + m_currentCollection.insertColumn(name, + column, + {}, + CollectionDataTypeHelper::typeFromString(propertyType)); endInsertColumns(); return m_currentCollection.containsPropertyName(name); } diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.h b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.h index 904ad57742f..7b0e668aec9 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.h +++ b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.h @@ -52,7 +52,7 @@ public: Q_INVOKABLE QString propertyType(int column) const; Q_INVOKABLE bool isPropertyAvailable(const QString &name); - Q_INVOKABLE bool addColumn(int column, const QString &name); + Q_INVOKABLE bool addColumn(int column, const QString &name, const QString &propertyType = {}); Q_INVOKABLE bool selectColumn(int section); Q_INVOKABLE bool renameColumn(int section, const QString &newValue); Q_INVOKABLE bool setPropertyType(int column, const QString &newValue, bool force = false); From cfa46fa97442285d4a369f2a0ea72934f9659616 Mon Sep 17 00:00:00 2001 From: Amr Essam Date: Mon, 30 Oct 2023 09:29:33 +0200 Subject: [PATCH 125/242] QmlDesigner: Fix textures not render in effect maker Task-number: QDS-11064 Change-Id: If300e61e389f66928787bf02273c9356a1a76185 Reviewed-by: Mahmoud Badri --- .../qmldesigner/effectMakerQmlSources/ValueImage.qml | 3 ++- src/plugins/effectmakernew/effectmakermodel.cpp | 8 +++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/share/qtcreator/qmldesigner/effectMakerQmlSources/ValueImage.qml b/share/qtcreator/qmldesigner/effectMakerQmlSources/ValueImage.qml index 571fac50002..acf70f0a759 100644 --- a/share/qtcreator/qmldesigner/effectMakerQmlSources/ValueImage.qml +++ b/share/qtcreator/qmldesigner/effectMakerQmlSources/ValueImage.qml @@ -18,6 +18,7 @@ Row { actionIndicatorVisible: false - onAbsoluteFilePathChanged: uniformValue = absoluteFilePath + //TODO: Disable until we figure out how to use images from outside qds + //onAbsoluteFilePathChanged: uniformValue = absoluteFilePath } } diff --git a/src/plugins/effectmakernew/effectmakermodel.cpp b/src/plugins/effectmakernew/effectmakermodel.cpp index 0745536cf23..7f8296fd3c7 100644 --- a/src/plugins/effectmakernew/effectmakermodel.cpp +++ b/src/plugins/effectmakernew/effectmakermodel.cpp @@ -462,9 +462,11 @@ QString EffectMakerModel::valueAsVariable(const Uniform &uniform) // Return name for the image property Image element QString EffectMakerModel::getImageElementName(const Uniform &uniform) { - // TODO - Q_UNUSED(uniform) - return {}; + if (uniform.value().toString().isEmpty()) + return QStringLiteral("null"); + QString simplifiedName = uniform.name().simplified(); + simplifiedName = simplifiedName.remove(' '); + return QStringLiteral("imageItem") + simplifiedName; } const QString EffectMakerModel::getConstVariables() From 7d6cb2b4000c785c4baf0650b31f37f979280a27 Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Mon, 30 Oct 2023 08:54:53 +0200 Subject: [PATCH 126/242] QmlDesigner: Fix the build error for CollectionEditor * fixes the gcc build error on CollectionEditorUtils * fixes the unused parameters error on macOS * fixes the build error for using the unknown template for QPointer Change-Id: I71f6fe319b5b682b858712139084d26079a822fb Reviewed-by: Miikka Heikkinen Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Mahmoud Badri --- .../collectiondetailssortfiltermodel.cpp | 4 ++-- .../collectiondetailssortfiltermodel.h | 1 + .../collectioneditor/collectioneditorutils.cpp | 11 +++++------ 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailssortfiltermodel.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailssortfiltermodel.cpp index 5fa82ace937..64bdb8a64d1 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailssortfiltermodel.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailssortfiltermodel.cpp @@ -64,8 +64,8 @@ bool CollectionDetailsSortFilterModel::selectColumn(int column) CollectionDetailsSortFilterModel::~CollectionDetailsSortFilterModel() = default; -bool CollectionDetailsSortFilterModel::filterAcceptsRow(int sourceRow, - const QModelIndex &sourceParent) const +bool CollectionDetailsSortFilterModel::filterAcceptsRow( + [[maybe_unused]] int sourceRow, [[maybe_unused]] const QModelIndex &sourceParent) const { return true; } diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailssortfiltermodel.h b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailssortfiltermodel.h index bb2c68126d7..fdf6f2df9aa 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailssortfiltermodel.h +++ b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailssortfiltermodel.h @@ -3,6 +3,7 @@ #pragma once +#include #include namespace QmlDesigner { diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.cpp index b18ea366bb6..69e39aedca1 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.cpp @@ -12,6 +12,11 @@ namespace { using CollectionDataVariant = std::variant; +inline bool operator<(const QColor &a, const QColor &b) +{ + return a.name(QColor::HexArgb) < b.name(QColor::HexArgb); +} + inline CollectionDataVariant valueToVariant(const QVariant &value, QmlDesigner::CollectionDetails::DataType type) { @@ -45,12 +50,6 @@ struct LessThanVisitor { return a < b; } - - template<> - bool operator()(const QColor &a, const QColor &b) const - { - return a.name(QColor::HexArgb) < b.name(QColor::HexArgb); - } }; } // namespace From 3a2927e20040d355d432b96a7e0d124457926574 Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Mon, 30 Oct 2023 11:39:57 +0200 Subject: [PATCH 127/242] EffectMaker: Allow scrolling color editors This also removes a warning when using the mouse wheel over the color editor. Fixes: QDS-11066 Change-Id: Ic32cb7d74c77ea29566a9f2f7f8dbd464d1ed6ab Reviewed-by: Miikka Heikkinen --- .../qtcreator/qmldesigner/effectMakerQmlSources/EffectMaker.qml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMaker.qml b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMaker.qml index 7bc23c9c1f3..c2c34cd8855 100644 --- a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMaker.qml +++ b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMaker.qml @@ -64,6 +64,8 @@ Item { } } + Component.onCompleted: HelperWidgets.Controller.mainScrollView = scrollView + HelperWidgets.ScrollView { id: scrollView From 354da9122236355b80da7d4ceb7d4bc9b36617b1 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Mon, 30 Oct 2023 12:57:57 +0200 Subject: [PATCH 128/242] QmlDesigner: Show currently active split when toggling off split view The active split is preserved even when split view is not enabled, so the content of the split that was active when split was toggled off is shown in full view. Fixes: QDS-11041 Change-Id: I764f6afb10271947443aa31e92df1778ddac4e03 Reviewed-by: Mahmoud Badri --- .../qml2puppet/mockfiles/qt6/EditView3D.qml | 59 +++++++++---------- 1 file changed, 29 insertions(+), 30 deletions(-) diff --git a/src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml b/src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml index 347e0b99479..6d9a89b89f2 100644 --- a/src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml +++ b/src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml @@ -336,9 +336,7 @@ Item { else if (resetToDefault) splitView = false; - if (!splitView) - activeSplit = 0; - else if ("activeSplit" in toolStates) + if ("activeSplit" in toolStates) activeSplit = toolStates.activeSplit; else if (resetToDefault) activeSplit = 0; @@ -532,9 +530,10 @@ Item { function resolveSplitPoint(x, y) { - if (activeSplit === 0) + if (!splitView || activeSplit === 0) return Qt.point(x, y); - else if (activeSplit === 1) + + if (activeSplit === 1) return Qt.point(x - viewContainer.width / 2, y); else if (activeSplit === 2) return Qt.point(x, y - viewContainer.height / 2); @@ -556,8 +555,6 @@ Item { else activeSplit = 3; } - } else { - activeSplit = 0; } } @@ -636,10 +633,11 @@ Item { Rectangle { id: viewRect0 - width: splitView ? parent.width / 2 : parent.width - height: splitView ? parent.height / 2 : parent.height + width: viewRoot.splitView ? parent.width / 2 : parent.width + height: viewRoot.splitView ? parent.height / 2 : parent.height x: 0 y: 0 + visible: viewRoot.splitView || viewRoot.activeSplit == 0 gradient: bgGradient OverlayView3D { id: overlayView0 @@ -647,7 +645,6 @@ Item { viewRoot: viewRoot importScene: overlayScene - onChangeObjectProperty: (nodes, props) => { viewRoot.changeObjectProperty(nodes, props); } @@ -664,11 +661,11 @@ Item { Rectangle { id: viewRect1 - width: parent.width / 2 - height: parent.height / 2 - x: parent.width / 2 + width: viewRoot.splitView ? parent.width / 2 : parent.width + height: viewRoot.splitView ? parent.height / 2 : parent.height + x: viewRoot.splitView ? parent.width / 2 : 0 y: 0 - visible: splitView + visible: viewRoot.splitView || viewRoot.activeSplit == 1 gradient: bgGradient OverlayView3D { id: overlayView1 @@ -692,11 +689,11 @@ Item { Rectangle { id: viewRect2 - width: parent.width / 2 - height: parent.height / 2 + width: viewRoot.splitView ? parent.width / 2 : parent.width + height: viewRoot.splitView ? parent.height / 2 : parent.height x: 0 - y: parent.height / 2 - visible: splitView + y: viewRoot.splitView ? parent.height / 2 : 0 + visible: viewRoot.splitView || viewRoot.activeSplit == 2 gradient: bgGradient OverlayView3D { id: overlayView2 @@ -720,11 +717,11 @@ Item { Rectangle { id: viewRect3 - width: parent.width / 2 - height: parent.height / 2 - x: parent.width / 2 - y: parent.height / 2 - visible: splitView + width: viewRoot.splitView ? parent.width / 2 : parent.width + height: viewRoot.splitView ? parent.height / 2 : parent.height + x: viewRoot.splitView ? parent.width / 2 : 0 + y: viewRoot.splitView ? parent.height / 2 : 0 + visible: viewRoot.splitView || viewRoot.activeSplit == 3 gradient: bgGradient OverlayView3D { id: overlayView3 @@ -748,7 +745,7 @@ Item { Rectangle { // Vertical border between splits - visible: splitView + visible: viewRoot.splitView x: parent.width / 2 y: 0 width: 1 @@ -759,7 +756,7 @@ Item { Rectangle { // Horizontal border between splits - visible: splitView + visible: viewRoot.splitView x: 0 y: parent.height / 2 height: 1 @@ -770,11 +767,13 @@ Item { Rectangle { // Active split highlight - visible: splitView - x: viewRects[activeSplit].x - y: viewRects[activeSplit].y - height: viewRects[activeSplit].height + (activeSplit === 0 || activeSplit === 1 ? 1 : 0) - width: viewRects[activeSplit].width + (activeSplit === 0 || activeSplit === 2 ? 1 : 0) + visible: viewRoot.splitView + x: viewRects[viewRoot.activeSplit].x + y: viewRects[viewRoot.activeSplit].y + height: viewRects[viewRoot.activeSplit].height + + (viewRoot.activeSplit === 0 || viewRoot.activeSplit === 1 ? 1 : 0) + width: viewRects[viewRoot.activeSplit].width + + (viewRoot.activeSplit === 0 || viewRoot.activeSplit === 2 ? 1 : 0) border.width: 1 border.color: "#57B9FC" color: "transparent" From 30941d228ec642c93f307b3dab4b4946b1b0bd7a Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Mon, 30 Oct 2023 12:15:05 +0200 Subject: [PATCH 129/242] EffectMaker: Remove anchor backend warning from color editor Change-Id: I2709e42337f026f69b0535fc40a2a91cda745ebd Reviewed-by: Amr Elsayed Reviewed-by: Miikka Heikkinen Reviewed-by: Qt CI Patch Build Bot --- src/plugins/effectmakernew/effectmakerwidget.cpp | 1 + src/plugins/effectmakernew/effectmakerwidget.h | 2 ++ .../components/materialeditor/materialeditorqmlbackend.cpp | 2 +- .../components/materialeditor/materialeditorqmlbackend.h | 4 ++-- .../qmldesigner/components/propertyeditor/gradientmodel.cpp | 2 +- .../components/propertyeditor/propertyeditorqmlbackend.cpp | 2 +- .../components/propertyeditor/propertyeditorqmlbackend.h | 4 ++-- .../components/propertyeditor/qmlanchorbindingproxy.cpp | 4 ---- .../components/propertyeditor/qmlanchorbindingproxy.h | 5 +---- .../components/propertyeditor/quick2propertyeditorview.cpp | 2 +- .../components/textureeditor/textureeditorqmlbackend.cpp | 2 +- .../components/textureeditor/textureeditorqmlbackend.h | 4 ++-- 12 files changed, 15 insertions(+), 19 deletions(-) diff --git a/src/plugins/effectmakernew/effectmakerwidget.cpp b/src/plugins/effectmakernew/effectmakerwidget.cpp index ed396113080..0199d2eca2a 100644 --- a/src/plugins/effectmakernew/effectmakerwidget.cpp +++ b/src/plugins/effectmakernew/effectmakerwidget.cpp @@ -134,6 +134,7 @@ void EffectMakerWidget::initView() m_quickWidget->rootContext()->setContextObject(ctxObj); m_backendModelNode.setup(m_effectMakerView->rootModelNode()); + m_quickWidget->rootContext()->setContextProperty("anchorBackend", &m_backendAnchorBinding); m_quickWidget->rootContext()->setContextProperty("modelNodeBackend", &m_backendModelNode); // init the first load of the QML UI elements diff --git a/src/plugins/effectmakernew/effectmakerwidget.h b/src/plugins/effectmakernew/effectmakerwidget.h index 6f55cbc786e..24efac7b586 100644 --- a/src/plugins/effectmakernew/effectmakerwidget.h +++ b/src/plugins/effectmakernew/effectmakerwidget.h @@ -3,6 +3,7 @@ #pragma once +#include "qmldesigner/components/propertyeditor/qmlanchorbindingproxy.h" #include "qmldesigner/components/propertyeditor/qmlmodelnodeproxy.h" #include @@ -54,6 +55,7 @@ private: QPointer m_effectMakerView; QPointer m_quickWidget; QmlDesigner::QmlModelNodeProxy m_backendModelNode; + QmlDesigner::QmlAnchorBindingProxy m_backendAnchorBinding; }; } // namespace EffectMaker diff --git a/src/plugins/qmldesigner/components/materialeditor/materialeditorqmlbackend.cpp b/src/plugins/qmldesigner/components/materialeditor/materialeditorqmlbackend.cpp index fe84c0c1841..f0ce34a0e62 100644 --- a/src/plugins/qmldesigner/components/materialeditor/materialeditorqmlbackend.cpp +++ b/src/plugins/qmldesigner/components/materialeditor/materialeditorqmlbackend.cpp @@ -206,7 +206,7 @@ void MaterialEditorQmlBackend::setSource(const QUrl &url) m_view->setSource(url); } -Internal::QmlAnchorBindingProxy &MaterialEditorQmlBackend::backendAnchorBinding() +QmlAnchorBindingProxy &MaterialEditorQmlBackend::backendAnchorBinding() { return m_backendAnchorBinding; } diff --git a/src/plugins/qmldesigner/components/materialeditor/materialeditorqmlbackend.h b/src/plugins/qmldesigner/components/materialeditor/materialeditorqmlbackend.h index c405334bd0e..65649329a23 100644 --- a/src/plugins/qmldesigner/components/materialeditor/materialeditorqmlbackend.h +++ b/src/plugins/qmldesigner/components/materialeditor/materialeditorqmlbackend.h @@ -38,7 +38,7 @@ public: MaterialEditorContextObject *contextObject() const; QQuickWidget *widget() const; void setSource(const QUrl &url); - Internal::QmlAnchorBindingProxy &backendAnchorBinding(); + QmlAnchorBindingProxy &backendAnchorBinding(); void updateMaterialPreview(const QPixmap &pixmap); DesignerPropertyMap &backendValuesPropertyMap(); MaterialEditorTransaction *materialEditorTransaction() const; @@ -59,7 +59,7 @@ private: PropertyName auxNamePostFix(const PropertyName &propertyName); QQuickWidget *m_view = nullptr; - Internal::QmlAnchorBindingProxy m_backendAnchorBinding; + QmlAnchorBindingProxy m_backendAnchorBinding; QmlModelNodeProxy m_backendModelNode; DesignerPropertyMap m_backendValuesPropertyMap; QScopedPointer m_materialEditorTransaction; diff --git a/src/plugins/qmldesigner/components/propertyeditor/gradientmodel.cpp b/src/plugins/qmldesigner/components/propertyeditor/gradientmodel.cpp index b435322c39d..cadca2fd604 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/gradientmodel.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/gradientmodel.cpp @@ -573,7 +573,7 @@ void GradientModel::setAnchorBackend(const QVariant &anchorBackend) auto anchorBackendObject = anchorBackend.value(); const auto backendCasted = - qobject_cast(anchorBackendObject); + qobject_cast(anchorBackendObject); if (backendCasted) m_itemNode = backendCasted->getItemNode(); diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp index 2e49befd9e1..393183f57c4 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp @@ -400,7 +400,7 @@ void PropertyEditorQmlBackend::setSource(const QUrl &url) } } -Internal::QmlAnchorBindingProxy &PropertyEditorQmlBackend::backendAnchorBinding() +QmlAnchorBindingProxy &PropertyEditorQmlBackend::backendAnchorBinding() { return m_backendAnchorBinding; } diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.h b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.h index b369a417e20..64cca972071 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.h +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.h @@ -41,7 +41,7 @@ public: PropertyEditorContextObject* contextObject(); QQuickWidget *widget(); void setSource(const QUrl& url); - Internal::QmlAnchorBindingProxy &backendAnchorBinding(); + QmlAnchorBindingProxy &backendAnchorBinding(); DesignerPropertyMap &backendValuesPropertyMap(); PropertyEditorTransaction *propertyEditorTransaction(); @@ -88,7 +88,7 @@ private: private: Quick2PropertyEditorView *m_view; - Internal::QmlAnchorBindingProxy m_backendAnchorBinding; + QmlAnchorBindingProxy m_backendAnchorBinding; QmlModelNodeProxy m_backendModelNode; DesignerPropertyMap m_backendValuesPropertyMap; QScopedPointer m_propertyEditorTransaction; diff --git a/src/plugins/qmldesigner/components/propertyeditor/qmlanchorbindingproxy.cpp b/src/plugins/qmldesigner/components/propertyeditor/qmlanchorbindingproxy.cpp index c28602e7307..140828756aa 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/qmlanchorbindingproxy.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/qmlanchorbindingproxy.cpp @@ -52,8 +52,6 @@ static inline void restoreProperty(const ModelNode &node, const PropertyName &pr node.variantProperty(propertyName).setValue(*value); } -namespace Internal { - QmlAnchorBindingProxy::QmlAnchorBindingProxy(QObject *parent) : QObject(parent), m_relativeTopTarget(SameEdge), m_relativeBottomTarget(SameEdge), @@ -1132,6 +1130,4 @@ void QmlAnchorBindingProxy::setDefaultAnchorTarget(const ModelNode &modelNode) m_rightTarget = modelNode; } -} // namespace Internal } // namespace QmlDesigner - diff --git a/src/plugins/qmldesigner/components/propertyeditor/qmlanchorbindingproxy.h b/src/plugins/qmldesigner/components/propertyeditor/qmlanchorbindingproxy.h index 4f65af289dc..2bb38980790 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/qmlanchorbindingproxy.h +++ b/src/plugins/qmldesigner/components/propertyeditor/qmlanchorbindingproxy.h @@ -13,9 +13,7 @@ namespace QmlDesigner { class NodeInstanceView; -namespace Internal { - -class QmlAnchorBindingProxy : public QObject +class QMLDESIGNERCORE_EXPORT QmlAnchorBindingProxy : public QObject { Q_OBJECT @@ -222,5 +220,4 @@ private: bool m_ignoreQml; }; -} // namespace Internal } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/propertyeditor/quick2propertyeditorview.cpp b/src/plugins/qmldesigner/components/propertyeditor/quick2propertyeditorview.cpp index 1c0071ee91b..15f69afc3fd 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/quick2propertyeditorview.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/quick2propertyeditorview.cpp @@ -53,7 +53,7 @@ void Quick2PropertyEditorView::registerQmlTypes() ItemFilterModel::registerDeclarativeType(); ListValidator::registerDeclarativeType(); ColorPaletteBackend::registerDeclarativeType(); - Internal::QmlAnchorBindingProxy::registerDeclarativeType(); + QmlAnchorBindingProxy::registerDeclarativeType(); BindingEditor::registerDeclarativeType(); ActionEditor::registerDeclarativeType(); AnnotationEditor::registerDeclarativeType(); diff --git a/src/plugins/qmldesigner/components/textureeditor/textureeditorqmlbackend.cpp b/src/plugins/qmldesigner/components/textureeditor/textureeditorqmlbackend.cpp index 1325ade10c7..0cf01cf6209 100644 --- a/src/plugins/qmldesigner/components/textureeditor/textureeditorqmlbackend.cpp +++ b/src/plugins/qmldesigner/components/textureeditor/textureeditorqmlbackend.cpp @@ -172,7 +172,7 @@ void TextureEditorQmlBackend::setSource(const QUrl &url) m_view->setSource(url); } -Internal::QmlAnchorBindingProxy &TextureEditorQmlBackend::backendAnchorBinding() +QmlAnchorBindingProxy &TextureEditorQmlBackend::backendAnchorBinding() { return m_backendAnchorBinding; } diff --git a/src/plugins/qmldesigner/components/textureeditor/textureeditorqmlbackend.h b/src/plugins/qmldesigner/components/textureeditor/textureeditorqmlbackend.h index 241195bd70d..b6d3fddf22d 100644 --- a/src/plugins/qmldesigner/components/textureeditor/textureeditorqmlbackend.h +++ b/src/plugins/qmldesigner/components/textureeditor/textureeditorqmlbackend.h @@ -39,7 +39,7 @@ public: TextureEditorContextObject *contextObject() const; QQuickWidget *widget() const; void setSource(const QUrl &url); - Internal::QmlAnchorBindingProxy &backendAnchorBinding(); + QmlAnchorBindingProxy &backendAnchorBinding(); DesignerPropertyMap &backendValuesPropertyMap(); TextureEditorTransaction *textureEditorTransaction() const; @@ -59,7 +59,7 @@ private: PropertyName auxNamePostFix(const PropertyName &propertyName); QQuickWidget *m_view = nullptr; - Internal::QmlAnchorBindingProxy m_backendAnchorBinding; + QmlAnchorBindingProxy m_backendAnchorBinding; QmlModelNodeProxy m_backendModelNode; DesignerPropertyMap m_backendValuesPropertyMap; QScopedPointer m_textureEditorTransaction; From afef8afc39ecb41f3dcaeda92b5f15923d5bd728 Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Fri, 27 Oct 2023 15:21:50 +0300 Subject: [PATCH 130/242] QmlDesigner: Add a new collection to the collection editor Task-number: QDS-11059 Change-Id: Iad622098ac7a95cbaf543d88f714e79cd5b3c153 Reviewed-by: Miikka Heikkinen Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Mahmoud Badri --- .../CollectionView.qml | 1 + .../NewCollectionDialog.qml | 247 ++++++++++++++++-- .../collectiondetailsmodel.cpp | 1 - .../collectioneditor/collectionlistmodel.cpp | 12 + .../collectioneditor/collectionlistmodel.h | 3 + .../collectionsourcemodel.cpp | 125 ++++++++- .../collectioneditor/collectionsourcemodel.h | 36 ++- .../collectioneditor/collectionview.cpp | 1 + .../collectioneditor/collectionwidget.cpp | 79 +++++- .../collectioneditor/collectionwidget.h | 6 +- 10 files changed, 475 insertions(+), 36 deletions(-) diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionView.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionView.qml index beaaad71641..4b2708e96ca 100644 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionView.qml +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionView.qml @@ -41,6 +41,7 @@ Item { id: newCollection backendValue: root.rootView + sourceModel: root.model anchors.centerIn: parent } diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/NewCollectionDialog.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/NewCollectionDialog.qml index b0cf950fd7a..83eb642f79d 100644 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/NewCollectionDialog.qml +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/NewCollectionDialog.qml @@ -3,23 +3,37 @@ import QtQuick import QtQuick.Controls +import QtQuick.Layouts import QtQuickDesignerTheme 1.0 +import Qt.labs.platform as PlatformWidgets import HelperWidgets 2.0 as HelperWidgets import StudioControls 1.0 as StudioControls import StudioTheme as StudioTheme +import CollectionEditor 1.0 StudioControls.Dialog { id: root + enum SourceType { NewJson, NewCsv, ExistingCollection, NewCollectionToJson } + + required property var backendValue + required property var sourceModel + + readonly property alias collectionType: typeMode.collectionType + readonly property bool isValid: collectionName.isValid + && jsonCollections.isValid + && newCollectionPath.isValid + title: qsTr("Add a new Collection") anchors.centerIn: parent closePolicy: Popup.CloseOnEscape modal: true - required property var backendValue - onOpened: { - collectionName.text = "Collection" + collectionName.text = qsTr("Collection") + updateType() + updateJsonSourceIndex() + updateCollectionExists() } onRejected: { @@ -27,24 +41,197 @@ StudioControls.Dialog { } onAccepted: { - if (collectionName.text !== "") - root.backendValue.addCollection(collectionName.text) + if (root.isValid) { + root.backendValue.addCollection(collectionName.text, + root.collectionType, + newCollectionPath.text, + jsonCollections.currentValue) + + } } - contentItem: Column { + function updateType() { + newCollectionPath.text = "" + if (typeMode.currentValue === NewCollectionDialog.SourceType.NewJson) { + newCollectionFileDialog.nameFilters = ["Json Files (*.json)"] + newCollectionFileDialog.fileMode = PlatformWidgets.FileDialog.SaveFile + newCollectionPath.enabled = true + jsonCollections.enabled = false + typeMode.collectionType = "json" + } else if (typeMode.currentValue === NewCollectionDialog.SourceType.NewCsv) { + newCollectionFileDialog.nameFilters = ["Comma-Separated Values (*.csv)"] + newCollectionFileDialog.fileMode = PlatformWidgets.FileDialog.SaveFile + newCollectionPath.enabled = true + jsonCollections.enabled = false + typeMode.collectionType = "csv" + } else if (typeMode.currentValue === NewCollectionDialog.SourceType.ExistingCollection) { + newCollectionFileDialog.nameFilters = ["All Collection Files (*.json *.csv)", + "Json Files (*.json)", + "Comma-Separated Values (*.csv)"] + newCollectionFileDialog.fileMode = PlatformWidgets.FileDialog.OpenFile + newCollectionPath.enabled = true + jsonCollections.enabled = false + typeMode.collectionType = "existing" + } else if (typeMode.currentValue === NewCollectionDialog.SourceType.NewCollectionToJson) { + newCollectionFileDialog.nameFilters = [""] + newCollectionPath.enabled = false + jsonCollections.enabled = true + typeMode.collectionType = "json" + } + } + + function updateJsonSourceIndex() { + if (!jsonCollections.enabled) { + jsonCollections.currentIndex = -1 + return + } + + if (jsonCollections.currentIndex === -1 && jsonCollections.model.rowCount()) + jsonCollections.currentIndex = 0 + } + + function updateCollectionExists() { + collectionName.alreadyExists = sourceModel.collectionExists(jsonCollections.currentValue, + collectionName.text) + } + + component NameField: Text { + Layout.alignment: Qt.AlignRight | Qt.AlignVCenter + horizontalAlignment: Qt.AlignRight + verticalAlignment: Qt.AlignCenter + color: StudioTheme.Values.themeTextColor + font.family: StudioTheme.Constants.font.family + font.pixelSize: StudioTheme.Values.baseIconFontSize + } + + component ErrorField: Text { + Layout.columnSpan: 2 + color: StudioTheme.Values.themeError + text: qsTr("Collection name can not be empty") + font.family: StudioTheme.Constants.font.family + font.pixelSize: StudioTheme.Values.baseIconFontSize + } + + contentItem: ColumnLayout { spacing: 10 - Row { - spacing: 10 - Text { - text: qsTr("Collection name: ") - anchors.verticalCenter: parent.verticalCenter - color: StudioTheme.Values.themeTextColor + GridLayout { + columns: 2 + rowSpacing: 10 + + NameField { + text: qsTr("Type") + } + + StudioControls.ComboBox { + id: typeMode + + property string collectionType + + Layout.minimumWidth: 300 + Layout.fillWidth: true + + model: ListModel { + ListElement { text: qsTr("New Json collection"); value: NewCollectionDialog.SourceType.NewJson} + ListElement { text: qsTr("New CSV collection"); value: NewCollectionDialog.SourceType.NewCsv} + ListElement { text: qsTr("Import an existing collection"); value: NewCollectionDialog.SourceType.ExistingCollection} + ListElement { text: qsTr("Add collection to an available JSON"); value: NewCollectionDialog.SourceType.NewCollectionToJson} + } + + textRole: "text" + valueRole: "value" + actionIndicatorVisible: false + + onCurrentValueChanged: root.updateType() + } + + NameField { + text: qsTr("File location") + visible: newCollectionPath.enabled + } + + RowLayout { + visible: newCollectionPath.enabled + + Text { + id: newCollectionPath + + readonly property bool isValid: !enabled || text !== "" + + Layout.fillWidth: true + Layout.alignment: Qt.AlignRight | Qt.AlignVCenter + elide: Text.ElideRight + font.family: StudioTheme.Constants.font.family + font.pixelSize: StudioTheme.Values.baseIconFontSize + color: StudioTheme.Values.themePlaceholderTextColor + } + + HelperWidgets.Button { + Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter + text: qsTr("Select") + + onClicked: newCollectionFileDialog.open() + + PlatformWidgets.FileDialog { + id: newCollectionFileDialog + + title: "Select source file" + fileMode: PlatformWidgets.FileDialog.OpenFile + acceptLabel: fileMode === PlatformWidgets.FileDialog.OpenFile ? qsTr("Open") : qsTr("Add") + + onAccepted: newCollectionPath.text = newCollectionFileDialog.currentFile + } + } + } + + ErrorField { + visible: !newCollectionPath.isValid + text: qsTr("Select a file to continue") + } + + NameField { + text: qsTr("Json Collection") + visible: jsonCollections.enabled + } + + StudioControls.ComboBox { + id: jsonCollections + + readonly property bool isValid: !enabled || currentIndex !== -1 + + implicitWidth: 300 + textRole: "sourceName" + valueRole: "sourceNode" + visible: enabled + actionIndicatorVisible: false + + model: CollectionJsonSourceFilterModel { + sourceModel: root.sourceModel + onRowsInserted: root.updateJsonSourceIndex() + onModelReset: root.updateJsonSourceIndex() + onRowsRemoved: root.updateJsonSourceIndex() + } + + onEnabledChanged: root.updateJsonSourceIndex() + onCurrentValueChanged: root.updateCollectionExists() + } + + ErrorField { + visible: !jsonCollections.isValid + text: qsTr("Add a json resource to continue") + } + + NameField { + text: qsTr("Collection name") + visible: collectionName.enabled } StudioControls.TextField { id: collectionName - anchors.verticalCenter: parent.verticalCenter + readonly property bool isValid: !enabled || (text !== "" && !alreadyExists) + property bool alreadyExists + + visible: enabled actionIndicator.visible: false translationIndicator.visible: false validator: HelperWidgets.RegExpValidator { @@ -54,38 +241,42 @@ StudioControls.Dialog { Keys.onEnterPressed: btnCreate.onClicked() Keys.onReturnPressed: btnCreate.onClicked() Keys.onEscapePressed: root.reject() + + onTextChanged: root.updateCollectionExists() + } + + ErrorField { + text: qsTr("Collection name can not be empty") + visible: collectionName.enabled && collectionName.text === "" + } + + ErrorField { + text: qsTr("Collection name already exists %1").arg(collectionName.text) + visible: collectionName.enabled && collectionName.alreadyExists } } - Text { - id: fieldErrorText - color: StudioTheme.Values.themeTextColor - anchors.right: parent.right - text: qsTr("Collection name can not be empty") - visible: collectionName.text === "" - } - Item { // spacer - width: 1 - height: 20 + Layout.fillHeight: true + Layout.preferredWidth: 1 } - Row { - anchors.right: parent.right + RowLayout { spacing: 10 + Layout.alignment: Qt.AlignRight | Qt.AlignBottom HelperWidgets.Button { id: btnCreate - anchors.verticalCenter: parent.verticalCenter + Layout.alignment: Qt.AlignRight | Qt.AlignVCenter text: qsTr("Create") - enabled: collectionName.text !== "" + enabled: root.isValid onClicked: root.accept() } HelperWidgets.Button { + Layout.alignment: Qt.AlignRight | Qt.AlignVCenter text: qsTr("Cancel") - anchors.verticalCenter: parent.verticalCenter onClicked: root.reject() } } diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.cpp index b8596f81928..e082a54cc3f 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.cpp @@ -485,7 +485,6 @@ void CollectionDetailsModel::loadJsonCollection(const QString &source, const QSt } if (collectionNodes.isEmpty()) { - closeCurrentCollectionIfSaved(); endResetModel(); return; }; diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionlistmodel.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionlistmodel.cpp index 4c49d5e4954..2d7952c1021 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionlistmodel.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionlistmodel.cpp @@ -94,6 +94,11 @@ QString CollectionListModel::sourceAddress() const return m_sourceNode.variantProperty(CollectionEditor::SOURCEFILE_PROPERTY).value().toString(); } +bool CollectionListModel::contains(const QString &collectionName) const +{ + return stringList().contains(collectionName); +} + void CollectionListModel::selectCollectionIndex(int idx, bool selectAtLeastOne) { int collectionCount = stringList().size(); @@ -108,6 +113,13 @@ void CollectionListModel::selectCollectionIndex(int idx, bool selectAtLeastOne) setSelectedIndex(preferredIndex); } +void CollectionListModel::selectCollectionName(const QString &collectionName) +{ + int idx = stringList().indexOf(collectionName); + if (idx > -1) + selectCollectionIndex(idx); +} + QString CollectionListModel::collectionNameAt(int idx) const { return index(idx).data(NameRole).toString(); diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionlistmodel.h b/src/plugins/qmldesigner/components/collectioneditor/collectionlistmodel.h index e30b16a5ff7..8450ee62b74 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionlistmodel.h +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionlistmodel.h @@ -28,13 +28,16 @@ public: Q_INVOKABLE int selectedIndex() const; Q_INVOKABLE ModelNode sourceNode() const; Q_INVOKABLE QString sourceAddress() const; + Q_INVOKABLE bool contains(const QString &collectionName) const; void selectCollectionIndex(int idx, bool selectAtLeastOne = false); + void selectCollectionName(const QString &collectionName); QString collectionNameAt(int idx) const; signals: void selectedIndexChanged(int idx); void isEmptyChanged(bool); + void collectionNameChanged(const QString &oldName, const QString &newName); private: void setSelectedIndex(int idx); diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.cpp index 19dce052852..a33aff95787 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.cpp @@ -9,8 +9,11 @@ #include "variantproperty.h" #include +#include #include +#include +#include #include #include #include @@ -58,11 +61,26 @@ QSharedPointer loadCollection( } return collectionsList; } + +QString getSourceCollectionType(const QmlDesigner::ModelNode &node) +{ + using namespace QmlDesigner; + if (node.type() == CollectionEditor::JSONCOLLECTIONMODEL_TYPENAME) + return "json"; + + if (node.type() == CollectionEditor::CSVCOLLECTIONMODEL_TYPENAME) + return "csv"; + + return {}; +} + } // namespace namespace QmlDesigner { -CollectionSourceModel::CollectionSourceModel() {} +CollectionSourceModel::CollectionSourceModel(QObject *parent) + : Super(parent) +{} int CollectionSourceModel::rowCount(const QModelIndex &) const { @@ -80,6 +98,10 @@ QVariant CollectionSourceModel::data(const QModelIndex &index, int role) const return collectionSource->id(); case NameRole: return collectionSource->variantProperty("objectName").value(); + case NodeRole: + return QVariant::fromValue(*collectionSource); + case CollectionTypeRole: + return getSourceCollectionType(*collectionSource); case SourceRole: return collectionSource->variantProperty(CollectionEditor::SOURCEFILE_PROPERTY).value(); case SelectedRole: @@ -188,6 +210,8 @@ QHash CollectionSourceModel::roleNames() const roles.insert(Super::roleNames()); roles.insert({{IdRole, "sourceId"}, {NameRole, "sourceName"}, + {NodeRole, "sourceNode"}, + {CollectionTypeRole, "sourceCollectionType"}, {SelectedRole, "sourceIsSelected"}, {SourceRole, "sourceAddress"}, {CollectionsRole, "collections"}}); @@ -265,6 +289,83 @@ void CollectionSourceModel::selectSource(const ModelNode &node) selectSourceIndex(nodePlace, true); } +bool CollectionSourceModel::collectionExists(const ModelNode &node, const QString &collectionName) const +{ + int idx = sourceIndex(node); + if (idx < 0) + return false; + + auto collections = m_collectionList.at(idx); + if (collections.isNull()) + return false; + + return collections->contains(collectionName); +} + +bool CollectionSourceModel::addCollectionToSource(const ModelNode &node, + const QString &collectionName, + QString *errorString) +{ + auto returnError = [errorString](const QString &msg) -> bool { + if (errorString) + *errorString = msg; + return false; + }; + + int idx = sourceIndex(node); + if (idx < 0) + return returnError(tr("Node is not indexed in the collections model.")); + + if (node.type() != CollectionEditor::JSONCOLLECTIONMODEL_TYPENAME) + return returnError(tr("Node should be a json collection model.")); + + if (collectionExists(node, collectionName)) + return returnError(tr("Collection does not exist.")); + + QString sourceFileAddress = node.variantProperty(CollectionEditor::SOURCEFILE_PROPERTY) + .value() + .toString(); + + QFileInfo sourceFileInfo(sourceFileAddress); + if (!sourceFileInfo.isFile()) + return returnError(tr("Selected node should have a valid source file address")); + + QFile jsonFile(sourceFileAddress); + if (!jsonFile.open(QFile::ReadWrite)) + return returnError(tr("Can't open the file to read.\n") + jsonFile.errorString()); + + QJsonParseError parseError; + QJsonDocument document = QJsonDocument::fromJson(jsonFile.readAll(), &parseError); + if (parseError.error != QJsonParseError::NoError) + return returnError(tr("Saved json file is messy.\n") + parseError.errorString()); + + if (document.isObject()) { + QJsonObject sourceObject = document.object(); + sourceObject.insert(collectionName, QJsonArray{}); + document.setObject(sourceObject); + if (!jsonFile.resize(0)) + return returnError(tr("Can't clean the json file.")); + + QByteArray jsonData = document.toJson(); + auto writtenBytes = jsonFile.write(jsonData); + jsonFile.close(); + + if (writtenBytes != jsonData.size()) + return returnError(tr("Can't write to the json file.")); + + updateCollectionList(index(idx)); + + auto collections = m_collectionList.at(idx); + if (collections.isNull()) + return returnError(tr("No collection is available for the json file.")); + + collections->selectCollectionName(collectionName); + return true; + } else { + return returnError(tr("Json document type should be an object containing collections.")); + } +} + QmlDesigner::ModelNode CollectionSourceModel::sourceNodeAt(int idx) { QModelIndex data = index(idx); @@ -309,6 +410,11 @@ void CollectionSourceModel::updateSelectedSource(bool selectAtLeastOne) selectSourceIndex(idx, selectAtLeastOne); } +bool CollectionSourceModel::collectionExists(const QVariant &node, const QString &collectionName) const +{ + return collectionExists(node.value(), collectionName); +} + void CollectionSourceModel::updateNodeName(const ModelNode &node) { QModelIndex index = indexOfNode(node); @@ -412,4 +518,21 @@ QModelIndex CollectionSourceModel::indexOfNode(const ModelNode &node) const { return index(m_sourceIndexHash.value(node.internalId(), -1)); } + +void CollectionJsonSourceFilterModel::registerDeclarativeType() +{ + qmlRegisterType("CollectionEditor", + 1, + 0, + "CollectionJsonSourceFilterModel"); +} + +bool CollectionJsonSourceFilterModel::filterAcceptsRow(int source_row, const QModelIndex &) const +{ + if (!sourceModel()) + return false; + QModelIndex sourceItem = sourceModel()->index(source_row, 0, {}); + return sourceItem.data(CollectionSourceModel::Roles::CollectionTypeRole).toString() == "json"; +} + } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.h b/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.h index 5a71635c874..447df5d5dea 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.h +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.h @@ -7,9 +7,13 @@ #include #include +#include namespace QmlDesigner { + +class CollectionJsonSourceFilterModel; class CollectionListModel; + class CollectionSourceModel : public QAbstractListModel { Q_OBJECT @@ -18,9 +22,17 @@ class CollectionSourceModel : public QAbstractListModel Q_PROPERTY(bool isEmpty MEMBER m_isEmpty NOTIFY isEmptyChanged) public: - enum Roles { IdRole = Qt::UserRole + 1, NameRole, SourceRole, SelectedRole, CollectionsRole }; + enum Roles { + IdRole = Qt::UserRole + 1, + NameRole, + NodeRole, + CollectionTypeRole, + SourceRole, + SelectedRole, + CollectionsRole + }; - explicit CollectionSourceModel(); + explicit CollectionSourceModel(QObject *parent = nullptr); virtual int rowCount(const QModelIndex &parent = QModelIndex()) const override; virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; @@ -40,12 +52,19 @@ public: void addSource(const ModelNode &node); void selectSource(const ModelNode &node); + bool collectionExists(const ModelNode &node, const QString &collectionName) const; + bool addCollectionToSource(const ModelNode &node, + const QString &collectionName, + QString *errorString = nullptr); + ModelNode sourceNodeAt(int idx); CollectionListModel *selectedCollectionList(); Q_INVOKABLE void selectSourceIndex(int idx, bool selectAtLeastOne = false); Q_INVOKABLE void deselect(); Q_INVOKABLE void updateSelectedSource(bool selectAtLeastOne = false); + Q_INVOKABLE bool collectionExists(const QVariant &node, const QString &collectionName) const; + void updateNodeName(const ModelNode &node); void updateNodeSource(const ModelNode &node); void updateNodeId(const ModelNode &node); @@ -64,10 +83,10 @@ private: void setSelectedIndex(int idx); void updateEmpty(); void updateCollectionList(QModelIndex index); + QModelIndex indexOfNode(const ModelNode &node) const; using Super = QAbstractListModel; - QModelIndex indexOfNode(const ModelNode &node) const; ModelNodes m_collectionSources; QHash m_sourceIndexHash; // internalId -> index QList> m_collectionList; @@ -76,4 +95,15 @@ private: bool m_isEmpty = true; }; +class CollectionJsonSourceFilterModel : public QSortFilterProxyModel +{ + Q_OBJECT + +public: + static void registerDeclarativeType(); + +protected: + bool filterAcceptsRow(int source_row, const QModelIndex &) const override; +}; + } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp index 6b0d063081f..c3ab244177a 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp @@ -156,6 +156,7 @@ void CollectionView::addResource(const QUrl &url, const QString &name, const QSt void CollectionView::registerDeclarativeType() { CollectionDetails::registerDeclarativeType(); + CollectionJsonSourceFilterModel::registerDeclarativeType(); } void CollectionView::refreshModel() diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp index 12e9e9ee856..7811ee622cd 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp @@ -16,7 +16,9 @@ #include #include +#include #include +#include #include #include #include @@ -25,6 +27,7 @@ #include namespace { + QString collectionViewResourcesPath() { #ifdef SHARE_QML_PATH @@ -33,6 +36,22 @@ QString collectionViewResourcesPath() #endif return Core::ICore::resourcePath("qmldesigner/collectionEditorQmlSource").toString(); } + +static QString urlToLocalPath(const QUrl &url) +{ + QString localPath; + + if (url.isLocalFile()) + localPath = url.toLocalFile(); + + if (url.scheme() == QLatin1String("qrc")) { + const QString &path = url.path(); + localPath = QStringLiteral(":") + path; + } + + return localPath; +} + } // namespace namespace QmlDesigner { @@ -161,9 +180,65 @@ bool CollectionWidget::isCsvFile(const QString &csvFileAddress) const return true; } -bool CollectionWidget::addCollection([[maybe_unused]] const QString &collectionName) const +bool CollectionWidget::addCollection(const QString &collectionName, + const QString &collectionType, + const QString &sourceAddress, + const QVariant &sourceNode) { - // TODO + const ModelNode node = sourceNode.value(); + bool isNewCollection = !node.isValid(); + + if (isNewCollection) { + QString sourcePath = ::urlToLocalPath(sourceAddress); + if (collectionType == "json") { + QJsonObject jsonObject; + jsonObject.insert(collectionName, QJsonArray()); + + QFile sourceFile(sourcePath); + if (!sourceFile.open(QFile::WriteOnly)) { + warn(tr("File error"), + tr("Can not open the file to write.\n") + sourceFile.errorString()); + return false; + } + + sourceFile.write(QJsonDocument(jsonObject).toJson()); + sourceFile.close(); + + bool loaded = loadJsonFile(sourcePath); + if (!loaded) + sourceFile.remove(); + + return loaded; + } else if (collectionType == "csv") { + QFile sourceFile(sourcePath); + if (!sourceFile.open(QFile::WriteOnly)) { + warn(tr("File error"), + tr("Can not open the file to write.\n") + sourceFile.errorString()); + return false; + } + + sourceFile.close(); + + bool loaded = loadCsvFile(collectionName, sourcePath); + if (!loaded) + sourceFile.remove(); + + return loaded; + } else if (collectionType == "existing") { + QFileInfo fileInfo(sourcePath); + if (fileInfo.suffix() == "json") + return loadJsonFile(sourcePath); + else if (fileInfo.suffix() == "csv") + return loadCsvFile(collectionName, sourcePath); + } + } else if (collectionType == "json") { + QString errorMsg; + bool added = m_sourceModel->addCollectionToSource(node, collectionName, &errorMsg); + if (!added) + warn(tr("Can not add a collection to the json file"), errorMsg); + return added; + } + return false; } diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.h b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.h index 21fddc0092b..f1e3e232e89 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.h +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.h @@ -15,6 +15,7 @@ class CollectionDetailsModel; class CollectionDetailsSortFilterModel; class CollectionSourceModel; class CollectionView; +class ModelNode; class CollectionWidget : public QFrame { @@ -33,7 +34,10 @@ public: Q_INVOKABLE bool loadCsvFile(const QString &collectionName, const QString &csvFileAddress); Q_INVOKABLE bool isJsonFile(const QString &jsonFileAddress) const; Q_INVOKABLE bool isCsvFile(const QString &csvFileAddress) const; - Q_INVOKABLE bool addCollection(const QString &collectionName) const; + Q_INVOKABLE bool addCollection(const QString &collectionName, + const QString &collectionType, + const QString &sourceAddress, + const QVariant &sourceNode); void warn(const QString &title, const QString &body); From 1641035388bad543b80b7324bc5c8be5ad39e987 Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Mon, 30 Oct 2023 15:58:19 +0200 Subject: [PATCH 131/242] QmlDesigner: Prevent clipping the CollectionDetailsView delegates Task-number: QDS-11011 Change-Id: I7e7997df09c8c62c72a911a3f51a9b9fe492010b Reviewed-by: Miikka Heikkinen Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Mahmoud Badri --- .../CollectionDetailsEditDelegate.qml | 114 +++++++++++++----- 1 file changed, 87 insertions(+), 27 deletions(-) diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsEditDelegate.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsEditDelegate.qml index d5f85ce49ae..b1b913c5a05 100644 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsEditDelegate.qml +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsEditDelegate.qml @@ -6,84 +6,144 @@ import CollectionDetails 1.0 as CollectionDetails import HelperWidgets 2.0 as HelperWidgets import StudioControls 1.0 as StudioControls import StudioTheme 1.0 as StudioTheme +import QtQuick.Templates as T Item { id: root required property var columnType property var __modifier : textEditor + property bool __changesAccepted: true - width: itemColumn.width - height: itemColumn.height - - TableView.onCommit: edit = __modifier.editValue + TableView.onCommit: { + if (root.__changesAccepted) + edit = __modifier.editor.editValue + } Component.onCompleted: { + __changesAccepted = true if (edit && edit !== "") - root.__modifier.editValue = edit + root.__modifier.editor.editValue = edit } onActiveFocusChanged: { if (root.activeFocus) - root.__modifier.forceActiveFocus() + root.__modifier.editor.forceActiveFocus() } Connections { id: modifierFocusConnection - target: root.__modifier + + target: root.__modifier.editor + function onActiveFocusChanged() { if (!modifierFocusConnection.target.activeFocus) - TableView.commit() + root.TableView.commit() } } - Column { - id: itemColumn + EditorPopup { + id: textEditor + + editor: textField StudioControls.TextField { - id: textEditor + id: textField - property alias editValue: textEditor.text + property alias editValue: textField.text actionIndicator.visible: false translationIndicatorVisible: false - enabled: visible - visible: false + + onRejected: root.__changesAccepted = false } + } + + EditorPopup { + id: numberEditor + + editor: numberField StudioControls.RealSpinBox { - id: numberEditor + id: numberField - property alias editValue: numberEditor.realValue + property alias editValue: numberField.realValue actionIndicator.visible: false - enabled: visible - visible: false - realFrom: -9e9 realTo: 9e9 realStepSize: 1.0 decimals: 6 } + } + + EditorPopup { + id: boolEditor + + editor: boolField StudioControls.CheckBox { - id: boolEditor + id: boolField - property alias editValue: boolEditor.checked + property alias editValue: boolField.checked actionIndicatorVisible: false - enabled: visible - visible: false } + } + + EditorPopup { + id: colorEditor + + editor: colorPicker + + implicitHeight: colorPicker.height + topPadding + bottomPadding + implicitWidth: colorPicker.width + leftPadding + rightPadding + padding: 8 HelperWidgets.ColorPicker { - id: colorEditor + id: colorPicker - property alias editValue: colorEditor.color + property alias editValue: colorPicker.color width: 100 - enabled: visible - visible: false + + Keys.onEnterPressed: colorPicker.focus = false + } + + background: Rectangle { + color: StudioTheme.Values.themeControlBackgroundInteraction + border.color: StudioTheme.Values.themeInteraction + border.width: StudioTheme.Values.border + } + } + + component EditorPopup: T.Popup { + id: editorPopup + + required property Item editor + + implicitHeight: contentHeight + implicitWidth: contentWidth + + enabled: visible + visible: false + + Connections { + target: editorPopup.editor + + function onActiveFocusChanged() { + if (!editorPopup.editor.activeFocus) + editorPopup.close() + } + } + + Connections { + target: editorPopup.editor.Keys + + function onEscapePressed() { + root.__changesAccepted = false + editorPopup.close() + } } } From dca7f6121c83277a7d05895e24f1a311dbecaa10 Mon Sep 17 00:00:00 2001 From: Brook Cronin Date: Mon, 30 Oct 2023 14:56:44 +0100 Subject: [PATCH 132/242] QmlDesigner: Make toolbutton include label MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Task-number: QDS-9220 Change-Id: I91b30b0f1d8b94662492c0480ec216b2eaff3339 Reviewed-by: Thomas Hartmann Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Henning Gründl --- .../imports/StudioControls/AbstractButton.qml | 21 ++--- share/qtcreator/qmldesigner/toolbar/Main.qml | 83 +++++++++++++++---- 2 files changed, 77 insertions(+), 27 deletions(-) diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/AbstractButton.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/AbstractButton.qml index 0acf8418f79..79f27ddaf27 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/AbstractButton.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/AbstractButton.qml @@ -12,6 +12,7 @@ T.AbstractButton { property bool globalHover: false property bool hover: control.hovered + property bool press: control.pressed property alias buttonIcon: buttonIcon.text property alias iconColor: buttonIcon.color @@ -67,7 +68,7 @@ T.AbstractButton { states: [ State { name: "default" - when: control.enabled && !control.pressed && !control.checked && !control.hover + when: control.enabled && !control.press && !control.checked && !control.hover PropertyChanges { target: buttonIcon color: control.style.icon.idle @@ -75,7 +76,7 @@ T.AbstractButton { }, State { name: "hover" - when: control.enabled && !control.pressed && !control.checked && control.hover + when: control.enabled && !control.press && !control.checked && control.hover PropertyChanges { target: buttonIcon color: control.style.icon.hover @@ -83,7 +84,7 @@ T.AbstractButton { }, State { name: "press" - when: control.enabled && control.pressed + when: control.enabled && control.press PropertyChanges { target: buttonIcon color: control.style.icon.interaction @@ -91,7 +92,7 @@ T.AbstractButton { }, State { name: "check" - when: control.enabled && !control.pressed && control.checked + when: control.enabled && !control.press && control.checked PropertyChanges { target: buttonIcon color: control.checkedInverted ? control.style.text.selectedText // TODO rather something in icon @@ -114,7 +115,7 @@ T.AbstractButton { State { name: "default" when: control.enabled && !control.globalHover && !control.hover - && !control.pressed && !control.checked + && !control.press && !control.checked PropertyChanges { target: buttonBackground color: control.style.background.idle @@ -127,7 +128,7 @@ T.AbstractButton { }, State { name: "globalHover" - when: control.globalHover && !control.hover && !control.pressed && control.enabled + when: control.globalHover && !control.hover && !control.press && control.enabled PropertyChanges { target: buttonBackground color: control.style.background.idle @@ -136,7 +137,7 @@ T.AbstractButton { }, State { name: "hover" - when: !control.checked && control.hover && !control.pressed && control.enabled + when: !control.checked && control.hover && !control.press && control.enabled PropertyChanges { target: buttonBackground color: control.style.background.hover @@ -149,7 +150,7 @@ T.AbstractButton { }, State { name: "hoverCheck" - when: control.checked && control.hover && !control.pressed && control.enabled + when: control.checked && control.hover && !control.press && control.enabled PropertyChanges { target: buttonBackground color: control.checkedInverted ? control.style.interactionHover @@ -164,7 +165,7 @@ T.AbstractButton { }, State { name: "press" - when: control.hover && control.pressed && control.enabled + when: control.hover && control.press && control.enabled PropertyChanges { target: buttonBackground color: control.style.interaction @@ -177,7 +178,7 @@ T.AbstractButton { }, State { name: "check" - when: control.enabled && !control.pressed && control.checked + when: control.enabled && !control.press && control.checked PropertyChanges { target: buttonBackground color: control.checkedInverted ? control.style.interaction diff --git a/share/qtcreator/qmldesigner/toolbar/Main.qml b/share/qtcreator/qmldesigner/toolbar/Main.qml index ddd63b0708f..360800a5f70 100644 --- a/share/qtcreator/qmldesigner/toolbar/Main.qml +++ b/share/qtcreator/qmldesigner/toolbar/Main.qml @@ -26,26 +26,75 @@ Rectangle { anchors.fill: parent visible: !backend.isInDesignMode - ToolbarButton { - id: homeOther - anchors.verticalCenter: parent.verticalCenter + Rectangle { + id: returnExtended + height: homeOther.height + width: backTo.visible ? (homeOther.width + backTo.width + contentRow.spacing + 6) : homeOther.width + anchors.verticalCenter: topToolbarOtherMode.verticalCenter anchors.left: parent.left anchors.leftMargin: 10 - tooltip: backend.isDesignModeEnabled ? qsTr("Switch to Design Mode.") - : qsTr("Switch to Welcome Mode.") - buttonIcon: backend.isDesignModeEnabled ? StudioTheme.Constants.designMode_large - : StudioTheme.Constants.home_large - onClicked: backend.triggerModeChange() - } + color: StudioTheme.Values.themeToolbarBackground + radius: StudioTheme.Values.smallRadius + state: "default" - Text { - id: backTo - visible: backend.isDesignModeEnabled - anchors.verticalCenter: parent.verticalCenter - text: qsTr("Return to Design") - anchors.left: homeOther.right - anchors.leftMargin: 10 - color: StudioTheme.Values.themeTextColor + Row { + id: contentRow + spacing: 6 + anchors.fill: parent + + ToolbarButton { + id: homeOther + tooltip: backend.isDesignModeEnabled ? qsTr("Switch to Design Mode.") + : qsTr("Switch to Welcome Mode.") + buttonIcon: backend.isDesignModeEnabled ? StudioTheme.Constants.designMode_large + : StudioTheme.Constants.home_large + hover: mouseArea.containsMouse + press: mouseArea.pressed + + onClicked: backend.triggerModeChange() + + } + + Text { + id: backTo + height: homeOther.height + visible: backend.isDesignModeEnabled + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + text: qsTr("Return to Design") + color: StudioTheme.Values.themeTextColor + } + } + + MouseArea{ + id: mouseArea + anchors.fill: parent + hoverEnabled: true + onClicked: homeOther.onClicked() + } + + states: [ + State { + name: "default" + when: !mouseArea.containsMouse && !mouseArea.pressed + }, + State { + name: "hover" + when: mouseArea.containsMouse && !mouseArea.pressed + PropertyChanges { + target: returnExtended + color: StudioTheme.Values.themeControlBackground_topToolbarHover + } + }, + State { + name: "pressed" + when: mouseArea.pressed + PropertyChanges { + target: returnExtended + color: StudioTheme.Values.themeInteraction + } + } + ] } } From eae92fd247e38fa0ad7cda72d2ec4a8a1bb27e33 Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Tue, 24 Oct 2023 15:24:09 +0300 Subject: [PATCH 133/242] QmlDesigner: Consider responsive size for the CollectionView Task-number: QDS-11037 Change-Id: Ida49ccf67e6aeb943087b2f51a168f98053c838e Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Mahmoud Badri --- .../CollectionDetailsView.qml | 2 +- .../CollectionItem.qml | 66 ++++++++------- .../CollectionView.qml | 53 ++++++------ .../ModelSourceItem.qml | 81 ++++++++++--------- .../collectioneditor/collectionwidget.cpp | 5 ++ .../collectioneditor/collectionwidget.h | 2 + 6 files changed, 113 insertions(+), 96 deletions(-) diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsView.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsView.qml index 192d027e635..9bd6d23f63c 100644 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsView.qml +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsView.qml @@ -15,7 +15,7 @@ Rectangle { required property var backend required property var sortedModel - implicitWidth: 600 + implicitWidth: 300 implicitHeight: 400 color: StudioTheme.Values.themeBackgroundColorAlternate diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionItem.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionItem.qml index a92cdf065d8..5ce42d0a901 100644 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionItem.qml +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionItem.qml @@ -3,7 +3,7 @@ import QtQuick import QtQuick.Controls -import Qt.labs.platform as PlatformWidgets +import QtQuick.Layouts import HelperWidgets 2.0 as HelperWidgets import StudioControls 1.0 as StudioControls import StudioTheme as StudioTheme @@ -12,7 +12,7 @@ Item { id: root implicitWidth: 300 - implicitHeight: innerRect.height + 6 + implicitHeight: innerRect.height + 3 property color textColor @@ -23,7 +23,7 @@ Item { id: boundingRect anchors.centerIn: root - width: root.width - 24 + width: parent.width height: nameHolder.height clip: true @@ -47,21 +47,23 @@ Item { anchors.fill: parent } - Row { - width: parent.width - threeDots.width - leftPadding: 20 + RowLayout { + width: parent.width Text { id: moveTool property StudioTheme.ControlStyle style: StudioTheme.Values.viewBarButtonStyle - width: moveTool.style.squareControlSize.width - height: nameHolder.height + Layout.preferredWidth: moveTool.style.squareControlSize.width + Layout.preferredHeight: nameHolder.height + Layout.leftMargin: 12 + Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter text: StudioTheme.Constants.dragmarks font.family: StudioTheme.Constants.iconFont.family font.pixelSize: moveTool.style.baseIconFontSize + color: root.textColor horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter } @@ -69,9 +71,12 @@ Item { Text { id: nameHolder + Layout.fillWidth: true + Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter + text: collectionName font.pixelSize: StudioTheme.Values.baseFontSize - color: textColor + color: root.textColor leftPadding: 5 topPadding: 8 rightPadding: 8 @@ -79,43 +84,42 @@ Item { elide: Text.ElideMiddle verticalAlignment: Text.AlignVCenter } - } - Text { - id: threeDots + Text { + id: threeDots - text: StudioTheme.Constants.more_medium - font.family: StudioTheme.Constants.iconFont.family - font.pixelSize: StudioTheme.Values.baseIconFontSize - color: textColor - anchors.right: boundingRect.right - anchors.verticalCenter: parent.verticalCenter - rightPadding: 12 - topPadding: nameHolder.topPadding - bottomPadding: nameHolder.bottomPadding - verticalAlignment: Text.AlignVCenter + Layout.alignment: Qt.AlignRight | Qt.AlignVCenter + text: StudioTheme.Constants.more_medium + font.family: StudioTheme.Constants.iconFont.family + font.pixelSize: StudioTheme.Values.baseIconFontSize + color: root.textColor + rightPadding: 12 + topPadding: nameHolder.topPadding + bottomPadding: nameHolder.bottomPadding + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter - MouseArea { - anchors.fill: parent - acceptedButtons: Qt.RightButton + Qt.LeftButton - onClicked: (event) => { - collectionMenu.open() - event.accepted = true + MouseArea { + anchors.fill: parent + acceptedButtons: Qt.RightButton | Qt.LeftButton + onClicked: collectionMenu.popup() } } } } - PlatformWidgets.Menu { + StudioControls.Menu { id: collectionMenu - PlatformWidgets.MenuItem { + closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside + + StudioControls.MenuItem { text: qsTr("Delete") shortcut: StandardKey.Delete onTriggered: deleteDialog.open() } - PlatformWidgets.MenuItem { + StudioControls.MenuItem { text: qsTr("Rename") shortcut: StandardKey.Replace onTriggered: renameDialog.open() diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionView.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionView.qml index 4b2708e96ca..4903913b7c0 100644 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionView.qml +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionView.qml @@ -3,6 +3,7 @@ import QtQuick import QtQuick.Controls +import QtQuick.Layouts import QtQuickDesignerTheme 1.0 import HelperWidgets 2.0 as HelperWidgets import StudioTheme 1.0 as StudioTheme @@ -52,19 +53,23 @@ Item { message: "" } - Rectangle { - id: collectionsRect + GridLayout { + id: grid + readonly property bool isHorizontal: width >= 500 - color: StudioTheme.Values.themeToolbarBackground - width: 300 - height: root.height + anchors.fill: parent + columns: isHorizontal ? 2 : 1 - Column { - width: parent.width + ColumnLayout { + id: collectionsSideBar + + Layout.alignment: Qt.AlignTop | Qt.AlignLeft + Layout.minimumWidth: 300 + Layout.fillWidth: !grid.isHorizontal Rectangle { - width: parent.width - height: StudioTheme.Values.height + 5 + Layout.fillWidth: true + Layout.preferredHeight: StudioTheme.Values.height + 5 color: StudioTheme.Values.themeToolbarBackground Text { @@ -100,9 +105,10 @@ Item { } Rectangle { // Collections - width: parent.width + Layout.fillWidth: true color: StudioTheme.Values.themeBackgroundColorNormal - height: 330 + Layout.minimumHeight: 150 + Layout.preferredHeight: sourceListView.contentHeight MouseArea { anchors.fill: parent @@ -116,20 +122,19 @@ Item { ListView { id: sourceListView - width: parent.width - height: contentHeight + anchors.fill: parent model: root.model delegate: ModelSourceItem { + implicitWidth: sourceListView.width onDeleteItem: root.model.removeRow(index) } - } } Rectangle { - width: parent.width - height: addCollectionButton.height + Layout.fillWidth: true + Layout.preferredHeight: addCollectionButton.height color: StudioTheme.Values.themeBackgroundColorNormal IconTextButton { @@ -142,17 +147,13 @@ Item { } } } - } - CollectionDetailsView { - model: root.collectionDetailsModel - backend: root.model - sortedModel: root.collectionDetailsSortFilterModel - anchors { - left: collectionsRect.right - right: parent.right - top: parent.top - bottom: parent.bottom + CollectionDetailsView { + model: root.collectionDetailsModel + backend: root.model + sortedModel: root.collectionDetailsSortFilterModel + Layout.fillHeight: true + Layout.fillWidth: true } } } diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/ModelSourceItem.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/ModelSourceItem.qml index ef609e7f2be..74cc718a6b0 100644 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/ModelSourceItem.qml +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/ModelSourceItem.qml @@ -3,6 +3,7 @@ import QtQuick import QtQuick.Controls +import QtQuick.Layouts import HelperWidgets 2.0 as HelperWidgets import StudioControls 1.0 as StudioControls import StudioTheme as StudioTheme @@ -11,7 +12,7 @@ Item { id: root implicitWidth: 300 - implicitHeight: wholeColumn.height + 6 + implicitHeight: wholeColumn.height property color textColor property var collectionModel @@ -26,15 +27,17 @@ Item { root.expanded = !root.expanded || sourceIsSelected; } - Column { + ColumnLayout { id: wholeColumn + width: parent.width + spacing: 0 Item { id: boundingRect - anchors.centerIn: root - width: root.width - 24 - height: nameHolder.height + Layout.fillWidth: true + Layout.preferredHeight: nameHolder.height + Layout.leftMargin: 6 clip: true MouseArea { @@ -62,17 +65,17 @@ Item { anchors.fill: parent } - Row { - width: parent.width - threeDots.width - leftPadding: 20 + RowLayout { + width: parent.width Text { id: expandButton property StudioTheme.ControlStyle style: StudioTheme.Values.viewBarButtonStyle - width: expandButton.style.squareControlSize.width - height: nameHolder.height + Layout.preferredWidth: expandButton.style.squareControlSize.width + Layout.preferredHeight: nameHolder.height + Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter text: StudioTheme.Constants.startNode font.family: StudioTheme.Constants.iconFont.family @@ -90,17 +93,17 @@ Item { MouseArea { anchors.fill: parent - acceptedButtons: Qt.RightButton + Qt.LeftButton - onClicked: (event) => { - root.toggleExpanded() - event.accepted = true - } + acceptedButtons: Qt.RightButton | Qt.LeftButton + onClicked: root.toggleExpanded() } } Text { id: nameHolder + Layout.fillWidth: true + Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter + text: sourceName font.pixelSize: StudioTheme.Values.baseFontSize color: textColor @@ -109,30 +112,29 @@ Item { rightPadding: 8 bottomPadding: 8 elide: Text.ElideMiddle + horizontalAlignment: Text.AlignLeft verticalAlignment: Text.AlignVCenter } - } - Text { - id: threeDots + Text { + id: threeDots - text: StudioTheme.Constants.more_medium - font.family: StudioTheme.Constants.iconFont.family - font.pixelSize: StudioTheme.Values.baseIconFontSize - color: textColor - anchors.right: boundingRect.right - anchors.verticalCenter: parent.verticalCenter - rightPadding: 12 - topPadding: nameHolder.topPadding - bottomPadding: nameHolder.bottomPadding - verticalAlignment: Text.AlignVCenter + Layout.alignment: Qt.AlignRight | Qt.AlignVCenter - MouseArea { - anchors.fill: parent - acceptedButtons: Qt.RightButton + Qt.LeftButton - onClicked: (event) => { - collectionMenu.popup() - event.accepted = true + text: StudioTheme.Constants.more_medium + font.family: StudioTheme.Constants.iconFont.family + font.pixelSize: StudioTheme.Values.baseIconFontSize + color: textColor + rightPadding: 12 + topPadding: nameHolder.topPadding + bottomPadding: nameHolder.bottomPadding + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + + MouseArea { + anchors.fill: parent + acceptedButtons: Qt.RightButton | Qt.LeftButton + onClicked: collectionMenu.popup() } } } @@ -141,17 +143,18 @@ Item { ListView { id: collectionListView - width: parent.width - height: root.expanded ? contentHeight : 0 + Layout.fillWidth: true + Layout.preferredHeight: root.expanded ? contentHeight : 0 + Layout.leftMargin: 6 model: collections clip: true - Behavior on height { + Behavior on Layout.preferredHeight { NumberAnimation {duration: 500} } delegate: CollectionItem { - width: parent.width + width: collectionListView.width onDeleteItem: root.model.removeRow(index) } } @@ -160,6 +163,8 @@ Item { StudioControls.Menu { id: collectionMenu + closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside + StudioControls.MenuItem { text: qsTr("Delete") shortcut: StandardKey.Delete diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp index 7811ee622cd..2cb09044be1 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp @@ -129,6 +129,11 @@ void CollectionWidget::reloadQmlSource() m_quickWidget->setSource(QUrl::fromLocalFile(collectionViewQmlPath)); } +QSize CollectionWidget::minimumSizeHint() const +{ + return {300, 400}; +} + bool CollectionWidget::loadJsonFile(const QString &jsonFileAddress) { if (!isJsonFile(jsonFileAddress)) diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.h b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.h index f1e3e232e89..f902c86c226 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.h +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.h @@ -30,6 +30,8 @@ public: void reloadQmlSource(); + virtual QSize minimumSizeHint() const; + Q_INVOKABLE bool loadJsonFile(const QString &jsonFileAddress); Q_INVOKABLE bool loadCsvFile(const QString &collectionName, const QString &csvFileAddress); Q_INVOKABLE bool isJsonFile(const QString &jsonFileAddress) const; From f5ec01a1e8ea63add42123a3d36c16967f685284 Mon Sep 17 00:00:00 2001 From: Yasser Grimes Date: Mon, 23 Oct 2023 15:59:47 +0300 Subject: [PATCH 134/242] QmlDesigner: Add the QtQuickUltralite profiling module for QtMCUs 2.6 Task-number: QDS-10392 Change-Id: If67466e151f2c57d1c04233346c455fb96fa0feb Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Kwangsub Kim Reviewed-by: Reviewed-by: Aleksei German --- share/qtcreator/qmldesigner/qt4mcu/metadata.qml | 2 +- share/qtcreator/qmldesigner/qt4mcu/qul-26.qml | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/share/qtcreator/qmldesigner/qt4mcu/metadata.qml b/share/qtcreator/qmldesigner/qt4mcu/metadata.qml index d208ee096ac..b4ae840bddb 100644 --- a/share/qtcreator/qmldesigner/qt4mcu/metadata.qml +++ b/share/qtcreator/qmldesigner/qt4mcu/metadata.qml @@ -5,7 +5,7 @@ Metadata { id: metadataFile - defaultVersion: v25 + defaultVersion: v26 VersionData { id: v14 diff --git a/share/qtcreator/qmldesigner/qt4mcu/qul-26.qml b/share/qtcreator/qmldesigner/qt4mcu/qul-26.qml index 26c68a6e7d4..7943ecabba5 100644 --- a/share/qtcreator/qmldesigner/qt4mcu/qul-26.qml +++ b/share/qtcreator/qmldesigner/qt4mcu/qul-26.qml @@ -57,7 +57,8 @@ VersionData { "QtQuick.Shapes", "QtQuick.Timeline", "QtQuickUltralite.Extras", - "QtQuickUltralite.Layers" + "QtQuickUltralite.Layers", + "QtQuickUltralite.Profiling" ] bannedImports: [ From 673cf2605fc240ad150150829aa85c1bae385b64 Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Tue, 31 Oct 2023 11:51:37 +0200 Subject: [PATCH 135/242] QmlDesigner: Replace StudioModels with the QtQuick.Studio.Utils Task-number: QDS-11010 Change-Id: I8d7c33d2d88ed28f67bc9acdd909bbfba700b318 Reviewed-by: Thomas Hartmann --- .../collectioneditor/collectioneditorconstants.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorconstants.h b/src/plugins/qmldesigner/components/collectioneditor/collectioneditorconstants.h index f5e18a93ec3..11ceb034fa5 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorconstants.h +++ b/src/plugins/qmldesigner/components/collectioneditor/collectioneditorconstants.h @@ -7,10 +7,10 @@ namespace QmlDesigner::CollectionEditor { enum class SourceFormat { Unknown, Json, Csv }; -inline constexpr char SOURCEFILE_PROPERTY[] = "sourceFile"; +inline constexpr char SOURCEFILE_PROPERTY[] = "source"; -inline constexpr char COLLECTIONMODEL_IMPORT[] = "QtQuick.Studio.Models"; -inline constexpr char JSONCOLLECTIONMODEL_TYPENAME[] = "QtQuick.Studio.Models.JsonSourceModel"; -inline constexpr char CSVCOLLECTIONMODEL_TYPENAME[] = "QtQuick.Studio.Models.CsvSourceModel"; +inline constexpr char COLLECTIONMODEL_IMPORT[] = "QtQuick.Studio.Utils"; +inline constexpr char JSONCOLLECTIONMODEL_TYPENAME[] = "QtQuick.Studio.Utils.JsonListModel"; +inline constexpr char CSVCOLLECTIONMODEL_TYPENAME[] = "QtQuick.Studio.Utils.CsvTableModel"; } // namespace QmlDesigner::CollectionEditor From 4f2a06297752398b2a0eac498e28da16d2f0b0ea Mon Sep 17 00:00:00 2001 From: Amr Essam Date: Wed, 1 Nov 2023 15:25:43 +0200 Subject: [PATCH 136/242] QmlDesigner: Create the composition stack with multiple effects Task-number: QDS-11064 Change-Id: I55fab43e572a989b450d4411f1208b959c4dd9c4 Reviewed-by: Mahmoud Badri --- .../effectmakernew/effectmakermodel.cpp | 84 ++++++++++--------- src/plugins/effectmakernew/effectmakermodel.h | 1 + 2 files changed, 45 insertions(+), 40 deletions(-) diff --git a/src/plugins/effectmakernew/effectmakermodel.cpp b/src/plugins/effectmakernew/effectmakermodel.cpp index 7f8296fd3c7..de34128747b 100644 --- a/src/plugins/effectmakernew/effectmakermodel.cpp +++ b/src/plugins/effectmakernew/effectmakermodel.cpp @@ -47,20 +47,6 @@ static bool writeToFile(const QByteArray &buf, const QString &filename, FileType EffectMakerModel::EffectMakerModel(QObject *parent) : QAbstractListModel{parent} { - m_vertexSourceFile.setFileTemplate(QDir::tempPath() + "/dsem_XXXXXX.vert"); - m_fragmentSourceFile.setFileTemplate(QDir::tempPath() + "/dsem_XXXXXX.frag"); - m_vertexShaderFile.setFileTemplate(QDir::tempPath() + "/dsem_XXXXXX.vert.qsb"); - m_fragmentShaderFile.setFileTemplate(QDir::tempPath() + "/dsem_XXXXXX.frag.qsb"); - if (!m_vertexSourceFile.open() || !m_fragmentSourceFile.open() - || !m_vertexShaderFile.open() || !m_fragmentShaderFile.open()) { - qWarning() << "Unable to open temporary files"; - } else { - m_vertexSourceFilename = m_vertexSourceFile.fileName(); - m_fragmentSourceFilename = m_fragmentSourceFile.fileName(); - m_vertexShaderFilename = m_vertexShaderFile.fileName(); - m_fragmentShaderFilename = m_fragmentShaderFile.fileName(); - } - connect(&m_fileWatcher, &Utils::FileSystemWatcher::fileChanged, this, [this]() { // Update component with images not set. m_loadComponentImages = false; @@ -641,19 +627,15 @@ QString EffectMakerModel::generateVertexShader(bool includeUniforms) m_shaderVaryingVariables.clear(); for (const CompositionNode *n : std::as_const(m_nodes)) { if (!n->vertexCode().isEmpty() && n->isEnabled()) { - if (n->type() == CompositionNode::NodeType::SourceNode) { - s_sourceCode = n->vertexCode().split('\n'); - } else if (n->type() == CompositionNode::NodeType::CustomNode) { - const QStringList vertexCode = n->vertexCode().split('\n'); - int mainIndex = getTagIndex(vertexCode, QStringLiteral("main")); - int line = 0; - for (const QString &ss : vertexCode) { - if (mainIndex == -1 || line > mainIndex) - s_main += QStringLiteral(" ") + ss + '\n'; - else if (line < mainIndex) - s_root += processVertexRootLine(ss); - line++; - } + const QStringList vertexCode = n->vertexCode().split('\n'); + int mainIndex = getTagIndex(vertexCode, "main"); + int line = 0; + for (const QString &ss : vertexCode) { + if (mainIndex == -1 || line > mainIndex) + s_main += " " + ss + '\n'; + else if (line < mainIndex) + s_root += processVertexRootLine(ss); + line++; } } } @@ -700,19 +682,15 @@ QString EffectMakerModel::generateFragmentShader(bool includeUniforms) QStringList s_sourceCode; for (const CompositionNode *n : std::as_const(m_nodes)) { if (!n->fragmentCode().isEmpty() && n->isEnabled()) { - if (n->type() == CompositionNode::NodeType::SourceNode) { - s_sourceCode = n->fragmentCode().split('\n'); - } else if (n->type() == CompositionNode::NodeType::CustomNode) { - const QStringList fragmentCode = n->fragmentCode().split('\n'); - int mainIndex = getTagIndex(fragmentCode, QStringLiteral("main")); - int line = 0; - for (const QString &ss : fragmentCode) { - if (mainIndex == -1 || line > mainIndex) - s_main += QStringLiteral(" ") + ss + '\n'; - else if (line < mainIndex) - s_root += processFragmentRootLine(ss); - line++; - } + const QStringList fragmentCode = n->fragmentCode().split('\n'); + int mainIndex = getTagIndex(fragmentCode, QStringLiteral("main")); + int line = 0; + for (const QString &ss : fragmentCode) { + if (mainIndex == -1 || line > mainIndex) + s_main += QStringLiteral(" ") + ss + '\n'; + else if (line < mainIndex) + s_root += processFragmentRootLine(ss); + line++; } } } @@ -830,6 +808,30 @@ void EffectMakerModel::updateCustomUniforms() m_exportedEffectPropertiesString = exportedEffectPropertiesString; } +void EffectMakerModel::createFiles() +{ + if (QFileInfo(m_vertexShaderFilename).exists()) + QFile(m_vertexShaderFilename).remove(); + if (QFileInfo(m_fragmentShaderFilename).exists()) + QFile(m_fragmentShaderFilename).remove(); + + auto vertexShaderFile = QTemporaryFile(QDir::tempPath() + "/dsem_XXXXXX.vert.qsb"); + auto fragmentShaderFile = QTemporaryFile(QDir::tempPath() + "/dsem_XXXXXX.frag.qsb"); + + m_vertexSourceFile.setFileTemplate(QDir::tempPath() + "/dsem_XXXXXX.vert"); + m_fragmentSourceFile.setFileTemplate(QDir::tempPath() + "/dsem_XXXXXX.frag"); + + if (!m_vertexSourceFile.open() || !m_fragmentSourceFile.open() + || !vertexShaderFile.open() || !fragmentShaderFile.open()) { + qWarning() << "Unable to open temporary files"; + } else { + m_vertexSourceFilename = m_vertexSourceFile.fileName(); + m_fragmentSourceFilename = m_fragmentSourceFile.fileName(); + m_vertexShaderFilename = vertexShaderFile.fileName(); + m_fragmentShaderFilename = fragmentShaderFile.fileName(); + } +} + void EffectMakerModel::bakeShaders() { const QString failMessage = "Shader baking failed: "; @@ -840,6 +842,8 @@ void EffectMakerModel::bakeShaders() return; } + createFiles(); + resetEffectError(ErrorPreprocessor); if (m_vertexShader == generateVertexShader() && m_fragmentShader == generateFragmentShader()) { setShadersUpToDate(true); diff --git a/src/plugins/effectmakernew/effectmakermodel.h b/src/plugins/effectmakernew/effectmakermodel.h index 77f3df5577e..02d7c57085d 100644 --- a/src/plugins/effectmakernew/effectmakermodel.h +++ b/src/plugins/effectmakernew/effectmakermodel.h @@ -139,6 +139,7 @@ private: void clearImageWatchers(); void updateCustomUniforms(); + void createFiles(); void bakeShaders(); QString mipmapPropertyName(const QString &name) const; From 6eb522f0cab48e9107c5c15bced80bd5c1c7193d Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Tue, 31 Oct 2023 10:28:03 +0200 Subject: [PATCH 137/242] QmlDesigner: Apply collection renames to the sources Task-number: QDS-11071 Change-Id: Ia03c11d3f61e12c1f71aba147d7686787dffb4a1 Reviewed-by: Mats Honkamaa --- .../collectioneditor/collectionlistmodel.cpp | 14 +- .../collectionsourcemodel.cpp | 131 ++++++++++++++++-- .../collectioneditor/collectionsourcemodel.h | 3 + .../collectioneditor/collectionwidget.cpp | 2 + 4 files changed, 133 insertions(+), 17 deletions(-) diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionlistmodel.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionlistmodel.cpp index 2d7952c1021..3745fef6983 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionlistmodel.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionlistmodel.cpp @@ -20,6 +20,7 @@ bool containsItem(const std::initializer_list &container, const Value auto it = std::find(begin, end, value); return it != end; } + } // namespace namespace QmlDesigner { @@ -52,8 +53,17 @@ bool CollectionListModel::setData(const QModelIndex &index, const QVariant &valu if (!index.isValid()) return false; - if (containsItem({IdRole, Qt::EditRole, Qt::DisplayRole}, role)) { - return Super::setData(index, value); + if (containsItem({Qt::EditRole, Qt::DisplayRole, NameRole}, role)) { + if (contains(value.toString())) + return false; + + QString oldName = collectionNameAt(index.row()); + bool nameChanged = Super::setData(index, value); + if (nameChanged) { + QString newName = collectionNameAt(index.row()); + emit this->collectionNameChanged(oldName, newName); + } + return nameChanged; } else if (role == SelectedRole) { if (value.toBool() != index.data(SelectedRole).toBool()) { setSelectedIndex(value.toBool() ? index.row() : -1); diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.cpp index a33aff95787..3dfec1829b6 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.cpp @@ -232,11 +232,7 @@ void CollectionSourceModel::setSources(const ModelNodes &sources) auto loadedCollection = loadCollection(collectionSource); m_collectionList.append(loadedCollection); - connect(loadedCollection.data(), - &CollectionListModel::selectedIndexChanged, - this, - &CollectionSourceModel::onSelectedCollectionChanged, - Qt::UniqueConnection); + registerCollection(loadedCollection); } updateEmpty(); @@ -269,11 +265,7 @@ void CollectionSourceModel::addSource(const ModelNode &node) auto loadedCollection = loadCollection(node); m_collectionList.append(loadedCollection); - connect(loadedCollection.data(), - &CollectionListModel::selectedIndexChanged, - this, - &CollectionSourceModel::onSelectedCollectionChanged, - Qt::UniqueConnection); + registerCollection(loadedCollection); updateEmpty(); endInsertRows(); @@ -328,30 +320,32 @@ bool CollectionSourceModel::addCollectionToSource(const ModelNode &node, QFileInfo sourceFileInfo(sourceFileAddress); if (!sourceFileInfo.isFile()) - return returnError(tr("Selected node should have a valid source file address")); + return returnError(tr("Selected node must have a valid source file address")); QFile jsonFile(sourceFileAddress); if (!jsonFile.open(QFile::ReadWrite)) - return returnError(tr("Can't open the file to read.\n") + jsonFile.errorString()); + return returnError(tr("Can't read or write \"%1\".\n%2") + .arg(sourceFileInfo.absoluteFilePath(), jsonFile.errorString())); QJsonParseError parseError; QJsonDocument document = QJsonDocument::fromJson(jsonFile.readAll(), &parseError); if (parseError.error != QJsonParseError::NoError) - return returnError(tr("Saved json file is messy.\n") + parseError.errorString()); + return returnError(tr("\"%1\" is corrupted.\n%2") + .arg(sourceFileInfo.absoluteFilePath(), parseError.errorString())); if (document.isObject()) { QJsonObject sourceObject = document.object(); sourceObject.insert(collectionName, QJsonArray{}); document.setObject(sourceObject); if (!jsonFile.resize(0)) - return returnError(tr("Can't clean the json file.")); + return returnError(tr("Can't clean \"%1\".").arg(sourceFileInfo.absoluteFilePath())); QByteArray jsonData = document.toJson(); auto writtenBytes = jsonFile.write(jsonData); jsonFile.close(); if (writtenBytes != jsonData.size()) - return returnError(tr("Can't write to the json file.")); + return returnError(tr("Can't write to \"%1\".").arg(sourceFileInfo.absoluteFilePath())); updateCollectionList(index(idx)); @@ -456,6 +450,98 @@ void CollectionSourceModel::onSelectedCollectionChanged(int collectionIndex) } } +void CollectionSourceModel::onCollectionNameChanged(const QString &oldName, const QString &newName) +{ + CollectionListModel *collectionList = qobject_cast(sender()); + QTC_ASSERT(collectionList, return); + + auto emitRenameWarning = [this](const QString &msg) -> void { + emit this->warning(tr("Rename Collection"), msg); + }; + + const ModelNode node = collectionList->sourceNode(); + const QModelIndex nodeIndex = indexOfNode(node); + + if (!nodeIndex.isValid()) { + emitRenameWarning(tr("Invalid node")); + return; + } + + if (node.type() == CollectionEditor::CSVCOLLECTIONMODEL_TYPENAME) { + if (!setData(nodeIndex, newName, NameRole)) + emitRenameWarning(tr("Can't rename the node")); + return; + } else if (node.type() != CollectionEditor::JSONCOLLECTIONMODEL_TYPENAME) { + emitRenameWarning(tr("Invalid node type")); + return; + } + + QString sourceFileAddress = node.variantProperty(CollectionEditor::SOURCEFILE_PROPERTY) + .value() + .toString(); + + QFileInfo sourceFileInfo(sourceFileAddress); + if (!sourceFileInfo.isFile()) { + emitRenameWarning(tr("Selected node must have a valid source file address")); + return; + } + + QFile jsonFile(sourceFileAddress); + if (!jsonFile.open(QFile::ReadWrite)) { + return emitRenameWarning(tr("Can't read or write \"%1\".\n%2") + .arg(sourceFileInfo.absoluteFilePath(), jsonFile.errorString())); + return; + } + + QJsonParseError parseError; + QJsonDocument document = QJsonDocument::fromJson(jsonFile.readAll(), &parseError); + if (parseError.error != QJsonParseError::NoError) { + emitRenameWarning(tr("\"%1\" is corrupted.\n%2") + .arg(sourceFileInfo.absoluteFilePath(), parseError.errorString())); + return; + } + + if (document.isObject()) { + QJsonObject rootObject = document.object(); + + bool collectionContainsOldName = rootObject.contains(oldName); + bool collectionContainsNewName = rootObject.contains(newName); + + if (!collectionContainsOldName) { + emitRenameWarning( + tr("Collection doesn't contain the old collection name (%1).").arg(oldName)); + return; + } + + if (collectionContainsNewName) { + emitRenameWarning( + tr("The collection name \"%1\" already exists in the source file.").arg(newName)); + return; + } + + QJsonValue oldValue = rootObject.value(oldName); + rootObject.insert(newName, oldValue); + rootObject.remove(oldName); + + document.setObject(rootObject); + if (!jsonFile.resize(0)) { + emitRenameWarning(tr("Can't clean \"%1\".").arg(sourceFileInfo.absoluteFilePath())); + return; + } + + QByteArray jsonData = document.toJson(); + auto writtenBytes = jsonFile.write(jsonData); + jsonFile.close(); + + if (writtenBytes != jsonData.size()) { + emitRenameWarning(tr("Can't write to \"%1\".").arg(sourceFileInfo.absoluteFilePath())); + return; + } + + updateCollectionList(nodeIndex); + } +} + void CollectionSourceModel::setSelectedIndex(int idx) { idx = (idx > -1 && idx < m_collectionSources.count()) ? idx : -1; @@ -514,6 +600,21 @@ void CollectionSourceModel::updateCollectionList(QModelIndex index) } } +void CollectionSourceModel::registerCollection(const QSharedPointer &collection) +{ + connect(collection.data(), + &CollectionListModel::selectedIndexChanged, + this, + &CollectionSourceModel::onSelectedCollectionChanged, + Qt::UniqueConnection); + + connect(collection.data(), + &CollectionListModel::collectionNameChanged, + this, + &CollectionSourceModel::onCollectionNameChanged, + Qt::UniqueConnection); +} + QModelIndex CollectionSourceModel::indexOfNode(const ModelNode &node) const { return index(m_sourceIndexHash.value(node.internalId(), -1)); diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.h b/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.h index 447df5d5dea..2da625502c2 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.h +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.h @@ -75,14 +75,17 @@ signals: void selectedIndexChanged(int idx); void collectionSelected(const ModelNode &sourceNode, const QString &collectionName); void isEmptyChanged(bool); + void warning(const QString &title, const QString &body); private slots: void onSelectedCollectionChanged(int collectionIndex); + void onCollectionNameChanged(const QString &oldName, const QString &newName); private: void setSelectedIndex(int idx); void updateEmpty(); void updateCollectionList(QModelIndex index); + void registerCollection(const QSharedPointer &collection); QModelIndex indexOfNode(const ModelNode &node) const; using Super = QAbstractListModel; diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp index 2cb09044be1..e956f5a2932 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp @@ -71,6 +71,8 @@ CollectionWidget::CollectionWidget(CollectionView *view) icontext->setContext(context); icontext->setWidget(this); + connect(m_sourceModel, &CollectionSourceModel::warning, this, &CollectionWidget::warn); + m_collectionDetailsSortFilterModel->setSourceModel(m_collectionDetailsModel); m_quickWidget->quickWidget()->setObjectName(Constants::OBJECT_NAME_COLLECTION_EDITOR); From 57ebcd2c4fbfbf8d0d6f9db8076af6b77dccfc9e Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Tue, 31 Oct 2023 18:44:52 +0100 Subject: [PATCH 138/242] QmlDesigner: Use simplifiedTypeName in variantTypeId MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The optional prefix has to be stripped. Task-number: QDS-11060 Change-Id: I65c9203e54179f58aa8512ce43efbb5530753e16 Reviewed-by: Henning Gründl Reviewed-by: Reviewed-by: Qt CI Patch Build Bot --- .../qmldesigner/designercore/metainfo/nodemetainfo.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp b/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp index 3a38ac06494..d4e102ef66e 100644 --- a/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp +++ b/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp @@ -1118,9 +1118,15 @@ bool NodeMetaInfoPrivate::cleverCheckType(const TypeName &otherType) const return typeName == convertedName.toUtf8(); } +static TypeName toSimplifiedTypeName(const TypeName &typeName) +{ + return typeName.split('.').constLast(); +} + QVariant::Type NodeMetaInfoPrivate::variantTypeId(const PropertyName &propertyName) const { - TypeName typeName = propertyType(propertyName); + TypeName typeName = toSimplifiedTypeName(propertyType(propertyName)); + if (typeName == "string") return QVariant::String; @@ -1929,7 +1935,7 @@ TypeName NodeMetaInfo::typeName() const TypeName NodeMetaInfo::simplifiedTypeName() const { if (isValid()) - return typeName().split('.').constLast(); + return toSimplifiedTypeName(typeName()); return {}; } From aca600385af6afed8c73747b69c3d1f3ac4c9173 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Wed, 1 Nov 2023 09:40:49 +0100 Subject: [PATCH 139/242] QmlDesigner: Add custom/dynamic properties to QtObject MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I0d42692bbc9fe9c376da447862c26272fe4296d2 Reviewed-by: Reviewed-by: Henning Gründl --- .../propertyEditorQmlSources/QtQuick/QtObjectPane.qml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/QtObjectPane.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/QtObjectPane.qml index 36d39f829cf..92e157dfdc4 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/QtObjectPane.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/QtObjectPane.qml @@ -12,6 +12,11 @@ PropertyEditorPane { ComponentSection {} + DynamicPropertiesSection { + propertiesModel: SelectionDynamicPropertiesModel {} + visible: !hasMultiSelection + } + Column { anchors.left: parent.left anchors.right: parent.right From fa3fcfe4c4b21566aea80ac0943f884b268b9cf4 Mon Sep 17 00:00:00 2001 From: Aleksei German Date: Mon, 30 Oct 2023 18:07:57 +0100 Subject: [PATCH 140/242] QmlDesigner: Add AnimatedSprite mcu metadata Task-number: QDS-11054 Change-Id: I22dbb9521700b620e272248d2fd7bdcf8445f710 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Aleksei German --- share/qtcreator/qmldesigner/qt4mcu/qul-24.qml | 6 ++++++ share/qtcreator/qmldesigner/qt4mcu/qul-25.qml | 6 ++++++ share/qtcreator/qmldesigner/qt4mcu/qul-26.qml | 6 ++++++ 3 files changed, 18 insertions(+) diff --git a/share/qtcreator/qmldesigner/qt4mcu/qul-24.qml b/share/qtcreator/qmldesigner/qt4mcu/qul-24.qml index dbeeabf971d..202033b65cb 100644 --- a/share/qtcreator/qmldesigner/qt4mcu/qul-24.qml +++ b/share/qtcreator/qmldesigner/qt4mcu/qul-24.qml @@ -149,6 +149,12 @@ VersionData { bannedProperties: ["paused"] } + QtQuick.AnimatedSprite { + allowedProperties: ["currentFrame", "frameCount"] + bannedProperties: ["finishBehavior", "frameRate", "frameSync", + "frameX", "frameY", "interpolate", "reverse", "paused"] + } + //Quick Controls2 Items and properties: QtQuick.Controls.Control { diff --git a/share/qtcreator/qmldesigner/qt4mcu/qul-25.qml b/share/qtcreator/qmldesigner/qt4mcu/qul-25.qml index 3fb7e1b9259..eada7c998b6 100644 --- a/share/qtcreator/qmldesigner/qt4mcu/qul-25.qml +++ b/share/qtcreator/qmldesigner/qt4mcu/qul-25.qml @@ -146,6 +146,12 @@ VersionData { bannedProperties: ["paused"] } + QtQuick.AnimatedSprite { + allowedProperties: ["currentFrame", "frameCount", "paused"] + bannedProperties: ["finishBehavior", "frameRate", "frameSync", + "frameX", "frameY", "interpolate", "reverse"] + } + //Quick Controls2 Items and properties: QtQuick.Controls.Control { diff --git a/share/qtcreator/qmldesigner/qt4mcu/qul-26.qml b/share/qtcreator/qmldesigner/qt4mcu/qul-26.qml index 7943ecabba5..55d04d18bed 100644 --- a/share/qtcreator/qmldesigner/qt4mcu/qul-26.qml +++ b/share/qtcreator/qmldesigner/qt4mcu/qul-26.qml @@ -149,6 +149,12 @@ VersionData { bannedProperties: ["paused"] } + QtQuick.AnimatedSprite { + allowedProperties: ["currentFrame", "frameCount", "paused"] + bannedProperties: ["finishBehavior", "frameRate", "frameSync", + "frameX", "frameY", "interpolate", "reverse"] + } + //Quick Controls2 Items and properties: QtQuick.Controls.Control { From 32c0aa2dbfa06dfcd1b9e1bf6d4b321d12b046bd Mon Sep 17 00:00:00 2001 From: Aleksei German Date: Thu, 2 Nov 2023 11:08:34 +0100 Subject: [PATCH 141/242] QmlDesigner: Fix AnimatedSprite loops Change-Id: I0943fee49e32d4ceb257563f7ace618d97d768c1 Reviewed-by: Aleksei German --- .../QtQuick/AnimatedSpriteSpecifics.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/AnimatedSpriteSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/AnimatedSpriteSpecifics.qml index 1dbd0a343b7..d5e93be11ed 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/AnimatedSpriteSpecifics.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/AnimatedSpriteSpecifics.qml @@ -240,7 +240,7 @@ Column { + StudioTheme.Values.actionIndicatorWidth backendValue: backendValues.loops decimals: 0 - minimumValue: 0 + minimumValue: -1 //AnimatedSprite.Infinite = -1 maximumValue: 100000 enabled: backendValue.isAvailable } From b46d9de784c87cbf2d2fdeffd98ae3a077bf80bf Mon Sep 17 00:00:00 2001 From: Johanna Vanhatapio Date: Thu, 19 Oct 2023 19:13:52 +0300 Subject: [PATCH 142/242] Doc: Remove outdated videos Task-number: QDS-10565 Change-Id: I91d8bf4f6fb8f0b5b756fa4a3bf94753ab16adfb Reviewed-by: Mats Honkamaa --- .../extraimages/qtdesignstudio-extraimages.qdocconf | 3 --- .../src/components/qtquick-preset-components.qdoc | 13 ------------- doc/qtdesignstudio/src/views/qtquick-designer.qdoc | 4 ---- 3 files changed, 20 deletions(-) diff --git a/doc/qtcreator/images/extraimages/qtdesignstudio-extraimages.qdocconf b/doc/qtcreator/images/extraimages/qtdesignstudio-extraimages.qdocconf index 35f519210c0..6eead9aa2cd 100644 --- a/doc/qtcreator/images/extraimages/qtdesignstudio-extraimages.qdocconf +++ b/doc/qtcreator/images/extraimages/qtdesignstudio-extraimages.qdocconf @@ -2,7 +2,6 @@ images/commercial.png \ images/SsFWyUeAA_4.jpg \ images/9ihYeC0YJ0M.jpg \ - images/RfEYO-5Mw6s.jpg \ images/yOUdg1o2KJM.jpg \ images/DVWd_xMMgvg.jpg \ images/Ed8WS03C-Vk.jpg \ @@ -11,7 +10,5 @@ images/w1yhDl93YI0.jpg \ images/pEETxSxYazg.jpg \ images/V3Po15bNErw.jpg \ - images/bMXeeQw6BYs.jpg \ - images/u3kZJjlk3CY.jpg \ images/9MqUCP6JLCQ.jpg \ images/KDxnMQzgmIY.jpg diff --git a/doc/qtdesignstudio/src/components/qtquick-preset-components.qdoc b/doc/qtdesignstudio/src/components/qtquick-preset-components.qdoc index 875d0f9ea67..19ee8e60ddc 100644 --- a/doc/qtdesignstudio/src/components/qtquick-preset-components.qdoc +++ b/doc/qtdesignstudio/src/components/qtquick-preset-components.qdoc @@ -73,19 +73,6 @@ \note Using 3D components will affect the performance of your UI. Do not use 3D components if the same results can be achieved using 2D components. - \section2 Videos About 3D Components - - The following video shows you how to add the components included in the - \uicontrol {Qt Quick 3D} module, such as 3D models, cameras, and lights, - to your scene: - - \youtube u3kZJjlk3CY - - The following video shows you how to use the custom shader utilities, 3D - effects, and materials: - - \youtube bMXeeQw6BYs - The following video shows you how to combine 2D and 3D components: \youtube w1yhDl93YI0 diff --git a/doc/qtdesignstudio/src/views/qtquick-designer.qdoc b/doc/qtdesignstudio/src/views/qtquick-designer.qdoc index 60e1dc191f6..dbf07849f0b 100644 --- a/doc/qtdesignstudio/src/views/qtquick-designer.qdoc +++ b/doc/qtdesignstudio/src/views/qtquick-designer.qdoc @@ -25,10 +25,6 @@ You can move the views anywhere on the screen and save them as \e workspaces, as instructed in \l {Managing Workspaces}. - To learn more about using the design views, see the following video: - - \youtube RfEYO-5Mw6s - \section1 Summary of Design Views In addition to the summary of design views, the table below includes an MCU From 6a5e7920ce625ad83482aa367795f7ecc24d156c Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Tue, 31 Oct 2023 18:07:19 +0200 Subject: [PATCH 143/242] EffectMaker: Enable using project assets as effect textures Also fixed few warnings by adding dummy needed context properties. Change-Id: Ieed0f9d409302ba9ff1409b9081cda942e46d2e9 Reviewed-by: Amr Elsayed Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Reviewed-by: Miikka Heikkinen --- .../effectMakerQmlSources/ValueImage.qml | 3 +-- .../effectmakernew/effectmakeruniformsmodel.cpp | 17 +++++++++++++++-- .../effectmakernew/effectmakerwidget.cpp | 7 +++++++ src/plugins/effectmakernew/uniform.cpp | 7 ++++--- src/plugins/effectmakernew/uniform.h | 2 -- .../propertyeditor/assetimageprovider.h | 3 ++- 6 files changed, 29 insertions(+), 10 deletions(-) diff --git a/share/qtcreator/qmldesigner/effectMakerQmlSources/ValueImage.qml b/share/qtcreator/qmldesigner/effectMakerQmlSources/ValueImage.qml index acf70f0a759..571fac50002 100644 --- a/share/qtcreator/qmldesigner/effectMakerQmlSources/ValueImage.qml +++ b/share/qtcreator/qmldesigner/effectMakerQmlSources/ValueImage.qml @@ -18,7 +18,6 @@ Row { actionIndicatorVisible: false - //TODO: Disable until we figure out how to use images from outside qds - //onAbsoluteFilePathChanged: uniformValue = absoluteFilePath + onAbsoluteFilePathChanged: uniformValue = absoluteFilePath } } diff --git a/src/plugins/effectmakernew/effectmakeruniformsmodel.cpp b/src/plugins/effectmakernew/effectmakeruniformsmodel.cpp index d8aa312df5a..cfbbf3f5778 100644 --- a/src/plugins/effectmakernew/effectmakeruniformsmodel.cpp +++ b/src/plugins/effectmakernew/effectmakeruniformsmodel.cpp @@ -50,8 +50,21 @@ bool EffectMakerUniformsModel::setData(const QModelIndex &index, const QVariant return false; auto uniform = m_uniforms.at(index.row()); - uniform->setValue(value); - g_propertyData.insert(uniform->name(), value); + + if (uniform->type() == Uniform::Type::Sampler) { + QString updatedValue = value.toString(); + int idx = value.toString().indexOf("file:"); + + QString path = idx > 0 ? updatedValue.right(updatedValue.size() - idx - 5) : updatedValue; + updatedValue = QUrl::fromLocalFile(path).toString(); + + uniform->setValue(updatedValue); + g_propertyData.insert(uniform->name(), updatedValue); + } else { + uniform->setValue(value); + g_propertyData.insert(uniform->name(), value); + } + emit dataChanged(index, index, {role}); return true; diff --git a/src/plugins/effectmakernew/effectmakerwidget.cpp b/src/plugins/effectmakernew/effectmakerwidget.cpp index 0199d2eca2a..c26247fef19 100644 --- a/src/plugins/effectmakernew/effectmakerwidget.cpp +++ b/src/plugins/effectmakernew/effectmakerwidget.cpp @@ -14,6 +14,8 @@ #include "qqmlcontext.h" #include "theme.h" +#include "qmldesigner/components/propertyeditor/assetimageprovider.h" + #include #include @@ -136,6 +138,11 @@ void EffectMakerWidget::initView() m_backendModelNode.setup(m_effectMakerView->rootModelNode()); m_quickWidget->rootContext()->setContextProperty("anchorBackend", &m_backendAnchorBinding); m_quickWidget->rootContext()->setContextProperty("modelNodeBackend", &m_backendModelNode); + m_quickWidget->rootContext()->setContextProperty("activeDragSuffix", ""); + + m_quickWidget->engine()->addImageProvider("qmldesigner_thumbnails", + new QmlDesigner::AssetImageProvider( + QmlDesigner::QmlDesignerPlugin::imageCache())); // init the first load of the QML UI elements reloadQmlSource(); diff --git a/src/plugins/effectmakernew/uniform.cpp b/src/plugins/effectmakernew/uniform.cpp index a9729b574ef..c7e460e460e 100644 --- a/src/plugins/effectmakernew/uniform.cpp +++ b/src/plugins/effectmakernew/uniform.cpp @@ -12,8 +12,8 @@ namespace EffectMaker { -Uniform::Uniform(const QJsonObject &propObj, const QString &qenPath) : - m_qenPath(qenPath) +Uniform::Uniform(const QJsonObject &propObj, const QString &qenPath) + : m_qenPath(qenPath) { QString value, defaultValue, minValue, maxValue; @@ -35,9 +35,10 @@ Uniform::Uniform(const QJsonObject &propObj, const QString &qenPath) : QString mipmapProperty = mipmapPropertyName(m_name); g_propertyData[mipmapProperty] = m_enableMipmap; } + if (propObj.contains("value")) { value = propObj.value("value").toString(); - if (m_type == Type::Sampler && !value.isEmpty()) + if (m_type == Type::Sampler) value = getResourcePath(value); } else { // QEN files don't store the current value, so with those use default value diff --git a/src/plugins/effectmakernew/uniform.h b/src/plugins/effectmakernew/uniform.h index 5146de6b67c..8f14383bb4c 100644 --- a/src/plugins/effectmakernew/uniform.h +++ b/src/plugins/effectmakernew/uniform.h @@ -14,8 +14,6 @@ QT_FORWARD_DECLARE_CLASS(QVector2D) namespace EffectMaker { - - class Uniform : public QObject { Q_OBJECT diff --git a/src/plugins/qmldesigner/components/propertyeditor/assetimageprovider.h b/src/plugins/qmldesigner/components/propertyeditor/assetimageprovider.h index 450086d323d..b71543444a6 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/assetimageprovider.h +++ b/src/plugins/qmldesigner/components/propertyeditor/assetimageprovider.h @@ -4,12 +4,13 @@ #pragma once #include "imagecache/midsizeimagecacheprovider.h" +#include "qmldesigner_global.h" #include namespace QmlDesigner { -class AssetImageProvider : public QQuickAsyncImageProvider +class QMLDESIGNER_EXPORT AssetImageProvider : public QQuickAsyncImageProvider { public: AssetImageProvider(AsynchronousImageCache &imageCache, const QImage &defaultImage = {}) From 37892773282b6444ea653f853114fca6e2088da2 Mon Sep 17 00:00:00 2001 From: Amr Essam Date: Wed, 1 Nov 2023 17:58:57 +0200 Subject: [PATCH 144/242] QmlDesigner: Sync compilation with add/remove composition nodes Now when there are no effects, default state is restored Also some cleanups Task-number: QDS-11064 Change-Id: Iad25adf2c0f855cb709c494d9f3eb47ef6207d36 Reviewed-by: Mahmoud Badri Reviewed-by: --- src/plugins/effectmakernew/effectmakermodel.cpp | 3 +++ src/plugins/effectmakernew/effectmakermodel.h | 2 -- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/plugins/effectmakernew/effectmakermodel.cpp b/src/plugins/effectmakernew/effectmakermodel.cpp index de34128747b..65896f66af1 100644 --- a/src/plugins/effectmakernew/effectmakermodel.cpp +++ b/src/plugins/effectmakernew/effectmakermodel.cpp @@ -104,6 +104,9 @@ void EffectMakerModel::setIsEmpty(bool val) if (m_isEmpty != val) { m_isEmpty = val; emit isEmptyChanged(); + + if (m_isEmpty) + bakeShaders(); } } diff --git a/src/plugins/effectmakernew/effectmakermodel.h b/src/plugins/effectmakernew/effectmakermodel.h index 02d7c57085d..3be8701deae 100644 --- a/src/plugins/effectmakernew/effectmakermodel.h +++ b/src/plugins/effectmakernew/effectmakermodel.h @@ -163,8 +163,6 @@ private: // Temp files to store shaders sources and binary data QTemporaryFile m_fragmentSourceFile; QTemporaryFile m_vertexSourceFile; - QTemporaryFile m_fragmentShaderFile; - QTemporaryFile m_vertexShaderFile; QString m_fragmentSourceFilename; QString m_vertexSourceFilename; QString m_fragmentShaderFilename; From eabebac670c5285a2ea2ffecd9f57448062e533d Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Thu, 2 Nov 2023 12:43:59 +0200 Subject: [PATCH 145/242] EffectMaker: Enable the enable node feature Also corrected an include Change-Id: I2c1c2385f30edbd6e5f53e81fc4794a0cc3e56d9 Reviewed-by: Amr Elsayed Reviewed-by: Miikka Heikkinen --- .../EffectCompositionNode.qml | 15 ++++----------- src/plugins/effectmakernew/effectmakermodel.cpp | 5 +++-- 2 files changed, 7 insertions(+), 13 deletions(-) diff --git a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectCompositionNode.qml b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectCompositionNode.qml index 9b0046f67c3..56a508377ec 100644 --- a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectCompositionNode.qml +++ b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectCompositionNode.qml @@ -12,14 +12,7 @@ import EffectMakerBackend HelperWidgets.Section { id: root - // model properties - required property string nodeName - required property bool nodeEnabled - required property var nodeUniformsModel - - required property int index - - caption: root.nodeName + caption: nodeName category: "EffectMaker" draggable: true @@ -32,18 +25,18 @@ HelperWidgets.Section { } showEyeButton: true - eyeEnabled: root.nodeEnabled + eyeEnabled: nodeEnabled eyeButtonToolTip: qsTr("Enable/Disable Node") onEyeButtonClicked: { - root.nodeEnabled = root.eyeEnabled + nodeEnabled = root.eyeEnabled } Column { spacing: 10 Repeater { - model: root.nodeUniformsModel + model: nodeUniformsModel EffectCompositionNodeUniform { width: root.width diff --git a/src/plugins/effectmakernew/effectmakermodel.cpp b/src/plugins/effectmakernew/effectmakermodel.cpp index 65896f66af1..16f7effb7c1 100644 --- a/src/plugins/effectmakernew/effectmakermodel.cpp +++ b/src/plugins/effectmakernew/effectmakermodel.cpp @@ -13,7 +13,7 @@ #include #include -#include +#include #include #include @@ -93,6 +93,8 @@ bool EffectMakerModel::setData(const QModelIndex &index, const QVariant &value, if (role == EnabledRole) { m_nodes.at(index.row())->setIsEnabled(value.toBool()); + bakeShaders(); + emit dataChanged(index, index, {role}); } @@ -1060,4 +1062,3 @@ void EffectMakerModel::clearImageWatchers() } } // namespace EffectMaker - From 94e55865f783ce62dd239a16b5d03f451910a8ec Mon Sep 17 00:00:00 2001 From: Amr Essam Date: Thu, 2 Nov 2023 12:54:34 +0200 Subject: [PATCH 146/242] QmlDesigner: Fix cmake issues for effect maker Also some cleanups to cmake removing ShaderTools Change-Id: I9ae35a808943f2e4ad7f2b395854b1a245221246 Reviewed-by: Mahmoud Badri --- src/plugins/effectmakernew/CMakeLists.txt | 8 ++++---- src/plugins/effectmakernew/EffectMakerNew.json.in | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/plugins/effectmakernew/CMakeLists.txt b/src/plugins/effectmakernew/CMakeLists.txt index 10cff8f2737..81496c7eb78 100644 --- a/src/plugins/effectmakernew/CMakeLists.txt +++ b/src/plugins/effectmakernew/CMakeLists.txt @@ -1,12 +1,12 @@ -find_package(Qt6 OPTIONAL_COMPONENTS Gui Quick ShaderTools) +find_package(Qt6 OPTIONAL_COMPONENTS Gui Quick) add_qtc_plugin(EffectMakerNew - CONDITION TARGET QmlDesigner AND TARGET Qt::ShaderTools + CONDITION TARGET QmlDesigner PLUGIN_DEPENDS - QtCreator::Core QtCreator::QmlDesigner QtCreator::ProjectExplorer QtCreator::QmlProjectManager + Qt::Core QtCreator::QmlDesigner QtCreator::ProjectExplorer QtCreator::QmlProjectManager DEPENDS Qt::Core - QtCreator::Utils Qt::CorePrivate Qt::Widgets Qt::Qml Qt::QmlPrivate Qt::Quick Qt::ShaderTools Qt::ShaderToolsPrivate + QtCreator::Utils Qt::CorePrivate Qt::Widgets Qt::Qml Qt::QmlPrivate Qt::Quick SOURCES effectmakerplugin.cpp effectmakerplugin.h effectmakerwidget.cpp effectmakerwidget.h diff --git a/src/plugins/effectmakernew/EffectMakerNew.json.in b/src/plugins/effectmakernew/EffectMakerNew.json.in index 46c5e12247f..7846ea6dc98 100644 --- a/src/plugins/effectmakernew/EffectMakerNew.json.in +++ b/src/plugins/effectmakernew/EffectMakerNew.json.in @@ -1,8 +1,7 @@ { \"Name\" : \"EffectMakerNew\", - \"Version\" : \"$$QTCREATOR_VERSION\", - \"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\", - \"Revision\" : \"$$QTC_PLUGIN_REVISION\", + \"Version\" : \"${IDE_VERSION}\", + \"CompatVersion\" : \"${IDE_VERSION_COMPAT}\", \"Vendor\" : \"The Qt Company Ltd\", \"Copyright\" : \"(C) $$QTCREATOR_COPYRIGHT_YEAR The Qt Company Ltd\", \"License\" : [ \"Commercial Usage\", @@ -11,5 +10,6 @@ ], \"Description\" : \"Plugin for Effect Maker.\", \"Url\" : \"http://www.qt.io\", - $$dependencyList + ${IDE_PLUGIN_DEPENDENCIES} } + From fc1c720aece967a4e563b6385e71b12057797a01 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Wed, 1 Nov 2023 14:39:28 +0100 Subject: [PATCH 147/242] QmlDesigner: Move the functions to handle assets drops Moving those functions to ModelNodeOperations allows reuse in other views like e.g. the TextEditor. Change-Id: I7eee1c6080b4208ffaab6637f0debf78ec648c8e Reviewed-by: Miikka Heikkinen --- .../componentcore/modelnodeoperations.cpp | 429 +++++++++++++++++- .../componentcore/modelnodeoperations.h | 24 + .../navigator/navigatortreemodel.cpp | 365 ++------------- .../components/navigator/navigatortreemodel.h | 16 +- 4 files changed, 474 insertions(+), 360 deletions(-) diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp index 12bb4d54604..1ed1c09b0c1 100644 --- a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp +++ b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp @@ -12,20 +12,22 @@ #include "addsignalhandlerdialog.h" #include -#include -#include -#include -#include -#include -#include -#include -#include +#include #include -#include -#include -#include -#include +#include +#include +#include +#include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include #include @@ -1739,6 +1741,409 @@ void jumpToCodeOperation(const SelectionContext &selectionState) jumpToCode(selectionState.currentSingleSelectedNode()); } +static bool moveNodeToParent(const NodeAbstractProperty &targetProperty, const ModelNode &node) +{ + NodeAbstractProperty parentProp = targetProperty.parentProperty(); + if (parentProp.isValid()) { + ModelNode targetModel = parentProp.parentModelNode(); + parentProp.reparentHere(node); + return true; + } + return false; +} + +ModelNode createTextureNode(const NodeAbstractProperty &targetProp, const QString &imagePath) +{ + AbstractView *view = targetProp.view(); + QTC_ASSERT(view, return {}); + + if (targetProp.isValid()) { + // create a texture item lib + ItemLibraryEntry itemLibraryEntry; + itemLibraryEntry.setName("Texture"); + itemLibraryEntry.setType("QtQuick3D.Texture", 1, 0); + + // set texture source + PropertyName prop = "source"; + QString type = "QUrl"; + QVariant val = imagePath; + itemLibraryEntry.addProperty(prop, type, val); + + // create a texture + ModelNode newModelNode = QmlItemNode::createQmlObjectNode(view, + itemLibraryEntry, + {}, + targetProp, + false); + + // Rename the node based on source image + QFileInfo fi(imagePath); + newModelNode.setIdWithoutRefactoring( + view->model()->generateNewId(fi.baseName(), "textureImage")); + return newModelNode; + } + return {}; +} + +bool dropAsImage3dTexture(const ModelNode &targetNode, + const NodeAbstractProperty &targetProp, + const QString &imagePath, + ModelNode &newNode, + bool &outMoveNodesAfter) +{ + AbstractView *view = targetNode.view(); + QTC_ASSERT(view, return {}); + + auto bindToProperty = [&](const PropertyName &propName, bool sibling) { + view->executeInTransaction("NavigatorTreeModel::dropAsImage3dTexture", [&] { + newNode = createTextureNode(targetProp, imagePath); + if (newNode.isValid()) { + targetNode.bindingProperty(propName).setExpression(newNode.validId()); + + // If dropping an image on e.g. TextureInput, create a texture on the same level as + // target, as the target doesn't support Texture children (QTBUG-86219) + if (sibling) + outMoveNodesAfter = !moveNodeToParent(targetProp, newNode); + } + }); + }; + + if (targetNode.metaInfo().isQtQuick3DDefaultMaterial() + || targetNode.metaInfo().isQtQuick3DPrincipledMaterial() + || targetNode.metaInfo().isQtQuick3DSpecularGlossyMaterial()) { + // if dropping an image on a material, create a texture instead of image + // Show texture property selection dialog + auto dialog = ChooseFromPropertyListDialog::createIfNeeded(targetNode, + view->model()->metaInfo( + "QtQuick3D.Texture"), + Core::ICore::dialogParent()); + if (!dialog) + return false; + + dialog->exec(); + + if (dialog->result() == QDialog::Accepted) { + view->executeInTransaction("NavigatorTreeModel::dropAsImage3dTexture", [&] { + newNode = createTextureNode(targetProp, imagePath); + if (newNode.isValid()) // Automatically set the texture to selected property + targetNode.bindingProperty(dialog->selectedProperty()) + .setExpression(newNode.validId()); + }); + } + + delete dialog; + return true; + } else if (targetNode.metaInfo().isQtQuick3DTextureInput()) { + bindToProperty("texture", true); + return newNode.isValid(); + } else if (targetNode.metaInfo().isQtQuick3DParticles3DSpriteParticle3D()) { + bindToProperty("sprite", false); + return newNode.isValid(); + } else if (targetNode.metaInfo().isQtQuick3DSceneEnvironment()) { + bindToProperty("lightProbe", false); + return newNode.isValid(); + } else if (targetNode.metaInfo().isQtQuick3DTexture()) { + // if dropping an image on an existing texture, set the source + targetNode.variantProperty("source").setValue(imagePath); + return true; + } else if (targetNode.metaInfo().isQtQuick3DModel()) { + QTimer::singleShot(0, view, [targetNode, imagePath, view]() { + if (view && targetNode.isValid()) { + // To MaterialBrowserView. Done async to avoid custom notification in transaction + view->emitCustomNotification("apply_asset_to_model3D", + {targetNode}, + {DocumentManager::currentFilePath() + .absolutePath() + .pathAppended(imagePath) + .cleanPath() + .toString()}); + } + }); + return true; + } + + return false; +} + +ModelNode handleItemLibraryEffectDrop(const QString &effectPath, const ModelNode &targetNode) +{ + AbstractView *view = targetNode.view(); + QTC_ASSERT(view, return {}); + + ModelNode newModelNode; + + if ((targetNode.hasParentProperty() && targetNode.parentProperty().name() == "layer.effect") + || !targetNode.metaInfo().isQtQuickItem()) { + return newModelNode; + } + + if (ModelNodeOperations::validateEffect(effectPath)) { + bool layerEffect = ModelNodeOperations::useLayerEffect(); + newModelNode = QmlItemNode::createQmlItemNodeForEffect(view, + targetNode, + effectPath, + layerEffect); + } + + return newModelNode; +} + +void handleTextureDrop(const QMimeData *mimeData, const ModelNode &targetModelNode) +{ + AbstractView *view = targetModelNode.view(); + QTC_ASSERT(view, return ); + + QmlObjectNode targetNode(targetModelNode); + + if (!targetNode.isValid()) + return; + + qint32 internalId = mimeData->data(Constants::MIME_TYPE_TEXTURE).toInt(); + ModelNode texNode = view->modelNodeForInternalId(internalId); + QTC_ASSERT(texNode.isValid(), return ); + + if (targetNode.modelNode().metaInfo().isQtQuick3DModel()) { + view->emitCustomNotification("apply_texture_to_model3D", {targetNode, texNode}); + } else { + auto *dialog = ChooseFromPropertyListDialog::createIfNeeded(targetNode, + texNode, + Core::ICore::dialogParent()); + if (dialog) { + bool soloProperty = dialog->isSoloProperty(); + if (!soloProperty) + dialog->exec(); + + if (soloProperty || dialog->result() == QDialog::Accepted) + targetNode.setBindingProperty(dialog->selectedProperty(), texNode.id()); + + delete dialog; + } + } +} + +void handleMaterialDrop(const QMimeData *mimeData, const ModelNode &targetNode) +{ + AbstractView *view = targetNode.view(); + QTC_ASSERT(view, return ); + + if (!targetNode.metaInfo().isQtQuick3DModel()) + return; + + qint32 internalId = mimeData->data(Constants::MIME_TYPE_MATERIAL).toInt(); + ModelNode matNode = view->modelNodeForInternalId(internalId); + + view->executeInTransaction(__FUNCTION__, [&] { + MaterialUtils::assignMaterialTo3dModel(view, targetNode, matNode); + }); +} + +ModelNode handleItemLibraryImageDrop(const QString &imagePath, + NodeAbstractProperty targetProperty, + const ModelNode &targetNode, + bool &outMoveNodesAfter) +{ + AbstractView *view = targetNode.view(); + QTC_ASSERT(view, return {}); + + const QString imagePathRelative + = DocumentManager::currentFilePath().toFileInfo().dir().relativeFilePath( + imagePath); // relative to .ui.qml file + + ModelNode newModelNode; + + if (!dropAsImage3dTexture(targetNode, + targetProperty, + imagePathRelative, + newModelNode, + outMoveNodesAfter)) { + if (targetNode.metaInfo().isQtQuickImage() || targetNode.metaInfo().isQtQuickBorderImage()) { + // if dropping an image on an existing image, set the source + targetNode.variantProperty("source").setValue(imagePathRelative); + } else { + // create an image + QmlItemNode newItemNode = QmlItemNode::createQmlItemNodeFromImage(view, + imagePath, + QPointF(), + targetProperty, + false); + if (NodeHints::fromModelNode(targetProperty.parentModelNode()) + .canBeContainerFor(newItemNode.modelNode())) { + newModelNode = newItemNode.modelNode(); + } else { + newItemNode.destroy(); + } + } + } + + return newModelNode; +} + +ModelNode handleItemLibraryFontDrop(const QString &fontFamily, + NodeAbstractProperty targetProperty, + const ModelNode &targetNode) +{ + AbstractView *view = targetNode.view(); + QTC_ASSERT(view, return {}); + + ModelNode newModelNode; + + if (targetNode.metaInfo().isQtQuickText()) { + // if dropping into an existing Text, update font + targetNode.variantProperty("font.family").setValue(fontFamily); + } else { + // create a Text node + QmlItemNode newItemNode = QmlItemNode::createQmlItemNodeFromFont(view, + fontFamily, + QPointF(), + targetProperty, + false); + if (NodeHints::fromModelNode(targetProperty.parentModelNode()) + .canBeContainerFor(newItemNode.modelNode())) { + newModelNode = newItemNode.modelNode(); + } else { + newItemNode.destroy(); + } + } + + return newModelNode; +} + +ModelNode handleItemLibraryShaderDrop(const QString &shaderPath, + bool isFragShader, + NodeAbstractProperty targetProperty, + const ModelNode &targetNode, + bool &outMoveNodesAfter) +{ + AbstractView *view = targetNode.view(); + QTC_ASSERT(view, return {}); + + ModelNode newModelNode; + + const QString relPath = DocumentManager::currentFilePath().toFileInfo().dir().relativeFilePath( + shaderPath); + + if (targetNode.metaInfo().isQtQuick3DShader()) { + // if dropping into an existing Shader, update + targetNode.variantProperty("stage").setEnumeration(isFragShader ? "Shader.Fragment" + : "Shader.Vertex"); + targetNode.variantProperty("shader").setValue(relPath); + } else { + view->executeInTransaction("NavigatorTreeModel::handleItemLibraryShaderDrop", [&] { + // create a new Shader + ItemLibraryEntry itemLibraryEntry; + itemLibraryEntry.setName("Shader"); + itemLibraryEntry.setType("QtQuick3D.Shader", 1, 0); + + // set shader properties + PropertyName prop = "shader"; + QString type = "QUrl"; + QVariant val = relPath; + itemLibraryEntry.addProperty(prop, type, val); + prop = "stage"; + type = "enum"; + val = isFragShader ? "Shader.Fragment" : "Shader.Vertex"; + itemLibraryEntry.addProperty(prop, type, val); + + // create a texture + newModelNode = QmlItemNode::createQmlObjectNode(view, + itemLibraryEntry, + {}, + targetProperty, + false); + + // Rename the node based on shader source + QFileInfo fi(relPath); + newModelNode.setIdWithoutRefactoring( + view->model()->generateNewId(fi.baseName(), "shader")); + // Passes can't have children, so move shader node under parent + if (targetProperty.parentModelNode().metaInfo().isQtQuick3DPass()) { + BindingProperty listProp = targetNode.bindingProperty("shaders"); + listProp.addModelNodeToArray(newModelNode); + outMoveNodesAfter = !moveNodeToParent(targetProperty, newModelNode); + } + }); + } + + return newModelNode; +} + +ModelNode handleItemLibrarySoundDrop(const QString &soundPath, + NodeAbstractProperty targetProperty, + const ModelNode &targetNode) +{ + AbstractView *view = targetNode.view(); + QTC_ASSERT(view, return {}); + + ModelNode newModelNode; + + const QString relPath = DocumentManager::currentFilePath().toFileInfo().dir().relativeFilePath( + soundPath); + + if (targetNode.metaInfo().isQtMultimediaSoundEffect()) { + // if dropping into on an existing SoundEffect, update + targetNode.variantProperty("source").setValue(relPath); + } else { + // create a new SoundEffect + ItemLibraryEntry itemLibraryEntry; + itemLibraryEntry.setName("SoundEffect"); + itemLibraryEntry.setType("QtMultimedia.SoundEffect", 1, 0); + + // set source property + PropertyName prop = "source"; + QString type = "QUrl"; + QVariant val = relPath; + itemLibraryEntry.addProperty(prop, type, val); + + // create a texture + newModelNode = QmlItemNode::createQmlObjectNode(view, + itemLibraryEntry, + {}, + targetProperty, + false); + + // Rename the node based on source + QFileInfo fi(relPath); + newModelNode.setIdWithoutRefactoring( + view->model()->generateNewId(fi.baseName(), "soundEffect")); + } + + return newModelNode; +} + +ModelNode handleItemLibraryTexture3dDrop(const QString &tex3DPath, + NodeAbstractProperty targetProperty, + const ModelNode &targetNode, + bool &outMoveNodesAfter) +{ + AbstractView *view = targetNode.view(); + QTC_ASSERT(view, return {}); + + Import import = Import::createLibraryImport(QStringLiteral("QtQuick3D")); + if (!view->model()->hasImport(import, true, true)) + return {}; + + const QString imagePath = DocumentManager::currentFilePath().toFileInfo().dir().relativeFilePath( + tex3DPath); // relative to qml file + + ModelNode newModelNode; + + if (!dropAsImage3dTexture(targetNode, + targetProperty, + imagePath, + newModelNode, + outMoveNodesAfter)) { + view->executeInTransaction("NavigatorTreeModel::handleItemLibraryTexture3dDrop", [&] { + // create a standalone Texture3D at drop location + newModelNode = createTextureNode(targetProperty, imagePath); + if (!NodeHints::fromModelNode(targetProperty.parentModelNode()) + .canBeContainerFor(newModelNode)) + newModelNode.destroy(); + }); + } + + return newModelNode; +} + } // namespace ModelNodeOperations } //QmlDesigner diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.h b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.h index d30a69b6049..c73530e02dd 100644 --- a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.h +++ b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.h @@ -133,6 +133,30 @@ bool validateEffect(const QString &effectPath); Utils::FilePath getImagesDefaultDirectory(); +//Item Library and Assets related drop operations +ModelNode handleItemLibraryEffectDrop(const QString &effectPath, const ModelNode &targetNode); +void handleTextureDrop(const QMimeData *mimeData, const ModelNode &targetModelNode); +void handleMaterialDrop(const QMimeData *mimeData, const ModelNode &targetNode); +ModelNode handleItemLibraryImageDrop(const QString &imagePath, + NodeAbstractProperty targetProperty, + const ModelNode &targetNode, + bool &outMoveNodesAfter); +ModelNode handleItemLibraryFontDrop(const QString &fontFamily, + NodeAbstractProperty targetProperty, + const ModelNode &targetNode); +ModelNode handleItemLibraryShaderDrop(const QString &shaderPath, + bool isFragShader, + NodeAbstractProperty targetProperty, + const ModelNode &targetNode, + bool &outMoveNodesAfter); +ModelNode handleItemLibrarySoundDrop(const QString &soundPath, + NodeAbstractProperty targetProperty, + const ModelNode &targetNode); +ModelNode handleItemLibraryTexture3dDrop(const QString &tex3DPath, + NodeAbstractProperty targetProperty, + const ModelNode &targetNode, + bool &outMoveNodesAfter); + // ModelNodePreviewImageOperations QVariant previewImageDataForGenericNode(const ModelNode &modelNode); QVariant previewImageDataForImageNode(const ModelNode &modelNode); diff --git a/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp b/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp index 33ed88ed722..4f6d6f6068a 100644 --- a/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp +++ b/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp @@ -554,9 +554,13 @@ bool NavigatorTreeModel::dropMimeData(const QMimeData *mimeData, if (mimeData->hasFormat(Constants::MIME_TYPE_ITEM_LIBRARY_INFO)) { handleItemLibraryItemDrop(mimeData, rowNumber, dropModelIndex); } else if (mimeData->hasFormat(Constants::MIME_TYPE_TEXTURE)) { - handleTextureDrop(mimeData, dropModelIndex); + const QModelIndex rowModelIndex = dropModelIndex.sibling(dropModelIndex.row(), 0); + ModelNode targetNode = modelNodeForIndex(rowModelIndex); + ModelNodeOperations::handleTextureDrop(mimeData, targetNode); } else if (mimeData->hasFormat(Constants::MIME_TYPE_MATERIAL)) { - handleMaterialDrop(mimeData, dropModelIndex); + const QModelIndex rowModelIndex = dropModelIndex.sibling(dropModelIndex.row(), 0); + ModelNode targetNode = modelNodeForIndex(rowModelIndex); + ModelNodeOperations::handleMaterialDrop(mimeData, targetNode); } else if (mimeData->hasFormat(Constants::MIME_TYPE_BUNDLE_TEXTURE)) { QByteArray filePath = mimeData->data(Constants::MIME_TYPE_BUNDLE_TEXTURE); ModelNode targetNode(modelNodeForIndex(dropModelIndex)); @@ -607,23 +611,36 @@ bool NavigatorTreeModel::dropMimeData(const QMimeData *mimeData, QString assetType = assetTypeAndData.first; QString assetData = QString::fromUtf8(assetTypeAndData.second); if (assetType == Constants::MIME_TYPE_ASSET_IMAGE) { - currNode = handleItemLibraryImageDrop(assetPath, targetProperty, - rowModelIndex, moveNodesAfter); + currNode + = ModelNodeOperations::handleItemLibraryImageDrop(assetPath, + targetProperty, + modelNodeForIndex( + rowModelIndex), + moveNodesAfter); } else if (assetType == Constants::MIME_TYPE_ASSET_FONT) { - currNode = handleItemLibraryFontDrop(assetData, // assetData is fontFamily - targetProperty, rowModelIndex); + currNode = ModelNodeOperations::handleItemLibraryFontDrop( + assetData, // assetData is fontFamily + targetProperty, + modelNodeForIndex(rowModelIndex)); } else if (assetType == Constants::MIME_TYPE_ASSET_SHADER) { - currNode = handleItemLibraryShaderDrop(assetPath, assetData == "f", - targetProperty, rowModelIndex, - moveNodesAfter); + currNode = ModelNodeOperations::handleItemLibraryShaderDrop( + assetPath, + assetData == "f", + targetProperty, + modelNodeForIndex(rowModelIndex), + moveNodesAfter); } else if (assetType == Constants::MIME_TYPE_ASSET_SOUND) { - currNode = handleItemLibrarySoundDrop(assetPath, targetProperty, - rowModelIndex); + currNode = ModelNodeOperations::handleItemLibrarySoundDrop( + assetPath, targetProperty, modelNodeForIndex(rowModelIndex)); } else if (assetType == Constants::MIME_TYPE_ASSET_TEXTURE3D) { - currNode = handleItemLibraryTexture3dDrop(assetPath, targetProperty, - rowModelIndex, moveNodesAfter); + currNode = ModelNodeOperations::handleItemLibraryTexture3dDrop( + assetPath, + targetProperty, + modelNodeForIndex(rowModelIndex), + moveNodesAfter); } else if (assetType == Constants::MIME_TYPE_ASSET_EFFECT) { - currNode = handleItemLibraryEffectDrop(assetPath, rowModelIndex); + currNode = ModelNodeOperations::handleItemLibraryEffectDrop( + assetPath, modelNodeForIndex(rowModelIndex)); moveNodesAfter = false; } @@ -785,109 +802,6 @@ void NavigatorTreeModel::handleItemLibraryItemDrop(const QMimeData *mimeData, in } } -void NavigatorTreeModel::handleTextureDrop(const QMimeData *mimeData, const QModelIndex &dropModelIndex) -{ - QTC_ASSERT(m_view, return); - - const QModelIndex rowModelIndex = dropModelIndex.sibling(dropModelIndex.row(), 0); - QmlObjectNode targetNode = modelNodeForIndex(rowModelIndex); - if (!targetNode.isValid()) - return; - - qint32 internalId = mimeData->data(Constants::MIME_TYPE_TEXTURE).toInt(); - ModelNode texNode = m_view->modelNodeForInternalId(internalId); - QTC_ASSERT(texNode.isValid(), return); - - if (targetNode.modelNode().metaInfo().isQtQuick3DModel()) { - m_view->emitCustomNotification("apply_texture_to_model3D", {targetNode, texNode}); - } else { - auto *dialog = ChooseFromPropertyListDialog::createIfNeeded(targetNode, texNode, Core::ICore::dialogParent()); - if (dialog) { - bool soloProperty = dialog->isSoloProperty(); - if (!soloProperty) - dialog->exec(); - - if (soloProperty || dialog->result() == QDialog::Accepted) - targetNode.setBindingProperty(dialog->selectedProperty(), texNode.id()); - - delete dialog; - } - } -} - -void NavigatorTreeModel::handleMaterialDrop(const QMimeData *mimeData, const QModelIndex &dropModelIndex) -{ - QTC_ASSERT(m_view, return); - - const QModelIndex rowModelIndex = dropModelIndex.sibling(dropModelIndex.row(), 0); - ModelNode targetNode = modelNodeForIndex(rowModelIndex); - if (!targetNode.metaInfo().isQtQuick3DModel()) - return; - - qint32 internalId = mimeData->data(Constants::MIME_TYPE_MATERIAL).toInt(); - ModelNode matNode = m_view->modelNodeForInternalId(internalId); - - m_view->executeInTransaction(__FUNCTION__, [&] { - MaterialUtils::assignMaterialTo3dModel(m_view, targetNode, matNode); - }); -} - -ModelNode NavigatorTreeModel::handleItemLibraryImageDrop(const QString &imagePath, - NodeAbstractProperty targetProperty, - const QModelIndex &rowModelIndex, - bool &outMoveNodesAfter) -{ - QTC_ASSERT(m_view, return {}); - - ModelNode targetNode(modelNodeForIndex(rowModelIndex)); - - const QString imagePathRelative = DocumentManager::currentFilePath().toFileInfo().dir().relativeFilePath(imagePath); // relative to .ui.qml file - - ModelNode newModelNode; - - if (!dropAsImage3dTexture(targetNode, targetProperty, imagePathRelative, newModelNode, outMoveNodesAfter)) { - if (targetNode.metaInfo().isQtQuickImage() || targetNode.metaInfo().isQtQuickBorderImage()) { - // if dropping an image on an existing image, set the source - targetNode.variantProperty("source").setValue(imagePathRelative); - } else { - // create an image - QmlItemNode newItemNode = QmlItemNode::createQmlItemNodeFromImage(m_view, imagePath, QPointF(), targetProperty, false); - if (NodeHints::fromModelNode(targetProperty.parentModelNode()).canBeContainerFor(newItemNode.modelNode())) - newModelNode = newItemNode.modelNode(); - else - newItemNode.destroy(); - } - } - - return newModelNode; -} - -ModelNode NavigatorTreeModel::handleItemLibraryFontDrop(const QString &fontFamily, - NodeAbstractProperty targetProperty, - const QModelIndex &rowModelIndex) -{ - QTC_ASSERT(m_view, return {}); - - ModelNode targetNode(modelNodeForIndex(rowModelIndex)); - - ModelNode newModelNode; - - if (targetNode.metaInfo().isQtQuickText()) { - // if dropping into an existing Text, update font - targetNode.variantProperty("font.family").setValue(fontFamily); - } else { - // create a Text node - QmlItemNode newItemNode = QmlItemNode::createQmlItemNodeFromFont( - m_view, fontFamily, QPointF(), targetProperty, false); - if (NodeHints::fromModelNode(targetProperty.parentModelNode()).canBeContainerFor(newItemNode.modelNode())) - newModelNode = newItemNode.modelNode(); - else - newItemNode.destroy(); - } - - return newModelNode; -} - void NavigatorTreeModel::addImport(const QString &importName) { if (!ModelUtils::addImportWithCheck(importName, m_view->model())) @@ -900,227 +814,12 @@ bool QmlDesigner::NavigatorTreeModel::moveNodeToParent(const NodeAbstractPropert NodeAbstractProperty parentProp = targetProperty.parentProperty(); if (parentProp.isValid()) { ModelNode targetModel = parentProp.parentModelNode(); - int targetRowNumber = rowCount(indexForModelNode(targetModel)); - moveNodesInteractive(parentProp, {node}, targetRowNumber, false); + parentProp.reparentHere(node); return true; } return false; } -ModelNode NavigatorTreeModel::handleItemLibraryShaderDrop(const QString &shaderPath, bool isFragShader, - NodeAbstractProperty targetProperty, - const QModelIndex &rowModelIndex, - bool &outMoveNodesAfter) -{ - QTC_ASSERT(m_view, return {}); - - ModelNode targetNode(modelNodeForIndex(rowModelIndex)); - ModelNode newModelNode; - - const QString relPath = DocumentManager::currentFilePath().toFileInfo().dir().relativeFilePath(shaderPath); - - if (targetNode.metaInfo().isQtQuick3DShader()) { - // if dropping into an existing Shader, update - targetNode.variantProperty("stage").setEnumeration(isFragShader ? "Shader.Fragment" - : "Shader.Vertex"); - targetNode.variantProperty("shader").setValue(relPath); - } else { - m_view->executeInTransaction("NavigatorTreeModel::handleItemLibraryShaderDrop", [&] { - // create a new Shader - ItemLibraryEntry itemLibraryEntry; - itemLibraryEntry.setName("Shader"); - itemLibraryEntry.setType("QtQuick3D.Shader", 1, 0); - - // set shader properties - PropertyName prop = "shader"; - QString type = "QUrl"; - QVariant val = relPath; - itemLibraryEntry.addProperty(prop, type, val); - prop = "stage"; - type = "enum"; - val = isFragShader ? "Shader.Fragment" : "Shader.Vertex"; - itemLibraryEntry.addProperty(prop, type, val); - - // create a texture - newModelNode = QmlItemNode::createQmlObjectNode(m_view, itemLibraryEntry, {}, - targetProperty, false); - - // Rename the node based on shader source - QFileInfo fi(relPath); - newModelNode.setIdWithoutRefactoring( - m_view->model()->generateNewId(fi.baseName(), "shader")); - // Passes can't have children, so move shader node under parent - if (targetProperty.parentModelNode().metaInfo().isQtQuick3DPass()) { - BindingProperty listProp = targetNode.bindingProperty("shaders"); - listProp.addModelNodeToArray(newModelNode); - outMoveNodesAfter = !moveNodeToParent(targetProperty, newModelNode); - } - }); - } - - return newModelNode; -} - -ModelNode NavigatorTreeModel::handleItemLibrarySoundDrop(const QString &soundPath, - NodeAbstractProperty targetProperty, - const QModelIndex &rowModelIndex) -{ - QTC_ASSERT(m_view, return {}); - - ModelNode targetNode(modelNodeForIndex(rowModelIndex)); - ModelNode newModelNode; - - const QString relPath = DocumentManager::currentFilePath().toFileInfo().dir().relativeFilePath(soundPath); - - if (targetNode.metaInfo().isQtMultimediaSoundEffect()) { - // if dropping into on an existing SoundEffect, update - targetNode.variantProperty("source").setValue(relPath); - } else { - // create a new SoundEffect - ItemLibraryEntry itemLibraryEntry; - itemLibraryEntry.setName("SoundEffect"); - itemLibraryEntry.setType("QtMultimedia.SoundEffect", 1, 0); - - // set source property - PropertyName prop = "source"; - QString type = "QUrl"; - QVariant val = relPath; - itemLibraryEntry.addProperty(prop, type, val); - - // create a texture - newModelNode = QmlItemNode::createQmlObjectNode(m_view, itemLibraryEntry, {}, - targetProperty, false); - - // Rename the node based on source - QFileInfo fi(relPath); - newModelNode.setIdWithoutRefactoring( - m_view->model()->generateNewId(fi.baseName(), "soundEffect")); - } - - return newModelNode; -} - -ModelNode NavigatorTreeModel::handleItemLibraryTexture3dDrop(const QString &tex3DPath, - NodeAbstractProperty targetProperty, - const QModelIndex &rowModelIndex, - bool &outMoveNodesAfter) -{ - QTC_ASSERT(m_view, return {}); - - Import import = Import::createLibraryImport(QStringLiteral("QtQuick3D")); - if (!m_view->model()->hasImport(import, true, true)) - return {}; - - ModelNode targetNode(modelNodeForIndex(rowModelIndex)); - - const QString imagePath = DocumentManager::currentFilePath().toFileInfo().dir() - .relativeFilePath(tex3DPath); // relative to qml file - - ModelNode newModelNode; - - if (!dropAsImage3dTexture(targetNode, targetProperty, imagePath, newModelNode, outMoveNodesAfter)) { - m_view->executeInTransaction("NavigatorTreeModel::handleItemLibraryTexture3dDrop", [&] { - // create a standalone Texture3D at drop location - newModelNode = createTextureNode(targetProperty, imagePath); - if (!NodeHints::fromModelNode(targetProperty.parentModelNode()).canBeContainerFor(newModelNode)) - newModelNode.destroy(); - }); - } - - return newModelNode; -} - -ModelNode NavigatorTreeModel::handleItemLibraryEffectDrop(const QString &effectPath, const QModelIndex &rowModelIndex) -{ - QTC_ASSERT(m_view, return {}); - - ModelNode targetNode(modelNodeForIndex(rowModelIndex)); - ModelNode newModelNode; - - if ((targetNode.hasParentProperty() && targetNode.parentProperty().name() == "layer.effect") - || !targetNode.metaInfo().isQtQuickItem()) - return newModelNode; - - if (ModelNodeOperations::validateEffect(effectPath)) { - bool layerEffect = ModelNodeOperations::useLayerEffect(); - newModelNode = QmlItemNode::createQmlItemNodeForEffect(m_view, targetNode, effectPath, layerEffect); - } - - return newModelNode; -} - -bool NavigatorTreeModel::dropAsImage3dTexture(const ModelNode &targetNode, - const NodeAbstractProperty &targetProp, - const QString &imagePath, - ModelNode &newNode, - bool &outMoveNodesAfter) -{ - auto bindToProperty = [&](const PropertyName &propName, bool sibling) { - m_view->executeInTransaction("NavigatorTreeModel::dropAsImage3dTexture", [&] { - newNode = createTextureNode(targetProp, imagePath); - if (newNode.isValid()) { - targetNode.bindingProperty(propName).setExpression(newNode.validId()); - - // If dropping an image on e.g. TextureInput, create a texture on the same level as - // target, as the target doesn't support Texture children (QTBUG-86219) - if (sibling) - outMoveNodesAfter = !moveNodeToParent(targetProp, newNode); - } - }); - }; - - if (targetNode.metaInfo().isQtQuick3DDefaultMaterial() - || targetNode.metaInfo().isQtQuick3DPrincipledMaterial() - || targetNode.metaInfo().isQtQuick3DSpecularGlossyMaterial()) { - // if dropping an image on a material, create a texture instead of image - // Show texture property selection dialog - auto dialog = ChooseFromPropertyListDialog::createIfNeeded(targetNode, - m_view->model()->metaInfo( - "QtQuick3D.Texture"), - Core::ICore::dialogParent()); - if (!dialog) - return false; - - dialog->exec(); - - if (dialog->result() == QDialog::Accepted) { - m_view->executeInTransaction("NavigatorTreeModel::dropAsImage3dTexture", [&] { - newNode = createTextureNode(targetProp, imagePath); - if (newNode.isValid()) // Automatically set the texture to selected property - targetNode.bindingProperty(dialog->selectedProperty()).setExpression(newNode.validId()); - }); - } - - delete dialog; - return true; - } else if (targetNode.metaInfo().isQtQuick3DTextureInput()) { - bindToProperty("texture", true); - return newNode.isValid(); - } else if (targetNode.metaInfo().isQtQuick3DParticles3DSpriteParticle3D()) { - bindToProperty("sprite", false); - return newNode.isValid(); - } else if (targetNode.metaInfo().isQtQuick3DSceneEnvironment()) { - bindToProperty("lightProbe", false); - return newNode.isValid(); - } else if (targetNode.metaInfo().isQtQuick3DTexture()) { - // if dropping an image on an existing texture, set the source - targetNode.variantProperty("source").setValue(imagePath); - return true; - } else if (targetNode.metaInfo().isQtQuick3DModel()) { - QTimer::singleShot(0, this, [this, targetNode, imagePath]() { - if (m_view && targetNode.isValid()) { - // To MaterialBrowserView. Done async to avoid custom notification in transaction - m_view->emitCustomNotification( - "apply_asset_to_model3D", {targetNode}, - {DocumentManager::currentFilePath().absolutePath().pathAppended(imagePath).cleanPath().toString()}); - } - }); - return true; - } - - return false; -} - ModelNode NavigatorTreeModel::createTextureNode(const NodeAbstractProperty &targetProp, const QString &imagePath) { diff --git a/src/plugins/qmldesigner/components/navigator/navigatortreemodel.h b/src/plugins/qmldesigner/components/navigator/navigatortreemodel.h index ab45294b75f..25a4d9637e7 100644 --- a/src/plugins/qmldesigner/components/navigator/navigatortreemodel.h +++ b/src/plugins/qmldesigner/components/navigator/navigatortreemodel.h @@ -93,21 +93,7 @@ private: int targetIndex, bool executeInTransaction = true); void handleInternalDrop(const QMimeData *mimeData, int rowNumber, const QModelIndex &dropModelIndex); void handleItemLibraryItemDrop(const QMimeData *mimeData, int rowNumber, const QModelIndex &dropModelIndex); - void handleTextureDrop(const QMimeData *mimeData, const QModelIndex &dropModelIndex); - void handleMaterialDrop(const QMimeData *mimeData, const QModelIndex &dropModelIndex); - ModelNode handleItemLibraryImageDrop(const QString &imagePath, NodeAbstractProperty targetProperty, - const QModelIndex &rowModelIndex, bool &outMoveNodesAfter); - ModelNode handleItemLibraryFontDrop(const QString &fontFamily, NodeAbstractProperty targetProperty, - const QModelIndex &rowModelIndex); - ModelNode handleItemLibraryShaderDrop(const QString &shaderPath, bool isFragShader, - NodeAbstractProperty targetProperty, - const QModelIndex &rowModelIndex, - bool &outMoveNodesAfter); - ModelNode handleItemLibrarySoundDrop(const QString &soundPath, NodeAbstractProperty targetProperty, - const QModelIndex &rowModelIndex); - ModelNode handleItemLibraryTexture3dDrop(const QString &tex3DPath, NodeAbstractProperty targetProperty, - const QModelIndex &rowModelIndex, bool &outMoveNodesAfter); - ModelNode handleItemLibraryEffectDrop(const QString &effectPath, const QModelIndex &rowModelIndex); + bool dropAsImage3dTexture(const ModelNode &targetNode, const NodeAbstractProperty &targetProp, const QString &imagePath, ModelNode &newNode, bool &outMoveNodesAfter); ModelNode createTextureNode(const NodeAbstractProperty &targetProp, const QString &imagePath); From b44008f15a9d5f18f66598b552143c47604ece9c Mon Sep 17 00:00:00 2001 From: Pranta Dastider Date: Wed, 1 Nov 2023 17:12:48 +0100 Subject: [PATCH 148/242] QmlDesigner: Update the Animated State Transitions doc This patch updates the Animated State Transitions document. Removes an old image and adds a new image to support the current connection view update. Also, changes some texts to keep the process relevant. Fixes: QDS-11055 Change-Id: I4ea3face56df6cec05adfd8244d32dc20d7e6e16 Reviewed-by: Leena Miettinen --- .../examples/doc/StateTransitions.qdoc | 15 +++++++++------ .../doc/images/state-transition-connections.png | Bin 3493 -> 0 bytes .../images/state-transition-connections.webp | Bin 0 -> 28476 bytes 3 files changed, 9 insertions(+), 6 deletions(-) delete mode 100644 doc/qtdesignstudio/examples/doc/images/state-transition-connections.png create mode 100644 doc/qtdesignstudio/examples/doc/images/state-transition-connections.webp diff --git a/doc/qtdesignstudio/examples/doc/StateTransitions.qdoc b/doc/qtdesignstudio/examples/doc/StateTransitions.qdoc index ad8ae0005e0..e7d4f318a0b 100644 --- a/doc/qtdesignstudio/examples/doc/StateTransitions.qdoc +++ b/doc/qtdesignstudio/examples/doc/StateTransitions.qdoc @@ -123,16 +123,19 @@ \list 1 \li Go to the \uicontrol Connections view. \li In \uicontrol{Navigator}, select \e button_side and in - \uicontrol {Connections}, select \inlineimage icons/plus.png - . - This creates a new connection with \e button_side as the target. - \li Set \uicontrol{Signal Handler} to \uicontrol onClicked. - \li Set \uicontrol Actions to \e {Change state to side}. + \uicontrol {Connections}, select the \inlineimage icons/plus.png + button to open the connection setup options. + \li Set \uicontrol Signal to \c clicked, \uicontrol Action to + \c {Change State}, \uicontrol {State Group} to \c rectangle and + \uicontrol State to \c side in the respective + drop-down menus. + \li Select the \inlineimage icons/close.png + button to close the connection setup options. \li Repeat steps 2 to 4 for the next three buttons and set them to go to their corresponding states. \endlist - \image state-transition-connections.png + \image state-transition-connections.webp Now you can preview and try the transitions to see how the UI moves between the states when you select the buttons. diff --git a/doc/qtdesignstudio/examples/doc/images/state-transition-connections.png b/doc/qtdesignstudio/examples/doc/images/state-transition-connections.png deleted file mode 100644 index c4c429378d4ce5135605342711b2a99414773817..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3493 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 W9u{XXn=c?KHJnj^fsG*r#0LOl8k&3n literal 0 HcmV?d00001 From 4d55d6be189a70ab30d1a0089c2229992743520a Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Thu, 2 Nov 2023 13:38:32 +0100 Subject: [PATCH 149/242] QmlProject: Fix crash If no replacment kit is created we have to return. Task-number: QTCREATORBUG-29717 Change-Id: I7c91eeb5a3710640adfa0a561aaac4fefc8f1431 Reviewed-by: Tim Jenssen --- src/plugins/projectexplorer/project.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/plugins/projectexplorer/project.cpp b/src/plugins/projectexplorer/project.cpp index 20c65655627..03856f80369 100644 --- a/src/plugins/projectexplorer/project.cpp +++ b/src/plugins/projectexplorer/project.cpp @@ -848,6 +848,8 @@ void Project::createTargetFromMap(const Store &map, int index) "kit \"%2\" with id %3, which does not exist anymore. The new kit \"%4\" was " "created in its place, in an attempt not to lose custom project settings.") .arg(displayName(), formerKitName, id.toString(), k->displayName()))); + } else { + return; } auto t = std::make_unique(this, k, Target::_constructor_tag{}); From f466ac7ce2422274590c2463b807a54cac17d653 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Thu, 2 Nov 2023 15:09:00 +0100 Subject: [PATCH 150/242] QmlDesigner: Add real as number type Task-number: QDS-11098 Change-Id: I2825215f0ce9c57f2f88318bcdf1ecc11defa793 Reviewed-by: Knud Dollereder --- src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp b/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp index d4e102ef66e..a12aff15792 100644 --- a/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp +++ b/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp @@ -2602,7 +2602,8 @@ bool NodeMetaInfo::isNumber() const auto type = simplifiedTypeName(); - return type == "int" || type == "uint" || type == "float" || type == "double"; + return type == "int" || type == "uint" || type == "float" || type == "double" + || type == "real"; } } From 4d1a2100606d2fc345eee9903efd80f33a5bc0a9 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Thu, 2 Nov 2023 15:24:27 +0100 Subject: [PATCH 151/242] QmlDesigner: Use isInteger, isFloat in isNumber Change-Id: I20b81b54c0f7758e9e864ff50174ac2d7527fb38 Reviewed-by: Knud Dollereder Reviewed-by: Qt CI Patch Build Bot --- .../qmldesigner/designercore/metainfo/nodemetainfo.cpp | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp b/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp index a12aff15792..3fb5de210cc 100644 --- a/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp +++ b/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp @@ -2600,10 +2600,7 @@ bool NodeMetaInfo::isNumber() const return false; } - auto type = simplifiedTypeName(); - - return type == "int" || type == "uint" || type == "float" || type == "double" - || type == "real"; + return isFloat() || isInteger(); } } @@ -3079,7 +3076,7 @@ bool NodeMetaInfo::isInteger() const auto type = simplifiedTypeName(); - return type == "int" || type == "integer"; + return type == "int" || type == "integer" || type == "uint"; } } @@ -3101,7 +3098,7 @@ bool NodeMetaInfo::isFloat() const auto type = simplifiedTypeName(); - return type == "qreal" || type == "double" || type == "float"; + return type == "qreal" || type == "double" || type == "float" || type == "real"; } } From 0899c540be95972672ab83f4422b166b4725ffc2 Mon Sep 17 00:00:00 2001 From: Brook Cronin Date: Mon, 30 Oct 2023 16:22:57 +0100 Subject: [PATCH 152/242] QmlDesigner: Make kit settings button a toggle Task-number: QDS-10258 Change-Id: If680778b6cc1f5795887ac9da69f35ae9858308c Reviewed-by: Thomas Hartmann Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Aleksei German --- share/qtcreator/qmldesigner/statusbar/Main.qml | 7 +++++-- .../qmldesigner/components/toolbar/toolbarbackend.cpp | 9 +++++++++ .../qmldesigner/components/toolbar/toolbarbackend.h | 3 +++ 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/share/qtcreator/qmldesigner/statusbar/Main.qml b/share/qtcreator/qmldesigner/statusbar/Main.qml index 16b687abc00..9569aced92c 100644 --- a/share/qtcreator/qmldesigner/statusbar/Main.qml +++ b/share/qtcreator/qmldesigner/statusbar/Main.qml @@ -34,8 +34,11 @@ Item { id: settingButton style: StudioTheme.Values.statusbarButtonStyle buttonIcon: StudioTheme.Constants.settings_medium - onClicked: backend.triggerProjectSettings() - enabled: backend.isInDesignMode || (backend.isInEditMode && backend.projectOpened) + checkable: true + checkedInverted: true + checked: backend.isInSessionMode + onClicked: settingButton.checked ? backend.triggerProjectSettings() : backend.triggerModeChange() + enabled: backend.projectOpened tooltip: qsTr("Set runtime configuration for the project.") } diff --git a/src/plugins/qmldesigner/components/toolbar/toolbarbackend.cpp b/src/plugins/qmldesigner/components/toolbar/toolbarbackend.cpp index 4ef8cf311c4..98cd50150ee 100644 --- a/src/plugins/qmldesigner/components/toolbar/toolbarbackend.cpp +++ b/src/plugins/qmldesigner/components/toolbar/toolbarbackend.cpp @@ -343,6 +343,7 @@ ToolBarBackend::ToolBarBackend(QObject *parent) connect(Core::ModeManager::instance(), &Core::ModeManager::currentModeChanged, this, [this]() { emit isInDesignModeChanged(); emit isInEditModeChanged(); + emit isInSessionModeChanged(); emit isDesignModeEnabledChanged(); }); @@ -634,6 +635,14 @@ bool ToolBarBackend::isInEditMode() const return Core::ModeManager::currentModeId() == Core::Constants::MODE_EDIT; } +bool ToolBarBackend::isInSessionMode() const +{ + if (!Core::ModeManager::instance()) + return false; + + return Core::ModeManager::currentModeId() == ProjectExplorer::Constants::MODE_SESSION; +} + bool ToolBarBackend::isDesignModeEnabled() const { if (Core::DesignMode::instance()) diff --git a/src/plugins/qmldesigner/components/toolbar/toolbarbackend.h b/src/plugins/qmldesigner/components/toolbar/toolbarbackend.h index 66572515fcb..52aa69a4db8 100644 --- a/src/plugins/qmldesigner/components/toolbar/toolbarbackend.h +++ b/src/plugins/qmldesigner/components/toolbar/toolbarbackend.h @@ -87,6 +87,7 @@ class ToolBarBackend : public QObject Q_PROPERTY(QStringList styles READ styles CONSTANT) Q_PROPERTY(bool isInDesignMode READ isInDesignMode NOTIFY isInDesignModeChanged) Q_PROPERTY(bool isInEditMode READ isInEditMode NOTIFY isInEditModeChanged) + Q_PROPERTY(bool isInSessionMode READ isInSessionMode NOTIFY isInSessionModeChanged) Q_PROPERTY(bool isDesignModeEnabled READ isDesignModeEnabled NOTIFY isDesignModeEnabledChanged) Q_PROPERTY(int currentStyle READ currentStyle NOTIFY currentStyleChanged) Q_PROPERTY(QStringList kits READ kits NOTIFY kitsChanged) @@ -128,6 +129,7 @@ public: bool isInDesignMode() const; bool isInEditMode() const; + bool isInSessionMode() const; bool isDesignModeEnabled() const; int currentStyle() const; @@ -149,6 +151,7 @@ signals: void lockWorkspaceChanged(); void isInDesignModeChanged(); void isInEditModeChanged(); + void isInSessionModeChanged(); void isDesignModeEnabledChanged(); void currentStyleChanged(); void kitsChanged(); From 52e652c98654c37b15e49593cc9fa60418d01f7b Mon Sep 17 00:00:00 2001 From: Aleksei German Date: Thu, 2 Nov 2023 14:42:55 +0100 Subject: [PATCH 153/242] QmlDesigner: Extend ToolBar backend for MCUs Task-number: QDS-10338 Change-Id: I552cb25b9690a0c5dd202d99e8220453225bcd3b Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Reviewed-by: Thomas Hartmann --- .../componentcore/changestyleaction.cpp | 9 +++++++-- .../components/toolbar/toolbarbackend.cpp | 15 +++++++++++++++ .../components/toolbar/toolbarbackend.h | 3 +++ 3 files changed, 25 insertions(+), 2 deletions(-) diff --git a/src/plugins/qmldesigner/components/componentcore/changestyleaction.cpp b/src/plugins/qmldesigner/components/componentcore/changestyleaction.cpp index 4eb0ba175cf..ddeaa1c835d 100644 --- a/src/plugins/qmldesigner/components/componentcore/changestyleaction.cpp +++ b/src/plugins/qmldesigner/components/componentcore/changestyleaction.cpp @@ -14,7 +14,8 @@ namespace QmlDesigner { static QString styleConfigFileName(const QString &qmlFileName) { - ProjectExplorer::Project *currentProject = ProjectExplorer::ProjectManager::projectForFile(Utils::FilePath::fromString(qmlFileName)); + ProjectExplorer::Project *currentProject = ProjectExplorer::ProjectManager::projectForFile( + Utils::FilePath::fromString(qmlFileName)); if (currentProject) { const QList fileNames = currentProject->files( @@ -58,6 +59,11 @@ QList ChangeStyleWidgetAction::getAllStyleItems() if (Utils::HostOsInfo::isWindowsHost()) items.append({"Windows", "Windows", {}}); + if (DesignerMcuManager::instance().isMCUProject()) + items.append({"MCUDefaultStyle", "MCUDefaultStyle", {}}); + + //what if we have a custom style set in .conf? + return items; } @@ -155,7 +161,6 @@ QWidget *ChangeStyleWidgetAction::createWidget(QWidget *parent) comboBox->setCurrentIndex(0); } else if (DesignerMcuManager::instance().isMCUProject()) { comboBox->setDisabled(true); - //TODO: add tooltip regarding MCU limitations, however we are behind string freeze comboBox->setEditText(style); } else { comboBox->setDisabled(false); diff --git a/src/plugins/qmldesigner/components/toolbar/toolbarbackend.cpp b/src/plugins/qmldesigner/components/toolbar/toolbarbackend.cpp index 98cd50150ee..5a9431677da 100644 --- a/src/plugins/qmldesigner/components/toolbar/toolbarbackend.cpp +++ b/src/plugins/qmldesigner/components/toolbar/toolbarbackend.cpp @@ -353,6 +353,7 @@ ToolBarBackend::ToolBarBackend(QObject *parent) [this](ProjectExplorer::Project *project) { disconnect(m_kitConnection); emit isQt6Changed(); + emit isMCUsChanged(); emit projectOpenedChanged(); if (project) { m_kitConnection = connect(project, @@ -701,6 +702,20 @@ bool ToolBarBackend::isQt6() const return isQt6Project; } +bool ToolBarBackend::isMCUs() const +{ + if (!ProjectExplorer::ProjectManager::startupTarget()) + return false; + + const QmlProjectManager::QmlBuildSystem *buildSystem = qobject_cast( + ProjectExplorer::ProjectManager::startupTarget()->buildSystem()); + QTC_ASSERT(buildSystem, return false); + + const bool isQtForMCUsProject = buildSystem && buildSystem->qtForMCUs(); + + return isQtForMCUsProject; +} + bool ToolBarBackend::projectOpened() const { return ProjectExplorer::ProjectManager::instance()->startupProject(); diff --git a/src/plugins/qmldesigner/components/toolbar/toolbarbackend.h b/src/plugins/qmldesigner/components/toolbar/toolbarbackend.h index 52aa69a4db8..ce8dd62b524 100644 --- a/src/plugins/qmldesigner/components/toolbar/toolbarbackend.h +++ b/src/plugins/qmldesigner/components/toolbar/toolbarbackend.h @@ -93,6 +93,7 @@ class ToolBarBackend : public QObject Q_PROPERTY(QStringList kits READ kits NOTIFY kitsChanged) Q_PROPERTY(int currentKit READ currentKit NOTIFY currentKitChanged) Q_PROPERTY(bool isQt6 READ isQt6 NOTIFY isQt6Changed) + Q_PROPERTY(bool isMCUs READ isMCUs NOTIFY isMCUsChanged) Q_PROPERTY(bool projectOpened READ projectOpened NOTIFY projectOpenedChanged) public: @@ -138,6 +139,7 @@ public: int currentKit() const; bool isQt6() const; + bool isMCUs() const; bool projectOpened() const; @@ -157,6 +159,7 @@ signals: void kitsChanged(); void currentKitChanged(); void isQt6Changed(); + void isMCUsChanged(); void projectOpenedChanged(); private: From 47c7f22107fb4f7c0ff1846c501da037dedee4d5 Mon Sep 17 00:00:00 2001 From: Aleksei German Date: Thu, 2 Nov 2023 14:58:34 +0100 Subject: [PATCH 154/242] QmlDesigner: Fix hardcoded qtquick style config Task-number: QDS-11097 Change-Id: I0ebd531ee04420eae4d42de4c03205b592ff59af Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Thomas Hartmann Reviewed-by: --- .../componentcore/changestyleaction.cpp | 23 +++++++++++++++---- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/src/plugins/qmldesigner/components/componentcore/changestyleaction.cpp b/src/plugins/qmldesigner/components/componentcore/changestyleaction.cpp index ddeaa1c835d..3e1721e3fc4 100644 --- a/src/plugins/qmldesigner/components/componentcore/changestyleaction.cpp +++ b/src/plugins/qmldesigner/components/componentcore/changestyleaction.cpp @@ -18,11 +18,24 @@ static QString styleConfigFileName(const QString &qmlFileName) Utils::FilePath::fromString(qmlFileName)); if (currentProject) { - const QList fileNames = currentProject->files( - ProjectExplorer::Project::SourceFiles); - for (const Utils::FilePath &fileName : fileNames) - if (fileName.endsWith("qtquickcontrols2.conf")) - return fileName.toString(); + const auto &environment = currentProject->additionalEnvironment(); + const auto &envVar = std::find_if(std::begin(environment), + std::end(environment), + [](const auto &envVar) { + return (envVar.name == u"QT_QUICK_CONTROLS_CONF" + && envVar.operation + != Utils::EnvironmentItem::SetDisabled); + }); + if (envVar != std::end(environment)) { + const auto &fileNames = currentProject->files(ProjectExplorer::Project::SourceFiles); + const auto &foundFile = std::find_if(std::begin(fileNames), + std::end(fileNames), + [&](const auto &fileName) { + return fileName.fileName() == envVar->value; + }); + if (foundFile != std::end(fileNames)) + return foundFile->toString(); + } } return QString(); From 6a4850b1d8b0258da67716e560021917aba498ec Mon Sep 17 00:00:00 2001 From: Aleksei German Date: Thu, 2 Nov 2023 15:12:16 +0100 Subject: [PATCH 155/242] QmlDesigner: Disable Style combobox for MCUs Task-number: QDS-10338 Change-Id: I6e8242db0bd3f0981bb4d6d699f8780ae1cb6925 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Thomas Hartmann --- share/qtcreator/qmldesigner/statusbar/Main.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/share/qtcreator/qmldesigner/statusbar/Main.qml b/share/qtcreator/qmldesigner/statusbar/Main.qml index 9569aced92c..1eb33b82360 100644 --- a/share/qtcreator/qmldesigner/statusbar/Main.qml +++ b/share/qtcreator/qmldesigner/statusbar/Main.qml @@ -89,9 +89,9 @@ Item { model: backend.styles onActivated: backend.setCurrentStyle(styles.currentIndex) openUpwards: true - enabled: backend.isInDesignMode + enabled: backend.isInDesignMode && !backend.isMCUs property int currentStyleIndex: backend.currentStyle - onCurrentStyleIndexChanged: currentIndex = backend.currentStyle + onCurrentStyleIndexChanged: styles.currentIndex = backend.currentStyle } } } From 23f12f7b42cf64493ee28832d9e48eda85587f21 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Tue, 31 Oct 2023 15:15:11 +0200 Subject: [PATCH 156/242] QmlDesigner: Add shader overrides for materials and models in 3D view It is now possible to override all materials and models used in the scene with options available in QtQuick3D.DebugSettings. The overrides are assigned per-split. Fixes: QDS-11068 Change-Id: I3a3bc372e860d7f61942eb40166464c9c86efd8e Reviewed-by: Mahmoud Badri Reviewed-by: Reviewed-by: Mats Honkamaa --- .../commands/puppettocreatorcommand.h | 1 + .../interfaces/nodeinstanceglobal.h | 4 +- .../components/edit3d/edit3dview.cpp | 53 ++++++ .../components/edit3d/edit3dview.h | 14 ++ .../components/edit3d/edit3dwidget.cpp | 161 ++++++++++++++++++ .../components/edit3d/edit3dwidget.h | 5 + .../instances/nodeinstanceview.cpp | 5 + .../qml2puppet/mockfiles/qt6/EditView3D.qml | 38 ++++- .../qt5informationnodeinstanceserver.cpp | 14 ++ .../qt5informationnodeinstanceserver.h | 1 + 10 files changed, 291 insertions(+), 5 deletions(-) diff --git a/src/libs/qmlpuppetcommunication/commands/puppettocreatorcommand.h b/src/libs/qmlpuppetcommunication/commands/puppettocreatorcommand.h index 30b0cce9981..1e8d82e632b 100644 --- a/src/libs/qmlpuppetcommunication/commands/puppettocreatorcommand.h +++ b/src/libs/qmlpuppetcommunication/commands/puppettocreatorcommand.h @@ -16,6 +16,7 @@ public: Edit3DToolState, Render3DView, ActiveSceneChanged, + ActiveSplitChanged, RenderModelNodePreviewImage, Import3DSupport, NodeAtPos, diff --git a/src/libs/qmlpuppetcommunication/interfaces/nodeinstanceglobal.h b/src/libs/qmlpuppetcommunication/interfaces/nodeinstanceglobal.h index 791b285e66f..aefaeb88e3f 100644 --- a/src/libs/qmlpuppetcommunication/interfaces/nodeinstanceglobal.h +++ b/src/libs/qmlpuppetcommunication/interfaces/nodeinstanceglobal.h @@ -47,7 +47,9 @@ enum class View3DActionType { SyncEnvBackground, GetNodeAtPos, SetBakeLightsView3D, - SplitViewToggle + SplitViewToggle, + MaterialOverride, + ShowWireframe }; constexpr bool isNanotraceEnabled() diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp index 319a4d510a2..5c1b0d39a3c 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp +++ b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp @@ -58,6 +58,9 @@ Edit3DView::Edit3DView(ExternalDependenciesInterface &externalDependencies) m_compressionTimer.setInterval(1000); m_compressionTimer.setSingleShot(true); connect(&m_compressionTimer, &QTimer::timeout, this, &Edit3DView::handleEntriesChanged); + + for (int i = 0; i < 4; ++i) + m_splitToolStates.append({0, false}); } void Edit3DView::createEdit3DWidget() @@ -108,6 +111,18 @@ void Edit3DView::renderImage3DChanged(const QImage &img) void Edit3DView::updateActiveScene3D(const QVariantMap &sceneState) { + const QString activeSplitKey = QStringLiteral("activeSplit"); + if (sceneState.contains(activeSplitKey)) { + m_activeSplit = sceneState[activeSplitKey].toInt(); + + // If the sceneState contained just activeSplit key, then this is simply an active split + // change rather than entire active scene change, and we don't need to process further. + if (sceneState.size() == 1) + return; + } else { + m_activeSplit = 0; + } + const QString sceneKey = QStringLiteral("sceneInstanceId"); const QString selectKey = QStringLiteral("selectionMode"); const QString transformKey = QStringLiteral("transformMode"); @@ -122,6 +137,8 @@ void Edit3DView::updateActiveScene3D(const QVariantMap &sceneState) const QString particlesPlayKey = QStringLiteral("particlePlay"); const QString syncEnvBgKey = QStringLiteral("syncEnvBackground"); const QString splitViewKey = QStringLiteral("splitView"); + const QString matOverrideKey = QStringLiteral("matOverride"); + const QString showWireframeKey = QStringLiteral("showWireframe"); if (sceneState.contains(sceneKey)) { qint32 newActiveScene = sceneState[sceneKey].value(); @@ -197,6 +214,24 @@ void Edit3DView::updateActiveScene3D(const QVariantMap &sceneState) else m_splitViewAction->action()->setChecked(false); + if (sceneState.contains(matOverrideKey)) { + const QVariantList overrides = sceneState[matOverrideKey].toList(); + for (int i = 0; i < 4; ++i) + m_splitToolStates[i].matOverride = i < overrides.size() ? overrides[i].toInt() : 0; + } else { + for (SplitToolState &state : m_splitToolStates) + state.matOverride = 0; + } + + if (sceneState.contains(showWireframeKey)) { + const QVariantList showList = sceneState[showWireframeKey].toList(); + for (int i = 0; i < 4; ++i) + m_splitToolStates[i].showWireframe = i < showList.size() ? showList[i].toBool() : false; + } else { + for (SplitToolState &state : m_splitToolStates) + state.showWireframe = false; + } + // Syncing background color only makes sense for children of View3D instances bool syncValue = false; bool syncEnabled = false; @@ -663,6 +698,24 @@ void Edit3DView::syncSnapAuxPropsToSettings() Edit3DViewConfig::load(DesignerSettingsKey::EDIT3DVIEW_SNAP_SCALE_INTERVAL)); } +const QList &Edit3DView::splitToolStates() const +{ + return m_splitToolStates; +} + +void Edit3DView::setSplitToolState(int splitIndex, const SplitToolState &state) +{ + if (splitIndex >= m_splitToolStates.size()) + return; + + m_splitToolStates[splitIndex] = state; +} + +int Edit3DView::activeSplit() const +{ + return m_activeSplit; +} + void Edit3DView::createEdit3DActions() { m_selectionModeAction = std::make_unique( diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dview.h b/src/plugins/qmldesigner/components/edit3d/edit3dview.h index 84742943a22..56248d9587c 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dview.h +++ b/src/plugins/qmldesigner/components/edit3d/edit3dview.h @@ -32,6 +32,12 @@ class QMLDESIGNERCOMPONENTS_EXPORT Edit3DView : public AbstractView Q_OBJECT public: + struct SplitToolState + { + int matOverride = 0; + bool showWireframe = false; + }; + Edit3DView(ExternalDependenciesInterface &externalDependencies); WidgetInfo widgetInfo() override; @@ -78,6 +84,11 @@ public: void syncSnapAuxPropsToSettings(); + const QList &splitToolStates() const; + void setSplitToolState(int splitIndex, const SplitToolState &state); + + int activeSplit() const; + private slots: void onEntriesChanged(); @@ -160,6 +171,9 @@ private: QPointer m_bakeLights; bool m_isBakingLightsSupported = false; QPointer m_snapConfiguration; + int m_activeSplit = 0; + + QList m_splitToolStates; friend class Edit3DAction; }; diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp index 70de2d29367..112d4e7eea5 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp +++ b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp @@ -281,6 +281,95 @@ void Edit3DWidget::createContextMenu() m_toggleGroupAction->setChecked(defaultToggleGroupAction->isChecked()); m_contextMenu->addSeparator(); + + auto overridesSubMenu = new QmlEditorMenu(tr("Shader Overrides"), m_contextMenu); + overridesSubMenu->setToolTipsVisible(true); + m_contextMenu->addMenu(overridesSubMenu); + + m_wireFrameAction = overridesSubMenu->addAction( + tr("Wireframe"), this, &Edit3DWidget::onWireframeAction); + m_wireFrameAction->setCheckable(true); + m_wireFrameAction->setToolTip(tr("Show models as wireframe.")); + + overridesSubMenu->addSeparator(); + + // The type enum order must match QQuick3DDebugSettings::QQuick3DMaterialOverrides enums + enum class MaterialOverrideType { + None, + BaseColor, + Roughness, + Metalness, + Diffuse, + Specular, + ShadowOcclusion, + Emission, + AmbientOcclusion, + Normals, + Tangents, + Binormals, + F0 + }; + + auto addOverrideMenuAction = [&](const QString &label, const QString &toolTip, + MaterialOverrideType type) { + QAction *action = overridesSubMenu->addAction( + label, this, &Edit3DWidget::onMatOverrideAction); + action->setData(int(type)); + action->setCheckable(true); + action->setToolTip(toolTip); + m_matOverrideActions.insert(int(type), action); + }; + + addOverrideMenuAction(tr("No Material Override"), + tr("Rendering occurs as normal."), + MaterialOverrideType::None); + addOverrideMenuAction(tr("Base Color"), + tr("The BaseColor or Diffuse color of a material is passed through without any lighting."), + MaterialOverrideType::BaseColor); + addOverrideMenuAction(tr("Roughness"), + tr("The Roughness of a material is passed through as an unlit greyscale value."), + MaterialOverrideType::Roughness); + addOverrideMenuAction(tr("Metalness"), + tr("The Metalness of a material is passed through as an unlit greyscale value."), + MaterialOverrideType::Metalness); + addOverrideMenuAction(tr("Diffuse"), + tr("Only the diffuse contribution of the material after all lighting."), + MaterialOverrideType::Diffuse); + addOverrideMenuAction(tr("Specular"), + tr("Only the specular contribution of the material after all lighting."), + MaterialOverrideType::Specular); + addOverrideMenuAction(tr("Shadow Occlusion"), + tr("The Occlusion caused by shadows as a greyscale value."), + MaterialOverrideType::ShadowOcclusion); + addOverrideMenuAction(tr("Emission"), + tr("Only the emissive contribution of the material."), + MaterialOverrideType::Emission); + addOverrideMenuAction(tr("Ambient Occlusion"), + tr("Only the Ambient Occlusion of the material."), + MaterialOverrideType::AmbientOcclusion); + addOverrideMenuAction(tr("Normals"), + tr("The interpolated world space Normal value of the material mapped to an RGB color."), + MaterialOverrideType::Normals); + addOverrideMenuAction(tr("Tangents"), + tr("The interpolated world space Tangent value of the material mapped to an RGB color.\n" + "This will only be visible if the Tangent value is used."), + MaterialOverrideType::Tangents); + addOverrideMenuAction(tr("Binormals"), + tr("The interpolated world space Binormal value of the material mapped to an RGB color.\n" + "This will only be visible if the Binormal value is used."), + MaterialOverrideType::Binormals); + addOverrideMenuAction(tr("Fresnel 0"), + tr("This represents the Fresnel Reflectance at 0 Degrees.\n" + "This will only be visible for materials that calculate an F0 value."), + MaterialOverrideType::F0); + + overridesSubMenu->addSeparator(); + + QAction *resetAction = overridesSubMenu->addAction( + tr("Reset All Overrides"), this, &Edit3DWidget::onResetAllOverridesAction); + resetAction->setToolTip(tr("Reset all overrides for all splits.")); + + m_contextMenu->addSeparator(); } bool Edit3DWidget::isPasteAvailable() const @@ -422,6 +511,69 @@ void Edit3DWidget::onCreateAction() }); } +void Edit3DWidget::onMatOverrideAction() +{ + QAction *action = qobject_cast(sender()); + if (!action || !m_view || !m_view->model()) + return; + + QVariantList list; + for (int i = 0; i < m_view->splitToolStates().size(); ++i) { + Edit3DView::SplitToolState state = m_view->splitToolStates()[i]; + if (i == m_view->activeSplit()) { + state.matOverride = action->data().toInt(); + m_view->setSplitToolState(i, state); + list.append(action->data()); + } else { + list.append(state.matOverride); + } + } + + view()->emitView3DAction(View3DActionType::MaterialOverride, list); +} + +void Edit3DWidget::onWireframeAction() +{ + QAction *action = qobject_cast(sender()); + if (!action || !m_view || !m_view->model()) + return; + + QVariantList list; + for (int i = 0; i < m_view->splitToolStates().size(); ++i) { + Edit3DView::SplitToolState state = m_view->splitToolStates()[i]; + if (i == m_view->activeSplit()) { + state.showWireframe = action->isChecked(); + m_view->setSplitToolState(i, state); + list.append(action->isChecked()); + } else { + list.append(state.showWireframe); + } + } + + view()->emitView3DAction(View3DActionType::ShowWireframe, list); +} + +void Edit3DWidget::onResetAllOverridesAction() +{ + if (!m_view || !m_view->model()) + return; + + QVariantList wList; + QVariantList mList; + + for (int i = 0; i < m_view->splitToolStates().size(); ++i) { + Edit3DView::SplitToolState state; + state.showWireframe = false; + state.matOverride = 0; + m_view->setSplitToolState(i, state); + wList.append(state.showWireframe); + mList.append(state.matOverride); + } + + view()->emitView3DAction(View3DActionType::ShowWireframe, wList); + view()->emitView3DAction(View3DActionType::MaterialOverride, mList); +} + void Edit3DWidget::contextHelp(const Core::IContext::HelpCallback &callback) const { if (m_view) @@ -502,6 +654,15 @@ void Edit3DWidget::showContextMenu(const QPoint &pos, const ModelNode &modelNode m_bakeLightsAction->setVisible(view()->bakeLightsAction()->action()->isVisible()); m_bakeLightsAction->setEnabled(view()->bakeLightsAction()->action()->isEnabled()); + if (m_view) { + int idx = m_view->activeSplit(); + m_wireFrameAction->setChecked(m_view->splitToolStates()[idx].showWireframe); + for (QAction *a : std::as_const(m_matOverrideActions)) + a->setChecked(false); + int type = m_view->splitToolStates()[idx].matOverride; + m_matOverrideActions[type]->setChecked(true); + } + m_contextMenu->popup(mapToGlobal(pos)); } diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.h b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.h index 7f119c205a5..67e23b0b98c 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.h +++ b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.h @@ -54,6 +54,9 @@ public: private slots: void onCreateAction(); + void onMatOverrideAction(); + void onWireframeAction(); + void onResetAllOverridesAction(); protected: void dragEnterEvent(QDragEnterEvent *dragEnterEvent) override; @@ -90,6 +93,8 @@ private: QPointer m_alignViewAction; QPointer m_selectParentAction; QPointer m_toggleGroupAction; + QPointer m_wireFrameAction; + QHash> m_matOverrideActions; QPointer m_createSubMenu; ModelNode m_contextMenuTarget; QVector3D m_contextMenuPos3d; diff --git a/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp b/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp index 2038e7f3cee..cd56af86643 100644 --- a/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp +++ b/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp @@ -1719,6 +1719,11 @@ void NodeInstanceView::handlePuppetToCreatorCommand(const PuppetToCreatorCommand } else if (command.type() == PuppetToCreatorCommand::ActiveSceneChanged) { const auto sceneState = qvariant_cast(command.data()); emitUpdateActiveScene3D(sceneState); + } else if (command.type() == PuppetToCreatorCommand::ActiveSplitChanged) { + // Active split change is a special case of active scene change + QVariantMap splitState; + splitState.insert("activeSplit", command.data()); + emitUpdateActiveScene3D(splitState); } else if (command.type() == PuppetToCreatorCommand::RenderModelNodePreviewImage) { ImageContainer container = qvariant_cast(command.data()); QImage image = container.image(); diff --git a/src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml b/src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml index 6d9a89b89f2..ec10d481e1d 100644 --- a/src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml +++ b/src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml @@ -1,9 +1,9 @@ // Copyright (C) 2019 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 -import QtQuick 6.0 -import QtQuick3D 6.0 -import MouseArea3D 1.0 +import QtQuick +import QtQuick3D +import MouseArea3D Item { id: viewRoot @@ -17,6 +17,8 @@ Item { property var overlayViews: [overlayView0, overlayView1, overlayView2, overlayView3] property var cameraControls: [cameraControl0, cameraControl1, cameraControl2, cameraControl3] property var viewRects: [viewRect0, viewRect1, viewRect2, viewRect3] + property var materialOverrides: [DebugSettings.None, DebugSettings.None, DebugSettings.None, DebugSettings.None] + property var showWireframes: [false, false, false, false] property var activeEditView: editViews[activeSplit] property var activeOverlayView: overlayViews[activeSplit] property string sceneId @@ -56,6 +58,7 @@ Item { signal commitObjectProperty(var objects, var propNames) signal changeObjectProperty(var objects, var propNames) signal notifyActiveSceneChange() + signal notifyActiveSplitChange(int index) onUsePerspectiveChanged: _generalHelper.storeToolState(sceneId, "usePerspective", usePerspective) onShowEditLightChanged: _generalHelper.storeToolState(sceneId, "showEditLight", showEditLight) @@ -68,6 +71,8 @@ Item { onShowParticleEmitterChanged: _generalHelper.storeToolState(sceneId, "showParticleEmitter", showParticleEmitter); onSelectionModeChanged: _generalHelper.storeToolState(sceneId, "selectionMode", selectionMode); onTransformModeChanged: _generalHelper.storeToolState(sceneId, "transformMode", transformMode); + onMaterialOverridesChanged: _generalHelper.storeToolState(sceneId, "matOverride", materialOverrides); + onShowWireframesChanged: _generalHelper.storeToolState(sceneId, "showWireframe", showWireframes); onSplitViewChanged: { _generalHelper.storeToolState(sceneId, "splitView", splitView); _generalHelper.requestOverlayUpdate(); @@ -75,6 +80,7 @@ Item { onActiveSplitChanged: { _generalHelper.storeToolState(sceneId, "activeSplit", activeSplit); cameraControls[activeSplit].forceActiveFocus(); + notifyActiveSplitChange(activeSplit); } onActiveSceneChanged: updateActiveScene() @@ -96,7 +102,9 @@ Item { "gridColor": gridColor, "importScene": activeScene, "cameraLookAt": cameraControls[i]._lookAtPoint, - "z": 1}); + "z": 1, + "sceneEnv.debugSettings.materialOverride": materialOverrides[i], + "sceneEnv.debugSettings.wireframeEnabled": showWireframes[i]}); editViews[i].usePerspective = Qt.binding(function() {return usePerspective;}); editViews[i].showSceneLight = Qt.binding(function() {return showEditLight;}); editViews[i].showGrid = Qt.binding(function() {return showGrid;}); @@ -107,6 +115,16 @@ Item { editViews[2].cameraLookAt = Qt.binding(function() {return cameraControl2._lookAtPoint;}); editViews[3].cameraLookAt = Qt.binding(function() {return cameraControl3._lookAtPoint;}); + editViews[0].sceneEnv.debugSettings.materialOverride = Qt.binding(function() {return materialOverrides[0];}); + editViews[1].sceneEnv.debugSettings.materialOverride = Qt.binding(function() {return materialOverrides[1];}); + editViews[2].sceneEnv.debugSettings.materialOverride = Qt.binding(function() {return materialOverrides[2];}); + editViews[3].sceneEnv.debugSettings.materialOverride = Qt.binding(function() {return materialOverrides[3];}); + + editViews[0].sceneEnv.debugSettings.wireframeEnabled = Qt.binding(function() {return showWireframes[0];}); + editViews[1].sceneEnv.debugSettings.wireframeEnabled = Qt.binding(function() {return showWireframes[1];}); + editViews[2].sceneEnv.debugSettings.wireframeEnabled = Qt.binding(function() {return showWireframes[2];}); + editViews[3].sceneEnv.debugSettings.wireframeEnabled = Qt.binding(function() {return showWireframes[3];}); + selectionBoxCount = 0; editViewsChanged(); return true; @@ -340,6 +358,16 @@ Item { activeSplit = toolStates.activeSplit; else if (resetToDefault) activeSplit = 0; + + if ("showWireframe" in toolStates) + showWireframes = toolStates.showWireframe; + else if (resetToDefault) + showWireframes = [false, false, false, false]; + + if ("matOverride" in toolStates) + materialOverrides = toolStates.matOverride; + else if (resetToDefault) + materialOverrides = [DebugSettings.None, DebugSettings.None, DebugSettings.None, DebugSettings.None]; } function storeCurrentToolStates() @@ -357,6 +385,8 @@ Item { _generalHelper.storeToolState(sceneId, "transformMode", transformMode); _generalHelper.storeToolState(sceneId, "splitView", splitView) _generalHelper.storeToolState(sceneId, "activeSplit", activeSplit) + _generalHelper.storeToolState(sceneId, "showWireframe", showWireframes) + _generalHelper.storeToolState(sceneId, "matOverride", materialOverrides) for (var i = 0; i < 4; ++i) cameraControls[i].storeCameraState(0); diff --git a/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp b/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp index 63d4e79241b..180390def44 100644 --- a/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp +++ b/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp @@ -881,6 +881,12 @@ void Qt5InformationNodeInstanceServer::handleActiveSceneChange() #endif } +void Qt5InformationNodeInstanceServer::handleActiveSplitChange(int index) +{ + nodeInstanceClient()->handlePuppetToCreatorCommand({PuppetToCreatorCommand::ActiveSplitChanged, + index}); +} + void Qt5InformationNodeInstanceServer::handleToolStateChanged(const QString &sceneId, const QString &tool, const QVariant &toolState) @@ -1860,6 +1866,8 @@ void Qt5InformationNodeInstanceServer::setup3DEditView( this, SLOT(handleObjectPropertyChange(QVariant, QVariant))); QObject::connect(m_editView3DData.rootItem, SIGNAL(notifyActiveSceneChange()), this, SLOT(handleActiveSceneChange())); + QObject::connect(m_editView3DData.rootItem, SIGNAL(notifyActiveSplitChange(int)), + this, SLOT(handleActiveSplitChange(int))); QObject::connect(&m_propertyChangeTimer, &QTimer::timeout, this, &Qt5InformationNodeInstanceServer::handleObjectPropertyChangeTimeout); QObject::connect(&m_selectionChangeTimer, &QTimer::timeout, @@ -2503,6 +2511,12 @@ void Qt5InformationNodeInstanceServer::view3DAction(const View3DActionCommand &c case View3DActionType::SplitViewToggle: updatedToolState.insert("splitView", command.isEnabled()); break; + case View3DActionType::ShowWireframe: + updatedToolState.insert("showWireframe", command.value().toList()); + break; + case View3DActionType::MaterialOverride: + updatedToolState.insert("matOverride", command.value().toList()); + break; default: break; diff --git a/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.h b/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.h index 3ac044cbace..b603bb134eb 100644 --- a/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.h +++ b/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.h @@ -68,6 +68,7 @@ private slots: void handleObjectPropertyCommit(const QVariant &objects, const QVariant &propNames); void handleObjectPropertyChange(const QVariant &objects, const QVariant &propNames); void handleActiveSceneChange(); + void handleActiveSplitChange(int index); void handleToolStateChanged(const QString &sceneId, const QString &tool, const QVariant &toolState); void handleView3DSizeChange(); From 84b050169331d1c7cc76d7cd50fc0dccbc28c798 Mon Sep 17 00:00:00 2001 From: Brook Cronin Date: Mon, 30 Oct 2023 18:07:37 +0100 Subject: [PATCH 157/242] QmlDesigner: Add micro toolbar for connection editor code preview buttons Task-number: QDS-10732 Change-Id: I67fef29db4b6dfb184a60e39723a6ab9437c694b Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Reviewed-by: Thomas Hartmann --- .../ConnectionsDialogForm.qml | 160 ++++++++++-------- .../StudioTheme/MicroToolbarButtonStyle.qml | 34 ++++ .../imports/StudioTheme/Values.qml | 4 + .../imports/StudioTheme/qmldir | 1 + share/qtcreator/themes/dark.creatortheme | 4 + share/qtcreator/themes/default.creatortheme | 4 + .../themes/design-light.creatortheme | 4 + share/qtcreator/themes/design.creatortheme | 4 + share/qtcreator/themes/flat-dark.creatortheme | 4 + .../qtcreator/themes/flat-light.creatortheme | 4 + share/qtcreator/themes/flat.creatortheme | 4 + src/libs/utils/theme/theme.h | 4 +- 12 files changed, 160 insertions(+), 71 deletions(-) create mode 100644 share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/MicroToolbarButtonStyle.qml diff --git a/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialogForm.qml b/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialogForm.qml index 883468947ff..1e924d30b20 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialogForm.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialogForm.qml @@ -216,80 +216,100 @@ Column { && backend.hasCondition && backend.hasElse } - Row { - - HelperWidgets.AbstractButton { - id: editorButton - buttonIcon: StudioTheme.Constants.codeEditor_medium - tooltip: qsTr("Write the conditions for the components and the signals manually.") - onClicked: expressionDialogLoader.show() - } - HelperWidgets.AbstractButton { - id: jumpToCodeButton - buttonIcon: StudioTheme.Constants.jumpToCode_medium - tooltip: qsTr("Jump to Code Editor.") - onClicked: backend.jumpToCode() - } - } - - // Editor - Rectangle { - id: editor + // code preview toolbar + Column { + id: miniToolbarEditor width: parent.width - height: 150 - color: StudioTheme.Values.themeConnectionCodeEditor + spacing: -2 - Text { - id: code - anchors.fill: parent - anchors.margins: 4 - text: backend.indentedSource - color: StudioTheme.Values.themeTextColor - font.pixelSize: StudioTheme.Values.myFontSize - wrapMode: Text.Wrap - horizontalAlignment: code.lineCount === 1 ? Text.AlignHCenter : Text.AlignLeft - verticalAlignment: Text.AlignVCenter - elide: Text.ElideRight + Rectangle { + id: miniToolbar + width: parent.width + height: editorButton.height + 2 + radius: 4 + z: -1 + color: StudioTheme.Values.themeConnectionEditorMicroToolbar - } - - Loader { - id: expressionDialogLoader - parent: editor - anchors.fill: parent - visible: false - active: visible - - function show() { - expressionDialogLoader.visible = true - } - - sourceComponent: Item { - id: bindingEditorParent - - Component.onCompleted: { - bindingEditor.showWidget() - bindingEditor.text = backend.source - bindingEditor.showControls(false) - bindingEditor.setMultilne(true) - bindingEditor.updateWindowName() + Row { + spacing: 2 + HelperWidgets.AbstractButton { + id: editorButton + style: StudioTheme.Values.microToolbarButtonStyle + buttonIcon: StudioTheme.Constants.codeEditor_medium + tooltip: qsTr("Write the conditions for the components and the signals manually.") + onClicked: expressionDialogLoader.show() } - - ActionEditor { - id: bindingEditor - - onRejected: { - hideWidget() - expressionDialogLoader.visible = false - } - - onAccepted: { - backend.setNewSource(bindingEditor.text) - hideWidget() - expressionDialogLoader.visible = false - } + HelperWidgets.AbstractButton { + id: jumpToCodeButton + style: StudioTheme.Values.microToolbarButtonStyle + buttonIcon: StudioTheme.Constants.jumpToCode_medium + tooltip: qsTr("Jump to Code Editor.") + onClicked: backend.jumpToCode() } } } - } -} + + // Editor + Rectangle { + id: editor + width: parent.width + height: 150 + color: StudioTheme.Values.themeConnectionCodeEditor + + Text { + id: code + anchors.fill: parent + anchors.margins: 4 + text: backend.indentedSource + color: StudioTheme.Values.themeTextColor + font.pixelSize: StudioTheme.Values.myFontSize + wrapMode: Text.Wrap + horizontalAlignment: code.lineCount === 1 ? Text.AlignHCenter : Text.AlignLeft + verticalAlignment: Text.AlignVCenter + elide: Text.ElideRight + + } + + + Loader { + id: expressionDialogLoader + parent: editor + anchors.fill: parent + visible: false + active: visible + + function show() { + expressionDialogLoader.visible = true + } + + sourceComponent: Item { + id: bindingEditorParent + + Component.onCompleted: { + bindingEditor.showWidget() + bindingEditor.text = backend.source + bindingEditor.showControls(false) + bindingEditor.setMultilne(true) + bindingEditor.updateWindowName() + } + + ActionEditor { + id: bindingEditor + + onRejected: { + hideWidget() + expressionDialogLoader.visible = false + } + + onAccepted: { + backend.setNewSource(bindingEditor.text) + hideWidget() + expressionDialogLoader.visible = false + } + } + } + } // loader + } // rect + } //col 2 +}//col1 + diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/MicroToolbarButtonStyle.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/MicroToolbarButtonStyle.qml new file mode 100644 index 00000000000..24d5f1517ee --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/MicroToolbarButtonStyle.qml @@ -0,0 +1,34 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0 WITH Qt-GPL-exception-1.0 + +import QtQuick + +ControlStyle { + + baseIconFontSize: Values.bigFont + controlSize: Qt.size(Values.viewBarComboWidth, Values.viewBarComboHeight) + smallIconFontSize: Values.viewBarComboIcon + + radius: Values.smallRadius + + icon: ControlStyle.IconColors { + idle: Values.themeTextColor + hover: Values.themeTextColor + interaction: Values.themeIconColor + disabled: "#636363" + } + + background: ControlStyle.BackgroundColors { + idle: Values.themeConnectionEditorMicroToolbar + hover: Values.themeConnectionEditorButtonBackground_hover //"#373737" + interaction: Values.themeInteraction + disabled: Values.themecontrolBackground_statusbarIdle + } + + border: ControlStyle.BorderColors { + idle: Values.themeConnectionEditorMicroToolbar + hover: Values.themeConnectionEditorButtonBorder_hover //"#5E5E5E" + interaction: Values.themeInteraction + disabled: Values.themecontrolBackground_statusbarIdle + } +} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/Values.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/Values.qml index 88961be3407..82dbf132b19 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/Values.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/Values.qml @@ -262,10 +262,12 @@ QtObject { property color themeControlBackground_toolbarHover: Theme.color(Theme.DScontrolBackground_toolbarHover) property color themeControlBackground_topToolbarHover: Theme.color(Theme.DScontrolBackground_topToolbarHover) property color themeToolbarBackground: Theme.color(Theme.DStoolbarBackground) + property color themeConnectionEditorButtonBackground_hover: Theme.color(Theme.DSconnectionEditorButtonBackground_hover) //outlines property color controlOutline_toolbarIdle: Theme.color(Theme.DScontrolOutline_topToolbarIdle) property color controlOutline_toolbarHover: Theme.color(Theme.DScontrolOutline_topToolbarHover) + property color themeConnectionEditorButtonBorder_hover: Theme.color(Theme.DSconnectionEditorButtonBorder_hover) //icons property color themeToolbarIcon_blocked: Theme.color(Theme.DStoolbarIcon_blocked) @@ -344,6 +346,7 @@ QtObject { property color themeBackgroundColorNormal: Theme.color(Theme.DSBackgroundColorNormal) property color themeBackgroundColorAlternate: Theme.color(Theme.DSBackgroundColorAlternate) property color themeConnectionCodeEditor: Theme.color(Theme.DSconnectionCodeEditor) + property color themeConnectionEditorMicroToolbar: Theme.color(Theme.DSconnectionEditorMicroToolbar) // Text colors property color themeTextColor: Theme.color(Theme.DStextColor) @@ -469,6 +472,7 @@ QtObject { property ControlStyle toolbarStyle: ToolbarStyle {} property ControlStyle primaryToolbarStyle: PrimaryButtonStyle {} property ControlStyle toolbarButtonStyle: TopToolbarButtonStyle {} + property ControlStyle microToolbarButtonStyle: MicroToolbarButtonStyle {} property ControlStyle viewBarButtonStyle: ViewBarButtonStyle {} property ControlStyle viewBarControlStyle: ViewBarControlStyle {} property ControlStyle statusbarButtonStyle: StatusBarButtonStyle {} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/qmldir b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/qmldir index 45e55c4edc6..e22b7d9a583 100755 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/qmldir +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/qmldir @@ -12,6 +12,7 @@ SearchControlStyle 1.0 SearchControlStyle.qml StatusBarButtonStyle 1.0 StatusBarButtonStyle.qml StatusBarControlStyle 1.0 StatusBarControlStyle.qml TopToolbarButtonStyle 1.0 TopToolbarButtonStyle.qml +MicroToolbarButtonStyle 1.0 MicroToolbarButtonStyle.qml ViewBarButtonStyle 1.0 ViewBarButtonStyle.qml ViewBarControlStyle 1.0 ViewBarControlStyle.qml ViewStyle 1.0 ViewStyle.qml diff --git a/share/qtcreator/themes/dark.creatortheme b/share/qtcreator/themes/dark.creatortheme index e5b312ed159..e5a6d348f7c 100644 --- a/share/qtcreator/themes/dark.creatortheme +++ b/share/qtcreator/themes/dark.creatortheme @@ -35,6 +35,7 @@ graniteGrey=ff343434 ashGrey=ff434343 midnightGrey=ff333333 dawnGrey=ff2a2a2a +coalGrey=ff282828 offBlack=ff202020 nearBlack=ff1b1b1b fullBlack=ff000000 @@ -102,6 +103,9 @@ DSpopoutButtonBorder_disabled=offBlack ;4.4 DSconnectionCodeEditor=midnightGrey +DSconnectionEditorMicroToolbar=coalGrey +DSconnectionEditorButtonBackground_hover=midnightGrey +DSconnectionEditorButtonBorder_hover=duskGrey DSpillText=fullWhite DSpillTextSelected=fullBlack DspillTextEdit=fullWhite diff --git a/share/qtcreator/themes/default.creatortheme b/share/qtcreator/themes/default.creatortheme index 241502d1e53..7368405cc19 100644 --- a/share/qtcreator/themes/default.creatortheme +++ b/share/qtcreator/themes/default.creatortheme @@ -27,6 +27,7 @@ graniteGrey=ff343434 ashGrey=ff434343 midnightGrey=ff333333 dawnGrey=ff2a2a2a +coalGrey=ff282828 offBlack=ff202020 nearBlack=ff1b1b1b fullBlack=ff000000 @@ -96,6 +97,9 @@ DSpopoutButtonBorder_disabled=offWhite ;4.4 DSconnectionCodeEditor=lightWhite +DSconnectionEditorMicroToolbar=concreteGrey +DSconnectionEditorButtonBackground_hover=lightWhite +DSconnectionEditorButtonBorder_hover=duskGrey DSpillText=fullWhite DSpillTextSelected=fullBlack DspillTextEdit=fullBlack diff --git a/share/qtcreator/themes/design-light.creatortheme b/share/qtcreator/themes/design-light.creatortheme index d77feb6d969..ae140107e51 100644 --- a/share/qtcreator/themes/design-light.creatortheme +++ b/share/qtcreator/themes/design-light.creatortheme @@ -40,6 +40,7 @@ graniteGrey=ff343434 ashGrey=ff434343 midnightGrey=ff333333 dawnGrey=ff2a2a2a +coalGrey=ff282828 offBlack=ff202020 nearBlack=ff1b1b1b fullBlack=ff000000 @@ -109,6 +110,9 @@ DSpopoutButtonBorder_disabled=offWhite ;4.4 DSconnectionCodeEditor=lightWhite +DSconnectionEditorMicroToolbar=concreteGrey +DSconnectionEditorButtonBackground_hover=lightWhite +DSconnectionEditorButtonBorder_hover=duskGrey DSpillText=fullWhite DSpillTextSelected=fullBlack DspillTextEdit=fullBlack diff --git a/share/qtcreator/themes/design.creatortheme b/share/qtcreator/themes/design.creatortheme index 52fbf12ee87..25cf18da54a 100644 --- a/share/qtcreator/themes/design.creatortheme +++ b/share/qtcreator/themes/design.creatortheme @@ -38,6 +38,7 @@ graniteGrey=ff343434 ashGrey=ff434343 midnightGrey=ff333333 dawnGrey=ff2a2a2a +coalGrey=ff282828 offBlack=ff202020 nearBlack=ff1b1b1b fullBlack=ff000000 @@ -107,6 +108,9 @@ DSpopoutButtonBorder_disabled=offBlack ;4.4 DSconnectionCodeEditor=midnightGrey +DSconnectionEditorMicroToolbar=coalGrey +DSconnectionEditorButtonBackground_hover=midnightGrey +DSconnectionEditorButtonBorder_hover=duskGrey DSpillText=fullWhite DSpillTextSelected=fullBlack DspillTextEdit=fullWhite diff --git a/share/qtcreator/themes/flat-dark.creatortheme b/share/qtcreator/themes/flat-dark.creatortheme index d015c149f0b..2efaf756b1e 100644 --- a/share/qtcreator/themes/flat-dark.creatortheme +++ b/share/qtcreator/themes/flat-dark.creatortheme @@ -39,6 +39,7 @@ graniteGrey=ff343434 ashGrey=ff434343 midnightGrey=ff333333 dawnGrey=ff2a2a2a +coalGrey=ff282828 offBlack=ff202020 nearBlack=ff1b1b1b fullBlack=ff000000 @@ -107,6 +108,9 @@ DSpopoutButtonBorder_disabled=offBlack ;4.4 DSconnectionCodeEditor=midnightGrey +DSconnectionEditorMicroToolbar=coalGrey +DSconnectionEditorButtonBackground_hover=midnightGrey +DSconnectionEditorButtonBorder_hover=duskGrey DSpillText=fullWhite DSpillTextSelected=fullBlack DspillTextEdit=fullWhite diff --git a/share/qtcreator/themes/flat-light.creatortheme b/share/qtcreator/themes/flat-light.creatortheme index 21ded057ece..f0315278d50 100644 --- a/share/qtcreator/themes/flat-light.creatortheme +++ b/share/qtcreator/themes/flat-light.creatortheme @@ -36,6 +36,7 @@ graniteGrey=ff343434 ashGrey=ff434343 midnightGrey=ff333333 dawnGrey=ff2a2a2a +coalGrey=ff282828 offBlack=ff202020 nearBlack=ff1b1b1b fullBlack=ff000000 @@ -105,6 +106,9 @@ DSpopoutButtonBorder_disabled=offWhite ;4.4 DSconnectionCodeEditor=lightWhite +DSconnectionEditorMicroToolbar=concreteGrey +DSconnectionEditorButtonBackground_hover=lightWhite +DSconnectionEditorButtonBorder_hover=duskGrey DSpillText=fullWhite DSpillTextSelected=fullBlack DspillTextEdit=fullBlack diff --git a/share/qtcreator/themes/flat.creatortheme b/share/qtcreator/themes/flat.creatortheme index 6dc677d9ff3..a7a851600e9 100644 --- a/share/qtcreator/themes/flat.creatortheme +++ b/share/qtcreator/themes/flat.creatortheme @@ -33,6 +33,7 @@ graniteGrey=ff343434 ashGrey=ff434343 midnightGrey=ff333333 dawnGrey=ff2a2a2a +coalGrey=ff282828 offBlack=ff202020 nearBlack=ff1b1b1b fullBlack=ff000000 @@ -101,6 +102,9 @@ DSpopoutButtonBorder_disabled=offBlack ;4.4 DSconnectionCodeEditor=midnightGrey +DSconnectionEditorMicroToolbar=coalGrey +DSconnectionEditorButtonBackground_hover=midnightGrey +DSconnectionEditorButtonBorder_hover=duskGrey DSpillText=fullWhite DSpillTextSelected=fullBlack DspillTextEdit=fullWhite diff --git a/src/libs/utils/theme/theme.h b/src/libs/utils/theme/theme.h index 0dbdb61fbe4..d032a81ecab 100644 --- a/src/libs/utils/theme/theme.h +++ b/src/libs/utils/theme/theme.h @@ -355,7 +355,9 @@ public: DSpillOperatorBackgroundHover, DSpillLiteralBackgroundIdle, DSpillLiteralBackgroundHover, - + DSconnectionEditorMicroToolbar, + DSconnectionEditorButtonBackground_hover, + DSconnectionEditorButtonBorder_hover, /*Legacy QtDS*/ DSiconColor, From 05037278bc4ef0fbd1ef994832891aa5cfb396f4 Mon Sep 17 00:00:00 2001 From: Amr Essam Date: Thu, 2 Nov 2023 18:09:06 +0200 Subject: [PATCH 158/242] QmlDesigner: Fix critical cmake issues for effect maker Qtc12 issues related to how effect maker configured Task-number: QDS-11064 Change-Id: Ic7768ef41928a97e447e12c0a2c252f87fe82c68 Reviewed-by: Mahmoud Badri --- src/plugins/effectmakernew/CMakeLists.txt | 3 +-- .../effectmakernew/EffectMakerNew.json.in | 20 +++++++++---------- .../effectmakernew/effectmakerwidget.cpp | 10 +++++----- 3 files changed, 16 insertions(+), 17 deletions(-) diff --git a/src/plugins/effectmakernew/CMakeLists.txt b/src/plugins/effectmakernew/CMakeLists.txt index 81496c7eb78..d190481db85 100644 --- a/src/plugins/effectmakernew/CMakeLists.txt +++ b/src/plugins/effectmakernew/CMakeLists.txt @@ -1,9 +1,8 @@ find_package(Qt6 OPTIONAL_COMPONENTS Gui Quick) add_qtc_plugin(EffectMakerNew - CONDITION TARGET QmlDesigner PLUGIN_DEPENDS - Qt::Core QtCreator::QmlDesigner QtCreator::ProjectExplorer QtCreator::QmlProjectManager + QtCreator::QmlDesigner QtCreator::ProjectExplorer QtCreator::QmlProjectManager DEPENDS Qt::Core QtCreator::Utils Qt::CorePrivate Qt::Widgets Qt::Qml Qt::QmlPrivate Qt::Quick diff --git a/src/plugins/effectmakernew/EffectMakerNew.json.in b/src/plugins/effectmakernew/EffectMakerNew.json.in index 7846ea6dc98..555876364b8 100644 --- a/src/plugins/effectmakernew/EffectMakerNew.json.in +++ b/src/plugins/effectmakernew/EffectMakerNew.json.in @@ -1,15 +1,15 @@ { - \"Name\" : \"EffectMakerNew\", - \"Version\" : \"${IDE_VERSION}\", - \"CompatVersion\" : \"${IDE_VERSION_COMPAT}\", - \"Vendor\" : \"The Qt Company Ltd\", - \"Copyright\" : \"(C) $$QTCREATOR_COPYRIGHT_YEAR The Qt Company Ltd\", - \"License\" : [ \"Commercial Usage\", - \"\", - \"Licensees holding valid Qt Enterprise licenses may use this plugin in accordance with the Qt Enterprise License Agreement provided with the Software or, alternatively, in accordance with the terms contained in a written agreement between you and The Qt Company.\" + "Name" : "EffectMakerNew", + "Version" : "${IDE_VERSION}", + "CompatVersion" : "${IDE_VERSION_COMPAT}", + "Vendor" : "The Qt Company Ltd", + "Copyright" : "(C) $$QTCREATOR_COPYRIGHT_YEAR The Qt Company Ltd", + "License" : [ "Commercial Usage", + "", + "Licensees holding valid Qt Enterprise licenses may use this plugin in accordance with the Qt Enterprise License Agreement provided with the Software or, alternatively, in accordance with the terms contained in a written agreement between you and The Qt Company." ], - \"Description\" : \"Plugin for Effect Maker.\", - \"Url\" : \"http://www.qt.io\", + "Description" : "Plugin for Effect Maker.", + "Url" : "http://www.qt.io", ${IDE_PLUGIN_DEPENDENCIES} } diff --git a/src/plugins/effectmakernew/effectmakerwidget.cpp b/src/plugins/effectmakernew/effectmakerwidget.cpp index c26247fef19..983459cffdb 100644 --- a/src/plugins/effectmakernew/effectmakerwidget.cpp +++ b/src/plugins/effectmakernew/effectmakerwidget.cpp @@ -9,13 +9,12 @@ #include "effectmakerview.h" #include "propertyhandler.h" +//#include "qmldesigner/designercore/imagecache/midsizeimagecacheprovider.h" #include "qmldesignerconstants.h" #include "qmldesignerplugin.h" #include "qqmlcontext.h" #include "theme.h" -#include "qmldesigner/components/propertyeditor/assetimageprovider.h" - #include #include @@ -140,9 +139,10 @@ void EffectMakerWidget::initView() m_quickWidget->rootContext()->setContextProperty("modelNodeBackend", &m_backendModelNode); m_quickWidget->rootContext()->setContextProperty("activeDragSuffix", ""); - m_quickWidget->engine()->addImageProvider("qmldesigner_thumbnails", - new QmlDesigner::AssetImageProvider( - QmlDesigner::QmlDesignerPlugin::imageCache())); + //TODO: Fix crash on macos +// m_quickWidget->engine()->addImageProvider("qmldesigner_thumbnails", +// new QmlDesigner::AssetImageProvider( +// QmlDesigner::QmlDesignerPlugin::imageCache())); // init the first load of the QML UI elements reloadQmlSource(); From 0461af4b90b50224f07e17676c38ce42d8529ac2 Mon Sep 17 00:00:00 2001 From: Amr Essam Date: Mon, 6 Nov 2023 11:26:27 +0200 Subject: [PATCH 159/242] QmlDesigner: Fix texture not updating effect Task-number: QDS-11064 Change-Id: I39f9baa306af1ccd951f6ab3a4cc5c1b2a5e29f2 Reviewed-by: Mahmoud Badri Reviewed-by: Qt CI Patch Build Bot --- src/plugins/effectmakernew/effectmakermodel.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/effectmakernew/effectmakermodel.cpp b/src/plugins/effectmakernew/effectmakermodel.cpp index 16f7effb7c1..5a49dcb59ce 100644 --- a/src/plugins/effectmakernew/effectmakermodel.cpp +++ b/src/plugins/effectmakernew/effectmakermodel.cpp @@ -944,7 +944,7 @@ QString EffectMakerModel::getQmlImagesString(bool localFiles) imagePath = fi.fileName(); } if (m_loadComponentImages) - imagesString += QString(" source: \"%1\"\n").arg(imagePath); + imagesString += QString(" source: g_propertyData.%1\n").arg(uniform->name()); if (!localFiles) { QString mipmapProperty = mipmapPropertyName(uniform->name()); imagesString += QString(" mipmap: g_propertyData.%1\n").arg(mipmapProperty); From a46abe518f203aaba629d00f80cc87ae23b8f48a Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Mon, 6 Nov 2023 10:35:21 +0200 Subject: [PATCH 160/242] QmlDesigner: Polish the user interface Task-number: QDS-10621 Change-Id: I6d045f95d18778928ce10efa367460f49f4ddfc4 Reviewed-by: Miikka Heikkinen Reviewed-by: Mahmoud Badri Reviewed-by: Qt CI Patch Build Bot --- .../CollectionDetailsToolbar.qml | 8 --- .../CollectionDetailsView.qml | 54 +++++++++++-------- .../CollectionItem.qml | 4 +- .../CollectionView.qml | 10 +++- .../EditPropertyDialog.qml | 6 ++- 5 files changed, 47 insertions(+), 35 deletions(-) diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsToolbar.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsToolbar.qml index 7930a4a444c..8f8a0b24416 100644 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsToolbar.qml +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsToolbar.qml @@ -160,14 +160,6 @@ Item { } } - Rectangle { - anchors.fill: parent - z: -1 - color: "grey" - opacity: 0.6 - radius: 5 - } - component IconButton: HelperWidgets.IconButton { height: root.iconHeight width: root.iconHeight diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsView.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsView.qml index 9bd6d23f63c..26ba2fe87a2 100644 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsView.qml +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsView.qml @@ -18,7 +18,7 @@ Rectangle { implicitWidth: 300 implicitHeight: 400 - color: StudioTheme.Values.themeBackgroundColorAlternate + color: StudioTheme.Values.themeControlBackground ColumnLayout { id: topRow @@ -46,12 +46,6 @@ Rectangle { text: root.model.collectionName font.pixelSize: StudioTheme.Values.mediumIconFont elide: Text.ElideRight - - Rectangle { - anchors.fill: parent - z: parent.z - 1 - color: StudioTheme.Values.themeBackgroundColorNormal - } } Item { // spacer @@ -64,6 +58,7 @@ Rectangle { model: root.model backend: root.backend Layout.fillWidth: true + Layout.minimumWidth: implicitWidth } Item { // spacer @@ -78,12 +73,13 @@ Rectangle { Layout.fillWidth: true Layout.fillHeight: true + Layout.maximumWidth: parent.width Rectangle { clip: true visible: !tableView.model.isEmpty - color: StudioTheme.Values.themeControlBackground - border.color: StudioTheme.Values.themeControlOutline + color: StudioTheme.Values.themeControlBackgroundInteraction + border.color: StudioTheme.Values.themeControlBackgroundInteraction border.width: 2 Layout.preferredWidth: rowIdView.width @@ -113,7 +109,14 @@ Rectangle { clip: true delegate: HeaderDelegate { + id: horizontalHeaderItem + selectedItem: tableView.model.selectedColumn + color: StudioTheme.Values.themeControlBackgroundInteraction + + function getGlobalBottomLeft() { + return mapToGlobal(0, horizontalHeaderItem.height) + } MouseArea { anchors.fill: parent @@ -123,7 +126,7 @@ Rectangle { tableView.model.selectColumn(index) if (mouse.button === Qt.RightButton) - headerMenu.popIndex(index) + headerMenu.popIndex(index, horizontalHeaderItem.getGlobalBottomLeft()) } } } @@ -132,10 +135,12 @@ Rectangle { id: headerMenu property int clickedHeader: -1 + property point initialPosition - function popIndex(clickedIndex) + function popIndex(clickedIndex, clickedRect) { headerMenu.clickedHeader = clickedIndex + headerMenu.initialPosition = clickedRect headerMenu.popup() } @@ -145,7 +150,7 @@ Rectangle { StudioControls.MenuItem { text: qsTr("Edit") - onTriggered: editProperyDialog.editProperty(headerMenu.clickedHeader) + onTriggered: editProperyDialog.editProperty(headerMenu.clickedHeader, headerMenu.initialPosition) } StudioControls.MenuItem { @@ -177,6 +182,7 @@ Rectangle { delegate: HeaderDelegate { selectedItem: tableView.model.selectedRow + color: StudioTheme.Values.themeControlBackgroundHover MouseArea { anchors.fill: parent @@ -197,13 +203,13 @@ Rectangle { Layout.preferredHeight: tableView.contentHeight Layout.minimumWidth: 10 Layout.minimumHeight: 10 + Layout.maximumWidth: root.width delegate: Rectangle { id: itemCell implicitWidth: 100 implicitHeight: itemText.height border.width: 1 - border.color: StudioTheme.Values.themeControlOutline Text { id: itemText @@ -235,11 +241,12 @@ Rectangle { PropertyChanges { target: itemCell color: StudioTheme.Values.themeControlBackground + border.color: StudioTheme.Values.themeControlBackgroundInteraction } PropertyChanges { target: itemText - color: StudioTheme.Values.themeTextColor + color: StudioTheme.Values.themePlaceholderTextColorInteraction } }, State { @@ -249,6 +256,7 @@ Rectangle { PropertyChanges { target: itemCell color: StudioTheme.Values.themeControlBackgroundInteraction + border.color: StudioTheme.Values.themeControlBackground } PropertyChanges { @@ -322,10 +330,9 @@ Rectangle { property alias horizontalAlignment: headerText.horizontalAlignment property alias verticalAlignment: headerText.verticalAlignment - implicitWidth: headerText.width - implicitHeight: headerText.height - border.width: 2 - border.color: StudioTheme.Values.themeControlOutline + implicitWidth: headerText.implicitWidth + implicitHeight: headerText.implicitHeight + border.width: 1 clip: true Text { @@ -337,9 +344,10 @@ Rectangle { rightPadding: 5 text: display font: headerTextMetrics.font + color: StudioTheme.Values.themeTextColor horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter - anchors.centerIn: parent + anchors.fill: parent elide: Text.ElideRight } @@ -349,12 +357,12 @@ Rectangle { when: index !== selectedItem PropertyChanges { target: headerItem - color: StudioTheme.Values.themeControlBackground + border.color: StudioTheme.Values.themeControlBackgroundInteraction } PropertyChanges { target: headerText - color: StudioTheme.Values.themeIdleGreen + font.bold: false } }, State { @@ -363,12 +371,12 @@ Rectangle { PropertyChanges { target: headerItem - color: StudioTheme.Values.themeControlBackgroundInteraction + border.color: StudioTheme.Values.themeControlBackground } PropertyChanges { target: headerText - color: StudioTheme.Values.themeRunningGreen + font.bold: true } } ] diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionItem.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionItem.qml index 5ce42d0a901..c7a9a105f90 100644 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionItem.qml +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionItem.qml @@ -281,12 +281,12 @@ Item { PropertyChanges { target: innerRect opacity: 1 - color: StudioTheme.Values.themeControlBackgroundInteraction + color: StudioTheme.Values.themeIconColorSelected } PropertyChanges { target: root - textColor: StudioTheme.Values.themeIconColorSelected + textColor: StudioTheme.Values.themeTextSelectedTextColor } } ] diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionView.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionView.qml index 4903913b7c0..e58e5d92541 100644 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionView.qml +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionView.qml @@ -58,7 +58,7 @@ Item { readonly property bool isHorizontal: width >= 500 anchors.fill: parent - columns: isHorizontal ? 2 : 1 + columns: isHorizontal ? 3 : 1 ColumnLayout { id: collectionsSideBar @@ -148,6 +148,14 @@ Item { } } + Rectangle { // Splitter + Layout.fillWidth: !grid.isHorizontal + Layout.fillHeight: grid.isHorizontal + Layout.minimumWidth: 2 + Layout.minimumHeight: 2 + color: "black" + } + CollectionDetailsView { model: root.collectionDetailsModel backend: root.model diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/EditPropertyDialog.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/EditPropertyDialog.qml index 173e1a00b61..15a6f20708d 100644 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/EditPropertyDialog.qml +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/EditPropertyDialog.qml @@ -14,7 +14,7 @@ StudioControls.Dialog { title: qsTr("Edit Property") - function editProperty(index) { + function editProperty(index, initialPosition) { root.__propertyIndex = index if (root.__propertyIndex < 0) @@ -30,6 +30,10 @@ StudioControls.Dialog { forceChangeType.checked = false + let newPoint = mapFromGlobal(initialPosition.x, initialPosition.y) + x = newPoint.x + y = newPoint.y + root.open() } From f36597e1a15ac7549e3e3233be1d6c808f5b90d4 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Fri, 3 Nov 2023 15:57:21 +0100 Subject: [PATCH 161/242] QmlDesigner: Load fonts in qml runtime Ensure all fonts in the project are always loaded, even if e.g. the project singleton is not created. We search for the .qmlproject file and then load all fonts. Task-number: QDS-11108 Change-Id: I1117fa6ba9677c75fe8fce96bb349b5e825af7f3 Reviewed-by: Burak Hancerli Reviewed-by: Thomas Hartmann --- .../qml2puppet/runner/qmlruntime.cpp | 35 +++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/src/tools/qml2puppet/qml2puppet/runner/qmlruntime.cpp b/src/tools/qml2puppet/qml2puppet/runner/qmlruntime.cpp index a4ade2b2b44..c1817e934d5 100644 --- a/src/tools/qml2puppet/qml2puppet/runner/qmlruntime.cpp +++ b/src/tools/qml2puppet/qml2puppet/runner/qmlruntime.cpp @@ -1,8 +1,6 @@ // Copyright (C) 2022 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 -#include - #include "loadwatcher.h" #include "qmlruntime.h" @@ -12,9 +10,41 @@ #include #endif +#include +#include +#include + #define FILE_OPEN_EVENT_WAIT_TIME 3000 // ms #define QSL QStringLiteral +static void registerFonts(const QDir &projectDir) +{ + // Autoregister all fonts found inside the project + QDirIterator it{projectDir.absolutePath(), + {"*.ttf", "*.otf"}, + QDir::Files, + QDirIterator::Subdirectories}; + while (it.hasNext()) { + QFontDatabase::addApplicationFont(it.next()); + } +} + +static QDir findProjectFolder(const QDir ¤tDir, int ret = 0) +{ + if (ret > 2) + QDir::current(); + + QDirIterator it{currentDir.absolutePath(), + {"*.qmlproject"}, + QDir::Files, + QDirIterator::NoIteratorFlags}; + while (it.hasNext()) + return currentDir; + QDir newDir = currentDir; + newDir.cdUp(); + return findProjectFolder(newDir, ret + 1); +} + void QmlRuntime::populateParser() { m_argParser.setSingleDashWordOptionMode(QCommandLineParser::ParseAsLongOptions); @@ -127,6 +157,7 @@ void QmlRuntime::initCoreApp() void QmlRuntime::initQmlRunner() { + registerFonts(findProjectFolder(QDir::current())); m_qmlEngine.reset(new QQmlApplicationEngine()); QStringList files; From bf0d95ddb3824c3adea8e3d729b90b3a49cdbde2 Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Mon, 6 Nov 2023 13:27:47 +0200 Subject: [PATCH 162/242] QmlDesigner: Force adding id when create a collection source model Task-number: QDS-11112 Change-Id: I239946d351addbdde3990168666ece7a5b4029e8 Reviewed-by: Mahmoud Badri --- .../collectionEditorQmlSource/CsvImport.qml | 2 +- .../collectioneditor/collectionview.cpp | 1 + .../collectioneditor/collectionwidget.cpp | 36 ++++++++++++------- .../collectioneditor/collectionwidget.h | 4 +-- 4 files changed, 27 insertions(+), 16 deletions(-) diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CsvImport.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CsvImport.qml index 5323d6759e9..f58150b3b57 100644 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CsvImport.qml +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CsvImport.qml @@ -169,7 +169,7 @@ StudioControls.Dialog { enabled: root.fileExists && collectionName.text !== "" onClicked: { - let csvLoaded = root.backendValue.loadCsvFile(collectionName.text, fileName.text) + let csvLoaded = root.backendValue.loadCsvFile(fileName.text, collectionName.text) if (csvLoaded) root.accept() diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp index c3ab244177a..854d2782084 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp @@ -149,6 +149,7 @@ void CollectionView::addResource(const QUrl &url, const QString &name, const QSt VariantProperty nameProperty = resourceNode.variantProperty("objectName"); sourceProperty.setValue(sourceAddress); nameProperty.setValue(name); + resourceNode.setIdWithoutRefactoring(model()->generateIdFromName(name, "collection")); rootModelNode().defaultNodeAbstractProperty().reparentHere(resourceNode); }); } diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp index e956f5a2932..8e36827580f 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp @@ -37,7 +37,7 @@ QString collectionViewResourcesPath() return Core::ICore::resourcePath("qmldesigner/collectionEditorQmlSource").toString(); } -static QString urlToLocalPath(const QUrl &url) +QString urlToLocalPath(const QUrl &url) { QString localPath; @@ -52,6 +52,16 @@ static QString urlToLocalPath(const QUrl &url) return localPath; } +QString getPreferredCollectionName(const QUrl &url, const QString &collectionName) +{ + if (collectionName.isEmpty()) { + QFileInfo fileInfo(url.isLocalFile() ? url.toLocalFile() : url.toString()); + return fileInfo.completeBaseName(); + } + + return collectionName; +} + } // namespace namespace QmlDesigner { @@ -136,23 +146,23 @@ QSize CollectionWidget::minimumSizeHint() const return {300, 400}; } -bool CollectionWidget::loadJsonFile(const QString &jsonFileAddress) +bool CollectionWidget::loadJsonFile(const QString &jsonFileAddress, const QString &collectionName) { if (!isJsonFile(jsonFileAddress)) return false; - QUrl jsonUrl(jsonFileAddress); - QFileInfo fileInfo(jsonUrl.isLocalFile() ? jsonUrl.toLocalFile() : jsonUrl.toString()); - - m_view->addResource(jsonUrl, fileInfo.completeBaseName(), "json"); + m_view->addResource(jsonFileAddress, + getPreferredCollectionName(jsonFileAddress, collectionName), + "json"); return true; } -bool CollectionWidget::loadCsvFile(const QString &collectionName, const QString &csvFileAddress) +bool CollectionWidget::loadCsvFile(const QString &csvFileAddress, const QString &collectionName) { - QUrl csvUrl(csvFileAddress); - m_view->addResource(csvUrl, collectionName, "csv"); + m_view->addResource(csvFileAddress, + getPreferredCollectionName(csvFileAddress, collectionName), + "csv"); return true; } @@ -211,7 +221,7 @@ bool CollectionWidget::addCollection(const QString &collectionName, sourceFile.write(QJsonDocument(jsonObject).toJson()); sourceFile.close(); - bool loaded = loadJsonFile(sourcePath); + bool loaded = loadJsonFile(sourcePath, collectionName); if (!loaded) sourceFile.remove(); @@ -226,7 +236,7 @@ bool CollectionWidget::addCollection(const QString &collectionName, sourceFile.close(); - bool loaded = loadCsvFile(collectionName, sourcePath); + bool loaded = loadCsvFile(sourcePath, collectionName); if (!loaded) sourceFile.remove(); @@ -234,9 +244,9 @@ bool CollectionWidget::addCollection(const QString &collectionName, } else if (collectionType == "existing") { QFileInfo fileInfo(sourcePath); if (fileInfo.suffix() == "json") - return loadJsonFile(sourcePath); + return loadJsonFile(sourcePath, collectionName); else if (fileInfo.suffix() == "csv") - return loadCsvFile(collectionName, sourcePath); + return loadCsvFile(sourcePath, collectionName); } } else if (collectionType == "json") { QString errorMsg; diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.h b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.h index f902c86c226..3b8925bd93d 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.h +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.h @@ -32,8 +32,8 @@ public: virtual QSize minimumSizeHint() const; - Q_INVOKABLE bool loadJsonFile(const QString &jsonFileAddress); - Q_INVOKABLE bool loadCsvFile(const QString &collectionName, const QString &csvFileAddress); + Q_INVOKABLE bool loadJsonFile(const QString &jsonFileAddress, const QString &collectionName = {}); + Q_INVOKABLE bool loadCsvFile(const QString &csvFileAddress, const QString &collectionName = {}); Q_INVOKABLE bool isJsonFile(const QString &jsonFileAddress) const; Q_INVOKABLE bool isCsvFile(const QString &csvFileAddress) const; Q_INVOKABLE bool addCollection(const QString &collectionName, From 2b396828896f3ab95267848386265208ce6e14e1 Mon Sep 17 00:00:00 2001 From: Amr Essam Date: Mon, 6 Nov 2023 14:41:31 +0200 Subject: [PATCH 163/242] QmlDesigner: Add effect maker animation support Task-number: QDS-11145 Change-Id: I59c68199a5616c095e9cac06a53fbc6a0635d6a8 Reviewed-by: Mahmoud Badri --- .../effectMakerQmlSources/EffectMaker.qml | 3 ++- .../EffectMakerPreview.qml | 23 ++++++++++++------- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMaker.qml b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMaker.qml index c2c34cd8855..b11c1cce796 100644 --- a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMaker.qml +++ b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMaker.qml @@ -14,6 +14,7 @@ Item { property var secsY: [] property int moveFromIdx: 0 property int moveToIdx: 0 + property bool previewAnimationRunning: false SaveDialog { id: saveDialog @@ -36,7 +37,7 @@ Item { FrameAnimation { id: previewFrameTimer running: true - paused: false // TODO: from the toolbar + paused: !previewAnimationRunning } } diff --git a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMakerPreview.qml b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMakerPreview.qml index 0e686964971..bf5fc4a154a 100644 --- a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMakerPreview.qml +++ b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMakerPreview.qml @@ -12,8 +12,9 @@ import EffectMakerBackend Column { id: root - property real animatedTime: 0 //TODO get from animator - property int animatedFrame: 0 //TODO get from animator + property real animatedTime: previewFrameTimer.elapsedTime + property int animatedFrame: previewFrameTimer.currentFrame + property bool timeRunning: previewAnimationRunning width: parent.width @@ -108,13 +109,14 @@ Column { Column { Text { - text: "0.000s" + text: animatedTime >= 100 + ? animatedTime.toFixed(1) + " s" : animatedTime.toFixed(3) + " s" color: StudioTheme.Values.themeTextColor font.pixelSize: 10 } Text { - text: "0000000" + text: (animatedFrame).toString().padStart(6, '0') color: StudioTheme.Values.themeTextColor font.pixelSize: 10 } @@ -125,15 +127,21 @@ Column { buttonIcon: StudioTheme.Constants.toStartFrame_medium tooltip: qsTr("Restart Animation") - onClicked: {} // TODO + onClicked: { + previewFrameTimer.reset() + } } + //TODO: Change the icon to outlined version HelperWidgets.AbstractButton { style: StudioTheme.Values.viewBarButtonStyle - buttonIcon: StudioTheme.Constants.topToolbar_runProject + buttonIcon: previewAnimationRunning + ? StudioTheme.Constants.pause : StudioTheme.Constants.playOutline_medium tooltip: qsTr("Play Animation") - onClicked: {} // TODO + onClicked: { + previewAnimationRunning = !previewAnimationRunning + } } } } @@ -175,7 +183,6 @@ Column { width: source.width height: source.height anchors.centerIn: parent - scale: 1 //TODO should come from toolbar // Cache the layer. This way heavy shaders rendering doesn't // slow down code editing & rest of the UI. layer.enabled: true From 4a9424445aa0b1e6edf37f4ec0a32fc7c8a9dc4b Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Mon, 6 Nov 2023 15:40:25 +0200 Subject: [PATCH 164/242] QmlDesigner: Update 3D viewport shading menu and split highlight Change-Id: Ided78b5e88485ab2bc5500885344cb4fb34c51a5 Reviewed-by: Mahmoud Badri --- .../components/edit3d/edit3dwidget.cpp | 50 +++++++------------ .../qml2puppet/mockfiles/qt6/EditView3D.qml | 2 +- 2 files changed, 20 insertions(+), 32 deletions(-) diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp index 112d4e7eea5..b56986e79df 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp +++ b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp @@ -282,7 +282,7 @@ void Edit3DWidget::createContextMenu() m_contextMenu->addSeparator(); - auto overridesSubMenu = new QmlEditorMenu(tr("Shader Overrides"), m_contextMenu); + auto overridesSubMenu = new QmlEditorMenu(tr("Viewport Shading"), m_contextMenu); overridesSubMenu->setToolTipsVisible(true); m_contextMenu->addMenu(overridesSubMenu); @@ -320,54 +320,42 @@ void Edit3DWidget::createContextMenu() m_matOverrideActions.insert(int(type), action); }; - addOverrideMenuAction(tr("No Material Override"), + addOverrideMenuAction(tr("Default"), tr("Rendering occurs as normal."), MaterialOverrideType::None); addOverrideMenuAction(tr("Base Color"), - tr("The BaseColor or Diffuse color of a material is passed through without any lighting."), + tr("The base or diffuse color of a material is passed through without any lighting."), MaterialOverrideType::BaseColor); addOverrideMenuAction(tr("Roughness"), - tr("The Roughness of a material is passed through as an unlit greyscale value."), + tr("The roughness of a material is passed through as an unlit greyscale value."), MaterialOverrideType::Roughness); addOverrideMenuAction(tr("Metalness"), - tr("The Metalness of a material is passed through as an unlit greyscale value."), + tr("The metalness of a material is passed through as an unlit greyscale value."), MaterialOverrideType::Metalness); + addOverrideMenuAction(tr("Normals"), + tr("The interpolated world space normal value of the material mapped to an RGB color."), + MaterialOverrideType::Normals); + addOverrideMenuAction(tr("Ambient Occlusion"), + tr("Only the ambient occlusion of the material."), + MaterialOverrideType::AmbientOcclusion); + addOverrideMenuAction(tr("Emission"), + tr("Only the emissive contribution of the material."), + MaterialOverrideType::Emission); + addOverrideMenuAction(tr("Shadow Occlusion"), + tr("The occlusion caused by shadows as a greyscale value."), + MaterialOverrideType::ShadowOcclusion); addOverrideMenuAction(tr("Diffuse"), tr("Only the diffuse contribution of the material after all lighting."), MaterialOverrideType::Diffuse); addOverrideMenuAction(tr("Specular"), tr("Only the specular contribution of the material after all lighting."), MaterialOverrideType::Specular); - addOverrideMenuAction(tr("Shadow Occlusion"), - tr("The Occlusion caused by shadows as a greyscale value."), - MaterialOverrideType::ShadowOcclusion); - addOverrideMenuAction(tr("Emission"), - tr("Only the emissive contribution of the material."), - MaterialOverrideType::Emission); - addOverrideMenuAction(tr("Ambient Occlusion"), - tr("Only the Ambient Occlusion of the material."), - MaterialOverrideType::AmbientOcclusion); - addOverrideMenuAction(tr("Normals"), - tr("The interpolated world space Normal value of the material mapped to an RGB color."), - MaterialOverrideType::Normals); - addOverrideMenuAction(tr("Tangents"), - tr("The interpolated world space Tangent value of the material mapped to an RGB color.\n" - "This will only be visible if the Tangent value is used."), - MaterialOverrideType::Tangents); - addOverrideMenuAction(tr("Binormals"), - tr("The interpolated world space Binormal value of the material mapped to an RGB color.\n" - "This will only be visible if the Binormal value is used."), - MaterialOverrideType::Binormals); - addOverrideMenuAction(tr("Fresnel 0"), - tr("This represents the Fresnel Reflectance at 0 Degrees.\n" - "This will only be visible for materials that calculate an F0 value."), - MaterialOverrideType::F0); overridesSubMenu->addSeparator(); QAction *resetAction = overridesSubMenu->addAction( - tr("Reset All Overrides"), this, &Edit3DWidget::onResetAllOverridesAction); - resetAction->setToolTip(tr("Reset all overrides for all splits.")); + tr("Reset All Viewports"), this, &Edit3DWidget::onResetAllOverridesAction); + resetAction->setToolTip(tr("Reset all shading options for all viewports.")); m_contextMenu->addSeparator(); } diff --git a/src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml b/src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml index ec10d481e1d..f526f01e2db 100644 --- a/src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml +++ b/src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml @@ -804,7 +804,7 @@ Item { + (viewRoot.activeSplit === 0 || viewRoot.activeSplit === 1 ? 1 : 0) width: viewRects[viewRoot.activeSplit].width + (viewRoot.activeSplit === 0 || viewRoot.activeSplit === 2 ? 1 : 0) - border.width: 1 + border.width: 2 border.color: "#57B9FC" color: "transparent" } From e10e85cd17dc2557017ccfd2add4a241efa5f873 Mon Sep 17 00:00:00 2001 From: Pranta Dastider Date: Fri, 3 Nov 2023 17:09:53 +0100 Subject: [PATCH 165/242] QmlDesigner: Update Washing Machine UI Doc for MCU setup info This patch updates the Washing Machine UI document. Updates the old instructions for setting up a new MCU project. Fixes: QDS-11058 Change-Id: Ib1295966cff7e7a6ea2a5051cce93a1e6596a30b Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Leena Miettinen --- .../examples/doc/washingMachineUI.qdoc | 21 +++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/doc/qtdesignstudio/examples/doc/washingMachineUI.qdoc b/doc/qtdesignstudio/examples/doc/washingMachineUI.qdoc index 29526ed0c26..634ea666e06 100644 --- a/doc/qtdesignstudio/examples/doc/washingMachineUI.qdoc +++ b/doc/qtdesignstudio/examples/doc/washingMachineUI.qdoc @@ -40,10 +40,23 @@ We use the \uicontrol {\QMCU Application} project template to create an application for MCUs, which support only a subset of the preset - \l{glossary-component}{components}. We select \uicontrol File > - \uicontrol {New Project} > \uicontrol {\QMCU Application} > - \uicontrol Choose, and follow the instructions of the wizard to create our - project. + \l{glossary-component}{components}. + + To create an MCU project: + + \list 1 + \li Select \uicontrol {File} > \uicontrol {New Project}. + \li In the \uicontrol {Presets} tab, select the \uicontrol {\QMCU} preset. + \li In the \uicontrol {Details} tab: + \list + \li Select the path for the project files. You can move the project + folders later. + \li Set the screen size to match the device screen, which also enables + previewing on the desktop. You can change the screen size later in + \l {Properties}. + \endlist + \li Select \uicontrol {Create} to create the project. + \endlist This way, only the components and properties supported on MCUs are visible in \l Components and \l Properties, and we won't accidentally From c524cf62e1135f0e6cbb8329ea8d038ed3a028d7 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Mon, 6 Nov 2023 16:33:59 +0100 Subject: [PATCH 166/242] QmlDesigner: Update item library if QmlJS library info is updated Change-Id: I12b3aeebda4440c33d6c832f0c6faeba1ee71153 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Miikka Heikkinen --- .../components/itemlibrary/itemlibraryview.cpp | 13 +++++++++---- .../designercore/include/customnotifications.h | 5 ++--- .../qmldesigner/designercore/model/rewriterview.cpp | 2 ++ 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp index a6adc0d4f7d..feff523b9c6 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -16,12 +17,12 @@ #include #include #include -#include -#include -#include #include #include #include +#include +#include +#include namespace QmlDesigner { @@ -197,8 +198,12 @@ void ItemLibraryView::customNotification(const AbstractView *view, const QString const QList &nodeList, const QList &data) { if (identifier == "UpdateImported3DAsset" && nodeList.size() > 0) { - ItemLibraryAssetImportDialog::updateImport(nodeList[0], m_importableExtensions3DMap, + ItemLibraryAssetImportDialog::updateImport(nodeList[0], + m_importableExtensions3DMap, m_importOptions3DMap); + + } else if (identifier == UpdateItemlibrary) { + updateImports(); } else { AbstractView::customNotification(view, identifier, nodeList, data); } diff --git a/src/plugins/qmldesigner/designercore/include/customnotifications.h b/src/plugins/qmldesigner/designercore/include/customnotifications.h index d7a15b293fa..1095a178402 100644 --- a/src/plugins/qmldesigner/designercore/include/customnotifications.h +++ b/src/plugins/qmldesigner/designercore/include/customnotifications.h @@ -11,6 +11,5 @@ const QString StartRewriterAmend = QStringLiteral("__start rewriter amend__"); const QString EndRewriterAmend = QStringLiteral("__end rewriter amend__"); const QString StartRewriterApply = QStringLiteral("start rewriter apply__"); const QString EndRewriterApply = QStringLiteral("__end rewriter apply__"); - - -} +const QString UpdateItemlibrary = QStringLiteral("__update itemlibrary__"); +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/model/rewriterview.cpp b/src/plugins/qmldesigner/designercore/model/rewriterview.cpp index 68b6426b8fa..4ae2261e604 100644 --- a/src/plugins/qmldesigner/designercore/model/rewriterview.cpp +++ b/src/plugins/qmldesigner/designercore/model/rewriterview.cpp @@ -892,6 +892,8 @@ void RewriterView::handleLibraryInfoUpdate() if (isAttached() && !m_modelAttachPending && !debugQmlPuppet(externalDependencies().designerSettings())) m_amendTimer.start(); + + emitCustomNotification(UpdateItemlibrary); } void RewriterView::handleProjectUpdate() From d92043e69c7fd42183ba82a103e4b5eaef341667 Mon Sep 17 00:00:00 2001 From: Burak Hancerli Date: Mon, 16 Oct 2023 14:22:53 +0200 Subject: [PATCH 167/242] ProjectExplorer: Overlay the original icon for missing files QmlProject could be edited manually to add files into the project. In case of the files are mising in the file system (or if there's a typo in the file path) file names still shown in the project explorer without any information. This patch brings a small overlay on top of the original icon to notify the users that the file doesn't exist. Task-number: QDS-10344 Change-Id: Ia73699b048725bcc70a1ee0f52c34b55b081a779 Reviewed-by: Alessandro Portale Reviewed-by: Qt CI Patch Build Bot --- src/plugins/projectexplorer/project.h | 3 ++- src/plugins/projectexplorer/projectmodels.cpp | 4 +++- src/plugins/projectexplorer/projectnodes.cpp | 11 +++++++++++ src/plugins/projectexplorer/projectnodes.h | 4 ++++ src/plugins/projectexplorer/projecttreewidget.cpp | 12 +++++++++++- 5 files changed, 31 insertions(+), 3 deletions(-) diff --git a/src/plugins/projectexplorer/project.h b/src/plugins/projectexplorer/project.h index 40b26d6faa0..e1bf1eb7b02 100644 --- a/src/plugins/projectexplorer/project.h +++ b/src/plugins/projectexplorer/project.h @@ -49,7 +49,8 @@ public: enum ModelRoles { // Absolute file path FilePathRole = QFileSystemModel::FilePathRole, - isParsingRole + isParsingRole, + UseUnavailableMarkerRole, }; Project(const QString &mimeType, const Utils::FilePath &fileName); diff --git a/src/plugins/projectexplorer/projectmodels.cpp b/src/plugins/projectexplorer/projectmodels.cpp index 0af76f1bbef..f824a19cc91 100644 --- a/src/plugins/projectexplorer/projectmodels.cpp +++ b/src/plugins/projectexplorer/projectmodels.cpp @@ -193,6 +193,7 @@ QVariant FlatModel::data(const QModelIndex &index, int role) const return {}; const FolderNode * const folderNode = node->asFolderNode(); + const FileNode * const fileNode = node->asFileNode(); const ContainerNode * const containerNode = node->asContainerNode(); const Project * const project = containerNode ? containerNode->project() : nullptr; const Target * const target = project ? project->activeTarget() : nullptr; @@ -246,6 +247,8 @@ QVariant FlatModel::data(const QModelIndex &index, int role) const return node->filePath().toString(); case Project::isParsingRole: return project && bs ? bs->isParsing() && !project->needsConfiguration() : false; + case Project::UseUnavailableMarkerRole: + return fileNode ? fileNode->useUnavailableMarker() : false; } return {}; } @@ -923,4 +926,3 @@ const QLoggingCategory &FlatModel::logger() } // namespace Internal } // namespace ProjectExplorer - diff --git a/src/plugins/projectexplorer/projectnodes.cpp b/src/plugins/projectexplorer/projectnodes.cpp index 11ab5fe0561..e1edbd7d930 100644 --- a/src/plugins/projectexplorer/projectnodes.cpp +++ b/src/plugins/projectexplorer/projectnodes.cpp @@ -260,6 +260,16 @@ void FileNode::setHasError(bool error) const m_hasError = error; } +bool FileNode::useUnavailableMarker() const +{ + return m_useUnavailableMarker; +} + +void FileNode::setUseUnavailableMarker(bool useUnavailableMarker) +{ + m_useUnavailableMarker = useUnavailableMarker; +} + /*! Returns \c true if the file is automatically generated by a compile step. */ @@ -382,6 +392,7 @@ FileNode::FileNode(const Utils::FilePath &filePath, const FileType fileType) : m_fileType(fileType) { setFilePath(filePath); + setUseUnavailableMarker(!filePath.needsDevice() && !filePath.exists()); setListInProject(true); if (fileType == FileType::Project) setPriority(DefaultProjectFilePriority); diff --git a/src/plugins/projectexplorer/projectnodes.h b/src/plugins/projectexplorer/projectnodes.h index 5e424b62bbc..9d159aeea35 100644 --- a/src/plugins/projectexplorer/projectnodes.h +++ b/src/plugins/projectexplorer/projectnodes.h @@ -204,10 +204,14 @@ public: QIcon icon() const; void setIcon(const QIcon icon); + bool useUnavailableMarker() const; + void setUseUnavailableMarker(bool useUnavailableMarker); + private: FileType m_fileType; mutable QIcon m_icon; mutable bool m_hasError = false; + bool m_useUnavailableMarker = false; }; // Documentation inside. diff --git a/src/plugins/projectexplorer/projecttreewidget.cpp b/src/plugins/projectexplorer/projecttreewidget.cpp index cb505a43a7c..a173b77c22a 100644 --- a/src/plugins/projectexplorer/projecttreewidget.cpp +++ b/src/plugins/projectexplorer/projecttreewidget.cpp @@ -72,8 +72,18 @@ public: void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override { - QStyledItemDelegate::paint(painter, option, index); + const bool useUnavailableMarker = index.data(Project::UseUnavailableMarkerRole).toBool(); + if (useUnavailableMarker) { + QStyleOptionViewItem opt = option; + opt.palette.setColor(QPalette::Text, creatorTheme()->color(Theme::TextColorDisabled)); + QStyledItemDelegate::paint(painter, opt, index); + static const QPixmap pixmap + = QApplication::style()->standardIcon(QStyle::SP_BrowserStop).pixmap(10); + painter->drawPixmap(option.rect.topLeft(), pixmap); + return; + } + QStyledItemDelegate::paint(painter, option, index); if (index.data(Project::isParsingRole).toBool()) { QStyleOptionViewItem opt = option; initStyleOption(&opt, index); From 610394cefbfffa12bc8f4bc941fac480e4683b99 Mon Sep 17 00:00:00 2001 From: Vikas Pachdha Date: Fri, 3 Nov 2023 14:39:16 +0100 Subject: [PATCH 168/242] Designer: Merge QtQuick.State nodes while style merging Fixes: QDS-11107 Change-Id: I676e48d584b233e9d86edc5ec805b79b0965f0eb Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Reviewed-by: Qt CI Bot Reviewed-by: Thomas Hartmann --- .../designercore/include/stylesheetmerger.h | 2 + .../designercore/model/stylesheetmerger.cpp | 77 +++++++++++++++++++ 2 files changed, 79 insertions(+) diff --git a/src/plugins/qmldesigner/designercore/include/stylesheetmerger.h b/src/plugins/qmldesigner/designercore/include/stylesheetmerger.h index 288dbc1082e..d4f387ad356 100644 --- a/src/plugins/qmldesigner/designercore/include/stylesheetmerger.h +++ b/src/plugins/qmldesigner/designercore/include/stylesheetmerger.h @@ -50,7 +50,9 @@ private: void syncBindingProperties(ModelNode &outputNode, const ModelNode &inputNode); void syncAuxiliaryProperties(ModelNode &outputNode, const ModelNode &inputNode); void syncVariantProperties(ModelNode &outputNode, const ModelNode &inputNode); + void syncStateNode(ModelNode &outputState, const ModelNode &inputState) const; void parseTemplateOptions(); + void mergeStates(ModelNode &outputNode, const ModelNode &inputNode) const; AbstractView *m_templateView; AbstractView *m_styleView; diff --git a/src/plugins/qmldesigner/designercore/model/stylesheetmerger.cpp b/src/plugins/qmldesigner/designercore/model/stylesheetmerger.cpp index 90891cbc8c1..c4b4eb8c09c 100644 --- a/src/plugins/qmldesigner/designercore/model/stylesheetmerger.cpp +++ b/src/plugins/qmldesigner/designercore/model/stylesheetmerger.cpp @@ -182,6 +182,7 @@ ModelNode StylesheetMerger::createReplacementNode(const ModelNode& styleNode, Mo syncId(newNode, modelNode); syncNodeProperties(newNode, modelNode); syncNodeListProperties(newNode, modelNode); + mergeStates(newNode, modelNode); return newNode; } @@ -402,6 +403,80 @@ void StylesheetMerger::parseTemplateOptions() } } +void StylesheetMerger::syncStateNode(ModelNode &outputState, const ModelNode &inputState) const +{ + auto addProperty = [](ModelNode &n, const AbstractProperty &p) { + if (n.hasProperty(p.name())) + return; // Do not ovewrite. Only merge when property not defined in template. + if (p.isBindingProperty()) + n.bindingProperty(p.name()).setExpression(p.toBindingProperty().expression()); + else + n.variantProperty(p.name()).setValue(p.toVariantProperty().value()); + }; + + addProperty(outputState, inputState.property("when")); + addProperty(outputState, inputState.property("extend")); + + auto changeSetKey = [](const ModelNode &n) { + return QString("%1::%2").arg(QString::fromUtf8(n.type()), + n.bindingProperty("target").expression()); + }; + + // Collect change sets already defined in the output state. + std::map outputChangeSets; + for (ModelNode propChange : outputState.directSubModelNodes()) + outputChangeSets.insert({changeSetKey(propChange), propChange}); + + // Merge the child nodes of the states i.e. AnchorChanges, PropertyChanges, etc. + for (ModelNode inputChangeset : inputState.directSubModelNodes()) { + const QString key = changeSetKey(inputChangeset); + const auto itr = outputChangeSets.find(key); + ModelNode changeSet; + if (itr != outputChangeSets.end()) { + changeSet = itr->second; + } else { + const QByteArray typeName = inputChangeset.type(); + NodeMetaInfo metaInfo = m_templateView->model()->metaInfo(typeName); + int major = metaInfo.majorVersion(); + int minor = metaInfo.minorVersion(); + changeSet = m_templateView->createModelNode(typeName, major, minor); + outputState.nodeListProperty("changes").reparentHere(changeSet); + outputChangeSets.insert({key, changeSet}); + } + + for (const auto &p : inputChangeset.properties()) + addProperty(changeSet, p); + } +} + +void StylesheetMerger::mergeStates(ModelNode &outputNode, const ModelNode &inputNode) const +{ + QMap outputStates; + for (auto stateNode : outputNode.nodeListProperty("states").toModelNodeList()) { + const QString name = stateNode.variantProperty("name").value().toString(); + if (name.isEmpty()) + continue; + outputStates[name] = stateNode; + } + + for (auto inputStateNode : inputNode.nodeListProperty("states").toModelNodeList()) { + const QString name = inputStateNode.variantProperty("name").value().toString(); + try { + if (outputStates.contains(name)) { + syncStateNode(outputStates[name], inputStateNode); + continue; + } + ModelMerger merger(m_templateView); + ModelNode stateClone = merger.insertModel(inputStateNode); + if (stateClone.isValid()) + outputNode.nodeListProperty("states").reparentHere(stateClone); + } catch (Exception &e) { + qDebug().noquote() << "Exception while merging states."; + e.createWarning(); + } + } +} + void StylesheetMerger::merge() { ModelNode templateRootNode = m_templateView->rootModelNode(); @@ -422,6 +497,8 @@ void StylesheetMerger::merge() return; } + mergeStates(templateRootNode, styleRootNode); + QQueue replacementNodes; QList directRootSubNodes = styleRootNode.directSubModelNodes(); From 0f861405ce5af1f8206bdbb491196dd1501125b1 Mon Sep 17 00:00:00 2001 From: Brook Cronin Date: Mon, 6 Nov 2023 13:17:45 +0100 Subject: [PATCH 169/242] QmlDesigner: Make Workspace lock button checkedInverted style Change-Id: I5377209713da4f5ed88902432791ccf12b4d6898 Reviewed-by: Thomas Hartmann --- share/qtcreator/qmldesigner/toolbar/Main.qml | 1 + share/qtcreator/qmldesigner/toolbar/ToolbarButton.qml | 1 + 2 files changed, 2 insertions(+) diff --git a/share/qtcreator/qmldesigner/toolbar/Main.qml b/share/qtcreator/qmldesigner/toolbar/Main.qml index 360800a5f70..63b3aef5653 100644 --- a/share/qtcreator/qmldesigner/toolbar/Main.qml +++ b/share/qtcreator/qmldesigner/toolbar/Main.qml @@ -290,6 +290,7 @@ Rectangle { visible: !root.flyoutEnabled checkable: true checked: backend.lockWorkspace + checkedInverted: true onClicked: backend.setLockWorkspace(lockWorkspace.checked) } diff --git a/share/qtcreator/qmldesigner/toolbar/ToolbarButton.qml b/share/qtcreator/qmldesigner/toolbar/ToolbarButton.qml index 706f0f19605..e1d68bd2c54 100644 --- a/share/qtcreator/qmldesigner/toolbar/ToolbarButton.qml +++ b/share/qtcreator/qmldesigner/toolbar/ToolbarButton.qml @@ -10,6 +10,7 @@ StudioControls.AbstractButton { id: button property alias tooltip: toolTipArea.tooltip + property alias checkedInverted: button.checkedInverted style: StudioTheme.Values.toolbarButtonStyle hover: toolTipArea.containsMouse From ca84a2afeb87d2dd363f84934082107791f2836a Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Tue, 7 Nov 2023 10:34:47 +0200 Subject: [PATCH 170/242] QmlDesigner: Apply deleting the collection from source files Task-number: QDS-11150 Change-Id: Ibe1294b64493594f22a03521e9f9dc2462c526c4 Reviewed-by: Mahmoud Badri Reviewed-by: Qt CI Patch Build Bot --- .../CollectionItem.qml | 30 +++-- .../ModelSourceItem.qml | 3 +- .../collectioneditorutils.cpp | 12 ++ .../collectioneditor/collectioneditorutils.h | 2 + .../collectioneditor/collectionlistmodel.cpp | 21 ++++ .../collectioneditor/collectionlistmodel.h | 5 + .../collectionsourcemodel.cpp | 107 +++++++++++++++--- .../collectioneditor/collectionsourcemodel.h | 1 + 8 files changed, 156 insertions(+), 25 deletions(-) diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionItem.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionItem.qml index c7a9a105f90..59857c8e172 100644 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionItem.qml +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionItem.qml @@ -15,6 +15,7 @@ Item { implicitHeight: innerRect.height + 3 property color textColor + property string sourceType signal selectItem(int itemIndex) signal deleteItem() @@ -130,13 +131,28 @@ Item { id: deleteDialog title: qsTr("Deleting whole collection") + clip: true - contentItem: Column { + contentItem: ColumnLayout { spacing: 2 Text { - text: qsTr("Are you sure that you want to delete collection \"" + collectionName + "\"?") + Layout.fillHeight: true + Layout.fillWidth: true + + wrapMode: Text.WordWrap color: StudioTheme.Values.themeTextColor + text: { + if (root.sourceType === "json") { + qsTr("Are you sure that you want to delete collection \"%1\"?" + + "\nYou can't undo this action if you proceed and " + + "the collection will be removed from the json file.").arg(collectionName) + } else if (root.sourceType === "csv") { + qsTr("Are you sure that you want to delete collection \"%1\"?" + + "\nIn this case, the csv file will not be deleted, but " + + "the collection model will be removed from the project.").arg(collectionName) + } + } } Item { // spacer @@ -144,21 +160,17 @@ Item { height: 20 } - Row { - anchors.right: parent.right + RowLayout { spacing: 10 + Layout.alignment: Qt.AlignRight | Qt.AlignVCenter HelperWidgets.Button { - id: btnDelete - anchors.verticalCenter: parent.verticalCenter - text: qsTr("Delete") - onClicked: root.deleteItem(index) + onClicked: root.deleteItem() } HelperWidgets.Button { text: qsTr("Cancel") - anchors.verticalCenter: parent.verticalCenter onClicked: deleteDialog.reject() } } diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/ModelSourceItem.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/ModelSourceItem.qml index 74cc718a6b0..4b5ce18100b 100644 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/ModelSourceItem.qml +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/ModelSourceItem.qml @@ -155,7 +155,8 @@ Item { delegate: CollectionItem { width: collectionListView.width - onDeleteItem: root.model.removeRow(index) + sourceType: collectionListView.model.sourceType + onDeleteItem: collectionListView.model.removeRow(index) } } } diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.cpp index 69e39aedca1..2b54b5ca609 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.cpp @@ -61,4 +61,16 @@ bool variantIslessThan(const QVariant &a, const QVariant &b, CollectionDetails:: return std::visit(LessThanVisitor{}, valueToVariant(a, type), valueToVariant(b, type)); } +QString getSourceCollectionType(const ModelNode &node) +{ + using namespace QmlDesigner; + if (node.type() == CollectionEditor::JSONCOLLECTIONMODEL_TYPENAME) + return "json"; + + if (node.type() == CollectionEditor::CSVCOLLECTIONMODEL_TYPENAME) + return "csv"; + + return {}; +} + } // namespace QmlDesigner::CollectionEditor diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.h b/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.h index 1281197633c..d9a7e3bb3d3 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.h +++ b/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.h @@ -9,4 +9,6 @@ namespace QmlDesigner::CollectionEditor { bool variantIslessThan(const QVariant &a, const QVariant &b, CollectionDetails::DataType type); +QString getSourceCollectionType(const QmlDesigner::ModelNode &node); + } // namespace QmlDesigner::CollectionEditor diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionlistmodel.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionlistmodel.cpp index 3745fef6983..bc0e1dc6662 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionlistmodel.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionlistmodel.cpp @@ -4,6 +4,7 @@ #include "collectionlistmodel.h" #include "collectioneditorconstants.h" +#include "collectioneditorutils.h" #include "variantproperty.h" #include @@ -28,6 +29,7 @@ namespace QmlDesigner { CollectionListModel::CollectionListModel(const ModelNode &sourceModel) : QStringListModel() , m_sourceNode(sourceModel) + , m_sourceType(CollectionEditor::getSourceCollectionType(sourceModel)) { connect(this, &CollectionListModel::modelReset, this, &CollectionListModel::updateEmpty); connect(this, &CollectionListModel::rowsRemoved, this, &CollectionListModel::updateEmpty); @@ -73,6 +75,24 @@ bool CollectionListModel::setData(const QModelIndex &index, const QVariant &valu return false; } +bool CollectionListModel::removeRows(int row, int count, const QModelIndex &parent) +{ + const int rows = rowCount(parent); + if (count < 1 || row >= rows) + return false; + + row = qBound(0, row, rows - 1); + count = qBound(1, count, rows - row); + + QStringList removedCollections = stringList().mid(row, count); + + bool itemsRemoved = Super::removeRows(row, count, parent); + if (itemsRemoved) + emit collectionsRemoved(removedCollections); + + return itemsRemoved; +} + QVariant CollectionListModel::data(const QModelIndex &index, int role) const { QTC_ASSERT(index.isValid(), return {}); @@ -166,4 +186,5 @@ void CollectionListModel::updateEmpty() setSelectedIndex(-1); } } + } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionlistmodel.h b/src/plugins/qmldesigner/components/collectioneditor/collectionlistmodel.h index 8450ee62b74..c65af750d80 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionlistmodel.h +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionlistmodel.h @@ -13,8 +13,10 @@ namespace QmlDesigner { class CollectionListModel : public QStringListModel { Q_OBJECT + Q_PROPERTY(int selectedIndex MEMBER m_selectedIndex NOTIFY selectedIndexChanged) Q_PROPERTY(bool isEmpty MEMBER m_isEmpty NOTIFY isEmptyChanged) + Q_PROPERTY(QString sourceType MEMBER m_sourceType CONSTANT) public: enum Roles { IdRole = Qt::UserRole + 1, NameRole, SourceRole, SelectedRole, CollectionsRole }; @@ -23,6 +25,7 @@ public: virtual QHash roleNames() const override; bool setData(const QModelIndex &index, const QVariant &value, int role) override; + bool removeRows(int row, int count, const QModelIndex &parent = {}) override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; Q_INVOKABLE int selectedIndex() const; @@ -38,6 +41,7 @@ signals: void selectedIndexChanged(int idx); void isEmptyChanged(bool); void collectionNameChanged(const QString &oldName, const QString &newName); + void collectionsRemoved(const QStringList &names); private: void setSelectedIndex(int idx); @@ -48,6 +52,7 @@ private: int m_selectedIndex = -1; bool m_isEmpty = false; const ModelNode m_sourceNode; + const QString m_sourceType; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.cpp index 3dfec1829b6..2cb78338d97 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.cpp @@ -5,6 +5,7 @@ #include "abstractview.h" #include "collectioneditorconstants.h" +#include "collectioneditorutils.h" #include "collectionlistmodel.h" #include "variantproperty.h" @@ -62,18 +63,6 @@ QSharedPointer loadCollection( return collectionsList; } -QString getSourceCollectionType(const QmlDesigner::ModelNode &node) -{ - using namespace QmlDesigner; - if (node.type() == CollectionEditor::JSONCOLLECTIONMODEL_TYPENAME) - return "json"; - - if (node.type() == CollectionEditor::CSVCOLLECTIONMODEL_TYPENAME) - return "csv"; - - return {}; -} - } // namespace namespace QmlDesigner { @@ -101,7 +90,7 @@ QVariant CollectionSourceModel::data(const QModelIndex &index, int role) const case NodeRole: return QVariant::fromValue(*collectionSource); case CollectionTypeRole: - return getSourceCollectionType(*collectionSource); + return CollectionEditor::getSourceCollectionType(*collectionSource); case SourceRole: return collectionSource->variantProperty(CollectionEditor::SOURCEFILE_PROPERTY).value(); case SelectedRole: @@ -488,8 +477,8 @@ void CollectionSourceModel::onCollectionNameChanged(const QString &oldName, cons QFile jsonFile(sourceFileAddress); if (!jsonFile.open(QFile::ReadWrite)) { - return emitRenameWarning(tr("Can't read or write \"%1\".\n%2") - .arg(sourceFileInfo.absoluteFilePath(), jsonFile.errorString())); + emitRenameWarning(tr("Can't read or write \"%1\".\n%2") + .arg(sourceFileInfo.absoluteFilePath(), jsonFile.errorString())); return; } @@ -542,6 +531,88 @@ void CollectionSourceModel::onCollectionNameChanged(const QString &oldName, cons } } +void CollectionSourceModel::onCollectionsRemoved(const QStringList &removedCollections) +{ + CollectionListModel *collectionList = qobject_cast(sender()); + QTC_ASSERT(collectionList, return); + + auto emitDeleteWarning = [this](const QString &msg) -> void { + emit warning(tr("Delete Collection"), msg); + }; + + const ModelNode node = collectionList->sourceNode(); + const QModelIndex nodeIndex = indexOfNode(node); + + if (!nodeIndex.isValid()) { + emitDeleteWarning(tr("Invalid node")); + return; + } + + if (node.type() == CollectionEditor::CSVCOLLECTIONMODEL_TYPENAME) { + removeSource(node); + return; + } else if (node.type() != CollectionEditor::JSONCOLLECTIONMODEL_TYPENAME) { + emitDeleteWarning(tr("Invalid node type")); + return; + } + + QString sourceFileAddress = node.variantProperty(CollectionEditor::SOURCEFILE_PROPERTY) + .value() + .toString(); + + QFileInfo sourceFileInfo(sourceFileAddress); + if (!sourceFileInfo.isFile()) { + emitDeleteWarning(tr("The selected node has an invalid source address")); + return; + } + + QFile jsonFile(sourceFileAddress); + if (!jsonFile.open(QFile::ReadWrite)) { + emitDeleteWarning(tr("Can't read or write \"%1\".\n%2") + .arg(sourceFileInfo.absoluteFilePath(), jsonFile.errorString())); + return; + } + + QJsonParseError parseError; + QJsonDocument document = QJsonDocument::fromJson(jsonFile.readAll(), &parseError); + if (parseError.error != QJsonParseError::NoError) { + emitDeleteWarning(tr("\"%1\" is corrupted.\n%2") + .arg(sourceFileInfo.absoluteFilePath(), parseError.errorString())); + return; + } + + if (document.isObject()) { + QJsonObject rootObject = document.object(); + + for (const QString &collectionName : removedCollections) { + bool sourceContainsCollection = rootObject.contains(collectionName); + if (sourceContainsCollection) { + rootObject.remove(collectionName); + } else { + emitDeleteWarning(tr("Collection doesn't contain the collection name (%1).") + .arg(sourceContainsCollection)); + } + } + + document.setObject(rootObject); + if (!jsonFile.resize(0)) { + emitDeleteWarning(tr("Can't clean \"%1\".").arg(sourceFileInfo.absoluteFilePath())); + return; + } + + QByteArray jsonData = document.toJson(); + auto writtenBytes = jsonFile.write(jsonData); + jsonFile.close(); + + if (writtenBytes != jsonData.size()) { + emitDeleteWarning(tr("Can't write to \"%1\".").arg(sourceFileInfo.absoluteFilePath())); + return; + } + + updateCollectionList(nodeIndex); + } +} + void CollectionSourceModel::setSelectedIndex(int idx) { idx = (idx > -1 && idx < m_collectionSources.count()) ? idx : -1; @@ -613,6 +684,12 @@ void CollectionSourceModel::registerCollection(const QSharedPointer Date: Tue, 7 Nov 2023 12:44:37 +0200 Subject: [PATCH 171/242] QmlDesigner: Only add a dragged material once to model Fixes: QDS-11095 Change-Id: Ia55c3cc906f42b3166b026c2c7447e649090069b Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Mahmoud Badri --- .../navigator/navigatortreemodel.cpp | 86 ++++++++++--------- 1 file changed, 44 insertions(+), 42 deletions(-) diff --git a/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp b/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp index 4f6d6f6068a..ed54fc4c52a 100644 --- a/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp +++ b/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp @@ -735,55 +735,57 @@ void NavigatorTreeModel::handleItemLibraryItemDrop(const QMimeData *mimeData, in return; } MaterialUtils::assignMaterialTo3dModel(m_view, targetNode, newModelNode); - } + } else { + ChooseFromPropertyListDialog *dialog = ChooseFromPropertyListDialog::createIfNeeded( + targetNode, newModelNode, Core::ICore::dialogParent()); + if (dialog) { + bool soloProperty = dialog->isSoloProperty(); + if (!soloProperty) + dialog->exec(); + if (soloProperty || dialog->result() == QDialog::Accepted) { + TypeName selectedProp = dialog->selectedProperty(); - ChooseFromPropertyListDialog *dialog = ChooseFromPropertyListDialog::createIfNeeded( - targetNode, newModelNode, Core::ICore::dialogParent()); - if (dialog) { - bool soloProperty = dialog->isSoloProperty(); - if (!soloProperty) - dialog->exec(); - if (soloProperty || dialog->result() == QDialog::Accepted) { - TypeName selectedProp = dialog->selectedProperty(); + // Pass and TextureInput can't have children, so we have to move nodes under parent + if (((newModelNode.metaInfo().isQtQuick3DShader() + || newModelNode.metaInfo().isQtQuick3DCommand() + || newModelNode.metaInfo().isQtQuick3DBuffer()) + && targetProperty.parentModelNode().metaInfo().isQtQuick3DPass()) + || (newModelNode.metaInfo().isQtQuick3DTexture() + && targetProperty.parentModelNode().metaInfo().isQtQuick3DTextureInput())) { + if (moveNodeToParent(targetProperty, newQmlObjectNode)) { + targetProperty = targetProperty.parentProperty(); + moveNodesAfter = false; + } + } - // Pass and TextureInput can't have children, so we have to move nodes under parent - if (((newModelNode.metaInfo().isQtQuick3DShader() - || newModelNode.metaInfo().isQtQuick3DCommand() - || newModelNode.metaInfo().isQtQuick3DBuffer()) - && targetProperty.parentModelNode().metaInfo().isQtQuick3DPass()) - || (newModelNode.metaInfo().isQtQuick3DTexture() - && targetProperty.parentModelNode().metaInfo().isQtQuick3DTextureInput())) { - if (moveNodeToParent(targetProperty, newQmlObjectNode)) { - targetProperty = targetProperty.parentProperty(); - moveNodesAfter = false; + if (targetNode.metaInfo().property(selectedProp).isListProperty()) { + BindingProperty listProp = targetNode.bindingProperty(selectedProp); + listProp.addModelNodeToArray(newModelNode); + validContainer = true; + } else { + targetNode.bindingProperty(dialog->selectedProperty()).setExpression( + newModelNode.validId()); + validContainer = true; } } - - if (targetNode.metaInfo().property(selectedProp).isListProperty()) { - BindingProperty listProp = targetNode.bindingProperty(selectedProp); - listProp.addModelNodeToArray(newModelNode); - validContainer = true; - } else { - targetNode.bindingProperty(dialog->selectedProperty()).setExpression(newModelNode.validId()); - validContainer = true; - } + delete dialog; } - delete dialog; - } - if (newModelNode.metaInfo().isQtQuick3DView3D()) { - const QList models = newModelNode.subModelNodesOfType( - m_view->model()->qtQuick3DModelMetaInfo()); - QTC_ASSERT(models.size() == 1, return); - MaterialUtils::assignMaterialTo3dModel(m_view, models.at(0)); - } else if (newModelNode.metaInfo().isQtQuick3DModel()) { - MaterialUtils::assignMaterialTo3dModel(m_view, newModelNode); - } + if (newModelNode.metaInfo().isQtQuick3DView3D()) { + const QList models = newModelNode.subModelNodesOfType( + m_view->model()->qtQuick3DModelMetaInfo()); + QTC_ASSERT(models.size() == 1, return); + MaterialUtils::assignMaterialTo3dModel(m_view, models.at(0)); + } else if (newModelNode.metaInfo().isQtQuick3DModel()) { + MaterialUtils::assignMaterialTo3dModel(m_view, newModelNode); + } - if (!validContainer) { - validContainer = NodeHints::fromModelNode(targetProperty.parentModelNode()).canBeContainerFor(newModelNode); - if (!validContainer) - newQmlObjectNode.destroy(); + if (!validContainer) { + validContainer = NodeHints::fromModelNode(targetProperty.parentModelNode()) + .canBeContainerFor(newModelNode); + if (!validContainer) + newQmlObjectNode.destroy(); + } } } }); From b357e267585ac1f8bc01d622b9d56f79ac176e02 Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Wed, 1 Nov 2023 17:54:12 +0200 Subject: [PATCH 172/242] QmlDesigner: Assign the collection to the selected node Task-number: QDS-11012 Change-Id: I68b23b276c804b7f95db2972de87583cd115e11f Reviewed-by: Mahmoud Badri Reviewed-by: Miikka Heikkinen Reviewed-by: Qt CI Patch Build Bot --- .../CollectionView.qml | 2 + .../ModelSourceItem.qml | 9 +++++ .../collectioneditorutils.cpp | 38 +++++++++++++++++++ .../collectioneditor/collectioneditorutils.h | 6 +++ .../collectioneditor/collectionview.cpp | 20 ++++++++-- .../collectioneditor/collectionwidget.cpp | 26 +++++++++++++ .../collectioneditor/collectionwidget.h | 9 +++++ 7 files changed, 106 insertions(+), 4 deletions(-) diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionView.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionView.qml index e58e5d92541..21a9bd2c3ee 100644 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionView.qml +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionView.qml @@ -128,6 +128,8 @@ Item { delegate: ModelSourceItem { implicitWidth: sourceListView.width onDeleteItem: root.model.removeRow(index) + hasSelectedTarget: root.rootView.targetNodeSelected + onAssignToSelected: root.rootView.assignSourceNodeToSelectedItem(sourceNode) } } } diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/ModelSourceItem.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/ModelSourceItem.qml index 4b5ce18100b..9c6ac9357db 100644 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/ModelSourceItem.qml +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/ModelSourceItem.qml @@ -14,6 +14,8 @@ Item { implicitWidth: 300 implicitHeight: wholeColumn.height + property bool hasSelectedTarget + property color textColor property var collectionModel @@ -21,6 +23,7 @@ Item { signal selectItem(int itemIndex) signal deleteItem() + signal assignToSelected() function toggleExpanded() { if (collectionListView.count > 0) @@ -177,6 +180,12 @@ Item { shortcut: StandardKey.Replace onTriggered: renameDialog.open() } + + StudioControls.MenuItem { + text: qsTr("Assign to the selected node") + enabled: root.hasSelectedTarget + onTriggered: root.assignToSelected() + } } StudioControls.Dialog { diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.cpp index 2b54b5ca609..1e47f6460f6 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.cpp @@ -3,8 +3,15 @@ #include "collectioneditorutils.h" +#include "abstractview.h" +#include "bindingproperty.h" +#include "nodemetainfo.h" +#include "propertymetainfo.h" + #include +#include + #include #include @@ -73,4 +80,35 @@ QString getSourceCollectionType(const ModelNode &node) return {}; } +void assignCollectionSourceToNode(AbstractView *view, + const ModelNode &modelNode, + const ModelNode &collectionSourceNode) +{ + QTC_ASSERT(modelNode.isValid() && collectionSourceNode.isValid(), return); + + if (collectionSourceNode.id().isEmpty() || !canAcceptCollectionAsModel(modelNode)) + return; + + BindingProperty modelProperty = modelNode.bindingProperty("model"); + + view->executeInTransaction("CollectionEditor::assignCollectionSourceToNode", + [&modelProperty, &collectionSourceNode]() { + modelProperty.setExpression(collectionSourceNode.id()); + }); +} + +bool canAcceptCollectionAsModel(const ModelNode &node) +{ + const NodeMetaInfo nodeMetaInfo = node.metaInfo(); + if (!nodeMetaInfo.isValid()) + return false; + + const PropertyMetaInfo modelProperty = nodeMetaInfo.property("model"); + if (!modelProperty.isValid()) + return false; + + return modelProperty.isWritable() && !modelProperty.isPrivate() + && modelProperty.propertyType().isVariant(); +} + } // namespace QmlDesigner::CollectionEditor diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.h b/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.h index d9a7e3bb3d3..464e9e49689 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.h +++ b/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.h @@ -11,4 +11,10 @@ bool variantIslessThan(const QVariant &a, const QVariant &b, CollectionDetails:: QString getSourceCollectionType(const QmlDesigner::ModelNode &node); +void assignCollectionSourceToNode(AbstractView *view, + const ModelNode &modelNode, + const ModelNode &collectionSourceNode = {}); + +bool canAcceptCollectionAsModel(const ModelNode &node); + } // namespace QmlDesigner::CollectionEditor diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp index 854d2782084..4f2cb1dacc9 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp @@ -5,6 +5,7 @@ #include "collectiondetailsmodel.h" #include "collectioneditorconstants.h" +#include "collectioneditorutils.h" #include "collectionsourcemodel.h" #include "collectionwidget.h" #include "designmodecontext.h" @@ -120,15 +121,26 @@ void CollectionView::variantPropertiesChanged(const QList &prop void CollectionView::selectedNodesChanged(const QList &selectedNodeList, [[maybe_unused]] const QList &lastSelectedNodeList) { - QList selectedJsonCollections = Utils::filtered(selectedNodeList, + QList selectedCollectionNodes = Utils::filtered(selectedNodeList, &isStudioCollectionModel); + bool singleNonCollectionNodeSelected = selectedNodeList.size() == 1 + && selectedCollectionNodes.isEmpty(); + + bool singleSelectedHasModelProperty = false; + if (singleNonCollectionNodeSelected) { + const ModelNode selectedNode = selectedNodeList.first(); + singleSelectedHasModelProperty = CollectionEditor::canAcceptCollectionAsModel(selectedNode); + } + + m_widget->setTargetNodeSelected(singleSelectedHasModelProperty); + // More than one collections are selected. So ignore them - if (selectedJsonCollections.size() > 1) + if (selectedCollectionNodes.size() > 1) return; - if (selectedJsonCollections.size() == 1) { // If exactly one collection is selected - m_widget->sourceModel()->selectSource(selectedJsonCollections.first()); + if (selectedCollectionNodes.size() == 1) { // If exactly one collection is selected + m_widget->sourceModel()->selectSource(selectedCollectionNodes.first()); return; } } diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp index 8e36827580f..517231359f8 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp @@ -5,6 +5,7 @@ #include "collectiondetailsmodel.h" #include "collectiondetailssortfiltermodel.h" +#include "collectioneditorutils.h" #include "collectionsourcemodel.h" #include "collectionview.h" #include "qmldesignerconstants.h" @@ -259,6 +260,22 @@ bool CollectionWidget::addCollection(const QString &collectionName, return false; } +void CollectionWidget::assignSourceNodeToSelectedItem(const QVariant &sourceNode) +{ + ModelNode sourceModel = sourceNode.value(); + ModelNode targetNode = m_view->singleSelectedModelNode(); + + QTC_ASSERT(sourceModel.isValid() && targetNode.isValid(), return); + + if (sourceModel.id().isEmpty()) { + warn(tr("Assigning the collection source"), + tr("The collection source must have a valid id to be assigned.")); + return; + } + + CollectionEditor::assignCollectionSourceToNode(m_view, targetNode, sourceModel); +} + void CollectionWidget::warn(const QString &title, const QString &body) { QMetaObject::invokeMethod(m_quickWidget->rootObject(), @@ -267,4 +284,13 @@ void CollectionWidget::warn(const QString &title, const QString &body) Q_ARG(QVariant, body)); } +void CollectionWidget::setTargetNodeSelected(bool selected) +{ + if (m_targetNodeSelected == selected) + return; + + m_targetNodeSelected = selected; + emit targetNodeSelectedChanged(m_targetNodeSelected); +} + } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.h b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.h index 3b8925bd93d..5bf728660cb 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.h +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.h @@ -21,6 +21,8 @@ class CollectionWidget : public QFrame { Q_OBJECT + Q_PROPERTY(bool targetNodeSelected MEMBER m_targetNodeSelected NOTIFY targetNodeSelectedChanged) + public: CollectionWidget(CollectionView *view); void contextHelp(const Core::IContext::HelpCallback &callback) const; @@ -41,7 +43,13 @@ public: const QString &sourceAddress, const QVariant &sourceNode); + Q_INVOKABLE void assignSourceNodeToSelectedItem(const QVariant &sourceNode); + void warn(const QString &title, const QString &body); + void setTargetNodeSelected(bool selected); + +signals: + void targetNodeSelectedChanged(bool); private: QPointer m_view; @@ -49,6 +57,7 @@ private: QPointer m_collectionDetailsModel; std::unique_ptr m_collectionDetailsSortFilterModel; QScopedPointer m_quickWidget; + bool m_targetNodeSelected = false; }; } // namespace QmlDesigner From 97db39c468d7b3297306051f8e81e20c66d29c31 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Tue, 7 Nov 2023 14:33:04 +0200 Subject: [PATCH 173/242] QmlDesigner: Remove temporary extraction folder FileExtractor::extract() creates a temporary folder for extraction if target path is not specified. This temporary folder was never cleaned up after it was no longer needed. Fixes: QDS-11057 Change-Id: I7c39429eb4beeb84fcaee6e8dc4f42d70e4d3fc0 Reviewed-by: Mahmoud Badri Reviewed-by: Qt CI Patch Build Bot --- .../qmldesigner/utils/fileextractor.cpp | 20 +++++++++++++++++-- src/plugins/qmldesigner/utils/fileextractor.h | 3 +++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/src/plugins/qmldesigner/utils/fileextractor.cpp b/src/plugins/qmldesigner/utils/fileextractor.cpp index 11827fca021..40381c5a27a 100644 --- a/src/plugins/qmldesigner/utils/fileextractor.cpp +++ b/src/plugins/qmldesigner/utils/fileextractor.cpp @@ -70,10 +70,14 @@ FileExtractor::FileExtractor(QObject *parent) }); } -FileExtractor::~FileExtractor() {} +FileExtractor::~FileExtractor() +{ + removeTempTargetPath(); +} void FileExtractor::changeTargetPath(const QString &path) { + removeTempTargetPath(); m_targetPath = FilePath::fromString(path); emit targetPathChanged(); emit targetFolderExistsChanged(); @@ -86,6 +90,7 @@ QString FileExtractor::targetPath() const void FileExtractor::setTargetPath(const QString &path) { + removeTempTargetPath(); m_targetPath = FilePath::fromString(path); QDir dir(m_targetPath.toString()); @@ -101,8 +106,10 @@ void FileExtractor::browse() { const FilePath path = FileUtils::getExistingDirectory(nullptr, tr("Choose Directory"), m_targetPath); - if (!path.isEmpty()) + if (!path.isEmpty()) { + removeTempTargetPath(); m_targetPath = path; + } emit targetPathChanged(); emit targetFolderExistsChanged(); @@ -203,6 +210,7 @@ void FileExtractor::extract() QString tempFileName = QDir::tempPath() + "/.qds_" + uniqueText + "_extract_" + m_archiveName + "_dir"; m_targetPath = FilePath::fromString(tempFileName); + m_isTempTargetPath = true; } m_targetFolder = m_targetPath.toString() + "/" + m_archiveName; @@ -250,4 +258,12 @@ void FileExtractor::extract() m_unarchiver->start(); } +void QmlDesigner::FileExtractor::removeTempTargetPath() +{ + if (m_isTempTargetPath && m_targetPath.exists()) { + m_targetPath.removeRecursively(); + m_isTempTargetPath = false; + } +} + } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/utils/fileextractor.h b/src/plugins/qmldesigner/utils/fileextractor.h index c02237fab4f..8cee7b34c42 100644 --- a/src/plugins/qmldesigner/utils/fileextractor.h +++ b/src/plugins/qmldesigner/utils/fileextractor.h @@ -72,6 +72,8 @@ signals: void alwaysCreateDirChanged(); private: + void removeTempTargetPath(); + Utils::FilePath m_targetPath; QString m_targetFolder; // The same as m_targetPath, but with the archive name also. Utils::FilePath m_sourceFile; @@ -86,6 +88,7 @@ private: QDateTime m_birthTime; bool m_clearTargetPathContents = false; bool m_alwaysCreateDir = false; + bool m_isTempTargetPath = false; qint64 m_bytesBefore = 0; qint64 m_compressedSize = 0; From ff6b34b21c4eaf8673f71fe2412be7fc83e30e07 Mon Sep 17 00:00:00 2001 From: Brook Cronin Date: Wed, 8 Nov 2023 08:33:29 +0100 Subject: [PATCH 174/242] QmlDesigner: Remove alias from ToolbarButton Change-Id: Icf3130fcc523a03745a1e6dde51644154b368880 Reviewed-by: Thomas Hartmann Reviewed-by: Qt CI Patch Build Bot --- share/qtcreator/qmldesigner/toolbar/ToolbarButton.qml | 1 - 1 file changed, 1 deletion(-) diff --git a/share/qtcreator/qmldesigner/toolbar/ToolbarButton.qml b/share/qtcreator/qmldesigner/toolbar/ToolbarButton.qml index e1d68bd2c54..706f0f19605 100644 --- a/share/qtcreator/qmldesigner/toolbar/ToolbarButton.qml +++ b/share/qtcreator/qmldesigner/toolbar/ToolbarButton.qml @@ -10,7 +10,6 @@ StudioControls.AbstractButton { id: button property alias tooltip: toolTipArea.tooltip - property alias checkedInverted: button.checkedInverted style: StudioTheme.Values.toolbarButtonStyle hover: toolTipArea.containsMouse From 536036acb7fd76800a575a1ebf20675ec3c02040 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Tue, 7 Nov 2023 16:04:06 +0100 Subject: [PATCH 175/242] QmlDesigner: Use qds-4.4 branch for components and add Utils Change-Id: Id20c68e9c73ce3bb1b658e4f7f7e39dd5ce9ae94 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Reviewed-by: Thomas Hartmann --- .../studio_templates/projects/common/qmlcomponents.tpl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/common/qmlcomponents.tpl b/share/qtcreator/qmldesigner/studio_templates/projects/common/qmlcomponents.tpl index 81f2ceba948..2e940be0329 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/common/qmlcomponents.tpl +++ b/share/qtcreator/qmldesigner/studio_templates/projects/common/qmlcomponents.tpl @@ -8,7 +8,7 @@ set(QT_QML_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/qml") include(FetchContent) FetchContent_Declare( ds - GIT_TAG qds-4.3 + GIT_TAG qds-4.4 GIT_REPOSITORY https://code.qt.io/qt-labs/qtquickdesigner-components.git ) @@ -24,6 +24,7 @@ target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE QuickStudioMultiTextplugin QuickStudioEventSimulatorplugin QuickStudioEventSystemplugin + QuickStudioUtilsplugin ) add_subdirectory(${ds_SOURCE_DIR} ${ds_BINARY_DIR}) From aab09423e43d704ec775beb56d89e9b4811dbb43 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Tue, 7 Nov 2023 16:03:13 +0100 Subject: [PATCH 176/242] QmlDesigner: Add more properties for ordering in code Change-Id: Ie7d65520e273c02b331531c64d666e95bedaa9bd Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Aleksei German --- .../designercore/model/modeltotextmerger.cpp | 80 ++++++++++--------- 1 file changed, 41 insertions(+), 39 deletions(-) diff --git a/src/plugins/qmldesigner/designercore/model/modeltotextmerger.cpp b/src/plugins/qmldesigner/designercore/model/modeltotextmerger.cpp index c57d947f69c..87a73f6ad34 100644 --- a/src/plugins/qmldesigner/designercore/model/modeltotextmerger.cpp +++ b/src/plugins/qmldesigner/designercore/model/modeltotextmerger.cpp @@ -349,45 +349,47 @@ QmlRefactoring::PropertyType ModelToTextMerger::propertyType(const AbstractPrope PropertyNameList ModelToTextMerger::propertyOrder() { - static const PropertyNameList properties = { - PropertyName("id"), - PropertyName("name"), - PropertyName("target"), - PropertyName("property"), - PropertyName("x"), - PropertyName("y"), - PropertyName("width"), - PropertyName("height"), - PropertyName("opacity"), - PropertyName("visible"), - PropertyName("position"), - PropertyName("color"), - PropertyName("radius"), - PropertyName("text"), - PropertyName("elide"), - PropertyName("border.color"), - PropertyName("border.width"), - PropertyName("anchors.verticalCenter"), - PropertyName("anchors.left"), - PropertyName("anchors.right"), - PropertyName("anchors.top"), - PropertyName("anchors.bottom"), - PropertyName("anchors.fill"), - PropertyName("anchors.margins"), - PropertyName("font.letterSpacing"), - PropertyName("font.pixelSize"), - PropertyName("horizontalAlignment"), - PropertyName("verticalAlignment"), - PropertyName("source"), - PropertyName("lineHeight"), - PropertyName("lineHeightMode"), - PropertyName("wrapMode"), - PropertyName(), - PropertyName("states"), - PropertyName("to"), - PropertyName("from"), - PropertyName("transitions") - }; + static const PropertyNameList properties = {PropertyName("id"), + PropertyName("name"), + PropertyName("target"), + PropertyName("property"), + PropertyName("x"), + PropertyName("y"), + PropertyName("width"), + PropertyName("height"), + PropertyName("opacity"), + PropertyName("visible"), + PropertyName("position"), + PropertyName("color"), + PropertyName("radius"), + PropertyName("text"), + PropertyName("elide"), + PropertyName("value"), + PropertyName("border.color"), + PropertyName("border.width"), + PropertyName("anchors.verticalCenter"), + PropertyName("anchors.left"), + PropertyName("anchors.right"), + PropertyName("anchors.top"), + PropertyName("anchors.bottom"), + PropertyName("anchors.fill"), + PropertyName("anchors.margins"), + PropertyName("anchors.leftMargin"), + PropertyName("anchors.rightMargin"), + PropertyName("anchors.topMargin"), + PropertyName("anchors.bottomMargin"), + PropertyName("font.letterSpacing"), + PropertyName("font.pixelSize"), + PropertyName("horizontalAlignment"), + PropertyName("verticalAlignment"), + PropertyName("source"), + PropertyName("lineHeight"), + PropertyName("lineHeightMode"), + PropertyName("wrapMode"), + PropertyName("states"), + PropertyName("to"), + PropertyName("from"), + PropertyName("transitions")}; return properties; } From 1b27dd383f32d3a9102182e062c0f5e28f023433 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Fri, 3 Nov 2023 16:18:49 +0100 Subject: [PATCH 177/242] QmlDesigner: Allow dropping components and assets to text editor Task-number: QDS-10868 Change-Id: I8037fd0fdd6060bbf84f0b24a52f18304380dcd2 Reviewed-by: Thomas Hartmann --- .../texteditor/texteditorwidget.cpp | 114 ++++++++++++++++-- .../components/texteditor/texteditorwidget.h | 2 + 2 files changed, 107 insertions(+), 9 deletions(-) diff --git a/src/plugins/qmldesigner/components/texteditor/texteditorwidget.cpp b/src/plugins/qmldesigner/components/texteditor/texteditorwidget.cpp index ec284caf8d6..b344985476d 100644 --- a/src/plugins/qmldesigner/components/texteditor/texteditorwidget.cpp +++ b/src/plugins/qmldesigner/components/texteditor/texteditorwidget.cpp @@ -4,6 +4,7 @@ #include "texteditorwidget.h" #include "utils/uniqueobjectptr.h" +#include #include #include #include @@ -11,22 +12,24 @@ #include #include +#include #include #include +#include #include #include -#include #include +#include #include #include #include -#include #include +#include namespace QmlDesigner { @@ -49,11 +52,15 @@ TextEditorWidget::TextEditorWidget(TextEditorView *textEditorView) m_updateSelectionTimer.setSingleShot(true); m_updateSelectionTimer.setInterval(200); - connect(&m_updateSelectionTimer, &QTimer::timeout, this, &TextEditorWidget::updateSelectionByCursorPosition); + connect(&m_updateSelectionTimer, + &QTimer::timeout, + this, + &TextEditorWidget::updateSelectionByCursorPosition); QmlDesignerPlugin::trackWidgetFocusTime(this, Constants::EVENT_TEXTEDITOR_TIME); } -void TextEditorWidget::setTextEditor(Utils::UniqueObjectLatePtr textEditor) +void TextEditorWidget::setTextEditor( + Utils::UniqueObjectLatePtr textEditor) { std::swap(m_textEditor, textEditor); @@ -216,17 +223,106 @@ bool TextEditorWidget::eventFilter(QObject *, QEvent *event) void TextEditorWidget::dragEnterEvent(QDragEnterEvent *dragEnterEvent) { - const DesignerActionManager &actionManager = QmlDesignerPlugin::instance() - ->viewManager().designerActionManager(); + const DesignerActionManager &actionManager + = QmlDesignerPlugin::instance()->viewManager().designerActionManager(); if (actionManager.externalDragHasSupportedAssets(dragEnterEvent->mimeData())) dragEnterEvent->acceptProposedAction(); + + if (dragEnterEvent->mimeData()->hasFormat(Constants::MIME_TYPE_ITEM_LIBRARY_INFO) + || dragEnterEvent->mimeData()->hasFormat(Constants::MIME_TYPE_ASSETS)) { + QByteArray data = dragEnterEvent->mimeData()->data(Constants::MIME_TYPE_ITEM_LIBRARY_INFO); + if (!data.isEmpty()) { + QDataStream stream(data); + stream >> m_draggedEntry; + } + dragEnterEvent->acceptProposedAction(); + } } void TextEditorWidget::dropEvent(QDropEvent *dropEvent) { - const DesignerActionManager &actionManager = QmlDesignerPlugin::instance() - ->viewManager().designerActionManager(); - actionManager.handleExternalAssetsDrop(dropEvent->mimeData()); + QTextCursor cursor = m_textEditor->editorWidget()->cursorForPosition(dropEvent->pos()); + const int cursorPosition = cursor.position(); + RewriterView *rewriterView = m_textEditorView->model()->rewriterView(); + + QTC_ASSERT(rewriterView, return); + ModelNode modelNode = rewriterView->nodeAtTextCursorPosition(cursorPosition); + + if (!modelNode.isValid()) + return; + + auto targetProperty = modelNode.defaultNodeAbstractProperty(); + + if (dropEvent->mimeData()->hasFormat(Constants::MIME_TYPE_ITEM_LIBRARY_INFO)) { + if (!m_draggedEntry.name().isEmpty()) { + m_textEditorView->executeInTransaction("TextEditorWidget::dropEventItem", [&] { + auto newQmlObjectNode = QmlItemNode::createQmlObjectNode(m_textEditorView, + m_draggedEntry, + QPointF(), + targetProperty, + false); + }); + } + } else if (dropEvent->mimeData()->hasFormat(Constants::MIME_TYPE_ASSETS)) { + const QStringList assetsPaths + = QString::fromUtf8(dropEvent->mimeData()->data(Constants::MIME_TYPE_ASSETS)).split(','); + + QTC_ASSERT(!assetsPaths.isEmpty(), return); + auto assetTypeAndData = AssetsLibraryWidget::getAssetTypeAndData(assetsPaths.first()); + QString assetType = assetTypeAndData.first; + QString assetData = QString::fromUtf8(assetTypeAndData.second); + + ModelNode newModelNode; + ModelNode targetNode = targetProperty.parentModelNode(); + QList addedNodes; + + for (const QString &assetPath : assetsPaths) { + auto assetTypeAndData = AssetsLibraryWidget::getAssetTypeAndData(assetPath); + QString assetType = assetTypeAndData.first; + QString assetData = QString::fromUtf8(assetTypeAndData.second); + bool moveNodesAfter = true; // Appending to parent is the default in text editor + if (assetType == Constants::MIME_TYPE_ASSET_IMAGE) { + newModelNode = ModelNodeOperations::handleItemLibraryImageDrop(assetPath, + targetProperty, + targetNode, + moveNodesAfter); + } else if (assetType == Constants::MIME_TYPE_ASSET_FONT) { + newModelNode = ModelNodeOperations::handleItemLibraryFontDrop( + assetData, // assetData is fontFamily + targetProperty, + targetNode); + } else if (assetType == Constants::MIME_TYPE_ASSET_SHADER) { + newModelNode = ModelNodeOperations::handleItemLibraryShaderDrop(assetPath, + assetData == "f", + targetProperty, + targetNode, + moveNodesAfter); + } else if (assetType == Constants::MIME_TYPE_ASSET_SOUND) { + newModelNode = ModelNodeOperations::handleItemLibrarySoundDrop(assetPath, + targetProperty, + targetNode); + } else if (assetType == Constants::MIME_TYPE_ASSET_TEXTURE3D) { + newModelNode = ModelNodeOperations::handleItemLibraryTexture3dDrop(assetPath, + targetProperty, + targetNode, + moveNodesAfter); + } else if (assetType == Constants::MIME_TYPE_ASSET_EFFECT) { + newModelNode = ModelNodeOperations::handleItemLibraryEffectDrop(assetPath, + targetNode); + } + + if (newModelNode.isValid()) + addedNodes.append(newModelNode); + } + + if (!addedNodes.isEmpty()) + m_textEditorView->setSelectedModelNodes(addedNodes); + } else { + const DesignerActionManager &actionManager + = QmlDesignerPlugin::instance()->viewManager().designerActionManager(); + actionManager.handleExternalAssetsDrop(dropEvent->mimeData()); + } + m_textEditorView->model()->endDrag(); } } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/texteditor/texteditorwidget.h b/src/plugins/qmldesigner/components/texteditor/texteditorwidget.h index 8c5f5b2dd86..8178050cf71 100644 --- a/src/plugins/qmldesigner/components/texteditor/texteditorwidget.h +++ b/src/plugins/qmldesigner/components/texteditor/texteditorwidget.h @@ -5,6 +5,7 @@ #include #include +#include #include #include @@ -63,6 +64,7 @@ private: QVBoxLayout *m_layout = nullptr; bool m_blockCursorSelectionSynchronisation = false; bool m_blockRoundTrip = false; + ItemLibraryEntry m_draggedEntry; }; } // namespace QmlDesigner From 578b66727a60c29d529957036689bbc7be9b4169 Mon Sep 17 00:00:00 2001 From: Knud Dollereder Date: Wed, 8 Nov 2023 13:21:16 +0100 Subject: [PATCH 178/242] QmlProject: Add QML_IMPORT_PATH to the main cmakegen template Change-Id: Idd55f372701c76f43869abfc3b493247e06ce8f5 Reviewed-by: Thomas Hartmann --- .../cmakegen/qmlprojectmaincmakelists.tpl | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/plugins/qmlprojectmanager/cmakegen/qmlprojectmaincmakelists.tpl b/src/plugins/qmlprojectmanager/cmakegen/qmlprojectmaincmakelists.tpl index 4cd900a31ff..9ca0ace63ba 100644 --- a/src/plugins/qmlprojectmanager/cmakegen/qmlprojectmaincmakelists.tpl +++ b/src/plugins/qmlprojectmanager/cmakegen/qmlprojectmaincmakelists.tpl @@ -5,8 +5,13 @@ option(BUILD_QDS_COMPONENTS "Build design studio components" ON) project(%1 LANGUAGES CXX) -set(CMAKE_INCLUDE_CURRENT_DIR ON) set(CMAKE_AUTOMOC ON) +set(CMAKE_INCLUDE_CURRENT_DIR ON) +set(QT_QML_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/qml) +set(QML_IMPORT_PATH ${QT_QML_OUTPUT_DIRECTORY} + CACHE STRING "Import paths for Qt Creator's code model" + FORCE +) find_package(Qt6 6.2 REQUIRED COMPONENTS Core Gui Qml Quick) @@ -39,7 +44,7 @@ if (LINK_INSIGHT) endif () include(GNUInstallDirs) -install(TARGETS CppExampleApp +install(TARGETS %1 BUNDLE DESTINATION . LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} From d8465f76434fb9e102160df072e6be1b0432a58f Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Wed, 8 Nov 2023 10:15:59 +0100 Subject: [PATCH 179/242] QmlDesigner: Add QML_IMPORT_PATH to cmake template Task-number: QDS-11149 Change-Id: Ic54d10bf5e6dd0cf23ae646df8fbac69334c9728 Reviewed-by: Knud Dollereder Reviewed-by: Qt CI Patch Build Bot --- .../projects/common/CMakeLists.main.txt.tpl | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/common/CMakeLists.main.txt.tpl b/share/qtcreator/qmldesigner/studio_templates/projects/common/CMakeLists.main.txt.tpl index 0adf6e1c98e..79f9385dad6 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/common/CMakeLists.main.txt.tpl +++ b/share/qtcreator/qmldesigner/studio_templates/projects/common/CMakeLists.main.txt.tpl @@ -28,6 +28,12 @@ target_link_libraries(%{ProjectName}App PRIVATE Qt6::Quick ) +set(QT_QML_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/qml) +set(QML_IMPORT_PATH ${QT_QML_OUTPUT_DIRECTORY} + CACHE STRING "Import paths for Qt Creator's code model" + FORCE +) + if (BUILD_QDS_COMPONENTS) include(${CMAKE_CURRENT_SOURCE_DIR}/qmlcomponents) endif() From 514b070a4cd0a9a577a33fe374d48389a97f7b99 Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Wed, 8 Nov 2023 15:29:56 +0200 Subject: [PATCH 180/242] QmlDesigner: Update icon font Added icons: addGroup_medium.svg assignTo.svg bakeLights_medium.svg listView_medium.svg pause_medium.svg tableView_medium.svg warning_medium.svg Change-Id: I17c39fdd771e3391ae6a37f171223fdea0e773fe Reviewed-by: Thomas Hartmann --- .../imports/StudioTheme/InternalConstants.qml | 707 +++++++++--------- .../imports/StudioTheme/icons.ttf | Bin 64936 -> 66888 bytes .../components/componentcore/theme.h | 7 + 3 files changed, 364 insertions(+), 350 deletions(-) diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/InternalConstants.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/InternalConstants.qml index b35f383c9e0..eb142b6dd3f 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/InternalConstants.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/InternalConstants.qml @@ -18,356 +18,363 @@ QtObject { readonly property string addColumnAfter: "\u0023" readonly property string addColumnBefore: "\u0024" readonly property string addFile: "\u0025" - readonly property string addRowAfter: "\u0026" - readonly property string addRowBefore: "\u0027" - readonly property string addTable: "\u0028" - readonly property string add_medium: "\u0029" - readonly property string add_small: "\u002A" - readonly property string addcolumnleft_medium: "\u002B" - readonly property string addcolumnright_medium: "\u002C" - readonly property string addrowabove_medium: "\u002D" - readonly property string addrowbelow_medium: "\u002E" - readonly property string adsClose: "\u002F" - readonly property string adsDetach: "\u0030" - readonly property string adsDropDown: "\u0031" - readonly property string alias: "\u0032" - readonly property string aliasAnimated: "\u0033" - readonly property string alignBottom: "\u0034" - readonly property string alignCenterHorizontal: "\u0035" - readonly property string alignCenterVertical: "\u0036" - readonly property string alignLeft: "\u0037" - readonly property string alignRight: "\u0038" - readonly property string alignTo: "\u0039" - readonly property string alignToCam_medium: "\u003A" - readonly property string alignToCamera_small: "\u003B" - readonly property string alignToObject_small: "\u003C" - readonly property string alignToView_medium: "\u003D" - readonly property string alignTop: "\u003E" - readonly property string anchorBaseline: "\u003F" - readonly property string anchorBottom: "\u0040" - readonly property string anchorFill: "\u0041" - readonly property string anchorLeft: "\u0042" - readonly property string anchorRight: "\u0043" - readonly property string anchorTop: "\u0044" - readonly property string anchors_small: "\u0045" - readonly property string animatedProperty: "\u0046" - readonly property string annotationBubble: "\u0047" - readonly property string annotationDecal: "\u0048" - readonly property string annotations_large: "\u0049" - readonly property string annotations_small: "\u004A" - readonly property string applyMaterialToSelected: "\u004B" - readonly property string apply_medium: "\u004C" - readonly property string apply_small: "\u004D" - readonly property string arrange_small: "\u004E" - readonly property string arrow_small: "\u004F" - readonly property string assign: "\u0050" - readonly property string attach_medium: "\u0051" - readonly property string back_medium: "\u0052" - readonly property string backspace_small: "\u0053" - readonly property string bevelAll: "\u0054" - readonly property string bevelCorner: "\u0055" - readonly property string bezier_medium: "\u0056" - readonly property string binding_medium: "\u0057" - readonly property string bounds_small: "\u0058" - readonly property string branch_medium: "\u0059" - readonly property string camera_small: "\u005A" - readonly property string centerHorizontal: "\u005B" - readonly property string centerVertical: "\u005C" - readonly property string cleanLogs_medium: "\u005D" - readonly property string closeCross: "\u005E" - readonly property string closeFile_large: "\u005F" - readonly property string closeLink: "\u0060" - readonly property string close_small: "\u0061" - readonly property string code: "\u0062" - readonly property string codeEditor_medium: "\u0063" - readonly property string codeview_medium: "\u0064" - readonly property string colorPopupClose: "\u0065" - readonly property string colorSelection_medium: "\u0066" - readonly property string columnsAndRows: "\u0067" - readonly property string cone_medium: "\u0068" - readonly property string cone_small: "\u0069" - readonly property string connection_small: "\u006A" - readonly property string connections_medium: "\u006B" - readonly property string copyLink: "\u006C" - readonly property string copyStyle: "\u006D" - readonly property string copy_small: "\u006E" - readonly property string cornerA: "\u006F" - readonly property string cornerB: "\u0070" - readonly property string cornersAll: "\u0071" - readonly property string createComponent_large: "\u0072" - readonly property string createComponent_small: "\u0073" - readonly property string create_medium: "\u0074" - readonly property string create_small: "\u0075" - readonly property string cube_medium: "\u0076" - readonly property string cube_small: "\u0077" - readonly property string curveDesigner: "\u0078" - readonly property string curveDesigner_medium: "\u0079" - readonly property string curveEditor: "\u007A" - readonly property string customMaterialEditor: "\u007B" - readonly property string cylinder_medium: "\u007C" - readonly property string cylinder_small: "\u007D" - readonly property string decisionNode: "\u007E" - readonly property string deleteColumn: "\u007F" - readonly property string deleteMaterial: "\u0080" - readonly property string deleteRow: "\u0081" - readonly property string deleteTable: "\u0082" - readonly property string delete_medium: "\u0083" - readonly property string delete_small: "\u0084" - readonly property string deletecolumn_medium: "\u0085" - readonly property string deleterow_medium: "\u0086" - readonly property string designMode_large: "\u0087" - readonly property string detach: "\u0088" - readonly property string directionalLight_small: "\u0089" - readonly property string distributeBottom: "\u008A" - readonly property string distributeCenterHorizontal: "\u008B" - readonly property string distributeCenterVertical: "\u008C" - readonly property string distributeLeft: "\u008D" - readonly property string distributeOriginBottomRight: "\u008E" - readonly property string distributeOriginCenter: "\u008F" - readonly property string distributeOriginNone: "\u0090" - readonly property string distributeOriginTopLeft: "\u0091" - readonly property string distributeRight: "\u0092" - readonly property string distributeSpacingHorizontal: "\u0093" - readonly property string distributeSpacingVertical: "\u0094" - readonly property string distributeTop: "\u0095" - readonly property string download: "\u0096" - readonly property string downloadUnavailable: "\u0097" - readonly property string downloadUpdate: "\u0098" - readonly property string downloadcsv_large: "\u0099" - readonly property string downloadcsv_medium: "\u009A" - readonly property string downloaded: "\u009B" - readonly property string downloadjson_large: "\u009D" - readonly property string downloadjson_medium: "\u009E" - readonly property string dragmarks: "\u009F" - readonly property string duplicate_small: "\u00A0" - readonly property string edit: "\u00A1" - readonly property string editComponent_large: "\u00A2" - readonly property string editComponent_small: "\u00A3" - readonly property string editLightOff_medium: "\u00A4" - readonly property string editLightOn_medium: "\u00A5" - readonly property string edit_medium: "\u00A6" - readonly property string edit_small: "\u00A7" - readonly property string effects: "\u00A8" - readonly property string events_small: "\u00A9" - readonly property string export_medium: "\u00AA" - readonly property string eyeDropper: "\u00AB" - readonly property string favorite: "\u00AC" - readonly property string fitAll_medium: "\u00AE" - readonly property string fitSelected_small: "\u00AF" - readonly property string fitSelection_medium: "\u00B0" - readonly property string fitToView_medium: "\u00B1" - readonly property string flowAction: "\u00B2" - readonly property string flowTransition: "\u00B3" - readonly property string fontStyleBold: "\u00B4" - readonly property string fontStyleItalic: "\u00B5" - readonly property string fontStyleStrikethrough: "\u00B6" - readonly property string fontStyleUnderline: "\u00B7" - readonly property string forward_medium: "\u00B8" - readonly property string globalOrient_medium: "\u00B9" - readonly property string gradient: "\u00BA" - readonly property string gridView: "\u00BB" - readonly property string grid_medium: "\u00BC" - readonly property string group_small: "\u00BD" - readonly property string help: "\u00BE" - readonly property string home_large: "\u00BF" - readonly property string idAliasOff: "\u00C0" - readonly property string idAliasOn: "\u00C1" - readonly property string import_medium: "\u00C2" - readonly property string imported: "\u00C3" - readonly property string importedModels_small: "\u00C4" - readonly property string infinity: "\u00C5" - readonly property string invisible_medium: "\u00C6" - readonly property string invisible_small: "\u00C7" - readonly property string jumpToCode_medium: "\u00C8" - readonly property string jumpToCode_small: "\u00C9" - readonly property string keyframe: "\u00CA" - readonly property string languageList_medium: "\u00CB" - readonly property string layouts_small: "\u00CC" - readonly property string lights_small: "\u00CD" - readonly property string linear_medium: "\u00CE" - readonly property string linkTriangle: "\u00CF" - readonly property string linked: "\u00D0" - readonly property string listView: "\u00D1" - readonly property string list_medium: "\u00D2" - readonly property string localOrient_medium: "\u00D3" - readonly property string lockOff: "\u00D4" - readonly property string lockOn: "\u00D5" - readonly property string loopPlayback_medium: "\u00D6" - readonly property string materialBrowser_medium: "\u00D7" - readonly property string materialPreviewEnvironment: "\u00D8" - readonly property string materialPreviewModel: "\u00D9" - readonly property string material_medium: "\u00DA" - readonly property string maxBar_small: "\u00DB" - readonly property string mergeCells: "\u00DC" - readonly property string merge_small: "\u00DD" - readonly property string minus: "\u00DE" - readonly property string mirror: "\u00DF" - readonly property string more_medium: "\u00E0" - readonly property string mouseArea_small: "\u00E1" - readonly property string moveDown_medium: "\u00E2" - readonly property string moveInwards_medium: "\u00E3" - readonly property string moveUp_medium: "\u00E4" - readonly property string moveUpwards_medium: "\u00E5" - readonly property string move_medium: "\u00E6" - readonly property string newMaterial: "\u00E7" - readonly property string nextFile_large: "\u00E8" - readonly property string normalBar_small: "\u00E9" - readonly property string openLink: "\u00EA" - readonly property string openMaterialBrowser: "\u00EB" - readonly property string orientation: "\u00EC" - readonly property string orthCam_medium: "\u00ED" - readonly property string orthCam_small: "\u00EE" - readonly property string paddingEdge: "\u00EF" - readonly property string paddingFrame: "\u00F0" - readonly property string particleAnimation_medium: "\u00F1" - readonly property string pasteStyle: "\u00F2" - readonly property string paste_small: "\u00F3" - readonly property string pause: "\u00F4" - readonly property string perspectiveCam_medium: "\u00F5" - readonly property string perspectiveCam_small: "\u00F6" - readonly property string pin: "\u00F7" - readonly property string plane_medium: "\u00F8" - readonly property string plane_small: "\u00F9" - readonly property string play: "\u00FA" - readonly property string playFill_medium: "\u00FB" - readonly property string playOutline_medium: "\u00FC" - readonly property string plus: "\u00FD" - readonly property string pointLight_small: "\u00FE" - readonly property string positioners_small: "\u00FF" - readonly property string previewEnv_medium: "\u0100" - readonly property string previousFile_large: "\u0101" - readonly property string promote: "\u0102" - readonly property string properties_medium: "\u0103" - readonly property string readOnly: "\u0104" - readonly property string recordFill_medium: "\u0105" - readonly property string recordOutline_medium: "\u0106" - readonly property string redo: "\u0107" - readonly property string reload_medium: "\u0108" - readonly property string remove_medium: "\u0109" - readonly property string remove_small: "\u010A" - readonly property string rename_small: "\u010B" - readonly property string replace_small: "\u010C" - readonly property string resetView_small: "\u010D" - readonly property string restartParticles_medium: "\u010E" - readonly property string reverseOrder_medium: "\u010F" - readonly property string roatate_medium: "\u0110" - readonly property string rotationFill: "\u0111" - readonly property string rotationOutline: "\u0112" - readonly property string runProjFill_large: "\u0113" - readonly property string runProjOutline_large: "\u0114" - readonly property string s_anchors: "\u0115" - readonly property string s_annotations: "\u0116" - readonly property string s_arrange: "\u0117" - readonly property string s_boundingBox: "\u0118" - readonly property string s_component: "\u0119" - readonly property string s_connections: "\u011A" - readonly property string s_edit: "\u011B" - readonly property string s_enterComponent: "\u011C" - readonly property string s_eventList: "\u011D" - readonly property string s_group: "\u011E" - readonly property string s_layouts: "\u011F" - readonly property string s_merging: "\u0120" - readonly property string s_mouseArea: "\u0121" - readonly property string s_positioners: "\u0122" - readonly property string s_selection: "\u0123" - readonly property string s_snapping: "\u0124" - readonly property string s_timeline: "\u0125" - readonly property string s_visibility: "\u0126" - readonly property string saveLogs_medium: "\u0127" - readonly property string scale_medium: "\u0128" - readonly property string search: "\u0129" - readonly property string search_small: "\u012A" - readonly property string sectionToggle: "\u012B" - readonly property string selectFill_medium: "\u012C" - readonly property string selectOutline_medium: "\u012D" - readonly property string selectParent_small: "\u012E" - readonly property string selection_small: "\u012F" - readonly property string settings_medium: "\u0130" - readonly property string signal_small: "\u0131" - readonly property string snapping_conf_medium: "\u0132" - readonly property string snapping_medium: "\u0133" - readonly property string snapping_small: "\u0134" - readonly property string sortascending_medium: "\u0135" - readonly property string sortdescending_medium: "\u0136" - readonly property string sphere_medium: "\u0137" - readonly property string sphere_small: "\u0138" - readonly property string splitColumns: "\u0139" - readonly property string splitRows: "\u013A" - readonly property string splitScreen_medium: "\u013B" - readonly property string spotLight_small: "\u013C" - readonly property string stackedContainer_small: "\u013D" - readonly property string startNode: "\u013E" - readonly property string step_medium: "\u013F" - readonly property string stop_medium: "\u0140" - readonly property string testIcon: "\u0141" - readonly property string textAlignBottom: "\u0142" - readonly property string textAlignCenter: "\u0143" - readonly property string textAlignJustified: "\u0144" - readonly property string textAlignLeft: "\u0145" - readonly property string textAlignMiddle: "\u0146" - readonly property string textAlignRight: "\u0147" - readonly property string textAlignTop: "\u0148" - readonly property string textBulletList: "\u0149" - readonly property string textFullJustification: "\u014A" - readonly property string textNumberedList: "\u014B" - readonly property string textures_medium: "\u014C" - readonly property string tickIcon: "\u014D" - readonly property string tickMark_small: "\u014E" - readonly property string timeline_small: "\u014F" - readonly property string toEndFrame_medium: "\u0150" - readonly property string toNextFrame_medium: "\u0151" - readonly property string toPrevFrame_medium: "\u0152" - readonly property string toStartFrame_medium: "\u0153" - readonly property string topToolbar_annotations: "\u0154" - readonly property string topToolbar_closeFile: "\u0155" - readonly property string topToolbar_designMode: "\u0156" - readonly property string topToolbar_enterComponent: "\u0157" - readonly property string topToolbar_home: "\u0158" - readonly property string topToolbar_makeComponent: "\u0159" - readonly property string topToolbar_navFile: "\u015A" - readonly property string topToolbar_runProject: "\u015B" - readonly property string translationCreateFiles: "\u015C" - readonly property string translationCreateReport: "\u015D" - readonly property string translationExport: "\u015E" - readonly property string translationImport: "\u015F" - readonly property string translationSelectLanguages: "\u0160" - readonly property string translationTest: "\u0161" - readonly property string transparent: "\u0162" - readonly property string triState: "\u0163" - readonly property string triangleArcA: "\u0164" - readonly property string triangleArcB: "\u0165" - readonly property string triangleCornerA: "\u0166" - readonly property string triangleCornerB: "\u0167" - readonly property string unLinked: "\u0168" - readonly property string undo: "\u0169" - readonly property string unify_medium: "\u016A" - readonly property string unpin: "\u016B" - readonly property string upDownIcon: "\u016C" - readonly property string upDownSquare2: "\u016D" - readonly property string updateAvailable_medium: "\u016E" - readonly property string updateContent_medium: "\u016F" - readonly property string uploadcsv_large: "\u0170" - readonly property string uploadcsv_medium: "\u0171" - readonly property string uploadjson_large: "\u0172" - readonly property string uploadjson_medium: "\u0173" - readonly property string visibilityOff: "\u0174" - readonly property string visibilityOn: "\u0175" - readonly property string visible_medium: "\u0176" - readonly property string visible_small: "\u0177" - readonly property string wildcard: "\u0178" - readonly property string wizardsAutomotive: "\u0179" - readonly property string wizardsDesktop: "\u017A" - readonly property string wizardsGeneric: "\u017B" - readonly property string wizardsMcuEmpty: "\u017C" - readonly property string wizardsMcuGraph: "\u017D" - readonly property string wizardsMobile: "\u017E" - readonly property string wizardsUnknown: "\u017F" - readonly property string zoomAll: "\u0180" - readonly property string zoomIn: "\u0181" - readonly property string zoomIn_medium: "\u0182" - readonly property string zoomOut: "\u0183" - readonly property string zoomOut_medium: "\u0184" - readonly property string zoomSelection: "\u0185" + readonly property string addGroup_medium: "\u0026" + readonly property string addRowAfter: "\u0027" + readonly property string addRowBefore: "\u0028" + readonly property string addTable: "\u0029" + readonly property string add_medium: "\u002A" + readonly property string add_small: "\u002B" + readonly property string addcolumnleft_medium: "\u002C" + readonly property string addcolumnright_medium: "\u002D" + readonly property string addrowabove_medium: "\u002E" + readonly property string addrowbelow_medium: "\u002F" + readonly property string adsClose: "\u0030" + readonly property string adsDetach: "\u0031" + readonly property string adsDropDown: "\u0032" + readonly property string alias: "\u0033" + readonly property string aliasAnimated: "\u0034" + readonly property string alignBottom: "\u0035" + readonly property string alignCenterHorizontal: "\u0036" + readonly property string alignCenterVertical: "\u0037" + readonly property string alignLeft: "\u0038" + readonly property string alignRight: "\u0039" + readonly property string alignTo: "\u003A" + readonly property string alignToCam_medium: "\u003B" + readonly property string alignToCamera_small: "\u003C" + readonly property string alignToObject_small: "\u003D" + readonly property string alignToView_medium: "\u003E" + readonly property string alignTop: "\u003F" + readonly property string anchorBaseline: "\u0040" + readonly property string anchorBottom: "\u0041" + readonly property string anchorFill: "\u0042" + readonly property string anchorLeft: "\u0043" + readonly property string anchorRight: "\u0044" + readonly property string anchorTop: "\u0045" + readonly property string anchors_small: "\u0046" + readonly property string animatedProperty: "\u0047" + readonly property string annotationBubble: "\u0048" + readonly property string annotationDecal: "\u0049" + readonly property string annotations_large: "\u004A" + readonly property string annotations_small: "\u004B" + readonly property string applyMaterialToSelected: "\u004C" + readonly property string apply_medium: "\u004D" + readonly property string apply_small: "\u004E" + readonly property string arrange_small: "\u004F" + readonly property string arrow_small: "\u0050" + readonly property string assign: "\u0051" + readonly property string assignTo: "\u0052" + readonly property string attach_medium: "\u0053" + readonly property string back_medium: "\u0054" + readonly property string backspace_small: "\u0055" + readonly property string bakeLights_medium: "\u0056" + readonly property string bevelAll: "\u0057" + readonly property string bevelCorner: "\u0058" + readonly property string bezier_medium: "\u0059" + readonly property string binding_medium: "\u005A" + readonly property string bounds_small: "\u005B" + readonly property string branch_medium: "\u005C" + readonly property string camera_small: "\u005D" + readonly property string centerHorizontal: "\u005E" + readonly property string centerVertical: "\u005F" + readonly property string cleanLogs_medium: "\u0060" + readonly property string closeCross: "\u0061" + readonly property string closeFile_large: "\u0062" + readonly property string closeLink: "\u0063" + readonly property string close_small: "\u0064" + readonly property string code: "\u0065" + readonly property string codeEditor_medium: "\u0066" + readonly property string codeview_medium: "\u0067" + readonly property string colorPopupClose: "\u0068" + readonly property string colorSelection_medium: "\u0069" + readonly property string columnsAndRows: "\u006A" + readonly property string cone_medium: "\u006B" + readonly property string cone_small: "\u006C" + readonly property string connection_small: "\u006D" + readonly property string connections_medium: "\u006E" + readonly property string copyLink: "\u006F" + readonly property string copyStyle: "\u0070" + readonly property string copy_small: "\u0071" + readonly property string cornerA: "\u0072" + readonly property string cornerB: "\u0073" + readonly property string cornersAll: "\u0074" + readonly property string createComponent_large: "\u0075" + readonly property string createComponent_small: "\u0076" + readonly property string create_medium: "\u0077" + readonly property string create_small: "\u0078" + readonly property string cube_medium: "\u0079" + readonly property string cube_small: "\u007A" + readonly property string curveDesigner: "\u007B" + readonly property string curveDesigner_medium: "\u007C" + readonly property string curveEditor: "\u007D" + readonly property string customMaterialEditor: "\u007E" + readonly property string cylinder_medium: "\u007F" + readonly property string cylinder_small: "\u0080" + readonly property string decisionNode: "\u0081" + readonly property string deleteColumn: "\u0082" + readonly property string deleteMaterial: "\u0083" + readonly property string deleteRow: "\u0084" + readonly property string deleteTable: "\u0085" + readonly property string delete_medium: "\u0086" + readonly property string delete_small: "\u0087" + readonly property string deletecolumn_medium: "\u0088" + readonly property string deleterow_medium: "\u0089" + readonly property string designMode_large: "\u008A" + readonly property string detach: "\u008B" + readonly property string directionalLight_small: "\u008C" + readonly property string distributeBottom: "\u008D" + readonly property string distributeCenterHorizontal: "\u008E" + readonly property string distributeCenterVertical: "\u008F" + readonly property string distributeLeft: "\u0090" + readonly property string distributeOriginBottomRight: "\u0091" + readonly property string distributeOriginCenter: "\u0092" + readonly property string distributeOriginNone: "\u0093" + readonly property string distributeOriginTopLeft: "\u0094" + readonly property string distributeRight: "\u0095" + readonly property string distributeSpacingHorizontal: "\u0096" + readonly property string distributeSpacingVertical: "\u0097" + readonly property string distributeTop: "\u0098" + readonly property string download: "\u0099" + readonly property string downloadUnavailable: "\u009A" + readonly property string downloadUpdate: "\u009B" + readonly property string downloadcsv_large: "\u009D" + readonly property string downloadcsv_medium: "\u009E" + readonly property string downloaded: "\u009F" + readonly property string downloadjson_large: "\u00A0" + readonly property string downloadjson_medium: "\u00A1" + readonly property string dragmarks: "\u00A2" + readonly property string duplicate_small: "\u00A3" + readonly property string edit: "\u00A4" + readonly property string editComponent_large: "\u00A5" + readonly property string editComponent_small: "\u00A6" + readonly property string editLightOff_medium: "\u00A7" + readonly property string editLightOn_medium: "\u00A8" + readonly property string edit_medium: "\u00A9" + readonly property string edit_small: "\u00AA" + readonly property string effects: "\u00AB" + readonly property string events_small: "\u00AC" + readonly property string export_medium: "\u00AE" + readonly property string eyeDropper: "\u00AF" + readonly property string favorite: "\u00B0" + readonly property string fitAll_medium: "\u00B1" + readonly property string fitSelected_small: "\u00B2" + readonly property string fitSelection_medium: "\u00B3" + readonly property string fitToView_medium: "\u00B4" + readonly property string flowAction: "\u00B5" + readonly property string flowTransition: "\u00B6" + readonly property string fontStyleBold: "\u00B7" + readonly property string fontStyleItalic: "\u00B8" + readonly property string fontStyleStrikethrough: "\u00B9" + readonly property string fontStyleUnderline: "\u00BA" + readonly property string forward_medium: "\u00BB" + readonly property string globalOrient_medium: "\u00BC" + readonly property string gradient: "\u00BD" + readonly property string gridView: "\u00BE" + readonly property string grid_medium: "\u00BF" + readonly property string group_small: "\u00C0" + readonly property string help: "\u00C1" + readonly property string home_large: "\u00C2" + readonly property string idAliasOff: "\u00C3" + readonly property string idAliasOn: "\u00C4" + readonly property string import_medium: "\u00C5" + readonly property string imported: "\u00C6" + readonly property string importedModels_small: "\u00C7" + readonly property string infinity: "\u00C8" + readonly property string invisible_medium: "\u00C9" + readonly property string invisible_small: "\u00CA" + readonly property string jumpToCode_medium: "\u00CB" + readonly property string jumpToCode_small: "\u00CC" + readonly property string keyframe: "\u00CD" + readonly property string languageList_medium: "\u00CE" + readonly property string layouts_small: "\u00CF" + readonly property string lights_small: "\u00D0" + readonly property string linear_medium: "\u00D1" + readonly property string linkTriangle: "\u00D2" + readonly property string linked: "\u00D3" + readonly property string listView: "\u00D4" + readonly property string listView_medium: "\u00D5" + readonly property string list_medium: "\u00D6" + readonly property string localOrient_medium: "\u00D7" + readonly property string lockOff: "\u00D8" + readonly property string lockOn: "\u00D9" + readonly property string loopPlayback_medium: "\u00DA" + readonly property string materialBrowser_medium: "\u00DB" + readonly property string materialPreviewEnvironment: "\u00DC" + readonly property string materialPreviewModel: "\u00DD" + readonly property string material_medium: "\u00DE" + readonly property string maxBar_small: "\u00DF" + readonly property string mergeCells: "\u00E0" + readonly property string merge_small: "\u00E1" + readonly property string minus: "\u00E2" + readonly property string mirror: "\u00E3" + readonly property string more_medium: "\u00E4" + readonly property string mouseArea_small: "\u00E5" + readonly property string moveDown_medium: "\u00E6" + readonly property string moveInwards_medium: "\u00E7" + readonly property string moveUp_medium: "\u00E8" + readonly property string moveUpwards_medium: "\u00E9" + readonly property string move_medium: "\u00EA" + readonly property string newMaterial: "\u00EB" + readonly property string nextFile_large: "\u00EC" + readonly property string normalBar_small: "\u00ED" + readonly property string openLink: "\u00EE" + readonly property string openMaterialBrowser: "\u00EF" + readonly property string orientation: "\u00F0" + readonly property string orthCam_medium: "\u00F1" + readonly property string orthCam_small: "\u00F2" + readonly property string paddingEdge: "\u00F3" + readonly property string paddingFrame: "\u00F4" + readonly property string particleAnimation_medium: "\u00F5" + readonly property string pasteStyle: "\u00F6" + readonly property string paste_small: "\u00F7" + readonly property string pause: "\u00F8" + readonly property string pause_medium: "\u00F9" + readonly property string perspectiveCam_medium: "\u00FA" + readonly property string perspectiveCam_small: "\u00FB" + readonly property string pin: "\u00FC" + readonly property string plane_medium: "\u00FD" + readonly property string plane_small: "\u00FE" + readonly property string play: "\u00FF" + readonly property string playFill_medium: "\u0100" + readonly property string playOutline_medium: "\u0101" + readonly property string plus: "\u0102" + readonly property string pointLight_small: "\u0103" + readonly property string positioners_small: "\u0104" + readonly property string previewEnv_medium: "\u0105" + readonly property string previousFile_large: "\u0106" + readonly property string promote: "\u0107" + readonly property string properties_medium: "\u0108" + readonly property string readOnly: "\u0109" + readonly property string recordFill_medium: "\u010A" + readonly property string recordOutline_medium: "\u010B" + readonly property string redo: "\u010C" + readonly property string reload_medium: "\u010D" + readonly property string remove_medium: "\u010E" + readonly property string remove_small: "\u010F" + readonly property string rename_small: "\u0110" + readonly property string replace_small: "\u0111" + readonly property string resetView_small: "\u0112" + readonly property string restartParticles_medium: "\u0113" + readonly property string reverseOrder_medium: "\u0114" + readonly property string roatate_medium: "\u0115" + readonly property string rotationFill: "\u0116" + readonly property string rotationOutline: "\u0117" + readonly property string runProjFill_large: "\u0118" + readonly property string runProjOutline_large: "\u0119" + readonly property string s_anchors: "\u011A" + readonly property string s_annotations: "\u011B" + readonly property string s_arrange: "\u011C" + readonly property string s_boundingBox: "\u011D" + readonly property string s_component: "\u011E" + readonly property string s_connections: "\u011F" + readonly property string s_edit: "\u0120" + readonly property string s_enterComponent: "\u0121" + readonly property string s_eventList: "\u0122" + readonly property string s_group: "\u0123" + readonly property string s_layouts: "\u0124" + readonly property string s_merging: "\u0125" + readonly property string s_mouseArea: "\u0126" + readonly property string s_positioners: "\u0127" + readonly property string s_selection: "\u0128" + readonly property string s_snapping: "\u0129" + readonly property string s_timeline: "\u012A" + readonly property string s_visibility: "\u012B" + readonly property string saveLogs_medium: "\u012C" + readonly property string scale_medium: "\u012D" + readonly property string search: "\u012E" + readonly property string search_small: "\u012F" + readonly property string sectionToggle: "\u0130" + readonly property string selectFill_medium: "\u0131" + readonly property string selectOutline_medium: "\u0132" + readonly property string selectParent_small: "\u0133" + readonly property string selection_small: "\u0134" + readonly property string settings_medium: "\u0135" + readonly property string signal_small: "\u0136" + readonly property string snapping_conf_medium: "\u0137" + readonly property string snapping_medium: "\u0138" + readonly property string snapping_small: "\u0139" + readonly property string sortascending_medium: "\u013A" + readonly property string sortdescending_medium: "\u013B" + readonly property string sphere_medium: "\u013C" + readonly property string sphere_small: "\u013D" + readonly property string splitColumns: "\u013E" + readonly property string splitRows: "\u013F" + readonly property string splitScreen_medium: "\u0140" + readonly property string spotLight_small: "\u0141" + readonly property string stackedContainer_small: "\u0142" + readonly property string startNode: "\u0143" + readonly property string step_medium: "\u0144" + readonly property string stop_medium: "\u0145" + readonly property string tableView_medium: "\u0146" + readonly property string testIcon: "\u0147" + readonly property string textAlignBottom: "\u0148" + readonly property string textAlignCenter: "\u0149" + readonly property string textAlignJustified: "\u014A" + readonly property string textAlignLeft: "\u014B" + readonly property string textAlignMiddle: "\u014C" + readonly property string textAlignRight: "\u014D" + readonly property string textAlignTop: "\u014E" + readonly property string textBulletList: "\u014F" + readonly property string textFullJustification: "\u0150" + readonly property string textNumberedList: "\u0151" + readonly property string textures_medium: "\u0152" + readonly property string tickIcon: "\u0153" + readonly property string tickMark_small: "\u0154" + readonly property string timeline_small: "\u0155" + readonly property string toEndFrame_medium: "\u0156" + readonly property string toNextFrame_medium: "\u0157" + readonly property string toPrevFrame_medium: "\u0158" + readonly property string toStartFrame_medium: "\u0159" + readonly property string topToolbar_annotations: "\u015A" + readonly property string topToolbar_closeFile: "\u015B" + readonly property string topToolbar_designMode: "\u015C" + readonly property string topToolbar_enterComponent: "\u015D" + readonly property string topToolbar_home: "\u015E" + readonly property string topToolbar_makeComponent: "\u015F" + readonly property string topToolbar_navFile: "\u0160" + readonly property string topToolbar_runProject: "\u0161" + readonly property string translationCreateFiles: "\u0162" + readonly property string translationCreateReport: "\u0163" + readonly property string translationExport: "\u0164" + readonly property string translationImport: "\u0165" + readonly property string translationSelectLanguages: "\u0166" + readonly property string translationTest: "\u0167" + readonly property string transparent: "\u0168" + readonly property string triState: "\u0169" + readonly property string triangleArcA: "\u016A" + readonly property string triangleArcB: "\u016B" + readonly property string triangleCornerA: "\u016C" + readonly property string triangleCornerB: "\u016D" + readonly property string unLinked: "\u016E" + readonly property string undo: "\u016F" + readonly property string unify_medium: "\u0170" + readonly property string unpin: "\u0171" + readonly property string upDownIcon: "\u0172" + readonly property string upDownSquare2: "\u0173" + readonly property string updateAvailable_medium: "\u0174" + readonly property string updateContent_medium: "\u0175" + readonly property string uploadcsv_large: "\u0176" + readonly property string uploadcsv_medium: "\u0177" + readonly property string uploadjson_large: "\u0178" + readonly property string uploadjson_medium: "\u0179" + readonly property string visibilityOff: "\u017A" + readonly property string visibilityOn: "\u017B" + readonly property string visible_medium: "\u017C" + readonly property string visible_small: "\u017D" + readonly property string warning_medium: "\u017E" + readonly property string wildcard: "\u017F" + readonly property string wizardsAutomotive: "\u0180" + readonly property string wizardsDesktop: "\u0181" + readonly property string wizardsGeneric: "\u0182" + readonly property string wizardsMcuEmpty: "\u0183" + readonly property string wizardsMcuGraph: "\u0184" + readonly property string wizardsMobile: "\u0185" + readonly property string wizardsUnknown: "\u0186" + readonly property string zoomAll: "\u0187" + readonly property string zoomIn: "\u0188" + readonly property string zoomIn_medium: "\u0189" + readonly property string zoomOut: "\u018A" + readonly property string zoomOut_medium: "\u018B" + readonly property string zoomSelection: "\u018C" readonly property font iconFont: Qt.font({ "family": controlIcons.name, diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/icons.ttf b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/icons.ttf index 5d166dd13cd632ed2d80937d743151b5059f8eec..de1490f58c595a0a5e85bd0b56db4fc4a181f358 100644 GIT binary patch delta 3172 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 X|1&T#K=MArJvOa8NKS|4`0Nh=98#2e diff --git a/src/plugins/qmldesigner/components/componentcore/theme.h b/src/plugins/qmldesigner/components/componentcore/theme.h index 21d4d6fc7fb..f363df71b26 100644 --- a/src/plugins/qmldesigner/components/componentcore/theme.h +++ b/src/plugins/qmldesigner/components/componentcore/theme.h @@ -26,6 +26,7 @@ public: addColumnAfter, addColumnBefore, addFile, + addGroup_medium, addRowAfter, addRowBefore, addTable, @@ -69,9 +70,11 @@ public: arrange_small, arrow_small, assign, + assignTo, attach_medium, back_medium, backspace_small, + bakeLights_medium, bevelAll, bevelCorner, bezier_medium, @@ -196,6 +199,7 @@ public: linkTriangle, linked, listView, + listView_medium, list_medium, localOrient_medium, lockOff, @@ -231,6 +235,7 @@ public: pasteStyle, paste_small, pause, + pause_medium, perspectiveCam_medium, perspectiveCam_small, pin, @@ -307,6 +312,7 @@ public: startNode, step_medium, stop_medium, + tableView_medium, testIcon, textAlignBottom, textAlignCenter, @@ -362,6 +368,7 @@ public: visibilityOn, visible_medium, visible_small, + warning_medium, wildcard, wizardsAutomotive, wizardsDesktop, From d9ab8e8befd5a509225febf00a08e0b1193eaa79 Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Wed, 8 Nov 2023 16:03:23 +0200 Subject: [PATCH 181/242] QmlDesigner: Update light baking and effect maker pause icons Also hide the code icon from effect maker Change-Id: I1045a0718b5b86ef13164e215e5f0f1420487497 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Thomas Hartmann Reviewed-by: Amr Elsayed --- share/qtcreator/qmldesigner/designericons.json | 3 +++ .../qmldesigner/effectMakerQmlSources/EffectMaker.qml | 1 + .../qmldesigner/effectMakerQmlSources/EffectMakerPreview.qml | 5 ++--- .../qmldesigner/components/componentcore/designericons.h | 1 + src/plugins/qmldesigner/components/edit3d/edit3dview.cpp | 2 +- 5 files changed, 8 insertions(+), 4 deletions(-) diff --git a/share/qtcreator/qmldesigner/designericons.json b/share/qtcreator/qmldesigner/designericons.json index 715454bea22..85c76066bb7 100644 --- a/share/qtcreator/qmldesigner/designericons.json +++ b/share/qtcreator/qmldesigner/designericons.json @@ -221,6 +221,9 @@ "EditColorIcon": { "iconName": "colorSelection_medium" }, + "BakeLightIcon": { + "iconName": "bakeLights_medium" + }, "EditLightIcon": { "Off": { "iconName": "editLightOff_medium" diff --git a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMaker.qml b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMaker.qml index b11c1cce796..a94987b9f6d 100644 --- a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMaker.qml +++ b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMaker.qml @@ -60,6 +60,7 @@ Item { style: StudioTheme.Values.viewBarButtonStyle buttonIcon: StudioTheme.Constants.code tooltip: qsTr("Open Shader in Code Editor") + visible: false // TODO: to be implemented onClicked: {} // TODO } diff --git a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMakerPreview.qml b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMakerPreview.qml index bf5fc4a154a..cd2fde0fee1 100644 --- a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMakerPreview.qml +++ b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMakerPreview.qml @@ -132,11 +132,10 @@ Column { } } - //TODO: Change the icon to outlined version HelperWidgets.AbstractButton { style: StudioTheme.Values.viewBarButtonStyle - buttonIcon: previewAnimationRunning - ? StudioTheme.Constants.pause : StudioTheme.Constants.playOutline_medium + buttonIcon: previewAnimationRunning ? StudioTheme.Constants.pause_medium + : StudioTheme.Constants.playOutline_medium tooltip: qsTr("Play Animation") onClicked: { diff --git a/src/plugins/qmldesigner/components/componentcore/designericons.h b/src/plugins/qmldesigner/components/componentcore/designericons.h index 26d6ba76771..3cb5a1e9731 100644 --- a/src/plugins/qmldesigner/components/componentcore/designericons.h +++ b/src/plugins/qmldesigner/components/componentcore/designericons.h @@ -54,6 +54,7 @@ public: AnnotationIcon, ArrangeIcon, BackspaceIcon, + BakeLightIcon, CameraIcon, CameraOrthographicIcon, CameraPerspectiveIcon, diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp index 5c1b0d39a3c..9cf849bc4ce 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp +++ b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp @@ -1029,7 +1029,7 @@ void Edit3DView::createEdit3DActions() createSeekerSliderAction(); m_bakeLightsAction = std::make_unique( - toolbarIcon(DesignerIcons::EditLightIcon), //: TODO placeholder icon + toolbarIcon(DesignerIcons::BakeLightIcon), this, bakeLightsTrigger); From 900889466472b5f8c0e736711804a115ac16bcbc Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Wed, 8 Nov 2023 12:24:18 +0200 Subject: [PATCH 182/242] QmlDesigner: Remove HelperWidgets.RegExpValidator from CollectionEditor Change-Id: I24f86c32f1e01e17b51c809da82e625b6a6d70c2 Reviewed-by: Mahmoud Badri Reviewed-by: Qt CI Patch Build Bot Reviewed-by: --- .../CollectionDetailsToolbar.qml | 4 ++-- .../collectionEditorQmlSource/CollectionItem.qml | 8 ++++---- .../qmldesigner/collectionEditorQmlSource/CsvImport.qml | 8 ++++---- .../collectionEditorQmlSource/EditPropertyDialog.qml | 4 ++-- .../qmldesigner/collectionEditorQmlSource/JsonImport.qml | 4 ++-- .../collectionEditorQmlSource/ModelSourceItem.qml | 4 ++-- .../collectionEditorQmlSource/NewCollectionDialog.qml | 4 ++-- 7 files changed, 18 insertions(+), 18 deletions(-) diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsToolbar.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsToolbar.qml index 8f8a0b24416..b05725f85ab 100644 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsToolbar.qml +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsToolbar.qml @@ -165,9 +165,9 @@ Item { width: root.iconHeight } - HelperWidgets.RegExpValidator { + RegularExpressionValidator { id: nameValidator - regExp: /^\w+$/ + regularExpression: /^\w+$/ } StudioControls.Dialog { diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionItem.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionItem.qml index 59857c8e172..ac254fe7f8b 100644 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionItem.qml +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionItem.qml @@ -156,8 +156,8 @@ Item { } Item { // spacer - width: 1 - height: 20 + Layout.fillWidth: true + Layout.preferredHeight: 20 } RowLayout { @@ -250,9 +250,9 @@ Item { } } - HelperWidgets.RegExpValidator { + RegularExpressionValidator { id: newNameValidator - regExp: /^\w+$/ + regularExpression: /^\w+$/ } states: [ diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CsvImport.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CsvImport.qml index f58150b3b57..eb46587316e 100644 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CsvImport.qml +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CsvImport.qml @@ -32,9 +32,9 @@ StudioControls.Dialog { fileName.text = "" } - HelperWidgets.RegExpValidator { + RegularExpressionValidator { id: fileNameValidator - regExp: /^(\w[^*> Date: Thu, 2 Nov 2023 01:30:40 +0200 Subject: [PATCH 183/242] QmlDesigner: Warn of possible data loss if column data type is changed Task-number: QDS-11013 Change-Id: I388492480355fc5feb14ecc940aa54f9dfc8b2f6 Reviewed-by: Mahmoud Badri Reviewed-by: Ali Kianian --- .../EditPropertyDialog.qml | 38 ++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/EditPropertyDialog.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/EditPropertyDialog.qml index ae71e74fc32..aded1c5d63d 100644 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/EditPropertyDialog.qml +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/EditPropertyDialog.qml @@ -2,6 +2,7 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 import QtQuick +import QtQuick.Layouts import StudioTheme 1.0 as StudioTheme import StudioControls 1.0 as StudioControls import HelperWidgets 2.0 as HelperWidgets @@ -119,7 +120,42 @@ StudioControls.Dialog { Item { // spacer width: 1 - height: 20 + height: 10 + } + + Rectangle { + id: warningBox + + visible: propertyType.initialType !== propertyType.currentText + width: parent.width + height: warning.implicitHeight + color: "transparent" + border.color: StudioTheme.Values.themeWarning + + RowLayout { + id: warning + + anchors.fill: parent + + HelperWidgets.IconLabel { + icon: StudioTheme.Constants.warning + Layout.leftMargin: 10 + } + + Text { + text: qsTr("Conversion from %1 to %2 may lead to irreversible data loss").arg(propertyType.initialType).arg(propertyType.currentText) + color: StudioTheme.Values.themeTextColor + wrapMode: Text.WordWrap + Layout.fillWidth: true + Layout.margins: 8 + } + } + } + + Item { // spacer + visible: warningBox.visible + width: 1 + height: 10 } Row { From 036963de029deef8e1f3faff70b4f5ea9617b8fe Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Fri, 3 Nov 2023 15:55:07 +0100 Subject: [PATCH 184/242] QmlDesigner: Add signals of dynamic properties MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We have to take into account that signal themselves are considered dynamic properties, but do not require the changed suffix. Task-number: QDS-10977 Change-Id: I6af83586eee1b8f37e0510839baecee42972494c Reviewed-by: Henning Gründl Reviewed-by: Qt CI Patch Build Bot --- .../connectioneditor/propertytreemodel.cpp | 16 +++++++++++++++- .../connectioneditor/propertytreemodel.h | 1 + 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/plugins/qmldesigner/components/connectioneditor/propertytreemodel.cpp b/src/plugins/qmldesigner/components/connectioneditor/propertytreemodel.cpp index 073b8214927..3ae89a3185f 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/propertytreemodel.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/propertytreemodel.cpp @@ -494,7 +494,9 @@ const std::vector PropertyTreeModel::sortedAndFilteredPropertyName return returnValue; if (m_type == SignalType) { - returnValue = sortedAndFilteredSignalNames(modelNode.metaInfo()); + auto list = sortedAndFilteredSignalNames(modelNode.metaInfo()); + returnValue = getDynamicSignals(modelNode); + std::move(list.begin(), list.end(), std::back_inserter(returnValue)); } else if (m_type == SlotType) { returnValue = sortedAndFilteredSlotNames(modelNode.metaInfo()); } else { @@ -549,6 +551,18 @@ const std::vector PropertyTreeModel::getDynamicProperties( return Utils::sorted(std::vector(filtered.begin(), filtered.end())); } +const std::vector PropertyTreeModel::getDynamicSignals(const ModelNode &modelNode) const +{ + QList list = Utils::transform(modelNode.dynamicProperties(), + [](const AbstractProperty &property) { + if (property.isSignalDeclarationProperty()) + return property.name(); + + return PropertyName(property.name() + "Changed"); + }); + return Utils::sorted(std::vector(list.begin(), list.end())); +} + const std::vector PropertyTreeModel::sortedAndFilteredPropertyNames( const NodeMetaInfo &metaInfo, bool recursive) const { diff --git a/src/plugins/qmldesigner/components/connectioneditor/propertytreemodel.h b/src/plugins/qmldesigner/components/connectioneditor/propertytreemodel.h index b93820ac7b9..df17c112da9 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/propertytreemodel.h +++ b/src/plugins/qmldesigner/components/connectioneditor/propertytreemodel.h @@ -93,6 +93,7 @@ private: const ModelNode &modelNode) const; const std::vector getDynamicProperties(const ModelNode &modelNode) const; + const std::vector getDynamicSignals(const ModelNode &modelNode) const; const std::vector sortedAndFilteredPropertyNames(const NodeMetaInfo &metaInfo, bool recursive = false) const; From ad49605ea9c252e57f97b25a4cea993216babfc9 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Wed, 8 Nov 2023 15:37:38 +0100 Subject: [PATCH 185/242] QmlDesigner: Highlight selected node in code editor MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We also highlight the drag target when using drag and drop. Task-number: QDS-10869 Change-Id: Ic396848f93deaa3cce899c2cf431d5a7f5a940f5 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Henning Gründl --- .../texteditor/texteditorwidget.cpp | 35 +++++++++++++++++++ .../components/texteditor/texteditorwidget.h | 2 ++ 2 files changed, 37 insertions(+) diff --git a/src/plugins/qmldesigner/components/texteditor/texteditorwidget.cpp b/src/plugins/qmldesigner/components/texteditor/texteditorwidget.cpp index b344985476d..93976f10d9e 100644 --- a/src/plugins/qmldesigner/components/texteditor/texteditorwidget.cpp +++ b/src/plugins/qmldesigner/components/texteditor/texteditorwidget.cpp @@ -116,10 +116,26 @@ void TextEditorWidget::jumpToModelNode(const ModelNode &modelNode) int line, column; m_textEditor->editorWidget()->convertPosition(nodeOffset, &line, &column); m_textEditor->editorWidget()->gotoLine(line + 1, column); + + highlightToModelNode(modelNode); } m_blockCursorSelectionSynchronisation = false; } +void TextEditorWidget::highlightToModelNode(const ModelNode &modelNode) +{ + RewriterView *rewriterView = m_textEditorView->model()->rewriterView(); + const int nodeOffset = rewriterView->nodeOffset(modelNode); + if (nodeOffset > 0) { + int line, column; + m_textEditor->editorWidget()->convertPosition(nodeOffset, &line, &column); + + QTextCursor cursor = m_textEditor->textCursor(); + cursor.setPosition(nodeOffset); + m_textEditor->editorWidget()->updateFoldingHighlight(cursor); + } +} + void TextEditorWidget::jumpTextCursorToSelectedModelNode() { if (m_blockRoundTrip) @@ -217,6 +233,10 @@ bool TextEditorWidget::eventFilter(QObject *, QEvent *event) return true; } } + } else if (event->type() == QEvent::FocusIn) { + m_textEditor->editorWidget()->updateFoldingHighlight(QTextCursor()); + } else if (event->type() == QEvent::FocusOut) { + m_textEditor->editorWidget()->updateFoldingHighlight(QTextCursor()); } return false; } @@ -239,6 +259,20 @@ void TextEditorWidget::dragEnterEvent(QDragEnterEvent *dragEnterEvent) } } +void TextEditorWidget::dragMoveEvent(QDragMoveEvent *dragMoveEvent) +{ + QTextCursor cursor = m_textEditor->editorWidget()->cursorForPosition(dragMoveEvent->pos()); + const int cursorPosition = cursor.position(); + RewriterView *rewriterView = m_textEditorView->model()->rewriterView(); + + QTC_ASSERT(rewriterView, return ); + ModelNode modelNode = rewriterView->nodeAtTextCursorPosition(cursorPosition); + + if (!modelNode.isValid()) + return; + highlightToModelNode(modelNode); +} + void TextEditorWidget::dropEvent(QDropEvent *dropEvent) { QTextCursor cursor = m_textEditor->editorWidget()->cursorForPosition(dropEvent->pos()); @@ -323,6 +357,7 @@ void TextEditorWidget::dropEvent(QDropEvent *dropEvent) actionManager.handleExternalAssetsDrop(dropEvent->mimeData()); } m_textEditorView->model()->endDrag(); + m_textEditor->editorWidget()->updateFoldingHighlight(QTextCursor()); } } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/texteditor/texteditorwidget.h b/src/plugins/qmldesigner/components/texteditor/texteditorwidget.h index 8178050cf71..c6f23110af0 100644 --- a/src/plugins/qmldesigner/components/texteditor/texteditorwidget.h +++ b/src/plugins/qmldesigner/components/texteditor/texteditorwidget.h @@ -47,10 +47,12 @@ public: void setBlockCursorSelectionSynchronisation(bool b); void jumpToModelNode(const ModelNode &modelNode); + void highlightToModelNode(const ModelNode &modelNode); protected: bool eventFilter(QObject *object, QEvent *event) override; void dragEnterEvent(QDragEnterEvent *dragEnterEvent) override; + void dragMoveEvent(QDragMoveEvent *dragMoveEvent) override; void dropEvent(QDropEvent *dropEvent) override; private: From 070022e9e276547564202498a7258791252591b5 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Wed, 8 Nov 2023 15:34:06 +0100 Subject: [PATCH 186/242] TextEditor: Allow to programmatically highlight a block by cursor MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This can be used in QmlDesigner to highlight the currently selected QML object. Change-Id: I9907d54f767c7d034739d4111a46cd994c961426 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Henning Gründl Reviewed-by: Thomas Hartmann --- src/plugins/texteditor/texteditor.cpp | 21 +++++++++++++++------ src/plugins/texteditor/texteditor.h | 1 + 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/src/plugins/texteditor/texteditor.cpp b/src/plugins/texteditor/texteditor.cpp index 4905d745f9a..1a30c3af98e 100644 --- a/src/plugins/texteditor/texteditor.cpp +++ b/src/plugins/texteditor/texteditor.cpp @@ -6240,9 +6240,6 @@ void TextEditorWidget::updateFoldingHighlight(const QPoint &pos) QTextCursor cursor = cursorForPosition(QPoint(0, pos.y())); // Update which folder marker is highlighted - const int highlightBlockNumber = d->extraAreaHighlightFoldedBlockNumber; - d->extraAreaHighlightFoldedBlockNumber = -1; - int boxWidth = 0; if (TextEditorSettings::fontSettings().relativeLineSpacing() == 100) boxWidth = foldBoxWidth(fontMetrics()); @@ -6250,13 +6247,25 @@ void TextEditorWidget::updateFoldingHighlight(const QPoint &pos) boxWidth = foldBoxWidth(); if (pos.x() > extraArea()->width() - boxWidth) { - d->extraAreaHighlightFoldedBlockNumber = cursor.blockNumber(); + updateFoldingHighlight(cursor); } else if (d->m_displaySettings.m_highlightBlocks) { QTextCursor cursor = textCursor(); - d->extraAreaHighlightFoldedBlockNumber = cursor.blockNumber(); + updateFoldingHighlight(cursor); + } else { + updateFoldingHighlight(QTextCursor()); } +} - if (highlightBlockNumber != d->extraAreaHighlightFoldedBlockNumber) +void TextEditorWidget::updateFoldingHighlight(const QTextCursor &cursor) +{ + const int highlightBlockNumber = d->extraAreaHighlightFoldedBlockNumber; + const bool curserIsNull = !cursor.isNull(); + if (curserIsNull) + d->extraAreaHighlightFoldedBlockNumber = cursor.blockNumber(); + else + d->extraAreaHighlightFoldedBlockNumber = -1; + + if (curserIsNull || (highlightBlockNumber != d->extraAreaHighlightFoldedBlockNumber)) d->m_highlightBlocksTimer.start(d->m_highlightBlocksInfo.isEmpty() ? 120 : 0); } diff --git a/src/plugins/texteditor/texteditor.h b/src/plugins/texteditor/texteditor.h index 0abe3305639..cc7f1ed252a 100644 --- a/src/plugins/texteditor/texteditor.h +++ b/src/plugins/texteditor/texteditor.h @@ -275,6 +275,7 @@ public: virtual void extraAreaContextMenuEvent(QContextMenuEvent *); virtual void extraAreaMouseEvent(QMouseEvent *); void updateFoldingHighlight(const QPoint &pos); + void updateFoldingHighlight(const QTextCursor &cursor); void setLanguageSettingsId(Utils::Id settingsId); Utils::Id languageSettingsId() const; From 17d386f25b706796c28478d4f9ab33d6b6f5c852 Mon Sep 17 00:00:00 2001 From: Henning Gruendl Date: Wed, 8 Nov 2023 18:03:07 +0100 Subject: [PATCH 187/242] QmlDesigner: Remove vertical scroll bar gap Since we have the transient scroll bars in place, we usually let them overlap with the content as they will fade out when not in use. Change-Id: I686d7c374066063c7bce8eca35646d704c633e8f Reviewed-by: Thomas Hartmann Reviewed-by: Qt CI Patch Build Bot --- .../itemLibraryQmlSources/ItemsView.qml | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/share/qtcreator/qmldesigner/itemLibraryQmlSources/ItemsView.qml b/share/qtcreator/qmldesigner/itemLibraryQmlSources/ItemsView.qml index a90fbaaa15f..cf5049d6473 100644 --- a/share/qtcreator/qmldesigner/itemLibraryQmlSources/ItemsView.qml +++ b/share/qtcreator/qmldesigner/itemLibraryQmlSources/ItemsView.qml @@ -237,9 +237,7 @@ Item { Repeater { model: ItemLibraryBackend.itemLibraryModel // to be set in Qml context delegate: HelperWidgets.Section { - width: itemsView.width - - (verticalScrollView.verticalScrollBarVisible - ? verticalScrollView.verticalThickness : 0) + width: itemsView.width caption: importName visible: importVisible sectionHeight: 30 @@ -270,9 +268,7 @@ Item { Repeater { model: categoryModel delegate: HelperWidgets.Section { - width: itemsView.width - - (verticalScrollView.verticalScrollBarVisible - ? verticalScrollView.verticalThickness : 0) + width: itemsView.width sectionBackgroundColor: "transparent" showTopSeparator: index > 0 hideHeader: categoryModel.rowCount() <= 1 @@ -351,9 +347,7 @@ Item { Repeater { model: ItemLibraryBackend.itemLibraryModel // to be set in Qml context delegate: HelperWidgets.Section { - width: 265 - - (horizontalScrollView.verticalScrollBarVisible - ? horizontalScrollView.verticalThickness : 0) + width: 265 caption: importName visible: importVisible sectionHeight: 30 @@ -384,9 +378,7 @@ Item { Repeater { model: categoryModel delegate: Rectangle { - width: 265 - - (horizontalScrollView.verticalScrollBarVisible - ? horizontalScrollView.verticalThickness : 0) + width: 265 height: 25 visible: categoryVisible border.width: StudioTheme.Values.border From b2c4dd681159c75da68f2b151353fd0c49b0449a Mon Sep 17 00:00:00 2001 From: Henning Gruendl Date: Wed, 8 Nov 2023 18:29:54 +0100 Subject: [PATCH 188/242] QmlDesigner: Remove vertical scroll bar gap Since we have the transient scroll bars in place, we usually let them overlap with the content as they will fade out when not in use. Change-Id: I49c06468d467b7b1b3af954f3319b6bb0b36d324 Reviewed-by: Thomas Hartmann Reviewed-by: Qt CI Patch Build Bot --- .../assetsLibraryQmlSources/AssetDelegate.qml | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/share/qtcreator/qmldesigner/assetsLibraryQmlSources/AssetDelegate.qml b/share/qtcreator/qmldesigner/assetsLibraryQmlSources/AssetDelegate.qml index edaf038ac8d..96690fd9690 100644 --- a/share/qtcreator/qmldesigner/assetsLibraryQmlSources/AssetDelegate.qml +++ b/share/qtcreator/qmldesigner/assetsLibraryQmlSources/AssetDelegate.qml @@ -32,12 +32,7 @@ TreeViewDelegate { readonly property int __dirItemHeight: 21 implicitHeight: root.__isDirectory ? root.__dirItemHeight : root.__fileItemHeight - implicitWidth: { - if (root.assetsView.verticalScrollBar.scrollBarVisible) - return root.assetsView.width - root.indentation - root.assetsView.verticalScrollBar.width - else - return root.assetsView.width - root.indentation - } + implicitWidth: root.assetsView.width leftMargin: root.__isDirectory ? 0 : thumbnailImage.width @@ -88,7 +83,7 @@ TreeViewDelegate { background: Rectangle { id: bg - x: root.indentation * root.depth + x: root.indentation * (root.depth - 1) width: root.implicitWidth - bg.x color: { From a923d3ca4a81dd278647f1d1874a345ff1ce9c91 Mon Sep 17 00:00:00 2001 From: Shrief Gabr Date: Wed, 8 Nov 2023 18:37:55 +0200 Subject: [PATCH 189/242] QmlDesigner: Reset column data type when context menu is closed Task-number: QDS-11101 Change-Id: I60c35ac01107c3ff79f2b637294997bfa62f4f5e Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Ali Kianian Reviewed-by: Mahmoud Badri --- .../collectionEditorQmlSource/EditPropertyDialog.qml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/EditPropertyDialog.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/EditPropertyDialog.qml index aded1c5d63d..c74fe1cb237 100644 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/EditPropertyDialog.qml +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/EditPropertyDialog.qml @@ -46,6 +46,11 @@ StudioControls.Dialog { root.model.setPropertyType(root.__propertyIndex, propertyType.currentText, forceChangeType.checked) } + onRejected: { + let currentDatatype = propertyType.initialType + propertyType.currentIndex = propertyType.find(currentDatatype) + } + contentItem: Column { spacing: 2 From 0cbda147a86da737824376130cdd5bed0db5060e Mon Sep 17 00:00:00 2001 From: Amr Essam Date: Wed, 8 Nov 2023 16:12:35 +0200 Subject: [PATCH 190/242] QmlDesigner: Export effect maker composition qep project into assets Meanwhile, composition resources are not yet exported Task-number: QDS-10500 Change-Id: I3687d1d62a64472c7ec84716c584dced5fbb6a85 Reviewed-by: Mahmoud Badri Reviewed-by: Qt CI Patch Build Bot --- .../effectMakerQmlSources/EffectMaker.qml | 2 +- .../effectMakerQmlSources/SaveDialog.qml | 13 +- .../effectmakernew/compositionnode.cpp | 11 ++ src/plugins/effectmakernew/compositionnode.h | 6 + .../effectmakernew/effectmakermodel.cpp | 166 ++++++++++++++++++ src/plugins/effectmakernew/effectmakermodel.h | 2 + .../componentcore/modelnodeoperations.cpp | 6 + .../componentcore/modelnodeoperations.h | 2 +- 8 files changed, 203 insertions(+), 5 deletions(-) diff --git a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMaker.qml b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMaker.qml index a94987b9f6d..2430739c469 100644 --- a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMaker.qml +++ b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMaker.qml @@ -19,7 +19,7 @@ Item { SaveDialog { id: saveDialog anchors.centerIn: parent - onAccepted: print("TODO: export and save effect files") + onAccepted: EffectMakerBackend.effectMakerModel.exportComposition(saveDialog.compositionName) } Column { diff --git a/share/qtcreator/qmldesigner/effectMakerQmlSources/SaveDialog.qml b/share/qtcreator/qmldesigner/effectMakerQmlSources/SaveDialog.qml index 309dd433395..f04a81db9f9 100644 --- a/share/qtcreator/qmldesigner/effectMakerQmlSources/SaveDialog.qml +++ b/share/qtcreator/qmldesigner/effectMakerQmlSources/SaveDialog.qml @@ -17,8 +17,10 @@ StudioControls.Dialog { modal: true implicitWidth: 250 + property string compositionName: null + onOpened: { - nameText.text = "" + nameText.text = "" //TODO: Generate unique name emptyText.opacity = 0 nameText.forceActiveFocus() } @@ -47,7 +49,9 @@ StudioControls.Dialog { validator: validator onTextChanged: { - emptyText.opacity = nameText.text === "" ? 1 : 0 + let validator = /^[A-Z]\w{2,}[A-Za-z0-9_]*$/ + emptyText.visible = text.length > 0 && !validator.test(text) + btnSave.enabled = !emptyText.visible } Keys.onEnterPressed: btnSave.onClicked() Keys.onReturnPressed: btnSave.onClicked() @@ -76,7 +80,10 @@ StudioControls.Dialog { text: qsTr("Save") enabled: nameText.text !== "" - onClicked: root.accept() + onClicked: { + root.compositionName = nameText.text + root.accept() //TODO: Check if name is unique + } } HelperWidgets.Button { diff --git a/src/plugins/effectmakernew/compositionnode.cpp b/src/plugins/effectmakernew/compositionnode.cpp index a57ac74e94f..796b06151f9 100644 --- a/src/plugins/effectmakernew/compositionnode.cpp +++ b/src/plugins/effectmakernew/compositionnode.cpp @@ -104,6 +104,7 @@ void CompositionNode::parse(const QString &qenPath) for (const auto /*QJsonValueRef*/ &prop : jsonProps) { const auto uniform = new Uniform(prop.toObject(), qenPath); m_unifomrsModel.addUniform(uniform); + m_uniforms.append(uniform); g_propertyData.insert(uniform->name(), uniform->value()); } @@ -123,5 +124,15 @@ void CompositionNode::parse(const QString &qenPath) } } +QList CompositionNode::uniforms() const +{ + return m_uniforms; +} + +QString CompositionNode::name() const +{ + return m_name; +} + } // namespace EffectMaker diff --git a/src/plugins/effectmakernew/compositionnode.h b/src/plugins/effectmakernew/compositionnode.h index 2637bfb3879..37203ef4219 100644 --- a/src/plugins/effectmakernew/compositionnode.h +++ b/src/plugins/effectmakernew/compositionnode.h @@ -39,6 +39,10 @@ public: bool isEnabled() const; void setIsEnabled(bool newIsEnabled); + QString name() const; + + QList uniforms() const; + signals: void uniformsModelChanged(); void isEnabledChanged(); @@ -54,6 +58,8 @@ private: QStringList m_requiredNodes; bool m_isEnabled = true; + QList m_uniforms; + EffectMakerUniformsModel m_unifomrsModel; }; diff --git a/src/plugins/effectmakernew/effectmakermodel.cpp b/src/plugins/effectmakernew/effectmakermodel.cpp index 5a49dcb59ce..20879526bae 100644 --- a/src/plugins/effectmakernew/effectmakermodel.cpp +++ b/src/plugins/effectmakernew/effectmakermodel.cpp @@ -18,6 +18,8 @@ #include #include +#include + #include #include @@ -355,6 +357,170 @@ void EffectMakerModel::setEffectError(const QString &errorMessage, int type, int Q_EMIT effectErrorChanged(); } +QString variantAsDataString(const Uniform::Type type, const QVariant &variant) +{ + QString s; + switch (type) { + case Uniform::Type::Bool: + s = variant.toBool() ? QString("true") : QString("false"); + break; + case Uniform::Type::Int: + s = QString::number(variant.toInt()); + break; + case Uniform::Type::Float: + s = QString::number(variant.toDouble()); + break; + case Uniform::Type::Vec2: { + QStringList list; + QVector2D v2 = variant.value(); + list << QString::number(v2.x()); + list << QString::number(v2.y()); + s = list.join(", "); + break; + } + case Uniform::Type::Vec3: { + QStringList list; + QVector3D v3 = variant.value(); + list << QString::number(v3.x()); + list << QString::number(v3.y()); + list << QString::number(v3.z()); + s = list.join(", "); + break; + } + case Uniform::Type::Vec4: { + QStringList list; + QVector4D v4 = variant.value(); + list << QString::number(v4.x()); + list << QString::number(v4.y()); + list << QString::number(v4.z()); + list << QString::number(v4.w()); + s = list.join(", "); + break; + } + case Uniform::Type::Color: { + QStringList list; + QColor c = variant.value(); + list << QString::number(c.redF(), 'g', 3); + list << QString::number(c.greenF(), 'g', 3); + list << QString::number(c.blueF(), 'g', 3); + list << QString::number(c.alphaF(), 'g', 3); + s = list.join(", "); + break; + } + case Uniform::Type::Sampler: + case Uniform::Type::Define: { + s = variant.toString(); + break; + } + } + return s; +} + +QJsonObject nodeToJson(const CompositionNode &node) +{ + QJsonObject nodeObject; + nodeObject.insert("name", node.name()); + if (!node.description().isEmpty()) + nodeObject.insert("description", node.description()); + nodeObject.insert("enabled", node.isEnabled()); + nodeObject.insert("version", 1); + // Add properties + QJsonArray propertiesArray; + const QList uniforms = node.uniforms(); + for (const Uniform *uniform : uniforms) { + QJsonObject uniformObject; + uniformObject.insert("name", QString(uniform->name())); + QString type = Uniform::stringFromType(uniform->type()); + uniformObject.insert("type", type); + + QString value = variantAsDataString(uniform->type(), uniform->value()); + if (uniform->type() == Uniform::Type::Sampler) + value = QFileInfo(value).fileName(); + uniformObject.insert("value", value); + + QString defaultValue = variantAsDataString(uniform->type(), uniform->defaultValue()); + if (uniform->type() == Uniform::Type::Sampler) { + defaultValue = QFileInfo(value).fileName(); + if (uniform->enableMipmap()) + uniformObject.insert("enableMipmap", uniform->enableMipmap()); + } + uniformObject.insert("defaultValue", defaultValue); + if (!uniform->description().isEmpty()) + uniformObject.insert("description", uniform->description()); + if (uniform->type() == Uniform::Type::Float + || uniform->type() == Uniform::Type::Int + || uniform->type() == Uniform::Type::Vec2 + || uniform->type() == Uniform::Type::Vec3 + || uniform->type() == Uniform::Type::Vec4) { + uniformObject.insert("minValue", variantAsDataString(uniform->type(), uniform->minValue())); + uniformObject.insert("maxValue", variantAsDataString(uniform->type(), uniform->maxValue())); + } + if (!uniform->customValue().isEmpty()) + uniformObject.insert("customValue", uniform->customValue()); + if (uniform->useCustomValue()) + uniformObject.insert("useCustomValue", true); + + propertiesArray.append(uniformObject); + } + if (!propertiesArray.isEmpty()) + nodeObject.insert("properties", propertiesArray); + + // Add shaders + if (!node.fragmentCode().trimmed().isEmpty()) { + QJsonArray fragmentCodeArray; + const QStringList fsLines = node.fragmentCode().split('\n'); + for (const QString &line : fsLines) + fragmentCodeArray.append(line); + + if (!fragmentCodeArray.isEmpty()) + nodeObject.insert("fragmentCode", fragmentCodeArray); + } + if (!node.vertexCode().trimmed().isEmpty()) { + QJsonArray vertexCodeArray; + const QStringList vsLines = node.vertexCode().split('\n'); + for (const QString &line : vsLines) + vertexCodeArray.append(line); + + if (!vertexCodeArray.isEmpty()) + nodeObject.insert("vertexCode", vertexCodeArray); + } + + return nodeObject; +} + +void EffectMakerModel::exportComposition(const QString &name) +{ + const QString effectsAssetsDir = QmlDesigner::ModelNodeOperations::getEffectsDefaultDirectory(); + const QString path = effectsAssetsDir + QDir::separator() + name + ".qep"; + auto saveFile = QFile(path); + if (!saveFile.open(QIODevice::WriteOnly)) { + QString error = QString("Error: Couldn't save composition file: '%1'").arg(path); + qWarning() << error; + return; + } + + QJsonObject json; + // File format version + json.insert("version", 1); + + // Add nodes + QJsonArray nodesArray; + for (const CompositionNode *node : std::as_const(m_nodes)) { + QJsonObject nodeObject = nodeToJson(*node); + nodesArray.append(nodeObject); + } + + if (!nodesArray.isEmpty()) + json.insert("nodes", nodesArray); + + QJsonObject rootJson; + rootJson.insert("QEP", json); + QJsonDocument jsonDoc(rootJson); + + saveFile.write(jsonDoc.toJson()); + saveFile.close(); +} + void EffectMakerModel::resetEffectError(int type) { if (m_effectErrors.contains(type)) { diff --git a/src/plugins/effectmakernew/effectmakermodel.h b/src/plugins/effectmakernew/effectmakermodel.h index 3be8701deae..5f7550bb37f 100644 --- a/src/plugins/effectmakernew/effectmakermodel.h +++ b/src/plugins/effectmakernew/effectmakermodel.h @@ -83,6 +83,8 @@ public: Q_INVOKABLE void resetEffectError(int type); Q_INVOKABLE void setEffectError(const QString &errorMessage, int type = -1, int lineNumber = -1); + Q_INVOKABLE void exportComposition(const QString &name); + signals: void isEmptyChanged(); void selectedIndexChanged(int idx); diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp index 1ed1c09b0c1..53c7b7fa216 100644 --- a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp +++ b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp @@ -1683,6 +1683,12 @@ Utils::FilePath getEffectsImportDirectory() QString getEffectsDefaultDirectory(const QString &defaultDir) { + if (defaultDir.isEmpty()) { + return Utils::FilePath::fromString(getAssetDefaultDirectory( + "effects", + QmlDesignerPlugin::instance()->documentManager().currentProjectDirPath().toString())).toString(); + } + return getAssetDefaultDirectory("effects", defaultDir); } diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.h b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.h index c73530e02dd..7d7a985283a 100644 --- a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.h +++ b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.h @@ -125,7 +125,7 @@ void openSignalDialog(const SelectionContext &selectionContext); void updateImported3DAsset(const SelectionContext &selectionContext); QMLDESIGNERCOMPONENTS_EXPORT Utils::FilePath getEffectsImportDirectory(); -QMLDESIGNERCOMPONENTS_EXPORT QString getEffectsDefaultDirectory(const QString &defaultDir); +QMLDESIGNERCOMPONENTS_EXPORT QString getEffectsDefaultDirectory(const QString &defaultDir = {}); void openEffectMaker(const QString &filePath); QString getEffectIcon(const QString &effectPath); bool useLayerEffect(); From 8f57d1ec0e41bf7f9f655608c7d11ac6ad2417a6 Mon Sep 17 00:00:00 2001 From: Yasser Grimes Date: Wed, 8 Nov 2023 19:13:32 +0200 Subject: [PATCH 191/242] McuSupport: Add Shapes moudule include to qmlprojectexporter deploy step Task-number: QDS-10814 Change-Id: I0453360a501a2cdb21522a6f257523899b36df95 Reviewed-by: Knud Dollereder Reviewed-by: Qt CI Patch Build Bot --- src/plugins/mcusupport/mcubuildstep.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/plugins/mcusupport/mcubuildstep.cpp b/src/plugins/mcusupport/mcubuildstep.cpp index ceef09d078e..52efbbed91b 100644 --- a/src/plugins/mcusupport/mcubuildstep.cpp +++ b/src/plugins/mcusupport/mcubuildstep.cpp @@ -89,7 +89,8 @@ DeployMcuProcessStep::DeployMcuProcessStep(ProjectExplorer::BuildStepList *bc, I const FilePath qulIncludeDir = FilePath::fromVariant(kit->value(importPathConstant)); QStringList includeDirs { ProcessArgs::quoteArg(qulIncludeDir.toString()), - ProcessArgs::quoteArg(qulIncludeDir.pathAppended("Timeline").toString()) + ProcessArgs::quoteArg(qulIncludeDir.pathAppended("Timeline").toString()), + ProcessArgs::quoteArg(qulIncludeDir.pathAppended("Shapes").toString()) }; const char *toolChainConstant = Internal::Constants::KIT_MCUTARGET_TOOLCHAIN_KEY; From 44ce489ea39e5d6c3dde40d93ffd0834ace20612 Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Thu, 9 Nov 2023 14:34:42 +0200 Subject: [PATCH 192/242] EffectMaker: improve save dialog Few fixes and tweaks Change-Id: I4071b93a64f839c3f2e9ccdc8f648d907723610e Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Miikka Heikkinen Reviewed-by: Amr Elsayed --- .../effectMakerQmlSources/SaveDialog.qml | 86 ++++++++++--------- 1 file changed, 44 insertions(+), 42 deletions(-) diff --git a/share/qtcreator/qmldesigner/effectMakerQmlSources/SaveDialog.qml b/share/qtcreator/qmldesigner/effectMakerQmlSources/SaveDialog.qml index f04a81db9f9..08bb8c0832a 100644 --- a/share/qtcreator/qmldesigner/effectMakerQmlSources/SaveDialog.qml +++ b/share/qtcreator/qmldesigner/effectMakerQmlSources/SaveDialog.qml @@ -16,64 +16,66 @@ StudioControls.Dialog { closePolicy: Popup.CloseOnEscape modal: true implicitWidth: 250 + implicitHeight: 160 - property string compositionName: null + property string compositionName: "" onOpened: { nameText.text = "" //TODO: Generate unique name - emptyText.opacity = 0 + emptyText.text = "" nameText.forceActiveFocus() } - HelperWidgets.RegExpValidator { - id: validator - regExp: /^(\w[^*/> 0 && !validator.test(text) - btnSave.enabled = !emptyText.visible + Row { + id: row + Text { + text: qsTr("Effect name: ") + anchors.verticalCenter: parent.verticalCenter + color: StudioTheme.Values.themeTextColor + } + + StudioControls.TextField { + id: nameText + + actionIndicator.visible: false + translationIndicator.visible: false + + onTextChanged: { + let errMsg = "" + if (/[^A-Za-z0-9_]+/.test(text)) + errMsg = qsTr("Name contains invalid characters.") + else if (!/^[A-Z]/.test(text)) + errMsg = qsTr("Name must start with a capital letter") + else if (text.length < 3) + errMsg = qsTr("Name must have at least 3 characters") + else if (/\s/.test(text)) + errMsg = qsTr("Name cannot contain white space") + + emptyText.text = errMsg + btnSave.enabled = errMsg.length === 0 + } + Keys.onEnterPressed: btnSave.onClicked() + Keys.onReturnPressed: btnSave.onClicked() + Keys.onEscapePressed: root.reject() } - Keys.onEnterPressed: btnSave.onClicked() - Keys.onReturnPressed: btnSave.onClicked() - Keys.onEscapePressed: root.reject() } - } - Text { - id: emptyText + Text { + id: emptyText - text: qsTr("Effect name cannot be empty.") - color: StudioTheme.Values.themeError - anchors.right: row.right - } - - Item { // spacer - width: 1 - height: 20 + color: StudioTheme.Values.themeError + anchors.right: row.right + } } Row { anchors.right: parent.right + anchors.bottom: parent.bottom + spacing: 2 HelperWidgets.Button { id: btnSave From 6b2963e678bf01b551683480d54230e703701efa Mon Sep 17 00:00:00 2001 From: Henning Gruendl Date: Tue, 24 Oct 2023 12:10:29 +0200 Subject: [PATCH 193/242] QmlDesigner: Add color editor to StudioControls * Add QML Window based popover to StudioControls * Add color editor to StudioControls * Move color editor modules to StudioControls * Move IconIndicator to StudioControls * Move ToolTipArea to StudioControls * Add color backend to qmldesignerbase to allow control value binding * Use popover in connections editor * Add window manager to send focus changes to QML Windows * Update ColorEditor UX * Remove HelperWindow workaround for GradientPresetList (Qt 5 vs 6) * Fix eye dropper for QWindow Task-number: QDS-10926 Change-Id: Ia87b30543affde88faaef2ebdf120cb5d348c935 Reviewed-by: Thomas Hartmann Reviewed-by: Qt CI Patch Build Bot --- .../connectionseditor/BindingsDialog.qml | 11 +- .../connectionseditor/BindingsDialogForm.qml | 7 +- .../connectionseditor/BindingsListView.qml | 18 +- .../connectionseditor/ConnectionsDialog.qml | 10 +- .../ConnectionsDialogForm.qml | 2 +- .../connectionseditor/ConnectionsListView.qml | 14 +- .../connectionseditor/PopupDialog.qml | 107 - .../connectionseditor/PropertiesDialog.qml | 9 +- .../PropertiesDialogForm.qml | 15 +- .../connectionseditor/PropertiesListView.qml | 18 +- .../Qt5HelperWindow.qml | 4 - .../Qt6HelperWindow.qml | 4 - .../imports/HelperWidgets/ColorEditor.qml | 132 +- .../HelperWidgets/ColorEditorPopup.qml | 1774 ++++++++--------- .../imports/HelperWidgets/ColorLine.qml | 30 - .../HelperWidgets/GradientPresetList.qml | 27 +- .../HelperWidgets/GradientPropertySpinBox.qml | 65 +- .../imports/HelperWidgets/IconIndicator.qml | 73 +- .../imports/HelperWidgets/ToolTipArea.qml | 26 +- .../imports/HelperWidgets/qmldir | 6 - .../StudioControls/ActionIndicator.qml | 10 +- .../imports/StudioControls/ColorEditor.qml | 138 ++ .../imports/StudioControls/IconIndicator.qml | 70 + .../imports/StudioControls/PopupDialog.qml | 460 +++++ .../imports/StudioControls/Section.qml | 41 +- .../imports/StudioControls/ToolTipArea.qml | 25 + .../StudioControls/TopLevelComboBox.qml | 11 +- .../StudioControls/impl/ColorEditorPopup.qml | 560 ++++++ .../impl}/ColorPalette.qml | 73 +- .../impl}/ColorPicker.qml | 4 +- .../impl}/HueSlider.qml | 0 .../impl}/LuminanceSlider.qml | 0 .../impl}/OpacitySlider.qml | 4 +- .../imports/StudioControls/qmldir | 10 + .../propertyeditor/colorpalettebackend.cpp | 41 +- .../quick2propertyeditorview.cpp | 7 - .../components/propertyeditor/tooltip.cpp | 1 + src/plugins/qmldesigner/qmldesignerplugin.cpp | 6 +- src/plugins/qmldesignerbase/CMakeLists.txt | 1 + .../studio/studioquickwidget.cpp | 18 +- .../studio/studioquickwidget.h | 41 + .../qmldesignerbase/utils/windowmanager.cpp | 36 + .../qmldesignerbase/utils/windowmanager.h | 37 + 43 files changed, 2470 insertions(+), 1476 deletions(-) delete mode 100644 share/qtcreator/qmldesigner/connectionseditor/PopupDialog.qml delete mode 100644 share/qtcreator/qmldesigner/propertyEditorQmlSources/Qt5HelperWindow.qml delete mode 100644 share/qtcreator/qmldesigner/propertyEditorQmlSources/Qt6HelperWindow.qml delete mode 100644 share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ColorLine.qml create mode 100644 share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/ColorEditor.qml create mode 100644 share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/IconIndicator.qml create mode 100644 share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/PopupDialog.qml create mode 100644 share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/ToolTipArea.qml create mode 100644 share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/impl/ColorEditorPopup.qml rename share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/{HelperWidgets => StudioControls/impl}/ColorPalette.qml (61%) rename share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/{HelperWidgets => StudioControls/impl}/ColorPicker.qml (99%) rename share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/{HelperWidgets => StudioControls/impl}/HueSlider.qml (100%) rename share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/{HelperWidgets => StudioControls/impl}/LuminanceSlider.qml (100%) rename share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/{HelperWidgets => StudioControls/impl}/OpacitySlider.qml (96%) create mode 100644 src/plugins/qmldesignerbase/utils/windowmanager.cpp create mode 100644 src/plugins/qmldesignerbase/utils/windowmanager.h diff --git a/share/qtcreator/qmldesigner/connectionseditor/BindingsDialog.qml b/share/qtcreator/qmldesigner/connectionseditor/BindingsDialog.qml index 8830e298df3..93970c5af83 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/BindingsDialog.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/BindingsDialog.qml @@ -2,11 +2,13 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 import QtQuick -import StudioTheme 1.0 as StudioTheme +import StudioTheme as StudioTheme +import StudioControls as StudioControls import HelperWidgets as HelperWidgets -PopupDialog { +StudioControls.PopupDialog { property alias backend: form.backend + titleBar: Row { spacing: 30 // TODO anchors.fill: parent @@ -16,6 +18,7 @@ PopupDialog { text: qsTr("Owner") font.pixelSize: StudioTheme.Values.myFontSize anchors.verticalCenter: parent.verticalCenter + HelperWidgets.ToolTipArea { anchors.fill: parent tooltip: qsTr("The owner of the property") @@ -27,14 +30,10 @@ PopupDialog { font.pixelSize: StudioTheme.Values.myFontSize anchors.verticalCenter: parent.verticalCenter text: form.backend.targetNode - } - } BindingsDialogForm { id: form - y: 32 - height: 160 } } diff --git a/share/qtcreator/qmldesigner/connectionseditor/BindingsDialogForm.qml b/share/qtcreator/qmldesigner/connectionseditor/BindingsDialogForm.qml index 749add63c43..9d1d74901e9 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/BindingsDialogForm.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/BindingsDialogForm.qml @@ -1,6 +1,6 @@ - // Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + import QtQuick import QtQuick.Controls import StudioControls as StudioControls @@ -10,12 +10,11 @@ Column { id: root readonly property real horizontalSpacing: 10 - readonly property real verticalSpacing: 16 + readonly property real verticalSpacing: 12 readonly property real columnWidth: (root.width - root.horizontalSpacing) / 2 property var backend - y: StudioTheme.Values.popupMargin width: parent.width spacing: root.verticalSpacing @@ -53,8 +52,8 @@ Column { PopupLabel { width: root.columnWidth text: backend.targetNode + anchors.verticalCenter: parent.verticalCenter } - } Row { diff --git a/share/qtcreator/qmldesigner/connectionseditor/BindingsListView.qml b/share/qtcreator/qmldesigner/connectionseditor/BindingsListView.qml index 83db20646e0..b50e1fbb5c5 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/BindingsListView.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/BindingsListView.qml @@ -15,12 +15,6 @@ ListView { property bool adsFocus: false - // Temporarily remove due to dockwidget focus issue - //onAdsFocusChanged: { - // if (!root.adsFocus) - // dialog.close() - //} - clip: true interactive: true highlightMoveDuration: 0 @@ -43,9 +37,7 @@ ListView { && verticalScrollBar.isNeeded } - onVisibleChanged: { - dialog.hide() - } + onVisibleChanged: dialog.close() property int modelCurrentIndex: root.model.currentIndex ?? 0 @@ -72,7 +64,7 @@ ListView { function addBinding() { ConnectionsEditorEditorBackend.bindingModel.add() if (root.currentItem) - dialog.popup(root.currentItem.delegateMouseArea) + dialog.show(root.currentItem.delegateMouseArea) } function resetIndex() { @@ -104,9 +96,11 @@ ListView { property alias delegateMouseArea: mouseArea + property bool hovered: mouseArea.containsMouse || toolTipArea.containsMouse + width: ListView.view.width height: root.style.squareControlSize.height - color: mouseArea.containsMouse ? + color: itemDelegate.hovered ? itemDelegate.ListView.isCurrentItem ? root.style.interactionHover : root.style.background.hover : "transparent" @@ -120,7 +114,7 @@ ListView { onClicked: { root.model.currentIndex = itemDelegate.index root.currentIndex = itemDelegate.index - dialog.popup(mouseArea) + dialog.show(mouseArea) } } diff --git a/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialog.qml b/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialog.qml index c7f0034b211..0d3393352f9 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialog.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialog.qml @@ -2,12 +2,13 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 import QtQuick -import StudioControls 1.0 as StudioControls -import StudioTheme 1.0 as StudioTheme -import HelperWidgets 2.0 as HelperWidgets +import StudioControls as StudioControls +import StudioTheme as StudioTheme +import HelperWidgets as HelperWidgets -PopupDialog { +StudioControls.PopupDialog { id: root + property alias backend: form.backend titleBar: Row { @@ -19,6 +20,7 @@ PopupDialog { text: qsTr("Target") font.pixelSize: StudioTheme.Values.myFontSize anchors.verticalCenter: parent.verticalCenter + HelperWidgets.ToolTipArea { anchors.fill: parent tooltip: qsTr("Sets the Component that is connected to a Signal.") diff --git a/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialogForm.qml b/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialogForm.qml index 1e924d30b20..c56cdef16cc 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialogForm.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialogForm.qml @@ -16,7 +16,6 @@ Column { property var backend - y: StudioTheme.Values.popupMargin width: parent.width spacing: root.verticalSpacing @@ -45,6 +44,7 @@ Column { StudioControls.TopLevelComboBox { id: signal + style: StudioTheme.Values.connectionPopupControlStyle width: root.columnWidth diff --git a/share/qtcreator/qmldesigner/connectionseditor/ConnectionsListView.qml b/share/qtcreator/qmldesigner/connectionseditor/ConnectionsListView.qml index b783627c827..53a51a89e97 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/ConnectionsListView.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/ConnectionsListView.qml @@ -15,12 +15,6 @@ ListView { property bool adsFocus: false - // Temporarily remove due to dockwidget focus issue - //onAdsFocusChanged: { - // if (!root.adsFocus) - // dialog.close() - //} - clip: true interactive: true highlightMoveDuration: 0 @@ -43,9 +37,7 @@ ListView { && verticalScrollBar.isNeeded } - onVisibleChanged: { - dialog.hide() - } + onVisibleChanged: dialog.close() property int modelCurrentIndex: root.model.currentIndex ?? 0 @@ -73,7 +65,7 @@ ListView { function addConnection() { ConnectionsEditorEditorBackend.connectionModel.add() if (root.currentItem) - dialog.popup(root.currentItem.delegateMouseArea) + dialog.show(root.currentItem.delegateMouseArea) } function resetIndex() { @@ -124,7 +116,7 @@ ListView { root.model.currentIndex = itemDelegate.index root.currentIndex = itemDelegate.index dialog.backend.currentRow = itemDelegate.index - dialog.popup(mouseArea) + dialog.show(mouseArea) } } diff --git a/share/qtcreator/qmldesigner/connectionseditor/PopupDialog.qml b/share/qtcreator/qmldesigner/connectionseditor/PopupDialog.qml deleted file mode 100644 index da911854ada..00000000000 --- a/share/qtcreator/qmldesigner/connectionseditor/PopupDialog.qml +++ /dev/null @@ -1,107 +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 - -import QtQuick -import QtQuick.Controls -import HelperWidgets 2.0 as HelperWidgets -import StudioTheme 1.0 as StudioTheme -import ConnectionsEditorEditorBackend - -Window { - id: window - - property alias titleBar: titleBarContent.children - default property alias content: mainContent.children - property StudioTheme.ControlStyle style: StudioTheme.Values.controlStyle - - width: 300 - height: column.implicitHeight - visible: true - flags: Qt.FramelessWindowHint | Qt.Dialog - - Rectangle { - anchors.fill: parent - color: StudioTheme.Values.themePopoutBackground - border.color: "#636363"//StudioTheme.Values.themeTextColor - } - - function ensureVerticalPosition() { - if ((window.y + window.height) > (Screen.height - window.style.dialogScreenMargin)) { - window.y = (Screen.height - window.height - window.style.dialogScreenMargin) - } - } - - onHeightChanged: window.ensureVerticalPosition() - - function popup(item) { - var padding = 12 - var p = item.mapToGlobal(0, 0) - window.x = p.x - window.width - padding - if (window.x < 0) - window.x = p.x + item.width + padding - window.y = p.y - - window.ensureVerticalPosition() - - window.show() - window.raise() - } - - Column { - id: column - anchors.fill: parent - - Item { - id: titleBarItem - width: parent.width - height: StudioTheme.Values.titleBarHeight - - DragHandler { - id: dragHandler - - target: null - grabPermissions: PointerHandler.CanTakeOverFromAnything - onActiveChanged: { - if (dragHandler.active) - window.startSystemMove() // QTBUG-102488 - } - } - - Row { - id: row - anchors.fill: parent - anchors.leftMargin: StudioTheme.Values.popupMargin - anchors.rightMargin: StudioTheme.Values.popupMargin - spacing: 0 - - Item { - id: titleBarContent - width: row.width - closeIndicator.width //- row.anchors.leftMargin - height: row.height - } - - HelperWidgets.IconIndicator { - id: closeIndicator - anchors.verticalCenter: parent.verticalCenter - icon: StudioTheme.Constants.colorPopupClose - pixelSize: StudioTheme.Values.myIconFontSize// * 1.4 - onClicked: window.close() - } - } - } - - Rectangle { - width: parent.width - 8 - height: 1 - anchors.horizontalCenter: parent.horizontalCenter - color: "#636363" - } - - Item { - id: mainContent - width: parent.width - 2 * StudioTheme.Values.popupMargin - height: mainContent.childrenRect.height + 2 * StudioTheme.Values.popupMargin - anchors.horizontalCenter: parent.horizontalCenter - } - } -} diff --git a/share/qtcreator/qmldesigner/connectionseditor/PropertiesDialog.qml b/share/qtcreator/qmldesigner/connectionseditor/PropertiesDialog.qml index 20e9568c14b..4595d940402 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/PropertiesDialog.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/PropertiesDialog.qml @@ -2,10 +2,11 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 import QtQuick -import StudioTheme 1.0 as StudioTheme +import StudioTheme as StudioTheme import HelperWidgets as HelperWidgets +import StudioControls as StudioControls -PopupDialog { +StudioControls.PopupDialog { property alias backend: form.backend titleBar: Row { @@ -17,6 +18,7 @@ PopupDialog { text: qsTr("Owner") font.pixelSize: StudioTheme.Values.myFontSize anchors.verticalCenter: parent.verticalCenter + HelperWidgets.ToolTipArea { anchors.fill: parent tooltip: qsTr("The owner of the property") @@ -29,12 +31,9 @@ PopupDialog { anchors.verticalCenter: parent.verticalCenter text: form.backend.targetNode } - } PropertiesDialogForm { id: form - y: 32 - height: 180 } } diff --git a/share/qtcreator/qmldesigner/connectionseditor/PropertiesDialogForm.qml b/share/qtcreator/qmldesigner/connectionseditor/PropertiesDialogForm.qml index 8d62daacf8d..eee45df8605 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/PropertiesDialogForm.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/PropertiesDialogForm.qml @@ -10,12 +10,11 @@ Column { id: root readonly property real horizontalSpacing: 10 - readonly property real verticalSpacing: 16 + readonly property real verticalSpacing: 12 readonly property real columnWidth: (root.width - root.horizontalSpacing) / 2 property var backend - y: StudioTheme.Values.popupMargin width: parent.width spacing: root.verticalSpacing @@ -23,11 +22,11 @@ Column { text: qsTr("Type") tooltip: qsTr("Sets the category of the Local Custom Property.") } + StudioControls.TopLevelComboBox { id: type style: StudioTheme.Values.connectionPopupControlStyle width: root.columnWidth - //width: root.width model: backend.type.model ?? [] onActivated: backend.type.activateIndex(type.currentIndex) @@ -53,6 +52,7 @@ Column { Row { spacing: root.horizontalSpacing + StudioControls.TextField { id: name @@ -61,10 +61,9 @@ Column { translationIndicatorVisible: false text: backend.name.text ?? "" - onEditingFinished: { - backend.name.activateText(name.text) - } + onEditingFinished: backend.name.activateText(name.text) } + StudioControls.TextField { id: value @@ -74,9 +73,7 @@ Column { text: backend.value.text ?? "" - onEditingFinished: { - backend.value.activateText(value.text) - } + onEditingFinished: backend.value.activateText(value.text) } } } diff --git a/share/qtcreator/qmldesigner/connectionseditor/PropertiesListView.qml b/share/qtcreator/qmldesigner/connectionseditor/PropertiesListView.qml index 53d1edea95a..0606febb0dd 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/PropertiesListView.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/PropertiesListView.qml @@ -15,12 +15,6 @@ ListView { property bool adsFocus: false - // Temporarily remove due to dockwidget focus issue - //onAdsFocusChanged: { - // if (!root.adsFocus) - // dialog.close() - //} - clip: true interactive: true highlightMoveDuration: 0 @@ -43,9 +37,7 @@ ListView { && verticalScrollBar.isNeeded } - onVisibleChanged: { - dialog.hide() - } + onVisibleChanged: dialog.close() property int modelCurrentIndex: root.model.currentIndex ?? 0 @@ -72,7 +64,7 @@ ListView { function addProperty() { ConnectionsEditorEditorBackend.dynamicPropertiesModel.add() if (root.currentItem) - dialog.popup(root.currentItem.delegateMouseArea) + dialog.show(root.currentItem.delegateMouseArea) } function resetIndex() { @@ -104,9 +96,11 @@ ListView { property alias delegateMouseArea: mouseArea + property bool hovered: mouseArea.containsMouse || toolTipArea.containsMouse + width: ListView.view.width height: root.style.squareControlSize.height - color: mouseArea.containsMouse ? + color: itemDelegate.hovered ? itemDelegate.ListView.isCurrentItem ? root.style.interactionHover : root.style.background.hover : "transparent" @@ -124,7 +118,7 @@ ListView { function onClicked() { root.model.currentIndex = itemDelegate.index root.currentIndex = itemDelegate.index - dialog.popup(mouseArea) + dialog.show(mouseArea) } } } diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/Qt5HelperWindow.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/Qt5HelperWindow.qml deleted file mode 100644 index a2232b13da5..00000000000 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/Qt5HelperWindow.qml +++ /dev/null @@ -1,4 +0,0 @@ -import QtQuick.Window 2.15 - -Window { -} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/Qt6HelperWindow.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/Qt6HelperWindow.qml deleted file mode 100644 index 448a7575a47..00000000000 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/Qt6HelperWindow.qml +++ /dev/null @@ -1,4 +0,0 @@ -import QtQuick 2.15 - -Window { -} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ColorEditor.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ColorEditor.qml index 7c5b9535473..4ff7c30c2bf 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ColorEditor.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ColorEditor.qml @@ -1,13 +1,14 @@ -// Copyright (C) 2021 The Qt Company Ltd. +// Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 -import QtQuick 2.15 -import QtQuick.Layouts 1.15 -import QtQuick.Shapes 1.15 -import QtQuick.Templates 2.15 as T -import QtQuickDesignerTheme 1.0 -import StudioTheme 1.0 as StudioTheme -import StudioControls 1.0 as StudioControls -import QtQuickDesignerColorPalette 1.0 + +import QtQuick +import QtQuick.Layouts +import QtQuick.Shapes +import QtQuick.Templates as T +import StudioTheme as StudioTheme +import StudioControls as StudioControls +import QtQuickDesignerTheme +import QtQuickDesignerColorPalette SecondColumnLayout { id: colorEditor @@ -29,7 +30,7 @@ SecondColumnLayout { return colorEditor.backendValue.value } - property alias gradientPropertyName: popupLoader.gradientPropertyName + property alias gradientPropertyName: popupDialog.gradientPropertyName property alias gradientThumbnail: gradientThumbnail property alias shapeGradientThumbnail: shapeGradientThumbnail @@ -66,12 +67,12 @@ SecondColumnLayout { target: colorEditor function onValueChanged() { - if (popupLoader.isNotInGradientMode()) + if (popupDialog.isSolid()) colorEditor.syncColor() } function onBackendValueChanged() { - if (popupLoader.isNotInGradientMode()) + if (popupDialog.isSolid()) colorEditor.syncColor() } } @@ -101,13 +102,13 @@ SecondColumnLayout { if (colorEditor.__block) return - if (!popupLoader.isInValidState) + if (!popupDialog.isInValidState) return - popupLoader.commitToGradient() + popupDialog.commitToGradient() // Delay setting the color to keep ui responsive - if (popupLoader.isNotInGradientMode()) + if (popupDialog.isSolid()) colorEditorTimer.restart() } @@ -127,17 +128,16 @@ SecondColumnLayout { id: gradientThumbnail anchors.fill: parent anchors.margins: StudioTheme.Values.border - visible: !popupLoader.isNotInGradientMode() + visible: !popupDialog.isSolid() && !colorEditor.shapeGradients - && popupLoader.hasLinearGradient() + && popupDialog.isLinearGradient() } Shape { id: shape anchors.fill: parent anchors.margins: StudioTheme.Values.border - visible: !popupLoader.isNotInGradientMode() - && colorEditor.shapeGradients + visible: !popupDialog.isSolid() && colorEditor.shapeGradients ShapePath { id: shapeGradientThumbnail @@ -171,78 +171,77 @@ SecondColumnLayout { MouseArea { anchors.fill: parent onClicked: { - popupLoader.opened ? popupLoader.close() : popupLoader.open() + popupDialog.visibility ? popupDialog.close() : popupDialog.open() forceActiveFocus() } } - QtObject { - id: popupLoader - - property bool isInValidState: popupLoader.active ? popupLoader.dialog.isInValidState : true - - property QtObject dialog: popupLoader.loader.item - - property bool opened: popupLoader.active ? popupLoader.dialog.opened : false + StudioControls.PopupDialog { + id: popupDialog + property bool isInValidState: loader.active ? popupDialog.loaderItem.isInValidState : true + property QtObject loaderItem: loader.item property string gradientPropertyName + width: 260 + maximumHeight: Screen.desktopAvailableHeight * 0.7 + function commitToGradient() { - if (!popupLoader.active) + if (!loader.active) return - if (colorEditor.supportGradient && popupLoader.dialog.gradientModel.hasGradient) { + if (colorEditor.supportGradient && popupDialog.loaderItem.gradientModel.hasGradient) { var hexColor = convertColorToString(colorEditor.color) hexTextField.text = hexColor - popupLoader.dialog.commitGradientColor() + popupDialog.loaderItem.commitGradientColor() } } - function isNotInGradientMode() { - if (!popupLoader.active) + function isSolid() { + if (!loader.active) return true - return popupLoader.dialog.isNotInGradientMode() + + return popupDialog.loaderItem.isSolid() } - function hasLinearGradient(){ - if (!popupLoader.active) + function isLinearGradient(){ + if (!loader.active) return false - return popupLoader.dialog.hasLinearGradient() + + return popupDialog.loaderItem.isLinearGradient() } function ensureLoader() { - if (!popupLoader.active) - popupLoader.active = true + if (!loader.active) + loader.active = true } function open() { - popupLoader.ensureLoader() - popupLoader.dialog.open() - } - - function close() { - popupLoader.ensureLoader() - popupLoader.dialog.close() + popupDialog.ensureLoader() + popupDialog.show(preview) } function determineActiveColorMode() { - if (popupLoader.active && popupLoader.dialog) - popupLoader.dialog.determineActiveColorMode() + if (loader.active && popupDialog.loaderItem) + popupDialog.loaderItem.determineActiveColorMode() else colorEditor.syncColor() } - property alias active: popupLoader.loader.active - property Loader loader: Loader { - parent: preview + Loader { + id: loader active: colorEditor.supportGradient sourceComponent: ColorEditorPopup { - id: cePopup - x: cePopup.__defaultX - y: cePopup.__defaultY + shapeGradients: colorEditor.shapeGradients + supportGradient: colorEditor.supportGradient + width: popupDialog.contentWidth + } + + onLoaded: { + popupDialog.loaderItem.initEditor() + popupDialog.titleBar = loader.item.titleBarContent } - onLoaded: popupLoader.dialog.initEditor() } } } @@ -255,19 +254,26 @@ SecondColumnLayout { id: hexTextField implicitWidth: StudioTheme.Values.twoControlColumnWidth + StudioTheme.Values.actionIndicatorWidth - width: implicitWidth - enabled: popupLoader.isNotInGradientMode() + width: hexTextField.implicitWidth + enabled: popupDialog.isSolid() writeValueManually: true - validator: RegExpValidator { - regExp: /#[0-9A-Fa-f]{6}([0-9A-Fa-f]{2})?/g + validator: RegularExpressionValidator { + regularExpression: /#[0-9A-Fa-f]{6}([0-9A-Fa-f]{2})?/g } showTranslateCheckBox: false + indicatorVisible: true + indicator.icon.text: StudioTheme.Constants.copy_small + indicator.onClicked: { + hexTextField.selectAll() + hexTextField.copy() + hexTextField.deselect() + } backendValue: colorEditor.backendValue onAccepted: colorEditor.color = colorFromString(hexTextField.text) onCommitData: { colorEditor.color = colorFromString(hexTextField.text) - if (popupLoader.isNotInGradientMode()) { + if (popupDialog.isSolid()) { if (colorEditor.isVector3D) { backendValue.value = Qt.vector3d(colorEditor.color.r, colorEditor.color.g, @@ -283,9 +289,7 @@ SecondColumnLayout { id: spacer } - Component.onCompleted: popupLoader.determineActiveColorMode() + Component.onCompleted: popupDialog.determineActiveColorMode() - onBackendValueChanged: { - popupLoader.determineActiveColorMode() - } + onBackendValueChanged: popupDialog.determineActiveColorMode() } diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ColorEditorPopup.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ColorEditorPopup.qml index 2a4b036effb..c5e0ef63ee0 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ColorEditorPopup.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ColorEditorPopup.qml @@ -1,65 +1,211 @@ // Copyright (C) 2022 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 -import QtQuick 2.15 -import QtQuick.Layouts 1.15 -import QtQuick.Shapes 1.15 -import QtQuick.Templates 2.15 as T -import QtQuickDesignerTheme 1.0 -import StudioTheme 1.0 as StudioTheme -import StudioControls 1.0 as StudioControls -import QtQuickDesignerColorPalette 1.0 +import QtQuick +import QtQuick.Layouts +import QtQuick.Shapes +import QtQuick.Templates as T +import QtQuickDesignerTheme +import StudioTheme as StudioTheme +import StudioControls as StudioControls +import QtQuickDesignerColorPalette -T.Popup { - id: cePopup +Column { + id: root + property bool supportGradient: false + property bool shapeGradients: false property alias gradientLine: gradientLine property alias popupHexTextField: popupHexTextField - property alias gradientPropertyName: cePopup.gradientModel.gradientPropertyName + property alias gradientPropertyName: root.gradientModel.gradientPropertyName property alias gradientOrientation: gradientOrientation property alias gradientModel: gradientModel property bool isInValidState: false + readonly property real twoColumnWidth: (colorColumn.width - StudioTheme.Values.controlGap) * 0.5 + readonly property real fourColumnWidth: (colorColumn.width - (3 * StudioTheme.Values.controlGap)) * 0.25 + + property Item titleBarContent: Row { + anchors.fill: parent + spacing: 10 + + StudioControls.ComboBox { + id: ceMode + + property ListModel items: ListModel {} + + anchors.verticalCenter: parent.verticalCenter + enabled: isBaseState + implicitWidth: StudioTheme.Values.colorEditorPopupComboBoxWidth + width: ceMode.implicitWidth + actionIndicatorVisible: false + textRole: "text" + valueRole: "value" + model: ceMode.items + onActivated: { + switch (ceMode.currentValue) { + case "Solid": + gradientLine.deleteGradient() + hexTextField.text = colorEditor.color + popupHexTextField.text = colorEditor.color + colorEditor.resetShapeColor() + break + case "LinearGradient": + colorEditor.resetShapeColor() + + if (root.shapeGradients) + gradientModel.gradientTypeName = "LinearGradient" + else + gradientModel.gradientTypeName = "Gradient" + + if (gradientModel.hasGradient) + gradientLine.updateGradient() + else { + gradientLine.deleteGradient() + gradientLine.addGradient() + } + break + case "RadialGradient": + colorEditor.resetShapeColor() + gradientModel.gradientTypeName = "RadialGradient" + + if (gradientLine.hasGradient) + gradientLine.updateGradient() + else { + gradientLine.deleteGradient() + gradientLine.addGradient() + } + break + case "ConicalGradient": + colorEditor.resetShapeColor() + gradientModel.gradientTypeName = "ConicalGradient" + + if (gradientModel.hasGradient) + gradientLine.updateGradient() + else { + gradientLine.deleteGradient() + gradientLine.addGradient() + } + break + default: + console.warn("Unknown item selected in color mode ComboBox.") + } + root.updateThumbnail() + } + + ToolTipArea { + enabled: !isBaseState + anchors.fill: parent + tooltip: qsTr("Fill type can only be changed in base state.") + z: 10 + } + } + + ExpandingSpacer {} + + IconIndicator { + id: transparentIndicator + anchors.verticalCenter: parent.verticalCenter + icon: StudioTheme.Constants.transparent + pixelSize: StudioTheme.Values.myIconFontSize * 1.4 + tooltip: qsTr("Transparent") + onClicked: { + colorPicker.alpha = 0 + colorPicker.invalidateColor() + colorPicker.updateColor() + } + } + + IconIndicator { + id: gradientPickerIndicator + anchors.verticalCenter: parent.verticalCenter + icon: StudioTheme.Constants.gradient + pixelSize: StudioTheme.Values.myIconFontSize * 1.4 + tooltip: qsTr("Gradient Picker") + enabled: root.supportGradient + onClicked: presetList.show() + + GradientPresetList { + id: presetList + visible: false + transientParent: root.parentWindow + + function applyPreset() { + if (!gradientModel.hasGradient) { + if (root.shapeGradients) + gradientModel.gradientTypeName = "LinearGradient" + else + gradientModel.gradientTypeName = "Gradient" + } + + if (presetList.gradientData.presetType == 0) { + gradientLine.setPresetByID(presetList.gradientData.presetID) + } else if (presetList.gradientData.presetType == 1) { + gradientLine.setPresetByStops( + presetList.gradientData.stops, + presetList.gradientData.colors, + presetList.gradientData.stopsCount) + } else { + console.warn("Invalid Gradient type:", presetList.gradientData.presetType) + } + } + + onApplied: { + if (presetList.gradientData.stopsCount > 0) + presetList.applyPreset() + } + + onSaved: { + gradientLine.savePreset() + presetList.updatePresets() + } + + onAccepted: { // return key + if (presetList.gradientData.stopsCount > 0) + presetList.applyPreset() + } + } + } + + IconIndicator { + id: eyeDropperIndicator + anchors.verticalCenter: parent.verticalCenter + icon: StudioTheme.Constants.eyeDropper + pixelSize: StudioTheme.Values.myIconFontSize * 1.4 + tooltip: qsTr("Eye Dropper") + onClicked: ColorPaletteBackend.eyeDropper() + } + } + function initEditor() { - if (colorEditor.supportGradient && gradientModel.hasGradient) { + if (root.supportGradient && gradientModel.hasGradient) { colorEditor.color = gradientLine.currentColor gradientLine.currentColor = colorEditor.color hexTextField.text = colorEditor.color popupHexTextField.text = colorEditor.color } - cePopup.isInValidState = true + root.isInValidState = true colorEditor.originalColor = colorEditor.color colorPalette.selectedColor = colorEditor.color colorPicker.color = colorEditor.color - cePopup.createModel() - cePopup.determineActiveColorMode() + root.createModel() + root.determineActiveColorMode() } function commitGradientColor() { var hexColor = convertColorToString(colorEditor.color) - cePopup.popupHexTextField.text = hexColor - cePopup.gradientLine.currentColor = colorEditor.color + root.popupHexTextField.text = hexColor + root.gradientLine.currentColor = colorEditor.color } - function isNotInGradientMode() { - return ceMode.currentValue === "Solid" - } - - function hasLinearGradient() { - return ceMode.currentValue === "LinearGradient" - } - - function hasConicalGradient() { - return ceMode.currentValue === "ConicalGradient" - } - - function hasRadialGradient() { - return ceMode.currentValue === "RadialGradient" - } + function isSolid() { return ceMode.currentValue === "Solid" } + function isLinearGradient() { return ceMode.currentValue === "LinearGradient" } + function isConicalGradient() { return ceMode.currentValue === "ConicalGradient" } + function isRadialGradient() { return ceMode.currentValue === "RadialGradient" } function createModel() { // Build the color editor combobox model @@ -72,23 +218,23 @@ T.Popup { ceMode.items.append({ value: "LinearGradient", text: qsTr("Linear"), - enabled: colorEditor.supportGradient + enabled: root.supportGradient }) ceMode.items.append({ value: "RadialGradient", text: qsTr("Radial"), - enabled: colorEditor.supportGradient && colorEditor.shapeGradients + enabled: root.supportGradient && root.shapeGradients }) ceMode.items.append({ value: "ConicalGradient", text: qsTr("Conical"), - enabled: colorEditor.supportGradient && colorEditor.shapeGradients + enabled: root.supportGradient && root.shapeGradients }) } function determineActiveColorMode() { - if (colorEditor.supportGradient && gradientModel.hasGradient) { - if (colorEditor.shapeGradients) { + if (root.supportGradient && gradientModel.hasGradient) { + if (root.shapeGradients) { switch (gradientModel.gradientTypeName) { case "LinearGradient": ceMode.currentIndex = ceMode.indexOfValue("LinearGradient") @@ -118,9 +264,9 @@ T.Popup { if (!gradientModel.hasGradient) return - if (!colorEditor.shapeGradients) { + if (!root.shapeGradients) { var gradientString = "import QtQuick 2.15; Gradient {" - var orientation = cePopup.gradientOrientation.currentValue + var orientation = root.gradientOrientation.currentValue === Gradient.Horizontal ? "Gradient.Horizontal" : "Gradient.Vertical" gradientString += "orientation: " + orientation + ";" @@ -193,986 +339,716 @@ T.Popup { gradientPropertyName: "gradient" } - WheelHandler { - onWheel: function(event) { - Controller.mainScrollView.flick(0, event.angleDelta.y * 5) + Column { + id: colorColumn + bottomPadding: StudioTheme.Values.popupMargin + width: root.width + spacing: StudioTheme.Values.colorEditorPopupSpacing + + GradientLine { + id: gradientLine + + width: parent.width + visible: !root.isSolid() + + model: gradientModel + + onCurrentColorChanged: { + if (root.supportGradient && gradientModel.hasGradient) { + colorEditor.color = gradientLine.currentColor + colorPicker.color = colorEditor.color + } + } + + onHasGradientChanged: { + if (!root.supportGradient) + return + + root.determineActiveColorMode() + } + + onSelectedNodeChanged: { + if (root.supportGradient && gradientModel.hasGradient) + colorEditor.originalColor = gradientLine.currentColor + } + + onInvalidated: root.updateThumbnail() + + Connections { + target: modelNodeBackend + function onSelectionToBeChanged() { + colorEditorTimer.stop() + root.isInValidState = false + + var hexOriginalColor = convertColorToString(colorEditor.originalColor) + var hexColor = convertColorToString(colorEditor.color) + + if (hexOriginalColor !== hexColor) { + if (colorEditor.color !== "#ffffff" + && colorEditor.color !== "#000000" + && colorEditor.color !== "#00000000") { + colorPalette.addColorToPalette(colorEditor.color) + } + } + } + } + + Connections { + target: modelNodeBackend + function onSelectionChanged() { + root.initEditor() + } + } } - } - // This connection is meant to update the popups y-position and the main scrollviews - // height as soon as the height of the color picker changes. Initially the height of the - // color picker is 0 until its completion is done. - Connections { - target: colorPicker - function onHeightChanged() { - cePopup.setPopupY() - cePopup.setMainScrollViewHeight() + StudioControls.ColorPicker { + id: colorPicker + + width: parent.width + sliderMargins: 4 + + onUpdateColor: { + colorEditor.color = colorPicker.color + + if (contextMenu.opened) + contextMenu.close() + } + onRightMouseButtonClicked: contextMenu.popup(colorPicker) + + onColorInvalidated: { + hslHueSpinBox.value = colorPicker.hue + hslSaturationSpinBox.value = colorPicker.saturationHSL + hslLightnessSpinBox.value = colorPicker.lightness + hslAlphaSpinBox.value = colorPicker.alpha + + redSpinBox.value = (colorPicker.red * 255) + greenSpinBox.value = (colorPicker.green * 255) + blueSpinBox.value = (colorPicker.blue * 255) + rgbAlphaSpinBox.value = (colorPicker.alpha * 255) + + hsvHueSpinBox.value = colorPicker.hue + hsvSaturationSpinBox.value = colorPicker.saturationHSV + hsvValueSpinBox.value = colorPicker.value + hsvAlphaSpinBox.value = colorPicker.alpha + } } - } - - onOpened: { - cePopup.setPopupY() - cePopup.setMainScrollViewHeight() - } - onYChanged: cePopup.setMainScrollViewHeight() - onHeightChanged: cePopup.setMainScrollViewHeight() - - function setMainScrollViewHeight() { - if (Controller.mainScrollView === null) - return - - var mapped = preview.mapToItem(Controller.mainScrollView.contentItem, cePopup.x, cePopup.y) - Controller.mainScrollView.temporaryHeight = mapped.y + cePopup.height - + StudioTheme.Values.colorEditorPopupMargin - } - - function setPopupY() { - if (Controller.mainScrollView === null) - return - - var tmp = preview.mapToItem(Controller.mainScrollView.contentItem, preview.x, preview.y) - cePopup.y = Math.max(-tmp.y + StudioTheme.Values.colorEditorPopupMargin, - cePopup.__defaultY) - } - - onClosed: Controller.mainScrollView.temporaryHeight = 0 - - property real __defaultX: - StudioTheme.Values.colorEditorPopupWidth * 0.5 - + preview.width * 0.5 - property real __defaultY: - StudioTheme.Values.colorEditorPopupPadding - - (StudioTheme.Values.colorEditorPopupSpacing * 2) - - StudioTheme.Values.defaultControlHeight - - StudioTheme.Values.colorEditorPopupLineHeight - - colorPicker.height * 0.5 - + preview.height * 0.5 - - width: StudioTheme.Values.colorEditorPopupWidth - height: colorColumn.height + sectionColumn.height - + StudioTheme.Values.colorEditorPopupPadding + 2 // TODO magic number - - padding: StudioTheme.Values.border - margins: -1 // If not defined margin will be -1 - - closePolicy: T.Popup.CloseOnPressOutside | T.Popup.CloseOnPressOutsideParent - - contentItem: Item { - id: todoItem - - property color color - property bool supportGradient: false Column { - id: colorColumn - - anchors.top: parent.top - anchors.left: parent.left - anchors.right: parent.right - anchors.margins: StudioTheme.Values.colorEditorPopupPadding - spacing: StudioTheme.Values.colorEditorPopupSpacing + id: colorCompare + width: parent.width RowLayout { width: parent.width Layout.alignment: Qt.AlignTop + spacing: StudioTheme.Values.controlGap - StudioControls.ComboBox { - id: ceMode + Label { + text: qsTr("Original") + width: root.twoColumnWidth + } - property ListModel items: ListModel {} + Label { + text: qsTr("New") + width: root.twoColumnWidth + } + } - enabled: isBaseState - implicitWidth: StudioTheme.Values.colorEditorPopupComboBoxWidth - width: implicitWidth - actionIndicatorVisible: false - textRole: "text" - valueRole: "value" - model: ceMode.items - onActivated: { - switch (ceMode.currentValue) { - case "Solid": - gradientLine.deleteGradient() - hexTextField.text = colorEditor.color - popupHexTextField.text = colorEditor.color - colorEditor.resetShapeColor() - break - case "LinearGradient": - colorEditor.resetShapeColor() + RowLayout { + width: parent.width + Layout.alignment: Qt.AlignTop + spacing: StudioTheme.Values.controlGap - if (colorEditor.shapeGradients) - gradientModel.gradientTypeName = "LinearGradient" - else - gradientModel.gradientTypeName = "Gradient" + Rectangle { + id: originalColorRectangle + color: colorEditor.originalColor + width: root.twoColumnWidth + height: StudioTheme.Values.height + border.width: StudioTheme.Values.border + border.color: StudioTheme.Values.themeControlOutline - if (gradientModel.hasGradient) - gradientLine.updateGradient() - else { - gradientLine.deleteGradient() - gradientLine.addGradient() - } - break - case "RadialGradient": - colorEditor.resetShapeColor() - gradientModel.gradientTypeName = "RadialGradient" - - if (gradientLine.hasGradient) - gradientLine.updateGradient() - else { - gradientLine.deleteGradient() - gradientLine.addGradient() - } - break - case "ConicalGradient": - colorEditor.resetShapeColor() - gradientModel.gradientTypeName = "ConicalGradient" - - if (gradientModel.hasGradient) - gradientLine.updateGradient() - else { - gradientLine.deleteGradient() - gradientLine.addGradient() - } - break - default: - console.log("Unknown item selected in color mode ComboBox.") - } - cePopup.updateThumbnail() + Image { + anchors.fill: parent + source: "images/checkers.png" + fillMode: Image.Tile + z: -1 } ToolTipArea { - enabled: !isBaseState anchors.fill: parent - tooltip: qsTr("Fill type can only be changed in base state.") - z: 10 - } - } + tooltip: originalColorRectangle.color + acceptedButtons: Qt.LeftButton | Qt.RightButton + onClicked: function(mouse) { + if (mouse.button === Qt.LeftButton) + colorEditor.color = colorEditor.originalColor - ExpandingSpacer {} - - IconIndicator { - id: transparentIndicator - icon: StudioTheme.Constants.transparent - pixelSize: StudioTheme.Values.myIconFontSize * 1.4 - tooltip: qsTr("Transparent") - onClicked: { - colorPicker.alpha = 0 - colorPicker.invalidateColor() - colorPicker.updateColor() - } - } - - IconIndicator { - id: gradientPickerIndicator - icon: StudioTheme.Constants.gradient - pixelSize: StudioTheme.Values.myIconFontSize * 1.4 - tooltip: qsTr("Gradient Picker") - enabled: colorEditor.supportGradient - onClicked: presetList.show() - - GradientPresetList { - id: presetList - visible: false - - function applyPreset() { - if (!gradientModel.hasGradient) { - if (colorEditor.shapeGradients) - gradientModel.gradientTypeName = "LinearGradient" - else - gradientModel.gradientTypeName = "Gradient" - } - - if (presetList.gradientData.presetType == 0) { - gradientLine.setPresetByID(presetList.gradientData.presetID) - } else if (presetList.gradientData.presetType == 1) { - gradientLine.setPresetByStops( - presetList.gradientData.stops, - presetList.gradientData.colors, - presetList.gradientData.stopsCount) - } else { - console.log("INVALID GRADIENT TYPE: " + - presetList.gradientData.presetType) - } - } - - onApplied: { - if (presetList.gradientData.stopsCount > 0) - applyPreset() - } - - onSaved: { - gradientLine.savePreset() - presetList.updatePresets() - } - - onAccepted: { // return key - if (presetList.gradientData.stopsCount > 0) - applyPreset() - } - } - } - - IconIndicator { - id: eyeDropperIndicator - icon: StudioTheme.Constants.eyeDropper - pixelSize: StudioTheme.Values.myIconFontSize * 1.4 - tooltip: qsTr("Eye Dropper") - onClicked: ColorPaletteBackend.eyeDropper() - } - - IconIndicator { - id: closeIndicator - icon: StudioTheme.Constants.colorPopupClose - pixelSize: StudioTheme.Values.myIconFontSize * 1.4 - onClicked: cePopup.close() - } - } - - ColorLine { - id: colorLine - width: parent.width - currentColor: colorEditor.color - visible: cePopup.isNotInGradientMode() - } - - GradientLine { - id: gradientLine - - width: parent.width - visible: !cePopup.isNotInGradientMode() - - model: gradientModel - - onCurrentColorChanged: { - if (colorEditor.supportGradient && gradientModel.hasGradient) { - colorEditor.color = gradientLine.currentColor - colorPicker.color = colorEditor.color - } - } - - onHasGradientChanged: { - if (!colorEditor.supportGradient) - return - - cePopup.determineActiveColorMode() - } - - onSelectedNodeChanged: { - if (colorEditor.supportGradient && gradientModel.hasGradient) - colorEditor.originalColor = gradientLine.currentColor - } - - onInvalidated: cePopup.updateThumbnail() - - Connections { - target: modelNodeBackend - function onSelectionToBeChanged() { - colorEditorTimer.stop() - cePopup.isInValidState = false - - var hexOriginalColor = convertColorToString(colorEditor.originalColor) - var hexColor = convertColorToString(colorEditor.color) - - if (hexOriginalColor !== hexColor) { - if (colorEditor.color !== "#ffffff" - && colorEditor.color !== "#000000" - && colorEditor.color !== "#00000000") { - colorPalette.addColorToPalette(colorEditor.color) + if (mouse.button === Qt.RightButton) { + contextMenuFavorite.currentColor = colorEditor.originalColor + contextMenuFavorite.popup() } } } } - Connections { - target: modelNodeBackend - function onSelectionChanged() { - cePopup.initEditor() + + Rectangle { + id: newColorRectangle + color: colorEditor.color + width: root.twoColumnWidth + height: StudioTheme.Values.height + border.width: StudioTheme.Values.border + border.color: StudioTheme.Values.themeControlOutline + + Image { + anchors.fill: parent + source: "images/checkers.png" + fillMode: Image.Tile + z: -1 + } + + ToolTipArea { + anchors.fill: parent + tooltip: newColorRectangle.color + acceptedButtons: Qt.RightButton + onClicked: function(mouse) { + contextMenuFavorite.currentColor = colorEditor.color + contextMenuFavorite.popup() + } } } } - ColorPicker { - id: colorPicker + StudioControls.Menu { + id: contextMenuFavorite - width: parent.width - sliderMargins: 4 + property color currentColor - onUpdateColor: { - colorEditor.color = colorPicker.color - - if (contextMenu.opened) - contextMenu.close() - } - onRightMouseButtonClicked: contextMenu.popup(colorPicker) - - onColorInvalidated: { - hslHueSpinBox.value = colorPicker.hue - hslSaturationSpinBox.value = colorPicker.saturationHSL - hslLightnessSpinBox.value = colorPicker.lightness - hslAlphaSpinBox.value = colorPicker.alpha - - redSpinBox.value = (colorPicker.red * 255) - greenSpinBox.value = (colorPicker.green * 255) - blueSpinBox.value = (colorPicker.blue * 255) - rgbAlphaSpinBox.value = (colorPicker.alpha * 255) - - hsvHueSpinBox.value = colorPicker.hue - hsvSaturationSpinBox.value = colorPicker.saturationHSV - hsvValueSpinBox.value = colorPicker.value - hsvAlphaSpinBox.value = colorPicker.alpha - } - } - - Column { - id: colorCompare - width: parent.width - - RowLayout { - width: parent.width - Layout.alignment: Qt.AlignTop - spacing: StudioTheme.Values.controlGap - - Label { - text: qsTr("Original") - width: 2 * StudioTheme.Values.colorEditorPopupSpinBoxWidth - + StudioTheme.Values.controlGap - } - - Label { - text: qsTr("New") - width: 2 * StudioTheme.Values.colorEditorPopupSpinBoxWidth - + StudioTheme.Values.controlGap - } - } - - RowLayout { - width: parent.width - Layout.alignment: Qt.AlignTop - spacing: StudioTheme.Values.controlGap - - Rectangle { - id: originalColorRectangle - color: colorEditor.originalColor - height: StudioTheme.Values.height - width: 2 * StudioTheme.Values.colorEditorPopupSpinBoxWidth - + StudioTheme.Values.controlGap - border.width: StudioTheme.Values.border - border.color: StudioTheme.Values.themeControlOutline - - Image { - anchors.fill: parent - source: "images/checkers.png" - fillMode: Image.Tile - z: -1 - } - - ToolTipArea { - anchors.fill: parent - tooltip: originalColorRectangle.color - acceptedButtons: Qt.LeftButton | Qt.RightButton - onClicked: function(mouse) { - if (mouse.button === Qt.LeftButton) - colorEditor.color = colorEditor.originalColor - - if (mouse.button === Qt.RightButton) { - contextMenuFavorite.currentColor = colorEditor.originalColor - contextMenuFavorite.popup() - } - } - } - } - - Rectangle { - id: newColorRectangle - color: colorEditor.color - height: StudioTheme.Values.height - width: 2 * StudioTheme.Values.colorEditorPopupSpinBoxWidth - + StudioTheme.Values.controlGap - border.width: StudioTheme.Values.border - border.color: StudioTheme.Values.themeControlOutline - - Image { - anchors.fill: parent - source: "images/checkers.png" - fillMode: Image.Tile - z: -1 - } - - ToolTipArea { - anchors.fill: parent - tooltip: newColorRectangle.color - acceptedButtons: Qt.RightButton - onClicked: function(mouse) { - if (mouse.button === Qt.RightButton) { - contextMenuFavorite.currentColor = colorEditor.color - contextMenuFavorite.popup() - } - } - } - } - } - - StudioControls.Menu { - id: contextMenuFavorite - - property color currentColor - - StudioControls.MenuItem { - text: qsTr("Add to Favorites") - onTriggered: ColorPaletteBackend.addFavoriteColor( - contextMenuFavorite.currentColor) - } + StudioControls.MenuItem { + text: qsTr("Add to Favorites") + onTriggered: ColorPaletteBackend.addFavoriteColor(contextMenuFavorite.currentColor) } } } + } - Column { - id: sectionColumn - anchors.topMargin: StudioTheme.Values.colorEditorPopupPadding - anchors.top: colorColumn.bottom + Column { + id: sectionColumn + width: root.width + + StudioControls.Section { + caption: qsTr("Color Details") anchors.left: parent.left anchors.right: parent.right - bottomPadding: 10 + Column { + spacing: StudioTheme.Values.colorEditorPopupSpacing - Section { - caption: qsTr("Color Details") - anchors.left: parent.left - anchors.right: parent.right + Row { + spacing: StudioTheme.Values.controlGap - leftPadding: 10 - rightPadding: 10 + StudioControls.ComboBox { + id: colorMode + implicitWidth: root.twoColumnWidth + width: colorMode.implicitWidth + actionIndicatorVisible: false + textRole: "text" + valueRole: "value" + model: [ + { value: ColorPicker.Mode.HSVA, text: "HSVA" }, + { value: ColorPicker.Mode.RGBA, text: "RGBA" }, + { value: ColorPicker.Mode.HSLA, text: "HSLA" } + ] - Column { - spacing: 10 - - RowLayout { - Layout.fillWidth: true - spacing: 0 - - LineEdit { - id: popupHexTextField - implicitWidth: 2 * StudioTheme.Values.colorEditorPopupSpinBoxWidth - + StudioTheme.Values.controlGap - width: implicitWidth - writeValueManually: true - validator: RegExpValidator { regExp: /#[0-9A-Fa-f]{6}([0-9A-Fa-f]{2})?/g } - showTranslateCheckBox: false - showExtendedFunctionButton: false - backendValue: colorEditor.backendValue - - onAccepted: colorEditor.color = colorFromString(popupHexTextField.text) - onCommitData: { - colorEditor.color = colorFromString(popupHexTextField.text) - if (cePopup.isNotInGradientMode()) { - if (colorEditor.isVector3D) { - backendValue.value = Qt.vector3d(colorEditor.color.r, - colorEditor.color.g, - colorEditor.color.b) - } else { - backendValue.value = colorEditor.color - } - } - } - } - - Spacer { implicitWidth: StudioTheme.Values.controlLabelGap } - - ControlLabel { - text: "Hex" - width: 2 * StudioTheme.Values.colorEditorPopupSpinBoxWidth - + StudioTheme.Values.controlGap - horizontalAlignment: Text.AlignLeft - } + onActivated: colorPicker.mode = colorMode.currentValue } - RowLayout { - Layout.fillWidth: true - spacing: 0 + LineEdit { + id: popupHexTextField + implicitWidth: root.twoColumnWidth + width: popupHexTextField.implicitWidth + writeValueManually: true + validator: RegularExpressionValidator { + regularExpression: /#[0-9A-Fa-f]{6}([0-9A-Fa-f]{2})?/g + } + showTranslateCheckBox: false + showExtendedFunctionButton: false + indicatorVisible: true + indicator.icon.text: StudioTheme.Constants.copy_small + indicator.onClicked: { + popupHexTextField.selectAll() + popupHexTextField.copy() + popupHexTextField.deselect() + } + backendValue: colorEditor.backendValue - StudioControls.ComboBox { - id: colorMode - - implicitWidth: 3 * StudioTheme.Values.controlGap - + 4 * StudioTheme.Values.colorEditorPopupSpinBoxWidth - width: implicitWidth - actionIndicatorVisible: false - textRole: "text" - valueRole: "value" - model: [ - { value: ColorPicker.Mode.HSVA, text: "HSVA" }, - { value: ColorPicker.Mode.RGBA, text: "RGBA" }, - { value: ColorPicker.Mode.HSLA, text: "HSLA" } - ] - - onActivated: colorPicker.mode = colorMode.currentValue + onAccepted: colorEditor.color = colorFromString(popupHexTextField.text) + onCommitData: { + colorEditor.color = colorFromString(popupHexTextField.text) + if (root.isSolid()) { + if (colorEditor.isVector3D) { + backendValue.value = Qt.vector3d(colorEditor.color.r, + colorEditor.color.g, + colorEditor.color.b) + } else { + backendValue.value = colorEditor.color + } + } } } + } - RowLayout { - id: rgbaRow - visible: colorPicker.mode === ColorPicker.Mode.RGBA - Layout.fillWidth: true - spacing: StudioTheme.Values.controlGap + Row { + id: rgbaRow + visible: colorPicker.mode === ColorPicker.Mode.RGBA + spacing: StudioTheme.Values.controlGap - DoubleSpinBox { - id: redSpinBox - width: StudioTheme.Values.colorEditorPopupSpinBoxWidth - stepSize: 1 - minimumValue: 0 - maximumValue: 255 - decimals: 0 + DoubleSpinBox { + id: redSpinBox + width: root.fourColumnWidth + stepSize: 1 + minimumValue: 0 + maximumValue: 255 + decimals: 0 - onValueModified: { - var tmp = redSpinBox.value / 255.0 - if (colorPicker.red !== tmp && !colorPicker.block) { - colorPicker.red = tmp - colorPicker.invalidateColor() - colorPicker.updateColor() - } + onValueModified: { + var tmp = redSpinBox.value / 255.0 + if (colorPicker.red !== tmp && !colorPicker.block) { + colorPicker.red = tmp + colorPicker.invalidateColor() + colorPicker.updateColor() } - onDragStarted: colorEditorTimer.stop() - onIndicatorPressed: colorEditorTimer.stop() - } - - DoubleSpinBox { - id: greenSpinBox - width: StudioTheme.Values.colorEditorPopupSpinBoxWidth - stepSize: 1 - minimumValue: 0 - maximumValue: 255 - decimals: 0 - - onValueModified: { - var tmp = greenSpinBox.value / 255.0 - if (colorPicker.green !== tmp && !colorPicker.block) { - colorPicker.green = tmp - colorPicker.invalidateColor() - colorPicker.updateColor() - } - } - onDragStarted: colorEditorTimer.stop() - onIndicatorPressed: colorEditorTimer.stop() - } - - DoubleSpinBox { - id: blueSpinBox - width: StudioTheme.Values.colorEditorPopupSpinBoxWidth - stepSize: 1 - minimumValue: 0 - maximumValue: 255 - decimals: 0 - - onValueModified: { - var tmp = blueSpinBox.value / 255.0 - if (colorPicker.blue !== tmp && !colorPicker.block) { - colorPicker.blue = tmp - colorPicker.invalidateColor() - colorPicker.updateColor() - } - } - onDragStarted: colorEditorTimer.stop() - onIndicatorPressed: colorEditorTimer.stop() - } - - DoubleSpinBox { - id: rgbAlphaSpinBox - width: StudioTheme.Values.colorEditorPopupSpinBoxWidth - stepSize: 1 - minimumValue: 0 - maximumValue: 255 - decimals: 0 - - onValueModified: { - var tmp = rgbAlphaSpinBox.value / 255.0 - if (colorPicker.alpha !== tmp && !colorPicker.block) { - colorPicker.alpha = tmp - colorPicker.invalidateColor() - colorPicker.updateColor() - } - } - onDragStarted: colorEditorTimer.stop() - onIndicatorPressed: colorEditorTimer.stop() } + onDragStarted: colorEditorTimer.stop() + onIndicatorPressed: colorEditorTimer.stop() } - RowLayout { - id: hslaRow - visible: colorPicker.mode === ColorPicker.Mode.HSLA - Layout.fillWidth: true - spacing: StudioTheme.Values.controlGap + DoubleSpinBox { + id: greenSpinBox + width: root.fourColumnWidth + stepSize: 1 + minimumValue: 0 + maximumValue: 255 + decimals: 0 - DoubleSpinBox { - id: hslHueSpinBox - width: StudioTheme.Values.colorEditorPopupSpinBoxWidth - onValueModified: { - if (colorPicker.hue !== hslHueSpinBox.value - && !colorPicker.block) { - colorPicker.hue = hslHueSpinBox.value - colorPicker.invalidateColor() - colorPicker.updateColor() - } - } - onDragStarted: colorEditorTimer.stop() - onIndicatorPressed: colorEditorTimer.stop() - } - - DoubleSpinBox { - id: hslSaturationSpinBox - width: StudioTheme.Values.colorEditorPopupSpinBoxWidth - onValueModified: { - if (colorPicker.saturationHSL !== hslSaturationSpinBox.value - && !colorPicker.block) { - colorPicker.saturationHSL = hslSaturationSpinBox.value - colorPicker.invalidateColor() - colorPicker.updateColor() - } - } - onDragStarted: colorEditorTimer.stop() - onIndicatorPressed: colorEditorTimer.stop() - } - - DoubleSpinBox { - id: hslLightnessSpinBox - width: StudioTheme.Values.colorEditorPopupSpinBoxWidth - onValueModified: { - if (colorPicker.lightness !== hslLightnessSpinBox.value - && !colorPicker.block) { - colorPicker.lightness = hslLightnessSpinBox.value - colorPicker.invalidateColor() - colorPicker.updateColor() - } + onValueModified: { + var tmp = greenSpinBox.value / 255.0 + if (colorPicker.green !== tmp && !colorPicker.block) { + colorPicker.green = tmp + colorPicker.invalidateColor() + colorPicker.updateColor() } } - - DoubleSpinBox { - id: hslAlphaSpinBox - width: StudioTheme.Values.colorEditorPopupSpinBoxWidth - onValueModified: { - if (colorPicker.alpha !== hslAlphaSpinBox.value - && !colorPicker.block) { - colorPicker.alpha = hslAlphaSpinBox.value - colorPicker.invalidateColor() - colorPicker.updateColor() - } - } - onDragStarted: colorEditorTimer.stop() - onIndicatorPressed: colorEditorTimer.stop() - } + onDragStarted: colorEditorTimer.stop() + onIndicatorPressed: colorEditorTimer.stop() } - RowLayout { - id: hsvaRow - visible: colorPicker.mode === ColorPicker.Mode.HSVA - Layout.fillWidth: true - spacing: StudioTheme.Values.controlGap + DoubleSpinBox { + id: blueSpinBox + width: root.fourColumnWidth + stepSize: 1 + minimumValue: 0 + maximumValue: 255 + decimals: 0 - DoubleSpinBox { - id: hsvHueSpinBox - width: StudioTheme.Values.colorEditorPopupSpinBoxWidth - onValueModified: { - if (colorPicker.hue !== hsvHueSpinBox.value - && !colorPicker.block) { - colorPicker.hue = hsvHueSpinBox.value - colorPicker.invalidateColor() - colorPicker.updateColor() - } + onValueModified: { + var tmp = blueSpinBox.value / 255.0 + if (colorPicker.blue !== tmp && !colorPicker.block) { + colorPicker.blue = tmp + colorPicker.invalidateColor() + colorPicker.updateColor() } - onDragStarted: colorEditorTimer.stop() - onIndicatorPressed: colorEditorTimer.stop() } + onDragStarted: colorEditorTimer.stop() + onIndicatorPressed: colorEditorTimer.stop() + } - DoubleSpinBox { - id: hsvSaturationSpinBox - width: StudioTheme.Values.colorEditorPopupSpinBoxWidth - onValueModified: { - if (colorPicker.saturationHSV !== hsvSaturationSpinBox.value - && !colorPicker.block) { - colorPicker.saturationHSV = hsvSaturationSpinBox.value - colorPicker.invalidateColor() - colorPicker.updateColor() - } - } - onDragStarted: colorEditorTimer.stop() - onIndicatorPressed: colorEditorTimer.stop() - } + DoubleSpinBox { + id: rgbAlphaSpinBox + width: root.fourColumnWidth + stepSize: 1 + minimumValue: 0 + maximumValue: 255 + decimals: 0 - DoubleSpinBox { - id: hsvValueSpinBox - width: StudioTheme.Values.colorEditorPopupSpinBoxWidth - onValueModified: { - if (colorPicker.value !== hsvValueSpinBox.value - && !colorPicker.block) { - colorPicker.value = hsvValueSpinBox.value - colorPicker.invalidateColor() - colorPicker.updateColor() - } + onValueModified: { + var tmp = rgbAlphaSpinBox.value / 255.0 + if (colorPicker.alpha !== tmp && !colorPicker.block) { + colorPicker.alpha = tmp + colorPicker.invalidateColor() + colorPicker.updateColor() } - onDragStarted: colorEditorTimer.stop() - onIndicatorPressed: colorEditorTimer.stop() } + onDragStarted: colorEditorTimer.stop() + onIndicatorPressed: colorEditorTimer.stop() + } + } - DoubleSpinBox { - id: hsvAlphaSpinBox - width: StudioTheme.Values.colorEditorPopupSpinBoxWidth - onValueModified: { - if (colorPicker.alpha !== hsvAlphaSpinBox.value - && !colorPicker.block) { - colorPicker.alpha = hsvAlphaSpinBox.value - colorPicker.invalidateColor() - colorPicker.updateColor() - } + Row { + id: hslaRow + visible: colorPicker.mode === ColorPicker.Mode.HSLA + spacing: StudioTheme.Values.controlGap + + DoubleSpinBox { + id: hslHueSpinBox + width: root.fourColumnWidth + onValueModified: { + if (colorPicker.hue !== hslHueSpinBox.value + && !colorPicker.block) { + colorPicker.hue = hslHueSpinBox.value + colorPicker.invalidateColor() + colorPicker.updateColor() } - onDragStarted: colorEditorTimer.stop() - onIndicatorPressed: colorEditorTimer.stop() } + onDragStarted: colorEditorTimer.stop() + onIndicatorPressed: colorEditorTimer.stop() + } + + DoubleSpinBox { + id: hslSaturationSpinBox + width: root.fourColumnWidth + onValueModified: { + if (colorPicker.saturationHSL !== hslSaturationSpinBox.value + && !colorPicker.block) { + colorPicker.saturationHSL = hslSaturationSpinBox.value + colorPicker.invalidateColor() + colorPicker.updateColor() + } + } + onDragStarted: colorEditorTimer.stop() + onIndicatorPressed: colorEditorTimer.stop() + } + + DoubleSpinBox { + id: hslLightnessSpinBox + width: root.fourColumnWidth + onValueModified: { + if (colorPicker.lightness !== hslLightnessSpinBox.value + && !colorPicker.block) { + colorPicker.lightness = hslLightnessSpinBox.value + colorPicker.invalidateColor() + colorPicker.updateColor() + } + } + onDragStarted: colorEditorTimer.stop() + onIndicatorPressed: colorEditorTimer.stop() + } + + DoubleSpinBox { + id: hslAlphaSpinBox + width: root.fourColumnWidth + onValueModified: { + if (colorPicker.alpha !== hslAlphaSpinBox.value + && !colorPicker.block) { + colorPicker.alpha = hslAlphaSpinBox.value + colorPicker.invalidateColor() + colorPicker.updateColor() + } + } + onDragStarted: colorEditorTimer.stop() + onIndicatorPressed: colorEditorTimer.stop() + } + } + + Row { + id: hsvaRow + visible: colorPicker.mode === ColorPicker.Mode.HSVA + spacing: StudioTheme.Values.controlGap + + DoubleSpinBox { + id: hsvHueSpinBox + width: root.fourColumnWidth + onValueModified: { + if (colorPicker.hue !== hsvHueSpinBox.value + && !colorPicker.block) { + colorPicker.hue = hsvHueSpinBox.value + colorPicker.invalidateColor() + colorPicker.updateColor() + } + } + onDragStarted: colorEditorTimer.stop() + onIndicatorPressed: colorEditorTimer.stop() + } + + DoubleSpinBox { + id: hsvSaturationSpinBox + width: root.fourColumnWidth + onValueModified: { + if (colorPicker.saturationHSV !== hsvSaturationSpinBox.value + && !colorPicker.block) { + colorPicker.saturationHSV = hsvSaturationSpinBox.value + colorPicker.invalidateColor() + colorPicker.updateColor() + } + } + onDragStarted: colorEditorTimer.stop() + onIndicatorPressed: colorEditorTimer.stop() + } + + DoubleSpinBox { + id: hsvValueSpinBox + width: root.fourColumnWidth + onValueModified: { + if (colorPicker.value !== hsvValueSpinBox.value + && !colorPicker.block) { + colorPicker.value = hsvValueSpinBox.value + colorPicker.invalidateColor() + colorPicker.updateColor() + } + } + onDragStarted: colorEditorTimer.stop() + onIndicatorPressed: colorEditorTimer.stop() + } + + DoubleSpinBox { + id: hsvAlphaSpinBox + width: root.fourColumnWidth + onValueModified: { + if (colorPicker.alpha !== hsvAlphaSpinBox.value + && !colorPicker.block) { + colorPicker.alpha = hsvAlphaSpinBox.value + colorPicker.invalidateColor() + colorPicker.updateColor() + } + } + onDragStarted: colorEditorTimer.stop() + onIndicatorPressed: colorEditorTimer.stop() } } } - - Section { - caption: qsTr("Palette") - anchors.left: parent.left - anchors.right: parent.right - leftPadding: 10 - rightPadding: 10 - bottomPadding: 5 - - ColorPalette { - id: colorPalette - enableSingletonConnection: cePopup.opened - onSelectedColorChanged: { - colorPicker.color = colorPalette.selectedColor - colorEditor.color = colorPalette.selectedColor - } - onDialogColorChanged: { - colorPicker.color = colorPalette.selectedColor - colorEditor.color = colorPalette.selectedColor - } - } - } - - Section { - id: gradientControls - caption: qsTr("Gradient Controls") - anchors.left: parent.left - anchors.right: parent.right - visible: !cePopup.isNotInGradientMode() - leftPadding: 10 - rightPadding: 10 - - component ControlsRow: RowLayout { - property alias propertyName: spinBox.propertyName - property alias gradientTypeName: spinBox.gradientTypeName - property alias labelText: label.text - property alias labelTooltip: label.tooltip - property alias value: spinBox.value - - Layout.fillWidth: true - spacing: 0 - - Connections { - target: ceMode - function onActivated() { - spinBox.readValue() - } - } - - Connections { - target: modelNodeBackend - function onSelectionChanged() { - spinBox.readValue() - } - } - - ControlLabel { - id: label - horizontalAlignment: Text.AlignLeft - width: StudioTheme.Values.controlGap - + StudioTheme.Values.colorEditorPopupSpinBoxWidth - } - - Spacer { implicitWidth: StudioTheme.Values.controlLabelGap } - - GradientPropertySpinBox { - id: spinBox - implicitWidth: StudioTheme.Values.controlGap - + 2 * StudioTheme.Values.colorEditorPopupSpinBoxWidth - width: implicitWidth - } - } - - // Default Gradient Controls - Column { - id: defaultGradientControls - spacing: 10 - visible: cePopup.hasLinearGradient() && !colorEditor.shapeGradients - - RowLayout { - id: defaultGradientOrientation - Layout.fillWidth: true - spacing: 0 - - StudioControls.ComboBox { - id: gradientOrientation - implicitWidth: StudioTheme.Values.controlGap - + 3 * StudioTheme.Values.colorEditorPopupSpinBoxWidth - width: implicitWidth - model: [{ value: Gradient.Vertical, text: qsTr("Vertical") }, - { value: Gradient.Horizontal, text: qsTr("Horizontal") }] - textRole: "text" - valueRole: "value" - - onActivated: { - gradientLine.model.setGradientOrientation(gradientOrientation.currentValue) - cePopup.updateThumbnail() - } - - Component.onCompleted: { - var orientation = gradientLine.model.readGradientOrientation() - - if (orientation === "Horizontal") - gradientOrientation.currentIndex = - gradientOrientation.indexOfValue(Gradient.Horizontal) - else - gradientOrientation.currentIndex = - gradientOrientation.indexOfValue(Gradient.Vertical) - } - } - - Spacer { implicitWidth: StudioTheme.Values.controlLabelGap + 6 } - - IconLabel { - id: iconLabel - icon: StudioTheme.Constants.orientation - pixelSize: StudioTheme.Values.myIconFontSize * 1.4 - tooltip: qsTr("Defines the direction of the gradient.") - } - } - } - - // Linear Gradient Controls - Column { - id: linearGradientControls - spacing: 10 - visible: cePopup.hasLinearGradient() && colorEditor.shapeGradients - readonly property string gradientTypeName: "LinearGradient" - - ControlsRow { - propertyName: "x1" - gradientTypeName: linearGradientControls.gradientTypeName - labelText: "X1" - labelTooltip: qsTr("Defines the start point for color interpolation.") - } - - ControlsRow { - propertyName: "x2" - gradientTypeName: linearGradientControls.gradientTypeName - labelText: "X2" - labelTooltip: qsTr("Defines the end point for color interpolation.") - } - - ControlsRow { - propertyName: "y1" - gradientTypeName: linearGradientControls.gradientTypeName - labelText: "Y1" - labelTooltip: qsTr("Defines the start point for color interpolation.") - } - - ControlsRow { - propertyName: "y2" - gradientTypeName: linearGradientControls.gradientTypeName - labelText: "Y2" - labelTooltip: qsTr("Defines the end point for color interpolation.") - } - } - - // Radial Gradient Controls - Column { - id: radialGradientControls - spacing: 10 - visible: cePopup.hasRadialGradient() - readonly property string gradientTypeName: "RadialGradient" - - ControlsRow { - propertyName: "centerX" - gradientTypeName: radialGradientControls.gradientTypeName - labelText: "CenterX" - labelTooltip: qsTr("Defines the center point.") - } - - ControlsRow { - propertyName: "centerY" - gradientTypeName: radialGradientControls.gradientTypeName - labelText: "CenterY" - labelTooltip: qsTr("Defines the center point.") - } - - ControlsRow { - propertyName: "focalX" - gradientTypeName: radialGradientControls.gradientTypeName - labelText: "FocalX" - labelTooltip: qsTr("Defines the focal point.") - } - - ControlsRow { - propertyName: "focalY" - gradientTypeName: radialGradientControls.gradientTypeName - labelText: "FocalY" - labelTooltip: qsTr("Defines the focal point.") - } - - ControlsRow { - propertyName: "centerRadius" - gradientTypeName: radialGradientControls.gradientTypeName - labelText: "Center Radius" - labelTooltip: qsTr("Defines the center radius.") - } - - ControlsRow { - propertyName: "focalRadius" - gradientTypeName: radialGradientControls.gradientTypeName - labelText: "Focal Radius" - labelTooltip: qsTr("Defines the focal radius. Set to 0 for simple radial gradients.") - } - } - - // Conical Gradient Controls - Column { - id: conicalGradientControls - spacing: 10 - visible: cePopup.hasConicalGradient() - readonly property string gradientTypeName: "ConicalGradient" - - ControlsRow { - propertyName: "centerX" - gradientTypeName: conicalGradientControls.gradientTypeName - labelText: "CenterX" - labelTooltip: qsTr("Defines the center point.") - } - - ControlsRow { - propertyName: "centerY" - gradientTypeName: conicalGradientControls.gradientTypeName - labelText: "CenterY" - labelTooltip: qsTr("Defines the center point.") - } - - ControlsRow { - propertyName: "angle" - gradientTypeName: conicalGradientControls.gradientTypeName - labelText: "Angle" - labelTooltip: qsTr("Defines the start angle for the conical gradient. The value is in degrees (0-360).") - } - } - } - - } - } //content - background: Rectangle { - color: StudioTheme.Values.themeControlBackground - border.color: StudioTheme.Values.themeInteraction - border.width: StudioTheme.Values.border + StudioControls.Section { + id: sectionPalette + caption: qsTr("Palette") + anchors.left: parent.left + anchors.right: parent.right + bottomPadding: root.isSolid() ? 0 : sectionPalette.style.sectionHeadSpacerHeight + collapsedBottomPadding: root.isSolid() ? 0 : StudioTheme.Values.border + + StudioControls.ColorPalette { + id: colorPalette + width: root.width + twoColumnWidth: root.twoColumnWidth + fourColumnWidth: root.fourColumnWidth + + enableSingletonConnection: root.opened + onSelectedColorChanged: { + colorPicker.color = colorPalette.selectedColor + colorEditor.color = colorPalette.selectedColor + } + onDialogColorChanged: { + colorPicker.color = colorPalette.selectedColor + colorEditor.color = colorPalette.selectedColor + } + } + } + + StudioControls.Section { + id: gradientControls + caption: qsTr("Gradient Controls") + visible: !root.isSolid() + anchors.left: parent.left + anchors.right: parent.right + bottomPadding: 0 + collapsedBottomPadding: 0 + + component ControlsRow: Row { + property alias propertyName: spinBox.propertyName + property alias gradientTypeName: spinBox.gradientTypeName + property alias labelText: label.text + property alias labelTooltip: label.tooltip + property alias value: spinBox.value + + spacing: StudioTheme.Values.controlGap + + Connections { + target: ceMode + function onActivated() { + spinBox.readValue() + } + } + + Connections { + target: modelNodeBackend + function onSelectionChanged() { + spinBox.readValue() + } + } + + ControlLabel { + id: label + anchors.verticalCenter: parent.verticalCenter + horizontalAlignment: Text.AlignRight + width: root.fourColumnWidth + } + + GradientPropertySpinBox { + id: spinBox + spinBoxWidth: root.twoColumnWidth + unitWidth: root.fourColumnWidth + } + } + + // Default Gradient Controls + Column { + id: defaultGradientControls + spacing: StudioTheme.Values.colorEditorPopupSpacing + visible: root.isLinearGradient() && !colorEditor.shapeGradients + + Row { + id: defaultGradientOrientation + spacing: StudioTheme.Values.controlGap + + StudioControls.ComboBox { + id: gradientOrientation + implicitWidth: root.twoColumnWidth + width: gradientOrientation.implicitWidth + model: [{ value: Gradient.Vertical, text: qsTr("Vertical") }, + { value: Gradient.Horizontal, text: qsTr("Horizontal") }] + textRole: "text" + valueRole: "value" + actionIndicatorVisible: false + + onActivated: { + gradientLine.model.setGradientOrientation(gradientOrientation.currentValue) + root.updateThumbnail() + } + + Component.onCompleted: { + var orientation = gradientLine.model.readGradientOrientation() + + if (orientation === "Horizontal") { + gradientOrientation.currentIndex = + gradientOrientation.indexOfValue(Gradient.Horizontal) + } else { + gradientOrientation.currentIndex = + gradientOrientation.indexOfValue(Gradient.Vertical) + } + } + } + + IconLabel { + id: iconLabel + anchors.verticalCenter: parent.verticalCenter + icon: StudioTheme.Constants.orientation + pixelSize: StudioTheme.Values.myIconFontSize * 1.4 + tooltip: qsTr("Defines the direction of the gradient.") + } + } + } + + // Linear Gradient Controls + Column { + id: linearGradientControls + spacing: StudioTheme.Values.colorEditorPopupSpacing + visible: root.isLinearGradient() && colorEditor.shapeGradients + readonly property string gradientTypeName: "LinearGradient" + + ControlsRow { + propertyName: "x1" + gradientTypeName: linearGradientControls.gradientTypeName + labelText: "X1" + labelTooltip: qsTr("Defines the start point for color interpolation.") + } + + ControlsRow { + propertyName: "x2" + gradientTypeName: linearGradientControls.gradientTypeName + labelText: "X2" + labelTooltip: qsTr("Defines the end point for color interpolation.") + } + + ControlsRow { + propertyName: "y1" + gradientTypeName: linearGradientControls.gradientTypeName + labelText: "Y1" + labelTooltip: qsTr("Defines the start point for color interpolation.") + } + + ControlsRow { + propertyName: "y2" + gradientTypeName: linearGradientControls.gradientTypeName + labelText: "Y2" + labelTooltip: qsTr("Defines the end point for color interpolation.") + } + } + + // Radial Gradient Controls + Column { + id: radialGradientControls + spacing: StudioTheme.Values.colorEditorPopupSpacing + visible: root.isRadialGradient() + readonly property string gradientTypeName: "RadialGradient" + + ControlsRow { + propertyName: "centerX" + gradientTypeName: radialGradientControls.gradientTypeName + labelText: "CenterX" + labelTooltip: qsTr("Defines the center point.") + } + + ControlsRow { + propertyName: "centerY" + gradientTypeName: radialGradientControls.gradientTypeName + labelText: "CenterY" + labelTooltip: qsTr("Defines the center point.") + } + + ControlsRow { + propertyName: "focalX" + gradientTypeName: radialGradientControls.gradientTypeName + labelText: "FocalX" + labelTooltip: qsTr("Defines the focal point.") + } + + ControlsRow { + propertyName: "focalY" + gradientTypeName: radialGradientControls.gradientTypeName + labelText: "FocalY" + labelTooltip: qsTr("Defines the focal point.") + } + + ControlsRow { + propertyName: "centerRadius" + gradientTypeName: radialGradientControls.gradientTypeName + labelText: "Center Radius" + labelTooltip: qsTr("Defines the center radius.") + } + + ControlsRow { + propertyName: "focalRadius" + gradientTypeName: radialGradientControls.gradientTypeName + labelText: "Focal Radius" + labelTooltip: qsTr("Defines the focal radius. Set to 0 for simple radial gradients.") + } + } + + // Conical Gradient Controls + Column { + id: conicalGradientControls + spacing: StudioTheme.Values.colorEditorPopupSpacing + visible: root.isConicalGradient() + readonly property string gradientTypeName: "ConicalGradient" + + ControlsRow { + propertyName: "centerX" + gradientTypeName: conicalGradientControls.gradientTypeName + labelText: "CenterX" + labelTooltip: qsTr("Defines the center point.") + } + + ControlsRow { + propertyName: "centerY" + gradientTypeName: conicalGradientControls.gradientTypeName + labelText: "CenterY" + labelTooltip: qsTr("Defines the center point.") + } + + ControlsRow { + propertyName: "angle" + gradientTypeName: conicalGradientControls.gradientTypeName + labelText: "Angle" + labelTooltip: qsTr("Defines the start angle for the conical gradient. The value is in degrees (0-360).") + } + } + } } - - enter: Transition {} - exit: Transition {} } diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ColorLine.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ColorLine.qml deleted file mode 100644 index 245a655a62b..00000000000 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ColorLine.qml +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -import QtQuick 2.15 -import HelperWidgets 2.0 -import StudioTheme 1.0 as StudioTheme - -Item { - property alias currentColor: colorLine.color - - width: 300 - height: StudioTheme.Values.colorEditorPopupLineHeight - - Image { - id: checkerboard - anchors.fill: colorLine - source: "images/checkers.png" - fillMode: Image.Tile - } - - Rectangle { - id: colorLine - height: StudioTheme.Values.hueSliderHeight - width: parent.width - border.color: StudioTheme.Values.themeControlOutline - border.width: StudioTheme.Values.border - color: "white" - anchors.bottom: parent.bottom - } -} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/GradientPresetList.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/GradientPresetList.qml index c37787df500..6af54c11b98 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/GradientPresetList.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/GradientPresetList.qml @@ -1,27 +1,26 @@ // Copyright (C) 2021 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 -import QtQuick 2.15 -import QtQuick.Layouts 1.15 -import Qt.labs.platform 1.1 -import QtQuickDesignerTheme 1.0 -import HelperWidgets 2.0 -import StudioControls 1.0 as StudioControls -import StudioTheme 1.0 as StudioTheme +import QtQuick +import QtQuick.Layouts +import Qt.labs.platform +import QtQuickDesignerTheme +import HelperWidgets +import StudioControls as StudioControls +import StudioTheme as StudioTheme - -HelperWindow { - id: dialogWindow +Window { + id: root width: 1200 height: 650 title: qsTr("Gradient Picker") + flags: Qt.Dialog signal saved signal applied signal accepted property alias gradientData: gradientPickerData - QtObject { id: gradientPickerData property var stops @@ -104,9 +103,9 @@ HelperWindow { Layout.alignment: Qt.AlignBottom | Qt.AlignRight Layout.topMargin: 5 - Button { id: buttonClose; text: qsTr("Close"); onClicked: { dialogWindow.hide(); } } - Button { id: buttonSave; text: qsTr("Save"); onClicked: { dialogWindow.saved(); } } - Button { id: buttonApply; text: qsTr("Apply"); onClicked: { dialogWindow.applied(); dialogWindow.hide(); } } + Button { id: buttonClose; text: qsTr("Close"); onClicked: { root.hide(); } } + Button { id: buttonSave; text: qsTr("Save"); onClicked: { root.saved(); } } + Button { id: buttonApply; text: qsTr("Apply"); onClicked: { root.applied(); root.hide(); } } } } } diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/GradientPropertySpinBox.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/GradientPropertySpinBox.qml index 2656c251177..cdd080be09f 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/GradientPropertySpinBox.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/GradientPropertySpinBox.qml @@ -1,13 +1,12 @@ -// Copyright (C) 2021 The Qt Company Ltd. +// Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 -import QtQuick 2.15 -import QtQuick.Layouts 1.15 -import StudioControls 1.0 as StudioControls -import StudioTheme 1.0 as StudioTheme +import QtQuick +import StudioControls as StudioControls +import StudioTheme as StudioTheme -SecondColumnLayout { - id: wrapper +Row { + id: root property string propertyName property string gradientTypeName @@ -20,24 +19,26 @@ SecondColumnLayout { property alias pixelsPerUnit: spinBox.pixelsPerUnit - width: 90 - implicitHeight: spinBox.height + property real spinBoxWidth: 100 + property real unitWidth: 50 + + spacing: StudioTheme.Values.controlGap onFocusChanged: restoreCursor() property bool __isPercentage: false - property bool __mightHavePercents: gradientLine.model.isPercentageSupportedByProperty(wrapper.propertyName, wrapper.gradientTypeName) + property bool __mightHavePercents: gradientLine.model.isPercentageSupportedByProperty(root.propertyName, root.gradientTypeName) function readValue() { - wrapper.__isPercentage = (gradientLine.model.readGradientPropertyUnits(wrapper.propertyName) === GradientModel.Percentage); + root.__isPercentage = (gradientLine.model.readGradientPropertyUnits(root.propertyName) === GradientModel.Percentage); - if (wrapper.__isPercentage) { + if (root.__isPercentage) { unitType.currentIndex = 1; - spinBox.realValue = gradientLine.model.readGradientPropertyPercentage(wrapper.propertyName) + spinBox.realValue = gradientLine.model.readGradientPropertyPercentage(root.propertyName) } else { unitType.currentIndex = 0; - spinBox.realValue = gradientLine.model.readGradientProperty(wrapper.propertyName) + spinBox.realValue = gradientLine.model.readGradientProperty(root.propertyName) } } @@ -46,21 +47,21 @@ SecondColumnLayout { __devicePixelRatio: devicePixelRatio() - implicitWidth: StudioTheme.Values.colorEditorPopupSpinBoxWidth * 1.5 - width: implicitWidth + implicitWidth: root.spinBoxWidth + width: spinBox.implicitWidth actionIndicatorVisible: false realFrom: -9999 realTo: 9999 - realStepSize: wrapper.__isPercentage ? 0.1 : 1 - decimals: wrapper.__isPercentage ? 4 : 0 + realStepSize: root.__isPercentage ? 0.1 : 1 + decimals: root.__isPercentage ? 4 : 0 - Component.onCompleted: wrapper.readValue() + Component.onCompleted: root.readValue() onCompressedRealValueModified: { - if (wrapper.__isPercentage) - gradientLine.model.setGradientPropertyPercentage(wrapper.propertyName, spinBox.realValue) + if (root.__isPercentage) + gradientLine.model.setGradientPropertyPercentage(root.propertyName, spinBox.realValue) else - gradientLine.model.setGradientProperty(wrapper.propertyName, spinBox.realValue) + gradientLine.model.setGradientProperty(root.propertyName, spinBox.realValue) } onDragStarted: hideCursor() @@ -68,30 +69,24 @@ SecondColumnLayout { onDragging: holdCursorInPlace() } - Spacer { - implicitWidth: StudioTheme.Values.twoControlColumnGap - } - StudioControls.ComboBox { id: unitType - implicitWidth: StudioTheme.Values.colorEditorPopupSpinBoxWidth - width: implicitWidth + implicitWidth: root.unitWidth + width: unitType.implicitWidth model: ["px", "%"] //px = 0, % = 1 actionIndicatorVisible: false - visible: wrapper.__mightHavePercents + visible: root.__mightHavePercents onActivated: { - if (!wrapper.__mightHavePercents) + if (!root.__mightHavePercents) return if (unitType.currentIndex === 0) - gradientLine.model.setGradientPropertyUnits(wrapper.propertyName, GradientModel.Pixels) + gradientLine.model.setGradientPropertyUnits(root.propertyName, GradientModel.Pixels) else - gradientLine.model.setGradientPropertyUnits(wrapper.propertyName, GradientModel.Percentage) + gradientLine.model.setGradientPropertyUnits(root.propertyName, GradientModel.Percentage) - wrapper.readValue() + root.readValue() } } - - ExpandingSpacer {} } diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/IconIndicator.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/IconIndicator.qml index a1dd39c39b9..700d4465415 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/IconIndicator.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/IconIndicator.qml @@ -1,74 +1,11 @@ -// Copyright (C) 2021 The Qt Company Ltd. +// Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 -import QtQuick 2.15 -import QtQuick.Templates 2.15 as T -import HelperWidgets 2.0 -import StudioTheme 1.0 as StudioTheme +import QtQuick +import StudioControls as StudioControls -Rectangle { +StudioControls.IconIndicator { id: root - property Item myControl - property alias icon: indicatorIcon.text - property alias iconColor: indicatorIcon.color - property alias pixelSize: indicatorIcon.font.pixelSize - property alias tooltip: toolTipArea.tooltip - - property bool hovered: toolTipArea.containsMouse && root.enabled - - signal clicked() - - color: "transparent" - border.color: "transparent" - - implicitWidth: StudioTheme.Values.linkControlWidth - implicitHeight: StudioTheme.Values.linkControlHeight - - z: 10 - - T.Label { - id: indicatorIcon - anchors.fill: parent - text: "?" - visible: true - color: StudioTheme.Values.themeTextColor - font.family: StudioTheme.Constants.iconFont.family - font.pixelSize: StudioTheme.Values.myIconFontSize - verticalAlignment: Text.AlignVCenter - horizontalAlignment: Text.AlignHCenter - } - - ToolTipArea { - id: toolTipArea - anchors.fill: parent - onClicked: root.clicked() - } - - states: [ - State { - name: "default" - when: !toolTipArea.containsMouse && root.enabled - PropertyChanges { - target: indicatorIcon - color: StudioTheme.Values.themeLinkIndicatorColor - } - }, - State { - name: "hover" - when: toolTipArea.containsMouse && root.enabled - PropertyChanges { - target: indicatorIcon - color: StudioTheme.Values.themeLinkIndicatorColorHover - } - }, - State { - name: "disable" - when: !root.enabled - PropertyChanges { - target: indicatorIcon - color: StudioTheme.Values.themeLinkIndicatorColorDisabled - } - } - ] + property alias tooltip: root.toolTip } diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ToolTipArea.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ToolTipArea.qml index d54d64007a6..e093c09e0ca 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ToolTipArea.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ToolTipArea.qml @@ -1,26 +1,10 @@ -// Copyright (C) 2021 The Qt Company Ltd. +// Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 import QtQuick -import QtQuick.Layouts -import HelperWidgets +import StudioControls as StudioControls -MouseArea { - id: mouseArea - - Tooltip { id: myTooltip } - - onExited: myTooltip.hideText() - onCanceled: myTooltip.hideText() - onClicked: forceActiveFocus() - - hoverEnabled: true - - property string tooltip - - Timer { - interval: 1000 - running: mouseArea.containsMouse && tooltip.length - onTriggered: myTooltip.showText(mouseArea, Qt.point(mouseArea.mouseX, mouseArea.mouseY), tooltip) - } +StudioControls.ToolTipArea { + id: root + property alias tooltip: root.text } diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/qmldir b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/qmldir index 9c19a45e2ef..6fc885ac57b 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/qmldir +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/qmldir @@ -10,10 +10,7 @@ ButtonRowButton 2.0 ButtonRowButton.qml CharacterSection 2.0 CharacterSection.qml CheckBox 2.0 CheckBox.qml ColorEditor 2.0 ColorEditor.qml -ColorLine 2.0 ColorLine.qml ColorLogic 2.0 ColorLogic.qml -ColorPalette 2.0 ColorPalette.qml -ColorPicker 2.0 ColorPicker.qml ComboBox 2.0 ComboBox.qml ComponentButton 2.0 ComponentButton.qml ComponentSection 2.0 ComponentSection.qml @@ -36,7 +33,6 @@ GradientPresetList 2.0 GradientPresetList.qml GradientPresetTabContent 2.0 GradientPresetTabContent.qml GradientPropertySpinBox 2.0 GradientPropertySpinBox.qml HorizontalScrollBar 2.0 HorizontalScrollBar.qml -HueSlider 2.0 HueSlider.qml IconIndicator 2.0 IconIndicator.qml IconButton 2.0 IconButton.qml IconLabel 2.0 IconLabel.qml @@ -48,10 +44,8 @@ Label 2.0 Label.qml LineEdit 2.0 LineEdit.qml LinkIndicator2D 2.0 LinkIndicator2D.qml ListViewComboBox 2.0 ListViewComboBox.qml -LuminanceSlider 2.0 LuminanceSlider.qml MarginSection 2.0 MarginSection.qml MultiIconLabel 2.0 MultiIconLabel.qml -OpacitySlider 2.0 OpacitySlider.qml OriginControl 2.0 OriginControl.qml OriginIndicator 2.0 OriginIndicator.qml OriginSelector 2.0 OriginSelector.qml diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/ActionIndicator.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/ActionIndicator.qml index 1d52ac9244a..96a28d6d5c8 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/ActionIndicator.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/ActionIndicator.qml @@ -30,11 +30,11 @@ Rectangle { id: icon anchors.fill: parent text: StudioTheme.Constants.actionIcon - visible: text !== StudioTheme.Constants.actionIcon || control.forceVisible - || (control.__parentControl !== undefined && - ((control.__parentControl.edit !== undefined && control.__parentControl.edit) - || (control.__parentControl.hover !== undefined && control.__parentControl.hover) - || (control.__parentControl.drag !== undefined && control.__parentControl.drag))) + visible: icon.text !== StudioTheme.Constants.actionIcon || control.forceVisible + || ((control.__parentControl ?? false) && + ((control.__parentControl.edit ?? false) + || (control.__parentControl.hover ?? false) + || (control.__parentControl.drag ?? false))) color: control.style.icon.idle font.family: StudioTheme.Constants.iconFont.family font.pixelSize: control.style.baseIconFontSize diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/ColorEditor.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/ColorEditor.qml new file mode 100644 index 00000000000..458cceb31e1 --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/ColorEditor.qml @@ -0,0 +1,138 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import QtQuick +import StudioTheme as StudioTheme +import QtQuickDesignerTheme +import QtQuickDesignerColorPalette +import StudioHelpers + +Item { + id: root + + property StudioTheme.ControlStyle style: StudioTheme.Values.controlStyle + + property alias color: colorBackend.color + + property alias actionIndicatorVisible: actionIndicator.visible + property real __actionIndicatorWidth: root.style.actionIndicatorSize.width + property real __actionIndicatorHeight: root.style.actionIndicatorSize.height + + property alias showHexTextField: hexTextField.visible + + width: root.style.controlSize.width + height: root.style.controlSize.height + + ColorBackend { + id: colorBackend + } + + ActionIndicator { + id: actionIndicator + style: root.style + __parentControl: root + x: 0 + y: 0 + width: actionIndicator.visible ? root.__actionIndicatorWidth : 0 + height: actionIndicator.visible ? root.__actionIndicatorHeight : 0 + } + + Rectangle { + id: preview + x: actionIndicator.width + y: 0 + z: previewMouseArea.containsMouse ? 10 : 0 + implicitWidth: root.style.controlSize.height + implicitHeight: root.style.controlSize.height + color: root.color + border.color: previewMouseArea.containsMouse ? root.style.border.hover : root.style.border.idle + border.width: root.style.borderWidth + + Image { + anchors.fill: parent + source: "qrc:/navigator/icon/checkers.png" + fillMode: Image.Tile + z: -1 + } + + MouseArea { + id: previewMouseArea + anchors.fill: parent + hoverEnabled: true + onClicked: { + popupDialog.visibility ? popupDialog.close() : popupDialog.open() + previewMouseArea.forceActiveFocus() + } + } + + PopupDialog { + id: popupDialog + + property QtObject loaderItem: loader.item + + width: 260 + + function ensureLoader() { + if (!loader.active) + loader.active = true + } + + function open() { + popupDialog.ensureLoader() + popupDialog.show(preview) + + if (loader.status === Loader.Ready) + loader.item.originalColor = root.color + } + + Loader { + id: loader + + sourceComponent: ColorEditorPopup { + id: popup + width: popupDialog.contentWidth + + onActivateColor: function(color) { + colorBackend.activateColor(color) + } + } + + Binding { + target: loader.item + property: "color" + value: root.color + when: loader.status === Loader.Ready + } + + onLoaded: { + loader.item.originalColor = root.color + popupDialog.titleBar = loader.item.titleBarContent + } + } + } + } + + TextField { + id: hexTextField + style: root.style + x: actionIndicator.width + preview.width - preview.border.width + y: 0 + width: root.width - hexTextField.x + text: root.color + actionIndicatorVisible: false + translationIndicatorVisible: false + indicatorVisible: true + indicator.icon.text: StudioTheme.Constants.copy_small + indicator.onClicked: { + hexTextField.selectAll() + hexTextField.copy() + hexTextField.deselect() + } + + validator: RegularExpressionValidator { + regularExpression: /#[0-9A-Fa-f]{6}([0-9A-Fa-f]{2})?/g + } + + onAccepted: colorBackend.activateColor(colorFromString(hexTextField.text)) + } +} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/IconIndicator.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/IconIndicator.qml new file mode 100644 index 00000000000..cc8bf7645e1 --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/IconIndicator.qml @@ -0,0 +1,70 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import QtQuick +import QtQuick.Templates as T +import StudioTheme as StudioTheme + +Item { + id: root + + property Item myControl + property alias icon: indicatorIcon.text + property alias iconColor: indicatorIcon.color + property alias pixelSize: indicatorIcon.font.pixelSize + property alias toolTip: toolTipArea.text + + property bool hovered: toolTipArea.containsMouse && root.enabled + + signal clicked() + + implicitWidth: StudioTheme.Values.linkControlWidth + implicitHeight: StudioTheme.Values.linkControlHeight + + z: 10 + + T.Label { + id: indicatorIcon + anchors.fill: parent + text: "?" + visible: true + color: StudioTheme.Values.themeTextColor + font.family: StudioTheme.Constants.iconFont.family + font.pixelSize: StudioTheme.Values.myIconFontSize + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignHCenter + } + + ToolTipArea { + id: toolTipArea + anchors.fill: parent + onClicked: root.clicked() + } + + states: [ + State { + name: "default" + when: !toolTipArea.containsMouse && root.enabled + PropertyChanges { + target: indicatorIcon + color: StudioTheme.Values.themeLinkIndicatorColor + } + }, + State { + name: "hover" + when: toolTipArea.containsMouse && root.enabled + PropertyChanges { + target: indicatorIcon + color: StudioTheme.Values.themeLinkIndicatorColorHover + } + }, + State { + name: "disable" + when: !root.enabled + PropertyChanges { + target: indicatorIcon + color: StudioTheme.Values.themeLinkIndicatorColorDisabled + } + } + ] +} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/PopupDialog.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/PopupDialog.qml new file mode 100644 index 00000000000..d5243f996b7 --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/PopupDialog.qml @@ -0,0 +1,460 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import QtQuick +import QtQuick.Controls.Basic as Basic +import QtQuick.Window +import QtQuick.Shapes +import StudioTheme as StudioTheme +import QtQuickDesignerWindowManager + +QtObject { + id: root + + property StudioTheme.ControlStyle style: StudioTheme.Values.controlStyle + + property alias titleBar: titleBarContent.children + default property alias content: mainContent.children + property alias contentWidth: mainContent.width + + property int width: 320 + property int height: column.implicitHeight + property int maximumWidth: -1 + property int maximumHeight: -1 + + property alias flags: window.flags + property alias visible: window.visible + + property int edge: Qt.LeftEdge + property int actualEdge: root.edge + property alias chevronVisible: chevron.visible + + property rect __itemGlobal: Qt.rect(0, 0, 100, 100) + + signal closing(close: var) + + function show(target: Item) { + var originGlobal = target.mapToGlobal(0, 0) + root.__itemGlobal = Qt.rect(originGlobal.x, originGlobal.y, target.width, target.height) + + root.layout() + window.show() + window.raise() + } + + function close() { + window.close() + } + + function layout() { + // Setup + var screen = Qt.rect(0, //Screen.virtualX, // TODO + 0, //Screen.virtualY, // TODO + Screen.desktopAvailableWidth, + Screen.desktopAvailableHeight) + + // Collect region information + let edges = window.getRegions(screen, root.__itemGlobal) + + if (Object.keys(edges).length === 0) { + console.log("Warning: Couldn't find regions.") + return + } + + // Determine edge + let edge = window.findPlacement(edges) + root.actualEdge = edge + + let anchor = edges[edge].anchor + let popoverRect = window.popoverGeometry(edge, anchor, edges[edge].region) + + if (chevron.visible) + chevron.layout(edge, popoverRect, anchor) + + window.x = popoverRect.x + window.y = popoverRect.y + } + + property Window window: Window { + id: window + + property int margin: 20 + + width: root.width + (2 * window.margin) + height: root.height + (2 * window.margin) + maximumWidth: { + if (root.maximumWidth < 0) + return root.width + (2 * window.margin) + else + return root.maximumWidth + (2 * window.margin) + } + maximumHeight:{ + if (root.maximumHeight < 0) + return root.height + (2 * window.margin) + else + return root.maximumHeight + (2 * window.margin) + } + visible: false + flags: Qt.FramelessWindowHint | Qt.Dialog + color: "transparent" + + onClosing: function (close) { + root.closing(close) + } + + function findPlacement(edges: var): int { + if (edges[root.edge].fit) // Original edge does fit + return root.edge + + return window.findAlternativePlacement(edges) + } + + function findAlternativePlacement(edges: var): int { + let horizontal = (Qt.LeftEdge | Qt.RightEdge) + if (root.edge & horizontal) + if (edges[root.edge ^ horizontal].fit) + return root.edge ^ horizontal + + let vertical = (Qt.TopEdge | Qt.BottomEdge) + if (root.edge & vertical) + if (edges[root.edge ^ vertical].fit) + return root.edge ^ vertical + + // Take the first that fits + for (var key in edges) { + if (edges[key].fit) + return Number(key) + } + + return Qt.LeftEdge // Default + } + + function contains(a: rect, b: rect): boolean { + let halfSizeA = Qt.size(a.width * 0.5, a.height * 0.5) + let halfSizeB = Qt.size(b.width * 0.5, b.height * 0.5) + + let centerA = Qt.point(a.x + halfSizeA.width, a.y + halfSizeA.height) + let centerB = Qt.point(b.x + halfSizeB.width, b.y + halfSizeB.height) + + if (Math.abs(centerA.x - centerB.x) > (halfSizeA.width + halfSizeB.width)) + return false + + if (Math.abs(centerA.y - centerB.y) > (halfSizeA.height + halfSizeB.height)) + return false + + return true + } + + function getRegions(source: rect, target: rect) { + var edges = {} + + // Overlaps or Inside + if (!window.contains(source, target)) + return edges + + var targetCenter = Qt.point(target.x + (target.width * 0.5), + target.y + (target.height * 0.5)) + + // TOP + let topAnchor = Qt.point(targetCenter.x, target.y) + let topRegion = Qt.rect(source.x, source.y, source.width, Math.max(0, topAnchor.y)) + + edges[Qt.TopEdge] = { + anchor: topAnchor, + region: topRegion, + fit: topRegion.width >= window.maximumWidth + && topRegion.height >= window.maximumHeight + } + + // RIGHT + let rightAnchor = Qt.point(target.x + target.width, targetCenter.y) + let rightRegion = Qt.rect(rightAnchor.x, + source.y, + Math.max(0, source.width - rightAnchor.x), + source.height) + + edges[Qt.RightEdge] = { + anchor: rightAnchor, + region: rightRegion, + fit: rightRegion.width >= window.maximumWidth + && rightRegion.height >= window.maximumHeight + } + + // BOTTOM + let bottomAnchor = Qt.point(targetCenter.x, target.y + target.height) + let bottomRegion = Qt.rect(source.x, + bottomAnchor.y, + source.width, + Math.max(0, source.height - bottomAnchor.y)) + + edges[Qt.BottomEdge] = { + anchor: bottomAnchor, + region: bottomRegion, + fit: bottomRegion.width >= window.maximumWidth + && bottomRegion.height >= window.maximumHeight + } + + // LEFT + let leftAnchor = Qt.point(target.x, targetCenter.y) + let leftRegion = Qt.rect(source.x, source.y, Math.max(0, leftAnchor.x), source.height) + + edges[Qt.LeftEdge] = { + anchor: leftAnchor, + region: leftRegion, + fit: leftRegion.width >= window.maximumWidth + && leftRegion.height >= window.maximumHeight + } + + return edges + } + + function popoverGeometry(edge: int, anchor: point, region: rect) { + if (edge === Qt.TopEdge) { + let height = Math.min(window.height, region.height) + return Qt.rect(Math.max(0, Math.min(anchor.x - (window.width * 0.5), region.width - window.width)), + anchor.y - height, + Math.min(window.width, region.width), + height) + } + + if (edge === Qt.RightEdge) { + let width = Math.min(window.width, region.width) + return Qt.rect(anchor.x, + Math.max(0, Math.min(anchor.y - (window.height * 0.5), region.height - window.height)), + width, + Math.min(window.height, region.height)) + } + + if (edge === Qt.BottomEdge) { + let height = Math.min(window.height, region.height) + return Qt.rect(Math.max(0, Math.min(anchor.x - (window.width * 0.5), region.width - window.width)), + anchor.y, + Math.min(window.width, region.width), + height) + } + + if (edge === Qt.LeftEdge) { + let width = Math.min(window.width, region.width) + return Qt.rect(anchor.x - width, + Math.max(0, Math.min(anchor.y - (window.height * 0.5), region.height - window.height)), + width, + Math.min(window.height, region.height)) + } + } + + onHeightChanged: { + if (window.visible) + root.layout() + } + + Component.onCompleted: { + if (window.visible) + root.layout() + } + + Connections { + target: WindowManager + enabled: root.visible + + function onFocusWindowChanged(focusWindow) { + if (!focusWindow) + return + + if (focusWindow !== window && focusWindow.transientParent !== window) + root.close() + } + + function onAboutToQuit() { + root.close() + } + + function onMainWindowVisibleChanged(value) { + if (!value) + root.close() + } + } + + Rectangle { + id: background + anchors.fill: parent + anchors.margins: window.margin + color: StudioTheme.Values.themePopoutBackground + border.color: "#636363" + border.width: StudioTheme.Values.border + + TapHandler { + id: tapHandler + onTapped: root.close() + } + + containmentMask: QtObject { + function contains(point: point): bool { + return point.x < 0 || point.x > background.width + || point.y < 0 || point.y > background.height + } + } + } + + Shape { + id: chevron + + property int chevronWidth: window.margin + + anchors.fill: parent + + function layout(edge: int, rect: rect, anchor: point) { + let center = Qt.point(rect.x + (rect.width * 0.5), + rect.y + (rect.height * 0.5)) + + // Horizontal + if (edge === Qt.LeftEdge || edge === Qt.RightEdge) { + let topLimit = window.margin + let bottomLimit = window.height - window.margin - background.border.width + let point = Math.round((window.height * 0.5) + (anchor.y - center.y)) + + peak.y = Math.max(topLimit, Math.min(bottomLimit, point)) + + let topLimitChevron = topLimit + chevron.chevronWidth + let bottomLimitChevron = bottomLimit - chevron.chevronWidth + + path.startY = Math.max(topLimit, Math.min(bottomLimitChevron, point - chevron.chevronWidth)) + end.y = Math.max(topLimitChevron, Math.min(bottomLimit, point + chevron.chevronWidth)) + } + + if (edge === Qt.LeftEdge) { + peak.x = window.width - background.border.width + path.startX = end.x = window.width - window.margin - background.border.width + } + + if (edge === Qt.RightEdge) { + peak.x = background.border.width + path.startX = end.x = window.margin + background.border.width + } + + // Vertical + if (edge === Qt.TopEdge || edge === Qt.BottomEdge) { + let leftLimit = window.margin + background.border.width + let rightLimit = window.width - window.margin + let point = Math.round((window.width * 0.5) + (anchor.x - center.x)) + + peak.x = Math.max(leftLimit, Math.min(rightLimit, point)) + + let leftLimitChevron = leftLimit + chevron.chevronWidth + let rightLimitChevron = rightLimit - chevron.chevronWidth + + path.startX = Math.max(leftLimit, Math.min(rightLimitChevron, point - chevron.chevronWidth)) + end.x = Math.max(leftLimitChevron, Math.min(rightLimit, point + chevron.chevronWidth)) + } + + if (edge === Qt.TopEdge) { + peak.y = window.height - background.border.width + path.startY = end.y = window.height - window.margin - background.border.width + } + + if (edge === Qt.BottomEdge) { + peak.y = background.border.width + path.startY = end.y = window.margin + background.border.width + } + } + + ShapePath { + id: path + strokeStyle: ShapePath.SolidLine + strokeWidth: background.border.width + strokeColor: background.border.color + fillColor: background.color + startX: 0 + startY: 0 + + PathLine { id: peak; x: 0; y: 0 } + + PathLine { id: end; x: 0; y: 0 } + } + } + + Column { + id: column + anchors.fill: parent + anchors.margins: window.margin + StudioTheme.Values.border + + Item { + id: titleBarItem + width: parent.width + height: StudioTheme.Values.titleBarHeight + + Row { + id: row + anchors.fill: parent + anchors.leftMargin: StudioTheme.Values.popupMargin + anchors.rightMargin: StudioTheme.Values.popupMargin + spacing: 0 + + Item { + id: titleBarContent + width: row.width - closeIndicator.width + height: row.height + } + + IconIndicator { + id: closeIndicator + anchors.verticalCenter: parent.verticalCenter + icon: StudioTheme.Constants.colorPopupClose + pixelSize: StudioTheme.Values.myIconFontSize + onClicked: root.close() + } + } + } + + Rectangle { + width: parent.width - 8 + height: StudioTheme.Values.border + anchors.horizontalCenter: parent.horizontalCenter + color: "#636363" + } + + Basic.ScrollView { + id: scrollView + width: parent.width + height: { + let actualHeight = mainContent.childrenRect.height + 2 * StudioTheme.Values.popupMargin + + if (root.maximumHeight < 0) + return actualHeight + + return Math.min(actualHeight, + root.maximumHeight - titleBarItem.height - 3 * StudioTheme.Values.border) + } + padding: StudioTheme.Values.popupMargin + clip: true + + ScrollBar.vertical: TransientScrollBar { + id: verticalBar + style: StudioTheme.Values.controlStyle + parent: scrollView + x: scrollView.width - verticalBar.width + y: scrollView.topPadding + height: scrollView.availableHeight + orientation: Qt.Vertical + + show: (scrollView.hovered || scrollView.focus) && verticalBar.isNeeded + //otherInUse: horizontalBar.inUse + } + + Flickable { + id: frame + boundsMovement: Flickable.StopAtBounds + boundsBehavior: Flickable.StopAtBounds + contentWidth: mainContent.width + contentHeight: mainContent.height + + Item { + id: mainContent + width: scrollView.width - 2 * scrollView.padding + height: mainContent.childrenRect.height + anchors.horizontalCenter: parent.horizontalCenter + } + } + } + } + } +} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/Section.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/Section.qml index 5b2fc328455..d937055dff2 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/Section.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/Section.qml @@ -3,7 +3,7 @@ import QtQuick import QtQuick.Layouts -import StudioTheme 1.0 as StudioTheme +import StudioTheme as StudioTheme Item { id: control @@ -14,11 +14,13 @@ Item { property alias captionPixelSize: label.font.pixelSize property alias captionColor: header.color property alias captionTextColor: label.color - property int leftPadding: 8 - property int topPadding: 4 + property int leftPadding: 0 property int rightPadding: 0 + property int topPadding: control.style.sectionHeadSpacerHeight + property int bottomPadding: control.style.sectionHeadSpacerHeight property int animationDuration: 120 property bool expanded: true + property int collapsedBottomPadding: StudioTheme.Values.border clip: true @@ -28,16 +30,7 @@ Item { anchors.right: parent.right height: control.style.sectionHeadHeight color: control.style.section.head - - SectionLabel { - id: label - style: control.style - anchors.verticalCenter: parent.verticalCenter - color: control.style.text.idle - x: 22 - font.pixelSize: control.style.baseFontSize - font.capitalization: Font.AllUppercase - } + radius: StudioTheme.Values.smallRadius SectionLabel { id: arrow @@ -52,6 +45,7 @@ Item { anchors.verticalCenter: parent.verticalCenter font.pixelSize: control.style.smallIconFontSize font.family: StudioTheme.Constants.iconFont.family + Behavior on rotation { NumberAnimation { easing.type: Easing.OutCubic @@ -60,6 +54,17 @@ Item { } } + SectionLabel { + id: label + style: control.style + anchors.verticalCenter: parent.verticalCenter + color: control.style.text.idle + x: 22 + width: header.width - label.x + font.pixelSize: control.style.baseFontSize + font.capitalization: Font.AllUppercase + } + MouseArea { anchors.fill: parent onClicked: { @@ -78,22 +83,22 @@ Item { Row { id: topRow - height: control.style.sectionHeadSpacerHeight + height: control.topPadding anchors.top: header.bottom } Column { id: column anchors.left: parent.left - anchors.leftMargin: leftPadding + anchors.leftMargin: control.leftPadding anchors.right: parent.right - anchors.rightMargin: rightPadding + anchors.rightMargin: control.rightPadding anchors.top: topRow.bottom } Row { id: bottomRow - height: control.style.sectionHeadSpacerHeight + height: control.bottomPadding anchors.top: column.bottom } @@ -118,7 +123,7 @@ Item { when: !control.expanded PropertyChanges { target: control - implicitHeight: header.height + implicitHeight: header.height + control.collapsedBottomPadding } PropertyChanges { target: arrow diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/ToolTipArea.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/ToolTipArea.qml new file mode 100644 index 00000000000..6a7aed8068c --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/ToolTipArea.qml @@ -0,0 +1,25 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import QtQuick +import QtQuick.Layouts + +MouseArea { + id: root + + ToolTipExt { id: toolTip } + + onExited: toolTip.hideText() + onCanceled: toolTip.hideText() + onClicked: root.forceActiveFocus() + + hoverEnabled: true + + property string text + + Timer { + interval: 1000 + running: root.containsMouse && root.text.length + onTriggered: toolTip.showText(root, Qt.point(root.mouseX, root.mouseY), root.text) + } +} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/TopLevelComboBox.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/TopLevelComboBox.qml index d2dc962c306..cc92a1cd923 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/TopLevelComboBox.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/TopLevelComboBox.qml @@ -3,8 +3,9 @@ import QtQuick import QtQuick.Templates as T -import StudioTheme 1.0 as StudioTheme -import QtQuickDesignerTheme 1.0 +import StudioTheme as StudioTheme +import QtQuickDesignerTheme +import QtQuickDesignerWindowManager T.ComboBox { id: control @@ -112,7 +113,7 @@ T.ComboBox { visible: false flags: Qt.FramelessWindowHint | Qt.Dialog | Qt.NoDropShadowWindowHint | Qt.WindowStaysOnTopHint modality: Qt.NonModal - transientParent: null + transientParent: control.Window.window color: "transparent" onActiveFocusItemChanged: { @@ -157,6 +158,10 @@ T.ComboBox { id: itemDelegate onClicked: { + // Necessary to keep the transient parent open otherwise it will change the focus + // to the main window "Utils::AppMainWindowClassWindow" and closes the transient + // parent. + window.transientParent.requestActivate() comboBoxPopup.close() control.currentIndex = index control.activated(index) diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/impl/ColorEditorPopup.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/impl/ColorEditorPopup.qml new file mode 100644 index 00000000000..ba37645239f --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/impl/ColorEditorPopup.qml @@ -0,0 +1,560 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import QtQuick.Templates as T +import QtQuickDesignerTheme +import StudioControls as StudioControls +import StudioTheme as StudioTheme +import QtQuickDesignerColorPalette + +Column { + id: root + + property color color + property color originalColor + + readonly property real twoColumnWidth: (colorColumn.width - StudioTheme.Values.controlGap) * 0.5 + readonly property real fourColumnWidth: (colorColumn.width - (3 * StudioTheme.Values.controlGap)) * 0.25 + + property Item titleBarContent: Row { + anchors.fill: parent + spacing: 10 + + StudioControls.IconIndicator { + id: transparentIndicator + anchors.verticalCenter: parent.verticalCenter + icon: StudioTheme.Constants.transparent + pixelSize: StudioTheme.Values.myIconFontSize * 1.4 + toolTip: qsTr("Transparent") + + onClicked: { + colorPicker.alpha = 0 + colorPicker.invalidateColor() + colorPicker.updateColor() + } + } + + StudioControls.IconIndicator { + id: eyeDropperIndicator + anchors.verticalCenter: parent.verticalCenter + icon: StudioTheme.Constants.eyeDropper + pixelSize: StudioTheme.Values.myIconFontSize * 1.4 + toolTip: qsTr("Eye Dropper") + onClicked: ColorPaletteBackend.eyeDropper() + } + } + + onColorChanged: { + colorPicker.color = root.color + hexTextField.text = root.color + } + + signal activateColor(var color) + + StudioControls.Menu { + id: contextMenu + + StudioControls.MenuItem { + text: qsTr("Open Color Dialog") + onTriggered: colorPalette.showColorDialog(colorEditor.color) + } + } + + Column { + id: colorColumn + bottomPadding: StudioTheme.Values.popupMargin + width: root.width + spacing: StudioTheme.Values.colorEditorPopupSpacing + + ColorPicker { + id: colorPicker + + width: parent.width + sliderMargins: 4 + + onUpdateColor: { + root.activateColor(colorPicker.color) + + if (contextMenu.opened) + contextMenu.close() + } + onRightMouseButtonClicked: contextMenu.popup(colorPicker) + + onColorInvalidated: { + hslHueSpinBox.realValue = colorPicker.hue + hslSaturationSpinBox.realValue = colorPicker.saturationHSL + hslLightnessSpinBox.realValue = colorPicker.lightness + hslAlphaSpinBox.realValue = colorPicker.alpha + + redSpinBox.realValue = (colorPicker.red * 255) + greenSpinBox.realValue = (colorPicker.green * 255) + blueSpinBox.realValue = (colorPicker.blue * 255) + rgbAlphaSpinBox.realValue = (colorPicker.alpha * 255) + + hsvHueSpinBox.realValue = colorPicker.hue + hsvSaturationSpinBox.realValue = colorPicker.saturationHSV + hsvValueSpinBox.realValue = colorPicker.value + hsvAlphaSpinBox.realValue = colorPicker.alpha + } + } + + Column { + id: colorCompare + width: parent.width + + RowLayout { + width: parent.width + Layout.alignment: Qt.AlignTop + spacing: StudioTheme.Values.controlGap + + Label { + text: qsTr("Original") + width: root.twoColumnWidth + color: StudioTheme.Values.themeTextColor + elide: Text.ElideRight + font.pixelSize: StudioTheme.Values.myFontSize + Layout.preferredWidth: width + Layout.minimumWidth: width + Layout.maximumWidth: width + } + + Label { + text: qsTr("New") + width: root.twoColumnWidth + color: StudioTheme.Values.themeTextColor + elide: Text.ElideRight + font.pixelSize: StudioTheme.Values.myFontSize + Layout.preferredWidth: width + Layout.minimumWidth: width + Layout.maximumWidth: width + } + } + + RowLayout { + width: parent.width + Layout.alignment: Qt.AlignTop + spacing: StudioTheme.Values.controlGap + + Rectangle { + id: originalColorRectangle + color: root.originalColor + width: root.twoColumnWidth + height: StudioTheme.Values.height + border.width: StudioTheme.Values.border + border.color: StudioTheme.Values.themeControlOutline + + Image { + anchors.fill: parent + source: "qrc:/navigator/icon/checkers.png" + fillMode: Image.Tile + z: -1 + } + + StudioControls.ToolTipArea { + anchors.fill: parent + text: originalColorRectangle.color + acceptedButtons: Qt.LeftButton | Qt.RightButton + onClicked: function(mouse) { + if (mouse.button === Qt.LeftButton) + root.activateColor(root.originalColor) + + if (mouse.button === Qt.RightButton) { + contextMenuFavorite.currentColor = root.originalColor + contextMenuFavorite.popup() + } + } + } + } + + Rectangle { + id: newColorRectangle + color: root.color + width: root.twoColumnWidth + height: StudioTheme.Values.height + border.width: StudioTheme.Values.border + border.color: StudioTheme.Values.themeControlOutline + + Image { + anchors.fill: parent + source: "qrc:/navigator/icon/checkers.png" + fillMode: Image.Tile + z: -1 + } + + StudioControls.ToolTipArea { + anchors.fill: parent + text: newColorRectangle.color + acceptedButtons: Qt.RightButton + onClicked: function(mouse) { + if (mouse.button === Qt.RightButton) { + contextMenuFavorite.currentColor = colorEditor.color + contextMenuFavorite.popup() + } + } + } + } + } + + StudioControls.Menu { + id: contextMenuFavorite + + property color currentColor + + StudioControls.MenuItem { + text: qsTr("Add to Favorites") + onTriggered: ColorPaletteBackend.addFavoriteColor(contextMenuFavorite.currentColor) + } + } + } + } + + Column { + id: sectionColumn + width: root.width + + StudioControls.Section { + caption: qsTr("Color Details") + anchors.left: parent.left + anchors.right: parent.right + + Column { + spacing: StudioTheme.Values.colorEditorPopupSpacing + + Row { + spacing: StudioTheme.Values.controlGap + + StudioControls.ComboBox { + id: colorMode + implicitWidth: root.twoColumnWidth + width: colorMode.implicitWidth + actionIndicatorVisible: false + textRole: "text" + valueRole: "value" + model: [ + { value: ColorPicker.Mode.HSVA, text: "HSVA" }, + { value: ColorPicker.Mode.RGBA, text: "RGBA" }, + { value: ColorPicker.Mode.HSLA, text: "HSLA" } + ] + + onActivated: colorPicker.mode = colorMode.currentValue + } + + StudioControls.TextField { + id: hexTextField + implicitWidth: root.twoColumnWidth + width: hexTextField.implicitWidth + actionIndicatorVisible: false + translationIndicatorVisible: false + indicatorVisible: true + indicator.icon.text: StudioTheme.Constants.copy_small + indicator.onClicked: { + hexTextField.selectAll() + hexTextField.copy() + hexTextField.deselect() + } + + validator: RegularExpressionValidator { + regularExpression: /#[0-9A-Fa-f]{6}([0-9A-Fa-f]{2})?/g + } + + onEditingFinished: root.activateColor(colorFromString(hexTextField.text)) + } + } + + Row { + id: rgbaRow + visible: colorPicker.mode === ColorPicker.Mode.RGBA + spacing: StudioTheme.Values.controlGap + + StudioControls.RealSpinBox { + id: redSpinBox + width: root.fourColumnWidth + realStepSize: 1 + realFrom: 0 + realTo: 255 + decimals: 0 + actionIndicatorVisible: false + + onRealValueModified: { + var tmp = redSpinBox.realValue / 255.0 + if (colorPicker.red !== tmp && !colorPicker.block) { + colorPicker.red = tmp + colorPicker.invalidateColor() + colorPicker.updateColor() + } + } + //onDragStarted: colorEditorTimer.stop() + //onIndicatorPressed: colorEditorTimer.stop() + } + + StudioControls.RealSpinBox { + id: greenSpinBox + width: root.fourColumnWidth + realStepSize: 1 + realFrom: 0 + realTo: 255 + decimals: 0 + actionIndicatorVisible: false + + onRealValueModified: { + var tmp = greenSpinBox.realValue / 255.0 + if (colorPicker.green !== tmp && !colorPicker.block) { + colorPicker.green = tmp + colorPicker.invalidateColor() + colorPicker.updateColor() + } + } + //onDragStarted: colorEditorTimer.stop() + //onIndicatorPressed: colorEditorTimer.stop() + } + + StudioControls.RealSpinBox { + id: blueSpinBox + width: root.fourColumnWidth + realStepSize: 1 + realFrom: 0 + realTo: 255 + decimals: 0 + actionIndicatorVisible: false + + onRealValueModified: { + var tmp = blueSpinBox.realValue / 255.0 + if (colorPicker.blue !== tmp && !colorPicker.block) { + colorPicker.blue = tmp + colorPicker.invalidateColor() + colorPicker.updateColor() + } + } + //onDragStarted: colorEditorTimer.stop() + //onIndicatorPressed: colorEditorTimer.stop() + } + + StudioControls.RealSpinBox { + id: rgbAlphaSpinBox + width: root.fourColumnWidth + realStepSize: 1 + realFrom: 0 + realTo: 255 + decimals: 0 + actionIndicatorVisible: false + + onRealValueModified: { + var tmp = rgbAlphaSpinBox.realValue / 255.0 + if (colorPicker.alpha !== tmp && !colorPicker.block) { + colorPicker.alpha = tmp + colorPicker.invalidateColor() + colorPicker.updateColor() + } + } + //onDragStarted: colorEditorTimer.stop() + //onIndicatorPressed: colorEditorTimer.stop() + } + } + + Row { + id: hslaRow + visible: colorPicker.mode === ColorPicker.Mode.HSLA + spacing: StudioTheme.Values.controlGap + + StudioControls.RealSpinBox { + id: hslHueSpinBox + width: root.fourColumnWidth + realFrom: 0.0 + realTo: 1.0 + realStepSize: 0.1 + decimals: 2 + actionIndicatorVisible: false + + onRealValueModified: { + if (colorPicker.hue !== hslHueSpinBox.realValue + && !colorPicker.block) { + colorPicker.hue = hslHueSpinBox.realValue + colorPicker.invalidateColor() + colorPicker.updateColor() + } + } + //onDragStarted: colorEditorTimer.stop() + //onIndicatorPressed: colorEditorTimer.stop() + } + + StudioControls.RealSpinBox { + id: hslSaturationSpinBox + width: root.fourColumnWidth + realFrom: 0.0 + realTo: 1.0 + realStepSize: 0.1 + decimals: 2 + actionIndicatorVisible: false + + onRealValueModified: { + if (colorPicker.saturationHSL !== hslSaturationSpinBox.realValue + && !colorPicker.block) { + colorPicker.saturationHSL = hslSaturationSpinBox.realValue + colorPicker.invalidateColor() + colorPicker.updateColor() + } + } + //onDragStarted: colorEditorTimer.stop() + //onIndicatorPressed: colorEditorTimer.stop() + } + + StudioControls.RealSpinBox { + id: hslLightnessSpinBox + width: root.fourColumnWidth + realFrom: 0.0 + realTo: 1.0 + realStepSize: 0.1 + decimals: 2 + actionIndicatorVisible: false + + onValueModified: { + if (colorPicker.lightness !== hslLightnessSpinBox.realValue + && !colorPicker.block) { + colorPicker.lightness = hslLightnessSpinBox.realValue + colorPicker.invalidateColor() + colorPicker.updateColor() + } + } + //onDragStarted: colorEditorTimer.stop() + //onIndicatorPressed: colorEditorTimer.stop() + } + + StudioControls.RealSpinBox { + id: hslAlphaSpinBox + width: root.fourColumnWidth + realFrom: 0.0 + realTo: 1.0 + realStepSize: 0.1 + decimals: 2 + actionIndicatorVisible: false + + onRealValueModified: { + if (colorPicker.alpha !== hslAlphaSpinBox.realValue + && !colorPicker.block) { + colorPicker.alpha = hslAlphaSpinBox.realValue + colorPicker.invalidateColor() + colorPicker.updateColor() + } + } + //onDragStarted: colorEditorTimer.stop() + //onIndicatorPressed: colorEditorTimer.stop() + } + } + + Row { + id: hsvaRow + visible: colorPicker.mode === ColorPicker.Mode.HSVA + spacing: StudioTheme.Values.controlGap + + StudioControls.RealSpinBox { + id: hsvHueSpinBox + width: root.fourColumnWidth + realFrom: 0.0 + realTo: 1.0 + realStepSize: 0.1 + decimals: 2 + actionIndicatorVisible: false + + onRealValueModified: { + if (colorPicker.hue !== hsvHueSpinBox.realValue + && !colorPicker.block) { + colorPicker.hue = hsvHueSpinBox.realValue + colorPicker.invalidateColor() + colorPicker.updateColor() + } + } + //onDragStarted: colorEditorTimer.stop() + //onIndicatorPressed: colorEditorTimer.stop() + } + + StudioControls.RealSpinBox { + id: hsvSaturationSpinBox + width: root.fourColumnWidth + realFrom: 0.0 + realTo: 1.0 + realStepSize: 0.1 + decimals: 2 + actionIndicatorVisible: false + + onRealValueModified: { + if (colorPicker.saturationHSV !== hsvSaturationSpinBox.realValue + && !colorPicker.block) { + colorPicker.saturationHSV = hsvSaturationSpinBox.realValue + colorPicker.invalidateColor() + colorPicker.updateColor() + } + } + //onDragStarted: colorEditorTimer.stop() + //onIndicatorPressed: colorEditorTimer.stop() + } + + StudioControls.RealSpinBox { + id: hsvValueSpinBox + width: root.fourColumnWidth + realFrom: 0.0 + realTo: 1.0 + realStepSize: 0.1 + decimals: 2 + actionIndicatorVisible: false + + onRealValueModified: { + if (colorPicker.value !== hsvValueSpinBox.realValue + && !colorPicker.block) { + colorPicker.value = hsvValueSpinBox.realValue + colorPicker.invalidateColor() + colorPicker.updateColor() + } + } + //onDragStarted: colorEditorTimer.stop() + //onIndicatorPressed: colorEditorTimer.stop() + } + + StudioControls.RealSpinBox { + id: hsvAlphaSpinBox + width: root.fourColumnWidth + realFrom: 0.0 + realTo: 1.0 + realStepSize: 0.1 + decimals: 2 + actionIndicatorVisible: false + + onRealValueModified: { + if (colorPicker.alpha !== hsvAlphaSpinBox.realValue + && !colorPicker.block) { + colorPicker.alpha = hsvAlphaSpinBox.realValue + colorPicker.invalidateColor() + colorPicker.updateColor() + } + } + //onDragStarted: colorEditorTimer.stop() + //onIndicatorPressed: colorEditorTimer.stop() + } + } + } + } + + StudioControls.Section { + caption: qsTr("Palette") + anchors.left: parent.left + anchors.right: parent.right + bottomPadding: 0 + collapsedBottomPadding: 0 + + StudioControls.ColorPalette { + id: colorPalette + + width: root.width + + twoColumnWidth: root.twoColumnWidth + fourColumnWidth: root.fourColumnWidth + + enableSingletonConnection: root.visible + onSelectedColorChanged: root.activateColor(colorPalette.selectedColor) + onDialogColorChanged: root.activateColor(colorPalette.selectedColor) + } + } + } +} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ColorPalette.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/impl/ColorPalette.qml similarity index 61% rename from share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ColorPalette.qml rename to share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/impl/ColorPalette.qml index c79a399691f..976573ba5be 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ColorPalette.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/impl/ColorPalette.qml @@ -1,12 +1,12 @@ -// Copyright (C) 2021 The Qt Company Ltd. +// Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 -import QtQuick 2.15 -import QtQuick.Layouts 1.15 -import HelperWidgets 2.0 -import StudioControls 1.0 as StudioControls -import StudioTheme 1.0 as StudioTheme -import QtQuickDesignerColorPalette 1.0 +import QtQuick +import QtQuick.Layouts +import HelperWidgets +import StudioControls as StudioControls +import StudioTheme as StudioTheme +import QtQuickDesignerColorPalette Column { id: root @@ -16,7 +16,10 @@ Column { property alias enableSingletonConnection: singletonConnection.enabled - spacing: 10 + property real twoColumnWidth: 50 + property real fourColumnWidth: 25 + + spacing: StudioTheme.Values.colorEditorPopupSpacing function addColorToPalette(colorStr) { ColorPaletteBackend.addRecentColor(colorStr) @@ -35,7 +38,7 @@ Column { Rectangle { id: colorRectangle - width: StudioTheme.Values.colorEditorPopupSpinBoxWidth + width: root.fourColumnWidth height: StudioTheme.Values.defaultControlHeight color: (modelData !== "") ? modelData : "transparent" border.color: (modelData !== "") ? StudioTheme.Values.themeControlOutline @@ -45,7 +48,7 @@ Column { Image { visible: modelData !== "" anchors.fill: parent - source: "images/checkers.png" + source: "qrc:/navigator/icon/checkers.png" fillMode: Image.Tile z: -1 } @@ -90,48 +93,36 @@ Column { function onCurrentColorChanged(color) { root.selectedColor = color - dialogColorChanged() + root.dialogColorChanged() } function onColorDialogRejected() { root.selectedColor = root.oldColor - dialogColorChanged() + root.dialogColorChanged() } } - RowLayout { - Layout.fillWidth: true - spacing: 0 + StudioControls.ComboBox { + id: colorMode + implicitWidth: root.width + width: colorMode.implicitWidth + actionIndicatorVisible: false + model: ColorPaletteBackend.palettes + currentIndex: colorMode.find(ColorPaletteBackend.currentPalette) - StudioControls.ComboBox { - id: colorMode + onActivated: ColorPaletteBackend.currentPalette = colorMode.currentText - implicitWidth: 3 * StudioTheme.Values.controlGap - + 4 * StudioTheme.Values.colorEditorPopupSpinBoxWidth - width: implicitWidth - actionIndicatorVisible: false - model: ColorPaletteBackend.palettes - currentIndex: colorMode.find(ColorPaletteBackend.currentPalette) - - onActivated: ColorPaletteBackend.currentPalette = colorMode.currentText - - Component.onCompleted: colorMode.currentIndex = colorMode.find(ColorPaletteBackend.currentPalette) - } + Component.onCompleted: colorMode.currentIndex = colorMode.find(ColorPaletteBackend.currentPalette) } - GridView { + Grid { id: colorPaletteView - model: ColorPaletteBackend.currentPaletteColors - delegate: colorItemDelegate - cellWidth: StudioTheme.Values.colorEditorPopupSpinBoxWidth - + StudioTheme.Values.controlGap - cellHeight: StudioTheme.Values.defaultControlHeight - + StudioTheme.Values.controlGap - width: 4 * (StudioTheme.Values.colorEditorPopupSpinBoxWidth - + StudioTheme.Values.controlGap) - height: 2 * (StudioTheme.Values.defaultControlHeight - + StudioTheme.Values.controlGap) - clip: true - interactive: false + columns: 4 + spacing: StudioTheme.Values.controlGap + + Repeater { + model: ColorPaletteBackend.currentPaletteColors + delegate: colorItemDelegate + } } } diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ColorPicker.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/impl/ColorPicker.qml similarity index 99% rename from share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ColorPicker.qml rename to share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/impl/ColorPicker.qml index 4e0101a2e4f..cd525114268 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ColorPicker.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/impl/ColorPicker.qml @@ -192,7 +192,7 @@ Column { width: parent.width - 2 * StudioTheme.Values.border height: width - source: "images/checkers.png" + source: "qrc:/navigator/icon/checkers.png" fillMode: Image.Tile // Note: We smoothscale the shader from a smaller version to improve performance @@ -234,7 +234,7 @@ Column { id: pickerCross property color strokeStyle: "lightGray" - property string loadImageUrl: "images/checkers.png" + property string loadImageUrl: "qrc:/navigator/icon/checkers.png" property int radius: 10 Component.onCompleted: pickerCross.loadImage(pickerCross.loadImageUrl) diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/HueSlider.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/impl/HueSlider.qml similarity index 100% rename from share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/HueSlider.qml rename to share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/impl/HueSlider.qml diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/LuminanceSlider.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/impl/LuminanceSlider.qml similarity index 100% rename from share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/LuminanceSlider.qml rename to share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/impl/LuminanceSlider.qml diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/OpacitySlider.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/impl/OpacitySlider.qml similarity index 96% rename from share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/OpacitySlider.qml rename to share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/impl/OpacitySlider.qml index b3b8ff2daab..aaed4ef98ce 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/OpacitySlider.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/impl/OpacitySlider.qml @@ -35,7 +35,7 @@ Item { Image { anchors.fill: parent - source: "images/checkers.png" + source: "qrc:/navigator/icon/checkers.png" fillMode: Image.Tile } @@ -66,7 +66,7 @@ Item { Image { anchors.fill: handleInside - source: "images/checkers.png" + source: "qrc:/navigator/icon/checkers.png" fillMode: Image.Tile } diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/qmldir b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/qmldir index 5ce433812c2..5376aeb0df2 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/qmldir +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/qmldir @@ -5,6 +5,10 @@ ButtonGroup 1.0 ButtonGroup.qml ButtonRow 1.0 ButtonRow.qml CheckBox 1.0 CheckBox.qml CheckIndicator 1.0 CheckIndicator.qml +ColorEditor 1.0 ColorEditor.qml +ColorEditorPopup 1.0 impl/ColorEditorPopup.qml +ColorPalette 1.0 impl/ColorPalette.qml +ColorPicker 1.0 impl/ColorPicker.qml ComboBox 1.0 ComboBox.qml ComboBoxInput 1.0 ComboBoxInput.qml ContextMenu 1.0 ContextMenu.qml @@ -12,16 +16,21 @@ Dialog 1.0 Dialog.qml DialogButton 1.0 DialogButton.qml DialogButtonBox 1.0 DialogButtonBox.qml FilterComboBox 1.0 FilterComboBox.qml +HueSlider 1.0 impl/HueSlider.qml +IconIndicator 1.0 IconIndicator.qml Indicator 1.0 Indicator.qml InfinityLoopIndicator 1.0 InfinityLoopIndicator.qml ItemDelegate 1.0 ItemDelegate.qml LinkIndicator2D 1.0 LinkIndicator2D.qml LinkIndicator3D 1.0 LinkIndicator3D.qml LinkIndicator3DComponent 1.0 LinkIndicator3DComponent.qml +LuminanceSlider 1.0 impl/LuminanceSlider.qml Menu 1.0 Menu.qml MenuItem 1.0 MenuItem.qml MenuItemWithIcon 1.0 MenuItemWithIcon.qml MenuSeparator 1.0 MenuSeparator.qml +OpacitySlider 1.0 impl/OpacitySlider.qml +PopupDialog 1.0 PopupDialog.qml ProgressBar 1.0 ProgressBar.qml RadioButton 1.0 RadioButton.qml RealSliderPopup 1.0 RealSliderPopup.qml @@ -47,6 +56,7 @@ TabButton 1.0 TabButton.qml TextArea 1.0 TextArea.qml TextField 1.0 TextField.qml ToolTip 1.0 ToolTip.qml +ToolTipArea 1.0 ToolTipArea.qml TransientScrollBar 1.0 TransientScrollBar.qml TranslationIndicator 1.0 TranslationIndicator.qml TopLevelComboBox 1.0 TopLevelComboBox.qml diff --git a/src/plugins/qmldesigner/components/propertyeditor/colorpalettebackend.cpp b/src/plugins/qmldesigner/components/propertyeditor/colorpalettebackend.cpp index 8b48ca0ce7f..5f15f351110 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/colorpalettebackend.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/colorpalettebackend.cpp @@ -205,20 +205,17 @@ void ColorPaletteBackend::showDialog(QColor color) void ColorPaletteBackend::eyeDropper() { - QWidget *widget = QApplication::activeWindow(); - if (!widget) + QWindow *window = QGuiApplication::focusWindow(); + if (!window) return; if (!m_colorPickingEventFilter) m_colorPickingEventFilter = new QColorPickingEventFilter(this); - widget->installEventFilter(m_colorPickingEventFilter); + window->installEventFilter(m_colorPickingEventFilter); + window->setMouseGrabEnabled(true); + window->setKeyboardGrabEnabled(true); -#ifndef QT_NO_CURSOR - widget->grabMouse(/*Qt::CrossCursor*/); -#else - w->grabMouse(); -#endif #ifdef Q_OS_WIN32 // excludes WinRT // On Windows mouse tracking doesn't work over other processes's windows updateTimer->start(30); @@ -227,11 +224,6 @@ void ColorPaletteBackend::eyeDropper() // and loose focus. dummyTransparentWindow.show(); #endif - widget->grabKeyboard(); - /* With setMouseTracking(true) the desired color can be more precisely picked up, - * and continuously pushing the mouse button is not necessary. - */ - widget->setMouseTracking(true); updateEyeDropperPosition(QCursor::pos()); } @@ -281,8 +273,8 @@ void ColorPaletteBackend::updateEyeDropperPosition(const QPoint &globalPos) void ColorPaletteBackend::updateCursor(const QImage &image) { - QWidget *widget = QApplication::activeWindow(); - if (!widget) + QWindow *window = QGuiApplication::focusWindow(); + if (!window) return; QPixmap pixmap(QSize(g_cursorWidth, g_cursorHeight)); @@ -316,25 +308,23 @@ void ColorPaletteBackend::updateCursor(const QImage &image) painter.end(); QCursor cursor(pixmap); - widget->setCursor(cursor); + window->setCursor(cursor); } void ColorPaletteBackend::releaseEyeDropper() { - QWidget *widget = QApplication::activeWindow(); - if (!widget) + QWindow *window = QGuiApplication::focusWindow(); + if (!window) return; - widget->removeEventFilter(m_colorPickingEventFilter); - widget->releaseMouse(); + window->removeEventFilter(m_colorPickingEventFilter); + window->setMouseGrabEnabled(false); #ifdef Q_OS_WIN32 updateTimer->stop(); dummyTransparentWindow.setVisible(false); #endif - widget->releaseKeyboard(); - widget->setMouseTracking(false); - - widget->unsetCursor(); + window->setKeyboardGrabEnabled(false); + window->unsetCursor(); } bool ColorPaletteBackend::handleEyeDropperMouseMove(QMouseEvent *e) @@ -370,7 +360,4 @@ bool ColorPaletteBackend::handleEyeDropperKeyPress(QKeyEvent *e) return true; } -/// EYE DROPPER - - } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/propertyeditor/quick2propertyeditorview.cpp b/src/plugins/qmldesigner/components/propertyeditor/quick2propertyeditorview.cpp index 15f69afc3fd..59fbbffc9ca 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/quick2propertyeditorview.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/quick2propertyeditorview.cpp @@ -70,13 +70,6 @@ void Quick2PropertyEditorView::registerQmlTypes() QUrl regExpUrl = QUrl::fromLocalFile(resourcePath + "/RegExpValidator.qml"); qmlRegisterType(regExpUrl, "HelperWidgets", 2, 0, "RegExpValidator"); - - const QString qtPrefix = "/Qt6"; - qmlRegisterType(QUrl::fromLocalFile(resourcePath + qtPrefix + "HelperWindow.qml"), - "HelperWidgets", - 2, - 0, - "HelperWindow"); } } diff --git a/src/plugins/qmldesigner/components/propertyeditor/tooltip.cpp b/src/plugins/qmldesigner/components/propertyeditor/tooltip.cpp index 824e104cdff..dd50e1ff171 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/tooltip.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/tooltip.cpp @@ -34,5 +34,6 @@ void Tooltip::hideText() void Tooltip::registerDeclarativeType() { + qmlRegisterType("StudioControls", 1, 0, "ToolTipExt"); qmlRegisterType("HelperWidgets", 2, 0, "Tooltip"); } diff --git a/src/plugins/qmldesigner/qmldesignerplugin.cpp b/src/plugins/qmldesigner/qmldesignerplugin.cpp index c5d0048902f..6e1aa10584f 100644 --- a/src/plugins/qmldesigner/qmldesignerplugin.cpp +++ b/src/plugins/qmldesigner/qmldesignerplugin.cpp @@ -26,6 +26,8 @@ #include #include #include +#include +#include #ifndef QDS_USE_PROJECTSTORAGE # include #endif @@ -72,12 +74,12 @@ #include #include #include +#include #include #include #include #include #include -#include #include "nanotrace/nanotrace.h" #include @@ -283,6 +285,8 @@ bool QmlDesignerPlugin::initialize(const QStringList & /*arguments*/, QString *e //TODO Move registering those types out of the property editor, since they are used also in the states editor Quick2PropertyEditorView::registerQmlTypes(); CollectionView::registerDeclarativeType(); + StudioQuickWidget::registerDeclarativeType(); + QmlDesignerBase::WindowManager::registerDeclarativeType(); if (checkEnterpriseLicense()) Core::IWizardFactory::registerFeatureProvider(new EnterpriseFeatureProvider); diff --git a/src/plugins/qmldesignerbase/CMakeLists.txt b/src/plugins/qmldesignerbase/CMakeLists.txt index 5f5f1ef258b..40fe8bbb134 100644 --- a/src/plugins/qmldesignerbase/CMakeLists.txt +++ b/src/plugins/qmldesignerbase/CMakeLists.txt @@ -29,6 +29,7 @@ extend_qtc_plugin(QmlDesignerBase designerpaths.cpp designerpaths.h designersettings.cpp designersettings.h qmlpuppetpaths.cpp qmlpuppetpaths.h + windowmanager.cpp windowmanager.h ) extend_qtc_plugin(QmlDesignerBase diff --git a/src/plugins/qmldesignerbase/studio/studioquickwidget.cpp b/src/plugins/qmldesignerbase/studio/studioquickwidget.cpp index 5da9aaec6e2..15baebecfaf 100644 --- a/src/plugins/qmldesignerbase/studio/studioquickwidget.cpp +++ b/src/plugins/qmldesignerbase/studio/studioquickwidget.cpp @@ -52,12 +52,17 @@ void StudioQuickWidget::setSource(const QUrl &url) { m_quickWidget->setSource(url); - if (rootObject()) { - const auto windows = rootObject()->findChildren(); + if (!rootObject()) + return; - for (auto window : windows) { + if (!Core::ICore::dialogParent()->windowHandle()) + return; + + const auto windows = rootObject()->findChildren(); + + for (auto window : windows) { + if (!window->transientParent()) window->setTransientParent(Core::ICore::dialogParent()->windowHandle()); - } } } @@ -86,6 +91,11 @@ QQuickWidget *StudioQuickWidget::quickWidget() const return m_quickWidget; } +void StudioQuickWidget::registerDeclarativeType() +{ + qmlRegisterType("StudioHelpers", 1, 0, "ColorBackend"); +} + StudioPropertyMap::StudioPropertyMap(QObject *parent) : QQmlPropertyMap(parent) {} diff --git a/src/plugins/qmldesignerbase/studio/studioquickwidget.h b/src/plugins/qmldesignerbase/studio/studioquickwidget.h index 165db562e21..5c4bad8dae3 100644 --- a/src/plugins/qmldesignerbase/studio/studioquickwidget.h +++ b/src/plugins/qmldesignerbase/studio/studioquickwidget.h @@ -9,6 +9,45 @@ #include #include +class QMLDESIGNERBASE_EXPORT StudioQmlColorBackend : public QObject +{ + Q_OBJECT + + Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged) + +public: + explicit StudioQmlColorBackend(QObject *parent = nullptr) + : QObject(parent) + {} + + void setColor(const QColor &value) + { + if (m_color == value) + return; + + m_color = value; + emit colorChanged(); + } + + QColor color() const { return m_color; } + + Q_INVOKABLE void activateColor(const QColor &value) + { + if (m_color == value) + return; + + setColor(value); + emit activated(value); + } + +signals: + void colorChanged(); + void activated(const QColor &value); + +private: + QColor m_color = Qt::red; +}; + class QMLDESIGNERBASE_EXPORT StudioQmlTextBackend : public QObject { Q_OBJECT @@ -169,6 +208,8 @@ public: StudioPropertyMap *registerPropertyMap(const QByteArray &name); QQuickWidget *quickWidget() const; + static void registerDeclarativeType(); + signals: void adsFocusChanged(); diff --git a/src/plugins/qmldesignerbase/utils/windowmanager.cpp b/src/plugins/qmldesignerbase/utils/windowmanager.cpp new file mode 100644 index 00000000000..0f79b3b75e9 --- /dev/null +++ b/src/plugins/qmldesignerbase/utils/windowmanager.cpp @@ -0,0 +1,36 @@ +// 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 "windowmanager.h" + +#include + +#include +#include +#include + +namespace QmlDesignerBase { + +QPointer WindowManager::m_instance = nullptr; + +WindowManager::WindowManager() +{ + connect(qGuiApp, &QGuiApplication::focusWindowChanged, this, &WindowManager::focusWindowChanged); + connect(qGuiApp, &QGuiApplication::aboutToQuit, this, &WindowManager::aboutToQuit); + connect(Core::ICore::instance()->mainWindow()->windowHandle(), + &QWindow::visibleChanged, + this, + &WindowManager::mainWindowVisibleChanged); +} + +void WindowManager::registerDeclarativeType() +{ + [[maybe_unused]] static const int typeIndex = qmlRegisterSingletonType( + "QtQuickDesignerWindowManager", 1, 0, "WindowManager", [](QQmlEngine *, QJSEngine *) { + return new WindowManager(); + }); +} + +WindowManager::~WindowManager() {} + +} // namespace QmlDesignerBase diff --git a/src/plugins/qmldesignerbase/utils/windowmanager.h b/src/plugins/qmldesignerbase/utils/windowmanager.h new file mode 100644 index 00000000000..094d548ab3c --- /dev/null +++ b/src/plugins/qmldesignerbase/utils/windowmanager.h @@ -0,0 +1,37 @@ +// 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 "../qmldesignerbase_global.h" + +#include + +QT_FORWARD_DECLARE_CLASS(QWindow) + +namespace QmlDesignerBase { + +class QMLDESIGNERBASE_EXPORT WindowManager : public QObject +{ + Q_OBJECT + +public: + ~WindowManager(); + + WindowManager(const WindowManager &) = delete; + void operator=(const WindowManager &) = delete; + + static void registerDeclarativeType(); + +signals: + void focusWindowChanged(QWindow *window); + void aboutToQuit(); + void mainWindowVisibleChanged(bool value); + +private: + WindowManager(); + + static QPointer m_instance; +}; + +} // namespace QmlDesignerBase From 0b50d22bbdbbfce2997083fb04d9f235e8072612 Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Wed, 8 Nov 2023 16:56:14 +0200 Subject: [PATCH 194/242] QmlDesigner: Rename some expressions Task-number: QDS-11168 Change-Id: Id402267708eeed794b78766e9b5504fbb636d357 Reviewed-by: Mats Honkamaa Reviewed-by: Mahmoud Badri Reviewed-by: Qt CI Patch Build Bot --- .../CollectionDetailsToolbar.qml | 4 +-- .../CollectionDetailsView.qml | 2 +- .../CollectionItem.qml | 15 +++++----- .../CollectionView.qml | 26 ++++++++--------- .../collectionEditorQmlSource/CsvImport.qml | 4 +-- .../collectionEditorQmlSource/JsonImport.qml | 4 +-- .../ModelSourceItem.qml | 2 +- .../NewCollectionDialog.qml | 29 +++++++++---------- .../collectionsourcemodel.cpp | 22 +++++++------- .../collectioneditor/collectionview.cpp | 14 ++++----- .../collectioneditor/collectionwidget.cpp | 10 +++---- 11 files changed, 64 insertions(+), 68 deletions(-) diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsToolbar.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsToolbar.qml index b05725f85ab..9dba1b86015 100644 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsToolbar.qml +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsToolbar.qml @@ -116,7 +116,7 @@ Item { IconButton { icon: StudioTheme.Constants.export_medium - tooltip: qsTr("Export collection to a new file") + tooltip: qsTr("Export the model to a new file") enabled: root.model.collectionName !== "" onClicked: exportMenu.popup() } @@ -228,7 +228,7 @@ Item { } Text { - text: qsTr("The collection already contains \"%1\"!").arg(columnName.text) + text: qsTr("The model already contains \"%1\"!").arg(columnName.text) visible: columnName.text !== "" && !addColumnDialog.nameIsValid color: StudioTheme.Values.themeRedLight Layout.columnSpan: 2 diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsView.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsView.qml index 26ba2fe87a2..f7b0bf5166a 100644 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsView.qml +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsView.qml @@ -307,7 +307,7 @@ Rectangle { Text { anchors.fill: parent - text: qsTr("Select a collection to continue") + text: qsTr("Select a model to continue") visible: !topRow.visible textFormat: Text.RichText color: StudioTheme.Values.themeTextColor diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionItem.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionItem.qml index ac254fe7f8b..1cf7a2a0308 100644 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionItem.qml +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionItem.qml @@ -130,7 +130,7 @@ Item { StudioControls.Dialog { id: deleteDialog - title: qsTr("Deleting whole collection") + title: qsTr("Deleting the model") clip: true contentItem: ColumnLayout { @@ -144,13 +144,12 @@ Item { color: StudioTheme.Values.themeTextColor text: { if (root.sourceType === "json") { - qsTr("Are you sure that you want to delete collection \"%1\"?" - + "\nYou can't undo this action if you proceed and " - + "the collection will be removed from the json file.").arg(collectionName) + qsTr("Are you sure that you want to delete model \"%1\"?" + + "\nThe model will be deleted permanently.").arg(collectionName) } else if (root.sourceType === "csv") { - qsTr("Are you sure that you want to delete collection \"%1\"?" - + "\nIn this case, the csv file will not be deleted, but " - + "the collection model will be removed from the project.").arg(collectionName) + qsTr("Are you sure that you want to delete model \"%1\"?" + + "\nThe model will be removed from the project " + + "but the file will not be deleted.").arg(collectionName) } } } @@ -180,7 +179,7 @@ Item { StudioControls.Dialog { id: renameDialog - title: qsTr("Rename collection") + title: qsTr("Rename model") onAccepted: { if (newNameField.text !== "") diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionView.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionView.qml index 21a9bd2c3ee..fdfc7be3042 100644 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionView.qml +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionView.qml @@ -76,7 +76,7 @@ Item { id: collectionText anchors.verticalCenter: parent.verticalCenter - text: qsTr("Collections") + text: qsTr("Data Models") font.pixelSize: StudioTheme.Values.mediumIconFont color: StudioTheme.Values.themeTextColor leftPadding: 15 @@ -90,7 +90,7 @@ Item { HelperWidgets.IconButton { icon: StudioTheme.Constants.downloadjson_large - tooltip: qsTr("Import Json") + tooltip: qsTr("Import JSON") onClicked: jsonImporter.open() } @@ -104,7 +104,7 @@ Item { } } - Rectangle { // Collections + Rectangle { // Model Groups Layout.fillWidth: true color: StudioTheme.Values.themeBackgroundColorNormal Layout.minimumHeight: 150 @@ -134,19 +134,17 @@ Item { } } - Rectangle { + HelperWidgets.IconButton { + id: addCollectionButton + + iconSize:16 Layout.fillWidth: true - Layout.preferredHeight: addCollectionButton.height - color: StudioTheme.Values.themeBackgroundColorNormal + Layout.minimumWidth: 24 + Layout.alignment: Qt.AlignTop | Qt.AlignHCenter - IconTextButton { - id: addCollectionButton - - anchors.centerIn: parent - text: qsTr("Add new collection") - icon: StudioTheme.Constants.create_medium - onClicked: newCollection.open() - } + tooltip: qsTr("Add a new model") + icon: StudioTheme.Constants.create_medium + onClicked: newCollection.open() } } diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CsvImport.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CsvImport.qml index eb46587316e..7f1f9dde2b0 100644 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CsvImport.qml +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CsvImport.qml @@ -91,7 +91,7 @@ StudioControls.Dialog { Row { spacing: 10 Text { - text: qsTr("Collection name: ") + text: qsTr("The model name: ") anchors.verticalCenter: parent.verticalCenter color: StudioTheme.Values.themeTextColor } @@ -145,7 +145,7 @@ StudioControls.Dialog { PropertyChanges { target: fieldErrorText - text: qsTr("Collection name can not be empty") + text: qsTr("The model name can not be empty") visible: true } } diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/JsonImport.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/JsonImport.qml index 51986894b8a..38298a4d6a0 100644 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/JsonImport.qml +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/JsonImport.qml @@ -12,7 +12,7 @@ import StudioTheme as StudioTheme StudioControls.Dialog { id: root - title: qsTr("Import Collections") + title: qsTr("Import Models") anchors.centerIn: parent closePolicy: Popup.CloseOnEscape modal: true @@ -21,7 +21,7 @@ StudioControls.Dialog { property bool fileExists: false onOpened: { - fileName.text = qsTr("New Json File") + fileName.text = qsTr("New JSON File") fileName.selectAll() fileName.forceActiveFocus() } diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/ModelSourceItem.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/ModelSourceItem.qml index 82e0dd5cb37..be3d62c2e7c 100644 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/ModelSourceItem.qml +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/ModelSourceItem.qml @@ -149,7 +149,7 @@ Item { Layout.fillWidth: true Layout.preferredHeight: root.expanded ? contentHeight : 0 Layout.leftMargin: 6 - model: collections + model: internalModels clip: true Behavior on Layout.preferredHeight { diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/NewCollectionDialog.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/NewCollectionDialog.qml index 061ccd80fa4..91b7ad4230c 100644 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/NewCollectionDialog.qml +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/NewCollectionDialog.qml @@ -24,13 +24,13 @@ StudioControls.Dialog { && jsonCollections.isValid && newCollectionPath.isValid - title: qsTr("Add a new Collection") + title: qsTr("Add a new Model") anchors.centerIn: parent closePolicy: Popup.CloseOnEscape modal: true onOpened: { - collectionName.text = qsTr("Collection") + collectionName.text = qsTr("Model") updateType() updateJsonSourceIndex() updateCollectionExists() @@ -53,7 +53,7 @@ StudioControls.Dialog { function updateType() { newCollectionPath.text = "" if (typeMode.currentValue === NewCollectionDialog.SourceType.NewJson) { - newCollectionFileDialog.nameFilters = ["Json Files (*.json)"] + newCollectionFileDialog.nameFilters = ["JSON Files (*.json)"] newCollectionFileDialog.fileMode = PlatformWidgets.FileDialog.SaveFile newCollectionPath.enabled = true jsonCollections.enabled = false @@ -65,8 +65,8 @@ StudioControls.Dialog { jsonCollections.enabled = false typeMode.collectionType = "csv" } else if (typeMode.currentValue === NewCollectionDialog.SourceType.ExistingCollection) { - newCollectionFileDialog.nameFilters = ["All Collection Files (*.json *.csv)", - "Json Files (*.json)", + newCollectionFileDialog.nameFilters = ["All Model Group Files (*.json *.csv)", + "JSON Files (*.json)", "Comma-Separated Values (*.csv)"] newCollectionFileDialog.fileMode = PlatformWidgets.FileDialog.OpenFile newCollectionPath.enabled = true @@ -107,7 +107,6 @@ StudioControls.Dialog { component ErrorField: Text { Layout.columnSpan: 2 color: StudioTheme.Values.themeError - text: qsTr("Collection name can not be empty") font.family: StudioTheme.Constants.font.family font.pixelSize: StudioTheme.Values.baseIconFontSize } @@ -131,10 +130,10 @@ StudioControls.Dialog { Layout.fillWidth: true model: ListModel { - ListElement { text: qsTr("New Json collection"); value: NewCollectionDialog.SourceType.NewJson} - ListElement { text: qsTr("New CSV collection"); value: NewCollectionDialog.SourceType.NewCsv} - ListElement { text: qsTr("Import an existing collection"); value: NewCollectionDialog.SourceType.ExistingCollection} - ListElement { text: qsTr("Add collection to an available JSON"); value: NewCollectionDialog.SourceType.NewCollectionToJson} + ListElement { text: qsTr("New JSON model group"); value: NewCollectionDialog.SourceType.NewJson} + ListElement { text: qsTr("New CSV model"); value: NewCollectionDialog.SourceType.NewCsv} + ListElement { text: qsTr("Import an existing model group"); value: NewCollectionDialog.SourceType.ExistingCollection} + ListElement { text: qsTr("Add a model to an available JSON model group"); value: NewCollectionDialog.SourceType.NewCollectionToJson} } textRole: "text" @@ -189,7 +188,7 @@ StudioControls.Dialog { } NameField { - text: qsTr("Json Collection") + text: qsTr("JSON model group") visible: jsonCollections.enabled } @@ -217,11 +216,11 @@ StudioControls.Dialog { ErrorField { visible: !jsonCollections.isValid - text: qsTr("Add a json resource to continue") + text: qsTr("Add a JSON resource to continue") } NameField { - text: qsTr("Collection name") + text: qsTr("Model name") visible: collectionName.enabled } @@ -246,12 +245,12 @@ StudioControls.Dialog { } ErrorField { - text: qsTr("Collection name can not be empty") + text: qsTr("The model name can not be empty") visible: collectionName.enabled && collectionName.text === "" } ErrorField { - text: qsTr("Collection name already exists %1").arg(collectionName.text) + text: qsTr("The model name already exists %1").arg(collectionName.text) visible: collectionName.enabled && collectionName.alreadyExists } } diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.cpp index 2cb78338d97..d79ea8acf87 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.cpp @@ -203,7 +203,7 @@ QHash CollectionSourceModel::roleNames() const {CollectionTypeRole, "sourceCollectionType"}, {SelectedRole, "sourceIsSelected"}, {SourceRole, "sourceAddress"}, - {CollectionsRole, "collections"}}); + {CollectionsRole, "internalModels"}}); } return roles; } @@ -295,13 +295,13 @@ bool CollectionSourceModel::addCollectionToSource(const ModelNode &node, int idx = sourceIndex(node); if (idx < 0) - return returnError(tr("Node is not indexed in the collections model.")); + return returnError(tr("Node is not indexed in the models.")); if (node.type() != CollectionEditor::JSONCOLLECTIONMODEL_TYPENAME) - return returnError(tr("Node should be a json collection model.")); + return returnError(tr("Node should be a JSON model.")); if (collectionExists(node, collectionName)) - return returnError(tr("Collection does not exist.")); + return returnError(tr("Model does not exist.")); QString sourceFileAddress = node.variantProperty(CollectionEditor::SOURCEFILE_PROPERTY) .value() @@ -340,12 +340,12 @@ bool CollectionSourceModel::addCollectionToSource(const ModelNode &node, auto collections = m_collectionList.at(idx); if (collections.isNull()) - return returnError(tr("No collection is available for the json file.")); + return returnError(tr("No model is available for the JSON model group.")); collections->selectCollectionName(collectionName); return true; } else { - return returnError(tr("Json document type should be an object containing collections.")); + return returnError(tr("JSON document type should be an object containing models.")); } } @@ -445,7 +445,7 @@ void CollectionSourceModel::onCollectionNameChanged(const QString &oldName, cons QTC_ASSERT(collectionList, return); auto emitRenameWarning = [this](const QString &msg) -> void { - emit this->warning(tr("Rename Collection"), msg); + emit this->warning(tr("Rename Model"), msg); }; const ModelNode node = collectionList->sourceNode(); @@ -498,13 +498,13 @@ void CollectionSourceModel::onCollectionNameChanged(const QString &oldName, cons if (!collectionContainsOldName) { emitRenameWarning( - tr("Collection doesn't contain the old collection name (%1).").arg(oldName)); + tr("The model group doesn't contain the old model name (%1).").arg(oldName)); return; } if (collectionContainsNewName) { emitRenameWarning( - tr("The collection name \"%1\" already exists in the source file.").arg(newName)); + tr("The model name \"%1\" already exists in the model group.").arg(newName)); return; } @@ -537,7 +537,7 @@ void CollectionSourceModel::onCollectionsRemoved(const QStringList &removedColle QTC_ASSERT(collectionList, return); auto emitDeleteWarning = [this](const QString &msg) -> void { - emit warning(tr("Delete Collection"), msg); + emit warning(tr("Delete Model"), msg); }; const ModelNode node = collectionList->sourceNode(); @@ -589,7 +589,7 @@ void CollectionSourceModel::onCollectionsRemoved(const QStringList &removedColle if (sourceContainsCollection) { rootObject.remove(collectionName); } else { - emitDeleteWarning(tr("Collection doesn't contain the collection name (%1).") + emitDeleteWarning(tr("The model group doesn't contain the model name (%1).") .arg(sourceContainsCollection)); } } diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp index 4f2cb1dacc9..327e1ed89df 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp @@ -64,8 +64,8 @@ QmlDesigner::WidgetInfo CollectionView::widgetInfo() "CollectionEditor", WidgetInfo::LeftPane, 0, - tr("Collection Editor"), - tr("Collection Editor view")); + tr("Model Editor"), + tr("Model Editor view")); } void CollectionView::modelAttached(Model *model) @@ -89,7 +89,7 @@ void CollectionView::nodeReparented(const ModelNode &node, void CollectionView::nodeAboutToBeRemoved(const ModelNode &removedNode) { - // removing the collections lib node + // removing the model lib node if (isStudioCollectionModel(removedNode)) m_widget->sourceModel()->removeSource(removedNode); } @@ -135,11 +135,11 @@ void CollectionView::selectedNodesChanged(const QList &selectedNodeLi m_widget->setTargetNodeSelected(singleSelectedHasModelProperty); - // More than one collections are selected. So ignore them + // More than one model is selected. So ignore them if (selectedCollectionNodes.size() > 1) return; - if (selectedCollectionNodes.size() == 1) { // If exactly one collection is selected + if (selectedCollectionNodes.size() == 1) { // If exactly one model is selected m_widget->sourceModel()->selectSource(selectedCollectionNodes.first()); return; } @@ -161,7 +161,7 @@ void CollectionView::addResource(const QUrl &url, const QString &name, const QSt VariantProperty nameProperty = resourceNode.variantProperty("objectName"); sourceProperty.setValue(sourceAddress); nameProperty.setValue(name); - resourceNode.setIdWithoutRefactoring(model()->generateIdFromName(name, "collection")); + resourceNode.setIdWithoutRefactoring(model()->generateIdFromName(name, "model")); rootModelNode().defaultNodeAbstractProperty().reparentHere(resourceNode); }); } @@ -177,7 +177,7 @@ void CollectionView::refreshModel() if (!model()) return; - // Load Collections + // Load Model Groups const ModelNodes collectionSourceNodes = rootModelNode().subModelNodesOfType( jsonCollectionMetaInfo()) + rootModelNode().subModelNodesOfType( diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp index 517231359f8..a8861b3808a 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp @@ -74,7 +74,7 @@ CollectionWidget::CollectionWidget(CollectionView *view) , m_collectionDetailsSortFilterModel(std::make_unique()) , m_quickWidget(new StudioQuickWidget(this)) { - setWindowTitle(tr("Collection View", "Title of collection view widget")); + setWindowTitle(tr("Model Editor", "Title of model editor widget")); Core::IContext *icontext = nullptr; Core::Context context(Constants::C_QMLMATERIALBROWSER); @@ -194,7 +194,7 @@ bool CollectionWidget::isCsvFile(const QString &csvFileAddress) const if (!file.exists()) return false; - // TODO: Evaluate the csv file + // TODO: Evaluate the CSV file return true; } @@ -253,7 +253,7 @@ bool CollectionWidget::addCollection(const QString &collectionName, QString errorMsg; bool added = m_sourceModel->addCollectionToSource(node, collectionName, &errorMsg); if (!added) - warn(tr("Can not add a collection to the json file"), errorMsg); + warn(tr("Can not add a model to the JSON file"), errorMsg); return added; } @@ -268,8 +268,8 @@ void CollectionWidget::assignSourceNodeToSelectedItem(const QVariant &sourceNode QTC_ASSERT(sourceModel.isValid() && targetNode.isValid(), return); if (sourceModel.id().isEmpty()) { - warn(tr("Assigning the collection source"), - tr("The collection source must have a valid id to be assigned.")); + warn(tr("Assigning the model group"), + tr("The model group must have a valid id to be assigned.")); return; } From c946a30f367c811a045155759ff4981b991345c6 Mon Sep 17 00:00:00 2001 From: Amr Essam Date: Thu, 9 Nov 2023 18:34:18 +0200 Subject: [PATCH 195/242] QmlDesigner: Save effect resources Also effects are now can be dragged in the form editor Task-number: QDS-10500 Change-Id: I5a701b8077231bcbb299c0c4f1cbf2f5173a14dd Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Mahmoud Badri --- .../effectMakerQmlSources/EffectMaker.qml | 6 +- .../effectmakernew/effectmakermodel.cpp | 145 ++++++++++++++++++ src/plugins/effectmakernew/effectmakermodel.h | 2 + .../componentcore/modelnodeoperations.cpp | 2 +- 4 files changed, 153 insertions(+), 2 deletions(-) diff --git a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMaker.qml b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMaker.qml index 2430739c469..19e08acd525 100644 --- a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMaker.qml +++ b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMaker.qml @@ -19,7 +19,11 @@ Item { SaveDialog { id: saveDialog anchors.centerIn: parent - onAccepted: EffectMakerBackend.effectMakerModel.exportComposition(saveDialog.compositionName) + onAccepted: { + let name = saveDialog.compositionName + EffectMakerBackend.effectMakerModel.exportComposition(name) + EffectMakerBackend.effectMakerModel.exportResources(name) + } } Column { diff --git a/src/plugins/effectmakernew/effectmakermodel.cpp b/src/plugins/effectmakernew/effectmakermodel.cpp index 20879526bae..da329afc567 100644 --- a/src/plugins/effectmakernew/effectmakermodel.cpp +++ b/src/plugins/effectmakernew/effectmakermodel.cpp @@ -488,6 +488,56 @@ QJsonObject nodeToJson(const CompositionNode &node) return nodeObject; } +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'; + if (m_shaderFeatures.enabled(ShaderFeatures::Source)) { + s += " // This is the main source for the effect\n"; + s += " property Item source: null\n"; + } + 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"; + } + if (m_shaderFeatures.enabled(ShaderFeatures::Time)) { + s += " // When timeRunning is false, this can be used to control iTime manually\n"; + s += " property real animatedTime: frameAnimation.elapsedTime\n"; + } + if (m_shaderFeatures.enabled(ShaderFeatures::Frame)) { + s += " // When timeRunning is false, this can be used to control iFrame manually\n"; + s += " property int animatedFrame: frameAnimation.currentFrame\n"; + } + s += '\n'; + // Custom properties + if (!m_exportedRootPropertiesString.isEmpty()) { + s += m_exportedRootPropertiesString; + s += '\n'; + } + if (m_shaderFeatures.enabled(ShaderFeatures::Time) + || m_shaderFeatures.enabled(ShaderFeatures::Frame)) { + s += " FrameAnimation {\n"; + s += " id: frameAnimation\n"; + s += " running: rootItem.timeRunning\n"; + s += " }\n"; + s += '\n'; + } + + //TODO: Blue stuff goes here + + s += getQmlComponentString(true); + s += "}\n"; + return s; +} + void EffectMakerModel::exportComposition(const QString &name) { const QString effectsAssetsDir = QmlDesigner::ModelNodeOperations::getEffectsDefaultDirectory(); @@ -521,6 +571,101 @@ void EffectMakerModel::exportComposition(const QString &name) saveFile.close(); } +void EffectMakerModel::exportResources(const QString &name) +{ + // Make sure that uniforms are up-to-date + updateCustomUniforms(); + + QString qmlFilename = name + ".qml"; + QString vsFilename = name + ".vert.qsb"; + QString fsFilename = name + ".frag.qsb"; + + // Shaders should be all lowercase + vsFilename = vsFilename.toLower(); + fsFilename = fsFilename.toLower(); + + // Get effects dir + const Utils::FilePath effectsResDir = QmlDesigner::ModelNodeOperations::getEffectsImportDirectory(); + const QString effectsResPath = effectsResDir.pathAppended(name).toString() + QDir::separator(); + + // Create the qmldir for effects + Utils::FilePath qmldirPath = effectsResDir.resolvePath(QStringLiteral("qmldir")); + QString qmldirContent = QString::fromUtf8(qmldirPath.fileContents().value_or(QByteArray())); + if (qmldirContent.isEmpty()) { + qmldirContent.append("module Effects\n"); + qmldirPath.writeFileContents(qmldirContent.toUtf8()); + } + + // Create effect folder if not created + Utils::FilePath effectPath = Utils::FilePath::fromString(effectsResPath); + if (!effectPath.exists()) { + QDir effectDir(effectsResDir.toString()); + effectDir.mkdir(name); + } + + // Create effect qmldir + qmldirPath = effectPath.resolvePath(QStringLiteral("qmldir")); + qmldirContent = QString::fromUtf8(qmldirPath.fileContents().value_or(QByteArray())); + if (qmldirContent.isEmpty()) { + qmldirContent.append("module Effects."); + qmldirContent.append(name); + qmldirContent.append('\n'); + qmldirContent.append(name); + qmldirContent.append(" 1.0 "); + qmldirContent.append(name); + qmldirContent.append(".qml\n"); + qmldirPath.writeFileContents(qmldirContent.toUtf8()); + } + + // Create the qml file + QString qmlComponentString = getQmlEffectString(); + QStringList qmlStringList = qmlComponentString.split('\n'); + + // Replace shaders with local versions + for (int i = 1; i < qmlStringList.size(); i++) { + QString line = qmlStringList.at(i).trimmed(); + if (line.startsWith("vertexShader")) { + QString vsLine = " vertexShader: '" + vsFilename + "'"; + qmlStringList[i] = vsLine; + } else if (line.startsWith("fragmentShader")) { + QString fsLine = " fragmentShader: '" + fsFilename + "'"; + qmlStringList[i] = fsLine; + } + } + + const QString qmlString = qmlStringList.join('\n'); + QString qmlFilePath = effectsResPath + qmlFilename; + writeToFile(qmlString.toUtf8(), qmlFilePath, FileType::Text); + + // Export shaders and images + QStringList sources = {m_vertexShaderFilename, m_fragmentShaderFilename}; + QStringList dests = {vsFilename, fsFilename}; + + const QList uniforms = allUniforms(); + for (const Uniform *uniform : uniforms) { + if (uniform->type() == Uniform::Type::Sampler && !uniform->value().toString().isEmpty()) { + QString imagePath = uniform->value().toString(); + QFileInfo fi(imagePath); + QString imageFilename = fi.fileName(); + sources.append(imagePath); + dests.append(imageFilename); + } + } + + //TODO: Copy source files if requested in future versions + + // Copy files + for (int i = 0; i < sources.count(); ++i) { + Utils::FilePath source = Utils::FilePath::fromString(sources[i]); + Utils::FilePath target = Utils::FilePath::fromString(effectsResPath + dests[i]); + if (target.exists()) + target.removeFile(); // Remove existing file for update + + if (!source.copyFile(target)) + qWarning() << __FUNCTION__ << " Failed to copy file: " << source; + } +} + void EffectMakerModel::resetEffectError(int type) { if (m_effectErrors.contains(type)) { diff --git a/src/plugins/effectmakernew/effectmakermodel.h b/src/plugins/effectmakernew/effectmakermodel.h index 5f7550bb37f..9647b8886a1 100644 --- a/src/plugins/effectmakernew/effectmakermodel.h +++ b/src/plugins/effectmakernew/effectmakermodel.h @@ -84,6 +84,7 @@ public: Q_INVOKABLE void setEffectError(const QString &errorMessage, int type = -1, int lineNumber = -1); Q_INVOKABLE void exportComposition(const QString &name); + Q_INVOKABLE void exportResources(const QString &name); signals: void isEmptyChanged(); @@ -139,6 +140,7 @@ private: QString stripFileFromURL(const QString &urlString) const; void updateImageWatchers(); void clearImageWatchers(); + QString getQmlEffectString(); void updateCustomUniforms(); void createFiles(); diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp index 53c7b7fa216..ffa51710f6f 100644 --- a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp +++ b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp @@ -1675,7 +1675,7 @@ Utils::FilePath getEffectsImportDirectory() if (!effectsPath.exists()) { QDir dir(projectPath.toString()); - dir.mkpath(defaultDir); + dir.mkpath(effectsPath.toString()); } return effectsPath; From 95ed1ab5b9446b1e871be28b09c7b9835c795b2b Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Thu, 9 Nov 2023 18:06:25 +0200 Subject: [PATCH 196/242] EffectMaker: Update color value to use the new ColorEditor Change-Id: I70cb1786667023209539713e2aa3c2149479f3b5 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Miikka Heikkinen Reviewed-by: Amr Elsayed --- .../qmldesigner/effectMakerQmlSources/ValueColor.qml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/share/qtcreator/qmldesigner/effectMakerQmlSources/ValueColor.qml b/share/qtcreator/qmldesigner/effectMakerQmlSources/ValueColor.qml index 8b381ccaa1b..1f601f90639 100644 --- a/share/qtcreator/qmldesigner/effectMakerQmlSources/ValueColor.qml +++ b/share/qtcreator/qmldesigner/effectMakerQmlSources/ValueColor.qml @@ -3,7 +3,7 @@ import QtQuick import QtQuickDesignerTheme -import HelperWidgets as HelperWidgets +import StudioControls as StudioControls import StudioTheme 1.0 as StudioTheme import EffectMakerBackend @@ -13,11 +13,11 @@ Row { width: parent.width spacing: 5 - HelperWidgets.ColorEditor { - backendValue: uniformBackendValue + StudioControls.ColorEditor { + actionIndicatorVisible: false - showExtendedFunctionButton: false + Component.onCompleted: color = uniformValue - onValueChanged: uniformValue = convertColorToString(color) + onColorChanged: uniformValue = color } } From 43e074581e741dd030160f3f8050ef72b8c92671 Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Thu, 9 Nov 2023 19:52:58 +0200 Subject: [PATCH 197/242] EffectMaker: Add the change preview bg color editor Fixes: QDS-10623 Change-Id: I216c254421dc65d21c0a582bcaac02f2fe3227e8 Reviewed-by: Amr Elsayed Reviewed-by: Miikka Heikkinen Reviewed-by: Qt CI Patch Build Bot --- .../EffectMakerPreview.qml | 29 ++++++++++++------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMakerPreview.qml b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMakerPreview.qml index cd2fde0fee1..25ab469f098 100644 --- a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMakerPreview.qml +++ b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMakerPreview.qml @@ -2,7 +2,6 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 import QtQuick -import QtQuick.Layouts import QtQuickDesignerTheme import HelperWidgets as HelperWidgets import StudioControls as StudioControls @@ -54,11 +53,10 @@ Column { height: StudioTheme.Values.toolbarHeight color: StudioTheme.Values.themeToolbarBackground - RowLayout { - anchors.fill: parent + Row { spacing: 5 - anchors.rightMargin: 5 anchors.leftMargin: 5 + anchors.left: parent.left PreviewImagesComboBox { id: imagesComboBox @@ -66,9 +64,18 @@ Column { mainRoot: root.mainRoot } - Item { - Layout.fillWidth: true + StudioControls.ColorEditor { + id: colorEditor + + actionIndicatorVisible: false + showHexTextField: false + color: "#dddddd" } + } + + Row { + spacing: 5 + anchors.horizontalCenter: parent.horizontalCenter HelperWidgets.AbstractButton { enabled: sourceImage.scale > .4 @@ -102,10 +109,12 @@ Column { sourceImage.scale = 1 } } + } - Item { - Layout.fillWidth: true - } + Row { + spacing: 5 + anchors.rightMargin: 5 + anchors.right: parent.right Column { Text { @@ -148,7 +157,7 @@ Column { Rectangle { // preview image id: preview - color: "#dddddd" + color: colorEditor.color width: parent.width height: 200 clip: true From 0d6e2b10a203752046723c69d87d5cd3bc8651ec Mon Sep 17 00:00:00 2001 From: Henning Gruendl Date: Thu, 9 Nov 2023 17:29:59 +0100 Subject: [PATCH 198/242] QmlDesigner: Rename window manager uri Change-Id: Ied88f4cbc394d8faba7280fbd1af210bb7daf76a Reviewed-by: Thomas Hartmann Reviewed-by: Qt CI Patch Build Bot --- .../imports/StudioControls/PopupDialog.qml | 2 +- .../imports/StudioControls/TopLevelComboBox.qml | 2 +- src/plugins/qmldesignerbase/utils/windowmanager.cpp | 12 ++++++++---- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/PopupDialog.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/PopupDialog.qml index d5243f996b7..d3d0d56882e 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/PopupDialog.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/PopupDialog.qml @@ -6,7 +6,7 @@ import QtQuick.Controls.Basic as Basic import QtQuick.Window import QtQuick.Shapes import StudioTheme as StudioTheme -import QtQuickDesignerWindowManager +import StudioWindowManager QtObject { id: root diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/TopLevelComboBox.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/TopLevelComboBox.qml index cc92a1cd923..3aa5d709402 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/TopLevelComboBox.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/TopLevelComboBox.qml @@ -5,7 +5,7 @@ import QtQuick import QtQuick.Templates as T import StudioTheme as StudioTheme import QtQuickDesignerTheme -import QtQuickDesignerWindowManager +import StudioWindowManager T.ComboBox { id: control diff --git a/src/plugins/qmldesignerbase/utils/windowmanager.cpp b/src/plugins/qmldesignerbase/utils/windowmanager.cpp index 0f79b3b75e9..9c76c3f10a0 100644 --- a/src/plugins/qmldesignerbase/utils/windowmanager.cpp +++ b/src/plugins/qmldesignerbase/utils/windowmanager.cpp @@ -25,10 +25,14 @@ WindowManager::WindowManager() void WindowManager::registerDeclarativeType() { - [[maybe_unused]] static const int typeIndex = qmlRegisterSingletonType( - "QtQuickDesignerWindowManager", 1, 0, "WindowManager", [](QQmlEngine *, QJSEngine *) { - return new WindowManager(); - }); + [[maybe_unused]] static const int typeIndex + = qmlRegisterSingletonType("StudioWindowManager", + 1, + 0, + "WindowManager", + [](QQmlEngine *, QJSEngine *) { + return new WindowManager(); + }); } WindowManager::~WindowManager() {} From acd4792dedc7c7f98f0cd7238a455f0190d23c09 Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Wed, 8 Nov 2023 09:17:58 +0200 Subject: [PATCH 199/242] QmlDesigner: Prevent accepting invalid lines on collection sort Task-number: QDS-11117 Change-Id: Id603e5a5b85e99cf87972c853b130b26a195b223 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Miikka Heikkinen Reviewed-by: Mahmoud Badri --- .../collectiondetailsmodel.cpp | 6 ++ .../collectiondetailssortfiltermodel.cpp | 83 ++++++++++++++++--- .../collectiondetailssortfiltermodel.h | 5 ++ 3 files changed, 82 insertions(+), 12 deletions(-) diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.cpp index e082a54cc3f..969333bc3fd 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.cpp @@ -176,6 +176,7 @@ bool CollectionDetailsModel::insertRows(int row, int count, const QModelIndex &p m_currentCollection.insertEmptyElements(row, count); endInsertRows(); + selectRow(row); return true; } @@ -189,6 +190,11 @@ bool CollectionDetailsModel::removeColumns(int column, int count, const QModelIn bool columnsRemoved = m_currentCollection.removeColumns(column, count); endRemoveColumns(); + int nextColumn = column - 1; + if (nextColumn < 0 && columnCount(parent) > 0) + nextColumn = 0; + + selectColumn(nextColumn); return columnsRemoved; } diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailssortfiltermodel.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailssortfiltermodel.cpp index 64bdb8a64d1..50fcadd4944 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailssortfiltermodel.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailssortfiltermodel.cpp @@ -14,24 +14,24 @@ CollectionDetailsSortFilterModel::CollectionDetailsSortFilterModel(QObject *pare : QSortFilterProxyModel(parent) { connect(this, &CollectionDetailsSortFilterModel::rowsInserted, - this, &CollectionDetailsSortFilterModel::updateEmpty); + this, &CollectionDetailsSortFilterModel::updateRowCountChanges); connect(this, &CollectionDetailsSortFilterModel::rowsRemoved, - this, &CollectionDetailsSortFilterModel::updateEmpty); + this, &CollectionDetailsSortFilterModel::updateRowCountChanges); connect(this, &CollectionDetailsSortFilterModel::modelReset, - this, &CollectionDetailsSortFilterModel::updateEmpty); + this, &CollectionDetailsSortFilterModel::updateRowCountChanges); + + setDynamicSortFilter(true); } void CollectionDetailsSortFilterModel::setSourceModel(CollectionDetailsModel *model) { m_source = model; Super::setSourceModel(model); - connect(m_source, &CollectionDetailsModel::selectedColumnChanged, this, [this](int sourceColumn) { - emit selectedColumnChanged(mapFromSource(m_source->index(0, sourceColumn)).column()); - }); + connect(m_source, &CollectionDetailsModel::selectedColumnChanged, + this, &CollectionDetailsSortFilterModel::updateSelectedColumn); - connect(m_source, &CollectionDetailsModel::selectedRowChanged, this, [this](int sourceRow) { - emit selectedRowChanged(mapFromSource(m_source->index(sourceRow, 0)).row()); - }); + connect(m_source, &CollectionDetailsModel::selectedRowChanged, + this, &CollectionDetailsSortFilterModel::updateSelectedRow); } int CollectionDetailsSortFilterModel::selectedRow() const @@ -64,10 +64,12 @@ bool CollectionDetailsSortFilterModel::selectColumn(int column) CollectionDetailsSortFilterModel::~CollectionDetailsSortFilterModel() = default; -bool CollectionDetailsSortFilterModel::filterAcceptsRow( - [[maybe_unused]] int sourceRow, [[maybe_unused]] const QModelIndex &sourceParent) const +bool CollectionDetailsSortFilterModel::filterAcceptsRow(int sourceRow, + const QModelIndex &sourceParent) const { - return true; + QTC_ASSERT(m_source, return false); + QModelIndex sourceIndex(m_source->index(sourceRow, 0, sourceParent)); + return sourceIndex.isValid(); } bool CollectionDetailsSortFilterModel::lessThan(const QModelIndex &sourceleft, @@ -93,4 +95,61 @@ void CollectionDetailsSortFilterModel::updateEmpty() } } +void CollectionDetailsSortFilterModel::updateSelectedRow() +{ + const int upToDateSelectedRow = selectedRow(); + if (m_selectedRow == upToDateSelectedRow) + return; + + const int rows = rowCount(); + const int columns = columnCount(); + const int previousRow = m_selectedRow; + + m_selectedRow = upToDateSelectedRow; + emit this->selectedRowChanged(m_selectedRow); + + auto notifySelectedDataChanged = [this, rows, columns](int notifyingRow) { + if (notifyingRow > -1 && notifyingRow < rows && columns) { + emit dataChanged(index(notifyingRow, 0), + index(notifyingRow, columns - 1), + {CollectionDetailsModel::SelectedRole}); + } + }; + + notifySelectedDataChanged(previousRow); + notifySelectedDataChanged(m_selectedRow); +} + +void CollectionDetailsSortFilterModel::updateSelectedColumn() +{ + const int upToDateSelectedColumn = selectedColumn(); + if (m_selectedColumn == upToDateSelectedColumn) + return; + + const int rows = rowCount(); + const int columns = columnCount(); + const int previousColumn = m_selectedColumn; + + m_selectedColumn = upToDateSelectedColumn; + emit this->selectedColumnChanged(m_selectedColumn); + + auto notifySelectedDataChanged = [this, rows, columns](int notifyingCol) { + if (notifyingCol > -1 && notifyingCol < columns && rows) { + emit dataChanged(index(0, notifyingCol), + index(rows - 1, notifyingCol), + {CollectionDetailsModel::SelectedRole}); + } + }; + + notifySelectedDataChanged(previousColumn); + notifySelectedDataChanged(m_selectedColumn); +} + +void CollectionDetailsSortFilterModel::updateRowCountChanges() +{ + updateEmpty(); + updateSelectedRow(); + invalidate(); +} + } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailssortfiltermodel.h b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailssortfiltermodel.h index fdf6f2df9aa..93305f3ca20 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailssortfiltermodel.h +++ b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailssortfiltermodel.h @@ -44,8 +44,13 @@ protected: private: void updateEmpty(); + void updateSelectedRow(); + void updateSelectedColumn(); + void updateRowCountChanges(); QPointer m_source; + int m_selectedColumn = -1; + int m_selectedRow = -1; bool m_isEmpty = true; }; From 41217720301de51993b995dd803beebe566c97b7 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Thu, 9 Nov 2023 17:37:52 +0100 Subject: [PATCH 200/242] QmlDesigner: Open Connection Editor from context menu MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Open the ConnectionsDialog from context menu. Task-number: QDS-10711 Change-Id: If0b78dc3d9820cdefa7efe9c85dacfae8f3d1d40 Reviewed-by: Henning Gründl Reviewed-by: Qt CI Patch Build Bot --- .../connectionseditor/ConnectionsDialog.qml | 3 ++ .../imports/StudioControls/PopupDialog.qml | 12 ++++++- .../componentcore/designeractionmanager.cpp | 25 ++++++++++++-- .../connectioneditor/connectionmodel.cpp | 18 +++++++--- .../connectioneditor/connectionmodel.h | 5 ++- .../connectioneditor/connectionview.cpp | 33 +++++++++++++++++-- .../connectioneditor/connectionview.h | 5 +++ .../include/customnotifications.h | 2 ++ .../qmldesignerbase/utils/windowmanager.cpp | 6 ++++ .../qmldesignerbase/utils/windowmanager.h | 3 ++ 10 files changed, 100 insertions(+), 12 deletions(-) diff --git a/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialog.qml b/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialog.qml index 0d3393352f9..85a847ba5e0 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialog.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialog.qml @@ -48,6 +48,9 @@ StudioControls.PopupDialog { function onPopupShouldClose() { root.close() } + function onPopupShouldOpen() { + root.showGlobal() + } } } } diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/PopupDialog.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/PopupDialog.qml index d3d0d56882e..ae821804a5d 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/PopupDialog.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/PopupDialog.qml @@ -33,10 +33,20 @@ QtObject { signal closing(close: var) + function showGlobal() + { + var pos = WindowManager.globalCursorPosition(); + root.__itemGlobal = Qt.rect(pos.x, pos.y, 300, 20) + root.chevronVisible = false + root.layout() + window.show() + window.raise() + } + function show(target: Item) { var originGlobal = target.mapToGlobal(0, 0) root.__itemGlobal = Qt.rect(originGlobal.x, originGlobal.y, target.width, target.height) - + root.chevronVisible = true root.layout() window.show() window.raise() diff --git a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp index cb38789b633..865c5dfa8a5 100644 --- a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp +++ b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp @@ -15,6 +15,7 @@ #include "qmleditormenu.h" #include "rewritingexception.h" #include +#include #include #include #include @@ -740,7 +741,19 @@ public: activeSignalHandlerGroup->addMenu(editSlotGroup); } - //add an action to open Connection Form from here: + ActionTemplate *openEditorAction = new ActionTemplate( + (propertyName + "OpenEditorId").toLatin1(), + QString( + QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Open Connections Editor")), + [=](const SelectionContext &) { + signalHandler.view() + ->emitCustomNotification(EditConnectionNotification, + {signalHandler.parentModelNode()}, + {signalHandler.name()}); + //ActionEditor::invokeEditor(signalHandler, removeSignal); + }); + + activeSignalHandlerGroup->addAction(openEditorAction); ActionTemplate *removeSignalHandlerAction = new ActionTemplate( (propertyName + "RemoveSignalHandlerId").toLatin1(), @@ -809,7 +822,15 @@ public: } } - //add an action to open Connection Form from here + ActionTemplate *openEditorAction = new ActionTemplate( + (signalStr + "OpenEditorId").toLatin1(), + QString(QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Add Connection")), + [=](const SelectionContext &) { + currentNode.view()->emitCustomNotification(AddConnectionNotification, + {currentNode}, + {signalStr}); + }); + newSignal->addAction(openEditorAction); addConnection->addMenu(newSignal); } diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp index 55f9f5dcae8..dd6350f4e56 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp @@ -346,7 +346,7 @@ static PropertyName getFirstSignalForTarget(const NodeMetaInfo &target) return ret; } -void ConnectionModel::addConnection() +void ConnectionModel::addConnection(const PropertyName &signalName) { QmlDesignerPlugin::emitUsageStatistics(Constants::EVENT_CONNECTION_ADDED); @@ -358,10 +358,13 @@ void ConnectionModel::addConnection() if (nodeMetaInfo.isValid()) { ModelNode selectedNode = connectionView()->selectedModelNodes().constFirst(); - const PropertyName signalHandlerName = addOnToSignalName( - QString::fromUtf8(getFirstSignalForTarget( - selectedNode.metaInfo()))) - .toUtf8(); + + PropertyName signalHandlerName = signalName; + if (signalHandlerName.isEmpty()) { + signalHandlerName = addOnToSignalName(QString::fromUtf8(getFirstSignalForTarget( + selectedNode.metaInfo()))) + .toUtf8(); + } connectionView() ->executeInTransaction("ConnectionModel::addConnection", [=, &rootModelNode]() { @@ -2127,4 +2130,9 @@ void QmlDesigner::ConnectionModel::modelAboutToBeDetached() emit m_delegate->popupShouldClose(); } +void ConnectionModel::showPopup() +{ + emit m_delegate->popupShouldOpen(); +} + } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.h b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.h index 1aa94f77337..d45014914d4 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.h +++ b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.h @@ -54,7 +54,7 @@ public: QStringList getflowActionTriggerForRow(int row) const; ModelNode getTargetNodeForConnection(const ModelNode &connection) const; - void addConnection(); + void addConnection(const PropertyName &signalName = {}); void bindingPropertyChanged(const BindingProperty &bindingProperty); void variantPropertyChanged(const VariantProperty &variantProperty); @@ -74,6 +74,8 @@ public: void nodeAboutToBeRemoved(const ModelNode &removedNode); void modelAboutToBeDetached(); + void showPopup(); + signals: void currentIndexChanged(); @@ -302,6 +304,7 @@ signals: void hasElseChanged(); void sourceChanged(); void popupShouldClose(); + void popupShouldOpen(); private: int currentRow() const; diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionview.cpp b/src/plugins/qmldesigner/components/connectioneditor/connectionview.cpp index c92a675ecd7..ab010e3f355 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectionview.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/connectionview.cpp @@ -11,13 +11,14 @@ #include "theme.h" #include +#include #include -#include -#include -#include #include #include #include +#include +#include +#include #include @@ -352,4 +353,30 @@ ConnectionView *ConnectionView::instance() return s_instance; } +void ConnectionView::customNotification(const AbstractView *, + const QString &identifier, + const QList &nodeList, + const QList &data) +{ + if (identifier == AddConnectionNotification) { + QTC_ASSERT(data.count() == 1, return ); + + const PropertyName name = data.first().toString().toUtf8(); + m_connectionModel->addConnection(name); + m_connectionModel->showPopup(); + } else if (identifier == EditConnectionNotification) { + QTC_ASSERT(nodeList.count() == 1, return ); + ModelNode modelNode = nodeList.first(); + + QTC_ASSERT(data.count() == 1, return ); + + const PropertyName name = data.first().toByteArray(); + + QTC_ASSERT(modelNode.hasSignalHandlerProperty(name), return ); + + m_connectionModel->selectProperty(modelNode.signalHandlerProperty(name)); + m_connectionModel->showPopup(); + } +} + } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionview.h b/src/plugins/qmldesigner/components/connectioneditor/connectionview.h index 338d1fb14ed..12a53d43a9e 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectionview.h +++ b/src/plugins/qmldesigner/components/connectioneditor/connectionview.h @@ -72,6 +72,11 @@ public: static ConnectionView *instance(); + void customNotification(const AbstractView *view, + const QString &identifier, + const QList &nodeList, + const QList &data) override; + signals: void currentIndexChanged(); diff --git a/src/plugins/qmldesigner/designercore/include/customnotifications.h b/src/plugins/qmldesigner/designercore/include/customnotifications.h index 1095a178402..217c67730fb 100644 --- a/src/plugins/qmldesigner/designercore/include/customnotifications.h +++ b/src/plugins/qmldesigner/designercore/include/customnotifications.h @@ -12,4 +12,6 @@ const QString EndRewriterAmend = QStringLiteral("__end rewriter amend__"); const QString StartRewriterApply = QStringLiteral("start rewriter apply__"); const QString EndRewriterApply = QStringLiteral("__end rewriter apply__"); const QString UpdateItemlibrary = QStringLiteral("__update itemlibrary__"); +const QString AddConnectionNotification = QStringLiteral("__add connection__"); +const QString EditConnectionNotification = QStringLiteral("edit connection__"); } // namespace QmlDesigner diff --git a/src/plugins/qmldesignerbase/utils/windowmanager.cpp b/src/plugins/qmldesignerbase/utils/windowmanager.cpp index 9c76c3f10a0..c52d5d469a6 100644 --- a/src/plugins/qmldesignerbase/utils/windowmanager.cpp +++ b/src/plugins/qmldesignerbase/utils/windowmanager.cpp @@ -5,6 +5,7 @@ #include +#include #include #include #include @@ -37,4 +38,9 @@ void WindowManager::registerDeclarativeType() WindowManager::~WindowManager() {} +QPoint WindowManager::globalCursorPosition() +{ + return QCursor::pos(); +} + } // namespace QmlDesignerBase diff --git a/src/plugins/qmldesignerbase/utils/windowmanager.h b/src/plugins/qmldesignerbase/utils/windowmanager.h index 094d548ab3c..3d8e692c1aa 100644 --- a/src/plugins/qmldesignerbase/utils/windowmanager.h +++ b/src/plugins/qmldesignerbase/utils/windowmanager.h @@ -5,6 +5,7 @@ #include "../qmldesignerbase_global.h" +#include #include QT_FORWARD_DECLARE_CLASS(QWindow) @@ -23,6 +24,8 @@ public: static void registerDeclarativeType(); + Q_INVOKABLE QPoint globalCursorPosition(); + signals: void focusWindowChanged(QWindow *window); void aboutToQuit(); From c2581b3bc418a520979e67d1286e6df85cb095c5 Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Thu, 9 Nov 2023 16:52:01 +0200 Subject: [PATCH 201/242] QmlDesigner: Consider an initial value for the model files Task-number: QDS-11176 Change-Id: I5d35628022194760f63f26c67bc273d02b061575 Reviewed-by: Mahmoud Badri Reviewed-by: Miikka Heikkinen --- .../components/collectioneditor/collectionwidget.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp index a8861b3808a..ba634533a48 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp @@ -210,7 +210,12 @@ bool CollectionWidget::addCollection(const QString &collectionName, QString sourcePath = ::urlToLocalPath(sourceAddress); if (collectionType == "json") { QJsonObject jsonObject; - jsonObject.insert(collectionName, QJsonArray()); + QJsonObject initialObject; + QJsonArray initialCollection; + + initialObject.insert("Column1", ""); + initialCollection.append(initialObject); + jsonObject.insert(collectionName, initialCollection); QFile sourceFile(sourcePath); if (!sourceFile.open(QFile::WriteOnly)) { @@ -235,6 +240,7 @@ bool CollectionWidget::addCollection(const QString &collectionName, return false; } + sourceFile.write("Column1\n\n"); sourceFile.close(); bool loaded = loadCsvFile(sourcePath, collectionName); From 52818b8367ec249ecbf02d1d5b36736204d33bfe Mon Sep 17 00:00:00 2001 From: Yasser Grimes Date: Wed, 8 Nov 2023 19:13:32 +0200 Subject: [PATCH 202/242] McuSupport: Prevent crash while selecting QtMCUs kits for previewing Users tend to select QtMCUs kits when opening a QtMCUs project in Design Studio this leads to a crash on some of them because of incompatible or broken kit configurations (but not QtMCUs desktop kits) This patch will prevent the crash by preventing the use of QtMCUs kit and issues a warning about it. Fixes: QDS-10143 Change-Id: I54acd7b3f1439492bc37979028a1c2d4548da4cf Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Thomas Hartmann --- src/plugins/mcusupport/mcubuildstep.cpp | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/plugins/mcusupport/mcubuildstep.cpp b/src/plugins/mcusupport/mcubuildstep.cpp index 52efbbed91b..52c7d9129ce 100644 --- a/src/plugins/mcusupport/mcubuildstep.cpp +++ b/src/plugins/mcusupport/mcubuildstep.cpp @@ -7,6 +7,8 @@ #include "mculegacyconstants.h" #include "mcusupportconstants.h" +#include + #include #include @@ -27,6 +29,7 @@ #include +#include #include #include @@ -165,6 +168,27 @@ void MCUBuildStepFactory::updateDeployStep(ProjectExplorer::Target *target, bool return; ProjectExplorer::DeployConfiguration *deployConfiguration = target->activeDeployConfiguration(); + + // Return if the kit is currupted or is an MCU kit (unsupported in Design Studio) + if (!deployConfiguration + || (target->kit() && target->kit()->hasValue(Constants::KIT_MCUTARGET_KITVERSION_KEY))) { + // This branch is called multiple times when selecting the run configuration + // Show the message only once when the kit changes (avoid repitition) + static ProjectExplorer::Kit *previousSelectedKit = nullptr; + if (previousSelectedKit && previousSelectedKit == target->kit()) + return; + previousSelectedKit = target->kit(); + + //TODO use DeployMcuProcessStep::showError instead when the Issues panel + // supports poping up on new entries + QMessageBox::warning( + Core::ICore::dialogParent(), + QmlProjectManager::Tr::tr("The Selected Kit Is Not Supported"), + QmlProjectManager::Tr::tr( + "You cannot use the selected kit to preview Qt for MCUs applications.")); + return; + } + ProjectExplorer::BuildStepList *stepList = deployConfiguration->stepList(); ProjectExplorer::BuildStep *step = stepList->firstStepWithId(DeployMcuProcessStep::id); if (!step && enabled) { From d0a7b9b359f5e902ebbad787f40cec93d20c7b8b Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Fri, 10 Nov 2023 15:33:21 +0200 Subject: [PATCH 203/242] EffectMaker: Fix qsb tool version parameter Change-Id: I4a1ceafba63d466d9cec555b332d6894b1016ba4 Reviewed-by: Amr Elsayed Reviewed-by: Mahmoud Badri --- src/plugins/effectmakernew/effectmakermodel.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/effectmakernew/effectmakermodel.cpp b/src/plugins/effectmakernew/effectmakermodel.cpp index da329afc567..c388e38b42b 100644 --- a/src/plugins/effectmakernew/effectmakermodel.cpp +++ b/src/plugins/effectmakernew/effectmakermodel.cpp @@ -1200,7 +1200,7 @@ void EffectMakerModel::bakeShaders() const QStringList outPaths = {m_vertexShaderFilename, m_fragmentShaderFilename}; for (int i = 0; i < 2; ++i) { const auto workDir = Utils::FilePath::fromString(outPaths[i]); - QStringList args = {"-s", "--glsl", "\"300 es,120,150,440\"", "--hlsl", "50", "--msl", "12"}; + QStringList args = {"-s", "--glsl", "300es,120,150,440", "--hlsl", "50", "--msl", "12"}; args << "-o" << outPaths[i] << srcPaths[i]; auto qsbProcess = new Utils::Process(this); From 6d3c08ac4bcbaeb8a5b5bdddbd32762734e1ed95 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Fri, 10 Nov 2023 14:53:14 +0100 Subject: [PATCH 204/242] QmlDesigner: Always enable model editor Change-Id: I4a83b4b61e468b6db6c2930fa4e8ae470f8dbc72 Reviewed-by: Mahmoud Badri --- .../components/componentcore/viewmanager.cpp | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/plugins/qmldesigner/components/componentcore/viewmanager.cpp b/src/plugins/qmldesigner/components/componentcore/viewmanager.cpp index 58f766b9157..9ba4bfd26b1 100644 --- a/src/plugins/qmldesigner/components/componentcore/viewmanager.cpp +++ b/src/plugins/qmldesigner/components/componentcore/viewmanager.cpp @@ -203,7 +203,8 @@ QList ViewManager::standardViews() const &d->materialBrowserView, &d->textureEditorView, &d->statesEditorView, - &d->designerActionManagerView}; + &d->designerActionManagerView, + &d->collectionView}; if (QmlDesignerPlugin::instance() ->settings() @@ -211,9 +212,6 @@ QList ViewManager::standardViews() const .toBool()) list.append(&d->debugView); - if (qEnvironmentVariableIsSet("ENABLE_QDS_COLLECTIONVIEW")) - list.append(&d->collectionView); - #ifdef CHECK_LICENSE if (checkLicense() == FoundLicense::enterprise) list.append(&d->contentLibraryView); @@ -388,9 +386,7 @@ QList ViewManager::widgetInfos() const widgetInfoList.append(d->materialBrowserView.widgetInfo()); widgetInfoList.append(d->textureEditorView.widgetInfo()); widgetInfoList.append(d->statesEditorView.widgetInfo()); - - if (qEnvironmentVariableIsSet("ENABLE_QDS_COLLECTIONVIEW")) - widgetInfoList.append(d->collectionView.widgetInfo()); + widgetInfoList.append(d->collectionView.widgetInfo()); #ifdef CHECK_LICENSE if (checkLicense() == FoundLicense::enterprise) From 172f889223d17a2b36a0816abbf8391d221d21c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Jen=C3=9Fen?= Date: Fri, 10 Nov 2023 08:12:22 +0100 Subject: [PATCH 205/242] qds: enable EffectMakerNew Change-Id: Id1170ba7d9e61102c2e1d400f184ff3da53eacc2 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Tim Jenssen --- src/plugins/CMakeLists.txt | 4 +--- src/plugins/effectmakernew/CMakeLists.txt | 8 +++----- src/plugins/effectmakernew/EffectMakerNew.json.in | 1 + src/plugins/studiowelcome/CMakeLists.txt | 2 +- 4 files changed, 6 insertions(+), 9 deletions(-) diff --git a/src/plugins/CMakeLists.txt b/src/plugins/CMakeLists.txt index 5d7316dee61..15416d790d6 100644 --- a/src/plugins/CMakeLists.txt +++ b/src/plugins/CMakeLists.txt @@ -103,6 +103,7 @@ if (WITH_QMLDESIGNER) set(qmldesigner_builddir ${PROJECT_BINARY_DIR}/qmldsgnr) endif() add_subdirectory(qmldesigner ${qmldesigner_builddir}) + add_subdirectory(effectmakernew) add_subdirectory(studiowelcome) add_subdirectory(insight) endif() @@ -113,7 +114,4 @@ add_subdirectory(saferenderer) add_subdirectory(copilot) add_subdirectory(terminal) -if (WITH_QMLDESIGNER) - add_subdirectory(effectmakernew) -endif() add_subdirectory(compilerexplorer) diff --git a/src/plugins/effectmakernew/CMakeLists.txt b/src/plugins/effectmakernew/CMakeLists.txt index d190481db85..4c1474d8eb7 100644 --- a/src/plugins/effectmakernew/CMakeLists.txt +++ b/src/plugins/effectmakernew/CMakeLists.txt @@ -1,11 +1,10 @@ -find_package(Qt6 OPTIONAL_COMPONENTS Gui Quick) - add_qtc_plugin(EffectMakerNew + CONDITION TARGET Qt::Quick AND TARGET QtCreator::QmlDesigner PLUGIN_DEPENDS QtCreator::QmlDesigner QtCreator::ProjectExplorer QtCreator::QmlProjectManager DEPENDS - Qt::Core - QtCreator::Utils Qt::CorePrivate Qt::Widgets Qt::Qml Qt::QmlPrivate Qt::Quick + Qt::Core Qt::CorePrivate Qt::Widgets Qt::Qml Qt::QmlPrivate Qt::Quick + QtCreator::Utils SOURCES effectmakerplugin.cpp effectmakerplugin.h effectmakerwidget.cpp effectmakerwidget.h @@ -22,5 +21,4 @@ add_qtc_plugin(EffectMakerNew shaderfeatures.cpp shaderfeatures.h syntaxhighlighterdata.cpp syntaxhighlighterdata.h propertyhandler.cpp propertyhandler.h - BUILD_DEFAULT OFF ) diff --git a/src/plugins/effectmakernew/EffectMakerNew.json.in b/src/plugins/effectmakernew/EffectMakerNew.json.in index 555876364b8..c48476418b5 100644 --- a/src/plugins/effectmakernew/EffectMakerNew.json.in +++ b/src/plugins/effectmakernew/EffectMakerNew.json.in @@ -10,6 +10,7 @@ ], "Description" : "Plugin for Effect Maker.", "Url" : "http://www.qt.io", + "DisabledByDefault" : true, ${IDE_PLUGIN_DEPENDENCIES} } diff --git a/src/plugins/studiowelcome/CMakeLists.txt b/src/plugins/studiowelcome/CMakeLists.txt index 74c8315507a..c4db40db5e9 100644 --- a/src/plugins/studiowelcome/CMakeLists.txt +++ b/src/plugins/studiowelcome/CMakeLists.txt @@ -1,5 +1,5 @@ add_qtc_plugin(StudioWelcome - CONDITION TARGET Qt::QuickWidgets AND TARGET QmlDesigner + CONDITION TARGET Qt::QuickWidgets AND TARGET QtCreator::QmlDesigner DEPENDS Qt::QuickWidgets Qt::QmlPrivate PLUGIN_DEPENDS Core ProjectExplorer QtSupport QmlDesigner DEFINES STUDIO_QML_PATH="${CMAKE_CURRENT_SOURCE_DIR}/qml/" From a4e65cfb0d63d60a347edd8f3bf8c1fe87cadcd7 Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Fri, 10 Nov 2023 16:17:43 +0200 Subject: [PATCH 206/242] EffectMaker: Elide long uniform names Also fix preview toolbar vertical alignment Fixes: QDS-10559 Change-Id: I6f03a4232742c0ea3afca042e15711f1abb272c5 Reviewed-by: Miikka Heikkinen Reviewed-by: Amr Elsayed --- .../effectMakerQmlSources/EffectCompositionNodeUniform.qml | 1 + .../qmldesigner/effectMakerQmlSources/EffectMakerPreview.qml | 3 +++ 2 files changed, 4 insertions(+) diff --git a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectCompositionNodeUniform.qml b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectCompositionNodeUniform.qml index 978fd95358c..6aedc798f59 100644 --- a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectCompositionNodeUniform.qml +++ b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectCompositionNodeUniform.qml @@ -50,6 +50,7 @@ Item { Layout.maximumWidth: 140 Layout.minimumWidth: 140 Layout.preferredWidth: 140 + elide: Text.ElideRight HelperWidgets.ToolTipArea { anchors.fill: parent diff --git a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMakerPreview.qml b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMakerPreview.qml index 25ab469f098..7ffdcc7b5e8 100644 --- a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMakerPreview.qml +++ b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMakerPreview.qml @@ -57,6 +57,7 @@ Column { spacing: 5 anchors.leftMargin: 5 anchors.left: parent.left + anchors.verticalCenter: parent.verticalCenter PreviewImagesComboBox { id: imagesComboBox @@ -76,6 +77,7 @@ Column { Row { spacing: 5 anchors.horizontalCenter: parent.horizontalCenter + anchors.verticalCenter: parent.verticalCenter HelperWidgets.AbstractButton { enabled: sourceImage.scale > .4 @@ -115,6 +117,7 @@ Column { spacing: 5 anchors.rightMargin: 5 anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter Column { Text { From 2d07c94d355ace0f759ae21e88cbf96fb9ee85bf Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Fri, 10 Nov 2023 14:52:11 +0100 Subject: [PATCH 207/242] QmlDesigner: Show warning if collection editor cannot be created Change-Id: Ia0aea335f58c6e206dd23e004c081a43a96abf4c Reviewed-by: Mahmoud Badri --- .../collectioneditor/collectionwidget.cpp | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp index ba634533a48..e86eb090b0e 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp @@ -12,8 +12,9 @@ #include "qmldesignerplugin.h" #include "theme.h" -#include #include +#include +#include #include #include @@ -140,6 +141,18 @@ void CollectionWidget::reloadQmlSource() QTC_ASSERT(QFileInfo::exists(collectionViewQmlPath), return); m_quickWidget->setSource(QUrl::fromLocalFile(collectionViewQmlPath)); + + if (!m_quickWidget->rootObject()) { + QString errorString; + const auto errors = m_quickWidget->errors(); + for (const QQmlError &error : errors) + errorString.append("\n" + error.toString()); + + Core::AsynchronousMessageBox::warning(tr("Cannot Create QtQuick View"), + tr("StatesEditorWidget: %1 cannot be created.%2") + .arg(collectionViewQmlPath, errorString)); + return; + } } QSize CollectionWidget::minimumSizeHint() const From cf13950b7b9b4849a48dd70c60a24b6f361e7dd8 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Fri, 10 Nov 2023 16:55:09 +0100 Subject: [PATCH 208/242] QmlDesigner: Add missing () to function call MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This does silently fail. Change-Id: Ie80e8400dd555f40875580d8ea054842b9bf6086 Reviewed-by: Henning Gründl --- share/qtcreator/qmldesigner/stateseditor/Main.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/qtcreator/qmldesigner/stateseditor/Main.qml b/share/qtcreator/qmldesigner/stateseditor/Main.qml index b160bd5e86f..80209864a17 100644 --- a/share/qtcreator/qmldesigner/stateseditor/Main.qml +++ b/share/qtcreator/qmldesigner/stateseditor/Main.qml @@ -557,7 +557,7 @@ Rectangle { isTiny: root.tinyMode onFocusSignal: root.currentStateInternalId = 0 - onDefaultClicked: StatesEditorBackend.statesEditorModel.resetDefaultState + onDefaultClicked: StatesEditorBackend.statesEditorModel.resetDefaultState() } } From 5c830613e87eff89fea4a49ce607657a484ebdb2 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Fri, 10 Nov 2023 17:05:52 +0100 Subject: [PATCH 209/242] QmlDesigner: Fix reflection in states editor MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We block reflection, therefore the model has to be explicitly reset. Blocking reflection also in propertiesRemoved. Change-Id: I0de1172c0b066fa71494c7b07331fa19f7fa502c Reviewed-by: Henning Gründl --- .../components/stateseditor/stateseditorview.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/plugins/qmldesigner/components/stateseditor/stateseditorview.cpp b/src/plugins/qmldesigner/components/stateseditor/stateseditorview.cpp index 5df44802e4b..26530647d86 100644 --- a/src/plugins/qmldesigner/components/stateseditor/stateseditorview.cpp +++ b/src/plugins/qmldesigner/components/stateseditor/stateseditorview.cpp @@ -612,6 +612,7 @@ void StatesEditorView::setStateAsDefault(int internalNodeId) e.showException(); } } + resetModel(); } void StatesEditorView::resetDefaultState() @@ -629,6 +630,7 @@ void StatesEditorView::resetDefaultState() } catch (const RewritingException &e) { e.showException(); } + resetModel(); } bool StatesEditorView::hasDefaultState() const @@ -729,8 +731,12 @@ void StatesEditorView::modelAboutToBeDetached(Model *model) void StatesEditorView::propertiesRemoved(const QList &propertyList) { + if (m_block) + return; + for (const AbstractProperty &property : propertyList) { - if (property.name() == "states" && property.parentModelNode() == activeStateGroup().modelNode()) + if ((property.name() == "states" || property.name() == "state") + && property.parentModelNode() == activeStateGroup().modelNode()) resetModel(); if ((property.name() == "when" || property.name() == "name") && QmlModelState::isValidQmlModelState(property.parentModelNode())) From 1914b123177d05c8e4bc36ae05aab902e5bc9f87 Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Thu, 9 Nov 2023 13:47:48 +0200 Subject: [PATCH 210/242] QmlDesigner: Polish the Collection Editor ui Task-number: QDS-10621 Change-Id: I0d9655888538bee8c759b6bf2c93eb5dc2455856 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Mahmoud Badri Reviewed-by: Thomas Hartmann --- .../CollectionDetailsEditDelegate.qml | 16 +- .../CollectionDetailsToolbar.qml | 310 +++++++++--------- .../CollectionDetailsView.qml | 29 +- .../CollectionItem.qml | 66 ++-- .../CollectionView.qml | 43 +-- .../collectionEditorQmlSource/CsvImport.qml | 109 +++--- .../EditPropertyDialog.qml | 129 ++++---- .../IconTextButton.qml | 36 +- .../collectionEditorQmlSource/JsonImport.qml | 62 ++-- .../collectionEditorQmlSource/Message.qml | 11 +- .../ModelSourceItem.qml | 69 ++-- .../NewCollectionDialog.qml | 269 +++++++-------- 12 files changed, 613 insertions(+), 536 deletions(-) diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsEditDelegate.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsEditDelegate.qml index b1b913c5a05..44058419be8 100644 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsEditDelegate.qml +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsEditDelegate.qml @@ -5,6 +5,7 @@ import QtQuick import CollectionDetails 1.0 as CollectionDetails import HelperWidgets 2.0 as HelperWidgets import StudioControls 1.0 as StudioControls +import StudioHelpers as StudioHelpers import StudioTheme 1.0 as StudioTheme import QtQuick.Templates as T @@ -100,14 +101,23 @@ Item { implicitWidth: colorPicker.width + leftPadding + rightPadding padding: 8 - HelperWidgets.ColorPicker { + StudioHelpers.ColorBackend { + id: colorBackend + } + + StudioControls.ColorEditorPopup { id: colorPicker - property alias editValue: colorPicker.color + property alias editValue: colorBackend.color + color: colorBackend.color - width: 100 + width: 200 Keys.onEnterPressed: colorPicker.focus = false + + onActivateColor: function(color) { + colorBackend.activateColor(color) + } } background: Rectangle { diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsToolbar.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsToolbar.qml index 9dba1b86015..93839872ba2 100644 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsToolbar.qml +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsToolbar.qml @@ -2,6 +2,7 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 import QtQuick +import QtQuick.Controls import QtQuick.Layouts import Qt.labs.platform as PlatformWidgets import HelperWidgets 2.0 as HelperWidgets @@ -12,13 +13,12 @@ import CollectionEditorBackend Item { id: root - property real iconHeight: 20 + property real iconHeight: 2 * StudioTheme.Values.bigFont required property var model required property var backend property int selectedRow: -1 - implicitHeight: 30 - implicitWidth: leftSideToolbar.width + rightSideToolbar.width + implicitHeight: container.height function addNewColumn() { addColumnDialog.popUp(root.model.columnCount()) @@ -28,122 +28,121 @@ Item { root.model.insertRow(root.model.rowCount()) } - Row { - id: leftSideToolbar + RowLayout { + id: container + width: parent.width - topPadding: (root.height - root.iconHeight) / 2 - leftPadding: 10 - spacing: 5 + spacing: StudioTheme.Values.sectionRowSpacing - anchors { - left: parent.left - top: parent.top - bottom: parent.bottom - } + RowLayout { + id: leftSideToolbar - IconButton { - icon: StudioTheme.Constants.addcolumnleft_medium - tooltip: qsTr("Add property left %1").arg(leftSideToolbar.topPadding) - enabled: root.model.selectedColumn > -1 - onClicked: addColumnDialog.popUp(root.model.selectedColumn - 1) - } + Layout.alignment: Qt.AlignRight | Qt.AlignVCenter + spacing: StudioTheme.Values.sectionRowSpacing - IconButton { - icon: StudioTheme.Constants.addcolumnright_medium - tooltip: qsTr("Add property right") - enabled: root.model.selectedColumn > -1 - onClicked: addColumnDialog.popUp(root.model.selectedColumn + 1) - } + IconButton { + icon: StudioTheme.Constants.addcolumnleft_medium + tooltip: qsTr("Add property left %1").arg(leftSideToolbar.topPadding) + enabled: root.model.selectedColumn > -1 + onClicked: addColumnDialog.popUp(root.model.selectedColumn - 1) + } - IconButton { - icon: StudioTheme.Constants.deletecolumn_medium - tooltip: qsTr("Delete selected property") - enabled: root.model.selectedColumn > -1 - onClicked: root.model.removeColumn(root.model.selectedColumn) + IconButton { + icon: StudioTheme.Constants.addcolumnright_medium + tooltip: qsTr("Add property right") + enabled: root.model.selectedColumn > -1 + onClicked: addColumnDialog.popUp(root.model.selectedColumn + 1) + } + + IconButton { + icon: StudioTheme.Constants.deletecolumn_medium + tooltip: qsTr("Delete selected property") + enabled: root.model.selectedColumn > -1 + onClicked: root.model.removeColumn(root.model.selectedColumn) + } + + Item { // spacer + implicitWidth: StudioTheme.Values.toolbarSpacing + implicitHeight: 1 + } + + IconButton { + icon: StudioTheme.Constants.addrowbelow_medium + tooltip: qsTr("Insert row below") + enabled: root.model.selectedRow > -1 + onClicked: root.model.insertRow(root.model.selectedRow + 1) + } + + IconButton { + icon: StudioTheme.Constants.addrowabove_medium + tooltip: qsTr("Insert row above") + enabled: root.model.selectedRow > -1 + onClicked: root.model.insertRow(root.model.selectedRow) + } + + IconButton { + icon: StudioTheme.Constants.deleterow_medium + tooltip: qsTr("Delete selected row") + enabled: root.model.selectedRow > -1 + onClicked: root.model.removeRow(root.model.selectedRow) + } } Item { // spacer - width: 20 - height: 1 + Layout.minimumHeight: 1 + Layout.fillWidth: true } - IconButton { - icon: StudioTheme.Constants.addrowbelow_medium - tooltip: qsTr("Insert row below") - enabled: root.model.selectedRow > -1 - onClicked: root.model.insertRow(root.model.selectedRow + 1) - } + RowLayout { + id: rightSideToolbar + spacing: StudioTheme.Values.sectionRowSpacing + Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter - IconButton { - icon: StudioTheme.Constants.addrowabove_medium - tooltip: qsTr("Insert row above") - enabled: root.model.selectedRow > -1 - onClicked: root.model.insertRow(root.model.selectedRow) - } - - IconButton { - icon: StudioTheme.Constants.deleterow_medium - tooltip: qsTr("Delete selected row") - enabled: root.model.selectedRow > -1 - onClicked: root.model.removeRow(root.model.selectedRow) - } - } - - Row { - id: rightSideToolbar - spacing: 5 - topPadding: (root.height - root.iconHeight) / 2 - rightPadding: 10 - - anchors { - right: parent.right - top: parent.top - bottom: parent.bottom - } - - IconButton { - icon: StudioTheme.Constants.updateContent_medium - tooltip: qsTr("Update existing file with changes") - enabled: root.model.collectionName !== "" - onClicked: - { - if (root.backend.selectedSourceAddress().indexOf("json") !== -1) - root.model.exportCollection(root.backend.selectedSourceAddress(), root.model.collectionName, "JSON") - else - root.model.exportCollection(root.backend.selectedSourceAddress(), root.model.collectionName, "CSV") - } - } - - IconButton { - icon: StudioTheme.Constants.export_medium - tooltip: qsTr("Export the model to a new file") - enabled: root.model.collectionName !== "" - onClicked: exportMenu.popup() - } - - StudioControls.Menu { - id: exportMenu - - StudioControls.MenuItem { - text: qsTr("Export as JSON") - onTriggered: + IconButton { + icon: StudioTheme.Constants.updateContent_medium + tooltip: qsTr("Update existing file with changes") + enabled: root.model.collectionName !== "" + onClicked: { - fileDialog.defaultSuffix = "json" - fileDialog.open() + if (root.backend.selectedSourceAddress().indexOf("json") !== -1) + root.model.exportCollection(root.backend.selectedSourceAddress(), root.model.collectionName, "JSON") + else + root.model.exportCollection(root.backend.selectedSourceAddress(), root.model.collectionName, "CSV") } } - StudioControls.MenuItem { - text: qsTr("Export as CSV") - onTriggered: - { - fileDialog.defaultSuffix = "csv" - fileDialog.open() + IconButton { + icon: StudioTheme.Constants.export_medium + tooltip: qsTr("Export the model to a new file") + enabled: root.model.collectionName !== "" + onClicked: exportMenu.popup() + } + + StudioControls.Menu { + id: exportMenu + + StudioControls.MenuItem { + text: qsTr("Export as JSON") + onTriggered: + { + fileDialog.defaultSuffix = "json" + fileDialog.open() + } + } + + StudioControls.MenuItem { + text: qsTr("Export as CSV") + onTriggered: + { + fileDialog.defaultSuffix = "csv" + fileDialog.open() + } } } } } + PlatformWidgets.FileDialog { id: fileDialog fileMode: PlatformWidgets.FileDialog.SaveFile @@ -161,8 +160,15 @@ Item { } component IconButton: HelperWidgets.IconButton { - height: root.iconHeight - width: root.iconHeight + Layout.preferredHeight: root.iconHeight + Layout.preferredWidth: root.iconHeight + radius: StudioTheme.Values.smallRadius + iconSize: StudioTheme.Values.bigFont + } + + component Spacer: Item { + implicitWidth: 1 + implicitHeight: StudioTheme.Values.columnGap } RegularExpressionValidator { @@ -177,7 +183,6 @@ Item { property bool nameIsValid title: qsTr("Add Column") - width: 400 function popUp(index) { @@ -197,64 +202,73 @@ Item { } } - contentItem: Column { + contentItem: ColumnLayout { spacing: 2 - GridLayout { - rowSpacing: 10 - columnSpacing: 10 - columns: 2 + Text { + text: qsTr("Column name:") + color: StudioTheme.Values.themeTextColor + } - Text { - text: qsTr("Column name:") - color: StudioTheme.Values.themeTextColor - } + StudioControls.TextField { + id: columnName - StudioControls.TextField { - id: columnName + Layout.fillWidth: true - actionIndicator.visible: false - translationIndicator.visible: false - validator: nameValidator + actionIndicator.visible: false + translationIndicator.visible: false + validator: nameValidator - Keys.onEnterPressed: addColumnDialog.addColumnName() - Keys.onReturnPressed: addColumnDialog.addColumnName() - Keys.onEscapePressed: addColumnDialog.reject() + Keys.onEnterPressed: addColumnDialog.addColumnName() + Keys.onReturnPressed: addColumnDialog.addColumnName() + Keys.onEscapePressed: addColumnDialog.reject() - onTextChanged: { - addColumnDialog.nameIsValid = (columnName.text !== "" - && !root.model.isPropertyAvailable(columnName.text)) - } - } - - Text { - text: qsTr("The model already contains \"%1\"!").arg(columnName.text) - visible: columnName.text !== "" && !addColumnDialog.nameIsValid - color: StudioTheme.Values.themeRedLight - Layout.columnSpan: 2 - } - - Text { - text: qsTr("Type:") - color: StudioTheme.Values.themeTextColor - } - - StudioControls.ComboBox { - id: addedPropertyType - - model: root.model.typesList() - actionIndicatorVisible: false + onTextChanged: { + addColumnDialog.nameIsValid = (columnName.text !== "" + && !root.model.isPropertyAvailable(columnName.text)) } } - Item { // spacer - width: 1 - height: 20 + Spacer { implicitHeight: StudioTheme.Values.controlLabelGap } + + Label { + Layout.fillWidth: true + + text: qsTr("The model already contains \"%1\"!").arg(columnName.text) + visible: columnName.text !== "" && !addColumnDialog.nameIsValid + + color: StudioTheme.Values.themeTextColor + wrapMode: Label.WordWrap + padding: 5 + + background: Rectangle { + color: "transparent" + border.width: StudioTheme.Values.border + border.color: StudioTheme.Values.themeWarning + } } - Row { - anchors.right: parent.right - spacing: 10 + Spacer {} + + Text { + text: qsTr("Type:") + color: StudioTheme.Values.themeTextColor + } + + StudioControls.ComboBox { + id: addedPropertyType + + Layout.fillWidth: true + + model: root.model.typesList() + actionIndicatorVisible: false + } + + Spacer {} + + RowLayout { + Layout.alignment: Qt.AlignRight | Qt.AlignVCenter + spacing: StudioTheme.Values.sectionRowSpacing HelperWidgets.Button { enabled: addColumnDialog.nameIsValid diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsView.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsView.qml index f7b0bf5166a..5b88ebc2619 100644 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsView.qml +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsView.qml @@ -44,13 +44,13 @@ Rectangle { color: StudioTheme.Values.themeTextColor text: root.model.collectionName - font.pixelSize: StudioTheme.Values.mediumIconFont + font.pixelSize: StudioTheme.Values.baseFontSize elide: Text.ElideRight } Item { // spacer - width: 1 - height: 10 + implicitWidth: 1 + implicitHeight: 10 } CollectionDetailsToolbar { @@ -62,8 +62,8 @@ Rectangle { } Item { // spacer - width: 1 - height: 5 + implicitWidth: 1 + implicitHeight: 5 } GridLayout { @@ -201,8 +201,8 @@ Rectangle { Layout.preferredWidth: tableView.contentWidth Layout.preferredHeight: tableView.contentHeight - Layout.minimumWidth: 10 - Layout.minimumHeight: 10 + Layout.minimumWidth: 100 + Layout.minimumHeight: 20 Layout.maximumWidth: root.width delegate: Rectangle { @@ -405,8 +405,8 @@ Rectangle { deleteColumnDialog.open() } - contentItem: Column { - spacing: 2 + contentItem: ColumnLayout { + spacing: StudioTheme.Values.sectionColumnSpacing Text { text: qsTr("Are you sure that you want to delete column \"%1\"?").arg( @@ -415,14 +415,9 @@ Rectangle { color: StudioTheme.Values.themeTextColor } - Item { // spacer - width: 1 - height: 20 - } - - Row { - anchors.right: parent.right - spacing: 10 + RowLayout { + Layout.alignment: Qt.AlignRight | Qt.AlignVCenter + spacing: StudioTheme.Values.sectionRowSpacing HelperWidgets.Button { text: qsTr("Delete") diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionItem.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionItem.qml index 1cf7a2a0308..0c28395b10b 100644 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionItem.qml +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionItem.qml @@ -127,11 +127,17 @@ Item { } } + component Spacer: Item { + implicitWidth: 1 + implicitHeight: StudioTheme.Values.columnGap + } + StudioControls.Dialog { id: deleteDialog title: qsTr("Deleting the model") clip: true + implicitWidth: contentItem.width contentItem: ColumnLayout { spacing: 2 @@ -154,13 +160,10 @@ Item { } } - Item { // spacer - Layout.fillWidth: true - Layout.preferredHeight: 20 - } + Spacer {} RowLayout { - spacing: 10 + spacing: StudioTheme.Values.sectionRowSpacing Layout.alignment: Qt.AlignRight | Qt.AlignVCenter HelperWidgets.Button { @@ -190,7 +193,7 @@ Item { newNameField.text = collectionName } - contentItem: Column { + contentItem: ColumnLayout { spacing: 2 Text { @@ -198,51 +201,48 @@ Item { color: StudioTheme.Values.themeTextColor } - Row { - spacing: 10 - Text { - text: qsTr("New name:") - color: StudioTheme.Values.themeTextColor - } + Spacer {} - StudioControls.TextField { - id: newNameField + Text { + Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter + text: qsTr("New name:") + color: StudioTheme.Values.themeTextColor + } - anchors.verticalCenter: parent.verticalCenter - actionIndicator.visible: false - translationIndicator.visible: false - validator: newNameValidator + StudioControls.TextField { + id: newNameField - Keys.onEnterPressed: renameDialog.accept() - Keys.onReturnPressed: renameDialog.accept() - Keys.onEscapePressed: renameDialog.reject() + Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter + Layout.fillWidth: true - onTextChanged: { - btnRename.enabled = newNameField.text !== "" - } + actionIndicator.visible: false + translationIndicator.visible: false + validator: newNameValidator + + Keys.onEnterPressed: renameDialog.accept() + Keys.onReturnPressed: renameDialog.accept() + Keys.onEscapePressed: renameDialog.reject() + + onTextChanged: { + btnRename.enabled = newNameField.text !== "" } } - Item { // spacer - width: 1 - height: 20 - } + Spacer {} - Row { - anchors.right: parent.right - spacing: 10 + RowLayout { + Layout.alignment: Qt.AlignRight | Qt.AlignVCenter + spacing: StudioTheme.Values.sectionRowSpacing HelperWidgets.Button { id: btnRename - anchors.verticalCenter: parent.verticalCenter text: qsTr("Rename") onClicked: renameDialog.accept() } HelperWidgets.Button { text: qsTr("Cancel") - anchors.verticalCenter: parent.verticalCenter onClicked: renameDialog.reject() } } diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionView.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionView.qml index fdfc7be3042..70eb32c9c86 100644 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionView.qml +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionView.qml @@ -67,40 +67,41 @@ Item { Layout.minimumWidth: 300 Layout.fillWidth: !grid.isHorizontal - Rectangle { + RowLayout { + spacing: StudioTheme.Values.sectionRowSpacing Layout.fillWidth: true - Layout.preferredHeight: StudioTheme.Values.height + 5 - color: StudioTheme.Values.themeToolbarBackground + Layout.preferredHeight: 50 Text { - id: collectionText + Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter + Layout.fillWidth: true - anchors.verticalCenter: parent.verticalCenter text: qsTr("Data Models") - font.pixelSize: StudioTheme.Values.mediumIconFont + font.pixelSize: StudioTheme.Values.baseFontSize color: StudioTheme.Values.themeTextColor leftPadding: 15 } - Row { - anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter - rightPadding: 12 - spacing: 2 + IconTextButton { + Layout.alignment: Qt.AlignRight | Qt.AlignVCenter - HelperWidgets.IconButton { - icon: StudioTheme.Constants.downloadjson_large - tooltip: qsTr("Import JSON") + icon: StudioTheme.Constants.import_medium + text: qsTr("JSON") + tooltip: qsTr("Import JSON") + radius: StudioTheme.Values.smallRadius - onClicked: jsonImporter.open() - } + onClicked: jsonImporter.open() + } - HelperWidgets.IconButton { - icon: StudioTheme.Constants.downloadcsv_large - tooltip: qsTr("Import CSV") + IconTextButton { + Layout.alignment: Qt.AlignRight | Qt.AlignVCenter - onClicked: csvImporter.open() - } + icon: StudioTheme.Constants.import_medium + text: qsTr("CSV") + tooltip: qsTr("Import CSV") + radius: StudioTheme.Values.smallRadius + + onClicked: csvImporter.open() } } diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CsvImport.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CsvImport.qml index 7f1f9dde2b0..6bcd6e97a3a 100644 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CsvImport.qml +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CsvImport.qml @@ -3,6 +3,7 @@ import QtQuick import QtQuick.Controls +import QtQuick.Layouts import QtQuickDesignerTheme 1.0 import Qt.labs.platform as PlatformWidgets import HelperWidgets 2.0 as HelperWidgets @@ -51,21 +52,30 @@ StudioControls.Dialog { onClosed: root.reject() } - contentItem: Column { - spacing: 10 + component Spacer: Item { + implicitWidth: 1 + implicitHeight: StudioTheme.Values.columnGap + } - Row { - spacing: 10 - Text { - text: qsTr("File name: ") - anchors.verticalCenter: parent.verticalCenter - color: StudioTheme.Values.themeTextColor - } + contentItem: ColumnLayout { + spacing: 2 + + Text { + text: qsTr("File name: ") + color: StudioTheme.Values.themeTextColor + } + + RowLayout { + spacing: StudioTheme.Values.sectionRowSpacing + + Layout.fillWidth: true StudioControls.TextField { id: fileName - anchors.verticalCenter: parent.verticalCenter + Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter + Layout.fillWidth: true + actionIndicator.visible: false translationIndicator.visible: false validator: fileNameValidator @@ -74,49 +84,58 @@ StudioControls.Dialog { Keys.onReturnPressed: btnCreate.onClicked() Keys.onEscapePressed: root.reject() - onTextChanged: { - root.fileExists = root.backendValue.isCsvFile(fileName.text) - } + onTextChanged: root.fileExists = root.backendValue.isCsvFile(fileName.text) } HelperWidgets.Button { id: fileDialogButton - anchors.verticalCenter: parent.verticalCenter + Layout.alignment: Qt.AlignRight | Qt.AlignVCenter + text: qsTr("Open") onClicked: fileDialog.open() } } - Row { - spacing: 10 - Text { - text: qsTr("The model name: ") - anchors.verticalCenter: parent.verticalCenter - color: StudioTheme.Values.themeTextColor - } - - StudioControls.TextField { - id: collectionName - - anchors.verticalCenter: parent.verticalCenter - actionIndicator.visible: false - translationIndicator.visible: false - validator: RegularExpressionValidator { - regularExpression: /^\w+$/ - } - - Keys.onEnterPressed: btnCreate.onClicked() - Keys.onReturnPressed: btnCreate.onClicked() - Keys.onEscapePressed: root.reject() - } - } + Spacer {} Text { + text: qsTr("The model name: ") + color: StudioTheme.Values.themeTextColor + } + + StudioControls.TextField { + id: collectionName + + Layout.fillWidth: true + + actionIndicator.visible: false + translationIndicator.visible: false + validator: RegularExpressionValidator { + regularExpression: /^\w+$/ + } + + Keys.onEnterPressed: btnCreate.onClicked() + Keys.onReturnPressed: btnCreate.onClicked() + Keys.onEscapePressed: root.reject() + } + + Spacer { implicitHeight: StudioTheme.Values.controlLabelGap } + + Label { id: fieldErrorText + Layout.fillWidth: true + color: StudioTheme.Values.themeTextColor - anchors.right: parent.right + wrapMode: Label.WordWrap + padding: 5 + + background: Rectangle { + color: "transparent" + border.width: StudioTheme.Values.border + border.color: StudioTheme.Values.themeWarning + } states: [ State { @@ -152,19 +171,16 @@ StudioControls.Dialog { ] } - Item { // spacer - width: 1 - height: 20 - } + Spacer {} - Row { - anchors.right: parent.right - spacing: 10 + RowLayout { + spacing: StudioTheme.Values.sectionRowSpacing + + Layout.alignment: Qt.AlignRight | Qt.AlignVCenter HelperWidgets.Button { id: btnCreate - anchors.verticalCenter: parent.verticalCenter text: qsTr("Import") enabled: root.fileExists && collectionName.text !== "" @@ -180,7 +196,6 @@ StudioControls.Dialog { HelperWidgets.Button { text: qsTr("Cancel") - anchors.verticalCenter: parent.verticalCenter onClicked: root.reject() } } diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/EditPropertyDialog.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/EditPropertyDialog.qml index c74fe1cb237..545896cc278 100644 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/EditPropertyDialog.qml +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/EditPropertyDialog.qml @@ -12,8 +12,10 @@ StudioControls.Dialog { required property var model property int __propertyIndex: -1 + property string __oldName title: qsTr("Edit Property") + clip: true function editProperty(index, initialPosition) { root.__propertyIndex = index @@ -24,7 +26,7 @@ StudioControls.Dialog { let previousName = root.model.propertyName(root.__propertyIndex) let previousType = root.model.propertyType(root.__propertyIndex) - oldName.text = previousName + root.__oldName = previousName newNameField.text = previousName propertyType.initialType = previousType @@ -39,7 +41,7 @@ StudioControls.Dialog { } onAccepted: { - if (newNameField.text !== "" && newNameField.text !== oldName.text) + if (newNameField.text !== "" && newNameField.text !== root.__oldName) root.model.renameColumn(root.__propertyIndex, newNameField.text) if (propertyType.changed || forceChangeType.checked) @@ -51,66 +53,65 @@ StudioControls.Dialog { propertyType.currentIndex = propertyType.find(currentDatatype) } - contentItem: Column { + component Spacer: Item { + implicitWidth: 1 + implicitHeight: StudioTheme.Values.columnGap + } + + contentItem: ColumnLayout { spacing: 2 - Grid { - spacing: 10 - columns: 2 + Text { + text: qsTr("Name") + color: StudioTheme.Values.themeTextColor + } - Text { - text: qsTr("Previous name") - color: StudioTheme.Values.themeTextColor + StudioControls.TextField { + id: newNameField + + Layout.fillWidth: true + + actionIndicator.visible: false + translationIndicator.visible: false + + Keys.onEnterPressed: root.accept() + Keys.onReturnPressed: root.accept() + Keys.onEscapePressed: root.reject() + + validator: RegularExpressionValidator { + regularExpression: /^\w+$/ } - Text { - id: oldName - color: StudioTheme.Values.themeTextColor + onTextChanged: { + editButton.enabled = newNameField.text !== "" } + } - Text { - text: qsTr("New name") - color: StudioTheme.Values.themeTextColor - } + Spacer {} - StudioControls.TextField { - id: newNameField + Text { + text: qsTr("Type") + color: StudioTheme.Values.themeTextColor + } - actionIndicator.visible: false - translationIndicator.visible: false + StudioControls.ComboBox { + id: propertyType - Keys.onEnterPressed: root.accept() - Keys.onReturnPressed: root.accept() - Keys.onEscapePressed: root.reject() + Layout.fillWidth: true - validator: RegularExpressionValidator { - regularExpression: /^\w+$/ - } + property string initialType + readonly property bool changed: propertyType.initialType !== propertyType.currentText - onTextChanged: { - editButton.enabled = newNameField.text !== "" - } - } + model: root.model.typesList() + actionIndicatorVisible: false - Text { - text: qsTr("Type") - color: StudioTheme.Values.themeTextColor - } + onInitialTypeChanged: propertyType.currentIndex = propertyType.find(initialType) + } - StudioControls.ComboBox { - id: propertyType + Spacer {} - property string initialType - readonly property bool changed: propertyType.initialType !== propertyType.currentText - - model: root.model.typesList() - actionIndicatorVisible: false - - onInitialTypeChanged: { - let propertyIndex = propertyType.find(initialType) - propertyType.currentIndex = propertyIndex - } - } + RowLayout { + spacing: StudioTheme.Values.sectionRowSpacing StudioControls.CheckBox { id: forceChangeType @@ -120,35 +121,41 @@ StudioControls.Dialog { Text { text: qsTr("Force update values") color: StudioTheme.Values.themeTextColor + Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter } } - Item { // spacer - width: 1 - height: 10 + Spacer { + visible: warningBox.visible + implicitHeight: StudioTheme.Values.controlLabelGap } Rectangle { id: warningBox + Layout.fillWidth: true + Layout.preferredHeight: warning.height + visible: propertyType.initialType !== propertyType.currentText - width: parent.width - height: warning.implicitHeight color: "transparent" + clip: true border.color: StudioTheme.Values.themeWarning RowLayout { id: warning - anchors.fill: parent + width: parent.width HelperWidgets.IconLabel { - icon: StudioTheme.Constants.warning + icon: StudioTheme.Constants.warning_medium Layout.leftMargin: 10 } Text { - text: qsTr("Conversion from %1 to %2 may lead to irreversible data loss").arg(propertyType.initialType).arg(propertyType.currentText) + text: qsTr("Conversion from %1 to %2 may lead to irreversible data loss") + .arg(propertyType.initialType) + .arg(propertyType.currentText) + color: StudioTheme.Values.themeTextColor wrapMode: Text.WordWrap Layout.fillWidth: true @@ -157,15 +164,11 @@ StudioControls.Dialog { } } - Item { // spacer - visible: warningBox.visible - width: 1 - height: 10 - } + Spacer {} - Row { - anchors.right: parent.right - spacing: 10 + RowLayout { + Layout.alignment: Qt.AlignRight | Qt.AlignVCenter + spacing: StudioTheme.Values.sectionRowSpacing HelperWidgets.Button { id: editButton diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/IconTextButton.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/IconTextButton.qml index 9edad2d4e9a..fe9d094349c 100644 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/IconTextButton.qml +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/IconTextButton.qml @@ -2,6 +2,8 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 import QtQuick +import QtQuick.Controls +import QtQuick.Layouts import StudioTheme as StudioTheme Rectangle { @@ -10,42 +12,45 @@ Rectangle { required property string text required property string icon + property alias tooltip: toolTip.text property StudioTheme.ControlStyle style: StudioTheme.Values.viewBarButtonStyle + property int fontSize: StudioTheme.Values.baseFontSize implicitHeight: style.squareControlSize.height implicitWidth: rowAlign.width signal clicked() - Row { + RowLayout { id: rowAlign - spacing: 0 - leftPadding: StudioTheme.Values.inputHorizontalPadding - rightPadding: StudioTheme.Values.inputHorizontalPadding + + anchors.verticalCenter: parent.verticalCenter + spacing: StudioTheme.Values.inputHorizontalPadding Text { id: iconItem - width: root.style.squareControlSize.width - height: root.height - anchors.verticalCenter: parent.verticalCenter + + Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter text: root.icon color: StudioTheme.Values.themeTextColor font.family: StudioTheme.Constants.iconFont.family - font.pixelSize: root.style.baseIconFontSize + font.pixelSize: StudioTheme.Values.bigFont horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter + leftPadding: StudioTheme.Values.inputHorizontalPadding } Text { id: textItem - height: root.height - anchors.verticalCenter: parent.verticalCenter + + Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter text: root.text color: StudioTheme.Values.themeTextColor font.family: StudioTheme.Constants.font.family - font.pixelSize: root.style.baseIconFontSize + font.pixelSize: StudioTheme.Values.baseFontSize horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter + rightPadding: StudioTheme.Values.inputHorizontalPadding } } @@ -56,13 +61,20 @@ Rectangle { onClicked: root.clicked() } + ToolTip { + id: toolTip + + visible: mouseArea.containsMouse && text !== "" + delay: 1000 + } + states: [ State { name: "default" when: !mouseArea.pressed && !mouseArea.containsMouse PropertyChanges { target: root - color: StudioTheme.Values.themeBackgroundColorNormal + color: StudioTheme.Values.themeControlBackground } }, State { diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/JsonImport.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/JsonImport.qml index 38298a4d6a0..fec1155927b 100644 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/JsonImport.qml +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/JsonImport.qml @@ -3,6 +3,7 @@ import QtQuick import QtQuick.Controls +import QtQuick.Layouts import QtQuickDesignerTheme 1.0 import Qt.labs.platform as PlatformWidgets import HelperWidgets 2.0 as HelperWidgets @@ -49,21 +50,25 @@ StudioControls.Dialog { onClosed: root.reject() } - contentItem: Column { + contentItem: ColumnLayout { spacing: 2 - Row { - spacing: 10 - Text { - text: qsTr("File name: ") - anchors.verticalCenter: parent.verticalCenter - color: StudioTheme.Values.themeTextColor - } + Text { + text: qsTr("File name: ") + color: StudioTheme.Values.themeTextColor + } + + RowLayout { + spacing: StudioTheme.Values.sectionRowSpacing + + Layout.fillWidth: true StudioControls.TextField { id: fileName - anchors.verticalCenter: parent.verticalCenter + Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter + Layout.fillWidth: true + actionIndicator.visible: false translationIndicator.visible: false validator: fileNameValidator @@ -72,38 +77,52 @@ StudioControls.Dialog { Keys.onReturnPressed: btnCreate.onClicked() Keys.onEscapePressed: root.reject() - onTextChanged: { - root.fileExists = root.backendValue.isJsonFile(fileName.text) - } + onTextChanged: root.fileExists = root.backendValue.isJsonFile(fileName.text) } HelperWidgets.Button { id: fileDialogButton - anchors.verticalCenter: parent.verticalCenter + + Layout.alignment: Qt.AlignRight | Qt.AlignVCenter + text: qsTr("Open") onClicked: fileDialog.open() } } - Text { + Item { // spacer + implicitWidth: 1 + implicitHeight: StudioTheme.Values.controlLabelGap + } + + Label { + Layout.fillWidth: true + + padding: 5 text: qsTr("File name cannot be empty.") + wrapMode: Label.WordWrap color: StudioTheme.Values.themeTextColor - anchors.right: parent.right visible: fileName.text === "" + + background: Rectangle { + color: "transparent" + border.width: StudioTheme.Values.border + border.color: StudioTheme.Values.themeWarning + } } Item { // spacer - width: 1 - height: 20 + implicitWidth: 1 + implicitHeight: StudioTheme.Values.sectionColumnSpacing } - Row { - anchors.right: parent.right - spacing: 10 + RowLayout { + spacing: StudioTheme.Values.sectionRowSpacing + + Layout.alignment: Qt.AlignRight | Qt.AlignVCenter HelperWidgets.Button { id: btnCreate - anchors.verticalCenter: parent.verticalCenter text: qsTr("Import") enabled: root.fileExists @@ -119,7 +138,6 @@ StudioControls.Dialog { HelperWidgets.Button { text: qsTr("Cancel") - anchors.verticalCenter: parent.verticalCenter onClicked: root.reject() } } diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/Message.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/Message.qml index fcc7f8c6660..f6e7af4560c 100644 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/Message.qml +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/Message.qml @@ -3,6 +3,7 @@ import QtQuick import QtQuick.Controls +import QtQuick.Layouts import HelperWidgets 2.0 as HelperWidgets import StudioControls 1.0 as StudioControls import StudioTheme as StudioTheme @@ -17,22 +18,22 @@ StudioControls.Dialog { implicitWidth: 300 modal: true - contentItem: Column { - spacing: 20 - width: parent.width + contentItem: ColumnLayout { + spacing: StudioTheme.Values.sectionColumnSpacing Text { + Layout.fillWidth: true text: root.message color: StudioTheme.Values.themeTextColor wrapMode: Text.WordWrap - width: root.width leftPadding: 10 rightPadding: 10 } HelperWidgets.Button { + Layout.alignment: Qt.AlignRight | Qt.AlignVCenter + text: qsTr("Close") - anchors.right: parent.right onClicked: root.reject() } } diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/ModelSourceItem.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/ModelSourceItem.qml index be3d62c2e7c..365fc930c5f 100644 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/ModelSourceItem.qml +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/ModelSourceItem.qml @@ -82,7 +82,7 @@ Item { text: StudioTheme.Constants.startNode font.family: StudioTheme.Constants.iconFont.family - font.pixelSize: expandButton.style.baseIconFontSize + font.pixelSize: 6 horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter color: textColor @@ -188,27 +188,27 @@ Item { } } + component Spacer: Item { + implicitWidth: 1 + implicitHeight: StudioTheme.Values.sectionColumnSpacing + } + StudioControls.Dialog { id: deleteDialog title: qsTr("Deleting source") - contentItem: Column { - spacing: 2 + contentItem: ColumnLayout { + spacing: StudioTheme.Values.sectionColumnSpacing Text { text: qsTr("Are you sure that you want to delete source \"" + sourceName + "\"?") color: StudioTheme.Values.themeTextColor } - Item { // spacer - width: 1 - height: 20 - } - - Row { - anchors.right: parent.right - spacing: 10 + RowLayout { + Layout.alignment: Qt.AlignRight | Qt.AlignVCenter + spacing: StudioTheme.Values.sectionRowSpacing HelperWidgets.Button { id: btnDelete @@ -239,7 +239,7 @@ Item { newNameField.text = sourceName } - contentItem: Column { + contentItem: ColumnLayout { spacing: 2 Text { @@ -247,38 +247,35 @@ Item { color: StudioTheme.Values.themeTextColor } - Row { - spacing: 10 - Text { - text: qsTr("New name:") - color: StudioTheme.Values.themeTextColor - } + Spacer {} - StudioControls.TextField { - id: newNameField + Text { + text: qsTr("New name:") + color: StudioTheme.Values.themeTextColor + } - actionIndicator.visible: false - translationIndicator.visible: false - validator: newNameValidator + StudioControls.TextField { + id: newNameField - Keys.onEnterPressed: renameDialog.accept() - Keys.onReturnPressed: renameDialog.accept() - Keys.onEscapePressed: renameDialog.reject() + Layout.fillWidth: true + actionIndicator.visible: false + translationIndicator.visible: false + validator: newNameValidator - onTextChanged: { - btnRename.enabled = newNameField.text !== "" - } + Keys.onEnterPressed: renameDialog.accept() + Keys.onReturnPressed: renameDialog.accept() + Keys.onEscapePressed: renameDialog.reject() + + onTextChanged: { + btnRename.enabled = newNameField.text !== "" } } - Item { // spacer - width: 1 - height: 20 - } + Spacer {} - Row { - anchors.right: parent.right - spacing: 10 + RowLayout { + Layout.alignment: Qt.AlignRight | Qt.AlignVCenter + spacing: StudioTheme.Values.sectionRowSpacing HelperWidgets.Button { id: btnRename diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/NewCollectionDialog.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/NewCollectionDialog.qml index 91b7ad4230c..ee5dba02706 100644 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/NewCollectionDialog.qml +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/NewCollectionDialog.qml @@ -96,7 +96,7 @@ StudioControls.Dialog { } component NameField: Text { - Layout.alignment: Qt.AlignRight | Qt.AlignVCenter + Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter horizontalAlignment: Qt.AlignRight verticalAlignment: Qt.AlignCenter color: StudioTheme.Values.themeTextColor @@ -111,157 +111,168 @@ StudioControls.Dialog { font.pixelSize: StudioTheme.Values.baseIconFontSize } + component Spacer: Item { + Layout.minimumWidth: 1 + Layout.preferredHeight: StudioTheme.Values.columnGap + } + contentItem: ColumnLayout { - spacing: 10 - GridLayout { - columns: 2 - rowSpacing: 10 + spacing: 5 - NameField { - text: qsTr("Type") + NameField { + text: qsTr("Type") + } + + StudioControls.ComboBox { + id: typeMode + + property string collectionType + + Layout.minimumWidth: 300 + Layout.fillWidth: true + + model: ListModel { + ListElement { text: qsTr("New JSON model group"); value: NewCollectionDialog.SourceType.NewJson} + ListElement { text: qsTr("New CSV model"); value: NewCollectionDialog.SourceType.NewCsv} + ListElement { text: qsTr("Import an existing model group"); value: NewCollectionDialog.SourceType.ExistingCollection} + ListElement { text: qsTr("Add a model to an available JSON model group"); value: NewCollectionDialog.SourceType.NewCollectionToJson} } - StudioControls.ComboBox { - id: typeMode + textRole: "text" + valueRole: "value" + actionIndicatorVisible: false - property string collectionType + onCurrentValueChanged: root.updateType() + } - Layout.minimumWidth: 300 - Layout.fillWidth: true + Spacer {} - model: ListModel { - ListElement { text: qsTr("New JSON model group"); value: NewCollectionDialog.SourceType.NewJson} - ListElement { text: qsTr("New CSV model"); value: NewCollectionDialog.SourceType.NewCsv} - ListElement { text: qsTr("Import an existing model group"); value: NewCollectionDialog.SourceType.ExistingCollection} - ListElement { text: qsTr("Add a model to an available JSON model group"); value: NewCollectionDialog.SourceType.NewCollectionToJson} - } - - textRole: "text" - valueRole: "value" - actionIndicatorVisible: false - - onCurrentValueChanged: root.updateType() - } + RowLayout { + visible: newCollectionPath.enabled NameField { text: qsTr("File location") visible: newCollectionPath.enabled } - RowLayout { - visible: newCollectionPath.enabled + Text { + id: newCollectionPath - Text { - id: newCollectionPath + readonly property bool isValid: !newCollectionPath.enabled || newCollectionPath.text !== "" - readonly property bool isValid: !enabled || text !== "" + Layout.fillWidth: true + Layout.alignment: Qt.AlignRight | Qt.AlignVCenter + elide: Text.ElideRight + font.family: StudioTheme.Constants.font.family + font.pixelSize: StudioTheme.Values.baseIconFontSize + color: StudioTheme.Values.themePlaceholderTextColor + } - Layout.fillWidth: true - Layout.alignment: Qt.AlignRight | Qt.AlignVCenter - elide: Text.ElideRight - font.family: StudioTheme.Constants.font.family - font.pixelSize: StudioTheme.Values.baseIconFontSize - color: StudioTheme.Values.themePlaceholderTextColor + HelperWidgets.Button { + Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter + text: qsTr("Select") + + onClicked: newCollectionFileDialog.open() + + PlatformWidgets.FileDialog { + id: newCollectionFileDialog + + title: qsTr("Select source file") + fileMode: PlatformWidgets.FileDialog.OpenFile + acceptLabel: newCollectionFileDialog.fileMode === PlatformWidgets.FileDialog.OpenFile + ? qsTr("Open") + : qsTr("Add") + + onAccepted: newCollectionPath.text = newCollectionFileDialog.currentFile } - - HelperWidgets.Button { - Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter - text: qsTr("Select") - - onClicked: newCollectionFileDialog.open() - - PlatformWidgets.FileDialog { - id: newCollectionFileDialog - - title: "Select source file" - fileMode: PlatformWidgets.FileDialog.OpenFile - acceptLabel: fileMode === PlatformWidgets.FileDialog.OpenFile ? qsTr("Open") : qsTr("Add") - - onAccepted: newCollectionPath.text = newCollectionFileDialog.currentFile - } - } - } - - ErrorField { - visible: !newCollectionPath.isValid - text: qsTr("Select a file to continue") - } - - NameField { - text: qsTr("JSON model group") - visible: jsonCollections.enabled - } - - StudioControls.ComboBox { - id: jsonCollections - - readonly property bool isValid: !enabled || currentIndex !== -1 - - implicitWidth: 300 - textRole: "sourceName" - valueRole: "sourceNode" - visible: enabled - actionIndicatorVisible: false - - model: CollectionJsonSourceFilterModel { - sourceModel: root.sourceModel - onRowsInserted: root.updateJsonSourceIndex() - onModelReset: root.updateJsonSourceIndex() - onRowsRemoved: root.updateJsonSourceIndex() - } - - onEnabledChanged: root.updateJsonSourceIndex() - onCurrentValueChanged: root.updateCollectionExists() - } - - ErrorField { - visible: !jsonCollections.isValid - text: qsTr("Add a JSON resource to continue") - } - - NameField { - text: qsTr("Model name") - visible: collectionName.enabled - } - - StudioControls.TextField { - id: collectionName - - readonly property bool isValid: !enabled || (text !== "" && !alreadyExists) - property bool alreadyExists - - visible: enabled - actionIndicator.visible: false - translationIndicator.visible: false - validator: RegularExpressionValidator { - regularExpression: /^\w+$/ - } - - Keys.onEnterPressed: btnCreate.onClicked() - Keys.onReturnPressed: btnCreate.onClicked() - Keys.onEscapePressed: root.reject() - - onTextChanged: root.updateCollectionExists() - } - - ErrorField { - text: qsTr("The model name can not be empty") - visible: collectionName.enabled && collectionName.text === "" - } - - ErrorField { - text: qsTr("The model name already exists %1").arg(collectionName.text) - visible: collectionName.enabled && collectionName.alreadyExists } } - Item { // spacer - Layout.fillHeight: true - Layout.preferredWidth: 1 + ErrorField { + visible: !newCollectionPath.isValid + text: qsTr("Select a file to continue") } + Spacer { visible: newCollectionPath.enabled } + + NameField { + text: qsTr("JSON model group") + visible: jsonCollections.enabled + } + + StudioControls.ComboBox { + id: jsonCollections + + readonly property bool isValid: !jsonCollections.enabled || jsonCollections.currentIndex !== -1 + + Layout.fillWidth: true + + implicitWidth: 300 + textRole: "sourceName" + valueRole: "sourceNode" + visible: jsonCollections.enabled + actionIndicatorVisible: false + + model: CollectionJsonSourceFilterModel { + sourceModel: root.sourceModel + onRowsInserted: root.updateJsonSourceIndex() + onModelReset: root.updateJsonSourceIndex() + onRowsRemoved: root.updateJsonSourceIndex() + } + + onEnabledChanged: root.updateJsonSourceIndex() + onCurrentValueChanged: root.updateCollectionExists() + } + + ErrorField { + visible: !jsonCollections.isValid + text: qsTr("Add a JSON resource to continue") + } + + Spacer {visible: jsonCollections.visible } + + NameField { + text: qsTr("The model name") + visible: collectionName.enabled + } + + StudioControls.TextField { + id: collectionName + + readonly property bool isValid: !collectionName.enabled + || (collectionName.text !== "" && !collectionName.alreadyExists) + property bool alreadyExists + + Layout.fillWidth: true + + visible: collectionName.enabled + actionIndicator.visible: false + translationIndicator.visible: false + validator: RegularExpressionValidator { + regularExpression: /^\w+$/ + } + + Keys.onEnterPressed: btnCreate.onClicked() + Keys.onReturnPressed: btnCreate.onClicked() + Keys.onEscapePressed: root.reject() + + onTextChanged: root.updateCollectionExists() + } + + ErrorField { + text: qsTr("The model name can not be empty") + visible: collectionName.enabled && collectionName.text === "" + } + + ErrorField { + text: qsTr("The model name already exists %1").arg(collectionName.text) + visible: collectionName.enabled && collectionName.alreadyExists + } + + Spacer { visible: collectionName.visible } + RowLayout { - spacing: 10 + spacing: StudioTheme.Values.sectionRowSpacing Layout.alignment: Qt.AlignRight | Qt.AlignBottom HelperWidgets.Button { From 1541a486c19dc98c95de06a73d0bfc0db4bdbbea Mon Sep 17 00:00:00 2001 From: Leena Miettinen Date: Mon, 13 Nov 2023 10:59:33 +0100 Subject: [PATCH 211/242] Doc: Fix double "the" Change-Id: Ibffeb5da3dd7dda3b8886bf909ecd97bf3b06d83 Reviewed-by: Johanna Vanhatapio Reviewed-by: Qt CI Patch Build Bot --- doc/qtdesignstudio/examples/doc/loginui1.qdoc | 2 +- doc/qtdesignstudio/src/components/qtquick-components.qdoc | 2 +- doc/qtdesignstudio/src/components/qtquick-positioning.qdoc | 2 +- doc/qtdesignstudio/src/components/qtquick-text.qdoc | 2 +- doc/qtdesignstudio/src/overviews/qtquick-annotations.qdoc | 2 +- .../src/prototyping/qtquick-live-preview-android.qdoc | 2 +- .../src/qtquick3d-editor/qtdesignstudio-3d-particles.qdoc | 4 ++-- .../qtdesignstudio-logic-helpers.qdoc | 2 +- doc/qtdesignstudio/src/views/qtquick-form-editor.qdoc | 2 +- 9 files changed, 10 insertions(+), 10 deletions(-) diff --git a/doc/qtdesignstudio/examples/doc/loginui1.qdoc b/doc/qtdesignstudio/examples/doc/loginui1.qdoc index afb7c50556e..773c2eaaac3 100644 --- a/doc/qtdesignstudio/examples/doc/loginui1.qdoc +++ b/doc/qtdesignstudio/examples/doc/loginui1.qdoc @@ -252,7 +252,7 @@ \image loginui1-entry-field-styled.jpg "Modified button in the 2D view" - \note Do not edit the the value of \uicontrol Text in the \uicontrol Character + \note Do not edit the value of \uicontrol Text in the \uicontrol Character property, because this will break the connection, and later you won't be able to change the text in \uicontrol {Button Content} > \uicontrol Text. diff --git a/doc/qtdesignstudio/src/components/qtquick-components.qdoc b/doc/qtdesignstudio/src/components/qtquick-components.qdoc index edd28485db3..f512b8f67d4 100644 --- a/doc/qtdesignstudio/src/components/qtquick-components.qdoc +++ b/doc/qtdesignstudio/src/components/qtquick-components.qdoc @@ -72,7 +72,7 @@ the \l{UI Files}{UI files} (.ui.qml), while developers should work on the corresponding implementation files (.qml) to define their programmatic behaviors or JavaScript. This enables iteration from - both the design and development side of the process without the the + both the design and development side of the process without the risk of overwriting each other's work. \endlist */ diff --git a/doc/qtdesignstudio/src/components/qtquick-positioning.qdoc b/doc/qtdesignstudio/src/components/qtquick-positioning.qdoc index 1eb55ebd771..6c91124d697 100644 --- a/doc/qtdesignstudio/src/components/qtquick-positioning.qdoc +++ b/doc/qtdesignstudio/src/components/qtquick-positioning.qdoc @@ -421,7 +421,7 @@ The child components of grid layout components are arranged according to the \uicontrol Flow property. When the direction of a flow is set to \uicontrol LeftToRight, child components are positioned next to to each - other until the the number of columns specified in the + other until the number of columns specified in the \uicontrol {Columns & Rows} field is reached. Then, the auto-positioning wraps back to the beginning of the next row. diff --git a/doc/qtdesignstudio/src/components/qtquick-text.qdoc b/doc/qtdesignstudio/src/components/qtquick-text.qdoc index 0bb541f1c31..0e8695b5101 100644 --- a/doc/qtdesignstudio/src/components/qtquick-text.qdoc +++ b/doc/qtdesignstudio/src/components/qtquick-text.qdoc @@ -206,7 +206,7 @@ Text can be either in plain text or rich text format, depending on the value you set in the \uicontrol Format field. If you select - \uicontrol AutoText and the the first line of text contains an HTML tag, + \uicontrol AutoText and the first line of text contains an HTML tag, the text is treated as rich text. Rich text supports a subset of HTML 4 described on the \l {Supported HTML Subset}. Note that plain text offers better performance than rich text. diff --git a/doc/qtdesignstudio/src/overviews/qtquick-annotations.qdoc b/doc/qtdesignstudio/src/overviews/qtquick-annotations.qdoc index aeaf304bfcf..ee21695251c 100644 --- a/doc/qtdesignstudio/src/overviews/qtquick-annotations.qdoc +++ b/doc/qtdesignstudio/src/overviews/qtquick-annotations.qdoc @@ -41,7 +41,7 @@ \image qtquick-annotation-editor.png "Annotation Editor" \li The \uicontrol {Selected Item} field displays the ID of the component. - \li In the the \uicontrol Name field, enter a free-form text that + \li In the \uicontrol Name field, enter a free-form text that describes the component. \li In the \uicontrol Title field, enter the text to display in the tab for this comment. diff --git a/doc/qtdesignstudio/src/prototyping/qtquick-live-preview-android.qdoc b/doc/qtdesignstudio/src/prototyping/qtquick-live-preview-android.qdoc index c4cdec1ea5f..69840eb0b2d 100644 --- a/doc/qtdesignstudio/src/prototyping/qtquick-live-preview-android.qdoc +++ b/doc/qtdesignstudio/src/prototyping/qtquick-live-preview-android.qdoc @@ -132,7 +132,7 @@ \section2 Set the AVD as the Device in the Android Kit - Next, you need to set the AVD as the Android device kit. You do this under the the + Next, you need to set the AVD as the Android device kit. You do this under the \uicontrol Kits tab. If the \uicontrol Kits list is empty, restart \QDS. \image qtds-options-kits.png diff --git a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-particles.qdoc b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-particles.qdoc index b9f5463c83d..85201a857e1 100644 --- a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-particles.qdoc +++ b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-particles.qdoc @@ -792,7 +792,7 @@ in particle size, specify values for \uicontrol {Particle scale variation} and \uicontrol {Particle end scale variation}. - \uicontrol {Depth bias} specifies the the depth bias of the emitter. Depth + \uicontrol {Depth bias} specifies the depth bias of the emitter. Depth bias is added to the object's distance from camera when sorting objects. This can be used to force the rendering order of objects that are located close to each other if it might otherwise change between frames. Negative @@ -1046,7 +1046,7 @@ \uicontrol {Maximum Size} defines the maximum size that the affector can scale particles to. - \uicontrol Duration defines the the duration of the scaling cycle in + \uicontrol Duration defines the duration of the scaling cycle in milliseconds. \uicontrol {Easing Curve} defines the diff --git a/doc/qtdesignstudio/src/qtquickdesigner-components/qtdesignstudio-logic-helpers.qdoc b/doc/qtdesignstudio/src/qtquickdesigner-components/qtdesignstudio-logic-helpers.qdoc index e616f82c972..c928fff58c6 100644 --- a/doc/qtdesignstudio/src/qtquickdesigner-components/qtdesignstudio-logic-helpers.qdoc +++ b/doc/qtdesignstudio/src/qtquickdesigner-components/qtdesignstudio-logic-helpers.qdoc @@ -285,7 +285,7 @@ \uicontrol {Input 01} field to the value of the \uicontrol {Below min} field of the minimum-maximum mapper for the bad value range. For the \e overValueAnd operator, we bind it to the value of the - \uicontrol {Above max} field of the the same mapper. + \uicontrol {Above max} field of the same mapper. \image studio-logic-helper-combining-example-ao2.png "Under value minimum-maximum mapper Input 01" diff --git a/doc/qtdesignstudio/src/views/qtquick-form-editor.qdoc b/doc/qtdesignstudio/src/views/qtquick-form-editor.qdoc index fc6d8c0223c..3ac30273920 100644 --- a/doc/qtdesignstudio/src/views/qtquick-form-editor.qdoc +++ b/doc/qtdesignstudio/src/views/qtquick-form-editor.qdoc @@ -45,7 +45,7 @@ \li \l{Previewing Component Size} \row \li \inlineimage icons/canvas-color.png - \li Sets the color of the the \uicontrol {2D} view working area. + \li Sets the color of the \uicontrol {2D} view working area. \li \l{Setting Canvas Color} \row \li \inlineimage icons/zoomIn.png From 41b7e727a8729533fc8d4e807970ee3e3f507c84 Mon Sep 17 00:00:00 2001 From: Shrief Gabr Date: Sat, 11 Nov 2023 03:06:15 +0200 Subject: [PATCH 212/242] QmlDesigner: Prevent Csv import dialog from importing unsupported files Task-number: QDS-11179 Change-Id: I31b947066571864c97b7604970d5b7ef7921a441 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Mahmoud Badri --- .../collectioneditor/collectionwidget.cpp | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp index e86eb090b0e..a6dc2054905 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp @@ -198,17 +198,13 @@ bool CollectionWidget::isJsonFile(const QString &jsonFileAddress) const return true; } -bool CollectionWidget::isCsvFile(const QString &csvFileAddress) const +bool CollectionWidget::isCsvFile(const QString &csvFilePath) const { - QUrl csvUrl(csvFileAddress); - QString fileAddress = csvUrl.isLocalFile() ? csvUrl.toLocalFile() : csvUrl.toString(); - QFile file(fileAddress); + QUrl csvUrl(csvFilePath); + QString filePath = csvUrl.isLocalFile() ? csvUrl.toLocalFile() : csvUrl.toString(); + QFile file(filePath); - if (!file.exists()) - return false; - - // TODO: Evaluate the CSV file - return true; + return file.exists() && file.fileName().endsWith(".csv"); } bool CollectionWidget::addCollection(const QString &collectionName, From 5831368fab9482a60019fd6808f63276dda10c58 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Mon, 13 Nov 2023 16:31:03 +0200 Subject: [PATCH 213/242] EffectMaker: Don't show plain preview image when there is an effect Plain preview image shows through transparent parts of the final effect image, so hide the original image when there is an effect. This fixes opacity mask effect preview. Fixes: QDS-11219 Change-Id: Ib726a6136182090fba7a3680680e998c039a497b Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Mahmoud Badri --- .../qmldesigner/effectMakerQmlSources/EffectMakerPreview.qml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMakerPreview.qml b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMakerPreview.qml index 7ffdcc7b5e8..9b58cd56bd2 100644 --- a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMakerPreview.qml +++ b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMakerPreview.qml @@ -25,6 +25,10 @@ Column { // Create a dummy parent to host the effect qml object function createNewComponent() { + // If we have a working effect, do not show preview image as it shows through + // transparent parts of the final image + source.visible = false; + var oldComponent = componentParent.children[0]; if (oldComponent) oldComponent.destroy(); @@ -45,6 +49,7 @@ Column { errorLine = e.lineNumber; } effectMakerModel.setEffectError(errorString, 0, errorLine); + source.visible = true; } } From 2ef1cd51b9b9411df2dca0847a4e31de2a3d920f Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Mon, 13 Nov 2023 17:12:44 +0100 Subject: [PATCH 214/242] QmlDesigner: Add tracking of usage to model editor Change-Id: Icb1de1537ac7a43741cbe2563322919978d91928 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Mahmoud Badri --- .../components/collectioneditor/collectionwidget.cpp | 2 ++ src/plugins/qmldesigner/qmldesignerconstants.h | 1 + 2 files changed, 3 insertions(+) diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp index a6dc2054905..8f6168dfb2d 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp @@ -114,6 +114,8 @@ CollectionWidget::CollectionWidget(CollectionView *view) connect(hotReloadShortcut, &QShortcut::activated, this, &CollectionWidget::reloadQmlSource); reloadQmlSource(); + + QmlDesignerPlugin::trackWidgetFocusTime(this, Constants::EVENT_MODELEDITOR_TIME); } void CollectionWidget::contextHelp(const Core::IContext::HelpCallback &callback) const diff --git a/src/plugins/qmldesigner/qmldesignerconstants.h b/src/plugins/qmldesigner/qmldesignerconstants.h index 56a4ebca95f..e2cf4a18525 100644 --- a/src/plugins/qmldesigner/qmldesignerconstants.h +++ b/src/plugins/qmldesigner/qmldesignerconstants.h @@ -135,6 +135,7 @@ const char EVENT_MATERIALEDITOR_TIME[] = "materialEditor"; const char EVENT_MATERIALBROWSER_TIME[] = "materialBrowser"; const char EVENT_CONTENTLIBRARY_TIME[] = "contentLibrary"; const char EVENT_INSIGHT_TIME[] = "insight"; +const char EVENT_MODELEDITOR_TIME[] = "modelEditor"; const char EVENT_TOOLBAR_MODE_CHANGE[] = "ToolBarTriggerModeChange"; const char EVENT_TOOLBAR_PROJECT_SETTINGS[] = "ToolBarTriggerProjectSettings"; const char EVENT_TOOLBAR_RUN_PROJECT[] = "ToolBarRunProject"; From eef9508b35d877e1bc86ec94a64936e94b121b87 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Mon, 13 Nov 2023 17:40:49 +0100 Subject: [PATCH 215/242] QmlDesigner: Remove states from property order list Having the property hear leads to peoperties be added after states. Since states is an array property we do not want this. Change-Id: I4688c5cdbadffd4072a575a43ca3753f5db378c8 Reviewed-by: Aleksei German --- src/plugins/qmldesigner/designercore/model/modeltotextmerger.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/plugins/qmldesigner/designercore/model/modeltotextmerger.cpp b/src/plugins/qmldesigner/designercore/model/modeltotextmerger.cpp index 87a73f6ad34..d4ae2edac33 100644 --- a/src/plugins/qmldesigner/designercore/model/modeltotextmerger.cpp +++ b/src/plugins/qmldesigner/designercore/model/modeltotextmerger.cpp @@ -386,7 +386,6 @@ PropertyNameList ModelToTextMerger::propertyOrder() PropertyName("lineHeight"), PropertyName("lineHeightMode"), PropertyName("wrapMode"), - PropertyName("states"), PropertyName("to"), PropertyName("from"), PropertyName("transitions")}; From 850e6d3eccaa413bc60876a97acbc894c157205e Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Mon, 13 Nov 2023 17:13:13 +0100 Subject: [PATCH 216/242] QmlDesigner: Add tracking of time to new effect maker Change-Id: I3182b059efacbddc4be2d63a4096ab8f1326abfb Reviewed-by: Mahmoud Badri Reviewed-by: Qt CI Patch Build Bot --- src/plugins/effectmakernew/effectmakerwidget.cpp | 4 ++++ src/plugins/qmldesigner/qmldesignerconstants.h | 1 + 2 files changed, 5 insertions(+) diff --git a/src/plugins/effectmakernew/effectmakerwidget.cpp b/src/plugins/effectmakernew/effectmakerwidget.cpp index 983459cffdb..738c0a00168 100644 --- a/src/plugins/effectmakernew/effectmakerwidget.cpp +++ b/src/plugins/effectmakernew/effectmakerwidget.cpp @@ -17,6 +17,8 @@ #include +#include +#include #include #include @@ -72,6 +74,8 @@ EffectMakerWidget::EffectMakerWidget(EffectMakerView *view) map->setProperties({{"effectMakerNodesModel", QVariant::fromValue(m_effectMakerNodesModel.data())}, {"effectMakerModel", QVariant::fromValue(m_effectMakerModel.data())}, {"rootView", QVariant::fromValue(this)}}); + QmlDesigner::QmlDesignerPlugin::trackWidgetFocusTime( + this, QmlDesigner::Constants::EVENT_NEWEFFECTMAKER_TIME); } diff --git a/src/plugins/qmldesigner/qmldesignerconstants.h b/src/plugins/qmldesigner/qmldesignerconstants.h index e2cf4a18525..dc545e06a8c 100644 --- a/src/plugins/qmldesigner/qmldesignerconstants.h +++ b/src/plugins/qmldesigner/qmldesignerconstants.h @@ -136,6 +136,7 @@ const char EVENT_MATERIALBROWSER_TIME[] = "materialBrowser"; const char EVENT_CONTENTLIBRARY_TIME[] = "contentLibrary"; const char EVENT_INSIGHT_TIME[] = "insight"; const char EVENT_MODELEDITOR_TIME[] = "modelEditor"; +const char EVENT_NEWEFFECTMAKER_TIME[] = "newEffectMaker"; const char EVENT_TOOLBAR_MODE_CHANGE[] = "ToolBarTriggerModeChange"; const char EVENT_TOOLBAR_PROJECT_SETTINGS[] = "ToolBarTriggerProjectSettings"; const char EVENT_TOOLBAR_RUN_PROJECT[] = "ToolBarRunProject"; From 8f47454b6ea30874ca2a4759d00221a2d44667c5 Mon Sep 17 00:00:00 2001 From: Yasser Grimes Date: Mon, 13 Nov 2023 16:52:34 +0200 Subject: [PATCH 217/242] McuSupport: Show only the MCU tag for QtMCUs projects As older QtMCUs projects supports only Qt5 and newer ones supoprt only Qt6 This change disable showing QtX tag for QtMCUs projects. Fixes: QDS-10333 Change-Id: I379a84948efe2f8cad21245859dc53f7f26ff9d3 Reviewed-by: Aleksei German --- src/plugins/studiowelcome/studiowelcomeplugin.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/plugins/studiowelcome/studiowelcomeplugin.cpp b/src/plugins/studiowelcome/studiowelcomeplugin.cpp index e01215e3d5c..a08e2f48bd2 100644 --- a/src/plugins/studiowelcome/studiowelcomeplugin.cpp +++ b/src/plugins/studiowelcome/studiowelcomeplugin.cpp @@ -410,13 +410,13 @@ static QString tags(const FilePath &projectFilePath) const bool isQt6 = data.contains("qt6Project: true"); const bool isMcu = data.contains("qtForMCUs: true"); - if (isQt6) + if (isMcu) + ret.append("Qt For MCU"); + else if (isQt6) ret.append("Qt 6"); else ret.append("Qt 5"); - if (isMcu) - ret.append("Qt For MCU"); return ret.join(","); } From baf0fc9598021cd27d692204834df510b390d71d Mon Sep 17 00:00:00 2001 From: Aleksei German Date: Mon, 13 Nov 2023 17:33:03 +0100 Subject: [PATCH 218/242] QmlDesigner: Fix the wrong environment list Change-Id: I3dbab618d9af457f0c9a442b94e84ad6a9220f4e Reviewed-by: Aleksei German Reviewed-by: Qt CI Patch Build Bot --- .../componentcore/changestyleaction.cpp | 42 +++++++++++-------- 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/src/plugins/qmldesigner/components/componentcore/changestyleaction.cpp b/src/plugins/qmldesigner/components/componentcore/changestyleaction.cpp index 3e1721e3fc4..aa386929400 100644 --- a/src/plugins/qmldesigner/components/componentcore/changestyleaction.cpp +++ b/src/plugins/qmldesigner/components/componentcore/changestyleaction.cpp @@ -6,6 +6,9 @@ #include #include +#include + +#include #include #include @@ -17,24 +20,27 @@ static QString styleConfigFileName(const QString &qmlFileName) ProjectExplorer::Project *currentProject = ProjectExplorer::ProjectManager::projectForFile( Utils::FilePath::fromString(qmlFileName)); - if (currentProject) { - const auto &environment = currentProject->additionalEnvironment(); - const auto &envVar = std::find_if(std::begin(environment), - std::end(environment), - [](const auto &envVar) { - return (envVar.name == u"QT_QUICK_CONTROLS_CONF" - && envVar.operation - != Utils::EnvironmentItem::SetDisabled); - }); - if (envVar != std::end(environment)) { - const auto &fileNames = currentProject->files(ProjectExplorer::Project::SourceFiles); - const auto &foundFile = std::find_if(std::begin(fileNames), - std::end(fileNames), - [&](const auto &fileName) { - return fileName.fileName() == envVar->value; - }); - if (foundFile != std::end(fileNames)) - return foundFile->toString(); + if (currentProject && currentProject->activeTarget()) { + const auto *qmlBuild = qobject_cast( + currentProject->activeTarget()->buildSystem()); + + if (qmlBuild) { + const auto &environment = qmlBuild->environment(); + const auto &envVar = std::find_if( + std::begin(environment), std::end(environment), [](const auto &envVar) { + return (envVar.name == u"QT_QUICK_CONTROLS_CONF" + && envVar.operation != Utils::EnvironmentItem::SetDisabled); + }); + if (envVar != std::end(environment)) { + const auto &fileNames = currentProject->files(ProjectExplorer::Project::SourceFiles); + const auto &foundFile = std::find_if(std::begin(fileNames), + std::end(fileNames), + [&](const auto &fileName) { + return fileName.fileName() == envVar->value; + }); + if (foundFile != std::end(fileNames)) + return foundFile->toString(); + } } } From 0bcd0b915ea0b5ff373f5f8af3ab5fa4f8677d5a Mon Sep 17 00:00:00 2001 From: Shrief Gabr Date: Fri, 10 Nov 2023 01:44:06 +0200 Subject: [PATCH 219/242] QmlDesigner: Detect data types for CSV models Task-number: QDS-11081 Change-Id: I88418e31f0fd033d7d5ec3c03fea26ae1e4e2362 Reviewed-by: Ali Kianian Reviewed-by: Mahmoud Badri Reviewed-by: Qt CI Patch Build Bot --- .../collectioneditor/collectiondetails.cpp | 1 + .../collectiondetailsmodel.cpp | 40 +++++++++++++++++++ .../collectioneditor/collectiondetailsmodel.h | 1 + 3 files changed, 42 insertions(+) diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp index 5c05d685a79..cd8296ca2de 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp @@ -428,6 +428,7 @@ void CollectionDetails::resetPropertyType(CollectionProperty &property) break; } } + property.type = type; } diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.cpp index 969333bc3fd..2374726f92c 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.cpp @@ -542,12 +542,52 @@ void CollectionDetailsModel::loadCsvCollection(const QString &source, } } + for (const QString &header : std::as_const(headers)) { + for (QJsonObject &element: elements) { + QVariant variantValue; + if (element.contains(header)) { + variantValue = variantFromString(element.value(header).toString()); + element[header] = variantValue.toJsonValue(); + + if (variantValue.isValid()) + break; + } + } + } + SourceFormat sourceFormat = csvFileIsOk ? SourceFormat::Csv : SourceFormat::Unknown; beginResetModel(); m_currentCollection.resetDetails(headers, elements, sourceFormat); endResetModel(); } +QVariant CollectionDetailsModel::variantFromString(const QString &value) +{ + constexpr QStringView typesPattern{u"(?^(?:true|false)$)|" + u"(?^(?:-?(?:0|[1-9]\\d*)?(?:\\.\\d*)?(?<=\\d|\\.)" + u"(?:e-?(?:0|[1-9]\\d*))?|0x[0-9a-f]+)$)|" + u"(?^(?:#(?:(?:[0-9a-fA-F]{2}){3,4}|" + u"(?:[0-9a-fA-F]){3,4}))$)|" + u"(?[A-Za-z][A-Za-z0-9_ -]*)"}; + static QRegularExpression validator(typesPattern.toString()); + const QString trimmedValue = value.trimmed(); + QRegularExpressionMatch match = validator.match(trimmedValue); + QVariant variantValue = value; + + if (value.isEmpty()) + return QVariant(); + if (!match.captured(u"boolean").isEmpty()) + return variantValue.toBool(); + if (!match.captured(u"number").isEmpty()) + return variantValue.toDouble(); + if (!match.captured(u"color").isEmpty()) + return variantValue.value(); + if (!match.captured(u"string").isEmpty()) + return variantValue.toString(); + + return QVariant::fromValue(value); +} + void CollectionDetailsModel::setCollectionName(const QString &newCollectionName) { if (m_collectionName != newCollectionName) { diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.h b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.h index 7b0e668aec9..38c59d8fe92 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.h +++ b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.h @@ -83,6 +83,7 @@ private: void loadCsvCollection(const QString &source, const QString &collectionName); bool saveCollectionAsJson(const QString &path, const QJsonArray &content, const QString &collectionName); bool saveCollectionAsCsv(const QString &path, const QString &content); + QVariant variantFromString(const QString &value); QHash m_openedCollections; CollectionDetails m_currentCollection; From dd2a2909630948ee89cb1dda7e0bb804bcb3dfc0 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Mon, 13 Nov 2023 17:38:48 +0100 Subject: [PATCH 220/242] QmlDesigner: Adjust display strings for connections in context menu Change-Id: I2f89413cfe86aad2f365acf9aaba378047e54cca Reviewed-by: Mats Honkamaa Reviewed-by: Pranta Ghosh Dastider Reviewed-by: Qt CI Patch Build Bot --- .../components/componentcore/designeractionmanager.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp index 865c5dfa8a5..6ad7ed994b7 100644 --- a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp +++ b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp @@ -743,8 +743,7 @@ public: ActionTemplate *openEditorAction = new ActionTemplate( (propertyName + "OpenEditorId").toLatin1(), - QString( - QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Open Connections Editor")), + QString(QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Edit the Connection")), [=](const SelectionContext &) { signalHandler.view() ->emitCustomNotification(EditConnectionNotification, @@ -757,7 +756,8 @@ public: ActionTemplate *removeSignalHandlerAction = new ActionTemplate( (propertyName + "RemoveSignalHandlerId").toLatin1(), - QString(QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Remove This Handler")), + QString( + QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Remove the Connection")), [signalHandler](const SelectionContext &) { signalHandler.parentModelNode().view()->executeInTransaction( "ConnectionsModelNodeActionGroup::" @@ -774,7 +774,7 @@ public: //singular add connection: QMenu *addConnection = new QmlEditorMenu(QString(QT_TRANSLATE_NOOP("QmlDesignerContextMenu", - "Add Signal Handler")), + "Add new Connection")), menu()); for (const auto &signalStr : signalsList) { @@ -824,7 +824,7 @@ public: ActionTemplate *openEditorAction = new ActionTemplate( (signalStr + "OpenEditorId").toLatin1(), - QString(QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Add Connection")), + QString(QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Add new Connection")), [=](const SelectionContext &) { currentNode.view()->emitCustomNotification(AddConnectionNotification, {currentNode}, From d581c80fe6c42abb80a18fde0847cdc8d01ee5c9 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Tue, 14 Nov 2023 11:36:40 +0200 Subject: [PATCH 221/242] Use OpenGL core profile for macOS Some of the shader features we use in Effect Maker plugin require newer OpenGL than the macOS default. Task-number: QDS-11194 Change-Id: Iad166a9f99b2a8fc37b0f9912947cf63ef4b3328 Reviewed-by: Mahmoud Badri Reviewed-by: Thomas Hartmann --- src/app/main.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/app/main.cpp b/src/app/main.cpp index 036d169c063..c7857dcd9d9 100644 --- a/src/app/main.cpp +++ b/src/app/main.cpp @@ -35,6 +35,7 @@ #include #include #include +#include #include #include #include @@ -524,6 +525,15 @@ int main(int argc, char **argv) } } + if (Utils::HostOsInfo::isMacHost()) { + QSurfaceFormat surfaceFormat; + surfaceFormat.setStencilBufferSize(8); + surfaceFormat.setDepthBufferSize(24); + surfaceFormat.setVersion(4, 1); + surfaceFormat.setProfile(QSurfaceFormat::CoreProfile); + QSurfaceFormat::setDefaultFormat(surfaceFormat); + } + qputenv("QSG_RHI_BACKEND", "opengl"); if (qEnvironmentVariableIsSet("QTCREATOR_DISABLE_NATIVE_MENUBAR") From 2dae80de93cbb6fdc0f9a8f317cb4f303e3f5db8 Mon Sep 17 00:00:00 2001 From: Karim Abdelrahman Date: Wed, 1 Nov 2023 11:17:38 +0200 Subject: [PATCH 222/242] McuSupport: detect if a project is QtForMcu Update the way we used to check for mcus project in McuSupport plugin. The old way relied on the deployment step which is only available when the user has already added a valid QtForMcus package to "Devices->MCU". The addition of "isQtForMcusProject" in externaldependenciesinterface.h will be used in a follow-up patch in qt-creator/tqtc-plugin-qtquickdesigner which adds a warning when using .qtbridge for Mcu project. Task-number: QDS-10599 Change-Id: Ibf46477a4f0cb4a82a10ac848acec75458bf6c03 Reviewed-by: Thomas Hartmann Reviewed-by: Reviewed-by: Aleksei German Reviewed-by: Yasser Grimes --- src/plugins/mcusupport/mcubuildstep.cpp | 3 --- src/plugins/mcusupport/mcusupportplugin.cpp | 5 ++--- .../include/externaldependenciesinterface.h | 1 + .../qmldesigner/qmldesignerexternaldependencies.cpp | 12 ++++++++++++ .../qmldesigner/qmldesignerexternaldependencies.h | 1 + .../auto/qml/qmldesigner/coretests/tst_testcore.cpp | 1 + tests/unit/tests/mocks/externaldependenciesmock.h | 1 + 7 files changed, 18 insertions(+), 6 deletions(-) diff --git a/src/plugins/mcusupport/mcubuildstep.cpp b/src/plugins/mcusupport/mcubuildstep.cpp index 52c7d9129ce..102ee306fef 100644 --- a/src/plugins/mcusupport/mcubuildstep.cpp +++ b/src/plugins/mcusupport/mcubuildstep.cpp @@ -125,9 +125,6 @@ DeployMcuProcessStep::DeployMcuProcessStep(ProjectExplorer::BuildStepList *bc, I cmdLine.addArg(directory); return cmdLine; }); - - if(target()) - target()->setNamedSettings("IS_MCU_QDS_PROJECT", true); } QString DeployMcuProcessStep::findKitInformation(ProjectExplorer::Kit *kit, const QString &key) diff --git a/src/plugins/mcusupport/mcusupportplugin.cpp b/src/plugins/mcusupport/mcusupportplugin.cpp index b9cd52a0e06..20edc2dd5db 100644 --- a/src/plugins/mcusupport/mcusupportplugin.cpp +++ b/src/plugins/mcusupport/mcusupportplugin.cpp @@ -129,10 +129,9 @@ static bool isQtMCUsProject(ProjectExplorer::Project *p) if (!target) return false; - if (!target->namedSettings("IS_MCU_QDS_PROJECT").toBool()) - return false; + const bool isMcuProject = target->additionalData("CustomQtForMCUs").toBool(); - return true; + return isMcuProject; } void McuSupportPlugin::initialize() diff --git a/src/plugins/qmldesigner/designercore/include/externaldependenciesinterface.h b/src/plugins/qmldesigner/designercore/include/externaldependenciesinterface.h index eecbdd96b8f..71ddeb7dc19 100644 --- a/src/plugins/qmldesigner/designercore/include/externaldependenciesinterface.h +++ b/src/plugins/qmldesigner/designercore/include/externaldependenciesinterface.h @@ -44,6 +44,7 @@ public: virtual QStringList modulePaths() const = 0; virtual QStringList projectModulePaths() const = 0; virtual bool isQt6Project() const = 0; + virtual bool isQtForMcusProject() const = 0; virtual QString qtQuickVersion() const = 0; virtual Utils::FilePath resourcePath(const QString &relativePath) const = 0; }; diff --git a/src/plugins/qmldesigner/qmldesignerexternaldependencies.cpp b/src/plugins/qmldesigner/qmldesignerexternaldependencies.cpp index f2c96de6736..0b4d0ac5f4e 100644 --- a/src/plugins/qmldesigner/qmldesignerexternaldependencies.cpp +++ b/src/plugins/qmldesigner/qmldesignerexternaldependencies.cpp @@ -246,6 +246,18 @@ bool ExternalDependencies::isQt6Project() const return qmlBuildSystem && qmlBuildSystem->qt6Project(); } +bool ExternalDependencies::isQtForMcusProject() const +{ + // CMakeBuildSystem + ProjectExplorer::Target *activeTarget = ProjectExplorer::ProjectManager::startupTarget(); + if (activeTarget && activeTarget->kit()) + return activeTarget->kit()->hasValue("McuSupport.McuTargetKitVersion"); + + // QmlBuildSystem + auto [project, target, qmlBuildSystem] = activeProjectEntries(); + return qmlBuildSystem && qmlBuildSystem->qtForMCUs(); +} + QString ExternalDependencies::qtQuickVersion() const { auto [project, target, qmlBuildSystem] = activeProjectEntries(); diff --git a/src/plugins/qmldesigner/qmldesignerexternaldependencies.h b/src/plugins/qmldesigner/qmldesignerexternaldependencies.h index 13317075027..b4908c23833 100644 --- a/src/plugins/qmldesigner/qmldesignerexternaldependencies.h +++ b/src/plugins/qmldesigner/qmldesignerexternaldependencies.h @@ -37,6 +37,7 @@ public: QStringList modulePaths() const override; QStringList projectModulePaths() const override; bool isQt6Project() const override; + bool isQtForMcusProject() const override; QString qtQuickVersion() const override; Utils::FilePath resourcePath(const QString &relativePath) const override; diff --git a/tests/auto/qml/qmldesigner/coretests/tst_testcore.cpp b/tests/auto/qml/qmldesigner/coretests/tst_testcore.cpp index 92ff4f9b2b2..9be03d22612 100644 --- a/tests/auto/qml/qmldesigner/coretests/tst_testcore.cpp +++ b/tests/auto/qml/qmldesigner/coretests/tst_testcore.cpp @@ -163,6 +163,7 @@ public: QStringList modulePaths() const override { return {}; } QStringList projectModulePaths() const override { return {}; } bool isQt6Project() const override { return {}; } + bool isQtForMcusProject() const override { return {}; } QString qtQuickVersion() const override { return {}; } Utils::FilePath resourcePath(const QString &) const override { return {}; } diff --git a/tests/unit/tests/mocks/externaldependenciesmock.h b/tests/unit/tests/mocks/externaldependenciesmock.h index 673363a2148..c4cfe6cd3b5 100644 --- a/tests/unit/tests/mocks/externaldependenciesmock.h +++ b/tests/unit/tests/mocks/externaldependenciesmock.h @@ -37,6 +37,7 @@ public: MOCK_METHOD(QStringList, modulePaths, (), (const, override)); MOCK_METHOD(QStringList, projectModulePaths, (), (const, override)); MOCK_METHOD(bool, isQt6Project, (), (const, override)); + MOCK_METHOD(bool, isQtForMcusProject, (), (const, override)); MOCK_METHOD(QString, qtQuickVersion, (), (const, override)); MOCK_METHOD(Utils::FilePath, resourcePath, (const QString &relativePath), (const, override)); }; From 6276658e523207117f0f634c663a7184e6aa60ac Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Tue, 14 Nov 2023 11:43:13 +0200 Subject: [PATCH 223/242] EffectMaker: Update QSB tool parameters With updated parameters, same shader versions will be generated as with the standalone effect maker tool. Task-number: QDS-11194 Change-Id: Id7c0b36e7526a8e285d02b9102a65aac954b3d98 Reviewed-by: Amr Elsayed Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Mahmoud Badri --- src/plugins/effectmakernew/effectmakermodel.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/plugins/effectmakernew/effectmakermodel.cpp b/src/plugins/effectmakernew/effectmakermodel.cpp index c388e38b42b..fa11f2fb3c7 100644 --- a/src/plugins/effectmakernew/effectmakermodel.cpp +++ b/src/plugins/effectmakernew/effectmakermodel.cpp @@ -1200,7 +1200,8 @@ void EffectMakerModel::bakeShaders() const QStringList outPaths = {m_vertexShaderFilename, m_fragmentShaderFilename}; for (int i = 0; i < 2; ++i) { const auto workDir = Utils::FilePath::fromString(outPaths[i]); - QStringList args = {"-s", "--glsl", "300es,120,150,440", "--hlsl", "50", "--msl", "12"}; + // TODO: Optional legacy glsl support like standalone effect maker needs to add "100es,120" + QStringList args = {"-s", "--glsl", "300es,140,330,410", "--hlsl", "50", "--msl", "12"}; args << "-o" << outPaths[i] << srcPaths[i]; auto qsbProcess = new Utils::Process(this); From 820b1ce2d0d4530ab47911f900fddf583a82a77a Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Tue, 14 Nov 2023 13:57:14 +0200 Subject: [PATCH 224/242] QmlDesigner: Remove collection editor's id role It is not needed, and implemented wrongly anyway. Change-Id: I17a1565912c1d3f68f273e314b58b1998aced03d Reviewed-by: Miikka Heikkinen Reviewed-by: Shrief Gabr --- .../collectionsourcemodel.cpp | 25 +------------------ .../collectioneditor/collectionsourcemodel.h | 4 +-- .../collectioneditor/collectionview.cpp | 2 -- 3 files changed, 2 insertions(+), 29 deletions(-) diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.cpp index d79ea8acf87..74cf8d41b43 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.cpp @@ -83,8 +83,6 @@ QVariant CollectionSourceModel::data(const QModelIndex &index, int role) const const ModelNode *collectionSource = &m_collectionSources.at(index.row()); switch (role) { - case IdRole: - return collectionSource->id(); case NameRole: return collectionSource->variantProperty("objectName").value(); case NodeRole: @@ -109,20 +107,6 @@ bool CollectionSourceModel::setData(const QModelIndex &index, const QVariant &va ModelNode collectionSource = m_collectionSources.at(index.row()); switch (role) { - case IdRole: { - if (collectionSource.id() == value) - return false; - - bool duplicatedId = Utils::anyOf(std::as_const(m_collectionSources), - [&collectionSource, &value](const ModelNode &otherCollection) { - return (otherCollection.id() == value - && otherCollection != collectionSource); - }); - if (duplicatedId) - return false; - - collectionSource.setIdWithRefactoring(value.toString()); - } break; case Qt::DisplayRole: case NameRole: { auto collectionName = collectionSource.variantProperty("objectName"); @@ -197,8 +181,7 @@ QHash CollectionSourceModel::roleNames() const static QHash roles; if (roles.isEmpty()) { roles.insert(Super::roleNames()); - roles.insert({{IdRole, "sourceId"}, - {NameRole, "sourceName"}, + roles.insert({{NameRole, "sourceName"}, {NodeRole, "sourceNode"}, {CollectionTypeRole, "sourceCollectionType"}, {SelectedRole, "sourceIsSelected"}, @@ -412,12 +395,6 @@ void CollectionSourceModel::updateNodeSource(const ModelNode &node) updateCollectionList(index); } -void CollectionSourceModel::updateNodeId(const ModelNode &node) -{ - QModelIndex index = indexOfNode(node); - emit dataChanged(index, index, {IdRole}); -} - QString CollectionSourceModel::selectedSourceAddress() const { return index(m_selectedIndex).data(SourceRole).toString(); diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.h b/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.h index ce9a6ed7007..39c82ec5b31 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.h +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.h @@ -23,8 +23,7 @@ class CollectionSourceModel : public QAbstractListModel public: enum Roles { - IdRole = Qt::UserRole + 1, - NameRole, + NameRole = Qt::UserRole + 1, NodeRole, CollectionTypeRole, SourceRole, @@ -67,7 +66,6 @@ public: void updateNodeName(const ModelNode &node); void updateNodeSource(const ModelNode &node); - void updateNodeId(const ModelNode &node); Q_INVOKABLE QString selectedSourceAddress() const; diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp index 327e1ed89df..e3c8d26519c 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp @@ -112,8 +112,6 @@ void CollectionView::variantPropertiesChanged(const QList &prop m_widget->sourceModel()->updateNodeName(node); else if (property.name() == CollectionEditor::SOURCEFILE_PROPERTY) m_widget->sourceModel()->updateNodeSource(node); - else if (property.name() == "id") - m_widget->sourceModel()->updateNodeId(node); } } } From b080a3772c3f913d72f80be431fb0cdf48d42447 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sivert=20Kr=C3=B8vel?= Date: Wed, 8 Nov 2023 14:16:23 +0100 Subject: [PATCH 225/242] Handle nodes and properties required by MCUs in JSON conversion Qt for MCUs uses several features in the qmlproject files which are unknown to QDS. It is important that this information does not get lost when converting to and from the internal JSON project format, to avoid breaking MCU projects. The following changes were made: - Files nodes keep the type (ImageFiles, QmlFiles...) - Added support for more Files node types used by MCU projects (ModuleFiles, InterfaceFiles, FontFiles...) - Files nodes can have child properties - Added a JSON object to store properties unknown to QDS. They may be used by Qt for MCUs, and new properties may be added between versions - Added support for the MCU.Config and MCU.Module nodes - Added a test project for MCU. This project is also tested with Qt for MCUs. Both the original and the converted project build correctly - Added instructions for notifying the MCU team before modifying the MCU test cases to avoid breaking changes. Fixes: QDS-10774 Task-number: QDS-10969 Change-Id: I0dfd7f3b150a8661fc0398a8a3d575c7e8777ef3 Reviewed-by: Burak Hancerli Reviewed-by: Yasser Grimes Reviewed-by: Thomas Hartmann Reviewed-by: Qt CI Patch Build Bot --- .../buildsystem/projectitem/converters.cpp | 257 ++++++++++++++---- .../projectitem/qmlprojectitem.cpp | 2 +- .../qmlprojectmanager/converters-test.cpp | 4 +- .../qmlprojectmanager/data/README.md | 31 +++ .../converter/test-set-1/testfile.jsontoqml | 34 +-- .../converter/test-set-1/testfile.qmltojson | 119 ++++++-- .../converter/test-set-2/testfile.jsontoqml | 24 +- .../converter/test-set-2/testfile.qmltojson | 53 +++- .../converter/test-set-3/testfile.jsontoqml | 34 +-- .../converter/test-set-3/testfile.qmltojson | 119 ++++++-- .../test-set-mcu-1/testfile.jsontoqml | 111 ++++++++ .../test-set-mcu-1/testfile.qmlproject | 156 +++++++++++ .../test-set-mcu-1/testfile.qmltojson | 236 ++++++++++++++++ .../test-set-mcu-2/testfile.jsontoqml | 32 +++ .../test-set-mcu-2/testfile.qmlproject | 29 ++ .../test-set-mcu-2/testfile.qmltojson | 56 ++++ 16 files changed, 1119 insertions(+), 178 deletions(-) create mode 100644 tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-mcu-1/testfile.jsontoqml create mode 100644 tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-mcu-1/testfile.qmlproject create mode 100644 tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-mcu-1/testfile.qmltojson create mode 100644 tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-mcu-2/testfile.jsontoqml create mode 100644 tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-mcu-2/testfile.qmlproject create mode 100644 tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-mcu-2/testfile.qmltojson diff --git a/src/plugins/qmlprojectmanager/buildsystem/projectitem/converters.cpp b/src/plugins/qmlprojectmanager/buildsystem/projectitem/converters.cpp index 89abb79fcdf..b35679ed348 100644 --- a/src/plugins/qmlprojectmanager/buildsystem/projectitem/converters.cpp +++ b/src/plugins/qmlprojectmanager/buildsystem/projectitem/converters.cpp @@ -3,10 +3,21 @@ #include "converters.h" -#include +#include namespace QmlProjectManager::Converters { +const static QStringList qmlFilesFilter{QStringLiteral("*.qml")}; +const static QStringList javaScriptFilesFilter{QStringLiteral("*.js"), QStringLiteral("*.ts")}; +const static QStringList imageFilesFilter{QStringLiteral("*.jpeg"), + QStringLiteral("*.jpg"), + QStringLiteral("*.png"), + QStringLiteral("*.svg"), + QStringLiteral("*.hdr"), + QStringLiteral("*.ktx")}; + +QString jsonValueToString(const QJsonValue &val, int indentationLevel, bool indented); + QString jsonToQmlProject(const QJsonObject &rootObject) { QString qmlProjectString; @@ -19,6 +30,11 @@ QString jsonToQmlProject(const QJsonObject &rootObject) QJsonObject environmentConfig = rootObject["environment"].toObject(); QJsonObject deploymentConfig = rootObject["deployment"].toObject(); QJsonArray filesConfig = rootObject["fileGroups"].toArray(); + QJsonObject otherProperties = rootObject["otherProperties"].toObject(); + + QJsonObject mcuObject = rootObject["mcu"].toObject(); + QJsonObject mcuConfig = mcuObject["config"].toObject(); + QJsonObject mcuModule = mcuObject["module"].toObject(); int indentationLevel = 0; @@ -35,6 +51,8 @@ QString jsonToQmlProject(const QJsonObject &rootObject) }; auto appendString = [&appendItem](const QString &key, const QString &val) { + if (val.isEmpty()) + return; appendItem(key, val, true); }; @@ -42,7 +60,9 @@ QString jsonToQmlProject(const QJsonObject &rootObject) appendItem(key, QString::fromStdString(val ? "true" : "false"), false); }; - auto appendArray = [&appendItem](const QString &key, const QStringList &vals) { + auto appendStringArray = [&appendItem](const QString &key, const QStringList &vals) { + if (vals.isEmpty()) + return; QString finalString; for (const QString &value : vals) finalString.append("\"").append(value).append("\"").append(","); @@ -52,6 +72,27 @@ QString jsonToQmlProject(const QJsonObject &rootObject) appendItem(key, finalString, false); }; + auto appendJsonArray = [&appendItem, &indentationLevel](const QString &key, + const QJsonArray &vals) { + if (vals.isEmpty()) + return; + appendItem(key, jsonValueToString(vals, indentationLevel, /*indented*/ true), false); + }; + + auto appendProperties = [&appendItem, &indentationLevel](const QJsonObject &props, + const QString &prefix) { + for (const auto &key : props.keys()) { + QJsonValue val = props[key]; + QString keyWithPrefix = key; + if (!prefix.isEmpty()) { + keyWithPrefix.prepend(prefix + "."); + } + appendItem(keyWithPrefix, + jsonValueToString(val, indentationLevel, /*indented*/ false), + false); + } + }; + auto startObject = [&ts, &indentationLevel](const QString &objectName) { ts << Qt::endl << QString(" ").repeated(indentationLevel * 4) << objectName << " {" << Qt::endl; @@ -63,22 +104,32 @@ QString jsonToQmlProject(const QJsonObject &rootObject) ts << QString(" ").repeated(indentationLevel * 4) << "}" << Qt::endl; }; - auto appendFileGroup = - [&startObject, &endObject, &appendString, &appendArray](const QJsonObject &fileGroup, - const QString &qmlKey) { - startObject(qmlKey); - appendString("directory", fileGroup["directory"].toString()); - appendString("filter", fileGroup["filters"].toVariant().toStringList().join(";")); - appendArray("files", fileGroup["files"].toVariant().toStringList()); - endObject(); - }; - - auto appendQmlFileGroup = - [&startObject, &endObject, &appendString](const QJsonObject &fileGroup) { - startObject("QmlFiles"); - appendString("directory", fileGroup["directory"].toString()); - endObject(); - }; + auto appendFileGroup = [&startObject, + &endObject, + &appendString, + &appendProperties, + &appendJsonArray](const QJsonObject &fileGroup, + const QString &nodeName) { + startObject(nodeName); + appendString("directory", fileGroup["directory"].toString()); + QStringList filters = fileGroup["filters"].toVariant().toStringList(); + QStringList filter = {}; + if (nodeName.toLower() == "qmlfiles") { + filter = qmlFilesFilter; + } else if (nodeName.toLower() == "imagefiles") { + filter = imageFilesFilter; + } else if (nodeName.toLower() == "javascriptfiles") { + filter = javaScriptFilesFilter; + } + for (const QString &entry : filter) { + filters.removeOne(entry); + } + appendString("filter", filters.join(";")); + appendJsonArray("files", fileGroup["files"].toArray()); + appendProperties(fileGroup["mcuProperties"].toObject(), "MCU"); + appendProperties(fileGroup["otherProperties"].toObject(), ""); + endObject(); + }; // start creating the file content appendComment("prop: json-converted"); @@ -93,24 +144,37 @@ QString jsonToQmlProject(const QJsonObject &rootObject) appendString("mainUiFile", runConfig["mainUiFile"].toString()); appendString("targetDirectory", deploymentConfig["targetDirectory"].toString()); appendBool("widgetApp", runConfig["widgetApp"].toBool()); - appendArray("importPaths", rootObject["importPaths"].toVariant().toStringList()); + appendStringArray("importPaths", rootObject["importPaths"].toVariant().toStringList()); appendBreak(); appendString("qdsVersion", versionConfig["designStudio"].toString()); appendString("quickVersion", versionConfig["qtQuick"].toString()); appendBool("qt6Project", versionConfig["qt"].toString() == "6"); - appendBool("qtForMCUs", !(rootObject["mcuConfig"].toObject().isEmpty())); - appendBreak(); - appendBool("multilanguageSupport", languageConfig["multiLanguageSupport"].toBool()); - appendString("primaryLanguage", languageConfig["primaryLanguage"].toString()); - appendArray("supportedLanguages", - languageConfig["supportedLanguages"].toVariant().toStringList()); + appendBool("qtForMCUs", + mcuObject["enabled"].toBool() || !mcuConfig.isEmpty() || !mcuModule.isEmpty()); + if (!languageConfig.isEmpty()) { + appendBreak(); + appendBool("multilanguageSupport", languageConfig["multiLanguageSupport"].toBool()); + appendString("primaryLanguage", languageConfig["primaryLanguage"].toString()); + appendStringArray("supportedLanguages", + languageConfig["supportedLanguages"].toVariant().toStringList()); + } + + // Since different versions of Qt for MCUs may introduce new properties, we collect all + // unknown properties in a separate object. + // We need to avoid losing content regardless of which QDS/QUL version combo is used. + if (!otherProperties.isEmpty()) { + appendBreak(); + appendProperties(otherProperties, ""); + } // append Environment object - startObject("Environment"); - for (const QString &key : environmentConfig.keys()) - appendItem(key, environmentConfig[key].toString(), true); - - endObject(); + if (!environmentConfig.isEmpty()) { + startObject("Environment"); + for (const QString &key : environmentConfig.keys()) { + appendItem(key, environmentConfig[key].toString(), true); + } + endObject(); + } // append ShaderTool object if (!shaderConfig["args"].toVariant().toStringList().isEmpty()) { @@ -118,16 +182,35 @@ QString jsonToQmlProject(const QJsonObject &rootObject) appendString("args", shaderConfig["args"].toVariant().toStringList().join(" ").replace("\"", "\\\"")); - appendArray("files", shaderConfig["files"].toVariant().toStringList()); + appendStringArray("files", shaderConfig["files"].toVariant().toStringList()); + endObject(); + } + + // append the MCU.Config object + if (!mcuConfig.isEmpty()) { + // Append MCU.Config + startObject("MCU.Config"); + appendProperties(mcuConfig, ""); + endObject(); + } + + // Append the MCU.Module object + if (!mcuModule.isEmpty()) { + // Append MCU.Module + startObject("MCU.Module"); + appendProperties(mcuModule, ""); endObject(); } // append files objects for (const QJsonValue &fileGroup : filesConfig) { - if (fileGroup["filters"].toArray().contains("*.qml")) - appendQmlFileGroup(fileGroup.toObject()); - else - appendFileGroup(fileGroup.toObject(), "Files"); + QString nodeType = QString("%1Files").arg(fileGroup["type"].toString()); + if (fileGroup["type"].toString().isEmpty() + && fileGroup["filters"].toArray().contains("*.qml")) { + // TODO: IS this important? It turns Files node with *.qml in the filters into QmlFiles nodes + nodeType = "QmlFiles"; + } + appendFileGroup(fileGroup.toObject(), nodeType); } endObject(); // Closing 'Project' @@ -169,7 +252,12 @@ QJsonObject qmlProjectTojson(const Utils::FilePath &projectFile) QJsonObject runConfigObject; QJsonObject deploymentObject; QJsonObject mcuObject; + QJsonObject mcuConfigObject; + QJsonObject mcuModuleObject; QJsonObject shaderToolObject; + QJsonObject otherProperties; + + bool qtForMCUs = false; // convert the the non-object props for (const QString &propName : rootNode->propertyNames()) { @@ -177,10 +265,7 @@ QJsonObject qmlProjectTojson(const Utils::FilePath &projectFile) QString objKey = QString(propName).remove("QDS.", Qt::CaseInsensitive); QJsonValue value = rootNode->property(propName).value.toJsonValue(); - if (propName.startsWith("mcu.", Qt::CaseInsensitive)) { - currentObj = &mcuObject; - objKey = QString(propName).remove("MCU."); - } else if (propName.contains("language", Qt::CaseInsensitive)) { + if (propName.contains("language", Qt::CaseInsensitive)) { currentObj = &languageObject; if (propName.contains("multilanguagesupport", Qt::CaseInsensitive)) // fixing the camelcase @@ -200,12 +285,17 @@ QJsonObject qmlProjectTojson(const Utils::FilePath &projectFile) } else if (propName.contains("targetdirectory", Qt::CaseInsensitive)) { currentObj = &deploymentObject; } else if (propName.contains("qtformcus", Qt::CaseInsensitive)) { - currentObj = &mcuObject; - objKey = "mcuEnabled"; + qtForMCUs = value.toBool(); + continue; } else if (propName.contains("qt6project", Qt::CaseInsensitive)) { currentObj = &versionObject; objKey = "qt"; value = rootNode->property(propName).value.toBool() ? "6" : "5"; + } else if (propName.contains("importpaths", Qt::CaseInsensitive)) { + objKey = "importPaths"; + } else { + currentObj = &otherProperties; + objKey = propName; // With prefix } currentObj->insert(objKey, value); @@ -220,14 +310,20 @@ QJsonObject qmlProjectTojson(const Utils::FilePath &projectFile) versionObject.insert("qt", "5"); } + rootObject.insert("otherProperties", otherProperties); + // convert the the object props for (const QmlJS::SimpleReaderNode::Ptr &childNode : rootNode->children()) { if (childNode->name().contains("files", Qt::CaseInsensitive)) { - const QString childNodeName = childNode->name().toLower().remove("qds."); + QString childNodeName = childNode->name().remove("qds.", Qt::CaseInsensitive); + qsizetype filesPos = childNodeName.indexOf("files", 0, Qt::CaseInsensitive); + const QString childNodeType = childNodeName.first(filesPos); + childNodeName = childNodeName.toLower(); + QJsonArray childNodeFiles = childNode->property("files").value.toJsonArray(); QString childNodeDirectory = childNode->property("directory").value.toString(); - QStringList filters = childNode->property("filter").value.toString().split(";"); - filters.removeAll(""); + QStringList filters + = childNode->property("filter").value.toString().split(";", Qt::SkipEmptyParts); QJsonArray childNodeFilters = QJsonArray::fromStringList(filters); // files have priority over filters @@ -239,25 +335,22 @@ QJsonObject qmlProjectTojson(const Utils::FilePath &projectFile) std::for_each(filterSource.begin(), filterSource.end(), [&childNodeFilters](const auto &value) { - childNodeFilters << value; + if (!childNodeFilters.contains(value)) { + childNodeFilters << value; + } }); }; // Those 3 file groups are the special ones - // that have a default set of filters. After the first - // conversion (QmlProject -> JSON) they are converted to - // the generic file group format ('Files' or 'QDS.Files') + // that have a default set of filters. + // The default filters are written to the + // qmlproject file after conversion if (childNodeName == "qmlfiles") { - inserter({QStringLiteral("*.qml")}); + inserter(qmlFilesFilter); } else if (childNodeName == "javascriptfiles") { - inserter({QStringLiteral("*.js"), QStringLiteral("*.ts")}); + inserter(javaScriptFilesFilter); } else if (childNodeName == "imagefiles") { - inserter({QStringLiteral("*.jpeg"), - QStringLiteral("*.jpg"), - QStringLiteral("*.png"), - QStringLiteral("*.svg"), - QStringLiteral("*.hdr"), - QStringLiteral(".ktx")}); + inserter(imageFilesFilter); } } @@ -266,6 +359,26 @@ QJsonObject qmlProjectTojson(const Utils::FilePath &projectFile) targetObject.insert("directory", childNodeDirectory); targetObject.insert("filters", childNodeFilters); targetObject.insert("files", childNodeFiles); + targetObject.insert("type", childNodeType); + + QJsonObject mcuPropertiesObject; + QJsonObject otherPropertiesObject; + for (const auto &propName : childNode->propertyNames()) { + if (propName == "directory" || propName == "filter" || propName == "files") { + continue; + } + + auto val = QJsonValue::fromVariant(childNode->property(propName).value); + + if (propName.startsWith("MCU.", Qt::CaseInsensitive)) { + mcuPropertiesObject.insert(propName.mid(4), val); + } else { + otherPropertiesObject.insert(propName, val); + } + } + + targetObject.insert("mcuProperties", mcuPropertiesObject); + targetObject.insert("otherProperties", otherPropertiesObject); fileGroupsObject.append(targetObject); } else if (childNode->name().contains("shadertool", Qt::CaseInsensitive)) { @@ -283,21 +396,51 @@ QJsonObject qmlProjectTojson(const Utils::FilePath &projectFile) shaderToolObject.insert("args", QJsonArray::fromStringList(args)); shaderToolObject.insert("files", childNode->property("files").value.toJsonValue()); + } else if (childNode->name().contains("config", Qt::CaseInsensitive)) { + mcuConfigObject = nodeToJsonObject(childNode); + } else if (childNode->name().contains("module", Qt::CaseInsensitive)) { + mcuModuleObject = nodeToJsonObject(childNode); } else { rootObject.insert(toCamelCase(childNode->name().remove("qds.", Qt::CaseInsensitive)), nodeToJsonObject(childNode)); } } + mcuObject.insert("config", mcuConfigObject); + mcuObject.insert("module", mcuModuleObject); + qtForMCUs |= !(mcuModuleObject.isEmpty() && mcuConfigObject.isEmpty()); + mcuObject.insert("enabled", qtForMCUs); + rootObject.insert("fileGroups", fileGroupsObject); rootObject.insert("language", languageObject); rootObject.insert("versions", versionObject); rootObject.insert("runConfig", runConfigObject); rootObject.insert("deployment", deploymentObject); - rootObject.insert("mcuConfig", mcuObject); + rootObject.insert("mcu", mcuObject); if (!shaderToolObject.isEmpty()) rootObject.insert("shaderTool", shaderToolObject); rootObject.insert("fileVersion", 1); return rootObject; } + +QString jsonValueToString(const QJsonValue &val, int indentationLevel, bool indented) +{ + if (val.isArray()) { + auto jsonFormat = indented ? QJsonDocument::JsonFormat::Indented + : QJsonDocument::JsonFormat::Compact; + QString str = QString::fromUtf8((QJsonDocument(val.toArray()).toJson(jsonFormat))); + if (indented) { + // Strip trailing newline + str.chop(1); + } + return str.replace("\n", QString(" ").repeated(indentationLevel * 4).prepend("\n")); + } else if (val.isBool()) { + return val.toBool() ? QString("true") : QString("false"); + } else if (val.isDouble()) { + return QString("%1").arg(val.toDouble()); + } else { + return val.toString().prepend("\"").append("\""); + } +} + } // namespace QmlProjectManager::Converters diff --git a/src/plugins/qmlprojectmanager/buildsystem/projectitem/qmlprojectitem.cpp b/src/plugins/qmlprojectmanager/buildsystem/projectitem/qmlprojectitem.cpp index c80f13e9b2b..77d0eea82d5 100644 --- a/src/plugins/qmlprojectmanager/buildsystem/projectitem/qmlprojectitem.cpp +++ b/src/plugins/qmlprojectmanager/buildsystem/projectitem/qmlprojectitem.cpp @@ -130,7 +130,7 @@ QString QmlProjectItem::targetDirectory() const bool QmlProjectItem::isQt4McuProject() const { - return m_project["mcuConfig"].toObject()["mcuEnabled"].toBool(); + return m_project["mcu"].toObject()["enabled"].toBool(); } Utils::EnvironmentItems QmlProjectItem::environment() const diff --git a/tests/unit/tests/unittests/qmlprojectmanager/converters-test.cpp b/tests/unit/tests/unittests/qmlprojectmanager/converters-test.cpp index a1ad263cf7c..adef15462a7 100644 --- a/tests/unit/tests/unittests/qmlprojectmanager/converters-test.cpp +++ b/tests/unit/tests/unittests/qmlprojectmanager/converters-test.cpp @@ -67,7 +67,9 @@ INSTANTIATE_TEST_SUITE_P(QmlProjectItem, QmlProjectConverter, ::testing::Values(QString("test-set-1"), QString("test-set-2"), - QString("test-set-3"))); + QString("test-set-3"), + QString("test-set-mcu-1"), + QString("test-set-mcu-2"))); TEST_P(QmlProjectConverter, qml_project_to_json) { diff --git a/tests/unit/tests/unittests/qmlprojectmanager/data/README.md b/tests/unit/tests/unittests/qmlprojectmanager/data/README.md index bffd46b79ee..c73ef285904 100644 --- a/tests/unit/tests/unittests/qmlprojectmanager/data/README.md +++ b/tests/unit/tests/unittests/qmlprojectmanager/data/README.md @@ -30,6 +30,37 @@ Test functions iterate over the "test-set-*" folders and run the tests by using * **purpose**: testing `QDS.` prefixes * **origin**: copy of test-set-1 +### Qt for MCUs test sets + +These tests define the requirements for Qt for MCUs (QUL) projects. They are maintained by the Qt for MCUs +team. Please do not make changes to these test sets before consulting them. These tests help make sure +that QDS and QUL are aligned on the qmlproject format and that new features in QDS does not break +qmlproject files for MCU projects. The test set will be tested in the Qt for MCUs repositories +to make sure both the original and the converted qmlprojects build correctly. + +The qmlproject files in the test set aim to cover all the possible contents of a Qt for MCUs qmlproject, +but since new features are added with every release, it is not guaranteed to be exhaustive. + +Some main points for qmlproject files for MCU projects: + +* Unknown content in the qmlproject file will cause errors in the QUL tooling +* Any node or property with the `QDS`-prefix is ignored by QUL. When adding new properties, + these must either be prefixed or communicated with the MCU team to whitelist them in the tooling. +* Plain `Files` nodes are ignored by QUL, it needs to know the node type. The node contents will be processed + by different tools depending on which type of files it includes. The node types used by QUL are: + + `QmlFiles` + + `ImageFiles` + + `FontFiles` + + `ModuleFiles` + + `InterfaceFiles` + + `TranslationFiles` +* In addition to adding files to the project, any of the `*Files` nodes listed can also contain properties to + apply to the file group added by the node. +* MCU projects may have MCU-specific configuration nodes: `MCU.Config` and `MCU.Module`. +* A new version of Qt for MCUs may add new properties to any node, including `Project`. For this reason + it is not possible to define an exact set of properties to support which is valid across all versions. +* Qt for MCUs developers must still edit and maintain qmlproject files manually. The converted format + of the qmlproject file should be easy to read and edit by hand ## File Filters test data diff --git a/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-1/testfile.jsontoqml b/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-1/testfile.jsontoqml index 4db64285c7e..2a67b12d505 100644 --- a/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-1/testfile.jsontoqml +++ b/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-1/testfile.jsontoqml @@ -40,76 +40,56 @@ Project { directory: "imports" } - Files { + JavaScriptFiles { directory: "content" - filter: "*.js;*.ts" - files: [ ] } - Files { + JavaScriptFiles { directory: "imports" - filter: "*.js;*.ts" - files: [ ] } - Files { + ImageFiles { directory: "content" - filter: "*.jpeg;*.jpg;*.png;*.svg;*.hdr;.ktx" - files: [ ] } - Files { + ImageFiles { directory: "asset_imports" - filter: "*.jpeg;*.jpg;*.png;*.svg;*.hdr;.ktx" - files: [ ] } Files { - directory: "" - filter: "" - files: [ "qtquickcontrols2.conf" ] + files: [ + "qtquickcontrols2.conf" + ] } Files { - directory: "" filter: "*.conf" - files: [ ] } Files { directory: "." filter: "qmldir" - files: [ ] } Files { - directory: "" filter: "*.ttf;*.otf;*.ctf" - files: [ ] } Files { - directory: "" filter: "*.wav;*.mp3" - files: [ ] } Files { - directory: "" filter: "*.mp4" - files: [ ] } Files { - directory: "" filter: "*.glsl;*.glslv;*.glslf;*.vsh;*.fsh;*.vert;*.frag;*.trag" - files: [ ] } Files { directory: "asset_imports" filter: "*.mesh" - files: [ ] } QmlFiles { diff --git a/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-1/testfile.qmltojson b/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-1/testfile.qmltojson index 4b039c2e1ed..c4475af39c4 100644 --- a/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-1/testfile.qmltojson +++ b/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-1/testfile.qmltojson @@ -16,7 +16,12 @@ ], "filters": [ "*.qml" - ] + ], + "mcuProperties": { + }, + "otherProperties": { + }, + "type": "Qml" }, { "directory": "imports", @@ -24,7 +29,12 @@ ], "filters": [ "*.qml" - ] + ], + "mcuProperties": { + }, + "otherProperties": { + }, + "type": "Qml" }, { "directory": "content", @@ -33,7 +43,12 @@ "filters": [ "*.js", "*.ts" - ] + ], + "mcuProperties": { + }, + "otherProperties": { + }, + "type": "JavaScript" }, { "directory": "imports", @@ -42,7 +57,12 @@ "filters": [ "*.js", "*.ts" - ] + ], + "mcuProperties": { + }, + "otherProperties": { + }, + "type": "JavaScript" }, { "directory": "content", @@ -54,8 +74,13 @@ "*.png", "*.svg", "*.hdr", - ".ktx" - ] + "*.ktx" + ], + "mcuProperties": { + }, + "otherProperties": { + }, + "type": "Image" }, { "directory": "asset_imports", @@ -67,8 +92,13 @@ "*.png", "*.svg", "*.hdr", - ".ktx" - ] + "*.ktx" + ], + "mcuProperties": { + }, + "otherProperties": { + }, + "type": "Image" }, { "directory": "", @@ -76,7 +106,12 @@ "qtquickcontrols2.conf" ], "filters": [ - ] + ], + "mcuProperties": { + }, + "otherProperties": { + }, + "type": "" }, { "directory": "", @@ -84,7 +119,12 @@ ], "filters": [ "*.conf" - ] + ], + "mcuProperties": { + }, + "otherProperties": { + }, + "type": "" }, { "directory": ".", @@ -92,7 +132,12 @@ ], "filters": [ "qmldir" - ] + ], + "mcuProperties": { + }, + "otherProperties": { + }, + "type": "" }, { "directory": "", @@ -102,7 +147,12 @@ "*.ttf", "*.otf", "*.ctf" - ] + ], + "mcuProperties": { + }, + "otherProperties": { + }, + "type": "" }, { "directory": "", @@ -111,7 +161,12 @@ "filters": [ "*.wav", "*.mp3" - ] + ], + "mcuProperties": { + }, + "otherProperties": { + }, + "type": "" }, { "directory": "", @@ -119,7 +174,12 @@ ], "filters": [ "*.mp4" - ] + ], + "mcuProperties": { + }, + "otherProperties": { + }, + "type": "" }, { "directory": "", @@ -134,7 +194,12 @@ "*.vert", "*.frag", "*.trag" - ] + ], + "mcuProperties": { + }, + "otherProperties": { + }, + "type": "" }, { "directory": "asset_imports", @@ -142,7 +207,12 @@ ], "filters": [ "*.mesh" - ] + ], + "mcuProperties": { + }, + "otherProperties": { + }, + "type": "" }, { "directory": "asset_imports", @@ -150,7 +220,12 @@ ], "filters": [ "*.qml" - ] + ], + "mcuProperties": { + }, + "otherProperties": { + }, + "type": "" } ], "fileVersion": 1, @@ -165,8 +240,14 @@ "en" ] }, - "mcuConfig": { - "mcuEnabled": true + "mcu": { + "config": { + }, + "enabled": true, + "module": { + } + }, + "otherProperties": { }, "runConfig": { "fileSelectors": [ diff --git a/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-2/testfile.jsontoqml b/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-2/testfile.jsontoqml index 484a425b91c..5878bdafc73 100644 --- a/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-2/testfile.jsontoqml +++ b/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-2/testfile.jsontoqml @@ -5,20 +5,13 @@ import QmlProject Project { mainFile: "fileSelectors.qml" - mainUiFile: "" targetDirectory: "/opt/fileSelectors" widgetApp: false importPaths: [ "imports" ] - qdsVersion: "" - quickVersion: "" qt6Project: false qtForMCUs: false - multilanguageSupport: false - primaryLanguage: "" - supportedLanguages: [ ] - Environment { QT_AUTO_SCREEN_SCALE_FACTOR: "1" QT_QUICK_CONTROLS_CONF: "qtquickcontrols2.conf" @@ -28,33 +21,26 @@ Project { directory: "." } - Files { + JavaScriptFiles { directory: "." - filter: "*.js;*.ts" - files: [ ] } - Files { + ImageFiles { directory: "." - filter: "*.jpeg;*.jpg;*.png;*.svg;*.hdr;.ktx" - files: [ ] } Files { - directory: "" - filter: "" - files: [ "qtquickcontrols2.conf" ] + files: [ + "qtquickcontrols2.conf" + ] } Files { - directory: "" filter: "*.conf" - files: [ ] } Files { directory: "." filter: "qmldir" - files: [ ] } } diff --git a/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-2/testfile.qmltojson b/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-2/testfile.qmltojson index abe1b455e4b..a26e0fc1607 100644 --- a/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-2/testfile.qmltojson +++ b/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-2/testfile.qmltojson @@ -13,7 +13,12 @@ ], "filters": [ "*.qml" - ] + ], + "mcuProperties": { + }, + "otherProperties": { + }, + "type": "Qml" }, { "directory": ".", @@ -22,7 +27,12 @@ "filters": [ "*.js", "*.ts" - ] + ], + "mcuProperties": { + }, + "otherProperties": { + }, + "type": "JavaScript" }, { "directory": ".", @@ -34,8 +44,13 @@ "*.png", "*.svg", "*.hdr", - ".ktx" - ] + "*.ktx" + ], + "mcuProperties": { + }, + "otherProperties": { + }, + "type": "Image" }, { "directory": "", @@ -43,7 +58,12 @@ "qtquickcontrols2.conf" ], "filters": [ - ] + ], + "mcuProperties": { + }, + "otherProperties": { + }, + "type": "" }, { "directory": "", @@ -51,7 +71,12 @@ ], "filters": [ "*.conf" - ] + ], + "mcuProperties": { + }, + "otherProperties": { + }, + "type": "" }, { "directory": ".", @@ -59,7 +84,12 @@ ], "filters": [ "qmldir" - ] + ], + "mcuProperties": { + }, + "otherProperties": { + }, + "type": "" } ], "fileVersion": 1, @@ -68,7 +98,14 @@ ], "language": { }, - "mcuConfig": { + "mcu": { + "config": { + }, + "enabled": false, + "module": { + } + }, + "otherProperties": { }, "runConfig": { "fileSelectors": [ diff --git a/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-3/testfile.jsontoqml b/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-3/testfile.jsontoqml index 4db64285c7e..2a67b12d505 100644 --- a/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-3/testfile.jsontoqml +++ b/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-3/testfile.jsontoqml @@ -40,76 +40,56 @@ Project { directory: "imports" } - Files { + JavaScriptFiles { directory: "content" - filter: "*.js;*.ts" - files: [ ] } - Files { + JavaScriptFiles { directory: "imports" - filter: "*.js;*.ts" - files: [ ] } - Files { + ImageFiles { directory: "content" - filter: "*.jpeg;*.jpg;*.png;*.svg;*.hdr;.ktx" - files: [ ] } - Files { + ImageFiles { directory: "asset_imports" - filter: "*.jpeg;*.jpg;*.png;*.svg;*.hdr;.ktx" - files: [ ] } Files { - directory: "" - filter: "" - files: [ "qtquickcontrols2.conf" ] + files: [ + "qtquickcontrols2.conf" + ] } Files { - directory: "" filter: "*.conf" - files: [ ] } Files { directory: "." filter: "qmldir" - files: [ ] } Files { - directory: "" filter: "*.ttf;*.otf;*.ctf" - files: [ ] } Files { - directory: "" filter: "*.wav;*.mp3" - files: [ ] } Files { - directory: "" filter: "*.mp4" - files: [ ] } Files { - directory: "" filter: "*.glsl;*.glslv;*.glslf;*.vsh;*.fsh;*.vert;*.frag;*.trag" - files: [ ] } Files { directory: "asset_imports" filter: "*.mesh" - files: [ ] } QmlFiles { diff --git a/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-3/testfile.qmltojson b/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-3/testfile.qmltojson index 4b039c2e1ed..c4475af39c4 100644 --- a/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-3/testfile.qmltojson +++ b/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-3/testfile.qmltojson @@ -16,7 +16,12 @@ ], "filters": [ "*.qml" - ] + ], + "mcuProperties": { + }, + "otherProperties": { + }, + "type": "Qml" }, { "directory": "imports", @@ -24,7 +29,12 @@ ], "filters": [ "*.qml" - ] + ], + "mcuProperties": { + }, + "otherProperties": { + }, + "type": "Qml" }, { "directory": "content", @@ -33,7 +43,12 @@ "filters": [ "*.js", "*.ts" - ] + ], + "mcuProperties": { + }, + "otherProperties": { + }, + "type": "JavaScript" }, { "directory": "imports", @@ -42,7 +57,12 @@ "filters": [ "*.js", "*.ts" - ] + ], + "mcuProperties": { + }, + "otherProperties": { + }, + "type": "JavaScript" }, { "directory": "content", @@ -54,8 +74,13 @@ "*.png", "*.svg", "*.hdr", - ".ktx" - ] + "*.ktx" + ], + "mcuProperties": { + }, + "otherProperties": { + }, + "type": "Image" }, { "directory": "asset_imports", @@ -67,8 +92,13 @@ "*.png", "*.svg", "*.hdr", - ".ktx" - ] + "*.ktx" + ], + "mcuProperties": { + }, + "otherProperties": { + }, + "type": "Image" }, { "directory": "", @@ -76,7 +106,12 @@ "qtquickcontrols2.conf" ], "filters": [ - ] + ], + "mcuProperties": { + }, + "otherProperties": { + }, + "type": "" }, { "directory": "", @@ -84,7 +119,12 @@ ], "filters": [ "*.conf" - ] + ], + "mcuProperties": { + }, + "otherProperties": { + }, + "type": "" }, { "directory": ".", @@ -92,7 +132,12 @@ ], "filters": [ "qmldir" - ] + ], + "mcuProperties": { + }, + "otherProperties": { + }, + "type": "" }, { "directory": "", @@ -102,7 +147,12 @@ "*.ttf", "*.otf", "*.ctf" - ] + ], + "mcuProperties": { + }, + "otherProperties": { + }, + "type": "" }, { "directory": "", @@ -111,7 +161,12 @@ "filters": [ "*.wav", "*.mp3" - ] + ], + "mcuProperties": { + }, + "otherProperties": { + }, + "type": "" }, { "directory": "", @@ -119,7 +174,12 @@ ], "filters": [ "*.mp4" - ] + ], + "mcuProperties": { + }, + "otherProperties": { + }, + "type": "" }, { "directory": "", @@ -134,7 +194,12 @@ "*.vert", "*.frag", "*.trag" - ] + ], + "mcuProperties": { + }, + "otherProperties": { + }, + "type": "" }, { "directory": "asset_imports", @@ -142,7 +207,12 @@ ], "filters": [ "*.mesh" - ] + ], + "mcuProperties": { + }, + "otherProperties": { + }, + "type": "" }, { "directory": "asset_imports", @@ -150,7 +220,12 @@ ], "filters": [ "*.qml" - ] + ], + "mcuProperties": { + }, + "otherProperties": { + }, + "type": "" } ], "fileVersion": 1, @@ -165,8 +240,14 @@ "en" ] }, - "mcuConfig": { - "mcuEnabled": true + "mcu": { + "config": { + }, + "enabled": true, + "module": { + } + }, + "otherProperties": { }, "runConfig": { "fileSelectors": [ diff --git a/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-mcu-1/testfile.jsontoqml b/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-mcu-1/testfile.jsontoqml new file mode 100644 index 00000000000..99e4f60bb34 --- /dev/null +++ b/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-mcu-1/testfile.jsontoqml @@ -0,0 +1,111 @@ +// prop: json-converted +// prop: auto-generated + +import QmlProject + +Project { + mainFile: "Main.qml" + targetDirectory: "/opt/UntitledProject13" + widgetApp: true + importPaths: [ "imports","asset_imports" ] + + qdsVersion: "4.0" + quickVersion: "6.2" + qt6Project: false + qtForMCUs: true + + multilanguageSupport: true + primaryLanguage: "en" + supportedLanguages: [ "no" ] + + idBasedTranslations: true + projectRootPath: ".." + + Environment { + QML_COMPAT_RESOLVE_URLS_ON_ASSIGNMENT: "1" + QT_AUTO_SCREEN_SCALE_FACTOR: "1" + QT_ENABLE_HIGHDPI_SCALING: "0" + QT_LOGGING_RULES: "qt.qml.connections=false" + QT_QUICK_CONTROLS_CONF: "qtquickcontrols2.conf" + } + + ShaderTool { + args: "-s --glsl \"100 es,120,150\" --hlsl 50 --msl 12" + files: [ "content/shaders/*" ] + } + + MCU.Config { + defaultFontFamily: "Roboto" + fontEngine: "Spark" + maxResourceCacheSize: [[128,128],[16384,1]] + resourceCompression: true + } + + QmlFiles { + directory: "content" + MCU.copyQmlFiles: true + } + + QmlFiles { + files: [ + "qml/Header.qml", + "qml/Footer.qml" + ] + } + + ImageFiles { + directory: "assets" + } + + ImageFiles { + files: [ + "images/clock.png" + ] + MCU.resourceCompression: false + } + + ImageFiles { + files: [ + "images/weather.png", + "images/navigation.png" + ] + MCU.base: "images" + MCU.prefix: "assets" + } + + ModuleFiles { + files: [ + "qmlproject/module/module.qmlproject" + ] + MCU.qulModules: ["Controls","Timeline"] + } + + FontFiles { + files: [ + "fonts/RobotoFonts.fmp" + ] + } + + InterfaceFiles { + files: [ + "src/BoardInterface.h" + ] + } + + TranslationFiles { + files: [ + "additional_translations/qml_en.ts", + "i18n/qml_no.ts" + ] + MCU.omitSourceLanguage: true + } + + JavaScriptFiles { + directory: "scripts" + } + + Files { + directory: "more_files" + filter: "*.mp3" + } +} diff --git a/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-mcu-1/testfile.qmlproject b/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-mcu-1/testfile.qmlproject new file mode 100644 index 00000000000..1b5b10ec18b --- /dev/null +++ b/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-mcu-1/testfile.qmlproject @@ -0,0 +1,156 @@ +import QmlProject 1.3 + +// This file works with Qt for MCUs 2.6, with the testfile of set-set-mcu-2 used as the module. +// Do not make any changes to this or the expected output before communicating with the MCU team + +// The set of properties may not be exhaustive and may not match every supported Qt for MCUs version + +Project { +// These properties are used by Qt for MCUs +// They should never have the QDS-prefix + mainFile: "Main.qml" + supportedLanguages: ["no"] + primaryLanguage: "en" + importPaths: [ "imports", "asset_imports" ] + idBasedTranslations: true + projectRootPath: ".." +// END of common properties + +// The following properties are ignored by Qt for MCUs +// They can have the QDS-prefix, but do not need it + targetDirectory: "/opt/UntitledProject13" + qdsVersion: "4.0" + quickVersion: "6.2" + qtForMCUs: true + widgetApp: true + multilanguageSupport: true +// END of ignored properties + +// The following properties must have the QDS prefix as of Qt for MCUs 2.6 +// empty +// END of QDS prefixed properties + +// These nodes are used by Qt for MCUs as well as Design Studio +// Their object type and content must never change based on conversion +// Any child properties prefixed with MCU must remain unchanged + +// The nodes can have properties set on the file group. +// This test set does not contain an exhaustive set of the available properties + + QmlFiles { + directory: "content" + MCU.copyQmlFiles: true + } + + QmlFiles { + files: [ + "qml/Header.qml", + "qml/Footer.qml" + ] + } + + ImageFiles { + directory: "assets" + } + + ImageFiles { + files: [ + "images/clock.png" + ] + MCU.resourceCompression: false + } + + ImageFiles { + files: [ + "images/weather.png", + "images/navigation.png" + ] + MCU.prefix: "assets" + MCU.base: "images" + } + +// END common nodes + +// These nodes are used by Qt for MCUs but not Design Studio +// Their object type and content must never change based on conversion +// Any child properties prefixed with MCU must remain unchanged + ModuleFiles { + files: [ + "qmlproject/module/module.qmlproject" + ] + MCU.qulModules: [ + "Controls", + "Timeline" + ] + } + + FontFiles { + files: [ + "fonts/RobotoFonts.fmp" + ] + } + + InterfaceFiles { + files: [ + "src/BoardInterface.h" + ] + } + + TranslationFiles { + files: [ + "additional_translations/qml_en.ts", + "i18n/qml_no.ts" + ] + MCU.omitSourceLanguage: true + } +// END MCU nodes + +// This node is prefixed with MCU. Its props are not prefixed +// It must remain unchanged after conversion. Modules may have MCU.Module + MCU.Config { + resourceCompression: true + fontEngine: "Spark" + defaultFontFamily: "Roboto" + + maxResourceCacheSize: [ + [128, 128], + [16384, 1] + ] + + } +//END MCU-prefixed node + +// The following nodes are ignored by Qt for MCUs +// They do not need the QDS-prefix, but may have it + Environment { + QT_QUICK_CONTROLS_CONF: "qtquickcontrols2.conf" + QT_AUTO_SCREEN_SCALE_FACTOR: "1" + QML_COMPAT_RESOLVE_URLS_ON_ASSIGNMENT: "1" + QT_LOGGING_RULES: "qt.qml.connections=false" + QT_ENABLE_HIGHDPI_SCALING: "0" + /* Useful for debugging + QSG_VISUALIZE=batches + QSG_VISUALIZE=clip + QSG_VISUALIZE=changes + QSG_VISUALIZE=overdraw + */ + } + + JavaScriptFiles { + directory: "scripts" + } + + Files { + directory: "more_files" + filter: "*.mp3" + } +// END of ignored nodes + +// The following nodes are unknown to Qt for MCUs (2.6) +// Unknown nodes cause a warning in MCU tooling, but no error as of Qt for MCUs 2.6 + ShaderTool { + args: "-s --glsl \"100 es,120,150\" --hlsl 50 --msl 12" + files: [ "content/shaders/*" ] + } +// END of unknown nodes +} diff --git a/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-mcu-1/testfile.qmltojson b/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-mcu-1/testfile.qmltojson new file mode 100644 index 00000000000..303bfc3899d --- /dev/null +++ b/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-mcu-1/testfile.qmltojson @@ -0,0 +1,236 @@ +{ + "deployment": { + "targetDirectory": "/opt/UntitledProject13" + }, + "environment": { + "QML_COMPAT_RESOLVE_URLS_ON_ASSIGNMENT": "1", + "QT_AUTO_SCREEN_SCALE_FACTOR": "1", + "QT_ENABLE_HIGHDPI_SCALING": "0", + "QT_LOGGING_RULES": "qt.qml.connections=false", + "QT_QUICK_CONTROLS_CONF": "qtquickcontrols2.conf" + }, + "fileGroups": [ + { + "directory": "content", + "files": [ + ], + "filters": [ + "*.qml" + ], + "mcuProperties": { + "copyQmlFiles": true + }, + "otherProperties": { + }, + "type": "Qml" + }, + { + "directory": "", + "files": [ + "qml/Header.qml", + "qml/Footer.qml" + ], + "filters": [ + ], + "mcuProperties": { + }, + "otherProperties": { + }, + "type": "Qml" + }, + { + "directory": "assets", + "files": [ + ], + "filters": [ + "*.jpeg", + "*.jpg", + "*.png", + "*.svg", + "*.hdr", + "*.ktx" + ], + "mcuProperties": { + }, + "otherProperties": { + }, + "type": "Image" + }, + { + "directory": "", + "files": [ + "images/clock.png" + ], + "filters": [ + ], + "mcuProperties": { + "resourceCompression": false + }, + "otherProperties": { + }, + "type": "Image" + }, + { + "directory": "", + "files": [ + "images/weather.png", + "images/navigation.png" + ], + "filters": [ + ], + "mcuProperties": { + "base": "images", + "prefix": "assets" + }, + "otherProperties": { + }, + "type": "Image" + }, + { + "directory": "", + "files": [ + "qmlproject/module/module.qmlproject" + ], + "filters": [ + ], + "mcuProperties": { + "qulModules": [ + "Controls", + "Timeline" + ] + }, + "otherProperties": { + }, + "type": "Module" + }, + { + "directory": "", + "files": [ + "fonts/RobotoFonts.fmp" + ], + "filters": [ + ], + "mcuProperties": { + }, + "otherProperties": { + }, + "type": "Font" + }, + { + "directory": "", + "files": [ + "src/BoardInterface.h" + ], + "filters": [ + ], + "mcuProperties": { + }, + "otherProperties": { + }, + "type": "Interface" + }, + { + "directory": "", + "files": [ + "additional_translations/qml_en.ts", + "i18n/qml_no.ts" + ], + "filters": [ + ], + "mcuProperties": { + "omitSourceLanguage": true + }, + "otherProperties": { + }, + "type": "Translation" + }, + { + "directory": "scripts", + "files": [ + ], + "filters": [ + "*.js", + "*.ts" + ], + "mcuProperties": { + }, + "otherProperties": { + }, + "type": "JavaScript" + }, + { + "directory": "more_files", + "files": [ + ], + "filters": [ + "*.mp3" + ], + "mcuProperties": { + }, + "otherProperties": { + }, + "type": "" + } + ], + "fileVersion": 1, + "importPaths": [ + "imports", + "asset_imports" + ], + "language": { + "multiLanguageSupport": true, + "primaryLanguage": "en", + "supportedLanguages": [ + "no" + ] + }, + "mcu": { + "config": { + "defaultFontFamily": "Roboto", + "fontEngine": "Spark", + "maxResourceCacheSize": [ + [ + 128, + 128 + ], + [ + 16384, + 1 + ] + ], + "resourceCompression": true + }, + "enabled": true, + "module": { + } + }, + "otherProperties": { + "idBasedTranslations": true, + "projectRootPath": ".." + }, + "runConfig": { + "fileSelectors": [ + ], + "mainFile": "Main.qml", + "widgetApp": true + }, + "shaderTool": { + "args": [ + "-s", + "--glsl", + "\"100 es,120,150\"", + "--hlsl", + "50", + "--msl", + "12" + ], + "files": [ + "content/shaders/*" + ] + }, + "versions": { + "designStudio": "4.0", + "qt": "5", + "qtQuick": "6.2" + } +} diff --git a/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-mcu-2/testfile.jsontoqml b/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-mcu-2/testfile.jsontoqml new file mode 100644 index 00000000000..2e73146cdaf --- /dev/null +++ b/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-mcu-2/testfile.jsontoqml @@ -0,0 +1,32 @@ +// prop: json-converted +// prop: auto-generated + +import QmlProject + +Project { + widgetApp: false + + qt6Project: false + qtForMCUs: true + + projectRootPath: "../.." + + MCU.Module { + generateQmltypes: true + uri: "MyModule" + } + + QmlFiles { + files: [ + "qml/TextIcon.qml" + ] + } + + ImageFiles { + files: [ + "images/qt_logo.png" + ] + MCU.base: "images" + MCU.prefix: "logo" + } +} diff --git a/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-mcu-2/testfile.qmlproject b/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-mcu-2/testfile.qmlproject new file mode 100644 index 00000000000..bb4e83f0338 --- /dev/null +++ b/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-mcu-2/testfile.qmlproject @@ -0,0 +1,29 @@ +import QmlProject 1.3 + +// This file represents the module contained in the testfile of test-set-mcu-1 + +// Do not modify without discussing with the MCU team + +Project { + + projectRootPath: "../.." + + MCU.Module { + uri: "MyModule" + generateQmltypes: true + } + + QmlFiles { + files: [ + "qml/TextIcon.qml" + ] + } + + ImageFiles { + files: [ + "images/qt_logo.png" + ] + MCU.base: "images" + MCU.prefix: "logo" + } +} diff --git a/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-mcu-2/testfile.qmltojson b/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-mcu-2/testfile.qmltojson new file mode 100644 index 00000000000..0ef011b615c --- /dev/null +++ b/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-mcu-2/testfile.qmltojson @@ -0,0 +1,56 @@ +{ + "deployment": { + }, + "fileGroups": [ + { + "directory": "", + "files": [ + "qml/TextIcon.qml" + ], + "filters": [ + ], + "mcuProperties": { + }, + "otherProperties": { + }, + "type": "Qml" + }, + { + "directory": "", + "files": [ + "images/qt_logo.png" + ], + "filters": [ + ], + "mcuProperties": { + "base": "images", + "prefix": "logo" + }, + "otherProperties": { + }, + "type": "Image" + } + ], + "fileVersion": 1, + "language": { + }, + "mcu": { + "config": { + }, + "enabled": true, + "module": { + "generateQmltypes": true, + "uri": "MyModule" + } + }, + "otherProperties": { + "projectRootPath": "../.." + }, + "runConfig": { + "fileSelectors": [ + ] + }, + "versions": { + "qt": "5" + } +} From 4da241f26facfe41bc2faf06ed356e9921eb19e5 Mon Sep 17 00:00:00 2001 From: Henning Gruendl Date: Tue, 14 Nov 2023 11:53:22 +0100 Subject: [PATCH 226/242] QmlDesigner: Make recent project removable Add functionality to remove individual recent projects and the complete list of recent projects. Task-number: QDS-6606 Task-number: QDS-8761 Change-Id: I8ff4de918ebb2f7a6ffb06e6d3ba04e38eb7f386 Reviewed-by: Brook Cronin Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Thomas Hartmann --- .../studiowelcome/studiowelcomeplugin.cpp | 43 ++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/src/plugins/studiowelcome/studiowelcomeplugin.cpp b/src/plugins/studiowelcome/studiowelcomeplugin.cpp index a08e2f48bd2..d9b29ffb951 100644 --- a/src/plugins/studiowelcome/studiowelcomeplugin.cpp +++ b/src/plugins/studiowelcome/studiowelcomeplugin.cpp @@ -221,6 +221,7 @@ public: Q_PROPERTY(bool communityVersion MEMBER m_communityVersion NOTIFY communityVersionChanged) Q_PROPERTY(bool enterpriseVersion MEMBER m_enterpriseVersion NOTIFY enterpriseVersionChanged) + Q_PROPERTY(int count READ count NOTIFY countChanged) explicit ProjectModel(QObject *parent = nullptr); @@ -228,6 +229,8 @@ public: QVariant data(const QModelIndex &index, int role) const override; QHash roleNames() const override; + int count() { return ProjectExplorer::ProjectExplorerPlugin::recentProjects().count(); } + Q_INVOKABLE void createProject() { QTimer::singleShot(0, this, []() { @@ -259,6 +262,33 @@ public: }; } + delayedResetProjects(); + } + + Q_INVOKABLE void removeFromRecentProjects(int row) + { + if (m_blockOpenRecent) + return; + + m_blockOpenRecent = true; + const FilePath projectFile = FilePath::fromVariant( + data(index(row, 0), ProjectModel::FilePathRole)); + + if (projectFile.exists()) + ProjectExplorer::ProjectExplorerPlugin::removeFromRecentProjects(projectFile); + + resetProjects(); + } + + Q_INVOKABLE void clearRecentProjects() + { + if (m_blockOpenRecent) + return; + + m_blockOpenRecent = true; + + ProjectExplorer::ProjectExplorerPlugin::clearRecentProjects(); + resetProjects(); } @@ -317,10 +347,12 @@ public: public slots: void resetProjects(); + void delayedResetProjects(); signals: void communityVersionChanged(); void enterpriseVersionChanged(); + void countChanged(); private: void setupVersion(); @@ -343,7 +375,9 @@ ProjectModel::ProjectModel(QObject *parent) connect(ProjectExplorer::ProjectExplorerPlugin::instance(), &ProjectExplorer::ProjectExplorerPlugin::recentProjectsChanged, this, - &ProjectModel::resetProjects); + &ProjectModel::delayedResetProjects); + + connect(this, &QAbstractListModel::modelReset, this, &ProjectModel::countChanged); setupVersion(); } @@ -467,6 +501,13 @@ QHash ProjectModel::roleNames() const } void ProjectModel::resetProjects() +{ + beginResetModel(); + endResetModel(); + m_blockOpenRecent = false; +} + +void ProjectModel::delayedResetProjects() { QTimer::singleShot(2000, this, [this]() { beginResetModel(); From 6239b079f8ca9d2354870be58d86f7ee45b80589 Mon Sep 17 00:00:00 2001 From: Henning Gruendl Date: Tue, 14 Nov 2023 14:26:10 +0100 Subject: [PATCH 227/242] QmlDesigner: Fix insight view sections Task-number: QDS-11220 Change-Id: I97471ccc3362617b7f1613c77e5cc86ae81d7e15 Reviewed-by: Thomas Hartmann --- share/qtcreator/qmldesigner/insight/Main.qml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/share/qtcreator/qmldesigner/insight/Main.qml b/share/qtcreator/qmldesigner/insight/Main.qml index 3d0cd777753..1abad0050f5 100644 --- a/share/qtcreator/qmldesigner/insight/Main.qml +++ b/share/qtcreator/qmldesigner/insight/Main.qml @@ -55,7 +55,7 @@ Rectangle { id: column width: root.width - Section { + HelperWidgets.Section { id: trackingSection caption: qsTr("Tracking") anchors.left: parent.left @@ -167,7 +167,7 @@ Rectangle { } } - Section { + HelperWidgets.Section { id: predefinedSection caption: qsTr("Predefined Categories") anchors.left: parent.left @@ -227,7 +227,7 @@ Rectangle { } } - Item { width: 1; height: 4} + Item { width: 1; height: 4 } Repeater { model: insightModel @@ -268,7 +268,7 @@ Rectangle { } } - Section { + HelperWidgets.Section { id: customSection caption: qsTr("Custom Categories") anchors.left: parent.left @@ -325,7 +325,7 @@ Rectangle { } } - Item { width: 1; height: 4} + Item { width: 1; height: 4 } Repeater { id: customRepeater @@ -387,7 +387,7 @@ Rectangle { } } - Item { width: 1; height: 4} + Item { width: 1; height: 4 } Row { spacing: StudioTheme.Values.checkBoxSpacing From c1edcc5d9f9062a548e40bf4c4e7a68d82808846 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Tue, 14 Nov 2023 18:17:31 +0100 Subject: [PATCH 228/242] QmlDesigner: Do not allow invalid imports For example: import QtQuick '-1.-1' Change-Id: Ia63be5471f4fa69b7f798876239fb57db7806f91 Reviewed-by: Thomas Hartmann --- src/plugins/qmldesigner/designercore/include/import.h | 2 +- src/plugins/qmldesigner/designercore/model/import.cpp | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/plugins/qmldesigner/designercore/include/import.h b/src/plugins/qmldesigner/designercore/include/import.h index 5dbab393d21..741c5ae54da 100644 --- a/src/plugins/qmldesigner/designercore/include/import.h +++ b/src/plugins/qmldesigner/designercore/include/import.h @@ -59,7 +59,7 @@ public: bool isEmpty() const { return m_type == Type::Empty; } bool isFileImport() const { return m_type == Type::File; } bool isLibraryImport() const { return m_type == Type::Library; } - bool hasVersion() const { return !m_version.isEmpty(); } + bool hasVersion() const; bool hasAlias() const { return !m_alias.isEmpty(); } const QString &url() const { return m_type == Type::Library ? m_url : emptyString; } diff --git a/src/plugins/qmldesigner/designercore/model/import.cpp b/src/plugins/qmldesigner/designercore/model/import.cpp index 6d4cd19d8b8..f23331edebf 100644 --- a/src/plugins/qmldesigner/designercore/model/import.cpp +++ b/src/plugins/qmldesigner/designercore/model/import.cpp @@ -24,6 +24,11 @@ Import Import::empty() return Import(QString(), QString(), QString(), QStringList(), Type::Empty); } +bool Import::hasVersion() const +{ + return !m_version.isEmpty() && m_version != "-1.-1"; +} + QString Import::toImportString() const { QString result = QStringLiteral("import "); From f199755cfecc4bbe8b38057f8f65d7faf32914fe Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Mon, 13 Nov 2023 17:37:45 +0100 Subject: [PATCH 229/242] QmlDesigner: Adjust display strings for jump to code Change-Id: Ia1f976491be0834ce936092429ebceee360be2df Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Pranta Ghosh Dastider Reviewed-by: Mats Honkamaa --- .../qmldesigner/connectionseditor/ConnectionsDialogForm.qml | 2 +- share/qtcreator/qmldesigner/stateseditor/StateMenu.qml | 2 +- .../components/componentcore/componentcore_constants.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialogForm.qml b/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialogForm.qml index c56cdef16cc..603903dbdd2 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialogForm.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialogForm.qml @@ -243,7 +243,7 @@ Column { id: jumpToCodeButton style: StudioTheme.Values.microToolbarButtonStyle buttonIcon: StudioTheme.Constants.jumpToCode_medium - tooltip: qsTr("Jump to Code Editor.") + tooltip: qsTr("Jump to the code.") onClicked: backend.jumpToCode() } } diff --git a/share/qtcreator/qmldesigner/stateseditor/StateMenu.qml b/share/qtcreator/qmldesigner/stateseditor/StateMenu.qml index c30538e31ed..e7139162d77 100644 --- a/share/qtcreator/qmldesigner/stateseditor/StateMenu.qml +++ b/share/qtcreator/qmldesigner/stateseditor/StateMenu.qml @@ -87,7 +87,7 @@ StudioControls.Menu { StudioControls.MenuItem { enabled: !root.isBaseState - text: qsTr("Jump To Code") + text: qsTr("Jump to the code") onTriggered: root.jumpToCode() } diff --git a/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h b/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h index 3f8cbd21346..44015d59fce 100644 --- a/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h +++ b/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h @@ -121,7 +121,7 @@ const char copyFormatDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", const char applyFormatDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Apply Formatting"); const char enterComponentDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Edit Component"); -const char JumpToCodeDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Jump To Code"); +const char JumpToCodeDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Jump to the Code"); const char mergeTemplateDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Merge with Template"); const char goToImplementationDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Go to Implementation"); const char makeComponentDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Create Component"); From 5b90cbc29af2e32119f284865ee3e8fc2d223f18 Mon Sep 17 00:00:00 2001 From: Aleksei German Date: Tue, 14 Nov 2023 17:57:20 +0100 Subject: [PATCH 230/242] QmlDesigner: Disable State Groups for Qt for MCUs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Task-number: QDS-10355 Change-Id: I1b26b8e5b9ef3ed3176b7d6cd34bc1e27879ce1c Reviewed-by: Henning Gründl Reviewed-by: Thomas Hartmann Reviewed-by: Qt CI Patch Build Bot --- share/qtcreator/qmldesigner/stateseditor/Main.qml | 11 +++++++++-- .../components/stateseditor/stateseditormodel.cpp | 7 +++++++ .../components/stateseditor/stateseditormodel.h | 4 ++++ 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/share/qtcreator/qmldesigner/stateseditor/Main.qml b/share/qtcreator/qmldesigner/stateseditor/Main.qml index 80209864a17..8774f3a02b2 100644 --- a/share/qtcreator/qmldesigner/stateseditor/Main.qml +++ b/share/qtcreator/qmldesigner/stateseditor/Main.qml @@ -412,6 +412,8 @@ Rectangle { anchors.verticalCenter: parent.verticalCenter width: stateGroupLabel.visible ? StudioTheme.Values.defaultControlWidth : root.width - 2 * root.padding + enabled: !(StatesEditorBackend.statesEditorModel.isMCUs + && stateGroupComboBox.count <= 1) HelperWidgets.Tooltip { id: comboBoxTooltip } @@ -420,7 +422,9 @@ Rectangle { running: stateGroupComboBox.hovered onTriggered: comboBoxTooltip.showText(stateGroupComboBox, hoverHandler.point.position, - qsTr("Switch State Group")) + StatesEditorBackend.statesEditorModel.isMCUs + ? qsTr("State Groups are not supported with Qt for MCUs") + : qsTr("Switch State Group")) } onHoverChanged: { @@ -463,8 +467,11 @@ Rectangle { style: StudioTheme.Values.viewBarButtonStyle buttonIcon: StudioTheme.Constants.add_medium anchors.verticalCenter: parent.verticalCenter - tooltip: qsTr("Create State Group") + tooltip: StatesEditorBackend.statesEditorModel.isMCUs + ? qsTr("State Groups are not supported with Qt for MCUs") + : qsTr("Create State Group") onClicked: StatesEditorBackend.statesEditorModel.addStateGroup("stateGroup") + enabled: !StatesEditorBackend.statesEditorModel.isMCUs } HelperWidgets.AbstractButton { diff --git a/src/plugins/qmldesigner/components/stateseditor/stateseditormodel.cpp b/src/plugins/qmldesigner/components/stateseditor/stateseditormodel.cpp index c317374c8da..606e136de01 100644 --- a/src/plugins/qmldesigner/components/stateseditor/stateseditormodel.cpp +++ b/src/plugins/qmldesigner/components/stateseditor/stateseditormodel.cpp @@ -5,6 +5,7 @@ #include "stateseditorview.h" #include +#include #include #include #include @@ -74,6 +75,7 @@ void StatesEditorModel::reset() evaluateExtend(); emit baseStateChanged(); + emit isMCUsChanged(); } QVariant StatesEditorModel::data(const QModelIndex &index, int role) const @@ -456,4 +458,9 @@ void StatesEditorModel::setCanAddNewStates(bool b) emit canAddNewStatesChanged(); } +bool StatesEditorModel::isMCUs() const +{ + return DesignerMcuManager::instance().isMCUProject(); +} + } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/stateseditor/stateseditormodel.h b/src/plugins/qmldesigner/components/stateseditor/stateseditormodel.h index 0e228e46bd8..12d66e29bf2 100644 --- a/src/plugins/qmldesigner/components/stateseditor/stateseditormodel.h +++ b/src/plugins/qmldesigner/components/stateseditor/stateseditormodel.h @@ -82,6 +82,7 @@ public: Q_PROPERTY(int activeStateGroupIndex READ activeStateGroupIndex WRITE setActiveStateGroupIndex NOTIFY activeStateGroupIndexChanged) Q_PROPERTY(QStringList stateGroups READ stateGroups NOTIFY stateGroupsChanged) + Q_PROPERTY(bool isMCUs READ isMCUs NOTIFY isMCUsChanged) Q_INVOKABLE void move(int from, int to); Q_INVOKABLE void drop(int from, int to); @@ -92,6 +93,8 @@ public: bool canAddNewStates() const; void setCanAddNewStates(bool b); + bool isMCUs() const; + signals: void changedToState(int n); void baseStateChanged(); @@ -101,6 +104,7 @@ signals: void activeStateGroupIndexChanged(); void stateGroupsChanged(); void canAddNewStatesChanged(); + void isMCUsChanged(); private: QPointer m_statesEditorView; From 031e23edbeaf0efed4a7f1c0828bc1cc7881b145 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esa=20T=C3=B6rm=C3=A4nen?= Date: Wed, 15 Nov 2023 09:16:30 +0200 Subject: [PATCH 231/242] Doc: Update QDS version compatibility with Qt for MCUs SDKs Modified the QDS version compatibility with Qt for MCUs SDKs page so that it's up to date with the latest/upcoming releases (Qt for MCUs 2.6 & QDS 4.4). Task-number: QDS-11228 Change-Id: I21ca6e5e7362c8bab756d1adcd81954deea48784 Reviewed-by: Yasser Grimes Reviewed-by: Maija Metso Reviewed-by: Mats Honkamaa Reviewed-by: Thomas Hartmann Reviewed-by: Qt CI Patch Build Bot --- .../qtdesignstudio-compatibility-with-mcu-sdks.qdoc | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/doc/qtdesignstudio/src/mcus/qtdesignstudio-compatibility-with-mcu-sdks.qdoc b/doc/qtdesignstudio/src/mcus/qtdesignstudio-compatibility-with-mcu-sdks.qdoc index 9dad2fd4057..de94f59d805 100644 --- a/doc/qtdesignstudio/src/mcus/qtdesignstudio-compatibility-with-mcu-sdks.qdoc +++ b/doc/qtdesignstudio/src/mcus/qtdesignstudio-compatibility-with-mcu-sdks.qdoc @@ -16,8 +16,14 @@ \li \QDS Version \li \QMCU SDK Version \row - \li 4.0 or later - \li 2.4 or later + \li 4.3 or later + \li 2.6 or later + \row + \li 4.2 or later + \li 2.5 + \row + \li 4.0 up to 4.1 + \li 2.4 \row \li 3.8 up to 3.9 \li 2.3 From de4c871655afb116bead829599ef55d42f3b0b40 Mon Sep 17 00:00:00 2001 From: Karim Abdelrahman Date: Wed, 15 Nov 2023 09:27:03 +0200 Subject: [PATCH 232/242] QmlDesigner: Fix isQtForMcusProject() bad if condition "if(activeTarget && activeTarget->kit())" will always return true for both Qml & CMake build systems. The value of McuSupport.McuTargetKitVersion has to also be checked as the third condition for Cmake build system. Task-number: QDS-10599 Change-Id: I5904e34082d1d81cccc4dd43b6911f9160eb5ea1 Reviewed-by: Yasser Grimes Reviewed-by: Aleksei German --- .../qmldesigner/qmldesignerexternaldependencies.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/plugins/qmldesigner/qmldesignerexternaldependencies.cpp b/src/plugins/qmldesigner/qmldesignerexternaldependencies.cpp index 0b4d0ac5f4e..321d95197fc 100644 --- a/src/plugins/qmldesigner/qmldesignerexternaldependencies.cpp +++ b/src/plugins/qmldesigner/qmldesignerexternaldependencies.cpp @@ -248,14 +248,14 @@ bool ExternalDependencies::isQt6Project() const bool ExternalDependencies::isQtForMcusProject() const { - // CMakeBuildSystem - ProjectExplorer::Target *activeTarget = ProjectExplorer::ProjectManager::startupTarget(); - if (activeTarget && activeTarget->kit()) - return activeTarget->kit()->hasValue("McuSupport.McuTargetKitVersion"); - // QmlBuildSystem auto [project, target, qmlBuildSystem] = activeProjectEntries(); - return qmlBuildSystem && qmlBuildSystem->qtForMCUs(); + if (qmlBuildSystem) + return qmlBuildSystem->qtForMCUs(); + + // CMakeBuildSystem + ProjectExplorer::Target *activeTarget = ProjectExplorer::ProjectManager::startupTarget(); + return activeTarget && activeTarget->kit() && activeTarget->kit()->hasValue("McuSupport.McuTargetKitVersion"); } QString ExternalDependencies::qtQuickVersion() const From 306ce4ab35086dc90165433d09b53e11435b7b30 Mon Sep 17 00:00:00 2001 From: Amr Essam Date: Tue, 14 Nov 2023 14:49:42 +0200 Subject: [PATCH 233/242] EffectMaker: Open saved compositions - Also fixing issues related to image paths - Composition name is shown in save dialog when re-save - Clear current composition for reset or open a new one Task-number: QDS-11192 Change-Id: I97aad4b5216e6b116343bb274db0f9abd1275fec Reviewed-by: Mahmoud Badri --- .../effectMakerQmlSources/EffectMaker.qml | 1 + .../effectMakerQmlSources/SaveDialog.qml | 2 +- .../effectmakernew/compositionnode.cpp | 53 +++---- src/plugins/effectmakernew/compositionnode.h | 5 +- .../effectmakernew/effectmakermodel.cpp | 139 ++++++++++++------ src/plugins/effectmakernew/effectmakermodel.h | 14 +- .../effectmakernew/effectmakerview.cpp | 13 +- src/plugins/effectmakernew/uniform.cpp | 31 ++-- src/plugins/effectmakernew/uniform.h | 4 +- .../assetslibrary/assetslibrarywidget.cpp | 18 ++- 10 files changed, 181 insertions(+), 99 deletions(-) diff --git a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMaker.qml b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMaker.qml index 19e08acd525..a60b41acf31 100644 --- a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMaker.qml +++ b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMaker.qml @@ -18,6 +18,7 @@ Item { SaveDialog { id: saveDialog + compositionName: EffectMakerBackend.effectMakerModel.currentComposition anchors.centerIn: parent onAccepted: { let name = saveDialog.compositionName diff --git a/share/qtcreator/qmldesigner/effectMakerQmlSources/SaveDialog.qml b/share/qtcreator/qmldesigner/effectMakerQmlSources/SaveDialog.qml index 08bb8c0832a..8bd48e1d6c4 100644 --- a/share/qtcreator/qmldesigner/effectMakerQmlSources/SaveDialog.qml +++ b/share/qtcreator/qmldesigner/effectMakerQmlSources/SaveDialog.qml @@ -21,7 +21,7 @@ StudioControls.Dialog { property string compositionName: "" onOpened: { - nameText.text = "" //TODO: Generate unique name + nameText.text = compositionName //TODO: Generate unique name emptyText.text = "" nameText.forceActiveFocus() } diff --git a/src/plugins/effectmakernew/compositionnode.cpp b/src/plugins/effectmakernew/compositionnode.cpp index 796b06151f9..8cd5bda971e 100644 --- a/src/plugins/effectmakernew/compositionnode.cpp +++ b/src/plugins/effectmakernew/compositionnode.cpp @@ -11,13 +11,36 @@ #include #include #include -#include namespace EffectMaker { -CompositionNode::CompositionNode(const QString &qenPath) +CompositionNode::CompositionNode(const QString &effectName, const QString &qenPath, const QJsonObject &jsonObject) { - parse(qenPath); + QJsonObject json; + if (jsonObject.isEmpty()) { + QFile qenFile(qenPath); + if (!qenFile.open(QIODevice::ReadOnly)) { + qWarning("Couldn't open effect file."); + return; + } + + QByteArray loadData = qenFile.readAll(); + QJsonParseError parseError; + QJsonDocument jsonDoc(QJsonDocument::fromJson(loadData, &parseError)); + + if (parseError.error != QJsonParseError::NoError) { + QString error = QString("Error parsing effect node"); + QString errorDetails = QString("%1: %2").arg(parseError.offset).arg(parseError.errorString()); + qWarning() << error; + qWarning() << errorDetails; + return; + } + json = jsonDoc.object().value("QEN").toObject(); + parse(effectName, qenPath, json); + } + else { + parse(effectName, "", jsonObject); + } } QString CompositionNode::fragmentCode() const @@ -63,28 +86,8 @@ CompositionNode::NodeType CompositionNode::type() const return m_type; } -void CompositionNode::parse(const QString &qenPath) +void CompositionNode::parse(const QString &effectName, const QString &qenPath, const QJsonObject &json) { - QFile qenFile(qenPath); - - if (!qenFile.open(QIODevice::ReadOnly)) { - qWarning("Couldn't open effect file."); - return; - } - - QByteArray loadData = qenFile.readAll(); - QJsonParseError parseError; - QJsonDocument jsonDoc(QJsonDocument::fromJson(loadData, &parseError)); - if (parseError.error != QJsonParseError::NoError) { - QString error = QString("Error parsing the effect node: %1:").arg(qenPath); - QString errorDetails = QString("%1: %2").arg(parseError.offset).arg(parseError.errorString()); - qWarning() << qPrintable(error); - qWarning() << qPrintable(errorDetails); - return; - } - - QJsonObject json = jsonDoc.object().value("QEN").toObject(); - int version = -1; if (json.contains("version")) version = json["version"].toInt(-1); @@ -102,7 +105,7 @@ void CompositionNode::parse(const QString &qenPath) // parse properties QJsonArray jsonProps = json.value("properties").toArray(); for (const auto /*QJsonValueRef*/ &prop : jsonProps) { - const auto uniform = new Uniform(prop.toObject(), qenPath); + const auto uniform = new Uniform(effectName, prop.toObject(), qenPath); m_unifomrsModel.addUniform(uniform); m_uniforms.append(uniform); g_propertyData.insert(uniform->name(), uniform->value()); diff --git a/src/plugins/effectmakernew/compositionnode.h b/src/plugins/effectmakernew/compositionnode.h index 37203ef4219..4736f1d8afa 100644 --- a/src/plugins/effectmakernew/compositionnode.h +++ b/src/plugins/effectmakernew/compositionnode.h @@ -5,6 +5,7 @@ #include "effectmakeruniformsmodel.h" +#include #include namespace EffectMaker { @@ -24,7 +25,7 @@ public: CustomNode }; - CompositionNode(const QString &qenPath); + CompositionNode(const QString &effectName, const QString &qenPath, const QJsonObject &json = {}); QString fragmentCode() const; QString vertexCode() const; @@ -48,7 +49,7 @@ signals: void isEnabledChanged(); private: - void parse(const QString &qenPath); + void parse(const QString &effectName, const QString &qenPath, const QJsonObject &json); QString m_name; NodeType m_type = CustomNode; diff --git a/src/plugins/effectmakernew/effectmakermodel.cpp b/src/plugins/effectmakernew/effectmakermodel.cpp index fa11f2fb3c7..ee147218a6d 100644 --- a/src/plugins/effectmakernew/effectmakermodel.cpp +++ b/src/plugins/effectmakernew/effectmakermodel.cpp @@ -49,19 +49,6 @@ static bool writeToFile(const QByteArray &buf, const QString &filename, FileType EffectMakerModel::EffectMakerModel(QObject *parent) : QAbstractListModel{parent} { - connect(&m_fileWatcher, &Utils::FileSystemWatcher::fileChanged, this, [this]() { - // Update component with images not set. - m_loadComponentImages = false; - updateQmlComponent(); - // Then enable component images with a longer delay than - // the component updating delay. This way Image elements - // will reload the changed image files. - const int enableImagesDelay = 200; - QTimer::singleShot(enableImagesDelay, this, [this]() { - m_loadComponentImages = true; - updateQmlComponent(); - } ); - }); } QHash EffectMakerModel::roleNames() const @@ -117,7 +104,7 @@ void EffectMakerModel::setIsEmpty(bool val) void EffectMakerModel::addNode(const QString &nodeQenPath) { beginInsertRows({}, m_nodes.size(), m_nodes.size()); - auto *node = new CompositionNode(nodeQenPath); + auto *node = new CompositionNode("", nodeQenPath); m_nodes.append(node); endInsertRows(); @@ -189,12 +176,17 @@ void EffectMakerModel::clear() if (m_nodes.isEmpty()) return; + beginRemoveRows({}, 0, m_nodes.count()); + for (CompositionNode *node : std::as_const(m_nodes)) delete node; m_nodes.clear(); + endRemoveRows(); + setIsEmpty(true); + bakeShaders(); } const QList EffectMakerModel::allUniforms() @@ -571,6 +563,69 @@ void EffectMakerModel::exportComposition(const QString &name) saveFile.close(); } +void EffectMakerModel::openComposition(const QString &path) +{ + clear(); + + QFile compFile(path); + if (!compFile.open(QIODevice::ReadOnly)) { + QString error = QString("Couldn't open composition file: '%1'").arg(path); + qWarning() << qPrintable(error); + setEffectError(error); + return; + } + + QByteArray data = compFile.readAll(); + QJsonParseError parseError; + QJsonDocument jsonDoc(QJsonDocument::fromJson(data, &parseError)); + if (parseError.error != QJsonParseError::NoError) { + QString error = QString("Error parsing the project file: %1").arg(parseError.errorString()); + qWarning() << error; + setEffectError(error); + return; + } + QJsonObject rootJson = jsonDoc.object(); + if (!rootJson.contains("QEP")) { + QString error = QStringLiteral("Error: Invalid project file"); + qWarning() << error; + setEffectError(error); + return; + } + + QJsonObject json = rootJson["QEP"].toObject(); + + int version = -1; + if (json.contains("version")) + version = json["version"].toInt(-1); + + if (version != 1) { + QString error = QString("Error: Unknown project version (%1)").arg(version); + qWarning() << error; + setEffectError(error); + return; + } + + // Get effects dir + const QString effectName = QFileInfo(path).baseName(); + const Utils::FilePath effectsResDir = QmlDesigner::ModelNodeOperations::getEffectsImportDirectory(); + const QString effectsResPath = effectsResDir.pathAppended(effectName).toString(); + + if (json.contains("nodes") && json["nodes"].isArray()) { + const QJsonArray nodesArray = json["nodes"].toArray(); + for (const auto &nodeElement : nodesArray) { + beginInsertRows({}, m_nodes.size(), m_nodes.size()); + auto *node = new CompositionNode(effectName, "", nodeElement.toObject()); + m_nodes.append(node); + endInsertRows(); + } + + setIsEmpty(m_nodes.isEmpty()); + bakeShaders(); + } + + setCurrentComposition(effectName); +} + void EffectMakerModel::exportResources(const QString &name) { // Make sure that uniforms are up-to-date @@ -647,18 +702,15 @@ void EffectMakerModel::exportResources(const QString &name) QString imagePath = uniform->value().toString(); QFileInfo fi(imagePath); QString imageFilename = fi.fileName(); - sources.append(imagePath); + sources.append(imagePath.remove(0, 7)); // Removes "file://" dests.append(imageFilename); } } - //TODO: Copy source files if requested in future versions - - // Copy files for (int i = 0; i < sources.count(); ++i) { Utils::FilePath source = Utils::FilePath::fromString(sources[i]); Utils::FilePath target = Utils::FilePath::fromString(effectsResPath + dests[i]); - if (target.exists()) + if (target.exists() && source.fileName() != target.fileName()) target.removeFile(); // Remove existing file for update if (!source.copyFile(target)) @@ -1254,15 +1306,16 @@ QString EffectMakerModel::getQmlImagesString(bool localFiles) if (localFiles) { QFileInfo fi(imagePath); imagePath = fi.fileName(); - } - if (m_loadComponentImages) + imagesString += QString(" source: %1\n").arg(uniform->name()); + } else { imagesString += QString(" source: g_propertyData.%1\n").arg(uniform->name()); - if (!localFiles) { - QString mipmapProperty = mipmapPropertyName(uniform->name()); - imagesString += QString(" mipmap: g_propertyData.%1\n").arg(mipmapProperty); - } else if (uniform->enableMipmap()) { - imagesString += " mipmap: true\n"; + + if (uniform->enableMipmap()) + imagesString += " mipmap: true\n"; + else + QString mipmapProperty = mipmapPropertyName(uniform->name()); } + imagesString += " visible: false\n"; imagesString += " }\n"; } @@ -1336,6 +1389,19 @@ QString EffectMakerModel::getQmlComponentString(bool localFiles) return s; } +QString EffectMakerModel::currentComposition() const +{ + return m_currentComposition; +} + +void EffectMakerModel::setCurrentComposition(const QString &newCurrentComposition) +{ + if (m_currentComposition == newCurrentComposition) + return; + m_currentComposition = newCurrentComposition; + emit currentCompositionChanged(); +} + void EffectMakerModel::updateQmlComponent() { // Clear possible QML runtime errors @@ -1352,25 +1418,4 @@ QString EffectMakerModel::stripFileFromURL(const QString &urlString) const return filePath; } -void EffectMakerModel::updateImageWatchers() -{ - const QList uniforms = allUniforms(); - for (Uniform *uniform : uniforms) { - if (uniform->type() == Uniform::Type::Sampler) { - // Watch all image properties files - QString imagePath = stripFileFromURL(uniform->value().toString()); - if (imagePath.isEmpty()) - continue; - m_fileWatcher.addFile(imagePath, Utils::FileSystemWatcher::WatchAllChanges); - } - } -} - -void EffectMakerModel::clearImageWatchers() -{ - const auto watchedFiles = m_fileWatcher.files(); - if (!watchedFiles.isEmpty()) - m_fileWatcher.removeFiles(watchedFiles); -} - } // namespace EffectMaker diff --git a/src/plugins/effectmakernew/effectmakermodel.h b/src/plugins/effectmakernew/effectmakermodel.h index 9647b8886a1..96eb0e19b46 100644 --- a/src/plugins/effectmakernew/effectmakermodel.h +++ b/src/plugins/effectmakernew/effectmakermodel.h @@ -6,7 +6,6 @@ #include "shaderfeatures.h" #include -#include #include #include @@ -47,7 +46,7 @@ class EffectMakerModel : public QAbstractListModel Q_PROPERTY(int selectedIndex MEMBER m_selectedIndex NOTIFY selectedIndexChanged) Q_PROPERTY(bool shadersUpToDate READ shadersUpToDate WRITE setShadersUpToDate NOTIFY shadersUpToDateChanged) Q_PROPERTY(QString qmlComponentString READ qmlComponentString) - + Q_PROPERTY(QString currentComposition READ currentComposition WRITE setCurrentComposition NOTIFY currentCompositionChanged) public: EffectMakerModel(QObject *parent = nullptr); @@ -86,6 +85,11 @@ public: Q_INVOKABLE void exportComposition(const QString &name); Q_INVOKABLE void exportResources(const QString &name); + void openComposition(const QString &path); + + QString currentComposition() const; + void setCurrentComposition(const QString &newCurrentComposition); + signals: void isEmptyChanged(); void selectedIndexChanged(int idx); @@ -93,6 +97,8 @@ signals: void shadersUpToDateChanged(); void shadersBaked(); + void currentCompositionChanged(); + private: enum Roles { NameRole = Qt::UserRole + 1, @@ -138,8 +144,6 @@ private: QString generateFragmentShader(bool includeUniforms = true); void handleQsbProcessExit(Utils::Process *qsbProcess, const QString &shader); QString stripFileFromURL(const QString &urlString) const; - void updateImageWatchers(); - void clearImageWatchers(); QString getQmlEffectString(); void updateCustomUniforms(); @@ -179,7 +183,7 @@ private: QString m_previewEffectPropertiesString; QString m_qmlComponentString; bool m_loadComponentImages = true; - Utils::FileSystemWatcher m_fileWatcher; + QString m_currentComposition; const QRegularExpression m_spaceReg = QRegularExpression("\\s+"); }; diff --git a/src/plugins/effectmakernew/effectmakerview.cpp b/src/plugins/effectmakernew/effectmakerview.cpp index 40ba4987e67..637c12f6d5a 100644 --- a/src/plugins/effectmakernew/effectmakerview.cpp +++ b/src/plugins/effectmakernew/effectmakerview.cpp @@ -59,12 +59,15 @@ QmlDesigner::WidgetInfo EffectMakerView::widgetInfo() QmlDesigner::WidgetInfo::LeftPane, 0, tr("Effect Maker")); } -void EffectMakerView::customNotification(const AbstractView * /*view*/, - const QString & /*identifier*/, - const QList & /*nodeList*/, - const QList & /*data*/) +void EffectMakerView::customNotification([[maybe_unused]] const AbstractView *view, + const QString &identifier, + [[maybe_unused]] const QList &nodeList, + const QList &data) { - // TODO + if (identifier == "open_effectmaker_composition" && data.count() > 0) { + const QString compositionPath = data[0].toString(); + m_widget->effectMakerModel()->openComposition(compositionPath); + } } void EffectMakerView::modelAttached(QmlDesigner::Model *model) diff --git a/src/plugins/effectmakernew/uniform.cpp b/src/plugins/effectmakernew/uniform.cpp index c7e460e460e..be10cc7f421 100644 --- a/src/plugins/effectmakernew/uniform.cpp +++ b/src/plugins/effectmakernew/uniform.cpp @@ -6,13 +6,15 @@ #include "propertyhandler.h" +#include + #include #include #include namespace EffectMaker { -Uniform::Uniform(const QJsonObject &propObj, const QString &qenPath) +Uniform::Uniform(const QString &effectName, const QJsonObject &propObj, const QString &qenPath) : m_qenPath(qenPath) { QString value, defaultValue, minValue, maxValue; @@ -26,9 +28,11 @@ Uniform::Uniform(const QJsonObject &propObj, const QString &qenPath) if (m_displayName.isEmpty()) m_displayName = m_name; + QString resPath; if (m_type == Type::Sampler) { + resPath = getResourcePath(effectName, defaultValue, qenPath); if (!defaultValue.isEmpty()) - defaultValue = getResourcePath(defaultValue); + defaultValue = resPath; if (propObj.contains("enableMipmap")) m_enableMipmap = getBoolValue(propObj.value("enableMipmap"), false); // Update the mipmap property @@ -39,7 +43,7 @@ Uniform::Uniform(const QJsonObject &propObj, const QString &qenPath) if (propObj.contains("value")) { value = propObj.value("value").toString(); if (m_type == Type::Sampler) - value = getResourcePath(value); + value = resPath; } else { // QEN files don't store the current value, so with those use default value value = defaultValue; @@ -166,15 +170,20 @@ bool Uniform::getBoolValue(const QJsonValue &jsonValue, bool defaultValue) // Returns the path for a shader resource // Used with sampler types -QString Uniform::getResourcePath(const QString &value) const +QString Uniform::getResourcePath(const QString &effectName, const QString &value, const QString &qenPath) const { QString filePath = value; - QDir dir(m_qenPath); - dir.cdUp(); - QString absPath = dir.absoluteFilePath(filePath); - absPath = QDir::cleanPath(absPath); - absPath = QUrl::fromLocalFile(absPath).toString(); - return absPath; + if (qenPath.isEmpty()) { + const Utils::FilePath effectsResDir = QmlDesigner::ModelNodeOperations::getEffectsImportDirectory(); + return effectsResDir.pathAppended(effectName).pathAppended(value).toString(); + } else { + QDir dir(m_qenPath); + dir.cdUp(); + QString absPath = dir.absoluteFilePath(filePath); + absPath = QDir::cleanPath(absPath); + absPath = QUrl::fromLocalFile(absPath).toString(); + return absPath; + } } // Validation and setting values @@ -300,7 +309,7 @@ Uniform::Type Uniform::typeFromString(const QString &typeString) return Uniform::Type::Vec4; else if (typeString == "color") return Uniform::Type::Color; - else if (typeString == "image") + else if (typeString == "sampler2D" || typeString == "image") //TODO: change image to sample2D in all QENs return Uniform::Type::Sampler; else if (typeString == "define") return Uniform::Type::Define; diff --git a/src/plugins/effectmakernew/uniform.h b/src/plugins/effectmakernew/uniform.h index 8f14383bb4c..f5731af00a8 100644 --- a/src/plugins/effectmakernew/uniform.h +++ b/src/plugins/effectmakernew/uniform.h @@ -40,7 +40,7 @@ public: Define }; - Uniform(const QJsonObject &props, const QString &qenPath); + Uniform(const QString &effectName, const QJsonObject &props, const QString &qenPath); Type type() const; QString typeName() const; @@ -78,7 +78,7 @@ signals: private: QString mipmapPropertyName(const QString &name) const; bool getBoolValue(const QJsonValue &jsonValue, bool defaultValue); - QString getResourcePath(const QString &value) const; + QString getResourcePath(const QString &effectName, const QString &value, const QString &qenPath) const; void setValueData(const QString &value, const QString &defaultValue, const QString &minValue, const QString &maxValue); diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp index 48d10a34d20..a141c696978 100644 --- a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp +++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp @@ -13,6 +13,9 @@ #include "qmldesignerplugin.h" #include "theme.h" +#include +#include + #include #include @@ -364,9 +367,22 @@ QSet AssetsLibraryWidget::supportedAssetSuffixes(bool complex) return suffixes; } +bool isEffectMakerActivated() +{ + const QVector specs = ExtensionSystem::PluginManager::plugins(); + return std::find_if(specs.begin(), specs.end(), + [](ExtensionSystem::PluginSpec *spec) { + return spec->name() == "EffectMakerNew" && spec->isEffectivelyEnabled(); + }) + != specs.end(); +} + void AssetsLibraryWidget::openEffectMaker(const QString &filePath) { - ModelNodeOperations::openEffectMaker(filePath); + if (isEffectMakerActivated()) + m_assetsView->emitCustomNotification("open_effectmaker_composition", {}, {filePath}); + else + ModelNodeOperations::openEffectMaker(filePath); } QString AssetsLibraryWidget::qmlSourcesPath() From 9958987be77a960bf5d767a33f138f76cc133d5d Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Wed, 15 Nov 2023 17:13:48 +0100 Subject: [PATCH 234/242] QmlDesigner: Allow QtQuick 6.6 import Change-Id: I5f16c6dbd96a22fba540a3877c7eb3ebf2b61734 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Thomas Hartmann --- .../qmldesigner/designercore/model/texttomodelmerger.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp index dac9e2f6924..676011bdc57 100644 --- a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp +++ b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp @@ -73,7 +73,7 @@ bool isSupportedVersion(QmlDesigner::Version version) return version.minor <= 15; if (version.major == 6) - return version.minor <= 5; + return version.minor <= 6; return false; } From b7657a8d23b5f43bc9247438365ad892a589a07e Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Wed, 15 Nov 2023 17:42:15 +0100 Subject: [PATCH 235/242] QmlDesigner: Add source property to MediaPlayer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: Ie70e6f8c0e8d3ac316b98bc43f3cb7baecbb9d1b Reviewed-by: Henning Gründl --- .../QtMultimedia/MediaPlayerSection.qml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtMultimedia/MediaPlayerSection.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtMultimedia/MediaPlayerSection.qml index 4b66032bcf4..89a489f1cff 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtMultimedia/MediaPlayerSection.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtMultimedia/MediaPlayerSection.qml @@ -20,6 +20,21 @@ Section { // TODO position property, what should be the range?! SectionLayout { + + PropertyLabel { + text: qsTr("Source") + tooltip: qsTr("Adds an image from the local file system.") + } + + SecondColumnLayout { + UrlChooser { + backendValue: backendValues.source + filter: "*.avi *.mp4 *.mpeg *.wav" + } + + ExpandingSpacer {} + } + PropertyLabel { text: qsTr("Playback rate") } SecondColumnLayout { From 68be6228db4456115079abd181a92a9d6081ee3b Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Wed, 15 Nov 2023 15:43:25 +0200 Subject: [PATCH 236/242] QmlDesigner: Icon font updates Added: - deletepermanently_medium.svg - alphabetical_medium.svg - assignTo_medium.svg - comboBox_medium.svg - createObject_medium.svg - recent_medium.svg Modified: - addcolumnleft_medium.svg - addcolumnright_medium.svg - addrowabove_medium.svg - addrowbelow_medium.svg - deletecolumn_medium.svg - deleterow_medium.svg - export_medium.svg - import_medium.svg - tableView_medium.svg Removed: - downloadcsv_large.svg - downloadcsv_medium.svg - downloadjson_large.svg - downloadjson_medium.svg - uploadcsv_large.svg - uploadcsv_medium.svg - uploadjson_large.svg - uploadjson_medium.svg Change-Id: Ib5fb5ee5dfb3224ad99ee895d83a8910a5cc18c5 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Miikka Heikkinen --- .../imports/StudioTheme/InternalConstants.qml | 660 +++++++++--------- .../imports/StudioTheme/icons.ttf | Bin 66888 -> 65152 bytes .../components/componentcore/theme.h | 14 +- 3 files changed, 335 insertions(+), 339 deletions(-) diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/InternalConstants.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/InternalConstants.qml index eb142b6dd3f..1f7351a8a00 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/InternalConstants.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/InternalConstants.qml @@ -44,337 +44,335 @@ QtObject { readonly property string alignToObject_small: "\u003D" readonly property string alignToView_medium: "\u003E" readonly property string alignTop: "\u003F" - readonly property string anchorBaseline: "\u0040" - readonly property string anchorBottom: "\u0041" - readonly property string anchorFill: "\u0042" - readonly property string anchorLeft: "\u0043" - readonly property string anchorRight: "\u0044" - readonly property string anchorTop: "\u0045" - readonly property string anchors_small: "\u0046" - readonly property string animatedProperty: "\u0047" - readonly property string annotationBubble: "\u0048" - readonly property string annotationDecal: "\u0049" - readonly property string annotations_large: "\u004A" - readonly property string annotations_small: "\u004B" - readonly property string applyMaterialToSelected: "\u004C" - readonly property string apply_medium: "\u004D" - readonly property string apply_small: "\u004E" - readonly property string arrange_small: "\u004F" - readonly property string arrow_small: "\u0050" - readonly property string assign: "\u0051" - readonly property string assignTo: "\u0052" - readonly property string attach_medium: "\u0053" - readonly property string back_medium: "\u0054" - readonly property string backspace_small: "\u0055" - readonly property string bakeLights_medium: "\u0056" - readonly property string bevelAll: "\u0057" - readonly property string bevelCorner: "\u0058" - readonly property string bezier_medium: "\u0059" - readonly property string binding_medium: "\u005A" - readonly property string bounds_small: "\u005B" - readonly property string branch_medium: "\u005C" - readonly property string camera_small: "\u005D" - readonly property string centerHorizontal: "\u005E" - readonly property string centerVertical: "\u005F" - readonly property string cleanLogs_medium: "\u0060" - readonly property string closeCross: "\u0061" - readonly property string closeFile_large: "\u0062" - readonly property string closeLink: "\u0063" - readonly property string close_small: "\u0064" - readonly property string code: "\u0065" - readonly property string codeEditor_medium: "\u0066" - readonly property string codeview_medium: "\u0067" - readonly property string colorPopupClose: "\u0068" - readonly property string colorSelection_medium: "\u0069" - readonly property string columnsAndRows: "\u006A" - readonly property string cone_medium: "\u006B" - readonly property string cone_small: "\u006C" - readonly property string connection_small: "\u006D" - readonly property string connections_medium: "\u006E" - readonly property string copyLink: "\u006F" - readonly property string copyStyle: "\u0070" - readonly property string copy_small: "\u0071" - readonly property string cornerA: "\u0072" - readonly property string cornerB: "\u0073" - readonly property string cornersAll: "\u0074" - readonly property string createComponent_large: "\u0075" - readonly property string createComponent_small: "\u0076" - readonly property string create_medium: "\u0077" - readonly property string create_small: "\u0078" - readonly property string cube_medium: "\u0079" - readonly property string cube_small: "\u007A" - readonly property string curveDesigner: "\u007B" - readonly property string curveDesigner_medium: "\u007C" - readonly property string curveEditor: "\u007D" - readonly property string customMaterialEditor: "\u007E" - readonly property string cylinder_medium: "\u007F" - readonly property string cylinder_small: "\u0080" - readonly property string decisionNode: "\u0081" - readonly property string deleteColumn: "\u0082" - readonly property string deleteMaterial: "\u0083" - readonly property string deleteRow: "\u0084" - readonly property string deleteTable: "\u0085" - readonly property string delete_medium: "\u0086" - readonly property string delete_small: "\u0087" - readonly property string deletecolumn_medium: "\u0088" - readonly property string deleterow_medium: "\u0089" - readonly property string designMode_large: "\u008A" - readonly property string detach: "\u008B" - readonly property string directionalLight_small: "\u008C" - readonly property string distributeBottom: "\u008D" - readonly property string distributeCenterHorizontal: "\u008E" - readonly property string distributeCenterVertical: "\u008F" - readonly property string distributeLeft: "\u0090" - readonly property string distributeOriginBottomRight: "\u0091" - readonly property string distributeOriginCenter: "\u0092" - readonly property string distributeOriginNone: "\u0093" - readonly property string distributeOriginTopLeft: "\u0094" - readonly property string distributeRight: "\u0095" - readonly property string distributeSpacingHorizontal: "\u0096" - readonly property string distributeSpacingVertical: "\u0097" - readonly property string distributeTop: "\u0098" - readonly property string download: "\u0099" - readonly property string downloadUnavailable: "\u009A" - readonly property string downloadUpdate: "\u009B" - readonly property string downloadcsv_large: "\u009D" - readonly property string downloadcsv_medium: "\u009E" - readonly property string downloaded: "\u009F" - readonly property string downloadjson_large: "\u00A0" - readonly property string downloadjson_medium: "\u00A1" - readonly property string dragmarks: "\u00A2" - readonly property string duplicate_small: "\u00A3" - readonly property string edit: "\u00A4" - readonly property string editComponent_large: "\u00A5" - readonly property string editComponent_small: "\u00A6" - readonly property string editLightOff_medium: "\u00A7" - readonly property string editLightOn_medium: "\u00A8" - readonly property string edit_medium: "\u00A9" - readonly property string edit_small: "\u00AA" - readonly property string effects: "\u00AB" - readonly property string events_small: "\u00AC" - readonly property string export_medium: "\u00AE" - readonly property string eyeDropper: "\u00AF" - readonly property string favorite: "\u00B0" - readonly property string fitAll_medium: "\u00B1" - readonly property string fitSelected_small: "\u00B2" - readonly property string fitSelection_medium: "\u00B3" - readonly property string fitToView_medium: "\u00B4" - readonly property string flowAction: "\u00B5" - readonly property string flowTransition: "\u00B6" - readonly property string fontStyleBold: "\u00B7" - readonly property string fontStyleItalic: "\u00B8" - readonly property string fontStyleStrikethrough: "\u00B9" - readonly property string fontStyleUnderline: "\u00BA" - readonly property string forward_medium: "\u00BB" - readonly property string globalOrient_medium: "\u00BC" - readonly property string gradient: "\u00BD" - readonly property string gridView: "\u00BE" - readonly property string grid_medium: "\u00BF" - readonly property string group_small: "\u00C0" - readonly property string help: "\u00C1" - readonly property string home_large: "\u00C2" - readonly property string idAliasOff: "\u00C3" - readonly property string idAliasOn: "\u00C4" - readonly property string import_medium: "\u00C5" - readonly property string imported: "\u00C6" - readonly property string importedModels_small: "\u00C7" - readonly property string infinity: "\u00C8" - readonly property string invisible_medium: "\u00C9" - readonly property string invisible_small: "\u00CA" - readonly property string jumpToCode_medium: "\u00CB" - readonly property string jumpToCode_small: "\u00CC" - readonly property string keyframe: "\u00CD" - readonly property string languageList_medium: "\u00CE" - readonly property string layouts_small: "\u00CF" - readonly property string lights_small: "\u00D0" - readonly property string linear_medium: "\u00D1" - readonly property string linkTriangle: "\u00D2" - readonly property string linked: "\u00D3" - readonly property string listView: "\u00D4" - readonly property string listView_medium: "\u00D5" - readonly property string list_medium: "\u00D6" - readonly property string localOrient_medium: "\u00D7" - readonly property string lockOff: "\u00D8" - readonly property string lockOn: "\u00D9" - readonly property string loopPlayback_medium: "\u00DA" - readonly property string materialBrowser_medium: "\u00DB" - readonly property string materialPreviewEnvironment: "\u00DC" - readonly property string materialPreviewModel: "\u00DD" - readonly property string material_medium: "\u00DE" - readonly property string maxBar_small: "\u00DF" - readonly property string mergeCells: "\u00E0" - readonly property string merge_small: "\u00E1" - readonly property string minus: "\u00E2" - readonly property string mirror: "\u00E3" - readonly property string more_medium: "\u00E4" - readonly property string mouseArea_small: "\u00E5" - readonly property string moveDown_medium: "\u00E6" - readonly property string moveInwards_medium: "\u00E7" - readonly property string moveUp_medium: "\u00E8" - readonly property string moveUpwards_medium: "\u00E9" - readonly property string move_medium: "\u00EA" - readonly property string newMaterial: "\u00EB" - readonly property string nextFile_large: "\u00EC" - readonly property string normalBar_small: "\u00ED" - readonly property string openLink: "\u00EE" - readonly property string openMaterialBrowser: "\u00EF" - readonly property string orientation: "\u00F0" - readonly property string orthCam_medium: "\u00F1" - readonly property string orthCam_small: "\u00F2" - readonly property string paddingEdge: "\u00F3" - readonly property string paddingFrame: "\u00F4" - readonly property string particleAnimation_medium: "\u00F5" - readonly property string pasteStyle: "\u00F6" - readonly property string paste_small: "\u00F7" - readonly property string pause: "\u00F8" - readonly property string pause_medium: "\u00F9" - readonly property string perspectiveCam_medium: "\u00FA" - readonly property string perspectiveCam_small: "\u00FB" - readonly property string pin: "\u00FC" - readonly property string plane_medium: "\u00FD" - readonly property string plane_small: "\u00FE" - readonly property string play: "\u00FF" - readonly property string playFill_medium: "\u0100" - readonly property string playOutline_medium: "\u0101" - readonly property string plus: "\u0102" - readonly property string pointLight_small: "\u0103" - readonly property string positioners_small: "\u0104" - readonly property string previewEnv_medium: "\u0105" - readonly property string previousFile_large: "\u0106" - readonly property string promote: "\u0107" - readonly property string properties_medium: "\u0108" - readonly property string readOnly: "\u0109" - readonly property string recordFill_medium: "\u010A" - readonly property string recordOutline_medium: "\u010B" - readonly property string redo: "\u010C" - readonly property string reload_medium: "\u010D" - readonly property string remove_medium: "\u010E" - readonly property string remove_small: "\u010F" - readonly property string rename_small: "\u0110" - readonly property string replace_small: "\u0111" - readonly property string resetView_small: "\u0112" - readonly property string restartParticles_medium: "\u0113" - readonly property string reverseOrder_medium: "\u0114" - readonly property string roatate_medium: "\u0115" - readonly property string rotationFill: "\u0116" - readonly property string rotationOutline: "\u0117" - readonly property string runProjFill_large: "\u0118" - readonly property string runProjOutline_large: "\u0119" - readonly property string s_anchors: "\u011A" - readonly property string s_annotations: "\u011B" - readonly property string s_arrange: "\u011C" - readonly property string s_boundingBox: "\u011D" - readonly property string s_component: "\u011E" - readonly property string s_connections: "\u011F" - readonly property string s_edit: "\u0120" - readonly property string s_enterComponent: "\u0121" - readonly property string s_eventList: "\u0122" - readonly property string s_group: "\u0123" - readonly property string s_layouts: "\u0124" - readonly property string s_merging: "\u0125" - readonly property string s_mouseArea: "\u0126" - readonly property string s_positioners: "\u0127" - readonly property string s_selection: "\u0128" - readonly property string s_snapping: "\u0129" - readonly property string s_timeline: "\u012A" - readonly property string s_visibility: "\u012B" - readonly property string saveLogs_medium: "\u012C" - readonly property string scale_medium: "\u012D" - readonly property string search: "\u012E" - readonly property string search_small: "\u012F" - readonly property string sectionToggle: "\u0130" - readonly property string selectFill_medium: "\u0131" - readonly property string selectOutline_medium: "\u0132" - readonly property string selectParent_small: "\u0133" - readonly property string selection_small: "\u0134" - readonly property string settings_medium: "\u0135" - readonly property string signal_small: "\u0136" - readonly property string snapping_conf_medium: "\u0137" - readonly property string snapping_medium: "\u0138" - readonly property string snapping_small: "\u0139" - readonly property string sortascending_medium: "\u013A" - readonly property string sortdescending_medium: "\u013B" - readonly property string sphere_medium: "\u013C" - readonly property string sphere_small: "\u013D" - readonly property string splitColumns: "\u013E" - readonly property string splitRows: "\u013F" - readonly property string splitScreen_medium: "\u0140" - readonly property string spotLight_small: "\u0141" - readonly property string stackedContainer_small: "\u0142" - readonly property string startNode: "\u0143" - readonly property string step_medium: "\u0144" - readonly property string stop_medium: "\u0145" - readonly property string tableView_medium: "\u0146" - readonly property string testIcon: "\u0147" - readonly property string textAlignBottom: "\u0148" - readonly property string textAlignCenter: "\u0149" - readonly property string textAlignJustified: "\u014A" - readonly property string textAlignLeft: "\u014B" - readonly property string textAlignMiddle: "\u014C" - readonly property string textAlignRight: "\u014D" - readonly property string textAlignTop: "\u014E" - readonly property string textBulletList: "\u014F" - readonly property string textFullJustification: "\u0150" - readonly property string textNumberedList: "\u0151" - readonly property string textures_medium: "\u0152" - readonly property string tickIcon: "\u0153" - readonly property string tickMark_small: "\u0154" - readonly property string timeline_small: "\u0155" - readonly property string toEndFrame_medium: "\u0156" - readonly property string toNextFrame_medium: "\u0157" - readonly property string toPrevFrame_medium: "\u0158" - readonly property string toStartFrame_medium: "\u0159" - readonly property string topToolbar_annotations: "\u015A" - readonly property string topToolbar_closeFile: "\u015B" - readonly property string topToolbar_designMode: "\u015C" - readonly property string topToolbar_enterComponent: "\u015D" - readonly property string topToolbar_home: "\u015E" - readonly property string topToolbar_makeComponent: "\u015F" - readonly property string topToolbar_navFile: "\u0160" - readonly property string topToolbar_runProject: "\u0161" - readonly property string translationCreateFiles: "\u0162" - readonly property string translationCreateReport: "\u0163" - readonly property string translationExport: "\u0164" - readonly property string translationImport: "\u0165" - readonly property string translationSelectLanguages: "\u0166" - readonly property string translationTest: "\u0167" - readonly property string transparent: "\u0168" - readonly property string triState: "\u0169" - readonly property string triangleArcA: "\u016A" - readonly property string triangleArcB: "\u016B" - readonly property string triangleCornerA: "\u016C" - readonly property string triangleCornerB: "\u016D" - readonly property string unLinked: "\u016E" - readonly property string undo: "\u016F" - readonly property string unify_medium: "\u0170" - readonly property string unpin: "\u0171" - readonly property string upDownIcon: "\u0172" - readonly property string upDownSquare2: "\u0173" - readonly property string updateAvailable_medium: "\u0174" - readonly property string updateContent_medium: "\u0175" - readonly property string uploadcsv_large: "\u0176" - readonly property string uploadcsv_medium: "\u0177" - readonly property string uploadjson_large: "\u0178" - readonly property string uploadjson_medium: "\u0179" - readonly property string visibilityOff: "\u017A" - readonly property string visibilityOn: "\u017B" - readonly property string visible_medium: "\u017C" - readonly property string visible_small: "\u017D" - readonly property string warning_medium: "\u017E" - readonly property string wildcard: "\u017F" - readonly property string wizardsAutomotive: "\u0180" - readonly property string wizardsDesktop: "\u0181" - readonly property string wizardsGeneric: "\u0182" - readonly property string wizardsMcuEmpty: "\u0183" - readonly property string wizardsMcuGraph: "\u0184" - readonly property string wizardsMobile: "\u0185" - readonly property string wizardsUnknown: "\u0186" - readonly property string zoomAll: "\u0187" - readonly property string zoomIn: "\u0188" - readonly property string zoomIn_medium: "\u0189" - readonly property string zoomOut: "\u018A" - readonly property string zoomOut_medium: "\u018B" - readonly property string zoomSelection: "\u018C" + readonly property string alphabetical_medium: "\u0040" + readonly property string anchorBaseline: "\u0041" + readonly property string anchorBottom: "\u0042" + readonly property string anchorFill: "\u0043" + readonly property string anchorLeft: "\u0044" + readonly property string anchorRight: "\u0045" + readonly property string anchorTop: "\u0046" + readonly property string anchors_small: "\u0047" + readonly property string animatedProperty: "\u0048" + readonly property string annotationBubble: "\u0049" + readonly property string annotationDecal: "\u004A" + readonly property string annotations_large: "\u004B" + readonly property string annotations_small: "\u004C" + readonly property string applyMaterialToSelected: "\u004D" + readonly property string apply_medium: "\u004E" + readonly property string apply_small: "\u004F" + readonly property string arrange_small: "\u0050" + readonly property string arrow_small: "\u0051" + readonly property string assign: "\u0052" + readonly property string assignTo: "\u0053" + readonly property string assignTo_medium: "\u0054" + readonly property string attach_medium: "\u0055" + readonly property string back_medium: "\u0056" + readonly property string backspace_small: "\u0057" + readonly property string bakeLights_medium: "\u0058" + readonly property string bevelAll: "\u0059" + readonly property string bevelCorner: "\u005A" + readonly property string bezier_medium: "\u005B" + readonly property string binding_medium: "\u005C" + readonly property string bounds_small: "\u005D" + readonly property string branch_medium: "\u005E" + readonly property string camera_small: "\u005F" + readonly property string centerHorizontal: "\u0060" + readonly property string centerVertical: "\u0061" + readonly property string cleanLogs_medium: "\u0062" + readonly property string closeCross: "\u0063" + readonly property string closeFile_large: "\u0064" + readonly property string closeLink: "\u0065" + readonly property string close_small: "\u0066" + readonly property string code: "\u0067" + readonly property string codeEditor_medium: "\u0068" + readonly property string codeview_medium: "\u0069" + readonly property string colorPopupClose: "\u006A" + readonly property string colorSelection_medium: "\u006B" + readonly property string columnsAndRows: "\u006C" + readonly property string comboBox_medium: "\u006D" + readonly property string cone_medium: "\u006E" + readonly property string cone_small: "\u006F" + readonly property string connection_small: "\u0070" + readonly property string connections_medium: "\u0071" + readonly property string copyLink: "\u0072" + readonly property string copyStyle: "\u0073" + readonly property string copy_small: "\u0074" + readonly property string cornerA: "\u0075" + readonly property string cornerB: "\u0076" + readonly property string cornersAll: "\u0077" + readonly property string createComponent_large: "\u0078" + readonly property string createComponent_small: "\u0079" + readonly property string createObject_medium: "\u007A" + readonly property string create_medium: "\u007B" + readonly property string create_small: "\u007C" + readonly property string cube_medium: "\u007D" + readonly property string cube_small: "\u007E" + readonly property string curveDesigner: "\u007F" + readonly property string curveDesigner_medium: "\u0080" + readonly property string curveEditor: "\u0081" + readonly property string customMaterialEditor: "\u0082" + readonly property string cylinder_medium: "\u0083" + readonly property string cylinder_small: "\u0084" + readonly property string decisionNode: "\u0085" + readonly property string deleteColumn: "\u0086" + readonly property string deleteMaterial: "\u0087" + readonly property string deleteRow: "\u0088" + readonly property string deleteTable: "\u0089" + readonly property string delete_medium: "\u008A" + readonly property string delete_small: "\u008B" + readonly property string deletecolumn_medium: "\u008C" + readonly property string deletepermanently_medium: "\u008D" + readonly property string deleterow_medium: "\u008E" + readonly property string designMode_large: "\u008F" + readonly property string detach: "\u0090" + readonly property string directionalLight_small: "\u0091" + readonly property string distributeBottom: "\u0092" + readonly property string distributeCenterHorizontal: "\u0093" + readonly property string distributeCenterVertical: "\u0094" + readonly property string distributeLeft: "\u0095" + readonly property string distributeOriginBottomRight: "\u0096" + readonly property string distributeOriginCenter: "\u0097" + readonly property string distributeOriginNone: "\u0098" + readonly property string distributeOriginTopLeft: "\u0099" + readonly property string distributeRight: "\u009A" + readonly property string distributeSpacingHorizontal: "\u009B" + readonly property string distributeSpacingVertical: "\u009D" + readonly property string distributeTop: "\u009E" + readonly property string download: "\u009F" + readonly property string downloadUnavailable: "\u00A0" + readonly property string downloadUpdate: "\u00A1" + readonly property string downloaded: "\u00A2" + readonly property string dragmarks: "\u00A3" + readonly property string duplicate_small: "\u00A4" + readonly property string edit: "\u00A5" + readonly property string editComponent_large: "\u00A6" + readonly property string editComponent_small: "\u00A7" + readonly property string editLightOff_medium: "\u00A8" + readonly property string editLightOn_medium: "\u00A9" + readonly property string edit_medium: "\u00AA" + readonly property string edit_small: "\u00AB" + readonly property string effects: "\u00AC" + readonly property string events_small: "\u00AE" + readonly property string export_medium: "\u00AF" + readonly property string eyeDropper: "\u00B0" + readonly property string favorite: "\u00B1" + readonly property string fitAll_medium: "\u00B2" + readonly property string fitSelected_small: "\u00B3" + readonly property string fitSelection_medium: "\u00B4" + readonly property string fitToView_medium: "\u00B5" + readonly property string flowAction: "\u00B6" + readonly property string flowTransition: "\u00B7" + readonly property string fontStyleBold: "\u00B8" + readonly property string fontStyleItalic: "\u00B9" + readonly property string fontStyleStrikethrough: "\u00BA" + readonly property string fontStyleUnderline: "\u00BB" + readonly property string forward_medium: "\u00BC" + readonly property string globalOrient_medium: "\u00BD" + readonly property string gradient: "\u00BE" + readonly property string gridView: "\u00BF" + readonly property string grid_medium: "\u00C0" + readonly property string group_small: "\u00C1" + readonly property string help: "\u00C2" + readonly property string home_large: "\u00C3" + readonly property string idAliasOff: "\u00C4" + readonly property string idAliasOn: "\u00C5" + readonly property string import_medium: "\u00C6" + readonly property string imported: "\u00C7" + readonly property string importedModels_small: "\u00C8" + readonly property string infinity: "\u00C9" + readonly property string invisible_medium: "\u00CA" + readonly property string invisible_small: "\u00CB" + readonly property string jumpToCode_medium: "\u00CC" + readonly property string jumpToCode_small: "\u00CD" + readonly property string keyframe: "\u00CE" + readonly property string languageList_medium: "\u00CF" + readonly property string layouts_small: "\u00D0" + readonly property string lights_small: "\u00D1" + readonly property string linear_medium: "\u00D2" + readonly property string linkTriangle: "\u00D3" + readonly property string linked: "\u00D4" + readonly property string listView: "\u00D5" + readonly property string listView_medium: "\u00D6" + readonly property string list_medium: "\u00D7" + readonly property string localOrient_medium: "\u00D8" + readonly property string lockOff: "\u00D9" + readonly property string lockOn: "\u00DA" + readonly property string loopPlayback_medium: "\u00DB" + readonly property string materialBrowser_medium: "\u00DC" + readonly property string materialPreviewEnvironment: "\u00DD" + readonly property string materialPreviewModel: "\u00DE" + readonly property string material_medium: "\u00DF" + readonly property string maxBar_small: "\u00E0" + readonly property string mergeCells: "\u00E1" + readonly property string merge_small: "\u00E2" + readonly property string minus: "\u00E3" + readonly property string mirror: "\u00E4" + readonly property string more_medium: "\u00E5" + readonly property string mouseArea_small: "\u00E6" + readonly property string moveDown_medium: "\u00E7" + readonly property string moveInwards_medium: "\u00E8" + readonly property string moveUp_medium: "\u00E9" + readonly property string moveUpwards_medium: "\u00EA" + readonly property string move_medium: "\u00EB" + readonly property string newMaterial: "\u00EC" + readonly property string nextFile_large: "\u00ED" + readonly property string normalBar_small: "\u00EE" + readonly property string openLink: "\u00EF" + readonly property string openMaterialBrowser: "\u00F0" + readonly property string orientation: "\u00F1" + readonly property string orthCam_medium: "\u00F2" + readonly property string orthCam_small: "\u00F3" + readonly property string paddingEdge: "\u00F4" + readonly property string paddingFrame: "\u00F5" + readonly property string particleAnimation_medium: "\u00F6" + readonly property string pasteStyle: "\u00F7" + readonly property string paste_small: "\u00F8" + readonly property string pause: "\u00F9" + readonly property string pause_medium: "\u00FA" + readonly property string perspectiveCam_medium: "\u00FB" + readonly property string perspectiveCam_small: "\u00FC" + readonly property string pin: "\u00FD" + readonly property string plane_medium: "\u00FE" + readonly property string plane_small: "\u00FF" + readonly property string play: "\u0100" + readonly property string playFill_medium: "\u0101" + readonly property string playOutline_medium: "\u0102" + readonly property string plus: "\u0103" + readonly property string pointLight_small: "\u0104" + readonly property string positioners_small: "\u0105" + readonly property string previewEnv_medium: "\u0106" + readonly property string previousFile_large: "\u0107" + readonly property string promote: "\u0108" + readonly property string properties_medium: "\u0109" + readonly property string readOnly: "\u010A" + readonly property string recent_medium: "\u010B" + readonly property string recordFill_medium: "\u010C" + readonly property string recordOutline_medium: "\u010D" + readonly property string redo: "\u010E" + readonly property string reload_medium: "\u010F" + readonly property string remove_medium: "\u0110" + readonly property string remove_small: "\u0111" + readonly property string rename_small: "\u0112" + readonly property string replace_small: "\u0113" + readonly property string resetView_small: "\u0114" + readonly property string restartParticles_medium: "\u0115" + readonly property string reverseOrder_medium: "\u0116" + readonly property string roatate_medium: "\u0117" + readonly property string rotationFill: "\u0118" + readonly property string rotationOutline: "\u0119" + readonly property string runProjFill_large: "\u011A" + readonly property string runProjOutline_large: "\u011B" + readonly property string s_anchors: "\u011C" + readonly property string s_annotations: "\u011D" + readonly property string s_arrange: "\u011E" + readonly property string s_boundingBox: "\u011F" + readonly property string s_component: "\u0120" + readonly property string s_connections: "\u0121" + readonly property string s_edit: "\u0122" + readonly property string s_enterComponent: "\u0123" + readonly property string s_eventList: "\u0124" + readonly property string s_group: "\u0125" + readonly property string s_layouts: "\u0126" + readonly property string s_merging: "\u0127" + readonly property string s_mouseArea: "\u0128" + readonly property string s_positioners: "\u0129" + readonly property string s_selection: "\u012A" + readonly property string s_snapping: "\u012B" + readonly property string s_timeline: "\u012C" + readonly property string s_visibility: "\u012D" + readonly property string saveLogs_medium: "\u012E" + readonly property string scale_medium: "\u012F" + readonly property string search: "\u0130" + readonly property string search_small: "\u0131" + readonly property string sectionToggle: "\u0132" + readonly property string selectFill_medium: "\u0133" + readonly property string selectOutline_medium: "\u0134" + readonly property string selectParent_small: "\u0135" + readonly property string selection_small: "\u0136" + readonly property string settings_medium: "\u0137" + readonly property string signal_small: "\u0138" + readonly property string snapping_conf_medium: "\u0139" + readonly property string snapping_medium: "\u013A" + readonly property string snapping_small: "\u013B" + readonly property string sortascending_medium: "\u013C" + readonly property string sortdescending_medium: "\u013D" + readonly property string sphere_medium: "\u013E" + readonly property string sphere_small: "\u013F" + readonly property string splitColumns: "\u0140" + readonly property string splitRows: "\u0141" + readonly property string splitScreen_medium: "\u0142" + readonly property string spotLight_small: "\u0143" + readonly property string stackedContainer_small: "\u0144" + readonly property string startNode: "\u0145" + readonly property string step_medium: "\u0146" + readonly property string stop_medium: "\u0147" + readonly property string tableView_medium: "\u0148" + readonly property string testIcon: "\u0149" + readonly property string textAlignBottom: "\u014A" + readonly property string textAlignCenter: "\u014B" + readonly property string textAlignJustified: "\u014C" + readonly property string textAlignLeft: "\u014D" + readonly property string textAlignMiddle: "\u014E" + readonly property string textAlignRight: "\u014F" + readonly property string textAlignTop: "\u0150" + readonly property string textBulletList: "\u0151" + readonly property string textFullJustification: "\u0152" + readonly property string textNumberedList: "\u0153" + readonly property string textures_medium: "\u0154" + readonly property string tickIcon: "\u0155" + readonly property string tickMark_small: "\u0156" + readonly property string timeline_small: "\u0157" + readonly property string toEndFrame_medium: "\u0158" + readonly property string toNextFrame_medium: "\u0159" + readonly property string toPrevFrame_medium: "\u015A" + readonly property string toStartFrame_medium: "\u015B" + readonly property string topToolbar_annotations: "\u015C" + readonly property string topToolbar_closeFile: "\u015D" + readonly property string topToolbar_designMode: "\u015E" + readonly property string topToolbar_enterComponent: "\u015F" + readonly property string topToolbar_home: "\u0160" + readonly property string topToolbar_makeComponent: "\u0161" + readonly property string topToolbar_navFile: "\u0162" + readonly property string topToolbar_runProject: "\u0163" + readonly property string translationCreateFiles: "\u0164" + readonly property string translationCreateReport: "\u0165" + readonly property string translationExport: "\u0166" + readonly property string translationImport: "\u0167" + readonly property string translationSelectLanguages: "\u0168" + readonly property string translationTest: "\u0169" + readonly property string transparent: "\u016A" + readonly property string triState: "\u016B" + readonly property string triangleArcA: "\u016C" + readonly property string triangleArcB: "\u016D" + readonly property string triangleCornerA: "\u016E" + readonly property string triangleCornerB: "\u016F" + readonly property string unLinked: "\u0170" + readonly property string undo: "\u0171" + readonly property string unify_medium: "\u0172" + readonly property string unpin: "\u0173" + readonly property string upDownIcon: "\u0174" + readonly property string upDownSquare2: "\u0175" + readonly property string updateAvailable_medium: "\u0176" + readonly property string updateContent_medium: "\u0177" + readonly property string visibilityOff: "\u0178" + readonly property string visibilityOn: "\u0179" + readonly property string visible_medium: "\u017A" + readonly property string visible_small: "\u017B" + readonly property string warning_medium: "\u017C" + readonly property string wildcard: "\u017D" + readonly property string wizardsAutomotive: "\u017E" + readonly property string wizardsDesktop: "\u017F" + readonly property string wizardsGeneric: "\u0180" + readonly property string wizardsMcuEmpty: "\u0181" + readonly property string wizardsMcuGraph: "\u0182" + readonly property string wizardsMobile: "\u0183" + readonly property string wizardsUnknown: "\u0184" + readonly property string zoomAll: "\u0185" + readonly property string zoomIn: "\u0186" + readonly property string zoomIn_medium: "\u0187" + readonly property string zoomOut: "\u0188" + readonly property string zoomOut_medium: "\u0189" + readonly property string zoomSelection: "\u018A" readonly property font iconFont: Qt.font({ "family": controlIcons.name, diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/icons.ttf b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/icons.ttf index de1490f58c595a0a5e85bd0b56db4fc4a181f358..c622c1950af00d87bcc5bd0c6007d1f297d169a8 100644 GIT binary patch delta 3892 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 I^%e7)0SN!!F8}}l delta 5751 zcmZqp%Y34XrJjM2fq{XSp@D&!A;8To#CO4qc`^))tU3$~3^M+~`bLop(kvJl7$X=M z7!s0m6AQG$b;KALSmrP=Foh+Tl_)SMGF37#uzg`*U|>m4EH3!}pMjZykyV9(fq^4E zr!sAGjI*PYy6JFkL9fFD?n^IPjc-;rRpx2H8nq&oMGEI4}0jiRZWZ%D~P1f`Ng7;bEcW zH3-dQd3_S2EpslzmB|i_(TqKl=P+hYW@plvY{;ZLIe|%Wax;^{CI1)H&INouxaq4k~an9kqz%`5O5w`~SGVXIc4m@Xg z#dsZfn|S~5Y4BO_webDppT+-AAV{D}V3EKtK?A`w!8w9AgtUZa2>laQ5I!V)ON6gp z#7?9^rt#lRPEmB(+UiLb^nHmGlD{C7B4B zHklK$O0p5MeR36Y*W{bzr^&CAKPG=ifl0wc!AGG;p-o|t!ajvdiW-UqihGm}C>tpk zsmxH7Q_WC4p(dafqP9$3OT9z=iiV6vfkyomO&QHB%{I+bS}Ix-w3W0yv}b9b(te<$ zr4ymkpmR&tOgBeQM9)p{n!bsCiT)M?CIbb7MFw9C{R~$bJ~2`;vN8H&>|{L6_=)i^ z69!#^Rc#oMnUM49iWHcdS^fjI3g;x~z6sYgngP z_taaTvQe<P3_Av+{{PmWklM9!+5C%J05Wt-1%K4hzBW?=Yl!Tf?*fq|DnkU@fhfzen{(bQNF z3K@+Bm6^@Ol|_|JnOE8U?YFnvW4Gtels$Xw=FF+FXIA(#)t*`B&lM16wD`xv^ls0d zJ$pcz1;bonn300UqKblyg35yC%A(5krpo3ZBkk<}_A{^A^Q+drW)8Cg#6Xat_RQJ; zc>eJ)e)$D57sF6dMN=3w7F1+ZG*wg;H5OGCRR`&XVEaE)nO}fR_$#z$&z`P5c6&f} zF}>Sk2eK=KfdRu%K~qIRWw^P*qKc;M>gFJ0nX~O-F5B~u2jrqXOpjoJ@;6}5o?oCq zVPM$&TPTcu^G7KGuFY#zW%xFynxwFA_O&tMt_OuPH-i9!7=sK01FJf_y1BZ!xVbpH zIHR#3yE>zwvZ=8kqo}f=u^>}%eZ4gd{X6-qww9^z*Pg!tzwAzcMC+_8E3ND5ne+d3 z)YLF;`{!lPSpKimo^jj1P6h@Rc$~uRVq_O*S7#4`*vDw9D9+BPD5`9#$Y=}-Lgp7p zX8xJTSW#2+&x>)J{l8Aea{GT?jOC20K?A~u78Dg>(`HmvS2Hs=Gd7ZE6f`k25)&0Pw_}uJ zehG&T~KV-yz?7gbgR2`Vdr z6v#4)*NcJlfpyw3nwqGq=`(^An3{l0GByIsD(f+-t0^n7iHeBHGlDgUih%S(EKpZ7 zH8&PFHaBKhj$~9Z(PfkrVm1?!WYjfLVf+_(ij9ebgM)`-E}t0RTy{|bCbm=TlK=j) zv2k+=@p5t2v$HblGV<_4vaxb=@(FPBu(91?WMN@r z;bQ0ETPs<`xx^6BQKT z;bvnyYihz|V(e>v!bF9UG4J0bAxTC?Ng)u6QKjDG&0}_9Hdf9Jj9Og0yj=gTZQx+y z5#o5v!PFwk%g4^n%EHOP!y+mmz{$zOEiS4cBQGW{!OAMa%O}9X#m&ja%E7_MCnBT5 z&cepR%*@Qf%+JBY#>UFd%FfQqBPhbp$H@UwA;`hQ!Yshc#lr%U;o%V$5#Z zs?(yY)1s65uUJ&j*rfeiOhT;#|X*>wv5K0L<&oy>a5`8 zFJ^22k~B9%PU`h0hR`emO52DGA}S(|J-w^5qU0_!b2Cuxl4TTuW-UHOSx9~aXGdE` z6LC{xabr_sQDt>yQOrD2k0Xu>LfVv1jKL-almxQRIn6MBJ4;vdJ z<7OTS2}MN-30_`aUI_^&35j}1OAa1BenB2yehv;EUIktr4)!#yTqb?ZJSJ>;26vW8 z=3rxI=j0TaHdRGfn3a`_i<^g!nT;EgG1%ETdDz%N>7SFEm!DBsfM0-vou5~Tqn?eG zm6?Nsmy3;!g`I_iorgzQSdfp4gO!zoi<_HQP@J2Ghg)1sUPeI-%+!~Yk&owNXJ_Y- z5|b6<*VwkNxhtx+r^lyV_biduClwrm3^Hw<9K> zf%27I9jMU&%3vU!p!~MS&hFQ*U;j?n+11t6Fj{QB8&ttwugD+jiLAn@<_FGkB>ApB<$E9b9YASvcJR?c6{3UKuS^142(oWK4Z2O+5He`3c$xdgJn2F0IK&;|ul zB|9?*2M0IDTz*mhxon~W%DQE3$paEpS8nT3y&n~j}?gN2=g2h^hA=K!@Rc!fB4SXcykxOtetGTg$V z{QR7pto1Bh96UU{^Vh7oeH&EWBH9%E3=#}73pkN3FMF3=Rw~3?2*& z;0&fLsICY~8zyGPjLL%S>gM9?>ZG

Y0~-nh}2%u-R$IFy6OM)!Dc~C-vWT?Cl9= z1r3>hXBmyz*g&L=#@~KM<5ZoE8+B6u^?_Rxb#-+$c6Pf#MVmdOoP)F`YHC2O3HJa0 z|AUGXNMiS8n9i`ip5ZvdWl*_JPKrluIv8Ly9>DcEa?`=U6usq8uLf#c)28V_Om?V; zmWz4}7`9jh!9TdJu%P9$2`zxOsV* z*tuAlnVFcGIoM&j0Ni>I;fJ*z*w|QDI5>H@*xA4tp`M3F2-LRbWM$>#-5(pu+9ysvim;i_D=OHwU!&HHV5xRL(iW1Xv6N|Eo zL7IhAN(*u_H_v*j!@l{(7c&k|;nD&OcZeXw5avJy!KTAqEDBKx65P!EH=1#B{68^n J_;9P`bpWnnHShoc diff --git a/src/plugins/qmldesigner/components/componentcore/theme.h b/src/plugins/qmldesigner/components/componentcore/theme.h index f363df71b26..c18f5a9b91d 100644 --- a/src/plugins/qmldesigner/components/componentcore/theme.h +++ b/src/plugins/qmldesigner/components/componentcore/theme.h @@ -52,6 +52,7 @@ public: alignToObject_small, alignToView_medium, alignTop, + alphabetical_medium, anchorBaseline, anchorBottom, anchorFill, @@ -71,6 +72,7 @@ public: arrow_small, assign, assignTo, + assignTo_medium, attach_medium, back_medium, backspace_small, @@ -95,6 +97,7 @@ public: colorPopupClose, colorSelection_medium, columnsAndRows, + comboBox_medium, cone_medium, cone_small, connection_small, @@ -107,6 +110,7 @@ public: cornersAll, createComponent_large, createComponent_small, + createObject_medium, create_medium, create_small, cube_medium, @@ -125,6 +129,7 @@ public: delete_medium, delete_small, deletecolumn_medium, + deletepermanently_medium, deleterow_medium, designMode_large, detach, @@ -144,11 +149,7 @@ public: download, downloadUnavailable, downloadUpdate, - downloadcsv_large, - downloadcsv_medium, downloaded, - downloadjson_large, - downloadjson_medium, dragmarks, duplicate_small, edit, @@ -252,6 +253,7 @@ public: promote, properties_medium, readOnly, + recent_medium, recordFill_medium, recordOutline_medium, redo, @@ -360,10 +362,6 @@ public: upDownSquare2, updateAvailable_medium, updateContent_medium, - uploadcsv_large, - uploadcsv_medium, - uploadjson_large, - uploadjson_medium, visibilityOff, visibilityOn, visible_medium, From 95dfd5271232cd69dba4ace560b705ff8baa3044 Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Thu, 16 Nov 2023 11:52:05 +0200 Subject: [PATCH 237/242] QmlDesigner: Fix the delete dialog size problem for CollectionEditor Task-number: QDS-11284 Change-Id: Iba60a27b0037c7db4270603f22855fac4a9c1d05 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Miikka Heikkinen --- .../qmldesigner/collectionEditorQmlSource/CollectionItem.qml | 2 -- 1 file changed, 2 deletions(-) diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionItem.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionItem.qml index 0c28395b10b..589a1433965 100644 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionItem.qml +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionItem.qml @@ -137,13 +137,11 @@ Item { title: qsTr("Deleting the model") clip: true - implicitWidth: contentItem.width contentItem: ColumnLayout { spacing: 2 Text { - Layout.fillHeight: true Layout.fillWidth: true wrapMode: Text.WordWrap From b960fb448152c394c259a82237f602fef7c01a7c Mon Sep 17 00:00:00 2001 From: David Schulz Date: Thu, 16 Nov 2023 09:48:38 +0100 Subject: [PATCH 238/242] Editor: centrally emit filesChangedInternally after refactorings ...instead of relying on the surrounding code of the refactoring to emit the signal. This also ensures that the signal is only emitted for files that are not opened inside a TextEditor. Change-Id: I6223362864014c691962d895b864f9f44c36e035 Reviewed-by: Christian Kandeler --- src/plugins/clangcodemodel/clangdfindreferences.cpp | 4 +--- src/plugins/languageclient/languageclientsymbolsupport.cpp | 7 ------- src/plugins/texteditor/basefilefind.cpp | 1 - src/plugins/texteditor/refactoringchanges.cpp | 6 +++--- 4 files changed, 4 insertions(+), 14 deletions(-) diff --git a/src/plugins/clangcodemodel/clangdfindreferences.cpp b/src/plugins/clangcodemodel/clangdfindreferences.cpp index 19e1df4dd16..b7e62d6e055 100644 --- a/src/plugins/clangcodemodel/clangdfindreferences.cpp +++ b/src/plugins/clangcodemodel/clangdfindreferences.cpp @@ -252,10 +252,8 @@ void ClangdFindReferences::Private::handleRenameRequest( { const Utils::FilePaths filePaths = BaseFileFind::replaceAll(newSymbolName, checkedItems, preserveCase); - if (!filePaths.isEmpty()) { - DocumentManager::notifyFilesChangedInternally(filePaths); + if (!filePaths.isEmpty()) SearchResultWindow::instance()->hide(); - } const auto renameFilesCheckBox = qobject_cast(search->additionalReplaceWidget()); QTC_ASSERT(renameFilesCheckBox, return); diff --git a/src/plugins/languageclient/languageclientsymbolsupport.cpp b/src/plugins/languageclient/languageclientsymbolsupport.cpp index 2631a853996..4f93e537f70 100644 --- a/src/plugins/languageclient/languageclientsymbolsupport.cpp +++ b/src/plugins/languageclient/languageclientsymbolsupport.cpp @@ -691,13 +691,10 @@ void SymbolSupport::handleRenameResponse(Core::SearchResult *search, void SymbolSupport::applyRename(const Utils::SearchResultItems &checkedItems, Core::SearchResult *search) { - QSet affectedNonOpenFilePaths; QMap> editsForDocuments; QList changes; for (const Utils::SearchResultItem &item : checkedItems) { const auto filePath = Utils::FilePath::fromUserInput(item.path().value(0)); - if (!m_client->documentForFilePath(filePath)) - affectedNonOpenFilePaths << filePath; const QJsonObject jsonObject = item.userData().toJsonObject(); if (const TextEdit edit(jsonObject); edit.isValid()) editsForDocuments[filePath] << edit; @@ -715,10 +712,6 @@ void SymbolSupport::applyRename(const Utils::SearchResultItems &checkedItems, for (auto it = editsForDocuments.begin(), end = editsForDocuments.end(); it != end; ++it) applyTextEdits(m_client, it.key(), it.value()); - if (!affectedNonOpenFilePaths.isEmpty()) { - Core::DocumentManager::notifyFilesChangedInternally(Utils::toList(affectedNonOpenFilePaths)); - } - const auto extraWidget = qobject_cast(search->additionalReplaceWidget()); QTC_ASSERT(extraWidget, return); if (!extraWidget->shouldRenameFiles()) diff --git a/src/plugins/texteditor/basefilefind.cpp b/src/plugins/texteditor/basefilefind.cpp index 4bda6757745..8ddadeb7c7e 100644 --- a/src/plugins/texteditor/basefilefind.cpp +++ b/src/plugins/texteditor/basefilefind.cpp @@ -388,7 +388,6 @@ void BaseFileFind::doReplace(const QString &text, const SearchResultItems &items if (!files.isEmpty()) { FadingIndicator::showText(ICore::dialogParent(), Tr::tr("%n occurrences replaced.", nullptr, items.size()), FadingIndicator::SmallText); - DocumentManager::notifyFilesChangedInternally(files); SearchResultWindow::instance()->hide(); } } diff --git a/src/plugins/texteditor/refactoringchanges.cpp b/src/plugins/texteditor/refactoringchanges.cpp index 1398d55bcaa..e49d6ff75fc 100644 --- a/src/plugins/texteditor/refactoringchanges.cpp +++ b/src/plugins/texteditor/refactoringchanges.cpp @@ -355,9 +355,9 @@ bool RefactoringFile::apply() QString error; // suppress "file has changed" warnings if the file is open in a read-only editor Core::FileChangeBlocker block(m_filePath); - if (!m_textFileFormat.writeFile(m_filePath, - doc->toPlainText(), - &error)) { + if (m_textFileFormat.writeFile(m_filePath, doc->toPlainText(), &error)) { + Core::DocumentManager::notifyFilesChangedInternally({m_filePath}); + } else { qWarning() << "Could not apply changes to" << m_filePath << ". Error: " << error; result = false; From 458d44fdc598964009700215a826ea54b9964df7 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Thu, 16 Nov 2023 14:42:59 +0200 Subject: [PATCH 239/242] EffectMaker: Fix image path in generated effect qml Fixes: QDS-11293 Change-Id: I687e3038aa9a92c61955eda4532c2b672c2d6c4d Reviewed-by: Amr Elsayed Reviewed-by: Mahmoud Badri --- src/plugins/effectmakernew/effectmakermodel.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/effectmakernew/effectmakermodel.cpp b/src/plugins/effectmakernew/effectmakermodel.cpp index ee147218a6d..2c9dd051638 100644 --- a/src/plugins/effectmakernew/effectmakermodel.cpp +++ b/src/plugins/effectmakernew/effectmakermodel.cpp @@ -1306,7 +1306,7 @@ QString EffectMakerModel::getQmlImagesString(bool localFiles) if (localFiles) { QFileInfo fi(imagePath); imagePath = fi.fileName(); - imagesString += QString(" source: %1\n").arg(uniform->name()); + imagesString += QString(" source: \"%1\"\n").arg(imagePath); } else { imagesString += QString(" source: g_propertyData.%1\n").arg(uniform->name()); From 5e0c97a38e84d1cd23f4dcae5bc50352619b9502 Mon Sep 17 00:00:00 2001 From: David Schulz Date: Thu, 16 Nov 2023 13:50:51 +0100 Subject: [PATCH 240/242] LanguageClient: fix restarting language servers A timer is used to ensure a server shuts down after a certain amount of time. This timer needs to be reset after restarting the client otherwise the client gets forcefully deleted after the timeout. Change-Id: I804678ec9491328e3da11fd0f9faa59f6e5f7d92 Reviewed-by: Christian Kandeler --- src/plugins/languageclient/client.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/plugins/languageclient/client.cpp b/src/plugins/languageclient/client.cpp index def0016f3ab..5a1fcc44668 100644 --- a/src/plugins/languageclient/client.cpp +++ b/src/plugins/languageclient/client.cpp @@ -1661,6 +1661,7 @@ void Client::setLogTarget(LogTarget target) void Client::start() { + d->m_shutdownTimer.stop(); LanguageClientManager::addClient(this); d->m_clientInterface->start(); } From 171bb13aec6cce59e13861e8b951926bfe18709d Mon Sep 17 00:00:00 2001 From: Knud Dollereder Date: Thu, 16 Nov 2023 13:45:05 +0100 Subject: [PATCH 241/242] QmlDesigner: Fix Formeditors override size behavior - Set override size only if it differs from the actual size. - Remove override size auxiliary data when the model gets detached. - Remember the size when the user sets it explicitly and use that as default size from now on. - Make sure that the rootItemRect is correctly set when the override size auxiliary data changes. Fixes: QDS-11078 Change-Id: Iaedf076a5c21658478fe257e9f6caca78d6d1461 Reviewed-by: Thomas Hartmann Reviewed-by: Qt CI Patch Build Bot --- .../components/formeditor/formeditorview.cpp | 55 ++++++++++++------- .../formeditor/formeditorwidget.cpp | 2 + .../include/auxiliarydataproperties.h | 7 +++ src/plugins/qmldesigner/documentmanager.cpp | 20 ++++--- 4 files changed, 55 insertions(+), 29 deletions(-) diff --git a/src/plugins/qmldesigner/components/formeditor/formeditorview.cpp b/src/plugins/qmldesigner/components/formeditor/formeditorview.cpp index 5d7afed39c3..9ddec21f350 100644 --- a/src/plugins/qmldesigner/components/formeditor/formeditorview.cpp +++ b/src/plugins/qmldesigner/components/formeditor/formeditorview.cpp @@ -42,6 +42,10 @@ namespace QmlDesigner { +namespace { +constexpr AuxiliaryDataKeyView autoSizeProperty{AuxiliaryDataType::Temporary, "autoSize"}; +} + FormEditorView::FormEditorView(ExternalDependenciesInterface &externalDependencies) : AbstractView{externalDependencies} {} @@ -255,6 +259,11 @@ void FormEditorView::cleanupToolsAndScene() void FormEditorView::modelAboutToBeDetached(Model *model) { + rootModelNode().removeAuxiliaryData(contextImageProperty); + rootModelNode().removeAuxiliaryData(widthProperty); + rootModelNode().removeAuxiliaryData(heightProperty); + rootModelNode().removeAuxiliaryData(autoSizeProperty); + cleanupToolsAndScene(); AbstractView::modelAboutToBeDetached(model); } @@ -685,7 +694,6 @@ void FormEditorView::instancesCompleted(const QVector &completedNodeL const bool isFlow = QmlItemNode(rootModelNode()).isFlowView(); QList itemNodeList; for (const ModelNode &node : completedNodeList) { - ; if (const QmlItemNode qmlItemNode = (node)) { if (FormEditorItem *item = scene()->itemForQmlItemNode(qmlItemNode)) { scene()->synchronizeParent(qmlItemNode); @@ -698,10 +706,6 @@ void FormEditorView::instancesCompleted(const QVector &completedNodeL currentTool()->instancesCompleted(itemNodeList); } -namespace { -constexpr AuxiliaryDataKeyView autoSizeProperty{AuxiliaryDataType::Temporary, "autoSize"}; -} - void FormEditorView::instanceInformationsChanged(const QMultiHash &informationChangedHash) { QList changedItems; @@ -939,34 +943,45 @@ void FormEditorView::setupFormEditor3DView() void FormEditorView::setupRootItemSize() { if (const QmlItemNode rootQmlNode = rootModelNode()) { - const int rootElementInitWidth = QmlDesignerPlugin::settings() + int rootElementInitWidth = QmlDesignerPlugin::settings() .value(DesignerSettingsKey::ROOT_ELEMENT_INIT_WIDTH) .toInt(); - const int rootElementInitHeight = QmlDesignerPlugin::settings() + int rootElementInitHeight = QmlDesignerPlugin::settings() .value(DesignerSettingsKey::ROOT_ELEMENT_INIT_HEIGHT) .toInt(); - if (rootQmlNode.instanceBoundingRect().isEmpty() - && !(rootQmlNode.propertyAffectedByCurrentState("width") - && rootQmlNode.propertyAffectedByCurrentState("height"))) { + if (rootModelNode().hasAuxiliaryData(defaultWidthProperty)) + rootElementInitWidth = rootModelNode().auxiliaryData(defaultWidthProperty).value().toInt(); + if (rootModelNode().hasAuxiliaryData(defaultHeightProperty)) + rootElementInitHeight = rootModelNode().auxiliaryData(defaultHeightProperty).value().toInt(); + + bool affectedByCurrentState = + rootQmlNode.propertyAffectedByCurrentState("width") || + rootQmlNode.propertyAffectedByCurrentState("height"); + + QRectF rootRect = rootQmlNode.instanceBoundingRect(); + if (rootRect.isEmpty() && !affectedByCurrentState) { + if (!rootModelNode().hasAuxiliaryData(widthProperty)) rootModelNode().setAuxiliaryData(widthProperty, rootElementInitWidth); + if (!rootModelNode().hasAuxiliaryData(heightProperty)) rootModelNode().setAuxiliaryData(heightProperty, rootElementInitHeight); + rootModelNode().setAuxiliaryData(autoSizeProperty, true); + + formEditorWidget()->updateActions(); + rootRect.setWidth(rootModelNode().auxiliaryData(widthProperty).value().toFloat() ); + rootRect.setHeight(rootModelNode().auxiliaryData(heightProperty).value().toFloat() ); + + } else if (rootModelNode().hasAuxiliaryData(autoSizeProperty) && affectedByCurrentState ) { + rootModelNode().removeAuxiliaryData(widthProperty); + rootModelNode().removeAuxiliaryData(heightProperty); + rootModelNode().removeAuxiliaryData(autoSizeProperty); formEditorWidget()->updateActions(); - } else { - if (rootModelNode().hasAuxiliaryData(autoSizeProperty) - && (rootQmlNode.propertyAffectedByCurrentState("width") - || rootQmlNode.propertyAffectedByCurrentState("height"))) { - rootModelNode().removeAuxiliaryData(widthProperty); - rootModelNode().removeAuxiliaryData(heightProperty); - rootModelNode().removeAuxiliaryData(autoSizeProperty); - formEditorWidget()->updateActions(); - } } - formEditorWidget()->setRootItemRect(rootQmlNode.instanceBoundingRect()); + formEditorWidget()->setRootItemRect(rootRect); formEditorWidget()->centerScene(); auto contextImage = rootModelNode().auxiliaryData(contextImageProperty); diff --git a/src/plugins/qmldesigner/components/formeditor/formeditorwidget.cpp b/src/plugins/qmldesigner/components/formeditor/formeditorwidget.cpp index 2e7a5b2c887..e75a95cfb06 100644 --- a/src/plugins/qmldesigner/components/formeditor/formeditorwidget.cpp +++ b/src/plugins/qmldesigner/components/formeditor/formeditorwidget.cpp @@ -305,6 +305,7 @@ void FormEditorWidget::changeRootItemWidth(const QString &widthText) bool canConvert; int width = widthText.toInt(&canConvert); if (canConvert) { + m_formEditorView->rootModelNode().setAuxiliaryData(defaultWidthProperty, width); m_formEditorView->rootModelNode().setAuxiliaryData(widthProperty, width); } else { m_formEditorView->rootModelNode().removeAuxiliaryData(widthProperty); @@ -316,6 +317,7 @@ void FormEditorWidget::changeRootItemHeight(const QString &heighText) bool canConvert; int height = heighText.toInt(&canConvert); if (canConvert) { + m_formEditorView->rootModelNode().setAuxiliaryData(defaultHeightProperty, height); m_formEditorView->rootModelNode().setAuxiliaryData(heightProperty, height); } else { m_formEditorView->rootModelNode().removeAuxiliaryData(heightProperty); diff --git a/src/plugins/qmldesigner/designercore/include/auxiliarydataproperties.h b/src/plugins/qmldesigner/designercore/include/auxiliarydataproperties.h index b93626a331b..fe55402f174 100644 --- a/src/plugins/qmldesigner/designercore/include/auxiliarydataproperties.h +++ b/src/plugins/qmldesigner/designercore/include/auxiliarydataproperties.h @@ -16,6 +16,13 @@ namespace QmlDesigner { inline constexpr AuxiliaryDataKeyDefaultValue customIdProperty{AuxiliaryDataType::Document, "customId", QStringView{}}; + +inline constexpr AuxiliaryDataKeyDefaultValue defaultWidthProperty{ + AuxiliaryDataType::NodeInstancePropertyOverwrite, "defaultWidth", 640}; + +inline constexpr AuxiliaryDataKeyDefaultValue defaultHeightProperty{ + AuxiliaryDataType::NodeInstancePropertyOverwrite, "defaultHeight", 480}; + inline constexpr AuxiliaryDataKeyDefaultValue widthProperty{ AuxiliaryDataType::NodeInstancePropertyOverwrite, "width", 4}; inline constexpr AuxiliaryDataKeyView heightProperty{AuxiliaryDataType::NodeInstancePropertyOverwrite, diff --git a/src/plugins/qmldesigner/documentmanager.cpp b/src/plugins/qmldesigner/documentmanager.cpp index 0ce1f5e07fd..3d173842b45 100644 --- a/src/plugins/qmldesigner/documentmanager.cpp +++ b/src/plugins/qmldesigner/documentmanager.cpp @@ -75,19 +75,21 @@ inline static QHash getProperties(const ModelNode &node) inline static void applyProperties(ModelNode &node, const QHash &propertyHash) { const auto auxiliaryData = node.auxiliaryData(AuxiliaryDataType::NodeInstancePropertyOverwrite); - for (const auto &element : auxiliaryData) node.removeAuxiliaryData(AuxiliaryDataType::NodeInstancePropertyOverwrite, element.first); - for (auto propertyIterator = propertyHash.cbegin(), end = propertyHash.cend(); - propertyIterator != end; - ++propertyIterator) { - const PropertyName propertyName = propertyIterator.key(); - if (propertyName == "width" || propertyName == "height") { - node.setAuxiliaryData(AuxiliaryDataType::NodeInstancePropertyOverwrite, - propertyIterator.key(), - propertyIterator.value()); + auto needsOverwrite = [&node](const auto& check, const auto& name, const auto& instanceValue) { + if (check == name) { + VariantProperty property = node.variantProperty(name); + if (property.isValid() && property.value() != instanceValue) + return true; } + return false; + }; + + for (const auto& [name, value] : propertyHash.asKeyValueRange()) { + if (needsOverwrite("width", name, value) || needsOverwrite("height", name, value)) + node.setAuxiliaryData(AuxiliaryDataType::NodeInstancePropertyOverwrite, name, value); } } From be757e49f44a66b7b54c9861c08af0eac302a77e Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Thu, 16 Nov 2023 09:22:02 +0100 Subject: [PATCH 242/242] QmlDesigner: Add back empty entry to ordered properties The empty entry defines where default properties and signals end up. Change-Id: I9a17dbbe80374e45e78318608c67a213441aef4f Reviewed-by: Brook Cronin Reviewed-by: Thomas Hartmann --- .../qmldesigner/designercore/model/modeltotextmerger.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/plugins/qmldesigner/designercore/model/modeltotextmerger.cpp b/src/plugins/qmldesigner/designercore/model/modeltotextmerger.cpp index d4ae2edac33..7b2c61f5767 100644 --- a/src/plugins/qmldesigner/designercore/model/modeltotextmerger.cpp +++ b/src/plugins/qmldesigner/designercore/model/modeltotextmerger.cpp @@ -386,6 +386,8 @@ PropertyNameList ModelToTextMerger::propertyOrder() PropertyName("lineHeight"), PropertyName("lineHeightMode"), PropertyName("wrapMode"), + PropertyName(), + PropertyName("states"), PropertyName("to"), PropertyName("from"), PropertyName("transitions")};