From 99e04056f172de3c9031dd35f530a058358af424 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Tue, 23 Apr 2024 17:31:24 +0300 Subject: [PATCH 001/154] QmlDesigner: Add "Edit Material" context menu option for material nodes Fixes: QDS-12372 Change-Id: Ie412216514973aa6d108f3595489f92e13497576 Reviewed-by: Mahmoud Badri Reviewed-by: Reviewed-by: Qt CI Patch Build Bot --- .../componentcore/designeractionmanager.cpp | 4 ++-- .../modelnodecontextmenu_helper.h | 9 +++++--- .../componentcore/modelnodeoperations.cpp | 22 +++++++++++-------- 3 files changed, 21 insertions(+), 14 deletions(-) diff --git a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp index 8d2b2c43c2a..0722d2d5f81 100644 --- a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp +++ b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp @@ -1988,8 +1988,8 @@ void DesignerActionManager::createDefaultDesignerActions() QKeySequence(), 44, &editMaterial, - &modelHasMaterial, - &isModel)); + &hasEditableMaterial, + &isModelOrMaterial)); addDesignerAction(new ModelNodeContextMenuAction( mergeTemplateCommandId, diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.h b/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.h index aec14e9d04b..a7060fcdc6c 100644 --- a/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.h +++ b/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.h @@ -64,16 +64,19 @@ inline bool addMouseAreaFillCheck(const SelectionContext &selectionContext) return false; } -inline bool isModel(const SelectionContext &selectionState) +inline bool isModelOrMaterial(const SelectionContext &selectionState) { ModelNode node = selectionState.currentSingleSelectedNode(); - return node.metaInfo().isQtQuick3DModel(); + return node.metaInfo().isQtQuick3DModel() || node.metaInfo().isQtQuick3DMaterial(); } -inline bool modelHasMaterial(const SelectionContext &selectionState) +inline bool hasEditableMaterial(const SelectionContext &selectionState) { ModelNode node = selectionState.currentSingleSelectedNode(); + if (node.metaInfo().isQtQuick3DMaterial()) + return true; + BindingProperty prop = node.bindingProperty("materials"); return prop.exists() && (!prop.expression().isEmpty() || !prop.resolveToModelNodeList().empty()); diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp index c24ec9aa3e1..1cb58d3c6b3 100644 --- a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp +++ b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp @@ -817,21 +817,25 @@ void editMaterial(const SelectionContext &selectionContext) QTC_ASSERT(modelNode.isValid(), return); - BindingProperty prop = modelNode.bindingProperty("materials"); - if (!prop.exists()) - return; - AbstractView *view = selectionContext.view(); ModelNode material; - if (view->hasId(prop.expression())) { - material = view->modelNodeForId(prop.expression()); + if (modelNode.metaInfo().isQtQuick3DMaterial()) { + material = modelNode; } else { - QList materials = prop.resolveToModelNodeList(); + BindingProperty prop = modelNode.bindingProperty("materials"); + if (!prop.exists()) + return; - if (materials.size() > 0) - material = materials.first(); + if (view->hasId(prop.expression())) { + material = view->modelNodeForId(prop.expression()); + } else { + QList materials = prop.resolveToModelNodeList(); + + if (materials.size() > 0) + material = materials.first(); + } } if (material.isValid()) { From cc187e1ce66d0b831b7da0eccce96cb1fc857955 Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Wed, 24 Apr 2024 19:15:59 +0300 Subject: [PATCH 002/154] QmlDesigner: remove a texture from content library user tab Fixes: QDS-12542 Change-Id: Iee3ea0bd8a3ce6cb41c27a645801eef608cd8da8 Reviewed-by: Miikka Heikkinen --- .../ContentLibraryTextureContextMenu.qml | 12 ++++++++++-- .../ContentLibraryUserView.qml | 1 + .../contentlibrary/contentlibraryusermodel.cpp | 15 +++++++++++++++ .../contentlibrary/contentlibraryusermodel.h | 1 + 4 files changed, 27 insertions(+), 2 deletions(-) diff --git a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryTextureContextMenu.qml b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryTextureContextMenu.qml index f804f16d893..b1f690f10c9 100644 --- a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryTextureContextMenu.qml +++ b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryTextureContextMenu.qml @@ -12,6 +12,7 @@ StudioControls.Menu { property var targetTexture: null property bool hasSceneEnv: false + property bool enableRemove: false // true: adds an option to remove targetTexture property bool canUse3D: targetTexture && ContentLibraryBackend.rootView.hasQuick3DImport && ContentLibraryBackend.rootView.hasMaterialLibrary @@ -32,13 +33,20 @@ StudioControls.Menu { StudioControls.MenuItem { text: qsTr("Add texture") - enabled: canUse3D + enabled: root.canUse3D onTriggered: ContentLibraryBackend.rootView.addTexture(root.targetTexture) } StudioControls.MenuItem { text: qsTr("Add light probe") - enabled: root.hasSceneEnv && canUse3D + enabled: root.hasSceneEnv && root.canUse3D onTriggered: ContentLibraryBackend.rootView.addLightProbe(root.targetTexture) } + + StudioControls.MenuItem { + text: qsTr("Remove from Content Library") + visible: root.targetTexture && root.enableRemove + height: visible ? implicitHeight : 0 + onTriggered: ContentLibraryBackend.userModel.removeTexture(root.targetTexture) + } } diff --git a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryUserView.qml b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryUserView.qml index 1288e14c2a7..d711a0fc310 100644 --- a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryUserView.qml +++ b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryUserView.qml @@ -61,6 +61,7 @@ HelperWidgets.ScrollView { ContentLibraryTextureContextMenu { id: ctxMenuTexture + enableRemove: true hasSceneEnv: ContentLibraryBackend.texturesModel.hasSceneEnv } diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp index ed9723a1512..793e3bbe7ad 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp @@ -121,6 +121,21 @@ void ContentLibraryUserModel::addTextures(const QStringList &paths) emit dataChanged(index(texSectionIdx), index(texSectionIdx)); } +void ContentLibraryUserModel::removeTexture(ContentLibraryTexture *tex) +{ + // remove resources + Utils::FilePath::fromString(tex->texturePath()).removeFile(); + Utils::FilePath::fromString(tex->iconPath()).removeFile(); + + // remove from model + m_userTextures.removeOne(tex); + tex->deleteLater(); + + // update model + int texSectionIdx = 1; + emit dataChanged(index(texSectionIdx), index(texSectionIdx)); +} + // returns unique library material's name and qml component QPair ContentLibraryUserModel::getUniqueLibMaterialNameAndQml(const QString &matName) const { diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.h index 3e9a96fd9d7..affa9a89120 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.h +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.h @@ -72,6 +72,7 @@ public: Q_INVOKABLE void applyToSelected(QmlDesigner::ContentLibraryMaterial *mat, bool add = false); Q_INVOKABLE void addToProject(QmlDesigner::ContentLibraryMaterial *mat); Q_INVOKABLE void removeFromProject(QmlDesigner::ContentLibraryMaterial *mat); + Q_INVOKABLE void removeTexture(QmlDesigner::ContentLibraryTexture *tex); signals: void isEmptyChanged(); From e79cab5d99427fdb7c7d4a0eeda28e9467e8ab60 Mon Sep 17 00:00:00 2001 From: Shrief Gabr Date: Wed, 24 Apr 2024 22:35:05 +0300 Subject: [PATCH 003/154] QmlDesigner: Notify save indicator on row/column deletion Task-number: QDS-12499 Change-Id: I8d61bc3abdd9b8bda299a93c88579f2d0e392221 Reviewed-by: Ali Kianian Reviewed-by: Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Mahmoud Badri --- .../components/collectioneditor/collectiondetailsmodel.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.cpp index fcd6d686efd..2ca2d3e8004 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.cpp @@ -153,6 +153,7 @@ bool CollectionDetailsModel::removeColumns(int column, int count, const QModelIn removeRows(0, rowCount(parent), parent); ensureSingleCell(); + setHasUnsavedChanges(true); return columnsRemoved; } @@ -169,6 +170,7 @@ bool CollectionDetailsModel::removeRows(int row, int count, const QModelIndex &p endRemoveRows(); ensureSingleCell(); + setHasUnsavedChanges(true); return rowsRemoved; } From 23e8be1ef4f576b484b5aa5a13cc3627a46ea1ac Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Tue, 20 Feb 2024 14:07:44 +0200 Subject: [PATCH 004/154] QmlDesigner: Fix deleting collections using the keyboard delete key Fixes: QDS-11735 Change-Id: I188856918da6d478e16383017d808205ee20ee8c Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Mahmoud Badri --- .../CollectionItem.qml | 2 +- .../CollectionListView.qml | 155 ++++-------------- .../CollectionView.qml | 2 +- .../ConfirmDeleteCollectionDialog.qml | 57 +++++++ .../RenameCollectionDialog.qml | 88 ++++++++++ .../imports/StudioControls/MenuItem.qml | 14 +- .../collectioneditor/collectionwidget.cpp | 9 +- .../collectioneditor/collectionwidget.h | 1 + 8 files changed, 194 insertions(+), 134 deletions(-) create mode 100644 share/qtcreator/qmldesigner/collectionEditorQmlSource/ConfirmDeleteCollectionDialog.qml create mode 100644 share/qtcreator/qmldesigner/collectionEditorQmlSource/RenameCollectionDialog.qml diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionItem.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionItem.qml index d963070536f..e110a8a62a6 100644 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionItem.qml +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionItem.qml @@ -98,7 +98,7 @@ Item { MouseArea { anchors.fill: parent acceptedButtons: Qt.RightButton | Qt.LeftButton - onClicked: contextMenuRequested() + onClicked: root.contextMenuRequested() } } } diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionListView.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionListView.qml index 2b95abfc4f4..cbfab526152 100644 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionListView.qml +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionListView.qml @@ -22,6 +22,10 @@ ListView { renameDialog.reject() } + function deleteCurrentCollection() { + deleteDialog.open() + } + delegate: CollectionItem { implicitWidth: root.width onDeleteItem: root.model.removeRow(index) @@ -36,6 +40,10 @@ ListView { readonly property bool selected: item ? item.isSelected : false readonly property int index: item ? item.id : -1 + function updateItem() { + currentCollection.item = collectionMenu.clickedItem ?? root.itemAtIndex(root.model.selectedIndex) + } + function rename(newName) { if (item) item.rename(newName) @@ -54,145 +62,56 @@ ListView { StudioControls.Menu { id: collectionMenu + property CollectionItem clickedItem + closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside + enabled: root.count function openMenu(item) { - currentCollection.item = item - popup() + collectionMenu.clickedItem = item + currentCollection.updateItem() + collectionMenu.popup() } - StudioControls.MenuItem { + onClosed: collectionMenu.clickedItem = null + + Action { + id: menuDeleteAction + text: qsTr("Delete") - shortcut: StandardKey.Delete onTriggered: deleteDialog.open() } - StudioControls.MenuItem { + Action { + id: menuRenameAction + text: qsTr("Rename") - shortcut: StandardKey.Replace onTriggered: renameDialog.open() } - StudioControls.MenuItem { + Action { + id: menuAssignAction + text: qsTr("Assign to the selected node") enabled: CollectionEditorBackend.rootView.targetNodeSelected onTriggered: rootView.assignCollectionToSelectedNode(currentCollection.name) } } - StudioControls.Dialog { + ConfirmDeleteCollectionDialog { id: deleteDialog - title: qsTr("Deleting the model") - clip: true - + collectionName: currentCollection.name + onAboutToShow: currentCollection.updateItem() onAccepted: currentCollection.deleteItem() - - contentItem: ColumnLayout { - id: deleteDialogContent // Keep the id here even if it's not used, because the dialog might lose implicitSize - - width: 300 - spacing: 2 - - Text { - Layout.fillWidth: true - - wrapMode: Text.WordWrap - color: StudioTheme.Values.themeTextColor - text: qsTr("Are you sure that you want to delete model \"%1\"?" - + "\nThe model will be deleted permanently.").arg(currentCollection.name) - - } - - Spacer {} - - RowLayout { - spacing: StudioTheme.Values.sectionRowSpacing - Layout.alignment: Qt.AlignRight | Qt.AlignVCenter - Layout.fillWidth: true - Layout.preferredHeight: 40 - - HelperWidgets.Button { - text: qsTr("Delete") - onClicked: deleteDialog.accept() - } - - HelperWidgets.Button { - text: qsTr("Cancel") - onClicked: deleteDialog.reject() - } - } - } } - StudioControls.Dialog { + RenameCollectionDialog { id: renameDialog - title: qsTr("Rename model") - - onAccepted: { - if (newNameField.text !== "") - currentCollection.rename(newNameField.text) - } - - onOpened: { - newNameField.text = currentCollection.name - } - - contentItem: ColumnLayout { - spacing: 2 - - Text { - text: qsTr("Previous name: " + currentCollection.name) - color: StudioTheme.Values.themeTextColor - } - - Spacer {} - - Text { - Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter - text: qsTr("New name:") - color: StudioTheme.Values.themeTextColor - } - - StudioControls.TextField { - id: newNameField - - Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter - Layout.fillWidth: true - - 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 !== "" - } - } - - Spacer {} - - RowLayout { - Layout.alignment: Qt.AlignRight | Qt.AlignVCenter - spacing: StudioTheme.Values.sectionRowSpacing - - HelperWidgets.Button { - id: btnRename - - text: qsTr("Rename") - onClicked: renameDialog.accept() - } - - HelperWidgets.Button { - text: qsTr("Cancel") - onClicked: renameDialog.reject() - } - } - } + collectionName: currentCollection.name + onAboutToShow: currentCollection.updateItem() + onAccepted: currentCollection.rename(renameDialog.newCollectionName) } Connections { @@ -202,14 +121,4 @@ ListView { root.closeDialogs() } } - - RegularExpressionValidator { - id: newNameValidator - regularExpression: /^\w+$/ - } - - component Spacer: Item { - implicitWidth: 1 - implicitHeight: StudioTheme.Values.columnGap - } } diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionView.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionView.qml index 9d483037ac8..b57a4f75fe2 100644 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionView.qml +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionView.qml @@ -25,7 +25,7 @@ Item { // called from C++ when using the delete key function deleteSelectedCollection() { - print("TODO: deleteSelectedCollection") + collectionListView.deleteCurrentCollection() } function closeDialogs() { diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/ConfirmDeleteCollectionDialog.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/ConfirmDeleteCollectionDialog.qml new file mode 100644 index 00000000000..60f0f5e7b31 --- /dev/null +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/ConfirmDeleteCollectionDialog.qml @@ -0,0 +1,57 @@ +// Copyright (C) 2024 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 HelperWidgets 2.0 as HelperWidgets +import StudioControls 1.0 as StudioControls +import StudioTheme as StudioTheme + +StudioControls.Dialog { + id: root + + required property string collectionName + + title: qsTr("Deleting the model") + clip: true + + contentItem: ColumnLayout { + id: deleteDialogContent // Keep the id here even if it's not used, because the dialog might lose implicitSize + + width: 300 + spacing: 2 + + Text { + Layout.fillWidth: true + + wrapMode: Text.WordWrap + color: StudioTheme.Values.themeTextColor + text: qsTr("Are you sure that you want to delete model \"%1\"?" + + "\nThe model will be deleted permanently.").arg(root.collectionName) + + } + + Item { // spacer + implicitWidth: 1 + implicitHeight: StudioTheme.Values.columnGap + } + + RowLayout { + spacing: StudioTheme.Values.sectionRowSpacing + Layout.alignment: Qt.AlignRight | Qt.AlignVCenter + Layout.fillWidth: true + Layout.preferredHeight: 40 + + HelperWidgets.Button { + text: qsTr("Delete") + onClicked: root.accept() + } + + HelperWidgets.Button { + text: qsTr("Cancel") + onClicked: root.reject() + } + } + } +} diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/RenameCollectionDialog.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/RenameCollectionDialog.qml new file mode 100644 index 00000000000..d421cc1a969 --- /dev/null +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/RenameCollectionDialog.qml @@ -0,0 +1,88 @@ +// Copyright (C) 2024 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 HelperWidgets 2.0 as HelperWidgets +import StudioControls 1.0 as StudioControls +import StudioTheme as StudioTheme + +StudioControls.Dialog { + id: root + + required property string collectionName + readonly property alias newCollectionName: newNameField.text + readonly property bool isValid: newNameField.text !== "" + + title: qsTr("Rename model") + + onOpened: { + newNameField.text = root.collectionName + newNameField.forceActiveFocus() + } + + function acceptIfVerified() { + if (root.isValid) + root.accept() + } + + contentItem: ColumnLayout { + id: renameDialogContent + + spacing: 2 + + Text { + text: qsTr("Previous name: " + root.collectionName) + color: StudioTheme.Values.themeTextColor + } + + Spacer {} + + Text { + Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter + text: qsTr("New name:") + color: StudioTheme.Values.themeTextColor + } + + StudioControls.TextField { + id: newNameField + + Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter + Layout.fillWidth: true + + actionIndicator.visible: false + translationIndicator.visible: false + validator: RegularExpressionValidator { + regularExpression: /^\w+$/ + } + + Keys.onEnterPressed: root.acceptIfVerified() + Keys.onReturnPressed: root.acceptIfVerified() + Keys.onEscapePressed: root.reject() + } + + Spacer {} + + RowLayout { + Layout.alignment: Qt.AlignRight | Qt.AlignVCenter + spacing: StudioTheme.Values.sectionRowSpacing + + HelperWidgets.Button { + text: qsTr("Rename") + enabled: root.isValid + onClicked: root.acceptIfVerified() + } + + HelperWidgets.Button { + text: qsTr("Cancel") + onClicked: root.reject() + } + } + } + + component Spacer: Item { + implicitWidth: 1 + implicitHeight: StudioTheme.Values.columnGap + } +} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/MenuItem.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/MenuItem.qml index 07265e41a73..28372e41be1 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/MenuItem.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/MenuItem.qml @@ -9,7 +9,7 @@ import StudioTheme 1.0 as StudioTheme T.MenuItem { id: control - property alias shortcut: itemAction.shortcut + property alias shortcut: shortcutObserver.shortcutWorkaround property StudioTheme.ControlStyle style: StudioTheme.Values.controlStyle @@ -24,9 +24,6 @@ T.MenuItem { padding: 0 spacing: 0 horizontalPadding: control.style.contextMenuHorizontalPadding - action: Action { - id: itemAction - } contentItem: Item { Text { @@ -41,16 +38,23 @@ T.MenuItem { Text { id: shortcutLabel + anchors.right: parent.right anchors.verticalCenter: parent.verticalCenter text: shortcutObserver.nativeText + ? shortcutObserver.nativeText + : control.action + ? control.action.fakeShortcut ? control.action.fakeShortcut : "" + : "" font: control.font color: textLabel.color Shortcut { id: shortcutObserver - property int shortcutWorkaround: control.shortcut ?? 0 + + property int shortcutWorkaround: 0 sequence: shortcutObserver.shortcutWorkaround + enabled: false } } } diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp index dd706145cf2..137486802aa 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp @@ -53,6 +53,7 @@ QString getPreferredCollectionName(const QUrl &url, const QString &collectionNam } // namespace namespace QmlDesigner { + CollectionWidget::CollectionWidget(CollectionView *view) : m_view(view) , m_listModel(new CollectionListModel) @@ -62,11 +63,11 @@ CollectionWidget::CollectionWidget(CollectionView *view) { setWindowTitle(tr("Model Editor", "Title of model editor widget")); - Core::IContext *icontext = nullptr; Core::Context context(Constants::C_QMLCOLLECTIONEDITOR); - icontext = new Core::IContext(this); - icontext->setContext(context); - icontext->setWidget(this); + m_iContext = new Core::IContext(this); + m_iContext->setContext(context); + m_iContext->setWidget(this); + Core::ICore::addContextObject(m_iContext); connect(m_listModel, &CollectionListModel::warning, this, &CollectionWidget::warn); diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.h b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.h index 13c3566c78e..56f5ff30edc 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.h +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.h @@ -71,6 +71,7 @@ private: QPointer m_view; QPointer m_listModel; QPointer m_collectionDetailsModel; + QPointer m_iContext; std::unique_ptr m_collectionDetailsSortFilterModel; QScopedPointer m_quickWidget; bool m_targetNodeSelected = false; From b4a8d75d17e2e30a46785376dcf4920189706761 Mon Sep 17 00:00:00 2001 From: Teea Poldsam Date: Tue, 23 Apr 2024 14:41:55 +0300 Subject: [PATCH 005/154] Doc: Update Applying Effects to Transitions Fixes: QDS-11494 Change-Id: Ib4a8e09f1ec158aa96f90e78e7a232520f78061a Reviewed-by: Johanna Vanhatapio --- .../src/qtdesignstudio-app-flows.qdoc | 38 +++++++++++-------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/doc/qtdesignstudio/src/qtdesignstudio-app-flows.qdoc b/doc/qtdesignstudio/src/qtdesignstudio-app-flows.qdoc index 5e4a503800d..43015624570 100644 --- a/doc/qtdesignstudio/src/qtdesignstudio-app-flows.qdoc +++ b/doc/qtdesignstudio/src/qtdesignstudio-app-flows.qdoc @@ -376,13 +376,13 @@ \title Applying Effects to Transitions - You can apply effects, such as fade, move, or push to + You can apply effects, such as fade, move, or push, to \l{Adding Action Areas and Transitions}{transitions} between \l{Adding Flow Items}{flow items}. A fade effect makes the first flow item appear to fade out, while the next flow item fades in. A move effect makes the second flow item appear to move in over the - first flow item, while the push effect appears to make a flow item - push out the previous one. You can also design and use custom effects. + first flow item. The push effect makes a flow item appear to push out the previous one. + You can also use your own custom effects that you have created with some other tool. The transition direction determines the direction the new flow item appears from: left, right, top, bottom. You can set the duration of the effect and @@ -392,34 +392,42 @@ \list 1 \li Select a transition line in the \l {2D} view. - \li In the context menu, select \uicontrol {Flow} > - \uicontrol {Assign Flow Effects}, and then select the effect - to apply. + \li Right-click the transition line to open the context menu, select \uicontrol {Flow} > + \uicontrol {Flow Effects}, and then select the effect to apply. \li In \l Properties, modify the properties of the effect. \endlist - To edit effect properties later, select a transition, and then select - \uicontrol {Flow} > \uicontrol {Select Effect} in the context menu. + To edit effect properties later, select a transition, open the context menu, and then select + \uicontrol {Flow} > \uicontrol {Select Effect}. + + To use your own custom effects, select a transition, open the context menu, and then select + \uicontrol {Flow} > \uicontrol {Flow Effects} > \uicontrol {Assign Custom FlowEffect}. + Then specify the path to your custom effect file. + + To remove the current effect from a transition, select a transition, open the context menu, + and then select \uicontrol {Flow} > \uicontrol {Flow Effects} > + \uicontrol {Assign FlowEffect None}. \section1 Flow Effect Properties - You can specify basic properties for a \uicontrol {Flow Effect} - component in the \l Type and \l ID fields in the \uicontrol Component - section in the \uicontrol Properties view. + Specify basic properties for a \uicontrol {Flow Effect} component in the \l Type and + \l ID fields in the \uicontrol Component section in the \uicontrol Properties view. \image studio-flow-effect-properties.png "Flow Effect properties" - You can set the duration and easing curve of all flow effects: + Set the duration and easing curve of flow effects in the \uicontrol {Transition Effect} + section: \list \li In the \uicontrol Duration field, specify the duration of the effect. \li Select the \inlineimage icons/curve_editor.png - button to open \uicontrol {Easing Curve Editor} for attaching an + button to open \uicontrol {Easing Curve Editor} to attach an \l{Editing Easing Curves}{easing curve} to the effect. \endlist - For a move or push effect, you can set some additional properties: + Set some additional properties for a move or push effect in the \uicontrol {Push Effect} + or \uicontrol {Move Effect} section: \image studio-flow-effect-push-properties.png "Flow Push Effect properties" @@ -431,7 +439,7 @@ \li In the \uicontrol {Incoming opacity} and \uicontrol {Outgoing opacity} fields, specify the opacity of the effect as a number between 0 and 1. - \li Select the \uicontrol Reveal check box to reveal the + \li Select the \uicontrol Reveal checkbox to reveal the \uicontrol {Flow Item} where the transition starts. \endlist */ From dc35a7fd3acd454563b418ed187523cc2f640d59 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Thu, 25 Apr 2024 15:09:18 +0300 Subject: [PATCH 006/154] QmlDesigner: Add Shift and Alt modifiers to fly mode While in fly mode, Shift will speed up the WASD/QE movement by 100% and Alt will slow it down 50%. Also changed how shortcuts are disabled in fly mode. ShortcutOverride events are now used to suppress conflicting shortcuts, which has the advantage over old method as it will also suppress application global shortcuts (such as Alt key moving focus to application menu). Task-number: QDS-12291 Change-Id: I5c97d10b6f8955f3b3214e8e254a80cae7357ce5 Reviewed-by: Mahmoud Badri Reviewed-by: Qt CI Patch Build Bot --- .../components/edit3d/edit3dcanvas.cpp | 19 ++++++++++++++ .../components/edit3d/edit3dcanvas.h | 1 + .../components/edit3d/edit3dview.cpp | 26 ------------------- .../components/edit3d/edit3dview.h | 1 - .../mockfiles/qt6/EditCameraController.qml | 15 ++++++++++- .../qml2puppet/editor3d/generalhelper.h | 1 + 6 files changed, 35 insertions(+), 28 deletions(-) diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dcanvas.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dcanvas.cpp index 2e8ef8304f6..ef2e2ee7b56 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dcanvas.cpp +++ b/src/plugins/qmldesigner/components/edit3d/edit3dcanvas.cpp @@ -51,6 +51,8 @@ Edit3DCanvas::Edit3DCanvas(Edit3DWidget *parent) setAcceptDrops(true); setFocusPolicy(Qt::ClickFocus); m_busyIndicator->show(); + + installEventFilter(this); } void Edit3DCanvas::updateRenderImage(const QImage &img) @@ -132,6 +134,23 @@ void Edit3DCanvas::setFlyMode(bool enabled, const QPoint &pos) m_parent->view()->setFlyMode(enabled); } +bool Edit3DCanvas::eventFilter(QObject *obj, QEvent *event) +{ + if (m_flyMode && event->type() == QEvent::ShortcutOverride) { + // Suppress shortcuts that conflict with fly mode keys + const QList controlKeys = { Qt::Key_W, Qt::Key_A, Qt::Key_S, + Qt::Key_D, Qt::Key_Q, Qt::Key_E, + Qt::Key_Up, Qt::Key_Down, Qt::Key_Left, + Qt::Key_Right, Qt::Key_PageDown, Qt::Key_PageUp, + Qt::Key_Alt, Qt::Key_Shift }; + auto ke = static_cast(event); + if (controlKeys.contains(ke->key())) + event->accept(); + } + + return QObject::eventFilter(obj, event); +} + void Edit3DCanvas::mousePressEvent(QMouseEvent *e) { m_contextMenuPending = false; diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dcanvas.h b/src/plugins/qmldesigner/components/edit3d/edit3dcanvas.h index 39207554a73..7f043520b33 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dcanvas.h +++ b/src/plugins/qmldesigner/components/edit3d/edit3dcanvas.h @@ -30,6 +30,7 @@ public: bool isFlyMode() const { return m_flyMode; } protected: + bool eventFilter(QObject *obj, QEvent *event) override; void mousePressEvent(QMouseEvent *e) override; void mouseReleaseEvent(QMouseEvent *e) override; void mouseDoubleClickEvent(QMouseEvent *e) override; diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp index 4712b048b14..c7d70d4b8af 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp +++ b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp @@ -736,32 +736,6 @@ void Edit3DView::showContextMenu() void Edit3DView::setFlyMode(bool enabled) { emitView3DAction(View3DActionType::FlyModeToggle, enabled); - - // Disable any actions with conflicting hotkeys - if (enabled) { - m_flyModeDisabledActions.clear(); - const QList controlKeys = { Qt::Key_W, Qt::Key_A, Qt::Key_S, - Qt::Key_D, Qt::Key_Q, Qt::Key_E, - Qt::Key_Up, Qt::Key_Down, Qt::Key_Left, - Qt::Key_Right, Qt::Key_PageDown, Qt::Key_PageUp}; - for (auto i = m_edit3DActions.cbegin(), end = m_edit3DActions.cend(); i != end; ++i) { - for (const QKeySequence &controlKey : controlKeys) { - if (Core::Command *cmd = m_edit3DWidget->actionToCommandHash().value(i.value()->action())) { - if (cmd->keySequence().matches(controlKey) == QKeySequence::ExactMatch) { - if (i.value()->action()->isEnabled()) { - m_flyModeDisabledActions.append(i.value()); - i.value()->action()->setEnabled(false); - } - break; - } - } - } - } - } else { - for (Edit3DAction *action : std::as_const(m_flyModeDisabledActions)) - action->action()->setEnabled(true); - m_flyModeDisabledActions.clear(); - } } void Edit3DView::syncSnapAuxPropsToSettings() diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dview.h b/src/plugins/qmldesigner/components/edit3d/edit3dview.h index ade2ef6a8f9..918df3c6f98 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dview.h +++ b/src/plugins/qmldesigner/components/edit3d/edit3dview.h @@ -187,7 +187,6 @@ private: int m_activeSplit = 0; QList m_splitToolStates; - QList m_flyModeDisabledActions; ModelNode m_contextMenuPendingNode; ModelNode m_pickView3dNode; diff --git a/src/tools/qml2puppet/mockfiles/qt6/EditCameraController.qml b/src/tools/qml2puppet/mockfiles/qt6/EditCameraController.qml index 255d93e5295..509273901a4 100644 --- a/src/tools/qml2puppet/mockfiles/qt6/EditCameraController.qml +++ b/src/tools/qml2puppet/mockfiles/qt6/EditCameraController.qml @@ -29,6 +29,7 @@ Item { readonly property real _keyPanAmount: _generalHelper.cameraSpeed property bool ignoreToolState: false property bool flyMode: viewRoot.flyMode + property real _cameraSpeedModifier: 1 z: 10 anchors.fill: parent @@ -244,6 +245,7 @@ Item { cameraCtrl.storeCameraState(0); } _generalHelper.stopAllCameraMoves() + cameraCtrl._cameraSpeedModifier = 1 } Connections { @@ -251,7 +253,7 @@ Item { enabled: viewRoot.activeSplit === cameraCtrl.splitId function onRequestCameraMove(camera, moveVec) { if (camera === cameraCtrl.camera) { - cameraCtrl.moveCamera(moveVec); + cameraCtrl.moveCamera(moveVec.times(_cameraSpeedModifier)); _generalHelper.requestRender(); } } @@ -326,7 +328,17 @@ Item { } } + function setCameraSpeed(event) { + if (event.modifiers === Qt.AltModifier) + cameraCtrl._cameraSpeedModifier = 0.5 + else if (event.modifiers === Qt.ShiftModifier) + cameraCtrl._cameraSpeedModifier = 2 + else + cameraCtrl._cameraSpeedModifier = 1 + } + Keys.onPressed: (event) => { + setCameraSpeed(event) event.accepted = true; if (cameraCtrl.flyMode && event.key === Qt.Key_Space) approachObject(); @@ -335,6 +347,7 @@ Item { } Keys.onReleased: (event) => { + setCameraSpeed(event) event.accepted = true; _generalHelper.stopCameraMove(cameraCtrl.getMoveVectorForKey(event.key)); } diff --git a/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.h b/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.h index 62a95d86a6f..558e0363df2 100644 --- a/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.h +++ b/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.h @@ -221,6 +221,7 @@ private: double m_snapRotationInterval = 5.; double m_snapScaleInterval = .1; double m_cameraSpeed = 10.; + double m_cameraSpeedModifier = 1.; QVariant m_bgColor; }; From 280239ca36ae22280b6f3c166e9f5d340c28fa97 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Tue, 23 Apr 2024 14:46:30 +0200 Subject: [PATCH 007/154] QmlDesigner: Fix controls properties Fixes: QDS-12450 Change-Id: I1b49a2ba0d199ca98fcd92367db9b966ab7ec1d5 Reviewed-by: Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Tim Jenssen --- .../Controls/Basic/BusyIndicatorSpecifics.qml | 6 +++++ .../Controls/Basic/ButtonSpecifics.qml | 6 +++++ .../Controls/Basic/CheckBoxSpecifics.qml | 6 +++++ .../Controls/Basic/CheckDelegateSpecifics.qml | 6 +++++ .../Controls/Basic/ComboBoxSpecifics.qml | 6 +++++ .../Controls/Basic/ControlSpecifics.qml | 6 +++++ .../Controls/Basic/DelayButtonSpecifics.qml | 6 +++++ .../QtQuick/Controls/Basic/DialSpecifics.qml | 6 +++++ .../Controls/Basic/DialogSpecifics.qml | 6 +++++ .../Controls/Basic/DrawerSpecifics.qml | 6 +++++ .../QtQuick/Controls/Basic/FrameSpecifics.qml | 6 +++++ .../Controls/Basic/GroupBoxSpecifics.qml | 6 +++++ .../Controls/Basic/ItemDelegateSpecifics.qml | 6 +++++ .../QtQuick/Controls/Basic/LabelSpecifics.qml | 27 +++++++++++++++++++ .../Controls/Basic/PageIndicatorSpecifics.qml | 6 +++++ .../QtQuick/Controls/Basic/PageSpecifics.qml | 6 +++++ .../QtQuick/Controls/Basic/PaneSpecifics.qml | 6 +++++ .../QtQuick/Controls/Basic/PopupSpecifics.qml | 6 +++++ .../Controls/Basic/ProgressBarSpecifics.qml | 6 +++++ .../Controls/Basic/RadioButtonSpecifics.qml | 6 +++++ .../Controls/Basic/RadioDelegateSpecifics.qml | 6 +++++ .../Controls/Basic/RangeSliderSpecifics.qml | 6 +++++ .../Controls/Basic/RoundButtonSpecifics.qml | 6 +++++ .../Controls/Basic/ScrollViewSpecifics.qml | 6 +++++ .../Controls/Basic/SliderSpecifics.qml | 6 +++++ .../Controls/Basic/SpinBoxSpecifics.qml | 6 +++++ .../Controls/Basic/StackViewSpecifics.qml | 6 +++++ .../Controls/Basic/SwipeDelegateSpecifics.qml | 6 +++++ .../Controls/Basic/SwipeViewSpecifics.qml | 6 +++++ .../Basic/SwitchDelegateSpecifics.qml | 6 +++++ .../Controls/Basic/SwitchSpecifics.qml | 6 +++++ .../Controls/Basic/TabBarSpecifics.qml | 6 +++++ .../Controls/Basic/TabButtonSpecifics.qml | 6 +++++ .../Controls/Basic/TextAreaSpecifics.qml | 6 +++++ .../Controls/Basic/TextFieldSpecifics.qml | 6 +++++ .../Controls/Basic/ToolBarSpecifics.qml | 6 +++++ .../Controls/Basic/ToolButtonSpecifics.qml | 6 +++++ .../Controls/Basic/ToolSeparatorSpecifics.qml | 6 +++++ .../Controls/Basic/TumblerSpecifics.qml | 6 +++++ 39 files changed, 255 insertions(+) create mode 100644 share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/BusyIndicatorSpecifics.qml create mode 100644 share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/ButtonSpecifics.qml create mode 100644 share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/CheckBoxSpecifics.qml create mode 100644 share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/CheckDelegateSpecifics.qml create mode 100644 share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/ComboBoxSpecifics.qml create mode 100644 share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/ControlSpecifics.qml create mode 100644 share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/DelayButtonSpecifics.qml create mode 100644 share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/DialSpecifics.qml create mode 100644 share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/DialogSpecifics.qml create mode 100644 share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/DrawerSpecifics.qml create mode 100644 share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/FrameSpecifics.qml create mode 100644 share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/GroupBoxSpecifics.qml create mode 100644 share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/ItemDelegateSpecifics.qml create mode 100644 share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/LabelSpecifics.qml create mode 100644 share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/PageIndicatorSpecifics.qml create mode 100644 share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/PageSpecifics.qml create mode 100644 share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/PaneSpecifics.qml create mode 100644 share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/PopupSpecifics.qml create mode 100644 share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/ProgressBarSpecifics.qml create mode 100644 share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/RadioButtonSpecifics.qml create mode 100644 share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/RadioDelegateSpecifics.qml create mode 100644 share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/RangeSliderSpecifics.qml create mode 100644 share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/RoundButtonSpecifics.qml create mode 100644 share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/ScrollViewSpecifics.qml create mode 100644 share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/SliderSpecifics.qml create mode 100644 share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/SpinBoxSpecifics.qml create mode 100644 share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/StackViewSpecifics.qml create mode 100644 share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/SwipeDelegateSpecifics.qml create mode 100644 share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/SwipeViewSpecifics.qml create mode 100644 share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/SwitchDelegateSpecifics.qml create mode 100644 share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/SwitchSpecifics.qml create mode 100644 share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/TabBarSpecifics.qml create mode 100644 share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/TabButtonSpecifics.qml create mode 100644 share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/TextAreaSpecifics.qml create mode 100644 share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/TextFieldSpecifics.qml create mode 100644 share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/ToolBarSpecifics.qml create mode 100644 share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/ToolButtonSpecifics.qml create mode 100644 share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/ToolSeparatorSpecifics.qml create mode 100644 share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/TumblerSpecifics.qml diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/BusyIndicatorSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/BusyIndicatorSpecifics.qml new file mode 100644 index 00000000000..2cc82d7fabd --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/BusyIndicatorSpecifics.qml @@ -0,0 +1,6 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import ".." as Original + +Original.BusyIndicatorSpecifics {} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/ButtonSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/ButtonSpecifics.qml new file mode 100644 index 00000000000..ea32a005941 --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/ButtonSpecifics.qml @@ -0,0 +1,6 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import ".." as Original + +Original.ButtonSpecifics {} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/CheckBoxSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/CheckBoxSpecifics.qml new file mode 100644 index 00000000000..c38b75b327b --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/CheckBoxSpecifics.qml @@ -0,0 +1,6 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import ".." as Original + +Original.CheckBoxSpecifics {} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/CheckDelegateSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/CheckDelegateSpecifics.qml new file mode 100644 index 00000000000..939433f5eaf --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/CheckDelegateSpecifics.qml @@ -0,0 +1,6 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import ".." as Original + +Original.CheckDelegateSpecifics {} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/ComboBoxSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/ComboBoxSpecifics.qml new file mode 100644 index 00000000000..13ad9346d40 --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/ComboBoxSpecifics.qml @@ -0,0 +1,6 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import ".." as Original + +Original.ComboBoxSpecifics {} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/ControlSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/ControlSpecifics.qml new file mode 100644 index 00000000000..07cc704a071 --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/ControlSpecifics.qml @@ -0,0 +1,6 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import ".." as Original + +Original.ControlSpecifics {} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/DelayButtonSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/DelayButtonSpecifics.qml new file mode 100644 index 00000000000..3818cebc33b --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/DelayButtonSpecifics.qml @@ -0,0 +1,6 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import ".." as Original + +Original.DelayButtonSpecifics {} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/DialSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/DialSpecifics.qml new file mode 100644 index 00000000000..c62608cd004 --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/DialSpecifics.qml @@ -0,0 +1,6 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import ".." as Original + +Original.DialSpecifics {} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/DialogSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/DialogSpecifics.qml new file mode 100644 index 00000000000..237cb418622 --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/DialogSpecifics.qml @@ -0,0 +1,6 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import ".." as Original + +Original.DialogSpecifics {} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/DrawerSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/DrawerSpecifics.qml new file mode 100644 index 00000000000..d5cf0a482eb --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/DrawerSpecifics.qml @@ -0,0 +1,6 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import ".." as Original + +Original.DrawerSpecifics {} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/FrameSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/FrameSpecifics.qml new file mode 100644 index 00000000000..ac1881a865d --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/FrameSpecifics.qml @@ -0,0 +1,6 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import ".." as Original + +Original.FrameSpecifics {} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/GroupBoxSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/GroupBoxSpecifics.qml new file mode 100644 index 00000000000..3067af76405 --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/GroupBoxSpecifics.qml @@ -0,0 +1,6 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import ".." as Original + +Original.GroupBoxSpecifics {} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/ItemDelegateSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/ItemDelegateSpecifics.qml new file mode 100644 index 00000000000..6c7f5dc8127 --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/ItemDelegateSpecifics.qml @@ -0,0 +1,6 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import ".." as Original + +Original.ItemDelegateSpecifics {} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/LabelSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/LabelSpecifics.qml new file mode 100644 index 00000000000..b3cb5a46ab7 --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/LabelSpecifics.qml @@ -0,0 +1,27 @@ +// 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 HelperWidgets 2.0 +import QtQuick.Layouts 1.15 +import StudioTheme 1.0 as StudioTheme + +Column { + width: parent.width + + CharacterSection { + showVerticalAlignment: true + } + + TextExtrasSection { + width: parent.width + showWrapMode: true + showFormatProperty: true + } + + FontExtrasSection {} + + PaddingSection {} + + InsetSection {} +} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/PageIndicatorSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/PageIndicatorSpecifics.qml new file mode 100644 index 00000000000..7437975f960 --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/PageIndicatorSpecifics.qml @@ -0,0 +1,6 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import ".." as Original + +Original.PageIndicatorSpecifics {} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/PageSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/PageSpecifics.qml new file mode 100644 index 00000000000..16e8b51cb52 --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/PageSpecifics.qml @@ -0,0 +1,6 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import ".." as Original + +Original.PageSpecifics {} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/PaneSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/PaneSpecifics.qml new file mode 100644 index 00000000000..dd07a3cf7fa --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/PaneSpecifics.qml @@ -0,0 +1,6 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import ".." as Original + +Original.PaneSpecifics {} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/PopupSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/PopupSpecifics.qml new file mode 100644 index 00000000000..788b2a43593 --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/PopupSpecifics.qml @@ -0,0 +1,6 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import ".." as Original + +Original.PopupSpecifics {} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/ProgressBarSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/ProgressBarSpecifics.qml new file mode 100644 index 00000000000..a58f9e2f360 --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/ProgressBarSpecifics.qml @@ -0,0 +1,6 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import ".." as Original + +Original.ProgressBarSpecifics {} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/RadioButtonSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/RadioButtonSpecifics.qml new file mode 100644 index 00000000000..fc2500dd1b7 --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/RadioButtonSpecifics.qml @@ -0,0 +1,6 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import ".." as Original + +Original.RadioButtonSpecifics {} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/RadioDelegateSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/RadioDelegateSpecifics.qml new file mode 100644 index 00000000000..d07f17b7e85 --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/RadioDelegateSpecifics.qml @@ -0,0 +1,6 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import ".." as Original + +Original.RadioDelegateSpecifics {} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/RangeSliderSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/RangeSliderSpecifics.qml new file mode 100644 index 00000000000..14e493e69e9 --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/RangeSliderSpecifics.qml @@ -0,0 +1,6 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import ".." as Original + +Original.RangeSliderSpecifics {} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/RoundButtonSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/RoundButtonSpecifics.qml new file mode 100644 index 00000000000..5bd8776f624 --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/RoundButtonSpecifics.qml @@ -0,0 +1,6 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import ".." as Original + +Original.RoundButtonSpecifics {} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/ScrollViewSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/ScrollViewSpecifics.qml new file mode 100644 index 00000000000..2af497fcb71 --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/ScrollViewSpecifics.qml @@ -0,0 +1,6 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import ".." as Original + +Original.ScrollViewSpecifics {} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/SliderSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/SliderSpecifics.qml new file mode 100644 index 00000000000..846ca8bb14f --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/SliderSpecifics.qml @@ -0,0 +1,6 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import ".." as Original + +Original.SliderSpecifics {} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/SpinBoxSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/SpinBoxSpecifics.qml new file mode 100644 index 00000000000..f75983a9fad --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/SpinBoxSpecifics.qml @@ -0,0 +1,6 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import ".." as Original + +Original.SpinBoxSpecifics {} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/StackViewSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/StackViewSpecifics.qml new file mode 100644 index 00000000000..593cba24d75 --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/StackViewSpecifics.qml @@ -0,0 +1,6 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import ".." as Original + +Original.StackViewSpecifics {} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/SwipeDelegateSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/SwipeDelegateSpecifics.qml new file mode 100644 index 00000000000..40648a34313 --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/SwipeDelegateSpecifics.qml @@ -0,0 +1,6 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import ".." as Original + +Original.SwipeDelegateSpecifics {} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/SwipeViewSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/SwipeViewSpecifics.qml new file mode 100644 index 00000000000..eaf769135c1 --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/SwipeViewSpecifics.qml @@ -0,0 +1,6 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import ".." as Original + +Original.SwipeViewSpecifics {} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/SwitchDelegateSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/SwitchDelegateSpecifics.qml new file mode 100644 index 00000000000..cba96b930f1 --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/SwitchDelegateSpecifics.qml @@ -0,0 +1,6 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import ".." as Original + +Original.SwitchDelegateSpecifics {} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/SwitchSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/SwitchSpecifics.qml new file mode 100644 index 00000000000..e6647f4f7d4 --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/SwitchSpecifics.qml @@ -0,0 +1,6 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import ".." as Original + +Original.SwitchSpecifics {} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/TabBarSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/TabBarSpecifics.qml new file mode 100644 index 00000000000..c23580a25e9 --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/TabBarSpecifics.qml @@ -0,0 +1,6 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import ".." as Original + +Original.TabBarSpecifics {} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/TabButtonSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/TabButtonSpecifics.qml new file mode 100644 index 00000000000..dc5bcb36e2e --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/TabButtonSpecifics.qml @@ -0,0 +1,6 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import ".." as Original + +Original.TabButtonSpecifics {} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/TextAreaSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/TextAreaSpecifics.qml new file mode 100644 index 00000000000..a9871612a97 --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/TextAreaSpecifics.qml @@ -0,0 +1,6 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import ".." as Original + +Original.TextAreaSpecifics {} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/TextFieldSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/TextFieldSpecifics.qml new file mode 100644 index 00000000000..fbcac9c3898 --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/TextFieldSpecifics.qml @@ -0,0 +1,6 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import ".." as Original + +Original.TextFieldSpecifics {} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/ToolBarSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/ToolBarSpecifics.qml new file mode 100644 index 00000000000..63d1f179eb0 --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/ToolBarSpecifics.qml @@ -0,0 +1,6 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import ".." as Original + +Original.ToolBarSpecifics {} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/ToolButtonSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/ToolButtonSpecifics.qml new file mode 100644 index 00000000000..98aabdd2742 --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/ToolButtonSpecifics.qml @@ -0,0 +1,6 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import ".." as Original + +Original.ToolButtonSpecifics {} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/ToolSeparatorSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/ToolSeparatorSpecifics.qml new file mode 100644 index 00000000000..060808a2a6d --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/ToolSeparatorSpecifics.qml @@ -0,0 +1,6 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import ".." as Original + +Original.ToolSeparatorSpecifics {} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/TumblerSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/TumblerSpecifics.qml new file mode 100644 index 00000000000..5ca82806427 --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/TumblerSpecifics.qml @@ -0,0 +1,6 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import ".." as Original + +Original.TumblerSpecifics {} From 7f8d7f511d2bafb5106f61a2f5e5c6f2e3679884 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Tue, 23 Apr 2024 15:54:59 +0200 Subject: [PATCH 008/154] QmlDesigner: Skip non property Task-number: QDS-11951 Change-Id: Ic84f46b74f9c65501bb4f0f9883de1bceb176818 Reviewed-by: Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Tim Jenssen --- .../projectstorage/projectstorageupdater.cpp | 8 ++++ .../projectstorageupdater-test.cpp | 38 ++++++++++++++++++- 2 files changed, 44 insertions(+), 2 deletions(-) diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.cpp b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.cpp index 761d6371efe..d8d0c6ef525 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.cpp +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.cpp @@ -191,6 +191,11 @@ void addModuleExportedImport(Storage::Synchronization::ModuleExportedImports &im imports.emplace_back(moduleId, exportedModuleId, version, isAutoVersion); } +bool isOptionalImport(QmlDirParser::Import::Flags flags) +{ + return flags & QmlDirParser::Import::Optional && !(flags & QmlDirParser::Import::OptionalDefault); +} + void addModuleExportedImports(Storage::Synchronization::ModuleExportedImports &imports, ModuleId moduleId, ModuleId cppModuleId, @@ -205,6 +210,9 @@ void addModuleExportedImports(Storage::Synchronization::ModuleExportedImports &i keyValue("module id", moduleId)}; for (const QmlDirParser::Import &qmldirImport : qmldirImports) { + if (isOptionalImport(qmldirImport.flags)) + continue; + Utils::PathString exportedModuleName{qmldirImport.module}; ModuleId exportedModuleId = projectStorage.moduleId(exportedModuleName); addModuleExportedImport(imports, diff --git a/tests/unit/tests/unittests/projectstorage/projectstorageupdater-test.cpp b/tests/unit/tests/unittests/projectstorage/projectstorageupdater-test.cpp index 96909857b32..0d8b5cfc34e 100644 --- a/tests/unit/tests/unittests/projectstorage/projectstorageupdater-test.cpp +++ b/tests/unit/tests/unittests/projectstorage/projectstorageupdater-test.cpp @@ -1541,12 +1541,12 @@ TEST_F(ProjectStorageUpdater, synchronize_qmldir_imports_with_double_entries) updater.update(directories, {}, {}, {}); } -TEST_F(ProjectStorageUpdater, synchronize_qmldir_optional_imports) +TEST_F(ProjectStorageUpdater, synchronize_qmldir_default_imports) { QString qmldir{R"(module Example import Qml auto import QML 2.1 - optional import Quick + default import Quick )"}; setContent(u"/path/qmldir", qmldir); @@ -1583,6 +1583,40 @@ TEST_F(ProjectStorageUpdater, synchronize_qmldir_optional_imports) updater.update(directories, {}, {}, {}); } +TEST_F(ProjectStorageUpdater, do_not_synchronize_qmldir_optional_imports) +{ + QString qmldir{R"(module Example + import Qml auto + import QML 2.1 + optional import Quick + )"}; + setContent(u"/path/qmldir", qmldir); + + EXPECT_CALL(projectStorageMock, + synchronize( + AllOf(Field(&SynchronizationPackage::moduleExportedImports, + UnorderedElementsAre(ModuleExportedImport{exampleModuleId, + qmlModuleId, + Storage::Version{}, + IsAutoVersion::Yes}, + ModuleExportedImport{exampleCppNativeModuleId, + qmlCppNativeModuleId, + Storage::Version{}, + IsAutoVersion::No}, + ModuleExportedImport{exampleModuleId, + builtinModuleId, + Storage::Version{2, 1}, + IsAutoVersion::No}, + ModuleExportedImport{exampleCppNativeModuleId, + builtinCppNativeModuleId, + Storage::Version{}, + IsAutoVersion::No})), + Field(&SynchronizationPackage::updatedModuleIds, + ElementsAre(exampleModuleId))))); + + updater.update(directories, {}, {}, {}); +} + TEST_F(ProjectStorageUpdater, update_path_watcher_directories) { EXPECT_CALL(patchWatcherMock, From 4268c50f30c8b160d135d966c998a84b552cbaf7 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Tue, 23 Apr 2024 18:29:18 +0200 Subject: [PATCH 009/154] QmlDesigner: Fix property editor lookup Task-number: QDS-11951 Change-Id: I22563aca2aacf515d2a2e66d87e39c418ea3b7d4 Reviewed-by: Reviewed-by: Tim Jenssen Reviewed-by: Qt CI Patch Build Bot --- .../projectstorage/projectstorageupdater.cpp | 25 ++++++++++++------- .../projectstorage/projectstorageupdater.h | 6 +++-- .../projectstorageupdater-test.cpp | 23 +++++++++++------ 3 files changed, 35 insertions(+), 19 deletions(-) diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.cpp b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.cpp index d8d0c6ef525..f1f8a2ca28f 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.cpp +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.cpp @@ -517,8 +517,12 @@ void ProjectStorageUpdater::updatePropertyEditorPaths( auto state = fileState(directorySourceId, package, notUpdatedSourceIds); - if (state == FileState::Changed) - updatePropertyEditorPath(pathInfo.filePath(), package, directorySourceId); + if (state == FileState::Changed) { + updatePropertyEditorPath(pathInfo.filePath(), + package, + directorySourceId, + propertyEditorResourcesPath.size() + 1); + } } } @@ -655,7 +659,8 @@ void ProjectStorageUpdater::updateTypeAnnotation(const QString &directoryPath, void ProjectStorageUpdater::updatePropertyEditorPath( const QString &directoryPath, Storage::Synchronization::SynchronizationPackage &package, - SourceId directorySourceId) + SourceId directorySourceId, + long long pathOffset) { NanotraceHR::Tracer tracer{"update property editor path"_t, category(), @@ -668,27 +673,29 @@ void ProjectStorageUpdater::updatePropertyEditorPath( auto dir = QDir{directoryPath}; const auto fileInfos = dir.entryInfoList({"*Pane.qml", "*Specifics.qml"}, QDir::Files); for (const auto &fileInfo : fileInfos) - updatePropertyEditorFilePath(fileInfo.filePath(), package, directorySourceId); + updatePropertyEditorFilePath(fileInfo.filePath(), package, directorySourceId, pathOffset); } void ProjectStorageUpdater::updatePropertyEditorFilePath( const QString &path, Storage::Synchronization::SynchronizationPackage &package, - SourceId directorySourceId) + SourceId directorySourceId, + long long pathOffset) { NanotraceHR::Tracer tracer{"update property editor file path"_t, category(), keyValue("directory path", path), keyValue("directory source id", directorySourceId)}; - QRegularExpression regex{R"xo(.+\/(\w+)\/(\w+)(Specifics|Pane).qml)xo"}; - auto match = regex.match(path); + QRegularExpression regex{R"xo((.+)\/(\w+)(Specifics|Pane).qml)xo"}; + auto match = regex.match(QStringView{path}.mid(pathOffset)); QString oldModuleName; ModuleId moduleId; if (match.hasMatch()) { - auto moduleName = match.capturedView(1); + auto moduleName = match.capturedView(1).toString(); + moduleName.replace('/', '.'); if (oldModuleName != moduleName) { - oldModuleName = moduleName.toString(); + oldModuleName = moduleName; moduleId = m_projectStorage.moduleId(Utils::SmallString{moduleName}); } Storage::TypeNameString typeName{match.capturedView(2)}; diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.h index 640969fe990..bbaa71a2891 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.h @@ -177,10 +177,12 @@ private: Storage::Synchronization::SynchronizationPackage &package); void updatePropertyEditorPath(const QString &path, Storage::Synchronization::SynchronizationPackage &package, - SourceId directorySourceId); + SourceId directorySourceId, + long long pathOffset); void updatePropertyEditorFilePath(const QString &filePath, Storage::Synchronization::SynchronizationPackage &package, - SourceId directorySourceId); + SourceId directorySourceId, + long long pathOffset); void parseTypeInfos(const QStringList &typeInfos, const QList &qmldirDependencies, const QList &qmldirImports, diff --git a/tests/unit/tests/unittests/projectstorage/projectstorageupdater-test.cpp b/tests/unit/tests/unittests/projectstorage/projectstorageupdater-test.cpp index 0d8b5cfc34e..2d1e7aa61ea 100644 --- a/tests/unit/tests/unittests/projectstorage/projectstorageupdater-test.cpp +++ b/tests/unit/tests/unittests/projectstorage/projectstorageupdater-test.cpp @@ -3533,19 +3533,26 @@ TEST_F(ProjectStorageUpdater, update_property_editor_specifics) ON_CALL(projectStorageMock, fetchFileStatus(_)).WillByDefault([](SourceId sourceId) { return FileStatus{sourceId, 1, 21}; }); - auto sourceId = sourcePathCache.sourceId( + auto textSourceId = sourcePathCache.sourceId( QmlDesigner::SourcePath{propertyEditorQmlPath + "/QtQuick/TextSpecifics.qml"}); - auto directoryId = sourcePathCache.sourceId( + auto qtQuickDirectoryId = sourcePathCache.sourceId( QmlDesigner::SourcePath{propertyEditorQmlPath + "/QtQuick/."}); - setFilesChanged({directoryId}); + auto buttonSourceId = sourcePathCache.sourceId( + QmlDesigner::SourcePath{propertyEditorQmlPath + "/QtQuick/Controls/ButtonSpecifics.qml"}); + auto controlsDirectoryId = sourcePathCache.sourceId( + QmlDesigner::SourcePath{propertyEditorQmlPath + "/QtQuick/Controls/."}); + setFilesChanged({qtQuickDirectoryId, controlsDirectoryId}); auto qtQuickModuleId = storage.moduleId("QtQuick"); + auto controlsModuleId = storage.moduleId("QtQuick.Controls"); EXPECT_CALL(projectStorageMock, - synchronize( - AllOf(Field(&SynchronizationPackage::propertyEditorQmlPaths, - Contains(IsPropertyEditorQmlPath(qtQuickModuleId, "Text", sourceId))), - Field(&SynchronizationPackage::updatedPropertyEditorQmlPathSourceIds, - ElementsAre(directoryId))))); + synchronize(AllOf( + Field(&SynchronizationPackage::propertyEditorQmlPaths, + IsSupersetOf( + {IsPropertyEditorQmlPath(qtQuickModuleId, "Text", textSourceId), + IsPropertyEditorQmlPath(controlsModuleId, "Button", buttonSourceId)})), + Field(&SynchronizationPackage::updatedPropertyEditorQmlPathSourceIds, + ElementsAre(qtQuickDirectoryId, controlsDirectoryId))))); updater.update({}, {}, propertyEditorQmlPath, {}); } From 1cc99b996a9f158c39692892cea0e5ef8c3137d5 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Wed, 24 Apr 2024 10:51:42 +0200 Subject: [PATCH 010/154] QmlDesigner: remove useless code That was submitted by accident Change-Id: Ie5606604295ec2e942548690a77c3855f7614a9f Reviewed-by: Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Tim Jenssen --- .../designercore/projectstorage/projectstorage.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.cpp b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.cpp index a7577d3ab77..8aa162224ed 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.cpp +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.cpp @@ -2277,12 +2277,6 @@ void ProjectStorage::updateTypeIdInTypeAnnotations(Storage::Synchronization::Typ annotation.typeName); } - for (auto &annotation : typeAnnotations) { - if (!annotation.typeId) - qWarning() << moduleName(annotation.moduleId).toQString() - << annotation.typeName.toQString(); - } - typeAnnotations.erase(std::remove_if(typeAnnotations.begin(), typeAnnotations.end(), [](const auto &annotation) { return !annotation.typeId; }), From 31aec73423591483133448416ebbaac4c5768837 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Thu, 4 Apr 2024 13:45:51 +0200 Subject: [PATCH 011/154] QmlDesiger: Add tracing to meta info Change-Id: I0ef7b061c729e6ad161db77a03f375f4452ad273 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Reviewed-by: Tim Jenssen --- src/plugins/qmldesigner/CMakeLists.txt | 7 +- .../designercore/metainfo/nodemetainfo.cpp | 1451 ++++++++++++++--- .../tracing/qmldesignertracing.cpp | 9 + .../designercore/tracing/qmldesignertracing.h | 16 + 4 files changed, 1242 insertions(+), 241 deletions(-) diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index 520c4ebc799..1d3fa69d937 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -27,6 +27,10 @@ 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} "") +env_with_default("QTC_ENABLE_METAINFO_TRACING" ENV_QTC_ENABLE_METAINFO_TRACING OFF) +option(ENABLE_METAINFO_TRACING "Enable meta info tracing" ${ENV_QTC_ENABLE_METAINFO_TRACING}) +add_feature_info("Meta info tracing" ${ENABLE_METAINFO_TRACING} "") + add_qtc_library(QmlDesignerUtils STATIC DEPENDS Qt::Gui Utils Qt::QmlPrivate @@ -99,7 +103,7 @@ add_qtc_library(QmlDesignerCore STATIC ) extend_qtc_library(QmlDesignerCore - CONDITION ENABLE_PROJECT_STORAGE_TRACING OR ENABLE_IMAGE_CACHE_TRACING OR ENABLE_MODEL_TRACING + CONDITION ENABLE_PROJECT_STORAGE_TRACING OR ENABLE_IMAGE_CACHE_TRACING OR ENABLE_MODEL_TRACING OR ENABLE_METAINFO_TRACING PUBLIC_DEPENDS Nanotrace PUBLIC_DEFINES ENABLE_QMLDESIGNER_TRACING @@ -107,6 +111,7 @@ extend_qtc_library(QmlDesignerCore $<$:ENABLE_PROJECT_STORAGE_TRACING> $<$:ENABLE_IMAGE_CACHE_TRACING> $<$:ENABLE_MODEL_TRACING> + $<$:ENABLE_METAINFO_TRACING> ) extend_qtc_library(QmlDesignerCore diff --git a/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp b/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp index b8c3e610be5..886ad8686f6 100644 --- a/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp +++ b/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp @@ -53,6 +53,8 @@ NodeMetaInfo object will result in an InvalidMetaInfoException being thrown. namespace { +auto category = MetaInfoTracing::category; + struct TypeDescription { QString className; @@ -1493,15 +1495,20 @@ MetaInfoType NodeMetaInfo::type() const { if constexpr (useProjectStorage()) { if (isValid()) { - switch (typeData().traits.kind) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get type"_t, category(), keyValue("type id", m_typeId)}; + auto kind = typeData().traits.kind; + tracer.end(keyValue("type kind", kind)); + + switch (kind) { case Storage::TypeTraitsKind::Reference: return MetaInfoType::Reference; case Storage::TypeTraitsKind::Value: return MetaInfoType::Value; case Storage::TypeTraitsKind::Sequence: return MetaInfoType::Sequence; - default: - break; + case Storage::TypeTraitsKind::None: + return MetaInfoType::None; } } } @@ -1511,16 +1518,38 @@ MetaInfoType NodeMetaInfo::type() const bool NodeMetaInfo::isFileComponent() const { - if constexpr (useProjectStorage()) - return isValid() && typeData().traits.isFileComponent; - else + if constexpr (useProjectStorage()) { + if (!isValid()) + return {}; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is file component"_t, category(), keyValue("type id", m_typeId)}; + + auto isFileComponent = typeData().traits.isFileComponent; + + tracer.end(keyValue("is file component", isFileComponent)); + + return isFileComponent; + + } else { return isValid() && m_privateData->isFileComponent(); + } } bool NodeMetaInfo::isProjectComponent() const { if constexpr (useProjectStorage()) { - return isValid() && typeData().traits.isProjectComponent; + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is project component"_t, category(), keyValue("type id", m_typeId)}; + + auto isProjectComponent = typeData().traits.isProjectComponent; + + tracer.end(keyValue("is project component", isProjectComponent)); + + return isProjectComponent; } return false; @@ -1529,7 +1558,17 @@ bool NodeMetaInfo::isProjectComponent() const bool NodeMetaInfo::isInProjectModule() const { if constexpr (useProjectStorage()) { - return isValid() && typeData().traits.isInProjectModule; + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is project module"_t, category(), keyValue("type id", m_typeId)}; + + auto isInProjectModule = typeData().traits.isInProjectModule; + + tracer.end(keyValue("is project module", isInProjectModule)); + + return isInProjectModule; } return false; @@ -1538,10 +1577,17 @@ bool NodeMetaInfo::isInProjectModule() const FlagIs NodeMetaInfo::canBeContainer() const { if constexpr (useProjectStorage()) { - if (isValid()) - return typeData().traits.canBeContainer; + if (!isValid()) + return FlagIs::False; - return FlagIs::False; + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"can be container"_t, category(), keyValue("type id", m_typeId)}; + + auto canBeContainer = typeData().traits.canBeContainer; + + tracer.end(keyValue("can be container", canBeContainer)); + + return canBeContainer; } return FlagIs::Set; @@ -1550,10 +1596,17 @@ FlagIs NodeMetaInfo::canBeContainer() const FlagIs NodeMetaInfo::forceClip() const { if constexpr (useProjectStorage()) { - if (isValid()) - return typeData().traits.forceClip; + if (!isValid()) + return FlagIs::False; - return FlagIs::False; + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"force clip"_t, category(), keyValue("type id", m_typeId)}; + + auto forceClip = typeData().traits.forceClip; + + tracer.end(keyValue("force clip", forceClip)); + + return forceClip; } return FlagIs::Set; @@ -1562,10 +1615,17 @@ FlagIs NodeMetaInfo::forceClip() const FlagIs NodeMetaInfo::doesLayoutChildren() const { if constexpr (useProjectStorage()) { - if (isValid()) - return typeData().traits.doesLayoutChildren; + if (!isValid()) + return FlagIs::False; - return FlagIs::False; + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"does layout children"_t, category(), keyValue("type id", m_typeId)}; + + auto doesLayoutChildren = typeData().traits.doesLayoutChildren; + + tracer.end(keyValue("does layout children", doesLayoutChildren)); + + return doesLayoutChildren; } return FlagIs::Set; @@ -1574,10 +1634,19 @@ FlagIs NodeMetaInfo::doesLayoutChildren() const FlagIs NodeMetaInfo::canBeDroppedInFormEditor() const { if constexpr (useProjectStorage()) { - if (isValid()) - return typeData().traits.canBeDroppedInFormEditor; + if (!isValid()) + return FlagIs::False; - return FlagIs::False; + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"can be dropped in form editor"_t, + category(), + keyValue("type id", m_typeId)}; + + auto canBeDroppedInFormEditor = typeData().traits.canBeDroppedInFormEditor; + + tracer.end(keyValue("can be dropped in form editor", canBeDroppedInFormEditor)); + + return canBeDroppedInFormEditor; } return FlagIs::Set; @@ -1586,10 +1655,19 @@ FlagIs NodeMetaInfo::canBeDroppedInFormEditor() const FlagIs NodeMetaInfo::canBeDroppedInNavigator() const { if constexpr (useProjectStorage()) { - if (isValid()) - return typeData().traits.canBeDroppedInNavigator; + if (!isValid()) + return FlagIs::False; - return FlagIs::False; + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"can be dropped in navigator"_t, + category(), + keyValue("type id", m_typeId)}; + + auto canBeDroppedInNavigator = typeData().traits.canBeDroppedInNavigator; + + tracer.end(keyValue("can be dropped in navigator", canBeDroppedInNavigator)); + + return canBeDroppedInNavigator; } return FlagIs::Set; @@ -1598,10 +1676,19 @@ FlagIs NodeMetaInfo::canBeDroppedInNavigator() const FlagIs NodeMetaInfo::canBeDroppedInView3D() const { if constexpr (useProjectStorage()) { - if (isValid()) - return typeData().traits.canBeDroppedInView3D; + if (!isValid()) + return FlagIs::False; - return FlagIs::False; + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"can be dropped in view3d"_t, + category(), + keyValue("type id", m_typeId)}; + + auto canBeDroppedInView3D = typeData().traits.canBeDroppedInView3D; + + tracer.end(keyValue("can be dropped in view3d", canBeDroppedInView3D)); + + return canBeDroppedInView3D; } return FlagIs::Set; @@ -1610,10 +1697,17 @@ FlagIs NodeMetaInfo::canBeDroppedInView3D() const FlagIs NodeMetaInfo::isMovable() const { if constexpr (useProjectStorage()) { - if (isValid()) - return typeData().traits.isMovable; + if (!isValid()) + return FlagIs::False; - return FlagIs::False; + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is movable"_t, category(), keyValue("type id", m_typeId)}; + + auto isMovable = typeData().traits.isMovable; + + tracer.end(keyValue("is movable", isMovable)); + + return isMovable; } return FlagIs::Set; @@ -1622,10 +1716,17 @@ FlagIs NodeMetaInfo::isMovable() const FlagIs NodeMetaInfo::isResizable() const { if constexpr (useProjectStorage()) { - if (isValid()) - return typeData().traits.isResizable; + if (!isValid()) + return FlagIs::False; - return FlagIs::False; + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is resizable"_t, category(), keyValue("type id", m_typeId)}; + + auto isResizable = typeData().traits.isResizable; + + tracer.end(keyValue("is resizable", isResizable)); + + return isResizable; } return FlagIs::Set; @@ -1634,10 +1735,17 @@ FlagIs NodeMetaInfo::isResizable() const FlagIs NodeMetaInfo::hasFormEditorItem() const { if constexpr (useProjectStorage()) { - if (isValid()) - return typeData().traits.hasFormEditorItem; + if (!isValid()) + return FlagIs::False; - return FlagIs::False; + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"has form editor item"_t, category(), keyValue("type id", m_typeId)}; + + auto hasFormEditorItem = typeData().traits.hasFormEditorItem; + + tracer.end(keyValue("has form editor item", hasFormEditorItem)); + + return hasFormEditorItem; } return FlagIs::Set; @@ -1646,10 +1754,17 @@ FlagIs NodeMetaInfo::hasFormEditorItem() const FlagIs NodeMetaInfo::isStackedContainer() const { if constexpr (useProjectStorage()) { - if (isValid()) - return typeData().traits.isStackedContainer; + if (!isValid()) + return FlagIs::False; - return FlagIs::False; + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is stacked container"_t, category(), keyValue("type id", m_typeId)}; + + auto isStackedContainer = typeData().traits.isStackedContainer; + + tracer.end(keyValue("is stacked container", isStackedContainer)); + + return isStackedContainer; } return FlagIs::Set; @@ -1658,10 +1773,19 @@ FlagIs NodeMetaInfo::isStackedContainer() const FlagIs NodeMetaInfo::takesOverRenderingOfChildren() const { if constexpr (useProjectStorage()) { - if (isValid()) - return typeData().traits.takesOverRenderingOfChildren; + if (!isValid()) + return FlagIs::False; - return FlagIs::False; + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"takes over rendering of children"_t, + category(), + keyValue("type id", m_typeId)}; + + auto takesOverRenderingOfChildren = typeData().traits.takesOverRenderingOfChildren; + + tracer.end(keyValue("takes over rendering of children", takesOverRenderingOfChildren)); + + return takesOverRenderingOfChildren; } return FlagIs::Set; @@ -1670,10 +1794,17 @@ FlagIs NodeMetaInfo::takesOverRenderingOfChildren() const FlagIs NodeMetaInfo::visibleInNavigator() const { if constexpr (useProjectStorage()) { - if (isValid()) - return typeData().traits.visibleInNavigator; + if (!isValid()) + return FlagIs::False; - return FlagIs::False; + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"visible in navigator"_t, category(), keyValue("type id", m_typeId)}; + + auto visibleInNavigator = typeData().traits.visibleInNavigator; + + tracer.end(keyValue("visible in navigator", visibleInNavigator)); + + return visibleInNavigator; } return FlagIs::Set; @@ -1682,10 +1813,17 @@ FlagIs NodeMetaInfo::visibleInNavigator() const FlagIs NodeMetaInfo::visibleInLibrary() const { if constexpr (useProjectStorage()) { - if (isValid()) - return typeData().traits.visibleInLibrary; + if (!isValid()) + return FlagIs::False; - return FlagIs::False; + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"visible in library"_t, category(), keyValue("type id", m_typeId)}; + + auto visibleInLibrary = typeData().traits.visibleInLibrary; + + tracer.end(keyValue("visible in library", visibleInLibrary)); + + return visibleInLibrary; } return FlagIs::Set; @@ -1697,6 +1835,12 @@ namespace { TypeId typeId, Utils::SmallStringView propertyName) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get combound property id"_t, + category(), + keyValue("type id", typeId), + keyValue("property name", propertyName)}; + auto begin = propertyName.begin(); const auto end = propertyName.end(); @@ -1712,11 +1856,17 @@ namespace { if (propertyId && found != end) { begin = std::next(found); - return projectStorage.propertyDeclarationId(propertyTypeId, {begin, end}); + auto id = projectStorage.propertyDeclarationId(propertyTypeId, {begin, end}); + + tracer.end(keyValue("property id", id)); + + return id; } } } + tracer.end(keyValue("property id", propertyId)); + return propertyId; } @@ -1724,10 +1874,24 @@ namespace { bool NodeMetaInfo::hasProperty(Utils::SmallStringView propertyName) const { - if constexpr (useProjectStorage()) - return isValid() && bool(propertyId(*m_projectStorage, m_typeId, propertyName)); - else + if constexpr (useProjectStorage()) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"has property"_t, + category(), + keyValue("type id", m_typeId), + keyValue("property name", propertyName)}; + + if (!isValid()) + return false; + + auto hasPropertyId = bool(propertyId(*m_projectStorage, m_typeId, propertyName)); + + tracer.end(keyValue("has property", hasPropertyId)); + + return hasPropertyId; + } else { return isValid() && m_privateData->properties().contains(QByteArrayView(propertyName)); + } } PropertyMetaInfos NodeMetaInfo::properties() const @@ -1736,12 +1900,14 @@ PropertyMetaInfos NodeMetaInfo::properties() const return {}; if constexpr (useProjectStorage()) { - if (isValid()) { - return Utils::transform( - m_projectStorage->propertyDeclarationIds(m_typeId), [&](auto id) { - return PropertyMetaInfo{id, m_projectStorage}; - }); - } + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get properties"_t, category(), keyValue("type id", m_typeId)}; + + return Utils::transform( + m_projectStorage->propertyDeclarationIds(m_typeId), [&](auto id) { + return PropertyMetaInfo{id, m_projectStorage}; + }); + } else { const auto &properties = m_privateData->properties(); @@ -1753,19 +1919,22 @@ PropertyMetaInfos NodeMetaInfo::properties() const return propertyMetaInfos; } - - return {}; } PropertyMetaInfos NodeMetaInfo::localProperties() const { + if (!isValid()) + return {}; + if constexpr (useProjectStorage()) { - if (isValid()) { - return Utils::transform( - m_projectStorage->localPropertyDeclarationIds(m_typeId), [&](auto id) { - return PropertyMetaInfo{id, m_projectStorage}; - }); - } + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get local properties"_t, category(), keyValue("type id", m_typeId)}; + + return Utils::transform( + m_projectStorage->localPropertyDeclarationIds(m_typeId), [&](auto id) { + return PropertyMetaInfo{id, m_projectStorage}; + }); + } else { const auto &properties = m_privateData->localProperties(); @@ -1777,71 +1946,82 @@ PropertyMetaInfos NodeMetaInfo::localProperties() const return propertyMetaInfos; } - - return {}; } PropertyMetaInfo NodeMetaInfo::property(const PropertyName &propertyName) const { + if (!isValid()) + return {}; + if constexpr (useProjectStorage()) { - if (isValid()) - return {propertyId(*m_projectStorage, m_typeId, propertyName), m_projectStorage}; + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get property"_t, + category(), + keyValue("type id", m_typeId), + keyValue("property name", propertyName)}; + + return {propertyId(*m_projectStorage, m_typeId, propertyName), m_projectStorage}; } else { if (hasProperty(propertyName)) { return PropertyMetaInfo{m_privateData, propertyName}; } + return {}; } - - return {}; } PropertyNameList NodeMetaInfo::signalNames() const { - if constexpr (useProjectStorage()) { - if (isValid()) { - return Utils::transform(m_projectStorage->signalDeclarationNames( - m_typeId), - [&](const auto &name) { - return name.toQByteArray(); - }); - } - } else { - if (isValid()) - return m_privateData->signalNames(); - } + if (!isValid()) + return {}; - return {}; + if constexpr (useProjectStorage()) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get signal names"_t, category(), keyValue("type id", m_typeId)}; + + return Utils::transform(m_projectStorage->signalDeclarationNames(m_typeId), + [&](const auto &name) { + return name.toQByteArray(); + }); + + } else { + return m_privateData->signalNames(); + } } PropertyNameList NodeMetaInfo::slotNames() const { - if constexpr (useProjectStorage()) { - if (isValid()) { - return Utils::transform(m_projectStorage->functionDeclarationNames( - m_typeId), - [&](const auto &name) { - return name.toQByteArray(); - }); - } - } else { - if (isValid()) - return m_privateData->slotNames(); - } + if (!isValid()) + return {}; - return {}; + if constexpr (useProjectStorage()) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get slot names"_t, category(), keyValue("type id", m_typeId)}; + return Utils::transform(m_projectStorage->functionDeclarationNames(m_typeId), + [&](const auto &name) { + return name.toQByteArray(); + }); + } else { + return m_privateData->slotNames(); + } } PropertyName NodeMetaInfo::defaultPropertyName() const { + if (!isValid()) + return {}; + if constexpr (useProjectStorage()) { - if (isValid()) { - if (auto name = m_projectStorage->propertyName(defaultPropertyDeclarationId())) { - return name->toQByteArray(); - } + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get default property name"_t, + category(), + keyValue("type id", m_typeId)}; + if (auto name = m_projectStorage->propertyName(defaultPropertyDeclarationId())) { + tracer.end(keyValue("default property name", name)); + return name->toQByteArray(); } + } else { - if (isValid()) - return m_privateData->defaultPropertyName(); + return m_privateData->defaultPropertyName(); } return {}; @@ -1849,88 +2029,128 @@ PropertyName NodeMetaInfo::defaultPropertyName() const PropertyMetaInfo NodeMetaInfo::defaultProperty() const { + if (!isValid()) + return {}; + if constexpr (useProjectStorage()) { - if (isValid()) { - return PropertyMetaInfo(defaultPropertyDeclarationId(), m_projectStorage); - } + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get default property"_t, category(), keyValue("type id", m_typeId)}; + + auto id = defaultPropertyDeclarationId(); + + tracer.end(keyValue("default property id", id)); + + return PropertyMetaInfo(id, m_projectStorage); } else { return property(defaultPropertyName()); } - - return {}; } bool NodeMetaInfo::hasDefaultProperty() const { - if constexpr (useProjectStorage()) - return isValid() && bool(defaultPropertyDeclarationId()); - else + if (!isValid()) + return false; + + if constexpr (useProjectStorage()) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"has default property"_t, category(), keyValue("type id", m_typeId)}; + auto hasDefaultProperty = bool(defaultPropertyDeclarationId()); + tracer.end(keyValue("has default property", hasDefaultProperty)); + + return hasDefaultProperty; + } else { return !defaultPropertyName().isEmpty(); + } } std::vector NodeMetaInfo::selfAndPrototypes() const { + if (!isValid()) + return {}; + if constexpr (useProjectStorage()) { - if (isValid()) { - return Utils::transform( - m_projectStorage->prototypeAndSelfIds(m_typeId), [&](TypeId typeId) { - return NodeMetaInfo{typeId, m_projectStorage}; - }); - } + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get self and prototypes"_t, + category(), + keyValue("type id", m_typeId)}; + + return Utils::transform(m_projectStorage->prototypeAndSelfIds(m_typeId), + [&](TypeId typeId) { + return NodeMetaInfo{typeId, m_projectStorage}; + }); } else { - if (isValid()) { - NodeMetaInfos hierarchy = {*this}; - Model *model = m_privateData->model(); - for (const TypeDescription &type : m_privateData->prototypes()) { - auto &last = hierarchy.emplace_back(model, - type.className.toUtf8(), - type.majorVersion, - type.minorVersion); - if (!last.isValid()) - hierarchy.pop_back(); - } - - return hierarchy; + NodeMetaInfos hierarchy = {*this}; + Model *model = m_privateData->model(); + for (const TypeDescription &type : m_privateData->prototypes()) { + auto &last = hierarchy.emplace_back(model, + type.className.toUtf8(), + type.majorVersion, + type.minorVersion); + if (!last.isValid()) + hierarchy.pop_back(); } - } - return {}; + return hierarchy; + } } NodeMetaInfos NodeMetaInfo::prototypes() const { + if (!isValid()) + return {}; + if constexpr (useProjectStorage()) { - if (isValid()) { - return Utils::transform( - m_projectStorage->prototypeIds(m_typeId), [&](TypeId typeId) { - return NodeMetaInfo{typeId, m_projectStorage}; - }); - } + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get prototypes"_t, category(), keyValue("type id", m_typeId)}; + return Utils::transform(m_projectStorage->prototypeIds(m_typeId), + [&](TypeId typeId) { + return NodeMetaInfo{typeId, m_projectStorage}; + }); + } else { - if (isValid()) { - NodeMetaInfos hierarchy; - Model *model = m_privateData->model(); - for (const TypeDescription &type : m_privateData->prototypes()) { - auto &last = hierarchy.emplace_back(model, - type.className.toUtf8(), - type.majorVersion, - type.minorVersion); - if (!last.isValid()) - hierarchy.pop_back(); - } - - return hierarchy; + NodeMetaInfos hierarchy; + Model *model = m_privateData->model(); + for (const TypeDescription &type : m_privateData->prototypes()) { + auto &last = hierarchy.emplace_back(model, + type.className.toUtf8(), + type.majorVersion, + type.minorVersion); + if (!last.isValid()) + hierarchy.pop_back(); } - } - return {}; + return hierarchy; + } } +namespace { +template +bool isBasedOnCommonType(NotNullPointer projectStorage, TypeId typeId) +{ + if (!typeId) + return false; + + auto base = projectStorage->commonTypeId(); + + return projectStorage->isBasedOn(typeId, base); +} +} // namespace + bool NodeMetaInfo::defaultPropertyIsComponent() const { - if (hasDefaultProperty()) - return defaultProperty().propertyType().isQmlComponent(); + if (!isValid()) + return false; - return false; + if (useProjectStorage()) { + auto id = defaultPropertyDeclarationId(); + auto propertyDeclaration = m_projectStorage->propertyDeclaration(id); + + using namespace Storage::Info; + return isBasedOnCommonType(m_projectStorage, propertyDeclaration->typeId); + } else { + if (hasDefaultProperty()) + return defaultProperty().propertyType().isQmlComponent(); + return false; + } } TypeName NodeMetaInfo::displayName() const @@ -1976,10 +2196,16 @@ int NodeMetaInfo::minorVersion() const Storage::Info::ExportedTypeNames NodeMetaInfo::allExportedTypeNames() const { + if (!isValid()) + return {}; + if constexpr (useProjectStorage()) { - if (isValid()) { - return m_projectStorage->exportedTypeNames(m_typeId); - } + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get all exported type names"_t, + category(), + keyValue("type id", m_typeId)}; + + return m_projectStorage->exportedTypeNames(m_typeId); } return {}; @@ -1987,10 +2213,17 @@ Storage::Info::ExportedTypeNames NodeMetaInfo::allExportedTypeNames() const Storage::Info::ExportedTypeNames NodeMetaInfo::exportedTypeNamesForSourceId(SourceId sourceId) const { + if (!isValid()) + return {}; + if constexpr (useProjectStorage()) { - if (isValid()) { - return m_projectStorage->exportedTypeNames(m_typeId, sourceId); - } + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get exported type names for source id"_t, + category(), + keyValue("type id", m_typeId), + keyValue("source id", sourceId)}; + + return m_projectStorage->exportedTypeNames(m_typeId, sourceId); } return {}; @@ -1998,9 +2231,18 @@ Storage::Info::ExportedTypeNames NodeMetaInfo::exportedTypeNamesForSourceId(Sour Storage::Info::TypeHints NodeMetaInfo::typeHints() const { + if (!isValid()) + return {}; + if constexpr (useProjectStorage()) { - if (isValid()) - return m_projectStorage->typeHints(m_typeId); + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get type hints"_t, category(), keyValue("type id", m_typeId)}; + + auto hints = m_projectStorage->typeHints(m_typeId); + + tracer.end(keyValue("type hints", hints)); + + return hints; } return {}; @@ -2008,9 +2250,18 @@ Storage::Info::TypeHints NodeMetaInfo::typeHints() const Utils::PathString NodeMetaInfo::iconPath() const { + if (!isValid()) + return {}; + if constexpr (useProjectStorage()) { - if (isValid()) - return m_projectStorage->typeIconPath(m_typeId); + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get icon path"_t, category(), keyValue("type id", m_typeId)}; + + auto iconPath = m_projectStorage->typeIconPath(m_typeId); + + tracer.end(keyValue("icon path", iconPath)); + + return iconPath; } return {}; @@ -2018,9 +2269,20 @@ Utils::PathString NodeMetaInfo::iconPath() const Storage::Info::ItemLibraryEntries NodeMetaInfo::itemLibrariesEntries() const { + if (!isValid()) + return {}; + if constexpr (useProjectStorage()) { - if (isValid()) - return m_projectStorage->itemLibraryEntries(m_typeId); + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get item library entries"_t, + category(), + keyValue("type id", m_typeId)}; + + auto entries = m_projectStorage->itemLibraryEntries(m_typeId); + + tracer.end(keyValue("item library entries", entries)); + + return entries; } return {}; @@ -2028,10 +2290,18 @@ Storage::Info::ItemLibraryEntries NodeMetaInfo::itemLibrariesEntries() const SourceId NodeMetaInfo::sourceId() const { + if (!isValid()) + return SourceId{}; + if constexpr (useProjectStorage()) { - if (isValid()) { - return typeData().sourceId; - } + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get source id"_t, category(), keyValue("type id", m_typeId)}; + + auto id = typeData().sourceId; + + tracer.end(keyValue("source id", id)); + + return id; } return SourceId{}; @@ -2064,18 +2334,31 @@ QString NodeMetaInfo::requiredImportString() const if (!isValid()) return {}; - Import imp = m_privateData->requiredImport(); - if (!imp.isEmpty()) - return imp.toImportString(); + if constexpr (!useProjectStorage()) { + Import imp = m_privateData->requiredImport(); + if (!imp.isEmpty()) + return imp.toImportString(); + } return {}; } SourceId NodeMetaInfo::propertyEditorPathId() const { + if (!isValid()) + return SourceId{}; + if (useProjectStorage()) { - if (isValid()) - return m_projectStorage->propertyEditorPathId(m_typeId); + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get property editor path id"_t, + category(), + keyValue("type id", m_typeId)}; + + auto id = m_projectStorage->propertyEditorPathId(m_typeId); + + tracer.end(keyValue("property editor path id", id)); + + return id; } return SourceId{}; @@ -2133,9 +2416,13 @@ bool NodeMetaInfo::isSubclassOf(const TypeName &type, int majorVersion, int mino bool NodeMetaInfo::isSuitableForMouseAreaFill() const { if constexpr (useProjectStorage()) { - if (!isValid()) { + if (!isValid()) return false; - } + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is suitable for mouse area fill"_t, + category(), + keyValue("type id", m_typeId)}; using namespace Storage::Info; auto itemId = m_projectStorage->commonTypeId(); @@ -2143,11 +2430,16 @@ bool NodeMetaInfo::isSuitableForMouseAreaFill() const auto controlsControlId = m_projectStorage->commonTypeId(); auto templatesControlId = m_projectStorage->commonTypeId(); - return m_projectStorage->isBasedOn(m_typeId, - itemId, - mouseAreaId, - controlsControlId, - templatesControlId); + auto isSuitableForMouseAreaFill = m_projectStorage->isBasedOn(m_typeId, + itemId, + mouseAreaId, + controlsControlId, + templatesControlId); + + tracer.end(keyValue("is suitable for mouse area fill", isSuitableForMouseAreaFill)); + + return isSuitableForMouseAreaFill; + } else { return isSubclassOf("QtQuick.Item") && !isSubclassOf("QtQuick.MouseArea") && !isSubclassOf("QtQuick.Controls.Control") @@ -2158,6 +2450,15 @@ bool NodeMetaInfo::isSuitableForMouseAreaFill() const bool NodeMetaInfo::isBasedOn(const NodeMetaInfo &metaInfo) const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is based on"_t, + category(), + keyValue("type id", m_typeId), + keyValue("meta info type id", metaInfo.m_typeId)}; + return m_projectStorage->isBasedOn(m_typeId, metaInfo.m_typeId); } else { if (!isValid()) @@ -2171,6 +2472,12 @@ bool NodeMetaInfo::isBasedOn(const NodeMetaInfo &metaInfo) const bool NodeMetaInfo::isBasedOn(const NodeMetaInfo &metaInfo1, const NodeMetaInfo &metaInfo2) const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is based on"_t, category(), keyValue("type id", m_typeId)}; + return m_projectStorage->isBasedOn(m_typeId, metaInfo1.m_typeId, metaInfo2.m_typeId); } else { if (!isValid()) @@ -2189,6 +2496,12 @@ bool NodeMetaInfo::isBasedOn(const NodeMetaInfo &metaInfo1, const NodeMetaInfo &metaInfo3) const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is based on"_t, category(), keyValue("type id", m_typeId)}; + return m_projectStorage->isBasedOn(m_typeId, metaInfo1.m_typeId, metaInfo2.m_typeId, @@ -2213,6 +2526,12 @@ bool NodeMetaInfo::isBasedOn(const NodeMetaInfo &metaInfo1, const NodeMetaInfo &metaInfo4) const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is based on"_t, category(), keyValue("type id", m_typeId)}; + return m_projectStorage->isBasedOn(m_typeId, metaInfo1.m_typeId, metaInfo2.m_typeId, @@ -2240,6 +2559,12 @@ bool NodeMetaInfo::isBasedOn(const NodeMetaInfo &metaInfo1, const NodeMetaInfo &metaInfo5) const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is based on"_t, category(), keyValue("type id", m_typeId)}; + return m_projectStorage->isBasedOn(m_typeId, metaInfo1.m_typeId, metaInfo2.m_typeId, @@ -2272,6 +2597,12 @@ bool NodeMetaInfo::isBasedOn(const NodeMetaInfo &metaInfo1, const NodeMetaInfo &metaInfo6) const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is based on"_t, category(), keyValue("type id", m_typeId)}; + return m_projectStorage->isBasedOn(m_typeId, metaInfo1.m_typeId, metaInfo2.m_typeId, @@ -2309,6 +2640,12 @@ bool NodeMetaInfo::isBasedOn(const NodeMetaInfo &metaInfo1, const NodeMetaInfo &metaInfo7) const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is based on"_t, category(), keyValue("type id", m_typeId)}; + return m_projectStorage->isBasedOn(m_typeId, metaInfo1.m_typeId, metaInfo2.m_typeId, @@ -2341,26 +2678,14 @@ bool NodeMetaInfo::isBasedOn(const NodeMetaInfo &metaInfo1, } } -namespace { -template -bool isBasedOnCommonType(NotNullPointer projectStorage, TypeId typeId) -{ - if (!typeId) { - return false; - } - - auto base = projectStorage->commonTypeId(); - - return projectStorage->isBasedOn(typeId, base); -} -} // namespace - bool NodeMetaInfo::isGraphicalItem() const { if constexpr (useProjectStorage()) { - if (!isValid()) { + if (!isValid()) return false; - } + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is graphical item"_t, category(), keyValue("type id", m_typeId)}; using namespace Storage::Info; auto itemId = m_projectStorage->commonTypeId(); @@ -2380,6 +2705,12 @@ bool NodeMetaInfo::isGraphicalItem() const bool NodeMetaInfo::isQtObject() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is Qt object"_t, category(), keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); } else { @@ -2390,6 +2721,14 @@ bool NodeMetaInfo::isQtObject() const bool NodeMetaInfo::isQtQmlConnections() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is Qt Qml connections"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); } else { @@ -2400,9 +2739,11 @@ bool NodeMetaInfo::isQtQmlConnections() const bool NodeMetaInfo::isLayoutable() const { if constexpr (useProjectStorage()) { - if (!isValid()) { + if (!isValid()) return false; - } + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is layoutable"_t, category(), keyValue("type id", m_typeId)}; using namespace Storage::Info; auto positionerId = m_projectStorage->commonTypeId(); @@ -2421,6 +2762,14 @@ bool NodeMetaInfo::isLayoutable() const bool NodeMetaInfo::isQtQuickLayoutsLayout() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick.Layouts.Layout"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); } else { @@ -2431,9 +2780,11 @@ bool NodeMetaInfo::isQtQuickLayoutsLayout() const bool NodeMetaInfo::isView() const { if constexpr (useProjectStorage()) { - if (!isValid()) { + if (!isValid()) return false; - } + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is view"_t, category(), keyValue("type id", m_typeId)}; using namespace Storage::Info; auto listViewId = m_projectStorage->commonTypeId(); @@ -2450,7 +2801,13 @@ bool NodeMetaInfo::isView() const bool NodeMetaInfo::usesCustomParser() const { if constexpr (useProjectStorage()) { - return isValid() && typeData().traits.usesCustomParser; + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"uses custom parser"_t, category(), keyValue("type id", m_typeId)}; + + return typeData().traits.usesCustomParser; } else { if (!isValid()) return false; @@ -2476,8 +2833,14 @@ bool isTypeId(TypeId typeId, TypeIds... otherTypeIds) bool NodeMetaInfo::isVector2D() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is vector2d"_t, category(), keyValue("type id", m_typeId)}; + using namespace Storage::Info; - return isValid() && isTypeId(m_typeId, m_projectStorage->commonTypeId()); + return isTypeId(m_typeId, m_projectStorage->commonTypeId()); } else { if (!m_privateData) return false; @@ -2491,8 +2854,14 @@ bool NodeMetaInfo::isVector2D() const bool NodeMetaInfo::isVector3D() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is vector3d"_t, category(), keyValue("type id", m_typeId)}; + using namespace Storage::Info; - return isValid() && isTypeId(m_typeId, m_projectStorage->commonTypeId()); + return isTypeId(m_typeId, m_projectStorage->commonTypeId()); } else { if (!m_privateData) return false; @@ -2506,8 +2875,14 @@ bool NodeMetaInfo::isVector3D() const bool NodeMetaInfo::isVector4D() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is vector4d"_t, category(), keyValue("type id", m_typeId)}; + using namespace Storage::Info; - return isValid() && isTypeId(m_typeId, m_projectStorage->commonTypeId()); + return isTypeId(m_typeId, m_projectStorage->commonTypeId()); } else { if (!m_privateData) return false; @@ -2521,6 +2896,14 @@ bool NodeMetaInfo::isVector4D() const bool NodeMetaInfo::isQtQuickPropertyChanges() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick.PropertyChanges"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); @@ -2532,6 +2915,14 @@ bool NodeMetaInfo::isQtQuickPropertyChanges() const bool NodeMetaInfo::isQtSafeRendererSafeRendererPicture() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is Qt.SafeRenderer.SafeRendererPicture"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); } else { @@ -2542,6 +2933,14 @@ bool NodeMetaInfo::isQtSafeRendererSafeRendererPicture() const bool NodeMetaInfo::isQtSafeRendererSafePicture() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is Qt.SafeRenderer.SafePicture"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); } else { @@ -2552,6 +2951,14 @@ bool NodeMetaInfo::isQtSafeRendererSafePicture() const bool NodeMetaInfo::isQtQuickTimelineKeyframe() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick.Timeline.Keyframe"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); @@ -2563,6 +2970,14 @@ bool NodeMetaInfo::isQtQuickTimelineKeyframe() const bool NodeMetaInfo::isQtQuickTimelineTimelineAnimation() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick.Timeline.TimelineAnimation"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); } else { @@ -2573,6 +2988,14 @@ bool NodeMetaInfo::isQtQuickTimelineTimelineAnimation() const bool NodeMetaInfo::isQtQuickTimelineTimeline() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick.Timeline.Timeline"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); } else { @@ -2583,6 +3006,14 @@ bool NodeMetaInfo::isQtQuickTimelineTimeline() const bool NodeMetaInfo::isQtQuickTimelineKeyframeGroup() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick.Timeline.KeyframeGroup"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); } else { @@ -2593,9 +3024,11 @@ bool NodeMetaInfo::isQtQuickTimelineKeyframeGroup() const bool NodeMetaInfo::isListOrGridView() const { if constexpr (useProjectStorage()) { - if (!isValid()) { + if (!isValid()) return false; - } + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is list or grid view"_t, category(), keyValue("type id", m_typeId)}; using namespace Storage::Info; auto listViewId = m_projectStorage->commonTypeId(); @@ -2609,9 +3042,11 @@ bool NodeMetaInfo::isListOrGridView() const bool NodeMetaInfo::isNumber() const { if constexpr (useProjectStorage()) { - if (!isValid()) { + if (!isValid()) return false; - } + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is number"_t, category(), keyValue("type id", m_typeId)}; using namespace Storage::Info; auto intId = m_projectStorage->builtinTypeId(); @@ -2632,6 +3067,14 @@ bool NodeMetaInfo::isNumber() const bool NodeMetaInfo::isQtQuickExtrasPicture() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick.Extras.Picture"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); } else { @@ -2642,6 +3085,12 @@ bool NodeMetaInfo::isQtQuickExtrasPicture() const bool NodeMetaInfo::isQtQuickImage() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick.Image"_t, category(), keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); @@ -2653,6 +3102,14 @@ bool NodeMetaInfo::isQtQuickImage() const bool NodeMetaInfo::isQtQuickBorderImage() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick.BorderImage"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); @@ -2664,7 +3121,13 @@ bool NodeMetaInfo::isQtQuickBorderImage() const bool NodeMetaInfo::isAlias() const { if constexpr (useProjectStorage()) { - return false; // there is no type alias + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is alias"_t, category(), keyValue("type id", m_typeId)}; + + return false; // all types are already resolved } else { return isValid() && m_privateData->qualfiedTypeName() == "alias"; } @@ -2673,6 +3136,14 @@ bool NodeMetaInfo::isAlias() const bool NodeMetaInfo::isQtQuickPositioner() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick.Positioner"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); @@ -2684,6 +3155,14 @@ bool NodeMetaInfo::isQtQuickPositioner() const bool NodeMetaInfo::isQtQuickPropertyAnimation() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick.PropertyAnimation"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); } else { @@ -2694,6 +3173,12 @@ bool NodeMetaInfo::isQtQuickPropertyAnimation() const bool NodeMetaInfo::isQtQuickRepeater() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick.Repeater"_t, category(), keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); } else { @@ -2704,6 +3189,14 @@ bool NodeMetaInfo::isQtQuickRepeater() const bool NodeMetaInfo::isQtQuickControlsTabBar() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick.Controls.TabBar"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); } else { @@ -2714,6 +3207,14 @@ bool NodeMetaInfo::isQtQuickControlsTabBar() const bool NodeMetaInfo::isQtQuickControlsSwipeView() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick.Controls.SwipeView"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); } else { @@ -2724,6 +3225,12 @@ bool NodeMetaInfo::isQtQuickControlsSwipeView() const bool NodeMetaInfo::isQtQuick3DCamera() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick3D.Camera"_t, category(), keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); } else { @@ -2734,6 +3241,14 @@ bool NodeMetaInfo::isQtQuick3DCamera() const bool NodeMetaInfo::isQtQuick3DBakedLightmap() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick3D.BakedLightmap"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); } else { @@ -2744,6 +3259,12 @@ bool NodeMetaInfo::isQtQuick3DBakedLightmap() const bool NodeMetaInfo::isQtQuick3DBuffer() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick3D.Buffer"_t, category(), keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); } else { @@ -2754,6 +3275,14 @@ bool NodeMetaInfo::isQtQuick3DBuffer() const bool NodeMetaInfo::isQtQuick3DInstanceListEntry() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick3D.InstanceListEntry"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); } else { @@ -2764,6 +3293,12 @@ bool NodeMetaInfo::isQtQuick3DInstanceListEntry() const bool NodeMetaInfo::isQtQuick3DLight() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick3D.Light"_t, category(), keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); } else { @@ -2774,6 +3309,14 @@ bool NodeMetaInfo::isQtQuick3DLight() const bool NodeMetaInfo::isQtQmlModelsListElement() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQml.Models.ListElement"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); } else { @@ -2784,6 +3327,12 @@ bool NodeMetaInfo::isQtQmlModelsListElement() const bool NodeMetaInfo::isQtQuickListModel() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick.ListModel"_t, category(), keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); } else { @@ -2794,6 +3343,12 @@ bool NodeMetaInfo::isQtQuickListModel() const bool NodeMetaInfo::isQtQuickListView() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick.ListView"_t, category(), keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); } else { @@ -2804,6 +3359,14 @@ bool NodeMetaInfo::isQtQuickListView() const bool NodeMetaInfo::isQtQuick3DInstanceList() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick3D.InstanceList"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); } else { @@ -2814,6 +3377,14 @@ bool NodeMetaInfo::isQtQuick3DInstanceList() const bool NodeMetaInfo::isQtQuick3DParticles3DParticle3D() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick3D.Particles3D.Particle3D"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); } else { @@ -2824,6 +3395,14 @@ bool NodeMetaInfo::isQtQuick3DParticles3DParticle3D() const bool NodeMetaInfo::isQtQuick3DParticles3DParticleEmitter3D() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick3D.Particles3D.ParticleEmitter3D"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); @@ -2835,6 +3414,14 @@ bool NodeMetaInfo::isQtQuick3DParticles3DParticleEmitter3D() const bool NodeMetaInfo::isQtQuick3DParticles3DAttractor3D() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick3D.Particles3D.Attractor3D"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); } else { @@ -2845,6 +3432,14 @@ bool NodeMetaInfo::isQtQuick3DParticles3DAttractor3D() const bool NodeMetaInfo::isQtQuick3DParticlesAbstractShape() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick3D.Particles3D.AbstractShape"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType( m_projectStorage, m_typeId); @@ -2856,6 +3451,12 @@ bool NodeMetaInfo::isQtQuick3DParticlesAbstractShape() const bool NodeMetaInfo::isQtQuickItem() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick.Item"_t, category(), keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); } else { @@ -2866,6 +3467,12 @@ bool NodeMetaInfo::isQtQuickItem() const bool NodeMetaInfo::isQtQuickPath() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick.Path"_t, category(), keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); } else { @@ -2876,6 +3483,14 @@ bool NodeMetaInfo::isQtQuickPath() const bool NodeMetaInfo::isQtQuickPauseAnimation() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick.PauseAnimation"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); } else { @@ -2886,6 +3501,14 @@ bool NodeMetaInfo::isQtQuickPauseAnimation() const bool NodeMetaInfo::isQtQuickTransition() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick.Transition"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); } else { @@ -2896,6 +3519,14 @@ bool NodeMetaInfo::isQtQuickTransition() const bool NodeMetaInfo::isQtQuickWindowWindow() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick.Window.Window"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); } else { @@ -2906,6 +3537,12 @@ bool NodeMetaInfo::isQtQuickWindowWindow() const bool NodeMetaInfo::isQtQuickLoader() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick.Loader"_t, category(), keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); } else { @@ -2916,6 +3553,12 @@ bool NodeMetaInfo::isQtQuickLoader() const bool NodeMetaInfo::isQtQuickState() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick.State"_t, category(), keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); } else { @@ -2926,6 +3569,14 @@ bool NodeMetaInfo::isQtQuickState() const bool NodeMetaInfo::isQtQuickStateOperation() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick.StateOperation"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); @@ -2937,6 +3588,12 @@ bool NodeMetaInfo::isQtQuickStateOperation() const bool NodeMetaInfo::isQtQuickText() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick.Text"_t, category(), keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); } else { @@ -2947,6 +3604,14 @@ bool NodeMetaInfo::isQtQuickText() const bool NodeMetaInfo::isQtMultimediaSoundEffect() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtMultimedia.SoundEffect"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); } else { @@ -2957,9 +3622,11 @@ bool NodeMetaInfo::isQtMultimediaSoundEffect() const bool NodeMetaInfo::isFlowViewItem() const { if constexpr (useProjectStorage()) { - if (!isValid()) { + if (!isValid()) return false; - } + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is FlowView.ViewItem"_t, category(), keyValue("type id", m_typeId)}; using namespace Storage::Info; auto flowItemId = m_projectStorage->commonTypeId(); @@ -2976,6 +3643,12 @@ bool NodeMetaInfo::isFlowViewItem() const bool NodeMetaInfo::isFlowViewFlowItem() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is FlowView.FlowItem"_t, category(), keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); } else { @@ -2986,6 +3659,12 @@ bool NodeMetaInfo::isFlowViewFlowItem() const bool NodeMetaInfo::isFlowViewFlowView() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is FlowView.FlowView"_t, category(), keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); } else { @@ -3006,6 +3685,14 @@ bool NodeMetaInfo::isFlowViewFlowActionArea() const bool NodeMetaInfo::isFlowViewFlowTransition() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is FlowView.FlowTransition"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); } else { @@ -3016,6 +3703,14 @@ bool NodeMetaInfo::isFlowViewFlowTransition() const bool NodeMetaInfo::isFlowViewFlowDecision() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is FlowView.FlowDecision"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); } else { @@ -3026,6 +3721,14 @@ bool NodeMetaInfo::isFlowViewFlowDecision() const bool NodeMetaInfo::isFlowViewFlowWildcard() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is FlowView.FlowWildcard"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); } else { @@ -3036,6 +3739,14 @@ bool NodeMetaInfo::isFlowViewFlowWildcard() const bool NodeMetaInfo::isQtQuickStudioComponentsGroupItem() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick.Studio.Components.GroupItem"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); } else { @@ -3046,6 +3757,14 @@ bool NodeMetaInfo::isQtQuickStudioComponentsGroupItem() const bool NodeMetaInfo::isQtQuickStudioUtilsJsonListModel() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick.Studio.Utils.JsonListModel"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); @@ -3057,6 +3776,12 @@ bool NodeMetaInfo::isQtQuickStudioUtilsJsonListModel() const bool NodeMetaInfo::isQmlComponent() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QML.Component"_t, category(), keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); } else { @@ -3072,6 +3797,12 @@ bool NodeMetaInfo::isQmlComponent() const bool NodeMetaInfo::isFont() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is font"_t, category(), keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isValid() && isTypeId(m_typeId, m_projectStorage->commonTypeId()); } else { @@ -3082,6 +3813,12 @@ bool NodeMetaInfo::isFont() const bool NodeMetaInfo::isColor() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is color"_t, category(), keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isValid() && isTypeId(m_typeId, m_projectStorage->builtinTypeId()); } else { @@ -3097,6 +3834,12 @@ bool NodeMetaInfo::isColor() const bool NodeMetaInfo::isBool() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is bool"_t, category(), keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isValid() && isTypeId(m_typeId, m_projectStorage->builtinTypeId()); } else { @@ -3112,6 +3855,12 @@ bool NodeMetaInfo::isBool() const bool NodeMetaInfo::isInteger() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is integer"_t, category(), keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isValid() && isTypeId(m_typeId, m_projectStorage->builtinTypeId()); } else { @@ -3127,9 +3876,11 @@ bool NodeMetaInfo::isInteger() const bool NodeMetaInfo::isFloat() const { if constexpr (useProjectStorage()) { - if (!isValid()) { + if (!isValid()) return false; - } + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is float"_t, category(), keyValue("type id", m_typeId)}; using namespace Storage::Info; auto floatId = m_projectStorage->builtinTypeId(); @@ -3149,6 +3900,12 @@ bool NodeMetaInfo::isFloat() const bool NodeMetaInfo::isVariant() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is variant"_t, category(), keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isValid() && isTypeId(m_typeId, m_projectStorage->builtinTypeId()); } else { @@ -3164,6 +3921,12 @@ bool NodeMetaInfo::isVariant() const bool NodeMetaInfo::isString() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is string"_t, category(), keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isValid() && isTypeId(m_typeId, m_projectStorage->builtinTypeId()); } else { @@ -3179,6 +3942,12 @@ bool NodeMetaInfo::isString() const bool NodeMetaInfo::isUrl() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is url"_t, category(), keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isValid() && isTypeId(m_typeId, m_projectStorage->builtinTypeId()); } else { @@ -3194,6 +3963,12 @@ bool NodeMetaInfo::isUrl() const bool NodeMetaInfo::isQtQuick3DTexture() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick3D.Texture"_t, category(), keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); } else { @@ -3205,6 +3980,12 @@ bool NodeMetaInfo::isQtQuick3DTexture() const bool NodeMetaInfo::isQtQuick3DShader() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick3D.Shader"_t, category(), keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); } else { @@ -3215,6 +3996,12 @@ bool NodeMetaInfo::isQtQuick3DShader() const bool NodeMetaInfo::isQtQuick3DPass() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick3D.Pass"_t, category(), keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); } else { @@ -3225,6 +4012,12 @@ bool NodeMetaInfo::isQtQuick3DPass() const bool NodeMetaInfo::isQtQuick3DCommand() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick3D.Command"_t, category(), keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); } else { @@ -3235,6 +4028,14 @@ bool NodeMetaInfo::isQtQuick3DCommand() const bool NodeMetaInfo::isQtQuick3DDefaultMaterial() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick3D.DefaultMaterial"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); } else { @@ -3255,6 +4056,12 @@ bool NodeMetaInfo::isQtQuick3DMaterial() const bool NodeMetaInfo::isQtQuick3DModel() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick3D.Model"_t, category(), keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); } else { @@ -3265,6 +4072,12 @@ bool NodeMetaInfo::isQtQuick3DModel() const bool NodeMetaInfo::isQtQuick3DNode() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick3D.Node"_t, category(), keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); } else { @@ -3275,6 +4088,14 @@ bool NodeMetaInfo::isQtQuick3DNode() const bool NodeMetaInfo::isQtQuick3DParticles3DAffector3D() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick3D.Particles3D.Affector3D"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); } else { @@ -3285,6 +4106,12 @@ bool NodeMetaInfo::isQtQuick3DParticles3DAffector3D() const bool NodeMetaInfo::isQtQuick3DView3D() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick3D.View3D"_t, category(), keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); } else { @@ -3295,6 +4122,14 @@ bool NodeMetaInfo::isQtQuick3DView3D() const bool NodeMetaInfo::isQtQuick3DPrincipledMaterial() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick3D.PrincipledMaterial"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); } else { @@ -3305,6 +4140,14 @@ bool NodeMetaInfo::isQtQuick3DPrincipledMaterial() const bool NodeMetaInfo::isQtQuick3DSpecularGlossyMaterial() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick3D.SpecularGlossyMaterial"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); } else { @@ -3315,6 +4158,14 @@ bool NodeMetaInfo::isQtQuick3DSpecularGlossyMaterial() const bool NodeMetaInfo::isQtQuick3DParticles3DSpriteParticle3D() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick3D.Particles3D.SpriteParticle3D"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); @@ -3326,6 +4177,14 @@ bool NodeMetaInfo::isQtQuick3DParticles3DSpriteParticle3D() const bool NodeMetaInfo::isQtQuick3DTextureInput() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick3D.TextureInput"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); } else { @@ -3336,6 +4195,14 @@ bool NodeMetaInfo::isQtQuick3DTextureInput() const bool NodeMetaInfo::isQtQuick3DCubeMapTexture() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick3D.CubeMapTexture"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); } else { @@ -3348,6 +4215,14 @@ bool NodeMetaInfo::isQtQuick3DCubeMapTexture() const bool NodeMetaInfo::isQtQuick3DSceneEnvironment() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick3D.SceneEnvironment"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); } else { @@ -3358,6 +4233,12 @@ bool NodeMetaInfo::isQtQuick3DSceneEnvironment() const bool NodeMetaInfo::isQtQuick3DEffect() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick3D.Effect"_t, category(), keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); } else { @@ -3367,8 +4248,15 @@ bool NodeMetaInfo::isQtQuick3DEffect() const bool NodeMetaInfo::isEnumeration() const { - if constexpr (useProjectStorage()) - return isValid() && typeData().traits.isEnum; + if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is enumeration"_t, category(), keyValue("type id", m_typeId)}; + + return typeData().traits.isEnum; + } return false; } @@ -3393,8 +4281,15 @@ PropertyMetaInfo::~PropertyMetaInfo() = default; NodeMetaInfo PropertyMetaInfo::propertyType() const { if constexpr (useProjectStorage()) { - if (isValid()) - return {propertyData().propertyTypeId, m_projectStorage}; + if (!isValid()) + return {}; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get property type"_t, + category(), + keyValue("property declaration id", m_id)}; + + return {propertyData().propertyTypeId, m_projectStorage}; } else { if (isValid()) return NodeMetaInfo{nodeMetaInfoPrivateData()->model(), @@ -3409,8 +4304,15 @@ NodeMetaInfo PropertyMetaInfo::propertyType() const NodeMetaInfo PropertyMetaInfo::type() const { if constexpr (useProjectStorage()) { - if (isValid()) - return NodeMetaInfo(propertyData().typeId, m_projectStorage); + if (!isValid()) + return {}; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get property owner type "_t, + category(), + keyValue("property declaration id", m_id)}; + + return NodeMetaInfo(propertyData().typeId, m_projectStorage); } return {}; @@ -3418,11 +4320,18 @@ NodeMetaInfo PropertyMetaInfo::type() const PropertyName PropertyMetaInfo::name() const { - if (isValid()) { - if constexpr (useProjectStorage()) - return PropertyName(Utils::SmallStringView(propertyData().name)); - else - return propertyName(); + if (!isValid()) + return {}; + + if constexpr (useProjectStorage()) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get property name"_t, + category(), + keyValue("property declaration id", m_id)}; + + return PropertyName(Utils::SmallStringView(propertyData().name)); + } else { + return propertyName(); } return {}; @@ -3430,47 +4339,104 @@ PropertyName PropertyMetaInfo::name() const bool PropertyMetaInfo::isWritable() const { - if constexpr (useProjectStorage()) - return isValid() && !(propertyData().traits & Storage::PropertyDeclarationTraits::IsReadOnly); - else + if constexpr (useProjectStorage()) { + if (!isValid()) + return {}; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is property writable"_t, + category(), + keyValue("property declaration id", m_id)}; + + return !(propertyData().traits & Storage::PropertyDeclarationTraits::IsReadOnly); + } else { return isValid() && nodeMetaInfoPrivateData()->isPropertyWritable(propertyName()); + } } bool PropertyMetaInfo::isReadOnly() const { - return !isWritable(); + if constexpr (useProjectStorage()) { + if (!isValid()) + return {}; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is property read only"_t, + category(), + keyValue("property declaration id", m_id)}; + + return propertyData().traits & Storage::PropertyDeclarationTraits::IsReadOnly; + } else { + return !isWritable(); + } } bool PropertyMetaInfo::isListProperty() const { - if constexpr (useProjectStorage()) - return isValid() && propertyData().traits & Storage::PropertyDeclarationTraits::IsList; - else + if constexpr (useProjectStorage()) { + if (!isValid()) + return {}; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is list property"_t, + category(), + keyValue("property declaration id", m_id)}; + + return propertyData().traits & Storage::PropertyDeclarationTraits::IsList; + } else { return isValid() && nodeMetaInfoPrivateData()->isPropertyList(propertyName()); + } } bool PropertyMetaInfo::isEnumType() const { - if constexpr (useProjectStorage()) + if constexpr (useProjectStorage()) { + if (!isValid()) + return {}; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is enum type"_t, + category(), + keyValue("property has enumeration type", m_id)}; + return propertyType().isEnumeration(); - else + } else { return isValid() && nodeMetaInfoPrivateData()->isPropertyEnum(propertyName()); + } } bool PropertyMetaInfo::isPrivate() const { - if constexpr (useProjectStorage()) + if constexpr (useProjectStorage()) { + if (!isValid()) + return {}; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is private property"_t, + category(), + keyValue("property declaration id", m_id)}; + return isValid() && propertyData().name.startsWith("__"); - else + } else { return isValid() && propertyName().startsWith("__"); + } } bool PropertyMetaInfo::isPointer() const { - if constexpr (useProjectStorage()) + if constexpr (useProjectStorage()) { + if (!isValid()) + return {}; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is pointer property"_t, + category(), + keyValue("property declaration id", m_id)}; + return isValid() && (propertyData().traits & Storage::PropertyDeclarationTraits::IsPointer); - else + } else { return isValid() && nodeMetaInfoPrivateData()->isPropertyPointer(propertyName()); + } } namespace { @@ -3487,6 +4453,11 @@ QVariant PropertyMetaInfo::castedValue(const QVariant &value) const return {}; if constexpr (!useProjectStorage()) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"cast value"_t, + category(), + keyValue("property declaration id", m_id)}; + const QVariant variant = value; QVariant copyVariant = variant; const TypeName &typeName = propertyTypeName(); diff --git a/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.cpp b/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.cpp index b5798b713d2..cbe7b0ec384 100644 --- a/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.cpp +++ b/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.cpp @@ -88,4 +88,13 @@ Category &projectStorageUpdaterCategory() } // namespace ProjectStorageTracing +namespace MetaInfoTracing { +Category &category() +{ + thread_local Category category_{"meta info"_t, Tracing::eventQueueWithStringArguments(), category}; + + return category_; +} +} // namespace MetaInfoTracing + } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.h b/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.h index 3a33834c708..899ceb6cd2c 100644 --- a/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.h +++ b/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.h @@ -62,4 +62,20 @@ using Category = NanotraceHR::StringViewWithStringArgumentsCategory; + +[[gnu::pure]] Category &category(); + +} // namespace MetaInfoTracing } // namespace QmlDesigner From 3edd18a9da2dbf6ed42f5343734b0a203adfa256 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Wed, 24 Apr 2024 16:24:14 +0200 Subject: [PATCH 012/154] QmlDesigner: Deprecate more NodeMetaInfo There is now allExportedTypeNames which provides the same information. Maybe we have to add more information to the modules like a flag for the module type. Change-Id: I1a8c0b33fc70a157d16a153102331447f370a032 Reviewed-by: Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Tim Jenssen --- .../materialbrowser/materialbrowserview.cpp | 5 ++ .../designercore/include/nodemetainfo.h | 8 ++- .../designercore/model/rewriterview.cpp | 59 +++++++++++++++++-- 3 files changed, 64 insertions(+), 8 deletions(-) diff --git a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.cpp b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.cpp index 8bd57617285..7d90dffffc6 100644 --- a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.cpp +++ b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.cpp @@ -446,8 +446,13 @@ void QmlDesigner::MaterialBrowserView::loadPropertyGroups() if (!m_hasQuick3DImport || m_propertyGroupsLoaded || !model()) return; +#ifdef QDS_USE_PROJECTSTORAGE + // TODO + QString matPropsPath; +#else QString matPropsPath = model()->metaInfo("QtQuick3D.Material").importDirectoryPath() + "/designer/propertyGroups.json"; +#endif m_propertyGroupsLoaded = m_widget->materialBrowserModel()->loadPropertyGroups(matPropsPath); } diff --git a/src/plugins/qmldesigner/designercore/include/nodemetainfo.h b/src/plugins/qmldesigner/designercore/include/nodemetainfo.h index ba2e2cda65e..9c8e32002f6 100644 --- a/src/plugins/qmldesigner/designercore/include/nodemetainfo.h +++ b/src/plugins/qmldesigner/designercore/include/nodemetainfo.h @@ -28,10 +28,14 @@ QT_END_NAMESPACE [[deprecated( \ "In most cases you don't need them anymore because the import is setting them!")]] # define DEPRECATED_COMPONENT_FILE_NAME [[deprecated("Use sourceId() instead.")]] +# define DEPRECATED_IMPORT_DIRECTORY_PATH [[deprecated("Use allExportedTypeNames().")]] +# define DEPRECATED_REQUIRED_IMPORT_STRING [[deprecated("Use allExportedTypeNames().")]] #else # define DEPRECATED_TYPENAME # define DEPRECATED_VERSION_NUMBER # define DEPRECATED_COMPONENT_FILE_NAME +# define DEPRECATED_IMPORT_DIRECTORY_PATH +# define DEPRECATED_REQUIRED_IMPORT_STRING #endif namespace QmlDesigner { @@ -237,8 +241,8 @@ public: bool usesCustomParser() const; bool isEnumeration() const; - QString importDirectoryPath() const; - QString requiredImportString() const; + DEPRECATED_IMPORT_DIRECTORY_PATH QString importDirectoryPath() const; + DEPRECATED_REQUIRED_IMPORT_STRING QString requiredImportString() const; friend bool operator==(const NodeMetaInfo &first, const NodeMetaInfo &second) { diff --git a/src/plugins/qmldesigner/designercore/model/rewriterview.cpp b/src/plugins/qmldesigner/designercore/model/rewriterview.cpp index 17d40daca3e..c9f8b3bd218 100644 --- a/src/plugins/qmldesigner/designercore/model/rewriterview.cpp +++ b/src/plugins/qmldesigner/designercore/model/rewriterview.cpp @@ -16,12 +16,14 @@ #include #include #include +#include #include +#include +#include +#include #include #include #include -#include -#include #include #include @@ -1004,6 +1006,45 @@ QSet > RewriterView::qrcMapping() const return m_textToModelMerger->qrcMapping(); } +namespace { +#ifdef QDS_USE_PROJECTSTORAGE + +ModuleIds generateModuleIds(const ModelNodes &nodes) +{ + ModuleIds moduleIds; + moduleIds.reserve(Utils::usize(nodes)); + for (const auto &node : nodes) { + auto exportedNames = node.metaInfo().allExportedTypeNames(); + if (exportedNames.size()) + moduleIds.push_back(exportedNames.front().moduleId); + } + + std::sort(moduleIds.begin(), moduleIds.end()); + moduleIds.erase(std::unique(moduleIds.begin(), moduleIds.end()), moduleIds.end()); + + return moduleIds; +} + +QStringList generateImports(ModuleIds moduleIds, const ProjectStorageType &projectStorage) +{ + return Utils::transform(moduleIds, [&](auto id) { + return "import " + projectStorage.moduleName(id).toQString(); + }); +} + +QStringList generateImports(const ModelNodes &nodes) +{ + if (nodes.empty()) + return {}; + + auto moduleIds = generateModuleIds(nodes); + + return generateImports(moduleIds, *nodes.front().model()->projectStorage()); +} + +#endif +} // namespace + void RewriterView::moveToComponent(const ModelNode &modelNode) { if (!modelNode.isValid()) @@ -1012,20 +1053,26 @@ void RewriterView::moveToComponent(const ModelNode &modelNode) int offset = nodeOffset(modelNode); const QList nodes = modelNode.allSubModelNodesAndThisNode(); - QSet directPaths; +#ifdef QDS_USE_PROJECTSTORAGE + auto directPaths = generateImports(nodes); +#else + QSet directPathsSet; // Always add QtQuick import QString quickImport = model()->qtQuickItemMetaInfo().requiredImportString(); if (!quickImport.isEmpty()) - directPaths.insert(quickImport); + directPathsSet.insert(quickImport); for (const ModelNode &partialNode : nodes) { QString importStr = partialNode.metaInfo().requiredImportString(); if (importStr.size()) - directPaths << importStr; + directPathsSet << importStr; } - QString importData = Utils::sorted(directPaths.values()).join(QChar::LineFeed); + auto directPaths = directPathsSet.values(); +#endif + + QString importData = Utils::sorted(directPaths).join(QChar::LineFeed); if (importData.size()) importData.append(QString(2, QChar::LineFeed)); From fa86d63188a67ca9d41b897904ebbc176d42eafc Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Thu, 25 Apr 2024 15:36:27 +0300 Subject: [PATCH 013/154] QmlDesigner: Refresh the puppet when DataStore is usable Change-Id: I42bb69ffab3da7dd49284a1af513ef860f76fbcc Reviewed-by: Mahmoud Badri --- .../qmldesigner/components/collectioneditor/collectionview.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp index 0c9a2eed94d..151c7aca3ed 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp @@ -365,6 +365,7 @@ void CollectionView::resetDataStoreNode() if (dataStoreSingletonFound) { m_widget->listModel()->setDataStoreNode(dataStore); m_dataStoreTypeFound = true; + resetPuppet(); while (!m_delayedTasks.isEmpty()) m_delayedTasks.takeFirst()->process(); From d6065db9839368f23486040b26350c5753aa29bb Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Thu, 25 Apr 2024 15:15:58 +0200 Subject: [PATCH 014/154] QmlDesigner: Fix JavaScript wizard The old wizard syntax stopped working. Using QML file wizard as template. Task-number: QDS-12520 Change-Id: Ib188f898c28be8b7ad9284f431ef0526fbc7bf7e Reviewed-by: Tim Jenssen Reviewed-by: Qt CI Patch Build Bot --- .../files/javascript/wizard.json | 50 +++++++++++++++---- 1 file changed, 39 insertions(+), 11 deletions(-) diff --git a/share/qtcreator/qmldesigner/studio_templates/files/javascript/wizard.json b/share/qtcreator/qmldesigner/studio_templates/files/javascript/wizard.json index 7ba90f75f6f..f70ed11c1da 100644 --- a/share/qtcreator/qmldesigner/studio_templates/files/javascript/wizard.json +++ b/share/qtcreator/qmldesigner/studio_templates/files/javascript/wizard.json @@ -9,13 +9,46 @@ "icon": "file_javascript.png", "platformIndependent": true, + "options": + [ + { "key": "JSFile", "value": "%{Class}.%{JS: Util.preferredSuffix('application/javascript')}" }, + { "key": "ApplicationImport", "value": "%{QmlProjectName} 1.0" }, + { "key": "RootItem", "value": "%{JS: %{RootItemCB}.RootItem}" }, + { "key": "UseImportDefault", "value": "%{JS: false}" }, + { "key": "UseQtQuickControls2Default", "value": "%{JS: true}" } + ], "pages" : - [ + [ { - "trDisplayName": "Location", - "trShortTitle": "Location", - "typeId": "File" + "trDisplayName": "Define Class", + "trShortTitle": "Details", + "typeId": "Fields", + "data" : + [ + { + "name": "Class", + "trDisplayName": "Component name:", + "mandatory": true, + "type": "LineEdit", + "data": { + "validator": "(?:[A-Z_][a-zA-Z_0-9]*|)", + "fixup": "%{JS: '%{INPUT}'.charAt(0).toUpperCase() + '%{INPUT}'.slice(1) }" + } + }, + { + "name": "TargetPath", + "type": "PathChooser", + "trDisplayName": "Path:", + "mandatory": true, + "data": + { + "kind": "existingDirectory", + "basePath": "%{InitialPath}", + "path": "%{InitialPath}" + } + } + ] }, { "trDisplayName": "Options", @@ -34,21 +67,16 @@ } } ] - }, - { - "trDisplayName": "Project Management", - "trShortTitle": "Summary", - "typeId": "Summary" } ], "generators" : - [ + [ { "typeId": "File", "data": { "source": "file.js.tpl", - "target": "%{JS: Util.fileName('%{TargetPath}', '%{JS: Util.preferredSuffix('application/javascript')}')}", + "target": "%{TargetPath}/%{JSFile}", "openInEditor": true } } From 05e5aa7f0cec6aea9cc64a8a45c607a9dedb04db Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Wed, 24 Apr 2024 20:29:13 +0200 Subject: [PATCH 015/154] QmlDesigner: Add kind to module id In some cases we need to find out what kind a module is. Task-number: QTCREATORBUG-30735 Change-Id: Ibd5a70ee6fe0f619009fd645f444d3fbb2fd6e01 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Tim Jenssen Reviewed-by: --- .../propertycomponentgenerator.cpp | 2 +- .../components/edit3d/edit3dview.cpp | 2 +- .../components/edit3d/edit3dwidget.cpp | 3 +- .../qmldesigner/designercore/include/model.h | 2 +- .../instances/nodeinstanceview.cpp | 16 +- .../designercore/metainfo/nodemetainfo.cpp | 14 +- .../qmldesigner/designercore/model/model.cpp | 21 +- .../designercore/model/rewriterview.cpp | 22 +- .../projectstorage/commontypecache.h | 247 +++++++++-------- .../projectstorage/projectstorage.cpp | 82 +++--- .../projectstorage/projectstorage.h | 93 +++++-- .../projectstorage/projectstorageinfotypes.h | 28 ++ .../projectstorage/projectstorageinterface.h | 10 +- .../projectstorage/projectstorageupdater.cpp | 33 ++- .../projectstorage/qmldocumentparser.cpp | 35 ++- .../projectstorage/qmltypesparser.cpp | 19 +- .../projectstorage/storagecache.h | 2 +- .../projectstorage/typeannotationreader.cpp | 3 +- tests/unit/tests/mocks/projectstoragemock.cpp | 41 +-- tests/unit/tests/mocks/projectstoragemock.h | 12 +- .../propertycomponentgenerator-test.cpp | 12 +- .../propertyeditorcomponentgenerator-test.cpp | 4 +- .../unittests/metainfo/nodemetainfo-test.cpp | 262 ++++++++++-------- .../metainfo/propertymetainfo-test.cpp | 126 ++++----- .../unit/tests/unittests/model/model-test.cpp | 41 +-- .../tests/unittests/model/modelutils-test.cpp | 3 +- .../unittests/model/nodelistproperty-test.cpp | 5 - .../projectstorage/projectstorage-test.cpp | 68 +++-- .../projectstorageupdater-test.cpp | 50 ++-- .../projectstorage/qmldocumentparser-test.cpp | 35 +-- .../projectstorage/qmltypesparser-test.cpp | 20 +- .../typeannotationreader-test.cpp | 5 +- 32 files changed, 753 insertions(+), 565 deletions(-) diff --git a/src/plugins/qmldesigner/components/componentcore/propertycomponentgenerator.cpp b/src/plugins/qmldesigner/components/componentcore/propertycomponentgenerator.cpp index 8cc84058d20..15bd7f1d6da 100644 --- a/src/plugins/qmldesigner/components/componentcore/propertycomponentgenerator.cpp +++ b/src/plugins/qmldesigner/components/componentcore/propertycomponentgenerator.cpp @@ -179,7 +179,7 @@ std::optional createEntry(QmlJS::SimpleReader if (moduleName.isEmpty()) return {}; - auto module = model->module(moduleName); + auto module = model->module(moduleName, Storage::ModuleKind::QmlLibrary); auto typeName = getProperty(node, "typeNames"); diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp index c7d70d4b8af..bd01cc2c281 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp +++ b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp @@ -369,7 +369,7 @@ void Edit3DView::handleEntriesChanged() .generatedComponentUtils() .import3dTypePrefix(); - auto assetsModule = model()->module(import3dTypePrefix); + auto assetsModule = model()->module(import3dTypePrefix, Storage::ModuleKind::QmlLibrary); for (const auto &metaInfo : model()->metaInfosForModule(assetsModule)) append(metaInfo, EK_importedModels); diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp index 6f1cf2e1837..2a5457e3dea 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp +++ b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp @@ -770,7 +770,8 @@ void Edit3DWidget::dropEvent(QDropEvent *dropEvent) ->documentManager() .generatedComponentUtils() .import3dTypePrefix(); - auto metaInfo = model->metaInfo(model->module(import3dTypePrefix), fileName.toUtf8()); + auto moduleId = model->module(import3dTypePrefix, Storage::ModuleKind::QmlLibrary); + auto metaInfo = model->metaInfo(moduleId, fileName.toUtf8()); if (auto entries = metaInfo.itemLibrariesEntries(); entries.size()) { auto entry = ItemLibraryEntry{entries.front(), *model->projectStorage()}; QmlVisualNode::createQml3DNode(view(), entry, m_canvas->activeScene(), {}, false); diff --git a/src/plugins/qmldesigner/designercore/include/model.h b/src/plugins/qmldesigner/designercore/include/model.h index 85f129cdbce..74c5697990c 100644 --- a/src/plugins/qmldesigner/designercore/include/model.h +++ b/src/plugins/qmldesigner/designercore/include/model.h @@ -147,7 +147,7 @@ public: void setMetaInfo(const MetaInfo &metaInfo); #endif - Module module(Utils::SmallStringView moduleName); + Module module(Utils::SmallStringView moduleName, Storage::ModuleKind moduleKind); NodeMetaInfo metaInfo(const TypeName &typeName, int majorVersion = -1, int minorVersion = -1) const; NodeMetaInfo metaInfo(Module module, Utils::SmallStringView typeName, diff --git a/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp b/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp index 1b965db66a7..71354edb92a 100644 --- a/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp +++ b/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp @@ -1031,8 +1031,20 @@ TypeName createQualifiedTypeName(const ModelNode &node) auto exportedTypes = node.metaInfo().exportedTypeNamesForSourceId(model->fileUrlSourceId()); if (exportedTypes.size()) { const auto &exportedType = exportedTypes.front(); - Utils::PathString typeName = model->projectStorage()->moduleName(exportedType.moduleId); - typeName += '/'; + using Storage::ModuleKind; + auto module = model->projectStorage()->module(exportedType.moduleId); + Utils::PathString typeName; + switch (module.kind) { + case ModuleKind::QmlLibrary: + typeName += module.name; + typeName += '/'; + break; + case ModuleKind::PathLibrary: + break; + case ModuleKind::CppLibrary: + break; + } + typeName += exportedType.name; return typeName.toQByteArray(); diff --git a/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp b/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp index 886ad8686f6..04c151444d5 100644 --- a/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp +++ b/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp @@ -53,6 +53,8 @@ NodeMetaInfo object will result in an InvalidMetaInfoException being thrown. namespace { +using Storage::ModuleKind; + auto category = MetaInfoTracing::category; struct TypeDescription @@ -2123,13 +2125,13 @@ NodeMetaInfos NodeMetaInfo::prototypes() const } namespace { -template +template bool isBasedOnCommonType(NotNullPointer projectStorage, TypeId typeId) { if (!typeId) return false; - auto base = projectStorage->commonTypeId(); + auto base = projectStorage->commonTypeId(); return projectStorage->isBasedOn(typeId, base); } @@ -3441,7 +3443,7 @@ bool NodeMetaInfo::isQtQuick3DParticlesAbstractShape() const keyValue("type id", m_typeId)}; using namespace Storage::Info; - return isBasedOnCommonType( + return isBasedOnCommonType( m_projectStorage, m_typeId); } else { return isValid() && isSubclassOf("QQuick3DParticleAbstractShape"); @@ -3578,8 +3580,8 @@ bool NodeMetaInfo::isQtQuickStateOperation() const keyValue("type id", m_typeId)}; using namespace Storage::Info; - return isBasedOnCommonType(m_projectStorage, - m_typeId); + return isBasedOnCommonType(m_projectStorage, + m_typeId); } else { return isValid() && isSubclassOf(".QQuickStateOperation"); } @@ -4333,8 +4335,6 @@ PropertyName PropertyMetaInfo::name() const } else { return propertyName(); } - - return {}; } bool PropertyMetaInfo::isWritable() const diff --git a/src/plugins/qmldesigner/designercore/model/model.cpp b/src/plugins/qmldesigner/designercore/model/model.cpp index 4f0bfba1ced..bf15410232b 100644 --- a/src/plugins/qmldesigner/designercore/model/model.cpp +++ b/src/plugins/qmldesigner/designercore/model/model.cpp @@ -170,10 +170,10 @@ Storage::Imports createStorageImports(const Imports &imports, SourceId fileId) { return Utils::transform(imports, [&](const Import &import) { - return Storage::Import{projectStorage.moduleId(Utils::SmallString{import.url()}), - import.majorVersion(), - import.minorVersion(), - fileId}; + using Storage::ModuleKind; + auto moduleKind = import.isLibraryImport() ? ModuleKind::QmlLibrary : ModuleKind::PathLibrary; + auto moduleId = projectStorage.moduleId(Utils::SmallString{import.url()}, moduleKind); + return Storage::Import{moduleId, import.majorVersion(), import.minorVersion(), fileId}; }); } @@ -390,7 +390,11 @@ ImportedTypeNameId ModelPrivate::importedTypeNameId(Utils::SmallStringView typeN return import.alias() == aliasName; }); if (found != m_imports.end()) { - ModuleId moduleId = projectStorage->moduleId(Utils::PathString{found->url()}); + using Storage::ModuleKind; + auto moduleKind = found->isLibraryImport() ? ModuleKind::QmlLibrary + : ModuleKind::PathLibrary; + ModuleId moduleId = projectStorage->moduleId(Utils::PathString{found->url()}, + moduleKind); ImportId importId = projectStorage->importId( Storage::Import{moduleId, found->majorVersion(), found->minorVersion(), m_sourceId}); return projectStorage->importedTypeNameId(importId, shortTypeName); @@ -2623,11 +2627,10 @@ MetaInfo Model::metaInfo() } #endif -Module Model::module(Utils::SmallStringView moduleName) +Module Model::module(Utils::SmallStringView moduleName, Storage::ModuleKind moduleKind) { - if constexpr (useProjectStorage()) { - return Module(d->projectStorage->moduleId(moduleName), d->projectStorage); - } + if constexpr (useProjectStorage()) + return Module(d->projectStorage->moduleId(moduleName, moduleKind), d->projectStorage); return {}; } diff --git a/src/plugins/qmldesigner/designercore/model/rewriterview.cpp b/src/plugins/qmldesigner/designercore/model/rewriterview.cpp index c9f8b3bd218..2dcc859001a 100644 --- a/src/plugins/qmldesigner/designercore/model/rewriterview.cpp +++ b/src/plugins/qmldesigner/designercore/model/rewriterview.cpp @@ -1027,9 +1027,25 @@ ModuleIds generateModuleIds(const ModelNodes &nodes) QStringList generateImports(ModuleIds moduleIds, const ProjectStorageType &projectStorage) { - return Utils::transform(moduleIds, [&](auto id) { - return "import " + projectStorage.moduleName(id).toQString(); - }); + QStringList imports; + imports.reserve(std::ssize(moduleIds)); + + for (auto moduleId : moduleIds) { + using Storage::ModuleKind; + auto module = projectStorage.module(moduleId); + switch (module.kind) { + case ModuleKind::QmlLibrary: + imports.push_back("import " + module.name.toQString()); + break; + case ModuleKind::PathLibrary: + imports.push_back("import \"" + module.name.toQString() + "\""); + break; + case ModuleKind::CppLibrary: + break; + } + } + + return imports; } QStringList generateImports(const ModelNodes &nodes) diff --git a/src/plugins/qmldesigner/designercore/projectstorage/commontypecache.h b/src/plugins/qmldesigner/designercore/projectstorage/commontypecache.h index 35658c005f1..76305b1fbee 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/commontypecache.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/commontypecache.h @@ -84,7 +84,6 @@ inline constexpr char PrincipledMaterial[] = "PrincipledMaterial"; inline constexpr char PropertyAnimation[] = "PropertyAnimation"; inline constexpr char PropertyChanges[] = "PropertyChanges"; inline constexpr char QML[] = "QML"; -inline constexpr char QML_cppnative[] = "QML-cppnative"; inline constexpr char QQuick3DParticleAbstractShape[] = "QQuick3DParticleAbstractShape"; inline constexpr char QQuickStateOperation[] = "QQuickStateOperation"; inline constexpr char QtMultimedia[] = "QtMultimedia"; @@ -94,7 +93,6 @@ inline constexpr char QtQml_Models[] = "QtQml.Models"; inline constexpr char QtQml_XmlListModel[] = "QtQml.XmlListModel"; 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_Controls[] = "QtQuick.Controls"; inline constexpr char QtQuick_Dialogs[] = "QtQuick.Dialogs"; @@ -104,7 +102,6 @@ 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"; @@ -149,7 +146,7 @@ struct BaseCacheType QmlDesigner::TypeId typeId; }; -template +template struct CacheType : public BaseCacheType { }; @@ -157,106 +154,107 @@ struct CacheType : public BaseCacheType template class CommonTypeCache { - using CommonTypes = std::tuple, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType>; + using CommonTypes = std::tuple< + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType>; public: CommonTypeCache(const ProjectStorage &projectStorage) @@ -283,14 +281,14 @@ public: std::fill(std::begin(m_typesWithoutProperties), std ::end(m_typesWithoutProperties), TypeId{}); } - template + template TypeId typeId() const { - auto &type = std::get>(m_types); + auto &type = std::get>(m_types); if (type.typeId) return type.typeId; - return refreshTypedId(type, moduleName, typeName); + return refreshTypedId(type, moduleName, moduleKind, typeName); } template @@ -307,11 +305,11 @@ public: else if constexpr (std::is_same_v) return typeId(); else if constexpr (std::is_same_v) - return typeId(); + return typeId(); else if constexpr (std::is_same_v) return typeId(); else if constexpr (std::is_same_v) - return typeId(); + return typeId(); else if constexpr (std::is_same_v) return typeId(); else if constexpr (std::is_same_v) @@ -341,10 +339,11 @@ public: private: TypeId refreshTypedId(BaseCacheType &type, ::Utils::SmallStringView moduleName, + ModuleKind moduleKind, ::Utils::SmallStringView typeName) const { if (!type.moduleId) - type.moduleId = m_projectStorage.moduleId(moduleName); + type.moduleId = m_projectStorage.moduleId(moduleName, moduleKind); type.typeId = m_projectStorage.typeId(type.moduleId, typeName, Storage::Version{}); @@ -353,10 +352,11 @@ private: TypeId refreshTypedIdWithoutTransaction(BaseCacheType &type, ::Utils::SmallStringView moduleName, - ::Utils::SmallStringView typeName) const + ::Utils::SmallStringView typeName, + ModuleKind moduleKind) const { if (!type.moduleId) - type.moduleId = m_projectStorage.fetchModuleIdUnguarded(moduleName); + type.moduleId = m_projectStorage.fetchModuleIdUnguarded(moduleName, moduleKind); type.typeId = m_projectStorage.fetchTypeIdByModuleIdAndExportedName(type.moduleId, typeName); @@ -371,26 +371,27 @@ private: std::copy(std::begin(typeIds), std::end(typeIds), std::begin(m_typesWithoutProperties)); } - template + template TypeId typeIdWithoutTransaction() const { - auto &type = std::get>(m_types); + auto &type = std::get>(m_types); if (type.typeId) return type.typeId; - return refreshTypedIdWithoutTransaction(type, moduleName, typeName); + return refreshTypedIdWithoutTransaction(type, moduleName, typeName, moduleKind); } void updateTypeIdsWithoutProperties() { - setupTypeIdsWithoutProperties({typeIdWithoutTransaction(), - typeIdWithoutTransaction(), - typeIdWithoutTransaction(), - typeIdWithoutTransaction(), - typeIdWithoutTransaction(), - typeIdWithoutTransaction(), - typeIdWithoutTransaction(), - typeIdWithoutTransaction()}); + setupTypeIdsWithoutProperties( + {typeIdWithoutTransaction(), + typeIdWithoutTransaction(), + typeIdWithoutTransaction(), + typeIdWithoutTransaction(), + typeIdWithoutTransaction(), + typeIdWithoutTransaction(), + typeIdWithoutTransaction(), + typeIdWithoutTransaction()}); } private: diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.cpp b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.cpp index 8aa162224ed..9e8b882dea6 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.cpp +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.cpp @@ -235,14 +235,14 @@ struct ProjectStorage::Statements database}; Sqlite::WriteStatement<1> deleteEnumerationDeclarationStatement{ "DELETE FROM enumerationDeclarations WHERE enumerationDeclarationId=?", database}; - mutable Sqlite::ReadStatement<1, 1> selectModuleIdByNameStatement{ - "SELECT moduleId FROM modules WHERE name=? LIMIT 1", database}; - mutable Sqlite::ReadWriteStatement<1, 1> insertModuleNameStatement{ - "INSERT INTO modules(name) VALUES(?1) RETURNING moduleId", database}; - mutable Sqlite::ReadStatement<1, 1> selectModuleNameStatement{ - "SELECT name FROM modules WHERE moduleId =?1", database}; - mutable Sqlite::ReadStatement<2> selectAllModulesStatement{"SELECT name, moduleId FROM modules", - database}; + mutable Sqlite::ReadStatement<1, 2> selectModuleIdByNameStatement{ + "SELECT moduleId FROM modules WHERE kind=?1 AND name=?2 LIMIT 1", database}; + mutable Sqlite::ReadWriteStatement<1, 2> insertModuleNameStatement{ + "INSERT INTO modules(kind, name) VALUES(?1, ?2) RETURNING moduleId", database}; + mutable Sqlite::ReadStatement<2, 1> selectModuleStatement{ + "SELECT name, kind FROM modules WHERE moduleId =?1", database}; + mutable Sqlite::ReadStatement<3> selectAllModulesStatement{ + "SELECT name, kind, moduleId FROM modules", database}; mutable Sqlite::ReadStatement<1, 2> selectTypeIdBySourceIdAndNameStatement{ "SELECT typeId FROM types WHERE sourceId=?1 and name=?2", database}; mutable Sqlite::ReadStatement<1, 3> selectTypeIdByModuleIdsAndExportedNameStatement{ @@ -942,9 +942,10 @@ public: auto &modelIdColumn = table.addColumn("moduleId", Sqlite::StrictColumnType::Integer, {Sqlite::PrimaryKey{}}); + auto &kindColumn = table.addColumn("kind", Sqlite::StrictColumnType::Integer); auto &nameColumn = table.addColumn("name", Sqlite::StrictColumnType::Text); - table.addUniqueIndex({nameColumn}); + table.addUniqueIndex({kindColumn, nameColumn}); table.initialize(database); @@ -1207,21 +1208,21 @@ void ProjectStorage::removeObserver(ProjectStorageObserver *observer) observers.removeOne(observer); } -ModuleId ProjectStorage::moduleId(Utils::SmallStringView moduleName) const +ModuleId ProjectStorage::moduleId(Utils::SmallStringView moduleName, Storage::ModuleKind kind) const { using NanotraceHR::keyValue; NanotraceHR::Tracer tracer{"get module id"_t, projectStorageCategory(), keyValue("module name", moduleName)}; - auto moduleId = moduleCache.id(moduleName); + auto moduleId = moduleCache.id({moduleName, kind}); tracer.end(keyValue("module id", moduleId)); return moduleId; } -Utils::SmallString ProjectStorage::moduleName(ModuleId moduleId) const +Storage::Module ProjectStorage::module(ModuleId moduleId) const { using NanotraceHR::keyValue; NanotraceHR::Tracer tracer{"get module name"_t, @@ -1231,11 +1232,12 @@ Utils::SmallString ProjectStorage::moduleName(ModuleId moduleId) const if (!moduleId) throw ModuleDoesNotExists{}; - auto moduleName = moduleCache.value(moduleId); + auto module = moduleCache.value(moduleId); - tracer.end(keyValue("module name", moduleName)); + tracer.end(keyValue("module name", module.name)); + tracer.end(keyValue("module kind", module.kind)); - return moduleName; + return {module.name, module.kind}; } TypeId ProjectStorage::typeId(ModuleId moduleId, @@ -2168,20 +2170,17 @@ void ProjectStorage::resetForTestsOnly() moduleCache.clearForTestOnly(); } -bool ProjectStorage::moduleNameLess(Utils::SmallStringView first, Utils::SmallStringView second) noexcept -{ - return first < second; -} - -ModuleId ProjectStorage::fetchModuleId(Utils::SmallStringView moduleName) +ModuleId ProjectStorage::fetchModuleId(Utils::SmallStringView moduleName, + Storage::ModuleKind moduleKind) { using NanotraceHR::keyValue; NanotraceHR::Tracer tracer{"fetch module id"_t, projectStorageCategory(), - keyValue("module name", moduleName)}; + keyValue("module name", moduleName), + keyValue("module kind", moduleKind)}; auto moduleId = Sqlite::withDeferredTransaction(database, [&] { - return fetchModuleIdUnguarded(moduleName); + return fetchModuleIdUnguarded(moduleName, moduleKind); }); tracer.end(keyValue("module id", moduleId)); @@ -2189,26 +2188,26 @@ ModuleId ProjectStorage::fetchModuleId(Utils::SmallStringView moduleName) return moduleId; } -Utils::PathString ProjectStorage::fetchModuleName(ModuleId id) +Storage::Module ProjectStorage::fetchModule(ModuleId id) { using NanotraceHR::keyValue; NanotraceHR::Tracer tracer{"fetch module name"_t, projectStorageCategory(), keyValue("module id", id)}; - auto moduleName = Sqlite::withDeferredTransaction(database, - [&] { return fetchModuleNameUnguarded(id); }); + auto module = Sqlite::withDeferredTransaction(database, [&] { return fetchModuleUnguarded(id); }); - tracer.end(keyValue("module name", moduleName)); + tracer.end(keyValue("module name", module.name)); + tracer.end(keyValue("module name", module.kind)); - return moduleName; + return module; } -ProjectStorage::Modules ProjectStorage::fetchAllModules() const +ProjectStorage::ModuleCacheEntries ProjectStorage::fetchAllModules() const { NanotraceHR::Tracer tracer{"fetch all modules"_t, projectStorageCategory()}; - return s->selectAllModulesStatement.valuesWithTransaction(); + return s->selectAllModulesStatement.valuesWithTransaction(); } void ProjectStorage::callRefreshMetaInfoCallback(const TypeIds &deletedTypeIds) @@ -2644,38 +2643,41 @@ void ProjectStorage::synchromizeModuleExportedImports( Sqlite::insertUpdateDelete(range, moduleExportedImports, compareKey, insert, update, remove); } -ModuleId ProjectStorage::fetchModuleIdUnguarded(Utils::SmallStringView name) const +ModuleId ProjectStorage::fetchModuleIdUnguarded(Utils::SmallStringView name, + Storage::ModuleKind kind) const { using NanotraceHR::keyValue; NanotraceHR::Tracer tracer{"fetch module id ungarded"_t, projectStorageCategory(), - keyValue("module name", name)}; + keyValue("module name", name), + keyValue("module kind", kind)}; - auto moduleId = s->selectModuleIdByNameStatement.value(name); + auto moduleId = s->selectModuleIdByNameStatement.value(kind, name); if (!moduleId) - moduleId = s->insertModuleNameStatement.value(name); + moduleId = s->insertModuleNameStatement.value(kind, name); tracer.end(keyValue("module id", moduleId)); return moduleId; } -Utils::PathString ProjectStorage::fetchModuleNameUnguarded(ModuleId id) const +Storage::Module ProjectStorage::fetchModuleUnguarded(ModuleId id) const { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"fetch module name ungarded"_t, + NanotraceHR::Tracer tracer{"fetch module ungarded"_t, projectStorageCategory(), keyValue("module id", id)}; - auto moduleName = s->selectModuleNameStatement.value(id); + auto module = s->selectModuleStatement.value(id); - if (moduleName.empty()) + if (!module) throw ModuleDoesNotExists{}; - tracer.end(keyValue("module name", moduleName)); + tracer.end(keyValue("module name", module.name)); + tracer.end(keyValue("module name", module.kind)); - return moduleName; + return module; } void ProjectStorage::handleAliasPropertyDeclarationsWithPropertyType( diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h index e7826f531b4..579060b0aea 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h @@ -50,9 +50,9 @@ public: void removeObserver(ProjectStorageObserver *observer) override; - ModuleId moduleId(Utils::SmallStringView moduleName) const override; + ModuleId moduleId(Utils::SmallStringView moduleName, Storage::ModuleKind kind) const override; - Utils::SmallString moduleName(ModuleId moduleId) const override; + Storage::Module module(ModuleId moduleId) const override; TypeId typeId(ModuleId moduleId, Utils::SmallStringView exportedTypeName, @@ -116,7 +116,7 @@ public: return commonTypeCache_; } - template + template TypeId commonTypeId() const { using NanotraceHR::keyValue; @@ -125,7 +125,7 @@ public: keyValue("module name", std::string_view{moduleName}), keyValue("type name", std::string_view{typeName})}; - auto typeId = commonTypeCache_.typeId(); + auto typeId = commonTypeCache_.typeId(); tracer.end(keyValue("type id", typeId)); @@ -244,50 +244,90 @@ public: void resetForTestsOnly(); private: + struct ModuleView + { + ModuleView() = default; + + ModuleView(Utils::SmallStringView name, Storage::ModuleKind kind) + : name{name} + , kind{kind} + {} + + ModuleView(const Storage::Module &module) + : name{module.name} + , kind{module.kind} + {} + + Utils::SmallStringView name; + Storage::ModuleKind kind; + + friend bool operator<(ModuleView first, ModuleView second) + { + return std::tie(first.kind, first.name) < std::tie(second.kind, second.name); + } + + friend bool operator==(const Storage::Module &first, ModuleView second) + { + return first.name == second.name && first.kind == second.kind; + } + + friend bool operator==(ModuleView first, const Storage::Module &second) + { + return second == first; + } + }; + class ModuleStorageAdapter { public: - auto fetchId(const Utils::SmallStringView name) { return storage.fetchModuleId(name); } + auto fetchId(ModuleView module) { return storage.fetchModuleId(module.name, module.kind); } - auto fetchValue(ModuleId id) { return storage.fetchModuleName(id); } + auto fetchValue(ModuleId id) { return storage.fetchModule(id); } auto fetchAll() { return storage.fetchAllModules(); } ProjectStorage &storage; }; - class Module : public StorageCacheEntry + friend ModuleStorageAdapter; + + static bool moduleNameLess(ModuleView first, ModuleView second) noexcept { - using Base = StorageCacheEntry; + return first < second; + } + + class ModuleCacheEntry : public StorageCacheEntry + { + using Base = StorageCacheEntry; public: using Base::Base; - friend bool operator==(const Module &first, const Module &second) + ModuleCacheEntry(Utils::SmallStringView name, Storage::ModuleKind kind, ModuleId moduleId) + : Base{{name, kind}, moduleId} + {} + + friend bool operator==(const ModuleCacheEntry &first, const ModuleCacheEntry &second) { return &first == &second && first.value == second.value; } + + friend bool operator==(const ModuleCacheEntry &first, ModuleView second) + { + return first.value.name == second.name && first.value.kind == second.kind; + } }; - using Modules = std::vector; + using ModuleCacheEntries = std::vector; - friend ModuleStorageAdapter; + using ModuleCache + = StorageCache; - static bool moduleNameLess(Utils::SmallStringView first, Utils::SmallStringView second) noexcept; + ModuleId fetchModuleId(Utils::SmallStringView moduleName, Storage::ModuleKind moduleKind); - using ModuleCache = StorageCache; + Storage::Module fetchModule(ModuleId id); - ModuleId fetchModuleId(Utils::SmallStringView moduleName); - - Utils::PathString fetchModuleName(ModuleId id); - - Modules fetchAllModules() const; + ModuleCacheEntries fetchAllModules() const; void callRefreshMetaInfoCallback(const TypeIds &deletedTypeIds); @@ -532,9 +572,10 @@ private: Storage::Synchronization::ModuleExportedImports &moduleExportedImports, const ModuleIds &updatedModuleIds); - ModuleId fetchModuleIdUnguarded(Utils::SmallStringView name) const override; + ModuleId fetchModuleIdUnguarded(Utils::SmallStringView name, + Storage::ModuleKind moduleKind) const override; - Utils::PathString fetchModuleNameUnguarded(ModuleId id) const; + Storage::Module fetchModuleUnguarded(ModuleId id) const; void handleAliasPropertyDeclarationsWithPropertyType( TypeId typeId, AliasPropertyDeclarations &relinkableAliasPropertyDeclarations); diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinfotypes.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinfotypes.h index 9f0c134ed39..b4b1eafbb65 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinfotypes.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinfotypes.h @@ -42,6 +42,34 @@ void convertToString(String &string, const FlagIs &flagIs) namespace QmlDesigner::Storage { +enum class ModuleKind { QmlLibrary, CppLibrary, PathLibrary }; + +struct Module +{ + Module() = default; + + Module(Utils::SmallStringView name, Storage::ModuleKind kind) + : name{name} + , kind{kind} + {} + + template + Module(const ModuleType &module) + : name{module.name} + , kind{module.kind} + {} + + Utils::PathString name; + Storage::ModuleKind kind = Storage::ModuleKind::QmlLibrary; + + friend bool operator==(const Module &first, const Module &second) + { + return first.name == second.name && first.kind == second.kind; + } + + explicit operator bool() const { return name.size(); } +}; + enum class PropertyDeclarationTraits : int { None = 0, IsReadOnly = 1 << 0, diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinterface.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinterface.h index 971e635517a..2a1415e7918 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinterface.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinterface.h @@ -31,8 +31,8 @@ public: virtual void addObserver(ProjectStorageObserver *observer) = 0; virtual void removeObserver(ProjectStorageObserver *observer) = 0; - virtual ModuleId moduleId(::Utils::SmallStringView name) const = 0; - virtual Utils::SmallString moduleName(ModuleId moduleId) const = 0; + virtual ModuleId moduleId(::Utils::SmallStringView name, Storage::ModuleKind kind) const = 0; + virtual QmlDesigner::Storage::Module module(ModuleId moduleId) const = 0; virtual std::optional propertyDeclaration(PropertyDeclarationId propertyDeclarationId) const = 0; virtual TypeId typeId(ModuleId moduleId, @@ -86,10 +86,10 @@ public: virtual SourceId propertyEditorPathId(TypeId typeId) const = 0; virtual const Storage::Info::CommonTypeCache &commonTypeCache() const = 0; - template + template TypeId commonTypeId() const { - return commonTypeCache().template typeId(); + return commonTypeCache().template typeId(); } template @@ -108,7 +108,7 @@ protected: ProjectStorageInterface() = default; ~ProjectStorageInterface() = default; - virtual ModuleId fetchModuleIdUnguarded(Utils::SmallStringView name) const = 0; + virtual ModuleId fetchModuleIdUnguarded(Utils::SmallStringView name, Storage::ModuleKind moduleKind) const = 0; virtual TypeId fetchTypeIdByModuleIdAndExportedName(ModuleId moduleId, Utils::SmallStringView name) const = 0; }; diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.cpp b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.cpp index f1f8a2ca28f..509ffde99a9 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.cpp +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.cpp @@ -164,8 +164,8 @@ void addDependencies(Storage::Imports &dependencies, Tracer &tracer) { for (const QmlDirParser::Import &qmldirDependency : qmldirDependencies) { - ModuleId moduleId = projectStorage.moduleId(Utils::PathString{qmldirDependency.module} - + "-cppnative"); + ModuleId moduleId = projectStorage.moduleId(Utils::PathString{qmldirDependency.module}, + Storage::ModuleKind::CppLibrary); auto &import = dependencies.emplace_back(moduleId, Storage::Version{}, sourceId); tracer.tick(message, keyValue("import", import)); } @@ -177,6 +177,7 @@ void addModuleExportedImport(Storage::Synchronization::ModuleExportedImports &im Storage::Version version, Storage::Synchronization::IsAutoVersion isAutoVersion, std::string_view moduleName, + Storage::ModuleKind moduleKind, std::string_view exportedModuleName) { NanotraceHR::Tracer tracer{"add module exported imports"_t, @@ -186,6 +187,7 @@ void addModuleExportedImport(Storage::Synchronization::ModuleExportedImports &im keyValue("version", version), keyValue("is auto version", isAutoVersion), keyValue("module name", moduleName), + keyValue("module kind", moduleKind), keyValue("exported module name", exportedModuleName)}; imports.emplace_back(moduleId, exportedModuleId, version, isAutoVersion); @@ -200,7 +202,6 @@ void addModuleExportedImports(Storage::Synchronization::ModuleExportedImports &i ModuleId moduleId, ModuleId cppModuleId, std::string_view moduleName, - std::string_view cppModuleName, const QList &qmldirImports, ProjectStorageInterface &projectStorage) { @@ -214,23 +215,27 @@ void addModuleExportedImports(Storage::Synchronization::ModuleExportedImports &i continue; Utils::PathString exportedModuleName{qmldirImport.module}; - ModuleId exportedModuleId = projectStorage.moduleId(exportedModuleName); + using Storage::ModuleKind; + ModuleId exportedModuleId = projectStorage.moduleId(exportedModuleName, + ModuleKind::QmlLibrary); addModuleExportedImport(imports, moduleId, exportedModuleId, convertVersion(qmldirImport.version), convertToIsAutoVersion(qmldirImport.flags), moduleName, + ModuleKind::QmlLibrary, exportedModuleName); - exportedModuleName += "-cppnative"; - ModuleId exportedCppModuleId = projectStorage.moduleId(exportedModuleName); + ModuleId exportedCppModuleId = projectStorage.moduleId(exportedModuleName, + ModuleKind::CppLibrary); addModuleExportedImport(imports, cppModuleId, exportedCppModuleId, Storage::Version{}, Storage::Synchronization::IsAutoVersion::No, - cppModuleName, + moduleName, + ModuleKind::CppLibrary, exportedModuleName); } } @@ -297,7 +302,7 @@ void ProjectStorageUpdater::updateQmlTypes(const QStringList &qmlTypesPaths, NanotraceHR::Tracer tracer{"update qmltypes file"_t, category()}; - ModuleId moduleId = m_projectStorage.moduleId("QML-cppnative"); + ModuleId moduleId = m_projectStorage.moduleId("QML", Storage::ModuleKind::CppLibrary); for (const QString &qmlTypesPath : qmlTypesPaths) { SourceId sourceId = m_pathCache.sourceId(SourcePath{qmlTypesPath}); @@ -360,11 +365,11 @@ void ProjectStorageUpdater::updateDirectoryChanged(std::string_view directoryPat package.updatedSourceIds.push_back(qmldirSourceId); } + using Storage::ModuleKind; Utils::PathString moduleName{parser.typeNamespace()}; - ModuleId moduleId = m_projectStorage.moduleId(moduleName); - Utils::PathString cppModuleName = moduleName + "-cppnative"; - ModuleId cppModuleId = m_projectStorage.moduleId(cppModuleName); - ModuleId pathModuleId = m_projectStorage.moduleId(directoryPath); + ModuleId moduleId = m_projectStorage.moduleId(moduleName, ModuleKind::QmlLibrary); + ModuleId cppModuleId = m_projectStorage.moduleId(moduleName, ModuleKind::CppLibrary); + ModuleId pathModuleId = m_projectStorage.moduleId(directoryPath, ModuleKind::PathLibrary); auto imports = filterMultipleEntries(parser.imports()); @@ -372,7 +377,6 @@ void ProjectStorageUpdater::updateDirectoryChanged(std::string_view directoryPat moduleId, cppModuleId, moduleName, - cppModuleName, imports, m_projectStorage); tracer.tick("append updated module id"_t, keyValue("module id", moduleId)); @@ -696,7 +700,8 @@ void ProjectStorageUpdater::updatePropertyEditorFilePath( moduleName.replace('/', '.'); if (oldModuleName != moduleName) { oldModuleName = moduleName; - moduleId = m_projectStorage.moduleId(Utils::SmallString{moduleName}); + moduleId = m_projectStorage.moduleId(Utils::SmallString{moduleName}, + Storage::ModuleKind::QmlLibrary); } Storage::TypeNameString typeName{match.capturedView(2)}; SourceId pathId = m_pathCache.sourceId(SourcePath{path}); diff --git a/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.cpp b/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.cpp index 27efa8d530d..4338da62ce9 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.cpp +++ b/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.cpp @@ -66,23 +66,32 @@ Storage::Import createImport(const QmlDom::Import &qmlImport, Utils::SmallStringView directoryPath, QmlDocumentParser::ProjectStorage &storage) { + using Storage::ModuleKind; using QmlUriKind = QQmlJS::Dom::QmlUri::Kind; auto &&uri = qmlImport.uri; - if (uri.kind() == QmlUriKind::RelativePath) { - auto path = createNormalizedPath(directoryPath, uri.localPath()); - auto moduleId = storage.moduleId(createNormalizedPath(directoryPath, uri.localPath())); - return Storage::Import(moduleId, Storage::Version{}, sourceId); - } - - if (uri.kind() == QmlUriKind::ModuleUri) { - auto moduleId = storage.moduleId(Utils::PathString{uri.moduleUri()}); + switch (uri.kind()) { + case QmlUriKind::AbsolutePath: + case QmlUriKind::DirectoryUrl: { + auto moduleId = storage.moduleId(Utils::PathString{uri.toString()}, ModuleKind::PathLibrary); return Storage::Import(moduleId, convertVersion(qmlImport.version), sourceId); } + case QmlUriKind::RelativePath: { + auto path = createNormalizedPath(directoryPath, uri.localPath()); + auto moduleId = storage.moduleId(createNormalizedPath(directoryPath, uri.localPath()), + ModuleKind::PathLibrary); + return Storage::Import(moduleId, Storage::Version{}, sourceId); + } + case QmlUriKind::ModuleUri: { + auto moduleId = storage.moduleId(Utils::PathString{uri.moduleUri()}, ModuleKind::QmlLibrary); + return Storage::Import(moduleId, convertVersion(qmlImport.version), sourceId); + } + case QmlUriKind::Invalid: + return Storage::Import{}; + } - auto moduleId = storage.moduleId(Utils::PathString{uri.toString()}); - return Storage::Import(moduleId, convertVersion(qmlImport.version), sourceId); + return Storage::Import{}; } QualifiedImports createQualifiedImports(const QList &qmlImports, @@ -122,11 +131,13 @@ void addImports(Storage::Imports &imports, } } - auto localDirectoryModuleId = storage.moduleId(directoryPath); + using Storage::ModuleKind; + + auto localDirectoryModuleId = storage.moduleId(directoryPath, ModuleKind::PathLibrary); imports.emplace_back(localDirectoryModuleId, Storage::Version{}, sourceId); ++importCount; - auto qmlModuleId = storage.moduleId("QML"); + auto qmlModuleId = storage.moduleId("QML", ModuleKind::QmlLibrary); imports.emplace_back(qmlModuleId, Storage::Version{}, sourceId); ++importCount; diff --git a/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.cpp b/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.cpp index 104338e514b..66b40d76ca8 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.cpp +++ b/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.cpp @@ -28,7 +28,7 @@ namespace QmlDesigner { constexpr auto category = ProjectStorageTracing::projectStorageUpdaterCategory; using NanotraceHR::keyValue; using Tracer = ProjectStorageTracing::Category::TracerType; - +using Storage::ModuleKind; namespace QmlDom = QQmlJS::Dom; namespace { @@ -71,8 +71,7 @@ const Storage::Import &appendImports(Storage::Imports &imports, }); Utils::PathString moduleName{QStringView(dependency.begin(), spaceFound)}; - moduleName.append("-cppnative"); - ModuleId cppModuleId = storage.moduleId(moduleName); + ModuleId cppModuleId = storage.moduleId(moduleName, ModuleKind::CppLibrary); return imports.emplace_back(cppModuleId, Storage::Version{}, sourceId); } @@ -98,7 +97,8 @@ void addImports(Storage::Imports &imports, const auto &import = imports.emplace_back(cppModuleId, Storage::Version{}, sourceId); tracer.tick("append import"_t, keyValue("import", import)); - if (ModuleId qmlCppModuleId = storage.moduleId("QML-cppnative"); cppModuleId != qmlCppModuleId) { + if (ModuleId qmlCppModuleId = storage.moduleId("QML", ModuleKind::CppLibrary); + cppModuleId != qmlCppModuleId) { const auto &import = imports.emplace_back(qmlCppModuleId, Storage::Version{}, sourceId); tracer.tick("append import"_t, keyValue("import", import)); } @@ -145,7 +145,8 @@ Storage::Synchronization::ExportedTypes createExports(const QList getSkipList(std::string_view moduleName) +Utils::span getSkipList(const Storage::Module &module) { static constexpr Utils::span emptySkipList; auto currentSkipList = emptySkipList; std::apply( [&](const auto &entry) { - if (entry.first == moduleName) + if (entry.first.first == module.name && entry.first.second == module.kind) currentSkipList = entry.second; }, skipLists); @@ -502,7 +503,7 @@ void addTypes(Storage::Synchronization::Types &types, NanotraceHR::Tracer tracer{"add types"_t, category()}; types.reserve(Utils::usize(objects) + types.size()); - const auto skipList = getSkipList(storage.moduleName(projectData.moduleId)); + const auto skipList = getSkipList(storage.module(projectData.moduleId)); for (const auto &object : objects) { if (skipType(object, skipList)) diff --git a/src/plugins/qmldesigner/designercore/projectstorage/storagecache.h b/src/plugins/qmldesigner/designercore/projectstorage/storagecache.h index 85c6147d2c7..32ecb1c3f7e 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/storagecache.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/storagecache.h @@ -313,7 +313,7 @@ private: return entries.end(); } - auto value = *found; + const auto &value = *found; if (value == view) { return found; diff --git a/src/plugins/qmldesigner/designercore/projectstorage/typeannotationreader.cpp b/src/plugins/qmldesigner/designercore/projectstorage/typeannotationreader.cpp index 67a63542bcb..876a789120b 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/typeannotationreader.cpp +++ b/src/plugins/qmldesigner/designercore/projectstorage/typeannotationreader.cpp @@ -258,7 +258,8 @@ void TypeAnnotationReader::readTypeProperty(QStringView name, const QVariant &va auto [moduleName, typeName] = decomposeTypePath(fullTypeName); m_typeAnnotations.back().typeName = typeName; - m_typeAnnotations.back().moduleId = m_projectStorage.moduleId(moduleName); + m_typeAnnotations.back().moduleId = m_projectStorage.moduleId(moduleName, + ModuleKind::QmlLibrary); } else if (name == "icon"_L1) { m_typeAnnotations.back().iconPath = absoluteFilePathForDocument(value.toString()); diff --git a/tests/unit/tests/mocks/projectstoragemock.cpp b/tests/unit/tests/mocks/projectstoragemock.cpp index 6d5304879e6..d4a28d1ae6f 100644 --- a/tests/unit/tests/mocks/projectstoragemock.cpp +++ b/tests/unit/tests/mocks/projectstoragemock.cpp @@ -12,9 +12,10 @@ using QmlDesigner::ImportId; using QmlDesigner::ModuleId; using QmlDesigner::PropertyDeclarationId; using QmlDesigner::SourceId; +using QmlDesigner::Storage::ModuleKind; +using QmlDesigner::Storage::PropertyDeclarationTraits; using QmlDesigner::TypeId; using QmlDesigner::TypeIds; -using QmlDesigner::Storage::PropertyDeclarationTraits; namespace Storage = QmlDesigner::Storage; @@ -41,18 +42,20 @@ void setupIsBasedOn(ProjectStorageMock &mock) } // namespace -ModuleId ProjectStorageMock::createModule(Utils::SmallStringView moduleName) +ModuleId ProjectStorageMock::createModule(Utils::SmallStringView moduleName, + QmlDesigner::Storage::ModuleKind moduleKind) { - if (auto id = moduleId(moduleName)) { + if (auto id = moduleId(moduleName, moduleKind)) { return id; } static ModuleId moduleId; incrementBasicId(moduleId); - ON_CALL(*this, moduleId(Eq(moduleName))).WillByDefault(Return(moduleId)); - ON_CALL(*this, moduleName(Eq(moduleId))).WillByDefault(Return(moduleName)); - ON_CALL(*this, fetchModuleIdUnguarded(Eq(moduleName))).WillByDefault(Return(moduleId)); + ON_CALL(*this, moduleId(Eq(moduleName), Eq(moduleKind))).WillByDefault(Return(moduleId)); + ON_CALL(*this, module(Eq(moduleId))) + .WillByDefault(Return(QmlDesigner::Storage::Module{moduleName, moduleKind})); + ON_CALL(*this, fetchModuleIdUnguarded(Eq(moduleName), Eq(moduleKind))).WillByDefault(Return(moduleId)); return moduleId; } @@ -385,11 +388,11 @@ void ProjectStorageMock::setupQtQuick() { setupIsBasedOn(*this); - auto qmlModuleId = createModule("QML"); - auto qmlNativeModuleId = createModule("QML-cppnative"); - auto qtQmlModelsModuleId = createModule("QtQml.Models"); - auto qtQuickModuleId = createModule("QtQuick"); - auto qtQuickNativeModuleId = createModule("QtQuick-cppnative"); + auto qmlModuleId = createModule("QML", ModuleKind::QmlLibrary); + auto qmlNativeModuleId = createModule("QML", ModuleKind::CppLibrary); + auto qtQmlModelsModuleId = createModule("QtQml.Models", ModuleKind::QmlLibrary); + auto qtQuickModuleId = createModule("QtQuick", ModuleKind::QmlLibrary); + auto qtQuickNativeModuleId = createModule("QtQuick", ModuleKind::CppLibrary); auto boolId = createValue(qmlModuleId, "bool"); auto intId = createValue(qmlModuleId, "int"); @@ -463,11 +466,11 @@ void ProjectStorageMock::setupQtQuick() {qtObjectId}); createObject(qtQuickModuleId, "PropertyChanges", {qtObjectId, stateOperationsId}); - auto qtQuickTimelineModuleId = createModule("QtQuick.Timeline"); + auto qtQuickTimelineModuleId = createModule("QtQuick.Timeline", ModuleKind::QmlLibrary); createObject(qtQuickTimelineModuleId, "KeyframeGroup", {qtObjectId}); createObject(qtQuickTimelineModuleId, "Keyframe", {qtObjectId}); - auto flowViewModuleId = createModule("FlowView"); + auto flowViewModuleId = createModule("FlowView", ModuleKind::QmlLibrary); createObject(flowViewModuleId, "FlowActionArea", "data", @@ -492,12 +495,12 @@ void ProjectStorageMock::setupQtQuick() void ProjectStorageMock::setupQtQuickImportedTypeNameIds(QmlDesigner::SourceId sourceId) { - auto qmlModuleId = moduleId("QML"); - auto qtQmlModelsModuleId = moduleId("QtQml.Models"); - auto qtQuickModuleId = moduleId("QtQuick"); - auto qtQuickNativeModuleId = moduleId("QtQuick-cppnative"); - auto qtQuickTimelineModuleId = moduleId("QtQuick.Timeline"); - auto flowViewModuleId = moduleId("FlowView"); + auto qmlModuleId = moduleId("QML", ModuleKind::QmlLibrary); + auto qtQmlModelsModuleId = moduleId("QtQml.Models", ModuleKind::QmlLibrary); + auto qtQuickModuleId = moduleId("QtQuick", ModuleKind::QmlLibrary); + auto qtQuickNativeModuleId = moduleId("QtQuick", ModuleKind::CppLibrary); + auto qtQuickTimelineModuleId = moduleId("QtQuick.Timeline", ModuleKind::QmlLibrary); + auto flowViewModuleId = moduleId("FlowView", ModuleKind::QmlLibrary); createImportedTypeNameId(sourceId, "int", qmlModuleId); createImportedTypeNameId(sourceId, "QtObject", qmlModuleId); diff --git a/tests/unit/tests/mocks/projectstoragemock.h b/tests/unit/tests/mocks/projectstoragemock.h index 8aa5979ddbe..dad9f4db100 100644 --- a/tests/unit/tests/mocks/projectstoragemock.h +++ b/tests/unit/tests/mocks/projectstoragemock.h @@ -23,7 +23,8 @@ public: void setupQtQuickImportedTypeNameIds(QmlDesigner::SourceId sourceId); void setupCommonTypeCache(); - QmlDesigner::ModuleId createModule(Utils::SmallStringView moduleName); + QmlDesigner::ModuleId createModule(Utils::SmallStringView moduleName, + QmlDesigner::Storage::ModuleKind moduleKind); QmlDesigner::ImportedTypeNameId createImportedTypeNameId(QmlDesigner::SourceId sourceId, Utils::SmallStringView typeName, @@ -126,8 +127,11 @@ public: MOCK_METHOD(void, addObserver, (QmlDesigner::ProjectStorageObserver *), (override)); MOCK_METHOD(void, removeObserver, (QmlDesigner::ProjectStorageObserver *), (override)); - MOCK_METHOD(QmlDesigner::ModuleId, moduleId, (::Utils::SmallStringView), (const, override)); - MOCK_METHOD(Utils::SmallString, moduleName, (QmlDesigner::ModuleId), (const, override)); + MOCK_METHOD(QmlDesigner::ModuleId, + moduleId, + (::Utils::SmallStringView, QmlDesigner::Storage::ModuleKind moduleKind), + (const, override)); + MOCK_METHOD(QmlDesigner::Storage::Module, module, (QmlDesigner::ModuleId), (const, override)); MOCK_METHOD(std::optional, propertyDeclaration, @@ -337,7 +341,7 @@ public: (const, override)); MOCK_METHOD(QmlDesigner::ModuleId, fetchModuleIdUnguarded, - (Utils::SmallStringView name), + (Utils::SmallStringView name, QmlDesigner::Storage::ModuleKind), (const, override)); MOCK_METHOD(QmlDesigner::TypeId, fetchTypeIdByModuleIdAndExportedName, diff --git a/tests/unit/tests/unittests/componentcore/propertycomponentgenerator-test.cpp b/tests/unit/tests/unittests/componentcore/propertycomponentgenerator-test.cpp index 10e4cc32525..3255a64a0c7 100644 --- a/tests/unit/tests/unittests/componentcore/propertycomponentgenerator-test.cpp +++ b/tests/unit/tests/unittests/componentcore/propertycomponentgenerator-test.cpp @@ -11,6 +11,7 @@ #include using namespace Qt::StringLiterals; +using QmlDesigner::Storage::ModuleKind; namespace QmlDesigner { @@ -182,7 +183,7 @@ protected: resourceManagementMock)}; QmlDesigner::PropertyComponentGenerator generator{QString{sourcesPath}, &model}; QmlDesigner::NodeMetaInfo itemMetaInfo = model.qtQuickItemMetaInfo(); - QmlDesigner::ModuleId qmlModuleId = projectStorageMock.createModule("QML"); + QmlDesigner::ModuleId qmlModuleId = projectStorageMock.createModule("QML", ModuleKind::QmlLibrary); }; TEST_F(PropertyComponentGenerator, @@ -345,7 +346,8 @@ TEST_F(PropertyComponentGenerator, after_refresh_meta_infos_type_was_deleted) auto xProperty = itemMetaInfo.property("x"); auto doubleMetaInfo = model.doubleMetaInfo(); projectStorageMock.removeExportedTypeName(doubleMetaInfo.id(), - projectStorageMock.createModule("QML"), + projectStorageMock.createModule("QML", + ModuleKind::QmlLibrary), "real"); generator.refreshMetaInfos({doubleMetaInfo.id()}); @@ -359,11 +361,13 @@ TEST_F(PropertyComponentGenerator, after_refresh_meta_infos_type_was_added) auto xProperty = itemMetaInfo.property("x"); auto doubleMetaInfo = model.doubleMetaInfo(); projectStorageMock.removeExportedTypeName(doubleMetaInfo.id(), - projectStorageMock.createModule("QML"), + projectStorageMock.createModule("QML", + ModuleKind::QmlLibrary), "real"); generator.refreshMetaInfos({doubleMetaInfo.id()}); projectStorageMock.addExportedTypeName(doubleMetaInfo.id(), - projectStorageMock.createModule("QML"), + projectStorageMock.createModule("QML", + ModuleKind::QmlLibrary), "real"); generator.refreshMetaInfos({}); diff --git a/tests/unit/tests/unittests/componentcore/propertyeditorcomponentgenerator-test.cpp b/tests/unit/tests/unittests/componentcore/propertyeditorcomponentgenerator-test.cpp index d2f2143a737..398c54bfad4 100644 --- a/tests/unit/tests/unittests/componentcore/propertyeditorcomponentgenerator-test.cpp +++ b/tests/unit/tests/unittests/componentcore/propertyeditorcomponentgenerator-test.cpp @@ -13,6 +13,7 @@ namespace { using BasicProperty = QmlDesigner::PropertyComponentGenerator::BasicProperty; using ComplexProperty = QmlDesigner::PropertyComponentGenerator::ComplexProperty; using QmlDesigner::PropertyMetaInfo; +using QmlDesigner::Storage::ModuleKind; class PropertyEditorComponentGenerator : public ::testing::Test { @@ -86,7 +87,8 @@ protected: NiceMock projectStorageMock{sourceId}; NiceMock propertyGeneratorMock; QmlDesigner::PropertyEditorComponentGenerator generator{propertyGeneratorMock}; - QmlDesigner::ModuleId qtQuickModuleId = projectStorageMock.createModule("QtQuick"); + QmlDesigner::ModuleId qtQuickModuleId = projectStorageMock.createModule("QtQuick", + ModuleKind::QmlLibrary); QmlDesigner::NodeMetaInfo fooTypeInfo = createType("Foo"); QmlDesigner::TypeId dummyTypeId = projectStorageMock.commonTypeCache().builtinTypeId(); }; diff --git a/tests/unit/tests/unittests/metainfo/nodemetainfo-test.cpp b/tests/unit/tests/unittests/metainfo/nodemetainfo-test.cpp index af8c4bd2209..c3fd89d86d0 100644 --- a/tests/unit/tests/unittests/metainfo/nodemetainfo-test.cpp +++ b/tests/unit/tests/unittests/metainfo/nodemetainfo-test.cpp @@ -17,6 +17,7 @@ namespace { using QmlDesigner::FlagIs; using QmlDesigner::ModelNode; using QmlDesigner::ModelNodes; +using QmlDesigner::Storage::ModuleKind; using QmlDesigner::Storage::TypeTraits; using QmlDesigner::Storage::TypeTraitsKind; @@ -68,9 +69,10 @@ protected: } QmlDesigner::NodeMetaInfo createDerivedDummyMetaInfo(Utils::SmallStringView moduleName, + ModuleKind moduleKind, Utils::SmallStringView typeName) { - auto moduleId = projectStorageMock.createModule(moduleName); + auto moduleId = projectStorageMock.createModule(moduleName, moduleKind); auto typeId = projectStorageMock.createType(moduleId, typeName, {}); return createDerivedDummyMetaInfo(typeId); @@ -86,10 +88,11 @@ protected: } QmlDesigner::NodeMetaInfo createMetaInfo(Utils::SmallStringView moduleName, + ModuleKind moduleKind, Utils::SmallStringView typeName, QmlDesigner::Storage::TypeTraits typeTraits = {}) { - auto moduleId = projectStorageMock.createModule(moduleName); + auto moduleId = projectStorageMock.createModule(moduleName, moduleKind); auto typeId = projectStorageMock.createType(moduleId, typeName, typeTraits); return QmlDesigner::NodeMetaInfo{typeId, &projectStorageMock}; @@ -109,8 +112,9 @@ protected: ModelNode object = model.createModelNode("QtObject"); QmlDesigner::NodeMetaInfo itemMetaInfo = item.metaInfo(); QmlDesigner::NodeMetaInfo objectMetaInfo = object.metaInfo(); - QmlDesigner::ModuleId qmlModuleId = projectStorageMock.createModule("QML"); - QmlDesigner::ModuleId qtQuickModuleId = projectStorageMock.createModule("QtQuick"); + QmlDesigner::ModuleId qmlModuleId = projectStorageMock.createModule("QML", ModuleKind::QmlLibrary); + QmlDesigner::ModuleId qtQuickModuleId = projectStorageMock.createModule("QtQuick", + ModuleKind::QmlLibrary); QmlDesigner::TypeId intTypeId = projectStorageMock.typeId(qmlModuleId, "int", QmlDesigner::Storage::Version{}); @@ -215,7 +219,7 @@ TEST_F(NodeMetaInfo, invalid_is_not_file_component) TEST_F(NodeMetaInfo, component_is_file_component) { - auto moduleId = projectStorageMock.createModule("/path/to/project"); + auto moduleId = projectStorageMock.createModule("/path/to/project", ModuleKind::PathLibrary); TypeTraits traits{TypeTraitsKind::Reference}; traits.isFileComponent = true; auto typeId = projectStorageMock.createType(moduleId, "Foo", traits); @@ -228,7 +232,7 @@ TEST_F(NodeMetaInfo, component_is_file_component) TEST_F(NodeMetaInfo, is_project_component) { - auto moduleId = projectStorageMock.createModule("/path/to/project"); + auto moduleId = projectStorageMock.createModule("/path/to/project", ModuleKind::PathLibrary); TypeTraits traits{TypeTraitsKind::Reference}; traits.isProjectComponent = true; auto typeId = projectStorageMock.createType(moduleId, "Foo", traits); @@ -242,7 +246,7 @@ TEST_F(NodeMetaInfo, is_project_component) TEST_F(NodeMetaInfo, is_not_project_component) { using QmlDesigner::Storage::TypeTraits; - auto moduleId = projectStorageMock.createModule("/path/to/project"); + auto moduleId = projectStorageMock.createModule("/path/to/project", ModuleKind::PathLibrary); auto typeId = projectStorageMock.createType(moduleId, "Foo", {}); QmlDesigner::NodeMetaInfo metaInfo{typeId, &projectStorageMock}; @@ -262,7 +266,7 @@ TEST_F(NodeMetaInfo, invalid_is_not_project_component) TEST_F(NodeMetaInfo, is_in_project_module) { - auto moduleId = projectStorageMock.createModule("/path/to/project"); + auto moduleId = projectStorageMock.createModule("/path/to/project", ModuleKind::PathLibrary); TypeTraits traits{TypeTraitsKind::Reference}; traits.isInProjectModule = true; auto typeId = projectStorageMock.createType(moduleId, "Foo", traits); @@ -276,7 +280,7 @@ TEST_F(NodeMetaInfo, is_in_project_module) TEST_F(NodeMetaInfo, is_not_in_project_module) { using QmlDesigner::Storage::TypeTraits; - auto moduleId = projectStorageMock.createModule("/path/to/project"); + auto moduleId = projectStorageMock.createModule("/path/to/project", ModuleKind::PathLibrary); auto typeId = projectStorageMock.createType(moduleId, "Foo", {}); QmlDesigner::NodeMetaInfo metaInfo{typeId, &projectStorageMock}; @@ -873,7 +877,7 @@ TEST_F(NodeMetaInfo, second_input_is_invalid_for_common_base_returns_invalid) TEST_F(NodeMetaInfo, source_id) { - auto moduleId = projectStorageMock.createModule("/path/to/project"); + auto moduleId = projectStorageMock.createModule("/path/to/project", ModuleKind::PathLibrary); auto typeSourceId = QmlDesigner::SourceId::create(999); auto typeId = projectStorageMock.createType(moduleId, "Foo", {}, {}, typeSourceId); QmlDesigner::NodeMetaInfo metaInfo{typeId, &projectStorageMock}; @@ -930,7 +934,7 @@ TEST_F(NodeMetaInfo, default_is_not_color) TEST_F(NodeMetaInfo, float_is_a_floating_type) { - auto metaInfo = createMetaInfo("QML-cppnative", "float"); + auto metaInfo = createMetaInfo("QML", ModuleKind::CppLibrary, "float"); bool isType = metaInfo.isFloat(); @@ -957,7 +961,7 @@ TEST_F(NodeMetaInfo, default_is_not_float) TEST_F(NodeMetaInfo, is_FlowView_FlowActionArea) { - auto metaInfo = createDerivedDummyMetaInfo("FlowView", "FlowActionArea"); + auto metaInfo = createDerivedDummyMetaInfo("FlowView", ModuleKind::QmlLibrary, "FlowActionArea"); bool isType = metaInfo.isFlowViewFlowActionArea(); @@ -975,7 +979,7 @@ TEST_F(NodeMetaInfo, default_is_not_FlowView_FlowActionArea) TEST_F(NodeMetaInfo, is_FlowView_FlowDecision) { - auto metaInfo = createDerivedDummyMetaInfo("FlowView", "FlowDecision"); + auto metaInfo = createDerivedDummyMetaInfo("FlowView", ModuleKind::QmlLibrary, "FlowDecision"); bool isType = metaInfo.isFlowViewFlowDecision(); @@ -993,7 +997,7 @@ TEST_F(NodeMetaInfo, default_is_not_FlowView_FlowDecision) TEST_F(NodeMetaInfo, is_FlowView_FlowItem) { - auto metaInfo = createDerivedDummyMetaInfo("FlowView", "FlowItem"); + auto metaInfo = createDerivedDummyMetaInfo("FlowView", ModuleKind::QmlLibrary, "FlowItem"); bool isType = metaInfo.isFlowViewFlowItem(); @@ -1011,7 +1015,7 @@ TEST_F(NodeMetaInfo, default_is_not_FlowView_FlowItem) TEST_F(NodeMetaInfo, is_FlowView_FlowTransition) { - auto metaInfo = createDerivedDummyMetaInfo("FlowView", "FlowTransition"); + auto metaInfo = createDerivedDummyMetaInfo("FlowView", ModuleKind::QmlLibrary, "FlowTransition"); bool isType = metaInfo.isFlowViewFlowTransition(); @@ -1029,7 +1033,7 @@ TEST_F(NodeMetaInfo, default_is_not_FlowView_FlowTransition) TEST_F(NodeMetaInfo, is_FlowView_FlowView) { - auto metaInfo = createDerivedDummyMetaInfo("FlowView", "FlowView"); + auto metaInfo = createDerivedDummyMetaInfo("FlowView", ModuleKind::QmlLibrary, "FlowView"); bool isType = metaInfo.isFlowViewFlowView(); @@ -1047,7 +1051,7 @@ TEST_F(NodeMetaInfo, default_is_not_FlowView_FlowView) TEST_F(NodeMetaInfo, is_FlowView_FlowWildcard) { - auto metaInfo = createDerivedDummyMetaInfo("FlowView", "FlowWildcard"); + auto metaInfo = createDerivedDummyMetaInfo("FlowView", ModuleKind::QmlLibrary, "FlowWildcard"); bool isType = metaInfo.isFlowViewFlowWildcard(); @@ -1065,7 +1069,7 @@ TEST_F(NodeMetaInfo, default_is_not_FlowView_FlowWildcard) TEST_F(NodeMetaInfo, FlowItem_is_FlowView_item) { - auto metaInfo = createDerivedDummyMetaInfo("FlowView", "FlowItem"); + auto metaInfo = createDerivedDummyMetaInfo("FlowView", ModuleKind::QmlLibrary, "FlowItem"); bool isType = metaInfo.isFlowViewItem(); @@ -1074,7 +1078,7 @@ TEST_F(NodeMetaInfo, FlowItem_is_FlowView_item) TEST_F(NodeMetaInfo, FlowWildcard_is_FlowView_item) { - auto metaInfo = createDerivedDummyMetaInfo("FlowView", "FlowWildcard"); + auto metaInfo = createDerivedDummyMetaInfo("FlowView", ModuleKind::QmlLibrary, "FlowWildcard"); bool isType = metaInfo.isFlowViewItem(); @@ -1083,7 +1087,7 @@ TEST_F(NodeMetaInfo, FlowWildcard_is_FlowView_item) TEST_F(NodeMetaInfo, FlowDecision_is_FlowView_item) { - auto metaInfo = createDerivedDummyMetaInfo("FlowView", "FlowDecision"); + auto metaInfo = createDerivedDummyMetaInfo("FlowView", ModuleKind::QmlLibrary, "FlowDecision"); bool isType = metaInfo.isFlowViewItem(); @@ -1128,7 +1132,7 @@ TEST_F(NodeMetaInfo, QtQuick_Item_is_graphical_item) TEST_F(NodeMetaInfo, QtQuickWindow_Window_is_graphical_item) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick.Window", "Window"); + auto metaInfo = createDerivedDummyMetaInfo("QtQuick.Window", ModuleKind::QmlLibrary, "Window"); bool isType = metaInfo.isGraphicalItem(); @@ -1137,7 +1141,7 @@ TEST_F(NodeMetaInfo, QtQuickWindow_Window_is_graphical_item) TEST_F(NodeMetaInfo, QtQuickDialogs_Dialogs_is_graphical_item) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick.Dialogs", "Dialog"); + auto metaInfo = createDerivedDummyMetaInfo("QtQuick.Dialogs", ModuleKind::QmlLibrary, "Dialog"); bool isType = metaInfo.isGraphicalItem(); @@ -1146,7 +1150,7 @@ TEST_F(NodeMetaInfo, QtQuickDialogs_Dialogs_is_graphical_item) TEST_F(NodeMetaInfo, QtQuickControls_Popup_is_graphical_item) { - auto metaInfo = createMetaInfo("QtQuick.Controls", "Popup"); + auto metaInfo = createMetaInfo("QtQuick.Controls", ModuleKind::QmlLibrary, "Popup"); bool isType = metaInfo.isGraphicalItem(); @@ -1191,7 +1195,7 @@ TEST_F(NodeMetaInfo, QtQuick_Positioner_is_layoutable) TEST_F(NodeMetaInfo, QtQuick_Layouts_Layout_is_layoutable) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick.Layouts", "Layout"); + auto metaInfo = createDerivedDummyMetaInfo("QtQuick.Layouts", ModuleKind::QmlLibrary, "Layout"); bool isType = metaInfo.isLayoutable(); @@ -1200,7 +1204,7 @@ TEST_F(NodeMetaInfo, QtQuick_Layouts_Layout_is_layoutable) TEST_F(NodeMetaInfo, QtQuick_Controls_SplitView_is_layoutable) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick.Controls", "SplitView"); + auto metaInfo = createDerivedDummyMetaInfo("QtQuick.Controls", ModuleKind::QmlLibrary, "SplitView"); bool isType = metaInfo.isLayoutable(); @@ -1263,7 +1267,8 @@ TEST_F(NodeMetaInfo, default_is_not_qml_component) TEST_F(NodeMetaInfo, is_QtMultimedia_SoundEffect) { - auto qtMultimediaModuleId = projectStorageMock.createModule("QtMultimedia"); + auto qtMultimediaModuleId = projectStorageMock.createModule("QtMultimedia", + ModuleKind::QmlLibrary); auto metaInfo = createDerivedDummyMetaInfo(qtMultimediaModuleId, "SoundEffect"); bool isType = metaInfo.isQtMultimediaSoundEffect(); @@ -1300,7 +1305,7 @@ TEST_F(NodeMetaInfo, default_is_not_QtObject) TEST_F(NodeMetaInfo, is_QtQuick3D_BakedLightmap) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D", "BakedLightmap"); + auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D", ModuleKind::QmlLibrary, "BakedLightmap"); bool isType = metaInfo.isQtQuick3DBakedLightmap(); @@ -1318,7 +1323,7 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuick3D_BakedLightmap) TEST_F(NodeMetaInfo, is_QtQuick3D_Camera) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D", "Camera"); + auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D", ModuleKind::QmlLibrary, "Camera"); bool isType = metaInfo.isQtQuick3DCamera(); @@ -1336,7 +1341,7 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuick3D_Camera) TEST_F(NodeMetaInfo, is_QtQuick3D_Command) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D", "Command"); + auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D", ModuleKind::QmlLibrary, "Command"); bool isType = metaInfo.isQtQuick3DCommand(); @@ -1354,7 +1359,7 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuick3D_Command) TEST_F(NodeMetaInfo, is_QtQuick3D_DefaultMaterial) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D", "DefaultMaterial"); + auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D", ModuleKind::QmlLibrary, "DefaultMaterial"); bool isType = metaInfo.isQtQuick3DDefaultMaterial(); @@ -1372,7 +1377,7 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuick3D_DefaultMaterial) TEST_F(NodeMetaInfo, is_QtQuick3D_Effect) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D", "Effect"); + auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D", ModuleKind::QmlLibrary, "Effect"); bool isType = metaInfo.isQtQuick3DEffect(); @@ -1390,7 +1395,7 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuick3D_Effect) TEST_F(NodeMetaInfo, is_QtQuick3D_InstanceList) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D", "InstanceList"); + auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D", ModuleKind::QmlLibrary, "InstanceList"); bool isType = metaInfo.isQtQuick3DInstanceList(); @@ -1408,7 +1413,7 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuick3D_InstanceList) TEST_F(NodeMetaInfo, is_QtQuick3D_InstanceListEntry) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D", "InstanceListEntry"); + auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D", ModuleKind::QmlLibrary, "InstanceListEntry"); bool isType = metaInfo.isQtQuick3DInstanceListEntry(); @@ -1426,7 +1431,7 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuick3D_InstanceListEntry) TEST_F(NodeMetaInfo, is_QtQuick3D_Light) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D", "Light"); + auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D", ModuleKind::QmlLibrary, "Light"); bool isType = metaInfo.isQtQuick3DLight(); @@ -1444,7 +1449,7 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuick3D_Light) TEST_F(NodeMetaInfo, is_QtQuick3D_Material) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D", "Material"); + auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D", ModuleKind::QmlLibrary, "Material"); bool isType = metaInfo.isQtQuick3DMaterial(); @@ -1462,7 +1467,7 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuick3D_Material) TEST_F(NodeMetaInfo, is_QtQuick3D_Model) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D", "Model"); + auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D", ModuleKind::QmlLibrary, "Model"); bool isType = metaInfo.isQtQuick3DModel(); @@ -1480,7 +1485,7 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuick3D_Model) TEST_F(NodeMetaInfo, is_QtQuick3D_Node) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D", "Node"); + auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D", ModuleKind::QmlLibrary, "Node"); bool isType = metaInfo.isQtQuick3DNode(); @@ -1498,7 +1503,8 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuick3D_Node) TEST_F(NodeMetaInfo, is_QtQuick3D_Particles3D_cppnative_QQuick3DParticleAbstractShape) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D.Particles3D-cppnative", + auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D.Particles3D", + ModuleKind::CppLibrary, "QQuick3DParticleAbstractShape"); bool isType = metaInfo.isQtQuick3DParticlesAbstractShape(); @@ -1517,7 +1523,9 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuick3D_Particles3D_cppnative_QQuick3DPart TEST_F(NodeMetaInfo, is_QtQuick3D_Particles3D_Affector3D) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D.Particles3D", "Affector3D"); + auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D.Particles3D", + ModuleKind::QmlLibrary, + "Affector3D"); bool isType = metaInfo.isQtQuick3DParticles3DAffector3D(); @@ -1535,7 +1543,9 @@ TEST_F(NodeMetaInfo, QtQuick3D_Particles3D_Affector3D) TEST_F(NodeMetaInfo, is_QtQuick3D_Particles3D_Attractor3D) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D.Particles3D", "Attractor3D"); + auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D.Particles3D", + ModuleKind::QmlLibrary, + "Attractor3D"); bool isType = metaInfo.isQtQuick3DParticles3DAttractor3D(); @@ -1553,7 +1563,9 @@ TEST_F(NodeMetaInfo, QtQuick3D_Particles3D_Attractor3D) TEST_F(NodeMetaInfo, is_QtQuick3D_Particles3D_Particle3D) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D.Particles3D", "Particle3D"); + auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D.Particles3D", + ModuleKind::QmlLibrary, + "Particle3D"); bool isType = metaInfo.isQtQuick3DParticles3DParticle3D(); @@ -1571,7 +1583,9 @@ TEST_F(NodeMetaInfo, QtQuick3D_Particles3D_Particle3D) TEST_F(NodeMetaInfo, is_QtQuick3D_Particles3D_ParticleEmitter3D) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D.Particles3D", "ParticleEmitter3D"); + auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D.Particles3D", + ModuleKind::QmlLibrary, + "ParticleEmitter3D"); bool isType = metaInfo.isQtQuick3DParticles3DParticleEmitter3D(); @@ -1589,7 +1603,9 @@ TEST_F(NodeMetaInfo, QtQuick3D_Particles3D_ParticleEmitter3D) TEST_F(NodeMetaInfo, is_QtQuick3D_Particles3D_SpriteParticle3D) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D.Particles3D", "SpriteParticle3D"); + auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D.Particles3D", + ModuleKind::QmlLibrary, + "SpriteParticle3D"); bool isType = metaInfo.isQtQuick3DParticles3DSpriteParticle3D(); @@ -1607,7 +1623,7 @@ TEST_F(NodeMetaInfo, QtQuick3D_Particles3D_SpriteParticle3D) TEST_F(NodeMetaInfo, is_QtQuick3D_Pass) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D", "Pass"); + auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D", ModuleKind::QmlLibrary, "Pass"); bool isType = metaInfo.isQtQuick3DPass(); @@ -1625,7 +1641,9 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuick3D_Pass) TEST_F(NodeMetaInfo, is_QtQuick3D_PrincipledMaterial) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D", "PrincipledMaterial"); + auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D", + ModuleKind::QmlLibrary, + "PrincipledMaterial"); bool isType = metaInfo.isQtQuick3DPrincipledMaterial(); @@ -1643,7 +1661,9 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuick3D_PrincipledMaterial) TEST_F(NodeMetaInfo, is_QtQuick3D_SpecularGlossyMaterial) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D", "SpecularGlossyMaterial"); + auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D", + ModuleKind::QmlLibrary, + "SpecularGlossyMaterial"); bool isType = metaInfo.isQtQuick3DSpecularGlossyMaterial(); @@ -1661,7 +1681,7 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuick3D_SpecularGlossyMaterial) TEST_F(NodeMetaInfo, is_QtQuick3D_SceneEnvironment) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D", "SceneEnvironment"); + auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D", ModuleKind::QmlLibrary, "SceneEnvironment"); bool isType = metaInfo.isQtQuick3DSceneEnvironment(); @@ -1679,7 +1699,7 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuick3D_SceneEnvironment) TEST_F(NodeMetaInfo, is_QtQuick3D_Shader) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D", "Shader"); + auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D", ModuleKind::QmlLibrary, "Shader"); bool isType = metaInfo.isQtQuick3DShader(); @@ -1697,7 +1717,7 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuick3D_Shader) TEST_F(NodeMetaInfo, is_QtQuick3D_Texture) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D", "Texture"); + auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D", ModuleKind::QmlLibrary, "Texture"); bool isType = metaInfo.isQtQuick3DTexture(); @@ -1715,7 +1735,7 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuick3D_Texture) TEST_F(NodeMetaInfo, is_QtQuick3D_TextureInput) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D", "TextureInput"); + auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D", ModuleKind::QmlLibrary, "TextureInput"); bool isType = metaInfo.isQtQuick3DTextureInput(); @@ -1733,7 +1753,7 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuick3D_TextureInput) TEST_F(NodeMetaInfo, is_QtQuick3D_CubeMapTexture) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D", "CubeMapTexture"); + auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D", ModuleKind::QmlLibrary, "CubeMapTexture"); bool isType = metaInfo.isQtQuick3DCubeMapTexture(); @@ -1751,7 +1771,7 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuick3D_CubeMapTexture) TEST_F(NodeMetaInfo, is_QtQuick3D_View3D) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D", "View3D"); + auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D", ModuleKind::QmlLibrary, "View3D"); bool isType = metaInfo.isQtQuick3DView3D(); @@ -1769,7 +1789,7 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuick3D_View3D) TEST_F(NodeMetaInfo, is_QtQuick_BorderImage) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick", "BorderImage"); + auto metaInfo = createDerivedDummyMetaInfo("QtQuick", ModuleKind::QmlLibrary, "BorderImage"); bool isType = metaInfo.isQtQuickBorderImage(); @@ -1787,7 +1807,7 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuick_BorderImage) TEST_F(NodeMetaInfo, is_QtQuickControls_SwipeView) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick.Controls", "SwipeView"); + auto metaInfo = createDerivedDummyMetaInfo("QtQuick.Controls", ModuleKind::QmlLibrary, "SwipeView"); bool isType = metaInfo.isQtQuickControlsSwipeView(); @@ -1805,7 +1825,7 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuickControls_SwipeView) TEST_F(NodeMetaInfo, is_QtQuickControls_TabBar) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick.Controls", "TabBar"); + auto metaInfo = createDerivedDummyMetaInfo("QtQuick.Controls", ModuleKind::QmlLibrary, "TabBar"); bool isType = metaInfo.isQtQuickControlsTabBar(); @@ -1823,7 +1843,7 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuickControls_TabBar) TEST_F(NodeMetaInfo, is_QtQuickExtras_Picture) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick.Extras", "Picture"); + auto metaInfo = createDerivedDummyMetaInfo("QtQuick.Extras", ModuleKind::QmlLibrary, "Picture"); bool isType = metaInfo.isQtQuickExtrasPicture(); @@ -1841,7 +1861,7 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuickExtras_Picture) TEST_F(NodeMetaInfo, is_QtQuick_Image) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick", "Image"); + auto metaInfo = createDerivedDummyMetaInfo("QtQuick", ModuleKind::QmlLibrary, "Image"); bool isType = metaInfo.isQtQuickImage(); @@ -1859,7 +1879,7 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuick_Image) TEST_F(NodeMetaInfo, is_QtQuick_Item) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick", "Item"); + auto metaInfo = createDerivedDummyMetaInfo("QtQuick", ModuleKind::QmlLibrary, "Item"); bool isType = metaInfo.isQtQuickItem(); @@ -1877,7 +1897,7 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuick_Item) TEST_F(NodeMetaInfo, is_QtQuickLayouts_BorderImage) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick.Layouts", "Layout"); + auto metaInfo = createDerivedDummyMetaInfo("QtQuick.Layouts", ModuleKind::QmlLibrary, "Layout"); bool isType = metaInfo.isQtQuickLayoutsLayout(); @@ -1895,7 +1915,7 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuickLayouts_Layout) TEST_F(NodeMetaInfo, is_QtQuick_Loader) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick", "Loader"); + auto metaInfo = createDerivedDummyMetaInfo("QtQuick", ModuleKind::QmlLibrary, "Loader"); bool isType = metaInfo.isQtQuickLoader(); @@ -1913,7 +1933,7 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuick_Loader) TEST_F(NodeMetaInfo, is_QtQuick_Path) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick", "Path"); + auto metaInfo = createDerivedDummyMetaInfo("QtQuick", ModuleKind::QmlLibrary, "Path"); bool isType = metaInfo.isQtQuickPath(); @@ -1931,7 +1951,7 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuick_Path) TEST_F(NodeMetaInfo, is_QtQuick_PauseAnimation) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick", "PauseAnimation"); + auto metaInfo = createDerivedDummyMetaInfo("QtQuick", ModuleKind::QmlLibrary, "PauseAnimation"); bool isType = metaInfo.isQtQuickPauseAnimation(); @@ -1949,7 +1969,7 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuick_PauseAnimation) TEST_F(NodeMetaInfo, is_QtQuick_Positioner) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick", "Positioner"); + auto metaInfo = createDerivedDummyMetaInfo("QtQuick", ModuleKind::QmlLibrary, "Positioner"); bool isType = metaInfo.isQtQuickPositioner(); @@ -1967,7 +1987,7 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuick_Positioner) TEST_F(NodeMetaInfo, is_QtQuick_PropertyAnimation) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick", "PropertyAnimation"); + auto metaInfo = createDerivedDummyMetaInfo("QtQuick", ModuleKind::QmlLibrary, "PropertyAnimation"); bool isType = metaInfo.isQtQuickPropertyAnimation(); @@ -1985,7 +2005,7 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuick_PropertyAnimation) TEST_F(NodeMetaInfo, is_QtQuick_PropertyChanges) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick", "PropertyChanges"); + auto metaInfo = createDerivedDummyMetaInfo("QtQuick", ModuleKind::QmlLibrary, "PropertyChanges"); bool isType = metaInfo.isQtQuickPropertyChanges(); @@ -2003,7 +2023,7 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuick_PropertyChanges) TEST_F(NodeMetaInfo, is_QtQuick_Repeater) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick", "Repeater"); + auto metaInfo = createDerivedDummyMetaInfo("QtQuick", ModuleKind::QmlLibrary, "Repeater"); bool isType = metaInfo.isQtQuickRepeater(); @@ -2021,7 +2041,7 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuick_Repeater) TEST_F(NodeMetaInfo, is_QtQuick_State) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick", "State"); + auto metaInfo = createDerivedDummyMetaInfo("QtQuick", ModuleKind::QmlLibrary, "State"); bool isType = metaInfo.isQtQuickState(); @@ -2039,7 +2059,9 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuick_State) TEST_F(NodeMetaInfo, is_QtQuickNative_StateOperation) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick-cppnative", "QQuickStateOperation"); + auto metaInfo = createDerivedDummyMetaInfo("QtQuick", + ModuleKind::CppLibrary, + "QQuickStateOperation"); bool isType = metaInfo.isQtQuickStateOperation(); @@ -2057,7 +2079,9 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuickNative_StateOperation) TEST_F(NodeMetaInfo, is_QtQuickStudioComponents_GroupItem) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick.Studio.Components", "GroupItem"); + auto metaInfo = createDerivedDummyMetaInfo("QtQuick.Studio.Components", + ModuleKind::QmlLibrary, + "GroupItem"); bool isType = metaInfo.isQtQuickStudioComponentsGroupItem(); @@ -2075,7 +2099,7 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuickStudioComponents_GroupItem) TEST_F(NodeMetaInfo, is_QtQuick_Text) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick", "Text"); + auto metaInfo = createDerivedDummyMetaInfo("QtQuick", ModuleKind::QmlLibrary, "Text"); bool isType = metaInfo.isQtQuickText(); @@ -2093,7 +2117,7 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuick_Text) TEST_F(NodeMetaInfo, is_QtQuickTimeline_Keyframe) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick.Timeline", "Keyframe"); + auto metaInfo = createDerivedDummyMetaInfo("QtQuick.Timeline", ModuleKind::QmlLibrary, "Keyframe"); bool isType = metaInfo.isQtQuickTimelineKeyframe(); @@ -2111,7 +2135,9 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuick_Keyframe) TEST_F(NodeMetaInfo, is_QtQuickTimeline_KeyframeGroup) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick.Timeline", "KeyframeGroup"); + auto metaInfo = createDerivedDummyMetaInfo("QtQuick.Timeline", + ModuleKind::QmlLibrary, + "KeyframeGroup"); bool isType = metaInfo.isQtQuickTimelineKeyframeGroup(); @@ -2129,7 +2155,7 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuick_KeyframeGroup) TEST_F(NodeMetaInfo, is_QtQuickTimeline_Timeline) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick.Timeline", "Timeline"); + auto metaInfo = createDerivedDummyMetaInfo("QtQuick.Timeline", ModuleKind::QmlLibrary, "Timeline"); bool isType = metaInfo.isQtQuickTimelineTimeline(); @@ -2147,7 +2173,9 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuick_Timeline) TEST_F(NodeMetaInfo, is_QtQuickTimeline_TimelineAnimation) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick.Timeline", "TimelineAnimation"); + auto metaInfo = createDerivedDummyMetaInfo("QtQuick.Timeline", + ModuleKind::QmlLibrary, + "TimelineAnimation"); bool isType = metaInfo.isQtQuickTimelineTimelineAnimation(); @@ -2165,7 +2193,7 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuick_TimelineAnimation) TEST_F(NodeMetaInfo, is_QtQuick_Transition) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick", "Transition"); + auto metaInfo = createDerivedDummyMetaInfo("QtQuick", ModuleKind::QmlLibrary, "Transition"); bool isType = metaInfo.isQtQuickTransition(); @@ -2183,7 +2211,7 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuick_Transition) TEST_F(NodeMetaInfo, is_QtQuickWindow_Window) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick.Window", "Window"); + auto metaInfo = createDerivedDummyMetaInfo("QtQuick.Window", ModuleKind::QmlLibrary, "Window"); bool isType = metaInfo.isQtQuickWindowWindow(); @@ -2201,7 +2229,9 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuickWindow_Window) TEST_F(NodeMetaInfo, is_QtSafeRenderer_SafeRendererPicture) { - auto metaInfo = createDerivedDummyMetaInfo("Qt.SafeRenderer", "SafeRendererPicture"); + auto metaInfo = createDerivedDummyMetaInfo("Qt.SafeRenderer", + ModuleKind::QmlLibrary, + "SafeRendererPicture"); bool isType = metaInfo.isQtSafeRendererSafeRendererPicture(); @@ -2219,7 +2249,7 @@ TEST_F(NodeMetaInfo, default_is_not_QtSafeRenderer_SafeRendererPicture) TEST_F(NodeMetaInfo, is_QtSafeRenderer_SafePicture) { - auto metaInfo = createDerivedDummyMetaInfo("Qt.SafeRenderer", "SafePicture"); + auto metaInfo = createDerivedDummyMetaInfo("Qt.SafeRenderer", ModuleKind::QmlLibrary, "SafePicture"); bool isType = metaInfo.isQtSafeRendererSafePicture(); @@ -2237,7 +2267,7 @@ TEST_F(NodeMetaInfo, default_is_not_QtSafeRenderer_SafePicture) TEST_F(NodeMetaInfo, is_string) { - auto metaInfo = createMetaInfo("QML", "string"); + auto metaInfo = createMetaInfo("QML", ModuleKind::QmlLibrary, "string"); bool isType = metaInfo.isString(); @@ -2255,7 +2285,7 @@ TEST_F(NodeMetaInfo, default_is_not_string) TEST_F(NodeMetaInfo, QtQuick_Item_is_suitable_for_MouseArea_fill) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick", "Item"); + auto metaInfo = createDerivedDummyMetaInfo("QtQuick", ModuleKind::QmlLibrary, "Item"); bool isType = metaInfo.isSuitableForMouseAreaFill(); @@ -2264,7 +2294,7 @@ TEST_F(NodeMetaInfo, QtQuick_Item_is_suitable_for_MouseArea_fill) TEST_F(NodeMetaInfo, QtQuick_MouseArea_is_suitable_for_MouseArea_fill) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick", "MouseArea"); + auto metaInfo = createDerivedDummyMetaInfo("QtQuick", ModuleKind::QmlLibrary, "MouseArea"); bool isType = metaInfo.isSuitableForMouseAreaFill(); @@ -2273,7 +2303,7 @@ TEST_F(NodeMetaInfo, QtQuick_MouseArea_is_suitable_for_MouseArea_fill) TEST_F(NodeMetaInfo, QtQuickControls_Control_is_suitable_for_MouseArea_fill) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick.Controls", "Control"); + auto metaInfo = createDerivedDummyMetaInfo("QtQuick.Controls", ModuleKind::QmlLibrary, "Control"); bool isType = metaInfo.isSuitableForMouseAreaFill(); @@ -2282,7 +2312,7 @@ TEST_F(NodeMetaInfo, QtQuickControls_Control_is_suitable_for_MouseArea_fill) TEST_F(NodeMetaInfo, QtQuickTemplates_Control_is_suitable_for_MouseArea_fill) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick.Templates", "Control"); + auto metaInfo = createDerivedDummyMetaInfo("QtQuick.Templates", ModuleKind::QmlLibrary, "Control"); bool isType = metaInfo.isSuitableForMouseAreaFill(); @@ -2300,7 +2330,7 @@ TEST_F(NodeMetaInfo, default_is_not_suitable_for_MouseArea_fill) TEST_F(NodeMetaInfo, is_url) { - auto metaInfo = createMetaInfo("QML", "url"); + auto metaInfo = createMetaInfo("QML", ModuleKind::QmlLibrary, "url"); bool isType = metaInfo.isUrl(); @@ -2318,7 +2348,7 @@ TEST_F(NodeMetaInfo, default_is_not_url) TEST_F(NodeMetaInfo, is_variant) { - auto metaInfo = createMetaInfo("QML", "var"); + auto metaInfo = createMetaInfo("QML", ModuleKind::QmlLibrary, "var"); bool isType = metaInfo.isVariant(); @@ -2336,7 +2366,7 @@ TEST_F(NodeMetaInfo, default_is_not_variant) TEST_F(NodeMetaInfo, is_vector2d) { - auto metaInfo = createMetaInfo("QtQuick", "vector2d"); + auto metaInfo = createMetaInfo("QtQuick", ModuleKind::QmlLibrary, "vector2d"); bool isType = metaInfo.isVector2D(); @@ -2354,7 +2384,7 @@ TEST_F(NodeMetaInfo, default_is_not_vector2d) TEST_F(NodeMetaInfo, is_vector3d) { - auto metaInfo = createMetaInfo("QtQuick", "vector3d"); + auto metaInfo = createMetaInfo("QtQuick", ModuleKind::QmlLibrary, "vector3d"); bool isType = metaInfo.isVector3D(); @@ -2372,7 +2402,7 @@ TEST_F(NodeMetaInfo, default_is_not_vector3d) TEST_F(NodeMetaInfo, is_vector4d) { - auto metaInfo = createMetaInfo("QtQuick", "vector4d"); + auto metaInfo = createMetaInfo("QtQuick", ModuleKind::QmlLibrary, "vector4d"); bool isType = metaInfo.isVector4D(); @@ -2390,7 +2420,7 @@ TEST_F(NodeMetaInfo, default_is_not_vector4d) TEST_F(NodeMetaInfo, QtQuick_ListView_is_view) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick", "ListView"); + auto metaInfo = createDerivedDummyMetaInfo("QtQuick", ModuleKind::QmlLibrary, "ListView"); bool isType = metaInfo.isView(); @@ -2399,7 +2429,7 @@ TEST_F(NodeMetaInfo, QtQuick_ListView_is_view) TEST_F(NodeMetaInfo, QtQuick_GridView_is_view) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick", "GridView"); + auto metaInfo = createDerivedDummyMetaInfo("QtQuick", ModuleKind::QmlLibrary, "GridView"); bool isType = metaInfo.isView(); @@ -2408,7 +2438,7 @@ TEST_F(NodeMetaInfo, QtQuick_GridView_is_view) TEST_F(NodeMetaInfo, QtQuick_PathView_is_view) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick", "PathView"); + auto metaInfo = createDerivedDummyMetaInfo("QtQuick", ModuleKind::QmlLibrary, "PathView"); bool isType = metaInfo.isView(); @@ -2428,7 +2458,7 @@ TEST_F(NodeMetaInfo, is_enumeration) { TypeTraits traits; traits.isEnum = true; - auto metaInfo = createMetaInfo("QML", "Foo", traits); + auto metaInfo = createMetaInfo("QML", ModuleKind::QmlLibrary, "Foo", traits); bool isType = metaInfo.isEnumeration(); @@ -2437,7 +2467,7 @@ TEST_F(NodeMetaInfo, is_enumeration) TEST_F(NodeMetaInfo, is_not_enumeration) { - auto metaInfo = createMetaInfo("QML", "Foo", {}); + auto metaInfo = createMetaInfo("QML", ModuleKind::QmlLibrary, "Foo", {}); bool isType = metaInfo.isEnumeration(); @@ -2457,7 +2487,7 @@ TEST_F(NodeMetaInfo, all_external_type_names) { QmlDesigner::Storage::Info::ExportedTypeNames names{{qmlModuleId, "Object", 2, -1}, {qmlModuleId, "Obj", 2, 1}}; - auto metaInfo = createMetaInfo("QML", "Foo"); + auto metaInfo = createMetaInfo("QML", ModuleKind::QmlLibrary, "Foo"); ON_CALL(projectStorageMock, exportedTypeNames(metaInfo.id())).WillByDefault(Return(names)); auto exportedTypeNames = metaInfo.allExportedTypeNames(); @@ -2483,7 +2513,7 @@ TEST_F(NodeMetaInfo, external_type_names_for_source_id) { QmlDesigner::Storage::Info::ExportedTypeNames names{{qmlModuleId, "Object", 2, -1}, {qmlModuleId, "Obj", 2, 1}}; - auto metaInfo = createMetaInfo("QML", "Foo"); + auto metaInfo = createMetaInfo("QML", ModuleKind::QmlLibrary, "Foo"); ON_CALL(projectStorageMock, exportedTypeNames(metaInfo.id(), model.fileUrlSourceId())) .WillByDefault(Return(names)); @@ -2511,7 +2541,7 @@ TEST_F(NodeMetaInfo, invalid_source_id_has_no_external_type_names_for_source_id) { QmlDesigner::Storage::Info::ExportedTypeNames names{{qmlModuleId, "Object", 2, -1}, {qmlModuleId, "Obj", 2, 1}}; - auto metaInfo = createMetaInfo("QML", "Foo"); + auto metaInfo = createMetaInfo("QML", ModuleKind::QmlLibrary, "Foo"); ON_CALL(projectStorageMock, exportedTypeNames(metaInfo.id(), model.fileUrlSourceId())) .WillByDefault(Return(names)); QmlDesigner::SourceId sourceId; @@ -2523,7 +2553,7 @@ TEST_F(NodeMetaInfo, invalid_source_id_has_no_external_type_names_for_source_id) TEST_F(NodeMetaInfo, float_is_a_number) { - auto metaInfo = createMetaInfo("QML-cppnative", "float"); + auto metaInfo = createMetaInfo("QML", ModuleKind::CppLibrary, "float"); bool isType = metaInfo.isNumber(); @@ -2550,7 +2580,7 @@ TEST_F(NodeMetaInfo, int_is_a_number) TEST_F(NodeMetaInfo, uint_is_a_number) { - auto metaInfo = createMetaInfo("QML-cppnative", "uint"); + auto metaInfo = createMetaInfo("QML", ModuleKind::CppLibrary, "uint"); bool isType = metaInfo.isNumber(); @@ -2568,7 +2598,7 @@ TEST_F(NodeMetaInfo, default_is_not_number) TEST_F(NodeMetaInfo, property_editor_specifics_path) { - auto metaInfo = createMetaInfo("QtQuick", "Item"); + auto metaInfo = createMetaInfo("QtQuick", ModuleKind::QmlLibrary, "Item"); auto pathId = QmlDesigner::SourceId::create(45); ON_CALL(projectStorageMock, propertyEditorPathId(metaInfo.id())).WillByDefault(Return(pathId)); @@ -2588,7 +2618,7 @@ TEST_F(NodeMetaInfo, default_property_editor_specifics_path_is_empty) TEST_F(NodeMetaInfo, is_reference) { - auto metaInfo = createMetaInfo("QtQuick", "Item", TypeTraitsKind::Reference); + auto metaInfo = createMetaInfo("QtQuick", ModuleKind::QmlLibrary, "Item", TypeTraitsKind::Reference); auto type = metaInfo.type(); @@ -2597,7 +2627,7 @@ TEST_F(NodeMetaInfo, is_reference) TEST_F(NodeMetaInfo, is_value) { - auto metaInfo = createMetaInfo("QML", "bool", TypeTraitsKind::Value); + auto metaInfo = createMetaInfo("QML", ModuleKind::QmlLibrary, "bool", TypeTraitsKind::Value); auto type = metaInfo.type(); @@ -2606,7 +2636,7 @@ TEST_F(NodeMetaInfo, is_value) TEST_F(NodeMetaInfo, is_sequence) { - auto metaInfo = createMetaInfo("QML", "QObjectList", TypeTraitsKind::Sequence); + auto metaInfo = createMetaInfo("QML", ModuleKind::QmlLibrary, "QObjectList", TypeTraitsKind::Sequence); auto type = metaInfo.type(); @@ -2615,7 +2645,7 @@ TEST_F(NodeMetaInfo, is_sequence) TEST_F(NodeMetaInfo, is_none) { - auto metaInfo = createMetaInfo("QML", "void", TypeTraitsKind::None); + auto metaInfo = createMetaInfo("QML", ModuleKind::QmlLibrary, "void", TypeTraitsKind::None); auto type = metaInfo.type(); @@ -2657,7 +2687,7 @@ TEST_F(NodeMetaInfo, invalid_can_not_be_container) TEST_F(NodeMetaInfo, component_can_be_container) { - auto moduleId = projectStorageMock.createModule("/path/to/project"); + auto moduleId = projectStorageMock.createModule("/path/to/project", ModuleKind::PathLibrary); TypeTraits traits{TypeTraitsKind::Reference}; traits.canBeContainer = FlagIs::True; auto typeId = projectStorageMock.createType(moduleId, "Foo", traits); @@ -2694,7 +2724,7 @@ TEST_F(NodeMetaInfo, invalid_do_no_forces_clipping) TEST_F(NodeMetaInfo, component_forces_clipping) { - auto moduleId = projectStorageMock.createModule("/path/to/project"); + auto moduleId = projectStorageMock.createModule("/path/to/project", ModuleKind::PathLibrary); TypeTraits traits{TypeTraitsKind::Reference}; traits.forceClip = FlagIs::True; auto typeId = projectStorageMock.createType(moduleId, "Foo", traits); @@ -2731,7 +2761,7 @@ TEST_F(NodeMetaInfo, invalid_does_not_layout_children) TEST_F(NodeMetaInfo, component_layouts_children) { - auto moduleId = projectStorageMock.createModule("/path/to/project"); + auto moduleId = projectStorageMock.createModule("/path/to/project", ModuleKind::PathLibrary); TypeTraits traits{TypeTraitsKind::Reference}; traits.doesLayoutChildren = FlagIs::True; auto typeId = projectStorageMock.createType(moduleId, "Foo", traits); @@ -2768,7 +2798,7 @@ TEST_F(NodeMetaInfo, invalid_cannot_be_dropped_in_form_editor) TEST_F(NodeMetaInfo, component_can_be_dropped_in_form_editor) { - auto moduleId = projectStorageMock.createModule("/path/to/project"); + auto moduleId = projectStorageMock.createModule("/path/to/project", ModuleKind::PathLibrary); TypeTraits traits{TypeTraitsKind::Reference}; traits.canBeDroppedInFormEditor = FlagIs::True; auto typeId = projectStorageMock.createType(moduleId, "Foo", traits); @@ -2805,7 +2835,7 @@ TEST_F(NodeMetaInfo, invalid_cannot_be_dropped_in_navigator) TEST_F(NodeMetaInfo, component_can_be_dropped_in_navigator) { - auto moduleId = projectStorageMock.createModule("/path/to/project"); + auto moduleId = projectStorageMock.createModule("/path/to/project", ModuleKind::PathLibrary); TypeTraits traits{TypeTraitsKind::Reference}; traits.canBeDroppedInNavigator = FlagIs::True; auto typeId = projectStorageMock.createType(moduleId, "Foo", traits); @@ -2842,7 +2872,7 @@ TEST_F(NodeMetaInfo, invalid_cannot_be_dropped_in_3d_view) TEST_F(NodeMetaInfo, component_can_be_dropped_in_3d_view) { - auto moduleId = projectStorageMock.createModule("/path/to/project"); + auto moduleId = projectStorageMock.createModule("/path/to/project", ModuleKind::PathLibrary); TypeTraits traits{TypeTraitsKind::Reference}; traits.canBeDroppedInView3D = FlagIs::True; auto typeId = projectStorageMock.createType(moduleId, "Foo", traits); @@ -2879,7 +2909,7 @@ TEST_F(NodeMetaInfo, invalid_is_not_movable) TEST_F(NodeMetaInfo, component_is_movable) { - auto moduleId = projectStorageMock.createModule("/path/to/project"); + auto moduleId = projectStorageMock.createModule("/path/to/project", ModuleKind::PathLibrary); TypeTraits traits{TypeTraitsKind::Reference}; traits.isMovable = FlagIs::True; auto typeId = projectStorageMock.createType(moduleId, "Foo", traits); @@ -2916,7 +2946,7 @@ TEST_F(NodeMetaInfo, invalid_is_not_resizable) TEST_F(NodeMetaInfo, component_is_resizable) { - auto moduleId = projectStorageMock.createModule("/path/to/project"); + auto moduleId = projectStorageMock.createModule("/path/to/project", ModuleKind::PathLibrary); TypeTraits traits{TypeTraitsKind::Reference}; traits.isResizable = FlagIs::True; auto typeId = projectStorageMock.createType(moduleId, "Foo", traits); @@ -2953,7 +2983,7 @@ TEST_F(NodeMetaInfo, invalid_has_not_form_editor_item) TEST_F(NodeMetaInfo, component_has_form_editor_item) { - auto moduleId = projectStorageMock.createModule("/path/to/project"); + auto moduleId = projectStorageMock.createModule("/path/to/project", ModuleKind::PathLibrary); TypeTraits traits{TypeTraitsKind::Reference}; traits.hasFormEditorItem = FlagIs::True; auto typeId = projectStorageMock.createType(moduleId, "Foo", traits); @@ -2990,7 +3020,7 @@ TEST_F(NodeMetaInfo, invalid_is_not_stacked_container) TEST_F(NodeMetaInfo, component_is_stacked_container) { - auto moduleId = projectStorageMock.createModule("/path/to/project"); + auto moduleId = projectStorageMock.createModule("/path/to/project", ModuleKind::PathLibrary); TypeTraits traits{TypeTraitsKind::Reference}; traits.isStackedContainer = FlagIs::True; auto typeId = projectStorageMock.createType(moduleId, "Foo", traits); @@ -3027,7 +3057,7 @@ TEST_F(NodeMetaInfo, invalid_dont_takes_over_rendering_of_children) TEST_F(NodeMetaInfo, component_takes_over_rendering_of_children) { - auto moduleId = projectStorageMock.createModule("/path/to/project"); + auto moduleId = projectStorageMock.createModule("/path/to/project", ModuleKind::PathLibrary); TypeTraits traits{TypeTraitsKind::Reference}; traits.takesOverRenderingOfChildren = FlagIs::True; auto typeId = projectStorageMock.createType(moduleId, "Foo", traits); @@ -3064,7 +3094,7 @@ TEST_F(NodeMetaInfo, invalid_is_not_visible_in_navigator) TEST_F(NodeMetaInfo, component_is_visible_in_navigator) { - auto moduleId = projectStorageMock.createModule("/path/to/project"); + auto moduleId = projectStorageMock.createModule("/path/to/project", ModuleKind::PathLibrary); TypeTraits traits{TypeTraitsKind::Reference}; traits.visibleInNavigator = FlagIs::True; auto typeId = projectStorageMock.createType(moduleId, "Foo", traits); @@ -3101,7 +3131,7 @@ TEST_F(NodeMetaInfo, invalid_is_not_visible_in_library) TEST_F(NodeMetaInfo, component_is_visible_in_library) { - auto moduleId = projectStorageMock.createModule("/path/to/project"); + auto moduleId = projectStorageMock.createModule("/path/to/project", ModuleKind::PathLibrary); TypeTraits traits{TypeTraitsKind::Reference}; traits.visibleInLibrary = FlagIs::True; auto typeId = projectStorageMock.createType(moduleId, "Foo", traits); diff --git a/tests/unit/tests/unittests/metainfo/propertymetainfo-test.cpp b/tests/unit/tests/unittests/metainfo/propertymetainfo-test.cpp index 25436264aee..d2ec90b7a81 100644 --- a/tests/unit/tests/unittests/metainfo/propertymetainfo-test.cpp +++ b/tests/unit/tests/unittests/metainfo/propertymetainfo-test.cpp @@ -22,6 +22,7 @@ namespace { using QmlDesigner::Enumeration; using QmlDesigner::ModelNode; using QmlDesigner::ModelNodes; +using QmlDesigner::Storage::ModuleKind; using QmlDesigner::Storage::PropertyDeclarationTraits; using QmlDesigner::Storage::TypeTraits; @@ -29,10 +30,11 @@ class PropertyMetaInfo : public ::testing::Test { protected: QmlDesigner::NodeMetaInfo createNodeMetaInfo(Utils::SmallStringView moduleName, + ModuleKind moduleKind, Utils::SmallStringView typeName, QmlDesigner::Storage::TypeTraits typeTraits = {}) { - auto moduleId = projectStorageMock.createModule(moduleName); + auto moduleId = projectStorageMock.createModule(moduleName, moduleKind); auto typeId = projectStorageMock.createType(moduleId, typeName, typeTraits); return QmlDesigner::NodeMetaInfo{typeId, &projectStorageMock}; @@ -47,7 +49,7 @@ protected: QmlDesigner::Import::createLibraryImport("QtQuick"), QmlDesigner::Import::createLibraryImport("QtQml.Models")}, QUrl::fromLocalFile(pathCache.path.toQString())}; - QmlDesigner::NodeMetaInfo nodeInfo = createNodeMetaInfo("QtQuick", "Foo"); + QmlDesigner::NodeMetaInfo nodeInfo = createNodeMetaInfo("QtQuick", ModuleKind::QmlLibrary, "Foo"); }; TEST_F(PropertyMetaInfo, name) @@ -71,7 +73,7 @@ TEST_F(PropertyMetaInfo, default_has_no_name) TEST_F(PropertyMetaInfo, property_type) { - auto barInfo = createNodeMetaInfo("QtQuick", "Bar"); + auto barInfo = createNodeMetaInfo("QtQuick", ModuleKind::QmlLibrary, "Bar"); projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, barInfo.id()); auto propertyInfo = nodeInfo.property("bar"); @@ -91,7 +93,7 @@ TEST_F(PropertyMetaInfo, default_hads_invalid_property_type) TEST_F(PropertyMetaInfo, type) { - auto barInfo = createNodeMetaInfo("QtQuick", "Bar"); + auto barInfo = createNodeMetaInfo("QtQuick", ModuleKind::QmlLibrary, "Bar"); projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, barInfo.id()); auto propertyInfo = nodeInfo.property("bar"); @@ -181,7 +183,7 @@ TEST_F(PropertyMetaInfo, is_enumeration) { TypeTraits traits; traits.isEnum = true; - auto enumInfo = createNodeMetaInfo("QtQuick", "MyEnum", traits); + auto enumInfo = createNodeMetaInfo("QtQuick", ModuleKind::QmlLibrary, "MyEnum", traits); projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, enumInfo.id()); auto propertyInfo = nodeInfo.property("bar"); @@ -192,7 +194,7 @@ TEST_F(PropertyMetaInfo, is_enumeration) TEST_F(PropertyMetaInfo, is_not_enumeration) { - auto notEnumInfo = createNodeMetaInfo("QtQuick", "NoEnum", {}); + auto notEnumInfo = createNodeMetaInfo("QtQuick", ModuleKind::QmlLibrary, "NoEnum", {}); projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, notEnumInfo.id()); auto propertyInfo = nodeInfo.property("bar"); @@ -275,7 +277,7 @@ TEST_F(PropertyMetaInfo, cast_to_enumeration) { TypeTraits traits; traits.isEnum = true; - auto propertyTypeInfo = createNodeMetaInfo("QtQuick", "MyEnum", traits); + auto propertyTypeInfo = createNodeMetaInfo("QtQuick", ModuleKind::QmlLibrary, "MyEnum", traits); projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); auto propertyInfo = nodeInfo.property("bar"); Enumeration enumeration{"MyEnum.Foo"}; @@ -288,7 +290,7 @@ TEST_F(PropertyMetaInfo, cast_to_enumeration) TEST_F(PropertyMetaInfo, dont_to_cast_enumeration_if_property_type_is_not_enumeration) { - auto propertyTypeInfo = createNodeMetaInfo("QtQuick", "MyEnum", {}); + auto propertyTypeInfo = createNodeMetaInfo("QtQuick", ModuleKind::QmlLibrary, "MyEnum", {}); projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); auto propertyInfo = nodeInfo.property("bar"); Enumeration enumeration{"MyEnum.Foo"}; @@ -303,7 +305,7 @@ TEST_F(PropertyMetaInfo, dont_to_cast_enumeration_if_value_is_not_Enumeration) { TypeTraits traits; traits.isEnum = true; - auto propertyTypeInfo = createNodeMetaInfo("QtQuick", "MyEnum", traits); + auto propertyTypeInfo = createNodeMetaInfo("QtQuick", ModuleKind::QmlLibrary, "MyEnum", traits); projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); auto propertyInfo = nodeInfo.property("bar"); auto value = QVariant::fromValue(QString{"enumeration"}); @@ -315,7 +317,7 @@ TEST_F(PropertyMetaInfo, dont_to_cast_enumeration_if_value_is_not_Enumeration) TEST_F(PropertyMetaInfo, cast_to_model_node) { - auto propertyTypeInfo = createNodeMetaInfo("QML", "var", {}); + auto propertyTypeInfo = createNodeMetaInfo("QML", ModuleKind::QmlLibrary, "var", {}); projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); auto propertyInfo = nodeInfo.property("bar"); auto value = QVariant::fromValue(model.rootModelNode()); @@ -327,7 +329,7 @@ TEST_F(PropertyMetaInfo, cast_to_model_node) TEST_F(PropertyMetaInfo, cast_to_qvariant_always_returns_the_save_variant_if_the_property_type_is_var) { - auto propertyTypeInfo = createNodeMetaInfo("QML", "var", {}); + auto propertyTypeInfo = createNodeMetaInfo("QML", ModuleKind::QmlLibrary, "var", {}); projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); auto propertyInfo = nodeInfo.property("bar"); auto value = QVariant::fromValue(QString{"foo"}); @@ -339,7 +341,7 @@ TEST_F(PropertyMetaInfo, cast_to_qvariant_always_returns_the_save_variant_if_the TEST_F(PropertyMetaInfo, cast_double_to_double) { - auto propertyTypeInfo = createNodeMetaInfo("QML", "double", {}); + auto propertyTypeInfo = createNodeMetaInfo("QML", ModuleKind::QmlLibrary, "double", {}); projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); auto propertyInfo = nodeInfo.property("bar"); auto value = QVariant::fromValue(14.2); @@ -351,7 +353,7 @@ TEST_F(PropertyMetaInfo, cast_double_to_double) TEST_F(PropertyMetaInfo, cast_int_to_double_returns_number_variant) { - auto propertyTypeInfo = createNodeMetaInfo("QML", "double", {}); + auto propertyTypeInfo = createNodeMetaInfo("QML", ModuleKind::QmlLibrary, "double", {}); projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); auto propertyInfo = nodeInfo.property("bar"); auto value = QVariant::fromValue(14); @@ -363,7 +365,7 @@ TEST_F(PropertyMetaInfo, cast_int_to_double_returns_number_variant) TEST_F(PropertyMetaInfo, cast_default_to_double_returns_zero_variant) { - auto propertyTypeInfo = createNodeMetaInfo("QML", "double", {}); + auto propertyTypeInfo = createNodeMetaInfo("QML", ModuleKind::QmlLibrary, "double", {}); projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); auto propertyInfo = nodeInfo.property("bar"); auto value = QVariant(); @@ -375,7 +377,7 @@ TEST_F(PropertyMetaInfo, cast_default_to_double_returns_zero_variant) TEST_F(PropertyMetaInfo, cast_qstring_to_double_returns_zero_variant) { - auto propertyTypeInfo = createNodeMetaInfo("QML", "double", {}); + auto propertyTypeInfo = createNodeMetaInfo("QML", ModuleKind::QmlLibrary, "double", {}); projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); auto propertyInfo = nodeInfo.property("bar"); auto value = QVariant::fromValue(QString{"foo"}); @@ -387,7 +389,7 @@ TEST_F(PropertyMetaInfo, cast_qstring_to_double_returns_zero_variant) TEST_F(PropertyMetaInfo, cast_float_to_float) { - auto propertyTypeInfo = createNodeMetaInfo("QML-cppnative", "float", {}); + auto propertyTypeInfo = createNodeMetaInfo("QML", ModuleKind::CppLibrary, "float", {}); projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); auto propertyInfo = nodeInfo.property("bar"); auto value = QVariant::fromValue(14.2f); @@ -399,7 +401,7 @@ TEST_F(PropertyMetaInfo, cast_float_to_float) TEST_F(PropertyMetaInfo, cast_int_to_float_returns_number_variant) { - auto propertyTypeInfo = createNodeMetaInfo("QML-cppnative", "float", {}); + auto propertyTypeInfo = createNodeMetaInfo("QML", ModuleKind::CppLibrary, "float", {}); projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); auto propertyInfo = nodeInfo.property("bar"); auto value = QVariant::fromValue(14); @@ -411,7 +413,7 @@ TEST_F(PropertyMetaInfo, cast_int_to_float_returns_number_variant) TEST_F(PropertyMetaInfo, cast_default_to_float_returns_zero_variant) { - auto propertyTypeInfo = createNodeMetaInfo("QML-cppnative", "float", {}); + auto propertyTypeInfo = createNodeMetaInfo("QML", ModuleKind::CppLibrary, "float", {}); projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); auto propertyInfo = nodeInfo.property("bar"); auto value = QVariant(); @@ -423,7 +425,7 @@ TEST_F(PropertyMetaInfo, cast_default_to_float_returns_zero_variant) TEST_F(PropertyMetaInfo, cast_qstring_to_float_returns_zero_variant) { - auto propertyTypeInfo = createNodeMetaInfo("QML-cppnative", "float", {}); + auto propertyTypeInfo = createNodeMetaInfo("QML", ModuleKind::CppLibrary, "float", {}); projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); auto propertyInfo = nodeInfo.property("bar"); auto value = QVariant::fromValue(QString{"foo"}); @@ -435,7 +437,7 @@ TEST_F(PropertyMetaInfo, cast_qstring_to_float_returns_zero_variant) TEST_F(PropertyMetaInfo, cast_int_to_int) { - auto propertyTypeInfo = createNodeMetaInfo("QML", "int", {}); + auto propertyTypeInfo = createNodeMetaInfo("QML", ModuleKind::QmlLibrary, "int", {}); projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); auto propertyInfo = nodeInfo.property("bar"); auto value = QVariant::fromValue(14); @@ -447,7 +449,7 @@ TEST_F(PropertyMetaInfo, cast_int_to_int) TEST_F(PropertyMetaInfo, cast_double_to_int_returns_number_variant) { - auto propertyTypeInfo = createNodeMetaInfo("QML", "int", {}); + auto propertyTypeInfo = createNodeMetaInfo("QML", ModuleKind::QmlLibrary, "int", {}); projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); auto propertyInfo = nodeInfo.property("bar"); auto value = QVariant::fromValue(14.2); @@ -459,7 +461,7 @@ TEST_F(PropertyMetaInfo, cast_double_to_int_returns_number_variant) TEST_F(PropertyMetaInfo, cast_default_to_int_returns_zero_variant) { - auto propertyTypeInfo = createNodeMetaInfo("QML", "int", {}); + auto propertyTypeInfo = createNodeMetaInfo("QML", ModuleKind::QmlLibrary, "int", {}); projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); auto propertyInfo = nodeInfo.property("bar"); auto value = QVariant(); @@ -471,7 +473,7 @@ TEST_F(PropertyMetaInfo, cast_default_to_int_returns_zero_variant) TEST_F(PropertyMetaInfo, cast_qstring_to_int_returns_zero_variant) { - auto propertyTypeInfo = createNodeMetaInfo("QML", "int", {}); + auto propertyTypeInfo = createNodeMetaInfo("QML", ModuleKind::QmlLibrary, "int", {}); projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); auto propertyInfo = nodeInfo.property("bar"); auto value = QVariant::fromValue(QString{"foo"}); @@ -483,7 +485,7 @@ TEST_F(PropertyMetaInfo, cast_qstring_to_int_returns_zero_variant) TEST_F(PropertyMetaInfo, cast_bool_to_bool) { - auto propertyTypeInfo = createNodeMetaInfo("QML", "bool", {}); + auto propertyTypeInfo = createNodeMetaInfo("QML", ModuleKind::QmlLibrary, "bool", {}); projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); auto propertyInfo = nodeInfo.property("bar"); auto value = QVariant::fromValue(true); @@ -495,7 +497,7 @@ TEST_F(PropertyMetaInfo, cast_bool_to_bool) TEST_F(PropertyMetaInfo, cast_float_to_bool_returns_boolean_variant) { - auto propertyTypeInfo = createNodeMetaInfo("QML", "bool", {}); + auto propertyTypeInfo = createNodeMetaInfo("QML", ModuleKind::QmlLibrary, "bool", {}); projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); auto propertyInfo = nodeInfo.property("bar"); auto value = QVariant::fromValue(14.2f); @@ -507,7 +509,7 @@ TEST_F(PropertyMetaInfo, cast_float_to_bool_returns_boolean_variant) TEST_F(PropertyMetaInfo, cast_double_to_bool_returns_boolean_variant) { - auto propertyTypeInfo = createNodeMetaInfo("QML", "bool", {}); + auto propertyTypeInfo = createNodeMetaInfo("QML", ModuleKind::QmlLibrary, "bool", {}); projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); auto propertyInfo = nodeInfo.property("bar"); auto value = QVariant::fromValue(14.2); @@ -519,7 +521,7 @@ TEST_F(PropertyMetaInfo, cast_double_to_bool_returns_boolean_variant) TEST_F(PropertyMetaInfo, cast_int_to_bool_returns_boolean_variant) { - auto propertyTypeInfo = createNodeMetaInfo("QML", "bool", {}); + auto propertyTypeInfo = createNodeMetaInfo("QML", ModuleKind::QmlLibrary, "bool", {}); projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); auto propertyInfo = nodeInfo.property("bar"); auto value = QVariant::fromValue(14); @@ -531,7 +533,7 @@ TEST_F(PropertyMetaInfo, cast_int_to_bool_returns_boolean_variant) TEST_F(PropertyMetaInfo, cast_long_to_bool_returns_boolean_variant) { - auto propertyTypeInfo = createNodeMetaInfo("QML", "bool", {}); + auto propertyTypeInfo = createNodeMetaInfo("QML", ModuleKind::QmlLibrary, "bool", {}); projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); auto propertyInfo = nodeInfo.property("bar"); auto value = QVariant::fromValue(14L); @@ -543,7 +545,7 @@ TEST_F(PropertyMetaInfo, cast_long_to_bool_returns_boolean_variant) TEST_F(PropertyMetaInfo, cast_long_long_to_bool_returns_boolean_variant) { - auto propertyTypeInfo = createNodeMetaInfo("QML", "bool", {}); + auto propertyTypeInfo = createNodeMetaInfo("QML", ModuleKind::QmlLibrary, "bool", {}); projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); auto propertyInfo = nodeInfo.property("bar"); auto value = QVariant::fromValue(14LL); @@ -555,7 +557,7 @@ TEST_F(PropertyMetaInfo, cast_long_long_to_bool_returns_boolean_variant) TEST_F(PropertyMetaInfo, cast_default_to_bool_returns_zero_variant) { - auto propertyTypeInfo = createNodeMetaInfo("QML", "bool", {}); + auto propertyTypeInfo = createNodeMetaInfo("QML", ModuleKind::QmlLibrary, "bool", {}); projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); auto propertyInfo = nodeInfo.property("bar"); auto value = QVariant(); @@ -567,7 +569,7 @@ TEST_F(PropertyMetaInfo, cast_default_to_bool_returns_zero_variant) TEST_F(PropertyMetaInfo, cast_qstring_to_bool_returns_zero_variant) { - auto propertyTypeInfo = createNodeMetaInfo("QML", "bool", {}); + auto propertyTypeInfo = createNodeMetaInfo("QML", ModuleKind::QmlLibrary, "bool", {}); projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); auto propertyInfo = nodeInfo.property("bar"); auto value = QVariant::fromValue(QString{"foo"}); @@ -579,7 +581,7 @@ TEST_F(PropertyMetaInfo, cast_qstring_to_bool_returns_zero_variant) TEST_F(PropertyMetaInfo, cast_string_to_string) { - auto propertyTypeInfo = createNodeMetaInfo("QML", "string", {}); + auto propertyTypeInfo = createNodeMetaInfo("QML", ModuleKind::QmlLibrary, "string", {}); projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); auto propertyInfo = nodeInfo.property("bar"); auto value = QVariant::fromValue(QString{"foo"}); @@ -591,7 +593,7 @@ TEST_F(PropertyMetaInfo, cast_string_to_string) TEST_F(PropertyMetaInfo, cast_QByteArray_to_empty_string) { - auto propertyTypeInfo = createNodeMetaInfo("QML", "string", {}); + auto propertyTypeInfo = createNodeMetaInfo("QML", ModuleKind::QmlLibrary, "string", {}); projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); auto propertyInfo = nodeInfo.property("bar"); auto value = QVariant::fromValue(QByteArray{"foo"}); @@ -603,7 +605,7 @@ TEST_F(PropertyMetaInfo, cast_QByteArray_to_empty_string) TEST_F(PropertyMetaInfo, cast_int_to_empty_string) { - auto propertyTypeInfo = createNodeMetaInfo("QML", "string", {}); + auto propertyTypeInfo = createNodeMetaInfo("QML", ModuleKind::QmlLibrary, "string", {}); projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); auto propertyInfo = nodeInfo.property("bar"); auto value = QVariant::fromValue(14); @@ -615,7 +617,7 @@ TEST_F(PropertyMetaInfo, cast_int_to_empty_string) TEST_F(PropertyMetaInfo, cast_default_to_empty_string) { - auto propertyTypeInfo = createNodeMetaInfo("QML", "string", {}); + auto propertyTypeInfo = createNodeMetaInfo("QML", ModuleKind::QmlLibrary, "string", {}); projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); auto propertyInfo = nodeInfo.property("bar"); auto value = QVariant(); @@ -627,7 +629,7 @@ TEST_F(PropertyMetaInfo, cast_default_to_empty_string) TEST_F(PropertyMetaInfo, cast_datatime_to_datetime) { - auto propertyTypeInfo = createNodeMetaInfo("QML", "date", {}); + auto propertyTypeInfo = createNodeMetaInfo("QML", ModuleKind::QmlLibrary, "date", {}); projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); auto propertyInfo = nodeInfo.property("bar"); auto dataTime = QDateTime::currentDateTime(); @@ -640,7 +642,7 @@ TEST_F(PropertyMetaInfo, cast_datatime_to_datetime) TEST_F(PropertyMetaInfo, cast_int_to_datetime_returns_default_datetime) { - auto propertyTypeInfo = createNodeMetaInfo("QML", "date", {}); + auto propertyTypeInfo = createNodeMetaInfo("QML", ModuleKind::QmlLibrary, "date", {}); projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); auto propertyInfo = nodeInfo.property("bar"); auto value = QVariant::fromValue(14); @@ -652,7 +654,7 @@ TEST_F(PropertyMetaInfo, cast_int_to_datetime_returns_default_datetime) TEST_F(PropertyMetaInfo, cast_string_to_datetime_returns_default_datetime) { - auto propertyTypeInfo = createNodeMetaInfo("QML", "date", {}); + auto propertyTypeInfo = createNodeMetaInfo("QML", ModuleKind::QmlLibrary, "date", {}); projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); auto propertyInfo = nodeInfo.property("bar"); auto value = QVariant::fromValue(QString{"Monday"}); @@ -664,7 +666,7 @@ TEST_F(PropertyMetaInfo, cast_string_to_datetime_returns_default_datetime) TEST_F(PropertyMetaInfo, cast_default_to_datetime_returns_default_datetime) { - auto propertyTypeInfo = createNodeMetaInfo("QML", "date", {}); + auto propertyTypeInfo = createNodeMetaInfo("QML", ModuleKind::QmlLibrary, "date", {}); projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); auto propertyInfo = nodeInfo.property("bar"); auto value = QVariant(); @@ -676,7 +678,7 @@ TEST_F(PropertyMetaInfo, cast_default_to_datetime_returns_default_datetime) TEST_F(PropertyMetaInfo, cast_url_to_url) { - auto propertyTypeInfo = createNodeMetaInfo("QML", "url", {}); + auto propertyTypeInfo = createNodeMetaInfo("QML", ModuleKind::QmlLibrary, "url", {}); projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); auto propertyInfo = nodeInfo.property("bar"); auto url = QUrl("http://www.qt.io/future"); @@ -689,7 +691,7 @@ TEST_F(PropertyMetaInfo, cast_url_to_url) TEST_F(PropertyMetaInfo, cast_string_to_empty_url) { - auto propertyTypeInfo = createNodeMetaInfo("QML", "url", {}); + auto propertyTypeInfo = createNodeMetaInfo("QML", ModuleKind::QmlLibrary, "url", {}); projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); auto propertyInfo = nodeInfo.property("bar"); auto value = QVariant::fromValue(QString{"http://www.qt.io/future"}); @@ -701,7 +703,7 @@ TEST_F(PropertyMetaInfo, cast_string_to_empty_url) TEST_F(PropertyMetaInfo, cast_default_to_empty_url) { - auto propertyTypeInfo = createNodeMetaInfo("QML", "url", {}); + auto propertyTypeInfo = createNodeMetaInfo("QML", ModuleKind::QmlLibrary, "url", {}); projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); auto propertyInfo = nodeInfo.property("bar"); auto value = QVariant(); @@ -713,7 +715,7 @@ TEST_F(PropertyMetaInfo, cast_default_to_empty_url) TEST_F(PropertyMetaInfo, cast_color_to_color) { - auto propertyTypeInfo = createNodeMetaInfo("QtQuick", "color", {}); + auto propertyTypeInfo = createNodeMetaInfo("QtQuick", ModuleKind::QmlLibrary, "color", {}); projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); auto propertyInfo = nodeInfo.property("bar"); auto color = QColor(Qt::red); @@ -726,7 +728,7 @@ TEST_F(PropertyMetaInfo, cast_color_to_color) TEST_F(PropertyMetaInfo, cast_string_to_null_color) { - auto propertyTypeInfo = createNodeMetaInfo("QtQuick", "color", {}); + auto propertyTypeInfo = createNodeMetaInfo("QtQuick", ModuleKind::QmlLibrary, "color", {}); projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); auto propertyInfo = nodeInfo.property("bar"); auto value = QVariant("red"); @@ -738,7 +740,7 @@ TEST_F(PropertyMetaInfo, cast_string_to_null_color) TEST_F(PropertyMetaInfo, cast_int_to_null_color) { - auto propertyTypeInfo = createNodeMetaInfo("QtQuick", "color", {}); + auto propertyTypeInfo = createNodeMetaInfo("QtQuick", ModuleKind::QmlLibrary, "color", {}); projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); auto propertyInfo = nodeInfo.property("bar"); auto value = QVariant(14); @@ -750,7 +752,7 @@ TEST_F(PropertyMetaInfo, cast_int_to_null_color) TEST_F(PropertyMetaInfo, cast_default_to_null_color) { - auto propertyTypeInfo = createNodeMetaInfo("QtQuick", "color", {}); + auto propertyTypeInfo = createNodeMetaInfo("QtQuick", ModuleKind::QmlLibrary, "color", {}); projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); auto propertyInfo = nodeInfo.property("bar"); auto value = QVariant(); @@ -762,7 +764,7 @@ TEST_F(PropertyMetaInfo, cast_default_to_null_color) TEST_F(PropertyMetaInfo, cast_vector2d_to_vector2d) { - auto propertyTypeInfo = createNodeMetaInfo("QtQuick", "vector2d", {}); + auto propertyTypeInfo = createNodeMetaInfo("QtQuick", ModuleKind::QmlLibrary, "vector2d", {}); projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); auto propertyInfo = nodeInfo.property("bar"); auto vector2d = QVector2D{32.2f, 2.2f}; @@ -775,7 +777,7 @@ TEST_F(PropertyMetaInfo, cast_vector2d_to_vector2d) TEST_F(PropertyMetaInfo, cast_string_to_vector2d_returns_an_empty_vector2d) { - auto propertyTypeInfo = createNodeMetaInfo("QtQuick", "vector2d", {}); + auto propertyTypeInfo = createNodeMetaInfo("QtQuick", ModuleKind::QmlLibrary, "vector2d", {}); projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); auto propertyInfo = nodeInfo.property("bar"); auto value = QVariant(QString{"foo"}); @@ -787,7 +789,7 @@ TEST_F(PropertyMetaInfo, cast_string_to_vector2d_returns_an_empty_vector2d) TEST_F(PropertyMetaInfo, cast_int_to_vector2d_returns_an_empty_vector2d) { - auto propertyTypeInfo = createNodeMetaInfo("QtQuick", "vector2d", {}); + auto propertyTypeInfo = createNodeMetaInfo("QtQuick", ModuleKind::QmlLibrary, "vector2d", {}); projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); auto propertyInfo = nodeInfo.property("bar"); auto value = QVariant(12); @@ -799,7 +801,7 @@ TEST_F(PropertyMetaInfo, cast_int_to_vector2d_returns_an_empty_vector2d) TEST_F(PropertyMetaInfo, cast_vector3d_to_vector2d_returns_an_empty_vector2d) { - auto propertyTypeInfo = createNodeMetaInfo("QtQuick", "vector2d", {}); + auto propertyTypeInfo = createNodeMetaInfo("QtQuick", ModuleKind::QmlLibrary, "vector2d", {}); projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); auto propertyInfo = nodeInfo.property("bar"); auto value = QVariant(QVector3D{32.2f, 2.2f, 784.f}); @@ -811,7 +813,7 @@ TEST_F(PropertyMetaInfo, cast_vector3d_to_vector2d_returns_an_empty_vector2d) TEST_F(PropertyMetaInfo, cast_default_to_vector2d_returns_an_empty_vector2d) { - auto propertyTypeInfo = createNodeMetaInfo("QtQuick", "vector2d", {}); + auto propertyTypeInfo = createNodeMetaInfo("QtQuick", ModuleKind::QmlLibrary, "vector2d", {}); projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); auto propertyInfo = nodeInfo.property("bar"); auto value = QVariant(); @@ -823,7 +825,7 @@ TEST_F(PropertyMetaInfo, cast_default_to_vector2d_returns_an_empty_vector2d) TEST_F(PropertyMetaInfo, cast_vector3d_to_vector3d) { - auto propertyTypeInfo = createNodeMetaInfo("QtQuick", "vector3d", {}); + auto propertyTypeInfo = createNodeMetaInfo("QtQuick", ModuleKind::QmlLibrary, "vector3d", {}); projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); auto propertyInfo = nodeInfo.property("bar"); auto vector3d = QVector3D{32.2f, 2.2f, 44.4f}; @@ -836,7 +838,7 @@ TEST_F(PropertyMetaInfo, cast_vector3d_to_vector3d) TEST_F(PropertyMetaInfo, cast_string_to_vector3d_returns_an_empty_vector3d) { - auto propertyTypeInfo = createNodeMetaInfo("QtQuick", "vector3d", {}); + auto propertyTypeInfo = createNodeMetaInfo("QtQuick", ModuleKind::QmlLibrary, "vector3d", {}); projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); auto propertyInfo = nodeInfo.property("bar"); auto value = QVariant(QString{"foo"}); @@ -848,7 +850,7 @@ TEST_F(PropertyMetaInfo, cast_string_to_vector3d_returns_an_empty_vector3d) TEST_F(PropertyMetaInfo, cast_int_to_vector3d_returns_an_empty_vector3d) { - auto propertyTypeInfo = createNodeMetaInfo("QtQuick", "vector3d", {}); + auto propertyTypeInfo = createNodeMetaInfo("QtQuick", ModuleKind::QmlLibrary, "vector3d", {}); projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); auto propertyInfo = nodeInfo.property("bar"); auto value = QVariant(12); @@ -860,7 +862,7 @@ TEST_F(PropertyMetaInfo, cast_int_to_vector3d_returns_an_empty_vector3d) TEST_F(PropertyMetaInfo, cast_vector4d_to_vector3d_returns_an_empty_vector3d) { - auto propertyTypeInfo = createNodeMetaInfo("QtQuick", "vector3d", {}); + auto propertyTypeInfo = createNodeMetaInfo("QtQuick", ModuleKind::QmlLibrary, "vector3d", {}); projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); auto propertyInfo = nodeInfo.property("bar"); auto value = QVariant(QVector4D{32.2f, 2.2f, 784.f, 99.f}); @@ -872,7 +874,7 @@ TEST_F(PropertyMetaInfo, cast_vector4d_to_vector3d_returns_an_empty_vector3d) TEST_F(PropertyMetaInfo, cast_default_to_vector3d_returns_an_empty_vector3d) { - auto propertyTypeInfo = createNodeMetaInfo("QtQuick", "vector3d", {}); + auto propertyTypeInfo = createNodeMetaInfo("QtQuick", ModuleKind::QmlLibrary, "vector3d", {}); projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); auto propertyInfo = nodeInfo.property("bar"); auto value = QVariant(); @@ -884,7 +886,7 @@ TEST_F(PropertyMetaInfo, cast_default_to_vector3d_returns_an_empty_vector3d) TEST_F(PropertyMetaInfo, cast_vector4d_to_vector4d) { - auto propertyTypeInfo = createNodeMetaInfo("QtQuick", "vector4d", {}); + auto propertyTypeInfo = createNodeMetaInfo("QtQuick", ModuleKind::QmlLibrary, "vector4d", {}); projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); auto propertyInfo = nodeInfo.property("bar"); auto vector4d = QVector4D{32.2f, 2.2f, 44.4f, 23.f}; @@ -897,7 +899,7 @@ TEST_F(PropertyMetaInfo, cast_vector4d_to_vector4d) TEST_F(PropertyMetaInfo, cast_string_to_vector4d_returns_an_empty_vector4d) { - auto propertyTypeInfo = createNodeMetaInfo("QtQuick", "vector4d", {}); + auto propertyTypeInfo = createNodeMetaInfo("QtQuick", ModuleKind::QmlLibrary, "vector4d", {}); projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); auto propertyInfo = nodeInfo.property("bar"); auto value = QVariant(QString{"foo"}); @@ -909,7 +911,7 @@ TEST_F(PropertyMetaInfo, cast_string_to_vector4d_returns_an_empty_vector4d) TEST_F(PropertyMetaInfo, cast_int_to_vector4d_returns_an_empty_vector4d) { - auto propertyTypeInfo = createNodeMetaInfo("QtQuick", "vector4d", {}); + auto propertyTypeInfo = createNodeMetaInfo("QtQuick", ModuleKind::QmlLibrary, "vector4d", {}); projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); auto propertyInfo = nodeInfo.property("bar"); auto value = QVariant(12); @@ -921,7 +923,7 @@ TEST_F(PropertyMetaInfo, cast_int_to_vector4d_returns_an_empty_vector4d) TEST_F(PropertyMetaInfo, cast_vector2d_to_vector4d_returns_an_empty_vector4d) { - auto propertyTypeInfo = createNodeMetaInfo("QtQuick", "vector4d", {}); + auto propertyTypeInfo = createNodeMetaInfo("QtQuick", ModuleKind::QmlLibrary, "vector4d", {}); projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); auto propertyInfo = nodeInfo.property("bar"); auto value = QVariant(QVector2D{32.2f, 2.2f}); @@ -933,7 +935,7 @@ TEST_F(PropertyMetaInfo, cast_vector2d_to_vector4d_returns_an_empty_vector4d) TEST_F(PropertyMetaInfo, cast_default_to_vector4d_returns_an_empty_vector4d) { - auto propertyTypeInfo = createNodeMetaInfo("QtQuick", "vector4d", {}); + auto propertyTypeInfo = createNodeMetaInfo("QtQuick", ModuleKind::QmlLibrary, "vector4d", {}); projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); auto propertyInfo = nodeInfo.property("bar"); auto value = QVariant(); @@ -955,7 +957,7 @@ TEST_F(PropertyMetaInfo, default_cast_to_invalid_variant) TEST_F(PropertyMetaInfo, not_existing_property_cast_returns_invalid_value) { - auto propertyTypeInfo = createNodeMetaInfo("QtQuick", "vector4d", {}); + auto propertyTypeInfo = createNodeMetaInfo("QtQuick", ModuleKind::QmlLibrary, "vector4d", {}); auto propertyInfo = nodeInfo.property("bar"); auto value = QVariant(43); diff --git a/tests/unit/tests/unittests/model/model-test.cpp b/tests/unit/tests/unittests/model/model-test.cpp index bd34a3a6b12..80c37f3a6b5 100644 --- a/tests/unit/tests/unittests/model/model-test.cpp +++ b/tests/unit/tests/unittests/model/model-test.cpp @@ -26,6 +26,7 @@ using QmlDesigner::AbstractProperty; using QmlDesigner::ModelNode; using QmlDesigner::ModelNodes; using QmlDesigner::ModelResourceSet; +using QmlDesigner::Storage::ModuleKind; MATCHER(IsSorted, std::string(negation ? "isn't sorted" : "is sorted")) { @@ -125,10 +126,10 @@ protected: resourceManagementMock)}; NiceMock viewMock; QmlDesigner::SourceId filePathId = pathCacheMock.sourceId; - QmlDesigner::TypeId itemTypeId = projectStorageMock.typeId(projectStorageMock.moduleId( - "QtQuick"), - "Item", - QmlDesigner::Storage::Version{}); + QmlDesigner::TypeId itemTypeId = projectStorageMock.typeId( + projectStorageMock.moduleId("QtQuick", ModuleKind::QmlLibrary), + "Item", + QmlDesigner::Storage::Version{}); QmlDesigner::ImportedTypeNameId itemTypeNameId = projectStorageMock.createImportedTypeNameId( filePathId, "Item", itemTypeId); ModelNode rootNode; @@ -759,8 +760,8 @@ TEST_F(Model, change_imports_is_synchronizing_imports_with_project_storage) { QmlDesigner::SourceId directoryPathId = QmlDesigner::SourceId::create(2); ON_CALL(pathCacheMock, sourceId(Eq("/path/foo/."))).WillByDefault(Return(directoryPathId)); - auto qtQuickModuleId = projectStorageMock.moduleId("QtQuick"); - auto qtQmlModelsModuleId = projectStorageMock.moduleId("QtQml.Models"); + auto qtQuickModuleId = projectStorageMock.moduleId("QtQuick", ModuleKind::QmlLibrary); + auto qtQmlModelsModuleId = projectStorageMock.moduleId("QtQml.Models", ModuleKind::QmlLibrary); auto qtQuickImport = QmlDesigner::Import::createLibraryImport("QtQuick", "2.1"); auto qtQmlModelsImport = QmlDesigner::Import::createLibraryImport("QtQml.Models"); auto directoryImport = QmlDesigner::Import::createFileImport("foo"); @@ -793,8 +794,8 @@ TEST_F(Model, change_imports_is_adding_import_in_project_storage) { QmlDesigner::SourceId directoryPathId = QmlDesigner::SourceId::create(2); ON_CALL(pathCacheMock, sourceId(Eq("/path/foo/."))).WillByDefault(Return(directoryPathId)); - auto qtQuickModuleId = projectStorageMock.moduleId("QtQuick"); - auto qtQmlModelsModuleId = projectStorageMock.moduleId("QtQml.Models"); + auto qtQuickModuleId = projectStorageMock.moduleId("QtQuick", ModuleKind::QmlLibrary); + auto qtQmlModelsModuleId = projectStorageMock.moduleId("QtQml.Models", ModuleKind::QmlLibrary); auto qtQuickImport = QmlDesigner::Import::createLibraryImport("QtQuick", "2.1"); auto qtQmlModelsImport = QmlDesigner::Import::createLibraryImport("QtQml.Models"); auto directoryImport = QmlDesigner::Import::createFileImport("foo"); @@ -813,7 +814,7 @@ TEST_F(Model, change_imports_is_removing_import_in_project_storage) { QmlDesigner::SourceId directoryPathId = QmlDesigner::SourceId::create(2); ON_CALL(pathCacheMock, sourceId(Eq("/path/foo/."))).WillByDefault(Return(directoryPathId)); - auto qtQmlModelsModuleId = projectStorageMock.moduleId("QtQml.Models"); + auto qtQmlModelsModuleId = projectStorageMock.moduleId("QtQml.Models", ModuleKind::QmlLibrary); auto qtQuickImport = QmlDesigner::Import::createLibraryImport("QtQuick", "2.1"); auto qtQmlModelsImport = QmlDesigner::Import::createLibraryImport("QtQml.Models"); auto directoryImport = QmlDesigner::Import::createFileImport("foo"); @@ -846,8 +847,8 @@ TEST_F(Model, change_imports_is_changing_import_version_with_project_storage) { QmlDesigner::SourceId directoryPathId = QmlDesigner::SourceId::create(2); ON_CALL(pathCacheMock, sourceId(Eq("/path/foo/."))).WillByDefault(Return(directoryPathId)); - auto qtQuickModuleId = projectStorageMock.moduleId("QtQuick"); - auto qtQmlModelsModuleId = projectStorageMock.moduleId("QtQml.Models"); + auto qtQuickModuleId = projectStorageMock.moduleId("QtQuick", ModuleKind::QmlLibrary); + auto qtQmlModelsModuleId = projectStorageMock.moduleId("QtQml.Models", ModuleKind::QmlLibrary); auto qtQuickImport = QmlDesigner::Import::createLibraryImport("QtQuick", "2.1"); auto qtQmlModelsImport = QmlDesigner::Import::createLibraryImport("QtQml.Models"); auto directoryImport = QmlDesigner::Import::createFileImport("foo"); @@ -873,7 +874,7 @@ TEST_F(Model, create_model_node_has_meta_info) TEST_F(Model, create_qualified_model_node_has_meta_info) { auto qtQmlModelsImport = QmlDesigner::Import::createLibraryImport("QtQml.Models", "", "Foo"); - auto qtQmlModelsModulesId = projectStorageMock.moduleId("QtQml.Models"); + auto qtQmlModelsModulesId = projectStorageMock.moduleId("QtQml.Models", ModuleKind::QmlLibrary); auto importId = projectStorageMock.createImportId(qtQmlModelsModulesId, filePathId); auto listModelTypeId = projectStorageMock.typeId(qtQmlModelsModulesId, "ListModel", @@ -913,23 +914,23 @@ TEST_F(Model, meta_info_of_not_existing_type_is_invalid) TEST_F(Model, module_is_valid) { - auto module = model.module("QML"); + auto module = model.module("QML", ModuleKind::QmlLibrary); ASSERT_THAT(module, IsTrue()); } TEST_F(Model, module_returns_always_the_same) { - auto oldModule = model.module("QML"); + auto oldModule = model.module("QML", ModuleKind::QmlLibrary); - auto module = model.module("QML"); + auto module = model.module("QML", ModuleKind::QmlLibrary); ASSERT_THAT(module, oldModule); } TEST_F(Model, get_meta_info_by_module) { - auto module = model.module("QML"); + auto module = model.module("QML", ModuleKind::QmlLibrary); auto metaInfo = model.metaInfo(module, "QtObject"); @@ -938,7 +939,7 @@ TEST_F(Model, get_meta_info_by_module) TEST_F(Model, get_invalid_meta_info_by_module_for_wrong_name) { - auto module = model.module("QML"); + auto module = model.module("QML", ModuleKind::QmlLibrary); auto metaInfo = model.metaInfo(module, "Object"); @@ -947,7 +948,7 @@ TEST_F(Model, get_invalid_meta_info_by_module_for_wrong_name) TEST_F(Model, get_invalid_meta_info_by_module_for_wrong_module) { - auto module = model.module("Qml"); + auto module = model.module("Qml", ModuleKind::QmlLibrary); auto metaInfo = model.metaInfo(module, "Object"); @@ -986,8 +987,8 @@ TEST_F(Model, refresh_callback_is_calling_abstract_view) TEST_F(Model, meta_infos_for_mdoule) { - projectStorageMock.createModule("Foo"); - auto module = model.module("Foo"); + projectStorageMock.createModule("Foo", ModuleKind::QmlLibrary); + auto module = model.module("Foo", ModuleKind::QmlLibrary); auto typeId = projectStorageMock.createObject(module.id(), "Bar"); ON_CALL(projectStorageMock, typeIds(module.id())) .WillByDefault(Return(QVarLengthArray{typeId})); diff --git a/tests/unit/tests/unittests/model/modelutils-test.cpp b/tests/unit/tests/unittests/model/modelutils-test.cpp index 5a9e63b60da..2d49c6c94e6 100644 --- a/tests/unit/tests/unittests/model/modelutils-test.cpp +++ b/tests/unit/tests/unittests/model/modelutils-test.cpp @@ -13,6 +13,7 @@ namespace { using QmlDesigner::ModelNode; using QmlDesigner::ModelNodes; +using QmlDesigner::Storage::ModuleKind; class ModelUtils : public ::testing::Test { @@ -20,7 +21,7 @@ protected: NiceMock pathCacheMock{"/path/model.qml"}; QmlDesigner::SourceId sourceId = pathCacheMock.createSourceId("/path/foo.qml"); NiceMock projectStorageMock{pathCacheMock.sourceId}; - QmlDesigner::ModuleId moduleId = projectStorageMock.moduleId("QtQuick"); + QmlDesigner::ModuleId moduleId = projectStorageMock.moduleId("QtQuick", ModuleKind::QmlLibrary); QmlDesigner::Model model{{projectStorageMock, pathCacheMock}, "Item", {QmlDesigner::Import::createLibraryImport("QML"), diff --git a/tests/unit/tests/unittests/model/nodelistproperty-test.cpp b/tests/unit/tests/unittests/model/nodelistproperty-test.cpp index aac2e729a29..238e2f5d158 100644 --- a/tests/unit/tests/unittests/model/nodelistproperty-test.cpp +++ b/tests/unit/tests/unittests/model/nodelistproperty-test.cpp @@ -44,11 +44,6 @@ protected: ~NodeListProperty() { model->detachView(&abstractViewMock); } - void setModuleId(Utils::SmallStringView moduleName, ModuleId moduleId) - { - ON_CALL(projectStorageMock, moduleId(Eq(moduleName))).WillByDefault(Return(moduleId)); - } - void setType(ModuleId moduleId, Utils::SmallStringView typeName, Utils::SmallString defaultPeopertyName) diff --git a/tests/unit/tests/unittests/projectstorage/projectstorage-test.cpp b/tests/unit/tests/unittests/projectstorage/projectstorage-test.cpp index 4034ae58f9f..8e0b6919200 100644 --- a/tests/unit/tests/unittests/projectstorage/projectstorage-test.cpp +++ b/tests/unit/tests/unittests/projectstorage/projectstorage-test.cpp @@ -28,6 +28,7 @@ using QmlDesigner::PropertyDeclarationId; using QmlDesigner::SourceContextId; using QmlDesigner::SourceId; using QmlDesigner::SourceIds; +using QmlDesigner::Storage::ModuleKind; using QmlDesigner::Storage::Synchronization::SynchronizationPackage; using QmlDesigner::Storage::Synchronization::TypeAnnotations; using QmlDesigner::Storage::TypeTraits; @@ -48,6 +49,12 @@ Storage::Imports operator+(const Storage::Imports &first, return imports; } +auto IsModule(Utils::SmallStringView name, ModuleKind kind) +{ + return AllOf(Field(&QmlDesigner::Storage::Module::name, name), + Field(&QmlDesigner::Storage::Module::kind, kind)); +} + MATCHER_P2(IsSourceContext, id, value, @@ -1158,15 +1165,15 @@ protected: SourceId sourceIdPath6{sourcePathCache.sourceId(pathPath6)}; SourceId qmlProjectSourceId{sourcePathCache.sourceId("/path1/qmldir")}; SourceId qtQuickProjectSourceId{sourcePathCache.sourceId("/path2/qmldir")}; - ModuleId qmlModuleId{storage.moduleId("Qml")}; - ModuleId qmlNativeModuleId{storage.moduleId("Qml-cppnative")}; - ModuleId qtQuickModuleId{storage.moduleId("QtQuick")}; - ModuleId qtQuickNativeModuleId{storage.moduleId("QtQuick-cppnative")}; - ModuleId pathToModuleId{storage.moduleId("/path/to")}; - ModuleId qtQuick3DModuleId{storage.moduleId("QtQuick3D")}; - ModuleId myModuleModuleId{storage.moduleId("MyModule")}; - ModuleId QMLModuleId{storage.moduleId("QML")}; - ModuleId QMLNativeModuleId{storage.moduleId("QML-cppnative")}; + ModuleId qmlModuleId{storage.moduleId("Qml", ModuleKind::QmlLibrary)}; + ModuleId qmlNativeModuleId{storage.moduleId("Qml", ModuleKind::CppLibrary)}; + ModuleId qtQuickModuleId{storage.moduleId("QtQuick", ModuleKind::QmlLibrary)}; + ModuleId qtQuickNativeModuleId{storage.moduleId("QtQuick", ModuleKind::CppLibrary)}; + ModuleId pathToModuleId{storage.moduleId("/path/to", ModuleKind::PathLibrary)}; + ModuleId qtQuick3DModuleId{storage.moduleId("QtQuick3D", ModuleKind::QmlLibrary)}; + ModuleId myModuleModuleId{storage.moduleId("MyModule", ModuleKind::QmlLibrary)}; + ModuleId QMLModuleId{storage.moduleId("QML", ModuleKind::QmlLibrary)}; + ModuleId QMLNativeModuleId{storage.moduleId("QML", ModuleKind::CppLibrary)}; Storage::Imports importsSourceId1; Storage::Imports importsSourceId2; Storage::Imports importsSourceId3; @@ -2915,7 +2922,7 @@ TEST_F(ProjectStorage, fetch_invalid_type_id_by_impor_ids_and_exported_name_if_n { auto package{createSimpleSynchronizationPackage()}; storage.synchronize(package); - auto qtQuickModuleId = storage.moduleId("QtQuick"); + auto qtQuickModuleId = storage.moduleId("QtQuick", ModuleKind::QmlLibrary); auto typeId = storage.fetchTypeIdByModuleIdsAndExportedName({qtQuickModuleId}, "Object"); @@ -5065,46 +5072,55 @@ TEST_F(ProjectStorage, minimal_updates) TEST_F(ProjectStorage, get_module_id) { - auto id = storage.moduleId("Qml"); + auto id = storage.moduleId("Qml", ModuleKind::QmlLibrary); ASSERT_TRUE(id); } TEST_F(ProjectStorage, get_same_module_id_again) { - auto initialId = storage.moduleId("Qml"); + auto initialId = storage.moduleId("Qml", ModuleKind::QmlLibrary); - auto id = storage.moduleId("Qml"); + auto id = storage.moduleId("Qml", ModuleKind::QmlLibrary); ASSERT_THAT(id, Eq(initialId)); } -TEST_F(ProjectStorage, module_name_throws_if_id_is_invalid) +TEST_F(ProjectStorage, different_module_kind_returns_different_id) { - ASSERT_THROW(storage.moduleName(ModuleId{}), QmlDesigner::ModuleDoesNotExists); + auto qmlId = storage.moduleId("Qml", ModuleKind::QmlLibrary); + + auto cppId = storage.moduleId("Qml", ModuleKind::CppLibrary); + + ASSERT_THAT(cppId, Ne(qmlId)); } -TEST_F(ProjectStorage, module_name_throws_if_id_does_not_exists) +TEST_F(ProjectStorage, module_throws_if_id_is_invalid) { - ASSERT_THROW(storage.moduleName(ModuleId::create(222)), QmlDesigner::ModuleDoesNotExists); + ASSERT_THROW(storage.module(ModuleId{}), QmlDesigner::ModuleDoesNotExists); } -TEST_F(ProjectStorage, get_module_name) +TEST_F(ProjectStorage, module_throws_if_id_does_not_exists) { - auto id = storage.moduleId("Qml"); + ASSERT_THROW(storage.module(ModuleId::create(222)), QmlDesigner::ModuleDoesNotExists); +} - auto name = storage.moduleName(id); +TEST_F(ProjectStorage, get_module) +{ + auto id = storage.moduleId("Qml", ModuleKind::QmlLibrary); - ASSERT_THAT(name, Eq("Qml")); + auto module = storage.module(id); + + ASSERT_THAT(module, IsModule("Qml", ModuleKind::QmlLibrary)); } TEST_F(ProjectStorage, populate_module_cache) { - auto id = storage.moduleId("Qml"); + auto id = storage.moduleId("Qml", ModuleKind::QmlLibrary); QmlDesigner::ProjectStorage newStorage{database, database.isInitialized()}; - ASSERT_THAT(newStorage.moduleName(id), Eq("Qml")); + ASSERT_THAT(newStorage.module(id), IsModule("Qml", ModuleKind::QmlLibrary)); } TEST_F(ProjectStorage, add_project_dataes) @@ -5468,7 +5484,7 @@ TEST_F(ProjectStorage, module_exported_import_with_indirect_different_versions) TEST_F(ProjectStorage, module_exported_import_prevent_collision_if_module_is_indirectly_reexported_multiple_times) { - ModuleId qtQuick4DModuleId{storage.moduleId("QtQuick4D")}; + ModuleId qtQuick4DModuleId{storage.moduleId("QtQuick4D", ModuleKind::QmlLibrary)}; auto package{createModuleExportedImportSynchronizationPackage()}; package.imports.emplace_back(qtQuickModuleId, Storage::Version{1}, sourceId5); package.moduleExportedImports.emplace_back(qtQuick4DModuleId, @@ -5524,8 +5540,8 @@ TEST_F(ProjectStorage, TEST_F(ProjectStorage, distinguish_between_import_kinds) { - ModuleId qml1ModuleId{storage.moduleId("Qml1")}; - ModuleId qml11ModuleId{storage.moduleId("Qml11")}; + ModuleId qml1ModuleId{storage.moduleId("Qml1", ModuleKind::QmlLibrary)}; + ModuleId qml11ModuleId{storage.moduleId("Qml11", ModuleKind::QmlLibrary)}; auto package{createSimpleSynchronizationPackage()}; package.moduleDependencies.emplace_back(qmlModuleId, Storage::Version{}, sourceId1); package.moduleDependencies.emplace_back(qml1ModuleId, Storage::Version{1}, sourceId1); diff --git a/tests/unit/tests/unittests/projectstorage/projectstorageupdater-test.cpp b/tests/unit/tests/unittests/projectstorage/projectstorageupdater-test.cpp index 2d1e7aa61ea..103571822c4 100644 --- a/tests/unit/tests/unittests/projectstorage/projectstorageupdater-test.cpp +++ b/tests/unit/tests/unittests/projectstorage/projectstorageupdater-test.cpp @@ -27,6 +27,7 @@ using QmlDesigner::SourceId; namespace Storage = QmlDesigner::Storage; using QmlDesigner::IdPaths; using QmlDesigner::Storage::Import; +using QmlDesigner::Storage::ModuleKind; using QmlDesigner::Storage::Synchronization::FileType; using QmlDesigner::Storage::Synchronization::IsAutoVersion; using QmlDesigner::Storage::Synchronization::ModuleExportedImport; @@ -180,8 +181,8 @@ public: setQmlFileNames(u"/path", {"First.qml", "First2.qml", "Second.qml"}); - ON_CALL(projectStorageMock, moduleId(_)).WillByDefault([&](const auto &name) { - return storage.moduleId(name); + ON_CALL(projectStorageMock, moduleId(_, _)).WillByDefault([&](const auto &name, const auto &kind) { + return storage.moduleId(name, kind); }); firstType.prototype = Storage::Synchronization::ImportedType{"Object"}; @@ -302,7 +303,10 @@ public: EXPECT_CALL(fileSystemMock, contentAsQString(Eq(path))).WillRepeatedly(Return(content)); } - auto moduleId(Utils::SmallStringView name) const { return storage.moduleId(name); } + auto moduleId(Utils::SmallStringView name, ModuleKind kind) const + { + return storage.moduleId(name, kind); + } protected: NiceMock fileSystemMock; @@ -341,16 +345,16 @@ protected: QmlDesigner::SourcePath{itemLibraryPath + "/."}); SourceId qmlImportsPathSourceId = sourcePathCache.sourceId( QmlDesigner::SourcePath{qmlImportsPath + "/."}); - ModuleId qmlModuleId{storage.moduleId("Qml")}; - ModuleId qmlCppNativeModuleId{storage.moduleId("Qml-cppnative")}; - ModuleId exampleModuleId{storage.moduleId("Example")}; - ModuleId exampleCppNativeModuleId{storage.moduleId("Example-cppnative")}; - ModuleId builtinModuleId{storage.moduleId("QML")}; - ModuleId builtinCppNativeModuleId{storage.moduleId("QML-cppnative")}; - ModuleId quickModuleId{storage.moduleId("Quick")}; - ModuleId quickCppNativeModuleId{storage.moduleId("Quick-cppnative")}; - ModuleId pathModuleId{storage.moduleId("/path")}; - ModuleId subPathQmlModuleId{storage.moduleId("/path/qml")}; + ModuleId qmlModuleId{storage.moduleId("Qml", ModuleKind::QmlLibrary)}; + ModuleId qmlCppNativeModuleId{storage.moduleId("Qml", ModuleKind::CppLibrary)}; + ModuleId exampleModuleId{storage.moduleId("Example", ModuleKind::QmlLibrary)}; + ModuleId exampleCppNativeModuleId{storage.moduleId("Example", ModuleKind::CppLibrary)}; + ModuleId builtinModuleId{storage.moduleId("QML", ModuleKind::QmlLibrary)}; + ModuleId builtinCppNativeModuleId{storage.moduleId("QML", ModuleKind::CppLibrary)}; + ModuleId quickModuleId{storage.moduleId("Quick", ModuleKind::QmlLibrary)}; + ModuleId quickCppNativeModuleId{storage.moduleId("Quick", ModuleKind::CppLibrary)}; + ModuleId pathModuleId{storage.moduleId("/path", ModuleKind::PathLibrary)}; + ModuleId subPathQmlModuleId{storage.moduleId("/path/qml", ModuleKind::PathLibrary)}; Storage::Synchronization::Type objectType{ "QObject", Storage::Synchronization::ImportedType{}, @@ -500,9 +504,9 @@ TEST_F(ProjectStorageUpdater, synchronize_qml_types) imports.push_back(import); }); - EXPECT_CALL(projectStorageMock, moduleId(Eq("Example"))); - EXPECT_CALL(projectStorageMock, moduleId(Eq("Example-cppnative"))); - EXPECT_CALL(projectStorageMock, moduleId(Eq("/path"))); + EXPECT_CALL(projectStorageMock, moduleId(Eq("Example"), ModuleKind::QmlLibrary)); + EXPECT_CALL(projectStorageMock, moduleId(Eq("Example"), ModuleKind::CppLibrary)); + EXPECT_CALL(projectStorageMock, moduleId(Eq("/path"), ModuleKind::PathLibrary)); EXPECT_CALL(projectStorageMock, synchronize( AllOf(Field(&SynchronizationPackage::imports, ElementsAre(import)), @@ -3509,7 +3513,7 @@ TEST_F(ProjectStorageUpdater, update_property_editor_panes) auto directoryId = sourcePathCache.sourceId( QmlDesigner::SourcePath{propertyEditorQmlPath + "/QML/."}); setFilesChanged({directoryId}); - auto qmlModuleId = storage.moduleId("QML"); + auto qmlModuleId = storage.moduleId("QML", ModuleKind::QmlLibrary); EXPECT_CALL(projectStorageMock, synchronize( @@ -3542,8 +3546,8 @@ TEST_F(ProjectStorageUpdater, update_property_editor_specifics) auto controlsDirectoryId = sourcePathCache.sourceId( QmlDesigner::SourcePath{propertyEditorQmlPath + "/QtQuick/Controls/."}); setFilesChanged({qtQuickDirectoryId, controlsDirectoryId}); - auto qtQuickModuleId = storage.moduleId("QtQuick"); - auto controlsModuleId = storage.moduleId("QtQuick.Controls"); + auto qtQuickModuleId = storage.moduleId("QtQuick", ModuleKind::QmlLibrary); + auto controlsModuleId = storage.moduleId("QtQuick.Controls", ModuleKind::QmlLibrary); EXPECT_CALL(projectStorageMock, synchronize(AllOf( @@ -3579,8 +3583,8 @@ TEST_F(ProjectStorageUpdater, update_type_annotations) auto buttonSourceId = sourcePathCache.sourceId( QmlDesigner::SourcePath{itemLibraryPath + "/qtquickcontrols2.metainfo"}); setFilesChanged({itemLibraryPathSourceId, itemSourceId, buttonSourceId}); - auto qtQuickModuleId = moduleId("QtQuick"); - auto qtQuickControlsModuleId = moduleId("QtQuick.Controls.Basic"); + auto qtQuickModuleId = moduleId("QtQuick", ModuleKind::QmlLibrary); + auto qtQuickControlsModuleId = moduleId("QtQuick.Controls.Basic", ModuleKind::QmlLibrary); QmlDesigner::Storage::TypeTraits itemTraits; itemTraits.canBeContainer = QmlDesigner::FlagIs::True; @@ -3616,8 +3620,8 @@ TEST_F(ProjectStorageUpdater, update_changed_type_annotation) QmlDesigner::SourcePath{itemLibraryPath + "/qtquickcontrols2.metainfo"}); setFilesDontChanged({itemLibraryPathSourceId}); setFilesChanged({itemSourceId, buttonSourceId}); - auto qtQuickModuleId = moduleId("QtQuick"); - auto qtQuickControlsModuleId = moduleId("QtQuick.Controls.Basic"); + auto qtQuickModuleId = moduleId("QtQuick", ModuleKind::QmlLibrary); + auto qtQuickControlsModuleId = moduleId("QtQuick.Controls.Basic", ModuleKind::QmlLibrary); QmlDesigner::Storage::TypeTraits itemTraits; itemTraits.canBeContainer = QmlDesigner::FlagIs::True; diff --git a/tests/unit/tests/unittests/projectstorage/qmldocumentparser-test.cpp b/tests/unit/tests/unittests/projectstorage/qmldocumentparser-test.cpp index 513fbf2ec01..bbe42bab1df 100644 --- a/tests/unit/tests/unittests/projectstorage/qmldocumentparser-test.cpp +++ b/tests/unit/tests/unittests/projectstorage/qmldocumentparser-test.cpp @@ -16,6 +16,7 @@ namespace Synchronization = Storage::Synchronization; using QmlDesigner::ModuleId; using QmlDesigner::SourceContextId; using QmlDesigner::SourceId; +using QmlDesigner::Storage::ModuleKind; MATCHER_P(HasPrototype, prototype, std::string(negation ? "isn't " : "is ") + PrintToString(prototype)) { @@ -151,7 +152,7 @@ protected: SourceId qmlFileSourceId{sourcePathCache.sourceId("/path/to/qmlfile.qml")}; SourceContextId qmlFileSourceContextId{sourcePathCache.sourceContextId(qmlFileSourceId)}; Utils::PathString directoryPath{sourcePathCache.sourceContextPath(qmlFileSourceContextId)}; - ModuleId directoryModuleId{storage.moduleId(directoryPath)}; + ModuleId directoryModuleId{storage.moduleId(directoryPath, ModuleKind::PathLibrary)}; }; TEST_F(QmlDocumentParser, prototype) @@ -163,7 +164,7 @@ TEST_F(QmlDocumentParser, prototype) TEST_F(QmlDocumentParser, qualified_prototype) { - auto exampleModuleId = storage.moduleId("Example"); + auto exampleModuleId = storage.moduleId("Example", ModuleKind::QmlLibrary); QString text = R"(import Example 2.1 as Example Example.Item{})"; @@ -187,7 +188,7 @@ TEST_F(QmlDocumentParser, properties) TEST_F(QmlDocumentParser, qualified_properties) { - auto exampleModuleId = storage.moduleId("Example"); + auto exampleModuleId = storage.moduleId("Example", ModuleKind::QmlLibrary); auto type = parser.parse(R"(import Example 2.1 as Example Item{ property Example.Foo foo})", @@ -222,7 +223,7 @@ TEST_F(QmlDocumentParser, enumeration_in_properties) TEST_F(QmlDocumentParser, qualified_enumeration_in_properties) { - auto exampleModuleId = storage.moduleId("Example"); + auto exampleModuleId = storage.moduleId("Example", ModuleKind::QmlLibrary); auto type = parser.parse(R"(import Example 2.1 as Example Item{ property Example.Enumeration.Foo foo})", @@ -242,9 +243,9 @@ TEST_F(QmlDocumentParser, qualified_enumeration_in_properties) TEST_F(QmlDocumentParser, imports) { - ModuleId fooDirectoryModuleId = storage.moduleId("/path/foo"); - ModuleId qmlModuleId = storage.moduleId("QML"); - ModuleId qtQuickModuleId = storage.moduleId("QtQuick"); + ModuleId fooDirectoryModuleId = storage.moduleId("/path/foo", ModuleKind::PathLibrary); + ModuleId qmlModuleId = storage.moduleId("QML", ModuleKind::QmlLibrary); + ModuleId qtQuickModuleId = storage.moduleId("QtQuick", ModuleKind::QmlLibrary); auto type = parser.parse(R"(import QtQuick import "../foo" @@ -263,9 +264,9 @@ TEST_F(QmlDocumentParser, imports) TEST_F(QmlDocumentParser, imports_with_version) { - ModuleId fooDirectoryModuleId = storage.moduleId("/path/foo"); - ModuleId qmlModuleId = storage.moduleId("QML"); - ModuleId qtQuickModuleId = storage.moduleId("QtQuick"); + ModuleId fooDirectoryModuleId = storage.moduleId("/path/foo", ModuleKind::PathLibrary); + ModuleId qmlModuleId = storage.moduleId("QML", ModuleKind::QmlLibrary); + ModuleId qtQuickModuleId = storage.moduleId("QtQuick", ModuleKind::QmlLibrary); auto type = parser.parse(R"(import QtQuick 2.1 import "../foo" @@ -284,8 +285,8 @@ TEST_F(QmlDocumentParser, imports_with_version) TEST_F(QmlDocumentParser, imports_with_explict_directory) { - ModuleId qmlModuleId = storage.moduleId("QML"); - ModuleId qtQuickModuleId = storage.moduleId("QtQuick"); + ModuleId qmlModuleId = storage.moduleId("QML", ModuleKind::QmlLibrary); + ModuleId qtQuickModuleId = storage.moduleId("QtQuick", ModuleKind::QmlLibrary); auto type = parser.parse(R"(import QtQuick import "../to" @@ -358,10 +359,10 @@ TEST_F(QmlDocumentParser, enumeration) TEST_F(QmlDocumentParser, DISABLED_duplicate_imports_are_removed) { - ModuleId fooDirectoryModuleId = storage.moduleId("/path/foo"); - ModuleId qmlModuleId = storage.moduleId("QML"); - ModuleId qtQmlModuleId = storage.moduleId("QtQml"); - ModuleId qtQuickModuleId = storage.moduleId("QtQuick"); + ModuleId fooDirectoryModuleId = storage.moduleId("/path/foo", ModuleKind::PathLibrary); + ModuleId qmlModuleId = storage.moduleId("QML", ModuleKind::QmlLibrary); + ModuleId qtQmlModuleId = storage.moduleId("QtQml", ModuleKind::QmlLibrary); + ModuleId qtQuickModuleId = storage.moduleId("QtQuick", ModuleKind::QmlLibrary); auto type = parser.parse(R"(import QtQuick import "../foo" @@ -497,7 +498,7 @@ TEST_F(QmlDocumentParser, alias_on_list_property) TEST_F(QmlDocumentParser, qualified_list_property) { - auto exampleModuleId = storage.moduleId("Example"); + auto exampleModuleId = storage.moduleId("Example", ModuleKind::QmlLibrary); auto type = parser.parse(R"(import Example 2.1 as Example Item{ property list foos diff --git a/tests/unit/tests/unittests/projectstorage/qmltypesparser-test.cpp b/tests/unit/tests/unittests/projectstorage/qmltypesparser-test.cpp index 64f3631a68f..3acddb12a09 100644 --- a/tests/unit/tests/unittests/projectstorage/qmltypesparser-test.cpp +++ b/tests/unit/tests/unittests/projectstorage/qmltypesparser-test.cpp @@ -17,6 +17,7 @@ namespace Synchronization = QmlDesigner::Storage::Synchronization; using QmlDesigner::ModuleId; using QmlDesigner::SourceContextId; using QmlDesigner::SourceId; +using QmlDesigner::Storage::ModuleKind; MATCHER_P3(IsImport, moduleId, @@ -175,13 +176,12 @@ protected: Storage::Imports imports; Synchronization::Types types; SourceId qmltypesFileSourceId{sourcePathCache.sourceId("path/to/types.qmltypes")}; - ModuleId qtQmlNativeModuleId = storage.moduleId("QtQml-cppnative"); + ModuleId qtQmlNativeModuleId = storage.moduleId("QtQml", ModuleKind::CppLibrary); Synchronization::ProjectData projectData{qmltypesFileSourceId, qmltypesFileSourceId, qtQmlNativeModuleId, Synchronization::FileType::QmlTypes}; SourceContextId qmltypesFileSourceContextId{sourcePathCache.sourceContextId(qmltypesFileSourceId)}; - ModuleId directoryModuleId{storage.moduleId("path/to/")}; }; TEST_F(QmlTypesParser, imports) @@ -194,19 +194,19 @@ TEST_F(QmlTypesParser, imports) parser.parse(source, imports, types, projectData); ASSERT_THAT(imports, - UnorderedElementsAre(IsImport(storage.moduleId("QML-cppnative"), + UnorderedElementsAre(IsImport(storage.moduleId("QML", ModuleKind::CppLibrary), QmlDesigner::Storage::Version{}, qmltypesFileSourceId), - IsImport(storage.moduleId("QtQml-cppnative"), + IsImport(storage.moduleId("QtQml", ModuleKind::CppLibrary), QmlDesigner::Storage::Version{}, qmltypesFileSourceId), - IsImport(storage.moduleId("QtQuick-cppnative"), + IsImport(storage.moduleId("QtQuick", ModuleKind::CppLibrary), QmlDesigner::Storage::Version{}, qmltypesFileSourceId), - IsImport(storage.moduleId("QtQuick.Window-cppnative"), + IsImport(storage.moduleId("QtQuick.Window", ModuleKind::CppLibrary), QmlDesigner::Storage::Version{}, qmltypesFileSourceId), - IsImport(storage.moduleId("QtFoo-cppnative"), + IsImport(storage.moduleId("QtFoo", ModuleKind::CppLibrary), QmlDesigner::Storage::Version{}, qmltypesFileSourceId))); } @@ -286,8 +286,8 @@ TEST_F(QmlTypesParser, exported_types) Component { name: "QObject" exports: ["QML/QtObject 1.0", "QtQml/QtObject 2.1"] }})"}; - ModuleId qmlModuleId = storage.moduleId("QML"); - ModuleId qtQmlModuleId = storage.moduleId("QtQml"); + ModuleId qmlModuleId = storage.moduleId("QML", ModuleKind::QmlLibrary); + ModuleId qtQmlModuleId = storage.moduleId("QtQml", ModuleKind::QmlLibrary); parser.parse(source, imports, types, projectData); @@ -853,7 +853,7 @@ TEST_F(QmlTypesParser, default_property) TEST_F(QmlTypesParser, skip_template_item) { - ModuleId moduleId = storage.moduleId("QtQuick.Templates-cppnative"); + ModuleId moduleId = storage.moduleId("QtQuick.Templates", ModuleKind::CppLibrary); Synchronization::ProjectData projectData{qmltypesFileSourceId, qmltypesFileSourceId, moduleId, diff --git a/tests/unit/tests/unittests/projectstorage/typeannotationreader-test.cpp b/tests/unit/tests/unittests/projectstorage/typeannotationreader-test.cpp index a614a5c7cf2..93c7caa65a7 100644 --- a/tests/unit/tests/unittests/projectstorage/typeannotationreader-test.cpp +++ b/tests/unit/tests/unittests/projectstorage/typeannotationreader-test.cpp @@ -30,7 +30,10 @@ protected: static_database.reset(); } - auto moduleId(Utils::SmallStringView name) const { return storage.moduleId(name); } + auto moduleId(Utils::SmallStringView name) const + { + return storage.moduleId(name, QmlDesigner::Storage::ModuleKind::QmlLibrary); + } protected: inline static std::unique_ptr static_database; From ffe30a313eaf75eba42450a0eb89ee6e4fa7a57f Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Thu, 25 Apr 2024 14:55:19 +0200 Subject: [PATCH 016/154] QmlDesigner: Fix basic label specifics Change-Id: Ifb1fbdde28ab870b1eb9d2fbaa85d75fe8ca2e1a Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Tim Jenssen --- .../QtQuick/Controls/Basic/LabelSpecifics.qml | 27 +++---------------- 1 file changed, 3 insertions(+), 24 deletions(-) diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/LabelSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/LabelSpecifics.qml index b3cb5a46ab7..6c826e46ead 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/LabelSpecifics.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/LabelSpecifics.qml @@ -1,27 +1,6 @@ -// Copyright (C) 2022 The Qt Company Ltd. +// Copyright (C) 2024 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 QtQuick.Layouts 1.15 -import StudioTheme 1.0 as StudioTheme +import ".." as Original -Column { - width: parent.width - - CharacterSection { - showVerticalAlignment: true - } - - TextExtrasSection { - width: parent.width - showWrapMode: true - showFormatProperty: true - } - - FontExtrasSection {} - - PaddingSection {} - - InsetSection {} -} +Original.LabelSpecifics {} From 9b4aa051d4effefc5b1a2cb1e1ed4d7a89ea831d Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Fri, 26 Apr 2024 11:16:18 +0200 Subject: [PATCH 017/154] QmlDesigner: Add double as dynamic property MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Task-number: QDS-10439 Change-Id: I572d2fe943b187bee53a34cc4f832e6220f90d9e Reviewed-by: Henning Gründl --- .../imports/HelperWidgets/DynamicPropertiesSection.qml | 4 +++- .../components/connectioneditor/connectioneditorutils.cpp | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/DynamicPropertiesSection.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/DynamicPropertiesSection.qml index 88a0debae80..e0c70ff946b 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/DynamicPropertiesSection.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/DynamicPropertiesSection.qml @@ -528,6 +528,8 @@ Section { return intEditor if (propertyType == "real") return realEditor + if (propertyType == "double") + return realEditor if (propertyType == "string") return stringEditor if (propertyType == "bool") @@ -708,7 +710,7 @@ Section { StudioControls.ComboBox { id: comboBox actionIndicator.visible: false - model: ["int", "real", "color", "string", "bool", "url", "alias", "signal", + model: ["int", "real", "double", "color", "string", "bool", "url", "alias", "signal", "TextureInput", "vector2d", "vector3d", "vector4d"] width: cePopup.itemWidth } diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectioneditorutils.cpp b/src/plugins/qmldesigner/components/connectioneditor/connectioneditorutils.cpp index 57ca619a70a..3cbfb8c0383 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectioneditorutils.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/connectioneditorutils.cpp @@ -212,7 +212,8 @@ bool isDynamicVariantPropertyType(const TypeName &type) { // "variant" is considered value type as it is initialized as one. // This may need to change if we provide any kind of proper editor for it. - static const QSet valueTypes{"int", "real", "color", "string", "bool", "url", "var", "variant"}; + static const QSet valueTypes{ + "int", "real", "double", "color", "string", "bool", "url", "var", "variant"}; return valueTypes.contains(type); } From 17a28ea850604af6e4cf84d5b3a7198d2d03280f Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Thu, 25 Apr 2024 11:59:01 +0300 Subject: [PATCH 018/154] QmlDesigner: Allow removing a content library material Fixes: QDS-12541 Change-Id: I8efdd5c5f6185961bd8440e06d0adb60ad9d79f2 Reviewed-by: Miikka Heikkinen --- .../ContentLibrary.qml | 12 +++- .../ContentLibraryMaterialContextMenu.qml | 9 +++ .../ContentLibraryUserView.qml | 3 + .../DeleteBundleItemDialog.qml | 65 +++++++++++++++++++ ...ialog.qml => UnimportBundleItemDialog.qml} | 2 +- .../contentlibrary/contentlibrarymaterial.cpp | 5 ++ .../contentlibrary/contentlibrarymaterial.h | 1 + .../contentlibraryusermodel.cpp | 41 +++++++++++- .../contentlibrary/contentlibraryusermodel.h | 6 +- .../contentlibrary/contentlibraryview.cpp | 3 + 10 files changed, 140 insertions(+), 7 deletions(-) create mode 100644 share/qtcreator/qmldesigner/contentLibraryQmlSource/DeleteBundleItemDialog.qml rename share/qtcreator/qmldesigner/contentLibraryQmlSource/{UnimportBundleMaterialDialog.qml => UnimportBundleItemDialog.qml} (96%) diff --git a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibrary.qml b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibrary.qml index 2c98b58adc7..d06d942e3f9 100644 --- a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibrary.qml +++ b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibrary.qml @@ -130,10 +130,14 @@ Item { } } - UnimportBundleMaterialDialog { + UnimportBundleItemDialog { id: confirmUnimportDialog } + DeleteBundleItemDialog { + id: confirmDeleteDialog + } + StackLayout { id: stackLayout width: root.width @@ -246,6 +250,12 @@ Item { confirmUnimportDialog.open() } + onRemoveFromContentLib: (bundleItem) => { + confirmDeleteDialog.targetBundleItem = bundleItem + confirmDeleteDialog.targetBundleLabel = "material" + confirmDeleteDialog.open() + } + onCountChanged: root.responsiveResize(stackLayout.width, stackLayout.height) } } diff --git a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryMaterialContextMenu.qml b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryMaterialContextMenu.qml index b67ec311ef0..f1fa81ed76e 100644 --- a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryMaterialContextMenu.qml +++ b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryMaterialContextMenu.qml @@ -12,12 +12,14 @@ StudioControls.Menu { property var targetMaterial: null property bool hasModelSelection: false property bool importerRunning: false + property bool enableRemove: false // true: adds an option to remove targetMaterial readonly property bool targetAvailable: targetMaterial && !importerRunning signal unimport(); signal addToProject() signal applyToSelected(bool add) + signal removeFromContentLib() function popupMenu(targetMaterial = null) { @@ -56,4 +58,11 @@ StudioControls.Menu { onTriggered: root.unimport() } + + StudioControls.MenuItem { + text: qsTr("Remove from Content Library") + visible: root.enableRemove && root.targetAvailable + height: visible ? implicitHeight : 0 + onTriggered: root.removeFromContentLib() + } } diff --git a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryUserView.qml b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryUserView.qml index d711a0fc310..8fd196dbcde 100644 --- a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryUserView.qml +++ b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryUserView.qml @@ -31,6 +31,7 @@ HelperWidgets.ScrollView { required property var searchBox signal unimport(var bundleItem); + signal removeFromContentLib(var bundleItem); function closeContextMenu() { ctxMenuMaterial.close() @@ -49,6 +50,7 @@ HelperWidgets.ScrollView { ContentLibraryMaterialContextMenu { id: ctxMenuMaterial + enableRemove: true hasModelSelection: ContentLibraryBackend.userModel.hasModelSelection importerRunning: ContentLibraryBackend.userModel.importerRunning @@ -56,6 +58,7 @@ HelperWidgets.ScrollView { onUnimport: root.unimport(ctxMenuMaterial.targetMaterial) onAddToProject: ContentLibraryBackend.userModel.addToProject(ctxMenuMaterial.targetMaterial) + onRemoveFromContentLib: root.removeFromContentLib(ctxMenuMaterial.targetMaterial) } ContentLibraryTextureContextMenu { diff --git a/share/qtcreator/qmldesigner/contentLibraryQmlSource/DeleteBundleItemDialog.qml b/share/qtcreator/qmldesigner/contentLibraryQmlSource/DeleteBundleItemDialog.qml new file mode 100644 index 00000000000..d8f6551ae59 --- /dev/null +++ b/share/qtcreator/qmldesigner/contentLibraryQmlSource/DeleteBundleItemDialog.qml @@ -0,0 +1,65 @@ +// Copyright (C) 2024 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 HelperWidgets +import StudioControls as StudioControls +import StudioTheme as StudioTheme +import ContentLibraryBackend + +StudioControls.Dialog { + id: root + + property var targetBundleItem + property var targetBundleModel + property string targetBundleLabel // "effect" or "material" + + title: qsTr("Remove bundle %1").arg(root.targetBundleLabel) + anchors.centerIn: parent + closePolicy: Popup.CloseOnEscape + implicitWidth: 300 + modal: true + + onOpened: warningText.forceActiveFocus() + + contentItem: Column { + spacing: 20 + width: parent.width + + Text { + id: warningText + + text: qsTr("Are you sure you? The action cannot be undone") + color: StudioTheme.Values.themeTextColor + wrapMode: Text.WordWrap + anchors.right: parent.right + anchors.left: parent.left + leftPadding: 10 + rightPadding: 10 + + Keys.onEnterPressed: btnRemove.onClicked() + Keys.onReturnPressed: btnRemove.onClicked() + } + + Row { + anchors.right: parent.right + Button { + id: btnRemove + + text: qsTr("Remove") + + onClicked: { + ContentLibraryBackend.userModel.removeFromContentLib(root.targetBundleItem) + root.accept() + } + } + + Button { + text: qsTr("Cancel") + onClicked: root.reject() + } + } + } +} diff --git a/share/qtcreator/qmldesigner/contentLibraryQmlSource/UnimportBundleMaterialDialog.qml b/share/qtcreator/qmldesigner/contentLibraryQmlSource/UnimportBundleItemDialog.qml similarity index 96% rename from share/qtcreator/qmldesigner/contentLibraryQmlSource/UnimportBundleMaterialDialog.qml rename to share/qtcreator/qmldesigner/contentLibraryQmlSource/UnimportBundleItemDialog.qml index 4385e3bf82e..305e1018e37 100644 --- a/share/qtcreator/qmldesigner/contentLibraryQmlSource/UnimportBundleMaterialDialog.qml +++ b/share/qtcreator/qmldesigner/contentLibraryQmlSource/UnimportBundleItemDialog.qml @@ -13,8 +13,8 @@ StudioControls.Dialog { id: root property var targetBundleItem - property var targetBundleLabel // "effect" or "material" property var targetBundleModel + property string targetBundleLabel // "effect" or "material" title: qsTr("Bundle %1 might be in use").arg(root.targetBundleLabel) anchors.centerIn: parent diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterial.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterial.cpp index 834bc8aa30c..1d30b0217d8 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterial.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterial.cpp @@ -32,6 +32,11 @@ bool ContentLibraryMaterial::filter(const QString &searchText) return m_visible; } +QString ContentLibraryMaterial::name() const +{ + return m_name; +} + QUrl ContentLibraryMaterial::icon() const { return m_icon; diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterial.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterial.h index 55af2accbd3..a2f53b7e3cf 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterial.h +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterial.h @@ -37,6 +37,7 @@ public: Q_INVOKABLE bool isDownloaded() const; + QString name() const; QUrl icon() const; QString qml() const; TypeName type() const; diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp index 793e3bbe7ad..75ec5c3650f 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp @@ -33,9 +33,6 @@ ContentLibraryUserModel::ContentLibraryUserModel(ContentLibraryWidget *parent) , m_widget(parent) { m_userCategories = {tr("Materials"), tr("Textures")/*, tr("3D"), tr("Effects"), tr("2D components")*/}; // TODO - - loadMaterialBundle(); - loadTextureBundle(); } int ContentLibraryUserModel::rowCount(const QModelIndex &) const @@ -136,6 +133,44 @@ void ContentLibraryUserModel::removeTexture(ContentLibraryTexture *tex) emit dataChanged(index(texSectionIdx), index(texSectionIdx)); } +void ContentLibraryUserModel::removeFromContentLib(ContentLibraryMaterial *mat) +{ + auto bundlePath = Utils::FilePath::fromString(Paths::bundlesPathSetting() + "/User/materials/"); + + QJsonObject matsObj = m_bundleObj.value("materials").toObject(); + + // remove qml and icon files + Utils::FilePath::fromString(mat->qmlFilePath()).removeFile(); + Utils::FilePath::fromUrl(mat->icon()).removeFile(); + + // remove from the bundle json file + matsObj.remove(mat->name()); + m_bundleObj.insert("materials", matsObj); + auto result = bundlePath.pathAppended("user_materials_bundle.json") + .writeFileContents(QJsonDocument(m_bundleObj).toJson()); + if (!result) + qWarning() << __FUNCTION__ << result.error(); + + // delete dependency files if they are only used by the deleted material + QStringList allFiles; + for (const QJsonValueConstRef &mat : std::as_const(matsObj)) + allFiles.append(mat.toObject().value("files").toVariant().toStringList()); + + const QStringList matFiles = mat->files(); + for (const QString &matFile : matFiles) { + if (allFiles.count(matFile) == 0) // only used by the deleted material + bundlePath.pathAppended(matFile).removeFile(); + } + + // remove from model + m_userMaterials.removeOne(mat); + mat->deleteLater(); + + // update model + int matSectionIdx = 0; + emit dataChanged(index(matSectionIdx), index(matSectionIdx)); +} + // returns unique library material's name and qml component QPair ContentLibraryUserModel::getUniqueLibMaterialNameAndQml(const QString &matName) const { diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.h index affa9a89120..1bda49674a8 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.h +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.h @@ -67,12 +67,16 @@ public: void setBundleObj(const QJsonObject &newBundleObj); QJsonObject &bundleJsonObjectRef(); + void loadMaterialBundle(); + void loadTextureBundle(); + Internal::ContentLibraryBundleImporter *bundleImporter() const; Q_INVOKABLE void applyToSelected(QmlDesigner::ContentLibraryMaterial *mat, bool add = false); Q_INVOKABLE void addToProject(QmlDesigner::ContentLibraryMaterial *mat); Q_INVOKABLE void removeFromProject(QmlDesigner::ContentLibraryMaterial *mat); Q_INVOKABLE void removeTexture(QmlDesigner::ContentLibraryTexture *tex); + Q_INVOKABLE void removeFromContentLib(QmlDesigner::ContentLibraryMaterial *mat); signals: void isEmptyChanged(); @@ -96,8 +100,6 @@ signals: void matBundleExistsChanged(); private: - void loadMaterialBundle(); - void loadTextureBundle(); bool isValidIndex(int idx) const; void createImporter(const QString &bundlePath, const QString &bundleId, const QStringList &sharedFiles); diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp index dd8a4d9919e..1e0b69099be 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp @@ -304,6 +304,9 @@ void ContentLibraryView::modelAttached(Model *model) m_widget->effectsModel()->loadBundle(); updateBundleEffectsImportedState(); + + m_widget->userModel()->loadMaterialBundle(); + m_widget->userModel()->loadTextureBundle(); } void ContentLibraryView::modelAboutToBeDetached(Model *model) From ccf0a68c42875f5c5d6bbf4d128e315f48f3e03b Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Thu, 25 Apr 2024 18:36:24 +0300 Subject: [PATCH 019/154] QmlDesigner: Fix bundle material instance not removed after removing a bundle material from project Fixes: QDS-12527 Change-Id: I72b4d99d15a6fbd454d45bdf0ba1a1b469e1a5bb Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Miikka Heikkinen --- .../contentlibrary/contentlibrarymaterialsmodel.cpp | 3 +++ .../contentlibrary/contentlibrarymaterialsmodel.h | 1 + .../components/contentlibrary/contentlibraryview.cpp | 10 +++++++--- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.cpp index 26747d359cd..dd032a6d1fa 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.cpp @@ -40,7 +40,10 @@ ContentLibraryMaterialsModel::ContentLibraryMaterialsModel(ContentLibraryWidget qmlRegisterType("WebFetcher", 1, 0, "FileDownloader"); qmlRegisterType("WebFetcher", 1, 0, "MultiFileDownloader"); +} +void ContentLibraryMaterialsModel::loadBundle() +{ QDir bundleDir{m_downloadPath}; if (fetchBundleMetadata(bundleDir) && fetchBundleIcons(bundleDir)) loadMaterialBundle(bundleDir); diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.h index 21bd3741375..a2c2e90ce1e 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.h +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.h @@ -51,6 +51,7 @@ public: void resetModel(); void updateIsEmpty(); + void loadBundle(); Internal::ContentLibraryBundleImporter *bundleImporter() const; diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp index 1e0b69099be..9cbc0f73393 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp @@ -290,7 +290,6 @@ void ContentLibraryView::modelAttached(Model *model) m_hasQuick3DImport = model->hasImport("QtQuick3D"); updateBundlesQuick3DVersion(); - updateBundleMaterialsImportedState(); const bool hasLibrary = Utils3D::materialLibraryNode(this).isValid(); m_widget->setHasMaterialLibrary(hasLibrary); @@ -302,11 +301,16 @@ void ContentLibraryView::modelAttached(Model *model) m_widget->setHasActive3DScene(m_sceneId != -1); m_widget->clearSearchFilter(); + // bundles loading has to happen here, otherwise project path is not ready which will + // cause bundle items types to resolve incorrectly + m_widget->materialsModel()->loadBundle(); m_widget->effectsModel()->loadBundle(); - updateBundleEffectsImportedState(); - m_widget->userModel()->loadMaterialBundle(); m_widget->userModel()->loadTextureBundle(); + + updateBundleMaterialsImportedState(); + updateBundleEffectsImportedState(); + updateBundleUserMaterialsImportedState(); } void ContentLibraryView::modelAboutToBeDetached(Model *model) From 7df037cba3fc142f9e5032b14190c85e2432ef3c Mon Sep 17 00:00:00 2001 From: Henning Gruendl Date: Fri, 26 Apr 2024 12:33:25 +0200 Subject: [PATCH 020/154] QmlDesigner: Fix alignment in EffectsSection Change-Id: I55fb53233c1993e1897c010d37fad4bf0c12d326 Reviewed-by: Thomas Hartmann --- .../propertyEditorQmlSources/QtQuick/EffectsSection.qml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/EffectsSection.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/EffectsSection.qml index 2b7543f1d99..fca041fc19e 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/EffectsSection.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/EffectsSection.qml @@ -33,7 +33,7 @@ Section { if (!root.hasDesignerEffect) return - root.model = modelNodeBackend.allChildren(effect[0]) //ids for all effects + root.model = modelNodeBackend.allChildren(effect[0]) // ids for all effects } leftPadding: 0 @@ -50,6 +50,8 @@ Section { } SectionLayout { + x: StudioTheme.Values.sectionLeftPadding + PropertyLabel {} SecondColumnLayout { @@ -483,6 +485,7 @@ Section { } SectionLayout { + x: StudioTheme.Values.sectionLeftPadding visible: root.hasDesignerEffect PropertyLabel {} @@ -504,6 +507,8 @@ Section { root.invalidate() } } + + ExpandingSpacer {} } } } From d69e719e2cf5a680255b004eabd6b8a03f0c37df Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Fri, 26 Apr 2024 13:01:47 +0200 Subject: [PATCH 021/154] QmlDesigner: Add FrameAnimation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Task-number: QDS-12130 Change-Id: I0abf9b7c1dea37624362a2480c09c873a87b3e69 Reviewed-by: Henning Gründl --- .../images/frame-animation-16px.png | Bin 0 -> 319 bytes .../images/frame-animation-24px.png | Bin 0 -> 403 bytes .../images/frame-animation-24px@2x.png | Bin 0 -> 496 bytes .../qtquickplugin/qtquickplugin.qrc | 3 +++ .../qmldesigner/qtquickplugin/quick.metainfo | 20 ++++++++++++++++++ 5 files changed, 23 insertions(+) create mode 100644 src/plugins/qmldesigner/qtquickplugin/images/frame-animation-16px.png create mode 100644 src/plugins/qmldesigner/qtquickplugin/images/frame-animation-24px.png create mode 100644 src/plugins/qmldesigner/qtquickplugin/images/frame-animation-24px@2x.png diff --git a/src/plugins/qmldesigner/qtquickplugin/images/frame-animation-16px.png b/src/plugins/qmldesigner/qtquickplugin/images/frame-animation-16px.png new file mode 100644 index 0000000000000000000000000000000000000000..5171fdd79d96289323629f598dcda3845f96db03 GIT binary patch literal 319 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd4mJh`2Kmqb6B!s7**skwLo9mdUefhu2^48~ z=sq{$0&C*`hO0?8SNLwo5Mk>Mb?K2hlN306L&62omd4y!EZy>f2mkWBHS3ub8or5? zXp5Zl^+3V%ImP$C-{CIz(c7d`eRPkY5@&&w@W-`}Rx|#Bu@054bkDeX9xcHrl~!a2`Y`%HN9@BD@y&n1D?{kJrialLU04CsHXzp#C3L;1!y zqdx+3mjtYNtf#_$L%&gaqu#R2Ux6Y#D*V6v%hoN~-_BRZ7QW-S-^pW%7ricTvG%BA Zlr%Htm*kndnt_3V!PC{xWt~$(69DF8XdXVJd^m5}uydO_9-@&+@Cz zp5c@FBXioDX+aWGxyy3SWek!VGSsUWyjJcNDgWF2g~??}vS~?2_|fSyz6Lr=>KS*i z&-=$UftmY&O~KFG4KbRb%zcf#3EFIz_$O#D%~C4jkYRX!AdTk;yTP+(ZyApzDe5iA zoM}F17Wd9B#w)HV$CKYVg*CpFIAB%4c!zCXCEL4(n-T{kH!N{9pZAY_*YiIttoxg* zXH9!~ZX4H{mla2o>v+$w)a>u8y5F1Tyvk~x^dXrAyft5#e<%dAo>Bj~kN36L%Ig9L z+TL8gTO>S#?~DDfNs0}PTiCxyJUy_x@2r)8L66^=h@4uc**&3!RlFZF7#J8BJYD@< J);T3K0RXX4tLp#& literal 0 HcmV?d00001 diff --git a/src/plugins/qmldesigner/qtquickplugin/images/frame-animation-24px@2x.png b/src/plugins/qmldesigner/qtquickplugin/images/frame-animation-24px@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..170c113bbd1fd0b6c1b6bdad5de8176b7ad7d921 GIT binary patch literal 496 zcmeAS@N?(olHy`uVBq!ia0y~yU@!n-4rT@hhJ-tuTNxM_@&kNATp1V`R?ra~^Ss>0 zz`&qV666=mz{ttN%P$}#ucD(HosgcHos(NoQP(kL>YjZk&pmwd{Kf0ve-Co53ua(o zw<5il#5avg=j#^BH^cL{2z)>pV%7eX)B=lHh5(%qVtUy>s&- zGF6Z5Ty6fqa%%cYrB}ABUX4{kdya^GWh?k=ANT)g=Eftnl^4_IJ0E)Ww7i(B!(`HK wl~?zV7cx$Ke@*=&>xIjV23vO4i`y{ddOf?`xsaKgfq{X+)78&qol`;+0OB;~kN^Mx literal 0 HcmV?d00001 diff --git a/src/plugins/qmldesigner/qtquickplugin/qtquickplugin.qrc b/src/plugins/qmldesigner/qtquickplugin/qtquickplugin.qrc index 34838797d49..c2a1ff59295 100644 --- a/src/plugins/qmldesigner/qtquickplugin/qtquickplugin.qrc +++ b/src/plugins/qmldesigner/qtquickplugin/qtquickplugin.qrc @@ -127,5 +127,8 @@ images/extended-view3d-16px.png images/extended-view3d-24px.png images/extended-view3d-24px@2x.png + images/frame-animation-16px.png + images/frame-animation-24px.png + images/frame-animation-24px@2x.png diff --git a/src/plugins/qmldesigner/qtquickplugin/quick.metainfo b/src/plugins/qmldesigner/qtquickplugin/quick.metainfo index 46cb42b60d2..63d390dded1 100644 --- a/src/plugins/qmldesigner/qtquickplugin/quick.metainfo +++ b/src/plugins/qmldesigner/qtquickplugin/quick.metainfo @@ -548,6 +548,26 @@ MetaInfo { } } + Type { + name: "QtQuick.FrameAnimation" + icon: ":/qtquickplugin/images/frame-animation-16px.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeContainer: false + } + + ItemLibraryEntry { + name: "Frame Animation" + category: "d.Qt Quick - Animation" + libraryIcon: ":/qtquickplugin/images/frame-animation-24px.png" + version: "2.0" + toolTip: qsTr("Triggers a handler at every animation frame update.") + } + } + Type { name: "QtQml.Timer" icon: ":/qtquickplugin/images/timer-16px.png" From 97b646e30a7c580e40a85a8300e20b77193cdd51 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Fri, 26 Apr 2024 14:44:47 +0300 Subject: [PATCH 022/154] QmlDesigner: Fix typo in approachObject Fixes: QDS-12616 Change-Id: Ib73c10dfc2806df2f98083019d67a78a6bb44a0f Reviewed-by: Mahmoud Badri --- src/tools/qml2puppet/mockfiles/qt6/EditCameraController.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/qml2puppet/mockfiles/qt6/EditCameraController.qml b/src/tools/qml2puppet/mockfiles/qt6/EditCameraController.qml index 509273901a4..f840b5044a5 100644 --- a/src/tools/qml2puppet/mockfiles/qt6/EditCameraController.qml +++ b/src/tools/qml2puppet/mockfiles/qt6/EditCameraController.qml @@ -113,7 +113,7 @@ Item { if (resolvedResult) { var newLookAtAndZoom = _generalHelper.approachNode(camera, _defaultCameraLookAtDistance, - resolvedResult, view3D); + resolvedResult, view3d); _lookAtPoint = newLookAtAndZoom.toVector3d(); _zoomFactor = newLookAtAndZoom.w; storeCameraState(0); From 1cf6e7844a1836e649b5dcac54cb4d847882a812 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Fri, 26 Apr 2024 12:33:39 +0300 Subject: [PATCH 023/154] QmlDesigner: Change wheel to adjust camera speed in fly mode in 3D view Mouse wheel no longer zooms 3D view when done in fly mode. Instead, it adjusts the camera speed value. Fixes: QDS-12291 Change-Id: I0bb5960d67cb25d545d3ac2ce2567c75f057ea72 Reviewed-by: Mahmoud Badri Reviewed-by: Qt CI Patch Build Bot --- .../components/edit3d/edit3dcanvas.cpp | 11 +++- .../mockfiles/qt6/EditCameraController.qml | 16 +++--- .../qml2puppet/mockfiles/qt6/EditView3D.qml | 53 +++++++++++++++++-- .../qml2puppet/editor3d/generalhelper.cpp | 35 ++++++++++-- .../qml2puppet/editor3d/generalhelper.h | 5 ++ 5 files changed, 103 insertions(+), 17 deletions(-) diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dcanvas.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dcanvas.cpp index ef2e2ee7b56..4f4f08f1e18 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dcanvas.cpp +++ b/src/plugins/qmldesigner/components/edit3d/edit3dcanvas.cpp @@ -207,7 +207,16 @@ void Edit3DCanvas::mouseMoveEvent(QMouseEvent *e) void Edit3DCanvas::wheelEvent(QWheelEvent *e) { - m_parent->view()->sendInputEvent(e); + if (m_flyMode) { + // In fly mode, wheel controls the camera speed slider (value range 1-100) + double speed; + double mult; + m_parent->view()->getCameraSpeedAuxData(speed, mult); + speed = qMin(100., qMax(1., speed + double(e->angleDelta().y()) / 40.)); + m_parent->view()->setCameraSpeedAuxData(speed, mult); + } else { + m_parent->view()->sendInputEvent(e); + } QWidget::wheelEvent(e); } diff --git a/src/tools/qml2puppet/mockfiles/qt6/EditCameraController.qml b/src/tools/qml2puppet/mockfiles/qt6/EditCameraController.qml index f840b5044a5..bbc50a18e56 100644 --- a/src/tools/qml2puppet/mockfiles/qt6/EditCameraController.qml +++ b/src/tools/qml2puppet/mockfiles/qt6/EditCameraController.qml @@ -26,10 +26,9 @@ Item { readonly property vector3d _defaultCameraPosition: Qt.vector3d(0, 600, 600) readonly property vector3d _defaultCameraRotation: Qt.vector3d(-45, 0, 0) readonly property real _defaultCameraLookAtDistance: _defaultCameraPosition.length() - readonly property real _keyPanAmount: _generalHelper.cameraSpeed + readonly property real _keyPanAmount: 1.0 property bool ignoreToolState: false property bool flyMode: viewRoot.flyMode - property real _cameraSpeedModifier: 1 z: 10 anchors.fill: parent @@ -245,15 +244,16 @@ Item { cameraCtrl.storeCameraState(0); } _generalHelper.stopAllCameraMoves() - cameraCtrl._cameraSpeedModifier = 1 + _generalHelper.setCameraSpeedModifier(1.0); } Connections { target: _generalHelper enabled: viewRoot.activeSplit === cameraCtrl.splitId + function onRequestCameraMove(camera, moveVec) { if (camera === cameraCtrl.camera) { - cameraCtrl.moveCamera(moveVec.times(_cameraSpeedModifier)); + cameraCtrl.moveCamera(moveVec); _generalHelper.requestRender(); } } @@ -319,7 +319,7 @@ Item { onWheel: (wheel) => { if (cameraCtrl.flyMode && cameraCtrl.splitId !== viewRoot.activeSplit) return; - viewRoot.activeSplit = cameraCtrl.splitId + viewRoot.activeSplit = cameraCtrl.splitId; if (cameraCtrl.camera) { // Empirically determined divisor for nice zoom cameraCtrl.zoomRelative(wheel.angleDelta.y / -40); @@ -330,11 +330,11 @@ Item { function setCameraSpeed(event) { if (event.modifiers === Qt.AltModifier) - cameraCtrl._cameraSpeedModifier = 0.5 + _generalHelper.setCameraSpeedModifier(0.5); else if (event.modifiers === Qt.ShiftModifier) - cameraCtrl._cameraSpeedModifier = 2 + _generalHelper.setCameraSpeedModifier(2.0); else - cameraCtrl._cameraSpeedModifier = 1 + _generalHelper.setCameraSpeedModifier(1.0); } Keys.onPressed: (event) => { diff --git a/src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml b/src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml index 23fc1c6a78e..e3448ff77cc 100644 --- a/src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml +++ b/src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml @@ -38,6 +38,7 @@ Item { property bool syncEnvBackground: false property bool splitView: false property bool flyMode: false + property bool showCameraSpeed: false enum SelectionMode { Item, Group } enum TransformMode { Move, Rotate, Scale } @@ -353,10 +354,13 @@ Item { cameraControls[i].restoreDefaultState(); } - if ("flyMode" in toolStates) + if ("flyMode" in toolStates) { flyMode = toolStates.flyMode; - else if (resetToDefault) + viewRoot.showCameraSpeed = false; + } else if (resetToDefault) { flyMode = false; + viewRoot.showCameraSpeed = false; + } if ("splitView" in toolStates) splitView = toolStates.splitView; @@ -638,7 +642,6 @@ Item { { for (var i = 0; i < 4; ++i) overlayViews[i].handleHiddenStateChange(node); - } function onUpdateDragTooltip() @@ -651,6 +654,18 @@ Item { { updateEnvBackground(); } + + function onCameraSpeedChanged() { + _generalHelper.requestTimerEvent("hideSpeed", 1000); + viewRoot.showCameraSpeed = true + } + + function onRequestedTimerEvent(timerId) { + if (timerId === "hideSpeed") { + viewRoot.showCameraSpeed = false; + _generalHelper.requestRender(); + } + } } // Shared nodes of the overlay, set as importScene on all overlay views. @@ -1058,6 +1073,38 @@ Item { color: "white" visible: viewRoot.fps > 0 } + + Rectangle { + id: cameraSpeedLabel + width: 120 + height: 65 + anchors.centerIn: parent + opacity: 0.6 + radius: 10 + color: "white" + visible: flyMode && viewRoot.showCameraSpeed + enabled: false + + Column { + anchors.fill: parent + anchors.margins: 8 + spacing: 2 + Text { + width: parent.width + horizontalAlignment: Text.AlignHCenter + text: "Camera Speed" + font.pixelSize: 16 + } + Text { + width: parent.width + height: 20 + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + font.pixelSize: 20 + text: _generalHelper.cameraSpeed.toLocaleString(Qt.locale(), 'f', 1) + } + } + } } Keys.onPressed: (event) => { diff --git a/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp b/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp index dadd8176330..00f96f51949 100644 --- a/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp +++ b/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp @@ -163,16 +163,17 @@ QVector3D GeneralHelper::moveCamera(QQuick3DCamera *camera, const QVector3D &sta if (moveVector.length() < 0.001f) return startLookAt; + QVector3D speedVector = moveVector * m_cameraSpeed * m_cameraSpeedModifier; + QMatrix4x4 m = camera->sceneTransform(); // Works because edit camera is at scene root const float *dataPtr(m.data()); const QVector3D xAxis = QVector3D(dataPtr[0], dataPtr[1], dataPtr[2]).normalized(); const QVector3D yAxis = QVector3D(dataPtr[4], dataPtr[5], dataPtr[6]).normalized(); const QVector3D zAxis = QVector3D(dataPtr[8], dataPtr[9], dataPtr[10]).normalized(); - const QVector3D xDelta = xAxis * moveVector.x(); - const QVector3D yDelta = yAxis * moveVector.y(); - const QVector3D zDelta = zAxis * moveVector.z(); - // Delta multiplier for nice default speed in default scene - const QVector3D delta = (yDelta - xDelta - zDelta) * .5f; + const QVector3D xDelta = xAxis * speedVector.x(); + const QVector3D yDelta = yAxis * speedVector.y(); + const QVector3D zDelta = zAxis * speedVector.z(); + const QVector3D delta = (yDelta - xDelta - zDelta); camera->setPosition(camera->position() + delta); @@ -1187,6 +1188,11 @@ void GeneralHelper::setCameraSpeed(double speed) } } +void GeneralHelper::setCameraSpeedModifier(double modifier) +{ + m_cameraSpeedModifier = modifier; +} + QString GeneralHelper::formatVectorDragTooltip(const QVector3D &vec, const QString &suffix) const { return QObject::tr("x:%L1 y:%L2 z:%L3%L4") @@ -1414,6 +1420,25 @@ bool GeneralHelper::compareQuaternions(const QQuaternion &q1, const QQuaternion && qFuzzyCompare(q1.z(), q2.z()) && qFuzzyCompare(q1.scalar(), q2.scalar()); } +void GeneralHelper::requestTimerEvent(const QString &timerId, qint64 delay) +{ + if (m_eventTimers.contains(timerId)) { + m_eventTimers[timerId]->start(delay); + } else { + auto timer = new QTimer; + timer->setInterval(delay); + timer->setSingleShot(true); + connect(timer, &QTimer::timeout, this, [this, timerId]() { + if (m_eventTimers.contains(timerId)) { + QTimer *timer = m_eventTimers.take(timerId); + timer->deleteLater(); + } + emit requestedTimerEvent(timerId); + }); + m_eventTimers[timerId] = timer; + } +} + } } diff --git a/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.h b/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.h index 558e0363df2..10ed28aa721 100644 --- a/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.h +++ b/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.h @@ -139,6 +139,7 @@ public: void setSnapRotationInterval(double interval) { m_snapRotationInterval = interval; } void setSnapScaleInterval(double interval) { m_snapScaleInterval = interval / 100.; } void setCameraSpeed(double speed); + Q_INVOKABLE void setCameraSpeedModifier(double modifier); Q_INVOKABLE QString snapPositionDragTooltip(const QVector3D &pos) const; Q_INVOKABLE QString snapRotationDragTooltip(double angle) const; @@ -154,6 +155,8 @@ public: Q_INVOKABLE bool compareVectors(const QVector3D &v1, const QVector3D &v2) const; Q_INVOKABLE bool compareQuaternions(const QQuaternion &q1, const QQuaternion &q2) const; + Q_INVOKABLE void requestTimerEvent(const QString &timerId, qint64 delay); + signals: void overlayUpdateNeeded(); void toolStateChanged(const QString &sceneId, const QString &tool, const QVariant &toolState); @@ -167,6 +170,7 @@ signals: void requestCameraMove(QQuick3DCamera *camera, const QVector3D &moveVector); void requestRender(); void cameraSpeedChanged(); + void requestedTimerEvent(const QString &timerId); private: void handlePendingToolStateUpdate(); @@ -224,6 +228,7 @@ private: double m_cameraSpeedModifier = 1.; QVariant m_bgColor; + QHash m_eventTimers; }; } From 7ba7624bb6c778498f66963c4a210458a66d1df8 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Fri, 26 Apr 2024 16:00:07 +0300 Subject: [PATCH 024/154] QmlDesigner: Restore auxiliary properties after rewriter model attach Successfully restoring auxiliary properties requires scene model to be complete, so we must wait until model is attached. Fixes: QDS-12615 Change-Id: I906e9b50df14fcdbc0cad8370c5466db5c1aab02 Reviewed-by: Thomas Hartmann --- src/plugins/qmldesigner/designercore/model/rewriterview.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/plugins/qmldesigner/designercore/model/rewriterview.cpp b/src/plugins/qmldesigner/designercore/model/rewriterview.cpp index 2dcc859001a..81c699ce43a 100644 --- a/src/plugins/qmldesigner/designercore/model/rewriterview.cpp +++ b/src/plugins/qmldesigner/designercore/model/rewriterview.cpp @@ -106,6 +106,7 @@ void RewriterView::modelAttached(Model *model) m_modelAttachPending = true; QTimer::singleShot(1000, this, [this, model](){ modelAttached(model); + restoreAuxiliaryData(); }); } } From b0b1542814441096f4e8daaf02522d67c7354d62 Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Fri, 26 Apr 2024 14:49:05 +0300 Subject: [PATCH 025/154] QmlDesigner: Use QJsonValueConstRef instead of auto This used to not build on linux. Doesnt seem the case anymore. Change-Id: Ia58d388bfe4f5cc794b29f8e25a9a8d6b3f229c3 Reviewed-by: Miikka Heikkinen Reviewed-by: Qt CI Patch Build Bot --- src/plugins/effectcomposer/compositionnode.cpp | 2 +- .../contentlibrary/contentlibraryeffectsmodel.cpp | 4 ++-- .../contentlibrary/contentlibrarymaterialsmodel.cpp | 4 ++-- .../components/contentlibrary/contentlibraryusermodel.cpp | 4 ++-- .../components/materialbrowser/materialbrowsermodel.cpp | 8 +++----- 5 files changed, 10 insertions(+), 12 deletions(-) diff --git a/src/plugins/effectcomposer/compositionnode.cpp b/src/plugins/effectcomposer/compositionnode.cpp index d939e2283af..a69cd00e075 100644 --- a/src/plugins/effectcomposer/compositionnode.cpp +++ b/src/plugins/effectcomposer/compositionnode.cpp @@ -128,7 +128,7 @@ void CompositionNode::parse(const QString &effectName, const QString &qenPath, c // parse properties QJsonArray jsonProps = json.value("properties").toArray(); - for (const auto /*QJsonValueRef*/ &prop : jsonProps) { + for (const QJsonValueConstRef &prop : jsonProps) { const auto uniform = new Uniform(effectName, prop.toObject(), qenPath); m_unifomrsModel.addUniform(uniform); m_uniforms.append(uniform); diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectsmodel.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectsmodel.cpp index 334c017116c..157290c2d53 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectsmodel.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectsmodel.cpp @@ -182,7 +182,7 @@ void ContentLibraryEffectsModel::loadBundle() QStringList files; const QJsonArray assetsArr = itemObj.value("files").toArray(); - for (const auto /*QJson{Const,}ValueRef*/ &asset : assetsArr) + for (const QJsonValueConstRef &asset : assetsArr) files.append(asset.toString()); QUrl icon = QUrl::fromLocalFile(bundleDir.filePath(itemObj.value("icon").toString())); @@ -202,7 +202,7 @@ void ContentLibraryEffectsModel::loadBundle() QStringList sharedFiles; const QJsonArray sharedFilesArr = m_bundleObj.value("sharedFiles").toArray(); - for (const auto /*QJson{Const,}ValueRef*/ &file : sharedFilesArr) + for (const QJsonValueConstRef &file : sharedFilesArr) sharedFiles.append(file.toString()); createImporter(bundleDir.path(), bundleId, sharedFiles); diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.cpp index dd032a6d1fa..a2aef7b47f9 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.cpp @@ -284,7 +284,7 @@ void ContentLibraryMaterialsModel::loadMaterialBundle(const QDir &matBundleDir) QStringList files; const QJsonArray assetsArr = matObj.value("files").toArray(); - for (const auto /*QJson{Const,}ValueRef*/ &asset : assetsArr) + for (const QJsonValueConstRef &asset : assetsArr) files.append(asset.toString()); QUrl icon = QUrl::fromLocalFile(matBundleDir.filePath(matObj.value("icon").toString())); @@ -305,7 +305,7 @@ void ContentLibraryMaterialsModel::loadMaterialBundle(const QDir &matBundleDir) QStringList sharedFiles; const QJsonArray sharedFilesArr = m_matBundleObj.value("sharedFiles").toArray(); - for (const auto /*QJson{Const,}ValueRef*/ &file : sharedFilesArr) + for (const QJsonValueConstRef &file : sharedFilesArr) sharedFiles.append(file.toString()); QStringList missingSharedFiles; diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp index 75ec5c3650f..34cbadfb8ba 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp @@ -311,7 +311,7 @@ void ContentLibraryUserModel::loadMaterialBundle() QStringList files; const QJsonArray assetsArr = matObj.value("files").toArray(); - for (const auto /*QJson{Const,}ValueRef*/ &asset : assetsArr) + for (const QJsonValueConstRef &asset : assetsArr) files.append(asset.toString()); QUrl icon = QUrl::fromLocalFile(bundleDir.filePath(matObj.value("icon").toString())); @@ -327,7 +327,7 @@ void ContentLibraryUserModel::loadMaterialBundle() QStringList sharedFiles; const QJsonArray sharedFilesArr = m_bundleObj.value("sharedFiles").toArray(); - for (const auto /*QJson{Const,}ValueRef*/ &file : sharedFilesArr) + for (const QJsonValueConstRef &file : sharedFilesArr) sharedFiles.append(file.toString()); createImporter(bundleDir.path(), m_bundleId, sharedFiles); diff --git a/src/plugins/qmldesigner/components/materialbrowser/materialbrowsermodel.cpp b/src/plugins/qmldesigner/components/materialbrowser/materialbrowsermodel.cpp index d36e78512bb..2fcd56b6697 100644 --- a/src/plugins/qmldesigner/components/materialbrowser/materialbrowsermodel.cpp +++ b/src/plugins/qmldesigner/components/materialbrowser/materialbrowsermodel.cpp @@ -434,22 +434,20 @@ void MaterialBrowserModel::copyMaterialProperties(int idx, const QString §io QJsonObject propsSpecObj = m_propertyGroupsObj.value(m_copiedMaterialType).toObject(); if (propsSpecObj.contains(section)) { // should always be true const QJsonArray propNames = propsSpecObj.value(section).toArray(); - // auto == QJsonValueConstRef after 04dc959d49e5e3 / Qt 6.4, QJsonValueRef before - for (const auto &propName : propNames) + for (const QJsonValueConstRef &propName : propNames) copiedProps.append(propName.toString().toLatin1()); if (section == "Base") { // add QtQuick3D.Material base props as well QJsonObject propsMatObj = m_propertyGroupsObj.value("Material").toObject(); const QJsonArray propNames = propsMatObj.value("Base").toArray(); - // auto == QJsonValueConstRef after 04dc959d49e5e3 / Qt 6.4, QJsonValueRef before - for (const auto &propName : propNames) + for (const QJsonValueConstRef &propName : propNames) copiedProps.append(propName.toString().toLatin1()); } } } m_copiedMaterialProps.clear(); - for (const auto &propName : copiedProps) { + for (const PropertyName &propName : copiedProps) { PropertyCopyData data; data.name = propName; data.isValid = m_allPropsCopied || validProps.contains(propName); From 1f24a2fa4a42a9a1761f7a47610411c2955ba068 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Fri, 26 Apr 2024 15:17:37 +0200 Subject: [PATCH 026/154] QmlDesigner: Allow using selected node in QmlModelNodeProxy::moveNode() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I4c08a3fbfe750a8a9673828acfe542f9b8365e66 Reviewed-by: Henning Gründl Reviewed-by: Qt CI Patch Build Bot --- .../propertyeditor/qmlmodelnodeproxy.cpp | 26 ++++++++++--------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/src/plugins/qmldesigner/components/propertyeditor/qmlmodelnodeproxy.cpp b/src/plugins/qmldesigner/components/propertyeditor/qmlmodelnodeproxy.cpp index fc39b371375..1cff0e55e68 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/qmlmodelnodeproxy.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/qmlmodelnodeproxy.cpp @@ -161,13 +161,12 @@ void QmlModelNodeProxy::createModelNode(int internalIdParent, const QString &typeName, const QString &requiredImport) { - QTC_ASSERT(m_qmlObjectNode.isValid(), return ); - - auto modelNode = m_qmlObjectNode.modelNode(); - - AbstractView *view = modelNode.view(); - auto parentModelNode = m_qmlObjectNode.modelNode(); + + QTC_ASSERT(parentModelNode.isValid(), return ); + + AbstractView *view = parentModelNode.view(); + if (internalIdParent >= 0) parentModelNode = view->modelNodeForInternalId(internalIdParent); @@ -186,7 +185,7 @@ void QmlModelNodeProxy::createModelNode(int internalIdParent, #ifdef QDS_USE_PROJECTSTORAGE ModelNode newNode = view->createModelNode(typeName.toUtf8()); #else - NodeMetaInfo metaInfo = modelNode.model()->metaInfo(typeName.toUtf8()); + NodeMetaInfo metaInfo = parentModelNode.model()->metaInfo(typeName.toUtf8()); ModelNode newNode = view->createModelNode(metaInfo.typeName(), metaInfo.majorVersion(), metaInfo.minorVersion()); @@ -200,14 +199,17 @@ void QmlModelNodeProxy::moveNode(int internalIdParent, int fromIndex, int toIndex) { - QTC_ASSERT(m_qmlObjectNode.isValid(), return ); + ModelNode modelNode = m_qmlObjectNode.modelNode(); - ModelNode node = m_qmlObjectNode.view()->modelNodeForInternalId(internalIdParent); + QTC_ASSERT(modelNode.isValid(), return ); - QTC_ASSERT(node.isValid(), return ); + if (internalIdParent >= 0) + modelNode = m_qmlObjectNode.view()->modelNodeForInternalId(internalIdParent); + + QTC_ASSERT(modelNode.isValid(), return ); AbstractView *view = m_qmlObjectNode.view(); - view->executeInTransaction("QmlModelNodeProxy::swapNode", [&] { - node.nodeListProperty(propertyName.toUtf8()).slide(fromIndex, toIndex); + view->executeInTransaction("QmlModelNodeProxy::moveNode", [&] { + modelNode.nodeListProperty(propertyName.toUtf8()).slide(fromIndex, toIndex); }); } From f08a31fd6691dd198adf5c6e8acb57856e4c9ec7 Mon Sep 17 00:00:00 2001 From: Knud Dollereder Date: Fri, 26 Apr 2024 10:32:10 +0200 Subject: [PATCH 027/154] QmlProjectManager: Ignore build folder when parsing the project Fixes: QDS-12519 Change-Id: Idff419d22eb7c13fdce27fcbed7f04426f85dc94 Reviewed-by: Thomas Hartmann Reviewed-by: Reviewed-by: Qt CI Patch Build Bot --- .../cmakegen/cmakegenerator.cpp | 40 ++++++++++++++++--- .../cmakegen/cmakegenerator.h | 4 +- 2 files changed, 36 insertions(+), 8 deletions(-) diff --git a/src/plugins/qmlprojectmanager/cmakegen/cmakegenerator.cpp b/src/plugins/qmlprojectmanager/cmakegen/cmakegenerator.cpp index 659c544ebd8..f4b3d9ec531 100644 --- a/src/plugins/qmlprojectmanager/cmakegen/cmakegenerator.cpp +++ b/src/plugins/qmlprojectmanager/cmakegen/cmakegenerator.cpp @@ -17,8 +17,9 @@ #include "coreplugin/actionmanager/actioncontainer.h" #include -#include +#include #include +#include #include @@ -87,8 +88,8 @@ void CMakeGenerator::updateMenuAction() CMakeGenerator::CMakeGenerator(QmlBuildSystem *bs, QObject *parent) : QObject(parent) - , m_root(std::make_shared()) , m_buildSystem(bs) + , m_root(std::make_shared()) {} const QmlProject *CMakeGenerator::qmlProject() const @@ -170,6 +171,9 @@ void CMakeGenerator::update(const QSet &added, const QSet &rem std::set dirtyModules; for (const QString &add : added) { const Utils::FilePath path = Utils::FilePath::fromString(add); + if (ignore(path.parentDir())) + continue; + if (auto node = findOrCreateNode(m_root, path.parentDir())) { insertFile(node, path); if (auto module = findModuleFor(node)) @@ -208,10 +212,28 @@ bool CMakeGenerator::isResource(const Utils::FilePath &path) const return suffixes.contains(path.suffix(), Qt::CaseInsensitive); } -bool CMakeGenerator::ignoreFile(const Utils::FilePath &path) const +bool CMakeGenerator::ignore(const Utils::FilePath &path) const { - static const QStringList suffixes = { "hints" }; - return suffixes.contains(path.suffix(), Qt::CaseInsensitive); + if (path.isFile()) { + static const QStringList suffixes = { "hints" }; + return suffixes.contains(path.suffix(), Qt::CaseInsensitive); + } else if (path.isDir()) { + if (!m_root->dir.exists()) + return true; + + static const QStringList fileNames = { "CMakeCache.txt", "build.ninja" }; + + Utils::FilePath dir = path; + while (dir.isChildOf(m_root->dir)) { + for (const QString& fileName : fileNames) { + Utils::FilePath checkFile = dir.pathAppended(fileName); + if (checkFile.exists()) + return true; + } + dir = dir.parentDir(); + } + } + return false; } void CMakeGenerator::createCMakeFiles(const NodePtr &node) const @@ -445,6 +467,9 @@ void CMakeGenerator::parseNodeTree(NodePtr &generatorNode, { for (const auto *childNode : folderNode->nodes()) { if (const auto *subFolderNode = childNode->asFolderNode()) { + if (ignore(subFolderNode->filePath())) + continue; + NodePtr childGeneratorNode = std::make_shared(); childGeneratorNode->parent = generatorNode; childGeneratorNode->dir = subFolderNode->filePath(); @@ -499,7 +524,10 @@ void CMakeGenerator::compareWithFileSystem(const NodePtr &node) const while (iter.hasNext()) { auto next = Utils::FilePath::fromString(iter.next()); - if (isResource(next) && !findFile(next) && !ignoreFile(next)) + if (ignore(next.parentDir())) + continue; + + if (isResource(next) && !findFile(next) && !ignore(next)) files.push_back(next); } diff --git a/src/plugins/qmlprojectmanager/cmakegen/cmakegenerator.h b/src/plugins/qmlprojectmanager/cmakegen/cmakegenerator.h index 6077166d5b6..3af3405879f 100644 --- a/src/plugins/qmlprojectmanager/cmakegen/cmakegenerator.h +++ b/src/plugins/qmlprojectmanager/cmakegen/cmakegenerator.h @@ -47,7 +47,7 @@ public: private: bool isQml(const Utils::FilePath &path) const; bool isResource(const Utils::FilePath &path) const; - bool ignoreFile(const Utils::FilePath &path) const; + bool ignore(const Utils::FilePath &path) const; void createCMakeFiles(const NodePtr &node) const; void createSourceFiles() const; @@ -70,12 +70,12 @@ private: void compareWithFileSystem(const NodePtr &node) const; bool m_enabled = false; + QmlBuildSystem *m_buildSystem = nullptr; CMakeWriter::Ptr m_writer = {}; QString m_projectName = {}; NodePtr m_root = {}; QStringList m_moduleNames = {}; - QmlBuildSystem *m_buildSystem = nullptr; }; } // namespace GenerateCmake From d683e6d3c095dac99b2aba84f6d8d4cefd45c9e4 Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Thu, 18 Apr 2024 10:34:08 +0300 Subject: [PATCH 028/154] QmlDesigner: Enable sorting a model by id Task-number: QDS-11808 Change-Id: I088424b97b4e50a882aebcda82c784f95bd9948e Reviewed-by: Mahmoud Badri Reviewed-by: Shrief Gabr --- .../CollectionDetailsView.qml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsView.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsView.qml index 2193bd1763e..e9bdfdc675a 100644 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsView.qml +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsView.qml @@ -83,6 +83,17 @@ Rectangle { horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter color: StudioTheme.Values.themeTextColor + + MouseArea { + anchors.fill: parent + acceptedButtons: Qt.LeftButton + property int order: Qt.AscendingOrder + onClicked: { + order = (order == Qt.AscendingOrder) ? Qt.DescendingOrder : Qt.AscendingOrder; + tableView.closeEditor() + tableView.model.sort(-1, order) + } + } } } From 1b48ab6a1c3a1adf907533d17059856c791a7227 Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Mon, 29 Apr 2024 12:21:44 +0300 Subject: [PATCH 029/154] QmlDesigner: Update effect composer's help link Fixes: QDS-12601 Change-Id: Ib1f6008cf4ac3e50dc5c0b12233e65622eb142eb Reviewed-by: Tim Jenssen --- .../effectComposerQmlSources/EffectComposerTopBar.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectComposerTopBar.qml b/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectComposerTopBar.qml index 8f98ae25b25..799dbeeddc3 100644 --- a/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectComposerTopBar.qml +++ b/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectComposerTopBar.qml @@ -89,8 +89,8 @@ Rectangle { 2. Adjust the effect nodes properties 3. Change the order of the effects, if you like 4. See the preview -5. Save in the library, if you wish to reuse the effect later") // TODO: revise with doc engineer +5. Save in the assets library, if you wish to reuse the effect later") - onClicked: Qt.openUrlExternally("https://doc.qt.io/qtdesignstudio/qt-using-effect-maker-effects.html") + onClicked: Qt.openUrlExternally("https://doc.qt.io/qtdesignstudio/qtquick-effect-composer-view.html") } } From f666788df452c7804fa3cd66fbf3019122582ca3 Mon Sep 17 00:00:00 2001 From: Teea Poldsam Date: Fri, 26 Apr 2024 11:46:58 +0300 Subject: [PATCH 030/154] Doc: Update Simulating Events Fixes: QDS-11504 Change-Id: Ie9cac661f947efa7fac452a0f16493a1dc81f024 Reviewed-by: Johanna Vanhatapio --- .../images/studio-flow-events-assign.png | Bin 15537 -> 0 bytes .../images/studio-flow-events-assign.webp | Bin 0 -> 3638 bytes .../images/studio-flow-events-event-list.webp | Bin 0 -> 2950 bytes .../src/qtdesignstudio-app-flows.qdoc | 21 ++++++++---------- 4 files changed, 9 insertions(+), 12 deletions(-) delete mode 100644 doc/qtdesignstudio/images/studio-flow-events-assign.png create mode 100644 doc/qtdesignstudio/images/studio-flow-events-assign.webp create mode 100644 doc/qtdesignstudio/images/studio-flow-events-event-list.webp diff --git a/doc/qtdesignstudio/images/studio-flow-events-assign.png b/doc/qtdesignstudio/images/studio-flow-events-assign.png deleted file mode 100644 index c585dd8033549f6ee8a17abadcf78d210479d231..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15537 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 Y$O9?&mo61W3=9kmp00i_>zopr0IsQ8761SM diff --git a/doc/qtdesignstudio/images/studio-flow-events-assign.webp b/doc/qtdesignstudio/images/studio-flow-events-assign.webp new file mode 100644 index 0000000000000000000000000000000000000000..53468aca42bca5b97d9b0a8c6c33eb629c35a00c GIT binary patch literal 3638 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 literal 0 HcmV?d00001 diff --git a/doc/qtdesignstudio/src/qtdesignstudio-app-flows.qdoc b/doc/qtdesignstudio/src/qtdesignstudio-app-flows.qdoc index 43015624570..36d44d3b50c 100644 --- a/doc/qtdesignstudio/src/qtdesignstudio-app-flows.qdoc +++ b/doc/qtdesignstudio/src/qtdesignstudio-app-flows.qdoc @@ -461,7 +461,7 @@ keyboard shortcuts to simulate these events when you preview the UI. When you use the wizard to create a \uicontrol {Flow View} component, select - the \uicontrol {Use event simulator} check box to add an event simulator to + the \uicontrol {Use event simulator} checkbox to add an event simulator to the flow view. You can create an event list where you assign keyboard shortcuts to events, @@ -478,9 +478,8 @@ \li In the \uicontrol {Event List} dialog, select \inlineimage icons/plus.png to add a keyboard shortcut for triggering an event to the list. \image studio-flow-event-list.png "Event List dialog" - \li In the \uicontrol {Event ID} field, enter an identifier for the - event. You can search for existing events by entering search - criteria in the \uicontrol Filter field. + \li In the \uicontrol {Event ID} field, enter an identifier for the event. To search + for existing events, enter search criteria in the \uicontrol Filter field. \li In the \uicontrol Description field, describe the keyboard shortcut. \li In the \uicontrol Shortcut field, press the keyboard key that will trigger the event, and then select \uicontrol R to record the @@ -494,24 +493,22 @@ To assign events to actions: \list 1 + \li Select \uicontrol eventListSimulator in \uicontrol Navigator, and in + \uicontrol Properties > \uicontrol {Exposed Custom Properties}, select the + \uicontrol active checkbox. If \uicontrol eventListSimulator is not visible + in the \uicontrol Navigator, select \inlineimage icons/visibilityon.png. \li In \uicontrol Navigator, select an action area or transition line. \li In the context menu, select \uicontrol {Event List} > \uicontrol {Assign Events to Actions}. - \image studio-flow-events-assign.png "Assign Events to Actions dialog" - \li In the \uicontrol ID field, select a transition or an action area - \inlineimage icons/flow-action-icon.png - . You can search for events by entering search criteria in the - \uicontrol Filter field. + \image studio-flow-events-assign.webp "Assign Events to Actions dialog" \li To connect an event, select \uicontrol Connect next to an event in the list. To release a connected event, select \uicontrol Release. \li Press \key {Alt+P} to preview the UI. \li Select action areas in the preview, double-click events in the event list, or use the keyboard shortcuts to trigger events. - \image studio-flow-decision-preview.png "Event list in preview" + \image studio-flow-events-event-list.webp "Event list in Live Preview" \endlist - If the event triggers a \l{Simulating Conditions}{flow decision}, you - can select the path to take to the next flow item. */ /*! From fce30cb9d93b589b990eb37a66378e138ff3d140 Mon Sep 17 00:00:00 2001 From: Teea Poldsam Date: Mon, 29 Apr 2024 09:45:50 +0300 Subject: [PATCH 031/154] Doc: Update Simulating Conditions Fixes: QDS-11505 Change-Id: I91e793529d0d447ae82e02aedc6c32565f243df0 Reviewed-by: Johanna Vanhatapio --- .../images/studio-flow-decision-preview.png | Bin 12848 -> 0 bytes .../images/studio-flow-decision-preview.webp | Bin 0 -> 10926 bytes .../studio-flow-decision-properties.png | Bin 12037 -> 0 bytes .../studio-flow-decision-properties.webp | Bin 0 -> 12178 bytes .../images/studio-flow-decision.png | Bin 36350 -> 0 bytes .../images/studio-flow-decision.webp | Bin 0 -> 32674 bytes ...io-flow-transition-properties-question.png | Bin 11673 -> 0 bytes ...o-flow-transition-properties-question.webp | Bin 0 -> 11728 bytes .../src/qtdesignstudio-app-flows.qdoc | 24 +++++++++--------- 9 files changed, 12 insertions(+), 12 deletions(-) delete mode 100644 doc/qtdesignstudio/images/studio-flow-decision-preview.png create mode 100644 doc/qtdesignstudio/images/studio-flow-decision-preview.webp delete mode 100644 doc/qtdesignstudio/images/studio-flow-decision-properties.png create mode 100644 doc/qtdesignstudio/images/studio-flow-decision-properties.webp delete mode 100644 doc/qtdesignstudio/images/studio-flow-decision.png create mode 100644 doc/qtdesignstudio/images/studio-flow-decision.webp delete mode 100644 doc/qtdesignstudio/images/studio-flow-transition-properties-question.png create mode 100644 doc/qtdesignstudio/images/studio-flow-transition-properties-question.webp diff --git a/doc/qtdesignstudio/images/studio-flow-decision-preview.png b/doc/qtdesignstudio/images/studio-flow-decision-preview.png deleted file mode 100644 index c0c367d1439ee2b92dc9a1efd51faae3bb09fa87..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12848 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 diff --git a/doc/qtdesignstudio/images/studio-flow-decision-preview.webp b/doc/qtdesignstudio/images/studio-flow-decision-preview.webp new file mode 100644 index 0000000000000000000000000000000000000000..e3ac10b0b2404c7f9cb504011fb86f3f09b176aa GIT binary patch literal 10926 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 diff --git a/doc/qtdesignstudio/images/studio-flow-decision-properties.webp b/doc/qtdesignstudio/images/studio-flow-decision-properties.webp new file mode 100644 index 0000000000000000000000000000000000000000..4029d5cd1959288ac76f1e2361d9aa862d3ffa4a GIT binary patch literal 12178 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 literal 0 HcmV?d00001 diff --git a/doc/qtdesignstudio/images/studio-flow-decision.png b/doc/qtdesignstudio/images/studio-flow-decision.png deleted file mode 100644 index 0880a14559b24ee9f3f2995d61656edc9da0b535..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 36350 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 literal 0 HcmV?d00001 diff --git a/doc/qtdesignstudio/images/studio-flow-transition-properties-question.png b/doc/qtdesignstudio/images/studio-flow-transition-properties-question.png deleted file mode 100644 index 80cb7ad1a99d768684759e213d10b69c74c1fc60..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11673 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 diff --git a/doc/qtdesignstudio/images/studio-flow-transition-properties-question.webp b/doc/qtdesignstudio/images/studio-flow-transition-properties-question.webp new file mode 100644 index 0000000000000000000000000000000000000000..26250a17c12d529296d44d77da044c1f99b1ef2e GIT binary patch literal 11728 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 GU;qGo)coWC literal 0 HcmV?d00001 diff --git a/doc/qtdesignstudio/src/qtdesignstudio-app-flows.qdoc b/doc/qtdesignstudio/src/qtdesignstudio-app-flows.qdoc index 36d44d3b50c..f93759cf0d6 100644 --- a/doc/qtdesignstudio/src/qtdesignstudio-app-flows.qdoc +++ b/doc/qtdesignstudio/src/qtdesignstudio-app-flows.qdoc @@ -530,17 +530,17 @@ controls, backend, or sensor data that will be required for the production version. - \image studio-flow-decision.png "Flow Decision in the 2D view" + \image studio-flow-decision.webp "Flow Decision in the 2D view" To simulate conditions: \list 1 \li Drag a \uicontrol {Flow Decision} component from - \uicontrol Components \uicontrol {Flow View} to a + \uicontrol Components > \uicontrol {Flow View} to a \l{Adding Flow Views}{flow view} in the \l Navigator or \l {2D} view. \li Select the flow item where you want the application to start in - the \uicontrol Navigator or \uicontrol {2D} view, and then select - \uicontrol {Flow} > \uicontrol {Set Flow Start} in the context menu. + the \uicontrol Navigator or \uicontrol {2D} view. Then right-click the component + to open the context menu, and select \uicontrol Flow > \uicontrol {Set Flow Start}. \li Create an \l{Adding Action Areas and Transitions}{action area} for the component that will trigger the condition and connect it to the flow decision. @@ -551,10 +551,10 @@ title for the selection dialog that opens when the condition is triggered. \li Select a transition line in the \uicontrol Navigator or - \uicontrol {2D} view and add a descriptive text in the + \uicontrol {2D} view, and add a descriptive text in the \uicontrol {Question} field in \uicontrol Properties to represent a choice in the selection dialog. - \image studio-flow-transition-properties-question.png "Flow Transition properties" + \image studio-flow-transition-properties-question.webp "Flow Transition properties" \li Press \key {Alt+P} to preview the UI. \li Select action areas in the preview, double-click events in the event list, or use the keyboard shortcuts to trigger events. @@ -563,21 +563,21 @@ Flow decisions are listed in a dialog where you can select which condition is met to see the results. - \image studio-flow-decision-preview.png "Selection dialog for flow decision" + \image studio-flow-decision-preview.webp "Selection dialog for flow decision" \section1 Flow Decision Properties - You can specify basic properties for a \uicontrol {Flow Decision} component + Specify basic properties for a \uicontrol {Flow Decision} component in the \l Type and \l ID fields in the \uicontrol Component section in the \uicontrol Properties view. Specify properties for flow decisions in the \uicontrol {Flow Decision} section. - \image studio-flow-decision-properties.png "Flow Decision properties" + \image studio-flow-decision-properties.webp "Flow Decision properties" In the \uicontrol {Dialog title} field, enter a title for the selection dialog that opens when the condition is triggered. - You can specify the following properties to change the appearance of the + Specify the following properties to change the appearance of the flow decision icon \inlineimage icons/flow-decision-icon.png : @@ -587,14 +587,14 @@ component in the \l {2D} view. \li In the \uicontrol {Label position} field, select the corner of the flow decision icon to place the label in. + \li Use the \l{Picking Colors}{color picker} to set \uicontrol {Outline color} and + \uicontrol {Fill color} of the flow decision icon. \li In the \uicontrol Size field, specify the size of the flow decision icon. \li In the \uicontrol Radius field, specify the radius of the flow decision icon corners. \endlist - You can use the \l{Picking Colors}{color picker} to set the outline and - fill color of the flow decision icon. */ /*! From 1c34bbbc350b59150f8eb6b1a1ca7606549dafa3 Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Thu, 18 Apr 2024 11:37:49 +0300 Subject: [PATCH 032/154] QmlDesigner: Add Boolean delegate for the model editor * Also the edit property of the StudioControls.CheckBox is removed, because it might override the edit property of the item. Fixes: QDS-12059 Change-Id: I2fe66061e2018cdbea44a51ffca2fb0e696d5bbc Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Miikka Heikkinen Reviewed-by: Mahmoud Badri --- .../CollectionDetailsEditDelegate.qml | 26 ------------------- .../CollectionDetailsView.qml | 20 ++++++++++++++ .../imports/StudioControls/CheckBox.qml | 1 - 3 files changed, 20 insertions(+), 27 deletions(-) diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsEditDelegate.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsEditDelegate.qml index fd969382e63..71499cc47e8 100644 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsEditDelegate.qml +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsEditDelegate.qml @@ -135,23 +135,6 @@ Item { } } } - - Component { - id: boolEditor - - EditorPopup { - - editor: boolField - - StudioControls.CheckBox { - id: boolField - - property alias editValue: boolField.checked - - actionIndicatorVisible: false - } - } - } } component EditorPopup: T.Popup { @@ -225,15 +208,6 @@ Item { sourceComponent: realEditor } }, - State { - name: "bool" - when: columnType === CollectionDetails.DataType.Boolean - - PropertyChanges { - target: editorLoader - sourceComponent: boolEditor - } - }, State { name: "color" when: columnType === CollectionDetails.DataType.Color diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsView.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsView.qml index e9bdfdc675a..84a3f7f0d10 100644 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsView.qml +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsView.qml @@ -360,6 +360,24 @@ Rectangle { } } + Component { + id: checkBoxComponent + + StudioControls.CheckBox { + id: checkBoxDelegate + + readonly property bool editValue: edit + + text: "" + actionIndicatorVisible: false + checked: checkBoxDelegate.editValue + onCheckedChanged: { + if (checkBoxDelegate.editValue !== checkBoxDelegate.checked) + edit = checkBoxDelegate.checked + } + } + } + Component { id: colorEditorComponent @@ -369,6 +387,8 @@ Rectangle { function resetSource() { if (columnType === CollectionDetails.DataType.Color) cellContentLoader.sourceComponent = colorEditorComponent + else if (columnType === CollectionDetails.DataType.Boolean) + cellContentLoader.sourceComponent = checkBoxComponent else cellContentLoader.sourceComponent = cellText } diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/CheckBox.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/CheckBox.qml index 573b78012f3..7e565e3a47d 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/CheckBox.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/CheckBox.qml @@ -14,7 +14,6 @@ T.CheckBox { // This property is used to indicate the global hover state property bool hover: control.hovered && control.enabled - property bool edit: false property alias actionIndicatorVisible: actionIndicator.visible property real __actionIndicatorWidth: control.style.actionIndicatorSize.width From 6bd4daa53f6c3cd7772107fe97487c54ccf49f5a Mon Sep 17 00:00:00 2001 From: Shrief Gabr Date: Mon, 29 Apr 2024 14:51:05 +0300 Subject: [PATCH 033/154] QmlDesigner: Prevent data change on adding row/column Fixes: QDS-12498 Change-Id: I56ec2b0c81b3a77a375d46fa7fa8bc203d446c28 Reviewed-by: Ali Kianian Reviewed-by: Mahmoud Badri --- .../CollectionDetailsView.qml | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsView.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsView.qml index 84a3f7f0d10..337cad2ed8c 100644 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsView.qml +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsView.qml @@ -502,7 +502,10 @@ Rectangle { icon: StudioTheme.Constants.create_medium tooltip: "Add Column" - onClicked: toolbar.addNewColumn() + onClicked: { + tableView.closeEditor() + toolbar.addNewColumn() + } } HelperWidgets.IconButton { @@ -517,7 +520,10 @@ Rectangle { icon: StudioTheme.Constants.create_medium tooltip: "Add Row" - onClicked: toolbar.addNewRow() + onClicked: { + tableView.closeEditor() + toolbar.addNewRow() + } } Item { From c9c3303ad889b011460cb0d5a7a0cb5ccc290789 Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Mon, 29 Apr 2024 11:25:21 +0300 Subject: [PATCH 034/154] QmlDesigner: Refactor bundle importer so that it can be shared by more than 1 bundle. Also some relevant tweaks. Change-Id: I421648c26ac4a0d51612f21a5475f0938dfff331 Reviewed-by: Miikka Heikkinen Reviewed-by: Qt CI Patch Build Bot --- .../ContentLibraryMaterial.qml | 2 +- .../contentlibrarybundleimporter.cpp | 122 +++++++----------- .../contentlibrarybundleimporter.h | 26 ++-- .../contentlibrary/contentlibraryeffect.cpp | 2 - .../contentlibrary/contentlibraryeffect.h | 1 - .../contentlibraryeffectsmodel.cpp | 18 +-- .../contentlibraryeffectsmodel.h | 9 +- .../contentlibrary/contentlibrarymaterial.cpp | 2 +- .../contentlibrary/contentlibrarymaterial.h | 4 +- .../contentlibrarymaterialsmodel.cpp | 33 ++--- .../contentlibrarymaterialsmodel.h | 5 +- .../contentlibraryusermodel.cpp | 17 ++- .../contentlibrary/contentlibraryusermodel.h | 3 +- .../contentlibrary/contentlibraryview.cpp | 8 +- 14 files changed, 102 insertions(+), 150 deletions(-) diff --git a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryMaterial.qml b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryMaterial.qml index 0e9fc4903eb..775e5811936 100644 --- a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryMaterial.qml +++ b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryMaterial.qml @@ -186,7 +186,7 @@ Item { baseUrl: modelData.bundleMaterialBaseWebUrl files: modelData.bundleMaterialFiles - targetDirPath: modelData.bundleMaterialParentPath + targetDirPath: modelData.bundleMaterialDirPath onDownloadStarting: { root.downloadState = "downloading" diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarybundleimporter.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarybundleimporter.cpp index 5c8d42a3065..85d42f9d128 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarybundleimporter.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarybundleimporter.cpp @@ -12,7 +12,6 @@ #include -#include #include #include #include @@ -21,14 +20,8 @@ using namespace Utils; namespace QmlDesigner::Internal { -ContentLibraryBundleImporter::ContentLibraryBundleImporter(const QString &bundleDir, - const QString &bundleId, - const QStringList &sharedFiles, - QObject *parent) +ContentLibraryBundleImporter::ContentLibraryBundleImporter(QObject *parent) : QObject(parent) - , m_bundleDir(FilePath::fromString(bundleDir)) - , m_bundleId(bundleId) - , m_sharedFiles(sharedFiles) { m_importTimer.setInterval(200); connect(&m_importTimer, &QTimer::timeout, this, &ContentLibraryBundleImporter::handleImportTimer); @@ -38,47 +31,38 @@ ContentLibraryBundleImporter::ContentLibraryBundleImporter(const QString &bundle // Note that there is also an asynchronous portion to the import, which will only // be done if this method returns success. Once the asynchronous portion of the // import is completed, importFinished signal will be emitted. -QString ContentLibraryBundleImporter::importComponent(const QString &qmlFile, +QString ContentLibraryBundleImporter::importComponent(const QString &bundleDir, + const TypeName &type, + const QString &qmlFile, const QStringList &files) { - FilePath bundleImportPath = resolveBundleImportPath(); + QString module = QString::fromLatin1(type.left(type.lastIndexOf('.'))); + QString bundleId = module.mid(module.lastIndexOf('.') + 1); + + FilePath bundleDirPath = FilePath::fromString(bundleDir); // source dir + FilePath bundleImportPath = resolveBundleImportPath(bundleId); // target dir + if (bundleImportPath.isEmpty()) return "Failed to resolve bundle import folder"; - bool bundleImportPathExists = bundleImportPath.exists(); - - if (!bundleImportPathExists && !bundleImportPath.createDir()) + if (!bundleImportPath.exists() && !bundleImportPath.createDir()) return QStringLiteral("Failed to create bundle import folder: '%1'").arg(bundleImportPath.toString()); - for (const QString &file : std::as_const(m_sharedFiles)) { - FilePath target = bundleImportPath.resolvePath(file); - if (!target.exists()) { - FilePath parentDir = target.parentDir(); - if (!parentDir.exists() && !parentDir.createDir()) - return QStringLiteral("Failed to create folder for: '%1'").arg(target.toString()); - FilePath source = m_bundleDir.resolvePath(file); - if (!source.copyFile(target)) - return QStringLiteral("Failed to copy shared file: '%1'").arg(source.toString()); - } - } - - FilePath qmldirPath = bundleImportPath.resolvePath(QStringLiteral("qmldir")); + FilePath qmldirPath = bundleImportPath.pathAppended("qmldir"); QString qmldirContent = QString::fromUtf8(qmldirPath.fileContents().value_or(QByteArray())); if (qmldirContent.isEmpty()) { qmldirContent.append("module "); - qmldirContent.append(moduleName()); + qmldirContent.append(module); qmldirContent.append('\n'); } - FilePath qmlSourceFile = bundleImportPath.resolvePath(FilePath::fromString(qmlFile)); + FilePath qmlSourceFile = bundleImportPath.pathAppended(qmlFile); const bool qmlFileExists = qmlSourceFile.exists(); const QString qmlType = qmlSourceFile.baseName(); - const QString fullTypeName = QStringLiteral("%1.%2.%3") - .arg(QmlDesignerPlugin::instance()->documentManager() - .generatedComponentUtils().componentBundlesTypePrefix(), - m_bundleId, qmlType); - if (m_pendingTypes.contains(fullTypeName) && !m_pendingTypes[fullTypeName]) - return QStringLiteral("Unable to import while unimporting the same type: '%1'").arg(fullTypeName); + + if (m_pendingTypes.contains(type) && !m_pendingTypes.value(type)) + return QStringLiteral("Unable to import while unimporting the same type: '%1'").arg(QLatin1String(type)); + if (!qmldirContent.contains(qmlFile)) { qmldirContent.append(qmlType); qmldirContent.append(" 1.0 "); @@ -91,12 +75,12 @@ QString ContentLibraryBundleImporter::importComponent(const QString &qmlFile, allFiles.append(files); allFiles.append(qmlFile); for (const QString &file : std::as_const(allFiles)) { - FilePath target = bundleImportPath.resolvePath(file); + FilePath target = bundleImportPath.pathAppended(file); FilePath parentDir = target.parentDir(); if (!parentDir.exists() && !parentDir.createDir()) return QStringLiteral("Failed to create folder for: '%1'").arg(target.toString()); - FilePath source = m_bundleDir.resolvePath(file); + FilePath source = bundleDirPath.pathAppended(file); if (target.exists()) { if (source.lastModified() == target.lastModified()) continue; @@ -125,23 +109,23 @@ QString ContentLibraryBundleImporter::importComponent(const QString &qmlFile, if (!model) return "Model not available, cannot add import statement or update code model"; - Import import = Import::createLibraryImport(moduleName(), "1.0"); + Import import = Import::createLibraryImport(module, "1.0"); if (!model->hasImport(import)) { if (model->possibleImports().contains(import)) { - m_importAddPending = false; + m_pendingImport.clear(); try { model->changeImports({import}, {}); } catch (const RewritingException &) { // No point in trying to add import asynchronously either, so just fail out - return QStringLiteral("Failed to add import statement for: '%1'").arg(moduleName()); + return QStringLiteral("Failed to add import statement for: '%1'").arg(module); } } else { // If import is not yet possible, import statement needs to be added asynchronously to // avoid errors, as code model update takes a while. - m_importAddPending = true; + m_pendingImport = module; } } - m_pendingTypes.insert(fullTypeName, true); + m_pendingTypes.insert(type, true); m_importTimerCount = 0; m_importTimer.start(); @@ -153,14 +137,14 @@ void ContentLibraryBundleImporter::handleImportTimer() auto handleFailure = [this] { m_importTimer.stop(); m_fullReset = false; - m_importAddPending = false; + m_pendingImport.clear(); m_importTimerCount = 0; // Emit dummy finished signals for all pending types - const QStringList pendingTypes = m_pendingTypes.keys(); - for (const QString &pendingType : pendingTypes) { + const QList pendingTypes = m_pendingTypes.keys(); + for (const TypeName &pendingType : pendingTypes) { m_pendingTypes.remove(pendingType); - if (m_pendingTypes[pendingType]) + if (m_pendingTypes.value(pendingType)) emit importFinished({}); else emit unimportFinished({}); @@ -185,12 +169,12 @@ void ContentLibraryBundleImporter::handleImportTimer() QmlDesignerPlugin::instance()->documentManager().resetPossibleImports(); - if (m_importAddPending) { + if (!m_pendingImport.isEmpty()) { try { - Import import = Import::createLibraryImport(moduleName(), "1.0"); + Import import = Import::createLibraryImport(m_pendingImport, "1.0"); if (model->possibleImports().contains(import)) { model->changeImports({import}, {}); - m_importAddPending = false; + m_pendingImport.clear(); } } catch (const RewritingException &) { // Import adding is unlikely to succeed later, either, so just bail out @@ -200,16 +184,16 @@ void ContentLibraryBundleImporter::handleImportTimer() } // Detect when the code model has the new material(s) fully available - const QStringList pendingTypes = m_pendingTypes.keys(); - for (const QString &pendingType : pendingTypes) { - NodeMetaInfo metaInfo = model->metaInfo(pendingType.toUtf8()); - const bool isImport = m_pendingTypes[pendingType]; + const QList pendingTypes = m_pendingTypes.keys(); + for (const TypeName &pendingType : pendingTypes) { + NodeMetaInfo metaInfo = model->metaInfo(pendingType); + const bool isImport = m_pendingTypes.value(pendingType); const bool typeComplete = metaInfo.isValid() && !metaInfo.prototypes().empty(); if (isImport == typeComplete) { m_pendingTypes.remove(pendingType); if (isImport) #ifdef QDS_USE_PROJECTSTORAGE - emit importFinished(pendingType.toUtf8()); + emit importFinished(pendingType); #else emit importFinished(metaInfo); #endif @@ -252,16 +236,12 @@ void ContentLibraryBundleImporter::writeAssetRefMap(const Utils::FilePath &bundl } } -QString ContentLibraryBundleImporter::moduleName() +QString ContentLibraryBundleImporter::unimportComponent(const TypeName &type, const QString &qmlFile) { - return QStringLiteral("%1.%2").arg(QmlDesignerPlugin::instance()->documentManager() - .generatedComponentUtils().componentBundlesTypePrefix(), - m_bundleId); -} + QString module = QString::fromLatin1(type.left(type.lastIndexOf('.'))); + QString bundleId = module.mid(module.lastIndexOf('.') + 1); -QString ContentLibraryBundleImporter::unimportComponent(const QString &qmlFile) -{ - FilePath bundleImportPath = resolveBundleImportPath(); + FilePath bundleImportPath = resolveBundleImportPath(bundleId); if (bundleImportPath.isEmpty()) return QStringLiteral("Failed to resolve bundle import folder for: '%1'").arg(qmlFile); @@ -280,12 +260,10 @@ QString ContentLibraryBundleImporter::unimportComponent(const QString &qmlFile) QByteArray newContent; QString qmlType = qmlFilePath.baseName(); - const QString fullTypeName = QStringLiteral("%1.%2.%3") - .arg(QmlDesignerPlugin::instance()->documentManager() - .generatedComponentUtils().componentBundlesTypePrefix(), - m_bundleId, qmlType); - if (m_pendingTypes.contains(fullTypeName) && m_pendingTypes[fullTypeName]) - return QStringLiteral("Unable to unimport while importing the same type: '%1'").arg(fullTypeName); + if (m_pendingTypes.contains(type) && m_pendingTypes.value(type)) { + return QStringLiteral("Unable to unimport while importing the same type: '%1'") + .arg(QString::fromLatin1(type)); + } if (qmldirContent) { int typeIndex = qmldirContent->indexOf(qmlType.toUtf8()); @@ -301,7 +279,7 @@ QString ContentLibraryBundleImporter::unimportComponent(const QString &qmlFile) } } - m_pendingTypes.insert(fullTypeName, false); + m_pendingTypes.insert(type, false); QVariantHash assetRefMap = loadAssetRefMap(bundleImportPath); bool writeAssetRefs = false; @@ -335,7 +313,7 @@ QString ContentLibraryBundleImporter::unimportComponent(const QString &qmlFile) auto doc = QmlDesignerPlugin::instance()->currentDesignDocument(); Model *model = doc ? doc->currentModel() : nullptr; if (model) { - Import import = Import::createLibraryImport(moduleName(), "1.0"); + Import import = Import::createLibraryImport(module, "1.0"); if (model->imports().contains(import)) model->changeImports({}, {import}); } @@ -348,14 +326,14 @@ QString ContentLibraryBundleImporter::unimportComponent(const QString &qmlFile) return {}; } -FilePath ContentLibraryBundleImporter::resolveBundleImportPath() +FilePath ContentLibraryBundleImporter::resolveBundleImportPath(const QString &bundleId) { FilePath bundleImportPath = QmlDesignerPlugin::instance()->documentManager() .generatedComponentUtils().componentBundlesBasePath(); if (bundleImportPath.isEmpty()) - return bundleImportPath; + return {}; - return bundleImportPath.resolvePath(m_bundleId); + return bundleImportPath.resolvePath(bundleId); } } // namespace QmlDesigner::Internal diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarybundleimporter.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarybundleimporter.h index 7fb2a48886d..3a7c22a52e1 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarybundleimporter.h +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarybundleimporter.h @@ -3,16 +3,13 @@ #pragma once -#include +#include -#include "nodemetainfo.h" +#include #include #include -QT_BEGIN_NAMESPACE -QT_END_NAMESPACE - namespace QmlDesigner::Internal { class ContentLibraryBundleImporter : public QObject @@ -20,16 +17,13 @@ class ContentLibraryBundleImporter : public QObject Q_OBJECT public: - ContentLibraryBundleImporter(const QString &bundleDir, - const QString &bundleId, - const QStringList &sharedFiles, - QObject *parent = nullptr); + ContentLibraryBundleImporter(QObject *parent = nullptr); ~ContentLibraryBundleImporter() = default; - QString importComponent(const QString &qmlFile, + QString importComponent(const QString &bundleDir, const TypeName &type, const QString &qmlFile, const QStringList &files); - QString unimportComponent(const QString &qmlFile); - Utils::FilePath resolveBundleImportPath(); + QString unimportComponent(const TypeName &type, const QString &qmlFile); + Utils::FilePath resolveBundleImportPath(const QString &bundleId); signals: // The metaInfo parameter will be invalid if an error was encountered during @@ -46,16 +40,12 @@ private: void handleImportTimer(); QVariantHash loadAssetRefMap(const Utils::FilePath &bundlePath); void writeAssetRefMap(const Utils::FilePath &bundlePath, const QVariantHash &assetRefMap); - QString moduleName(); - Utils::FilePath m_bundleDir; - QString m_bundleId; - QStringList m_sharedFiles; QTimer m_importTimer; int m_importTimerCount = 0; - bool m_importAddPending = false; + QString m_pendingImport; bool m_fullReset = false; - QHash m_pendingTypes; // + QHash m_pendingTypes; // }; } // namespace QmlDesigner::Internal diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffect.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffect.cpp index f572fbe65f4..0ae800a3690 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffect.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffect.cpp @@ -3,8 +3,6 @@ #include "contentlibraryeffect.h" -#include - namespace QmlDesigner { ContentLibraryEffect::ContentLibraryEffect(QObject *parent, diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffect.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffect.h index fdb302b6139..cbfbf7ce3e3 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffect.h +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffect.h @@ -39,7 +39,6 @@ public: bool setImported(bool imported); bool imported() const; - QString parentDirPath() const; QStringList allFiles() const; signals: diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectsmodel.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectsmodel.cpp index 157290c2d53..f6c3f855baa 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectsmodel.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectsmodel.cpp @@ -88,10 +88,9 @@ QHash ContentLibraryEffectsModel::roleNames() const return roles; } -void ContentLibraryEffectsModel::createImporter(const QString &bundlePath, const QString &bundleId, - const QStringList &sharedFiles) +void ContentLibraryEffectsModel::createImporter() { - m_importer = new Internal::ContentLibraryBundleImporter(bundlePath, bundleId, sharedFiles); + m_importer = new Internal::ContentLibraryBundleImporter(); #ifdef QDS_USE_PROJECTSTORAGE connect(m_importer, &Internal::ContentLibraryBundleImporter::importFinished, @@ -200,13 +199,13 @@ void ContentLibraryEffectsModel::loadBundle() m_bundleCategories.append(category); } - QStringList sharedFiles; + m_importerSharedFiles.clear(); const QJsonArray sharedFilesArr = m_bundleObj.value("sharedFiles").toArray(); for (const QJsonValueConstRef &file : sharedFilesArr) - sharedFiles.append(file.toString()); - - createImporter(bundleDir.path(), bundleId, sharedFiles); + m_importerSharedFiles.append(file.toString()); + createImporter(); + m_bundlePath = bundleDir.path(); m_bundleExists = true; emit bundleExistsChanged(); } @@ -280,7 +279,8 @@ void ContentLibraryEffectsModel::resetModel() void ContentLibraryEffectsModel::addInstance(ContentLibraryEffect *bundleItem) { - QString err = m_importer->importComponent(bundleItem->qml(), bundleItem->files()); + QString err = m_importer->importComponent(m_bundlePath, bundleItem->type(), bundleItem->qml(), + bundleItem->files() + m_importerSharedFiles); if (err.isEmpty()) { m_importerRunning = true; @@ -294,7 +294,7 @@ void ContentLibraryEffectsModel::removeFromProject(ContentLibraryEffect *bundleI { emit bundleItemAboutToUnimport(bundleItem->type()); - QString err = m_importer->unimportComponent(bundleItem->qml()); + QString err = m_importer->unimportComponent(bundleItem->type(), bundleItem->qml()); if (err.isEmpty()) { m_importerRunning = true; diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectsmodel.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectsmodel.h index 5d67ac3da8b..5ac40bbef4b 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectsmodel.h +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectsmodel.h @@ -69,11 +69,12 @@ signals: private: bool isValidIndex(int idx) const; - void createImporter(const QString &bundlePath, const QString &bundleId, - const QStringList &sharedFiles); + void createImporter(); ContentLibraryWidget *m_widget = nullptr; QString m_searchText; + QString m_bundlePath; + QStringList m_importerSharedFiles; QList m_bundleCategories; QJsonObject m_bundleObj; Internal::ContentLibraryBundleImporter *m_importer = nullptr; @@ -85,10 +86,6 @@ private: int m_quick3dMajorVersion = -1; int m_quick3dMinorVersion = -1; - - QString m_importerBundlePath; - QString m_importerBundleId; - QStringList m_importerSharedFiles; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterial.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterial.cpp index 1d30b0217d8..25d15231991 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterial.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterial.cpp @@ -89,7 +89,7 @@ QString ContentLibraryMaterial::qmlFilePath() const return m_downloadPath + "/" + m_qml; } -QString ContentLibraryMaterial::parentDirPath() const +QString ContentLibraryMaterial::dirPath() const { return m_downloadPath; } diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterial.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterial.h index a2f53b7e3cf..d965f25014d 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterial.h +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterial.h @@ -19,7 +19,7 @@ class ContentLibraryMaterial : public QObject Q_PROPERTY(bool bundleMaterialVisible MEMBER m_visible NOTIFY materialVisibleChanged) Q_PROPERTY(bool bundleMaterialImported READ imported WRITE setImported NOTIFY materialImportedChanged) Q_PROPERTY(QString bundleMaterialBaseWebUrl MEMBER m_baseWebUrl CONSTANT) - Q_PROPERTY(QString bundleMaterialParentPath READ parentDirPath CONSTANT) + Q_PROPERTY(QString bundleMaterialDirPath READ dirPath CONSTANT) Q_PROPERTY(QStringList bundleMaterialFiles READ allFiles CONSTANT) Q_PROPERTY(QString itemType MEMBER m_itemType CONSTANT) @@ -47,7 +47,7 @@ public: bool setImported(bool imported); bool imported() const; - QString parentDirPath() const; + QString dirPath() const; QStringList allFiles() const; signals: diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.cpp index a2aef7b47f9..b60ac841d58 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.cpp @@ -198,8 +198,7 @@ void ContentLibraryMaterialsModel::downloadSharedFiles(const QDir &targetDir, co QObject::connect(extractor, &FileExtractor::finishedChanged, this, [this, downloader, extractor]() { downloader->deleteLater(); extractor->deleteLater(); - - createImporter(m_importerBundlePath, m_importerBundleId, m_importerSharedFiles); + createImporter(); }); extractor->extract(); @@ -208,10 +207,9 @@ void ContentLibraryMaterialsModel::downloadSharedFiles(const QDir &targetDir, co downloader->start(); } -void ContentLibraryMaterialsModel::createImporter(const QString &bundlePath, const QString &bundleId, - const QStringList &sharedFiles) +void ContentLibraryMaterialsModel::createImporter() { - m_importer = new Internal::ContentLibraryBundleImporter(bundlePath, bundleId, sharedFiles); + m_importer = new Internal::ContentLibraryBundleImporter(); #ifdef QDS_USE_PROJECTSTORAGE connect(m_importer, &Internal::ContentLibraryBundleImporter::importFinished, @@ -303,27 +301,21 @@ void ContentLibraryMaterialsModel::loadMaterialBundle(const QDir &matBundleDir) m_bundleCategories.append(category); } - QStringList sharedFiles; + m_importerSharedFiles.clear(); const QJsonArray sharedFilesArr = m_matBundleObj.value("sharedFiles").toArray(); for (const QJsonValueConstRef &file : sharedFilesArr) - sharedFiles.append(file.toString()); + m_importerSharedFiles.append(file.toString()); QStringList missingSharedFiles; - for (const QString &s : std::as_const(sharedFiles)) { - const QString fullSharedFilePath = matBundleDir.filePath(s); - - if (!QFileInfo::exists(fullSharedFilePath)) + for (const QString &s : std::as_const(m_importerSharedFiles)) { + if (!QFileInfo::exists(matBundleDir.filePath(s))) missingSharedFiles.push_back(s); } - if (missingSharedFiles.length() > 0) { - m_importerBundlePath = matBundleDir.path(); - m_importerBundleId = bundleId; - m_importerSharedFiles = sharedFiles; + if (missingSharedFiles.length() > 0) downloadSharedFiles(matBundleDir, missingSharedFiles); - } else { - createImporter(matBundleDir.path(), bundleId, sharedFiles); - } + else + createImporter(); m_matBundleExists = true; emit matBundleExistsChanged(); @@ -403,7 +395,8 @@ void ContentLibraryMaterialsModel::applyToSelected(ContentLibraryMaterial *mat, void ContentLibraryMaterialsModel::addToProject(ContentLibraryMaterial *mat) { - QString err = m_importer->importComponent(mat->qml(), mat->files()); + QString err = m_importer->importComponent(mat->dirPath(), mat->type(), + mat->qml(), mat->files() + m_importerSharedFiles); if (err.isEmpty()) { m_importerRunning = true; @@ -417,7 +410,7 @@ void ContentLibraryMaterialsModel::removeFromProject(ContentLibraryMaterial *mat { emit bundleMaterialAboutToUnimport(mat->type()); - QString err = m_importer->unimportComponent(mat->qml()); + QString err = m_importer->unimportComponent(mat->type(), mat->qml()); if (err.isEmpty()) { m_importerRunning = true; diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.h index a2c2e90ce1e..c37ffd39fbf 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.h +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.h @@ -81,8 +81,7 @@ private: bool fetchBundleMetadata(const QDir &bundleDir); bool isValidIndex(int idx) const; void downloadSharedFiles(const QDir &targetDir, const QStringList &files); - void createImporter(const QString &bundlePath, const QString &bundleId, - const QStringList &sharedFiles); + void createImporter(); ContentLibraryWidget *m_widget = nullptr; QString m_searchText; @@ -101,8 +100,6 @@ private: QString m_downloadPath; QString m_baseUrl; - QString m_importerBundlePath; - QString m_importerBundleId; QStringList m_importerSharedFiles; }; diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp index 34cbadfb8ba..9b53ae4f064 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp @@ -4,7 +4,6 @@ #include "contentlibraryusermodel.h" #include "contentlibrarybundleimporter.h" -#include "contentlibraryeffect.h" #include "contentlibrarymaterial.h" #include "contentlibrarymaterialscategory.h" #include "contentlibrarytexture.h" @@ -224,10 +223,9 @@ QHash ContentLibraryUserModel::roleNames() const return roles; } -void ContentLibraryUserModel::createImporter(const QString &bundlePath, const QString &bundleId, - const QStringList &sharedFiles) +void ContentLibraryUserModel::createImporter() { - m_importer = new Internal::ContentLibraryBundleImporter(bundlePath, bundleId, sharedFiles); + m_importer = new Internal::ContentLibraryBundleImporter(); #ifdef QDS_USE_PROJECTSTORAGE connect(m_importer, &Internal::ContentLibraryBundleImporter::importFinished, @@ -325,12 +323,12 @@ void ContentLibraryUserModel::loadMaterialBundle() m_userMaterials.append(userMat); } - QStringList sharedFiles; + m_importerSharedFiles.clear(); const QJsonArray sharedFilesArr = m_bundleObj.value("sharedFiles").toArray(); for (const QJsonValueConstRef &file : sharedFilesArr) - sharedFiles.append(file.toString()); + m_importerSharedFiles.append(file.toString()); - createImporter(bundleDir.path(), m_bundleId, sharedFiles); + createImporter(); m_matBundleExists = true; emit matBundleExistsChanged(); @@ -433,7 +431,8 @@ void ContentLibraryUserModel::applyToSelected(ContentLibraryMaterial *mat, bool void ContentLibraryUserModel::addToProject(ContentLibraryMaterial *mat) { - QString err = m_importer->importComponent(mat->qml(), mat->files()); + QString err = m_importer->importComponent(mat->dirPath(), mat->type(), mat->qml(), + mat->files() + m_importerSharedFiles); if (err.isEmpty()) { m_importerRunning = true; @@ -447,7 +446,7 @@ void ContentLibraryUserModel::removeFromProject(ContentLibraryMaterial *mat) { emit bundleMaterialAboutToUnimport(mat->type()); - QString err = m_importer->unimportComponent(mat->qml()); + QString err = m_importer->unimportComponent(mat->type(), mat->qml()); if (err.isEmpty()) { m_importerRunning = true; diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.h index 1bda49674a8..6ac2160e9b8 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.h +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.h @@ -101,8 +101,7 @@ signals: private: bool isValidIndex(int idx) const; - void createImporter(const QString &bundlePath, const QString &bundleId, - const QStringList &sharedFiles); + void createImporter(); ContentLibraryWidget *m_widget = nullptr; QString m_searchText; diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp index 9cbc0f73393..5981907f6c5 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp @@ -778,7 +778,9 @@ void ContentLibraryView::updateBundleMaterialsImportedState() QStringList importedBundleMats; - FilePath materialBundlePath = m_widget->materialsModel()->bundleImporter()->resolveBundleImportPath(); + // TODO: this will be refactored next: no need for the round trip from model to view then back to model + // (same applies for the similar cases for effects and user material bundles) + FilePath materialBundlePath = m_widget->materialsModel()->bundleImporter()->resolveBundleImportPath("MaterialBundle"); if (materialBundlePath.exists()) { importedBundleMats = transform(materialBundlePath.dirEntries({{"*.qml"}, QDir::Files}), @@ -797,7 +799,7 @@ void ContentLibraryView::updateBundleUserMaterialsImportedState() QStringList importedBundleMats; - FilePath bundlePath = m_widget->userModel()->bundleImporter()->resolveBundleImportPath(); + FilePath bundlePath = m_widget->userModel()->bundleImporter()->resolveBundleImportPath("UserMaterialBundle"); if (bundlePath.exists()) { importedBundleMats = transform(bundlePath.dirEntries({{"*.qml"}, QDir::Files}), @@ -816,7 +818,7 @@ void ContentLibraryView::updateBundleEffectsImportedState() QStringList importedBundleEffs; - FilePath bundlePath = m_widget->effectsModel()->bundleImporter()->resolveBundleImportPath(); + FilePath bundlePath = m_widget->effectsModel()->bundleImporter()->resolveBundleImportPath("EffectBundle"); if (bundlePath.exists()) { importedBundleEffs = transform(bundlePath.dirEntries({{"*.qml"}, QDir::Files}), From 7a9a30d68ccd6be88a006443acf363e4b596c922 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Mon, 29 Apr 2024 16:15:43 +0300 Subject: [PATCH 035/154] QmlDesigner: Add reflection probe icon gizmo Fixes: QDS-11964 Change-Id: I35fce676c282988e7faa1376da90e4cd53f353ae Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Mahmoud Badri --- src/tools/qml2puppet/editor3d_qt6.qrc | 3 + .../mockfiles/images/reflectionprobe.png | Bin 0 -> 463 bytes .../mockfiles/images/reflectionprobe@2x.png | Bin 0 -> 887 bytes .../qml2puppet/mockfiles/qt6/EditView3D.qml | 18 ++++ .../mockfiles/qt6/OverlayView3D.qml | 102 ++++++++++++++++-- .../mockfiles/qt6/ReflectionProbeGizmo.qml | 9 ++ .../qt5informationnodeinstanceserver.cpp | 28 ++++- .../qt5informationnodeinstanceserver.h | 2 +- 8 files changed, 148 insertions(+), 14 deletions(-) create mode 100644 src/tools/qml2puppet/mockfiles/images/reflectionprobe.png create mode 100644 src/tools/qml2puppet/mockfiles/images/reflectionprobe@2x.png create mode 100644 src/tools/qml2puppet/mockfiles/qt6/ReflectionProbeGizmo.qml diff --git a/src/tools/qml2puppet/editor3d_qt6.qrc b/src/tools/qml2puppet/editor3d_qt6.qrc index d76b1941b99..c437bad9adf 100644 --- a/src/tools/qml2puppet/editor3d_qt6.qrc +++ b/src/tools/qml2puppet/editor3d_qt6.qrc @@ -19,6 +19,8 @@ mockfiles/images/spot@2x.png mockfiles/images/preview_landscape.hdr mockfiles/images/preview_studio.hdr + mockfiles/images/reflectionprobe.png + mockfiles/images/reflectionprobe@2x.png mockfiles/qt6/AdjustableArrow.qml mockfiles/qt6/Arrow.qml mockfiles/qt6/AutoScaleHelper.qml @@ -50,6 +52,7 @@ mockfiles/qt6/PlanarMoveHandle.qml mockfiles/qt6/PlanarScaleHandle.qml mockfiles/qt6/ReflectionProbeBox.qml + mockfiles/qt6/ReflectionProbeGizmo.qml mockfiles/qt6/RotateGizmo.qml mockfiles/qt6/RotateRing.qml mockfiles/qt6/ScaleGizmo.qml diff --git a/src/tools/qml2puppet/mockfiles/images/reflectionprobe.png b/src/tools/qml2puppet/mockfiles/images/reflectionprobe.png new file mode 100644 index 0000000000000000000000000000000000000000..4f31a98f659455823c0852571cd495074ca7948e GIT binary patch literal 463 zcmeAS@N?(olHy`uVBq!ia0y~yU@!n-4rT@hhJ-tuTR~KSPlzi67%0mt{Qv)-fq?-+ zqH!UD3=9l#aRvqk0RaI81qEe!1p@| z|7#^#G#D5dgi3<^f|lR(j#;s8bI{7IK9bK%bA8snY*~{Sv31G3 z`x(YeZkHKPGvDwpa=iU>f%2}f@+~2pU!S>5J-jY2)n~V&UVawK0xc!4M~fCUv0sQU z?ppfimObMOz2jex$m+}PVaT|es9VT>^^kM}+sauLL5rk*urRFT5;o^x;z8O62rU6 z1sBRpGuDNbJ=(EUP-K3jo?Tw$Z{CzJDUFQ4-FFtfQ8chjd-G?;Lq4N#D^mC$|Ce|F f^_V60b@(+|u5PDpy~anmposBw^>bP0l+XkKM0dK? literal 0 HcmV?d00001 diff --git a/src/tools/qml2puppet/mockfiles/images/reflectionprobe@2x.png b/src/tools/qml2puppet/mockfiles/images/reflectionprobe@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..2aa3124a4144b4033c94b80302dca77fd6da9d76 GIT binary patch literal 887 zcmeAS@N?(olHy`uVBq!ia0y~yU`POA4rT@hhO2JvAc@`ppAc6DFi@6P`2YVugk)e~ z`2YVu0|Nt`3lTw608s-WmE{$X)e8s+C@3f>%PSZd7}#6eBqSu16_+$LH1zlP&!0PY z{`~pt*RS8dfB%W&$IqWXf9>kk_wV0-{P^+z|NoD~bBh@m7^F*r{DK+4rWiN`1Oy}$ z6g2er&!4~l{Q3L$-@kvqi}%wL1_q`&PZ!6KjC*fqMh71<5NHkF6EC<{iG`~uusikt z|MQ<66|SnKo6mi-!uYH*zisfRr*qz{G`#Dzv+8eRpvd7m|Cc|-7ysO)bNXvd$->Wn zk~XR;Eqc4?Lcoh&u`Ti46A$gbGC!Z6>2{_4-U9``d28EWvUY7!3lKXwzdoks1=ouG zjsN6U1TU%MyW;=pT({kqJs$Uc|G(L_$?W_kd$ZGP_Zl6ONS^-wvdvxxyW03$`i)&? zlO13A%K4auyk+TlaW-yw@0sl1ZZ3WeVc%??I$Cen{-x)je}4JPHIKghY^~$E@J7=+ zwe05Qo>@$y%|R`(&o7@+KCn=Qf6l=aXFJB8Nj66N89s3)Oj(k@f%$|sL!i*RgK`S{ z8Js-bE;24?U~up(ZSywST!6|7h;t6da>ZD z%!^AMEVDEeSFf9MJz27 zZw5Vh9?zn*`D*-?8z~|)bIo=(YA#yQ_Hu5(g31dCDQ@y6^Ft4u=-I-x|K`CHJx04H z#2!A;vmyKIfs1`UZ!d`R1pnFl{ol5;N4w`6v5Mjfr)XF-^_%TL$ys6r+D}pe;kqsJi~S@;#{LrSLA<%bCv-|mb_mp6v5u5 znP!{H@!lcuXwKqu6GegokG|RN-)RtXZiK5 rulA+>`z2j+-{}7V&v*M$v)1}IPMPq5Q}4AgC #include #include +#include #include #if defined(QUICK3D_ASSET_UTILS_MODULE) #include @@ -979,6 +980,9 @@ void Qt5InformationNodeInstanceServer::handleNode3DDestroyed([[maybe_unused]] QO QMetaObject::invokeMethod(m_editView3DData.rootItem, "releaseParticleEmitterGizmo", Q_ARG(QVariant, objectToVariant(obj))); #endif + } else if (qobject_cast(obj)) { + QMetaObject::invokeMethod(m_editView3DData.rootItem, "releaseReflectionProbeGizmo", + Q_ARG(QVariant, objectToVariant(obj))); } removeNode3D(obj); #endif @@ -1112,6 +1116,10 @@ void Qt5InformationNodeInstanceServer::resolveSceneRoots() Q_ARG(QVariant, objectToVariant(newRoot)), Q_ARG(QVariant, objectToVariant(node))); #endif + } else if (qobject_cast(node)) { + QMetaObject::invokeMethod(m_editView3DData.rootItem, "updateReflectionProbeGizmoScene", + Q_ARG(QVariant, objectToVariant(newRoot)), + Q_ARG(QVariant, objectToVariant(node))); } } ++it; @@ -1599,7 +1607,7 @@ QList Qt5InformationNodeInstanceServer::createInstances( if (m_editView3DSetupDone) { add3DViewPorts(createdInstances); add3DScenes(createdInstances); - createCameraAndLightGizmos(createdInstances); + createGizmos(createdInstances); } render3DEditView(); @@ -1652,13 +1660,14 @@ void Qt5InformationNodeInstanceServer::handleDynamicAddObjectTimeout() m_dynamicObjectConstructors.clear(); } -void Qt5InformationNodeInstanceServer::createCameraAndLightGizmos( +void Qt5InformationNodeInstanceServer::createGizmos( const QList &instanceList) const { QHash cameras; QHash lights; QHash particleSystems; QHash particleEmitters; + QHash reflectionProbes; for (const ServerNodeInstance &instance : instanceList) { if (instance.isSubclassOf("QQuick3DCamera")) { @@ -1671,6 +1680,8 @@ void Qt5InformationNodeInstanceServer::createCameraAndLightGizmos( || instance.isSubclassOf("QQuick3DParticleAttractor")) && !instance.isSubclassOf("QQuick3DParticleTrailEmitter")) { particleEmitters[find3DSceneRoot(instance)] << instance.internalObject(); + } else if (instance.isSubclassOf("QQuick3DReflectionProbe")) { + reflectionProbes[find3DSceneRoot(instance)] << instance.internalObject(); } } @@ -1715,6 +1726,17 @@ void Qt5InformationNodeInstanceServer::createCameraAndLightGizmos( } ++emitterIt; } + + auto refProbeIt = reflectionProbes.constBegin(); + while (refProbeIt != reflectionProbes.constEnd()) { + const auto refProbeObjs = refProbeIt.value(); + for (auto &obj : refProbeObjs) { + QMetaObject::invokeMethod(m_editView3DData.rootItem, "addReflectionProbeGizmo", + Q_ARG(QVariant, objectToVariant(refProbeIt.key())), + Q_ARG(QVariant, objectToVariant(obj))); + } + ++refProbeIt; + } } void Qt5InformationNodeInstanceServer::add3DViewPorts(const QList &instanceList) @@ -1977,7 +1999,7 @@ void Qt5InformationNodeInstanceServer::setup3DEditView( updateActiveSceneToEditView3D(); - createCameraAndLightGizmos(instanceList); + createGizmos(instanceList); // Queue two renders to make sure icon gizmos update properly render3DEditView(2); diff --git a/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.h b/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.h index 4f7fcd71774..321751cf238 100644 --- a/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.h +++ b/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.h @@ -92,7 +92,7 @@ private: void create3DPreviewView(); void setup3DEditView(const QList &instanceList, const CreateSceneCommand &command); - void createCameraAndLightGizmos(const QList &instanceList) const; + void createGizmos(const QList &instanceList) const; void add3DViewPorts(const QList &instanceList); void add3DScenes(const QList &instanceList); QObject *findView3DForInstance(const ServerNodeInstance &instance) const; From af6dcb66593df384e0d9a319eb5d922b28612b7d Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Mon, 29 Apr 2024 15:29:46 +0300 Subject: [PATCH 036/154] QmlDesigner: Remove "Internal" namespace from the bundle importer Change-Id: If082d7fe724b6db2aaad8c1a6bf56f68bbbb3baf Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Miikka Heikkinen --- .../contentlibrary/contentlibrarybundleimporter.cpp | 4 ++-- .../contentlibrary/contentlibrarybundleimporter.h | 4 ++-- .../contentlibrary/contentlibraryeffectsmodel.cpp | 10 +++++----- .../contentlibrary/contentlibraryeffectsmodel.h | 9 +++------ .../contentlibrary/contentlibrarymaterialsmodel.cpp | 10 +++++----- .../contentlibrary/contentlibrarymaterialsmodel.h | 9 +++------ .../contentlibrary/contentlibraryusermodel.cpp | 10 +++++----- .../contentlibrary/contentlibraryusermodel.h | 9 +++------ 8 files changed, 28 insertions(+), 37 deletions(-) diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarybundleimporter.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarybundleimporter.cpp index 85d42f9d128..d4859305538 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarybundleimporter.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarybundleimporter.cpp @@ -18,7 +18,7 @@ using namespace Utils; -namespace QmlDesigner::Internal { +namespace QmlDesigner { ContentLibraryBundleImporter::ContentLibraryBundleImporter(QObject *parent) : QObject(parent) @@ -336,4 +336,4 @@ FilePath ContentLibraryBundleImporter::resolveBundleImportPath(const QString &bu return bundleImportPath.resolvePath(bundleId); } -} // namespace QmlDesigner::Internal +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarybundleimporter.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarybundleimporter.h index 3a7c22a52e1..10786ead995 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarybundleimporter.h +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarybundleimporter.h @@ -10,7 +10,7 @@ #include #include -namespace QmlDesigner::Internal { +namespace QmlDesigner { class ContentLibraryBundleImporter : public QObject { @@ -48,4 +48,4 @@ private: QHash m_pendingTypes; // }; -} // namespace QmlDesigner::Internal +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectsmodel.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectsmodel.cpp index f6c3f855baa..fb7fde1014c 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectsmodel.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectsmodel.cpp @@ -90,10 +90,10 @@ QHash ContentLibraryEffectsModel::roleNames() const void ContentLibraryEffectsModel::createImporter() { - m_importer = new Internal::ContentLibraryBundleImporter(); + m_importer = new ContentLibraryBundleImporter(); #ifdef QDS_USE_PROJECTSTORAGE connect(m_importer, - &Internal::ContentLibraryBundleImporter::importFinished, + &ContentLibraryBundleImporter::importFinished, this, [&](const QmlDesigner::TypeName &typeName) { m_importerRunning = false; @@ -103,7 +103,7 @@ void ContentLibraryEffectsModel::createImporter() }); #else connect(m_importer, - &Internal::ContentLibraryBundleImporter::importFinished, + &ContentLibraryBundleImporter::importFinished, this, [&](const QmlDesigner::NodeMetaInfo &metaInfo) { m_importerRunning = false; @@ -113,7 +113,7 @@ void ContentLibraryEffectsModel::createImporter() }); #endif - connect(m_importer, &Internal::ContentLibraryBundleImporter::unimportFinished, this, + connect(m_importer, &ContentLibraryBundleImporter::unimportFinished, this, [&](const QmlDesigner::NodeMetaInfo &metaInfo) { Q_UNUSED(metaInfo) m_importerRunning = false; @@ -220,7 +220,7 @@ bool ContentLibraryEffectsModel::bundleExists() const return m_bundleExists; } -Internal::ContentLibraryBundleImporter *ContentLibraryEffectsModel::bundleImporter() const +ContentLibraryBundleImporter *ContentLibraryEffectsModel::bundleImporter() const { return m_importer; } diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectsmodel.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectsmodel.h index 5ac40bbef4b..f63e080b159 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectsmodel.h +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectsmodel.h @@ -11,14 +11,11 @@ namespace QmlDesigner { +class ContentLibraryBundleImporter; class ContentLibraryEffect; class ContentLibraryEffectsCategory; class ContentLibraryWidget; -namespace Internal { -class ContentLibraryBundleImporter; -} - class ContentLibraryEffectsModel : public QAbstractListModel { Q_OBJECT @@ -49,7 +46,7 @@ public: void resetModel(); void updateIsEmpty(); - Internal::ContentLibraryBundleImporter *bundleImporter() const; + ContentLibraryBundleImporter *bundleImporter() const; Q_INVOKABLE void addInstance(QmlDesigner::ContentLibraryEffect *bundleItem); Q_INVOKABLE void removeFromProject(QmlDesigner::ContentLibraryEffect *bundleItem); @@ -77,7 +74,7 @@ private: QStringList m_importerSharedFiles; QList m_bundleCategories; QJsonObject m_bundleObj; - Internal::ContentLibraryBundleImporter *m_importer = nullptr; + ContentLibraryBundleImporter *m_importer = nullptr; bool m_isEmpty = true; bool m_bundleExists = false; diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.cpp index b60ac841d58..1b5f0398982 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.cpp @@ -209,10 +209,10 @@ void ContentLibraryMaterialsModel::downloadSharedFiles(const QDir &targetDir, co void ContentLibraryMaterialsModel::createImporter() { - m_importer = new Internal::ContentLibraryBundleImporter(); + m_importer = new ContentLibraryBundleImporter(); #ifdef QDS_USE_PROJECTSTORAGE connect(m_importer, - &Internal::ContentLibraryBundleImporter::importFinished, + &ContentLibraryBundleImporter::importFinished, this, [&](const QmlDesigner::TypeName &typeName) { m_importerRunning = false; @@ -222,7 +222,7 @@ void ContentLibraryMaterialsModel::createImporter() }); #else connect(m_importer, - &Internal::ContentLibraryBundleImporter::importFinished, + &ContentLibraryBundleImporter::importFinished, this, [&](const QmlDesigner::NodeMetaInfo &metaInfo) { m_importerRunning = false; @@ -232,7 +232,7 @@ void ContentLibraryMaterialsModel::createImporter() }); #endif - connect(m_importer, &Internal::ContentLibraryBundleImporter::unimportFinished, this, + connect(m_importer, &ContentLibraryBundleImporter::unimportFinished, this, [&](const QmlDesigner::NodeMetaInfo &metaInfo) { Q_UNUSED(metaInfo) m_importerRunning = false; @@ -331,7 +331,7 @@ bool ContentLibraryMaterialsModel::matBundleExists() const return m_matBundleExists; } -Internal::ContentLibraryBundleImporter *ContentLibraryMaterialsModel::bundleImporter() const +ContentLibraryBundleImporter *ContentLibraryMaterialsModel::bundleImporter() const { return m_importer; } diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.h index c37ffd39fbf..6b0705e86d9 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.h +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.h @@ -11,14 +11,11 @@ namespace QmlDesigner { +class ContentLibraryBundleImporter; class ContentLibraryMaterial; class ContentLibraryMaterialsCategory; class ContentLibraryWidget; -namespace Internal { -class ContentLibraryBundleImporter; -} - class ContentLibraryMaterialsModel : public QAbstractListModel { Q_OBJECT @@ -53,7 +50,7 @@ public: void updateIsEmpty(); void loadBundle(); - Internal::ContentLibraryBundleImporter *bundleImporter() const; + ContentLibraryBundleImporter *bundleImporter() const; Q_INVOKABLE void applyToSelected(QmlDesigner::ContentLibraryMaterial *mat, bool add = false); Q_INVOKABLE void addToProject(QmlDesigner::ContentLibraryMaterial *mat); @@ -87,7 +84,7 @@ private: QString m_searchText; QList m_bundleCategories; QJsonObject m_matBundleObj; - Internal::ContentLibraryBundleImporter *m_importer = nullptr; + ContentLibraryBundleImporter *m_importer = nullptr; bool m_isEmpty = true; bool m_matBundleExists = false; diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp index 9b53ae4f064..6a511f42bd0 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp @@ -225,10 +225,10 @@ QHash ContentLibraryUserModel::roleNames() const void ContentLibraryUserModel::createImporter() { - m_importer = new Internal::ContentLibraryBundleImporter(); + m_importer = new ContentLibraryBundleImporter(); #ifdef QDS_USE_PROJECTSTORAGE connect(m_importer, - &Internal::ContentLibraryBundleImporter::importFinished, + &ContentLibraryBundleImporter::importFinished, this, [&](const QmlDesigner::TypeName &typeName) { m_importerRunning = false; @@ -238,7 +238,7 @@ void ContentLibraryUserModel::createImporter() }); #else connect(m_importer, - &Internal::ContentLibraryBundleImporter::importFinished, + &ContentLibraryBundleImporter::importFinished, this, [&](const QmlDesigner::NodeMetaInfo &metaInfo) { m_importerRunning = false; @@ -248,7 +248,7 @@ void ContentLibraryUserModel::createImporter() }); #endif - connect(m_importer, &Internal::ContentLibraryBundleImporter::unimportFinished, this, + connect(m_importer, &ContentLibraryBundleImporter::unimportFinished, this, [&](const QmlDesigner::NodeMetaInfo &metaInfo) { Q_UNUSED(metaInfo) m_importerRunning = false; @@ -370,7 +370,7 @@ bool ContentLibraryUserModel::matBundleExists() const return m_matBundleExists; } -Internal::ContentLibraryBundleImporter *ContentLibraryUserModel::bundleImporter() const +ContentLibraryBundleImporter *ContentLibraryUserModel::bundleImporter() const { return m_importer; } diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.h index 6ac2160e9b8..35c2de662be 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.h +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.h @@ -12,16 +12,13 @@ QT_FORWARD_DECLARE_CLASS(QUrl) namespace QmlDesigner { +class ContentLibraryBundleImporter; class ContentLibraryEffect; class ContentLibraryMaterial; class ContentLibraryTexture; class ContentLibraryWidget; class NodeMetaInfo; -namespace Internal { -class ContentLibraryBundleImporter; -} - class ContentLibraryUserModel : public QAbstractListModel { Q_OBJECT @@ -70,7 +67,7 @@ public: void loadMaterialBundle(); void loadTextureBundle(); - Internal::ContentLibraryBundleImporter *bundleImporter() const; + ContentLibraryBundleImporter *bundleImporter() const; Q_INVOKABLE void applyToSelected(QmlDesigner::ContentLibraryMaterial *mat, bool add = false); Q_INVOKABLE void addToProject(QmlDesigner::ContentLibraryMaterial *mat); @@ -114,7 +111,7 @@ private: QStringList m_userCategories; QJsonObject m_bundleObj; - Internal::ContentLibraryBundleImporter *m_importer = nullptr; + ContentLibraryBundleImporter *m_importer = nullptr; bool m_isEmpty = true; bool m_matBundleExists = false; From 465a2e7ac42af2135e0dde04cc1b15c62134f3ab Mon Sep 17 00:00:00 2001 From: Shrief Gabr Date: Fri, 12 Apr 2024 16:38:30 +0300 Subject: [PATCH 037/154] QmlDesigner: Fix fly mode speed on macOS Task-number: QDS-12337 Change-Id: I7590cd79a8d3b0b27cad7edb5e11be091223b6fb Reviewed-by: Miikka Heikkinen --- .../components/edit3d/edit3dcanvas.cpp | 18 ++++++++++++++++-- .../components/edit3d/edit3dcanvas.h | 2 ++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dcanvas.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dcanvas.cpp index 4f4f08f1e18..63d5e958b19 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dcanvas.cpp +++ b/src/plugins/qmldesigner/components/edit3d/edit3dcanvas.cpp @@ -81,11 +81,20 @@ QWidget *Edit3DCanvas::busyIndicator() const return m_busyIndicator; } +#ifdef Q_OS_MACOS +extern "C" bool AXIsProcessTrusted(); +#endif + void Edit3DCanvas::setFlyMode(bool enabled, const QPoint &pos) { if (m_flyMode == enabled) return; +#ifdef Q_OS_MACOS + if (!AXIsProcessTrusted()) + m_isTrusted = false; +#endif + m_flyMode = enabled; if (enabled) { @@ -190,7 +199,8 @@ void Edit3DCanvas::mouseMoveEvent(QMouseEvent *e) // We notify explicit camera rotation need for puppet rather than rely in mouse events, // as mouse isn't grabbed on puppet side and can't handle fast movements that go out of // edit camera mouse area. This also simplifies split view handling. - QPointF diff = m_hiddenCursorPos - e->globalPos(); + QPointF diff = m_isTrusted ? (m_hiddenCursorPos - e->globalPos()) : (m_lastCursorPos - e->globalPos()); + if (e->buttons() == (Qt::LeftButton | Qt::RightButton)) { m_parent->view()->emitView3DAction(View3DActionType::EditCameraMove, QVector3D{float(-diff.x()), float(-diff.y()), 0.f}); @@ -201,7 +211,11 @@ void Edit3DCanvas::mouseMoveEvent(QMouseEvent *e) // Skip first move to avoid undesirable jump occasionally when initiating flight mode m_flyModeFirstUpdate = false; } - QCursor::setPos(m_hiddenCursorPos); + + if (m_isTrusted) + QCursor::setPos(m_hiddenCursorPos); + else + m_lastCursorPos = e->globalPos(); } } diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dcanvas.h b/src/plugins/qmldesigner/components/edit3d/edit3dcanvas.h index 7f043520b33..16c1063dd61 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dcanvas.h +++ b/src/plugins/qmldesigner/components/edit3d/edit3dcanvas.h @@ -53,10 +53,12 @@ private: qint32 m_activeScene = -1; QElapsedTimer m_usageTimer; qreal m_opacity = 1.0; + bool m_isTrusted = true; QWidget *m_busyIndicator = nullptr; bool m_flyMode = false; QPoint m_flyModeStartCursorPos; QPoint m_hiddenCursorPos; + QPoint m_lastCursorPos; qint64 m_flyModeStartTime = 0; bool m_flyModeFirstUpdate = false; bool m_contextMenuPending = false; From 25f80810320800ebab055831af55b1d23f64601e Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Mon, 29 Apr 2024 17:28:39 +0300 Subject: [PATCH 038/154] QmlDesigner: Refactor update bundle imported state Remove the useless round trip from model to view and back Change-Id: I38a5826e165014f64d7855139b58e9691b0f6310 Reviewed-by: Miikka Heikkinen --- .../contentlibraryeffectsmodel.cpp | 23 +++++- .../contentlibraryeffectsmodel.h | 2 +- .../contentlibrarymaterialsmodel.cpp | 25 +++++- .../contentlibrarymaterialsmodel.h | 2 +- .../contentlibraryusermodel.cpp | 36 ++++++--- .../contentlibrary/contentlibraryusermodel.h | 4 +- .../contentlibrary/contentlibraryview.cpp | 80 +------------------ .../contentlibrary/contentlibraryview.h | 3 - 8 files changed, 75 insertions(+), 100 deletions(-) diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectsmodel.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectsmodel.cpp index fb7fde1014c..b496eb71ee9 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectsmodel.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectsmodel.cpp @@ -98,8 +98,10 @@ void ContentLibraryEffectsModel::createImporter() [&](const QmlDesigner::TypeName &typeName) { m_importerRunning = false; emit importerRunningChanged(); - if (typeName.size()) + if (typeName.size()) { emit bundleItemImported(typeName); + updateImportedState(); + } }); #else connect(m_importer, @@ -108,8 +110,10 @@ void ContentLibraryEffectsModel::createImporter() [&](const QmlDesigner::NodeMetaInfo &metaInfo) { m_importerRunning = false; emit importerRunningChanged(); - if (metaInfo.isValid()) + if (metaInfo.isValid()) { emit bundleItemImported(metaInfo); + updateImportedState(); + } }); #endif @@ -119,6 +123,7 @@ void ContentLibraryEffectsModel::createImporter() m_importerRunning = false; emit importerRunningChanged(); emit bundleItemUnimported(metaInfo); + updateImportedState(); }); resetModel(); @@ -244,8 +249,20 @@ void ContentLibraryEffectsModel::setSearchText(const QString &searchText) updateIsEmpty(); } -void ContentLibraryEffectsModel::updateImportedState(const QStringList &importedItems) +void ContentLibraryEffectsModel::updateImportedState() { + if (!m_importer) + return; + + QString bundleId = m_bundleObj.value("id").toString(); + Utils::FilePath bundlePath = m_importer->resolveBundleImportPath(bundleId); + + QStringList importedItems; + if (bundlePath.exists()) { + importedItems = transform(bundlePath.dirEntries({{"*.qml"}, QDir::Files}), + [](const Utils::FilePath &f) { return f.baseName(); }); + } + bool changed = false; for (ContentLibraryEffectsCategory *cat : std::as_const(m_bundleCategories)) changed |= cat->updateImportedState(importedItems); diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectsmodel.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectsmodel.h index f63e080b159..8accec8819e 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectsmodel.h +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectsmodel.h @@ -35,7 +35,7 @@ public: void loadBundle(); void setSearchText(const QString &searchText); - void updateImportedState(const QStringList &importedItems); + void updateImportedState(); void setQuick3DImportVersion(int major, int minor); diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.cpp index 1b5f0398982..8c035ea1b0b 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.cpp @@ -217,8 +217,10 @@ void ContentLibraryMaterialsModel::createImporter() [&](const QmlDesigner::TypeName &typeName) { m_importerRunning = false; emit importerRunningChanged(); - if (typeName.size()) + if (typeName.size()) { emit bundleMaterialImported(typeName); + updateImportedState(); + } }); #else connect(m_importer, @@ -227,8 +229,10 @@ void ContentLibraryMaterialsModel::createImporter() [&](const QmlDesigner::NodeMetaInfo &metaInfo) { m_importerRunning = false; emit importerRunningChanged(); - if (metaInfo.isValid()) + if (metaInfo.isValid()) { emit bundleMaterialImported(metaInfo); + updateImportedState(); + } }); #endif @@ -238,6 +242,7 @@ void ContentLibraryMaterialsModel::createImporter() m_importerRunning = false; emit importerRunningChanged(); emit bundleMaterialUnimported(metaInfo); + updateImportedState(); }); resetModel(); @@ -355,11 +360,23 @@ void ContentLibraryMaterialsModel::setSearchText(const QString &searchText) updateIsEmpty(); } -void ContentLibraryMaterialsModel::updateImportedState(const QStringList &importedMats) +void ContentLibraryMaterialsModel::updateImportedState() { + if (!m_importer) + return; + + QString bundleId = m_matBundleObj.value("id").toString(); + Utils::FilePath bundlePath = m_importer->resolveBundleImportPath(bundleId); + + QStringList importedItems; + if (bundlePath.exists()) { + importedItems = transform(bundlePath.dirEntries({{"*.qml"}, QDir::Files}), + [](const Utils::FilePath &f) { return f.baseName(); }); + } + bool changed = false; for (ContentLibraryMaterialsCategory *cat : std::as_const(m_bundleCategories)) - changed |= cat->updateImportedState(importedMats); + changed |= cat->updateImportedState(importedItems); if (changed) resetModel(); diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.h index 6b0705e86d9..93a353b6f04 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.h +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.h @@ -35,7 +35,7 @@ public: QHash roleNames() const override; void setSearchText(const QString &searchText); - void updateImportedState(const QStringList &importedMats); + void updateImportedState(); void setQuick3DImportVersion(int major, int minor); diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp index 6a511f42bd0..e72b80e296b 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp @@ -209,7 +209,7 @@ TypeName ContentLibraryUserModel::qmlToModule(const QString &qmlName) const { return QLatin1String("%1.%2.%3").arg(QmlDesignerPlugin::instance()->documentManager() .generatedComponentUtils().componentBundlesTypePrefix(), - m_bundleId, + m_bundleIdMaterial, qmlName.chopped(4)).toLatin1(); // chopped(4): remove .qml } @@ -233,8 +233,10 @@ void ContentLibraryUserModel::createImporter() [&](const QmlDesigner::TypeName &typeName) { m_importerRunning = false; emit importerRunningChanged(); - if (typeName.size()) + if (typeName.size()) { emit bundleMaterialImported(typeName); + updateImportedState(); + } }); #else connect(m_importer, @@ -243,8 +245,10 @@ void ContentLibraryUserModel::createImporter() [&](const QmlDesigner::NodeMetaInfo &metaInfo) { m_importerRunning = false; emit importerRunningChanged(); - if (metaInfo.isValid()) + if (metaInfo.isValid()) { emit bundleMaterialImported(metaInfo); + updateImportedState(); + } }); #endif @@ -254,6 +258,7 @@ void ContentLibraryUserModel::createImporter() m_importerRunning = false; emit importerRunningChanged(); emit bundleMaterialUnimported(metaInfo); + updateImportedState(); }); resetModel(); @@ -299,7 +304,7 @@ void ContentLibraryUserModel::loadMaterialBundle() } } - m_bundleId = m_bundleObj.value("id").toString(); + m_bundleIdMaterial = m_bundleObj.value("id").toString(); // parse materials const QJsonObject matsObj = m_bundleObj.value("materials").toObject(); @@ -390,15 +395,28 @@ void ContentLibraryUserModel::setSearchText(const QString &searchText) updateIsEmpty(); } -void ContentLibraryUserModel::updateImportedState(const QStringList &importedMats) +void ContentLibraryUserModel::updateImportedState() { + if (!m_importer) + return; + + QString bundleId = m_bundleObj.value("id").toString(); + Utils::FilePath bundlePath = m_importer->resolveBundleImportPath(bundleId); + + QStringList importedItems; + if (bundlePath.exists()) { + importedItems = transform(bundlePath.dirEntries({{"*.qml"}, QDir::Files}), + [](const Utils::FilePath &f) { return f.baseName(); }); + } + bool changed = false; - for (ContentLibraryMaterial *mat : std::as_const(m_userMaterials)) - changed |= mat->setImported(importedMats.contains(mat->qml().chopped(4))); + changed |= mat->setImported(importedItems.contains(mat->qml().chopped(4))); - if (changed) - resetModel(); + if (changed) { + int matSectionIdx = 0; + emit dataChanged(index(matSectionIdx), index(matSectionIdx)); + } } void ContentLibraryUserModel::setQuick3DImportVersion(int major, int minor) diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.h index 35c2de662be..e4bf030c57c 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.h +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.h @@ -41,7 +41,7 @@ public: QHash roleNames() const override; void setSearchText(const QString &searchText); - void updateImportedState(const QStringList &importedMats); + void updateImportedState(); QPair getUniqueLibMaterialNameAndQml(const QString &matName) const; TypeName qmlToModule(const QString &qmlName) const; @@ -102,7 +102,7 @@ private: ContentLibraryWidget *m_widget = nullptr; QString m_searchText; - QString m_bundleId; + QString m_bundleIdMaterial; QList m_userMaterials; QList m_userTextures; diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp index 5981907f6c5..fde46a7dc26 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp @@ -114,7 +114,6 @@ WidgetInfo ContentLibraryView::widgetInfo() this, [&](const QmlDesigner::TypeName &typeName) { applyBundleMaterialToDropTarget({}, typeName); - updateBundleMaterialsImportedState(); }); #else connect(materialsModel, @@ -122,7 +121,6 @@ WidgetInfo ContentLibraryView::widgetInfo() this, [&](const QmlDesigner::NodeMetaInfo &metaInfo) { applyBundleMaterialToDropTarget({}, metaInfo); - updateBundleMaterialsImportedState(); }); #endif @@ -141,9 +139,6 @@ WidgetInfo ContentLibraryView::widgetInfo() }); }); - connect(materialsModel, &ContentLibraryMaterialsModel::bundleMaterialUnimported, this, - &ContentLibraryView::updateBundleMaterialsImportedState); - ContentLibraryEffectsModel *effectsModel = m_widget->effectsModel().data(); #ifdef QDS_USE_PROJECTSTORAGE @@ -167,7 +162,6 @@ WidgetInfo ContentLibraryView::widgetInfo() selectModelNode(newEffNode); }); - updateBundleEffectsImportedState(); m_bundleEffectTarget = {}; m_bundleEffectPos = {}; }); @@ -196,7 +190,6 @@ WidgetInfo ContentLibraryView::widgetInfo() selectModelNode(newEffNode); }); - updateBundleEffectsImportedState(); m_bundleEffectTarget = {}; m_bundleEffectPos = {}; }); @@ -212,9 +205,6 @@ WidgetInfo ContentLibraryView::widgetInfo() }); }); - connect(effectsModel, &ContentLibraryEffectsModel::bundleItemUnimported, this, - &ContentLibraryView::updateBundleEffectsImportedState); - connectUserBundle(); } @@ -252,7 +242,6 @@ void ContentLibraryView::connectUserBundle() this, [&](const QmlDesigner::TypeName &typeName) { applyBundleMaterialToDropTarget({}, typeName); - updateBundleUserMaterialsImportedState(); }); #else connect(userModel, @@ -260,7 +249,6 @@ void ContentLibraryView::connectUserBundle() this, [&](const QmlDesigner::NodeMetaInfo &metaInfo) { applyBundleMaterialToDropTarget({}, metaInfo); - updateBundleUserMaterialsImportedState(); }); #endif @@ -278,9 +266,6 @@ void ContentLibraryView::connectUserBundle() }); }); }); - - connect(userModel, &ContentLibraryUserModel::bundleMaterialUnimported, this, - &ContentLibraryView::updateBundleUserMaterialsImportedState); } void ContentLibraryView::modelAttached(Model *model) @@ -308,9 +293,9 @@ void ContentLibraryView::modelAttached(Model *model) m_widget->userModel()->loadMaterialBundle(); m_widget->userModel()->loadTextureBundle(); - updateBundleMaterialsImportedState(); - updateBundleEffectsImportedState(); - updateBundleUserMaterialsImportedState(); + m_widget->materialsModel()->updateImportedState(); + m_widget->effectsModel()->updateImportedState(); + m_widget->userModel()->updateImportedState(); } void ContentLibraryView::modelAboutToBeDetached(Model *model) @@ -769,65 +754,6 @@ ModelNode ContentLibraryView::createMaterial(const NodeMetaInfo &metaInfo) } #endif -void ContentLibraryView::updateBundleMaterialsImportedState() -{ - using namespace Utils; - - if (!m_widget->materialsModel()->bundleImporter()) - return; - - QStringList importedBundleMats; - - // TODO: this will be refactored next: no need for the round trip from model to view then back to model - // (same applies for the similar cases for effects and user material bundles) - FilePath materialBundlePath = m_widget->materialsModel()->bundleImporter()->resolveBundleImportPath("MaterialBundle"); - - if (materialBundlePath.exists()) { - importedBundleMats = transform(materialBundlePath.dirEntries({{"*.qml"}, QDir::Files}), - [](const FilePath &f) { return f.fileName().chopped(4); }); - } - - m_widget->materialsModel()->updateImportedState(importedBundleMats); -} - -void ContentLibraryView::updateBundleUserMaterialsImportedState() -{ - using namespace Utils; - - if (!m_widget->userModel()->bundleImporter()) - return; - - QStringList importedBundleMats; - - FilePath bundlePath = m_widget->userModel()->bundleImporter()->resolveBundleImportPath("UserMaterialBundle"); - - if (bundlePath.exists()) { - importedBundleMats = transform(bundlePath.dirEntries({{"*.qml"}, QDir::Files}), - [](const FilePath &f) { return f.fileName().chopped(4); }); - } - - m_widget->userModel()->updateImportedState(importedBundleMats); -} - -void ContentLibraryView::updateBundleEffectsImportedState() -{ - using namespace Utils; - - if (!m_widget->effectsModel()->bundleImporter()) - return; - - QStringList importedBundleEffs; - - FilePath bundlePath = m_widget->effectsModel()->bundleImporter()->resolveBundleImportPath("EffectBundle"); - - if (bundlePath.exists()) { - importedBundleEffs = transform(bundlePath.dirEntries({{"*.qml"}, QDir::Files}), - [](const FilePath &f) { return f.fileName().chopped(4); }); - } - - m_widget->effectsModel()->updateImportedState(importedBundleEffs); -} - void ContentLibraryView::updateBundlesQuick3DVersion() { bool hasImport = false; diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.h index 03d42fa8bcd..4ff2a5db8fd 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.h +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.h @@ -50,9 +50,6 @@ public: private: void connectUserBundle(); void active3DSceneChanged(qint32 sceneId); - void updateBundleMaterialsImportedState(); - void updateBundleUserMaterialsImportedState(); - void updateBundleEffectsImportedState(); void updateBundlesQuick3DVersion(); void addLibMaterial(const ModelNode &mat, const QPixmap &icon); void addLibAssets(const QStringList &paths); From 8ed54cb3a8aa51ac826a1ddf1fdb0c103747ccd6 Mon Sep 17 00:00:00 2001 From: Teea Poldsam Date: Tue, 30 Apr 2024 10:01:52 +0300 Subject: [PATCH 039/154] Doc: Update Applying States in Flows Fixes: QDS-11506 Change-Id: Ic2e9993b96cf9ecc3f40fc9701f9044945610a99 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Johanna Vanhatapio --- .../images/studio-flow-item-component.webp | Bin 0 -> 4960 bytes .../studio-flow-states-item-properties.webp | Bin 0 -> 12408 bytes .../src/qtdesignstudio-app-flows.qdoc | 40 +++++++++--------- 3 files changed, 21 insertions(+), 19 deletions(-) create mode 100644 doc/qtdesignstudio/images/studio-flow-item-component.webp create mode 100644 doc/qtdesignstudio/images/studio-flow-states-item-properties.webp diff --git a/doc/qtdesignstudio/images/studio-flow-item-component.webp b/doc/qtdesignstudio/images/studio-flow-item-component.webp new file mode 100644 index 0000000000000000000000000000000000000000..8c1ea982b13c4a697ed18fbd5590b4e649f548e7 GIT binary patch literal 4960 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 o)0J!Mvv}_2oP6rjHi>IjoX#yXh($ literal 0 HcmV?d00001 diff --git a/doc/qtdesignstudio/images/studio-flow-states-item-properties.webp b/doc/qtdesignstudio/images/studio-flow-states-item-properties.webp new file mode 100644 index 0000000000000000000000000000000000000000..14e024dab539d169988fdd0e1281801579f620c0 GIT binary patch literal 12408 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 feQiA8*PBtAe=knpN7ViAzS+6g \uicontrol {Flow View}. + Use \l{Working with States}{states} in flows to modify how the \l{glossary-component} + {component} properties change in flow items. For this purpose, use the \uicontrol {Flow Item} + component available in \uicontrol Components > \uicontrol {Flow View}. + + To apply states in flows: \list 1 \li Select \uicontrol File > \uicontrol {New File} > \uicontrol {Qt Quick Files} > \uicontrol {Flow Item} to create a flow item. - \li In \l States, add states to the flow item. - \li Open the .ui.qml file that contains the \l{Adding Flow Views} - {flow view} in the \l {2D} view and drag the flow item to the - flow view in the \l Navigator or \l {2D} view. - \li Drag an empty \uicontrol {Flow Item} component from - \uicontrol Components > \uicontrol {Flow View} - to the flow for each state that you added. - \li In \l Properties, in the \uicontrol {State change target} - field, select the flow item that you created using the wizard. - \image studio-flow-item-properties.png "Flow Item properties" - \li In the \uicontrol {Target state} field, select the state to - apply to the flow item. + \li In the \l States view, add states to the flow item. + \li In the \l Projects view, open the .ui.qml file that contains the \l{Adding Flow Views} + {flow view}, and drag the flow item from \uicontrol Components > + \uicontrol {My Components} to the flow view in the \l Navigator or \l 2D view. + \li From \uicontrol Components > \uicontrol {Flow View}, drag an empty + \uicontrol {Flow Item} component (1) to the flow view for each state that you added. + \image studio-flow-item-component.webp "Flow Item in the Components view." + \li In the \uicontrol Navigator or \uicontrol 2D view, select an empty + \uicontrol {Flow Item} to open the \l Properties view. + \li In the \uicontrol {State change target} field, select the flow item that you created + using the wizard. + \li In the \uicontrol {Target state} field, select the state to apply to the flow item. + \image studio-flow-states-item-properties.webp "Flow Item properties" \endlist - You can now add \l{Adding Action Areas and Transitions}{action areas} and - \l{Simulating Conditions}{flow decisions} to apply the different states. + To apply the different states to your application flow, add + \l{Adding Action Areas and Transitions}{action areas} and + \l{Simulating Conditions}{flow decisions} to the flow items. */ /*! From 6cc8c13c7c6f2efecf37a6ad92cf64611094c4bb Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Tue, 30 Apr 2024 13:39:05 +0300 Subject: [PATCH 040/154] QmlDesigner: Tweak generated components folders Old type names were too long, shortened various names: "GeneratedComponents" -> "Generated" "ComponentBundles" -> "Bundles" "MaterialBundle" -> "Materials" "EffectBundle" -> "Effects" "UserMaterialBundle" -> "UserMaterials" Fixes: QDS-12629 Change-Id: I3a49e57bbc8d401cadd8c5de6648c7229579d2b2 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Reviewed-by: Mahmoud Badri --- .../imports/HelperWidgets/UrlChooser.qml | 2 +- .../projects/application-3d/wizard.json | 2 +- .../application-extended-3d/wizard.json | 2 +- .../projects/application/wizard.json | 2 +- .../projects/desktop-launcher/wizard.json | 2 +- .../projects/mobile-scroll/wizard.json | 2 +- .../projects/mobile-stack/wizard.json | 2 +- .../projects/mobile-swipe/wizard.json | 2 +- .../contentlibraryeffectsmodel.cpp | 13 ++--- .../contentlibrarymaterialsmodel.cpp | 13 ++--- .../contentlibraryusermodel.cpp | 7 ++- .../contentlibrary/contentlibraryview.cpp | 1 + .../itemlibrary/itemlibrarymodel.cpp | 7 ++- .../choosefrompropertylistdialog.cpp | 7 ++- .../designercore/generatedcomponentutils.cpp | 53 ++++++++++++++++++- .../designercore/generatedcomponentutils.h | 4 ++ .../qmldesigner/qmldesignerconstants.h | 9 +++- 17 files changed, 98 insertions(+), 32 deletions(-) diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/UrlChooser.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/UrlChooser.qml index 0e42515c76c..8dadd1db2c2 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/UrlChooser.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/UrlChooser.qml @@ -451,7 +451,7 @@ Row { // Prefer hiding generated component files rather than other project files let listIndex = nameMap.get(item.fileName) let absPath = comboBox.listModel.get(listIndex).absoluteFilePath - if (absPath.includes("/GeneratedComponents/") || absPath.includes("/asset_imports/")) { + if (absPath.includes("/Generated/") || absPath.includes("/asset_imports/")) { comboBox.listModel.set(listIndex, { absoluteFilePath: item.absoluteFilePath, relativeFilePath: item.relativeFilePath, 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 a5d0d7539c0..a897fa421ae 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/application-3d/wizard.json +++ b/share/qtcreator/qmldesigner/studio_templates/projects/application-3d/wizard.json @@ -18,7 +18,7 @@ { "key": "ProjectPluginClassName", "value": "%{ProjectName}Plugin" }, { "key": "QmlProjectFileName", "value": "%{JS: Util.fileName('%{ProjectName}', 'qmlproject')}" }, { "key": "IsQt6Project", "value": "%{JS: value('QtQuickVersion') !== '2.15' }" }, - { "key": "AssetDir", "value": "GeneratedComponents" }, + { "key": "AssetDir", "value": "Generated" }, { "key": "ContentDir", "value": "%{ProjectName}Content" }, { "key": "ImportModuleName", "value": "%{ProjectName}" }, { "key": "UIClassName", "value": "Screen01" }, diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/application-extended-3d/wizard.json b/share/qtcreator/qmldesigner/studio_templates/projects/application-extended-3d/wizard.json index 5f2e5bfcafd..ccd522f7c84 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/application-extended-3d/wizard.json +++ b/share/qtcreator/qmldesigner/studio_templates/projects/application-extended-3d/wizard.json @@ -18,7 +18,7 @@ { "key": "ProjectPluginClassName", "value": "%{ProjectName}Plugin" }, { "key": "QmlProjectFileName", "value": "%{JS: Util.fileName('%{ProjectName}', 'qmlproject')}" }, { "key": "IsQt6Project", "value": "%{JS: value('QtQuickVersion') !== '2.15' }" }, - { "key": "AssetDir", "value": "GeneratedComponents" }, + { "key": "AssetDir", "value": "Generated" }, { "key": "ContentDir", "value": "%{ProjectName}Content" }, { "key": "ImportModuleName", "value": "%{ProjectName}" }, { "key": "UIClassName", "value": "Screen01" }, diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/application/wizard.json b/share/qtcreator/qmldesigner/studio_templates/projects/application/wizard.json index 41fe2df289a..f0f98c33315 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/application/wizard.json +++ b/share/qtcreator/qmldesigner/studio_templates/projects/application/wizard.json @@ -17,7 +17,7 @@ { "key": "ProjectPluginName", "value": "%{ProjectName}plugin" }, { "key": "ProjectPluginClassName", "value": "%{ProjectName}Plugin" }, { "key": "QmlProjectFileName", "value": "%{JS: Util.fileName('%{ProjectName}', 'qmlproject')}" }, - { "key": "AssetDir", "value": "GeneratedComponents" }, + { "key": "AssetDir", "value": "Generated" }, { "key": "ContentDir", "value": "%{ProjectName}Content" }, { "key": "ImportModuleName", "value": "%{ProjectName}" }, { "key": "UIClassName", "value": "Screen01" }, 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 ddaf5021543..c15d4dbf744 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/desktop-launcher/wizard.json +++ b/share/qtcreator/qmldesigner/studio_templates/projects/desktop-launcher/wizard.json @@ -17,7 +17,7 @@ { "key": "ProjectPluginName", "value": "%{ProjectName}plugin" }, { "key": "ProjectPluginClassName", "value": "%{ProjectName}Plugin" }, { "key": "QmlProjectFileName", "value": "%{JS: Util.fileName('%{ProjectName}', 'qmlproject')}" }, - { "key": "AssetDir", "value": "GeneratedComponents" }, + { "key": "AssetDir", "value": "Generated" }, { "key": "ContentDir", "value": "%{ProjectName}Content" }, { "key": "ImportModuleName", "value": "%{ProjectName}" }, { "key": "UIClassName", "value": "Screen01" }, 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 585a73aa907..d9e4b979083 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/mobile-scroll/wizard.json +++ b/share/qtcreator/qmldesigner/studio_templates/projects/mobile-scroll/wizard.json @@ -17,7 +17,7 @@ { "key": "ProjectPluginName", "value": "%{ProjectName}plugin" }, { "key": "ProjectPluginClassName", "value": "%{ProjectName}Plugin" }, { "key": "QmlProjectFileName", "value": "%{JS: Util.fileName('%{ProjectName}', 'qmlproject')}" }, - { "key": "AssetDir", "value": "GeneratedComponents" }, + { "key": "AssetDir", "value": "Generated" }, { "key": "ContentDir", "value": "%{ProjectName}Content" }, { "key": "ImportModuleName", "value": "%{ProjectName}" }, { "key": "UIClassName", "value": "Screen01" }, 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 a44a1429bee..910c32a42cf 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/mobile-stack/wizard.json +++ b/share/qtcreator/qmldesigner/studio_templates/projects/mobile-stack/wizard.json @@ -17,7 +17,7 @@ { "key": "ProjectPluginName", "value": "%{ProjectName}plugin" }, { "key": "ProjectPluginClassName", "value": "%{ProjectName}Plugin" }, { "key": "QmlProjectFileName", "value": "%{JS: Util.fileName('%{ProjectName}', 'qmlproject')}" }, - { "key": "AssetDir", "value": "GeneratedComponents" }, + { "key": "AssetDir", "value": "Generated" }, { "key": "ContentDir", "value": "%{ProjectName}Content" }, { "key": "ImportModuleName", "value": "%{ProjectName}" }, { "key": "IsQt6Project", "value": "%{JS: value('QtQuickVersion') !== '2.15' }" }, 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 b3f70a8b797..1f5be300e16 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/mobile-swipe/wizard.json +++ b/share/qtcreator/qmldesigner/studio_templates/projects/mobile-swipe/wizard.json @@ -17,7 +17,7 @@ { "key": "ProjectPluginName", "value": "%{ProjectName}plugin" }, { "key": "ProjectPluginClassName", "value": "%{ProjectName}Plugin" }, { "key": "QmlProjectFileName", "value": "%{JS: Util.fileName('%{ProjectName}', 'qmlproject')}" }, - { "key": "AssetDir", "value": "GeneratedComponents" }, + { "key": "AssetDir", "value": "Generated" }, { "key": "ContentDir", "value": "%{ProjectName}Content" }, { "key": "ImportModuleName", "value": "%{ProjectName}" }, { "key": "IsQt6Project", "value": "%{JS: value('QtQuickVersion') !== '2.15' }" }, diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectsmodel.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectsmodel.cpp index b496eb71ee9..b0ce0b235a2 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectsmodel.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectsmodel.cpp @@ -172,7 +172,11 @@ void ContentLibraryEffectsModel::loadBundle() } } - QString bundleId = m_bundleObj.value("id").toString(); + QString bundleType = QmlDesignerPlugin::instance()->documentManager() + .generatedComponentUtils().effectsBundleType(); + + // Substitute correct id to avoid issues with old bundles + m_bundleObj["id"] = bundleType.split('.').last(); const QJsonObject catsObj = m_bundleObj.value("categories").toObject(); const QStringList categories = catsObj.keys(); @@ -191,11 +195,8 @@ void ContentLibraryEffectsModel::loadBundle() QUrl icon = QUrl::fromLocalFile(bundleDir.filePath(itemObj.value("icon").toString())); QString qml = itemObj.value("qml").toString(); - TypeName type = QLatin1String("%1.%2.%3") - .arg(QmlDesignerPlugin::instance()->documentManager() - .generatedComponentUtils().componentBundlesTypePrefix(), - bundleId, - qml.chopped(4)).toLatin1(); // chopped(4): remove .qml + TypeName type = QLatin1String("%1.%2") + .arg(bundleType, qml.chopped(4)).toLatin1(); // chopped(4): remove .qml auto bundleItem = new ContentLibraryEffect(category, item, qml, type, icon, files); diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.cpp index 8c035ea1b0b..c6d0f04a14e 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.cpp @@ -273,7 +273,11 @@ void ContentLibraryMaterialsModel::loadMaterialBundle(const QDir &matBundleDir) } } - QString bundleId = m_matBundleObj.value("id").toString(); + QString bundleType = QmlDesignerPlugin::instance()->documentManager() + .generatedComponentUtils().materialsBundleType(); + + // Substitute correct id to avoid issues with old bundles + m_matBundleObj["id"] = bundleType.split('.').last(); const QJsonObject catsObj = m_matBundleObj.value("categories").toObject(); const QStringList categories = catsObj.keys(); @@ -292,11 +296,8 @@ void ContentLibraryMaterialsModel::loadMaterialBundle(const QDir &matBundleDir) QUrl icon = QUrl::fromLocalFile(matBundleDir.filePath(matObj.value("icon").toString())); QString qml = matObj.value("qml").toString(); - TypeName type = QLatin1String("%1.%2.%3") - .arg(QmlDesignerPlugin::instance()->documentManager() - .generatedComponentUtils().componentBundlesTypePrefix(), - bundleId, - qml.chopped(4)).toLatin1(); // chopped(4): remove .qml + TypeName type = QLatin1String("%1.%2") + .arg(bundleType, qml.chopped(4)).toLatin1(); // chopped(4): remove .qml auto bundleMat = new ContentLibraryMaterial(category, matName, qml, type, icon, files, m_downloadPath, m_baseUrl); diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp index e72b80e296b..fcd1096ac11 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp @@ -282,7 +282,7 @@ void ContentLibraryUserModel::loadMaterialBundle() auto jsonFilePath = Utils::FilePath::fromString(bundleDir.filePath("user_materials_bundle.json")); if (!jsonFilePath.exists()) { QString jsonContent = "{\n"; - jsonContent += " \"id\": \"UserMaterialBundle\",\n"; + jsonContent += " \"id\": \"UserMaterials\",\n"; jsonContent += " \"materials\": {\n"; jsonContent += " }\n"; jsonContent += "}"; @@ -304,7 +304,10 @@ void ContentLibraryUserModel::loadMaterialBundle() } } - m_bundleIdMaterial = m_bundleObj.value("id").toString(); + m_bundleIdMaterial = "UserMaterials"; + + // Substitute correct id to avoid issues with old bundles + m_bundleObj["id"] = m_bundleIdMaterial; // parse materials const QJsonObject matsObj = m_bundleObj.value("materials").toObject(); diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp index fde46a7dc26..52c28f41b64 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp @@ -23,6 +23,7 @@ #include "utils3d.h" #include +#include #include #include diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp index 3bff2105209..837767cbe63 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp @@ -316,9 +316,12 @@ void ItemLibraryModel::update(Model *model) GeneratedComponentUtils compUtils = QmlDesignerPlugin::instance()->documentManager() .generatedComponentUtils(); + const QString compBundlePrefix = compUtils.componentBundlesTypePrefix(); QStringList excludedImports { - compUtils.componentBundlesTypePrefix() + ".MaterialBundle", - compUtils.componentBundlesTypePrefix() + ".EffectBundle" + compBundlePrefix + '.' + QLatin1String(Constants::COMPONENT_BUNDLES_MATERIAL_BUNDLE_TYPE), + compBundlePrefix + '.' + QLatin1String(Constants::COMPONENT_BUNDLES_EFFECT_BUNDLE_TYPE), + compBundlePrefix + '.' + QLatin1String(Constants::OLD_COMPONENT_BUNDLES_MATERIAL_BUNDLE_TYPE), + compBundlePrefix + '.' + QLatin1String(Constants::OLD_COMPONENT_BUNDLES_EFFECT_BUNDLE_TYPE) }; // create import sections diff --git a/src/plugins/qmldesigner/components/navigator/choosefrompropertylistdialog.cpp b/src/plugins/qmldesigner/components/navigator/choosefrompropertylistdialog.cpp index a3ab5f2cd71..fee3218af00 100644 --- a/src/plugins/qmldesigner/components/navigator/choosefrompropertylistdialog.cpp +++ b/src/plugins/qmldesigner/components/navigator/choosefrompropertylistdialog.cpp @@ -102,10 +102,9 @@ ChooseFromPropertyListFilter::ChooseFromPropertyListFilter(const NodeMetaInfo &i #ifdef QDS_USE_PROJECTSTORAGE // TODO add the types here or use the module #else - } else if (insertInfo.typeName().startsWith( - QString("%1.MaterialBundle").arg(QmlDesignerPlugin::instance()->documentManager() - .generatedComponentUtils().componentBundlesTypePrefix()) - .toUtf8())) { + } else if (insertInfo.typeName().startsWith( + QmlDesignerPlugin::instance()->documentManager() + .generatedComponentUtils().materialsBundleType().toUtf8())) { if (parentInfo.isQtQuick3DModel()) propertyList.append("materials"); #endif diff --git a/src/plugins/qmldesigner/designercore/generatedcomponentutils.cpp b/src/plugins/qmldesigner/designercore/generatedcomponentutils.cpp index 5ee5790b534..8c595d0833d 100644 --- a/src/plugins/qmldesigner/designercore/generatedcomponentutils.cpp +++ b/src/plugins/qmldesigner/designercore/generatedcomponentutils.cpp @@ -60,7 +60,10 @@ Utils::FilePath GeneratedComponentUtils::componentBundlesBasePath() const if (basePath.isEmpty()) return {}; - return basePath.resolvePath(QLatin1String(Constants::COMPONENT_BUNDLES_TYPE)); + if (basePath.endsWith(Constants::GENERATED_COMPONENTS_FOLDER)) + return basePath.resolvePath(QLatin1String(Constants::COMPONENT_BUNDLES_TYPE)); + + return basePath.resolvePath(QLatin1String(Constants::OLD_COMPONENT_BUNDLES_TYPE)); } Utils::FilePath GeneratedComponentUtils::import3dBasePath() const @@ -77,6 +80,32 @@ Utils::FilePath GeneratedComponentUtils::import3dBasePath() const return basePath.resolvePath(QLatin1String(Constants::QUICK_3D_COMPONENTS_FOLDER)); } +Utils::FilePath GeneratedComponentUtils::materialBundlePath() const +{ + Utils::FilePath basePath = componentBundlesBasePath(); + + if (basePath.isEmpty()) + return {}; + + if (basePath.endsWith(Constants::OLD_COMPONENT_BUNDLES_TYPE)) + return basePath.resolvePath(QLatin1String(Constants::OLD_COMPONENT_BUNDLES_MATERIAL_BUNDLE_TYPE)); + + return basePath.resolvePath(QLatin1String(Constants::COMPONENT_BUNDLES_MATERIAL_BUNDLE_TYPE)); +} + +Utils::FilePath GeneratedComponentUtils::effectBundlePath() const +{ + Utils::FilePath basePath = componentBundlesBasePath(); + + if (basePath.isEmpty()) + return {}; + + if (basePath.endsWith(Constants::OLD_COMPONENT_BUNDLES_TYPE)) + return basePath.resolvePath(QLatin1String(Constants::OLD_COMPONENT_BUNDLES_EFFECT_BUNDLE_TYPE)); + + return basePath.resolvePath(QLatin1String(Constants::COMPONENT_BUNDLES_EFFECT_BUNDLE_TYPE)); +} + bool GeneratedComponentUtils::isImport3dPath(const QString &path) const { return path.contains('/' + QLatin1String(Constants::OLD_QUICK_3D_ASSETS_FOLDER)) @@ -123,7 +152,7 @@ QString GeneratedComponentUtils::componentBundlesTypePrefix() const if (basePrefix.endsWith(Constants::GENERATED_COMPONENTS_FOLDER)) return basePrefix + '.' + QLatin1String(Constants::COMPONENT_BUNDLES_TYPE); - return Constants::COMPONENT_BUNDLES_TYPE; + return Constants::OLD_COMPONENT_BUNDLES_TYPE; } QString GeneratedComponentUtils::composedEffectsTypePrefix() const @@ -136,4 +165,24 @@ QString GeneratedComponentUtils::composedEffectsTypePrefix() const return Constants::OLD_EFFECTS_FOLDER; } +QString GeneratedComponentUtils::materialsBundleType() const +{ + QString basePrefix = componentBundlesTypePrefix(); + + if (basePrefix.endsWith(Constants::OLD_COMPONENT_BUNDLES_TYPE)) + return basePrefix + '.' + QLatin1String(Constants::OLD_COMPONENT_BUNDLES_MATERIAL_BUNDLE_TYPE); + + return basePrefix + '.' + QLatin1String(Constants::COMPONENT_BUNDLES_MATERIAL_BUNDLE_TYPE); +} + +QString GeneratedComponentUtils::effectsBundleType() const +{ + QString basePrefix = componentBundlesTypePrefix(); + + if (basePrefix.endsWith(Constants::OLD_COMPONENT_BUNDLES_TYPE)) + return basePrefix + '.' + QLatin1String(Constants::OLD_COMPONENT_BUNDLES_EFFECT_BUNDLE_TYPE); + + return basePrefix + '.' + QLatin1String(Constants::COMPONENT_BUNDLES_EFFECT_BUNDLE_TYPE); +} + } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/generatedcomponentutils.h b/src/plugins/qmldesigner/designercore/generatedcomponentutils.h index e2e9baf3e75..d9c04352a9e 100644 --- a/src/plugins/qmldesigner/designercore/generatedcomponentutils.h +++ b/src/plugins/qmldesigner/designercore/generatedcomponentutils.h @@ -21,6 +21,8 @@ public: Utils::FilePath composedEffectPath(const QString &effectPath) const; Utils::FilePath componentBundlesBasePath() const; Utils::FilePath import3dBasePath() const; + Utils::FilePath materialBundlePath() const; + Utils::FilePath effectBundlePath() const; bool isImport3dPath(const QString &path) const; bool isComposedEffectPath(const QString &path) const; @@ -30,6 +32,8 @@ public: QString import3dTypePath() const; QString componentBundlesTypePrefix() const; QString composedEffectsTypePrefix() const; + QString materialsBundleType() const; + QString effectsBundleType() const; private: ExternalDependenciesInterface &m_externalDependencies; diff --git a/src/plugins/qmldesigner/qmldesignerconstants.h b/src/plugins/qmldesigner/qmldesignerconstants.h index fb691097f72..188ec058d46 100644 --- a/src/plugins/qmldesigner/qmldesignerconstants.h +++ b/src/plugins/qmldesigner/qmldesignerconstants.h @@ -78,8 +78,10 @@ inline constexpr char EDIT3D_SNAP_CONFIG[] = "QmlDesigner.Editor3D.SnapConfig"; inline constexpr char EDIT3D_CAMERA_SPEED_CONFIG[] = "QmlDesigner.Editor3D.CameraSpeedConfig"; inline constexpr char QML_DESIGNER_SUBFOLDER[] = "/designer/"; -inline constexpr char COMPONENT_BUNDLES_TYPE[] = "ComponentBundles"; -inline constexpr char GENERATED_COMPONENTS_FOLDER[] = "GeneratedComponents"; +inline constexpr char COMPONENT_BUNDLES_TYPE[] = "Bundles"; +inline constexpr char COMPONENT_BUNDLES_MATERIAL_BUNDLE_TYPE[] = "Materials"; +inline constexpr char COMPONENT_BUNDLES_EFFECT_BUNDLE_TYPE[] = "Effects"; +inline constexpr char GENERATED_COMPONENTS_FOLDER[] = "Generated"; inline constexpr char COMPONENT_BUNDLES_ASSET_REF_FILE[] = "_asset_ref.json"; inline constexpr char OLD_QUICK_3D_ASSETS_FOLDER[] = "Quick3DAssets"; inline constexpr char QUICK_3D_COMPONENTS_FOLDER[] = "QtQuick3D"; @@ -90,6 +92,9 @@ inline constexpr char QUICK_3D_ASSET_IMPORT_DATA_SOURCE_KEY[] = "source_scene"; inline constexpr char OLD_ASSET_IMPORT_FOLDER[] = "asset_imports"; inline constexpr char OLD_EFFECTS_IMPORT_FOLDER[] = "/asset_imports/Effects"; inline constexpr char OLD_EFFECTS_FOLDER[] = "Effects"; +inline constexpr char OLD_COMPONENT_BUNDLES_TYPE[] = "ComponentBundles"; +inline constexpr char OLD_COMPONENT_BUNDLES_MATERIAL_BUNDLE_TYPE[] = "MaterialBundle"; +inline constexpr char OLD_COMPONENT_BUNDLES_EFFECT_BUNDLE_TYPE[] = "EffectBundle"; inline constexpr char COMPOSED_EFFECTS_TYPE[] = "ComposedEffects"; inline constexpr char MATERIAL_LIB_ID[] = "__materialLibrary__"; From cae233f5db8b0d92be8e6cbcd27d58853069c811 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Tue, 30 Apr 2024 16:09:34 +0300 Subject: [PATCH 041/154] EffectComposer: Shorten the generated effect path Changed "ComposedEffects" to "Effects". Task-number: QDS-12629 Change-Id: I45e2fd8443af72531d3c3e80f32227eacb948d26 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Thomas Hartmann Reviewed-by: Mahmoud Badri --- .../qmldesigner/designercore/generatedcomponentutils.cpp | 3 ++- src/plugins/qmldesigner/qmldesignerconstants.h | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/plugins/qmldesigner/designercore/generatedcomponentutils.cpp b/src/plugins/qmldesigner/designercore/generatedcomponentutils.cpp index 8c595d0833d..a2faa68d9e6 100644 --- a/src/plugins/qmldesigner/designercore/generatedcomponentutils.cpp +++ b/src/plugins/qmldesigner/designercore/generatedcomponentutils.cpp @@ -116,7 +116,8 @@ bool GeneratedComponentUtils::isImport3dPath(const QString &path) const bool GeneratedComponentUtils::isComposedEffectPath(const QString &path) const { return path.contains(Constants::OLD_EFFECTS_IMPORT_FOLDER) - || path.contains('/' + QLatin1String(Constants::COMPOSED_EFFECTS_TYPE)); + || path.contains(QLatin1String(Constants::GENERATED_COMPONENTS_FOLDER) + '/' + + QLatin1String(Constants::COMPOSED_EFFECTS_TYPE)); } QString GeneratedComponentUtils::generatedComponentTypePrefix() const diff --git a/src/plugins/qmldesigner/qmldesignerconstants.h b/src/plugins/qmldesigner/qmldesignerconstants.h index 188ec058d46..286a1633c7d 100644 --- a/src/plugins/qmldesigner/qmldesignerconstants.h +++ b/src/plugins/qmldesigner/qmldesignerconstants.h @@ -95,7 +95,7 @@ inline constexpr char OLD_EFFECTS_FOLDER[] = "Effects"; inline constexpr char OLD_COMPONENT_BUNDLES_TYPE[] = "ComponentBundles"; inline constexpr char OLD_COMPONENT_BUNDLES_MATERIAL_BUNDLE_TYPE[] = "MaterialBundle"; inline constexpr char OLD_COMPONENT_BUNDLES_EFFECT_BUNDLE_TYPE[] = "EffectBundle"; -inline constexpr char COMPOSED_EFFECTS_TYPE[] = "ComposedEffects"; +inline constexpr char COMPOSED_EFFECTS_TYPE[] = "Effects"; inline constexpr char MATERIAL_LIB_ID[] = "__materialLibrary__"; inline constexpr char MIME_TYPE_ITEM_LIBRARY_INFO[] From 15f05d0bbce1a98d554fb3733a59c3824c1c488a Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Mon, 29 Apr 2024 14:19:16 +0200 Subject: [PATCH 042/154] QmlDesigner: Drag and drop is now working with project storage Type annotaions are now saving the type name and dropInFormEditor is by default true. Task-number: QDS-12450 Change-Id: I757f59b296de321c4d0190a36ec581379f646735 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Reviewed-by: Tim Jenssen --- .../components/edit3d/edit3dview.cpp | 12 ++-- .../components/edit3d/edit3dwidget.cpp | 4 +- .../components/formeditor/dragtool.cpp | 23 ++++--- .../itemlibrary/itemlibrarymodel.cpp | 5 +- .../materialeditor/materialeditorview.cpp | 2 +- .../navigator/navigatortreemodel.cpp | 2 +- .../designercore/include/itemlibraryentry.h | 13 ++-- .../designercore/include/nodehints.h | 9 ++- .../metainfo/itemlibraryentry.cpp | 23 ++++--- .../designercore/metainfo/nodehints.cpp | 35 +++++------ .../qmldesigner/designercore/model/model.cpp | 3 +- .../designercore/model/qmlvisualnode.cpp | 13 ++-- .../projectstorage/projectstorage.cpp | 61 ++++++++++++------- .../projectstorage/projectstorage.h | 4 ++ .../projectstorage/projectstorageinfotypes.h | 6 ++ .../projectstorage/typeannotationreader.cpp | 4 +- .../tests/matchers/projectstorage-matcher.h | 4 +- .../tests/printers/gtest-creator-printing.cpp | 8 +-- .../unittests/metainfo/nodemetainfo-test.cpp | 2 + .../unit/tests/unittests/model/model-test.cpp | 20 ++++-- .../projectstorage/projectstorage-test.cpp | 12 ++++ .../typeannotationreader-test.cpp | 31 +++------- 22 files changed, 177 insertions(+), 119 deletions(-) diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp index bd01cc2c281..ff23e2b3d41 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp +++ b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp @@ -350,11 +350,10 @@ void Edit3DView::handleEntriesChanged() {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)); + entriesMap[key].entryList.append(toItemLibraryEntries(entries)); }; append(model()->qtQuick3DModelMetaInfo(), EK_primitives); @@ -386,9 +385,12 @@ void Edit3DView::handleEntriesChanged() } else if (entry.typeName() == "QtQuick3D.OrthographicCamera" || entry.typeName() == "QtQuick3D.PerspectiveCamera") { entryKey = EK_cameras; - } else if (entry.typeName().startsWith(QmlDesignerPlugin::instance()->documentManager() - .generatedComponentUtils().import3dTypePrefix().toUtf8()) - && NodeHints::fromItemLibraryEntry(entry).canBeDroppedInView3D()) { + } else if (entry.typeName().startsWith(QmlDesignerPlugin::instance() + ->documentManager() + .generatedComponentUtils() + .import3dTypePrefix() + .toUtf8()) + && NodeHints::fromItemLibraryEntry(entry, model()).canBeDroppedInView3D()) { entryKey = EK_importedModels; } else { continue; diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp index 2a5457e3dea..19a46e5b890 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp +++ b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp @@ -694,7 +694,7 @@ void Edit3DWidget::dragEnterEvent(QDragEnterEvent *dragEnterEvent) if (!data.isEmpty()) { QDataStream stream(data); stream >> m_draggedEntry; - if (NodeHints::fromItemLibraryEntry(m_draggedEntry).canBeDroppedInView3D()) + if (NodeHints::fromItemLibraryEntry(m_draggedEntry, view()->model()).canBeDroppedInView3D()) dragEnterEvent->acceptProposedAction(); } } @@ -773,7 +773,7 @@ void Edit3DWidget::dropEvent(QDropEvent *dropEvent) auto moduleId = model->module(import3dTypePrefix, Storage::ModuleKind::QmlLibrary); auto metaInfo = model->metaInfo(moduleId, fileName.toUtf8()); if (auto entries = metaInfo.itemLibrariesEntries(); entries.size()) { - auto entry = ItemLibraryEntry{entries.front(), *model->projectStorage()}; + auto entry = ItemLibraryEntry{entries.front()}; QmlVisualNode::createQml3DNode(view(), entry, m_canvas->activeScene(), {}, false); } } diff --git a/src/plugins/qmldesigner/components/formeditor/dragtool.cpp b/src/plugins/qmldesigner/components/formeditor/dragtool.cpp index 0b7d199b500..a6494811b63 100644 --- a/src/plugins/qmldesigner/components/formeditor/dragtool.cpp +++ b/src/plugins/qmldesigner/components/formeditor/dragtool.cpp @@ -206,9 +206,16 @@ static ItemLibraryEntry itemLibraryEntryFromMimeData(const QMimeData *mimeData) return itemLibraryEntry; } -static bool canBeDropped(const QMimeData *mimeData) +static bool canBeDropped(const QMimeData *mimeData, Model *model) { - return NodeHints::fromItemLibraryEntry(itemLibraryEntryFromMimeData(mimeData)).canBeDroppedInFormEditor(); +#ifdef QDS_USE_PROJECTSTORAGE + auto itemLibraryEntry = itemLibraryEntryFromMimeData(mimeData); + NodeMetaInfo metaInfo{itemLibraryEntry.typeId(), model->projectStorage()}; + return metaInfo.canBeDroppedInFormEditor() == FlagIs::True; +#else + return NodeHints::fromItemLibraryEntry(itemLibraryEntryFromMimeData(mimeData), model) + .canBeDroppedInFormEditor(); +#endif } static bool hasItemLibraryInfo(const QMimeData *mimeData) @@ -218,7 +225,7 @@ static bool hasItemLibraryInfo(const QMimeData *mimeData) void DragTool::dropEvent(const QList &itemList, QGraphicsSceneDragDropEvent *event) { - if (canBeDropped(event->mimeData())) { + if (canBeDropped(event->mimeData(), view()->model())) { event->accept(); end(generateUseSnapping(event->modifiers())); @@ -290,7 +297,7 @@ void DragTool::dropEvent(const QList &itemList, QGraphicsSceneD void DragTool::dragEnterEvent(const QList &/*itemList*/, QGraphicsSceneDragDropEvent *event) { - if (canBeDropped(event->mimeData())) { + if (canBeDropped(event->mimeData(), view()->model())) { m_blockMove = false; if (hasItemLibraryInfo(event->mimeData())) { @@ -306,7 +313,7 @@ void DragTool::dragEnterEvent(const QList &/*itemList*/, QGraph void DragTool::dragLeaveEvent(const QList &/*itemList*/, QGraphicsSceneDragDropEvent *event) { - if (canBeDropped(event->mimeData())) { + if (canBeDropped(event->mimeData(), view()->model())) { event->accept(); m_moveManipulator.end(); @@ -363,10 +370,8 @@ void DragTool::dragMoveEvent(const QList &itemList, QGraphicsSc ->data(Constants::MIME_TYPE_ASSETS)).split(','); QString assetType = AssetsLibraryWidget::getAssetTypeAndData(assetPaths[0]).first; - if (!m_blockMove - && !m_isAborted - && canBeDropped(event->mimeData()) - && assetType != Constants::MIME_TYPE_ASSET_EFFECT) { + if (!m_blockMove && !m_isAborted && canBeDropped(event->mimeData(), view()->model()) + && assetType != Constants::MIME_TYPE_ASSET_EFFECT) { event->accept(); if (!m_dragNodes.isEmpty()) { if (targetContainerItem) { diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp index 837767cbe63..ef06b692468 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp @@ -376,7 +376,7 @@ void ItemLibraryModel::update(Model *model) NodeMetaInfo metaInfo; if constexpr (useProjectStorage()) - metaInfo = entry.metaInfo(); + metaInfo = NodeMetaInfo{entry.typeId(), model->projectStorage()}; else metaInfo = model->metaInfo(entry.typeName()); @@ -388,7 +388,8 @@ void ItemLibraryModel::update(Model *model) || metaInfo.majorVersion() < 0); #endif bool isItem = valid && metaInfo.isQtQuickItem(); - bool forceVisibility = valid && NodeHints::fromItemLibraryEntry(entry).visibleInLibrary(); + bool forceVisibility = valid + && NodeHints::fromItemLibraryEntry(entry, model).visibleInLibrary(); if (m_flowMode) { isItem = metaInfo.isFlowViewItem(); diff --git a/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp b/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp index e083310cdbb..c1edd0fe1f7 100644 --- a/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp +++ b/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp @@ -716,7 +716,7 @@ void MaterialEditorView::updatePossibleTypes() auto heirs = model()->qtQuick3DMaterialMetaInfo().heirs(); heirs.push_back(model()->qtQuick3DMaterialMetaInfo()); auto entries = Utils::transform(heirs, [&](const auto &heir) { - return toItemLibraryEntries(heir.itemLibrariesEntries(), *model()->projectStorage()); + return toItemLibraryEntries(heir.itemLibrariesEntries()); }); // I am unsure about the code intention here diff --git a/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp b/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp index d305753ead3..c6fc35c10e4 100644 --- a/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp +++ b/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp @@ -705,7 +705,7 @@ void NavigatorTreeModel::handleItemLibraryItemDrop(const QMimeData *mimeData, in const ItemLibraryEntry itemLibraryEntry = createItemLibraryEntryFromMimeData(mimeData->data(Constants::MIME_TYPE_ITEM_LIBRARY_INFO)); - const NodeHints hints = NodeHints::fromItemLibraryEntry(itemLibraryEntry); + const NodeHints hints = NodeHints::fromItemLibraryEntry(itemLibraryEntry, m_view->model()); const QString targetPropertyName = hints.forceNonDefaultProperty(); diff --git a/src/plugins/qmldesigner/designercore/include/itemlibraryentry.h b/src/plugins/qmldesigner/designercore/include/itemlibraryentry.h index f88f9e35c6f..2d0f2ef31e5 100644 --- a/src/plugins/qmldesigner/designercore/include/itemlibraryentry.h +++ b/src/plugins/qmldesigner/designercore/include/itemlibraryentry.h @@ -42,13 +42,16 @@ class QMLDESIGNERCORE_EXPORT ItemLibraryEntry public: ItemLibraryEntry(); - explicit ItemLibraryEntry(const Storage::Info::ItemLibraryEntry &entry, - const ProjectStorageType &projectStorage); - ~ItemLibraryEntry() = default; + ItemLibraryEntry(const ItemLibraryEntry &) = default; + ItemLibraryEntry &operator=(const ItemLibraryEntry &) = default; + ItemLibraryEntry(ItemLibraryEntry &&) = default; + ItemLibraryEntry &operator=(ItemLibraryEntry &&) = default; + explicit ItemLibraryEntry(const Storage::Info::ItemLibraryEntry &entry); + ~ItemLibraryEntry(); QString name() const; TypeName typeName() const; - const NodeMetaInfo &metaInfo() const; + TypeId typeId() const; QIcon typeIcon() const; QString libraryEntryIconPath() const; int majorVersion() const; @@ -86,7 +89,7 @@ private: using ItemLibraryEntries = QList; QMLDESIGNERCORE_EXPORT QList toItemLibraryEntries( - const Storage::Info::ItemLibraryEntries &entries, const ProjectStorageType &projectStorage); + const Storage::Info::ItemLibraryEntries &entries); } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/include/nodehints.h b/src/plugins/qmldesigner/designercore/include/nodehints.h index 9e67c2d99b3..99470db65f1 100644 --- a/src/plugins/qmldesigner/designercore/include/nodehints.h +++ b/src/plugins/qmldesigner/designercore/include/nodehints.h @@ -3,9 +3,11 @@ #pragma once +#include "modelnode.h" +#include "nodemetainfo.h" + #include #include -#include "modelnode.h" #include "qmldesignercorelib_global.h" #include "invalidmetainfoexception.h" @@ -54,18 +56,19 @@ public: QHash hints() const; static NodeHints fromModelNode(const ModelNode &modelNode); - static NodeHints fromItemLibraryEntry(const ItemLibraryEntry &entry); + static NodeHints fromItemLibraryEntry(const ItemLibraryEntry &entry, Model *model); private: explicit NodeHints(const ModelNode &modelNode); explicit NodeHints(const NodeMetaInfo &metaInfo); - explicit NodeHints(const ItemLibraryEntry &entry); + explicit NodeHints(const ItemLibraryEntry &entry, Model *model); const ModelNode &modelNode() const; bool isValid() const; Model *model() const; bool evaluateBooleanExpression(const QString &hintName, bool defaultValue, const ModelNode potentialParent = ModelNode()) const; ModelNode m_modelNode; + NodeMetaInfo m_metaInfo; QHash m_hints; }; diff --git a/src/plugins/qmldesigner/designercore/metainfo/itemlibraryentry.cpp b/src/plugins/qmldesigner/designercore/metainfo/itemlibraryentry.cpp index 806da7e7c4d..2aec7660026 100644 --- a/src/plugins/qmldesigner/designercore/metainfo/itemlibraryentry.cpp +++ b/src/plugins/qmldesigner/designercore/metainfo/itemlibraryentry.cpp @@ -22,7 +22,7 @@ class ItemLibraryEntryData public: QString name; TypeName typeName; - NodeMetaInfo metaInfo; + TypeId typeId; QString category; int majorVersion{-1}; int minorVersion{-1}; @@ -64,12 +64,12 @@ ItemLibraryEntry::ItemLibraryEntry() : m_data(std::make_shared()) {} -ItemLibraryEntry::ItemLibraryEntry(const Storage::Info::ItemLibraryEntry &entry, - const ProjectStorageType &projectStorage) +ItemLibraryEntry::ItemLibraryEntry(const Storage::Info::ItemLibraryEntry &entry) : ItemLibraryEntry{} { m_data->name = entry.name.toQString(); - m_data->metaInfo = {entry.typeId, &projectStorage}; + m_data->typeId = entry.typeId; + m_data->typeName = entry.typeName.toQByteArray(); m_data->category = entry.category.toQString(); if (entry.iconPath.size()) m_data->libraryEntryIconPath = entry.iconPath.toQString(); @@ -87,6 +87,8 @@ ItemLibraryEntry::ItemLibraryEntry(const Storage::Info::ItemLibraryEntry &entry, m_data->extraFilePaths.emplace_back(extraFilePath.toQString()); } +ItemLibraryEntry::~ItemLibraryEntry() = default; + QString ItemLibraryEntry::name() const { return m_data->name; @@ -97,9 +99,9 @@ TypeName ItemLibraryEntry::typeName() const return m_data->typeName; } -const NodeMetaInfo &ItemLibraryEntry::metaInfo() const +TypeId ItemLibraryEntry::typeId() const { - return m_data->metaInfo; + return m_data->typeId; } QString ItemLibraryEntry::qmlSource() const @@ -245,6 +247,7 @@ QDataStream &operator<<(QDataStream &stream, const ItemLibraryEntry &itemLibrary stream << itemLibraryEntry.m_data->qmlSource; stream << itemLibraryEntry.m_data->customComponentSource; stream << itemLibraryEntry.m_data->extraFilePaths; + stream << itemLibraryEntry.m_data->typeId.internalId(); return stream; } @@ -270,6 +273,9 @@ QDataStream &operator>>(QDataStream &stream, ItemLibraryEntry &itemLibraryEntry) stream >> itemLibraryEntry.m_data->qmlSource; stream >> itemLibraryEntry.m_data->customComponentSource; stream >> itemLibraryEntry.m_data->extraFilePaths; + TypeId::DatabaseType internalTypeId; + stream >> internalTypeId; + itemLibraryEntry.m_data->typeId = TypeId::create(internalTypeId); return stream; } @@ -295,11 +301,10 @@ QDebug operator<<(QDebug debug, const ItemLibraryEntry &itemLibraryEntry) return debug.space(); } -QList toItemLibraryEntries(const Storage::Info::ItemLibraryEntries &entries, - const ProjectStorageType &projectStorage) +QList toItemLibraryEntries(const Storage::Info::ItemLibraryEntries &entries) { return Utils::transform>(entries, [&](const auto &entry) { - return ItemLibraryEntry{entry, projectStorage}; + return ItemLibraryEntry{entry}; }); } diff --git a/src/plugins/qmldesigner/designercore/metainfo/nodehints.cpp b/src/plugins/qmldesigner/designercore/metainfo/nodehints.cpp index 32e68a3cdc6..1f9a3e42bd6 100644 --- a/src/plugins/qmldesigner/designercore/metainfo/nodehints.cpp +++ b/src/plugins/qmldesigner/designercore/metainfo/nodehints.cpp @@ -106,14 +106,15 @@ QmlDesigner::NodeHints::NodeHints(const ModelNode &node) } NodeHints::NodeHints(const NodeMetaInfo &metaInfo) + : m_metaInfo{metaInfo} { for (const auto &[name, expression] : metaInfo.typeHints()) m_hints.insert(name.toQString(), expression.toQString()); } -NodeHints::NodeHints(const ItemLibraryEntry &entry) +NodeHints::NodeHints(const ItemLibraryEntry &entry, [[maybe_unused]] Model *model) #ifdef QDS_USE_PROJECTSTORAGE - : NodeHints{entry.metaInfo()} + : NodeHints{NodeMetaInfo{entry.typeId(), model->projectStorage()}} #endif { if constexpr (!useProjectStorage()) @@ -135,7 +136,7 @@ bool NodeHints::canBeContainerFor(const ModelNode &potenialChild) const if (!isValid()) return true; - auto flagIs = m_modelNode.metaInfo().canBeContainer(); + auto flagIs = m_metaInfo.canBeContainer(); if (flagIs != FlagIs::Set) return convert(flagIs); @@ -151,7 +152,7 @@ bool NodeHints::forceClip() const if (isSwipeView(modelNode())) return true; - auto flagIs = m_modelNode.metaInfo().forceClip(); + auto flagIs = m_metaInfo.forceClip(); if (flagIs != FlagIs::Set) return convert(flagIs); @@ -167,7 +168,7 @@ bool NodeHints::doesLayoutChildren() const if (isSwipeView(modelNode())) return true; - auto flagIs = m_modelNode.metaInfo().doesLayoutChildren(); + auto flagIs = m_metaInfo.doesLayoutChildren(); if (flagIs != FlagIs::Set) return convert(flagIs); @@ -177,7 +178,7 @@ bool NodeHints::doesLayoutChildren() const bool NodeHints::canBeDroppedInFormEditor() const { - auto flagIs = m_modelNode.metaInfo().canBeDroppedInFormEditor(); + auto flagIs = m_metaInfo.canBeDroppedInFormEditor(); if (flagIs != FlagIs::Set) return convert(flagIs); @@ -187,7 +188,7 @@ bool NodeHints::canBeDroppedInFormEditor() const bool NodeHints::canBeDroppedInNavigator() const { - auto flagIs = m_modelNode.metaInfo().canBeDroppedInNavigator(); + auto flagIs = m_metaInfo.canBeDroppedInNavigator(); if (flagIs != FlagIs::Set) return convert(flagIs); @@ -197,7 +198,7 @@ bool NodeHints::canBeDroppedInNavigator() const bool NodeHints::canBeDroppedInView3D() const { - auto flagIs = m_modelNode.metaInfo().canBeDroppedInView3D(); + auto flagIs = m_metaInfo.canBeDroppedInView3D(); if (flagIs != FlagIs::Set) return convert(flagIs); @@ -210,7 +211,7 @@ bool NodeHints::isMovable() const if (!isValid()) return true; - auto flagIs = m_modelNode.metaInfo().isMovable(); + auto flagIs = m_metaInfo.isMovable(); if (flagIs != FlagIs::Set) return convert(flagIs); @@ -223,7 +224,7 @@ bool NodeHints::isResizable() const if (!isValid()) return true; - auto flagIs = m_modelNode.metaInfo().isResizable(); + auto flagIs = m_metaInfo.isResizable(); if (flagIs != FlagIs::Set) return convert(flagIs); @@ -236,7 +237,7 @@ bool NodeHints::hasFormEditorItem() const if (!isValid()) return true; - auto flagIs = m_modelNode.metaInfo().hasFormEditorItem(); + auto flagIs = m_metaInfo.hasFormEditorItem(); if (flagIs != FlagIs::Set) return convert(flagIs); @@ -252,7 +253,7 @@ bool NodeHints::isStackedContainer() const if (isSwipeView(modelNode())) return true; - auto flagIs = m_modelNode.metaInfo().isStackedContainer(); + auto flagIs = m_metaInfo.isStackedContainer(); if (flagIs != FlagIs::Set) return convert(flagIs); @@ -299,7 +300,7 @@ bool NodeHints::takesOverRenderingOfChildren() const if (!isValid()) return false; - auto flagIs = m_modelNode.metaInfo().takesOverRenderingOfChildren(); + auto flagIs = m_metaInfo.takesOverRenderingOfChildren(); if (flagIs != FlagIs::Set) return convert(flagIs); @@ -312,7 +313,7 @@ bool NodeHints::visibleInNavigator() const if (!isValid()) return false; - auto flagIs = m_modelNode.metaInfo().visibleInNavigator(); + auto flagIs = m_metaInfo.visibleInNavigator(); if (flagIs != FlagIs::Set) return convert(flagIs); @@ -322,7 +323,7 @@ bool NodeHints::visibleInNavigator() const bool NodeHints::visibleInLibrary() const { - auto flagIs = m_modelNode.metaInfo().visibleInLibrary(); + auto flagIs = m_metaInfo.visibleInLibrary(); if (flagIs != FlagIs::Set) return convert(flagIs); @@ -391,9 +392,9 @@ NodeHints NodeHints::fromModelNode(const ModelNode &modelNode) return NodeHints(modelNode); } -NodeHints NodeHints::fromItemLibraryEntry(const ItemLibraryEntry &entry) +NodeHints NodeHints::fromItemLibraryEntry(const ItemLibraryEntry &entry, Model *model) { - return NodeHints(entry); + return NodeHints(entry, model); } const ModelNode &NodeHints::modelNode() const diff --git a/src/plugins/qmldesigner/designercore/model/model.cpp b/src/plugins/qmldesigner/designercore/model/model.cpp index bf15410232b..23871b66699 100644 --- a/src/plugins/qmldesigner/designercore/model/model.cpp +++ b/src/plugins/qmldesigner/designercore/model/model.cpp @@ -2560,8 +2560,7 @@ QList Model::itemLibraryEntries() const { #ifdef QDS_USE_PROJECTSTORAGE using namespace Storage::Info; - return toItemLibraryEntries(d->projectStorage->itemLibraryEntries(d->m_sourceId), - *d->projectStorage); + return toItemLibraryEntries(d->projectStorage->itemLibraryEntries(d->m_sourceId)); #else return d->metaInfo().itemLibraryInfo()->entries(); #endif diff --git a/src/plugins/qmldesigner/designercore/model/qmlvisualnode.cpp b/src/plugins/qmldesigner/designercore/model/qmlvisualnode.cpp index c84f2342576..1077c5bda8f 100644 --- a/src/plugins/qmldesigner/designercore/model/qmlvisualnode.cpp +++ b/src/plugins/qmldesigner/designercore/model/qmlvisualnode.cpp @@ -250,8 +250,7 @@ QmlObjectNode QmlVisualNode::createQmlObjectNode(AbstractView *view, NodeAbstractProperty parentProperty = parentQmlItemNode.defaultNodeAbstractProperty(); - - NodeHints hints = NodeHints::fromItemLibraryEntry(itemLibraryEntry); + NodeHints hints = NodeHints::fromItemLibraryEntry(itemLibraryEntry, view->model()); const PropertyName forceNonDefaultProperty = hints.forceNonDefaultProperty().toUtf8(); QmlObjectNode newNode = QmlItemNode::createQmlObjectNode(view, @@ -329,7 +328,7 @@ QmlObjectNode QmlVisualNode::createQmlObjectNode(AbstractView *view, { QmlObjectNode newQmlObjectNode; - NodeHints hints = NodeHints::fromItemLibraryEntry(itemLibraryEntry); + NodeHints hints = NodeHints::fromItemLibraryEntry(itemLibraryEntry, view->model()); auto createNodeFunc = [=, &newQmlObjectNode, &parentProperty]() { #ifndef QDS_USE_PROJECTSTORAGE @@ -361,13 +360,17 @@ QmlObjectNode QmlVisualNode::createQmlObjectNode(AbstractView *view, propertyPairList.append(position.propertyPairList()); ModelNode::NodeSourceType nodeSourceType = ModelNode::NodeWithoutSource; - if (itemLibraryEntry.typeName() == "QtQml.Component") - nodeSourceType = ModelNode::NodeWithComponentSource; #ifdef QDS_USE_PROJECTSTORAGE + NodeMetaInfo metaInfo{itemLibraryEntry.typeId(), view->model()->projectStorage()}; + if (metaInfo.isQmlComponent()) + nodeSourceType = ModelNode::NodeWithComponentSource; newQmlObjectNode = QmlObjectNode(view->createModelNode( itemLibraryEntry.typeName(), propertyPairList, {}, {}, nodeSourceType)); #else + if (itemLibraryEntry.typeName() == "QtQml.Component") + nodeSourceType = ModelNode::NodeWithComponentSource; + newQmlObjectNode = QmlObjectNode(view->createModelNode(itemLibraryEntry.typeName(), majorVersion, minorVersion, diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.cpp b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.cpp index 9e8b882dea6..74a4b946f11 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.cpp +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.cpp @@ -639,17 +639,21 @@ struct ProjectStorage::Statements database}; Sqlite::WriteStatement<1> deletePropertyEditorPathStatement{ "DELETE FROM propertyEditorPaths WHERE typeId=?1", database}; - mutable Sqlite::ReadStatement<4, 1> selectTypeAnnotationsForSourceIdsStatement{ - "SELECT typeId, iconPath, itemLibrary, hints FROM typeAnnotations WHERE " + mutable Sqlite::ReadStatement<5, 1> selectTypeAnnotationsForSourceIdsStatement{ + "SELECT typeId, typeName, iconPath, itemLibrary, hints FROM typeAnnotations WHERE " "sourceId IN carray(?1) ORDER BY typeId", database}; - Sqlite::WriteStatement<6> insertTypeAnnotationStatement{ + Sqlite::WriteStatement<7> insertTypeAnnotationStatement{ "INSERT INTO " - " typeAnnotations(typeId, sourceId, directorySourceId, iconPath, itemLibrary, hints) " - "VALUES(?1, ?2, ?3, ?4, ?5, ?6)", + " typeAnnotations(typeId, sourceId, directorySourceId, typeName, iconPath, itemLibrary, " + " hints) " + "VALUES(?1, ?2, ?3, ?4, ?5, ?6, ?7)", + database}; + Sqlite::WriteStatement<5> updateTypeAnnotationStatement{ + "UPDATE typeAnnotations " + "SET typeName=?2, iconPath=?3, itemLibrary=?4, hints=?5 " + "WHERE typeId=?1", database}; - Sqlite::WriteStatement<4> updateTypeAnnotationStatement{ - "UPDATE typeAnnotations SET iconPath=?2, itemLibrary=?3, hints=?4 WHERE typeId=?1", database}; Sqlite::WriteStatement<1> deleteTypeAnnotationStatement{ "DELETE FROM typeAnnotations WHERE typeId=?1", database}; mutable Sqlite::ReadStatement<1, 1> selectTypeIconPathStatement{ @@ -663,22 +667,22 @@ struct ProjectStorage::Statements "SELECT sourceId FROM typeAnnotations WHERE directorySourceId=?1 ORDER BY sourceId", database}; mutable Sqlite::ReadStatement<1, 0> selectTypeAnnotationDirectorySourceIdsStatement{ "SELECT DISTINCT directorySourceId FROM typeAnnotations ORDER BY directorySourceId", database}; - mutable Sqlite::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' " + mutable Sqlite::ReadStatement<10> selectItemLibraryEntriesStatement{ + "SELECT typeId, typeName, 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 AS ta , json_each(ta.itemLibrary) AS i " "WHERE ta.itemLibrary IS NOT NULL", database}; - mutable Sqlite::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' " + mutable Sqlite::ReadStatement<10, 1> selectItemLibraryEntriesByTypeIdStatement{ + "SELECT typeId, typeName, 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 AS ta, json_each(ta.itemLibrary) AS i " "WHERE typeId=?1 AND ta.itemLibrary IS NOT NULL", database}; - mutable Sqlite::ReadStatement<9, 1> selectItemLibraryEntriesBySourceIdStatement{ - "SELECT typeId, i.value->>'$.name', i.value->>'$.iconPath', " + mutable Sqlite::ReadStatement<10, 1> selectItemLibraryEntriesBySourceIdStatement{ + "SELECT typeId, typeName, i.value->>'$.name', i.value->>'$.iconPath', " "i.value->>'$.category', " " i.value->>'$.import', i.value->>'$.toolTip', i.value->>'$.properties', " " i.value->>'$.extraFilePaths', i.value->>'$.templatePath' " @@ -1087,7 +1091,7 @@ public: auto &sourceIdColumn = table.addColumn("sourceId", Sqlite::StrictColumnType::Integer); auto &directorySourceIdColumn = table.addColumn("directorySourceId", Sqlite::StrictColumnType::Integer); - + table.addColumn("typeName", Sqlite::StrictColumnType::Text); table.addColumn("iconPath", Sqlite::StrictColumnType::Text); table.addColumn("itemLibrary", Sqlite::StrictColumnType::Text); table.addColumn("hints", Sqlite::StrictColumnType::Text); @@ -1572,6 +1576,7 @@ Storage::Info::ItemLibraryEntries ProjectStorage::itemLibraryEntries(TypeId type Storage::Info::ItemLibraryEntries entries; auto callback = [&](TypeId typeId_, + Utils::SmallStringView typeName, Utils::SmallStringView name, Utils::SmallStringView iconPath, Utils::SmallStringView category, @@ -1580,7 +1585,8 @@ Storage::Info::ItemLibraryEntries ProjectStorage::itemLibraryEntries(TypeId type Utils::SmallStringView properties, Utils::SmallStringView extraFilePaths, Utils::SmallStringView templatePath) { - auto &last = entries.emplace_back(typeId_, name, iconPath, category, import, toolTip, templatePath); + auto &last = entries.emplace_back( + typeId_, typeName, name, iconPath, category, import, toolTip, templatePath); if (properties.size()) s->selectItemLibraryPropertiesStatement.readTo(last.properties, properties); if (extraFilePaths.size()) @@ -1605,6 +1611,7 @@ Storage::Info::ItemLibraryEntries ProjectStorage::itemLibraryEntries(ImportId im Storage::Info::ItemLibraryEntries entries; auto callback = [&](TypeId typeId_, + Utils::SmallStringView typeName, Utils::SmallStringView name, Utils::SmallStringView iconPath, Utils::SmallStringView category, @@ -1613,7 +1620,8 @@ Storage::Info::ItemLibraryEntries ProjectStorage::itemLibraryEntries(ImportId im Utils::SmallStringView properties, Utils::SmallStringView extraFilePaths, Utils::SmallStringView templatePath) { - auto &last = entries.emplace_back(typeId_, name, iconPath, category, import, toolTip, templatePath); + auto &last = entries.emplace_back( + typeId_, typeName, name, iconPath, category, import, toolTip, templatePath); if (properties.size()) s->selectItemLibraryPropertiesStatement.readTo(last.properties, properties); if (extraFilePaths.size()) @@ -1638,6 +1646,7 @@ Storage::Info::ItemLibraryEntries ProjectStorage::itemLibraryEntries(SourceId so Storage::Info::ItemLibraryEntries entries; auto callback = [&](TypeId typeId, + Utils::SmallStringView typeName, Utils::SmallStringView name, Utils::SmallStringView iconPath, Utils::SmallStringView category, @@ -1646,7 +1655,8 @@ Storage::Info::ItemLibraryEntries ProjectStorage::itemLibraryEntries(SourceId so Utils::SmallStringView properties, Utils::SmallStringView extraFilePaths, Utils::SmallStringView templatePath) { - auto &last = entries.emplace_back(typeId, name, iconPath, category, import, toolTip, templatePath); + auto &last = entries.emplace_back( + typeId, typeName, name, iconPath, category, import, toolTip, templatePath); if (properties.size()) s->selectItemLibraryPropertiesStatement.readTo(last.properties, properties); if (extraFilePaths.size()) @@ -1669,6 +1679,7 @@ Storage::Info::ItemLibraryEntries ProjectStorage::allItemLibraryEntries() const Storage::Info::ItemLibraryEntries entries; auto callback = [&](TypeId typeId, + Utils::SmallStringView typeName, Utils::SmallStringView name, Utils::SmallStringView iconPath, Utils::SmallStringView category, @@ -1677,7 +1688,8 @@ Storage::Info::ItemLibraryEntries ProjectStorage::allItemLibraryEntries() const Utils::SmallStringView properties, Utils::SmallStringView extraFilePaths, Utils::SmallStringView templatePath) { - auto &last = entries.emplace_back(typeId, name, iconPath, category, import, toolTip, templatePath); + auto &last = entries.emplace_back( + typeId, typeName, name, iconPath, category, import, toolTip, templatePath); if (properties.size()) s->selectItemLibraryPropertiesStatement.readTo(last.properties, properties); if (extraFilePaths.size()) @@ -2314,6 +2326,7 @@ void ProjectStorage::synchronizeTypeAnnotations(Storage::Synchronization::TypeAn s->insertTypeAnnotationStatement.write(annotation.typeId, annotation.sourceId, annotation.directorySourceId, + annotation.typeName, annotation.iconPath, createEmptyAsNull(annotation.itemLibraryJson), createEmptyAsNull(annotation.hintsJson)); @@ -2323,7 +2336,8 @@ void ProjectStorage::synchronizeTypeAnnotations(Storage::Synchronization::TypeAn const TypeAnnotation &annotation) { synchronizeTypeTraits(annotation.typeId, annotation.traits); - if (annotationFromDatabase.iconPath != annotation.iconPath + if (annotationFromDatabase.typeName != annotation.typeName + || annotationFromDatabase.iconPath != annotation.iconPath || annotationFromDatabase.itemLibraryJson != annotation.itemLibraryJson || annotationFromDatabase.hintsJson != annotation.hintsJson) { using NanotraceHR::keyValue; @@ -2334,6 +2348,7 @@ void ProjectStorage::synchronizeTypeAnnotations(Storage::Synchronization::TypeAn keyValue("type annotation", annotation)}; s->updateTypeAnnotationStatement.write(annotation.typeId, + annotation.typeName, annotation.iconPath, createEmptyAsNull(annotation.itemLibraryJson), createEmptyAsNull(annotation.hintsJson)); diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h index 579060b0aea..85eebeb2cb5 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h @@ -501,10 +501,12 @@ private: { public: TypeAnnotationView(TypeId typeId, + Utils::SmallStringView typeName, Utils::SmallStringView iconPath, Utils::SmallStringView itemLibraryJson, Utils::SmallStringView hintsJson) : typeId{typeId} + , typeName{typeName} , iconPath{iconPath} , itemLibraryJson{itemLibraryJson} , hintsJson{hintsJson} @@ -516,6 +518,7 @@ private: using NanotraceHR::dictonary; using NanotraceHR::keyValue; auto dict = dictonary(keyValue("type id", typeAnnotationView.typeId), + keyValue("type name", typeAnnotationView.typeName), keyValue("icon path", typeAnnotationView.iconPath), keyValue("item library json", typeAnnotationView.itemLibraryJson), keyValue("hints json", typeAnnotationView.hintsJson)); @@ -525,6 +528,7 @@ private: public: TypeId typeId; + Utils::SmallStringView typeName; Utils::SmallStringView iconPath; Utils::SmallStringView itemLibraryJson; Utils::PathString hintsJson; diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinfotypes.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinfotypes.h index b4b1eafbb65..98685d22e2d 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinfotypes.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinfotypes.h @@ -380,6 +380,7 @@ using ToolTipString = Utils::BasicSmallString<94>; struct ItemLibraryEntry { ItemLibraryEntry(TypeId typeId, + Utils::SmallStringView typeName, Utils::SmallStringView name, Utils::SmallStringView iconPath, Utils::SmallStringView category, @@ -387,6 +388,7 @@ struct ItemLibraryEntry Utils::SmallStringView toolTip, Utils::SmallStringView templatePath) : typeId{typeId} + , typeName{typeName} , name{name} , iconPath{iconPath} , category{category} @@ -396,6 +398,7 @@ struct ItemLibraryEntry {} ItemLibraryEntry(TypeId typeId, + Utils::SmallStringView typeName, Utils::SmallStringView name, Utils::SmallStringView iconPath, Utils::SmallStringView category, @@ -403,6 +406,7 @@ struct ItemLibraryEntry Utils::SmallStringView toolTip, ItemLibraryProperties properties) : typeId{typeId} + , typeName{typeName} , name{name} , iconPath{iconPath} , category{category} @@ -417,6 +421,7 @@ struct ItemLibraryEntry using NanotraceHR::dictonary; using NanotraceHR::keyValue; auto dict = dictonary(keyValue("type id", entry.typeId), + keyValue("type name", entry.typeName), keyValue("name", entry.name), keyValue("icon path", entry.iconPath), keyValue("category", entry.category), @@ -430,6 +435,7 @@ struct ItemLibraryEntry } TypeId typeId; + Utils::SmallString typeName; Utils::SmallString name; Utils::PathString iconPath; Utils::SmallString category; diff --git a/src/plugins/qmldesigner/designercore/projectstorage/typeannotationreader.cpp b/src/plugins/qmldesigner/designercore/projectstorage/typeannotationreader.cpp index 876a789120b..a0ef56616a2 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/typeannotationreader.cpp +++ b/src/plugins/qmldesigner/designercore/projectstorage/typeannotationreader.cpp @@ -178,8 +178,10 @@ TypeAnnotationReader::ParserSate TypeAnnotationReader::readDocument(const QStrin TypeAnnotationReader::ParserSate TypeAnnotationReader::readMetaInfoRootElement(const QString &name) { if (name == typeElementName) { - m_typeAnnotations.emplace_back(m_sourceId, m_directorySourceId); + auto &annotation = m_typeAnnotations.emplace_back(m_sourceId, m_directorySourceId); + annotation.traits.canBeDroppedInFormEditor = FlagIs::True; m_itemLibraryEntries = json::array(); + return ParsingType; } else { addErrorInvalidType(name); diff --git a/tests/unit/tests/matchers/projectstorage-matcher.h b/tests/unit/tests/matchers/projectstorage-matcher.h index 02861d7eea3..56b4ad9d6ae 100644 --- a/tests/unit/tests/matchers/projectstorage-matcher.h +++ b/tests/unit/tests/matchers/projectstorage-matcher.h @@ -20,6 +20,7 @@ MATCHER_P2(IsTypeHint, template auto IsItemLibraryEntry(QmlDesigner::TypeId typeId, + Utils::SmallStringView typeName, Utils::SmallStringView name, Utils::SmallStringView iconPath, Utils::SmallStringView category, @@ -31,6 +32,7 @@ auto IsItemLibraryEntry(QmlDesigner::TypeId typeId, { using QmlDesigner::Storage::Info::ItemLibraryEntry; return AllOf(Field("typeId", &ItemLibraryEntry::typeId, typeId), + Field("typeName", &ItemLibraryEntry::typeName, typeName), Field("name", &ItemLibraryEntry::name, name), Field("iconPath", &ItemLibraryEntry::iconPath, iconPath), Field("category", &ItemLibraryEntry::category, category), @@ -66,7 +68,7 @@ auto IsTypeAnnotation(QmlDesigner::SourceId sourceId, { using QmlDesigner::Storage::Synchronization::TypeAnnotation; return AllOf(Field("sourceId", &TypeAnnotation::sourceId, sourceId), - Field("sourceId", &TypeAnnotation::directorySourceId, directorySourceId), + Field("directory sourceId", &TypeAnnotation::directorySourceId, directorySourceId), Field("typeName", &TypeAnnotation::typeName, typeName), Field("moduleId", &TypeAnnotation::moduleId, moduleId), Field("iconPath", &TypeAnnotation::iconPath, iconPath), diff --git a/tests/unit/tests/printers/gtest-creator-printing.cpp b/tests/unit/tests/printers/gtest-creator-printing.cpp index 6bc78e99367..88b17e59309 100644 --- a/tests/unit/tests/printers/gtest-creator-printing.cpp +++ b/tests/unit/tests/printers/gtest-creator-printing.cpp @@ -695,10 +695,10 @@ std::ostream &operator<<(std::ostream &out, const ItemLibraryProperty &property) 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 << ")"; + return out << R"((")" << entry.typeName << 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 diff --git a/tests/unit/tests/unittests/metainfo/nodemetainfo-test.cpp b/tests/unit/tests/unittests/metainfo/nodemetainfo-test.cpp index c3fd89d86d0..c96def356ab 100644 --- a/tests/unit/tests/unittests/metainfo/nodemetainfo-test.cpp +++ b/tests/unit/tests/unittests/metainfo/nodemetainfo-test.cpp @@ -3182,6 +3182,7 @@ TEST_F(NodeMetaInfo, item_library_entries) { projectStorageMock.setItemLibraryEntries(objectMetaInfo.id(), {{objectMetaInfo.id(), + "QtObject", "Object", "/icon/path", "Basic", @@ -3193,6 +3194,7 @@ TEST_F(NodeMetaInfo, item_library_entries) ASSERT_THAT(entries, ElementsAre(IsItemLibraryEntry(objectMetaInfo.id(), + "QtObject", "Object", "/icon/path", "Basic", diff --git a/tests/unit/tests/unittests/model/model-test.cpp b/tests/unit/tests/unittests/model/model-test.cpp index 80c37f3a6b5..227360c01f6 100644 --- a/tests/unit/tests/unittests/model/model-test.cpp +++ b/tests/unit/tests/unittests/model/model-test.cpp @@ -36,7 +36,8 @@ MATCHER(IsSorted, std::string(negation ? "isn't sorted" : "is sorted")) } template -auto IsItemLibraryEntry(const QmlDesigner::NodeMetaInfo &metaInfo, +auto IsItemLibraryEntry(QmlDesigner::TypeId typeId, + QByteArrayView typeName, QStringView name, QStringView iconPath, QStringView category, @@ -47,7 +48,8 @@ auto IsItemLibraryEntry(const QmlDesigner::NodeMetaInfo &metaInfo, ExtraFilePathsMatcher extraFilePathsMatcher) { using QmlDesigner::ItemLibraryEntry; - return AllOf(Property("metaInfo", &ItemLibraryEntry::metaInfo, metaInfo), + return AllOf(Property("typeId", &ItemLibraryEntry::typeId, typeId), + Property("typeName", &ItemLibraryEntry::typeName, typeName), Property("name", &ItemLibraryEntry::name, name), Property("libraryEntryIconPath", &ItemLibraryEntry::libraryEntryIconPath, iconPath), Property("category", &ItemLibraryEntry::category, category), @@ -1001,18 +1003,24 @@ TEST_F(Model, meta_infos_for_mdoule) 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"}}; + QmlDesigner::Storage::Info::ItemLibraryEntries storageEntries{{itemTypeId, + "Item", + "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, + IsItemLibraryEntry(itemTypeId, + "Item", u"Item", u"/path/to/icon", u"basic category", diff --git a/tests/unit/tests/unittests/projectstorage/projectstorage-test.cpp b/tests/unit/tests/unittests/projectstorage/projectstorage-test.cpp index 8e0b6919200..cd817a1d06f 100644 --- a/tests/unit/tests/unittests/projectstorage/projectstorage-test.cpp +++ b/tests/unit/tests/unittests/projectstorage/projectstorage-test.cpp @@ -7560,6 +7560,7 @@ TEST_F(ProjectStorage, synchronize_item_library_entries) storage.allItemLibraryEntries(), UnorderedElementsAre( IsItemLibraryEntry(fetchTypeId(sourceId2, "QObject"), + "Object", "Foo", "/path/icon", "Basic Items", @@ -7571,6 +7572,7 @@ TEST_F(ProjectStorage, synchronize_item_library_entries) UnorderedElementsAre("/path/templates/frame.png", "/path/templates/frame.frag")), IsItemLibraryEntry(fetchTypeId(sourceId2, "QObject"), + "Object", "Bar", "/path/icon2", "Basic Items", @@ -7580,6 +7582,7 @@ TEST_F(ProjectStorage, synchronize_item_library_entries) UnorderedElementsAre(IsItemLibraryProperty("color", "color", "#blue")), IsEmpty()), IsItemLibraryEntry(fetchTypeId(sourceId1, "QQuickItem"), + "Item", "Item", "/path/icon3", "Advanced Items", @@ -7620,6 +7623,7 @@ TEST_F(ProjectStorage, synchronize_updates_item_library_entries) ASSERT_THAT(storage.itemLibraryEntries(fetchTypeId(sourceId2, "QObject")), ElementsAre( IsItemLibraryEntry(fetchTypeId(sourceId2, "QObject"), + "Object", "Foo", "/path/icon", "Basic Items", @@ -7698,6 +7702,7 @@ TEST_F(ProjectStorage, get_all_item_library_entries) entries, UnorderedElementsAre( IsItemLibraryEntry(fetchTypeId(sourceId2, "QObject"), + "Object", "Foo", "/path/icon", "Basic Items", @@ -7709,6 +7714,7 @@ TEST_F(ProjectStorage, get_all_item_library_entries) UnorderedElementsAre("/path/templates/frame.png", "/path/templates/frame.frag")), IsItemLibraryEntry(fetchTypeId(sourceId2, "QObject"), + "Object", "Bar", "/path/icon2", "Basic Items", @@ -7718,6 +7724,7 @@ TEST_F(ProjectStorage, get_all_item_library_entries) UnorderedElementsAre(IsItemLibraryProperty("color", "color", "#blue")), IsEmpty()), IsItemLibraryEntry(fetchTypeId(sourceId1, "QQuickItem"), + "Item", "Item", "/path/icon3", "Advanced Items", @@ -7743,6 +7750,7 @@ TEST_F(ProjectStorage, get_all_item_library_entries_handles_no_entries) ASSERT_THAT(entries, UnorderedElementsAre( IsItemLibraryEntry(fetchTypeId(sourceId1, "QQuickItem"), + "Item", "Item", "/path/icon3", "Advanced Items", @@ -7769,6 +7777,7 @@ TEST_F(ProjectStorage, get_item_library_entries_by_type_id) entries, UnorderedElementsAre( IsItemLibraryEntry(fetchTypeId(sourceId2, "QObject"), + "Object", "Foo", "/path/icon", "Basic Items", @@ -7780,6 +7789,7 @@ TEST_F(ProjectStorage, get_item_library_entries_by_type_id) UnorderedElementsAre("/path/templates/frame.png", "/path/templates/frame.frag")), IsItemLibraryEntry(fetchTypeId(sourceId2, "QObject"), + "Object", "Bar", "/path/icon2", "Basic Items", @@ -7832,6 +7842,7 @@ TEST_F(ProjectStorage, get_item_library_entries_by_source_id) entries, UnorderedElementsAre( IsItemLibraryEntry(fetchTypeId(sourceId2, "QObject"), + "Object", "Foo", "/path/icon", "Basic Items", @@ -7843,6 +7854,7 @@ TEST_F(ProjectStorage, get_item_library_entries_by_source_id) UnorderedElementsAre("/path/templates/frame.png", "/path/templates/frame.frag")), IsItemLibraryEntry(fetchTypeId(sourceId2, "QObject"), + "Object", "Bar", "/path/icon2", "Basic Items", diff --git a/tests/unit/tests/unittests/projectstorage/typeannotationreader-test.cpp b/tests/unit/tests/unittests/projectstorage/typeannotationreader-test.cpp index 93c7caa65a7..cb3bd6e05f9 100644 --- a/tests/unit/tests/unittests/projectstorage/typeannotationreader-test.cpp +++ b/tests/unit/tests/unittests/projectstorage/typeannotationreader-test.cpp @@ -12,10 +12,12 @@ namespace { +using QmlDesigner::FlagIs; class TypeAnnotationReader : public testing::Test { protected: + TypeAnnotationReader() { traits.canBeDroppedInFormEditor = FlagIs::True; } static void SetUpTestSuite() { static_database = std::make_unique(":memory:", Sqlite::JournalMode::Memory); @@ -43,6 +45,7 @@ protected: QmlDesigner::Storage::TypeAnnotationReader reader{storage}; QmlDesigner::SourceId sourceId = QmlDesigner::SourceId::create(33); QmlDesigner::SourceId directorySourceId = QmlDesigner::SourceId::create(77); + QmlDesigner::Storage::TypeTraits traits; }; TEST_F(TypeAnnotationReader, parse_type) @@ -58,7 +61,6 @@ TEST_F(TypeAnnotationReader, parse_type) icon: "images/item-icon16.png" } })xy"}; - QmlDesigner::Storage::TypeTraits traits; auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId, directorySourceId); @@ -95,7 +97,6 @@ TEST_F(TypeAnnotationReader, parse_true_canBeContainer) } } })xy"}; - QmlDesigner::Storage::TypeTraits traits; traits.canBeContainer = FlagIs::True; auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId, directorySourceId); @@ -125,7 +126,6 @@ TEST_F(TypeAnnotationReader, parse_true_forceClip) } } })xy"}; - QmlDesigner::Storage::TypeTraits traits; traits.forceClip = FlagIs::True; auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId, directorySourceId); @@ -155,7 +155,6 @@ TEST_F(TypeAnnotationReader, parse_true_doesLayoutChildren) } } })xy"}; - QmlDesigner::Storage::TypeTraits traits; traits.doesLayoutChildren = FlagIs::True; auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId, directorySourceId); @@ -181,12 +180,11 @@ TEST_F(TypeAnnotationReader, parse_true_canBeDroppedInFormEditor) icon: "images/frame-icon16.png" Hints { - canBeDroppedInFormEditor: true + canBeDroppedInFormEditor: false } } })xy"}; - QmlDesigner::Storage::TypeTraits traits; - traits.canBeDroppedInFormEditor = FlagIs::True; + traits.canBeDroppedInFormEditor = FlagIs::False; auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId, directorySourceId); @@ -215,7 +213,6 @@ TEST_F(TypeAnnotationReader, parse_true_canBeDroppedInNavigator) } } })xy"}; - QmlDesigner::Storage::TypeTraits traits; traits.canBeDroppedInNavigator = FlagIs::True; auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId, directorySourceId); @@ -245,7 +242,6 @@ TEST_F(TypeAnnotationReader, parse_true_canBeDroppedInView3D) } } })xy"}; - QmlDesigner::Storage::TypeTraits traits; traits.canBeDroppedInView3D = FlagIs::True; auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId, directorySourceId); @@ -275,7 +271,6 @@ TEST_F(TypeAnnotationReader, parse_true_isMovable) } } })xy"}; - QmlDesigner::Storage::TypeTraits traits; traits.isMovable = FlagIs::True; auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId, directorySourceId); @@ -305,7 +300,6 @@ TEST_F(TypeAnnotationReader, parse_true_isResizable) } } })xy"}; - QmlDesigner::Storage::TypeTraits traits; traits.isResizable = FlagIs::True; auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId, directorySourceId); @@ -335,7 +329,6 @@ TEST_F(TypeAnnotationReader, parse_true_hasFormEditorItem) } } })xy"}; - QmlDesigner::Storage::TypeTraits traits; traits.hasFormEditorItem = FlagIs::True; auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId, directorySourceId); @@ -365,7 +358,6 @@ TEST_F(TypeAnnotationReader, parse_true_isStackedContainer) } } })xy"}; - QmlDesigner::Storage::TypeTraits traits; traits.isStackedContainer = FlagIs::True; auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId, directorySourceId); @@ -395,7 +387,6 @@ TEST_F(TypeAnnotationReader, parse_true_takesOverRenderingOfChildren) } } })xy"}; - QmlDesigner::Storage::TypeTraits traits; traits.takesOverRenderingOfChildren = FlagIs::True; auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId, directorySourceId); @@ -425,7 +416,6 @@ TEST_F(TypeAnnotationReader, parse_true_visibleInNavigator) } } })xy"}; - QmlDesigner::Storage::TypeTraits traits; traits.visibleInNavigator = FlagIs::True; auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId, directorySourceId); @@ -455,7 +445,6 @@ TEST_F(TypeAnnotationReader, parse_true_visibleInLibrary) } } })xy"}; - QmlDesigner::Storage::TypeTraits traits; traits.visibleInLibrary = FlagIs::True; auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId, directorySourceId); @@ -485,7 +474,7 @@ TEST_F(TypeAnnotationReader, parse_false) } } })xy"}; - QmlDesigner::Storage::TypeTraits traits; + traits.canBeDroppedInFormEditor = FlagIs::True; auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId, directorySourceId); @@ -524,9 +513,9 @@ TEST_F(TypeAnnotationReader, parse_complex_expression) } } })xy"}; - QmlDesigner::Storage::TypeTraits frameTraits; + QmlDesigner::Storage::TypeTraits frameTraits = traits; frameTraits.isMovable = QmlDesigner::FlagIs::Set; - QmlDesigner::Storage::TypeTraits itemTraits; + QmlDesigner::Storage::TypeTraits itemTraits = traits; itemTraits.canBeContainer = QmlDesigner::FlagIs::True; auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId, directorySourceId); @@ -576,7 +565,6 @@ TEST_F(TypeAnnotationReader, parse_item_library_entry) } } })xy"}; - QmlDesigner::Storage::TypeTraits traits; auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId, directorySourceId); @@ -633,7 +621,6 @@ TEST_F(TypeAnnotationReader, parse_item_library_entry_with_properties) } } })xy"}; - QmlDesigner::Storage::TypeTraits traits; auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId, directorySourceId); @@ -684,7 +671,6 @@ TEST_F(TypeAnnotationReader, parse_item_library_entry_template_path) } } })xy"}; - QmlDesigner::Storage::TypeTraits traits; auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId, directorySourceId); @@ -737,7 +723,6 @@ TEST_F(TypeAnnotationReader, parse_item_library_entry_extra_file_paths) } } })xy"}; - QmlDesigner::Storage::TypeTraits traits; auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId, directorySourceId); From c7790ab9814ec0154e044f046c710d6aa96e6060 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Tue, 30 Apr 2024 16:08:40 +0200 Subject: [PATCH 043/154] QmlDesigner: Suppress QDS compnent warnings in template Change-Id: I409866d43282d425795e8afe0bcb824d860a2a84 Reviewed-by: Fabian Kosmale --- .../qmlprojectmanager/cmakegen/templates/qmlcomponents.tpl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/plugins/qmlprojectmanager/cmakegen/templates/qmlcomponents.tpl b/src/plugins/qmlprojectmanager/cmakegen/templates/qmlcomponents.tpl index a9f20243a69..e6cd7c26dd3 100644 --- a/src/plugins/qmlprojectmanager/cmakegen/templates/qmlcomponents.tpl +++ b/src/plugins/qmlprojectmanager/cmakegen/templates/qmlcomponents.tpl @@ -3,6 +3,7 @@ message("Building designer components.") +set(QT_QDS_COMPONENTS_NOWARN on) set(QT_QML_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/qml") include(FetchContent) From 5a8b389ddd3f18605469bad10cd429d89758a1c2 Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Thu, 18 Apr 2024 11:49:03 +0300 Subject: [PATCH 044/154] QmlDesigner: Create and assign a model to the dropped GridView The delegate for GridView is different from ListView. Also cellWidth and cellHeight should be specified for the GridView Also the delegate for the listView is modified to clip and elide the text. Task-number: QDS-12306 Fixes: QDS-12386 Change-Id: I7e75b9e7f234c5e021637e703088539886a8f25f Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Mahmoud Badri Reviewed-by: Miikka Heikkinen --- .../collectioneditor/collectionview.cpp | 147 ++++++++++++++---- .../collectioneditor/collectionview.h | 2 + .../modelnodecontextmenu_helper.h | 2 +- .../designercore/include/nodemetainfo.h | 1 + .../designercore/metainfo/nodemetainfo.cpp | 16 ++ 5 files changed, 136 insertions(+), 32 deletions(-) diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp index 151c7aca3ed..9221919df45 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp @@ -49,6 +49,18 @@ inline void setVariantPropertyValue(const QmlDesigner::ModelNode &node, property.setValue(value); } +inline void setNodePropertyValue(const QmlDesigner::ModelNode &node, + const QmlDesigner::PropertyName &propertyName, + const QmlDesigner::ModelNode &value) +{ + QmlDesigner::NodeProperty nodeProperty = node.nodeProperty(propertyName); + // Remove the old model node if is available + if (nodeProperty.modelNode()) + nodeProperty.modelNode().destroy(); + + nodeProperty.setModelNode(value); +} + inline void setBindingPropertyExpression(const QmlDesigner::ModelNode &node, const QmlDesigner::PropertyName &propertyName, const QString &expression) @@ -256,7 +268,6 @@ void CollectionView::assignCollectionToNode(const QString &collectionName, const if (!m_widget) return; - using DataType = CollectionDetails::DataType; executeInTransaction("CollectionView::assignCollectionToNode", [&]() { m_dataStore->assignCollectionToNode( this, @@ -273,35 +284,14 @@ void CollectionView::assignCollectionToNode(const QString &collectionName, const // Create and assign a delegate to the list view item if (node.metaInfo().isQtQuickListView()) { - CollectionDetails collection = m_widget->collectionDetailsModel()->upToDateConstCollection( - {dataStoreNode(), collectionName}); - - ModelNode rowItem(createModelNode("QtQuick.Row")); - ::setVariantPropertyValue(rowItem, "spacing", 5); - - const int columnsCount = collection.columns(); - for (int column = 0; column < columnsCount; ++column) { - const DataType dataType = collection.typeAt(column); - const QString columnName = collection.propertyAt(column); - ModelNode cellItem; - if (dataType == DataType::Color) { - cellItem = createModelNode("QtQuick.Rectangle"); - ::setBindingPropertyExpression(cellItem, "color", columnName); - ::setVariantPropertyValue(cellItem, "height", 20); - } else { - cellItem = createModelNode("QtQuick.Text"); - ::setBindingPropertyExpression(cellItem, "text", columnName); - } - ::setVariantPropertyValue(cellItem, "width", 100); - rowItem.defaultNodeAbstractProperty().reparentHere(cellItem); - } - - NodeProperty delegateProperty = node.nodeProperty("delegate"); - // Remove the old model node if is available - if (delegateProperty.modelNode()) - delegateProperty.modelNode().destroy(); - - delegateProperty.setModelNode(rowItem); + ::setNodePropertyValue(node, "delegate", createListViewDelegate(collectionName)); + } else if (node.metaInfo().isQtQuickGridView()) { + QSize delegateSize; + ::setNodePropertyValue(node, + "delegate", + createGridViewDelegate(collectionName, delegateSize)); + ::setVariantPropertyValue(node, "cellWidth", delegateSize.width()); + ::setVariantPropertyValue(node, "cellHeight", delegateSize.height()); } }); } @@ -439,7 +429,7 @@ void CollectionView::onItemLibraryNodeCreated(const ModelNode &node) if (!m_widget) return; - if (node.metaInfo().isQtQuickListView()) { + if (node.metaInfo().isListOrGridView()) { addTask(QSharedPointer( new DropListViewTask(this, m_widget->listModel(), node))); } @@ -454,6 +444,101 @@ void CollectionView::addTask(QSharedPointer task) m_delayedTasks << task; } +ModelNode CollectionView::createListViewDelegate(const QString &collectionName) +{ + using DataType = CollectionDetails::DataType; + using namespace Qt::StringLiterals; + + CollectionDetails collection = m_widget->collectionDetailsModel()->upToDateConstCollection( + {dataStoreNode(), collectionName}); + + ModelNode rowItem(createModelNode("QtQuick.Row")); + ::setVariantPropertyValue(rowItem, "spacing", 5); + + const int columnsCount = collection.columns(); + for (int column = 0; column < columnsCount; ++column) { + const DataType dataType = collection.typeAt(column); + const QString columnName = collection.propertyAt(column); + ModelNode cellItem; + if (dataType == DataType::Color) { + cellItem = createModelNode("QtQuick.Rectangle"); + ::setBindingPropertyExpression(cellItem, "color", columnName); + ::setVariantPropertyValue(cellItem, "height", 20); + } else { + cellItem = createModelNode("QtQuick.Text"); + ::setBindingPropertyExpression(cellItem, "text", columnName); + ::setVariantPropertyValue(cellItem, "clip", true); + ::setBindingPropertyExpression(cellItem, "elide", "Text.ElideRight"_L1); + ::setVariantPropertyValue(cellItem, "leftPadding", 1); + ::setVariantPropertyValue(cellItem, "rightPadding", 1); + } + ::setVariantPropertyValue(cellItem, "width", 100); + rowItem.defaultNodeAbstractProperty().reparentHere(cellItem); + } + return rowItem; +} + +ModelNode CollectionView::createGridViewDelegate(const QString &collectionName, QSize &delegateSize) +{ + using DataType = CollectionDetails::DataType; + using namespace Qt::StringLiterals; + + CollectionDetails collection = m_widget->collectionDetailsModel()->upToDateConstCollection( + {dataStoreNode(), collectionName}); + + constexpr int spacing = 5; + int delegateWidth = 70; + int delegateHeight = 0; + + ModelNode rectItem(createModelNode("QtQuick.Rectangle")); + ::setVariantPropertyValue(rectItem, "clip", true); + ::setVariantPropertyValue(rectItem, "border.color", QColor(Qt::blue)); + ::setVariantPropertyValue(rectItem, "border.width", 1); + + ModelNode colItem(createModelNode("QtQuick.Column")); + ::setVariantPropertyValue(colItem, "spacing", spacing); + ::setVariantPropertyValue(colItem, "topPadding", spacing); + ::setBindingPropertyExpression(colItem, "anchors.fill", "parent"_L1); + + const int columnsCount = collection.columns(); + for (int column = 0; column < columnsCount; ++column) { + const DataType dataType = collection.typeAt(column); + const QString columnName = collection.propertyAt(column); + ModelNode cellItem; + if (dataType == DataType::Color) { + cellItem = createModelNode("QtQuick.Rectangle"); + ::setBindingPropertyExpression(cellItem, "color", columnName); + ::setVariantPropertyValue(cellItem, "width", 40); + ::setVariantPropertyValue(cellItem, "height", 40); + delegateHeight += 40; + } else { + cellItem = createModelNode("QtQuick.Text"); + ::setBindingPropertyExpression(cellItem, "width", "parent.width"_L1); + ::setVariantPropertyValue(cellItem, "height", 20); + ::setBindingPropertyExpression(cellItem, "text", columnName); + ::setVariantPropertyValue(cellItem, "clip", true); + ::setBindingPropertyExpression(cellItem, "elide", "Text.ElideRight"_L1); + ::setVariantPropertyValue(cellItem, "leftPadding", 1); + ::setVariantPropertyValue(cellItem, "rightPadding", 1); + ::setBindingPropertyExpression(cellItem, "horizontalAlignment", "Text.AlignHCenter"_L1); + delegateHeight += 20; + } + delegateHeight += spacing; + ::setBindingPropertyExpression(cellItem, + "anchors.horizontalCenter", + "parent.horizontalCenter"_L1); + colItem.defaultNodeAbstractProperty().reparentHere(cellItem); + } + + rectItem.defaultNodeAbstractProperty().reparentHere(colItem); + ::setVariantPropertyValue(rectItem, "width", delegateWidth); + ::setVariantPropertyValue(rectItem, "height", delegateHeight); + delegateSize.setWidth(delegateWidth); + delegateSize.setHeight(delegateHeight); + + return rectItem; +} + CollectionTask::CollectionTask(CollectionView *view, CollectionListModel *listModel) : m_collectionView(view) , m_listModel(listModel) diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionview.h b/src/plugins/qmldesigner/components/collectioneditor/collectionview.h index 3de3bd7ae6d..20cb045374d 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionview.h +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionview.h @@ -72,6 +72,8 @@ private: void ensureStudioModelImport(); void onItemLibraryNodeCreated(const ModelNode &node); void addTask(QSharedPointer task); + ModelNode createListViewDelegate(const QString &collectionName); + ModelNode createGridViewDelegate(const QString &collectionName, QSize &delegateSize); std::unique_ptr m_dataStore; Utils::UniqueObjectPtr m_widget; diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.h b/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.h index a7060fcdc6c..1a1284f35ee 100644 --- a/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.h +++ b/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.h @@ -89,7 +89,7 @@ inline bool hasCollectionAsModel(const SelectionContext &selectionState) const ModelNode singleSelectedNode = selectionState.currentSingleSelectedNode(); - return singleSelectedNode.metaInfo().isQtQuickListView() + return singleSelectedNode.metaInfo().isListOrGridView() && singleSelectedNode.property("model").toBindingProperty().expression().startsWith( "DataStore."); } diff --git a/src/plugins/qmldesigner/designercore/include/nodemetainfo.h b/src/plugins/qmldesigner/designercore/include/nodemetainfo.h index 9c8e32002f6..fd3f2f9be8d 100644 --- a/src/plugins/qmldesigner/designercore/include/nodemetainfo.h +++ b/src/plugins/qmldesigner/designercore/include/nodemetainfo.h @@ -185,6 +185,7 @@ public: bool isQtQuick3DLight() const; bool isQtQuickListModel() const; bool isQtQuickListView() const; + bool isQtQuickGridView() const; bool isQtQuick3DMaterial() const; bool isQtQuick3DModel() const; bool isQtQuick3DNode() const; diff --git a/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp b/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp index 04c151444d5..502b2675ccc 100644 --- a/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp +++ b/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp @@ -3358,6 +3358,22 @@ bool NodeMetaInfo::isQtQuickListView() const } } +bool QmlDesigner::NodeMetaInfo::isQtQuickGridView() const +{ + if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick.GridView"_t, category(), keyValue("type id", m_typeId)}; + + using namespace Storage::Info; + return isBasedOnCommonType(m_projectStorage, m_typeId); + } else { + return isValid() && (isSubclassOf("QtQuick.GridView")); + } +} + bool NodeMetaInfo::isQtQuick3DInstanceList() const { if constexpr (useProjectStorage()) { From d877e1bb1f6fb1e41f4c0fc4010ec368ec61af49 Mon Sep 17 00:00:00 2001 From: Knud Dollereder Date: Tue, 30 Apr 2024 16:36:24 +0200 Subject: [PATCH 045/154] QmlProjectManager: Fix splitting a path by separator Fixes: QDS-12633 Change-Id: Ide81944e6f9bf08723576aa86d3a1f977c4a7e3d Reviewed-by: Thomas Hartmann --- src/plugins/qmlprojectmanager/cmakegen/cmakegenerator.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/plugins/qmlprojectmanager/cmakegen/cmakegenerator.cpp b/src/plugins/qmlprojectmanager/cmakegen/cmakegenerator.cpp index f4b3d9ec531..9ae576ec35a 100644 --- a/src/plugins/qmlprojectmanager/cmakegen/cmakegenerator.cpp +++ b/src/plugins/qmlprojectmanager/cmakegen/cmakegenerator.cpp @@ -330,8 +330,7 @@ NodePtr CMakeGenerator::findOrCreateNode(NodePtr &node, const Utils::FilePath &p }; const Utils::FilePath relative = path.relativeChildPath(node->dir); - const QChar separator = relative.pathComponentSeparator(); - const QList components = relative.pathView().split(separator); + const QList components = relative.pathView().split('/'); NodePtr lastNode = node; for (const auto &comp : components) { From 522805629ad15c85b09692edb1f06eea0fe5b9ae Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Thu, 2 May 2024 11:32:37 +0200 Subject: [PATCH 046/154] QmlDesigner: Propagate annotation type traits recursivly Task-number: QDS-12661 Change-Id: I593eeaaa3800c450a0c2b9f08ae1993ddc29cd8f Reviewed-by: Thomas Hartmann --- .../projectstorage/projectstorage.cpp | 35 ++++++++++-- .../projectstorage/projectstorage-test.cpp | 57 +++++++++++++++---- 2 files changed, 76 insertions(+), 16 deletions(-) diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.cpp b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.cpp index 74a4b946f11..ea35fec3722 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.cpp +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.cpp @@ -90,7 +90,19 @@ struct ProjectStorage::Statements Sqlite::WriteStatement<2> updateTypeTraitStatement{ "UPDATE types SET traits = ?2 WHERE typeId=?1", database}; Sqlite::WriteStatement<2> updateTypeAnnotationTraitStatement{ - "UPDATE types SET annotationTraits = ?2 WHERE typeId=?1", database}; + "WITH RECURSIVE " + " typeSelection(typeId) AS (" + " VALUES(?1) " + " UNION ALL " + " SELECT t.typeId " + " FROM types AS t JOIN typeSelection AS ts " + " WHERE prototypeId=ts.typeId " + " AND t.typeId NOT IN (SELECT typeId FROM typeAnnotations)) " + "UPDATE types AS t " + "SET annotationTraits = ?2 " + "FROM typeSelection ts " + "WHERE t.typeId=ts.typeId", + database}; Sqlite::ReadStatement<1, 2> selectNotUpdatedTypesInSourcesStatement{ "SELECT DISTINCT typeId FROM types WHERE (sourceId IN carray(?1) AND typeId NOT IN " "carray(?2))", @@ -601,6 +613,11 @@ struct ProjectStorage::Statements "UPDATE types SET defaultPropertyId=NULL WHERE defaultPropertyId=?1", database}; mutable Sqlite::ReadStatement<3, 1> selectInfoTypeByTypeIdStatement{ "SELECT sourceId, traits, annotationTraits FROM types WHERE typeId=?", database}; + mutable Sqlite::ReadStatement<1, 1> selectPrototypeAnnotationTraitsByTypeIdStatement{ + "SELECT annotationTraits " + "FROM types " + "WHERE typeId=(SELECT prototypeId FROM types WHERE typeId=?)", + database}; mutable Sqlite::ReadStatement<1, 1> selectDefaultPropertyDeclarationIdStatement{ "SELECT defaultPropertyId FROM types WHERE typeId=?", database}; mutable Sqlite::ReadStatement<1, 1> selectPrototypeIdsForTypeIdInOrderStatement{ @@ -2316,7 +2333,6 @@ void ProjectStorage::synchronizeTypeAnnotations(Storage::Synchronization::TypeAn if (!annotation.sourceId) throw TypeAnnotationHasInvalidSourceId{}; - synchronizeTypeTraits(annotation.typeId, annotation.traits); using NanotraceHR::keyValue; NanotraceHR::Tracer tracer{"insert type annotations"_t, @@ -2330,11 +2346,12 @@ void ProjectStorage::synchronizeTypeAnnotations(Storage::Synchronization::TypeAn annotation.iconPath, createEmptyAsNull(annotation.itemLibraryJson), createEmptyAsNull(annotation.hintsJson)); + + synchronizeTypeTraits(annotation.typeId, annotation.traits); }; auto update = [&](const TypeAnnotationView &annotationFromDatabase, const TypeAnnotation &annotation) { - synchronizeTypeTraits(annotation.typeId, annotation.traits); if (annotationFromDatabase.typeName != annotation.typeName || annotationFromDatabase.iconPath != annotation.iconPath @@ -2352,21 +2369,29 @@ void ProjectStorage::synchronizeTypeAnnotations(Storage::Synchronization::TypeAn annotation.iconPath, createEmptyAsNull(annotation.itemLibraryJson), createEmptyAsNull(annotation.hintsJson)); + + synchronizeTypeTraits(annotation.typeId, annotation.traits); + return Sqlite::UpdateChange::Update; } + synchronizeTypeTraits(annotation.typeId, annotation.traits); + return Sqlite::UpdateChange::No; }; auto remove = [&](const TypeAnnotationView &annotationFromDatabase) { - synchronizeTypeTraits(annotationFromDatabase.typeId, Storage::TypeTraits{}); - using NanotraceHR::keyValue; NanotraceHR::Tracer tracer{"remove type annotations"_t, projectStorageCategory(), keyValue("type annotation", annotationFromDatabase)}; + auto prototypeAnnotationTraits = s->selectPrototypeAnnotationTraitsByTypeIdStatement + .value(annotationFromDatabase.typeId); s->deleteTypeAnnotationStatement.write(annotationFromDatabase.typeId); + + s->updateTypeAnnotationTraitStatement.write(annotationFromDatabase.typeId, + prototypeAnnotationTraits); }; Sqlite::insertUpdateDelete(range, typeAnnotations, compareKey, insert, update, remove); diff --git a/tests/unit/tests/unittests/projectstorage/projectstorage-test.cpp b/tests/unit/tests/unittests/projectstorage/projectstorage-test.cpp index cd817a1d06f..cc3b4b705d3 100644 --- a/tests/unit/tests/unittests/projectstorage/projectstorage-test.cpp +++ b/tests/unit/tests/unittests/projectstorage/projectstorage-test.cpp @@ -7358,9 +7358,6 @@ TEST_F(ProjectStorage, do_not_synchronize_type_annotations_without_type) package.typeAnnotations = createTypeAnnotions(); package.updatedTypeAnnotationSourceIds = createUpdatedTypeAnnotionSourceIds( package.typeAnnotations); - TypeTraits traits{TypeTraitsKind::Reference}; - traits.canBeContainer = FlagIs::True; - traits.visibleInLibrary = FlagIs::True; storage.synchronize(package); @@ -7382,13 +7379,29 @@ TEST_F(ProjectStorage, synchronize_type_annotation_type_traits) ASSERT_THAT(storage.type(fetchTypeId(sourceId2, "QObject"))->traits, traits); } +TEST_F(ProjectStorage, synchronize_type_annotation_type_traits_for_prototype_heirs) +{ + auto package{createSimpleSynchronizationPackage()}; + package.typeAnnotations = createTypeAnnotions(); + package.typeAnnotations.pop_back(); + 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(sourceId1, "QQuickItem"))->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( + annotationPackage.updatedTypeAnnotationSourceIds = createUpdatedTypeAnnotionSourceIds( package.typeAnnotations); TypeTraits traits{TypeTraitsKind::Reference}; traits.canBeContainer = FlagIs::True; @@ -7399,25 +7412,47 @@ TEST_F(ProjectStorage, synchronize_updates_type_annotation_type_traits) ASSERT_THAT(storage.type(fetchTypeId(sourceId2, "QObject"))->traits, traits); } +TEST_F(ProjectStorage, synchronize_updates_type_annotation_type_traits_for_prototype_heirs) +{ + auto package{createSimpleSynchronizationPackage()}; + storage.synchronize(package); + SynchronizationPackage annotationPackage; + annotationPackage.typeAnnotations = createTypeAnnotions(); + annotationPackage.typeAnnotations.pop_back(); + annotationPackage.updatedTypeAnnotationSourceIds = createUpdatedTypeAnnotionSourceIds( + package.typeAnnotations); + TypeTraits traits{TypeTraitsKind::Reference}; + traits.canBeContainer = FlagIs::True; + traits.visibleInLibrary = FlagIs::True; + + storage.synchronize(annotationPackage); + + ASSERT_THAT(storage.type(fetchTypeId(sourceId1, "QQuickItem"))->traits, traits); +} + TEST_F(ProjectStorage, synchronize_clears_annotation_type_traits_if_annotation_was_removed) +{ + +} + +TEST_F(ProjectStorage, + synchronize_clears_annotation_type_traits_if_annotation_was_removed_for_prototype_heirs) { 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); + package.typeAnnotations.pop_back(); storage.synchronize(package); - ASSERT_THAT(storage.type(fetchTypeId(sourceId2, "QObject"))->traits, traits); + ASSERT_THAT(storage.type(fetchTypeId(sourceId1, "QQuickItem"))->traits, + package.typeAnnotations[0].traits); } -TEST_F(ProjectStorage, synchronize_updatesannotation_type_traits) +TEST_F(ProjectStorage, synchronize_updates_annotation_type_traits) { auto package{createSimpleSynchronizationPackage()}; package.typeAnnotations = createTypeAnnotions(); From 7525ef79dea1cba831275ef687f7edd97106e5f1 Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Tue, 30 Apr 2024 12:56:02 +0300 Subject: [PATCH 047/154] QmlDesigner: Use only 1 content library importer for all bundles Change-Id: I59e8efda2e5704caf72297b8dee5178eb8ecfc71 Reviewed-by: Miikka Heikkinen Reviewed-by: Qt CI Patch Build Bot --- .../ContentLibraryEffect.qml | 2 +- .../ContentLibraryEffectContextMenu.qml | 2 +- .../ContentLibraryMaterialsView.qml | 4 +- .../ContentLibraryUserView.qml | 4 +- .../contentlibrarybundleimporter.cpp | 43 +-- .../contentlibrarybundleimporter.h | 12 +- .../contentlibraryeffectsmodel.cpp | 106 ++------ .../contentlibraryeffectsmodel.h | 25 +- .../contentlibrarymaterialsmodel.cpp | 106 ++------ .../contentlibrarymaterialsmodel.h | 28 +- .../contentlibraryusermodel.cpp | 101 +------ .../contentlibrary/contentlibraryusermodel.h | 22 +- .../contentlibrary/contentlibraryview.cpp | 247 ++++++++---------- .../contentlibrary/contentlibraryview.h | 10 +- .../contentlibrary/contentlibrarywidget.cpp | 76 +++++- .../contentlibrary/contentlibrarywidget.h | 16 +- .../designercore/generatedcomponentutils.cpp | 31 +++ .../designercore/generatedcomponentutils.h | 6 + .../qmldesigner/qmldesignerconstants.h | 3 + 19 files changed, 355 insertions(+), 489 deletions(-) diff --git a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryEffect.qml b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryEffect.qml index b20cc71e156..bf60ef66115 100644 --- a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryEffect.qml +++ b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryEffect.qml @@ -23,7 +23,7 @@ Item { acceptedButtons: Qt.LeftButton | Qt.RightButton onPressed: (mouse) => { - if (mouse.button === Qt.LeftButton && !ContentLibraryBackend.effectsModel.importerRunning) + if (mouse.button === Qt.LeftButton && !ContentLibraryBackend.rootView.importerRunning) ContentLibraryBackend.rootView.startDragEffect(modelData, mapToGlobal(mouse.x, mouse.y)) else if (mouse.button === Qt.RightButton) root.showContextMenu() diff --git a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryEffectContextMenu.qml b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryEffectContextMenu.qml index 1e0bcf1eb40..008f99d4fa7 100644 --- a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryEffectContextMenu.qml +++ b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryEffectContextMenu.qml @@ -12,7 +12,7 @@ StudioControls.Menu { property var targetItem: null - readonly property bool targetAvailable: targetItem && !ContentLibraryBackend.effectsModel.importerRunning + readonly property bool targetAvailable: targetItem && !ContentLibraryBackend.rootView.importerRunning signal unimport(var bundleEff); diff --git a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryMaterialsView.qml b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryMaterialsView.qml index 9a0e33b8e5e..3e58dd4306e 100644 --- a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryMaterialsView.qml +++ b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryMaterialsView.qml @@ -50,7 +50,7 @@ HelperWidgets.ScrollView { id: ctxMenu hasModelSelection: root.materialsModel.hasModelSelection - importerRunning: root.materialsModel.importerRunning + importerRunning: ContentLibraryBackend.rootView.importerRunning onApplyToSelected: (add) => root.materialsModel.applyToSelected(ctxMenu.targetMaterial, add) @@ -103,7 +103,7 @@ HelperWidgets.ScrollView { width: root.cellWidth height: root.cellHeight - importerRunning: root.materialsModel.importerRunning + importerRunning: ContentLibraryBackend.rootView.importerRunning onShowContextMenu: ctxMenu.popupMenu(modelData) onAddToProject: root.materialsModel.addToProject(modelData) diff --git a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryUserView.qml b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryUserView.qml index 8fd196dbcde..8528c75e739 100644 --- a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryUserView.qml +++ b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryUserView.qml @@ -52,7 +52,7 @@ HelperWidgets.ScrollView { enableRemove: true hasModelSelection: ContentLibraryBackend.userModel.hasModelSelection - importerRunning: ContentLibraryBackend.userModel.importerRunning + importerRunning: ContentLibraryBackend.rootView.importerRunning onApplyToSelected: (add) => ContentLibraryBackend.userModel.applyToSelected(ctxMenuMaterial.targetMaterial, add) @@ -114,7 +114,7 @@ HelperWidgets.ScrollView { width: root.cellWidth height: root.cellHeight - importerRunning: ContentLibraryBackend.userModel.importerRunning + importerRunning: ContentLibraryBackend.rootView.importerRunning onShowContextMenu: ctxMenuMaterial.popupMenu(modelData) onAddToProject: ContentLibraryBackend.userModel.addToProject(modelData) diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarybundleimporter.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarybundleimporter.cpp index d4859305538..6388c93fa96 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarybundleimporter.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarybundleimporter.cpp @@ -3,12 +3,13 @@ #include "contentlibrarybundleimporter.h" -#include "documentmanager.h" -#include "import.h" -#include "model.h" -#include "qmldesignerconstants.h" -#include "qmldesignerplugin.h" -#include "rewritingexception.h" +#include +#include +#include +#include +#include +#include +#include #include @@ -37,10 +38,10 @@ QString ContentLibraryBundleImporter::importComponent(const QString &bundleDir, const QStringList &files) { QString module = QString::fromLatin1(type.left(type.lastIndexOf('.'))); - QString bundleId = module.mid(module.lastIndexOf('.') + 1); + m_bundleId = module.mid(module.lastIndexOf('.') + 1); FilePath bundleDirPath = FilePath::fromString(bundleDir); // source dir - FilePath bundleImportPath = resolveBundleImportPath(bundleId); // target dir + FilePath bundleImportPath = resolveBundleImportPath(m_bundleId); // target dir if (bundleImportPath.isEmpty()) return "Failed to resolve bundle import folder"; @@ -145,9 +146,11 @@ void ContentLibraryBundleImporter::handleImportTimer() for (const TypeName &pendingType : pendingTypes) { m_pendingTypes.remove(pendingType); if (m_pendingTypes.value(pendingType)) - emit importFinished({}); + emit importFinished({}, m_bundleId); else - emit unimportFinished({}); + emit unimportFinished({}, m_bundleId); + + m_bundleId.clear(); } }; @@ -193,12 +196,14 @@ void ContentLibraryBundleImporter::handleImportTimer() m_pendingTypes.remove(pendingType); if (isImport) #ifdef QDS_USE_PROJECTSTORAGE - emit importFinished(pendingType); + emit importFinished(pendingType, m_bundleId); #else - emit importFinished(metaInfo); + emit importFinished(metaInfo, m_bundleId); #endif else - emit unimportFinished(metaInfo); + emit unimportFinished(metaInfo, m_bundleId); + + m_bundleId.clear(); } } @@ -208,10 +213,10 @@ void ContentLibraryBundleImporter::handleImportTimer() } } -QVariantHash ContentLibraryBundleImporter::loadAssetRefMap(const Utils::FilePath &bundlePath) +QVariantHash ContentLibraryBundleImporter::loadAssetRefMap(const FilePath &bundlePath) { FilePath assetRefPath = bundlePath.resolvePath(QLatin1String(Constants::COMPONENT_BUNDLES_ASSET_REF_FILE)); - const Utils::expected_str content = assetRefPath.fileContents(); + const expected_str content = assetRefPath.fileContents(); if (content) { QJsonParseError error; QJsonDocument bundleDataJsonDoc = QJsonDocument::fromJson(*content, &error); @@ -225,7 +230,7 @@ QVariantHash ContentLibraryBundleImporter::loadAssetRefMap(const Utils::FilePath return {}; } -void ContentLibraryBundleImporter::writeAssetRefMap(const Utils::FilePath &bundlePath, +void ContentLibraryBundleImporter::writeAssetRefMap(const FilePath &bundlePath, const QVariantHash &assetRefMap) { FilePath assetRefPath = bundlePath.resolvePath(QLatin1String(Constants::COMPONENT_BUNDLES_ASSET_REF_FILE)); @@ -239,9 +244,11 @@ void ContentLibraryBundleImporter::writeAssetRefMap(const Utils::FilePath &bundl QString ContentLibraryBundleImporter::unimportComponent(const TypeName &type, const QString &qmlFile) { QString module = QString::fromLatin1(type.left(type.lastIndexOf('.'))); - QString bundleId = module.mid(module.lastIndexOf('.') + 1); + m_bundleId = module.mid(module.lastIndexOf('.') + 1); - FilePath bundleImportPath = resolveBundleImportPath(bundleId); + emit aboutToUnimport(type, m_bundleId); + + FilePath bundleImportPath = resolveBundleImportPath(m_bundleId); if (bundleImportPath.isEmpty()) return QStringLiteral("Failed to resolve bundle import folder for: '%1'").arg(qmlFile); diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarybundleimporter.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarybundleimporter.h index 10786ead995..8155311e3e4 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarybundleimporter.h +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarybundleimporter.h @@ -3,7 +3,7 @@ #pragma once -#include +#include #include @@ -12,6 +12,8 @@ namespace QmlDesigner { +class NodeMetaInfo; + class ContentLibraryBundleImporter : public QObject { Q_OBJECT @@ -30,11 +32,12 @@ signals: // asynchronous part of the import. In this case all remaining pending imports have been // terminated, and will not receive separate importFinished notifications. #ifdef QDS_USE_PROJECTSTORAGE - void importFinished(const QmlDesigner::TypeName &typeName); + void importFinished(const QmlDesigner::TypeName &typeName, const QString &bundleId); #else - void importFinished(const QmlDesigner::NodeMetaInfo &metaInfo); + void importFinished(const QmlDesigner::NodeMetaInfo &metaInfo, const QString &bundleId); #endif - void unimportFinished(const QmlDesigner::NodeMetaInfo &metaInfo); + void unimportFinished(const QmlDesigner::NodeMetaInfo &metaInfo, const QString &bundleId); + void aboutToUnimport(const TypeName &type, const QString &bundleId); private: void handleImportTimer(); @@ -44,6 +47,7 @@ private: QTimer m_importTimer; int m_importTimerCount = 0; QString m_pendingImport; + QString m_bundleId; bool m_fullReset = false; QHash m_pendingTypes; // }; diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectsmodel.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectsmodel.cpp index b0ce0b235a2..7704c164fcd 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectsmodel.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectsmodel.cpp @@ -63,6 +63,11 @@ bool ContentLibraryEffectsModel::isValidIndex(int idx) const return idx > -1 && idx < rowCount(); } +QString ContentLibraryEffectsModel::bundleId() const +{ + return m_bundleId; +} + void ContentLibraryEffectsModel::updateIsEmpty() { bool anyCatVisible = Utils::anyOf(m_bundleCategories, [&](ContentLibraryEffectsCategory *cat) { @@ -88,48 +93,6 @@ QHash ContentLibraryEffectsModel::roleNames() const return roles; } -void ContentLibraryEffectsModel::createImporter() -{ - m_importer = new ContentLibraryBundleImporter(); -#ifdef QDS_USE_PROJECTSTORAGE - connect(m_importer, - &ContentLibraryBundleImporter::importFinished, - this, - [&](const QmlDesigner::TypeName &typeName) { - m_importerRunning = false; - emit importerRunningChanged(); - if (typeName.size()) { - emit bundleItemImported(typeName); - updateImportedState(); - } - }); -#else - connect(m_importer, - &ContentLibraryBundleImporter::importFinished, - this, - [&](const QmlDesigner::NodeMetaInfo &metaInfo) { - m_importerRunning = false; - emit importerRunningChanged(); - if (metaInfo.isValid()) { - emit bundleItemImported(metaInfo); - updateImportedState(); - } - }); -#endif - - connect(m_importer, &ContentLibraryBundleImporter::unimportFinished, this, - [&](const QmlDesigner::NodeMetaInfo &metaInfo) { - Q_UNUSED(metaInfo) - m_importerRunning = false; - emit importerRunningChanged(); - emit bundleItemUnimported(metaInfo); - updateImportedState(); - }); - - resetModel(); - updateIsEmpty(); -} - void ContentLibraryEffectsModel::loadBundle() { if (m_bundleExists || m_probeBundleDir) @@ -172,11 +135,9 @@ void ContentLibraryEffectsModel::loadBundle() } } - QString bundleType = QmlDesignerPlugin::instance()->documentManager() - .generatedComponentUtils().effectsBundleType(); - - // Substitute correct id to avoid issues with old bundles - m_bundleObj["id"] = bundleType.split('.').last(); + auto compUtils = QmlDesignerPlugin::instance()->documentManager().generatedComponentUtils(); + QString bundleType = compUtils.effectsBundleType(); + m_bundleId = compUtils.effectsBundleId(); const QJsonObject catsObj = m_bundleObj.value("categories").toObject(); const QStringList categories = catsObj.keys(); @@ -205,12 +166,13 @@ void ContentLibraryEffectsModel::loadBundle() m_bundleCategories.append(category); } - m_importerSharedFiles.clear(); + m_bundleSharedFiles.clear(); const QJsonArray sharedFilesArr = m_bundleObj.value("sharedFiles").toArray(); for (const QJsonValueConstRef &file : sharedFilesArr) - m_importerSharedFiles.append(file.toString()); + m_bundleSharedFiles.append(file.toString()); - createImporter(); + resetModel(); + updateIsEmpty(); m_bundlePath = bundleDir.path(); m_bundleExists = true; emit bundleExistsChanged(); @@ -226,11 +188,6 @@ bool ContentLibraryEffectsModel::bundleExists() const return m_bundleExists; } -ContentLibraryBundleImporter *ContentLibraryEffectsModel::bundleImporter() const -{ - return m_importer; -} - void ContentLibraryEffectsModel::setSearchText(const QString &searchText) { QString lowerSearchText = searchText.toLower(); @@ -250,20 +207,8 @@ void ContentLibraryEffectsModel::setSearchText(const QString &searchText) updateIsEmpty(); } -void ContentLibraryEffectsModel::updateImportedState() +void ContentLibraryEffectsModel::updateImportedState(const QStringList &importedItems) { - if (!m_importer) - return; - - QString bundleId = m_bundleObj.value("id").toString(); - Utils::FilePath bundlePath = m_importer->resolveBundleImportPath(bundleId); - - QStringList importedItems; - if (bundlePath.exists()) { - importedItems = transform(bundlePath.dirEntries({{"*.qml"}, QDir::Files}), - [](const Utils::FilePath &f) { return f.baseName(); }); - } - bool changed = false; for (ContentLibraryEffectsCategory *cat : std::as_const(m_bundleCategories)) changed |= cat->updateImportedState(importedItems); @@ -297,29 +242,24 @@ void ContentLibraryEffectsModel::resetModel() void ContentLibraryEffectsModel::addInstance(ContentLibraryEffect *bundleItem) { - QString err = m_importer->importComponent(m_bundlePath, bundleItem->type(), bundleItem->qml(), - bundleItem->files() + m_importerSharedFiles); + QString err = m_widget->importer()->importComponent(m_bundlePath, bundleItem->type(), + bundleItem->qml(), + bundleItem->files() + m_bundleSharedFiles); - if (err.isEmpty()) { - m_importerRunning = true; - emit importerRunningChanged(); - } else { + if (err.isEmpty()) + m_widget->setImporterRunning(true); + else qWarning() << __FUNCTION__ << err; - } } void ContentLibraryEffectsModel::removeFromProject(ContentLibraryEffect *bundleItem) { - emit bundleItemAboutToUnimport(bundleItem->type()); + QString err = m_widget->importer()->unimportComponent(bundleItem->type(), bundleItem->qml()); - QString err = m_importer->unimportComponent(bundleItem->type(), bundleItem->qml()); - - if (err.isEmpty()) { - m_importerRunning = true; - emit importerRunningChanged(); - } else { + if (err.isEmpty()) + m_widget->setImporterRunning(true); + else qWarning() << __FUNCTION__ << err; - } } } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectsmodel.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectsmodel.h index 8accec8819e..88f60f06b60 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectsmodel.h +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectsmodel.h @@ -3,15 +3,11 @@ #pragma once -#include "nodemetainfo.h" - #include -#include #include namespace QmlDesigner { -class ContentLibraryBundleImporter; class ContentLibraryEffect; class ContentLibraryEffectsCategory; class ContentLibraryWidget; @@ -23,7 +19,6 @@ class ContentLibraryEffectsModel : public QAbstractListModel Q_PROPERTY(bool bundleExists READ bundleExists NOTIFY bundleExistsChanged) Q_PROPERTY(bool isEmpty MEMBER m_isEmpty NOTIFY isEmptyChanged) Q_PROPERTY(bool hasRequiredQuick3DImport READ hasRequiredQuick3DImport NOTIFY hasRequiredQuick3DImportChanged) - Q_PROPERTY(bool importerRunning MEMBER m_importerRunning NOTIFY importerRunningChanged) public: ContentLibraryEffectsModel(ContentLibraryWidget *parent = nullptr); @@ -35,7 +30,7 @@ public: void loadBundle(); void setSearchText(const QString &searchText); - void updateImportedState(); + void updateImportedState(const QStringList &importedItems); void setQuick3DImportVersion(int major, int minor); @@ -46,40 +41,30 @@ public: void resetModel(); void updateIsEmpty(); - ContentLibraryBundleImporter *bundleImporter() const; - Q_INVOKABLE void addInstance(QmlDesigner::ContentLibraryEffect *bundleItem); Q_INVOKABLE void removeFromProject(QmlDesigner::ContentLibraryEffect *bundleItem); + QString bundleId() const; + signals: void isEmptyChanged(); void hasRequiredQuick3DImportChanged(); -#ifdef QDS_USE_PROJECTSTORAGE - void bundleItemImported(const QmlDesigner::TypeName &typeName); -#else - void bundleItemImported(const QmlDesigner::NodeMetaInfo &metaInfo); -#endif - void bundleItemAboutToUnimport(const QmlDesigner::TypeName &type); - void bundleItemUnimported(const QmlDesigner::NodeMetaInfo &metaInfo); - void importerRunningChanged(); void bundleExistsChanged(); private: bool isValidIndex(int idx) const; - void createImporter(); ContentLibraryWidget *m_widget = nullptr; QString m_searchText; QString m_bundlePath; - QStringList m_importerSharedFiles; + QString m_bundleId; + QStringList m_bundleSharedFiles; QList m_bundleCategories; QJsonObject m_bundleObj; - ContentLibraryBundleImporter *m_importer = nullptr; bool m_isEmpty = true; bool m_bundleExists = false; bool m_probeBundleDir = false; - bool m_importerRunning = false; int m_quick3dMajorVersion = -1; int m_quick3dMinorVersion = -1; diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.cpp index c6d0f04a14e..7f68ad379d0 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.cpp @@ -20,6 +20,7 @@ #include #include +#include #include #include #include @@ -195,10 +196,9 @@ void ContentLibraryMaterialsModel::downloadSharedFiles(const QDir &targetDir, co extractor->setAlwaysCreateDir(false); extractor->setClearTargetPathContents(false); - QObject::connect(extractor, &FileExtractor::finishedChanged, this, [this, downloader, extractor]() { + QObject::connect(extractor, &FileExtractor::finishedChanged, this, [downloader, extractor]() { downloader->deleteLater(); extractor->deleteLater(); - createImporter(); }); extractor->extract(); @@ -207,46 +207,9 @@ void ContentLibraryMaterialsModel::downloadSharedFiles(const QDir &targetDir, co downloader->start(); } -void ContentLibraryMaterialsModel::createImporter() +QString ContentLibraryMaterialsModel::bundleId() const { - m_importer = new ContentLibraryBundleImporter(); -#ifdef QDS_USE_PROJECTSTORAGE - connect(m_importer, - &ContentLibraryBundleImporter::importFinished, - this, - [&](const QmlDesigner::TypeName &typeName) { - m_importerRunning = false; - emit importerRunningChanged(); - if (typeName.size()) { - emit bundleMaterialImported(typeName); - updateImportedState(); - } - }); -#else - connect(m_importer, - &ContentLibraryBundleImporter::importFinished, - this, - [&](const QmlDesigner::NodeMetaInfo &metaInfo) { - m_importerRunning = false; - emit importerRunningChanged(); - if (metaInfo.isValid()) { - emit bundleMaterialImported(metaInfo); - updateImportedState(); - } - }); -#endif - - connect(m_importer, &ContentLibraryBundleImporter::unimportFinished, this, - [&](const QmlDesigner::NodeMetaInfo &metaInfo) { - Q_UNUSED(metaInfo) - m_importerRunning = false; - emit importerRunningChanged(); - emit bundleMaterialUnimported(metaInfo); - updateImportedState(); - }); - - resetModel(); - updateIsEmpty(); + return m_bundleId; } void ContentLibraryMaterialsModel::loadMaterialBundle(const QDir &matBundleDir) @@ -273,11 +236,9 @@ void ContentLibraryMaterialsModel::loadMaterialBundle(const QDir &matBundleDir) } } - QString bundleType = QmlDesignerPlugin::instance()->documentManager() - .generatedComponentUtils().materialsBundleType(); - - // Substitute correct id to avoid issues with old bundles - m_matBundleObj["id"] = bundleType.split('.').last(); + auto compUtils = QmlDesignerPlugin::instance()->documentManager().generatedComponentUtils(); + QString bundleType = compUtils.materialsBundleType(); + m_bundleId = compUtils.materialsBundleId(); const QJsonObject catsObj = m_matBundleObj.value("categories").toObject(); const QStringList categories = catsObj.keys(); @@ -307,22 +268,22 @@ void ContentLibraryMaterialsModel::loadMaterialBundle(const QDir &matBundleDir) m_bundleCategories.append(category); } - m_importerSharedFiles.clear(); + m_bundleSharedFiles.clear(); const QJsonArray sharedFilesArr = m_matBundleObj.value("sharedFiles").toArray(); for (const QJsonValueConstRef &file : sharedFilesArr) - m_importerSharedFiles.append(file.toString()); + m_bundleSharedFiles.append(file.toString()); QStringList missingSharedFiles; - for (const QString &s : std::as_const(m_importerSharedFiles)) { + for (const QString &s : std::as_const(m_bundleSharedFiles)) { if (!QFileInfo::exists(matBundleDir.filePath(s))) missingSharedFiles.push_back(s); } if (missingSharedFiles.length() > 0) downloadSharedFiles(matBundleDir, missingSharedFiles); - else - createImporter(); + resetModel(); + updateIsEmpty(); m_matBundleExists = true; emit matBundleExistsChanged(); } @@ -337,11 +298,6 @@ bool ContentLibraryMaterialsModel::matBundleExists() const return m_matBundleExists; } -ContentLibraryBundleImporter *ContentLibraryMaterialsModel::bundleImporter() const -{ - return m_importer; -} - void ContentLibraryMaterialsModel::setSearchText(const QString &searchText) { QString lowerSearchText = searchText.toLower(); @@ -361,20 +317,8 @@ void ContentLibraryMaterialsModel::setSearchText(const QString &searchText) updateIsEmpty(); } -void ContentLibraryMaterialsModel::updateImportedState() +void ContentLibraryMaterialsModel::updateImportedState(const QStringList &importedItems) { - if (!m_importer) - return; - - QString bundleId = m_matBundleObj.value("id").toString(); - Utils::FilePath bundlePath = m_importer->resolveBundleImportPath(bundleId); - - QStringList importedItems; - if (bundlePath.exists()) { - importedItems = transform(bundlePath.dirEntries({{"*.qml"}, QDir::Files}), - [](const Utils::FilePath &f) { return f.baseName(); }); - } - bool changed = false; for (ContentLibraryMaterialsCategory *cat : std::as_const(m_bundleCategories)) changed |= cat->updateImportedState(importedItems); @@ -413,29 +357,23 @@ void ContentLibraryMaterialsModel::applyToSelected(ContentLibraryMaterial *mat, void ContentLibraryMaterialsModel::addToProject(ContentLibraryMaterial *mat) { - QString err = m_importer->importComponent(mat->dirPath(), mat->type(), - mat->qml(), mat->files() + m_importerSharedFiles); + QString err = m_widget->importer()->importComponent(mat->dirPath(), mat->type(), mat->qml(), + mat->files() + m_bundleSharedFiles); - if (err.isEmpty()) { - m_importerRunning = true; - emit importerRunningChanged(); - } else { + if (err.isEmpty()) + m_widget->setImporterRunning(true); + else qWarning() << __FUNCTION__ << err; - } } void ContentLibraryMaterialsModel::removeFromProject(ContentLibraryMaterial *mat) { - emit bundleMaterialAboutToUnimport(mat->type()); + QString err = m_widget->importer()->unimportComponent(mat->type(), mat->qml()); - QString err = m_importer->unimportComponent(mat->type(), mat->qml()); - - if (err.isEmpty()) { - m_importerRunning = true; - emit importerRunningChanged(); - } else { + if (err.isEmpty()) + m_widget->setImporterRunning(true); + else qWarning() << __FUNCTION__ << err; - } } bool ContentLibraryMaterialsModel::hasModelSelection() const diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.h index 93a353b6f04..7c7e8809940 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.h +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.h @@ -3,15 +3,13 @@ #pragma once -#include "nodemetainfo.h" - #include -#include #include +QT_FORWARD_DECLARE_CLASS(QDir) + namespace QmlDesigner { -class ContentLibraryBundleImporter; class ContentLibraryMaterial; class ContentLibraryMaterialsCategory; class ContentLibraryWidget; @@ -24,7 +22,6 @@ class ContentLibraryMaterialsModel : public QAbstractListModel Q_PROPERTY(bool isEmpty MEMBER m_isEmpty NOTIFY isEmptyChanged) Q_PROPERTY(bool hasRequiredQuick3DImport READ hasRequiredQuick3DImport NOTIFY hasRequiredQuick3DImportChanged) Q_PROPERTY(bool hasModelSelection READ hasModelSelection NOTIFY hasModelSelectionChanged) - Q_PROPERTY(bool importerRunning MEMBER m_importerRunning NOTIFY importerRunningChanged) public: ContentLibraryMaterialsModel(ContentLibraryWidget *parent = nullptr); @@ -35,7 +32,7 @@ public: QHash roleNames() const override; void setSearchText(const QString &searchText); - void updateImportedState(); + void updateImportedState(const QStringList &importedItems); void setQuick3DImportVersion(int major, int minor); @@ -50,26 +47,18 @@ public: void updateIsEmpty(); void loadBundle(); - ContentLibraryBundleImporter *bundleImporter() const; - Q_INVOKABLE void applyToSelected(QmlDesigner::ContentLibraryMaterial *mat, bool add = false); Q_INVOKABLE void addToProject(QmlDesigner::ContentLibraryMaterial *mat); Q_INVOKABLE void removeFromProject(QmlDesigner::ContentLibraryMaterial *mat); + QString bundleId() const; + signals: void isEmptyChanged(); void hasRequiredQuick3DImportChanged(); void hasModelSelectionChanged(); void materialVisibleChanged(); void applyToSelectedTriggered(QmlDesigner::ContentLibraryMaterial *mat, bool add = false); -#ifdef QDS_USE_PROJECTSTORAGE - void bundleMaterialImported(const QmlDesigner::TypeName &typeName); -#else - void bundleMaterialImported(const QmlDesigner::NodeMetaInfo &metaInfo); -#endif - void bundleMaterialAboutToUnimport(const QmlDesigner::TypeName &type); - void bundleMaterialUnimported(const QmlDesigner::NodeMetaInfo &metaInfo); - void importerRunningChanged(); void matBundleExistsChanged(); private: @@ -78,26 +67,23 @@ private: bool fetchBundleMetadata(const QDir &bundleDir); bool isValidIndex(int idx) const; void downloadSharedFiles(const QDir &targetDir, const QStringList &files); - void createImporter(); ContentLibraryWidget *m_widget = nullptr; QString m_searchText; + QString m_bundleId; + QStringList m_bundleSharedFiles; QList m_bundleCategories; QJsonObject m_matBundleObj; - ContentLibraryBundleImporter *m_importer = nullptr; bool m_isEmpty = true; bool m_matBundleExists = false; bool m_hasModelSelection = false; - bool m_importerRunning = false; int m_quick3dMajorVersion = -1; int m_quick3dMinorVersion = -1; QString m_downloadPath; QString m_baseUrl; - - QStringList m_importerSharedFiles; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp index fcd1096ac11..db4817ebb67 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp @@ -14,15 +14,11 @@ #include #include -#include #include -#include #include #include #include -#include -#include #include namespace QmlDesigner { @@ -223,48 +219,6 @@ QHash ContentLibraryUserModel::roleNames() const return roles; } -void ContentLibraryUserModel::createImporter() -{ - m_importer = new ContentLibraryBundleImporter(); -#ifdef QDS_USE_PROJECTSTORAGE - connect(m_importer, - &ContentLibraryBundleImporter::importFinished, - this, - [&](const QmlDesigner::TypeName &typeName) { - m_importerRunning = false; - emit importerRunningChanged(); - if (typeName.size()) { - emit bundleMaterialImported(typeName); - updateImportedState(); - } - }); -#else - connect(m_importer, - &ContentLibraryBundleImporter::importFinished, - this, - [&](const QmlDesigner::NodeMetaInfo &metaInfo) { - m_importerRunning = false; - emit importerRunningChanged(); - if (metaInfo.isValid()) { - emit bundleMaterialImported(metaInfo); - updateImportedState(); - } - }); -#endif - - connect(m_importer, &ContentLibraryBundleImporter::unimportFinished, this, - [&](const QmlDesigner::NodeMetaInfo &metaInfo) { - Q_UNUSED(metaInfo) - m_importerRunning = false; - emit importerRunningChanged(); - emit bundleMaterialUnimported(metaInfo); - updateImportedState(); - }); - - resetModel(); - updateIsEmpty(); -} - QJsonObject &ContentLibraryUserModel::bundleJsonObjectRef() { return m_bundleObj; @@ -304,10 +258,8 @@ void ContentLibraryUserModel::loadMaterialBundle() } } - m_bundleIdMaterial = "UserMaterials"; - - // Substitute correct id to avoid issues with old bundles - m_bundleObj["id"] = m_bundleIdMaterial; + auto compUtils = QmlDesignerPlugin::instance()->documentManager().generatedComponentUtils(); + m_bundleObj["id"] = compUtils.userMaterialsBundleId(); // parse materials const QJsonObject matsObj = m_bundleObj.value("materials").toObject(); @@ -331,12 +283,10 @@ void ContentLibraryUserModel::loadMaterialBundle() m_userMaterials.append(userMat); } - m_importerSharedFiles.clear(); + m_bundleSharedFiles.clear(); const QJsonArray sharedFilesArr = m_bundleObj.value("sharedFiles").toArray(); for (const QJsonValueConstRef &file : sharedFilesArr) - m_importerSharedFiles.append(file.toString()); - - createImporter(); + m_bundleSharedFiles.append(file.toString()); m_matBundleExists = true; emit matBundleExistsChanged(); @@ -378,11 +328,6 @@ bool ContentLibraryUserModel::matBundleExists() const return m_matBundleExists; } -ContentLibraryBundleImporter *ContentLibraryUserModel::bundleImporter() const -{ - return m_importer; -} - void ContentLibraryUserModel::setSearchText(const QString &searchText) { QString lowerSearchText = searchText.toLower(); @@ -398,20 +343,8 @@ void ContentLibraryUserModel::setSearchText(const QString &searchText) updateIsEmpty(); } -void ContentLibraryUserModel::updateImportedState() +void ContentLibraryUserModel::updateImportedState(const QStringList &importedItems) { - if (!m_importer) - return; - - QString bundleId = m_bundleObj.value("id").toString(); - Utils::FilePath bundlePath = m_importer->resolveBundleImportPath(bundleId); - - QStringList importedItems; - if (bundlePath.exists()) { - importedItems = transform(bundlePath.dirEntries({{"*.qml"}, QDir::Files}), - [](const Utils::FilePath &f) { return f.baseName(); }); - } - bool changed = false; for (ContentLibraryMaterial *mat : std::as_const(m_userMaterials)) changed |= mat->setImported(importedItems.contains(mat->qml().chopped(4))); @@ -452,29 +385,23 @@ void ContentLibraryUserModel::applyToSelected(ContentLibraryMaterial *mat, bool void ContentLibraryUserModel::addToProject(ContentLibraryMaterial *mat) { - QString err = m_importer->importComponent(mat->dirPath(), mat->type(), mat->qml(), - mat->files() + m_importerSharedFiles); + QString err = m_widget->importer()->importComponent(mat->dirPath(), mat->type(), mat->qml(), + mat->files() + m_bundleSharedFiles); - if (err.isEmpty()) { - m_importerRunning = true; - emit importerRunningChanged(); - } else { + if (err.isEmpty()) + m_widget->setImporterRunning(true); + else qWarning() << __FUNCTION__ << err; - } } void ContentLibraryUserModel::removeFromProject(ContentLibraryMaterial *mat) { - emit bundleMaterialAboutToUnimport(mat->type()); + QString err = m_widget->importer()->unimportComponent(mat->type(), mat->qml()); - QString err = m_importer->unimportComponent(mat->type(), mat->qml()); - - if (err.isEmpty()) { - m_importerRunning = true; - emit importerRunningChanged(); - } else { + if (err.isEmpty()) + m_widget->setImporterRunning(true); + else qWarning() << __FUNCTION__ << err; - } } bool ContentLibraryUserModel::hasModelSelection() const diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.h index e4bf030c57c..b11cd343c88 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.h +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.h @@ -12,7 +12,6 @@ QT_FORWARD_DECLARE_CLASS(QUrl) namespace QmlDesigner { -class ContentLibraryBundleImporter; class ContentLibraryEffect; class ContentLibraryMaterial; class ContentLibraryTexture; @@ -27,7 +26,6 @@ class ContentLibraryUserModel : public QAbstractListModel Q_PROPERTY(bool isEmpty MEMBER m_isEmpty NOTIFY isEmptyChanged) Q_PROPERTY(bool hasRequiredQuick3DImport READ hasRequiredQuick3DImport NOTIFY hasRequiredQuick3DImportChanged) Q_PROPERTY(bool hasModelSelection READ hasModelSelection NOTIFY hasModelSelectionChanged) - Q_PROPERTY(bool importerRunning MEMBER m_importerRunning NOTIFY importerRunningChanged) Q_PROPERTY(QList userMaterials MEMBER m_userMaterials NOTIFY userMaterialsChanged) Q_PROPERTY(QList userTextures MEMBER m_userTextures NOTIFY userTexturesChanged) Q_PROPERTY(QList user3DItems MEMBER m_user3DItems NOTIFY user3DItemsChanged) @@ -41,7 +39,7 @@ public: QHash roleNames() const override; void setSearchText(const QString &searchText); - void updateImportedState(); + void updateImportedState(const QStringList &importedItems); QPair getUniqueLibMaterialNameAndQml(const QString &matName) const; TypeName qmlToModule(const QString &qmlName) const; @@ -67,8 +65,6 @@ public: void loadMaterialBundle(); void loadTextureBundle(); - ContentLibraryBundleImporter *bundleImporter() const; - Q_INVOKABLE void applyToSelected(QmlDesigner::ContentLibraryMaterial *mat, bool add = false); Q_INVOKABLE void addToProject(QmlDesigner::ContentLibraryMaterial *mat); Q_INVOKABLE void removeFromProject(QmlDesigner::ContentLibraryMaterial *mat); @@ -86,23 +82,16 @@ signals: void applyToSelectedTriggered(QmlDesigner::ContentLibraryMaterial *mat, bool add = false); -#ifdef QDS_USE_PROJECTSTORAGE - void bundleMaterialImported(const QmlDesigner::TypeName &typeName); -#else - void bundleMaterialImported(const QmlDesigner::NodeMetaInfo &metaInfo); -#endif - void bundleMaterialAboutToUnimport(const QmlDesigner::TypeName &type); - void bundleMaterialUnimported(const QmlDesigner::NodeMetaInfo &metaInfo); - void importerRunningChanged(); + void matBundleExistsChanged(); private: bool isValidIndex(int idx) const; - void createImporter(); ContentLibraryWidget *m_widget = nullptr; QString m_searchText; QString m_bundleIdMaterial; + QStringList m_bundleSharedFiles; QList m_userMaterials; QList m_userTextures; @@ -111,19 +100,14 @@ private: QStringList m_userCategories; QJsonObject m_bundleObj; - ContentLibraryBundleImporter *m_importer = nullptr; bool m_isEmpty = true; bool m_matBundleExists = false; bool m_hasModelSelection = false; - bool m_importerRunning = false; int m_quick3dMajorVersion = -1; int m_quick3dMinorVersion = -1; - QString m_importerBundlePath; - QString m_importerBundleId; - QStringList m_importerSharedFiles; enum Roles { NameRole = Qt::UserRole + 1, VisibleRole, ItemsRole }; }; diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp index 52c28f41b64..49b1a7c6a02 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp @@ -3,8 +3,6 @@ #include "contentlibraryview.h" -#include "asset.h" -#include "bindingproperty.h" #include "contentlibrarybundleimporter.h" #include "contentlibraryeffect.h" #include "contentlibraryeffectsmodel.h" @@ -14,19 +12,20 @@ #include "contentlibrarytexturesmodel.h" #include "contentlibraryusermodel.h" #include "contentlibrarywidget.h" -#include "documentmanager.h" -#include "externaldependenciesinterface.h" -#include "nodelistproperty.h" -#include "qmldesignerconstants.h" -#include "qmlobjectnode.h" -#include "variantproperty.h" -#include "utils3d.h" +#include +#include #include -#include - -#include +#include #include +#include +#include +#include +#include +#include +#include +#include + #include #ifndef QMLDESIGNER_TEST @@ -90,65 +89,61 @@ WidgetInfo ContentLibraryView::widgetInfo() m_widget->environmentsModel()->setHasSceneEnv(sceneEnvExists); }); - ContentLibraryMaterialsModel *materialsModel = m_widget->materialsModel().data(); - - connect(materialsModel, + connect(m_widget->materialsModel(), &ContentLibraryMaterialsModel::applyToSelectedTriggered, this, [&](ContentLibraryMaterial *bundleMat, bool add) { - if (m_selectedModels.isEmpty()) - return; + if (m_selectedModels.isEmpty()) + return; - m_bundleMaterialTargets = m_selectedModels; - m_bundleMaterialAddToSelected = add; + m_bundleMaterialTargets = m_selectedModels; + m_bundleMaterialAddToSelected = add; - ModelNode defaultMat = getBundleMaterialDefaultInstance(bundleMat->type()); - if (defaultMat.isValid()) - applyBundleMaterialToDropTarget(defaultMat); - else - m_widget->materialsModel()->addToProject(bundleMat); - }); - -#ifdef QDS_USE_PROJECTSTORAGE - connect(materialsModel, - &ContentLibraryMaterialsModel::bundleMaterialImported, - this, - [&](const QmlDesigner::TypeName &typeName) { - applyBundleMaterialToDropTarget({}, typeName); - }); -#else - connect(materialsModel, - &ContentLibraryMaterialsModel::bundleMaterialImported, - this, - [&](const QmlDesigner::NodeMetaInfo &metaInfo) { - applyBundleMaterialToDropTarget({}, metaInfo); - }); -#endif - - connect(materialsModel, &ContentLibraryMaterialsModel::bundleMaterialAboutToUnimport, this, - [&] (const QmlDesigner::TypeName &type) { - // delete instances of the bundle material that is about to be unimported - executeInTransaction("ContentLibraryView::widgetInfo", [&] { - ModelNode matLib = Utils3D::materialLibraryNode(this); - if (!matLib.isValid()) - return; - - Utils::reverseForeach(matLib.directSubModelNodes(), [&](const ModelNode &mat) { - if (mat.isValid() && mat.type() == type) - QmlObjectNode(mat).destroy(); - }); - }); + ModelNode defaultMat = getBundleMaterialDefaultInstance(bundleMat->type()); + if (defaultMat.isValid()) + applyBundleMaterialToDropTarget(defaultMat); + else + m_widget->materialsModel()->addToProject(bundleMat); }); - ContentLibraryEffectsModel *effectsModel = m_widget->effectsModel().data(); - -#ifdef QDS_USE_PROJECTSTORAGE - connect(effectsModel, - &ContentLibraryEffectsModel::bundleItemImported, + connect(m_widget->userModel(), + &ContentLibraryUserModel::applyToSelectedTriggered, this, - [&](const QmlDesigner::TypeName &typeName) { - QTC_ASSERT(typeName.size(), return); + [&](ContentLibraryMaterial *bundleMat, bool add) { + if (m_selectedModels.isEmpty()) + return; + m_bundleMaterialTargets = m_selectedModels; + m_bundleMaterialAddToSelected = add; + + ModelNode defaultMat = getBundleMaterialDefaultInstance(bundleMat->type()); + if (defaultMat.isValid()) + applyBundleMaterialToDropTarget(defaultMat); + else + m_widget->userModel()->addToProject(bundleMat); + }); + + connectImporter(); + } + + return createWidgetInfo(m_widget.data(), + "ContentLibrary", + WidgetInfo::LeftPane, + 0, + tr("Content Library")); +} + +void ContentLibraryView::connectImporter() +{ +#ifdef QDS_USE_PROJECTSTORAGE + connect(m_widget->importer(), + &ContentLibraryBundleImporter::importFinished, + this, + [&](const QmlDesigner::TypeName &typeName, const QString &bundleId) { + QTC_ASSERT(typeName.size(), return); + if (isMaterialBundle(bundleId)) { + applyBundleMaterialToDropTarget({}, typeName); + } else if (isEffectBundle(bundleId)) { if (!m_bundleEffectTarget) m_bundleEffectTarget = Utils3D::active3DSceneNode(this); @@ -165,20 +160,23 @@ WidgetInfo ContentLibraryView::widgetInfo() m_bundleEffectTarget = {}; m_bundleEffectPos = {}; - }); + } + }); #else - connect(effectsModel, - &ContentLibraryEffectsModel::bundleItemImported, - this, - [&](const QmlDesigner::NodeMetaInfo &metaInfo) { - QTC_ASSERT(metaInfo.isValid(), return); - + connect(m_widget->importer(), + &ContentLibraryBundleImporter::importFinished, + this, + [&](const QmlDesigner::NodeMetaInfo &metaInfo, const QString &bundleId) { + QTC_ASSERT(metaInfo.isValid(), return); + if (isMaterialBundle(bundleId)) { + applyBundleMaterialToDropTarget({}, metaInfo); + } else if (isEffectBundle(bundleId)) { if (!m_bundleEffectTarget) m_bundleEffectTarget = Utils3D::active3DSceneNode(this); QTC_ASSERT(m_bundleEffectTarget, return); - executeInTransaction("ContentLibraryView::widgetInfo", [&] { + executeInTransaction("ContentLibraryView::connectImporter", [&] { QVector3D pos = m_bundleEffectPos.value(); ModelNode newEffNode = createModelNode(metaInfo.typeName(), metaInfo.majorVersion(), @@ -193,80 +191,46 @@ WidgetInfo ContentLibraryView::widgetInfo() m_bundleEffectTarget = {}; m_bundleEffectPos = {}; - }); + } + }); #endif - connect(effectsModel, &ContentLibraryEffectsModel::bundleItemAboutToUnimport, this, - [&] (const QmlDesigner::TypeName &type) { - // delete instances of the bundle effect that is about to be unimported - executeInTransaction("ContentLibraryView::widgetInfo", [&] { - NodeMetaInfo metaInfo = model()->metaInfo(type); - QList effects = allModelNodesOfType(metaInfo); - for (ModelNode &eff : effects) - eff.destroy(); - }); - }); - connectUserBundle(); - } - - return createWidgetInfo(m_widget.data(), - "ContentLibrary", - WidgetInfo::LeftPane, - 0, - tr("Content Library")); -} - -void ContentLibraryView::connectUserBundle() -{ - ContentLibraryUserModel *userModel = m_widget->userModel().data(); - - connect(userModel, - &ContentLibraryUserModel::applyToSelectedTriggered, - this, - [&](ContentLibraryMaterial *bundleMat, bool add) { - if (m_selectedModels.isEmpty()) + connect(m_widget->importer(), &ContentLibraryBundleImporter::aboutToUnimport, this, + [&] (const QmlDesigner::TypeName &type, const QString &bundleId) { + if (isMaterialBundle(bundleId)) { + // delete instances of the bundle material that is about to be unimported + executeInTransaction("ContentLibraryView::connectImporter", [&] { + ModelNode matLib = Utils3D::materialLibraryNode(this); + if (!matLib.isValid()) return; - m_bundleMaterialTargets = m_selectedModels; - m_bundleMaterialAddToSelected = add; - - ModelNode defaultMat = getBundleMaterialDefaultInstance(bundleMat->type()); - if (defaultMat.isValid()) - applyBundleMaterialToDropTarget(defaultMat); - else - m_widget->userModel()->addToProject(bundleMat); - }); - -#ifdef QDS_USE_PROJECTSTORAGE - connect(userModel, - &ContentLibraryUserModel::bundleMaterialImported, - this, - [&](const QmlDesigner::TypeName &typeName) { - applyBundleMaterialToDropTarget({}, typeName); - }); -#else - connect(userModel, - &ContentLibraryUserModel::bundleMaterialImported, - this, - [&](const QmlDesigner::NodeMetaInfo &metaInfo) { - applyBundleMaterialToDropTarget({}, metaInfo); - }); -#endif - - connect(userModel, &ContentLibraryUserModel::bundleMaterialAboutToUnimport, this, - [&] (const QmlDesigner::TypeName &type) { - // delete instances of the bundle material that is about to be unimported - executeInTransaction("ContentLibraryView::connectUserModel", [&] { - ModelNode matLib = Utils3D::materialLibraryNode(this); - if (!matLib.isValid()) - return; - - Utils::reverseForeach(matLib.directSubModelNodes(), [&](const ModelNode &mat) { - if (mat.isValid() && mat.type() == type) - QmlObjectNode(mat).destroy(); - }); + Utils::reverseForeach(matLib.directSubModelNodes(), [&](const ModelNode &mat) { + if (mat.isValid() && mat.type() == type) + QmlObjectNode(mat).destroy(); }); }); + } else if (isEffectBundle(bundleId)) { + // delete instances of the bundle effect that is about to be unimported + executeInTransaction("ContentLibraryView::connectImporter", [&] { + NodeMetaInfo metaInfo = model()->metaInfo(type); + QList effects = allModelNodesOfType(metaInfo); + for (ModelNode &eff : effects) + eff.destroy(); + }); + } + }); +} + +bool ContentLibraryView::isMaterialBundle(const QString &bundleId) const +{ + auto compUtils = QmlDesignerPlugin::instance()->documentManager().generatedComponentUtils(); + return bundleId == compUtils.materialsBundleId() || bundleId == compUtils.userMaterialsBundleId(); +} + +bool ContentLibraryView::isEffectBundle(const QString &bundleId) const +{ + auto compUtils = QmlDesignerPlugin::instance()->documentManager().generatedComponentUtils(); + return bundleId == compUtils.effectsBundleId() || bundleId == compUtils.userEffectsBundleId(); } void ContentLibraryView::modelAttached(Model *model) @@ -294,9 +258,10 @@ void ContentLibraryView::modelAttached(Model *model) m_widget->userModel()->loadMaterialBundle(); m_widget->userModel()->loadTextureBundle(); - m_widget->materialsModel()->updateImportedState(); - m_widget->effectsModel()->updateImportedState(); - m_widget->userModel()->updateImportedState(); + auto compUtils = QmlDesignerPlugin::instance()->documentManager().generatedComponentUtils(); + m_widget->updateImportedState(compUtils.materialsBundleId()); + m_widget->updateImportedState(compUtils.effectsBundleId()); + m_widget->updateImportedState(compUtils.userMaterialsBundleId()); } void ContentLibraryView::modelAboutToBeDetached(Model *model) diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.h index 4ff2a5db8fd..3c80193478f 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.h +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.h @@ -3,9 +3,9 @@ #pragma once -#include "abstractview.h" -#include "createtexture.h" -#include "nodemetainfo.h" +#include +#include +#include #include #include @@ -48,7 +48,9 @@ public: const QVariant &data) override; private: - void connectUserBundle(); + void connectImporter(); + bool isMaterialBundle(const QString &bundleId) const; + bool isEffectBundle(const QString &bundleId) const; void active3DSceneChanged(qint32 sceneId); void updateBundlesQuick3DVersion(); void addLibMaterial(const ModelNode &mat, const QPixmap &icon); diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.cpp index 9375d43fd4f..0c680afa76f 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.cpp @@ -3,6 +3,7 @@ #include "contentlibrarywidget.h" +#include "contentlibrarybundleimporter.h" #include "contentlibraryeffect.h" #include "contentlibraryeffectsmodel.h" #include "contentlibrarymaterial.h" @@ -18,6 +19,7 @@ #include #include +#include #include #include @@ -40,7 +42,6 @@ #include #include #include -#include #include namespace QmlDesigner { @@ -177,6 +178,65 @@ ContentLibraryWidget::ContentLibraryWidget() {"userModel", QVariant::fromValue(m_userModel.data())}}); reloadQmlSource(); + createImporter(); +} + +void ContentLibraryWidget::createImporter() +{ + m_importer = new ContentLibraryBundleImporter(); +#ifdef QDS_USE_PROJECTSTORAGE + connect(m_importer, + &ContentLibraryBundleImporter::importFinished, + this, + [&](const QmlDesigner::TypeName &typeName, const QString &bundleId) { + setImporterRunning(false); + if (typeName.size()) + updateImportedState(bundleId); + }); +#else + connect(m_importer, + &ContentLibraryBundleImporter::importFinished, + this, + [&](const QmlDesigner::NodeMetaInfo &metaInfo, const QString &bundleId) { + setImporterRunning(false); + if (metaInfo.isValid()) + updateImportedState(bundleId); + }); +#endif + + connect(m_importer, &ContentLibraryBundleImporter::unimportFinished, this, + [&](const QmlDesigner::NodeMetaInfo &metaInfo, const QString &bundleId) { + Q_UNUSED(metaInfo) + setImporterRunning(false); + updateImportedState(bundleId); + }); +} + +void ContentLibraryWidget::updateImportedState(const QString &bundleId) +{ + if (!m_importer) + return; + + Utils::FilePath bundlePath = m_importer->resolveBundleImportPath(bundleId); + + QStringList importedItems; + if (bundlePath.exists()) { + importedItems = transform(bundlePath.dirEntries({{"*.qml"}, QDir::Files}), + [](const Utils::FilePath &f) { return f.baseName(); }); + } + + auto compUtils = QmlDesignerPlugin::instance()->documentManager().generatedComponentUtils(); + if (bundleId == compUtils.materialsBundleId()) + m_materialsModel->updateImportedState(importedItems); + else if (bundleId == compUtils.effectsBundleId()) + m_effectsModel->updateImportedState(importedItems); + else if (bundleId == compUtils.userMaterialsBundleId()) + m_userModel->updateImportedState(importedItems); +} + +ContentLibraryBundleImporter *ContentLibraryWidget::importer() const +{ + return m_importer; } QVariantMap ContentLibraryWidget::readTextureBundleJson() @@ -683,6 +743,20 @@ void ContentLibraryWidget::setIsQt6Project(bool b) emit isQt6ProjectChanged(); } +bool ContentLibraryWidget::importerRunning() const +{ + return m_importerRunning; +} + +void ContentLibraryWidget::setImporterRunning(bool b) +{ + if (m_importerRunning == b) + return; + + m_importerRunning = b; + emit importerRunningChanged(); +} + void ContentLibraryWidget::reloadQmlSource() { const QString materialBrowserQmlPath = qmlSourcesPath() + "/ContentLibrary.qml"; diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.h index c4d51d0362b..75339f02491 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.h +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.h @@ -4,7 +4,7 @@ #pragma once #include "createtexture.h" - +#include #include #include @@ -18,6 +18,7 @@ class StudioQuickWidget; namespace QmlDesigner { +class ContentLibraryBundleImporter; class ContentLibraryEffect; class ContentLibraryEffectsModel; class ContentLibraryMaterial; @@ -25,6 +26,7 @@ class ContentLibraryMaterialsModel; class ContentLibraryTexture; class ContentLibraryTexturesModel; class ContentLibraryUserModel; +class NodeMetaInfo; class ContentLibraryWidget : public QFrame { @@ -34,6 +36,7 @@ class ContentLibraryWidget : public QFrame Q_PROPERTY(bool hasMaterialLibrary READ hasMaterialLibrary NOTIFY hasMaterialLibraryChanged) Q_PROPERTY(bool hasActive3DScene READ hasActive3DScene WRITE setHasActive3DScene NOTIFY hasActive3DSceneChanged) Q_PROPERTY(bool isQt6Project READ isQt6Project NOTIFY isQt6ProjectChanged) + Q_PROPERTY(bool importerRunning READ importerRunning WRITE setImporterRunning NOTIFY importerRunningChanged) // Needed for a workaround for a bug where after drag-n-dropping an item, the ScrollView scrolls to a random position Q_PROPERTY(bool isDragging MEMBER m_isDragging NOTIFY isDraggingChanged) @@ -58,9 +61,13 @@ public: bool isQt6Project() const; void setIsQt6Project(bool b); + bool importerRunning() const; + void setImporterRunning(bool b); + Q_INVOKABLE void handleSearchFilterChanged(const QString &filterText); void setMaterialsModel(QPointer newMaterialsModel); + void updateImportedState(const QString &bundleId); QPointer materialsModel() const; QPointer texturesModel() const; @@ -80,6 +87,8 @@ public: QSize sizeHint() const override; + ContentLibraryBundleImporter *importer() const; + signals: void bundleEffectDragStarted(QmlDesigner::ContentLibraryEffect *bundleEff); void bundleMaterialDragStarted(QmlDesigner::ContentLibraryMaterial *bundleMat); @@ -91,6 +100,7 @@ signals: void hasActive3DSceneChanged(); void isDraggingChanged(); void isQt6ProjectChanged(); + void importerRunningChanged(); protected: bool eventFilter(QObject *obj, QEvent *event) override; @@ -110,6 +120,7 @@ private: const QString &existingMetaFile, const QString downloadedMetaFile); QStringList saveNewTextures(const QDir &bundleDir, const QStringList &newFiles); void populateTextureBundleModels(); + void createImporter(); QScopedPointer m_quickWidget; QPointer m_materialsModel; @@ -118,6 +129,8 @@ private: QPointer m_effectsModel; QPointer m_userModel; + ContentLibraryBundleImporter *m_importer = nullptr; + QShortcut *m_qmlSourceUpdateShortcut = nullptr; QString m_filterText; @@ -132,6 +145,7 @@ private: bool m_hasQuick3DImport = false; bool m_isDragging = false; bool m_isQt6Project = false; + bool m_importerRunning = false; QString m_textureBundleUrl; QString m_bundlePath; }; diff --git a/src/plugins/qmldesigner/designercore/generatedcomponentutils.cpp b/src/plugins/qmldesigner/designercore/generatedcomponentutils.cpp index a2faa68d9e6..a1fe4d47632 100644 --- a/src/plugins/qmldesigner/designercore/generatedcomponentutils.cpp +++ b/src/plugins/qmldesigner/designercore/generatedcomponentutils.cpp @@ -186,4 +186,35 @@ QString GeneratedComponentUtils::effectsBundleType() const return basePrefix + '.' + QLatin1String(Constants::COMPONENT_BUNDLES_EFFECT_BUNDLE_TYPE); } +QString GeneratedComponentUtils::materialsBundleId() const +{ + bool isNewImportDir = generatedComponentTypePrefix().endsWith(Constants::GENERATED_COMPONENTS_FOLDER); + + return QLatin1String(isNewImportDir ? Constants::COMPONENT_BUNDLES_MATERIAL_BUNDLE_TYPE + : Constants::OLD_COMPONENT_BUNDLES_MATERIAL_BUNDLE_TYPE); +} + +QString GeneratedComponentUtils::effectsBundleId() const +{ + bool isNewImportDir = generatedComponentTypePrefix().endsWith(Constants::GENERATED_COMPONENTS_FOLDER); + + return QLatin1String(isNewImportDir ? Constants::COMPONENT_BUNDLES_EFFECT_BUNDLE_TYPE + : Constants::OLD_COMPONENT_BUNDLES_EFFECT_BUNDLE_TYPE); +} + +QString GeneratedComponentUtils::userMaterialsBundleId() const +{ + return QLatin1String(Constants::COMPONENT_BUNDLES_USER_MATERIAL_BUNDLE_TYPE); +} + +QString GeneratedComponentUtils::userEffectsBundleId() const +{ + return QLatin1String(Constants::COMPONENT_BUNDLES_USER_EFFECT_BUNDLE_TYPE); +} + +QString GeneratedComponentUtils::user3DBundleId() const +{ + return QLatin1String(Constants::COMPONENT_BUNDLES_USER_3D_BUNDLE_TYPE); +} + } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/generatedcomponentutils.h b/src/plugins/qmldesigner/designercore/generatedcomponentutils.h index d9c04352a9e..ea4149b551c 100644 --- a/src/plugins/qmldesigner/designercore/generatedcomponentutils.h +++ b/src/plugins/qmldesigner/designercore/generatedcomponentutils.h @@ -35,6 +35,12 @@ public: QString materialsBundleType() const; QString effectsBundleType() const; + QString materialsBundleId() const; + QString effectsBundleId() const; + QString userMaterialsBundleId() const; + QString userEffectsBundleId() const; + QString user3DBundleId() const; + private: ExternalDependenciesInterface &m_externalDependencies; }; diff --git a/src/plugins/qmldesigner/qmldesignerconstants.h b/src/plugins/qmldesigner/qmldesignerconstants.h index 286a1633c7d..ff85f6d25a9 100644 --- a/src/plugins/qmldesigner/qmldesignerconstants.h +++ b/src/plugins/qmldesigner/qmldesignerconstants.h @@ -81,6 +81,9 @@ inline constexpr char QML_DESIGNER_SUBFOLDER[] = "/designer/"; inline constexpr char COMPONENT_BUNDLES_TYPE[] = "Bundles"; inline constexpr char COMPONENT_BUNDLES_MATERIAL_BUNDLE_TYPE[] = "Materials"; inline constexpr char COMPONENT_BUNDLES_EFFECT_BUNDLE_TYPE[] = "Effects"; +inline constexpr char COMPONENT_BUNDLES_USER_MATERIAL_BUNDLE_TYPE[] = "UserMaterials"; +inline constexpr char COMPONENT_BUNDLES_USER_EFFECT_BUNDLE_TYPE[] = "UserEffects"; +inline constexpr char COMPONENT_BUNDLES_USER_3D_BUNDLE_TYPE[] = "User3D"; inline constexpr char GENERATED_COMPONENTS_FOLDER[] = "Generated"; inline constexpr char COMPONENT_BUNDLES_ASSET_REF_FILE[] = "_asset_ref.json"; inline constexpr char OLD_QUICK_3D_ASSETS_FOLDER[] = "Quick3DAssets"; From 11f7ece0d80b47282f3c7138b4a9ed25647140a4 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Thu, 2 May 2024 12:12:00 +0200 Subject: [PATCH 048/154] QmlDesigner: Type annotaion reader set defaults So far the defaults were false but some of the are true. Task-number: QDS-12663 Change-Id: I710913ed371d6e094cf68c154bb9353c21ee0c16 Reviewed-by: Thomas Hartmann Reviewed-by: Qt CI Patch Build Bot --- .../projectstorage/typeannotationreader.cpp | 5 +++ .../typeannotationreader-test.cpp | 45 +++++++++++-------- 2 files changed, 31 insertions(+), 19 deletions(-) diff --git a/src/plugins/qmldesigner/designercore/projectstorage/typeannotationreader.cpp b/src/plugins/qmldesigner/designercore/projectstorage/typeannotationreader.cpp index a0ef56616a2..88f43dd33ab 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/typeannotationreader.cpp +++ b/src/plugins/qmldesigner/designercore/projectstorage/typeannotationreader.cpp @@ -180,6 +180,11 @@ TypeAnnotationReader::ParserSate TypeAnnotationReader::readMetaInfoRootElement(c if (name == typeElementName) { auto &annotation = m_typeAnnotations.emplace_back(m_sourceId, m_directorySourceId); annotation.traits.canBeDroppedInFormEditor = FlagIs::True; + annotation.traits.canBeDroppedInNavigator = FlagIs::True; + annotation.traits.isMovable = FlagIs::True; + annotation.traits.isResizable = FlagIs::True; + annotation.traits.hasFormEditorItem = FlagIs::True; + annotation.traits.visibleInLibrary = FlagIs::True; m_itemLibraryEntries = json::array(); return ParsingType; diff --git a/tests/unit/tests/unittests/projectstorage/typeannotationreader-test.cpp b/tests/unit/tests/unittests/projectstorage/typeannotationreader-test.cpp index cb3bd6e05f9..1a64a4980ec 100644 --- a/tests/unit/tests/unittests/projectstorage/typeannotationreader-test.cpp +++ b/tests/unit/tests/unittests/projectstorage/typeannotationreader-test.cpp @@ -17,7 +17,15 @@ using QmlDesigner::FlagIs; class TypeAnnotationReader : public testing::Test { protected: - TypeAnnotationReader() { traits.canBeDroppedInFormEditor = FlagIs::True; } + TypeAnnotationReader() + { + traits.canBeDroppedInFormEditor = FlagIs::True; + traits.canBeDroppedInNavigator = FlagIs::True; + traits.isMovable = FlagIs::True; + traits.isResizable = FlagIs::True; + traits.hasFormEditorItem = FlagIs::True; + traits.visibleInLibrary = FlagIs::True; + } static void SetUpTestSuite() { static_database = std::make_unique(":memory:", Sqlite::JournalMode::Memory); @@ -170,7 +178,7 @@ TEST_F(TypeAnnotationReader, parse_true_doesLayoutChildren) IsEmpty()))); } -TEST_F(TypeAnnotationReader, parse_true_canBeDroppedInFormEditor) +TEST_F(TypeAnnotationReader, parse_false_canBeDroppedInFormEditor) { using QmlDesigner::FlagIs; auto content = QString{R"xy( @@ -199,7 +207,7 @@ TEST_F(TypeAnnotationReader, parse_true_canBeDroppedInFormEditor) IsEmpty()))); } -TEST_F(TypeAnnotationReader, parse_true_canBeDroppedInNavigator) +TEST_F(TypeAnnotationReader, parse_false_canBeDroppedInNavigator) { using QmlDesigner::FlagIs; auto content = QString{R"xy( @@ -209,11 +217,11 @@ TEST_F(TypeAnnotationReader, parse_true_canBeDroppedInNavigator) icon: "images/frame-icon16.png" Hints { - canBeDroppedInNavigator: true + canBeDroppedInNavigator: false } } })xy"}; - traits.canBeDroppedInNavigator = FlagIs::True; + traits.canBeDroppedInNavigator = FlagIs::False; auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId, directorySourceId); @@ -257,7 +265,7 @@ TEST_F(TypeAnnotationReader, parse_true_canBeDroppedInView3D) IsEmpty()))); } -TEST_F(TypeAnnotationReader, parse_true_isMovable) +TEST_F(TypeAnnotationReader, parse_false_isMovable) { using QmlDesigner::FlagIs; auto content = QString{R"xy( @@ -267,11 +275,11 @@ TEST_F(TypeAnnotationReader, parse_true_isMovable) icon: "images/frame-icon16.png" Hints { - isMovable: true + isMovable: false } } })xy"}; - traits.isMovable = FlagIs::True; + traits.isMovable = FlagIs::False; auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId, directorySourceId); @@ -286,7 +294,7 @@ TEST_F(TypeAnnotationReader, parse_true_isMovable) IsEmpty()))); } -TEST_F(TypeAnnotationReader, parse_true_isResizable) +TEST_F(TypeAnnotationReader, parse_false_isResizable) { using QmlDesigner::FlagIs; auto content = QString{R"xy( @@ -296,11 +304,11 @@ TEST_F(TypeAnnotationReader, parse_true_isResizable) icon: "images/frame-icon16.png" Hints { - isResizable: true + isResizable: false } } })xy"}; - traits.isResizable = FlagIs::True; + traits.isResizable = FlagIs::False; auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId, directorySourceId); @@ -315,7 +323,7 @@ TEST_F(TypeAnnotationReader, parse_true_isResizable) IsEmpty()))); } -TEST_F(TypeAnnotationReader, parse_true_hasFormEditorItem) +TEST_F(TypeAnnotationReader, parse_false_hasFormEditorItem) { using QmlDesigner::FlagIs; auto content = QString{R"xy( @@ -325,11 +333,11 @@ TEST_F(TypeAnnotationReader, parse_true_hasFormEditorItem) icon: "images/frame-icon16.png" Hints { - hasFormEditorItem: true + hasFormEditorItem: false } } })xy"}; - traits.hasFormEditorItem = FlagIs::True; + traits.hasFormEditorItem = FlagIs::False; auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId, directorySourceId); @@ -431,7 +439,7 @@ TEST_F(TypeAnnotationReader, parse_true_visibleInNavigator) IsEmpty()))); } -TEST_F(TypeAnnotationReader, parse_true_visibleInLibrary) +TEST_F(TypeAnnotationReader, parse_false_visibleInLibrary) { using QmlDesigner::FlagIs; auto content = QString{R"xy( @@ -441,11 +449,11 @@ TEST_F(TypeAnnotationReader, parse_true_visibleInLibrary) icon: "images/frame-icon16.png" Hints { - visibleInLibrary: true + visibleInLibrary: false } } })xy"}; - traits.visibleInLibrary = FlagIs::True; + traits.visibleInLibrary = FlagIs::False; auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId, directorySourceId); @@ -470,11 +478,10 @@ TEST_F(TypeAnnotationReader, parse_false) icon: "images/frame-icon16.png" Hints { - isMovable: false + isMovable: true } } })xy"}; - traits.canBeDroppedInFormEditor = FlagIs::True; auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId, directorySourceId); From 0100eb2c1a398c979d4e4daa8c8c365a8cab08de Mon Sep 17 00:00:00 2001 From: Pranta Dastider Date: Fri, 26 Apr 2024 11:29:47 +0200 Subject: [PATCH 049/154] QmlDesigner: Update tooltips for QtQuick Studio Design Effects This patch update tooltips for QtQuick Studio Design Effects. Fixes: QDS-12568 Change-Id: I7f500a765ca58a27b7efe3923f637856684215f2 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Reviewed-by: Mats Honkamaa --- .../QtQuick/EffectsSection.qml | 55 +++++++++++++++---- 1 file changed, 44 insertions(+), 11 deletions(-) diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/EffectsSection.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/EffectsSection.qml index fca041fc19e..c7121127d5e 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/EffectsSection.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/EffectsSection.qml @@ -63,7 +63,7 @@ Section { width: StudioTheme.Values.singleControlColumnWidth buttonIcon: root.hasDesignerEffect ? qsTr("Remove Effects") : qsTr("Add Effects") iconFont: StudioTheme.Constants.font - tooltip: qsTr("Adds a note with a title to explain the component.") + tooltip: qsTr("Adds visual effects on the component.") onClicked: { if (root.hasDesignerEffect) { root.effectNodeWrapper.deleteModelNode() @@ -78,7 +78,9 @@ Section { } PropertyLabel { + text: qsTr("Visible") + tooltip: qsTr("Toggles the visibility of visual effects on the component.") visible: root.hasDesignerEffect } @@ -194,7 +196,10 @@ Section { SectionLayout { - PropertyLabel { text: qsTr("Visible") } + PropertyLabel { + text: qsTr("Visible") + tooltip: qsTr("Toggles the visibility of the Layer Blur on the component.") + } SecondColumnLayout { CheckBox { @@ -206,7 +211,10 @@ Section { ExpandingSpacer {} } - PropertyLabel { text: qsTr("Blur") } + PropertyLabel { + text: qsTr("Blur") + tooltip: qsTr("Sets the intensity of the Layer Blur on the component.") + } SecondColumnLayout { SpinBox { @@ -234,7 +242,10 @@ Section { SectionLayout { - PropertyLabel { text: qsTr("Visible") } + PropertyLabel { + text: qsTr("Visible") + tooltip: qsTr("Toggles the visibility of blur on the selected background component.") + } SecondColumnLayout { CheckBox { @@ -246,7 +257,12 @@ Section { ExpandingSpacer {} } - PropertyLabel { text: qsTr("Blur") } + PropertyLabel { + text: qsTr("Blur") + tooltip: qsTr("Sets the intensity of blur on the selected background component.\n" + + "The foreground component should be transparent, and the background " + + "component should be solid.") + } SecondColumnLayout { SpinBox { @@ -260,7 +276,12 @@ Section { ExpandingSpacer {} } - PropertyLabel { text: qsTr("Background") } + PropertyLabel { + text: qsTr("Background") + tooltip: qsTr("Sets a component as the background of a transparent component." + + "The Background Blur works only on this component. The component should " + + "be solid.") + } SecondColumnLayout { ItemFilterComboBox { @@ -364,7 +385,10 @@ Section { id: controlContainer property bool isDropShadow: shadowComboBox.currentValue === "DesignDropShadow" - PropertyLabel { text: qsTr("Visible") } + PropertyLabel { + text: qsTr("Visible") + tooltip: qsTr("Toggles the visibility of the component shadow.") + } SecondColumnLayout { CheckBox { @@ -376,7 +400,10 @@ Section { ExpandingSpacer {} } - PropertyLabel { text: qsTr("Blur") } + PropertyLabel { + text: qsTr("Blur") + tooltip: qsTr("Sets the intensity of the component shadow.") + } SecondColumnLayout { SpinBox { @@ -392,6 +419,7 @@ Section { PropertyLabel { text: qsTr("Spread") + tooltip: qsTr("Resizes the base shadow of the component by pixels.") enabled: modelNodeBackend.isInstanceOf("Rectangle") } @@ -410,7 +438,7 @@ Section { PropertyLabel { text: qsTr("Color") - tooltip: qsTr("Sets the color.") + tooltip: qsTr("Sets the color of the shadow.") } ColorEditor { @@ -418,7 +446,11 @@ Section { supportGradient: false } - PropertyLabel { text: qsTr("Offset") } + PropertyLabel { + text: qsTr("Offset") + tooltip: qsTr("Moves the shadow with respect to the component in " + + "X and Y coordinates by pixels.") + } SecondColumnLayout { SpinBox { @@ -460,6 +492,7 @@ Section { PropertyLabel { visible: controlContainer.isDropShadow text: qsTr("Show behind") + tooltip: qsTr("Toggles the visibility of the shadow behind a transparent component.") } SecondColumnLayout { @@ -499,7 +532,7 @@ Section { width: StudioTheme.Values.singleControlColumnWidth buttonIcon: qsTr("Add Shadow Effect") iconFont: StudioTheme.Constants.font - tooltip: qsTr("Adds a Design Drop Shadow.") + tooltip: qsTr("Adds Drop Shadow or Inner Shadow effects to a component.") onClicked: { modelNodeBackend.createModelNode(root.effectNode, "effects", From ac5d88716b103e2adb956fa32c1b9adec71fe1bf Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Thu, 2 May 2024 12:47:43 +0200 Subject: [PATCH 050/154] QmlProject: Add more image formats Task-number: QDS-12660 Change-Id: Idabe761b8ef7d1a02fb0a4a05632a8e11e036cce Reviewed-by: Burak Hancerli Reviewed-by: Thomas Hartmann --- .../buildsystem/projectitem/converters.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/plugins/qmlprojectmanager/buildsystem/projectitem/converters.cpp b/src/plugins/qmlprojectmanager/buildsystem/projectitem/converters.cpp index 95cc859af52..d9536484b7d 100644 --- a/src/plugins/qmlprojectmanager/buildsystem/projectitem/converters.cpp +++ b/src/plugins/qmlprojectmanager/buildsystem/projectitem/converters.cpp @@ -14,7 +14,11 @@ const static QStringList imageFilesFilter{QStringLiteral("*.jpeg"), QStringLiteral("*.png"), QStringLiteral("*.svg"), QStringLiteral("*.hdr"), - QStringLiteral("*.ktx")}; + QStringLiteral("*.ktx"), + QStringLiteral("*.bmp"), + QStringLiteral("*.ttf"), + QStringLiteral("*.tiff"), + QStringLiteral("*.webp")}; QString jsonValueToString(const QJsonValue &val, int indentationLevel, bool indented); From c8215e986d7a063d14cdf5de5ccd84d4328b76be Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Tue, 30 Apr 2024 23:49:58 +0300 Subject: [PATCH 051/154] QmlDesigner: Reload bundles when id change i.e. when opening a project that has a different import folder style from the currently open project. Fixes: QDS-12639 Change-Id: I8d341c38ec771dc5e28c4436cd9ad191e27c38a8 Reviewed-by: Miikka Heikkinen Reviewed-by: Qt CI Patch Build Bot --- .../contentlibraryeffectsmodel.cpp | 61 ++++++++++-------- .../contentlibrarymaterialsmodel.cpp | 61 ++++++++++-------- .../contentlibrarymaterialsmodel.h | 4 +- .../contentlibraryusermodel.cpp | 63 +++++++++++-------- 4 files changed, 109 insertions(+), 80 deletions(-) diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectsmodel.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectsmodel.cpp index 7704c164fcd..2d467d811ed 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectsmodel.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectsmodel.cpp @@ -95,9 +95,21 @@ QHash ContentLibraryEffectsModel::roleNames() const void ContentLibraryEffectsModel::loadBundle() { - if (m_bundleExists || m_probeBundleDir) + auto compUtils = QmlDesignerPlugin::instance()->documentManager().generatedComponentUtils(); + + if (m_probeBundleDir || (m_bundleExists && m_bundleId == compUtils.effectsBundleId())) return; + // clean up + qDeleteAll(m_bundleCategories); + m_bundleCategories.clear(); + m_bundleExists = false; + m_isEmpty = true; + m_probeBundleDir = false; + m_bundleObj = {}; + m_bundleId.clear(); + m_bundlePath.clear(); + QDir bundleDir; if (!qEnvironmentVariable("EFFECT_BUNDLE_PATH").isEmpty()) @@ -112,30 +124,30 @@ void ContentLibraryEffectsModel::loadBundle() while (!bundleDir.cd("effect_bundle") && bundleDir.cdUp()) ; // do nothing - if (bundleDir.dirName() != "effect_bundle") // bundlePathDir not found + if (bundleDir.dirName() != "effect_bundle") { // bundlePathDir not found + resetModel(); return; + } } QString bundlePath = bundleDir.filePath("effect_bundle.json"); - if (m_bundleObj.isEmpty()) { - QFile propsFile(bundlePath); - - if (!propsFile.open(QIODevice::ReadOnly)) { - qWarning("Couldn't open effect_bundle.json"); - return; - } - - QJsonDocument bundleJsonDoc = QJsonDocument::fromJson(propsFile.readAll()); - if (bundleJsonDoc.isNull()) { - qWarning("Invalid effect_bundle.json file"); - return; - } else { - m_bundleObj = bundleJsonDoc.object(); - } + QFile bundleFile(bundlePath); + if (!bundleFile.open(QIODevice::ReadOnly)) { + qWarning("Couldn't open effect_bundle.json"); + resetModel(); + return; } - auto compUtils = QmlDesignerPlugin::instance()->documentManager().generatedComponentUtils(); + QJsonDocument bundleJsonDoc = QJsonDocument::fromJson(bundleFile.readAll()); + if (bundleJsonDoc.isNull()) { + qWarning("Invalid effect_bundle.json file"); + resetModel(); + return; + } + + m_bundleObj = bundleJsonDoc.object(); + QString bundleType = compUtils.effectsBundleType(); m_bundleId = compUtils.effectsBundleId(); @@ -145,9 +157,9 @@ void ContentLibraryEffectsModel::loadBundle() auto category = new ContentLibraryEffectsCategory(this, cat); const QJsonObject itemsObj = catsObj.value(cat).toObject(); - const QStringList items = itemsObj.keys(); - for (const QString &item : items) { - const QJsonObject itemObj = itemsObj.value(item).toObject(); + const QStringList itemsNames = itemsObj.keys(); + for (const QString &itemName : itemsNames) { + const QJsonObject itemObj = itemsObj.value(itemName).toObject(); QStringList files; const QJsonArray assetsArr = itemObj.value("files").toArray(); @@ -159,7 +171,7 @@ void ContentLibraryEffectsModel::loadBundle() TypeName type = QLatin1String("%1.%2") .arg(bundleType, qml.chopped(4)).toLatin1(); // chopped(4): remove .qml - auto bundleItem = new ContentLibraryEffect(category, item, qml, type, icon, files); + auto bundleItem = new ContentLibraryEffect(category, itemName, qml, type, icon, files); category->addBundleItem(bundleItem); } @@ -171,11 +183,10 @@ void ContentLibraryEffectsModel::loadBundle() for (const QJsonValueConstRef &file : sharedFilesArr) m_bundleSharedFiles.append(file.toString()); - resetModel(); - updateIsEmpty(); m_bundlePath = bundleDir.path(); m_bundleExists = true; - emit bundleExistsChanged(); + updateIsEmpty(); + resetModel(); } bool ContentLibraryEffectsModel::hasRequiredQuick3DImport() const diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.cpp index 7f68ad379d0..1fc05c3503f 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.cpp @@ -212,35 +212,43 @@ QString ContentLibraryMaterialsModel::bundleId() const return m_bundleId; } -void ContentLibraryMaterialsModel::loadMaterialBundle(const QDir &matBundleDir) +void ContentLibraryMaterialsModel::loadMaterialBundle(const QDir &bundleDir) { - if (m_matBundleExists) + auto compUtils = QmlDesignerPlugin::instance()->documentManager().generatedComponentUtils(); + + if (m_bundleExists && m_bundleId == compUtils.materialsBundleId()) return; - QString matBundlePath = matBundleDir.filePath("material_bundle.json"); + // clean up + qDeleteAll(m_bundleCategories); + m_bundleCategories.clear(); + m_bundleExists = false; + m_isEmpty = true; + m_bundleObj = {}; + m_bundleId.clear(); - if (m_matBundleObj.isEmpty()) { - QFile matPropsFile(matBundlePath); + QString bundlePath = bundleDir.filePath("material_bundle.json"); - if (!matPropsFile.open(QIODevice::ReadOnly)) { - qWarning("Couldn't open material_bundle.json"); - return; - } - - QJsonDocument matBundleJsonDoc = QJsonDocument::fromJson(matPropsFile.readAll()); - if (matBundleJsonDoc.isNull()) { - qWarning("Invalid material_bundle.json file"); - return; - } else { - m_matBundleObj = matBundleJsonDoc.object(); - } + QFile bundleFile(bundlePath); + if (!bundleFile.open(QIODevice::ReadOnly)) { + qWarning("Couldn't open material_bundle.json"); + resetModel(); + return; } - auto compUtils = QmlDesignerPlugin::instance()->documentManager().generatedComponentUtils(); + QJsonDocument bundleJsonDoc = QJsonDocument::fromJson(bundleFile.readAll()); + if (bundleJsonDoc.isNull()) { + qWarning("Invalid material_bundle.json file"); + resetModel(); + return; + } + + m_bundleObj = bundleJsonDoc.object(); + QString bundleType = compUtils.materialsBundleType(); m_bundleId = compUtils.materialsBundleId(); - const QJsonObject catsObj = m_matBundleObj.value("categories").toObject(); + const QJsonObject catsObj = m_bundleObj.value("categories").toObject(); const QStringList categories = catsObj.keys(); for (const QString &cat : categories) { auto category = new ContentLibraryMaterialsCategory(this, cat); @@ -255,7 +263,7 @@ void ContentLibraryMaterialsModel::loadMaterialBundle(const QDir &matBundleDir) for (const QJsonValueConstRef &asset : assetsArr) files.append(asset.toString()); - QUrl icon = QUrl::fromLocalFile(matBundleDir.filePath(matObj.value("icon").toString())); + QUrl icon = QUrl::fromLocalFile(bundleDir.filePath(matObj.value("icon").toString())); QString qml = matObj.value("qml").toString(); TypeName type = QLatin1String("%1.%2") .arg(bundleType, qml.chopped(4)).toLatin1(); // chopped(4): remove .qml @@ -269,23 +277,22 @@ void ContentLibraryMaterialsModel::loadMaterialBundle(const QDir &matBundleDir) } m_bundleSharedFiles.clear(); - const QJsonArray sharedFilesArr = m_matBundleObj.value("sharedFiles").toArray(); + const QJsonArray sharedFilesArr = m_bundleObj.value("sharedFiles").toArray(); for (const QJsonValueConstRef &file : sharedFilesArr) m_bundleSharedFiles.append(file.toString()); QStringList missingSharedFiles; for (const QString &s : std::as_const(m_bundleSharedFiles)) { - if (!QFileInfo::exists(matBundleDir.filePath(s))) + if (!QFileInfo::exists(bundleDir.filePath(s))) missingSharedFiles.push_back(s); } if (missingSharedFiles.length() > 0) - downloadSharedFiles(matBundleDir, missingSharedFiles); + downloadSharedFiles(bundleDir, missingSharedFiles); - resetModel(); + m_bundleExists = true; updateIsEmpty(); - m_matBundleExists = true; - emit matBundleExistsChanged(); + resetModel(); } bool ContentLibraryMaterialsModel::hasRequiredQuick3DImport() const @@ -295,7 +302,7 @@ bool ContentLibraryMaterialsModel::hasRequiredQuick3DImport() const bool ContentLibraryMaterialsModel::matBundleExists() const { - return m_matBundleExists; + return m_bundleExists; } void ContentLibraryMaterialsModel::setSearchText(const QString &searchText) diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.h index 7c7e8809940..3c09bf07c04 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.h +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.h @@ -73,10 +73,10 @@ private: QString m_bundleId; QStringList m_bundleSharedFiles; QList m_bundleCategories; - QJsonObject m_matBundleObj; + QJsonObject m_bundleObj; bool m_isEmpty = true; - bool m_matBundleExists = false; + bool m_bundleExists = false; bool m_hasModelSelection = false; int m_quick3dMajorVersion = -1; diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp index db4817ebb67..c79922caddf 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp @@ -226,39 +226,49 @@ QJsonObject &ContentLibraryUserModel::bundleJsonObjectRef() void ContentLibraryUserModel::loadMaterialBundle() { - if (m_matBundleExists) + auto compUtils = QmlDesignerPlugin::instance()->documentManager().generatedComponentUtils(); + + if (m_matBundleExists && m_bundleIdMaterial == compUtils.userMaterialsBundleId()) return; + // clean up + qDeleteAll(m_userMaterials); + m_userMaterials.clear(); + m_matBundleExists = false; + m_isEmpty = true; + m_bundleObj = {}; + m_bundleIdMaterial.clear(); + + int matSectionIdx = 0; + QDir bundleDir{Paths::bundlesPathSetting() + "/User/materials"}; bundleDir.mkpath("."); - if (m_bundleObj.isEmpty()) { - auto jsonFilePath = Utils::FilePath::fromString(bundleDir.filePath("user_materials_bundle.json")); - if (!jsonFilePath.exists()) { - QString jsonContent = "{\n"; - jsonContent += " \"id\": \"UserMaterials\",\n"; - jsonContent += " \"materials\": {\n"; - jsonContent += " }\n"; - jsonContent += "}"; - jsonFilePath.writeFileContents(jsonContent.toLatin1()); - } - - QFile jsonFile(jsonFilePath.path()); - if (!jsonFile.open(QIODevice::ReadOnly)) { - qWarning("Couldn't open user_materials_bundle.json"); - return; - } - - QJsonDocument matBundleJsonDoc = QJsonDocument::fromJson(jsonFile.readAll()); - if (matBundleJsonDoc.isNull()) { - qWarning("Invalid user_materials_bundle.json file"); - return; - } else { - m_bundleObj = matBundleJsonDoc.object(); - } + auto jsonFilePath = Utils::FilePath::fromString(bundleDir.filePath("user_materials_bundle.json")); + if (!jsonFilePath.exists()) { + QString jsonContent = "{\n"; + jsonContent += " \"id\": \"UserMaterials\",\n"; + jsonContent += " \"materials\": {\n"; + jsonContent += " }\n"; + jsonContent += "}"; + jsonFilePath.writeFileContents(jsonContent.toLatin1()); } - auto compUtils = QmlDesignerPlugin::instance()->documentManager().generatedComponentUtils(); + QFile jsonFile(jsonFilePath.path()); + if (!jsonFile.open(QIODevice::ReadOnly)) { + qWarning("Couldn't open user_materials_bundle.json"); + emit dataChanged(index(matSectionIdx), index(matSectionIdx)); + return; + } + + QJsonDocument matBundleJsonDoc = QJsonDocument::fromJson(jsonFile.readAll()); + if (matBundleJsonDoc.isNull()) { + qWarning("Invalid user_materials_bundle.json file"); + emit dataChanged(index(matSectionIdx), index(matSectionIdx)); + return; + } + + m_bundleObj = matBundleJsonDoc.object(); m_bundleObj["id"] = compUtils.userMaterialsBundleId(); // parse materials @@ -290,6 +300,7 @@ void ContentLibraryUserModel::loadMaterialBundle() m_matBundleExists = true; emit matBundleExistsChanged(); + emit dataChanged(index(matSectionIdx), index(matSectionIdx)); } void ContentLibraryUserModel::loadTextureBundle() From 4cb9857293f32bb35ee4b60c630b88d6ba38ce5a Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Thu, 2 May 2024 14:03:50 +0300 Subject: [PATCH 052/154] QmlDesigner: Handle material type without using item library entries Recent project structure changes invalidated material bundle metainfo files, as we can't support both new and old structure with them. They were only used for populating possible types in material editor, so adjusted this functionality to only allow changing the type between basic types. If the material is a custom component, then the only the current type is displayed and the type combo box is disabled. Fixes: QDS-12628 Change-Id: I07b1a482977f0ecc94c35f3d485302c85c6153f9 Reviewed-by: Mahmoud Badri --- .../MaterialEditorTopSection.qml | 1 + .../materialeditor/materialeditorview.cpp | 89 +++---------------- .../materialeditor/materialeditorview.h | 2 - 3 files changed, 15 insertions(+), 77 deletions(-) diff --git a/share/qtcreator/qmldesigner/materialEditorQmlSources/MaterialEditorTopSection.qml b/share/qtcreator/qmldesigner/materialEditorQmlSources/MaterialEditorTopSection.qml index 23311636bc2..1879ea177c6 100644 --- a/share/qtcreator/qmldesigner/materialEditorQmlSources/MaterialEditorTopSection.qml +++ b/share/qtcreator/qmldesigner/materialEditorQmlSources/MaterialEditorTopSection.qml @@ -214,6 +214,7 @@ Column { model: possibleTypes showExtendedFunctionButton: false implicitWidth: StudioTheme.Values.singleControlColumnWidth + enabled: possibleTypes.length > 1 onActivated: changeTypeName(currentValue) } diff --git a/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp b/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp index c1edd0fe1f7..d65ce800c03 100644 --- a/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp +++ b/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp @@ -24,7 +24,6 @@ #include "qmldesignerplugin.h" #include "qmltimeline.h" #include "variantproperty.h" -#include #include #include @@ -63,10 +62,6 @@ MaterialEditorView::MaterialEditorView(ExternalDependenciesInterface &externalDe } }); - m_typeUpdateTimer.setSingleShot(true); - m_typeUpdateTimer.setInterval(500); - connect(&m_typeUpdateTimer, &QTimer::timeout, this, &MaterialEditorView::updatePossibleTypes); - QmlDesignerPlugin::trackWidgetFocusTime(m_stackedWidget, Constants::EVENT_MATERIALEDITOR_TIME); MaterialEditorDynamicPropertiesProxyModel::registerDeclarativeType(); @@ -333,8 +328,10 @@ void MaterialEditorView::resetView() setupQmlBackend(); - if (m_qmlBackEnd) + if (m_qmlBackEnd) { m_qmlBackEnd->emitSelectionChanged(); + updatePossibleTypes(); + } QTimer::singleShot(0, this, &MaterialEditorView::requestPreviewRender); @@ -605,7 +602,6 @@ void MaterialEditorView::setupQmlBackend() else m_dynamicPropertiesModel->reset(); - delayedTypeUpdate(); initPreviewData(); m_stackedWidget->setCurrentWidget(m_qmlBackEnd->widget()); @@ -690,21 +686,6 @@ void MaterialEditorView::initPreviewData() } } -void MaterialEditorView::delayedTypeUpdate() -{ - m_typeUpdateTimer.start(); -} - -[[maybe_unused]] static Import entryToImport(const ItemLibraryEntry &entry) -{ - if (entry.majorVersion() == -1 && entry.minorVersion() == -1) - return Import::createFileImport(entry.requiredImport()); - - return Import::createLibraryImport(entry.requiredImport(), - QString::number(entry.majorVersion()) + QLatin1Char('.') + - QString::number(entry.minorVersion())); -} - void MaterialEditorView::updatePossibleTypes() { QTC_ASSERT(model(), return); @@ -712,49 +693,21 @@ void MaterialEditorView::updatePossibleTypes() if (!m_qmlBackEnd) return; -#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()); - }); + static const QStringList basicTypes { + "CustomMaterial", + "DefaultMaterial", + "PrincipledMaterial", + "SpecularGlossyMaterial" + }; - // I am unsure about the code intention here -#else // Ensure basic types are always first - QStringList nonQuick3dTypes; - QStringList allTypes; + const QString matType = m_selectedMaterial.simplifiedTypeName(); - const QList itemLibEntries = m_itemLibraryInfo->entries(); - for (const ItemLibraryEntry &entry : itemLibEntries) { - NodeMetaInfo metaInfo = model()->metaInfo(entry.typeName()); - bool valid = metaInfo.isValid() - && (metaInfo.majorVersion() >= entry.majorVersion() - || metaInfo.majorVersion() < 0); - if (valid && metaInfo.isQtQuick3DMaterial()) { - bool addImport = entry.requiredImport().isEmpty(); - if (!addImport) { - Import import = entryToImport(entry); - addImport = model()->hasImport(import, true, true); - } - if (addImport) { - const QList typeSplit = entry.typeName().split('.'); - const QString typeName = QString::fromLatin1(typeSplit.last()); - if (typeSplit.size() == 2 && typeSplit.first() == "QtQuick3D") { - if (!allTypes.contains(typeName)) - allTypes.append(typeName); - } else if (!nonQuick3dTypes.contains(typeName)) { - nonQuick3dTypes.append(typeName); - } - } - } + if (basicTypes.contains(matType)) { + m_qmlBackEnd->contextObject()->setPossibleTypes(basicTypes); + return; } - allTypes.sort(); - nonQuick3dTypes.sort(); - allTypes.append(nonQuick3dTypes); - - m_qmlBackEnd->contextObject()->setPossibleTypes(allTypes); -#endif + m_qmlBackEnd->contextObject()->setPossibleTypes({matType}); } void MaterialEditorView::modelAttached(Model *model) @@ -774,20 +727,6 @@ 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, - this, &MaterialEditorView::delayedTypeUpdate); - } - m_itemLibraryInfo = model->metaInfo().itemLibraryInfo(); - if (m_itemLibraryInfo) { - connect(m_itemLibraryInfo.data(), &ItemLibraryInfo::entriesChanged, - this, &MaterialEditorView::delayedTypeUpdate); - } - } -#endif - if (!m_setupCompleted) { reloadQml(); m_setupCompleted = true; diff --git a/src/plugins/qmldesigner/components/materialeditor/materialeditorview.h b/src/plugins/qmldesigner/components/materialeditor/materialeditorview.h index c201742bd51..11bea460638 100644 --- a/src/plugins/qmldesigner/components/materialeditor/materialeditorview.h +++ b/src/plugins/qmldesigner/components/materialeditor/materialeditorview.h @@ -109,12 +109,10 @@ private: bool noValidSelection() const; void initPreviewData(); - void delayedTypeUpdate(); void updatePossibleTypes(); ModelNode m_selectedMaterial; QTimer m_ensureMatLibTimer; - QTimer m_typeUpdateTimer; QShortcut *m_updateShortcut = nullptr; int m_timerId = 0; QStackedWidget *m_stackedWidget = nullptr; From a327f38d5e37ae963701a203c6deffa253f61a9d Mon Sep 17 00:00:00 2001 From: Shrief Gabr Date: Thu, 2 May 2024 15:56:56 +0300 Subject: [PATCH 053/154] QmlDesigner: Ignore Image cell warning for Url column Task-number: QDS-12157 Change-Id: Ia2a46ef878d522f6c5a08fb817c2ef1332cb113b Reviewed-by: Miikka Heikkinen Reviewed-by: Mahmoud Badri --- .../components/collectioneditor/collectiondetails.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp index 3ff37a6fc27..7e61f5e6100 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp @@ -563,7 +563,10 @@ DataTypeWarning::Warning CollectionDetails::cellWarningCheck(int row, int column if ((columnType == DataType::String || columnType == DataType::Real) && cellType == DataType::Integer) return DataTypeWarning::Warning::None; - if ((columnType == DataType::Url || columnType == DataType::Image) && cellType == DataType::String) + if (columnType == DataType::Url && cellType == DataType::String) + return DataTypeWarning::Warning::None; + + if (columnType == DataType::Image && (cellType == DataType::Url || cellType == DataType::String)) return DataTypeWarning::Warning::None; if (columnType != cellType) From 34e4710f7ac206e0c89bea142463d95a4ff0f462 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Thu, 2 May 2024 15:13:42 +0200 Subject: [PATCH 054/154] QmlProject: Fix tests Change-Id: I988b1e623ee672109172ac8f746406a62b6a895b Reviewed-by: Thomas Hartmann Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Burak Hancerli --- .../data/converter/test-set-1/testfile.qmltojson | 12 ++++++++++-- .../data/converter/test-set-2/testfile.qmltojson | 6 +++++- .../data/converter/test-set-3/testfile.qmltojson | 12 ++++++++++-- .../data/converter/test-set-mcu-1/testfile.qmltojson | 6 +++++- 4 files changed, 30 insertions(+), 6 deletions(-) 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 815e9a0cc56..39c9517b991 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 @@ -75,7 +75,11 @@ "*.png", "*.svg", "*.hdr", - "*.ktx" + "*.ktx", + "*.bmp", + "*.ttf", + "*.tiff", + "*.webp" ], "mcuProperties": { }, @@ -93,7 +97,11 @@ "*.png", "*.svg", "*.hdr", - "*.ktx" + "*.ktx", + "*.bmp", + "*.ttf", + "*.tiff", + "*.webp" ], "mcuProperties": { }, 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 2231f36ff21..4444bbabce6 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 @@ -45,7 +45,11 @@ "*.png", "*.svg", "*.hdr", - "*.ktx" + "*.ktx", + "*.bmp", + "*.ttf", + "*.tiff", + "*.webp" ], "mcuProperties": { }, 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 815e9a0cc56..39c9517b991 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 @@ -75,7 +75,11 @@ "*.png", "*.svg", "*.hdr", - "*.ktx" + "*.ktx", + "*.bmp", + "*.ttf", + "*.tiff", + "*.webp" ], "mcuProperties": { }, @@ -93,7 +97,11 @@ "*.png", "*.svg", "*.hdr", - "*.ktx" + "*.ktx", + "*.bmp", + "*.ttf", + "*.tiff", + "*.webp" ], "mcuProperties": { }, 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 index 303bfc3899d..67eb8cc4ed2 100644 --- 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 @@ -48,7 +48,11 @@ "*.png", "*.svg", "*.hdr", - "*.ktx" + "*.ktx", + "*.bmp", + "*.ttf", + "*.tiff", + "*.webp" ], "mcuProperties": { }, From 3f0ac381b4a9d2f1404206542e71e62a5db01be9 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Thu, 2 May 2024 15:02:07 +0200 Subject: [PATCH 055/154] QmlDesigner: Guard for missing qml backend Change-Id: I3ced324b4bde1feadbd71434cbb6ce54a531dbbc Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Miikka Heikkinen Reviewed-by: Mahmoud Badri --- .../components/materialeditor/materialeditorview.cpp | 3 ++- .../qmldesigner/components/textureeditor/textureeditorview.cpp | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp b/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp index d65ce800c03..ce11c587584 100644 --- a/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp +++ b/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp @@ -877,7 +877,8 @@ void MaterialEditorView::selectedNodesChanged(const QList &selectedNo m_selectedModels.append(node); } - m_qmlBackEnd->contextObject()->setHasModelSelection(!m_selectedModels.isEmpty()); + if (m_qmlBackEnd) + m_qmlBackEnd->contextObject()->setHasModelSelection(!m_selectedModels.isEmpty()); } void MaterialEditorView::currentStateChanged(const ModelNode &node) diff --git a/src/plugins/qmldesigner/components/textureeditor/textureeditorview.cpp b/src/plugins/qmldesigner/components/textureeditor/textureeditorview.cpp index 5de3730c97b..4f2cc0f81f8 100644 --- a/src/plugins/qmldesigner/components/textureeditor/textureeditorview.cpp +++ b/src/plugins/qmldesigner/components/textureeditor/textureeditorview.cpp @@ -698,7 +698,8 @@ void TextureEditorView::selectedNodesChanged(const QList &selectedNod m_selectedModel = selectedNodeList.at(0); bool hasValidSelection = QmlObjectNode(m_selectedModel).hasBindingProperty("materials"); - m_qmlBackEnd->contextObject()->setHasSingleModelSelection(hasValidSelection); + if (m_qmlBackEnd) + m_qmlBackEnd->contextObject()->setHasSingleModelSelection(hasValidSelection); } void TextureEditorView::currentStateChanged(const ModelNode &node) From fe461b85e6b273b1b5e7db4d0cc0808ed9d8368c Mon Sep 17 00:00:00 2001 From: Henning Gruendl Date: Fri, 3 May 2024 09:59:29 +0200 Subject: [PATCH 056/154] QmlDesigner: Add DesignEffects plugin to template Add DesignEffects plugin to qmlcomponents template for CMake based project usage. Change-Id: Ie2a0ac4cf47719c4a2e49532acaa9a092a6dc372 Reviewed-by: Thomas Hartmann --- .../qmlprojectmanager/cmakegen/templates/qmlcomponents.tpl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/plugins/qmlprojectmanager/cmakegen/templates/qmlcomponents.tpl b/src/plugins/qmlprojectmanager/cmakegen/templates/qmlcomponents.tpl index e6cd7c26dd3..5640b85844c 100644 --- a/src/plugins/qmlprojectmanager/cmakegen/templates/qmlcomponents.tpl +++ b/src/plugins/qmlprojectmanager/cmakegen/templates/qmlcomponents.tpl @@ -18,6 +18,7 @@ FetchContent_Populate(ds) target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE QuickStudioComponentsplugin + QuickStudioDesignEffectsplugin QuickStudioEffectsplugin QuickStudioApplicationplugin FlowViewplugin From cb0e28d2e6c5654f507c12f0294abf2fe8317020 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Thu, 2 May 2024 16:09:44 +0300 Subject: [PATCH 057/154] QmlDesigner: Start requested timer when it's created in 3D view Fixes: QDS-12659 Change-Id: I78f5854185e4efebc00ea63c5dd290dc851e21eb Reviewed-by: Mahmoud Badri Reviewed-by: --- src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp b/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp index 00f96f51949..f24bc342b91 100644 --- a/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp +++ b/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp @@ -1436,6 +1436,7 @@ void GeneralHelper::requestTimerEvent(const QString &timerId, qint64 delay) emit requestedTimerEvent(timerId); }); m_eventTimers[timerId] = timer; + timer->start(delay); } } From 39fed3759c187f5c6d2e32c1bc4ad02fb899f4da Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Thu, 2 May 2024 16:24:41 +0300 Subject: [PATCH 058/154] QmlDesigner: Show crosshair only after there is a move in fly mode Fixes: QDS-12596 Change-Id: Id34ad1b0745814a5b3dee8b6c3001d19ca7f4118 Reviewed-by: Mahmoud Badri Reviewed-by: --- .../qml2puppet/mockfiles/qt6/EditCameraController.qml | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/tools/qml2puppet/mockfiles/qt6/EditCameraController.qml b/src/tools/qml2puppet/mockfiles/qt6/EditCameraController.qml index bbc50a18e56..31e9a7cfaca 100644 --- a/src/tools/qml2puppet/mockfiles/qt6/EditCameraController.qml +++ b/src/tools/qml2puppet/mockfiles/qt6/EditCameraController.qml @@ -29,6 +29,7 @@ Item { readonly property real _keyPanAmount: 1.0 property bool ignoreToolState: false property bool flyMode: viewRoot.flyMode + property bool hasMovedInFlyMode: false z: 10 anchors.fill: parent @@ -173,11 +174,15 @@ Item { function rotateCamera(angles) { + if (flyMode) + hasMovedInFlyMode = true; cameraCtrl._lookAtPoint = _generalHelper.rotateCamera(camera, angles, _lookAtPoint); } function moveCamera(moveVec) { + if (flyMode) + hasMovedInFlyMode = true; cameraCtrl._lookAtPoint = _generalHelper.moveCamera(camera, _lookAtPoint, moveVec); } @@ -243,7 +248,8 @@ Item { cameraCtrl._dragging = false; cameraCtrl.storeCameraState(0); } - _generalHelper.stopAllCameraMoves() + hasMovedInFlyMode = false; + _generalHelper.stopAllCameraMoves(); _generalHelper.setCameraSpeedModifier(1.0); } @@ -262,7 +268,7 @@ Item { Image { anchors.centerIn: parent source: "qrc:///qtquickplugin/mockfiles/images/crosshair.png" - visible: cameraCtrl.flyMode && viewRoot.activeSplit === cameraCtrl.splitId + visible: cameraCtrl.hasMovedInFlyMode && viewRoot.activeSplit === cameraCtrl.splitId opacity: 0.7 } From 854319bd7db9ec3ea2193fcd79a55b3e5f16a3e6 Mon Sep 17 00:00:00 2001 From: Teea Poldsam Date: Thu, 2 May 2024 14:34:09 +0300 Subject: [PATCH 059/154] Doc: Add the new fly mode spacebar shortcut to 3D doc Fixes: QDS-12339 Change-Id: I885fd118a5349adb4907d0dbf518101498daa5ea Reviewed-by: Miikka Heikkinen Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Johanna Vanhatapio --- .../src/qtquick3d-editor/qtdesignstudio-3d-editor.qdoc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-editor.qdoc b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-editor.qdoc index ebe842a5551..0c87fb499ae 100644 --- a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-editor.qdoc +++ b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-editor.qdoc @@ -161,6 +161,9 @@ \row \li \key Q or \key {Page down} \li Move down. + \row + \li \key Spacebar + \li Move to an object in the crosshairs. \endtable To adjust the movement speed, select \inlineimage icons/camera_speed.png in the From 6cec311c5d33aa718b5101d3a33fedb8859f67b2 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Fri, 3 May 2024 12:10:58 +0300 Subject: [PATCH 060/154] QmlDesigner: Fix url property path resolving Converting file:// urls to relative paths assumed that the file:// url would always point to a file that is in same or child folder of the qml document, which broke for urls that pointed under generated components folder. Fixes: QDS-12029 Change-Id: I352ba01650ebf6c1c5fedef9323bf27b44491736 Reviewed-by: Mahmoud Badri Reviewed-by: Thomas Hartmann Reviewed-by: Shrief Gabr Reviewed-by: Qt CI Patch Build Bot --- .../qml2puppet/qml2puppet/instances/objectnodeinstance.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/tools/qml2puppet/qml2puppet/instances/objectnodeinstance.cpp b/src/tools/qml2puppet/qml2puppet/instances/objectnodeinstance.cpp index a1c727f215b..453a30395f1 100644 --- a/src/tools/qml2puppet/qml2puppet/instances/objectnodeinstance.cpp +++ b/src/tools/qml2puppet/qml2puppet/instances/objectnodeinstance.cpp @@ -7,6 +7,7 @@ #include #include +#include #include #include #include @@ -627,8 +628,8 @@ QVariant ObjectNodeInstance::property(const PropertyName &name) const return QVariant(); if (url.scheme() == "file") { - int basePathLength = nodeInstanceServer()->fileUrl().toLocalFile().lastIndexOf('/'); - return QUrl(url.toLocalFile().mid(basePathLength + 1)); + QFileInfo fi{nodeInstanceServer()->fileUrl().toLocalFile()}; + return QUrl{fi.absoluteDir().relativeFilePath(url.toLocalFile())}; } } From 96bfa8e29f30856ff0737976747711669e5c3077 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Fri, 3 May 2024 11:29:42 +0200 Subject: [PATCH 061/154] QmlDesigner: Support for ExtendedSceneEnvironment.GlowBlendMode This is the first nested enum scope. Therefore we have to add support for nested enum scopes. In a subsequent patch, Enumeration has to be adjusted. This patch is minimizing the risk for QDS 4.5. Task-number: QDS-12645 Change-Id: I6ddfa89f3a3038eac8a7ce73c8c593191fa05b84 Reviewed-by: Miikka Heikkinen --- .../propertyeditor/propertyeditorvalue.cpp | 2 +- .../designercore/model/texttomodelmerger.cpp | 37 +++++++++++-------- 2 files changed, 23 insertions(+), 16 deletions(-) diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.cpp b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.cpp index 70686f31ae9..fbceb5dfea3 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.cpp @@ -143,7 +143,7 @@ void PropertyEditorValue::setValue(const QVariant &value) QString PropertyEditorValue::enumeration() const { - return m_value.value().nameToString(); + return m_value.value().nameToString().split('.').last(); } QString PropertyEditorValue::expression() const diff --git a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp index 1c1aba5feb9..b66270dca12 100644 --- a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp +++ b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp @@ -94,21 +94,23 @@ bool isGlobalQtEnums(QStringView value) bool isKnownEnumScopes(QStringView value) { - 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"}); + 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", + u"ExtendedSceneEnvironment.GlowBlendMode"}); return std::find(std::begin(list), std::end(list), QmlDesigner::ModelUtils::toStdStringView(value)) != std::end(list); @@ -559,6 +561,11 @@ public: //Check for known enum scopes used globally if (isKnownEnumScopes(astValueList.constFirst())) return QVariant::fromValue(Enumeration(astValue)); + } else if (astValueList.size() == 3) { + QString enumName = astValueList.constFirst() + '.' + astValueList.at(1); + if (isKnownEnumScopes(enumName)) + return QVariant::fromValue( + Enumeration(enumName.toUtf8(), astValueList.constLast().toUtf8())); } auto eStmt = AST::cast(rhs); From d9e4a205d255ceca740195e5806c0e47b56eed55 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Fri, 3 May 2024 13:44:21 +0300 Subject: [PATCH 062/154] QmlDesigner: Fix IconGizmo multiselection Clicked signal was directly connected to handleObjectClicked function, but it had different number of parameters, leading to multiselection parameter to always being undefined. Fixes: QDS-8137 Change-Id: If065531878df9ad7e6584c89dce19b6b7d71a676 Reviewed-by: Mahmoud Badri --- src/tools/qml2puppet/mockfiles/qt6/IconGizmo.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tools/qml2puppet/mockfiles/qt6/IconGizmo.qml b/src/tools/qml2puppet/mockfiles/qt6/IconGizmo.qml index acde896aeba..50c4aab1e73 100644 --- a/src/tools/qml2puppet/mockfiles/qt6/IconGizmo.qml +++ b/src/tools/qml2puppet/mockfiles/qt6/IconGizmo.qml @@ -29,7 +29,7 @@ Item { property alias iconSource: iconImage.source - signal clicked(Node node, bool multi) + signal clicked(Node node, int button, bool multi) function hasPoint(x, y) { @@ -83,7 +83,7 @@ Item { } onClicked: (mouse)=> { - iconGizmo.clicked(iconGizmo.targetNode, + iconGizmo.clicked(iconGizmo.targetNode, mouse.button, mouse.modifiers & Qt.ControlModifier); } hoverEnabled: iconGizmo.highlightOnHover && !iconGizmo.selected From 4366ed9aa2b2135def9db1a9a29186c0c58f4874 Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Fri, 3 May 2024 14:58:18 +0300 Subject: [PATCH 063/154] QmlDesigner: Add missing bundle types to GeneratedComponentUtils Also small refactoring. Change-Id: I54f1309aa69daf1d61b931063bb4b86612642cc1 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Miikka Heikkinen --- .../designercore/generatedcomponentutils.cpp | 45 ++++++++++--------- .../designercore/generatedcomponentutils.h | 8 +++- 2 files changed, 31 insertions(+), 22 deletions(-) diff --git a/src/plugins/qmldesigner/designercore/generatedcomponentutils.cpp b/src/plugins/qmldesigner/designercore/generatedcomponentutils.cpp index a1fe4d47632..137fbd9b528 100644 --- a/src/plugins/qmldesigner/designercore/generatedcomponentutils.cpp +++ b/src/plugins/qmldesigner/designercore/generatedcomponentutils.cpp @@ -166,26 +166,6 @@ QString GeneratedComponentUtils::composedEffectsTypePrefix() const return Constants::OLD_EFFECTS_FOLDER; } -QString GeneratedComponentUtils::materialsBundleType() const -{ - QString basePrefix = componentBundlesTypePrefix(); - - if (basePrefix.endsWith(Constants::OLD_COMPONENT_BUNDLES_TYPE)) - return basePrefix + '.' + QLatin1String(Constants::OLD_COMPONENT_BUNDLES_MATERIAL_BUNDLE_TYPE); - - return basePrefix + '.' + QLatin1String(Constants::COMPONENT_BUNDLES_MATERIAL_BUNDLE_TYPE); -} - -QString GeneratedComponentUtils::effectsBundleType() const -{ - QString basePrefix = componentBundlesTypePrefix(); - - if (basePrefix.endsWith(Constants::OLD_COMPONENT_BUNDLES_TYPE)) - return basePrefix + '.' + QLatin1String(Constants::OLD_COMPONENT_BUNDLES_EFFECT_BUNDLE_TYPE); - - return basePrefix + '.' + QLatin1String(Constants::COMPONENT_BUNDLES_EFFECT_BUNDLE_TYPE); -} - QString GeneratedComponentUtils::materialsBundleId() const { bool isNewImportDir = generatedComponentTypePrefix().endsWith(Constants::GENERATED_COMPONENTS_FOLDER); @@ -217,4 +197,29 @@ QString GeneratedComponentUtils::user3DBundleId() const return QLatin1String(Constants::COMPONENT_BUNDLES_USER_3D_BUNDLE_TYPE); } +QString GeneratedComponentUtils::materialsBundleType() const +{ + return componentBundlesTypePrefix() + '.' + materialsBundleId(); +} + +QString GeneratedComponentUtils::effectsBundleType() const +{ + return componentBundlesTypePrefix() + '.' + effectsBundleId(); +} + +QString GeneratedComponentUtils::userMaterialsBundleType() const +{ + return componentBundlesTypePrefix() + '.' + userMaterialsBundleId(); +} + +QString GeneratedComponentUtils::userEffectsBundleType() const +{ + return componentBundlesTypePrefix() + '.' + userEffectsBundleId(); +} + +QString GeneratedComponentUtils::user3DBundleType() const +{ + return componentBundlesTypePrefix() + '.' + user3DBundleId(); +} + } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/generatedcomponentutils.h b/src/plugins/qmldesigner/designercore/generatedcomponentutils.h index ea4149b551c..5fc51fd85f3 100644 --- a/src/plugins/qmldesigner/designercore/generatedcomponentutils.h +++ b/src/plugins/qmldesigner/designercore/generatedcomponentutils.h @@ -32,8 +32,6 @@ public: QString import3dTypePath() const; QString componentBundlesTypePrefix() const; QString composedEffectsTypePrefix() const; - QString materialsBundleType() const; - QString effectsBundleType() const; QString materialsBundleId() const; QString effectsBundleId() const; @@ -41,6 +39,12 @@ public: QString userEffectsBundleId() const; QString user3DBundleId() const; + QString materialsBundleType() const; + QString effectsBundleType() const; + QString userMaterialsBundleType() const; + QString userEffectsBundleType() const; + QString user3DBundleType() const; + private: ExternalDependenciesInterface &m_externalDependencies; }; From 3f48d2bb7e2e3c203e5952140e8d1bc786effd01 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Fri, 3 May 2024 15:28:52 +0300 Subject: [PATCH 064/154] QmlDesigner: Use SceneEnvironment background by default "Use Scene Environment" is not default option for 3D view background. Fixes: QDS-12414 Change-Id: I5981b5d77a044c5f2d250c5c1ccdf307a7764108 Reviewed-by: Mahmoud Badri --- src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml b/src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml index 381f3b11d14..0e876e0d7d0 100644 --- a/src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml +++ b/src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml @@ -35,7 +35,7 @@ Item { property color backgroundGradientColorStart: "#222222" property color backgroundGradientColorEnd: "#999999" property color gridColor: "#cccccc" - property bool syncEnvBackground: false + property bool syncEnvBackground: true property bool splitView: false property bool flyMode: false property bool showCameraSpeed: false @@ -302,7 +302,7 @@ Item { syncEnvBackground = toolStates.syncEnvBackground; updateEnvBackground(); } else if (resetToDefault) { - syncEnvBackground = false; + syncEnvBackground = true; updateEnvBackground(); } From df20f8924a2c9ac839bde15c30b7d3eb4742e4d6 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Fri, 3 May 2024 15:47:52 +0300 Subject: [PATCH 065/154] EffectComposer: Fix slider value precision Effects with slider controlled real values now properly cut the values to two decimals. Fixes: QDS-11773 Change-Id: I056a295e5a126536d3003490c938361e4ce382b3 Reviewed-by: Mahmoud Badri --- .../qmldesigner/effectComposerQmlSources/ValueFloat.qml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueFloat.qml b/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueFloat.qml index 969d7e29492..6b723a9bc59 100644 --- a/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueFloat.qml +++ b/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueFloat.qml @@ -42,8 +42,9 @@ Row { to: uniformMaxValue value: uniformValue onMoved: { - uniformValue = value - spinBox.value = value // binding isn't working for this property so update it + let fixedValue = Number.parseFloat(value).toFixed(slider.decimals) + uniformValue = fixedValue + spinBox.value = fixedValue // binding isn't working for this property so update it } } } From 0f2d15f16cd4457121490d02040ab5f23466083c Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Fri, 3 May 2024 14:23:07 +0200 Subject: [PATCH 066/154] QmlProject: Also add support for gif Change-Id: I46400e8bd605142401ab941e45baba1fa4b27fc0 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Burak Hancerli Reviewed-by: Thomas Hartmann --- .../buildsystem/projectitem/converters.cpp | 3 ++- .../data/converter/test-set-1/testfile.qmltojson | 6 ++++-- .../data/converter/test-set-2/testfile.qmltojson | 3 ++- .../data/converter/test-set-3/testfile.qmltojson | 6 ++++-- .../data/converter/test-set-mcu-1/testfile.qmltojson | 3 ++- 5 files changed, 14 insertions(+), 7 deletions(-) diff --git a/src/plugins/qmlprojectmanager/buildsystem/projectitem/converters.cpp b/src/plugins/qmlprojectmanager/buildsystem/projectitem/converters.cpp index d9536484b7d..82ba1fc2e41 100644 --- a/src/plugins/qmlprojectmanager/buildsystem/projectitem/converters.cpp +++ b/src/plugins/qmlprojectmanager/buildsystem/projectitem/converters.cpp @@ -18,7 +18,8 @@ const static QStringList imageFilesFilter{QStringLiteral("*.jpeg"), QStringLiteral("*.bmp"), QStringLiteral("*.ttf"), QStringLiteral("*.tiff"), - QStringLiteral("*.webp")}; + QStringLiteral("*.webp"), + QStringLiteral("*.gif")}; QString jsonValueToString(const QJsonValue &val, int indentationLevel, bool indented); 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 39c9517b991..e362cdf8860 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 @@ -79,7 +79,8 @@ "*.bmp", "*.ttf", "*.tiff", - "*.webp" + "*.webp", + "*.gif" ], "mcuProperties": { }, @@ -101,7 +102,8 @@ "*.bmp", "*.ttf", "*.tiff", - "*.webp" + "*.webp", + "*.gif" ], "mcuProperties": { }, 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 4444bbabce6..80eaf6fefa3 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 @@ -49,7 +49,8 @@ "*.bmp", "*.ttf", "*.tiff", - "*.webp" + "*.webp", + "*.gif" ], "mcuProperties": { }, 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 39c9517b991..e362cdf8860 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 @@ -79,7 +79,8 @@ "*.bmp", "*.ttf", "*.tiff", - "*.webp" + "*.webp", + "*.gif" ], "mcuProperties": { }, @@ -101,7 +102,8 @@ "*.bmp", "*.ttf", "*.tiff", - "*.webp" + "*.webp", + "*.gif" ], "mcuProperties": { }, 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 index 67eb8cc4ed2..634623c9cf2 100644 --- 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 @@ -52,7 +52,8 @@ "*.bmp", "*.ttf", "*.tiff", - "*.webp" + "*.webp", + "*.gif" ], "mcuProperties": { }, From 1b5ef0420bb1d147e0b26abb88bd5b31da11d5fa Mon Sep 17 00:00:00 2001 From: Pranta Dastider Date: Fri, 3 May 2024 15:10:28 +0200 Subject: [PATCH 067/154] QmlDesigner: Update the supported image list in the QDS doc This patch update the list of supported image formats in the documentation of Qt Design Studio. Fixes: QDS-12688 Change-Id: I5628b4d378447f0caa074e7264a3a4e5ee99b73c Reviewed-by: Thomas Hartmann --- doc/qtdesignstudio/src/components/qtquick-images.qdoc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/qtdesignstudio/src/components/qtquick-images.qdoc b/doc/qtdesignstudio/src/components/qtquick-images.qdoc index 176e4409f8a..dca494f8e13 100644 --- a/doc/qtdesignstudio/src/components/qtquick-images.qdoc +++ b/doc/qtdesignstudio/src/components/qtquick-images.qdoc @@ -44,6 +44,9 @@ If you need to display animated images, such as GIFs, use the \l {Animated Image} component. + \note Currently, the supported image formats include \b{jpeg, jpg, png, svg, + hdr, ktx, bmp, ttf, tiff, webp,} and \b{gif}. + \section1 Image Size \image qtquick-designer-image-properties.png "Image properties" From c985be451618bbead3eeb943a52a9a12ef570b58 Mon Sep 17 00:00:00 2001 From: Pranta Dastider Date: Fri, 3 May 2024 15:31:49 +0200 Subject: [PATCH 068/154] QmlDesigner: Update the supported image list in the QDS doc This patch update the list of supported image formats in the documentation of Qt Design Studio. Fixes: QDS-12688 Change-Id: I9280eeb5e8d37e1d51ef5c6bcb2917d81bdede80 Reviewed-by: Leena Miettinen --- doc/qtdesignstudio/src/components/qtquick-images.qdoc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/qtdesignstudio/src/components/qtquick-images.qdoc b/doc/qtdesignstudio/src/components/qtquick-images.qdoc index dca494f8e13..cbece71a4b5 100644 --- a/doc/qtdesignstudio/src/components/qtquick-images.qdoc +++ b/doc/qtdesignstudio/src/components/qtquick-images.qdoc @@ -44,8 +44,8 @@ If you need to display animated images, such as GIFs, use the \l {Animated Image} component. - \note Currently, the supported image formats include \b{jpeg, jpg, png, svg, - hdr, ktx, bmp, ttf, tiff, webp,} and \b{gif}. + \note Currently, the supported image formats include JPEG, JPG, PNG, SVG, + HDR, KTX, BMP, TTF, TIFF, WEBP, and GIF. \section1 Image Size From 77827660e15bf5c4c000b909d7bca8adc5597f14 Mon Sep 17 00:00:00 2001 From: Aleksei German Date: Fri, 3 May 2024 16:12:36 +0200 Subject: [PATCH 069/154] QmlDesigner: Bump MCUs metadata Task-number: QDS-11588 Change-Id: Ib1e5212406a856f32611d0a43d603e7c31a605c0 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Thomas Hartmann Reviewed-by: --- .../qtcreator/qmldesigner/qt4mcu/metadata.qml | 8 +- share/qtcreator/qmldesigner/qt4mcu/qul-27.qml | 227 ++++++++++++++++++ .../application-mcu/app_mcu.qmlproject.tpl | 8 +- 3 files changed, 239 insertions(+), 4 deletions(-) create mode 100644 share/qtcreator/qmldesigner/qt4mcu/qul-27.qml diff --git a/share/qtcreator/qmldesigner/qt4mcu/metadata.qml b/share/qtcreator/qmldesigner/qt4mcu/metadata.qml index b4ae840bddb..2df700e72ac 100644 --- a/share/qtcreator/qmldesigner/qt4mcu/metadata.qml +++ b/share/qtcreator/qmldesigner/qt4mcu/metadata.qml @@ -5,7 +5,7 @@ Metadata { id: metadataFile - defaultVersion: v26 + defaultVersion: v27 VersionData { id: v14 @@ -72,4 +72,10 @@ Metadata { name: "Qt for MCUs 2.6" path: "qul-26.qml" } + + VersionData { + id: v27 + name: "Qt for MCUs 2.7" + path: "qul-27.qml" + } } diff --git a/share/qtcreator/qmldesigner/qt4mcu/qul-27.qml b/share/qtcreator/qmldesigner/qt4mcu/qul-27.qml new file mode 100644 index 00000000000..6cb5640472a --- /dev/null +++ b/share/qtcreator/qmldesigner/qt4mcu/qul-27.qml @@ -0,0 +1,227 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +// new import: QUL.Studio.Components +// new types: ArcItem, Gradients, Basic and LinearGradient +// new properties: gradients related +// Layer Application renamed to ApplicationScreens + +VersionData { + name: "Qt for MCUs 2.7" + + bannedItems: [ + "QtQuick.AnimatedImage", + "QtQuick.Flow", + "QtQuick.FocusScope", + "QtQuick.Grid", + "QtQuick.GridView", + "QtQuick.PathView", + "QtQuick.TextEdit", + "QtQuick.TextInput", + "QtQuick.Controls", + "QtQuick.Controls.BusyIndicator", + "QtQuick.Controls.ButtonGroup", + "QtQuick.Controls.CheckDelegate", + "QtQuick.Controls.ComboBox", + "QtQuick.Controls.Container", + "QtQuick.Controls.DelayButton", + "QtQuick.Controls.Frame", + "QtQuick.Controls.GroupBox", + "QtQuick.Controls.ItemDelegate", + "QtQuick.Controls.Label", + "QtQuick.Controls.Page", + "QtQuick.Controls.PageIndicator", + "QtQuick.Controls.Pane", + "QtQuick.Controls.RadioDelegate", + "QtQuick.Controls.RangeSlider", + "QtQuick.Controls.RoundButton", + "QtQuick.Controls.ScrollView", + "QtQuick.Controls.SpinBox", + "QtQuick.Controls.StackView", + "QtQuick.Controls.SwipeDelegate", + "QtQuick.Controls.SwitchDelegate", + "QtQuick.Controls.TabBar", + "QtQuick.Controls.TabButton", + "QtQuick.Controls.TextArea", + "QtQuick.Controls.TextField", + "QtQuick.Controls.ToolBar", + "QtQuick.Controls.ToolButton", + "QtQuick.Controls.ToolSeparator", + "QtQuick.Controls.Tumbler", + "QtQuick.Shapes.ConicalGradient", + "QtQuick.Shapes.RadialGradient" + ] + + allowedImports: [ + "QtQuick", + "QtQuick.Controls", + "QtQuick.Shapes", + "QtQuick.Timeline", + "QtQuickUltralite.Extras", + "QtQuickUltralite.Layers", + "QtQuickUltralite.Profiling", + "QtQuickUltralite.Studio.Components" + ] + + bannedImports: [ + "FlowView", + "SimulinkConnector" + ] + + //ComplexProperty is not a type, it's just a way to handle bigger props + ComplexProperty { + prefix: "font" + bannedProperties: ["wordSpacing", "letterSpacing", "hintingPreference", + "kerning", "preferShaping", "capitalization", + "strikeout", "underline", "styleName"] + } + + QtQml.Timer { + bannedProperties: ["triggeredOnStart"] + } + + QtQuick.Item { + bannedProperties: ["layer", "opacity", "smooth", "antialiasing", + "baselineOffset", "focus", "activeFocusOnTab", + "rotation", "scale", "transformOrigin"] + } + + QtQuick.Rectangle { + bannedProperties: ["border"] + } + + QtQuick.Flickable { + bannedProperties: ["boundsMovement", "flickDeceleration", + "leftMargin", "rightMargin", "bottomMargin", "topMargin", + "originX", "originY", "pixelAligned", "pressDelay", "synchronousDrag"] + } + + QtQuick.MouseArea { + bannedProperties: ["propagateComposedEvents", "preventStealing", "cursorShape", + "scrollGestureEnabled", "drag", "acceptedButtons", "hoverEnabled"] + } + + QtQuick.Image { + allowChildren: false + allowedProperties: ["rotation", "scale", "transformOrigin"] + bannedProperties: ["mirror", "mipmap", "cache", "autoTransform", "asynchronous", + "sourceSize", "smooth"] + } + + QtQuick.BorderImage { + bannedProperties: ["asynchronous", "cache", "currentFrame", "frameCount", + "horizontalTileMode", "mirror", "progress", "smooth", "sourceSize", + "status", "verticalTileMode"] + } + + QtQuick.Text { + allowChildren: false + allowedProperties: ["rotation", "scale", "transformOrigin"] + bannedProperties: ["lineHeight", "lineHeightMode", "style", + "styleColor", "minimumPointSize", "minimumPixelSize", + "fontSizeMode", "renderType", "renderTypeQuality", "maximumLineCount"] + } + + QtQuick.Loader { + bannedProperties: ["asynchronous", "progress", "status"] + } + + //Padding is not an actual item, but rather set of properties in Text + Padding { + bannedProperties: ["bottomPadding", "topPadding", "leftPadding", "rightPadding"] + } + + QtQuick.Column { + bannedProperties: ["bottomPadding", "leftPadding", "rightPadding", "topPadding"] + } + + QtQuick.Row { + bannedProperties: ["bottomPadding", "leftPadding", "rightPadding", "topPadding", + "effectiveLayoutDirection", "layoutDirection"] + } + + QtQuick.ListView { + bannedProperties: ["cacheBuffer", "highlightRangeMode", "highlightMoveDuration", + "highlightResizeDuration", "preferredHighlightBegin", "layoutDirection", + "preferredHighlightEnd", "highlightFollowsCurrentItem", "keyNavigationWraps", + "snapMode", "highlightMoveVelocity", "highlightResizeVelocity"] + } + + QtQuick.Animation { + bannedProperties: ["paused"] + } + + QtQuick.AnimatedSprite { + allowedProperties: ["currentFrame", "frameCount", "paused"] + bannedProperties: ["finishBehavior", "frameRate", "frameSync", + "frameX", "frameY", "interpolate", "reverse"] + } + + //Quick Controls2 Items and properties: + + QtQuick.Controls.Control { + bannedProperties: ["focusPolicy", "hoverEnabled", "wheelEnabled"] + } + + QtQuick.Controls.AbstractButton { + bannedProperties: ["display", "autoExclusive", "icon"] + } + + QtQuick.Controls.ProgressBar { + bannedProperties: ["indeterminate"] + } + + QtQuick.Controls.Slider { + bannedProperties: ["live", "snapMode", "touchDragThreshold"] + } + + //Path and Shapes related: + + QtQuick.Path { + bannedProperties: ["scale", "pathElements"] + } + + QtQuick.PathArc { + bannedProperties: ["relativeX", "relativeY"] + } + + QtQuick.PathLine { + bannedProperties: ["relativeX", "relativeY"] + } + + QtQuick.PathMove { + bannedProperties: ["relativeX", "relativeY"] + } + + QtQuick.PathQuad { + bannedProperties: ["relativeX", "relativeY", + "relativeControlX", "relativeControlY"] + } + + QtQuick.PathCubic { + bannedProperties: ["relativeX", "relativeY", + "relativeControl1X", "relativeControl1Y", + "relativeControl2X", "relativeControl2Y"] + } + + QtQuick.PathElement { + //nothing + } + + QtQuick.PathSvg { + //nothing + } + + QtQuick.Shapes.Shape { + bannedProperties: ["asynchronous", "containsMode", "data", + "renderType", "status", "vendorExtensionsEnabled"] + } + + QtQuick.Shapes.ShapePath { + bannedProperties: ["dashOffset", "dashPattern", "strokeStyle"] + } + + QtQuickUltralite.Extras.ItemBuffer { + allowedProperties: ["rotation", "scale", "transformOrigin"] + } +} diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/application-mcu/app_mcu.qmlproject.tpl b/share/qtcreator/qmldesigner/studio_templates/projects/application-mcu/app_mcu.qmlproject.tpl index f17d608f96c..7c4453752a6 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/application-mcu/app_mcu.qmlproject.tpl +++ b/share/qtcreator/qmldesigner/studio_templates/projects/application-mcu/app_mcu.qmlproject.tpl @@ -42,7 +42,9 @@ Project { "Controls", "ControlsTemplates", "Timeline", - "Shapes" + "Shapes", + "Profiling", + "StudioComponents" ] } @@ -55,8 +57,8 @@ Project { QDS.qtForMCUs: true QDS.qt6Project: true - QDS.qdsVersion: "4.3" - QDS.quickVersion: "6.5" + QDS.qdsVersion: "4.5" + QDS.quickVersion: "6.7" /* List of plugin directories passed to QML runtime */ importPaths: [ "imports" ] From 09dcb1d7306509f4340e8c8142f03dd9afc041ba Mon Sep 17 00:00:00 2001 From: Aleksei German Date: Fri, 3 May 2024 17:55:21 +0200 Subject: [PATCH 070/154] QmlDesigner: Enable MCU Gradients in ColorEditor Specifics using ColorEditor should be able to use new MCUs-specific property Task-number: QDS-12691 Change-Id: If4d6b1241744ccbc7a9df8b7752ce4ab40502d43 Reviewed-by: Thomas Hartmann Reviewed-by: Qt CI Patch Build Bot --- .../imports/HelperWidgets/ColorEditor.qml | 5 +++++ .../imports/HelperWidgets/ColorEditorPopup.qml | 8 ++++++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ColorEditor.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ColorEditor.qml index 4b4d2b8dc6a..a695d16dd9e 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ColorEditor.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ColorEditor.qml @@ -38,6 +38,10 @@ SecondColumnLayout { property alias showHexTextField: hexTextField.visible property bool shapeGradients: false + + //for now, gradients on MCUs are limited to Basic and Shape Linear Gradient: + property bool mcuGradients: false + property color originalColor property bool isVector3D: false @@ -235,6 +239,7 @@ SecondColumnLayout { sourceComponent: ColorEditorPopup { shapeGradients: colorEditor.shapeGradients + mcuGradients: colorEditor.mcuGradients supportGradient: colorEditor.supportGradient width: popupDialog.contentWidth visible: popupDialog.visible diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ColorEditorPopup.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ColorEditorPopup.qml index 22be367c378..d3ad19ad852 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ColorEditorPopup.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ColorEditorPopup.qml @@ -16,6 +16,10 @@ Column { property bool supportGradient: false property bool shapeGradients: false + + //for now, gradients on MCUs are limited to Basic and Shape Linear Gradient: + property bool mcuGradients: false + property alias gradientLine: gradientLine property alias popupHexTextField: popupHexTextField property alias gradientPropertyName: root.gradientModel.gradientPropertyName @@ -224,12 +228,12 @@ Column { ceMode.items.append({ value: "RadialGradient", text: qsTr("Radial"), - enabled: root.supportGradient && root.shapeGradients + enabled: root.supportGradient && root.shapeGradients && !root.mcuGradients }) ceMode.items.append({ value: "ConicalGradient", text: qsTr("Conical"), - enabled: root.supportGradient && root.shapeGradients + enabled: root.supportGradient && root.shapeGradients && !root.mcuGradients }) } From 61d936af89e6f1f278b49e3cee624765016cad7f Mon Sep 17 00:00:00 2001 From: Teea Poldsam Date: Thu, 2 May 2024 13:10:03 +0300 Subject: [PATCH 071/154] Doc: Update Reacting to External Events Fixes: QDS-11507 Change-Id: Id6ba8895ee4141127feb199c7db05c26dcc9473d Reviewed-by: Johanna Vanhatapio Reviewed-by: Qt CI Patch Build Bot --- .../studio-flow-wildcard-properties.png | Bin 11673 -> 0 bytes .../studio-flow-wildcard-properties.webp | Bin 0 -> 12696 bytes .../images/studio-flow-wildcard.webp | Bin 0 -> 3866 bytes .../src/qtdesignstudio-app-flows.qdoc | 44 ++++++++++-------- 4 files changed, 24 insertions(+), 20 deletions(-) delete mode 100644 doc/qtdesignstudio/images/studio-flow-wildcard-properties.png create mode 100644 doc/qtdesignstudio/images/studio-flow-wildcard-properties.webp create mode 100644 doc/qtdesignstudio/images/studio-flow-wildcard.webp diff --git a/doc/qtdesignstudio/images/studio-flow-wildcard-properties.png b/doc/qtdesignstudio/images/studio-flow-wildcard-properties.png deleted file mode 100644 index 944fd79431d555048cc1295b3df8703bc947a68d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11673 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 N22WQ%mvv4FO#pwyWg!3n diff --git a/doc/qtdesignstudio/images/studio-flow-wildcard-properties.webp b/doc/qtdesignstudio/images/studio-flow-wildcard-properties.webp new file mode 100644 index 0000000000000000000000000000000000000000..66ade4994296673e01db9fa3014c1e97721c23a9 GIT binary patch literal 12696 zcmWIYbaR_v$iNWp>J$(bVBynZ$iSdKi!qiV^x@Wj^*f_2clO^o`2X(jtJCiJ-kPL) zI_b30iD{)er^Q~Mm{y{4n#X;cPUJM*(+bTx(QKaE`qn0UPt)PfiFBW)Q<%!uihR92Ou|}fU&G2G#z-I0)(tGjsM{(>xsFJGEJAiik78ScedpQZhHH_Pg26lHHszBMbA~mC8@^h-!r|;0D%n*FDJa4@ovA$wq6y_ zq%)zNZ=KG)nYlIfNQoV1gKFUPXP=flZxClFc;S3ozNF{Za@(D$y??j5oV{82eV$cQ zyW*;yJ5yZ*7JSeBIDeuVyK~p2S#!V02)3~ccNt|zRvlV0tJGcaK=jhYtmG_Vt_2(p zQ8iI_*5o#^bTLd#cYarJp)6z0HuG(rycJ>bi_UK|5OHO=wkgq~gki(ol=fx2SfdYm zNcesE$DG=7f_LirS?~VIG~Qa5efZ9;D?6FGE=flJS$<;S-@;q2_RsPHM50Yf^11on z_HUjmI{VkJqwk*RvEBc+P+5Y_VbwwdbBj3>GuN!!^zP)ov}Fx{9d9QHocQth$CzVg{yw6M_K(}`W= z=?|H60Z(?6btyTB&$gPsYktzZqL8mY^)EhNmbl~YLJ7IzIVx8!Jh7ePx9lc6kE@`9 z%#Qddd_3);nm%nULDCp;y@FTJK=$uZ4vh7PY)=$_S zCv*B?vV_uc4uy5e2bT3+)L$j=@a-iPn*|zOE!*u*pYguk?&ZE<#jU-!@4PyrbLH(k z?l$Y-7Pf7-wV(GLZ*{gksjcupL#y|l8L#CA@lwo^_z22-y437ZoHGX|6u5RrKxALGrBgJ{``ExHcCu* zZ}}I!^eO)?SbyIet!?pVn{R`M2bMXxoJ5J*^D;^7SfuY}RbQXUOza z`qbNX9Hq|9hi2_--K?;@&L>EuRC(dbcw6Vl&*vvhsJ>eAZsv(ObB=|XN~SsVWiQ{7 zcKy)wot^~>W!D}@Mjy|ZS5(Q-YkWOft8LmvCB{8+JAWrGU%5Vx!>hW=`gdxW{TJa0 zANei)>rFWsHwC^~dcJh!;nROzvb%0DP1-g0_nbJ_+uOA+sC@DbobuPZVwuiy+a(&6 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 YZ+K;M!R6hN=?ynTqh8BI%ywl40K*EI0RR91 literal 0 HcmV?d00001 diff --git a/doc/qtdesignstudio/src/qtdesignstudio-app-flows.qdoc b/doc/qtdesignstudio/src/qtdesignstudio-app-flows.qdoc index f0fc0aa8cc6..d5bf5c812fa 100644 --- a/doc/qtdesignstudio/src/qtdesignstudio-app-flows.qdoc +++ b/doc/qtdesignstudio/src/qtdesignstudio-app-flows.qdoc @@ -256,8 +256,9 @@ \li Drag the action area to the UI control that you want to connect to the other flow item. For example, to a button that opens another flow item when clicked. - \li Double-click the action area and drag the transition line to the - flow item you want to connect to. + \li Double-click the action area and drag the transition line to the flow item you want to + connect to. Alternatively, select the action area, right-click it to open the + context-menu, and then select \uicontrol Connect and the flow item from the list. \li In \l Properties, modify the properties of the action area and transition line. \endlist @@ -646,11 +647,10 @@ time, based on a conditional event. For example, push notifications appear on mobile devices and incoming call screens on a car's HMI. - You can use the \uicontrol {Flow Wildcard} component to model this type of - screens in your \l{Adding Flow Views}{flow view} using real or simulated - \l{Connecting and Releasing Signals}{signals} and \l{Simulating Conditions} - {conditions}. You can add \l{Adding Flow Items}{flow items} to a positive - list to prioritize them or to a negative list to stop some screens from + Use the \uicontrol {Flow Wildcard} component to model this type of + screens in your \l{Adding Flow Views}{flow view} using real or simulated events. + Add \l{Adding Flow Items}{flow items} to an \uicontrol {Allow + list} to prioritize them or to a \uicontrol {Block list} to stop some screens from appearing on others. For example, you could block the incoming call screen from appearing on a warning screen for the engine if you consider the warning more important. @@ -659,42 +659,46 @@ \list 1 \li Drag a \uicontrol {Flow Wildcard} component from - \uicontrol Components > \uicontrol {Flow View} to an - \l{Adding Action Areas and Transitions}{action area} in - the \l Navigator or \l {2D} view. - \li In \l Properties, select flow items to add them to the - positive and negative list of the action area. + \uicontrol Components > \uicontrol {Flow View} to the \l {2D} view. + \li To connect the wildcard to a flow item with a \l{Adding Action Areas and Transitions} + {transition line}, double-click the wildcard and drag the transition line to the flow + item. + \li To add logic to the \uicontrol {Flow Wildcard} component, use + \l{Simulating Events}{events}, \l{Simulating Conditions}{Flow Decision} components, + or \l{Connecting and Releasing Signals}{signals}. + \li To manage the priority of your flow items, add flow items to + \uicontrol {Allow list} or \uicontrol {Block list} in \l Properties. + \image studio-flow-wildcard.webp "The wildcard component in 2D view." \endlist \section1 Flow Wildcard Properties - You can specify basic properties for a \uicontrol {Flow Wildcard} component + Specify basic properties for a \uicontrol {Flow Wildcard} component in the \l Type and \l ID fields in the \uicontrol Component section in the \uicontrol Properties view. Specify properties for flow wildcards in the \uicontrol {Flow Wildcard} section. - \image studio-flow-wildcard-properties.png "Flow Wildcard properties" + \image studio-flow-wildcard-properties.webp "Flow Wildcard properties" In the \uicontrol {Event IDs} field, specify the IDs of the events to connect to, such as mouse, touch or keyboard events. - Select the \uicontrol {Global wildcard} check box to enable triggering + Select the \uicontrol {Global wildcard} checkbox to enable triggering the wildcard from several flows. To give flow items high priority, select them in the \uicontrol {Allow list} field. To block flow items, select them in the \uicontrol {Block list} field. - You can specify the following properties to change the appearance of the - wildcard icon \inlineimage icons/flow-wildcard-icon.png - : + Specify the following properties to change the appearance of the + wildcard icon \inlineimage icons/flow-wildcard-icon.png: \list + \li To set the outline and fill color of the wildcard icon, use the + \l{Picking Colors}{color picker}. \li In the \uicontrol Size field, specify the size of the wildcard icon. \li In the \uicontrol Radius field, specify the radius of the wildcard icon corners. \endlist - You can use the \l{Picking Colors}{color picker} to set the outline and - fill color of the wildcard icon. */ From 4279bdb31cbf0e5925e098d6bed6c959939c7143 Mon Sep 17 00:00:00 2001 From: Tim Jenssen Date: Mon, 6 May 2024 10:12:42 +0200 Subject: [PATCH 072/154] QmlDesigner: fix crash at shutdown Found at sentry https://the-qt-company-00.sentry.io/issues/4729592969 Pick-to: qds/4.5 Change-Id: I3e4903eebf5d328361a04f276659bd143eed0925 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Tim Jenssen --- .../qmldesigner/components/texteditor/texteditorview.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/plugins/qmldesigner/components/texteditor/texteditorview.cpp b/src/plugins/qmldesigner/components/texteditor/texteditorview.cpp index d400251648e..2e52b3358ba 100644 --- a/src/plugins/qmldesigner/components/texteditor/texteditorview.cpp +++ b/src/plugins/qmldesigner/components/texteditor/texteditorview.cpp @@ -96,7 +96,8 @@ void TextEditorView::modelAboutToBeDetached(Model *model) { AbstractView::modelAboutToBeDetached(model); - m_widget->setTextEditor(nullptr); + if (m_widget) + m_widget->setTextEditor(nullptr); // in case the user closed it explicit we do not want to do anything with the editor if (Core::ModeManager::currentModeId() == Core::Constants::MODE_DESIGN) { From 5201e51d9897f2c519d25ac04bc1bb17c50daefb Mon Sep 17 00:00:00 2001 From: Teea Poldsam Date: Mon, 6 May 2024 12:25:59 +0300 Subject: [PATCH 073/154] Doc: Fix language in 3D view doc Fixes: QDS-12701 Change-Id: Iae97930bb671fcc9f9b2a7e1cf8a6e2ab60ee3c1 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Johanna Vanhatapio --- .../src/qtquick3d-editor/qtdesignstudio-3d-editor.qdoc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-editor.qdoc b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-editor.qdoc index 0c87fb499ae..33c5dbfeeef 100644 --- a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-editor.qdoc +++ b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-editor.qdoc @@ -136,8 +136,8 @@ \section2 Using Fly Mode You can move around freely in the 3D scene with fly mode. To navigate the scene with - fly mode, keep the \key {right mouse button} pressed and use the listed keys to move - around the scene. + fly mode, right-click and hold in the \uicontrol 3D view, and use the keyboard shortcuts + to move around the scene: \table \header From 688e697fc05545c4de4fb5abc5a1fe5927bdadc6 Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Fri, 26 Apr 2024 16:41:29 +0300 Subject: [PATCH 074/154] QmlDesigner: Add user 3D bundle to content library Fixes: QDS-12391 Change-Id: Ia078e62274277774949b0fc6a679c17ddbf91968 Reviewed-by: Miikka Heikkinen --- .../ContentLibraryUserView.qml | 9 + .../contentlibrary/contentlibraryeffect.h | 2 + .../contentlibraryusermodel.cpp | 192 ++++++++++++++---- .../contentlibrary/contentlibraryusermodel.h | 35 +++- .../contentlibrary/contentlibraryview.cpp | 14 +- 5 files changed, 202 insertions(+), 50 deletions(-) diff --git a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryUserView.qml b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryUserView.qml index 8528c75e739..8b77c06a77d 100644 --- a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryUserView.qml +++ b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryUserView.qml @@ -133,6 +133,15 @@ HelperWidgets.ScrollView { onShowContextMenu: ctxMenuTexture.popupMenu(modelData) } } + DelegateChoice { + roleValue: "item" + delegate: ContentLibraryEffect { + width: root.cellWidth + height: root.cellHeight + + // onShowContextMenu: ctxMenuTexture.popupMenu(modelData) // TODO + } + } } onCountChanged: root.assignMaxCount() diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffect.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffect.h index cbfbf7ce3e3..a1050b7c67b 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffect.h +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffect.h @@ -19,6 +19,7 @@ class ContentLibraryEffect : public QObject Q_PROPERTY(QStringList bundleItemFiles READ allFiles CONSTANT) Q_PROPERTY(bool bundleItemVisible MEMBER m_visible NOTIFY itemVisibleChanged) Q_PROPERTY(bool bundleItemImported READ imported WRITE setImported NOTIFY itemImportedChanged) + Q_PROPERTY(QString itemType MEMBER m_itemType CONSTANT) public: ContentLibraryEffect(QObject *parent, @@ -56,6 +57,7 @@ private: bool m_imported = false; QStringList m_allFiles; + const QString m_itemType = "item"; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp index c79922caddf..918bccca219 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp @@ -4,6 +4,7 @@ #include "contentlibraryusermodel.h" #include "contentlibrarybundleimporter.h" +#include "contentlibraryeffect.h" #include "contentlibrarymaterial.h" #include "contentlibrarymaterialscategory.h" #include "contentlibrarytexture.h" @@ -27,7 +28,7 @@ ContentLibraryUserModel::ContentLibraryUserModel(ContentLibraryWidget *parent) : QAbstractListModel(parent) , m_widget(parent) { - m_userCategories = {tr("Materials"), tr("Textures")/*, tr("3D"), tr("Effects"), tr("2D components")*/}; // TODO + m_userCategories = {tr("Materials"), tr("Textures"), tr("3D"), /*tr("Effects"), tr("2D components")*/}; // TODO } int ContentLibraryUserModel::rowCount(const QModelIndex &) const @@ -65,7 +66,7 @@ bool ContentLibraryUserModel::isValidIndex(int idx) const return idx > -1 && idx < rowCount(); } -void ContentLibraryUserModel::updateIsEmpty() +void ContentLibraryUserModel::updateIsEmptyMaterials() { bool anyMatVisible = Utils::anyOf(m_userMaterials, [&](ContentLibraryMaterial *mat) { return mat->visible(); @@ -73,16 +74,34 @@ void ContentLibraryUserModel::updateIsEmpty() bool newEmpty = !anyMatVisible || !m_widget->hasMaterialLibrary() || !hasRequiredQuick3DImport(); - if (newEmpty != m_isEmpty) { - m_isEmpty = newEmpty; - emit isEmptyChanged(); + if (newEmpty != m_isEmptyMaterials) { + m_isEmptyMaterials = newEmpty; + emit isEmptyMaterialsChanged(); + } +} + +void ContentLibraryUserModel::updateIsEmpty3D() +{ + bool anyItemVisible = Utils::anyOf(m_user3DItems, [&](ContentLibraryEffect *item) { + return item->visible(); + }); + + bool newEmpty = !anyItemVisible || !m_widget->hasMaterialLibrary() || !hasRequiredQuick3DImport(); + + if (newEmpty != m_isEmpty3D) { + m_isEmpty3D = newEmpty; + emit isEmptyMaterialsChanged(); } } void ContentLibraryUserModel::addMaterial(const QString &name, const QString &qml, const QUrl &icon, const QStringList &files) { - auto libMat = new ContentLibraryMaterial(this, name, qml, qmlToModule(qml), icon, files, + auto compUtils = QmlDesignerPlugin::instance()->documentManager().generatedComponentUtils(); + QString typePrefix = compUtils.userMaterialsBundleType(); + TypeName type = QLatin1String("%1.%2").arg(typePrefix, qml.chopped(4)).toLatin1(); + + auto libMat = new ContentLibraryMaterial(this, name, qml, type, icon, files, Paths::bundlesPathSetting().append("/User/materials")); m_userMaterials.append(libMat); @@ -113,6 +132,18 @@ void ContentLibraryUserModel::addTextures(const QStringList &paths) emit dataChanged(index(texSectionIdx), index(texSectionIdx)); } +void ContentLibraryUserModel::add3DInstance(ContentLibraryEffect *bundleItem) +{ + QString err = m_widget->importer()->importComponent(m_bundlePath3D.path(), bundleItem->type(), + bundleItem->qml(), + bundleItem->files() + m_bundle3DSharedFiles); + + if (err.isEmpty()) + m_widget->setImporterRunning(true); + else + qWarning() << __FUNCTION__ << err; +} + void ContentLibraryUserModel::removeTexture(ContentLibraryTexture *tex) { // remove resources @@ -132,7 +163,7 @@ void ContentLibraryUserModel::removeFromContentLib(ContentLibraryMaterial *mat) { auto bundlePath = Utils::FilePath::fromString(Paths::bundlesPathSetting() + "/User/materials/"); - QJsonObject matsObj = m_bundleObj.value("materials").toObject(); + QJsonObject matsObj = m_bundleObjMaterial.value("materials").toObject(); // remove qml and icon files Utils::FilePath::fromString(mat->qmlFilePath()).removeFile(); @@ -140,9 +171,9 @@ void ContentLibraryUserModel::removeFromContentLib(ContentLibraryMaterial *mat) // remove from the bundle json file matsObj.remove(mat->name()); - m_bundleObj.insert("materials", matsObj); + m_bundleObjMaterial.insert("materials", matsObj); auto result = bundlePath.pathAppended("user_materials_bundle.json") - .writeFileContents(QJsonDocument(m_bundleObj).toJson()); + .writeFileContents(QJsonDocument(m_bundleObjMaterial).toJson()); if (!result) qWarning() << __FUNCTION__ << result.error(); @@ -169,9 +200,9 @@ void ContentLibraryUserModel::removeFromContentLib(ContentLibraryMaterial *mat) // returns unique library material's name and qml component QPair ContentLibraryUserModel::getUniqueLibMaterialNameAndQml(const QString &matName) const { - QTC_ASSERT(!m_bundleObj.isEmpty(), return {}); + QTC_ASSERT(!m_bundleObjMaterial.isEmpty(), return {}); - const QJsonObject matsObj = m_bundleObj.value("materials").toObject(); + const QJsonObject matsObj = m_bundleObjMaterial.value("materials").toObject(); const QStringList matNames = matsObj.keys(); QStringList matQmls; @@ -201,14 +232,6 @@ QPair ContentLibraryUserModel::getUniqueLibMaterialNameAndQml( return {retName, retQml + ".qml"}; } -TypeName ContentLibraryUserModel::qmlToModule(const QString &qmlName) const -{ - return QLatin1String("%1.%2.%3").arg(QmlDesignerPlugin::instance()->documentManager() - .generatedComponentUtils().componentBundlesTypePrefix(), - m_bundleIdMaterial, - qmlName.chopped(4)).toLatin1(); // chopped(4): remove .qml -} - QHash ContentLibraryUserModel::roleNames() const { static const QHash roles { @@ -221,7 +244,14 @@ QHash ContentLibraryUserModel::roleNames() const QJsonObject &ContentLibraryUserModel::bundleJsonObjectRef() { - return m_bundleObj; + return m_bundleObjMaterial; +} + +void ContentLibraryUserModel::loadBundles() +{ + loadMaterialBundle(); + load3DBundle(); + loadTextureBundle(); } void ContentLibraryUserModel::loadMaterialBundle() @@ -235,8 +265,8 @@ void ContentLibraryUserModel::loadMaterialBundle() qDeleteAll(m_userMaterials); m_userMaterials.clear(); m_matBundleExists = false; - m_isEmpty = true; - m_bundleObj = {}; + m_isEmptyMaterials = true; + m_bundleObjMaterial = {}; m_bundleIdMaterial.clear(); int matSectionIdx = 0; @@ -256,24 +286,26 @@ void ContentLibraryUserModel::loadMaterialBundle() QFile jsonFile(jsonFilePath.path()); if (!jsonFile.open(QIODevice::ReadOnly)) { - qWarning("Couldn't open user_materials_bundle.json"); + qWarning() << __FUNCTION__ << "Couldn't open user_materials_bundle.json"; emit dataChanged(index(matSectionIdx), index(matSectionIdx)); return; } QJsonDocument matBundleJsonDoc = QJsonDocument::fromJson(jsonFile.readAll()); if (matBundleJsonDoc.isNull()) { - qWarning("Invalid user_materials_bundle.json file"); + qWarning() << __FUNCTION__ << "Invalid user_materials_bundle.json file"; emit dataChanged(index(matSectionIdx), index(matSectionIdx)); return; } - m_bundleObj = matBundleJsonDoc.object(); - m_bundleObj["id"] = compUtils.userMaterialsBundleId(); + m_bundleObjMaterial = matBundleJsonDoc.object(); + m_bundleObjMaterial["id"] = compUtils.userMaterialsBundleId(); + m_bundleIdMaterial = compUtils.userMaterialsBundleId(); // parse materials - const QJsonObject matsObj = m_bundleObj.value("materials").toObject(); + const QJsonObject matsObj = m_bundleObjMaterial.value("materials").toObject(); const QStringList materialNames = matsObj.keys(); + QString typePrefix = compUtils.userMaterialsBundleType(); for (const QString &matName : materialNames) { const QJsonObject matObj = matsObj.value(matName).toObject(); @@ -284,8 +316,7 @@ void ContentLibraryUserModel::loadMaterialBundle() QUrl icon = QUrl::fromLocalFile(bundleDir.filePath(matObj.value("icon").toString())); QString qml = matObj.value("qml").toString(); - - TypeName type = qmlToModule(qml); + TypeName type = QLatin1String("%1.%2").arg(typePrefix, qml.chopped(4)).toLatin1(); auto userMat = new ContentLibraryMaterial(this, matName, qml, type, icon, files, bundleDir.path(), ""); @@ -293,14 +324,103 @@ void ContentLibraryUserModel::loadMaterialBundle() m_userMaterials.append(userMat); } - m_bundleSharedFiles.clear(); - const QJsonArray sharedFilesArr = m_bundleObj.value("sharedFiles").toArray(); + m_bundleMaterialSharedFiles.clear(); + const QJsonArray sharedFilesArr = m_bundleObjMaterial.value("sharedFiles").toArray(); for (const QJsonValueConstRef &file : sharedFilesArr) - m_bundleSharedFiles.append(file.toString()); + m_bundleMaterialSharedFiles.append(file.toString()); m_matBundleExists = true; emit matBundleExistsChanged(); emit dataChanged(index(matSectionIdx), index(matSectionIdx)); + + m_matBundleExists = true; + updateIsEmptyMaterials(); + resetModel(); +} + +void ContentLibraryUserModel::load3DBundle() +{ + auto compUtils = QmlDesignerPlugin::instance()->documentManager().generatedComponentUtils(); + + if (m_bundle3DExists && m_bundleId3D == compUtils.user3DBundleId()) + return; + + // clean up + qDeleteAll(m_user3DItems); + m_user3DItems.clear(); + m_bundle3DExists = false; + m_isEmpty3D = true; + m_bundleObj3D = {}; + m_bundleId3D.clear(); + + int section3DIdx = 2; + + m_bundlePath3D = Utils::FilePath::fromString(Paths::bundlesPathSetting() + "/User/3d"); + m_bundlePath3D.createDir(); + + auto jsonFilePath = m_bundlePath3D.pathAppended("user_3d_bundle.json"); + + if (!jsonFilePath.exists()) { + QByteArray jsonContent = "{\n"; + jsonContent += " \"id\": \"User3D\",\n"; + jsonContent += " \"items\": {\n"; + jsonContent += " }\n"; + jsonContent += "}"; + Utils::expected_str res = jsonFilePath.writeFileContents(jsonContent); + if (!res.has_value()) { + qWarning() << __FUNCTION__ << res.error(); + emit dataChanged(index(section3DIdx), index(section3DIdx)); + return; + } + } + + Utils::expected_str jsonContents = jsonFilePath.fileContents(); + if (!jsonContents.has_value()) { + qWarning() << __FUNCTION__ << jsonContents.error(); + emit dataChanged(index(section3DIdx), index(section3DIdx)); + return; + } + + QJsonDocument bundleJsonDoc = QJsonDocument::fromJson(jsonContents.value()); + if (bundleJsonDoc.isNull()) { + qWarning() << __FUNCTION__ << "Invalid user_3d_bundle.json file"; + emit dataChanged(index(section3DIdx), index(section3DIdx)); + return; + } + + m_bundleId3D = compUtils.user3DBundleId(); + m_bundleObj3D = bundleJsonDoc.object(); + m_bundleObj3D["id"] = m_bundleId3D; + + // parse 3d items + const QJsonObject itemsObj = m_bundleObj3D.value("items").toObject(); + const QStringList itemNames = itemsObj.keys(); + QString typePrefix = compUtils.user3DBundleType(); + for (const QString &itemName : itemNames) { + const QJsonObject itemObj = itemsObj.value(itemName).toObject(); + + QStringList files; + const QJsonArray assetsArr = itemObj.value("files").toArray(); + for (const QJsonValueConstRef &asset : assetsArr) + files.append(asset.toString()); + + QUrl icon = m_bundlePath3D.pathAppended(itemObj.value("icon").toString()).toUrl(); + QString qml = itemObj.value("qml").toString(); + TypeName type = QLatin1String("%1.%2").arg(typePrefix, qml.chopped(4)).toLatin1(); + + auto bundleItem = new ContentLibraryEffect(nullptr, itemName, qml, type, icon, files); + + m_user3DItems.append(bundleItem); + } + + m_bundle3DSharedFiles.clear(); + const QJsonArray sharedFilesArr = m_bundleObj3D.value("sharedFiles").toArray(); + for (const QJsonValueConstRef &file : sharedFilesArr) + m_bundle3DSharedFiles.append(file.toString()); + + m_bundle3DExists = true; + updateIsEmpty3D(); + emit dataChanged(index(section3DIdx), index(section3DIdx)); } void ContentLibraryUserModel::loadTextureBundle() @@ -351,7 +471,8 @@ void ContentLibraryUserModel::setSearchText(const QString &searchText) for (ContentLibraryMaterial *mat : std::as_const(m_userMaterials)) mat->filter(m_searchText); - updateIsEmpty(); + updateIsEmptyMaterials(); + updateIsEmpty3D(); } void ContentLibraryUserModel::updateImportedState(const QStringList &importedItems) @@ -380,7 +501,8 @@ void ContentLibraryUserModel::setQuick3DImportVersion(int major, int minor) emit hasRequiredQuick3DImportChanged(); - updateIsEmpty(); + updateIsEmptyMaterials(); + updateIsEmpty3D(); } void ContentLibraryUserModel::resetModel() @@ -397,7 +519,7 @@ void ContentLibraryUserModel::applyToSelected(ContentLibraryMaterial *mat, bool void ContentLibraryUserModel::addToProject(ContentLibraryMaterial *mat) { QString err = m_widget->importer()->importComponent(mat->dirPath(), mat->type(), mat->qml(), - mat->files() + m_bundleSharedFiles); + mat->files() + m_bundleMaterialSharedFiles); if (err.isEmpty()) m_widget->setImporterRunning(true); diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.h index b11cd343c88..79889135c6e 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.h +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.h @@ -3,7 +3,7 @@ #pragma once -#include "modelfwd.h" +#include #include #include @@ -23,7 +23,9 @@ class ContentLibraryUserModel : public QAbstractListModel Q_OBJECT Q_PROPERTY(bool matBundleExists READ matBundleExists NOTIFY matBundleExistsChanged) - Q_PROPERTY(bool isEmpty MEMBER m_isEmpty NOTIFY isEmptyChanged) + Q_PROPERTY(bool bundle3DExists MEMBER m_bundle3DExists NOTIFY bundle3DExistsChanged) + Q_PROPERTY(bool isEmptyMaterials MEMBER m_isEmptyMaterials NOTIFY isEmptyMaterialsChanged) + Q_PROPERTY(bool isEmpty3D MEMBER m_isEmpty3D NOTIFY isEmpty3DChanged) Q_PROPERTY(bool hasRequiredQuick3DImport READ hasRequiredQuick3DImport NOTIFY hasRequiredQuick3DImportChanged) Q_PROPERTY(bool hasModelSelection READ hasModelSelection NOTIFY hasModelSelectionChanged) Q_PROPERTY(QList userMaterials MEMBER m_userMaterials NOTIFY userMaterialsChanged) @@ -42,7 +44,6 @@ public: void updateImportedState(const QStringList &importedItems); QPair getUniqueLibMaterialNameAndQml(const QString &matName) const; - TypeName qmlToModule(const QString &qmlName) const; void setQuick3DImportVersion(int major, int minor); @@ -54,16 +55,18 @@ public: void setHasModelSelection(bool b); void resetModel(); - void updateIsEmpty(); + void updateIsEmptyMaterials(); + void updateIsEmpty3D(); void addMaterial(const QString &name, const QString &qml, const QUrl &icon, const QStringList &files); void addTextures(const QStringList &paths); + void add3DInstance(ContentLibraryEffect *bundleItem); + void setBundleObj(const QJsonObject &newBundleObj); QJsonObject &bundleJsonObjectRef(); - void loadMaterialBundle(); - void loadTextureBundle(); + void loadBundles(); Q_INVOKABLE void applyToSelected(QmlDesigner::ContentLibraryMaterial *mat, bool add = false); Q_INVOKABLE void addToProject(QmlDesigner::ContentLibraryMaterial *mat); @@ -72,7 +75,8 @@ public: Q_INVOKABLE void removeFromContentLib(QmlDesigner::ContentLibraryMaterial *mat); signals: - void isEmptyChanged(); + void isEmptyMaterialsChanged(); + void isEmpty3DChanged(); void hasRequiredQuick3DImportChanged(); void hasModelSelectionChanged(); void userMaterialsChanged(); @@ -84,14 +88,21 @@ signals: void matBundleExistsChanged(); + void bundle3DExistsChanged(); private: + void loadMaterialBundle(); + void load3DBundle(); + void loadTextureBundle(); bool isValidIndex(int idx) const; ContentLibraryWidget *m_widget = nullptr; QString m_searchText; QString m_bundleIdMaterial; - QStringList m_bundleSharedFiles; + QString m_bundleId3D; + QStringList m_bundleMaterialSharedFiles; + QStringList m_bundle3DSharedFiles; + Utils::FilePath m_bundlePath3D; QList m_userMaterials; QList m_userTextures; @@ -99,16 +110,18 @@ private: QList m_user3DItems; QStringList m_userCategories; - QJsonObject m_bundleObj; + QJsonObject m_bundleObjMaterial; + QJsonObject m_bundleObj3D; - bool m_isEmpty = true; + bool m_isEmptyMaterials = true; + bool m_isEmpty3D = true; bool m_matBundleExists = false; + bool m_bundle3DExists = false; bool m_hasModelSelection = false; int m_quick3dMajorVersion = -1; int m_quick3dMinorVersion = -1; - enum Roles { NameRole = Qt::UserRole + 1, VisibleRole, ItemsRole }; }; diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp index 49b1a7c6a02..798ad840358 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp @@ -230,7 +230,8 @@ bool ContentLibraryView::isMaterialBundle(const QString &bundleId) const bool ContentLibraryView::isEffectBundle(const QString &bundleId) const { auto compUtils = QmlDesignerPlugin::instance()->documentManager().generatedComponentUtils(); - return bundleId == compUtils.effectsBundleId() || bundleId == compUtils.userEffectsBundleId(); + return bundleId == compUtils.effectsBundleId() || bundleId == compUtils.userEffectsBundleId() + || bundleId == compUtils.user3DBundleId(); } void ContentLibraryView::modelAttached(Model *model) @@ -255,8 +256,7 @@ void ContentLibraryView::modelAttached(Model *model) // cause bundle items types to resolve incorrectly m_widget->materialsModel()->loadBundle(); m_widget->effectsModel()->loadBundle(); - m_widget->userModel()->loadMaterialBundle(); - m_widget->userModel()->loadTextureBundle(); + m_widget->userModel()->loadBundles(); auto compUtils = QmlDesignerPlugin::instance()->documentManager().generatedComponentUtils(); m_widget->updateImportedState(compUtils.materialsBundleId()); @@ -348,8 +348,14 @@ void ContentLibraryView::customNotification(const AbstractView *view, } else if (identifier == "drop_bundle_effect") { QTC_ASSERT(nodeList.size() == 1, return); + auto compUtils = QmlDesignerPlugin::instance()->documentManager().generatedComponentUtils(); + bool is3D = m_draggedBundleEffect->type().startsWith(compUtils.user3DBundleType().toLatin1()); + m_bundleEffectPos = data.size() == 1 ? data.first() : QVariant(); - m_widget->effectsModel()->addInstance(m_draggedBundleEffect); + if (is3D) + m_widget->userModel()->add3DInstance(m_draggedBundleEffect); + else + m_widget->effectsModel()->addInstance(m_draggedBundleEffect); m_bundleEffectTarget = nodeList.first() ? nodeList.first() : Utils3D::active3DSceneNode(this); } else if (identifier == "add_material_to_content_lib") { QTC_ASSERT(nodeList.size() == 1 && data.size() == 1, return); From 4b38f462f3fd95fe03649c19229bb97ae1bace73 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Thu, 2 May 2024 19:47:34 +0200 Subject: [PATCH 075/154] QmlDesigner: Cleanup QScopedPointer std::unique_ptr is a clear super set of QScopedPointer with the same behavoir. There is Utils::UniqueObjectPtr too which prevents dangling pointer if the parent is set. Change-Id: I16c88f51b69f005445a079be494b44506271e53b Reviewed-by: Mahmoud Badri Reviewed-by: Reviewed-by: Tim Jenssen Reviewed-by: Qt CI Patch Build Bot --- .../assetslibrary/assetslibrarywidget.cpp | 4 +-- .../assetslibrary/assetslibrarywidget.h | 4 ++- .../collectioneditor/collectionwidget.cpp | 4 +-- .../collectioneditor/collectionwidget.h | 4 ++- .../collectioneditor/datastoremodelnode.cpp | 13 ++++---- .../componentcore/abstractaction.cpp | 4 +-- .../components/componentcore/abstractaction.h | 5 +-- .../componentcore/abstractactiongroup.cpp | 10 +++--- .../componentcore/abstractactiongroup.h | 5 +-- .../componentcore/designeractionmanager.cpp | 3 +- .../componentcore/designeractionmanager.h | 2 +- .../contentlibrary/contentlibrarywidget.cpp | 4 +-- .../contentlibrary/contentlibrarywidget.h | 6 +++- .../components/formeditor/dragtool.h | 1 - .../components/integration/designdocument.cpp | 20 +++++++---- .../components/integration/designdocument.h | 11 ++++--- .../integration/designdocumentview.cpp | 10 +++--- .../materialbrowser/materialbrowserwidget.cpp | 6 ++-- .../materialbrowser/materialbrowserwidget.h | 4 ++- .../materialeditorqmlbackend.cpp | 19 +++++------ .../materialeditor/materialeditorqmlbackend.h | 6 ++-- .../propertyeditorqmlbackend.cpp | 33 ++++++++----------- .../propertyeditor/propertyeditorqmlbackend.h | 8 +++-- .../propertyeditor/propertyeditorvalue.cpp | 1 - .../propertyeditor/propertyeditorview.cpp | 1 - .../texttool/textedititemwidget.cpp | 20 +++++------ .../components/texttool/textedititemwidget.h | 7 ++-- .../textureeditor/textureeditorqmlbackend.cpp | 24 +++++++------- .../textureeditor/textureeditorqmlbackend.h | 8 +++-- .../textureeditor/textureeditorview.cpp | 1 - .../timelineeditor/timelinepropertyitem.cpp | 1 - .../designercore/include/rewriterview.h | 8 ++--- .../instances/nodeinstanceview.cpp | 1 - .../designercore/model/qmlvisualnode.cpp | 10 +++--- .../designercore/model/rewriterview.cpp | 20 +++++------ .../designercore/model/stylesheetmerger.cpp | 16 +++++---- src/plugins/qmldesigner/designmodewidget.h | 1 - 37 files changed, 161 insertions(+), 144 deletions(-) diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp index 4b270c8902d..5b6a2d76127 100644 --- a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp +++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp @@ -94,7 +94,7 @@ AssetsLibraryWidget::AssetsLibraryWidget(AsynchronousImageCache &asynchronousFon , m_assetsModel{new AssetsLibraryModel(this)} , m_assetsView{view} , m_createTextures{view} - , m_assetsWidget{new StudioQuickWidget(this)} + , m_assetsWidget{Utils::makeUniqueObjectPtr(this)} { setWindowTitle(tr("Assets Library", "Title of assets library widget")); setMinimumWidth(250); @@ -130,7 +130,7 @@ AssetsLibraryWidget::AssetsLibraryWidget(AsynchronousImageCache &asynchronousFon auto layout = new QVBoxLayout(this); layout->setContentsMargins({}); layout->setSpacing(0); - layout->addWidget(m_assetsWidget.data()); + layout->addWidget(m_assetsWidget.get()); updateSearch(); diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.h b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.h index 8b59ae07859..9e97080d4ac 100644 --- a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.h +++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.h @@ -8,6 +8,8 @@ #include +#include + #include #include #include @@ -137,7 +139,7 @@ private: AssetsLibraryView *m_assetsView = nullptr; CreateTextures m_createTextures = nullptr; - QScopedPointer m_assetsWidget; + Utils::UniqueObjectPtr m_assetsWidget; std::unique_ptr m_fontPreviewTooltipBackend; QShortcut *m_qmlSourceUpdateShortcut = nullptr; diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp index 137486802aa..7a90264145c 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp @@ -59,7 +59,7 @@ CollectionWidget::CollectionWidget(CollectionView *view) , m_listModel(new CollectionListModel) , m_collectionDetailsModel(new CollectionDetailsModel) , m_collectionDetailsSortFilterModel(std::make_unique()) - , m_quickWidget(new StudioQuickWidget(this)) + , m_quickWidget(Utils::makeUniqueObjectPtr(this)) { setWindowTitle(tr("Model Editor", "Title of model editor widget")); @@ -84,7 +84,7 @@ CollectionWidget::CollectionWidget(CollectionView *view) auto layout = new QVBoxLayout(this); layout->setContentsMargins({}); layout->setSpacing(0); - layout->addWidget(m_quickWidget.data()); + layout->addWidget(m_quickWidget.get()); qmlRegisterAnonymousType("CollectionEditorBackend", 1); auto map = m_quickWidget->registerPropertyMap("CollectionEditorBackend"); diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.h b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.h index 56f5ff30edc..f62d4664720 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.h +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.h @@ -7,6 +7,8 @@ #include +#include + class StudioQuickWidget; namespace QmlDesigner { @@ -73,7 +75,7 @@ private: QPointer m_collectionDetailsModel; QPointer m_iContext; std::unique_ptr m_collectionDetailsSortFilterModel; - QScopedPointer m_quickWidget; + Utils::UniqueObjectPtr m_quickWidget; bool m_targetNodeSelected = false; bool m_projectImportExists = false; bool m_dataStoreExists = false; diff --git a/src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.cpp b/src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.cpp index 5be9c20f9ed..d8b80061f32 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.cpp @@ -31,7 +31,8 @@ #include #include #include -#include + +#include namespace { @@ -104,14 +105,14 @@ void setQmlContextToModel(QmlDesigner::Model *model, const QString &qmlContext) using namespace QmlDesigner; Q_ASSERT(model); - QScopedPointer textEdit(new QPlainTextEdit); - QScopedPointer modifier( - new NotIndentingTextEditModifier(textEdit.data())); + std::unique_ptr textEdit = std::make_unique(); + std::unique_ptr modifier = std::make_unique( + textEdit.get()); textEdit->hide(); textEdit->setPlainText(qmlContext); QmlDesigner::ExternalDependencies externalDependencies{QmlDesignerBasePlugin::settings()}; - QScopedPointer rewriter( - new RewriterView(externalDependencies, QmlDesigner::RewriterView::Validate)); + std::unique_ptr rewriter = std::make_unique( + externalDependencies, QmlDesigner::RewriterView::Validate); rewriter->setParent(model); rewriter->setTextModifier(modifier.get()); diff --git a/src/plugins/qmldesigner/components/componentcore/abstractaction.cpp b/src/plugins/qmldesigner/components/componentcore/abstractaction.cpp index 559e8ea69c6..3379d998349 100644 --- a/src/plugins/qmldesigner/components/componentcore/abstractaction.cpp +++ b/src/plugins/qmldesigner/components/componentcore/abstractaction.cpp @@ -8,7 +8,7 @@ namespace QmlDesigner { AbstractAction::AbstractAction(const QString &description) - : m_pureAction(new DefaultAction(description)) + : m_pureAction(std::make_unique(description)) { const Utils::Icon defaultIcon({ {":/utils/images/select.png", Utils::Theme::QmlDesigner_FormEditorForegroundColor}}, Utils::Icon::MenuTintedStyle); @@ -56,7 +56,7 @@ void AbstractAction::setCheckable(bool checkable) PureActionInterface *AbstractAction::pureAction() const { - return m_pureAction.data(); + return m_pureAction.get(); } SelectionContext AbstractAction::selectionContext() const diff --git a/src/plugins/qmldesigner/components/componentcore/abstractaction.h b/src/plugins/qmldesigner/components/componentcore/abstractaction.h index ca4cc582ce9..53b540cc7a3 100644 --- a/src/plugins/qmldesigner/components/componentcore/abstractaction.h +++ b/src/plugins/qmldesigner/components/componentcore/abstractaction.h @@ -6,7 +6,8 @@ #include "actioninterface.h" #include -#include + +#include namespace QmlDesigner { @@ -58,7 +59,7 @@ protected: SelectionContext selectionContext() const; private: - QScopedPointer m_pureAction; + std::unique_ptr m_pureAction; SelectionContext m_selectionContext; }; diff --git a/src/plugins/qmldesigner/components/componentcore/abstractactiongroup.cpp b/src/plugins/qmldesigner/components/componentcore/abstractactiongroup.cpp index 288b8e409de..5b340343e7d 100644 --- a/src/plugins/qmldesigner/components/componentcore/abstractactiongroup.cpp +++ b/src/plugins/qmldesigner/components/componentcore/abstractactiongroup.cpp @@ -8,14 +8,14 @@ namespace QmlDesigner { -AbstractActionGroup::AbstractActionGroup(const QString &displayName) : - m_displayName(displayName), - m_menu(new QmlEditorMenu) +AbstractActionGroup::AbstractActionGroup(const QString &displayName) + : m_displayName(displayName) + , m_menu(Utils::makeUniqueObjectPtr()) { m_menu->setTitle(displayName); m_action = m_menu->menuAction(); - QmlEditorMenu *qmlEditorMenu = qobject_cast(m_menu.data()); + QmlEditorMenu *qmlEditorMenu = qobject_cast(m_menu.get()); if (qmlEditorMenu) qmlEditorMenu->setIconsVisible(false); } @@ -32,7 +32,7 @@ QAction *AbstractActionGroup::action() const QMenu *AbstractActionGroup::menu() const { - return m_menu.data(); + return m_menu.get(); } SelectionContext AbstractActionGroup::selectionContext() const diff --git a/src/plugins/qmldesigner/components/componentcore/abstractactiongroup.h b/src/plugins/qmldesigner/components/componentcore/abstractactiongroup.h index dd89849ecfa..f239eeab3de 100644 --- a/src/plugins/qmldesigner/components/componentcore/abstractactiongroup.h +++ b/src/plugins/qmldesigner/components/componentcore/abstractactiongroup.h @@ -5,9 +5,10 @@ #include "actioninterface.h" +#include + #include #include -#include namespace QmlDesigner { @@ -29,7 +30,7 @@ public: private: const QString m_displayName; SelectionContext m_selectionContext; - QScopedPointer m_menu; + Utils::UniqueObjectPtr m_menu; QAction *m_action; }; diff --git a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp index 0722d2d5f81..fd2321f82a3 100644 --- a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp +++ b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp @@ -2193,7 +2193,8 @@ void DesignerActionManager::addCustomTransitionEffectAction() void DesignerActionManager::setupIcons() { - m_designerIcons.reset(new DesignerIcons("qtds_propertyIconFont.ttf", designerIconResourcesPath())); + m_designerIcons = std::make_unique("qtds_propertyIconFont.ttf", + designerIconResourcesPath()); } QString DesignerActionManager::designerIconResourcesPath() const diff --git a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.h b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.h index 16d6219cd69..89505fcbe85 100644 --- a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.h +++ b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.h @@ -138,7 +138,7 @@ private: QList m_addResourceHandler; QList m_modelNodePreviewImageHandlers; ExternalDependenciesInterface &m_externalDependencies; - QScopedPointer m_designerIcons; + std::unique_ptr m_designerIcons; QList m_callBacks; }; diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.cpp index 0c680afa76f..96db7d7110d 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.cpp @@ -123,7 +123,7 @@ bool ContentLibraryWidget::eventFilter(QObject *obj, QEvent *event) } ContentLibraryWidget::ContentLibraryWidget() - : m_quickWidget(new StudioQuickWidget(this)) + : m_quickWidget(Utils::makeUniqueObjectPtr(this)) , m_materialsModel(new ContentLibraryMaterialsModel(this)) , m_texturesModel(new ContentLibraryTexturesModel("Textures", this)) , m_environmentsModel(new ContentLibraryTexturesModel("Environments", this)) @@ -156,7 +156,7 @@ ContentLibraryWidget::ContentLibraryWidget() auto layout = new QVBoxLayout(this); layout->setContentsMargins({}); layout->setSpacing(0); - layout->addWidget(m_quickWidget.data()); + layout->addWidget(m_quickWidget.get()); updateSearch(); diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.h index 75339f02491..6f4cc300c91 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.h +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.h @@ -4,7 +4,11 @@ #pragma once #include "createtexture.h" + #include + +#include + #include #include @@ -122,7 +126,7 @@ private: void populateTextureBundleModels(); void createImporter(); - QScopedPointer m_quickWidget; + Utils::UniqueObjectPtr m_quickWidget; QPointer m_materialsModel; QPointer m_texturesModel; QPointer m_environmentsModel; diff --git a/src/plugins/qmldesigner/components/formeditor/dragtool.h b/src/plugins/qmldesigner/components/formeditor/dragtool.h index 1cd2c9f4873..c1d5626d28d 100644 --- a/src/plugins/qmldesigner/components/formeditor/dragtool.h +++ b/src/plugins/qmldesigner/components/formeditor/dragtool.h @@ -7,7 +7,6 @@ #include "selectionindicator.h" #include -#include #include namespace QmlDesigner { diff --git a/src/plugins/qmldesigner/components/integration/designdocument.cpp b/src/plugins/qmldesigner/components/integration/designdocument.cpp index 804ac076e62..0b175916c9f 100644 --- a/src/plugins/qmldesigner/components/integration/designdocument.cpp +++ b/src/plugins/qmldesigner/components/integration/designdocument.cpp @@ -150,7 +150,10 @@ bool DesignDocument::loadInFileComponent(const ModelNode &componentNode) if (!componentNode.isRootNode()) { //change to subcomponent model - changeToInFileComponentModel(createComponentTextModifier(m_documentTextModifier.data(), rewriterView(), componentText, componentNode)); + changeToInFileComponentModel(createComponentTextModifier(m_documentTextModifier.get(), + rewriterView(), + componentText, + componentNode)); } return true; @@ -377,9 +380,12 @@ void DesignDocument::loadDocument(QPlainTextEdit *edit) m_documentTextModifier.reset(new BaseTextEditModifier(qobject_cast(plainTextEdit()))); - connect(m_documentTextModifier.data(), &TextModifier::textChanged, this, &DesignDocument::updateQrcFiles); + connect(m_documentTextModifier.get(), + &TextModifier::textChanged, + this, + &DesignDocument::updateQrcFiles); - m_rewriterView->setTextModifier(m_documentTextModifier.data()); + m_rewriterView->setTextModifier(m_documentTextModifier.get()); m_inFileComponentTextModifier.reset(); @@ -399,7 +405,7 @@ void DesignDocument::changeToDocumentModel() if (edit) edit->document()->clearUndoRedoStacks(); - m_rewriterView->setTextModifier(m_documentTextModifier.data()); + m_rewriterView->setTextModifier(m_documentTextModifier.get()); m_inFileComponentModel.reset(); m_inFileComponentTextModifier.reset(); @@ -432,7 +438,7 @@ bool DesignDocument::hasProject() const void DesignDocument::setModified() { - if (!m_documentTextModifier.isNull()) + if (m_documentTextModifier) m_documentTextModifier->textDocument()->setModified(true); } @@ -448,7 +454,7 @@ void DesignDocument::changeToInFileComponentModel(ComponentTextModifier *textMod m_inFileComponentModel = createInFileComponentModel(); - m_rewriterView->setTextModifier(m_inFileComponentTextModifier.data()); + m_rewriterView->setTextModifier(m_inFileComponentTextModifier.get()); viewManager().attachRewriterView(); viewManager().attachViewsExceptRewriterAndComponetView(); @@ -675,7 +681,7 @@ void DesignDocument::selectAll() RewriterView *DesignDocument::rewriterView() const { - return m_rewriterView.data(); + return m_rewriterView.get(); } void DesignDocument::setEditor(Core::IEditor *editor) diff --git a/src/plugins/qmldesigner/components/integration/designdocument.h b/src/plugins/qmldesigner/components/integration/designdocument.h index 52089d67c1b..1f67ff4b30b 100644 --- a/src/plugins/qmldesigner/components/integration/designdocument.h +++ b/src/plugins/qmldesigner/components/integration/designdocument.h @@ -16,9 +16,10 @@ #include #include - #include +#include + QT_BEGIN_NAMESPACE class QPlainTextEdit; QT_END_NAMESPACE @@ -143,12 +144,12 @@ private: // variables ModelPointer m_documentModel; ModelPointer m_inFileComponentModel; QPointer m_textEditor; - QScopedPointer m_documentTextModifier; - QScopedPointer m_inFileComponentTextModifier; + std::unique_ptr m_documentTextModifier; + std::unique_ptr m_inFileComponentTextModifier; #ifndef QDS_USE_PROJECTSTORAGE - QScopedPointer m_subComponentManager; + std::unique_ptr m_subComponentManager; #endif - QScopedPointer m_rewriterView; + std::unique_ptr m_rewriterView; bool m_documentLoaded; ProjectExplorer::Target *m_currentTarget; ProjectStorageDependencies m_projectStorageDependencies; diff --git a/src/plugins/qmldesigner/components/integration/designdocumentview.cpp b/src/plugins/qmldesigner/components/integration/designdocumentview.cpp index 6ef95bf4c45..d97b9ff06f9 100644 --- a/src/plugins/qmldesigner/components/integration/designdocumentview.cpp +++ b/src/plugins/qmldesigner/components/integration/designdocumentview.cpp @@ -23,6 +23,8 @@ #include #include +#include + namespace QmlDesigner { DesignDocumentView::DesignDocumentView(ExternalDependenciesInterface &externalDependencies) @@ -116,14 +118,14 @@ QString DesignDocumentView::toText() const textEdit.setPlainText(imports + QStringLiteral("Item {\n}\n")); NotIndentingTextEditModifier modifier(&textEdit); - QScopedPointer rewriterView( - new RewriterView(externalDependencies(), RewriterView::Amend)); + std::unique_ptr rewriterView = std::make_unique(externalDependencies(), + RewriterView::Amend); rewriterView->setCheckSemanticErrors(false); rewriterView->setPossibleImportsEnabled(false); rewriterView->setTextModifier(&modifier); - outputModel->setRewriterView(rewriterView.data()); + outputModel->setRewriterView(rewriterView.get()); - ModelMerger merger(rewriterView.data()); + ModelMerger merger(rewriterView.get()); merger.replaceModel(rootModelNode()); diff --git a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.cpp b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.cpp index 7cf0a875bcf..42268ee6fd1 100644 --- a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.cpp +++ b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.cpp @@ -150,7 +150,7 @@ MaterialBrowserWidget::MaterialBrowserWidget(AsynchronousImageCache &imageCache, : m_materialBrowserView(view) , m_materialBrowserModel(new MaterialBrowserModel(view, this)) , m_materialBrowserTexturesModel(new MaterialBrowserTexturesModel(view, this)) - , m_quickWidget(new StudioQuickWidget(this)) + , m_quickWidget(Utils::makeUniqueObjectPtr(this)) , m_previewImageProvider(new PreviewImageProvider()) { QImage defaultImage; @@ -179,7 +179,7 @@ MaterialBrowserWidget::MaterialBrowserWidget(AsynchronousImageCache &imageCache, auto layout = new QVBoxLayout(this); layout->setContentsMargins({}); layout->setSpacing(0); - layout->addWidget(m_quickWidget.data()); + layout->addWidget(m_quickWidget.get()); updateSearch(); @@ -411,7 +411,7 @@ void MaterialBrowserWidget::setIsDragging(bool val) StudioQuickWidget *MaterialBrowserWidget::quickWidget() const { - return m_quickWidget.data(); + return m_quickWidget.get(); } void MaterialBrowserWidget::clearPreviewCache() diff --git a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.h b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.h index 97c994e5652..c55d72c8f73 100644 --- a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.h +++ b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.h @@ -7,6 +7,8 @@ #include +#include + #include QT_BEGIN_NAMESPACE @@ -85,7 +87,7 @@ private: QPointer m_materialBrowserView; QPointer m_materialBrowserModel; QPointer m_materialBrowserTexturesModel; - QScopedPointer m_quickWidget; + Utils::UniqueObjectPtr m_quickWidget; QShortcut *m_qmlSourceUpdateShortcut = nullptr; PreviewImageProvider *m_previewImageProvider = nullptr; diff --git a/src/plugins/qmldesigner/components/materialeditor/materialeditorqmlbackend.cpp b/src/plugins/qmldesigner/components/materialeditor/materialeditorqmlbackend.cpp index ecc460ae510..0e508f8f360 100644 --- a/src/plugins/qmldesigner/components/materialeditor/materialeditorqmlbackend.cpp +++ b/src/plugins/qmldesigner/components/materialeditor/materialeditorqmlbackend.cpp @@ -80,8 +80,8 @@ public: MaterialEditorQmlBackend::MaterialEditorQmlBackend(MaterialEditorView *materialEditor) : m_quickWidget(Utils::makeUniqueObjectPtr()) - , m_materialEditorTransaction(new MaterialEditorTransaction(materialEditor)) - , m_contextObject(new MaterialEditorContextObject(m_quickWidget.get())) + , m_materialEditorTransaction(std::make_unique(materialEditor)) + , m_contextObject(std::make_unique(m_quickWidget.get())) , m_materialEditorImageProvider(new MaterialEditorImageProvider()) { m_quickWidget->setObjectName(Constants::OBJECT_NAME_MATERIAL_EDITOR); @@ -90,7 +90,7 @@ MaterialEditorQmlBackend::MaterialEditorQmlBackend(MaterialEditorView *materialE m_quickWidget->engine()->addImageProvider("materialEditor", m_materialEditorImageProvider); m_contextObject->setBackendValues(&m_backendValuesPropertyMap); m_contextObject->setModel(materialEditor->model()); - context()->setContextObject(m_contextObject.data()); + context()->setContextObject(m_contextObject.get()); QObject::connect(&m_backendValuesPropertyMap, &DesignerPropertyMap::valueChanged, materialEditor, &MaterialEditorView::changeValue); @@ -193,7 +193,7 @@ QQmlContext *MaterialEditorQmlBackend::context() const MaterialEditorContextObject *MaterialEditorQmlBackend::contextObject() const { - return m_contextObject.data(); + return m_contextObject.get(); } QQuickWidget *MaterialEditorQmlBackend::widget() const @@ -224,7 +224,7 @@ DesignerPropertyMap &MaterialEditorQmlBackend::backendValuesPropertyMap() MaterialEditorTransaction *MaterialEditorQmlBackend::materialEditorTransaction() const { - return m_materialEditorTransaction.data(); + return m_materialEditorTransaction.get(); } PropertyEditorValue *MaterialEditorQmlBackend::propertyValueForName(const QString &propertyName) @@ -267,12 +267,9 @@ void MaterialEditorQmlBackend::setup(const QmlObjectNode &selectedMaterialNode, // anchors m_backendAnchorBinding.setup(selectedMaterialNode.modelNode()); - context()->setContextProperties( - QVector{ - {{"anchorBackend"}, QVariant::fromValue(&m_backendAnchorBinding)}, - {{"transaction"}, QVariant::fromValue(m_materialEditorTransaction.data())} - } - ); + context()->setContextProperties(QVector{ + {{"anchorBackend"}, QVariant::fromValue(&m_backendAnchorBinding)}, + {{"transaction"}, QVariant::fromValue(m_materialEditorTransaction.get())}}); contextObject()->setSpecificsUrl(qmlSpecificsFile); contextObject()->setStateName(stateName); diff --git a/src/plugins/qmldesigner/components/materialeditor/materialeditorqmlbackend.h b/src/plugins/qmldesigner/components/materialeditor/materialeditorqmlbackend.h index 0792a635ca3..9fd5fc23992 100644 --- a/src/plugins/qmldesigner/components/materialeditor/materialeditorqmlbackend.h +++ b/src/plugins/qmldesigner/components/materialeditor/materialeditorqmlbackend.h @@ -11,6 +11,8 @@ #include +#include + class PropertyEditorValue; QT_BEGIN_NAMESPACE @@ -67,8 +69,8 @@ private: Utils::UniqueObjectPtr m_quickWidget = nullptr; QmlAnchorBindingProxy m_backendAnchorBinding; QmlModelNodeProxy m_backendModelNode; - QScopedPointer m_materialEditorTransaction; - QScopedPointer m_contextObject; + std::unique_ptr m_materialEditorTransaction; + std::unique_ptr m_contextObject; QPointer m_materialEditorImageProvider; }; diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp index eea53c3f5a1..c397d445b19 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp @@ -83,16 +83,17 @@ namespace QmlDesigner { PropertyEditorQmlBackend::PropertyEditorQmlBackend(PropertyEditorView *propertyEditor, AsynchronousImageCache &imageCache) : m_view(Utils::makeUniqueObjectPtr(imageCache)) - , m_propertyEditorTransaction(new PropertyEditorTransaction(propertyEditor)) - , m_dummyPropertyEditorValue(new PropertyEditorValue()) - , m_contextObject(new PropertyEditorContextObject(m_view.get())) + , m_propertyEditorTransaction(std::make_unique(propertyEditor)) + , m_dummyPropertyEditorValue(std::make_unique()) + , m_contextObject(std::make_unique(m_view.get())) { m_view->engine()->setOutputWarningsToStandardError(QmlDesignerPlugin::instance() ->settings().value(DesignerSettingsKey::SHOW_PROPERTYEDITOR_WARNINGS).toBool()); m_view->engine()->addImportPath(propertyEditorResourcesPath() + "/imports"); m_dummyPropertyEditorValue->setValue(QLatin1String("#000000")); - context()->setContextProperty(QLatin1String("dummyBackendValue"), m_dummyPropertyEditorValue.data()); + context()->setContextProperty(QLatin1String("dummyBackendValue"), + m_dummyPropertyEditorValue.get()); m_contextObject->setBackendValues(&m_backendValuesPropertyMap); m_contextObject->setModel(propertyEditor->model()); m_contextObject->insertInQmlContext(context()); @@ -402,7 +403,7 @@ QQmlContext *PropertyEditorQmlBackend::context() PropertyEditorContextObject *PropertyEditorQmlBackend::contextObject() { - return m_contextObject.data(); + return m_contextObject.get(); } QQuickWidget *PropertyEditorQmlBackend::widget() @@ -432,7 +433,7 @@ DesignerPropertyMap &PropertyEditorQmlBackend::backendValuesPropertyMap() { } PropertyEditorTransaction *PropertyEditorQmlBackend::propertyEditorTransaction() { - return m_propertyEditorTransaction.data(); + return m_propertyEditorTransaction.get(); } PropertyEditorValue *PropertyEditorQmlBackend::propertyValueForName(const QString &propertyName) @@ -495,12 +496,9 @@ void PropertyEditorQmlBackend::setup(const QmlObjectNode &qmlObjectNode, const Q // anchors m_backendAnchorBinding.setup(qmlObjectNode.modelNode()); - context()->setContextProperties( - QVector{ - {{"anchorBackend"}, QVariant::fromValue(&m_backendAnchorBinding)}, - {{"transaction"}, QVariant::fromValue(m_propertyEditorTransaction.data())} - } - ); + context()->setContextProperties(QVector{ + {{"anchorBackend"}, QVariant::fromValue(&m_backendAnchorBinding)}, + {{"transaction"}, QVariant::fromValue(m_propertyEditorTransaction.get())}}); contextObject()->setHasMultiSelection( !qmlObjectNode.view()->singleSelectedModelNode().isValid()); @@ -592,13 +590,10 @@ void PropertyEditorQmlBackend::initialSetup(const TypeName &typeName, const QUrl QObject::connect(valueObject, &PropertyEditorValue::valueChanged, &backendValuesPropertyMap(), &DesignerPropertyMap::valueChanged); m_backendValuesPropertyMap.insert(QLatin1String("id"), QVariant::fromValue(valueObject)); - context()->setContextProperties( - QVector{ - {{"anchorBackend"}, QVariant::fromValue(&m_backendAnchorBinding)}, - {{"modelNodeBackend"}, QVariant::fromValue(&m_backendModelNode)}, - {{"transaction"}, QVariant::fromValue(m_propertyEditorTransaction.data())} - } - ); + context()->setContextProperties(QVector{ + {{"anchorBackend"}, QVariant::fromValue(&m_backendAnchorBinding)}, + {{"modelNodeBackend"}, QVariant::fromValue(&m_backendModelNode)}, + {{"transaction"}, QVariant::fromValue(m_propertyEditorTransaction.get())}}); contextObject()->setSpecificsUrl(qmlSpecificsFile); diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.h b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.h index 120b7b4abcf..1c9b808ac3d 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.h +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.h @@ -16,6 +16,8 @@ #include +#include + class PropertyEditorValue; namespace QmlDesigner { @@ -109,9 +111,9 @@ private: Utils::UniqueObjectPtr m_view = nullptr; QmlAnchorBindingProxy m_backendAnchorBinding; QmlModelNodeProxy m_backendModelNode; - QScopedPointer m_propertyEditorTransaction; - QScopedPointer m_dummyPropertyEditorValue; - QScopedPointer m_contextObject; + std::unique_ptr m_propertyEditorTransaction; + std::unique_ptr m_dummyPropertyEditorValue; + std::unique_ptr m_contextObject; }; } //QmlDesigner diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.cpp b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.cpp index fbceb5dfea3..80ee2ea1723 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.cpp @@ -22,7 +22,6 @@ #include #include -#include #include namespace QmlDesigner { diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp index b22b39e238b..e0d5759617b 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp @@ -38,7 +38,6 @@ #include #include #include -#include #include #include diff --git a/src/plugins/qmldesigner/components/texttool/textedititemwidget.cpp b/src/plugins/qmldesigner/components/texttool/textedititemwidget.cpp index b9c5868bd89..2861344d750 100644 --- a/src/plugins/qmldesigner/components/texttool/textedititemwidget.cpp +++ b/src/plugins/qmldesigner/components/texttool/textedititemwidget.cpp @@ -38,8 +38,8 @@ void TextEditItemWidget::paint(QPainter *painter, const QStyleOptionGraphicsItem QLineEdit* TextEditItemWidget::lineEdit() const { - if (m_lineEdit.isNull()) { - m_lineEdit.reset(new QLineEdit); + if (!m_lineEdit) { + m_lineEdit = std::make_unique(); m_lineEdit->setAlignment(Qt::AlignVCenter | Qt::AlignHCenter); QPalette palette = m_lineEdit->palette(); static QColor selectionColor = Utils::creatorTheme()->color(Utils::Theme::QmlDesigner_FormEditorSelectionColor); @@ -49,13 +49,13 @@ QLineEdit* TextEditItemWidget::lineEdit() const palette.setColor(QPalette::Text, Qt::black); m_lineEdit->setPalette(palette); } - return m_lineEdit.data(); + return m_lineEdit.get(); } QTextEdit* TextEditItemWidget::textEdit() const { - if (m_textEdit.isNull()) { - m_textEdit.reset(new QTextEdit); + if (!m_textEdit) { + m_textEdit = std::make_unique(); QPalette palette = m_textEdit->palette(); static QColor selectionColor = Utils::creatorTheme()->color(Utils::Theme::QmlDesigner_FormEditorSelectionColor); palette.setColor(QPalette::Highlight, selectionColor); @@ -65,7 +65,7 @@ QTextEdit* TextEditItemWidget::textEdit() const m_textEdit->setPalette(palette); } - return m_textEdit.data(); + return m_textEdit.get(); } void TextEditItemWidget::activateTextEdit(const QSize &maximumSize) @@ -83,19 +83,19 @@ void TextEditItemWidget::activateLineEdit() QString TextEditItemWidget::text() const { - if (widget() == m_lineEdit.data()) + if (widget() == m_lineEdit.get()) return m_lineEdit->text(); - else if (widget() == m_textEdit.data()) + else if (widget() == m_textEdit.get()) return m_textEdit->toPlainText(); return QString(); } void TextEditItemWidget::updateText(const QString &text) { - if (widget() == m_lineEdit.data()) { + if (widget() == m_lineEdit.get()) { m_lineEdit->setText(text); m_lineEdit->selectAll(); - } else if (widget() == m_textEdit.data()) { + } else if (widget() == m_textEdit.get()) { m_textEdit->setText(text); m_textEdit->selectAll(); } diff --git a/src/plugins/qmldesigner/components/texttool/textedititemwidget.h b/src/plugins/qmldesigner/components/texttool/textedititemwidget.h index 8aa6c409f93..f975f1a3daa 100644 --- a/src/plugins/qmldesigner/components/texttool/textedititemwidget.h +++ b/src/plugins/qmldesigner/components/texttool/textedititemwidget.h @@ -3,7 +3,8 @@ #pragma once #include -#include + +#include QT_BEGIN_NAMESPACE class QTextEdit; @@ -32,7 +33,7 @@ protected: QString text() const; private: - mutable QScopedPointer m_lineEdit; - mutable QScopedPointer m_textEdit; + mutable std::unique_ptr m_lineEdit; + mutable std::unique_ptr m_textEdit; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/textureeditor/textureeditorqmlbackend.cpp b/src/plugins/qmldesigner/components/textureeditor/textureeditorqmlbackend.cpp index 972574554b0..415d943723d 100644 --- a/src/plugins/qmldesigner/components/textureeditor/textureeditorqmlbackend.cpp +++ b/src/plugins/qmldesigner/components/textureeditor/textureeditorqmlbackend.cpp @@ -42,10 +42,11 @@ static QObject *variantToQObject(const QVariant &value) namespace QmlDesigner { -TextureEditorQmlBackend::TextureEditorQmlBackend(TextureEditorView *textureEditor, AsynchronousImageCache &imageCache) - : m_quickWidget(new QQuickWidget) - , m_textureEditorTransaction(new TextureEditorTransaction(textureEditor)) - , m_contextObject(new TextureEditorContextObject(m_quickWidget->rootContext())) +TextureEditorQmlBackend::TextureEditorQmlBackend(TextureEditorView *textureEditor, + AsynchronousImageCache &imageCache) + : m_quickWidget(Utils::makeUniqueObjectPtr()) + , m_textureEditorTransaction(std::make_unique(textureEditor)) + , m_contextObject(std::make_unique(m_quickWidget->rootContext())) { QImage defaultImage; defaultImage.load(Utils::StyleHelper::dpiSpecificImageFile(":/textureeditor/images/texture_default.png")); @@ -56,7 +57,7 @@ TextureEditorQmlBackend::TextureEditorQmlBackend(TextureEditorView *textureEdito m_quickWidget->engine()->addImageProvider("qmldesigner_thumbnails", m_textureEditorImageProvider); m_contextObject->setBackendValues(&m_backendValuesPropertyMap); m_contextObject->setModel(textureEditor->model()); - context()->setContextObject(m_contextObject.data()); + context()->setContextObject(m_contextObject.get()); QObject::connect(&m_backendValuesPropertyMap, &DesignerPropertyMap::valueChanged, textureEditor, &TextureEditorView::changeValue); @@ -159,7 +160,7 @@ QQmlContext *TextureEditorQmlBackend::context() const TextureEditorContextObject *TextureEditorQmlBackend::contextObject() const { - return m_contextObject.data(); + return m_contextObject.get(); } QQuickWidget *TextureEditorQmlBackend::widget() const @@ -184,7 +185,7 @@ DesignerPropertyMap &TextureEditorQmlBackend::backendValuesPropertyMap() TextureEditorTransaction *TextureEditorQmlBackend::textureEditorTransaction() const { - return m_textureEditorTransaction.data(); + return m_textureEditorTransaction.get(); } PropertyEditorValue *TextureEditorQmlBackend::propertyValueForName(const QString &propertyName) @@ -227,12 +228,9 @@ void TextureEditorQmlBackend::setup(const QmlObjectNode &selectedTextureNode, co // anchors m_backendAnchorBinding.setup(selectedTextureNode.modelNode()); - context()->setContextProperties( - QVector{ - {{"anchorBackend"}, QVariant::fromValue(&m_backendAnchorBinding)}, - {{"transaction"}, QVariant::fromValue(m_textureEditorTransaction.data())} - } - ); + context()->setContextProperties(QVector{ + {{"anchorBackend"}, QVariant::fromValue(&m_backendAnchorBinding)}, + {{"transaction"}, QVariant::fromValue(m_textureEditorTransaction.get())}}); contextObject()->setSpecificsUrl(qmlSpecificsFile); contextObject()->setStateName(stateName); diff --git a/src/plugins/qmldesigner/components/textureeditor/textureeditorqmlbackend.h b/src/plugins/qmldesigner/components/textureeditor/textureeditorqmlbackend.h index c8a113f7d3d..f69c46a5698 100644 --- a/src/plugins/qmldesigner/components/textureeditor/textureeditorqmlbackend.h +++ b/src/plugins/qmldesigner/components/textureeditor/textureeditorqmlbackend.h @@ -11,6 +11,8 @@ #include +#include + class PropertyEditorValue; QT_BEGIN_NAMESPACE @@ -64,11 +66,11 @@ private: // this needs be destructed after m_quickWidget->engine() is destructed DesignerPropertyMap m_backendValuesPropertyMap; - Utils::UniqueObjectPtr m_quickWidget = nullptr; + Utils::UniqueObjectPtr m_quickWidget; QmlAnchorBindingProxy m_backendAnchorBinding; QmlModelNodeProxy m_backendModelNode; - QScopedPointer m_textureEditorTransaction; - QScopedPointer m_contextObject; + std::unique_ptr m_textureEditorTransaction; + std::unique_ptr m_contextObject; AssetImageProvider *m_textureEditorImageProvider = nullptr; }; diff --git a/src/plugins/qmldesigner/components/textureeditor/textureeditorview.cpp b/src/plugins/qmldesigner/components/textureeditor/textureeditorview.cpp index 4f2cc0f81f8..a637431c4dd 100644 --- a/src/plugins/qmldesigner/components/textureeditor/textureeditorview.cpp +++ b/src/plugins/qmldesigner/components/textureeditor/textureeditorview.cpp @@ -43,7 +43,6 @@ #include #include #include -#include #include #include #include diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelinepropertyitem.cpp b/src/plugins/qmldesigner/components/timelineeditor/timelinepropertyitem.cpp index 698ef0f03bb..762bd85c8b4 100644 --- a/src/plugins/qmldesigner/components/timelineeditor/timelinepropertyitem.cpp +++ b/src/plugins/qmldesigner/components/timelineeditor/timelinepropertyitem.cpp @@ -35,7 +35,6 @@ #include #include #include -#include #include diff --git a/src/plugins/qmldesigner/designercore/include/rewriterview.h b/src/plugins/qmldesigner/designercore/include/rewriterview.h index 0134349682e..92c79c78631 100644 --- a/src/plugins/qmldesigner/designercore/include/rewriterview.h +++ b/src/plugins/qmldesigner/designercore/include/rewriterview.h @@ -8,11 +8,11 @@ #include "documentmessage.h" #include "rewritertransaction.h" -#include #include #include #include +#include namespace QmlJS { class Document; @@ -203,9 +203,9 @@ private: //variables bool m_checkLinkErrors = true; DifferenceHandling m_differenceHandling; - QScopedPointer m_positionStorage; - QScopedPointer m_modelToTextMerger; - QScopedPointer m_textToModelMerger; + std::unique_ptr m_positionStorage; + std::unique_ptr m_modelToTextMerger; + std::unique_ptr m_textToModelMerger; QList m_errors; QList m_warnings; RewriterTransaction m_removeDefaultPropertyTransaction; diff --git a/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp b/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp index 71354edb92a..fd8c92d04d3 100644 --- a/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp +++ b/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp @@ -91,7 +91,6 @@ #include #include #include -#include #include #include diff --git a/src/plugins/qmldesigner/designercore/model/qmlvisualnode.cpp b/src/plugins/qmldesigner/designercore/model/qmlvisualnode.cpp index 1077c5bda8f..0c51c9a341e 100644 --- a/src/plugins/qmldesigner/designercore/model/qmlvisualnode.cpp +++ b/src/plugins/qmldesigner/designercore/model/qmlvisualnode.cpp @@ -24,6 +24,8 @@ #include #include +#include + namespace QmlDesigner { static char imagePlaceHolder[] = "qrc:/qtquickplugin/images/template_image.png"; @@ -288,17 +290,17 @@ static QmlObjectNode createQmlObjectNodeFromSource(AbstractView *view, textEdit.setPlainText(source); NotIndentingTextEditModifier modifier(&textEdit); - QScopedPointer rewriterView( - new RewriterView(view->externalDependencies(), RewriterView::Amend)); + std::unique_ptr rewriterView = std::make_unique( + view->externalDependencies(), RewriterView::Amend); rewriterView->setCheckSemanticErrors(false); rewriterView->setTextModifier(&modifier); rewriterView->setAllowComponentRoot(true); rewriterView->setPossibleImportsEnabled(false); - inputModel->setRewriterView(rewriterView.data()); + inputModel->setRewriterView(rewriterView.get()); if (rewriterView->errors().isEmpty() && rewriterView->rootModelNode().isValid()) { ModelNode rootModelNode = rewriterView->rootModelNode(); - inputModel->detachView(rewriterView.data()); + inputModel->detachView(rewriterView.get()); QmlVisualNode(rootModelNode).setPosition(position); ModelMerger merger(view); return merger.insertModel(rootModelNode); diff --git a/src/plugins/qmldesigner/designercore/model/rewriterview.cpp b/src/plugins/qmldesigner/designercore/model/rewriterview.cpp index 81c699ce43a..8905f95430c 100644 --- a/src/plugins/qmldesigner/designercore/model/rewriterview.cpp +++ b/src/plugins/qmldesigner/designercore/model/rewriterview.cpp @@ -58,9 +58,9 @@ RewriterView::RewriterView(ExternalDependenciesInterface &externalDependencies, DifferenceHandling differenceHandling) : AbstractView{externalDependencies} , m_differenceHandling(differenceHandling) - , m_positionStorage(new ModelNodePositionStorage) - , m_modelToTextMerger(new Internal::ModelToTextMerger(this)) - , m_textToModelMerger(new Internal::TextToModelMerger(this)) + , m_positionStorage(std::make_unique()) + , m_modelToTextMerger(std::make_unique(this)) + , m_textToModelMerger(std::make_unique(this)) { m_amendTimer.setSingleShot(true); @@ -80,12 +80,12 @@ RewriterView::~RewriterView() = default; Internal::ModelToTextMerger *RewriterView::modelToTextMerger() const { - return m_modelToTextMerger.data(); + return m_modelToTextMerger.get(); } Internal::TextToModelMerger *RewriterView::textToModelMerger() const { - return m_textToModelMerger.data(); + return m_textToModelMerger.get(); } void RewriterView::modelAttached(Model *model) @@ -94,7 +94,7 @@ void RewriterView::modelAttached(Model *model) AbstractView::modelAttached(model); - ModelAmender differenceHandler(m_textToModelMerger.data()); + ModelAmender differenceHandler(m_textToModelMerger.get()); const QString qmlSource = m_textModifier->text(); if (m_textToModelMerger->load(qmlSource, differenceHandler)) m_lastCorrectQmlSource = qmlSource; @@ -495,7 +495,7 @@ void RewriterView::amendQmlText() const QString newQmlText = m_textModifier->text(); - ModelAmender differenceHandler(m_textToModelMerger.data()); + ModelAmender differenceHandler(m_textToModelMerger.get()); if (m_textToModelMerger->load(newQmlText, differenceHandler)) m_lastCorrectQmlSource = newQmlText; emitCustomNotification(EndRewriterAmend); @@ -701,7 +701,7 @@ void RewriterView::forceAmend() Internal::ModelNodePositionStorage *RewriterView::positionStorage() const { - return m_positionStorage.data(); + return m_positionStorage.get(); } QList RewriterView::warnings() const @@ -758,7 +758,7 @@ void RewriterView::resetToLastCorrectQml() { m_textModifier->textDocument()->undo(); m_textModifier->textDocument()->clearUndoRedoStacks(QTextDocument::RedoStack); - ModelAmender differenceHandler(m_textToModelMerger.data()); + ModelAmender differenceHandler(m_textToModelMerger.get()); Internal::WriteLocker::unlock(model()); m_textToModelMerger->load(m_textModifier->text(), differenceHandler); Internal::WriteLocker::lock(model()); @@ -1155,7 +1155,7 @@ void RewriterView::qmlTextChanged() switch (m_differenceHandling) { case Validate: { - ModelValidator differenceHandler(m_textToModelMerger.data()); + ModelValidator differenceHandler(m_textToModelMerger.get()); if (m_textToModelMerger->load(newQmlText, differenceHandler)) m_lastCorrectQmlSource = newQmlText; break; diff --git a/src/plugins/qmldesigner/designercore/model/stylesheetmerger.cpp b/src/plugins/qmldesigner/designercore/model/stylesheetmerger.cpp index 16877b61db6..4f744d54e6c 100644 --- a/src/plugins/qmldesigner/designercore/model/stylesheetmerger.cpp +++ b/src/plugins/qmldesigner/designercore/model/stylesheetmerger.cpp @@ -23,6 +23,8 @@ #include #include +#include + namespace { QPoint pointForModelNode(const QmlDesigner::ModelNode &node) @@ -641,10 +643,10 @@ void StylesheetMerger::styleMerge(const QString &qmlTemplateString, textEditTemplate.setPlainText(imports + qmlTemplateString); NotIndentingTextEditModifier textModifierTemplate(&textEditTemplate); - QScopedPointer templateRewriterView( - new RewriterView(externalDependencies, RewriterView::Amend)); + std::unique_ptr templateRewriterView = std::make_unique( + externalDependencies, RewriterView::Amend); templateRewriterView->setTextModifier(&textModifierTemplate); - templateModel->attachView(templateRewriterView.data()); + templateModel->attachView(templateRewriterView.get()); templateRewriterView->setCheckSemanticErrors(false); templateRewriterView->setPossibleImportsEnabled(false); @@ -665,12 +667,12 @@ void StylesheetMerger::styleMerge(const QString &qmlTemplateString, textEditStyle.setPlainText(parentRewriterView->textModifierContent()); NotIndentingTextEditModifier textModifierStyle(&textEditStyle); - QScopedPointer styleRewriterView( - new RewriterView(externalDependencies, RewriterView::Amend)); + std::unique_ptr styleRewriterView = std::make_unique( + externalDependencies, RewriterView::Amend); styleRewriterView->setTextModifier(&textModifierStyle); - styleModel->attachView(styleRewriterView.data()); + styleModel->attachView(styleRewriterView.get()); - StylesheetMerger merger(templateRewriterView.data(), styleRewriterView.data()); + StylesheetMerger merger(templateRewriterView.get(), styleRewriterView.get()); try { merger.merge(); diff --git a/src/plugins/qmldesigner/designmodewidget.h b/src/plugins/qmldesigner/designmodewidget.h index 464994b7e34..881335da75c 100644 --- a/src/plugins/qmldesigner/designmodewidget.h +++ b/src/plugins/qmldesigner/designmodewidget.h @@ -11,7 +11,6 @@ #include #include -#include #include #include From dd10b5972cccacbf26c06ee014850cf61d04f40b Mon Sep 17 00:00:00 2001 From: Teea Poldsam Date: Tue, 7 May 2024 09:05:03 +0300 Subject: [PATCH 076/154] Doc: Fix QDS doc warnings Fixed some links and one command issue. Fixes: QDS-12704 Change-Id: Ie281718675cc3a71d02248096a57a5dceffce512 Reviewed-by: Mats Honkamaa --- doc/qtcreator/src/qtquick/qtquick-modules-with-plugins.qdoc | 2 +- doc/qtdesignstudio/src/qtdesignstudio-terms.qdoc | 2 +- .../src/qtquick3d-editor/qtdesignstudio-3d-repeater-3d.qdoc | 2 +- doc/qtdesignstudio/src/views/studio-model-editor.qdoc | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/qtcreator/src/qtquick/qtquick-modules-with-plugins.qdoc b/doc/qtcreator/src/qtquick/qtquick-modules-with-plugins.qdoc index 46651d8748a..e1f7e3bce54 100644 --- a/doc/qtcreator/src/qtquick/qtquick-modules-with-plugins.qdoc +++ b/doc/qtcreator/src/qtquick/qtquick-modules-with-plugins.qdoc @@ -82,7 +82,7 @@ \uicontrol {Reset Code Model} to reset the code model. \if defined(qtdesignstudio) For more information about how to show the \uicontrol {Tools} menu, see - \l{Customizing the Menu}. + \l{Customizing the Menu Bar}. \endif \if defined(qtcreator) diff --git a/doc/qtdesignstudio/src/qtdesignstudio-terms.qdoc b/doc/qtdesignstudio/src/qtdesignstudio-terms.qdoc index 70ff56517cb..0b3e8c68ad9 100644 --- a/doc/qtdesignstudio/src/qtdesignstudio-terms.qdoc +++ b/doc/qtdesignstudio/src/qtdesignstudio-terms.qdoc @@ -170,7 +170,7 @@ example, if you create a 3D project, preset 3D components are added to it. You can add more preset components in \uicontrol {Components}. - \image studio-project-wizards.png "New Project dialog" + \image studio-project-wizards.webp "New Project dialog" Read more about projects: diff --git a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-repeater-3d.qdoc b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-repeater-3d.qdoc index 0f0d0634cab..5cf2370cc98 100644 --- a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-repeater-3d.qdoc +++ b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-repeater-3d.qdoc @@ -142,7 +142,7 @@ \li From \uicontrol Components, drag a \uicontrol Sphere to \e _3DRepeater in \uicontrol Navigator. \image repeater3d-listmodel-navigator.png - \li Select \e sphere in \uicontrol Navigator and in the \Properties view, select + \li Select \e sphere in \uicontrol Navigator and in the \uicontrol Properties view, select \inlineimage icons/action-icon.png next to \uicontrol Scale > \uicontrol X. \li Select \uicontrol {Set binding} to open \uicontrol {Binding Editor}. diff --git a/doc/qtdesignstudio/src/views/studio-model-editor.qdoc b/doc/qtdesignstudio/src/views/studio-model-editor.qdoc index 4d10f87552e..0caebcfb493 100644 --- a/doc/qtdesignstudio/src/views/studio-model-editor.qdoc +++ b/doc/qtdesignstudio/src/views/studio-model-editor.qdoc @@ -18,7 +18,7 @@ \image edit-list-model-model-editor.webp For examples of how to use data models, see - \l {Adding a Repeater3D Component with a List Model}. + \l {Adding a Repeater3D Component with a Model}. \section1 Creating a Data Model From fa07abf6d89b1fc4693cf634b65718dfa8cb8c95 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Mon, 6 May 2024 17:23:29 +0200 Subject: [PATCH 077/154] QmlDesigner: Rename ProjectData into DirectoryInfo The design is now directory based. So the name should reflect that. Task-number: QDS-12665 Change-Id: I7479c37357a4fd33f5f3b4c93d63d2fdd4ca6ef9 Reviewed-by: Tim Jenssen Reviewed-by: Reviewed-by: Qt CI Patch Build Bot --- .../projectstorage/projectstorage.cpp | 144 ++-- .../projectstorage/projectstorage.h | 8 +- .../projectstorageexceptions.cpp | 18 +- .../projectstorage/projectstorageexceptions.h | 12 +- .../projectstorage/projectstorageinterface.h | 4 +- .../projectstorage/projectstoragetypes.h | 30 +- .../projectstorage/projectstorageupdater.cpp | 101 ++- .../projectstorage/projectstorageupdater.h | 10 +- .../projectstorage/qmltypesparser.cpp | 16 +- .../projectstorage/qmltypesparser.h | 2 +- .../projectstorage/qmltypesparserinterface.h | 2 +- .../qmldesigner/qmldesignerprojectmanager.cpp | 38 +- .../qmldesigner/qmldesignerprojectmanager.h | 4 +- tests/unit/tests/mocks/projectstoragemock.h | 8 +- tests/unit/tests/mocks/qmltypesparsermock.h | 2 +- .../tests/printers/gtest-creator-printing.cpp | 6 +- .../tests/printers/gtest-creator-printing.h | 4 +- .../projectstorage/projectstorage-test.cpp | 148 ++-- .../projectstorageupdater-test.cpp | 813 +++++++++--------- .../projectstorage/qmltypesparser-test.cpp | 58 +- 20 files changed, 716 insertions(+), 712 deletions(-) diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.cpp b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.cpp index ea35fec3722..98ee3253df7 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.cpp +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.cpp @@ -502,25 +502,25 @@ struct ProjectStorage::Statements "DELETE FROM exportedTypeNames WHERE exportedTypeNameId=?", database}; Sqlite::WriteStatement<2> updateExportedTypeNameTypeIdStatement{ "UPDATE exportedTypeNames SET typeId=?2 WHERE exportedTypeNameId=?1", database}; - mutable Sqlite::ReadStatement<4, 1> selectProjectDatasForSourceIdsStatement{ - "SELECT projectSourceId, sourceId, moduleId, fileType FROM projectDatas WHERE " - "projectSourceId IN carray(?1) ORDER BY projectSourceId, sourceId", + mutable Sqlite::ReadStatement<4, 1> selectDirectoryInfosForSourceIdsStatement{ + "SELECT directorySourceId, sourceId, moduleId, fileType FROM directoryInfos WHERE " + "directorySourceId IN carray(?1) ORDER BY directorySourceId, sourceId", database}; - Sqlite::WriteStatement<4> insertProjectDataStatement{ - "INSERT INTO projectDatas(projectSourceId, sourceId, " + Sqlite::WriteStatement<4> insertDirectoryInfoStatement{ + "INSERT INTO directoryInfos(directorySourceId, sourceId, " "moduleId, fileType) VALUES(?1, ?2, ?3, ?4)", database}; - Sqlite::WriteStatement<2> deleteProjectDataStatement{ - "DELETE FROM projectDatas WHERE projectSourceId=?1 AND sourceId=?2", database}; - Sqlite::WriteStatement<4> updateProjectDataStatement{ - "UPDATE projectDatas SET moduleId=?3, fileType=?4 WHERE projectSourceId=?1 AND sourceId=?2", + Sqlite::WriteStatement<2> deleteDirectoryInfoStatement{ + "DELETE FROM directoryInfos WHERE directorySourceId=?1 AND sourceId=?2", database}; + Sqlite::WriteStatement<4> updateDirectoryInfoStatement{ + "UPDATE directoryInfos SET moduleId=?3, fileType=?4 WHERE directorySourceId=?1 AND sourceId=?2", database}; - mutable Sqlite::ReadStatement<4, 1> selectProjectDatasForSourceIdStatement{ - "SELECT projectSourceId, sourceId, moduleId, fileType FROM projectDatas WHERE " - "projectSourceId=?1", + mutable Sqlite::ReadStatement<4, 1> selectDirectoryInfosForSourceIdStatement{ + "SELECT directorySourceId, sourceId, moduleId, fileType FROM directoryInfos WHERE " + "directorySourceId=?1", database}; - mutable Sqlite::ReadStatement<4, 1> selectProjectDataForSourceIdStatement{ - "SELECT projectSourceId, sourceId, moduleId, fileType FROM projectDatas WHERE " + mutable Sqlite::ReadStatement<4, 1> selectDirectoryInfoForSourceIdStatement{ + "SELECT directorySourceId, sourceId, moduleId, fileType FROM directoryInfos WHERE " "sourceId=?1 LIMIT 1", database}; mutable Sqlite::ReadStatement<1, 1> selectTypeIdsForSourceIdsStatement{ @@ -745,7 +745,7 @@ public: createModuleExportedImportsTable(database, moduleIdColumn); createDocumentImportsTable(database, moduleIdColumn); createFileStatusesTable(database); - createProjectDatasTable(database); + createDirectoryInfosTable(database); createPropertyEditorPathsTable(database); createTypeAnnotionsTable(database); } @@ -1063,19 +1063,19 @@ public: table.initialize(database); } - void createProjectDatasTable(Database &database) + void createDirectoryInfosTable(Database &database) { Sqlite::StrictTable table; table.setUseIfNotExists(true); table.setUseWithoutRowId(true); - table.setName("projectDatas"); - auto &projectSourceIdColumn = table.addColumn("projectSourceId", + table.setName("directoryInfos"); + auto &directorySourceIdColumn = table.addColumn("directorySourceId", Sqlite::StrictColumnType::Integer); auto &sourceIdColumn = table.addColumn("sourceId", Sqlite::StrictColumnType::Integer); table.addColumn("moduleId", Sqlite::StrictColumnType::Integer); table.addColumn("fileType", Sqlite::StrictColumnType::Integer); - table.addPrimaryKeyContraint({projectSourceIdColumn, sourceIdColumn}); + table.addPrimaryKeyContraint({directorySourceIdColumn, sourceIdColumn}); table.addUniqueIndex({sourceIdColumn}); table.initialize(database); @@ -1196,7 +1196,7 @@ void ProjectStorage::synchronize(Storage::Synchronization::SynchronizationPackag linkAliases(insertedAliasPropertyDeclarations, updatedAliasPropertyDeclarations); - synchronizeProjectDatas(package.projectDatas, package.updatedProjectSourceIds); + synchronizeDirectoryInfos(package.directoryInfos, package.updatedProjectSourceIds); commonTypeCache_.resetTypeIds(); }); @@ -2113,53 +2113,53 @@ FileStatus ProjectStorage::fetchFileStatus(SourceId sourceId) const return fileStatus; } -std::optional ProjectStorage::fetchProjectData(SourceId sourceId) const +std::optional ProjectStorage::fetchDirectoryInfo(SourceId sourceId) const { using NanotraceHR::keyValue; NanotraceHR::Tracer tracer{"fetch project data"_t, projectStorageCategory(), keyValue("source id", sourceId)}; - auto projectData = s->selectProjectDataForSourceIdStatement - .optionalValueWithTransaction( + auto directoryInfo = s->selectDirectoryInfoForSourceIdStatement + .optionalValueWithTransaction( sourceId); - tracer.end(keyValue("project data", projectData)); + tracer.end(keyValue("project data", directoryInfo)); - return projectData; + return directoryInfo; } -Storage::Synchronization::ProjectDatas ProjectStorage::fetchProjectDatas(SourceId projectSourceId) const +Storage::Synchronization::DirectoryInfos ProjectStorage::fetchDirectoryInfos(SourceId directorySourceId) const { using NanotraceHR::keyValue; NanotraceHR::Tracer tracer{"fetch project datas by source id"_t, projectStorageCategory(), - keyValue("source id", projectSourceId)}; + keyValue("source id", directorySourceId)}; - auto projectDatas = s->selectProjectDatasForSourceIdStatement - .valuesWithTransaction( - projectSourceId); + auto directoryInfos = s->selectDirectoryInfosForSourceIdStatement + .valuesWithTransaction( + directorySourceId); - tracer.end(keyValue("project datas", projectDatas)); + tracer.end(keyValue("project datas", directoryInfos)); - return projectDatas; + return directoryInfos; } -Storage::Synchronization::ProjectDatas ProjectStorage::fetchProjectDatas( - const SourceIds &projectSourceIds) const +Storage::Synchronization::DirectoryInfos ProjectStorage::fetchDirectoryInfos( + const SourceIds &directorySourceIds) const { using NanotraceHR::keyValue; NanotraceHR::Tracer tracer{"fetch project datas by source ids"_t, projectStorageCategory(), - keyValue("source ids", projectSourceIds)}; + keyValue("source ids", directorySourceIds)}; - auto projectDatas = s->selectProjectDatasForSourceIdsStatement - .valuesWithTransaction( - toIntegers(projectSourceIds)); + auto directoryInfos = s->selectDirectoryInfosForSourceIdsStatement + .valuesWithTransaction( + toIntegers(directorySourceIds)); - tracer.end(keyValue("project datas", projectDatas)); + tracer.end(keyValue("project datas", directoryInfos)); - return projectDatas; + return directoryInfos; } void ProjectStorage::setPropertyEditorPathId(TypeId typeId, SourceId pathId) @@ -2465,74 +2465,74 @@ void ProjectStorage::synchronizeTypes(Storage::Synchronization::Types &types, syncDefaultProperties(types); } -void ProjectStorage::synchronizeProjectDatas(Storage::Synchronization::ProjectDatas &projectDatas, +void ProjectStorage::synchronizeDirectoryInfos(Storage::Synchronization::DirectoryInfos &directoryInfos, 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) - return projectSourceIdDifference; + auto directorySourceIdDifference = first.directorySourceId - second.directorySourceId; + if (directorySourceIdDifference != 0) + return directorySourceIdDifference; return first.sourceId - second.sourceId; }; - std::sort(projectDatas.begin(), projectDatas.end(), [&](auto &&first, auto &&second) { - return std::tie(first.projectSourceId, first.sourceId) - < std::tie(second.projectSourceId, second.sourceId); + std::sort(directoryInfos.begin(), directoryInfos.end(), [&](auto &&first, auto &&second) { + return std::tie(first.directorySourceId, first.sourceId) + < std::tie(second.directorySourceId, second.sourceId); }); - auto range = s->selectProjectDatasForSourceIdsStatement.range( + auto range = s->selectDirectoryInfosForSourceIdsStatement.range( toIntegers(updatedProjectSourceIds)); - auto insert = [&](const Storage::Synchronization::ProjectData &projectData) { + auto insert = [&](const Storage::Synchronization::DirectoryInfo &directoryInfo) { using NanotraceHR::keyValue; NanotraceHR::Tracer tracer{"insert project data"_t, projectStorageCategory(), - keyValue("project data", projectData)}; + keyValue("project data", directoryInfo)}; - if (!projectData.projectSourceId) - throw ProjectDataHasInvalidProjectSourceId{}; - if (!projectData.sourceId) - throw ProjectDataHasInvalidSourceId{}; + if (!directoryInfo.directorySourceId) + throw DirectoryInfoHasInvalidProjectSourceId{}; + if (!directoryInfo.sourceId) + throw DirectoryInfoHasInvalidSourceId{}; - s->insertProjectDataStatement.write(projectData.projectSourceId, - projectData.sourceId, - projectData.moduleId, - projectData.fileType); + s->insertDirectoryInfoStatement.write(directoryInfo.directorySourceId, + directoryInfo.sourceId, + directoryInfo.moduleId, + directoryInfo.fileType); }; - auto update = [&](const Storage::Synchronization::ProjectData &projectDataFromDatabase, - const Storage::Synchronization::ProjectData &projectData) { - if (projectDataFromDatabase.fileType != projectData.fileType - || !compareInvalidAreTrue(projectDataFromDatabase.moduleId, projectData.moduleId)) { + auto update = [&](const Storage::Synchronization::DirectoryInfo &directoryInfoFromDatabase, + const Storage::Synchronization::DirectoryInfo &directoryInfo) { + if (directoryInfoFromDatabase.fileType != directoryInfo.fileType + || !compareInvalidAreTrue(directoryInfoFromDatabase.moduleId, directoryInfo.moduleId)) { using NanotraceHR::keyValue; NanotraceHR::Tracer tracer{"update project data"_t, projectStorageCategory(), - keyValue("project data", projectData), - keyValue("project data from database", projectDataFromDatabase)}; + keyValue("project data", directoryInfo), + keyValue("project data from database", directoryInfoFromDatabase)}; - s->updateProjectDataStatement.write(projectData.projectSourceId, - projectData.sourceId, - projectData.moduleId, - projectData.fileType); + s->updateDirectoryInfoStatement.write(directoryInfo.directorySourceId, + directoryInfo.sourceId, + directoryInfo.moduleId, + directoryInfo.fileType); return Sqlite::UpdateChange::Update; } return Sqlite::UpdateChange::No; }; - auto remove = [&](const Storage::Synchronization::ProjectData &projectData) { + auto remove = [&](const Storage::Synchronization::DirectoryInfo &directoryInfo) { using NanotraceHR::keyValue; NanotraceHR::Tracer tracer{"remove project data"_t, projectStorageCategory(), - keyValue("project data", projectData)}; + keyValue("project data", directoryInfo)}; - s->deleteProjectDataStatement.write(projectData.projectSourceId, projectData.sourceId); + s->deleteDirectoryInfoStatement.write(directoryInfo.directorySourceId, directoryInfo.sourceId); }; - Sqlite::insertUpdateDelete(range, projectDatas, compareKey, insert, update, remove); + Sqlite::insertUpdateDelete(range, directoryInfos, compareKey, insert, update, remove); } void ProjectStorage::synchronizeFileStatuses(FileStatuses &fileStatuses, diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h index 85eebeb2cb5..0ae508f58f1 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h @@ -229,11 +229,11 @@ public: FileStatus fetchFileStatus(SourceId sourceId) const override; - std::optional fetchProjectData(SourceId sourceId) const override; + std::optional fetchDirectoryInfo(SourceId sourceId) const override; - Storage::Synchronization::ProjectDatas fetchProjectDatas(SourceId projectSourceId) const override; + Storage::Synchronization::DirectoryInfos fetchDirectoryInfos(SourceId directorySourceId) const override; - Storage::Synchronization::ProjectDatas fetchProjectDatas(const SourceIds &projectSourceIds) const; + Storage::Synchronization::DirectoryInfos fetchDirectoryInfos(const SourceIds &directorySourceIds) const; void setPropertyEditorPathId(TypeId typeId, SourceId pathId); @@ -560,7 +560,7 @@ private: Prototypes &relinkableExtensions, const SourceIds &updatedSourceIds); - void synchronizeProjectDatas(Storage::Synchronization::ProjectDatas &projectDatas, + void synchronizeDirectoryInfos(Storage::Synchronization::DirectoryInfos &directoryInfos, const SourceIds &updatedProjectSourceIds); void synchronizeFileStatuses(FileStatuses &fileStatuses, const SourceIds &updatedSourceIds); diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.cpp b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.cpp index a5dc60c4fa4..a86b78a785f 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.cpp +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.cpp @@ -148,32 +148,32 @@ const char *CannotParseQmlDocumentFile::what() const noexcept return "Cannot parse qml types file!"; } -ProjectDataHasInvalidProjectSourceId::ProjectDataHasInvalidProjectSourceId() +DirectoryInfoHasInvalidProjectSourceId::DirectoryInfoHasInvalidProjectSourceId() { - category().threadEvent("ProjectDataHasInvalidProjectSourceId"_t); + category().threadEvent("DirectoryInfoHasInvalidProjectSourceId"_t); } -const char *ProjectDataHasInvalidProjectSourceId::what() const noexcept +const char *DirectoryInfoHasInvalidProjectSourceId::what() const noexcept { return "The project source id is invalid!"; } -ProjectDataHasInvalidSourceId::ProjectDataHasInvalidSourceId() +DirectoryInfoHasInvalidSourceId::DirectoryInfoHasInvalidSourceId() { - category().threadEvent("ProjectDataHasInvalidSourceId"_t); + category().threadEvent("DirectoryInfoHasInvalidSourceId"_t); } -const char *ProjectDataHasInvalidSourceId::what() const noexcept +const char *DirectoryInfoHasInvalidSourceId::what() const noexcept { return "The source id is invalid!"; } -ProjectDataHasInvalidModuleId::ProjectDataHasInvalidModuleId() +DirectoryInfoHasInvalidModuleId::DirectoryInfoHasInvalidModuleId() { - category().threadEvent("ProjectDataHasInvalidModuleId"_t); + category().threadEvent("DirectoryInfoHasInvalidModuleId"_t); } -const char *ProjectDataHasInvalidModuleId::what() const noexcept +const char *DirectoryInfoHasInvalidModuleId::what() const noexcept { return "The module id is invalid!"; } diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.h index d85f1f7f9ee..f4f78f714bb 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.h @@ -130,24 +130,24 @@ public: const char *what() const noexcept override; }; -class QMLDESIGNERCORE_EXPORT ProjectDataHasInvalidProjectSourceId : public ProjectStorageError +class QMLDESIGNERCORE_EXPORT DirectoryInfoHasInvalidProjectSourceId : public ProjectStorageError { public: - ProjectDataHasInvalidProjectSourceId(); + DirectoryInfoHasInvalidProjectSourceId(); const char *what() const noexcept override; }; -class QMLDESIGNERCORE_EXPORT ProjectDataHasInvalidSourceId : public ProjectStorageError +class QMLDESIGNERCORE_EXPORT DirectoryInfoHasInvalidSourceId : public ProjectStorageError { public: - ProjectDataHasInvalidSourceId(); + DirectoryInfoHasInvalidSourceId(); const char *what() const noexcept override; }; -class QMLDESIGNERCORE_EXPORT ProjectDataHasInvalidModuleId : public ProjectStorageError +class QMLDESIGNERCORE_EXPORT DirectoryInfoHasInvalidModuleId : public ProjectStorageError { public: - ProjectDataHasInvalidModuleId(); + DirectoryInfoHasInvalidModuleId(); const char *what() const noexcept override; }; diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinterface.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinterface.h index 2a1415e7918..fcaccd932ff 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinterface.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinterface.h @@ -80,8 +80,8 @@ public: virtual bool isBasedOn(TypeId, TypeId, TypeId, TypeId, TypeId, TypeId, TypeId, TypeId) const = 0; virtual FileStatus fetchFileStatus(SourceId sourceId) const = 0; - virtual Storage::Synchronization::ProjectDatas fetchProjectDatas(SourceId sourceId) const = 0; - virtual std::optional fetchProjectData(SourceId sourceId) const = 0; + virtual Storage::Synchronization::DirectoryInfos fetchDirectoryInfos(SourceId sourceId) const = 0; + virtual std::optional fetchDirectoryInfo(SourceId sourceId) const = 0; virtual SourceId propertyEditorPathId(TypeId typeId) const = 0; virtual const Storage::Info::CommonTypeCache &commonTypeCache() const = 0; diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstoragetypes.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstoragetypes.h index 8d810d94bd2..7eb3767219b 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstoragetypes.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstoragetypes.h @@ -1160,44 +1160,44 @@ public: using PropertyEditorQmlPaths = std::vector; -class ProjectData +class DirectoryInfo { public: - ProjectData(SourceId projectSourceId, SourceId sourceId, ModuleId moduleId, FileType fileType) - : projectSourceId{projectSourceId} + DirectoryInfo(SourceId directorySourceId, SourceId sourceId, ModuleId moduleId, FileType fileType) + : directorySourceId{directorySourceId} , sourceId{sourceId} , moduleId{moduleId} , fileType{fileType} {} - friend bool operator==(const ProjectData &first, const ProjectData &second) + friend bool operator==(const DirectoryInfo &first, const DirectoryInfo &second) { - return first.projectSourceId == second.projectSourceId && first.sourceId == second.sourceId + return first.directorySourceId == second.directorySourceId && first.sourceId == second.sourceId && first.moduleId.internalId() == second.moduleId.internalId() && first.fileType == second.fileType; } template - friend void convertToString(String &string, const ProjectData &projectData) + friend void convertToString(String &string, const DirectoryInfo &directoryInfo) { using NanotraceHR::dictonary; using NanotraceHR::keyValue; - auto dict = dictonary(keyValue("project source id", projectData.projectSourceId), - keyValue("source id", projectData.sourceId), - keyValue("module id", projectData.moduleId), - keyValue("file type", projectData.fileType)); + auto dict = dictonary(keyValue("project source id", directoryInfo.directorySourceId), + keyValue("source id", directoryInfo.sourceId), + keyValue("module id", directoryInfo.moduleId), + keyValue("file type", directoryInfo.fileType)); convertToString(string, dict); } public: - SourceId projectSourceId; + SourceId directorySourceId; SourceId sourceId; ModuleId moduleId; FileType fileType; }; -using ProjectDatas = std::vector; +using DirectoryInfos = std::vector; class TypeAnnotation { @@ -1291,8 +1291,8 @@ public: , fileStatuses(std::move(fileStatuses)) {} - SynchronizationPackage(SourceIds updatedProjectSourceIds, ProjectDatas projectDatas) - : projectDatas(std::move(projectDatas)) + SynchronizationPackage(SourceIds updatedProjectSourceIds, DirectoryInfos directoryInfos) + : directoryInfos(std::move(directoryInfos)) , updatedProjectSourceIds(std::move(updatedProjectSourceIds)) {} @@ -1302,7 +1302,7 @@ public: SourceIds updatedSourceIds; SourceIds updatedFileStatusSourceIds; FileStatuses fileStatuses; - ProjectDatas projectDatas; + DirectoryInfos directoryInfos; SourceIds updatedProjectSourceIds; Imports moduleDependencies; SourceIds updatedModuleDependencySourceIds; diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.cpp b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.cpp index 509ffde99a9..e6f347ff12a 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.cpp +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.cpp @@ -134,13 +134,13 @@ SourceIds filterNotUpdatedSourceIds(SourceIds updatedSourceIds, SourceIds notUpd } void addSourceIds(SourceIds &sourceIds, - const Storage::Synchronization::ProjectDatas &projectDatas, + const Storage::Synchronization::DirectoryInfos &directoryInfos, TracerLiteral message, Tracer &tracer) { - for (const auto &projectData : projectDatas) { - tracer.tick(message, keyValue("source id", projectData.sourceId)); - sourceIds.push_back(projectData.sourceId); + for (const auto &directoryInfo : directoryInfos) { + tracer.tick(message, keyValue("source id", directoryInfo.sourceId)); + sourceIds.push_back(directoryInfo.sourceId); } } @@ -311,19 +311,17 @@ void ProjectStorageUpdater::updateQmlTypes(const QStringList &qmlTypesPaths, keyValue("source id", sourceId), keyValue("qml types path", qmlTypesPath)); - Storage::Synchronization::ProjectData projectData{sourceId, - sourceId, - moduleId, - Storage::Synchronization::FileType::QmlTypes}; + Storage::Synchronization::DirectoryInfo directoryInfo{ + sourceId, sourceId, moduleId, Storage::Synchronization::FileType::QmlTypes}; - FileState state = parseTypeInfo(projectData, + FileState state = parseTypeInfo(directoryInfo, Utils::PathString{qmlTypesPath}, package, notUpdatedSourceIds); if (state == FileState::Changed) { - tracer.tick("append project data"_t, keyValue("project data", projectData)); - package.projectDatas.push_back(std::move(projectData)); + tracer.tick("append project data"_t, keyValue("project data", directoryInfo)); + package.directoryInfos.push_back(std::move(directoryInfo)); tracer.tick("append updated project source ids"_t, keyValue("source id", sourceId)); package.updatedProjectSourceIds.push_back(sourceId); } @@ -382,10 +380,10 @@ void ProjectStorageUpdater::updateDirectoryChanged(std::string_view directoryPat tracer.tick("append updated module id"_t, keyValue("module id", moduleId)); package.updatedModuleIds.push_back(moduleId); - const auto qmlProjectDatas = m_projectStorage.fetchProjectDatas(directorySourceId); - addSourceIds(package.updatedSourceIds, qmlProjectDatas, "append updated source id"_t, tracer); + const auto qmlDirectoryInfos = m_projectStorage.fetchDirectoryInfos(directorySourceId); + addSourceIds(package.updatedSourceIds, qmlDirectoryInfos, "append updated source id"_t, tracer); addSourceIds(package.updatedFileStatusSourceIds, - qmlProjectDatas, + qmlDirectoryInfos, "append updated file status source id"_t, tracer); @@ -463,10 +461,10 @@ void ProjectStorageUpdater::updateDirectory(const Utils::PathString &directoryPa case FileState::NotChanged: { tracer.tick("update directory not changed"_t); - parseProjectDatas(m_projectStorage.fetchProjectDatas(directorySourceId), - package, - notUpdatedSourceIds, - watchedSourceIdsIds); + parseDirectoryInfos(m_projectStorage.fetchDirectoryInfos(directorySourceId), + package, + notUpdatedSourceIds, + watchedSourceIdsIds); break; } case FileState::NotExists: { @@ -476,13 +474,13 @@ void ProjectStorageUpdater::updateDirectory(const Utils::PathString &directoryPa package.updatedFileStatusSourceIds.push_back(qmldirSourceId); package.updatedProjectSourceIds.push_back(directorySourceId); package.updatedSourceIds.push_back(qmldirSourceId); - auto qmlProjectDatas = m_projectStorage.fetchProjectDatas(directorySourceId); - for (const Storage::Synchronization::ProjectData &projectData : qmlProjectDatas) { - tracer.tick("append updated source id"_t, keyValue("source id", projectData.sourceId)); - package.updatedSourceIds.push_back(projectData.sourceId); + auto qmlDirectoryInfos = m_projectStorage.fetchDirectoryInfos(directorySourceId); + for (const Storage::Synchronization::DirectoryInfo &directoryInfo : qmlDirectoryInfos) { + tracer.tick("append updated source id"_t, keyValue("source id", directoryInfo.sourceId)); + package.updatedSourceIds.push_back(directoryInfo.sourceId); tracer.tick("append updated file status source id"_t, - keyValue("source id", projectData.sourceId)); - package.updatedFileStatusSourceIds.push_back(projectData.sourceId); + keyValue("source id", directoryInfo.sourceId)); + package.updatedFileStatusSourceIds.push_back(directoryInfo.sourceId); } break; @@ -802,9 +800,9 @@ void ProjectStorageUpdater::pathsWithIdsChanged(const std::vector &chan for (SourceId sourceId : filterUniqueSourceIds(std::move(qmltypesSourceIds))) { if (!contains(directorySourceContextIds, m_pathCache.sourceContextId(sourceId))) { auto qmltypesPath = m_pathCache.sourcePath(sourceId); - auto projectData = m_projectStorage.fetchProjectData(sourceId); - if (projectData) - parseTypeInfo(*projectData, qmltypesPath, package, notUpdatedSourceIds); + auto directoryInfo = m_projectStorage.fetchDirectoryInfo(sourceId); + if (directoryInfo) + parseTypeInfo(*directoryInfo, qmltypesPath, package, notUpdatedSourceIds); } } } catch (const QmlDesigner::CannotParseQmlTypesFile &) { @@ -869,41 +867,42 @@ void ProjectStorageUpdater::parseTypeInfos(const QStringList &typeInfos, tracer.tick("append module dependenct source source id"_t, keyValue("source id", sourceId)); package.updatedModuleDependencySourceIds.push_back(sourceId); - auto projectData = package.projectDatas.emplace_back( + auto directoryInfo = package.directoryInfos.emplace_back( directorySourceId, sourceId, moduleId, Storage::Synchronization::FileType::QmlTypes); tracer.tick("append project data"_t, keyValue("source id", sourceId)); - parseTypeInfo(projectData, qmltypesPath, package, notUpdatedSourceIds); + parseTypeInfo(directoryInfo, qmltypesPath, package, notUpdatedSourceIds); } } -void ProjectStorageUpdater::parseProjectDatas(const Storage::Synchronization::ProjectDatas &projectDatas, - Storage::Synchronization::SynchronizationPackage &package, - NotUpdatedSourceIds ¬UpdatedSourceIds, - WatchedSourceIdsIds &watchedSourceIds) +void ProjectStorageUpdater::parseDirectoryInfos( + const Storage::Synchronization::DirectoryInfos &directoryInfos, + Storage::Synchronization::SynchronizationPackage &package, + NotUpdatedSourceIds ¬UpdatedSourceIds, + WatchedSourceIdsIds &watchedSourceIds) { NanotraceHR::Tracer tracer{"parse project datas"_t, category()}; - for (const Storage::Synchronization::ProjectData &projectData : projectDatas) { - switch (projectData.fileType) { + for (const Storage::Synchronization::DirectoryInfo &directoryInfo : directoryInfos) { + switch (directoryInfo.fileType) { case Storage::Synchronization::FileType::QmlTypes: { - watchedSourceIds.qmltypesSourceIds.push_back(projectData.sourceId); + watchedSourceIds.qmltypesSourceIds.push_back(directoryInfo.sourceId); - auto qmltypesPath = m_pathCache.sourcePath(projectData.sourceId); - parseTypeInfo(projectData, qmltypesPath, package, notUpdatedSourceIds); + auto qmltypesPath = m_pathCache.sourcePath(directoryInfo.sourceId); + parseTypeInfo(directoryInfo, qmltypesPath, package, notUpdatedSourceIds); break; } case Storage::Synchronization::FileType::QmlDocument: { - watchedSourceIds.qmlSourceIds.push_back(projectData.sourceId); + watchedSourceIds.qmlSourceIds.push_back(directoryInfo.sourceId); - parseQmlComponent(projectData.sourceId, package, notUpdatedSourceIds); + parseQmlComponent(directoryInfo.sourceId, package, notUpdatedSourceIds); break; } } } } -auto ProjectStorageUpdater::parseTypeInfo(const Storage::Synchronization::ProjectData &projectData, +auto ProjectStorageUpdater::parseTypeInfo(const Storage::Synchronization::DirectoryInfo &directoryInfo, Utils::SmallStringView qmltypesPath, Storage::Synchronization::SynchronizationPackage &package, NotUpdatedSourceIds ¬UpdatedSourceIds) -> FileState @@ -912,19 +911,19 @@ auto ProjectStorageUpdater::parseTypeInfo(const Storage::Synchronization::Projec category(), keyValue("qmltypes path", qmltypesPath)}; - auto state = fileState(projectData.sourceId, package, notUpdatedSourceIds); + auto state = fileState(directoryInfo.sourceId, package, notUpdatedSourceIds); switch (state) { case FileState::Changed: { - tracer.tick("append updated source ids"_t, keyValue("source id", projectData.sourceId)); - package.updatedSourceIds.push_back(projectData.sourceId); + tracer.tick("append updated source ids"_t, keyValue("source id", directoryInfo.sourceId)); + package.updatedSourceIds.push_back(directoryInfo.sourceId); const auto content = m_fileSystem.contentAsQString(QString{qmltypesPath}); - m_qmlTypesParser.parse(content, package.imports, package.types, projectData); + m_qmlTypesParser.parse(content, package.imports, package.types, directoryInfo); break; } case FileState::NotChanged: { - tracer.tick("append not updated source ids"_t, keyValue("source id", projectData.sourceId)); - notUpdatedSourceIds.sourceIds.push_back(projectData.sourceId); + tracer.tick("append not updated source ids"_t, keyValue("source id", directoryInfo.sourceId)); + notUpdatedSourceIds.sourceIds.push_back(directoryInfo.sourceId); break; } case FileState::NotExists: @@ -971,9 +970,9 @@ void ProjectStorageUpdater::parseQmlComponent(Utils::SmallStringView relativeFil tracer.tick("append not updated source id"_t, keyValue("source id", sourceId)); notUpdatedSourceIds.sourceIds.emplace_back(sourceId); - const auto &projectData = package.projectDatas.emplace_back( + const auto &directoryInfo = package.directoryInfos.emplace_back( directorySourceId, sourceId, ModuleId{}, Storage::Synchronization::FileType::QmlDocument); - tracer.tick("append project data"_t, keyValue("project data", projectData)); + tracer.tick("append project data"_t, keyValue("project data", directoryInfo)); return; } @@ -987,9 +986,9 @@ void ProjectStorageUpdater::parseQmlComponent(Utils::SmallStringView relativeFil break; } - const auto &projectData = package.projectDatas.emplace_back( + const auto &directoryInfo = package.directoryInfos.emplace_back( directorySourceId, sourceId, ModuleId{}, Storage::Synchronization::FileType::QmlDocument); - tracer.tick("append project data"_t, keyValue("project data", projectData)); + tracer.tick("append project data"_t, keyValue("project data", directoryInfo)); tracer.tick("append updated source id"_t, keyValue("source id", sourceId)); package.updatedSourceIds.push_back(sourceId); diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.h index bbaa71a2891..3cb7d24c3f6 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.h @@ -192,11 +192,11 @@ private: Storage::Synchronization::SynchronizationPackage &package, NotUpdatedSourceIds ¬UpdatedSourceIds, WatchedSourceIdsIds &watchedSourceIdsIds); - void parseProjectDatas(const Storage::Synchronization::ProjectDatas &projectDatas, - Storage::Synchronization::SynchronizationPackage &package, - NotUpdatedSourceIds ¬UpdatedSourceIds, - WatchedSourceIdsIds &watchedSourceIdsIds); - FileState parseTypeInfo(const Storage::Synchronization::ProjectData &projectData, + void parseDirectoryInfos(const Storage::Synchronization::DirectoryInfos &directoryInfos, + Storage::Synchronization::SynchronizationPackage &package, + NotUpdatedSourceIds ¬UpdatedSourceIds, + WatchedSourceIdsIds &watchedSourceIdsIds); + FileState parseTypeInfo(const Storage::Synchronization::DirectoryInfo &directoryInfo, Utils::SmallStringView qmltypesPath, Storage::Synchronization::SynchronizationPackage &package, NotUpdatedSourceIds ¬UpdatedSourceIds); diff --git a/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.cpp b/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.cpp index 66b40d76ca8..b3ec4f00242 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.cpp +++ b/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.cpp @@ -495,7 +495,7 @@ bool skipType(const QQmlJSExportedScope &object, Utils::span &objects, QmlTypesParser::ProjectStorage &storage, const ComponentWithoutNamespaces &componentNameWithoutNamespaces) @@ -503,15 +503,15 @@ void addTypes(Storage::Synchronization::Types &types, NanotraceHR::Tracer tracer{"add types"_t, category()}; types.reserve(Utils::usize(objects) + types.size()); - const auto skipList = getSkipList(storage.module(projectData.moduleId)); + const auto skipList = getSkipList(storage.module(directoryInfo.moduleId)); for (const auto &object : objects) { if (skipType(object, skipList)) continue; addType(types, - projectData.sourceId, - projectData.moduleId, + directoryInfo.sourceId, + directoryInfo.moduleId, object, storage, componentNameWithoutNamespaces); @@ -523,7 +523,7 @@ void addTypes(Storage::Synchronization::Types &types, void QmlTypesParser::parse(const QString &sourceContent, Storage::Imports &imports, Storage::Synchronization::Types &types, - const Storage::Synchronization::ProjectData &projectData) + const Storage::Synchronization::DirectoryInfo &directoryInfo) { NanotraceHR::Tracer tracer{"qmltypes parser parse"_t, category()}; @@ -536,8 +536,8 @@ void QmlTypesParser::parse(const QString &sourceContent, auto componentNameWithoutNamespaces = createComponentNameWithoutNamespaces(components); - addImports(imports, projectData.sourceId, dependencies, m_storage, projectData.moduleId); - addTypes(types, projectData, components, m_storage, componentNameWithoutNamespaces); + addImports(imports, directoryInfo.sourceId, dependencies, m_storage, directoryInfo.moduleId); + addTypes(types, directoryInfo, components, m_storage, componentNameWithoutNamespaces); } #else @@ -545,7 +545,7 @@ void QmlTypesParser::parse(const QString &sourceContent, void QmlTypesParser::parse([[maybe_unused]] const QString &sourceContent, [[maybe_unused]] Storage::Imports &imports, [[maybe_unused]] Storage::Synchronization::Types &types, - [[maybe_unused]] const Storage::Synchronization::ProjectData &projectData) + [[maybe_unused]] const Storage::Synchronization::DirectoryInfo &directoryInfo) {} #endif diff --git a/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.h b/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.h index 4a6427501b3..c73a429f912 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.h @@ -31,7 +31,7 @@ public: void parse(const QString &sourceContent, Storage::Imports &imports, Storage::Synchronization::Types &types, - const Storage::Synchronization::ProjectData &projectData) override; + const Storage::Synchronization::DirectoryInfo &directoryInfo) override; private: #ifdef QDS_BUILD_QMLPARSER diff --git a/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparserinterface.h b/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparserinterface.h index cdc7cd54d7e..c0880cf5c64 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparserinterface.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparserinterface.h @@ -15,7 +15,7 @@ public: virtual void parse(const QString &sourceContent, Storage::Imports &imports, Storage::Synchronization::Types &types, - const Storage::Synchronization::ProjectData &projectData) + const Storage::Synchronization::DirectoryInfo &directoryInfo) = 0; protected: diff --git a/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp b/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp index 9602bf050fa..7954b6901bb 100644 --- a/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp +++ b/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp @@ -210,10 +210,10 @@ std::unique_ptr createProjectStorageData(::ProjectExplorer:: } } // namespace -class QmlDesignerProjectManager::QmlDesignerProjectManagerProjectData +class QmlDesignerProjectManager::QmlDesignerProjectManagerDirectoryInfo { public: - QmlDesignerProjectManagerProjectData(ImageCacheStorage &storage, + QmlDesignerProjectManagerDirectoryInfo(ImageCacheStorage &storage, ::ProjectExplorer::Project *project, ExternalDependenciesInterface &externalDependencies) : collector{connectionManager, @@ -297,8 +297,8 @@ namespace { ProjectStorageDependencies QmlDesignerProjectManager::projectStorageDependencies() { if constexpr (useProjectStorage()) { - return {m_projectData->projectStorageData->storage, - m_projectData->projectStorageData->pathCache}; + return {m_directoryInfo->projectStorageData->storage, + m_directoryInfo->projectStorageData->pathCache}; } else { return {*dummyProjectStorage(), *dummyPathCache()}; } @@ -499,10 +499,10 @@ QString qtCreatorItemLibraryPath() void QmlDesignerProjectManager::projectAdded(::ProjectExplorer::Project *project) { - m_projectData = std::make_unique(m_previewImageCacheData->storage, + m_directoryInfo = std::make_unique(m_previewImageCacheData->storage, project, m_externalDependencies); - m_projectData->activeTarget = project->activeTarget(); + m_directoryInfo->activeTarget = project->activeTarget(); QObject::connect(project, &::ProjectExplorer::Project::fileListChanged, [&]() { fileListChanged(); @@ -522,9 +522,9 @@ void QmlDesignerProjectManager::projectAdded(::ProjectExplorer::Project *project void QmlDesignerProjectManager::aboutToRemoveProject(::ProjectExplorer::Project *) { - if (m_projectData) { - m_previewImageCacheData->collector.setTarget(m_projectData->activeTarget); - m_projectData.reset(); + if (m_directoryInfo) { + m_previewImageCacheData->collector.setTarget(m_directoryInfo->activeTarget); + m_directoryInfo.reset(); } } @@ -532,14 +532,14 @@ void QmlDesignerProjectManager::projectRemoved(::ProjectExplorer::Project *) {} void QmlDesignerProjectManager::generatePreview() { - if (!m_projectData || !m_projectData->activeTarget) + if (!m_directoryInfo || !m_directoryInfo->activeTarget) return; ::QmlProjectManager::QmlBuildSystem *qmlBuildSystem = getQmlBuildSystem( - m_projectData->activeTarget); + m_directoryInfo->activeTarget); if (qmlBuildSystem) { - m_previewImageCacheData->collector.setTarget(m_projectData->activeTarget); + m_previewImageCacheData->collector.setTarget(m_directoryInfo->activeTarget); m_previewImageCacheData->factory.generate(qmlBuildSystem->mainFilePath().toString().toUtf8()); } } @@ -587,12 +587,12 @@ void QmlDesignerProjectManager::fileListChanged() void QmlDesignerProjectManager::activeTargetChanged(ProjectExplorer::Target *target) { - if (!m_projectData || !m_projectData->projectStorageData) + if (!m_directoryInfo || !m_directoryInfo->projectStorageData) return; - QObject::disconnect(m_projectData->activeTarget, nullptr, nullptr, nullptr); + QObject::disconnect(m_directoryInfo->activeTarget, nullptr, nullptr, nullptr); - m_projectData->activeTarget = target; + m_directoryInfo->activeTarget = target; if (target) { QObject::connect(target, &::ProjectExplorer::Target::kitChanged, [&]() { kitChanged(); }); @@ -625,17 +625,17 @@ void QmlDesignerProjectManager::projectChanged() void QmlDesignerProjectManager::update() { - if (!m_projectData || !m_projectData->projectStorageData) + if (!m_directoryInfo || !m_directoryInfo->projectStorageData) return; if constexpr (isUsingQmlDesignerLite()) { - m_projectData->projectStorageData->updater.update(directoriesForLiteDesigner(), + m_directoryInfo->projectStorageData->updater.update(directoriesForLiteDesigner(), qmlTypesForLiteDesigner(), propertyEditorResourcesPath(), {qtCreatorItemLibraryPath()}); } else { - m_projectData->projectStorageData->updater.update(directories(m_projectData->activeTarget), - qmlTypes(m_projectData->activeTarget), + m_directoryInfo->projectStorageData->updater.update(directories(m_directoryInfo->activeTarget), + qmlTypes(m_directoryInfo->activeTarget), propertyEditorResourcesPath(), {qtCreatorItemLibraryPath()}); } diff --git a/src/plugins/qmldesigner/qmldesignerprojectmanager.h b/src/plugins/qmldesigner/qmldesignerprojectmanager.h index bd45bf16c9a..3024d041b5f 100644 --- a/src/plugins/qmldesigner/qmldesignerprojectmanager.h +++ b/src/plugins/qmldesigner/qmldesignerprojectmanager.h @@ -32,7 +32,7 @@ class QmlDesignerProjectManager : public QObject { Q_OBJECT - class QmlDesignerProjectManagerProjectData; + class QmlDesignerProjectManagerDirectoryInfo; class PreviewImageCacheData; class ImageCacheData; @@ -68,7 +68,7 @@ private: std::once_flag imageCacheFlag; std::unique_ptr m_imageCacheData; std::unique_ptr m_previewImageCacheData; - std::unique_ptr m_projectData; + std::unique_ptr m_directoryInfo; ExternalDependenciesInterface &m_externalDependencies; }; } // namespace QmlDesigner diff --git a/tests/unit/tests/mocks/projectstoragemock.h b/tests/unit/tests/mocks/projectstoragemock.h index dad9f4db100..a0e880bc09e 100644 --- a/tests/unit/tests/mocks/projectstoragemock.h +++ b/tests/unit/tests/mocks/projectstoragemock.h @@ -300,13 +300,13 @@ public: (QmlDesigner::SourceId sourceId), (const, override)); - MOCK_METHOD(QmlDesigner::Storage::Synchronization::ProjectDatas, - fetchProjectDatas, + MOCK_METHOD(QmlDesigner::Storage::Synchronization::DirectoryInfos, + fetchDirectoryInfos, (QmlDesigner::SourceId sourceId), (const, override)); - MOCK_METHOD(std::optional, - fetchProjectData, + MOCK_METHOD(std::optional, + fetchDirectoryInfo, (QmlDesigner::SourceId sourceId), (const, override)); diff --git a/tests/unit/tests/mocks/qmltypesparsermock.h b/tests/unit/tests/mocks/qmltypesparsermock.h index e3fa1ca6053..0f57c634d07 100644 --- a/tests/unit/tests/mocks/qmltypesparsermock.h +++ b/tests/unit/tests/mocks/qmltypesparsermock.h @@ -15,6 +15,6 @@ public: (const QString &sourceContent, QmlDesigner::Storage::Imports &imports, QmlDesigner::Storage::Synchronization::Types &types, - const QmlDesigner::Storage::Synchronization::ProjectData &projectData), + const QmlDesigner::Storage::Synchronization::DirectoryInfo &directoryInfo), (override)); }; diff --git a/tests/unit/tests/printers/gtest-creator-printing.cpp b/tests/unit/tests/printers/gtest-creator-printing.cpp index 88b17e59309..165f54ebfbd 100644 --- a/tests/unit/tests/printers/gtest-creator-printing.cpp +++ b/tests/unit/tests/printers/gtest-creator-printing.cpp @@ -792,7 +792,7 @@ std::ostream &operator<<(std::ostream &out, const SynchronizationPackage &packag << ", fileStatuses: " << package.fileStatuses << ", updatedFileStatusSourceIds: " << package.updatedFileStatusSourceIds << ", updatedProjectSourceIds: " << package.updatedProjectSourceIds - << ", projectDatas: " << package.projectDatas + << ", directoryInfos: " << package.directoryInfos << ", propertyEditorQmlPaths: " << package.propertyEditorQmlPaths << ", updatedPropertyEditorQmlPathSourceIds: " << package.updatedPropertyEditorQmlPathSourceIds @@ -801,9 +801,9 @@ std::ostream &operator<<(std::ostream &out, const SynchronizationPackage &packag << ")"; } -std::ostream &operator<<(std::ostream &out, const ProjectData &data) +std::ostream &operator<<(std::ostream &out, const DirectoryInfo &data) { - return out << "(" << data.projectSourceId << ", " << data.sourceId << ", " << data.moduleId + return out << "(" << data.directorySourceId << ", " << data.sourceId << ", " << data.moduleId << ", " << data.fileType << ")"; } diff --git a/tests/unit/tests/printers/gtest-creator-printing.h b/tests/unit/tests/printers/gtest-creator-printing.h index 8d0c77888ec..2444b9d98b5 100644 --- a/tests/unit/tests/printers/gtest-creator-printing.h +++ b/tests/unit/tests/printers/gtest-creator-printing.h @@ -207,7 +207,7 @@ class EnumeratorDeclaration; enum class ImportKind : char; enum class IsAutoVersion : char; enum class IsQualified : int; -class ProjectData; +class DirectoryInfo; class SynchronizationPackage; enum class FileType : char; enum class ChangeLevel : char; @@ -227,7 +227,7 @@ std::ostream &operator<<(std::ostream &out, const EnumerationDeclaration &enumer std::ostream &operator<<(std::ostream &out, const EnumeratorDeclaration &enumeratorDeclaration); std::ostream &operator<<(std::ostream &out, const ImportKind &importKind); std::ostream &operator<<(std::ostream &out, IsQualified isQualified); -std::ostream &operator<<(std::ostream &out, const ProjectData &data); +std::ostream &operator<<(std::ostream &out, const DirectoryInfo &data); std::ostream &operator<<(std::ostream &out, const SynchronizationPackage &package); std::ostream &operator<<(std::ostream &out, FileType fileType); std::ostream &operator<<(std::ostream &out, ChangeLevel changeLevel); diff --git a/tests/unit/tests/unittests/projectstorage/projectstorage-test.cpp b/tests/unit/tests/unittests/projectstorage/projectstorage-test.cpp index cc3b4b705d3..b6a56cdda08 100644 --- a/tests/unit/tests/unittests/projectstorage/projectstorage-test.cpp +++ b/tests/unit/tests/unittests/projectstorage/projectstorage-test.cpp @@ -5123,220 +5123,220 @@ TEST_F(ProjectStorage, populate_module_cache) ASSERT_THAT(newStorage.module(id), IsModule("Qml", ModuleKind::QmlLibrary)); } -TEST_F(ProjectStorage, add_project_dataes) +TEST_F(ProjectStorage, add_directory_infoes) { - Storage::Synchronization::ProjectData projectData1{qmlProjectSourceId, + Storage::Synchronization::DirectoryInfo directoryInfo1{qmlProjectSourceId, sourceId1, qmlModuleId, Storage::Synchronization::FileType::QmlDocument}; - Storage::Synchronization::ProjectData projectData2{qmlProjectSourceId, + Storage::Synchronization::DirectoryInfo directoryInfo2{qmlProjectSourceId, sourceId2, qmlModuleId, Storage::Synchronization::FileType::QmlDocument}; - Storage::Synchronization::ProjectData projectData3{qtQuickProjectSourceId, + Storage::Synchronization::DirectoryInfo directoryInfo3{qtQuickProjectSourceId, sourceId3, qtQuickModuleId, Storage::Synchronization::FileType::QmlTypes}; storage.synchronize(SynchronizationPackage{{qmlProjectSourceId, qtQuickProjectSourceId}, - {projectData1, projectData2, projectData3}}); + {directoryInfo1, directoryInfo2, directoryInfo3}}); - ASSERT_THAT(storage.fetchProjectDatas({qmlProjectSourceId, qtQuickProjectSourceId}), - UnorderedElementsAre(projectData1, projectData2, projectData3)); + ASSERT_THAT(storage.fetchDirectoryInfos({qmlProjectSourceId, qtQuickProjectSourceId}), + UnorderedElementsAre(directoryInfo1, directoryInfo2, directoryInfo3)); } -TEST_F(ProjectStorage, remove_project_data) +TEST_F(ProjectStorage, remove_directory_info) { - Storage::Synchronization::ProjectData projectData1{qmlProjectSourceId, + Storage::Synchronization::DirectoryInfo directoryInfo1{qmlProjectSourceId, sourceId1, qmlModuleId, Storage::Synchronization::FileType::QmlDocument}; - Storage::Synchronization::ProjectData projectData2{qmlProjectSourceId, + Storage::Synchronization::DirectoryInfo directoryInfo2{qmlProjectSourceId, sourceId2, qmlModuleId, Storage::Synchronization::FileType::QmlDocument}; - Storage::Synchronization::ProjectData projectData3{qtQuickProjectSourceId, + Storage::Synchronization::DirectoryInfo directoryInfo3{qtQuickProjectSourceId, sourceId3, qtQuickModuleId, Storage::Synchronization::FileType::QmlTypes}; storage.synchronize(SynchronizationPackage{{qmlProjectSourceId, qtQuickProjectSourceId}, - {projectData1, projectData2, projectData3}}); + {directoryInfo1, directoryInfo2, directoryInfo3}}); storage.synchronize( - SynchronizationPackage{{qmlProjectSourceId, qtQuickProjectSourceId}, {projectData1}}); + SynchronizationPackage{{qmlProjectSourceId, qtQuickProjectSourceId}, {directoryInfo1}}); - ASSERT_THAT(storage.fetchProjectDatas({qmlProjectSourceId, qtQuickProjectSourceId}), - UnorderedElementsAre(projectData1)); + ASSERT_THAT(storage.fetchDirectoryInfos({qmlProjectSourceId, qtQuickProjectSourceId}), + UnorderedElementsAre(directoryInfo1)); } -TEST_F(ProjectStorage, update_project_data_file_type) +TEST_F(ProjectStorage, update_directory_info_file_type) { - Storage::Synchronization::ProjectData projectData1{qmlProjectSourceId, + Storage::Synchronization::DirectoryInfo directoryInfo1{qmlProjectSourceId, sourceId1, qmlModuleId, Storage::Synchronization::FileType::QmlDocument}; - Storage::Synchronization::ProjectData projectData2{qmlProjectSourceId, + Storage::Synchronization::DirectoryInfo directoryInfo2{qmlProjectSourceId, sourceId2, qmlModuleId, Storage::Synchronization::FileType::QmlDocument}; - Storage::Synchronization::ProjectData projectData2b{qmlProjectSourceId, + Storage::Synchronization::DirectoryInfo directoryInfo2b{qmlProjectSourceId, sourceId2, qmlModuleId, Storage::Synchronization::FileType::QmlTypes}; - Storage::Synchronization::ProjectData projectData3{qtQuickProjectSourceId, + Storage::Synchronization::DirectoryInfo directoryInfo3{qtQuickProjectSourceId, sourceId3, qtQuickModuleId, Storage::Synchronization::FileType::QmlTypes}; storage.synchronize(SynchronizationPackage{{qmlProjectSourceId, qtQuickProjectSourceId}, - {projectData1, projectData2, projectData3}}); + {directoryInfo1, directoryInfo2, directoryInfo3}}); - storage.synchronize(SynchronizationPackage{{qmlProjectSourceId}, {projectData1, projectData2b}}); + storage.synchronize(SynchronizationPackage{{qmlProjectSourceId}, {directoryInfo1, directoryInfo2b}}); - ASSERT_THAT(storage.fetchProjectDatas({qmlProjectSourceId, qtQuickProjectSourceId}), - UnorderedElementsAre(projectData1, projectData2b, projectData3)); + ASSERT_THAT(storage.fetchDirectoryInfos({qmlProjectSourceId, qtQuickProjectSourceId}), + UnorderedElementsAre(directoryInfo1, directoryInfo2b, directoryInfo3)); } -TEST_F(ProjectStorage, update_project_data_module_id) +TEST_F(ProjectStorage, update_directory_info_module_id) { - Storage::Synchronization::ProjectData projectData1{qmlProjectSourceId, + Storage::Synchronization::DirectoryInfo directoryInfo1{qmlProjectSourceId, sourceId1, qmlModuleId, Storage::Synchronization::FileType::QmlDocument}; - Storage::Synchronization::ProjectData projectData2{qmlProjectSourceId, + Storage::Synchronization::DirectoryInfo directoryInfo2{qmlProjectSourceId, sourceId3, qmlModuleId, Storage::Synchronization::FileType::QmlDocument}; - Storage::Synchronization::ProjectData projectData2b{qmlProjectSourceId, + Storage::Synchronization::DirectoryInfo directoryInfo2b{qmlProjectSourceId, sourceId3, qtQuickModuleId, Storage::Synchronization::FileType::QmlDocument}; - Storage::Synchronization::ProjectData projectData3{qtQuickProjectSourceId, + Storage::Synchronization::DirectoryInfo directoryInfo3{qtQuickProjectSourceId, sourceId2, qtQuickModuleId, Storage::Synchronization::FileType::QmlTypes}; storage.synchronize(SynchronizationPackage{{qmlProjectSourceId, qtQuickProjectSourceId}, - {projectData1, projectData2, projectData3}}); + {directoryInfo1, directoryInfo2, directoryInfo3}}); - storage.synchronize(SynchronizationPackage{{qmlProjectSourceId}, {projectData1, projectData2b}}); + storage.synchronize(SynchronizationPackage{{qmlProjectSourceId}, {directoryInfo1, directoryInfo2b}}); - ASSERT_THAT(storage.fetchProjectDatas({qmlProjectSourceId, qtQuickProjectSourceId}), - UnorderedElementsAre(projectData1, projectData2b, projectData3)); + ASSERT_THAT(storage.fetchDirectoryInfos({qmlProjectSourceId, qtQuickProjectSourceId}), + UnorderedElementsAre(directoryInfo1, directoryInfo2b, directoryInfo3)); } -TEST_F(ProjectStorage, throw_for_invalid_source_id_in_project_data) +TEST_F(ProjectStorage, throw_for_invalid_source_id_in_directory_info) { - Storage::Synchronization::ProjectData projectData1{qmlProjectSourceId, + Storage::Synchronization::DirectoryInfo directoryInfo1{qmlProjectSourceId, SourceId{}, qmlModuleId, Storage::Synchronization::FileType::QmlDocument}; - ASSERT_THROW(storage.synchronize(SynchronizationPackage{{qmlProjectSourceId}, {projectData1}}), - QmlDesigner::ProjectDataHasInvalidSourceId); + ASSERT_THROW(storage.synchronize(SynchronizationPackage{{qmlProjectSourceId}, {directoryInfo1}}), + QmlDesigner::DirectoryInfoHasInvalidSourceId); } -TEST_F(ProjectStorage, insert_project_data_with_invalid_module_id) +TEST_F(ProjectStorage, insert_directory_info_with_invalid_module_id) { - Storage::Synchronization::ProjectData projectData1{qmlProjectSourceId, + Storage::Synchronization::DirectoryInfo directoryInfo1{qmlProjectSourceId, sourceId1, ModuleId{}, Storage::Synchronization::FileType::QmlDocument}; - storage.synchronize(SynchronizationPackage{{qmlProjectSourceId}, {projectData1}}); + storage.synchronize(SynchronizationPackage{{qmlProjectSourceId}, {directoryInfo1}}); - ASSERT_THAT(storage.fetchProjectDatas({qmlProjectSourceId, qtQuickProjectSourceId}), - UnorderedElementsAre(projectData1)); + ASSERT_THAT(storage.fetchDirectoryInfos({qmlProjectSourceId, qtQuickProjectSourceId}), + UnorderedElementsAre(directoryInfo1)); } -TEST_F(ProjectStorage, update_project_data_with_invalid_module_id) +TEST_F(ProjectStorage, update_directory_info_with_invalid_module_id) { - Storage::Synchronization::ProjectData projectData1{qmlProjectSourceId, + Storage::Synchronization::DirectoryInfo directoryInfo1{qmlProjectSourceId, sourceId1, qmlModuleId, Storage::Synchronization::FileType::QmlDocument}; - storage.synchronize(SynchronizationPackage{{qmlProjectSourceId}, {projectData1}}); - projectData1.moduleId = ModuleId{}; + storage.synchronize(SynchronizationPackage{{qmlProjectSourceId}, {directoryInfo1}}); + directoryInfo1.moduleId = ModuleId{}; - storage.synchronize(SynchronizationPackage{{qmlProjectSourceId}, {projectData1}}); + storage.synchronize(SynchronizationPackage{{qmlProjectSourceId}, {directoryInfo1}}); - ASSERT_THAT(storage.fetchProjectDatas({qmlProjectSourceId, qtQuickProjectSourceId}), - UnorderedElementsAre(projectData1)); + ASSERT_THAT(storage.fetchDirectoryInfos({qmlProjectSourceId, qtQuickProjectSourceId}), + UnorderedElementsAre(directoryInfo1)); } -TEST_F(ProjectStorage, throw_for_updating_with_invalid_project_source_id_in_project_data) +TEST_F(ProjectStorage, throw_for_updating_with_invalid_project_source_id_in_directory_info) { - Storage::Synchronization::ProjectData projectData1{SourceId{}, + Storage::Synchronization::DirectoryInfo directoryInfo1{SourceId{}, sourceId1, qmlModuleId, Storage::Synchronization::FileType::QmlDocument}; - ASSERT_THROW(storage.synchronize(SynchronizationPackage{{qmlProjectSourceId}, {projectData1}}), - QmlDesigner::ProjectDataHasInvalidProjectSourceId); + ASSERT_THROW(storage.synchronize(SynchronizationPackage{{qmlProjectSourceId}, {directoryInfo1}}), + QmlDesigner::DirectoryInfoHasInvalidProjectSourceId); } -TEST_F(ProjectStorage, fetch_project_datas_by_directory_source_ids) +TEST_F(ProjectStorage, fetch_directory_infos_by_directory_source_ids) { - Storage::Synchronization::ProjectData projectData1{qmlProjectSourceId, + Storage::Synchronization::DirectoryInfo directoryInfo1{qmlProjectSourceId, sourceId1, qmlModuleId, Storage::Synchronization::FileType::QmlDocument}; - Storage::Synchronization::ProjectData projectData2{qmlProjectSourceId, + Storage::Synchronization::DirectoryInfo directoryInfo2{qmlProjectSourceId, sourceId2, qmlModuleId, Storage::Synchronization::FileType::QmlDocument}; - Storage::Synchronization::ProjectData projectData3{qtQuickProjectSourceId, + Storage::Synchronization::DirectoryInfo directoryInfo3{qtQuickProjectSourceId, sourceId3, qtQuickModuleId, Storage::Synchronization::FileType::QmlTypes}; storage.synchronize(SynchronizationPackage{{qmlProjectSourceId, qtQuickProjectSourceId}, - {projectData1, projectData2, projectData3}}); + {directoryInfo1, directoryInfo2, directoryInfo3}}); - auto projectDatas = storage.fetchProjectDatas({qmlProjectSourceId, qtQuickProjectSourceId}); + auto directoryInfos = storage.fetchDirectoryInfos({qmlProjectSourceId, qtQuickProjectSourceId}); - ASSERT_THAT(projectDatas, UnorderedElementsAre(projectData1, projectData2, projectData3)); + ASSERT_THAT(directoryInfos, UnorderedElementsAre(directoryInfo1, directoryInfo2, directoryInfo3)); } -TEST_F(ProjectStorage, fetch_project_datas_by_directory_source_id) +TEST_F(ProjectStorage, fetch_directory_infos_by_directory_source_id) { - Storage::Synchronization::ProjectData projectData1{qmlProjectSourceId, + Storage::Synchronization::DirectoryInfo directoryInfo1{qmlProjectSourceId, sourceId1, qmlModuleId, Storage::Synchronization::FileType::QmlDocument}; - Storage::Synchronization::ProjectData projectData2{qmlProjectSourceId, + Storage::Synchronization::DirectoryInfo directoryInfo2{qmlProjectSourceId, sourceId2, qmlModuleId, Storage::Synchronization::FileType::QmlDocument}; - Storage::Synchronization::ProjectData projectData3{qtQuickProjectSourceId, + Storage::Synchronization::DirectoryInfo directoryInfo3{qtQuickProjectSourceId, sourceId3, qtQuickModuleId, Storage::Synchronization::FileType::QmlTypes}; storage.synchronize(SynchronizationPackage{{qmlProjectSourceId, qtQuickProjectSourceId}, - {projectData1, projectData2, projectData3}}); + {directoryInfo1, directoryInfo2, directoryInfo3}}); - auto projectData = storage.fetchProjectDatas(qmlProjectSourceId); + auto directoryInfo = storage.fetchDirectoryInfos(qmlProjectSourceId); - ASSERT_THAT(projectData, UnorderedElementsAre(projectData1, projectData2)); + ASSERT_THAT(directoryInfo, UnorderedElementsAre(directoryInfo1, directoryInfo2)); } -TEST_F(ProjectStorage, fetch_project_data_by_source_ids) +TEST_F(ProjectStorage, fetch_directory_info_by_source_ids) { - Storage::Synchronization::ProjectData projectData1{qmlProjectSourceId, + Storage::Synchronization::DirectoryInfo directoryInfo1{qmlProjectSourceId, sourceId1, qmlModuleId, Storage::Synchronization::FileType::QmlDocument}; - Storage::Synchronization::ProjectData projectData2{qmlProjectSourceId, + Storage::Synchronization::DirectoryInfo directoryInfo2{qmlProjectSourceId, sourceId2, qmlModuleId, Storage::Synchronization::FileType::QmlDocument}; - Storage::Synchronization::ProjectData projectData3{qtQuickProjectSourceId, + Storage::Synchronization::DirectoryInfo directoryInfo3{qtQuickProjectSourceId, sourceId3, qtQuickModuleId, Storage::Synchronization::FileType::QmlTypes}; storage.synchronize(SynchronizationPackage{{qmlProjectSourceId, qtQuickProjectSourceId}, - {projectData1, projectData2, projectData3}}); + {directoryInfo1, directoryInfo2, directoryInfo3}}); - auto projectData = storage.fetchProjectData({sourceId2}); + auto directoryInfo = storage.fetchDirectoryInfo({sourceId2}); - ASSERT_THAT(projectData, Eq(projectData2)); + ASSERT_THAT(directoryInfo, Eq(directoryInfo2)); } TEST_F(ProjectStorage, exclude_exported_types) diff --git a/tests/unit/tests/unittests/projectstorage/projectstorageupdater-test.cpp b/tests/unit/tests/unittests/projectstorage/projectstorageupdater-test.cpp index 103571822c4..ae97c8b5abe 100644 --- a/tests/unit/tests/unittests/projectstorage/projectstorageupdater-test.cpp +++ b/tests/unit/tests/unittests/projectstorage/projectstorageupdater-test.cpp @@ -28,10 +28,10 @@ namespace Storage = QmlDesigner::Storage; using QmlDesigner::IdPaths; using QmlDesigner::Storage::Import; using QmlDesigner::Storage::ModuleKind; +using QmlDesigner::Storage::Synchronization::DirectoryInfo; using QmlDesigner::Storage::Synchronization::FileType; using QmlDesigner::Storage::Synchronization::IsAutoVersion; using QmlDesigner::Storage::Synchronization::ModuleExportedImport; -using QmlDesigner::Storage::Synchronization::ProjectData; using QmlDesigner::Storage::Synchronization::SynchronizationPackage; using QmlDesigner::Storage::TypeTraits; using QmlDesigner::Storage::TypeTraitsKind; @@ -96,21 +96,21 @@ MATCHER_P3(IsFileStatus, && fileStatus.lastModified == lastModified; } -MATCHER_P4(IsProjectData, - projectSourceId, +MATCHER_P4(IsDirectoryInfo, + directorySourceId, sourceId, moduleId, fileType, std::string(negation ? "isn't " : "is ") - + PrintToString(Storage::Synchronization::ProjectData{ - projectSourceId, sourceId, moduleId, fileType})) + + PrintToString(Storage::Synchronization::DirectoryInfo{ + directorySourceId, sourceId, moduleId, fileType})) { - const Storage::Synchronization::ProjectData &projectData = arg; + const Storage::Synchronization::DirectoryInfo &directoryInfo = arg; - return compareInvalidAreTrue(projectData.projectSourceId, projectSourceId) - && projectData.sourceId == sourceId - && compareInvalidAreTrue(projectData.moduleId, moduleId) - && projectData.fileType == fileType; + return compareInvalidAreTrue(directoryInfo.directorySourceId, directorySourceId) + && directoryInfo.sourceId == sourceId + && compareInvalidAreTrue(directoryInfo.moduleId, moduleId) + && directoryInfo.fileType == fileType; } MATCHER(PackageIsEmpty, std::string(negation ? "isn't empty" : "is empty")) @@ -118,7 +118,7 @@ MATCHER(PackageIsEmpty, std::string(negation ? "isn't empty" : "is empty")) const Storage::Synchronization::SynchronizationPackage &package = arg; return package.imports.empty() && package.types.empty() && package.fileStatuses.empty() - && package.updatedSourceIds.empty() && package.projectDatas.empty() + && package.updatedSourceIds.empty() && package.directoryInfos.empty() && package.updatedFileStatusSourceIds.empty() && package.updatedProjectSourceIds.empty() && package.moduleDependencies.empty() && package.updatedModuleDependencySourceIds.empty() && package.moduleExportedImports.empty() && package.updatedModuleIds.empty() @@ -282,14 +282,14 @@ public: ON_CALL(fileSystemMock, qmlFileNames(Eq(directoryPath))).WillByDefault(Return(qmlFileNames)); } - void setProjectDatas(SourceId directoryPathSourceId, - const QmlDesigner::Storage::Synchronization::ProjectDatas &projectDatas) + void setDirectoryInfos(SourceId directoryPathSourceId, + const QmlDesigner::Storage::Synchronization::DirectoryInfos &directoryInfos) { - ON_CALL(projectStorageMock, fetchProjectDatas(Eq(directoryPathSourceId))) - .WillByDefault(Return(projectDatas)); - for (const ProjectData &projectData : projectDatas) { - ON_CALL(projectStorageMock, fetchProjectData(Eq(projectData.sourceId))) - .WillByDefault(Return(std::optional{projectData})); + ON_CALL(projectStorageMock, fetchDirectoryInfos(Eq(directoryPathSourceId))) + .WillByDefault(Return(directoryInfos)); + for (const DirectoryInfo &directoryInfo : directoryInfos) { + ON_CALL(projectStorageMock, fetchDirectoryInfo(Eq(directoryInfo.sourceId))) + .WillByDefault(Return(std::optional{directoryInfo})); } } @@ -476,9 +476,9 @@ TEST_F(ProjectStorageUpdater, parse_qml_types) setContent(u"/path/example2.qmltypes", qmltypes2); EXPECT_CALL(qmlTypesParserMock, - parse(qmltypes, _, _, Field(&ProjectData::moduleId, exampleCppNativeModuleId))); + parse(qmltypes, _, _, Field(&DirectoryInfo::moduleId, exampleCppNativeModuleId))); EXPECT_CALL(qmlTypesParserMock, - parse(qmltypes2, _, _, Field(&ProjectData::moduleId, exampleCppNativeModuleId))); + parse(qmltypes2, _, _, Field(&DirectoryInfo::moduleId, exampleCppNativeModuleId))); updater.update(directories, {}, {}, {}); } @@ -516,11 +516,11 @@ TEST_F(ProjectStorageUpdater, synchronize_qml_types) Field(&SynchronizationPackage::fileStatuses, UnorderedElementsAre(IsFileStatus(qmlDirPathSourceId, 1, 21), IsFileStatus(qmltypesPathSourceId, 1, 21))), - Field(&SynchronizationPackage::projectDatas, - UnorderedElementsAre(IsProjectData(directoryPathSourceId, - qmltypesPathSourceId, - exampleCppNativeModuleId, - FileType::QmlTypes))), + Field(&SynchronizationPackage::directoryInfos, + UnorderedElementsAre(IsDirectoryInfo(directoryPathSourceId, + qmltypesPathSourceId, + exampleCppNativeModuleId, + FileType::QmlTypes))), Field(&SynchronizationPackage::updatedProjectSourceIds, UnorderedElementsAre(directoryPathSourceId))))); @@ -655,19 +655,19 @@ TEST_F(ProjectStorageUpdater, synchronize_qml_documents) IsFileStatus(qmlDocumentSourceId3, 1, 21))), Field(&SynchronizationPackage::updatedProjectSourceIds, UnorderedElementsAre(directoryPathSourceId)), - Field(&SynchronizationPackage::projectDatas, - UnorderedElementsAre(IsProjectData(directoryPathSourceId, - qmlDocumentSourceId1, - ModuleId{}, - FileType::QmlDocument), - IsProjectData(directoryPathSourceId, - qmlDocumentSourceId2, - ModuleId{}, - FileType::QmlDocument), - IsProjectData(directoryPathSourceId, - qmlDocumentSourceId3, - ModuleId{}, - FileType::QmlDocument)))))); + Field(&SynchronizationPackage::directoryInfos, + UnorderedElementsAre(IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId1, + ModuleId{}, + FileType::QmlDocument), + IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId2, + ModuleId{}, + FileType::QmlDocument), + IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId3, + ModuleId{}, + FileType::QmlDocument)))))); updater.update(directories, {}, {}, {}); } @@ -680,8 +680,8 @@ TEST_F(ProjectStorageUpdater, synchronize_add_only_qml_document_in_directory) setFilesChanged({directoryPathSourceId}); setFilesDontChanged({qmlDirPathSourceId, qmlDocumentSourceId1}); setFilesAdded({qmlDocumentSourceId2}); - setProjectDatas(directoryPathSourceId, - {{directoryPathSourceId, qmlDocumentSourceId1, ModuleId{}, FileType::QmlDocument}}); + setDirectoryInfos(directoryPathSourceId, + {{directoryPathSourceId, qmlDocumentSourceId1, ModuleId{}, FileType::QmlDocument}}); setQmlFileNames(u"/path", {"First.qml", "First2.qml"}); EXPECT_CALL(projectStorageMock, @@ -715,15 +715,15 @@ TEST_F(ProjectStorageUpdater, synchronize_add_only_qml_document_in_directory) IsFileStatus(directoryPathSourceId, 1, 21))), Field(&SynchronizationPackage::updatedProjectSourceIds, UnorderedElementsAre(directoryPathSourceId)), - Field(&SynchronizationPackage::projectDatas, - UnorderedElementsAre(IsProjectData(directoryPathSourceId, - qmlDocumentSourceId1, - ModuleId{}, - FileType::QmlDocument), - IsProjectData(directoryPathSourceId, - qmlDocumentSourceId2, - ModuleId{}, - FileType::QmlDocument)))))); + Field(&SynchronizationPackage::directoryInfos, + UnorderedElementsAre(IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId1, + ModuleId{}, + FileType::QmlDocument), + IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId2, + ModuleId{}, + FileType::QmlDocument)))))); updater.update(directories, {}, {}, {}); } @@ -738,10 +738,11 @@ TEST_F(ProjectStorageUpdater, synchronize_removes_qml_document) setFilesChanged({qmlDirPathSourceId}); setFilesDontChanged({qmlDocumentSourceId1, qmlDocumentSourceId2}); setFilesRemoved({qmlDocumentSourceId3}); - setProjectDatas(directoryPathSourceId, - {{directoryPathSourceId, qmlDocumentSourceId1, ModuleId{}, FileType::QmlDocument}, - {directoryPathSourceId, qmlDocumentSourceId2, ModuleId{}, FileType::QmlDocument}, - {directoryPathSourceId, qmlDocumentSourceId3, ModuleId{}, FileType::QmlDocument}}); + setDirectoryInfos( + directoryPathSourceId, + {{directoryPathSourceId, qmlDocumentSourceId1, ModuleId{}, FileType::QmlDocument}, + {directoryPathSourceId, qmlDocumentSourceId2, ModuleId{}, FileType::QmlDocument}, + {directoryPathSourceId, qmlDocumentSourceId3, ModuleId{}, FileType::QmlDocument}}); setQmlFileNames(u"/path", {"First.qml", "First2.qml"}); EXPECT_CALL(projectStorageMock, @@ -778,15 +779,15 @@ TEST_F(ProjectStorageUpdater, synchronize_removes_qml_document) UnorderedElementsAre(IsFileStatus(qmlDirPathSourceId, 1, 21))), Field(&SynchronizationPackage::updatedProjectSourceIds, UnorderedElementsAre(directoryPathSourceId)), - Field(&SynchronizationPackage::projectDatas, - UnorderedElementsAre(IsProjectData(directoryPathSourceId, - qmlDocumentSourceId1, - ModuleId{}, - FileType::QmlDocument), - IsProjectData(directoryPathSourceId, - qmlDocumentSourceId2, - ModuleId{}, - FileType::QmlDocument)))))); + Field(&SynchronizationPackage::directoryInfos, + UnorderedElementsAre(IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId1, + ModuleId{}, + FileType::QmlDocument), + IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId2, + ModuleId{}, + FileType::QmlDocument)))))); updater.update(directories, {}, {}, {}); } @@ -799,9 +800,9 @@ TEST_F(ProjectStorageUpdater, synchronize_removes_qml_document_in_qmldir_only) setContent(u"/path/qmldir", qmldir); setFilesChanged({qmlDirPathSourceId}); setFilesDontChanged({qmlDocumentSourceId1, qmlDocumentSourceId2}); - setProjectDatas(directoryPathSourceId, - {{directoryPathSourceId, qmlDocumentSourceId1, ModuleId{}, FileType::QmlDocument}, - {directoryPathSourceId, qmlDocumentSourceId2, ModuleId{}, FileType::QmlDocument}}); + setDirectoryInfos(directoryPathSourceId, + {{directoryPathSourceId, qmlDocumentSourceId1, ModuleId{}, FileType::QmlDocument}, + {directoryPathSourceId, qmlDocumentSourceId2, ModuleId{}, FileType::QmlDocument}}); setQmlFileNames(u"/path", {"First.qml", "First2.qml"}); EXPECT_CALL( @@ -833,15 +834,15 @@ TEST_F(ProjectStorageUpdater, synchronize_removes_qml_document_in_qmldir_only) UnorderedElementsAre(IsFileStatus(qmlDirPathSourceId, 1, 21))), Field(&SynchronizationPackage::updatedProjectSourceIds, UnorderedElementsAre(directoryPathSourceId)), - Field(&SynchronizationPackage::projectDatas, - UnorderedElementsAre(IsProjectData(directoryPathSourceId, - qmlDocumentSourceId1, - ModuleId{}, - FileType::QmlDocument), - IsProjectData(directoryPathSourceId, - qmlDocumentSourceId2, - ModuleId{}, - FileType::QmlDocument)))))); + Field(&SynchronizationPackage::directoryInfos, + UnorderedElementsAre(IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId1, + ModuleId{}, + FileType::QmlDocument), + IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId2, + ModuleId{}, + FileType::QmlDocument)))))); updater.update(directories, {}, {}, {}); } @@ -855,9 +856,9 @@ TEST_F(ProjectStorageUpdater, synchronize_add_qml_document_to_qmldir) setContent(u"/path/qmldir", qmldir); setFilesChanged({qmlDirPathSourceId}); setFilesDontChanged({qmlDocumentSourceId1, qmlDocumentSourceId2}); - setProjectDatas(directoryPathSourceId, - {{directoryPathSourceId, qmlDocumentSourceId1, ModuleId{}, FileType::QmlDocument}, - {directoryPathSourceId, qmlDocumentSourceId2, ModuleId{}, FileType::QmlDocument}}); + setDirectoryInfos(directoryPathSourceId, + {{directoryPathSourceId, qmlDocumentSourceId1, ModuleId{}, FileType::QmlDocument}, + {directoryPathSourceId, qmlDocumentSourceId2, ModuleId{}, FileType::QmlDocument}}); setQmlFileNames(u"/path", {"First.qml", "First2.qml"}); EXPECT_CALL( @@ -891,15 +892,15 @@ TEST_F(ProjectStorageUpdater, synchronize_add_qml_document_to_qmldir) UnorderedElementsAre(IsFileStatus(qmlDirPathSourceId, 1, 21))), Field(&SynchronizationPackage::updatedProjectSourceIds, UnorderedElementsAre(directoryPathSourceId)), - Field(&SynchronizationPackage::projectDatas, - UnorderedElementsAre(IsProjectData(directoryPathSourceId, - qmlDocumentSourceId1, - ModuleId{}, - FileType::QmlDocument), - IsProjectData(directoryPathSourceId, - qmlDocumentSourceId2, - ModuleId{}, - FileType::QmlDocument)))))); + Field(&SynchronizationPackage::directoryInfos, + UnorderedElementsAre(IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId1, + ModuleId{}, + FileType::QmlDocument), + IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId2, + ModuleId{}, + FileType::QmlDocument)))))); updater.update(directories, {}, {}, {}); } @@ -912,9 +913,9 @@ TEST_F(ProjectStorageUpdater, synchronize_remove_qml_document_from_qmldir) setContent(u"/path/qmldir", qmldir); setFilesDontChanged({qmlDocumentSourceId1, qmlDocumentSourceId2}); setFilesChanged({qmlDirPathSourceId}); - setProjectDatas(directoryPathSourceId, - {{directoryPathSourceId, qmlDocumentSourceId1, ModuleId{}, FileType::QmlDocument}, - {directoryPathSourceId, qmlDocumentSourceId2, ModuleId{}, FileType::QmlDocument}}); + setDirectoryInfos(directoryPathSourceId, + {{directoryPathSourceId, qmlDocumentSourceId1, ModuleId{}, FileType::QmlDocument}, + {directoryPathSourceId, qmlDocumentSourceId2, ModuleId{}, FileType::QmlDocument}}); setQmlFileNames(u"/path", {"First.qml", "First2.qml"}); EXPECT_CALL( @@ -946,15 +947,15 @@ TEST_F(ProjectStorageUpdater, synchronize_remove_qml_document_from_qmldir) UnorderedElementsAre(IsFileStatus(qmlDirPathSourceId, 1, 21))), Field(&SynchronizationPackage::updatedProjectSourceIds, UnorderedElementsAre(directoryPathSourceId)), - Field(&SynchronizationPackage::projectDatas, - UnorderedElementsAre(IsProjectData(directoryPathSourceId, - qmlDocumentSourceId1, - ModuleId{}, - FileType::QmlDocument), - IsProjectData(directoryPathSourceId, - qmlDocumentSourceId2, - ModuleId{}, - FileType::QmlDocument)))))); + Field(&SynchronizationPackage::directoryInfos, + UnorderedElementsAre(IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId1, + ModuleId{}, + FileType::QmlDocument), + IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId2, + ModuleId{}, + FileType::QmlDocument)))))); updater.update(directories, {}, {}, {}); } @@ -1012,26 +1013,26 @@ TEST_F(ProjectStorageUpdater, synchronize_qml_documents_dont_update_if_up_to_dat UnorderedElementsAre(qmlDirPathSourceId, qmlDocumentSourceId1, qmlDocumentSourceId2)), Field(&SynchronizationPackage::updatedProjectSourceIds, UnorderedElementsAre(directoryPathSourceId)), - Field(&SynchronizationPackage::projectDatas, - UnorderedElementsAre(IsProjectData(directoryPathSourceId, - qmlDocumentSourceId1, - ModuleId{}, - FileType::QmlDocument), - IsProjectData(directoryPathSourceId, - qmlDocumentSourceId2, - ModuleId{}, - FileType::QmlDocument), - IsProjectData(directoryPathSourceId, - qmlDocumentSourceId3, - ModuleId{}, - FileType::QmlDocument)))))); + Field(&SynchronizationPackage::directoryInfos, + UnorderedElementsAre(IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId1, + ModuleId{}, + FileType::QmlDocument), + IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId2, + ModuleId{}, + FileType::QmlDocument), + IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId3, + ModuleId{}, + FileType::QmlDocument)))))); updater.update(directories, {}, {}, {}); } TEST_F(ProjectStorageUpdater, synchroniz_if_qmldir_file_has_not_changed) { - setProjectDatas( + setDirectoryInfos( directoryPathSourceId, {{directoryPathSourceId, qmltypesPathSourceId, exampleModuleId, FileType::QmlTypes}, {directoryPathSourceId, qmltypes2PathSourceId, exampleModuleId, FileType::QmlTypes}, @@ -1075,14 +1076,14 @@ TEST_F(ProjectStorageUpdater, synchroniz_if_qmldir_file_has_not_changed) qmltypes2PathSourceId, qmlDocumentSourceId1, qmlDocumentSourceId2)), - Field(&SynchronizationPackage::projectDatas, IsEmpty())))); + Field(&SynchronizationPackage::directoryInfos, IsEmpty())))); updater.update(directories, {}, {}, {}); } TEST_F(ProjectStorageUpdater, synchroniz_if_qmldir_file_has_not_changed_and_some_updated_files) { - setProjectDatas( + setDirectoryInfos( directoryPathSourceId, {{directoryPathSourceId, qmltypesPathSourceId, exampleModuleId, FileType::QmlTypes}, {directoryPathSourceId, qmltypes2PathSourceId, exampleModuleId, FileType::QmlTypes}, @@ -1110,7 +1111,7 @@ TEST_F(ProjectStorageUpdater, synchroniz_if_qmldir_file_has_not_changed_and_some IsFileStatus(qmlDocumentSourceId1, 1, 21))), Field(&SynchronizationPackage::updatedFileStatusSourceIds, UnorderedElementsAre(qmltypesPathSourceId, qmlDocumentSourceId1)), - Field(&SynchronizationPackage::projectDatas, IsEmpty())))); + Field(&SynchronizationPackage::directoryInfos, IsEmpty())))); updater.update(directories, {}, {}, {}); } @@ -1118,7 +1119,7 @@ TEST_F(ProjectStorageUpdater, synchroniz_if_qmldir_file_has_not_changed_and_some TEST_F(ProjectStorageUpdater, synchroniz_if_qmldir_file_not_changed_and_some_removed_files) { setQmlFileNames(u"/path", {"First2.qml"}); - setProjectDatas( + setDirectoryInfos( directoryPathSourceId, {{directoryPathSourceId, qmltypesPathSourceId, exampleModuleId, FileType::QmlTypes}, {directoryPathSourceId, qmltypes2PathSourceId, exampleModuleId, FileType::QmlTypes}, @@ -1137,7 +1138,7 @@ TEST_F(ProjectStorageUpdater, synchroniz_if_qmldir_file_has_changed_and_some_rem typeinfo example2.qmltypes)"}; setContent(u"/path/qmldir", qmldir); setQmlFileNames(u"/path", {"First2.qml"}); - setProjectDatas( + setDirectoryInfos( directoryPathSourceId, {{directoryPathSourceId, qmltypesPathSourceId, exampleModuleId, FileType::QmlTypes}, {directoryPathSourceId, qmltypes2PathSourceId, exampleModuleId, FileType::QmlTypes}, @@ -1169,15 +1170,15 @@ TEST_F(ProjectStorageUpdater, synchroniz_if_qmldir_file_has_changed_and_some_rem UnorderedElementsAre(IsFileStatus(qmlDirPathSourceId, 1, 21))), Field(&SynchronizationPackage::updatedFileStatusSourceIds, UnorderedElementsAre(qmlDirPathSourceId, qmltypesPathSourceId, qmlDocumentSourceId1)), - Field(&SynchronizationPackage::projectDatas, - UnorderedElementsAre(IsProjectData(directoryPathSourceId, - qmlDocumentSourceId2, - ModuleId{}, - FileType::QmlDocument), - IsProjectData(directoryPathSourceId, - qmltypes2PathSourceId, - exampleCppNativeModuleId, - FileType::QmlTypes)))))); + Field(&SynchronizationPackage::directoryInfos, + UnorderedElementsAre(IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId2, + ModuleId{}, + FileType::QmlDocument), + IsDirectoryInfo(directoryPathSourceId, + qmltypes2PathSourceId, + exampleCppNativeModuleId, + FileType::QmlTypes)))))); updater.update(directories, {}, {}, {}); } @@ -1191,7 +1192,7 @@ TEST_F(ProjectStorageUpdater, update_qml_types_files_is_empty) Field(&SynchronizationPackage::updatedSourceIds, IsEmpty()), Field(&SynchronizationPackage::fileStatuses, IsEmpty()), Field(&SynchronizationPackage::updatedFileStatusSourceIds, IsEmpty()), - Field(&SynchronizationPackage::projectDatas, IsEmpty()), + Field(&SynchronizationPackage::directoryInfos, IsEmpty()), Field(&SynchronizationPackage::updatedProjectSourceIds, IsEmpty())))); updater.update({}, {}, {}, {}); @@ -1210,15 +1211,15 @@ TEST_F(ProjectStorageUpdater, update_qml_types_files) IsFileStatus(qmltypes2PathSourceId, 1, 21))), Field(&SynchronizationPackage::updatedFileStatusSourceIds, UnorderedElementsAre(qmltypesPathSourceId, qmltypes2PathSourceId)), - Field(&SynchronizationPackage::projectDatas, - UnorderedElementsAre(IsProjectData(qmltypesPathSourceId, - qmltypesPathSourceId, - builtinCppNativeModuleId, - FileType::QmlTypes), - IsProjectData(qmltypes2PathSourceId, - qmltypes2PathSourceId, - builtinCppNativeModuleId, - FileType::QmlTypes))), + Field(&SynchronizationPackage::directoryInfos, + UnorderedElementsAre(IsDirectoryInfo(qmltypesPathSourceId, + qmltypesPathSourceId, + builtinCppNativeModuleId, + FileType::QmlTypes), + IsDirectoryInfo(qmltypes2PathSourceId, + qmltypes2PathSourceId, + builtinCppNativeModuleId, + FileType::QmlTypes))), Field(&SynchronizationPackage::updatedProjectSourceIds, UnorderedElementsAre(qmltypesPathSourceId, qmltypes2PathSourceId))))); @@ -1239,11 +1240,11 @@ TEST_F(ProjectStorageUpdater, dont_update_qml_types_files_if_unchanged) UnorderedElementsAre(IsFileStatus(qmltypesPathSourceId, 1, 21))), Field(&SynchronizationPackage::updatedFileStatusSourceIds, UnorderedElementsAre(qmltypesPathSourceId)), - Field(&SynchronizationPackage::projectDatas, - UnorderedElementsAre(IsProjectData(qmltypesPathSourceId, - qmltypesPathSourceId, - builtinCppNativeModuleId, - FileType::QmlTypes))), + Field(&SynchronizationPackage::directoryInfos, + UnorderedElementsAre(IsDirectoryInfo(qmltypesPathSourceId, + qmltypesPathSourceId, + builtinCppNativeModuleId, + FileType::QmlTypes))), Field(&SynchronizationPackage::updatedProjectSourceIds, UnorderedElementsAre(qmltypesPathSourceId))))); @@ -1284,11 +1285,11 @@ TEST_F(ProjectStorageUpdater, synchronize_qml_documents_with_different_version_b IsFileStatus(qmlDocumentSourceId1, 1, 21))), Field(&SynchronizationPackage::updatedProjectSourceIds, UnorderedElementsAre(directoryPathSourceId)), - Field(&SynchronizationPackage::projectDatas, - UnorderedElementsAre(IsProjectData(directoryPathSourceId, - qmlDocumentSourceId1, - ModuleId{}, - FileType::QmlDocument)))))); + Field(&SynchronizationPackage::directoryInfos, + UnorderedElementsAre(IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId1, + ModuleId{}, + FileType::QmlDocument)))))); updater.update(directories, {}, {}, {}); } @@ -1325,11 +1326,11 @@ TEST_F(ProjectStorageUpdater, synchronize_qml_documents_with_different_type_name IsFileStatus(qmlDocumentSourceId1, 1, 21))), Field(&SynchronizationPackage::updatedProjectSourceIds, UnorderedElementsAre(directoryPathSourceId)), - Field(&SynchronizationPackage::projectDatas, - UnorderedElementsAre(IsProjectData(directoryPathSourceId, - qmlDocumentSourceId1, - ModuleId{}, - FileType::QmlDocument)))))); + Field(&SynchronizationPackage::directoryInfos, + UnorderedElementsAre(IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId1, + ModuleId{}, + FileType::QmlDocument)))))); updater.update(directories, {}, {}, {}); } @@ -1750,11 +1751,11 @@ TEST_F(ProjectStorageUpdater, update_path_watcher_only_qml_files_changed) { setFilesDontChanged({qmldir1SourceId, qmldir2SourceId, path1SourceId, path2SourceId}); setFilesChanged({firstSourceId, secondSourceId, thirdSourceId}); - setProjectDatas(path1SourceId, - {{path1SourceId, firstSourceId, exampleModuleId, FileType::QmlDocument}, - {path1SourceId, secondSourceId, exampleModuleId, FileType::QmlDocument}}); - setProjectDatas(path2SourceId, - {{path2SourceId, thirdSourceId, ModuleId{}, FileType::QmlDocument}}); + setDirectoryInfos(path1SourceId, + {{path1SourceId, firstSourceId, exampleModuleId, FileType::QmlDocument}, + {path1SourceId, secondSourceId, exampleModuleId, FileType::QmlDocument}}); + setDirectoryInfos(path2SourceId, + {{path2SourceId, thirdSourceId, ModuleId{}, FileType::QmlDocument}}); EXPECT_CALL(patchWatcherMock, updateIdPaths(Contains(IdPaths{projectPartId, @@ -1773,11 +1774,11 @@ TEST_F(ProjectStorageUpdater, update_path_watcher_qml_files_and_directories_dont firstSourceId, secondSourceId, thirdSourceId}); - setProjectDatas(path1SourceId, - {{path1SourceId, firstSourceId, exampleModuleId, FileType::QmlDocument}, - {path1SourceId, secondSourceId, exampleModuleId, FileType::QmlDocument}}); - setProjectDatas(path2SourceId, - {{path2SourceId, thirdSourceId, ModuleId{}, FileType::QmlDocument}}); + setDirectoryInfos(path1SourceId, + {{path1SourceId, firstSourceId, exampleModuleId, FileType::QmlDocument}, + {path1SourceId, secondSourceId, exampleModuleId, FileType::QmlDocument}}); + setDirectoryInfos(path2SourceId, + {{path2SourceId, thirdSourceId, ModuleId{}, FileType::QmlDocument}}); EXPECT_CALL(patchWatcherMock, updateIdPaths(Contains(IdPaths{projectPartId, @@ -1828,10 +1829,10 @@ TEST_F(ProjectStorageUpdater, update_path_watcher_only_qmltypes_files_changed) { setFilesDontChanged({qmldir1SourceId, qmldir2SourceId, path1SourceId, path2SourceId}); setFilesChanged({qmltypes1SourceId, qmltypes2SourceId}); - setProjectDatas(path1SourceId, - {{path1SourceId, qmltypes1SourceId, exampleModuleId, FileType::QmlTypes}}); - setProjectDatas(path2SourceId, - {{path2SourceId, qmltypes2SourceId, exampleModuleId, FileType::QmlTypes}}); + setDirectoryInfos(path1SourceId, + {{path1SourceId, qmltypes1SourceId, exampleModuleId, FileType::QmlTypes}}); + setDirectoryInfos(path2SourceId, + {{path2SourceId, qmltypes2SourceId, exampleModuleId, FileType::QmlTypes}}); EXPECT_CALL(patchWatcherMock, updateIdPaths(Contains(IdPaths{projectPartId, @@ -1849,10 +1850,10 @@ TEST_F(ProjectStorageUpdater, update_path_watcher_qmltypes_files_and_directories path2SourceId, qmltypes1SourceId, qmltypes2SourceId}); - setProjectDatas(path1SourceId, - {{path1SourceId, qmltypes1SourceId, exampleModuleId, FileType::QmlTypes}}); - setProjectDatas(path2SourceId, - {{path2SourceId, qmltypes2SourceId, exampleModuleId, FileType::QmlTypes}}); + setDirectoryInfos(path1SourceId, + {{path1SourceId, qmltypes1SourceId, exampleModuleId, FileType::QmlTypes}}); + setDirectoryInfos(path2SourceId, + {{path2SourceId, qmltypes2SourceId, exampleModuleId, FileType::QmlTypes}}); EXPECT_CALL(patchWatcherMock, updateIdPaths(Contains(IdPaths{projectPartId, @@ -1927,19 +1928,19 @@ TEST_F(ProjectStorageUpdater, synchronize_qml_documents_without_qmldir) IsFileStatus(qmlDocumentSourceId3, 1, 21))), Field(&SynchronizationPackage::updatedProjectSourceIds, UnorderedElementsAre(directoryPathSourceId)), - Field(&SynchronizationPackage::projectDatas, - UnorderedElementsAre(IsProjectData(directoryPathSourceId, - qmlDocumentSourceId1, - ModuleId{}, - FileType::QmlDocument), - IsProjectData(directoryPathSourceId, - qmlDocumentSourceId2, - ModuleId{}, - FileType::QmlDocument), - IsProjectData(directoryPathSourceId, - qmlDocumentSourceId3, - ModuleId{}, - FileType::QmlDocument)))))); + Field(&SynchronizationPackage::directoryInfos, + UnorderedElementsAre(IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId1, + ModuleId{}, + FileType::QmlDocument), + IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId2, + ModuleId{}, + FileType::QmlDocument), + IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId3, + ModuleId{}, + FileType::QmlDocument)))))); updater.update(directories, {}, {}, {}); } @@ -1955,10 +1956,11 @@ TEST_F(ProjectStorageUpdater, synchronize_qml_documents_without_qmldir_throws_if TEST_F(ProjectStorageUpdater, synchronize_qml_documents_without_qmldir_throws_if_directory_does_not_exists) { setFilesDontExists({qmlDirPathSourceId, directoryPathSourceId}); - setProjectDatas(directoryPathSourceId, - {{directoryPathSourceId, qmlDocumentSourceId1, ModuleId{}, FileType::QmlDocument}, - {directoryPathSourceId, qmlDocumentSourceId2, ModuleId{}, FileType::QmlDocument}, - {directoryPathSourceId, qmlDocumentSourceId3, ModuleId{}, FileType::QmlDocument}}); + setDirectoryInfos( + directoryPathSourceId, + {{directoryPathSourceId, qmlDocumentSourceId1, ModuleId{}, FileType::QmlDocument}, + {directoryPathSourceId, qmlDocumentSourceId2, ModuleId{}, FileType::QmlDocument}, + {directoryPathSourceId, qmlDocumentSourceId3, ModuleId{}, FileType::QmlDocument}}); EXPECT_CALL(projectStorageMock, synchronize(AllOf(Field(&SynchronizationPackage::imports, IsEmpty()), @@ -1977,7 +1979,7 @@ TEST_F(ProjectStorageUpdater, synchronize_qml_documents_without_qmldir_throws_if Field(&SynchronizationPackage::fileStatuses, IsEmpty()), Field(&SynchronizationPackage::updatedProjectSourceIds, UnorderedElementsAre(directoryPathSourceId)), - Field(&SynchronizationPackage::projectDatas, IsEmpty())))); + Field(&SynchronizationPackage::directoryInfos, IsEmpty())))); updater.update(directories, {}, {}, {}); } @@ -1988,9 +1990,9 @@ TEST_F(ProjectStorageUpdater, synchronize_qml_documents_without_qmldir_add_qml_d setFilesChanged({directoryPathSourceId}); setFilesAdded({qmlDocumentSourceId3}); setFilesDontChanged({qmlDocumentSourceId1, qmlDocumentSourceId2}); - setProjectDatas(directoryPathSourceId, - {{directoryPathSourceId, qmlDocumentSourceId1, ModuleId{}, FileType::QmlDocument}, - {directoryPathSourceId, qmlDocumentSourceId2, ModuleId{}, FileType::QmlDocument}}); + setDirectoryInfos(directoryPathSourceId, + {{directoryPathSourceId, qmlDocumentSourceId1, ModuleId{}, FileType::QmlDocument}, + {directoryPathSourceId, qmlDocumentSourceId2, ModuleId{}, FileType::QmlDocument}}); EXPECT_CALL( projectStorageMock, @@ -2014,19 +2016,19 @@ TEST_F(ProjectStorageUpdater, synchronize_qml_documents_without_qmldir_add_qml_d IsFileStatus(qmlDocumentSourceId3, 1, 21))), Field(&SynchronizationPackage::updatedProjectSourceIds, UnorderedElementsAre(directoryPathSourceId)), - Field(&SynchronizationPackage::projectDatas, - UnorderedElementsAre(IsProjectData(directoryPathSourceId, - qmlDocumentSourceId1, - ModuleId{}, - FileType::QmlDocument), - IsProjectData(directoryPathSourceId, - qmlDocumentSourceId2, - ModuleId{}, - FileType::QmlDocument), - IsProjectData(directoryPathSourceId, - qmlDocumentSourceId3, - ModuleId{}, - FileType::QmlDocument)))))); + Field(&SynchronizationPackage::directoryInfos, + UnorderedElementsAre(IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId1, + ModuleId{}, + FileType::QmlDocument), + IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId2, + ModuleId{}, + FileType::QmlDocument), + IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId3, + ModuleId{}, + FileType::QmlDocument)))))); updater.update(directories, {}, {}, {}); } @@ -2038,10 +2040,11 @@ TEST_F(ProjectStorageUpdater, synchronize_qml_documents_without_qmldir_removes_q setFilesRemoved({qmlDocumentSourceId3}); setFilesDontChanged({qmlDocumentSourceId1, qmlDocumentSourceId2}); setQmlFileNames(u"/path", {"First.qml", "First2.qml"}); - setProjectDatas(directoryPathSourceId, - {{directoryPathSourceId, qmlDocumentSourceId1, ModuleId{}, FileType::QmlDocument}, - {directoryPathSourceId, qmlDocumentSourceId2, ModuleId{}, FileType::QmlDocument}, - {directoryPathSourceId, qmlDocumentSourceId3, ModuleId{}, FileType::QmlDocument}}); + setDirectoryInfos( + directoryPathSourceId, + {{directoryPathSourceId, qmlDocumentSourceId1, ModuleId{}, FileType::QmlDocument}, + {directoryPathSourceId, qmlDocumentSourceId2, ModuleId{}, FileType::QmlDocument}, + {directoryPathSourceId, qmlDocumentSourceId3, ModuleId{}, FileType::QmlDocument}}); EXPECT_CALL(projectStorageMock, synchronize( @@ -2057,15 +2060,15 @@ TEST_F(ProjectStorageUpdater, synchronize_qml_documents_without_qmldir_removes_q UnorderedElementsAre(IsFileStatus(directoryPathSourceId, 1, 21))), Field(&SynchronizationPackage::updatedProjectSourceIds, UnorderedElementsAre(directoryPathSourceId)), - Field(&SynchronizationPackage::projectDatas, - UnorderedElementsAre(IsProjectData(directoryPathSourceId, - qmlDocumentSourceId1, - ModuleId{}, - FileType::QmlDocument), - IsProjectData(directoryPathSourceId, - qmlDocumentSourceId2, - ModuleId{}, - FileType::QmlDocument)))))); + Field(&SynchronizationPackage::directoryInfos, + UnorderedElementsAre(IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId1, + ModuleId{}, + FileType::QmlDocument), + IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId2, + ModuleId{}, + FileType::QmlDocument)))))); updater.update(directories, {}, {}, {}); } @@ -2125,19 +2128,19 @@ TEST_F(ProjectStorageUpdater, watcher_updates_directories) IsFileStatus(qmlDocumentSourceId3, 1, 21))), Field(&SynchronizationPackage::updatedProjectSourceIds, UnorderedElementsAre(directoryPathSourceId)), - Field(&SynchronizationPackage::projectDatas, - UnorderedElementsAre(IsProjectData(directoryPathSourceId, - qmlDocumentSourceId1, - ModuleId{}, - FileType::QmlDocument), - IsProjectData(directoryPathSourceId, - qmlDocumentSourceId2, - ModuleId{}, - FileType::QmlDocument), - IsProjectData(directoryPathSourceId, - qmlDocumentSourceId3, - ModuleId{}, - FileType::QmlDocument)))))); + Field(&SynchronizationPackage::directoryInfos, + UnorderedElementsAre(IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId1, + ModuleId{}, + FileType::QmlDocument), + IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId2, + ModuleId{}, + FileType::QmlDocument), + IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId3, + ModuleId{}, + FileType::QmlDocument)))))); updater.pathsWithIdsChanged({{directoryProjectChunkId, {directoryPathSourceId}}}); } @@ -2149,10 +2152,11 @@ TEST_F(ProjectStorageUpdater, watcher_updates_removed_directory) qmlDocumentSourceId1, qmlDocumentSourceId2, qmlDocumentSourceId3}); - setProjectDatas(directoryPathSourceId, - {{directoryPathSourceId, qmlDocumentSourceId1, ModuleId{}, FileType::QmlDocument}, - {directoryPathSourceId, qmlDocumentSourceId2, ModuleId{}, FileType::QmlDocument}, - {directoryPathSourceId, qmlDocumentSourceId3, ModuleId{}, FileType::QmlDocument}}); + setDirectoryInfos( + directoryPathSourceId, + {{directoryPathSourceId, qmlDocumentSourceId1, ModuleId{}, FileType::QmlDocument}, + {directoryPathSourceId, qmlDocumentSourceId2, ModuleId{}, FileType::QmlDocument}, + {directoryPathSourceId, qmlDocumentSourceId3, ModuleId{}, FileType::QmlDocument}}); EXPECT_CALL(projectStorageMock, synchronize(AllOf(Field(&SynchronizationPackage::imports, IsEmpty()), @@ -2171,7 +2175,7 @@ TEST_F(ProjectStorageUpdater, watcher_updates_removed_directory) Field(&SynchronizationPackage::fileStatuses, UnorderedElementsAre()), Field(&SynchronizationPackage::updatedProjectSourceIds, UnorderedElementsAre(directoryPathSourceId)), - Field(&SynchronizationPackage::projectDatas, IsEmpty())))); + Field(&SynchronizationPackage::directoryInfos, IsEmpty())))); updater.pathsWithIdsChanged({{directoryProjectChunkId, {directoryPathSourceId}}}); } @@ -2273,19 +2277,19 @@ TEST_F(ProjectStorageUpdater, watcher_updates_directories_and_qmldir) IsFileStatus(qmlDocumentSourceId3, 1, 21))), Field(&SynchronizationPackage::updatedProjectSourceIds, UnorderedElementsAre(directoryPathSourceId)), - Field(&SynchronizationPackage::projectDatas, - UnorderedElementsAre(IsProjectData(directoryPathSourceId, - qmlDocumentSourceId1, - ModuleId{}, - FileType::QmlDocument), - IsProjectData(directoryPathSourceId, - qmlDocumentSourceId2, - ModuleId{}, - FileType::QmlDocument), - IsProjectData(directoryPathSourceId, - qmlDocumentSourceId3, - ModuleId{}, - FileType::QmlDocument)))))); + Field(&SynchronizationPackage::directoryInfos, + UnorderedElementsAre(IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId1, + ModuleId{}, + FileType::QmlDocument), + IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId2, + ModuleId{}, + FileType::QmlDocument), + IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId3, + ModuleId{}, + FileType::QmlDocument)))))); updater.pathsWithIdsChanged({{directoryProjectChunkId, {directoryPathSourceId}}, {qmldirProjectChunkId, {qmlDirPathSourceId}}}); @@ -2335,8 +2339,8 @@ TEST_F(ProjectStorageUpdater, watcher_updates_add_only_qml_document_in_directory setFilesChanged({directoryPathSourceId}); setFilesDontChanged({qmlDirPathSourceId, qmlDocumentSourceId1}); setFilesAdded({qmlDocumentSourceId2}); - setProjectDatas(directoryPathSourceId, - {{directoryPathSourceId, qmlDocumentSourceId1, ModuleId{}, FileType::QmlDocument}}); + setDirectoryInfos(directoryPathSourceId, + {{directoryPathSourceId, qmlDocumentSourceId1, ModuleId{}, FileType::QmlDocument}}); setQmlFileNames(u"/path", {"First.qml", "First2.qml"}); EXPECT_CALL(projectStorageMock, @@ -2370,15 +2374,15 @@ TEST_F(ProjectStorageUpdater, watcher_updates_add_only_qml_document_in_directory IsFileStatus(directoryPathSourceId, 1, 21))), Field(&SynchronizationPackage::updatedProjectSourceIds, UnorderedElementsAre(directoryPathSourceId)), - Field(&SynchronizationPackage::projectDatas, - UnorderedElementsAre(IsProjectData(directoryPathSourceId, - qmlDocumentSourceId1, - ModuleId{}, - FileType::QmlDocument), - IsProjectData(directoryPathSourceId, - qmlDocumentSourceId2, - ModuleId{}, - FileType::QmlDocument)))))); + Field(&SynchronizationPackage::directoryInfos, + UnorderedElementsAre(IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId1, + ModuleId{}, + FileType::QmlDocument), + IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId2, + ModuleId{}, + FileType::QmlDocument)))))); updater.pathsWithIdsChanged({{directoryProjectChunkId, {directoryPathSourceId}}}); } @@ -2393,10 +2397,11 @@ TEST_F(ProjectStorageUpdater, watcher_updates_removes_qml_document) setFilesChanged({qmlDirPathSourceId}); setFilesDontChanged({qmlDocumentSourceId1, qmlDocumentSourceId2}); setFilesRemoved({qmlDocumentSourceId3}); - setProjectDatas(directoryPathSourceId, - {{directoryPathSourceId, qmlDocumentSourceId1, ModuleId{}, FileType::QmlDocument}, - {directoryPathSourceId, qmlDocumentSourceId2, ModuleId{}, FileType::QmlDocument}, - {directoryPathSourceId, qmlDocumentSourceId3, ModuleId{}, FileType::QmlDocument}}); + setDirectoryInfos( + directoryPathSourceId, + {{directoryPathSourceId, qmlDocumentSourceId1, ModuleId{}, FileType::QmlDocument}, + {directoryPathSourceId, qmlDocumentSourceId2, ModuleId{}, FileType::QmlDocument}, + {directoryPathSourceId, qmlDocumentSourceId3, ModuleId{}, FileType::QmlDocument}}); setQmlFileNames(u"/path", {"First.qml", "First2.qml"}); EXPECT_CALL(projectStorageMock, @@ -2433,15 +2438,15 @@ TEST_F(ProjectStorageUpdater, watcher_updates_removes_qml_document) UnorderedElementsAre(IsFileStatus(qmlDirPathSourceId, 1, 21))), Field(&SynchronizationPackage::updatedProjectSourceIds, UnorderedElementsAre(directoryPathSourceId)), - Field(&SynchronizationPackage::projectDatas, - UnorderedElementsAre(IsProjectData(directoryPathSourceId, - qmlDocumentSourceId1, - ModuleId{}, - FileType::QmlDocument), - IsProjectData(directoryPathSourceId, - qmlDocumentSourceId2, - ModuleId{}, - FileType::QmlDocument)))))); + Field(&SynchronizationPackage::directoryInfos, + UnorderedElementsAre(IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId1, + ModuleId{}, + FileType::QmlDocument), + IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId2, + ModuleId{}, + FileType::QmlDocument)))))); updater.pathsWithIdsChanged({{directoryProjectChunkId, {directoryPathSourceId}}}); } @@ -2454,9 +2459,9 @@ TEST_F(ProjectStorageUpdater, watcher_updates_removes_qml_document_in_qmldir_onl setContent(u"/path/qmldir", qmldir); setFilesChanged({qmlDirPathSourceId}); setFilesDontChanged({qmlDocumentSourceId1, qmlDocumentSourceId2}); - setProjectDatas(directoryPathSourceId, - {{directoryPathSourceId, qmlDocumentSourceId1, ModuleId{}, FileType::QmlDocument}, - {directoryPathSourceId, qmlDocumentSourceId2, ModuleId{}, FileType::QmlDocument}}); + setDirectoryInfos(directoryPathSourceId, + {{directoryPathSourceId, qmlDocumentSourceId1, ModuleId{}, FileType::QmlDocument}, + {directoryPathSourceId, qmlDocumentSourceId2, ModuleId{}, FileType::QmlDocument}}); setQmlFileNames(u"/path", {"First.qml", "First2.qml"}); EXPECT_CALL( @@ -2488,15 +2493,15 @@ TEST_F(ProjectStorageUpdater, watcher_updates_removes_qml_document_in_qmldir_onl UnorderedElementsAre(IsFileStatus(qmlDirPathSourceId, 1, 21))), Field(&SynchronizationPackage::updatedProjectSourceIds, UnorderedElementsAre(directoryPathSourceId)), - Field(&SynchronizationPackage::projectDatas, - UnorderedElementsAre(IsProjectData(directoryPathSourceId, - qmlDocumentSourceId1, - ModuleId{}, - FileType::QmlDocument), - IsProjectData(directoryPathSourceId, - qmlDocumentSourceId2, - ModuleId{}, - FileType::QmlDocument)))))); + Field(&SynchronizationPackage::directoryInfos, + UnorderedElementsAre(IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId1, + ModuleId{}, + FileType::QmlDocument), + IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId2, + ModuleId{}, + FileType::QmlDocument)))))); updater.pathsWithIdsChanged({{qmldirProjectChunkId, {qmlDirPathSourceId}}}); } @@ -2510,9 +2515,9 @@ TEST_F(ProjectStorageUpdater, watcher_updates_directories_add_qml_document_to_qm setContent(u"/path/qmldir", qmldir); setFilesChanged({qmlDirPathSourceId}); setFilesDontChanged({qmlDocumentSourceId1, qmlDocumentSourceId2}); - setProjectDatas(directoryPathSourceId, - {{directoryPathSourceId, qmlDocumentSourceId1, ModuleId{}, FileType::QmlDocument}, - {directoryPathSourceId, qmlDocumentSourceId2, ModuleId{}, FileType::QmlDocument}}); + setDirectoryInfos(directoryPathSourceId, + {{directoryPathSourceId, qmlDocumentSourceId1, ModuleId{}, FileType::QmlDocument}, + {directoryPathSourceId, qmlDocumentSourceId2, ModuleId{}, FileType::QmlDocument}}); setQmlFileNames(u"/path", {"First.qml", "First2.qml"}); EXPECT_CALL( @@ -2546,15 +2551,15 @@ TEST_F(ProjectStorageUpdater, watcher_updates_directories_add_qml_document_to_qm UnorderedElementsAre(IsFileStatus(qmlDirPathSourceId, 1, 21))), Field(&SynchronizationPackage::updatedProjectSourceIds, UnorderedElementsAre(directoryPathSourceId)), - Field(&SynchronizationPackage::projectDatas, - UnorderedElementsAre(IsProjectData(directoryPathSourceId, - qmlDocumentSourceId1, - ModuleId{}, - FileType::QmlDocument), - IsProjectData(directoryPathSourceId, - qmlDocumentSourceId2, - ModuleId{}, - FileType::QmlDocument)))))); + Field(&SynchronizationPackage::directoryInfos, + UnorderedElementsAre(IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId1, + ModuleId{}, + FileType::QmlDocument), + IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId2, + ModuleId{}, + FileType::QmlDocument)))))); updater.pathsWithIdsChanged({{qmldirProjectChunkId, {qmlDirPathSourceId}}}); } @@ -2567,9 +2572,9 @@ TEST_F(ProjectStorageUpdater, watcher_updates_directories_remove_qml_document_fr setContent(u"/path/qmldir", qmldir); setFilesDontChanged({qmlDocumentSourceId1, qmlDocumentSourceId2}); setFilesChanged({qmlDirPathSourceId}); - setProjectDatas(directoryPathSourceId, - {{directoryPathSourceId, qmlDocumentSourceId1, ModuleId{}, FileType::QmlDocument}, - {directoryPathSourceId, qmlDocumentSourceId2, ModuleId{}, FileType::QmlDocument}}); + setDirectoryInfos(directoryPathSourceId, + {{directoryPathSourceId, qmlDocumentSourceId1, ModuleId{}, FileType::QmlDocument}, + {directoryPathSourceId, qmlDocumentSourceId2, ModuleId{}, FileType::QmlDocument}}); setQmlFileNames(u"/path", {"First.qml", "First2.qml"}); EXPECT_CALL( @@ -2601,15 +2606,15 @@ TEST_F(ProjectStorageUpdater, watcher_updates_directories_remove_qml_document_fr UnorderedElementsAre(IsFileStatus(qmlDirPathSourceId, 1, 21))), Field(&SynchronizationPackage::updatedProjectSourceIds, UnorderedElementsAre(directoryPathSourceId)), - Field(&SynchronizationPackage::projectDatas, - UnorderedElementsAre(IsProjectData(directoryPathSourceId, - qmlDocumentSourceId1, - ModuleId{}, - FileType::QmlDocument), - IsProjectData(directoryPathSourceId, - qmlDocumentSourceId2, - ModuleId{}, - FileType::QmlDocument)))))); + Field(&SynchronizationPackage::directoryInfos, + UnorderedElementsAre(IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId1, + ModuleId{}, + FileType::QmlDocument), + IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId2, + ModuleId{}, + FileType::QmlDocument)))))); updater.pathsWithIdsChanged({{qmldirProjectChunkId, {qmlDirPathSourceId}}}); } @@ -2667,19 +2672,19 @@ TEST_F(ProjectStorageUpdater, watcher_updates_directories_dont_update_qml_docume UnorderedElementsAre(qmlDirPathSourceId, qmlDocumentSourceId1, qmlDocumentSourceId2)), Field(&SynchronizationPackage::updatedProjectSourceIds, UnorderedElementsAre(directoryPathSourceId)), - Field(&SynchronizationPackage::projectDatas, - UnorderedElementsAre(IsProjectData(directoryPathSourceId, - qmlDocumentSourceId1, - ModuleId{}, - FileType::QmlDocument), - IsProjectData(directoryPathSourceId, - qmlDocumentSourceId2, - ModuleId{}, - FileType::QmlDocument), - IsProjectData(directoryPathSourceId, - qmlDocumentSourceId3, - ModuleId{}, - FileType::QmlDocument)))))); + Field(&SynchronizationPackage::directoryInfos, + UnorderedElementsAre(IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId1, + ModuleId{}, + FileType::QmlDocument), + IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId2, + ModuleId{}, + FileType::QmlDocument), + IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId3, + ModuleId{}, + FileType::QmlDocument)))))); updater.pathsWithIdsChanged({{directoryProjectChunkId, {directoryPathSourceId}}}); } @@ -2737,26 +2742,26 @@ TEST_F(ProjectStorageUpdater, watcher_updates_qmldirs_dont_update_qml_documents_ UnorderedElementsAre(qmlDirPathSourceId, qmlDocumentSourceId1, qmlDocumentSourceId2)), Field(&SynchronizationPackage::updatedProjectSourceIds, UnorderedElementsAre(directoryPathSourceId)), - Field(&SynchronizationPackage::projectDatas, - UnorderedElementsAre(IsProjectData(directoryPathSourceId, - qmlDocumentSourceId1, - ModuleId{}, - FileType::QmlDocument), - IsProjectData(directoryPathSourceId, - qmlDocumentSourceId2, - ModuleId{}, - FileType::QmlDocument), - IsProjectData(directoryPathSourceId, - qmlDocumentSourceId3, - ModuleId{}, - FileType::QmlDocument)))))); + Field(&SynchronizationPackage::directoryInfos, + UnorderedElementsAre(IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId1, + ModuleId{}, + FileType::QmlDocument), + IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId2, + ModuleId{}, + FileType::QmlDocument), + IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId3, + ModuleId{}, + FileType::QmlDocument)))))); updater.pathsWithIdsChanged({{qmldirProjectChunkId, {qmlDirPathSourceId}}}); } TEST_F(ProjectStorageUpdater, watcher_updates_directory_but_not_qmldir) { - setProjectDatas( + setDirectoryInfos( directoryPathSourceId, {{directoryPathSourceId, qmltypesPathSourceId, exampleModuleId, FileType::QmlTypes}, {directoryPathSourceId, qmltypes2PathSourceId, exampleModuleId, FileType::QmlTypes}, @@ -2800,7 +2805,7 @@ TEST_F(ProjectStorageUpdater, watcher_updates_directory_but_not_qmldir) qmltypes2PathSourceId, qmlDocumentSourceId1, qmlDocumentSourceId2)), - Field(&SynchronizationPackage::projectDatas, IsEmpty())))); + Field(&SynchronizationPackage::directoryInfos, IsEmpty())))); updater.pathsWithIdsChanged({{directoryProjectChunkId, {directoryPathSourceId}}}); } @@ -2832,7 +2837,7 @@ TEST_F(ProjectStorageUpdater, watcher_updates_qml_documents) IsFileStatus(qmlDocumentSourceId2, 1, 21))), Field(&SynchronizationPackage::updatedFileStatusSourceIds, UnorderedElementsAre(qmlDocumentSourceId1, qmlDocumentSourceId2)), - Field(&SynchronizationPackage::projectDatas, IsEmpty())))); + Field(&SynchronizationPackage::directoryInfos, IsEmpty())))); updater.pathsWithIdsChanged( {{qmlDocumentProjectChunkId, {qmlDocumentSourceId1, qmlDocumentSourceId2}}}); @@ -2859,7 +2864,7 @@ TEST_F(ProjectStorageUpdater, watcher_updates_removed_qml_documents) UnorderedElementsAre(IsFileStatus(qmlDocumentSourceId1, 1, 21))), Field(&SynchronizationPackage::updatedFileStatusSourceIds, UnorderedElementsAre(qmlDocumentSourceId1, qmlDocumentSourceId2)), - Field(&SynchronizationPackage::projectDatas, IsEmpty())))); + Field(&SynchronizationPackage::directoryInfos, IsEmpty())))); updater.pathsWithIdsChanged( {{qmlDocumentProjectChunkId, {qmlDocumentSourceId1, qmlDocumentSourceId2}}}); @@ -2882,7 +2887,7 @@ TEST_F(ProjectStorageUpdater, watcher_dont_updates_qml_documents_for_other_proje TEST_F(ProjectStorageUpdater, watcher_updates_qmltypes) { - setProjectDatas( + setDirectoryInfos( directoryPathSourceId, {{directoryPathSourceId, qmltypesPathSourceId, exampleModuleId, FileType::QmlTypes}, {directoryPathSourceId, qmltypes2PathSourceId, exampleModuleId, FileType::QmlTypes}}); @@ -2902,7 +2907,7 @@ TEST_F(ProjectStorageUpdater, watcher_updates_qmltypes) IsFileStatus(qmltypes2PathSourceId, 1, 21))), Field(&SynchronizationPackage::updatedFileStatusSourceIds, UnorderedElementsAre(qmltypesPathSourceId, qmltypes2PathSourceId)), - Field(&SynchronizationPackage::projectDatas, IsEmpty())))); + Field(&SynchronizationPackage::directoryInfos, IsEmpty())))); updater.pathsWithIdsChanged( {{qmltypesProjectChunkId, {qmltypesPathSourceId, qmltypes2PathSourceId}}}); @@ -2910,7 +2915,7 @@ TEST_F(ProjectStorageUpdater, watcher_updates_qmltypes) TEST_F(ProjectStorageUpdater, watcher_updates_removed_qmltypes_without_updated_qmldir) { - setProjectDatas( + setDirectoryInfos( directoryPathSourceId, {{directoryPathSourceId, qmltypesPathSourceId, exampleModuleId, FileType::QmlTypes}, {directoryPathSourceId, qmltypes2PathSourceId, exampleModuleId, FileType::QmlTypes}}); @@ -2926,7 +2931,7 @@ TEST_F(ProjectStorageUpdater, watcher_updates_removed_qmltypes_without_updated_q TEST_F(ProjectStorageUpdater, watcher_updates_removed_qmltypes_with_updated_qmldir) { - setProjectDatas( + setDirectoryInfos( directoryPathSourceId, {{directoryPathSourceId, qmltypesPathSourceId, exampleModuleId, FileType::QmlTypes}, {directoryPathSourceId, qmltypes2PathSourceId, exampleModuleId, FileType::QmlTypes}}); @@ -2956,18 +2961,18 @@ TEST_F(ProjectStorageUpdater, watcher_updates_removed_qmltypes_with_updated_qmld UnorderedElementsAre(qmlDirPathSourceId, qmltypesPathSourceId, qmltypes2PathSourceId)), - Field(&SynchronizationPackage::projectDatas, - UnorderedElementsAre(IsProjectData(directoryPathSourceId, - qmltypes2PathSourceId, - exampleCppNativeModuleId, - FileType::QmlTypes)))))); + Field(&SynchronizationPackage::directoryInfos, + UnorderedElementsAre(IsDirectoryInfo(directoryPathSourceId, + qmltypes2PathSourceId, + exampleCppNativeModuleId, + FileType::QmlTypes)))))); updater.pathsWithIdsChanged({{qmldirProjectChunkId, {qmlDirPathSourceId}}}); } TEST_F(ProjectStorageUpdater, watcher_dont_watches_directories_after_qmltypes_changes) { - setProjectDatas( + setDirectoryInfos( directoryPathSourceId, {{directoryPathSourceId, qmltypesPathSourceId, exampleModuleId, FileType::QmlTypes}, {directoryPathSourceId, qmltypes2PathSourceId, exampleModuleId, FileType::QmlTypes}}); @@ -2982,7 +2987,7 @@ TEST_F(ProjectStorageUpdater, watcher_dont_watches_directories_after_qmltypes_ch TEST_F(ProjectStorageUpdater, watcher_dont_updates_qmltypes_for_other_projects) { - setProjectDatas( + setDirectoryInfos( directoryPathSourceId, {{directoryPathSourceId, qmltypesPathSourceId, exampleModuleId, FileType::QmlTypes}, {directoryPathSourceId, qmltypes2PathSourceId, exampleModuleId, FileType::QmlTypes}}); @@ -3050,19 +3055,19 @@ TEST_F(ProjectStorageUpdater, watcher_updates_directories_and_but_not_included_q IsFileStatus(qmlDocumentSourceId3, 1, 21))), Field(&SynchronizationPackage::updatedProjectSourceIds, UnorderedElementsAre(directoryPathSourceId)), - Field(&SynchronizationPackage::projectDatas, - UnorderedElementsAre(IsProjectData(directoryPathSourceId, - qmlDocumentSourceId1, - ModuleId{}, - FileType::QmlDocument), - IsProjectData(directoryPathSourceId, - qmlDocumentSourceId2, - ModuleId{}, - FileType::QmlDocument), - IsProjectData(directoryPathSourceId, - qmlDocumentSourceId3, - ModuleId{}, - FileType::QmlDocument)))))); + Field(&SynchronizationPackage::directoryInfos, + UnorderedElementsAre(IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId1, + ModuleId{}, + FileType::QmlDocument), + IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId2, + ModuleId{}, + FileType::QmlDocument), + IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId3, + ModuleId{}, + FileType::QmlDocument)))))); updater.pathsWithIdsChanged({{directoryProjectChunkId, {directoryPathSourceId}}, {qmlDocumentProjectChunkId, @@ -3127,19 +3132,19 @@ TEST_F(ProjectStorageUpdater, watcher_updates_qmldir_and_but_not_included_qml_do IsFileStatus(qmlDocumentSourceId3, 1, 21))), Field(&SynchronizationPackage::updatedProjectSourceIds, UnorderedElementsAre(directoryPathSourceId)), - Field(&SynchronizationPackage::projectDatas, - UnorderedElementsAre(IsProjectData(directoryPathSourceId, - qmlDocumentSourceId1, - ModuleId{}, - FileType::QmlDocument), - IsProjectData(directoryPathSourceId, - qmlDocumentSourceId2, - ModuleId{}, - FileType::QmlDocument), - IsProjectData(directoryPathSourceId, - qmlDocumentSourceId3, - ModuleId{}, - FileType::QmlDocument)))))); + Field(&SynchronizationPackage::directoryInfos, + UnorderedElementsAre(IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId1, + ModuleId{}, + FileType::QmlDocument), + IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId2, + ModuleId{}, + FileType::QmlDocument), + IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId3, + ModuleId{}, + FileType::QmlDocument)))))); updater.pathsWithIdsChanged({{qmldirProjectChunkId, {qmlDirPathSourceId}}, {qmlDocumentProjectChunkId, @@ -3148,7 +3153,7 @@ TEST_F(ProjectStorageUpdater, watcher_updates_qmldir_and_but_not_included_qml_do TEST_F(ProjectStorageUpdater, watcher_updates_qmldir_and_but_not_included_qmltypes) { - setProjectDatas( + setDirectoryInfos( directoryPathSourceId, {{directoryPathSourceId, qmltypesPathSourceId, exampleModuleId, FileType::QmlTypes}, {directoryPathSourceId, qmltypes2PathSourceId, exampleModuleId, FileType::QmlTypes}, @@ -3226,27 +3231,27 @@ TEST_F(ProjectStorageUpdater, watcher_updates_qmldir_and_but_not_included_qmltyp IsFileStatus(qmlDocumentSourceId3, 1, 21))), Field(&SynchronizationPackage::updatedProjectSourceIds, UnorderedElementsAre(directoryPathSourceId)), - Field(&SynchronizationPackage::projectDatas, - UnorderedElementsAre(IsProjectData(directoryPathSourceId, - qmltypesPathSourceId, - exampleCppNativeModuleId, - FileType::QmlTypes), - IsProjectData(directoryPathSourceId, - qmltypes2PathSourceId, - exampleCppNativeModuleId, - FileType::QmlTypes), - IsProjectData(directoryPathSourceId, - qmlDocumentSourceId1, - ModuleId{}, - FileType::QmlDocument), - IsProjectData(directoryPathSourceId, - qmlDocumentSourceId2, - ModuleId{}, - FileType::QmlDocument), - IsProjectData(directoryPathSourceId, - qmlDocumentSourceId3, - ModuleId{}, - FileType::QmlDocument)))))); + Field(&SynchronizationPackage::directoryInfos, + UnorderedElementsAre(IsDirectoryInfo(directoryPathSourceId, + qmltypesPathSourceId, + exampleCppNativeModuleId, + FileType::QmlTypes), + IsDirectoryInfo(directoryPathSourceId, + qmltypes2PathSourceId, + exampleCppNativeModuleId, + FileType::QmlTypes), + IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId1, + ModuleId{}, + FileType::QmlDocument), + IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId2, + ModuleId{}, + FileType::QmlDocument), + IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId3, + ModuleId{}, + FileType::QmlDocument)))))); updater.pathsWithIdsChanged( {{qmldirProjectChunkId, {qmlDirPathSourceId}}, @@ -3274,7 +3279,7 @@ TEST_F(ProjectStorageUpdater, input_is_reused_next_call_if_an_error_happens) FirstType 2.2 First2.qml SecondType 2.2 Second.qml)"}; setContent(u"/path/qmldir", qmldir); - setProjectDatas( + setDirectoryInfos( directoryPathSourceId, {{directoryPathSourceId, qmltypesPathSourceId, exampleModuleId, FileType::QmlTypes}, {directoryPathSourceId, qmltypes2PathSourceId, exampleModuleId, FileType::QmlTypes}}); @@ -3321,7 +3326,7 @@ TEST_F(ProjectStorageUpdater, input_is_reused_next_call_if_an_error_happens) qmlDocumentSourceId2, qmltypesPathSourceId, qmltypes2PathSourceId)), - Field(&SynchronizationPackage::projectDatas, IsEmpty())))); + Field(&SynchronizationPackage::directoryInfos, IsEmpty())))); updater.pathsWithIdsChanged( {{qmlDocumentProjectChunkId, {qmlDocumentSourceId1, qmlDocumentSourceId2}}}); @@ -3334,7 +3339,7 @@ TEST_F(ProjectStorageUpdater, input_is_reused_next_call_if_an_error_happens_and_ FirstType 2.2 First2.qml SecondType 2.2 Second.qml)"}; setContent(u"/path/qmldir", qmldir); - setProjectDatas( + setDirectoryInfos( directoryPathSourceId, {{directoryPathSourceId, qmltypesPathSourceId, exampleModuleId, FileType::QmlTypes}, {directoryPathSourceId, qmltypes2PathSourceId, exampleModuleId, FileType::QmlTypes}}); @@ -3381,7 +3386,7 @@ TEST_F(ProjectStorageUpdater, input_is_reused_next_call_if_an_error_happens_and_ qmlDocumentSourceId2, qmltypesPathSourceId, qmltypes2PathSourceId)), - Field(&SynchronizationPackage::projectDatas, IsEmpty())))); + Field(&SynchronizationPackage::directoryInfos, IsEmpty())))); updater.pathsWithIdsChanged( {{qmltypesProjectChunkId, {qmltypesPathSourceId, qmltypes2PathSourceId}}, @@ -3395,7 +3400,7 @@ TEST_F(ProjectStorageUpdater, input_is_reused_next_call_if_an_error_happens_and_ FirstType 2.2 First2.qml SecondType 2.2 Second.qml)"}; setContent(u"/path/qmldir", qmldir); - setProjectDatas( + setDirectoryInfos( directoryPathSourceId, {{directoryPathSourceId, qmltypesPathSourceId, exampleModuleId, FileType::QmlTypes}, {directoryPathSourceId, qmltypes2PathSourceId, exampleModuleId, FileType::QmlTypes}, @@ -3444,7 +3449,7 @@ TEST_F(ProjectStorageUpdater, input_is_reused_next_call_if_an_error_happens_and_ qmlDocumentSourceId2, qmltypesPathSourceId, qmltypes2PathSourceId)), - Field(&SynchronizationPackage::projectDatas, IsEmpty())))); + Field(&SynchronizationPackage::directoryInfos, IsEmpty())))); updater.pathsWithIdsChanged( {{qmltypesProjectChunkId, {qmltypesPathSourceId, qmltypes2PathSourceId}}, @@ -3458,7 +3463,7 @@ TEST_F(ProjectStorageUpdater, input_is_cleared_after_successful_update) FirstType 2.2 First2.qml SecondType 2.2 Second.qml)"}; setContent(u"/path/qmldir", qmldir); - setProjectDatas( + setDirectoryInfos( directoryPathSourceId, {{directoryPathSourceId, qmltypesPathSourceId, exampleModuleId, FileType::QmlTypes}, {directoryPathSourceId, qmltypes2PathSourceId, exampleModuleId, FileType::QmlTypes}}); @@ -3491,7 +3496,7 @@ TEST_F(ProjectStorageUpdater, input_is_cleared_after_successful_update) IsFileStatus(qmlDocumentSourceId2, 1, 21))), Field(&SynchronizationPackage::updatedFileStatusSourceIds, UnorderedElementsAre(qmlDocumentSourceId1, qmlDocumentSourceId2)), - Field(&SynchronizationPackage::projectDatas, IsEmpty())))); + Field(&SynchronizationPackage::directoryInfos, IsEmpty())))); updater.pathsWithIdsChanged( {{qmlDocumentProjectChunkId, {qmlDocumentSourceId1, qmlDocumentSourceId2}}}); diff --git a/tests/unit/tests/unittests/projectstorage/qmltypesparser-test.cpp b/tests/unit/tests/unittests/projectstorage/qmltypesparser-test.cpp index 3acddb12a09..aeb68cc2d6c 100644 --- a/tests/unit/tests/unittests/projectstorage/qmltypesparser-test.cpp +++ b/tests/unit/tests/unittests/projectstorage/qmltypesparser-test.cpp @@ -177,7 +177,7 @@ protected: Synchronization::Types types; SourceId qmltypesFileSourceId{sourcePathCache.sourceId("path/to/types.qmltypes")}; ModuleId qtQmlNativeModuleId = storage.moduleId("QtQml", ModuleKind::CppLibrary); - Synchronization::ProjectData projectData{qmltypesFileSourceId, + Synchronization::DirectoryInfo directoryInfo{qmltypesFileSourceId, qmltypesFileSourceId, qtQmlNativeModuleId, Synchronization::FileType::QmlTypes}; @@ -191,7 +191,7 @@ TEST_F(QmlTypesParser, imports) dependencies: ["QtQuick 2.15", "QtQuick.Window 2.1", "QtFoo 6"]})"}; - parser.parse(source, imports, types, projectData); + parser.parse(source, imports, types, directoryInfo); ASSERT_THAT(imports, UnorderedElementsAre(IsImport(storage.moduleId("QML", ModuleKind::CppLibrary), @@ -218,7 +218,7 @@ TEST_F(QmlTypesParser, types) Component { name: "QObject"} Component { name: "QQmlComponent"}})"}; - parser.parse(source, imports, types, projectData); + parser.parse(source, imports, types, directoryInfo); ASSERT_THAT(types, UnorderedElementsAre(IsType("QObject", @@ -241,7 +241,7 @@ TEST_F(QmlTypesParser, prototype) Component { name: "QQmlComponent" prototype: "QObject"}})"}; - parser.parse(source, imports, types, projectData); + parser.parse(source, imports, types, directoryInfo); ASSERT_THAT(types, UnorderedElementsAre(IsType("QObject", @@ -264,7 +264,7 @@ TEST_F(QmlTypesParser, extension) Component { name: "QQmlComponent" extension: "QObject"}})"}; - parser.parse(source, imports, types, projectData); + parser.parse(source, imports, types, directoryInfo); ASSERT_THAT(types, UnorderedElementsAre(IsType("QObject", @@ -289,7 +289,7 @@ TEST_F(QmlTypesParser, exported_types) ModuleId qmlModuleId = storage.moduleId("QML", ModuleKind::QmlLibrary); ModuleId qtQmlModuleId = storage.moduleId("QtQml", ModuleKind::QmlLibrary); - parser.parse(source, imports, types, projectData); + parser.parse(source, imports, types, directoryInfo); ASSERT_THAT( types, @@ -312,7 +312,7 @@ TEST_F(QmlTypesParser, properties) Property { name: "targets"; type: "QQuickItem"; isList: true; isReadonly: true; isPointer: true } }})"}; - parser.parse(source, imports, types, projectData); + parser.parse(source, imports, types, directoryInfo); ASSERT_THAT(types, ElementsAre(Field( @@ -346,7 +346,7 @@ TEST_F(QmlTypesParser, properties_with_qualified_types) Property { name: "values2"; type: "Qt::Vector" } }})"}; - parser.parse(source, imports, types, projectData); + parser.parse(source, imports, types, directoryInfo); ASSERT_THAT(types, Contains( @@ -372,7 +372,7 @@ TEST_F(QmlTypesParser, properties_without_type) Property { name: "target"; type: "QObject"; isPointer: true } }})"}; - parser.parse(source, imports, types, projectData); + parser.parse(source, imports, types, directoryInfo); ASSERT_THAT(types, ElementsAre( @@ -405,7 +405,7 @@ TEST_F(QmlTypesParser, functions) } }})"}; - parser.parse(source, imports, types, projectData); + parser.parse(source, imports, types, directoryInfo); ASSERT_THAT(types, ElementsAre(Field( @@ -436,7 +436,7 @@ TEST_F(QmlTypesParser, skip_java_script_functions) } }})"}; - parser.parse(source, imports, types, projectData); + parser.parse(source, imports, types, directoryInfo); ASSERT_THAT(types, ElementsAre(Field(&Synchronization::Type::functionDeclarations, IsEmpty()))); } @@ -456,7 +456,7 @@ TEST_F(QmlTypesParser, functions_with_qualified_types) } }})"}; - parser.parse(source, imports, types, projectData); + parser.parse(source, imports, types, directoryInfo); ASSERT_THAT(types, Contains( @@ -491,7 +491,7 @@ TEST_F(QmlTypesParser, signals) } }})"}; - parser.parse(source, imports, types, projectData); + parser.parse(source, imports, types, directoryInfo); ASSERT_THAT(types, ElementsAre(Field(&Synchronization::Type::signalDeclarations, @@ -524,7 +524,7 @@ TEST_F(QmlTypesParser, signals_with_qualified_types) } }})"}; - parser.parse(source, imports, types, projectData); + parser.parse(source, imports, types, directoryInfo); ASSERT_THAT(types, Contains( @@ -557,7 +557,7 @@ TEST_F(QmlTypesParser, enumerations) } }})"}; - parser.parse(source, imports, types, projectData); + parser.parse(source, imports, types, directoryInfo); ASSERT_THAT(types, Contains(Field( @@ -596,7 +596,7 @@ TEST_F(QmlTypesParser, enumeration_is_exported_as_type) exports: ["QML/QtObject 1.0", "QtQml/QtObject 2.1"] }})"}; - parser.parse(source, imports, types, projectData); + parser.parse(source, imports, types, directoryInfo); QmlDesigner::Storage::TypeTraits traits{QmlDesigner::Storage::TypeTraitsKind::Value}; traits.isEnum = true; @@ -642,7 +642,7 @@ TEST_F(QmlTypesParser, enumeration_is_exported_as_type_with_alias) exports: ["QML/QtObject 1.0", "QtQml/QtObject 2.1"] }})"}; - parser.parse(source, imports, types, projectData); + parser.parse(source, imports, types, directoryInfo); QmlDesigner::Storage::TypeTraits traits{QmlDesigner::Storage::TypeTraitsKind::Value}; traits.isEnum = true; @@ -690,7 +690,7 @@ TEST_F(QmlTypesParser, enumeration_is_exported_as_type_with_alias_too) exports: ["QML/QtObject 1.0", "QtQml/QtObject 2.1"] }})"}; - parser.parse(source, imports, types, projectData); + parser.parse(source, imports, types, directoryInfo); QmlDesigner::Storage::TypeTraits traits{QmlDesigner::Storage::TypeTraitsKind::Value}; traits.isEnum = true; @@ -728,7 +728,7 @@ TEST_F(QmlTypesParser, enumeration_is_referenced_by_qualified_name) } }})"}; - parser.parse(source, imports, types, projectData); + parser.parse(source, imports, types, directoryInfo); ASSERT_THAT(types, Contains(Field(&Synchronization::Type::propertyDeclarations, @@ -756,7 +756,7 @@ TEST_F(QmlTypesParser, alias_enumeration_is_referenced_by_qualified_name) } }})"}; - parser.parse(source, imports, types, projectData); + parser.parse(source, imports, types, directoryInfo); ASSERT_THAT(types, Contains(Field(&Synchronization::Type::propertyDeclarations, @@ -773,7 +773,7 @@ TEST_F(QmlTypesParser, access_type_is_reference) Component { name: "QObject" accessSemantics: "reference"}})"}; - parser.parse(source, imports, types, projectData); + parser.parse(source, imports, types, directoryInfo); ASSERT_THAT(types, ElementsAre(IsTypeTrait(Storage::TypeTraitsKind::Reference))); } @@ -785,7 +785,7 @@ TEST_F(QmlTypesParser, access_type_is_value) Component { name: "QObject" accessSemantics: "value"}})"}; - parser.parse(source, imports, types, projectData); + parser.parse(source, imports, types, directoryInfo); ASSERT_THAT(types, ElementsAre(IsTypeTrait(Storage::TypeTraitsKind::Value))); } @@ -797,7 +797,7 @@ TEST_F(QmlTypesParser, access_type_is_sequence) Component { name: "QObject" accessSemantics: "sequence"}})"}; - parser.parse(source, imports, types, projectData); + parser.parse(source, imports, types, directoryInfo); ASSERT_THAT(types, ElementsAre(IsTypeTrait(Storage::TypeTraitsKind::Sequence))); } @@ -809,7 +809,7 @@ TEST_F(QmlTypesParser, access_type_is_none) Component { name: "QObject" accessSemantics: "none"}})"}; - parser.parse(source, imports, types, projectData); + parser.parse(source, imports, types, directoryInfo); ASSERT_THAT(types, ElementsAre(IsTypeTrait(Storage::TypeTraitsKind::None))); } @@ -821,7 +821,7 @@ TEST_F(QmlTypesParser, uses_custom_parser) Component { name: "QObject" hasCustomParser: true }})"}; - parser.parse(source, imports, types, projectData); + parser.parse(source, imports, types, directoryInfo); ASSERT_THAT(types, ElementsAre(IsTypeTrait(UsesCustomParser(true)))); } @@ -833,7 +833,7 @@ TEST_F(QmlTypesParser, uses_no_custom_parser) Component { name: "QObject" hasCustomParser: false }})"}; - parser.parse(source, imports, types, projectData); + parser.parse(source, imports, types, directoryInfo); ASSERT_THAT(types, ElementsAre(IsTypeTrait(UsesCustomParser(false)))); } @@ -845,7 +845,7 @@ TEST_F(QmlTypesParser, default_property) Component { name: "QObject" defaultProperty: "children" }})"}; - parser.parse(source, imports, types, projectData); + parser.parse(source, imports, types, directoryInfo); ASSERT_THAT(types, ElementsAre(Field(&Synchronization::Type::defaultPropertyName, Eq("children")))); @@ -854,7 +854,7 @@ TEST_F(QmlTypesParser, default_property) TEST_F(QmlTypesParser, skip_template_item) { ModuleId moduleId = storage.moduleId("QtQuick.Templates", ModuleKind::CppLibrary); - Synchronization::ProjectData projectData{qmltypesFileSourceId, + Synchronization::DirectoryInfo directoryInfo{qmltypesFileSourceId, qmltypesFileSourceId, moduleId, Synchronization::FileType::QmlTypes}; @@ -863,7 +863,7 @@ TEST_F(QmlTypesParser, skip_template_item) Component { name: "QQuickItem"} Component { name: "QQmlComponent"}})"}; - parser.parse(source, imports, types, projectData); + parser.parse(source, imports, types, directoryInfo); ASSERT_THAT(types, UnorderedElementsAre(IsType("QQmlComponent", From 09e012cbb7ff780ba75478f5bd7edf7c7210f5f3 Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Mon, 6 May 2024 18:49:00 +0300 Subject: [PATCH 078/154] QmlDesigner: Rename "Effect" to "Item" in the content library Item includes any item except materials and textures Change-Id: Icf981661dcd611638fe33fae18f65ad62a5e617c Reviewed-by: Miikka Heikkinen Reviewed-by: Qt CI Patch Build Bot --- .../ContentLibraryEffectsView.qml | 2 +- ...braryEffect.qml => ContentLibraryItem.qml} | 6 +- .../ContentLibraryUserView.qml | 2 +- src/plugins/qmldesigner/CMakeLists.txt | 2 +- .../contentlibraryeffectscategory.cpp | 10 +-- .../contentlibraryeffectscategory.h | 12 +-- .../contentlibraryeffectsmodel.cpp | 8 +- .../contentlibraryeffectsmodel.h | 6 +- ...braryeffect.cpp => contentlibraryitem.cpp} | 32 ++++---- ...ntlibraryeffect.h => contentlibraryitem.h} | 14 ++-- .../contentlibraryusermodel.cpp | 10 +-- .../contentlibrary/contentlibraryusermodel.h | 13 ++-- .../contentlibrary/contentlibraryview.cpp | 74 ++++++++++--------- .../contentlibrary/contentlibraryview.h | 10 +-- .../contentlibrary/contentlibrarywidget.cpp | 25 ++++--- .../contentlibrary/contentlibrarywidget.h | 9 +-- .../components/edit3d/edit3dview.cpp | 2 +- .../components/edit3d/edit3dwidget.cpp | 6 +- .../navigator/navigatortreemodel.cpp | 6 +- .../qmldesigner/qmldesignerconstants.h | 2 +- 20 files changed, 126 insertions(+), 125 deletions(-) rename share/qtcreator/qmldesigner/contentLibraryQmlSource/{ContentLibraryEffect.qml => ContentLibraryItem.qml} (92%) rename src/plugins/qmldesigner/components/contentlibrary/{contentlibraryeffect.cpp => contentlibraryitem.cpp} (50%) rename src/plugins/qmldesigner/components/contentlibrary/{contentlibraryeffect.h => contentlibraryitem.h} (80%) diff --git a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryEffectsView.qml b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryEffectsView.qml index f6df99156bf..199f784db18 100644 --- a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryEffectsView.qml +++ b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryEffectsView.qml @@ -91,7 +91,7 @@ HelperWidgets.ScrollView { id: repeater model: bundleCategoryItems - delegate: ContentLibraryEffect { + delegate: ContentLibraryItem { width: root.cellWidth height: root.cellHeight diff --git a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryEffect.qml b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryItem.qml similarity index 92% rename from share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryEffect.qml rename to share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryItem.qml index bf60ef66115..e6e5cd05c30 100644 --- a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryEffect.qml +++ b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryItem.qml @@ -24,7 +24,7 @@ Item { onPressed: (mouse) => { if (mouse.button === Qt.LeftButton && !ContentLibraryBackend.rootView.importerRunning) - ContentLibraryBackend.rootView.startDragEffect(modelData, mapToGlobal(mouse.x, mouse.y)) + ContentLibraryBackend.rootView.startDragItem(modelData, mapToGlobal(mouse.x, mouse.y)) else if (mouse.button === Qt.RightButton) root.showContextMenu() } @@ -43,7 +43,7 @@ Item { source: modelData.bundleItemIcon cache: false - Rectangle { // circular indicator for imported bundle effect + Rectangle { // circular indicator for imported bundle item width: 10 height: 10 radius: 5 @@ -57,7 +57,7 @@ Item { ToolTip { visible: indicatorMouseArea.containsMouse - text: qsTr("Effect is imported to project") + text: qsTr("Item is imported to the project") delay: 1000 } diff --git a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryUserView.qml b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryUserView.qml index 8b77c06a77d..55a03a2f07c 100644 --- a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryUserView.qml +++ b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryUserView.qml @@ -135,7 +135,7 @@ HelperWidgets.ScrollView { } DelegateChoice { roleValue: "item" - delegate: ContentLibraryEffect { + delegate: ContentLibraryItem { width: root.cellWidth height: root.cellHeight diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index 1d3fa69d937..d41518e0182 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -830,7 +830,7 @@ extend_qtc_plugin(QmlDesigner contentlibrarymaterialscategory.cpp contentlibrarymaterialscategory.h contentlibrarymaterial.cpp contentlibrarymaterial.h contentlibraryiconprovider.cpp contentlibraryiconprovider.h - contentlibraryeffect.cpp contentlibraryeffect.h + contentlibraryitem.cpp contentlibraryitem.h contentlibraryeffectscategory.cpp contentlibraryeffectscategory.h contentlibraryeffectsmodel.cpp contentlibraryeffectsmodel.h contentlibraryusermodel.cpp contentlibraryusermodel.h diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectscategory.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectscategory.cpp index 38e6eed3dad..f904775d52d 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectscategory.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectscategory.cpp @@ -3,14 +3,12 @@ #include "contentlibraryeffectscategory.h" -#include "contentlibraryeffect.h" - namespace QmlDesigner { ContentLibraryEffectsCategory::ContentLibraryEffectsCategory(QObject *parent, const QString &name) : QObject(parent), m_name(name) {} -void ContentLibraryEffectsCategory::addBundleItem(ContentLibraryEffect *bundleItem) +void ContentLibraryEffectsCategory::addBundleItem(ContentLibraryItem *bundleItem) { m_categoryItems.append(bundleItem); } @@ -19,7 +17,7 @@ bool ContentLibraryEffectsCategory::updateImportedState(const QStringList &impor { bool changed = false; - for (ContentLibraryEffect *item : std::as_const(m_categoryItems)) + for (ContentLibraryItem *item : std::as_const(m_categoryItems)) changed |= item->setImported(importedItems.contains(item->qml().chopped(4))); return changed; @@ -28,7 +26,7 @@ bool ContentLibraryEffectsCategory::updateImportedState(const QStringList &impor bool ContentLibraryEffectsCategory::filter(const QString &searchText) { bool visible = false; - for (ContentLibraryEffect *item : std::as_const(m_categoryItems)) + for (ContentLibraryItem *item : std::as_const(m_categoryItems)) visible |= item->filter(searchText); if (visible != m_visible) { @@ -55,7 +53,7 @@ bool ContentLibraryEffectsCategory::expanded() const return m_expanded; } -QList ContentLibraryEffectsCategory::categoryItems() const +QList ContentLibraryEffectsCategory::categoryItems() const { return m_categoryItems; } diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectscategory.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectscategory.h index 79737c1ec24..9f56de3c6b6 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectscategory.h +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectscategory.h @@ -3,12 +3,12 @@ #pragma once +#include "contentlibraryitem.h" + #include namespace QmlDesigner { -class ContentLibraryEffect; - class ContentLibraryEffectsCategory : public QObject { Q_OBJECT @@ -16,20 +16,20 @@ class ContentLibraryEffectsCategory : public QObject Q_PROPERTY(QString bundleCategoryName MEMBER m_name CONSTANT) Q_PROPERTY(bool bundleCategoryVisible MEMBER m_visible NOTIFY categoryVisibleChanged) Q_PROPERTY(bool bundleCategoryExpanded MEMBER m_expanded NOTIFY categoryExpandChanged) - Q_PROPERTY(QList bundleCategoryItems MEMBER m_categoryItems + Q_PROPERTY(QList bundleCategoryItems MEMBER m_categoryItems NOTIFY categoryItemsChanged) public: ContentLibraryEffectsCategory(QObject *parent, const QString &name); - void addBundleItem(ContentLibraryEffect *bundleItem); + void addBundleItem(ContentLibraryItem *bundleItem); bool updateImportedState(const QStringList &importedMats); bool filter(const QString &searchText); QString name() const; bool visible() const; bool expanded() const; - QList categoryItems() const; + QList categoryItems() const; signals: void categoryVisibleChanged(); @@ -41,7 +41,7 @@ private: bool m_visible = true; bool m_expanded = true; - QList m_categoryItems; + QList m_categoryItems; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectsmodel.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectsmodel.cpp index 2d467d811ed..e0fe2b6c54e 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectsmodel.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectsmodel.cpp @@ -4,8 +4,8 @@ #include "contentlibraryeffectsmodel.h" #include "contentlibrarybundleimporter.h" -#include "contentlibraryeffect.h" #include "contentlibraryeffectscategory.h" +#include "contentlibraryitem.h" #include "contentlibrarywidget.h" #include @@ -171,7 +171,7 @@ void ContentLibraryEffectsModel::loadBundle() TypeName type = QLatin1String("%1.%2") .arg(bundleType, qml.chopped(4)).toLatin1(); // chopped(4): remove .qml - auto bundleItem = new ContentLibraryEffect(category, itemName, qml, type, icon, files); + auto bundleItem = new ContentLibraryItem(category, itemName, qml, type, icon, files); category->addBundleItem(bundleItem); } @@ -251,7 +251,7 @@ void ContentLibraryEffectsModel::resetModel() endResetModel(); } -void ContentLibraryEffectsModel::addInstance(ContentLibraryEffect *bundleItem) +void ContentLibraryEffectsModel::addInstance(ContentLibraryItem *bundleItem) { QString err = m_widget->importer()->importComponent(m_bundlePath, bundleItem->type(), bundleItem->qml(), @@ -263,7 +263,7 @@ void ContentLibraryEffectsModel::addInstance(ContentLibraryEffect *bundleItem) qWarning() << __FUNCTION__ << err; } -void ContentLibraryEffectsModel::removeFromProject(ContentLibraryEffect *bundleItem) +void ContentLibraryEffectsModel::removeFromProject(ContentLibraryItem *bundleItem) { QString err = m_widget->importer()->unimportComponent(bundleItem->type(), bundleItem->qml()); diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectsmodel.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectsmodel.h index 88f60f06b60..5bcb857fca5 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectsmodel.h +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectsmodel.h @@ -8,7 +8,7 @@ namespace QmlDesigner { -class ContentLibraryEffect; +class ContentLibraryItem; class ContentLibraryEffectsCategory; class ContentLibraryWidget; @@ -41,8 +41,8 @@ public: void resetModel(); void updateIsEmpty(); - Q_INVOKABLE void addInstance(QmlDesigner::ContentLibraryEffect *bundleItem); - Q_INVOKABLE void removeFromProject(QmlDesigner::ContentLibraryEffect *bundleItem); + Q_INVOKABLE void addInstance(QmlDesigner::ContentLibraryItem *bundleItem); + Q_INVOKABLE void removeFromProject(QmlDesigner::ContentLibraryItem *bundleItem); QString bundleId() const; diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffect.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryitem.cpp similarity index 50% rename from src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffect.cpp rename to src/plugins/qmldesigner/components/contentlibrary/contentlibraryitem.cpp index 0ae800a3690..38fb69abbc2 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffect.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryitem.cpp @@ -1,23 +1,23 @@ // 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 "contentlibraryeffect.h" +#include "contentlibraryitem.h" namespace QmlDesigner { -ContentLibraryEffect::ContentLibraryEffect(QObject *parent, - const QString &name, - const QString &qml, - const TypeName &type, - const QUrl &icon, - const QStringList &files) +ContentLibraryItem::ContentLibraryItem(QObject *parent, + const QString &name, + const QString &qml, + const TypeName &type, + const QUrl &icon, + const QStringList &files) : QObject(parent), m_name(name), m_qml(qml), m_type(type), m_icon(icon), m_files(files) { m_allFiles = m_files; m_allFiles.push_back(m_qml); } -bool ContentLibraryEffect::filter(const QString &searchText) +bool ContentLibraryItem::filter(const QString &searchText) { if (m_visible != m_name.contains(searchText, Qt::CaseInsensitive)) { m_visible = !m_visible; @@ -27,32 +27,32 @@ bool ContentLibraryEffect::filter(const QString &searchText) return m_visible; } -QUrl ContentLibraryEffect::icon() const +QUrl ContentLibraryItem::icon() const { return m_icon; } -QString ContentLibraryEffect::qml() const +QString ContentLibraryItem::qml() const { return m_qml; } -TypeName ContentLibraryEffect::type() const +TypeName ContentLibraryItem::type() const { return m_type; } -QStringList ContentLibraryEffect::files() const +QStringList ContentLibraryItem::files() const { return m_files; } -bool ContentLibraryEffect::visible() const +bool ContentLibraryItem::visible() const { return m_visible; } -bool ContentLibraryEffect::setImported(bool imported) +bool ContentLibraryItem::setImported(bool imported) { if (m_imported != imported) { m_imported = imported; @@ -63,12 +63,12 @@ bool ContentLibraryEffect::setImported(bool imported) return false; } -bool ContentLibraryEffect::imported() const +bool ContentLibraryItem::imported() const { return m_imported; } -QStringList ContentLibraryEffect::allFiles() const +QStringList ContentLibraryItem::allFiles() const { return m_allFiles; } diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffect.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryitem.h similarity index 80% rename from src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffect.h rename to src/plugins/qmldesigner/components/contentlibrary/contentlibraryitem.h index a1050b7c67b..53587e73291 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffect.h +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryitem.h @@ -10,7 +10,7 @@ namespace QmlDesigner { -class ContentLibraryEffect : public QObject +class ContentLibraryItem : public QObject { Q_OBJECT @@ -22,12 +22,12 @@ class ContentLibraryEffect : public QObject Q_PROPERTY(QString itemType MEMBER m_itemType CONSTANT) public: - ContentLibraryEffect(QObject *parent, - const QString &name, - const QString &qml, - const TypeName &type, - const QUrl &icon, - const QStringList &files); + ContentLibraryItem(QObject *parent, + const QString &name, + const QString &qml, + const TypeName &type, + const QUrl &icon, + const QStringList &files); bool filter(const QString &searchText); diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp index 918bccca219..b8f528d6557 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp @@ -4,7 +4,7 @@ #include "contentlibraryusermodel.h" #include "contentlibrarybundleimporter.h" -#include "contentlibraryeffect.h" +#include "contentlibraryitem.h" #include "contentlibrarymaterial.h" #include "contentlibrarymaterialscategory.h" #include "contentlibrarytexture.h" @@ -82,7 +82,7 @@ void ContentLibraryUserModel::updateIsEmptyMaterials() void ContentLibraryUserModel::updateIsEmpty3D() { - bool anyItemVisible = Utils::anyOf(m_user3DItems, [&](ContentLibraryEffect *item) { + bool anyItemVisible = Utils::anyOf(m_user3DItems, [&](ContentLibraryItem *item) { return item->visible(); }); @@ -90,7 +90,7 @@ void ContentLibraryUserModel::updateIsEmpty3D() if (newEmpty != m_isEmpty3D) { m_isEmpty3D = newEmpty; - emit isEmptyMaterialsChanged(); + emit isEmpty3DChanged(); } } @@ -132,7 +132,7 @@ void ContentLibraryUserModel::addTextures(const QStringList &paths) emit dataChanged(index(texSectionIdx), index(texSectionIdx)); } -void ContentLibraryUserModel::add3DInstance(ContentLibraryEffect *bundleItem) +void ContentLibraryUserModel::add3DInstance(ContentLibraryItem *bundleItem) { QString err = m_widget->importer()->importComponent(m_bundlePath3D.path(), bundleItem->type(), bundleItem->qml(), @@ -408,7 +408,7 @@ void ContentLibraryUserModel::load3DBundle() QString qml = itemObj.value("qml").toString(); TypeName type = QLatin1String("%1.%2").arg(typePrefix, qml.chopped(4)).toLatin1(); - auto bundleItem = new ContentLibraryEffect(nullptr, itemName, qml, type, icon, files); + auto bundleItem = new ContentLibraryItem(nullptr, itemName, qml, type, icon, files); m_user3DItems.append(bundleItem); } diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.h index 79889135c6e..c9cf4ad0772 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.h +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.h @@ -12,7 +12,7 @@ QT_FORWARD_DECLARE_CLASS(QUrl) namespace QmlDesigner { -class ContentLibraryEffect; +class ContentLibraryItem; class ContentLibraryMaterial; class ContentLibraryTexture; class ContentLibraryWidget; @@ -30,8 +30,8 @@ class ContentLibraryUserModel : public QAbstractListModel Q_PROPERTY(bool hasModelSelection READ hasModelSelection NOTIFY hasModelSelectionChanged) Q_PROPERTY(QList userMaterials MEMBER m_userMaterials NOTIFY userMaterialsChanged) Q_PROPERTY(QList userTextures MEMBER m_userTextures NOTIFY userTexturesChanged) - Q_PROPERTY(QList user3DItems MEMBER m_user3DItems NOTIFY user3DItemsChanged) - Q_PROPERTY(QList userEffects MEMBER m_userEffects NOTIFY userEffectsChanged) + Q_PROPERTY(QList user3DItems MEMBER m_user3DItems NOTIFY user3DItemsChanged) + Q_PROPERTY(QList userEffects MEMBER m_userEffects NOTIFY userEffectsChanged) public: ContentLibraryUserModel(ContentLibraryWidget *parent = nullptr); @@ -61,7 +61,7 @@ public: void addMaterial(const QString &name, const QString &qml, const QUrl &icon, const QStringList &files); void addTextures(const QStringList &paths); - void add3DInstance(ContentLibraryEffect *bundleItem); + void add3DInstance(ContentLibraryItem *bundleItem); void setBundleObj(const QJsonObject &newBundleObj); QJsonObject &bundleJsonObjectRef(); @@ -86,7 +86,6 @@ signals: void applyToSelectedTriggered(QmlDesigner::ContentLibraryMaterial *mat, bool add = false); - void matBundleExistsChanged(); void bundle3DExistsChanged(); @@ -106,8 +105,8 @@ private: QList m_userMaterials; QList m_userTextures; - QList m_userEffects; - QList m_user3DItems; + QList m_userEffects; + QList m_user3DItems; QStringList m_userCategories; QJsonObject m_bundleObjMaterial; diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp index 798ad840358..a72475f6d75 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp @@ -4,7 +4,7 @@ #include "contentlibraryview.h" #include "contentlibrarybundleimporter.h" -#include "contentlibraryeffect.h" +#include "contentlibraryitem.h" #include "contentlibraryeffectsmodel.h" #include "contentlibrarymaterial.h" #include "contentlibrarymaterialsmodel.h" @@ -70,9 +70,9 @@ WidgetInfo ContentLibraryView::widgetInfo() [&] (QmlDesigner::ContentLibraryTexture *tex) { m_draggedBundleTexture = tex; }); - connect(m_widget, &ContentLibraryWidget::bundleEffectDragStarted, this, - [&] (QmlDesigner::ContentLibraryEffect *eff) { - m_draggedBundleEffect = eff; + connect(m_widget, &ContentLibraryWidget::bundleItemDragStarted, this, + [&] (QmlDesigner::ContentLibraryItem *item) { + m_draggedBundleItem = item; }); connect(m_widget, &ContentLibraryWidget::addTextureRequested, this, @@ -143,23 +143,23 @@ void ContentLibraryView::connectImporter() QTC_ASSERT(typeName.size(), return); if (isMaterialBundle(bundleId)) { applyBundleMaterialToDropTarget({}, typeName); - } else if (isEffectBundle(bundleId)) { - if (!m_bundleEffectTarget) - m_bundleEffectTarget = Utils3D::active3DSceneNode(this); + } else if (isItemBundle(bundleId)) { + if (!m_bundleItemTarget) + m_bundleItemTarget = Utils3D::active3DSceneNode(this); - QTC_ASSERT(m_bundleEffectTarget, return); + QTC_ASSERT(m_bundleItemTarget, return); executeInTransaction("ContentLibraryView::widgetInfo", [&] { - QVector3D pos = m_bundleEffectPos.value(); - ModelNode newEffNode = createModelNode( + QVector3D pos = m_bundleItemPos.value(); + ModelNode newNode = createModelNode( typeName, -1, -1, {{"x", pos.x()}, {"y", pos.y()}, {"z", pos.z()}}); - m_bundleEffectTarget.defaultNodeListProperty().reparentHere(newEffNode); + m_bundleItemTarget.defaultNodeListProperty().reparentHere(newNode); clearSelectedModelNodes(); - selectModelNode(newEffNode); + selectModelNode(newNode); }); - m_bundleEffectTarget = {}; - m_bundleEffectPos = {}; + m_bundleItemTarget = {}; + m_bundleItemPos = {}; } }); #else @@ -170,27 +170,27 @@ void ContentLibraryView::connectImporter() QTC_ASSERT(metaInfo.isValid(), return); if (isMaterialBundle(bundleId)) { applyBundleMaterialToDropTarget({}, metaInfo); - } else if (isEffectBundle(bundleId)) { - if (!m_bundleEffectTarget) - m_bundleEffectTarget = Utils3D::active3DSceneNode(this); + } else if (isItemBundle(bundleId)) { + if (!m_bundleItemTarget) + m_bundleItemTarget = Utils3D::active3DSceneNode(this); - QTC_ASSERT(m_bundleEffectTarget, return); + QTC_ASSERT(m_bundleItemTarget, return); executeInTransaction("ContentLibraryView::connectImporter", [&] { - QVector3D pos = m_bundleEffectPos.value(); - ModelNode newEffNode = createModelNode(metaInfo.typeName(), + QVector3D pos = m_bundleItemPos.value(); + ModelNode newNode = createModelNode(metaInfo.typeName(), metaInfo.majorVersion(), metaInfo.minorVersion(), {{"x", pos.x()}, {"y", pos.y()}, {"z", pos.z()}}); - m_bundleEffectTarget.defaultNodeListProperty().reparentHere(newEffNode); + m_bundleItemTarget.defaultNodeListProperty().reparentHere(newNode); clearSelectedModelNodes(); - selectModelNode(newEffNode); + selectModelNode(newNode); }); - m_bundleEffectTarget = {}; - m_bundleEffectPos = {}; + m_bundleItemTarget = {}; + m_bundleItemPos = {}; } }); #endif @@ -209,13 +209,13 @@ void ContentLibraryView::connectImporter() QmlObjectNode(mat).destroy(); }); }); - } else if (isEffectBundle(bundleId)) { - // delete instances of the bundle effect that is about to be unimported + } else if (isItemBundle(bundleId)) { + // delete instances of the bundle item that is about to be unimported executeInTransaction("ContentLibraryView::connectImporter", [&] { NodeMetaInfo metaInfo = model()->metaInfo(type); - QList effects = allModelNodesOfType(metaInfo); - for (ModelNode &eff : effects) - eff.destroy(); + QList nodes = allModelNodesOfType(metaInfo); + for (ModelNode &node : nodes) + node.destroy(); }); } }); @@ -227,7 +227,8 @@ bool ContentLibraryView::isMaterialBundle(const QString &bundleId) const return bundleId == compUtils.materialsBundleId() || bundleId == compUtils.userMaterialsBundleId(); } -bool ContentLibraryView::isEffectBundle(const QString &bundleId) const +// item bundle includes effects and 3D components +bool ContentLibraryView::isItemBundle(const QString &bundleId) const { auto compUtils = QmlDesignerPlugin::instance()->documentManager().generatedComponentUtils(); return bundleId == compUtils.effectsBundleId() || bundleId == compUtils.userEffectsBundleId() @@ -345,18 +346,18 @@ void ContentLibraryView::customNotification(const AbstractView *view, m_widget->addTexture(m_draggedBundleTexture); m_draggedBundleTexture = nullptr; - } else if (identifier == "drop_bundle_effect") { + } else if (identifier == "drop_bundle_item") { QTC_ASSERT(nodeList.size() == 1, return); auto compUtils = QmlDesignerPlugin::instance()->documentManager().generatedComponentUtils(); - bool is3D = m_draggedBundleEffect->type().startsWith(compUtils.user3DBundleType().toLatin1()); + bool is3D = m_draggedBundleItem->type().startsWith(compUtils.user3DBundleType().toLatin1()); - m_bundleEffectPos = data.size() == 1 ? data.first() : QVariant(); + m_bundleItemPos = data.size() == 1 ? data.first() : QVariant(); if (is3D) - m_widget->userModel()->add3DInstance(m_draggedBundleEffect); + m_widget->userModel()->add3DInstance(m_draggedBundleItem); else - m_widget->effectsModel()->addInstance(m_draggedBundleEffect); - m_bundleEffectTarget = nodeList.first() ? nodeList.first() : Utils3D::active3DSceneNode(this); + m_widget->effectsModel()->addInstance(m_draggedBundleItem); + m_bundleItemTarget = nodeList.first() ? nodeList.first() : Utils3D::active3DSceneNode(this); } else if (identifier == "add_material_to_content_lib") { QTC_ASSERT(nodeList.size() == 1 && data.size() == 1, return); @@ -760,6 +761,7 @@ void ContentLibraryView::updateBundlesQuick3DVersion() #endif m_widget->materialsModel()->setQuick3DImportVersion(major, minor); m_widget->effectsModel()->setQuick3DImportVersion(major, minor); + m_widget->userModel()->setQuick3DImportVersion(major, minor); } } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.h index 3c80193478f..232f7dd194c 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.h +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.h @@ -14,7 +14,7 @@ QT_FORWARD_DECLARE_CLASS(QPixmap) namespace QmlDesigner { -class ContentLibraryEffect; +class ContentLibraryItem; class ContentLibraryMaterial; class ContentLibraryTexture; class ContentLibraryWidget; @@ -50,7 +50,7 @@ public: private: void connectImporter(); bool isMaterialBundle(const QString &bundleId) const; - bool isEffectBundle(const QString &bundleId) const; + bool isItemBundle(const QString &bundleId) const; void active3DSceneChanged(qint32 sceneId); void updateBundlesQuick3DVersion(); void addLibMaterial(const ModelNode &mat, const QPixmap &icon); @@ -73,12 +73,12 @@ private: #endif QPointer m_widget; QList m_bundleMaterialTargets; - ModelNode m_bundleEffectTarget; // target of the dropped bundle effect - QVariant m_bundleEffectPos; // pos of the dropped bundle effect + ModelNode m_bundleItemTarget; // target of the dropped bundle item + QVariant m_bundleItemPos; // pos of the dropped bundle item QList m_selectedModels; // selected 3D model nodes ContentLibraryMaterial *m_draggedBundleMaterial = nullptr; ContentLibraryTexture *m_draggedBundleTexture = nullptr; - ContentLibraryEffect *m_draggedBundleEffect = nullptr; + ContentLibraryItem *m_draggedBundleItem = nullptr; bool m_bundleMaterialAddToSelected = false; bool m_hasQuick3DImport = false; qint32 m_sceneId = -1; diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.cpp index 96db7d7110d..1935f02d5a1 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.cpp @@ -4,8 +4,8 @@ #include "contentlibrarywidget.h" #include "contentlibrarybundleimporter.h" -#include "contentlibraryeffect.h" #include "contentlibraryeffectsmodel.h" +#include "contentlibraryitem.h" #include "contentlibrarymaterial.h" #include "contentlibrarymaterialsmodel.h" #include "contentlibrarytexture.h" @@ -68,18 +68,18 @@ bool ContentLibraryWidget::eventFilter(QObject *obj, QEvent *event) Model *model = document->currentModel(); QTC_ASSERT(model, return false); - if (m_effectToDrag) { + if (m_itemToDrag) { QMouseEvent *me = static_cast(event); if ((me->globalPos() - m_dragStartPoint).manhattanLength() > 20) { QByteArray data; QMimeData *mimeData = new QMimeData; QDataStream stream(&data, QIODevice::WriteOnly); - stream << m_effectToDrag->type(); - mimeData->setData(Constants::MIME_TYPE_BUNDLE_EFFECT, data); + stream << m_itemToDrag->type(); + mimeData->setData(Constants::MIME_TYPE_BUNDLE_ITEM, data); - emit bundleEffectDragStarted(m_effectToDrag); - model->startDrag(mimeData, m_effectToDrag->icon().toLocalFile()); - m_effectToDrag = nullptr; + emit bundleItemDragStarted(m_itemToDrag); + model->startDrag(mimeData, m_itemToDrag->icon().toLocalFile()); + m_itemToDrag = nullptr; } } else if (m_materialToDrag) { QMouseEvent *me = static_cast(event); @@ -113,7 +113,7 @@ bool ContentLibraryWidget::eventFilter(QObject *obj, QEvent *event) } } } else if (event->type() == QMouseEvent::MouseButtonRelease) { - m_effectToDrag = nullptr; + m_itemToDrag = nullptr; m_materialToDrag = nullptr; m_textureToDrag = nullptr; setIsDragging(false); @@ -697,6 +697,8 @@ void ContentLibraryWidget::setHasQuick3DImport(bool b) m_materialsModel->updateIsEmpty(); m_effectsModel->updateIsEmpty(); + m_userModel->updateIsEmptyMaterials(); + m_userModel->updateIsEmpty3D(); } bool ContentLibraryWidget::hasMaterialLibrary() const @@ -713,6 +715,8 @@ void ContentLibraryWidget::setHasMaterialLibrary(bool b) emit hasMaterialLibraryChanged(); m_materialsModel->updateIsEmpty(); + m_userModel->updateIsEmptyMaterials(); + m_userModel->updateIsEmpty3D(); } bool ContentLibraryWidget::hasActive3DScene() const @@ -806,10 +810,9 @@ QString ContentLibraryWidget::findTextureBundlePath() return texBundleDir.path(); } -void ContentLibraryWidget::startDragEffect(QmlDesigner::ContentLibraryEffect *eff, - const QPointF &mousePos) +void ContentLibraryWidget::startDragItem(QmlDesigner::ContentLibraryItem *item, const QPointF &mousePos) { - m_effectToDrag = eff; + m_itemToDrag = item; m_dragStartPoint = mousePos.toPoint(); setIsDragging(true); } diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.h index 6f4cc300c91..2e2850b1214 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.h +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.h @@ -23,8 +23,8 @@ class StudioQuickWidget; namespace QmlDesigner { class ContentLibraryBundleImporter; -class ContentLibraryEffect; class ContentLibraryEffectsModel; +class ContentLibraryItem; class ContentLibraryMaterial; class ContentLibraryMaterialsModel; class ContentLibraryTexture; @@ -79,7 +79,7 @@ public: QPointer effectsModel() const; QPointer userModel() const; - Q_INVOKABLE void startDragEffect(QmlDesigner::ContentLibraryEffect *eff, const QPointF &mousePos); + Q_INVOKABLE void startDragItem(QmlDesigner::ContentLibraryItem *item, const QPointF &mousePos); Q_INVOKABLE void startDragMaterial(QmlDesigner::ContentLibraryMaterial *mat, const QPointF &mousePos); Q_INVOKABLE void startDragTexture(QmlDesigner::ContentLibraryTexture *tex, const QPointF &mousePos); Q_INVOKABLE void addImage(QmlDesigner::ContentLibraryTexture *tex); @@ -94,7 +94,7 @@ public: ContentLibraryBundleImporter *importer() const; signals: - void bundleEffectDragStarted(QmlDesigner::ContentLibraryEffect *bundleEff); + void bundleItemDragStarted(QmlDesigner::ContentLibraryItem *item); void bundleMaterialDragStarted(QmlDesigner::ContentLibraryMaterial *bundleMat); void bundleTextureDragStarted(QmlDesigner::ContentLibraryTexture *bundleTex); void addTextureRequested(const QString texPath, QmlDesigner::AddTextureMode mode); @@ -134,12 +134,11 @@ private: QPointer m_userModel; ContentLibraryBundleImporter *m_importer = nullptr; - QShortcut *m_qmlSourceUpdateShortcut = nullptr; QString m_filterText; - ContentLibraryEffect *m_effectToDrag = nullptr; + ContentLibraryItem *m_itemToDrag = nullptr; ContentLibraryMaterial *m_materialToDrag = nullptr; ContentLibraryTexture *m_textureToDrag = nullptr; QPoint m_dragStartPoint; diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp index ff23e2b3d41..7b9966d8bae 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp +++ b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp @@ -507,7 +507,7 @@ void Edit3DView::nodeAtPosReady(const ModelNode &modelNode, const QVector3D &pos } else if (m_nodeAtPosReqType == NodeAtPosReqType::BundleMaterialDrop) { emitCustomNotification("drop_bundle_material", {modelNode}); // To ContentLibraryView } else if (m_nodeAtPosReqType == NodeAtPosReqType::BundleEffectDrop) { - emitCustomNotification("drop_bundle_effect", {modelNode}, {pos3d}); // To ContentLibraryView + emitCustomNotification("drop_bundle_item", {modelNode}, {pos3d}); // To ContentLibraryView } else if (m_nodeAtPosReqType == NodeAtPosReqType::TextureDrop) { emitCustomNotification("apply_texture_to_model3D", {modelNode, m_droppedModelNode}); } else if (m_nodeAtPosReqType == NodeAtPosReqType::AssetDrop) { diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp index 19a46e5b890..6aba55f6a86 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp +++ b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp @@ -685,7 +685,7 @@ void Edit3DWidget::dragEnterEvent(QDragEnterEvent *dragEnterEvent) } else if (actionManager.externalDragHasSupportedAssets(dragEnterEvent->mimeData()) || dragEnterEvent->mimeData()->hasFormat(Constants::MIME_TYPE_MATERIAL) || dragEnterEvent->mimeData()->hasFormat(Constants::MIME_TYPE_BUNDLE_MATERIAL) - || dragEnterEvent->mimeData()->hasFormat(Constants::MIME_TYPE_BUNDLE_EFFECT) + || dragEnterEvent->mimeData()->hasFormat(Constants::MIME_TYPE_BUNDLE_ITEM) || dragEnterEvent->mimeData()->hasFormat(Constants::MIME_TYPE_TEXTURE)) { if (Utils3D::active3DSceneNode(m_view).isValid()) dragEnterEvent->acceptProposedAction(); @@ -730,8 +730,8 @@ void Edit3DWidget::dropEvent(QDropEvent *dropEvent) return; } - // handle dropping bundle effects - if (dropEvent->mimeData()->hasFormat(Constants::MIME_TYPE_BUNDLE_EFFECT)) { + // handle dropping bundle items + if (dropEvent->mimeData()->hasFormat(Constants::MIME_TYPE_BUNDLE_ITEM)) { m_view->dropBundleEffect(pos); m_view->model()->endDrag(); return; diff --git a/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp b/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp index c6fc35c10e4..c5fa30fc7d8 100644 --- a/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp +++ b/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp @@ -455,7 +455,7 @@ QStringList NavigatorTreeModel::mimeTypes() const Constants::MIME_TYPE_MATERIAL, Constants::MIME_TYPE_BUNDLE_TEXTURE, Constants::MIME_TYPE_BUNDLE_MATERIAL, - Constants::MIME_TYPE_BUNDLE_EFFECT, + Constants::MIME_TYPE_BUNDLE_ITEM, Constants::MIME_TYPE_ASSETS}); return types; @@ -570,9 +570,9 @@ bool NavigatorTreeModel::dropMimeData(const QMimeData *mimeData, } else if (mimeData->hasFormat(Constants::MIME_TYPE_BUNDLE_MATERIAL)) { if (targetNode.isValid()) m_view->emitCustomNotification("drop_bundle_material", {targetNode}); // To ContentLibraryView - } else if (mimeData->hasFormat(Constants::MIME_TYPE_BUNDLE_EFFECT)) { + } else if (mimeData->hasFormat(Constants::MIME_TYPE_BUNDLE_ITEM)) { if (targetNode.isValid()) - m_view->emitCustomNotification("drop_bundle_effect", {targetNode}); // To ContentLibraryView + m_view->emitCustomNotification("drop_bundle_item", {targetNode}); // To ContentLibraryView } else if (mimeData->hasFormat(Constants::MIME_TYPE_ASSETS)) { const QStringList assetsPaths = QString::fromUtf8(mimeData->data(Constants::MIME_TYPE_ASSETS)).split(','); NodeAbstractProperty targetProperty; diff --git a/src/plugins/qmldesigner/qmldesignerconstants.h b/src/plugins/qmldesigner/qmldesignerconstants.h index ff85f6d25a9..f2f78a1526f 100644 --- a/src/plugins/qmldesigner/qmldesignerconstants.h +++ b/src/plugins/qmldesigner/qmldesignerconstants.h @@ -106,7 +106,7 @@ inline constexpr char MIME_TYPE_ITEM_LIBRARY_INFO[] inline constexpr char MIME_TYPE_ASSETS[] = "application/vnd.qtdesignstudio.assets"; inline constexpr char MIME_TYPE_MATERIAL[] = "application/vnd.qtdesignstudio.material"; inline constexpr char MIME_TYPE_TEXTURE[] = "application/vnd.qtdesignstudio.texture"; -inline constexpr char MIME_TYPE_BUNDLE_EFFECT[] = "application/vnd.qtdesignstudio.bundleeffect"; +inline constexpr char MIME_TYPE_BUNDLE_ITEM[] = "application/vnd.qtdesignstudio.bundleitem"; inline constexpr char MIME_TYPE_BUNDLE_MATERIAL[] = "application/vnd.qtdesignstudio.bundlematerial"; inline constexpr char MIME_TYPE_BUNDLE_TEXTURE[] = "application/vnd.qtdesignstudio.bundletexture"; inline constexpr char MIME_TYPE_ASSET_IMAGE[] = "application/vnd.qtdesignstudio.asset.image"; From 20da89dd074bfafcde9fff55e9ab971de24ec234 Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Tue, 7 May 2024 17:47:31 +0300 Subject: [PATCH 079/154] QmlDesigner: Remove ContentLibraryWidget::findTextureBundlePath() Unused method. Change-Id: I021c9e3c0180245a826f375f14dd94350a209cfd Reviewed-by: Miikka Heikkinen --- .../contentlibrary/contentlibrarywidget.cpp | 22 ------------------- .../contentlibrary/contentlibrarywidget.h | 1 - 2 files changed, 23 deletions(-) diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.cpp index 1935f02d5a1..2b79dc023e7 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.cpp @@ -788,28 +788,6 @@ void ContentLibraryWidget::setIsDragging(bool val) } } -QString ContentLibraryWidget::findTextureBundlePath() -{ - QDir texBundleDir; - - if (!qEnvironmentVariable("TEXTURE_BUNDLE_PATH").isEmpty()) - texBundleDir.setPath(qEnvironmentVariable("TEXTURE_BUNDLE_PATH")); - else if (Utils::HostOsInfo::isMacHost()) - texBundleDir.setPath(QCoreApplication::applicationDirPath() + "/../Resources/texture_bundle"); - - // search for matBundleDir from exec dir and up - if (texBundleDir.dirName() == ".") { - texBundleDir.setPath(QCoreApplication::applicationDirPath()); - while (!texBundleDir.cd("texture_bundle") && texBundleDir.cdUp()) - ; // do nothing - - if (texBundleDir.dirName() != "texture_bundle") // bundlePathDir not found - return {}; - } - - return texBundleDir.path(); -} - void ContentLibraryWidget::startDragItem(QmlDesigner::ContentLibraryItem *item, const QPointF &mousePos) { m_itemToDrag = item; diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.h index 2e2850b1214..7bcd403bb70 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.h +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.h @@ -113,7 +113,6 @@ private: void reloadQmlSource(); void updateSearch(); void setIsDragging(bool val); - QString findTextureBundlePath(); void loadTextureBundles(); QVariantMap readTextureBundleJson(); bool fetchTextureBundleJson(const QDir &bundleDir); From 11802e70d351a2dd01da4eecc407250d2ce72a4d Mon Sep 17 00:00:00 2001 From: Aleksei German Date: Tue, 7 May 2024 16:52:30 +0200 Subject: [PATCH 080/154] QmlDesigner: Fix for ColorEditors Pickers Canvas MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pick-to: qds/4.5 Task-number: QDS-12714 Change-Id: Ie8a6b8be88f4f020cbc82f66015dc7db76d14f56 Reviewed-by: Henning Gründl --- .../imports/HelperWidgets/ColorEditor.qml | 3 +++ .../imports/HelperWidgets/ColorEditorPopup.qml | 13 +++++++++++++ .../imports/StudioControls/impl/ColorPicker.qml | 5 +++++ 3 files changed, 21 insertions(+) diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ColorEditor.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ColorEditor.qml index a695d16dd9e..385d01c4df8 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ColorEditor.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ColorEditor.qml @@ -223,7 +223,10 @@ SecondColumnLayout { function open() { popupDialog.ensureLoader() + popupDialog.show(preview) + + popupDialog.loaderItem.aboutToBeShown() //need it for now } function determineActiveColorMode() { diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ColorEditorPopup.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ColorEditorPopup.qml index d3ad19ad852..2c292c5030f 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ColorEditorPopup.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ColorEditorPopup.qml @@ -12,6 +12,12 @@ import QtQuickDesignerColorPalette Column { id: root + // There seems to be an issue on Windows and MacOS with ColorPickers + // Canvases not being painted on initialization + // because ColorEditorPopup is invisible at init time, + // so we use this signal to explicitly pass visibility status + signal aboutToBeShown + property bool eyeDropperActive: ColorPaletteBackend.eyeDropperActive property bool supportGradient: false @@ -433,6 +439,13 @@ Column { hsvValueSpinBox.value = colorPicker.value hsvAlphaSpinBox.value = colorPicker.alpha } + + Connections { + target: root + onAboutToBeShown: { + colorPicker.aboutToBeShown() + } + } } Column { diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/impl/ColorPicker.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/impl/ColorPicker.qml index cd525114268..fc2a0256644 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/impl/ColorPicker.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/impl/ColorPicker.qml @@ -13,6 +13,8 @@ Column { HSLA } + signal aboutToBeShown + property int mode: ColorPicker.Mode.HSVA property color color: "#303091" @@ -206,6 +208,8 @@ Column { target: root function onHueChanged() { gradientOverlay.requestPaint() } function onModeChanged() { gradientOverlay.requestPaint() } + function onAlphaChanged() { gradientOverlay.requestPaint() } + function onAboutToBeShown() { gradientOverlay.requestPaint() } } onPaint: { @@ -249,6 +253,7 @@ Column { function onColorInvalidated() { pickerCross.requestPaint() } function onColorChanged() { pickerCross.requestPaint() } function onModeChanged() { pickerCross.requestPaint() } + function onAboutToBeShown() { pickerCross.requestPaint() } } onPaint: { From a7fd0a78d1c957ac7e9abb4a8c1fe16fead4426b Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Tue, 7 May 2024 21:57:23 +0200 Subject: [PATCH 081/154] QmlDesignerProjectManager: prevent crash at destruction found in https://the-qt-company-00.sentry.io/issues/5315357110 Pick-to: qds/4.5 Change-Id: Ie8d7358c246799c5883e52fbdadb6f3a5e2a4359 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Marco Bubke --- .../qmldesigner/qmldesignerprojectmanager.cpp | 68 ++++++++++--------- .../qmldesigner/qmldesignerprojectmanager.h | 9 ++- 2 files changed, 40 insertions(+), 37 deletions(-) diff --git a/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp b/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp index 7954b6901bb..30a827b80bb 100644 --- a/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp +++ b/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp @@ -210,10 +210,10 @@ std::unique_ptr createProjectStorageData(::ProjectExplorer:: } } // namespace -class QmlDesignerProjectManager::QmlDesignerProjectManagerDirectoryInfo +class QmlDesignerProjectManager::QmlDesignerProjectManagerProjectData { public: - QmlDesignerProjectManagerDirectoryInfo(ImageCacheStorage &storage, + QmlDesignerProjectManagerProjectData(ImageCacheStorage &storage, ::ProjectExplorer::Project *project, ExternalDependenciesInterface &externalDependencies) : collector{connectionManager, @@ -238,30 +238,34 @@ QmlDesignerProjectManager::QmlDesignerProjectManager(ExternalDependenciesInterfa , m_externalDependencies{externalDependencies} { auto editorManager = ::Core::EditorManager::instance(); - QObject::connect(editorManager, &::Core::EditorManager::editorOpened, [&](auto *editor) { + QObject::connect(editorManager, &::Core::EditorManager::editorOpened, &dummy, [&](auto *editor) { editorOpened(editor); }); - QObject::connect(editorManager, &::Core::EditorManager::currentEditorChanged, [&](auto *editor) { - currentEditorChanged(editor); - }); - QObject::connect(editorManager, &::Core::EditorManager::editorsClosed, [&](const auto &editors) { - editorsClosed(editors); - }); + QObject::connect(editorManager, + &::Core::EditorManager::currentEditorChanged, + &dummy, + [&](auto *editor) { currentEditorChanged(editor); }); + QObject::connect(editorManager, + &::Core::EditorManager::editorsClosed, + &dummy, + [&](const auto &editors) { editorsClosed(editors); }); auto sessionManager = ::ProjectExplorer::ProjectManager::instance(); QObject::connect(sessionManager, &::ProjectExplorer::ProjectManager::projectAdded, + &dummy, [&](auto *project) { projectAdded(project); }); QObject::connect(sessionManager, &::ProjectExplorer::ProjectManager::aboutToRemoveProject, + &dummy, [&](auto *project) { aboutToRemoveProject(project); }); QObject::connect(sessionManager, &::ProjectExplorer::ProjectManager::projectRemoved, + &dummy, [&](auto *project) { projectRemoved(project); }); - QObject::connect(&m_previewImageCacheData->timer, - &QTimer::timeout, - this, - &QmlDesignerProjectManager::generatePreview); + QObject::connect(&m_previewImageCacheData->timer, &QTimer::timeout, &dummy, [&]() { + generatePreview(); + }); } QmlDesignerProjectManager::~QmlDesignerProjectManager() = default; @@ -297,8 +301,8 @@ namespace { ProjectStorageDependencies QmlDesignerProjectManager::projectStorageDependencies() { if constexpr (useProjectStorage()) { - return {m_directoryInfo->projectStorageData->storage, - m_directoryInfo->projectStorageData->pathCache}; + return {m_projectData->projectStorageData->storage, + m_projectData->projectStorageData->pathCache}; } else { return {*dummyProjectStorage(), *dummyPathCache()}; } @@ -499,10 +503,10 @@ QString qtCreatorItemLibraryPath() void QmlDesignerProjectManager::projectAdded(::ProjectExplorer::Project *project) { - m_directoryInfo = std::make_unique(m_previewImageCacheData->storage, + m_projectData = std::make_unique(m_previewImageCacheData->storage, project, m_externalDependencies); - m_directoryInfo->activeTarget = project->activeTarget(); + m_projectData->activeTarget = project->activeTarget(); QObject::connect(project, &::ProjectExplorer::Project::fileListChanged, [&]() { fileListChanged(); @@ -522,9 +526,9 @@ void QmlDesignerProjectManager::projectAdded(::ProjectExplorer::Project *project void QmlDesignerProjectManager::aboutToRemoveProject(::ProjectExplorer::Project *) { - if (m_directoryInfo) { - m_previewImageCacheData->collector.setTarget(m_directoryInfo->activeTarget); - m_directoryInfo.reset(); + if (m_projectData) { + m_previewImageCacheData->collector.setTarget(m_projectData->activeTarget); + m_projectData.reset(); } } @@ -532,14 +536,14 @@ void QmlDesignerProjectManager::projectRemoved(::ProjectExplorer::Project *) {} void QmlDesignerProjectManager::generatePreview() { - if (!m_directoryInfo || !m_directoryInfo->activeTarget) + if (!m_projectData || !m_projectData->activeTarget) return; ::QmlProjectManager::QmlBuildSystem *qmlBuildSystem = getQmlBuildSystem( - m_directoryInfo->activeTarget); + m_projectData->activeTarget); if (qmlBuildSystem) { - m_previewImageCacheData->collector.setTarget(m_directoryInfo->activeTarget); + m_previewImageCacheData->collector.setTarget(m_projectData->activeTarget); m_previewImageCacheData->factory.generate(qmlBuildSystem->mainFilePath().toString().toUtf8()); } } @@ -567,12 +571,12 @@ QmlDesignerProjectManager::ImageCacheData *QmlDesignerProjectManager::imageCache m_imageCacheData->nodeInstanceCollector.setTarget(project->activeTarget()); QObject::connect(project, &ProjectExplorer::Project::activeTargetChanged, - this, + &dummy, setTargetInImageCache); } QObject::connect(ProjectExplorer::ProjectManager::instance(), &ProjectExplorer::ProjectManager::startupProjectChanged, - this, + &dummy, [=](ProjectExplorer::Project *project) { setTargetInImageCache(activeTarget(project)); }); @@ -587,12 +591,12 @@ void QmlDesignerProjectManager::fileListChanged() void QmlDesignerProjectManager::activeTargetChanged(ProjectExplorer::Target *target) { - if (!m_directoryInfo || !m_directoryInfo->projectStorageData) + if (!m_projectData || !m_projectData->projectStorageData) return; - QObject::disconnect(m_directoryInfo->activeTarget, nullptr, nullptr, nullptr); + QObject::disconnect(m_projectData->activeTarget, nullptr, nullptr, nullptr); - m_directoryInfo->activeTarget = target; + m_projectData->activeTarget = target; if (target) { QObject::connect(target, &::ProjectExplorer::Target::kitChanged, [&]() { kitChanged(); }); @@ -625,17 +629,17 @@ void QmlDesignerProjectManager::projectChanged() void QmlDesignerProjectManager::update() { - if (!m_directoryInfo || !m_directoryInfo->projectStorageData) + if (!m_projectData || !m_projectData->projectStorageData) return; if constexpr (isUsingQmlDesignerLite()) { - m_directoryInfo->projectStorageData->updater.update(directoriesForLiteDesigner(), + m_projectData->projectStorageData->updater.update(directoriesForLiteDesigner(), qmlTypesForLiteDesigner(), propertyEditorResourcesPath(), {qtCreatorItemLibraryPath()}); } else { - m_directoryInfo->projectStorageData->updater.update(directories(m_directoryInfo->activeTarget), - qmlTypes(m_directoryInfo->activeTarget), + m_projectData->projectStorageData->updater.update(directories(m_projectData->activeTarget), + qmlTypes(m_projectData->activeTarget), propertyEditorResourcesPath(), {qtCreatorItemLibraryPath()}); } diff --git a/src/plugins/qmldesigner/qmldesignerprojectmanager.h b/src/plugins/qmldesigner/qmldesignerprojectmanager.h index 3024d041b5f..1e5cec2e3e3 100644 --- a/src/plugins/qmldesigner/qmldesignerprojectmanager.h +++ b/src/plugins/qmldesigner/qmldesignerprojectmanager.h @@ -28,11 +28,9 @@ namespace QmlDesigner { class ExternalDependenciesInterface; -class QmlDesignerProjectManager : public QObject +class QmlDesignerProjectManager { - Q_OBJECT - - class QmlDesignerProjectManagerDirectoryInfo; + class QmlDesignerProjectManagerProjectData; class PreviewImageCacheData; class ImageCacheData; @@ -68,7 +66,8 @@ private: std::once_flag imageCacheFlag; std::unique_ptr m_imageCacheData; std::unique_ptr m_previewImageCacheData; - std::unique_ptr m_directoryInfo; + std::unique_ptr m_projectData; ExternalDependenciesInterface &m_externalDependencies; + QObject dummy; }; } // namespace QmlDesigner From dd20f54207c886538963d2ce627f56861969226d Mon Sep 17 00:00:00 2001 From: Burak Hancerli Date: Mon, 29 Apr 2024 13:19:39 +0200 Subject: [PATCH 082/154] QmlProjectManager: Add initial support for multiple qmlproject files This patch adds read-only support for sub MCU projects. Task-number: QDS-12575 Change-Id: I0dc3b6ff37731f30875c03a3896bbabc4867c4fc Reviewed-by: Marco Bubke --- src/plugins/qmlprojectmanager/.clang-format | 50 ++++++++++++++ .../projectitem/qmlprojectitem.cpp | 7 +- .../buildsystem/projectitem/qmlprojectitem.h | 2 +- .../buildsystem/qmlbuildsystem.cpp | 69 +++++++++++++++---- .../buildsystem/qmlbuildsystem.h | 9 ++- 5 files changed, 118 insertions(+), 19 deletions(-) create mode 100644 src/plugins/qmlprojectmanager/.clang-format diff --git a/src/plugins/qmlprojectmanager/.clang-format b/src/plugins/qmlprojectmanager/.clang-format new file mode 100644 index 00000000000..366f82f76f2 --- /dev/null +++ b/src/plugins/qmlprojectmanager/.clang-format @@ -0,0 +1,50 @@ +Language: Cpp +AccessModifierOffset: -4 +AlignEscapedNewlines: DontAlign +AllowShortFunctionsOnASingleLine: Inline +AlwaysBreakTemplateDeclarations: true # use with clang 19 +BinPackArguments: false +BinPackParameters: false +BraceWrapping: + AfterClass: true + AfterFunction: true + AfterStruct: true + SplitEmptyFunction: false + SplitEmptyRecord: false + SplitEmptyNamespace: false +BreakBeforeBinaryOperators: All +BreakBeforeBraces: Custom +BreakConstructorInitializers: BeforeComma +BreakInheritanceList: AfterComma +# BreakTemplateDeclarations: Yes # use with clang 19 +ColumnLimit: 100 +IncludeCategories: + - Regex: 'Q.*' + Priority: 8 + CaseSensitive: true +IndentPPDirectives: AfterHash +IndentWidth: 4 +KeepEmptyLinesAtTheStartOfBlocks: false +# Do not add QT_BEGIN_NAMESPACE/QT_END_NAMESPACE as this will indent lines in between. +ObjCBlockIndentWidth: 4 +PPIndentWidth: 2 +PackConstructorInitializers: Never +PenaltyBreakAssignment: 500 +PenaltyBreakBeforeFirstCallParameter: 150 +PenaltyBreakComment: 500 +PenaltyBreakFirstLessLess: 400 +PenaltyBreakString: 600 +PenaltyExcessCharacter: 7 +PenaltyReturnTypeOnItsOwnLine: 300 +QualifierAlignment: Custom +QualifierOrder: ['friend', 'inline', 'static', 'constexpr', 'const', 'type'] +ReferenceAlignment: Right +ReflowComments: false +SeparateDefinitionBlocks: Always +SortUsingDeclarations: Lexicographic +SpaceAfterCStyleCast: true +SpaceAfterTemplateKeyword: false +SpaceBeforeParens: ControlStatementsExceptControlMacros +SpacesInContainerLiterals: false +StatementAttributeLikeMacros: [emit] +TabWidth: 4 diff --git a/src/plugins/qmlprojectmanager/buildsystem/projectitem/qmlprojectitem.cpp b/src/plugins/qmlprojectmanager/buildsystem/projectitem/qmlprojectitem.cpp index eb6e6de177a..5229d486ce3 100644 --- a/src/plugins/qmlprojectmanager/buildsystem/projectitem/qmlprojectitem.cpp +++ b/src/plugins/qmlprojectmanager/buildsystem/projectitem/qmlprojectitem.cpp @@ -79,7 +79,7 @@ void QmlProjectItem::setupFileFilters() connect(fileFilterItem.get(), &FileFilterItem::filesChanged, this, - &QmlProjectItem::qmlFilesChanged); + &QmlProjectItem::filesChanged); #endif m_content.push_back(std::move(fileFilterItem)); }; @@ -105,10 +105,7 @@ void QmlProjectItem::setupFileFilters() fileFilterItem->setDefaultDirectory(m_projectFile.parentDir().toString()); fileFilterItem->setDirectory(groupDir.toString()); #ifndef TESTS_ENABLED_QMLPROJECTITEM - connect(fileFilterItem.get(), - &FileFilterItem::filesChanged, - this, - &QmlProjectItem::qmlFilesChanged); + connect(fileFilterItem.get(), &FileFilterItem::filesChanged, this, &QmlProjectItem::filesChanged); #endif m_content.push_back(std::move(fileFilterItem)); }; diff --git a/src/plugins/qmlprojectmanager/buildsystem/projectitem/qmlprojectitem.h b/src/plugins/qmlprojectmanager/buildsystem/projectitem/qmlprojectitem.h index 7d57ad2e60d..5d0b520f144 100644 --- a/src/plugins/qmlprojectmanager/buildsystem/projectitem/qmlprojectitem.h +++ b/src/plugins/qmlprojectmanager/buildsystem/projectitem/qmlprojectitem.h @@ -92,7 +92,7 @@ public: void setEnableCMakeGeneration(bool enable); signals: - void qmlFilesChanged(const QSet &, const QSet &); + void filesChanged(const QSet &, const QSet &); private: typedef QSharedPointer ShrdPtrQPI; diff --git a/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.cpp b/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.cpp index 00e501d6f88..35662a3d923 100644 --- a/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.cpp +++ b/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.cpp @@ -37,6 +37,8 @@ #include "projectnode/qmlprojectnodes.h" #include "utils/algorithm.h" +#include "utils/filepath.h" +#include "utils/filesystemwatcher.h" #include "utils/qtcassert.h" #include "texteditor/textdocument.h" @@ -220,17 +222,56 @@ void QmlBuildSystem::refresh(RefreshOptions options) void QmlBuildSystem::initProjectItem() { m_projectItem.reset(new QmlProjectItem{projectFilePath()}); - connect(m_projectItem.get(), - &QmlProjectItem::qmlFilesChanged, - this, - &QmlBuildSystem::refreshFiles); - connect(m_projectItem.get(), - &QmlProjectItem::qmlFilesChanged, + connect(m_projectItem.data(), &QmlProjectItem::filesChanged, this, &QmlBuildSystem::refreshFiles); + connect(m_projectItem.data(), + &QmlProjectItem::filesChanged, m_cmakeGen, &GenerateCmake::CMakeGenerator::update); m_cmakeGen->setEnabled(m_projectItem->enableCMakeGeneration()); + + initMcuProjectItems(); +} + +void QmlBuildSystem::initMcuProjectItems() +{ + m_mcuProjectItems.clear(); + m_mcuProjectFilesWatcher.clear(); + + Utils::FilePath projectDir = projectFilePath().parentDir(); + // traverse the project dir and find all other mcu projects (.qmlproject files) in the project tree + // and add them to the m_mcuProjectItems vector + QDirIterator it(projectDir.toString(), QDir::Files, QDirIterator::Subdirectories); + while (it.hasNext()) { + it.next(); + if (it.fileInfo().suffix() == "qmlproject" && it.filePath() != projectFilePath().toString()) { + auto qmlProjectItem = QSharedPointer( + new QmlProjectItem{Utils::FilePath::fromString(it.filePath())}); + + m_mcuProjectItems.append(qmlProjectItem); + connect(qmlProjectItem.data(), + &QmlProjectItem::filesChanged, + this, + &QmlBuildSystem::refreshFiles); + connect(qmlProjectItem.data(), + &QmlProjectItem::filesChanged, + m_cmakeGen, + &GenerateCmake::CMakeGenerator::update); + + m_mcuProjectFilesWatcher.addFile(it.filePath(), + Utils::FileSystemWatcher::WatchModifiedDate); + + connect(&m_mcuProjectFilesWatcher, + &Utils::FileSystemWatcher::fileChanged, + this, + [this](const QString &file) { + Q_UNUSED(file) + initMcuProjectItems(); + refresh(RefreshOptions::Files); + }); + } + } } void QmlBuildSystem::parseProjectFiles() @@ -239,7 +280,6 @@ void QmlBuildSystem::parseProjectFiles() modelManager->updateSourceFiles(m_projectItem->files(), true); } - const QString mainFileName = m_projectItem->mainFile(); if (!mainFileName.isEmpty()) { Utils::FilePath mainFilePath = canonicalProjectDir().resolvePath(mainFileName); @@ -265,6 +305,16 @@ void QmlBuildSystem::generateProjectTree() : FileNode::fileTypeForFileName(file); newRoot->addNestedNode(std::make_unique(file, fileType)); } + + for (const auto &mcuProjectItem : m_mcuProjectItems) { + for (const auto &file : mcuProjectItem->files()) { + // newRoot->addNestedNode(std::make_unique(file, FileType::Project)); + const FileType fileType = (file == projectFilePath()) + ? FileType::Project + : FileNode::fileTypeForFileName(file); + newRoot->addNestedNode(std::make_unique(file, fileType)); + } + } newRoot->addNestedNode(std::make_unique(projectFilePath(), FileType::Project)); setRootProjectNode(std::move(newRoot)); @@ -692,11 +742,6 @@ QStringList QmlBuildSystem::absoluteImportPaths() }); } -Utils::FilePaths QmlBuildSystem::files() const -{ - return m_projectItem->files(); -} - QString QmlBuildSystem::versionQt() const { return m_projectItem->versionQt(); diff --git a/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.h b/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.h index e3f9b99adb0..998b5c8c937 100644 --- a/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.h +++ b/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.h @@ -7,9 +7,12 @@ #include "../qmlprojectmanager_global.h" #include +#include #include "qmlprojectmanager/cmakegen/cmakegenerator.h" +#include "utils/filesystemwatcher.h" + namespace QmlProjectManager { class QmlProject; @@ -91,7 +94,6 @@ public: QStringList shaderToolArgs() const; QStringList shaderToolFiles() const; - Utils::FilePaths files() const; QString versionQt() const; QString versionQtQuick() const; @@ -117,10 +119,15 @@ private: const Utils::FilePath &mainFilePath, const QString &oldFile); + // this is the main project item QSharedPointer m_projectItem; + // these are the mcu project items which can be found in the project tree + QVector> m_mcuProjectItems; + Utils::FileSystemWatcher m_mcuProjectFilesWatcher; bool m_blockFilesUpdate = false; void initProjectItem(); + void initMcuProjectItems(); void parseProjectFiles(); void generateProjectTree(); From 542520e31c600274fb7c329983ba01c5774d4c20 Mon Sep 17 00:00:00 2001 From: Burak Hancerli Date: Wed, 8 May 2024 18:18:03 +0200 Subject: [PATCH 083/154] QmlProjectManager: Fix include paths Change-Id: I1f5b0089e8d31d96682401a87bfd21eb3f4880f5 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Reviewed-by: hjk --- .../buildsystem/qmlbuildsystem.cpp | 18 +++++++++--------- .../buildsystem/qmlbuildsystem.h | 7 +++---- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.cpp b/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.cpp index 35662a3d923..4e627c3368e 100644 --- a/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.cpp +++ b/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.cpp @@ -32,16 +32,16 @@ #include #include -#include "projectexplorer/projectmanager.h" -#include "projectitem/qmlprojectitem.h" -#include "projectnode/qmlprojectnodes.h" +#include +#include +#include -#include "utils/algorithm.h" -#include "utils/filepath.h" -#include "utils/filesystemwatcher.h" -#include "utils/qtcassert.h" +#include +#include +#include +#include -#include "texteditor/textdocument.h" +#include #include @@ -242,7 +242,7 @@ void QmlBuildSystem::initMcuProjectItems() Utils::FilePath projectDir = projectFilePath().parentDir(); // traverse the project dir and find all other mcu projects (.qmlproject files) in the project tree // and add them to the m_mcuProjectItems vector - QDirIterator it(projectDir.toString(), QDir::Files, QDirIterator::Subdirectories); + QDirIterator it(projectDir.toFSPathString(), QDir::Files, QDirIterator::Subdirectories); while (it.hasNext()) { it.next(); if (it.fileInfo().suffix() == "qmlproject" && it.filePath() != projectFilePath().toString()) { diff --git a/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.h b/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.h index 998b5c8c937..d7e9c071245 100644 --- a/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.h +++ b/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.h @@ -6,13 +6,12 @@ #pragma once #include "../qmlprojectmanager_global.h" + #include -#include +#include #include "qmlprojectmanager/cmakegen/cmakegenerator.h" -#include "utils/filesystemwatcher.h" - namespace QmlProjectManager { class QmlProject; @@ -122,7 +121,7 @@ private: // this is the main project item QSharedPointer m_projectItem; // these are the mcu project items which can be found in the project tree - QVector> m_mcuProjectItems; + QList> m_mcuProjectItems; Utils::FileSystemWatcher m_mcuProjectFilesWatcher; bool m_blockFilesUpdate = false; From 18febc9d76de021e4828a9072577da32faf20f75 Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Fri, 3 May 2024 18:03:11 +0300 Subject: [PATCH 084/154] QmlDesigner: Support more json structures in Model Editor * A visitor is added to detect the property order of the nested json models. * A pure json object is defined as a json which does not contain any array or object as its member. * All of the json lists which has pure models, will be imported. * A pure object which is a child of another object, will be imported. Fixes: QDS-12546 Change-Id: Ib44e1567e3dde0fc5cb433b4f1dc20358e6a3949 Reviewed-by: Mahmoud Badri --- src/plugins/qmldesigner/CMakeLists.txt | 1 + .../collectioneditor/collectiondetails.cpp | 111 +------- .../collectioneditor/collectiondetails.h | 4 +- .../collectioneditorutils.cpp | 15 +- .../collectioneditor/collectionjsonparser.cpp | 257 ++++++++++++++++++ .../collectioneditor/collectionjsonparser.h | 58 ++++ .../collectioneditor/collectionwidget.cpp | 33 ++- 7 files changed, 368 insertions(+), 111 deletions(-) create mode 100644 src/plugins/qmldesigner/components/collectioneditor/collectionjsonparser.cpp create mode 100644 src/plugins/qmldesigner/components/collectioneditor/collectionjsonparser.h diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index d41518e0182..5d34810d438 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -856,6 +856,7 @@ extend_qtc_plugin(QmlDesigner collectiondetailssortfiltermodel.cpp collectiondetailssortfiltermodel.h collectioneditorconstants.h collectioneditorutils.cpp collectioneditorutils.h + collectionjsonparser.cpp collectionjsonparser.h collectionlistmodel.cpp collectionlistmodel.h collectionview.cpp collectionview.h collectionwidget.cpp collectionwidget.h diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp index 7e61f5e6100..eedfbd3178a 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp @@ -5,12 +5,11 @@ #include "collectiondatatypemodel.h" #include "collectioneditorutils.h" +#include "collectionjsonparser.h" -#include -#include -#include -#include #include +#include +#include #include #include @@ -279,47 +278,6 @@ QStringList csvReadLine(const QString &line) return result; } -class PropertyOrderFinder : public QmlJS::AST::Visitor -{ -public: - static QStringList parse(const QString &jsonContent) - { - PropertyOrderFinder finder; - QmlJS::Document::MutablePtr jsonDoc = QmlJS::Document::create(Utils::FilePath::fromString( - ""), - QmlJS::Dialect::Json); - - jsonDoc->setSource(jsonContent); - jsonDoc->parseJavaScript(); - - if (!jsonDoc->isParsedCorrectly()) - return {}; - - jsonDoc->ast()->accept(&finder); - return finder.m_orderedList; - } - -protected: - bool visit(QmlJS::AST::PatternProperty *patternProperty) override - { - const QString propertyName = patternProperty->name->asString(); - if (!m_propertySet.contains(propertyName)) { - m_propertySet.insert(propertyName); - m_orderedList.append(propertyName); - } - return true; - } - - void throwRecursionDepthError() override - { - qWarning() << Q_FUNC_INFO << __LINE__ << "Recursion depth error"; - }; - -private: - QSet m_propertySet; - QStringList m_orderedList; -}; - QString CollectionParseError::errorString() const { switch (errorNo) { @@ -757,63 +715,24 @@ CollectionDetails CollectionDetails::fromImportedCsv(const QByteArray &document, return fromImportedJson(importedArray, headers); } -CollectionDetails CollectionDetails::fromImportedJson(const QByteArray &json, QJsonParseError *error) +QList CollectionDetails::fromImportedJson(const QByteArray &jsonContent, + QJsonParseError *error) { - QJsonArray importedCollection; - auto refineJsonArray = [](const QJsonArray &array) -> QJsonArray { - QJsonArray resultArray; - for (const QJsonValue &collectionData : array) { - if (collectionData.isObject()) { - QJsonObject rowObject = collectionData.toObject(); - const QStringList rowKeys = rowObject.keys(); - for (const QString &key : rowKeys) { - const QJsonValue cellValue = rowObject.value(key); - if (cellValue.isArray()) - rowObject.remove(key); - } - resultArray.push_back(rowObject); - } - } - return resultArray; - }; - QJsonParseError parseError; - QJsonDocument document = QJsonDocument::fromJson(json, &parseError); + + QList collectionObjects = JsonCollectionParser::parseCollectionObjects(jsonContent, + error); + if (error) *error = parseError; if (parseError.error != QJsonParseError::NoError) - return CollectionDetails{}; - - if (document.isArray()) { - importedCollection = refineJsonArray(document.array()); - } else if (document.isObject()) { - QJsonObject documentObject = document.object(); - const QStringList mainKeys = documentObject.keys(); - - bool arrayFound = false; - for (const QString &key : mainKeys) { - const QJsonValue value = documentObject.value(key); - if (value.isArray()) { - arrayFound = true; - importedCollection = refineJsonArray(value.toArray()); - break; - } - } - - if (!arrayFound) { - QJsonObject singleObject; - for (const QString &key : mainKeys) { - const QJsonValue value = documentObject.value(key); - - if (!value.isObject()) - singleObject.insert(key, value); - } - importedCollection.push_back(singleObject); - } - } - - return fromImportedJson(importedCollection, PropertyOrderFinder::parse(QLatin1String(json))); + return {}; + return Utils::transform(collectionObjects, [](const CollectionObject &object) { + CollectionDetails result = fromImportedJson(object.array, object.propertyOrder); + result.d->reference.name = object.name; + return result; + }); } CollectionDetails CollectionDetails::fromLocalJson(const QJsonDocument &document, diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.h b/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.h index 7243c585c6b..984feabc0a3 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.h +++ b/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.h @@ -127,8 +127,8 @@ public: static CollectionDetails fromImportedCsv(const QByteArray &document, const bool &firstRowIsHeader = true); - static CollectionDetails fromImportedJson(const QByteArray &json, - QJsonParseError *error = nullptr); + static QList fromImportedJson(const QByteArray &jsonContent, + QJsonParseError *error = nullptr); static CollectionDetails fromLocalJson(const QJsonDocument &document, const QString &collectionName, CollectionParseError *error = nullptr); diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.cpp index 29b833cc2ce..40350802d26 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.cpp @@ -315,18 +315,25 @@ QJsonObject defaultColorCollection() FileReader fileReader; if (!fileReader.fetch(templatePath)) { - qWarning() << Q_FUNC_INFO << __LINE__ << "Can't read the content of the file" << templatePath; + qWarning() << __FUNCTION__ << "Can't read the content of the file" << templatePath; return {}; } QJsonParseError parseError; - const CollectionDetails collection = CollectionDetails::fromImportedJson(fileReader.data(), - &parseError); + const QList collections = CollectionDetails::fromImportedJson(fileReader.data(), + &parseError); + if (parseError.error != QJsonParseError::NoError) { - qWarning() << Q_FUNC_INFO << __LINE__ << "Error in template file" << parseError.errorString(); + qWarning() << __FUNCTION__ << "Error in template file" << parseError.errorString(); return {}; } + if (!collections.size()) { + qWarning() << __FUNCTION__ << "Can not generate collections from template file!"; + return {}; + } + + const CollectionDetails collection = collections.first(); return collection.toLocalJson(); } diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionjsonparser.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionjsonparser.cpp new file mode 100644 index 00000000000..3756af47857 --- /dev/null +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionjsonparser.cpp @@ -0,0 +1,257 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "collectionjsonparser.h" + +#include +#include +#include + +#include +#include +#include + +namespace QmlDesigner { + +/** + * @brief A json object is a plain object, if it has only primitive properties (not arrays or objects) + * @return true if @param jsonObject is a plain object + */ +inline static bool isPlainObject(const QJsonObject &jsonObj) +{ + return !Utils::anyOf(jsonObj, [](const QJsonValueConstRef &val) { + return val.isArray() || val.isObject(); + }); +} + +static bool isPlainObject(const QJsonValueConstRef &value) +{ + if (!value.isObject()) + return false; + return isPlainObject(value.toObject()); +} + +static QJsonArray parsePlainObject(const QJsonObject &jsonObj) +{ + QJsonObject result; + auto item = jsonObj.constBegin(); + const auto itemEnd = jsonObj.constEnd(); + while (item != itemEnd) { + QJsonValueConstRef ref = item.value(); + if (!ref.isArray() && !ref.isObject()) + result.insert(item.key(), ref); + ++item; + } + if (!result.isEmpty()) + return QJsonArray{result}; + + return {}; +} + +static QJsonArray parseArray(const QJsonArray &array, + QList &plainCollections, + JsonKeyChain &chainTracker) +{ + chainTracker.append(0); + QJsonArray plainArray; + int i = -1; + for (const QJsonValueConstRef &item : array) { + chainTracker.last() = ++i; + if (isPlainObject(item)) { + const QJsonObject plainObject = item.toObject(); + if (plainObject.count()) + plainArray.append(plainObject); + } else if (item.isArray()) { + parseArray(item.toArray(), plainCollections, chainTracker); + } + } + chainTracker.removeLast(); + return plainArray; +} + +static void parseObject(const QJsonObject &jsonObj, + QList &plainCollections, + JsonKeyChain &chainTracker) +{ + chainTracker.append(QString{}); + auto item = jsonObj.constBegin(); + const auto itemEnd = jsonObj.constEnd(); + while (item != itemEnd) { + chainTracker.last() = item.key(); + QJsonValueConstRef ref = item.value(); + QJsonArray parsedArray; + if (ref.isArray()) { + parsedArray = parseArray(ref.toArray(), plainCollections, chainTracker); + } else if (ref.isObject()) { + if (isPlainObject(ref)) + parsedArray = parsePlainObject(ref.toObject()); + else + parseObject(ref.toObject(), plainCollections, chainTracker); + } + if (!parsedArray.isEmpty()) + plainCollections.append({item.key(), parsedArray, chainTracker}); + ++item; + } + chainTracker.removeLast(); +} + +static QList parseDocument(const QJsonDocument &document, + const QString &defaultName = "Model") +{ + QList plainCollections; + JsonKeyChain chainTracker; + if (document.isObject()) { + const QJsonObject documentObject = document.object(); + if (isPlainObject(documentObject)) { + QJsonArray parsedArray = parsePlainObject(documentObject); + if (!parsedArray.isEmpty()) + plainCollections.append({defaultName, parsedArray}); + } else { + parseObject(document.object(), plainCollections, chainTracker); + } + } else { + QJsonArray parsedArray = parseArray(document.array(), plainCollections, chainTracker); + if (!parsedArray.isEmpty()) + plainCollections.append({defaultName, parsedArray, {0}}); + } + return plainCollections; +} + +QList JsonCollectionParser::parseCollectionObjects(const QByteArray &json, + QJsonParseError *error) +{ + QJsonParseError parseError; + QJsonDocument document = QJsonDocument::fromJson(json, &parseError); + if (error) + *error = parseError; + + if (parseError.error != QJsonParseError::NoError) + return {}; + + QList allCollections = parseDocument(document); + QList keyChains = Utils::transform(allCollections, [](const CollectionObject &obj) { + return obj.keyChain; + }); + + JsonCollectionParser jsonVisitor(QString::fromLatin1(json), keyChains); + + for (CollectionObject &collection : allCollections) + collection.propertyOrder = jsonVisitor.collectionPaths.value(collection.keyChain); + + return allCollections; +} + +JsonCollectionParser::JsonCollectionParser(const QString &jsonContent, + const QList &keyChains) +{ + for (const JsonKeyChain &chain : keyChains) + collectionPaths.insert(chain, {}); + + QmlJS::Document::MutablePtr newDoc = QmlJS::Document::create(Utils::FilePath::fromString( + ""), + QmlJS::Dialect::Json); + + newDoc->setSource(jsonContent); + newDoc->parseExpression(); + + if (!newDoc->isParsedCorrectly()) + return; + + newDoc->ast()->accept(this); +} + +bool JsonCollectionParser::visit([[maybe_unused]] QmlJS::AST::ObjectPattern *objectPattern) +{ + propertyOrderStack.push({}); + return true; +} + +void JsonCollectionParser::endVisit([[maybe_unused]] QmlJS::AST::ObjectPattern *objectPattern) + +{ + if (!propertyOrderStack.isEmpty()) { + QStringList objectProperties = propertyOrderStack.top(); + propertyOrderStack.pop(); + checkPropertyUpdates(keyStack, objectProperties); + } +} + +bool JsonCollectionParser::visit(QmlJS::AST::PatternProperty *patternProperty) +{ + const QString propertyName = patternProperty->name->asString(); + if (!propertyOrderStack.isEmpty()) + propertyOrderStack.top().append(propertyName); + + keyStack.push(propertyName); + return true; +} + +void JsonCollectionParser::endVisit(QmlJS::AST::PatternProperty *patternProperty) +{ + const QString propertyName = patternProperty->name->asString(); + + if (auto curIndex = std::get_if(&keyStack.top())) { + if (*curIndex == propertyName) + keyStack.pop(); + } +} + +bool JsonCollectionParser::visit([[maybe_unused]] QmlJS::AST::PatternElementList *patternElementList) +{ + keyStack.push(-1); + return true; +} + +void JsonCollectionParser::endVisit([[maybe_unused]] QmlJS::AST::PatternElementList *patternElementList) +{ + if (auto curIndex = std::get_if(&keyStack.top())) + keyStack.pop(); +} + +bool JsonCollectionParser::visit([[maybe_unused]] QmlJS::AST::PatternElement *patternElement) +{ + if (auto curIndex = std::get_if(&keyStack.top())) + *curIndex += 1; + return true; +} + +void JsonCollectionParser::checkPropertyUpdates(QStack stack, + const QStringList &objectProperties) +{ + bool shouldUpdate = collectionPaths.contains(stack); + if (!shouldUpdate && !stack.isEmpty()) { + if (auto lastIndex = std::get_if(&stack.top())) { + stack.pop(); + shouldUpdate = collectionPaths.contains(stack); + } + } + if (!shouldUpdate) + return; + + QStringList propertyList = collectionPaths.value(stack); + QSet allKeys; + for (const QString &val : std::as_const(propertyList)) + allKeys.insert(val); + + std::optional prevVal; + for (const QString &val : objectProperties) { + if (!allKeys.contains(val)) { + if (prevVal.has_value()) { + const int idx = propertyList.indexOf(prevVal); + propertyList.insert(idx + 1, val); + } else { + propertyList.append(val); + } + allKeys.insert(val); + } + prevVal = val; + } + collectionPaths.insert(stack, propertyList); +} + +void JsonCollectionParser::throwRecursionDepthError() +{ + qWarning() << __FUNCTION__ << "Recursion Depth Error"; +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionjsonparser.h b/src/plugins/qmldesigner/components/collectioneditor/collectionjsonparser.h new file mode 100644 index 00000000000..16069d3f4cc --- /dev/null +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionjsonparser.h @@ -0,0 +1,58 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include + +#include +#include + +QT_BEGIN_NAMESPACE +struct QJsonParseError; +QT_END_NAMESPACE + +using JsonKey = std::variant; // Key can be either int (index) or string (property name) + +using JsonKeyChain = QList; // A chain of keys leading to a specific json value + +namespace QmlDesigner { + +struct CollectionObject +{ + QString name; + QJsonArray array = {}; + JsonKeyChain keyChain = {}; + QStringList propertyOrder = {}; +}; + +class JsonCollectionParser : public QmlJS::AST::Visitor +{ +public: + static QList parseCollectionObjects(const QByteArray &json, + QJsonParseError *error = nullptr); + +private: + JsonCollectionParser(const QString &jsonContent, const QList &keyChains); + + bool visit(QmlJS::AST::ObjectPattern *objectPattern) override; + void endVisit(QmlJS::AST::ObjectPattern *objectPattern) override; + + bool visit(QmlJS::AST::PatternProperty *patternProperty) override; + void endVisit(QmlJS::AST::PatternProperty *patternProperty) override; + + bool visit(QmlJS::AST::PatternElementList *patternElementList) override; + void endVisit(QmlJS::AST::PatternElementList *patternElementList) override; + + bool visit(QmlJS::AST::PatternElement *patternElement) override; + + void checkPropertyUpdates(QStack stack, const QStringList &objectProperties); + + void throwRecursionDepthError() override; + + QStack keyStack; + QStack propertyOrderStack; + QMap collectionPaths; // Key chains, Priorities +}; + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp index 7a90264145c..f6bc1829ee8 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp @@ -212,7 +212,6 @@ bool CollectionWidget::importFile(const QString &collectionName, FilePath fileInfo = FilePath::fromUserInput(url.isLocalFile() ? url.toLocalFile() : url.toString()); - CollectionDetails loadedCollection; QByteArray fileContent; auto loadUrlContent = [&]() -> bool { @@ -231,24 +230,40 @@ bool CollectionWidget::importFile(const QString &collectionName, return false; QJsonParseError parseError; - loadedCollection = CollectionDetails::fromImportedJson(fileContent, &parseError); + const QList loadedCollections = CollectionDetails::fromImportedJson( + fileContent, &parseError); if (parseError.error != QJsonParseError::NoError) { warn(tr("Json file Import error"), tr("Cannot parse json content\n%1").arg(parseError.errorString())); + return false; + } + if (loadedCollections.size() > 1) { + for (const CollectionDetails &loadedCollection : loadedCollections) { + m_view->addNewCollection(loadedCollection.reference().name, + loadedCollection.toLocalJson()); + } + return true; + } else if (loadedCollections.size() == 1) { + m_view->addNewCollection(collectionName, loadedCollections.first().toLocalJson()); + return true; + } else { + warn(tr("Can not add a model to the JSON file"), + tr("The imported model is empty or is not supported.")); } } else if (fileInfo.suffix() == "csv") { + CollectionDetails loadedCollection; if (!loadUrlContent()) return false; loadedCollection = CollectionDetails::fromImportedCsv(fileContent, firstRowIsHeader); + if (loadedCollection.columns()) { + m_view->addNewCollection(collectionName, loadedCollection.toLocalJson()); + return true; + } else { + warn(tr("Can not add a model to the JSON file"), + tr("The imported model is empty or is not supported.")); + } } - if (loadedCollection.columns()) { - m_view->addNewCollection(collectionName, loadedCollection.toLocalJson()); - return true; - } else { - warn(tr("Can not add a model to the JSON file"), - tr("The imported model is empty or is not supported.")); - } return false; } From 99fcb5c14b6cb29e3d6ac64eec9b5239951795ec Mon Sep 17 00:00:00 2001 From: Burak Hancerli Date: Fri, 10 May 2024 13:31:01 +0200 Subject: [PATCH 085/154] QmlPuppet: Fix the debug messages Change-Id: I2b1d969a665863232a8d60a943fce87b6bb4dd3c Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Tim Jenssen --- src/tools/qml2puppet/CMakeLists.txt | 25 +-------- .../qml2puppet/qml2puppet/appmetadata.cpp | 51 ----------------- src/tools/qml2puppet/qml2puppet/appmetadata.h | 55 ------------------- .../qml2puppet/qml2puppet/qml2puppetmain.cpp | 31 ++++++++++- src/tools/qml2puppet/qml2puppet/qmlbase.h | 25 +-------- src/tools/qml2puppet/qml2puppet/qmlpuppet.cpp | 1 - .../qml2puppet/runner/qmlruntime.cpp | 48 +++++++++------- 7 files changed, 61 insertions(+), 175 deletions(-) delete mode 100644 src/tools/qml2puppet/qml2puppet/appmetadata.cpp delete mode 100644 src/tools/qml2puppet/qml2puppet/appmetadata.h diff --git a/src/tools/qml2puppet/CMakeLists.txt b/src/tools/qml2puppet/CMakeLists.txt index b9f1a1544cc..36ca6293d43 100644 --- a/src/tools/qml2puppet/CMakeLists.txt +++ b/src/tools/qml2puppet/CMakeLists.txt @@ -38,39 +38,20 @@ add_qtc_executable(qml2puppet DEPENDS Qt::CorePrivate Qt::Widgets Qt::QmlPrivate Qt::QuickPrivate Qt::Network Qt::GuiPrivate + app_version QmlPuppetCommunication INCLUDES ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR} SOURCES qml2puppet/qml2puppetmain.cpp qml2puppet/qmlbase.h - qml2puppet/appmetadata.cpp qml2puppet/appmetadata.h - qml2puppet/qmlpuppet.h qml2puppet/qmlpuppet.cpp qml2puppet/configcrashpad.h + qml2puppet/qmlpuppet.h qml2puppet/qmlpuppet.cpp + qml2puppet/configcrashpad.h qmlpuppet.qrc PROPERTIES OUTPUT_NAME qml2puppet-${IDE_VERSION} ) -if (TARGET qml2puppet) - execute_process( - COMMAND git describe --tags --always --dirty=+ - WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} - RESULT_VARIABLE GIT_SHA_RESULT - OUTPUT_VARIABLE GIT_SHA - ERROR_VARIABLE GIT_SHA_ERROR - OUTPUT_STRIP_TRAILING_WHITESPACE - ) - - #if we are not a git repository use the .tag file - if(NOT GIT_SHA) - file(STRINGS ${CMAKE_CURRENT_SOURCE_DIR}/../../../.tag GIT_SHA LIMIT_COUNT 1) - endif() - - set(IDE_REVISION_STR ${GIT_SHA}) - - configure_file(../../app/app_version.h.cmakein app/app_version.h ESCAPE_QUOTES) -endif() - extend_qtc_executable(qml2puppet CONDITION Qt6_VERSION SOURCES diff --git a/src/tools/qml2puppet/qml2puppet/appmetadata.cpp b/src/tools/qml2puppet/qml2puppet/appmetadata.cpp deleted file mode 100644 index 1896e4db926..00000000000 --- a/src/tools/qml2puppet/qml2puppet/appmetadata.cpp +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0 - -#include "appmetadata.h" - -#include - -namespace QDSMeta::AppInfo { - -void printAppInfo() -{ - qInfo() << Qt::endl - << "<< QDS Meta Info >>" << Qt::endl - << "App Info" << Qt::endl - << " - Name :" << Core::Constants::IDE_ID << Qt::endl - << " - Version :" << Core::Constants::IDE_VERSION_DISPLAY << Qt::endl - << " - Author :" << Core::Constants::IDE_AUTHOR << Qt::endl - << " - Year :" << Core::Constants::IDE_YEAR << Qt::endl - << " - App :" << QCoreApplication::applicationName() << Qt::endl - << "Build Info " << Qt::endl - << " - Date :" << __DATE__ << Qt::endl - << " - Commit :" << QStringLiteral(QDS_STRINGIFY(IDE_REVISION_STR)) << Qt::endl - << " - Qt Version :" << QT_VERSION_STR << Qt::endl - << "Compiler Info " << Qt::endl -#if defined(__GNUC__) - << " - GCC :" << __GNUC__ << Qt::endl - << " - GCC Minor :" << __GNUC_MINOR__ << Qt::endl - << " - GCC Patch :" << __GNUC_PATCHLEVEL__ << Qt::endl -#endif -#if defined(_MSC_VER) - << " - MSC Short :" << _MSC_VER << Qt::endl - << " - MSC Full :" << _MSC_FULL_VER << Qt::endl -#endif -#if defined(__clang__) - << " - clang maj :" << __clang_major__ << Qt::endl - << " - clang min :" << __clang_minor__ << Qt::endl - << " - clang patch :" << __clang_patchlevel__ << Qt::endl -#endif - << "<< End Of QDS Meta Info >>" << Qt::endl; - exit(0); -} - -void registerAppInfo(const QString &appName) -{ - QCoreApplication::setOrganizationName(Core::Constants::IDE_AUTHOR); - QCoreApplication::setOrganizationDomain("qt-project.org"); - QCoreApplication::setApplicationName(appName); - QCoreApplication::setApplicationVersion(Core::Constants::IDE_VERSION_LONG); -} - -} // namespace QDSMeta::AppInfo diff --git a/src/tools/qml2puppet/qml2puppet/appmetadata.h b/src/tools/qml2puppet/qml2puppet/appmetadata.h deleted file mode 100644 index 18eb650461e..00000000000 --- a/src/tools/qml2puppet/qml2puppet/appmetadata.h +++ /dev/null @@ -1,55 +0,0 @@ -// 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 -#include - -// Common functions can be used in all QDS apps -namespace QDSMeta { - -namespace Logging { -inline Q_LOGGING_CATEGORY(deprecated, "qt.tools.qds.deprecated"); -inline Q_LOGGING_CATEGORY(verbose1, "qt.tools.qds.verbose1"); -inline Q_LOGGING_CATEGORY(verbose2, "qt.tools.qds.verbose2"); - -inline void registerMessageHandler() -{ - qInstallMessageHandler( - [](QtMsgType type, const QMessageLogContext &context, const QString &msg) { - auto tPrinter = [&](const QString &msgPrefix) { - fprintf(stderr, - "%s: %s (%s:%u, %s)\n", - msgPrefix.toLocal8Bit().constData(), - msg.toLocal8Bit().constData(), - context.file, - context.line, - context.function); - }; - - if (type == QtDebugMsg) - tPrinter("Debug"); - else if (type == QtInfoMsg) - tPrinter("Info"); - else if (type == QtWarningMsg) - tPrinter("Warning"); - else if (type == QtCriticalMsg) - tPrinter("Critical"); - else if (type == QtFatalMsg) { - tPrinter("Fatal"); - abort(); - } - }); -} -} // namespace Logging - -namespace AppInfo { - -#define STRINGIFY_INTERNAL(x) #x -#define QDS_STRINGIFY(x) STRINGIFY_INTERNAL(x) - -void printAppInfo(); -void registerAppInfo(const QString &appName); - -} // namespace AppInfo -} // namespace QDSMeta diff --git a/src/tools/qml2puppet/qml2puppet/qml2puppetmain.cpp b/src/tools/qml2puppet/qml2puppet/qml2puppetmain.cpp index 5896df39e1f..937bb36d8eb 100644 --- a/src/tools/qml2puppet/qml2puppet/qml2puppetmain.cpp +++ b/src/tools/qml2puppet/qml2puppet/qml2puppetmain.cpp @@ -7,6 +7,34 @@ #include "runner/qmlruntime.h" #endif +#include + +inline Q_LOGGING_CATEGORY(deprecated, "qt.tools.qds.deprecated"); +inline Q_LOGGING_CATEGORY(verbose1, "qt.tools.qds.verbose1"); +inline Q_LOGGING_CATEGORY(verbose2, "qt.tools.qds.verbose2"); + +void registerMessageHandler( + QtMsgType type, [[maybe_unused]] const QMessageLogContext &context, const QString &msg) +{ + auto tPrinter = [&](const QString &msgPrefix) { + fprintf( + stderr, "%s: %s\n", msgPrefix.toLocal8Bit().constData(), msg.toLocal8Bit().constData()); + }; + + if (type == QtDebugMsg) + tPrinter("Debug"); + else if (type == QtInfoMsg) + tPrinter("Info"); + else if (type == QtWarningMsg) + tPrinter("Warning"); + else if (type == QtCriticalMsg) + tPrinter("Critical"); + else if (type == QtFatalMsg) { + tPrinter("Fatal"); + abort(); + } +} + auto getQmlRunner(int &argc, char **argv) { #ifdef ENABLE_INTERNAL_QML_RUNTIME @@ -24,7 +52,6 @@ auto getQmlRunner(int &argc, char **argv) int main(int argc, char *argv[]) { - QDSMeta::Logging::registerMessageHandler(); - QDSMeta::AppInfo::registerAppInfo("Qml2Puppet"); + qInstallMessageHandler(registerMessageHandler); return getQmlRunner(argc, argv)->run(); } diff --git a/src/tools/qml2puppet/qml2puppet/qmlbase.h b/src/tools/qml2puppet/qml2puppet/qmlbase.h index ae8995f4893..45be0cae71d 100644 --- a/src/tools/qml2puppet/qml2puppet/qmlbase.h +++ b/src/tools/qml2puppet/qml2puppet/qmlbase.h @@ -3,27 +3,12 @@ #pragma once -#include -#include -#include -#include - -#include -#include -#include -#include - +#include #include +#include -#include -#include - -#include - -#include "appmetadata.h" #include -#include class QmlBase : public QObject { Q_OBJECT @@ -44,7 +29,6 @@ public: #ifdef ENABLE_INTERNAL_QML_RUNTIME m_argParser.addOption({"qml-runtime", "Run QML Runtime"}); #endif - m_argParser.addOption({"appinfo", "Print build information"}); m_argParser.addOption({"test", "Run test mode"}); } @@ -92,7 +76,6 @@ private: void initParser() { QCommandLineOption optHelp = m_argParser.addHelpOption(); - QCommandLineOption optVers = m_argParser.addVersionOption(); if (!m_argParser.parse(m_coreApp->arguments())) { std::cout << "Error: " << m_argParser.errorText().toStdString() << std::endl; @@ -103,12 +86,8 @@ private: std::cout << std::endl; m_argParser.showHelp(1); - } else if (m_argParser.isSet(optVers)) { - m_argParser.showVersion(); } else if (m_argParser.isSet(optHelp)) { m_argParser.showHelp(0); - } else if (m_argParser.isSet("appinfo")) { - QDSMeta::AppInfo::printAppInfo(); } else if (m_argParser.isSet("test")) { exit(startTestMode()); } diff --git a/src/tools/qml2puppet/qml2puppet/qmlpuppet.cpp b/src/tools/qml2puppet/qml2puppet/qmlpuppet.cpp index 86dce8cb90d..332def504ee 100644 --- a/src/tools/qml2puppet/qml2puppet/qmlpuppet.cpp +++ b/src/tools/qml2puppet/qml2puppet/qmlpuppet.cpp @@ -10,7 +10,6 @@ #include #include - #include #include diff --git a/src/tools/qml2puppet/qml2puppet/runner/qmlruntime.cpp b/src/tools/qml2puppet/qml2puppet/runner/qmlruntime.cpp index 4435d0e9f42..2876339a903 100644 --- a/src/tools/qml2puppet/qml2puppet/runner/qmlruntime.cpp +++ b/src/tools/qml2puppet/qml2puppet/runner/qmlruntime.cpp @@ -13,6 +13,10 @@ #include #include #include +#include +#include +#include +#include #define FILE_OPEN_EVENT_WAIT_TIME 3000 // ms #define QSL QStringLiteral @@ -242,12 +246,12 @@ void QmlRuntime::initQmlRunner() if (translator.load(translationFile)) { m_coreApp->installTranslator(&translator); if (m_verboseMode) - qInfo() << "qml: Loaded translation file %s\n", - qPrintable(QDir::toNativeSeparators(translationFile)); + qInfo() << "qml: Loaded translation file " + << qPrintable(QDir::toNativeSeparators(translationFile)); } else { if (!m_quietMode) - qInfo() << "qml: Could not load the translation file %s\n", - qPrintable(QDir::toNativeSeparators(translationFile)); + qInfo() << "qml: Could not load the translation file " + << qPrintable(QDir::toNativeSeparators(translationFile)); } } #else @@ -278,7 +282,7 @@ void QmlRuntime::initQmlRunner() for (const QString &path : std::as_const(files)) { QUrl url = QUrl::fromUserInput(path, QDir::currentPath(), QUrl::AssumeLocalFile); if (m_verboseMode) - qInfo() << "qml: loading %s\n", qPrintable(url.toString()); + qInfo() << "qml: loading " << qPrintable(url.toString()); m_qmlEngine->load(url); } @@ -318,8 +322,8 @@ void QmlRuntime::loadConf(const QString &override, bool quiet) // Terminates app else fi.setFile(override); if (!fi.exists()) { - qCritical() << "qml: Couldn't find required configuration file: %s\n", - qPrintable(QDir::toNativeSeparators(fi.absoluteFilePath())); + qCritical() << "qml: Couldn't find required configuration file:" + << qPrintable(QDir::toNativeSeparators(fi.absoluteFilePath())); exit(1); } settingsUrl = QQmlImports::urlFromLocalFileOrQrcOrUrl(fi.absoluteFilePath()); @@ -327,13 +331,14 @@ void QmlRuntime::loadConf(const QString &override, bool quiet) // Terminates app } if (!quiet) { - qInfo() << "qml: %s\n", QLibraryInfo::build(); + qInfo() << "qml:" << QLibraryInfo::build(); if (builtIn) { - qInfo() << "qml: Using built-in configuration: %s\n", - qPrintable(override.isEmpty() ? defaultFileName : override); + qInfo() << "qml: Using built-in configuration:" + << qPrintable(override.isEmpty() ? defaultFileName : override); } else { - qInfo() << "qml: Using configuration: %s\n", - qPrintable(settingsUrl.isLocalFile() + qInfo() << "qml: Using configuration:" + << qPrintable( + settingsUrl.isLocalFile() ? QDir::toNativeSeparators(settingsUrl.toLocalFile()) : settingsUrl.toString()); } @@ -345,18 +350,19 @@ void QmlRuntime::loadConf(const QString &override, bool quiet) // Terminates app m_conf.reset(qobject_cast(c2.create())); if (!m_conf) { - qCritical() << "qml: Error loading configuration file: %s\n", qPrintable(c2.errorString()); + qCritical() << "qml: Error loading configuration file:" << qPrintable(c2.errorString()); exit(1); } } void QmlRuntime::listConfFiles() { + qDebug() << "qml: Built-in configurations:"; const QDir confResourceDir(m_confResourcePath); - qInfo() << "%s\n", qPrintable(QCoreApplication::translate("main", "Built-in configurations:")); + qInfo() << qPrintable(QCoreApplication::translate("main", "Built-in configurations:")); for (const QFileInfo &fi : confResourceDir.entryInfoList(QDir::Files)) - qInfo() << " %s\n", qPrintable(fi.baseName()); - qInfo() << "%s\n", qPrintable(QCoreApplication::translate("main", "Other configurations:")); + qInfo() << qPrintable(fi.baseName()); + qInfo() << qPrintable(QCoreApplication::translate("main", "Other configurations:")); bool foundOther = false; const QStringList otherLocations = QStandardPaths::standardLocations( QStandardPaths::AppConfigLocation); @@ -365,16 +371,16 @@ void QmlRuntime::listConfFiles() for (const QFileInfo &fi : confDir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot)) { foundOther = true; if (m_verboseMode) - qInfo() << " %s\n", qPrintable(fi.absoluteFilePath()); + qInfo() << qPrintable(fi.absoluteFilePath()); else - qInfo() << " %s\n", qPrintable(fi.baseName()); + qInfo() << qPrintable(fi.baseName()); } } if (!foundOther) - qInfo() << " %s\n", qPrintable(QCoreApplication::translate("main", "none")); + qInfo() << qPrintable(QCoreApplication::translate("main", "none")); if (m_verboseMode) { - qInfo() << "%s\n", qPrintable(QCoreApplication::translate("main", "Checked in:")); + qInfo() << qPrintable(QCoreApplication::translate("main", "Checked in:")); for (const auto &confDirPath : otherLocations) - qInfo() << " %s\n", qPrintable(confDirPath); + qInfo() << qPrintable(confDirPath); } } From c1172ced982aa69949e8b10f60695a6cf290b3a9 Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Wed, 8 May 2024 18:29:07 +0300 Subject: [PATCH 086/154] QmlDesigner: Allow saving a 3D node to the content library Fixes: QDS-12393 Change-Id: I3112244bcbe74ea0f8f2eda8ccb6ad7470ca0e1e Reviewed-by: Miikka Heikkinen Reviewed-by: Qt CI Patch Build Bot --- .../components/componentcore/viewmanager.cpp | 2 +- .../contentlibraryusermodel.cpp | 85 ++++++++++---- .../contentlibrary/contentlibraryusermodel.h | 8 +- .../contentlibrary/contentlibraryview.cpp | 106 ++++++++++++++++-- .../contentlibrary/contentlibraryview.h | 9 +- .../components/edit3d/edit3dwidget.cpp | 10 ++ .../components/edit3d/edit3dwidget.h | 1 + 7 files changed, 188 insertions(+), 33 deletions(-) diff --git a/src/plugins/qmldesigner/components/componentcore/viewmanager.cpp b/src/plugins/qmldesigner/components/componentcore/viewmanager.cpp index b011d9fbbf3..025ef5cb73a 100644 --- a/src/plugins/qmldesigner/components/componentcore/viewmanager.cpp +++ b/src/plugins/qmldesigner/components/componentcore/viewmanager.cpp @@ -65,7 +65,7 @@ public: externalDependencies, true) , collectionView{externalDependencies} - , contentLibraryView{externalDependencies} + , contentLibraryView{imageCache, externalDependencies} , componentView{externalDependencies} #ifndef QTC_USE_QML_DESIGNER_LITE , edit3DView{externalDependencies} diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp index b8f528d6557..165f3350aa2 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp @@ -98,17 +98,35 @@ void ContentLibraryUserModel::addMaterial(const QString &name, const QString &qm const QUrl &icon, const QStringList &files) { auto compUtils = QmlDesignerPlugin::instance()->documentManager().generatedComponentUtils(); + QString typePrefix = compUtils.userMaterialsBundleType(); TypeName type = QLatin1String("%1.%2").arg(typePrefix, qml.chopped(4)).toLatin1(); auto libMat = new ContentLibraryMaterial(this, name, qml, type, icon, files, Paths::bundlesPathSetting().append("/User/materials")); - m_userMaterials.append(libMat); + int matSectionIdx = 0; emit dataChanged(index(matSectionIdx), index(matSectionIdx)); } +void ContentLibraryUserModel::add3DItem(const QString &name, const QString &qml, + const QUrl &icon, const QStringList &files) +{ + auto compUtils = QmlDesignerPlugin::instance()->documentManager().generatedComponentUtils(); + + QString typePrefix = compUtils.user3DBundleType(); + TypeName type = QLatin1String("%1.%2").arg(typePrefix, qml.chopped(4)).toLatin1(); + + m_user3DItems.append(new ContentLibraryItem(this, name, qml, type, icon, files)); +} + +void ContentLibraryUserModel::refresh3DSection() +{ + int sectionIdx = 2; + emit dataChanged(index(sectionIdx), index(sectionIdx)); +} + void ContentLibraryUserModel::addTextures(const QStringList &paths) { QDir bundleDir{Paths::bundlesPathSetting() + "/User/textures"}; @@ -198,7 +216,7 @@ void ContentLibraryUserModel::removeFromContentLib(ContentLibraryMaterial *mat) } // returns unique library material's name and qml component -QPair ContentLibraryUserModel::getUniqueLibMaterialNameAndQml(const QString &matName) const +QPair ContentLibraryUserModel::getUniqueLibMaterialNameAndQml(const QString &defaultName) const { QTC_ASSERT(!m_bundleObjMaterial.isEmpty(), return {}); @@ -209,10 +227,9 @@ QPair ContentLibraryUserModel::getUniqueLibMaterialNameAndQml( for (const QString &matName : matNames) matQmls.append(matsObj.value(matName).toObject().value("qml").toString().chopped(4)); // remove .qml - QString retName = matName.isEmpty() ? "Material" : matName; - retName = retName.trimmed(); - + QString retName = defaultName.isEmpty() ? "Material" : defaultName.trimmed(); QString retQml = retName; + retQml.remove(' '); if (retQml.at(0).isLower()) retQml[0] = retQml.at(0).toUpper(); @@ -232,6 +249,32 @@ QPair ContentLibraryUserModel::getUniqueLibMaterialNameAndQml( return {retName, retQml + ".qml"}; } +QString ContentLibraryUserModel::getUniqueLib3DQmlName(const QString &defaultName) const +{ + QTC_ASSERT(!m_bundleObj3D.isEmpty(), return {}); + + const QJsonArray itemsArr = m_bundleObj3D.value("items").toArray(); + + QStringList itemQmls; + for (const QJsonValueConstRef &itemRef : itemsArr) + itemQmls.append(itemRef.toObject().value("qml").toString().chopped(4)); // remove .qml + + QString baseQml = defaultName.isEmpty() ? "Item" : defaultName.trimmed(); + baseQml.remove(' '); + baseQml[0] = baseQml.at(0).toUpper(); + baseQml.prepend("My"); + + QString uniqueQml = baseQml; + + int counter = 1; + while (itemQmls.contains(uniqueQml)) { + uniqueQml = QString("%1%2").arg(uniqueQml).arg(counter); + ++counter; + } + + return uniqueQml + ".qml"; +} + QHash ContentLibraryUserModel::roleNames() const { static const QHash roles { @@ -242,11 +285,16 @@ QHash ContentLibraryUserModel::roleNames() const return roles; } -QJsonObject &ContentLibraryUserModel::bundleJsonObjectRef() +QJsonObject &ContentLibraryUserModel::bundleJsonMaterialObjectRef() { return m_bundleObjMaterial; } +QJsonObject &ContentLibraryUserModel::bundleJson3DObjectRef() +{ + return m_bundleObj3D; +} + void ContentLibraryUserModel::loadBundles() { loadMaterialBundle(); @@ -356,15 +404,15 @@ void ContentLibraryUserModel::load3DBundle() int section3DIdx = 2; m_bundlePath3D = Utils::FilePath::fromString(Paths::bundlesPathSetting() + "/User/3d"); - m_bundlePath3D.createDir(); + m_bundlePath3D.ensureWritableDir(); + m_bundlePath3D.pathAppended("icons").ensureWritableDir(); auto jsonFilePath = m_bundlePath3D.pathAppended("user_3d_bundle.json"); if (!jsonFilePath.exists()) { QByteArray jsonContent = "{\n"; jsonContent += " \"id\": \"User3D\",\n"; - jsonContent += " \"items\": {\n"; - jsonContent += " }\n"; + jsonContent += " \"items\": []\n"; jsonContent += "}"; Utils::expected_str res = jsonFilePath.writeFileContents(jsonContent); if (!res.has_value()) { @@ -393,24 +441,21 @@ void ContentLibraryUserModel::load3DBundle() m_bundleObj3D["id"] = m_bundleId3D; // parse 3d items - const QJsonObject itemsObj = m_bundleObj3D.value("items").toObject(); - const QStringList itemNames = itemsObj.keys(); QString typePrefix = compUtils.user3DBundleType(); - for (const QString &itemName : itemNames) { - const QJsonObject itemObj = itemsObj.value(itemName).toObject(); + const QJsonArray itemsArr = m_bundleObj3D.value("items").toArray(); + for (const QJsonValueConstRef &itemRef : itemsArr) { + const QJsonObject itemObj = itemRef.toObject(); + QString name = itemObj.value("name").toString(); + QString qml = itemObj.value("qml").toString(); + TypeName type = QLatin1String("%1.%2").arg(typePrefix, qml.chopped(4)).toLatin1(); + QUrl icon = m_bundlePath3D.pathAppended(itemObj.value("icon").toString()).toUrl(); QStringList files; const QJsonArray assetsArr = itemObj.value("files").toArray(); for (const QJsonValueConstRef &asset : assetsArr) files.append(asset.toString()); - QUrl icon = m_bundlePath3D.pathAppended(itemObj.value("icon").toString()).toUrl(); - QString qml = itemObj.value("qml").toString(); - TypeName type = QLatin1String("%1.%2").arg(typePrefix, qml.chopped(4)).toLatin1(); - - auto bundleItem = new ContentLibraryItem(nullptr, itemName, qml, type, icon, files); - - m_user3DItems.append(bundleItem); + m_user3DItems.append(new ContentLibraryItem(nullptr, name, qml, type, icon, files)); } m_bundle3DSharedFiles.clear(); diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.h index c9cf4ad0772..d3ed51b1c95 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.h +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.h @@ -43,7 +43,8 @@ public: void setSearchText(const QString &searchText); void updateImportedState(const QStringList &importedItems); - QPair getUniqueLibMaterialNameAndQml(const QString &matName) const; + QPair getUniqueLibMaterialNameAndQml(const QString &defaultName = {}) const; + QString getUniqueLib3DQmlName(const QString &defaultName = {}) const; void setQuick3DImportVersion(int major, int minor); @@ -59,12 +60,15 @@ public: void updateIsEmpty3D(); void addMaterial(const QString &name, const QString &qml, const QUrl &icon, const QStringList &files); + void add3DItem(const QString &name, const QString &qml, const QUrl &icon, const QStringList &files); + void refresh3DSection(); void addTextures(const QStringList &paths); void add3DInstance(ContentLibraryItem *bundleItem); void setBundleObj(const QJsonObject &newBundleObj); - QJsonObject &bundleJsonObjectRef(); + QJsonObject &bundleJsonMaterialObjectRef(); + QJsonObject &bundleJson3DObjectRef(); void loadBundles(); diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp index a72475f6d75..b7e5b6775fa 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp @@ -44,8 +44,10 @@ namespace QmlDesigner { -ContentLibraryView::ContentLibraryView(ExternalDependenciesInterface &externalDependencies) +ContentLibraryView::ContentLibraryView(AsynchronousImageCache &imageCache, + ExternalDependenciesInterface &externalDependencies) : AbstractView(externalDependencies) + , m_imageCache(imageCache) , m_createTexture(this) {} @@ -364,6 +366,8 @@ void ContentLibraryView::customNotification(const AbstractView *view, addLibMaterial(nodeList.first(), data.first().value()); } else if (identifier == "add_assets_to_content_lib") { addLibAssets(data.first().toStringList()); + } else if (identifier == "add_3d_to_content_lib") { + addLib3DItem(nodeList.first()); } } @@ -513,10 +517,10 @@ void ContentLibraryView::addLibMaterial(const ModelNode &mat, const QPixmap &ico qWarning() << __FUNCTION__ << "icon save failed"; // generate and save material Qml file - const QStringList depAssets = writeLibMaterialQml(mat, qml); + const QStringList depAssets = writeLibItemQml(mat, qml); // add the material to the bundle json - QJsonObject &jsonRef = m_widget->userModel()->bundleJsonObjectRef(); + QJsonObject &jsonRef = m_widget->userModel()->bundleJsonMaterialObjectRef(); QJsonObject matsObj = jsonRef.value("materials").toObject(); QJsonObject matObj; matObj.insert("qml", qml); @@ -554,14 +558,16 @@ void ContentLibraryView::addLibMaterial(const ModelNode &mat, const QPixmap &ico m_widget->userModel()->addMaterial(name, qml, QUrl::fromLocalFile(fullIconPath), depAssets); } -QStringList ContentLibraryView::writeLibMaterialQml(const ModelNode &mat, const QString &qml) +QStringList ContentLibraryView::writeLibItemQml(const ModelNode &node, const QString &qml) { QStringList depListIds; - auto [qmlString, assets] = modelNodeToQmlString(mat, depListIds); + auto [qmlString, assets] = modelNodeToQmlString(node, depListIds); qmlString.prepend("import QtQuick\nimport QtQuick3D\n\n"); - auto qmlPath = Utils::FilePath::fromString(Paths::bundlesPathSetting() + "/User/materials/" + qml); + QString itemType = QLatin1String(node.metaInfo().isQtQuick3DMaterial() ? "material" : "3d"); + auto qmlPath = Utils::FilePath::fromString(QLatin1String("%1/User/%2/%3") + .arg(Paths::bundlesPathSetting(), itemType, qml)); auto result = qmlPath.writeFileContents(qmlString.toUtf8()); if (!result) qWarning() << __FUNCTION__ << result.error(); @@ -584,16 +590,24 @@ QPair> ContentLibraryView::modelNodeToQmlString(const Mod qml += indent + "id: " + (depth == 0 ? "root" : node.id()) + " \n\n"; + const QList excludedProps = {"x", "y", "z", "eulerRotation.x", "eulerRotation.y", + "eulerRotation.z", "scale.x", "scale.y", "scale.z", + "pivot.x", "pivot.y", "pivot.z"}; const QList matProps = node.properties(); for (const AbstractProperty &p : matProps) { + if (excludedProps.contains(p.name())) + continue; + if (p.isVariantProperty()) { QVariant pValue = p.toVariantProperty().value(); QString val; if (strcmp(pValue.typeName(), "QString") == 0 || strcmp(pValue.typeName(), "QColor") == 0) { val = QLatin1String("\"%1\"").arg(pValue.toString()); } else if (strcmp(pValue.typeName(), "QUrl") == 0) { - val = QLatin1String("\"%1\"").arg(pValue.toString()); - assets.insert(pValue.toString()); + QString pValueStr = pValue.toString(); + val = QLatin1String("\"%1\"").arg(pValueStr); + if (!pValueStr.startsWith("#")) + assets.insert(pValue.toString()); } else if (strcmp(pValue.typeName(), "QmlDesigner::Enumeration") == 0) { val = pValue.value().toString(); } else { @@ -649,6 +663,82 @@ void ContentLibraryView::addLibAssets(const QStringList &paths) m_widget->userModel()->addTextures(pathsInBundle); } +void ContentLibraryView::addLib3DItem(const ModelNode &node) +{ + auto bundlePath = Utils::FilePath::fromString(Paths::bundlesPathSetting() + "/User/3d/"); + + QString name = node.variantProperty("objectName").value().toString(); + QString qml = m_widget->userModel()->getUniqueLib3DQmlName(node.id()); + QString iconPath = QLatin1String("icons/%1.png").arg(node.id()); // TODO: make sure path is unique + + // generate and save item Qml file + const QStringList depAssets = writeLibItemQml(node, qml); + + // generate and save icon + QString qmlPath = QLatin1String("%1/User/3d/%2").arg(Paths::bundlesPathSetting(), qml); + QString fullIconPath = bundlePath.pathAppended(iconPath).toString(); + genAndSaveIcon(qmlPath, fullIconPath); + + // add the item to the bundle json + QJsonObject &jsonRef = m_widget->userModel()->bundleJson3DObjectRef(); + QJsonArray itemsArr = jsonRef.value("items").toArray(); + QJsonObject itemObj; + itemObj.insert("name", name); + itemObj.insert("qml", qml); + itemObj.insert("icon", iconPath); + QJsonArray filesArr; + for (const QString &assetPath : depAssets) + filesArr.append(assetPath); + itemObj.insert("files", filesArr); + + itemsArr.append(itemObj); + jsonRef["items"] = itemsArr; + + auto result = bundlePath.pathAppended("user_3d_bundle.json") + .writeFileContents(QJsonDocument(jsonRef).toJson()); + if (!result) + qWarning() << __FUNCTION__ << result.error(); + + // copy item's assets to bundle folder + for (const QString &assetPath : depAssets) { + Utils::FilePath assetPathSource = DocumentManager::currentResourcePath().pathAppended(assetPath); + Utils::FilePath assetPathTarget = bundlePath.pathAppended(assetPath); + assetPathTarget.parentDir().ensureWritableDir(); + + auto result = assetPathSource.copyFile(assetPathTarget); + if (!result) + qWarning() << __FUNCTION__ << result.error(); + } + + m_widget->userModel()->add3DItem(name, qml, QUrl::fromLocalFile(fullIconPath), depAssets); +} + +/** + * @brief Generates an icon image from a qml component + * @param qmlPath path to the qml component file to be rendered + * @param iconPath output save path of the generated icon + */ +void ContentLibraryView::genAndSaveIcon(const QString &qmlPath, const QString &iconPath) +{ + m_imageCache.requestSmallImage( + Utils::PathString{qmlPath}, + [&, qmlPath, iconPath](const QImage &image) { + bool iconSaved = image.save(iconPath); + if (iconSaved) + m_widget->userModel()->refresh3DSection(); + else + qWarning() << "ContentLibraryView::genAndSaveIcon(): icon save failed"; + }, + [](ImageCache::AbortReason abortReason) { + if (abortReason == ImageCache::AbortReason::Abort) + qWarning() << "ContentLibraryView::genAndSaveIcon(): icon generation aborted, reason: Abort"; + else if (abortReason == ImageCache::AbortReason::Failed) + qWarning() << "ContentLibraryView::genAndSaveIcon(): icon generation aborted, reason: Failed"; + else if (abortReason == ImageCache::AbortReason::NoEntry) + qWarning() << "ContentLibraryView::genAndSaveIcon(): icon generation aborted, reason: NoEntry"; + }); +} + ModelNode ContentLibraryView::getBundleMaterialDefaultInstance(const TypeName &type) { ModelNode matLib = Utils3D::materialLibraryNode(this); diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.h index 232f7dd194c..2891960f337 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.h +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.h @@ -3,6 +3,7 @@ #pragma once +#include #include #include #include @@ -25,7 +26,8 @@ class ContentLibraryView : public AbstractView Q_OBJECT public: - ContentLibraryView(ExternalDependenciesInterface &externalDependencies); + ContentLibraryView(AsynchronousImageCache &imageCache, + ExternalDependenciesInterface &externalDependencies); ~ContentLibraryView() override; bool hasWidget() const override; @@ -55,7 +57,9 @@ private: void updateBundlesQuick3DVersion(); void addLibMaterial(const ModelNode &mat, const QPixmap &icon); void addLibAssets(const QStringList &paths); - QStringList writeLibMaterialQml(const ModelNode &mat, const QString &qml); + void addLib3DItem(const ModelNode &node); + void genAndSaveIcon(const QString &qmlPath, const QString &iconPath); + QStringList writeLibItemQml(const ModelNode &node, const QString &qml); QPair> modelNodeToQmlString(const ModelNode &node, QStringList &depListIds, int depth = 0); @@ -79,6 +83,7 @@ private: ContentLibraryMaterial *m_draggedBundleMaterial = nullptr; ContentLibraryTexture *m_draggedBundleTexture = nullptr; ContentLibraryItem *m_draggedBundleItem = nullptr; + AsynchronousImageCache &m_imageCache; bool m_bundleMaterialAddToSelected = false; bool m_hasQuick3DImport = false; qint32 m_sceneId = -1; diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp index 6aba55f6a86..b5e6e245e5e 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp +++ b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp @@ -359,6 +359,14 @@ void Edit3DWidget::createContextMenu() resetAction->setToolTip(tr("Reset all shading options for all viewports.")); m_contextMenu->addSeparator(); + + m_addToContentLibAction = m_contextMenu->addAction( + contextIcon(DesignerIcons::CreateIcon), // TODO: placeholder icon + tr("Add to Content Library"), [&] { + view()->emitCustomNotification("add_3d_to_content_lib", {m_contextMenuTarget}); + }); + + m_contextMenu->addSeparator(); } bool Edit3DWidget::isPasteAvailable() const @@ -612,6 +620,7 @@ void Edit3DWidget::showContextMenu(const QPoint &pos, const ModelNode &modelNode m_contextMenuPos3d = pos3d; const bool isModel = modelNode.metaInfo().isQtQuick3DModel(); + const bool isNode = modelNode.metaInfo().isQtQuick3DNode(); const bool allowAlign = view()->edit3DAction(View3DActionType::AlignCamerasToView)->action()->isEnabled(); const bool isSingleComponent = view()->hasSingleSelectedModelNode() && modelNode.isComponent(); const bool anyNodeSelected = view()->hasSelectedModelNodes(); @@ -633,6 +642,7 @@ void Edit3DWidget::showContextMenu(const QPoint &pos, const ModelNode &modelNode m_toggleGroupAction->setEnabled(true); m_bakeLightsAction->setVisible(view()->bakeLightsAction()->action()->isVisible()); m_bakeLightsAction->setEnabled(view()->bakeLightsAction()->action()->isEnabled()); + m_addToContentLibAction->setEnabled(isNode); if (m_view) { int idx = m_view->activeSplit(); diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.h b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.h index 211b044d41d..97c0469668f 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.h +++ b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.h @@ -100,6 +100,7 @@ private: QPointer m_selectParentAction; QPointer m_toggleGroupAction; QPointer m_wireFrameAction; + QPointer m_addToContentLibAction; QHash> m_matOverrideActions; QPointer m_createSubMenu; ModelNode m_contextMenuTarget; From e4bdb92d0f575416d4ab4943af2998482b2b5d48 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Mon, 29 Apr 2024 16:23:36 +0200 Subject: [PATCH 087/154] QmlDesigner: Fix adding keyframe MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Keyframes were always added on the selected model node instead of the modelnode belonging to the property. Task-number: QDS-12622 Change-Id: Iae44cfecd862d79b14c062854d5f329da595f729 Reviewed-by: Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Henning Gründl --- .../HelperWidgets/ExtendedFunctionLogic.qml | 2 +- .../propertyeditor/propertyeditorvalue.cpp | 17 +++++++++++++++++ .../propertyeditor/propertyeditorvalue.h | 2 ++ 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ExtendedFunctionLogic.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ExtendedFunctionLogic.qml index 76e6bd8f094..8a8c8c33ce6 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ExtendedFunctionLogic.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ExtendedFunctionLogic.qml @@ -103,7 +103,7 @@ Item { StudioControls.MenuItem { text: qsTr("Insert Keyframe") enabled: hasActiveTimeline - onTriggered: insertKeyframe(backendValue.name) + onTriggered: backendValue.insertKeyframe() } } } diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.cpp b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.cpp index 80ee2ea1723..412db1055fa 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.cpp @@ -548,6 +548,23 @@ void PropertyEditorValue::setForceBound(bool b) emit isBoundChanged(); } +void PropertyEditorValue::insertKeyframe() +{ + if (!m_modelNode.isValid()) + return; + + /*If we add more code here we have to forward the property editor view */ + AbstractView *view = m_modelNode.view(); + + QmlTimeline timeline = view->currentTimeline(); + + QTC_ASSERT(timeline.isValid(), return ); + QTC_ASSERT(m_modelNode.isValid(), return ); + + view->executeInTransaction("PropertyEditorContextObject::insertKeyframe", + [&] { timeline.insertKeyframe(m_modelNode, name()); }); +} + QStringList PropertyEditorValue::generateStringList(const QString &string) const { QString copy = string; diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.h b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.h index 70a51fffc2c..c4b09f6b5af 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.h +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.h @@ -172,6 +172,8 @@ public: Q_INVOKABLE void setForceBound(bool b); + Q_INVOKABLE void insertKeyframe(); + public slots: void resetValue(); void setEnumeration(const QString &scope, const QString &name); From 4f23a553d1f12b9dc77c90a848a7a5a5219f6c53 Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Sat, 11 May 2024 00:04:11 +0300 Subject: [PATCH 088/154] QmlDesigner: Enable content lib user 3D context menu Also some relevant tweaks. Change-Id: I7bace9ce6bd7b45951cc18f7175b4646251196f0 Reviewed-by: Ali Kianian Reviewed-by: Miikka Heikkinen --- ....qml => ContentLibraryItemContextMenu.qml} | 43 +++--- .../ContentLibraryMaterial.qml | 8 +- .../ContentLibraryMaterialsView.qml | 13 +- .../ContentLibraryUserView.qml | 24 ++- .../contentlibrary/contentlibraryitem.h | 1 - .../contentlibrary/contentlibrarymaterial.h | 2 +- .../contentlibrarymaterialsmodel.cpp | 14 -- .../contentlibrarymaterialsmodel.h | 8 - .../contentlibraryusermodel.cpp | 143 ++++++++++++++---- .../contentlibrary/contentlibraryusermodel.h | 19 +-- .../contentlibrary/contentlibraryview.cpp | 5 +- .../contentlibrary/contentlibrarywidget.cpp | 18 ++- .../contentlibrary/contentlibrarywidget.h | 7 +- 13 files changed, 184 insertions(+), 121 deletions(-) rename share/qtcreator/qmldesigner/contentLibraryQmlSource/{ContentLibraryMaterialContextMenu.qml => ContentLibraryItemContextMenu.qml} (55%) diff --git a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryMaterialContextMenu.qml b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryItemContextMenu.qml similarity index 55% rename from share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryMaterialContextMenu.qml rename to share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryItemContextMenu.qml index f1fa81ed76e..6f505124300 100644 --- a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryMaterialContextMenu.qml +++ b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryItemContextMenu.qml @@ -1,43 +1,50 @@ -// Copyright (C) 2022 The Qt Company Ltd. +// Copyright (C) 2024 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 StudioControls 1.0 as StudioControls -import StudioTheme 1.0 as StudioTheme +import QtQuick +import StudioControls as StudioControls +import StudioTheme as StudioTheme +import ContentLibraryBackend StudioControls.Menu { id: root - property var targetMaterial: null - property bool hasModelSelection: false - property bool importerRunning: false - property bool enableRemove: false // true: adds an option to remove targetMaterial + property var targetItem: null + property bool enableRemove: false // true: adds an option to remove targetItem - readonly property bool targetAvailable: targetMaterial && !importerRunning + readonly property bool targetAvailable: targetItem && !ContentLibraryBackend.rootView.importerRunning signal unimport(); signal addToProject() signal applyToSelected(bool add) signal removeFromContentLib() - function popupMenu(targetMaterial = null) + function popupMenu(item = null) { - this.targetMaterial = targetMaterial + root.targetItem = item + + let isMaterial = root.targetItem.itemType === "material" + applyToSelectedReplace.visible = isMaterial + applyToSelectedAdd.visible = isMaterial + popup() } closePolicy: StudioControls.Menu.CloseOnEscape | StudioControls.Menu.CloseOnPressOutside StudioControls.MenuItem { + id: applyToSelectedReplace text: qsTr("Apply to selected (replace)") - enabled: root.targetAvailable && root.hasModelSelection + height: visible ? implicitHeight : 0 + enabled: root.targetAvailable && ContentLibraryBackend.rootView.hasModelSelection onTriggered: root.applyToSelected(false) } StudioControls.MenuItem { + id: applyToSelectedAdd text: qsTr("Apply to selected (add)") - enabled: root.targetAvailable && root.hasModelSelection + height: visible ? implicitHeight : 0 + enabled: root.targetAvailable && ContentLibraryBackend.rootView.hasModelSelection onTriggered: root.applyToSelected(true) } @@ -46,16 +53,12 @@ StudioControls.Menu { StudioControls.MenuItem { enabled: root.targetAvailable text: qsTr("Add an instance to project") - - onTriggered: { - root.addToProject() - } + onTriggered: root.addToProject() } StudioControls.MenuItem { - enabled: root.targetAvailable && root.targetMaterial.bundleMaterialImported + enabled: root.targetAvailable && root.targetItem.bundleItemImported text: qsTr("Remove from project") - onTriggered: root.unimport() } diff --git a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryMaterial.qml b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryMaterial.qml index 775e5811936..20ba17d8041 100644 --- a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryMaterial.qml +++ b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryMaterial.qml @@ -16,8 +16,6 @@ Item { // "failed" property string downloadState: modelData.isDownloaded() ? "downloaded" : "" - property bool importerRunning: false - signal showContextMenu() signal addToProject() @@ -32,7 +30,7 @@ Item { acceptedButtons: Qt.LeftButton | Qt.RightButton onPressed: (mouse) => { - if (mouse.button === Qt.LeftButton && !root.importerRunning) { + if (mouse.button === Qt.LeftButton && !ContentLibraryBackend.rootView.importerRunning) { if (root.downloadState === "downloaded") ContentLibraryBackend.rootView.startDragMaterial(modelData, mapToGlobal(mouse.x, mouse.y)) } else if (mouse.button === Qt.RightButton && root.downloadState === "downloaded") { @@ -74,7 +72,7 @@ Item { color: "#00ff00" border.color: "#555555" border.width: 1 - visible: modelData.bundleMaterialImported + visible: modelData.bundleItemImported ToolTip { visible: indicatorMouseArea.containsMouse @@ -99,7 +97,7 @@ Item { pressColor: Qt.hsla(c.hslHue, c.hslSaturation, c.hslLightness, .4) anchors.right: img.right anchors.bottom: img.bottom - enabled: !root.importerRunning + enabled: !ContentLibraryBackend.rootView.importerRunning visible: root.downloadState === "downloaded" && (containsMouse || mouseArea.containsMouse) diff --git a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryMaterialsView.qml b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryMaterialsView.qml index 3e58dd4306e..e9fca319736 100644 --- a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryMaterialsView.qml +++ b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryMaterialsView.qml @@ -46,16 +46,13 @@ HelperWidgets.ScrollView { } Column { - ContentLibraryMaterialContextMenu { + ContentLibraryItemContextMenu { id: ctxMenu - hasModelSelection: root.materialsModel.hasModelSelection - importerRunning: ContentLibraryBackend.rootView.importerRunning + onApplyToSelected: (add) => root.materialsModel.applyToSelected(ctxMenu.targetItem, add) - onApplyToSelected: (add) => root.materialsModel.applyToSelected(ctxMenu.targetMaterial, add) - - onUnimport: root.unimport(ctxMenu.targetMaterial) - onAddToProject: root.materialsModel.addToProject(ctxMenu.targetMaterial) + onUnimport: root.unimport(ctxMenu.targetItem) + onAddToProject: root.materialsModel.addToProject(ctxMenu.targetItem) } Repeater { @@ -103,8 +100,6 @@ HelperWidgets.ScrollView { width: root.cellWidth height: root.cellHeight - importerRunning: ContentLibraryBackend.rootView.importerRunning - onShowContextMenu: ctxMenu.popupMenu(modelData) onAddToProject: root.materialsModel.addToProject(modelData) } diff --git a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryUserView.qml b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryUserView.qml index 55a03a2f07c..cb976fc1465 100644 --- a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryUserView.qml +++ b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryUserView.qml @@ -12,7 +12,7 @@ HelperWidgets.ScrollView { id: root clip: true - interactive: !ctxMenuMaterial.opened && !ctxMenuTexture.opened + interactive: !ctxMenuItem.opened && !ctxMenuTexture.opened && !ContentLibraryBackend.rootView.isDragging && !HelperWidgets.Controller.contextMenuOpened property real cellWidth: 100 @@ -34,7 +34,7 @@ HelperWidgets.ScrollView { signal removeFromContentLib(var bundleItem); function closeContextMenu() { - ctxMenuMaterial.close() + ctxMenuItem.close() ctxMenuTexture.close() } @@ -47,18 +47,16 @@ HelperWidgets.ScrollView { } Column { - ContentLibraryMaterialContextMenu { - id: ctxMenuMaterial + ContentLibraryItemContextMenu { + id: ctxMenuItem enableRemove: true - hasModelSelection: ContentLibraryBackend.userModel.hasModelSelection - importerRunning: ContentLibraryBackend.rootView.importerRunning - onApplyToSelected: (add) => ContentLibraryBackend.userModel.applyToSelected(ctxMenuMaterial.targetMaterial, add) + onApplyToSelected: (add) => ContentLibraryBackend.userModel.applyToSelected(ctxMenuItem.targetItem, add) - onUnimport: root.unimport(ctxMenuMaterial.targetMaterial) - onAddToProject: ContentLibraryBackend.userModel.addToProject(ctxMenuMaterial.targetMaterial) - onRemoveFromContentLib: root.removeFromContentLib(ctxMenuMaterial.targetMaterial) + onUnimport: root.unimport(ctxMenuItem.targetItem) + onAddToProject: ContentLibraryBackend.userModel.addToProject(ctxMenuItem.targetItem) + onRemoveFromContentLib: root.removeFromContentLib(ctxMenuItem.targetItem) } ContentLibraryTextureContextMenu { @@ -114,9 +112,7 @@ HelperWidgets.ScrollView { width: root.cellWidth height: root.cellHeight - importerRunning: ContentLibraryBackend.rootView.importerRunning - - onShowContextMenu: ctxMenuMaterial.popupMenu(modelData) + onShowContextMenu: ctxMenuItem.popupMenu(modelData) onAddToProject: ContentLibraryBackend.userModel.addToProject(modelData) onVisibleChanged: { @@ -139,7 +135,7 @@ HelperWidgets.ScrollView { width: root.cellWidth height: root.cellHeight - // onShowContextMenu: ctxMenuTexture.popupMenu(modelData) // TODO + onShowContextMenu: ctxMenuItem.popupMenu(modelData) } } } diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryitem.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryitem.h index 53587e73291..bdf87367281 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryitem.h +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryitem.h @@ -36,7 +36,6 @@ public: TypeName type() const; QStringList files() const; bool visible() const; - QString qmlFilePath() const; bool setImported(bool imported); bool imported() const; diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterial.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterial.h index d965f25014d..aaaf9517f99 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterial.h +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterial.h @@ -17,7 +17,7 @@ class ContentLibraryMaterial : public QObject Q_PROPERTY(QString bundleMaterialName MEMBER m_name CONSTANT) Q_PROPERTY(QUrl bundleMaterialIcon MEMBER m_icon CONSTANT) Q_PROPERTY(bool bundleMaterialVisible MEMBER m_visible NOTIFY materialVisibleChanged) - Q_PROPERTY(bool bundleMaterialImported READ imported WRITE setImported NOTIFY materialImportedChanged) + Q_PROPERTY(bool bundleItemImported READ imported WRITE setImported NOTIFY materialImportedChanged) Q_PROPERTY(QString bundleMaterialBaseWebUrl MEMBER m_baseWebUrl CONSTANT) Q_PROPERTY(QString bundleMaterialDirPath READ dirPath CONSTANT) Q_PROPERTY(QStringList bundleMaterialFiles READ allFiles CONSTANT) diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.cpp index 1fc05c3503f..adb47108a9a 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.cpp @@ -383,18 +383,4 @@ void ContentLibraryMaterialsModel::removeFromProject(ContentLibraryMaterial *mat qWarning() << __FUNCTION__ << err; } -bool ContentLibraryMaterialsModel::hasModelSelection() const -{ - return m_hasModelSelection; -} - -void ContentLibraryMaterialsModel::setHasModelSelection(bool b) -{ - if (b == m_hasModelSelection) - return; - - m_hasModelSelection = b; - emit hasModelSelectionChanged(); -} - } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.h index 3c09bf07c04..c0840dc3cc7 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.h +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.h @@ -21,7 +21,6 @@ class ContentLibraryMaterialsModel : public QAbstractListModel Q_PROPERTY(bool matBundleExists READ matBundleExists NOTIFY matBundleExistsChanged) Q_PROPERTY(bool isEmpty MEMBER m_isEmpty NOTIFY isEmptyChanged) Q_PROPERTY(bool hasRequiredQuick3DImport READ hasRequiredQuick3DImport NOTIFY hasRequiredQuick3DImportChanged) - Q_PROPERTY(bool hasModelSelection READ hasModelSelection NOTIFY hasModelSelectionChanged) public: ContentLibraryMaterialsModel(ContentLibraryWidget *parent = nullptr); @@ -33,16 +32,11 @@ public: void setSearchText(const QString &searchText); void updateImportedState(const QStringList &importedItems); - void setQuick3DImportVersion(int major, int minor); bool hasRequiredQuick3DImport() const; - bool matBundleExists() const; - bool hasModelSelection() const; - void setHasModelSelection(bool b); - void resetModel(); void updateIsEmpty(); void loadBundle(); @@ -56,7 +50,6 @@ public: signals: void isEmptyChanged(); void hasRequiredQuick3DImportChanged(); - void hasModelSelectionChanged(); void materialVisibleChanged(); void applyToSelectedTriggered(QmlDesigner::ContentLibraryMaterial *mat, bool add = false); void matBundleExistsChanged(); @@ -77,7 +70,6 @@ private: bool m_isEmpty = true; bool m_bundleExists = false; - bool m_hasModelSelection = false; int m_quick3dMajorVersion = -1; int m_quick3dMinorVersion = -1; diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp index 165f3350aa2..efee2224452 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp @@ -177,7 +177,15 @@ void ContentLibraryUserModel::removeTexture(ContentLibraryTexture *tex) emit dataChanged(index(texSectionIdx), index(texSectionIdx)); } -void ContentLibraryUserModel::removeFromContentLib(ContentLibraryMaterial *mat) +void ContentLibraryUserModel::removeFromContentLib(QObject *item) +{ + if (auto mat = qobject_cast(item)) + removeMaterialFromContentLib(mat); + else if (auto itm = qobject_cast(item)) + remove3DFromContentLib(itm); +} + +void ContentLibraryUserModel::removeMaterialFromContentLib(ContentLibraryMaterial *mat) { auto bundlePath = Utils::FilePath::fromString(Paths::bundlesPathSetting() + "/User/materials/"); @@ -215,6 +223,48 @@ void ContentLibraryUserModel::removeFromContentLib(ContentLibraryMaterial *mat) emit dataChanged(index(matSectionIdx), index(matSectionIdx)); } +void ContentLibraryUserModel::remove3DFromContentLib(ContentLibraryItem *item) +{ + QJsonArray itemsArr = m_bundleObj3D.value("items").toArray(); + + // remove qml and icon files + m_bundlePath3D.pathAppended(item->qml()).removeFile(); + Utils::FilePath::fromUrl(item->icon()).removeFile(); + + // remove from the bundle json file + for (int i = 0; i < itemsArr.size(); ++i) { + if (itemsArr.at(i).toObject().value("qml") == item->qml()) { + itemsArr.removeAt(i); + break; + } + } + m_bundleObj3D.insert("items", itemsArr); + + auto result = m_bundlePath3D.pathAppended("user_3d_bundle.json") + .writeFileContents(QJsonDocument(m_bundleObj3D).toJson()); + if (!result) + qWarning() << __FUNCTION__ << result.error(); + + // delete dependency files if they are only used by the deleted item + QStringList allFiles; + for (const QJsonValueConstRef &itemRef : std::as_const(itemsArr)) + allFiles.append(itemRef.toObject().value("files").toVariant().toStringList()); + + const QStringList itemFiles = item->files(); + for (const QString &file : itemFiles) { + if (allFiles.count(file) == 0) // only used by the deleted item + m_bundlePath3D.pathAppended(file).removeFile(); + } + + // remove from model + m_user3DItems.removeOne(item); + item->deleteLater(); + + // update model + int sectionIdx = 2; + emit dataChanged(index(sectionIdx), index(sectionIdx)); +} + // returns unique library material's name and qml component QPair ContentLibraryUserModel::getUniqueLibMaterialNameAndQml(const QString &defaultName) const { @@ -520,7 +570,7 @@ void ContentLibraryUserModel::setSearchText(const QString &searchText) updateIsEmpty3D(); } -void ContentLibraryUserModel::updateImportedState(const QStringList &importedItems) +void ContentLibraryUserModel::updateMaterialsImportedState(const QStringList &importedItems) { bool changed = false; for (ContentLibraryMaterial *mat : std::as_const(m_userMaterials)) @@ -532,6 +582,18 @@ void ContentLibraryUserModel::updateImportedState(const QStringList &importedIte } } +void ContentLibraryUserModel::update3DImportedState(const QStringList &importedItems) +{ + bool changed = false; + for (ContentLibraryItem *item : std::as_const(m_user3DItems)) + changed |= item->setImported(importedItems.contains(item->qml().chopped(4))); + + if (changed) { + int sectionIdx = 2; + emit dataChanged(index(sectionIdx), index(sectionIdx)); + } +} + void ContentLibraryUserModel::setQuick3DImportVersion(int major, int minor) { bool oldRequiredImport = hasRequiredQuick3DImport(); @@ -561,39 +623,56 @@ void ContentLibraryUserModel::applyToSelected(ContentLibraryMaterial *mat, bool emit applyToSelectedTriggered(mat, add); } -void ContentLibraryUserModel::addToProject(ContentLibraryMaterial *mat) +void ContentLibraryUserModel::addToProject(QObject *item) { - QString err = m_widget->importer()->importComponent(mat->dirPath(), mat->type(), mat->qml(), - mat->files() + m_bundleMaterialSharedFiles); - - if (err.isEmpty()) - m_widget->setImporterRunning(true); - else - qWarning() << __FUNCTION__ << err; -} - -void ContentLibraryUserModel::removeFromProject(ContentLibraryMaterial *mat) -{ - QString err = m_widget->importer()->unimportComponent(mat->type(), mat->qml()); - - if (err.isEmpty()) - m_widget->setImporterRunning(true); - else - qWarning() << __FUNCTION__ << err; -} - -bool ContentLibraryUserModel::hasModelSelection() const -{ - return m_hasModelSelection; -} - -void ContentLibraryUserModel::setHasModelSelection(bool b) -{ - if (b == m_hasModelSelection) + QString bundleDir; + TypeName type; + QString qmlFile; + QStringList files; + if (auto mat = qobject_cast(item)) { + bundleDir = mat->dirPath(); + type = mat->type(); + qmlFile = mat->qml(); + files = mat->files() + m_bundleMaterialSharedFiles; + } else if (auto itm = qobject_cast(item)) { + bundleDir = m_bundlePath3D.toString(); + type = itm->type(); + qmlFile = itm->qml(); + files = itm->files() + m_bundle3DSharedFiles; + } else { + qWarning() << __FUNCTION__ << "Unsupported Item"; return; + } - m_hasModelSelection = b; - emit hasModelSelectionChanged(); + QString err = m_widget->importer()->importComponent(bundleDir, type, qmlFile, files); + + if (err.isEmpty()) + m_widget->setImporterRunning(true); + else + qWarning() << __FUNCTION__ << err; +} + +void ContentLibraryUserModel::removeFromProject(QObject *item) +{ + TypeName type; + QString qml; + if (auto mat = qobject_cast(item)) { + type = mat->type(); + qml = mat->qml(); + } else if (auto itm = qobject_cast(item)) { + type = itm->type(); + qml = itm->qml(); + } else { + qWarning() << __FUNCTION__ << "Unsupported Item"; + return; + } + + QString err = m_widget->importer()->unimportComponent(type, qml); + + if (err.isEmpty()) + m_widget->setImporterRunning(true); + else + qWarning() << __FUNCTION__ << err; } } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.h index d3ed51b1c95..7af5e861749 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.h +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.h @@ -27,7 +27,6 @@ class ContentLibraryUserModel : public QAbstractListModel Q_PROPERTY(bool isEmptyMaterials MEMBER m_isEmptyMaterials NOTIFY isEmptyMaterialsChanged) Q_PROPERTY(bool isEmpty3D MEMBER m_isEmpty3D NOTIFY isEmpty3DChanged) Q_PROPERTY(bool hasRequiredQuick3DImport READ hasRequiredQuick3DImport NOTIFY hasRequiredQuick3DImportChanged) - Q_PROPERTY(bool hasModelSelection READ hasModelSelection NOTIFY hasModelSelectionChanged) Q_PROPERTY(QList userMaterials MEMBER m_userMaterials NOTIFY userMaterialsChanged) Q_PROPERTY(QList userTextures MEMBER m_userTextures NOTIFY userTexturesChanged) Q_PROPERTY(QList user3DItems MEMBER m_user3DItems NOTIFY user3DItemsChanged) @@ -41,7 +40,8 @@ public: QHash roleNames() const override; void setSearchText(const QString &searchText); - void updateImportedState(const QStringList &importedItems); + void updateMaterialsImportedState(const QStringList &importedItems); + void update3DImportedState(const QStringList &importedItems); QPair getUniqueLibMaterialNameAndQml(const QString &defaultName = {}) const; QString getUniqueLib3DQmlName(const QString &defaultName = {}) const; @@ -52,9 +52,6 @@ public: bool matBundleExists() const; - bool hasModelSelection() const; - void setHasModelSelection(bool b); - void resetModel(); void updateIsEmptyMaterials(); void updateIsEmpty3D(); @@ -73,23 +70,20 @@ public: void loadBundles(); Q_INVOKABLE void applyToSelected(QmlDesigner::ContentLibraryMaterial *mat, bool add = false); - Q_INVOKABLE void addToProject(QmlDesigner::ContentLibraryMaterial *mat); - Q_INVOKABLE void removeFromProject(QmlDesigner::ContentLibraryMaterial *mat); + Q_INVOKABLE void addToProject(QObject *item); + Q_INVOKABLE void removeFromProject(QObject *item); Q_INVOKABLE void removeTexture(QmlDesigner::ContentLibraryTexture *tex); - Q_INVOKABLE void removeFromContentLib(QmlDesigner::ContentLibraryMaterial *mat); + Q_INVOKABLE void removeFromContentLib(QObject *item); signals: void isEmptyMaterialsChanged(); void isEmpty3DChanged(); void hasRequiredQuick3DImportChanged(); - void hasModelSelectionChanged(); void userMaterialsChanged(); void userTexturesChanged(); void user3DItemsChanged(); void userEffectsChanged(); - void applyToSelectedTriggered(QmlDesigner::ContentLibraryMaterial *mat, bool add = false); - void matBundleExistsChanged(); void bundle3DExistsChanged(); @@ -98,6 +92,8 @@ private: void load3DBundle(); void loadTextureBundle(); bool isValidIndex(int idx) const; + void removeMaterialFromContentLib(ContentLibraryMaterial *mat); + void remove3DFromContentLib(ContentLibraryItem *item); ContentLibraryWidget *m_widget = nullptr; QString m_searchText; @@ -120,7 +116,6 @@ private: bool m_isEmpty3D = true; bool m_matBundleExists = false; bool m_bundle3DExists = false; - bool m_hasModelSelection = false; int m_quick3dMajorVersion = -1; int m_quick3dMinorVersion = -1; diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp index b7e5b6775fa..5d7efdf2c93 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp @@ -306,8 +306,7 @@ void ContentLibraryView::selectedNodesChanged(const QList &selectedNo return node.metaInfo().isQtQuick3DModel(); }); - m_widget->materialsModel()->setHasModelSelection(!m_selectedModels.isEmpty()); - m_widget->userModel()->setHasModelSelection(!m_selectedModels.isEmpty()); + m_widget->setHasModelSelection(!m_selectedModels.isEmpty()); } void ContentLibraryView::customNotification(const AbstractView *view, @@ -565,7 +564,7 @@ QStringList ContentLibraryView::writeLibItemQml(const ModelNode &node, const QSt qmlString.prepend("import QtQuick\nimport QtQuick3D\n\n"); - QString itemType = QLatin1String(node.metaInfo().isQtQuick3DMaterial() ? "material" : "3d"); + QString itemType = QLatin1String(node.metaInfo().isQtQuick3DMaterial() ? "materials" : "3d"); auto qmlPath = Utils::FilePath::fromString(QLatin1String("%1/User/%2/%3") .arg(Paths::bundlesPathSetting(), itemType, qml)); auto result = qmlPath.writeFileContents(qmlString.toUtf8()); diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.cpp index 2b79dc023e7..4d09eed05fc 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.cpp @@ -231,7 +231,9 @@ void ContentLibraryWidget::updateImportedState(const QString &bundleId) else if (bundleId == compUtils.effectsBundleId()) m_effectsModel->updateImportedState(importedItems); else if (bundleId == compUtils.userMaterialsBundleId()) - m_userModel->updateImportedState(importedItems); + m_userModel->updateMaterialsImportedState(importedItems); + else if (bundleId == compUtils.user3DBundleId()) + m_userModel->update3DImportedState(importedItems); } ContentLibraryBundleImporter *ContentLibraryWidget::importer() const @@ -865,4 +867,18 @@ QPointer ContentLibraryWidget::userModel() const return m_userModel; } +bool ContentLibraryWidget::hasModelSelection() const +{ + return m_hasModelSelection; +} + +void ContentLibraryWidget::setHasModelSelection(bool b) +{ + if (b == m_hasModelSelection) + return; + + m_hasModelSelection = b; + emit hasModelSelectionChanged(); +} + } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.h index 7bcd403bb70..a617347f6f8 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.h +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.h @@ -41,6 +41,7 @@ class ContentLibraryWidget : public QFrame Q_PROPERTY(bool hasActive3DScene READ hasActive3DScene WRITE setHasActive3DScene NOTIFY hasActive3DSceneChanged) Q_PROPERTY(bool isQt6Project READ isQt6Project NOTIFY isQt6ProjectChanged) Q_PROPERTY(bool importerRunning READ importerRunning WRITE setImporterRunning NOTIFY importerRunningChanged) + Q_PROPERTY(bool hasModelSelection READ hasModelSelection NOTIFY hasModelSelectionChanged) // Needed for a workaround for a bug where after drag-n-dropping an item, the ScrollView scrolls to a random position Q_PROPERTY(bool isDragging MEMBER m_isDragging NOTIFY isDraggingChanged) @@ -68,7 +69,8 @@ public: bool importerRunning() const; void setImporterRunning(bool b); - Q_INVOKABLE void handleSearchFilterChanged(const QString &filterText); + bool hasModelSelection() const; + void setHasModelSelection(bool b); void setMaterialsModel(QPointer newMaterialsModel); void updateImportedState(const QString &bundleId); @@ -79,6 +81,7 @@ public: QPointer effectsModel() const; QPointer userModel() const; + Q_INVOKABLE void handleSearchFilterChanged(const QString &filterText); Q_INVOKABLE void startDragItem(QmlDesigner::ContentLibraryItem *item, const QPointF &mousePos); Q_INVOKABLE void startDragMaterial(QmlDesigner::ContentLibraryMaterial *mat, const QPointF &mousePos); Q_INVOKABLE void startDragTexture(QmlDesigner::ContentLibraryTexture *tex, const QPointF &mousePos); @@ -105,6 +108,7 @@ signals: void isDraggingChanged(); void isQt6ProjectChanged(); void importerRunningChanged(); + void hasModelSelectionChanged(); protected: bool eventFilter(QObject *obj, QEvent *event) override; @@ -148,6 +152,7 @@ private: bool m_isDragging = false; bool m_isQt6Project = false; bool m_importerRunning = false; + bool m_hasModelSelection = false; QString m_textureBundleUrl; QString m_bundlePath; }; From d1c86959748c389e34723b7e9a3fbf82f0f8ff38 Mon Sep 17 00:00:00 2001 From: Teea Poldsam Date: Mon, 6 May 2024 11:38:41 +0300 Subject: [PATCH 089/154] Doc: Update Designing Application Flows Fixes: QDS-11403 Change-Id: Iee8d9387244f9f7099119ece5124f976b0ebf00e Reviewed-by: Johanna Vanhatapio Reviewed-by: Qt CI Patch Build Bot --- .../images/studio-flow-view.png | Bin 58274 -> 0 bytes .../images/studio-flow-view.webp | Bin 0 -> 41390 bytes .../src/qtdesignstudio-app-flows.qdoc | 35 ++++++++++-------- 3 files changed, 19 insertions(+), 16 deletions(-) delete mode 100644 doc/qtdesignstudio/images/studio-flow-view.png create mode 100644 doc/qtdesignstudio/images/studio-flow-view.webp diff --git a/doc/qtdesignstudio/images/studio-flow-view.png b/doc/qtdesignstudio/images/studio-flow-view.png deleted file mode 100644 index 1eda708fcb4692558d31087df7a3ec0673370a8f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 58274 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 diff --git a/doc/qtdesignstudio/images/studio-flow-view.webp b/doc/qtdesignstudio/images/studio-flow-view.webp new file mode 100644 index 0000000000000000000000000000000000000000..21db99f04303d3dc3c5aeb91ee3a8ad2fd46cd24 GIT binary patch literal 41390 zcmWIYbaPv_kbxoG)hQq>z(QfxLIwtb-%N8EwGOaIFxk!ZnD4zzLs?1rPRKW6_A`%K&8|33a|{x|dA?(gOI!vBlEy}s-G zN4W+2Z`7*UfBpae|Jx6V|Fo}o|GfWCKidCa-P!+7_J6N?^ZVTY8($gz|9`vp5A*;3 zVgF~==lr|)>*~AZ&+eb7pY>n%r{t^mQ|D*bef*dGPyGA;+3_dsAAj6``CnGO#qZsJ z{6EA`s<*O__`mzR?~D7V_kaF>>Yry#)4$Ds{{LP7Lq9)%*ZO8qpo;%j|1bHU@dfn>_G{*Uu1)`c^#AH(*WcTJ`v10mHmEH@&Ar^pZmA}fB9eibK<-0ulxVWzpi7dUs|8{|Li~K+s*&~|6AWOf0zA# z`xi9^|DOE+ulhFihGX~vp$nToi>*!YJ$?4={oBWuyS7dYJM`=N#Daj*7N_uK0p&8& zv^uZN`6I+{!MP#h+0*-cYUQ797RDYP+eJ-nfXHM!Gn|n(q9*kL}e6p+C^jGZh zI$d{l=^1a=yBw*mwSH>)WzE+^>^Y&zhc50*QHtHa`_-P`v!-Wcw|cp{?#?-Lm5FD) zaR`Uep0obHX9~zah?li#P*h8?mpZ*rp=`^kmf~A}fu&ZgAB7)A*6A0%;;59HrqT4@ z%6SFniP;y_n&*8mc&a~ps^-x}b%~naK1}YP@Xh+>7Ovid(`>>H`*m$H{W;M{%uUv} zY*6=jsQ$I}Nv!cj4mPW{u$>*pd=*|4T~^;0`+a?dEniepT;Dyxzo(ks zFuG3U{=Qf3Lr&nt?A};K;rR|fH|S^2y}%;)HArFElBbJnjog>i7;~(d{#UHTBS)V_u&e1qjadPoqJ3Zef!xIUopFZ{3r@cV&ecb&C z{k1a|ookz44-F_AhV2uxN^acK zOjMe;_m3lYjM?e?D+)vUPRuHe-=F>{U&v$C49B~xK5mvi8pu2Mi_58>6Dtg7h`hQh zzO!(*fIF|(L9N|lH%Bn+k^=M|rf8D01tG+C`%QgL>ca6)+ zJsTl}z{`lof;mR~mV6AY6J|S1?>S>#Q{Q|3qNauX=^fuB7A6=)e^IXbH*N2Ip?UxF zCRcJC4x6vC_GW*Rygjd4)4eae^Xv;3zAOBD>g2cAwq1-_#~)f=^*1CgFSo+d{7WHS2Mf2aTkXD(>x`mQjaz!<(X0AzVud4TU%q?x+Zpa}=Ov7O zZ&2!%-l1B!Zmwj<%_+6F!;Vb~>%YJ#I)hbe-K8@YDIy2OSMAu$_f~BgGbqE=NODB9 zW-ISeH28c{4!t#In<`_p~S zvJZcRbuQZf>-nsQ6@L5uJJa{2=xJK4cTyL)^d``#H8g3ap3e5(Rc!e+YC$H8?WXrW zZIss6S8#u@kj;}*vatQYHin0f%kEbF(sNuj(Iejb=trSV(eX`}N_IC3h`7I0@6r3I z*ATi#^2n^j`>))*gf|yHTG1{4`qy}_{QGwTFV&sjV^=is598lm z7M3|j=c)~p0W8`cy4rz#)0y?Q@|bCMEsY{u`dcmF=Eshd_ZqfI*Y$_t5vrt%UQ zqrcmE7R3cx?~T}Zt#8@6I9+y!!0U^C?&4s0-EiT|<(ezSo`ws~M@!|N=I@xu^yk;( ziIzqVv6i$6MEp4}TaCJX-lkK2u=;Vb977MQ8DCV?oj1~MA`+uJP2aSc6zvM$Q z_N1Qputjs~Qor{r_AcC^m-@?b=8Nca!pL%qQ_iO5XfA?yh|MkA@@7;xd{EpB(v|Fm) zuqckDPV5<|w5u@xyY!c%bE@L+K=z%D9BpwsrCOFva+-6_VB)`JpN@V|eRLwS_l`$k zq9fDpB!(4FoJ{JTS#xa{P5C`#O}~8T)8?}(-e*iFUX1wHoSt=x@8z>~9dnMevxv+( zV$JP4bESJh=3i6uY5E;gt#>Ib-6|b4WkZC))B0)0tNIj9_#R5};g}KwvoGWs#A3p3kJG|&!=!@9)YZHzK$@#ROe{K=Tb$7=*cgf`W8i8j#)2!;pFxEXG{F`%dM^-*tsTI)NWhg z3$=qziyn461kBpe*kk2fVj}ru!jjcy;(LQsT(Uo(OZQTdnz$m}i_?96l|sks4TrB8 zaeMGcInUAzFWtQ$rsVsZAX~8ttNl~VrBp7IIoG!NsH#0v-TBV%$^FaH5eFlJ$DHyWR0`j?$mDjxo4IC@ zhWzbjhu?0_xI6KlKrpZ6lPl&sgBfm{#M|GQ#@(nEApQHC`_r|}FIgrXYuuJGiNQ+h zv#CR~#M=Ar&lfHbeH{O9mcV?${*Yhub&hi+>t1{MavkgcyMB!|j#oI&Oq_T+Wo5o) zFlVRGMy>gg6Sz_{TnZko&8ao7v zCKeMJy^PqIbK|StKV|uG-1jse_oq`ChxYoWI^SKMu*mcBr(Me1;yI@jt=q>f_<2>1 zxcBdpKC7w%E9uURcbq?YbSMAr=L|ip?RWXT$B}#8R#H`+!t2hM^G#tgXRVB!YCEy^ z=;l6d3BEo33+n})pLBJVJy2+rpU<*9q-?L;1`QL1kI&_v_wL^J@aQ$=EBkIf+Hhl4 z<{nY4()1NB-n|d!Rk(aARO2XX@;_y#zHZv$f3r_=>C|s}$M%hX z&&C@Yl&#K9HP5&t@_gYE%l-yRSf_tw;G%hR@5pWax{G@xj7~f_Y*2-Jw zL~eKJJ-Rg&Ty&Tp&nuIxKYYLZkgvd7p)?sWkBhe>lS|mb|5sd3Q-5B&MtV-q8QDME z&NPeH-VQCE(;}i`#C||S_51-*mbkc|Co|@$r`kpRIq1=^G=od_*X6S3F0AWylR@^;~jmtk@R%YPg3b1(NR`NutbkIrtk!z(x)e=$~AZ+K{VzVF9Bz6Z>ui8bu1 z!md-!ZvDi{C@}TY!}rhiY}Rc5`uOt|-hFYl?AKI!=UV8<#wGH2XH4$oQ{AUf=Vm*W^PMY2DYb~u13-050_~K@0~r#__K1$ zwEO%MR2uHIoquYWb+Pxv>ir-7ow|9h;pdCRM}CUN-0l|M!y>YCqx&C+Z8sL*J}rLz z#!69Tt1Sg9_9wmyW{^_8FyZ?xfj)tAw>~(9p4sCiI(M$2ZbFd7v_8-CpH(bnA9AjH z^2TMWh~VVQ@r5~typuRoRO(H)TDQlzPd!^a!TC~b{q(1Q3d5AnDYj}ns+i4PmsnNy z#HF;vpL5sr&z)!gRWP4fBff9;-QR&SUaTiVvr206&hEUwxADMs@qc$X5^APumGVbw z#Z2!#rQQ{1o-$9fO!;zC&!M~a3bD%#cdITr<@dgo)k8kUyCz)STw%l2HM>im-I<{` zL(fJ==3m+7(l?6lmPTqSUlPg@=#y;pdg|kNpW(>)bsfd~4}6(=wqO^-Q<FzR zpHE);NT<1a?emqYXa1cpy3%z#%}qz`LztbE=8;c64$wDGUNWP%-{US zKlbi_A2`qQxzD2Q;RkZ6y;QU|_53|$r+lU-the~|LNhP6Dm@L=tKV7vJ>yvLq0e^5 zs-r>8*FNl8_bT3XV$CE4CW8s$QddP(&M14lIO%!h+pB4L+iohIW%}D(y?S9`uk||F zjN5+>NwaSM!F1zf>z+b&F`q71yGYHf8paKRxie?YpV_rDNam{YAD14r#LW)o?^BNy zwMt*rv^{Ccu#356S@B=(pnWkCdp=d~UOwx?EA_IC|T{ey6`;B?EmpLf`Y#h3^0TITiX{b`efvu|4WFn+fBrX};(>$9Hr-l;je zc&8{g_&m4rJuYb1y{6W+%ZC5;{g4v7g=a#-9Z&YihCWxBn(SNnW9e?M$TcVC7&fW; zF}8ea@jM`G&-n_C6m*O-I8E5WO+XCtdI*Ghr@#KmK!ySo?K4--*Q?0VQa-?+G z`g;R^A4p9xG1u~Fi0)pHX}0=AZNr2}hsA6MbsKM3B`yGW(jK*5k)-ta-&5e0Yt2tI>tIJ-5bNPKRI?T@7TItUiv@0V0vhCM| z%?7)felOabYcp5Bd*aMUr*+fIER|f_xvor>+4ALz?hilq>0T%AEx#Wx=lN(#@>hci zYK@@Y_llDW@g?t;hxZ(*l6V|9jiKyz=3)7RyR=<) zQi0d?nOpSi#vKcd zYypc>Ax`NF;S8TO4Aw4sspF9$_jQ~8bp`O~nZWLKk*m+?e*6b2h%0U|Yo-0q>{F|L z8TCFcKz{O@+`9MfHw76cEtj7+_r%vvkEa!H%xDS9sYvdc_0iYo^2x}XPtQ*mclNKX zG_(+v7wV7b9^%n|^DAzq2{CS6 zc;%YSj(@q2e=ae2%AS5f@`By%U4k6Ea{8Ti`vhjX<{nOCiVrvLmi-ctROej(DZb^; z{sZ5?Jm=jKY3N_6<0O%$b9CafOvA*Y33?BN7BR2e$8_gpcP+=fNq(pN+A|L}m6^A` zOt09YduXZYjfGm9-)>&SDF1%qY1P!67n7t)=wF%r&v%w@P-%%6r_8X7xwQo|vf2 zSGd+;ovO=1VH@-Gyq(!^Ta({O@3Xt>bLiZgz2zE!VS&qR-6z&b?2(;{a3c789%O=&-j1#;vZh` zRQ`M4<`o>RG)?_KKjZj6#s;@Dy*H*bd{HX4e(aK{c`{Cl@oqEkZQiTvO|FaHYm@Vg zvs`AzGdIfoVDdD(-#6vzCj^wZ*0X=vp>AXzcmzw2B{(Ym=# zQHnRZ9mKe@^6djey{igbqvAz6T3(6n488j}KO@zD@4w$qpFQ%h&<_5yc!>mStm#bc zd}ra7{UMCsFEaMtHTW746?sW%>e|lc9e%5Y?95&y9)G)ap0d67PWRr>joY+-YdzhO z`Tdv93kY$Ef#L68=dBSsN90WZ>->~H8E%xhP?Py*qr;=cQa5Bz6thg7Df;4Tzy0&; zlT5YOb@Ry!mF6G$wc?`Qw?}J^U7m5ub!X$Q6HXgG8=onY|MB4Ul=yGm`U|!fxLi6~ zsIdKT-3p#{S7h#_I?u}ZE)vPt%$ayC*0TK2w;gAiVlQ59*s)33>vn7-lL-zqYxKNM$8(QH05 zZ%-)e?U*+lOTHJ}{CXjLwN<*(KUH&uI-OMu^;5EbtpCq4$uvfmaoO|t7q8uWY^8F; zpHE^+x|2!i*5ZUWjy$Klrzv#YdB14Ws=da4{3Et1{w`T9nzDLU-l{;2ncWL#HzaEA zJF2*_fk*33M*@H1pGkIy@4u?gdF-lb@nligfz$ehk-Ii3y|5Mkp=+mqhcV;v@&6K6 zu4VT6u1iwpb}H=pw?VGB{mVB)$GM{G=P!J|YR)D(-tzp7;x`X2mJF<%x?E3tmhqR_ z5+_r*jwuJ)7@A#|j=!N3ccuCIoxhVGS&KieG+gMc_~dQRCRIz-Lz4fF`F!Oy{j#ve zN;N=?U1-MR_@nDwP8g}NbYv7In9vFNH@HQDmp^mN%CyWKa!p#RqW$oB zSt~YhEc@SXA;g}m9ASRtyxIOzWiiwEt{;b=``wo8F}W#p;^SKn zmR8LzSGykf@38-5!uh}Rwc7Usx6}nBS*8ZFE@0p}s9;yjEyy!-_xXaj=UEKCTeTk9S_lWEGYtXy&|k7rHPE1&6JdD4B=35!$4r#seO{p|hYmRV1|_BOxi z|Lg9~GoRXEIA_L*#G|I`**l)K*oZ9<6Ro|^v`3mHWb2gOt5#cOFZjn@8D-Pn)!L zHB5ZxzV7Sc?!egdb*tAYbvEys@`Lfy49l4=f)Nh)^A5dv=e~tE##MdJX|JRs(yN}X ze3$;u;zjhzm5(d`TikzO`sGJ->X{Y!lUvU^Zr#3tH_N5u%J~MVZ8fY5$vfcFvgagJ~b%%iuK83sYjAM&4?Dy|UtbECb6X zqnL|JnxEYIedfrE-Tgsw4!l#D13xz~uw^IwC|?`!@$(DC45=sUHqJcoqL_V&tb6JA z)mBXm%$9Lg4!KfXb!Xcv6^~Dvo^f>1K8K7q7w$%ChE(f_G`u^e;IsdEbBf9eyHwGp ztDbvBUI_ZXI29`YF4yG#&kd)(&-lMsGbAHP=ItYa&8Gvj>Mkwe3UyJ_4N9~><+RGo zQd2ELWU7DizuS3}%kr{!3(SdmvYV@8kx1YzVc}BEH)Z?g+`gG8B6F5Y@W5rROGmm6 zOaB&~5b;L+nvzG97yH@YS@X8F>fh+Qm%x;>e?z2+;j<5-=kD=ohBNcLRl4AJX5-2F zm4S=)x`+n9Zg9U;6o2}$oyCgfol|vXUu{jhC+d=Pg|RK*{hEg>pWQTN8_x-^6EJA(0M|eB+B)e-)pv> zmwPGKp_r0%L%8P;+sbcSHrhN3+w8tq<%aSk137o@`k3nPiOvV6Wk_`Oeu|m*{R2at z_|>b6p8YLVU0`~kYSYJVo;_w^j6V$#X^BE7RAgxImrv(EmmUx?fKuxdC~tD|59%>DhB*5Y1$;h zz#^7rkZjtuq<%8{Z^tbilh40g$L2TtuiLTp(^WrD>$cAl46JA0FTfhKuX0Jaf9;>L zMIHaR8TnZi#GY~7(cdSR{%GrKpOEW5H|n>SE=|eVn)+#PQJ!=8k^f<Dc%x?CXi5UVjZahAh_Kr3JZX<}SYR z>sWDqWvJbjsBgW+>kTDO>z`e|l<_5B#aW%W`Ic&j8Qf0KdTE#WORMUO%CzFTX+8N3 z=eifU?77atskPt{o4nRM)0UU#ri*;vHJ7zkExUcE_g$Y~pO5Y8QLNLMyE*Q_pDyv; zE8aP5&wj4C{(wLCLy^_&jTRZMHOdZpo~&mXc`LXNKFU#jTD8>FthaAfq~Iw=W7nBm ztesnBww}~5Pr5zVQ2y=m*?r4aUw>*K^43CC=SxlSHSR*Lw+0VFBrh6c zWY}h1HZP;yP~vyY#xq|mGj6>6@Iv{zk6!P--}eML?3v~)tPr-jarwBye8&INi`5Ep zxgE?`2OWRwl~OM8_}1e!J1;)F=`8upC$LF`cSCRbH><0Wi)IGDK9mylYGQY1dbaZU z#HY*F6#hQN`?Mr~i?w(9>yAwd(%!bx83D_jT+&3HmuLC7ekhH#iP(83B3HR>Pcui^ zRrv=Kr_G+kTFkYuJv~!ablZE`$-%8xi_Nol-}!Dca}(2t8#j;4)11qaSbgK#;iT8T z-|kPyZBdPmSgFO)dvdDj+u+03bf17WKdBu zEPcIJu*KWv=AtiBItx_JE;ZzoJ-*=j=M#co4jjG3aLRG6MDPC^hJ*uNum9C?AAbG# z9^2Y;x3mAAn|Q%<%HE}MlM;8V4p`FMv{QEZ^lJuLJ1P|=t0%d5e67rw#Hcm#n)|C2 zC(aa~+W+37CeQoXz4;3lZ_B+N_H)t1E7CbSDiakG5?|f>zAo;?6^ruac10Io3%vMq zKG`C@)b^N)?sF*%J-_tUW^w*+2XAG}4tvk=UE=UsrfsIX4jtBtUVOA+lGyx1KN8$b zww#bTCy=$xlBX?sn}bMgfr=L2za8&%Z?iLAnb_f!Dt<@3%GPZ9Bn4NUZObBfy|Q;7 zZ0NtP8u_H*)U-vbPJi)KSS+J*vP*bU@AY%Wm-A{=Z>wC%`l=?iY@t7^XY86EPoFp} z4g39nr*dS`fz55czc#ohI%s|=m|pSPKx?O+mj3}Bz7>UQZ|ju=|4Dysxav;C|9z4z z5uzVhve&KMx#H^eXip!DjVpEk@a^#EQxw~A`a|w?t+224sY$O(XT|&KIkEmY7`9UD z@S`W%_ln}4+`JO5ayMn-Df@^;tXn?i{<<;e2J7R=Hi7YPY<4|lD`}BvJ=?t|Jo2^0 z%f0TeWM1#KF|w3@&}q4S+ti-rJB<@n;=WfKH2J=(jNEzli&@CFg~Eq+Sv=Wz!TQ+A zU&Zm=k{5idYmR?=`M+XsPx7?`MjpNlhq>nbRsDFdrknqw=<068*2d?%%5%5Mr@lx@ z{-k$gv%ZMD!oiQXT0dsSSl-~TI$YFI8Mb=D>1)454;e{!J++ymrf}TuJKGLb0p%R~ z*cW!9Mv|>R)_S_IJlC4&vzB+Iz39rS&jyLyduKG9YX1AYGTKK`-)%>Xnqgz=VmWqWtVb^-4tLKh-;J)Ci}+3Ee%FUQ*cb?@C*^vw8n|8dUG>FxPH zeH&&9TY4WhPG+w3y0QNIf@f-BG8JDHf+7S!!=KMA_^|Ns>z;`oT28s4|L2`|=+khW);w1x zOS8`{(R!EVyN_EFu1@>suYBQaopaImVDlz1@#E}sb3$G#syM8xx*)V$#Gu}5;htOj zuBY_XY>qVIVy^xDUnRpr()aJ#lXNc>H9g}t4s=i9U53{ z1ajY8_sdxlYqjg<;}`q(n>VQ3(pK1f_vqpk4zY)RPKs9l!6GJSzqjc7H1%^kvw7n# zonN>r@Ve`xUQf%7JnwdFV{qPme}<}LyPVytmYqTuZl9cCwn3(_==VptLf*W$b=_00 z8>Po@3)a8X-9Ih$XZ(%R*CvW)wOzBisjA(nc4k^5FNbcE)bf z-0@vcw+1XJ-P^)@oy$u6aDY$2W25VWS6*{JS1hu!)GzG4lhZi+rl+Ud(wS$EGX=J6 z&snvQrzCC7ho~j9)3;yv?%;Ij+x&frXR{|gyc;w)7T}hl1|`DF+w4y*X#gJszk0Ph!av*<13CNxhK@bCC_c{(a^H&K(=MzD&FqvS`87 z{A$a!DP1X-r<`SZp|MBBp3_!iYR7}u2kvaF+~L3Jg<*`>k&n|pJX5Qk|G{{%PS!(K z;oni4ee0MPK72l5m1oJ750f)W7Hsf-r`&%&b1h%Gk!bw9_nKF{cXBS+yNF9J-PMER zA&ZpAY4_ASX}P+mB1==bw5krjVtkcz-IAqmNufs>_bRr@d`u6sC7d%KeIzOEOG6jHT;ijTa+_ipL_Fi)`>ZmJ8$jV;3j?ANp1D7?RoP#vTiVN zZRUy2vFH;q32<2HvSOX4vhS83PSYkvCbD(DE0u9NAbMtjr|s3toU(zOlizRs{HDR! zE3aeC16ODE=PUK5EM!0Qf!TUc4`2QL$+ux!wX*Tt8jm z?jjkjQJ%W6F@E{~2w!I>WApes-5fjRY!>?}!ryuNkl5zC; zhuORMUhHsE49)rdFL_>`Xt|lK|IOIMh6>G}lJB|=HNICEyT?Z@Yr5*y!?nHg+L;$K z`O26-c80!hZ8a0Tet*y5+&%j9WQET0{Pmf8b82J%_MW12<?`YPD> zdQboVX(qCKOi#VPZs<164}U$`w_@+aNQRrLY|1PCO#T*>Hp{~-%_@HB!$rC5`NDEsazbS01ikjQjgZ9ax1rIFJ!y+H_%TB6I zS=#rJMcscQYdQBX@7R;8HgU-2vQDXEn00xUi`Q?7+?Lw(;1~Ckr~b`0Gg%Zfg;RBk zS!6?F-2@?5A3pKjmrD1Suh=`qYwM>)|5Xov(Wt44(sRBuYEc7!0co8Bc)5p^;i5?UM$~Sb2P*9PL*;| zpU@OpzWh&2#Tw5QSmp%HJ$|HH<=499>>bYES_9f=eLV77|4jIt6`SrJ->J_RP#&~O zZztcajPL)p32!=<$)e;lHM}nI<}}IJ45_z0e4o7@2}wO)uvFghq`{lIa}0r#UTZ6y z$y%S@v%ryU|GHFLpG8~_*Ar{A_H1-d(A}bT=z&`9p_7frd~Rb%{bkK+ z4lP>C89c*Hc5ziiESzn^bZ6ViQ_TKvmG?)QtL;i$bfWvo*`0mq+j<_XblzmQ`?kja z^}5Dw&l)sef3-U~Y2n?(ms_%qea)G<)WqcX6wZlj62B{O`qaH>7CmL?ut-m#`nItZ zpVMA{FWW`E+5-Al44HmTJ$CQ=k&w7Ist>Y%Ih!e_Xy*I8_Fk}NKWkILrPgZ`7Ymu2 z9AD;;^KiGHajhf&zou=+7hLT;av);M@yqH5@-?DdW#?*q-`tok6Zq`+(I9>4OSzGE zZm$b|dfZe_JwE=@O5aZ}Et_QI?-+>-cq@MsUbdG@*8KfMAEAcX&E~z{7L9p#HL|8_ zz4@%hd_-&Q-9xkqy&;IHzS(h~9;&h(Fs$Z`qT5@i>Ae8f4Dc>-2Ti8b5Kf?DjK3e5{*uT5t z+rHbApEYNdi@kX$UwO~ha)RsB+>d7*UT?i8@cMWDQyfOj37xaxs8aLTh?}x6FmG@@ErWmBVuutZkEt zPu;fXf8&n{Qx*#5aFkv#`hRHM{~RgVwU>DIF)(m$j9F=%Djk==@#y%MAN_e>edTMO z3;fKRvgO#D0FK}vo2T|LY^XN9vuE8$g~CHuzuvn1$Ugp;wR%tr&&6=H*=Wq z9%S0eosh^=ckj}T(-ONgcwfyq7%vch@*B^L1r6urXSQF>&iG?~O5lCjvSUrt=CYW1 zb$|GuC_Tqr(kHXuesRUkZ*!GSXtI|-P>cy;xAw_mkUo4+Afc-K_Ro3K7N%`ajn#k6 zz{q>4FKb;;zRlw#YqMR8-n57Ql-R!V{>KO_lixzOqOLOCczb*8oxNJpzwWqL>ou+o z-n**sisyqPsx#95ui4Aw-N@`*-FtWU0`J_f*V~eU&%QaHcl$?Lm{O!z{+8PRr!L94 zerq$zS@!z0CHt$d3~ZM-UR`gs!g2G??H>y}9$eMdw5p!kpW^@g?Hfm}r7VU4TdvM} z#}Vyt`A72f&(kLb%;_w$j-2m0ZM|uP$K_G%6;oeixhdz*5rg?t z*t5(E%O=$_g-_mlI&z)A0LO`+Jf%NMPgZ6}J~vuj#O$^CVs(rTi%+Gnn9b!2FMs~{ z{P!{Al$^L~mEx+5|LT9te(%|QAmPXzkp+z+a<`My`jkqq6{k6vpLottHF^26r+P=^ zN>c)V{uFq(X2IbT-#*$*h&!BcBqu{@QrbuIlC#GimTAi7huxZe`_JBo{7uJ9 z?AYrS(-;*Y`I3}iYU)p zSr-oMvJaQk{`%nm)Fs=kyFA@CsxMgyPn=Xc}L?V%SM|v zE|*aIgb#cAStSdKs=Yt()!lxY_b5H~;&j!ZWRHeK%^eoIr7E5=w+ai({##rtaix#`qS@-^qLNwfUj15Ove;ne{eSaj?c?MUU$;$kUsUV%Ltd{>mRL6wM(laQ()7e` z+8W!_`)18g5Iq(wbjq)-_mPOxkG$XI9El>oPEA&|GSc9(slQleKku&{hic1i&$3Bx zRd0E^UbUOQ{6}xox<1P}QK2`!3TMvRab}mN;v|zlPuE>rZ++_Eo-px$YpqYzUtM0w z|7**8#s6V*pV+^;yr+Zp%#qXk7QR@X8~D!e%9-beXA@_vHL7k4V=g^*NW<2nVq)FZ z`I7U`btbxM|9t(Y>d+P5?Jg1brdgOQ*lixcdZqL?XOh482hN&1DVJhh;(I@&-*77A z$!bbXHgueLq+x$o>Z9M+a!zXdpYiMeop(1{Bk@^}H1A)VsW%EPzfjy!^{8D>G5EqF zNwuKcJ-1H<^9p-D2~T7X5XkEaccFV;ZpM zS?+_bEuKa3{wki|c(30m$^7!?a`e_UhHNLB=PY|=6#DCI+H={ct0C)SH}HM*oOnTG zxxcHXhm>w$$`0Y3K2~;ItAl4O{kuQQcKN%6qG?Gbt4&YvrhG`bCvctjqPgd%C+kH2 z$_R^2iQT5C@KG->;i9CL;H&i|@8&CtdNzw~{Il|d2j~uCI1BFWz4g(bQ#Z^?&8zH}%dpWs`OHmOMUVuxPLKgVbY-S-XGX2TryX>ZIANBT*d>je!HhIi3=7xHN9($-MrId zm44u6#%o#(D+|s|y}o0n%Il@?OXvEQ?0efj%aL()u+^JSifb(=Z_7D&{OjS@lf2lM zzSnc_<~vcQw8yA2Ly?f-qA>nf-19{p=> zLE=05qr&stMC0TloFA~BTX&=-VCN<-)71qnhEJI5n3M}r)y~DMOqg!=xi4Ui*rIar z!{=m@l#fijvNhpG)bc&Nsi*2|W#1i4oHm~;VJnxS8S`r4!{wji44zib`)fX7pRvLE zrlQ-g*XADk@L_N3K0Y#V6fb=~=S5PI$IE#hcYFfYv+9|xi?nB5b4+W-_4}&!9GZ8x6iQr5?b3Lc zptI3Uzbaw`}$t-ww|AS=B)mo|6=aNH^cT-f0Ppcr?zh4imTQybSAz( zzv$z{uEb3hn=T7Fow}%^B4asen%-QQ35GQjpFFaE?)CT}i}r^ZN8;VT&-{69w!OBW zxyIesDR1)rJWHqx3wDo?&fh#YtFf|c%U;FiSHFW@cG_xv-{rG*mZRt7tpQi1yG~0STOXFEc29WI{_4%g zc3UgzX%rOx7R}J#KRf1`fm5N@)^8o}X9n@+NAS+t5-JRyHNy z*pl9NV$xj&`%gUQIE7q9L;R~azPLN__ul_dm~h!3`k=UR_4m@3&d~~XyV!4<@ zRhYLcPod(~nzjvUcf!6N{ORy1p;?tvjy>dl_JQ2bbxXb8O^cK$ousT@Tw>|G+PY=6 z_uNCFOV69nlo656D*52J?Z;#jYjy{2-MMUTf`H;vz7oGjOM1?8WAid*qCdKd!p5OhD_?zIMNbg3&hbuRmpYHreT0O=rjA-e(uii%W`e zn@n-&6LQQByS($u&Ohwl1xmSjH9l|Oh352LiL_hvWqX2z_>K}Oj>~^HT{PP)%-YB> zpvFTAm>L zzEu@^^9~+4G-+v8;FqK5$lid zso!$9DO;^m6xaRcefIQjOJ;M0roN1|GhXwbS3H*W#LPkK=L7lZ64hjBf6MFaFZGBjsf8FS7<5hK=#rGce-7egIc4|rp zTr0|3a!KQ|Lhz}*epV+N^d_H~&Qo@~cb^n1qwyUbg*Mh3g#`z;mFmZnG(Gv(eaiUE zV|&le)8V{y-#`2B+ZEqV(JbEj@x}5OJ*UszCb{qO~Uq4KF6sy{sAE{ETICeT}$U}Kk(efMm1Nu&Mx z^XKl_{wwZa`}FizUi(H%mbZJakt7av~jw1YvLd6vK0Uyg@uFWn<8gATELs8(wB zl>8EAWh}^EFzw%N-vC3|yMbQG0<*S!Eek1kU{n_GPCncJp+m6Q#`#;~4e9Xnjn%VV zO(GYl{AsP+s+aTn`-=XU|8lzDJ}%MVnEd(CI-PB0xeB_EmLB3-sl=kgU8MA9rq~tb z!&~hn`_D0^zNk3cV}HguR^C~0?otu^|->H$}V|@2XTlV4=kAAPZxn{e? z(jV(v_41`9ue>?%t@Yn4j-v(^aw*|*8#|?M-ZfWJI(NelsK$9~UwzVWa8%-y@Yc%|R_k}LU< z#V+b6PiIf=h5d89cB&l7TNiaicalKRqT^LZ`@jBKofh>=_@D8APuU|UL%teM-r!$7 z=f+VVuhkA!SH%vd1{;3}wQyj{*)f0h{MuN_&F8c9>Z7cCgYKU(yt3`+mMyMN`f8Tg z3O!rQu=And-sL|Poz9m{_gZf&rk9*|O4lqjQsBW7{qRoy#m(`j40rBe_o~`2$oN8= zQK#+r)%}mHzixhYH?*7YboKT;j%{03Ph)14W0HO7UX-9Z^{!`rPgR11Yii)x!^{3G zUv$vwQ2W!;{8@T@)22N5H|L+x{uMEKg3GS1XycytZ@=g7+Y_Vol|OcI3xxRR=7*&! zzB1-JIpdHC$J&zbRrmC^Pdw!iC%M_bTPrS$_0=`KdG{_&d@TFLO)1Mue45QZExqpL z5+=TdJkgg#_RM^B+QRvQpwfqv$DGT~7k%-MaP-`UxvMyttlhfy7bP5)Gv>H*?Yqt zKl=W99rr$tzw5ug_RJESpIN=>&D&3ZSk$?RvBd-@)T+|Sit=k`QLMEKmD z+=7M`Hj){KEd9G%lrp}3b2Q|<7${pZ{TDBP=kws$U5`&6ue5x8OXVTQybobsrx(l- z{abr(v2bZjY~__T(Pb~(w@*5Gb;ZIWLtpUt$R zME!d0@ptE$XS7Vc^tM`AG;&JH6W0S)H?xmyKJq*}-oJG9$?ORN|D}(a&3YK6H~-!p zp6eIq`TEQMdh)kO{lZ`ClQS)D*B{~&Hdf_gs|$Xuo7%)SlWE$_`OT3tWUYg^6Z5pz zS{vu==Q}z*GkJ#6wjJ-)yPvhO2hF+Lapu^KX!}p?cj_MAIA*lTZ2i>QpIpnh6r#Q) z<@e2+xH$dcd(WUuv_1Sa>hZd>k&3Vl6 z8cinaj&BpMdj8YpmT{|s$}!U&_18Jq-_Mve<-kIfvYwv6-a6x1R|L;73O>=U@;$ka zH~s2q{)LgRS2T!P8JMmrk<5K3DA>C8Wu)RP@87}-CKcRLS(C#`V!vNm^))nX+Xv34 znL64#Ugk%ZJ^Jp>+H~oYz|}|v`3~R4Wb^mNwY|1CZba={+0GD-y+1jn;Ur(s9Le?f7RNG9uh-DN<^4CsxY28MXHjVs&#Pq>$95O&lDTwl&&>$t z`!}?>7G0}fv`D@|{I^!M^p8C+nU+4Vx9Q~*I&%9@w(hekBkOrQJm#`VZZ&U%=N{O! zveraiAe~#jl3BP*RX6w2t7Fa&IJ{VY zJH7bq74|LdXo73c*%J+|6Bd2%taGhP;uD_syun}R_=<%WBa>nrm|WiXi)^{f(bx0M zTeAK{tzqHJ2>&kWRg;aEWrl`c%nGPEHNho0L{Y%2=wVxsx~E-YYEDh6^RfN+{;v7( zB6{Ntx&Kv;-#7KQ##~)o-)3*NEJk#FWD(h9s&f7=U3NDX09TTB6Y06{P z!~h?Kn-4z8?h{p0w=zviyxw;%R=%O~cEgs7HMYJRwg;>{yC|b{{*$|*F+4tYH~se} zs!pg{@+(`TV)jd0C4sruul!hNdiShCU~}-E8=i|=BP}ETbLH1IaM{QU8$Aul)YlFP z?8*Pv6_~iPEhPGF#2S6ioHMI3+-0xw!hCL-~R94;g%4`7b=JU3K?e_&IkAmFIUe!|D$TX&?KrU1$2^M~07j zo`m@=TXoZMwYFBdxqqd){q%Si4K`8!z;EmZi~ZQ`3s1L;*G`+BR_%1%e9dk(W5o?V zQyV-Z7H(q{?8}MYAuZjVaN?5x`bR&S43F|}|KqoC`9_WB?>WvN%5^MxJ~94I-D57r zJ1f;^Ma(T|KYppaRx!)xt8TTXrG~_I&Iu2dOCDS}H1X_>w8=qS=_bt{ZA*7%uU&J2 zuTjmJzg};dm-g;`5@xI8*JxX(KAmKmwygDL*U!oqv6dwh1Dv<{tYKEaloyuWw9%&J zqGGpOv3-qN-pbjos`d=&-kaT5pTA@0ocP84;o(`^!p-=bSLLU#%Fwy;t)d6M!e*GWa^J`uokA8Uk z+25z%tKZgab?VzPmEL z&Tl8P_%E$Rt|kJnX5PxW#{JK7UPz4imQ!g*JpPM{Cf_VS|Bxq#!R@@N=hB5W627zA z&gVZCsS|4YqcwHrhul)bYi*fqMj{_3bzi;W5StOQk?mNPcFpY_uJcNgb_UHgzdDuO ze;4{{(k)N>`LD_+9!Q+2!dTiU9^w9Nnp>~K4$I4x}cMazP|n2_PoEsV3WY`<6Kt$ns|e- zbk$vp%bcawW!x>kJta(`>*Fsordyh?)_v4u*i$%@=alZgCWbllQ}=F*)82hMvtZSa zDFrp5&b5{4(JE6to$FTDZccWn`+0o#^lxg5pPmdjskhg+Z=vJH179b}hgLSPzpJNb za=7Z4X{7bbtGwHazgex=t9yfuO}#mBi`ar=5;(uKzVynUjSm^JEGPC%3bmFxXMI7i>T`*?wL^GHnp~>*>?O5-y<8HP{PNi1 zbi?}ne!b$9snV;xW%U-WE;L#GEphXgx-5hGNfoNs_V4RE;HI_s;arhvD_8Afl{RIT z+ig@{zI0Wv@|UyAU$4=Bmh`cy(UIY}_M(jjx@S&Sie7PY+g$lTM73i}&#LQnH)bjR z4?VgjCY_zV@!rS9^&Ee7@7%b>-aY5Q^~mj0bob2gOsU?xt>f<9qgf%5et%8*-JdX@ zHZXb3t?82VW<$=i zeRSK2Jd%rb4c{Jzl@poqe4qcib@#dS~!;?HOvv-TxT{(Ux#8Ig5wbI_by^BmP zEPA_VmBf|HSs!iEA}X1;aj+bl9TgCp8Kp7vLayTEt0tmeJ9fXCQ7M`7*y8A!F8!TJ zXTn$P-jLYRw_4-RCQXYeTuZNtRtXnKl(ODj*RbOFoc{(NS-*FftiL`{-sXejlDj6y zG#ZjOyo`+a?EWx#*%S^=rlZO}0*B{qN_DTSws)EG_+I**xa-q1f>&lM@PC-GbX)G+ zo~riQtId19=dmfj6XVt@k;@86GC;oLaUbgdI;>+K>B2OZjzxFSgzvb=50`rCC*Z%K6FQ}J( z<_ptfeJNg5-MbC~oEK_JC+v(>T_l`QYT>hQvDLH}O|N-R9`D?|u4LPmS&9i7#$_BU z?tPxTq%ro_({0UpcjW}Wma}~@O-T}#J?Zjsrh!e)`T2W)pI~XVS3O=_P$DI66E<<> zq(qJ+i8TEs@1nWG->j|Ju{5yY%1rA|45>{zicZ?y`A77F8MQ;q1Y~{~Zuk55T<^MH z+B+?q`>A)9M8AKu_wJvWsVA~1zWT+1w?apmTrJA_svDT6wK`9UJSL#E*I%f|@Dr1w z#cQ!@x1C=usjoN}`sQY~^;vl#FR{OqTt8$u70x@bvUYKZ`fR?>88Szsw<-Br_*8O# zIece@O1@y+*U3A#ef#D9>to^*VaaPYB{}gIqm(2jwA`_h4rAJWVwqQ5VI{Y3?a}nV zhn`#$zOuqwIX(NPUhb^?RpLJ#x>i&$uM1w1yW_mpy4@8u`(poXaSq@J_MIN6l3>ra zU45eIU*TnO`}H-dbb21SCvJHWESaQjV^W^TwIclbUB=vgy-7CKh}Z#`GhfT|k{yLvku`}WMd$yK!%fwUn`mT0NJ-RNJTj%Jhm&$xOt^%gc zFCr2$j^%AyzJMpQeFf`kl}#-lL^Ra`-!n|#&d__a)zQ4R)}l7`wLMW}O4NHoEiinY!$pxtvSx&&$11JIivAv1ZGd;AeOnDKfwM%_@ptH{5 zRnxYg>&#h@%#*i0V$W)~#tOr<)!XEi>~|bHe%$%OfvK~_Pd=`Gw*0}G9p9B7`);Y6 zkyP}gs;lrxy}&uQgcB)`1ax9ctkwON?_)KN)c-%5Env0$BKD_F#-SpIO>TJbe%Qj! zB2~{Rym|hNzi(2S!xi$J@5I-Nn4k7r^-qB76_9XSI-eQ@{<|}Kvpiq=*iy@f$@#FcQ<)ck^WB70Rzbyu zesZmoAN$8?9bYy1X#9ecs=*J|NA3UhSngb{fa;kUtE`%4-KuF_*gEmh0@nqBJF=82 zejGYpzJCJip_fs6`h;Jtk@&7C-~Qokj_RDpLI2vf_NCbrM@Vj)^K8n-OU>qP3=NF7 z8+<=bzP-if`FalPDGrxE{LlM*1W_BgkKp=4rDT<`WRENAVG%vgH=da+^P8iwT$;i==szJ=U(>x-l-PhJ&Yx zfA_t~VaxrdtEzFnXseR)+gy>zucDA^^)WBIK;YHFe+%3M_3mC|vS-RXwR0XrvBbvi zpU=L;OIMtm_R-;aow)1l`H$AT?JI5ORq)X}Xj87=e(r;!$8O8>H7yHz*2b+pcc*9l z;(Cd17g#0t$(QVl$dBnzKYrp?b}!>!vju7TcNl&rG^ySySetgjJ4A1KN~}VVm+4&7 zg zmR>seM*iiewQ{^qn~!!Mn8&+gN$+j0)v>Ft`gSNu?q6>GpMS2?iv`cN2o^te+{hpz zDC{K8U$b@Pl(sppn)wqf>z3VT3NU9}ZI#r0=HahuQ^f;}r7!u8%KrK|PuQcE=~hI+ z!Z!jB+lxgW*LIsIJYKOPA^4t#-5my=-R}!FyLvGH6UnZ=wbAI^-(wU086VT|_#R{Y zwtnNqvIKp-iAVYwALnK%h#$LmrtIy_FF&{zY&mt%GBV=c<8IbPm&>@G9eJpDnV~<% z?n|=dua5K`q3g2GANMT}3JAORjz{)nUDHy>ug_AOPJMgX%BC>spyt{s@0RjfPI!AQ zL#oLroBJ5yRq2u%=dSTRg&Qp_bLq!KOZ|~7WeAsol9>1_1-47W70OQ zPiB+s?v$jw)V_M=(vz)h3+EKI)F$rP{U6Vs0MrTpIbcWTrnsWWx!wUa)%W|@4xYp^k0yk8uh9FGrFeew)huDNkMDOqV~|vI*L`qLRXSkRTK9&a4bwEY{W&_f z-sXhI$8SaEOQb_4-Mj9+J?Pb=RGaLoht^+>=hV1r9=kVVPT0A_)xJ{gi`Z9X&G8V* zt;nzId+Cs(o|ds7ZG*gPe#jGzz5BlCCvs1f>N~hE>43p)1?_EGRoqh*Qu(DXKQk%p zGJUGceu+~^BS}s#YTc(bK~3V{lcQ_9Z-1X5dHF)Ss6{|k9K-KV?tM9j_!mXRuJpPU zth0G#SmiM_?YY+8Tb;i2ny~0~rWf#Bv79&S*eaft{l4Wb0sB_pKf5I2lckC_54Y0m z*OJO{VM<@Ceuyr;SJEM~Z<~Jh+3NoP=lXN2_C+hgu~NoGn5u; zOFpqpjy@$E?7FV{rsBhw@8Z4&h^ZV5d?BB%XUirypTYK6)H0!Gw<_DiPS!j=)2ZOF zUSJ*ja>1bDq#X|)HpWc&|2*PD?cVo=Hfx)&-<>Mj6k9QQb4uDhSI(b*(#jJ8o?S?M z_K5TP`!4h9=l=dng74n===AHw?yP;g_V;u#Eo} zxzJx#!}9#bC9TV}l%Fmv_|@lC8yC4eglXcP>lbvJo=ME?S#>R@^Hu-lpI54lvS;)x z=KJ$U`w#cC2V60^Q`%lu?%;4xz4qa83FkaftKOKdxD8U5T3?IS9QbKuubGBbqFW`^a z+=4`P$NCFvTh6D>{wHI%Qlh1!;*!Ns<753XKtL}X36UzYp&D&u=$2mQ8v_T6&G z*WXS~eE)=8bDP-M=hENG9*6q-N#8!9`Ok4qXNpuspyEXDH#}cC?0#rpQCpar*dSqR z;Mc2MbBQ~3X3()v-}}x}gktWmU(z_I?~kcPW3PPDiVnXur@uVDe)y&T*8|&czhCx0 zdx?~EyPC=0Zf*#=XKvYxjXmPk7>rmCuG{w0Wv1Nnv#~cXf7$)R<>7(>2i0`v#YaGSDjd9yE&9?cTW2Rm#rH) z=EaD*KAF3vDt@ZM#%s@8;$#!T9mReqK07^$`R>ly8!fWg)=#+>uQlQLjHS^_&5X7; z_B0hu?_%C#ocw#1)rE}_CaP+tI_%|snYaEo^Z)|q1iY~v$!REM$rqMqGrjb6qr)wc$O{HdUhxG-Aj#Mt*a0F1^H@D?)h4<_(v0u=BDJGw~kGk z-SkVJ{qejfH(Do}2zLL{w%chP{77n>&!w*9jul%EtX$0e-gSHU^vFf;3V!mpo%B(@ z_wz4{h{3aK4=P)3?*7Nx!Ngps^7E6HOGVhq*fV=YA8h97{j2!Sz&m%k-PFtcWtGp4 zMWh!P>wZzVu+~-N)2zQ`I!>% zUmQ$-TP8ewIW=s}#4lk=&XM_+QDu31~ubHHdhpZ2Xo zU)7UueRAT<{xV_RWc34**Um+oXh*EjpOf&hYfInX7zVx&ljs7-m>8Ghe*_WV??W0X(?gILs)IqTmz zyoytty-n%j{Od9wJr~b&O)OTvdOfl)R*R}#m^|6|{O7ZUcZ6gOIPR_CeyF7uK)4a#QPT>!YlBN>jH#?^+krR2k)6ykp6fY<9Nwa~p3ym@nECdhq(n>=QS| z?e1lZ*ruAyF@5r2t$M?|CCUGoJ(702Zpr%Q_&xK%TgT%XpWCjx+?^cA9A8#gT=h@j z$(vu1^~H}myIKCd4C2Z;?c(C%Gt>5HqVJj+({4q3?a-_5ld|0Dx@CgJvDgps)g7`` zna?(bZM6`!x%F4NZjp!Rs@&va4QGyrXZ4xgicJL=sunlwI>P^>H)9<iSG;ZIt%b_$=JM8@KE2{HR~w({Ea-Xq%>AO;%(5SkPQLPvcdJb_%2H=kKBBy% z)imUCz$CVkzi01vBs#~YGrg@n-ZVkoXR6^m+lhuNO}2}bxV_sp_r9}p{27t07i#5e zww^QIp2MGG61=-0MsvT5%1@ue{n9o>|~FMcmfq+Kll1 zaeSMjZijxo`X^W~Z;kw~fHy6;)+^`rUc(+qwpg#U9Fa14z6?L6t_ z(H?^{YYuZA(QB=q!|o?DOVEd9F2nrx))k4x|L!q3YnZ9*s!(bl@`IJ^kFRUXv!dRCBC*aDS_rz=lhwUW)tgik-Z6x}dVsM$fmeD^6KGynjHa zT2g(N=){*Ym!`{vJhn6`?O$+8R>W=Rua%kqy6Xj1Wm-Z?tbDo}gf;*C>bAb)GP9<8 z%K`Vc@|PzoGaX!el%`}S9(>R1_D8QTYJb?8uY7A9ubnXYRdlKTf2ho-1-8nP$yb-l zMEvP-E@kyD`ToC>)x&X3iiM8czH=YmOw=r_d-dd|=7VdWS+ydyKF^C;#{FiMEBo6- z{-}da9mhVaY%Jh8!9Mxw^hr9=+};!%o+6M>Vv}XTLr((3aKx!)HXZM zWgEnE$Z6KYncE*N&|Q}ynzn!WZL5>*zn4#3v_s(KgjW(>H*>EfCmhiAezUFA&+^RA zkSo`(&9FMgq59?DcU$+gq;EpIjs6RERXs~Ry6;}T-Np4fCZ?>e-uilT*xs-t)z6fk z#3{CV^1G`3*e3Vrzw5aK$}P{o?K|=E)_+9@y%63du_@*=Lbfn!y@*yjfBV#>xwaA~ zQ)hqVuAYA*e$7QK!G(2OR9LnzSD*E`d4eG0v4q8w_Oba!Q|-P^nc=iwh6?a-m!9@?A8TuH7^NrmMBe;x-6vXv`9O2 zb=%CFBKIAZXtL_gKD4#3)8K8J^HqKal|@cYs^>~psS4leRrxMG{c!0H-L=!^_x1do zlWy{IW!uxaeDBK575*(0RGr;gy3$tBI5FtOzLw*^ZGO+w+|cQ;H;+8I8KDNA3UvfrxA_hXI_k8x<6&S4!VV?*;)?cSR$mqbJYbEUV*#QM?>Vhv*mL;tn)C`m{%xNU-+p?0Qea2iA|B(=nBym2899Bl z2%59yQ^nLMUZV`XoogLSXPS7axPIQ|Sv-NMnepU8k5#rit^aN-Dc1gQwNh|@s(lQj zcj@4%MHIXi%7nN$RoRC#FSE?86ow&2*?wtwiuGOxcVez{4(j1M1yfwu$ zRO^LTIB@JDdO_`H%YV(u*SLp;j*~GY8PxP;d z)4PM6|F2FKPSUz5l+u!_736lvbrGnp(48F{V===hWZ#TdcZ;q2+aDDNxh^XI%dTDe zxoKjB?751~7PFlW$gA^Ce*dZcyXWhl_pfJ668-G^>$6ZD<1eTE&lmi+@?ZGFy?34E zowo0%l1^o29hr95ROtPO*jHr+1_7F%=HH#CGVz9k(&ENo5!SrvaSTWQCZ|rn8Pgc( z-?nMT=OsrBt1>mX|L&<3u3hNY%K7N-%17V6>y$D)x6Iv{sPSd$KLH-zX^!iS&Y7{b zJv^{9IzLpzv(X-E2%9%Y=>k+kd$lJmKWK6%n0$?QdhJ zTI{prSQDqR4$0GtM15`Qj%`bLob$P@cKs(M#lSO&;7k?fs#(K{bcpq-;+&@WM7{wIlPE> z-J}zL|74f!sY;vd_ApvdFvCeSV?}IeQu;zpgO@A!YsKHNIls`TWZkkZWlP09%ieDI zbpNa=*M-Li=h$!av8;RapgnrxZSivrS1Yw!FU>gLc9ZGIx1{G6H>~1#wboWoeWS;r zOUEv(?0;?A_(o;%RAaYO9g<3F!UR3zTwdOzj#A^#sbhu`e5S8rBpcxVxF z=httBt!LxYdl@*&FE0~wj?zEh@VVfo^K;wihM`#N=E11<}lba=-f?<01?;^3^( z;?^?t41;xEHPHvGyWH3QI+^rHO|9ugm3=@n`+FaY6op)w#*CzoF+LHormK{~%nc7% zI(;Y!zI}DmvxZl(I#T|}C)-J{@G+NN&a7XKJesh-VHKC;htz_w7yk|_XE8B;{-Hi! z%zIbG+@_;>e6PYI#RHF=O3=T3*URv}NA>TV08`b;`_xvQ+g14TpVgKb|Lb>YfuF4BpALN6S>?rI@;cz?`OpF$hCzm>B8ovzF5_#$r&cYA9MgZ?xy7~fWy%YM|NBa8D998#Odd5zyJF_@T%qg6P4VIKOzFe^9;Uv z?q_NLkY_jFO{Qi0*M&be*QMRIh`7k1+ICO%qUro7$4#N%XZl>a{ip5Y*BQboJKl+0 z;%+mT>?#=}mbF)MQqHg4EuANXzS}V@yH?5c(kN`ZXyXd&CCbMXf8KrN_~_ohNngyl zlUd&J7TTG+ANNXJ@!IJ1^p3DNC#JB^ZzleaXH1yl-=dh`#vkCf_WqOwOog-7pWIU} z@l~z(kh4z1w$s7gGB+QDITj>-FFm>4%|mMU!6=cPRdZLLau#3Mx5Hhiq=M(Xd-=l| zHX^43WY68cyI))2&^3XLlO&=q?#}+cWwGQ}p_k{@C8r6-Gw0t32xGb*KjA_fLjwbI zFn@UApBW-^JGLgxuiE_LK;Dg-Ga}s6)`yBE6>PnEHCN}->f6f%7BX8`uGD2YQ!lms znZd-!IhniG1ciz#b4Mxuli*&fqV1t}o|$i& z!~UaQbF&L;lmYWfOP5W`ojR}799C$(&Imf1aC+K%^Cvkwn9m03w^hC{Y?;1aw&Ly! zfsC)$T@L#fd`-xflw*(*)4v*2sIMTY`~HW=RDQmv^V!Re+A!`~yFxSh;E94AbGRDc z&Wg3XP%9}=-{*E!Xj#aFI-^6G?pLDTCDb~aoH=H_N|0;UsmRq@4>cZqn|>`fYu@jr zZ^P!Le~E` zXKF1k`@CYueu2Ps*2|ToXPnVxzhv zM$LN9pSAyVUfjmEv@5k+sIMjCn4N04euH8_;1>N$){`DFsP`W}ydr02ZAI3ndBq-L zrTM4-x5*1Uv8+0__D*^666?qN70(@v*~Y)Sy)?W_=V!c&gI(->HBX7(eUHx@KN5Vd zWZo!mv}s#fJ5&FkOmo}YeZ}?aaytWO?|(j{Yk}RDX}@%Q7j1Yr*>v~qa@Mz5+1Ky2 zOx0>$vvqpj@fiP8OLfl49Sx#~}8@UPn&`8R*yd_FDNp4^)| zgy-hx31}8J2>GpNO%?QeaV)Gi_ zAu~*SI2ls2CU4{amvW$@HFV>NXQ%QGHZ0O?Wp0-Ll2KcKBJ5X+?#9>M425i)_bDlA z7Y9hPW<6c5sWw@ObKecsgP#iLZU2-Jm~))Vg3VEQ&8n3@+E`96-giIcl!Jxs9SQ46 ze_8#~8jtv{O9&VIc=?*pj;U-bp2tq**HlcBT>c^U@{Fwq56oj*aLatfHpU{h^1lsv zr>(@9J;Gj1PiN$;>JDdaS$Cx7l@xP@Y|7L(rt_{}`Ve~mkj=vfzXQtxkL&Nd^Um{7 z@8dIjf2X%b{F@NAcb(~ah0YBhzbL5mwD#La6v!=&{poYx>D_(11%dlc+?>ri|C_i% zW*qzaeixZ3lf*nUO>R9~{+?0ke?w!?i^hIFbKfn~R0HqXGlrec&b~6Qm(8qP#&~JL z+-r>?Arkew9?VvpDem`DX2xrV=a&MT%GQT(xVCTJ#S;0zAJhM71{UQWirf<1^m3cf zt)RpE4!Ux$ezU2pc#kg2vyD5&pR?XfbdEc+t9kCCU5kDiJiqW^#N3Yc4H+vpk+UalRdVUg9&7WE4l!}f8d#_m2DOA2H=Tj%&V^7U*5xTYVi?10) zEM4Mw?y~F+3C0CSyfbzr@M>OiESTebI{0_{k7MG(_L4S62YyI>Umf*J4y)t&?2sVWFqj z&3tuXWMi~hj-=17hCLq{n>BA;n{IkQAmQd|R*t@i^T$H^sw?_iN>9$(U)mYF(|Nwf zM*TD5hD>5T2N+K4NpA{jnX%(Zp6+*}cl)&KstwyuGVT9l(Ztnt{+7&iKb^$3)HI1O z#S-m153CBFTdiafYr_;QP;hcNYujc=ug&W=WN*?p ziZaNsy)CKp{p!cJ37V-@7DBU{+~ntF`zK`tPks`5O>e@|jegGs9K$@dXZl?!$X?&Z zvg*_JhmThp*iM>$r~XFVk)Pc*+%;ON3C=tltppfC_i=xI)=*ttQuAQd&!i-Li+ zpQY+OZC^~ca4g#1ywcr6R=&S`vT4w)6Nfo^?5-SGw|D2~uXf+77#{Rrh%J;cWMGu| z(Kd69cf9SI z9Dd(AnwZ(5VcUGYBJtbY?0|P$P93zaV7fE6Dm!y~wgjWryEPi}{Yf4_4LY_zJ#D}H zyOi^f*XQEZej7h=Iw^8mfRmlw^!b~piSq&{mM<^FRheocHBxt~ce*k#)?<1gkvl&8!z-8Hk*Ja}egg9@kC@fnVy0_wF7 zTu(AE2xmH1gcNG4YkkaHJ0m$q;EUkaS9i7_V%fW8)v>Sp_6FBa^3~WT{j}|}cB4%6 z-nuBpeb-B0Mf{xnc2VrFdRB|Cwd9)bv<5M%kWhBW&5N1 z(TUqH_|_k&UvRU+bl#SjkdPU=1qvSDtwULC@)cToOS}`6mq^DikkUEvVI{|^^h3@T zowHxci)+3syYWR$O65d~5y$yme3Mi+TzZhbLcQ6o)-%DHF`e69*10o8&gY=Qmcn%! z`~Pl;Q@xhB`ln>lm8k3IKC}7$luG&AxOf69{sWUT7M_{$ob%1I3Ht5j`Wl~2 zB)ETfH$LfZn7WQVTWsxK!Zi24 zx=SCPzoQcF?HTvPjO(*B%lxnM!M%$&#aBn2u=N&rwpr_Wd-}u;JVw8^-)i7zh}?fV zcy4v=B=OyK$)BC`CSU*X-Fnhbm8JFX7nSM#bkdtw zZhyc0+UBbVBl}$Am*hoUUhp?RFlepY5zox8st$qFHyoHi%3q zsx+Q@So4gW@y74kQu3OQ-Ul)A^sJrPud@55#{DVTS{?>3TPJVUxMUDq=(nSeHKS2$ zqT%CEhm#W&c~TigGm>g+R-R4E`<5#9y-MI_@qaCe9C?%vb&IT?!>@e z?kOtyo6PTZ_lhsiJ;dN4xGSON4Ci#)byrpw*VQcN2+TgT^wEu%>;VtDe_s-ap6_yX z&w|+RQNkRmKX?zRtru{7yqtZ%uGaBCn~JZ8m&Cd(b506Pi;4RA>H5Ua!Q~0sZA+FO z;mkO+v?uA$#jh@lrgL#Vwh|6Bp1GBIUFuxU75hFSjCB9Vb|K_dDqMGVw-pE}R=->~!YJWX%^!Wu-lre_ao$ z=u4g`R&m?GRs7F~NjswdPU;qWxapv?#i~mu%v$Hig^I{Cd&xeVcf^zRUgVi2+8#Rl z-<7rbDeU|9RrAM^8Jq>Ok~%XNt$)?7bJe5lTYIDKr9T1^E1ScQzGhB*cHrM%+n9_v z!ygSF=KkV6CmSotbUXX+@%YcTjZ>a-O5ccg%T9^9eD>h62WM^O^u&tkJ1=1LlskKH zm1bhc+GJ_A^^*3py43i-o_?RUBzo8Ox@7%@3ny&7w}z!!lWqPvdp9QQXWX1FZZqW# zD$?|loz|BY#>Kd374pHX$w zlxfon93L+_&pfkL>w$M^>}>6pyx7YoE6dpqYxfnIOx?BjvzvR*k@h#b&ljA3bi(0# z443~D_6aX4y&nJgY5$@1o%p&51qUosnYAZ8tt+089294L(5ATO*KrQL*H7B^c1o|V zKkBkrVo&$h>K|;IJ=B-w&Hdxgv#9T5dxOyuuGKbSbIf^T6PC|=eC6xqy2LEDlk;DD zyM*^JYenzsiM1>{8nS==)Y59jN#{y6ie&zO@lF5wJ>oz?ZO4J!eJn@bad-B8f8rFi zAl*bP;BUanNkZ)h3JU)nij489Xg=OG{bT4)?I)KTKE0C+zN%uik*{R)-V4W<6<2lh z=wItJdOz*7ZNK#VuY4hwXU6K4%FfoQdi-T!?cOJErUy<}ELr69ecjVuf3u)O=__lt zuuSq^h$^C(bvF*Cl^2Xyt8$yU1}6Bw^_&R<0%FV49sc<7x5P?c zzJAk*eVfT^ekDAL>1g#WIn##U`iYId^>ow@Uf?>;WbnjUbZgZ81-EzZE8O#Nb9{2S z!`s8_&*~=H>Ghwp7D^8ckLF2@_@=x~DAe|i{ghR=<4$qK-&p!}6?65U=Pkx|tMYFe zo_fOi{sqfx>4|b$1${9yBU(2+mk?1rnP9%ALTo{V^h=3NlGQi%-s5OtDu2Fk_m4M* z=KE3u+1?sC%?>zS@nuem<7*4fr<-SNiJ9)Oc;agrhyIs0-9q>^rpPtr$bIB)%q>6g zXx^zQ|1KrZ)Zfr2RsR3=x4g+yZkQ>#r_GfRemy~b>XI#Wt7l&?b67sL>d-`|SjG2w zGn`s@-agw9+3uar|9g$^C#G3Cj0JDr?wq2weaYjC3y*v4c0KA+ndq=KOML&fwuzyA z2i)T~f0*&j`KA=lVGtdmMz68Ek7=f{GX0v5BT&v0YiRk{6TnpxYz z%w>Uw9Kjm|Z+Hex&inJ@G}CvDwY*Ic53D(^xtg->(|G>v*{4?q_H#s|nRZKfZn>ww zV0V#1q1>~VS(TUA)pfa_bp7HLK&%wWh~=f8}$j zzPAirZ2MP)HFxopGYJ8Im4EEmQsm!z^Z1{+*~j#jbvdwd;Ic~d;Gfb%dPg>F z5-xR9dS!d++mo%k`Mb4NZBA8jS@$aS@Wy-0D;~`Ex6PdXUvHL|UM=HqO&QPLDDByn zf{Y2iH-#m>2sZX7zgoXWE&av8TfsX{Y*1eCp*rjA+MwG*p z*QRw^ZtYPN*yqkIwf$yg#dMxGTJ2i5#iryd{#L45Q^OkmJ}vV~*^etd zmXj?o>zIejz#=0l(_Wyq*!T4#(ooct3cgh8&)~-CJh0gQ&Ox9_s zh`g`)_N>+!@5nlt1`Px&tj{WjmZTMRtA)%*wBXilsiz^x5=q->trSR>< z`!fHFkzR#9{8ML|ANc8*yxFsZGcUaN$bRN`{}-Koz#x3Y=ZODek^9d-@jP``zjC&r zaNtEm#>UxtvrV5nWnbFG>t?e1E-ZBn*SyZ2@9Xb9WqyHKlKRE2 z{LeqxiWX0Pm3F{Fw2!~O*LcHYSr+wGkFT6PxZVDuV|UX6JtZ~m$i?S+>-TRjx-4Ph zr?GIF?(~c^k&gT`I=>{%{`1dq&Wb0$W^UjUiH=+A@jCI{)33R^@}CuI_h$qxSUu%+ z^Y&#cWttObL>%OmKBQ=VElT^${m0i+jG3lv6n$Zvaj&uX!HL{bjYhV0uJ5a(R-Ie1 z_?4`(O80|{YTG8dSvxWqv{FqcbL1*QUEw+5b4DA;PrQwmoO;)H2tlkI#={Z;a^kF?lnJnrV7 z{3hv)hfCMjr$sNV>>ba15Im-H$6Z=y*R~fkXWLf0HW+TcnOSL@(s;GO@jUCC&AED2 z&4yC`v6r}|PsB9-{VTOl;nDoCSoJAq1H-2+Hy3=<+Hzv$9!AkHfw*IJeZ4p0%k*o$ z7j=65+pOTZ%6^{rm0te;UD^xZ3tT>Wnr({Ap@)?&D-Y=Uwi_h>z0EAI&vLJ2d;OG` zMfM@h_m=0iv-Erv`M>G^g6mH#u0Kgy|J_aS@2sHuNs|q>ybu<78MLVXsewCZ&r|*? zh1lZaxgD|z9Dg0Td2M!j2ax}hELgK0|7v-=SIOw_RKne&FAdRnM0Q+a)VS+*#5c?6YZx>XFag>d*FH$V@lmJ^xx# zCza>=l8a6eLs_1^>o~xAP$W;B7*B3 zGA~TjdHh*^>A{(tf;``hZTesDYrC*Ue!tnLuCKdSUV8RqS*Gzs;^I*W#eMM1RF`b+LCD-W6wT zPwM(FJHKuDrVE$OR$9!B7tP3=zux)MO+OI>gHJ7aLJ7A5_osQ?a`=;9{nwoPV#^KB zV`{k;>L2+zRY7fq1F{!uH=TW6vpUQ>we`~{Go}NqX0IOA98Fa_qjBuV9E}8D^{k4` zGc4C;Jv9y1$y{E(?*Fg+8;id!Y)luNK4n$nyh_ogqcVJFT6yp6YX5QV?v2}Ki_7jM zSJ{}xTw4Bl!z3x@BQaekoXWp=?${PRyZY|+oChkCE(sa%FJRV=ik95q9v!JNeTjom z(a9U4w{NW9z3<2D#s}_(r;5AJ`S8zo`#X1I_=WhXXW z=}mp9zsv<$lV@%3=oUJg{()iZ?isCnB3OL-izIw6zMH@F;mXpT(wcK^xM#ZE)wvsF zXYw?6`DV^z#wJFRhi^Do1bV; zZa(fby?xCWr}rnKEYB?VnLK4d(gU;h+NouB6AP1X(&H;lU72fM@!bx%)}+aLHi)%&op-~mC0!xB!F4EH-69m)*O&v>}Pz5QnPH-;Gj z=Zzn&FVIWlo;i={+0VmnU4@uEA!NAJr5{Wml2Fbi8>Q$CVtomEp+()O#e?z@Tk->!uQ$71*C_TAlN zbt3wWPR@o(>sdZO9?zStBl6})yR=&R{3naInYpyw0__4fTD!3){gvXphcQmKu9&bs zj6LA|q_vFwC9})Nt3D#!E2fCOe3Lf2xqHip2*J3=T6;onvu*5%weIQqz*2a92LI0} zZ?XR;>zbC^Rc)M;))g#zL_$rxTe*ZYlr7f%aI4B5i9km)#=gy#we$Ui|Pj6PuP?lKo zwp(C+a&wD?spS%}=z0GqbZ(sOA$^KFai(%R&n}jKiW4(aLUpZQE!4e!li6C2C-d*h zn6lJM_k+C{ymcH!IM=;+C6d3QBx>#o6-oJRw^z#N=6hy-jXu*+(z$KcQ%2hx-zG>X z>ir46|AA5Vx{_1ZdzP+)AFPV*KHhzH@NWy%{Gqlb(Ouf`TIvsN zyEoGl=1On+RK3JbWsVEi`R&=OY*gRRy;POIH0}_SqxNq8%b)+muHBa&|CZ@-^^~R8 zx`S;r5*XwT?i9Jkcyx8qs*oGp^P0HszW=%Tc!O^Jx+jkrC$63-=z4g0o0^BAAP@hfS?+hbS4Ga1cJ0gRYZdZN?u+DhS$-(LJ@`&^ z@vVkl+n%3%?_6x=huHU@p7v>B>P4M5`i<|@%#LU?XJ6kly^{UG-R?-{+Ve+#PW-;$ zSDT{X^Q{!nRv~j$}gq7Lg2vUDx{pd6u8%z42;scUZ@!XR*F( zU(0atKJ9rXy6^6@h)V7n-DP(|rKRN4O};Ut{d~i?uwxdRbemo73XL;y-&)Z~YvK&ivoCTF7M)`KFuz(Y)_UW|z~+eC+Dlj)10T7Syphj)`4=t-Mos_88_D!()!b)~q|?sr?2{Cen8@r^ z*8SP}#q`KIaqVZOb{ggud!9bF*FN_g&#UO=Ie&eNW*ys=(RcHk&rbbaf;V-KY|~r0 zGFxfp+|5_px2`iN{$a}9-?VsJ>bA=n8~<3>NLrNGxY%ce1=;6(FOV?)$QOLB&NeJn z_kJo%%l;-k$GIZ&6u4FwE12D>>&%W@nOB(f)O1~Uhi%B8)UO?7iw}r>e)&K8gws1y z`}Z$2X3R>SV!?)U3%X0a?Xn%)oV{m9MlXr{oQ=` zxs8?I{~v0b$8})Q%%!}QZaYr|G*=!M%i_H>-*|tfR7J$J1&p6BeY0O;k+AIh?A=>0 zJkh!u_^dM~N=#jXv2fuA(Vvqamzi2TsGGcgmO_MRQ;5?4tx`(A`W^+koZH>*>Qd8k z|6k(AFKgGONLwDxp727t@YJcD_hQV0Cg_|O+IMj2Iq$cur#6_ztXcEpob-#_j-Y9( zN{44uR?N+?X)ONB{2;@ovX=XZwx7o9+f`LkO6-Sx?poY2UOXw$^2X6M;%X;mJ)Y6m za;}}vH<|s=jZDGf<`RyXn;)Iyxuo}NrTLlK`xBqbyq!DUDrt$@&EMT6T9SrYQqOV@ z`ve?OY;*X;aL-XeY04*Wrs|yip1*x!tY3#@U+w<>9w>dC9_i#yrsdtvYS{vVsCGEU2|ZRuU^ zG_k8(C$)FVJF8s2M~Ad^U9YNc>zVrN-13KWe)iArck;a5^7VU0 zl7IC!=O#f%von9YOu8yn{vA(cV|X!FoQtnHExKPs<-~Rj*I$ z#A8Yi=6|nM+xtD6bKAOu*Qf9LEB!9KnDw5))qQu~ZwTs9T%jVIdEf4Aw^85j8!VeA z>g&yBP+M@?#N&|xo0H7%O*cMPu54PkX;)?asx=O364&+T=U;!zy5hCq{WJaz;&oFz z4Obq_xD`1kTQUrlH2@`Uv2$d-<-`}?>aFzr3Ic=PL~GSyXc+GpDzyt#9a+15*;X5MYr zcep8@pIz?Ed@=0sN1^56&U-Y&_SrT#bMLs%)m?mE%q;rWg|~4|tGt@eOjI|X@-c?V zA$WG|!qu^Cjb9E=+>{>hw{Aw?<<8eDR~(f0JM(2yt5!q#`|tPm)*U+eF6PSCj|bQ~ zWotIvecT}4|F1Ph@k$zF{;lKwM`nF@%~59hr0`byltd-pW30-Juyf$ zRP6dcwS|XrY_|M7sVNsSYBuCi6x|M7lP z%P$d|hKsdZI9GfK^mVVbcyYJ;jEwWWh*=^<*IxxySst>HxW~(1dUw+d^%W979OYiH zu9&bYM08q&ON;m(^TO7huk4P@pI>vSKgWTgJ^RI@yPcd>d){68D}JfR{4SeN#`b7^-=N(!l{nY-+HTR8Ao=p#oJXVpnVX@@a z0}d0i9aA3c`@8K&=S{I4+xz}bez54{-$fd=f!|%tmTEQb+nL9_Vg3WZl$94}Ey)Y3 zId-71@6g8?>jR3@XC}oqoeysUtQ24f{ z_1k5EKNmGEwSR>x+({GPs6gCTZ1EsZUpLiq=*1?>&5=VB6XHCxL1VJKb-Z-nVQ`ef()nYD(>4 zl{vW&J>4G&^oAz;rYmMd*EX5!d4lxQU!Q*1`WwdndTD$=JF=R~Al&S9 zb@o)B)clD)OOht+U3#RsFs^241NX(mz&W-B@nP$3%<;Z#Sp54P2T#+Ag=7OUN^vFBwMbOJZLw?XtH=LGFurTMVWw-qkW;c|s#kWnka# zUTv1voi<%Olbv#BdK8u=GPgSH*JMdO#E}`ym~zeeOTbPZj*slx-z{~yST*eKbY4@5 zS=tq8pubqW&6fM?oR6ZBqWmGk38M0A)Q`=Rx4F~X_fK(RaZtv>drWWn4W}Z~*ADR2bv)l#`1D2P^%-sMipAAsb)vS9`TKNl?T8bZedQmQ z)*1UJYFAH+**;tR==b8sPAvuvS$^tH*|&_J*rya}^j6k07pTR`?0+-U{Y{`o-$e#J zxkIN?M1;Af$xV63da2IAdF!M4mF-Vf@$7u9U*I|IkMWD<<2&ll?&sm2<0{;>%5U!C zWmgLpvlT`=+dI6QcGE(Y(VQ*6%5J~U2EpeNfxSJ7kHnm>w_W-ErR1+@XGYIg@7$Z` z);%e)lRG#q&*2F7O5WKT3oqRg3je=2^Mi=?1DoCxGtCo^w^h%6etO;o!+js$Co}C` z^5XW&nk9Qqy^nf!`p1^Tsgcq;>>`qnXVph%PN{ZhIDTz@^wrffBi1-I&uUq+`BZEv zv($>*$b%J8QfK4lD0=X!73$1=_vllO{$ug9)ECXy!sq$MyS7(OV_3*2c3^kP@05R=vv#+e+nfC|nty$wy06N+h6`oqb_Tn@d3xQz zFfqNAbKh;*J3D*rrIxEn>+rA$fw443KD$s(u_^YkQ zFSXy&T(fqeD_bYL;&wYViCEhnY z0?$8ne8;QpqIR-F;m{-5plP0uCOThcozPtq;2l+%7HH+Lu6%L*U6HKf;vWS+W^D_M zEa6_&B3_|;L~qmbV~zZgJHGGKHH_Pp%fPqtYr&4ajpko^%^50qY%abKfA<57`Gmp$QGU^KNSRa|wpX()?ki)>pp!?z0PS0Goi(yIc6BWI7 zpWD?Cv0&?3i3`$gCHp4%d^&2{%#ta0Ctfq*-}Va*Dc@f2Q`~NMM#i{Qxp~qZqi?*) zB8SV^N)yh$^X-Tfc|GmIf~_b0a(_Nr{p&=4&zp$MJ{iArQ>t$_vTV@%I^omCt3~g& zY*Ng&e{^;AuGcZK1{vFyXIBKA^0)3i$J=-LSh8au<5rQ(>+Ba6?rmN)`PQzPhwRM@ zCoh#e{(e?x&ILxl5UFqL(*kp4&EAGGe@uF2UH0Zz4XcUS!;G8WX$=Pc7bB)QhNm&l zHZrr3^mPh7yW{r51p&gghYoQh#KdmsFRd({xfnFqRhi3UxUi_D_l<#-Vb5O|<2zcD z=AQX>amvEd567i%e&jRrId6aBeIs~PWQP5$P0>2~(;-<|HcUFb> z%$!$0sUg*4u}GlEp;?#utJ|z|kKGr!c4T2)kL;ZZ>neHsn_Xtza+Tt7U9H{__C&;l zn|r3&i#PAmxAi0&YQ!&QFIUmt_`xkPfOSv2b9wLiCbicO`uTo6+hQ5DRYP9S%KgtJ z#`lYhMO@-nUp?mP$Dn=tv{Jvxf2YzW=6> zEqBwhtLs#aR4S4UHgs29Ir-Q1Xv6c(dNySpkDPrc@AS-4VpA4qI#!m{GuQIRO0n)H zt*ASf4l^jfN?y9Xy8o+x&N0W|k9A%&-Y;zu=AF{m!2a^Q&&1>UTaK@(n|VH~W5%46 zCDUr&1SXzh(e2Yd$=>c5`uAROS+QpRZ397$+57po7>Mt`&>n4Y`4h8ZQ&#Z#^~`EZ z8!ukC=U7*C_?(6vwb_wX!BH#pP4nQNl4VsP!bbm&oZy+~ z#Qf_*?+TW}>xJzH|7&}S)#W|SWL1eut>L>}x%_Kr*tV%MUw7-~SUlgqQ8wzya@IW1 z1pRY`qtjo!2AxATSIzd0(5sElS2nsWwWoI?dzF$&y z?kvVV>1+SY5{XKhCEqOVmL#<1_4j;R58Hxx{}~BUrcO;VCE~h!`xv4hyt?IeYD)E; zS(Abb;(d#b6`nHGbGIy=ckarM>l5XVngnqy-FeD#VNh^Ee6ZcR!nMzSs|7LoD8KQ0 za;na41xU_Dz3RaWN7t*m`^`7x-@4h{cVLR}xg&2cZR~im`{tA=p#tei-%fO|N{dNL z-`2B~dwpxy#~GIYf5ufk?>x(B@y7RWRsY_Jkq;N|<_LA#x9koHgIEKU{sIhFkD{(^DPAaTn+FUPyd#>ge9bvxPk})t4IfmmHL0 z*D{_yfA0&ant5B-Ex0OHuvXd1#o@V{RY@LO=^4#}Mk(I|E zxBf18t@dyM!wG+(y698)v$i(e^FIAiui$6h&50q&DN@g8uU;>8a$y)l!TFtMxD zi{x)ow`*DcV+KRui8xLfZ~k4IuKI+;Y*JV{A);hXfqiIr!EF5<`8Gu%X5V$5R$Pfb zd!2(p?43!R>&tlu^>;tl7oK@qbk3-x7Xo8-4QT1^jCThV#4cEdk5_rz*(VZ{kM z#nvv+RZ$RP?M_vgpmK%VYm@Fu!(){*yx(YFUy{J1vGCi{o}782mtN1FaUkIB&xKBW z7n)4o{-`O~zrgxg>7A7Sy;js)FIM zj(2Ty#l|fbZj1ja7jtg3S#ei8X@c9i)&IE?HfWSg-F3dBa8p|4zW|;3Q7Qd@HaF!1`c<_QuRStf z`t|nR59PMsdzi>#pm%uBVXk*&r;dG>wYX#NA*FuotNiSpQI1w&+yB2f%bdUW&?Wyn z*XIXk@0t`nLH>$2=thA54)ek$-Fc^}Ds}LP>_?jmRvYK9Xsd3GI;ZhfA=dJOfy(!S zgm>OwM76l?=`Xl)Bz4KluePUWH+SzWabvUXn^P`h|8DEM2m3d!oNp>_;^~xe?DEXB zQ|{K?d2(R0@2=`JHqF*YFMUO)eB_iZF8Z>ualsUmR+)bxau1qZGf#zG`t3f`J>baY zZ>JSwgrX~MtLi*_^K{DPTdH9$?ym3TrY=mn(fCH}-b)1uj?HIzPn=`$y&0IWq58)n z?oao{R3Emlk*S$C?Sid`u1#GqYu1fb6K=g~R%|KtPU1gNk@wxZ<8+(EnS){-)^8oT zw&qAYW&F5o@!pk5vE{E@@6BZXzr_2L>CgMi^V%k)yVdUJY+mCUvC?(D96Mu{wZZmm zrTbd)mm=PsIFoN`QF;Ev?%Q=-_gNlJI&K$YcB-z&`cO+G#}XeOOwUYt8(e$fQ`W@Rm7M+ieAV3V1@PRu zAJo??@6@dPtj+fiWHlOvwv2A4%P^jO}iV%Yv@$8{aNI7+uaA-fPBM51yN&7T`xw7X zKQ_!$m|@yj|0ir|{O1XI#gcl)9DDDEwYkMDI++}LzU%78^rFnNhiAgO_FH5xujlD`1hNZ-Srbr z>@sI;&K}@N>`0*{o4~Q~OKT&X}pHRXC}4{Vp4Z1$()Q zcE3B6V01*{(>G_O+e=@4>r-xAc>39-^3z;--ArB{!Dm8vPsPV?vU}L=BilLu(4?Io z&3<}C*PYHkzCqYB*KYAD$93T?8@5-szRucQtne*LA?MeeJ8c0ktmp2CvFR_$G+QQ} zz^I-tcx&;*WQNj+>SKNCd4*YS3wP{bGYsxxaE=eS6nLykl)Ap&W6wH;>S`(8R)gF;xoMxfa(WKPUh%y-ZS|d$xA~V|=Z2ihn4-(~Yo|d* zJmVJ0+8?`=ns~bOZ`Qq-S2S_&>Uw@Nn>j1gQiJ)tSd$|D?Kv%J@k)1!&GldZ*SH@o z|1d{ycS-Q1s^ps+bp%y^+-!X`Wl?=f&L(MbiH8#>Kg~Y!>D|eg-XlCwMoH{M%)s|;Qp&>I(e1on%*tT;6 z+u>~Kkdmqi$xT)IVZHmGy-EJ`c&T^x!>jkry-Zuz?hvlg{CF#J{)7J}Tss!k7|wZl zc+pc9>k1|xiLX}SVL73-g_XT+Z(gkYY38`;E}zbwCDV;JR<4PbG}HLNv2{(L(XwMn zKhoB!I@OdIE2d4IOed7)*d zvBxz=#=D-cvT+ zsI}TI65?*(JxjLTHoEpwUBR!%8;^aDOJDO>|NM(^rw>n(_4VfTv;DF9rINeASn}xR z3|9%^SC=N}+lm}#YTW+v`L>V+QEa<5G%PG#d7Slmk+g+Lz>V1g2WLB7*!-Glt9ERb z)90+?cf$;n-qWwXbu}E+ zpmSVpdO6?Y(!GrnlxA}UzAreQyW{qwGO?{XN#6fXPHC*2bC9{>1XG^(ikp4Uvo9S@ zQc}7Sb5d;e(~IkNv|M@7q~Le@yZ5|Q$@Q0bzfDO}`xLcZzdbhW#n+?{2QRFjX)QZ> zlgpoz7Ag)`nw?V$|Agq&Cmt+k`I6ftI5Di}YVaxV{l`D(?lVuwW7yDjH?4Hf;SaAh zr$#U>_2Z@m*ev|wtvW0doH|Xt)-Aks`)~# ztLOi%7GpTGcYAAD^rz+5qW(PFy<2Ug*yk1hoeJ;Gzpz;&F~~c6($esX6{|iQIBJme2|af2 zS(kSNaAlr|QDr=HdQ$6Ro+$MlhaG)iiR;gLx%S=VCuYAN9=x!ja{cT(R~UWj3^z1% hD;@c-;Ulv4>!FT \uicontrol {Flow View}, as described in + \li Use the event list simulator to replace transition lines with connections to real + signals from UI controls, as described in \l{Simulating Events}. + \li Use \uicontrol {Flow Decision} components from \uicontrol Components > \uicontrol {Flow + View} to set up alternative pathways between flow items, as described in \l{Simulating Conditions}. \li Use \l{Working with States}{states} in flows to modify the appearance of components on screens in response to user interaction, as From cf614eca9150b59e7f42bcd22098fe67dff4b2af Mon Sep 17 00:00:00 2001 From: Tim Jenssen Date: Fri, 10 May 2024 21:10:07 +0200 Subject: [PATCH 090/154] StudioWelcome: silent splashscreen if there is a dialog open Pick-to: qds/4.5 Task-number: QDS-12713 Change-Id: I206f132f526928fb02ea8d2f117c0ba19c08156e Reviewed-by: Thomas Hartmann Reviewed-by: Qt CI Patch Build Bot Reviewed-by: --- src/plugins/studiowelcome/studiowelcomeplugin.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/plugins/studiowelcome/studiowelcomeplugin.cpp b/src/plugins/studiowelcome/studiowelcomeplugin.cpp index fe209b5fd0b..fae0c83d41d 100644 --- a/src/plugins/studiowelcome/studiowelcomeplugin.cpp +++ b/src/plugins/studiowelcome/studiowelcomeplugin.cpp @@ -587,6 +587,9 @@ static bool forceDownLoad() static bool showSplashScreen() { + // some error dialog is maybe open, be silent to avoid focus problems (macOS had some) + if (Core::ICore::mainWindow() != Core::ICore::dialogParent()) + return false; const Key lastQDSVersionEntry = "QML/Designer/lastQDSVersion"; QtcSettings *settings = Core::ICore::settings(); From 21f00445be1ff13e52d8cf2b8418aed9aeca11b9 Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Mon, 13 May 2024 14:13:03 +0300 Subject: [PATCH 091/154] QmlDesigner: Add UniqueName class The class is used for generating a unique name anywhere in QmlDesigner codebase. The role of this class is generation of next candidate name when name is not unique. Actual checking of name uniqueness is provided as a function param. Also applied the new UniqueName on 1 usecase in ContentLibraryUserModel::getUniqueLib3DQmlName Change-Id: I777aeef7c269bed7d999695cf5fcee6a5576222b Reviewed-by: Miikka Heikkinen --- src/plugins/qmldesigner/CMakeLists.txt | 2 +- .../contentlibraryusermodel.cpp | 11 ++-- src/plugins/qmldesigner/utils/uniquename.cpp | 58 +++++++++++++++++++ src/plugins/qmldesigner/utils/uniquename.h | 19 ++++++ 4 files changed, 82 insertions(+), 8 deletions(-) create mode 100644 src/plugins/qmldesigner/utils/uniquename.cpp create mode 100644 src/plugins/qmldesigner/utils/uniquename.h diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index 5d34810d438..87f6e9c66d1 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -46,10 +46,10 @@ add_qtc_library(QmlDesignerUtils STATIC hdrimage.cpp hdrimage.h ktximage.cpp ktximage.h imageutils.cpp imageutils.h + uniquename.cpp uniquename.h qmldesignerutils_global.h ) - target_compile_options(QmlDesignerUtils PUBLIC $<$:-Wno-error=maybe-uninitialized>) target_compile_options(QmlDesignerUtils PUBLIC $<$:-Wno-unneeded-internal-declaration>) diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp index efee2224452..3e40b9c12c5 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp @@ -16,6 +16,7 @@ #include #include +#include #include #include @@ -314,13 +315,9 @@ QString ContentLibraryUserModel::getUniqueLib3DQmlName(const QString &defaultNam baseQml[0] = baseQml.at(0).toUpper(); baseQml.prepend("My"); - QString uniqueQml = baseQml; - - int counter = 1; - while (itemQmls.contains(uniqueQml)) { - uniqueQml = QString("%1%2").arg(uniqueQml).arg(counter); - ++counter; - } + QString uniqueQml = UniqueName::get(baseQml, [&] (const QString &name) { + return !itemQmls.contains(name); + }); return uniqueQml + ".qml"; } diff --git a/src/plugins/qmldesigner/utils/uniquename.cpp b/src/plugins/qmldesigner/utils/uniquename.cpp new file mode 100644 index 00000000000..ef6d3d8e5e2 --- /dev/null +++ b/src/plugins/qmldesigner/utils/uniquename.cpp @@ -0,0 +1,58 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "uniquename.h" + +#include + +namespace QmlDesigner { + +/** + * @brief Gets a unique name from a give name + * @param oldName input name + * @param predicate function to check if the name is unique. Retuns true if name is unique + * @return a unique name + */ +// static +QString UniqueName::get(const QString &oldName, std::function predicate) +{ + QString newName = oldName; + while (!predicate(newName)) + newName = nextName(newName); + + return newName; +} + +// static +QString UniqueName::nextName(const QString &oldName) +{ + static QRegularExpression rgx("\\d+$"); // matches a number at the end of a string + + QString uniqueName = oldName; + // if the name ends with a number, increment it + QRegularExpressionMatch match = rgx.match(uniqueName); + if (match.hasMatch()) { // ends with a number + QString numStr = match.captured(0); + int num = match.captured(0).toInt(); + + // get number of padding zeros, ex: for "005" = 2 + int nPaddingZeros = 0; + for (; nPaddingZeros < numStr.size() && numStr[nPaddingZeros] == '0'; ++nPaddingZeros); + + ++num; + + // if the incremented number's digits increased, decrease the padding zeros + if (std::fmod(std::log10(num), 1.0) == 0) + --nPaddingZeros; + + uniqueName = oldName.mid(0, match.capturedStart()) + + QString('0').repeated(nPaddingZeros) + + QString::number(num); + } else { + uniqueName = oldName + '1'; + } + + return uniqueName; +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/utils/uniquename.h b/src/plugins/qmldesigner/utils/uniquename.h new file mode 100644 index 00000000000..51e21bf17ad --- /dev/null +++ b/src/plugins/qmldesigner/utils/uniquename.h @@ -0,0 +1,19 @@ +// Copyright (C) 2024 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 UniqueName +{ +public: + static QString get(const QString &oldName, std::function predicate); + +private: + static QString nextName(const QString &oldName); +}; + +} // namespace QmlDesigner From 924019e8c2701ca1fb85ea66c475e971ddc5cac9 Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Mon, 13 May 2024 12:10:31 +0300 Subject: [PATCH 092/154] QmlDesigner: Use GeneratedComponentsUtils in Model Editor Change-Id: I567c746eb098c0c64d590ea7fed71f17de5adc76 Reviewed-by: Mahmoud Badri --- .../collectiondetailsmodel.cpp | 17 +- .../collectioneditor/collectiondetailsmodel.h | 4 + .../collectioneditorconstants.h | 5 + .../collectioneditorutils.cpp | 194 +++--------------- .../collectioneditor/collectioneditorutils.h | 11 +- .../collectioneditor/collectionlistmodel.cpp | 1 + .../collectioneditor/collectionview.cpp | 107 +++++++++- .../collectioneditor/collectionview.h | 2 + .../collectioneditor/datastoremodelnode.cpp | 27 ++- .../collectioneditor/datastoremodelnode.h | 3 +- .../designercore/generatedcomponentutils.cpp | 57 ++++- .../designercore/generatedcomponentutils.h | 1 + .../include/externaldependenciesinterface.h | 1 + .../qmldesignerexternaldependencies.cpp | 5 + .../qmldesignerexternaldependencies.h | 1 + .../qmldesigner/coretests/tst_testcore.cpp | 1 + .../tests/mocks/externaldependenciesmock.h | 1 + 17 files changed, 240 insertions(+), 198 deletions(-) diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.cpp index 2ca2d3e8004..ab2278fb726 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.cpp @@ -407,16 +407,14 @@ void CollectionDetailsModel::renameCollection(const ModelNode &sourceNode, bool CollectionDetailsModel::saveDataStoreCollections() { const ModelNode node = m_currentCollection.reference().node; - const Utils::FilePath path = CollectionEditorUtils::dataStoreJsonFilePath(); - Utils::FileReader fileData; - - if (!fileData.fetch(path)) { - qWarning() << Q_FUNC_INFO << "Cannot read the json file:" << fileData.errorString(); + Utils::expected_str jsonContents = m_jsonFilePath.fileContents(); + if (!jsonContents.has_value()) { + qWarning() << __FUNCTION__ << jsonContents.error(); return false; } QJsonParseError jpe; - QJsonDocument document = QJsonDocument::fromJson(fileData.data(), &jpe); + QJsonDocument document = QJsonDocument::fromJson(jsonContents.value(), &jpe); if (jpe.error == QJsonParseError::NoError) { QJsonObject obj = document.object(); @@ -432,7 +430,7 @@ bool CollectionDetailsModel::saveDataStoreCollections() document.setObject(obj); - if (CollectionEditorUtils::writeToJsonDocument(path, document)) { + if (CollectionEditorUtils::writeToJsonDocument(m_jsonFilePath, document)) { const CollectionReference currentReference = m_currentCollection.reference(); for (CollectionDetails &collection : collectionsToBeSaved) { collection.markSaved(); @@ -618,6 +616,11 @@ QString CollectionDetailsModel::warningToString(DataTypeWarning::Warning warning return DataTypeWarning::getDataTypeWarningString(warning); } +void CollectionDetailsModel::setJsonFilePath(const Utils::FilePath &filePath) +{ + m_jsonFilePath = filePath; +} + void CollectionDetailsModel::setHasUnsavedChanges(bool val) { if (m_hasUnsavedChanges == val) diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.h b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.h index 8844ff4a3ef..fbe90a6f567 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.h +++ b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.h @@ -5,6 +5,8 @@ #include "collectiondetails.h" +#include + #include #include @@ -60,6 +62,7 @@ public: Q_INVOKABLE void deselectAll(); Q_INVOKABLE QString warningToString(DataTypeWarning::Warning warning) const; + void setJsonFilePath(const Utils::FilePath &filePath); void loadCollection(const ModelNode &sourceNode, const QString &collection); void removeCollection(const ModelNode &sourceNode, const QString &collection); void removeAllCollections(); @@ -93,6 +96,7 @@ private: void ensureSingleCell(); QJsonDocument readJsonFile(const QUrl &url); + Utils::FilePath m_jsonFilePath; QHash m_openedCollections; CollectionDetails m_currentCollection; bool m_isEmpty = true; diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorconstants.h b/src/plugins/qmldesigner/components/collectioneditor/collectioneditorconstants.h index 76524762ede..2228c58518c 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorconstants.h +++ b/src/plugins/qmldesigner/components/collectioneditor/collectioneditorconstants.h @@ -16,4 +16,9 @@ inline constexpr char JSONCOLLECTIONMODEL_TYPENAME[] = "QtQuick.Studio.Ut inline constexpr char JSONCOLLECTIONCHILDMODEL_TYPENAME[] = "QtQuick.Studio.Utils.ChildListModel"; inline constexpr char JSONBACKEND_TYPENAME[] = "JsonData"; +inline constexpr QStringView DEFAULT_DATA_JSON_FILENAME = u"data.json"; +inline constexpr QStringView DEFAULT_MODELS_JSON_FILENAME = u"models.json"; +inline constexpr QStringView DEFAULT_DATASTORE_QML_FILENAME = u"DataStore.qml"; +inline constexpr QStringView DEFAULT_JSONDATA_QML_FILENAME = u"JsonData.qml"; + } // namespace QmlDesigner::CollectionEditorConstants diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.cpp index 40350802d26..a991e2d729c 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.cpp @@ -4,9 +4,11 @@ #include "collectioneditorutils.h" #include "collectiondatatypemodel.h" +#include "collectioneditorconstants.h" #include "model.h" #include "nodemetainfo.h" #include "propertymetainfo.h" +#include "variantproperty.h" #include #include @@ -73,47 +75,6 @@ struct LessThanVisitor } }; -Utils::FilePath findFile(const Utils::FilePath &path, const QString &fileName) -{ - QDirIterator it(path.toString(), QDirIterator::Subdirectories); - - while (it.hasNext()) { - QFileInfo file(it.next()); - if (file.isDir()) - continue; - - if (file.fileName() == fileName) - return Utils::FilePath::fromFileInfo(file); - } - return {}; -} - -Utils::FilePath dataStoreDir() -{ - using Utils::FilePath; - ProjectExplorer::Project *currentProject = ProjectExplorer::ProjectManager::startupProject(); - - if (!currentProject) - return {}; - - FilePath oldImportDirectory = currentProject->projectDirectory().pathAppended( - "imports/" + currentProject->displayName()); - if (oldImportDirectory.exists()) - return oldImportDirectory; - - return currentProject->projectDirectory().pathAppended(currentProject->displayName()); -} - -inline Utils::FilePath collectionPath(const QString &filePath) -{ - return dataStoreDir().pathAppended(filePath); -} - -inline Utils::FilePath qmlDirFilePath() -{ - return collectionPath("qmldir"); -} - } // namespace namespace QmlDesigner::CollectionEditorUtils { @@ -123,25 +84,6 @@ bool variantIslessThan(const QVariant &a, const QVariant &b, DataType type) return std::visit(LessThanVisitor{}, valueToVariant(a, type), valueToVariant(b, type)); } -QString getSourceCollectionType(const ModelNode &node) -{ - using namespace QmlDesigner; - if (node.type() == CollectionEditorConstants::JSONCOLLECTIONMODEL_TYPENAME) - return "json"; - - return {}; -} - -Utils::FilePath dataStoreJsonFilePath() -{ - return collectionPath("models.json"); -} - -Utils::FilePath dataStoreQmlFilePath() -{ - return collectionPath("DataStore.qml"); -} - bool canAcceptCollectionAsModel(const ModelNode &node) { const NodeMetaInfo nodeMetaInfo = node.metaInfo(); @@ -176,116 +118,25 @@ QString getSourceCollectionPath(const ModelNode &dataStoreNode) if (!dataStoreNode.isValid()) return {}; - const FilePath expectedFile = dataStoreJsonFilePath(); + const QUrl dataStoreUrl = dataStoreNode.model()->fileUrl(); + QUrl sourceValue = dataStoreNode.property("source").toVariantProperty().value().toUrl(); - if (expectedFile.exists()) + QUrl sourceUrl = sourceValue.isRelative() ? dataStoreUrl.resolved(sourceValue) : sourceValue; + + const FilePath expectedFile = FilePath::fromUrl(sourceUrl); + + if (expectedFile.isFile() && expectedFile.exists()) return expectedFile.toFSPathString(); + const FilePath defaultJsonFile = FilePath::fromUrl( + dataStoreUrl.resolved(CollectionEditorConstants::DEFAULT_MODELS_JSON_FILENAME.toString())); + + if (defaultJsonFile.exists()) + return defaultJsonFile.toFSPathString(); + return {}; } -bool isDataStoreNode(const ModelNode &dataStoreNode) -{ - using Utils::FilePath; - - if (!dataStoreNode.isValid()) - return false; - - const FilePath expectedFile = dataStoreQmlFilePath(); - - if (!expectedFile.exists()) - return false; - - FilePath modelPath = FilePath::fromUserInput(dataStoreNode.model()->fileUrl().toLocalFile()); - - return modelPath.isSameFile(expectedFile); -} - -bool ensureDataStoreExists(bool &justCreated) -{ - using Utils::FilePath; - using Utils::FileReader; - using Utils::FileSaver; - - FilePath qmlDestinationPath = dataStoreQmlFilePath(); - justCreated = false; - - auto extractDependency = [&justCreated](const FilePath &filePath) -> bool { - if (filePath.exists()) - return true; - - const QString templateFileName = filePath.fileName() + u".tpl"; - const FilePath templatePath = findFile(Core::ICore::resourcePath(), templateFileName); - if (!templatePath.exists()) { - qWarning() << Q_FUNC_INFO << __LINE__ << templateFileName << "does not exist"; - return false; - } - - if (!filePath.parentDir().ensureWritableDir()) { - qWarning() << Q_FUNC_INFO << __LINE__ << "Cannot create directory" - << filePath.parentDir(); - return false; - } - - if (templatePath.copyFile(filePath)) { - justCreated = true; - return true; - } - - qWarning() << Q_FUNC_INFO << __LINE__ << "Cannot copy" << templateFileName << "to" << filePath; - return false; - }; - - if (!extractDependency(dataStoreJsonFilePath())) - return false; - - if (!extractDependency(collectionPath("data.json"))) - return false; - - if (!extractDependency(collectionPath("JsonData.qml"))) - return false; - - if (!qmlDestinationPath.exists()) { - if (qmlDestinationPath.ensureExistingFile()) { - justCreated = true; - } else { - qWarning() << Q_FUNC_INFO << __LINE__ << "Can't create DataStore Qml File"; - return false; - } - } - - FilePath qmlDirPath = qmlDirFilePath(); - qmlDirPath.ensureExistingFile(); - - FileReader qmlDirReader; - if (!qmlDirReader.fetch(qmlDirPath)) { - qWarning() << Q_FUNC_INFO << __LINE__ << "Can't read the content of the qmldir"; - return false; - } - - QByteArray qmlDirContent = qmlDirReader.data(); - const QList qmlDirLines = qmlDirContent.split('\n'); - for (const QByteArray &line : qmlDirLines) { - if (line.startsWith("singleton DataStore ")) - return true; - } - - if (!qmlDirContent.isEmpty() && qmlDirContent.back() != '\n') - qmlDirContent.append("\n"); - qmlDirContent.append("singleton DataStore 1.0 DataStore.qml\n"); - - FileSaver qmlDirSaver(qmlDirPath); - qmlDirSaver.write(qmlDirContent); - - if (qmlDirSaver.finalize()) { - justCreated = true; - return true; - } - - qWarning() << Q_FUNC_INFO << __LINE__ << "Can't write to the qmldir file"; - return false; -} - QJsonObject defaultCollection() { QJsonObject collectionObject; @@ -337,6 +188,21 @@ QJsonObject defaultColorCollection() return collection.toLocalJson(); } +Utils::FilePath findFile(const Utils::FilePath &path, const QString &fileName) +{ + QDirIterator it(path.toString(), QDirIterator::Subdirectories); + + while (it.hasNext()) { + QFileInfo file(it.next()); + if (file.isDir()) + continue; + + if (file.fileName() == fileName) + return Utils::FilePath::fromFileInfo(file); + } + return {}; +} + bool writeToJsonDocument(const Utils::FilePath &path, const QJsonDocument &document, QString *errorString) { Core::FileChangeBlocker fileBlocker(path); diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.h b/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.h index 355addf59be..7afc6f233fe 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.h +++ b/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.h @@ -4,7 +4,6 @@ #pragma once #include "collectiondetails.h" -#include "collectioneditorconstants.h" QT_BEGIN_NAMESPACE class QJsonArray; @@ -19,22 +18,14 @@ namespace QmlDesigner::CollectionEditorUtils { bool variantIslessThan(const QVariant &a, const QVariant &b, CollectionDetails::DataType type); -QString getSourceCollectionType(const QmlDesigner::ModelNode &node); - QString getSourceCollectionPath(const QmlDesigner::ModelNode &dataStoreNode); -Utils::FilePath dataStoreJsonFilePath(); - -Utils::FilePath dataStoreQmlFilePath(); +Utils::FilePath findFile(const Utils::FilePath &path, const QString &fileName); bool writeToJsonDocument(const Utils::FilePath &path, const QJsonDocument &document, QString *errorString = nullptr); -bool isDataStoreNode(const ModelNode &dataStoreNode); - -bool ensureDataStoreExists(bool &justCreated); - bool canAcceptCollectionAsModel(const ModelNode &node); bool hasTextRoleProperty(const ModelNode &node); diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionlistmodel.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionlistmodel.cpp index d27a077d2a7..320bc1bc043 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionlistmodel.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionlistmodel.cpp @@ -3,6 +3,7 @@ #include "collectionlistmodel.h" +#include "collectioneditorconstants.h" #include "collectioneditorutils.h" #include diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp index 9221919df45..f8382b19897 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp @@ -17,6 +17,7 @@ #include "qmldesignerplugin.h" #include "variantproperty.h" +#include #include #include #include @@ -77,8 +78,7 @@ CollectionView::CollectionView(ExternalDependenciesInterface &externalDependenci : AbstractView(externalDependencies) , m_dataStore(std::make_unique()) -{ -} +{} CollectionView::~CollectionView() = default; @@ -330,7 +330,8 @@ void CollectionView::resetDataStoreNode() if (!m_widget) return; - m_dataStore->reloadModel(); + auto compUtils = QmlDesignerPlugin::instance()->documentManager().generatedComponentUtils(); + m_dataStore->reloadModel(compUtils.projectModulePath()); ModelNode dataStore = dataStoreNode(); m_widget->setDataStoreExists(dataStore.isValid()); @@ -354,6 +355,7 @@ void CollectionView::resetDataStoreNode() if (dataStoreSingletonFound) { m_widget->listModel()->setDataStoreNode(dataStore); + m_widget->collectionDetailsModel()->setJsonFilePath(m_dataStore->jsonFilePath()); m_dataStoreTypeFound = true; resetPuppet(); @@ -374,7 +376,7 @@ ModelNode CollectionView::dataStoreNode() const void CollectionView::ensureDataStoreExists() { bool filesJustCreated = false; - bool filesExist = CollectionEditorUtils::ensureDataStoreExists(filesJustCreated); + bool filesExist = createDataStore(filesJustCreated); if (filesExist && filesJustCreated) { // Force code model reset to notice changes to existing module if (auto modelManager = QmlJS::ModelManagerInterface::instance()) @@ -411,6 +413,103 @@ void CollectionView::unloadDataStore() } } +bool CollectionView::createDataStore(bool &justCreated) const +{ + using Utils::FilePath; + using Utils::FileReader; + using Utils::FileSaver; + using namespace Qt::StringLiterals; + + auto compUtils = QmlDesignerPlugin::instance()->documentManager().generatedComponentUtils(); + FilePath projectModulePath = compUtils.projectModulePath(true); + + FilePath qmlTargetPath = projectModulePath.resolvePath( + CollectionEditorConstants::DEFAULT_DATASTORE_QML_FILENAME.toString()); + justCreated = false; + + auto extractDependency = [&justCreated](const FilePath &filePath) -> bool { + if (filePath.exists()) + return true; + + const QString templateFileName = filePath.fileName() + u".tpl"; + const FilePath templatePath = CollectionEditorUtils::findFile(Core::ICore::resourcePath(), + templateFileName); + if (!templatePath.exists()) { + qWarning() << Q_FUNC_INFO << __LINE__ << templateFileName << "does not exist"; + return false; + } + + if (!filePath.parentDir().ensureWritableDir()) { + qWarning() << Q_FUNC_INFO << __LINE__ << "Cannot create directory" + << filePath.parentDir(); + return false; + } + + if (templatePath.copyFile(filePath)) { + justCreated = true; + return true; + } + + qWarning() << Q_FUNC_INFO << __LINE__ << "Cannot copy" << templateFileName << "to" << filePath; + return false; + }; + + if (!extractDependency(projectModulePath.resolvePath( + CollectionEditorConstants::DEFAULT_MODELS_JSON_FILENAME.toString()))) { + return false; + } + + if (!extractDependency(projectModulePath.resolvePath( + CollectionEditorConstants::DEFAULT_DATA_JSON_FILENAME.toString()))) { + return false; + } + + if (!extractDependency(projectModulePath.resolvePath( + CollectionEditorConstants::DEFAULT_JSONDATA_QML_FILENAME.toString()))) { + return false; + } + + if (!qmlTargetPath.exists()) { + if (qmlTargetPath.ensureExistingFile()) { + justCreated = true; + } else { + qWarning() << Q_FUNC_INFO << __LINE__ << "Can't create DataStore Qml File"; + return false; + } + } + + FilePath qmlDirPath = projectModulePath.resolvePath("qmldir"_L1); + qmlDirPath.ensureExistingFile(); + + FileReader qmlDirReader; + if (!qmlDirReader.fetch(qmlDirPath)) { + qWarning() << Q_FUNC_INFO << __LINE__ << "Can't read the content of the qmldir"; + return false; + } + + QByteArray qmlDirContent = qmlDirReader.data(); + const QList qmlDirLines = qmlDirContent.split('\n'); + for (const QByteArray &line : qmlDirLines) { + if (line.startsWith("singleton DataStore ")) + return true; + } + + if (!qmlDirContent.isEmpty() && qmlDirContent.back() != '\n') + qmlDirContent.append("\n"); + qmlDirContent.append("singleton DataStore 1.0 DataStore.qml\n"); + + FileSaver qmlDirSaver(qmlDirPath); + qmlDirSaver.write(qmlDirContent); + + if (qmlDirSaver.finalize()) { + justCreated = true; + return true; + } + + qWarning() << Q_FUNC_INFO << __LINE__ << "Can't write to the qmldir file"; + return false; +} + void CollectionView::ensureStudioModelImport() { executeInTransaction(__FUNCTION__, [&] { diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionview.h b/src/plugins/qmldesigner/components/collectioneditor/collectionview.h index 20cb045374d..458ea6c60a5 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionview.h +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionview.h @@ -23,6 +23,7 @@ class CollectionListModel; class CollectionTask; class CollectionWidget; class DataStoreModelNode; +class GeneratedComponentUtils; class CollectionView : public AbstractView { @@ -69,6 +70,7 @@ private: NodeMetaInfo jsonCollectionMetaInfo() const; void unloadDataStore(); + bool createDataStore(bool &justCreated) const; void ensureStudioModelImport(); void onItemLibraryNodeCreated(const ModelNode &node); void addTask(QSharedPointer task); diff --git a/src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.cpp b/src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.cpp index d8b80061f32..5ffad6683c3 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.cpp @@ -21,9 +21,7 @@ #include #include -#include #include -#include #include #include @@ -114,7 +112,6 @@ void setQmlContextToModel(QmlDesigner::Model *model, const QString &qmlContext) std::unique_ptr rewriter = std::make_unique( externalDependencies, QmlDesigner::RewriterView::Validate); - rewriter->setParent(model); rewriter->setTextModifier(modifier.get()); rewriter->setCheckSemanticErrors(false); @@ -126,22 +123,21 @@ void setQmlContextToModel(QmlDesigner::Model *model, const QString &qmlContext) namespace QmlDesigner { -DataStoreModelNode::DataStoreModelNode() -{ - reloadModel(); -} +DataStoreModelNode::DataStoreModelNode() = default; -void DataStoreModelNode::reloadModel() +void DataStoreModelNode::reloadModel(const Utils::FilePath &projectModulePath) { using Utils::FilePath; - if (!ProjectExplorer::ProjectManager::startupProject()) { + if (!projectModulePath.exists()) { reset(); return; } bool forceUpdate = false; - const FilePath dataStoreQmlPath = CollectionEditorUtils::dataStoreQmlFilePath(); - const FilePath dataStoreJsonPath = CollectionEditorUtils::dataStoreJsonFilePath(); + const FilePath dataStoreQmlPath = projectModulePath.resolvePath( + CollectionEditorConstants::DEFAULT_DATASTORE_QML_FILENAME.toString()); + const FilePath dataStoreJsonPath = projectModulePath.resolvePath( + CollectionEditorConstants::DEFAULT_MODELS_JSON_FILENAME.toString()); QUrl dataStoreQmlUrl = dataStoreQmlPath.toUrl(); if (dataStoreQmlPath.exists() && dataStoreJsonPath.exists()) { @@ -196,6 +192,15 @@ ModelNode DataStoreModelNode::modelNode() const return m_model->rootModelNode(); } +Utils::FilePath DataStoreModelNode::jsonFilePath() const +{ + QUrl modelUrl = m_model->fileUrl(); + return Utils::FilePath::fromUserInput(modelUrl.isLocalFile() ? modelUrl.toLocalFile() + : modelUrl.toString()) + .parentDir() + .resolvePath(CollectionEditorConstants::DEFAULT_MODELS_JSON_FILENAME.toString()); +} + QString DataStoreModelNode::getModelQmlText() { ModelNode node = modelNode(); diff --git a/src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.h b/src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.h index 6cd969edbe6..9d438ef5f56 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.h +++ b/src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.h @@ -24,11 +24,12 @@ public: DataStoreModelNode(); - void reloadModel(); + void reloadModel(const Utils::FilePath &projectModulePath); QStringList collectionNames() const; Model *model() const; ModelNode modelNode() const; + Utils::FilePath jsonFilePath() const; void setCollectionNames(const QStringList &newCollectionNames); void addCollection(const QString &collectionName); diff --git a/src/plugins/qmldesigner/designercore/generatedcomponentutils.cpp b/src/plugins/qmldesigner/designercore/generatedcomponentutils.cpp index 137fbd9b528..d9e4480ef53 100644 --- a/src/plugins/qmldesigner/designercore/generatedcomponentutils.cpp +++ b/src/plugins/qmldesigner/designercore/generatedcomponentutils.cpp @@ -2,11 +2,36 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "generatedcomponentutils.h" - #include namespace QmlDesigner { +bool couldBeProjectModule(const Utils::FilePath &path, const QString &projectName) +{ + if (!path.exists()) + return false; + + Utils::FilePath qmlDirPath = path.pathAppended("qmldir"); + if (qmlDirPath.exists()) { + Utils::expected_str qmldirContents = qmlDirPath.fileContents(); + if (!qmldirContents.has_value()) + return false; + + const QString expectedLine = QLatin1String("module %1").arg(projectName); + QByteArray fileContents = qmldirContents.value(); + QTextStream stream(fileContents); + while (!stream.atEnd()) { + QString lineData = stream.readLine().trimmed(); + if (lineData.startsWith(u"module ")) + return lineData == expectedLine; + } + } + if (path.endsWith(projectName)) + return true; + + return false; +} + GeneratedComponentUtils::GeneratedComponentUtils(ExternalDependenciesInterface &externalDependencies) : m_externalDependencies(externalDependencies) { @@ -106,6 +131,36 @@ Utils::FilePath GeneratedComponentUtils::effectBundlePath() const return basePath.resolvePath(QLatin1String(Constants::COMPONENT_BUNDLES_EFFECT_BUNDLE_TYPE)); } +Utils::FilePath GeneratedComponentUtils::projectModulePath(bool generateIfNotExists) const +{ + using Utils::FilePath; + FilePath projectPath = FilePath::fromString(m_externalDependencies.currentProjectDirPath()); + + if (projectPath.isEmpty()) + return {}; + + const QString projectName = m_externalDependencies.projectName(); + + FilePath newImportDirectory = projectPath.pathAppended(projectName); + if (couldBeProjectModule(newImportDirectory, projectName)) + return newImportDirectory; + + FilePath oldImportDirectory = projectPath.resolvePath(QLatin1String("imports/") + projectName); + if (couldBeProjectModule(oldImportDirectory, projectName)) + return oldImportDirectory; + + for (const QString &path : m_externalDependencies.projectModulePaths()) { + FilePath dir = FilePath::fromString(path); + if (couldBeProjectModule(dir, projectName)) + return dir; + } + + if (generateIfNotExists) + newImportDirectory.createDir(); + + return newImportDirectory; +} + bool GeneratedComponentUtils::isImport3dPath(const QString &path) const { return path.contains('/' + QLatin1String(Constants::OLD_QUICK_3D_ASSETS_FOLDER)) diff --git a/src/plugins/qmldesigner/designercore/generatedcomponentutils.h b/src/plugins/qmldesigner/designercore/generatedcomponentutils.h index 5fc51fd85f3..cc51b5f8cbc 100644 --- a/src/plugins/qmldesigner/designercore/generatedcomponentutils.h +++ b/src/plugins/qmldesigner/designercore/generatedcomponentutils.h @@ -23,6 +23,7 @@ public: Utils::FilePath import3dBasePath() const; Utils::FilePath materialBundlePath() const; Utils::FilePath effectBundlePath() const; + Utils::FilePath projectModulePath(bool generateIfNotExists = false) const; bool isImport3dPath(const QString &path) const; bool isComposedEffectPath(const QString &path) const; diff --git a/src/plugins/qmldesigner/designercore/include/externaldependenciesinterface.h b/src/plugins/qmldesigner/designercore/include/externaldependenciesinterface.h index 71ddeb7dc19..9055f51b6a1 100644 --- a/src/plugins/qmldesigner/designercore/include/externaldependenciesinterface.h +++ b/src/plugins/qmldesigner/designercore/include/externaldependenciesinterface.h @@ -28,6 +28,7 @@ public: virtual QString qmlPuppetFallbackDirectory() const = 0; virtual QString defaultPuppetToplevelBuildDirectory() const = 0; virtual QUrl projectUrl() const = 0; + virtual QString projectName() const = 0; virtual QString currentProjectDirPath() const = 0; virtual QUrl currentResourcePath() const = 0; virtual void parseItemLibraryDescriptions() = 0; diff --git a/src/plugins/qmldesigner/qmldesignerexternaldependencies.cpp b/src/plugins/qmldesigner/qmldesignerexternaldependencies.cpp index 321d95197fc..97b2b46e7d5 100644 --- a/src/plugins/qmldesigner/qmldesignerexternaldependencies.cpp +++ b/src/plugins/qmldesigner/qmldesignerexternaldependencies.cpp @@ -56,6 +56,11 @@ QUrl ExternalDependencies::projectUrl() const return {}; } +QString ExternalDependencies::projectName() const +{ + return QmlDesignerPlugin::instance()->documentManager().currentProjectName(); +} + QString ExternalDependencies::currentProjectDirPath() const { return QmlDesignerPlugin::instance()->documentManager().currentProjectDirPath().toString(); diff --git a/src/plugins/qmldesigner/qmldesignerexternaldependencies.h b/src/plugins/qmldesigner/qmldesignerexternaldependencies.h index b4908c23833..6a49e4b5516 100644 --- a/src/plugins/qmldesigner/qmldesignerexternaldependencies.h +++ b/src/plugins/qmldesigner/qmldesignerexternaldependencies.h @@ -21,6 +21,7 @@ public: QString qmlPuppetFallbackDirectory() const override; QString defaultPuppetToplevelBuildDirectory() const override; QUrl projectUrl() const override; + QString projectName() const override; QString currentProjectDirPath() const override; QUrl currentResourcePath() const override; void parseItemLibraryDescriptions() override; diff --git a/tests/auto/qml/qmldesigner/coretests/tst_testcore.cpp b/tests/auto/qml/qmldesigner/coretests/tst_testcore.cpp index 569d0ce3a4e..2e88690f322 100644 --- a/tests/auto/qml/qmldesigner/coretests/tst_testcore.cpp +++ b/tests/auto/qml/qmldesigner/coretests/tst_testcore.cpp @@ -149,6 +149,7 @@ public: QString defaultPuppetToplevelBuildDirectory() const override { return {}; } QString qmlPuppetFallbackDirectory() const override { return {}; } QUrl projectUrl() const override { return {}; } + QString projectName() const override { return {}; } void parseItemLibraryDescriptions() override {} const QmlDesigner::DesignerSettings &designerSettings() const override { return settings; } void undoOnCurrentDesignDocument() override {} diff --git a/tests/unit/tests/mocks/externaldependenciesmock.h b/tests/unit/tests/mocks/externaldependenciesmock.h index c4cfe6cd3b5..df70a7fcdb3 100644 --- a/tests/unit/tests/mocks/externaldependenciesmock.h +++ b/tests/unit/tests/mocks/externaldependenciesmock.h @@ -15,6 +15,7 @@ public: MOCK_METHOD(QString, qmlPuppetFallbackDirectory, (), (const, override)); MOCK_METHOD(QString, defaultPuppetToplevelBuildDirectory, (), (const, override)); MOCK_METHOD(QUrl, projectUrl, (), (const, override)); + MOCK_METHOD(QString, projectName, (), (const, override)); MOCK_METHOD(QString, currentProjectDirPath, (), (const, override)); MOCK_METHOD(QUrl, currentResourcePath, (), (const, override)); MOCK_METHOD(void, parseItemLibraryDescriptions, (), (override)); From c29f51454364dedc84fdf12a2a792762a804c5e9 Mon Sep 17 00:00:00 2001 From: Henning Gruendl Date: Tue, 7 May 2024 18:04:03 +0200 Subject: [PATCH 093/154] QmlDesigner: Fix ColorEditor closing Fix ColorEditorPopup closing when opening gradient dialog. Pick-to: qds/4.5 Change-Id: I438bd41d2efff30b372c04bc17c85210811cd0ff Reviewed-by: Reviewed-by: Thomas Hartmann Reviewed-by: Qt CI Patch Build Bot --- .../imports/HelperWidgets/ColorEditor.qml | 1 + .../imports/HelperWidgets/ColorEditorPopup.qml | 2 ++ 2 files changed, 3 insertions(+) diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ColorEditor.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ColorEditor.qml index 385d01c4df8..cc8247d7063 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ColorEditor.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ColorEditor.qml @@ -246,6 +246,7 @@ SecondColumnLayout { supportGradient: colorEditor.supportGradient width: popupDialog.contentWidth visible: popupDialog.visible + parentWindow: popupDialog.window } onLoaded: { diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ColorEditorPopup.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ColorEditorPopup.qml index 2c292c5030f..41f2c433fa9 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ColorEditorPopup.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ColorEditorPopup.qml @@ -33,6 +33,8 @@ Column { property alias gradientModel: gradientModel + property Window parentWindow: null + property bool isInValidState: false readonly property real twoColumnWidth: (colorColumn.width - StudioTheme.Values.controlGap) * 0.5 From e86814c491e7d9997cf53cbf2c13e7f6e14fac5c Mon Sep 17 00:00:00 2001 From: Henning Gruendl Date: Mon, 13 May 2024 15:36:03 +0200 Subject: [PATCH 094/154] QmlDesigner: Fix Connection editor closing Pick-to: qds/4.5 Task-number: QDS-12013 Change-Id: I8add3418bb43b87e45153c012353646833413664 Reviewed-by: Thomas Hartmann --- .../connectionseditor/ConnectionsDialog.qml | 4 ++++ .../connectionseditor/ConnectionsDialogForm.qml | 17 +++++++++-------- .../imports/StudioControls/PopupDialog.qml | 6 ++---- 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialog.qml b/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialog.qml index 965f31d21ab..299234127ad 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialog.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialog.qml @@ -11,6 +11,8 @@ StudioControls.PopupDialog { property alias backend: form.backend + keepOpen: form.keepOpen + titleBar: Row { spacing: 30 // TODO anchors.fill: parent @@ -43,6 +45,8 @@ StudioControls.PopupDialog { ConnectionsDialogForm { id: form + parentWindow: root.window + Connections { target: root.backend function onPopupShouldClose() { diff --git a/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialogForm.qml b/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialogForm.qml index 603903dbdd2..a799165375f 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialogForm.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialogForm.qml @@ -16,6 +16,9 @@ Column { property var backend + property bool keepOpen: expressionDialogLoader.visible + property Window parentWindow: null + width: parent.width spacing: root.verticalSpacing @@ -267,10 +270,8 @@ Column { horizontalAlignment: code.lineCount === 1 ? Text.AlignHCenter : Text.AlignLeft verticalAlignment: Text.AlignVCenter elide: Text.ElideRight - } - Loader { id: expressionDialogLoader parent: editor @@ -297,19 +298,19 @@ Column { id: bindingEditor onRejected: { - hideWidget() + bindingEditor.hideWidget() expressionDialogLoader.visible = false } onAccepted: { backend.setNewSource(bindingEditor.text) - hideWidget() + bindingEditor.hideWidget() expressionDialogLoader.visible = false } } } - } // loader - } // rect - } //col 2 -}//col1 + } + } + } +} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/PopupDialog.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/PopupDialog.qml index 40c35df27c7..821ba3a8491 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/PopupDialog.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/PopupDialog.qml @@ -104,7 +104,7 @@ QtObject { return root.maximumHeight + (2 * window.margin) } visible: false - flags: Qt.FramelessWindowHint | Qt.Tool | Qt.WindowStaysOnTopHint + flags: Qt.FramelessWindowHint | Qt.Tool color: "transparent" onClosing: function (close) { @@ -286,10 +286,8 @@ QtObject { enabled: root.visible function onFocusWindowChanged(focusWindow) { - if (!focusWindow) { - root.close() + if (!focusWindow) return - } if (root.keepOpen) return From 54879113f17a1f5446564de1f5bea6980940a20e Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Mon, 13 May 2024 16:29:27 +0300 Subject: [PATCH 095/154] QmlDesigner: Ensure unique content lib user item icon names Also update user materials model to have the improvements done to the 3D model. Also trying to make the 2 mdoels as similar as possible to make it easy for further future refactoring. Change-Id: I5a32e1dcd7919bdf3cb638b068b0cdb5d4afecd9 Fixes: QDS-12736 Reviewed-by: Miikka Heikkinen --- .../contentlibraryusermodel.cpp | 193 +++++++++--------- .../contentlibrary/contentlibraryusermodel.h | 7 +- .../contentlibrary/contentlibraryview.cpp | 47 ++--- .../contentlibrary/contentlibraryview.h | 2 +- 4 files changed, 120 insertions(+), 129 deletions(-) diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp index 3e40b9c12c5..bb732080d7e 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp @@ -186,42 +186,48 @@ void ContentLibraryUserModel::removeFromContentLib(QObject *item) remove3DFromContentLib(itm); } -void ContentLibraryUserModel::removeMaterialFromContentLib(ContentLibraryMaterial *mat) +void ContentLibraryUserModel::removeMaterialFromContentLib(ContentLibraryMaterial *item) { auto bundlePath = Utils::FilePath::fromString(Paths::bundlesPathSetting() + "/User/materials/"); - QJsonObject matsObj = m_bundleObjMaterial.value("materials").toObject(); + QJsonArray itemsArr = m_bundleObjMaterial.value("items").toArray(); // remove qml and icon files - Utils::FilePath::fromString(mat->qmlFilePath()).removeFile(); - Utils::FilePath::fromUrl(mat->icon()).removeFile(); + Utils::FilePath::fromString(item->qmlFilePath()).removeFile(); + Utils::FilePath::fromUrl(item->icon()).removeFile(); // remove from the bundle json file - matsObj.remove(mat->name()); - m_bundleObjMaterial.insert("materials", matsObj); - auto result = bundlePath.pathAppended("user_materials_bundle.json") + for (int i = 0; i < itemsArr.size(); ++i) { + if (itemsArr.at(i).toObject().value("qml") == item->qml()) { + itemsArr.removeAt(i); + break; + } + } + m_bundleObjMaterial.insert("items", itemsArr); + + auto result = bundlePath.pathAppended("user_material_bundle.json") .writeFileContents(QJsonDocument(m_bundleObjMaterial).toJson()); if (!result) qWarning() << __FUNCTION__ << result.error(); // delete dependency files if they are only used by the deleted material QStringList allFiles; - for (const QJsonValueConstRef &mat : std::as_const(matsObj)) - allFiles.append(mat.toObject().value("files").toVariant().toStringList()); + for (const QJsonValueConstRef &itemRef : std::as_const(itemsArr)) + allFiles.append(itemRef.toObject().value("files").toVariant().toStringList()); - const QStringList matFiles = mat->files(); - for (const QString &matFile : matFiles) { - if (allFiles.count(matFile) == 0) // only used by the deleted material - bundlePath.pathAppended(matFile).removeFile(); + const QStringList itemFiles = item->files(); + for (const QString &file : itemFiles) { + if (allFiles.count(file) == 0) // only used by the deleted item + bundlePath.pathAppended(file).removeFile(); } // remove from model - m_userMaterials.removeOne(mat); - mat->deleteLater(); + m_userMaterials.removeOne(item); + item->deleteLater(); // update model - int matSectionIdx = 0; - emit dataChanged(index(matSectionIdx), index(matSectionIdx)); + int sectionIdx = 0; + emit dataChanged(index(sectionIdx), index(sectionIdx)); } void ContentLibraryUserModel::remove3DFromContentLib(ContentLibraryItem *item) @@ -266,51 +272,41 @@ void ContentLibraryUserModel::remove3DFromContentLib(ContentLibraryItem *item) emit dataChanged(index(sectionIdx), index(sectionIdx)); } -// returns unique library material's name and qml component -QPair ContentLibraryUserModel::getUniqueLibMaterialNameAndQml(const QString &defaultName) const +/** + * @brief Gets unique Qml component and icon file material names from a given name + * @param defaultName input name + * @return file names + */ +QPair ContentLibraryUserModel::getUniqueLibMaterialNames(const QString &defaultName) const { - QTC_ASSERT(!m_bundleObjMaterial.isEmpty(), return {}); - - const QJsonObject matsObj = m_bundleObjMaterial.value("materials").toObject(); - const QStringList matNames = matsObj.keys(); - - QStringList matQmls; - for (const QString &matName : matNames) - matQmls.append(matsObj.value(matName).toObject().value("qml").toString().chopped(4)); // remove .qml - - QString retName = defaultName.isEmpty() ? "Material" : defaultName.trimmed(); - QString retQml = retName; - - retQml.remove(' '); - if (retQml.at(0).isLower()) - retQml[0] = retQml.at(0).toUpper(); - retQml.prepend("My"); - - int num = 1; - if (matNames.contains(retName) || matQmls.contains(retQml)) { - while (matNames.contains(retName + QString::number(num)) - || matQmls.contains(retQml + QString::number(num))) { - ++num; - } - - retName += QString::number(num); - retQml += QString::number(num); - } - - return {retName, retQml + ".qml"}; + return getUniqueLibItemNames(defaultName, m_bundleObjMaterial); } -QString ContentLibraryUserModel::getUniqueLib3DQmlName(const QString &defaultName) const +/** + * @brief Gets unique Qml component and icon file 3d item names from a given name + * @param defaultName input name + * @return file names + */ +QPair ContentLibraryUserModel::getUniqueLib3DNames(const QString &defaultName) const { - QTC_ASSERT(!m_bundleObj3D.isEmpty(), return {}); + return getUniqueLibItemNames(defaultName, m_bundleObj3D); +} - const QJsonArray itemsArr = m_bundleObj3D.value("items").toArray(); +QPair ContentLibraryUserModel::getUniqueLibItemNames(const QString &defaultName, + const QJsonObject &bundleObj) const +{ + QTC_ASSERT(!bundleObj.isEmpty(), return {}); - QStringList itemQmls; - for (const QJsonValueConstRef &itemRef : itemsArr) - itemQmls.append(itemRef.toObject().value("qml").toString().chopped(4)); // remove .qml + const QJsonArray itemsArr = bundleObj.value("items").toArray(); - QString baseQml = defaultName.isEmpty() ? "Item" : defaultName.trimmed(); + QStringList itemQmls, itemIcons; + for (const QJsonValueConstRef &itemRef : itemsArr) { + const QJsonObject &obj = itemRef.toObject(); + itemQmls.append(obj.value("qml").toString().chopped(4)); // remove .qml + itemIcons.append(QFileInfo(obj.value("icon").toString()).baseName()); + } + + QString baseQml = defaultName.trimmed(); baseQml.remove(' '); baseQml[0] = baseQml.at(0).toUpper(); baseQml.prepend("My"); @@ -319,7 +315,11 @@ QString ContentLibraryUserModel::getUniqueLib3DQmlName(const QString &defaultNam return !itemQmls.contains(name); }); - return uniqueQml + ".qml"; + QString uniqueIcon = UniqueName::get(defaultName, [&] (const QString &name) { + return !itemIcons.contains(name); + }); + + return {uniqueQml + ".qml", uniqueIcon + ".png"}; } QHash ContentLibraryUserModel::roleNames() const @@ -364,59 +364,61 @@ void ContentLibraryUserModel::loadMaterialBundle() m_bundleObjMaterial = {}; m_bundleIdMaterial.clear(); - int matSectionIdx = 0; + int sectionIdx = 0; - QDir bundleDir{Paths::bundlesPathSetting() + "/User/materials"}; - bundleDir.mkpath("."); + m_bundlePathMaterial = Utils::FilePath::fromString(Paths::bundlesPathSetting() + "/User/materials"); + m_bundlePathMaterial.ensureWritableDir(); + m_bundlePathMaterial.pathAppended("icons").ensureWritableDir(); - auto jsonFilePath = Utils::FilePath::fromString(bundleDir.filePath("user_materials_bundle.json")); + auto jsonFilePath = m_bundlePathMaterial.pathAppended("user_materials_bundle.json"); if (!jsonFilePath.exists()) { QString jsonContent = "{\n"; jsonContent += " \"id\": \"UserMaterials\",\n"; - jsonContent += " \"materials\": {\n"; - jsonContent += " }\n"; + jsonContent += " \"items\": []\n"; jsonContent += "}"; - jsonFilePath.writeFileContents(jsonContent.toLatin1()); + Utils::expected_str res = jsonFilePath.writeFileContents(jsonContent.toLatin1()); + if (!res.has_value()) { + qWarning() << __FUNCTION__ << res.error(); + emit dataChanged(index(sectionIdx), index(sectionIdx)); + return; + } } - QFile jsonFile(jsonFilePath.path()); - if (!jsonFile.open(QIODevice::ReadOnly)) { - qWarning() << __FUNCTION__ << "Couldn't open user_materials_bundle.json"; - emit dataChanged(index(matSectionIdx), index(matSectionIdx)); + Utils::expected_str jsonContents = jsonFilePath.fileContents(); + if (!jsonContents.has_value()) { + qWarning() << __FUNCTION__ << jsonContents.error(); + emit dataChanged(index(sectionIdx), index(sectionIdx)); return; } - QJsonDocument matBundleJsonDoc = QJsonDocument::fromJson(jsonFile.readAll()); - if (matBundleJsonDoc.isNull()) { + QJsonDocument bundleJsonDoc = QJsonDocument::fromJson(jsonContents.value()); + if (bundleJsonDoc.isNull()) { qWarning() << __FUNCTION__ << "Invalid user_materials_bundle.json file"; - emit dataChanged(index(matSectionIdx), index(matSectionIdx)); + emit dataChanged(index(sectionIdx), index(sectionIdx)); return; } - m_bundleObjMaterial = matBundleJsonDoc.object(); - m_bundleObjMaterial["id"] = compUtils.userMaterialsBundleId(); m_bundleIdMaterial = compUtils.userMaterialsBundleId(); + m_bundleObjMaterial = bundleJsonDoc.object(); + m_bundleObjMaterial["id"] = m_bundleIdMaterial; - // parse materials - const QJsonObject matsObj = m_bundleObjMaterial.value("materials").toObject(); - const QStringList materialNames = matsObj.keys(); + // parse items QString typePrefix = compUtils.userMaterialsBundleType(); - for (const QString &matName : materialNames) { - const QJsonObject matObj = matsObj.value(matName).toObject(); + const QJsonArray itemsArr = m_bundleObj3D.value("items").toArray(); + for (const QJsonValueConstRef &itemRef : itemsArr) { + const QJsonObject itemObj = itemRef.toObject(); + QString name = itemObj.value("name").toString(); + QString qml = itemObj.value("qml").toString(); + TypeName type = QLatin1String("%1.%2").arg(typePrefix, qml.chopped(4)).toLatin1(); + QUrl icon = m_bundlePathMaterial.pathAppended(itemObj.value("icon").toString()).toUrl(); QStringList files; - const QJsonArray assetsArr = matObj.value("files").toArray(); + const QJsonArray assetsArr = itemObj.value("files").toArray(); for (const QJsonValueConstRef &asset : assetsArr) files.append(asset.toString()); - QUrl icon = QUrl::fromLocalFile(bundleDir.filePath(matObj.value("icon").toString())); - QString qml = matObj.value("qml").toString(); - TypeName type = QLatin1String("%1.%2").arg(typePrefix, qml.chopped(4)).toLatin1(); - - auto userMat = new ContentLibraryMaterial(this, matName, qml, type, icon, files, - bundleDir.path(), ""); - - m_userMaterials.append(userMat); + m_userMaterials.append(new ContentLibraryMaterial(this, name, qml, type, icon, files, + m_bundlePathMaterial.path(), "")); } m_bundleMaterialSharedFiles.clear(); @@ -424,13 +426,9 @@ void ContentLibraryUserModel::loadMaterialBundle() for (const QJsonValueConstRef &file : sharedFilesArr) m_bundleMaterialSharedFiles.append(file.toString()); - m_matBundleExists = true; - emit matBundleExistsChanged(); - emit dataChanged(index(matSectionIdx), index(matSectionIdx)); - m_matBundleExists = true; updateIsEmptyMaterials(); - resetModel(); + emit dataChanged(index(sectionIdx), index(sectionIdx)); } void ContentLibraryUserModel::load3DBundle() @@ -448,14 +446,13 @@ void ContentLibraryUserModel::load3DBundle() m_bundleObj3D = {}; m_bundleId3D.clear(); - int section3DIdx = 2; + int sectionIdx = 2; m_bundlePath3D = Utils::FilePath::fromString(Paths::bundlesPathSetting() + "/User/3d"); m_bundlePath3D.ensureWritableDir(); m_bundlePath3D.pathAppended("icons").ensureWritableDir(); auto jsonFilePath = m_bundlePath3D.pathAppended("user_3d_bundle.json"); - if (!jsonFilePath.exists()) { QByteArray jsonContent = "{\n"; jsonContent += " \"id\": \"User3D\",\n"; @@ -464,7 +461,7 @@ void ContentLibraryUserModel::load3DBundle() Utils::expected_str res = jsonFilePath.writeFileContents(jsonContent); if (!res.has_value()) { qWarning() << __FUNCTION__ << res.error(); - emit dataChanged(index(section3DIdx), index(section3DIdx)); + emit dataChanged(index(sectionIdx), index(sectionIdx)); return; } } @@ -472,14 +469,14 @@ void ContentLibraryUserModel::load3DBundle() Utils::expected_str jsonContents = jsonFilePath.fileContents(); if (!jsonContents.has_value()) { qWarning() << __FUNCTION__ << jsonContents.error(); - emit dataChanged(index(section3DIdx), index(section3DIdx)); + emit dataChanged(index(sectionIdx), index(sectionIdx)); return; } QJsonDocument bundleJsonDoc = QJsonDocument::fromJson(jsonContents.value()); if (bundleJsonDoc.isNull()) { qWarning() << __FUNCTION__ << "Invalid user_3d_bundle.json file"; - emit dataChanged(index(section3DIdx), index(section3DIdx)); + emit dataChanged(index(sectionIdx), index(sectionIdx)); return; } @@ -487,7 +484,7 @@ void ContentLibraryUserModel::load3DBundle() m_bundleObj3D = bundleJsonDoc.object(); m_bundleObj3D["id"] = m_bundleId3D; - // parse 3d items + // parse items QString typePrefix = compUtils.user3DBundleType(); const QJsonArray itemsArr = m_bundleObj3D.value("items").toArray(); for (const QJsonValueConstRef &itemRef : itemsArr) { @@ -512,7 +509,7 @@ void ContentLibraryUserModel::load3DBundle() m_bundle3DExists = true; updateIsEmpty3D(); - emit dataChanged(index(section3DIdx), index(section3DIdx)); + emit dataChanged(index(sectionIdx), index(sectionIdx)); } void ContentLibraryUserModel::loadTextureBundle() diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.h index 7af5e861749..e38e84e5c08 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.h +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.h @@ -43,8 +43,8 @@ public: void updateMaterialsImportedState(const QStringList &importedItems); void update3DImportedState(const QStringList &importedItems); - QPair getUniqueLibMaterialNameAndQml(const QString &defaultName = {}) const; - QString getUniqueLib3DQmlName(const QString &defaultName = {}) const; + QPair getUniqueLibMaterialNames(const QString &defaultName = "Material") const; + QPair getUniqueLib3DNames(const QString &defaultName = "Item") const; void setQuick3DImportVersion(int major, int minor); @@ -94,6 +94,8 @@ private: bool isValidIndex(int idx) const; void removeMaterialFromContentLib(ContentLibraryMaterial *mat); void remove3DFromContentLib(ContentLibraryItem *item); + QPair getUniqueLibItemNames(const QString &defaultName, + const QJsonObject &bundleObj) const; ContentLibraryWidget *m_widget = nullptr; QString m_searchText; @@ -101,6 +103,7 @@ private: QString m_bundleId3D; QStringList m_bundleMaterialSharedFiles; QStringList m_bundle3DSharedFiles; + Utils::FilePath m_bundlePathMaterial; Utils::FilePath m_bundlePath3D; QList m_userMaterials; diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp index 5d7efdf2c93..2f32a36b973 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp @@ -496,41 +496,39 @@ void ContentLibraryView::applyBundleMaterialToDropTarget(const ModelNode &bundle #endif // Add a project material to Content Library's user tab -void ContentLibraryView::addLibMaterial(const ModelNode &mat, const QPixmap &icon) +void ContentLibraryView::addLibMaterial(const ModelNode &node, const QPixmap &iconPixmap) { auto bundlePath = Utils::FilePath::fromString(Paths::bundlesPathSetting() + "/User/materials/"); - auto [name, qml] = m_widget->userModel()->getUniqueLibMaterialNameAndQml( - mat.variantProperty("objectName").value().toString()); + QString name = node.variantProperty("objectName").value().toString(); + auto [qml, icon] = m_widget->userModel()->getUniqueLibMaterialNames(node.id()); - bundlePath.pathAppended("icons").createDir(); - bundlePath.pathAppended("images").createDir(); - bundlePath.pathAppended("shaders").createDir(); - - QString iconPath = QLatin1String("icons/%1.png").arg(mat.id()); + QString iconPath = QLatin1String("icons/%1").arg(icon); QString fullIconPath = bundlePath.pathAppended(iconPath).toString(); // save icon - bool iconSaved = icon.save(fullIconPath); + bool iconSaved = iconPixmap.save(fullIconPath); if (!iconSaved) qWarning() << __FUNCTION__ << "icon save failed"; // generate and save material Qml file - const QStringList depAssets = writeLibItemQml(mat, qml); + const QStringList depAssets = writeLibItemQml(node, qml); // add the material to the bundle json QJsonObject &jsonRef = m_widget->userModel()->bundleJsonMaterialObjectRef(); - QJsonObject matsObj = jsonRef.value("materials").toObject(); - QJsonObject matObj; - matObj.insert("qml", qml); - matObj.insert("icon", iconPath); + QJsonArray itemsArr = jsonRef.value("items").toArray(); + QJsonObject itemObj; + itemObj.insert("name", name); + itemObj.insert("qml", qml); + itemObj.insert("icon", iconPath); QJsonArray filesArr; for (const QString &assetPath : depAssets) filesArr.append(assetPath); - matObj.insert("files", filesArr); + itemObj.insert("files", filesArr); + + itemsArr.append(itemObj); + jsonRef["items"] = itemsArr; - matsObj.insert(name, matObj); - jsonRef.insert("materials", matsObj); auto result = bundlePath.pathAppended("user_materials_bundle.json") .writeFileContents(QJsonDocument(jsonRef).toJson()); if (!result) @@ -538,16 +536,9 @@ void ContentLibraryView::addLibMaterial(const ModelNode &mat, const QPixmap &ico // copy material assets to bundle folder for (const QString &assetPath : depAssets) { - Asset asset(assetPath); - QString subDir; - if (asset.isImage()) - subDir = "images"; - else if (asset.isShader()) - subDir = "shaders"; - Utils::FilePath assetPathSource = DocumentManager::currentResourcePath().pathAppended(assetPath); - Utils::FilePath assetPathTarget = bundlePath.pathAppended(QString("%1/%2") - .arg(subDir, "/" + asset.fileName())); + Utils::FilePath assetPathTarget = bundlePath.pathAppended(assetPath); + assetPathTarget.parentDir().ensureWritableDir(); auto result = assetPathSource.copyFile(assetPathTarget); if (!result) @@ -667,8 +658,8 @@ void ContentLibraryView::addLib3DItem(const ModelNode &node) auto bundlePath = Utils::FilePath::fromString(Paths::bundlesPathSetting() + "/User/3d/"); QString name = node.variantProperty("objectName").value().toString(); - QString qml = m_widget->userModel()->getUniqueLib3DQmlName(node.id()); - QString iconPath = QLatin1String("icons/%1.png").arg(node.id()); // TODO: make sure path is unique + auto [qml, icon] = m_widget->userModel()->getUniqueLib3DNames(node.id()); + QString iconPath = QLatin1String("icons/%1").arg(icon); // generate and save item Qml file const QStringList depAssets = writeLibItemQml(node, qml); diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.h index 2891960f337..2041cc37a11 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.h +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.h @@ -55,7 +55,7 @@ private: bool isItemBundle(const QString &bundleId) const; void active3DSceneChanged(qint32 sceneId); void updateBundlesQuick3DVersion(); - void addLibMaterial(const ModelNode &mat, const QPixmap &icon); + void addLibMaterial(const ModelNode &node, const QPixmap &iconPixmap); void addLibAssets(const QStringList &paths); void addLib3DItem(const ModelNode &node); void genAndSaveIcon(const QString &qmlPath, const QString &iconPath); From f7a39ed57b6ce6f86358866b5b8fc70dea109a55 Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Mon, 13 May 2024 18:53:25 +0300 Subject: [PATCH 096/154] QmlDesigner: Use UniqueName util in the assets view Change-Id: I1e27cb55fd1085f36ba8b6b38829baef49040c29 Reviewed-by: Shrief Gabr Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Reviewed-by: Miikka Heikkinen --- .../assetslibrary/assetslibrarymodel.cpp | 51 +++++-------------- .../assetslibrary/assetslibrarymodel.h | 2 - .../assetslibrary/assetslibrarywidget.cpp | 24 +++------ 3 files changed, 22 insertions(+), 55 deletions(-) diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.cpp b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.cpp index 9d09f52d8ff..09ec468905d 100644 --- a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.cpp +++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.cpp @@ -3,13 +3,16 @@ #include "assetslibrarymodel.h" -#include #include #include #include + #include +#include +#include #include +#include #include #include @@ -151,16 +154,20 @@ bool AssetsLibraryModel::renameFolder(const QString &folderPath, const QString & QString AssetsLibraryModel::addNewFolder(const QString &folderPath) { - QString iterPath = folderPath; - QDir dir{folderPath}; + Utils::FilePath dir = Utils::FilePath::fromString(folderPath); + Utils::FilePath parentDir = dir.parentDir(); - while (dir.exists()) { - iterPath = getUniqueName(iterPath); + QString uniqueFolderName = UniqueName::get(dir.fileName(), [&] (const QString &folderName) { + return !parentDir.pathAppended(folderName).exists(); + }); - dir.setPath(iterPath); + auto res = parentDir.pathAppended(uniqueFolderName).ensureWritableDir(); + if (!res.has_value()) { + qWarning() << __FUNCTION__ << res.error(); + return {}; } - return dir.mkpath(iterPath) ? iterPath : ""; + return uniqueFolderName; } bool AssetsLibraryModel::urlPathExistsInModel(const QUrl &url) const @@ -242,36 +249,6 @@ void AssetsLibraryModel::syncHasFiles() setHasFiles(checkHasFiles()); } -QString AssetsLibraryModel::getUniqueName(const QString &oldName) { - static QRegularExpression rgx("\\d+$"); // matches a number at the end of a string - - QString uniqueName = oldName; - // if the folder name ends with a number, increment it - QRegularExpressionMatch match = rgx.match(uniqueName); - if (match.hasMatch()) { // ends with a number - QString numStr = match.captured(0); - int num = match.captured(0).toInt(); - - // get number of padding zeros, ex: for "005" = 2 - int nPaddingZeros = 0; - for (; nPaddingZeros < numStr.size() && numStr[nPaddingZeros] == '0'; ++nPaddingZeros); - - ++num; - - // if the incremented number's digits increased, decrease the padding zeros - if (std::fmod(std::log10(num), 1.0) == 0) - --nPaddingZeros; - - uniqueName = oldName.mid(0, match.capturedStart()) - + QString('0').repeated(nPaddingZeros) - + QString::number(num); - } else { - uniqueName = oldName + '1'; - } - - return uniqueName; -} - void AssetsLibraryModel::setRootPath(const QString &newPath) { beginResetModel(); diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.h b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.h index 2516be787fc..f08578651af 100644 --- a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.h +++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.h @@ -58,8 +58,6 @@ public: bool hasFiles() const { return m_hasFiles; } - QString getUniqueName(const QString &oldName); - signals: void directoryLoaded(const QString &path); void rootPathChanged(); diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp index 5b6a2d76127..9c46ace22d0 100644 --- a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp +++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp @@ -7,7 +7,6 @@ #include "assetslibrarymodel.h" #include "assetslibraryview.h" -#include #include #include #include @@ -25,9 +24,11 @@ #include #include +#include #include #include #include +#include #include #include @@ -174,23 +175,14 @@ void AssetsLibraryWidget::deleteSelectedAssets() QString AssetsLibraryWidget::getUniqueEffectPath(const QString &parentFolder, const QString &effectName) { - auto genEffectPath = [&parentFolder](const QString &name) { - QString effectsDir = ModelNodeOperations::getEffectsDefaultDirectory(parentFolder); - return QLatin1String("%1/%2.qep").arg(effectsDir, name); - }; + QString effectsDir = ModelNodeOperations::getEffectsDefaultDirectory(parentFolder); + QString effectPathTemplate = effectsDir + QLatin1String("/%1.qep"); - QString uniqueName = effectName; - QString path = genEffectPath(uniqueName); - QFileInfo file{path}; + QString uniqueName = UniqueName::get(effectName, [&] (const QString &name) { + return !QFile::exists(effectPathTemplate.arg(name)); + }); - while (file.exists()) { - uniqueName = m_assetsModel->getUniqueName(uniqueName); - - path = genEffectPath(uniqueName); - file.setFile(path); - } - - return path; + return effectPathTemplate.arg(uniqueName); } bool AssetsLibraryWidget::createNewEffect(const QString &effectPath, bool openInEffectComposer) From 5bf8845c423ecd0bcaceb20d097fbdb93c93a959 Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Tue, 14 May 2024 00:16:50 +0300 Subject: [PATCH 097/154] QmlDesigner: Add UniqueName::getpath() Also relevant tweaks in the same class Change-Id: I59ca0839a28478028b8aa36b9df500f29d831b90 Reviewed-by: Miikka Heikkinen --- .../assetslibrary/assetslibrarymodel.cpp | 11 +--- .../assetslibrary/assetslibrarywidget.cpp | 8 +-- src/plugins/qmldesigner/utils/uniquename.cpp | 57 +++++++++++++++---- src/plugins/qmldesigner/utils/uniquename.h | 1 + 4 files changed, 52 insertions(+), 25 deletions(-) diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.cpp b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.cpp index 09ec468905d..d3a701aa5f2 100644 --- a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.cpp +++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.cpp @@ -154,20 +154,15 @@ bool AssetsLibraryModel::renameFolder(const QString &folderPath, const QString & QString AssetsLibraryModel::addNewFolder(const QString &folderPath) { - Utils::FilePath dir = Utils::FilePath::fromString(folderPath); - Utils::FilePath parentDir = dir.parentDir(); + Utils::FilePath uniqueDirPath = Utils::FilePath::fromString(UniqueName::getPath(folderPath)); - QString uniqueFolderName = UniqueName::get(dir.fileName(), [&] (const QString &folderName) { - return !parentDir.pathAppended(folderName).exists(); - }); - - auto res = parentDir.pathAppended(uniqueFolderName).ensureWritableDir(); + auto res = uniqueDirPath.ensureWritableDir(); if (!res.has_value()) { qWarning() << __FUNCTION__ << res.error(); return {}; } - return uniqueFolderName; + return uniqueDirPath.path(); } bool AssetsLibraryModel::urlPathExistsInModel(const QUrl &url) const diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp index 9c46ace22d0..0324abe32e0 100644 --- a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp +++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp @@ -176,13 +176,9 @@ void AssetsLibraryWidget::deleteSelectedAssets() QString AssetsLibraryWidget::getUniqueEffectPath(const QString &parentFolder, const QString &effectName) { QString effectsDir = ModelNodeOperations::getEffectsDefaultDirectory(parentFolder); - QString effectPathTemplate = effectsDir + QLatin1String("/%1.qep"); + QString effectPath = QLatin1String("%1/%2.qep").arg(effectsDir, effectName); - QString uniqueName = UniqueName::get(effectName, [&] (const QString &name) { - return !QFile::exists(effectPathTemplate.arg(name)); - }); - - return effectPathTemplate.arg(uniqueName); + return UniqueName::getPath(effectPath); } bool AssetsLibraryWidget::createNewEffect(const QString &effectPath, bool openInEffectComposer) diff --git a/src/plugins/qmldesigner/utils/uniquename.cpp b/src/plugins/qmldesigner/utils/uniquename.cpp index ef6d3d8e5e2..e77814c97cf 100644 --- a/src/plugins/qmldesigner/utils/uniquename.cpp +++ b/src/plugins/qmldesigner/utils/uniquename.cpp @@ -3,17 +3,22 @@ #include "uniquename.h" +#include #include namespace QmlDesigner { /** - * @brief Gets a unique name from a give name - * @param oldName input name - * @param predicate function to check if the name is unique. Retuns true if name is unique - * @return a unique name + * @brief Generates a unique name based on the provided name. + * + * This method iteratively generates a name by appending suffixes until a unique name is found. + * The uniqueness of the generated name is determined by the provided predicate function. + * + * @param oldName The original name to be made unique. + * @param predicate A function that checks if a name is unique. Returns true if the name is unique, + * false otherwise. + * @return A unique name derived from the provided name. */ -// static QString UniqueName::get(const QString &oldName, std::function predicate) { QString newName = oldName; @@ -23,29 +28,59 @@ QString UniqueName::get(const QString &oldName, std::function predicate); + static QString getPath(const QString &oldName); private: static QString nextName(const QString &oldName); From 14a191018a924232d2bc4e802105812d5fd5e88c Mon Sep 17 00:00:00 2001 From: Burak Hancerli Date: Mon, 13 May 2024 11:41:15 +0200 Subject: [PATCH 098/154] QmlBuildSystem: Fix absolute import paths Task-number: QDS-12732 Change-Id: Ic299731e1bc9b98bc4da82dbb20f6a75299ce72d Reviewed-by: Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Thomas Hartmann --- .../buildsystem/qmlbuildsystem.cpp | 15 +++------------ .../buildsystem/qmlbuildsystem.h | 5 ++--- .../qmlprojectmanager/qmlprojectconstants.h | 1 - .../qmlprojectrunconfiguration.cpp | 6 +++--- 4 files changed, 8 insertions(+), 19 deletions(-) diff --git a/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.cpp b/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.cpp index 4e627c3368e..296c3218369 100644 --- a/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.cpp +++ b/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.cpp @@ -143,8 +143,6 @@ void QmlBuildSystem::registerMenuButtons() //wip: bool QmlBuildSystem::updateProjectFile() { - qDebug() << "debug#1-mainfilepath" << mainFilePath(); - QFile file(mainFilePath().fileName().append("project-test")); if (!file.open(QIODevice::ReadWrite | QIODevice::Truncate)) { qCritical() << "Cannot open Qml Project file for editing!"; @@ -584,7 +582,7 @@ void QmlBuildSystem::refreshFiles(const QSet & /*added*/, const QSetenvironment(); } -QStringList QmlBuildSystem::customImportPaths() const -{ - return m_projectItem->importPaths(); -} - -QStringList QmlBuildSystem::customFileSelectors() const +QStringList QmlBuildSystem::fileSelectors() const { return m_projectItem->fileSelectors(); } @@ -732,7 +723,7 @@ QStringList QmlBuildSystem::importPaths() const return m_projectItem->importPaths(); } -QStringList QmlBuildSystem::absoluteImportPaths() +QStringList QmlBuildSystem::absoluteImportPaths() const { return Utils::transform(m_projectItem->importPaths(), [&](const QString &importPath) { Utils::FilePath filePath = Utils::FilePath::fromString(importPath); diff --git a/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.h b/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.h index d7e9c071245..d91f60cdd12 100644 --- a/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.h +++ b/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.h @@ -74,9 +74,8 @@ public: Utils::EnvironmentItems environment() const; QStringList importPaths() const; - QStringList absoluteImportPaths(); - QStringList customImportPaths() const; - QStringList customFileSelectors() const; + QStringList absoluteImportPaths() const; + QStringList fileSelectors() const; bool multilanguageSupport() const; QStringList supportedLanguages() const; diff --git a/src/plugins/qmlprojectmanager/qmlprojectconstants.h b/src/plugins/qmlprojectmanager/qmlprojectconstants.h index a9379799540..ad779481056 100644 --- a/src/plugins/qmlprojectmanager/qmlprojectconstants.h +++ b/src/plugins/qmlprojectmanager/qmlprojectconstants.h @@ -13,7 +13,6 @@ const char customQtForMCUs[] = "CustomQtForMCUs"; const char customQt6Project[] = "CustomQt6Project"; const char mainFilePath[] = "MainFilePath"; -const char customImportPaths[] = "CustomImportPaths"; const char canonicalProjectDir[] ="CanonicalProjectDir"; const char enviromentLaunchedQDS[] = "QTC_LAUNCHED_QDS"; diff --git a/src/plugins/qmlprojectmanager/qmlprojectrunconfiguration.cpp b/src/plugins/qmlprojectmanager/qmlprojectrunconfiguration.cpp index 5742b945a8d..503738eba52 100644 --- a/src/plugins/qmlprojectmanager/qmlprojectrunconfiguration.cpp +++ b/src/plugins/qmlprojectmanager/qmlprojectrunconfiguration.cpp @@ -97,12 +97,12 @@ QmlProjectRunConfiguration::QmlProjectRunConfiguration(Target *target, Id id) // arguments from .qmlproject file const QmlBuildSystem *bs = qobject_cast(target->buildSystem()); - for (const QString &importPath : bs->customImportPaths()) { + for (const QString &importPath : bs->absoluteImportPaths()) { cmd.addArg("-I"); - cmd.addArg(bs->targetDirectory().pathAppended(importPath).path()); + cmd.addArg(importPath); } - for (const QString &fileSelector : bs->customFileSelectors()) { + for (const QString &fileSelector : bs->fileSelectors()) { cmd.addArg("-S"); cmd.addArg(fileSelector); } From 7ffd4805ca739c1ebe5159512b8eb8998443aaed Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Tue, 14 May 2024 12:52:29 +0300 Subject: [PATCH 099/154] QmlDesigner: Handle content library user items empty state - Make sure search is working - Hide sections with no items - Other tweaks Fixes: QDS-12682 Fixes: QDS-12625 Change-Id: Ia304743323c0459dd314752dba0cf1dc5e4c4889 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Miikka Heikkinen --- .../ContentLibraryUserView.qml | 14 +- .../contentlibrary/contentlibrarytexture.cpp | 5 + .../contentlibrary/contentlibrarytexture.h | 2 + .../contentlibraryusermodel.cpp | 163 +++++++++--------- .../contentlibrary/contentlibraryusermodel.h | 22 ++- .../contentlibrary/contentlibrarywidget.cpp | 4 - 6 files changed, 106 insertions(+), 104 deletions(-) diff --git a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryUserView.qml b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryUserView.qml index cb976fc1465..f7a210f6afb 100644 --- a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryUserView.qml +++ b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryUserView.qml @@ -92,8 +92,6 @@ HelperWidgets.ScrollView { onCountChanged: root.assignMaxCount() - property int numVisibleItem: 1 // initially, the tab is invisible so this will be 0 - Grid { width: section.width - section.leftPadding - section.rightPadding spacing: StudioTheme.Values.sectionGridSpacing @@ -114,10 +112,6 @@ HelperWidgets.ScrollView { onShowContextMenu: ctxMenuItem.popupMenu(modelData) onAddToProject: ContentLibraryBackend.userModel.addToProject(modelData) - - onVisibleChanged: { - section.numVisibleItem += visible ? 1 : -1 - } } } DelegateChoice { @@ -149,7 +143,7 @@ HelperWidgets.ScrollView { color: StudioTheme.Values.themeTextColor font.pixelSize: StudioTheme.Values.baseFontSize leftPadding: 10 - visible: !searchBox.isEmpty() && section.numVisibleItem === 0 + visible: infoText.text === "" && !searchBox.isEmpty() && categoryNoMatch } } } @@ -157,9 +151,7 @@ HelperWidgets.ScrollView { Text { id: infoText text: { - if (!ContentLibraryBackend.effectsModel.bundleExists) - qsTr("User bundle couldn't be found.") - else if (!ContentLibraryBackend.rootView.isQt6Project) + if (!ContentLibraryBackend.rootView.isQt6Project) qsTr("Content Library is not supported in Qt5 projects.") else if (!ContentLibraryBackend.rootView.hasQuick3DImport) qsTr("To use Content Library, first add the QtQuick3D module in the Components view.") @@ -172,7 +164,7 @@ HelperWidgets.ScrollView { font.pixelSize: StudioTheme.Values.baseFontSize topPadding: 10 leftPadding: 10 - visible: ContentLibraryBackend.effectsModel.isEmpty + visible: infoText.text !== "" } } } diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexture.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexture.cpp index 80dd7e816f8..d2c2df2baaf 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexture.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexture.cpp @@ -120,6 +120,11 @@ void ContentLibraryTexture::doSetDownloaded() m_toolTip = resolveToolTipText(); } +bool ContentLibraryTexture::visible() const +{ + return m_visible; +} + QString ContentLibraryTexture::parentDirPath() const { return m_dirPath; diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexture.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexture.h index 8f7197bc72e..7f5db6d7d60 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexture.h +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexture.h @@ -46,6 +46,8 @@ public: void setHasUpdate(bool value); bool hasUpdate() const; + bool visible() const; + signals: void textureVisibleChanged(); void textureToolTipChanged(); diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp index bb732080d7e..ca90c9c4517 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp @@ -46,18 +46,37 @@ QVariant ContentLibraryUserModel::data(const QModelIndex &index, int role) const return m_userCategories.at(index.row()); if (role == ItemsRole) { - if (index.row() == 0) + if (index.row() == MaterialsSectionIdx) return QVariant::fromValue(m_userMaterials); - if (index.row() == 1) + if (index.row() == TexturesSectionIdx) return QVariant::fromValue(m_userTextures); - if (index.row() == 2) + if (index.row() == Items3DSectionIdx) return QVariant::fromValue(m_user3DItems); - if (index.row() == 3) + if (index.row() == EffectsSectionIdx) return QVariant::fromValue(m_userEffects); } - if (role == VisibleRole) - return true; // TODO + if (role == NoMatchRole) { + if (index.row() == MaterialsSectionIdx) + return m_noMatchMaterials; + if (index.row() == TexturesSectionIdx) + return m_noMatchTextures; + if (index.row() == Items3DSectionIdx) + return m_noMatch3D; + if (index.row() == EffectsSectionIdx) + return m_noMatchEffects; + } + + if (role == VisibleRole) { + if (index.row() == MaterialsSectionIdx) + return !m_userMaterials.isEmpty(); + if (index.row() == TexturesSectionIdx) + return !m_userTextures.isEmpty(); + if (index.row() == Items3DSectionIdx) + return !m_user3DItems.isEmpty(); + if (index.row() == EffectsSectionIdx) + return !m_userEffects.isEmpty(); + } return {}; } @@ -67,32 +86,25 @@ bool ContentLibraryUserModel::isValidIndex(int idx) const return idx > -1 && idx < rowCount(); } -void ContentLibraryUserModel::updateIsEmptyMaterials() +void ContentLibraryUserModel::updateNoMatchMaterials() { - bool anyMatVisible = Utils::anyOf(m_userMaterials, [&](ContentLibraryMaterial *mat) { - return mat->visible(); + m_noMatchMaterials = Utils::allOf(m_userMaterials, [&](ContentLibraryMaterial *item) { + return !item->visible(); }); - - bool newEmpty = !anyMatVisible || !m_widget->hasMaterialLibrary() || !hasRequiredQuick3DImport(); - - if (newEmpty != m_isEmptyMaterials) { - m_isEmptyMaterials = newEmpty; - emit isEmptyMaterialsChanged(); - } } -void ContentLibraryUserModel::updateIsEmpty3D() +void ContentLibraryUserModel::updateNoMatchTextures() { - bool anyItemVisible = Utils::anyOf(m_user3DItems, [&](ContentLibraryItem *item) { - return item->visible(); + m_noMatchTextures = Utils::allOf(m_userTextures, [&](ContentLibraryTexture *item) { + return !item->visible(); }); +} - bool newEmpty = !anyItemVisible || !m_widget->hasMaterialLibrary() || !hasRequiredQuick3DImport(); - - if (newEmpty != m_isEmpty3D) { - m_isEmpty3D = newEmpty; - emit isEmpty3DChanged(); - } +void ContentLibraryUserModel::updateNoMatch3D() +{ + m_noMatch3D = Utils::allOf(m_user3DItems, [&](ContentLibraryItem *item) { + return !item->visible(); + }); } void ContentLibraryUserModel::addMaterial(const QString &name, const QString &qml, @@ -107,8 +119,7 @@ void ContentLibraryUserModel::addMaterial(const QString &name, const QString &qm Paths::bundlesPathSetting().append("/User/materials")); m_userMaterials.append(libMat); - int matSectionIdx = 0; - emit dataChanged(index(matSectionIdx), index(matSectionIdx)); + emit dataChanged(index(MaterialsSectionIdx), index(MaterialsSectionIdx)); } void ContentLibraryUserModel::add3DItem(const QString &name, const QString &qml, @@ -124,8 +135,7 @@ void ContentLibraryUserModel::add3DItem(const QString &name, const QString &qml, void ContentLibraryUserModel::refresh3DSection() { - int sectionIdx = 2; - emit dataChanged(index(sectionIdx), index(sectionIdx)); + emit dataChanged(index(Items3DSectionIdx), index(Items3DSectionIdx)); } void ContentLibraryUserModel::addTextures(const QStringList &paths) @@ -147,20 +157,19 @@ void ContentLibraryUserModel::addTextures(const QStringList &paths) m_userTextures.append(tex); } - int texSectionIdx = 1; - emit dataChanged(index(texSectionIdx), index(texSectionIdx)); + emit dataChanged(index(TexturesSectionIdx), index(TexturesSectionIdx)); } void ContentLibraryUserModel::add3DInstance(ContentLibraryItem *bundleItem) { - QString err = m_widget->importer()->importComponent(m_bundlePath3D.path(), bundleItem->type(), - bundleItem->qml(), - bundleItem->files() + m_bundle3DSharedFiles); + QString err = m_widget->importer()->importComponent(m_bundlePath3D.path(), bundleItem->type(), + bundleItem->qml(), + bundleItem->files() + m_bundle3DSharedFiles); - if (err.isEmpty()) - m_widget->setImporterRunning(true); - else - qWarning() << __FUNCTION__ << err; + if (err.isEmpty()) + m_widget->setImporterRunning(true); + else + qWarning() << __FUNCTION__ << err; } void ContentLibraryUserModel::removeTexture(ContentLibraryTexture *tex) @@ -174,8 +183,7 @@ void ContentLibraryUserModel::removeTexture(ContentLibraryTexture *tex) tex->deleteLater(); // update model - int texSectionIdx = 1; - emit dataChanged(index(texSectionIdx), index(texSectionIdx)); + emit dataChanged(index(TexturesSectionIdx), index(TexturesSectionIdx)); } void ContentLibraryUserModel::removeFromContentLib(QObject *item) @@ -226,8 +234,7 @@ void ContentLibraryUserModel::removeMaterialFromContentLib(ContentLibraryMateria item->deleteLater(); // update model - int sectionIdx = 0; - emit dataChanged(index(sectionIdx), index(sectionIdx)); + emit dataChanged(index(MaterialsSectionIdx), index(MaterialsSectionIdx)); } void ContentLibraryUserModel::remove3DFromContentLib(ContentLibraryItem *item) @@ -268,8 +275,7 @@ void ContentLibraryUserModel::remove3DFromContentLib(ContentLibraryItem *item) item->deleteLater(); // update model - int sectionIdx = 2; - emit dataChanged(index(sectionIdx), index(sectionIdx)); + emit dataChanged(index(Items3DSectionIdx), index(Items3DSectionIdx)); } /** @@ -327,7 +333,8 @@ QHash ContentLibraryUserModel::roleNames() const static const QHash roles { {NameRole, "categoryName"}, {VisibleRole, "categoryVisible"}, - {ItemsRole, "categoryItems"} + {ItemsRole, "categoryItems"}, + {NoMatchRole, "categoryNoMatch"} }; return roles; } @@ -352,7 +359,6 @@ void ContentLibraryUserModel::loadBundles() void ContentLibraryUserModel::loadMaterialBundle() { auto compUtils = QmlDesignerPlugin::instance()->documentManager().generatedComponentUtils(); - if (m_matBundleExists && m_bundleIdMaterial == compUtils.userMaterialsBundleId()) return; @@ -360,12 +366,10 @@ void ContentLibraryUserModel::loadMaterialBundle() qDeleteAll(m_userMaterials); m_userMaterials.clear(); m_matBundleExists = false; - m_isEmptyMaterials = true; + m_noMatchMaterials = true; m_bundleObjMaterial = {}; m_bundleIdMaterial.clear(); - int sectionIdx = 0; - m_bundlePathMaterial = Utils::FilePath::fromString(Paths::bundlesPathSetting() + "/User/materials"); m_bundlePathMaterial.ensureWritableDir(); m_bundlePathMaterial.pathAppended("icons").ensureWritableDir(); @@ -379,7 +383,7 @@ void ContentLibraryUserModel::loadMaterialBundle() Utils::expected_str res = jsonFilePath.writeFileContents(jsonContent.toLatin1()); if (!res.has_value()) { qWarning() << __FUNCTION__ << res.error(); - emit dataChanged(index(sectionIdx), index(sectionIdx)); + emit dataChanged(index(MaterialsSectionIdx), index(MaterialsSectionIdx)); return; } } @@ -387,14 +391,14 @@ void ContentLibraryUserModel::loadMaterialBundle() Utils::expected_str jsonContents = jsonFilePath.fileContents(); if (!jsonContents.has_value()) { qWarning() << __FUNCTION__ << jsonContents.error(); - emit dataChanged(index(sectionIdx), index(sectionIdx)); + emit dataChanged(index(MaterialsSectionIdx), index(MaterialsSectionIdx)); return; } QJsonDocument bundleJsonDoc = QJsonDocument::fromJson(jsonContents.value()); if (bundleJsonDoc.isNull()) { qWarning() << __FUNCTION__ << "Invalid user_materials_bundle.json file"; - emit dataChanged(index(sectionIdx), index(sectionIdx)); + emit dataChanged(index(MaterialsSectionIdx), index(MaterialsSectionIdx)); return; } @@ -404,7 +408,7 @@ void ContentLibraryUserModel::loadMaterialBundle() // parse items QString typePrefix = compUtils.userMaterialsBundleType(); - const QJsonArray itemsArr = m_bundleObj3D.value("items").toArray(); + const QJsonArray itemsArr = m_bundleObjMaterial.value("items").toArray(); for (const QJsonValueConstRef &itemRef : itemsArr) { const QJsonObject itemObj = itemRef.toObject(); @@ -427,8 +431,8 @@ void ContentLibraryUserModel::loadMaterialBundle() m_bundleMaterialSharedFiles.append(file.toString()); m_matBundleExists = true; - updateIsEmptyMaterials(); - emit dataChanged(index(sectionIdx), index(sectionIdx)); + updateNoMatchMaterials(); + emit dataChanged(index(MaterialsSectionIdx), index(MaterialsSectionIdx)); } void ContentLibraryUserModel::load3DBundle() @@ -442,12 +446,10 @@ void ContentLibraryUserModel::load3DBundle() qDeleteAll(m_user3DItems); m_user3DItems.clear(); m_bundle3DExists = false; - m_isEmpty3D = true; + m_noMatch3D = true; m_bundleObj3D = {}; m_bundleId3D.clear(); - int sectionIdx = 2; - m_bundlePath3D = Utils::FilePath::fromString(Paths::bundlesPathSetting() + "/User/3d"); m_bundlePath3D.ensureWritableDir(); m_bundlePath3D.pathAppended("icons").ensureWritableDir(); @@ -461,7 +463,7 @@ void ContentLibraryUserModel::load3DBundle() Utils::expected_str res = jsonFilePath.writeFileContents(jsonContent); if (!res.has_value()) { qWarning() << __FUNCTION__ << res.error(); - emit dataChanged(index(sectionIdx), index(sectionIdx)); + emit dataChanged(index(Items3DSectionIdx), index(Items3DSectionIdx)); return; } } @@ -469,14 +471,14 @@ void ContentLibraryUserModel::load3DBundle() Utils::expected_str jsonContents = jsonFilePath.fileContents(); if (!jsonContents.has_value()) { qWarning() << __FUNCTION__ << jsonContents.error(); - emit dataChanged(index(sectionIdx), index(sectionIdx)); + emit dataChanged(index(Items3DSectionIdx), index(Items3DSectionIdx)); return; } QJsonDocument bundleJsonDoc = QJsonDocument::fromJson(jsonContents.value()); if (bundleJsonDoc.isNull()) { qWarning() << __FUNCTION__ << "Invalid user_3d_bundle.json file"; - emit dataChanged(index(sectionIdx), index(sectionIdx)); + emit dataChanged(index(Items3DSectionIdx), index(Items3DSectionIdx)); return; } @@ -508,8 +510,8 @@ void ContentLibraryUserModel::load3DBundle() m_bundle3DSharedFiles.append(file.toString()); m_bundle3DExists = true; - updateIsEmpty3D(); - emit dataChanged(index(sectionIdx), index(sectionIdx)); + updateNoMatch3D(); + emit dataChanged(index(Items3DSectionIdx), index(Items3DSectionIdx)); } void ContentLibraryUserModel::loadTextureBundle() @@ -534,8 +536,8 @@ void ContentLibraryUserModel::loadTextureBundle() m_userTextures.append(tex); } - int texSectionIdx = 1; - emit dataChanged(index(texSectionIdx), index(texSectionIdx)); + updateNoMatchTextures(); + emit dataChanged(index(TexturesSectionIdx), index(TexturesSectionIdx)); } bool ContentLibraryUserModel::hasRequiredQuick3DImport() const @@ -557,11 +559,19 @@ void ContentLibraryUserModel::setSearchText(const QString &searchText) m_searchText = lowerSearchText; - for (ContentLibraryMaterial *mat : std::as_const(m_userMaterials)) - mat->filter(m_searchText); + for (ContentLibraryMaterial *item : std::as_const(m_userMaterials)) + item->filter(m_searchText); - updateIsEmptyMaterials(); - updateIsEmpty3D(); + for (ContentLibraryTexture *item : std::as_const(m_userTextures)) + item->filter(m_searchText); + + for (ContentLibraryItem *item : std::as_const(m_user3DItems)) + item->filter(m_searchText); + + updateNoMatchMaterials(); + updateNoMatchTextures(); + updateNoMatch3D(); + resetModel(); } void ContentLibraryUserModel::updateMaterialsImportedState(const QStringList &importedItems) @@ -570,10 +580,8 @@ void ContentLibraryUserModel::updateMaterialsImportedState(const QStringList &im for (ContentLibraryMaterial *mat : std::as_const(m_userMaterials)) changed |= mat->setImported(importedItems.contains(mat->qml().chopped(4))); - if (changed) { - int matSectionIdx = 0; - emit dataChanged(index(matSectionIdx), index(matSectionIdx)); - } + if (changed) + emit dataChanged(index(MaterialsSectionIdx), index(MaterialsSectionIdx)); } void ContentLibraryUserModel::update3DImportedState(const QStringList &importedItems) @@ -582,10 +590,8 @@ void ContentLibraryUserModel::update3DImportedState(const QStringList &importedI for (ContentLibraryItem *item : std::as_const(m_user3DItems)) changed |= item->setImported(importedItems.contains(item->qml().chopped(4))); - if (changed) { - int sectionIdx = 2; - emit dataChanged(index(sectionIdx), index(sectionIdx)); - } + if (changed) + emit dataChanged(index(Items3DSectionIdx), index(Items3DSectionIdx)); } void ContentLibraryUserModel::setQuick3DImportVersion(int major, int minor) @@ -601,9 +607,6 @@ void ContentLibraryUserModel::setQuick3DImportVersion(int major, int minor) return; emit hasRequiredQuick3DImportChanged(); - - updateIsEmptyMaterials(); - updateIsEmpty3D(); } void ContentLibraryUserModel::resetModel() diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.h index e38e84e5c08..67dc36b98e5 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.h +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.h @@ -24,8 +24,6 @@ class ContentLibraryUserModel : public QAbstractListModel Q_PROPERTY(bool matBundleExists READ matBundleExists NOTIFY matBundleExistsChanged) Q_PROPERTY(bool bundle3DExists MEMBER m_bundle3DExists NOTIFY bundle3DExistsChanged) - Q_PROPERTY(bool isEmptyMaterials MEMBER m_isEmptyMaterials NOTIFY isEmptyMaterialsChanged) - Q_PROPERTY(bool isEmpty3D MEMBER m_isEmpty3D NOTIFY isEmpty3DChanged) Q_PROPERTY(bool hasRequiredQuick3DImport READ hasRequiredQuick3DImport NOTIFY hasRequiredQuick3DImportChanged) Q_PROPERTY(QList userMaterials MEMBER m_userMaterials NOTIFY userMaterialsChanged) Q_PROPERTY(QList userTextures MEMBER m_userTextures NOTIFY userTexturesChanged) @@ -53,8 +51,9 @@ public: bool matBundleExists() const; void resetModel(); - void updateIsEmptyMaterials(); - void updateIsEmpty3D(); + void updateNoMatchMaterials(); + void updateNoMatchTextures(); + void updateNoMatch3D(); void addMaterial(const QString &name, const QString &qml, const QUrl &icon, const QStringList &files); void add3DItem(const QString &name, const QString &qml, const QUrl &icon, const QStringList &files); @@ -76,8 +75,6 @@ public: Q_INVOKABLE void removeFromContentLib(QObject *item); signals: - void isEmptyMaterialsChanged(); - void isEmpty3DChanged(); void hasRequiredQuick3DImportChanged(); void userMaterialsChanged(); void userTexturesChanged(); @@ -88,6 +85,11 @@ signals: void bundle3DExistsChanged(); private: + enum SectionIndex { MaterialsSectionIdx = 0, + TexturesSectionIdx, + Items3DSectionIdx, + EffectsSectionIdx }; + void loadMaterialBundle(); void load3DBundle(); void loadTextureBundle(); @@ -115,15 +117,17 @@ private: QJsonObject m_bundleObjMaterial; QJsonObject m_bundleObj3D; - bool m_isEmptyMaterials = true; - bool m_isEmpty3D = true; + bool m_noMatchMaterials = true; + bool m_noMatchTextures = true; + bool m_noMatch3D = true; + bool m_noMatchEffects = true; bool m_matBundleExists = false; bool m_bundle3DExists = false; int m_quick3dMajorVersion = -1; int m_quick3dMinorVersion = -1; - enum Roles { NameRole = Qt::UserRole + 1, VisibleRole, ItemsRole }; + enum Roles { NameRole = Qt::UserRole + 1, VisibleRole, ItemsRole, NoMatchRole }; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.cpp index 4d09eed05fc..3edad3d11dd 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.cpp @@ -699,8 +699,6 @@ void ContentLibraryWidget::setHasQuick3DImport(bool b) m_materialsModel->updateIsEmpty(); m_effectsModel->updateIsEmpty(); - m_userModel->updateIsEmptyMaterials(); - m_userModel->updateIsEmpty3D(); } bool ContentLibraryWidget::hasMaterialLibrary() const @@ -717,8 +715,6 @@ void ContentLibraryWidget::setHasMaterialLibrary(bool b) emit hasMaterialLibraryChanged(); m_materialsModel->updateIsEmpty(); - m_userModel->updateIsEmptyMaterials(); - m_userModel->updateIsEmpty3D(); } bool ContentLibraryWidget::hasActive3DScene() const From 22657af3f0d1df073c1a697d59f45dc3e2bf3c0a Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Mon, 13 May 2024 15:08:25 +0200 Subject: [PATCH 100/154] QmlDesigner: Use reverse iterators to support qualified scopes Change-Id: I88a4dcbff7a50f399b81a948f273e9cf9e36782d Reviewed-by: Marco Bubke --- src/libs/qmlpuppetcommunication/types/enumeration.h | 13 ++++++++----- .../propertyeditor/propertyeditorvalue.cpp | 2 +- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/libs/qmlpuppetcommunication/types/enumeration.h b/src/libs/qmlpuppetcommunication/types/enumeration.h index 57bfe6c0ef4..5fe2294621e 100644 --- a/src/libs/qmlpuppetcommunication/types/enumeration.h +++ b/src/libs/qmlpuppetcommunication/types/enumeration.h @@ -43,17 +43,20 @@ public: EnumerationNameView scope() const { - auto found = std::find(m_enumerationName.begin(), m_enumerationName.end(), '.'); - return {m_enumerationName.begin(), found}; + auto found = std::find(m_enumerationName.rbegin(), m_enumerationName.rend(), '.'); + if (found != m_enumerationName.rend()) + return {m_enumerationName.begin(), std::prev(found.base())}; + + return {m_enumerationName.end(), m_enumerationName.end()}; } EnumerationNameView toScope() const { return scope().toByteArray(); } EnumerationNameView name() const { - auto found = std::find(m_enumerationName.begin(), m_enumerationName.end(), '.'); - if (found != m_enumerationName.end()) - return {std::next(found), m_enumerationName.end()}; + auto found = std::find(m_enumerationName.rbegin(), m_enumerationName.rend(), '.'); + if (found != m_enumerationName.rend()) + return {found.base(), m_enumerationName.end()}; return {m_enumerationName.end(), m_enumerationName.end()}; } diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.cpp b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.cpp index 412db1055fa..f8e1711d7df 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.cpp @@ -142,7 +142,7 @@ void PropertyEditorValue::setValue(const QVariant &value) QString PropertyEditorValue::enumeration() const { - return m_value.value().nameToString().split('.').last(); + return m_value.value().nameToString(); } QString PropertyEditorValue::expression() const From c2778b2301ef52c9f721e355f6734acaba4dc8d0 Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Tue, 14 May 2024 14:59:55 +0300 Subject: [PATCH 101/154] QmlDesigner: Don't override existing content lib user textures Better solution to ask for overriding or not is for the future. Fixes: QDS-12626 Change-Id: Ib147121d3b9cb783f45a79a96fa432d371750a0e Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Miikka Heikkinen --- .../contentlibrary/contentlibraryview.cpp | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp index 2f32a36b973..d010bc76ba3 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp @@ -631,19 +631,28 @@ void ContentLibraryView::addLibAssets(const QStringList &paths) auto bundlePath = Utils::FilePath::fromString(Paths::bundlesPathSetting() + "/User/textures"); QStringList pathsInBundle; + const QStringList existingTextures = Utils::transform(bundlePath.dirEntries(QDir::Files), + [](const Utils::FilePath &path) { + return path.fileName(); + }); + for (const QString &path : paths) { + auto assetFilePath = Utils::FilePath::fromString(path); + if (existingTextures.contains(assetFilePath.fileName())) + continue; + Asset asset(path); - auto assetPath = Utils::FilePath::fromString(path); // save icon - QString iconSavePath = bundlePath.pathAppended("icons/" + assetPath.baseName() + ".png").toString(); + QString iconSavePath = bundlePath.pathAppended("icons/" + assetFilePath.baseName() + ".png") + .toString(); QPixmap icon = asset.pixmap({120, 120}); bool iconSaved = icon.save(iconSavePath); if (!iconSaved) qWarning() << __FUNCTION__ << "icon save failed"; // save asset - auto result = assetPath.copyFile(bundlePath.pathAppended(asset.fileName())); + auto result = assetFilePath.copyFile(bundlePath.pathAppended(asset.fileName())); if (!result) qWarning() << __FUNCTION__ << result.error(); From bfbcf8816124e5ed3e52a03478e88fb4d042e227 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Tue, 14 May 2024 15:41:50 +0200 Subject: [PATCH 102/154] QmlDesigner: Fix crash on exit Change-Id: I26f3c1154d8cdef9cf76b6f608634067b099ea0e Reviewed-by: Marco Bubke Reviewed-by: Mahmoud Badri --- .../components/materialeditor/materialeditorview.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp b/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp index ce11c587584..02d9333e09c 100644 --- a/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp +++ b/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp @@ -740,7 +740,8 @@ void MaterialEditorView::modelAboutToBeDetached(Model *model) { AbstractView::modelAboutToBeDetached(model); m_dynamicPropertiesModel->reset(); - m_qmlBackEnd->materialEditorTransaction()->end(); + if (auto transaction = m_qmlBackEnd->materialEditorTransaction()) + transaction->end(); m_qmlBackEnd->contextObject()->setHasMaterialLibrary(false); m_selectedMaterial = {}; } From f462be8e80f0d2589f467a16321147220e753a0e Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Tue, 14 May 2024 16:35:11 +0300 Subject: [PATCH 103/154] QmlDesigner: Prevent adding component materials to the content lib Also remove the experimental restriction on the content lib user section Fixes: QDS-12637 Change-Id: Ice2ac3f32a934d7c65ea7f406a2b5f8c4b5fed0c Reviewed-by: Miikka Heikkinen --- .../AssetsContextMenu.qml | 2 +- .../contentLibraryQmlSource/ContentLibrary.qml | 18 +++++++----------- .../MaterialBrowserContextMenu.qml | 12 +++++------- .../assetslibrary/assetslibrarywidget.cpp | 6 ------ .../assetslibrary/assetslibrarywidget.h | 1 - .../contentlibrary/contentlibrarywidget.cpp | 6 ------ .../contentlibrary/contentlibrarywidget.h | 1 - .../materialbrowser/materialbrowsermodel.cpp | 3 +++ .../materialbrowser/materialbrowsermodel.h | 5 ++++- .../materialbrowser/materialbrowserwidget.cpp | 6 ------ .../materialbrowser/materialbrowserwidget.h | 1 - 11 files changed, 20 insertions(+), 41 deletions(-) diff --git a/share/qtcreator/qmldesigner/assetsLibraryQmlSources/AssetsContextMenu.qml b/share/qtcreator/qmldesigner/assetsLibraryQmlSources/AssetsContextMenu.qml index a8eb5285f57..fe22d7ce51f 100644 --- a/share/qtcreator/qmldesigner/assetsLibraryQmlSources/AssetsContextMenu.qml +++ b/share/qtcreator/qmldesigner/assetsLibraryQmlSources/AssetsContextMenu.qml @@ -249,7 +249,7 @@ StudioControls.Menu { StudioControls.MenuItem { text: qsTr("Add to Content Library") - visible: root.rootView.userBundleEnabled() && root.__fileIndex && root.assetsModel.allFilePathsAreTextures(root.__selectedAssetPathsList) + visible: root.__fileIndex && root.assetsModel.allFilePathsAreTextures(root.__selectedAssetPathsList) height: visible ? implicitHeight : 0 onTriggered: root.rootView.addAssetsToContentLibrary(root.__selectedAssetPathsList) } diff --git a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibrary.qml b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibrary.qml index d06d942e3f9..d3eb29563d2 100644 --- a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibrary.qml +++ b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibrary.qml @@ -115,17 +115,13 @@ Item { width: parent.width height: StudioTheme.Values.toolbarHeight - Component.onCompleted: { - var tabs = [ - { name: qsTr("Materials"), icon: StudioTheme.Constants.material_medium }, - { name: qsTr("Textures"), icon: StudioTheme.Constants.textures_medium }, - { name: qsTr("Environments"), icon: StudioTheme.Constants.languageList_medium }, - { name: qsTr("Effects"), icon: StudioTheme.Constants.effects } - ]; - if (ContentLibraryBackend.rootView.userBundleEnabled()) - tabs.push({ name: qsTr("User Assets"), icon: StudioTheme.Constants.effects }); - tabBar.tabsModel = tabs; - } + tabsModel: [ + { name: qsTr("Materials"), icon: StudioTheme.Constants.material_medium }, + { name: qsTr("Textures"), icon: StudioTheme.Constants.textures_medium }, + { name: qsTr("Environments"), icon: StudioTheme.Constants.languageList_medium }, + { name: qsTr("Effects"), icon: StudioTheme.Constants.effects }, + { name: qsTr("User Assets"), icon: StudioTheme.Constants.effects } // TODO: update icon + ] } } } diff --git a/share/qtcreator/qmldesigner/materialBrowserQmlSource/MaterialBrowserContextMenu.qml b/share/qtcreator/qmldesigner/materialBrowserQmlSource/MaterialBrowserContextMenu.qml index b01aa7d2a3c..248e07bf6c4 100644 --- a/share/qtcreator/qmldesigner/materialBrowserQmlSource/MaterialBrowserContextMenu.qml +++ b/share/qtcreator/qmldesigner/materialBrowserQmlSource/MaterialBrowserContextMenu.qml @@ -128,12 +128,10 @@ StudioControls.Menu { onTriggered: materialBrowserModel.addNewMaterial() } - Component.onCompleted: { - if (MaterialBrowserBackend.rootView.userBundleEnabled()) { - var menuItem = Qt.createQmlObject("import StudioControls as StudioControls; StudioControls.MenuItem {}", root) - menuItem.text = qsTr("Add to Content Library") - menuItem.onTriggered.connect(MaterialBrowserBackend.rootView.addMaterialToContentLibrary) - root.addItem(menuItem) - } + StudioControls.MenuItem { + text: qsTr("Add to Content Library") + enabled: !materialBrowserModel.selectedMaterialIsComponent + + onTriggered: MaterialBrowserBackend.rootView.addMaterialToContentLibrary() } } diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp index 0324abe32e0..e63ac9b0d5a 100644 --- a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp +++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp @@ -635,12 +635,6 @@ void AssetsLibraryWidget::addResources(const QStringList &files, bool showDialog } } -bool AssetsLibraryWidget::userBundleEnabled() const -{ - // TODO: this method is to be removed after user bundle implementation is complete - return Core::ICore::settings()->value("QML/Designer/UseExperimentalFeatures45", false).toBool(); -} - void AssetsLibraryWidget::addAssetsToContentLibrary(const QStringList &assetPaths) { m_assetsView->emitCustomNotification("add_assets_to_content_lib", {}, {assetPaths}); diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.h b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.h index 9e97080d4ac..f2d476c8427 100644 --- a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.h +++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.h @@ -100,7 +100,6 @@ public: Q_INVOKABLE void showInGraphicalShell(const QString &path); Q_INVOKABLE QString showInGraphicalShellMsg() const; - Q_INVOKABLE bool userBundleEnabled() const; Q_INVOKABLE void addAssetsToContentLibrary(const QStringList &assetPaths); signals: diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.cpp index 3edad3d11dd..72bece4c98b 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.cpp @@ -640,12 +640,6 @@ void ContentLibraryWidget::markTextureUpdated(const QString &textureKey) m_environmentsModel->markTextureHasNoUpdates(subcategory, textureKey); } -bool ContentLibraryWidget::userBundleEnabled() const -{ - // TODO: this method is to be removed after user bundle implementation is complete - return Core::ICore::settings()->value("QML/Designer/UseExperimentalFeatures45", false).toBool(); -} - QSize ContentLibraryWidget::sizeHint() const { return {420, 420}; diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.h index a617347f6f8..8e96d9d2f3b 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.h +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.h @@ -90,7 +90,6 @@ public: Q_INVOKABLE void addLightProbe(QmlDesigner::ContentLibraryTexture *tex); Q_INVOKABLE void updateSceneEnvState(); Q_INVOKABLE void markTextureUpdated(const QString &textureKey); - Q_INVOKABLE bool userBundleEnabled() const; QSize sizeHint() const override; diff --git a/src/plugins/qmldesigner/components/materialbrowser/materialbrowsermodel.cpp b/src/plugins/qmldesigner/components/materialbrowser/materialbrowsermodel.cpp index 2fcd56b6697..b74f3107412 100644 --- a/src/plugins/qmldesigner/components/materialbrowser/materialbrowsermodel.cpp +++ b/src/plugins/qmldesigner/components/materialbrowser/materialbrowsermodel.cpp @@ -366,6 +366,9 @@ void MaterialBrowserModel::selectMaterial(int idx, bool force) if (idx != m_selectedIndex || force) { m_selectedIndex = idx; emit selectedIndexChanged(idx); + + m_selectedMaterialIsComponent = selectedMaterial().isComponent(); + emit selectedMaterialIsComponentChanged(); } } diff --git a/src/plugins/qmldesigner/components/materialbrowser/materialbrowsermodel.h b/src/plugins/qmldesigner/components/materialbrowser/materialbrowsermodel.h index 337dce05507..3b6b64ec865 100644 --- a/src/plugins/qmldesigner/components/materialbrowser/materialbrowsermodel.h +++ b/src/plugins/qmldesigner/components/materialbrowser/materialbrowsermodel.h @@ -3,7 +3,7 @@ #pragma once -#include "modelnode.h" +#include #include #include @@ -20,6 +20,7 @@ class MaterialBrowserModel : public QAbstractListModel Q_PROPERTY(bool isEmpty MEMBER m_isEmpty NOTIFY isEmptyChanged) Q_PROPERTY(int selectedIndex MEMBER m_selectedIndex NOTIFY selectedIndexChanged) + Q_PROPERTY(bool selectedMaterialIsComponent MEMBER m_selectedMaterialIsComponent NOTIFY selectedMaterialIsComponentChanged) Q_PROPERTY(bool hasQuick3DImport READ hasQuick3DImport WRITE setHasQuick3DImport NOTIFY hasQuick3DImportChanged) Q_PROPERTY(bool hasModelSelection READ hasModelSelection WRITE setHasModelSelection NOTIFY hasModelSelectionChanged) Q_PROPERTY(bool hasMaterialLibrary READ hasMaterialLibrary WRITE setHasMaterialLibrary NOTIFY hasMaterialLibraryChanged) @@ -110,6 +111,7 @@ signals: const QList &props, bool all); void isQt6ProjectChanged(); + void selectedMaterialIsComponentChanged(); private: bool isValidIndex(int idx) const; @@ -132,6 +134,7 @@ private: bool m_hasMaterialLibrary = false; bool m_allPropsCopied = true; bool m_isQt6Project = false; + bool m_selectedMaterialIsComponent = false; QString m_copiedMaterialType; QPointer m_view; diff --git a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.cpp b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.cpp index 42268ee6fd1..8723611be07 100644 --- a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.cpp +++ b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.cpp @@ -434,10 +434,4 @@ QPointer MaterialBrowserWidget::materialBrowserTex return m_materialBrowserTexturesModel; } -bool MaterialBrowserWidget::userBundleEnabled() const -{ - // TODO: this method is to be removed after user bundle implementation is complete - return Core::ICore::settings()->value("QML/Designer/UseExperimentalFeatures45", false).toBool(); -} - } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.h b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.h index c55d72c8f73..6506283f858 100644 --- a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.h +++ b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.h @@ -63,7 +63,6 @@ public: Q_INVOKABLE void acceptTextureDropOnMaterial(int matIndex, const QString &texId); Q_INVOKABLE void focusMaterialSection(bool focusMatSec); Q_INVOKABLE void addMaterialToContentLibrary(); - Q_INVOKABLE bool userBundleEnabled() const; StudioQuickWidget *quickWidget() const; From 2376e008f7d3bdb69e7e5ebe5b91d1c7dbeae68e Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Tue, 14 May 2024 16:52:02 +0300 Subject: [PATCH 104/154] QmlDesigner: Update 3d user bundle imported state on load Change-Id: I8df19486523cfd25db832ed78f0792095548dfea Reviewed-by: Miikka Heikkinen Reviewed-by: Qt CI Patch Build Bot --- .../qmldesigner/components/contentlibrary/contentlibraryview.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp index d010bc76ba3..8e5b5c4e3b3 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp @@ -265,6 +265,7 @@ void ContentLibraryView::modelAttached(Model *model) m_widget->updateImportedState(compUtils.materialsBundleId()); m_widget->updateImportedState(compUtils.effectsBundleId()); m_widget->updateImportedState(compUtils.userMaterialsBundleId()); + m_widget->updateImportedState(compUtils.user3DBundleId()); } void ContentLibraryView::modelAboutToBeDetached(Model *model) From 76ec1246980ae026ed541c0d1ccf9e7eac699aab Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Tue, 14 May 2024 15:27:20 +0200 Subject: [PATCH 105/154] QmlDesigner: Fix compilation error Change-Id: Ia49491a7deae085460fe4498cbcc6727cc10db7b Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Marco Bubke Reviewed-by: Ali Kianian --- .../components/collectioneditor/collectionjsonparser.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionjsonparser.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionjsonparser.cpp index 3756af47857..630abb7b4a4 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionjsonparser.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionjsonparser.cpp @@ -204,7 +204,7 @@ bool JsonCollectionParser::visit([[maybe_unused]] QmlJS::AST::PatternElementList void JsonCollectionParser::endVisit([[maybe_unused]] QmlJS::AST::PatternElementList *patternElementList) { - if (auto curIndex = std::get_if(&keyStack.top())) + if (std::get_if(&keyStack.top())) keyStack.pop(); } @@ -220,7 +220,7 @@ void JsonCollectionParser::checkPropertyUpdates(QStack stack, { bool shouldUpdate = collectionPaths.contains(stack); if (!shouldUpdate && !stack.isEmpty()) { - if (auto lastIndex = std::get_if(&stack.top())) { + if (std::get_if(&stack.top())) { stack.pop(); shouldUpdate = collectionPaths.contains(stack); } From 310abd3b700e09f9c14d9aa70779c73d009e54ad Mon Sep 17 00:00:00 2001 From: Tim Jenssen Date: Wed, 8 May 2024 10:54:56 +0200 Subject: [PATCH 106/154] TextEditor: use modern approach to delete TextEditorWidgetPrivate Try to tackle the crash at destruction: https://the-qt-company-00.sentry.io/issues/5315538277 https://the-qt-company-00.sentry.io/issues/5312380342 https://the-qt-company-00.sentry.io/issues/5237253338 Pick-to: qds/4.5 Change-Id: Id4436bf5adc0c725f9b5538480619ed99428413f Reviewed-by: Reviewed-by: Tim Jenssen Reviewed-by: hjk --- src/plugins/texteditor/texteditor.cpp | 16 +++++----------- src/plugins/texteditor/texteditor.h | 2 +- 2 files changed, 6 insertions(+), 12 deletions(-) diff --git a/src/plugins/texteditor/texteditor.cpp b/src/plugins/texteditor/texteditor.cpp index bea58df3c88..bc4e605296c 100644 --- a/src/plugins/texteditor/texteditor.cpp +++ b/src/plugins/texteditor/texteditor.cpp @@ -1262,15 +1262,15 @@ TextEditorWidget::TextEditorWidget(QWidget *parent) { // "Needed", as the creation below triggers ChildEvents that are // passed to this object's event() which uses 'd'. - d = nullptr; - d = new TextEditorWidgetPrivate(this); - + d = std::make_unique(this); setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); setLayoutDirection(Qt::LeftToRight); viewport()->setMouseTracking(true); setFrameStyle(QFrame::NoFrame); } +TextEditorWidget::~TextEditorWidget() = default; + void TextEditorWidget::setTextDocument(const QSharedPointer &doc) { d->setDocument(doc); @@ -1449,12 +1449,6 @@ void TextEditorWidgetPrivate::setDocument(const QSharedPointer &do setupFromDefinition(currentDefinition()); } -TextEditorWidget::~TextEditorWidget() -{ - delete d; - d = nullptr; -} - void TextEditorWidget::print(QPrinter *printer) { const bool oldFullPage = printer->fullPage(); @@ -5796,7 +5790,7 @@ void TextEditorWidgetPrivate::paintRevisionMarker(QPainter &painter, void TextEditorWidget::extraAreaPaintEvent(QPaintEvent *e) { - ExtraAreaPaintEventData data(this, d); + ExtraAreaPaintEventData data(this, d.get()); QTC_ASSERT(data.documentLayout, return); QPainter painter(d->m_extraArea); @@ -9402,7 +9396,7 @@ void TextEditorWidget::setupGenericHighlighter() setLineSeparatorsAllowed(true); connect(textDocument(), &IDocument::filePathChanged, - d, &TextEditorWidgetPrivate::reconfigure); + d.get(), &TextEditorWidgetPrivate::reconfigure); } // diff --git a/src/plugins/texteditor/texteditor.h b/src/plugins/texteditor/texteditor.h index 85344a8dbd0..9f2a6f3b9c4 100644 --- a/src/plugins/texteditor/texteditor.h +++ b/src/plugins/texteditor/texteditor.h @@ -637,7 +637,7 @@ protected: virtual void slotCodeStyleSettingsChanged(const QVariant &); // Used in CppEditor private: - Internal::TextEditorWidgetPrivate *d; + std::unique_ptr d; friend class BaseTextEditor; friend class TextEditorFactory; friend class Internal::TextEditorFactoryPrivate; From 36ed2c6278669c1cd98c268a108e1d28e30531dc Mon Sep 17 00:00:00 2001 From: Pranta Dastider Date: Tue, 7 May 2024 18:32:42 +0200 Subject: [PATCH 107/154] QmlDesigner: Create new document for QDS Property Effects This patch creates a new document for Qt Design Studio Property Effects. Fixes: QDS-11087 Change-Id: I53287ae9635161a5a48c9cdaed045977944f34c0 Reviewed-by: Mats Honkamaa --- .../studio-effects-background-blur.webp | Bin 0 -> 40922 bytes .../images/studio-effects-drop-shadow.webp | Bin 0 -> 37860 bytes .../images/studio-effects-inner-shadow.webp | Bin 0 -> 12254 bytes .../images/studio-effects-layer-blur.webp | Bin 0 -> 34870 bytes .../studio-effects-show-shadow-behind.webp | Bin 0 -> 18410 bytes doc/qtdesignstudio/images/studio-effects.webp | Bin 0 -> 8156 bytes .../src/qtdesignstudio-toc.qdoc | 3 +- .../qtdesignstudio-logic-helpers.qdoc | 2 +- ...tdesignstudio-property-visual-effects.qdoc | 136 ++++++++++++++++++ .../qtdesignstudio-visual-effects.qdoc | 2 +- 10 files changed, 140 insertions(+), 3 deletions(-) create mode 100644 doc/qtdesignstudio/images/studio-effects-background-blur.webp create mode 100644 doc/qtdesignstudio/images/studio-effects-drop-shadow.webp create mode 100644 doc/qtdesignstudio/images/studio-effects-inner-shadow.webp create mode 100644 doc/qtdesignstudio/images/studio-effects-layer-blur.webp create mode 100644 doc/qtdesignstudio/images/studio-effects-show-shadow-behind.webp create mode 100644 doc/qtdesignstudio/images/studio-effects.webp create mode 100644 doc/qtdesignstudio/src/qtquickdesigner-components/qtdesignstudio-property-visual-effects.qdoc diff --git a/doc/qtdesignstudio/images/studio-effects-background-blur.webp b/doc/qtdesignstudio/images/studio-effects-background-blur.webp new file mode 100644 index 0000000000000000000000000000000000000000..b1fd21726377345ef236d4ed7dde7be3d99a3ed5 GIT binary patch literal 40922 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

Y(wO~ZHjN#46& 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 literal 0 HcmV?d00001 diff --git a/doc/qtdesignstudio/images/studio-effects-drop-shadow.webp b/doc/qtdesignstudio/images/studio-effects-drop-shadow.webp new file mode 100644 index 0000000000000000000000000000000000000000..7de04ead3242141758758636e54665b5d2032e32 GIT binary patch literal 37860 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 literal 0 HcmV?d00001 diff --git a/doc/qtdesignstudio/images/studio-effects-inner-shadow.webp b/doc/qtdesignstudio/images/studio-effects-inner-shadow.webp new file mode 100644 index 0000000000000000000000000000000000000000..35029035515ea3cd74e34a3731d8cc6eac74042a GIT binary patch literal 12254 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

import3dcanvas.h
+ 1 + + diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.cpp index ed1f8041e9c..010d00a970a 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.cpp @@ -21,6 +21,7 @@ #include #include +#include #include #include @@ -58,8 +59,6 @@ void ItemLibraryAssetImporter::importQuick3D(const QStringList &inputFiles, const QHash &extToImportOptionsMap, const QSet &preselectedFilesForOverwrite) { - if (m_isImporting) - cancelImport(); reset(); m_isImporting = true; @@ -92,6 +91,53 @@ void ItemLibraryAssetImporter::importQuick3D(const QStringList &inputFiles, } } +void ItemLibraryAssetImporter::reImportQuick3D(const QString &assetName, + const QVector &options) +{ + if (!assetName.isEmpty() && !m_parseData.contains(assetName)) { + addError(tr("Attempted to reimport non-existing asset: %1").arg(assetName)); + return; + } + + ParseData &pd = m_parseData[assetName]; + // Change outDir just in case reimport generates different files + QDir oldDir = pd.outDir; + QString assetFolder = generateAssetFolderName(pd.assetName); + pd.outDir.cdUp(); + pd.outDir.mkpath(assetFolder); + + if (!pd.outDir.cd(assetFolder)) { + addError(tr("Could not access temporary asset directory: \"%1\".") + .arg(pd.outDir.filePath(assetFolder))); + return; + } + + if (oldDir.absolutePath().contains(tempDirNameBase())) + oldDir.removeRecursively(); + + m_isImporting = false; + m_cancelled = false; + + m_puppetProcess.reset(); + m_requiredImports.clear(); + m_currentImportId = 0; + m_puppetQueue.clear(); + + for (ParseData &pd : m_parseData) + pd.importId = -1; + + pd.options = options[pd.optionsIndex]; + pd.importId = 1; + + m_importFiles.remove(assetName); + + m_importIdToAssetNameMap.clear(); + m_importIdToAssetNameMap[pd.importId] = assetName; + + m_puppetQueue.append(pd.importId); + startNextImportProcess(); +} + bool ItemLibraryAssetImporter::isImporting() const { return m_isImporting; @@ -104,19 +150,19 @@ void ItemLibraryAssetImporter::cancelImport() notifyFinished(); } -void ItemLibraryAssetImporter::addError(const QString &errMsg, const QString &srcPath) const +void ItemLibraryAssetImporter::addError(const QString &errMsg, const QString &srcPath) { qCDebug(importerLog) << "Error: "<< errMsg << srcPath; emit errorReported(errMsg, srcPath); } -void ItemLibraryAssetImporter::addWarning(const QString &warningMsg, const QString &srcPath) const +void ItemLibraryAssetImporter::addWarning(const QString &warningMsg, const QString &srcPath) { qCDebug(importerLog) << "Warning: " << warningMsg << srcPath; emit warningReported(warningMsg, srcPath); } -void ItemLibraryAssetImporter::addInfo(const QString &infoMsg, const QString &srcPath) const +void ItemLibraryAssetImporter::addInfo(const QString &infoMsg, const QString &srcPath) { qCDebug(importerLog) << "Info: " << infoMsg << srcPath; emit infoReported(infoMsg, srcPath); @@ -127,8 +173,8 @@ void ItemLibraryAssetImporter::importProcessFinished([[maybe_unused]] int exitCo { m_puppetProcess.reset(); - if (m_parseData.contains(m_currentImportId)) { - const ParseData &pd = m_parseData[m_currentImportId]; + if (m_importIdToAssetNameMap.contains(m_currentImportId)) { + const ParseData &pd = m_parseData[m_importIdToAssetNameMap[m_currentImportId]]; QString errStr; if (exitStatus == QProcess::ExitStatus::CrashExit) { errStr = tr("Import process crashed."); @@ -151,11 +197,12 @@ void ItemLibraryAssetImporter::importProcessFinished([[maybe_unused]] int exitCo addError(tr("Asset import process failed: \"%1\".") .arg(pd.sourceInfo.absoluteFilePath())); addError(errStr); - m_parseData.remove(m_currentImportId); + m_parseData.remove(m_importIdToAssetNameMap[m_currentImportId]); + m_importIdToAssetNameMap.remove(m_currentImportId); } } - int finishedCount = m_parseData.size() - m_puppetQueue.size(); + int finishedCount = m_importIdToAssetNameMap.size() - m_puppetQueue.size(); if (!m_puppetQueue.isEmpty()) startNextImportProcess(); @@ -163,7 +210,7 @@ void ItemLibraryAssetImporter::importProcessFinished([[maybe_unused]] int exitCo notifyProgress(100); QTimer::singleShot(0, this, &ItemLibraryAssetImporter::postImport); } else { - notifyProgress(int(100. * (double(finishedCount) / double(m_parseData.size())))); + notifyProgress(int(100. * (double(finishedCount) / double(m_importIdToAssetNameMap.size())))); } } @@ -179,7 +226,7 @@ void ItemLibraryAssetImporter::reset() m_cancelled = false; delete m_tempDir; - m_tempDir = new QTemporaryDir; + m_tempDir = new QTemporaryDir(QDir::tempPath() + tempDirNameBase()); m_importFiles.clear(); m_overwrittenImports.clear(); m_puppetProcess.reset(); @@ -187,6 +234,7 @@ void ItemLibraryAssetImporter::reset() m_requiredImports.clear(); m_currentImportId = 0; m_puppetQueue.clear(); + m_importIdToAssetNameMap.clear(); } void ItemLibraryAssetImporter::parseFiles(const QStringList &filePaths, @@ -208,9 +256,11 @@ void ItemLibraryAssetImporter::parseFiles(const QStringList &filePaths, int index = extToImportOptionsMap.value(QFileInfo(file).suffix()); ParseData pd; pd.options = options[index]; + pd.optionsIndex = index; if (preParseQuick3DAsset(file, pd, preselectedFilesForOverwrite)) { pd.importId = ++m_importIdCounter; - m_parseData.insert(pd.importId, pd); + m_importIdToAssetNameMap[pd.importId] = pd.assetName; + m_parseData.insert(pd.assetName, pd); } notifyProgress(qRound(++count * quota), progressTitle); } @@ -239,7 +289,7 @@ bool ItemLibraryAssetImporter::preParseQuick3DAsset(const QString &file, ParseDa pd.targetDirPath = pd.targetDir.filePath(pd.assetName); - if (pd.outDir.exists(pd.assetName)) { + if (m_parseData.contains(pd.assetName)) { addWarning(tr("Skipped import of duplicate asset: \"%1\".").arg(pd.assetName)); return false; } @@ -288,11 +338,12 @@ bool ItemLibraryAssetImporter::preParseQuick3DAsset(const QString &file, ParseDa } } - pd.outDir.mkpath(pd.assetName); + QString assetFolder = generateAssetFolderName(pd.assetName); + pd.outDir.mkpath(assetFolder); - if (!pd.outDir.cd(pd.assetName)) { + if (!pd.outDir.cd(assetFolder)) { addError(tr("Could not access temporary asset directory: \"%1\".") - .arg(pd.outDir.filePath(pd.assetName))); + .arg(pd.outDir.filePath(assetFolder))); return false; } return true; @@ -425,7 +476,7 @@ void ItemLibraryAssetImporter::postParseQuick3DAsset(ParseData &pd) // Copy the original asset into a subdirectory assetFiles.insert(sourcePath, sourceSceneTargetFilePath(pd)); - m_importFiles.insert(assetFiles); + m_importFiles.insert(pd.assetName, assetFiles); } void ItemLibraryAssetImporter::copyImportedFiles() @@ -500,6 +551,12 @@ void ItemLibraryAssetImporter::keepUiAlive() const QApplication::processEvents(); } +QString ItemLibraryAssetImporter::generateAssetFolderName(const QString &assetName) const +{ + static int counter = 0; + return assetName + "_QDS_" + QString::number(counter++); +} + ItemLibraryAssetImporter::OverwriteResult ItemLibraryAssetImporter::confirmAssetOverwrite(const QString &assetName) { const QString title = tr("Overwrite Existing Asset?"); @@ -534,7 +591,8 @@ void ItemLibraryAssetImporter::startNextImportProcess() if (model && view) { bool done = false; while (!m_puppetQueue.isEmpty() && !done) { - const ParseData pd = m_parseData.value(m_puppetQueue.takeLast()); + const ParseData pd = m_parseData.value( + m_importIdToAssetNameMap.value(m_puppetQueue.takeLast())); QStringList puppetArgs; QJsonDocument optDoc(pd.options); @@ -557,7 +615,8 @@ void ItemLibraryAssetImporter::startNextImportProcess() } else { addError(tr("Failed to start import 3D asset process."), pd.sourceInfo.absoluteFilePath()); - m_parseData.remove(pd.importId); + const QString assetName = m_importIdToAssetNameMap.take(pd.importId); + m_parseData.remove(assetName); m_puppetProcess.reset(); } } @@ -573,8 +632,16 @@ void ItemLibraryAssetImporter::postImport() postParseQuick3DAsset(pd); } - if (!isCancelled()) - finalizeQuick3DImport(); + if (!isCancelled()) { + // TODO: Currently we only support import preview for single imports + if (m_parseData.size() != 1) { + finalizeQuick3DImport(); + } else { + const ParseData &pd = m_parseData[m_parseData.keys().first()]; + const QString importedComponentName = pd.assetName; + emit importReadyForPreview(pd.outDir.absolutePath(), importedComponentName); + } + } } void ItemLibraryAssetImporter::finalizeQuick3DImport() diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.h b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.h index 8ad7b5a2dec..abe96909514 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.h +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.h @@ -2,8 +2,6 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #pragma once -#include "import.h" - #include #include @@ -35,20 +33,28 @@ public: const QHash &extToImportOptionsMap, const QSet &preselectedFilesForOverwrite); + void reImportQuick3D(const QString &assetName, const QVector &options); + bool isImporting() const; void cancelImport(); bool isCancelled() const; - void addError(const QString &errMsg, const QString &srcPath = {}) const; - void addWarning(const QString &warningMsg, const QString &srcPath = {}) const; - void addInfo(const QString &infoMsg, const QString &srcPath = {}) const; + void addError(const QString &errMsg, const QString &srcPath = {}); + void addWarning(const QString &warningMsg, const QString &srcPath = {}); + void addInfo(const QString &infoMsg, const QString &srcPath = {}); + + QString previewFileName() const { return "QDSImport3dPreviewScene.qml"; } + QString tempDirNameBase() const { return "/qds3dimport"; } + + void finalizeQuick3DImport(); signals: - void errorReported(const QString &, const QString &) const; - void warningReported(const QString &, const QString &) const; - void infoReported(const QString &, const QString &) const; - void progressChanged(int value, const QString &text) const; - void importNearlyFinished() const; + void errorReported(const QString &, const QString &); + void warningReported(const QString &, const QString &); + void infoReported(const QString &, const QString &); + void progressChanged(int value, const QString &text); + void importReadyForPreview(const QString &path, const QString &compName); + void importNearlyFinished(); void importFinished(); private slots: @@ -63,7 +69,8 @@ private: QFileInfo sourceInfo; QString assetName; QString originalAssetName; - int importId; + int importId = -1; + int optionsIndex = -1; }; void notifyFinished(); @@ -79,6 +86,7 @@ private: void notifyProgress(int value, const QString &text); void notifyProgress(int value); void keepUiAlive() const; + QString generateAssetFolderName(const QString &assetName) const; enum class OverwriteResult { Skip, @@ -89,10 +97,9 @@ private: OverwriteResult confirmAssetOverwrite(const QString &assetName); void startNextImportProcess(); void postImport(); - void finalizeQuick3DImport(); QString sourceSceneTargetFilePath(const ParseData &pd); - QSet> m_importFiles; + QHash> m_importFiles; // Key: asset name QHash m_overwrittenImports; bool m_isImporting = false; bool m_cancelled = false; @@ -101,7 +108,8 @@ private: QProcessUniquePointer m_puppetProcess; int m_importIdCounter = 0; int m_currentImportId = 0; - QHash m_parseData; + QHash m_importIdToAssetNameMap; + QHash m_parseData; // Key: asset name QString m_progressTitle; QStringList m_requiredImports; QList m_puppetQueue; diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp index feff523b9c6..e4f4ffcd9c6 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp @@ -164,7 +164,7 @@ void ItemLibraryView::updateImport3DSupport(const QVariantMap &supportMap) auto importDlg = new ItemLibraryAssetImportDialog(fileNames, defaultDir, m_importableExtensions3DMap, m_importOptions3DMap, {}, {}, - Core::ICore::dialogParent()); + this, Core::ICore::dialogParent()); int result = importDlg->exec(); return result == QDialog::Accepted ? AddFilesResult::succeeded() : AddFilesResult::cancelled(); @@ -198,7 +198,7 @@ void ItemLibraryView::customNotification(const AbstractView *view, const QString const QList &nodeList, const QList &data) { if (identifier == "UpdateImported3DAsset" && nodeList.size() > 0) { - ItemLibraryAssetImportDialog::updateImport(nodeList[0], + ItemLibraryAssetImportDialog::updateImport(this, nodeList[0], m_importableExtensions3DMap, m_importOptions3DMap); diff --git a/src/plugins/qmldesigner/settingspage.cpp b/src/plugins/qmldesigner/settingspage.cpp index 17c75a02853..df17f005a21 100644 --- a/src/plugins/qmldesigner/settingspage.cpp +++ b/src/plugins/qmldesigner/settingspage.cpp @@ -39,7 +39,8 @@ namespace Internal { static QStringList puppetModes() { - static QStringList puppetModeList{"", "all", "editormode", "rendermode", "previewmode", "bakelightsmode"}; + static QStringList puppetModeList{"", "all", "editormode", "rendermode", "previewmode", + "bakelightsmode", "import3dmode"}; return puppetModeList; } diff --git a/src/tools/qml2puppet/CMakeLists.txt b/src/tools/qml2puppet/CMakeLists.txt index 36ca6293d43..e77887adcca 100644 --- a/src/tools/qml2puppet/CMakeLists.txt +++ b/src/tools/qml2puppet/CMakeLists.txt @@ -140,6 +140,7 @@ extend_qtc_executable(qml2puppet qmltransitionnodeinstance.cpp qmltransitionnodeinstance.h qt3dpresentationnodeinstance.cpp qt3dpresentationnodeinstance.h qt5bakelightsnodeinstanceserver.cpp qt5bakelightsnodeinstanceserver.h + qt5import3dnodeinstanceserver.cpp qt5import3dnodeinstanceserver.h qt5informationnodeinstanceserver.cpp qt5informationnodeinstanceserver.h qt5nodeinstanceclientproxy.cpp qt5nodeinstanceclientproxy.h qt5nodeinstanceserver.cpp qt5nodeinstanceserver.h diff --git a/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp b/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp index d4f79fdabdd..1ccea6049c8 100644 --- a/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp +++ b/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp @@ -462,9 +462,9 @@ QVector4D GeneralHelper::approachNode( // a selection box for bound calculations to work. This is used to focus the view for // various preview image generations, where doing things asynchronously is not good // and recalculating bounds for every frame is not a problem. -void GeneralHelper::calculateNodeBoundsAndFocusCamera( - QQuick3DCamera *camera, QQuick3DNode *node, QQuick3DViewport *viewPort, - float defaultLookAtDistance, bool closeUp) +QVector3D GeneralHelper::calculateNodeBoundsAndFocusCamera( + QQuick3DCamera *camera, QQuick3DNode *node, QQuick3DViewport *viewPort, + float defaultLookAtDistance, bool closeUp) { QVector3D minBounds; QVector3D maxBounds; @@ -505,8 +505,9 @@ void GeneralHelper::calculateNodeBoundsAndFocusCamera( perspectiveCamera->setClipNear(minDist * 0.99); perspectiveCamera->setClipFar(maxDist * 1.01); } - } + + return extents; } // Aligns any cameras found in nodes list to a camera. diff --git a/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.h b/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.h index 50eaba68ebb..996a06488f4 100644 --- a/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.h +++ b/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.h @@ -72,9 +72,11 @@ public: bool closeUp = false); Q_INVOKABLE QVector4D approachNode(QQuick3DCamera *camera, float defaultLookAtDistance, QObject *node, QQuick3DViewport *viewPort); - Q_INVOKABLE void calculateNodeBoundsAndFocusCamera(QQuick3DCamera *camera, QQuick3DNode *node, - QQuick3DViewport *viewPort, - float defaultLookAtDistance, bool closeUp); + Q_INVOKABLE QVector3D calculateNodeBoundsAndFocusCamera(QQuick3DCamera *camera, + QQuick3DNode *node, + QQuick3DViewport *viewPort, + float defaultLookAtDistance, + bool closeUp); Q_INVOKABLE void alignCameras(QQuick3DCamera *camera, const QVariant &nodes); Q_INVOKABLE QVector4D alignView(QQuick3DCamera *camera, const QVariant &nodes, const QVector3D &lookAtPoint, float defaultLookAtDistance); diff --git a/src/tools/qml2puppet/qml2puppet/instances/nodeinstanceserverdispatcher.cpp b/src/tools/qml2puppet/qml2puppet/instances/nodeinstanceserverdispatcher.cpp index 9815d21ac47..9e3cdac151e 100644 --- a/src/tools/qml2puppet/qml2puppet/instances/nodeinstanceserverdispatcher.cpp +++ b/src/tools/qml2puppet/qml2puppet/instances/nodeinstanceserverdispatcher.cpp @@ -6,6 +6,7 @@ #include "qt5bakelightsnodeinstanceserver.h" #include "qt5captureimagenodeinstanceserver.h" #include "qt5capturepreviewnodeinstanceserver.h" +#include "qt5import3dnodeinstanceserver.h" #include "qt5informationnodeinstanceserver.h" #include "qt5rendernodeinstanceserver.h" @@ -173,6 +174,8 @@ std::unique_ptr createNodeInstanceServer( return std::make_unique(nodeInstanceClient); else if (serverName == "bakelightsmode") return std::make_unique(nodeInstanceClient); + else if (serverName == "import3dmode") + return std::make_unique(nodeInstanceClient); return {}; } diff --git a/src/tools/qml2puppet/qml2puppet/instances/qt5import3dnodeinstanceserver.cpp b/src/tools/qml2puppet/qml2puppet/instances/qt5import3dnodeinstanceserver.cpp new file mode 100644 index 00000000000..b5afa949ec2 --- /dev/null +++ b/src/tools/qml2puppet/qml2puppet/instances/qt5import3dnodeinstanceserver.cpp @@ -0,0 +1,186 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0 WITH Qt-GPL-exception-1.0 + +#include "qt5import3dnodeinstanceserver.h" + +#include "createscenecommand.h" +#include "view3dactioncommand.h" + +#include "imagecontainer.h" +#include "nodeinstanceclientinterface.h" +#include "puppettocreatorcommand.h" + +#include +#include +#include +#include + +#include + +#include +#include +#include + +namespace QmlDesigner { + +Qt5Import3dNodeInstanceServer::Qt5Import3dNodeInstanceServer(NodeInstanceClientInterface *nodeInstanceClient) : + Qt5NodeInstanceServer(nodeInstanceClient) +{ + setSlowRenderTimerInterval(100000000); + setRenderTimerInterval(20); + +#ifdef QUICK3D_MODULE + m_generalHelper = new Internal::GeneralHelper(); + QObject::connect(m_generalHelper, &Internal::GeneralHelper::requestRender, this, [this]() { + startRenderTimer(); + }); +#endif +} + +Qt5Import3dNodeInstanceServer::~Qt5Import3dNodeInstanceServer() +{ + cleanup(); +} + +void Qt5Import3dNodeInstanceServer::createScene(const CreateSceneCommand &command) +{ + initializeView(); + registerFonts(command.resourceUrl); + setTranslationLanguage(command.language); + setupScene(command); + startRenderTimer(); +} + +void Qt5Import3dNodeInstanceServer::view3DAction([[maybe_unused]] const View3DActionCommand &command) +{ + switch (command.type()) { + case View3DActionType::Import3dUpdatePreviewImage: { + QObject *obj = rootItem(); + if (obj) { + QSize size = command.value().toSize(); + QQmlProperty wProp(obj, "width", context()); + QQmlProperty hProp(obj, "height", context()); + wProp.write(size.width()); + hProp.write(size.height()); + resizeCanvasToRootItem(); + + startRenderTimer(); + } + break; + } + default: + break; + } +} + +void Qt5Import3dNodeInstanceServer::startRenderTimer() +{ + if (timerId() != 0) + killTimer(timerId()); + + int timerId = startTimer(renderTimerInterval()); + + setTimerId(timerId); +} + +void Qt5Import3dNodeInstanceServer::cleanup() +{ +#ifdef QUICK3D_MODULE + delete m_previewNode; + delete m_generalHelper; +#endif +} + +void Qt5Import3dNodeInstanceServer::finish() +{ + cleanup(); +} + +void Qt5Import3dNodeInstanceServer::collectItemChangesAndSendChangeCommands() +{ + static bool inFunction = false; + + if (!rootNodeInstance().holdsGraphical()) + return; + + if (!inFunction) { + inFunction = true; + + QQuickDesignerSupport::polishItems(quickWindow()); + + render(); + + inFunction = false; + } +} + +void Qt5Import3dNodeInstanceServer::render() +{ +#ifdef QUICK3D_MODULE + ++m_renderCount; + + if (m_renderCount == 1) { + QObject *obj = rootItem(); + QQmlProperty viewProp(obj, "view3d", context()); + QObject *viewObj = viewProp.read().value(); + m_view3D = qobject_cast(viewObj); + if (m_view3D) { + QQmlProperty sceneModelNameProp(obj, "sceneModelName", context()); + QString sceneModelName = sceneModelNameProp.read().toString(); + QFileInfo fi(fileUrl().toLocalFile()); + QString compPath = fi.absolutePath() + '/' + sceneModelName + ".qml"; + QQmlComponent comp(engine(), compPath, QQmlComponent::PreferSynchronous); + m_previewNode = qobject_cast(comp.create(context())); + if (m_previewNode) { + engine()->setObjectOwnership(m_previewNode, QJSEngine::CppOwnership); + m_previewNode->setParent(m_view3D); + m_view3D->setImportScene(m_previewNode); + } + } + } + + // Render scene once to ensure geometries are intialized so bounds calculations work correctly + if (m_renderCount == 2 && m_view3D) { + QVector3D extents = + m_generalHelper->calculateNodeBoundsAndFocusCamera(m_view3D->camera(), m_previewNode, + m_view3D, 1040, false); + auto getExtentStr = [&extents](int idx) -> QString { + int prec = 0; + float val = extents[idx]; + while (val < 100.f) { + ++prec; + val *= 10.f; + } + // Strip unnecessary zeroes after decimal separator + if (prec > 0) { + QString checkStr = QString::number(extents[idx], 'f', prec); + while (prec > 0 && (checkStr.last(1) == "0" || checkStr.last(1) == ".")) { + --prec; + checkStr.chop(1); + } + } + QString retval = QLocale().toString(extents[idx], 'f', prec); + return retval; + }; + QQmlProperty extentsProp(rootItem(), "extents", context()); + extentsProp.write(tr("Dimensions: %1 x %2 x %3").arg(getExtentStr(0)) + .arg(getExtentStr(1)) + .arg(getExtentStr(2))); + } + + rootNodeInstance().updateDirtyNodeRecursive(); + QImage renderImage = grabWindow(); + + if (m_renderCount >= 2) { + ImageContainer imgContainer(0, renderImage, m_renderCount); + nodeInstanceClient()->handlePuppetToCreatorCommand( + {PuppetToCreatorCommand::Import3DPreviewImage, + QVariant::fromValue(imgContainer)}); + slowDownRenderTimer(); // No more renders needed for now + } +#else + slowDownRenderTimer(); +#endif +} + +} // namespace QmlDesigner diff --git a/src/tools/qml2puppet/qml2puppet/instances/qt5import3dnodeinstanceserver.h b/src/tools/qml2puppet/qml2puppet/instances/qt5import3dnodeinstanceserver.h new file mode 100644 index 00000000000..bc3506ea3c0 --- /dev/null +++ b/src/tools/qml2puppet/qml2puppet/instances/qt5import3dnodeinstanceserver.h @@ -0,0 +1,46 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0 WITH Qt-GPL-exception-1.0 + +#pragma once + +#include "generalhelper.h" +#include "qt5nodeinstanceserver.h" + +QT_BEGIN_NAMESPACE +class QQuick3DNode; +QT_END_NAMESPACE + +namespace QmlDesigner { + +class Qt5Import3dNodeInstanceServer : public Qt5NodeInstanceServer +{ + Q_OBJECT +public: + explicit Qt5Import3dNodeInstanceServer(NodeInstanceClientInterface *nodeInstanceClient); + +public: + virtual ~Qt5Import3dNodeInstanceServer(); + + void createScene(const CreateSceneCommand &command) override; + void view3DAction(const View3DActionCommand &command) override; + + void render(); + +protected: + void collectItemChangesAndSendChangeCommands() override; + void startRenderTimer() override; + +private: + void finish(); + void cleanup(); + + int m_renderCount = 0; + +#ifdef QUICK3D_MODULE + QQuick3DViewport *m_view3D = nullptr; + Internal::GeneralHelper *m_generalHelper = nullptr; + QQuick3DNode *m_previewNode = nullptr; +#endif +}; + +} // namespace QmlDesigner diff --git a/src/tools/qml2puppet/qml2puppet/instances/qt5nodeinstanceclientproxy.cpp b/src/tools/qml2puppet/qml2puppet/instances/qt5nodeinstanceclientproxy.cpp index f4d01d28db4..c11899e0ec9 100644 --- a/src/tools/qml2puppet/qml2puppet/instances/qt5nodeinstanceclientproxy.cpp +++ b/src/tools/qml2puppet/qml2puppet/instances/qt5nodeinstanceclientproxy.cpp @@ -10,6 +10,7 @@ #include "qt5captureimagenodeinstanceserver.h" #include "qt5capturepreviewnodeinstanceserver.h" #include "qt5informationnodeinstanceserver.h" +#include "qt5import3dnodeinstanceserver.h" #include "qt5previewnodeinstanceserver.h" #include "qt5rendernodeinstanceserver.h" #include "qt5testnodeinstanceserver.h" @@ -70,6 +71,9 @@ Qt5NodeInstanceClientProxy::Qt5NodeInstanceClientProxy(QObject *parent) : } else if (QCoreApplication::arguments().at(2) == QLatin1String("bakelightsmode")) { setNodeInstanceServer(std::make_unique(this)); initializeSocket(); + } else if (QCoreApplication::arguments().at(2) == QLatin1String("import3dmode")) { + setNodeInstanceServer(std::make_unique(this)); + initializeSocket(); } } From 6cf945af59637107f017e94549ad605b9c4398ca Mon Sep 17 00:00:00 2001 From: Pranta Dastider Date: Fri, 17 May 2024 14:55:19 +0200 Subject: [PATCH 120/154] QmlDesigner: Update Qt Design Studio to Qt Creator workflow doc This patch updates the Qt Design Studio to Qt Creator workflow. Adding the new way to generate CMake and then importing it in Qt Creator. Fixes: QDS-10787 Change-Id: I31c54c92ddca8872977ac066d06d7877e3770671 Reviewed-by: Leena Miettinen Reviewed-by: Thomas Hartmann --- .../qtcreator-qt-design-studio-project.webp | Bin 0 -> 8422 bytes .../studio-project-cmake-generation.webp | Bin 0 -> 5072 bytes ...tudio-project-export-advanced-options.webp | Bin 11752 -> 0 bytes .../studio-project-export-advanced.webp | Bin 2488 -> 0 bytes .../images/studio-project-export.webp | Bin 12244 -> 10078 bytes .../studio-designer-developer-workflow.qdoc | 81 ++++++++---------- 6 files changed, 36 insertions(+), 45 deletions(-) create mode 100644 doc/qtdesignstudio/images/qtcreator-qt-design-studio-project.webp create mode 100644 doc/qtdesignstudio/images/studio-project-cmake-generation.webp delete mode 100644 doc/qtdesignstudio/images/studio-project-export-advanced-options.webp delete mode 100644 doc/qtdesignstudio/images/studio-project-export-advanced.webp diff --git a/doc/qtdesignstudio/images/qtcreator-qt-design-studio-project.webp b/doc/qtdesignstudio/images/qtcreator-qt-design-studio-project.webp new file mode 100644 index 0000000000000000000000000000000000000000..3e98d42ce2ae1827e30bbf518cd8b5cdf6333c06 GIT binary patch literal 8422 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 literal 0 HcmV?d00001 diff --git a/doc/qtdesignstudio/images/studio-project-export-advanced-options.webp b/doc/qtdesignstudio/images/studio-project-export-advanced-options.webp deleted file mode 100644 index 33be8549a700de37828c98956aceaf8ae2c277c0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11752 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 X&<9S%#d3$%Oc8U6)PBC-MMe?;vA&DJ diff --git a/doc/qtdesignstudio/images/studio-project-export-advanced.webp b/doc/qtdesignstudio/images/studio-project-export-advanced.webp deleted file mode 100644 index d6f1189093e7e8a6d2360d94c79234638af17b1b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2488 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 W@BgEzZx2?@whP<&e_z=_b_M{aw8azv diff --git a/doc/qtdesignstudio/images/studio-project-export.webp b/doc/qtdesignstudio/images/studio-project-export.webp index 8847dd945f801b7fcd7896705ec08d0e5f4eecec..b79ce329ee1f40d38b49f4abc769e5e944d9d567 100644 GIT binary patch literal 10078 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 literal 12244 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 \uicontrol {Export Project} > \uicontrol {Generate CMake Build Files}. - \image studio-project-export.webp "Export the \QDS project for Qt Creator" + \note If you are creating a new project in \QDS, select the + \uicontrol {Target Qt Version} that is not higher than the Qt version + used in your Qt Creator. - \li Select \uicontrol {Details} to access the \uicontrol {Advanced Options}. - \image studio-project-export-advanced.webp "Access Advanced Options in the project exporter" + \li Go to \uicontrol {File} > \uicontrol {Export Project} + > \uicontrol {Enable Automatic CMake Generation}. This creates a + \e {CMakeLists.txt} file in your project folder. - \note The project exporter has default settings selected. This works better if the project - is combined with an existing Qt project. + \note Enabling this option tracks the changes made to the project in Qt Creator + and automatically updates in \QDS. The connection works unless you + deactivate the option. - \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" + \image studio-project-export.webp "Exporting Qt Design Studio project" + \endlist - \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 Opening the \QDS Project in Qt Creator - \section1 Using the Exported Project in Qt Creator + Open the \e {CMakeLists.txt} file in Qt Creator. To open: - After exporting the project from the \QDS, you have to open it from Qt Creator. + \list 1 + \li In Qt Creator, select \uicontrol File > \uicontrol {Open File or Project}. + \li Browse through your project directory and select the \e {CMakeLists.txt}. + Then select \uicontrol Open. - If you have used any version before \QDS 4.0 to create the project, manually include this code - in the \e {CMakeLists.txt} file so the exported project works in Qt Creator. + \image studio-project-cmake-generation.webp "Project folder after CMake generation" - \code - set(BUILD_QDS_COMPONENTS ON CACHE BOOL "Build design studio components") + \li Select the Qt version and then \uicontrol {Configure Project}. - 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 - \e {CMakeLists.txt} by default. + \note If your \QDS project was created with a more updated Qt than the one + available in Qt Creator, the import doesn't work. Use + \l {Get and Install Qt with Qt Online Installer} {Qt Online Installer} + to install the latest Qt version. If successfully opened, all the files are + accessible in the \uicontrol Projects view. + \image qtcreator-qt-design-studio-project.webp "Qt Design studio projects in Qt Creator after successful import" + \li To run the project, select \uicontrol Run. + \endlist */ From 9a6bd997b57257eaff9e3fcd621c6a508d9da96c Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Tue, 21 May 2024 00:05:19 +0300 Subject: [PATCH 121/154] QmlDesigner: Convert UniqueName keywords to array of stringviews Also change the class to namespace, rename "get" to "generate" and add unit tests. Change-Id: Ib52bf7e3e0110e33acb40ca6e3d9bfc61cefdca0 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Miikka Heikkinen Reviewed-by: Marco Bubke --- .../assetslibrary/assetslibrarymodel.cpp | 2 +- .../assetslibrary/assetslibrarywidget.cpp | 2 +- .../contentlibraryusermodel.cpp | 4 +- .../qmldesigner/designercore/model/model.cpp | 2 +- .../qmldesigner/designercore/uniquename.cpp | 79 ++++++++++---- .../qmldesigner/designercore/uniquename.h | 26 +---- .../unit/tests/unittests/model/CMakeLists.txt | 2 +- .../tests/unittests/model/uniquename-test.cpp | 103 ++++++++++++++++++ 8 files changed, 174 insertions(+), 46 deletions(-) create mode 100644 tests/unit/tests/unittests/model/uniquename-test.cpp diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.cpp b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.cpp index 36299e586bf..7f488bd6153 100644 --- a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.cpp +++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.cpp @@ -154,7 +154,7 @@ bool AssetsLibraryModel::renameFolder(const QString &folderPath, const QString & QString AssetsLibraryModel::addNewFolder(const QString &folderPath) { - Utils::FilePath uniqueDirPath = Utils::FilePath::fromString(UniqueName::getPath(folderPath)); + Utils::FilePath uniqueDirPath = Utils::FilePath::fromString(UniqueName::generatePath(folderPath)); auto res = uniqueDirPath.ensureWritableDir(); if (!res.has_value()) { diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp index 11439409a71..5e2211ce0d9 100644 --- a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp +++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp @@ -178,7 +178,7 @@ QString AssetsLibraryWidget::getUniqueEffectPath(const QString &parentFolder, co QString effectsDir = ModelNodeOperations::getEffectsDefaultDirectory(parentFolder); QString effectPath = QLatin1String("%1/%2.qep").arg(effectsDir, effectName); - return UniqueName::getPath(effectPath); + return UniqueName::generatePath(effectPath); } bool AssetsLibraryWidget::createNewEffect(const QString &effectPath, bool openInEffectComposer) diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp index 7f664f0388d..cfe1fcde830 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp @@ -317,11 +317,11 @@ QPair ContentLibraryUserModel::getUniqueLibItemNames(const QSt baseQml[0] = baseQml.at(0).toUpper(); baseQml.prepend("My"); - QString uniqueQml = UniqueName::get(baseQml, [&] (const QString &name) { + QString uniqueQml = UniqueName::generate(baseQml, [&] (const QString &name) { return itemQmls.contains(name); }); - QString uniqueIcon = UniqueName::get(defaultName, [&] (const QString &name) { + QString uniqueIcon = UniqueName::generate(defaultName, [&] (const QString &name) { return itemIcons.contains(name); }); diff --git a/src/plugins/qmldesigner/designercore/model/model.cpp b/src/plugins/qmldesigner/designercore/model/model.cpp index d95d3e294bb..1100da62d6e 100644 --- a/src/plugins/qmldesigner/designercore/model/model.cpp +++ b/src/plugins/qmldesigner/designercore/model/model.cpp @@ -1874,7 +1874,7 @@ QString Model::generateNewId(const QString &prefixName, const QString &fallbackP if (!isDuplicate.has_value()) // TODO: to be removed separately to not break build isDuplicate = std::bind(&Model::hasId, this, std::placeholders::_1); - return UniqueName::getId(prefixName, [&] (const QString &id) { + return UniqueName::generateId(prefixName, [&] (const QString &id) { // Properties of the root node are not allowed for ids, because they are available in the // complete context without qualification. return isDuplicate.value()(id) || d->rootNode()->property(id.toUtf8()); diff --git a/src/plugins/qmldesigner/designercore/uniquename.cpp b/src/plugins/qmldesigner/designercore/uniquename.cpp index 7bfa2c74303..06121b326a1 100644 --- a/src/plugins/qmldesigner/designercore/uniquename.cpp +++ b/src/plugins/qmldesigner/designercore/uniquename.cpp @@ -3,10 +3,58 @@ #include "uniquename.h" +#include + #include #include -namespace QmlDesigner { +namespace QmlDesigner::UniqueName { + +using namespace Qt::Literals; + +constexpr QLatin1StringView keywords[] { + "anchors"_L1, "as"_L1, "baseState"_L1, + "border"_L1, "bottom"_L1, "break"_L1, + "case"_L1, "catch"_L1, "clip"_L1, + "color"_L1, "continue"_L1, "data"_L1, + "debugger"_L1, "default"_L1, "delete"_L1, + "do"_L1, "else"_L1, "enabled"_L1, + "finally"_L1, "flow"_L1, "focus"_L1, + "font"_L1, "for"_L1, "function"_L1, + "height"_L1, "if"_L1, "import"_L1, + "in"_L1, "instanceof"_L1, "item"_L1, + "layer"_L1, "left"_L1, "margin"_L1, + "new"_L1, "opacity"_L1, "padding"_L1, + "parent"_L1, "print"_L1, "rect"_L1, + "return"_L1, "right"_L1, "scale"_L1, + "shaderInfo"_L1, "source"_L1, "sprite"_L1, + "spriteSequence"_L1, "state"_L1, "switch"_L1, + "text"_L1, "this"_L1, "throw"_L1, + "top"_L1, "try"_L1, "typeof"_L1, + "var"_L1, "visible"_L1, "void"_L1, + "while"_L1, "with"_L1, "x"_L1, + "y"_L1 +}; + +namespace { + +QString toCamelCase(const QString &input) +{ + QString result = input.at(0).toLower(); + bool capitalizeNext = false; + + for (const QChar &c : Utils::span{input}.subspan(1)) { + bool isValidChar = c.isLetterOrNumber() || c == '_'; + if (isValidChar) + result += capitalizeNext ? c.toUpper() : c; + + capitalizeNext = !isValidChar; + } + + return result; +} + +} // namespace /** * @brief Generates a unique name based on the provided name. @@ -19,7 +67,7 @@ namespace QmlDesigner { * false if name is unique. * @return A unique name derived from the provided name. */ -QString UniqueName::get(const QString &name, std::function predicate) +QString generate(const QString &name, std::function predicate) { if (!predicate(name)) return name; @@ -61,7 +109,7 @@ QString UniqueName::get(const QString &name, std::function predicate) +QString generateId(const QString &id, std::function predicate) { - // remove non word (non A-Z, a-z, 0-9) characters - static const QRegularExpression nonWordCharsRgx("\\W"); - QString newId = id.simplified(); - newId.remove(nonWordCharsRgx); + // remove non word (non A-Z, a-z, 0-9) or space characters + QString newId = id.trimmed(); - // convert to camel case - QStringList idParts = newId.split(" "); - idParts[0] = idParts[0].at(0).toLower() + idParts[0].mid(1); - for (int i = 1; i < idParts.size(); ++i) - idParts[i] = idParts[i].at(0).toUpper() + idParts[i].mid(1); - newId = idParts.join(""); + newId = toCamelCase(newId); // prepend _ if starts with a digit or invalid id (such as reserved words) - if (newId.at(0).isDigit() || std::binary_search(keywords.begin(), keywords.end(), newId)) + if (newId.at(0).isDigit() || std::binary_search(std::begin(keywords), std::end(keywords), newId)) newId.prepend('_'); - return UniqueName::get(newId, predicate); + return UniqueName::generate(newId, predicate); } -} // namespace QmlDesigner +} // namespace QmlDesigner::UniqueName diff --git a/src/plugins/qmldesigner/designercore/uniquename.h b/src/plugins/qmldesigner/designercore/uniquename.h index 3d6bc6c0e36..d264233ddd6 100644 --- a/src/plugins/qmldesigner/designercore/uniquename.h +++ b/src/plugins/qmldesigner/designercore/uniquename.h @@ -6,27 +6,11 @@ #include #include -#include -namespace QmlDesigner { +namespace QmlDesigner::UniqueName { -class QMLDESIGNERCORE_EXPORT UniqueName -{ -public: - static QString get(const QString &name, std::function predicate); - static QString getPath(const QString &path); - static QString getId(const QString &id, std::function predicate); +QString generate(const QString &name, std::function predicate); +QString generatePath(const QString &path); +QMLDESIGNERCORE_EXPORT QString generateId(const QString &id, std::function predicate); -private: - static inline const QStringList keywords { - "anchors", "as", "baseState", "border", "bottom", "break", "case", "catch", "clip", "color", - "continue", "data", "debugger", "default", "delete", "do", "else", "enabled", "finally", - "flow", "focus", "font", "for", "function", "height", "if", "import", "in", "instanceof", - "item", "layer", "left", "margin", "new", "opacity", "padding", "parent", "print", "rect", - "return", "right", "scale", "shaderInfo", "source", "sprite", "spriteSequence", "state", - "switch", "text", "this", "throw", "top", "try", "typeof", "var", "visible", "void", - "while", "with", "x", "y" - }; -}; - -} // namespace QmlDesigner +} // namespace QmlDesigner::UniqueName diff --git a/tests/unit/tests/unittests/model/CMakeLists.txt b/tests/unit/tests/unittests/model/CMakeLists.txt index 75c81cca6f5..43d6154c504 100644 --- a/tests/unit/tests/unittests/model/CMakeLists.txt +++ b/tests/unit/tests/unittests/model/CMakeLists.txt @@ -7,5 +7,5 @@ extend_qtc_test(unittest modelresourcemanagement-test.cpp modelutils-test.cpp nodelistproperty-test.cpp - + uniquename-test.cpp ) diff --git a/tests/unit/tests/unittests/model/uniquename-test.cpp b/tests/unit/tests/unittests/model/uniquename-test.cpp new file mode 100644 index 00000000000..a6fe4560903 --- /dev/null +++ b/tests/unit/tests/unittests/model/uniquename-test.cpp @@ -0,0 +1,103 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include + +#include + +namespace { + +namespace UniqueName = QmlDesigner::UniqueName; + +TEST(UniqueName, generate_returns_same_input_if_predicate_returns_false) +{ + auto pred = [] (const QString &name) -> bool { + return false; + }; + QString name = "abc"; + + QString uniqueName = UniqueName::generate(name, pred); + + ASSERT_THAT(uniqueName, "abc"); +} + +TEST(UniqueName, generateId_returns_properly_formatted_id) +{ + auto pred = [] (const QString &id) -> bool { + return false; + }; + QString id = " A bc d _"; + + QString uniqueId = UniqueName::generateId(id, pred); + + ASSERT_THAT(uniqueId, "aBcD_"); +} + +TEST(UniqueName, generateId_returns_properly_formatted_unique_id_when_id_exists) +{ + QStringList existingIds = {"aBcD009", "aBcD010"}; + auto pred = [&] (const QString &id) -> bool { + return existingIds.contains(id); + }; + QString id = " A bc d 0 \t 0 9\n"; + + QString uniqueId = UniqueName::generateId(id, pred); + + ASSERT_THAT(uniqueId, "aBcD011"); +} + +TEST(UniqueName, generateId_properly_handles_dot_separated_words) +{ + auto pred = [&] (const QString &id) -> bool { + return false; + }; + QString id = "Foo.bar*foo"; + + QString uniqueId = UniqueName::generateId(id, pred); + + ASSERT_THAT(uniqueId, "fooBarFoo"); +} + +TEST(UniqueName, generateId_prefixes_with_underscore_if_id_is_a_reserved_word) +{ + auto pred = [&] (const QString &id) -> bool { + return false; + }; + QString id = "for"; + + QString uniqueId = UniqueName::generateId(id, pred); + + ASSERT_THAT(uniqueId, "_for"); +} + +TEST(UniqueName, generateId_prefixes_with_underscore_if_id_is_a_number) +{ + auto pred = [&] (const QString &id) -> bool { + return false; + }; + QString id = "436"; + + QString uniqueId = UniqueName::generateId(id, pred); + + ASSERT_THAT(uniqueId, "_436"); +} + +TEST(UniqueName, generatePath_returns_same_path_when_path_doesnt_exist) +{ + QString path = "<<>>"; + + QString uniquePath = UniqueName::generatePath(path); + + ASSERT_THAT(uniquePath, path); +} + +TEST(UniqueName, generatePath_returns_unique_path_when_path_exists) +{ + QString path = UNITTEST_DIR; + + QString uniquePath = UniqueName::generatePath(path); + + ASSERT_THAT(uniquePath, QString(UNITTEST_DIR).append("1")); +} + +} // namespace From d9f4987d716a703d1331152c30eef42483a1214a Mon Sep 17 00:00:00 2001 From: Mats Honkamaa Date: Wed, 22 May 2024 10:42:48 +0300 Subject: [PATCH 122/154] Doc: Add link to Design Effects Change-Id: Ia62f86bc586a6ccb5b60b95fdfef97068decb408 Reviewed-by: Johanna Vanhatapio --- doc/qtdesignstudio/src/components/qtquick-preset-components.qdoc | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/qtdesignstudio/src/components/qtquick-preset-components.qdoc b/doc/qtdesignstudio/src/components/qtquick-preset-components.qdoc index 4920cf582dd..267517cb6c7 100644 --- a/doc/qtdesignstudio/src/components/qtquick-preset-components.qdoc +++ b/doc/qtdesignstudio/src/components/qtquick-preset-components.qdoc @@ -31,6 +31,7 @@ \li \l {UI Controls} \li \l {Lists and Other Data Models} \li \l {2D Effects} + \li \l {Design Effects} \li \l {Logic Helpers} \li \l Animations \endlist From b5db9f8fb3aed4564d63cd2ff939f5a60b570203 Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Wed, 22 May 2024 14:22:43 +0300 Subject: [PATCH 123/154] QmlDesigner: Remove Model Editor Task-number: QDS-12808 Change-Id: I9d1a716cda8d4a972b335270961ee7b489ed07c3 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Mahmoud Badri --- .../CollectionDetailsEditDelegate.qml | 221 ----- .../CollectionDetailsToolbar.qml | 291 ------ .../CollectionDetailsView.qml | 813 ---------------- .../CollectionItem.qml | 154 --- .../CollectionListView.qml | 124 --- .../CollectionView.qml | 206 ---- .../ColorViewDelegate.qml | 182 ---- .../ConfirmDeleteCollectionDialog.qml | 57 -- .../EditPropertyDialog.qml | 158 ---- .../IconTextButton.qml | 97 -- .../ImportDialog.qml | 225 ----- .../collectionEditorQmlSource/Message.qml | 42 - .../NewCollectionDialog.qml | 111 --- .../RenameCollectionDialog.qml | 88 -- .../imports/StudioTheme/Values.qml | 10 - .../shared-plugin/name/Colors.json.tpl | 18 - .../shared-plugin/name/DataStore.qml.tpl | 17 - .../shared-plugin/name/models.json.tpl | 56 -- src/plugins/qmldesigner/CMakeLists.txt | 17 - .../collectiondatatypemodel.cpp | 86 -- .../collectiondatatypemodel.h | 34 - .../collectioneditor/collectiondetails.cpp | 882 ------------------ .../collectioneditor/collectiondetails.h | 152 --- .../collectiondetailsmodel.cpp | 632 ------------- .../collectioneditor/collectiondetailsmodel.h | 110 --- .../collectiondetailssortfiltermodel.cpp | 163 ---- .../collectiondetailssortfiltermodel.h | 58 -- .../collectioneditorconstants.h | 24 - .../collectioneditorutils.cpp | 218 ----- .../collectioneditor/collectioneditorutils.h | 37 - .../collectioneditor/collectionjsonparser.cpp | 257 ----- .../collectioneditor/collectionjsonparser.h | 58 -- .../collectioneditor/collectionlistmodel.cpp | 522 ----------- .../collectioneditor/collectionlistmodel.h | 79 -- .../collectioneditor/collectionview.cpp | 686 -------------- .../collectioneditor/collectionview.h | 128 --- .../collectioneditor/collectionwidget.cpp | 336 ------- .../collectioneditor/collectionwidget.h | 84 -- .../collectioneditor/datastoremodelnode.cpp | 517 ---------- .../collectioneditor/datastoremodelnode.h | 64 -- .../componentcore/componentcore_constants.h | 3 - .../componentcore/designeractionmanager.cpp | 12 - .../modelnodecontextmenu_helper.h | 12 - .../componentcore/modelnodeoperations.cpp | 24 - .../componentcore/modelnodeoperations.h | 1 - .../components/componentcore/viewmanager.cpp | 16 - src/plugins/qmldesigner/designmodecontext.cpp | 12 - src/plugins/qmldesigner/designmodecontext.h | 9 - .../qmldesigner/qmldesignerconstants.h | 2 - src/plugins/qmldesigner/qmldesignerplugin.cpp | 4 - src/plugins/qmldesigner/shortcutmanager.cpp | 6 +- src/plugins/qmldesigner/shortcutmanager.h | 1 - 52 files changed, 1 insertion(+), 8115 deletions(-) delete mode 100644 share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsEditDelegate.qml delete mode 100644 share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsToolbar.qml delete mode 100644 share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsView.qml delete mode 100644 share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionItem.qml delete mode 100644 share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionListView.qml delete mode 100644 share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionView.qml delete mode 100644 share/qtcreator/qmldesigner/collectionEditorQmlSource/ColorViewDelegate.qml delete mode 100644 share/qtcreator/qmldesigner/collectionEditorQmlSource/ConfirmDeleteCollectionDialog.qml delete mode 100644 share/qtcreator/qmldesigner/collectionEditorQmlSource/EditPropertyDialog.qml delete mode 100644 share/qtcreator/qmldesigner/collectionEditorQmlSource/IconTextButton.qml delete mode 100644 share/qtcreator/qmldesigner/collectionEditorQmlSource/ImportDialog.qml delete mode 100644 share/qtcreator/qmldesigner/collectionEditorQmlSource/Message.qml delete mode 100644 share/qtcreator/qmldesigner/collectionEditorQmlSource/NewCollectionDialog.qml delete mode 100644 share/qtcreator/qmldesigner/collectionEditorQmlSource/RenameCollectionDialog.qml delete mode 100644 share/qtcreator/qmldesigner/studio_templates/projects/shared-plugin/name/Colors.json.tpl delete mode 100644 share/qtcreator/qmldesigner/studio_templates/projects/shared-plugin/name/DataStore.qml.tpl delete mode 100644 share/qtcreator/qmldesigner/studio_templates/projects/shared-plugin/name/models.json.tpl delete mode 100644 src/plugins/qmldesigner/components/collectioneditor/collectiondatatypemodel.cpp delete mode 100644 src/plugins/qmldesigner/components/collectioneditor/collectiondatatypemodel.h delete mode 100644 src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp delete mode 100644 src/plugins/qmldesigner/components/collectioneditor/collectiondetails.h delete mode 100644 src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.cpp delete mode 100644 src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.h delete mode 100644 src/plugins/qmldesigner/components/collectioneditor/collectiondetailssortfiltermodel.cpp delete mode 100644 src/plugins/qmldesigner/components/collectioneditor/collectiondetailssortfiltermodel.h delete mode 100644 src/plugins/qmldesigner/components/collectioneditor/collectioneditorconstants.h delete mode 100644 src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.cpp delete mode 100644 src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.h delete mode 100644 src/plugins/qmldesigner/components/collectioneditor/collectionjsonparser.cpp delete mode 100644 src/plugins/qmldesigner/components/collectioneditor/collectionjsonparser.h delete mode 100644 src/plugins/qmldesigner/components/collectioneditor/collectionlistmodel.cpp delete mode 100644 src/plugins/qmldesigner/components/collectioneditor/collectionlistmodel.h delete mode 100644 src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp delete mode 100644 src/plugins/qmldesigner/components/collectioneditor/collectionview.h delete mode 100644 src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp delete mode 100644 src/plugins/qmldesigner/components/collectioneditor/collectionwidget.h delete mode 100644 src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.cpp delete mode 100644 src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.h diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsEditDelegate.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsEditDelegate.qml deleted file mode 100644 index 71499cc47e8..00000000000 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsEditDelegate.qml +++ /dev/null @@ -1,221 +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 CollectionDetails 1.0 as CollectionDetails -import StudioControls 1.0 as StudioControls -import StudioHelpers as StudioHelpers -import StudioTheme 1.0 as StudioTheme -import QtQuick.Templates as T - -Item { - id: root - - required property var columnType - - TableView.onCommit: { - if (editorLoader.changesAccepted && edit !== editorLoader.acceptedValue) - edit = editorLoader.acceptedValue - } - - onActiveFocusChanged: { - if (root.activeFocus && !editorLoader.triggered && editorLoader.item) { - editorLoader.triggered = true - editorLoader.item.open() - } - - // active focus should be checked again, because it might be affected by editorLoader.item - if (root.activeFocus && editorLoader.editor) - editorLoader.editor.forceActiveFocus() - } - - Loader { - id: editorLoader - - active: true - - property var editor: editorLoader.item ? editorLoader.item.editor : null - property var editValue: editorLoader.editor ? editorLoader.editor.editValue : null - property var acceptedValue: null - property bool changesAccepted: true - property bool triggered: false - - Connections { - id: modifierFocusConnection - - target: editorLoader.editor - enabled: editorLoader.item !== undefined - - function onActiveFocusChanged() { - if (!modifierFocusConnection.target.activeFocus) { - editorLoader.acceptedValue = editorLoader.editValue - root.TableView.commit() - } - } - } - - Component { - id: textEditor - - EditorPopup { - editor: textField - - StudioControls.TextField { - id: textField - - property alias editValue: textField.text - - actionIndicator.visible: false - translationIndicatorVisible: false - - onRejected: editorLoader.changesAccepted = false - } - } - } - - Component { - id: realEditor - - EditorPopup { - - editor: realField - - StudioControls.RealSpinBox { - id: realField - - property alias editValue: realField.realValue - - actionIndicator.visible: false - realFrom: -9e9 - realTo: 9e9 - realStepSize: 1.0 - decimals: 6 - trailingZeroes: false - - onActiveFocusChanged: { - if (realField.activeFocus) - realField.contentItem.focus = true - } - - textFromValue: function (value, locale) { - locale.numberOptions = Locale.OmitGroupSeparator - var decimals = realField.trailingZeroes ? realField.decimals : decimalCounter(realField.realValue) - if (decimals > 0) { - var text = Number(realField.realValue).toLocaleString(locale, 'f', decimals + 1) - return text.substring(0, text.length - 1) - } - return Number(realField.realValue).toLocaleString(locale, 'f', decimals) - } - } - } - } - - Component { - id: integerEditor - - EditorPopup { - - editor: integerField - - StudioControls.SpinBox { - id: integerField - - property alias editValue: integerField.value - - actionIndicatorVisible: false - spinBoxIndicatorVisible: true - from: -2147483647 - to: 2147483647 - decimals: 0 - - onActiveFocusChanged: { - if (integerField.activeFocus) - integerField.contentItem.focus = true - } - } - } - } - } - - component EditorPopup: T.Popup { - id: editorPopup - - required property Item editor - - implicitHeight: contentHeight - implicitWidth: contentWidth - - focus: true - visible: false - - Connections { - target: editorPopup.editor - - function onActiveFocusChanged() { - if (!editorPopup.editor.activeFocus) - editorPopup.close() - else if (edit) - editorPopup.editor.editValue = edit - } - } - - Connections { - target: editorPopup.editor.Keys - - function onEscapePressed() { - editorLoader.changesAccepted = false - editorPopup.close() - } - - function onReturnPressed() { - editorPopup.close() - } - - function onEnterPressed() { - editorPopup.close() - } - } - } - - states: [ - State { - name: "default" - when: columnType !== CollectionDetails.DataType.Boolean - && columnType !== CollectionDetails.DataType.Color - && columnType !== CollectionDetails.DataType.Integer - && columnType !== CollectionDetails.DataType.Real - - PropertyChanges { - target: editorLoader - sourceComponent: textEditor - } - }, - State { - name: "integer" - when: columnType === CollectionDetails.DataType.Integer - - PropertyChanges { - target: editorLoader - sourceComponent: integerEditor - } - }, - State { - name: "real" - when: columnType === CollectionDetails.DataType.Real - - PropertyChanges { - target: editorLoader - sourceComponent: realEditor - } - }, - State { - name: "color" - when: columnType === CollectionDetails.DataType.Color - - PropertyChanges { - target: editorLoader - sourceComponent: null - } - } - ] -} diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsToolbar.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsToolbar.qml deleted file mode 100644 index e7997f7eae6..00000000000 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsToolbar.qml +++ /dev/null @@ -1,291 +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 QtQuick.Layouts -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 CollectionDetails -import CollectionEditorBackend - -Rectangle { - id: root - - required property var model - required property var backend - property int selectedRow: -1 - - implicitHeight: StudioTheme.Values.toolbarHeight - color: StudioTheme.Values.themeToolbarBackground - - function addNewColumn() { - addColumnDialog.popUp(root.model.columnCount()) - } - - function addNewRow() { - root.model.insertRow(root.model.rowCount()) - } - - function closeDialogs() { - addColumnDialog.reject() - fileDialog.reject() - } - - RowLayout { - id: container - - anchors.fill: parent - anchors.topMargin: StudioTheme.Values.toolbarVerticalMargin - anchors.bottomMargin: StudioTheme.Values.toolbarVerticalMargin - - spacing: StudioTheme.Values.sectionRowSpacing - - RowLayout { - id: leftSideToolbar - - Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter - Layout.leftMargin: StudioTheme.Values.toolbarHorizontalMargin - spacing: StudioTheme.Values.sectionRowSpacing - - IconButton { - id: addColumnLeftButton - - buttonIcon: StudioTheme.Constants.addcolumnleft_medium - tooltip: qsTr("Add column left") - enabled: root.model.selectedColumn > -1 - onClicked: addColumnDialog.popUp(root.model.selectedColumn) - } - - IconButton { - id: addColumnRightButton - - buttonIcon: StudioTheme.Constants.addcolumnright_medium - tooltip: qsTr("Add column right") - enabled: root.model.selectedColumn > -1 - onClicked: addColumnDialog.popUp(root.model.selectedColumn + 1) - } - - IconButton { - id: deleteColumnButton - - buttonIcon: StudioTheme.Constants.deletecolumn_medium - tooltip: qsTr("Delete selected column") - enabled: root.model.selectedColumn > -1 - onClicked: root.model.removeColumn(root.model.selectedColumn) - } - - Item { // spacer - implicitWidth: StudioTheme.Values.toolbarSpacing - implicitHeight: 1 - } - - IconButton { - id: addRowBelowButton - - buttonIcon: StudioTheme.Constants.addrowbelow_medium - tooltip: qsTr("Add row below") - enabled: root.model.selectedRow > -1 - onClicked: root.model.insertRow(root.model.selectedRow + 1) - } - - IconButton { - id: addRowAboveButton - - buttonIcon: StudioTheme.Constants.addrowabove_medium - tooltip: qsTr("Add row above") - enabled: root.model.selectedRow > -1 - onClicked: root.model.insertRow(root.model.selectedRow) - } - - IconButton { - id: deleteSelectedRowButton - - buttonIcon: StudioTheme.Constants.deleterow_medium - tooltip: qsTr("Delete selected row") - enabled: root.model.selectedRow > -1 - onClicked: root.model.removeRow(root.model.selectedRow) - } - } - - RowLayout { - id: rightSideToolbar - - spacing: StudioTheme.Values.sectionRowSpacing - Layout.alignment: Qt.AlignRight | Qt.AlignVCenter - Layout.rightMargin: StudioTheme.Values.toolbarHorizontalMargin - - IconButton { - id: saveCollectionButton - - buttonIcon: StudioTheme.Constants.save_medium - tooltip: qsTr("Save changes") - enabled: root.model.collectionName !== "" && root.model.hasUnsavedChanges - onClicked: root.model.saveDataStoreCollections() - - Rectangle { - width: StudioTheme.Values.smallStatusIndicatorDiameter - height: StudioTheme.Values.smallStatusIndicatorDiameter - radius: StudioTheme.Values.smallStatusIndicatorDiameter / 2 - anchors.right: parent.right - anchors.top: parent.top - visible: root.model.hasUnsavedChanges - color: StudioTheme.Values.themeIconColorSelected - } - } - - IconButton { - id: exportCollectionButton - - buttonIcon: StudioTheme.Constants.export_medium - tooltip: qsTr("Export model") - enabled: root.model.collectionName !== "" - onClicked: fileDialog.open() - } - } - } - - PlatformWidgets.FileDialog { - id: fileDialog - - fileMode: PlatformWidgets.FileDialog.SaveFile - - nameFilters: ["JSON Files (*.json)", - "Comma-Separated Values (*.csv)" - ] - - selectedNameFilter.index: 0 - - onAccepted: { - let filePath = fileDialog.file.toString() - root.model.exportCollection(filePath) - } - } - - component IconButton: HelperWidgets.AbstractButton { - style: StudioTheme.Values.viewBarButtonStyle - } - - component Spacer: Item { - implicitWidth: 1 - implicitHeight: StudioTheme.Values.columnGap - } - - RegularExpressionValidator { - id: nameValidator - regularExpression: /^\w+$/ - } - - StudioControls.Dialog { - id: addColumnDialog - - property int clickedIndex: -1 - property bool nameIsValid - - title: qsTr("Add Column") - - function popUp(index) - { - addColumnDialog.clickedIndex = index - columnName.text = "" - columnName.forceActiveFocus() - addedPropertyType.currentIndex = addedPropertyType.find("String") - - addColumnDialog.open() - } - - function addColumnName() { - if (addColumnDialog.nameIsValid) { - root.model.addColumn(addColumnDialog.clickedIndex, columnName.text, addedPropertyType.currentText) - addColumnDialog.accept() - } else { - addColumnDialog.reject() - } - } - - contentItem: ColumnLayout { - spacing: 2 - - Text { - text: qsTr("Column name:") - color: StudioTheme.Values.themeTextColor - } - - StudioControls.TextField { - id: columnName - - Layout.fillWidth: true - - 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)) - } - } - - 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 - } - } - - Spacer {} - - Text { - text: qsTr("Type:") - color: StudioTheme.Values.themeTextColor - } - - StudioControls.ComboBox { - id: addedPropertyType - - Layout.fillWidth: true - - model: CollectionDataTypeModel{} - textRole: "display" - tooltipRole: "toolTip" - actionIndicatorVisible: false - } - - Spacer {} - - RowLayout { - Layout.alignment: Qt.AlignRight | Qt.AlignVCenter - spacing: StudioTheme.Values.sectionRowSpacing - - 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/CollectionDetailsView.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsView.qml deleted file mode 100644 index ad721472ed7..00000000000 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsView.qml +++ /dev/null @@ -1,813 +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 QtQuick.Layouts -import CollectionDetails 1.0 as CollectionDetails -import HelperWidgets 2.0 as HelperWidgets -import StudioControls 1.0 as StudioControls -import StudioTheme 1.0 as StudioTheme - -Rectangle { - id: root - - required property var model - required property var backend - required property var sortedModel - - implicitWidth: 300 - implicitHeight: 400 - color: StudioTheme.Values.themeControlBackground - - function closeDialogs() { - editPropertyDialog.reject() - deleteColumnDialog.reject() - toolbar.closeDialogs() - } - - MouseArea { - anchors.fill: parent - onClicked: tableView.model.deselectAll() - } - - Column { - id: topRow - readonly property real maxAvailableHeight: root.height - - visible: root.model.collectionName !== "" - width: parent.width - spacing: 10 - - CollectionDetailsToolbar { - id: toolbar - model: root.model - backend: root.backend - width: parent.width - } - - GridLayout { - id: gridLayout - readonly property real maxAvailableHeight: topRow.maxAvailableHeight - - topRow.spacing - - toolbar.height - - columns: 3 - rowSpacing: 1 - columnSpacing: 1 - width: parent.width - - anchors { - left: parent.left - leftMargin: StudioTheme.Values.collectionTableHorizontalMargin - } - - Rectangle { - id: tableTopLeftCorner - - clip: true - visible: !tableView.model.isEmpty - color: StudioTheme.Values.themeControlBackgroundInteraction - border.color: StudioTheme.Values.themeControlBackgroundInteraction - border.width: 2 - - Layout.preferredWidth: rowIdView.width - Layout.preferredHeight: headerView.height - Layout.minimumWidth: rowIdView.width - Layout.minimumHeight: headerView.height - - Text { - anchors.fill: parent - font: headerTextMetrics.font - text: "#" - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter - color: StudioTheme.Values.themeTextColor - - MouseArea { - anchors.fill: parent - acceptedButtons: Qt.LeftButton - property int order: Qt.AscendingOrder - onClicked: { - order = (order == Qt.AscendingOrder) ? Qt.DescendingOrder : Qt.AscendingOrder; - tableView.closeEditors() - tableView.model.sort(-1, order) - } - } - } - } - - HorizontalHeaderView { - id: headerView - - property real topPadding: 5 - property real bottomPadding: 5 - - Layout.preferredHeight: headerTextMetrics.height + topPadding + bottomPadding - Layout.columnSpan: 2 - syncView: tableView - clip: true - - delegate: HeaderDelegate { - selectedItem: tableView.model.selectedColumn - color: StudioTheme.Values.themeControlBackgroundInteraction - - MouseArea { - id: topHeaderMouseArea - - anchors.fill: parent - anchors.leftMargin: StudioTheme.Values.borderHover - anchors.rightMargin: StudioTheme.Values.borderHover - acceptedButtons: Qt.LeftButton | Qt.RightButton - hoverEnabled: true - onClicked: (mouse) => { - tableView.model.selectColumn(index) - - if (mouse.button === Qt.RightButton) { - let posX = index === root.model.columnCount() - 1 ? parent.width - editPropertyDialog.width : 0 - - headerMenu.clickedHeaderIndex = index - headerMenu.dialogPos = parent.mapToGlobal(posX, parent.height) - headerMenu.popup() - } else { - headerMenu.close() - } - } - } - - ToolTip { - id: topHeaderToolTip - - property bool expectedToBeShown: topHeaderMouseArea.containsMouse - visible: expectedToBeShown && text !== "" - delay: 1000 - - onExpectedToBeShownChanged: { - if (expectedToBeShown) - text = root.model.propertyType(index) - } - } - } - - StudioControls.Menu { - id: headerMenu - - property int clickedHeaderIndex: -1 - property point dialogPos - - onClosed: { - headerMenu.clickedHeaderIndex = -1 - } - - StudioControls.MenuItem { - text: qsTr("Edit") - onTriggered: editPropertyDialog.openDialog(headerMenu.clickedHeaderIndex, - headerMenu.dialogPos) - } - - StudioControls.MenuItem { - text: qsTr("Delete") - onTriggered: deleteColumnDialog.popUp(headerMenu.clickedHeaderIndex) - } - - StudioControls.MenuItem { - text: qsTr("Sort Ascending") - onTriggered: { - tableView.closeEditors() - tableView.model.sort(headerMenu.clickedHeaderIndex, Qt.AscendingOrder) - } - } - - StudioControls.MenuItem { - text: qsTr("Sort Descending") - onTriggered: { - tableView.closeEditors() - tableView.model.sort(headerMenu.clickedHeaderIndex, Qt.DescendingOrder) - } - } - } - } - - VerticalHeaderView { - id: rowIdView - - syncView: tableView - clip: true - - Layout.preferredHeight: tableView.height - Layout.rowSpan: 2 - Layout.alignment: Qt.AlignTop + Qt.AlignLeft - width: implicitWidth // suppresses GridLayout warnings when resizing - - delegate: HeaderDelegate { - selectedItem: tableView.model.selectedRow - color: StudioTheme.Values.themeControlBackgroundHover - - MouseArea { - anchors.fill: parent - anchors.topMargin: StudioTheme.Values.borderHover - anchors.bottomMargin: StudioTheme.Values.borderHover - acceptedButtons: Qt.LeftButton - onClicked: tableView.model.selectRow(index) - } - } - } - - TableView { - id: tableView - - model: root.sortedModel - clip: true - - readonly property real maxAvailableHeight: gridLayout.maxAvailableHeight - - addRowButton.height - - headerView.height - - (2 * gridLayout.rowSpacing) - readonly property real maxAvailableWidth: gridLayout.width - - StudioTheme.Values.collectionTableHorizontalMargin - - rowIdView.width - - addColumnButton.width - - gridLayout.columnSpacing - - property real childrenWidth: tableView.contentItem.childrenRect.width - property real childrenHeight: tableView.contentItem.childrenRect.height - - property int targetRow - property int targetColumn - - property Item popupEditingItem - - Layout.alignment: Qt.AlignTop + Qt.AlignLeft - Layout.preferredWidth: tableView.contentWidth - Layout.preferredHeight: tableView.contentHeight - Layout.minimumWidth: 100 - Layout.minimumHeight: 20 - Layout.maximumWidth: maxAvailableWidth - Layout.maximumHeight: maxAvailableHeight - - columnWidthProvider: function(column) { - if (!isColumnLoaded(column)) - return -1 - let w = explicitColumnWidth(column) - if (w < 0) - w = implicitColumnWidth(column) - return Math.max(w, StudioTheme.Values.collectionCellMinimumWidth) - } - - rowHeightProvider: function(row) { - if (!isRowLoaded(row)) - return -1 - let h = explicitRowHeight(row) - if (h < 0) - h = implicitRowHeight(row) - return Math.max(h, StudioTheme.Values.collectionCellMinimumHeight) - } - - function closePopupEditor() { - if (tableView.popupEditingItem) - tableView.popupEditingItem.closeEditor() - tableView.popupEditingItem = null - } - - function openNewPopupEditor(item, editor) { - if (tableView.popupEditingItem !== item) { - closePopupEditor() - tableView.popupEditingItem = item - } - } - - function closeEditors() { - closeEditor() - closePopupEditor() - } - - function ensureRowIsVisible(row) { - let rows = tableView.model.rowCount() - let rowIsLoaded = tableView.isRowLoaded(row) - - if (row < 0 || row >= rows || rowIsLoaded) { - if (rowIsLoaded) - tableView.positionViewAtRow(row, Qt.AlignLeft | Qt.AlignTop) - - tableView.targetRow = -1 - return - } - - tableView.targetRow = row - tableView.positionViewAtRow(row, Qt.AlignLeft | Qt.AlignTop) - ensureTimer.start() - } - - function ensureColumnIsVisible(column) { - let columns = tableView.model.columnCount() - let columnIsLoaded = tableView.isColumnLoaded(column) - - if (column < 0 || column >= columns || columnIsLoaded) { - if (columnIsLoaded) - tableView.positionViewAtColumn(column, Qt.AlignLeft | Qt.AlignTop) - - tableView.targetColumn = -1 - return - } - - tableView.targetColumn = column - tableView.positionViewAtColumn(column, Qt.AlignLeft | Qt.AlignTop) - ensureTimer.start() - } - - onMaxAvailableHeightChanged: resetSizeTimer.start() - onMaxAvailableWidthChanged: resetSizeTimer.start() - onChildrenWidthChanged: resetSizeTimer.start() - onChildrenHeightChanged: resetSizeTimer.start() - - delegate: Rectangle { - id: itemCell - - clip: true - implicitWidth: 100 - implicitHeight: StudioTheme.Values.baseHeight - color: itemSelected ? StudioTheme.Values.themeControlBackgroundInteraction - : StudioTheme.Values.themeControlBackground - border.width: 1 - border.color: { - if (dataTypeWarning !== CollectionDetails.Warning.None) - return StudioTheme.Values.themeWarning - - if (itemSelected) - return StudioTheme.Values.themeControlOutlineInteraction - - return StudioTheme.Values.themeControlBackgroundInteraction - } - - HelperWidgets.ToolTipArea { - anchors.fill: parent - text: root.model.warningToString(dataTypeWarning) - enabled: dataTypeWarning !== CollectionDetails.Warning.None && text !== "" - hoverEnabled: true - acceptedButtons: Qt.NoButton - } - - MouseArea { - anchors.fill: parent - acceptedButtons: Qt.RightButton - onClicked: (mouse) => { - let row = index % tableView.model.rowCount() - - tableView.model.selectRow(row) - cellContextMenu.showMenu(row) - } - } - - Loader { - id: cellContentLoader - - property int cellColumnType: columnType ? columnType : 0 - - Component { - id: cellText - - Text { - text: display ?? "" - color: itemSelected ? StudioTheme.Values.themeInteraction - : StudioTheme.Values.themeTextColor - leftPadding: 5 - topPadding: 3 - bottomPadding: 3 - font.pixelSize: StudioTheme.Values.baseFontSize - horizontalAlignment: Text.AlignLeft - verticalAlignment: Text.AlignVCenter - elide: Text.ElideRight - } - } - - Component { - id: checkBoxComponent - - StudioControls.CheckBox { - id: checkBoxDelegate - - readonly property bool editValue: edit - - text: "" - actionIndicatorVisible: false - checked: checkBoxDelegate.editValue - onCheckedChanged: { - if (checkBoxDelegate.editValue !== checkBoxDelegate.checked) - edit = checkBoxDelegate.checked - } - } - } - - Component { - id: colorEditorComponent - - ColorViewDelegate { - id: colorEditorItem - - readonly property color editValue: edit - readonly property color displayValue: display - property string _frontColorStr - property string _backendColorStr - - actionIndicatorVisible: false - - onColorChanged: { - _frontColorStr = colorEditorItem.color.toString() - if (_frontColorStr != _backendColorStr) - edit = colorEditorItem.color - } - - Component.onCompleted: { - colorEditorItem.color = display - } - - onDisplayValueChanged: { - _backendColorStr = colorEditorItem.displayValue.toString() - if (_frontColorStr != _backendColorStr) - colorEditorItem.color = colorEditorItem.displayValue - } - - onEditorOpened: (item, editor) => { - tableView.openNewPopupEditor(item, editor) - } - } - } - - function resetSource() { - if (columnType === CollectionDetails.DataType.Color) - cellContentLoader.sourceComponent = colorEditorComponent - else if (columnType === CollectionDetails.DataType.Boolean) - cellContentLoader.sourceComponent = checkBoxComponent - else - cellContentLoader.sourceComponent = cellText - } - - Component.onCompleted: resetSource() - onCellColumnTypeChanged: resetSource() - } - - TableView.editDelegate: CollectionDetailsEditDelegate { - anchors { - top: itemCell.top - left: itemCell.left - } - Component.onCompleted: tableView.model.deselectAll() - } - } - - Timer { - id: resetSizeTimer - - interval: 100 - repeat: false - onTriggered: { - let cWidth = Math.min(tableView.maxAvailableWidth, tableView.childrenWidth) - let cHeight = Math.min(tableView.maxAvailableHeight, tableView.childrenHeight) - - if (tableView.contentWidth !== cWidth || tableView.contentHeight !== cHeight) - tableView.returnToBounds() - } - } - - Timer { - id: ensureTimer - - interval: 100 - repeat: false - onTriggered: { - tableView.ensureRowIsVisible(tableView.targetRow) - tableView.ensureColumnIsVisible(tableView.targetColumn) - } - } - - Connections { - target: tableView.model - - function onModelReset() { - root.closeDialogs() - tableView.clearColumnWidths() - tableView.clearRowHeights() - } - - function onRowsInserted(parent, first, last) { - tableView.closeEditors() - tableView.model.selectRow(first) - tableView.ensureRowIsVisible(first) - } - - function onColumnsInserted(parent, first, last) { - tableView.closeEditors() - tableView.model.selectColumn(first) - tableView.ensureColumnIsVisible(first) - } - - function onRowsRemoved(parent, first, last) { - let nextRow = first - 1 - if (nextRow < 0 && tableView.model.rowCount(parent) > 0) - nextRow = 0 - - tableView.model.selectRow(nextRow) - } - - function onColumnsRemoved(parent, first, last) { - let nextColumn = first - 1 - if (nextColumn < 0 && tableView.model.columnCount(parent) > 0) - nextColumn = 0 - - tableView.model.selectColumn(nextColumn) - } - } - - HoverHandler { id: hoverHandler } - - ScrollBar.horizontal: StudioControls.TransientScrollBar { - id: horizontalScrollBar - style: StudioTheme.Values.viewStyle - orientation: Qt.Horizontal - - show: (hoverHandler.hovered || tableView.focus || horizontalScrollBar.inUse) - && horizontalScrollBar.isNeeded - } - - ScrollBar.vertical: StudioControls.TransientScrollBar { - id: verticalScrollBar - style: StudioTheme.Values.viewStyle - orientation: Qt.Vertical - - show: (hoverHandler.hovered || tableView.focus || verticalScrollBar.inUse) - && verticalScrollBar.isNeeded - } - } - - HelperWidgets.IconButton { - id: addColumnButton - - 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: { - tableView.closeEditors() - toolbar.addNewColumn() - } - } - - HelperWidgets.IconButton { - id: addRowButton - - 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: { - tableView.closeEditors() - toolbar.addNewRow() - } - } - - Item { - Layout.fillWidth: true - Layout.fillHeight: true - } - } - } - - ColumnLayout { - id: importsProblem - - visible: !topRow.visible && rootView.dataStoreExists && !rootView.projectImportExists - width: parent.width - anchors.verticalCenter: parent.verticalCenter - clip: true - - Text { - text: qsTr("Import the project to your design document to make the Model Editor enabled.") - Layout.alignment: Qt.AlignCenter - Layout.maximumWidth: parent.width - leftPadding: StudioTheme.Values.collectionItemTextPadding - rightPadding: StudioTheme.Values.collectionItemTextPadding - color: StudioTheme.Values.themeTextColor - font.pixelSize: StudioTheme.Values.mediumFontSize - wrapMode: Text.Wrap - } - - HelperWidgets.Button { - text: qsTr("Enable DataStore (This will add the required import)") - Layout.alignment: Qt.AlignCenter - onClicked: rootView.addProjectImport() - leftPadding: StudioTheme.Values.collectionItemTextPadding - rightPadding: StudioTheme.Values.collectionItemTextPadding - } - } - - Text { - anchors.centerIn: parent - text: qsTr("There are no models in this project.\nAdd or import a model.") - visible: !topRow.visible && !importsProblem.visible - color: StudioTheme.Values.themeTextColor - font.pixelSize: StudioTheme.Values.mediumFontSize - } - - TextMetrics { - id: headerTextMetrics - - font.pixelSize: StudioTheme.Values.baseFontSize - text: "Xq" - } - - StudioControls.Menu { - id: cellContextMenu - - property int rowIndex: -1 - - closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside - - function showMenu(rowIndex) { - cellContextMenu.rowIndex = rowIndex - cellContextMenu.popup() - } - - CellContextMenuItem { - id: addRowAboveCellMenuItem - - itemText: qsTr("Add row above") - itemIcon: StudioTheme.Constants.addrowabove_medium - onTriggered: root.model.insertRow(cellContextMenu.rowIndex) - } - CellContextMenuItem { - id: addRowBelowCellMenuItem - - itemText: qsTr("Add row below") - itemIcon: StudioTheme.Constants.addrowbelow_medium - onTriggered: root.model.insertRow(cellContextMenu.rowIndex + 1) - } - CellContextMenuItem { - id: deleteRowCellMenuItem - - itemText: qsTr("Delete row") - itemIcon: StudioTheme.Constants.deleterow_medium - onTriggered: root.model.removeRows(cellContextMenu.rowIndex, 1) - } - } - - component HeaderDelegate: Rectangle { - id: headerItem - - required property int selectedItem - property alias horizontalAlignment: headerText.horizontalAlignment - property alias verticalAlignment: headerText.verticalAlignment - - implicitWidth: headerText.implicitWidth - implicitHeight: headerText.implicitHeight - border.width: 1 - clip: true - - Text { - id: headerText - - topPadding: headerView.topPadding - bottomPadding: headerView.bottomPadding - leftPadding: 5 - rightPadding: 5 - text: display - font: headerTextMetrics.font - color: StudioTheme.Values.themeTextColor - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter - elide: Text.ElideRight - } - - states: [ - State { - name: "default" - when: index !== selectedItem - PropertyChanges { - target: headerItem - border.color: StudioTheme.Values.themeControlBackgroundInteraction - } - - PropertyChanges { - target: headerText - font.bold: false - } - }, - State { - name: "selected" - when: index === selectedItem - - PropertyChanges { - target: headerItem - border.color: StudioTheme.Values.themeControlBackground - } - - PropertyChanges { - target: headerText - font.bold: true - } - } - ] - } - - component CellContextMenuItem: StudioControls.MenuItem { - id: cellContextMenuItemComponent - - property alias itemText: cellContextMenuText.text - property alias itemIcon: cellContextMenuIcon.text - text: "" - - implicitWidth: cellContextMenuRow.width - implicitHeight: cellContextMenuRow.height - - Row { - id: cellContextMenuRow - - property color textColor : cellContextMenuItemComponent.enabled - ? cellContextMenuItemComponent.highlighted - ? cellContextMenuItemComponent.style.text.selectedText - : cellContextMenuItemComponent.style.text.idle - : cellContextMenuItemComponent.style.text.disabled - - spacing: 2 * StudioTheme.Values.contextMenuHorizontalPadding - height: StudioTheme.Values.defaultControlHeight - leftPadding: StudioTheme.Values.contextMenuHorizontalPadding - rightPadding: StudioTheme.Values.contextMenuHorizontalPadding - - Text { - id: cellContextMenuIcon - - color: cellContextMenuRow.textColor - text: StudioTheme.Constants.addrowabove_medium - font.family: StudioTheme.Constants.iconFont.family - font.pixelSize: StudioTheme.Values.myIconFontSize - anchors.verticalCenter: parent.verticalCenter - } - - Text { - id: cellContextMenuText - - color: cellContextMenuRow.textColor - anchors.verticalCenter: parent.verticalCenter - } - } - } - - EditPropertyDialog { - id: editPropertyDialog - 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: ColumnLayout { - spacing: StudioTheme.Values.sectionColumnSpacing - - 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 - } - - RowLayout { - Layout.alignment: Qt.AlignRight | Qt.AlignVCenter - spacing: StudioTheme.Values.sectionRowSpacing - - HelperWidgets.Button { - text: qsTr("Delete") - onClicked: deleteColumnDialog.accept() - } - - HelperWidgets.Button { - text: qsTr("Cancel") - onClicked: deleteColumnDialog.reject() - } - } - } - } -} diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionItem.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionItem.qml deleted file mode 100644 index e110a8a62a6..00000000000 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionItem.qml +++ /dev/null @@ -1,154 +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 QtQuick.Layouts -import HelperWidgets 2.0 as HelperWidgets -import StudioControls 1.0 as StudioControls -import StudioTheme as StudioTheme -import CollectionEditorBackend - -Item { - id: root - - implicitWidth: 300 - implicitHeight: boundingRect.height + 3 - - property color textColor - readonly property string name: collectionName ?? "" - readonly property bool isSelected: collectionIsSelected - readonly property int id: index - - function rename(newName) { - collectionName = newName - } - - signal selectItem(int itemIndex) - signal deleteItem() - signal contextMenuRequested() - - Item { - id: boundingRect - - width: parent.width - height: itemLayout.height - clip: true - - MouseArea { - id: itemMouse - - anchors.fill: parent - acceptedButtons: Qt.LeftButton - propagateComposedEvents: true - hoverEnabled: true - onClicked: (event) => { - if (!collectionIsSelected) { - collectionIsSelected = true - event.accepted = true - } - } - } - - Rectangle { - id: innerRect - anchors.fill: parent - } - - RowLayout { - id: itemLayout - - width: parent.width - - Text { - id: nameHolder - - Layout.fillWidth: true - Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter - Layout.leftMargin: StudioTheme.Values.collectionItemTextSideMargin - Layout.topMargin: StudioTheme.Values.collectionItemTextMargin - Layout.bottomMargin: StudioTheme.Values.collectionItemTextMargin - - text: collectionName - font.pixelSize: StudioTheme.Values.baseFontSize - color: root.textColor - topPadding: StudioTheme.Values.collectionItemTextPadding - bottomPadding: StudioTheme.Values.collectionItemTextPadding - elide: Text.ElideMiddle - verticalAlignment: Text.AlignVCenter - } - - Text { - id: threeDots - - Layout.alignment: Qt.AlignRight | Qt.AlignVCenter - Layout.topMargin: StudioTheme.Values.collectionItemTextMargin - Layout.bottomMargin: StudioTheme.Values.collectionItemTextMargin - Layout.rightMargin: StudioTheme.Values.collectionItemTextSideMargin - - text: StudioTheme.Constants.more_medium - font.family: StudioTheme.Constants.iconFont.family - font.pixelSize: StudioTheme.Values.baseIconFontSize - color: root.textColor - padding: StudioTheme.Values.collectionItemTextPadding - - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter - - MouseArea { - anchors.fill: parent - acceptedButtons: Qt.RightButton | Qt.LeftButton - onClicked: root.contextMenuRequested() - } - } - } - } - - states: [ - State { - name: "default" - when: !collectionIsSelected && !itemMouse.containsMouse - - PropertyChanges { - target: innerRect - opacity: 0.6 - color: StudioTheme.Values.themeControlBackground - } - - PropertyChanges { - target: root - textColor: StudioTheme.Values.themeTextColor - } - }, - State { - name: "hovered" - when: !collectionIsSelected && itemMouse.containsMouse - - PropertyChanges { - target: innerRect - opacity: 0.8 - color: StudioTheme.Values.themeControlBackgroundHover - } - - PropertyChanges { - target: root - textColor: StudioTheme.Values.themeTextColor - } - }, - State { - name: "selected" - when: collectionIsSelected - - PropertyChanges { - target: innerRect - opacity: 1 - color: StudioTheme.Values.themeIconColorSelected - } - - PropertyChanges { - target: root - textColor: StudioTheme.Values.themeTextSelectedTextColor - } - } - ] -} diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionListView.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionListView.qml deleted file mode 100644 index cbfab526152..00000000000 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionListView.qml +++ /dev/null @@ -1,124 +0,0 @@ -// Copyright (C) 2024 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 HelperWidgets 2.0 as HelperWidgets -import StudioControls 1.0 as StudioControls -import StudioTheme as StudioTheme -import CollectionEditorBackend - -ListView { - id: root - - model: CollectionEditorBackend.model - clip: true - - function closeDialogs() { - currentCollection.dereference() - collectionMenu.close() - deleteDialog.reject() - renameDialog.reject() - } - - function deleteCurrentCollection() { - deleteDialog.open() - } - - delegate: CollectionItem { - implicitWidth: root.width - onDeleteItem: root.model.removeRow(index) - onContextMenuRequested: collectionMenu.openMenu(this) - } - - QtObject { - id: currentCollection - - property CollectionItem item - readonly property string name: item ? item.name : "" - readonly property bool selected: item ? item.isSelected : false - readonly property int index: item ? item.id : -1 - - function updateItem() { - currentCollection.item = collectionMenu.clickedItem ?? root.itemAtIndex(root.model.selectedIndex) - } - - function rename(newName) { - if (item) - item.rename(newName) - } - - function deleteItem() { - if (item) - item.deleteItem() - } - - function dereference() { - item = null - } - } - - StudioControls.Menu { - id: collectionMenu - - property CollectionItem clickedItem - - closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside - enabled: root.count - - function openMenu(item) { - collectionMenu.clickedItem = item - currentCollection.updateItem() - collectionMenu.popup() - } - - onClosed: collectionMenu.clickedItem = null - - Action { - id: menuDeleteAction - - text: qsTr("Delete") - onTriggered: deleteDialog.open() - } - - Action { - id: menuRenameAction - - text: qsTr("Rename") - onTriggered: renameDialog.open() - } - - Action { - id: menuAssignAction - - text: qsTr("Assign to the selected node") - enabled: CollectionEditorBackend.rootView.targetNodeSelected - onTriggered: rootView.assignCollectionToSelectedNode(currentCollection.name) - } - } - - ConfirmDeleteCollectionDialog { - id: deleteDialog - - collectionName: currentCollection.name - onAboutToShow: currentCollection.updateItem() - onAccepted: currentCollection.deleteItem() - } - - RenameCollectionDialog { - id: renameDialog - - collectionName: currentCollection.name - onAboutToShow: currentCollection.updateItem() - onAccepted: currentCollection.rename(renameDialog.newCollectionName) - } - - Connections { - target: root.model - - function onModelReset() { - root.closeDialogs() - } - } -} diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionView.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionView.qml deleted file mode 100644 index b57a4f75fe2..00000000000 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionView.qml +++ /dev/null @@ -1,206 +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 QtQuick.Layouts -import HelperWidgets as HelperWidgets -import StudioTheme as StudioTheme -import CollectionEditorBackend - -Item { - id: root - focus: true - - 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 - warningDialog.message = message - warningDialog.open() - } - - // called from C++ when using the delete key - function deleteSelectedCollection() { - collectionListView.deleteCurrentCollection() - } - - function closeDialogs() { - importDialog.reject() - newCollection.reject() - warningDialog.reject() - } - - ImportDialog { - id: importDialog - - backendValue: root.rootView - anchors.centerIn: parent - } - - NewCollectionDialog { - id: newCollection - - anchors.centerIn: parent - } - - Message { - id: warningDialog - - title: "" - message: "" - } - - Rectangle { - // Covers the toolbar color on top to prevent the background - // color for the margin of splitter - - anchors.top: parent.top - anchors.left: parent.left - anchors.right: parent.right - height: topToolbar.height - color: topToolbar.color - } - - SplitView { - id: splitView - - readonly property bool isHorizontal: splitView.orientation === Qt.Horizontal - - orientation: width >= 500 ? Qt.Horizontal : Qt.Vertical - anchors.fill: parent - - onOrientationChanged: detailsView.closeDialogs() - - handle: Item { - id: handleDelegate - - property color color: SplitHandle.pressed ? StudioTheme.Values.themeControlOutlineInteraction - : SplitHandle.hovered ? StudioTheme.Values.themeControlOutlineHover - : StudioTheme.Values.themeControlOutline - - implicitWidth: 1 - implicitHeight: 1 - - Rectangle { - id: handleRect - - property real verticalMargin: splitView.isHorizontal ? StudioTheme.Values.splitterMargin : 0 - property real horizontalMargin: splitView.isHorizontal ? 0 : StudioTheme.Values.splitterMargin - - anchors.fill: parent - anchors.topMargin: handleRect.verticalMargin - anchors.bottomMargin: handleRect.verticalMargin - anchors.leftMargin: handleRect.horizontalMargin - anchors.rightMargin: handleRect.horizontalMargin - - color: handleDelegate.color - } - - containmentMask: Item { - x: splitView.isHorizontal ? ((handleDelegate.width - width) / 2) : 0 - y: splitView.isHorizontal ? 0 : ((handleDelegate.height - height) / 2) - height: splitView.isHorizontal ? handleDelegate.height : StudioTheme.Values.borderHover - width: splitView.isHorizontal ? StudioTheme.Values.borderHover : handleDelegate.width - } - } - - ColumnLayout { - id: collectionsSideBar - spacing: 0 - - SplitView.minimumWidth: 200 - SplitView.maximumWidth: 450 - SplitView.minimumHeight: 200 - SplitView.maximumHeight: 400 - SplitView.fillWidth: !splitView.isHorizontal - SplitView.fillHeight: splitView.isHorizontal - - Rectangle { - id: topToolbar - color: StudioTheme.Values.themeToolbarBackground - - Layout.preferredHeight: StudioTheme.Values.toolbarHeight - Layout.fillWidth: true - Layout.alignment: Qt.AlignTop | Qt.AlignHCenter - - Text { - anchors.verticalCenter: parent.verticalCenter - anchors.left: parent.left - anchors.leftMargin: StudioTheme.Values.toolbarHorizontalMargin - - text: qsTr("Data Models") - font.pixelSize: StudioTheme.Values.baseFontSize - color: StudioTheme.Values.themeTextColor - } - - HelperWidgets.AbstractButton { - id: importCollectionButton - - anchors.verticalCenter: parent.verticalCenter - anchors.right: parent.right - anchors.rightMargin: StudioTheme.Values.toolbarHorizontalMargin - - style: StudioTheme.Values.viewBarButtonStyle - buttonIcon: StudioTheme.Constants.import_medium - tooltip: qsTr("Import a model") - - onClicked: importDialog.open() - } - } - - CollectionListView { // Model Groups - id: collectionListView - - Layout.fillWidth: true - Layout.minimumHeight: bottomSpacer.isExpanded ? 150 : 0 - Layout.fillHeight: !bottomSpacer.isExpanded - Layout.preferredHeight: contentHeight - Layout.maximumHeight: contentHeight - Layout.alignment: Qt.AlignTop | Qt.AlignHCenter - } - - HelperWidgets.IconButton { - id: addCollectionButton - - iconSize: 16 - Layout.fillWidth: true - Layout.minimumWidth: 24 - Layout.alignment: Qt.AlignTop | Qt.AlignHCenter - - tooltip: qsTr("Add a new model") - icon: StudioTheme.Constants.create_medium - onClicked: newCollection.open() - } - - Item { - id: bottomSpacer - - readonly property bool isExpanded: height > 0 - Layout.minimumWidth: 1 - Layout.fillHeight: true - } - } - - CollectionDetailsView { - id: detailsView - - model: root.collectionDetailsModel - backend: root.model - sortedModel: root.collectionDetailsSortFilterModel - SplitView.fillHeight: true - SplitView.fillWidth: true - } - } - - Connections { - target: root.model - - function onModelReset() { - root.closeDialogs() - } - } -} diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/ColorViewDelegate.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/ColorViewDelegate.qml deleted file mode 100644 index b563b6734af..00000000000 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/ColorViewDelegate.qml +++ /dev/null @@ -1,182 +0,0 @@ -// Copyright (C) 2024 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 QtQuick.Shapes -import QtQuick.Templates as T -import HelperWidgets 2.0 as HelperWidgets -import StudioTheme as StudioTheme -import StudioControls as StudioControls -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 - - readonly property real padding: 2 - readonly property real innerItemsHeight: root.height - 2 * root.padding - - width: root.style.controlSize.width - height: root.style.controlSize.height - - clip: true - - signal editorOpened(var item, var editorPopup) - - function closeEditor() { - loader.close() - } - - ColorBackend { - id: colorBackend - } - - StudioControls.ActionIndicator { - id: actionIndicator - style: root.style - __parentControl: root - x: root.padding - y: root.padding - width: actionIndicator.visible ? root.__actionIndicatorWidth : 0 - height: actionIndicator.visible ? root.__actionIndicatorHeight : 0 - } - - Rectangle { - id: preview - x: root.padding + actionIndicator.width - y: root.padding - z: previewMouseArea.containsMouse ? 10 : 0 - implicitWidth: root.innerItemsHeight - implicitHeight: root.innerItemsHeight - 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: { - loader.toggle() - previewMouseArea.forceActiveFocus() - } - } - - Loader { - id: loader - - function ensureLoader() { - if (!loader.active) - loader.active = true - if (loader.sourceComponent === null) - loader.sourceComponent = popupDialogComponent - } - - function open() { - loader.ensureLoader() - loader.item.show(preview) - - if (loader.status === Loader.Ready) - loader.item.originalColor = root.color - } - - function close() { - if (loader.item) - loader.item.close() - } - - function toggle() { - if (loader.item) - loader.close() - else - loader.open() - } - - Component { - id: popupDialogComponent - - - StudioControls.PopupDialog { - id: popupDialog - - property alias color: popup.color - property alias originalColor: popup.originalColor - titleBar: popup.titleBarContent - - width: 260 - - StudioControls.ColorEditorPopup { - id: popup - width: popupDialog.contentWidth - - onActivateColor: function(color) { - colorBackend.activateColor(color) - } - } - - onClosing: { - loader.sourceComponent = null - } - } - } - - sourceComponent: null - - Binding { - target: loader.item - property: "color" - value: root.color - when: loader.status === Loader.Ready - } - - onLoaded: { - loader.item.originalColor = root.color - root.editorOpened(root, loader.item) - } - } - } - - StudioControls.TextField { - id: hexTextField - style: root.style - x: root.padding + actionIndicator.width + preview.width - preview.border.width - y: root.padding - width: root.width - hexTextField.x - 2 * root.padding - height: root.innerItemsHeight - 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/collectionEditorQmlSource/ConfirmDeleteCollectionDialog.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/ConfirmDeleteCollectionDialog.qml deleted file mode 100644 index 60f0f5e7b31..00000000000 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/ConfirmDeleteCollectionDialog.qml +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright (C) 2024 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 HelperWidgets 2.0 as HelperWidgets -import StudioControls 1.0 as StudioControls -import StudioTheme as StudioTheme - -StudioControls.Dialog { - id: root - - required property string collectionName - - title: qsTr("Deleting the model") - clip: true - - contentItem: ColumnLayout { - id: deleteDialogContent // Keep the id here even if it's not used, because the dialog might lose implicitSize - - width: 300 - spacing: 2 - - Text { - Layout.fillWidth: true - - wrapMode: Text.WordWrap - color: StudioTheme.Values.themeTextColor - text: qsTr("Are you sure that you want to delete model \"%1\"?" - + "\nThe model will be deleted permanently.").arg(root.collectionName) - - } - - Item { // spacer - implicitWidth: 1 - implicitHeight: StudioTheme.Values.columnGap - } - - RowLayout { - spacing: StudioTheme.Values.sectionRowSpacing - Layout.alignment: Qt.AlignRight | Qt.AlignVCenter - Layout.fillWidth: true - Layout.preferredHeight: 40 - - HelperWidgets.Button { - text: qsTr("Delete") - onClicked: root.accept() - } - - HelperWidgets.Button { - text: qsTr("Cancel") - onClicked: root.reject() - } - } - } -} diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/EditPropertyDialog.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/EditPropertyDialog.qml deleted file mode 100644 index 546dc5d770c..00000000000 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/EditPropertyDialog.qml +++ /dev/null @@ -1,158 +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.Layouts -import StudioTheme 1.0 as StudioTheme -import StudioControls 1.0 as StudioControls -import HelperWidgets 2.0 as HelperWidgets -import CollectionDetails - -StudioControls.Dialog { - id: root - - required property var model - property int __propertyIndex: -1 - property string __currentName - - title: qsTr("Edit Column") - - function openDialog(index, initialPosition) { - root.__propertyIndex = index - - if (root.__propertyIndex < 0) - return - - root.__currentName = root.model.propertyName(root.__propertyIndex) - nameTextField.text = root.__currentName - nameTextField.selectAll() - nameTextField.forceActiveFocus() - - typeComboBox.initialType = root.model.propertyType(root.__propertyIndex) - typeComboBox.currentIndex = typeComboBox.find(typeComboBox.initialType) - - let newPoint = mapFromGlobal(initialPosition.x, initialPosition.y) - x = newPoint.x - y = newPoint.y - - root.open() - } - - onWidthChanged: { - if (visible && x > parent.width) - root.close() - } - - onAccepted: { - if (nameTextField.text !== "" && nameTextField.text !== root.__currentName) - root.model.renameColumn(root.__propertyIndex, nameTextField.text) - - if (typeComboBox.initialType !== typeComboBox.currentText) - root.model.setPropertyType(root.__propertyIndex, typeComboBox.currentText) - } - - contentItem: Column { - spacing: 5 - - Grid { - columns: 2 - rows: 2 - rowSpacing: 2 - columnSpacing: 25 - verticalItemAlignment: Grid.AlignVCenter - - Text { - text: qsTr("Name") - color: StudioTheme.Values.themeTextColor - verticalAlignment: Text.AlignVCenter - } - - StudioControls.TextField { - id: nameTextField - - actionIndicator.visible: false - translationIndicator.visible: false - - Keys.onEnterPressed: root.accept() - Keys.onReturnPressed: root.accept() - Keys.onEscapePressed: root.reject() - - validator: RegularExpressionValidator { - regularExpression: /^\w+$/ - } - } - - Text { - text: qsTr("Type") - color: StudioTheme.Values.themeTextColor - } - - StudioControls.ComboBox { - id: typeComboBox - - property string initialType - - model: CollectionDataTypeModel{} - textRole: "display" - tooltipRole: "toolTip" - actionIndicatorVisible: false - } - } - - Rectangle { - id: warningBox - - visible: typeComboBox.initialType !== typeComboBox.currentText - color: "transparent" - clip: true - border.color: StudioTheme.Values.themeWarning - width: parent.width - height: warning.height - - Row { - id: warning - - padding: 5 - spacing: 5 - - HelperWidgets.IconLabel { - icon: StudioTheme.Constants.warning_medium - anchors.verticalCenter: parent.verticalCenter - } - - Text { - text: qsTr("Conversion from %1 to %2 may lead to data loss") - .arg(typeComboBox.initialType) - .arg(typeComboBox.currentText) - - width: warningBox.width - 20 - - color: StudioTheme.Values.themeTextColor - wrapMode: Text.WordWrap - } - } - } - - Row { - height: 40 - spacing: 5 - - HelperWidgets.Button { - id: editButton - - text: qsTr("Apply") - enabled: nameTextField.text !== "" - anchors.bottom: parent.bottom - - onClicked: root.accept() - } - - HelperWidgets.Button { - text: qsTr("Cancel") - anchors.bottom: parent.bottom - - onClicked: root.reject() - } - } - } -} diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/IconTextButton.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/IconTextButton.qml deleted file mode 100644 index fe9d094349c..00000000000 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/IconTextButton.qml +++ /dev/null @@ -1,97 +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 QtQuick.Layouts -import StudioTheme as StudioTheme - -Rectangle { - id: root - - 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() - - RowLayout { - id: rowAlign - - anchors.verticalCenter: parent.verticalCenter - spacing: StudioTheme.Values.inputHorizontalPadding - - Text { - id: iconItem - - Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter - text: root.icon - color: StudioTheme.Values.themeTextColor - font.family: StudioTheme.Constants.iconFont.family - font.pixelSize: StudioTheme.Values.bigFont - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter - leftPadding: StudioTheme.Values.inputHorizontalPadding - } - - Text { - id: textItem - - Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter - text: root.text - color: StudioTheme.Values.themeTextColor - font.family: StudioTheme.Constants.font.family - font.pixelSize: StudioTheme.Values.baseFontSize - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter - rightPadding: StudioTheme.Values.inputHorizontalPadding - } - } - - MouseArea { - id: mouseArea - anchors.fill: parent - hoverEnabled: true - 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.themeControlBackground - } - }, - State { - name: "Pressed" - when: mouseArea.pressed - PropertyChanges { - target: root - color: StudioTheme.Values.themeControlBackgroundInteraction - } - }, - State { - name: "Hovered" - when: !mouseArea.pressed && mouseArea.containsMouse - PropertyChanges { - target: root - color: StudioTheme.Values.themeControlBackgroundHover - } - } - ] -} diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/ImportDialog.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/ImportDialog.qml deleted file mode 100644 index 21a5e4e8e04..00000000000 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/ImportDialog.qml +++ /dev/null @@ -1,225 +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 QtQuick.Layouts -import Qt.labs.platform as PlatformWidgets -import HelperWidgets as HelperWidgets -import StudioControls as StudioControls -import StudioTheme as StudioTheme - -StudioControls.Dialog { - id: root - - title: qsTr("Import a model") - anchors.centerIn: parent - closePolicy: Popup.CloseOnEscape - modal: true - - required property var backendValue - - property bool fileExists: false - - onOpened: { - collectionName.text = "Model" - fileName.text = qsTr("Model path") - fileName.selectAll() - fileName.forceActiveFocus() - } - - onRejected: { - fileName.text = "" - } - - function acceptIfIsValid() { - if (btnImport.enabled) - btnImport.onClicked() - } - - RegularExpressionValidator { - id: fileNameValidator - regularExpression: /^(\w[^*> -#include - -namespace QmlDesigner { - -struct CollectionDataTypeModel::Details -{ - CollectionDetails::DataType type; - QString name; - QString description; -}; - -const QList CollectionDataTypeModel::m_orderedDetails{ - {DataType::String, "String", "Text"}, - {DataType::Integer, "Integer", "Whole number that can be positive, negative, or zero"}, - {DataType::Real, "Real", "Number with a decimal"}, - {DataType::Image, "Image", "Image resource"}, - {DataType::Color, "Color", "HEX value"}, - {DataType::Url, "Url", "Resource locator"}, - {DataType::Boolean, "Boolean", "True/false"}, -}; - -CollectionDataTypeModel::CollectionDataTypeModel(QObject *parent) - : QAbstractListModel(parent) -{ -} - -int CollectionDataTypeModel::rowCount([[maybe_unused]] const QModelIndex &parent) const -{ - return m_orderedDetails.size(); -} - -QVariant CollectionDataTypeModel::data(const QModelIndex &index, int role) const -{ - if (!index.isValid()) - return {}; - - if (role == Qt::DisplayRole) - return m_orderedDetails.at(index.row()).name; - if (role == Qt::ToolTipRole) - return m_orderedDetails.at(index.row()).description; - - return {}; -} - -QString CollectionDataTypeModel::dataTypeToString(DataType dataType) -{ - static const QHash dataTypeHash = []() -> QHash { - QHash result; - for (const Details &details : m_orderedDetails) - result.insert(details.type, details.name); - return result; - }(); - - if (dataTypeHash.contains(dataType)) - return dataTypeHash.value(dataType); - - return "Unknown"; -} - -CollectionDetails::DataType CollectionDataTypeModel::dataTypeFromString(const QString &dataType) -{ - static const QHash stringTypeHash = []() -> QHash { - QHash result; - for (const Details &details : m_orderedDetails) - result.insert(details.name, details.type); - return result; - }(); - - if (stringTypeHash.contains(dataType)) - return stringTypeHash.value(dataType); - - return DataType::String; -} - -void CollectionDataTypeModel::registerDeclarativeType() -{ - qmlRegisterType("CollectionDetails", 1, 0, "CollectionDataTypeModel"); -} - -} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondatatypemodel.h b/src/plugins/qmldesigner/components/collectioneditor/collectiondatatypemodel.h deleted file mode 100644 index 1f91aecbff7..00000000000 --- a/src/plugins/qmldesigner/components/collectioneditor/collectiondatatypemodel.h +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright (C) 2024 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" - -#include -#include - -namespace QmlDesigner { - -class CollectionDataTypeModel : public QAbstractListModel -{ - Q_OBJECT - -public: - using DataType = CollectionDetails::DataType; - CollectionDataTypeModel(QObject *parent = nullptr); - - int rowCount(const QModelIndex &parent = QModelIndex()) const override; - QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; - - static Q_INVOKABLE QString dataTypeToString(DataType dataType); - static Q_INVOKABLE DataType dataTypeFromString(const QString &dataType); - - static void registerDeclarativeType(); - -private: - struct Details; - static const QList
m_orderedDetails; -}; - -} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp deleted file mode 100644 index eedfbd3178a..00000000000 --- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp +++ /dev/null @@ -1,882 +0,0 @@ -// Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "collectiondetails.h" - -#include "collectiondatatypemodel.h" -#include "collectioneditorutils.h" -#include "collectionjsonparser.h" - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -namespace QmlDesigner { -#define COLLERR_OK QT_TRANSLATE_NOOP("CollectioParseError", "no error occurred") -#define COLLERR_MAINOBJECT QT_TRANSLATE_NOOP("CollectioParseError", "Document object not found") -#define COLLERR_COLLECTIONNAME QT_TRANSLATE_NOOP("CollectioParseError", "Model name not found") -#define COLLERR_COLLECTIONOBJ QT_TRANSLATE_NOOP("CollectioParseError", "Model is not an object") -#define COLLERR_COLUMNARRAY QT_TRANSLATE_NOOP("CollectioParseError", "Column is not an array") -#define COLLERR_UNKNOWN QT_TRANSLATE_NOOP("CollectioParseError", "Unknown error") - -struct CollectionProperty -{ - using DataType = CollectionDetails::DataType; - - QString name; - DataType type; -}; - -const QMap DataTypeWarning::dataTypeWarnings = { - {DataTypeWarning::CellDataTypeMismatch, "Cell and column data types do not match."} -}; - -class CollectionDetails::Private -{ -public: - QList properties; - QList dataRecords; - CollectionReference reference; - bool isChanged = false; - - bool isValidColumnId(int column) const { return column > -1 && column < properties.size(); } - - bool isValidRowId(int row) const { return row > -1 && row < dataRecords.size(); } -}; - -/** - * @brief getCustomUrl - * Address = - * - * @param value The input value to be evaluated - * @param dataType if the value is a valid url, the data type - * will be stored to this parameter, otherwise, it will be String - * @param urlResult if the value is a valid url, the address - * will be stored in this parameter, otherwise it will be empty. - * @return true if the result is url - */ -static bool getCustomUrl(const QString &value, - CollectionDetails::DataType &dataType, - QUrl *urlResult = nullptr) -{ - static const QRegularExpression urlRegex{ - "^(?
" - "(?https?:\\/\\/" - "(?:www\\.|(?!www))[A-z0-9][A-z0-9-]+[A-z0-9]\\.[^\\s]{2,}|www\\.[A-z0-9][A-z0-9-]+" - "[A-z0-9]\\.[^\\s]{2,}|https?:\\/\\/" - "(?:www\\.|(?!www))[A-z0-9]+\\.[^\\s]{2,}|www\\.[A-z0-9]+\\.[^\\s]{2,})|" // end of Url - "(?(" - "?:(?:[A-z]:)|(?:(?:\\\\|\\/){1,2}\\w+)\\$?)(?:(?:\\\\|\\/)(?:\\w[\\w ]*.*))+)" // end of LocalFile - "){1}$" // end of Address - }; - - const QRegularExpressionMatch match = urlRegex.match(value.trimmed()); - if (match.hasCaptured("Address")) { - dataType = CollectionDetails::DataType::Url; - - if (urlResult) - urlResult->setUrl(match.captured("Address")); - - return true; - } - - if (urlResult) - urlResult->clear(); - - dataType = CollectionDetails::DataType::String; - return false; -} - -/** - * @brief dataTypeFromString - * @param value The string value to be evaluated - * @return Bool, Color, Integer, Real, Url, - * Image if these types are detected within the non-empty string, - * Otherwise it returns String. - * If the value is integer, but it's out of the int range, it will be - * considered as a Real. - */ -static CollectionDetails::DataType dataTypeFromString(const QString &value) -{ - using DataType = CollectionDetails::DataType; - static const QRegularExpression validator{ - "(?^(?:true|false)$)|" - "(?^(?:#(?:(?:[0-9a-fA-F]{2}){3,4}|(?:[0-9a-fA-F]){3,4}))$)|" - "(?^\\d+$)|" - "(?^(?:-?(?:0|[1-9]\\d*)?(?:\\.\\d*)?(?<=\\d|\\.)" - "(?:e-?(?:0|[1-9]\\d*))?|0x[0-9a-f]+)$)"}; - static const int boolIndex = validator.namedCaptureGroups().indexOf("boolean"); - static const int colorIndex = validator.namedCaptureGroups().indexOf("color"); - static const int integerIndex = validator.namedCaptureGroups().indexOf("integer"); - static const int realIndex = validator.namedCaptureGroups().indexOf("real"); - - [[maybe_unused]] static const bool allIndexesFound = - [](const std::initializer_list &captureIndexes) { - QTC_ASSERT(Utils::allOf(captureIndexes, [](int val) { return val > -1; }), return false); - return true; - }({boolIndex, colorIndex, integerIndex, realIndex}); - - if (value.isEmpty()) - return DataType::String; - - const QString trimmedValue = value.trimmed(); - QRegularExpressionMatch match = validator.match(trimmedValue); - - if (match.hasCaptured(boolIndex)) - return DataType::Boolean; - if (match.hasCaptured(colorIndex)) - return DataType::Color; - if (match.hasCaptured(integerIndex)) { - bool isInt = false; - trimmedValue.toInt(&isInt); - return isInt ? DataType::Integer : DataType::Real; - } - if (match.hasCaptured(realIndex)) - return DataType::Real; - - DataType urlType; - if (getCustomUrl(trimmedValue, urlType)) - return urlType; - - return DataType::String; -} - -static CollectionProperty::DataType dataTypeFromJsonValue(const QJsonValue &value) -{ - using DataType = CollectionDetails::DataType; - using JsonType = QJsonValue::Type; - - switch (value.type()) { - case JsonType::Null: - case JsonType::Undefined: - return DataType::String; - case JsonType::Bool: - return DataType::Boolean; - case JsonType::Double: { - if (qFuzzyIsNull(std::remainder(value.toDouble(), 1))) - return DataType::Integer; - return DataType::Real; - } - case JsonType::String: - return dataTypeFromString(value.toString()); - default: - return DataType::String; - } -} - -static QList getColumnsFromImportedJsonArray(const QJsonArray &importedArray) -{ - using DataType = CollectionDetails::DataType; - - QHash resultSet; - QList result; - - for (const QJsonValue &value : importedArray) { - if (value.isObject()) { - const QJsonObject object = value.toObject(); - QJsonObject::ConstIterator element = object.constBegin(); - const QJsonObject::ConstIterator stopItem = object.constEnd(); - - while (element != stopItem) { - const QString propertyName = element.key(); - if (resultSet.contains(propertyName)) { - CollectionProperty &property = result[resultSet.value(propertyName)]; - if (property.type == DataType::Integer) { - const DataType currentCellDataType = dataTypeFromJsonValue(element.value()); - if (currentCellDataType == DataType::Real) - property.type = currentCellDataType; - } - } else { - result.append({propertyName, dataTypeFromJsonValue(element.value())}); - resultSet.insert(propertyName, resultSet.size()); - } - ++element; - } - } - } - - return result; -} - -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::Integer: - return variantValue.toInt(); - case DataType::Real: - return variantValue.toDouble(); - case DataType::Boolean: - return variantValue.toBool(); - case DataType::Color: - return variantValue.value(); - case DataType::Url: - case DataType::Image: - return variantValue.value(); - default: - return variantValue; - } -} - -static QJsonValue variantToJsonValue( - const QVariant &variant, CollectionDetails::DataType type = CollectionDetails::DataType::String) -{ - using DataType = CollectionDetails::DataType; - - switch (type) { - case DataType::Boolean: - return variant.toBool(); - case DataType::Real: - return variant.toDouble(); - case DataType::Integer: - return variant.toInt(); - case DataType::Image: - case DataType::String: - case DataType::Color: - case DataType::Url: - default: - return variant.toString(); - } -} - -inline static bool isEmptyJsonValue(const QJsonValue &value) -{ - return value.isNull() || value.isUndefined() || (value.isString() && value.toString().isEmpty()); -} - -QStringList csvReadLine(const QString &line) -{ - static const QRegularExpression lineRegex{ - "(?:,\\\"|^\\\")(?\\\"\\\"|[\\w\\W]*?)(?=\\\",|\\\"$)" - "|(?:,(?!\\\")|^(?!\\\"))(?[^,]*?)(?=$|,)|(\\\\r\\\\n|\\\\n)"}; - static const int valueIndex = lineRegex.namedCaptureGroups().indexOf("value"); - static const int quoteIndex = lineRegex.namedCaptureGroups().indexOf("quote"); - Q_ASSERT(valueIndex > 0 && quoteIndex > 0); - - QStringList result; - QRegularExpressionMatchIterator iterator = lineRegex.globalMatch(line, 0); - while (iterator.hasNext()) { - const QRegularExpressionMatch match = iterator.next(); - - if (match.hasCaptured(valueIndex)) - result.append(match.captured(valueIndex)); - else if (match.hasCaptured(quoteIndex)) - result.append(match.captured(quoteIndex)); - } - return result; -} - -QString CollectionParseError::errorString() const -{ - switch (errorNo) { - case NoError: - return COLLERR_OK; - case MainObjectMissing: - return COLLERR_MAINOBJECT; - case CollectionNameNotFound: - return COLLERR_COLLECTIONNAME; - case CollectionIsNotObject: - return COLLERR_COLLECTIONOBJ; - case ColumnsBlockIsNotArray: - return COLLERR_COLUMNARRAY; - case UnknownError: - default: - return COLLERR_UNKNOWN; - } -} - -CollectionDetails::CollectionDetails() - : d(new Private()) -{} - -CollectionDetails::CollectionDetails(const CollectionReference &reference) - : CollectionDetails() -{ - d->reference = reference; -} - -void CollectionDetails::resetData(const QJsonDocument &localDocument, - const QString &collectionToImport, - CollectionParseError *error) -{ - CollectionDetails importedCollection = fromLocalJson(localDocument, collectionToImport, error); - d->properties.swap(importedCollection.d->properties); - d->dataRecords.swap(importedCollection.d->dataRecords); -} - -CollectionDetails::CollectionDetails(const CollectionDetails &other) = default; - -CollectionDetails::~CollectionDetails() = default; - -void CollectionDetails::insertColumn(const QString &propertyName, - int colIdx, - const QVariant &defaultValue, - DataType type) -{ - if (containsPropertyName(propertyName)) - return; - - CollectionProperty property = {propertyName, type}; - if (d->isValidColumnId(colIdx)) { - d->properties.insert(colIdx, property); - } else { - colIdx = d->properties.size(); - d->properties.append(property); - } - - const QJsonValue defaultJsonValue = QJsonValue::fromVariant(defaultValue); - for (QJsonArray &record : d->dataRecords) - record.insert(colIdx, defaultJsonValue); - - markChanged(); -} - -bool CollectionDetails::removeColumns(int colIdx, int count) -{ - if (!d->isValidColumnId(colIdx)) - return false; - - int maxCount = d->properties.count() - colIdx; - count = std::min(maxCount, count); - - if (count < 1) - return false; - - d->properties.remove(colIdx, count); - - for (QJsonArray &record : d->dataRecords) { - QJsonArray newElement; - - auto elementItr = record.constBegin(); - auto elementEnd = elementItr + colIdx; - while (elementItr != elementEnd) - newElement.append(*(elementItr++)); - - elementItr += count; - elementEnd = record.constEnd(); - - while (elementItr != elementEnd) - newElement.append(*(elementItr++)); - - record = newElement; - } - - markChanged(); - - return true; -} - -void CollectionDetails::insertEmptyRows(int row, int count) -{ - if (count < 1) - return; - - row = qBound(0, row, rows()); - - insertRecords({}, row, count); - - markChanged(); -} - -bool CollectionDetails::removeRows(int row, int count) -{ - if (!d->isValidRowId(row)) - return false; - - int maxCount = d->dataRecords.count() - row; - count = std::min(maxCount, count); - - if (count < 1) - return false; - - d->dataRecords.remove(row, count); - markChanged(); - return true; -} - -bool CollectionDetails::setPropertyValue(int row, int column, const QVariant &value) -{ - if (!d->isValidRowId(row) || !d->isValidColumnId(column)) - return false; - - QVariant currentValue = data(row, column); - if (value == currentValue) - return false; - - QJsonArray &record = d->dataRecords[row]; - record.replace(column, variantToJsonValue(value, typeAt(column))); - markChanged(); - return true; -} - -bool CollectionDetails::setPropertyName(int column, const QString &value) -{ - if (!d->isValidColumnId(column)) - return false; - - const CollectionProperty &oldProperty = d->properties.at(column); - if (oldProperty.name == value) - return false; - - d->properties.replace(column, {value, oldProperty.type}); - - markChanged(); - return true; -} - -bool CollectionDetails::setPropertyType(int column, DataType type) -{ - if (!d->isValidColumnId(column)) - return false; - - bool changed = false; - CollectionProperty &property = d->properties[column]; - if (property.type != type) - changed = true; - - const DataType formerType = property.type; - property.type = type; - - for (QJsonArray &rowData : d->dataRecords) { - if (column < rowData.size()) { - const QJsonValue value = rowData.at(column); - const QVariant properTypedValue = valueToVariant(value, formerType); - const QJsonValue properTypedJsonValue = variantToJsonValue(properTypedValue, type); - rowData.replace(column, properTypedJsonValue); - changed = true; - } - } - - if (changed) - markChanged(); - - return changed; -} - -CollectionReference CollectionDetails::reference() const -{ - return d->reference; -} - -QVariant CollectionDetails::data(int row, int column) const -{ - if (!d->isValidRowId(row)) - return {}; - - if (!d->isValidColumnId(column)) - return {}; - - const QJsonValue cellValue = d->dataRecords.at(row).at(column); - - return cellValue.toVariant(); -} - -QString CollectionDetails::propertyAt(int column) const -{ - if (!d->isValidColumnId(column)) - return {}; - - return d->properties.at(column).name; -} - -CollectionDetails::DataType CollectionDetails::typeAt(int column) const -{ - if (!d->isValidColumnId(column)) - return {}; - - return d->properties.at(column).type; -} - -CollectionDetails::DataType CollectionDetails::typeAt(int row, int column) const -{ - if (!d->isValidRowId(row) || !d->isValidColumnId(column)) - return {}; - - const QJsonValue cellData = d->dataRecords.at(row).at(column); - return dataTypeFromJsonValue(cellData); -} - -DataTypeWarning::Warning CollectionDetails::cellWarningCheck(int row, int column) const -{ - const QJsonValue cellValue = d->dataRecords.at(row).at(column); - - const DataType columnType = typeAt(column); - const DataType cellType = typeAt(row, column); - - if (isEmptyJsonValue(cellValue)) - return DataTypeWarning::Warning::None; - - if ((columnType == DataType::String || columnType == DataType::Real) && cellType == DataType::Integer) - return DataTypeWarning::Warning::None; - - if (columnType == DataType::Url && cellType == DataType::String) - return DataTypeWarning::Warning::None; - - if (columnType == DataType::Image && (cellType == DataType::Url || cellType == DataType::String)) - return DataTypeWarning::Warning::None; - - if (columnType != cellType) - return DataTypeWarning::Warning::CellDataTypeMismatch; - - return DataTypeWarning::Warning::None; -} - -bool CollectionDetails::containsPropertyName(const QString &propertyName) const -{ - return Utils::anyOf(d->properties, [&propertyName](const CollectionProperty &property) { - return property.name == propertyName; - }); -} - -bool CollectionDetails::hasValidReference() const -{ - return d->reference.node.isValid() && d->reference.name.size(); -} - -bool CollectionDetails::isChanged() const -{ - return d->isChanged; -} - -int CollectionDetails::columns() const -{ - return d->properties.size(); -} - -int CollectionDetails::rows() const -{ - return d->dataRecords.size(); -} - -bool CollectionDetails::markSaved() -{ - if (d->isChanged) { - d->isChanged = false; - return true; - } - return false; -} - -void CollectionDetails::swap(CollectionDetails &other) -{ - d.swap(other.d); -} - -void CollectionDetails::resetReference(const CollectionReference &reference) -{ - if (d->reference != reference) { - d->reference = reference; - markChanged(); - } -} - -QString CollectionDetails::toJson() const -{ - QJsonArray exportedArray; - const int propertyCount = d->properties.count(); - - for (const QJsonArray &record : std::as_const(d->dataRecords)) { - const int valueCount = std::min(int(record.count()), propertyCount); - - QJsonObject exportedElement; - for (int i = 0; i < valueCount; ++i) { - const QJsonValue &value = record.at(i); - if (isEmptyJsonValue(value)) - exportedElement.insert(d->properties.at(i).name, QJsonValue::Null); - else - exportedElement.insert(d->properties.at(i).name, value); - } - - exportedArray.append(exportedElement); - } - - return QString::fromUtf8(QJsonDocument(exportedArray).toJson()); -} - -QString CollectionDetails::toCsv() const -{ - QString content; - - auto gotoNextLine = [&content]() { - if (content.size() && content.back() == ',') - content.back() = '\n'; - else - content += "\n"; - }; - - const int propertyCount = d->properties.count(); - if (propertyCount <= 0) - return ""; - - for (const CollectionProperty &property : std::as_const(d->properties)) - content += property.name + ','; - - gotoNextLine(); - - for (const QJsonArray &record : std::as_const(d->dataRecords)) { - const int valueCount = std::min(int(record.count()), propertyCount); - int i = 0; - for (; i < valueCount; ++i) { - const QJsonValue &value = record.at(i); - - if (value.isDouble()) - content += QString::number(value.toDouble()) + ','; - else if (value.isBool()) - content += value.toBool() ? "true," : "false,"; - else - content += value.toString() + ','; - } - - for (; i < propertyCount; ++i) - content += ','; - - gotoNextLine(); - } - - return content; -} - -QJsonObject CollectionDetails::toLocalJson() const -{ - QJsonObject collectionObject; - QJsonArray columnsArray; - QJsonArray dataArray; - - for (const CollectionProperty &property : std::as_const(d->properties)) { - QJsonObject columnObject; - columnObject.insert("name", property.name); - columnObject.insert("type", CollectionDataTypeModel::dataTypeToString(property.type)); - columnsArray.append(columnObject); - } - - for (const QJsonArray &record : std::as_const(d->dataRecords)) - dataArray.append(record); - - collectionObject.insert("columns", columnsArray); - collectionObject.insert("data", dataArray); - - return collectionObject; -} - -void CollectionDetails::registerDeclarativeType() -{ - typedef CollectionDetails::DataType DataType; - qRegisterMetaType("DataType"); - qmlRegisterUncreatableType("CollectionDetails", 1, 0, "DataType", "Enum type"); - - qRegisterMetaType("Warning"); - qmlRegisterUncreatableType("CollectionDetails", 1, 0, "Warning", "Enum type"); -} - -CollectionDetails CollectionDetails::fromImportedCsv(const QByteArray &document, - const bool &firstRowIsHeader) -{ - QStringList headers; - QJsonArray importedArray; - - QTextStream stream(document); - stream.setEncoding(QStringConverter::Latin1); - - if (firstRowIsHeader && !stream.atEnd()) { - headers = Utils::transform(csvReadLine(stream.readLine()), - [](const QString &value) -> QString { return value.trimmed(); }); - } - - while (!stream.atEnd()) { - const QStringList recordDataList = csvReadLine(stream.readLine()); - int column = -1; - QJsonObject recordData; - for (const QString &cellData : recordDataList) { - if (++column == headers.size()) { - QString proposalName; - int proposalId = column; - do - proposalName = QString("Column %1").arg(++proposalId); - while (headers.contains(proposalName)); - headers.append(proposalName); - } - recordData.insert(headers.at(column), cellData); - } - importedArray.append(recordData); - } - - return fromImportedJson(importedArray, headers); -} - -QList CollectionDetails::fromImportedJson(const QByteArray &jsonContent, - QJsonParseError *error) -{ - QJsonParseError parseError; - - QList collectionObjects = JsonCollectionParser::parseCollectionObjects(jsonContent, - error); - - if (error) - *error = parseError; - - if (parseError.error != QJsonParseError::NoError) - return {}; - return Utils::transform(collectionObjects, [](const CollectionObject &object) { - CollectionDetails result = fromImportedJson(object.array, object.propertyOrder); - result.d->reference.name = object.name; - return result; - }); -} - -CollectionDetails CollectionDetails::fromLocalJson(const QJsonDocument &document, - const QString &collectionName, - CollectionParseError *error) -{ - auto setError = [&error](CollectionParseError::ParseError parseError) { - if (error) - error->errorNo = parseError; - }; - - setError(CollectionParseError::NoError); - - if (document.isObject()) { - QJsonObject collectionMap = document.object(); - if (collectionMap.contains(collectionName)) { - QJsonValue collectionValue = collectionMap.value(collectionName); - if (collectionValue.isObject()) - return fromLocalCollection(collectionValue.toObject()); - else - setError(CollectionParseError::CollectionIsNotObject); - } else { - setError(CollectionParseError::CollectionNameNotFound); - } - } else { - setError(CollectionParseError::MainObjectMissing); - } - - return CollectionDetails{}; -} - -CollectionDetails &CollectionDetails::operator=(const CollectionDetails &other) -{ - CollectionDetails value(other); - swap(value); - return *this; -} - -void CollectionDetails::markChanged() -{ - d->isChanged = true; -} - -void CollectionDetails::insertRecords(const QJsonArray &record, int idx, int count) -{ - if (count < 1) - return; - - QJsonArray localRecord; - const int columnsCount = columns(); - for (int i = 0; i < columnsCount; i++) { - const QJsonValue originalCellData = record.at(i); - if (originalCellData.isArray()) - localRecord.append({}); - else - localRecord.append(originalCellData); - } - - if (idx > d->dataRecords.size() || idx < 0) - idx = d->dataRecords.size(); - - d->dataRecords.insert(idx, count, localRecord); -} - -CollectionDetails CollectionDetails::fromImportedJson(const QJsonArray &importedArray, - const QStringList &propertyPriority) -{ - QList columnData = getColumnsFromImportedJsonArray(importedArray); - if (!propertyPriority.isEmpty()) { - QMap priorityMap; - for (const QString &propertyName : propertyPriority) { - if (!priorityMap.contains(propertyName)) - priorityMap.insert(propertyName, priorityMap.size()); - } - const int lowestPriority = priorityMap.size(); - - Utils::sort(columnData, [&](const CollectionProperty &a, const CollectionProperty &b) { - return priorityMap.value(a.name, lowestPriority) - < priorityMap.value(b.name, lowestPriority); - }); - } - - QList localJsonArray; - for (const QJsonValue &importedRowValue : importedArray) { - QJsonObject importedRowObject = importedRowValue.toObject(); - QJsonArray localRow; - for (const CollectionProperty &property : columnData) - localRow.append(importedRowObject.value(property.name)); - localJsonArray.append(localRow); - } - CollectionDetails result; - result.d->properties = columnData; - result.d->dataRecords = localJsonArray; - result.markSaved(); - - return result; -} - -CollectionDetails CollectionDetails::fromLocalCollection(const QJsonObject &localCollection, - CollectionParseError *error) -{ - auto setError = [&error](CollectionParseError::ParseError parseError) { - if (error) - error->errorNo = parseError; - }; - - CollectionDetails result; - setError(CollectionParseError::NoError); - - if (localCollection.contains("columns")) { - const QJsonValue columnsValue = localCollection.value("columns"); - if (columnsValue.isArray()) { - const QJsonArray columns = columnsValue.toArray(); - for (const QJsonValue &columnValue : columns) { - if (columnValue.isObject()) { - const QJsonObject column = columnValue.toObject(); - const QString columnName = column.value("name").toString(); - if (!columnName.isEmpty()) { - result.insertColumn(columnName, - -1, - {}, - CollectionDataTypeModel::dataTypeFromString( - column.value("type").toString())); - } - } - } - - if (int columnsCount = result.columns()) { - const QJsonArray dataRecords = localCollection.value("data").toArray(); - for (const QJsonValue &dataRecordValue : dataRecords) { - QJsonArray dataRecord = dataRecordValue.toArray(); - while (dataRecord.count() > columnsCount) - dataRecord.removeLast(); - - result.insertRecords(dataRecord); - } - } - } else { - setError(CollectionParseError::ColumnsBlockIsNotArray); - return result; - } - } - - return result; -} - -} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.h b/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.h deleted file mode 100644 index 984feabc0a3..00000000000 --- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.h +++ /dev/null @@ -1,152 +0,0 @@ -// Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include "modelnode.h" - -#include - -QT_BEGIN_NAMESPACE -class QJsonObject; -struct QJsonParseError; -class QVariant; -QT_END_NAMESPACE - -namespace QmlDesigner { - -struct CollectionReference -{ - ModelNode node; - QString name; - - friend auto qHash(const CollectionReference &collection) - { - return qHash(collection.node) ^ ::qHash(collection.name); - } - - bool operator==(const CollectionReference &other) const - { - return node == other.node && name == other.name; - } - - bool operator!=(const CollectionReference &other) const { return !(*this == other); } -}; - -struct CollectionProperty; - -struct DataTypeWarning { -public: - enum Warning { None, CellDataTypeMismatch }; - Q_ENUM(Warning) - - Warning warning = None; - DataTypeWarning(Warning warning) - : warning(warning) - {} - - static QString getDataTypeWarningString(Warning warning) - { - return dataTypeWarnings.value(warning); - } - -private: - Q_GADGET - static const QMap dataTypeWarnings; -}; - -struct CollectionParseError -{ - enum ParseError { - NoError, - MainObjectMissing, - CollectionNameNotFound, - CollectionIsNotObject, - ColumnsBlockIsNotArray, - UnknownError - }; - - ParseError errorNo = ParseError::NoError; - QString errorString() const; -}; - -class CollectionDetails -{ - Q_GADGET - -public: - enum class DataType { Unknown, String, Url, Integer, Real, Boolean, Image, Color }; - Q_ENUM(DataType) - - explicit CollectionDetails(); - CollectionDetails(const CollectionReference &reference); - CollectionDetails(const CollectionDetails &other); - ~CollectionDetails(); - - void resetData(const QJsonDocument &localDocument, - const QString &collectionToImport, - CollectionParseError *error = nullptr); - - void insertColumn(const QString &propertyName, - int colIdx = -1, - const QVariant &defaultValue = {}, - DataType type = DataType::String); - bool removeColumns(int colIdx, int count = 1); - - void insertEmptyRows(int row = 0, int count = 1); - bool removeRows(int row, int count = 1); - bool setPropertyValue(int row, int column, const QVariant &value); - - bool setPropertyName(int column, const QString &value); - bool setPropertyType(int column, DataType type); - - CollectionReference reference() const; - QVariant data(int row, int column) const; - QString propertyAt(int column) const; - DataType typeAt(int column) const; - DataType typeAt(int row, int column) const; - DataTypeWarning::Warning cellWarningCheck(int row, int column) const; - bool containsPropertyName(const QString &propertyName) const; - - bool hasValidReference() const; - bool isChanged() const; - - int columns() const; - int rows() const; - - bool markSaved(); - - void swap(CollectionDetails &other); - void resetReference(const CollectionReference &reference); - - QString toJson() const; - QString toCsv() const; - QJsonObject toLocalJson() const; - - static void registerDeclarativeType(); - - static CollectionDetails fromImportedCsv(const QByteArray &document, - const bool &firstRowIsHeader = true); - static QList fromImportedJson(const QByteArray &jsonContent, - QJsonParseError *error = nullptr); - static CollectionDetails fromLocalJson(const QJsonDocument &document, - const QString &collectionName, - CollectionParseError *error = nullptr); - - CollectionDetails &operator=(const CollectionDetails &other); - -private: - void markChanged(); - void insertRecords(const QJsonArray &record, int idx = -1, int count = 1); - - static CollectionDetails fromImportedJson(const QJsonArray &importedArray, - const QStringList &propertyPriority = {}); - static CollectionDetails fromLocalCollection(const QJsonObject &localCollection, - CollectionParseError *error = nullptr); - - // 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/collectiondetailsmodel.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.cpp deleted file mode 100644 index ab2278fb726..00000000000 --- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.cpp +++ /dev/null @@ -1,632 +0,0 @@ -// Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "collectiondetailsmodel.h" - -#include "collectiondatatypemodel.h" -#include "collectioneditorutils.h" -#include "modelnode.h" - -#include - -#include -#include -#include - -#include -#include -#include -#include - -namespace QmlDesigner { - -CollectionDetailsModel::CollectionDetailsModel(QObject *parent) - : QAbstractTableModel(parent) -{ - connect(this, &CollectionDetailsModel::modelReset, this, &CollectionDetailsModel::updateEmpty); - connect(this, &CollectionDetailsModel::rowsInserted, this, &CollectionDetailsModel::updateEmpty); - connect(this, &CollectionDetailsModel::rowsRemoved, this, &CollectionDetailsModel::updateEmpty); -} - -QHash CollectionDetailsModel::roleNames() const -{ - static QHash roles; - if (roles.isEmpty()) { - roles.insert(QAbstractTableModel::roleNames()); - roles.insert(SelectedRole, "itemSelected"); - roles.insert(DataTypeRole, "dataType"); - roles.insert(ColumnDataTypeRole, "columnType"); - roles.insert(DataTypeWarningRole, "dataTypeWarning"); - } - return roles; -} - -int CollectionDetailsModel::rowCount([[maybe_unused]] const QModelIndex &parent) const -{ - return m_currentCollection.rows(); -} - -int CollectionDetailsModel::columnCount([[maybe_unused]] const QModelIndex &parent) const -{ - return m_currentCollection.columns(); -} - -QVariant CollectionDetailsModel::data(const QModelIndex &index, int role) const -{ - if (!index.isValid()) - return {}; - - QTC_ASSERT(m_currentCollection.hasValidReference(), return {}); - - if (role == SelectedRole) - return (index.column() == m_selectedColumn || index.row() == m_selectedRow); - - 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()); - - if (role == DataTypeWarningRole ) - return QVariant::fromValue(m_currentCollection.cellWarningCheck(index.row(), index.column())); - - return m_currentCollection.data(index.row(), index.column()).toString(); -} - -bool CollectionDetailsModel::setData(const QModelIndex &index, const QVariant &value, int role) -{ - QTC_ASSERT(m_currentCollection.hasValidReference(), return false); - - if (!index.isValid()) - return {}; - - if (role == Qt::EditRole) { - DataTypeWarning::Warning prevWarning = m_currentCollection.cellWarningCheck(index.row(), index.column()); - bool changed = m_currentCollection.setPropertyValue(index.row(), index.column(), value); - - if (changed) { - QList roles = {Qt::DisplayRole, Qt::EditRole}; - - if (prevWarning != m_currentCollection.cellWarningCheck(index.row(), index.column())) - roles << DataTypeWarningRole; - - setHasUnsavedChanges(true); - emit dataChanged(index, index, roles); - } - - return true; - } - - return false; -} - -bool CollectionDetailsModel::setHeaderData(int section, - Qt::Orientation orientation, - const QVariant &value, - [[maybe_unused]] int role) -{ - QTC_ASSERT(m_currentCollection.hasValidReference(), return false); - - if (orientation == Qt::Vertical) - return false; - - bool headerChanged = m_currentCollection.setPropertyName(section, value.toString()); - if (headerChanged) - emit this->headerDataChanged(orientation, section, section); - - return headerChanged; -} - -bool CollectionDetailsModel::insertRows(int row, int count, [[maybe_unused]] const QModelIndex &parent) -{ - QTC_ASSERT(m_currentCollection.hasValidReference(), return false); - - if (count < 1) - return false; - - row = qBound(0, row, rowCount()); - - beginInsertRows({}, row, row + count - 1); - m_currentCollection.insertEmptyRows(row, count); - endInsertRows(); - setHasUnsavedChanges(true); - - return true; -} - -bool CollectionDetailsModel::removeColumns(int column, int count, const QModelIndex &parent) -{ - QTC_ASSERT(m_currentCollection.hasValidReference(), return false); - - if (column < 0 || column >= columnCount(parent) || count < 1) - return false; - - count = std::min(count, columnCount(parent) - column); - beginRemoveColumns(parent, column, column + count - 1); - bool columnsRemoved = m_currentCollection.removeColumns(column, count); - endRemoveColumns(); - - if (!columnCount(parent)) - removeRows(0, rowCount(parent), parent); - - ensureSingleCell(); - setHasUnsavedChanges(true); - return columnsRemoved; -} - -bool CollectionDetailsModel::removeRows(int row, int count, const QModelIndex &parent) -{ - QTC_ASSERT(m_currentCollection.hasValidReference(), return false); - - 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.removeRows(row, count); - endRemoveRows(); - - ensureSingleCell(); - setHasUnsavedChanges(true); - return rowsRemoved; -} - -Qt::ItemFlags CollectionDetailsModel::flags(const QModelIndex &index) const -{ - if (!index.isValid()) - return {}; - - return {Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsEditable}; -} - -QVariant CollectionDetailsModel::headerData(int section, Qt::Orientation orientation, int role) const -{ - if (orientation == Qt::Horizontal) { - if (role == DataTypeRole) - return CollectionDataTypeModel::dataTypeToString(m_currentCollection.typeAt(section)); - else - return m_currentCollection.propertyAt(section); - } - - if (orientation == Qt::Vertical) - return section + 1; - - return {}; -} - -CollectionDetails::DataType CollectionDetailsModel::propertyDataType(int column) const -{ - QTC_ASSERT(m_currentCollection.hasValidReference(), return CollectionDetails::DataType::String); - - return m_currentCollection.typeAt(column); -} - -int CollectionDetailsModel::selectedColumn() const -{ - return m_selectedColumn; -} - -int CollectionDetailsModel::selectedRow() const -{ - return m_selectedRow; -} - -QString CollectionDetailsModel::propertyName(int column) const -{ - QTC_ASSERT(m_currentCollection.hasValidReference(), return {}); - - return m_currentCollection.propertyAt(column); -} - -QString CollectionDetailsModel::propertyType(int column) const -{ - QTC_ASSERT(m_currentCollection.hasValidReference(), return {}); - - return CollectionDataTypeModel::dataTypeToString(m_currentCollection.typeAt(column)); -} - -bool CollectionDetailsModel::isPropertyAvailable(const QString &name) -{ - QTC_ASSERT(m_currentCollection.hasValidReference(), return false); - - return m_currentCollection.containsPropertyName(name); -} - -bool CollectionDetailsModel::addColumn(int column, const QString &name, const QString &propertyType) -{ - QTC_ASSERT(m_currentCollection.hasValidReference(), return false); - - if (m_currentCollection.containsPropertyName(name)) - return false; - - if (column < 0 || column > columnCount()) - column = columnCount(); - - beginInsertColumns({}, column, column); - m_currentCollection.insertColumn(name, - column, - {}, - CollectionDataTypeModel::dataTypeFromString(propertyType)); - endInsertColumns(); - setHasUnsavedChanges(true); - return m_currentCollection.containsPropertyName(name); -} - -bool CollectionDetailsModel::selectColumn(int section) -{ - if (m_selectedColumn == section) - return false; - - const int columns = columnCount(); - - if (section >= columns) - section = columns - 1; - - selectRow(-1); - - 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 CollectionDetailsModel::renameColumn(int section, const QString &newValue) -{ - return setHeaderData(section, Qt::Horizontal, newValue); -} - -bool CollectionDetailsModel::setPropertyType(int column, const QString &newValue) -{ - QTC_ASSERT(m_currentCollection.hasValidReference(), return false); - - bool changed = m_currentCollection.setPropertyType(column, - CollectionDataTypeModel::dataTypeFromString( - newValue)); - if (changed) { - emit headerDataChanged(Qt::Horizontal, column, column); - emit dataChanged( - index(0, column), - index(rowCount() - 1, column), - {Qt::DisplayRole, Qt::EditRole, DataTypeRole, DataTypeWarningRole, ColumnDataTypeRole}); - } - - setHasUnsavedChanges(true); - return changed; -} - -bool CollectionDetailsModel::selectRow(int row) -{ - if (m_selectedRow == row) - return false; - - const int rows = rowCount(); - - if (row >= rows) - row = rows - 1; - - 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 CollectionDetailsModel::deselectAll() -{ - selectColumn(-1); - selectRow(-1); -} - -void CollectionDetailsModel::loadCollection(const ModelNode &sourceNode, const QString &collection) -{ - QString fileName = CollectionEditorUtils::getSourceCollectionPath(sourceNode); - - CollectionReference newReference{sourceNode, collection}; - bool alreadyOpen = m_openedCollections.contains(newReference); - - if (alreadyOpen) { - if (m_currentCollection.reference() != newReference) { - deselectAll(); - beginResetModel(); - switchToCollection(newReference); - ensureSingleCell(); - endResetModel(); - } - } else { - deselectAll(); - switchToCollection(newReference); - loadJsonCollection(fileName, collection); - } -} - -void CollectionDetailsModel::removeCollection(const ModelNode &sourceNode, const QString &collection) -{ - CollectionReference collectionRef{sourceNode, collection}; - if (!m_openedCollections.contains(collectionRef)) - return; - - if (m_currentCollection.reference() == collectionRef) - loadCollection({}, {}); - - m_openedCollections.remove(collectionRef); -} - -void CollectionDetailsModel::removeAllCollections() -{ - loadCollection({}, {}); - m_openedCollections.clear(); -} - -void CollectionDetailsModel::renameCollection(const ModelNode &sourceNode, - const QString &oldName, - const QString &newName) -{ - CollectionReference oldRef{sourceNode, oldName}; - if (!m_openedCollections.contains(oldRef)) - return; - - CollectionReference newReference{sourceNode, newName}; - bool collectionIsSelected = m_currentCollection.reference() == oldRef; - CollectionDetails collection = m_openedCollections.take(oldRef); - collection.resetReference(newReference); - m_openedCollections.insert(newReference, collection); - - if (collectionIsSelected) - setCollectionName(newName); -} - -bool CollectionDetailsModel::saveDataStoreCollections() -{ - const ModelNode node = m_currentCollection.reference().node; - Utils::expected_str jsonContents = m_jsonFilePath.fileContents(); - if (!jsonContents.has_value()) { - qWarning() << __FUNCTION__ << jsonContents.error(); - return false; - } - - QJsonParseError jpe; - QJsonDocument document = QJsonDocument::fromJson(jsonContents.value(), &jpe); - - if (jpe.error == QJsonParseError::NoError) { - QJsonObject obj = document.object(); - - QList collectionsToBeSaved; - for (CollectionDetails &openedCollection : m_openedCollections) { - const CollectionReference reference = openedCollection.reference(); - if (reference.node == node) { - obj.insert(reference.name, openedCollection.toLocalJson()); - collectionsToBeSaved << openedCollection; - } - } - - document.setObject(obj); - - if (CollectionEditorUtils::writeToJsonDocument(m_jsonFilePath, document)) { - const CollectionReference currentReference = m_currentCollection.reference(); - for (CollectionDetails &collection : collectionsToBeSaved) { - collection.markSaved(); - const CollectionReference reference = collection.reference(); - if (reference != currentReference) - closeCollectionIfSaved(reference); - } - setHasUnsavedChanges(false); - return true; - } - } - return false; -} - -bool CollectionDetailsModel::exportCollection(const QUrl &url) -{ - using Core::EditorManager; - using Utils::FilePath; - using Utils::TextFileFormat; - - QTC_ASSERT(m_currentCollection.hasValidReference(), return false); - - bool saved = false; - const FilePath filePath = FilePath::fromUserInput(url.isLocalFile() ? url.toLocalFile() - : url.toString()); - const QString saveFormat = filePath.toFileInfo().suffix().toLower(); - const QString content = saveFormat == "csv" ? m_currentCollection.toCsv() - : m_currentCollection.toJson(); - - TextFileFormat textFileFormat; - textFileFormat.codec = EditorManager::defaultTextCodec(); - textFileFormat.lineTerminationMode = EditorManager::defaultLineEnding(); - QString errorMessage; - saved = textFileFormat.writeFile(filePath, content, &errorMessage); - - if (!saved) - qWarning() << Q_FUNC_INFO << "Unable to write file" << errorMessage; - - return saved; -} - -const CollectionDetails CollectionDetailsModel::upToDateConstCollection( - const CollectionReference &reference) const -{ - using Utils::FilePath; - using Utils::FileReader; - CollectionDetails collection; - - if (m_openedCollections.contains(reference)) { - collection = m_openedCollections.value(reference); - } else { - QUrl url = CollectionEditorUtils::getSourceCollectionPath(reference.node); - FilePath path = FilePath::fromUserInput(url.isLocalFile() ? url.toLocalFile() - : url.toString()); - FileReader file; - - if (!file.fetch(path)) - return collection; - - QJsonParseError jpe; - QJsonDocument document = QJsonDocument::fromJson(file.data(), &jpe); - - if (jpe.error != QJsonParseError::NoError) - return collection; - - collection = CollectionDetails::fromLocalJson(document, reference.name); - collection.resetReference(reference); - } - return collection; -} - -bool CollectionDetailsModel::collectionHasColumn(const CollectionReference &reference, - const QString &columnName) const -{ - const CollectionDetails collection = upToDateConstCollection(reference); - return collection.containsPropertyName(columnName); -} - -QString CollectionDetailsModel::getFirstColumnName(const CollectionReference &reference) const -{ - const CollectionDetails collection = upToDateConstCollection(reference); - return collection.propertyAt(0); -} - -void CollectionDetailsModel::updateEmpty() -{ - bool isEmptyNow = rowCount() == 0; - if (m_isEmpty != isEmptyNow) { - m_isEmpty = isEmptyNow; - emit isEmptyChanged(m_isEmpty); - } -} - -void CollectionDetailsModel::switchToCollection(const CollectionReference &collection) -{ - if (m_currentCollection.reference() == collection) - return; - - closeCurrentCollectionIfSaved(); - - if (!m_openedCollections.contains(collection)) - m_openedCollections.insert(collection, CollectionDetails(collection)); - - m_currentCollection = m_openedCollections.value(collection); - - setCollectionName(collection.name); -} - -void CollectionDetailsModel::closeCollectionIfSaved(const CollectionReference &collection) -{ - if (!m_openedCollections.contains(collection)) - return; - - const CollectionDetails &collectionDetails = m_openedCollections.value(collection); - - if (!collectionDetails.isChanged()) - m_openedCollections.remove(collection); -} - -void CollectionDetailsModel::closeCurrentCollectionIfSaved() -{ - if (m_currentCollection.hasValidReference()) { - closeCollectionIfSaved(m_currentCollection.reference()); - m_currentCollection = CollectionDetails{}; - } -} - -void CollectionDetailsModel::loadJsonCollection(const QString &filePath, const QString &collection) -{ - QJsonDocument document = readJsonFile(filePath); - - beginResetModel(); - m_currentCollection.resetData(document, collection); - ensureSingleCell(); - endResetModel(); -} - -void CollectionDetailsModel::ensureSingleCell() -{ - if (!m_currentCollection.hasValidReference()) - return; - - if (!columnCount()) - addColumn(0, "Column 1", "String"); - - if (!rowCount()) - insertRow(0); - - updateEmpty(); -} - -QJsonDocument CollectionDetailsModel::readJsonFile(const QUrl &url) -{ - using Utils::FilePath; - using Utils::FileReader; - FilePath path = FilePath::fromUserInput(url.isLocalFile() ? url.toLocalFile() : url.toString()); - FileReader file; - - if (!file.fetch(path)) { - emit warning(tr("File reading problem"), file.errorString()); - return {}; - } - - QJsonParseError jpe; - QJsonDocument document = QJsonDocument::fromJson(file.data(), &jpe); - - if (jpe.error != QJsonParseError::NoError) - emit warning(tr("Json parse error"), jpe.errorString()); - - return document; -} - -void CollectionDetailsModel::setCollectionName(const QString &newCollectionName) -{ - if (m_collectionName != newCollectionName) { - m_collectionName = newCollectionName; - emit this->collectionNameChanged(m_collectionName); - } -} - -QString CollectionDetailsModel::warningToString(DataTypeWarning::Warning warning) const -{ - return DataTypeWarning::getDataTypeWarningString(warning); -} - -void CollectionDetailsModel::setJsonFilePath(const Utils::FilePath &filePath) -{ - m_jsonFilePath = filePath; -} - -void CollectionDetailsModel::setHasUnsavedChanges(bool val) -{ - if (m_hasUnsavedChanges == val) - return; - m_hasUnsavedChanges = val; - emit hasUnsavedChangesChanged(); -} - -} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.h b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.h deleted file mode 100644 index fbe90a6f567..00000000000 --- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.h +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include "collectiondetails.h" - -#include - -#include -#include - -namespace QmlDesigner { - -class ModelNode; - -class CollectionDetailsModel : public QAbstractTableModel -{ - Q_OBJECT - - 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) - Q_PROPERTY(bool hasUnsavedChanges MEMBER m_hasUnsavedChanges WRITE setHasUnsavedChanges NOTIFY hasUnsavedChangesChanged) - -public: - enum DataRoles { SelectedRole = Qt::UserRole + 1, DataTypeRole, ColumnDataTypeRole, DataTypeWarningRole }; - explicit CollectionDetailsModel(QObject *parent = nullptr); - - 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, - 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 = {}) override; - bool removeRows(int row, int count, const QModelIndex &parent = {}) override; - - Qt::ItemFlags flags(const QModelIndex &index) const override; - 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; - Q_INVOKABLE QString propertyType(int column) const; - - Q_INVOKABLE bool isPropertyAvailable(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); - Q_INVOKABLE bool selectRow(int row); - Q_INVOKABLE void deselectAll(); - Q_INVOKABLE QString warningToString(DataTypeWarning::Warning warning) const; - - void setJsonFilePath(const Utils::FilePath &filePath); - void loadCollection(const ModelNode &sourceNode, const QString &collection); - void removeCollection(const ModelNode &sourceNode, const QString &collection); - void removeAllCollections(); - void renameCollection(const ModelNode &sourceNode, const QString &oldName, const QString &newName); - - Q_INVOKABLE bool saveDataStoreCollections(); - Q_INVOKABLE bool exportCollection(const QUrl &url); - - const CollectionDetails upToDateConstCollection(const CollectionReference &reference) const; - bool collectionHasColumn(const CollectionReference &reference, const QString &columnName) const; - QString getFirstColumnName(const CollectionReference &reference) const; - void setHasUnsavedChanges(bool val); - -signals: - void collectionNameChanged(const QString &collectionName); - void selectedColumnChanged(int); - void selectedRowChanged(int); - void isEmptyChanged(bool); - void hasUnsavedChangesChanged(); - void warning(const QString &title, const QString &body); - -private slots: - void updateEmpty(); - -private: - void switchToCollection(const CollectionReference &collection); - void closeCollectionIfSaved(const CollectionReference &collection); - void closeCurrentCollectionIfSaved(); - void setCollectionName(const QString &newCollectionName); - void loadJsonCollection(const QString &filePath, const QString &collection); - void ensureSingleCell(); - QJsonDocument readJsonFile(const QUrl &url); - - Utils::FilePath m_jsonFilePath; - QHash m_openedCollections; - CollectionDetails m_currentCollection; - bool m_isEmpty = true; - bool m_hasUnsavedChanges = false; - int m_selectedColumn = -1; - int m_selectedRow = -1; - - QString m_collectionName; -}; - -} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailssortfiltermodel.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailssortfiltermodel.cpp deleted file mode 100644 index 2cc6ac05a6b..00000000000 --- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailssortfiltermodel.cpp +++ /dev/null @@ -1,163 +0,0 @@ -// Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "collectiondetailssortfiltermodel.h" - -#include "collectiondetailsmodel.h" -#include "collectioneditorutils.h" - -#include - -namespace QmlDesigner { - -CollectionDetailsSortFilterModel::CollectionDetailsSortFilterModel(QObject *parent) - : QSortFilterProxyModel(parent) -{ - connect(this, &CollectionDetailsSortFilterModel::rowsInserted, - this, &CollectionDetailsSortFilterModel::updateRowCountChanges); - connect(this, &CollectionDetailsSortFilterModel::rowsRemoved, - this, &CollectionDetailsSortFilterModel::updateRowCountChanges); - connect(this, &CollectionDetailsSortFilterModel::modelReset, - this, &CollectionDetailsSortFilterModel::updateRowCountChanges); - - setDynamicSortFilter(true); -} - -void CollectionDetailsSortFilterModel::setSourceModel(CollectionDetailsModel *model) -{ - m_source = model; - Super::setSourceModel(model); - connect(m_source, &CollectionDetailsModel::selectedColumnChanged, - this, &CollectionDetailsSortFilterModel::updateSelectedColumn); - - connect(m_source, &CollectionDetailsModel::selectedRowChanged, - this, &CollectionDetailsSortFilterModel::updateSelectedRow); -} - -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()); -} - -void CollectionDetailsSortFilterModel::deselectAll() -{ - QTC_ASSERT(m_source, return); - m_source->deselectAll(); -} - -CollectionDetailsSortFilterModel::~CollectionDetailsSortFilterModel() = default; - -bool CollectionDetailsSortFilterModel::filterAcceptsRow(int sourceRow, - const QModelIndex &sourceParent) const -{ - QTC_ASSERT(m_source, return false); - QModelIndex sourceIndex(m_source->index(sourceRow, 0, sourceParent)); - return sourceIndex.isValid(); -} - -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 CollectionEditorUtils::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); - } -} - -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 deleted file mode 100644 index 10f6e09b057..00000000000 --- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailssortfiltermodel.h +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include -#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); - Q_INVOKABLE void deselectAll(); - -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(); - void updateSelectedRow(); - void updateSelectedColumn(); - void updateRowCountChanges(); - - QPointer m_source; - int m_selectedColumn = -1; - int m_selectedRow = -1; - bool m_isEmpty = true; -}; - -} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorconstants.h b/src/plugins/qmldesigner/components/collectioneditor/collectioneditorconstants.h deleted file mode 100644 index 2228c58518c..00000000000 --- a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorconstants.h +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -namespace QmlDesigner::CollectionEditorConstants { - -enum class SourceFormat { Unknown, Json }; - -inline constexpr char SOURCEFILE_PROPERTY[] = "source"; -inline constexpr char ALLMODELS_PROPERTY[] = "allModels"; -inline constexpr char JSONCHILDMODELNAME_PROPERTY[] = "modelName"; - -inline constexpr char COLLECTIONMODEL_IMPORT[] = "QtQuick.Studio.Utils"; -inline constexpr char JSONCOLLECTIONMODEL_TYPENAME[] = "QtQuick.Studio.Utils.JsonListModel"; -inline constexpr char JSONCOLLECTIONCHILDMODEL_TYPENAME[] = "QtQuick.Studio.Utils.ChildListModel"; -inline constexpr char JSONBACKEND_TYPENAME[] = "JsonData"; - -inline constexpr QStringView DEFAULT_DATA_JSON_FILENAME = u"data.json"; -inline constexpr QStringView DEFAULT_MODELS_JSON_FILENAME = u"models.json"; -inline constexpr QStringView DEFAULT_DATASTORE_QML_FILENAME = u"DataStore.qml"; -inline constexpr QStringView DEFAULT_JSONDATA_QML_FILENAME = u"JsonData.qml"; - -} // namespace QmlDesigner::CollectionEditorConstants diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.cpp deleted file mode 100644 index a991e2d729c..00000000000 --- a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.cpp +++ /dev/null @@ -1,218 +0,0 @@ -// Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "collectioneditorutils.h" - -#include "collectiondatatypemodel.h" -#include "collectioneditorconstants.h" -#include "model.h" -#include "nodemetainfo.h" -#include "propertymetainfo.h" -#include "variantproperty.h" - -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include -#include -#include -#include -#include - -using DataType = QmlDesigner::CollectionDetails::DataType; - -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, DataType type) -{ - switch (type) { - case DataType::String: - return value.toString(); - case DataType::Real: - return value.toDouble(); - case DataType::Integer: - return value.toInt(); - case DataType::Boolean: - return value.toBool(); - case DataType::Color: - return value.value(); - case DataType::Image: - 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; - } -}; - -} // namespace - -namespace QmlDesigner::CollectionEditorUtils { - -bool variantIslessThan(const QVariant &a, const QVariant &b, DataType type) -{ - return std::visit(LessThanVisitor{}, valueToVariant(a, type), valueToVariant(b, type)); -} - -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(); -} - -bool hasTextRoleProperty(const ModelNode &node) -{ - const NodeMetaInfo nodeMetaInfo = node.metaInfo(); - if (!nodeMetaInfo.isValid()) - return false; - - const PropertyMetaInfo textRoleProperty = nodeMetaInfo.property("textRole"); - if (!textRoleProperty.isValid()) - return false; - - return textRoleProperty.isWritable() && !textRoleProperty.isPrivate() - && textRoleProperty.propertyType().isString(); -} - -QString getSourceCollectionPath(const ModelNode &dataStoreNode) -{ - using Utils::FilePath; - if (!dataStoreNode.isValid()) - return {}; - - const QUrl dataStoreUrl = dataStoreNode.model()->fileUrl(); - QUrl sourceValue = dataStoreNode.property("source").toVariantProperty().value().toUrl(); - - QUrl sourceUrl = sourceValue.isRelative() ? dataStoreUrl.resolved(sourceValue) : sourceValue; - - const FilePath expectedFile = FilePath::fromUrl(sourceUrl); - - if (expectedFile.isFile() && expectedFile.exists()) - return expectedFile.toFSPathString(); - - const FilePath defaultJsonFile = FilePath::fromUrl( - dataStoreUrl.resolved(CollectionEditorConstants::DEFAULT_MODELS_JSON_FILENAME.toString())); - - if (defaultJsonFile.exists()) - return defaultJsonFile.toFSPathString(); - - return {}; -} - -QJsonObject defaultCollection() -{ - QJsonObject collectionObject; - - QJsonArray columns; - QJsonObject defaultColumn; - defaultColumn.insert("name", "Column 1"); - defaultColumn.insert("type", CollectionDataTypeModel::dataTypeToString(DataType::String)); - columns.append(defaultColumn); - - QJsonArray collectionData; - QJsonArray cellData; - cellData.append(QString{}); - collectionData.append(cellData); - - collectionObject.insert("columns", columns); - collectionObject.insert("data", collectionData); - - return collectionObject; -} - -QJsonObject defaultColorCollection() -{ - using Utils::FilePath; - using Utils::FileReader; - const FilePath templatePath = findFile(Core::ICore::resourcePath(), "Colors.json.tpl"); - - FileReader fileReader; - if (!fileReader.fetch(templatePath)) { - qWarning() << __FUNCTION__ << "Can't read the content of the file" << templatePath; - return {}; - } - - QJsonParseError parseError; - const QList collections = CollectionDetails::fromImportedJson(fileReader.data(), - &parseError); - - if (parseError.error != QJsonParseError::NoError) { - qWarning() << __FUNCTION__ << "Error in template file" << parseError.errorString(); - return {}; - } - - if (!collections.size()) { - qWarning() << __FUNCTION__ << "Can not generate collections from template file!"; - return {}; - } - - const CollectionDetails collection = collections.first(); - return collection.toLocalJson(); -} - -Utils::FilePath findFile(const Utils::FilePath &path, const QString &fileName) -{ - QDirIterator it(path.toString(), QDirIterator::Subdirectories); - - while (it.hasNext()) { - QFileInfo file(it.next()); - if (file.isDir()) - continue; - - if (file.fileName() == fileName) - return Utils::FilePath::fromFileInfo(file); - } - return {}; -} - -bool writeToJsonDocument(const Utils::FilePath &path, const QJsonDocument &document, QString *errorString) -{ - Core::FileChangeBlocker fileBlocker(path); - Utils::FileSaver jsonFile(path); - if (jsonFile.write(document.toJson())) - jsonFile.finalize(); - if (errorString) - *errorString = jsonFile.errorString(); - - return !jsonFile.hasError(); -} - -} // namespace QmlDesigner::CollectionEditorUtils diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.h b/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.h deleted file mode 100644 index 7afc6f233fe..00000000000 --- a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.h +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include "collectiondetails.h" - -QT_BEGIN_NAMESPACE -class QJsonArray; -class QJsonObject; -QT_END_NAMESPACE - -namespace Utils { -class FilePath; -} - -namespace QmlDesigner::CollectionEditorUtils { - -bool variantIslessThan(const QVariant &a, const QVariant &b, CollectionDetails::DataType type); - -QString getSourceCollectionPath(const QmlDesigner::ModelNode &dataStoreNode); - -Utils::FilePath findFile(const Utils::FilePath &path, const QString &fileName); - -bool writeToJsonDocument(const Utils::FilePath &path, - const QJsonDocument &document, - QString *errorString = nullptr); - -bool canAcceptCollectionAsModel(const ModelNode &node); - -bool hasTextRoleProperty(const ModelNode &node); - -QJsonObject defaultCollection(); - -QJsonObject defaultColorCollection(); - -} // namespace QmlDesigner::CollectionEditorUtils diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionjsonparser.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionjsonparser.cpp deleted file mode 100644 index 630abb7b4a4..00000000000 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionjsonparser.cpp +++ /dev/null @@ -1,257 +0,0 @@ -// Copyright (C) 2024 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "collectionjsonparser.h" - -#include -#include -#include - -#include -#include -#include - -namespace QmlDesigner { - -/** - * @brief A json object is a plain object, if it has only primitive properties (not arrays or objects) - * @return true if @param jsonObject is a plain object - */ -inline static bool isPlainObject(const QJsonObject &jsonObj) -{ - return !Utils::anyOf(jsonObj, [](const QJsonValueConstRef &val) { - return val.isArray() || val.isObject(); - }); -} - -static bool isPlainObject(const QJsonValueConstRef &value) -{ - if (!value.isObject()) - return false; - return isPlainObject(value.toObject()); -} - -static QJsonArray parsePlainObject(const QJsonObject &jsonObj) -{ - QJsonObject result; - auto item = jsonObj.constBegin(); - const auto itemEnd = jsonObj.constEnd(); - while (item != itemEnd) { - QJsonValueConstRef ref = item.value(); - if (!ref.isArray() && !ref.isObject()) - result.insert(item.key(), ref); - ++item; - } - if (!result.isEmpty()) - return QJsonArray{result}; - - return {}; -} - -static QJsonArray parseArray(const QJsonArray &array, - QList &plainCollections, - JsonKeyChain &chainTracker) -{ - chainTracker.append(0); - QJsonArray plainArray; - int i = -1; - for (const QJsonValueConstRef &item : array) { - chainTracker.last() = ++i; - if (isPlainObject(item)) { - const QJsonObject plainObject = item.toObject(); - if (plainObject.count()) - plainArray.append(plainObject); - } else if (item.isArray()) { - parseArray(item.toArray(), plainCollections, chainTracker); - } - } - chainTracker.removeLast(); - return plainArray; -} - -static void parseObject(const QJsonObject &jsonObj, - QList &plainCollections, - JsonKeyChain &chainTracker) -{ - chainTracker.append(QString{}); - auto item = jsonObj.constBegin(); - const auto itemEnd = jsonObj.constEnd(); - while (item != itemEnd) { - chainTracker.last() = item.key(); - QJsonValueConstRef ref = item.value(); - QJsonArray parsedArray; - if (ref.isArray()) { - parsedArray = parseArray(ref.toArray(), plainCollections, chainTracker); - } else if (ref.isObject()) { - if (isPlainObject(ref)) - parsedArray = parsePlainObject(ref.toObject()); - else - parseObject(ref.toObject(), plainCollections, chainTracker); - } - if (!parsedArray.isEmpty()) - plainCollections.append({item.key(), parsedArray, chainTracker}); - ++item; - } - chainTracker.removeLast(); -} - -static QList parseDocument(const QJsonDocument &document, - const QString &defaultName = "Model") -{ - QList plainCollections; - JsonKeyChain chainTracker; - if (document.isObject()) { - const QJsonObject documentObject = document.object(); - if (isPlainObject(documentObject)) { - QJsonArray parsedArray = parsePlainObject(documentObject); - if (!parsedArray.isEmpty()) - plainCollections.append({defaultName, parsedArray}); - } else { - parseObject(document.object(), plainCollections, chainTracker); - } - } else { - QJsonArray parsedArray = parseArray(document.array(), plainCollections, chainTracker); - if (!parsedArray.isEmpty()) - plainCollections.append({defaultName, parsedArray, {0}}); - } - return plainCollections; -} - -QList JsonCollectionParser::parseCollectionObjects(const QByteArray &json, - QJsonParseError *error) -{ - QJsonParseError parseError; - QJsonDocument document = QJsonDocument::fromJson(json, &parseError); - if (error) - *error = parseError; - - if (parseError.error != QJsonParseError::NoError) - return {}; - - QList allCollections = parseDocument(document); - QList keyChains = Utils::transform(allCollections, [](const CollectionObject &obj) { - return obj.keyChain; - }); - - JsonCollectionParser jsonVisitor(QString::fromLatin1(json), keyChains); - - for (CollectionObject &collection : allCollections) - collection.propertyOrder = jsonVisitor.collectionPaths.value(collection.keyChain); - - return allCollections; -} - -JsonCollectionParser::JsonCollectionParser(const QString &jsonContent, - const QList &keyChains) -{ - for (const JsonKeyChain &chain : keyChains) - collectionPaths.insert(chain, {}); - - QmlJS::Document::MutablePtr newDoc = QmlJS::Document::create(Utils::FilePath::fromString( - ""), - QmlJS::Dialect::Json); - - newDoc->setSource(jsonContent); - newDoc->parseExpression(); - - if (!newDoc->isParsedCorrectly()) - return; - - newDoc->ast()->accept(this); -} - -bool JsonCollectionParser::visit([[maybe_unused]] QmlJS::AST::ObjectPattern *objectPattern) -{ - propertyOrderStack.push({}); - return true; -} - -void JsonCollectionParser::endVisit([[maybe_unused]] QmlJS::AST::ObjectPattern *objectPattern) - -{ - if (!propertyOrderStack.isEmpty()) { - QStringList objectProperties = propertyOrderStack.top(); - propertyOrderStack.pop(); - checkPropertyUpdates(keyStack, objectProperties); - } -} - -bool JsonCollectionParser::visit(QmlJS::AST::PatternProperty *patternProperty) -{ - const QString propertyName = patternProperty->name->asString(); - if (!propertyOrderStack.isEmpty()) - propertyOrderStack.top().append(propertyName); - - keyStack.push(propertyName); - return true; -} - -void JsonCollectionParser::endVisit(QmlJS::AST::PatternProperty *patternProperty) -{ - const QString propertyName = patternProperty->name->asString(); - - if (auto curIndex = std::get_if(&keyStack.top())) { - if (*curIndex == propertyName) - keyStack.pop(); - } -} - -bool JsonCollectionParser::visit([[maybe_unused]] QmlJS::AST::PatternElementList *patternElementList) -{ - keyStack.push(-1); - return true; -} - -void JsonCollectionParser::endVisit([[maybe_unused]] QmlJS::AST::PatternElementList *patternElementList) -{ - if (std::get_if(&keyStack.top())) - keyStack.pop(); -} - -bool JsonCollectionParser::visit([[maybe_unused]] QmlJS::AST::PatternElement *patternElement) -{ - if (auto curIndex = std::get_if(&keyStack.top())) - *curIndex += 1; - return true; -} - -void JsonCollectionParser::checkPropertyUpdates(QStack stack, - const QStringList &objectProperties) -{ - bool shouldUpdate = collectionPaths.contains(stack); - if (!shouldUpdate && !stack.isEmpty()) { - if (std::get_if(&stack.top())) { - stack.pop(); - shouldUpdate = collectionPaths.contains(stack); - } - } - if (!shouldUpdate) - return; - - QStringList propertyList = collectionPaths.value(stack); - QSet allKeys; - for (const QString &val : std::as_const(propertyList)) - allKeys.insert(val); - - std::optional prevVal; - for (const QString &val : objectProperties) { - if (!allKeys.contains(val)) { - if (prevVal.has_value()) { - const int idx = propertyList.indexOf(prevVal); - propertyList.insert(idx + 1, val); - } else { - propertyList.append(val); - } - allKeys.insert(val); - } - prevVal = val; - } - collectionPaths.insert(stack, propertyList); -} - -void JsonCollectionParser::throwRecursionDepthError() -{ - qWarning() << __FUNCTION__ << "Recursion Depth Error"; -} - -} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionjsonparser.h b/src/plugins/qmldesigner/components/collectioneditor/collectionjsonparser.h deleted file mode 100644 index 16069d3f4cc..00000000000 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionjsonparser.h +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright (C) 2024 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include - -#include -#include - -QT_BEGIN_NAMESPACE -struct QJsonParseError; -QT_END_NAMESPACE - -using JsonKey = std::variant; // Key can be either int (index) or string (property name) - -using JsonKeyChain = QList; // A chain of keys leading to a specific json value - -namespace QmlDesigner { - -struct CollectionObject -{ - QString name; - QJsonArray array = {}; - JsonKeyChain keyChain = {}; - QStringList propertyOrder = {}; -}; - -class JsonCollectionParser : public QmlJS::AST::Visitor -{ -public: - static QList parseCollectionObjects(const QByteArray &json, - QJsonParseError *error = nullptr); - -private: - JsonCollectionParser(const QString &jsonContent, const QList &keyChains); - - bool visit(QmlJS::AST::ObjectPattern *objectPattern) override; - void endVisit(QmlJS::AST::ObjectPattern *objectPattern) override; - - bool visit(QmlJS::AST::PatternProperty *patternProperty) override; - void endVisit(QmlJS::AST::PatternProperty *patternProperty) override; - - bool visit(QmlJS::AST::PatternElementList *patternElementList) override; - void endVisit(QmlJS::AST::PatternElementList *patternElementList) override; - - bool visit(QmlJS::AST::PatternElement *patternElement) override; - - void checkPropertyUpdates(QStack stack, const QStringList &objectProperties); - - void throwRecursionDepthError() override; - - QStack keyStack; - QStack propertyOrderStack; - QMap collectionPaths; // Key chains, Priorities -}; - -} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionlistmodel.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionlistmodel.cpp deleted file mode 100644 index 320bc1bc043..00000000000 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionlistmodel.cpp +++ /dev/null @@ -1,522 +0,0 @@ -// Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "collectionlistmodel.h" - -#include "collectioneditorconstants.h" -#include "collectioneditorutils.h" - -#include -#include -#include - -#include -#include -#include - -namespace { - -template -bool containsItem(const std::initializer_list &container, const ValueType &value) -{ - auto begin = std::cbegin(container); - auto end = std::cend(container); - - auto it = std::find(begin, end, value); - return it != end; -} - -bool sameCollectionNames(QStringList a, QStringList b) -{ - if (a.size() != b.size()) - return false; - - a.sort(Qt::CaseSensitive); - b.sort(Qt::CaseSensitive); - - return a == b; -} - -} // namespace - -namespace QmlDesigner { - -CollectionListModel::CollectionListModel() - : QAbstractListModel() -{ - connect(this, &CollectionListModel::modelReset, this, &CollectionListModel::updateEmpty); - connect(this, &CollectionListModel::rowsRemoved, this, &CollectionListModel::updateEmpty); - connect(this, &CollectionListModel::rowsInserted, this, &CollectionListModel::updateEmpty); -} - -QHash CollectionListModel::roleNames() const -{ - static QHash roles; - if (roles.isEmpty()) { - roles.insert(Super::roleNames()); - roles.insert({ - {IdRole, "collectionId"}, - {NameRole, "collectionName"}, - {SelectedRole, "collectionIsSelected"}, - }); - } - return roles; -} - -int CollectionListModel::rowCount([[maybe_unused]] const QModelIndex &parent) const -{ - return m_data.count(); -} - -bool CollectionListModel::setData(const QModelIndex &index, const QVariant &value, int role) -{ - if (!index.isValid()) - return false; - - if (containsItem({Qt::EditRole, Qt::DisplayRole, NameRole}, role)) { - if (collectionExists(value.toString())) - return false; - - QString oldName = collectionNameAt(index.row()); - bool nameChanged = value != data(index); - if (nameChanged) { - QString newName = value.toString(); - QString errorString; - if (renameCollectionInDataStore(oldName, newName, errorString)) { - m_data.replace(index.row(), newName); - emit dataChanged(index, index, {Qt::DisplayRole, Qt::EditRole, NameRole}); - emit this->collectionNameChanged(oldName, newName); - if (m_selectedCollectionName == oldName) - updateSelectedCollectionName(); - return true; - } else { - emit warning("Rename Model", errorString); - return false; - } - } - } else if (role == SelectedRole) { - if (value.toBool() != index.data(SelectedRole).toBool()) { - setSelectedIndex(value.toBool() ? index.row() : -1); - return true; - } - } - return false; -} - -bool CollectionListModel::removeRows(int row, int count, const QModelIndex &parent) -{ - const int rows = rowCount(parent); - if (row >= rows) - return false; - - row = qBound(0, row, rows - 1); - count = qBound(0, count, rows - row); - - if (count < 1) - return false; - - QString errorString; - QStringList removedCollections = m_data.mid(row, count); - if (removeCollectionsFromDataStore(removedCollections, errorString)) { - beginRemoveRows(parent, row, row + count - 1); - m_data.remove(row, count); - endRemoveRows(); - - emit collectionsRemoved(removedCollections); - if (m_selectedIndex >= row) { - int preferredIndex = m_selectedIndex - count; - if (preferredIndex < 0) // If the selected item is deleted, reset selection - selectCollectionIndex(-1); - selectCollectionIndex(preferredIndex, true); - } - - updateSelectedCollectionName(); - return true; - } else { - emit warning("Remove Model", errorString); - return false; - } -} - -QVariant CollectionListModel::data(const QModelIndex &index, int role) const -{ - QTC_ASSERT(index.isValid(), return {}); - - switch (role) { - case IdRole: - return index.row(); - case SelectedRole: - return index.row() == m_selectedIndex; - case NameRole: - default: - return m_data.at(index.row()); - } -} - -void CollectionListModel::setDataStoreNode(const ModelNode &dataStoreNode) -{ - m_dataStoreNode = dataStoreNode; - update(); -} - -int CollectionListModel::selectedIndex() const -{ - return m_selectedIndex; -} - -ModelNode CollectionListModel::sourceNode() const -{ - return m_dataStoreNode; -} - -bool CollectionListModel::collectionExists(const QString &collectionName) const -{ - return m_data.contains(collectionName); -} - -QStringList CollectionListModel::collections() const -{ - return m_data; -} - -QString CollectionListModel::getUniqueCollectionName(const QString &baseName) const -{ - QString name = baseName.isEmpty() ? "Model" : baseName; - QString nameTemplate = name + "%1"; - - int num = 0; - - while (collectionExists(name)) - name = nameTemplate.arg(++num, 2, 10, QChar('0')); - - return name; -} - -void CollectionListModel::selectCollectionIndex(int idx, bool selectAtLeastOne) -{ - int collectionCount = m_data.size(); - int preferredIndex = -1; - if (collectionCount) { - if (selectAtLeastOne) - preferredIndex = std::max(0, std::min(idx, collectionCount - 1)); - else if (idx > -1 && idx < collectionCount) - preferredIndex = idx; - } - - setSelectedIndex(preferredIndex); -} - -void CollectionListModel::selectCollectionName(QString collectionName, bool selectAtLeastOne) -{ - int idx = m_data.indexOf(collectionName); - if (idx > -1) - selectCollectionIndex(idx); - else - selectCollectionIndex(selectedIndex(), selectAtLeastOne); - - collectionName = collectionNameAt(selectedIndex()); - if (m_selectedCollectionName == collectionName) - return; - - m_selectedCollectionName = collectionName; - emit selectedCollectionNameChanged(m_selectedCollectionName); -} - -QString CollectionListModel::collectionNameAt(int idx) const -{ - return index(idx).data(NameRole).toString(); -} - -QString CollectionListModel::selectedCollectionName() const -{ - return m_selectedCollectionName; -} - -void CollectionListModel::update() -{ - using Utils::FilePath; - using Utils::FileReader; - - FileReader sourceFile; - QString sourceFileAddress = CollectionEditorUtils::getSourceCollectionPath(m_dataStoreNode); - FilePath path = FilePath::fromUserInput(sourceFileAddress); - bool fileRead = false; - if (path.exists()) { - fileRead = sourceFile.fetch(path); - if (!fileRead) - emit this->warning(tr("Model Editor"), - tr("Cannot read the dataStore file\n%1").arg(sourceFile.errorString())); - } - - QStringList collectionNames; - if (fileRead) { - QJsonParseError parseError; - QJsonDocument document = QJsonDocument::fromJson(sourceFile.data(), &parseError); - if (parseError.error != QJsonParseError::NoError) { - emit this->warning(tr("Model Editor"), - tr("There is an error in the JSON file.\n%1") - .arg(parseError.errorString())); - } else { - if (document.isObject()) - collectionNames = document.object().toVariantMap().keys(); - else - emit this->warning(tr("Model Editor"), tr("The JSON document be an object.")); - } - } - - if (!sameCollectionNames(m_data, collectionNames)) { - QString prevSelectedCollection = selectedIndex() > -1 ? m_data.at(selectedIndex()) - : QString(); - beginResetModel(); - m_data = collectionNames; - endResetModel(); - emit this->collectionNamesChanged(collections()); - selectCollectionName(prevSelectedCollection, true); - } -} - -bool CollectionListModel::addCollection(const QString &collectionName, - const QJsonObject &localCollection) -{ - if (collectionExists(collectionName)) { - emit warning(tr("Add Model"), tr("Model \"%1\" already exists.").arg(collectionName)); - return false; - } - - QString errorMessage; - if (addCollectionToDataStore(collectionName, localCollection, errorMessage)) { - int row = rowCount(); - beginInsertRows({}, row, row); - m_data.append(collectionName); - endInsertRows(); - - selectCollectionName(collectionName); - emit collectionAdded(collectionName); - return true; - } else { - emit warning(tr("Add Collection"), errorMessage); - } - return false; -} - -void CollectionListModel::setSelectedIndex(int idx) -{ - idx = (idx > -1 && idx < rowCount()) ? idx : -1; - - if (m_selectedIndex != idx) { - QModelIndex previousIndex = index(m_selectedIndex); - QModelIndex newIndex = index(idx); - - m_selectedIndex = idx; - - if (previousIndex.isValid()) - emit dataChanged(previousIndex, previousIndex, {SelectedRole}); - - if (newIndex.isValid()) - emit dataChanged(newIndex, newIndex, {SelectedRole}); - - emit selectedIndexChanged(idx); - updateSelectedCollectionName(); - } -} - -bool CollectionListModel::removeCollectionsFromDataStore(const QStringList &removedCollections, - QString &error) const -{ - using Utils::FilePath; - using Utils::FileReader; - auto setErrorAndReturn = [&error](const QString &msg) -> bool { - error = msg; - return false; - }; - - if (m_dataStoreNode.type() != CollectionEditorConstants::JSONCOLLECTIONMODEL_TYPENAME) - return setErrorAndReturn(tr("Invalid node type")); - - QString sourceFileAddress = CollectionEditorUtils::getSourceCollectionPath(m_dataStoreNode); - - QFileInfo sourceFileInfo(sourceFileAddress); - if (!sourceFileInfo.isFile()) - return setErrorAndReturn(tr("The selected node has an invalid source address")); - - FilePath jsonPath = FilePath::fromUserInput(sourceFileAddress); - FileReader jsonFile; - if (!jsonFile.fetch(jsonPath)) { - return setErrorAndReturn(tr("Can't read file \"%1\".\n%2") - .arg(sourceFileInfo.absoluteFilePath(), jsonFile.errorString())); - } - - QJsonParseError parseError; - QJsonDocument document = QJsonDocument::fromJson(jsonFile.data(), &parseError); - if (parseError.error != QJsonParseError::NoError) { - return setErrorAndReturn(tr("\"%1\" is corrupted.\n%2") - .arg(sourceFileInfo.absoluteFilePath(), parseError.errorString())); - } - - if (document.isObject()) { - QJsonObject rootObject = document.object(); - - for (const QString &collectionName : removedCollections) { - bool sourceContainsCollection = rootObject.contains(collectionName); - if (sourceContainsCollection) { - rootObject.remove(collectionName); - } else { - setErrorAndReturn(tr("The model group doesn't contain the model name (%1).") - .arg(sourceContainsCollection)); - } - } - - document.setObject(rootObject); - - if (CollectionEditorUtils::writeToJsonDocument(jsonPath, document)) { - error.clear(); - return true; - } else { - return setErrorAndReturn( - tr("Can't write to \"%1\".").arg(sourceFileInfo.absoluteFilePath())); - } - } else { - return setErrorAndReturn(tr("Local Json Document should be an object")); - } - - return false; -} - -bool CollectionListModel::renameCollectionInDataStore(const QString &oldName, - const QString &newName, - QString &error) -{ - using Utils::FilePath; - using Utils::FileReader; - using Utils::FileSaver; - - auto setErrorAndReturn = [&error](const QString &msg) -> bool { - error = msg; - return false; - }; - - if (m_dataStoreNode.type() != CollectionEditorConstants::JSONCOLLECTIONMODEL_TYPENAME) - return setErrorAndReturn(tr("Invalid node type")); - - QString sourceFileAddress = CollectionEditorUtils::getSourceCollectionPath(m_dataStoreNode); - - QFileInfo sourceFileInfo(sourceFileAddress); - if (!sourceFileInfo.isFile()) - return setErrorAndReturn(tr("Selected node must have a valid source file address")); - - FilePath jsonPath = FilePath::fromUserInput(sourceFileAddress); - FileReader jsonFile; - if (!jsonFile.fetch(jsonPath)) { - return setErrorAndReturn( - tr("Can't read \"%1\".\n%2").arg(sourceFileInfo.absoluteFilePath(), jsonFile.errorString())); - } - - QJsonParseError parseError; - QJsonDocument document = QJsonDocument::fromJson(jsonFile.data(), &parseError); - if (parseError.error != QJsonParseError::NoError) { - return setErrorAndReturn(tr("\"%1\" is corrupted.\n%2") - .arg(sourceFileInfo.absoluteFilePath(), parseError.errorString())); - } - - if (document.isObject()) { - QJsonObject rootObject = document.object(); - - bool collectionContainsOldName = rootObject.contains(oldName); - bool collectionContainsNewName = rootObject.contains(newName); - - if (!collectionContainsOldName) { - return setErrorAndReturn( - tr("The model group doesn't contain the old model name (%1).").arg(oldName)); - } - - if (collectionContainsNewName) { - return setErrorAndReturn( - tr("The model name \"%1\" already exists in the model group.").arg(newName)); - } - - QJsonValue oldValue = rootObject.value(oldName); - rootObject.insert(newName, oldValue); - rootObject.remove(oldName); - - document.setObject(rootObject); - - if (CollectionEditorUtils::writeToJsonDocument(jsonPath, document)) { - error.clear(); - return true; - } else { - return setErrorAndReturn( - tr("Can't write to \"%1\".").arg(sourceFileInfo.absoluteFilePath())); - } - } else { - return setErrorAndReturn(tr("Local Json Document should be an object")); - } - return false; -} - -bool CollectionListModel::addCollectionToDataStore(const QString &collectionName, - const QJsonObject &localCollection, - QString &errorString) const -{ - using Utils::FilePath; - using Utils::FileReader; - auto returnError = [&errorString](const QString &msg) -> bool { - errorString = msg; - return false; - }; - - if (collectionExists(collectionName)) - return returnError(tr("A model with the identical name already exists.")); - - QString sourceFileAddress = CollectionEditorUtils::getSourceCollectionPath(m_dataStoreNode); - - QFileInfo sourceFileInfo(sourceFileAddress); - if (!sourceFileInfo.isFile()) - return returnError(tr("Selected node must have a valid source file address")); - - FilePath jsonPath = FilePath::fromUserInput(sourceFileAddress); - FileReader jsonFile; - if (!jsonFile.fetch(jsonPath)) { - return returnError( - tr("Can't read \"%1\".\n%2").arg(sourceFileInfo.absoluteFilePath(), jsonFile.errorString())); - } - - QJsonParseError parseError; - QJsonDocument document = QJsonDocument::fromJson(jsonFile.data(), &parseError); - if (parseError.error != QJsonParseError::NoError) - return returnError(tr("\"%1\" is corrupted.\n%2") - .arg(sourceFileInfo.absoluteFilePath(), parseError.errorString())); - - if (document.isObject()) { - QJsonObject sourceObject = document.object(); - sourceObject.insert(collectionName, localCollection); - document.setObject(sourceObject); - - if (CollectionEditorUtils::writeToJsonDocument(jsonPath, document)) - return true; - else - return returnError(tr("Can't write to \"%1\".").arg(sourceFileInfo.absoluteFilePath())); - } else { - return returnError(tr("JSON document type should be an object containing models.")); - } -} - -void CollectionListModel::updateEmpty() -{ - bool isEmptyNow = m_data.isEmpty(); - if (m_isEmpty != isEmptyNow) { - m_isEmpty = isEmptyNow; - emit isEmptyChanged(m_isEmpty); - - if (m_isEmpty) - setSelectedIndex(-1); - } -} - -void CollectionListModel::updateSelectedCollectionName() -{ - QString selectedCollectionByIndex = collectionNameAt(selectedIndex()); - if (selectedCollectionByIndex != selectedCollectionName()) - selectCollectionName(selectedCollectionByIndex); -} - -} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionlistmodel.h b/src/plugins/qmldesigner/components/collectioneditor/collectionlistmodel.h deleted file mode 100644 index 7902fd59097..00000000000 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionlistmodel.h +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include -#include - -#include "modelnode.h" - -namespace QmlDesigner { - -class CollectionListModel : public QAbstractListModel -{ - Q_OBJECT - - Q_PROPERTY(int selectedIndex MEMBER m_selectedIndex NOTIFY selectedIndexChanged) - Q_PROPERTY(bool isEmpty MEMBER m_isEmpty NOTIFY isEmptyChanged) - Q_PROPERTY(QString selectedCollectionName - READ selectedCollectionName - WRITE selectCollectionName - NOTIFY selectedCollectionNameChanged) - -public: - enum Roles { IdRole = Qt::UserRole + 1, NameRole, SelectedRole }; - - explicit CollectionListModel(); - QHash roleNames() const override; - - int rowCount(const QModelIndex &parent = QModelIndex()) 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; - void setDataStoreNode(const ModelNode &dataStoreNode = {}); - - Q_INVOKABLE int selectedIndex() const; - Q_INVOKABLE ModelNode sourceNode() const; - Q_INVOKABLE bool collectionExists(const QString &collectionName) const; - Q_INVOKABLE QStringList collections() const; - Q_INVOKABLE QString getUniqueCollectionName(const QString &baseName = {}) const; - - void selectCollectionIndex(int idx, bool selectAtLeastOne = false); - void selectCollectionName(QString collectionName, bool selectAtLeastOne = false); - QString collectionNameAt(int idx) const; - QString selectedCollectionName() const; - - void update(); - bool addCollection(const QString &collectionName, const QJsonObject &localCollection); - -signals: - void selectedIndexChanged(int idx); - void isEmptyChanged(bool); - void collectionNameChanged(const QString &oldName, const QString &newName); - void collectionNamesChanged(const QStringList &collectionNames); - void collectionsRemoved(const QStringList &names); - void collectionAdded(const QString &name); - void selectedCollectionNameChanged(const QString &selectedCollectionName); - void warning(const QString &title, const QString &body); - -private: - void setSelectedIndex(int idx); - bool removeCollectionsFromDataStore(const QStringList &removedCollections, QString &error) const; - bool renameCollectionInDataStore(const QString &oldName, const QString &newName, QString &error); - bool addCollectionToDataStore(const QString &collectionName, - const QJsonObject &localCollection, - QString &errorString) const; - - void updateEmpty(); - void updateSelectedCollectionName(); - - using Super = QAbstractListModel; - int m_selectedIndex = -1; - bool m_isEmpty = false; - ModelNode m_dataStoreNode; - QString m_selectedCollectionName; - QStringList m_data; -}; - -} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp deleted file mode 100644 index 8a9e6a8276b..00000000000 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp +++ /dev/null @@ -1,686 +0,0 @@ -// Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "collectionview.h" - -#include "collectiondatatypemodel.h" -#include "collectiondetailsmodel.h" -#include "collectioneditorconstants.h" -#include "collectioneditorutils.h" -#include "collectionlistmodel.h" -#include "collectionwidget.h" -#include "datastoremodelnode.h" -#include "designmodecontext.h" -#include "nodeabstractproperty.h" -#include "nodemetainfo.h" -#include "nodeproperty.h" -#include "qmldesignerplugin.h" -#include "variantproperty.h" - -#include -#include -#include -#include -#include - -#include -#include -#include - -#include - -namespace { - -bool isStudioCollectionModel(const QmlDesigner::ModelNode &node) -{ - return node.metaInfo().isQtQuickStudioUtilsJsonListModel(); -} - -inline bool isProjectImport(const QmlDesigner::Import &import) -{ - ProjectExplorer::Project *currentProject = ProjectExplorer::ProjectManager::startupProject(); - return currentProject && import.toString() == currentProject->displayName(); -} - -inline void setVariantPropertyValue(const QmlDesigner::ModelNode &node, - const QmlDesigner::PropertyName &propertyName, - const QVariant &value) -{ - QmlDesigner::VariantProperty property = node.variantProperty(propertyName); - property.setValue(value); -} - -inline void setNodePropertyValue(const QmlDesigner::ModelNode &node, - const QmlDesigner::PropertyName &propertyName, - const QmlDesigner::ModelNode &value) -{ - QmlDesigner::NodeProperty nodeProperty = node.nodeProperty(propertyName); - // Remove the old model node if is available - if (nodeProperty.modelNode()) - nodeProperty.modelNode().destroy(); - - nodeProperty.setModelNode(value); -} - -inline void setBindingPropertyExpression(const QmlDesigner::ModelNode &node, - const QmlDesigner::PropertyName &propertyName, - const QString &expression) -{ - QmlDesigner::BindingProperty property = node.bindingProperty(propertyName); - property.setExpression(expression); -} - -} // namespace - -namespace QmlDesigner { - -CollectionView::CollectionView(ExternalDependenciesInterface &externalDependencies) - : AbstractView(externalDependencies) - , m_dataStore(std::make_unique()) - -{} - -CollectionView::~CollectionView() = default; - -bool CollectionView::hasWidget() const -{ - return true; -} - -QmlDesigner::WidgetInfo CollectionView::widgetInfo() -{ - if (!m_widget) { - m_widget = Utils::makeUniqueObjectPtr(this); - m_widget->setMinimumSize(m_widget->minimumSizeHint()); - connect(ProjectExplorer::ProjectManager::instance(), - &ProjectExplorer::ProjectManager::startupProjectChanged, m_widget.get(), [&] { - resetDataStoreNode(); - m_widget->collectionDetailsModel()->removeAllCollections(); - }); - - auto collectionEditorContext = new Internal::CollectionEditorContext(m_widget.get()); - Core::ICore::addContextObject(collectionEditorContext); - CollectionListModel *listModel = m_widget->listModel().data(); - - connect(listModel, - &CollectionListModel::selectedCollectionNameChanged, - this, - [this](const QString &collection) { - m_widget->collectionDetailsModel()->loadCollection(dataStoreNode(), collection); - }); - - connect(listModel, &CollectionListModel::isEmptyChanged, this, [this](bool isEmpty) { - if (isEmpty) - m_widget->collectionDetailsModel()->loadCollection({}, {}); - }); - - connect(listModel, &CollectionListModel::modelReset, this, [this] { - CollectionListModel *listModel = m_widget->listModel().data(); - if (listModel->sourceNode() == dataStoreNode()) - m_dataStore->setCollectionNames(listModel->collections()); - }); - - connect(listModel, - &CollectionListModel::collectionAdded, - this, - [this](const QString &collectionName) { m_dataStore->addCollection(collectionName); }); - - connect(listModel, - &CollectionListModel::collectionNameChanged, - this, - [this](const QString &oldName, const QString &newName) { - m_dataStore->renameCollection(oldName, newName); - m_widget->collectionDetailsModel()->renameCollection(dataStoreNode(), - oldName, - newName); - }); - - connect(listModel, - &CollectionListModel::collectionsRemoved, - this, - [this](const QStringList &collectionNames) { - m_dataStore->removeCollections(collectionNames); - for (const QString &collectionName : collectionNames) { - m_widget->collectionDetailsModel()->removeCollection(dataStoreNode(), - collectionName); - } - }); - } - - return createWidgetInfo(m_widget.get(), - "CollectionEditor", - WidgetInfo::LeftPane, - 0, - tr("Model Editor [beta]"), - tr("Model Editor view")); -} - -void CollectionView::modelAttached(Model *model) -{ - AbstractView::modelAttached(model); - m_widget->setProjectImportExists(Utils::anyOf(model->imports(), isProjectImport)); - resetDataStoreNode(); -} - -void CollectionView::modelAboutToBeDetached([[maybe_unused]] Model *model) -{ - unloadDataStore(); - m_widget->setProjectImportExists(false); -} - -void CollectionView::selectedNodesChanged(const QList &selectedNodeList, - [[maybe_unused]] const QList &lastSelectedNodeList) -{ - if (!m_widget) - return; - - QList selectedCollectionNodes = Utils::filtered(selectedNodeList, - &isStudioCollectionModel); - - bool singleNonCollectionNodeSelected = selectedNodeList.size() == 1 - && selectedCollectionNodes.isEmpty(); - - bool singleSelectedHasModelProperty = false; - if (singleNonCollectionNodeSelected) { - const ModelNode selectedNode = selectedNodeList.first(); - singleSelectedHasModelProperty = CollectionEditorUtils::canAcceptCollectionAsModel( - selectedNode); - } - - m_widget->setTargetNodeSelected(singleSelectedHasModelProperty); -} - -void CollectionView::importsChanged(const Imports &addedImports, const Imports &removedImports) -{ - if (Utils::anyOf(addedImports, isProjectImport)) { - m_widget->setProjectImportExists(true); - resetDataStoreNode(); - } else if (Utils::anyOf(removedImports, isProjectImport)) { - m_widget->setProjectImportExists(false); - unloadDataStore(); - } -} - -void CollectionView::customNotification(const AbstractView *, - const QString &identifier, - const QList &nodeList, - const QList &data) -{ - if (!m_widget) - return; - - if (identifier == QLatin1String("item_library_created_by_drop") && !nodeList.isEmpty()) - onItemLibraryNodeCreated(nodeList.first()); - else if (identifier == QLatin1String("open_collection_by_id") && !data.isEmpty()) - m_widget->openCollection(collectionNameFromDataStoreChildren(data.first().toByteArray())); - else if (identifier == "delete_selected_collection") - m_widget->deleteSelectedCollection(); -} - -void CollectionView::addResource(const QUrl &url, const QString &name) -{ - executeInTransaction(Q_FUNC_INFO, [this, &url, &name]() { - ensureStudioModelImport(); - QString sourceAddress; - if (url.isLocalFile()) { - Utils::FilePath fp = QmlDesignerPlugin::instance()->currentDesignDocument()->fileName().parentDir(); - sourceAddress = Utils::FilePath::calcRelativePath(url.toLocalFile(), - fp.absoluteFilePath().toString()); - } else { - sourceAddress = url.toString(); - } -#ifdef QDS_USE_PROJECTSTORAGE - ModelNode resourceNode = createModelNode("JsonListModel"); -#else - const NodeMetaInfo resourceMetaInfo = jsonCollectionMetaInfo(); - ModelNode resourceNode = createModelNode(resourceMetaInfo.typeName(), - resourceMetaInfo.majorVersion(), - resourceMetaInfo.minorVersion()); -#endif - VariantProperty sourceProperty = resourceNode.variantProperty( - CollectionEditorConstants::SOURCEFILE_PROPERTY); - VariantProperty nameProperty = resourceNode.variantProperty("objectName"); - sourceProperty.setValue(sourceAddress); - nameProperty.setValue(name); - resourceNode.setIdWithoutRefactoring(model()->generateNewId(name, "model")); - rootModelNode().defaultNodeAbstractProperty().reparentHere(resourceNode); - }); -} - -void CollectionView::addProjectImport() -{ - if (!m_widget) - return; - - ProjectExplorer::Project *currentProject = ProjectExplorer::ProjectManager::startupProject(); - if (!currentProject) - return; - - executeInTransaction(__FUNCTION__, [&] { - Import import = Import::createLibraryImport(currentProject->displayName()); - if (!model()->hasImport(import, true, true)) - model()->changeImports({import}, {}); - }); -} - -void CollectionView::assignCollectionToNode(const QString &collectionName, const ModelNode &node) -{ - if (!m_widget) - return; - - executeInTransaction("CollectionView::assignCollectionToNode", [&]() { - m_dataStore->assignCollectionToNode( - this, - node, - collectionName, - [&](const QString &collectionName, const QString &columnName) -> bool { - const CollectionReference reference{dataStoreNode(), collectionName}; - return m_widget->collectionDetailsModel()->collectionHasColumn(reference, columnName); - }, - [&](const QString &collectionName) -> QString { - const CollectionReference reference{dataStoreNode(), collectionName}; - return m_widget->collectionDetailsModel()->getFirstColumnName(reference); - }); - - // Create and assign a delegate to the list view item - if (node.metaInfo().isQtQuickListView()) { - ::setNodePropertyValue(node, "delegate", createListViewDelegate(collectionName)); - } else if (node.metaInfo().isQtQuickGridView()) { - QSize delegateSize; - ::setNodePropertyValue(node, - "delegate", - createGridViewDelegate(collectionName, delegateSize)); - ::setVariantPropertyValue(node, "cellWidth", delegateSize.width()); - ::setVariantPropertyValue(node, "cellHeight", delegateSize.height()); - } - }); -} - -void CollectionView::assignCollectionToSelectedNode(const QString &collectionName) -{ - QTC_ASSERT(dataStoreNode() && hasSingleSelectedModelNode(), return); - assignCollectionToNode(collectionName, singleSelectedModelNode()); -} - -void CollectionView::addNewCollection(const QString &collectionName, const QJsonObject &localCollection) -{ - if (!m_widget) - return; - - addTask(QSharedPointer( - new AddCollectionTask(this, m_widget->listModel(), localCollection, collectionName))); -} - -void CollectionView::openCollection(const QString &collectionName) -{ - if (!m_widget) - return; - - m_widget->openCollection(collectionName); -} - -void CollectionView::registerDeclarativeType() -{ - CollectionDetails::registerDeclarativeType(); - CollectionDataTypeModel::registerDeclarativeType(); -} - -void CollectionView::resetDataStoreNode() -{ - if (!m_widget) - return; - - auto compUtils = QmlDesignerPlugin::instance()->documentManager().generatedComponentUtils(); - m_dataStore->reloadModel(compUtils.projectModulePath()); - - ModelNode dataStore = dataStoreNode(); - m_widget->setDataStoreExists(dataStore.isValid()); - if (!dataStore || m_widget->listModel()->sourceNode() == dataStore) - return; - - bool dataStoreSingletonFound = m_dataStoreTypeFound; - if (!dataStoreSingletonFound && rewriterView() && rewriterView()->isAttached()) { - const QList types = rewriterView()->getQMLTypes(); - for (const QmlTypeData &cppTypeData : types) { - if (cppTypeData.isSingleton && cppTypeData.typeName == "DataStore") { - dataStoreSingletonFound = true; - break; - } - } - if (!dataStoreSingletonFound && !m_rewriterAmended) { - rewriterView()->forceAmend(); - m_rewriterAmended = true; - } - } - - if (dataStoreSingletonFound) { - m_widget->listModel()->setDataStoreNode(dataStore); - m_widget->collectionDetailsModel()->setJsonFilePath(m_dataStore->jsonFilePath()); - m_dataStoreTypeFound = true; - resetPuppet(); - - while (!m_delayedTasks.isEmpty()) - m_delayedTasks.takeFirst()->process(); - } else if (++m_reloadCounter < 50) { - QTimer::singleShot(200, this, &CollectionView::resetDataStoreNode); - } else { - QTC_ASSERT(false, m_delayedTasks.clear()); - } -} - -ModelNode CollectionView::dataStoreNode() const -{ - return m_dataStore->modelNode(); -} - -void CollectionView::ensureDataStoreExists() -{ - bool filesJustCreated = false; - bool filesExist = createDataStore(filesJustCreated); - if (filesExist && filesJustCreated) { - // Force code model reset to notice changes to existing module - if (auto modelManager = QmlJS::ModelManagerInterface::instance()) - modelManager->resetCodeModel(); - resetDataStoreNode(); - } -} - -QString CollectionView::collectionNameFromDataStoreChildren(const PropertyName &childPropertyName) const -{ - return dataStoreNode() - .nodeProperty(childPropertyName) - .modelNode() - .property(CollectionEditorConstants::JSONCHILDMODELNAME_PROPERTY) - .toVariantProperty() - .value() - .toString(); -} - -NodeMetaInfo CollectionView::jsonCollectionMetaInfo() const -{ - return model()->metaInfo(CollectionEditorConstants::JSONCOLLECTIONMODEL_TYPENAME); -} - -void CollectionView::unloadDataStore() -{ - m_reloadCounter = 0; - m_rewriterAmended = false; - m_dataStoreTypeFound = false; - QTC_ASSERT(m_delayedTasks.isEmpty(), m_delayedTasks.clear()); - if (m_widget) { - m_widget->setDataStoreExists(dataStoreNode().isValid()); - m_widget->listModel()->setDataStoreNode(); - } -} - -bool CollectionView::createDataStore(bool &justCreated) const -{ - using Utils::FilePath; - using Utils::FileReader; - using Utils::FileSaver; - using namespace Qt::StringLiterals; - - auto compUtils = QmlDesignerPlugin::instance()->documentManager().generatedComponentUtils(); - FilePath projectModulePath = compUtils.projectModulePath(true); - - FilePath qmlTargetPath = projectModulePath.resolvePath( - CollectionEditorConstants::DEFAULT_DATASTORE_QML_FILENAME.toString()); - justCreated = false; - - auto extractDependency = [&justCreated](const FilePath &filePath) -> bool { - if (filePath.exists()) - return true; - - const QString templateFileName = filePath.fileName() + u".tpl"; - const FilePath templatePath = CollectionEditorUtils::findFile(Core::ICore::resourcePath(), - templateFileName); - if (!templatePath.exists()) { - qWarning() << Q_FUNC_INFO << __LINE__ << templateFileName << "does not exist"; - return false; - } - - if (!filePath.parentDir().ensureWritableDir()) { - qWarning() << Q_FUNC_INFO << __LINE__ << "Cannot create directory" - << filePath.parentDir(); - return false; - } - - if (templatePath.copyFile(filePath)) { - justCreated = true; - return true; - } - - qWarning() << Q_FUNC_INFO << __LINE__ << "Cannot copy" << templateFileName << "to" << filePath; - return false; - }; - - if (!extractDependency(projectModulePath.resolvePath( - CollectionEditorConstants::DEFAULT_MODELS_JSON_FILENAME.toString()))) { - return false; - } - - if (!extractDependency(projectModulePath.resolvePath( - CollectionEditorConstants::DEFAULT_DATA_JSON_FILENAME.toString()))) { - return false; - } - - if (!extractDependency(projectModulePath.resolvePath( - CollectionEditorConstants::DEFAULT_JSONDATA_QML_FILENAME.toString()))) { - return false; - } - - if (!qmlTargetPath.exists()) { - if (qmlTargetPath.ensureExistingFile()) { - justCreated = true; - } else { - qWarning() << Q_FUNC_INFO << __LINE__ << "Can't create DataStore Qml File"; - return false; - } - } - - FilePath qmlDirPath = projectModulePath.resolvePath("qmldir"_L1); - qmlDirPath.ensureExistingFile(); - - FileReader qmlDirReader; - if (!qmlDirReader.fetch(qmlDirPath)) { - qWarning() << Q_FUNC_INFO << __LINE__ << "Can't read the content of the qmldir"; - return false; - } - - QByteArray qmlDirContent = qmlDirReader.data(); - const QList qmlDirLines = qmlDirContent.split('\n'); - for (const QByteArray &line : qmlDirLines) { - if (line.startsWith("singleton DataStore ")) - return true; - } - - if (!qmlDirContent.isEmpty() && qmlDirContent.back() != '\n') - qmlDirContent.append("\n"); - qmlDirContent.append("singleton DataStore 1.0 DataStore.qml\n"); - - FileSaver qmlDirSaver(qmlDirPath); - qmlDirSaver.write(qmlDirContent); - - if (qmlDirSaver.finalize()) { - justCreated = true; - return true; - } - - qWarning() << Q_FUNC_INFO << __LINE__ << "Can't write to the qmldir file"; - return false; -} - -void CollectionView::ensureStudioModelImport() -{ - executeInTransaction(__FUNCTION__, [&] { - Import import = Import::createLibraryImport(CollectionEditorConstants::COLLECTIONMODEL_IMPORT); - try { - if (!model()->hasImport(import, true, true)) - model()->changeImports({import}, {}); - } catch (const Exception &) { - QTC_ASSERT(false, return); - } - }); -} - -void CollectionView::onItemLibraryNodeCreated(const ModelNode &node) -{ - if (!m_widget) - return; - - if (node.metaInfo().isListOrGridView()) { - addTask(QSharedPointer( - new DropListViewTask(this, m_widget->listModel(), node))); - } -} - -void CollectionView::addTask(QSharedPointer task) -{ - ensureDataStoreExists(); - if (m_dataStoreTypeFound) - task->process(); - else if (dataStoreNode()) - m_delayedTasks << task; -} - -ModelNode CollectionView::createListViewDelegate(const QString &collectionName) -{ - using DataType = CollectionDetails::DataType; - using namespace Qt::StringLiterals; - - CollectionDetails collection = m_widget->collectionDetailsModel()->upToDateConstCollection( - {dataStoreNode(), collectionName}); - - ModelNode rowItem(createModelNode("QtQuick.Row")); - ::setVariantPropertyValue(rowItem, "spacing", 5); - - const int columnsCount = collection.columns(); - for (int column = 0; column < columnsCount; ++column) { - const DataType dataType = collection.typeAt(column); - const QString columnName = collection.propertyAt(column); - ModelNode cellItem; - if (dataType == DataType::Color) { - cellItem = createModelNode("QtQuick.Rectangle"); - ::setBindingPropertyExpression(cellItem, "color", columnName); - ::setVariantPropertyValue(cellItem, "height", 20); - } else { - cellItem = createModelNode("QtQuick.Text"); - ::setBindingPropertyExpression(cellItem, "text", columnName); - ::setVariantPropertyValue(cellItem, "clip", true); - ::setBindingPropertyExpression(cellItem, "elide", "Text.ElideRight"_L1); - ::setVariantPropertyValue(cellItem, "leftPadding", 1); - ::setVariantPropertyValue(cellItem, "rightPadding", 1); - } - ::setVariantPropertyValue(cellItem, "width", 100); - rowItem.defaultNodeAbstractProperty().reparentHere(cellItem); - } - return rowItem; -} - -ModelNode CollectionView::createGridViewDelegate(const QString &collectionName, QSize &delegateSize) -{ - using DataType = CollectionDetails::DataType; - using namespace Qt::StringLiterals; - - CollectionDetails collection = m_widget->collectionDetailsModel()->upToDateConstCollection( - {dataStoreNode(), collectionName}); - - constexpr int spacing = 5; - int delegateWidth = 70; - int delegateHeight = 0; - - ModelNode rectItem(createModelNode("QtQuick.Rectangle")); - ::setVariantPropertyValue(rectItem, "clip", true); - ::setVariantPropertyValue(rectItem, "border.color", QColor(Qt::blue)); - ::setVariantPropertyValue(rectItem, "border.width", 1); - - ModelNode colItem(createModelNode("QtQuick.Column")); - ::setVariantPropertyValue(colItem, "spacing", spacing); - ::setVariantPropertyValue(colItem, "topPadding", spacing); - ::setBindingPropertyExpression(colItem, "anchors.fill", "parent"_L1); - - const int columnsCount = collection.columns(); - for (int column = 0; column < columnsCount; ++column) { - const DataType dataType = collection.typeAt(column); - const QString columnName = collection.propertyAt(column); - ModelNode cellItem; - if (dataType == DataType::Color) { - cellItem = createModelNode("QtQuick.Rectangle"); - ::setBindingPropertyExpression(cellItem, "color", columnName); - ::setVariantPropertyValue(cellItem, "width", 40); - ::setVariantPropertyValue(cellItem, "height", 40); - delegateHeight += 40; - } else { - cellItem = createModelNode("QtQuick.Text"); - ::setBindingPropertyExpression(cellItem, "width", "parent.width"_L1); - ::setVariantPropertyValue(cellItem, "height", 20); - ::setBindingPropertyExpression(cellItem, "text", columnName); - ::setVariantPropertyValue(cellItem, "clip", true); - ::setBindingPropertyExpression(cellItem, "elide", "Text.ElideRight"_L1); - ::setVariantPropertyValue(cellItem, "leftPadding", 1); - ::setVariantPropertyValue(cellItem, "rightPadding", 1); - ::setBindingPropertyExpression(cellItem, "horizontalAlignment", "Text.AlignHCenter"_L1); - delegateHeight += 20; - } - delegateHeight += spacing; - ::setBindingPropertyExpression(cellItem, - "anchors.horizontalCenter", - "parent.horizontalCenter"_L1); - colItem.defaultNodeAbstractProperty().reparentHere(cellItem); - } - - rectItem.defaultNodeAbstractProperty().reparentHere(colItem); - ::setVariantPropertyValue(rectItem, "width", delegateWidth); - ::setVariantPropertyValue(rectItem, "height", delegateHeight); - delegateSize.setWidth(delegateWidth); - delegateSize.setHeight(delegateHeight); - - return rectItem; -} - -CollectionTask::CollectionTask(CollectionView *view, CollectionListModel *listModel) - : m_collectionView(view) - , m_listModel(listModel) -{} - -DropListViewTask::DropListViewTask(CollectionView *view, - CollectionListModel *listModel, - const ModelNode &node) - : CollectionTask(view, listModel) - , m_node(node) -{} - -void DropListViewTask::process() -{ - AbstractView *view = m_node.view(); - if (!m_node || !m_collectionView || !m_listModel || !view) - return; - - const QString newCollectionName = m_listModel->getUniqueCollectionName("ListModel"); - m_listModel->addCollection(newCollectionName, CollectionEditorUtils::defaultColorCollection()); - m_collectionView->openCollection(newCollectionName); - m_collectionView->assignCollectionToNode(newCollectionName, m_node); -} - -AddCollectionTask::AddCollectionTask(CollectionView *view, - CollectionListModel *listModel, - const QJsonObject &localJsonObject, - const QString &collectionName) - : CollectionTask(view, listModel) - , m_localJsonObject(localJsonObject) - , m_name(collectionName) -{} - -void AddCollectionTask::process() -{ - if (!m_listModel) - return; - - const QString newCollectionName = m_listModel->collectionExists(m_name) - ? m_listModel->getUniqueCollectionName(m_name) - : m_name; - - m_listModel->addCollection(newCollectionName, m_localJsonObject); -} - -} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionview.h b/src/plugins/qmldesigner/components/collectioneditor/collectionview.h deleted file mode 100644 index 458ea6c60a5..00000000000 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionview.h +++ /dev/null @@ -1,128 +0,0 @@ -// Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include "datastoremodelnode.h" - -#include -#include - -#include - -#include - -namespace QmlJS { -class Document; -} - -namespace QmlDesigner { - -class CollectionDetails; -class CollectionListModel; -class CollectionTask; -class CollectionWidget; -class DataStoreModelNode; -class GeneratedComponentUtils; - -class CollectionView : public AbstractView -{ - Q_OBJECT - -public: - explicit CollectionView(ExternalDependenciesInterface &externalDependencies); - ~CollectionView(); - - bool hasWidget() const override; - WidgetInfo widgetInfo() override; - - void modelAttached(Model *model) override; - void modelAboutToBeDetached(Model *model) override; - - void selectedNodesChanged(const QList &selectedNodeList, - const QList &lastSelectedNodeList) override; - - void importsChanged(const Imports &addedImports, const Imports &removedImports) override; - - void customNotification(const AbstractView *view, - const QString &identifier, - const QList &nodeList, - const QList &data) override; - - void addResource(const QUrl &url, const QString &name); - - void addProjectImport(); - void assignCollectionToNode(const QString &collectionName, const ModelNode &node); - void assignCollectionToSelectedNode(const QString &collectionName); - void addNewCollection(const QString &collectionName, const QJsonObject &localCollection); - - void openCollection(const QString &collectionName); - - static void registerDeclarativeType(); - - void resetDataStoreNode(); - ModelNode dataStoreNode() const; - void ensureDataStoreExists(); - QString collectionNameFromDataStoreChildren(const PropertyName &childPropertyName) const; - -private: - friend class CollectionTask; - - NodeMetaInfo jsonCollectionMetaInfo() const; - void unloadDataStore(); - bool createDataStore(bool &justCreated) const; - void ensureStudioModelImport(); - void onItemLibraryNodeCreated(const ModelNode &node); - void addTask(QSharedPointer task); - ModelNode createListViewDelegate(const QString &collectionName); - ModelNode createGridViewDelegate(const QString &collectionName, QSize &delegateSize); - - std::unique_ptr m_dataStore; - Utils::UniqueObjectPtr m_widget; - QList> m_delayedTasks; - bool m_dataStoreTypeFound = false; - bool m_rewriterAmended = false; - int m_reloadCounter = 0; -}; - -class CollectionTask -{ -public: - CollectionTask(CollectionView *view, CollectionListModel *listModel); - CollectionTask() = delete; - virtual ~CollectionTask() = default; - - virtual void process() = 0; - -protected: - QPointer m_collectionView; - QPointer m_listModel; -}; - -class DropListViewTask : public CollectionTask -{ -public: - DropListViewTask(CollectionView *view, CollectionListModel *listModel, const ModelNode &node); - - void process() override; - -private: - ModelNode m_node; -}; - -class AddCollectionTask : public CollectionTask -{ -public: - AddCollectionTask(CollectionView *view, - CollectionListModel *listModel, - const QJsonObject &localJsonObject, - const QString &collectionName); - - void process() override; - -private: - QJsonObject m_localJsonObject; - QString m_name; -}; - -} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp deleted file mode 100644 index f6bc1829ee8..00000000000 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp +++ /dev/null @@ -1,336 +0,0 @@ -// Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "collectionwidget.h" - -#include "collectiondetails.h" -#include "collectiondetailsmodel.h" -#include "collectiondetailssortfiltermodel.h" -#include "collectioneditorutils.h" -#include "collectionlistmodel.h" -#include "collectionview.h" -#include "designmodewidget.h" -#include "qmldesignerconstants.h" -#include "qmldesignerplugin.h" -#include "theme.h" - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace { - -QString collectionViewResourcesPath() -{ -#ifdef SHARE_QML_PATH - if (qEnvironmentVariableIsSet("LOAD_QML_FROM_SOURCE")) - return QLatin1String(SHARE_QML_PATH) + "/collectionEditorQmlSource"; -#endif - return Core::ICore::resourcePath("qmldesigner/collectionEditorQmlSource").toString(); -} - -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 { - -CollectionWidget::CollectionWidget(CollectionView *view) - : m_view(view) - , m_listModel(new CollectionListModel) - , m_collectionDetailsModel(new CollectionDetailsModel) - , m_collectionDetailsSortFilterModel(std::make_unique()) - , m_quickWidget(Utils::makeUniqueObjectPtr(this)) -{ - setWindowTitle(tr("Model Editor", "Title of model editor widget")); - - Core::Context context(Constants::C_QMLCOLLECTIONEDITOR); - m_iContext = new Core::IContext(this); - m_iContext->setContext(context); - m_iContext->setWidget(this); - Core::ICore::addContextObject(m_iContext); - - connect(m_listModel, &CollectionListModel::warning, this, &CollectionWidget::warn); - - 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"); - m_quickWidget->setClearColor(Theme::getColor(Theme::Color::DSpanelBackground)); - - Theme::setupTheme(m_quickWidget->engine()); - m_quickWidget->quickWidget()->installEventFilter(this); - - auto layout = new QVBoxLayout(this); - layout->setContentsMargins({}); - layout->setSpacing(0); - layout->addWidget(m_quickWidget.get()); - - qmlRegisterAnonymousType("CollectionEditorBackend", 1); - auto map = m_quickWidget->registerPropertyMap("CollectionEditorBackend"); - map->setProperties({ - {"rootView", QVariant::fromValue(this)}, - {"model", QVariant::fromValue(m_listModel.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); - - reloadQmlSource(); - - QmlDesignerPlugin::trackWidgetFocusTime(this, Constants::EVENT_MODELEDITOR_TIME); -} - -CollectionWidget::~CollectionWidget() = default; - -void CollectionWidget::contextHelp(const Core::IContext::HelpCallback &callback) const -{ - if (m_view) - QmlDesignerPlugin::contextHelp(callback, m_view->contextHelpId()); - else - callback({}); -} - -QPointer CollectionWidget::listModel() const -{ - return m_listModel; -} - -QPointer CollectionWidget::collectionDetailsModel() const -{ - return m_collectionDetailsModel; -} - -void CollectionWidget::reloadQmlSource() -{ - const QString collectionViewQmlPath = collectionViewResourcesPath() + "/CollectionView.qml"; - - 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 -{ - return {300, 300}; -} - -bool CollectionWidget::loadJsonFile(const QUrl &url, const QString &collectionName) -{ - if (!isJsonFile(url)) - return false; - - m_view->addResource(url, getPreferredCollectionName(url, collectionName)); - - return true; -} - -bool CollectionWidget::loadCsvFile(const QUrl &url, const QString &collectionName) -{ - m_view->addResource(url, getPreferredCollectionName(url, collectionName)); - - return true; -} - -bool CollectionWidget::isJsonFile(const QUrl &url) const -{ - Utils::FilePath filePath = Utils::FilePath::fromUserInput(url.isLocalFile() ? url.toLocalFile() - : url.toString()); - Utils::FileReader file; - if (!file.fetch(filePath)) - return false; - - QJsonParseError error; - QJsonDocument::fromJson(file.data(), &error); - if (error.error) - return false; - - return true; -} - -bool CollectionWidget::isCsvFile(const QUrl &url) const -{ - QString filePath = url.isLocalFile() ? url.toLocalFile() : url.toString(); - QFileInfo fileInfo(filePath); - return fileInfo.exists() && !fileInfo.suffix().compare("csv", Qt::CaseInsensitive); -} - -bool CollectionWidget::isValidUrlToImport(const QUrl &url) const -{ - using Utils::FilePath; - FilePath fileInfo = FilePath::fromUserInput(url.isLocalFile() ? url.toLocalFile() - : url.toString()); - if (fileInfo.suffix() == "json") - return isJsonFile(url); - - if (fileInfo.suffix() == "csv") - return isCsvFile(url); - - return false; -} - -bool CollectionWidget::importFile(const QString &collectionName, - const QUrl &url, - const bool &firstRowIsHeader) -{ - using Utils::FilePath; - - FilePath fileInfo = FilePath::fromUserInput(url.isLocalFile() ? url.toLocalFile() - : url.toString()); - QByteArray fileContent; - - auto loadUrlContent = [&]() -> bool { - Utils::FileReader file; - if (file.fetch(fileInfo)) { - fileContent = file.data(); - return true; - } - - warn(tr("Import from file"), tr("Cannot import from file \"%1\"").arg(fileInfo.fileName())); - return false; - }; - - if (fileInfo.suffix() == "json") { - if (!loadUrlContent()) - return false; - - QJsonParseError parseError; - const QList loadedCollections = CollectionDetails::fromImportedJson( - fileContent, &parseError); - if (parseError.error != QJsonParseError::NoError) { - warn(tr("Json file Import error"), - tr("Cannot parse json content\n%1").arg(parseError.errorString())); - return false; - } - if (loadedCollections.size() > 1) { - for (const CollectionDetails &loadedCollection : loadedCollections) { - m_view->addNewCollection(loadedCollection.reference().name, - loadedCollection.toLocalJson()); - } - return true; - } else if (loadedCollections.size() == 1) { - m_view->addNewCollection(collectionName, loadedCollections.first().toLocalJson()); - return true; - } else { - warn(tr("Can not add a model to the JSON file"), - tr("The imported model is empty or is not supported.")); - } - } else if (fileInfo.suffix() == "csv") { - CollectionDetails loadedCollection; - if (!loadUrlContent()) - return false; - loadedCollection = CollectionDetails::fromImportedCsv(fileContent, firstRowIsHeader); - if (loadedCollection.columns()) { - m_view->addNewCollection(collectionName, loadedCollection.toLocalJson()); - return true; - } else { - warn(tr("Can not add a model to the JSON file"), - tr("The imported model is empty or is not supported.")); - } - } - - return false; -} - -void CollectionWidget::addProjectImport() -{ - m_view->addProjectImport(); -} - -void CollectionWidget::addCollectionToDataStore(const QString &collectionName) -{ - m_view->addNewCollection(collectionName, CollectionEditorUtils::defaultCollection()); -} - -void CollectionWidget::assignCollectionToSelectedNode(const QString collectionName) -{ - m_view->assignCollectionToSelectedNode(collectionName); -} - -void CollectionWidget::openCollection(const QString &collectionName) -{ - m_listModel->selectCollectionName(collectionName); - QmlDesignerPlugin::instance()->mainWidget()->showDockWidget("CollectionEditor", true); -} - -ModelNode CollectionWidget::dataStoreNode() const -{ - return m_view->dataStoreNode(); -} - -void CollectionWidget::warn(const QString &title, const QString &body) -{ - QMetaObject::invokeMethod(m_quickWidget->rootObject(), - "showWarning", - Q_ARG(QVariant, title), - Q_ARG(QVariant, body)); -} - -void CollectionWidget::setTargetNodeSelected(bool selected) -{ - if (m_targetNodeSelected == selected) - return; - - m_targetNodeSelected = selected; - emit targetNodeSelectedChanged(m_targetNodeSelected); -} - -void CollectionWidget::setProjectImportExists(bool exists) -{ - if (m_projectImportExists == exists) - return; - - m_projectImportExists = exists; - emit projectImportExistsChanged(m_projectImportExists); -} - -void CollectionWidget::setDataStoreExists(bool exists) -{ - if (m_dataStoreExists == exists) - return; - - m_dataStoreExists = exists; - emit dataStoreExistsChanged(m_dataStoreExists); -} - -void CollectionWidget::deleteSelectedCollection() -{ - QMetaObject::invokeMethod(m_quickWidget->quickWidget()->rootObject(), "deleteSelectedCollection"); -} - -} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.h b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.h deleted file mode 100644 index f62d4664720..00000000000 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.h +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include - -#include - -#include - -class StudioQuickWidget; - -namespace QmlDesigner { - -class CollectionDetailsModel; -class CollectionDetailsSortFilterModel; -class CollectionListModel; -class CollectionView; -class ModelNode; - -class CollectionWidget : public QFrame -{ - Q_OBJECT - - Q_PROPERTY(bool targetNodeSelected MEMBER m_targetNodeSelected NOTIFY targetNodeSelectedChanged) - Q_PROPERTY(bool projectImportExists MEMBER m_projectImportExists NOTIFY projectImportExistsChanged) - Q_PROPERTY(bool dataStoreExists MEMBER m_dataStoreExists NOTIFY dataStoreExistsChanged) - -public: - CollectionWidget(CollectionView *view); - ~CollectionWidget(); - void contextHelp(const Core::IContext::HelpCallback &callback) const; - - QPointer listModel() const; - QPointer collectionDetailsModel() const; - - void reloadQmlSource(); - - QSize minimumSizeHint() const override; - - Q_INVOKABLE bool loadJsonFile(const QUrl &url, const QString &collectionName = {}); - Q_INVOKABLE bool loadCsvFile(const QUrl &url, const QString &collectionName = {}); - Q_INVOKABLE bool isJsonFile(const QUrl &url) const; - Q_INVOKABLE bool isCsvFile(const QUrl &url) const; - Q_INVOKABLE bool isValidUrlToImport(const QUrl &url) const; - - Q_INVOKABLE bool importFile(const QString &collectionName, - const QUrl &url, - const bool &firstRowIsHeader = true); - - Q_INVOKABLE void addProjectImport(); - Q_INVOKABLE void addCollectionToDataStore(const QString &collectionName); - Q_INVOKABLE void assignCollectionToSelectedNode(const QString collectionName); - Q_INVOKABLE void openCollection(const QString &collectionName); - Q_INVOKABLE ModelNode dataStoreNode() const; - - void warn(const QString &title, const QString &body); - void setTargetNodeSelected(bool selected); - void setProjectImportExists(bool exists); - void setDataStoreExists(bool exists); - - void deleteSelectedCollection(); - -signals: - void targetNodeSelectedChanged(bool); - void projectImportExistsChanged(bool); - void dataStoreExistsChanged(bool); - -private: - QString generateUniqueCollectionName(const ModelNode &node, const QString &name); - - QPointer m_view; - QPointer m_listModel; - QPointer m_collectionDetailsModel; - QPointer m_iContext; - std::unique_ptr m_collectionDetailsSortFilterModel; - Utils::UniqueObjectPtr m_quickWidget; - bool m_targetNodeSelected = false; - bool m_projectImportExists = false; - bool m_dataStoreExists = false; -}; - -} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.cpp b/src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.cpp deleted file mode 100644 index 5ffad6683c3..00000000000 --- a/src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.cpp +++ /dev/null @@ -1,517 +0,0 @@ -// Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "datastoremodelnode.h" - -#include "abstractview.h" -#include "collectioneditorconstants.h" -#include "collectioneditorutils.h" -#include "model/qmltextgenerator.h" -#include "plaintexteditmodifier.h" -#include "qmldesignerbase/qmldesignerbaseplugin.h" -#include "qmldesignerexternaldependencies.h" -#include "rewriterview.h" - -#include -#include -#include -#include - -#include -#include - -#include -#include - -#include -#include - -#include -#include -#include - -#include - -namespace { - -inline constexpr char CHILDLISTMODEL_TYPENAME[] = "ChildListModel"; - -QmlDesigner::PropertyNameList createNameList(const QmlDesigner::ModelNode &node) -{ - using QmlDesigner::AbstractProperty; - using QmlDesigner::PropertyName; - using QmlDesigner::PropertyNameList; - static PropertyNameList defaultsNodeProps = { - "id", - QmlDesigner::CollectionEditorConstants::SOURCEFILE_PROPERTY, - QmlDesigner::CollectionEditorConstants::JSONCHILDMODELNAME_PROPERTY, - "backend"}; - PropertyNameList dynamicPropertyNames = Utils::transform( - node.dynamicProperties(), - [](const AbstractProperty &property) -> PropertyName { return property.name(); }); - - Utils::sort(dynamicPropertyNames); - - return defaultsNodeProps + dynamicPropertyNames; -} - -bool isValidCollectionPropertyName(const QString &collectionId) -{ - static const QmlDesigner::PropertyNameList reservedKeywords = { - QmlDesigner::CollectionEditorConstants::SOURCEFILE_PROPERTY, - QmlDesigner::CollectionEditorConstants::JSONBACKEND_TYPENAME, - "backend", - "models", - }; - - return QmlDesigner::ModelNode::isValidId(collectionId) - && !reservedKeywords.contains(collectionId.toLatin1()); -} - -QMap getModelIdMap(const QmlDesigner::ModelNode &rootNode) -{ - using namespace QmlDesigner; - QMap modelNameForId; - - const QList propertyNames = rootNode.dynamicProperties(); - - for (const AbstractProperty &property : std::as_const(propertyNames)) { - if (!property.isNodeProperty()) - continue; - - NodeProperty nodeProperty = property.toNodeProperty(); - if (!nodeProperty.hasDynamicTypeName(CHILDLISTMODEL_TYPENAME)) - continue; - - ModelNode childNode = nodeProperty.modelNode(); - if (childNode.hasProperty(CollectionEditorConstants::JSONCHILDMODELNAME_PROPERTY)) { - QString modelName = childNode - .property(CollectionEditorConstants::JSONCHILDMODELNAME_PROPERTY) - .toVariantProperty() - .value() - .toString(); - - if (!modelName.isEmpty()) - modelNameForId.insert(modelName, property.name()); - } - } - return modelNameForId; -} - -void setQmlContextToModel(QmlDesigner::Model *model, const QString &qmlContext) -{ - using namespace QmlDesigner; - Q_ASSERT(model); - - std::unique_ptr textEdit = std::make_unique(); - std::unique_ptr modifier = std::make_unique( - textEdit.get()); - textEdit->hide(); - textEdit->setPlainText(qmlContext); - QmlDesigner::ExternalDependencies externalDependencies{QmlDesignerBasePlugin::settings()}; - std::unique_ptr rewriter = std::make_unique( - externalDependencies, QmlDesigner::RewriterView::Validate); - - rewriter->setTextModifier(modifier.get()); - rewriter->setCheckSemanticErrors(false); - - model->attachView(rewriter.get()); - model->detachView(rewriter.get()); -} - -} // namespace - -namespace QmlDesigner { - -DataStoreModelNode::DataStoreModelNode() = default; - -void DataStoreModelNode::reloadModel(const Utils::FilePath &projectModulePath) -{ - using Utils::FilePath; - if (!projectModulePath.exists()) { - reset(); - return; - } - bool forceUpdate = false; - - const FilePath dataStoreQmlPath = projectModulePath.resolvePath( - CollectionEditorConstants::DEFAULT_DATASTORE_QML_FILENAME.toString()); - const FilePath dataStoreJsonPath = projectModulePath.resolvePath( - CollectionEditorConstants::DEFAULT_MODELS_JSON_FILENAME.toString()); - QUrl dataStoreQmlUrl = dataStoreQmlPath.toUrl(); - - if (dataStoreQmlPath.exists() && dataStoreJsonPath.exists()) { - if (!m_model.get() || m_model->fileUrl() != dataStoreQmlUrl) { -#ifdef QDS_USE_PROJECTSTORAGE - m_model = model()->createModel("JsonListModel"); - forceUpdate = true; - Import import = Import::createLibraryImport("QtQuick.Studio.Utils"); - m_model->changeImports({import}, {}); -#else - m_model = Model::create(CollectionEditorConstants::JSONCOLLECTIONMODEL_TYPENAME, 1, 1); - forceUpdate = true; - Import import = Import::createLibraryImport( - CollectionEditorConstants::COLLECTIONMODEL_IMPORT); - try { - if (!m_model->hasImport(import, true, true)) - m_model->changeImports({import}, {}); - } catch (const Exception &) { - QTC_ASSERT(false, return); - } -#endif - } - } else { - reset(); - } - - if (!m_model.get()) - return; - - if (forceUpdate) { - m_model->setFileUrl(dataStoreQmlUrl); - m_dataRelativePath = dataStoreJsonPath.relativePathFrom(dataStoreQmlPath).toFSPathString(); - preloadFile(); - update(); - } -} - -QStringList DataStoreModelNode::collectionNames() const -{ - return m_collectionPropertyNames.keys(); -} - -Model *DataStoreModelNode::model() const -{ - return m_model.get(); -} - -ModelNode DataStoreModelNode::modelNode() const -{ - if (!m_model.get()) - return {}; - return m_model->rootModelNode(); -} - -Utils::FilePath DataStoreModelNode::jsonFilePath() const -{ - QUrl modelUrl = m_model->fileUrl(); - return Utils::FilePath::fromUserInput(modelUrl.isLocalFile() ? modelUrl.toLocalFile() - : modelUrl.toString()) - .parentDir() - .resolvePath(CollectionEditorConstants::DEFAULT_MODELS_JSON_FILENAME.toString()); -} - -QString DataStoreModelNode::getModelQmlText() -{ - ModelNode node = modelNode(); - QTC_ASSERT(node, return {}); - - Internal::QmlTextGenerator textGen(createNameList(node), - QmlJSTools::QmlJSToolsSettings::globalCodeStyle()->tabSettings()); - - QString genText = textGen(node); - return genText; -} - -void DataStoreModelNode::reset() -{ - if (m_model) - m_model.reset(); - - m_dataRelativePath.clear(); - setCollectionNames({}); -} - -void DataStoreModelNode::preloadFile() -{ - using Utils::FilePath; - using Utils::FileReader; - - if (!m_model) - return; - - const FilePath dataStoreQmlPath = dataStoreQmlFilePath(); - FileReader dataStoreQmlFile; - QString sourceQmlContext; - - if (dataStoreQmlFile.fetch(dataStoreQmlPath)) - sourceQmlContext = QString::fromLatin1(dataStoreQmlFile.data()); - - setQmlContextToModel(m_model.get(), sourceQmlContext); - m_collectionPropertyNames = getModelIdMap(m_model->rootModelNode()); -} - -void DataStoreModelNode::updateDataStoreProperties() -{ - QTC_ASSERT(model(), return); - - ModelNode rootNode = modelNode(); - QTC_ASSERT(rootNode.isValid(), return); - - QSet collectionNamesToBeAdded; - const QStringList allCollectionNames = m_collectionPropertyNames.keys(); - for (const QString &collectionName : allCollectionNames) - collectionNamesToBeAdded << collectionName; - - const QList formerPropertyNames = rootNode.dynamicProperties(); - - // Remove invalid collection names from the properties - for (const AbstractProperty &property : formerPropertyNames) { - if (!property.isNodeProperty()) - continue; - - NodeProperty nodeProprty = property.toNodeProperty(); - if (!nodeProprty.hasDynamicTypeName(CHILDLISTMODEL_TYPENAME)) - continue; - - ModelNode childNode = nodeProprty.modelNode(); - if (childNode.hasProperty(CollectionEditorConstants::JSONCHILDMODELNAME_PROPERTY)) { - QString modelName = childNode - .property(CollectionEditorConstants::JSONCHILDMODELNAME_PROPERTY) - .toVariantProperty() - .value() - .toString(); - if (collectionNamesToBeAdded.contains(modelName)) { - m_collectionPropertyNames.insert(modelName, property.name()); - collectionNamesToBeAdded.remove(modelName); - } else { - rootNode.removeProperty(property.name()); - } - } else { - rootNode.removeProperty(property.name()); - } - } - - rootNode.setIdWithoutRefactoring("models"); - - QStringList collectionNamesLeft = collectionNamesToBeAdded.values(); - Utils::sort(collectionNamesLeft); - for (const QString &collectionName : std::as_const(collectionNamesLeft)) - addCollectionNameToTheModel(collectionName, getUniquePropertyName(collectionName)); - - // Backend Property - ModelNode backendNode = model()->createModelNode(CollectionEditorConstants::JSONBACKEND_TYPENAME); - NodeProperty backendProperty = rootNode.nodeProperty("backend"); - backendProperty.setDynamicTypeNameAndsetModelNode(CollectionEditorConstants::JSONBACKEND_TYPENAME, - backendNode); - // Source Property - VariantProperty sourceProp = rootNode.variantProperty( - CollectionEditorConstants::SOURCEFILE_PROPERTY); - sourceProp.setValue(m_dataRelativePath); -} - -void DataStoreModelNode::updateSingletonFile() -{ - using Utils::FilePath; - using Utils::FileSaver; - QTC_ASSERT(m_model.get(), return); - - const QString pragmaSingleTone = "pragma Singleton\n"; - QString imports; - - for (const Import &import : m_model->imports()) - imports += QStringLiteral("import %1\n").arg(import.toString(true)); - - QString content = pragmaSingleTone + imports + getModelQmlText(); - Core::DocumentManager::expectFileChange(dataStoreQmlFilePath()); - FileSaver file(dataStoreQmlFilePath()); - file.write(content.toLatin1()); - file.finalize(); -} - -void DataStoreModelNode::update() -{ - if (!m_model.get()) - return; - - updateDataStoreProperties(); - updateSingletonFile(); -} - -void DataStoreModelNode::addCollectionNameToTheModel(const QString &collectionName, - const PropertyName &dataStorePropertyName) -{ - ModelNode rootNode = modelNode(); - QTC_ASSERT(rootNode.isValid(), return); - - if (dataStorePropertyName.isEmpty()) { - qWarning() << __FUNCTION__ << __LINE__ - << QString("The property name cannot be generated from \"%1\"").arg(collectionName); - return; - } - - ModelNode collectionNode = model()->createModelNode(CHILDLISTMODEL_TYPENAME); - VariantProperty modelNameProperty = collectionNode.variantProperty( - CollectionEditorConstants::JSONCHILDMODELNAME_PROPERTY); - modelNameProperty.setValue(collectionName); - - NodeProperty nodeProp = rootNode.nodeProperty(dataStorePropertyName); - nodeProp.setDynamicTypeNameAndsetModelNode(CHILDLISTMODEL_TYPENAME, collectionNode); - - m_collectionPropertyNames.insert(collectionName, dataStorePropertyName); -} - -Utils::FilePath DataStoreModelNode::dataStoreQmlFilePath() const -{ - QUrl modelUrl = m_model->fileUrl(); - return Utils::FilePath::fromUserInput(modelUrl.isLocalFile() ? modelUrl.toLocalFile() - : modelUrl.toString()); -} - -PropertyName DataStoreModelNode::getUniquePropertyName(const QString &collectionName) -{ - ModelNode dataStoreNode = modelNode(); - QTC_ASSERT(!collectionName.isEmpty() && dataStoreNode.isValid(), return {}); - - QString newProperty; - - // convert to camel case - QStringList nameWords = collectionName.split(' '); - nameWords[0] = nameWords[0].at(0).toLower() + nameWords[0].mid(1); - for (int i = 1; i < nameWords.size(); ++i) - nameWords[i] = nameWords[i].at(0).toUpper() + nameWords[i].mid(1); - newProperty = nameWords.join(""); - - // if id starts with a number prepend an underscore - if (newProperty.at(0).isDigit()) - newProperty.prepend('_'); - - // If the new id is not valid (e.g. qml keyword match), prepend an underscore - if (!isValidCollectionPropertyName(newProperty)) - newProperty.prepend('_'); - - static const QRegularExpression rgx("\\d+$"); // matches a number at the end of a string - while (dataStoreNode.hasProperty(newProperty.toLatin1())) { // id exists - QRegularExpressionMatch match = rgx.match(newProperty); - if (match.hasMatch()) { // ends with a number, increment it - QString numStr = match.captured(); - int num = numStr.toInt() + 1; - newProperty = newProperty.mid(0, match.capturedStart()) + QString::number(num); - } else { - newProperty.append('1'); - } - } - - return newProperty.toLatin1(); -} - -void DataStoreModelNode::setCollectionNames(const QStringList &newCollectionNames) -{ - m_collectionPropertyNames.clear(); - for (const QString &collectionName : newCollectionNames) - m_collectionPropertyNames.insert(collectionName, {}); - update(); -} - -void DataStoreModelNode::addCollection(const QString &collectionName) -{ - if (!m_collectionPropertyNames.contains(collectionName)) { - m_collectionPropertyNames.insert(collectionName, {}); - update(); - } -} - -void DataStoreModelNode::renameCollection(const QString &oldName, const QString &newName) -{ - ModelNode dataStoreNode = modelNode(); - QTC_ASSERT(dataStoreNode.isValid(), return); - - if (m_collectionPropertyNames.contains(oldName)) { - const PropertyName oldPropertyName = m_collectionPropertyNames.value(oldName); - if (!oldPropertyName.isEmpty() && dataStoreNode.hasProperty(oldPropertyName)) { - NodeProperty collectionNode = dataStoreNode.property(oldPropertyName).toNodeProperty(); - if (collectionNode.isValid()) { - VariantProperty modelNameProperty = collectionNode.modelNode().variantProperty( - CollectionEditorConstants::JSONCHILDMODELNAME_PROPERTY); - modelNameProperty.setValue(newName); - m_collectionPropertyNames.remove(oldName); - m_collectionPropertyNames.insert(newName, collectionNode.name()); - update(); - return; - } - qWarning() << __FUNCTION__ << __LINE__ - << "There is no valid node for the old collection name"; - return; - } - qWarning() << __FUNCTION__ << __LINE__ << QString("Invalid old property name") - << oldPropertyName; - return; - } - qWarning() << __FUNCTION__ << __LINE__ - << QString("There is no old collection name registered with this name \"%1\"").arg(oldName); -} - -void DataStoreModelNode::removeCollections(const QStringList &collectionNames) -{ - bool updateRequired = false; - for (const QString &collectionName : collectionNames) { - if (m_collectionPropertyNames.contains(collectionName)) { - m_collectionPropertyNames.remove(collectionName); - updateRequired = true; - } - } - - if (updateRequired) - update(); -} - -void DataStoreModelNode::assignCollectionToNode(AbstractView *view, - const ModelNode &targetNode, - const QString &collectionName, - CollectionColumnFinder collectionHasColumn, - FirstColumnProvider firstColumnProvider) -{ - QTC_ASSERT(targetNode.isValid(), return); - - if (!CollectionEditorUtils::canAcceptCollectionAsModel(targetNode)) - return; - - if (!m_collectionPropertyNames.contains(collectionName)) { - qWarning() << __FUNCTION__ << __LINE__ << "Collection doesn't exist in the DataStore" - << collectionName; - return; - } - - PropertyName propertyName = m_collectionPropertyNames.value(collectionName); - - const ModelNode dataStore = modelNode(); - VariantProperty sourceProperty = dataStore.variantProperty(propertyName); - if (!sourceProperty.exists()) { - qWarning() << __FUNCTION__ << __LINE__ - << "The source property doesn't exist in the DataStore."; - return; - } - - view->executeInTransaction("assignCollectionToNode", [&]() { - QString identifier = QString("DataStore.%1").arg(QString::fromLatin1(sourceProperty.name())); - - // Remove the old model node property if exists - NodeProperty modelNodeProperty = targetNode.nodeProperty("model"); - if (modelNodeProperty.modelNode()) - modelNodeProperty.modelNode().destroy(); - - // Assign the collection to the node - BindingProperty modelProperty = targetNode.bindingProperty("model"); - modelProperty.setExpression(identifier); - - if (CollectionEditorUtils::hasTextRoleProperty(targetNode)) { - VariantProperty textRoleProperty = targetNode.variantProperty("textRole"); - const QVariant currentTextRoleValue = textRoleProperty.value(); - - if (currentTextRoleValue.isValid() && !currentTextRoleValue.isNull()) { - if (currentTextRoleValue.type() == QVariant::String) { - const QString currentTextRole = currentTextRoleValue.toString(); - if (collectionHasColumn(collectionName, currentTextRole)) - return; - } else { - return; - } - } - - QString textRoleValue = firstColumnProvider(collectionName); - textRoleProperty.setValue(textRoleValue); - } - }); -} - -} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.h b/src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.h deleted file mode 100644 index 9d438ef5f56..00000000000 --- a/src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.h +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include - -#include - -namespace Utils { -class FilePath; -} - -namespace QmlDesigner { - -class Model; - -class DataStoreModelNode -{ -public: - using CollectionColumnFinder = std::function; - using FirstColumnProvider = std::function; - - DataStoreModelNode(); - - void reloadModel(const Utils::FilePath &projectModulePath); - QStringList collectionNames() const; - - Model *model() const; - ModelNode modelNode() const; - Utils::FilePath jsonFilePath() const; - - void setCollectionNames(const QStringList &newCollectionNames); - void addCollection(const QString &collectionName); - void renameCollection(const QString &oldName, const QString &newName); - void removeCollections(const QStringList &collectionNames); - - void assignCollectionToNode(AbstractView *view, - const ModelNode &targetNode, - const QString &collectionName, - CollectionColumnFinder collectionHasColumn, - FirstColumnProvider firstColumnProvider); - -private: - QString getModelQmlText(); - - void reset(); - void preloadFile(); - void updateDataStoreProperties(); - void updateSingletonFile(); - void update(); - void addCollectionNameToTheModel(const QString &collectionName, - const PropertyName &dataStorePropertyName); - Utils::FilePath dataStoreQmlFilePath() const; - - PropertyName getUniquePropertyName(const QString &collectionName); - - ModelPointer m_model; - QMap m_collectionPropertyNames; - QString m_dataRelativePath; -}; - -} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h b/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h index d992a6a5bf0..da7c5bf72eb 100644 --- a/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h +++ b/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h @@ -69,7 +69,6 @@ const char mergeTemplateCommandId[] = "MergeTemplate"; const char goToImplementationCommandId[] = "GoToImplementation"; const char makeComponentCommandId[] = "MakeComponent"; const char editMaterialCommandId[] = "EditMaterial"; -const char editCollectionCommandId[] = "EditCollection"; const char addItemToStackedContainerCommandId[] = "AddItemToStackedContainer"; const char addTabBarToStackedContainerCommandId[] = "AddTabBarToStackedContainer"; const char increaseIndexOfStackedContainerCommandId[] = "IncreaseIndexOfStackedContainer"; @@ -128,7 +127,6 @@ const char mergeTemplateDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMen const char goToImplementationDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Go to Implementation"); const char makeComponentDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Create Component"); const char editMaterialDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Edit Material"); -const char editCollectionDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Edit Model"); const char editAnnotationsDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Edit Annotations"); const char addMouseAreaFillDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Add Mouse Area"); const char editIn3dViewDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Edit in 3D View"); @@ -214,7 +212,6 @@ enum PrioritiesEnum : int { ArrangeCategory, EditCategory, EditListModel, - EditCollection, /******** Section *****************************/ PositionSection = 2000, SnappingCategory, diff --git a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp index fd2321f82a3..bbe64935f61 100644 --- a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp +++ b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp @@ -113,7 +113,6 @@ void DesignerActionManager::polishActions() const Core::Context qmlDesignerNavigatorContext(Constants::C_QMLNAVIGATOR); Core::Context qmlDesignerMaterialBrowserContext(Constants::C_QMLMATERIALBROWSER); Core::Context qmlDesignerAssetsLibraryContext(Constants::C_QMLASSETSLIBRARY); - Core::Context qmlDesignerCollectionEditorContext(Constants::C_QMLCOLLECTIONEDITOR); Core::Context qmlDesignerUIContext; qmlDesignerUIContext.add(qmlDesignerFormEditorContext); @@ -121,7 +120,6 @@ void DesignerActionManager::polishActions() const qmlDesignerUIContext.add(qmlDesignerNavigatorContext); qmlDesignerUIContext.add(qmlDesignerMaterialBrowserContext); qmlDesignerUIContext.add(qmlDesignerAssetsLibraryContext); - qmlDesignerUIContext.add(qmlDesignerCollectionEditorContext); for (auto *action : actions) { if (!action->menuId().isEmpty()) { @@ -2011,16 +2009,6 @@ void DesignerActionManager::createDefaultDesignerActions() addDesignerAction(new EditListModelAction); - addDesignerAction(new ModelNodeContextMenuAction(editCollectionCommandId, - editCollectionDisplayName, - contextIcon(DesignerIcons::EditIcon), - rootCategory, - QKeySequence("Alt+e"), - ComponentCoreConstants::Priorities::EditCollection, - &editCollection, - &hasCollectionAsModel, - &hasCollectionAsModel)); - addDesignerAction(new ModelNodeContextMenuAction(openSignalDialogCommandId, openSignalDialogDisplayName, {}, diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.h b/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.h index 1a1284f35ee..6734bac568b 100644 --- a/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.h +++ b/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.h @@ -82,18 +82,6 @@ inline bool hasEditableMaterial(const SelectionContext &selectionState) return prop.exists() && (!prop.expression().isEmpty() || !prop.resolveToModelNodeList().empty()); } -inline bool hasCollectionAsModel(const SelectionContext &selectionState) -{ - if (!selectionState.isInBaseState() || !selectionState.singleNodeIsSelected()) - return false; - - const ModelNode singleSelectedNode = selectionState.currentSingleSelectedNode(); - - return singleSelectedNode.metaInfo().isListOrGridView() - && singleSelectedNode.property("model").toBindingProperty().expression().startsWith( - "DataStore."); -} - inline bool selectionEnabled(const SelectionContext &selectionState) { return selectionState.showSelectionTools(); diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp index 1cb58d3c6b3..7c32814b82c 100644 --- a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp +++ b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp @@ -846,30 +846,6 @@ void editMaterial(const SelectionContext &selectionContext) } } -// Open a collection in the collection editor -void editCollection(const SelectionContext &selectionContext) -{ - ModelNode modelNode = selectionContext.targetNode(); - - if (!modelNode) - modelNode = selectionContext.currentSingleSelectedNode(); - - if (!modelNode) - return; - - const QString dataStoreExpression = "DataStore."; - - BindingProperty prop = modelNode.bindingProperty("model"); - if (!prop.exists() || !prop.expression().startsWith(dataStoreExpression)) - return; - - AbstractView *view = selectionContext.view(); - const QString collectionId = prop.expression().mid(dataStoreExpression.size()); - - // to CollectionEditor... - view->emitCustomNotification("open_collection_by_id", {}, {collectionId}); -} - void addItemToStackedContainer(const SelectionContext &selectionContext) { AbstractView *view = selectionContext.view(); diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.h b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.h index a67cef49424..26562f429a0 100644 --- a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.h +++ b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.h @@ -92,7 +92,6 @@ void layoutGridLayout(const SelectionContext &selectionState); void goImplementation(const SelectionContext &selectionState); void addNewSignalHandler(const SelectionContext &selectionState); void editMaterial(const SelectionContext &selectionContext); -void editCollection(const SelectionContext &selectionContext); void addSignalHandlerOrGotoImplementation(const SelectionContext &selectionState, bool addAlwaysNewSlot); void removeLayout(const SelectionContext &selectionContext); void removePositioner(const SelectionContext &selectionContext); diff --git a/src/plugins/qmldesigner/components/componentcore/viewmanager.cpp b/src/plugins/qmldesigner/components/componentcore/viewmanager.cpp index 025ef5cb73a..a56735862f3 100644 --- a/src/plugins/qmldesigner/components/componentcore/viewmanager.cpp +++ b/src/plugins/qmldesigner/components/componentcore/viewmanager.cpp @@ -7,7 +7,6 @@ #include #include #include -#include #include #include #include @@ -42,14 +41,6 @@ namespace QmlDesigner { -static bool enableModelEditor() -{ - Utils::QtcSettings *settings = Core::ICore::settings(); - const Utils::Key enableModelManagerKey = "QML/Designer/UseExperimentalFeatures44"; - - return settings->value(enableModelManagerKey, false).toBool(); -} - static Q_LOGGING_CATEGORY(viewBenchmark, "qtc.viewmanager.attach", QtWarningMsg) class ViewManagerData @@ -64,7 +55,6 @@ public: : connectionManager, externalDependencies, true) - , collectionView{externalDependencies} , contentLibraryView{imageCache, externalDependencies} , componentView{externalDependencies} #ifndef QTC_USE_QML_DESIGNER_LITE @@ -90,7 +80,6 @@ public: Internal::DebugView debugView; DesignerActionManagerView designerActionManagerView; NodeInstanceView nodeInstanceView; - CollectionView collectionView; ContentLibraryView contentLibraryView; ComponentView componentView; #ifndef QTC_USE_QML_DESIGNER_LITE @@ -235,9 +224,6 @@ QList ViewManager::standardViews() const &d->designerActionManagerView}; #endif - if (enableModelEditor()) - list.append(&d->collectionView); - if (QmlDesignerPlugin::instance() ->settings() .value(DesignerSettingsKey::ENABLE_DEBUGVIEW) @@ -418,8 +404,6 @@ QList ViewManager::widgetInfos() const widgetInfoList.append(d->textureEditorView.widgetInfo()); #endif widgetInfoList.append(d->statesEditorView.widgetInfo()); - if (enableModelEditor()) - widgetInfoList.append(d->collectionView.widgetInfo()); if (checkEnterpriseLicense()) widgetInfoList.append(d->contentLibraryView.widgetInfo()); diff --git a/src/plugins/qmldesigner/designmodecontext.cpp b/src/plugins/qmldesigner/designmodecontext.cpp index 43fb0d9d756..796261935af 100644 --- a/src/plugins/qmldesigner/designmodecontext.cpp +++ b/src/plugins/qmldesigner/designmodecontext.cpp @@ -3,7 +3,6 @@ #include "designmodecontext.h" #include "assetslibrarywidget.h" -#include "collectionwidget.h" #include "designmodewidget.h" #include "edit3dwidget.h" #include "formeditorwidget.h" @@ -98,15 +97,4 @@ void TextEditorContext::contextHelp(const HelpCallback &callback) const qobject_cast(m_widget)->contextHelp(callback); } -CollectionEditorContext::CollectionEditorContext(QWidget *widget) - : IContext(widget) -{ - setWidget(widget); - setContext(Core::Context(Constants::C_QMLCOLLECTIONEDITOR, Constants::C_QT_QUICK_TOOLS_MENU)); -} - -void CollectionEditorContext::contextHelp(const HelpCallback &callback) const -{ - qobject_cast(m_widget)->contextHelp(callback); -} } // namespace QmlDesigner::Internal diff --git a/src/plugins/qmldesigner/designmodecontext.h b/src/plugins/qmldesigner/designmodecontext.h index 12f0113d977..1d146deb7d8 100644 --- a/src/plugins/qmldesigner/designmodecontext.h +++ b/src/plugins/qmldesigner/designmodecontext.h @@ -73,14 +73,5 @@ public: TextEditorContext(QWidget *widget); void contextHelp(const Core::IContext::HelpCallback &callback) const override; }; - -class CollectionEditorContext : public Core::IContext -{ - Q_OBJECT - -public: - CollectionEditorContext(QWidget *widget); - void contextHelp(const Core::IContext::HelpCallback &callback) const override; -}; } // namespace Internal } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/qmldesignerconstants.h b/src/plugins/qmldesigner/qmldesignerconstants.h index f2f78a1526f..537e65c5b41 100644 --- a/src/plugins/qmldesigner/qmldesignerconstants.h +++ b/src/plugins/qmldesigner/qmldesignerconstants.h @@ -19,7 +19,6 @@ inline constexpr char C_QMLNAVIGATOR[] = "QmlDesigner::Navigator"; inline constexpr char C_QMLTEXTEDITOR[] = "QmlDesigner::TextEditor"; inline constexpr char C_QMLMATERIALBROWSER[] = "QmlDesigner::MaterialBrowser"; inline constexpr char C_QMLASSETSLIBRARY[] = "QmlDesigner::AssetsLibrary"; -inline constexpr char C_QMLCOLLECTIONEDITOR[] = "QmlDesigner::CollectionEditor"; // Special context for preview menu, shared b/w designer and text editor inline constexpr char C_QT_QUICK_TOOLS_MENU[] = "QmlDesigner::ToolsMenu"; @@ -186,7 +185,6 @@ inline constexpr char OBJECT_NAME_EFFECT_COMPOSER[] = "QQuickWidgetEffectCompose inline constexpr char OBJECT_NAME_MATERIAL_BROWSER[] = "QQuickWidgetMaterialBrowser"; inline constexpr char OBJECT_NAME_MATERIAL_EDITOR[] = "QQuickWidgetMaterialEditor"; inline constexpr char OBJECT_NAME_PROPERTY_EDITOR[] = "QQuickWidgetPropertyEditor"; -inline constexpr char OBJECT_NAME_COLLECTION_EDITOR[] = "QQuickWidgetQDSCollectionEditor"; inline constexpr char OBJECT_NAME_STATES_EDITOR[] = "QQuickWidgetStatesEditor"; inline constexpr char OBJECT_NAME_TEXTURE_EDITOR[] = "QQuickWidgetTextureEditor"; inline constexpr char OBJECT_NAME_TOP_TOOLBAR[] = "QQuickWidgetTopToolbar"; diff --git a/src/plugins/qmldesigner/qmldesignerplugin.cpp b/src/plugins/qmldesigner/qmldesignerplugin.cpp index 601cf966813..d2518f41e40 100644 --- a/src/plugins/qmldesigner/qmldesignerplugin.cpp +++ b/src/plugins/qmldesigner/qmldesignerplugin.cpp @@ -4,7 +4,6 @@ #include "qmldesignerplugin.h" #include "qmldesignertr.h" -#include "collectioneditor/collectionview.h" #include "coreplugin/iwizardfactory.h" #include "designmodecontext.h" #include "designmodewidget.h" @@ -298,7 +297,6 @@ 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(); @@ -392,7 +390,6 @@ void QmlDesignerPlugin::integrateIntoQtCreator(QWidget *modeWidget) Core::Context qmlDesignerNavigatorContext(Constants::C_QMLNAVIGATOR); Core::Context qmlDesignerMaterialBrowserContext(Constants::C_QMLMATERIALBROWSER); Core::Context qmlDesignerAssetsLibraryContext(Constants::C_QMLASSETSLIBRARY); - Core::Context qmlDesignerCollectionEditorContext(Constants::C_QMLCOLLECTIONEDITOR); context->context().add(qmlDesignerMainContext); context->context().add(qmlDesignerFormEditorContext); @@ -400,7 +397,6 @@ void QmlDesignerPlugin::integrateIntoQtCreator(QWidget *modeWidget) context->context().add(qmlDesignerNavigatorContext); context->context().add(qmlDesignerMaterialBrowserContext); context->context().add(qmlDesignerAssetsLibraryContext); - context->context().add(qmlDesignerCollectionEditorContext); context->context().add(ProjectExplorer::Constants::QMLJS_LANGUAGE_ID); d->shortCutManager.registerActions(qmlDesignerMainContext, qmlDesignerFormEditorContext, diff --git a/src/plugins/qmldesigner/shortcutmanager.cpp b/src/plugins/qmldesigner/shortcutmanager.cpp index 961aa1c9520..79642c15f5e 100644 --- a/src/plugins/qmldesigner/shortcutmanager.cpp +++ b/src/plugins/qmldesigner/shortcutmanager.cpp @@ -231,12 +231,10 @@ void ShortCutManager::registerActions(const Core::Context &qmlDesignerMainContex connect(Core::ICore::instance(), &Core::ICore::contextChanged, this, [&](const Core::Context &context) { isMatBrowserActive = context.contains(Constants::C_QMLMATERIALBROWSER); isAssetsLibraryActive = context.contains(Constants::C_QMLASSETSLIBRARY); - isCollectionEditorActive = context.contains(Constants::C_QMLCOLLECTIONEDITOR); if (!context.contains(Constants::C_QMLFORMEDITOR) && !context.contains(Constants::C_QMLEDITOR3D) && !context.contains(Constants::C_QMLNAVIGATOR)) { - m_deleteAction.setEnabled(isMatBrowserActive || isAssetsLibraryActive - || isCollectionEditorActive); + m_deleteAction.setEnabled(isMatBrowserActive || isAssetsLibraryActive); m_cutAction.setEnabled(false); m_copyAction.setEnabled(false); m_pasteAction.setEnabled(false); @@ -293,8 +291,6 @@ void ShortCutManager::deleteSelected() actionManager.view()->emitCustomNotification("delete_selected_material"); else if (isAssetsLibraryActive) actionManager.view()->emitCustomNotification("delete_selected_assets"); - else if (isCollectionEditorActive) - actionManager.view()->emitCustomNotification("delete_selected_collection"); else if (currentDesignDocument()) currentDesignDocument()->deleteSelected(); } diff --git a/src/plugins/qmldesigner/shortcutmanager.h b/src/plugins/qmldesigner/shortcutmanager.h index 8714bb5fbcd..70b019217ca 100644 --- a/src/plugins/qmldesigner/shortcutmanager.h +++ b/src/plugins/qmldesigner/shortcutmanager.h @@ -64,7 +64,6 @@ private: bool isMatBrowserActive = false; bool isAssetsLibraryActive = false; - bool isCollectionEditorActive = false; }; } // namespace QmlDesigner From 0433b8c47923577cbac44005718d17b7d871cf41 Mon Sep 17 00:00:00 2001 From: Tim Jenssen Date: Wed, 22 May 2024 13:07:55 +0200 Subject: [PATCH 124/154] Qml2Puppet: fix stand alone puppet builds Change-Id: I24f5ca625429c636052d0c92c495a20a6942c92f Reviewed-by: Burak Hancerli Reviewed-by: Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Tim Jenssen --- src/tools/qml2puppet/CMakeLists.txt | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/tools/qml2puppet/CMakeLists.txt b/src/tools/qml2puppet/CMakeLists.txt index e77887adcca..26fd0b6dd21 100644 --- a/src/tools/qml2puppet/CMakeLists.txt +++ b/src/tools/qml2puppet/CMakeLists.txt @@ -26,10 +26,7 @@ if (NOT QT_CREATOR_API_DEFINED) COMPONENTS Concurrent Core Gui Network PrintSupport Qml Quick Sql Widgets Xml REQUIRED ) -endif() - -if (NOT TARGET QmlPuppetCommunication) - include(../../libs/qmlpuppetcommunication/QmlPuppetCommunication.cmake) + set(IS_STAND_ALONE_PUPPET_BUILD ON) endif() add_qtc_executable(qml2puppet @@ -38,7 +35,6 @@ add_qtc_executable(qml2puppet DEPENDS Qt::CorePrivate Qt::Widgets Qt::QmlPrivate Qt::QuickPrivate Qt::Network Qt::GuiPrivate - app_version QmlPuppetCommunication INCLUDES ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR} @@ -52,6 +48,15 @@ add_qtc_executable(qml2puppet OUTPUT_NAME qml2puppet-${IDE_VERSION} ) +if (IS_STAND_ALONE_PUPPET_BUILD) + include(../../libs/qmlpuppetcommunication/QmlPuppetCommunication.cmake) + configure_file(../../app/app_version.h.cmakein app/app_version.h ESCAPE_QUOTES) +else() + extend_qtc_executable(qml2puppet + DEPENDS app_version + ) +endif() + extend_qtc_executable(qml2puppet CONDITION Qt6_VERSION SOURCES From 93861d6d564371a522082483bcf579df395c199d Mon Sep 17 00:00:00 2001 From: Teea Poldsam Date: Thu, 23 May 2024 08:16:29 +0300 Subject: [PATCH 125/154] Doc: Reorganize the sidebar Fixes: QDS-12809 Change-Id: Ic28b94db2752992c4b08fe0c6ac15a159399a486 Reviewed-by: Mats Honkamaa --- .../config/style/qt5-sidebar.html | 112 ++++++++++++++++-- 1 file changed, 101 insertions(+), 11 deletions(-) diff --git a/doc/qtdesignstudio/config/style/qt5-sidebar.html b/doc/qtdesignstudio/config/style/qt5-sidebar.html index 0a7c93702a8..22b2895dc89 100644 --- a/doc/qtdesignstudio/config/style/qt5-sidebar.html +++ b/doc/qtdesignstudio/config/style/qt5-sidebar.html @@ -1,18 +1,108 @@ + + + + +
+
+

Implementing Applications

+
+ +
+
+
+

Advanced Designer Topics

+
+ +
+ +
+
+

Help

+
+ +
From 8dfef06aecaa13c99ef0d7873134be1d5cd05e57 Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Wed, 22 May 2024 10:31:44 +0300 Subject: [PATCH 126/154] QmlDesigner: Remove the optional func param from Model::generateNewId() Not needed anymore after using UniqueName class. Change-Id: I252659376af7e8aaf5e4642f9b9bfd2c57cc19fb Reviewed-by: Reviewed-by: Miikka Heikkinen Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Marco Bubke Reviewed-by: Vikas Pachdha --- src/plugins/qmldesigner/designercore/include/model.h | 3 +-- src/plugins/qmldesigner/designercore/model/model.cpp | 8 ++------ 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/src/plugins/qmldesigner/designercore/include/model.h b/src/plugins/qmldesigner/designercore/include/model.h index 5529064284e..bb45456def8 100644 --- a/src/plugins/qmldesigner/designercore/include/model.h +++ b/src/plugins/qmldesigner/designercore/include/model.h @@ -255,8 +255,7 @@ public: bool hasId(const QString &id) const; bool hasImport(const QString &importUrl) const; - QString generateNewId(const QString &prefixName, const QString &fallbackPrefix = "element", - std::optional> isDuplicate = {}) const; + QString generateNewId(const QString &prefixName, const QString &fallbackPrefix = "element") const; void startDrag(QMimeData *mimeData, const QPixmap &icon); void endDrag(); diff --git a/src/plugins/qmldesigner/designercore/model/model.cpp b/src/plugins/qmldesigner/designercore/model/model.cpp index 1100da62d6e..75b9dfd1bb2 100644 --- a/src/plugins/qmldesigner/designercore/model/model.cpp +++ b/src/plugins/qmldesigner/designercore/model/model.cpp @@ -1863,21 +1863,17 @@ bool Model::hasImport(const QString &importUrl) const }); } -QString Model::generateNewId(const QString &prefixName, const QString &fallbackPrefix, - std::optional> isDuplicate) const +QString Model::generateNewId(const QString &prefixName, const QString &fallbackPrefix) const { QString newId = prefixName; if (newId.isEmpty()) newId = fallbackPrefix; - if (!isDuplicate.has_value()) // TODO: to be removed separately to not break build - isDuplicate = std::bind(&Model::hasId, this, std::placeholders::_1); - return UniqueName::generateId(prefixName, [&] (const QString &id) { // Properties of the root node are not allowed for ids, because they are available in the // complete context without qualification. - return isDuplicate.value()(id) || d->rootNode()->property(id.toUtf8()); + return hasId(id) || d->rootNode()->property(id.toUtf8()); }); } From 1c216f0a3809d161e2bdc2d227dc8ca592812962 Mon Sep 17 00:00:00 2001 From: Mats Honkamaa Date: Mon, 13 May 2024 15:33:36 +0300 Subject: [PATCH 127/154] Doc: Add glow best practices docs Task-number: QDS-11695 Change-Id: Icf57fe5d94194e5be3d741a19f9f3dea51114048 Reviewed-by: Johanna Vanhatapio --- .../config/style/qt5-sidebar.html | 1 + doc/qtdesignstudio/images/bleed-scale-1.webp | Bin 0 -> 37134 bytes doc/qtdesignstudio/images/bleed-scale-8.webp | Bin 0 -> 34696 bytes doc/qtdesignstudio/images/bleed-scale-no.webp | Bin 0 -> 35312 bytes .../images/ext-scene-env-navigator.webp | Bin 0 -> 3884 bytes .../images/glow-additive-blend.webp | Bin 0 -> 77682 bytes doc/qtdesignstudio/images/glow-bicubic.webp | Bin 0 -> 19314 bytes .../images/glow-example-project.webp | Bin 0 -> 26004 bytes doc/qtdesignstudio/images/glow-example.webp | Bin 0 -> 23438 bytes .../images/glow-high-quality.webp | Bin 0 -> 21128 bytes .../images/glow-no-enhancment.webp | Bin 0 -> 18856 bytes .../images/glow-properties.webp | Bin 0 -> 15626 bytes .../images/glow-replace-blend.webp | Bin 0 -> 53186 bytes .../images/glow-screen-blend.webp | Bin 0 -> 77272 bytes .../images/glow-softlight-blend.webp | Bin 0 -> 72306 bytes .../images/glow_all_blur_levels.webp | Bin 0 -> 37506 bytes .../images/glow_high_blur_levels.webp | Bin 0 -> 29482 bytes .../images/glow_low_blur_levels.webp | Bin 0 -> 7888 bytes .../best practices/best-practices-glow.qdoc | 200 ++++++++++++++++++ doc/qtdesignstudio/src/best-practices.qdoc | 16 ++ .../src/qtdesignstudio-terms.qdoc | 2 +- .../src/qtdesignstudio-toc.qdoc | 1 + doc/qtdesignstudio/src/qtdesignstudio.qdoc | 1 + 23 files changed, 220 insertions(+), 1 deletion(-) create mode 100644 doc/qtdesignstudio/images/bleed-scale-1.webp create mode 100644 doc/qtdesignstudio/images/bleed-scale-8.webp create mode 100644 doc/qtdesignstudio/images/bleed-scale-no.webp create mode 100644 doc/qtdesignstudio/images/ext-scene-env-navigator.webp create mode 100644 doc/qtdesignstudio/images/glow-additive-blend.webp create mode 100644 doc/qtdesignstudio/images/glow-bicubic.webp create mode 100644 doc/qtdesignstudio/images/glow-example-project.webp create mode 100644 doc/qtdesignstudio/images/glow-example.webp create mode 100644 doc/qtdesignstudio/images/glow-high-quality.webp create mode 100644 doc/qtdesignstudio/images/glow-no-enhancment.webp create mode 100644 doc/qtdesignstudio/images/glow-properties.webp create mode 100644 doc/qtdesignstudio/images/glow-replace-blend.webp create mode 100644 doc/qtdesignstudio/images/glow-screen-blend.webp create mode 100644 doc/qtdesignstudio/images/glow-softlight-blend.webp create mode 100644 doc/qtdesignstudio/images/glow_all_blur_levels.webp create mode 100644 doc/qtdesignstudio/images/glow_high_blur_levels.webp create mode 100644 doc/qtdesignstudio/images/glow_low_blur_levels.webp create mode 100644 doc/qtdesignstudio/src/best practices/best-practices-glow.qdoc create mode 100644 doc/qtdesignstudio/src/best-practices.qdoc diff --git a/doc/qtdesignstudio/config/style/qt5-sidebar.html b/doc/qtdesignstudio/config/style/qt5-sidebar.html index 22b2895dc89..994ecd3db30 100644 --- a/doc/qtdesignstudio/config/style/qt5-sidebar.html +++ b/doc/qtdesignstudio/config/style/qt5-sidebar.html @@ -16,6 +16,7 @@
  • Creating Projects
  • Use Cases
  • Concepts and Terms
  • +
  • Best Practices
  • Examples
  • diff --git a/doc/qtdesignstudio/images/bleed-scale-1.webp b/doc/qtdesignstudio/images/bleed-scale-1.webp new file mode 100644 index 0000000000000000000000000000000000000000..cfe9d7bd7e0e468e138fbb0301e8aaf71432f828 GIT binary patch literal 37134 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 literal 0 HcmV?d00001 diff --git a/doc/qtdesignstudio/images/bleed-scale-8.webp b/doc/qtdesignstudio/images/bleed-scale-8.webp new file mode 100644 index 0000000000000000000000000000000000000000..3d4ef0b7834efb27aba7c7b871338f827f600e6c GIT binary patch literal 34696 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 I(w;Vq03Y}~a{vGU literal 0 HcmV?d00001 diff --git a/doc/qtdesignstudio/images/bleed-scale-no.webp b/doc/qtdesignstudio/images/bleed-scale-no.webp new file mode 100644 index 0000000000000000000000000000000000000000..e102ae7501aa7db0dd19c0e663d1bb78c38ba94d GIT binary patch literal 35312 zcmWIYbaQ*r$-ofq>J$(bVBvGSlYv3sg)xx9H=swwyhp{=)S+S5v0wQHnrHX33;vzo z(DdN>Uxt*lSvk7i0uvlu%)d_Rf9$9wmm2D{=y)%qr`8R#Evd?>_RILAE-ei+caZW3 zm^_s;M080|gKrv>M`)ZRgKyD0-Vcv_>du6QddggC4k}%sU&%0Asp;ONE4s}6M}FD_ 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 literal 0 HcmV?d00001 diff --git a/doc/qtdesignstudio/images/ext-scene-env-navigator.webp b/doc/qtdesignstudio/images/ext-scene-env-navigator.webp new file mode 100644 index 0000000000000000000000000000000000000000..675a9aedfcc6e1a80b399e48e8511c96072b0a89 GIT binary patch literal 3884 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 literal 0 HcmV?d00001 diff --git a/doc/qtdesignstudio/images/glow-additive-blend.webp b/doc/qtdesignstudio/images/glow-additive-blend.webp new file mode 100644 index 0000000000000000000000000000000000000000..418628fc8be40df297b5368eaf58a38aa342ed67 GIT binary patch literal 77682 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 H@}Ca?UozWD literal 0 HcmV?d00001 diff --git a/doc/qtdesignstudio/images/glow-example-project.webp b/doc/qtdesignstudio/images/glow-example-project.webp new file mode 100644 index 0000000000000000000000000000000000000000..2163fee2d91f2d5799d74ad38e41b7fbca44e619 GIT binary patch literal 26004 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 Fd;l2h@pAwG literal 0 HcmV?d00001 diff --git a/doc/qtdesignstudio/images/glow-example.webp b/doc/qtdesignstudio/images/glow-example.webp new file mode 100644 index 0000000000000000000000000000000000000000..4de776aa443fc03d3e76f91a9384506b59705c6b GIT binary patch literal 23438 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 literal 0 HcmV?d00001 diff --git a/doc/qtdesignstudio/images/glow-high-quality.webp b/doc/qtdesignstudio/images/glow-high-quality.webp new file mode 100644 index 0000000000000000000000000000000000000000..80434dde67ac0dd36dd2d913fc00406fa2e1da17 GIT binary patch literal 21128 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 literal 0 HcmV?d00001 diff --git a/doc/qtdesignstudio/images/glow-no-enhancment.webp b/doc/qtdesignstudio/images/glow-no-enhancment.webp new file mode 100644 index 0000000000000000000000000000000000000000..87d90da35f10f99eb05ed6bacb72f113ea74c5f2 GIT binary patch literal 18856 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 literal 0 HcmV?d00001 diff --git a/doc/qtdesignstudio/images/glow-properties.webp b/doc/qtdesignstudio/images/glow-properties.webp new file mode 100644 index 0000000000000000000000000000000000000000..b2d22736eb33265a5ad60dffbe11537b5d253c3d GIT binary patch literal 15626 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�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 literal 0 HcmV?d00001 diff --git a/doc/qtdesignstudio/images/glow-softlight-blend.webp b/doc/qtdesignstudio/images/glow-softlight-blend.webp new file mode 100644 index 0000000000000000000000000000000000000000..3ac9ef2640607ab771f4b68c299f7ba9c9657e5d GIT binary patch literal 72306 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 literal 0 HcmV?d00001 diff --git a/doc/qtdesignstudio/images/glow_all_blur_levels.webp b/doc/qtdesignstudio/images/glow_all_blur_levels.webp new file mode 100644 index 0000000000000000000000000000000000000000..a5cc30d9cc00cf13291f1cfb1b94791521f2752a GIT binary patch literal 37506 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 z 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 Date: Wed, 22 May 2024 17:18:14 +0200 Subject: [PATCH 128/154] QmlDesigner: Update Qt Academy course links in documents This patch adds some of the Qt Academy course links in the relevant documents. Fixes: QDS-12747 Change-Id: I8309681869922be3277e775c312ffa1210d64a29 Reviewed-by: Jaishree Vyas --- doc/config/macros.qdocconf | 1 + .../src/qtbridge/qtbridge-figma-overview.qdoc | 6 ++++- .../src/qtbridge/qtbridge-figma-setup.qdoc | 4 +++- .../src/qtbridge/qtbridge-figma-using.qdoc | 4 +++- .../src/qtdesignstudio-getting-started.qdoc | 4 +++- .../src/qtdesignstudio-qt-academy.qdocinc | 22 +++++++++++++++++++ .../qtdesignstudio-3d-editor.qdoc | 4 +++- 7 files changed, 40 insertions(+), 5 deletions(-) create mode 100644 doc/qtdesignstudio/src/qtdesignstudio-qt-academy.qdocinc diff --git a/doc/config/macros.qdocconf b/doc/config/macros.qdocconf index f86590735b6..46e4e0b20c2 100644 --- a/doc/config/macros.qdocconf +++ b/doc/config/macros.qdocconf @@ -18,6 +18,7 @@ macro.oslash.HTML = "ø" macro.ouml.HTML = "ö" macro.Q3DS = "Qt 3D Studio" macro.QA = "Qt Assistant" +macro.QAC = "Qt Academy" macro.QB = "Qt Bridge" macro.QBPS = "Qt Bridge for Adobe Photoshop" macro.QBXD = "Qt Bridge for Adobe XD" diff --git a/doc/qtdesignstudio/src/qtbridge/qtbridge-figma-overview.qdoc b/doc/qtdesignstudio/src/qtbridge/qtbridge-figma-overview.qdoc index b721acfa9e3..798ca9551c6 100644 --- a/doc/qtdesignstudio/src/qtbridge/qtbridge-figma-overview.qdoc +++ b/doc/qtdesignstudio/src/qtbridge/qtbridge-figma-overview.qdoc @@ -1,4 +1,4 @@ -// Copyright (C) 2020 The Qt Company Ltd. +// Copyright (C) 2024 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only // Note: The \page value is hard-coded as a link in Qt Bridge for Figma. @@ -29,5 +29,9 @@ To get the best results when you use \QBF to export designs from Figma, you should follow the guidelines for working with Figma and organizing your assets. + \endlist + + \include qtdesignstudio-qt-academy.qdocinc qt-academy-using-qt-bridge-for-figma + */ diff --git a/doc/qtdesignstudio/src/qtbridge/qtbridge-figma-setup.qdoc b/doc/qtdesignstudio/src/qtbridge/qtbridge-figma-setup.qdoc index 33d130f3360..00e9586df25 100644 --- a/doc/qtdesignstudio/src/qtbridge/qtbridge-figma-setup.qdoc +++ b/doc/qtdesignstudio/src/qtbridge/qtbridge-figma-setup.qdoc @@ -1,4 +1,4 @@ -// Copyright (C) 2020 The Qt Company Ltd. +// Copyright (C) 2024 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only /*! @@ -25,4 +25,6 @@ \endlist You can launch the installed Figma plugin from \uicontrol Plugins > \uicontrol {\QBF} in Figma. + + \include qtdesignstudio-qt-academy.qdocinc qt-academy-using-qt-bridge-for-figma */ diff --git a/doc/qtdesignstudio/src/qtbridge/qtbridge-figma-using.qdoc b/doc/qtdesignstudio/src/qtbridge/qtbridge-figma-using.qdoc index b189500f491..22cc72105e7 100644 --- a/doc/qtdesignstudio/src/qtbridge/qtbridge-figma-using.qdoc +++ b/doc/qtdesignstudio/src/qtbridge/qtbridge-figma-using.qdoc @@ -1,4 +1,4 @@ -// Copyright (C) 2021 The Qt Company Ltd. +// Copyright (C) 2024 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only /*! @@ -247,4 +247,6 @@ \uicontrol Home tab) to default values. This means that you will lose all your changes to the settings. \endtable + + \include qtdesignstudio-qt-academy.qdocinc qt-academy-using-qt-bridge-for-figma */ diff --git a/doc/qtdesignstudio/src/qtdesignstudio-getting-started.qdoc b/doc/qtdesignstudio/src/qtdesignstudio-getting-started.qdoc index 493242254f8..a975dfe7fc6 100644 --- a/doc/qtdesignstudio/src/qtdesignstudio-getting-started.qdoc +++ b/doc/qtdesignstudio/src/qtdesignstudio-getting-started.qdoc @@ -1,4 +1,4 @@ -// Copyright (C) 2021 The Qt Company Ltd. +// Copyright (C) 2024 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only /*! @@ -56,4 +56,6 @@ If you would rather be shown things than read about them, you can watch our extensive video tutorials. \endlist + + \include qtdesignstudio-qt-academy.qdocinc qt-academy-getting-started-with-qt-design-studio */ diff --git a/doc/qtdesignstudio/src/qtdesignstudio-qt-academy.qdocinc b/doc/qtdesignstudio/src/qtdesignstudio-qt-academy.qdocinc new file mode 100644 index 00000000000..3769ddc5e34 --- /dev/null +++ b/doc/qtdesignstudio/src/qtdesignstudio-qt-academy.qdocinc @@ -0,0 +1,22 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only + +//! [qt-academy-using-qt-bridge-for-figma] + To learn the basics of \QBF, take the + \l {https://www.qt.io/academy/course-catalog#how-to-use-qt-bridge-for-figma} + {How to Use \QBF} course in \QAC. +//! [qt-academy-using-qt-bridge-for-figma] + +//! [qt-academy-3D-with-qt-design-studio] + To learn the basics of using 3D in \QDS, take the + \l {https://www.qt.io/academy/course-catalog#3d-with-qt-design-studio} + {3D with \QDS} course in \QAC. +//! [qt-academy-3D-with-qt-design-studio] + +//! [qt-academy-getting-started-with-qt-design-studio] + To learn the basics of using \QDS, take the + \l {https://www.qt.io/academy/course-catalog#getting-started-with-qt-design-studio} + {Getting Started with \QDS} course in \QAC. +//! [qt-academy-getting-started-with-qt-design-studio] + +*/ diff --git a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-editor.qdoc b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-editor.qdoc index aba12f38c1e..e691b47c770 100644 --- a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-editor.qdoc +++ b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-editor.qdoc @@ -47,7 +47,9 @@ \youtube SsFWyUeAA_4 - \section1 Usingn the Context Menu in the 3D View + \include qtdesignstudio-qt-academy.qdocinc qt-academy-3D-with-qt-design-studio + + \section1 Using the Context Menu in the 3D View There is a context menu in the \uicontrol 3D view. To open it, right-click in the \uicontrol 3D view. From the context menu you can, for example: From fd2c7db691618050742e28aed358385552bc75a0 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Mon, 20 May 2024 12:32:36 +0300 Subject: [PATCH 129/154] QmlDesigner: Use correct Enumeration type for instance values Puppet used to return just string for enumeration types. Now it will use Enumeration type properly. Change-Id: I602891e34c03cb659ee0a8e4571b39b6e24f9e1c Reviewed-by: Thomas Hartmann Reviewed-by: Qt CI Patch Build Bot --- .../qml2puppet/qml2puppet/instances/nodeinstanceserver.cpp | 5 +++-- .../qml2puppet/qml2puppet/instances/objectnodeinstance.cpp | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/tools/qml2puppet/qml2puppet/instances/nodeinstanceserver.cpp b/src/tools/qml2puppet/qml2puppet/instances/nodeinstanceserver.cpp index f3227e386cc..0f7133b1ff5 100644 --- a/src/tools/qml2puppet/qml2puppet/instances/nodeinstanceserver.cpp +++ b/src/tools/qml2puppet/qml2puppet/instances/nodeinstanceserver.cpp @@ -1187,8 +1187,9 @@ InformationChangedCommand NodeInstanceServer::createAllInformationChangedCommand static bool supportedVariantType(int type) { - return type < int(QVariant::UserType) && type != QMetaType::QObjectStar - && type != QMetaType::QModelIndex && type != QMetaType::VoidStar; + return (type < int(QVariant::UserType) && type != QMetaType::QObjectStar + && type != QMetaType::QModelIndex && type != QMetaType::VoidStar) + || type == QMetaType::fromType().id(); } ValuesChangedCommand NodeInstanceServer::createValuesChangedCommand(const QList &instanceList) const diff --git a/src/tools/qml2puppet/qml2puppet/instances/objectnodeinstance.cpp b/src/tools/qml2puppet/qml2puppet/instances/objectnodeinstance.cpp index 453a30395f1..2ad26c42866 100644 --- a/src/tools/qml2puppet/qml2puppet/instances/objectnodeinstance.cpp +++ b/src/tools/qml2puppet/qml2puppet/instances/objectnodeinstance.cpp @@ -619,7 +619,8 @@ QVariant ObjectNodeInstance::property(const PropertyName &name) const QQmlProperty property(object(), QString::fromUtf8(name), context()); if (property.property().isEnumType()) { QVariant value = property.read(); - return property.property().enumerator().valueToKey(value.toInt()); + QMetaEnum me = property.property().enumerator(); + return QVariant::fromValue(Enumeration(me.scope(), me.valueToKey(value.toInt()))); } if (property.propertyType() == QVariant::Url) { From f296f9d77cad461118ef5cd594e3fd9c173bedfb Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Mon, 6 May 2024 17:02:32 +0200 Subject: [PATCH 130/154] QmlDesigner: Parse components recursively Change-Id: I8fa892cce8e34b5e58cbdde04c57e30b9fc74866 Reviewed-by: Reviewed-by: Tim Jenssen Reviewed-by: Qt CI Patch Build Bot --- .../qmldesigner/projectstorage/fake.qmltypes | 9 + .../designercore/include/projectstorageids.h | 2 + .../projectstorage/filesystem.cpp | 13 + .../designercore/projectstorage/filesystem.h | 1 + .../projectstorage/filesysteminterface.h | 1 + .../projectstorage/projectstorage.cpp | 78 +++-- .../projectstorage/projectstorage.h | 6 +- .../projectstorage/projectstorageinfotypes.h | 5 + .../projectstorage/projectstorageinterface.h | 4 + .../projectstorage/projectstoragetypes.h | 11 +- .../projectstorage/projectstorageupdater.cpp | 135 ++++++++- .../projectstorage/projectstorageupdater.h | 8 + .../designercore/projectstorage/sourcepath.h | 5 +- .../qmldesigner/qmldesignerprojectmanager.cpp | 69 +---- tests/unit/tests/mocks/filesystemmock.h | 1 + tests/unit/tests/mocks/projectstoragemock.h | 10 + .../tests/printers/gtest-creator-printing.cpp | 4 +- .../projectstorage/projectstorage-test.cpp | 43 +++ .../projectstorageupdater-test.cpp | 286 ++++++++++++++++-- 19 files changed, 566 insertions(+), 125 deletions(-) diff --git a/share/qtcreator/qmldesigner/projectstorage/fake.qmltypes b/share/qtcreator/qmldesigner/projectstorage/fake.qmltypes index f6f21596899..60096cd7900 100644 --- a/share/qtcreator/qmldesigner/projectstorage/fake.qmltypes +++ b/share/qtcreator/qmldesigner/projectstorage/fake.qmltypes @@ -318,4 +318,13 @@ Component { Component { name: "QScxmlError" } + +Component { + name: "QList" +} + +Component { + name: "VertexColorMaskFlags" +} + } diff --git a/src/plugins/qmldesigner/designercore/include/projectstorageids.h b/src/plugins/qmldesigner/designercore/include/projectstorageids.h index 0b157e55e77..7c2e8c4b252 100644 --- a/src/plugins/qmldesigner/designercore/include/projectstorageids.h +++ b/src/plugins/qmldesigner/designercore/include/projectstorageids.h @@ -48,6 +48,8 @@ using EnumerationDeclarationIds = std::vector; using SourceContextId = Sqlite::BasicId; using SourceContextIds = std::vector; +template +using SmallSourceContextIds = QVarLengthArray; using SourceId = Sqlite::BasicId; using SourceIds = std::vector; diff --git a/src/plugins/qmldesigner/designercore/projectstorage/filesystem.cpp b/src/plugins/qmldesigner/designercore/projectstorage/filesystem.cpp index 1376b2c3d9d..d11190fdc74 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/filesystem.cpp +++ b/src/plugins/qmldesigner/designercore/projectstorage/filesystem.cpp @@ -11,6 +11,7 @@ #include #include +#include #include namespace QmlDesigner { @@ -69,6 +70,18 @@ QString FileSystem::contentAsQString(const QString &filePath) const return {}; } +QStringList FileSystem::subdirectories(const QString &directoryPath) const +{ + QStringList directoryPaths; + directoryPaths.reserve(100); + QDirIterator directoryIterator{directoryPath, QDir::Dirs | QDir::NoDotAndDotDot}; + + while (directoryIterator.hasNext()) + directoryPaths.push_back(directoryIterator.next()); + + return directoryPaths; +} + void FileSystem::remove(const SourceIds &sourceIds) { for (SourceId sourceId : sourceIds) diff --git a/src/plugins/qmldesigner/designercore/projectstorage/filesystem.h b/src/plugins/qmldesigner/designercore/projectstorage/filesystem.h index 28754a8560b..1c881741c6a 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/filesystem.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/filesystem.h @@ -31,6 +31,7 @@ public: long long lastModified(SourceId sourceId) const override; FileStatus fileStatus(SourceId sourceId) const override; QString contentAsQString(const QString &filePath) const override; + QStringList subdirectories(const QString &directoryPath) const override; void remove(const SourceIds &sourceIds) override; diff --git a/src/plugins/qmldesigner/designercore/projectstorage/filesysteminterface.h b/src/plugins/qmldesigner/designercore/projectstorage/filesysteminterface.h index 6a7c964fa61..ff7608c9a3f 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/filesysteminterface.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/filesysteminterface.h @@ -20,6 +20,7 @@ public: virtual FileStatus fileStatus(SourceId sourceId) const = 0; virtual void remove(const SourceIds &sourceIds) = 0; virtual QString contentAsQString(const QString &filePath) const = 0; + virtual QStringList subdirectories(const QString &directoryPath) const = 0; protected: ~FileSystemInterface() = default; diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.cpp b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.cpp index 98ee3253df7..3c6636f1f5f 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.cpp +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.cpp @@ -519,6 +519,12 @@ struct ProjectStorage::Statements "SELECT directorySourceId, sourceId, moduleId, fileType FROM directoryInfos WHERE " "directorySourceId=?1", database}; + mutable Sqlite::ReadStatement<4, 2> selectDirectoryInfosForSourceIdAndFileTypeStatement{ + "SELECT directorySourceId, sourceId, moduleId, fileType FROM directoryInfos WHERE " + "directorySourceId=?1 AND fileType=?2", + database}; + mutable Sqlite::ReadStatement<1, 2> selectDirectoryInfosSourceIdsForSourceIdAndFileTypeStatement{ + "SELECT sourceId FROM directoryInfos WHERE directorySourceId=?1 AND fileType=?2", database}; mutable Sqlite::ReadStatement<4, 1> selectDirectoryInfoForSourceIdStatement{ "SELECT directorySourceId, sourceId, moduleId, fileType FROM directoryInfos WHERE " "sourceId=?1 LIMIT 1", @@ -1073,10 +1079,11 @@ public: Sqlite::StrictColumnType::Integer); auto &sourceIdColumn = table.addColumn("sourceId", Sqlite::StrictColumnType::Integer); table.addColumn("moduleId", Sqlite::StrictColumnType::Integer); - table.addColumn("fileType", Sqlite::StrictColumnType::Integer); + auto &fileTypeColumn = table.addColumn("fileType", Sqlite::StrictColumnType::Integer); table.addPrimaryKeyContraint({directorySourceIdColumn, sourceIdColumn}); table.addUniqueIndex({sourceIdColumn}); + table.addIndex({directorySourceIdColumn, fileTypeColumn}); table.initialize(database); } @@ -1196,7 +1203,7 @@ void ProjectStorage::synchronize(Storage::Synchronization::SynchronizationPackag linkAliases(insertedAliasPropertyDeclarations, updatedAliasPropertyDeclarations); - synchronizeDirectoryInfos(package.directoryInfos, package.updatedProjectSourceIds); + synchronizeDirectoryInfos(package.directoryInfos, package.updatedDirectoryInfoSourceIds); commonTypeCache_.resetTypeIds(); }); @@ -2116,7 +2123,7 @@ FileStatus ProjectStorage::fetchFileStatus(SourceId sourceId) const std::optional ProjectStorage::fetchDirectoryInfo(SourceId sourceId) const { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"fetch project data"_t, + NanotraceHR::Tracer tracer{"fetch directory info"_t, projectStorageCategory(), keyValue("source id", sourceId)}; @@ -2124,7 +2131,7 @@ std::optional ProjectStorage::fetchDire .optionalValueWithTransaction( sourceId); - tracer.end(keyValue("project data", directoryInfo)); + tracer.end(keyValue("directory info", directoryInfo)); return directoryInfo; } @@ -2132,7 +2139,7 @@ std::optional ProjectStorage::fetchDire Storage::Synchronization::DirectoryInfos ProjectStorage::fetchDirectoryInfos(SourceId directorySourceId) const { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"fetch project datas by source id"_t, + NanotraceHR::Tracer tracer{"fetch directory infos by source id"_t, projectStorageCategory(), keyValue("source id", directorySourceId)}; @@ -2140,7 +2147,25 @@ Storage::Synchronization::DirectoryInfos ProjectStorage::fetchDirectoryInfos(Sou .valuesWithTransaction( directorySourceId); - tracer.end(keyValue("project datas", directoryInfos)); + tracer.end(keyValue("directory infos", directoryInfos)); + + return directoryInfos; +} + +Storage::Synchronization::DirectoryInfos ProjectStorage::fetchDirectoryInfos( + SourceId directorySourceId, Storage::Synchronization::FileType fileType) const +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch directory infos by source id and file type"_t, + projectStorageCategory(), + keyValue("source id", directorySourceId), + keyValue("file type", fileType)}; + + auto directoryInfos = s->selectDirectoryInfosForSourceIdAndFileTypeStatement + .valuesWithTransaction( + directorySourceId, fileType); + + tracer.end(keyValue("directory infos", directoryInfos)); return directoryInfos; } @@ -2149,7 +2174,7 @@ Storage::Synchronization::DirectoryInfos ProjectStorage::fetchDirectoryInfos( const SourceIds &directorySourceIds) const { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"fetch project datas by source ids"_t, + NanotraceHR::Tracer tracer{"fetch directory infos by source ids"_t, projectStorageCategory(), keyValue("source ids", directorySourceIds)}; @@ -2157,11 +2182,27 @@ Storage::Synchronization::DirectoryInfos ProjectStorage::fetchDirectoryInfos( .valuesWithTransaction( toIntegers(directorySourceIds)); - tracer.end(keyValue("project datas", directoryInfos)); + tracer.end(keyValue("directory infos", directoryInfos)); return directoryInfos; } +SmallSourceIds<32> ProjectStorage::fetchSubdirectorySourceIds(SourceId directorySourceId) const +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch subdirectory source ids"_t, + projectStorageCategory(), + keyValue("source id", directorySourceId)}; + + auto sourceIds = s->selectDirectoryInfosSourceIdsForSourceIdAndFileTypeStatement + .valuesWithTransaction>( + directorySourceId, Storage::Synchronization::FileType::Directory); + + tracer.end(keyValue("source ids", sourceIds)); + + return sourceIds; +} + void ProjectStorage::setPropertyEditorPathId(TypeId typeId, SourceId pathId) { Sqlite::ImmediateSessionTransaction transaction{database}; @@ -2466,9 +2507,9 @@ void ProjectStorage::synchronizeTypes(Storage::Synchronization::Types &types, } void ProjectStorage::synchronizeDirectoryInfos(Storage::Synchronization::DirectoryInfos &directoryInfos, - const SourceIds &updatedProjectSourceIds) + const SourceIds &updatedDirectoryInfoSourceIds) { - NanotraceHR::Tracer tracer{"synchronize project datas"_t, projectStorageCategory()}; + NanotraceHR::Tracer tracer{"synchronize directory infos"_t, projectStorageCategory()}; auto compareKey = [](auto &&first, auto &&second) { auto directorySourceIdDifference = first.directorySourceId - second.directorySourceId; @@ -2484,13 +2525,13 @@ void ProjectStorage::synchronizeDirectoryInfos(Storage::Synchronization::Directo }); auto range = s->selectDirectoryInfosForSourceIdsStatement.range( - toIntegers(updatedProjectSourceIds)); + toIntegers(updatedDirectoryInfoSourceIds)); auto insert = [&](const Storage::Synchronization::DirectoryInfo &directoryInfo) { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"insert project data"_t, + NanotraceHR::Tracer tracer{"insert directory info"_t, projectStorageCategory(), - keyValue("project data", directoryInfo)}; + keyValue("directory info", directoryInfo)}; if (!directoryInfo.directorySourceId) throw DirectoryInfoHasInvalidProjectSourceId{}; @@ -2508,10 +2549,11 @@ void ProjectStorage::synchronizeDirectoryInfos(Storage::Synchronization::Directo if (directoryInfoFromDatabase.fileType != directoryInfo.fileType || !compareInvalidAreTrue(directoryInfoFromDatabase.moduleId, directoryInfo.moduleId)) { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"update project data"_t, + NanotraceHR::Tracer tracer{"update directory info"_t, projectStorageCategory(), - keyValue("project data", directoryInfo), - keyValue("project data from database", directoryInfoFromDatabase)}; + keyValue("directory info", directoryInfo), + keyValue("directory info from database", + directoryInfoFromDatabase)}; s->updateDirectoryInfoStatement.write(directoryInfo.directorySourceId, directoryInfo.sourceId, @@ -2525,9 +2567,9 @@ void ProjectStorage::synchronizeDirectoryInfos(Storage::Synchronization::Directo auto remove = [&](const Storage::Synchronization::DirectoryInfo &directoryInfo) { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"remove project data"_t, + NanotraceHR::Tracer tracer{"remove directory info"_t, projectStorageCategory(), - keyValue("project data", directoryInfo)}; + keyValue("directory info", directoryInfo)}; s->deleteDirectoryInfoStatement.write(directoryInfo.directorySourceId, directoryInfo.sourceId); }; diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h index 0ae508f58f1..8f69049f706 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h @@ -232,8 +232,10 @@ public: std::optional fetchDirectoryInfo(SourceId sourceId) const override; Storage::Synchronization::DirectoryInfos fetchDirectoryInfos(SourceId directorySourceId) const override; - + Storage::Synchronization::DirectoryInfos fetchDirectoryInfos( + SourceId directorySourceId, Storage::Synchronization::FileType fileType) const override; Storage::Synchronization::DirectoryInfos fetchDirectoryInfos(const SourceIds &directorySourceIds) const; + SmallSourceIds<32> fetchSubdirectorySourceIds(SourceId directorySourceId) const override; void setPropertyEditorPathId(TypeId typeId, SourceId pathId); @@ -561,7 +563,7 @@ private: const SourceIds &updatedSourceIds); void synchronizeDirectoryInfos(Storage::Synchronization::DirectoryInfos &directoryInfos, - const SourceIds &updatedProjectSourceIds); + const SourceIds &updatedDirectoryInfoSourceIds); void synchronizeFileStatuses(FileStatuses &fileStatuses, const SourceIds &updatedSourceIds); diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinfotypes.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinfotypes.h index 98685d22e2d..75ab0d1f5a5 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinfotypes.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinfotypes.h @@ -8,6 +8,8 @@ #include #include +#include + #include #include #include @@ -15,6 +17,9 @@ namespace QmlDesigner { +template +using SmallPathStrings = QVarLengthArray; + template constexpr std::underlying_type_t to_underlying(Enumeration enumeration) noexcept { diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinterface.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinterface.h index fcaccd932ff..4d840d2a5c5 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinterface.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinterface.h @@ -81,7 +81,11 @@ public: virtual FileStatus fetchFileStatus(SourceId sourceId) const = 0; virtual Storage::Synchronization::DirectoryInfos fetchDirectoryInfos(SourceId sourceId) const = 0; + virtual Storage::Synchronization::DirectoryInfos fetchDirectoryInfos( + SourceId directorySourceId, Storage::Synchronization::FileType) const + = 0; virtual std::optional fetchDirectoryInfo(SourceId sourceId) const = 0; + virtual SmallSourceIds<32> fetchSubdirectorySourceIds(SourceId directorySourceId) const = 0; virtual SourceId propertyEditorPathId(TypeId typeId) const = 0; virtual const Storage::Info::CommonTypeCache &commonTypeCache() const = 0; diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstoragetypes.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstoragetypes.h index 7eb3767219b..5f51a9acf61 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstoragetypes.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstoragetypes.h @@ -82,7 +82,7 @@ void convertToString(String &string, const TypeNameKind &kind) } } -enum class FileType : char { QmlTypes, QmlDocument }; +enum class FileType : char { QmlTypes, QmlDocument, Directory }; template void convertToString(String &string, const FileType &type) @@ -94,6 +94,9 @@ void convertToString(String &string, const FileType &type) case FileType::QmlDocument: convertToString(string, "QmlDocument"); break; + case FileType::Directory: + convertToString(string, "Directory"); + break; } } @@ -1291,9 +1294,9 @@ public: , fileStatuses(std::move(fileStatuses)) {} - SynchronizationPackage(SourceIds updatedProjectSourceIds, DirectoryInfos directoryInfos) + SynchronizationPackage(SourceIds updatedDirectoryInfoSourceIds, DirectoryInfos directoryInfos) : directoryInfos(std::move(directoryInfos)) - , updatedProjectSourceIds(std::move(updatedProjectSourceIds)) + , updatedDirectoryInfoSourceIds(std::move(updatedDirectoryInfoSourceIds)) {} public: @@ -1303,7 +1306,7 @@ public: SourceIds updatedFileStatusSourceIds; FileStatuses fileStatuses; DirectoryInfos directoryInfos; - SourceIds updatedProjectSourceIds; + SourceIds updatedDirectoryInfoSourceIds; Imports moduleDependencies; SourceIds updatedModuleDependencySourceIds; ModuleExportedImports moduleExportedImports; diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.cpp b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.cpp index e6f347ff12a..a0e7bba3c52 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.cpp +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.cpp @@ -13,9 +13,9 @@ #include "sourcepathcache.h" #include "typeannotationreader.h" -#include - #include +#include +#include #include #include @@ -102,6 +102,8 @@ ProjectStorageUpdater::Components createComponents( } for (const QmlDirParser::Component &qmlDirParserComponent : qmlDirParserComponents) { + if (qmlDirParserComponent.fileName.contains('/')) + continue; components.push_back(ProjectStorageUpdater::Component{qmlDirParserComponent.fileName, qmlDirParserComponent.typeName, moduleId, @@ -285,6 +287,8 @@ void ProjectStorageUpdater::update(QStringList directories, try { m_projectStorage.synchronize(std::move(package)); + } catch (const TypeNameDoesNotExists &exception) { + qDebug() << "missing type: " << exception.what(); } catch (...) { qWarning() << "Project storage could not been updated!"; } @@ -323,7 +327,7 @@ void ProjectStorageUpdater::updateQmlTypes(const QStringList &qmlTypesPaths, tracer.tick("append project data"_t, keyValue("project data", directoryInfo)); package.directoryInfos.push_back(std::move(directoryInfo)); tracer.tick("append updated project source ids"_t, keyValue("source id", sourceId)); - package.updatedProjectSourceIds.push_back(sourceId); + package.updatedDirectoryInfoSourceIds.push_back(sourceId); } } } @@ -409,7 +413,7 @@ void ProjectStorageUpdater::updateDirectoryChanged(std::string_view directoryPat watchedSourceIdsIds, qmldirState); tracer.tick("append updated project source id"_t, keyValue("module id", moduleId)); - package.updatedProjectSourceIds.push_back(directorySourceId); + package.updatedDirectoryInfoSourceIds.push_back(directorySourceId); } void ProjectStorageUpdater::updateDirectories(const QStringList &directories, @@ -420,10 +424,111 @@ void ProjectStorageUpdater::updateDirectories(const QStringList &directories, NanotraceHR::Tracer tracer{"update directories"_t, category()}; for (const QString &directory : directories) - updateDirectory({directory}, package, notUpdatedSourceIds, watchedSourceIdsIds); + updateDirectory({directory}, {}, package, notUpdatedSourceIds, watchedSourceIdsIds); +} + +void ProjectStorageUpdater::updateSubdirectories(const Utils::PathString &directoryPath, + SourceId directorySourceId, + FileState directoryState, + const SourceContextIds &subdirectoriesToIgnore, + Storage::Synchronization::SynchronizationPackage &package, + NotUpdatedSourceIds ¬UpdatedSourceIds, + WatchedSourceIdsIds &watchedSourceIdsIds) +{ + struct Directory + { + Directory(Utils::SmallStringView path, SourceContextId sourceContextId, SourceId sourceId) + : path{path} + , sourceContextId{sourceContextId} + , sourceId{sourceId} + {} + + bool operator<(const Directory &other) const + { + return sourceContextId < other.sourceContextId; + } + + bool operator==(const Directory &other) const + { + return sourceContextId == other.sourceContextId; + } + + Utils::PathString path; + SourceContextId sourceContextId; + SourceId sourceId; + }; + + struct Compare + { + bool operator()(const Directory &first, const Directory &second) const + { + return first.sourceContextId < second.sourceContextId; + } + + bool operator()(const Directory &first, SourceContextId second) const + { + return first.sourceContextId < second; + } + + bool operator()(SourceContextId first, const Directory &second) const + { + return first < second.sourceContextId; + } + }; + + using Directories = QVarLengthArray; + + auto subdirectorySourceIds = m_projectStorage.fetchSubdirectorySourceIds(directorySourceId); + auto subdirectories = Utils::transform( + subdirectorySourceIds, [&](SourceId sourceId) -> Directory { + auto sourceContextId = m_pathCache.sourceContextId(sourceId); + auto subdirectoryPath = m_pathCache.sourceContextPath(sourceContextId); + return {subdirectoryPath, sourceContextId, sourceId}; + }); + + auto exisitingSubdirectoryPaths = m_fileSystem.subdirectories(directoryPath.toQString()); + Directories existingSubdirecories; + for (const QString &subdirectory : exisitingSubdirectoryPaths) { + if (subdirectory.endsWith("/designer") || subdirectory.endsWith("/QtQuick/Scene2D") + || subdirectory.endsWith("/QtQuick/Scene3D")) + continue; + Utils::PathString subdirectoryPath = subdirectory; + auto [sourceContextId, sourceId] = m_pathCache.sourceContextAndSourceId( + SourcePath{subdirectoryPath + "/."}); + subdirectories.emplace_back(subdirectoryPath, sourceContextId, sourceId); + existingSubdirecories.emplace_back(subdirectoryPath, sourceContextId, sourceId); + } + + std::sort(subdirectories.begin(), subdirectories.end()); + subdirectories.erase(std::unique(subdirectories.begin(), subdirectories.end()), + subdirectories.end()); + + std::set_difference(subdirectories.begin(), + subdirectories.end(), + subdirectoriesToIgnore.begin(), + subdirectoriesToIgnore.end(), + Utils::make_iterator([&](const Directory &subdirectory) { + updateDirectory(subdirectory.path, + subdirectoriesToIgnore, + package, + notUpdatedSourceIds, + watchedSourceIdsIds); + }), + Compare{}); + + if (directoryState == FileState::Changed) { + for (const auto &[subdirectoryPath, sourceContextId, subdirectorySourceId] : + existingSubdirecories) { + package.directoryInfos.emplace_back(directorySourceId, + subdirectorySourceId, + ModuleId{}, + Storage::Synchronization::FileType::Directory); + } + } } void ProjectStorageUpdater::updateDirectory(const Utils::PathString &directoryPath, + const SourceContextIds &subdirectoriesToIgnore, Storage::Synchronization::SynchronizationPackage &package, NotUpdatedSourceIds ¬UpdatedSourceIds, WatchedSourceIdsIds &watchedSourceIdsIds) @@ -472,7 +577,7 @@ void ProjectStorageUpdater::updateDirectory(const Utils::PathString &directoryPa package.updatedFileStatusSourceIds.push_back(directorySourceId); package.updatedFileStatusSourceIds.push_back(qmldirSourceId); - package.updatedProjectSourceIds.push_back(directorySourceId); + package.updatedDirectoryInfoSourceIds.push_back(directorySourceId); package.updatedSourceIds.push_back(qmldirSourceId); auto qmlDirectoryInfos = m_projectStorage.fetchDirectoryInfos(directorySourceId); for (const Storage::Synchronization::DirectoryInfo &directoryInfo : qmlDirectoryInfos) { @@ -487,6 +592,14 @@ void ProjectStorageUpdater::updateDirectory(const Utils::PathString &directoryPa } } + updateSubdirectories(directoryPath, + directorySourceId, + directoryState, + subdirectoriesToIgnore, + package, + notUpdatedSourceIds, + watchedSourceIdsIds); + tracer.end(keyValue("qmldir source path", qmldirSourcePath), keyValue("directory source path", directorySourcePath), keyValue("directory id", directoryId), @@ -788,7 +901,11 @@ void ProjectStorageUpdater::pathsWithIdsChanged(const std::vector &chan for (auto sourceContextId : directorySourceContextIds) { Utils::PathString directory = m_pathCache.sourceContextPath(sourceContextId); - updateDirectory(directory, package, notUpdatedSourceIds, watchedSourceIds); + updateDirectory(directory, + directorySourceContextIds, + package, + notUpdatedSourceIds, + watchedSourceIds); } for (SourceId sourceId : filterUniqueSourceIds(qmlDocumentSourceIds)) { @@ -867,7 +984,7 @@ void ProjectStorageUpdater::parseTypeInfos(const QStringList &typeInfos, tracer.tick("append module dependenct source source id"_t, keyValue("source id", sourceId)); package.updatedModuleDependencySourceIds.push_back(sourceId); - auto directoryInfo = package.directoryInfos.emplace_back( + const auto &directoryInfo = package.directoryInfos.emplace_back( directorySourceId, sourceId, moduleId, Storage::Synchronization::FileType::QmlTypes); tracer.tick("append project data"_t, keyValue("source id", sourceId)); @@ -898,6 +1015,8 @@ void ProjectStorageUpdater::parseDirectoryInfos( parseQmlComponent(directoryInfo.sourceId, package, notUpdatedSourceIds); break; } + case Storage::Synchronization::FileType::Directory: + break; } } } diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.h index 3cb7d24c3f6..baecbd6b11b 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.h @@ -143,9 +143,17 @@ private: WatchedSourceIdsIds &watchedSourceIdsIds); void updateDirectory(const Utils::PathString &directory, + const SourceContextIds &subdirecoriesToIgnore, Storage::Synchronization::SynchronizationPackage &package, NotUpdatedSourceIds ¬UpdatedSourceIds, WatchedSourceIdsIds &watchedSourceIdsIds); + void updateSubdirectories(const Utils::PathString &directory, + SourceId directorySourceId, + FileState directoryFileState, + const SourceContextIds &subdirecoriesToIgnore, + Storage::Synchronization::SynchronizationPackage &package, + NotUpdatedSourceIds ¬UpdatedSourceIds, + WatchedSourceIdsIds &watchedSourceIdsIds); void updateDirectoryChanged(std::string_view directoryPath, FileState qmldirState, SourcePath qmldirSourcePath, diff --git a/src/plugins/qmldesigner/designercore/projectstorage/sourcepath.h b/src/plugins/qmldesigner/designercore/projectstorage/sourcepath.h index b655c5cc345..fa550a4d52e 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/sourcepath.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/sourcepath.h @@ -7,6 +7,8 @@ #include +#include + namespace QmlDesigner { class SourcePath : public Utils::PathString @@ -128,5 +130,6 @@ private: }; using SourcePaths = std::vector; - +template +using SmallSourcePaths = QVarLengthArray; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp b/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp index 30a827b80bb..6af4d77974a 100644 --- a/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp +++ b/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp @@ -341,81 +341,32 @@ Utils::FilePath qmlPath(::ProjectExplorer::Target *target) return {}; } -template -bool skipDirectoriesWith(const QStringView directoryPath, const Path &...paths) -{ - return (directoryPath.contains(paths) || ...); -} - -template -bool skipDirectoriesEndsWith(const QStringView directoryPath, const Path &...paths) -{ - return (directoryPath.endsWith(paths) || ...); -} - -bool skipPath(const QString &directoryPath) -{ - return skipDirectoriesWith(directoryPath, - u"QtApplicationManager", - u"QtInterfaceFramework", - u"QtOpcUa", - u"Qt3D", - u"Scene2D", - u"Scene3D", - u"QtWayland", - u"Qt5Compat", - u"QtCharts", - u"QtLocation", - u"QtPositioning", - u"MaterialEditor", - u"QtTextToSpeech", - u"QtWebEngine", - u"Qt/labs", - u"QtDataVisualization") - || skipDirectoriesEndsWith(directoryPath, u"designer"); -} - -void collectQmldirPaths(const QString &path, QStringList &qmldirPaths) -{ - QDirIterator dirIterator{path, QDir::Dirs | QDir::NoDotAndDotDot, QDirIterator::Subdirectories}; - - QString rootQmldirPath = path + "/qmldir"; - if (!skipPath(path) && QFileInfo::exists(rootQmldirPath)) - qmldirPaths.push_back(path); - - while (dirIterator.hasNext()) { - auto directoryPath = dirIterator.next(); - - QString qmldirPath = directoryPath + "/qmldir"; - if (!skipPath(directoryPath) && QFileInfo::exists(qmldirPath)) - qmldirPaths.push_back(directoryPath); - } -} - [[maybe_unused]] void projectQmldirPaths(::ProjectExplorer::Target *target, QStringList &qmldirPaths) { ::QmlProjectManager::QmlBuildSystem *buildSystem = getQmlBuildSystem(target); const Utils::FilePath projectDirectoryPath = buildSystem->canonicalProjectDir(); - const QStringList importPaths = buildSystem->importPaths(); - const QDir projectDirectory(projectDirectoryPath.toString()); - for (const QString &importPath : importPaths) - collectQmldirPaths(importPath, qmldirPaths); + qmldirPaths.push_back(projectDirectoryPath.path()); } [[maybe_unused]] void qtQmldirPaths(::ProjectExplorer::Target *target, QStringList &qmldirPaths) { - if constexpr (useProjectStorage()) - collectQmldirPaths(qmlPath(target).toString(), qmldirPaths); + if constexpr (useProjectStorage()) { + auto qmlRootPath = qmlPath(target).toString(); + qmldirPaths.push_back(qmlRootPath + "/QtQml"); + qmldirPaths.push_back(qmlRootPath + "/QtQuick"); + qmldirPaths.push_back(qmlRootPath + "/QtQuick3D"); + qmldirPaths.push_back(qmlRootPath + "/Qt5Compat"); + } } [[maybe_unused]] void qtQmldirPathsForLiteDesigner(QStringList &qmldirPaths) { if constexpr (useProjectStorage()) { auto qmlRootPath = QLibraryInfo::path(QLibraryInfo::QmlImportsPath); - collectQmldirPaths(qmlRootPath + "/QtQml", qmldirPaths); - collectQmldirPaths(qmlRootPath + "/QtQuick", qmldirPaths); + qmldirPaths.push_back(qmlRootPath + "/QtQml"); + qmldirPaths.push_back(qmlRootPath + "/QtQuick"); } } diff --git a/tests/unit/tests/mocks/filesystemmock.h b/tests/unit/tests/mocks/filesystemmock.h index cb1d4df4bc4..f8544e509f6 100644 --- a/tests/unit/tests/mocks/filesystemmock.h +++ b/tests/unit/tests/mocks/filesystemmock.h @@ -20,4 +20,5 @@ public: MOCK_METHOD(QmlDesigner::FileStatus, fileStatus, (QmlDesigner::SourceId sourceId), (const, override)); MOCK_METHOD(void, remove, (const QmlDesigner::SourceIds &sourceIds), (override)); MOCK_METHOD(QString, contentAsQString, (const QString &filePath), (const, override)); + MOCK_METHOD(QStringList, subdirectories, (const QString &directoryPath), (const, override)); }; diff --git a/tests/unit/tests/mocks/projectstoragemock.h b/tests/unit/tests/mocks/projectstoragemock.h index a0e880bc09e..8d9c3381b2e 100644 --- a/tests/unit/tests/mocks/projectstoragemock.h +++ b/tests/unit/tests/mocks/projectstoragemock.h @@ -305,6 +305,16 @@ public: (QmlDesigner::SourceId sourceId), (const, override)); + MOCK_METHOD(QmlDesigner::Storage::Synchronization::DirectoryInfos, + fetchDirectoryInfos, + (QmlDesigner::SourceId sourceId, QmlDesigner::Storage::Synchronization::FileType), + (const, override)); + + MOCK_METHOD(QmlDesigner::SmallSourceIds<32>, + fetchSubdirectorySourceIds, + (QmlDesigner::SourceId sourceId), + (const, override)); + MOCK_METHOD(std::optional, fetchDirectoryInfo, (QmlDesigner::SourceId sourceId), diff --git a/tests/unit/tests/printers/gtest-creator-printing.cpp b/tests/unit/tests/printers/gtest-creator-printing.cpp index 165f54ebfbd..0cbef7b1b7f 100644 --- a/tests/unit/tests/printers/gtest-creator-printing.cpp +++ b/tests/unit/tests/printers/gtest-creator-printing.cpp @@ -754,6 +754,8 @@ const char *fileTypeToText(FileType fileType) return "QmlDocument"; case FileType::QmlTypes: return "QmlTypes"; + case FileType::Directory: + return "Directory"; } return ""; @@ -791,7 +793,7 @@ std::ostream &operator<<(std::ostream &out, const SynchronizationPackage &packag << ", updatedSourceIds: " << package.updatedSourceIds << ", fileStatuses: " << package.fileStatuses << ", updatedFileStatusSourceIds: " << package.updatedFileStatusSourceIds - << ", updatedProjectSourceIds: " << package.updatedProjectSourceIds + << ", updatedDirectoryInfoSourceIds: " << package.updatedDirectoryInfoSourceIds << ", directoryInfos: " << package.directoryInfos << ", propertyEditorQmlPaths: " << package.propertyEditorQmlPaths << ", updatedPropertyEditorQmlPathSourceIds: " diff --git a/tests/unit/tests/unittests/projectstorage/projectstorage-test.cpp b/tests/unit/tests/unittests/projectstorage/projectstorage-test.cpp index b6a56cdda08..230a6cbb21d 100644 --- a/tests/unit/tests/unittests/projectstorage/projectstorage-test.cpp +++ b/tests/unit/tests/unittests/projectstorage/projectstorage-test.cpp @@ -5317,6 +5317,49 @@ TEST_F(ProjectStorage, fetch_directory_infos_by_directory_source_id) ASSERT_THAT(directoryInfo, UnorderedElementsAre(directoryInfo1, directoryInfo2)); } +TEST_F(ProjectStorage, fetch_directory_infos_by_directory_source_id_and_file_type) +{ + Storage::Synchronization::DirectoryInfo directoryInfo1{ + qmlProjectSourceId, sourceId1, qmlModuleId, Storage::Synchronization::FileType::QmlDocument}; + Storage::Synchronization::DirectoryInfo directoryInfo2{ + qmlProjectSourceId, sourceId2, ModuleId{}, Storage::Synchronization::FileType::Directory}; + Storage::Synchronization::DirectoryInfo directoryInfo3{qtQuickProjectSourceId, + sourceId3, + qtQuickModuleId, + Storage::Synchronization::FileType::QmlTypes}; + Storage::Synchronization::DirectoryInfo directoryInfo4{ + qmlProjectSourceId, sourceId4, ModuleId{}, Storage::Synchronization::FileType::Directory}; + storage.synchronize( + SynchronizationPackage{{qmlProjectSourceId, qtQuickProjectSourceId}, + {directoryInfo1, directoryInfo2, directoryInfo3, directoryInfo4}}); + + auto directoryInfo = storage.fetchDirectoryInfos(qmlProjectSourceId, + Storage::Synchronization::FileType::Directory); + + ASSERT_THAT(directoryInfo, UnorderedElementsAre(directoryInfo2, directoryInfo4)); +} + +TEST_F(ProjectStorage, fetch_subdirectory_source_ids) +{ + Storage::Synchronization::DirectoryInfo directoryInfo1{ + qmlProjectSourceId, sourceId1, qmlModuleId, Storage::Synchronization::FileType::QmlDocument}; + Storage::Synchronization::DirectoryInfo directoryInfo2{ + qmlProjectSourceId, sourceId2, ModuleId{}, Storage::Synchronization::FileType::Directory}; + Storage::Synchronization::DirectoryInfo directoryInfo3{qtQuickProjectSourceId, + sourceId3, + qtQuickModuleId, + Storage::Synchronization::FileType::QmlTypes}; + Storage::Synchronization::DirectoryInfo directoryInfo4{ + qmlProjectSourceId, sourceId4, ModuleId{}, Storage::Synchronization::FileType::Directory}; + storage.synchronize( + SynchronizationPackage{{qmlProjectSourceId, qtQuickProjectSourceId}, + {directoryInfo1, directoryInfo2, directoryInfo3, directoryInfo4}}); + + auto directoryInfo = storage.fetchSubdirectorySourceIds(qmlProjectSourceId); + + ASSERT_THAT(directoryInfo, UnorderedElementsAre(sourceId2, sourceId4)); +} + TEST_F(ProjectStorage, fetch_directory_info_by_source_ids) { Storage::Synchronization::DirectoryInfo directoryInfo1{qmlProjectSourceId, diff --git a/tests/unit/tests/unittests/projectstorage/projectstorageupdater-test.cpp b/tests/unit/tests/unittests/projectstorage/projectstorageupdater-test.cpp index ae97c8b5abe..7ad09044c6b 100644 --- a/tests/unit/tests/unittests/projectstorage/projectstorageupdater-test.cpp +++ b/tests/unit/tests/unittests/projectstorage/projectstorageupdater-test.cpp @@ -119,8 +119,9 @@ MATCHER(PackageIsEmpty, std::string(negation ? "isn't empty" : "is empty")) return package.imports.empty() && package.types.empty() && package.fileStatuses.empty() && package.updatedSourceIds.empty() && package.directoryInfos.empty() - && package.updatedFileStatusSourceIds.empty() && package.updatedProjectSourceIds.empty() - && package.moduleDependencies.empty() && package.updatedModuleDependencySourceIds.empty() + && package.updatedFileStatusSourceIds.empty() + && package.updatedDirectoryInfoSourceIds.empty() && package.moduleDependencies.empty() + && package.updatedModuleDependencySourceIds.empty() && package.moduleExportedImports.empty() && package.updatedModuleIds.empty() && package.propertyEditorQmlPaths.empty() && package.updatedPropertyEditorQmlPathSourceIds.empty() @@ -303,6 +304,18 @@ public: EXPECT_CALL(fileSystemMock, contentAsQString(Eq(path))).WillRepeatedly(Return(content)); } + void setSubdirectoryPaths(QStringView directoryPath, const QStringList &subdirectoryPaths) + { + ON_CALL(fileSystemMock, subdirectories(Eq(directoryPath))).WillByDefault(Return(subdirectoryPaths)); + } + + void setSubdirectorySourceIds(SourceId directorySourceId, + const QmlDesigner::SmallSourceIds<32> &subdirectorySourceId) + { + ON_CALL(projectStorageMock, fetchSubdirectorySourceIds(Eq(directorySourceId))) + .WillByDefault(Return(subdirectorySourceId)); + } + auto moduleId(Utils::SmallStringView name, ModuleKind kind) const { return storage.moduleId(name, kind); @@ -430,6 +443,24 @@ TEST_F(ProjectStorageUpdater, get_content_for_qml_dir_paths_if_file_status_is_di updater.update(directories, {}, {}, {}); } +TEST_F(ProjectStorageUpdater, + get_content_for_qml_dir_paths_if_file_status_is_different_for_subdirectories) +{ + SourceId qmlDir1PathSourceId = sourcePathCache.sourceId("/path/one/qmldir"); + SourceId qmlDir2PathSourceId = sourcePathCache.sourceId("/path/two/qmldir"); + SourceId qmlDir3PathSourceId = sourcePathCache.sourceId("/path/three/qmldir"); + SourceId path3SourceId = sourcePathCache.sourceId("/path/three/."); + QStringList directories = {"/path/one"}; + setSubdirectoryPaths(u"/path/one", {"/path/two", "/path/three"}); + setFilesChanged({qmlDir1PathSourceId, qmlDir2PathSourceId}); + setFilesDontChanged({qmlDir3PathSourceId, path3SourceId}); + + EXPECT_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/one/qmldir")))); + EXPECT_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/two/qmldir")))); + + updater.update(directories, {}, {}, {}); +} + TEST_F(ProjectStorageUpdater, request_file_status_from_file_system) { EXPECT_CALL(fileSystemMock, fileStatus(Ne(directoryPathSourceId))).Times(AnyNumber()); @@ -439,6 +470,20 @@ TEST_F(ProjectStorageUpdater, request_file_status_from_file_system) updater.update(directories, {}, {}, {}); } +TEST_F(ProjectStorageUpdater, request_file_status_from_file_system_for_subdirectories) +{ + EXPECT_CALL(fileSystemMock, + fileStatus(AllOf(Ne(directoryPathSourceId), Ne(path1SourceId), Ne(path2SourceId)))) + .Times(AnyNumber()); + setSubdirectoryPaths(u"/path", {"/path/one", "/path/two"}); + + EXPECT_CALL(fileSystemMock, fileStatus(Eq(path1SourceId))); + EXPECT_CALL(fileSystemMock, fileStatus(Eq(path2SourceId))); + EXPECT_CALL(fileSystemMock, fileStatus(Eq(directoryPathSourceId))); + + updater.update(directories, {}, {}, {}); +} + TEST_F(ProjectStorageUpdater, get_content_for_qml_types) { QString qmldir{R"(module Example @@ -483,6 +528,27 @@ TEST_F(ProjectStorageUpdater, parse_qml_types) updater.update(directories, {}, {}, {}); } +TEST_F(ProjectStorageUpdater, parse_qml_types_in_subdirectories) +{ + QString qmldir{R"(module Example + typeinfo example.qmltypes + typeinfo example2.qmltypes)"}; + setContent(u"/path/qmldir", qmldir); + QString qmltypes{"Module {\ndependencies: []}"}; + QString qmltypes2{"Module {\ndependencies: [foo]}"}; + setContent(u"/path/example.qmltypes", qmltypes); + setContent(u"/path/example2.qmltypes", qmltypes2); + QStringList directories = {"/root"}; + setSubdirectoryPaths(u"/root", {"/path"}); + + EXPECT_CALL(qmlTypesParserMock, + parse(qmltypes, _, _, Field(&DirectoryInfo::moduleId, exampleCppNativeModuleId))); + EXPECT_CALL(qmlTypesParserMock, + parse(qmltypes2, _, _, Field(&DirectoryInfo::moduleId, exampleCppNativeModuleId))); + + updater.update(directories, {}, {}, {}); +} + TEST_F(ProjectStorageUpdater, synchronize_is_empty_for_no_change) { setFilesDontChanged({qmltypesPathSourceId, qmltypes2PathSourceId, qmlDirPathSourceId}); @@ -492,6 +558,23 @@ TEST_F(ProjectStorageUpdater, synchronize_is_empty_for_no_change) updater.update(directories, {}, {}, {}); } +TEST_F(ProjectStorageUpdater, synchronize_is_empty_for_no_change_in_subdirectory) +{ + SourceId qmlDirRootPathSourceId = sourcePathCache.sourceId("/root/qmldir"); + SourceId rootPathSourceId = sourcePathCache.sourceId("/root/."); + setFilesDontChanged({qmltypesPathSourceId, + qmltypes2PathSourceId, + qmlDirPathSourceId, + qmlDirRootPathSourceId, + rootPathSourceId}); + QStringList directories = {"/root"}; + setSubdirectoryPaths(u"/root", {"/path"}); + + EXPECT_CALL(projectStorageMock, synchronize(PackageIsEmpty())); + + updater.update(directories, {}, {}, {}); +} + TEST_F(ProjectStorageUpdater, synchronize_qml_types) { Storage::Import import{qmlModuleId, Storage::Version{2, 3}, qmltypesPathSourceId}; @@ -521,12 +604,83 @@ TEST_F(ProjectStorageUpdater, synchronize_qml_types) qmltypesPathSourceId, exampleCppNativeModuleId, FileType::QmlTypes))), - Field(&SynchronizationPackage::updatedProjectSourceIds, + Field(&SynchronizationPackage::updatedDirectoryInfoSourceIds, UnorderedElementsAre(directoryPathSourceId))))); updater.update(directories, {}, {}, {}); } +TEST_F(ProjectStorageUpdater, synchronize_subdircectories) +{ + QStringList directories = {"/root"}; + setSubdirectoryPaths(u"/root", {"/path/one", "/path/two"}); + setSubdirectoryPaths(u"/path/one", {"/path/three"}); + SourceId rootDirectoryPathSourceId = sourcePathCache.sourceId("/root/."); + setFilesChanged({rootDirectoryPathSourceId, path1SourceId, path2SourceId, path3SourceId}); + + EXPECT_CALL( + projectStorageMock, + synchronize(AllOf( + Field(&SynchronizationPackage::directoryInfos, + UnorderedElementsAre( + IsDirectoryInfo(rootDirectoryPathSourceId, path1SourceId, ModuleId{}, FileType::Directory), + IsDirectoryInfo(rootDirectoryPathSourceId, path2SourceId, ModuleId{}, FileType::Directory), + IsDirectoryInfo(path1SourceId, path3SourceId, ModuleId{}, FileType::Directory))), + Field(&SynchronizationPackage::updatedDirectoryInfoSourceIds, + UnorderedElementsAre( + rootDirectoryPathSourceId, path1SourceId, path2SourceId, path3SourceId))))); + + updater.update(directories, {}, {}, {}); +} + +TEST_F(ProjectStorageUpdater, synchronize_subdircectories_even_for_no_changes) +{ + QStringList directories = {"/root"}; + setSubdirectoryPaths(u"/root", {"/path/one", "/path/two"}); + setSubdirectoryPaths(u"/path/one", {"/path/three"}); + SourceId rootDirectoryPathSourceId = sourcePathCache.sourceId("/root/."); + setFilesChanged({path1SourceId, path2SourceId, path3SourceId}); + setFilesDontChanged({rootDirectoryPathSourceId}); + + EXPECT_CALL(projectStorageMock, + synchronize( + AllOf(Field(&SynchronizationPackage::directoryInfos, + UnorderedElementsAre(IsDirectoryInfo( + path1SourceId, path3SourceId, ModuleId{}, FileType::Directory))), + Field(&SynchronizationPackage::updatedDirectoryInfoSourceIds, + UnorderedElementsAre(path1SourceId, path2SourceId, path3SourceId))))); + + updater.update(directories, {}, {}, {}); +} + +TEST_F(ProjectStorageUpdater, synchronize_subdircectories_for_deleted_subdirecties) +{ + QStringList directories = {"/root"}; + setSubdirectoryPaths(u"/root", {"/path/two"}); + SourceId rootDirectoryPathSourceId = sourcePathCache.sourceId("/root/."); + setFilesChanged({rootDirectoryPathSourceId}); + setFilesDontExists({ + path1SourceId, + path3SourceId, + }); + setSubdirectorySourceIds(rootDirectoryPathSourceId, {path1SourceId, path2SourceId}); + setSubdirectorySourceIds(path1SourceId, {path3SourceId}); + + EXPECT_CALL(projectStorageMock, + synchronize(AllOf(Field(&SynchronizationPackage::directoryInfos, + UnorderedElementsAre(IsDirectoryInfo(rootDirectoryPathSourceId, + path2SourceId, + ModuleId{}, + FileType::Directory))), + Field(&SynchronizationPackage::updatedDirectoryInfoSourceIds, + UnorderedElementsAre(rootDirectoryPathSourceId, + path1SourceId, + path2SourceId, + path3SourceId))))); + + updater.update(directories, {}, {}, {}); +} + TEST_F(ProjectStorageUpdater, synchronize_qml_types_throws_if_qmltpes_does_not_exists) { Storage::Import import{qmlModuleId, Storage::Version{2, 3}, qmltypesPathSourceId}; @@ -653,7 +807,7 @@ TEST_F(ProjectStorageUpdater, synchronize_qml_documents) IsFileStatus(qmlDocumentSourceId1, 1, 21), IsFileStatus(qmlDocumentSourceId2, 1, 21), IsFileStatus(qmlDocumentSourceId3, 1, 21))), - Field(&SynchronizationPackage::updatedProjectSourceIds, + Field(&SynchronizationPackage::updatedDirectoryInfoSourceIds, UnorderedElementsAre(directoryPathSourceId)), Field(&SynchronizationPackage::directoryInfos, UnorderedElementsAre(IsDirectoryInfo(directoryPathSourceId, @@ -713,7 +867,7 @@ TEST_F(ProjectStorageUpdater, synchronize_add_only_qml_document_in_directory) Field(&SynchronizationPackage::fileStatuses, UnorderedElementsAre(IsFileStatus(qmlDocumentSourceId2, 1, 21), IsFileStatus(directoryPathSourceId, 1, 21))), - Field(&SynchronizationPackage::updatedProjectSourceIds, + Field(&SynchronizationPackage::updatedDirectoryInfoSourceIds, UnorderedElementsAre(directoryPathSourceId)), Field(&SynchronizationPackage::directoryInfos, UnorderedElementsAre(IsDirectoryInfo(directoryPathSourceId, @@ -777,7 +931,7 @@ TEST_F(ProjectStorageUpdater, synchronize_removes_qml_document) UnorderedElementsAre(qmlDirPathSourceId, qmlDocumentSourceId3)), Field(&SynchronizationPackage::fileStatuses, UnorderedElementsAre(IsFileStatus(qmlDirPathSourceId, 1, 21))), - Field(&SynchronizationPackage::updatedProjectSourceIds, + Field(&SynchronizationPackage::updatedDirectoryInfoSourceIds, UnorderedElementsAre(directoryPathSourceId)), Field(&SynchronizationPackage::directoryInfos, UnorderedElementsAre(IsDirectoryInfo(directoryPathSourceId, @@ -832,7 +986,7 @@ TEST_F(ProjectStorageUpdater, synchronize_removes_qml_document_in_qmldir_only) UnorderedElementsAre(qmlDirPathSourceId)), Field(&SynchronizationPackage::fileStatuses, UnorderedElementsAre(IsFileStatus(qmlDirPathSourceId, 1, 21))), - Field(&SynchronizationPackage::updatedProjectSourceIds, + Field(&SynchronizationPackage::updatedDirectoryInfoSourceIds, UnorderedElementsAre(directoryPathSourceId)), Field(&SynchronizationPackage::directoryInfos, UnorderedElementsAre(IsDirectoryInfo(directoryPathSourceId, @@ -890,7 +1044,7 @@ TEST_F(ProjectStorageUpdater, synchronize_add_qml_document_to_qmldir) UnorderedElementsAre(qmlDirPathSourceId)), Field(&SynchronizationPackage::fileStatuses, UnorderedElementsAre(IsFileStatus(qmlDirPathSourceId, 1, 21))), - Field(&SynchronizationPackage::updatedProjectSourceIds, + Field(&SynchronizationPackage::updatedDirectoryInfoSourceIds, UnorderedElementsAre(directoryPathSourceId)), Field(&SynchronizationPackage::directoryInfos, UnorderedElementsAre(IsDirectoryInfo(directoryPathSourceId, @@ -945,7 +1099,7 @@ TEST_F(ProjectStorageUpdater, synchronize_remove_qml_document_from_qmldir) UnorderedElementsAre(qmlDirPathSourceId)), Field(&SynchronizationPackage::fileStatuses, UnorderedElementsAre(IsFileStatus(qmlDirPathSourceId, 1, 21))), - Field(&SynchronizationPackage::updatedProjectSourceIds, + Field(&SynchronizationPackage::updatedDirectoryInfoSourceIds, UnorderedElementsAre(directoryPathSourceId)), Field(&SynchronizationPackage::directoryInfos, UnorderedElementsAre(IsDirectoryInfo(directoryPathSourceId, @@ -1011,7 +1165,7 @@ TEST_F(ProjectStorageUpdater, synchronize_qml_documents_dont_update_if_up_to_dat IsFileStatus(qmlDocumentSourceId2, 1, 21))), Field(&SynchronizationPackage::updatedFileStatusSourceIds, UnorderedElementsAre(qmlDirPathSourceId, qmlDocumentSourceId1, qmlDocumentSourceId2)), - Field(&SynchronizationPackage::updatedProjectSourceIds, + Field(&SynchronizationPackage::updatedDirectoryInfoSourceIds, UnorderedElementsAre(directoryPathSourceId)), Field(&SynchronizationPackage::directoryInfos, UnorderedElementsAre(IsDirectoryInfo(directoryPathSourceId, @@ -1193,7 +1347,7 @@ TEST_F(ProjectStorageUpdater, update_qml_types_files_is_empty) Field(&SynchronizationPackage::fileStatuses, IsEmpty()), Field(&SynchronizationPackage::updatedFileStatusSourceIds, IsEmpty()), Field(&SynchronizationPackage::directoryInfos, IsEmpty()), - Field(&SynchronizationPackage::updatedProjectSourceIds, IsEmpty())))); + Field(&SynchronizationPackage::updatedDirectoryInfoSourceIds, IsEmpty())))); updater.update({}, {}, {}, {}); } @@ -1220,7 +1374,7 @@ TEST_F(ProjectStorageUpdater, update_qml_types_files) qmltypes2PathSourceId, builtinCppNativeModuleId, FileType::QmlTypes))), - Field(&SynchronizationPackage::updatedProjectSourceIds, + Field(&SynchronizationPackage::updatedDirectoryInfoSourceIds, UnorderedElementsAre(qmltypesPathSourceId, qmltypes2PathSourceId))))); updater.update({}, {"/path/example.qmltypes", "/path/example2.qmltypes"}, {}, {}); @@ -1245,7 +1399,7 @@ TEST_F(ProjectStorageUpdater, dont_update_qml_types_files_if_unchanged) qmltypesPathSourceId, builtinCppNativeModuleId, FileType::QmlTypes))), - Field(&SynchronizationPackage::updatedProjectSourceIds, + Field(&SynchronizationPackage::updatedDirectoryInfoSourceIds, UnorderedElementsAre(qmltypesPathSourceId))))); updater.update({}, {"/path/example.qmltypes", "/path/example2.qmltypes"}, {}, {}); @@ -1283,7 +1437,7 @@ TEST_F(ProjectStorageUpdater, synchronize_qml_documents_with_different_version_b Field(&SynchronizationPackage::fileStatuses, UnorderedElementsAre(IsFileStatus(qmlDirPathSourceId, 1, 21), IsFileStatus(qmlDocumentSourceId1, 1, 21))), - Field(&SynchronizationPackage::updatedProjectSourceIds, + Field(&SynchronizationPackage::updatedDirectoryInfoSourceIds, UnorderedElementsAre(directoryPathSourceId)), Field(&SynchronizationPackage::directoryInfos, UnorderedElementsAre(IsDirectoryInfo(directoryPathSourceId, @@ -1324,7 +1478,7 @@ TEST_F(ProjectStorageUpdater, synchronize_qml_documents_with_different_type_name Field(&SynchronizationPackage::fileStatuses, UnorderedElementsAre(IsFileStatus(qmlDirPathSourceId, 1, 21), IsFileStatus(qmlDocumentSourceId1, 1, 21))), - Field(&SynchronizationPackage::updatedProjectSourceIds, + Field(&SynchronizationPackage::updatedDirectoryInfoSourceIds, UnorderedElementsAre(directoryPathSourceId)), Field(&SynchronizationPackage::directoryInfos, UnorderedElementsAre(IsDirectoryInfo(directoryPathSourceId, @@ -1926,7 +2080,7 @@ TEST_F(ProjectStorageUpdater, synchronize_qml_documents_without_qmldir) IsFileStatus(qmlDocumentSourceId1, 1, 21), IsFileStatus(qmlDocumentSourceId2, 1, 21), IsFileStatus(qmlDocumentSourceId3, 1, 21))), - Field(&SynchronizationPackage::updatedProjectSourceIds, + Field(&SynchronizationPackage::updatedDirectoryInfoSourceIds, UnorderedElementsAre(directoryPathSourceId)), Field(&SynchronizationPackage::directoryInfos, UnorderedElementsAre(IsDirectoryInfo(directoryPathSourceId, @@ -1977,7 +2131,7 @@ TEST_F(ProjectStorageUpdater, synchronize_qml_documents_without_qmldir_throws_if qmlDocumentSourceId2, qmlDocumentSourceId3)), Field(&SynchronizationPackage::fileStatuses, IsEmpty()), - Field(&SynchronizationPackage::updatedProjectSourceIds, + Field(&SynchronizationPackage::updatedDirectoryInfoSourceIds, UnorderedElementsAre(directoryPathSourceId)), Field(&SynchronizationPackage::directoryInfos, IsEmpty())))); @@ -2014,7 +2168,7 @@ TEST_F(ProjectStorageUpdater, synchronize_qml_documents_without_qmldir_add_qml_d Field(&SynchronizationPackage::fileStatuses, UnorderedElementsAre(IsFileStatus(directoryPathSourceId, 1, 21), IsFileStatus(qmlDocumentSourceId3, 1, 21))), - Field(&SynchronizationPackage::updatedProjectSourceIds, + Field(&SynchronizationPackage::updatedDirectoryInfoSourceIds, UnorderedElementsAre(directoryPathSourceId)), Field(&SynchronizationPackage::directoryInfos, UnorderedElementsAre(IsDirectoryInfo(directoryPathSourceId, @@ -2058,7 +2212,7 @@ TEST_F(ProjectStorageUpdater, synchronize_qml_documents_without_qmldir_removes_q qmlDocumentSourceId3)), Field(&SynchronizationPackage::fileStatuses, UnorderedElementsAre(IsFileStatus(directoryPathSourceId, 1, 21))), - Field(&SynchronizationPackage::updatedProjectSourceIds, + Field(&SynchronizationPackage::updatedDirectoryInfoSourceIds, UnorderedElementsAre(directoryPathSourceId)), Field(&SynchronizationPackage::directoryInfos, UnorderedElementsAre(IsDirectoryInfo(directoryPathSourceId, @@ -2126,7 +2280,7 @@ TEST_F(ProjectStorageUpdater, watcher_updates_directories) IsFileStatus(qmlDocumentSourceId1, 1, 21), IsFileStatus(qmlDocumentSourceId2, 1, 21), IsFileStatus(qmlDocumentSourceId3, 1, 21))), - Field(&SynchronizationPackage::updatedProjectSourceIds, + Field(&SynchronizationPackage::updatedDirectoryInfoSourceIds, UnorderedElementsAre(directoryPathSourceId)), Field(&SynchronizationPackage::directoryInfos, UnorderedElementsAre(IsDirectoryInfo(directoryPathSourceId, @@ -2145,6 +2299,74 @@ TEST_F(ProjectStorageUpdater, watcher_updates_directories) updater.pathsWithIdsChanged({{directoryProjectChunkId, {directoryPathSourceId}}}); } +TEST_F(ProjectStorageUpdater, watcher_updates_subdirectories) +{ + QString qmldir{R"(module Example + FirstType 1.0 First.qml + FirstType 2.2 First2.qml + SecondType 2.2 Second.qml)"}; + setContent(u"/path/qmldir", qmldir); + SourceId rootPathSourceId = sourcePathCache.sourceId("/root/."); + SourceId rootQmldirPathSourceId = sourcePathCache.sourceId("/root/qmldir"); + setFilesChanged({directoryPathSourceId, rootPathSourceId}); + setFilesDontChanged({qmlDirPathSourceId, rootQmldirPathSourceId}); + setSubdirectoryPaths(u"/root", {"/path"}); + setSubdirectorySourceIds(rootPathSourceId, {directoryPathSourceId}); + + EXPECT_CALL( + projectStorageMock, + synchronize(AllOf( + Field(&SynchronizationPackage::imports, UnorderedElementsAre(import1, import2, import3)), + Field( + &SynchronizationPackage::types, + UnorderedElementsAre( + AllOf(IsStorageType("First.qml", + Storage::Synchronization::ImportedType{"Object"}, + TypeTraitsKind::Reference, + qmlDocumentSourceId1, + Storage::Synchronization::ChangeLevel::Full), + Field(&Storage::Synchronization::Type::exportedTypes, + UnorderedElementsAre(IsExportedType(exampleModuleId, "FirstType", 1, 0), + IsExportedType(pathModuleId, "First", -1, -1)))), + AllOf(IsStorageType("First2.qml", + Storage::Synchronization::ImportedType{"Object2"}, + TypeTraitsKind::Reference, + qmlDocumentSourceId2, + Storage::Synchronization::ChangeLevel::Full), + Field(&Storage::Synchronization::Type::exportedTypes, + UnorderedElementsAre(IsExportedType(exampleModuleId, "FirstType", 2, 2), + IsExportedType(pathModuleId, "First2", -1, -1)))), + AllOf(IsStorageType("Second.qml", + Storage::Synchronization::ImportedType{"Object3"}, + TypeTraitsKind::Reference, + qmlDocumentSourceId3, + Storage::Synchronization::ChangeLevel::Full), + Field(&Storage::Synchronization::Type::exportedTypes, + UnorderedElementsAre(IsExportedType(exampleModuleId, "SecondType", 2, 2), + IsExportedType(pathModuleId, "Second", -1, -1)))))), + Field(&SynchronizationPackage::updatedSourceIds, + UnorderedElementsAre(qmlDocumentSourceId1, qmlDocumentSourceId2, qmlDocumentSourceId3)), + Field(&SynchronizationPackage::directoryInfos, + UnorderedElementsAre(IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId1, + ModuleId{}, + FileType::QmlDocument), + IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId2, + ModuleId{}, + FileType::QmlDocument), + IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId3, + ModuleId{}, + FileType::QmlDocument), + IsDirectoryInfo(rootPathSourceId, + directoryPathSourceId, + ModuleId{}, + FileType::Directory)))))); + + updater.pathsWithIdsChanged({{directoryProjectChunkId, {rootPathSourceId, directoryPathSourceId}}}); +} + TEST_F(ProjectStorageUpdater, watcher_updates_removed_directory) { setFilesRemoved({directoryPathSourceId, @@ -2173,7 +2395,7 @@ TEST_F(ProjectStorageUpdater, watcher_updates_removed_directory) qmlDocumentSourceId2, qmlDocumentSourceId3)), Field(&SynchronizationPackage::fileStatuses, UnorderedElementsAre()), - Field(&SynchronizationPackage::updatedProjectSourceIds, + Field(&SynchronizationPackage::updatedDirectoryInfoSourceIds, UnorderedElementsAre(directoryPathSourceId)), Field(&SynchronizationPackage::directoryInfos, IsEmpty())))); @@ -2275,7 +2497,7 @@ TEST_F(ProjectStorageUpdater, watcher_updates_directories_and_qmldir) IsFileStatus(qmlDocumentSourceId1, 1, 21), IsFileStatus(qmlDocumentSourceId2, 1, 21), IsFileStatus(qmlDocumentSourceId3, 1, 21))), - Field(&SynchronizationPackage::updatedProjectSourceIds, + Field(&SynchronizationPackage::updatedDirectoryInfoSourceIds, UnorderedElementsAre(directoryPathSourceId)), Field(&SynchronizationPackage::directoryInfos, UnorderedElementsAre(IsDirectoryInfo(directoryPathSourceId, @@ -2372,7 +2594,7 @@ TEST_F(ProjectStorageUpdater, watcher_updates_add_only_qml_document_in_directory Field(&SynchronizationPackage::fileStatuses, UnorderedElementsAre(IsFileStatus(qmlDocumentSourceId2, 1, 21), IsFileStatus(directoryPathSourceId, 1, 21))), - Field(&SynchronizationPackage::updatedProjectSourceIds, + Field(&SynchronizationPackage::updatedDirectoryInfoSourceIds, UnorderedElementsAre(directoryPathSourceId)), Field(&SynchronizationPackage::directoryInfos, UnorderedElementsAre(IsDirectoryInfo(directoryPathSourceId, @@ -2436,7 +2658,7 @@ TEST_F(ProjectStorageUpdater, watcher_updates_removes_qml_document) UnorderedElementsAre(qmlDirPathSourceId, qmlDocumentSourceId3)), Field(&SynchronizationPackage::fileStatuses, UnorderedElementsAre(IsFileStatus(qmlDirPathSourceId, 1, 21))), - Field(&SynchronizationPackage::updatedProjectSourceIds, + Field(&SynchronizationPackage::updatedDirectoryInfoSourceIds, UnorderedElementsAre(directoryPathSourceId)), Field(&SynchronizationPackage::directoryInfos, UnorderedElementsAre(IsDirectoryInfo(directoryPathSourceId, @@ -2491,7 +2713,7 @@ TEST_F(ProjectStorageUpdater, watcher_updates_removes_qml_document_in_qmldir_onl UnorderedElementsAre(qmlDirPathSourceId)), Field(&SynchronizationPackage::fileStatuses, UnorderedElementsAre(IsFileStatus(qmlDirPathSourceId, 1, 21))), - Field(&SynchronizationPackage::updatedProjectSourceIds, + Field(&SynchronizationPackage::updatedDirectoryInfoSourceIds, UnorderedElementsAre(directoryPathSourceId)), Field(&SynchronizationPackage::directoryInfos, UnorderedElementsAre(IsDirectoryInfo(directoryPathSourceId, @@ -2549,7 +2771,7 @@ TEST_F(ProjectStorageUpdater, watcher_updates_directories_add_qml_document_to_qm UnorderedElementsAre(qmlDirPathSourceId)), Field(&SynchronizationPackage::fileStatuses, UnorderedElementsAre(IsFileStatus(qmlDirPathSourceId, 1, 21))), - Field(&SynchronizationPackage::updatedProjectSourceIds, + Field(&SynchronizationPackage::updatedDirectoryInfoSourceIds, UnorderedElementsAre(directoryPathSourceId)), Field(&SynchronizationPackage::directoryInfos, UnorderedElementsAre(IsDirectoryInfo(directoryPathSourceId, @@ -2604,7 +2826,7 @@ TEST_F(ProjectStorageUpdater, watcher_updates_directories_remove_qml_document_fr UnorderedElementsAre(qmlDirPathSourceId)), Field(&SynchronizationPackage::fileStatuses, UnorderedElementsAre(IsFileStatus(qmlDirPathSourceId, 1, 21))), - Field(&SynchronizationPackage::updatedProjectSourceIds, + Field(&SynchronizationPackage::updatedDirectoryInfoSourceIds, UnorderedElementsAre(directoryPathSourceId)), Field(&SynchronizationPackage::directoryInfos, UnorderedElementsAre(IsDirectoryInfo(directoryPathSourceId, @@ -2670,7 +2892,7 @@ TEST_F(ProjectStorageUpdater, watcher_updates_directories_dont_update_qml_docume IsFileStatus(qmlDocumentSourceId2, 1, 21))), Field(&SynchronizationPackage::updatedFileStatusSourceIds, UnorderedElementsAre(qmlDirPathSourceId, qmlDocumentSourceId1, qmlDocumentSourceId2)), - Field(&SynchronizationPackage::updatedProjectSourceIds, + Field(&SynchronizationPackage::updatedDirectoryInfoSourceIds, UnorderedElementsAre(directoryPathSourceId)), Field(&SynchronizationPackage::directoryInfos, UnorderedElementsAre(IsDirectoryInfo(directoryPathSourceId, @@ -2740,7 +2962,7 @@ TEST_F(ProjectStorageUpdater, watcher_updates_qmldirs_dont_update_qml_documents_ IsFileStatus(qmlDocumentSourceId2, 1, 21))), Field(&SynchronizationPackage::updatedFileStatusSourceIds, UnorderedElementsAre(qmlDirPathSourceId, qmlDocumentSourceId1, qmlDocumentSourceId2)), - Field(&SynchronizationPackage::updatedProjectSourceIds, + Field(&SynchronizationPackage::updatedDirectoryInfoSourceIds, UnorderedElementsAre(directoryPathSourceId)), Field(&SynchronizationPackage::directoryInfos, UnorderedElementsAre(IsDirectoryInfo(directoryPathSourceId, @@ -3053,7 +3275,7 @@ TEST_F(ProjectStorageUpdater, watcher_updates_directories_and_but_not_included_q IsFileStatus(qmlDocumentSourceId1, 1, 21), IsFileStatus(qmlDocumentSourceId2, 1, 21), IsFileStatus(qmlDocumentSourceId3, 1, 21))), - Field(&SynchronizationPackage::updatedProjectSourceIds, + Field(&SynchronizationPackage::updatedDirectoryInfoSourceIds, UnorderedElementsAre(directoryPathSourceId)), Field(&SynchronizationPackage::directoryInfos, UnorderedElementsAre(IsDirectoryInfo(directoryPathSourceId, @@ -3130,7 +3352,7 @@ TEST_F(ProjectStorageUpdater, watcher_updates_qmldir_and_but_not_included_qml_do IsFileStatus(qmlDocumentSourceId1, 1, 21), IsFileStatus(qmlDocumentSourceId2, 1, 21), IsFileStatus(qmlDocumentSourceId3, 1, 21))), - Field(&SynchronizationPackage::updatedProjectSourceIds, + Field(&SynchronizationPackage::updatedDirectoryInfoSourceIds, UnorderedElementsAre(directoryPathSourceId)), Field(&SynchronizationPackage::directoryInfos, UnorderedElementsAre(IsDirectoryInfo(directoryPathSourceId, @@ -3229,7 +3451,7 @@ TEST_F(ProjectStorageUpdater, watcher_updates_qmldir_and_but_not_included_qmltyp IsFileStatus(qmlDocumentSourceId1, 1, 21), IsFileStatus(qmlDocumentSourceId2, 1, 21), IsFileStatus(qmlDocumentSourceId3, 1, 21))), - Field(&SynchronizationPackage::updatedProjectSourceIds, + Field(&SynchronizationPackage::updatedDirectoryInfoSourceIds, UnorderedElementsAre(directoryPathSourceId)), Field(&SynchronizationPackage::directoryInfos, UnorderedElementsAre(IsDirectoryInfo(directoryPathSourceId, From d3452545796bdf4ee87daf5b24b42ecf7d6036af Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Wed, 15 May 2024 15:33:10 +0200 Subject: [PATCH 131/154] QmlDesigner: Add project storage error mechanism Task-number: QDS-12761 Change-Id: If782b5d81b53979033e2738292dedac91e1adcbf Reviewed-by: Tim Jenssen --- src/plugins/qmldesigner/CMakeLists.txt | 2 + .../projectstorage/projectstorage.cpp | 5 ++- .../projectstorage/projectstorage.h | 6 ++- .../projectstorageerrornotifier.cpp | 17 +++++++++ .../projectstorageerrornotifier.h | 25 +++++++++++++ .../projectstorageerrornotifierinterface.h | 27 ++++++++++++++ .../qmldesigner/qmldesignerprojectmanager.cpp | 6 ++- tests/unit/tests/mocks/CMakeLists.txt | 1 + .../mocks/projectstorageerrornotifiermock.h | 16 ++++++++ .../tests/testdesignercore/CMakeLists.txt | 2 + .../projectstorage/projectstorage-test.cpp | 35 ++++++++---------- .../projectstoragepathwatcher-test.cpp | 35 ++++++++---------- .../projectstorageupdater-test.cpp | 37 +++++++++---------- .../projectstorage/qmldocumentparser-test.cpp | 5 ++- .../projectstorage/qmltypesparser-test.cpp | 5 ++- .../typeannotationreader-test.cpp | 28 +++++++------- 16 files changed, 174 insertions(+), 78 deletions(-) create mode 100644 src/plugins/qmldesigner/designercore/projectstorage/projectstorageerrornotifier.cpp create mode 100644 src/plugins/qmldesigner/designercore/projectstorage/projectstorageerrornotifier.h create mode 100644 src/plugins/qmldesigner/designercore/projectstorage/projectstorageerrornotifierinterface.h create mode 100644 tests/unit/tests/mocks/projectstorageerrornotifiermock.h diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index 08689e6f417..4e62b72369d 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -465,6 +465,8 @@ extend_qtc_library(QmlDesignerCore projectstoragetypes.h projectstorageupdater.cpp projectstorageupdater.h projectstorage.cpp projectstorage.h + projectstorageerrornotifierinterface.h + projectstorageerrornotifier.cpp projectstorageerrornotifier.h sourcepath.h sourcepathcache.h sourcepathcacheinterface.h diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.cpp b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.cpp index 3c6636f1f5f..8855c3251ca 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.cpp +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.cpp @@ -1127,8 +1127,11 @@ public: } }; -ProjectStorage::ProjectStorage(Database &database, bool isInitialized) +ProjectStorage::ProjectStorage(Database &database, + ProjectStorageErrorNotifierInterface &errorNotifier, + bool isInitialized) : database{database} + , errorNotifier{errorNotifier} , exclusiveTransaction{database} , initializer{std::make_unique(database, isInitialized)} , moduleCache{ModuleStorageAdapter{*this}} diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h index 8f69049f706..a5773448621 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h @@ -4,6 +4,7 @@ #pragma once #include "commontypecache.h" +#include "projectstorageerrornotifier.h" #include "projectstorageexceptions.h" #include "projectstorageinterface.h" #include "projectstoragetypes.h" @@ -39,7 +40,9 @@ class ProjectStorage final : public ProjectStorageInterface friend Storage::Info::CommonTypeCache; public: - ProjectStorage(Database &database, bool isInitialized); + ProjectStorage(Database &database, + ProjectStorageErrorNotifierInterface &errorNotifier, + bool isInitialized); ~ProjectStorage(); void synchronize(Storage::Synchronization::SynchronizationPackage package) override; @@ -967,6 +970,7 @@ private: public: Database &database; + ProjectStorageErrorNotifierInterface &errorNotifier; Sqlite::ExclusiveNonThrowingDestructorTransaction exclusiveTransaction; std::unique_ptr initializer; mutable ModuleCache moduleCache{ModuleStorageAdapter{*this}}; diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageerrornotifier.cpp b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageerrornotifier.cpp new file mode 100644 index 00000000000..a4705f5eecb --- /dev/null +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageerrornotifier.cpp @@ -0,0 +1,17 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "projectstorageerrornotifier.h" + +#include "sourcepathcache.h" + +namespace QmlDesigner { + +void ProjectStorageErrorNotifier::typeNameCannotBeResolved(Utils::SmallStringView typeName, + SourceId sourceId) +{ + qDebug() << "Missing type name: " << typeName + << " in file: " << m_pathCache.sourcePath(sourceId).toStringView(); +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageerrornotifier.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageerrornotifier.h new file mode 100644 index 00000000000..2695e930193 --- /dev/null +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageerrornotifier.h @@ -0,0 +1,25 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include "projectstorageerrornotifierinterface.h" + +#include + +namespace QmlDesigner { + +class ProjectStorageErrorNotifier final : public ProjectStorageErrorNotifierInterface +{ +public: + ProjectStorageErrorNotifier(PathCacheType &pathCache) + : m_pathCache{pathCache} + {} + + void typeNameCannotBeResolved(Utils::SmallStringView typeName, SourceId souceId) override; + +private: + PathCacheType &m_pathCache; +}; + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageerrornotifierinterface.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageerrornotifierinterface.h new file mode 100644 index 00000000000..8136c9d599c --- /dev/null +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageerrornotifierinterface.h @@ -0,0 +1,27 @@ +// Copyright (C) 2024 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" + +#include + +namespace QmlDesigner { + +class ProjectStorageErrorNotifierInterface +{ +public: + ProjectStorageErrorNotifierInterface() = default; + ProjectStorageErrorNotifierInterface(ProjectStorageErrorNotifierInterface &&) = default; + ProjectStorageErrorNotifierInterface &operator=(ProjectStorageErrorNotifierInterface &&) = default; + ProjectStorageErrorNotifierInterface(const ProjectStorageErrorNotifierInterface &) = delete; + ProjectStorageErrorNotifierInterface &operator=(const ProjectStorageErrorNotifierInterface &) = delete; + + virtual void typeNameCannotBeResolved(Utils::SmallStringView typeName, SourceId souceId) = 0; + +protected: + ~ProjectStorageErrorNotifierInterface() = default; +}; + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp b/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp index 6af4d77974a..730a557d120 100644 --- a/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp +++ b/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp @@ -12,15 +12,16 @@ #include #include #include +#include #include #include #include #include #include -#include #include #include #include +#include #include #include @@ -181,7 +182,8 @@ public: pathCache.sourceId(SourcePath{project->projectDirectory().toString() + "/."}).internalId())} {} Sqlite::Database database; - ProjectStorage storage{database, database.isInitialized()}; + ProjectStorageErrorNotifier errorNotifier{pathCache}; + ProjectStorage storage{database, errorNotifier, database.isInitialized()}; PathCacheType pathCache{storage}; FileSystem fileSystem{pathCache}; FileStatusCache fileStatusCache{fileSystem}; diff --git a/tests/unit/tests/mocks/CMakeLists.txt b/tests/unit/tests/mocks/CMakeLists.txt index d2090432626..0fdfa639c09 100644 --- a/tests/unit/tests/mocks/CMakeLists.txt +++ b/tests/unit/tests/mocks/CMakeLists.txt @@ -22,6 +22,7 @@ add_qtc_library(TestMocks OBJECT mocktimestampprovider.h modelresourcemanagementmock.h propertycomponentgeneratormock.h + projectstorageerrornotifiermock.h projectstoragemock.cpp projectstoragemock.h projectstorageobservermock.h diff --git a/tests/unit/tests/mocks/projectstorageerrornotifiermock.h b/tests/unit/tests/mocks/projectstorageerrornotifiermock.h new file mode 100644 index 00000000000..28443300cc1 --- /dev/null +++ b/tests/unit/tests/mocks/projectstorageerrornotifiermock.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 "../utils/googletest.h" + +#include + +class ProjectStorageErrorNotifierMock : public QmlDesigner::ProjectStorageErrorNotifierInterface +{ + MOCK_METHOD(void, + typeNameCannotBeResolved, + (Utils::SmallStringView typeName, QmlDesigner::SourceId souceId), + (override)); +}; diff --git a/tests/unit/tests/testdesignercore/CMakeLists.txt b/tests/unit/tests/testdesignercore/CMakeLists.txt index ebe7f7df120..c446af81a35 100644 --- a/tests/unit/tests/testdesignercore/CMakeLists.txt +++ b/tests/unit/tests/testdesignercore/CMakeLists.txt @@ -125,6 +125,8 @@ add_qtc_library(TestDesignerCore OBJECT projectstorage/projectstorageinterface.h projectstorage/projectstorageobserver.h projectstorage/projectstorage.cpp projectstorage/projectstorage.h + projectstorage/projectstorageerrornotifierinterface.h + projectstorage/projectstorageerrornotifier.cpp projectstorage/projectstorageerrornotifier.h projectstorage/projectstoragepathwatcher.h projectstorage/projectstoragepathwatcherinterface.h projectstorage/projectstoragepathwatchernotifierinterface.h diff --git a/tests/unit/tests/unittests/projectstorage/projectstorage-test.cpp b/tests/unit/tests/unittests/projectstorage/projectstorage-test.cpp index 230a6cbb21d..c59971fec1c 100644 --- a/tests/unit/tests/unittests/projectstorage/projectstorage-test.cpp +++ b/tests/unit/tests/unittests/projectstorage/projectstorage-test.cpp @@ -5,7 +5,8 @@ #include #include -#include +#include +#include #include #include @@ -274,21 +275,18 @@ MATCHER_P2(IsInfoType, class ProjectStorage : public testing::Test { protected: - static void SetUpTestSuite() + struct StaticData { - static_database = std::make_unique(":memory:", Sqlite::JournalMode::Memory); + Sqlite::Database database{":memory:", Sqlite::JournalMode::Memory}; + NiceMock errorNotifierMock; + QmlDesigner::ProjectStorage storage{database, errorNotifierMock, database.isInitialized()}; + }; - static_projectStorage = std::make_unique( - *static_database, static_database->isInitialized()); - } + static void SetUpTestSuite() { staticData = std::make_unique(); } - static void TearDownTestSuite() - { - static_projectStorage.reset(); - static_database.reset(); - } + static void TearDownTestSuite() { staticData.reset(); } - ~ProjectStorage() { static_projectStorage->resetForTestsOnly(); } + ~ProjectStorage() { storage.resetForTestsOnly(); } template static auto toValues(Range &&range) @@ -1141,12 +1139,11 @@ protected: } 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::SourcePathCache sourcePathCache{ - storage}; + inline static std::unique_ptr staticData; + Sqlite::Database &database = staticData->database; + QmlDesigner::ProjectStorage &storage = staticData->storage; + ProjectStorageErrorNotifierMock &errorNotifierMock = staticData->errorNotifierMock; + QmlDesigner::SourcePathCache sourcePathCache{storage}; QmlDesigner::SourcePathView path1{"/path1/to"}; QmlDesigner::SourcePathView path2{"/path2/to"}; QmlDesigner::SourcePathView path3{"/path3/to"}; @@ -5118,7 +5115,7 @@ TEST_F(ProjectStorage, populate_module_cache) { auto id = storage.moduleId("Qml", ModuleKind::QmlLibrary); - QmlDesigner::ProjectStorage newStorage{database, database.isInitialized()}; + QmlDesigner::ProjectStorage newStorage{database, errorNotifierMock, database.isInitialized()}; ASSERT_THAT(newStorage.module(id), IsModule("Qml", ModuleKind::QmlLibrary)); } diff --git a/tests/unit/tests/unittests/projectstorage/projectstoragepathwatcher-test.cpp b/tests/unit/tests/unittests/projectstorage/projectstoragepathwatcher-test.cpp index 26d5af8af8a..771107ece79 100644 --- a/tests/unit/tests/unittests/projectstorage/projectstoragepathwatcher-test.cpp +++ b/tests/unit/tests/unittests/projectstorage/projectstoragepathwatcher-test.cpp @@ -3,10 +3,11 @@ #include "../utils/googletest.h" -#include "../mocks/filesystemmock.h" -#include "../mocks/mockqfilesystemwatcher.h" -#include "../mocks/mocktimer.h" -#include "../mocks/projectstoragepathwatchernotifiermock.h" +#include +#include +#include +#include +#include #include #include @@ -39,21 +40,18 @@ using QmlDesigner::WatcherEntry; class ProjectStoragePathWatcher : public testing::Test { protected: - static void SetUpTestSuite() + struct StaticData { - static_database = std::make_unique(":memory:", Sqlite::JournalMode::Memory); + Sqlite::Database database{":memory:", Sqlite::JournalMode::Memory}; + ProjectStorageErrorNotifierMock errorNotifierMock; + QmlDesigner::ProjectStorage storage{database, errorNotifierMock, database.isInitialized()}; + }; - static_projectStorage = std::make_unique( - *static_database, static_database->isInitialized()); - } + static void SetUpTestSuite() { staticData = std::make_unique(); } - static void TearDownTestSuite() - { - static_projectStorage.reset(); - static_database.reset(); - } + static void TearDownTestSuite() { staticData.reset(); } - ~ProjectStoragePathWatcher() { static_projectStorage->resetForTestsOnly(); } + ~ProjectStoragePathWatcher() { storage.resetForTestsOnly(); } ProjectStoragePathWatcher() { @@ -79,10 +77,9 @@ protected: protected: NiceMock notifier; NiceMock mockFileSystem; - inline static std::unique_ptr static_database; - Sqlite::Database &database = *static_database; - inline static std::unique_ptr static_projectStorage; - QmlDesigner::ProjectStorage &storage = *static_projectStorage; + inline static std::unique_ptr staticData; + Sqlite::Database &database = staticData->database; + QmlDesigner::ProjectStorage &storage = staticData->storage; 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 7ad09044c6b..e38f05349be 100644 --- a/tests/unit/tests/unittests/projectstorage/projectstorageupdater-test.cpp +++ b/tests/unit/tests/unittests/projectstorage/projectstorageupdater-test.cpp @@ -3,11 +3,12 @@ #include "../utils/googletest.h" -#include "../mocks/filesystemmock.h" -#include "../mocks/projectstoragemock.h" -#include "../mocks/projectstoragepathwatchermock.h" -#include "../mocks/qmldocumentparsermock.h" -#include "../mocks/qmltypesparsermock.h" +#include +#include +#include +#include +#include +#include #include @@ -142,19 +143,16 @@ auto IsPropertyEditorQmlPath(const ModuleIdMatcher &moduleIdMatcher, class ProjectStorageUpdater : public testing::Test { public: - static void SetUpTestSuite() + struct StaticData { - static_database = std::make_unique(":memory:", Sqlite::JournalMode::Memory); + Sqlite::Database database{":memory:", Sqlite::JournalMode::Memory}; + NiceMock errorNotifierMock; + QmlDesigner::ProjectStorage storage{database, errorNotifierMock, database.isInitialized()}; + }; - static_projectStorage = std::make_unique( - *static_database, static_database->isInitialized()); - } + static void SetUpTestSuite() { staticData = std::make_unique(); } - static void TearDownTestSuite() - { - static_projectStorage.reset(); - static_database.reset(); - } + static void TearDownTestSuite() { staticData.reset(); } ProjectStorageUpdater() { @@ -228,7 +226,7 @@ public: }); } - ~ProjectStorageUpdater() { static_projectStorage->resetForTestsOnly(); } + ~ProjectStorageUpdater() { storage.resetForTestsOnly(); } void setFilesDontChanged(const QmlDesigner::SourceIds &sourceIds) { @@ -327,10 +325,9 @@ protected: NiceMock qmlTypesParserMock; NiceMock qmlDocumentParserMock; QmlDesigner::FileStatusCache fileStatusCache{fileSystemMock}; - inline static std::unique_ptr static_database; - Sqlite::Database &database = *static_database; - inline static std::unique_ptr static_projectStorage; - QmlDesigner::ProjectStorage &storage = *static_projectStorage; + inline static std::unique_ptr staticData; + Sqlite::Database &database = staticData->database; + QmlDesigner::ProjectStorage &storage = staticData->storage; QmlDesigner::SourcePathCache sourcePathCache{ storage}; NiceMock patchWatcherMock; diff --git a/tests/unit/tests/unittests/projectstorage/qmldocumentparser-test.cpp b/tests/unit/tests/unittests/projectstorage/qmldocumentparser-test.cpp index bbe42bab1df..e135bb27bd3 100644 --- a/tests/unit/tests/unittests/projectstorage/qmldocumentparser-test.cpp +++ b/tests/unit/tests/unittests/projectstorage/qmldocumentparser-test.cpp @@ -3,6 +3,8 @@ #include "../utils/googletest.h" +#include + #include #include @@ -144,7 +146,8 @@ class QmlDocumentParser : public ::testing::Test public: protected: Sqlite::Database database{":memory:", Sqlite::JournalMode::Memory}; - QmlDesigner::ProjectStorage storage{database, database.isInitialized()}; + ProjectStorageErrorNotifierMock errorNotifierMock; + QmlDesigner::ProjectStorage storage{database, errorNotifierMock, database.isInitialized()}; QmlDesigner::SourcePathCache sourcePathCache{ storage}; QmlDesigner::QmlDocumentParser parser{storage, sourcePathCache}; diff --git a/tests/unit/tests/unittests/projectstorage/qmltypesparser-test.cpp b/tests/unit/tests/unittests/projectstorage/qmltypesparser-test.cpp index aeb68cc2d6c..e75f7bf3a90 100644 --- a/tests/unit/tests/unittests/projectstorage/qmltypesparser-test.cpp +++ b/tests/unit/tests/unittests/projectstorage/qmltypesparser-test.cpp @@ -3,6 +3,8 @@ #include "../utils/googletest.h" +#include + #include #include @@ -169,7 +171,8 @@ class QmlTypesParser : public ::testing::Test public: protected: Sqlite::Database database{":memory:", Sqlite::JournalMode::Memory}; - QmlDesigner::ProjectStorage storage{database, database.isInitialized()}; + ProjectStorageErrorNotifierMock errorNotifierMock; + QmlDesigner::ProjectStorage storage{database, errorNotifierMock, database.isInitialized()}; QmlDesigner::SourcePathCache sourcePathCache{ storage}; QmlDesigner::QmlTypesParser parser{storage}; diff --git a/tests/unit/tests/unittests/projectstorage/typeannotationreader-test.cpp b/tests/unit/tests/unittests/projectstorage/typeannotationreader-test.cpp index 1a64a4980ec..d55c368f6ee 100644 --- a/tests/unit/tests/unittests/projectstorage/typeannotationreader-test.cpp +++ b/tests/unit/tests/unittests/projectstorage/typeannotationreader-test.cpp @@ -4,6 +4,7 @@ #include "../utils/googletest.h" #include +#include #include #include @@ -26,19 +27,19 @@ protected: traits.hasFormEditorItem = FlagIs::True; traits.visibleInLibrary = FlagIs::True; } - static void SetUpTestSuite() - { - static_database = std::make_unique(":memory:", Sqlite::JournalMode::Memory); - static_projectStorage = std::make_unique( - *static_database, static_database->isInitialized()); - } + ~TypeAnnotationReader() { storage.resetForTestsOnly(); } - static void TearDownTestSuite() + struct StaticData { - static_projectStorage.reset(); - static_database.reset(); - } + Sqlite::Database database{":memory:", Sqlite::JournalMode::Memory}; + ProjectStorageErrorNotifierMock errorNotifierMock; + QmlDesigner::ProjectStorage storage{database, errorNotifierMock, database.isInitialized()}; + }; + + static void SetUpTestSuite() { staticData = std::make_unique(); } + + static void TearDownTestSuite() { staticData.reset(); } auto moduleId(Utils::SmallStringView name) const { @@ -46,10 +47,9 @@ protected: } 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; + inline static std::unique_ptr staticData; + Sqlite::Database &database = staticData->database; + QmlDesigner::ProjectStorage &storage = staticData->storage; QmlDesigner::Storage::TypeAnnotationReader reader{storage}; QmlDesigner::SourceId sourceId = QmlDesigner::SourceId::create(33); QmlDesigner::SourceId directorySourceId = QmlDesigner::SourceId::create(77); From 8bfe8056459d264cf2b83de0269f7034eb2f6b2b Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Thu, 23 May 2024 12:34:57 +0300 Subject: [PATCH 132/154] QmlDesigner: Allow rotation of 3D import preview 3D import preview can now be rotated using left mouse button and dragging the preview image. This causes camera to orbit around the previewed model, similar to rotation to 3D edit view orbit camera. Close/Cancel button logic was also improved. Fixes: QDS-12795 Change-Id: I0c7d1ad28f8fe779b9bedc4bf76be704078d91a6 Reviewed-by: Mahmoud Badri --- .../interfaces/nodeinstanceglobal.h | 3 +- .../components/itemlibrary/import3dcanvas.cpp | 26 +++++++++ .../components/itemlibrary/import3dcanvas.h | 7 ++- .../itemlibraryassetimportdialog.cpp | 34 ++++++++++-- .../itemlibraryassetimportdialog.h | 3 + .../qml2puppet/editor3d/generalhelper.cpp | 23 ++++++-- .../qml2puppet/editor3d/generalhelper.h | 11 ++-- .../qml2puppet/instances/nodeinstanceserver.h | 1 + .../qt5import3dnodeinstanceserver.cpp | 55 +++++++++++++------ .../instances/qt5import3dnodeinstanceserver.h | 2 + 10 files changed, 129 insertions(+), 36 deletions(-) diff --git a/src/libs/qmlpuppetcommunication/interfaces/nodeinstanceglobal.h b/src/libs/qmlpuppetcommunication/interfaces/nodeinstanceglobal.h index 06df0f79754..206fb760956 100644 --- a/src/libs/qmlpuppetcommunication/interfaces/nodeinstanceglobal.h +++ b/src/libs/qmlpuppetcommunication/interfaces/nodeinstanceglobal.h @@ -56,7 +56,8 @@ enum class View3DActionType { EditCameraMove, EditCameraStopAllMoves, SetLastSceneEnvData, - Import3dUpdatePreviewImage + Import3dUpdatePreviewImage, + Import3dRotatePreviewModel }; constexpr bool isNanotraceEnabled() diff --git a/src/plugins/qmldesigner/components/itemlibrary/import3dcanvas.cpp b/src/plugins/qmldesigner/components/itemlibrary/import3dcanvas.cpp index 87014cbe603..608bf42eb36 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/import3dcanvas.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/import3dcanvas.cpp @@ -5,6 +5,7 @@ #include #include +#include #include namespace QmlDesigner { @@ -52,5 +53,30 @@ void Import3dCanvas::resizeEvent(QResizeEvent *) emit requestImageUpdate(); } +void Import3dCanvas::mousePressEvent(QMouseEvent *e) +{ + if (e->buttons() == Qt::LeftButton) + m_dragPos = e->position(); + else + m_dragPos = {}; +} + +void Import3dCanvas::mouseReleaseEvent(QMouseEvent *) +{ + m_dragPos = {}; +} + +void Import3dCanvas::mouseMoveEvent(QMouseEvent *e) +{ + if (m_dragPos.isNull()) + return; + + const QPointF curPos = e->position(); + const QPointF delta = curPos - m_dragPos; + + m_dragPos = curPos; + + emit requestRotation(delta); +} } diff --git a/src/plugins/qmldesigner/components/itemlibrary/import3dcanvas.h b/src/plugins/qmldesigner/components/itemlibrary/import3dcanvas.h index 22bcc04d70f..72fb19acffc 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/import3dcanvas.h +++ b/src/plugins/qmldesigner/components/itemlibrary/import3dcanvas.h @@ -4,7 +4,7 @@ #include #include -#include +#include #include namespace QmlDesigner { @@ -20,13 +20,18 @@ public: signals: void requestImageUpdate(); + void requestRotation(const QPointF &delta); protected: void paintEvent(QPaintEvent *e) override; void resizeEvent(QResizeEvent *e) override; + void mousePressEvent(QMouseEvent *e) override; + void mouseReleaseEvent(QMouseEvent *e) override; + void mouseMoveEvent(QMouseEvent *e) override; private: QImage m_image; + QPointF m_dragPos; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.cpp index 4e49cd2b807..8af81a7b82e 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.cpp @@ -221,6 +221,8 @@ ItemLibraryAssetImportDialog::ItemLibraryAssetImportDialog( this, &ItemLibraryAssetImportDialog::updateUi); connect(canvas(), &Import3dCanvas::requestImageUpdate, this, &ItemLibraryAssetImportDialog::onRequestImageUpdate); + connect(canvas(), &Import3dCanvas::requestRotation, + this, &ItemLibraryAssetImportDialog::onRequestRotation); connect(&m_importer, &ItemLibraryAssetImporter::errorReported, this, &ItemLibraryAssetImportDialog::addError); @@ -856,9 +858,10 @@ Rectangle { width: %1 height: %2 - property string sceneModelName: "%3" + property alias sceneNode: sceneNode property alias view3d: view3d property string extents + property string sceneModelName: "%3" gradient: Gradient { GradientStop { position: 1.0; color: "#222222" } @@ -877,9 +880,9 @@ Rectangle { PerspectiveCamera { id: viewCamera - z: 600 - y: 600 x: 600 + y: 600 + z: 600 eulerRotation.x: -45 eulerRotation.y: -45 clipFar: 100000 @@ -889,6 +892,10 @@ Rectangle { DirectionalLight { rotation: viewCamera.rotation } + + Node { + id: sceneNode + } } Text { @@ -1041,7 +1048,8 @@ void ItemLibraryAssetImportDialog::onImportReadyForPreview(const QString &path, ui->acceptButton->setEnabled(true); ui->importButton->setEnabled(true); - addInfo(tr("Generating import preview for %1.").arg(compName)); + addInfo(tr("Import is ready for preview.")); + addInfo(tr("Click \"Accept\" to finish the import or adjust options an click \"Import\" to import again.")); } void ItemLibraryAssetImportDialog::onRequestImageUpdate() @@ -1050,6 +1058,12 @@ void ItemLibraryAssetImportDialog::onRequestImageUpdate() m_nodeInstanceView->view3DAction(View3DActionType::Import3dUpdatePreviewImage, canvas()->size()); } +void ItemLibraryAssetImportDialog::onRequestRotation(const QPointF &delta) +{ + if (m_nodeInstanceView) + m_nodeInstanceView->view3DAction(View3DActionType::Import3dRotatePreviewModel, delta); +} + void ItemLibraryAssetImportDialog::onImportNearlyFinished() { // Canceling import is no longer doable @@ -1063,18 +1077,28 @@ void ItemLibraryAssetImportDialog::onImportFinished() QString interruptStr = tr("Import interrupted."); addError(interruptStr); setImportProgress(0, interruptStr); + if (m_explicitClose) + QTimer::singleShot(1000, this, &ItemLibraryAssetImportDialog::doClose); } else { QString doneStr = tr("Import done."); addInfo(doneStr); setImportProgress(100, doneStr); if (m_closeOnFinish) { // Add small delay to allow user to visually confirm import finishing - QTimer::singleShot(1000, this, &ItemLibraryAssetImportDialog::onClose); + QTimer::singleShot(1000, this, &ItemLibraryAssetImportDialog::doClose); } } } void ItemLibraryAssetImportDialog::onClose() +{ + ui->importButton->setEnabled(false); + ui->acceptButton->setEnabled(false); + m_explicitClose = true; + doClose(); +} + +void ItemLibraryAssetImportDialog::doClose() { if (m_importer.isImporting()) { addInfo(tr("Canceling import.")); diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.h b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.h index efbd189c2fd..635be6db696 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.h +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.h @@ -68,9 +68,11 @@ private: void setImportProgress(int value, const QString &text); void onImportReadyForPreview(const QString &path, const QString &compName); void onRequestImageUpdate(); + void onRequestRotation(const QPointF &delta); void onImportNearlyFinished(); void onImportFinished(); void onClose(); + void doClose(); void onAccept(); void toggleAdvanced(); @@ -116,5 +118,6 @@ private: OptionsData m_advancedData; bool m_advancedMode = false; int m_dialogHeight = 350; + bool m_explicitClose = false; }; } diff --git a/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp b/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp index 1ccea6049c8..3baf91c1464 100644 --- a/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp +++ b/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp @@ -462,17 +462,19 @@ QVector4D GeneralHelper::approachNode( // a selection box for bound calculations to work. This is used to focus the view for // various preview image generations, where doing things asynchronously is not good // and recalculating bounds for every frame is not a problem. -QVector3D GeneralHelper::calculateNodeBoundsAndFocusCamera( - QQuick3DCamera *camera, QQuick3DNode *node, QQuick3DViewport *viewPort, - float defaultLookAtDistance, bool closeUp) +void GeneralHelper::calculateBoundsAndFocusCamera(QQuick3DCamera *camera, QQuick3DNode *node, + QQuick3DViewport *viewPort, + float defaultLookAtDistance, + bool closeUp, QVector3D &lookAt, + QVector3D &extents) { QVector3D minBounds; QVector3D maxBounds; getBounds(viewPort, node, minBounds, maxBounds); - QVector3D extents = maxBounds - minBounds; - QVector3D lookAt = minBounds + (extents / 2.f); + extents = maxBounds - minBounds; + lookAt = minBounds + (extents / 2.f); float maxExtent = qSqrt(qreal(extents.x()) * qreal(extents.x()) + qreal(extents.y()) * qreal(extents.y()) + qreal(extents.z()) * qreal(extents.z())); @@ -506,8 +508,17 @@ QVector3D GeneralHelper::calculateNodeBoundsAndFocusCamera( perspectiveCamera->setClipFar(maxDist * 1.01); } } +} - return extents; +void GeneralHelper::calculateNodeBoundsAndFocusCamera(QQuick3DCamera *camera, QQuick3DNode *node, + QQuick3DViewport *viewPort, + float defaultLookAtDistance, + bool closeUp) +{ + QVector3D extents; + QVector3D lookAt; + calculateBoundsAndFocusCamera(camera, node, viewPort, defaultLookAtDistance, closeUp, + lookAt, extents); } // Aligns any cameras found in nodes list to a camera. diff --git a/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.h b/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.h index 996a06488f4..f1b57f4122f 100644 --- a/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.h +++ b/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.h @@ -72,11 +72,12 @@ public: bool closeUp = false); Q_INVOKABLE QVector4D approachNode(QQuick3DCamera *camera, float defaultLookAtDistance, QObject *node, QQuick3DViewport *viewPort); - Q_INVOKABLE QVector3D calculateNodeBoundsAndFocusCamera(QQuick3DCamera *camera, - QQuick3DNode *node, - QQuick3DViewport *viewPort, - float defaultLookAtDistance, - bool closeUp); + void calculateBoundsAndFocusCamera(QQuick3DCamera *camera, QQuick3DNode *node, + QQuick3DViewport *viewPort, float defaultLookAtDistance, + bool closeUp, QVector3D &lookAt, QVector3D &extents); + Q_INVOKABLE void calculateNodeBoundsAndFocusCamera(QQuick3DCamera *camera, QQuick3DNode *node, + QQuick3DViewport *viewPort, + float defaultLookAtDistance, bool closeUp); Q_INVOKABLE void alignCameras(QQuick3DCamera *camera, const QVariant &nodes); Q_INVOKABLE QVector4D alignView(QQuick3DCamera *camera, const QVariant &nodes, const QVector3D &lookAtPoint, float defaultLookAtDistance); diff --git a/src/tools/qml2puppet/qml2puppet/instances/nodeinstanceserver.h b/src/tools/qml2puppet/qml2puppet/instances/nodeinstanceserver.h index 65542dedc25..09c87b3af92 100644 --- a/src/tools/qml2puppet/qml2puppet/instances/nodeinstanceserver.h +++ b/src/tools/qml2puppet/qml2puppet/instances/nodeinstanceserver.h @@ -258,6 +258,7 @@ protected: void setRenderTimerInterval(int timerInterval); int renderTimerInterval() const; void setSlowRenderTimerInterval(int timerInterval); + TimerMode timerMode() const { return m_timerMode; } virtual void initializeView() = 0; virtual void initializeAuxiliaryViews(); diff --git a/src/tools/qml2puppet/qml2puppet/instances/qt5import3dnodeinstanceserver.cpp b/src/tools/qml2puppet/qml2puppet/instances/qt5import3dnodeinstanceserver.cpp index b5afa949ec2..5d45a881af5 100644 --- a/src/tools/qml2puppet/qml2puppet/instances/qt5import3dnodeinstanceserver.cpp +++ b/src/tools/qml2puppet/qml2puppet/instances/qt5import3dnodeinstanceserver.cpp @@ -68,6 +68,19 @@ void Qt5Import3dNodeInstanceServer::view3DAction([[maybe_unused]] const View3DAc } break; } + case View3DActionType::Import3dRotatePreviewModel: { + QObject *obj = rootItem(); + QQmlProperty sceneNodeProp(obj, "sceneNode", context()); + auto sceneNode = sceneNodeProp.read().value(); + if (sceneNode) { + QPointF delta = command.value().toPointF(); + m_generalHelper->orbitCamera(m_view3D->camera(), m_view3D->camera()->eulerRotation(), + m_lookAt, {}, {float(delta.x()), float(delta.y()), 0.f}); + m_keepRendering = true; + startRenderTimer(); + } + break; + } default: break; } @@ -75,12 +88,10 @@ void Qt5Import3dNodeInstanceServer::view3DAction([[maybe_unused]] const View3DAc void Qt5Import3dNodeInstanceServer::startRenderTimer() { - if (timerId() != 0) - killTimer(timerId()); + if (m_keepRendering && timerMode() == TimerMode::NormalTimer) + return; - int timerId = startTimer(renderTimerInterval()); - - setTimerId(timerId); + NodeInstanceServer::startRenderTimer(); } void Qt5Import3dNodeInstanceServer::cleanup() @@ -126,24 +137,28 @@ void Qt5Import3dNodeInstanceServer::render() m_view3D = qobject_cast(viewObj); if (m_view3D) { QQmlProperty sceneModelNameProp(obj, "sceneModelName", context()); - QString sceneModelName = sceneModelNameProp.read().toString(); - QFileInfo fi(fileUrl().toLocalFile()); - QString compPath = fi.absolutePath() + '/' + sceneModelName + ".qml"; - QQmlComponent comp(engine(), compPath, QQmlComponent::PreferSynchronous); - m_previewNode = qobject_cast(comp.create(context())); - if (m_previewNode) { - engine()->setObjectOwnership(m_previewNode, QJSEngine::CppOwnership); - m_previewNode->setParent(m_view3D); - m_view3D->setImportScene(m_previewNode); + QQmlProperty sceneNodeProp(obj, "sceneNode", context()); + auto sceneNode = sceneNodeProp.read().value(); + if (sceneNode) { + QString sceneModelName = sceneModelNameProp.read().toString(); + QFileInfo fi(fileUrl().toLocalFile()); + QString compPath = fi.absolutePath() + '/' + sceneModelName + ".qml"; + QQmlComponent comp(engine(), compPath, QQmlComponent::PreferSynchronous); + m_previewNode = qobject_cast(comp.create(context())); + if (m_previewNode) { + engine()->setObjectOwnership(m_previewNode, QJSEngine::CppOwnership); + m_previewNode->setParentItem(sceneNode); + m_previewNode->setParent(sceneNode); + } } } } // Render scene once to ensure geometries are intialized so bounds calculations work correctly if (m_renderCount == 2 && m_view3D) { - QVector3D extents = - m_generalHelper->calculateNodeBoundsAndFocusCamera(m_view3D->camera(), m_previewNode, - m_view3D, 1040, false); + QVector3D extents; + m_generalHelper->calculateBoundsAndFocusCamera(m_view3D->camera(), m_previewNode, + m_view3D, 1050, false, m_lookAt, extents); auto getExtentStr = [&extents](int idx) -> QString { int prec = 0; float val = extents[idx]; @@ -176,7 +191,11 @@ void Qt5Import3dNodeInstanceServer::render() nodeInstanceClient()->handlePuppetToCreatorCommand( {PuppetToCreatorCommand::Import3DPreviewImage, QVariant::fromValue(imgContainer)}); - slowDownRenderTimer(); // No more renders needed for now + + if (!m_keepRendering) + slowDownRenderTimer(); + + m_keepRendering = false; } #else slowDownRenderTimer(); diff --git a/src/tools/qml2puppet/qml2puppet/instances/qt5import3dnodeinstanceserver.h b/src/tools/qml2puppet/qml2puppet/instances/qt5import3dnodeinstanceserver.h index bc3506ea3c0..a68401ef9fd 100644 --- a/src/tools/qml2puppet/qml2puppet/instances/qt5import3dnodeinstanceserver.h +++ b/src/tools/qml2puppet/qml2puppet/instances/qt5import3dnodeinstanceserver.h @@ -35,11 +35,13 @@ private: void cleanup(); int m_renderCount = 0; + bool m_keepRendering = false; #ifdef QUICK3D_MODULE QQuick3DViewport *m_view3D = nullptr; Internal::GeneralHelper *m_generalHelper = nullptr; QQuick3DNode *m_previewNode = nullptr; + QVector3D m_lookAt; #endif }; From 2eb396b83de6582a8f34fb387c37057006cc74f1 Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Thu, 23 May 2024 11:33:45 +0300 Subject: [PATCH 133/154] QmlDesigner: Allow adding imported 3D components to content library Fixes: QDS-12784 Change-Id: I87edf8f6c14fabb066c85e8536c91b58e439e5d0 Reviewed-by: Marco Bubke Reviewed-by: Ali Kianian Reviewed-by: Miikka Heikkinen --- .../contentlibraryusermodel.cpp | 3 +- .../contentlibrary/contentlibraryview.cpp | 84 ++++++++++++++++--- .../contentlibrary/contentlibraryview.h | 1 + .../qmldesigner/designercore/uniquename.cpp | 3 + .../qmldesigner/designercore/uniquename.h | 3 +- .../tests/unittests/model/uniquename-test.cpp | 9 ++ 6 files changed, 89 insertions(+), 14 deletions(-) diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp index cfe1fcde830..f0d7506e286 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp @@ -312,8 +312,7 @@ QPair ContentLibraryUserModel::getUniqueLibItemNames(const QSt itemIcons.append(QFileInfo(obj.value("icon").toString()).baseName()); } - QString baseQml = defaultName.trimmed(); - baseQml.remove(' '); + QString baseQml = UniqueName::generateId(defaultName); baseQml[0] = baseQml.at(0).toUpper(); baseQml.prepend("My"); diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp index 0dbc3a4da71..b9f8d175863 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp @@ -23,8 +23,9 @@ #include #include #include -#include +#include #include +#include #include @@ -367,7 +368,10 @@ void ContentLibraryView::customNotification(const AbstractView *view, } else if (identifier == "add_assets_to_content_lib") { addLibAssets(data.first().toStringList()); } else if (identifier == "add_3d_to_content_lib") { - addLib3DItem(nodeList.first()); + if (nodeList.first().isComponent()) + addLib3DComponent(nodeList.first()); + else + addLib3DItem(nodeList.first()); } } @@ -663,6 +667,67 @@ void ContentLibraryView::addLibAssets(const QStringList &paths) m_widget->userModel()->addTextures(pathsInBundle); } +void ContentLibraryView::addLib3DComponent(const ModelNode &node) +{ + auto compUtils = QmlDesignerPlugin::instance()->documentManager().generatedComponentUtils(); + + // TODO: check component with existing name and show a confirmation dialog + QString compBaseName = node.simplifiedTypeName(); + QString compFileName = compBaseName + ".qml"; + + Utils::FilePath compDir = DocumentManager::currentProjectDirPath() + .pathAppended(compUtils.import3dTypePath() + '/' + compBaseName); + + auto bundlePath = Utils::FilePath::fromString(Paths::bundlesPathSetting() + "/User/3d/"); + + // generate and save icon + UniqueName::generateId(compBaseName); + QString iconPath = QLatin1String("icons/%1").arg(UniqueName::generateId(compBaseName) + ".png"); + QString fullIconPath = bundlePath.pathAppended(iconPath).toString(); + genAndSaveIcon(compDir.pathAppended(compFileName).path(), fullIconPath); + + const Utils::FilePaths sourceFiles = compDir.dirEntries({{}, QDir::Files, QDirIterator::Subdirectories}); + const QStringList ignoreList {"_importdata.json", "qmldir", compBaseName + ".hints"}; + QStringList filesList; // 3D component's assets (dependencies) + + for (const Utils::FilePath &sourcePath : sourceFiles) { + Utils::FilePath relativePath = sourcePath.relativePathFrom(compDir); + if (ignoreList.contains(sourcePath.fileName()) || relativePath.startsWith("source scene")) + continue; + + Utils::FilePath targetPath = bundlePath.pathAppended(relativePath.path()); + targetPath.parentDir().ensureWritableDir(); + + // copy item from project to user bundle + auto result = sourcePath.copyFile(targetPath); + if (!result) + qWarning() << __FUNCTION__ << result.error(); + + if (sourcePath.fileName() != compFileName) // skip component file (only collect dependencies) + filesList.append(relativePath.path()); + } + + // add the item to the bundle json + QJsonObject &jsonRef = m_widget->userModel()->bundleJson3DObjectRef(); + QJsonArray itemsArr = jsonRef.value("items").toArray(); + itemsArr.append(QJsonObject { + {"name", node.simplifiedTypeName()}, + {"qml", compFileName}, + {"icon", iconPath}, + {"files", QJsonArray::fromStringList(filesList)} + }); + + jsonRef["items"] = itemsArr; + + auto result = bundlePath.pathAppended("user_3d_bundle.json") + .writeFileContents(QJsonDocument(jsonRef).toJson()); + if (!result) + qWarning() << __FUNCTION__ << result.error(); + + m_widget->userModel()->add3DItem(compBaseName, compFileName, QUrl::fromLocalFile(fullIconPath), + filesList); +} + void ContentLibraryView::addLib3DItem(const ModelNode &node) { auto bundlePath = Utils::FilePath::fromString(Paths::bundlesPathSetting() + "/User/3d/"); @@ -682,16 +747,13 @@ void ContentLibraryView::addLib3DItem(const ModelNode &node) // add the item to the bundle json QJsonObject &jsonRef = m_widget->userModel()->bundleJson3DObjectRef(); QJsonArray itemsArr = jsonRef.value("items").toArray(); - QJsonObject itemObj; - itemObj.insert("name", name); - itemObj.insert("qml", qml); - itemObj.insert("icon", iconPath); - QJsonArray filesArr; - for (const QString &assetPath : depAssets) - filesArr.append(assetPath); - itemObj.insert("files", filesArr); + itemsArr.append(QJsonObject { + {"name", name}, + {"qml", qml}, + {"icon", iconPath}, + {"files", QJsonArray::fromStringList(depAssets)} + }); - itemsArr.append(itemObj); jsonRef["items"] = itemsArr; auto result = bundlePath.pathAppended("user_3d_bundle.json") diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.h index 2041cc37a11..914a8b8ea00 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.h +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.h @@ -57,6 +57,7 @@ private: void updateBundlesQuick3DVersion(); void addLibMaterial(const ModelNode &node, const QPixmap &iconPixmap); void addLibAssets(const QStringList &paths); + void addLib3DComponent(const ModelNode &node); void addLib3DItem(const ModelNode &node); void genAndSaveIcon(const QString &qmlPath, const QString &iconPath); QStringList writeLibItemQml(const ModelNode &node, const QString &qml); diff --git a/src/plugins/qmldesigner/designercore/uniquename.cpp b/src/plugins/qmldesigner/designercore/uniquename.cpp index 06121b326a1..d7506164dbe 100644 --- a/src/plugins/qmldesigner/designercore/uniquename.cpp +++ b/src/plugins/qmldesigner/designercore/uniquename.cpp @@ -156,6 +156,9 @@ QString generateId(const QString &id, std::function predi if (newId.at(0).isDigit() || std::binary_search(std::begin(keywords), std::end(keywords), newId)) newId.prepend('_'); + if (!predicate) + return newId; + return UniqueName::generate(newId, predicate); } diff --git a/src/plugins/qmldesigner/designercore/uniquename.h b/src/plugins/qmldesigner/designercore/uniquename.h index d264233ddd6..85927c45147 100644 --- a/src/plugins/qmldesigner/designercore/uniquename.h +++ b/src/plugins/qmldesigner/designercore/uniquename.h @@ -11,6 +11,7 @@ namespace QmlDesigner::UniqueName { QString generate(const QString &name, std::function predicate); QString generatePath(const QString &path); -QMLDESIGNERCORE_EXPORT QString generateId(const QString &id, std::function predicate); +QMLDESIGNERCORE_EXPORT QString generateId(const QString &id, + std::function predicate = {}); } // namespace QmlDesigner::UniqueName diff --git a/tests/unit/tests/unittests/model/uniquename-test.cpp b/tests/unit/tests/unittests/model/uniquename-test.cpp index a6fe4560903..ca32972b6df 100644 --- a/tests/unit/tests/unittests/model/uniquename-test.cpp +++ b/tests/unit/tests/unittests/model/uniquename-test.cpp @@ -21,6 +21,15 @@ TEST(UniqueName, generate_returns_same_input_if_predicate_returns_false) ASSERT_THAT(uniqueName, "abc"); } +TEST(UniqueName, generateId_returns_properly_formatted_id_when_predicate_is_not_provided) +{ + QString id = " A bc d _"; + + QString uniqueId = UniqueName::generateId(id); + + ASSERT_THAT(uniqueId, "aBcD_"); +} + TEST(UniqueName, generateId_returns_properly_formatted_id) { auto pred = [] (const QString &id) -> bool { From d2e4a980bf472f7ef712d1fd02c387574bdd11d2 Mon Sep 17 00:00:00 2001 From: Teea Poldsam Date: Thu, 23 May 2024 12:37:27 +0300 Subject: [PATCH 134/154] Doc: Document fly mode speed shortcuts Fixes: QDS-12664 Change-Id: I4cf001ec3f98c364323b1389e024df5f06055c59 Reviewed-by: Johanna Vanhatapio --- .../qtquick3d-editor/qtdesignstudio-3d-editor.qdoc | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-editor.qdoc b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-editor.qdoc index e691b47c770..ff67fa14a8a 100644 --- a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-editor.qdoc +++ b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-editor.qdoc @@ -158,13 +158,20 @@ \row \li \key Q or \key {Page down} \li Move down. + \row + \li \key Shift + \li Move faster. + \row + \li \key Alt + \li Move slower. \row \li \key Spacebar \li Move to an object in the crosshairs. \endtable - To adjust the movement speed, select \inlineimage icons/camera_speed.png in the - \uicontrol 3D view toolbar to open the configuration dialog. + To adjust the movement speed, right-click and hold in the \uicontrol 3D view, and rotate the + mouse wheel. Alternatively, select \inlineimage icons/camera_speed.png in the \uicontrol 3D + view toolbar to open the configuration dialog. In the configuration dialog, you can: \list From b21fe3c7b1ea2feb62a6e3815fdd2ba3347ba99a Mon Sep 17 00:00:00 2001 From: Teea Poldsam Date: Thu, 23 May 2024 14:30:12 +0300 Subject: [PATCH 135/154] Doc: Add info about live previewing in different sizes Fixes: QDS-12821 Change-Id: I4753d48ea47f92870594633a45672921ca3da031 Reviewed-by: Johanna Vanhatapio Reviewed-by: Mats Honkamaa --- doc/qtcreator/src/qtquick/qtquick-live-preview-desktop.qdoc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/doc/qtcreator/src/qtquick/qtquick-live-preview-desktop.qdoc b/doc/qtcreator/src/qtquick/qtquick-live-preview-desktop.qdoc index 2f939c69cb5..00547a61243 100644 --- a/doc/qtcreator/src/qtquick/qtquick-live-preview-desktop.qdoc +++ b/doc/qtcreator/src/qtquick/qtquick-live-preview-desktop.qdoc @@ -31,9 +31,12 @@ \uicontrol {Preview File}. \endlist - To preview the whole UI, select \uicontrol {Live Preview} + To preview the whole application, select \uicontrol {Live Preview} when viewing the main QML file of the project. + To preview the application in a different zoom level, right-click the \uicontrol {Live Preview} + button and select the zoom level. + \section1 Overriding the Preview Tool By default, the QML runtime is used for previewing. From 2a8de8ab8eb5741e4c2247c6cc6848938e0079fc Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Thu, 23 May 2024 17:54:46 +0300 Subject: [PATCH 136/154] QmlDesigner: Handle abort reason in PreviewTooltipBackend::showTooltip() Change-Id: Ib71d4e7e2e74d7c8fbcc74188a155a08d610be00 Reviewed-by: Miikka Heikkinen --- .../previewtooltip/previewtooltipbackend.cpp | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/plugins/qmldesigner/components/previewtooltip/previewtooltipbackend.cpp b/src/plugins/qmldesigner/components/previewtooltip/previewtooltipbackend.cpp index 16328ae532f..b6d5e2ae476 100644 --- a/src/plugins/qmldesigner/components/previewtooltip/previewtooltipbackend.cpp +++ b/src/plugins/qmldesigner/components/previewtooltip/previewtooltipbackend.cpp @@ -42,7 +42,18 @@ void PreviewTooltipBackend::showTooltip() } }); }, - [](auto) {}, + [&](ImageCache::AbortReason abortReason) { + if (abortReason == ImageCache::AbortReason::Abort) { + qWarning() << QLatin1String("PreviewTooltipBackend::showTooltip(): preview generation " + "failed for path %1, reason: Abort").arg(m_path); + } else if (abortReason == ImageCache::AbortReason::Failed) { + qWarning() << QLatin1String("PreviewTooltipBackend::showTooltip(): preview generation " + "failed for path %1, reason: Failed").arg(m_path); + } else if (abortReason == ImageCache::AbortReason::NoEntry) { + qWarning() << QLatin1String("PreviewTooltipBackend::showTooltip(): preview generation " + "failed for path %1, reason: NoEntry").arg(m_path); + } + }, Utils::PathString{m_extraId}, m_auxiliaryData); From 5cc9cf63b7ae57af4edbbc613bba23ca6bfd02d4 Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Fri, 24 May 2024 00:53:33 +0300 Subject: [PATCH 137/154] QmlDesigner: Confirm overwriting 3d items in content library Fixes: QDS-12826 Change-Id: I29ee10f670ba38e219375f0217f89809a496f45c Reviewed-by: Miikka Heikkinen --- .../contentlibraryusermodel.cpp | 11 ++++++ .../contentlibrary/contentlibraryusermodel.h | 2 ++ .../contentlibrary/contentlibraryview.cpp | 34 ++++++++++++++----- 3 files changed, 38 insertions(+), 9 deletions(-) diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp index f0d7506e286..2af636da6d6 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp @@ -237,6 +237,17 @@ void ContentLibraryUserModel::removeMaterialFromContentLib(ContentLibraryMateria emit dataChanged(index(MaterialsSectionIdx), index(MaterialsSectionIdx)); } +void ContentLibraryUserModel::remove3DFromContentLibByName(const QString &qmlFileName) +{ + ContentLibraryItem *itemToRemove = Utils::findOr(m_user3DItems, nullptr, + [&qmlFileName](ContentLibraryItem *item) { + return item->qml() == qmlFileName; + }); + + if (itemToRemove) + remove3DFromContentLib(itemToRemove); +} + void ContentLibraryUserModel::remove3DFromContentLib(ContentLibraryItem *item) { QJsonArray itemsArr = m_bundleObj3D.value("items").toArray(); diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.h index 67dc36b98e5..2a7f9a66f30 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.h +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.h @@ -62,6 +62,8 @@ public: void add3DInstance(ContentLibraryItem *bundleItem); + void remove3DFromContentLibByName(const QString &qmlFileName); + void setBundleObj(const QJsonObject &newBundleObj); QJsonObject &bundleJsonMaterialObjectRef(); QJsonObject &bundleJson3DObjectRef(); diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp index b9f8d175863..63e5819c132 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp @@ -40,6 +40,7 @@ #include #include #include +#include #include #include @@ -671,7 +672,6 @@ void ContentLibraryView::addLib3DComponent(const ModelNode &node) { auto compUtils = QmlDesignerPlugin::instance()->documentManager().generatedComponentUtils(); - // TODO: check component with existing name and show a confirmation dialog QString compBaseName = node.simplifiedTypeName(); QString compFileName = compBaseName + ".qml"; @@ -680,8 +680,20 @@ void ContentLibraryView::addLib3DComponent(const ModelNode &node) auto bundlePath = Utils::FilePath::fromString(Paths::bundlesPathSetting() + "/User/3d/"); + // confirm overwrite if an item with same name exists + if (bundlePath.pathAppended(compFileName).exists()) { + // Show a QML confirmation dialog before proceeding + QMessageBox::StandardButton reply = QMessageBox::question(m_widget, tr("3D Item Exists"), + tr("A 3D item with the same name '%1' already exists in the Content Library, are you sure you want to overwrite it?") + .arg(compFileName), QMessageBox::Yes | QMessageBox::No); + if (reply == QMessageBox::No) + return; + + // before overwriting remove old item (to avoid partial items and dangling assets) + m_widget->userModel()->remove3DFromContentLibByName(compFileName); + } + // generate and save icon - UniqueName::generateId(compBaseName); QString iconPath = QLatin1String("icons/%1").arg(UniqueName::generateId(compBaseName) + ".png"); QString fullIconPath = bundlePath.pathAppended(iconPath).toString(); genAndSaveIcon(compDir.pathAppended(compFileName).path(), fullIconPath); @@ -791,13 +803,17 @@ void ContentLibraryView::genAndSaveIcon(const QString &qmlPath, const QString &i else qWarning() << "ContentLibraryView::genAndSaveIcon(): icon save failed"; }, - [](ImageCache::AbortReason abortReason) { - if (abortReason == ImageCache::AbortReason::Abort) - qWarning() << "ContentLibraryView::genAndSaveIcon(): icon generation aborted, reason: Abort"; - else if (abortReason == ImageCache::AbortReason::Failed) - qWarning() << "ContentLibraryView::genAndSaveIcon(): icon generation aborted, reason: Failed"; - else if (abortReason == ImageCache::AbortReason::NoEntry) - qWarning() << "ContentLibraryView::genAndSaveIcon(): icon generation aborted, reason: NoEntry"; + [&](ImageCache::AbortReason abortReason) { + if (abortReason == ImageCache::AbortReason::Abort) { + qWarning() << QLatin1String("ContentLibraryView::genAndSaveIcon(): icon generation " + "failed for path %1, reason: Abort").arg(qmlPath); + } else if (abortReason == ImageCache::AbortReason::Failed) { + qWarning() << QLatin1String("ContentLibraryView::genAndSaveIcon(): icon generation " + "failed for path %1, reason: Failed").arg(qmlPath); + } else if (abortReason == ImageCache::AbortReason::NoEntry) { + qWarning() << QLatin1String("ContentLibraryView::genAndSaveIcon(): icon generation " + "failed for path %1, reason: NoEntry").arg(qmlPath); + } }); } From 411d74cbce981c4c4b18e96c6da5af47b0d13b48 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Thu, 23 May 2024 16:22:52 +0300 Subject: [PATCH 138/154] QmlDesigner: Improve 3D import dialog logic Got rid of the separate accept button. Now the import button turns into accept button after preview is shown, as long as user hasn't changed any options. Import preview is also generated automatically using default options. Fixes: QDS-12822 Change-Id: I5f1080f855cf1ba09e917cef7741534c0fb16d8c Reviewed-by: Mahmoud Badri --- .../itemlibraryassetimportdialog.cpp | 78 ++++++++++++------- .../itemlibraryassetimportdialog.h | 3 +- .../itemlibraryassetimportdialog.ui | 10 --- 3 files changed, 50 insertions(+), 41 deletions(-) diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.cpp index 8af81a7b82e..1cbced4dfac 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.cpp @@ -215,8 +215,6 @@ ItemLibraryAssetImportDialog::ItemLibraryAssetImportDialog( connect(ui->closeButton, &QPushButton::clicked, this, &ItemLibraryAssetImportDialog::onClose); - connect(ui->acceptButton, &QPushButton::clicked, - this, &ItemLibraryAssetImportDialog::onAccept); connect(ui->tabWidget, &QTabWidget::currentChanged, this, &ItemLibraryAssetImportDialog::updateUi); connect(canvas(), &Import3dCanvas::requestImageUpdate, @@ -239,14 +237,22 @@ ItemLibraryAssetImportDialog::ItemLibraryAssetImportDialog( connect(&m_importer, &ItemLibraryAssetImporter::importReadyForPreview, this, &ItemLibraryAssetImportDialog::onImportReadyForPreview); - addInfo(tr("Select import options and press \"Import\" to import the following files:")); - for (const auto &file : std::as_const(m_quick3DFiles)) - addInfo(file); - connect(ui->advancedSettingsButton, &QPushButton::clicked, this, &ItemLibraryAssetImportDialog::toggleAdvanced); QTimer::singleShot(0, this, &ItemLibraryAssetImportDialog::updateUi); + + if (m_quick3DFiles.size() != 1) { + addInfo(tr("Select import options and press \"Import\" to import the following files:")); + } else { + addInfo(tr("Importing:")); + QTimer::singleShot(0, this, &ItemLibraryAssetImportDialog::onImport); + } + + for (const auto &file : std::as_const(m_quick3DFiles)) + addInfo(file); + + updateImportButtonState(); } ItemLibraryAssetImportDialog::~ItemLibraryAssetImportDialog() @@ -495,6 +501,7 @@ QGridLayout *ItemLibraryAssetImportDialog::createOptionsGrid( QJsonValue value(optCheck->isChecked()); optObj.insert("value", value); m_importOptions[optionsIndex].insert(optKey, optObj); + updateImportButtonState(); }); } else { // Simple options also exist in advanced, so don't connect simple controls directly @@ -502,13 +509,17 @@ QGridLayout *ItemLibraryAssetImportDialog::createOptionsGrid( auto *advCheck = qobject_cast( m_labelToControlWidgetMaps[optionsIndex].value(optKey)); if (advCheck) { - QObject::connect(optCheck, &QCheckBox::toggled, this, [optCheck, advCheck]() { - if (advCheck->isChecked() != optCheck->isChecked()) + QObject::connect(optCheck, &QCheckBox::toggled, this, [this, optCheck, advCheck]() { + if (advCheck->isChecked() != optCheck->isChecked()) { advCheck->setChecked(optCheck->isChecked()); + updateImportButtonState(); + } }); - QObject::connect(advCheck, &QCheckBox::toggled, this, [optCheck, advCheck]() { - if (advCheck->isChecked() != optCheck->isChecked()) + QObject::connect(advCheck, &QCheckBox::toggled, this, [this, optCheck, advCheck]() { + if (advCheck->isChecked() != optCheck->isChecked()) { optCheck->setChecked(advCheck->isChecked()); + updateImportButtonState(); + } }); } } @@ -544,6 +555,7 @@ QGridLayout *ItemLibraryAssetImportDialog::createOptionsGrid( QJsonValue value(optSpin->value()); optObj.insert("value", value); m_importOptions[optionsIndex].insert(optKey, optObj); + updateImportButtonState(); }); } else { auto *advSpin = qobject_cast( @@ -551,14 +563,18 @@ QGridLayout *ItemLibraryAssetImportDialog::createOptionsGrid( if (advSpin) { // Connect corresponding advanced control QObject::connect(optSpin, &QDoubleSpinBox::valueChanged, - this, [optSpin, advSpin] { - if (advSpin->value() != optSpin->value()) + this, [this, optSpin, advSpin] { + if (advSpin->value() != optSpin->value()) { advSpin->setValue(optSpin->value()); + updateImportButtonState(); + } }); QObject::connect(advSpin, &QDoubleSpinBox::valueChanged, - this, [optSpin, advSpin] { - if (advSpin->value() != optSpin->value()) + this, [this, optSpin, advSpin] { + if (advSpin->value() != optSpin->value()) { optSpin->setValue(advSpin->value()); + updateImportButtonState(); + } }); } } @@ -994,6 +1010,11 @@ void ItemLibraryAssetImportDialog::setCloseButtonState(bool importing) ui->closeButton->setText(importing ? tr("Cancel") : tr("Close")); } +void ItemLibraryAssetImportDialog::updateImportButtonState() +{ + ui->importButton->setText(m_previewOptions == m_importOptions ? tr("Accept") : tr("Import")); +} + void ItemLibraryAssetImportDialog::addError(const QString &error, const QString &srcPath) { m_closeOnFinish = false; @@ -1013,8 +1034,14 @@ void ItemLibraryAssetImportDialog::addInfo(const QString &info, const QString &s void ItemLibraryAssetImportDialog::onImport() { - ui->acceptButton->setEnabled(false); ui->importButton->setEnabled(false); + + if (!m_previewCompName.isEmpty() && m_previewOptions == m_importOptions) { + cleanupPreviewPuppet(); + m_importer.finalizeQuick3DImport(); + return; + } + setCloseButtonState(true); ui->progressBar->setValue(0); @@ -1041,15 +1068,17 @@ void ItemLibraryAssetImportDialog::setImportProgress(int value, const QString &t void ItemLibraryAssetImportDialog::onImportReadyForPreview(const QString &path, const QString &compName) { + addInfo(tr("Import is ready for preview.")); + if (m_previewCompName.isEmpty()) + addInfo(tr("Click \"Accept\" to finish the import or adjust options and click \"Import\" to import again.")); + m_previewFile = Utils::FilePath::fromString(path).pathAppended(m_importer.previewFileName()); m_previewCompName = compName; + m_previewOptions = m_importOptions; QTimer::singleShot(0, this, &ItemLibraryAssetImportDialog::startPreview); - ui->acceptButton->setEnabled(true); ui->importButton->setEnabled(true); - - addInfo(tr("Import is ready for preview.")); - addInfo(tr("Click \"Accept\" to finish the import or adjust options an click \"Import\" to import again.")); + updateImportButtonState(); } void ItemLibraryAssetImportDialog::onRequestImageUpdate() @@ -1093,7 +1122,6 @@ void ItemLibraryAssetImportDialog::onImportFinished() void ItemLibraryAssetImportDialog::onClose() { ui->importButton->setEnabled(false); - ui->acceptButton->setEnabled(false); m_explicitClose = true; doClose(); } @@ -1113,16 +1141,6 @@ void ItemLibraryAssetImportDialog::doClose() } } -void ItemLibraryAssetImportDialog::onAccept() -{ - cleanupPreviewPuppet(); - - ui->importButton->setEnabled(false); - ui->acceptButton->setEnabled(false); - - m_importer.finalizeQuick3DImport(); -} - void ItemLibraryAssetImportDialog::toggleAdvanced() { m_advancedMode = !m_advancedMode; diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.h b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.h index 635be6db696..e7c49330561 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.h +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.h @@ -63,6 +63,7 @@ private slots: private: void setCloseButtonState(bool importing); + void updateImportButtonState(); void onImport(); void setImportProgress(int value, const QString &text); @@ -73,7 +74,6 @@ private: void onImportFinished(); void onClose(); void doClose(); - void onAccept(); void toggleAdvanced(); void createTab(const QString &tabLabel, int optionsIndex, const QJsonObject &groups); @@ -110,6 +110,7 @@ private: QString m_quick3DImportPath; ItemLibraryAssetImporter m_importer; QVector m_importOptions; + QVector m_previewOptions; QHash m_extToImportOptionsMap; QSet m_preselectedFilesForOverwrite; bool m_closeOnFinish = true; diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.ui b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.ui index a35875a5eb7..e0b9d925fca 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.ui +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.ui @@ -151,16 +151,6 @@ - - - - false - - - Accept - - - From c881408e5c8481891d07431d2087f3541724d4a4 Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Fri, 24 May 2024 12:41:58 +0300 Subject: [PATCH 139/154] QmlDesigner: Prevent adding bundle items back to content lib Change-Id: I65e2eb936fa741599ffd087f903e33a71d01de99 Reviewed-by: Miikka Heikkinen --- .../components/edit3d/edit3dwidget.cpp | 41 +++++++++++-------- 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp index b5e6e245e5e..02271518be9 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp +++ b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp @@ -2,37 +2,41 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "edit3dwidget.h" -#include "designdocument.h" -#include "designericons.h" + #include "edit3dactions.h" #include "edit3dcanvas.h" #include "edit3dtoolbarmenu.h" #include "edit3dview.h" -#include "externaldependenciesinterface.h" -#include "materialutils.h" -#include "metainfo.h" -#include "modelnodeoperations.h" -#include "nodeabstractproperty.h" -#include "nodehints.h" -#include "qmldesignerconstants.h" -#include "qmldesignerplugin.h" -#include "qmleditormenu.h" -#include "qmlvisualnode.h" -#include "viewmanager.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 #include @@ -616,6 +620,8 @@ void Edit3DWidget::showBackgroundColorMenu(bool show, const QPoint &pos) void Edit3DWidget::showContextMenu(const QPoint &pos, const ModelNode &modelNode, const QVector3D &pos3d) { + auto compUtils = QmlDesignerPlugin::instance()->documentManager().generatedComponentUtils(); + m_contextMenuTarget = modelNode; m_contextMenuPos3d = pos3d; @@ -625,6 +631,7 @@ void Edit3DWidget::showContextMenu(const QPoint &pos, const ModelNode &modelNode const bool isSingleComponent = view()->hasSingleSelectedModelNode() && modelNode.isComponent(); const bool anyNodeSelected = view()->hasSelectedModelNodes(); const bool selectionExcludingRoot = anyNodeSelected && !view()->rootModelNode().isSelected(); + const bool isInBundle = modelNode.type().startsWith(compUtils.componentBundlesTypePrefix().toLatin1()); if (m_createSubMenu) m_createSubMenu->setEnabled(!isSceneLocked()); @@ -642,7 +649,7 @@ void Edit3DWidget::showContextMenu(const QPoint &pos, const ModelNode &modelNode m_toggleGroupAction->setEnabled(true); m_bakeLightsAction->setVisible(view()->bakeLightsAction()->action()->isVisible()); m_bakeLightsAction->setEnabled(view()->bakeLightsAction()->action()->isEnabled()); - m_addToContentLibAction->setEnabled(isNode); + m_addToContentLibAction->setEnabled(isNode && !isInBundle); if (m_view) { int idx = m_view->activeSplit(); From c5d0263b32251de8bd41741b419bb5f287b0c7a8 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Fri, 24 May 2024 13:01:56 +0300 Subject: [PATCH 140/154] QmlDesigner: Add look-at gizmo to visualize 3D edit camera look-at pos Look-at gizmo can be toggled from visibility toggles submenu. Look-at gizmo is hidden when fly mode crosshairs is visible. Fixes: QDS-12767 Change-Id: I2c71b23f14e403c9774498937764607cfea3bd1d Reviewed-by: Mahmoud Badri --- .../interfaces/nodeinstanceglobal.h | 1 + .../components/edit3d/edit3dview.cpp | 19 ++++++ .../components/edit3d/edit3dview.h | 1 + .../qmldesigner/qmldesignerconstants.h | 1 + src/tools/qml2puppet/CMakeLists.txt | 1 + src/tools/qml2puppet/editor3d_qt6.qrc | 1 + .../mockfiles/qt6/EditCameraController.qml | 14 +++-- .../qml2puppet/mockfiles/qt6/EditView3D.qml | 8 +++ .../qml2puppet/mockfiles/qt6/LookAtGizmo.qml | 27 ++++++++ .../mockfiles/qt6/OverlayView3D.qml | 16 +++++ .../qml2puppet/editor3d/lookatgeometry.cpp | 62 +++++++++++++++++++ .../qml2puppet/editor3d/lookatgeometry.h | 42 +++++++++++++ .../qt5informationnodeinstanceserver.cpp | 15 +++-- 13 files changed, 198 insertions(+), 10 deletions(-) create mode 100644 src/tools/qml2puppet/mockfiles/qt6/LookAtGizmo.qml create mode 100644 src/tools/qml2puppet/qml2puppet/editor3d/lookatgeometry.cpp create mode 100644 src/tools/qml2puppet/qml2puppet/editor3d/lookatgeometry.h diff --git a/src/libs/qmlpuppetcommunication/interfaces/nodeinstanceglobal.h b/src/libs/qmlpuppetcommunication/interfaces/nodeinstanceglobal.h index 206fb760956..d248e5e6fd1 100644 --- a/src/libs/qmlpuppetcommunication/interfaces/nodeinstanceglobal.h +++ b/src/libs/qmlpuppetcommunication/interfaces/nodeinstanceglobal.h @@ -36,6 +36,7 @@ enum class View3DActionType { OrientationToggle, EditLightToggle, ShowGrid, + ShowLookAt, ShowSelectionBox, ShowIconGizmo, ShowCameraFrustum, diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp index a78ff758613..bb7404f2522 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp +++ b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp @@ -135,6 +135,7 @@ void Edit3DView::updateActiveScene3D(const QVariantMap &sceneState) const QString orientationKey = QStringLiteral("globalOrientation"); const QString editLightKey = QStringLiteral("showEditLight"); const QString gridKey = QStringLiteral("showGrid"); + const QString showLookAtKey = QStringLiteral("showLookAt"); const QString selectionBoxKey = QStringLiteral("showSelectionBox"); const QString iconGizmoKey = QStringLiteral("showIconGizmo"); const QString cameraFrustumKey = QStringLiteral("showCameraFrustum"); @@ -189,6 +190,11 @@ void Edit3DView::updateActiveScene3D(const QVariantMap &sceneState) else m_showGridAction->action()->setChecked(false); + if (sceneState.contains(showLookAtKey)) + m_showLookAtAction->action()->setChecked(sceneState[showLookAtKey].toBool()); + else + m_showLookAtAction->action()->setChecked(false); + if (sceneState.contains(selectionBoxKey)) m_showSelectionBoxAction->action()->setChecked(sceneState[selectionBoxKey].toBool()); else @@ -1040,6 +1046,18 @@ void Edit3DView::createEdit3DActions() nullptr, QCoreApplication::translate("ShowGridAction", "Toggle the visibility of the helper grid.")); + m_showLookAtAction = std::make_unique( + QmlDesigner::Constants::EDIT3D_EDIT_SHOW_LOOKAT, + View3DActionType::ShowLookAt, + QCoreApplication::translate("ShowLookAtAction", "Show Look-at"), + QKeySequence(Qt::Key_L), + true, + true, + QIcon(), + this, + nullptr, + QCoreApplication::translate("ShowLookAtAction", "Toggle the visibility of the edit camera look-at indicator.")); + m_showSelectionBoxAction = std::make_unique( QmlDesigner::Constants::EDIT3D_EDIT_SHOW_SELECTION_BOX, View3DActionType::ShowSelectionBox, @@ -1343,6 +1361,7 @@ void Edit3DView::createEdit3DActions() m_rightActions << m_resetAction.get(); m_visibilityToggleActions << m_showGridAction.get(); + m_visibilityToggleActions << m_showLookAtAction.get(); m_visibilityToggleActions << m_showSelectionBoxAction.get(); m_visibilityToggleActions << m_showIconGizmoAction.get(); m_visibilityToggleActions << m_showCameraFrustumAction.get(); diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dview.h b/src/plugins/qmldesigner/components/edit3d/edit3dview.h index a76607f003e..755efc0ae38 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dview.h +++ b/src/plugins/qmldesigner/components/edit3d/edit3dview.h @@ -159,6 +159,7 @@ private: std::unique_ptr m_orientationModeAction; std::unique_ptr m_editLightAction; std::unique_ptr m_showGridAction; + std::unique_ptr m_showLookAtAction; std::unique_ptr m_showSelectionBoxAction; std::unique_ptr m_showIconGizmoAction; std::unique_ptr m_showCameraFrustumAction; diff --git a/src/plugins/qmldesigner/qmldesignerconstants.h b/src/plugins/qmldesigner/qmldesignerconstants.h index 537e65c5b41..c0d69bef656 100644 --- a/src/plugins/qmldesigner/qmldesignerconstants.h +++ b/src/plugins/qmldesigner/qmldesignerconstants.h @@ -50,6 +50,7 @@ inline constexpr char EDIT3D_EDIT_CAMERA[] = "QmlDesigner.Editor3D.EditCameraTog inline constexpr char EDIT3D_ORIENTATION[] = "QmlDesigner.Editor3D.OrientationToggle"; inline constexpr char EDIT3D_EDIT_LIGHT[] = "QmlDesigner.Editor3D.EditLightToggle"; inline constexpr char EDIT3D_EDIT_SHOW_GRID[] = "QmlDesigner.Editor3D.ToggleGrid"; +inline constexpr char EDIT3D_EDIT_SHOW_LOOKAT[] = "QmlDesigner.Editor3D.ToggleLookAt"; inline constexpr char EDIT3D_EDIT_SELECT_BACKGROUND_COLOR[] = "QmlDesigner.Editor3D.SelectBackgroundColor"; inline constexpr char EDIT3D_EDIT_SELECT_GRID_COLOR[] = "QmlDesigner.Editor3D.SelectGridColor"; diff --git a/src/tools/qml2puppet/CMakeLists.txt b/src/tools/qml2puppet/CMakeLists.txt index 26fd0b6dd21..a4e3e44174e 100644 --- a/src/tools/qml2puppet/CMakeLists.txt +++ b/src/tools/qml2puppet/CMakeLists.txt @@ -93,6 +93,7 @@ extend_qtc_executable(qml2puppet linegeometry.cpp linegeometry.h icongizmoimageprovider.cpp icongizmoimageprovider.h boxgeometry.cpp boxgeometry.h + lookatgeometry.cpp lookatgeometry.h ) find_package(Qt6 COMPONENTS Quick3DAssetImport QUIET) diff --git a/src/tools/qml2puppet/editor3d_qt6.qrc b/src/tools/qml2puppet/editor3d_qt6.qrc index c437bad9adf..85df241f162 100644 --- a/src/tools/qml2puppet/editor3d_qt6.qrc +++ b/src/tools/qml2puppet/editor3d_qt6.qrc @@ -37,6 +37,7 @@ mockfiles/qt6/LightIconGizmo.qml mockfiles/qt6/LightModel.qml mockfiles/qt6/Line3D.qml + mockfiles/qt6/LookAtGizmo.qml mockfiles/qt6/MaterialNodeView.qml mockfiles/qt6/ModelNode2DImageView.qml mockfiles/qt6/ModelNode3DImageView.qml diff --git a/src/tools/qml2puppet/mockfiles/qt6/EditCameraController.qml b/src/tools/qml2puppet/mockfiles/qt6/EditCameraController.qml index 31e9a7cfaca..36d67c534d1 100644 --- a/src/tools/qml2puppet/mockfiles/qt6/EditCameraController.qml +++ b/src/tools/qml2puppet/mockfiles/qt6/EditCameraController.qml @@ -29,7 +29,7 @@ Item { readonly property real _keyPanAmount: 1.0 property bool ignoreToolState: false property bool flyMode: viewRoot.flyMode - property bool hasMovedInFlyMode: false + property bool showCrosshairs: false z: 10 anchors.fill: parent @@ -175,14 +175,14 @@ Item { function rotateCamera(angles) { if (flyMode) - hasMovedInFlyMode = true; + showCrosshairs = true; cameraCtrl._lookAtPoint = _generalHelper.rotateCamera(camera, angles, _lookAtPoint); } function moveCamera(moveVec) { if (flyMode) - hasMovedInFlyMode = true; + showCrosshairs = true; cameraCtrl._lookAtPoint = _generalHelper.moveCamera(camera, _lookAtPoint, moveVec); } @@ -248,11 +248,15 @@ Item { cameraCtrl._dragging = false; cameraCtrl.storeCameraState(0); } - hasMovedInFlyMode = false; + showCrosshairs = false; _generalHelper.stopAllCameraMoves(); _generalHelper.setCameraSpeedModifier(1.0); } + on_LookAtPointChanged: { + viewRoot.overlayViews[splitId].lookAtGizmo.position = _lookAtPoint; + } + Connections { target: _generalHelper enabled: viewRoot.activeSplit === cameraCtrl.splitId @@ -268,7 +272,7 @@ Item { Image { anchors.centerIn: parent source: "qrc:///qtquickplugin/mockfiles/images/crosshair.png" - visible: cameraCtrl.hasMovedInFlyMode && viewRoot.activeSplit === cameraCtrl.splitId + visible: cameraCtrl.showCrosshairs && viewRoot.activeSplit === cameraCtrl.splitId opacity: 0.7 } diff --git a/src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml b/src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml index c4dc15ab5eb..751d21c0e8a 100644 --- a/src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml +++ b/src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml @@ -25,6 +25,7 @@ Item { property bool showEditLight: false property bool showGrid: true + property bool showLookAt: true property bool showSelectionBox: true property bool showIconGizmo: true property bool showCameraFrustum: false @@ -66,6 +67,7 @@ Item { onShowEditLightChanged: _generalHelper.storeToolState(sceneId, "showEditLight", showEditLight) onGlobalOrientationChanged: _generalHelper.storeToolState(sceneId, "globalOrientation", globalOrientation) onShowGridChanged: _generalHelper.storeToolState(sceneId, "showGrid", showGrid); + onShowLookAtChanged: _generalHelper.storeToolState(sceneId, "showLookAt", showLookAt); onSyncEnvBackgroundChanged: _generalHelper.storeToolState(sceneId, "syncEnvBackground", syncEnvBackground); onShowSelectionBoxChanged: _generalHelper.storeToolState(sceneId, "showSelectionBox", showSelectionBox); onShowIconGizmoChanged: _generalHelper.storeToolState(sceneId, "showIconGizmo", showIconGizmo); @@ -304,6 +306,11 @@ Item { else if (resetToDefault) showGrid = true; + if ("showLookAt" in toolStates) + showLookAt = toolStates.showLookAt; + else if (resetToDefault) + showLookAt = true; + if ("syncEnvBackground" in toolStates) { syncEnvBackground = toolStates.syncEnvBackground; updateEnvBackground(); @@ -393,6 +400,7 @@ Item { { _generalHelper.storeToolState(sceneId, "showEditLight", showEditLight) _generalHelper.storeToolState(sceneId, "showGrid", showGrid) + _generalHelper.storeToolState(sceneId, "showLookAt", showLookAt) _generalHelper.storeToolState(sceneId, "syncEnvBackground", syncEnvBackground) _generalHelper.storeToolState(sceneId, "showSelectionBox", showSelectionBox) _generalHelper.storeToolState(sceneId, "showIconGizmo", showIconGizmo) diff --git a/src/tools/qml2puppet/mockfiles/qt6/LookAtGizmo.qml b/src/tools/qml2puppet/mockfiles/qt6/LookAtGizmo.qml new file mode 100644 index 00000000000..71527d9fb3d --- /dev/null +++ b/src/tools/qml2puppet/mockfiles/qt6/LookAtGizmo.qml @@ -0,0 +1,27 @@ +// Copyright (C) 2024 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 LookAtGeometry + +Node { + id: root + + property alias crossScale: lookAtGeometry.crossScale + property alias color: lookAtMat.baseColor + + Model { + readonly property bool _edit3dLocked: true // Make this non-pickable + geometry: LookAtGeometry { + id: lookAtGeometry + } + materials: [ + PrincipledMaterial { + id: lookAtMat + lighting: DefaultMaterial.NoLighting + cullMode: Material.NoCulling + } + ] + } +} diff --git a/src/tools/qml2puppet/mockfiles/qt6/OverlayView3D.qml b/src/tools/qml2puppet/mockfiles/qt6/OverlayView3D.qml index 71f2250b7ca..464e0b6b79c 100644 --- a/src/tools/qml2puppet/mockfiles/qt6/OverlayView3D.qml +++ b/src/tools/qml2puppet/mockfiles/qt6/OverlayView3D.qml @@ -12,6 +12,7 @@ View3D { property alias rotateGizmo: rotateGizmo property alias scaleGizmo: scaleGizmo property alias lightGizmo: lightGizmo + property alias lookAtGizmo: lookAtGizmo property var viewRoot: null property View3D editView: null @@ -484,6 +485,12 @@ View3D { position: pivotLine.startPos } + AutoScaleHelper { + id: lookAtAutoScale + view3D: overlayView + position: lookAtGizmo.scenePosition + } + MoveGizmo { id: moveGizmo scale: autoScale.getScale(Qt.vector3d(5, 5, 5)) @@ -619,5 +626,14 @@ View3D { ] } } + + LookAtGizmo { + id: lookAtGizmo + color: "#ddd600" + scale: lookAtAutoScale.getScale(Qt.vector3d(10, 10, 10)) + visible: overlayView.viewRoot.showLookAt + && overlayView.isActive + && !overlayView.viewRoot.cameraControls[viewRoot.activeSplit].showCrosshairs + } } } diff --git a/src/tools/qml2puppet/qml2puppet/editor3d/lookatgeometry.cpp b/src/tools/qml2puppet/qml2puppet/editor3d/lookatgeometry.cpp new file mode 100644 index 00000000000..b569f6ea5ab --- /dev/null +++ b/src/tools/qml2puppet/qml2puppet/editor3d/lookatgeometry.cpp @@ -0,0 +1,62 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#ifdef QUICK3D_MODULE + +#include "lookatgeometry.h" + +namespace QmlDesigner::Internal { + +LookAtGeometry::LookAtGeometry() + : GeometryBase() + , m_crossScale(1.f, 1.f, 1.f) +{ +} + +LookAtGeometry::~LookAtGeometry() +{ +} + +QVector3D LookAtGeometry::crossScale() const +{ + return m_crossScale; +} + +void LookAtGeometry::setCrossScale(const QVector3D &scale) +{ + if (scale != m_crossScale) { + m_crossScale = scale; + emit crossScaleChanged(); + updateGeometry(); + } +} + +void LookAtGeometry::doUpdateGeometry() +{ + GeometryBase::doUpdateGeometry(); + + QByteArray vertexData; + vertexData.resize(6 * 3 * 4); // 6 vertices of 3 floats each 4 bytes + float *dataPtr = reinterpret_cast(vertexData.data()); + + auto addVertex = [&dataPtr](float x, float y, float z) { + dataPtr[0] = x; + dataPtr[1] = y; + dataPtr[2] = z; + dataPtr += 3; + }; + + addVertex(m_crossScale.x(), 0.f, 0.f); + addVertex(-m_crossScale.x(), 0.f, 0.f); + addVertex(0.f, m_crossScale.y(), 0.f); + addVertex(0.f, -m_crossScale.y(), 0.f); + addVertex(0.f, 0.f, m_crossScale.z()); + addVertex(0.f, 0.f, -m_crossScale.z()); + + setVertexData(vertexData); + setBounds(-m_crossScale, m_crossScale); +} + +} // namespace QmlDesigner::Internal + +#endif // QUICK3D_MODULE diff --git a/src/tools/qml2puppet/qml2puppet/editor3d/lookatgeometry.h b/src/tools/qml2puppet/qml2puppet/editor3d/lookatgeometry.h new file mode 100644 index 00000000000..e881c2750e1 --- /dev/null +++ b/src/tools/qml2puppet/qml2puppet/editor3d/lookatgeometry.h @@ -0,0 +1,42 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#ifdef QUICK3D_MODULE + +#include "geometrybase.h" + +#include + +namespace QmlDesigner::Internal { + +class LookAtGeometry : public GeometryBase +{ + Q_OBJECT + Q_PROPERTY(QVector3D crossScale READ crossScale WRITE setCrossScale NOTIFY crossScaleChanged) + +public: + LookAtGeometry(); + ~LookAtGeometry() override; + + QVector3D crossScale() const; + +public slots: + void setCrossScale(const QVector3D &pos); + +signals: + void crossScaleChanged(); + +protected: + void doUpdateGeometry() override; + +private: + QVector3D m_crossScale; +}; + +} // namespace QmlDesigner::Internal + +QML_DECLARE_TYPE(QmlDesigner::Internal::LookAtGeometry) + +#endif // QUICK3D_MODULE diff --git a/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp b/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp index d4353fc8f23..9549f59d3f3 100644 --- a/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp +++ b/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp @@ -41,14 +41,15 @@ #include "changeauxiliarycommand.h" #include "../editor3d/boxgeometry.h" -#include "../editor3d/generalhelper.h" -#include "../editor3d/mousearea3d.h" #include "../editor3d/camerageometry.h" -#include "../editor3d/lightgeometry.h" +#include "../editor3d/generalhelper.h" #include "../editor3d/gridgeometry.h" -#include "../editor3d/selectionboxgeometry.h" -#include "../editor3d/linegeometry.h" #include "../editor3d/icongizmoimageprovider.h" +#include "../editor3d/lightgeometry.h" +#include "../editor3d/linegeometry.h" +#include "../editor3d/lookatgeometry.h" +#include "../editor3d/mousearea3d.h" +#include "../editor3d/selectionboxgeometry.h" #include #include @@ -526,6 +527,7 @@ void Qt5InformationNodeInstanceServer::createEditView3D() qmlRegisterType("SelectionBoxGeometry", 1, 0, "SelectionBoxGeometry"); qmlRegisterType("LineGeometry", 1, 0, "LineGeometry"); qmlRegisterType("BoxGeometry", 1, 0, "BoxGeometry"); + qmlRegisterType("LookAtGeometry", 1, 0, "LookAtGeometry"); auto helper = new QmlDesigner::Internal::GeneralHelper(); QObject::connect(helper, &QmlDesigner::Internal::GeneralHelper::toolStateChanged, @@ -2533,6 +2535,9 @@ void Qt5InformationNodeInstanceServer::view3DAction(const View3DActionCommand &c case View3DActionType::ShowGrid: updatedToolState.insert("showGrid", command.isEnabled()); break; + case View3DActionType::ShowLookAt: + updatedToolState.insert("showLookAt", command.isEnabled()); + break; case View3DActionType::ShowSelectionBox: updatedToolState.insert("showSelectionBox", command.isEnabled()); break; From 7724da302794937314c41911e64723854c58b8f8 Mon Sep 17 00:00:00 2001 From: Teea Poldsam Date: Mon, 27 May 2024 09:19:35 +0300 Subject: [PATCH 141/154] Doc: Add topics to Best Practices section Fixes: QDS-12756 Change-Id: I34db98abb6c1b30e0df94440711ff1979552a25d Reviewed-by: Mats Honkamaa --- doc/qtdesignstudio/src/best-practices.qdoc | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/doc/qtdesignstudio/src/best-practices.qdoc b/doc/qtdesignstudio/src/best-practices.qdoc index 91df948f560..4abcc80f6af 100644 --- a/doc/qtdesignstudio/src/best-practices.qdoc +++ b/doc/qtdesignstudio/src/best-practices.qdoc @@ -13,4 +13,26 @@ \list \li \l {Creating Glow and Bloom Effects} \endlist + + \section1 Performance + + \list + \li \l {Optimizing Designs} + \li \l {QML Performance Considerations And Suggestions} + \li \l {Creating Optimized 3D Scenes} + \li \l {Using Components Economically} + \endlist + + \section1 Projects + + \list + \li \l {Converting Qt 5 Projects into Qt 6 Projects} + \li \l {Converting UI Projects to Applications} + \endlist + + \section1 Workflow + + \list + \li \l {Designer-Developer Workflow} + \endlist */ From 72b3f2a09042be7ba377c0c36bdbb960b190eedd Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Fri, 24 May 2024 15:42:11 +0300 Subject: [PATCH 142/154] EffectComposer: Simplify temporary file handling QTemporaryFile was used just to generate file names for shader files, and the actual files didn't get removed at shutdown. There was also a possibility of reloading effect quickly, which left additional dangling compiled qsb files because they are generated asynchronously with a separate process. Now temporary shader files are gathered into one temporary directory, which removes itself at shutdown, so dangling files won't be an issue. Fixes: QDS-12711 Change-Id: I7ce682acb1a0d8d58b84129c4e0442c8fb63ac2f Reviewed-by: Mahmoud Badri --- .../effectcomposer/effectcomposermodel.cpp | 58 +++++++++---------- .../effectcomposer/effectcomposermodel.h | 7 +-- 2 files changed, 31 insertions(+), 34 deletions(-) diff --git a/src/plugins/effectcomposer/effectcomposermodel.cpp b/src/plugins/effectcomposer/effectcomposermodel.cpp index a983072334b..f90b450e7a4 100644 --- a/src/plugins/effectcomposer/effectcomposermodel.cpp +++ b/src/plugins/effectcomposer/effectcomposermodel.cpp @@ -21,6 +21,7 @@ #include #include +#include #include namespace EffectComposer { @@ -48,6 +49,7 @@ static bool writeToFile(const QByteArray &buf, const QString &filename, FileType EffectComposerModel::EffectComposerModel(QObject *parent) : QAbstractListModel{parent} + , m_shaderDir(QDir::tempPath() + "/qds_ec_XXXXXX") { m_rebakeTimer.setSingleShot(true); connect(&m_rebakeTimer, &QTimer::timeout, this, &EffectComposerModel::bakeShaders); @@ -1713,37 +1715,33 @@ void EffectComposerModel::updateCustomUniforms() m_exportedEffectPropertiesString = exportedEffectPropertiesString; } -void EffectComposerModel::createFiles() +void EffectComposerModel::initShaderDir() { - if (QFileInfo::exists(m_vertexShaderFilename)) - QFile(m_vertexShaderFilename).remove(); - if (QFileInfo::exists(m_fragmentShaderFilename)) - QFile(m_fragmentShaderFilename).remove(); - if (QFileInfo::exists(m_vertexShaderPreviewFilename)) - QFile(m_vertexShaderPreviewFilename).remove(); - if (QFileInfo::exists(m_fragmentShaderPreviewFilename)) - QFile(m_fragmentShaderPreviewFilename).remove(); + static int count = 0; + static const QString fileNameTemplate = "%1_%2.%3"; + const QString countStr = QString::number(count); - auto vertexShaderFile = QTemporaryFile(QDir::tempPath() + "/dsem_XXXXXX.vert.qsb"); - auto fragmentShaderFile = QTemporaryFile(QDir::tempPath() + "/dsem_XXXXXX.frag.qsb"); - auto vertexShaderPreviewFile = QTemporaryFile(QDir::tempPath() + "/dsem_prev_XXXXXX.vert.qsb"); - auto fragmentShaderPreviewFile = QTemporaryFile(QDir::tempPath() + "/dsem_prev_XXXXXX.frag.qsb"); + auto resetFile = [&countStr, this](QString &fileName, const QString prefix, const QString suffix) { + // qsb generation is done in separate process, so it is not guaranteed all of the old files + // get deleted here, as they may not exist yet. Any dangling files will be deleted at + // application shutdown, when the temporary directory is destroyed. + if (!fileName.isEmpty()) { + QFile file(fileName); + if (file.exists()) + file.remove(); + } - m_vertexSourceFile.setFileTemplate(QDir::tempPath() + "/dsem_XXXXXX.vert"); - m_fragmentSourceFile.setFileTemplate(QDir::tempPath() + "/dsem_XXXXXX.frag"); + fileName = m_shaderDir.filePath(fileNameTemplate.arg(prefix, countStr, suffix)); + }; - if (!m_vertexSourceFile.open() || !m_fragmentSourceFile.open() - || !vertexShaderFile.open() || !fragmentShaderFile.open() - || !vertexShaderPreviewFile.open() || !fragmentShaderPreviewFile.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(); - m_vertexShaderPreviewFilename = vertexShaderPreviewFile.fileName(); - m_fragmentShaderPreviewFilename = fragmentShaderPreviewFile.fileName(); - } + resetFile(m_vertexSourceFilename, "source", "vert"); + resetFile(m_fragmentSourceFilename, "source", "frag"); + resetFile(m_vertexShaderFilename, "compiled", "vert.qsb"); + resetFile(m_fragmentShaderFilename, "compiled", "frag.qsb"); + resetFile(m_vertexShaderPreviewFilename, "compiled_prev", "vert.qsb"); + resetFile(m_fragmentShaderPreviewFilename, "compiled_prev", "frag.qsb"); + + ++count; } void EffectComposerModel::bakeShaders() @@ -1756,7 +1754,7 @@ void EffectComposerModel::bakeShaders() return; } - createFiles(); + initShaderDir(); resetEffectError(ErrorPreprocessor); if (m_vertexShader == generateVertexShader() && m_fragmentShader == generateFragmentShader()) { @@ -1775,11 +1773,11 @@ void EffectComposerModel::bakeShaders() setVertexShader(generateVertexShader()); QString vs = m_vertexShader; - writeToFile(vs.toUtf8(), m_vertexSourceFile.fileName(), FileType::Text); + writeToFile(vs.toUtf8(), m_vertexSourceFilename, FileType::Text); setFragmentShader(generateFragmentShader()); QString fs = m_fragmentShader; - writeToFile(fs.toUtf8(), m_fragmentSourceFile.fileName(), FileType::Text); + writeToFile(fs.toUtf8(), m_fragmentSourceFilename, FileType::Text); QtSupport::QtVersion *qtVer = QtSupport::QtKitAspect::qtVersion(target->kit()); if (!qtVer) { diff --git a/src/plugins/effectcomposer/effectcomposermodel.h b/src/plugins/effectcomposer/effectcomposermodel.h index 14ef09e8a97..b377ae06180 100644 --- a/src/plugins/effectcomposer/effectcomposermodel.h +++ b/src/plugins/effectcomposer/effectcomposermodel.h @@ -12,7 +12,7 @@ #include #include #include -#include +#include #include namespace ProjectExplorer { @@ -176,7 +176,7 @@ private: QString getQmlEffectString(); void updateCustomUniforms(); - void createFiles(); + void initShaderDir(); void bakeShaders(); void saveResources(const QString &name); @@ -205,8 +205,7 @@ private: QStringList m_defaultRootVertexShader; QStringList m_defaultRootFragmentShader; // Temp files to store shaders sources and binary data - QTemporaryFile m_fragmentSourceFile; - QTemporaryFile m_vertexSourceFile; + QTemporaryDir m_shaderDir; QString m_fragmentSourceFilename; QString m_vertexSourceFilename; QString m_fragmentShaderFilename; From 1cd3667dcea2cfb7e033a8d7c8c6e41bd3daaa6e Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Mon, 27 May 2024 12:22:08 +0300 Subject: [PATCH 143/154] EffectComposer: Fix channel property saving Channel properties were saved as regular int properties to .qep files, making the property control incorrect once you reopened it in effect composer. Fixes: QDS-12835 Change-Id: I833763f385e64420bc5f1a1da9266869e739d3be Reviewed-by: Mahmoud Badri --- src/plugins/effectcomposer/effectcomposermodel.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/plugins/effectcomposer/effectcomposermodel.cpp b/src/plugins/effectcomposer/effectcomposermodel.cpp index f90b450e7a4..43efefe51d6 100644 --- a/src/plugins/effectcomposer/effectcomposermodel.cpp +++ b/src/plugins/effectcomposer/effectcomposermodel.cpp @@ -518,11 +518,9 @@ QJsonObject nodeToJson(const CompositionNode &node) uniformObject.insert("type", type); - if (uniform->type() == Uniform::Type::Define || uniform->type() == Uniform::Type::Channel) { - QString controlType = Uniform::stringFromType(uniform->controlType()); - if (controlType != type) - uniformObject.insert("controlType", controlType); - } + QString controlType = Uniform::stringFromType(uniform->controlType()); + if (controlType != type) + uniformObject.insert("controlType", controlType); if (!uniform->displayName().isEmpty()) uniformObject.insert("displayName", QString(uniform->displayName())); @@ -544,7 +542,8 @@ QJsonObject nodeToJson(const CompositionNode &node) if (!uniform->description().isEmpty()) uniformObject.insert("description", uniform->description()); if (uniform->type() == Uniform::Type::Float - || uniform->type() == Uniform::Type::Int + || (uniform->type() == Uniform::Type::Int + && uniform->controlType() != Uniform::Type::Channel) || uniform->type() == Uniform::Type::Vec2 || uniform->type() == Uniform::Type::Vec3 || uniform->type() == Uniform::Type::Vec4 From 10657d4a1f2c7d62c07ccbb25540866d047a12bb Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Thu, 23 May 2024 16:19:01 +0200 Subject: [PATCH 144/154] QmlDesigner: Return const & fileUrl Change-Id: I80cf03c53692d5a0593fdc63009df715d8d5e2a5 Reviewed-by: Tim Jenssen --- src/plugins/qmldesigner/designercore/include/model.h | 2 +- src/plugins/qmldesigner/designercore/model/model.cpp | 4 ++-- src/plugins/qmldesigner/designercore/model/model_p.h | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/plugins/qmldesigner/designercore/include/model.h b/src/plugins/qmldesigner/designercore/include/model.h index bb45456def8..39b5cdaa811 100644 --- a/src/plugins/qmldesigner/designercore/include/model.h +++ b/src/plugins/qmldesigner/designercore/include/model.h @@ -137,7 +137,7 @@ public: ModelPointer createModel(const TypeName &typeName, std::unique_ptr resourceManagement = {}); - QUrl fileUrl() const; + const QUrl &fileUrl() const; SourceId fileUrlSourceId() const; void setFileUrl(const QUrl &url); diff --git a/src/plugins/qmldesigner/designercore/model/model.cpp b/src/plugins/qmldesigner/designercore/model/model.cpp index 75b9dfd1bb2..d4eea263785 100644 --- a/src/plugins/qmldesigner/designercore/model/model.cpp +++ b/src/plugins/qmldesigner/designercore/model/model.cpp @@ -243,7 +243,7 @@ void ModelPrivate::notifyUsedImportsChanged(const Imports &usedImports) } } -QUrl ModelPrivate::fileUrl() const +const QUrl &ModelPrivate::fileUrl() const { return m_fileUrl; } @@ -2046,7 +2046,7 @@ void Model::clearMetaInfoCache() \brief Returns the URL against which relative URLs within the model should be resolved. \return The base URL. */ -QUrl Model::fileUrl() const +const QUrl &Model::fileUrl() const { return d->fileUrl(); } diff --git a/src/plugins/qmldesigner/designercore/model/model_p.h b/src/plugins/qmldesigner/designercore/model/model_p.h index a3e972f329f..cb082fd1d70 100644 --- a/src/plugins/qmldesigner/designercore/model/model_p.h +++ b/src/plugins/qmldesigner/designercore/model/model_p.h @@ -122,7 +122,7 @@ public: ModelPrivate(const ModelPrivate &) = delete; ModelPrivate &operator=(const ModelPrivate &) = delete; - QUrl fileUrl() const; + const QUrl &fileUrl() const; void setFileUrl(const QUrl &url); InternalNodePointer createNode(const TypeName &typeName, From bcf0594419e07fea37fdeff2a005bd58c6fd338f Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Mon, 27 May 2024 20:52:21 +0300 Subject: [PATCH 145/154] QmlDesigner: Use id if name is empty when adding a comp to user bundle Change-Id: I64082413603310bc84d2b711e9e209fc990d1734 Reviewed-by: Miikka Heikkinen --- .../components/contentlibrary/contentlibraryview.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp index 63e5819c132..1287e246d13 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp @@ -748,6 +748,9 @@ void ContentLibraryView::addLib3DItem(const ModelNode &node) auto [qml, icon] = m_widget->userModel()->getUniqueLib3DNames(node.id()); QString iconPath = QLatin1String("icons/%1").arg(icon); + if (name.isEmpty()) + name = node.id(); + // generate and save item Qml file const QStringList depAssets = writeLibItemQml(node, qml); From b8322aece2eef2c14c95319355e2991cc92f230d Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Thu, 16 May 2024 18:17:21 +0200 Subject: [PATCH 146/154] QmlDesigner: Use error notifier for prototype and extension type name resolving If the prototype or extension has an unresolved id, it needs to be checked, if an exported name belonging to the prototype or extension was updated. In that case the id has to be again resolved. Task-number: QDS-12761 Change-Id: I7a733662cf37e13e8c2db53dec5a4f3e0a9b6ecf Reviewed-by: Tim Jenssen --- src/libs/sqlite/sqlitebasestatement.h | 9 +- src/libs/sqlite/sqliteids.h | 33 +- .../projectstorage/projectstorage.cpp | 355 +++++++++++-- .../projectstorage/projectstorage.h | 75 +-- .../mocks/projectstorageerrornotifiermock.h | 1 + .../tests/printers/gtest-creator-printing.cpp | 5 +- .../projectstorage/projectstorage-test.cpp | 493 ++++++++++++++++-- .../unittests/sqlite/sqlitestatement-test.cpp | 15 + 8 files changed, 846 insertions(+), 140 deletions(-) diff --git a/src/libs/sqlite/sqlitebasestatement.h b/src/libs/sqlite/sqlitebasestatement.h index 3710021ff53..9a4bb0a2a2f 100644 --- a/src/libs/sqlite/sqlitebasestatement.h +++ b/src/libs/sqlite/sqlitebasestatement.h @@ -36,13 +36,6 @@ class DatabaseBackend; enum class Type : char { Invalid, Integer, Float, Text, Blob, Null }; -template -constexpr static std::underlying_type_t to_underlying(Enumeration enumeration) noexcept -{ - static_assert(std::is_enum_v, "to_underlying expect an enumeration"); - return static_cast>(enumeration); -} - class SQLITE_EXPORT BaseStatement { public: @@ -87,7 +80,7 @@ public: template> void bind(int index, Type id) { - if (id) + if (!id.isNull()) bind(index, id.internalId()); else bindNull(index); diff --git a/src/libs/sqlite/sqliteids.h b/src/libs/sqlite/sqliteids.h index 1ffd546d9f2..460cd91415a 100644 --- a/src/libs/sqlite/sqliteids.h +++ b/src/libs/sqlite/sqliteids.h @@ -11,6 +11,13 @@ namespace Sqlite { +template +static constexpr std::underlying_type_t to_underlying(Enumeration enumeration) noexcept +{ + static_assert(std::is_enum_v, "to_underlying expect an enumeration"); + return static_cast>(enumeration); +} + template class BasicId { @@ -27,7 +34,15 @@ public: return id; } - constexpr friend bool compareInvalidAreTrue(BasicId first, BasicId second) + template + static constexpr BasicId createSpecialState(Enumeration specialState) + { + BasicId id; + id.id = ::Sqlite::to_underlying(specialState); + return id; + } + + friend constexpr bool compareInvalidAreTrue(BasicId first, BasicId second) { return first.id == second.id; } @@ -57,6 +72,14 @@ public: constexpr bool isValid() const { return id > 0; } + constexpr bool isNull() const { return id == 0; } + + template + constexpr bool hasSpecialState(Enumeration specialState) const + { + return id == ::Sqlite::to_underlying(specialState); + } + explicit operator bool() const { return isValid(); } explicit operator std::size_t() const { return static_cast(id); } @@ -68,13 +91,13 @@ public: template friend void convertToString(String &string, BasicId id) { - if (id.isValid()) - NanotraceHR::convertToString(string, id.internalId()); + if (id.isNull()) + NanotraceHR::convertToString(string, "invalid null"); else - NanotraceHR::convertToString(string, "invalid"); + NanotraceHR::convertToString(string, id.internalId()); } -private: +protected: InternalIntegerType id = 0; }; diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.cpp b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.cpp index 8855c3251ca..2f56d569087 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.cpp +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.cpp @@ -7,6 +7,25 @@ namespace QmlDesigner { +enum class SpecialIdState { Unresolved = -1 }; + +constexpr TypeId unresolvedTypeId = TypeId::createSpecialState(SpecialIdState::Unresolved); + +class UnresolvedTypeId : public TypeId +{ +public: + constexpr UnresolvedTypeId() + : TypeId{TypeId::createSpecialState(SpecialIdState::Unresolved)} + {} + + static constexpr UnresolvedTypeId create(DatabaseType idNumber) + { + UnresolvedTypeId id; + id.id = idNumber; + return id; + } +}; + struct ProjectStorage::Statements { Statements(Sqlite::Database &database) @@ -17,9 +36,13 @@ struct ProjectStorage::Statements Sqlite::ReadWriteStatement<1, 2> insertTypeStatement{ "INSERT OR IGNORE INTO types(sourceId, name) VALUES(?1, ?2) RETURNING typeId", database}; Sqlite::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 " - "IS NOT ?4 OR extensionNameId IS NOT ?5)", + "UPDATE types " + "SET prototypeId=?2, prototypeNameId=?3, extensionId=?4, extensionNameId=?5 " + "WHERE typeId=?1 AND ( " + " prototypeId IS NOT ?2 " + " OR extensionId IS NOT ?3 " + " OR prototypeId IS NOT ?4 " + " OR extensionNameId IS NOT ?5)", database}; mutable Sqlite::ReadStatement<1, 1> selectTypeIdByExportedNameStatement{ "SELECT typeId FROM exportedTypeNames WHERE name=?1", database}; @@ -357,13 +380,51 @@ struct ProjectStorage::Statements "SELECT name FROM propertyDeclarations WHERE propertyDeclarationId=?", database}; Sqlite::WriteStatement<2> updatePropertyDeclarationTypeStatement{ "UPDATE propertyDeclarations SET propertyTypeId=?2 WHERE propertyDeclarationId=?1", database}; - Sqlite::ReadWriteStatement<2, 1> updatePrototypeIdToNullStatement{ - "UPDATE types SET prototypeId=NULL WHERE prototypeId=?1 RETURNING " - "typeId, prototypeNameId", + Sqlite::ReadWriteStatement<2, 2> updatePrototypeIdToTypeIdStatement{ + "UPDATE types " + "SET prototypeId=?2 " + "WHERE prototypeId=?1 " + "RETURNING typeId, prototypeNameId", database}; - Sqlite::ReadWriteStatement<2, 1> updateExtensionIdToNullStatement{ - "UPDATE types SET extensionId=NULL WHERE extensionId=?1 RETURNING " - "typeId, extensionNameId", + Sqlite::ReadWriteStatement<2, 2> updateExtensionIdToTypeIdStatement{ + "UPDATE types " + "SET extensionId=?2 " + "WHERE extensionId=?1 " + "RETURNING typeId, extensionNameId", + database}; + Sqlite::ReadStatement<2, 2> selectTypeIdAndPrototypeNameIdForPrototypeIdAndTypeNameStatement{ + "SELECT typeId, prototypeNameId " + "FROM types " + "WHERE prototypeNameId IN ( " + " SELECT importedTypeNameId " + " FROM " + " importedTypeNames WHERE name=?1) " + " AND prototypeId=?2", + database}; + Sqlite::ReadStatement<2, 2> selectTypeIdAndPrototypeNameIdForPrototypeIdAndSourceIdStatement{ + "SELECT typeId , prototypeNameId " + "FROM types " + "WHERE prototypeId=?1 AND sourceId=?2", + database}; + Sqlite::ReadStatement<2, 2> selectTypeIdAndExtensionNameIdForExtensionIdAndSourceIdStatement{ + "SELECT typeId, extensionNameId " + "FROM types " + "WHERE extensionId=?1 AND sourceId=?2", + database}; + Sqlite::ReadWriteStatement<3, 3> updatePrototypeIdAndExtensionIdToTypeIdForSourceIdStatement{ + "UPDATE types " + "SET prototypeId=?2, extensionId=?3 " + "WHERE sourceId=?1 " + "RETURNING typeId, prototypeNameId, extensionNameId", + database}; + Sqlite::ReadStatement<2, 2> selectTypeIdForExtensionIdAndTypeNameStatement{ + "SELECT typeId , prototypeNameId " + "FROM types " + "WHERE extensionNameId IN ( " + " SELECT importedTypeNameId " + " FROM importedTypeNames " + " WHERE name=?1) " + " AND extensionId=?2", database}; Sqlite::WriteStatement<2> updateTypePrototypeStatement{ "UPDATE types SET prototypeId=?2 WHERE typeId=?1", database}; @@ -619,6 +680,8 @@ struct ProjectStorage::Statements "UPDATE types SET defaultPropertyId=NULL WHERE defaultPropertyId=?1", database}; mutable Sqlite::ReadStatement<3, 1> selectInfoTypeByTypeIdStatement{ "SELECT sourceId, traits, annotationTraits FROM types WHERE typeId=?", database}; + mutable Sqlite::ReadStatement<1, 1> selectSourceIdByTypeIdStatement{ + "SELECT sourceId FROM types WHERE typeId=?", database}; mutable Sqlite::ReadStatement<1, 1> selectPrototypeAnnotationTraitsByTypeIdStatement{ "SELECT annotationTraits " "FROM types " @@ -801,23 +864,23 @@ public: auto &sourceIdColumn = typesTable.addColumn("sourceId", Sqlite::StrictColumnType::Integer); auto &typesNameColumn = typesTable.addColumn("name", Sqlite::StrictColumnType::Text); typesTable.addColumn("traits", Sqlite::StrictColumnType::Integer); - auto &prototypeIdColumn = typesTable.addForeignKeyColumn("prototypeId", - typesTable, - Sqlite::ForeignKeyAction::NoAction, - Sqlite::ForeignKeyAction::Restrict); - typesTable.addColumn("prototypeNameId", Sqlite::StrictColumnType::Integer); - auto &extensionIdColumn = typesTable.addForeignKeyColumn("extensionId", - typesTable, - Sqlite::ForeignKeyAction::NoAction, - Sqlite::ForeignKeyAction::Restrict); - typesTable.addColumn("extensionNameId", Sqlite::StrictColumnType::Integer); + auto &prototypeIdColumn = typesTable.addColumn("prototypeId", + Sqlite::StrictColumnType::Integer); + auto &prototypeNameIdColumn = typesTable.addColumn("prototypeNameId", + Sqlite::StrictColumnType::Integer); + auto &extensionIdColumn = typesTable.addColumn("extensionId", + Sqlite::StrictColumnType::Integer); + auto &extensionNameIdColumn = 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.addIndex({prototypeIdColumn, sourceIdColumn}); + typesTable.addIndex({extensionIdColumn, sourceIdColumn}); + typesTable.addIndex({prototypeNameIdColumn}); + typesTable.addIndex({extensionNameIdColumn}); typesTable.initialize(database); @@ -1131,7 +1194,7 @@ ProjectStorage::ProjectStorage(Database &database, ProjectStorageErrorNotifierInterface &errorNotifier, bool isInitialized) : database{database} - , errorNotifier{errorNotifier} + , errorNotifier{&errorNotifier} , exclusiveTransaction{database} , initializer{std::make_unique(database, isInitialized)} , moduleCache{ModuleStorageAdapter{*this}} @@ -1175,7 +1238,9 @@ void ProjectStorage::synchronize(Storage::Synchronization::SynchronizationPackag package.moduleDependencies, package.updatedModuleDependencySourceIds, package.moduleExportedImports, - package.updatedModuleIds); + package.updatedModuleIds, + relinkablePrototypes, + relinkableExtensions); synchronizeTypes(package.types, updatedTypeIds, insertedAliasPropertyDeclarations, @@ -1223,7 +1288,24 @@ void ProjectStorage::synchronizeDocumentImports(Storage::Imports imports, Source keyValue("source id", sourceId)}; Sqlite::withImmediateTransaction(database, [&] { - synchronizeDocumentImports(imports, {sourceId}, Storage::Synchronization::ImportKind::Import); + AliasPropertyDeclarations relinkableAliasPropertyDeclarations; + PropertyDeclarations relinkablePropertyDeclarations; + Prototypes relinkablePrototypes; + Prototypes relinkableExtensions; + TypeIds deletedTypeIds; + + synchronizeDocumentImports(imports, + {sourceId}, + Storage::Synchronization::ImportKind::Import, + Relink::Yes, + relinkablePrototypes, + relinkableExtensions); + + relink(relinkableAliasPropertyDeclarations, + relinkablePropertyDeclarations, + relinkablePrototypes, + relinkableExtensions, + deletedTypeIds); }); } @@ -2642,19 +2724,29 @@ void ProjectStorage::synchronizeImports(Storage::Imports &imports, Storage::Imports &moduleDependencies, const SourceIds &updatedModuleDependencySourceIds, Storage::Synchronization::ModuleExportedImports &moduleExportedImports, - const ModuleIds &updatedModuleIds) + const ModuleIds &updatedModuleIds, + Prototypes &relinkablePrototypes, + Prototypes &relinkableExtensions) { NanotraceHR::Tracer tracer{"synchronize imports"_t, projectStorageCategory()}; synchromizeModuleExportedImports(moduleExportedImports, updatedModuleIds); NanotraceHR::Tracer importTracer{"synchronize qml document imports"_t, projectStorageCategory()}; - synchronizeDocumentImports(imports, updatedSourceIds, Storage::Synchronization::ImportKind::Import); + synchronizeDocumentImports(imports, + updatedSourceIds, + Storage::Synchronization::ImportKind::Import, + Relink::No, + relinkablePrototypes, + relinkableExtensions); importTracer.end(); NanotraceHR::Tracer moduleDependenciesTracer{"synchronize module depdencies"_t, projectStorageCategory()}; synchronizeDocumentImports(moduleDependencies, updatedModuleDependencySourceIds, - Storage::Synchronization::ImportKind::ModuleDependency); + Storage::Synchronization::ImportKind::ModuleDependency, + Relink::Yes, + relinkablePrototypes, + relinkableExtensions); moduleDependenciesTracer.end(); } @@ -2821,11 +2913,30 @@ void ProjectStorage::handlePrototypes(TypeId prototypeId, Prototypes &relinkable keyValue("type id", prototypeId), keyValue("relinkable prototypes", relinkablePrototypes)}; + auto callback = [&](TypeId typeId, ImportedTypeNameId prototypeNameId) { + if (prototypeNameId) + relinkablePrototypes.emplace_back(typeId, prototypeNameId); + }; + + s->updatePrototypeIdToTypeIdStatement.readCallback(callback, prototypeId, unresolvedTypeId); +} + +void ProjectStorage::handlePrototypesWithExportedTypeNameAndTypeId( + Utils::SmallStringView exportedTypeName, TypeId typeId, Prototypes &relinkablePrototypes) +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"handle invalid prototypes"_t, + projectStorageCategory(), + keyValue("type id", exportedTypeName), + keyValue("relinkable prototypes", relinkablePrototypes)}; + auto callback = [&](TypeId typeId, ImportedTypeNameId prototypeNameId) { relinkablePrototypes.emplace_back(typeId, prototypeNameId); }; - s->updatePrototypeIdToNullStatement.readCallback(callback, prototypeId); + s->selectTypeIdAndPrototypeNameIdForPrototypeIdAndTypeNameStatement.readCallback(callback, + exportedTypeName, + typeId); } void ProjectStorage::handleExtensions(TypeId extensionId, Prototypes &relinkableExtensions) @@ -2836,11 +2947,28 @@ void ProjectStorage::handleExtensions(TypeId extensionId, Prototypes &relinkable keyValue("type id", extensionId), keyValue("relinkable extensions", relinkableExtensions)}; + auto callback = [&](TypeId typeId, ImportedTypeNameId extensionNameId) { + if (extensionNameId) + relinkableExtensions.emplace_back(typeId, extensionNameId); + }; + + s->updateExtensionIdToTypeIdStatement.readCallback(callback, extensionId, unresolvedTypeId); +} + +void ProjectStorage::handleExtensionsWithExportedTypeNameAndTypeId( + Utils::SmallStringView exportedTypeName, TypeId typeId, Prototypes &relinkableExtensions) +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"handle invalid extensions"_t, + projectStorageCategory(), + keyValue("type id", exportedTypeName), + keyValue("relinkable extensions", relinkableExtensions)}; + auto callback = [&](TypeId typeId, ImportedTypeNameId extensionNameId) { relinkableExtensions.emplace_back(typeId, extensionNameId); }; - s->updateExtensionIdToNullStatement.readCallback(callback, extensionId); + s->selectTypeIdForExtensionIdAndTypeNameStatement.readCallback(callback, exportedTypeName, typeId); } void ProjectStorage::deleteType(TypeId typeId, @@ -2927,6 +3055,39 @@ void ProjectStorage::relinkPropertyDeclarations(PropertyDeclarations &relinkable TypeCompare{}); } +template +void ProjectStorage::relinkPrototypes(Prototypes &relinkablePrototypes, + const TypeIds &deletedTypeIds, + Callable updateStatement) +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"relink prototypes"_t, + projectStorageCategory(), + keyValue("relinkable prototypes", relinkablePrototypes), + keyValue("deleted type ids", deletedTypeIds)}; + + std::sort(relinkablePrototypes.begin(), relinkablePrototypes.end()); + relinkablePrototypes.erase(std::unique(relinkablePrototypes.begin(), relinkablePrototypes.end()), + relinkablePrototypes.end()); + + Utils::set_greedy_difference( + relinkablePrototypes.cbegin(), + relinkablePrototypes.cend(), + deletedTypeIds.begin(), + deletedTypeIds.end(), + [&](const Prototype &prototype) { + TypeId prototypeId = fetchTypeId(prototype.prototypeNameId); + + if (!prototypeId) + errorNotifier->typeNameCannotBeResolved(fetchImportedTypeName(prototype.prototypeNameId), + fetchTypeSourceId(prototype.typeId)); + + updateStatement(prototype.typeId, prototypeId); + checkForPrototypeChainCycle(prototype.typeId); + }, + TypeCompare{}); +} + void ProjectStorage::deleteNotUpdatedTypes(const TypeIds &updatedTypeIds, const SourceIds &updatedSourceIds, const TypeIds &typeIdsToBeDeleted, @@ -3141,6 +3302,9 @@ void ProjectStorage::synchronizeExportedTypes(const TypeIds &updatedTypeIds, } catch (const Sqlite::ConstraintPreventsModification &) { throw QmlDesigner::ExportedTypeCannotBeInserted{type.name}; } + + handlePrototypesWithExportedTypeNameAndTypeId(type.name, unresolvedTypeId, relinkablePrototypes); + handleExtensionsWithExportedTypeNameAndTypeId(type.name, unresolvedTypeId, relinkableExtensions); }; auto update = [&](const Storage::Synchronization::ExportedTypeView &view, @@ -3176,6 +3340,7 @@ void ProjectStorage::synchronizeExportedTypes(const TypeIds &updatedTypeIds, relinkableAliasPropertyDeclarations); handlePrototypes(view.typeId, relinkablePrototypes); handleExtensions(view.typeId, relinkableExtensions); + s->deleteExportedTypeNameStatement.write(view.exportedTypeNameId); }; @@ -3491,11 +3656,90 @@ void ProjectStorage::resetRemovedAliasPropertyDeclarationsToNull( PropertyCompare{}); } +void ProjectStorage::handlePrototypesWithSourceIdAndPrototypeId(SourceId sourceId, + TypeId prototypeId, + Prototypes &relinkablePrototypes) +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"handle prototypes with source id and prototype id"_t, + projectStorageCategory(), + keyValue("source id", sourceId), + keyValue("type id", prototypeId)}; + + auto callback = [&](TypeId typeId, ImportedTypeNameId prototypeNameId) { + if (prototypeNameId) + relinkablePrototypes.emplace_back(typeId, prototypeNameId); + }; + + s->selectTypeIdAndPrototypeNameIdForPrototypeIdAndSourceIdStatement.readCallback(callback, + prototypeId, + sourceId); +} + +void ProjectStorage::handlePrototypesAndExtensionsWithSourceId(SourceId sourceId, + TypeId prototypeId, + TypeId extensionId, + Prototypes &relinkablePrototypes, + Prototypes &relinkableExtensions) +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"handle prototypes with source id"_t, + projectStorageCategory(), + keyValue("source id", sourceId), + keyValue("prototype id", prototypeId), + keyValue("extension id", extensionId)}; + + auto callback = + [&](TypeId typeId, ImportedTypeNameId prototypeNameId, ImportedTypeNameId extensionNameId) { + if (prototypeNameId) + relinkablePrototypes.emplace_back(typeId, prototypeNameId); + if (extensionNameId) + relinkableExtensions.emplace_back(typeId, extensionNameId); + }; + + s->updatePrototypeIdAndExtensionIdToTypeIdForSourceIdStatement.readCallback(callback, + sourceId, + prototypeId, + extensionId); +} + +void ProjectStorage::handleExtensionsWithSourceIdAndExtensionId(SourceId sourceId, + TypeId extensionId, + Prototypes &relinkableExtensions) +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"handle prototypes with source id and prototype id"_t, + projectStorageCategory(), + keyValue("source id", sourceId), + keyValue("type id", extensionId)}; + + auto callback = [&](TypeId typeId, ImportedTypeNameId extensionNameId) { + if (extensionNameId) + relinkableExtensions.emplace_back(typeId, extensionNameId); + }; + + s->selectTypeIdAndExtensionNameIdForExtensionIdAndSourceIdStatement.readCallback(callback, + extensionId, + sourceId); +} + ImportId ProjectStorage::insertDocumentImport(const Storage::Import &import, Storage::Synchronization::ImportKind importKind, ModuleId sourceModuleId, - ImportId parentImportId) + ImportId parentImportId, + Relink relink, + Prototypes &relinkablePrototypes, + Prototypes &relinkableExtensions) { + if (relink == Relink::Yes) { + handlePrototypesWithSourceIdAndPrototypeId(import.sourceId, + unresolvedTypeId, + relinkablePrototypes); + handleExtensionsWithSourceIdAndExtensionId(import.sourceId, + unresolvedTypeId, + relinkableExtensions); + } + if (import.version.minor) { return s->insertDocumentImportWithVersionStatement.value(import.sourceId, import.moduleId, @@ -3522,7 +3766,10 @@ ImportId ProjectStorage::insertDocumentImport(const Storage::Import &import, void ProjectStorage::synchronizeDocumentImports(Storage::Imports &imports, const SourceIds &updatedSourceIds, - Storage::Synchronization::ImportKind importKind) + Storage::Synchronization::ImportKind importKind, + Relink relink, + Prototypes &relinkablePrototypes, + Prototypes &relinkableExtensions) { std::sort(imports.begin(), imports.end(), [](auto &&first, auto &&second) { return std::tie(first.sourceId, first.moduleId, first.version) @@ -3559,7 +3806,13 @@ void ProjectStorage::synchronizeDocumentImports(Storage::Imports &imports, keyValue("source id", import.sourceId), keyValue("module id", import.moduleId)}; - auto importId = insertDocumentImport(import, importKind, import.moduleId, ImportId{}); + auto importId = insertDocumentImport(import, + importKind, + import.moduleId, + ImportId{}, + relink, + relinkablePrototypes, + relinkableExtensions); auto callback = [&](ModuleId exportedModuleId, int majorVersion, int minorVersion) { Storage::Import additionImport{exportedModuleId, Storage::Version{majorVersion, minorVersion}, @@ -3579,7 +3832,10 @@ void ProjectStorage::synchronizeDocumentImports(Storage::Imports &imports, auto indirectImportId = insertDocumentImport(additionImport, exportedImportKind, import.moduleId, - importId); + importId, + relink, + relinkablePrototypes, + relinkableExtensions); tracer.end(keyValue("import id", indirectImportId)); }; @@ -3606,6 +3862,13 @@ void ProjectStorage::synchronizeDocumentImports(Storage::Imports &imports, s->deleteDocumentImportStatement.write(view.importId); s->deleteDocumentImportsWithParentImportIdStatement.write(view.sourceId, view.importId); + if (relink == Relink::Yes) { + handlePrototypesAndExtensionsWithSourceId(view.sourceId, + unresolvedTypeId, + unresolvedTypeId, + relinkablePrototypes, + relinkableExtensions); + } }; Sqlite::insertUpdateDelete(range, imports, compareKey, insert, update, remove); @@ -4156,25 +4419,29 @@ void ProjectStorage::checkForAliasChainCycle(PropertyDeclarationId propertyDecla } std::pair ProjectStorage::fetchImportedTypeNameIdAndTypeId( - const Storage::Synchronization::ImportedTypeName &typeName, SourceId sourceId) + const Storage::Synchronization::ImportedTypeName &importedTypeName, SourceId sourceId) { using NanotraceHR::keyValue; NanotraceHR::Tracer tracer{"fetch imported type name id and type id"_t, projectStorageCategory(), - keyValue("imported type name", typeName), + keyValue("imported type name", importedTypeName), keyValue("source id", sourceId)}; TypeId typeId; ImportedTypeNameId typeNameId; - if (!std::visit([](auto &&typeName_) -> bool { return typeName_.name.isEmpty(); }, typeName)) { - typeNameId = fetchImportedTypeNameId(typeName, sourceId); + auto typeName = std::visit([](auto &&importedTypeName) { return importedTypeName.name; }, + importedTypeName); + if (!typeName.empty()) { + typeNameId = fetchImportedTypeNameId(importedTypeName, sourceId); typeId = fetchTypeId(typeNameId); tracer.end(keyValue("type id", typeId), keyValue("type name id", typeNameId)); - if (!typeId) - throw TypeNameDoesNotExists{fetchImportedTypeName(typeNameId), sourceId}; + if (!typeId) { + errorNotifier->typeNameCannotBeResolved(typeName, sourceId); + return {unresolvedTypeId, typeNameId}; + } } return {typeId, typeNameId}; @@ -4323,6 +4590,11 @@ Utils::SmallString ProjectStorage::fetchImportedTypeName(ImportedTypeNameId type return s->selectNameFromImportedTypeNamesStatement.value(typeNameId); } +SourceId ProjectStorage::fetchTypeSourceId(TypeId typeId) const +{ + return s->selectSourceIdByTypeIdStatement.value(typeId); +} + TypeId ProjectStorage::fetchTypeId(ImportedTypeNameId typeNameId, Storage::Synchronization::TypeNameKind kind) const { @@ -4334,9 +4606,10 @@ TypeId ProjectStorage::fetchTypeId(ImportedTypeNameId typeNameId, TypeId typeId; if (kind == Storage::Synchronization::TypeNameKind::Exported) { - typeId = s->selectTypeIdForImportedTypeNameNamesStatement.value(typeNameId); + typeId = s->selectTypeIdForImportedTypeNameNamesStatement.value(typeNameId); } else { - typeId = s->selectTypeIdForQualifiedImportedTypeNameNamesStatement.value(typeNameId); + typeId = s->selectTypeIdForQualifiedImportedTypeNameNamesStatement.value( + typeNameId); } tracer.end(keyValue("type id", typeId)); diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h index a5773448621..54d91015968 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h @@ -39,6 +39,8 @@ class ProjectStorage final : public ProjectStorageInterface using Database = Sqlite::Database; friend Storage::Info::CommonTypeCache; + enum class Relink { No, Yes }; + public: ProjectStorage(Database &database, ProjectStorageErrorNotifierInterface &errorNotifier, @@ -49,6 +51,11 @@ public: void synchronizeDocumentImports(Storage::Imports imports, SourceId sourceId) override; + void setErrorNotifier(ProjectStorageErrorNotifierInterface &errorNotifier) + { + this->errorNotifier = &errorNotifier; + } + void addObserver(ProjectStorageObserver *observer) override; void removeObserver(ProjectStorageObserver *observer) override; @@ -443,6 +450,11 @@ private: return first.typeId < second.typeId; } + friend bool operator==(Prototype first, Prototype second) + { + return first.typeId == second.typeId; + } + template friend void convertToString(String &string, const Prototype &prototype) { @@ -575,7 +587,9 @@ private: Storage::Imports &moduleDependencies, const SourceIds &updatedModuleDependencySourceIds, Storage::Synchronization::ModuleExportedImports &moduleExportedImports, - const ModuleIds &updatedModuleIds); + const ModuleIds &updatedModuleIds, + Prototypes &relinkablePrototypes, + Prototypes &relinkableExtensions); void synchromizeModuleExportedImports( Storage::Synchronization::ModuleExportedImports &moduleExportedImports, @@ -593,9 +607,14 @@ private: PropertyDeclarations &relinkablePropertyDeclarations); void handlePrototypes(TypeId prototypeId, Prototypes &relinkablePrototypes); + void handlePrototypesWithExportedTypeNameAndTypeId(Utils::SmallStringView exportedTypeName, + TypeId typeId, + Prototypes &relinkablePrototypes); void handleExtensions(TypeId extensionId, Prototypes &relinkableExtensions); - + void handleExtensionsWithExportedTypeNameAndTypeId(Utils::SmallStringView exportedTypeName, + TypeId typeId, + Prototypes &relinkableExtensions); void deleteType(TypeId typeId, AliasPropertyDeclarations &relinkableAliasPropertyDeclarations, PropertyDeclarations &relinkablePropertyDeclarations, @@ -611,32 +630,7 @@ private: template void relinkPrototypes(Prototypes &relinkablePrototypes, const TypeIds &deletedTypeIds, - Callable updateStatement) - { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"relink prototypes"_t, - projectStorageCategory(), - keyValue("relinkable prototypes", relinkablePrototypes), - keyValue("deleted type ids", deletedTypeIds)}; - - std::sort(relinkablePrototypes.begin(), relinkablePrototypes.end()); - - Utils::set_greedy_difference( - relinkablePrototypes.cbegin(), - relinkablePrototypes.cend(), - deletedTypeIds.begin(), - deletedTypeIds.end(), - [&](const Prototype &prototype) { - TypeId prototypeId = fetchTypeId(prototype.prototypeNameId); - - if (!prototypeId) - throw TypeNameDoesNotExists{fetchImportedTypeName(prototype.prototypeNameId)}; - - updateStatement(prototype.typeId, prototypeId); - checkForPrototypeChainCycle(prototype.typeId); - }, - TypeCompare{}); - } + Callable updateStatement); void deleteNotUpdatedTypes(const TypeIds &updatedTypeIds, const SourceIds &updatedSourceIds, @@ -751,14 +745,32 @@ private: Storage::Synchronization::Types &types, AliasPropertyDeclarations &relinkableAliasPropertyDeclarations); + void handlePrototypesWithSourceIdAndPrototypeId(SourceId sourceId, + TypeId prototypeId, + Prototypes &relinkablePrototypes); + void handlePrototypesAndExtensionsWithSourceId(SourceId sourceId, + TypeId prototypeId, + TypeId extensionId, + Prototypes &relinkablePrototypes, + Prototypes &relinkableExtensions); + void handleExtensionsWithSourceIdAndExtensionId(SourceId sourceId, + TypeId extensionId, + Prototypes &relinkableExtensions); + ImportId insertDocumentImport(const Storage::Import &import, Storage::Synchronization::ImportKind importKind, ModuleId sourceModuleId, - ImportId parentImportId); + ImportId parentImportId, + Relink forceRelink, + Prototypes &relinkablePrototypes, + Prototypes &relinkableExtensions); void synchronizeDocumentImports(Storage::Imports &imports, const SourceIds &updatedSourceIds, - Storage::Synchronization::ImportKind importKind); + Storage::Synchronization::ImportKind importKind, + Relink forceRelink, + Prototypes &relinkablePrototypes, + Prototypes &relinkableExtensions); static Utils::PathString createJson(const Storage::Synchronization::ParameterDeclarations ¶meters); @@ -904,6 +916,7 @@ private: TypeId fetchTypeId(ImportedTypeNameId typeNameId) const; Utils::SmallString fetchImportedTypeName(ImportedTypeNameId typeNameId) const; + SourceId fetchTypeSourceId(TypeId typeId) const; TypeId fetchTypeId(ImportedTypeNameId typeNameId, Storage::Synchronization::TypeNameKind kind) const; @@ -970,7 +983,7 @@ private: public: Database &database; - ProjectStorageErrorNotifierInterface &errorNotifier; + ProjectStorageErrorNotifierInterface *errorNotifier = nullptr; // cannot be null Sqlite::ExclusiveNonThrowingDestructorTransaction exclusiveTransaction; std::unique_ptr initializer; mutable ModuleCache moduleCache{ModuleStorageAdapter{*this}}; diff --git a/tests/unit/tests/mocks/projectstorageerrornotifiermock.h b/tests/unit/tests/mocks/projectstorageerrornotifiermock.h index 28443300cc1..730c70a66ab 100644 --- a/tests/unit/tests/mocks/projectstorageerrornotifiermock.h +++ b/tests/unit/tests/mocks/projectstorageerrornotifiermock.h @@ -9,6 +9,7 @@ class ProjectStorageErrorNotifierMock : public QmlDesigner::ProjectStorageErrorNotifierInterface { +public: MOCK_METHOD(void, typeNameCannotBeResolved, (Utils::SmallStringView typeName, QmlDesigner::SourceId souceId), diff --git a/tests/unit/tests/printers/gtest-creator-printing.cpp b/tests/unit/tests/printers/gtest-creator-printing.cpp index 0cbef7b1b7f..8ca65f4526e 100644 --- a/tests/unit/tests/printers/gtest-creator-printing.cpp +++ b/tests/unit/tests/printers/gtest-creator-printing.cpp @@ -833,8 +833,9 @@ std::ostream &operator<<(std::ostream &out, const Type &type) { using std::operator<<; using Utils::operator<<; - return out << "( typename: \"" << type.typeName << "\", prototype: " << type.prototype << ", " - << type.prototypeId << ", " << type.traits << ", source: " << type.sourceId + return out << "( typename: \"" << type.typeName << "\", prototype: {\"" << type.prototype + << "\", " << type.prototypeId << "}, " << "\", extension: {\"" << type.extension + << "\", " << type.extensionId << "}, " << type.traits << ", source: " << type.sourceId << ", exports: " << type.exportedTypes << ", properties: " << type.propertyDeclarations << ", functions: " << type.functionDeclarations << ", signals: " << type.signalDeclarations << ", changeLevel: " << type.changeLevel diff --git a/tests/unit/tests/unittests/projectstorage/projectstorage-test.cpp b/tests/unit/tests/unittests/projectstorage/projectstorage-test.cpp index c59971fec1c..fc806d73a95 100644 --- a/tests/unit/tests/unittests/projectstorage/projectstorage-test.cpp +++ b/tests/unit/tests/unittests/projectstorage/projectstorage-test.cpp @@ -205,6 +205,23 @@ MATCHER_P4(IsInfoPropertyDeclaration, && propertyDeclaration.traits == traits; } +auto IsUnresolvedTypeId() +{ + return Property(&QmlDesigner::TypeId::internalId, -1); +} + +template +auto IsPrototypeId(const Matcher &matcher) +{ + return Field(&Storage::Synchronization::Type::prototypeId, matcher); +} + +template +auto IsExtensionId(const Matcher &matcher) +{ + return Field(&Storage::Synchronization::Type::extensionId, matcher); +} + class HasNameMatcher { public: @@ -286,6 +303,8 @@ protected: static void TearDownTestSuite() { staticData.reset(); } + ProjectStorage() { storage.setErrorNotifier(errorNotifierMock); } + ~ProjectStorage() { storage.resetForTestsOnly(); } template @@ -312,6 +331,32 @@ protected: storage.fetchSourceId(sourceContextId3, "bar"); } + auto createVerySimpleSynchronizationPackage() + { + SynchronizationPackage package; + + package.types.push_back(Storage::Synchronization::Type{ + "QQuickItem", + Storage::Synchronization::ImportedType{}, + Storage::Synchronization::ImportedType{}, + TypeTraitsKind::Reference, + sourceId1, + {Storage::Synchronization::ExportedType{qtQuickModuleId, "Item"}, + Storage::Synchronization::ExportedType{qtQuickNativeModuleId, "QQuickItem"}}}); + package.types.push_back(Storage::Synchronization::Type{ + "QObject", + Storage::Synchronization::ImportedType{}, + Storage::Synchronization::ImportedType{}, + TypeTraitsKind::Reference, + sourceId2, + {Storage::Synchronization::ExportedType{qmlModuleId, "Object"}, + Storage::Synchronization::ExportedType{qmlNativeModuleId, "QObject"}}}); + + package.updatedSourceIds = {sourceId1, sourceId2}; + + return package; + } + auto createSimpleSynchronizationPackage() { SynchronizationPackage package; @@ -1142,7 +1187,7 @@ protected: inline static std::unique_ptr staticData; Sqlite::Database &database = staticData->database; QmlDesigner::ProjectStorage &storage = staticData->storage; - ProjectStorageErrorNotifierMock &errorNotifierMock = staticData->errorNotifierMock; + NiceMock errorNotifierMock; QmlDesigner::SourcePathCache sourcePathCache{storage}; QmlDesigner::SourcePathView path1{"/path1/to"}; QmlDesigner::SourcePathView path2{"/path2/to"}; @@ -1486,22 +1531,193 @@ TEST_F(ProjectStorage, synchronize_types_adds_new_types_with_exported_extension_ "QQuickItem")))))); } -TEST_F(ProjectStorage, synchronize_types_adds_new_types_throws_with_wrong_prototype_name) +TEST_F(ProjectStorage, + synchronize_types_adds_unknown_prototype_which_notifies_about_unresolved_type_name) { auto package{createSimpleSynchronizationPackage()}; package.types[0].prototype = Storage::Synchronization::ImportedType{"Objec"}; - ASSERT_THROW(storage.synchronize(std::move(package)), QmlDesigner::TypeNameDoesNotExists); + EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("Objec"), sourceId1)); + + storage.synchronize(std::move(package)); } -TEST_F(ProjectStorage, synchronize_types_adds_new_types_throws_with_wrong_extension_name) +TEST_F(ProjectStorage, synchronize_types_adds_unknown_prototype_as_unresolved_type_id) +{ + auto package{createSimpleSynchronizationPackage()}; + package.types[0].prototype = Storage::Synchronization::ImportedType{"Objec"}; + + storage.synchronize(std::move(package)); + + ASSERT_THAT(storage.fetchTypeByTypeId(fetchTypeId(sourceId1, "QQuickItem")), + IsPrototypeId(IsUnresolvedTypeId())); +} + +TEST_F(ProjectStorage, synchronize_types_updates_unresolved_prototype_after_exported_type_name_is_added) +{ + auto package{createSimpleSynchronizationPackage()}; + package.types[0].prototype = Storage::Synchronization::ImportedType{"Objec"}; + storage.synchronize(package); + package.types[1].exportedTypes.emplace_back(qmlNativeModuleId, "Objec"); + + storage.synchronize(std::move(package)); + + ASSERT_THAT(storage.fetchTypeByTypeId(fetchTypeId(sourceId1, "QQuickItem")), + IsPrototypeId(fetchTypeId(sourceId2, "QObject"))); +} + +TEST_F(ProjectStorage, + synchronize_types_updates_prototype_to_unresolved_after_exported_type_name_is_removed) +{ + auto package{createSimpleSynchronizationPackage()}; + package.types[0].prototype = Storage::Synchronization::ImportedType{"Objec"}; + package.types[1].exportedTypes.emplace_back(qmlNativeModuleId, "Objec"); + storage.synchronize(package); + package.types[1].exportedTypes.pop_back(); + + storage.synchronize(std::move(package)); + + ASSERT_THAT(storage.fetchTypeByTypeId(fetchTypeId(sourceId1, "QQuickItem")), + IsPrototypeId(IsUnresolvedTypeId())); +} + +TEST_F(ProjectStorage, + synchronize_types_updates_unresolved_prototype_indirectly_after_exported_type_name_is_added) +{ + auto package{createSimpleSynchronizationPackage()}; + package.types[0].prototype = Storage::Synchronization::ImportedType{"Objec"}; + storage.synchronize(package); + package.types[1].exportedTypes.emplace_back(qmlNativeModuleId, "Objec"); + package.types.erase(package.types.begin()); + package.updatedSourceIds = {sourceId2}; + + storage.synchronize(std::move(package)); + + ASSERT_THAT(storage.fetchTypeByTypeId(fetchTypeId(sourceId1, "QQuickItem")), + IsPrototypeId(fetchTypeId(sourceId2, "QObject"))); +} + +TEST_F(ProjectStorage, + synchronize_types_updates_prototype_indirectly_to_unresolved_after_exported_type_name_is_removed) +{ + auto package{createSimpleSynchronizationPackage()}; + package.types[0].prototype = Storage::Synchronization::ImportedType{"Objec"}; + package.types[1].exportedTypes.emplace_back(qmlNativeModuleId, "Objec"); + storage.synchronize(package); + package.types[1].exportedTypes.pop_back(); + package.types.erase(package.types.begin()); + package.updatedSourceIds = {sourceId2}; + + storage.synchronize(std::move(package)); + + ASSERT_THAT(storage.fetchTypeByTypeId(fetchTypeId(sourceId1, "QQuickItem")), + IsPrototypeId(IsUnresolvedTypeId())); +} + +TEST_F(ProjectStorage, + synchronize_types_updates_prototype_indirectly_to_unresolved_after_exported_type_name_is_removed_notifies_type_name_cannot_be_resolved) +{ + auto package{createSimpleSynchronizationPackage()}; + package.types[0].prototype = Storage::Synchronization::ImportedType{"Objec"}; + package.types[1].exportedTypes.emplace_back(qmlNativeModuleId, "Objec"); + storage.synchronize(package); + package.types[1].exportedTypes.pop_back(); + package.types.erase(package.types.begin()); + package.updatedSourceIds = {sourceId2}; + + EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("Objec"), sourceId1)); + + storage.synchronize(std::move(package)); +} + +TEST_F(ProjectStorage, synchronize_types_updates_unresolved_extension_after_exported_type_name_is_added) +{ + auto package{createSimpleSynchronizationPackage()}; + package.types[0].extension = Storage::Synchronization::ImportedType{"Objec"}; + storage.synchronize(package); + package.types[1].exportedTypes.emplace_back(qmlNativeModuleId, "Objec"); + + storage.synchronize(std::move(package)); + + ASSERT_THAT(storage.fetchTypeByTypeId(fetchTypeId(sourceId1, "QQuickItem")), + IsExtensionId(fetchTypeId(sourceId2, "QObject"))); +} + +TEST_F(ProjectStorage, + synchronize_types_updates_extension_to_unresolved_after_exported_type_name_is_removed) +{ + auto package{createSimpleSynchronizationPackage()}; + package.types[0].extension = Storage::Synchronization::ImportedType{"Objec"}; + package.types[1].exportedTypes.emplace_back(qmlNativeModuleId, "Objec"); + storage.synchronize(package); + package.types[1].exportedTypes.pop_back(); + + storage.synchronize(std::move(package)); + + ASSERT_THAT(storage.fetchTypeByTypeId(fetchTypeId(sourceId1, "QQuickItem")), + IsExtensionId(IsUnresolvedTypeId())); +} + +TEST_F(ProjectStorage, + synchronize_types_updates_unresolved_extension_indirectly_after_exported_type_name_is_added) +{ + auto package{createSimpleSynchronizationPackage()}; + package.types[0].extension = Storage::Synchronization::ImportedType{"Objec"}; + storage.synchronize(package); + package.types[1].exportedTypes.emplace_back(qmlNativeModuleId, "Objec"); + package.types.erase(package.types.begin()); + package.updatedSourceIds = {sourceId2}; + + storage.synchronize(std::move(package)); + + ASSERT_THAT(storage.fetchTypeByTypeId(fetchTypeId(sourceId1, "QQuickItem")), + IsExtensionId(fetchTypeId(sourceId2, "QObject"))); +} + +TEST_F(ProjectStorage, + synchronize_types_updates_invalid_extension_indirectly_after_exported_type_name_is_removed) +{ + auto package{createSimpleSynchronizationPackage()}; + package.types[0].extension = Storage::Synchronization::ImportedType{"Objec"}; + package.types[1].exportedTypes.emplace_back(qmlNativeModuleId, "Objec"); + storage.synchronize(package); + package.types[1].exportedTypes.pop_back(); + package.types.erase(package.types.begin()); + package.updatedSourceIds = {sourceId2}; + + storage.synchronize(std::move(package)); + + ASSERT_THAT(storage.fetchTypeByTypeId(fetchTypeId(sourceId1, "QQuickItem")), + IsExtensionId(IsUnresolvedTypeId())); +} + +TEST_F(ProjectStorage, + synchronize_types_updates_extension_indirectly_to_unresolved_after_exported_type_name_is_removed_notifies_type_name_cannot_be_resolved) +{ + auto package{createSimpleSynchronizationPackage()}; + package.types[0].extension = Storage::Synchronization::ImportedType{"Objec"}; + package.types[1].exportedTypes.emplace_back(qmlNativeModuleId, "Objec"); + storage.synchronize(package); + package.types[1].exportedTypes.pop_back(); + package.types.erase(package.types.begin()); + package.updatedSourceIds = {sourceId2}; + + EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("Objec"), sourceId1)); + + storage.synchronize(std::move(package)); +} + +TEST_F(ProjectStorage, synchronize_types_adds_extension_which_notifies_about_unresolved_type_name) { auto package{createSimpleSynchronizationPackage()}; package.types[0].extension = Storage::Synchronization::ImportedType{"Objec"}; - ASSERT_THROW(storage.synchronize(std::move(package)), QmlDesigner::TypeNameDoesNotExists); + EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("Objec"), sourceId1)); + + storage.synchronize(std::move(package)); } + TEST_F(ProjectStorage, synchronize_types_adds_new_types_with_missing_module) { auto package{createSimpleSynchronizationPackage()}; @@ -1774,7 +1990,7 @@ TEST_F(ProjectStorage, synchronize_types_add_qualified_extension) IsExportedType(qtQuickNativeModuleId, "QQuickItem")))))); } -TEST_F(ProjectStorage, synchronize_types_throws_for_missing_prototype) +TEST_F(ProjectStorage, synchronize_types_notifies_cannot_resolve_for_missing_prototype) { auto package{createSimpleSynchronizationPackage()}; package.types = {Storage::Synchronization::Type{ @@ -1786,10 +2002,12 @@ TEST_F(ProjectStorage, synchronize_types_throws_for_missing_prototype) {Storage::Synchronization::ExportedType{qtQuickModuleId, "Item"}, Storage::Synchronization::ExportedType{qtQuickNativeModuleId, "QQuickItem"}}}}; - ASSERT_THROW(storage.synchronize(package), QmlDesigner::TypeNameDoesNotExists); + EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("QObject"), sourceId1)); + + storage.synchronize(package); } -TEST_F(ProjectStorage, synchronize_types_throws_for_missing_extension) +TEST_F(ProjectStorage, synchronize_types_notifies_cannot_resolve_for_missing_extension) { auto package{createSimpleSynchronizationPackage()}; package.types = {Storage::Synchronization::Type{ @@ -1801,7 +2019,9 @@ TEST_F(ProjectStorage, synchronize_types_throws_for_missing_extension) {Storage::Synchronization::ExportedType{qtQuickModuleId, "Item"}, Storage::Synchronization::ExportedType{qtQuickNativeModuleId, "QQuickItem"}}}}; - ASSERT_THROW(storage.synchronize(package), QmlDesigner::TypeNameDoesNotExists); + EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("QObject"), sourceId1)); + + storage.synchronize(package); } TEST_F(ProjectStorage, synchronize_types_throws_for_invalid_module) @@ -3670,7 +3890,8 @@ TEST_F(ProjectStorage, change_extension_type_module_id) TypeTraitsKind::Reference))); } -TEST_F(ProjectStorage, change_qualified_prototype_type_module_id_throws) +TEST_F(ProjectStorage, + change_qualified_prototype_type_module_id_notifies_that_type_name_cannot_be_resolved) { auto package{createSimpleSynchronizationPackage()}; package.types[0].prototype = Storage::Synchronization::QualifiedImportedType{ @@ -3678,12 +3899,12 @@ TEST_F(ProjectStorage, change_qualified_prototype_type_module_id_throws) storage.synchronize(package); package.types[1].exportedTypes[0].moduleId = qtQuickModuleId; - ASSERT_THROW(storage.synchronize( - SynchronizationPackage{importsSourceId2, {package.types[1]}, {sourceId2}}), - QmlDesigner::TypeNameDoesNotExists); + EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("Object"), sourceId1)); + + storage.synchronize(SynchronizationPackage{importsSourceId2, {package.types[1]}, {sourceId2}}); } -TEST_F(ProjectStorage, change_qualified_extension_type_module_id_throws) +TEST_F(ProjectStorage, change_qualified_extension_type_module_id_notifies_cannot_resolve) { auto package{createSimpleSynchronizationPackage()}; std::swap(package.types.front().extension, package.types.front().prototype); @@ -3692,9 +3913,9 @@ TEST_F(ProjectStorage, change_qualified_extension_type_module_id_throws) storage.synchronize(package); package.types[1].exportedTypes[0].moduleId = qtQuickModuleId; - ASSERT_THROW(storage.synchronize( - SynchronizationPackage{importsSourceId2, {package.types[1]}, {sourceId2}}), - QmlDesigner::TypeNameDoesNotExists); + EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("Object"), sourceId1)); + + storage.synchronize(SynchronizationPackage{importsSourceId2, {package.types[1]}, {sourceId2}}); } TEST_F(ProjectStorage, change_qualified_prototype_type_module_id) @@ -3798,9 +4019,9 @@ TEST_F(ProjectStorage, change_prototype_type_name_throws_for_wrong_native_protot package.types[1].exportedTypes[2].name = "QObject3"; package.types[1].typeName = "QObject3"; - ASSERT_THROW(storage.synchronize( - SynchronizationPackage{importsSourceId2, {package.types[1]}, {sourceId2}}), - QmlDesigner::TypeNameDoesNotExists); + EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("QObject"), sourceId1)); + + storage.synchronize(SynchronizationPackage{importsSourceId2, {package.types[1]}, {sourceId2}}); } TEST_F(ProjectStorage, change_extension_type_name_throws_for_wrong_native_extension_type_name) @@ -3813,9 +4034,9 @@ TEST_F(ProjectStorage, change_extension_type_name_throws_for_wrong_native_extens package.types[1].exportedTypes[2].name = "QObject3"; package.types[1].typeName = "QObject3"; - ASSERT_THROW(storage.synchronize( - SynchronizationPackage{importsSourceId2, {package.types[1]}, {sourceId2}}), - QmlDesigner::TypeNameDoesNotExists); + EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("QObject"), sourceId1)); + + storage.synchronize(SynchronizationPackage{importsSourceId2, {package.types[1]}, {sourceId2}}); } TEST_F(ProjectStorage, throw_for_prototype_chain_cycles) @@ -4117,23 +4338,29 @@ TEST_F(ProjectStorage, qualified_extension) TypeTraitsKind::Reference))); } -TEST_F(ProjectStorage, qualified_prototype_upper_down_the_module_chain_throws) +TEST_F(ProjectStorage, + qualified_prototype_upper_down_the_module_chain_notifies_type_name_cannot_be_resolved) { auto package{createSimpleSynchronizationPackage()}; package.types[0].prototype = Storage::Synchronization::QualifiedImportedType{ "Object", Storage::Import{qtQuickModuleId, Storage::Version{}, sourceId1}}; - ASSERT_THROW(storage.synchronize(package), QmlDesigner::TypeNameDoesNotExists); + EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("Object"), sourceId1)); + + storage.synchronize(package); } -TEST_F(ProjectStorage, qualified_extension_upper_down_the_module_chain_throws) +TEST_F(ProjectStorage, + qualified_extension_upper_down_the_module_chain_notifies_type_name_cannot_be_resolved) { auto package{createSimpleSynchronizationPackage()}; std::swap(package.types.front().extension, package.types.front().prototype); package.types[0].extension = Storage::Synchronization::QualifiedImportedType{ "Object", Storage::Import{qtQuickModuleId, Storage::Version{}, sourceId1}}; - ASSERT_THROW(storage.synchronize(package), QmlDesigner::TypeNameDoesNotExists); + EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("Object"), sourceId1)); + + storage.synchronize(package); } TEST_F(ProjectStorage, qualified_prototype_upper_in_the_module_chain) @@ -4189,7 +4416,7 @@ TEST_F(ProjectStorage, qualified_extension_upper_in_the_module_chain) TypeTraitsKind::Reference))); } -TEST_F(ProjectStorage, qualified_prototype_with_wrong_version_throws) +TEST_F(ProjectStorage, qualified_prototype_with_wrong_version_notifies_type_name_cannot_be_resolved) { auto package{createSimpleSynchronizationPackage()}; package.types[0].prototype = Storage::Synchronization::QualifiedImportedType{ @@ -4205,10 +4432,12 @@ TEST_F(ProjectStorage, qualified_prototype_with_wrong_version_throws) package.imports.emplace_back(qtQuickModuleId, Storage::Version{}, sourceId3); package.updatedSourceIds.push_back(sourceId3); - ASSERT_THROW(storage.synchronize(package), QmlDesigner::TypeNameDoesNotExists); + EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("Object"), sourceId1)); + + storage.synchronize(package); } -TEST_F(ProjectStorage, qualified_extension_with_wrong_version_throws) +TEST_F(ProjectStorage, qualified_extension_with_wrong_version_notifies_type_name_cannot_be_resolved) { auto package{createSimpleSynchronizationPackage()}; std::swap(package.types.front().extension, package.types.front().prototype); @@ -4225,7 +4454,9 @@ TEST_F(ProjectStorage, qualified_extension_with_wrong_version_throws) package.imports.emplace_back(qtQuickModuleId, Storage::Version{}, sourceId3); package.updatedSourceIds.push_back(sourceId3); - ASSERT_THROW(storage.synchronize(package), QmlDesigner::TypeNameDoesNotExists); + EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("Object"), sourceId1)); + + storage.synchronize(package); } TEST_F(ProjectStorage, qualified_prototype_with_version) @@ -4338,23 +4569,29 @@ TEST_F(ProjectStorage, qualified_extension_with_version_in_the_proto_type_chain) TypeTraitsKind::Reference))); } -TEST_F(ProjectStorage, qualified_prototype_with_version_down_the_proto_type_chain_throws) +TEST_F(ProjectStorage, + qualified_prototype_with_version_down_the_proto_type_chain_notifies_type_name_cannot_be_resolved) { auto package{createSimpleSynchronizationPackage()}; package.types[0].prototype = Storage::Synchronization::QualifiedImportedType{ "Object", Storage::Import{qtQuickModuleId, Storage::Version{2}, sourceId1}}; - ASSERT_THROW(storage.synchronize(package), QmlDesigner::TypeNameDoesNotExists); + EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("Object"), sourceId1)); + + storage.synchronize(package); } -TEST_F(ProjectStorage, qualified_extension_with_version_down_the_proto_type_chain_throws) +TEST_F(ProjectStorage, + qualified_extension_with_version_down_the_proto_type_chain_notifies_type_name_cannot_be_resolved) { auto package{createSimpleSynchronizationPackage()}; std::swap(package.types.front().extension, package.types.front().prototype); package.types[0].extension = Storage::Synchronization::QualifiedImportedType{ "Object", Storage::Import{qtQuickModuleId, Storage::Version{2}, sourceId1}}; - ASSERT_THROW(storage.synchronize(package), QmlDesigner::TypeNameDoesNotExists); + EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("Object"), sourceId1)); + + storage.synchronize(package); } TEST_F(ProjectStorage, qualified_property_declaration_type_name) @@ -4659,7 +4896,7 @@ TEST_F(ProjectStorage, fetch_by_major_version_and_minor_version_for_qualified_im } TEST_F(ProjectStorage, - fetch_by_major_version_and_minor_version_for_imported_type_if_minor_version_is_not_exported_throws) + fetch_by_major_version_and_minor_version_for_imported_type_if_minor_version_is_not_exported_notifies_type_name_cannot_be_resolved) { auto package{createSynchronizationPackageWithVersions()}; storage.synchronize(package); @@ -4673,12 +4910,13 @@ TEST_F(ProjectStorage, Storage::Version{}}}}; Storage::Import import{qmlModuleId, Storage::Version{1, 1}, sourceId2}; - ASSERT_THROW(storage.synchronize(SynchronizationPackage{{import}, {type}, {sourceId2}}), - QmlDesigner::TypeNameDoesNotExists); + EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("Object"), sourceId2)); + + storage.synchronize(SynchronizationPackage{{import}, {type}, {sourceId2}}); } TEST_F(ProjectStorage, - fetch_by_major_version_and_minor_version_for_qualified_imported_type_if_minor_version_is_not_exported_throws) + fetch_by_major_version_and_minor_version_for_qualified_imported_type_if_minor_version_is_not_exported_notifies_type_name_cannot_be_resolved) { auto package{createSynchronizationPackageWithVersions()}; storage.synchronize(package); @@ -4691,11 +4929,12 @@ TEST_F(ProjectStorage, sourceId2, {Storage::Synchronization::ExportedType{qtQuickModuleId, "Item", Storage::Version{}}}}; - ASSERT_THROW(storage.synchronize(SynchronizationPackage{{import}, {type}, {sourceId2}}), - QmlDesigner::TypeNameDoesNotExists); + EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("Object"), sourceId2)); + + storage.synchronize(SynchronizationPackage{{import}, {type}, {sourceId2}}); } -TEST_F(ProjectStorage, fetch_low_minor_version_for_imported_type_throws) +TEST_F(ProjectStorage, fetch_low_minor_version_for_imported_type_notifies_type_name_cannot_be_resolved) { auto package{createSynchronizationPackageWithVersions()}; storage.synchronize(package); @@ -4709,11 +4948,13 @@ TEST_F(ProjectStorage, fetch_low_minor_version_for_imported_type_throws) Storage::Version{}}}}; Storage::Import import{qmlModuleId, Storage::Version{1, 1}, sourceId2}; - ASSERT_THROW(storage.synchronize(SynchronizationPackage{{import}, {type}, {sourceId2}}), - QmlDesigner::TypeNameDoesNotExists); + EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("Obj"), sourceId2)); + + storage.synchronize(SynchronizationPackage{{import}, {type}, {sourceId2}}); } -TEST_F(ProjectStorage, fetch_low_minor_version_for_qualified_imported_type_throws) +TEST_F(ProjectStorage, + fetch_low_minor_version_for_qualified_imported_type_notifies_type_name_cannot_be_resolved) { auto package{createSynchronizationPackageWithVersions()}; storage.synchronize(package); @@ -4726,8 +4967,9 @@ TEST_F(ProjectStorage, fetch_low_minor_version_for_qualified_imported_type_throw sourceId2, {Storage::Synchronization::ExportedType{qtQuickModuleId, "Item", Storage::Version{}}}}; - ASSERT_THROW(storage.synchronize(SynchronizationPackage{{import}, {type}, {sourceId2}}), - QmlDesigner::TypeNameDoesNotExists); + EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("Obj"), sourceId2)); + + storage.synchronize(SynchronizationPackage{{import}, {type}, {sourceId2}}); } TEST_F(ProjectStorage, fetch_higher_minor_version_for_imported_type) @@ -4775,7 +5017,8 @@ TEST_F(ProjectStorage, fetch_higher_minor_version_for_qualified_imported_type) TypeTraitsKind::Reference))); } -TEST_F(ProjectStorage, fetch_different_major_version_for_imported_type_throws) +TEST_F(ProjectStorage, + fetch_different_major_version_for_imported_type_notifies_type_name_cannot_be_resolved) { auto package{createSynchronizationPackageWithVersions()}; storage.synchronize(package); @@ -4789,11 +5032,13 @@ TEST_F(ProjectStorage, fetch_different_major_version_for_imported_type_throws) Storage::Version{}}}}; Storage::Import import{qmlModuleId, Storage::Version{3, 1}, sourceId2}; - ASSERT_THROW(storage.synchronize(SynchronizationPackage{{import}, {type}, {sourceId2}}), - QmlDesigner::TypeNameDoesNotExists); + EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("Obj"), sourceId2)); + + storage.synchronize(SynchronizationPackage{{import}, {type}, {sourceId2}}); } -TEST_F(ProjectStorage, fetch_different_major_version_for_qualified_imported_type_throws) +TEST_F(ProjectStorage, + fetch_different_major_version_for_qualified_imported_type_notifies_type_name_cannot_be_resolved) { auto package{createSynchronizationPackageWithVersions()}; storage.synchronize(package); @@ -4806,8 +5051,9 @@ TEST_F(ProjectStorage, fetch_different_major_version_for_qualified_imported_type sourceId2, {Storage::Synchronization::ExportedType{qtQuickModuleId, "Item", Storage::Version{}}}}; - ASSERT_THROW(storage.synchronize(SynchronizationPackage{{import}, {type}, {sourceId2}}), - QmlDesigner::TypeNameDoesNotExists); + EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("Obj"), sourceId2)); + + storage.synchronize(SynchronizationPackage{{import}, {type}, {sourceId2}}); } TEST_F(ProjectStorage, fetch_other_type_by_different_version_for_imported_type) @@ -7248,6 +7494,46 @@ TEST_F(ProjectStorage, synchronize_document_imports_adds_import) ASSERT_TRUE(storage.importId(imports.back())); } +TEST_F(ProjectStorage, + synchronize_document_imports_removes_import_notifies_that_type_name_cannot_be_resolved) +{ + auto package{createVerySimpleSynchronizationPackage()}; + package.imports.emplace_back(qmlModuleId, Storage::Version{}, sourceId1); + package.types[0].prototype = Storage::Synchronization::ImportedType{"Object"}; + storage.synchronize(package); + + EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("Object"), sourceId1)); + + storage.synchronizeDocumentImports({}, sourceId1); +} + +TEST_F(ProjectStorage, synchronize_document_imports_removes_import_which_makes_prototype_unresolved) +{ + auto package{createVerySimpleSynchronizationPackage()}; + package.imports.emplace_back(qmlModuleId, Storage::Version{}, sourceId1); + package.types[0].prototype = Storage::Synchronization::ImportedType{"Object"}; + storage.synchronize(package); + + storage.synchronizeDocumentImports({}, sourceId1); + + ASSERT_THAT(storage.fetchTypeByTypeId(fetchTypeId(sourceId1, "QQuickItem")), + IsPrototypeId(IsUnresolvedTypeId())); +} + +TEST_F(ProjectStorage, synchronize_document_imports_adds_import_which_makes_prototype_resolved) +{ + auto package{createVerySimpleSynchronizationPackage()}; + package.types[0].prototype = Storage::Synchronization::ImportedType{"Object"}; + storage.synchronize(package); + Storage::Imports imports; + imports.emplace_back(qmlModuleId, Storage::Version{}, sourceId1); + + storage.synchronizeDocumentImports(imports, sourceId1); + + ASSERT_THAT(storage.fetchTypeByTypeId(fetchTypeId(sourceId1, "QQuickItem")), + IsPrototypeId(fetchTypeId(sourceId2, "QObject"))); +} + TEST_F(ProjectStorage, get_exported_type_names) { auto package{createSimpleSynchronizationPackage()}; @@ -7996,4 +8282,105 @@ TEST_F(ProjectStorage, get_no_hair_ids_for_invalid_type_id) ASSERT_THAT(heirIds, IsEmpty()); } + +TEST_F(ProjectStorage, + removed_document_import_notifies_for_prototypes_that_type_name_cannot_be_resolved) +{ + auto package{createVerySimpleSynchronizationPackage()}; + package.moduleDependencies.emplace_back(qmlNativeModuleId, Storage::Version{}, sourceId1); + package.types[0].prototype = Storage::Synchronization::ImportedType{"QObject"}; + storage.synchronize(package); + package.moduleDependencies.clear(); + package.types.clear(); + package.updatedSourceIds.clear(); + package.updatedModuleDependencySourceIds = {sourceId1}; + + EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("QObject"), sourceId1)); + + storage.synchronize(package); +} + +TEST_F(ProjectStorage, + removed_document_import_notifies_for_extensions_that_type_name_cannot_be_resolved) +{ + auto package{createVerySimpleSynchronizationPackage()}; + package.moduleDependencies.emplace_back(qmlNativeModuleId, Storage::Version{}, sourceId1); + package.types[0].extension = Storage::Synchronization::ImportedType{"QObject"}; + storage.synchronize(package); + package.moduleDependencies.clear(); + package.types.clear(); + package.updatedSourceIds.clear(); + package.updatedModuleDependencySourceIds = {sourceId1}; + + EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("QObject"), sourceId1)); + + storage.synchronize(package); +} + +TEST_F(ProjectStorage, removed_document_import_changes_prototype_to_unresolved) +{ + auto package{createVerySimpleSynchronizationPackage()}; + package.moduleDependencies.emplace_back(qmlNativeModuleId, Storage::Version{}, sourceId1); + package.types[0].prototype = Storage::Synchronization::ImportedType{"QObject"}; + storage.synchronize(package); + package.moduleDependencies.clear(); + package.types.clear(); + package.updatedSourceIds.clear(); + package.updatedModuleDependencySourceIds = {sourceId1}; + + storage.synchronize(package); + + ASSERT_THAT(storage.fetchTypeByTypeId(fetchTypeId(sourceId1, "QQuickItem")), + Field(&Storage::Synchronization::Type::prototypeId, IsUnresolvedTypeId())); +} + +TEST_F(ProjectStorage, removed_document_import_changes_extension_to_unresolved) +{ + auto package{createVerySimpleSynchronizationPackage()}; + package.moduleDependencies.emplace_back(qmlNativeModuleId, Storage::Version{}, sourceId1); + package.types[0].extension = Storage::Synchronization::ImportedType{"QObject"}; + storage.synchronize(package); + package.moduleDependencies.clear(); + package.types.clear(); + package.updatedSourceIds.clear(); + package.updatedModuleDependencySourceIds = {sourceId1}; + + storage.synchronize(package); + + ASSERT_THAT(storage.fetchTypeByTypeId(fetchTypeId(sourceId1, "QQuickItem")), + Field(&Storage::Synchronization::Type::extensionId, IsUnresolvedTypeId())); +} + +TEST_F(ProjectStorage, added_document_import_fixes_unresolved_prototype) +{ + auto package{createVerySimpleSynchronizationPackage()}; + package.types[0].prototype = Storage::Synchronization::ImportedType{"QObject"}; + storage.synchronize(package); + package.moduleDependencies.emplace_back(qmlNativeModuleId, Storage::Version{}, sourceId1); + package.types.clear(); + package.updatedSourceIds.clear(); + package.updatedModuleDependencySourceIds = {sourceId1}; + + storage.synchronize(package); + + ASSERT_THAT(storage.fetchTypeByTypeId(fetchTypeId(sourceId1, "QQuickItem")), + IsPrototypeId(fetchTypeId(sourceId2, "QObject"))); +} + +TEST_F(ProjectStorage, added_document_import_fixes_unresolved_extension) +{ + auto package{createVerySimpleSynchronizationPackage()}; + package.types[0].extension = Storage::Synchronization::ImportedType{"QObject"}; + storage.synchronize(package); + package.moduleDependencies.emplace_back(qmlNativeModuleId, Storage::Version{}, sourceId1); + package.types.clear(); + package.updatedSourceIds.clear(); + package.updatedModuleDependencySourceIds = {sourceId1}; + + storage.synchronize(package); + + ASSERT_THAT(storage.fetchTypeByTypeId(fetchTypeId(sourceId1, "QQuickItem")), + IsExtensionId(fetchTypeId(sourceId2, "QObject"))); +} + } // namespace diff --git a/tests/unit/tests/unittests/sqlite/sqlitestatement-test.cpp b/tests/unit/tests/unittests/sqlite/sqlitestatement-test.cpp index f533c651e96..24b20176eaa 100644 --- a/tests/unit/tests/unittests/sqlite/sqlitestatement-test.cpp +++ b/tests/unit/tests/unittests/sqlite/sqlitestatement-test.cpp @@ -269,6 +269,21 @@ TEST_F(SqliteStatement, bind_int_id) ASSERT_THAT(readStatement.fetchIntValue(0), 42); } +TEST_F(SqliteStatement, bind_special_state_id) +{ + enum class SpecialIdState { Unresolved = -1 }; + constexpr TestIntId unresolvedTypeId = TestIntId::createSpecialState(SpecialIdState::Unresolved); + SqliteTestStatement<0, 1> statement("INSERT INTO test VALUES ('id', 323, ?)", database); + + statement.bind(1, unresolvedTypeId); + statement.next(); + + SqliteTestStatement<1, 1> readStatement("SELECT value FROM test WHERE name='id'", database); + readStatement.next(); + ASSERT_THAT(readStatement.fetchType(0), Sqlite::Type::Integer); + ASSERT_THAT(readStatement.fetchIntValue(0), -1); +} + TEST_F(SqliteStatement, bind_invalid_long_long_id_to_null) { TestLongLongId id; From fd8cef3c6e8e9fbe408ee7a0122403cdf3a99323 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Fri, 24 May 2024 13:42:38 +0200 Subject: [PATCH 147/154] QmlDesigner: Use the noexcept interface for optional and unexpected Optional and unexpected have a interface designed after pointer usage. That cannot throw exceptions. They added continuation too. That is making it harder to make mistakes. Change-Id: Ie9d41a2e69c7467a7e4ac4999825aede1326e529 Reviewed-by: Tim Jenssen --- .../components/curveeditor/detail/keyframeitem.cpp | 2 +- .../qmldesigner/components/integration/designdocument.cpp | 7 +++---- .../qmldesigner/designercore/generatedcomponentutils.cpp | 2 +- .../qmldesigner/designercore/model/qmlvisualnode.cpp | 4 ++-- 4 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/plugins/qmldesigner/components/curveeditor/detail/keyframeitem.cpp b/src/plugins/qmldesigner/components/curveeditor/detail/keyframeitem.cpp index a1c229f57e2..159e7c31ee1 100644 --- a/src/plugins/qmldesigner/components/curveeditor/detail/keyframeitem.cpp +++ b/src/plugins/qmldesigner/components/curveeditor/detail/keyframeitem.cpp @@ -422,7 +422,7 @@ QVariant KeyframeItem::itemChange(QGraphicsItem::GraphicsItemChange change, cons rseg.moveLeftTo(position); if (legalLeft() && legalRight()) { - if (qApp->keyboardModifiers().testFlag(Qt::ShiftModifier) && m_validPos.has_value()) { + if (qApp->keyboardModifiers().testFlag(Qt::ShiftModifier) && m_validPos) { if (m_firstPos) { auto firstToNow = QLineF(*m_firstPos, position); if (std::abs(firstToNow.dx()) > std::abs(firstToNow.dy())) diff --git a/src/plugins/qmldesigner/components/integration/designdocument.cpp b/src/plugins/qmldesigner/components/integration/designdocument.cpp index 0b175916c9f..aa2dfd3b28a 100644 --- a/src/plugins/qmldesigner/components/integration/designdocument.cpp +++ b/src/plugins/qmldesigner/components/integration/designdocument.cpp @@ -284,11 +284,10 @@ void DesignDocument::moveNodesToPosition(const QList &nodes, const st parentProperty.reparentHere(pastedNode); QmlVisualNode visualNode(pastedNode); - if (!firstVisualNode.has_value() && visualNode.isValid()){ + if (!firstVisualNode && visualNode) { firstVisualNode = visualNode; - translationVect = (position.has_value() && firstVisualNode.has_value()) - ? position.value() - firstVisualNode->position() - : QVector3D(); + translationVect = (position && firstVisualNode) ? *position - firstVisualNode->position() + : QVector3D(); } visualNode.translate(translationVect); } diff --git a/src/plugins/qmldesigner/designercore/generatedcomponentutils.cpp b/src/plugins/qmldesigner/designercore/generatedcomponentutils.cpp index a04713a884a..da40c4d387d 100644 --- a/src/plugins/qmldesigner/designercore/generatedcomponentutils.cpp +++ b/src/plugins/qmldesigner/designercore/generatedcomponentutils.cpp @@ -18,7 +18,7 @@ bool couldBeProjectModule(const Utils::FilePath &path, const QString &projectNam return false; const QString expectedLine = QLatin1String("module %1").arg(projectName); - QByteArray fileContents = qmldirContents.value(); + QByteArray fileContents = *qmldirContents; QTextStream stream(fileContents); while (!stream.atEnd()) { QString lineData = stream.readLine().trimmed(); diff --git a/src/plugins/qmldesigner/designercore/model/qmlvisualnode.cpp b/src/plugins/qmldesigner/designercore/model/qmlvisualnode.cpp index 0c51c9a341e..726d3d52af4 100644 --- a/src/plugins/qmldesigner/designercore/model/qmlvisualnode.cpp +++ b/src/plugins/qmldesigner/designercore/model/qmlvisualnode.cpp @@ -184,8 +184,8 @@ void QmlVisualNode::scatter(const ModelNode &targetNode, const std::optionaltranslate(QVector3D(offsetValue, offsetValue, offsetValue)); } else { // scatter in range const double scatterRange = 20.; From 72df0a9cededa4ddedc4a3fb3efd913e83728088 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Tue, 28 May 2024 11:53:35 +0200 Subject: [PATCH 148/154] QmlDesigner: Fix wild casts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Never use a cast for a dependency injection. If it can not be null use a reference. Task-number: QDS-12883 Change-Id: Ie63a4968f264587a8781f04fa10048e1a7371e44 Reviewed-by: Henning Gründl --- .../connectioneditor/bindingmodel.cpp | 28 ++++++----------- .../connectioneditor/bindingmodel.h | 3 +- .../dynamicpropertiesmodel.cpp | 31 +++++++------------ .../connectioneditor/dynamicpropertiesmodel.h | 3 +- 4 files changed, 24 insertions(+), 41 deletions(-) diff --git a/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.cpp b/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.cpp index f871bae84b4..2cee7b0f977 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.cpp @@ -22,6 +22,7 @@ namespace QmlDesigner { BindingModel::BindingModel(ConnectionView *view) : m_connectionView(view) + , m_delegate(*this) { setHorizontalHeaderLabels(BindingModelItem::headerLabels()); } @@ -246,11 +247,8 @@ void BindingModel::addModelNode(const ModelNode &node) appendRow(new BindingModelItem(property)); } -BindingModelBackendDelegate::BindingModelBackendDelegate() - : m_targetNode() - , m_property() - , m_sourceNode() - , m_sourceNodeProperty() +BindingModelBackendDelegate::BindingModelBackendDelegate(BindingModel &model) + : m_model{model} { connect(&m_sourceNode, &StudioQmlComboBoxBackend::activated, this, [this] { sourceNodeChanged(); @@ -322,17 +320,14 @@ StudioQmlComboBoxBackend *BindingModelBackendDelegate::sourceProperty() void BindingModelBackendDelegate::sourceNodeChanged() { - BindingModel *model = qobject_cast(parent()); - QTC_ASSERT(model, return); - - ConnectionView *view = model->connectionView(); + ConnectionView *view = m_model.connectionView(); QTC_ASSERT(view, return); QTC_ASSERT(view->isAttached(), return ); const QString sourceNode = m_sourceNode.currentText(); const QString sourceProperty = m_sourceNodeProperty.currentText(); - BindingProperty targetProperty = model->currentProperty(); + BindingProperty targetProperty = m_model.currentProperty(); QStringList properties = availableSourceProperties(sourceNode, targetProperty, view); if (!properties.contains(sourceProperty)) { @@ -351,9 +346,6 @@ void BindingModelBackendDelegate::sourcePropertyNameChanged() const return; auto commit = [this, sourceProperty]() { - BindingModel *model = qobject_cast(parent()); - QTC_ASSERT(model, return); - const QString sourceNode = m_sourceNode.currentText(); QString expression; if (sourceProperty.isEmpty()) @@ -361,8 +353,8 @@ void BindingModelBackendDelegate::sourcePropertyNameChanged() const else expression = sourceNode + QLatin1String(".") + sourceProperty; - int row = model->currentIndex(); - model->commitExpression(row, expression); + int row = m_model.currentIndex(); + m_model.commitExpression(row, expression); }; callLater(commit); @@ -371,11 +363,9 @@ void BindingModelBackendDelegate::sourcePropertyNameChanged() const void BindingModelBackendDelegate::targetPropertyNameChanged() const { auto commit = [this] { - BindingModel *model = qobject_cast(parent()); - QTC_ASSERT(model, return); const PropertyName propertyName = m_property.currentText().toUtf8(); - int row = model->currentIndex(); - model->commitPropertyName(row, propertyName); + int row = m_model.currentIndex(); + m_model.commitPropertyName(row, propertyName); }; callLater(commit); diff --git a/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.h b/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.h index b57cc5c9587..69b137e78a5 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.h +++ b/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.h @@ -30,7 +30,7 @@ signals: void targetNodeChanged(); public: - BindingModelBackendDelegate(); + BindingModelBackendDelegate(class BindingModel &model); void update(const BindingProperty &property, AbstractView *view); @@ -44,6 +44,7 @@ private: StudioQmlComboBoxBackend *sourceNode(); StudioQmlComboBoxBackend *sourceProperty(); + BindingModel &m_model; QString m_targetNode; StudioQmlComboBoxBackend m_property; StudioQmlComboBoxBackend m_sourceNode; diff --git a/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.cpp b/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.cpp index 9fdd3daec31..fa29c6c8a1b 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.cpp @@ -25,7 +25,7 @@ namespace QmlDesigner { DynamicPropertiesModel::DynamicPropertiesModel(bool exSelection, AbstractView *view) : m_view(view) - , m_delegate(std::make_unique()) + , m_delegate(std::make_unique(*this)) , m_explicitSelection(exSelection) { setHorizontalHeaderLabels(DynamicPropertiesItem::headerLabels()); @@ -382,8 +382,8 @@ void DynamicPropertiesModel::setSelectedNode(const ModelNode &node) reset(); } -DynamicPropertiesModelBackendDelegate::DynamicPropertiesModelBackendDelegate() - : m_internalNodeId(std::nullopt) +DynamicPropertiesModelBackendDelegate::DynamicPropertiesModelBackendDelegate(DynamicPropertiesModel &model) + : m_model(model) { m_type.setModel({"int", "bool", "var", "real", "string", "url", "color"}); connect(&m_type, &StudioQmlComboBoxBackend::activated, this, [this] { handleTypeChanged(); }); @@ -411,32 +411,26 @@ void DynamicPropertiesModelBackendDelegate::update(const AbstractProperty &prope void DynamicPropertiesModelBackendDelegate::handleTypeChanged() { - DynamicPropertiesModel *model = qobject_cast(parent()); - QTC_ASSERT(model, return); - const PropertyName name = m_name.text().toUtf8(); - int current = model->currentIndex(); + int current = m_model.currentIndex(); const TypeName type = m_type.currentText().toUtf8(); - model->commitPropertyType(current, type); + m_model.commitPropertyType(current, type); // The order might have changed! - model->setCurrent(m_internalNodeId.value_or(-1), name); + m_model.setCurrent(m_internalNodeId.value_or(-1), name); } void DynamicPropertiesModelBackendDelegate::handleNameChanged() { - DynamicPropertiesModel *model = qobject_cast(parent()); - QTC_ASSERT(model, return); - const PropertyName name = m_name.text().toUtf8(); QTC_ASSERT(!name.isEmpty(), return); - int current = model->currentIndex(); - model->commitPropertyName(current, name); + int current = m_model.currentIndex(); + m_model.commitPropertyName(current, name); // The order might have changed! - model->setCurrent(m_internalNodeId.value_or(-1), name); + m_model.setCurrent(m_internalNodeId.value_or(-1), name); } // TODO: Maybe replace with utils typeConvertVariant? @@ -456,12 +450,9 @@ QVariant valueFromText(const QString &value, const QString &type) void DynamicPropertiesModelBackendDelegate::handleValueChanged() { - DynamicPropertiesModel *model = qobject_cast(parent()); - QTC_ASSERT(model, return); - - int current = model->currentIndex(); + int current = m_model.currentIndex(); QVariant value = valueFromText(m_value.text(), m_type.currentText()); - model->commitPropertyValue(current, value); + m_model.commitPropertyValue(current, value); } QString DynamicPropertiesModelBackendDelegate::targetNode() const diff --git a/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.h b/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.h index c2beed87304..071c72bef81 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.h +++ b/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.h @@ -97,7 +97,7 @@ class DynamicPropertiesModelBackendDelegate : public QObject Q_PROPERTY(StudioQmlTextBackend *value READ value CONSTANT) public: - DynamicPropertiesModelBackendDelegate(); + DynamicPropertiesModelBackendDelegate(DynamicPropertiesModel &model); void update(const AbstractProperty &property); @@ -116,6 +116,7 @@ private: StudioQmlTextBackend *value(); QString targetNode() const; + DynamicPropertiesModel &m_model; std::optional m_internalNodeId; StudioQmlComboBoxBackend m_type; StudioQmlTextBackend m_name; From ec2642e362f43d1275650a118df0b6a05ce6aa43 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Tue, 28 May 2024 12:11:48 +0200 Subject: [PATCH 149/154] Utils: Add to_underlying to_underlying was added to C++23 to get the underlying integer type for an enumeration. Change-Id: Ib7262882a47cf4b060cff428bb10a6a65c089fc5 Reviewed-by: Eike Ziller Reviewed-by: Tim Jenssen --- src/libs/nanotrace/nanotracehr.h | 10 ++-------- .../commands/view3dactioncommand.cpp | 11 +++-------- src/libs/sqlite/sqlitebasestatement.h | 2 +- src/libs/sqlite/sqliteids.h | 12 +++--------- src/libs/utils/CMakeLists.txt | 1 + src/libs/utils/utility.h | 14 ++++++++++++++ .../designercore/projectstorage/projectstorage.cpp | 2 +- .../projectstorage/projectstorageinfotypes.h | 8 +------- .../projectstorage/projectstoragetypes.h | 3 ++- 9 files changed, 28 insertions(+), 35 deletions(-) create mode 100644 src/libs/utils/utility.h diff --git a/src/libs/nanotrace/nanotracehr.h b/src/libs/nanotrace/nanotracehr.h index 810e4e3c5d7..15f8109fe91 100644 --- a/src/libs/nanotrace/nanotracehr.h +++ b/src/libs/nanotrace/nanotracehr.h @@ -9,6 +9,7 @@ #include #include +#include #include #include @@ -144,17 +145,10 @@ void convertToString(String &string, Number number) string.append(number); } -template -constexpr std::underlying_type_t to_underlying(Enumeration enumeration) noexcept -{ - static_assert(std::is_enum_v, "to_underlying expect an enumeration"); - return static_cast>(enumeration); -} - template, bool> = true> void convertToString(String &string, Enumeration enumeration) { - string.append(to_underlying(enumeration)); + string.append(Utils::to_underlying(enumeration)); } template diff --git a/src/libs/qmlpuppetcommunication/commands/view3dactioncommand.cpp b/src/libs/qmlpuppetcommunication/commands/view3dactioncommand.cpp index eac0961393a..a522499ffa7 100644 --- a/src/libs/qmlpuppetcommunication/commands/view3dactioncommand.cpp +++ b/src/libs/qmlpuppetcommunication/commands/view3dactioncommand.cpp @@ -3,6 +3,8 @@ #include "view3dactioncommand.h" +#include + #include #include @@ -64,16 +66,9 @@ QDebug operator<<(QDebug debug, const View3DActionCommand &command) << command.m_value << ")\n"; } -template -constexpr std::underlying_type_t to_underlying(Enumeration enumeration) noexcept -{ - static_assert(std::is_enum_v, "to_underlying expect an enumeration"); - return static_cast>(enumeration); -} - QDebug operator<<(QDebug debug, View3DActionType type) { - return debug.nospace() << to_underlying(type); + return debug.nospace() << Utils::to_underlying(type); } } // namespace QmlDesigner diff --git a/src/libs/sqlite/sqlitebasestatement.h b/src/libs/sqlite/sqlitebasestatement.h index 9a4bb0a2a2f..64acba0a27a 100644 --- a/src/libs/sqlite/sqlitebasestatement.h +++ b/src/libs/sqlite/sqlitebasestatement.h @@ -89,7 +89,7 @@ public: template, bool> = true> void bind(int index, Enumeration enumeration) { - bind(index, to_underlying(enumeration)); + bind(index, Utils::to_underlying(enumeration)); } void bind(int index, uint value) { bind(index, static_cast(value)); } diff --git a/src/libs/sqlite/sqliteids.h b/src/libs/sqlite/sqliteids.h index 460cd91415a..abbb13d7c16 100644 --- a/src/libs/sqlite/sqliteids.h +++ b/src/libs/sqlite/sqliteids.h @@ -4,6 +4,7 @@ #pragma once #include +#include #include #include @@ -11,13 +12,6 @@ namespace Sqlite { -template -static constexpr std::underlying_type_t to_underlying(Enumeration enumeration) noexcept -{ - static_assert(std::is_enum_v, "to_underlying expect an enumeration"); - return static_cast>(enumeration); -} - template class BasicId { @@ -38,7 +32,7 @@ public: static constexpr BasicId createSpecialState(Enumeration specialState) { BasicId id; - id.id = ::Sqlite::to_underlying(specialState); + id.id = ::Utils::to_underlying(specialState); return id; } @@ -77,7 +71,7 @@ public: template constexpr bool hasSpecialState(Enumeration specialState) const { - return id == ::Sqlite::to_underlying(specialState); + return id == ::Utils::to_underlying(specialState); } explicit operator bool() const { return isValid(); } diff --git a/src/libs/utils/CMakeLists.txt b/src/libs/utils/CMakeLists.txt index a9912b3575f..30e4be2eaa9 100644 --- a/src/libs/utils/CMakeLists.txt +++ b/src/libs/utils/CMakeLists.txt @@ -197,6 +197,7 @@ add_qtc_library(Utils utilstr.h utilsicons.cpp utilsicons.h utiltypes.h + utility.h variablechooser.cpp variablechooser.h winutils.cpp winutils.h wizard.cpp wizard.h diff --git a/src/libs/utils/utility.h b/src/libs/utils/utility.h new file mode 100644 index 00000000000..27de88d3398 --- /dev/null +++ b/src/libs/utils/utility.h @@ -0,0 +1,14 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +namespace Utils { + +template +[[nodiscard]] constexpr std::underlying_type_t to_underlying(Enumeration enumeration) noexcept +{ + return static_cast>(enumeration); +} + +} // namespace Utils diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.cpp b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.cpp index 2f56d569087..2283b64945a 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.cpp +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.cpp @@ -3894,7 +3894,7 @@ Utils::PathString ProjectStorage::createJson(const Storage::Synchronization::Par json.append("\"}"); } else { json.append(R"(","tr":)"); - json.append(Utils::SmallString::number(to_underlying(parameter.traits))); + json.append(Utils::SmallString::number(Utils::to_underlying(parameter.traits))); json.append("}"); } } diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinfotypes.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinfotypes.h index 75ab0d1f5a5..1d630344aec 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinfotypes.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinfotypes.h @@ -7,6 +7,7 @@ #include #include +#include #include @@ -20,13 +21,6 @@ namespace QmlDesigner { template using SmallPathStrings = QVarLengthArray; -template -constexpr std::underlying_type_t to_underlying(Enumeration enumeration) noexcept -{ - static_assert(std::is_enum_v, "to_underlying expect an enumeration"); - return static_cast>(enumeration); -} - enum class FlagIs : unsigned int { False, Set, True }; template diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstoragetypes.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstoragetypes.h index 5f51a9acf61..1592628af53 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstoragetypes.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstoragetypes.h @@ -10,6 +10,7 @@ #include #include #include +#include #include #include @@ -207,7 +208,7 @@ void convertToString(String &string, const IsAutoVersion &isAutoVersion) constexpr bool operator<(IsAutoVersion first, IsAutoVersion second) { - return to_underlying(first) < to_underlying(second); + return Utils::to_underlying(first) < Utils::to_underlying(second); } class ModuleExportedImport From b01e039cec52517bb60f9e72c068d1ee02b52f6c Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Tue, 28 May 2024 12:58:21 +0200 Subject: [PATCH 150/154] Sqlite: Update to 3.46.0 Change-Id: Ic2b2e75a7b88fa92409118381cf74c00bf120f85 Reviewed-by: Tim Jenssen --- src/libs/3rdparty/sqlite/sqlite3.c | 8471 +++++++++++++++++----------- src/libs/3rdparty/sqlite/sqlite3.h | 97 +- 2 files changed, 5180 insertions(+), 3388 deletions(-) diff --git a/src/libs/3rdparty/sqlite/sqlite3.c b/src/libs/3rdparty/sqlite/sqlite3.c index 08c593e55c7..eaa24a13107 100644 --- a/src/libs/3rdparty/sqlite/sqlite3.c +++ b/src/libs/3rdparty/sqlite/sqlite3.c @@ -1,6 +1,6 @@ /****************************************************************************** ** This file is an amalgamation of many separate C source files from SQLite -** version 3.45.3. By combining all the individual C code files into this +** version 3.46.0. By combining all the individual C code files into this ** single large file, the entire code can be compiled as a single translation ** unit. This allows many compilers to do optimizations that would not be ** possible if the files were compiled separately. Performance improvements @@ -18,7 +18,7 @@ ** separate file. This file contains only code for the core SQLite library. ** ** The content in this amalgamation comes from Fossil check-in -** 8653b758870e6ef0c98d46b3ace27849054a. +** 96c92aba00c8375bc32fafcdf12429c58bd8. */ #define SQLITE_CORE 1 #define SQLITE_AMALGAMATION 1 @@ -459,9 +459,9 @@ extern "C" { ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ -#define SQLITE_VERSION "3.45.3" -#define SQLITE_VERSION_NUMBER 3045003 -#define SQLITE_SOURCE_ID "2024-04-15 13:34:05 8653b758870e6ef0c98d46b3ace27849054af85da891eb121e9aaa537f1e8355" +#define SQLITE_VERSION "3.46.0" +#define SQLITE_VERSION_NUMBER 3046000 +#define SQLITE_SOURCE_ID "2024-05-23 13:25:27 96c92aba00c8375bc32fafcdf12429c58bd8aabfcadab6683e35bbb9cdebf19e" /* ** CAPI3REF: Run-Time Library Version Numbers @@ -1077,11 +1077,11 @@ struct sqlite3_file { ** ** xLock() upgrades the database file lock. In other words, xLock() moves the ** database file lock in the direction NONE toward EXCLUSIVE. The argument to -** xLock() is always on of SHARED, RESERVED, PENDING, or EXCLUSIVE, never +** xLock() is always one of SHARED, RESERVED, PENDING, or EXCLUSIVE, never ** SQLITE_LOCK_NONE. If the database file lock is already at or above the ** requested lock, then the call to xLock() is a no-op. ** xUnlock() downgrades the database file lock to either SHARED or NONE. -* If the lock is already at or below the requested lock state, then the call +** If the lock is already at or below the requested lock state, then the call ** to xUnlock() is a no-op. ** The xCheckReservedLock() method checks whether any database connection, ** either in this process or in some other process, is holding a RESERVED, @@ -3618,8 +3618,8 @@ SQLITE_API int sqlite3_set_authorizer( #define SQLITE_RECURSIVE 33 /* NULL NULL */ /* -** CAPI3REF: Tracing And Profiling Functions -** METHOD: sqlite3 +** CAPI3REF: Deprecated Tracing And Profiling Functions +** DEPRECATED ** ** These routines are deprecated. Use the [sqlite3_trace_v2()] interface ** instead of the routines described here. @@ -7200,6 +7200,12 @@ SQLITE_API int sqlite3_autovacuum_pages( ** The exceptions defined in this paragraph might change in a future ** release of SQLite. ** +** Whether the update hook is invoked before or after the +** corresponding change is currently unspecified and may differ +** depending on the type of change. Do not rely on the order of the +** hook call with regards to the final result of the operation which +** triggers the hook. +** ** The update hook implementation must not do anything that will modify ** the database connection that invoked the update hook. Any actions ** to modify the database connection must be deferred until after the @@ -8670,7 +8676,7 @@ SQLITE_API int sqlite3_test_control(int op, ...); ** The sqlite3_keyword_count() interface returns the number of distinct ** keywords understood by SQLite. ** -** The sqlite3_keyword_name(N,Z,L) interface finds the N-th keyword and +** The sqlite3_keyword_name(N,Z,L) interface finds the 0-based N-th keyword and ** makes *Z point to that keyword expressed as UTF8 and writes the number ** of bytes in the keyword into *L. The string that *Z points to is not ** zero-terminated. The sqlite3_keyword_name(N,Z,L) routine returns @@ -10249,24 +10255,45 @@ SQLITE_API const char *sqlite3_vtab_collation(sqlite3_index_info*,int); **
  • ** ^(If the sqlite3_vtab_distinct() interface returns 2, that means ** that the query planner does not need the rows returned in any particular -** order, as long as rows with the same values in all "aOrderBy" columns -** are adjacent.)^ ^(Furthermore, only a single row for each particular -** combination of values in the columns identified by the "aOrderBy" field -** needs to be returned.)^ ^It is always ok for two or more rows with the same -** values in all "aOrderBy" columns to be returned, as long as all such rows -** are adjacent. ^The virtual table may, if it chooses, omit extra rows -** that have the same value for all columns identified by "aOrderBy". -** ^However omitting the extra rows is optional. +** order, as long as rows with the same values in all columns identified +** by "aOrderBy" are adjacent.)^ ^(Furthermore, when two or more rows +** contain the same values for all columns identified by "colUsed", all but +** one such row may optionally be omitted from the result.)^ +** The virtual table is not required to omit rows that are duplicates +** over the "colUsed" columns, but if the virtual table can do that without +** too much extra effort, it could potentially help the query to run faster. ** This mode is used for a DISTINCT query. **

  • -** ^(If the sqlite3_vtab_distinct() interface returns 3, that means -** that the query planner needs only distinct rows but it does need the -** rows to be sorted.)^ ^The virtual table implementation is free to omit -** rows that are identical in all aOrderBy columns, if it wants to, but -** it is not required to omit any rows. This mode is used for queries +** ^(If the sqlite3_vtab_distinct() interface returns 3, that means the +** virtual table must return rows in the order defined by "aOrderBy" as +** if the sqlite3_vtab_distinct() interface had returned 0. However if +** two or more rows in the result have the same values for all columns +** identified by "colUsed", then all but one such row may optionally be +** omitted.)^ Like when the return value is 2, the virtual table +** is not required to omit rows that are duplicates over the "colUsed" +** columns, but if the virtual table can do that without +** too much extra effort, it could potentially help the query to run faster. +** This mode is used for queries ** that have both DISTINCT and ORDER BY clauses. ** ** +**

    The following table summarizes the conditions under which the +** virtual table is allowed to set the "orderByConsumed" flag based on +** the value returned by sqlite3_vtab_distinct(). This table is a +** restatement of the previous four paragraphs: +** +** +** +**
    sqlite3_vtab_distinct() return value +** Rows are returned in aOrderBy order +** Rows with the same value in all aOrderBy columns are adjacent +** Duplicates over all colUsed columns may be omitted +**
    0yesyesno +**
    1noyesno +**
    2noyesyes +**
    3yesyesyes +**
    +** ** ^For the purposes of comparing virtual table output values to see if the ** values are same value for sorting purposes, two NULL values are considered ** to be the same. In other words, the comparison operator is "IS" @@ -12311,6 +12338,30 @@ SQLITE_API int sqlite3changegroup_schema(sqlite3_changegroup*, sqlite3*, const c */ SQLITE_API int sqlite3changegroup_add(sqlite3_changegroup*, int nData, void *pData); +/* +** CAPI3REF: Add A Single Change To A Changegroup +** METHOD: sqlite3_changegroup +** +** This function adds the single change currently indicated by the iterator +** passed as the second argument to the changegroup object. The rules for +** adding the change are just as described for [sqlite3changegroup_add()]. +** +** If the change is successfully added to the changegroup, SQLITE_OK is +** returned. Otherwise, an SQLite error code is returned. +** +** The iterator must point to a valid entry when this function is called. +** If it does not, SQLITE_ERROR is returned and no change is added to the +** changegroup. Additionally, the iterator must not have been opened with +** the SQLITE_CHANGESETAPPLY_INVERT flag. In this case SQLITE_ERROR is also +** returned. +*/ +SQLITE_API int sqlite3changegroup_add_change( + sqlite3_changegroup*, + sqlite3_changeset_iter* +); + + + /* ** CAPI3REF: Obtain A Composite Changeset From A Changegroup ** METHOD: sqlite3_changegroup @@ -13115,8 +13166,8 @@ struct Fts5PhraseIter { ** EXTENSION API FUNCTIONS ** ** xUserData(pFts): -** Return a copy of the context pointer the extension function was -** registered with. +** Return a copy of the pUserData pointer passed to the xCreateFunction() +** API when the extension function was registered. ** ** xColumnTotalSize(pFts, iCol, pnToken): ** If parameter iCol is less than zero, set output variable *pnToken @@ -14314,6 +14365,8 @@ struct fts5_api { # define SQLITE_OMIT_ALTERTABLE #endif +#define SQLITE_DIGIT_SEPARATOR '_' + /* ** Return true (non-zero) if the input is an integer that is too large ** to fit in 32-bits. This macro is used inside of various testcase() @@ -14606,8 +14659,8 @@ SQLITE_PRIVATE void sqlite3HashClear(Hash*); #define TK_TRUEFALSE 170 #define TK_ISNOT 171 #define TK_FUNCTION 172 -#define TK_UMINUS 173 -#define TK_UPLUS 174 +#define TK_UPLUS 173 +#define TK_UMINUS 174 #define TK_TRUTH 175 #define TK_REGISTER 176 #define TK_VECTOR 177 @@ -14616,8 +14669,9 @@ SQLITE_PRIVATE void sqlite3HashClear(Hash*); #define TK_ASTERISK 180 #define TK_SPAN 181 #define TK_ERROR 182 -#define TK_SPACE 183 -#define TK_ILLEGAL 184 +#define TK_QNUMBER 183 +#define TK_SPACE 184 +#define TK_ILLEGAL 185 /************** End of parse.h ***********************************************/ /************** Continuing where we left off in sqliteInt.h ******************/ @@ -14879,7 +14933,7 @@ typedef INT16_TYPE LogEst; # define SQLITE_PTRSIZE __SIZEOF_POINTER__ # elif defined(i386) || defined(__i386__) || defined(_M_IX86) || \ defined(_M_ARM) || defined(__arm__) || defined(__x86) || \ - (defined(__APPLE__) && defined(__POWERPC__)) || \ + (defined(__APPLE__) && defined(__ppc__)) || \ (defined(__TOS_AIX__) && !defined(__64BIT__)) # define SQLITE_PTRSIZE 4 # else @@ -15147,7 +15201,7 @@ SQLITE_PRIVATE u32 sqlite3WhereTrace; ** 0x00000010 Display sqlite3_index_info xBestIndex calls ** 0x00000020 Range an equality scan metrics ** 0x00000040 IN operator decisions -** 0x00000080 WhereLoop cost adjustements +** 0x00000080 WhereLoop cost adjustments ** 0x00000100 ** 0x00000200 Covering index decisions ** 0x00000400 OR optimization @@ -16296,6 +16350,7 @@ SQLITE_PRIVATE int sqlite3BtreeIntegrityCheck( sqlite3 *db, /* Database connection that is running the check */ Btree *p, /* The btree to be checked */ Pgno *aRoot, /* An array of root pages numbers for individual trees */ + sqlite3_value *aCnt, /* OUT: entry counts for each btree in aRoot[] */ int nRoot, /* Number of entries in aRoot[] */ int mxErr, /* Stop reporting errors after this many */ int *pnErr, /* OUT: Write number of errors seen to this variable */ @@ -16566,12 +16621,12 @@ typedef struct VdbeOpList VdbeOpList; #define OP_Vacuum 5 #define OP_VFilter 6 /* jump, synopsis: iplan=r[P3] zplan='P4' */ #define OP_VUpdate 7 /* synopsis: data=r[P3@P2] */ -#define OP_Init 8 /* jump, synopsis: Start at P2 */ +#define OP_Init 8 /* jump0, synopsis: Start at P2 */ #define OP_Goto 9 /* jump */ #define OP_Gosub 10 /* jump */ -#define OP_InitCoroutine 11 /* jump */ -#define OP_Yield 12 /* jump */ -#define OP_MustBeInt 13 /* jump */ +#define OP_InitCoroutine 11 /* jump0 */ +#define OP_Yield 12 /* jump0 */ +#define OP_MustBeInt 13 /* jump0 */ #define OP_Jump 14 /* jump */ #define OP_Once 15 /* jump */ #define OP_If 16 /* jump */ @@ -16579,22 +16634,22 @@ typedef struct VdbeOpList VdbeOpList; #define OP_IsType 18 /* jump, synopsis: if typeof(P1.P3) in P5 goto P2 */ #define OP_Not 19 /* same as TK_NOT, synopsis: r[P2]= !r[P1] */ #define OP_IfNullRow 20 /* jump, synopsis: if P1.nullRow then r[P3]=NULL, goto P2 */ -#define OP_SeekLT 21 /* jump, synopsis: key=r[P3@P4] */ -#define OP_SeekLE 22 /* jump, synopsis: key=r[P3@P4] */ -#define OP_SeekGE 23 /* jump, synopsis: key=r[P3@P4] */ -#define OP_SeekGT 24 /* jump, synopsis: key=r[P3@P4] */ +#define OP_SeekLT 21 /* jump0, synopsis: key=r[P3@P4] */ +#define OP_SeekLE 22 /* jump0, synopsis: key=r[P3@P4] */ +#define OP_SeekGE 23 /* jump0, synopsis: key=r[P3@P4] */ +#define OP_SeekGT 24 /* jump0, synopsis: key=r[P3@P4] */ #define OP_IfNotOpen 25 /* jump, synopsis: if( !csr[P1] ) goto P2 */ #define OP_IfNoHope 26 /* jump, synopsis: key=r[P3@P4] */ #define OP_NoConflict 27 /* jump, synopsis: key=r[P3@P4] */ #define OP_NotFound 28 /* jump, synopsis: key=r[P3@P4] */ #define OP_Found 29 /* jump, synopsis: key=r[P3@P4] */ -#define OP_SeekRowid 30 /* jump, synopsis: intkey=r[P3] */ +#define OP_SeekRowid 30 /* jump0, synopsis: intkey=r[P3] */ #define OP_NotExists 31 /* jump, synopsis: intkey=r[P3] */ -#define OP_Last 32 /* jump */ -#define OP_IfSmaller 33 /* jump */ +#define OP_Last 32 /* jump0 */ +#define OP_IfSizeBetween 33 /* jump */ #define OP_SorterSort 34 /* jump */ #define OP_Sort 35 /* jump */ -#define OP_Rewind 36 /* jump */ +#define OP_Rewind 36 /* jump0 */ #define OP_SorterNext 37 /* jump */ #define OP_Prev 38 /* jump */ #define OP_Next 39 /* jump */ @@ -16606,7 +16661,7 @@ typedef struct VdbeOpList VdbeOpList; #define OP_IdxGE 45 /* jump, synopsis: key=r[P3@P4] */ #define OP_RowSetRead 46 /* jump, synopsis: r[P3]=rowset(P1) */ #define OP_RowSetTest 47 /* jump, synopsis: if r[P3] in rowset(P1) goto P2 */ -#define OP_Program 48 /* jump */ +#define OP_Program 48 /* jump0 */ #define OP_FkIfZero 49 /* jump, synopsis: if fkctr[P1]==0 goto P2 */ #define OP_IsNull 50 /* jump, same as TK_ISNULL, synopsis: if r[P1]==NULL goto P2 */ #define OP_NotNull 51 /* jump, same as TK_NOTNULL, synopsis: if r[P1]!=NULL goto P2 */ @@ -16636,7 +16691,7 @@ typedef struct VdbeOpList VdbeOpList; #define OP_Null 75 /* synopsis: r[P2..P3]=NULL */ #define OP_SoftNull 76 /* synopsis: r[P1]=NULL */ #define OP_Blob 77 /* synopsis: r[P2]=P4 (len=P1) */ -#define OP_Variable 78 /* synopsis: r[P2]=parameter(P1,P4) */ +#define OP_Variable 78 /* synopsis: r[P2]=parameter(P1) */ #define OP_Move 79 /* synopsis: r[P2@P3]=r[P1@P3] */ #define OP_Copy 80 /* synopsis: r[P2@P3+1]=r[P1@P3+1] */ #define OP_SCopy 81 /* synopsis: r[P2]=r[P1] */ @@ -16760,14 +16815,15 @@ typedef struct VdbeOpList VdbeOpList; #define OPFLG_OUT2 0x10 /* out2: P2 is an output */ #define OPFLG_OUT3 0x20 /* out3: P3 is an output */ #define OPFLG_NCYCLE 0x40 /* ncycle:Cycles count against P1 */ +#define OPFLG_JUMP0 0x80 /* jump0: P2 might be zero */ #define OPFLG_INITIALIZER {\ /* 0 */ 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x41, 0x00,\ -/* 8 */ 0x01, 0x01, 0x01, 0x01, 0x03, 0x03, 0x01, 0x01,\ -/* 16 */ 0x03, 0x03, 0x01, 0x12, 0x01, 0x49, 0x49, 0x49,\ -/* 24 */ 0x49, 0x01, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49,\ -/* 32 */ 0x41, 0x01, 0x41, 0x41, 0x41, 0x01, 0x41, 0x41,\ +/* 8 */ 0x81, 0x01, 0x01, 0x81, 0x83, 0x83, 0x01, 0x01,\ +/* 16 */ 0x03, 0x03, 0x01, 0x12, 0x01, 0xc9, 0xc9, 0xc9,\ +/* 24 */ 0xc9, 0x01, 0x49, 0x49, 0x49, 0x49, 0xc9, 0x49,\ +/* 32 */ 0xc1, 0x01, 0x41, 0x41, 0xc1, 0x01, 0x41, 0x41,\ /* 40 */ 0x41, 0x41, 0x41, 0x26, 0x26, 0x41, 0x23, 0x0b,\ -/* 48 */ 0x01, 0x01, 0x03, 0x03, 0x0b, 0x0b, 0x0b, 0x0b,\ +/* 48 */ 0x81, 0x01, 0x03, 0x03, 0x0b, 0x0b, 0x0b, 0x0b,\ /* 56 */ 0x0b, 0x0b, 0x01, 0x03, 0x03, 0x03, 0x01, 0x41,\ /* 64 */ 0x01, 0x00, 0x00, 0x02, 0x02, 0x08, 0x00, 0x10,\ /* 72 */ 0x10, 0x10, 0x00, 0x10, 0x00, 0x10, 0x10, 0x00,\ @@ -16927,6 +16983,8 @@ SQLITE_PRIVATE RecordCompare sqlite3VdbeFindCompare(UnpackedRecord*); SQLITE_PRIVATE void sqlite3VdbeLinkSubProgram(Vdbe *, SubProgram *); SQLITE_PRIVATE int sqlite3VdbeHasSubProgram(Vdbe*); +SQLITE_PRIVATE void sqlite3MemSetArrayInt64(sqlite3_value *aMem, int iIdx, i64 val); + SQLITE_PRIVATE int sqlite3NotPureFunc(sqlite3_context*); #ifdef SQLITE_ENABLE_BYTECODE_VTAB SQLITE_PRIVATE int sqlite3VdbeBytecodeVtabInit(sqlite3*); @@ -17514,6 +17572,10 @@ struct FuncDefHash { }; #define SQLITE_FUNC_HASH(C,L) (((C)+(L))%SQLITE_FUNC_HASH_SZ) +#if defined(SQLITE_USER_AUTHENTICATION) +# warning "The SQLITE_USER_AUTHENTICATION extension is deprecated. \ + See ext/userauth/user-auth.txt for details." +#endif #ifdef SQLITE_USER_AUTHENTICATION /* ** Information held in the "sqlite3" database connection object and used @@ -17817,7 +17879,7 @@ struct sqlite3 { #define SQLITE_CursorHints 0x00000400 /* Add OP_CursorHint opcodes */ #define SQLITE_Stat4 0x00000800 /* Use STAT4 data */ /* TH3 expects this value ^^^^^^^^^^ to be 0x0000800. Don't change it */ -#define SQLITE_PushDown 0x00001000 /* The push-down optimization */ +#define SQLITE_PushDown 0x00001000 /* WHERE-clause push-down opt */ #define SQLITE_SimplifyJoin 0x00002000 /* Convert LEFT JOIN to JOIN */ #define SQLITE_SkipScan 0x00004000 /* Skip-scans */ #define SQLITE_PropagateConst 0x00008000 /* The constant propagation opt */ @@ -18390,8 +18452,7 @@ struct Table { #define TF_HasStored 0x00000040 /* Has one or more STORED columns */ #define TF_HasGenerated 0x00000060 /* Combo: HasVirtual + HasStored */ #define TF_WithoutRowid 0x00000080 /* No rowid. PRIMARY KEY is the key */ -#define TF_StatsUsed 0x00000100 /* Query planner decisions affected by - ** Index.aiRowLogEst[] values */ +#define TF_MaybeReanalyze 0x00000100 /* Maybe run ANALYZE on this table */ #define TF_NoVisibleRowid 0x00000200 /* No user-visible "rowid" column */ #define TF_OOOHidden 0x00000400 /* Out-of-Order hidden columns */ #define TF_HasNotNull 0x00000800 /* Contains NOT NULL constraints */ @@ -19191,10 +19252,12 @@ struct IdList { ** ** Union member validity: ** -** u1.zIndexedBy fg.isIndexedBy && !fg.isTabFunc -** u1.pFuncArg fg.isTabFunc && !fg.isIndexedBy -** u2.pIBIndex fg.isIndexedBy && !fg.isCte -** u2.pCteUse fg.isCte && !fg.isIndexedBy +** u1.zIndexedBy fg.isIndexedBy && !fg.isTabFunc +** u1.pFuncArg fg.isTabFunc && !fg.isIndexedBy +** u1.nRow !fg.isTabFunc && !fg.isIndexedBy +** +** u2.pIBIndex fg.isIndexedBy && !fg.isCte +** u2.pCteUse fg.isCte && !fg.isIndexedBy */ struct SrcItem { Schema *pSchema; /* Schema to which this item is fixed */ @@ -19222,6 +19285,7 @@ struct SrcItem { unsigned isOn :1; /* u3.pOn was once valid and non-NULL */ unsigned isSynthUsing :1; /* u3.pUsing is synthesized from NATURAL */ unsigned isNestedFrom :1; /* pSelect is a SF_NestedFrom subquery */ + unsigned rowidUsed :1; /* The ROWID of this table is referenced */ } fg; int iCursor; /* The VDBE cursor number used to access this table */ union { @@ -19232,6 +19296,7 @@ struct SrcItem { union { char *zIndexedBy; /* Identifier from "INDEXED BY " clause */ ExprList *pFuncArg; /* Arguments to table-valued-function */ + u32 nRow; /* Number of rows in a VALUES clause */ } u1; union { Index *pIBIndex; /* Index structure corresponding to u1.zIndexedBy */ @@ -19489,11 +19554,12 @@ struct Select { #define SF_View 0x0200000 /* SELECT statement is a view */ #define SF_NoopOrderBy 0x0400000 /* ORDER BY is ignored for this query */ #define SF_UFSrcCheck 0x0800000 /* Check pSrc as required by UPDATE...FROM */ -#define SF_PushDown 0x1000000 /* SELECT has be modified by push-down opt */ +#define SF_PushDown 0x1000000 /* Modified by WHERE-clause push-down opt */ #define SF_MultiPart 0x2000000 /* Has multiple incompatible PARTITIONs */ #define SF_CopyCte 0x4000000 /* SELECT statement is a copy of a CTE */ #define SF_OrderByReqd 0x8000000 /* The ORDER BY clause may not be omitted */ #define SF_UpdateFrom 0x10000000 /* Query originates with UPDATE FROM */ +#define SF_Correlated 0x20000000 /* True if references the outer context */ /* True if S exists and has SF_NestedFrom */ #define IsNestedFrom(S) ((S)!=0 && ((S)->selFlags&SF_NestedFrom)!=0) @@ -19733,6 +19799,7 @@ struct Parse { u8 disableLookaside; /* Number of times lookaside has been disabled */ u8 prepFlags; /* SQLITE_PREPARE_* flags */ u8 withinRJSubrtn; /* Nesting level for RIGHT JOIN body subroutines */ + u8 bHasWith; /* True if statement contains WITH */ #if defined(SQLITE_DEBUG) || defined(SQLITE_COVERAGE_TEST) u8 earlyCleanup; /* OOM inside sqlite3ParserAddCleanup() */ #endif @@ -20412,6 +20479,9 @@ struct Window { ** due to the SQLITE_SUBTYPE flag */ }; +SQLITE_PRIVATE Select *sqlite3MultiValues(Parse *pParse, Select *pLeft, ExprList *pRow); +SQLITE_PRIVATE void sqlite3MultiValuesEnd(Parse *pParse, Select *pVal); + #ifndef SQLITE_OMIT_WINDOWFUNC SQLITE_PRIVATE void sqlite3WindowDelete(sqlite3*, Window*); SQLITE_PRIVATE void sqlite3WindowUnlinkFromSelect(Window*); @@ -20729,6 +20799,7 @@ SQLITE_PRIVATE int sqlite3ErrorToParser(sqlite3*,int); SQLITE_PRIVATE void sqlite3Dequote(char*); SQLITE_PRIVATE void sqlite3DequoteExpr(Expr*); SQLITE_PRIVATE void sqlite3DequoteToken(Token*); +SQLITE_PRIVATE void sqlite3DequoteNumber(Parse*, Expr*); SQLITE_PRIVATE void sqlite3TokenInit(Token*,char*); SQLITE_PRIVATE int sqlite3KeywordCode(const unsigned char*, int); SQLITE_PRIVATE int sqlite3RunParser(Parse*, const char*); @@ -20759,7 +20830,7 @@ SQLITE_PRIVATE void sqlite3ExprFunctionUsable(Parse*,const Expr*,const FuncDef*) SQLITE_PRIVATE void sqlite3ExprAssignVarNumber(Parse*, Expr*, u32); SQLITE_PRIVATE void sqlite3ExprDelete(sqlite3*, Expr*); SQLITE_PRIVATE void sqlite3ExprDeleteGeneric(sqlite3*,void*); -SQLITE_PRIVATE void sqlite3ExprDeferredDelete(Parse*, Expr*); +SQLITE_PRIVATE int sqlite3ExprDeferredDelete(Parse*, Expr*); SQLITE_PRIVATE void sqlite3ExprUnmapAndDelete(Parse*, Expr*); SQLITE_PRIVATE ExprList *sqlite3ExprListAppend(Parse*,ExprList*,Expr*); SQLITE_PRIVATE ExprList *sqlite3ExprListAppendVector(Parse*,ExprList*,IdList*,Expr*); @@ -20982,12 +21053,10 @@ SQLITE_PRIVATE void sqlite3LeaveMutexAndCloseZombie(sqlite3*); SQLITE_PRIVATE u32 sqlite3IsTrueOrFalse(const char*); SQLITE_PRIVATE int sqlite3ExprIdToTrueFalse(Expr*); SQLITE_PRIVATE int sqlite3ExprTruthValue(const Expr*); -SQLITE_PRIVATE int sqlite3ExprIsConstant(Expr*); -SQLITE_PRIVATE int sqlite3ExprIsConstantNotJoin(Expr*); +SQLITE_PRIVATE int sqlite3ExprIsConstant(Parse*,Expr*); SQLITE_PRIVATE int sqlite3ExprIsConstantOrFunction(Expr*, u8); SQLITE_PRIVATE int sqlite3ExprIsConstantOrGroupBy(Parse*, Expr*, ExprList*); -SQLITE_PRIVATE int sqlite3ExprIsTableConstant(Expr*,int); -SQLITE_PRIVATE int sqlite3ExprIsSingleTableConstraint(Expr*,const SrcList*,int); +SQLITE_PRIVATE int sqlite3ExprIsSingleTableConstraint(Expr*,const SrcList*,int,int); #ifdef SQLITE_ENABLE_CURSOR_HINTS SQLITE_PRIVATE int sqlite3ExprContainsSubquery(Expr*); #endif @@ -21172,7 +21241,9 @@ SQLITE_PRIVATE void sqlite3ErrorWithMsg(sqlite3*, int, const char*,...); SQLITE_PRIVATE void sqlite3Error(sqlite3*,int); SQLITE_PRIVATE void sqlite3ErrorClear(sqlite3*); SQLITE_PRIVATE void sqlite3SystemError(sqlite3*,int); +#if !defined(SQLITE_OMIT_BLOB_LITERAL) SQLITE_PRIVATE void *sqlite3HexToBlob(sqlite3*, const char *z, int n); +#endif SQLITE_PRIVATE u8 sqlite3HexToInt(int h); SQLITE_PRIVATE int sqlite3TwoPartName(Parse *, Token *, Token *, Token **); @@ -24219,13 +24290,14 @@ struct DateTime { int tz; /* Timezone offset in minutes */ double s; /* Seconds */ char validJD; /* True (1) if iJD is valid */ - char rawS; /* Raw numeric value stored in s */ char validYMD; /* True (1) if Y,M,D are valid */ char validHMS; /* True (1) if h,m,s are valid */ - char validTZ; /* True (1) if tz is valid */ - char tzSet; /* Timezone was set explicitly */ - char isError; /* An overflow has occurred */ - char useSubsec; /* Display subsecond precision */ + char nFloor; /* Days to implement "floor" */ + unsigned rawS : 1; /* Raw numeric value stored in s */ + unsigned isError : 1; /* An overflow has occurred */ + unsigned useSubsec : 1; /* Display subsecond precision */ + unsigned isUtc : 1; /* Time is known to be UTC */ + unsigned isLocal : 1; /* Time is known to be localtime */ }; @@ -24323,6 +24395,8 @@ static int parseTimezone(const char *zDate, DateTime *p){ sgn = +1; }else if( c=='Z' || c=='z' ){ zDate++; + p->isLocal = 0; + p->isUtc = 1; goto zulu_time; }else{ return c!=0; @@ -24335,7 +24409,6 @@ static int parseTimezone(const char *zDate, DateTime *p){ p->tz = sgn*(nMn + nHr*60); zulu_time: while( sqlite3Isspace(*zDate) ){ zDate++; } - p->tzSet = 1; return *zDate!=0; } @@ -24379,7 +24452,6 @@ static int parseHhMmSs(const char *zDate, DateTime *p){ p->m = m; p->s = s + ms; if( parseTimezone(zDate, p) ) return 1; - p->validTZ = (p->tz!=0)?1:0; return 0; } @@ -24426,15 +24498,40 @@ static void computeJD(DateTime *p){ p->validJD = 1; if( p->validHMS ){ p->iJD += p->h*3600000 + p->m*60000 + (sqlite3_int64)(p->s*1000 + 0.5); - if( p->validTZ ){ + if( p->tz ){ p->iJD -= p->tz*60000; p->validYMD = 0; p->validHMS = 0; - p->validTZ = 0; + p->tz = 0; + p->isUtc = 1; + p->isLocal = 0; } } } +/* +** Given the YYYY-MM-DD information current in p, determine if there +** is day-of-month overflow and set nFloor to the number of days that +** would need to be subtracted from the date in order to bring the +** date back to the end of the month. +*/ +static void computeFloor(DateTime *p){ + assert( p->validYMD || p->isError ); + assert( p->D>=0 && p->D<=31 ); + assert( p->M>=0 && p->M<=12 ); + if( p->D<=28 ){ + p->nFloor = 0; + }else if( (1<M) & 0x15aa ){ + p->nFloor = 0; + }else if( p->M!=2 ){ + p->nFloor = (p->D==31); + }else if( p->Y%4!=0 || (p->Y%100==0 && p->Y%400!=0) ){ + p->nFloor = p->D - 28; + }else{ + p->nFloor = p->D - 29; + } +} + /* ** Parse dates of the form ** @@ -24473,12 +24570,16 @@ static int parseYyyyMmDd(const char *zDate, DateTime *p){ p->Y = neg ? -Y : Y; p->M = M; p->D = D; - if( p->validTZ ){ + computeFloor(p); + if( p->tz ){ computeJD(p); } return 0; } + +static void clearYMD_HMS_TZ(DateTime *p); /* Forward declaration */ + /* ** Set the time to the current time reported by the VFS. ** @@ -24488,6 +24589,9 @@ static int setDateTimeToCurrent(sqlite3_context *context, DateTime *p){ p->iJD = sqlite3StmtCurrentTime(context); if( p->iJD>0 ){ p->validJD = 1; + p->isUtc = 1; + p->isLocal = 0; + clearYMD_HMS_TZ(p); return 0; }else{ return 1; @@ -24626,7 +24730,7 @@ static void computeYMD_HMS(DateTime *p){ static void clearYMD_HMS_TZ(DateTime *p){ p->validYMD = 0; p->validHMS = 0; - p->validTZ = 0; + p->tz = 0; } #ifndef SQLITE_OMIT_LOCALTIME @@ -24758,7 +24862,7 @@ static int toLocaltime( p->validHMS = 1; p->validJD = 0; p->rawS = 0; - p->validTZ = 0; + p->tz = 0; p->isError = 0; return SQLITE_OK; } @@ -24778,12 +24882,12 @@ static const struct { float rLimit; /* Maximum NNN value for this transform */ float rXform; /* Constant used for this transform */ } aXformType[] = { - { 6, "second", 4.6427e+14, 1.0 }, - { 6, "minute", 7.7379e+12, 60.0 }, - { 4, "hour", 1.2897e+11, 3600.0 }, - { 3, "day", 5373485.0, 86400.0 }, - { 5, "month", 176546.0, 2592000.0 }, - { 4, "year", 14713.0, 31536000.0 }, + /* 0 */ { 6, "second", 4.6427e+14, 1.0 }, + /* 1 */ { 6, "minute", 7.7379e+12, 60.0 }, + /* 2 */ { 4, "hour", 1.2897e+11, 3600.0 }, + /* 3 */ { 3, "day", 5373485.0, 86400.0 }, + /* 4 */ { 5, "month", 176546.0, 30.0*86400.0 }, + /* 5 */ { 4, "year", 14713.0, 365.0*86400.0 }, }; /* @@ -24815,14 +24919,20 @@ static void autoAdjustDate(DateTime *p){ ** NNN.NNNN seconds ** NNN months ** NNN years +** +/-YYYY-MM-DD HH:MM:SS.SSS +** ceiling +** floor ** start of month ** start of year ** start of week ** start of day ** weekday N ** unixepoch +** auto ** localtime ** utc +** subsec +** subsecond ** ** Return 0 on success and 1 if there is any kind of error. If the error ** is in a system call (i.e. localtime()), then an error message is written @@ -24853,6 +24963,37 @@ static int parseModifier( } break; } + case 'c': { + /* + ** ceiling + ** + ** Resolve day-of-month overflow by rolling forward into the next + ** month. As this is the default action, this modifier is really + ** a no-op that is only included for symmetry. See "floor". + */ + if( sqlite3_stricmp(z, "ceiling")==0 ){ + computeJD(p); + clearYMD_HMS_TZ(p); + rc = 0; + p->nFloor = 0; + } + break; + } + case 'f': { + /* + ** floor + ** + ** Resolve day-of-month overflow by rolling back to the end of the + ** previous month. + */ + if( sqlite3_stricmp(z, "floor")==0 ){ + computeJD(p); + p->iJD -= p->nFloor*86400000; + clearYMD_HMS_TZ(p); + rc = 0; + } + break; + } case 'j': { /* ** julianday @@ -24879,7 +25020,9 @@ static int parseModifier( ** show local time. */ if( sqlite3_stricmp(z, "localtime")==0 && sqlite3NotPureFunc(pCtx) ){ - rc = toLocaltime(p, pCtx); + rc = p->isLocal ? SQLITE_OK : toLocaltime(p, pCtx); + p->isUtc = 0; + p->isLocal = 1; } break; } @@ -24904,7 +25047,7 @@ static int parseModifier( } #ifndef SQLITE_OMIT_LOCALTIME else if( sqlite3_stricmp(z, "utc")==0 && sqlite3NotPureFunc(pCtx) ){ - if( p->tzSet==0 ){ + if( p->isUtc==0 ){ i64 iOrigJD; /* Original localtime */ i64 iGuess; /* Guess at the corresponding utc time */ int cnt = 0; /* Safety to prevent infinite loop */ @@ -24927,7 +25070,8 @@ static int parseModifier( memset(p, 0, sizeof(*p)); p->iJD = iGuess; p->validJD = 1; - p->tzSet = 1; + p->isUtc = 1; + p->isLocal = 0; } rc = SQLITE_OK; } @@ -24947,7 +25091,7 @@ static int parseModifier( && r>=0.0 && r<7.0 && (n=(int)r)==r ){ sqlite3_int64 Z; computeYMD_HMS(p); - p->validTZ = 0; + p->tz = 0; p->validJD = 0; computeJD(p); Z = ((p->iJD + 129600000)/86400000) % 7; @@ -24987,7 +25131,7 @@ static int parseModifier( p->h = p->m = 0; p->s = 0.0; p->rawS = 0; - p->validTZ = 0; + p->tz = 0; p->validJD = 0; if( sqlite3_stricmp(z,"month")==0 ){ p->D = 1; @@ -25058,6 +25202,7 @@ static int parseModifier( x = p->M>0 ? (p->M-1)/12 : (p->M-12)/12; p->Y += x; p->M -= x*12; + computeFloor(p); computeJD(p); p->validHMS = 0; p->validYMD = 0; @@ -25104,11 +25249,12 @@ static int parseModifier( z += n; while( sqlite3Isspace(*z) ) z++; n = sqlite3Strlen30(z); - if( n>10 || n<3 ) break; + if( n<3 || n>10 ) break; if( sqlite3UpperToLower[(u8)z[n-1]]=='s' ) n--; computeJD(p); assert( rc==1 ); rRounder = r<0 ? -0.5 : +0.5; + p->nFloor = 0; for(i=0; iM += (int)r; x = p->M>0 ? (p->M-1)/12 : (p->M-12)/12; p->Y += x; p->M -= x*12; + computeFloor(p); p->validJD = 0; r -= (int)r; break; } case 5: { /* Special processing to add years */ int y = (int)r; - assert( strcmp(aXformType[i].zName,"year")==0 ); + assert( strcmp(aXformType[5].zName,"year")==0 ); computeYMD_HMS(p); + assert( p->M>=0 && p->M<=12 ); p->Y += y; + computeFloor(p); p->validJD = 0; r -= (int)r; break; @@ -25384,22 +25533,83 @@ static void dateFunc( } } +/* +** Compute the number of days after the most recent January 1. +** +** In other words, compute the zero-based day number for the +** current year: +** +** Jan01 = 0, Jan02 = 1, ..., Jan31 = 30, Feb01 = 31, ... +** Dec31 = 364 or 365. +*/ +static int daysAfterJan01(DateTime *pDate){ + DateTime jan01 = *pDate; + assert( jan01.validYMD ); + assert( jan01.validHMS ); + assert( pDate->validJD ); + jan01.validJD = 0; + jan01.M = 1; + jan01.D = 1; + computeJD(&jan01); + return (int)((pDate->iJD-jan01.iJD+43200000)/86400000); +} + +/* +** Return the number of days after the most recent Monday. +** +** In other words, return the day of the week according +** to this code: +** +** 0=Monday, 1=Tuesday, 2=Wednesday, ..., 6=Sunday. +*/ +static int daysAfterMonday(DateTime *pDate){ + assert( pDate->validJD ); + return (int)((pDate->iJD+43200000)/86400000) % 7; +} + +/* +** Return the number of days after the most recent Sunday. +** +** In other words, return the day of the week according +** to this code: +** +** 0=Sunday, 1=Monday, 2=Tues, ..., 6=Saturday +*/ +static int daysAfterSunday(DateTime *pDate){ + assert( pDate->validJD ); + return (int)((pDate->iJD+129600000)/86400000) % 7; +} + /* ** strftime( FORMAT, TIMESTRING, MOD, MOD, ...) ** ** Return a string described by FORMAT. Conversions as follows: ** -** %d day of month +** %d day of month 01-31 +** %e day of month 1-31 ** %f ** fractional seconds SS.SSS +** %F ISO date. YYYY-MM-DD +** %G ISO year corresponding to %V 0000-9999. +** %g 2-digit ISO year corresponding to %V 00-99 ** %H hour 00-24 -** %j day of year 000-366 +** %k hour 0-24 (leading zero converted to space) +** %I hour 01-12 +** %j day of year 001-366 ** %J ** julian day number +** %l hour 1-12 (leading zero converted to space) ** %m month 01-12 ** %M minute 00-59 +** %p "am" or "pm" +** %P "AM" or "PM" +** %R time as HH:MM ** %s seconds since 1970-01-01 ** %S seconds 00-59 -** %w day of week 0-6 Sunday==0 -** %W week of year 00-53 +** %T time as HH:MM:SS +** %u day of week 1-7 Monday==1, Sunday==7 +** %w day of week 0-6 Sunday==0, Monday==1 +** %U week of year 00-53 (First Sunday is start of week 01) +** %V week of year 01-53 (First week containing Thursday is week 01) +** %W week of year 00-53 (First Monday is start of week 01) ** %Y year 0000-9999 ** %% % */ @@ -25436,7 +25646,7 @@ static void strftimeFunc( sqlite3_str_appendf(&sRes, cf=='d' ? "%02d" : "%2d", x.D); break; } - case 'f': { + case 'f': { /* Fractional seconds. (Non-standard) */ double s = x.s; if( s>59.999 ) s = 59.999; sqlite3_str_appendf(&sRes, "%06.3f", s); @@ -25446,6 +25656,21 @@ static void strftimeFunc( sqlite3_str_appendf(&sRes, "%04d-%02d-%02d", x.Y, x.M, x.D); break; } + case 'G': /* Fall thru */ + case 'g': { + DateTime y = x; + assert( y.validJD ); + /* Move y so that it is the Thursday in the same week as x */ + y.iJD += (3 - daysAfterMonday(&x))*86400000; + y.validYMD = 0; + computeYMD(&y); + if( cf=='g' ){ + sqlite3_str_appendf(&sRes, "%02d", y.Y%100); + }else{ + sqlite3_str_appendf(&sRes, "%04d", y.Y); + } + break; + } case 'H': case 'k': { sqlite3_str_appendf(&sRes, cf=='H' ? "%02d" : "%2d", x.h); @@ -25459,25 +25684,11 @@ static void strftimeFunc( sqlite3_str_appendf(&sRes, cf=='I' ? "%02d" : "%2d", h); break; } - case 'W': /* Fall thru */ - case 'j': { - int nDay; /* Number of days since 1st day of year */ - DateTime y = x; - y.validJD = 0; - y.M = 1; - y.D = 1; - computeJD(&y); - nDay = (int)((x.iJD-y.iJD+43200000)/86400000); - if( cf=='W' ){ - int wd; /* 0=Monday, 1=Tuesday, ... 6=Sunday */ - wd = (int)(((x.iJD+43200000)/86400000)%7); - sqlite3_str_appendf(&sRes,"%02d",(nDay+7-wd)/7); - }else{ - sqlite3_str_appendf(&sRes,"%03d",nDay+1); - } + case 'j': { /* Day of year. Jan01==1, Jan02==2, and so forth */ + sqlite3_str_appendf(&sRes,"%03d",daysAfterJan01(&x)+1); break; } - case 'J': { + case 'J': { /* Julian day number. (Non-standard) */ sqlite3_str_appendf(&sRes,"%.16g",x.iJD/86400000.0); break; } @@ -25520,13 +25731,33 @@ static void strftimeFunc( sqlite3_str_appendf(&sRes,"%02d:%02d:%02d", x.h, x.m, (int)x.s); break; } - case 'u': /* Fall thru */ - case 'w': { - char c = (char)(((x.iJD+129600000)/86400000) % 7) + '0'; + case 'u': /* Day of week. 1 to 7. Monday==1, Sunday==7 */ + case 'w': { /* Day of week. 0 to 6. Sunday==0, Monday==1 */ + char c = (char)daysAfterSunday(&x) + '0'; if( c=='0' && cf=='u' ) c = '7'; sqlite3_str_appendchar(&sRes, 1, c); break; } + case 'U': { /* Week num. 00-53. First Sun of the year is week 01 */ + sqlite3_str_appendf(&sRes,"%02d", + (daysAfterJan01(&x)-daysAfterSunday(&x)+7)/7); + break; + } + case 'V': { /* Week num. 01-53. First week with a Thur is week 01 */ + DateTime y = x; + /* Adjust y so that is the Thursday in the same week as x */ + assert( y.validJD ); + y.iJD += (3 - daysAfterMonday(&x))*86400000; + y.validYMD = 0; + computeYMD(&y); + sqlite3_str_appendf(&sRes,"%02d", daysAfterJan01(&y)/7+1); + break; + } + case 'W': { /* Week num. 00-53. First Mon of the year is week 01 */ + sqlite3_str_appendf(&sRes,"%02d", + (daysAfterJan01(&x)-daysAfterMonday(&x)+7)/7); + break; + } case 'Y': { sqlite3_str_appendf(&sRes,"%04d",x.Y); break; @@ -25673,9 +25904,7 @@ static void timediffFunc( d1.iJD = d2.iJD - d1.iJD; d1.iJD += (u64)1486995408 * (u64)100000; } - d1.validYMD = 0; - d1.validHMS = 0; - d1.validTZ = 0; + clearYMD_HMS_TZ(&d1); computeYMD_HMS(&d1); sqlite3StrAccumInit(&sRes, 0, 0, 0, 100); sqlite3_str_appendf(&sRes, "%c%04d-%02d-%02d %02d:%02d:%06.3f", @@ -25744,6 +25973,36 @@ static void currentTimeFunc( } #endif +#if !defined(SQLITE_OMIT_DATETIME_FUNCS) && defined(SQLITE_DEBUG) +/* +** datedebug(...) +** +** This routine returns JSON that describes the internal DateTime object. +** Used for debugging and testing only. Subject to change. +*/ +static void datedebugFunc( + sqlite3_context *context, + int argc, + sqlite3_value **argv +){ + DateTime x; + if( isDate(context, argc, argv, &x)==0 ){ + char *zJson; + zJson = sqlite3_mprintf( + "{iJD:%lld,Y:%d,M:%d,D:%d,h:%d,m:%d,tz:%d," + "s:%.3f,validJD:%d,validYMS:%d,validHMS:%d," + "nFloor:%d,rawS:%d,isError:%d,useSubsec:%d," + "isUtc:%d,isLocal:%d}", + x.iJD, x.Y, x.M, x.D, x.h, x.m, x.tz, + x.s, x.validJD, x.validYMD, x.validHMS, + x.nFloor, x.rawS, x.isError, x.useSubsec, + x.isUtc, x.isLocal); + sqlite3_result_text(context, zJson, -1, sqlite3_free); + } +} +#endif /* !SQLITE_OMIT_DATETIME_FUNCS && SQLITE_DEBUG */ + + /* ** This function registered all of the above C functions as SQL ** functions. This should be the only routine in this file with @@ -25759,6 +26018,9 @@ SQLITE_PRIVATE void sqlite3RegisterDateTimeFunctions(void){ PURE_DATE(datetime, -1, 0, 0, datetimeFunc ), PURE_DATE(strftime, -1, 0, 0, strftimeFunc ), PURE_DATE(timediff, 2, 0, 0, timediffFunc ), +#ifdef SQLITE_DEBUG + PURE_DATE(datedebug, -1, 0, 0, datedebugFunc ), +#endif DFUNCTION(current_time, 0, 0, 0, ctimeFunc ), DFUNCTION(current_timestamp, 0, 0, 0, ctimestampFunc), DFUNCTION(current_date, 0, 0, 0, cdateFunc ), @@ -30174,6 +30436,24 @@ static void sqlite3MallocAlarm(int nByte){ sqlite3_mutex_enter(mem0.mutex); } +#ifdef SQLITE_DEBUG +/* +** This routine is called whenever an out-of-memory condition is seen, +** It's only purpose to to serve as a breakpoint for gdb or similar +** code debuggers when working on out-of-memory conditions, for example +** caused by PRAGMA hard_heap_limit=N. +*/ +static SQLITE_NOINLINE void test_oom_breakpoint(u64 n){ + static u64 nOomFault = 0; + nOomFault += n; + /* The assert() is never reached in a human lifetime. It is here mostly + ** to prevent code optimizers from optimizing out this function. */ + assert( (nOomFault>>32) < 0xffffffff ); +} +#else +# define test_oom_breakpoint(X) /* No-op for production builds */ +#endif + /* ** Do a memory allocation with statistics and alarms. Assume the ** lock is already held. @@ -30200,6 +30480,7 @@ static void mallocWithAlarm(int n, void **pp){ if( mem0.hardLimit ){ nUsed = sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED); if( nUsed >= mem0.hardLimit - nFull ){ + test_oom_breakpoint(1); *pp = 0; return; } @@ -30488,6 +30769,7 @@ SQLITE_PRIVATE void *sqlite3Realloc(void *pOld, u64 nBytes){ sqlite3MallocAlarm(nDiff); if( mem0.hardLimit>0 && nUsed >= mem0.hardLimit - nDiff ){ sqlite3_mutex_leave(mem0.mutex); + test_oom_breakpoint(1); return 0; } } @@ -31390,13 +31672,14 @@ SQLITE_API void sqlite3_str_vappendf( } exp = s.iDP-1; - if( xtype==etGENERIC && precision>0 ) precision--; /* ** If the field type is etGENERIC, then convert to either etEXP ** or etFLOAT, as appropriate. */ if( xtype==etGENERIC ){ + assert( precision>0 ); + precision--; flag_rtz = !flag_alternateform; if( exp<-4 || exp>precision ){ xtype = etEXP; @@ -31712,9 +31995,13 @@ SQLITE_API void sqlite3_str_vappendf( sqlite3_str_appendall(pAccum, pItem->zAlias); }else{ Select *pSel = pItem->pSelect; - assert( pSel!=0 ); + assert( pSel!=0 ); /* Because of tag-20240424-1 */ if( pSel->selFlags & SF_NestedFrom ){ sqlite3_str_appendf(pAccum, "(join-%u)", pSel->selId); + }else if( pSel->selFlags & SF_MultiValue ){ + assert( !pItem->fg.isTabFunc && !pItem->fg.isIndexedBy ); + sqlite3_str_appendf(pAccum, "%u-ROW VALUES CLAUSE", + pItem->u1.nRow); }else{ sqlite3_str_appendf(pAccum, "(subquery-%u)", pSel->selId); } @@ -32491,8 +32778,10 @@ SQLITE_PRIVATE void sqlite3TreeViewSrcList(TreeView *pView, const SrcList *pSrc) x.printfFlags |= SQLITE_PRINTF_INTERNAL; sqlite3_str_appendf(&x, "{%d:*} %!S", pItem->iCursor, pItem); if( pItem->pTab ){ - sqlite3_str_appendf(&x, " tab=%Q nCol=%d ptr=%p used=%llx", - pItem->pTab->zName, pItem->pTab->nCol, pItem->pTab, pItem->colUsed); + sqlite3_str_appendf(&x, " tab=%Q nCol=%d ptr=%p used=%llx%s", + pItem->pTab->zName, pItem->pTab->nCol, pItem->pTab, + pItem->colUsed, + pItem->fg.rowidUsed ? "+rowid" : ""); } if( (pItem->fg.jointype & (JT_LEFT|JT_RIGHT))==(JT_LEFT|JT_RIGHT) ){ sqlite3_str_appendf(&x, " FULL-OUTER-JOIN"); @@ -32532,12 +32821,14 @@ SQLITE_PRIVATE void sqlite3TreeViewSrcList(TreeView *pView, const SrcList *pSrc) sqlite3TreeViewIdList(pView, pItem->u3.pUsing, (--n)>0, "USING"); } if( pItem->pSelect ){ + sqlite3TreeViewPush(&pView, i+1nSrc); if( pItem->pTab ){ Table *pTab = pItem->pTab; sqlite3TreeViewColumnList(pView, pTab->aCol, pTab->nCol, 1); } assert( (int)pItem->fg.isNestedFrom == IsNestedFrom(pItem->pSelect) ); sqlite3TreeViewSelect(pView, pItem->pSelect, (--n)>0); + sqlite3TreeViewPop(&pView); } if( pItem->fg.isTabFunc ){ sqlite3TreeViewExprList(pView, pItem->u1.pFuncArg, 0, "func-args:"); @@ -32641,7 +32932,7 @@ SQLITE_PRIVATE void sqlite3TreeViewSelect(TreeView *pView, const Select *p, u8 m sqlite3TreeViewItem(pView, "LIMIT", (n--)>0); sqlite3TreeViewExpr(pView, p->pLimit->pLeft, p->pLimit->pRight!=0); if( p->pLimit->pRight ){ - sqlite3TreeViewItem(pView, "OFFSET", (n--)>0); + sqlite3TreeViewItem(pView, "OFFSET", 0); sqlite3TreeViewExpr(pView, p->pLimit->pRight, 0); sqlite3TreeViewPop(&pView); } @@ -34942,6 +35233,44 @@ SQLITE_PRIVATE void sqlite3DequoteExpr(Expr *p){ sqlite3Dequote(p->u.zToken); } +/* +** Expression p is a QNUMBER (quoted number). Dequote the value in p->u.zToken +** and set the type to INTEGER or FLOAT. "Quoted" integers or floats are those +** that contain '_' characters that must be removed before further processing. +*/ +SQLITE_PRIVATE void sqlite3DequoteNumber(Parse *pParse, Expr *p){ + assert( p!=0 || pParse->db->mallocFailed ); + if( p ){ + const char *pIn = p->u.zToken; + char *pOut = p->u.zToken; + int bHex = (pIn[0]=='0' && (pIn[1]=='x' || pIn[1]=='X')); + int iValue; + assert( p->op==TK_QNUMBER ); + p->op = TK_INTEGER; + do { + if( *pIn!=SQLITE_DIGIT_SEPARATOR ){ + *pOut++ = *pIn; + if( *pIn=='e' || *pIn=='E' || *pIn=='.' ) p->op = TK_FLOAT; + }else{ + if( (bHex==0 && (!sqlite3Isdigit(pIn[-1]) || !sqlite3Isdigit(pIn[1]))) + || (bHex==1 && (!sqlite3Isxdigit(pIn[-1]) || !sqlite3Isxdigit(pIn[1]))) + ){ + sqlite3ErrorMsg(pParse, "unrecognized token: \"%s\"", p->u.zToken); + } + } + }while( *pIn++ ); + if( bHex ) p->op = TK_INTEGER; + + /* tag-20240227-a: If after dequoting, the number is an integer that + ** fits in 32 bits, then it must be converted into EP_IntValue. Other + ** parts of the code expect this. See also tag-20240227-b. */ + if( p->op==TK_INTEGER && sqlite3GetInt32(p->u.zToken, &iValue) ){ + p->u.iValue = iValue; + p->flags |= EP_IntValue; + } + } +} + /* ** If the input token p is quoted, try to adjust the token to remove ** the quotes. This is not always possible: @@ -36881,7 +37210,7 @@ SQLITE_PRIVATE const char *sqlite3OpcodeName(int i){ /* 30 */ "SeekRowid" OpHelp("intkey=r[P3]"), /* 31 */ "NotExists" OpHelp("intkey=r[P3]"), /* 32 */ "Last" OpHelp(""), - /* 33 */ "IfSmaller" OpHelp(""), + /* 33 */ "IfSizeBetween" OpHelp(""), /* 34 */ "SorterSort" OpHelp(""), /* 35 */ "Sort" OpHelp(""), /* 36 */ "Rewind" OpHelp(""), @@ -36926,7 +37255,7 @@ SQLITE_PRIVATE const char *sqlite3OpcodeName(int i){ /* 75 */ "Null" OpHelp("r[P2..P3]=NULL"), /* 76 */ "SoftNull" OpHelp("r[P1]=NULL"), /* 77 */ "Blob" OpHelp("r[P2]=P4 (len=P1)"), - /* 78 */ "Variable" OpHelp("r[P2]=parameter(P1,P4)"), + /* 78 */ "Variable" OpHelp("r[P2]=parameter(P1)"), /* 79 */ "Move" OpHelp("r[P2@P3]=r[P1@P3]"), /* 80 */ "Copy" OpHelp("r[P2@P3+1]=r[P1@P3+1]"), /* 81 */ "SCopy" OpHelp("r[P2]=r[P1]"), @@ -39324,8 +39653,12 @@ static int unixLogErrorAtLine( ** available, the error message will often be an empty string. Not a ** huge problem. Incorrectly concluding that the GNU version is available ** could lead to a segfault though. + ** + ** Forum post 3f13857fa4062301 reports that the Android SDK may use + ** int-type return, depending on its version. */ -#if defined(STRERROR_R_CHAR_P) || defined(__USE_GNU) +#if (defined(STRERROR_R_CHAR_P) || defined(__USE_GNU)) \ + && !defined(ANDROID) && !defined(__ANDROID__) zErr = # endif strerror_r(iErrno, aErr, sizeof(aErr)-1); @@ -44423,12 +44756,19 @@ static int unixOpen( rc = SQLITE_READONLY_DIRECTORY; }else if( errno!=EISDIR && isReadWrite ){ /* Failed to open the file for read/write access. Try read-only. */ + UnixUnusedFd *pReadonly = 0; flags &= ~(SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE); openFlags &= ~(O_RDWR|O_CREAT); flags |= SQLITE_OPEN_READONLY; openFlags |= O_RDONLY; isReadonly = 1; - fd = robust_open(zName, openFlags, openMode); + pReadonly = findReusableFd(zName, flags); + if( pReadonly ){ + fd = pReadonly->fd; + sqlite3_free(pReadonly); + }else{ + fd = robust_open(zName, openFlags, openMode); + } } } if( fd<0 ){ @@ -69879,6 +70219,7 @@ struct IntegrityCk { StrAccum errMsg; /* Accumulate the error message text here */ u32 *heap; /* Min-heap used for analyzing cell coverage */ sqlite3 *db; /* Database connection running the check */ + i64 nRow; /* Number of rows visited in current tree */ }; /* @@ -70353,8 +70694,47 @@ int corruptPageError(int lineno, MemPage *p){ # define SQLITE_CORRUPT_PAGE(pMemPage) SQLITE_CORRUPT_PGNO(pMemPage->pgno) #endif +/* Default value for SHARED_LOCK_TRACE macro if shared-cache is disabled +** or if the lock tracking is disabled. This is always the value for +** release builds. +*/ +#define SHARED_LOCK_TRACE(X,MSG,TAB,TYPE) /*no-op*/ + #ifndef SQLITE_OMIT_SHARED_CACHE +#if 0 +/* ^---- Change to 1 and recompile to enable shared-lock tracing +** for debugging purposes. +** +** Print all shared-cache locks on a BtShared. Debugging use only. +*/ +static void sharedLockTrace( + BtShared *pBt, + const char *zMsg, + int iRoot, + int eLockType +){ + BtLock *pLock; + if( iRoot>0 ){ + printf("%s-%p %u%s:", zMsg, pBt, iRoot, eLockType==READ_LOCK?"R":"W"); + }else{ + printf("%s-%p:", zMsg, pBt); + } + for(pLock=pBt->pLock; pLock; pLock=pLock->pNext){ + printf(" %p/%u%s", pLock->pBtree, pLock->iTable, + pLock->eLock==READ_LOCK ? "R" : "W"); + while( pLock->pNext && pLock->pBtree==pLock->pNext->pBtree ){ + pLock = pLock->pNext; + printf(",%u%s", pLock->iTable, pLock->eLock==READ_LOCK ? "R" : "W"); + } + } + printf("\n"); + fflush(stdout); +} +#undef SHARED_LOCK_TRACE +#define SHARED_LOCK_TRACE(X,MSG,TAB,TYPE) sharedLockTrace(X,MSG,TAB,TYPE) +#endif /* Shared-lock tracing */ + #ifdef SQLITE_DEBUG /* **** This function is only used as part of an assert() statement. *** @@ -70431,6 +70811,8 @@ static int hasSharedCacheTableLock( iTab = iRoot; } + SHARED_LOCK_TRACE(pBtree->pBt,"hasLock",iRoot,eLockType); + /* Search for the required lock. Either a write-lock on root-page iTab, a ** write-lock on the schema table, or (if the client is reading) a ** read-lock on iTab will suffice. Return 1 if any of these are found. */ @@ -70564,6 +70946,8 @@ static int setSharedCacheTableLock(Btree *p, Pgno iTable, u8 eLock){ BtLock *pLock = 0; BtLock *pIter; + SHARED_LOCK_TRACE(pBt,"setLock", iTable, eLock); + assert( sqlite3BtreeHoldsMutex(p) ); assert( eLock==READ_LOCK || eLock==WRITE_LOCK ); assert( p->db!=0 ); @@ -70631,6 +71015,8 @@ static void clearAllSharedCacheTableLocks(Btree *p){ assert( p->sharable || 0==*ppIter ); assert( p->inTrans>0 ); + SHARED_LOCK_TRACE(pBt, "clearAllLocks", 0, 0); + while( *ppIter ){ BtLock *pLock = *ppIter; assert( (pBt->btsFlags & BTS_EXCLUSIVE)==0 || pBt->pWriter==pLock->pBtree ); @@ -70669,6 +71055,9 @@ static void clearAllSharedCacheTableLocks(Btree *p){ */ static void downgradeAllSharedCacheTableLocks(Btree *p){ BtShared *pBt = p->pBt; + + SHARED_LOCK_TRACE(pBt, "downgradeLocks", 0, 0); + if( pBt->pWriter==p ){ BtLock *pLock; pBt->pWriter = 0; @@ -75282,9 +75671,12 @@ static int accessPayload( if( pCur->aOverflow==0 || nOvfl*(int)sizeof(Pgno) > sqlite3MallocSize(pCur->aOverflow) ){ - Pgno *aNew = (Pgno*)sqlite3Realloc( - pCur->aOverflow, nOvfl*2*sizeof(Pgno) - ); + Pgno *aNew; + if( sqlite3FaultSim(413) ){ + aNew = 0; + }else{ + aNew = (Pgno*)sqlite3Realloc(pCur->aOverflow, nOvfl*2*sizeof(Pgno)); + } if( aNew==0 ){ return SQLITE_NOMEM_BKPT; }else{ @@ -75294,6 +75686,12 @@ static int accessPayload( memset(pCur->aOverflow, 0, nOvfl*sizeof(Pgno)); pCur->curFlags |= BTCF_ValidOvfl; }else{ + /* Sanity check the validity of the overflow page cache */ + assert( pCur->aOverflow[0]==nextPage + || pCur->aOverflow[0]==0 + || CORRUPT_DB ); + assert( pCur->aOverflow[0]!=0 || pCur->aOverflow[offset/ovflSize]==0 ); + /* If the overflow page-list cache has been allocated and the ** entry for the first required overflow page is valid, skip ** directly to it. @@ -75775,6 +76173,23 @@ SQLITE_PRIVATE int sqlite3BtreeFirst(BtCursor *pCur, int *pRes){ return rc; } +#ifdef SQLITE_DEBUG +/* The cursors is CURSOR_VALID and has BTCF_AtLast set. Verify that +** this flags are true for a consistent database. +** +** This routine is is called from within assert() statements only. +** It is an internal verification routine and does not appear in production +** builds. +*/ +static int cursorIsAtLastEntry(BtCursor *pCur){ + int ii; + for(ii=0; iiiPage; ii++){ + if( pCur->aiIdx[ii]!=pCur->apPage[ii]->nCell ) return 0; + } + return pCur->ix==pCur->pPage->nCell-1 && pCur->pPage->leaf!=0; +} +#endif + /* Move the cursor to the last entry in the table. Return SQLITE_OK ** on success. Set *pRes to 0 if the cursor actually points to something ** or set *pRes to 1 if the table is empty. @@ -75803,18 +76218,7 @@ SQLITE_PRIVATE int sqlite3BtreeLast(BtCursor *pCur, int *pRes){ /* If the cursor already points to the last entry, this is a no-op. */ if( CURSOR_VALID==pCur->eState && (pCur->curFlags & BTCF_AtLast)!=0 ){ -#ifdef SQLITE_DEBUG - /* This block serves to assert() that the cursor really does point - ** to the last entry in the b-tree. */ - int ii; - for(ii=0; iiiPage; ii++){ - assert( pCur->aiIdx[ii]==pCur->apPage[ii]->nCell ); - } - assert( pCur->ix==pCur->pPage->nCell-1 || CORRUPT_DB ); - testcase( pCur->ix!=pCur->pPage->nCell-1 ); - /* ^-- dbsqlfuzz b92b72e4de80b5140c30ab71372ca719b8feb618 */ - assert( pCur->pPage->leaf ); -#endif + assert( cursorIsAtLastEntry(pCur) || CORRUPT_DB ); *pRes = 0; return SQLITE_OK; } @@ -75867,6 +76271,7 @@ SQLITE_PRIVATE int sqlite3BtreeTableMoveto( } if( pCur->info.nKeycurFlags & BTCF_AtLast)!=0 ){ + assert( cursorIsAtLastEntry(pCur) || CORRUPT_DB ); *pRes = -1; return SQLITE_OK; } @@ -76333,10 +76738,10 @@ SQLITE_PRIVATE i64 sqlite3BtreeRowCountEst(BtCursor *pCur){ assert( cursorOwnsBtShared(pCur) ); assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) ); - /* Currently this interface is only called by the OP_IfSmaller - ** opcode, and it that case the cursor will always be valid and - ** will always point to a leaf node. */ - if( NEVER(pCur->eState!=CURSOR_VALID) ) return -1; + /* Currently this interface is only called by the OP_IfSizeBetween + ** opcode and the OP_Count opcode with P3=1. In either case, + ** the cursor will always be valid unless the btree is empty. */ + if( pCur->eState!=CURSOR_VALID ) return 0; if( NEVER(pCur->pPage->leaf==0) ) return -1; n = pCur->pPage->nCell; @@ -78467,7 +78872,7 @@ static int balance_nonroot( ** table-interior, index-leaf, or index-interior). */ if( pOld->aData[0]!=apOld[0]->aData[0] ){ - rc = SQLITE_CORRUPT_BKPT; + rc = SQLITE_CORRUPT_PAGE(pOld); goto balance_cleanup; } @@ -78491,7 +78896,7 @@ static int balance_nonroot( memset(&b.szCell[b.nCell], 0, sizeof(b.szCell[0])*(limit+pOld->nOverflow)); if( pOld->nOverflow>0 ){ if( NEVER(limitaiOvfl[0]) ){ - rc = SQLITE_CORRUPT_BKPT; + rc = SQLITE_CORRUPT_PAGE(pOld); goto balance_cleanup; } limit = pOld->aiOvfl[0]; @@ -79134,7 +79539,7 @@ static int anotherValidCursor(BtCursor *pCur){ && pOther->eState==CURSOR_VALID && pOther->pPage==pCur->pPage ){ - return SQLITE_CORRUPT_BKPT; + return SQLITE_CORRUPT_PAGE(pCur->pPage); } } return SQLITE_OK; @@ -79194,7 +79599,7 @@ static int balance(BtCursor *pCur){ /* The page being written is not a root page, and there is currently ** more than one reference to it. This only happens if the page is one ** of its own ancestor pages. Corruption. */ - rc = SQLITE_CORRUPT_BKPT; + rc = SQLITE_CORRUPT_PAGE(pPage); }else{ MemPage * const pParent = pCur->apPage[iPage-1]; int const iIdx = pCur->aiIdx[iPage-1]; @@ -79358,7 +79763,7 @@ static SQLITE_NOINLINE int btreeOverwriteOverflowCell( rc = btreeGetPage(pBt, ovflPgno, &pPage, 0); if( rc ) return rc; if( sqlite3PagerPageRefcount(pPage->pDbPage)!=1 || pPage->isInit ){ - rc = SQLITE_CORRUPT_BKPT; + rc = SQLITE_CORRUPT_PAGE(pPage); }else{ if( iOffset+ovflPageSize<(u32)nTotal ){ ovflPgno = get4byte(pPage->aData); @@ -79386,7 +79791,7 @@ static int btreeOverwriteCell(BtCursor *pCur, const BtreePayload *pX){ if( pCur->info.pPayload + pCur->info.nLocal > pPage->aDataEnd || pCur->info.pPayload < pPage->aData + pPage->cellOffset ){ - return SQLITE_CORRUPT_BKPT; + return SQLITE_CORRUPT_PAGE(pPage); } if( pCur->info.nLocal==nTotal ){ /* The entire cell is local */ @@ -79467,7 +79872,7 @@ SQLITE_PRIVATE int sqlite3BtreeInsert( ** Which can only happen if the SQLITE_NoSchemaError flag was set when ** the schema was loaded. This cannot be asserted though, as a user might ** set the flag, load the schema, and then unset the flag. */ - return SQLITE_CORRUPT_BKPT; + return SQLITE_CORRUPT_PGNO(pCur->pgnoRoot); } } @@ -79590,7 +79995,7 @@ SQLITE_PRIVATE int sqlite3BtreeInsert( if( pPage->nFree<0 ){ if( NEVER(pCur->eState>CURSOR_INVALID) ){ /* ^^^^^--- due to the moveToRoot() call above */ - rc = SQLITE_CORRUPT_BKPT; + rc = SQLITE_CORRUPT_PAGE(pPage); }else{ rc = btreeComputeFreeSpace(pPage); } @@ -79632,7 +80037,7 @@ SQLITE_PRIVATE int sqlite3BtreeInsert( CellInfo info; assert( idx>=0 ); if( idx>=pPage->nCell ){ - return SQLITE_CORRUPT_BKPT; + return SQLITE_CORRUPT_PAGE(pPage); } rc = sqlite3PagerWrite(pPage->pDbPage); if( rc ){ @@ -79659,10 +80064,10 @@ SQLITE_PRIVATE int sqlite3BtreeInsert( ** necessary to add the PTRMAP_OVERFLOW1 pointer-map entry. */ assert( rc==SQLITE_OK ); /* clearCell never fails when nLocal==nPayload */ if( oldCell < pPage->aData+pPage->hdrOffset+10 ){ - return SQLITE_CORRUPT_BKPT; + return SQLITE_CORRUPT_PAGE(pPage); } if( oldCell+szNew > pPage->aDataEnd ){ - return SQLITE_CORRUPT_BKPT; + return SQLITE_CORRUPT_PAGE(pPage); } memcpy(oldCell, newCell, szNew); return SQLITE_OK; @@ -79764,7 +80169,7 @@ SQLITE_PRIVATE int sqlite3BtreeTransferRow(BtCursor *pDest, BtCursor *pSrc, i64 nIn = pSrc->info.nLocal; aIn = pSrc->info.pPayload; if( aIn+nIn>pSrc->pPage->aDataEnd ){ - return SQLITE_CORRUPT_BKPT; + return SQLITE_CORRUPT_PAGE(pSrc->pPage); } nRem = pSrc->info.nPayload; if( nIn==nRem && nInpPage->maxLocal ){ @@ -79789,7 +80194,7 @@ SQLITE_PRIVATE int sqlite3BtreeTransferRow(BtCursor *pDest, BtCursor *pSrc, i64 if( nRem>nIn ){ if( aIn+nIn+4>pSrc->pPage->aDataEnd ){ - return SQLITE_CORRUPT_BKPT; + return SQLITE_CORRUPT_PAGE(pSrc->pPage); } ovflIn = get4byte(&pSrc->info.pPayload[nIn]); } @@ -79885,7 +80290,7 @@ SQLITE_PRIVATE int sqlite3BtreeDelete(BtCursor *pCur, u8 flags){ assert( rc!=SQLITE_OK || CORRUPT_DB || pCur->eState==CURSOR_VALID ); if( rc || pCur->eState!=CURSOR_VALID ) return rc; }else{ - return SQLITE_CORRUPT_BKPT; + return SQLITE_CORRUPT_PGNO(pCur->pgnoRoot); } } assert( pCur->eState==CURSOR_VALID ); @@ -79894,14 +80299,14 @@ SQLITE_PRIVATE int sqlite3BtreeDelete(BtCursor *pCur, u8 flags){ iCellIdx = pCur->ix; pPage = pCur->pPage; if( pPage->nCell<=iCellIdx ){ - return SQLITE_CORRUPT_BKPT; + return SQLITE_CORRUPT_PAGE(pPage); } pCell = findCell(pPage, iCellIdx); if( pPage->nFree<0 && btreeComputeFreeSpace(pPage) ){ - return SQLITE_CORRUPT_BKPT; + return SQLITE_CORRUPT_PAGE(pPage); } if( pCell<&pPage->aCellIdx[pPage->nCell] ){ - return SQLITE_CORRUPT_BKPT; + return SQLITE_CORRUPT_PAGE(pPage); } /* If the BTREE_SAVEPOSITION bit is on, then the cursor position must @@ -79992,7 +80397,7 @@ SQLITE_PRIVATE int sqlite3BtreeDelete(BtCursor *pCur, u8 flags){ n = pCur->pPage->pgno; } pCell = findCell(pLeaf, pLeaf->nCell-1); - if( pCell<&pLeaf->aData[4] ) return SQLITE_CORRUPT_BKPT; + if( pCell<&pLeaf->aData[4] ) return SQLITE_CORRUPT_PAGE(pLeaf); nCell = pLeaf->xCellSize(pLeaf, pCell); assert( MX_CELL_SIZE(pBt) >= nCell ); pTmp = pBt->pTmpSpace; @@ -80108,7 +80513,7 @@ static int btreeCreateTable(Btree *p, Pgno *piTable, int createTabFlags){ */ sqlite3BtreeGetMeta(p, BTREE_LARGEST_ROOT_PAGE, &pgnoRoot); if( pgnoRoot>btreePagecount(pBt) ){ - return SQLITE_CORRUPT_BKPT; + return SQLITE_CORRUPT_PGNO(pgnoRoot); } pgnoRoot++; @@ -80156,7 +80561,7 @@ static int btreeCreateTable(Btree *p, Pgno *piTable, int createTabFlags){ } rc = ptrmapGet(pBt, pgnoRoot, &eType, &iPtrPage); if( eType==PTRMAP_ROOTPAGE || eType==PTRMAP_FREEPAGE ){ - rc = SQLITE_CORRUPT_BKPT; + rc = SQLITE_CORRUPT_PGNO(pgnoRoot); } if( rc!=SQLITE_OK ){ releasePage(pRoot); @@ -80246,14 +80651,14 @@ static int clearDatabasePage( assert( sqlite3_mutex_held(pBt->mutex) ); if( pgno>btreePagecount(pBt) ){ - return SQLITE_CORRUPT_BKPT; + return SQLITE_CORRUPT_PGNO(pgno); } rc = getAndInitPage(pBt, pgno, &pPage, 0); if( rc ) return rc; if( (pBt->openFlags & BTREE_SINGLE)==0 && sqlite3PagerPageRefcount(pPage->pDbPage) != (1 + (pgno==1)) ){ - rc = SQLITE_CORRUPT_BKPT; + rc = SQLITE_CORRUPT_PAGE(pPage); goto cleardatabasepage_out; } hdr = pPage->hdrOffset; @@ -80357,7 +80762,7 @@ static int btreeDropTable(Btree *p, Pgno iTable, int *piMoved){ assert( p->inTrans==TRANS_WRITE ); assert( iTable>=2 ); if( iTable>btreePagecount(pBt) ){ - return SQLITE_CORRUPT_BKPT; + return SQLITE_CORRUPT_PGNO(iTable); } rc = sqlite3BtreeClearTable(p, iTable, 0); @@ -80951,6 +81356,9 @@ static int checkTreePage( ** number of cells on the page. */ nCell = get2byte(&data[hdr+3]); assert( pPage->nCell==nCell ); + if( pPage->leaf || pPage->intKey==0 ){ + pCheck->nRow += nCell; + } /* EVIDENCE-OF: R-23882-45353 The cell pointer array of a b-tree page ** immediately follows the b-tree page header. */ @@ -81062,6 +81470,7 @@ static int checkTreePage( btreeHeapInsert(heap, (pc<<16)|(pc+size-1)); } } + assert( heap!=0 ); /* Add the freeblocks to the min-heap ** ** EVIDENCE-OF: R-20690-50594 The second field of the b-tree page header @@ -81161,6 +81570,7 @@ SQLITE_PRIVATE int sqlite3BtreeIntegrityCheck( sqlite3 *db, /* Database connection that is running the check */ Btree *p, /* The btree to be checked */ Pgno *aRoot, /* An array of root pages numbers for individual trees */ + Mem *aCnt, /* Memory cells to write counts for each tree to */ int nRoot, /* Number of entries in aRoot[] */ int mxErr, /* Stop reporting errors after this many */ int *pnErr, /* OUT: Write number of errors seen to this variable */ @@ -81174,7 +81584,9 @@ SQLITE_PRIVATE int sqlite3BtreeIntegrityCheck( int bPartial = 0; /* True if not checking all btrees */ int bCkFreelist = 1; /* True to scan the freelist */ VVA_ONLY( int nRef ); + assert( nRoot>0 ); + assert( aCnt!=0 ); /* aRoot[0]==0 means this is a partial check */ if( aRoot[0]==0 ){ @@ -81247,15 +81659,18 @@ SQLITE_PRIVATE int sqlite3BtreeIntegrityCheck( testcase( pBt->db->flags & SQLITE_CellSizeCk ); pBt->db->flags &= ~(u64)SQLITE_CellSizeCk; for(i=0; (int)iautoVacuum && aRoot[i]>1 && !bPartial ){ - checkPtrmap(&sCheck, aRoot[i], PTRMAP_ROOTPAGE, 0); - } + if( pBt->autoVacuum && aRoot[i]>1 && !bPartial ){ + checkPtrmap(&sCheck, aRoot[i], PTRMAP_ROOTPAGE, 0); + } #endif - sCheck.v0 = aRoot[i]; - checkTreePage(&sCheck, aRoot[i], ¬Used, LARGEST_INT64); + sCheck.v0 = aRoot[i]; + checkTreePage(&sCheck, aRoot[i], ¬Used, LARGEST_INT64); + } + sqlite3MemSetArrayInt64(aCnt, i, sCheck.nRow); } pBt->db->flags = savedDbFlags; @@ -83310,6 +83725,13 @@ SQLITE_PRIVATE void sqlite3VdbeMemSetInt64(Mem *pMem, i64 val){ } } +/* +** Set the iIdx'th entry of array aMem[] to contain integer value val. +*/ +SQLITE_PRIVATE void sqlite3MemSetArrayInt64(sqlite3_value *aMem, int iIdx, i64 val){ + sqlite3VdbeMemSetInt64(&aMem[iIdx], val); +} + /* A no-op destructor */ SQLITE_PRIVATE void sqlite3NoopDestructor(void *p){ UNUSED_PARAMETER(p); } @@ -83998,14 +84420,20 @@ static int valueFromExpr( } /* Handle negative integers in a single step. This is needed in the - ** case when the value is -9223372036854775808. - */ - if( op==TK_UMINUS - && (pExpr->pLeft->op==TK_INTEGER || pExpr->pLeft->op==TK_FLOAT) ){ - pExpr = pExpr->pLeft; - op = pExpr->op; - negInt = -1; - zNeg = "-"; + ** case when the value is -9223372036854775808. Except - do not do this + ** for hexadecimal literals. */ + if( op==TK_UMINUS ){ + Expr *pLeft = pExpr->pLeft; + if( (pLeft->op==TK_INTEGER || pLeft->op==TK_FLOAT) ){ + if( ExprHasProperty(pLeft, EP_IntValue) + || pLeft->u.zToken[0]!='0' || (pLeft->u.zToken[1] & ~0x20)!='X' + ){ + pExpr = pLeft; + op = pExpr->op; + negInt = -1; + zNeg = "-"; + } + } } if( op==TK_STRING || op==TK_FLOAT || op==TK_INTEGER ){ @@ -84014,12 +84442,26 @@ static int valueFromExpr( if( ExprHasProperty(pExpr, EP_IntValue) ){ sqlite3VdbeMemSetInt64(pVal, (i64)pExpr->u.iValue*negInt); }else{ - zVal = sqlite3MPrintf(db, "%s%s", zNeg, pExpr->u.zToken); - if( zVal==0 ) goto no_mem; - sqlite3ValueSetStr(pVal, -1, zVal, SQLITE_UTF8, SQLITE_DYNAMIC); + i64 iVal; + if( op==TK_INTEGER && 0==sqlite3DecOrHexToI64(pExpr->u.zToken, &iVal) ){ + sqlite3VdbeMemSetInt64(pVal, iVal*negInt); + }else{ + zVal = sqlite3MPrintf(db, "%s%s", zNeg, pExpr->u.zToken); + if( zVal==0 ) goto no_mem; + sqlite3ValueSetStr(pVal, -1, zVal, SQLITE_UTF8, SQLITE_DYNAMIC); + } } - if( (op==TK_INTEGER || op==TK_FLOAT ) && affinity==SQLITE_AFF_BLOB ){ - sqlite3ValueApplyAffinity(pVal, SQLITE_AFF_NUMERIC, SQLITE_UTF8); + if( affinity==SQLITE_AFF_BLOB ){ + if( op==TK_FLOAT ){ + assert( pVal && pVal->z && pVal->flags==(MEM_Str|MEM_Term) ); + sqlite3AtoF(pVal->z, &pVal->u.r, pVal->n, SQLITE_UTF8); + pVal->flags = MEM_Real; + }else if( op==TK_INTEGER ){ + /* This case is required by -9223372036854775808 and other strings + ** that look like integers but cannot be handled by the + ** sqlite3DecOrHexToI64() call above. */ + sqlite3ValueApplyAffinity(pVal, SQLITE_AFF_NUMERIC, SQLITE_UTF8); + } }else{ sqlite3ValueApplyAffinity(pVal, affinity, SQLITE_UTF8); } @@ -84289,17 +84731,17 @@ SQLITE_PRIVATE int sqlite3Stat4Column( sqlite3_value **ppVal /* OUT: Extracted value */ ){ u32 t = 0; /* a column type code */ - int nHdr; /* Size of the header in the record */ - int iHdr; /* Next unread header byte */ - int iField; /* Next unread data byte */ - int szField = 0; /* Size of the current data field */ + u32 nHdr; /* Size of the header in the record */ + u32 iHdr; /* Next unread header byte */ + i64 iField; /* Next unread data byte */ + u32 szField = 0; /* Size of the current data field */ int i; /* Column index */ u8 *a = (u8*)pRec; /* Typecast byte array */ Mem *pMem = *ppVal; /* Write result into this Mem object */ assert( iCol>0 ); iHdr = getVarint32(a, nHdr); - if( nHdr>nRec || iHdr>=nHdr ) return SQLITE_CORRUPT_BKPT; + if( nHdr>(u32)nRec || iHdr>=nHdr ) return SQLITE_CORRUPT_BKPT; iField = nHdr; for(i=0; i<=iCol; i++){ iHdr += getVarint32(&a[iHdr], t); @@ -85334,6 +85776,15 @@ static void resolveP2Values(Vdbe *p, int *pMaxFuncArgs){ assert( aLabel!=0 ); /* True because of tag-20230419-1 */ pOp->p2 = aLabel[ADDR(pOp->p2)]; } + + /* OPFLG_JUMP opcodes never have P2==0, though OPFLG_JUMP0 opcodes + ** might */ + assert( pOp->p2>0 + || (sqlite3OpcodeProperty[pOp->opcode] & OPFLG_JUMP0)!=0 ); + + /* Jumps never go off the end of the bytecode array */ + assert( pOp->p2nOp + || (sqlite3OpcodeProperty[pOp->opcode] & OPFLG_JUMP)==0 ); break; } } @@ -87741,7 +88192,7 @@ SQLITE_PRIVATE int sqlite3VdbeHalt(Vdbe *p){ /* Check for immediate foreign key violations. */ if( p->rc==SQLITE_OK || (p->errorAction==OE_Fail && !isSpecialError) ){ - sqlite3VdbeCheckFk(p, 0); + (void)sqlite3VdbeCheckFk(p, 0); } /* If the auto-commit flag is set and this is the only active writer @@ -88911,17 +89362,15 @@ SQLITE_PRIVATE int sqlite3IntFloatCompare(i64 i, double r){ return (xr); }else{ i64 y; - double s; if( r<-9223372036854775808.0 ) return +1; if( r>=9223372036854775808.0 ) return -1; y = (i64)r; if( iy ) return +1; - s = (double)i; - testcase( doubleLt(s,r) ); - testcase( doubleLt(r,s) ); - testcase( doubleEq(r,s) ); - return (sr); + testcase( doubleLt(((double)i),r) ); + testcase( doubleLt(r,((double)i)) ); + testcase( doubleEq(r,((double)i)) ); + return (((double)i)r); } } @@ -92329,7 +92778,6 @@ SQLITE_API int sqlite3_stmt_scanstatus_v2( } if( flags & SQLITE_SCANSTAT_COMPLEX ){ idx = iScan; - pScan = &p->aScan[idx]; }else{ /* If the COMPLEX flag is clear, then this function must ignore any ** ScanStatus structures with ScanStatus.addrLoop set to 0. */ @@ -92342,6 +92790,8 @@ SQLITE_API int sqlite3_stmt_scanstatus_v2( } } if( idx>=p->nScan ) return 1; + assert( pScan==0 || pScan==&p->aScan[idx] ); + pScan = &p->aScan[idx]; switch( iScanStatusOp ){ case SQLITE_SCANSTAT_NLOOP: { @@ -93790,7 +94240,7 @@ case OP_Return: { /* in1 */ ** ** See also: EndCoroutine */ -case OP_InitCoroutine: { /* jump */ +case OP_InitCoroutine: { /* jump0 */ assert( pOp->p1>0 && pOp->p1<=(p->nMem+1 - p->nCursor) ); assert( pOp->p2>=0 && pOp->p2nOp ); assert( pOp->p3>=0 && pOp->p3nOp ); @@ -93813,7 +94263,9 @@ jump_to_p2: ** ** The instruction at the address in register P1 is a Yield. ** Jump to the P2 parameter of that Yield. -** After the jump, register P1 becomes undefined. +** After the jump, the value register P1 is left with a value +** such that subsequent OP_Yields go back to the this same +** OP_EndCoroutine instruction. ** ** See also: InitCoroutine */ @@ -93825,8 +94277,8 @@ case OP_EndCoroutine: { /* in1 */ pCaller = &aOp[pIn1->u.i]; assert( pCaller->opcode==OP_Yield ); assert( pCaller->p2>=0 && pCaller->p2nOp ); + pIn1->u.i = (int)(pOp - p->aOp) - 1; pOp = &aOp[pCaller->p2 - 1]; - pIn1->flags = MEM_Undefined; break; } @@ -93843,7 +94295,7 @@ case OP_EndCoroutine: { /* in1 */ ** ** See also: InitCoroutine */ -case OP_Yield: { /* in1, jump */ +case OP_Yield: { /* in1, jump0 */ int pcDest; pIn1 = &aMem[pOp->p1]; assert( VdbeMemDynamic(pIn1)==0 ); @@ -94173,19 +94625,15 @@ case OP_Blob: { /* out2 */ break; } -/* Opcode: Variable P1 P2 * P4 * -** Synopsis: r[P2]=parameter(P1,P4) +/* Opcode: Variable P1 P2 * * * +** Synopsis: r[P2]=parameter(P1) ** ** Transfer the values of bound parameter P1 into register P2 -** -** If the parameter is named, then its name appears in P4. -** The P4 value is used by sqlite3_bind_parameter_name(). */ case OP_Variable: { /* out2 */ Mem *pVar; /* Value being transferred */ assert( pOp->p1>0 && pOp->p1<=p->nVar ); - assert( pOp->p4.z==0 || pOp->p4.z==sqlite3VListNumToName(p->pVList,pOp->p1) ); pVar = &p->aVar[pOp->p1 - 1]; if( sqlite3VdbeMemTooBig(pVar) ){ goto too_big; @@ -94706,7 +95154,7 @@ case OP_AddImm: { /* in1 */ ** without data loss, then jump immediately to P2, or if P2==0 ** raise an SQLITE_MISMATCH exception. */ -case OP_MustBeInt: { /* jump, in1 */ +case OP_MustBeInt: { /* jump0, in1 */ pIn1 = &aMem[pOp->p1]; if( (pIn1->flags & MEM_Int)==0 ){ applyAffinity(pIn1, SQLITE_AFF_NUMERIC, encoding); @@ -94747,7 +95195,7 @@ case OP_RealAffinity: { /* in1 */ } #endif -#ifndef SQLITE_OMIT_CAST +#if !defined(SQLITE_OMIT_CAST) && !defined(SQLITE_OMIT_ANALYZE) /* Opcode: Cast P1 P2 * * * ** Synopsis: affinity(r[P1]) ** @@ -96319,11 +96767,16 @@ case OP_MakeRecord: { switch( len ){ default: zPayload[7] = (u8)(v&0xff); v >>= 8; zPayload[6] = (u8)(v&0xff); v >>= 8; + /* no break */ deliberate_fall_through case 6: zPayload[5] = (u8)(v&0xff); v >>= 8; zPayload[4] = (u8)(v&0xff); v >>= 8; + /* no break */ deliberate_fall_through case 4: zPayload[3] = (u8)(v&0xff); v >>= 8; + /* no break */ deliberate_fall_through case 3: zPayload[2] = (u8)(v&0xff); v >>= 8; + /* no break */ deliberate_fall_through case 2: zPayload[1] = (u8)(v&0xff); v >>= 8; + /* no break */ deliberate_fall_through case 1: zPayload[0] = (u8)(v&0xff); } zPayload += len; @@ -97242,7 +97695,8 @@ case OP_SequenceTest: { ** is the only cursor opcode that works with a pseudo-table. ** ** P3 is the number of fields in the records that will be stored by -** the pseudo-table. +** the pseudo-table. If P2 is 0 or negative then the pseudo-cursor +** will return NULL for every column. */ case OP_OpenPseudo: { VdbeCursor *pCx; @@ -97385,10 +97839,10 @@ case OP_ColumnsUsed: { ** ** See also: Found, NotFound, SeekGt, SeekGe, SeekLt */ -case OP_SeekLT: /* jump, in3, group, ncycle */ -case OP_SeekLE: /* jump, in3, group, ncycle */ -case OP_SeekGE: /* jump, in3, group, ncycle */ -case OP_SeekGT: { /* jump, in3, group, ncycle */ +case OP_SeekLT: /* jump0, in3, group, ncycle */ +case OP_SeekLE: /* jump0, in3, group, ncycle */ +case OP_SeekGE: /* jump0, in3, group, ncycle */ +case OP_SeekGT: { /* jump0, in3, group, ncycle */ int res; /* Comparison result */ int oc; /* Opcode */ VdbeCursor *pC; /* The cursor to seek */ @@ -98055,7 +98509,7 @@ case OP_Found: { /* jump, in3, ncycle */ ** ** See also: Found, NotFound, NoConflict, SeekRowid */ -case OP_SeekRowid: { /* jump, in3, ncycle */ +case OP_SeekRowid: { /* jump0, in3, ncycle */ VdbeCursor *pC; BtCursor *pCrsr; int res; @@ -98814,7 +99268,7 @@ case OP_NullRow: { ** configured to use Prev, not Next. */ case OP_SeekEnd: /* ncycle */ -case OP_Last: { /* jump, ncycle */ +case OP_Last: { /* jump0, ncycle */ VdbeCursor *pC; BtCursor *pCrsr; int res; @@ -98848,28 +99302,38 @@ case OP_Last: { /* jump, ncycle */ break; } -/* Opcode: IfSmaller P1 P2 P3 * * +/* Opcode: IfSizeBetween P1 P2 P3 P4 * ** -** Estimate the number of rows in the table P1. Jump to P2 if that -** estimate is less than approximately 2**(0.1*P3). +** Let N be the approximate number of rows in the table or index +** with cursor P1 and let X be 10*log2(N) if N is positive or -1 +** if N is zero. +** +** Jump to P2 if X is in between P3 and P4, inclusive. */ -case OP_IfSmaller: { /* jump */ +case OP_IfSizeBetween: { /* jump */ VdbeCursor *pC; BtCursor *pCrsr; int res; i64 sz; assert( pOp->p1>=0 && pOp->p1nCursor ); + assert( pOp->p4type==P4_INT32 ); + assert( pOp->p3>=-1 && pOp->p3<=640*2 ); + assert( pOp->p4.i>=-1 && pOp->p4.i<=640*2 ); pC = p->apCsr[pOp->p1]; assert( pC!=0 ); pCrsr = pC->uc.pCursor; assert( pCrsr ); rc = sqlite3BtreeFirst(pCrsr, &res); if( rc ) goto abort_due_to_error; - if( res==0 ){ + if( res!=0 ){ + sz = -1; /* -Infinity encoding */ + }else{ sz = sqlite3BtreeRowCountEst(pCrsr); - if( ALWAYS(sz>=0) && sqlite3LogEst((u64)sz)p3 ) res = 1; + assert( sz>0 ); + sz = sqlite3LogEst((u64)sz); } + res = sz>=pOp->p3 && sz<=pOp->p4.i; VdbeBranchTaken(res!=0,2); if( res ) goto jump_to_p2; break; @@ -98922,7 +99386,7 @@ case OP_Sort: { /* jump ncycle */ ** from the beginning toward the end. In other words, the cursor is ** configured to use Next, not Prev. */ -case OP_Rewind: { /* jump, ncycle */ +case OP_Rewind: { /* jump0, ncycle */ VdbeCursor *pC; BtCursor *pCrsr; int res; @@ -99569,11 +100033,18 @@ case OP_CreateBtree: { /* out2 */ break; } -/* Opcode: SqlExec * * * P4 * +/* Opcode: SqlExec P1 P2 * P4 * ** ** Run the SQL statement or statements specified in the P4 string. -** Disable Auth and Trace callbacks while those statements are running if -** P1 is true. +** +** The P1 parameter is a bitmask of options: +** +** 0x0001 Disable Auth and Trace callbacks while the statements +** in P4 are running. +** +** 0x0002 Set db->nAnalysisLimit to P2 while the statements in +** P4 are running. +** */ case OP_SqlExec: { char *zErr; @@ -99581,6 +100052,7 @@ case OP_SqlExec: { sqlite3_xauth xAuth; #endif u8 mTrace; + int savedAnalysisLimit; sqlite3VdbeIncrWriteCounter(p, 0); db->nSqlExec++; @@ -99589,18 +100061,23 @@ case OP_SqlExec: { xAuth = db->xAuth; #endif mTrace = db->mTrace; - if( pOp->p1 ){ + savedAnalysisLimit = db->nAnalysisLimit; + if( pOp->p1 & 0x0001 ){ #ifndef SQLITE_OMIT_AUTHORIZATION db->xAuth = 0; #endif db->mTrace = 0; } + if( pOp->p1 & 0x0002 ){ + db->nAnalysisLimit = pOp->p2; + } rc = sqlite3_exec(db, pOp->p4.z, 0, 0, &zErr); db->nSqlExec--; #ifndef SQLITE_OMIT_AUTHORIZATION db->xAuth = xAuth; #endif db->mTrace = mTrace; + db->nAnalysisLimit = savedAnalysisLimit; if( zErr || rc ){ sqlite3VdbeError(p, "%s", zErr); sqlite3_free(zErr); @@ -99752,11 +100229,11 @@ case OP_DropTrigger: { /* Opcode: IntegrityCk P1 P2 P3 P4 P5 ** ** Do an analysis of the currently open database. Store in -** register P1 the text of an error message describing any problems. -** If no problems are found, store a NULL in register P1. +** register (P1+1) the text of an error message describing any problems. +** If no problems are found, store a NULL in register (P1+1). ** -** The register P3 contains one less than the maximum number of allowed errors. -** At most reg(P3) errors will be reported. +** The register (P1) contains one less than the maximum number of allowed +** errors. At most reg(P1) errors will be reported. ** In other words, the analysis stops as soon as reg(P1) errors are ** seen. Reg(P1) is updated with the number of errors remaining. ** @@ -99776,19 +100253,21 @@ case OP_IntegrityCk: { Mem *pnErr; /* Register keeping track of errors remaining */ assert( p->bIsReader ); + assert( pOp->p4type==P4_INTARRAY ); nRoot = pOp->p2; aRoot = pOp->p4.ai; assert( nRoot>0 ); + assert( aRoot!=0 ); assert( aRoot[0]==(Pgno)nRoot ); - assert( pOp->p3>0 && pOp->p3<=(p->nMem+1 - p->nCursor) ); - pnErr = &aMem[pOp->p3]; + assert( pOp->p1>0 && (pOp->p1+1)<=(p->nMem+1 - p->nCursor) ); + pnErr = &aMem[pOp->p1]; assert( (pnErr->flags & MEM_Int)!=0 ); assert( (pnErr->flags & (MEM_Str|MEM_Blob))==0 ); - pIn1 = &aMem[pOp->p1]; + pIn1 = &aMem[pOp->p1+1]; assert( pOp->p5nDb ); assert( DbMaskTest(p->btreeMask, pOp->p5) ); - rc = sqlite3BtreeIntegrityCheck(db, db->aDb[pOp->p5].pBt, &aRoot[1], nRoot, - (int)pnErr->u.i+1, &nErr, &z); + rc = sqlite3BtreeIntegrityCheck(db, db->aDb[pOp->p5].pBt, &aRoot[1], + &aMem[pOp->p3], nRoot, (int)pnErr->u.i+1, &nErr, &z); sqlite3VdbeMemSetNull(pIn1); if( nErr==0 ){ assert( z==0 ); @@ -99915,7 +100394,9 @@ case OP_RowSetTest: { /* jump, in1, in3 */ ** P1 contains the address of the memory cell that contains the first memory ** cell in an array of values used as arguments to the sub-program. P2 ** contains the address to jump to if the sub-program throws an IGNORE -** exception using the RAISE() function. Register P3 contains the address +** exception using the RAISE() function. P2 might be zero, if there is +** no possibility that an IGNORE exception will be raised. +** Register P3 contains the address ** of a memory cell in this (the parent) VM that is used to allocate the ** memory required by the sub-vdbe at runtime. ** @@ -99923,7 +100404,7 @@ case OP_RowSetTest: { /* jump, in1, in3 */ ** ** If P5 is non-zero, then recursive program invocation is enabled. */ -case OP_Program: { /* jump */ +case OP_Program: { /* jump0 */ int nMem; /* Number of memory registers for sub-program */ int nByte; /* Bytes of runtime space required for sub-program */ Mem *pRt; /* Register to allocate runtime space */ @@ -101472,7 +101953,7 @@ case OP_Filter: { /* jump */ ** error is encountered. */ case OP_Trace: -case OP_Init: { /* jump */ +case OP_Init: { /* jump0 */ int i; #ifndef SQLITE_OMIT_TRACE char *zTrace; @@ -105373,10 +105854,10 @@ static int bytecodevtabColumn( #ifdef SQLITE_ENABLE_STMT_SCANSTATUS case 9: /* nexec */ - sqlite3_result_int(ctx, pOp->nExec); + sqlite3_result_int64(ctx, pOp->nExec); break; case 10: /* ncycle */ - sqlite3_result_int(ctx, pOp->nCycle); + sqlite3_result_int64(ctx, pOp->nCycle); break; #else case 9: /* nexec */ @@ -106520,7 +107001,7 @@ static int lookupName( Parse *pParse, /* The parsing context */ const char *zDb, /* Name of the database containing table, or NULL */ const char *zTab, /* Name of table containing column, or NULL */ - const char *zCol, /* Name of the column. */ + const Expr *pRight, /* Name of the column. */ NameContext *pNC, /* The name context used to resolve the name */ Expr *pExpr /* Make this EXPR node point to the selected column */ ){ @@ -106537,6 +107018,7 @@ static int lookupName( Table *pTab = 0; /* Table holding the row */ Column *pCol; /* A column of pTab */ ExprList *pFJMatch = 0; /* Matches for FULL JOIN .. USING */ + const char *zCol = pRight->u.zToken; assert( pNC ); /* the name context cannot be NULL. */ assert( zCol ); /* The Z in X.Y.Z cannot be NULL */ @@ -106768,7 +107250,8 @@ static int lookupName( if( pParse->bReturning ){ if( (pNC->ncFlags & NC_UBaseReg)!=0 && ALWAYS(zTab==0 - || sqlite3StrICmp(zTab,pParse->pTriggerTab->zName)==0) + || sqlite3StrICmp(zTab,pParse->pTriggerTab->zName)==0 + || isValidSchemaTableName(zTab, pParse->pTriggerTab, 0)) ){ pExpr->iTable = op!=TK_DELETE; pTab = pParse->pTriggerTab; @@ -106872,6 +107355,11 @@ static int lookupName( && ALWAYS(VisibleRowid(pMatch->pTab) || pMatch->fg.isNestedFrom) ){ cnt = cntTab; +#if SQLITE_ALLOW_ROWID_IN_VIEW+0==2 + if( pMatch->pTab!=0 && IsView(pMatch->pTab) ){ + eNewExprOp = TK_NULL; + } +#endif if( pMatch->fg.isNestedFrom==0 ) pExpr->iColumn = -1; pExpr->affExpr = SQLITE_AFF_INTEGER; } @@ -107025,6 +107513,10 @@ static int lookupName( sqlite3ErrorMsg(pParse, "%s: %s.%s.%s", zErr, zDb, zTab, zCol); }else if( zTab ){ sqlite3ErrorMsg(pParse, "%s: %s.%s", zErr, zTab, zCol); + }else if( cnt==0 && ExprHasProperty(pRight,EP_DblQuoted) ){ + sqlite3ErrorMsg(pParse, "%s: \"%s\" - should this be a" + " string literal in single-quotes?", + zErr, zCol); }else{ sqlite3ErrorMsg(pParse, "%s: %s", zErr, zCol); } @@ -107058,8 +107550,12 @@ static int lookupName( ** If a generated column is referenced, set bits for every column ** of the table. */ - if( pExpr->iColumn>=0 && cnt==1 && pMatch!=0 ){ - pMatch->colUsed |= sqlite3ExprColUsed(pExpr); + if( pMatch ){ + if( pExpr->iColumn>=0 ){ + pMatch->colUsed |= sqlite3ExprColUsed(pExpr); + }else{ + pMatch->fg.rowidUsed = 1; + } } pExpr->op = eNewExprOp; @@ -107302,7 +107798,6 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ */ case TK_ID: case TK_DOT: { - const char *zColumn; const char *zTable; const char *zDb; Expr *pRight; @@ -107311,7 +107806,7 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ zDb = 0; zTable = 0; assert( !ExprHasProperty(pExpr, EP_IntValue) ); - zColumn = pExpr->u.zToken; + pRight = pExpr; }else{ Expr *pLeft = pExpr->pLeft; testcase( pNC->ncFlags & NC_IdxExpr ); @@ -107330,14 +107825,13 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ } assert( ExprUseUToken(pLeft) && ExprUseUToken(pRight) ); zTable = pLeft->u.zToken; - zColumn = pRight->u.zToken; assert( ExprUseYTab(pExpr) ); if( IN_RENAME_OBJECT ){ sqlite3RenameTokenRemap(pParse, (void*)pExpr, (void*)pRight); sqlite3RenameTokenRemap(pParse, (void*)&pExpr->y.pTab, (void*)pLeft); } } - return lookupName(pParse, zDb, zTable, zColumn, pNC, pExpr); + return lookupName(pParse, zDb, zTable, pRight, pNC, pExpr); } /* Resolve function names @@ -107513,11 +108007,9 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ #endif } } -#ifndef SQLITE_OMIT_WINDOWFUNC - else if( ExprHasProperty(pExpr, EP_WinFunc) ){ + else if( ExprHasProperty(pExpr, EP_WinFunc) || pExpr->pLeft ){ is_agg = 1; } -#endif sqlite3WalkExprList(pWalker, pList); if( is_agg ){ if( pExpr->pLeft ){ @@ -107587,6 +108079,7 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ testcase( pNC->ncFlags & NC_PartIdx ); testcase( pNC->ncFlags & NC_IdxExpr ); testcase( pNC->ncFlags & NC_GenCol ); + assert( pExpr->x.pSelect ); if( pNC->ncFlags & NC_SelfRef ){ notValidImpl(pParse, pNC, "subqueries", pExpr, pExpr); }else{ @@ -107595,6 +108088,7 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ assert( pNC->nRef>=nRef ); if( nRef!=pNC->nRef ){ ExprSetProperty(pExpr, EP_VarSelect); + pExpr->x.pSelect->selFlags |= SF_Correlated; } pNC->ncFlags |= NC_Subquery; } @@ -108120,6 +108614,7 @@ static int resolveSelectStep(Walker *pWalker, Select *p){ if( pOuterNC ) pOuterNC->nNestedSelect++; for(i=0; ipSrc->nSrc; i++){ SrcItem *pItem = &p->pSrc->a[i]; + assert( pItem->zName!=0 || pItem->pSelect!=0 );/* Test of tag-20240424-1*/ if( pItem->pSelect && (pItem->pSelect->selFlags & SF_Resolved)==0 ){ int nRef = pOuterNC ? pOuterNC->nRef : 0; const char *zSavedContext = pParse->zAuthContext; @@ -109426,11 +109921,12 @@ SQLITE_PRIVATE void sqlite3ExprSetErrorOffset(Expr *pExpr, int iOfst){ ** appear to be quoted. If the quotes were of the form "..." (double-quotes) ** then the EP_DblQuoted flag is set on the expression node. ** -** Special case: If op==TK_INTEGER and pToken points to a string that -** can be translated into a 32-bit integer, then the token is not -** stored in u.zToken. Instead, the integer values is written -** into u.iValue and the EP_IntValue flag is set. No extra storage +** Special case (tag-20240227-a): If op==TK_INTEGER and pToken points to +** a string that can be translated into a 32-bit integer, then the token is +** not stored in u.zToken. Instead, the integer values is written +** into u.iValue and the EP_IntValue flag is set. No extra storage ** is allocated to hold the integer text and the dequote flag is ignored. +** See also tag-20240227-b. */ SQLITE_PRIVATE Expr *sqlite3ExprAlloc( sqlite3 *db, /* Handle for sqlite3DbMallocRawNN() */ @@ -109446,7 +109942,7 @@ SQLITE_PRIVATE Expr *sqlite3ExprAlloc( if( pToken ){ if( op!=TK_INTEGER || pToken->z==0 || sqlite3GetInt32(pToken->z, &iValue)==0 ){ - nExtra = pToken->n+1; + nExtra = pToken->n+1; /* tag-20240227-a */ assert( iValue>=0 ); } } @@ -109878,6 +110374,7 @@ SQLITE_PRIVATE void sqlite3ExprAssignVarNumber(Parse *pParse, Expr *pExpr, u32 n static SQLITE_NOINLINE void sqlite3ExprDeleteNN(sqlite3 *db, Expr *p){ assert( p!=0 ); assert( db!=0 ); +exprDeleteRestart: assert( !ExprUseUValue(p) || p->u.iValue>=0 ); assert( !ExprUseYWin(p) || !ExprUseYSub(p) ); assert( !ExprUseYWin(p) || p->y.pWin!=0 || db->mallocFailed ); @@ -109893,7 +110390,6 @@ static SQLITE_NOINLINE void sqlite3ExprDeleteNN(sqlite3 *db, Expr *p){ if( !ExprHasProperty(p, (EP_TokenOnly|EP_Leaf)) ){ /* The Expr.x union is never used at the same time as Expr.pRight */ assert( (ExprUseXList(p) && p->x.pList==0) || p->pRight==0 ); - if( p->pLeft && p->op!=TK_SELECT_COLUMN ) sqlite3ExprDeleteNN(db, p->pLeft); if( p->pRight ){ assert( !ExprHasProperty(p, EP_WinFunc) ); sqlite3ExprDeleteNN(db, p->pRight); @@ -109908,6 +110404,19 @@ static SQLITE_NOINLINE void sqlite3ExprDeleteNN(sqlite3 *db, Expr *p){ } #endif } + if( p->pLeft && p->op!=TK_SELECT_COLUMN ){ + Expr *pLeft = p->pLeft; + if( !ExprHasProperty(p, EP_Static) + && !ExprHasProperty(pLeft, EP_Static) + ){ + /* Avoid unnecessary recursion on unary operators */ + sqlite3DbNNFreeNN(db, p); + p = pLeft; + goto exprDeleteRestart; + }else{ + sqlite3ExprDeleteNN(db, pLeft); + } + } } if( !ExprHasProperty(p, EP_Static) ){ sqlite3DbNNFreeNN(db, p); @@ -109940,11 +110449,11 @@ SQLITE_PRIVATE void sqlite3ClearOnOrUsing(sqlite3 *db, OnOrUsing *p){ ** ** The pExpr might be deleted immediately on an OOM error. ** -** The deferred delete is (currently) implemented by adding the -** pExpr to the pParse->pConstExpr list with a register number of 0. +** Return 0 if the delete was successfully deferred. Return non-zero +** if the delete happened immediately because of an OOM. */ -SQLITE_PRIVATE void sqlite3ExprDeferredDelete(Parse *pParse, Expr *pExpr){ - sqlite3ParserAddCleanup(pParse, sqlite3ExprDeleteGeneric, pExpr); +SQLITE_PRIVATE int sqlite3ExprDeferredDelete(Parse *pParse, Expr *pExpr){ + return 0==sqlite3ParserAddCleanup(pParse, sqlite3ExprDeleteGeneric, pExpr); } /* Invoke sqlite3RenameExprUnmap() and sqlite3ExprDelete() on the @@ -110380,17 +110889,19 @@ SQLITE_PRIVATE SrcList *sqlite3SrcListDup(sqlite3 *db, const SrcList *p, int fla pNewItem->iCursor = pOldItem->iCursor; pNewItem->addrFillSub = pOldItem->addrFillSub; pNewItem->regReturn = pOldItem->regReturn; + pNewItem->regResult = pOldItem->regResult; if( pNewItem->fg.isIndexedBy ){ pNewItem->u1.zIndexedBy = sqlite3DbStrDup(db, pOldItem->u1.zIndexedBy); + }else if( pNewItem->fg.isTabFunc ){ + pNewItem->u1.pFuncArg = + sqlite3ExprListDup(db, pOldItem->u1.pFuncArg, flags); + }else{ + pNewItem->u1.nRow = pOldItem->u1.nRow; } pNewItem->u2 = pOldItem->u2; if( pNewItem->fg.isCte ){ pNewItem->u2.pCteUse->nUse++; } - if( pNewItem->fg.isTabFunc ){ - pNewItem->u1.pFuncArg = - sqlite3ExprListDup(db, pOldItem->u1.pFuncArg, flags); - } pTab = pNewItem->pTab = pOldItem->pTab; if( pTab ){ pTab->nTabRef++; @@ -110856,6 +111367,54 @@ SQLITE_PRIVATE Expr *sqlite3ExprSimplifiedAndOr(Expr *pExpr){ return pExpr; } +/* +** pExpr is a TK_FUNCTION node. Try to determine whether or not the +** function is a constant function. A function is constant if all of +** the following are true: +** +** (1) It is a scalar function (not an aggregate or window function) +** (2) It has either the SQLITE_FUNC_CONSTANT or SQLITE_FUNC_SLOCHNG +** property. +** (3) All of its arguments are constants +** +** This routine sets pWalker->eCode to 0 if pExpr is not a constant. +** It makes no changes to pWalker->eCode if pExpr is constant. In +** every case, it returns WRC_Abort. +** +** Called as a service subroutine from exprNodeIsConstant(). +*/ +static SQLITE_NOINLINE int exprNodeIsConstantFunction( + Walker *pWalker, + Expr *pExpr +){ + int n; /* Number of arguments */ + ExprList *pList; /* List of arguments */ + FuncDef *pDef; /* The function */ + sqlite3 *db; /* The database */ + + assert( pExpr->op==TK_FUNCTION ); + if( ExprHasProperty(pExpr, EP_TokenOnly) + || (pList = pExpr->x.pList)==0 + ){; + n = 0; + }else{ + n = pList->nExpr; + sqlite3WalkExprList(pWalker, pList); + if( pWalker->eCode==0 ) return WRC_Abort; + } + db = pWalker->pParse->db; + pDef = sqlite3FindFunction(db, pExpr->u.zToken, n, ENC(db), 0); + if( pDef==0 + || pDef->xFinalize!=0 + || (pDef->funcFlags & (SQLITE_FUNC_CONSTANT|SQLITE_FUNC_SLOCHNG))==0 + || ExprHasProperty(pExpr, EP_WinFunc) + ){ + pWalker->eCode = 0; + return WRC_Abort; + } + return WRC_Prune; +} + /* ** These routines are Walker callbacks used to check expressions to @@ -110884,6 +111443,7 @@ SQLITE_PRIVATE Expr *sqlite3ExprSimplifiedAndOr(Expr *pExpr){ ** malformed schema error. */ static int exprNodeIsConstant(Walker *pWalker, Expr *pExpr){ + assert( pWalker->eCode>0 ); /* If pWalker->eCode is 2 then any term of the expression that comes from ** the ON or USING clauses of an outer join disqualifies the expression @@ -110903,6 +111463,8 @@ static int exprNodeIsConstant(Walker *pWalker, Expr *pExpr){ ){ if( pWalker->eCode==5 ) ExprSetProperty(pExpr, EP_FromDDL); return WRC_Continue; + }else if( pWalker->pParse ){ + return exprNodeIsConstantFunction(pWalker, pExpr); }else{ pWalker->eCode = 0; return WRC_Abort; @@ -110931,9 +111493,11 @@ static int exprNodeIsConstant(Walker *pWalker, Expr *pExpr){ case TK_IF_NULL_ROW: case TK_REGISTER: case TK_DOT: + case TK_RAISE: testcase( pExpr->op==TK_REGISTER ); testcase( pExpr->op==TK_IF_NULL_ROW ); testcase( pExpr->op==TK_DOT ); + testcase( pExpr->op==TK_RAISE ); pWalker->eCode = 0; return WRC_Abort; case TK_VARIABLE: @@ -110955,15 +111519,15 @@ static int exprNodeIsConstant(Walker *pWalker, Expr *pExpr){ return WRC_Continue; } } -static int exprIsConst(Expr *p, int initFlag, int iCur){ +static int exprIsConst(Parse *pParse, Expr *p, int initFlag){ Walker w; w.eCode = initFlag; + w.pParse = pParse; w.xExprCallback = exprNodeIsConstant; w.xSelectCallback = sqlite3SelectWalkFail; #ifdef SQLITE_DEBUG w.xSelectCallback2 = sqlite3SelectWalkAssert2; #endif - w.u.iCur = iCur; sqlite3WalkExpr(&w, p); return w.eCode; } @@ -110975,9 +111539,15 @@ static int exprIsConst(Expr *p, int initFlag, int iCur){ ** For the purposes of this function, a double-quoted string (ex: "abc") ** is considered a variable but a single-quoted string (ex: 'abc') is ** a constant. +** +** The pParse parameter may be NULL. But if it is NULL, there is no way +** to determine if function calls are constant or not, and hence all +** function calls will be considered to be non-constant. If pParse is +** not NULL, then a function call might be constant, depending on the +** function and on its parameters. */ -SQLITE_PRIVATE int sqlite3ExprIsConstant(Expr *p){ - return exprIsConst(p, 1, 0); +SQLITE_PRIVATE int sqlite3ExprIsConstant(Parse *pParse, Expr *p){ + return exprIsConst(pParse, p, 1); } /* @@ -110993,8 +111563,24 @@ SQLITE_PRIVATE int sqlite3ExprIsConstant(Expr *p){ ** can be added to the pParse->pConstExpr list and evaluated once when ** the prepared statement starts up. See sqlite3ExprCodeRunJustOnce(). */ -SQLITE_PRIVATE int sqlite3ExprIsConstantNotJoin(Expr *p){ - return exprIsConst(p, 2, 0); +static int sqlite3ExprIsConstantNotJoin(Parse *pParse, Expr *p){ + return exprIsConst(pParse, p, 2); +} + +/* +** This routine examines sub-SELECT statements as an expression is being +** walked as part of sqlite3ExprIsTableConstant(). Sub-SELECTs are considered +** constant as long as they are uncorrelated - meaning that they do not +** contain any terms from outer contexts. +*/ +static int exprSelectWalkTableConstant(Walker *pWalker, Select *pSelect){ + assert( pSelect!=0 ); + assert( pWalker->eCode==3 || pWalker->eCode==0 ); + if( (pSelect->selFlags & SF_Correlated)!=0 ){ + pWalker->eCode = 0; + return WRC_Abort; + } + return WRC_Prune; } /* @@ -111002,9 +111588,26 @@ SQLITE_PRIVATE int sqlite3ExprIsConstantNotJoin(Expr *p){ ** for any single row of the table with cursor iCur. In other words, the ** expression must not refer to any non-deterministic function nor any ** table other than iCur. +** +** Consider uncorrelated subqueries to be constants if the bAllowSubq +** parameter is true. */ -SQLITE_PRIVATE int sqlite3ExprIsTableConstant(Expr *p, int iCur){ - return exprIsConst(p, 3, iCur); +static int sqlite3ExprIsTableConstant(Expr *p, int iCur, int bAllowSubq){ + Walker w; + w.eCode = 3; + w.pParse = 0; + w.xExprCallback = exprNodeIsConstant; + if( bAllowSubq ){ + w.xSelectCallback = exprSelectWalkTableConstant; + }else{ + w.xSelectCallback = sqlite3SelectWalkFail; +#ifdef SQLITE_DEBUG + w.xSelectCallback2 = sqlite3SelectWalkAssert2; +#endif + } + w.u.iCur = iCur; + sqlite3WalkExpr(&w, p); + return w.eCode; } /* @@ -111022,7 +111625,10 @@ SQLITE_PRIVATE int sqlite3ExprIsTableConstant(Expr *p, int iCur){ ** ** (1) pExpr cannot refer to any table other than pSrc->iCursor. ** -** (2) pExpr cannot use subqueries or non-deterministic functions. +** (2a) pExpr cannot use subqueries unless the bAllowSubq parameter is +** true and the subquery is non-correlated +** +** (2b) pExpr cannot use non-deterministic functions. ** ** (3) pSrc cannot be part of the left operand for a RIGHT JOIN. ** (Is there some way to relax this constraint?) @@ -111051,7 +111657,8 @@ SQLITE_PRIVATE int sqlite3ExprIsTableConstant(Expr *p, int iCur){ SQLITE_PRIVATE int sqlite3ExprIsSingleTableConstraint( Expr *pExpr, /* The constraint */ const SrcList *pSrcList, /* Complete FROM clause */ - int iSrc /* Which element of pSrcList to use */ + int iSrc, /* Which element of pSrcList to use */ + int bAllowSubq /* Allow non-correlated subqueries */ ){ const SrcItem *pSrc = &pSrcList->a[iSrc]; if( pSrc->fg.jointype & JT_LTORJ ){ @@ -111076,7 +111683,8 @@ SQLITE_PRIVATE int sqlite3ExprIsSingleTableConstraint( } } } - return sqlite3ExprIsTableConstant(pExpr, pSrc->iCursor); /* rules (1), (2) */ + /* Rules (1), (2a), and (2b) handled by the following: */ + return sqlite3ExprIsTableConstant(pExpr, pSrc->iCursor, bAllowSubq); } @@ -111161,7 +111769,7 @@ SQLITE_PRIVATE int sqlite3ExprIsConstantOrGroupBy(Parse *pParse, Expr *p, ExprLi */ SQLITE_PRIVATE int sqlite3ExprIsConstantOrFunction(Expr *p, u8 isInit){ assert( isInit==0 || isInit==1 ); - return exprIsConst(p, 4+isInit, 0); + return exprIsConst(0, p, 4+isInit); } #ifdef SQLITE_ENABLE_CURSOR_HINTS @@ -111409,13 +112017,13 @@ static void sqlite3SetHasNullFlag(Vdbe *v, int iCur, int regHasNull){ ** The argument is an IN operator with a list (not a subquery) on the ** right-hand side. Return TRUE if that list is constant. */ -static int sqlite3InRhsIsConstant(Expr *pIn){ +static int sqlite3InRhsIsConstant(Parse *pParse, Expr *pIn){ Expr *pLHS; int res; assert( !ExprHasProperty(pIn, EP_xIsSelect) ); pLHS = pIn->pLeft; pIn->pLeft = 0; - res = sqlite3ExprIsConstant(pIn); + res = sqlite3ExprIsConstant(pParse, pIn); pIn->pLeft = pLHS; return res; } @@ -111684,7 +112292,7 @@ SQLITE_PRIVATE int sqlite3FindInIndex( if( eType==0 && (inFlags & IN_INDEX_NOOP_OK) && ExprUseXList(pX) - && (!sqlite3InRhsIsConstant(pX) || pX->x.pList->nExpr<=2) + && (!sqlite3InRhsIsConstant(pParse,pX) || pX->x.pList->nExpr<=2) ){ pParse->nTab--; /* Back out the allocation of the unused cursor */ iTab = -1; /* Cursor is not allocated */ @@ -111967,7 +112575,7 @@ SQLITE_PRIVATE void sqlite3CodeRhsOfIN( ** this code only executes once. Because for a non-constant ** expression we need to rerun this code each time. */ - if( addrOnce && !sqlite3ExprIsConstant(pE2) ){ + if( addrOnce && !sqlite3ExprIsConstant(pParse, pE2) ){ sqlite3VdbeChangeToNoop(v, addrOnce-1); sqlite3VdbeChangeToNoop(v, addrOnce); ExprClearProperty(pExpr, EP_Subrtn); @@ -113131,12 +113739,6 @@ expr_code_doover: assert( pExpr->u.zToken!=0 ); assert( pExpr->u.zToken[0]!=0 ); sqlite3VdbeAddOp2(v, OP_Variable, pExpr->iColumn, target); - if( pExpr->u.zToken[1]!=0 ){ - const char *z = sqlite3VListNumToName(pParse->pVList, pExpr->iColumn); - assert( pExpr->u.zToken[0]=='?' || (z && !strcmp(pExpr->u.zToken, z)) ); - pParse->pVList[0] = 0; /* Indicate VList may no longer be enlarged */ - sqlite3VdbeAppendP4(v, (char*)z, P4_STATIC); - } return target; } case TK_REGISTER: { @@ -113310,7 +113912,9 @@ expr_code_doover: } #endif - if( ConstFactorOk(pParse) && sqlite3ExprIsConstantNotJoin(pExpr) ){ + if( ConstFactorOk(pParse) + && sqlite3ExprIsConstantNotJoin(pParse,pExpr) + ){ /* SQL functions can be expensive. So try to avoid running them ** multiple times if we know they always give the same result */ return sqlite3ExprCodeRunJustOnce(pParse, pExpr, -1); @@ -113341,7 +113945,7 @@ expr_code_doover: } for(i=0; ia[i].pExpr) ){ + if( i<32 && sqlite3ExprIsConstant(pParse, pFarg->a[i].pExpr) ){ testcase( i==31 ); constMask |= MASKBIT32(i); } @@ -113483,8 +114087,9 @@ expr_code_doover: if( !ExprHasProperty(pExpr, EP_Collate) ){ /* A TK_COLLATE Expr node without the EP_Collate tag is a so-called ** "SOFT-COLLATE" that is added to constraints that are pushed down - ** from outer queries into sub-queries by the push-down optimization. - ** Clear subtypes as subtypes may not cross a subquery boundary. + ** from outer queries into sub-queries by the WHERE-clause push-down + ** optimization. Clear subtypes as subtypes may not cross a subquery + ** boundary. */ assert( pExpr->pLeft ); sqlite3ExprCode(pParse, pExpr->pLeft, target); @@ -113808,7 +114413,7 @@ SQLITE_PRIVATE int sqlite3ExprCodeTemp(Parse *pParse, Expr *pExpr, int *pReg){ if( ConstFactorOk(pParse) && ALWAYS(pExpr!=0) && pExpr->op!=TK_REGISTER - && sqlite3ExprIsConstantNotJoin(pExpr) + && sqlite3ExprIsConstantNotJoin(pParse, pExpr) ){ *pReg = 0; r2 = sqlite3ExprCodeRunJustOnce(pParse, pExpr, -1); @@ -113872,7 +114477,7 @@ SQLITE_PRIVATE void sqlite3ExprCodeCopy(Parse *pParse, Expr *pExpr, int target){ ** might choose to code the expression at initialization time. */ SQLITE_PRIVATE void sqlite3ExprCodeFactorable(Parse *pParse, Expr *pExpr, int target){ - if( pParse->okConstFactor && sqlite3ExprIsConstantNotJoin(pExpr) ){ + if( pParse->okConstFactor && sqlite3ExprIsConstantNotJoin(pParse,pExpr) ){ sqlite3ExprCodeRunJustOnce(pParse, pExpr, target); }else{ sqlite3ExprCodeCopy(pParse, pExpr, target); @@ -113931,7 +114536,7 @@ SQLITE_PRIVATE int sqlite3ExprCodeExprList( sqlite3VdbeAddOp2(v, copyOp, j+srcReg-1, target+i); } }else if( (flags & SQLITE_ECEL_FACTOR)!=0 - && sqlite3ExprIsConstantNotJoin(pExpr) + && sqlite3ExprIsConstantNotJoin(pParse,pExpr) ){ sqlite3ExprCodeRunJustOnce(pParse, pExpr, target+i); }else{ @@ -115082,9 +115687,8 @@ static int agginfoPersistExprCb(Walker *pWalker, Expr *pExpr){ && pAggInfo->aCol[iAgg].pCExpr==pExpr ){ pExpr = sqlite3ExprDup(db, pExpr, 0); - if( pExpr ){ + if( pExpr && !sqlite3ExprDeferredDelete(pParse, pExpr) ){ pAggInfo->aCol[iAgg].pCExpr = pExpr; - sqlite3ExprDeferredDelete(pParse, pExpr); } } }else{ @@ -115093,9 +115697,8 @@ static int agginfoPersistExprCb(Walker *pWalker, Expr *pExpr){ && pAggInfo->aFunc[iAgg].pFExpr==pExpr ){ pExpr = sqlite3ExprDup(db, pExpr, 0); - if( pExpr ){ + if( pExpr && !sqlite3ExprDeferredDelete(pParse, pExpr) ){ pAggInfo->aFunc[iAgg].pFExpr = pExpr; - sqlite3ExprDeferredDelete(pParse, pExpr); } } } @@ -117796,7 +118399,12 @@ SQLITE_PRIVATE void sqlite3AlterDropColumn(Parse *pParse, SrcList *pSrc, const T if( i==pTab->iPKey ){ sqlite3VdbeAddOp2(v, OP_Null, 0, regOut); }else{ + char aff = pTab->aCol[i].affinity; + if( aff==SQLITE_AFF_REAL ){ + pTab->aCol[i].affinity = SQLITE_AFF_NUMERIC; + } sqlite3ExprCodeGetColumnOfTable(v, pTab, iCur, i, regOut); + pTab->aCol[i].affinity = aff; } nField++; } @@ -118715,7 +119323,7 @@ static void statGet( if( iVal==2 && p->nRow*10 <= nDistinct*11 ) iVal = 1; sqlite3_str_appendf(&sStat, " %llu", iVal); #ifdef SQLITE_ENABLE_STAT4 - assert( p->current.anEq[i] ); + assert( p->current.anEq[i] || p->nRow==0 ); #endif } sqlite3ResultStrAccum(context, &sStat); @@ -118900,7 +119508,7 @@ static void analyzeOneTable( for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ int nCol; /* Number of columns in pIdx. "N" */ - int addrRewind; /* Address of "OP_Rewind iIdxCur" */ + int addrGotoEnd; /* Address of "OP_Rewind iIdxCur" */ int addrNextRow; /* Address of "next_row:" */ const char *zIdxName; /* Name of the index */ int nColTest; /* Number of columns to test for changes */ @@ -118924,9 +119532,14 @@ static void analyzeOneTable( /* ** Pseudo-code for loop that calls stat_push(): ** - ** Rewind csr - ** if eof(csr) goto end_of_scan; ** regChng = 0 + ** Rewind csr + ** if eof(csr){ + ** stat_init() with count = 0; + ** goto end_of_scan; + ** } + ** count() + ** stat_init() ** goto chng_addr_0; ** ** next_row: @@ -118965,41 +119578,36 @@ static void analyzeOneTable( sqlite3VdbeSetP4KeyInfo(pParse, pIdx); VdbeComment((v, "%s", pIdx->zName)); - /* Invoke the stat_init() function. The arguments are: + /* Implementation of the following: ** + ** regChng = 0 + ** Rewind csr + ** if eof(csr){ + ** stat_init() with count = 0; + ** goto end_of_scan; + ** } + ** count() + ** stat_init() + ** goto chng_addr_0; + */ + assert( regTemp2==regStat+4 ); + sqlite3VdbeAddOp2(v, OP_Integer, db->nAnalysisLimit, regTemp2); + + /* Arguments to stat_init(): ** (1) the number of columns in the index including the rowid ** (or for a WITHOUT ROWID table, the number of PK columns), ** (2) the number of columns in the key without the rowid/pk - ** (3) estimated number of rows in the index, - */ + ** (3) estimated number of rows in the index. */ sqlite3VdbeAddOp2(v, OP_Integer, nCol, regStat+1); assert( regRowid==regStat+2 ); sqlite3VdbeAddOp2(v, OP_Integer, pIdx->nKeyCol, regRowid); -#ifdef SQLITE_ENABLE_STAT4 - if( OptimizationEnabled(db, SQLITE_Stat4) ){ - sqlite3VdbeAddOp2(v, OP_Count, iIdxCur, regTemp); - addrRewind = sqlite3VdbeAddOp1(v, OP_Rewind, iIdxCur); - VdbeCoverage(v); - }else -#endif - { - addrRewind = sqlite3VdbeAddOp1(v, OP_Rewind, iIdxCur); - VdbeCoverage(v); - sqlite3VdbeAddOp3(v, OP_Count, iIdxCur, regTemp, 1); - } - assert( regTemp2==regStat+4 ); - sqlite3VdbeAddOp2(v, OP_Integer, db->nAnalysisLimit, regTemp2); + sqlite3VdbeAddOp3(v, OP_Count, iIdxCur, regTemp, + OptimizationDisabled(db, SQLITE_Stat4)); sqlite3VdbeAddFunctionCall(pParse, 0, regStat+1, regStat, 4, &statInitFuncdef, 0); + addrGotoEnd = sqlite3VdbeAddOp1(v, OP_Rewind, iIdxCur); + VdbeCoverage(v); - /* Implementation of the following: - ** - ** Rewind csr - ** if eof(csr) goto end_of_scan; - ** regChng = 0 - ** goto next_push_0; - ** - */ sqlite3VdbeAddOp2(v, OP_Integer, 0, regChng); addrNextRow = sqlite3VdbeCurrentAddr(v); @@ -119106,6 +119714,12 @@ static void analyzeOneTable( } /* Add the entry to the stat1 table. */ + if( pIdx->pPartIdxWhere ){ + /* Partial indexes might get a zero-entry in sqlite_stat1. But + ** an empty table is omitted from sqlite_stat1. */ + sqlite3VdbeJumpHere(v, addrGotoEnd); + addrGotoEnd = 0; + } callStatGet(pParse, regStat, STAT_GET_STAT1, regStat1); assert( "BBB"[0]==SQLITE_AFF_TEXT ); sqlite3VdbeAddOp4(v, OP_MakeRecord, regTabname, 3, regTemp, "BBB", 0); @@ -119129,6 +119743,13 @@ static void analyzeOneTable( int addrIsNull; u8 seekOp = HasRowid(pTab) ? OP_NotExists : OP_NotFound; + /* No STAT4 data is generated if the number of rows is zero */ + if( addrGotoEnd==0 ){ + sqlite3VdbeAddOp2(v, OP_Cast, regStat1, SQLITE_AFF_INTEGER); + addrGotoEnd = sqlite3VdbeAddOp1(v, OP_IfNot, regStat1); + VdbeCoverage(v); + } + if( doOnce ){ int mxCol = nCol; Index *pX; @@ -119181,7 +119802,7 @@ static void analyzeOneTable( #endif /* SQLITE_ENABLE_STAT4 */ /* End of analysis */ - sqlite3VdbeJumpHere(v, addrRewind); + if( addrGotoEnd ) sqlite3VdbeJumpHere(v, addrGotoEnd); } @@ -120930,7 +121551,7 @@ SQLITE_PRIVATE void sqlite3FinishCoding(Parse *pParse){ } sqlite3VdbeAddOp0(v, OP_Halt); -#if SQLITE_USER_AUTHENTICATION +#if SQLITE_USER_AUTHENTICATION && !defined(SQLITE_OMIT_SHARED_CACHE) if( pParse->nTableLock>0 && db->init.busy==0 ){ sqlite3UserAuthInit(db); if( db->auth.authLevelrc = SQLITE_ERROR; pParse->nErr++; return; } + iCsr = pParse->nTab++; regYield = ++pParse->nMem; regRec = ++pParse->nMem; regRowid = ++pParse->nMem; - assert(pParse->nTab==1); sqlite3MayAbort(pParse); - sqlite3VdbeAddOp3(v, OP_OpenWrite, 1, pParse->regRoot, iDb); + sqlite3VdbeAddOp3(v, OP_OpenWrite, iCsr, pParse->regRoot, iDb); sqlite3VdbeChangeP5(v, OPFLAG_P2ISREG); - pParse->nTab = 2; addrTop = sqlite3VdbeCurrentAddr(v) + 1; sqlite3VdbeAddOp3(v, OP_InitCoroutine, regYield, 0, addrTop); if( pParse->nErr ) return; @@ -123603,11 +124224,11 @@ SQLITE_PRIVATE void sqlite3EndTable( VdbeCoverage(v); sqlite3VdbeAddOp3(v, OP_MakeRecord, dest.iSdst, dest.nSdst, regRec); sqlite3TableAffinity(v, p, 0); - sqlite3VdbeAddOp2(v, OP_NewRowid, 1, regRowid); - sqlite3VdbeAddOp3(v, OP_Insert, 1, regRec, regRowid); + sqlite3VdbeAddOp2(v, OP_NewRowid, iCsr, regRowid); + sqlite3VdbeAddOp3(v, OP_Insert, iCsr, regRec, regRowid); sqlite3VdbeGoto(v, addrInsLoop); sqlite3VdbeJumpHere(v, addrInsLoop); - sqlite3VdbeAddOp1(v, OP_Close, 1); + sqlite3VdbeAddOp1(v, OP_Close, iCsr); } /* Compute the complete text of the CREATE statement */ @@ -123664,13 +124285,10 @@ SQLITE_PRIVATE void sqlite3EndTable( /* Test for cycles in generated columns and illegal expressions ** in CHECK constraints and in DEFAULT clauses. */ if( p->tabFlags & TF_HasGenerated ){ - sqlite3VdbeAddOp4(v, OP_SqlExec, 1, 0, 0, + sqlite3VdbeAddOp4(v, OP_SqlExec, 0x0001, 0, 0, sqlite3MPrintf(db, "SELECT*FROM\"%w\".\"%w\"", db->aDb[iDb].zDbSName, p->zName), P4_DYNAMIC); } - sqlite3VdbeAddOp4(v, OP_SqlExec, 1, 0, 0, - sqlite3MPrintf(db, "PRAGMA \"%w\".integrity_check(%Q)", - db->aDb[iDb].zDbSName, p->zName), P4_DYNAMIC); } /* Add the table to the in-memory representation of the database. @@ -132844,6 +133462,195 @@ SQLITE_PRIVATE void sqlite3AutoincrementEnd(Parse *pParse){ # define autoIncStep(A,B,C) #endif /* SQLITE_OMIT_AUTOINCREMENT */ +/* +** If argument pVal is a Select object returned by an sqlite3MultiValues() +** that was able to use the co-routine optimization, finish coding the +** co-routine. +*/ +SQLITE_PRIVATE void sqlite3MultiValuesEnd(Parse *pParse, Select *pVal){ + if( ALWAYS(pVal) && pVal->pSrc->nSrc>0 ){ + SrcItem *pItem = &pVal->pSrc->a[0]; + sqlite3VdbeEndCoroutine(pParse->pVdbe, pItem->regReturn); + sqlite3VdbeJumpHere(pParse->pVdbe, pItem->addrFillSub - 1); + } +} + +/* +** Return true if all expressions in the expression-list passed as the +** only argument are constant. +*/ +static int exprListIsConstant(Parse *pParse, ExprList *pRow){ + int ii; + for(ii=0; iinExpr; ii++){ + if( 0==sqlite3ExprIsConstant(pParse, pRow->a[ii].pExpr) ) return 0; + } + return 1; +} + +/* +** Return true if all expressions in the expression-list passed as the +** only argument are both constant and have no affinity. +*/ +static int exprListIsNoAffinity(Parse *pParse, ExprList *pRow){ + int ii; + if( exprListIsConstant(pParse,pRow)==0 ) return 0; + for(ii=0; iinExpr; ii++){ + Expr *pExpr = pRow->a[ii].pExpr; + assert( pExpr->op!=TK_RAISE ); + assert( pExpr->affExpr==0 ); + if( 0!=sqlite3ExprAffinity(pExpr) ) return 0; + } + return 1; + +} + +/* +** This function is called by the parser for the second and subsequent +** rows of a multi-row VALUES clause. Argument pLeft is the part of +** the VALUES clause already parsed, argument pRow is the vector of values +** for the new row. The Select object returned represents the complete +** VALUES clause, including the new row. +** +** There are two ways in which this may be achieved - by incremental +** coding of a co-routine (the "co-routine" method) or by returning a +** Select object equivalent to the following (the "UNION ALL" method): +** +** "pLeft UNION ALL SELECT pRow" +** +** If the VALUES clause contains a lot of rows, this compound Select +** object may consume a lot of memory. +** +** When the co-routine method is used, each row that will be returned +** by the VALUES clause is coded into part of a co-routine as it is +** passed to this function. The returned Select object is equivalent to: +** +** SELECT * FROM ( +** Select object to read co-routine +** ) +** +** The co-routine method is used in most cases. Exceptions are: +** +** a) If the current statement has a WITH clause. This is to avoid +** statements like: +** +** WITH cte AS ( VALUES('x'), ('y') ... ) +** SELECT * FROM cte AS a, cte AS b; +** +** This will not work, as the co-routine uses a hard-coded register +** for its OP_Yield instructions, and so it is not possible for two +** cursors to iterate through it concurrently. +** +** b) The schema is currently being parsed (i.e. the VALUES clause is part +** of a schema item like a VIEW or TRIGGER). In this case there is no VM +** being generated when parsing is taking place, and so generating +** a co-routine is not possible. +** +** c) There are non-constant expressions in the VALUES clause (e.g. +** the VALUES clause is part of a correlated sub-query). +** +** d) One or more of the values in the first row of the VALUES clause +** has an affinity (i.e. is a CAST expression). This causes problems +** because the complex rules SQLite uses (see function +** sqlite3SubqueryColumnTypes() in select.c) to determine the effective +** affinity of such a column for all rows require access to all values in +** the column simultaneously. +*/ +SQLITE_PRIVATE Select *sqlite3MultiValues(Parse *pParse, Select *pLeft, ExprList *pRow){ + + if( pParse->bHasWith /* condition (a) above */ + || pParse->db->init.busy /* condition (b) above */ + || exprListIsConstant(pParse,pRow)==0 /* condition (c) above */ + || (pLeft->pSrc->nSrc==0 && + exprListIsNoAffinity(pParse,pLeft->pEList)==0) /* condition (d) above */ + || IN_SPECIAL_PARSE + ){ + /* The co-routine method cannot be used. Fall back to UNION ALL. */ + Select *pSelect = 0; + int f = SF_Values | SF_MultiValue; + if( pLeft->pSrc->nSrc ){ + sqlite3MultiValuesEnd(pParse, pLeft); + f = SF_Values; + }else if( pLeft->pPrior ){ + /* In this case set the SF_MultiValue flag only if it was set on pLeft */ + f = (f & pLeft->selFlags); + } + pSelect = sqlite3SelectNew(pParse, pRow, 0, 0, 0, 0, 0, f, 0); + pLeft->selFlags &= ~SF_MultiValue; + if( pSelect ){ + pSelect->op = TK_ALL; + pSelect->pPrior = pLeft; + pLeft = pSelect; + } + }else{ + SrcItem *p = 0; /* SrcItem that reads from co-routine */ + + if( pLeft->pSrc->nSrc==0 ){ + /* Co-routine has not yet been started and the special Select object + ** that accesses the co-routine has not yet been created. This block + ** does both those things. */ + Vdbe *v = sqlite3GetVdbe(pParse); + Select *pRet = sqlite3SelectNew(pParse, 0, 0, 0, 0, 0, 0, 0, 0); + + /* Ensure the database schema has been read. This is to ensure we have + ** the correct text encoding. */ + if( (pParse->db->mDbFlags & DBFLAG_SchemaKnownOk)==0 ){ + sqlite3ReadSchema(pParse); + } + + if( pRet ){ + SelectDest dest; + pRet->pSrc->nSrc = 1; + pRet->pPrior = pLeft->pPrior; + pRet->op = pLeft->op; + pLeft->pPrior = 0; + pLeft->op = TK_SELECT; + assert( pLeft->pNext==0 ); + assert( pRet->pNext==0 ); + p = &pRet->pSrc->a[0]; + p->pSelect = pLeft; + p->fg.viaCoroutine = 1; + p->addrFillSub = sqlite3VdbeCurrentAddr(v) + 1; + p->regReturn = ++pParse->nMem; + p->iCursor = -1; + p->u1.nRow = 2; + sqlite3VdbeAddOp3(v,OP_InitCoroutine,p->regReturn,0,p->addrFillSub); + sqlite3SelectDestInit(&dest, SRT_Coroutine, p->regReturn); + + /* Allocate registers for the output of the co-routine. Do so so + ** that there are two unused registers immediately before those + ** used by the co-routine. This allows the code in sqlite3Insert() + ** to use these registers directly, instead of copying the output + ** of the co-routine to a separate array for processing. */ + dest.iSdst = pParse->nMem + 3; + dest.nSdst = pLeft->pEList->nExpr; + pParse->nMem += 2 + dest.nSdst; + + pLeft->selFlags |= SF_MultiValue; + sqlite3Select(pParse, pLeft, &dest); + p->regResult = dest.iSdst; + assert( pParse->nErr || dest.iSdst>0 ); + pLeft = pRet; + } + }else{ + p = &pLeft->pSrc->a[0]; + assert( !p->fg.isTabFunc && !p->fg.isIndexedBy ); + p->u1.nRow++; + } + + if( pParse->nErr==0 ){ + assert( p!=0 ); + if( p->pSelect->pEList->nExpr!=pRow->nExpr ){ + sqlite3SelectWrongNumTermsError(pParse, p->pSelect); + }else{ + sqlite3ExprCodeExprList(pParse, pRow, p->regResult, 0, 0); + sqlite3VdbeAddOp1(pParse->pVdbe, OP_Yield, p->regReturn); + } + } + sqlite3ExprListDelete(pParse->db, pRow); + } + + return pLeft; +} /* Forward declaration */ static int xferOptimization( @@ -133180,25 +133987,40 @@ SQLITE_PRIVATE void sqlite3Insert( if( pSelect ){ /* Data is coming from a SELECT or from a multi-row VALUES clause. ** Generate a co-routine to run the SELECT. */ - int regYield; /* Register holding co-routine entry-point */ - int addrTop; /* Top of the co-routine */ int rc; /* Result code */ - regYield = ++pParse->nMem; - addrTop = sqlite3VdbeCurrentAddr(v) + 1; - sqlite3VdbeAddOp3(v, OP_InitCoroutine, regYield, 0, addrTop); - sqlite3SelectDestInit(&dest, SRT_Coroutine, regYield); - dest.iSdst = bIdListInOrder ? regData : 0; - dest.nSdst = pTab->nCol; - rc = sqlite3Select(pParse, pSelect, &dest); - regFromSelect = dest.iSdst; - assert( db->pParse==pParse ); - if( rc || pParse->nErr ) goto insert_cleanup; - assert( db->mallocFailed==0 ); - sqlite3VdbeEndCoroutine(v, regYield); - sqlite3VdbeJumpHere(v, addrTop - 1); /* label B: */ - assert( pSelect->pEList ); - nColumn = pSelect->pEList->nExpr; + if( pSelect->pSrc->nSrc==1 + && pSelect->pSrc->a[0].fg.viaCoroutine + && pSelect->pPrior==0 + ){ + SrcItem *pItem = &pSelect->pSrc->a[0]; + dest.iSDParm = pItem->regReturn; + regFromSelect = pItem->regResult; + nColumn = pItem->pSelect->pEList->nExpr; + ExplainQueryPlan((pParse, 0, "SCAN %S", pItem)); + if( bIdListInOrder && nColumn==pTab->nCol ){ + regData = regFromSelect; + regRowid = regData - 1; + regIns = regRowid - (IsVirtual(pTab) ? 1 : 0); + } + }else{ + int addrTop; /* Top of the co-routine */ + int regYield = ++pParse->nMem; + addrTop = sqlite3VdbeCurrentAddr(v) + 1; + sqlite3VdbeAddOp3(v, OP_InitCoroutine, regYield, 0, addrTop); + sqlite3SelectDestInit(&dest, SRT_Coroutine, regYield); + dest.iSdst = bIdListInOrder ? regData : 0; + dest.nSdst = pTab->nCol; + rc = sqlite3Select(pParse, pSelect, &dest); + regFromSelect = dest.iSdst; + assert( db->pParse==pParse ); + if( rc || pParse->nErr ) goto insert_cleanup; + assert( db->mallocFailed==0 ); + sqlite3VdbeEndCoroutine(v, regYield); + sqlite3VdbeJumpHere(v, addrTop - 1); /* label B: */ + assert( pSelect->pEList ); + nColumn = pSelect->pEList->nExpr; + } /* Set useTempTable to TRUE if the result of the SELECT statement ** should be written into a temporary table (template 4). Set to @@ -137923,6 +138745,34 @@ static const PragmaName aPragmaName[] = { /************** End of pragma.h **********************************************/ /************** Continuing where we left off in pragma.c *********************/ +/* +** When the 0x10 bit of PRAGMA optimize is set, any ANALYZE commands +** will be run with an analysis_limit set to the lessor of the value of +** the following macro or to the actual analysis_limit if it is non-zero, +** in order to prevent PRAGMA optimize from running for too long. +** +** The value of 2000 is chosen emperically so that the worst-case run-time +** for PRAGMA optimize does not exceed 100 milliseconds against a variety +** of test databases on a RaspberryPI-4 compiled using -Os and without +** -DSQLITE_DEBUG. Of course, your mileage may vary. For the purpose of +** this paragraph, "worst-case" means that ANALYZE ends up being +** run on every table in the database. The worst case typically only +** happens if PRAGMA optimize is run on a database file for which ANALYZE +** has not been previously run and the 0x10000 flag is included so that +** all tables are analyzed. The usual case for PRAGMA optimize is that +** no ANALYZE commands will be run at all, or if any ANALYZE happens it +** will be against a single table, so that expected timing for PRAGMA +** optimize on a PI-4 is more like 1 millisecond or less with the 0x10000 +** flag or less than 100 microseconds without the 0x10000 flag. +** +** An analysis limit of 2000 is almost always sufficient for the query +** planner to fully characterize an index. The additional accuracy from +** a larger analysis is not usually helpful. +*/ +#ifndef SQLITE_DEFAULT_OPTIMIZE_LIMIT +# define SQLITE_DEFAULT_OPTIMIZE_LIMIT 2000 +#endif + /* ** Interpret the given string as a safety level. Return 0 for OFF, ** 1 for ON or NORMAL, 2 for FULL, and 3 for EXTRA. Return 1 for an empty or @@ -139568,7 +140418,7 @@ SQLITE_PRIVATE void sqlite3Pragma( /* Set the maximum error count */ mxErr = SQLITE_INTEGRITY_CHECK_ERROR_MAX; if( zRight ){ - if( sqlite3GetInt32(zRight, &mxErr) ){ + if( sqlite3GetInt32(pValue->z, &mxErr) ){ if( mxErr<=0 ){ mxErr = SQLITE_INTEGRITY_CHECK_ERROR_MAX; } @@ -139585,7 +140435,6 @@ SQLITE_PRIVATE void sqlite3Pragma( Hash *pTbls; /* Set of all tables in the schema */ int *aRoot; /* Array of root page numbers of all btrees */ int cnt = 0; /* Number of entries in aRoot[] */ - int mxIdx = 0; /* Maximum number of indexes for any table */ if( OMIT_TEMPDB && i==1 ) continue; if( iDb>=0 && i!=iDb ) continue; @@ -139607,7 +140456,6 @@ SQLITE_PRIVATE void sqlite3Pragma( if( pObjTab && pObjTab!=pTab ) continue; if( HasRowid(pTab) ) cnt++; for(nIdx=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, nIdx++){ cnt++; } - if( nIdx>mxIdx ) mxIdx = nIdx; } if( cnt==0 ) continue; if( pObjTab ) cnt++; @@ -139627,11 +140475,11 @@ SQLITE_PRIVATE void sqlite3Pragma( aRoot[0] = cnt; /* Make sure sufficient number of registers have been allocated */ - sqlite3TouchRegister(pParse, 8+mxIdx); + sqlite3TouchRegister(pParse, 8+cnt); sqlite3ClearTempRegCache(pParse); /* Do the b-tree integrity checks */ - sqlite3VdbeAddOp4(v, OP_IntegrityCk, 2, cnt, 1, (char*)aRoot,P4_INTARRAY); + sqlite3VdbeAddOp4(v, OP_IntegrityCk, 1, cnt, 8, (char*)aRoot,P4_INTARRAY); sqlite3VdbeChangeP5(v, (u8)i); addr = sqlite3VdbeAddOp1(v, OP_IsNull, 2); VdbeCoverage(v); sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0, @@ -139641,6 +140489,36 @@ SQLITE_PRIVATE void sqlite3Pragma( integrityCheckResultRow(v); sqlite3VdbeJumpHere(v, addr); + /* Check that the indexes all have the right number of rows */ + cnt = pObjTab ? 1 : 0; + sqlite3VdbeLoadString(v, 2, "wrong # of entries in index "); + for(x=sqliteHashFirst(pTbls); x; x=sqliteHashNext(x)){ + int iTab = 0; + Table *pTab = sqliteHashData(x); + Index *pIdx; + if( pObjTab && pObjTab!=pTab ) continue; + if( HasRowid(pTab) ){ + iTab = cnt++; + }else{ + iTab = cnt; + for(pIdx=pTab->pIndex; ALWAYS(pIdx); pIdx=pIdx->pNext){ + if( IsPrimaryKeyIndex(pIdx) ) break; + iTab++; + } + } + for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ + if( pIdx->pPartIdxWhere==0 ){ + addr = sqlite3VdbeAddOp3(v, OP_Eq, 8+cnt, 0, 8+iTab); + VdbeCoverageNeverNull(v); + sqlite3VdbeLoadString(v, 4, pIdx->zName); + sqlite3VdbeAddOp3(v, OP_Concat, 4, 2, 3); + integrityCheckResultRow(v); + sqlite3VdbeJumpHere(v, addr); + } + cnt++; + } + } + /* Make sure all the indices are constructed correctly. */ for(x=sqliteHashFirst(pTbls); x; x=sqliteHashNext(x)){ @@ -139964,21 +140842,9 @@ SQLITE_PRIVATE void sqlite3Pragma( } sqlite3VdbeAddOp2(v, OP_Next, iDataCur, loopTop); VdbeCoverage(v); sqlite3VdbeJumpHere(v, loopTop-1); - if( !isQuick ){ - sqlite3VdbeLoadString(v, 2, "wrong # of entries in index "); - for(j=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, j++){ - if( pPk==pIdx ) continue; - sqlite3VdbeAddOp2(v, OP_Count, iIdxCur+j, 3); - addr = sqlite3VdbeAddOp3(v, OP_Eq, 8+j, 0, 3); VdbeCoverage(v); - sqlite3VdbeChangeP5(v, SQLITE_NOTNULL); - sqlite3VdbeLoadString(v, 4, pIdx->zName); - sqlite3VdbeAddOp3(v, OP_Concat, 4, 2, 3); - integrityCheckResultRow(v); - sqlite3VdbeJumpHere(v, addr); - } - if( pPk ){ - sqlite3ReleaseTempRange(pParse, r2, pPk->nKeyCol); - } + if( pPk ){ + assert( !isQuick ); + sqlite3ReleaseTempRange(pParse, r2, pPk->nKeyCol); } } @@ -140276,44 +141142,63 @@ SQLITE_PRIVATE void sqlite3Pragma( ** ** The optional argument is a bitmask of optimizations to perform: ** - ** 0x0001 Debugging mode. Do not actually perform any optimizations - ** but instead return one line of text for each optimization - ** that would have been done. Off by default. + ** 0x00001 Debugging mode. Do not actually perform any optimizations + ** but instead return one line of text for each optimization + ** that would have been done. Off by default. ** - ** 0x0002 Run ANALYZE on tables that might benefit. On by default. - ** See below for additional information. + ** 0x00002 Run ANALYZE on tables that might benefit. On by default. + ** See below for additional information. ** - ** 0x0004 (Not yet implemented) Record usage and performance - ** information from the current session in the - ** database file so that it will be available to "optimize" - ** pragmas run by future database connections. + ** 0x00010 Run all ANALYZE operations using an analysis_limit that + ** is the lessor of the current analysis_limit and the + ** SQLITE_DEFAULT_OPTIMIZE_LIMIT compile-time option. + ** The default value of SQLITE_DEFAULT_OPTIMIZE_LIMIT is + ** currently (2024-02-19) set to 2000, which is such that + ** the worst case run-time for PRAGMA optimize on a 100MB + ** database will usually be less than 100 milliseconds on + ** a RaspberryPI-4 class machine. On by default. ** - ** 0x0008 (Not yet implemented) Create indexes that might have - ** been helpful to recent queries + ** 0x10000 Look at tables to see if they need to be reanalyzed + ** due to growth or shrinkage even if they have not been + ** queried during the current connection. Off by default. ** - ** The default MASK is and always shall be 0xfffe. 0xfffe means perform all - ** of the optimizations listed above except Debug Mode, including new - ** optimizations that have not yet been invented. If new optimizations are - ** ever added that should be off by default, those off-by-default - ** optimizations will have bitmasks of 0x10000 or larger. + ** The default MASK is and always shall be 0x0fffe. In the current + ** implementation, the default mask only covers the 0x00002 optimization, + ** though additional optimizations that are covered by 0x0fffe might be + ** added in the future. Optimizations that are off by default and must + ** be explicitly requested have masks of 0x10000 or greater. ** ** DETERMINATION OF WHEN TO RUN ANALYZE ** ** In the current implementation, a table is analyzed if only if all of ** the following are true: ** - ** (1) MASK bit 0x02 is set. + ** (1) MASK bit 0x00002 is set. ** - ** (2) The query planner used sqlite_stat1-style statistics for one or - ** more indexes of the table at some point during the lifetime of - ** the current connection. + ** (2) The table is an ordinary table, not a virtual table or view. ** - ** (3) One or more indexes of the table are currently unanalyzed OR - ** the number of rows in the table has increased by 25 times or more - ** since the last time ANALYZE was run. + ** (3) The table name does not begin with "sqlite_". + ** + ** (4) One or more of the following is true: + ** (4a) The 0x10000 MASK bit is set. + ** (4b) One or more indexes on the table lacks an entry + ** in the sqlite_stat1 table. + ** (4c) The query planner used sqlite_stat1-style statistics for one + ** or more indexes of the table at some point during the lifetime + ** of the current connection. + ** + ** (5) One or more of the following is true: + ** (5a) One or more indexes on the table lacks an entry + ** in the sqlite_stat1 table. (Same as 4a) + ** (5b) The number of rows in the table has increased or decreased by + ** 10-fold. In other words, the current size of the table is + ** 10 times larger than the size in sqlite_stat1 or else the + ** current size is less than 1/10th the size in sqlite_stat1. ** ** The rules for when tables are analyzed are likely to change in - ** future releases. + ** future releases. Future versions of SQLite might accept a string + ** literal argument to this pragma that contains a mnemonic description + ** of the options rather than a bitmap. */ case PragTyp_OPTIMIZE: { int iDbLast; /* Loop termination point for the schema loop */ @@ -140325,6 +141210,10 @@ SQLITE_PRIVATE void sqlite3Pragma( LogEst szThreshold; /* Size threshold above which reanalysis needed */ char *zSubSql; /* SQL statement for the OP_SqlExec opcode */ u32 opMask; /* Mask of operations to perform */ + int nLimit; /* Analysis limit to use */ + int nCheck = 0; /* Number of tables to be optimized */ + int nBtree = 0; /* Number of btrees to scan */ + int nIndex; /* Number of indexes on the current table */ if( zRight ){ opMask = (u32)sqlite3Atoi(zRight); @@ -140332,6 +141221,14 @@ SQLITE_PRIVATE void sqlite3Pragma( }else{ opMask = 0xfffe; } + if( (opMask & 0x10)==0 ){ + nLimit = 0; + }else if( db->nAnalysisLimit>0 + && db->nAnalysisLimitnTab++; for(iDbLast = zDb?iDb:db->nDb-1; iDb<=iDbLast; iDb++){ if( iDb==1 ) continue; @@ -140340,23 +141237,61 @@ SQLITE_PRIVATE void sqlite3Pragma( for(k=sqliteHashFirst(&pSchema->tblHash); k; k=sqliteHashNext(k)){ pTab = (Table*)sqliteHashData(k); - /* If table pTab has not been used in a way that would benefit from - ** having analysis statistics during the current session, then skip it. - ** This also has the effect of skipping virtual tables and views */ - if( (pTab->tabFlags & TF_StatsUsed)==0 ) continue; + /* This only works for ordinary tables */ + if( !IsOrdinaryTable(pTab) ) continue; - /* Reanalyze if the table is 25 times larger than the last analysis */ - szThreshold = pTab->nRowLogEst + 46; assert( sqlite3LogEst(25)==46 ); + /* Do not scan system tables */ + if( 0==sqlite3StrNICmp(pTab->zName, "sqlite_", 7) ) continue; + + /* Find the size of the table as last recorded in sqlite_stat1. + ** If any index is unanalyzed, then the threshold is -1 to + ** indicate a new, unanalyzed index + */ + szThreshold = pTab->nRowLogEst; + nIndex = 0; for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ + nIndex++; if( !pIdx->hasStat1 ){ - szThreshold = 0; /* Always analyze if any index lacks statistics */ - break; + szThreshold = -1; /* Always analyze if any index lacks statistics */ } } - if( szThreshold ){ - sqlite3OpenTable(pParse, iTabCur, iDb, pTab, OP_OpenRead); - sqlite3VdbeAddOp3(v, OP_IfSmaller, iTabCur, - sqlite3VdbeCurrentAddr(v)+2+(opMask&1), szThreshold); + + /* If table pTab has not been used in a way that would benefit from + ** having analysis statistics during the current session, then skip it, + ** unless the 0x10000 MASK bit is set. */ + if( (pTab->tabFlags & TF_MaybeReanalyze)!=0 ){ + /* Check for size change if stat1 has been used for a query */ + }else if( opMask & 0x10000 ){ + /* Check for size change if 0x10000 is set */ + }else if( pTab->pIndex!=0 && szThreshold<0 ){ + /* Do analysis if unanalyzed indexes exists */ + }else{ + /* Otherwise, we can skip this table */ + continue; + } + + nCheck++; + if( nCheck==2 ){ + /* If ANALYZE might be invoked two or more times, hold a write + ** transaction for efficiency */ + sqlite3BeginWriteOperation(pParse, 0, iDb); + } + nBtree += nIndex+1; + + /* Reanalyze if the table is 10 times larger or smaller than + ** the last analysis. Unconditional reanalysis if there are + ** unanalyzed indexes. */ + sqlite3OpenTable(pParse, iTabCur, iDb, pTab, OP_OpenRead); + if( szThreshold>=0 ){ + const LogEst iRange = 33; /* 10x size change */ + sqlite3VdbeAddOp4Int(v, OP_IfSizeBetween, iTabCur, + sqlite3VdbeCurrentAddr(v)+2+(opMask&1), + szThreshold>=iRange ? szThreshold-iRange : -1, + szThreshold+iRange); + VdbeCoverage(v); + }else{ + sqlite3VdbeAddOp2(v, OP_Rewind, iTabCur, + sqlite3VdbeCurrentAddr(v)+2+(opMask&1)); VdbeCoverage(v); } zSubSql = sqlite3MPrintf(db, "ANALYZE \"%w\".\"%w\"", @@ -140366,11 +141301,27 @@ SQLITE_PRIVATE void sqlite3Pragma( sqlite3VdbeAddOp4(v, OP_String8, 0, r1, 0, zSubSql, P4_DYNAMIC); sqlite3VdbeAddOp2(v, OP_ResultRow, r1, 1); }else{ - sqlite3VdbeAddOp4(v, OP_SqlExec, 0, 0, 0, zSubSql, P4_DYNAMIC); + sqlite3VdbeAddOp4(v, OP_SqlExec, nLimit ? 0x02 : 00, nLimit, 0, + zSubSql, P4_DYNAMIC); } } } sqlite3VdbeAddOp0(v, OP_Expire); + + /* In a schema with a large number of tables and indexes, scale back + ** the analysis_limit to avoid excess run-time in the worst case. + */ + if( !db->mallocFailed && nLimit>0 && nBtree>100 ){ + int iAddr, iEnd; + VdbeOp *aOp; + nLimit = 100*nLimit/nBtree; + if( nLimit<100 ) nLimit = 100; + aOp = sqlite3VdbeGetOp(v, 0); + iEnd = sqlite3VdbeCurrentAddr(v); + for(iAddr=0; iAddrnConstraint; i++, pConstraint++){ - if( pConstraint->usable==0 ) continue; - if( pConstraint->op!=SQLITE_INDEX_CONSTRAINT_EQ ) continue; if( pConstraint->iColumn < pTab->iHidden ) continue; + if( pConstraint->op!=SQLITE_INDEX_CONSTRAINT_EQ ) continue; + if( pConstraint->usable==0 ) return SQLITE_CONSTRAINT; j = pConstraint->iColumn - pTab->iHidden; assert( j < 2 ); seen[j] = i+1; @@ -140649,16 +141600,13 @@ static int pragmaVtabBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ j = seen[0]-1; pIdxInfo->aConstraintUsage[j].argvIndex = 1; pIdxInfo->aConstraintUsage[j].omit = 1; - if( seen[1]==0 ){ - pIdxInfo->estimatedCost = (double)1000; - pIdxInfo->estimatedRows = 1000; - return SQLITE_OK; - } pIdxInfo->estimatedCost = (double)20; pIdxInfo->estimatedRows = 20; - j = seen[1]-1; - pIdxInfo->aConstraintUsage[j].argvIndex = 2; - pIdxInfo->aConstraintUsage[j].omit = 1; + if( seen[1] ){ + j = seen[1]-1; + pIdxInfo->aConstraintUsage[j].argvIndex = 2; + pIdxInfo->aConstraintUsage[j].omit = 1; + } return SQLITE_OK; } @@ -140678,6 +141626,7 @@ static void pragmaVtabCursorClear(PragmaVtabCursor *pCsr){ int i; sqlite3_finalize(pCsr->pPragma); pCsr->pPragma = 0; + pCsr->iRowid = 0; for(i=0; iazArg); i++){ sqlite3_free(pCsr->azArg[i]); pCsr->azArg[i] = 0; @@ -141478,7 +142427,13 @@ SQLITE_PRIVATE void *sqlite3ParserAddCleanup( void (*xCleanup)(sqlite3*,void*), /* The cleanup routine */ void *pPtr /* Pointer to object to be cleaned up */ ){ - ParseCleanup *pCleanup = sqlite3DbMallocRaw(pParse->db, sizeof(*pCleanup)); + ParseCleanup *pCleanup; + if( sqlite3FaultSim(300) ){ + pCleanup = 0; + sqlite3OomFault(pParse->db); + }else{ + pCleanup = sqlite3DbMallocRaw(pParse->db, sizeof(*pCleanup)); + } if( pCleanup ){ pCleanup->pNext = pParse->pCleanup; pParse->pCleanup = pCleanup; @@ -143600,9 +144555,16 @@ static void generateSortTail( int addrExplain; /* Address of OP_Explain instruction */ #endif - ExplainQueryPlan2(addrExplain, (pParse, 0, - "USE TEMP B-TREE FOR %sORDER BY", pSort->nOBSat>0?"RIGHT PART OF ":"") - ); + nKey = pOrderBy->nExpr - pSort->nOBSat; + if( pSort->nOBSat==0 || nKey==1 ){ + ExplainQueryPlan2(addrExplain, (pParse, 0, + "USE TEMP B-TREE FOR %sORDER BY", pSort->nOBSat?"LAST TERM OF ":"" + )); + }else{ + ExplainQueryPlan2(addrExplain, (pParse, 0, + "USE TEMP B-TREE FOR LAST %d TERMS OF ORDER BY", nKey + )); + } sqlite3VdbeScanStatusRange(v, addrExplain,pSort->addrPush,pSort->addrPushEnd); sqlite3VdbeScanStatusCounters(v, addrExplain, addrExplain, pSort->addrPush); @@ -143640,7 +144602,6 @@ static void generateSortTail( regRow = sqlite3GetTempRange(pParse, nColumn); } } - nKey = pOrderBy->nExpr - pSort->nOBSat; if( pSort->sortFlags & SORTFLAG_UseSorter ){ int regSortOut = ++pParse->nMem; iSortTab = pParse->nTab++; @@ -144245,8 +145206,7 @@ SQLITE_PRIVATE void sqlite3SubqueryColumnTypes( NameContext sNC; assert( pSelect!=0 ); - testcase( (pSelect->selFlags & SF_Resolved)==0 ); - assert( (pSelect->selFlags & SF_Resolved)!=0 || IN_RENAME_OBJECT ); + assert( (pSelect->selFlags & SF_Resolved)!=0 ); assert( pTab->nCol==pSelect->pEList->nExpr || pParse->nErr>0 ); assert( aff==SQLITE_AFF_NONE || aff==SQLITE_AFF_BLOB ); if( db->mallocFailed || IN_RENAME_OBJECT ) return; @@ -144257,17 +145217,22 @@ SQLITE_PRIVATE void sqlite3SubqueryColumnTypes( for(i=0, pCol=pTab->aCol; inCol; i++, pCol++){ const char *zType; i64 n; + int m = 0; + Select *pS2 = pSelect; pTab->tabFlags |= (pCol->colFlags & COLFLAG_NOINSERT); p = a[i].pExpr; /* pCol->szEst = ... // Column size est for SELECT tables never used */ pCol->affinity = sqlite3ExprAffinity(p); + while( pCol->affinity<=SQLITE_AFF_NONE && pS2->pNext!=0 ){ + m |= sqlite3ExprDataType(pS2->pEList->a[i].pExpr); + pS2 = pS2->pNext; + pCol->affinity = sqlite3ExprAffinity(pS2->pEList->a[i].pExpr); + } if( pCol->affinity<=SQLITE_AFF_NONE ){ pCol->affinity = aff; } - if( pCol->affinity>=SQLITE_AFF_TEXT && pSelect->pNext ){ - int m = 0; - Select *pS2; - for(m=0, pS2=pSelect->pNext; pS2; pS2=pS2->pNext){ + if( pCol->affinity>=SQLITE_AFF_TEXT && (pS2->pNext || pS2!=pSelect) ){ + for(pS2=pS2->pNext; pS2; pS2=pS2->pNext){ m |= sqlite3ExprDataType(pS2->pEList->a[i].pExpr); } if( pCol->affinity==SQLITE_AFF_TEXT && (m&0x01)!=0 ){ @@ -144297,12 +145262,12 @@ SQLITE_PRIVATE void sqlite3SubqueryColumnTypes( } } if( zType ){ - i64 m = sqlite3Strlen30(zType); + const i64 k = sqlite3Strlen30(zType); n = sqlite3Strlen30(pCol->zCnName); - pCol->zCnName = sqlite3DbReallocOrFree(db, pCol->zCnName, n+m+2); + pCol->zCnName = sqlite3DbReallocOrFree(db, pCol->zCnName, n+k+2); pCol->colFlags &= ~(COLFLAG_HASTYPE|COLFLAG_HASCOLL); if( pCol->zCnName ){ - memcpy(&pCol->zCnName[n+1], zType, m+1); + memcpy(&pCol->zCnName[n+1], zType, k+1); pCol->colFlags |= COLFLAG_HASTYPE; } } @@ -146699,7 +147664,7 @@ static void constInsert( ){ int i; assert( pColumn->op==TK_COLUMN ); - assert( sqlite3ExprIsConstant(pValue) ); + assert( sqlite3ExprIsConstant(pConst->pParse, pValue) ); if( ExprHasProperty(pColumn, EP_FixedCol) ) return; if( sqlite3ExprAffinity(pValue)!=0 ) return; @@ -146757,10 +147722,10 @@ static void findConstInWhere(WhereConst *pConst, Expr *pExpr){ pLeft = pExpr->pLeft; assert( pRight!=0 ); assert( pLeft!=0 ); - if( pRight->op==TK_COLUMN && sqlite3ExprIsConstant(pLeft) ){ + if( pRight->op==TK_COLUMN && sqlite3ExprIsConstant(pConst->pParse, pLeft) ){ constInsert(pConst,pRight,pLeft,pExpr); } - if( pLeft->op==TK_COLUMN && sqlite3ExprIsConstant(pRight) ){ + if( pLeft->op==TK_COLUMN && sqlite3ExprIsConstant(pConst->pParse, pRight) ){ constInsert(pConst,pLeft,pRight,pExpr); } } @@ -146981,6 +147946,18 @@ static int pushDownWindowCheck(Parse *pParse, Select *pSubq, Expr *pExpr){ ** The hope is that the terms added to the inner query will make it more ** efficient. ** +** NAME AMBIGUITY +** +** This optimization is called the "WHERE-clause push-down optimization". +** +** Do not confuse this optimization with another unrelated optimization +** with a similar name: The "MySQL push-down optimization" causes WHERE +** clause terms that can be evaluated using only the index and without +** reference to the table are run first, so that if they are false, +** unnecessary table seeks are avoided. +** +** RULES +** ** Do not attempt this optimization if: ** ** (1) (** This restriction was removed on 2017-09-29. We used to @@ -147046,10 +148023,10 @@ static int pushDownWindowCheck(Parse *pParse, Select *pSubq, Expr *pExpr){ ** (9c) There is a RIGHT JOIN (or FULL JOIN) in between the ON/USING ** clause and the subquery. ** -** Without this restriction, the push-down optimization might move -** the ON/USING filter expression from the left side of a RIGHT JOIN -** over to the right side, which leads to incorrect answers. See -** also restriction (6) in sqlite3ExprIsSingleTableConstraint(). +** Without this restriction, the WHERE-clause push-down optimization +** might move the ON/USING filter expression from the left side of a +** RIGHT JOIN over to the right side, which leads to incorrect answers. +** See also restriction (6) in sqlite3ExprIsSingleTableConstraint(). ** ** (10) The inner query is not the right-hand table of a RIGHT JOIN. ** @@ -147181,7 +148158,7 @@ static int pushDownWhereTerms( } #endif - if( sqlite3ExprIsSingleTableConstraint(pWhere, pSrcList, iSrc) ){ + if( sqlite3ExprIsSingleTableConstraint(pWhere, pSrcList, iSrc, 1) ){ nChng++; pSubq->selFlags |= SF_PushDown; while( pSubq ){ @@ -148316,8 +149293,7 @@ static void selectAddSubqueryTypeInfo(Walker *pWalker, Select *p){ if( p->selFlags & SF_HasTypeInfo ) return; p->selFlags |= SF_HasTypeInfo; pParse = pWalker->pParse; - testcase( (p->selFlags & SF_Resolved)==0 ); - assert( (p->selFlags & SF_Resolved) || IN_RENAME_OBJECT ); + assert( (p->selFlags & SF_Resolved) ); pTabList = p->pSrc; for(i=0, pFrom=pTabList->a; inSrc; i++, pFrom++){ Table *pTab = pFrom->pTab; @@ -148387,6 +149363,8 @@ SQLITE_PRIVATE void sqlite3SelectPrep( */ static void printAggInfo(AggInfo *pAggInfo){ int ii; + sqlite3DebugPrintf("AggInfo %d/%p:\n", + pAggInfo->selId, pAggInfo); for(ii=0; iinColumn; ii++){ struct AggInfo_col *pCol = &pAggInfo->aCol[ii]; sqlite3DebugPrintf( @@ -149577,7 +150555,7 @@ SQLITE_PRIVATE int sqlite3Select( /* Generate code for all sub-queries in the FROM clause */ pSub = pItem->pSelect; - if( pSub==0 ) continue; + if( pSub==0 || pItem->addrFillSub!=0 ) continue; /* The code for a subquery should only be generated once. */ assert( pItem->addrFillSub==0 ); @@ -149608,7 +150586,7 @@ SQLITE_PRIVATE int sqlite3Select( #endif assert( pItem->pSelect && (pItem->pSelect->selFlags & SF_PushDown)!=0 ); }else{ - TREETRACE(0x4000,pParse,p,("Push-down not possible\n")); + TREETRACE(0x4000,pParse,p,("WHERE-lcause push-down not possible\n")); } /* Convert unused result columns of the subquery into simple NULL @@ -150489,6 +151467,12 @@ select_end: sqlite3ExprListDelete(db, pMinMaxOrderBy); #ifdef SQLITE_DEBUG if( pAggInfo && !db->mallocFailed ){ +#if TREETRACE_ENABLED + if( sqlite3TreeTrace & 0x20 ){ + TREETRACE(0x20,pParse,p,("Finished with AggInfo\n")); + printAggInfo(pAggInfo); + } +#endif for(i=0; inColumn; i++){ Expr *pExpr = pAggInfo->aCol[i].pCExpr; if( pExpr==0 ) continue; @@ -151670,6 +152654,72 @@ static ExprList *sqlite3ExpandReturning( return pNew; } +/* If the Expr node is a subquery or an EXISTS operator or an IN operator that +** uses a subquery, and if the subquery is SF_Correlated, then mark the +** expression as EP_VarSelect. +*/ +static int sqlite3ReturningSubqueryVarSelect(Walker *NotUsed, Expr *pExpr){ + UNUSED_PARAMETER(NotUsed); + if( ExprUseXSelect(pExpr) + && (pExpr->x.pSelect->selFlags & SF_Correlated)!=0 + ){ + testcase( ExprHasProperty(pExpr, EP_VarSelect) ); + ExprSetProperty(pExpr, EP_VarSelect); + } + return WRC_Continue; +} + + +/* +** If the SELECT references the table pWalker->u.pTab, then do two things: +** +** (1) Mark the SELECT as as SF_Correlated. +** (2) Set pWalker->eCode to non-zero so that the caller will know +** that (1) has happened. +*/ +static int sqlite3ReturningSubqueryCorrelated(Walker *pWalker, Select *pSelect){ + int i; + SrcList *pSrc; + assert( pSelect!=0 ); + pSrc = pSelect->pSrc; + assert( pSrc!=0 ); + for(i=0; inSrc; i++){ + if( pSrc->a[i].pTab==pWalker->u.pTab ){ + testcase( pSelect->selFlags & SF_Correlated ); + pSelect->selFlags |= SF_Correlated; + pWalker->eCode = 1; + break; + } + } + return WRC_Continue; +} + +/* +** Scan the expression list that is the argument to RETURNING looking +** for subqueries that depend on the table which is being modified in the +** statement that is hosting the RETURNING clause (pTab). Mark all such +** subqueries as SF_Correlated. If the subqueries are part of an +** expression, mark the expression as EP_VarSelect. +** +** https://sqlite.org/forum/forumpost/2c83569ce8945d39 +*/ +static void sqlite3ProcessReturningSubqueries( + ExprList *pEList, + Table *pTab +){ + Walker w; + memset(&w, 0, sizeof(w)); + w.xExprCallback = sqlite3ExprWalkNoop; + w.xSelectCallback = sqlite3ReturningSubqueryCorrelated; + w.u.pTab = pTab; + sqlite3WalkExprList(&w, pEList); + if( w.eCode ){ + w.xExprCallback = sqlite3ReturningSubqueryVarSelect; + w.xSelectCallback = sqlite3SelectWalkNoop; + sqlite3WalkExprList(&w, pEList); + } +} + /* ** Generate code for the RETURNING trigger. Unlike other triggers ** that invoke a subprogram in the bytecode, the code for RETURNING @@ -151706,6 +152756,7 @@ static void codeReturningTrigger( sSelect.pSrc = &sFrom; sFrom.nSrc = 1; sFrom.a[0].pTab = pTab; + sFrom.a[0].zName = pTab->zName; /* tag-20240424-1 */ sFrom.a[0].iCursor = -1; sqlite3SelectPrep(pParse, &sSelect, 0); if( pParse->nErr==0 ){ @@ -151732,6 +152783,7 @@ static void codeReturningTrigger( int i; int nCol = pNew->nExpr; int reg = pParse->nMem+1; + sqlite3ProcessReturningSubqueries(pNew, pTab); pParse->nMem += nCol+2; pReturning->iRetReg = reg; for(i=0; ipVtabCtx = &sCtx; pTab->nTabRef++; rc = xConstruct(db, pMod->pAux, nArg, azArg, &pVTable->pVtab, &zErr); + assert( pTab!=0 ); + assert( pTab->nTabRef>1 || rc!=SQLITE_OK ); sqlite3DeleteTable(db, pTab); db->pVtabCtx = sCtx.pPrior; if( rc==SQLITE_NOMEM ) sqlite3OomFault(db); @@ -154964,7 +156018,7 @@ static int vtabCallConstructor( pVTable->nRef = 1; if( sCtx.bDeclared==0 ){ const char *zFormat = "vtable constructor did not declare schema: %s"; - *pzErr = sqlite3MPrintf(db, zFormat, pTab->zName); + *pzErr = sqlite3MPrintf(db, zFormat, zModuleName); sqlite3VtabUnlock(pVTable); rc = SQLITE_ERROR; }else{ @@ -155142,12 +156196,30 @@ SQLITE_API int sqlite3_declare_vtab(sqlite3 *db, const char *zCreateTable){ Table *pTab; Parse sParse; int initBusy; + int i; + const unsigned char *z; + static const u8 aKeyword[] = { TK_CREATE, TK_TABLE, 0 }; #ifdef SQLITE_ENABLE_API_ARMOR if( !sqlite3SafetyCheckOk(db) || zCreateTable==0 ){ return SQLITE_MISUSE_BKPT; } #endif + + /* Verify that the first two keywords in the CREATE TABLE statement + ** really are "CREATE" and "TABLE". If this is not the case, then + ** sqlite3_declare_vtab() is being misused. + */ + z = (const unsigned char*)zCreateTable; + for(i=0; aKeyword[i]; i++){ + int tokenType = 0; + do{ z += sqlite3GetToken(z, &tokenType); }while( tokenType==TK_SPACE ); + if( tokenType!=aKeyword[i] ){ + sqlite3ErrorWithMsg(db, SQLITE_ERROR, "syntax error"); + return SQLITE_ERROR; + } + } + sqlite3_mutex_enter(db->mutex); pCtx = db->pVtabCtx; if( !pCtx || pCtx->bDeclared ){ @@ -155155,6 +156227,7 @@ SQLITE_API int sqlite3_declare_vtab(sqlite3 *db, const char *zCreateTable){ sqlite3_mutex_leave(db->mutex); return SQLITE_MISUSE_BKPT; } + pTab = pCtx->pTab; assert( IsVirtual(pTab) ); @@ -155168,11 +156241,10 @@ SQLITE_API int sqlite3_declare_vtab(sqlite3 *db, const char *zCreateTable){ initBusy = db->init.busy; db->init.busy = 0; sParse.nQueryLoop = 1; - if( SQLITE_OK==sqlite3RunParser(&sParse, zCreateTable) - && ALWAYS(sParse.pNewTable!=0) - && ALWAYS(!db->mallocFailed) - && IsOrdinaryTable(sParse.pNewTable) - ){ + if( SQLITE_OK==sqlite3RunParser(&sParse, zCreateTable) ){ + assert( sParse.pNewTable!=0 ); + assert( !db->mallocFailed ); + assert( IsOrdinaryTable(sParse.pNewTable) ); assert( sParse.zErrMsg==0 ); if( !pTab->aCol ){ Table *pNew = sParse.pNewTable; @@ -157667,6 +158739,27 @@ static SQLITE_NOINLINE void filterPullDown( } } +/* +** Loop pLoop is a WHERE_INDEXED level that uses at least one IN(...) +** operator. Return true if level pLoop is guaranteed to visit only one +** row for each key generated for the index. +*/ +static int whereLoopIsOneRow(WhereLoop *pLoop){ + if( pLoop->u.btree.pIndex->onError + && pLoop->nSkip==0 + && pLoop->u.btree.nEq==pLoop->u.btree.pIndex->nKeyCol + ){ + int ii; + for(ii=0; iiu.btree.nEq; ii++){ + if( pLoop->aLTerm[ii]->eOperator & (WO_IS|WO_ISNULL) ){ + return 0; + } + } + return 1; + } + return 0; +} + /* ** Generate code for the start of the iLevel-th loop in the WHERE clause ** implementation described by pWInfo. @@ -157745,7 +158838,7 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( if( pLevel->iFrom>0 && (pTabItem[0].fg.jointype & JT_LEFT)!=0 ){ pLevel->iLeftJoin = ++pParse->nMem; sqlite3VdbeAddOp2(v, OP_Integer, 0, pLevel->iLeftJoin); - VdbeComment((v, "init LEFT JOIN no-match flag")); + VdbeComment((v, "init LEFT JOIN match flag")); } /* Compute a safe address to jump to if we discover that the table for @@ -158414,7 +159507,9 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( } /* Record the instruction used to terminate the loop. */ - if( pLoop->wsFlags & WHERE_ONEROW ){ + if( (pLoop->wsFlags & WHERE_ONEROW) + || (pLevel->u.in.nIn && regBignull==0 && whereLoopIsOneRow(pLoop)) + ){ pLevel->op = OP_Noop; }else if( bRev ){ pLevel->op = OP_Prev; @@ -158804,6 +159899,12 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( ** iLoop==3: Code all remaining expressions. ** ** An effort is made to skip unnecessary iterations of the loop. + ** + ** This optimization of causing simple query restrictions to occur before + ** more complex one is call the "push-down" optimization in MySQL. Here + ** in SQLite, the name is "MySQL push-down", since there is also another + ** totally unrelated optimization called "WHERE-clause push-down". + ** Sometimes the qualifier is omitted, resulting in an ambiguity, so beware. */ iLoop = (pIdx ? 1 : 2); do{ @@ -159054,7 +160155,16 @@ SQLITE_PRIVATE SQLITE_NOINLINE void sqlite3WhereRightJoinLoop( pRJ->regReturn); for(k=0; ka[k].pWLoop->iTab == pWInfo->a[k].iFrom ); + pRight = &pWInfo->pTabList->a[pWInfo->a[k].iFrom]; mAll |= pWInfo->a[k].pWLoop->maskSelf; + if( pRight->fg.viaCoroutine ){ + sqlite3VdbeAddOp3( + v, OP_Null, 0, pRight->regResult, + pRight->regResult + pRight->pSelect->pEList->nExpr-1 + ); + } sqlite3VdbeAddOp1(v, OP_NullRow, pWInfo->a[k].iTabCur); iIdxCur = pWInfo->a[k].iIdxCur; if( iIdxCur ){ @@ -160111,7 +161221,7 @@ static SQLITE_NOINLINE int exprMightBeIndexed2( if( pIdx->aiColumn[i]!=XN_EXPR ) continue; assert( pIdx->bHasExpr ); if( sqlite3ExprCompareSkip(pExpr,pIdx->aColExpr->a[i].pExpr,iCur)==0 - && pExpr->op!=TK_STRING + && !sqlite3ExprIsConstant(0,pIdx->aColExpr->a[i].pExpr) ){ aiCurCol[0] = iCur; aiCurCol[1] = XN_EXPR; @@ -160760,6 +161870,7 @@ SQLITE_PRIVATE void SQLITE_NOINLINE sqlite3WhereAddLimit(WhereClause *pWC, Selec continue; } if( pWC->a[ii].leftCursor!=iCsr ) return; + if( pWC->a[ii].prereqRight!=0 ) return; } /* Check condition (5). Return early if it is not met. */ @@ -160774,12 +161885,14 @@ SQLITE_PRIVATE void SQLITE_NOINLINE sqlite3WhereAddLimit(WhereClause *pWC, Selec /* All conditions are met. Add the terms to the where-clause object. */ assert( p->pLimit->op==TK_LIMIT ); - whereAddLimitExpr(pWC, p->iLimit, p->pLimit->pLeft, - iCsr, SQLITE_INDEX_CONSTRAINT_LIMIT); - if( p->iOffset>0 ){ + if( p->iOffset!=0 && (p->selFlags & SF_Compound)==0 ){ whereAddLimitExpr(pWC, p->iOffset, p->pLimit->pRight, iCsr, SQLITE_INDEX_CONSTRAINT_OFFSET); } + if( p->iOffset==0 || (p->selFlags & SF_Compound)==0 ){ + whereAddLimitExpr(pWC, p->iLimit, p->pLimit->pLeft, + iCsr, SQLITE_INDEX_CONSTRAINT_LIMIT); + } } } @@ -161297,6 +162410,42 @@ static Expr *whereRightSubexprIsColumn(Expr *p){ return 0; } +/* +** Term pTerm is guaranteed to be a WO_IN term. It may be a component term +** of a vector IN expression of the form "(x, y, ...) IN (SELECT ...)". +** This function checks to see if the term is compatible with an index +** column with affinity idxaff (one of the SQLITE_AFF_XYZ values). If so, +** it returns a pointer to the name of the collation sequence (e.g. "BINARY" +** or "NOCASE") used by the comparison in pTerm. If it is not compatible +** with affinity idxaff, NULL is returned. +*/ +static SQLITE_NOINLINE const char *indexInAffinityOk( + Parse *pParse, + WhereTerm *pTerm, + u8 idxaff +){ + Expr *pX = pTerm->pExpr; + Expr inexpr; + + assert( pTerm->eOperator & WO_IN ); + + if( sqlite3ExprIsVector(pX->pLeft) ){ + int iField = pTerm->u.x.iField - 1; + inexpr.flags = 0; + inexpr.op = TK_EQ; + inexpr.pLeft = pX->pLeft->x.pList->a[iField].pExpr; + assert( ExprUseXSelect(pX) ); + inexpr.pRight = pX->x.pSelect->pEList->a[iField].pExpr; + pX = &inexpr; + } + + if( sqlite3IndexAffinityOk(pX, idxaff) ){ + CollSeq *pRet = sqlite3ExprCompareCollSeq(pParse, pX); + return pRet ? pRet->zName : sqlite3StrBINARY; + } + return 0; +} + /* ** Advance to the next WhereTerm that matches according to the criteria ** established when the pScan object was initialized by whereScanInit(). @@ -161347,16 +162496,24 @@ static WhereTerm *whereScanNext(WhereScan *pScan){ if( (pTerm->eOperator & pScan->opMask)!=0 ){ /* Verify the affinity and collating sequence match */ if( pScan->zCollName && (pTerm->eOperator & WO_ISNULL)==0 ){ - CollSeq *pColl; + const char *zCollName; Parse *pParse = pWC->pWInfo->pParse; pX = pTerm->pExpr; - if( !sqlite3IndexAffinityOk(pX, pScan->idxaff) ){ - continue; + + if( (pTerm->eOperator & WO_IN) ){ + zCollName = indexInAffinityOk(pParse, pTerm, pScan->idxaff); + if( !zCollName ) continue; + }else{ + CollSeq *pColl; + if( !sqlite3IndexAffinityOk(pX, pScan->idxaff) ){ + continue; + } + assert(pX->pLeft); + pColl = sqlite3ExprCompareCollSeq(pParse, pX); + zCollName = pColl ? pColl->zName : sqlite3StrBINARY; } - assert(pX->pLeft); - pColl = sqlite3ExprCompareCollSeq(pParse, pX); - if( pColl==0 ) pColl = pParse->db->pDfltColl; - if( sqlite3StrICmp(pColl->zName, pScan->zCollName) ){ + + if( sqlite3StrICmp(zCollName, pScan->zCollName) ){ continue; } } @@ -161708,9 +162865,13 @@ static void translateColumnToCopy( ** are no-ops. */ #if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(WHERETRACE_ENABLED) -static void whereTraceIndexInfoInputs(sqlite3_index_info *p){ +static void whereTraceIndexInfoInputs( + sqlite3_index_info *p, /* The IndexInfo object */ + Table *pTab /* The TABLE that is the virtual table */ +){ int i; if( (sqlite3WhereTrace & 0x10)==0 ) return; + sqlite3DebugPrintf("sqlite3_index_info inputs for %s:\n", pTab->zName); for(i=0; inConstraint; i++){ sqlite3DebugPrintf( " constraint[%d]: col=%d termid=%d op=%d usabled=%d collseq=%s\n", @@ -161728,9 +162889,13 @@ static void whereTraceIndexInfoInputs(sqlite3_index_info *p){ p->aOrderBy[i].desc); } } -static void whereTraceIndexInfoOutputs(sqlite3_index_info *p){ +static void whereTraceIndexInfoOutputs( + sqlite3_index_info *p, /* The IndexInfo object */ + Table *pTab /* The TABLE that is the virtual table */ +){ int i; if( (sqlite3WhereTrace & 0x10)==0 ) return; + sqlite3DebugPrintf("sqlite3_index_info outputs for %s:\n", pTab->zName); for(i=0; inConstraint; i++){ sqlite3DebugPrintf(" usage[%d]: argvIdx=%d omit=%d\n", i, @@ -161744,8 +162909,8 @@ static void whereTraceIndexInfoOutputs(sqlite3_index_info *p){ sqlite3DebugPrintf(" estimatedRows=%lld\n", p->estimatedRows); } #else -#define whereTraceIndexInfoInputs(A) -#define whereTraceIndexInfoOutputs(A) +#define whereTraceIndexInfoInputs(A,B) +#define whereTraceIndexInfoOutputs(A,B) #endif /* @@ -161929,7 +163094,7 @@ static SQLITE_NOINLINE void constructAutomaticIndex( ** WHERE clause (or the ON clause of a LEFT join) that constrain which ** rows of the target table (pSrc) that can be used. */ if( (pTerm->wtFlags & TERM_VIRTUAL)==0 - && sqlite3ExprIsSingleTableConstraint(pExpr, pTabList, pLevel->iFrom) + && sqlite3ExprIsSingleTableConstraint(pExpr, pTabList, pLevel->iFrom, 0) ){ pPartial = sqlite3ExprAnd(pParse, pPartial, sqlite3ExprDup(pParse->db, pExpr, 0)); @@ -161971,7 +163136,7 @@ static SQLITE_NOINLINE void constructAutomaticIndex( ** if they go out of sync. */ if( IsView(pTable) ){ - extraCols = ALLBITS; + extraCols = ALLBITS & ~idxCols; }else{ extraCols = pSrc->colUsed & (~idxCols | MASKBIT(BMS-1)); } @@ -162198,7 +163363,7 @@ static SQLITE_NOINLINE void sqlite3ConstructBloomFilter( for(pTerm=pWInfo->sWC.a; pTermpExpr; if( (pTerm->wtFlags & TERM_VIRTUAL)==0 - && sqlite3ExprIsSingleTableConstraint(pExpr, pTabList, iSrc) + && sqlite3ExprIsSingleTableConstraint(pExpr, pTabList, iSrc, 0) ){ sqlite3ExprIfFalse(pParse, pTerm->pExpr, addrCont, SQLITE_JUMPIFNULL); } @@ -162324,7 +163489,7 @@ static sqlite3_index_info *allocateIndexInfo( Expr *pE2; /* Skip over constant terms in the ORDER BY clause */ - if( sqlite3ExprIsConstant(pExpr) ){ + if( sqlite3ExprIsConstant(0, pExpr) ){ continue; } @@ -162359,7 +163524,7 @@ static sqlite3_index_info *allocateIndexInfo( } if( i==n ){ nOrderBy = n; - if( (pWInfo->wctrlFlags & WHERE_DISTINCTBY) ){ + if( (pWInfo->wctrlFlags & WHERE_DISTINCTBY) && !pSrc->fg.rowidUsed ){ eDistinct = 2 + ((pWInfo->wctrlFlags & WHERE_SORTBYGROUP)!=0); }else if( pWInfo->wctrlFlags & WHERE_GROUPBY ){ eDistinct = 1; @@ -162436,7 +163601,7 @@ static sqlite3_index_info *allocateIndexInfo( pIdxInfo->nConstraint = j; for(i=j=0; ia[i].pExpr; - if( sqlite3ExprIsConstant(pExpr) ) continue; + if( sqlite3ExprIsConstant(0, pExpr) ) continue; assert( pExpr->op==TK_COLUMN || (pExpr->op==TK_COLLATE && pExpr->pLeft->op==TK_COLUMN && pExpr->iColumn==pExpr->pLeft->iColumn) ); @@ -162488,11 +163653,11 @@ static int vtabBestIndex(Parse *pParse, Table *pTab, sqlite3_index_info *p){ sqlite3_vtab *pVtab = sqlite3GetVTable(pParse->db, pTab)->pVtab; int rc; - whereTraceIndexInfoInputs(p); + whereTraceIndexInfoInputs(p, pTab); pParse->db->nSchemaLock++; rc = pVtab->pModule->xBestIndex(pVtab, p); pParse->db->nSchemaLock--; - whereTraceIndexInfoOutputs(p); + whereTraceIndexInfoOutputs(p, pTab); if( rc!=SQLITE_OK && rc!=SQLITE_CONSTRAINT ){ if( rc==SQLITE_NOMEM ){ @@ -163970,7 +165135,9 @@ static int whereLoopAddBtreeIndex( } if( pProbe->bUnordered || pProbe->bLowQual ){ if( pProbe->bUnordered ) opMask &= ~(WO_GT|WO_GE|WO_LT|WO_LE); - if( pProbe->bLowQual ) opMask &= ~(WO_EQ|WO_IN|WO_IS); + if( pProbe->bLowQual && pSrc->fg.isIndexedBy==0 ){ + opMask &= ~(WO_EQ|WO_IN|WO_IS); + } } assert( pNew->u.btree.nEqnColumn ); @@ -164237,10 +165404,13 @@ static int whereLoopAddBtreeIndex( } } - /* Set rCostIdx to the cost of visiting selected rows in index. Add - ** it to pNew->rRun, which is currently set to the cost of the index - ** seek only. Then, if this is a non-covering index, add the cost of - ** visiting the rows in the main table. */ + /* Set rCostIdx to the estimated cost of visiting selected rows in the + ** index. The estimate is the sum of two values: + ** 1. The cost of doing one search-by-key to find the first matching + ** entry + ** 2. Stepping forward in the index pNew->nOut times to find all + ** additional matching entries. + */ assert( pSrc->pTab->szTabRow>0 ); if( pProbe->idxType==SQLITE_IDXTYPE_IPK ){ /* The pProbe->szIdxRow is low for an IPK table since the interior @@ -164251,7 +165421,15 @@ static int whereLoopAddBtreeIndex( }else{ rCostIdx = pNew->nOut + 1 + (15*pProbe->szIdxRow)/pSrc->pTab->szTabRow; } - pNew->rRun = sqlite3LogEstAdd(rLogSize, rCostIdx); + rCostIdx = sqlite3LogEstAdd(rLogSize, rCostIdx); + + /* Estimate the cost of running the loop. If all data is coming + ** from the index, then this is just the cost of doing the index + ** lookup and scan. But if some data is coming out of the main table, + ** we also have to add in the cost of doing pNew->nOut searches to + ** locate the row in the main table that corresponds to the index entry. + */ + pNew->rRun = rCostIdx; if( (pNew->wsFlags & (WHERE_IDX_ONLY|WHERE_IPK|WHERE_EXPRIDX))==0 ){ pNew->rRun = sqlite3LogEstAdd(pNew->rRun, pNew->nOut + 16); } @@ -164357,7 +165535,9 @@ static int indexMightHelpWithOrderBy( for(ii=0; iinExpr; ii++){ Expr *pExpr = sqlite3ExprSkipCollateAndLikely(pOB->a[ii].pExpr); if( NEVER(pExpr==0) ) continue; - if( pExpr->op==TK_COLUMN && pExpr->iTable==iCursor ){ + if( (pExpr->op==TK_COLUMN || pExpr->op==TK_AGG_COLUMN) + && pExpr->iTable==iCursor + ){ if( pExpr->iColumn<0 ) return 1; for(jj=0; jjnKeyCol; jj++){ if( pExpr->iColumn==pIndex->aiColumn[jj] ) return 1; @@ -164614,7 +165794,7 @@ static void wherePartIdxExpr( u8 aff; if( pLeft->op!=TK_COLUMN ) return; - if( !sqlite3ExprIsConstant(pRight) ) return; + if( !sqlite3ExprIsConstant(0, pRight) ) return; if( !sqlite3IsBinary(sqlite3ExprCompareCollSeq(pParse, pPart)) ) return; if( pLeft->iColumn<0 ) return; aff = pIdx->pTable->aCol[pLeft->iColumn].affinity; @@ -164963,7 +166143,7 @@ static int whereLoopAddBtree( ** unique index is used (making the index functionally non-unique) ** then the sqlite_stat1 data becomes important for scoring the ** plan */ - pTab->tabFlags |= TF_StatsUsed; + pTab->tabFlags |= TF_MaybeReanalyze; } #ifdef SQLITE_ENABLE_STAT4 sqlite3Stat4ProbeFree(pBuilder->pRec); @@ -164985,6 +166165,21 @@ static int isLimitTerm(WhereTerm *pTerm){ && pTerm->eMatchOp<=SQLITE_INDEX_CONSTRAINT_OFFSET; } +/* +** Return true if the first nCons constraints in the pUsage array are +** marked as in-use (have argvIndex>0). False otherwise. +*/ +static int allConstraintsUsed( + struct sqlite3_index_constraint_usage *aUsage, + int nCons +){ + int ii; + for(ii=0; iipNew->iTab. This @@ -165125,13 +166320,20 @@ static int whereLoopAddVirtualOne( *pbIn = 1; assert( (mExclude & WO_IN)==0 ); } + /* Unless pbRetryLimit is non-NULL, there should be no LIMIT/OFFSET + ** terms. And if there are any, they should follow all other terms. */ assert( pbRetryLimit || !isLimitTerm(pTerm) ); - if( isLimitTerm(pTerm) && *pbIn ){ + assert( !isLimitTerm(pTerm) || i>=nConstraint-2 ); + assert( !isLimitTerm(pTerm) || i==nConstraint-1 || isLimitTerm(pTerm+1) ); + + if( isLimitTerm(pTerm) && (*pbIn || !allConstraintsUsed(pUsage, i)) ){ /* If there is an IN(...) term handled as an == (separate call to ** xFilter for each value on the RHS of the IN) and a LIMIT or - ** OFFSET term handled as well, the plan is unusable. Set output - ** variable *pbRetryLimit to true to tell the caller to retry with - ** LIMIT and OFFSET disabled. */ + ** OFFSET term handled as well, the plan is unusable. Similarly, + ** if there is a LIMIT/OFFSET and there are other unused terms, + ** the plan cannot be used. In these cases set variable *pbRetryLimit + ** to true to tell the caller to retry with LIMIT and OFFSET + ** disabled. */ if( pIdxInfo->needToFreeIdxStr ){ sqlite3_free(pIdxInfo->idxStr); pIdxInfo->idxStr = 0; @@ -165988,7 +167190,7 @@ static i8 wherePathSatisfiesOrderBy( if( MASKBIT(i) & obSat ) continue; p = pOrderBy->a[i].pExpr; mTerm = sqlite3WhereExprUsage(&pWInfo->sMaskSet,p); - if( mTerm==0 && !sqlite3ExprIsConstant(p) ) continue; + if( mTerm==0 && !sqlite3ExprIsConstant(0,p) ) continue; if( (mTerm&~orderDistinctMask)==0 ){ obSat |= MASKBIT(i); } @@ -166457,10 +167659,9 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){ if( pFrom->isOrdered==pWInfo->pOrderBy->nExpr ){ pWInfo->eDistinct = WHERE_DISTINCT_ORDERED; } - if( pWInfo->pSelect->pOrderBy - && pWInfo->nOBSat > pWInfo->pSelect->pOrderBy->nExpr ){ - pWInfo->nOBSat = pWInfo->pSelect->pOrderBy->nExpr; - } + /* vvv--- See check-in [12ad822d9b827777] on 2023-03-16 ---vvv */ + assert( pWInfo->pSelect->pOrderBy==0 + || pWInfo->nOBSat <= pWInfo->pSelect->pOrderBy->nExpr ); }else{ pWInfo->revMask = pFrom->revLoop; if( pWInfo->nOBSat<=0 ){ @@ -166503,7 +167704,6 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){ } } - pWInfo->nRowOut = pFrom->nRow; /* Free temporary memory and return success */ @@ -166511,6 +167711,83 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){ return SQLITE_OK; } +/* +** This routine implements a heuristic designed to improve query planning. +** This routine is called in between the first and second call to +** wherePathSolver(). Hence the name "Interstage" "Heuristic". +** +** The first call to wherePathSolver() (hereafter just "solver()") computes +** the best path without regard to the order of the outputs. The second call +** to the solver() builds upon the first call to try to find an alternative +** path that satisfies the ORDER BY clause. +** +** This routine looks at the results of the first solver() run, and for +** every FROM clause term in the resulting query plan that uses an equality +** constraint against an index, disable other WhereLoops for that same +** FROM clause term that would try to do a full-table scan. This prevents +** an index search from being converted into a full-table scan in order to +** satisfy an ORDER BY clause, since even though we might get slightly better +** performance using the full-scan without sorting if the output size +** estimates are very precise, we might also get severe performance +** degradation using the full-scan if the output size estimate is too large. +** It is better to err on the side of caution. +** +** Except, if the first solver() call generated a full-table scan in an outer +** loop then stop this analysis at the first full-scan, since the second +** solver() run might try to swap that full-scan for another in order to +** get the output into the correct order. In other words, we allow a +** rewrite like this: +** +** First Solver() Second Solver() +** |-- SCAN t1 |-- SCAN t2 +** |-- SEARCH t2 `-- SEARCH t1 +** `-- SORT USING B-TREE +** +** The purpose of this routine is to disallow rewrites such as: +** +** First Solver() Second Solver() +** |-- SEARCH t1 |-- SCAN t2 <--- bad! +** |-- SEARCH t2 `-- SEARCH t1 +** `-- SORT USING B-TREE +** +** See test cases in test/whereN.test for the real-world query that +** originally provoked this heuristic. +*/ +static SQLITE_NOINLINE void whereInterstageHeuristic(WhereInfo *pWInfo){ + int i; +#ifdef WHERETRACE_ENABLED + int once = 0; +#endif + for(i=0; inLevel; i++){ + WhereLoop *p = pWInfo->a[i].pWLoop; + if( p==0 ) break; + if( (p->wsFlags & WHERE_VIRTUALTABLE)!=0 ) continue; + if( (p->wsFlags & (WHERE_COLUMN_EQ|WHERE_COLUMN_NULL|WHERE_COLUMN_IN))!=0 ){ + u8 iTab = p->iTab; + WhereLoop *pLoop; + for(pLoop=pWInfo->pLoops; pLoop; pLoop=pLoop->pNextLoop){ + if( pLoop->iTab!=iTab ) continue; + if( (pLoop->wsFlags & (WHERE_CONSTRAINT|WHERE_AUTO_INDEX))!=0 ){ + /* Auto-index and index-constrained loops allowed to remain */ + continue; + } +#ifdef WHERETRACE_ENABLED + if( sqlite3WhereTrace & 0x80 ){ + if( once==0 ){ + sqlite3DebugPrintf("Loops disabled by interstage heuristic:\n"); + once = 1; + } + sqlite3WhereLoopPrint(pLoop, &pWInfo->sWC); + } +#endif /* WHERETRACE_ENABLED */ + pLoop->prereq = ALLBITS; /* Prevent 2nd solver() from using this one */ + } + }else{ + break; + } + } +} + /* ** Most queries use only a single table (they are not joins) and have ** simple == constraints against indexed fields. This routine attempts @@ -166799,7 +168076,7 @@ static SQLITE_NOINLINE void whereCheckIfBloomFilterIsUseful( SrcItem *pItem = &pWInfo->pTabList->a[pLoop->iTab]; Table *pTab = pItem->pTab; if( (pTab->tabFlags & TF_HasStat1)==0 ) break; - pTab->tabFlags |= TF_StatsUsed; + pTab->tabFlags |= TF_MaybeReanalyze; if( i>=1 && (pLoop->wsFlags & reqFlags)==reqFlags /* vvvvvv--- Always the case if WHERE_COLUMN_EQ is defined */ @@ -166820,6 +168097,58 @@ static SQLITE_NOINLINE void whereCheckIfBloomFilterIsUseful( } } +/* +** Expression Node callback for sqlite3ExprCanReturnSubtype(). +** +** Only a function call is able to return a subtype. So if the node +** is not a function call, return WRC_Prune immediately. +** +** A function call is able to return a subtype if it has the +** SQLITE_RESULT_SUBTYPE property. +** +** Assume that every function is able to pass-through a subtype from +** one of its argument (using sqlite3_result_value()). Most functions +** are not this way, but we don't have a mechanism to distinguish those +** that are from those that are not, so assume they all work this way. +** That means that if one of its arguments is another function and that +** other function is able to return a subtype, then this function is +** able to return a subtype. +*/ +static int exprNodeCanReturnSubtype(Walker *pWalker, Expr *pExpr){ + int n; + FuncDef *pDef; + sqlite3 *db; + if( pExpr->op!=TK_FUNCTION ){ + return WRC_Prune; + } + assert( ExprUseXList(pExpr) ); + db = pWalker->pParse->db; + n = pExpr->x.pList ? pExpr->x.pList->nExpr : 0; + pDef = sqlite3FindFunction(db, pExpr->u.zToken, n, ENC(db), 0); + if( pDef==0 || (pDef->funcFlags & SQLITE_RESULT_SUBTYPE)!=0 ){ + pWalker->eCode = 1; + return WRC_Prune; + } + return WRC_Continue; +} + +/* +** Return TRUE if expression pExpr is able to return a subtype. +** +** A TRUE return does not guarantee that a subtype will be returned. +** It only indicates that a subtype return is possible. False positives +** are acceptable as they only disable an optimization. False negatives, +** on the other hand, can lead to incorrect answers. +*/ +static int sqlite3ExprCanReturnSubtype(Parse *pParse, Expr *pExpr){ + Walker w; + memset(&w, 0, sizeof(w)); + w.pParse = pParse; + w.xExprCallback = exprNodeCanReturnSubtype; + sqlite3WalkExpr(&w, pExpr); + return w.eCode; +} + /* ** The index pIdx is used by a query and contains one or more expressions. ** In other words pIdx is an index on an expression. iIdxCur is the cursor @@ -166852,20 +168181,12 @@ static SQLITE_NOINLINE void whereAddIndexedExpr( }else{ continue; } - if( sqlite3ExprIsConstant(pExpr) ) continue; - if( pExpr->op==TK_FUNCTION ){ + if( sqlite3ExprIsConstant(0,pExpr) ) continue; + if( pExpr->op==TK_FUNCTION && sqlite3ExprCanReturnSubtype(pParse,pExpr) ){ /* Functions that might set a subtype should not be replaced by the ** value taken from an expression index since the index omits the ** subtype. https://sqlite.org/forum/forumpost/68d284c86b082c3e */ - int n; - FuncDef *pDef; - sqlite3 *db = pParse->db; - assert( ExprUseXList(pExpr) ); - n = pExpr->x.pList ? pExpr->x.pList->nExpr : 0; - pDef = sqlite3FindFunction(db, pExpr->u.zToken, n, ENC(db), 0); - if( pDef==0 || (pDef->funcFlags & SQLITE_RESULT_SUBTYPE)!=0 ){ - continue; - } + continue; } p = sqlite3DbMallocRaw(pParse->db, sizeof(IndexedExpr)); if( p==0 ) break; @@ -167130,7 +168451,11 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin( ){ pWInfo->eDistinct = WHERE_DISTINCT_UNIQUE; } - ExplainQueryPlan((pParse, 0, "SCAN CONSTANT ROW")); + if( ALWAYS(pWInfo->pSelect) + && (pWInfo->pSelect->selFlags & SF_MultiValue)==0 + ){ + ExplainQueryPlan((pParse, 0, "SCAN CONSTANT ROW")); + } }else{ /* Assign a bit from the bitmask to every term in the FROM clause. ** @@ -167283,6 +168608,7 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin( wherePathSolver(pWInfo, 0); if( db->mallocFailed ) goto whereBeginError; if( pWInfo->pOrderBy ){ + whereInterstageHeuristic(pWInfo); wherePathSolver(pWInfo, pWInfo->nRowOut+1); if( db->mallocFailed ) goto whereBeginError; } @@ -167832,7 +169158,15 @@ SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo *pWInfo){ addr = sqlite3VdbeAddOp1(v, OP_IfPos, pLevel->iLeftJoin); VdbeCoverage(v); assert( (ws & WHERE_IDX_ONLY)==0 || (ws & WHERE_INDEXED)!=0 ); if( (ws & WHERE_IDX_ONLY)==0 ){ - assert( pLevel->iTabCur==pTabList->a[pLevel->iFrom].iCursor ); + SrcItem *pSrc = &pTabList->a[pLevel->iFrom]; + assert( pLevel->iTabCur==pSrc->iCursor ); + if( pSrc->fg.viaCoroutine ){ + int m, n; + n = pSrc->regResult; + assert( pSrc->pTab!=0 ); + m = pSrc->pTab->nCol; + sqlite3VdbeAddOp3(v, OP_Null, 0, n, n+m-1); + } sqlite3VdbeAddOp1(v, OP_NullRow, pLevel->iTabCur); } if( (ws & WHERE_INDEXED) @@ -167882,6 +169216,7 @@ SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo *pWInfo){ */ if( pTabItem->fg.viaCoroutine ){ testcase( pParse->db->mallocFailed ); + assert( pTabItem->regResult>=0 ); translateColumnToCopy(pParse, pLevel->addrBody, pLevel->iTabCur, pTabItem->regResult, 0); continue; @@ -169186,7 +170521,7 @@ SQLITE_PRIVATE void sqlite3WindowListDelete(sqlite3 *db, Window *p){ ** variable values in the expression tree. */ static Expr *sqlite3WindowOffsetExpr(Parse *pParse, Expr *pExpr){ - if( 0==sqlite3ExprIsConstant(pExpr) ){ + if( 0==sqlite3ExprIsConstant(0,pExpr) ){ if( IN_RENAME_OBJECT ) sqlite3RenameExprUnmap(pParse, pExpr); sqlite3ExprDelete(pParse->db, pExpr); pExpr = sqlite3ExprAlloc(pParse->db, TK_NULL, 0, 0); @@ -171278,6 +172613,14 @@ static void updateDeleteLimitError( return pSelect; } + /* Memory allocator for parser stack resizing. This is a thin wrapper around + ** sqlite3_realloc() that includes a call to sqlite3FaultSim() to facilitate + ** testing. + */ + static void *parserStackRealloc(void *pOld, sqlite3_uint64 newSize){ + return sqlite3FaultSim(700) ? 0 : sqlite3_realloc(pOld, newSize); + } + /* Construct a new Expr object from a single token */ static Expr *tokenExpr(Parse *pParse, int op, Token t){ @@ -171527,8 +172870,8 @@ static void updateDeleteLimitError( #define TK_TRUEFALSE 170 #define TK_ISNOT 171 #define TK_FUNCTION 172 -#define TK_UMINUS 173 -#define TK_UPLUS 174 +#define TK_UPLUS 173 +#define TK_UMINUS 174 #define TK_TRUTH 175 #define TK_REGISTER 176 #define TK_VECTOR 177 @@ -171537,8 +172880,9 @@ static void updateDeleteLimitError( #define TK_ASTERISK 180 #define TK_SPAN 181 #define TK_ERROR 182 -#define TK_SPACE 183 -#define TK_ILLEGAL 184 +#define TK_QNUMBER 183 +#define TK_SPACE 184 +#define TK_ILLEGAL 185 #endif /**************** End token definitions ***************************************/ @@ -171579,6 +172923,9 @@ static void updateDeleteLimitError( ** sqlite3ParserARG_STORE Code to store %extra_argument into yypParser ** sqlite3ParserARG_FETCH Code to extract %extra_argument from yypParser ** sqlite3ParserCTX_* As sqlite3ParserARG_ except for %extra_context +** YYREALLOC Name of the realloc() function to use +** YYFREE Name of the free() function to use +** YYDYNSTACK True if stack space should be extended on heap ** YYERRORSYMBOL is the code number of the error symbol. If not ** defined, then do no error processing. ** YYNSTATE the combined number of states. @@ -171592,37 +172939,39 @@ static void updateDeleteLimitError( ** YY_NO_ACTION The yy_action[] code for no-op ** YY_MIN_REDUCE Minimum value for reduce actions ** YY_MAX_REDUCE Maximum value for reduce actions +** YY_MIN_DSTRCTR Minimum symbol value that has a destructor +** YY_MAX_DSTRCTR Maximum symbol value that has a destructor */ #ifndef INTERFACE # define INTERFACE 1 #endif /************* Begin control #defines *****************************************/ #define YYCODETYPE unsigned short int -#define YYNOCODE 319 +#define YYNOCODE 322 #define YYACTIONTYPE unsigned short int #define YYWILDCARD 101 #define sqlite3ParserTOKENTYPE Token typedef union { int yyinit; sqlite3ParserTOKENTYPE yy0; - TriggerStep* yy33; - Window* yy41; - Select* yy47; - SrcList* yy131; - struct TrigEvent yy180; - struct {int value; int mask;} yy231; - IdList* yy254; - u32 yy285; - ExprList* yy322; - Cte* yy385; - int yy394; - Upsert* yy444; - u8 yy516; - With* yy521; - const char* yy522; - Expr* yy528; - OnOrUsing yy561; - struct FrameBound yy595; + ExprList* yy14; + With* yy59; + Cte* yy67; + Upsert* yy122; + IdList* yy132; + int yy144; + const char* yy168; + SrcList* yy203; + Window* yy211; + OnOrUsing yy269; + struct TrigEvent yy286; + struct {int value; int mask;} yy383; + u32 yy391; + TriggerStep* yy427; + Expr* yy454; + u8 yy462; + struct FrameBound yy509; + Select* yy555; } YYMINORTYPE; #ifndef YYSTACKDEPTH #define YYSTACKDEPTH 100 @@ -171632,24 +172981,29 @@ typedef union { #define sqlite3ParserARG_PARAM #define sqlite3ParserARG_FETCH #define sqlite3ParserARG_STORE +#define YYREALLOC parserStackRealloc +#define YYFREE sqlite3_free +#define YYDYNSTACK 1 #define sqlite3ParserCTX_SDECL Parse *pParse; #define sqlite3ParserCTX_PDECL ,Parse *pParse #define sqlite3ParserCTX_PARAM ,pParse #define sqlite3ParserCTX_FETCH Parse *pParse=yypParser->pParse; #define sqlite3ParserCTX_STORE yypParser->pParse=pParse; #define YYFALLBACK 1 -#define YYNSTATE 579 -#define YYNRULE 405 -#define YYNRULE_WITH_ACTION 340 -#define YYNTOKEN 185 -#define YY_MAX_SHIFT 578 -#define YY_MIN_SHIFTREDUCE 838 -#define YY_MAX_SHIFTREDUCE 1242 -#define YY_ERROR_ACTION 1243 -#define YY_ACCEPT_ACTION 1244 -#define YY_NO_ACTION 1245 -#define YY_MIN_REDUCE 1246 -#define YY_MAX_REDUCE 1650 +#define YYNSTATE 583 +#define YYNRULE 409 +#define YYNRULE_WITH_ACTION 344 +#define YYNTOKEN 186 +#define YY_MAX_SHIFT 582 +#define YY_MIN_SHIFTREDUCE 845 +#define YY_MAX_SHIFTREDUCE 1253 +#define YY_ERROR_ACTION 1254 +#define YY_ACCEPT_ACTION 1255 +#define YY_NO_ACTION 1256 +#define YY_MIN_REDUCE 1257 +#define YY_MAX_REDUCE 1665 +#define YY_MIN_DSTRCTR 205 +#define YY_MAX_DSTRCTR 319 /************* End control #defines *******************************************/ #define YY_NLOOKAHEAD ((int)(sizeof(yy_lookahead)/sizeof(yy_lookahead[0]))) @@ -171665,6 +173019,22 @@ typedef union { # define yytestcase(X) #endif +/* Macro to determine if stack space has the ability to grow using +** heap memory. +*/ +#if YYSTACKDEPTH<=0 || YYDYNSTACK +# define YYGROWABLESTACK 1 +#else +# define YYGROWABLESTACK 0 +#endif + +/* Guarantee a minimum number of initial stack slots. +*/ +#if YYSTACKDEPTH<=0 +# undef YYSTACKDEPTH +# define YYSTACKDEPTH 2 /* Need a minimum stack size */ +#endif + /* Next are the tables used to determine what action to take based on the ** current state and lookahead token. These tables are used to implement @@ -171716,619 +173086,630 @@ typedef union { ** yy_default[] Default action for each state. ** *********** Begin parsing tables **********************************************/ -#define YY_ACTTAB_COUNT (2100) +#define YY_ACTTAB_COUNT (2142) static const YYACTIONTYPE yy_action[] = { - /* 0 */ 572, 210, 572, 119, 116, 231, 572, 119, 116, 231, - /* 10 */ 572, 1317, 379, 1296, 410, 566, 566, 566, 572, 411, - /* 20 */ 380, 1317, 1279, 42, 42, 42, 42, 210, 1529, 72, - /* 30 */ 72, 974, 421, 42, 42, 495, 305, 281, 305, 975, - /* 40 */ 399, 72, 72, 126, 127, 81, 1217, 1217, 1054, 1057, - /* 50 */ 1044, 1044, 124, 124, 125, 125, 125, 125, 480, 411, - /* 60 */ 1244, 1, 1, 578, 2, 1248, 554, 119, 116, 231, - /* 70 */ 319, 484, 147, 484, 528, 119, 116, 231, 533, 1330, - /* 80 */ 419, 527, 143, 126, 127, 81, 1217, 1217, 1054, 1057, - /* 90 */ 1044, 1044, 124, 124, 125, 125, 125, 125, 119, 116, - /* 100 */ 231, 329, 123, 123, 123, 123, 122, 122, 121, 121, - /* 110 */ 121, 120, 117, 448, 286, 286, 286, 286, 446, 446, - /* 120 */ 446, 1568, 378, 1570, 1193, 377, 1164, 569, 1164, 569, - /* 130 */ 411, 1568, 541, 261, 228, 448, 102, 146, 453, 318, - /* 140 */ 563, 242, 123, 123, 123, 123, 122, 122, 121, 121, - /* 150 */ 121, 120, 117, 448, 126, 127, 81, 1217, 1217, 1054, - /* 160 */ 1057, 1044, 1044, 124, 124, 125, 125, 125, 125, 143, - /* 170 */ 296, 1193, 341, 452, 121, 121, 121, 120, 117, 448, - /* 180 */ 128, 1193, 1194, 1193, 149, 445, 444, 572, 120, 117, - /* 190 */ 448, 125, 125, 125, 125, 118, 123, 123, 123, 123, - /* 200 */ 122, 122, 121, 121, 121, 120, 117, 448, 458, 114, - /* 210 */ 13, 13, 550, 123, 123, 123, 123, 122, 122, 121, - /* 220 */ 121, 121, 120, 117, 448, 424, 318, 563, 1193, 1194, - /* 230 */ 1193, 150, 1225, 411, 1225, 125, 125, 125, 125, 123, - /* 240 */ 123, 123, 123, 122, 122, 121, 121, 121, 120, 117, - /* 250 */ 448, 469, 344, 1041, 1041, 1055, 1058, 126, 127, 81, - /* 260 */ 1217, 1217, 1054, 1057, 1044, 1044, 124, 124, 125, 125, - /* 270 */ 125, 125, 1282, 526, 224, 1193, 572, 411, 226, 519, - /* 280 */ 177, 83, 84, 123, 123, 123, 123, 122, 122, 121, - /* 290 */ 121, 121, 120, 117, 448, 1010, 16, 16, 1193, 134, - /* 300 */ 134, 126, 127, 81, 1217, 1217, 1054, 1057, 1044, 1044, - /* 310 */ 124, 124, 125, 125, 125, 125, 123, 123, 123, 123, - /* 320 */ 122, 122, 121, 121, 121, 120, 117, 448, 1045, 550, - /* 330 */ 1193, 375, 1193, 1194, 1193, 254, 1438, 401, 508, 505, - /* 340 */ 504, 112, 564, 570, 4, 929, 929, 435, 503, 342, - /* 350 */ 464, 330, 362, 396, 1238, 1193, 1194, 1193, 567, 572, - /* 360 */ 123, 123, 123, 123, 122, 122, 121, 121, 121, 120, - /* 370 */ 117, 448, 286, 286, 371, 1581, 1607, 445, 444, 155, - /* 380 */ 411, 449, 72, 72, 1289, 569, 1222, 1193, 1194, 1193, - /* 390 */ 86, 1224, 273, 561, 547, 520, 520, 572, 99, 1223, - /* 400 */ 6, 1281, 476, 143, 126, 127, 81, 1217, 1217, 1054, - /* 410 */ 1057, 1044, 1044, 124, 124, 125, 125, 125, 125, 554, - /* 420 */ 13, 13, 1031, 511, 1225, 1193, 1225, 553, 110, 110, - /* 430 */ 224, 572, 1239, 177, 572, 429, 111, 199, 449, 573, - /* 440 */ 449, 432, 1555, 1019, 327, 555, 1193, 272, 289, 370, - /* 450 */ 514, 365, 513, 259, 72, 72, 547, 72, 72, 361, - /* 460 */ 318, 563, 1613, 123, 123, 123, 123, 122, 122, 121, - /* 470 */ 121, 121, 120, 117, 448, 1019, 1019, 1021, 1022, 28, - /* 480 */ 286, 286, 1193, 1194, 1193, 1159, 572, 1612, 411, 904, - /* 490 */ 192, 554, 358, 569, 554, 940, 537, 521, 1159, 437, - /* 500 */ 415, 1159, 556, 1193, 1194, 1193, 572, 548, 548, 52, - /* 510 */ 52, 216, 126, 127, 81, 1217, 1217, 1054, 1057, 1044, - /* 520 */ 1044, 124, 124, 125, 125, 125, 125, 1193, 478, 136, - /* 530 */ 136, 411, 286, 286, 1493, 509, 122, 122, 121, 121, - /* 540 */ 121, 120, 117, 448, 1010, 569, 522, 219, 545, 545, - /* 550 */ 318, 563, 143, 6, 536, 126, 127, 81, 1217, 1217, - /* 560 */ 1054, 1057, 1044, 1044, 124, 124, 125, 125, 125, 125, - /* 570 */ 1557, 123, 123, 123, 123, 122, 122, 121, 121, 121, - /* 580 */ 120, 117, 448, 489, 1193, 1194, 1193, 486, 283, 1270, - /* 590 */ 960, 254, 1193, 375, 508, 505, 504, 1193, 342, 574, - /* 600 */ 1193, 574, 411, 294, 503, 960, 879, 193, 484, 318, - /* 610 */ 563, 386, 292, 382, 123, 123, 123, 123, 122, 122, - /* 620 */ 121, 121, 121, 120, 117, 448, 126, 127, 81, 1217, - /* 630 */ 1217, 1054, 1057, 1044, 1044, 124, 124, 125, 125, 125, - /* 640 */ 125, 411, 396, 1139, 1193, 872, 101, 286, 286, 1193, - /* 650 */ 1194, 1193, 375, 1096, 1193, 1194, 1193, 1193, 1194, 1193, - /* 660 */ 569, 459, 33, 375, 235, 126, 127, 81, 1217, 1217, - /* 670 */ 1054, 1057, 1044, 1044, 124, 124, 125, 125, 125, 125, - /* 680 */ 1437, 962, 572, 230, 961, 123, 123, 123, 123, 122, - /* 690 */ 122, 121, 121, 121, 120, 117, 448, 1159, 230, 1193, - /* 700 */ 158, 1193, 1194, 1193, 1556, 13, 13, 303, 960, 1233, - /* 710 */ 1159, 154, 411, 1159, 375, 1584, 1177, 5, 371, 1581, - /* 720 */ 431, 1239, 3, 960, 123, 123, 123, 123, 122, 122, - /* 730 */ 121, 121, 121, 120, 117, 448, 126, 127, 81, 1217, - /* 740 */ 1217, 1054, 1057, 1044, 1044, 124, 124, 125, 125, 125, - /* 750 */ 125, 411, 210, 571, 1193, 1032, 1193, 1194, 1193, 1193, - /* 760 */ 390, 855, 156, 1555, 376, 404, 1101, 1101, 492, 572, - /* 770 */ 469, 344, 1322, 1322, 1555, 126, 127, 81, 1217, 1217, - /* 780 */ 1054, 1057, 1044, 1044, 124, 124, 125, 125, 125, 125, - /* 790 */ 130, 572, 13, 13, 532, 123, 123, 123, 123, 122, - /* 800 */ 122, 121, 121, 121, 120, 117, 448, 304, 572, 457, - /* 810 */ 229, 1193, 1194, 1193, 13, 13, 1193, 1194, 1193, 1300, - /* 820 */ 467, 1270, 411, 1320, 1320, 1555, 1015, 457, 456, 436, - /* 830 */ 301, 72, 72, 1268, 123, 123, 123, 123, 122, 122, - /* 840 */ 121, 121, 121, 120, 117, 448, 126, 127, 81, 1217, - /* 850 */ 1217, 1054, 1057, 1044, 1044, 124, 124, 125, 125, 125, - /* 860 */ 125, 411, 384, 1076, 1159, 286, 286, 421, 314, 280, - /* 870 */ 280, 287, 287, 461, 408, 407, 1539, 1159, 569, 572, - /* 880 */ 1159, 1196, 569, 409, 569, 126, 127, 81, 1217, 1217, - /* 890 */ 1054, 1057, 1044, 1044, 124, 124, 125, 125, 125, 125, - /* 900 */ 457, 1485, 13, 13, 1541, 123, 123, 123, 123, 122, - /* 910 */ 122, 121, 121, 121, 120, 117, 448, 202, 572, 462, - /* 920 */ 1587, 578, 2, 1248, 843, 844, 845, 1563, 319, 409, - /* 930 */ 147, 6, 411, 257, 256, 255, 208, 1330, 9, 1196, - /* 940 */ 264, 72, 72, 1436, 123, 123, 123, 123, 122, 122, - /* 950 */ 121, 121, 121, 120, 117, 448, 126, 127, 81, 1217, - /* 960 */ 1217, 1054, 1057, 1044, 1044, 124, 124, 125, 125, 125, - /* 970 */ 125, 572, 286, 286, 572, 1213, 411, 577, 315, 1248, - /* 980 */ 421, 371, 1581, 356, 319, 569, 147, 495, 529, 1644, - /* 990 */ 397, 935, 495, 1330, 71, 71, 934, 72, 72, 242, - /* 1000 */ 1328, 105, 81, 1217, 1217, 1054, 1057, 1044, 1044, 124, - /* 1010 */ 124, 125, 125, 125, 125, 123, 123, 123, 123, 122, - /* 1020 */ 122, 121, 121, 121, 120, 117, 448, 1117, 286, 286, - /* 1030 */ 1422, 452, 1528, 1213, 443, 286, 286, 1492, 1355, 313, - /* 1040 */ 478, 569, 1118, 454, 351, 495, 354, 1266, 569, 209, - /* 1050 */ 572, 418, 179, 572, 1031, 242, 385, 1119, 523, 123, - /* 1060 */ 123, 123, 123, 122, 122, 121, 121, 121, 120, 117, - /* 1070 */ 448, 1020, 108, 72, 72, 1019, 13, 13, 915, 572, - /* 1080 */ 1498, 572, 286, 286, 98, 530, 1537, 452, 916, 1334, - /* 1090 */ 1329, 203, 411, 286, 286, 569, 152, 211, 1498, 1500, - /* 1100 */ 426, 569, 56, 56, 57, 57, 569, 1019, 1019, 1021, - /* 1110 */ 447, 572, 411, 531, 12, 297, 126, 127, 81, 1217, - /* 1120 */ 1217, 1054, 1057, 1044, 1044, 124, 124, 125, 125, 125, - /* 1130 */ 125, 572, 411, 867, 15, 15, 126, 127, 81, 1217, - /* 1140 */ 1217, 1054, 1057, 1044, 1044, 124, 124, 125, 125, 125, - /* 1150 */ 125, 373, 529, 264, 44, 44, 126, 115, 81, 1217, - /* 1160 */ 1217, 1054, 1057, 1044, 1044, 124, 124, 125, 125, 125, - /* 1170 */ 125, 1498, 478, 1271, 417, 123, 123, 123, 123, 122, - /* 1180 */ 122, 121, 121, 121, 120, 117, 448, 205, 1213, 495, - /* 1190 */ 430, 867, 468, 322, 495, 123, 123, 123, 123, 122, - /* 1200 */ 122, 121, 121, 121, 120, 117, 448, 572, 557, 1140, - /* 1210 */ 1642, 1422, 1642, 543, 572, 123, 123, 123, 123, 122, - /* 1220 */ 122, 121, 121, 121, 120, 117, 448, 572, 1422, 572, - /* 1230 */ 13, 13, 542, 323, 1325, 411, 334, 58, 58, 349, - /* 1240 */ 1422, 1170, 326, 286, 286, 549, 1213, 300, 895, 530, - /* 1250 */ 45, 45, 59, 59, 1140, 1643, 569, 1643, 565, 417, - /* 1260 */ 127, 81, 1217, 1217, 1054, 1057, 1044, 1044, 124, 124, - /* 1270 */ 125, 125, 125, 125, 1367, 373, 500, 290, 1193, 512, - /* 1280 */ 1366, 427, 394, 394, 393, 275, 391, 896, 1138, 852, - /* 1290 */ 478, 258, 1422, 1170, 463, 1159, 12, 331, 428, 333, - /* 1300 */ 1117, 460, 236, 258, 325, 460, 544, 1544, 1159, 1098, - /* 1310 */ 491, 1159, 324, 1098, 440, 1118, 335, 516, 123, 123, - /* 1320 */ 123, 123, 122, 122, 121, 121, 121, 120, 117, 448, - /* 1330 */ 1119, 318, 563, 1138, 572, 1193, 1194, 1193, 112, 564, - /* 1340 */ 201, 4, 238, 433, 935, 490, 285, 228, 1517, 934, - /* 1350 */ 170, 560, 572, 142, 1516, 567, 572, 60, 60, 572, - /* 1360 */ 416, 572, 441, 572, 535, 302, 875, 8, 487, 572, - /* 1370 */ 237, 572, 416, 572, 485, 61, 61, 572, 449, 62, - /* 1380 */ 62, 332, 63, 63, 46, 46, 47, 47, 361, 572, - /* 1390 */ 561, 572, 48, 48, 50, 50, 51, 51, 572, 295, - /* 1400 */ 64, 64, 482, 295, 539, 412, 471, 1031, 572, 538, - /* 1410 */ 318, 563, 65, 65, 66, 66, 409, 475, 572, 1031, - /* 1420 */ 572, 14, 14, 875, 1020, 110, 110, 409, 1019, 572, - /* 1430 */ 474, 67, 67, 111, 455, 449, 573, 449, 98, 317, - /* 1440 */ 1019, 132, 132, 133, 133, 572, 1561, 572, 974, 409, - /* 1450 */ 6, 1562, 68, 68, 1560, 6, 975, 572, 6, 1559, - /* 1460 */ 1019, 1019, 1021, 6, 346, 218, 101, 531, 53, 53, - /* 1470 */ 69, 69, 1019, 1019, 1021, 1022, 28, 1586, 1181, 451, - /* 1480 */ 70, 70, 290, 87, 215, 31, 1363, 394, 394, 393, - /* 1490 */ 275, 391, 350, 109, 852, 107, 572, 112, 564, 483, - /* 1500 */ 4, 1212, 572, 239, 153, 572, 39, 236, 1299, 325, - /* 1510 */ 112, 564, 1298, 4, 567, 572, 32, 324, 572, 54, - /* 1520 */ 54, 572, 1135, 353, 398, 165, 165, 567, 166, 166, - /* 1530 */ 572, 291, 355, 572, 17, 357, 572, 449, 77, 77, - /* 1540 */ 1313, 55, 55, 1297, 73, 73, 572, 238, 470, 561, - /* 1550 */ 449, 472, 364, 135, 135, 170, 74, 74, 142, 163, - /* 1560 */ 163, 374, 561, 539, 572, 321, 572, 886, 540, 137, - /* 1570 */ 137, 339, 1353, 422, 298, 237, 539, 572, 1031, 572, - /* 1580 */ 340, 538, 101, 369, 110, 110, 162, 131, 131, 164, - /* 1590 */ 164, 1031, 111, 368, 449, 573, 449, 110, 110, 1019, - /* 1600 */ 157, 157, 141, 141, 572, 111, 572, 449, 573, 449, - /* 1610 */ 412, 288, 1019, 572, 882, 318, 563, 572, 219, 572, - /* 1620 */ 241, 1012, 477, 263, 263, 894, 893, 140, 140, 138, - /* 1630 */ 138, 1019, 1019, 1021, 1022, 28, 139, 139, 525, 455, - /* 1640 */ 76, 76, 78, 78, 1019, 1019, 1021, 1022, 28, 1181, - /* 1650 */ 451, 572, 1083, 290, 112, 564, 1575, 4, 394, 394, - /* 1660 */ 393, 275, 391, 572, 1023, 852, 572, 479, 345, 263, - /* 1670 */ 101, 567, 882, 1376, 75, 75, 1421, 501, 236, 260, - /* 1680 */ 325, 112, 564, 359, 4, 101, 43, 43, 324, 49, - /* 1690 */ 49, 901, 902, 161, 449, 101, 977, 978, 567, 1079, - /* 1700 */ 1349, 260, 965, 932, 263, 114, 561, 1095, 517, 1095, - /* 1710 */ 1083, 1094, 865, 1094, 151, 933, 1144, 114, 238, 1361, - /* 1720 */ 558, 449, 1023, 559, 1426, 1278, 170, 1269, 1257, 142, - /* 1730 */ 1601, 1256, 1258, 561, 1594, 1031, 496, 278, 213, 1346, - /* 1740 */ 310, 110, 110, 939, 311, 312, 237, 11, 234, 111, - /* 1750 */ 221, 449, 573, 449, 293, 395, 1019, 1408, 337, 1403, - /* 1760 */ 1396, 338, 1031, 299, 343, 1413, 1412, 481, 110, 110, - /* 1770 */ 506, 402, 225, 1296, 206, 367, 111, 1358, 449, 573, - /* 1780 */ 449, 412, 1359, 1019, 1489, 1488, 318, 563, 1019, 1019, - /* 1790 */ 1021, 1022, 28, 562, 207, 220, 80, 564, 389, 4, - /* 1800 */ 1597, 1357, 552, 1356, 1233, 181, 267, 232, 1536, 1534, - /* 1810 */ 455, 1230, 420, 567, 82, 1019, 1019, 1021, 1022, 28, - /* 1820 */ 86, 217, 85, 1494, 190, 175, 183, 465, 185, 466, - /* 1830 */ 36, 1409, 186, 187, 188, 499, 449, 244, 37, 99, - /* 1840 */ 400, 1415, 1414, 488, 1417, 194, 473, 403, 561, 1483, - /* 1850 */ 248, 92, 1505, 494, 198, 279, 112, 564, 250, 4, - /* 1860 */ 348, 497, 405, 352, 1259, 251, 252, 515, 1316, 434, - /* 1870 */ 1315, 1314, 94, 567, 1307, 886, 1306, 1031, 226, 406, - /* 1880 */ 1611, 1610, 438, 110, 110, 1580, 1286, 524, 439, 308, - /* 1890 */ 266, 111, 1285, 449, 573, 449, 449, 309, 1019, 366, - /* 1900 */ 1284, 1609, 265, 1566, 1565, 442, 372, 1381, 561, 129, - /* 1910 */ 550, 1380, 10, 1470, 383, 106, 316, 551, 100, 35, - /* 1920 */ 534, 575, 212, 1339, 381, 387, 1187, 1338, 274, 276, - /* 1930 */ 1019, 1019, 1021, 1022, 28, 277, 413, 1031, 576, 1254, - /* 1940 */ 388, 1521, 1249, 110, 110, 167, 1522, 168, 148, 1520, - /* 1950 */ 1519, 111, 306, 449, 573, 449, 222, 223, 1019, 839, - /* 1960 */ 169, 79, 450, 214, 414, 233, 320, 145, 1093, 1091, - /* 1970 */ 328, 182, 171, 1212, 918, 184, 240, 336, 243, 1107, - /* 1980 */ 189, 172, 173, 423, 425, 88, 180, 191, 89, 90, - /* 1990 */ 1019, 1019, 1021, 1022, 28, 91, 174, 1110, 245, 1106, - /* 2000 */ 246, 159, 18, 247, 347, 1099, 263, 195, 1227, 493, - /* 2010 */ 249, 196, 38, 854, 498, 368, 253, 360, 897, 197, - /* 2020 */ 502, 93, 19, 20, 507, 884, 363, 510, 95, 307, - /* 2030 */ 160, 96, 518, 97, 1175, 1060, 1146, 40, 21, 227, - /* 2040 */ 176, 1145, 282, 284, 969, 200, 963, 114, 262, 1165, - /* 2050 */ 22, 23, 24, 1161, 1169, 25, 1163, 1150, 34, 26, - /* 2060 */ 1168, 546, 27, 204, 101, 103, 104, 1074, 7, 1061, - /* 2070 */ 1059, 1063, 1116, 1064, 1115, 268, 269, 29, 41, 270, - /* 2080 */ 1024, 866, 113, 30, 568, 392, 1183, 144, 178, 1182, - /* 2090 */ 271, 928, 1245, 1245, 1245, 1245, 1245, 1245, 1245, 1602, + /* 0 */ 576, 128, 125, 232, 1622, 549, 576, 1290, 1281, 576, + /* 10 */ 328, 576, 1300, 212, 576, 128, 125, 232, 578, 412, + /* 20 */ 578, 391, 1542, 51, 51, 523, 405, 1293, 529, 51, + /* 30 */ 51, 983, 51, 51, 81, 81, 1107, 61, 61, 984, + /* 40 */ 1107, 1292, 380, 135, 136, 90, 1228, 1228, 1063, 1066, + /* 50 */ 1053, 1053, 133, 133, 134, 134, 134, 134, 1577, 412, + /* 60 */ 287, 287, 7, 287, 287, 422, 1050, 1050, 1064, 1067, + /* 70 */ 289, 556, 492, 573, 524, 561, 573, 497, 561, 482, + /* 80 */ 530, 262, 229, 135, 136, 90, 1228, 1228, 1063, 1066, + /* 90 */ 1053, 1053, 133, 133, 134, 134, 134, 134, 128, 125, + /* 100 */ 232, 1506, 132, 132, 132, 132, 131, 131, 130, 130, + /* 110 */ 130, 129, 126, 450, 1204, 1255, 1, 1, 582, 2, + /* 120 */ 1259, 1571, 420, 1582, 379, 320, 1174, 153, 1174, 1584, + /* 130 */ 412, 378, 1582, 543, 1341, 330, 111, 570, 570, 570, + /* 140 */ 293, 1054, 132, 132, 132, 132, 131, 131, 130, 130, + /* 150 */ 130, 129, 126, 450, 135, 136, 90, 1228, 1228, 1063, + /* 160 */ 1066, 1053, 1053, 133, 133, 134, 134, 134, 134, 287, + /* 170 */ 287, 1204, 1205, 1204, 255, 287, 287, 510, 507, 506, + /* 180 */ 137, 455, 573, 212, 561, 447, 446, 505, 573, 1616, + /* 190 */ 561, 134, 134, 134, 134, 127, 400, 243, 132, 132, + /* 200 */ 132, 132, 131, 131, 130, 130, 130, 129, 126, 450, + /* 210 */ 282, 471, 345, 132, 132, 132, 132, 131, 131, 130, + /* 220 */ 130, 130, 129, 126, 450, 574, 155, 936, 936, 454, + /* 230 */ 227, 521, 1236, 412, 1236, 134, 134, 134, 134, 132, + /* 240 */ 132, 132, 132, 131, 131, 130, 130, 130, 129, 126, + /* 250 */ 450, 130, 130, 130, 129, 126, 450, 135, 136, 90, + /* 260 */ 1228, 1228, 1063, 1066, 1053, 1053, 133, 133, 134, 134, + /* 270 */ 134, 134, 128, 125, 232, 450, 576, 412, 397, 1249, + /* 280 */ 180, 92, 93, 132, 132, 132, 132, 131, 131, 130, + /* 290 */ 130, 130, 129, 126, 450, 381, 387, 1204, 383, 81, + /* 300 */ 81, 135, 136, 90, 1228, 1228, 1063, 1066, 1053, 1053, + /* 310 */ 133, 133, 134, 134, 134, 134, 132, 132, 132, 132, + /* 320 */ 131, 131, 130, 130, 130, 129, 126, 450, 131, 131, + /* 330 */ 130, 130, 130, 129, 126, 450, 556, 1204, 302, 319, + /* 340 */ 567, 121, 568, 480, 4, 555, 1149, 1657, 1628, 1657, + /* 350 */ 45, 128, 125, 232, 1204, 1205, 1204, 1250, 571, 1169, + /* 360 */ 132, 132, 132, 132, 131, 131, 130, 130, 130, 129, + /* 370 */ 126, 450, 1169, 287, 287, 1169, 1019, 576, 422, 1019, + /* 380 */ 412, 451, 1602, 582, 2, 1259, 573, 44, 561, 95, + /* 390 */ 320, 110, 153, 565, 1204, 1205, 1204, 522, 522, 1341, + /* 400 */ 81, 81, 7, 44, 135, 136, 90, 1228, 1228, 1063, + /* 410 */ 1066, 1053, 1053, 133, 133, 134, 134, 134, 134, 295, + /* 420 */ 1149, 1658, 1040, 1658, 1204, 1147, 319, 567, 119, 119, + /* 430 */ 343, 466, 331, 343, 287, 287, 120, 556, 451, 577, + /* 440 */ 451, 1169, 1169, 1028, 319, 567, 438, 573, 210, 561, + /* 450 */ 1339, 1451, 546, 531, 1169, 1169, 1598, 1169, 1169, 416, + /* 460 */ 319, 567, 243, 132, 132, 132, 132, 131, 131, 130, + /* 470 */ 130, 130, 129, 126, 450, 1028, 1028, 1030, 1031, 35, + /* 480 */ 44, 1204, 1205, 1204, 472, 287, 287, 1328, 412, 1307, + /* 490 */ 372, 1595, 359, 225, 454, 1204, 195, 1328, 573, 1147, + /* 500 */ 561, 1333, 1333, 274, 576, 1188, 576, 340, 46, 196, + /* 510 */ 537, 217, 135, 136, 90, 1228, 1228, 1063, 1066, 1053, + /* 520 */ 1053, 133, 133, 134, 134, 134, 134, 19, 19, 19, + /* 530 */ 19, 412, 581, 1204, 1259, 511, 1204, 319, 567, 320, + /* 540 */ 944, 153, 425, 491, 430, 943, 1204, 488, 1341, 1450, + /* 550 */ 532, 1277, 1204, 1205, 1204, 135, 136, 90, 1228, 1228, + /* 560 */ 1063, 1066, 1053, 1053, 133, 133, 134, 134, 134, 134, + /* 570 */ 575, 132, 132, 132, 132, 131, 131, 130, 130, 130, + /* 580 */ 129, 126, 450, 287, 287, 528, 287, 287, 372, 1595, + /* 590 */ 1204, 1205, 1204, 1204, 1205, 1204, 573, 486, 561, 573, + /* 600 */ 889, 561, 412, 1204, 1205, 1204, 886, 40, 22, 22, + /* 610 */ 220, 243, 525, 1449, 132, 132, 132, 132, 131, 131, + /* 620 */ 130, 130, 130, 129, 126, 450, 135, 136, 90, 1228, + /* 630 */ 1228, 1063, 1066, 1053, 1053, 133, 133, 134, 134, 134, + /* 640 */ 134, 412, 180, 454, 1204, 879, 255, 287, 287, 510, + /* 650 */ 507, 506, 372, 1595, 1568, 1331, 1331, 576, 889, 505, + /* 660 */ 573, 44, 561, 559, 1207, 135, 136, 90, 1228, 1228, + /* 670 */ 1063, 1066, 1053, 1053, 133, 133, 134, 134, 134, 134, + /* 680 */ 81, 81, 422, 576, 377, 132, 132, 132, 132, 131, + /* 690 */ 131, 130, 130, 130, 129, 126, 450, 297, 287, 287, + /* 700 */ 460, 1204, 1205, 1204, 1204, 534, 19, 19, 448, 448, + /* 710 */ 448, 573, 412, 561, 230, 436, 1187, 535, 319, 567, + /* 720 */ 363, 432, 1207, 1435, 132, 132, 132, 132, 131, 131, + /* 730 */ 130, 130, 130, 129, 126, 450, 135, 136, 90, 1228, + /* 740 */ 1228, 1063, 1066, 1053, 1053, 133, 133, 134, 134, 134, + /* 750 */ 134, 412, 211, 949, 1169, 1041, 1110, 1110, 494, 547, + /* 760 */ 547, 1204, 1205, 1204, 7, 539, 1570, 1169, 376, 576, + /* 770 */ 1169, 5, 1204, 486, 3, 135, 136, 90, 1228, 1228, + /* 780 */ 1063, 1066, 1053, 1053, 133, 133, 134, 134, 134, 134, + /* 790 */ 576, 513, 19, 19, 427, 132, 132, 132, 132, 131, + /* 800 */ 131, 130, 130, 130, 129, 126, 450, 305, 1204, 433, + /* 810 */ 225, 1204, 385, 19, 19, 273, 290, 371, 516, 366, + /* 820 */ 515, 260, 412, 538, 1568, 549, 1024, 362, 437, 1204, + /* 830 */ 1205, 1204, 902, 1552, 132, 132, 132, 132, 131, 131, + /* 840 */ 130, 130, 130, 129, 126, 450, 135, 136, 90, 1228, + /* 850 */ 1228, 1063, 1066, 1053, 1053, 133, 133, 134, 134, 134, + /* 860 */ 134, 412, 1435, 514, 1281, 1204, 1205, 1204, 1204, 1205, + /* 870 */ 1204, 903, 48, 342, 1568, 1568, 1279, 1627, 1568, 911, + /* 880 */ 576, 129, 126, 450, 110, 135, 136, 90, 1228, 1228, + /* 890 */ 1063, 1066, 1053, 1053, 133, 133, 134, 134, 134, 134, + /* 900 */ 265, 576, 459, 19, 19, 132, 132, 132, 132, 131, + /* 910 */ 131, 130, 130, 130, 129, 126, 450, 1345, 204, 576, + /* 920 */ 459, 458, 50, 47, 19, 19, 49, 434, 1105, 573, + /* 930 */ 497, 561, 412, 428, 108, 1224, 1569, 1554, 376, 205, + /* 940 */ 550, 550, 81, 81, 132, 132, 132, 132, 131, 131, + /* 950 */ 130, 130, 130, 129, 126, 450, 135, 136, 90, 1228, + /* 960 */ 1228, 1063, 1066, 1053, 1053, 133, 133, 134, 134, 134, + /* 970 */ 134, 480, 576, 1204, 576, 1541, 412, 1435, 969, 315, + /* 980 */ 1659, 398, 284, 497, 969, 893, 1569, 1569, 376, 376, + /* 990 */ 1569, 461, 376, 1224, 459, 80, 80, 81, 81, 497, + /* 1000 */ 374, 114, 90, 1228, 1228, 1063, 1066, 1053, 1053, 133, + /* 1010 */ 133, 134, 134, 134, 134, 132, 132, 132, 132, 131, + /* 1020 */ 131, 130, 130, 130, 129, 126, 450, 1204, 1505, 576, + /* 1030 */ 1204, 1205, 1204, 1366, 316, 486, 281, 281, 497, 431, + /* 1040 */ 557, 288, 288, 402, 1340, 471, 345, 298, 429, 573, + /* 1050 */ 576, 561, 81, 81, 573, 374, 561, 971, 386, 132, + /* 1060 */ 132, 132, 132, 131, 131, 130, 130, 130, 129, 126, + /* 1070 */ 450, 231, 117, 81, 81, 287, 287, 231, 287, 287, + /* 1080 */ 576, 1511, 576, 1336, 1204, 1205, 1204, 139, 573, 556, + /* 1090 */ 561, 573, 412, 561, 441, 456, 969, 213, 558, 1511, + /* 1100 */ 1513, 1550, 969, 143, 143, 145, 145, 1368, 314, 478, + /* 1110 */ 444, 970, 412, 850, 851, 852, 135, 136, 90, 1228, + /* 1120 */ 1228, 1063, 1066, 1053, 1053, 133, 133, 134, 134, 134, + /* 1130 */ 134, 357, 412, 397, 1148, 304, 135, 136, 90, 1228, + /* 1140 */ 1228, 1063, 1066, 1053, 1053, 133, 133, 134, 134, 134, + /* 1150 */ 134, 1575, 323, 6, 862, 7, 135, 124, 90, 1228, + /* 1160 */ 1228, 1063, 1066, 1053, 1053, 133, 133, 134, 134, 134, + /* 1170 */ 134, 409, 408, 1511, 212, 132, 132, 132, 132, 131, + /* 1180 */ 131, 130, 130, 130, 129, 126, 450, 411, 118, 1204, + /* 1190 */ 116, 10, 352, 265, 355, 132, 132, 132, 132, 131, + /* 1200 */ 131, 130, 130, 130, 129, 126, 450, 576, 324, 306, + /* 1210 */ 576, 306, 1250, 469, 158, 132, 132, 132, 132, 131, + /* 1220 */ 131, 130, 130, 130, 129, 126, 450, 207, 1224, 1126, + /* 1230 */ 65, 65, 470, 66, 66, 412, 447, 446, 882, 531, + /* 1240 */ 335, 258, 257, 256, 1127, 1233, 1204, 1205, 1204, 327, + /* 1250 */ 1235, 874, 159, 576, 16, 480, 1085, 1040, 1234, 1128, + /* 1260 */ 136, 90, 1228, 1228, 1063, 1066, 1053, 1053, 133, 133, + /* 1270 */ 134, 134, 134, 134, 1029, 576, 81, 81, 1028, 1040, + /* 1280 */ 922, 576, 463, 1236, 576, 1236, 1224, 502, 107, 1435, + /* 1290 */ 923, 6, 576, 410, 1498, 882, 1029, 480, 21, 21, + /* 1300 */ 1028, 332, 1380, 334, 53, 53, 497, 81, 81, 874, + /* 1310 */ 1028, 1028, 1030, 445, 259, 19, 19, 533, 132, 132, + /* 1320 */ 132, 132, 131, 131, 130, 130, 130, 129, 126, 450, + /* 1330 */ 551, 301, 1028, 1028, 1030, 107, 532, 545, 121, 568, + /* 1340 */ 1188, 4, 1126, 1576, 449, 576, 462, 7, 1282, 418, + /* 1350 */ 462, 350, 1435, 576, 518, 571, 544, 1127, 121, 568, + /* 1360 */ 442, 4, 1188, 464, 533, 1180, 1223, 9, 67, 67, + /* 1370 */ 487, 576, 1128, 303, 410, 571, 54, 54, 451, 576, + /* 1380 */ 123, 944, 576, 417, 576, 333, 943, 1379, 576, 236, + /* 1390 */ 565, 576, 1574, 564, 68, 68, 7, 576, 451, 362, + /* 1400 */ 419, 182, 69, 69, 541, 70, 70, 71, 71, 540, + /* 1410 */ 565, 72, 72, 484, 55, 55, 473, 1180, 296, 1040, + /* 1420 */ 56, 56, 296, 493, 541, 119, 119, 410, 1573, 542, + /* 1430 */ 569, 418, 7, 120, 1244, 451, 577, 451, 465, 1040, + /* 1440 */ 1028, 576, 1557, 552, 476, 119, 119, 527, 259, 121, + /* 1450 */ 568, 240, 4, 120, 576, 451, 577, 451, 576, 477, + /* 1460 */ 1028, 576, 156, 576, 57, 57, 571, 576, 286, 229, + /* 1470 */ 410, 336, 1028, 1028, 1030, 1031, 35, 59, 59, 219, + /* 1480 */ 983, 60, 60, 220, 73, 73, 74, 74, 984, 451, + /* 1490 */ 75, 75, 1028, 1028, 1030, 1031, 35, 96, 216, 291, + /* 1500 */ 552, 565, 1188, 318, 395, 395, 394, 276, 392, 576, + /* 1510 */ 485, 859, 474, 1311, 410, 541, 576, 417, 1530, 1144, + /* 1520 */ 540, 399, 1188, 292, 237, 1153, 326, 38, 23, 576, + /* 1530 */ 1040, 576, 20, 20, 325, 299, 119, 119, 164, 76, + /* 1540 */ 76, 1529, 121, 568, 120, 4, 451, 577, 451, 203, + /* 1550 */ 576, 1028, 141, 141, 142, 142, 576, 322, 39, 571, + /* 1560 */ 341, 1021, 110, 264, 239, 901, 900, 423, 242, 908, + /* 1570 */ 909, 370, 173, 77, 77, 43, 479, 1310, 264, 62, + /* 1580 */ 62, 369, 451, 1028, 1028, 1030, 1031, 35, 1601, 1192, + /* 1590 */ 453, 1092, 238, 291, 565, 163, 1309, 110, 395, 395, + /* 1600 */ 394, 276, 392, 986, 987, 859, 481, 346, 264, 110, + /* 1610 */ 1032, 489, 576, 1188, 503, 1088, 261, 261, 237, 576, + /* 1620 */ 326, 121, 568, 1040, 4, 347, 1376, 413, 325, 119, + /* 1630 */ 119, 948, 319, 567, 351, 78, 78, 120, 571, 451, + /* 1640 */ 577, 451, 79, 79, 1028, 354, 356, 576, 360, 1092, + /* 1650 */ 110, 576, 974, 942, 264, 123, 457, 358, 239, 576, + /* 1660 */ 519, 451, 939, 1104, 123, 1104, 173, 576, 1032, 43, + /* 1670 */ 63, 63, 1324, 565, 168, 168, 1028, 1028, 1030, 1031, + /* 1680 */ 35, 576, 169, 169, 1308, 872, 238, 157, 1589, 576, + /* 1690 */ 86, 86, 365, 89, 568, 375, 4, 1103, 941, 1103, + /* 1700 */ 123, 576, 1040, 1389, 64, 64, 1188, 1434, 119, 119, + /* 1710 */ 571, 576, 82, 82, 563, 576, 120, 165, 451, 577, + /* 1720 */ 451, 413, 1362, 1028, 144, 144, 319, 567, 576, 1374, + /* 1730 */ 562, 498, 279, 451, 83, 83, 1439, 576, 166, 166, + /* 1740 */ 576, 1289, 554, 576, 1280, 565, 576, 12, 576, 1268, + /* 1750 */ 457, 146, 146, 1267, 576, 1028, 1028, 1030, 1031, 35, + /* 1760 */ 140, 140, 1269, 167, 167, 1609, 160, 160, 1359, 150, + /* 1770 */ 150, 149, 149, 311, 1040, 576, 312, 147, 147, 313, + /* 1780 */ 119, 119, 222, 235, 576, 1188, 396, 576, 120, 576, + /* 1790 */ 451, 577, 451, 1192, 453, 1028, 508, 291, 148, 148, + /* 1800 */ 1421, 1612, 395, 395, 394, 276, 392, 85, 85, 859, + /* 1810 */ 87, 87, 84, 84, 553, 576, 294, 576, 1426, 338, + /* 1820 */ 339, 1425, 237, 300, 326, 1416, 1409, 1028, 1028, 1030, + /* 1830 */ 1031, 35, 325, 344, 403, 483, 226, 1307, 52, 52, + /* 1840 */ 58, 58, 368, 1371, 1502, 566, 1501, 121, 568, 221, + /* 1850 */ 4, 208, 268, 209, 390, 1244, 1549, 1188, 1372, 1370, + /* 1860 */ 1369, 1547, 239, 184, 571, 233, 421, 1241, 95, 218, + /* 1870 */ 173, 1507, 193, 43, 91, 94, 178, 186, 467, 188, + /* 1880 */ 468, 1422, 13, 189, 190, 191, 501, 451, 245, 108, + /* 1890 */ 238, 401, 1428, 1427, 1430, 475, 404, 1496, 197, 565, + /* 1900 */ 14, 490, 249, 101, 1518, 496, 349, 280, 251, 201, + /* 1910 */ 353, 499, 252, 406, 1270, 253, 517, 1327, 1326, 435, + /* 1920 */ 1325, 1318, 103, 893, 1296, 413, 227, 407, 1040, 1626, + /* 1930 */ 319, 567, 1625, 1297, 119, 119, 439, 367, 1317, 1295, + /* 1940 */ 1624, 526, 120, 440, 451, 577, 451, 1594, 309, 1028, + /* 1950 */ 310, 373, 266, 267, 457, 1580, 1579, 443, 138, 1394, + /* 1960 */ 552, 1393, 11, 1483, 384, 115, 317, 1350, 109, 536, + /* 1970 */ 42, 579, 382, 214, 1349, 388, 1198, 389, 275, 277, + /* 1980 */ 278, 1028, 1028, 1030, 1031, 35, 580, 1265, 414, 1260, + /* 1990 */ 170, 415, 183, 1534, 1535, 1533, 171, 154, 307, 1532, + /* 2000 */ 846, 223, 224, 88, 452, 215, 172, 321, 234, 1102, + /* 2010 */ 152, 1188, 1100, 329, 185, 174, 1223, 925, 187, 241, + /* 2020 */ 337, 244, 1116, 192, 175, 176, 424, 426, 97, 194, + /* 2030 */ 98, 99, 100, 177, 1119, 1115, 246, 247, 161, 24, + /* 2040 */ 248, 348, 1238, 264, 1108, 250, 495, 199, 198, 15, + /* 2050 */ 861, 500, 369, 254, 504, 509, 512, 200, 102, 25, + /* 2060 */ 179, 361, 26, 364, 104, 891, 308, 162, 105, 904, + /* 2070 */ 520, 106, 1185, 1069, 1155, 17, 228, 27, 1154, 283, + /* 2080 */ 285, 263, 978, 202, 972, 123, 28, 1175, 29, 30, + /* 2090 */ 1179, 1171, 31, 1173, 1160, 41, 32, 206, 548, 33, + /* 2100 */ 110, 1178, 1083, 8, 112, 1070, 113, 1068, 1072, 34, + /* 2110 */ 1073, 560, 1125, 269, 1124, 270, 36, 18, 1194, 1033, + /* 2120 */ 873, 151, 122, 37, 393, 271, 272, 572, 181, 1193, + /* 2130 */ 1256, 1256, 1256, 935, 1256, 1256, 1256, 1256, 1256, 1256, + /* 2140 */ 1256, 1617, }; static const YYCODETYPE yy_lookahead[] = { - /* 0 */ 193, 193, 193, 274, 275, 276, 193, 274, 275, 276, - /* 10 */ 193, 223, 219, 225, 206, 210, 211, 212, 193, 19, - /* 20 */ 219, 233, 216, 216, 217, 216, 217, 193, 295, 216, - /* 30 */ 217, 31, 193, 216, 217, 193, 228, 213, 230, 39, - /* 40 */ 206, 216, 217, 43, 44, 45, 46, 47, 48, 49, - /* 50 */ 50, 51, 52, 53, 54, 55, 56, 57, 193, 19, - /* 60 */ 185, 186, 187, 188, 189, 190, 253, 274, 275, 276, - /* 70 */ 195, 193, 197, 193, 261, 274, 275, 276, 253, 204, - /* 80 */ 238, 204, 81, 43, 44, 45, 46, 47, 48, 49, - /* 90 */ 50, 51, 52, 53, 54, 55, 56, 57, 274, 275, - /* 100 */ 276, 262, 102, 103, 104, 105, 106, 107, 108, 109, - /* 110 */ 110, 111, 112, 113, 239, 240, 239, 240, 210, 211, - /* 120 */ 212, 314, 315, 314, 59, 316, 86, 252, 88, 252, - /* 130 */ 19, 314, 315, 256, 257, 113, 25, 72, 296, 138, - /* 140 */ 139, 266, 102, 103, 104, 105, 106, 107, 108, 109, + /* 0 */ 194, 276, 277, 278, 216, 194, 194, 217, 194, 194, + /* 10 */ 194, 194, 224, 194, 194, 276, 277, 278, 204, 19, + /* 20 */ 206, 202, 297, 217, 218, 205, 207, 217, 205, 217, + /* 30 */ 218, 31, 217, 218, 217, 218, 29, 217, 218, 39, + /* 40 */ 33, 217, 220, 43, 44, 45, 46, 47, 48, 49, + /* 50 */ 50, 51, 52, 53, 54, 55, 56, 57, 312, 19, + /* 60 */ 240, 241, 316, 240, 241, 194, 46, 47, 48, 49, + /* 70 */ 22, 254, 65, 253, 254, 255, 253, 194, 255, 194, + /* 80 */ 263, 258, 259, 43, 44, 45, 46, 47, 48, 49, + /* 90 */ 50, 51, 52, 53, 54, 55, 56, 57, 276, 277, + /* 100 */ 278, 285, 102, 103, 104, 105, 106, 107, 108, 109, + /* 110 */ 110, 111, 112, 113, 59, 186, 187, 188, 189, 190, + /* 120 */ 191, 310, 239, 317, 318, 196, 86, 198, 88, 317, + /* 130 */ 19, 319, 317, 318, 205, 264, 25, 211, 212, 213, + /* 140 */ 205, 121, 102, 103, 104, 105, 106, 107, 108, 109, /* 150 */ 110, 111, 112, 113, 43, 44, 45, 46, 47, 48, - /* 160 */ 49, 50, 51, 52, 53, 54, 55, 56, 57, 81, - /* 170 */ 292, 59, 292, 298, 108, 109, 110, 111, 112, 113, - /* 180 */ 69, 116, 117, 118, 72, 106, 107, 193, 111, 112, - /* 190 */ 113, 54, 55, 56, 57, 58, 102, 103, 104, 105, - /* 200 */ 106, 107, 108, 109, 110, 111, 112, 113, 120, 25, - /* 210 */ 216, 217, 145, 102, 103, 104, 105, 106, 107, 108, - /* 220 */ 109, 110, 111, 112, 113, 231, 138, 139, 116, 117, - /* 230 */ 118, 164, 153, 19, 155, 54, 55, 56, 57, 102, + /* 160 */ 49, 50, 51, 52, 53, 54, 55, 56, 57, 240, + /* 170 */ 241, 116, 117, 118, 119, 240, 241, 122, 123, 124, + /* 180 */ 69, 298, 253, 194, 255, 106, 107, 132, 253, 141, + /* 190 */ 255, 54, 55, 56, 57, 58, 207, 268, 102, 103, + /* 200 */ 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, + /* 210 */ 214, 128, 129, 102, 103, 104, 105, 106, 107, 108, + /* 220 */ 109, 110, 111, 112, 113, 134, 25, 136, 137, 300, + /* 230 */ 165, 166, 153, 19, 155, 54, 55, 56, 57, 102, /* 240 */ 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, - /* 250 */ 113, 128, 129, 46, 47, 48, 49, 43, 44, 45, + /* 250 */ 113, 108, 109, 110, 111, 112, 113, 43, 44, 45, /* 260 */ 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, - /* 270 */ 56, 57, 216, 193, 25, 59, 193, 19, 165, 166, - /* 280 */ 193, 67, 24, 102, 103, 104, 105, 106, 107, 108, - /* 290 */ 109, 110, 111, 112, 113, 73, 216, 217, 59, 216, - /* 300 */ 217, 43, 44, 45, 46, 47, 48, 49, 50, 51, + /* 270 */ 56, 57, 276, 277, 278, 113, 194, 19, 22, 23, + /* 280 */ 194, 67, 24, 102, 103, 104, 105, 106, 107, 108, + /* 290 */ 109, 110, 111, 112, 113, 220, 250, 59, 252, 217, + /* 300 */ 218, 43, 44, 45, 46, 47, 48, 49, 50, 51, /* 310 */ 52, 53, 54, 55, 56, 57, 102, 103, 104, 105, - /* 320 */ 106, 107, 108, 109, 110, 111, 112, 113, 121, 145, - /* 330 */ 59, 193, 116, 117, 118, 119, 273, 204, 122, 123, - /* 340 */ 124, 19, 20, 134, 22, 136, 137, 19, 132, 127, - /* 350 */ 128, 129, 24, 22, 23, 116, 117, 118, 36, 193, + /* 320 */ 106, 107, 108, 109, 110, 111, 112, 113, 106, 107, + /* 330 */ 108, 109, 110, 111, 112, 113, 254, 59, 205, 138, + /* 340 */ 139, 19, 20, 194, 22, 263, 22, 23, 231, 25, + /* 350 */ 72, 276, 277, 278, 116, 117, 118, 101, 36, 76, /* 360 */ 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, - /* 370 */ 112, 113, 239, 240, 311, 312, 215, 106, 107, 241, - /* 380 */ 19, 59, 216, 217, 223, 252, 115, 116, 117, 118, - /* 390 */ 151, 120, 26, 71, 193, 308, 309, 193, 149, 128, - /* 400 */ 313, 216, 269, 81, 43, 44, 45, 46, 47, 48, - /* 410 */ 49, 50, 51, 52, 53, 54, 55, 56, 57, 253, - /* 420 */ 216, 217, 100, 95, 153, 59, 155, 261, 106, 107, - /* 430 */ 25, 193, 101, 193, 193, 231, 114, 25, 116, 117, - /* 440 */ 118, 113, 304, 121, 193, 204, 59, 119, 120, 121, - /* 450 */ 122, 123, 124, 125, 216, 217, 193, 216, 217, 131, - /* 460 */ 138, 139, 230, 102, 103, 104, 105, 106, 107, 108, + /* 370 */ 112, 113, 89, 240, 241, 92, 73, 194, 194, 73, + /* 380 */ 19, 59, 188, 189, 190, 191, 253, 81, 255, 151, + /* 390 */ 196, 25, 198, 71, 116, 117, 118, 311, 312, 205, + /* 400 */ 217, 218, 316, 81, 43, 44, 45, 46, 47, 48, + /* 410 */ 49, 50, 51, 52, 53, 54, 55, 56, 57, 270, + /* 420 */ 22, 23, 100, 25, 59, 101, 138, 139, 106, 107, + /* 430 */ 127, 128, 129, 127, 240, 241, 114, 254, 116, 117, + /* 440 */ 118, 76, 76, 121, 138, 139, 263, 253, 264, 255, + /* 450 */ 205, 275, 87, 19, 89, 89, 194, 92, 92, 199, + /* 460 */ 138, 139, 268, 102, 103, 104, 105, 106, 107, 108, /* 470 */ 109, 110, 111, 112, 113, 153, 154, 155, 156, 157, - /* 480 */ 239, 240, 116, 117, 118, 76, 193, 23, 19, 25, - /* 490 */ 22, 253, 23, 252, 253, 108, 87, 204, 89, 261, - /* 500 */ 198, 92, 261, 116, 117, 118, 193, 306, 307, 216, - /* 510 */ 217, 150, 43, 44, 45, 46, 47, 48, 49, 50, - /* 520 */ 51, 52, 53, 54, 55, 56, 57, 59, 193, 216, - /* 530 */ 217, 19, 239, 240, 283, 23, 106, 107, 108, 109, - /* 540 */ 110, 111, 112, 113, 73, 252, 253, 142, 308, 309, - /* 550 */ 138, 139, 81, 313, 145, 43, 44, 45, 46, 47, + /* 480 */ 81, 116, 117, 118, 129, 240, 241, 224, 19, 226, + /* 490 */ 314, 315, 23, 25, 300, 59, 22, 234, 253, 101, + /* 500 */ 255, 236, 237, 26, 194, 183, 194, 152, 72, 22, + /* 510 */ 145, 150, 43, 44, 45, 46, 47, 48, 49, 50, + /* 520 */ 51, 52, 53, 54, 55, 56, 57, 217, 218, 217, + /* 530 */ 218, 19, 189, 59, 191, 23, 59, 138, 139, 196, + /* 540 */ 135, 198, 232, 283, 232, 140, 59, 287, 205, 275, + /* 550 */ 116, 205, 116, 117, 118, 43, 44, 45, 46, 47, /* 560 */ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, - /* 570 */ 307, 102, 103, 104, 105, 106, 107, 108, 109, 110, - /* 580 */ 111, 112, 113, 281, 116, 117, 118, 285, 23, 193, - /* 590 */ 25, 119, 59, 193, 122, 123, 124, 59, 127, 203, - /* 600 */ 59, 205, 19, 268, 132, 25, 23, 22, 193, 138, - /* 610 */ 139, 249, 204, 251, 102, 103, 104, 105, 106, 107, + /* 570 */ 194, 102, 103, 104, 105, 106, 107, 108, 109, 110, + /* 580 */ 111, 112, 113, 240, 241, 194, 240, 241, 314, 315, + /* 590 */ 116, 117, 118, 116, 117, 118, 253, 194, 255, 253, + /* 600 */ 59, 255, 19, 116, 117, 118, 23, 22, 217, 218, + /* 610 */ 142, 268, 205, 275, 102, 103, 104, 105, 106, 107, /* 620 */ 108, 109, 110, 111, 112, 113, 43, 44, 45, 46, /* 630 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, - /* 640 */ 57, 19, 22, 23, 59, 23, 25, 239, 240, 116, - /* 650 */ 117, 118, 193, 11, 116, 117, 118, 116, 117, 118, - /* 660 */ 252, 269, 22, 193, 15, 43, 44, 45, 46, 47, + /* 640 */ 57, 19, 194, 300, 59, 23, 119, 240, 241, 122, + /* 650 */ 123, 124, 314, 315, 194, 236, 237, 194, 117, 132, + /* 660 */ 253, 81, 255, 205, 59, 43, 44, 45, 46, 47, /* 670 */ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, - /* 680 */ 273, 143, 193, 118, 143, 102, 103, 104, 105, 106, - /* 690 */ 107, 108, 109, 110, 111, 112, 113, 76, 118, 59, - /* 700 */ 241, 116, 117, 118, 304, 216, 217, 292, 143, 60, - /* 710 */ 89, 241, 19, 92, 193, 193, 23, 22, 311, 312, - /* 720 */ 231, 101, 22, 143, 102, 103, 104, 105, 106, 107, + /* 680 */ 217, 218, 194, 194, 194, 102, 103, 104, 105, 106, + /* 690 */ 107, 108, 109, 110, 111, 112, 113, 294, 240, 241, + /* 700 */ 120, 116, 117, 118, 59, 194, 217, 218, 211, 212, + /* 710 */ 213, 253, 19, 255, 194, 19, 23, 254, 138, 139, + /* 720 */ 24, 232, 117, 194, 102, 103, 104, 105, 106, 107, /* 730 */ 108, 109, 110, 111, 112, 113, 43, 44, 45, 46, /* 740 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, - /* 750 */ 57, 19, 193, 193, 59, 23, 116, 117, 118, 59, - /* 760 */ 201, 21, 241, 304, 193, 206, 127, 128, 129, 193, - /* 770 */ 128, 129, 235, 236, 304, 43, 44, 45, 46, 47, + /* 750 */ 57, 19, 264, 108, 76, 23, 127, 128, 129, 311, + /* 760 */ 312, 116, 117, 118, 316, 87, 306, 89, 308, 194, + /* 770 */ 92, 22, 59, 194, 22, 43, 44, 45, 46, 47, /* 780 */ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, - /* 790 */ 22, 193, 216, 217, 193, 102, 103, 104, 105, 106, - /* 800 */ 107, 108, 109, 110, 111, 112, 113, 231, 193, 193, - /* 810 */ 193, 116, 117, 118, 216, 217, 116, 117, 118, 226, - /* 820 */ 80, 193, 19, 235, 236, 304, 23, 211, 212, 231, - /* 830 */ 204, 216, 217, 205, 102, 103, 104, 105, 106, 107, + /* 790 */ 194, 95, 217, 218, 265, 102, 103, 104, 105, 106, + /* 800 */ 107, 108, 109, 110, 111, 112, 113, 232, 59, 113, + /* 810 */ 25, 59, 194, 217, 218, 119, 120, 121, 122, 123, + /* 820 */ 124, 125, 19, 145, 194, 194, 23, 131, 232, 116, + /* 830 */ 117, 118, 35, 194, 102, 103, 104, 105, 106, 107, /* 840 */ 108, 109, 110, 111, 112, 113, 43, 44, 45, 46, /* 850 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, - /* 860 */ 57, 19, 193, 123, 76, 239, 240, 193, 253, 239, - /* 870 */ 240, 239, 240, 244, 106, 107, 193, 89, 252, 193, - /* 880 */ 92, 59, 252, 254, 252, 43, 44, 45, 46, 47, + /* 860 */ 57, 19, 194, 66, 194, 116, 117, 118, 116, 117, + /* 870 */ 118, 74, 242, 294, 194, 194, 206, 23, 194, 25, + /* 880 */ 194, 111, 112, 113, 25, 43, 44, 45, 46, 47, /* 890 */ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, - /* 900 */ 284, 161, 216, 217, 193, 102, 103, 104, 105, 106, - /* 910 */ 107, 108, 109, 110, 111, 112, 113, 231, 193, 244, - /* 920 */ 187, 188, 189, 190, 7, 8, 9, 309, 195, 254, - /* 930 */ 197, 313, 19, 127, 128, 129, 262, 204, 22, 117, - /* 940 */ 24, 216, 217, 273, 102, 103, 104, 105, 106, 107, + /* 900 */ 24, 194, 194, 217, 218, 102, 103, 104, 105, 106, + /* 910 */ 107, 108, 109, 110, 111, 112, 113, 241, 232, 194, + /* 920 */ 212, 213, 242, 242, 217, 218, 242, 130, 11, 253, + /* 930 */ 194, 255, 19, 265, 149, 59, 306, 194, 308, 232, + /* 940 */ 309, 310, 217, 218, 102, 103, 104, 105, 106, 107, /* 950 */ 108, 109, 110, 111, 112, 113, 43, 44, 45, 46, /* 960 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, - /* 970 */ 57, 193, 239, 240, 193, 59, 19, 188, 253, 190, - /* 980 */ 193, 311, 312, 16, 195, 252, 197, 193, 19, 301, - /* 990 */ 302, 135, 193, 204, 216, 217, 140, 216, 217, 266, - /* 1000 */ 204, 159, 45, 46, 47, 48, 49, 50, 51, 52, + /* 970 */ 57, 194, 194, 59, 194, 239, 19, 194, 25, 254, + /* 980 */ 303, 304, 23, 194, 25, 126, 306, 306, 308, 308, + /* 990 */ 306, 271, 308, 117, 286, 217, 218, 217, 218, 194, + /* 1000 */ 194, 159, 45, 46, 47, 48, 49, 50, 51, 52, /* 1010 */ 53, 54, 55, 56, 57, 102, 103, 104, 105, 106, - /* 1020 */ 107, 108, 109, 110, 111, 112, 113, 12, 239, 240, - /* 1030 */ 193, 298, 238, 117, 253, 239, 240, 238, 259, 260, - /* 1040 */ 193, 252, 27, 193, 77, 193, 79, 204, 252, 262, - /* 1050 */ 193, 299, 300, 193, 100, 266, 278, 42, 204, 102, + /* 1020 */ 107, 108, 109, 110, 111, 112, 113, 59, 239, 194, + /* 1030 */ 116, 117, 118, 260, 254, 194, 240, 241, 194, 233, + /* 1040 */ 205, 240, 241, 205, 239, 128, 129, 270, 265, 253, + /* 1050 */ 194, 255, 217, 218, 253, 194, 255, 143, 280, 102, /* 1060 */ 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, - /* 1070 */ 113, 117, 159, 216, 217, 121, 216, 217, 63, 193, - /* 1080 */ 193, 193, 239, 240, 115, 116, 193, 298, 73, 240, - /* 1090 */ 238, 231, 19, 239, 240, 252, 22, 24, 211, 212, - /* 1100 */ 263, 252, 216, 217, 216, 217, 252, 153, 154, 155, - /* 1110 */ 253, 193, 19, 144, 213, 268, 43, 44, 45, 46, + /* 1070 */ 113, 118, 159, 217, 218, 240, 241, 118, 240, 241, + /* 1080 */ 194, 194, 194, 239, 116, 117, 118, 22, 253, 254, + /* 1090 */ 255, 253, 19, 255, 233, 194, 143, 24, 263, 212, + /* 1100 */ 213, 194, 143, 217, 218, 217, 218, 261, 262, 271, + /* 1110 */ 254, 143, 19, 7, 8, 9, 43, 44, 45, 46, /* 1120 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, - /* 1130 */ 57, 193, 19, 59, 216, 217, 43, 44, 45, 46, + /* 1130 */ 57, 16, 19, 22, 23, 294, 43, 44, 45, 46, /* 1140 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, - /* 1150 */ 57, 193, 19, 24, 216, 217, 43, 44, 45, 46, + /* 1150 */ 57, 312, 194, 214, 21, 316, 43, 44, 45, 46, /* 1160 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, - /* 1170 */ 57, 284, 193, 208, 209, 102, 103, 104, 105, 106, - /* 1180 */ 107, 108, 109, 110, 111, 112, 113, 286, 59, 193, - /* 1190 */ 232, 117, 291, 193, 193, 102, 103, 104, 105, 106, - /* 1200 */ 107, 108, 109, 110, 111, 112, 113, 193, 204, 22, - /* 1210 */ 23, 193, 25, 66, 193, 102, 103, 104, 105, 106, - /* 1220 */ 107, 108, 109, 110, 111, 112, 113, 193, 193, 193, - /* 1230 */ 216, 217, 85, 193, 238, 19, 16, 216, 217, 238, - /* 1240 */ 193, 94, 193, 239, 240, 231, 117, 268, 35, 116, - /* 1250 */ 216, 217, 216, 217, 22, 23, 252, 25, 208, 209, + /* 1170 */ 57, 106, 107, 286, 194, 102, 103, 104, 105, 106, + /* 1180 */ 107, 108, 109, 110, 111, 112, 113, 207, 158, 59, + /* 1190 */ 160, 22, 77, 24, 79, 102, 103, 104, 105, 106, + /* 1200 */ 107, 108, 109, 110, 111, 112, 113, 194, 194, 229, + /* 1210 */ 194, 231, 101, 80, 22, 102, 103, 104, 105, 106, + /* 1220 */ 107, 108, 109, 110, 111, 112, 113, 288, 59, 12, + /* 1230 */ 217, 218, 293, 217, 218, 19, 106, 107, 59, 19, + /* 1240 */ 16, 127, 128, 129, 27, 115, 116, 117, 118, 194, + /* 1250 */ 120, 59, 22, 194, 24, 194, 123, 100, 128, 42, /* 1260 */ 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, - /* 1270 */ 54, 55, 56, 57, 193, 193, 19, 5, 59, 66, - /* 1280 */ 193, 263, 10, 11, 12, 13, 14, 74, 101, 17, - /* 1290 */ 193, 46, 193, 146, 193, 76, 213, 77, 263, 79, - /* 1300 */ 12, 260, 30, 46, 32, 264, 87, 193, 89, 29, - /* 1310 */ 263, 92, 40, 33, 232, 27, 193, 108, 102, 103, + /* 1270 */ 54, 55, 56, 57, 117, 194, 217, 218, 121, 100, + /* 1280 */ 63, 194, 245, 153, 194, 155, 117, 19, 115, 194, + /* 1290 */ 73, 214, 194, 256, 161, 116, 117, 194, 217, 218, + /* 1300 */ 121, 77, 194, 79, 217, 218, 194, 217, 218, 117, + /* 1310 */ 153, 154, 155, 254, 46, 217, 218, 144, 102, 103, /* 1320 */ 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, - /* 1330 */ 42, 138, 139, 101, 193, 116, 117, 118, 19, 20, - /* 1340 */ 255, 22, 70, 130, 135, 65, 256, 257, 193, 140, - /* 1350 */ 78, 63, 193, 81, 193, 36, 193, 216, 217, 193, - /* 1360 */ 115, 193, 263, 193, 145, 268, 59, 48, 193, 193, - /* 1370 */ 98, 193, 115, 193, 291, 216, 217, 193, 59, 216, - /* 1380 */ 217, 161, 216, 217, 216, 217, 216, 217, 131, 193, - /* 1390 */ 71, 193, 216, 217, 216, 217, 216, 217, 193, 260, - /* 1400 */ 216, 217, 19, 264, 85, 133, 244, 100, 193, 90, - /* 1410 */ 138, 139, 216, 217, 216, 217, 254, 244, 193, 100, - /* 1420 */ 193, 216, 217, 116, 117, 106, 107, 254, 121, 193, - /* 1430 */ 115, 216, 217, 114, 162, 116, 117, 118, 115, 244, - /* 1440 */ 121, 216, 217, 216, 217, 193, 309, 193, 31, 254, - /* 1450 */ 313, 309, 216, 217, 309, 313, 39, 193, 313, 309, - /* 1460 */ 153, 154, 155, 313, 193, 150, 25, 144, 216, 217, - /* 1470 */ 216, 217, 153, 154, 155, 156, 157, 0, 1, 2, - /* 1480 */ 216, 217, 5, 149, 150, 22, 193, 10, 11, 12, - /* 1490 */ 13, 14, 193, 158, 17, 160, 193, 19, 20, 116, - /* 1500 */ 22, 25, 193, 24, 22, 193, 24, 30, 226, 32, - /* 1510 */ 19, 20, 226, 22, 36, 193, 53, 40, 193, 216, - /* 1520 */ 217, 193, 23, 193, 25, 216, 217, 36, 216, 217, - /* 1530 */ 193, 99, 193, 193, 22, 193, 193, 59, 216, 217, - /* 1540 */ 193, 216, 217, 193, 216, 217, 193, 70, 129, 71, - /* 1550 */ 59, 129, 193, 216, 217, 78, 216, 217, 81, 216, - /* 1560 */ 217, 193, 71, 85, 193, 133, 193, 126, 90, 216, - /* 1570 */ 217, 152, 258, 61, 152, 98, 85, 193, 100, 193, - /* 1580 */ 23, 90, 25, 121, 106, 107, 23, 216, 217, 216, - /* 1590 */ 217, 100, 114, 131, 116, 117, 118, 106, 107, 121, - /* 1600 */ 216, 217, 216, 217, 193, 114, 193, 116, 117, 118, - /* 1610 */ 133, 22, 121, 193, 59, 138, 139, 193, 142, 193, - /* 1620 */ 141, 23, 23, 25, 25, 120, 121, 216, 217, 216, - /* 1630 */ 217, 153, 154, 155, 156, 157, 216, 217, 19, 162, - /* 1640 */ 216, 217, 216, 217, 153, 154, 155, 156, 157, 1, - /* 1650 */ 2, 193, 59, 5, 19, 20, 318, 22, 10, 11, - /* 1660 */ 12, 13, 14, 193, 59, 17, 193, 23, 23, 25, - /* 1670 */ 25, 36, 117, 193, 216, 217, 193, 23, 30, 25, - /* 1680 */ 32, 19, 20, 23, 22, 25, 216, 217, 40, 216, - /* 1690 */ 217, 7, 8, 23, 59, 25, 83, 84, 36, 23, - /* 1700 */ 193, 25, 23, 23, 25, 25, 71, 153, 145, 155, - /* 1710 */ 117, 153, 23, 155, 25, 23, 97, 25, 70, 193, - /* 1720 */ 193, 59, 117, 236, 193, 193, 78, 193, 193, 81, - /* 1730 */ 141, 193, 193, 71, 193, 100, 288, 287, 242, 255, - /* 1740 */ 255, 106, 107, 108, 255, 255, 98, 243, 297, 114, - /* 1750 */ 214, 116, 117, 118, 245, 191, 121, 271, 293, 267, - /* 1760 */ 267, 246, 100, 246, 245, 271, 271, 293, 106, 107, - /* 1770 */ 220, 271, 229, 225, 249, 219, 114, 259, 116, 117, - /* 1780 */ 118, 133, 259, 121, 219, 219, 138, 139, 153, 154, - /* 1790 */ 155, 156, 157, 280, 249, 243, 19, 20, 245, 22, - /* 1800 */ 196, 259, 140, 259, 60, 297, 141, 297, 200, 200, - /* 1810 */ 162, 38, 200, 36, 294, 153, 154, 155, 156, 157, - /* 1820 */ 151, 150, 294, 283, 22, 43, 234, 18, 237, 200, - /* 1830 */ 270, 272, 237, 237, 237, 18, 59, 199, 270, 149, - /* 1840 */ 246, 272, 272, 200, 234, 234, 246, 246, 71, 246, - /* 1850 */ 199, 158, 290, 62, 22, 200, 19, 20, 199, 22, - /* 1860 */ 289, 221, 221, 200, 200, 199, 199, 115, 218, 64, - /* 1870 */ 218, 218, 22, 36, 227, 126, 227, 100, 165, 221, - /* 1880 */ 224, 224, 24, 106, 107, 312, 218, 305, 113, 282, - /* 1890 */ 91, 114, 220, 116, 117, 118, 59, 282, 121, 218, - /* 1900 */ 218, 218, 200, 317, 317, 82, 221, 265, 71, 148, - /* 1910 */ 145, 265, 22, 277, 200, 158, 279, 140, 147, 25, - /* 1920 */ 146, 202, 248, 250, 249, 247, 13, 250, 194, 194, - /* 1930 */ 153, 154, 155, 156, 157, 6, 303, 100, 192, 192, - /* 1940 */ 246, 213, 192, 106, 107, 207, 213, 207, 222, 213, - /* 1950 */ 213, 114, 222, 116, 117, 118, 214, 214, 121, 4, - /* 1960 */ 207, 213, 3, 22, 303, 15, 163, 16, 23, 23, - /* 1970 */ 139, 151, 130, 25, 20, 142, 24, 16, 144, 1, - /* 1980 */ 142, 130, 130, 61, 37, 53, 300, 151, 53, 53, - /* 1990 */ 153, 154, 155, 156, 157, 53, 130, 116, 34, 1, - /* 2000 */ 141, 5, 22, 115, 161, 68, 25, 68, 75, 41, - /* 2010 */ 141, 115, 24, 20, 19, 131, 125, 23, 28, 22, - /* 2020 */ 67, 22, 22, 22, 67, 59, 24, 96, 22, 67, - /* 2030 */ 23, 149, 22, 25, 23, 23, 23, 22, 34, 141, - /* 2040 */ 37, 97, 23, 23, 116, 22, 143, 25, 34, 75, - /* 2050 */ 34, 34, 34, 88, 75, 34, 86, 23, 22, 34, - /* 2060 */ 93, 24, 34, 25, 25, 142, 142, 23, 44, 23, - /* 2070 */ 23, 23, 23, 11, 23, 25, 22, 22, 22, 141, - /* 2080 */ 23, 23, 22, 22, 25, 15, 1, 23, 25, 1, - /* 2090 */ 141, 135, 319, 319, 319, 319, 319, 319, 319, 141, - /* 2100 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, - /* 2110 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, - /* 2120 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, - /* 2130 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, - /* 2140 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, - /* 2150 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, - /* 2160 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, - /* 2170 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, - /* 2180 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, - /* 2190 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, - /* 2200 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, - /* 2210 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, - /* 2220 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, - /* 2230 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, - /* 2240 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, - /* 2250 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, - /* 2260 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, - /* 2270 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, - /* 2280 */ 319, 319, 319, 319, 319, + /* 1330 */ 232, 270, 153, 154, 155, 115, 116, 66, 19, 20, + /* 1340 */ 183, 22, 12, 312, 254, 194, 262, 316, 209, 210, + /* 1350 */ 266, 239, 194, 194, 108, 36, 85, 27, 19, 20, + /* 1360 */ 265, 22, 183, 245, 144, 94, 25, 48, 217, 218, + /* 1370 */ 293, 194, 42, 270, 256, 36, 217, 218, 59, 194, + /* 1380 */ 25, 135, 194, 115, 194, 161, 140, 194, 194, 15, + /* 1390 */ 71, 194, 312, 63, 217, 218, 316, 194, 59, 131, + /* 1400 */ 301, 302, 217, 218, 85, 217, 218, 217, 218, 90, + /* 1410 */ 71, 217, 218, 19, 217, 218, 245, 146, 262, 100, + /* 1420 */ 217, 218, 266, 265, 85, 106, 107, 256, 312, 90, + /* 1430 */ 209, 210, 316, 114, 60, 116, 117, 118, 194, 100, + /* 1440 */ 121, 194, 194, 145, 115, 106, 107, 19, 46, 19, + /* 1450 */ 20, 24, 22, 114, 194, 116, 117, 118, 194, 245, + /* 1460 */ 121, 194, 164, 194, 217, 218, 36, 194, 258, 259, + /* 1470 */ 256, 194, 153, 154, 155, 156, 157, 217, 218, 150, + /* 1480 */ 31, 217, 218, 142, 217, 218, 217, 218, 39, 59, + /* 1490 */ 217, 218, 153, 154, 155, 156, 157, 149, 150, 5, + /* 1500 */ 145, 71, 183, 245, 10, 11, 12, 13, 14, 194, + /* 1510 */ 116, 17, 129, 227, 256, 85, 194, 115, 194, 23, + /* 1520 */ 90, 25, 183, 99, 30, 97, 32, 22, 22, 194, + /* 1530 */ 100, 194, 217, 218, 40, 152, 106, 107, 23, 217, + /* 1540 */ 218, 194, 19, 20, 114, 22, 116, 117, 118, 257, + /* 1550 */ 194, 121, 217, 218, 217, 218, 194, 133, 53, 36, + /* 1560 */ 23, 23, 25, 25, 70, 120, 121, 61, 141, 7, + /* 1570 */ 8, 121, 78, 217, 218, 81, 23, 227, 25, 217, + /* 1580 */ 218, 131, 59, 153, 154, 155, 156, 157, 0, 1, + /* 1590 */ 2, 59, 98, 5, 71, 23, 227, 25, 10, 11, + /* 1600 */ 12, 13, 14, 83, 84, 17, 23, 23, 25, 25, + /* 1610 */ 59, 194, 194, 183, 23, 23, 25, 25, 30, 194, + /* 1620 */ 32, 19, 20, 100, 22, 194, 194, 133, 40, 106, + /* 1630 */ 107, 108, 138, 139, 194, 217, 218, 114, 36, 116, + /* 1640 */ 117, 118, 217, 218, 121, 194, 194, 194, 23, 117, + /* 1650 */ 25, 194, 23, 23, 25, 25, 162, 194, 70, 194, + /* 1660 */ 145, 59, 23, 153, 25, 155, 78, 194, 117, 81, + /* 1670 */ 217, 218, 194, 71, 217, 218, 153, 154, 155, 156, + /* 1680 */ 157, 194, 217, 218, 194, 23, 98, 25, 321, 194, + /* 1690 */ 217, 218, 194, 19, 20, 194, 22, 153, 23, 155, + /* 1700 */ 25, 194, 100, 194, 217, 218, 183, 194, 106, 107, + /* 1710 */ 36, 194, 217, 218, 237, 194, 114, 243, 116, 117, + /* 1720 */ 118, 133, 194, 121, 217, 218, 138, 139, 194, 194, + /* 1730 */ 194, 290, 289, 59, 217, 218, 194, 194, 217, 218, + /* 1740 */ 194, 194, 140, 194, 194, 71, 194, 244, 194, 194, + /* 1750 */ 162, 217, 218, 194, 194, 153, 154, 155, 156, 157, + /* 1760 */ 217, 218, 194, 217, 218, 194, 217, 218, 257, 217, + /* 1770 */ 218, 217, 218, 257, 100, 194, 257, 217, 218, 257, + /* 1780 */ 106, 107, 215, 299, 194, 183, 192, 194, 114, 194, + /* 1790 */ 116, 117, 118, 1, 2, 121, 221, 5, 217, 218, + /* 1800 */ 273, 197, 10, 11, 12, 13, 14, 217, 218, 17, + /* 1810 */ 217, 218, 217, 218, 140, 194, 246, 194, 273, 295, + /* 1820 */ 247, 273, 30, 247, 32, 269, 269, 153, 154, 155, + /* 1830 */ 156, 157, 40, 246, 273, 295, 230, 226, 217, 218, + /* 1840 */ 217, 218, 220, 261, 220, 282, 220, 19, 20, 244, + /* 1850 */ 22, 250, 141, 250, 246, 60, 201, 183, 261, 261, + /* 1860 */ 261, 201, 70, 299, 36, 299, 201, 38, 151, 150, + /* 1870 */ 78, 285, 22, 81, 296, 296, 43, 235, 18, 238, + /* 1880 */ 201, 274, 272, 238, 238, 238, 18, 59, 200, 149, + /* 1890 */ 98, 247, 274, 274, 235, 247, 247, 247, 235, 71, + /* 1900 */ 272, 201, 200, 158, 292, 62, 291, 201, 200, 22, + /* 1910 */ 201, 222, 200, 222, 201, 200, 115, 219, 219, 64, + /* 1920 */ 219, 228, 22, 126, 221, 133, 165, 222, 100, 225, + /* 1930 */ 138, 139, 225, 219, 106, 107, 24, 219, 228, 219, + /* 1940 */ 219, 307, 114, 113, 116, 117, 118, 315, 284, 121, + /* 1950 */ 284, 222, 201, 91, 162, 320, 320, 82, 148, 267, + /* 1960 */ 145, 267, 22, 279, 201, 158, 281, 251, 147, 146, + /* 1970 */ 25, 203, 250, 249, 251, 248, 13, 247, 195, 195, + /* 1980 */ 6, 153, 154, 155, 156, 157, 193, 193, 305, 193, + /* 1990 */ 208, 305, 302, 214, 214, 214, 208, 223, 223, 214, + /* 2000 */ 4, 215, 215, 214, 3, 22, 208, 163, 15, 23, + /* 2010 */ 16, 183, 23, 139, 151, 130, 25, 20, 142, 24, + /* 2020 */ 16, 144, 1, 142, 130, 130, 61, 37, 53, 151, + /* 2030 */ 53, 53, 53, 130, 116, 1, 34, 141, 5, 22, + /* 2040 */ 115, 161, 75, 25, 68, 141, 41, 115, 68, 24, + /* 2050 */ 20, 19, 131, 125, 67, 67, 96, 22, 22, 22, + /* 2060 */ 37, 23, 22, 24, 22, 59, 67, 23, 149, 28, + /* 2070 */ 22, 25, 23, 23, 23, 22, 141, 34, 97, 23, + /* 2080 */ 23, 34, 116, 22, 143, 25, 34, 75, 34, 34, + /* 2090 */ 75, 88, 34, 86, 23, 22, 34, 25, 24, 34, + /* 2100 */ 25, 93, 23, 44, 142, 23, 142, 23, 23, 22, + /* 2110 */ 11, 25, 23, 25, 23, 22, 22, 22, 1, 23, + /* 2120 */ 23, 23, 22, 22, 15, 141, 141, 25, 25, 1, + /* 2130 */ 322, 322, 322, 135, 322, 322, 322, 322, 322, 322, + /* 2140 */ 322, 141, 322, 322, 322, 322, 322, 322, 322, 322, + /* 2150 */ 322, 322, 322, 322, 322, 322, 322, 322, 322, 322, + /* 2160 */ 322, 322, 322, 322, 322, 322, 322, 322, 322, 322, + /* 2170 */ 322, 322, 322, 322, 322, 322, 322, 322, 322, 322, + /* 2180 */ 322, 322, 322, 322, 322, 322, 322, 322, 322, 322, + /* 2190 */ 322, 322, 322, 322, 322, 322, 322, 322, 322, 322, + /* 2200 */ 322, 322, 322, 322, 322, 322, 322, 322, 322, 322, + /* 2210 */ 322, 322, 322, 322, 322, 322, 322, 322, 322, 322, + /* 2220 */ 322, 322, 322, 322, 322, 322, 322, 322, 322, 322, + /* 2230 */ 322, 322, 322, 322, 322, 322, 322, 322, 322, 322, + /* 2240 */ 322, 322, 322, 322, 322, 322, 322, 322, 322, 322, + /* 2250 */ 322, 322, 322, 322, 322, 322, 322, 322, 322, 322, + /* 2260 */ 322, 322, 322, 322, 322, 322, 322, 322, 322, 322, + /* 2270 */ 322, 322, 322, 322, 322, 322, 322, 322, 322, 322, + /* 2280 */ 322, 322, 322, 322, 322, 322, 322, 322, 322, 322, + /* 2290 */ 322, 322, 322, 322, 322, 322, 322, 322, 322, 322, + /* 2300 */ 322, 322, 322, 322, 322, 322, 322, 322, 322, 322, + /* 2310 */ 322, 322, 322, 322, 322, 322, 322, 322, 322, 322, + /* 2320 */ 322, 322, 322, 322, 322, 322, 322, 322, }; -#define YY_SHIFT_COUNT (578) +#define YY_SHIFT_COUNT (582) #define YY_SHIFT_MIN (0) -#define YY_SHIFT_MAX (2088) +#define YY_SHIFT_MAX (2128) static const unsigned short int yy_shift_ofst[] = { - /* 0 */ 1648, 1477, 1272, 322, 322, 1, 1319, 1478, 1491, 1837, - /* 10 */ 1837, 1837, 471, 0, 0, 214, 1093, 1837, 1837, 1837, - /* 20 */ 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, - /* 30 */ 1837, 271, 271, 1219, 1219, 216, 88, 1, 1, 1, - /* 40 */ 1, 1, 40, 111, 258, 361, 469, 512, 583, 622, - /* 50 */ 693, 732, 803, 842, 913, 1073, 1093, 1093, 1093, 1093, - /* 60 */ 1093, 1093, 1093, 1093, 1093, 1093, 1093, 1093, 1093, 1093, - /* 70 */ 1093, 1093, 1093, 1093, 1113, 1093, 1216, 957, 957, 1635, - /* 80 */ 1662, 1777, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, - /* 90 */ 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, - /* 100 */ 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, - /* 110 */ 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, - /* 120 */ 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, - /* 130 */ 1837, 137, 181, 181, 181, 181, 181, 181, 181, 94, - /* 140 */ 430, 66, 65, 112, 366, 533, 533, 740, 1257, 533, - /* 150 */ 533, 79, 79, 533, 412, 412, 412, 77, 412, 123, - /* 160 */ 113, 113, 113, 22, 22, 2100, 2100, 328, 328, 328, - /* 170 */ 239, 468, 468, 468, 468, 1015, 1015, 409, 366, 1187, - /* 180 */ 1232, 533, 533, 533, 533, 533, 533, 533, 533, 533, - /* 190 */ 533, 533, 533, 533, 533, 533, 533, 533, 533, 533, - /* 200 */ 533, 969, 621, 621, 533, 642, 788, 788, 1133, 1133, - /* 210 */ 822, 822, 67, 1193, 2100, 2100, 2100, 2100, 2100, 2100, - /* 220 */ 2100, 1307, 954, 954, 585, 472, 640, 387, 695, 538, - /* 230 */ 541, 700, 533, 533, 533, 533, 533, 533, 533, 533, - /* 240 */ 533, 533, 222, 533, 533, 533, 533, 533, 533, 533, - /* 250 */ 533, 533, 533, 533, 533, 1213, 1213, 1213, 533, 533, - /* 260 */ 533, 565, 533, 533, 533, 916, 1147, 533, 533, 1288, - /* 270 */ 533, 533, 533, 533, 533, 533, 533, 533, 639, 1280, - /* 280 */ 209, 1129, 1129, 1129, 1129, 580, 209, 209, 1209, 768, - /* 290 */ 917, 649, 1315, 1334, 405, 1334, 1383, 249, 1315, 1315, - /* 300 */ 249, 1315, 405, 1383, 1441, 464, 1245, 1417, 1417, 1417, - /* 310 */ 1323, 1323, 1323, 1323, 184, 184, 1335, 1476, 856, 1482, - /* 320 */ 1744, 1744, 1665, 1665, 1773, 1773, 1665, 1669, 1671, 1802, - /* 330 */ 1782, 1809, 1809, 1809, 1809, 1665, 1817, 1690, 1671, 1671, - /* 340 */ 1690, 1802, 1782, 1690, 1782, 1690, 1665, 1817, 1693, 1791, - /* 350 */ 1665, 1817, 1832, 1665, 1817, 1665, 1817, 1832, 1752, 1752, - /* 360 */ 1752, 1805, 1850, 1850, 1832, 1752, 1749, 1752, 1805, 1752, - /* 370 */ 1752, 1713, 1858, 1775, 1775, 1832, 1665, 1799, 1799, 1823, - /* 380 */ 1823, 1761, 1765, 1890, 1665, 1757, 1761, 1771, 1774, 1690, - /* 390 */ 1894, 1913, 1913, 1929, 1929, 1929, 2100, 2100, 2100, 2100, - /* 400 */ 2100, 2100, 2100, 2100, 2100, 2100, 2100, 2100, 2100, 2100, - /* 410 */ 2100, 207, 1220, 331, 620, 967, 806, 1074, 1499, 1432, - /* 420 */ 1463, 1479, 1419, 1422, 1557, 1512, 1598, 1599, 1644, 1645, - /* 430 */ 1654, 1660, 1555, 1505, 1684, 1462, 1670, 1563, 1619, 1593, - /* 440 */ 1676, 1679, 1613, 1680, 1554, 1558, 1689, 1692, 1605, 1589, - /* 450 */ 1955, 1959, 1941, 1803, 1950, 1951, 1945, 1946, 1831, 1820, - /* 460 */ 1842, 1948, 1948, 1952, 1833, 1954, 1834, 1961, 1978, 1838, - /* 470 */ 1851, 1948, 1852, 1922, 1947, 1948, 1836, 1932, 1935, 1936, - /* 480 */ 1942, 1866, 1881, 1964, 1859, 1998, 1996, 1980, 1888, 1843, - /* 490 */ 1937, 1981, 1939, 1933, 1968, 1869, 1896, 1988, 1993, 1995, - /* 500 */ 1884, 1891, 1997, 1953, 1999, 2000, 1994, 2001, 1957, 1966, - /* 510 */ 2002, 1931, 1990, 2006, 1962, 2003, 2007, 2004, 1882, 2010, - /* 520 */ 2011, 2012, 2008, 2013, 2015, 1944, 1898, 2019, 2020, 1928, - /* 530 */ 2014, 2023, 1903, 2022, 2016, 2017, 2018, 2021, 1965, 1974, - /* 540 */ 1970, 2024, 1979, 1967, 2025, 2034, 2036, 2037, 2038, 2039, - /* 550 */ 2028, 1923, 1924, 2044, 2022, 2046, 2047, 2048, 2049, 2050, - /* 560 */ 2051, 2054, 2062, 2055, 2056, 2057, 2058, 2060, 2061, 2059, - /* 570 */ 1956, 1938, 1949, 1958, 2063, 2064, 2070, 2085, 2088, + /* 0 */ 1792, 1588, 1494, 322, 322, 399, 306, 1319, 1339, 1430, + /* 10 */ 1828, 1828, 1828, 580, 399, 399, 399, 399, 399, 0, + /* 20 */ 0, 214, 1093, 1828, 1828, 1828, 1828, 1828, 1828, 1828, + /* 30 */ 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1130, 1130, + /* 40 */ 365, 365, 55, 278, 436, 713, 713, 201, 201, 201, + /* 50 */ 201, 40, 111, 258, 361, 469, 512, 583, 622, 693, + /* 60 */ 732, 803, 842, 913, 1073, 1093, 1093, 1093, 1093, 1093, + /* 70 */ 1093, 1093, 1093, 1093, 1093, 1093, 1093, 1093, 1093, 1093, + /* 80 */ 1093, 1093, 1093, 1113, 1093, 1216, 957, 957, 1523, 1602, + /* 90 */ 1674, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, + /* 100 */ 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, + /* 110 */ 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, + /* 120 */ 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, + /* 130 */ 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, + /* 140 */ 137, 181, 181, 181, 181, 181, 181, 181, 96, 222, + /* 150 */ 143, 477, 713, 1133, 1268, 713, 713, 79, 79, 713, + /* 160 */ 770, 83, 65, 65, 65, 288, 162, 162, 2142, 2142, + /* 170 */ 696, 696, 696, 238, 474, 474, 474, 474, 1217, 1217, + /* 180 */ 678, 477, 324, 398, 713, 713, 713, 713, 713, 713, + /* 190 */ 713, 713, 713, 713, 713, 713, 713, 713, 713, 713, + /* 200 */ 713, 713, 713, 1220, 366, 366, 713, 917, 283, 283, + /* 210 */ 434, 434, 605, 605, 1298, 2142, 2142, 2142, 2142, 2142, + /* 220 */ 2142, 2142, 1179, 1157, 1157, 487, 527, 585, 645, 749, + /* 230 */ 914, 968, 752, 713, 713, 713, 713, 713, 713, 713, + /* 240 */ 713, 713, 713, 303, 713, 713, 713, 713, 713, 713, + /* 250 */ 713, 713, 713, 713, 713, 713, 797, 797, 797, 713, + /* 260 */ 713, 713, 959, 713, 713, 713, 1169, 1271, 713, 713, + /* 270 */ 1330, 713, 713, 713, 713, 713, 713, 713, 713, 629, + /* 280 */ 7, 91, 876, 876, 876, 876, 953, 91, 91, 1246, + /* 290 */ 1065, 1106, 1374, 1329, 1348, 468, 1348, 1394, 785, 1329, + /* 300 */ 1329, 785, 1329, 468, 1394, 859, 854, 1402, 1449, 1449, + /* 310 */ 1449, 1173, 1173, 1173, 1173, 1355, 1355, 1030, 1341, 405, + /* 320 */ 1230, 1795, 1795, 1711, 1711, 1829, 1829, 1711, 1717, 1719, + /* 330 */ 1850, 1833, 1860, 1860, 1860, 1860, 1711, 1868, 1740, 1719, + /* 340 */ 1719, 1740, 1850, 1833, 1740, 1833, 1740, 1711, 1868, 1745, + /* 350 */ 1843, 1711, 1868, 1887, 1711, 1868, 1711, 1868, 1887, 1801, + /* 360 */ 1801, 1801, 1855, 1900, 1900, 1887, 1801, 1797, 1801, 1855, + /* 370 */ 1801, 1801, 1761, 1912, 1830, 1830, 1887, 1711, 1862, 1862, + /* 380 */ 1875, 1875, 1810, 1815, 1940, 1711, 1807, 1810, 1821, 1823, + /* 390 */ 1740, 1945, 1963, 1963, 1974, 1974, 1974, 2142, 2142, 2142, + /* 400 */ 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, + /* 410 */ 2142, 2142, 20, 1224, 256, 1111, 1115, 1114, 1192, 1496, + /* 420 */ 1424, 1505, 1427, 355, 1383, 1537, 1506, 1538, 1553, 1583, + /* 430 */ 1584, 1591, 1625, 541, 1445, 1562, 1450, 1572, 1515, 1428, + /* 440 */ 1532, 1592, 1629, 1520, 1630, 1639, 1510, 1544, 1662, 1675, + /* 450 */ 1551, 48, 1996, 2001, 1983, 1844, 1993, 1994, 1986, 1989, + /* 460 */ 1874, 1863, 1885, 1991, 1991, 1995, 1876, 1997, 1877, 2004, + /* 470 */ 2021, 1881, 1894, 1991, 1895, 1965, 1990, 1991, 1878, 1975, + /* 480 */ 1977, 1978, 1979, 1903, 1918, 2002, 1896, 2034, 2033, 2017, + /* 490 */ 1925, 1880, 1976, 2018, 1980, 1967, 2005, 1904, 1932, 2025, + /* 500 */ 2030, 2032, 1921, 1928, 2035, 1987, 2036, 2037, 2038, 2040, + /* 510 */ 1988, 2006, 2039, 1960, 2041, 2042, 1999, 2023, 2044, 2043, + /* 520 */ 1919, 2048, 2049, 2050, 2046, 2051, 2053, 1981, 1935, 2056, + /* 530 */ 2057, 1966, 2047, 2061, 1941, 2060, 2052, 2054, 2055, 2058, + /* 540 */ 2003, 2012, 2007, 2059, 2015, 2008, 2062, 2071, 2073, 2074, + /* 550 */ 2072, 2075, 2065, 1962, 1964, 2079, 2060, 2082, 2084, 2085, + /* 560 */ 2087, 2086, 2089, 2088, 2091, 2093, 2099, 2094, 2095, 2096, + /* 570 */ 2097, 2100, 2101, 2102, 1998, 1984, 1985, 2000, 2103, 2098, + /* 580 */ 2109, 2117, 2128, }; -#define YY_REDUCE_COUNT (410) -#define YY_REDUCE_MIN (-271) -#define YY_REDUCE_MAX (1753) +#define YY_REDUCE_COUNT (411) +#define YY_REDUCE_MIN (-275) +#define YY_REDUCE_MAX (1798) static const short yy_reduce_ofst[] = { - /* 0 */ -125, 733, 789, 241, 293, -123, -193, -191, -183, -187, - /* 10 */ 166, 238, 133, -207, -199, -267, -176, -6, 204, 489, - /* 20 */ 576, 598, -175, 686, 860, 615, 725, 1014, 778, 781, - /* 30 */ 857, 616, 887, 87, 240, -192, 408, 626, 796, 843, - /* 40 */ 854, 1004, -271, -271, -271, -271, -271, -271, -271, -271, - /* 50 */ -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, - /* 60 */ -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, - /* 70 */ -271, -271, -271, -271, -271, -271, -271, -271, -271, 80, - /* 80 */ 83, 313, 886, 888, 918, 938, 1021, 1034, 1036, 1141, - /* 90 */ 1159, 1163, 1166, 1168, 1170, 1176, 1178, 1180, 1184, 1196, - /* 100 */ 1198, 1205, 1215, 1225, 1227, 1236, 1252, 1254, 1264, 1303, - /* 110 */ 1309, 1312, 1322, 1325, 1328, 1337, 1340, 1343, 1353, 1371, - /* 120 */ 1373, 1384, 1386, 1411, 1413, 1420, 1424, 1426, 1458, 1470, - /* 130 */ 1473, -271, -271, -271, -271, -271, -271, -271, -271, -271, - /* 140 */ -271, -271, 138, 459, 396, -158, 470, 302, -212, 521, - /* 150 */ 201, -195, -92, 559, 630, 632, 630, -271, 632, 901, - /* 160 */ 63, 407, 670, -271, -271, -271, -271, 161, 161, 161, - /* 170 */ 251, 335, 847, 979, 1097, 537, 588, 618, 628, 688, - /* 180 */ 688, -166, -161, 674, 787, 794, 799, 852, 996, -122, - /* 190 */ 837, -120, 1018, 1035, 415, 1047, 1001, 958, 1082, 400, - /* 200 */ 1099, 779, 1137, 1142, 263, 1083, 1145, 1150, 1041, 1139, - /* 210 */ 965, 1050, 362, 849, 752, 629, 675, 1162, 1173, 1090, - /* 220 */ 1195, -194, 56, 185, -135, 232, 522, 560, 571, 601, - /* 230 */ 617, 669, 683, 711, 850, 893, 1000, 1040, 1049, 1081, - /* 240 */ 1087, 1101, 392, 1114, 1123, 1155, 1161, 1175, 1271, 1293, - /* 250 */ 1299, 1330, 1339, 1342, 1347, 593, 1282, 1286, 1350, 1359, - /* 260 */ 1368, 1314, 1480, 1483, 1507, 1085, 1338, 1526, 1527, 1487, - /* 270 */ 1531, 560, 1532, 1534, 1535, 1538, 1539, 1541, 1448, 1450, - /* 280 */ 1496, 1484, 1485, 1489, 1490, 1314, 1496, 1496, 1504, 1536, - /* 290 */ 1564, 1451, 1486, 1492, 1509, 1493, 1465, 1515, 1494, 1495, - /* 300 */ 1517, 1500, 1519, 1474, 1550, 1543, 1548, 1556, 1565, 1566, - /* 310 */ 1518, 1523, 1542, 1544, 1525, 1545, 1513, 1553, 1552, 1604, - /* 320 */ 1508, 1510, 1608, 1609, 1520, 1528, 1612, 1540, 1559, 1560, - /* 330 */ 1592, 1591, 1595, 1596, 1597, 1629, 1638, 1594, 1569, 1570, - /* 340 */ 1600, 1568, 1610, 1601, 1611, 1603, 1643, 1651, 1562, 1571, - /* 350 */ 1655, 1659, 1640, 1663, 1666, 1664, 1667, 1641, 1650, 1652, - /* 360 */ 1653, 1647, 1656, 1657, 1658, 1668, 1672, 1681, 1649, 1682, - /* 370 */ 1683, 1573, 1582, 1607, 1615, 1685, 1702, 1586, 1587, 1642, - /* 380 */ 1646, 1673, 1675, 1636, 1714, 1637, 1677, 1674, 1678, 1694, - /* 390 */ 1719, 1734, 1735, 1746, 1747, 1750, 1633, 1661, 1686, 1738, - /* 400 */ 1728, 1733, 1736, 1737, 1740, 1726, 1730, 1742, 1743, 1748, - /* 410 */ 1753, + /* 0 */ -71, 194, 343, 835, -180, -177, 838, -194, -188, -185, + /* 10 */ -183, 82, 183, -65, 133, 245, 346, 407, 458, -178, + /* 20 */ 75, -275, -4, 310, 312, 489, 575, 596, 463, 686, + /* 30 */ 707, 725, 780, 1098, 856, 778, 1059, 1090, 708, 887, + /* 40 */ 86, 448, 980, 630, 680, 681, 684, 796, 801, 796, + /* 50 */ 801, -261, -261, -261, -261, -261, -261, -261, -261, -261, + /* 60 */ -261, -261, -261, -261, -261, -261, -261, -261, -261, -261, + /* 70 */ -261, -261, -261, -261, -261, -261, -261, -261, -261, -261, + /* 80 */ -261, -261, -261, -261, -261, -261, -261, -261, 391, 886, + /* 90 */ 888, 1013, 1016, 1081, 1087, 1151, 1159, 1177, 1185, 1188, + /* 100 */ 1190, 1194, 1197, 1203, 1247, 1260, 1264, 1267, 1269, 1273, + /* 110 */ 1315, 1322, 1335, 1337, 1356, 1362, 1418, 1425, 1453, 1457, + /* 120 */ 1465, 1473, 1487, 1495, 1507, 1517, 1521, 1534, 1543, 1546, + /* 130 */ 1549, 1552, 1554, 1560, 1581, 1590, 1593, 1595, 1621, 1623, + /* 140 */ -261, -261, -261, -261, -261, -261, -261, -261, -261, -261, + /* 150 */ -261, -186, -117, 260, 263, 460, 631, -74, 497, -181, + /* 160 */ -261, 939, 176, 274, 338, 676, -261, -261, -261, -261, + /* 170 */ -212, -212, -212, -184, 149, 777, 1061, 1103, 265, 419, + /* 180 */ -254, 670, 677, 677, -11, -129, 184, 488, 736, 789, + /* 190 */ 805, 844, 403, 529, 579, 668, 783, 841, 1158, 1112, + /* 200 */ 806, 861, 1095, 846, 839, 1031, -189, 1077, 1080, 1116, + /* 210 */ 1084, 1156, 1139, 1221, 46, 1099, 1037, 1118, 1171, 1214, + /* 220 */ 1210, 1258, -210, -190, -176, -115, 117, 262, 376, 490, + /* 230 */ 511, 520, 618, 639, 743, 901, 907, 958, 1014, 1055, + /* 240 */ 1108, 1193, 1244, 720, 1248, 1277, 1324, 1347, 1417, 1431, + /* 250 */ 1432, 1440, 1451, 1452, 1463, 1478, 1286, 1350, 1369, 1490, + /* 260 */ 1498, 1501, 773, 1509, 1513, 1528, 1292, 1367, 1535, 1536, + /* 270 */ 1477, 1542, 376, 1547, 1550, 1555, 1559, 1568, 1571, 1441, + /* 280 */ 1443, 1474, 1511, 1516, 1519, 1522, 773, 1474, 1474, 1503, + /* 290 */ 1567, 1594, 1484, 1527, 1556, 1570, 1557, 1524, 1573, 1545, + /* 300 */ 1548, 1576, 1561, 1587, 1540, 1575, 1606, 1611, 1622, 1624, + /* 310 */ 1626, 1582, 1597, 1598, 1599, 1601, 1603, 1563, 1608, 1605, + /* 320 */ 1604, 1564, 1566, 1655, 1660, 1578, 1579, 1665, 1586, 1607, + /* 330 */ 1610, 1642, 1641, 1645, 1646, 1647, 1679, 1688, 1644, 1618, + /* 340 */ 1619, 1648, 1628, 1659, 1649, 1663, 1650, 1700, 1702, 1612, + /* 350 */ 1615, 1706, 1708, 1689, 1709, 1712, 1713, 1715, 1691, 1698, + /* 360 */ 1699, 1701, 1693, 1704, 1707, 1705, 1714, 1703, 1718, 1710, + /* 370 */ 1720, 1721, 1632, 1634, 1664, 1666, 1729, 1751, 1635, 1636, + /* 380 */ 1692, 1694, 1716, 1722, 1684, 1763, 1685, 1723, 1724, 1727, + /* 390 */ 1730, 1768, 1783, 1784, 1793, 1794, 1796, 1683, 1686, 1690, + /* 400 */ 1782, 1779, 1780, 1781, 1785, 1788, 1774, 1775, 1786, 1787, + /* 410 */ 1789, 1798, }; static const YYACTIONTYPE yy_default[] = { - /* 0 */ 1648, 1648, 1648, 1478, 1243, 1354, 1243, 1243, 1243, 1478, - /* 10 */ 1478, 1478, 1243, 1384, 1384, 1531, 1276, 1243, 1243, 1243, - /* 20 */ 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1477, 1243, - /* 30 */ 1243, 1243, 1243, 1564, 1564, 1243, 1243, 1243, 1243, 1243, - /* 40 */ 1243, 1243, 1243, 1393, 1243, 1400, 1243, 1243, 1243, 1243, - /* 50 */ 1243, 1479, 1480, 1243, 1243, 1243, 1530, 1532, 1495, 1407, - /* 60 */ 1406, 1405, 1404, 1513, 1372, 1398, 1391, 1395, 1474, 1475, - /* 70 */ 1473, 1626, 1480, 1479, 1243, 1394, 1442, 1458, 1441, 1243, - /* 80 */ 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, - /* 90 */ 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, - /* 100 */ 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, - /* 110 */ 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, - /* 120 */ 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, - /* 130 */ 1243, 1450, 1457, 1456, 1455, 1464, 1454, 1451, 1444, 1443, - /* 140 */ 1445, 1446, 1243, 1243, 1267, 1243, 1243, 1264, 1318, 1243, - /* 150 */ 1243, 1243, 1243, 1243, 1550, 1549, 1243, 1447, 1243, 1276, - /* 160 */ 1435, 1434, 1433, 1461, 1448, 1460, 1459, 1538, 1600, 1599, - /* 170 */ 1496, 1243, 1243, 1243, 1243, 1243, 1243, 1564, 1243, 1243, - /* 180 */ 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, - /* 190 */ 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, - /* 200 */ 1243, 1374, 1564, 1564, 1243, 1276, 1564, 1564, 1375, 1375, - /* 210 */ 1272, 1272, 1378, 1243, 1545, 1345, 1345, 1345, 1345, 1354, - /* 220 */ 1345, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, - /* 230 */ 1243, 1243, 1243, 1243, 1243, 1243, 1535, 1533, 1243, 1243, - /* 240 */ 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, - /* 250 */ 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, - /* 260 */ 1243, 1243, 1243, 1243, 1243, 1350, 1243, 1243, 1243, 1243, - /* 270 */ 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1593, 1243, 1508, - /* 280 */ 1332, 1350, 1350, 1350, 1350, 1352, 1333, 1331, 1344, 1277, - /* 290 */ 1250, 1640, 1410, 1399, 1351, 1399, 1637, 1397, 1410, 1410, - /* 300 */ 1397, 1410, 1351, 1637, 1293, 1615, 1288, 1384, 1384, 1384, - /* 310 */ 1374, 1374, 1374, 1374, 1378, 1378, 1476, 1351, 1344, 1243, - /* 320 */ 1640, 1640, 1360, 1360, 1639, 1639, 1360, 1496, 1623, 1419, - /* 330 */ 1321, 1327, 1327, 1327, 1327, 1360, 1261, 1397, 1623, 1623, - /* 340 */ 1397, 1419, 1321, 1397, 1321, 1397, 1360, 1261, 1512, 1634, - /* 350 */ 1360, 1261, 1486, 1360, 1261, 1360, 1261, 1486, 1319, 1319, - /* 360 */ 1319, 1308, 1243, 1243, 1486, 1319, 1293, 1319, 1308, 1319, - /* 370 */ 1319, 1582, 1243, 1490, 1490, 1486, 1360, 1574, 1574, 1387, - /* 380 */ 1387, 1392, 1378, 1481, 1360, 1243, 1392, 1390, 1388, 1397, - /* 390 */ 1311, 1596, 1596, 1592, 1592, 1592, 1645, 1645, 1545, 1608, - /* 400 */ 1276, 1276, 1276, 1276, 1608, 1295, 1295, 1277, 1277, 1276, - /* 410 */ 1608, 1243, 1243, 1243, 1243, 1243, 1243, 1603, 1243, 1540, - /* 420 */ 1497, 1364, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, - /* 430 */ 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1551, 1243, - /* 440 */ 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1424, - /* 450 */ 1243, 1246, 1542, 1243, 1243, 1243, 1243, 1243, 1243, 1243, - /* 460 */ 1243, 1401, 1402, 1365, 1243, 1243, 1243, 1243, 1243, 1243, - /* 470 */ 1243, 1416, 1243, 1243, 1243, 1411, 1243, 1243, 1243, 1243, - /* 480 */ 1243, 1243, 1243, 1243, 1636, 1243, 1243, 1243, 1243, 1243, - /* 490 */ 1243, 1511, 1510, 1243, 1243, 1362, 1243, 1243, 1243, 1243, - /* 500 */ 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1291, - /* 510 */ 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, - /* 520 */ 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, - /* 530 */ 1243, 1243, 1243, 1389, 1243, 1243, 1243, 1243, 1243, 1243, - /* 540 */ 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1579, 1379, - /* 550 */ 1243, 1243, 1243, 1243, 1627, 1243, 1243, 1243, 1243, 1243, - /* 560 */ 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1619, - /* 570 */ 1335, 1425, 1243, 1428, 1265, 1243, 1255, 1243, 1243, + /* 0 */ 1663, 1663, 1663, 1491, 1254, 1367, 1254, 1254, 1254, 1254, + /* 10 */ 1491, 1491, 1491, 1254, 1254, 1254, 1254, 1254, 1254, 1397, + /* 20 */ 1397, 1544, 1287, 1254, 1254, 1254, 1254, 1254, 1254, 1254, + /* 30 */ 1254, 1254, 1254, 1254, 1254, 1490, 1254, 1254, 1254, 1254, + /* 40 */ 1578, 1578, 1254, 1254, 1254, 1254, 1254, 1563, 1562, 1254, + /* 50 */ 1254, 1254, 1406, 1254, 1413, 1254, 1254, 1254, 1254, 1254, + /* 60 */ 1492, 1493, 1254, 1254, 1254, 1543, 1545, 1508, 1420, 1419, + /* 70 */ 1418, 1417, 1526, 1385, 1411, 1404, 1408, 1487, 1488, 1486, + /* 80 */ 1641, 1493, 1492, 1254, 1407, 1455, 1471, 1454, 1254, 1254, + /* 90 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, + /* 100 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, + /* 110 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, + /* 120 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, + /* 130 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, + /* 140 */ 1463, 1470, 1469, 1468, 1477, 1467, 1464, 1457, 1456, 1458, + /* 150 */ 1459, 1278, 1254, 1275, 1329, 1254, 1254, 1254, 1254, 1254, + /* 160 */ 1460, 1287, 1448, 1447, 1446, 1254, 1474, 1461, 1473, 1472, + /* 170 */ 1551, 1615, 1614, 1509, 1254, 1254, 1254, 1254, 1254, 1254, + /* 180 */ 1578, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, + /* 190 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, + /* 200 */ 1254, 1254, 1254, 1387, 1578, 1578, 1254, 1287, 1578, 1578, + /* 210 */ 1388, 1388, 1283, 1283, 1391, 1558, 1358, 1358, 1358, 1358, + /* 220 */ 1367, 1358, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, + /* 230 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1548, 1546, 1254, + /* 240 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, + /* 250 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, + /* 260 */ 1254, 1254, 1254, 1254, 1254, 1254, 1363, 1254, 1254, 1254, + /* 270 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1608, 1254, + /* 280 */ 1521, 1343, 1363, 1363, 1363, 1363, 1365, 1344, 1342, 1357, + /* 290 */ 1288, 1261, 1655, 1423, 1412, 1364, 1412, 1652, 1410, 1423, + /* 300 */ 1423, 1410, 1423, 1364, 1652, 1304, 1630, 1299, 1397, 1397, + /* 310 */ 1397, 1387, 1387, 1387, 1387, 1391, 1391, 1489, 1364, 1357, + /* 320 */ 1254, 1655, 1655, 1373, 1373, 1654, 1654, 1373, 1509, 1638, + /* 330 */ 1432, 1332, 1338, 1338, 1338, 1338, 1373, 1272, 1410, 1638, + /* 340 */ 1638, 1410, 1432, 1332, 1410, 1332, 1410, 1373, 1272, 1525, + /* 350 */ 1649, 1373, 1272, 1499, 1373, 1272, 1373, 1272, 1499, 1330, + /* 360 */ 1330, 1330, 1319, 1254, 1254, 1499, 1330, 1304, 1330, 1319, + /* 370 */ 1330, 1330, 1596, 1254, 1503, 1503, 1499, 1373, 1588, 1588, + /* 380 */ 1400, 1400, 1405, 1391, 1494, 1373, 1254, 1405, 1403, 1401, + /* 390 */ 1410, 1322, 1611, 1611, 1607, 1607, 1607, 1660, 1660, 1558, + /* 400 */ 1623, 1287, 1287, 1287, 1287, 1623, 1306, 1306, 1288, 1288, + /* 410 */ 1287, 1623, 1254, 1254, 1254, 1254, 1254, 1254, 1618, 1254, + /* 420 */ 1553, 1510, 1377, 1254, 1254, 1254, 1254, 1254, 1254, 1254, + /* 430 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1564, + /* 440 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, + /* 450 */ 1254, 1437, 1254, 1257, 1555, 1254, 1254, 1254, 1254, 1254, + /* 460 */ 1254, 1254, 1254, 1414, 1415, 1378, 1254, 1254, 1254, 1254, + /* 470 */ 1254, 1254, 1254, 1429, 1254, 1254, 1254, 1424, 1254, 1254, + /* 480 */ 1254, 1254, 1254, 1254, 1254, 1254, 1651, 1254, 1254, 1254, + /* 490 */ 1254, 1254, 1254, 1524, 1523, 1254, 1254, 1375, 1254, 1254, + /* 500 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, + /* 510 */ 1254, 1302, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, + /* 520 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, + /* 530 */ 1254, 1254, 1254, 1254, 1254, 1402, 1254, 1254, 1254, 1254, + /* 540 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, + /* 550 */ 1593, 1392, 1254, 1254, 1254, 1254, 1642, 1254, 1254, 1254, + /* 560 */ 1254, 1352, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, + /* 570 */ 1254, 1254, 1254, 1634, 1346, 1438, 1254, 1441, 1276, 1254, + /* 580 */ 1266, 1254, 1254, }; /********** End of lemon-generated parsing tables *****************************/ @@ -172521,8 +173902,8 @@ static const YYCODETYPE yyFallback[] = { 0, /* TRUEFALSE => nothing */ 0, /* ISNOT => nothing */ 0, /* FUNCTION => nothing */ - 0, /* UMINUS => nothing */ 0, /* UPLUS => nothing */ + 0, /* UMINUS => nothing */ 0, /* TRUTH => nothing */ 0, /* REGISTER => nothing */ 0, /* VECTOR => nothing */ @@ -172531,6 +173912,7 @@ static const YYCODETYPE yyFallback[] = { 0, /* ASTERISK => nothing */ 0, /* SPAN => nothing */ 0, /* ERROR => nothing */ + 0, /* QNUMBER => nothing */ 0, /* SPACE => nothing */ 0, /* ILLEGAL => nothing */ }; @@ -172573,14 +173955,9 @@ struct yyParser { #endif sqlite3ParserARG_SDECL /* A place to hold %extra_argument */ sqlite3ParserCTX_SDECL /* A place to hold %extra_context */ -#if YYSTACKDEPTH<=0 - int yystksz; /* Current side of the stack */ - yyStackEntry *yystack; /* The parser's stack */ - yyStackEntry yystk0; /* First stack entry */ -#else - yyStackEntry yystack[YYSTACKDEPTH]; /* The parser's stack */ - yyStackEntry *yystackEnd; /* Last entry in the stack */ -#endif + yyStackEntry *yystackEnd; /* Last entry in the stack */ + yyStackEntry *yystack; /* The parser stack */ + yyStackEntry yystk0[YYSTACKDEPTH]; /* Initial stack space */ }; typedef struct yyParser yyParser; @@ -172794,8 +174171,8 @@ static const char *const yyTokenName[] = { /* 170 */ "TRUEFALSE", /* 171 */ "ISNOT", /* 172 */ "FUNCTION", - /* 173 */ "UMINUS", - /* 174 */ "UPLUS", + /* 173 */ "UPLUS", + /* 174 */ "UMINUS", /* 175 */ "TRUTH", /* 176 */ "REGISTER", /* 177 */ "VECTOR", @@ -172804,142 +174181,145 @@ static const char *const yyTokenName[] = { /* 180 */ "ASTERISK", /* 181 */ "SPAN", /* 182 */ "ERROR", - /* 183 */ "SPACE", - /* 184 */ "ILLEGAL", - /* 185 */ "input", - /* 186 */ "cmdlist", - /* 187 */ "ecmd", - /* 188 */ "cmdx", - /* 189 */ "explain", - /* 190 */ "cmd", - /* 191 */ "transtype", - /* 192 */ "trans_opt", - /* 193 */ "nm", - /* 194 */ "savepoint_opt", - /* 195 */ "create_table", - /* 196 */ "create_table_args", - /* 197 */ "createkw", - /* 198 */ "temp", - /* 199 */ "ifnotexists", - /* 200 */ "dbnm", - /* 201 */ "columnlist", - /* 202 */ "conslist_opt", - /* 203 */ "table_option_set", - /* 204 */ "select", - /* 205 */ "table_option", - /* 206 */ "columnname", - /* 207 */ "carglist", - /* 208 */ "typetoken", - /* 209 */ "typename", - /* 210 */ "signed", - /* 211 */ "plus_num", - /* 212 */ "minus_num", - /* 213 */ "scanpt", - /* 214 */ "scantok", - /* 215 */ "ccons", - /* 216 */ "term", - /* 217 */ "expr", - /* 218 */ "onconf", - /* 219 */ "sortorder", - /* 220 */ "autoinc", - /* 221 */ "eidlist_opt", - /* 222 */ "refargs", - /* 223 */ "defer_subclause", - /* 224 */ "generated", - /* 225 */ "refarg", - /* 226 */ "refact", - /* 227 */ "init_deferred_pred_opt", - /* 228 */ "conslist", - /* 229 */ "tconscomma", - /* 230 */ "tcons", - /* 231 */ "sortlist", - /* 232 */ "eidlist", - /* 233 */ "defer_subclause_opt", - /* 234 */ "orconf", - /* 235 */ "resolvetype", - /* 236 */ "raisetype", - /* 237 */ "ifexists", - /* 238 */ "fullname", - /* 239 */ "selectnowith", - /* 240 */ "oneselect", - /* 241 */ "wqlist", - /* 242 */ "multiselect_op", - /* 243 */ "distinct", - /* 244 */ "selcollist", - /* 245 */ "from", - /* 246 */ "where_opt", - /* 247 */ "groupby_opt", - /* 248 */ "having_opt", - /* 249 */ "orderby_opt", - /* 250 */ "limit_opt", - /* 251 */ "window_clause", - /* 252 */ "values", - /* 253 */ "nexprlist", - /* 254 */ "sclp", - /* 255 */ "as", - /* 256 */ "seltablist", - /* 257 */ "stl_prefix", - /* 258 */ "joinop", - /* 259 */ "on_using", - /* 260 */ "indexed_by", - /* 261 */ "exprlist", - /* 262 */ "xfullname", - /* 263 */ "idlist", - /* 264 */ "indexed_opt", - /* 265 */ "nulls", - /* 266 */ "with", - /* 267 */ "where_opt_ret", - /* 268 */ "setlist", - /* 269 */ "insert_cmd", - /* 270 */ "idlist_opt", - /* 271 */ "upsert", - /* 272 */ "returning", - /* 273 */ "filter_over", - /* 274 */ "likeop", - /* 275 */ "between_op", - /* 276 */ "in_op", - /* 277 */ "paren_exprlist", - /* 278 */ "case_operand", - /* 279 */ "case_exprlist", - /* 280 */ "case_else", - /* 281 */ "uniqueflag", - /* 282 */ "collate", - /* 283 */ "vinto", - /* 284 */ "nmnum", - /* 285 */ "trigger_decl", - /* 286 */ "trigger_cmd_list", - /* 287 */ "trigger_time", - /* 288 */ "trigger_event", - /* 289 */ "foreach_clause", - /* 290 */ "when_clause", - /* 291 */ "trigger_cmd", - /* 292 */ "trnm", - /* 293 */ "tridxby", - /* 294 */ "database_kw_opt", - /* 295 */ "key_opt", - /* 296 */ "add_column_fullname", - /* 297 */ "kwcolumn_opt", - /* 298 */ "create_vtab", - /* 299 */ "vtabarglist", - /* 300 */ "vtabarg", - /* 301 */ "vtabargtoken", - /* 302 */ "lp", - /* 303 */ "anylist", - /* 304 */ "wqitem", - /* 305 */ "wqas", - /* 306 */ "windowdefn_list", - /* 307 */ "windowdefn", - /* 308 */ "window", - /* 309 */ "frame_opt", - /* 310 */ "part_opt", - /* 311 */ "filter_clause", - /* 312 */ "over_clause", - /* 313 */ "range_or_rows", - /* 314 */ "frame_bound", - /* 315 */ "frame_bound_s", - /* 316 */ "frame_bound_e", - /* 317 */ "frame_exclude_opt", - /* 318 */ "frame_exclude", + /* 183 */ "QNUMBER", + /* 184 */ "SPACE", + /* 185 */ "ILLEGAL", + /* 186 */ "input", + /* 187 */ "cmdlist", + /* 188 */ "ecmd", + /* 189 */ "cmdx", + /* 190 */ "explain", + /* 191 */ "cmd", + /* 192 */ "transtype", + /* 193 */ "trans_opt", + /* 194 */ "nm", + /* 195 */ "savepoint_opt", + /* 196 */ "create_table", + /* 197 */ "create_table_args", + /* 198 */ "createkw", + /* 199 */ "temp", + /* 200 */ "ifnotexists", + /* 201 */ "dbnm", + /* 202 */ "columnlist", + /* 203 */ "conslist_opt", + /* 204 */ "table_option_set", + /* 205 */ "select", + /* 206 */ "table_option", + /* 207 */ "columnname", + /* 208 */ "carglist", + /* 209 */ "typetoken", + /* 210 */ "typename", + /* 211 */ "signed", + /* 212 */ "plus_num", + /* 213 */ "minus_num", + /* 214 */ "scanpt", + /* 215 */ "scantok", + /* 216 */ "ccons", + /* 217 */ "term", + /* 218 */ "expr", + /* 219 */ "onconf", + /* 220 */ "sortorder", + /* 221 */ "autoinc", + /* 222 */ "eidlist_opt", + /* 223 */ "refargs", + /* 224 */ "defer_subclause", + /* 225 */ "generated", + /* 226 */ "refarg", + /* 227 */ "refact", + /* 228 */ "init_deferred_pred_opt", + /* 229 */ "conslist", + /* 230 */ "tconscomma", + /* 231 */ "tcons", + /* 232 */ "sortlist", + /* 233 */ "eidlist", + /* 234 */ "defer_subclause_opt", + /* 235 */ "orconf", + /* 236 */ "resolvetype", + /* 237 */ "raisetype", + /* 238 */ "ifexists", + /* 239 */ "fullname", + /* 240 */ "selectnowith", + /* 241 */ "oneselect", + /* 242 */ "wqlist", + /* 243 */ "multiselect_op", + /* 244 */ "distinct", + /* 245 */ "selcollist", + /* 246 */ "from", + /* 247 */ "where_opt", + /* 248 */ "groupby_opt", + /* 249 */ "having_opt", + /* 250 */ "orderby_opt", + /* 251 */ "limit_opt", + /* 252 */ "window_clause", + /* 253 */ "values", + /* 254 */ "nexprlist", + /* 255 */ "mvalues", + /* 256 */ "sclp", + /* 257 */ "as", + /* 258 */ "seltablist", + /* 259 */ "stl_prefix", + /* 260 */ "joinop", + /* 261 */ "on_using", + /* 262 */ "indexed_by", + /* 263 */ "exprlist", + /* 264 */ "xfullname", + /* 265 */ "idlist", + /* 266 */ "indexed_opt", + /* 267 */ "nulls", + /* 268 */ "with", + /* 269 */ "where_opt_ret", + /* 270 */ "setlist", + /* 271 */ "insert_cmd", + /* 272 */ "idlist_opt", + /* 273 */ "upsert", + /* 274 */ "returning", + /* 275 */ "filter_over", + /* 276 */ "likeop", + /* 277 */ "between_op", + /* 278 */ "in_op", + /* 279 */ "paren_exprlist", + /* 280 */ "case_operand", + /* 281 */ "case_exprlist", + /* 282 */ "case_else", + /* 283 */ "uniqueflag", + /* 284 */ "collate", + /* 285 */ "vinto", + /* 286 */ "nmnum", + /* 287 */ "trigger_decl", + /* 288 */ "trigger_cmd_list", + /* 289 */ "trigger_time", + /* 290 */ "trigger_event", + /* 291 */ "foreach_clause", + /* 292 */ "when_clause", + /* 293 */ "trigger_cmd", + /* 294 */ "trnm", + /* 295 */ "tridxby", + /* 296 */ "database_kw_opt", + /* 297 */ "key_opt", + /* 298 */ "add_column_fullname", + /* 299 */ "kwcolumn_opt", + /* 300 */ "create_vtab", + /* 301 */ "vtabarglist", + /* 302 */ "vtabarg", + /* 303 */ "vtabargtoken", + /* 304 */ "lp", + /* 305 */ "anylist", + /* 306 */ "wqitem", + /* 307 */ "wqas", + /* 308 */ "withnm", + /* 309 */ "windowdefn_list", + /* 310 */ "windowdefn", + /* 311 */ "window", + /* 312 */ "frame_opt", + /* 313 */ "part_opt", + /* 314 */ "filter_clause", + /* 315 */ "over_clause", + /* 316 */ "range_or_rows", + /* 317 */ "frame_bound", + /* 318 */ "frame_bound_s", + /* 319 */ "frame_bound_e", + /* 320 */ "frame_exclude_opt", + /* 321 */ "frame_exclude", }; #endif /* defined(YYCOVERAGE) || !defined(NDEBUG) */ @@ -173042,351 +174422,363 @@ static const char *const yyRuleName[] = { /* 92 */ "oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt", /* 93 */ "oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt window_clause orderby_opt limit_opt", /* 94 */ "values ::= VALUES LP nexprlist RP", - /* 95 */ "values ::= values COMMA LP nexprlist RP", - /* 96 */ "distinct ::= DISTINCT", - /* 97 */ "distinct ::= ALL", - /* 98 */ "distinct ::=", - /* 99 */ "sclp ::=", - /* 100 */ "selcollist ::= sclp scanpt expr scanpt as", - /* 101 */ "selcollist ::= sclp scanpt STAR", - /* 102 */ "selcollist ::= sclp scanpt nm DOT STAR", - /* 103 */ "as ::= AS nm", - /* 104 */ "as ::=", - /* 105 */ "from ::=", - /* 106 */ "from ::= FROM seltablist", - /* 107 */ "stl_prefix ::= seltablist joinop", - /* 108 */ "stl_prefix ::=", - /* 109 */ "seltablist ::= stl_prefix nm dbnm as on_using", - /* 110 */ "seltablist ::= stl_prefix nm dbnm as indexed_by on_using", - /* 111 */ "seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_using", - /* 112 */ "seltablist ::= stl_prefix LP select RP as on_using", - /* 113 */ "seltablist ::= stl_prefix LP seltablist RP as on_using", - /* 114 */ "dbnm ::=", - /* 115 */ "dbnm ::= DOT nm", - /* 116 */ "fullname ::= nm", - /* 117 */ "fullname ::= nm DOT nm", - /* 118 */ "xfullname ::= nm", - /* 119 */ "xfullname ::= nm DOT nm", - /* 120 */ "xfullname ::= nm DOT nm AS nm", - /* 121 */ "xfullname ::= nm AS nm", - /* 122 */ "joinop ::= COMMA|JOIN", - /* 123 */ "joinop ::= JOIN_KW JOIN", - /* 124 */ "joinop ::= JOIN_KW nm JOIN", - /* 125 */ "joinop ::= JOIN_KW nm nm JOIN", - /* 126 */ "on_using ::= ON expr", - /* 127 */ "on_using ::= USING LP idlist RP", - /* 128 */ "on_using ::=", - /* 129 */ "indexed_opt ::=", - /* 130 */ "indexed_by ::= INDEXED BY nm", - /* 131 */ "indexed_by ::= NOT INDEXED", - /* 132 */ "orderby_opt ::=", - /* 133 */ "orderby_opt ::= ORDER BY sortlist", - /* 134 */ "sortlist ::= sortlist COMMA expr sortorder nulls", - /* 135 */ "sortlist ::= expr sortorder nulls", - /* 136 */ "sortorder ::= ASC", - /* 137 */ "sortorder ::= DESC", - /* 138 */ "sortorder ::=", - /* 139 */ "nulls ::= NULLS FIRST", - /* 140 */ "nulls ::= NULLS LAST", - /* 141 */ "nulls ::=", - /* 142 */ "groupby_opt ::=", - /* 143 */ "groupby_opt ::= GROUP BY nexprlist", - /* 144 */ "having_opt ::=", - /* 145 */ "having_opt ::= HAVING expr", - /* 146 */ "limit_opt ::=", - /* 147 */ "limit_opt ::= LIMIT expr", - /* 148 */ "limit_opt ::= LIMIT expr OFFSET expr", - /* 149 */ "limit_opt ::= LIMIT expr COMMA expr", - /* 150 */ "cmd ::= with DELETE FROM xfullname indexed_opt where_opt_ret", - /* 151 */ "where_opt ::=", - /* 152 */ "where_opt ::= WHERE expr", - /* 153 */ "where_opt_ret ::=", - /* 154 */ "where_opt_ret ::= WHERE expr", - /* 155 */ "where_opt_ret ::= RETURNING selcollist", - /* 156 */ "where_opt_ret ::= WHERE expr RETURNING selcollist", - /* 157 */ "cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist from where_opt_ret", - /* 158 */ "setlist ::= setlist COMMA nm EQ expr", - /* 159 */ "setlist ::= setlist COMMA LP idlist RP EQ expr", - /* 160 */ "setlist ::= nm EQ expr", - /* 161 */ "setlist ::= LP idlist RP EQ expr", - /* 162 */ "cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert", - /* 163 */ "cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES returning", - /* 164 */ "upsert ::=", - /* 165 */ "upsert ::= RETURNING selcollist", - /* 166 */ "upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt upsert", - /* 167 */ "upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING upsert", - /* 168 */ "upsert ::= ON CONFLICT DO NOTHING returning", - /* 169 */ "upsert ::= ON CONFLICT DO UPDATE SET setlist where_opt returning", - /* 170 */ "returning ::= RETURNING selcollist", - /* 171 */ "insert_cmd ::= INSERT orconf", - /* 172 */ "insert_cmd ::= REPLACE", - /* 173 */ "idlist_opt ::=", - /* 174 */ "idlist_opt ::= LP idlist RP", - /* 175 */ "idlist ::= idlist COMMA nm", - /* 176 */ "idlist ::= nm", - /* 177 */ "expr ::= LP expr RP", - /* 178 */ "expr ::= ID|INDEXED|JOIN_KW", - /* 179 */ "expr ::= nm DOT nm", - /* 180 */ "expr ::= nm DOT nm DOT nm", - /* 181 */ "term ::= NULL|FLOAT|BLOB", - /* 182 */ "term ::= STRING", - /* 183 */ "term ::= INTEGER", - /* 184 */ "expr ::= VARIABLE", - /* 185 */ "expr ::= expr COLLATE ID|STRING", - /* 186 */ "expr ::= CAST LP expr AS typetoken RP", - /* 187 */ "expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP", - /* 188 */ "expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist ORDER BY sortlist RP", - /* 189 */ "expr ::= ID|INDEXED|JOIN_KW LP STAR RP", - /* 190 */ "expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP filter_over", - /* 191 */ "expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist ORDER BY sortlist RP filter_over", - /* 192 */ "expr ::= ID|INDEXED|JOIN_KW LP STAR RP filter_over", - /* 193 */ "term ::= CTIME_KW", - /* 194 */ "expr ::= LP nexprlist COMMA expr RP", - /* 195 */ "expr ::= expr AND expr", - /* 196 */ "expr ::= expr OR expr", - /* 197 */ "expr ::= expr LT|GT|GE|LE expr", - /* 198 */ "expr ::= expr EQ|NE expr", - /* 199 */ "expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr", - /* 200 */ "expr ::= expr PLUS|MINUS expr", - /* 201 */ "expr ::= expr STAR|SLASH|REM expr", - /* 202 */ "expr ::= expr CONCAT expr", - /* 203 */ "likeop ::= NOT LIKE_KW|MATCH", - /* 204 */ "expr ::= expr likeop expr", - /* 205 */ "expr ::= expr likeop expr ESCAPE expr", - /* 206 */ "expr ::= expr ISNULL|NOTNULL", - /* 207 */ "expr ::= expr NOT NULL", - /* 208 */ "expr ::= expr IS expr", - /* 209 */ "expr ::= expr IS NOT expr", - /* 210 */ "expr ::= expr IS NOT DISTINCT FROM expr", - /* 211 */ "expr ::= expr IS DISTINCT FROM expr", - /* 212 */ "expr ::= NOT expr", - /* 213 */ "expr ::= BITNOT expr", - /* 214 */ "expr ::= PLUS|MINUS expr", - /* 215 */ "expr ::= expr PTR expr", - /* 216 */ "between_op ::= BETWEEN", - /* 217 */ "between_op ::= NOT BETWEEN", - /* 218 */ "expr ::= expr between_op expr AND expr", - /* 219 */ "in_op ::= IN", - /* 220 */ "in_op ::= NOT IN", - /* 221 */ "expr ::= expr in_op LP exprlist RP", - /* 222 */ "expr ::= LP select RP", - /* 223 */ "expr ::= expr in_op LP select RP", - /* 224 */ "expr ::= expr in_op nm dbnm paren_exprlist", - /* 225 */ "expr ::= EXISTS LP select RP", - /* 226 */ "expr ::= CASE case_operand case_exprlist case_else END", - /* 227 */ "case_exprlist ::= case_exprlist WHEN expr THEN expr", - /* 228 */ "case_exprlist ::= WHEN expr THEN expr", - /* 229 */ "case_else ::= ELSE expr", - /* 230 */ "case_else ::=", - /* 231 */ "case_operand ::=", - /* 232 */ "exprlist ::=", - /* 233 */ "nexprlist ::= nexprlist COMMA expr", - /* 234 */ "nexprlist ::= expr", - /* 235 */ "paren_exprlist ::=", - /* 236 */ "paren_exprlist ::= LP exprlist RP", - /* 237 */ "cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt", - /* 238 */ "uniqueflag ::= UNIQUE", - /* 239 */ "uniqueflag ::=", - /* 240 */ "eidlist_opt ::=", - /* 241 */ "eidlist_opt ::= LP eidlist RP", - /* 242 */ "eidlist ::= eidlist COMMA nm collate sortorder", - /* 243 */ "eidlist ::= nm collate sortorder", - /* 244 */ "collate ::=", - /* 245 */ "collate ::= COLLATE ID|STRING", - /* 246 */ "cmd ::= DROP INDEX ifexists fullname", - /* 247 */ "cmd ::= VACUUM vinto", - /* 248 */ "cmd ::= VACUUM nm vinto", - /* 249 */ "vinto ::= INTO expr", - /* 250 */ "vinto ::=", - /* 251 */ "cmd ::= PRAGMA nm dbnm", - /* 252 */ "cmd ::= PRAGMA nm dbnm EQ nmnum", - /* 253 */ "cmd ::= PRAGMA nm dbnm LP nmnum RP", - /* 254 */ "cmd ::= PRAGMA nm dbnm EQ minus_num", - /* 255 */ "cmd ::= PRAGMA nm dbnm LP minus_num RP", - /* 256 */ "plus_num ::= PLUS INTEGER|FLOAT", - /* 257 */ "minus_num ::= MINUS INTEGER|FLOAT", - /* 258 */ "cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END", - /* 259 */ "trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause", - /* 260 */ "trigger_time ::= BEFORE|AFTER", - /* 261 */ "trigger_time ::= INSTEAD OF", - /* 262 */ "trigger_time ::=", - /* 263 */ "trigger_event ::= DELETE|INSERT", - /* 264 */ "trigger_event ::= UPDATE", - /* 265 */ "trigger_event ::= UPDATE OF idlist", - /* 266 */ "when_clause ::=", - /* 267 */ "when_clause ::= WHEN expr", - /* 268 */ "trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI", - /* 269 */ "trigger_cmd_list ::= trigger_cmd SEMI", - /* 270 */ "trnm ::= nm DOT nm", - /* 271 */ "tridxby ::= INDEXED BY nm", - /* 272 */ "tridxby ::= NOT INDEXED", - /* 273 */ "trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt", - /* 274 */ "trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt", - /* 275 */ "trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt", - /* 276 */ "trigger_cmd ::= scanpt select scanpt", - /* 277 */ "expr ::= RAISE LP IGNORE RP", - /* 278 */ "expr ::= RAISE LP raisetype COMMA nm RP", - /* 279 */ "raisetype ::= ROLLBACK", - /* 280 */ "raisetype ::= ABORT", - /* 281 */ "raisetype ::= FAIL", - /* 282 */ "cmd ::= DROP TRIGGER ifexists fullname", - /* 283 */ "cmd ::= ATTACH database_kw_opt expr AS expr key_opt", - /* 284 */ "cmd ::= DETACH database_kw_opt expr", - /* 285 */ "key_opt ::=", - /* 286 */ "key_opt ::= KEY expr", - /* 287 */ "cmd ::= REINDEX", - /* 288 */ "cmd ::= REINDEX nm dbnm", - /* 289 */ "cmd ::= ANALYZE", - /* 290 */ "cmd ::= ANALYZE nm dbnm", - /* 291 */ "cmd ::= ALTER TABLE fullname RENAME TO nm", - /* 292 */ "cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist", - /* 293 */ "cmd ::= ALTER TABLE fullname DROP kwcolumn_opt nm", - /* 294 */ "add_column_fullname ::= fullname", - /* 295 */ "cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm", - /* 296 */ "cmd ::= create_vtab", - /* 297 */ "cmd ::= create_vtab LP vtabarglist RP", - /* 298 */ "create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm", - /* 299 */ "vtabarg ::=", - /* 300 */ "vtabargtoken ::= ANY", - /* 301 */ "vtabargtoken ::= lp anylist RP", - /* 302 */ "lp ::= LP", - /* 303 */ "with ::= WITH wqlist", - /* 304 */ "with ::= WITH RECURSIVE wqlist", - /* 305 */ "wqas ::= AS", - /* 306 */ "wqas ::= AS MATERIALIZED", - /* 307 */ "wqas ::= AS NOT MATERIALIZED", - /* 308 */ "wqitem ::= nm eidlist_opt wqas LP select RP", - /* 309 */ "wqlist ::= wqitem", - /* 310 */ "wqlist ::= wqlist COMMA wqitem", - /* 311 */ "windowdefn_list ::= windowdefn_list COMMA windowdefn", - /* 312 */ "windowdefn ::= nm AS LP window RP", - /* 313 */ "window ::= PARTITION BY nexprlist orderby_opt frame_opt", - /* 314 */ "window ::= nm PARTITION BY nexprlist orderby_opt frame_opt", - /* 315 */ "window ::= ORDER BY sortlist frame_opt", - /* 316 */ "window ::= nm ORDER BY sortlist frame_opt", - /* 317 */ "window ::= nm frame_opt", - /* 318 */ "frame_opt ::=", - /* 319 */ "frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt", - /* 320 */ "frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt", - /* 321 */ "range_or_rows ::= RANGE|ROWS|GROUPS", - /* 322 */ "frame_bound_s ::= frame_bound", - /* 323 */ "frame_bound_s ::= UNBOUNDED PRECEDING", - /* 324 */ "frame_bound_e ::= frame_bound", - /* 325 */ "frame_bound_e ::= UNBOUNDED FOLLOWING", - /* 326 */ "frame_bound ::= expr PRECEDING|FOLLOWING", - /* 327 */ "frame_bound ::= CURRENT ROW", - /* 328 */ "frame_exclude_opt ::=", - /* 329 */ "frame_exclude_opt ::= EXCLUDE frame_exclude", - /* 330 */ "frame_exclude ::= NO OTHERS", - /* 331 */ "frame_exclude ::= CURRENT ROW", - /* 332 */ "frame_exclude ::= GROUP|TIES", - /* 333 */ "window_clause ::= WINDOW windowdefn_list", - /* 334 */ "filter_over ::= filter_clause over_clause", - /* 335 */ "filter_over ::= over_clause", - /* 336 */ "filter_over ::= filter_clause", - /* 337 */ "over_clause ::= OVER LP window RP", - /* 338 */ "over_clause ::= OVER nm", - /* 339 */ "filter_clause ::= FILTER LP WHERE expr RP", - /* 340 */ "input ::= cmdlist", - /* 341 */ "cmdlist ::= cmdlist ecmd", - /* 342 */ "cmdlist ::= ecmd", - /* 343 */ "ecmd ::= SEMI", - /* 344 */ "ecmd ::= cmdx SEMI", - /* 345 */ "ecmd ::= explain cmdx SEMI", - /* 346 */ "trans_opt ::=", - /* 347 */ "trans_opt ::= TRANSACTION", - /* 348 */ "trans_opt ::= TRANSACTION nm", - /* 349 */ "savepoint_opt ::= SAVEPOINT", - /* 350 */ "savepoint_opt ::=", - /* 351 */ "cmd ::= create_table create_table_args", - /* 352 */ "table_option_set ::= table_option", - /* 353 */ "columnlist ::= columnlist COMMA columnname carglist", - /* 354 */ "columnlist ::= columnname carglist", - /* 355 */ "nm ::= ID|INDEXED|JOIN_KW", - /* 356 */ "nm ::= STRING", - /* 357 */ "typetoken ::= typename", - /* 358 */ "typename ::= ID|STRING", - /* 359 */ "signed ::= plus_num", - /* 360 */ "signed ::= minus_num", - /* 361 */ "carglist ::= carglist ccons", - /* 362 */ "carglist ::=", - /* 363 */ "ccons ::= NULL onconf", - /* 364 */ "ccons ::= GENERATED ALWAYS AS generated", - /* 365 */ "ccons ::= AS generated", - /* 366 */ "conslist_opt ::= COMMA conslist", - /* 367 */ "conslist ::= conslist tconscomma tcons", - /* 368 */ "conslist ::= tcons", - /* 369 */ "tconscomma ::=", - /* 370 */ "defer_subclause_opt ::= defer_subclause", - /* 371 */ "resolvetype ::= raisetype", - /* 372 */ "selectnowith ::= oneselect", - /* 373 */ "oneselect ::= values", - /* 374 */ "sclp ::= selcollist COMMA", - /* 375 */ "as ::= ID|STRING", - /* 376 */ "indexed_opt ::= indexed_by", - /* 377 */ "returning ::=", - /* 378 */ "expr ::= term", - /* 379 */ "likeop ::= LIKE_KW|MATCH", - /* 380 */ "case_operand ::= expr", - /* 381 */ "exprlist ::= nexprlist", - /* 382 */ "nmnum ::= plus_num", - /* 383 */ "nmnum ::= nm", - /* 384 */ "nmnum ::= ON", - /* 385 */ "nmnum ::= DELETE", - /* 386 */ "nmnum ::= DEFAULT", - /* 387 */ "plus_num ::= INTEGER|FLOAT", - /* 388 */ "foreach_clause ::=", - /* 389 */ "foreach_clause ::= FOR EACH ROW", - /* 390 */ "trnm ::= nm", - /* 391 */ "tridxby ::=", - /* 392 */ "database_kw_opt ::= DATABASE", - /* 393 */ "database_kw_opt ::=", - /* 394 */ "kwcolumn_opt ::=", - /* 395 */ "kwcolumn_opt ::= COLUMNKW", - /* 396 */ "vtabarglist ::= vtabarg", - /* 397 */ "vtabarglist ::= vtabarglist COMMA vtabarg", - /* 398 */ "vtabarg ::= vtabarg vtabargtoken", - /* 399 */ "anylist ::=", - /* 400 */ "anylist ::= anylist LP anylist RP", - /* 401 */ "anylist ::= anylist ANY", - /* 402 */ "with ::=", - /* 403 */ "windowdefn_list ::= windowdefn", - /* 404 */ "window ::= frame_opt", + /* 95 */ "oneselect ::= mvalues", + /* 96 */ "mvalues ::= values COMMA LP nexprlist RP", + /* 97 */ "mvalues ::= mvalues COMMA LP nexprlist RP", + /* 98 */ "distinct ::= DISTINCT", + /* 99 */ "distinct ::= ALL", + /* 100 */ "distinct ::=", + /* 101 */ "sclp ::=", + /* 102 */ "selcollist ::= sclp scanpt expr scanpt as", + /* 103 */ "selcollist ::= sclp scanpt STAR", + /* 104 */ "selcollist ::= sclp scanpt nm DOT STAR", + /* 105 */ "as ::= AS nm", + /* 106 */ "as ::=", + /* 107 */ "from ::=", + /* 108 */ "from ::= FROM seltablist", + /* 109 */ "stl_prefix ::= seltablist joinop", + /* 110 */ "stl_prefix ::=", + /* 111 */ "seltablist ::= stl_prefix nm dbnm as on_using", + /* 112 */ "seltablist ::= stl_prefix nm dbnm as indexed_by on_using", + /* 113 */ "seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_using", + /* 114 */ "seltablist ::= stl_prefix LP select RP as on_using", + /* 115 */ "seltablist ::= stl_prefix LP seltablist RP as on_using", + /* 116 */ "dbnm ::=", + /* 117 */ "dbnm ::= DOT nm", + /* 118 */ "fullname ::= nm", + /* 119 */ "fullname ::= nm DOT nm", + /* 120 */ "xfullname ::= nm", + /* 121 */ "xfullname ::= nm DOT nm", + /* 122 */ "xfullname ::= nm DOT nm AS nm", + /* 123 */ "xfullname ::= nm AS nm", + /* 124 */ "joinop ::= COMMA|JOIN", + /* 125 */ "joinop ::= JOIN_KW JOIN", + /* 126 */ "joinop ::= JOIN_KW nm JOIN", + /* 127 */ "joinop ::= JOIN_KW nm nm JOIN", + /* 128 */ "on_using ::= ON expr", + /* 129 */ "on_using ::= USING LP idlist RP", + /* 130 */ "on_using ::=", + /* 131 */ "indexed_opt ::=", + /* 132 */ "indexed_by ::= INDEXED BY nm", + /* 133 */ "indexed_by ::= NOT INDEXED", + /* 134 */ "orderby_opt ::=", + /* 135 */ "orderby_opt ::= ORDER BY sortlist", + /* 136 */ "sortlist ::= sortlist COMMA expr sortorder nulls", + /* 137 */ "sortlist ::= expr sortorder nulls", + /* 138 */ "sortorder ::= ASC", + /* 139 */ "sortorder ::= DESC", + /* 140 */ "sortorder ::=", + /* 141 */ "nulls ::= NULLS FIRST", + /* 142 */ "nulls ::= NULLS LAST", + /* 143 */ "nulls ::=", + /* 144 */ "groupby_opt ::=", + /* 145 */ "groupby_opt ::= GROUP BY nexprlist", + /* 146 */ "having_opt ::=", + /* 147 */ "having_opt ::= HAVING expr", + /* 148 */ "limit_opt ::=", + /* 149 */ "limit_opt ::= LIMIT expr", + /* 150 */ "limit_opt ::= LIMIT expr OFFSET expr", + /* 151 */ "limit_opt ::= LIMIT expr COMMA expr", + /* 152 */ "cmd ::= with DELETE FROM xfullname indexed_opt where_opt_ret", + /* 153 */ "where_opt ::=", + /* 154 */ "where_opt ::= WHERE expr", + /* 155 */ "where_opt_ret ::=", + /* 156 */ "where_opt_ret ::= WHERE expr", + /* 157 */ "where_opt_ret ::= RETURNING selcollist", + /* 158 */ "where_opt_ret ::= WHERE expr RETURNING selcollist", + /* 159 */ "cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist from where_opt_ret", + /* 160 */ "setlist ::= setlist COMMA nm EQ expr", + /* 161 */ "setlist ::= setlist COMMA LP idlist RP EQ expr", + /* 162 */ "setlist ::= nm EQ expr", + /* 163 */ "setlist ::= LP idlist RP EQ expr", + /* 164 */ "cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert", + /* 165 */ "cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES returning", + /* 166 */ "upsert ::=", + /* 167 */ "upsert ::= RETURNING selcollist", + /* 168 */ "upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt upsert", + /* 169 */ "upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING upsert", + /* 170 */ "upsert ::= ON CONFLICT DO NOTHING returning", + /* 171 */ "upsert ::= ON CONFLICT DO UPDATE SET setlist where_opt returning", + /* 172 */ "returning ::= RETURNING selcollist", + /* 173 */ "insert_cmd ::= INSERT orconf", + /* 174 */ "insert_cmd ::= REPLACE", + /* 175 */ "idlist_opt ::=", + /* 176 */ "idlist_opt ::= LP idlist RP", + /* 177 */ "idlist ::= idlist COMMA nm", + /* 178 */ "idlist ::= nm", + /* 179 */ "expr ::= LP expr RP", + /* 180 */ "expr ::= ID|INDEXED|JOIN_KW", + /* 181 */ "expr ::= nm DOT nm", + /* 182 */ "expr ::= nm DOT nm DOT nm", + /* 183 */ "term ::= NULL|FLOAT|BLOB", + /* 184 */ "term ::= STRING", + /* 185 */ "term ::= INTEGER", + /* 186 */ "expr ::= VARIABLE", + /* 187 */ "expr ::= expr COLLATE ID|STRING", + /* 188 */ "expr ::= CAST LP expr AS typetoken RP", + /* 189 */ "expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP", + /* 190 */ "expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist ORDER BY sortlist RP", + /* 191 */ "expr ::= ID|INDEXED|JOIN_KW LP STAR RP", + /* 192 */ "expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP filter_over", + /* 193 */ "expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist ORDER BY sortlist RP filter_over", + /* 194 */ "expr ::= ID|INDEXED|JOIN_KW LP STAR RP filter_over", + /* 195 */ "term ::= CTIME_KW", + /* 196 */ "expr ::= LP nexprlist COMMA expr RP", + /* 197 */ "expr ::= expr AND expr", + /* 198 */ "expr ::= expr OR expr", + /* 199 */ "expr ::= expr LT|GT|GE|LE expr", + /* 200 */ "expr ::= expr EQ|NE expr", + /* 201 */ "expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr", + /* 202 */ "expr ::= expr PLUS|MINUS expr", + /* 203 */ "expr ::= expr STAR|SLASH|REM expr", + /* 204 */ "expr ::= expr CONCAT expr", + /* 205 */ "likeop ::= NOT LIKE_KW|MATCH", + /* 206 */ "expr ::= expr likeop expr", + /* 207 */ "expr ::= expr likeop expr ESCAPE expr", + /* 208 */ "expr ::= expr ISNULL|NOTNULL", + /* 209 */ "expr ::= expr NOT NULL", + /* 210 */ "expr ::= expr IS expr", + /* 211 */ "expr ::= expr IS NOT expr", + /* 212 */ "expr ::= expr IS NOT DISTINCT FROM expr", + /* 213 */ "expr ::= expr IS DISTINCT FROM expr", + /* 214 */ "expr ::= NOT expr", + /* 215 */ "expr ::= BITNOT expr", + /* 216 */ "expr ::= PLUS|MINUS expr", + /* 217 */ "expr ::= expr PTR expr", + /* 218 */ "between_op ::= BETWEEN", + /* 219 */ "between_op ::= NOT BETWEEN", + /* 220 */ "expr ::= expr between_op expr AND expr", + /* 221 */ "in_op ::= IN", + /* 222 */ "in_op ::= NOT IN", + /* 223 */ "expr ::= expr in_op LP exprlist RP", + /* 224 */ "expr ::= LP select RP", + /* 225 */ "expr ::= expr in_op LP select RP", + /* 226 */ "expr ::= expr in_op nm dbnm paren_exprlist", + /* 227 */ "expr ::= EXISTS LP select RP", + /* 228 */ "expr ::= CASE case_operand case_exprlist case_else END", + /* 229 */ "case_exprlist ::= case_exprlist WHEN expr THEN expr", + /* 230 */ "case_exprlist ::= WHEN expr THEN expr", + /* 231 */ "case_else ::= ELSE expr", + /* 232 */ "case_else ::=", + /* 233 */ "case_operand ::=", + /* 234 */ "exprlist ::=", + /* 235 */ "nexprlist ::= nexprlist COMMA expr", + /* 236 */ "nexprlist ::= expr", + /* 237 */ "paren_exprlist ::=", + /* 238 */ "paren_exprlist ::= LP exprlist RP", + /* 239 */ "cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt", + /* 240 */ "uniqueflag ::= UNIQUE", + /* 241 */ "uniqueflag ::=", + /* 242 */ "eidlist_opt ::=", + /* 243 */ "eidlist_opt ::= LP eidlist RP", + /* 244 */ "eidlist ::= eidlist COMMA nm collate sortorder", + /* 245 */ "eidlist ::= nm collate sortorder", + /* 246 */ "collate ::=", + /* 247 */ "collate ::= COLLATE ID|STRING", + /* 248 */ "cmd ::= DROP INDEX ifexists fullname", + /* 249 */ "cmd ::= VACUUM vinto", + /* 250 */ "cmd ::= VACUUM nm vinto", + /* 251 */ "vinto ::= INTO expr", + /* 252 */ "vinto ::=", + /* 253 */ "cmd ::= PRAGMA nm dbnm", + /* 254 */ "cmd ::= PRAGMA nm dbnm EQ nmnum", + /* 255 */ "cmd ::= PRAGMA nm dbnm LP nmnum RP", + /* 256 */ "cmd ::= PRAGMA nm dbnm EQ minus_num", + /* 257 */ "cmd ::= PRAGMA nm dbnm LP minus_num RP", + /* 258 */ "plus_num ::= PLUS INTEGER|FLOAT", + /* 259 */ "minus_num ::= MINUS INTEGER|FLOAT", + /* 260 */ "cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END", + /* 261 */ "trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause", + /* 262 */ "trigger_time ::= BEFORE|AFTER", + /* 263 */ "trigger_time ::= INSTEAD OF", + /* 264 */ "trigger_time ::=", + /* 265 */ "trigger_event ::= DELETE|INSERT", + /* 266 */ "trigger_event ::= UPDATE", + /* 267 */ "trigger_event ::= UPDATE OF idlist", + /* 268 */ "when_clause ::=", + /* 269 */ "when_clause ::= WHEN expr", + /* 270 */ "trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI", + /* 271 */ "trigger_cmd_list ::= trigger_cmd SEMI", + /* 272 */ "trnm ::= nm DOT nm", + /* 273 */ "tridxby ::= INDEXED BY nm", + /* 274 */ "tridxby ::= NOT INDEXED", + /* 275 */ "trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt", + /* 276 */ "trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt", + /* 277 */ "trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt", + /* 278 */ "trigger_cmd ::= scanpt select scanpt", + /* 279 */ "expr ::= RAISE LP IGNORE RP", + /* 280 */ "expr ::= RAISE LP raisetype COMMA nm RP", + /* 281 */ "raisetype ::= ROLLBACK", + /* 282 */ "raisetype ::= ABORT", + /* 283 */ "raisetype ::= FAIL", + /* 284 */ "cmd ::= DROP TRIGGER ifexists fullname", + /* 285 */ "cmd ::= ATTACH database_kw_opt expr AS expr key_opt", + /* 286 */ "cmd ::= DETACH database_kw_opt expr", + /* 287 */ "key_opt ::=", + /* 288 */ "key_opt ::= KEY expr", + /* 289 */ "cmd ::= REINDEX", + /* 290 */ "cmd ::= REINDEX nm dbnm", + /* 291 */ "cmd ::= ANALYZE", + /* 292 */ "cmd ::= ANALYZE nm dbnm", + /* 293 */ "cmd ::= ALTER TABLE fullname RENAME TO nm", + /* 294 */ "cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist", + /* 295 */ "cmd ::= ALTER TABLE fullname DROP kwcolumn_opt nm", + /* 296 */ "add_column_fullname ::= fullname", + /* 297 */ "cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm", + /* 298 */ "cmd ::= create_vtab", + /* 299 */ "cmd ::= create_vtab LP vtabarglist RP", + /* 300 */ "create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm", + /* 301 */ "vtabarg ::=", + /* 302 */ "vtabargtoken ::= ANY", + /* 303 */ "vtabargtoken ::= lp anylist RP", + /* 304 */ "lp ::= LP", + /* 305 */ "with ::= WITH wqlist", + /* 306 */ "with ::= WITH RECURSIVE wqlist", + /* 307 */ "wqas ::= AS", + /* 308 */ "wqas ::= AS MATERIALIZED", + /* 309 */ "wqas ::= AS NOT MATERIALIZED", + /* 310 */ "wqitem ::= withnm eidlist_opt wqas LP select RP", + /* 311 */ "withnm ::= nm", + /* 312 */ "wqlist ::= wqitem", + /* 313 */ "wqlist ::= wqlist COMMA wqitem", + /* 314 */ "windowdefn_list ::= windowdefn_list COMMA windowdefn", + /* 315 */ "windowdefn ::= nm AS LP window RP", + /* 316 */ "window ::= PARTITION BY nexprlist orderby_opt frame_opt", + /* 317 */ "window ::= nm PARTITION BY nexprlist orderby_opt frame_opt", + /* 318 */ "window ::= ORDER BY sortlist frame_opt", + /* 319 */ "window ::= nm ORDER BY sortlist frame_opt", + /* 320 */ "window ::= nm frame_opt", + /* 321 */ "frame_opt ::=", + /* 322 */ "frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt", + /* 323 */ "frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt", + /* 324 */ "range_or_rows ::= RANGE|ROWS|GROUPS", + /* 325 */ "frame_bound_s ::= frame_bound", + /* 326 */ "frame_bound_s ::= UNBOUNDED PRECEDING", + /* 327 */ "frame_bound_e ::= frame_bound", + /* 328 */ "frame_bound_e ::= UNBOUNDED FOLLOWING", + /* 329 */ "frame_bound ::= expr PRECEDING|FOLLOWING", + /* 330 */ "frame_bound ::= CURRENT ROW", + /* 331 */ "frame_exclude_opt ::=", + /* 332 */ "frame_exclude_opt ::= EXCLUDE frame_exclude", + /* 333 */ "frame_exclude ::= NO OTHERS", + /* 334 */ "frame_exclude ::= CURRENT ROW", + /* 335 */ "frame_exclude ::= GROUP|TIES", + /* 336 */ "window_clause ::= WINDOW windowdefn_list", + /* 337 */ "filter_over ::= filter_clause over_clause", + /* 338 */ "filter_over ::= over_clause", + /* 339 */ "filter_over ::= filter_clause", + /* 340 */ "over_clause ::= OVER LP window RP", + /* 341 */ "over_clause ::= OVER nm", + /* 342 */ "filter_clause ::= FILTER LP WHERE expr RP", + /* 343 */ "term ::= QNUMBER", + /* 344 */ "input ::= cmdlist", + /* 345 */ "cmdlist ::= cmdlist ecmd", + /* 346 */ "cmdlist ::= ecmd", + /* 347 */ "ecmd ::= SEMI", + /* 348 */ "ecmd ::= cmdx SEMI", + /* 349 */ "ecmd ::= explain cmdx SEMI", + /* 350 */ "trans_opt ::=", + /* 351 */ "trans_opt ::= TRANSACTION", + /* 352 */ "trans_opt ::= TRANSACTION nm", + /* 353 */ "savepoint_opt ::= SAVEPOINT", + /* 354 */ "savepoint_opt ::=", + /* 355 */ "cmd ::= create_table create_table_args", + /* 356 */ "table_option_set ::= table_option", + /* 357 */ "columnlist ::= columnlist COMMA columnname carglist", + /* 358 */ "columnlist ::= columnname carglist", + /* 359 */ "nm ::= ID|INDEXED|JOIN_KW", + /* 360 */ "nm ::= STRING", + /* 361 */ "typetoken ::= typename", + /* 362 */ "typename ::= ID|STRING", + /* 363 */ "signed ::= plus_num", + /* 364 */ "signed ::= minus_num", + /* 365 */ "carglist ::= carglist ccons", + /* 366 */ "carglist ::=", + /* 367 */ "ccons ::= NULL onconf", + /* 368 */ "ccons ::= GENERATED ALWAYS AS generated", + /* 369 */ "ccons ::= AS generated", + /* 370 */ "conslist_opt ::= COMMA conslist", + /* 371 */ "conslist ::= conslist tconscomma tcons", + /* 372 */ "conslist ::= tcons", + /* 373 */ "tconscomma ::=", + /* 374 */ "defer_subclause_opt ::= defer_subclause", + /* 375 */ "resolvetype ::= raisetype", + /* 376 */ "selectnowith ::= oneselect", + /* 377 */ "oneselect ::= values", + /* 378 */ "sclp ::= selcollist COMMA", + /* 379 */ "as ::= ID|STRING", + /* 380 */ "indexed_opt ::= indexed_by", + /* 381 */ "returning ::=", + /* 382 */ "expr ::= term", + /* 383 */ "likeop ::= LIKE_KW|MATCH", + /* 384 */ "case_operand ::= expr", + /* 385 */ "exprlist ::= nexprlist", + /* 386 */ "nmnum ::= plus_num", + /* 387 */ "nmnum ::= nm", + /* 388 */ "nmnum ::= ON", + /* 389 */ "nmnum ::= DELETE", + /* 390 */ "nmnum ::= DEFAULT", + /* 391 */ "plus_num ::= INTEGER|FLOAT", + /* 392 */ "foreach_clause ::=", + /* 393 */ "foreach_clause ::= FOR EACH ROW", + /* 394 */ "trnm ::= nm", + /* 395 */ "tridxby ::=", + /* 396 */ "database_kw_opt ::= DATABASE", + /* 397 */ "database_kw_opt ::=", + /* 398 */ "kwcolumn_opt ::=", + /* 399 */ "kwcolumn_opt ::= COLUMNKW", + /* 400 */ "vtabarglist ::= vtabarg", + /* 401 */ "vtabarglist ::= vtabarglist COMMA vtabarg", + /* 402 */ "vtabarg ::= vtabarg vtabargtoken", + /* 403 */ "anylist ::=", + /* 404 */ "anylist ::= anylist LP anylist RP", + /* 405 */ "anylist ::= anylist ANY", + /* 406 */ "with ::=", + /* 407 */ "windowdefn_list ::= windowdefn", + /* 408 */ "window ::= frame_opt", }; #endif /* NDEBUG */ -#if YYSTACKDEPTH<=0 +#if YYGROWABLESTACK /* ** Try to increase the size of the parser stack. Return the number ** of errors. Return 0 on success. */ static int yyGrowStack(yyParser *p){ + int oldSize = 1 + (int)(p->yystackEnd - p->yystack); int newSize; int idx; yyStackEntry *pNew; - newSize = p->yystksz*2 + 100; - idx = p->yytos ? (int)(p->yytos - p->yystack) : 0; - if( p->yystack==&p->yystk0 ){ - pNew = malloc(newSize*sizeof(pNew[0])); - if( pNew ) pNew[0] = p->yystk0; + newSize = oldSize*2 + 100; + idx = (int)(p->yytos - p->yystack); + if( p->yystack==p->yystk0 ){ + pNew = YYREALLOC(0, newSize*sizeof(pNew[0])); + if( pNew==0 ) return 1; + memcpy(pNew, p->yystack, oldSize*sizeof(pNew[0])); }else{ - pNew = realloc(p->yystack, newSize*sizeof(pNew[0])); + pNew = YYREALLOC(p->yystack, newSize*sizeof(pNew[0])); + if( pNew==0 ) return 1; } - if( pNew ){ - p->yystack = pNew; - p->yytos = &p->yystack[idx]; + p->yystack = pNew; + p->yytos = &p->yystack[idx]; #ifndef NDEBUG - if( yyTraceFILE ){ - fprintf(yyTraceFILE,"%sStack grows from %d to %d entries.\n", - yyTracePrompt, p->yystksz, newSize); - } -#endif - p->yystksz = newSize; + if( yyTraceFILE ){ + fprintf(yyTraceFILE,"%sStack grows from %d to %d entries.\n", + yyTracePrompt, oldSize, newSize); } - return pNew==0; +#endif + p->yystackEnd = &p->yystack[newSize-1]; + return 0; } +#endif /* YYGROWABLESTACK */ + +#if !YYGROWABLESTACK +/* For builds that do no have a growable stack, yyGrowStack always +** returns an error. +*/ +# define yyGrowStack(X) 1 #endif /* Datatype of the argument to the memory allocated passed as the @@ -173406,24 +174798,14 @@ SQLITE_PRIVATE void sqlite3ParserInit(void *yypRawParser sqlite3ParserCTX_PDECL) #ifdef YYTRACKMAXSTACKDEPTH yypParser->yyhwm = 0; #endif -#if YYSTACKDEPTH<=0 - yypParser->yytos = NULL; - yypParser->yystack = NULL; - yypParser->yystksz = 0; - if( yyGrowStack(yypParser) ){ - yypParser->yystack = &yypParser->yystk0; - yypParser->yystksz = 1; - } -#endif + yypParser->yystack = yypParser->yystk0; + yypParser->yystackEnd = &yypParser->yystack[YYSTACKDEPTH-1]; #ifndef YYNOERRORRECOVERY yypParser->yyerrcnt = -1; #endif yypParser->yytos = yypParser->yystack; yypParser->yystack[0].stateno = 0; yypParser->yystack[0].major = 0; -#if YYSTACKDEPTH>0 - yypParser->yystackEnd = &yypParser->yystack[YYSTACKDEPTH-1]; -#endif } #ifndef sqlite3Parser_ENGINEALWAYSONSTACK @@ -173477,97 +174859,98 @@ static void yy_destructor( ** inside the C code. */ /********* Begin destructor definitions ***************************************/ - case 204: /* select */ - case 239: /* selectnowith */ - case 240: /* oneselect */ - case 252: /* values */ + case 205: /* select */ + case 240: /* selectnowith */ + case 241: /* oneselect */ + case 253: /* values */ + case 255: /* mvalues */ { -sqlite3SelectDelete(pParse->db, (yypminor->yy47)); +sqlite3SelectDelete(pParse->db, (yypminor->yy555)); } break; - case 216: /* term */ - case 217: /* expr */ - case 246: /* where_opt */ - case 248: /* having_opt */ - case 267: /* where_opt_ret */ - case 278: /* case_operand */ - case 280: /* case_else */ - case 283: /* vinto */ - case 290: /* when_clause */ - case 295: /* key_opt */ - case 311: /* filter_clause */ + case 217: /* term */ + case 218: /* expr */ + case 247: /* where_opt */ + case 249: /* having_opt */ + case 269: /* where_opt_ret */ + case 280: /* case_operand */ + case 282: /* case_else */ + case 285: /* vinto */ + case 292: /* when_clause */ + case 297: /* key_opt */ + case 314: /* filter_clause */ { -sqlite3ExprDelete(pParse->db, (yypminor->yy528)); +sqlite3ExprDelete(pParse->db, (yypminor->yy454)); } break; - case 221: /* eidlist_opt */ - case 231: /* sortlist */ - case 232: /* eidlist */ - case 244: /* selcollist */ - case 247: /* groupby_opt */ - case 249: /* orderby_opt */ - case 253: /* nexprlist */ - case 254: /* sclp */ - case 261: /* exprlist */ - case 268: /* setlist */ - case 277: /* paren_exprlist */ - case 279: /* case_exprlist */ - case 310: /* part_opt */ + case 222: /* eidlist_opt */ + case 232: /* sortlist */ + case 233: /* eidlist */ + case 245: /* selcollist */ + case 248: /* groupby_opt */ + case 250: /* orderby_opt */ + case 254: /* nexprlist */ + case 256: /* sclp */ + case 263: /* exprlist */ + case 270: /* setlist */ + case 279: /* paren_exprlist */ + case 281: /* case_exprlist */ + case 313: /* part_opt */ { -sqlite3ExprListDelete(pParse->db, (yypminor->yy322)); +sqlite3ExprListDelete(pParse->db, (yypminor->yy14)); } break; - case 238: /* fullname */ - case 245: /* from */ - case 256: /* seltablist */ - case 257: /* stl_prefix */ - case 262: /* xfullname */ + case 239: /* fullname */ + case 246: /* from */ + case 258: /* seltablist */ + case 259: /* stl_prefix */ + case 264: /* xfullname */ { -sqlite3SrcListDelete(pParse->db, (yypminor->yy131)); +sqlite3SrcListDelete(pParse->db, (yypminor->yy203)); } break; - case 241: /* wqlist */ + case 242: /* wqlist */ { -sqlite3WithDelete(pParse->db, (yypminor->yy521)); +sqlite3WithDelete(pParse->db, (yypminor->yy59)); } break; - case 251: /* window_clause */ - case 306: /* windowdefn_list */ + case 252: /* window_clause */ + case 309: /* windowdefn_list */ { -sqlite3WindowListDelete(pParse->db, (yypminor->yy41)); +sqlite3WindowListDelete(pParse->db, (yypminor->yy211)); } break; - case 263: /* idlist */ - case 270: /* idlist_opt */ + case 265: /* idlist */ + case 272: /* idlist_opt */ { -sqlite3IdListDelete(pParse->db, (yypminor->yy254)); +sqlite3IdListDelete(pParse->db, (yypminor->yy132)); } break; - case 273: /* filter_over */ - case 307: /* windowdefn */ - case 308: /* window */ - case 309: /* frame_opt */ - case 312: /* over_clause */ + case 275: /* filter_over */ + case 310: /* windowdefn */ + case 311: /* window */ + case 312: /* frame_opt */ + case 315: /* over_clause */ { -sqlite3WindowDelete(pParse->db, (yypminor->yy41)); +sqlite3WindowDelete(pParse->db, (yypminor->yy211)); } break; - case 286: /* trigger_cmd_list */ - case 291: /* trigger_cmd */ + case 288: /* trigger_cmd_list */ + case 293: /* trigger_cmd */ { -sqlite3DeleteTriggerStep(pParse->db, (yypminor->yy33)); +sqlite3DeleteTriggerStep(pParse->db, (yypminor->yy427)); } break; - case 288: /* trigger_event */ + case 290: /* trigger_event */ { -sqlite3IdListDelete(pParse->db, (yypminor->yy180).b); +sqlite3IdListDelete(pParse->db, (yypminor->yy286).b); } break; - case 314: /* frame_bound */ - case 315: /* frame_bound_s */ - case 316: /* frame_bound_e */ + case 317: /* frame_bound */ + case 318: /* frame_bound_s */ + case 319: /* frame_bound_e */ { -sqlite3ExprDelete(pParse->db, (yypminor->yy595).pExpr); +sqlite3ExprDelete(pParse->db, (yypminor->yy509).pExpr); } break; /********* End destructor definitions *****************************************/ @@ -173601,9 +174984,26 @@ static void yy_pop_parser_stack(yyParser *pParser){ */ SQLITE_PRIVATE void sqlite3ParserFinalize(void *p){ yyParser *pParser = (yyParser*)p; - while( pParser->yytos>pParser->yystack ) yy_pop_parser_stack(pParser); -#if YYSTACKDEPTH<=0 - if( pParser->yystack!=&pParser->yystk0 ) free(pParser->yystack); + + /* In-lined version of calling yy_pop_parser_stack() for each + ** element left in the stack */ + yyStackEntry *yytos = pParser->yytos; + while( yytos>pParser->yystack ){ +#ifndef NDEBUG + if( yyTraceFILE ){ + fprintf(yyTraceFILE,"%sPopping %s\n", + yyTracePrompt, + yyTokenName[yytos->major]); + } +#endif + if( yytos->major>=YY_MIN_DSTRCTR ){ + yy_destructor(pParser, yytos->major, &yytos->minor); + } + yytos--; + } + +#if YYGROWABLESTACK + if( pParser->yystack!=pParser->yystk0 ) YYFREE(pParser->yystack); #endif } @@ -173786,7 +175186,7 @@ static void yyStackOverflow(yyParser *yypParser){ ** stack every overflows */ /******** Begin %stack_overflow code ******************************************/ - sqlite3ErrorMsg(pParse, "parser stack overflow"); + sqlite3OomFault(pParse->db); /******** End %stack_overflow code ********************************************/ sqlite3ParserARG_STORE /* Suppress warning about unused %extra_argument var */ sqlite3ParserCTX_STORE @@ -173830,25 +175230,19 @@ static void yy_shift( assert( yypParser->yyhwm == (int)(yypParser->yytos - yypParser->yystack) ); } #endif -#if YYSTACKDEPTH>0 - if( yypParser->yytos>yypParser->yystackEnd ){ - yypParser->yytos--; - yyStackOverflow(yypParser); - return; - } -#else - if( yypParser->yytos>=&yypParser->yystack[yypParser->yystksz] ){ + yytos = yypParser->yytos; + if( yytos>yypParser->yystackEnd ){ if( yyGrowStack(yypParser) ){ yypParser->yytos--; yyStackOverflow(yypParser); return; } + yytos = yypParser->yytos; + assert( yytos <= yypParser->yystackEnd ); } -#endif if( yyNewState > YY_MAX_SHIFT ){ yyNewState += YY_MIN_REDUCE - YY_MIN_SHIFTREDUCE; } - yytos = yypParser->yytos; yytos->stateno = yyNewState; yytos->major = yyMajor; yytos->minor.yy0 = yyMinor; @@ -173858,411 +175252,415 @@ static void yy_shift( /* For rule J, yyRuleInfoLhs[J] contains the symbol on the left-hand side ** of that rule */ static const YYCODETYPE yyRuleInfoLhs[] = { - 189, /* (0) explain ::= EXPLAIN */ - 189, /* (1) explain ::= EXPLAIN QUERY PLAN */ - 188, /* (2) cmdx ::= cmd */ - 190, /* (3) cmd ::= BEGIN transtype trans_opt */ - 191, /* (4) transtype ::= */ - 191, /* (5) transtype ::= DEFERRED */ - 191, /* (6) transtype ::= IMMEDIATE */ - 191, /* (7) transtype ::= EXCLUSIVE */ - 190, /* (8) cmd ::= COMMIT|END trans_opt */ - 190, /* (9) cmd ::= ROLLBACK trans_opt */ - 190, /* (10) cmd ::= SAVEPOINT nm */ - 190, /* (11) cmd ::= RELEASE savepoint_opt nm */ - 190, /* (12) cmd ::= ROLLBACK trans_opt TO savepoint_opt nm */ - 195, /* (13) create_table ::= createkw temp TABLE ifnotexists nm dbnm */ - 197, /* (14) createkw ::= CREATE */ - 199, /* (15) ifnotexists ::= */ - 199, /* (16) ifnotexists ::= IF NOT EXISTS */ - 198, /* (17) temp ::= TEMP */ - 198, /* (18) temp ::= */ - 196, /* (19) create_table_args ::= LP columnlist conslist_opt RP table_option_set */ - 196, /* (20) create_table_args ::= AS select */ - 203, /* (21) table_option_set ::= */ - 203, /* (22) table_option_set ::= table_option_set COMMA table_option */ - 205, /* (23) table_option ::= WITHOUT nm */ - 205, /* (24) table_option ::= nm */ - 206, /* (25) columnname ::= nm typetoken */ - 208, /* (26) typetoken ::= */ - 208, /* (27) typetoken ::= typename LP signed RP */ - 208, /* (28) typetoken ::= typename LP signed COMMA signed RP */ - 209, /* (29) typename ::= typename ID|STRING */ - 213, /* (30) scanpt ::= */ - 214, /* (31) scantok ::= */ - 215, /* (32) ccons ::= CONSTRAINT nm */ - 215, /* (33) ccons ::= DEFAULT scantok term */ - 215, /* (34) ccons ::= DEFAULT LP expr RP */ - 215, /* (35) ccons ::= DEFAULT PLUS scantok term */ - 215, /* (36) ccons ::= DEFAULT MINUS scantok term */ - 215, /* (37) ccons ::= DEFAULT scantok ID|INDEXED */ - 215, /* (38) ccons ::= NOT NULL onconf */ - 215, /* (39) ccons ::= PRIMARY KEY sortorder onconf autoinc */ - 215, /* (40) ccons ::= UNIQUE onconf */ - 215, /* (41) ccons ::= CHECK LP expr RP */ - 215, /* (42) ccons ::= REFERENCES nm eidlist_opt refargs */ - 215, /* (43) ccons ::= defer_subclause */ - 215, /* (44) ccons ::= COLLATE ID|STRING */ - 224, /* (45) generated ::= LP expr RP */ - 224, /* (46) generated ::= LP expr RP ID */ - 220, /* (47) autoinc ::= */ - 220, /* (48) autoinc ::= AUTOINCR */ - 222, /* (49) refargs ::= */ - 222, /* (50) refargs ::= refargs refarg */ - 225, /* (51) refarg ::= MATCH nm */ - 225, /* (52) refarg ::= ON INSERT refact */ - 225, /* (53) refarg ::= ON DELETE refact */ - 225, /* (54) refarg ::= ON UPDATE refact */ - 226, /* (55) refact ::= SET NULL */ - 226, /* (56) refact ::= SET DEFAULT */ - 226, /* (57) refact ::= CASCADE */ - 226, /* (58) refact ::= RESTRICT */ - 226, /* (59) refact ::= NO ACTION */ - 223, /* (60) defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt */ - 223, /* (61) defer_subclause ::= DEFERRABLE init_deferred_pred_opt */ - 227, /* (62) init_deferred_pred_opt ::= */ - 227, /* (63) init_deferred_pred_opt ::= INITIALLY DEFERRED */ - 227, /* (64) init_deferred_pred_opt ::= INITIALLY IMMEDIATE */ - 202, /* (65) conslist_opt ::= */ - 229, /* (66) tconscomma ::= COMMA */ - 230, /* (67) tcons ::= CONSTRAINT nm */ - 230, /* (68) tcons ::= PRIMARY KEY LP sortlist autoinc RP onconf */ - 230, /* (69) tcons ::= UNIQUE LP sortlist RP onconf */ - 230, /* (70) tcons ::= CHECK LP expr RP onconf */ - 230, /* (71) tcons ::= FOREIGN KEY LP eidlist RP REFERENCES nm eidlist_opt refargs defer_subclause_opt */ - 233, /* (72) defer_subclause_opt ::= */ - 218, /* (73) onconf ::= */ - 218, /* (74) onconf ::= ON CONFLICT resolvetype */ - 234, /* (75) orconf ::= */ - 234, /* (76) orconf ::= OR resolvetype */ - 235, /* (77) resolvetype ::= IGNORE */ - 235, /* (78) resolvetype ::= REPLACE */ - 190, /* (79) cmd ::= DROP TABLE ifexists fullname */ - 237, /* (80) ifexists ::= IF EXISTS */ - 237, /* (81) ifexists ::= */ - 190, /* (82) cmd ::= createkw temp VIEW ifnotexists nm dbnm eidlist_opt AS select */ - 190, /* (83) cmd ::= DROP VIEW ifexists fullname */ - 190, /* (84) cmd ::= select */ - 204, /* (85) select ::= WITH wqlist selectnowith */ - 204, /* (86) select ::= WITH RECURSIVE wqlist selectnowith */ - 204, /* (87) select ::= selectnowith */ - 239, /* (88) selectnowith ::= selectnowith multiselect_op oneselect */ - 242, /* (89) multiselect_op ::= UNION */ - 242, /* (90) multiselect_op ::= UNION ALL */ - 242, /* (91) multiselect_op ::= EXCEPT|INTERSECT */ - 240, /* (92) oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt */ - 240, /* (93) oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt window_clause orderby_opt limit_opt */ - 252, /* (94) values ::= VALUES LP nexprlist RP */ - 252, /* (95) values ::= values COMMA LP nexprlist RP */ - 243, /* (96) distinct ::= DISTINCT */ - 243, /* (97) distinct ::= ALL */ - 243, /* (98) distinct ::= */ - 254, /* (99) sclp ::= */ - 244, /* (100) selcollist ::= sclp scanpt expr scanpt as */ - 244, /* (101) selcollist ::= sclp scanpt STAR */ - 244, /* (102) selcollist ::= sclp scanpt nm DOT STAR */ - 255, /* (103) as ::= AS nm */ - 255, /* (104) as ::= */ - 245, /* (105) from ::= */ - 245, /* (106) from ::= FROM seltablist */ - 257, /* (107) stl_prefix ::= seltablist joinop */ - 257, /* (108) stl_prefix ::= */ - 256, /* (109) seltablist ::= stl_prefix nm dbnm as on_using */ - 256, /* (110) seltablist ::= stl_prefix nm dbnm as indexed_by on_using */ - 256, /* (111) seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_using */ - 256, /* (112) seltablist ::= stl_prefix LP select RP as on_using */ - 256, /* (113) seltablist ::= stl_prefix LP seltablist RP as on_using */ - 200, /* (114) dbnm ::= */ - 200, /* (115) dbnm ::= DOT nm */ - 238, /* (116) fullname ::= nm */ - 238, /* (117) fullname ::= nm DOT nm */ - 262, /* (118) xfullname ::= nm */ - 262, /* (119) xfullname ::= nm DOT nm */ - 262, /* (120) xfullname ::= nm DOT nm AS nm */ - 262, /* (121) xfullname ::= nm AS nm */ - 258, /* (122) joinop ::= COMMA|JOIN */ - 258, /* (123) joinop ::= JOIN_KW JOIN */ - 258, /* (124) joinop ::= JOIN_KW nm JOIN */ - 258, /* (125) joinop ::= JOIN_KW nm nm JOIN */ - 259, /* (126) on_using ::= ON expr */ - 259, /* (127) on_using ::= USING LP idlist RP */ - 259, /* (128) on_using ::= */ - 264, /* (129) indexed_opt ::= */ - 260, /* (130) indexed_by ::= INDEXED BY nm */ - 260, /* (131) indexed_by ::= NOT INDEXED */ - 249, /* (132) orderby_opt ::= */ - 249, /* (133) orderby_opt ::= ORDER BY sortlist */ - 231, /* (134) sortlist ::= sortlist COMMA expr sortorder nulls */ - 231, /* (135) sortlist ::= expr sortorder nulls */ - 219, /* (136) sortorder ::= ASC */ - 219, /* (137) sortorder ::= DESC */ - 219, /* (138) sortorder ::= */ - 265, /* (139) nulls ::= NULLS FIRST */ - 265, /* (140) nulls ::= NULLS LAST */ - 265, /* (141) nulls ::= */ - 247, /* (142) groupby_opt ::= */ - 247, /* (143) groupby_opt ::= GROUP BY nexprlist */ - 248, /* (144) having_opt ::= */ - 248, /* (145) having_opt ::= HAVING expr */ - 250, /* (146) limit_opt ::= */ - 250, /* (147) limit_opt ::= LIMIT expr */ - 250, /* (148) limit_opt ::= LIMIT expr OFFSET expr */ - 250, /* (149) limit_opt ::= LIMIT expr COMMA expr */ - 190, /* (150) cmd ::= with DELETE FROM xfullname indexed_opt where_opt_ret */ - 246, /* (151) where_opt ::= */ - 246, /* (152) where_opt ::= WHERE expr */ - 267, /* (153) where_opt_ret ::= */ - 267, /* (154) where_opt_ret ::= WHERE expr */ - 267, /* (155) where_opt_ret ::= RETURNING selcollist */ - 267, /* (156) where_opt_ret ::= WHERE expr RETURNING selcollist */ - 190, /* (157) cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist from where_opt_ret */ - 268, /* (158) setlist ::= setlist COMMA nm EQ expr */ - 268, /* (159) setlist ::= setlist COMMA LP idlist RP EQ expr */ - 268, /* (160) setlist ::= nm EQ expr */ - 268, /* (161) setlist ::= LP idlist RP EQ expr */ - 190, /* (162) cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert */ - 190, /* (163) cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES returning */ - 271, /* (164) upsert ::= */ - 271, /* (165) upsert ::= RETURNING selcollist */ - 271, /* (166) upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt upsert */ - 271, /* (167) upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING upsert */ - 271, /* (168) upsert ::= ON CONFLICT DO NOTHING returning */ - 271, /* (169) upsert ::= ON CONFLICT DO UPDATE SET setlist where_opt returning */ - 272, /* (170) returning ::= RETURNING selcollist */ - 269, /* (171) insert_cmd ::= INSERT orconf */ - 269, /* (172) insert_cmd ::= REPLACE */ - 270, /* (173) idlist_opt ::= */ - 270, /* (174) idlist_opt ::= LP idlist RP */ - 263, /* (175) idlist ::= idlist COMMA nm */ - 263, /* (176) idlist ::= nm */ - 217, /* (177) expr ::= LP expr RP */ - 217, /* (178) expr ::= ID|INDEXED|JOIN_KW */ - 217, /* (179) expr ::= nm DOT nm */ - 217, /* (180) expr ::= nm DOT nm DOT nm */ - 216, /* (181) term ::= NULL|FLOAT|BLOB */ - 216, /* (182) term ::= STRING */ - 216, /* (183) term ::= INTEGER */ - 217, /* (184) expr ::= VARIABLE */ - 217, /* (185) expr ::= expr COLLATE ID|STRING */ - 217, /* (186) expr ::= CAST LP expr AS typetoken RP */ - 217, /* (187) expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP */ - 217, /* (188) expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist ORDER BY sortlist RP */ - 217, /* (189) expr ::= ID|INDEXED|JOIN_KW LP STAR RP */ - 217, /* (190) expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP filter_over */ - 217, /* (191) expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist ORDER BY sortlist RP filter_over */ - 217, /* (192) expr ::= ID|INDEXED|JOIN_KW LP STAR RP filter_over */ - 216, /* (193) term ::= CTIME_KW */ - 217, /* (194) expr ::= LP nexprlist COMMA expr RP */ - 217, /* (195) expr ::= expr AND expr */ - 217, /* (196) expr ::= expr OR expr */ - 217, /* (197) expr ::= expr LT|GT|GE|LE expr */ - 217, /* (198) expr ::= expr EQ|NE expr */ - 217, /* (199) expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */ - 217, /* (200) expr ::= expr PLUS|MINUS expr */ - 217, /* (201) expr ::= expr STAR|SLASH|REM expr */ - 217, /* (202) expr ::= expr CONCAT expr */ - 274, /* (203) likeop ::= NOT LIKE_KW|MATCH */ - 217, /* (204) expr ::= expr likeop expr */ - 217, /* (205) expr ::= expr likeop expr ESCAPE expr */ - 217, /* (206) expr ::= expr ISNULL|NOTNULL */ - 217, /* (207) expr ::= expr NOT NULL */ - 217, /* (208) expr ::= expr IS expr */ - 217, /* (209) expr ::= expr IS NOT expr */ - 217, /* (210) expr ::= expr IS NOT DISTINCT FROM expr */ - 217, /* (211) expr ::= expr IS DISTINCT FROM expr */ - 217, /* (212) expr ::= NOT expr */ - 217, /* (213) expr ::= BITNOT expr */ - 217, /* (214) expr ::= PLUS|MINUS expr */ - 217, /* (215) expr ::= expr PTR expr */ - 275, /* (216) between_op ::= BETWEEN */ - 275, /* (217) between_op ::= NOT BETWEEN */ - 217, /* (218) expr ::= expr between_op expr AND expr */ - 276, /* (219) in_op ::= IN */ - 276, /* (220) in_op ::= NOT IN */ - 217, /* (221) expr ::= expr in_op LP exprlist RP */ - 217, /* (222) expr ::= LP select RP */ - 217, /* (223) expr ::= expr in_op LP select RP */ - 217, /* (224) expr ::= expr in_op nm dbnm paren_exprlist */ - 217, /* (225) expr ::= EXISTS LP select RP */ - 217, /* (226) expr ::= CASE case_operand case_exprlist case_else END */ - 279, /* (227) case_exprlist ::= case_exprlist WHEN expr THEN expr */ - 279, /* (228) case_exprlist ::= WHEN expr THEN expr */ - 280, /* (229) case_else ::= ELSE expr */ - 280, /* (230) case_else ::= */ - 278, /* (231) case_operand ::= */ - 261, /* (232) exprlist ::= */ - 253, /* (233) nexprlist ::= nexprlist COMMA expr */ - 253, /* (234) nexprlist ::= expr */ - 277, /* (235) paren_exprlist ::= */ - 277, /* (236) paren_exprlist ::= LP exprlist RP */ - 190, /* (237) cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */ - 281, /* (238) uniqueflag ::= UNIQUE */ - 281, /* (239) uniqueflag ::= */ - 221, /* (240) eidlist_opt ::= */ - 221, /* (241) eidlist_opt ::= LP eidlist RP */ - 232, /* (242) eidlist ::= eidlist COMMA nm collate sortorder */ - 232, /* (243) eidlist ::= nm collate sortorder */ - 282, /* (244) collate ::= */ - 282, /* (245) collate ::= COLLATE ID|STRING */ - 190, /* (246) cmd ::= DROP INDEX ifexists fullname */ - 190, /* (247) cmd ::= VACUUM vinto */ - 190, /* (248) cmd ::= VACUUM nm vinto */ - 283, /* (249) vinto ::= INTO expr */ - 283, /* (250) vinto ::= */ - 190, /* (251) cmd ::= PRAGMA nm dbnm */ - 190, /* (252) cmd ::= PRAGMA nm dbnm EQ nmnum */ - 190, /* (253) cmd ::= PRAGMA nm dbnm LP nmnum RP */ - 190, /* (254) cmd ::= PRAGMA nm dbnm EQ minus_num */ - 190, /* (255) cmd ::= PRAGMA nm dbnm LP minus_num RP */ - 211, /* (256) plus_num ::= PLUS INTEGER|FLOAT */ - 212, /* (257) minus_num ::= MINUS INTEGER|FLOAT */ - 190, /* (258) cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */ - 285, /* (259) trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */ - 287, /* (260) trigger_time ::= BEFORE|AFTER */ - 287, /* (261) trigger_time ::= INSTEAD OF */ - 287, /* (262) trigger_time ::= */ - 288, /* (263) trigger_event ::= DELETE|INSERT */ - 288, /* (264) trigger_event ::= UPDATE */ - 288, /* (265) trigger_event ::= UPDATE OF idlist */ - 290, /* (266) when_clause ::= */ - 290, /* (267) when_clause ::= WHEN expr */ - 286, /* (268) trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */ - 286, /* (269) trigger_cmd_list ::= trigger_cmd SEMI */ - 292, /* (270) trnm ::= nm DOT nm */ - 293, /* (271) tridxby ::= INDEXED BY nm */ - 293, /* (272) tridxby ::= NOT INDEXED */ - 291, /* (273) trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt */ - 291, /* (274) trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */ - 291, /* (275) trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */ - 291, /* (276) trigger_cmd ::= scanpt select scanpt */ - 217, /* (277) expr ::= RAISE LP IGNORE RP */ - 217, /* (278) expr ::= RAISE LP raisetype COMMA nm RP */ - 236, /* (279) raisetype ::= ROLLBACK */ - 236, /* (280) raisetype ::= ABORT */ - 236, /* (281) raisetype ::= FAIL */ - 190, /* (282) cmd ::= DROP TRIGGER ifexists fullname */ - 190, /* (283) cmd ::= ATTACH database_kw_opt expr AS expr key_opt */ - 190, /* (284) cmd ::= DETACH database_kw_opt expr */ - 295, /* (285) key_opt ::= */ - 295, /* (286) key_opt ::= KEY expr */ - 190, /* (287) cmd ::= REINDEX */ - 190, /* (288) cmd ::= REINDEX nm dbnm */ - 190, /* (289) cmd ::= ANALYZE */ - 190, /* (290) cmd ::= ANALYZE nm dbnm */ - 190, /* (291) cmd ::= ALTER TABLE fullname RENAME TO nm */ - 190, /* (292) cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */ - 190, /* (293) cmd ::= ALTER TABLE fullname DROP kwcolumn_opt nm */ - 296, /* (294) add_column_fullname ::= fullname */ - 190, /* (295) cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */ - 190, /* (296) cmd ::= create_vtab */ - 190, /* (297) cmd ::= create_vtab LP vtabarglist RP */ - 298, /* (298) create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */ - 300, /* (299) vtabarg ::= */ - 301, /* (300) vtabargtoken ::= ANY */ - 301, /* (301) vtabargtoken ::= lp anylist RP */ - 302, /* (302) lp ::= LP */ - 266, /* (303) with ::= WITH wqlist */ - 266, /* (304) with ::= WITH RECURSIVE wqlist */ - 305, /* (305) wqas ::= AS */ - 305, /* (306) wqas ::= AS MATERIALIZED */ - 305, /* (307) wqas ::= AS NOT MATERIALIZED */ - 304, /* (308) wqitem ::= nm eidlist_opt wqas LP select RP */ - 241, /* (309) wqlist ::= wqitem */ - 241, /* (310) wqlist ::= wqlist COMMA wqitem */ - 306, /* (311) windowdefn_list ::= windowdefn_list COMMA windowdefn */ - 307, /* (312) windowdefn ::= nm AS LP window RP */ - 308, /* (313) window ::= PARTITION BY nexprlist orderby_opt frame_opt */ - 308, /* (314) window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */ - 308, /* (315) window ::= ORDER BY sortlist frame_opt */ - 308, /* (316) window ::= nm ORDER BY sortlist frame_opt */ - 308, /* (317) window ::= nm frame_opt */ - 309, /* (318) frame_opt ::= */ - 309, /* (319) frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */ - 309, /* (320) frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */ - 313, /* (321) range_or_rows ::= RANGE|ROWS|GROUPS */ - 315, /* (322) frame_bound_s ::= frame_bound */ - 315, /* (323) frame_bound_s ::= UNBOUNDED PRECEDING */ - 316, /* (324) frame_bound_e ::= frame_bound */ - 316, /* (325) frame_bound_e ::= UNBOUNDED FOLLOWING */ - 314, /* (326) frame_bound ::= expr PRECEDING|FOLLOWING */ - 314, /* (327) frame_bound ::= CURRENT ROW */ - 317, /* (328) frame_exclude_opt ::= */ - 317, /* (329) frame_exclude_opt ::= EXCLUDE frame_exclude */ - 318, /* (330) frame_exclude ::= NO OTHERS */ - 318, /* (331) frame_exclude ::= CURRENT ROW */ - 318, /* (332) frame_exclude ::= GROUP|TIES */ - 251, /* (333) window_clause ::= WINDOW windowdefn_list */ - 273, /* (334) filter_over ::= filter_clause over_clause */ - 273, /* (335) filter_over ::= over_clause */ - 273, /* (336) filter_over ::= filter_clause */ - 312, /* (337) over_clause ::= OVER LP window RP */ - 312, /* (338) over_clause ::= OVER nm */ - 311, /* (339) filter_clause ::= FILTER LP WHERE expr RP */ - 185, /* (340) input ::= cmdlist */ - 186, /* (341) cmdlist ::= cmdlist ecmd */ - 186, /* (342) cmdlist ::= ecmd */ - 187, /* (343) ecmd ::= SEMI */ - 187, /* (344) ecmd ::= cmdx SEMI */ - 187, /* (345) ecmd ::= explain cmdx SEMI */ - 192, /* (346) trans_opt ::= */ - 192, /* (347) trans_opt ::= TRANSACTION */ - 192, /* (348) trans_opt ::= TRANSACTION nm */ - 194, /* (349) savepoint_opt ::= SAVEPOINT */ - 194, /* (350) savepoint_opt ::= */ - 190, /* (351) cmd ::= create_table create_table_args */ - 203, /* (352) table_option_set ::= table_option */ - 201, /* (353) columnlist ::= columnlist COMMA columnname carglist */ - 201, /* (354) columnlist ::= columnname carglist */ - 193, /* (355) nm ::= ID|INDEXED|JOIN_KW */ - 193, /* (356) nm ::= STRING */ - 208, /* (357) typetoken ::= typename */ - 209, /* (358) typename ::= ID|STRING */ - 210, /* (359) signed ::= plus_num */ - 210, /* (360) signed ::= minus_num */ - 207, /* (361) carglist ::= carglist ccons */ - 207, /* (362) carglist ::= */ - 215, /* (363) ccons ::= NULL onconf */ - 215, /* (364) ccons ::= GENERATED ALWAYS AS generated */ - 215, /* (365) ccons ::= AS generated */ - 202, /* (366) conslist_opt ::= COMMA conslist */ - 228, /* (367) conslist ::= conslist tconscomma tcons */ - 228, /* (368) conslist ::= tcons */ - 229, /* (369) tconscomma ::= */ - 233, /* (370) defer_subclause_opt ::= defer_subclause */ - 235, /* (371) resolvetype ::= raisetype */ - 239, /* (372) selectnowith ::= oneselect */ - 240, /* (373) oneselect ::= values */ - 254, /* (374) sclp ::= selcollist COMMA */ - 255, /* (375) as ::= ID|STRING */ - 264, /* (376) indexed_opt ::= indexed_by */ - 272, /* (377) returning ::= */ - 217, /* (378) expr ::= term */ - 274, /* (379) likeop ::= LIKE_KW|MATCH */ - 278, /* (380) case_operand ::= expr */ - 261, /* (381) exprlist ::= nexprlist */ - 284, /* (382) nmnum ::= plus_num */ - 284, /* (383) nmnum ::= nm */ - 284, /* (384) nmnum ::= ON */ - 284, /* (385) nmnum ::= DELETE */ - 284, /* (386) nmnum ::= DEFAULT */ - 211, /* (387) plus_num ::= INTEGER|FLOAT */ - 289, /* (388) foreach_clause ::= */ - 289, /* (389) foreach_clause ::= FOR EACH ROW */ - 292, /* (390) trnm ::= nm */ - 293, /* (391) tridxby ::= */ - 294, /* (392) database_kw_opt ::= DATABASE */ - 294, /* (393) database_kw_opt ::= */ - 297, /* (394) kwcolumn_opt ::= */ - 297, /* (395) kwcolumn_opt ::= COLUMNKW */ - 299, /* (396) vtabarglist ::= vtabarg */ - 299, /* (397) vtabarglist ::= vtabarglist COMMA vtabarg */ - 300, /* (398) vtabarg ::= vtabarg vtabargtoken */ - 303, /* (399) anylist ::= */ - 303, /* (400) anylist ::= anylist LP anylist RP */ - 303, /* (401) anylist ::= anylist ANY */ - 266, /* (402) with ::= */ - 306, /* (403) windowdefn_list ::= windowdefn */ - 308, /* (404) window ::= frame_opt */ + 190, /* (0) explain ::= EXPLAIN */ + 190, /* (1) explain ::= EXPLAIN QUERY PLAN */ + 189, /* (2) cmdx ::= cmd */ + 191, /* (3) cmd ::= BEGIN transtype trans_opt */ + 192, /* (4) transtype ::= */ + 192, /* (5) transtype ::= DEFERRED */ + 192, /* (6) transtype ::= IMMEDIATE */ + 192, /* (7) transtype ::= EXCLUSIVE */ + 191, /* (8) cmd ::= COMMIT|END trans_opt */ + 191, /* (9) cmd ::= ROLLBACK trans_opt */ + 191, /* (10) cmd ::= SAVEPOINT nm */ + 191, /* (11) cmd ::= RELEASE savepoint_opt nm */ + 191, /* (12) cmd ::= ROLLBACK trans_opt TO savepoint_opt nm */ + 196, /* (13) create_table ::= createkw temp TABLE ifnotexists nm dbnm */ + 198, /* (14) createkw ::= CREATE */ + 200, /* (15) ifnotexists ::= */ + 200, /* (16) ifnotexists ::= IF NOT EXISTS */ + 199, /* (17) temp ::= TEMP */ + 199, /* (18) temp ::= */ + 197, /* (19) create_table_args ::= LP columnlist conslist_opt RP table_option_set */ + 197, /* (20) create_table_args ::= AS select */ + 204, /* (21) table_option_set ::= */ + 204, /* (22) table_option_set ::= table_option_set COMMA table_option */ + 206, /* (23) table_option ::= WITHOUT nm */ + 206, /* (24) table_option ::= nm */ + 207, /* (25) columnname ::= nm typetoken */ + 209, /* (26) typetoken ::= */ + 209, /* (27) typetoken ::= typename LP signed RP */ + 209, /* (28) typetoken ::= typename LP signed COMMA signed RP */ + 210, /* (29) typename ::= typename ID|STRING */ + 214, /* (30) scanpt ::= */ + 215, /* (31) scantok ::= */ + 216, /* (32) ccons ::= CONSTRAINT nm */ + 216, /* (33) ccons ::= DEFAULT scantok term */ + 216, /* (34) ccons ::= DEFAULT LP expr RP */ + 216, /* (35) ccons ::= DEFAULT PLUS scantok term */ + 216, /* (36) ccons ::= DEFAULT MINUS scantok term */ + 216, /* (37) ccons ::= DEFAULT scantok ID|INDEXED */ + 216, /* (38) ccons ::= NOT NULL onconf */ + 216, /* (39) ccons ::= PRIMARY KEY sortorder onconf autoinc */ + 216, /* (40) ccons ::= UNIQUE onconf */ + 216, /* (41) ccons ::= CHECK LP expr RP */ + 216, /* (42) ccons ::= REFERENCES nm eidlist_opt refargs */ + 216, /* (43) ccons ::= defer_subclause */ + 216, /* (44) ccons ::= COLLATE ID|STRING */ + 225, /* (45) generated ::= LP expr RP */ + 225, /* (46) generated ::= LP expr RP ID */ + 221, /* (47) autoinc ::= */ + 221, /* (48) autoinc ::= AUTOINCR */ + 223, /* (49) refargs ::= */ + 223, /* (50) refargs ::= refargs refarg */ + 226, /* (51) refarg ::= MATCH nm */ + 226, /* (52) refarg ::= ON INSERT refact */ + 226, /* (53) refarg ::= ON DELETE refact */ + 226, /* (54) refarg ::= ON UPDATE refact */ + 227, /* (55) refact ::= SET NULL */ + 227, /* (56) refact ::= SET DEFAULT */ + 227, /* (57) refact ::= CASCADE */ + 227, /* (58) refact ::= RESTRICT */ + 227, /* (59) refact ::= NO ACTION */ + 224, /* (60) defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt */ + 224, /* (61) defer_subclause ::= DEFERRABLE init_deferred_pred_opt */ + 228, /* (62) init_deferred_pred_opt ::= */ + 228, /* (63) init_deferred_pred_opt ::= INITIALLY DEFERRED */ + 228, /* (64) init_deferred_pred_opt ::= INITIALLY IMMEDIATE */ + 203, /* (65) conslist_opt ::= */ + 230, /* (66) tconscomma ::= COMMA */ + 231, /* (67) tcons ::= CONSTRAINT nm */ + 231, /* (68) tcons ::= PRIMARY KEY LP sortlist autoinc RP onconf */ + 231, /* (69) tcons ::= UNIQUE LP sortlist RP onconf */ + 231, /* (70) tcons ::= CHECK LP expr RP onconf */ + 231, /* (71) tcons ::= FOREIGN KEY LP eidlist RP REFERENCES nm eidlist_opt refargs defer_subclause_opt */ + 234, /* (72) defer_subclause_opt ::= */ + 219, /* (73) onconf ::= */ + 219, /* (74) onconf ::= ON CONFLICT resolvetype */ + 235, /* (75) orconf ::= */ + 235, /* (76) orconf ::= OR resolvetype */ + 236, /* (77) resolvetype ::= IGNORE */ + 236, /* (78) resolvetype ::= REPLACE */ + 191, /* (79) cmd ::= DROP TABLE ifexists fullname */ + 238, /* (80) ifexists ::= IF EXISTS */ + 238, /* (81) ifexists ::= */ + 191, /* (82) cmd ::= createkw temp VIEW ifnotexists nm dbnm eidlist_opt AS select */ + 191, /* (83) cmd ::= DROP VIEW ifexists fullname */ + 191, /* (84) cmd ::= select */ + 205, /* (85) select ::= WITH wqlist selectnowith */ + 205, /* (86) select ::= WITH RECURSIVE wqlist selectnowith */ + 205, /* (87) select ::= selectnowith */ + 240, /* (88) selectnowith ::= selectnowith multiselect_op oneselect */ + 243, /* (89) multiselect_op ::= UNION */ + 243, /* (90) multiselect_op ::= UNION ALL */ + 243, /* (91) multiselect_op ::= EXCEPT|INTERSECT */ + 241, /* (92) oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt */ + 241, /* (93) oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt window_clause orderby_opt limit_opt */ + 253, /* (94) values ::= VALUES LP nexprlist RP */ + 241, /* (95) oneselect ::= mvalues */ + 255, /* (96) mvalues ::= values COMMA LP nexprlist RP */ + 255, /* (97) mvalues ::= mvalues COMMA LP nexprlist RP */ + 244, /* (98) distinct ::= DISTINCT */ + 244, /* (99) distinct ::= ALL */ + 244, /* (100) distinct ::= */ + 256, /* (101) sclp ::= */ + 245, /* (102) selcollist ::= sclp scanpt expr scanpt as */ + 245, /* (103) selcollist ::= sclp scanpt STAR */ + 245, /* (104) selcollist ::= sclp scanpt nm DOT STAR */ + 257, /* (105) as ::= AS nm */ + 257, /* (106) as ::= */ + 246, /* (107) from ::= */ + 246, /* (108) from ::= FROM seltablist */ + 259, /* (109) stl_prefix ::= seltablist joinop */ + 259, /* (110) stl_prefix ::= */ + 258, /* (111) seltablist ::= stl_prefix nm dbnm as on_using */ + 258, /* (112) seltablist ::= stl_prefix nm dbnm as indexed_by on_using */ + 258, /* (113) seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_using */ + 258, /* (114) seltablist ::= stl_prefix LP select RP as on_using */ + 258, /* (115) seltablist ::= stl_prefix LP seltablist RP as on_using */ + 201, /* (116) dbnm ::= */ + 201, /* (117) dbnm ::= DOT nm */ + 239, /* (118) fullname ::= nm */ + 239, /* (119) fullname ::= nm DOT nm */ + 264, /* (120) xfullname ::= nm */ + 264, /* (121) xfullname ::= nm DOT nm */ + 264, /* (122) xfullname ::= nm DOT nm AS nm */ + 264, /* (123) xfullname ::= nm AS nm */ + 260, /* (124) joinop ::= COMMA|JOIN */ + 260, /* (125) joinop ::= JOIN_KW JOIN */ + 260, /* (126) joinop ::= JOIN_KW nm JOIN */ + 260, /* (127) joinop ::= JOIN_KW nm nm JOIN */ + 261, /* (128) on_using ::= ON expr */ + 261, /* (129) on_using ::= USING LP idlist RP */ + 261, /* (130) on_using ::= */ + 266, /* (131) indexed_opt ::= */ + 262, /* (132) indexed_by ::= INDEXED BY nm */ + 262, /* (133) indexed_by ::= NOT INDEXED */ + 250, /* (134) orderby_opt ::= */ + 250, /* (135) orderby_opt ::= ORDER BY sortlist */ + 232, /* (136) sortlist ::= sortlist COMMA expr sortorder nulls */ + 232, /* (137) sortlist ::= expr sortorder nulls */ + 220, /* (138) sortorder ::= ASC */ + 220, /* (139) sortorder ::= DESC */ + 220, /* (140) sortorder ::= */ + 267, /* (141) nulls ::= NULLS FIRST */ + 267, /* (142) nulls ::= NULLS LAST */ + 267, /* (143) nulls ::= */ + 248, /* (144) groupby_opt ::= */ + 248, /* (145) groupby_opt ::= GROUP BY nexprlist */ + 249, /* (146) having_opt ::= */ + 249, /* (147) having_opt ::= HAVING expr */ + 251, /* (148) limit_opt ::= */ + 251, /* (149) limit_opt ::= LIMIT expr */ + 251, /* (150) limit_opt ::= LIMIT expr OFFSET expr */ + 251, /* (151) limit_opt ::= LIMIT expr COMMA expr */ + 191, /* (152) cmd ::= with DELETE FROM xfullname indexed_opt where_opt_ret */ + 247, /* (153) where_opt ::= */ + 247, /* (154) where_opt ::= WHERE expr */ + 269, /* (155) where_opt_ret ::= */ + 269, /* (156) where_opt_ret ::= WHERE expr */ + 269, /* (157) where_opt_ret ::= RETURNING selcollist */ + 269, /* (158) where_opt_ret ::= WHERE expr RETURNING selcollist */ + 191, /* (159) cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist from where_opt_ret */ + 270, /* (160) setlist ::= setlist COMMA nm EQ expr */ + 270, /* (161) setlist ::= setlist COMMA LP idlist RP EQ expr */ + 270, /* (162) setlist ::= nm EQ expr */ + 270, /* (163) setlist ::= LP idlist RP EQ expr */ + 191, /* (164) cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert */ + 191, /* (165) cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES returning */ + 273, /* (166) upsert ::= */ + 273, /* (167) upsert ::= RETURNING selcollist */ + 273, /* (168) upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt upsert */ + 273, /* (169) upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING upsert */ + 273, /* (170) upsert ::= ON CONFLICT DO NOTHING returning */ + 273, /* (171) upsert ::= ON CONFLICT DO UPDATE SET setlist where_opt returning */ + 274, /* (172) returning ::= RETURNING selcollist */ + 271, /* (173) insert_cmd ::= INSERT orconf */ + 271, /* (174) insert_cmd ::= REPLACE */ + 272, /* (175) idlist_opt ::= */ + 272, /* (176) idlist_opt ::= LP idlist RP */ + 265, /* (177) idlist ::= idlist COMMA nm */ + 265, /* (178) idlist ::= nm */ + 218, /* (179) expr ::= LP expr RP */ + 218, /* (180) expr ::= ID|INDEXED|JOIN_KW */ + 218, /* (181) expr ::= nm DOT nm */ + 218, /* (182) expr ::= nm DOT nm DOT nm */ + 217, /* (183) term ::= NULL|FLOAT|BLOB */ + 217, /* (184) term ::= STRING */ + 217, /* (185) term ::= INTEGER */ + 218, /* (186) expr ::= VARIABLE */ + 218, /* (187) expr ::= expr COLLATE ID|STRING */ + 218, /* (188) expr ::= CAST LP expr AS typetoken RP */ + 218, /* (189) expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP */ + 218, /* (190) expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist ORDER BY sortlist RP */ + 218, /* (191) expr ::= ID|INDEXED|JOIN_KW LP STAR RP */ + 218, /* (192) expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP filter_over */ + 218, /* (193) expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist ORDER BY sortlist RP filter_over */ + 218, /* (194) expr ::= ID|INDEXED|JOIN_KW LP STAR RP filter_over */ + 217, /* (195) term ::= CTIME_KW */ + 218, /* (196) expr ::= LP nexprlist COMMA expr RP */ + 218, /* (197) expr ::= expr AND expr */ + 218, /* (198) expr ::= expr OR expr */ + 218, /* (199) expr ::= expr LT|GT|GE|LE expr */ + 218, /* (200) expr ::= expr EQ|NE expr */ + 218, /* (201) expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */ + 218, /* (202) expr ::= expr PLUS|MINUS expr */ + 218, /* (203) expr ::= expr STAR|SLASH|REM expr */ + 218, /* (204) expr ::= expr CONCAT expr */ + 276, /* (205) likeop ::= NOT LIKE_KW|MATCH */ + 218, /* (206) expr ::= expr likeop expr */ + 218, /* (207) expr ::= expr likeop expr ESCAPE expr */ + 218, /* (208) expr ::= expr ISNULL|NOTNULL */ + 218, /* (209) expr ::= expr NOT NULL */ + 218, /* (210) expr ::= expr IS expr */ + 218, /* (211) expr ::= expr IS NOT expr */ + 218, /* (212) expr ::= expr IS NOT DISTINCT FROM expr */ + 218, /* (213) expr ::= expr IS DISTINCT FROM expr */ + 218, /* (214) expr ::= NOT expr */ + 218, /* (215) expr ::= BITNOT expr */ + 218, /* (216) expr ::= PLUS|MINUS expr */ + 218, /* (217) expr ::= expr PTR expr */ + 277, /* (218) between_op ::= BETWEEN */ + 277, /* (219) between_op ::= NOT BETWEEN */ + 218, /* (220) expr ::= expr between_op expr AND expr */ + 278, /* (221) in_op ::= IN */ + 278, /* (222) in_op ::= NOT IN */ + 218, /* (223) expr ::= expr in_op LP exprlist RP */ + 218, /* (224) expr ::= LP select RP */ + 218, /* (225) expr ::= expr in_op LP select RP */ + 218, /* (226) expr ::= expr in_op nm dbnm paren_exprlist */ + 218, /* (227) expr ::= EXISTS LP select RP */ + 218, /* (228) expr ::= CASE case_operand case_exprlist case_else END */ + 281, /* (229) case_exprlist ::= case_exprlist WHEN expr THEN expr */ + 281, /* (230) case_exprlist ::= WHEN expr THEN expr */ + 282, /* (231) case_else ::= ELSE expr */ + 282, /* (232) case_else ::= */ + 280, /* (233) case_operand ::= */ + 263, /* (234) exprlist ::= */ + 254, /* (235) nexprlist ::= nexprlist COMMA expr */ + 254, /* (236) nexprlist ::= expr */ + 279, /* (237) paren_exprlist ::= */ + 279, /* (238) paren_exprlist ::= LP exprlist RP */ + 191, /* (239) cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */ + 283, /* (240) uniqueflag ::= UNIQUE */ + 283, /* (241) uniqueflag ::= */ + 222, /* (242) eidlist_opt ::= */ + 222, /* (243) eidlist_opt ::= LP eidlist RP */ + 233, /* (244) eidlist ::= eidlist COMMA nm collate sortorder */ + 233, /* (245) eidlist ::= nm collate sortorder */ + 284, /* (246) collate ::= */ + 284, /* (247) collate ::= COLLATE ID|STRING */ + 191, /* (248) cmd ::= DROP INDEX ifexists fullname */ + 191, /* (249) cmd ::= VACUUM vinto */ + 191, /* (250) cmd ::= VACUUM nm vinto */ + 285, /* (251) vinto ::= INTO expr */ + 285, /* (252) vinto ::= */ + 191, /* (253) cmd ::= PRAGMA nm dbnm */ + 191, /* (254) cmd ::= PRAGMA nm dbnm EQ nmnum */ + 191, /* (255) cmd ::= PRAGMA nm dbnm LP nmnum RP */ + 191, /* (256) cmd ::= PRAGMA nm dbnm EQ minus_num */ + 191, /* (257) cmd ::= PRAGMA nm dbnm LP minus_num RP */ + 212, /* (258) plus_num ::= PLUS INTEGER|FLOAT */ + 213, /* (259) minus_num ::= MINUS INTEGER|FLOAT */ + 191, /* (260) cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */ + 287, /* (261) trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */ + 289, /* (262) trigger_time ::= BEFORE|AFTER */ + 289, /* (263) trigger_time ::= INSTEAD OF */ + 289, /* (264) trigger_time ::= */ + 290, /* (265) trigger_event ::= DELETE|INSERT */ + 290, /* (266) trigger_event ::= UPDATE */ + 290, /* (267) trigger_event ::= UPDATE OF idlist */ + 292, /* (268) when_clause ::= */ + 292, /* (269) when_clause ::= WHEN expr */ + 288, /* (270) trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */ + 288, /* (271) trigger_cmd_list ::= trigger_cmd SEMI */ + 294, /* (272) trnm ::= nm DOT nm */ + 295, /* (273) tridxby ::= INDEXED BY nm */ + 295, /* (274) tridxby ::= NOT INDEXED */ + 293, /* (275) trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt */ + 293, /* (276) trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */ + 293, /* (277) trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */ + 293, /* (278) trigger_cmd ::= scanpt select scanpt */ + 218, /* (279) expr ::= RAISE LP IGNORE RP */ + 218, /* (280) expr ::= RAISE LP raisetype COMMA nm RP */ + 237, /* (281) raisetype ::= ROLLBACK */ + 237, /* (282) raisetype ::= ABORT */ + 237, /* (283) raisetype ::= FAIL */ + 191, /* (284) cmd ::= DROP TRIGGER ifexists fullname */ + 191, /* (285) cmd ::= ATTACH database_kw_opt expr AS expr key_opt */ + 191, /* (286) cmd ::= DETACH database_kw_opt expr */ + 297, /* (287) key_opt ::= */ + 297, /* (288) key_opt ::= KEY expr */ + 191, /* (289) cmd ::= REINDEX */ + 191, /* (290) cmd ::= REINDEX nm dbnm */ + 191, /* (291) cmd ::= ANALYZE */ + 191, /* (292) cmd ::= ANALYZE nm dbnm */ + 191, /* (293) cmd ::= ALTER TABLE fullname RENAME TO nm */ + 191, /* (294) cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */ + 191, /* (295) cmd ::= ALTER TABLE fullname DROP kwcolumn_opt nm */ + 298, /* (296) add_column_fullname ::= fullname */ + 191, /* (297) cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */ + 191, /* (298) cmd ::= create_vtab */ + 191, /* (299) cmd ::= create_vtab LP vtabarglist RP */ + 300, /* (300) create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */ + 302, /* (301) vtabarg ::= */ + 303, /* (302) vtabargtoken ::= ANY */ + 303, /* (303) vtabargtoken ::= lp anylist RP */ + 304, /* (304) lp ::= LP */ + 268, /* (305) with ::= WITH wqlist */ + 268, /* (306) with ::= WITH RECURSIVE wqlist */ + 307, /* (307) wqas ::= AS */ + 307, /* (308) wqas ::= AS MATERIALIZED */ + 307, /* (309) wqas ::= AS NOT MATERIALIZED */ + 306, /* (310) wqitem ::= withnm eidlist_opt wqas LP select RP */ + 308, /* (311) withnm ::= nm */ + 242, /* (312) wqlist ::= wqitem */ + 242, /* (313) wqlist ::= wqlist COMMA wqitem */ + 309, /* (314) windowdefn_list ::= windowdefn_list COMMA windowdefn */ + 310, /* (315) windowdefn ::= nm AS LP window RP */ + 311, /* (316) window ::= PARTITION BY nexprlist orderby_opt frame_opt */ + 311, /* (317) window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */ + 311, /* (318) window ::= ORDER BY sortlist frame_opt */ + 311, /* (319) window ::= nm ORDER BY sortlist frame_opt */ + 311, /* (320) window ::= nm frame_opt */ + 312, /* (321) frame_opt ::= */ + 312, /* (322) frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */ + 312, /* (323) frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */ + 316, /* (324) range_or_rows ::= RANGE|ROWS|GROUPS */ + 318, /* (325) frame_bound_s ::= frame_bound */ + 318, /* (326) frame_bound_s ::= UNBOUNDED PRECEDING */ + 319, /* (327) frame_bound_e ::= frame_bound */ + 319, /* (328) frame_bound_e ::= UNBOUNDED FOLLOWING */ + 317, /* (329) frame_bound ::= expr PRECEDING|FOLLOWING */ + 317, /* (330) frame_bound ::= CURRENT ROW */ + 320, /* (331) frame_exclude_opt ::= */ + 320, /* (332) frame_exclude_opt ::= EXCLUDE frame_exclude */ + 321, /* (333) frame_exclude ::= NO OTHERS */ + 321, /* (334) frame_exclude ::= CURRENT ROW */ + 321, /* (335) frame_exclude ::= GROUP|TIES */ + 252, /* (336) window_clause ::= WINDOW windowdefn_list */ + 275, /* (337) filter_over ::= filter_clause over_clause */ + 275, /* (338) filter_over ::= over_clause */ + 275, /* (339) filter_over ::= filter_clause */ + 315, /* (340) over_clause ::= OVER LP window RP */ + 315, /* (341) over_clause ::= OVER nm */ + 314, /* (342) filter_clause ::= FILTER LP WHERE expr RP */ + 217, /* (343) term ::= QNUMBER */ + 186, /* (344) input ::= cmdlist */ + 187, /* (345) cmdlist ::= cmdlist ecmd */ + 187, /* (346) cmdlist ::= ecmd */ + 188, /* (347) ecmd ::= SEMI */ + 188, /* (348) ecmd ::= cmdx SEMI */ + 188, /* (349) ecmd ::= explain cmdx SEMI */ + 193, /* (350) trans_opt ::= */ + 193, /* (351) trans_opt ::= TRANSACTION */ + 193, /* (352) trans_opt ::= TRANSACTION nm */ + 195, /* (353) savepoint_opt ::= SAVEPOINT */ + 195, /* (354) savepoint_opt ::= */ + 191, /* (355) cmd ::= create_table create_table_args */ + 204, /* (356) table_option_set ::= table_option */ + 202, /* (357) columnlist ::= columnlist COMMA columnname carglist */ + 202, /* (358) columnlist ::= columnname carglist */ + 194, /* (359) nm ::= ID|INDEXED|JOIN_KW */ + 194, /* (360) nm ::= STRING */ + 209, /* (361) typetoken ::= typename */ + 210, /* (362) typename ::= ID|STRING */ + 211, /* (363) signed ::= plus_num */ + 211, /* (364) signed ::= minus_num */ + 208, /* (365) carglist ::= carglist ccons */ + 208, /* (366) carglist ::= */ + 216, /* (367) ccons ::= NULL onconf */ + 216, /* (368) ccons ::= GENERATED ALWAYS AS generated */ + 216, /* (369) ccons ::= AS generated */ + 203, /* (370) conslist_opt ::= COMMA conslist */ + 229, /* (371) conslist ::= conslist tconscomma tcons */ + 229, /* (372) conslist ::= tcons */ + 230, /* (373) tconscomma ::= */ + 234, /* (374) defer_subclause_opt ::= defer_subclause */ + 236, /* (375) resolvetype ::= raisetype */ + 240, /* (376) selectnowith ::= oneselect */ + 241, /* (377) oneselect ::= values */ + 256, /* (378) sclp ::= selcollist COMMA */ + 257, /* (379) as ::= ID|STRING */ + 266, /* (380) indexed_opt ::= indexed_by */ + 274, /* (381) returning ::= */ + 218, /* (382) expr ::= term */ + 276, /* (383) likeop ::= LIKE_KW|MATCH */ + 280, /* (384) case_operand ::= expr */ + 263, /* (385) exprlist ::= nexprlist */ + 286, /* (386) nmnum ::= plus_num */ + 286, /* (387) nmnum ::= nm */ + 286, /* (388) nmnum ::= ON */ + 286, /* (389) nmnum ::= DELETE */ + 286, /* (390) nmnum ::= DEFAULT */ + 212, /* (391) plus_num ::= INTEGER|FLOAT */ + 291, /* (392) foreach_clause ::= */ + 291, /* (393) foreach_clause ::= FOR EACH ROW */ + 294, /* (394) trnm ::= nm */ + 295, /* (395) tridxby ::= */ + 296, /* (396) database_kw_opt ::= DATABASE */ + 296, /* (397) database_kw_opt ::= */ + 299, /* (398) kwcolumn_opt ::= */ + 299, /* (399) kwcolumn_opt ::= COLUMNKW */ + 301, /* (400) vtabarglist ::= vtabarg */ + 301, /* (401) vtabarglist ::= vtabarglist COMMA vtabarg */ + 302, /* (402) vtabarg ::= vtabarg vtabargtoken */ + 305, /* (403) anylist ::= */ + 305, /* (404) anylist ::= anylist LP anylist RP */ + 305, /* (405) anylist ::= anylist ANY */ + 268, /* (406) with ::= */ + 309, /* (407) windowdefn_list ::= windowdefn */ + 311, /* (408) window ::= frame_opt */ }; /* For rule J, yyRuleInfoNRhs[J] contains the negative of the number @@ -174363,316 +175761,320 @@ static const signed char yyRuleInfoNRhs[] = { -9, /* (92) oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt */ -10, /* (93) oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt window_clause orderby_opt limit_opt */ -4, /* (94) values ::= VALUES LP nexprlist RP */ - -5, /* (95) values ::= values COMMA LP nexprlist RP */ - -1, /* (96) distinct ::= DISTINCT */ - -1, /* (97) distinct ::= ALL */ - 0, /* (98) distinct ::= */ - 0, /* (99) sclp ::= */ - -5, /* (100) selcollist ::= sclp scanpt expr scanpt as */ - -3, /* (101) selcollist ::= sclp scanpt STAR */ - -5, /* (102) selcollist ::= sclp scanpt nm DOT STAR */ - -2, /* (103) as ::= AS nm */ - 0, /* (104) as ::= */ - 0, /* (105) from ::= */ - -2, /* (106) from ::= FROM seltablist */ - -2, /* (107) stl_prefix ::= seltablist joinop */ - 0, /* (108) stl_prefix ::= */ - -5, /* (109) seltablist ::= stl_prefix nm dbnm as on_using */ - -6, /* (110) seltablist ::= stl_prefix nm dbnm as indexed_by on_using */ - -8, /* (111) seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_using */ - -6, /* (112) seltablist ::= stl_prefix LP select RP as on_using */ - -6, /* (113) seltablist ::= stl_prefix LP seltablist RP as on_using */ - 0, /* (114) dbnm ::= */ - -2, /* (115) dbnm ::= DOT nm */ - -1, /* (116) fullname ::= nm */ - -3, /* (117) fullname ::= nm DOT nm */ - -1, /* (118) xfullname ::= nm */ - -3, /* (119) xfullname ::= nm DOT nm */ - -5, /* (120) xfullname ::= nm DOT nm AS nm */ - -3, /* (121) xfullname ::= nm AS nm */ - -1, /* (122) joinop ::= COMMA|JOIN */ - -2, /* (123) joinop ::= JOIN_KW JOIN */ - -3, /* (124) joinop ::= JOIN_KW nm JOIN */ - -4, /* (125) joinop ::= JOIN_KW nm nm JOIN */ - -2, /* (126) on_using ::= ON expr */ - -4, /* (127) on_using ::= USING LP idlist RP */ - 0, /* (128) on_using ::= */ - 0, /* (129) indexed_opt ::= */ - -3, /* (130) indexed_by ::= INDEXED BY nm */ - -2, /* (131) indexed_by ::= NOT INDEXED */ - 0, /* (132) orderby_opt ::= */ - -3, /* (133) orderby_opt ::= ORDER BY sortlist */ - -5, /* (134) sortlist ::= sortlist COMMA expr sortorder nulls */ - -3, /* (135) sortlist ::= expr sortorder nulls */ - -1, /* (136) sortorder ::= ASC */ - -1, /* (137) sortorder ::= DESC */ - 0, /* (138) sortorder ::= */ - -2, /* (139) nulls ::= NULLS FIRST */ - -2, /* (140) nulls ::= NULLS LAST */ - 0, /* (141) nulls ::= */ - 0, /* (142) groupby_opt ::= */ - -3, /* (143) groupby_opt ::= GROUP BY nexprlist */ - 0, /* (144) having_opt ::= */ - -2, /* (145) having_opt ::= HAVING expr */ - 0, /* (146) limit_opt ::= */ - -2, /* (147) limit_opt ::= LIMIT expr */ - -4, /* (148) limit_opt ::= LIMIT expr OFFSET expr */ - -4, /* (149) limit_opt ::= LIMIT expr COMMA expr */ - -6, /* (150) cmd ::= with DELETE FROM xfullname indexed_opt where_opt_ret */ - 0, /* (151) where_opt ::= */ - -2, /* (152) where_opt ::= WHERE expr */ - 0, /* (153) where_opt_ret ::= */ - -2, /* (154) where_opt_ret ::= WHERE expr */ - -2, /* (155) where_opt_ret ::= RETURNING selcollist */ - -4, /* (156) where_opt_ret ::= WHERE expr RETURNING selcollist */ - -9, /* (157) cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist from where_opt_ret */ - -5, /* (158) setlist ::= setlist COMMA nm EQ expr */ - -7, /* (159) setlist ::= setlist COMMA LP idlist RP EQ expr */ - -3, /* (160) setlist ::= nm EQ expr */ - -5, /* (161) setlist ::= LP idlist RP EQ expr */ - -7, /* (162) cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert */ - -8, /* (163) cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES returning */ - 0, /* (164) upsert ::= */ - -2, /* (165) upsert ::= RETURNING selcollist */ - -12, /* (166) upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt upsert */ - -9, /* (167) upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING upsert */ - -5, /* (168) upsert ::= ON CONFLICT DO NOTHING returning */ - -8, /* (169) upsert ::= ON CONFLICT DO UPDATE SET setlist where_opt returning */ - -2, /* (170) returning ::= RETURNING selcollist */ - -2, /* (171) insert_cmd ::= INSERT orconf */ - -1, /* (172) insert_cmd ::= REPLACE */ - 0, /* (173) idlist_opt ::= */ - -3, /* (174) idlist_opt ::= LP idlist RP */ - -3, /* (175) idlist ::= idlist COMMA nm */ - -1, /* (176) idlist ::= nm */ - -3, /* (177) expr ::= LP expr RP */ - -1, /* (178) expr ::= ID|INDEXED|JOIN_KW */ - -3, /* (179) expr ::= nm DOT nm */ - -5, /* (180) expr ::= nm DOT nm DOT nm */ - -1, /* (181) term ::= NULL|FLOAT|BLOB */ - -1, /* (182) term ::= STRING */ - -1, /* (183) term ::= INTEGER */ - -1, /* (184) expr ::= VARIABLE */ - -3, /* (185) expr ::= expr COLLATE ID|STRING */ - -6, /* (186) expr ::= CAST LP expr AS typetoken RP */ - -5, /* (187) expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP */ - -8, /* (188) expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist ORDER BY sortlist RP */ - -4, /* (189) expr ::= ID|INDEXED|JOIN_KW LP STAR RP */ - -6, /* (190) expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP filter_over */ - -9, /* (191) expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist ORDER BY sortlist RP filter_over */ - -5, /* (192) expr ::= ID|INDEXED|JOIN_KW LP STAR RP filter_over */ - -1, /* (193) term ::= CTIME_KW */ - -5, /* (194) expr ::= LP nexprlist COMMA expr RP */ - -3, /* (195) expr ::= expr AND expr */ - -3, /* (196) expr ::= expr OR expr */ - -3, /* (197) expr ::= expr LT|GT|GE|LE expr */ - -3, /* (198) expr ::= expr EQ|NE expr */ - -3, /* (199) expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */ - -3, /* (200) expr ::= expr PLUS|MINUS expr */ - -3, /* (201) expr ::= expr STAR|SLASH|REM expr */ - -3, /* (202) expr ::= expr CONCAT expr */ - -2, /* (203) likeop ::= NOT LIKE_KW|MATCH */ - -3, /* (204) expr ::= expr likeop expr */ - -5, /* (205) expr ::= expr likeop expr ESCAPE expr */ - -2, /* (206) expr ::= expr ISNULL|NOTNULL */ - -3, /* (207) expr ::= expr NOT NULL */ - -3, /* (208) expr ::= expr IS expr */ - -4, /* (209) expr ::= expr IS NOT expr */ - -6, /* (210) expr ::= expr IS NOT DISTINCT FROM expr */ - -5, /* (211) expr ::= expr IS DISTINCT FROM expr */ - -2, /* (212) expr ::= NOT expr */ - -2, /* (213) expr ::= BITNOT expr */ - -2, /* (214) expr ::= PLUS|MINUS expr */ - -3, /* (215) expr ::= expr PTR expr */ - -1, /* (216) between_op ::= BETWEEN */ - -2, /* (217) between_op ::= NOT BETWEEN */ - -5, /* (218) expr ::= expr between_op expr AND expr */ - -1, /* (219) in_op ::= IN */ - -2, /* (220) in_op ::= NOT IN */ - -5, /* (221) expr ::= expr in_op LP exprlist RP */ - -3, /* (222) expr ::= LP select RP */ - -5, /* (223) expr ::= expr in_op LP select RP */ - -5, /* (224) expr ::= expr in_op nm dbnm paren_exprlist */ - -4, /* (225) expr ::= EXISTS LP select RP */ - -5, /* (226) expr ::= CASE case_operand case_exprlist case_else END */ - -5, /* (227) case_exprlist ::= case_exprlist WHEN expr THEN expr */ - -4, /* (228) case_exprlist ::= WHEN expr THEN expr */ - -2, /* (229) case_else ::= ELSE expr */ - 0, /* (230) case_else ::= */ - 0, /* (231) case_operand ::= */ - 0, /* (232) exprlist ::= */ - -3, /* (233) nexprlist ::= nexprlist COMMA expr */ - -1, /* (234) nexprlist ::= expr */ - 0, /* (235) paren_exprlist ::= */ - -3, /* (236) paren_exprlist ::= LP exprlist RP */ - -12, /* (237) cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */ - -1, /* (238) uniqueflag ::= UNIQUE */ - 0, /* (239) uniqueflag ::= */ - 0, /* (240) eidlist_opt ::= */ - -3, /* (241) eidlist_opt ::= LP eidlist RP */ - -5, /* (242) eidlist ::= eidlist COMMA nm collate sortorder */ - -3, /* (243) eidlist ::= nm collate sortorder */ - 0, /* (244) collate ::= */ - -2, /* (245) collate ::= COLLATE ID|STRING */ - -4, /* (246) cmd ::= DROP INDEX ifexists fullname */ - -2, /* (247) cmd ::= VACUUM vinto */ - -3, /* (248) cmd ::= VACUUM nm vinto */ - -2, /* (249) vinto ::= INTO expr */ - 0, /* (250) vinto ::= */ - -3, /* (251) cmd ::= PRAGMA nm dbnm */ - -5, /* (252) cmd ::= PRAGMA nm dbnm EQ nmnum */ - -6, /* (253) cmd ::= PRAGMA nm dbnm LP nmnum RP */ - -5, /* (254) cmd ::= PRAGMA nm dbnm EQ minus_num */ - -6, /* (255) cmd ::= PRAGMA nm dbnm LP minus_num RP */ - -2, /* (256) plus_num ::= PLUS INTEGER|FLOAT */ - -2, /* (257) minus_num ::= MINUS INTEGER|FLOAT */ - -5, /* (258) cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */ - -11, /* (259) trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */ - -1, /* (260) trigger_time ::= BEFORE|AFTER */ - -2, /* (261) trigger_time ::= INSTEAD OF */ - 0, /* (262) trigger_time ::= */ - -1, /* (263) trigger_event ::= DELETE|INSERT */ - -1, /* (264) trigger_event ::= UPDATE */ - -3, /* (265) trigger_event ::= UPDATE OF idlist */ - 0, /* (266) when_clause ::= */ - -2, /* (267) when_clause ::= WHEN expr */ - -3, /* (268) trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */ - -2, /* (269) trigger_cmd_list ::= trigger_cmd SEMI */ - -3, /* (270) trnm ::= nm DOT nm */ - -3, /* (271) tridxby ::= INDEXED BY nm */ - -2, /* (272) tridxby ::= NOT INDEXED */ - -9, /* (273) trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt */ - -8, /* (274) trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */ - -6, /* (275) trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */ - -3, /* (276) trigger_cmd ::= scanpt select scanpt */ - -4, /* (277) expr ::= RAISE LP IGNORE RP */ - -6, /* (278) expr ::= RAISE LP raisetype COMMA nm RP */ - -1, /* (279) raisetype ::= ROLLBACK */ - -1, /* (280) raisetype ::= ABORT */ - -1, /* (281) raisetype ::= FAIL */ - -4, /* (282) cmd ::= DROP TRIGGER ifexists fullname */ - -6, /* (283) cmd ::= ATTACH database_kw_opt expr AS expr key_opt */ - -3, /* (284) cmd ::= DETACH database_kw_opt expr */ - 0, /* (285) key_opt ::= */ - -2, /* (286) key_opt ::= KEY expr */ - -1, /* (287) cmd ::= REINDEX */ - -3, /* (288) cmd ::= REINDEX nm dbnm */ - -1, /* (289) cmd ::= ANALYZE */ - -3, /* (290) cmd ::= ANALYZE nm dbnm */ - -6, /* (291) cmd ::= ALTER TABLE fullname RENAME TO nm */ - -7, /* (292) cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */ - -6, /* (293) cmd ::= ALTER TABLE fullname DROP kwcolumn_opt nm */ - -1, /* (294) add_column_fullname ::= fullname */ - -8, /* (295) cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */ - -1, /* (296) cmd ::= create_vtab */ - -4, /* (297) cmd ::= create_vtab LP vtabarglist RP */ - -8, /* (298) create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */ - 0, /* (299) vtabarg ::= */ - -1, /* (300) vtabargtoken ::= ANY */ - -3, /* (301) vtabargtoken ::= lp anylist RP */ - -1, /* (302) lp ::= LP */ - -2, /* (303) with ::= WITH wqlist */ - -3, /* (304) with ::= WITH RECURSIVE wqlist */ - -1, /* (305) wqas ::= AS */ - -2, /* (306) wqas ::= AS MATERIALIZED */ - -3, /* (307) wqas ::= AS NOT MATERIALIZED */ - -6, /* (308) wqitem ::= nm eidlist_opt wqas LP select RP */ - -1, /* (309) wqlist ::= wqitem */ - -3, /* (310) wqlist ::= wqlist COMMA wqitem */ - -3, /* (311) windowdefn_list ::= windowdefn_list COMMA windowdefn */ - -5, /* (312) windowdefn ::= nm AS LP window RP */ - -5, /* (313) window ::= PARTITION BY nexprlist orderby_opt frame_opt */ - -6, /* (314) window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */ - -4, /* (315) window ::= ORDER BY sortlist frame_opt */ - -5, /* (316) window ::= nm ORDER BY sortlist frame_opt */ - -2, /* (317) window ::= nm frame_opt */ - 0, /* (318) frame_opt ::= */ - -3, /* (319) frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */ - -6, /* (320) frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */ - -1, /* (321) range_or_rows ::= RANGE|ROWS|GROUPS */ - -1, /* (322) frame_bound_s ::= frame_bound */ - -2, /* (323) frame_bound_s ::= UNBOUNDED PRECEDING */ - -1, /* (324) frame_bound_e ::= frame_bound */ - -2, /* (325) frame_bound_e ::= UNBOUNDED FOLLOWING */ - -2, /* (326) frame_bound ::= expr PRECEDING|FOLLOWING */ - -2, /* (327) frame_bound ::= CURRENT ROW */ - 0, /* (328) frame_exclude_opt ::= */ - -2, /* (329) frame_exclude_opt ::= EXCLUDE frame_exclude */ - -2, /* (330) frame_exclude ::= NO OTHERS */ - -2, /* (331) frame_exclude ::= CURRENT ROW */ - -1, /* (332) frame_exclude ::= GROUP|TIES */ - -2, /* (333) window_clause ::= WINDOW windowdefn_list */ - -2, /* (334) filter_over ::= filter_clause over_clause */ - -1, /* (335) filter_over ::= over_clause */ - -1, /* (336) filter_over ::= filter_clause */ - -4, /* (337) over_clause ::= OVER LP window RP */ - -2, /* (338) over_clause ::= OVER nm */ - -5, /* (339) filter_clause ::= FILTER LP WHERE expr RP */ - -1, /* (340) input ::= cmdlist */ - -2, /* (341) cmdlist ::= cmdlist ecmd */ - -1, /* (342) cmdlist ::= ecmd */ - -1, /* (343) ecmd ::= SEMI */ - -2, /* (344) ecmd ::= cmdx SEMI */ - -3, /* (345) ecmd ::= explain cmdx SEMI */ - 0, /* (346) trans_opt ::= */ - -1, /* (347) trans_opt ::= TRANSACTION */ - -2, /* (348) trans_opt ::= TRANSACTION nm */ - -1, /* (349) savepoint_opt ::= SAVEPOINT */ - 0, /* (350) savepoint_opt ::= */ - -2, /* (351) cmd ::= create_table create_table_args */ - -1, /* (352) table_option_set ::= table_option */ - -4, /* (353) columnlist ::= columnlist COMMA columnname carglist */ - -2, /* (354) columnlist ::= columnname carglist */ - -1, /* (355) nm ::= ID|INDEXED|JOIN_KW */ - -1, /* (356) nm ::= STRING */ - -1, /* (357) typetoken ::= typename */ - -1, /* (358) typename ::= ID|STRING */ - -1, /* (359) signed ::= plus_num */ - -1, /* (360) signed ::= minus_num */ - -2, /* (361) carglist ::= carglist ccons */ - 0, /* (362) carglist ::= */ - -2, /* (363) ccons ::= NULL onconf */ - -4, /* (364) ccons ::= GENERATED ALWAYS AS generated */ - -2, /* (365) ccons ::= AS generated */ - -2, /* (366) conslist_opt ::= COMMA conslist */ - -3, /* (367) conslist ::= conslist tconscomma tcons */ - -1, /* (368) conslist ::= tcons */ - 0, /* (369) tconscomma ::= */ - -1, /* (370) defer_subclause_opt ::= defer_subclause */ - -1, /* (371) resolvetype ::= raisetype */ - -1, /* (372) selectnowith ::= oneselect */ - -1, /* (373) oneselect ::= values */ - -2, /* (374) sclp ::= selcollist COMMA */ - -1, /* (375) as ::= ID|STRING */ - -1, /* (376) indexed_opt ::= indexed_by */ - 0, /* (377) returning ::= */ - -1, /* (378) expr ::= term */ - -1, /* (379) likeop ::= LIKE_KW|MATCH */ - -1, /* (380) case_operand ::= expr */ - -1, /* (381) exprlist ::= nexprlist */ - -1, /* (382) nmnum ::= plus_num */ - -1, /* (383) nmnum ::= nm */ - -1, /* (384) nmnum ::= ON */ - -1, /* (385) nmnum ::= DELETE */ - -1, /* (386) nmnum ::= DEFAULT */ - -1, /* (387) plus_num ::= INTEGER|FLOAT */ - 0, /* (388) foreach_clause ::= */ - -3, /* (389) foreach_clause ::= FOR EACH ROW */ - -1, /* (390) trnm ::= nm */ - 0, /* (391) tridxby ::= */ - -1, /* (392) database_kw_opt ::= DATABASE */ - 0, /* (393) database_kw_opt ::= */ - 0, /* (394) kwcolumn_opt ::= */ - -1, /* (395) kwcolumn_opt ::= COLUMNKW */ - -1, /* (396) vtabarglist ::= vtabarg */ - -3, /* (397) vtabarglist ::= vtabarglist COMMA vtabarg */ - -2, /* (398) vtabarg ::= vtabarg vtabargtoken */ - 0, /* (399) anylist ::= */ - -4, /* (400) anylist ::= anylist LP anylist RP */ - -2, /* (401) anylist ::= anylist ANY */ - 0, /* (402) with ::= */ - -1, /* (403) windowdefn_list ::= windowdefn */ - -1, /* (404) window ::= frame_opt */ + -1, /* (95) oneselect ::= mvalues */ + -5, /* (96) mvalues ::= values COMMA LP nexprlist RP */ + -5, /* (97) mvalues ::= mvalues COMMA LP nexprlist RP */ + -1, /* (98) distinct ::= DISTINCT */ + -1, /* (99) distinct ::= ALL */ + 0, /* (100) distinct ::= */ + 0, /* (101) sclp ::= */ + -5, /* (102) selcollist ::= sclp scanpt expr scanpt as */ + -3, /* (103) selcollist ::= sclp scanpt STAR */ + -5, /* (104) selcollist ::= sclp scanpt nm DOT STAR */ + -2, /* (105) as ::= AS nm */ + 0, /* (106) as ::= */ + 0, /* (107) from ::= */ + -2, /* (108) from ::= FROM seltablist */ + -2, /* (109) stl_prefix ::= seltablist joinop */ + 0, /* (110) stl_prefix ::= */ + -5, /* (111) seltablist ::= stl_prefix nm dbnm as on_using */ + -6, /* (112) seltablist ::= stl_prefix nm dbnm as indexed_by on_using */ + -8, /* (113) seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_using */ + -6, /* (114) seltablist ::= stl_prefix LP select RP as on_using */ + -6, /* (115) seltablist ::= stl_prefix LP seltablist RP as on_using */ + 0, /* (116) dbnm ::= */ + -2, /* (117) dbnm ::= DOT nm */ + -1, /* (118) fullname ::= nm */ + -3, /* (119) fullname ::= nm DOT nm */ + -1, /* (120) xfullname ::= nm */ + -3, /* (121) xfullname ::= nm DOT nm */ + -5, /* (122) xfullname ::= nm DOT nm AS nm */ + -3, /* (123) xfullname ::= nm AS nm */ + -1, /* (124) joinop ::= COMMA|JOIN */ + -2, /* (125) joinop ::= JOIN_KW JOIN */ + -3, /* (126) joinop ::= JOIN_KW nm JOIN */ + -4, /* (127) joinop ::= JOIN_KW nm nm JOIN */ + -2, /* (128) on_using ::= ON expr */ + -4, /* (129) on_using ::= USING LP idlist RP */ + 0, /* (130) on_using ::= */ + 0, /* (131) indexed_opt ::= */ + -3, /* (132) indexed_by ::= INDEXED BY nm */ + -2, /* (133) indexed_by ::= NOT INDEXED */ + 0, /* (134) orderby_opt ::= */ + -3, /* (135) orderby_opt ::= ORDER BY sortlist */ + -5, /* (136) sortlist ::= sortlist COMMA expr sortorder nulls */ + -3, /* (137) sortlist ::= expr sortorder nulls */ + -1, /* (138) sortorder ::= ASC */ + -1, /* (139) sortorder ::= DESC */ + 0, /* (140) sortorder ::= */ + -2, /* (141) nulls ::= NULLS FIRST */ + -2, /* (142) nulls ::= NULLS LAST */ + 0, /* (143) nulls ::= */ + 0, /* (144) groupby_opt ::= */ + -3, /* (145) groupby_opt ::= GROUP BY nexprlist */ + 0, /* (146) having_opt ::= */ + -2, /* (147) having_opt ::= HAVING expr */ + 0, /* (148) limit_opt ::= */ + -2, /* (149) limit_opt ::= LIMIT expr */ + -4, /* (150) limit_opt ::= LIMIT expr OFFSET expr */ + -4, /* (151) limit_opt ::= LIMIT expr COMMA expr */ + -6, /* (152) cmd ::= with DELETE FROM xfullname indexed_opt where_opt_ret */ + 0, /* (153) where_opt ::= */ + -2, /* (154) where_opt ::= WHERE expr */ + 0, /* (155) where_opt_ret ::= */ + -2, /* (156) where_opt_ret ::= WHERE expr */ + -2, /* (157) where_opt_ret ::= RETURNING selcollist */ + -4, /* (158) where_opt_ret ::= WHERE expr RETURNING selcollist */ + -9, /* (159) cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist from where_opt_ret */ + -5, /* (160) setlist ::= setlist COMMA nm EQ expr */ + -7, /* (161) setlist ::= setlist COMMA LP idlist RP EQ expr */ + -3, /* (162) setlist ::= nm EQ expr */ + -5, /* (163) setlist ::= LP idlist RP EQ expr */ + -7, /* (164) cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert */ + -8, /* (165) cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES returning */ + 0, /* (166) upsert ::= */ + -2, /* (167) upsert ::= RETURNING selcollist */ + -12, /* (168) upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt upsert */ + -9, /* (169) upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING upsert */ + -5, /* (170) upsert ::= ON CONFLICT DO NOTHING returning */ + -8, /* (171) upsert ::= ON CONFLICT DO UPDATE SET setlist where_opt returning */ + -2, /* (172) returning ::= RETURNING selcollist */ + -2, /* (173) insert_cmd ::= INSERT orconf */ + -1, /* (174) insert_cmd ::= REPLACE */ + 0, /* (175) idlist_opt ::= */ + -3, /* (176) idlist_opt ::= LP idlist RP */ + -3, /* (177) idlist ::= idlist COMMA nm */ + -1, /* (178) idlist ::= nm */ + -3, /* (179) expr ::= LP expr RP */ + -1, /* (180) expr ::= ID|INDEXED|JOIN_KW */ + -3, /* (181) expr ::= nm DOT nm */ + -5, /* (182) expr ::= nm DOT nm DOT nm */ + -1, /* (183) term ::= NULL|FLOAT|BLOB */ + -1, /* (184) term ::= STRING */ + -1, /* (185) term ::= INTEGER */ + -1, /* (186) expr ::= VARIABLE */ + -3, /* (187) expr ::= expr COLLATE ID|STRING */ + -6, /* (188) expr ::= CAST LP expr AS typetoken RP */ + -5, /* (189) expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP */ + -8, /* (190) expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist ORDER BY sortlist RP */ + -4, /* (191) expr ::= ID|INDEXED|JOIN_KW LP STAR RP */ + -6, /* (192) expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP filter_over */ + -9, /* (193) expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist ORDER BY sortlist RP filter_over */ + -5, /* (194) expr ::= ID|INDEXED|JOIN_KW LP STAR RP filter_over */ + -1, /* (195) term ::= CTIME_KW */ + -5, /* (196) expr ::= LP nexprlist COMMA expr RP */ + -3, /* (197) expr ::= expr AND expr */ + -3, /* (198) expr ::= expr OR expr */ + -3, /* (199) expr ::= expr LT|GT|GE|LE expr */ + -3, /* (200) expr ::= expr EQ|NE expr */ + -3, /* (201) expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */ + -3, /* (202) expr ::= expr PLUS|MINUS expr */ + -3, /* (203) expr ::= expr STAR|SLASH|REM expr */ + -3, /* (204) expr ::= expr CONCAT expr */ + -2, /* (205) likeop ::= NOT LIKE_KW|MATCH */ + -3, /* (206) expr ::= expr likeop expr */ + -5, /* (207) expr ::= expr likeop expr ESCAPE expr */ + -2, /* (208) expr ::= expr ISNULL|NOTNULL */ + -3, /* (209) expr ::= expr NOT NULL */ + -3, /* (210) expr ::= expr IS expr */ + -4, /* (211) expr ::= expr IS NOT expr */ + -6, /* (212) expr ::= expr IS NOT DISTINCT FROM expr */ + -5, /* (213) expr ::= expr IS DISTINCT FROM expr */ + -2, /* (214) expr ::= NOT expr */ + -2, /* (215) expr ::= BITNOT expr */ + -2, /* (216) expr ::= PLUS|MINUS expr */ + -3, /* (217) expr ::= expr PTR expr */ + -1, /* (218) between_op ::= BETWEEN */ + -2, /* (219) between_op ::= NOT BETWEEN */ + -5, /* (220) expr ::= expr between_op expr AND expr */ + -1, /* (221) in_op ::= IN */ + -2, /* (222) in_op ::= NOT IN */ + -5, /* (223) expr ::= expr in_op LP exprlist RP */ + -3, /* (224) expr ::= LP select RP */ + -5, /* (225) expr ::= expr in_op LP select RP */ + -5, /* (226) expr ::= expr in_op nm dbnm paren_exprlist */ + -4, /* (227) expr ::= EXISTS LP select RP */ + -5, /* (228) expr ::= CASE case_operand case_exprlist case_else END */ + -5, /* (229) case_exprlist ::= case_exprlist WHEN expr THEN expr */ + -4, /* (230) case_exprlist ::= WHEN expr THEN expr */ + -2, /* (231) case_else ::= ELSE expr */ + 0, /* (232) case_else ::= */ + 0, /* (233) case_operand ::= */ + 0, /* (234) exprlist ::= */ + -3, /* (235) nexprlist ::= nexprlist COMMA expr */ + -1, /* (236) nexprlist ::= expr */ + 0, /* (237) paren_exprlist ::= */ + -3, /* (238) paren_exprlist ::= LP exprlist RP */ + -12, /* (239) cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */ + -1, /* (240) uniqueflag ::= UNIQUE */ + 0, /* (241) uniqueflag ::= */ + 0, /* (242) eidlist_opt ::= */ + -3, /* (243) eidlist_opt ::= LP eidlist RP */ + -5, /* (244) eidlist ::= eidlist COMMA nm collate sortorder */ + -3, /* (245) eidlist ::= nm collate sortorder */ + 0, /* (246) collate ::= */ + -2, /* (247) collate ::= COLLATE ID|STRING */ + -4, /* (248) cmd ::= DROP INDEX ifexists fullname */ + -2, /* (249) cmd ::= VACUUM vinto */ + -3, /* (250) cmd ::= VACUUM nm vinto */ + -2, /* (251) vinto ::= INTO expr */ + 0, /* (252) vinto ::= */ + -3, /* (253) cmd ::= PRAGMA nm dbnm */ + -5, /* (254) cmd ::= PRAGMA nm dbnm EQ nmnum */ + -6, /* (255) cmd ::= PRAGMA nm dbnm LP nmnum RP */ + -5, /* (256) cmd ::= PRAGMA nm dbnm EQ minus_num */ + -6, /* (257) cmd ::= PRAGMA nm dbnm LP minus_num RP */ + -2, /* (258) plus_num ::= PLUS INTEGER|FLOAT */ + -2, /* (259) minus_num ::= MINUS INTEGER|FLOAT */ + -5, /* (260) cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */ + -11, /* (261) trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */ + -1, /* (262) trigger_time ::= BEFORE|AFTER */ + -2, /* (263) trigger_time ::= INSTEAD OF */ + 0, /* (264) trigger_time ::= */ + -1, /* (265) trigger_event ::= DELETE|INSERT */ + -1, /* (266) trigger_event ::= UPDATE */ + -3, /* (267) trigger_event ::= UPDATE OF idlist */ + 0, /* (268) when_clause ::= */ + -2, /* (269) when_clause ::= WHEN expr */ + -3, /* (270) trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */ + -2, /* (271) trigger_cmd_list ::= trigger_cmd SEMI */ + -3, /* (272) trnm ::= nm DOT nm */ + -3, /* (273) tridxby ::= INDEXED BY nm */ + -2, /* (274) tridxby ::= NOT INDEXED */ + -9, /* (275) trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt */ + -8, /* (276) trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */ + -6, /* (277) trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */ + -3, /* (278) trigger_cmd ::= scanpt select scanpt */ + -4, /* (279) expr ::= RAISE LP IGNORE RP */ + -6, /* (280) expr ::= RAISE LP raisetype COMMA nm RP */ + -1, /* (281) raisetype ::= ROLLBACK */ + -1, /* (282) raisetype ::= ABORT */ + -1, /* (283) raisetype ::= FAIL */ + -4, /* (284) cmd ::= DROP TRIGGER ifexists fullname */ + -6, /* (285) cmd ::= ATTACH database_kw_opt expr AS expr key_opt */ + -3, /* (286) cmd ::= DETACH database_kw_opt expr */ + 0, /* (287) key_opt ::= */ + -2, /* (288) key_opt ::= KEY expr */ + -1, /* (289) cmd ::= REINDEX */ + -3, /* (290) cmd ::= REINDEX nm dbnm */ + -1, /* (291) cmd ::= ANALYZE */ + -3, /* (292) cmd ::= ANALYZE nm dbnm */ + -6, /* (293) cmd ::= ALTER TABLE fullname RENAME TO nm */ + -7, /* (294) cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */ + -6, /* (295) cmd ::= ALTER TABLE fullname DROP kwcolumn_opt nm */ + -1, /* (296) add_column_fullname ::= fullname */ + -8, /* (297) cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */ + -1, /* (298) cmd ::= create_vtab */ + -4, /* (299) cmd ::= create_vtab LP vtabarglist RP */ + -8, /* (300) create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */ + 0, /* (301) vtabarg ::= */ + -1, /* (302) vtabargtoken ::= ANY */ + -3, /* (303) vtabargtoken ::= lp anylist RP */ + -1, /* (304) lp ::= LP */ + -2, /* (305) with ::= WITH wqlist */ + -3, /* (306) with ::= WITH RECURSIVE wqlist */ + -1, /* (307) wqas ::= AS */ + -2, /* (308) wqas ::= AS MATERIALIZED */ + -3, /* (309) wqas ::= AS NOT MATERIALIZED */ + -6, /* (310) wqitem ::= withnm eidlist_opt wqas LP select RP */ + -1, /* (311) withnm ::= nm */ + -1, /* (312) wqlist ::= wqitem */ + -3, /* (313) wqlist ::= wqlist COMMA wqitem */ + -3, /* (314) windowdefn_list ::= windowdefn_list COMMA windowdefn */ + -5, /* (315) windowdefn ::= nm AS LP window RP */ + -5, /* (316) window ::= PARTITION BY nexprlist orderby_opt frame_opt */ + -6, /* (317) window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */ + -4, /* (318) window ::= ORDER BY sortlist frame_opt */ + -5, /* (319) window ::= nm ORDER BY sortlist frame_opt */ + -2, /* (320) window ::= nm frame_opt */ + 0, /* (321) frame_opt ::= */ + -3, /* (322) frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */ + -6, /* (323) frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */ + -1, /* (324) range_or_rows ::= RANGE|ROWS|GROUPS */ + -1, /* (325) frame_bound_s ::= frame_bound */ + -2, /* (326) frame_bound_s ::= UNBOUNDED PRECEDING */ + -1, /* (327) frame_bound_e ::= frame_bound */ + -2, /* (328) frame_bound_e ::= UNBOUNDED FOLLOWING */ + -2, /* (329) frame_bound ::= expr PRECEDING|FOLLOWING */ + -2, /* (330) frame_bound ::= CURRENT ROW */ + 0, /* (331) frame_exclude_opt ::= */ + -2, /* (332) frame_exclude_opt ::= EXCLUDE frame_exclude */ + -2, /* (333) frame_exclude ::= NO OTHERS */ + -2, /* (334) frame_exclude ::= CURRENT ROW */ + -1, /* (335) frame_exclude ::= GROUP|TIES */ + -2, /* (336) window_clause ::= WINDOW windowdefn_list */ + -2, /* (337) filter_over ::= filter_clause over_clause */ + -1, /* (338) filter_over ::= over_clause */ + -1, /* (339) filter_over ::= filter_clause */ + -4, /* (340) over_clause ::= OVER LP window RP */ + -2, /* (341) over_clause ::= OVER nm */ + -5, /* (342) filter_clause ::= FILTER LP WHERE expr RP */ + -1, /* (343) term ::= QNUMBER */ + -1, /* (344) input ::= cmdlist */ + -2, /* (345) cmdlist ::= cmdlist ecmd */ + -1, /* (346) cmdlist ::= ecmd */ + -1, /* (347) ecmd ::= SEMI */ + -2, /* (348) ecmd ::= cmdx SEMI */ + -3, /* (349) ecmd ::= explain cmdx SEMI */ + 0, /* (350) trans_opt ::= */ + -1, /* (351) trans_opt ::= TRANSACTION */ + -2, /* (352) trans_opt ::= TRANSACTION nm */ + -1, /* (353) savepoint_opt ::= SAVEPOINT */ + 0, /* (354) savepoint_opt ::= */ + -2, /* (355) cmd ::= create_table create_table_args */ + -1, /* (356) table_option_set ::= table_option */ + -4, /* (357) columnlist ::= columnlist COMMA columnname carglist */ + -2, /* (358) columnlist ::= columnname carglist */ + -1, /* (359) nm ::= ID|INDEXED|JOIN_KW */ + -1, /* (360) nm ::= STRING */ + -1, /* (361) typetoken ::= typename */ + -1, /* (362) typename ::= ID|STRING */ + -1, /* (363) signed ::= plus_num */ + -1, /* (364) signed ::= minus_num */ + -2, /* (365) carglist ::= carglist ccons */ + 0, /* (366) carglist ::= */ + -2, /* (367) ccons ::= NULL onconf */ + -4, /* (368) ccons ::= GENERATED ALWAYS AS generated */ + -2, /* (369) ccons ::= AS generated */ + -2, /* (370) conslist_opt ::= COMMA conslist */ + -3, /* (371) conslist ::= conslist tconscomma tcons */ + -1, /* (372) conslist ::= tcons */ + 0, /* (373) tconscomma ::= */ + -1, /* (374) defer_subclause_opt ::= defer_subclause */ + -1, /* (375) resolvetype ::= raisetype */ + -1, /* (376) selectnowith ::= oneselect */ + -1, /* (377) oneselect ::= values */ + -2, /* (378) sclp ::= selcollist COMMA */ + -1, /* (379) as ::= ID|STRING */ + -1, /* (380) indexed_opt ::= indexed_by */ + 0, /* (381) returning ::= */ + -1, /* (382) expr ::= term */ + -1, /* (383) likeop ::= LIKE_KW|MATCH */ + -1, /* (384) case_operand ::= expr */ + -1, /* (385) exprlist ::= nexprlist */ + -1, /* (386) nmnum ::= plus_num */ + -1, /* (387) nmnum ::= nm */ + -1, /* (388) nmnum ::= ON */ + -1, /* (389) nmnum ::= DELETE */ + -1, /* (390) nmnum ::= DEFAULT */ + -1, /* (391) plus_num ::= INTEGER|FLOAT */ + 0, /* (392) foreach_clause ::= */ + -3, /* (393) foreach_clause ::= FOR EACH ROW */ + -1, /* (394) trnm ::= nm */ + 0, /* (395) tridxby ::= */ + -1, /* (396) database_kw_opt ::= DATABASE */ + 0, /* (397) database_kw_opt ::= */ + 0, /* (398) kwcolumn_opt ::= */ + -1, /* (399) kwcolumn_opt ::= COLUMNKW */ + -1, /* (400) vtabarglist ::= vtabarg */ + -3, /* (401) vtabarglist ::= vtabarglist COMMA vtabarg */ + -2, /* (402) vtabarg ::= vtabarg vtabargtoken */ + 0, /* (403) anylist ::= */ + -4, /* (404) anylist ::= anylist LP anylist RP */ + -2, /* (405) anylist ::= anylist ANY */ + 0, /* (406) with ::= */ + -1, /* (407) windowdefn_list ::= windowdefn */ + -1, /* (408) window ::= frame_opt */ }; static void yy_accept(yyParser*); /* Forward Declaration */ @@ -174724,16 +176126,16 @@ static YYACTIONTYPE yy_reduce( { sqlite3FinishCoding(pParse); } break; case 3: /* cmd ::= BEGIN transtype trans_opt */ -{sqlite3BeginTransaction(pParse, yymsp[-1].minor.yy394);} +{sqlite3BeginTransaction(pParse, yymsp[-1].minor.yy144);} break; case 4: /* transtype ::= */ -{yymsp[1].minor.yy394 = TK_DEFERRED;} +{yymsp[1].minor.yy144 = TK_DEFERRED;} break; case 5: /* transtype ::= DEFERRED */ case 6: /* transtype ::= IMMEDIATE */ yytestcase(yyruleno==6); case 7: /* transtype ::= EXCLUSIVE */ yytestcase(yyruleno==7); - case 321: /* range_or_rows ::= RANGE|ROWS|GROUPS */ yytestcase(yyruleno==321); -{yymsp[0].minor.yy394 = yymsp[0].major; /*A-overwrites-X*/} + case 324: /* range_or_rows ::= RANGE|ROWS|GROUPS */ yytestcase(yyruleno==324); +{yymsp[0].minor.yy144 = yymsp[0].major; /*A-overwrites-X*/} break; case 8: /* cmd ::= COMMIT|END trans_opt */ case 9: /* cmd ::= ROLLBACK trans_opt */ yytestcase(yyruleno==9); @@ -174756,7 +176158,7 @@ static YYACTIONTYPE yy_reduce( break; case 13: /* create_table ::= createkw temp TABLE ifnotexists nm dbnm */ { - sqlite3StartTable(pParse,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0,yymsp[-4].minor.yy394,0,0,yymsp[-2].minor.yy394); + sqlite3StartTable(pParse,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0,yymsp[-4].minor.yy144,0,0,yymsp[-2].minor.yy144); } break; case 14: /* createkw ::= CREATE */ @@ -174768,40 +176170,40 @@ static YYACTIONTYPE yy_reduce( case 62: /* init_deferred_pred_opt ::= */ yytestcase(yyruleno==62); case 72: /* defer_subclause_opt ::= */ yytestcase(yyruleno==72); case 81: /* ifexists ::= */ yytestcase(yyruleno==81); - case 98: /* distinct ::= */ yytestcase(yyruleno==98); - case 244: /* collate ::= */ yytestcase(yyruleno==244); -{yymsp[1].minor.yy394 = 0;} + case 100: /* distinct ::= */ yytestcase(yyruleno==100); + case 246: /* collate ::= */ yytestcase(yyruleno==246); +{yymsp[1].minor.yy144 = 0;} break; case 16: /* ifnotexists ::= IF NOT EXISTS */ -{yymsp[-2].minor.yy394 = 1;} +{yymsp[-2].minor.yy144 = 1;} break; case 17: /* temp ::= TEMP */ -{yymsp[0].minor.yy394 = pParse->db->init.busy==0;} +{yymsp[0].minor.yy144 = pParse->db->init.busy==0;} break; case 19: /* create_table_args ::= LP columnlist conslist_opt RP table_option_set */ { - sqlite3EndTable(pParse,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0,yymsp[0].minor.yy285,0); + sqlite3EndTable(pParse,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0,yymsp[0].minor.yy391,0); } break; case 20: /* create_table_args ::= AS select */ { - sqlite3EndTable(pParse,0,0,0,yymsp[0].minor.yy47); - sqlite3SelectDelete(pParse->db, yymsp[0].minor.yy47); + sqlite3EndTable(pParse,0,0,0,yymsp[0].minor.yy555); + sqlite3SelectDelete(pParse->db, yymsp[0].minor.yy555); } break; case 21: /* table_option_set ::= */ -{yymsp[1].minor.yy285 = 0;} +{yymsp[1].minor.yy391 = 0;} break; case 22: /* table_option_set ::= table_option_set COMMA table_option */ -{yylhsminor.yy285 = yymsp[-2].minor.yy285|yymsp[0].minor.yy285;} - yymsp[-2].minor.yy285 = yylhsminor.yy285; +{yylhsminor.yy391 = yymsp[-2].minor.yy391|yymsp[0].minor.yy391;} + yymsp[-2].minor.yy391 = yylhsminor.yy391; break; case 23: /* table_option ::= WITHOUT nm */ { if( yymsp[0].minor.yy0.n==5 && sqlite3_strnicmp(yymsp[0].minor.yy0.z,"rowid",5)==0 ){ - yymsp[-1].minor.yy285 = TF_WithoutRowid | TF_NoVisibleRowid; + yymsp[-1].minor.yy391 = TF_WithoutRowid | TF_NoVisibleRowid; }else{ - yymsp[-1].minor.yy285 = 0; + yymsp[-1].minor.yy391 = 0; sqlite3ErrorMsg(pParse, "unknown table option: %.*s", yymsp[0].minor.yy0.n, yymsp[0].minor.yy0.z); } } @@ -174809,20 +176211,20 @@ static YYACTIONTYPE yy_reduce( case 24: /* table_option ::= nm */ { if( yymsp[0].minor.yy0.n==6 && sqlite3_strnicmp(yymsp[0].minor.yy0.z,"strict",6)==0 ){ - yylhsminor.yy285 = TF_Strict; + yylhsminor.yy391 = TF_Strict; }else{ - yylhsminor.yy285 = 0; + yylhsminor.yy391 = 0; sqlite3ErrorMsg(pParse, "unknown table option: %.*s", yymsp[0].minor.yy0.n, yymsp[0].minor.yy0.z); } } - yymsp[0].minor.yy285 = yylhsminor.yy285; + yymsp[0].minor.yy391 = yylhsminor.yy391; break; case 25: /* columnname ::= nm typetoken */ {sqlite3AddColumn(pParse,yymsp[-1].minor.yy0,yymsp[0].minor.yy0);} break; case 26: /* typetoken ::= */ case 65: /* conslist_opt ::= */ yytestcase(yyruleno==65); - case 104: /* as ::= */ yytestcase(yyruleno==104); + case 106: /* as ::= */ yytestcase(yyruleno==106); {yymsp[1].minor.yy0.n = 0; yymsp[1].minor.yy0.z = 0;} break; case 27: /* typetoken ::= typename LP signed RP */ @@ -174841,7 +176243,7 @@ static YYACTIONTYPE yy_reduce( case 30: /* scanpt ::= */ { assert( yyLookahead!=YYNOCODE ); - yymsp[1].minor.yy522 = yyLookaheadToken.z; + yymsp[1].minor.yy168 = yyLookaheadToken.z; } break; case 31: /* scantok ::= */ @@ -174855,17 +176257,17 @@ static YYACTIONTYPE yy_reduce( {pParse->constraintName = yymsp[0].minor.yy0;} break; case 33: /* ccons ::= DEFAULT scantok term */ -{sqlite3AddDefaultValue(pParse,yymsp[0].minor.yy528,yymsp[-1].minor.yy0.z,&yymsp[-1].minor.yy0.z[yymsp[-1].minor.yy0.n]);} +{sqlite3AddDefaultValue(pParse,yymsp[0].minor.yy454,yymsp[-1].minor.yy0.z,&yymsp[-1].minor.yy0.z[yymsp[-1].minor.yy0.n]);} break; case 34: /* ccons ::= DEFAULT LP expr RP */ -{sqlite3AddDefaultValue(pParse,yymsp[-1].minor.yy528,yymsp[-2].minor.yy0.z+1,yymsp[0].minor.yy0.z);} +{sqlite3AddDefaultValue(pParse,yymsp[-1].minor.yy454,yymsp[-2].minor.yy0.z+1,yymsp[0].minor.yy0.z);} break; case 35: /* ccons ::= DEFAULT PLUS scantok term */ -{sqlite3AddDefaultValue(pParse,yymsp[0].minor.yy528,yymsp[-2].minor.yy0.z,&yymsp[-1].minor.yy0.z[yymsp[-1].minor.yy0.n]);} +{sqlite3AddDefaultValue(pParse,yymsp[0].minor.yy454,yymsp[-2].minor.yy0.z,&yymsp[-1].minor.yy0.z[yymsp[-1].minor.yy0.n]);} break; case 36: /* ccons ::= DEFAULT MINUS scantok term */ { - Expr *p = sqlite3PExpr(pParse, TK_UMINUS, yymsp[0].minor.yy528, 0); + Expr *p = sqlite3PExpr(pParse, TK_UMINUS, yymsp[0].minor.yy454, 0); sqlite3AddDefaultValue(pParse,p,yymsp[-2].minor.yy0.z,&yymsp[-1].minor.yy0.z[yymsp[-1].minor.yy0.n]); } break; @@ -174880,151 +176282,151 @@ static YYACTIONTYPE yy_reduce( } break; case 38: /* ccons ::= NOT NULL onconf */ -{sqlite3AddNotNull(pParse, yymsp[0].minor.yy394);} +{sqlite3AddNotNull(pParse, yymsp[0].minor.yy144);} break; case 39: /* ccons ::= PRIMARY KEY sortorder onconf autoinc */ -{sqlite3AddPrimaryKey(pParse,0,yymsp[-1].minor.yy394,yymsp[0].minor.yy394,yymsp[-2].minor.yy394);} +{sqlite3AddPrimaryKey(pParse,0,yymsp[-1].minor.yy144,yymsp[0].minor.yy144,yymsp[-2].minor.yy144);} break; case 40: /* ccons ::= UNIQUE onconf */ -{sqlite3CreateIndex(pParse,0,0,0,0,yymsp[0].minor.yy394,0,0,0,0, +{sqlite3CreateIndex(pParse,0,0,0,0,yymsp[0].minor.yy144,0,0,0,0, SQLITE_IDXTYPE_UNIQUE);} break; case 41: /* ccons ::= CHECK LP expr RP */ -{sqlite3AddCheckConstraint(pParse,yymsp[-1].minor.yy528,yymsp[-2].minor.yy0.z,yymsp[0].minor.yy0.z);} +{sqlite3AddCheckConstraint(pParse,yymsp[-1].minor.yy454,yymsp[-2].minor.yy0.z,yymsp[0].minor.yy0.z);} break; case 42: /* ccons ::= REFERENCES nm eidlist_opt refargs */ -{sqlite3CreateForeignKey(pParse,0,&yymsp[-2].minor.yy0,yymsp[-1].minor.yy322,yymsp[0].minor.yy394);} +{sqlite3CreateForeignKey(pParse,0,&yymsp[-2].minor.yy0,yymsp[-1].minor.yy14,yymsp[0].minor.yy144);} break; case 43: /* ccons ::= defer_subclause */ -{sqlite3DeferForeignKey(pParse,yymsp[0].minor.yy394);} +{sqlite3DeferForeignKey(pParse,yymsp[0].minor.yy144);} break; case 44: /* ccons ::= COLLATE ID|STRING */ {sqlite3AddCollateType(pParse, &yymsp[0].minor.yy0);} break; case 45: /* generated ::= LP expr RP */ -{sqlite3AddGenerated(pParse,yymsp[-1].minor.yy528,0);} +{sqlite3AddGenerated(pParse,yymsp[-1].minor.yy454,0);} break; case 46: /* generated ::= LP expr RP ID */ -{sqlite3AddGenerated(pParse,yymsp[-2].minor.yy528,&yymsp[0].minor.yy0);} +{sqlite3AddGenerated(pParse,yymsp[-2].minor.yy454,&yymsp[0].minor.yy0);} break; case 48: /* autoinc ::= AUTOINCR */ -{yymsp[0].minor.yy394 = 1;} +{yymsp[0].minor.yy144 = 1;} break; case 49: /* refargs ::= */ -{ yymsp[1].minor.yy394 = OE_None*0x0101; /* EV: R-19803-45884 */} +{ yymsp[1].minor.yy144 = OE_None*0x0101; /* EV: R-19803-45884 */} break; case 50: /* refargs ::= refargs refarg */ -{ yymsp[-1].minor.yy394 = (yymsp[-1].minor.yy394 & ~yymsp[0].minor.yy231.mask) | yymsp[0].minor.yy231.value; } +{ yymsp[-1].minor.yy144 = (yymsp[-1].minor.yy144 & ~yymsp[0].minor.yy383.mask) | yymsp[0].minor.yy383.value; } break; case 51: /* refarg ::= MATCH nm */ -{ yymsp[-1].minor.yy231.value = 0; yymsp[-1].minor.yy231.mask = 0x000000; } +{ yymsp[-1].minor.yy383.value = 0; yymsp[-1].minor.yy383.mask = 0x000000; } break; case 52: /* refarg ::= ON INSERT refact */ -{ yymsp[-2].minor.yy231.value = 0; yymsp[-2].minor.yy231.mask = 0x000000; } +{ yymsp[-2].minor.yy383.value = 0; yymsp[-2].minor.yy383.mask = 0x000000; } break; case 53: /* refarg ::= ON DELETE refact */ -{ yymsp[-2].minor.yy231.value = yymsp[0].minor.yy394; yymsp[-2].minor.yy231.mask = 0x0000ff; } +{ yymsp[-2].minor.yy383.value = yymsp[0].minor.yy144; yymsp[-2].minor.yy383.mask = 0x0000ff; } break; case 54: /* refarg ::= ON UPDATE refact */ -{ yymsp[-2].minor.yy231.value = yymsp[0].minor.yy394<<8; yymsp[-2].minor.yy231.mask = 0x00ff00; } +{ yymsp[-2].minor.yy383.value = yymsp[0].minor.yy144<<8; yymsp[-2].minor.yy383.mask = 0x00ff00; } break; case 55: /* refact ::= SET NULL */ -{ yymsp[-1].minor.yy394 = OE_SetNull; /* EV: R-33326-45252 */} +{ yymsp[-1].minor.yy144 = OE_SetNull; /* EV: R-33326-45252 */} break; case 56: /* refact ::= SET DEFAULT */ -{ yymsp[-1].minor.yy394 = OE_SetDflt; /* EV: R-33326-45252 */} +{ yymsp[-1].minor.yy144 = OE_SetDflt; /* EV: R-33326-45252 */} break; case 57: /* refact ::= CASCADE */ -{ yymsp[0].minor.yy394 = OE_Cascade; /* EV: R-33326-45252 */} +{ yymsp[0].minor.yy144 = OE_Cascade; /* EV: R-33326-45252 */} break; case 58: /* refact ::= RESTRICT */ -{ yymsp[0].minor.yy394 = OE_Restrict; /* EV: R-33326-45252 */} +{ yymsp[0].minor.yy144 = OE_Restrict; /* EV: R-33326-45252 */} break; case 59: /* refact ::= NO ACTION */ -{ yymsp[-1].minor.yy394 = OE_None; /* EV: R-33326-45252 */} +{ yymsp[-1].minor.yy144 = OE_None; /* EV: R-33326-45252 */} break; case 60: /* defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt */ -{yymsp[-2].minor.yy394 = 0;} +{yymsp[-2].minor.yy144 = 0;} break; case 61: /* defer_subclause ::= DEFERRABLE init_deferred_pred_opt */ case 76: /* orconf ::= OR resolvetype */ yytestcase(yyruleno==76); - case 171: /* insert_cmd ::= INSERT orconf */ yytestcase(yyruleno==171); -{yymsp[-1].minor.yy394 = yymsp[0].minor.yy394;} + case 173: /* insert_cmd ::= INSERT orconf */ yytestcase(yyruleno==173); +{yymsp[-1].minor.yy144 = yymsp[0].minor.yy144;} break; case 63: /* init_deferred_pred_opt ::= INITIALLY DEFERRED */ case 80: /* ifexists ::= IF EXISTS */ yytestcase(yyruleno==80); - case 217: /* between_op ::= NOT BETWEEN */ yytestcase(yyruleno==217); - case 220: /* in_op ::= NOT IN */ yytestcase(yyruleno==220); - case 245: /* collate ::= COLLATE ID|STRING */ yytestcase(yyruleno==245); -{yymsp[-1].minor.yy394 = 1;} + case 219: /* between_op ::= NOT BETWEEN */ yytestcase(yyruleno==219); + case 222: /* in_op ::= NOT IN */ yytestcase(yyruleno==222); + case 247: /* collate ::= COLLATE ID|STRING */ yytestcase(yyruleno==247); +{yymsp[-1].minor.yy144 = 1;} break; case 64: /* init_deferred_pred_opt ::= INITIALLY IMMEDIATE */ -{yymsp[-1].minor.yy394 = 0;} +{yymsp[-1].minor.yy144 = 0;} break; case 66: /* tconscomma ::= COMMA */ {pParse->constraintName.n = 0;} break; case 68: /* tcons ::= PRIMARY KEY LP sortlist autoinc RP onconf */ -{sqlite3AddPrimaryKey(pParse,yymsp[-3].minor.yy322,yymsp[0].minor.yy394,yymsp[-2].minor.yy394,0);} +{sqlite3AddPrimaryKey(pParse,yymsp[-3].minor.yy14,yymsp[0].minor.yy144,yymsp[-2].minor.yy144,0);} break; case 69: /* tcons ::= UNIQUE LP sortlist RP onconf */ -{sqlite3CreateIndex(pParse,0,0,0,yymsp[-2].minor.yy322,yymsp[0].minor.yy394,0,0,0,0, +{sqlite3CreateIndex(pParse,0,0,0,yymsp[-2].minor.yy14,yymsp[0].minor.yy144,0,0,0,0, SQLITE_IDXTYPE_UNIQUE);} break; case 70: /* tcons ::= CHECK LP expr RP onconf */ -{sqlite3AddCheckConstraint(pParse,yymsp[-2].minor.yy528,yymsp[-3].minor.yy0.z,yymsp[-1].minor.yy0.z);} +{sqlite3AddCheckConstraint(pParse,yymsp[-2].minor.yy454,yymsp[-3].minor.yy0.z,yymsp[-1].minor.yy0.z);} break; case 71: /* tcons ::= FOREIGN KEY LP eidlist RP REFERENCES nm eidlist_opt refargs defer_subclause_opt */ { - sqlite3CreateForeignKey(pParse, yymsp[-6].minor.yy322, &yymsp[-3].minor.yy0, yymsp[-2].minor.yy322, yymsp[-1].minor.yy394); - sqlite3DeferForeignKey(pParse, yymsp[0].minor.yy394); + sqlite3CreateForeignKey(pParse, yymsp[-6].minor.yy14, &yymsp[-3].minor.yy0, yymsp[-2].minor.yy14, yymsp[-1].minor.yy144); + sqlite3DeferForeignKey(pParse, yymsp[0].minor.yy144); } break; case 73: /* onconf ::= */ case 75: /* orconf ::= */ yytestcase(yyruleno==75); -{yymsp[1].minor.yy394 = OE_Default;} +{yymsp[1].minor.yy144 = OE_Default;} break; case 74: /* onconf ::= ON CONFLICT resolvetype */ -{yymsp[-2].minor.yy394 = yymsp[0].minor.yy394;} +{yymsp[-2].minor.yy144 = yymsp[0].minor.yy144;} break; case 77: /* resolvetype ::= IGNORE */ -{yymsp[0].minor.yy394 = OE_Ignore;} +{yymsp[0].minor.yy144 = OE_Ignore;} break; case 78: /* resolvetype ::= REPLACE */ - case 172: /* insert_cmd ::= REPLACE */ yytestcase(yyruleno==172); -{yymsp[0].minor.yy394 = OE_Replace;} + case 174: /* insert_cmd ::= REPLACE */ yytestcase(yyruleno==174); +{yymsp[0].minor.yy144 = OE_Replace;} break; case 79: /* cmd ::= DROP TABLE ifexists fullname */ { - sqlite3DropTable(pParse, yymsp[0].minor.yy131, 0, yymsp[-1].minor.yy394); + sqlite3DropTable(pParse, yymsp[0].minor.yy203, 0, yymsp[-1].minor.yy144); } break; case 82: /* cmd ::= createkw temp VIEW ifnotexists nm dbnm eidlist_opt AS select */ { - sqlite3CreateView(pParse, &yymsp[-8].minor.yy0, &yymsp[-4].minor.yy0, &yymsp[-3].minor.yy0, yymsp[-2].minor.yy322, yymsp[0].minor.yy47, yymsp[-7].minor.yy394, yymsp[-5].minor.yy394); + sqlite3CreateView(pParse, &yymsp[-8].minor.yy0, &yymsp[-4].minor.yy0, &yymsp[-3].minor.yy0, yymsp[-2].minor.yy14, yymsp[0].minor.yy555, yymsp[-7].minor.yy144, yymsp[-5].minor.yy144); } break; case 83: /* cmd ::= DROP VIEW ifexists fullname */ { - sqlite3DropTable(pParse, yymsp[0].minor.yy131, 1, yymsp[-1].minor.yy394); + sqlite3DropTable(pParse, yymsp[0].minor.yy203, 1, yymsp[-1].minor.yy144); } break; case 84: /* cmd ::= select */ { SelectDest dest = {SRT_Output, 0, 0, 0, 0, 0, 0}; - sqlite3Select(pParse, yymsp[0].minor.yy47, &dest); - sqlite3SelectDelete(pParse->db, yymsp[0].minor.yy47); + sqlite3Select(pParse, yymsp[0].minor.yy555, &dest); + sqlite3SelectDelete(pParse->db, yymsp[0].minor.yy555); } break; case 85: /* select ::= WITH wqlist selectnowith */ -{yymsp[-2].minor.yy47 = attachWithToSelect(pParse,yymsp[0].minor.yy47,yymsp[-1].minor.yy521);} +{yymsp[-2].minor.yy555 = attachWithToSelect(pParse,yymsp[0].minor.yy555,yymsp[-1].minor.yy59);} break; case 86: /* select ::= WITH RECURSIVE wqlist selectnowith */ -{yymsp[-3].minor.yy47 = attachWithToSelect(pParse,yymsp[0].minor.yy47,yymsp[-1].minor.yy521);} +{yymsp[-3].minor.yy555 = attachWithToSelect(pParse,yymsp[0].minor.yy555,yymsp[-1].minor.yy59);} break; case 87: /* select ::= selectnowith */ { - Select *p = yymsp[0].minor.yy47; + Select *p = yymsp[0].minor.yy555; if( p ){ parserDoubleLinkSelect(pParse, p); } @@ -175032,8 +176434,8 @@ static YYACTIONTYPE yy_reduce( break; case 88: /* selectnowith ::= selectnowith multiselect_op oneselect */ { - Select *pRhs = yymsp[0].minor.yy47; - Select *pLhs = yymsp[-2].minor.yy47; + Select *pRhs = yymsp[0].minor.yy555; + Select *pLhs = yymsp[-2].minor.yy555; if( pRhs && pRhs->pPrior ){ SrcList *pFrom; Token x; @@ -175043,148 +176445,145 @@ static YYACTIONTYPE yy_reduce( pRhs = sqlite3SelectNew(pParse,0,pFrom,0,0,0,0,0,0); } if( pRhs ){ - pRhs->op = (u8)yymsp[-1].minor.yy394; + pRhs->op = (u8)yymsp[-1].minor.yy144; pRhs->pPrior = pLhs; if( ALWAYS(pLhs) ) pLhs->selFlags &= ~SF_MultiValue; pRhs->selFlags &= ~SF_MultiValue; - if( yymsp[-1].minor.yy394!=TK_ALL ) pParse->hasCompound = 1; + if( yymsp[-1].minor.yy144!=TK_ALL ) pParse->hasCompound = 1; }else{ sqlite3SelectDelete(pParse->db, pLhs); } - yymsp[-2].minor.yy47 = pRhs; + yymsp[-2].minor.yy555 = pRhs; } break; case 89: /* multiselect_op ::= UNION */ case 91: /* multiselect_op ::= EXCEPT|INTERSECT */ yytestcase(yyruleno==91); -{yymsp[0].minor.yy394 = yymsp[0].major; /*A-overwrites-OP*/} +{yymsp[0].minor.yy144 = yymsp[0].major; /*A-overwrites-OP*/} break; case 90: /* multiselect_op ::= UNION ALL */ -{yymsp[-1].minor.yy394 = TK_ALL;} +{yymsp[-1].minor.yy144 = TK_ALL;} break; case 92: /* oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt */ { - yymsp[-8].minor.yy47 = sqlite3SelectNew(pParse,yymsp[-6].minor.yy322,yymsp[-5].minor.yy131,yymsp[-4].minor.yy528,yymsp[-3].minor.yy322,yymsp[-2].minor.yy528,yymsp[-1].minor.yy322,yymsp[-7].minor.yy394,yymsp[0].minor.yy528); + yymsp[-8].minor.yy555 = sqlite3SelectNew(pParse,yymsp[-6].minor.yy14,yymsp[-5].minor.yy203,yymsp[-4].minor.yy454,yymsp[-3].minor.yy14,yymsp[-2].minor.yy454,yymsp[-1].minor.yy14,yymsp[-7].minor.yy144,yymsp[0].minor.yy454); } break; case 93: /* oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt window_clause orderby_opt limit_opt */ { - yymsp[-9].minor.yy47 = sqlite3SelectNew(pParse,yymsp[-7].minor.yy322,yymsp[-6].minor.yy131,yymsp[-5].minor.yy528,yymsp[-4].minor.yy322,yymsp[-3].minor.yy528,yymsp[-1].minor.yy322,yymsp[-8].minor.yy394,yymsp[0].minor.yy528); - if( yymsp[-9].minor.yy47 ){ - yymsp[-9].minor.yy47->pWinDefn = yymsp[-2].minor.yy41; + yymsp[-9].minor.yy555 = sqlite3SelectNew(pParse,yymsp[-7].minor.yy14,yymsp[-6].minor.yy203,yymsp[-5].minor.yy454,yymsp[-4].minor.yy14,yymsp[-3].minor.yy454,yymsp[-1].minor.yy14,yymsp[-8].minor.yy144,yymsp[0].minor.yy454); + if( yymsp[-9].minor.yy555 ){ + yymsp[-9].minor.yy555->pWinDefn = yymsp[-2].minor.yy211; }else{ - sqlite3WindowListDelete(pParse->db, yymsp[-2].minor.yy41); + sqlite3WindowListDelete(pParse->db, yymsp[-2].minor.yy211); } } break; case 94: /* values ::= VALUES LP nexprlist RP */ { - yymsp[-3].minor.yy47 = sqlite3SelectNew(pParse,yymsp[-1].minor.yy322,0,0,0,0,0,SF_Values,0); + yymsp[-3].minor.yy555 = sqlite3SelectNew(pParse,yymsp[-1].minor.yy14,0,0,0,0,0,SF_Values,0); } break; - case 95: /* values ::= values COMMA LP nexprlist RP */ + case 95: /* oneselect ::= mvalues */ { - Select *pRight, *pLeft = yymsp[-4].minor.yy47; - pRight = sqlite3SelectNew(pParse,yymsp[-1].minor.yy322,0,0,0,0,0,SF_Values|SF_MultiValue,0); - if( ALWAYS(pLeft) ) pLeft->selFlags &= ~SF_MultiValue; - if( pRight ){ - pRight->op = TK_ALL; - pRight->pPrior = pLeft; - yymsp[-4].minor.yy47 = pRight; - }else{ - yymsp[-4].minor.yy47 = pLeft; - } + sqlite3MultiValuesEnd(pParse, yymsp[0].minor.yy555); } break; - case 96: /* distinct ::= DISTINCT */ -{yymsp[0].minor.yy394 = SF_Distinct;} - break; - case 97: /* distinct ::= ALL */ -{yymsp[0].minor.yy394 = SF_All;} - break; - case 99: /* sclp ::= */ - case 132: /* orderby_opt ::= */ yytestcase(yyruleno==132); - case 142: /* groupby_opt ::= */ yytestcase(yyruleno==142); - case 232: /* exprlist ::= */ yytestcase(yyruleno==232); - case 235: /* paren_exprlist ::= */ yytestcase(yyruleno==235); - case 240: /* eidlist_opt ::= */ yytestcase(yyruleno==240); -{yymsp[1].minor.yy322 = 0;} - break; - case 100: /* selcollist ::= sclp scanpt expr scanpt as */ + case 96: /* mvalues ::= values COMMA LP nexprlist RP */ + case 97: /* mvalues ::= mvalues COMMA LP nexprlist RP */ yytestcase(yyruleno==97); { - yymsp[-4].minor.yy322 = sqlite3ExprListAppend(pParse, yymsp[-4].minor.yy322, yymsp[-2].minor.yy528); - if( yymsp[0].minor.yy0.n>0 ) sqlite3ExprListSetName(pParse, yymsp[-4].minor.yy322, &yymsp[0].minor.yy0, 1); - sqlite3ExprListSetSpan(pParse,yymsp[-4].minor.yy322,yymsp[-3].minor.yy522,yymsp[-1].minor.yy522); + yymsp[-4].minor.yy555 = sqlite3MultiValues(pParse, yymsp[-4].minor.yy555, yymsp[-1].minor.yy14); } break; - case 101: /* selcollist ::= sclp scanpt STAR */ + case 98: /* distinct ::= DISTINCT */ +{yymsp[0].minor.yy144 = SF_Distinct;} + break; + case 99: /* distinct ::= ALL */ +{yymsp[0].minor.yy144 = SF_All;} + break; + case 101: /* sclp ::= */ + case 134: /* orderby_opt ::= */ yytestcase(yyruleno==134); + case 144: /* groupby_opt ::= */ yytestcase(yyruleno==144); + case 234: /* exprlist ::= */ yytestcase(yyruleno==234); + case 237: /* paren_exprlist ::= */ yytestcase(yyruleno==237); + case 242: /* eidlist_opt ::= */ yytestcase(yyruleno==242); +{yymsp[1].minor.yy14 = 0;} + break; + case 102: /* selcollist ::= sclp scanpt expr scanpt as */ +{ + yymsp[-4].minor.yy14 = sqlite3ExprListAppend(pParse, yymsp[-4].minor.yy14, yymsp[-2].minor.yy454); + if( yymsp[0].minor.yy0.n>0 ) sqlite3ExprListSetName(pParse, yymsp[-4].minor.yy14, &yymsp[0].minor.yy0, 1); + sqlite3ExprListSetSpan(pParse,yymsp[-4].minor.yy14,yymsp[-3].minor.yy168,yymsp[-1].minor.yy168); +} + break; + case 103: /* selcollist ::= sclp scanpt STAR */ { Expr *p = sqlite3Expr(pParse->db, TK_ASTERISK, 0); sqlite3ExprSetErrorOffset(p, (int)(yymsp[0].minor.yy0.z - pParse->zTail)); - yymsp[-2].minor.yy322 = sqlite3ExprListAppend(pParse, yymsp[-2].minor.yy322, p); + yymsp[-2].minor.yy14 = sqlite3ExprListAppend(pParse, yymsp[-2].minor.yy14, p); } break; - case 102: /* selcollist ::= sclp scanpt nm DOT STAR */ + case 104: /* selcollist ::= sclp scanpt nm DOT STAR */ { Expr *pRight, *pLeft, *pDot; pRight = sqlite3PExpr(pParse, TK_ASTERISK, 0, 0); sqlite3ExprSetErrorOffset(pRight, (int)(yymsp[0].minor.yy0.z - pParse->zTail)); pLeft = tokenExpr(pParse, TK_ID, yymsp[-2].minor.yy0); pDot = sqlite3PExpr(pParse, TK_DOT, pLeft, pRight); - yymsp[-4].minor.yy322 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy322, pDot); + yymsp[-4].minor.yy14 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy14, pDot); } break; - case 103: /* as ::= AS nm */ - case 115: /* dbnm ::= DOT nm */ yytestcase(yyruleno==115); - case 256: /* plus_num ::= PLUS INTEGER|FLOAT */ yytestcase(yyruleno==256); - case 257: /* minus_num ::= MINUS INTEGER|FLOAT */ yytestcase(yyruleno==257); + case 105: /* as ::= AS nm */ + case 117: /* dbnm ::= DOT nm */ yytestcase(yyruleno==117); + case 258: /* plus_num ::= PLUS INTEGER|FLOAT */ yytestcase(yyruleno==258); + case 259: /* minus_num ::= MINUS INTEGER|FLOAT */ yytestcase(yyruleno==259); {yymsp[-1].minor.yy0 = yymsp[0].minor.yy0;} break; - case 105: /* from ::= */ - case 108: /* stl_prefix ::= */ yytestcase(yyruleno==108); -{yymsp[1].minor.yy131 = 0;} + case 107: /* from ::= */ + case 110: /* stl_prefix ::= */ yytestcase(yyruleno==110); +{yymsp[1].minor.yy203 = 0;} break; - case 106: /* from ::= FROM seltablist */ + case 108: /* from ::= FROM seltablist */ { - yymsp[-1].minor.yy131 = yymsp[0].minor.yy131; - sqlite3SrcListShiftJoinType(pParse,yymsp[-1].minor.yy131); + yymsp[-1].minor.yy203 = yymsp[0].minor.yy203; + sqlite3SrcListShiftJoinType(pParse,yymsp[-1].minor.yy203); } break; - case 107: /* stl_prefix ::= seltablist joinop */ + case 109: /* stl_prefix ::= seltablist joinop */ { - if( ALWAYS(yymsp[-1].minor.yy131 && yymsp[-1].minor.yy131->nSrc>0) ) yymsp[-1].minor.yy131->a[yymsp[-1].minor.yy131->nSrc-1].fg.jointype = (u8)yymsp[0].minor.yy394; + if( ALWAYS(yymsp[-1].minor.yy203 && yymsp[-1].minor.yy203->nSrc>0) ) yymsp[-1].minor.yy203->a[yymsp[-1].minor.yy203->nSrc-1].fg.jointype = (u8)yymsp[0].minor.yy144; } break; - case 109: /* seltablist ::= stl_prefix nm dbnm as on_using */ + case 111: /* seltablist ::= stl_prefix nm dbnm as on_using */ { - yymsp[-4].minor.yy131 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-4].minor.yy131,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0,0,&yymsp[0].minor.yy561); + yymsp[-4].minor.yy203 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-4].minor.yy203,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0,0,&yymsp[0].minor.yy269); } break; - case 110: /* seltablist ::= stl_prefix nm dbnm as indexed_by on_using */ + case 112: /* seltablist ::= stl_prefix nm dbnm as indexed_by on_using */ { - yymsp[-5].minor.yy131 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-5].minor.yy131,&yymsp[-4].minor.yy0,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,0,&yymsp[0].minor.yy561); - sqlite3SrcListIndexedBy(pParse, yymsp[-5].minor.yy131, &yymsp[-1].minor.yy0); + yymsp[-5].minor.yy203 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-5].minor.yy203,&yymsp[-4].minor.yy0,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,0,&yymsp[0].minor.yy269); + sqlite3SrcListIndexedBy(pParse, yymsp[-5].minor.yy203, &yymsp[-1].minor.yy0); } break; - case 111: /* seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_using */ + case 113: /* seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_using */ { - yymsp[-7].minor.yy131 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-7].minor.yy131,&yymsp[-6].minor.yy0,&yymsp[-5].minor.yy0,&yymsp[-1].minor.yy0,0,&yymsp[0].minor.yy561); - sqlite3SrcListFuncArgs(pParse, yymsp[-7].minor.yy131, yymsp[-3].minor.yy322); + yymsp[-7].minor.yy203 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-7].minor.yy203,&yymsp[-6].minor.yy0,&yymsp[-5].minor.yy0,&yymsp[-1].minor.yy0,0,&yymsp[0].minor.yy269); + sqlite3SrcListFuncArgs(pParse, yymsp[-7].minor.yy203, yymsp[-3].minor.yy14); } break; - case 112: /* seltablist ::= stl_prefix LP select RP as on_using */ + case 114: /* seltablist ::= stl_prefix LP select RP as on_using */ { - yymsp[-5].minor.yy131 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-5].minor.yy131,0,0,&yymsp[-1].minor.yy0,yymsp[-3].minor.yy47,&yymsp[0].minor.yy561); + yymsp[-5].minor.yy203 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-5].minor.yy203,0,0,&yymsp[-1].minor.yy0,yymsp[-3].minor.yy555,&yymsp[0].minor.yy269); } break; - case 113: /* seltablist ::= stl_prefix LP seltablist RP as on_using */ + case 115: /* seltablist ::= stl_prefix LP seltablist RP as on_using */ { - if( yymsp[-5].minor.yy131==0 && yymsp[-1].minor.yy0.n==0 && yymsp[0].minor.yy561.pOn==0 && yymsp[0].minor.yy561.pUsing==0 ){ - yymsp[-5].minor.yy131 = yymsp[-3].minor.yy131; - }else if( ALWAYS(yymsp[-3].minor.yy131!=0) && yymsp[-3].minor.yy131->nSrc==1 ){ - yymsp[-5].minor.yy131 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-5].minor.yy131,0,0,&yymsp[-1].minor.yy0,0,&yymsp[0].minor.yy561); - if( yymsp[-5].minor.yy131 ){ - SrcItem *pNew = &yymsp[-5].minor.yy131->a[yymsp[-5].minor.yy131->nSrc-1]; - SrcItem *pOld = yymsp[-3].minor.yy131->a; + if( yymsp[-5].minor.yy203==0 && yymsp[-1].minor.yy0.n==0 && yymsp[0].minor.yy269.pOn==0 && yymsp[0].minor.yy269.pUsing==0 ){ + yymsp[-5].minor.yy203 = yymsp[-3].minor.yy203; + }else if( ALWAYS(yymsp[-3].minor.yy203!=0) && yymsp[-3].minor.yy203->nSrc==1 ){ + yymsp[-5].minor.yy203 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-5].minor.yy203,0,0,&yymsp[-1].minor.yy0,0,&yymsp[0].minor.yy269); + if( yymsp[-5].minor.yy203 ){ + SrcItem *pNew = &yymsp[-5].minor.yy203->a[yymsp[-5].minor.yy203->nSrc-1]; + SrcItem *pOld = yymsp[-3].minor.yy203->a; pNew->zName = pOld->zName; pNew->zDatabase = pOld->zDatabase; pNew->pSelect = pOld->pSelect; @@ -175200,153 +176599,153 @@ static YYACTIONTYPE yy_reduce( pOld->zName = pOld->zDatabase = 0; pOld->pSelect = 0; } - sqlite3SrcListDelete(pParse->db, yymsp[-3].minor.yy131); + sqlite3SrcListDelete(pParse->db, yymsp[-3].minor.yy203); }else{ Select *pSubquery; - sqlite3SrcListShiftJoinType(pParse,yymsp[-3].minor.yy131); - pSubquery = sqlite3SelectNew(pParse,0,yymsp[-3].minor.yy131,0,0,0,0,SF_NestedFrom,0); - yymsp[-5].minor.yy131 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-5].minor.yy131,0,0,&yymsp[-1].minor.yy0,pSubquery,&yymsp[0].minor.yy561); + sqlite3SrcListShiftJoinType(pParse,yymsp[-3].minor.yy203); + pSubquery = sqlite3SelectNew(pParse,0,yymsp[-3].minor.yy203,0,0,0,0,SF_NestedFrom,0); + yymsp[-5].minor.yy203 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-5].minor.yy203,0,0,&yymsp[-1].minor.yy0,pSubquery,&yymsp[0].minor.yy269); } } break; - case 114: /* dbnm ::= */ - case 129: /* indexed_opt ::= */ yytestcase(yyruleno==129); + case 116: /* dbnm ::= */ + case 131: /* indexed_opt ::= */ yytestcase(yyruleno==131); {yymsp[1].minor.yy0.z=0; yymsp[1].minor.yy0.n=0;} break; - case 116: /* fullname ::= nm */ + case 118: /* fullname ::= nm */ { - yylhsminor.yy131 = sqlite3SrcListAppend(pParse,0,&yymsp[0].minor.yy0,0); - if( IN_RENAME_OBJECT && yylhsminor.yy131 ) sqlite3RenameTokenMap(pParse, yylhsminor.yy131->a[0].zName, &yymsp[0].minor.yy0); + yylhsminor.yy203 = sqlite3SrcListAppend(pParse,0,&yymsp[0].minor.yy0,0); + if( IN_RENAME_OBJECT && yylhsminor.yy203 ) sqlite3RenameTokenMap(pParse, yylhsminor.yy203->a[0].zName, &yymsp[0].minor.yy0); } - yymsp[0].minor.yy131 = yylhsminor.yy131; + yymsp[0].minor.yy203 = yylhsminor.yy203; break; - case 117: /* fullname ::= nm DOT nm */ + case 119: /* fullname ::= nm DOT nm */ { - yylhsminor.yy131 = sqlite3SrcListAppend(pParse,0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0); - if( IN_RENAME_OBJECT && yylhsminor.yy131 ) sqlite3RenameTokenMap(pParse, yylhsminor.yy131->a[0].zName, &yymsp[0].minor.yy0); + yylhsminor.yy203 = sqlite3SrcListAppend(pParse,0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0); + if( IN_RENAME_OBJECT && yylhsminor.yy203 ) sqlite3RenameTokenMap(pParse, yylhsminor.yy203->a[0].zName, &yymsp[0].minor.yy0); } - yymsp[-2].minor.yy131 = yylhsminor.yy131; + yymsp[-2].minor.yy203 = yylhsminor.yy203; break; - case 118: /* xfullname ::= nm */ -{yymsp[0].minor.yy131 = sqlite3SrcListAppend(pParse,0,&yymsp[0].minor.yy0,0); /*A-overwrites-X*/} + case 120: /* xfullname ::= nm */ +{yymsp[0].minor.yy203 = sqlite3SrcListAppend(pParse,0,&yymsp[0].minor.yy0,0); /*A-overwrites-X*/} break; - case 119: /* xfullname ::= nm DOT nm */ -{yymsp[-2].minor.yy131 = sqlite3SrcListAppend(pParse,0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0); /*A-overwrites-X*/} + case 121: /* xfullname ::= nm DOT nm */ +{yymsp[-2].minor.yy203 = sqlite3SrcListAppend(pParse,0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0); /*A-overwrites-X*/} break; - case 120: /* xfullname ::= nm DOT nm AS nm */ + case 122: /* xfullname ::= nm DOT nm AS nm */ { - yymsp[-4].minor.yy131 = sqlite3SrcListAppend(pParse,0,&yymsp[-4].minor.yy0,&yymsp[-2].minor.yy0); /*A-overwrites-X*/ - if( yymsp[-4].minor.yy131 ) yymsp[-4].minor.yy131->a[0].zAlias = sqlite3NameFromToken(pParse->db, &yymsp[0].minor.yy0); + yymsp[-4].minor.yy203 = sqlite3SrcListAppend(pParse,0,&yymsp[-4].minor.yy0,&yymsp[-2].minor.yy0); /*A-overwrites-X*/ + if( yymsp[-4].minor.yy203 ) yymsp[-4].minor.yy203->a[0].zAlias = sqlite3NameFromToken(pParse->db, &yymsp[0].minor.yy0); } break; - case 121: /* xfullname ::= nm AS nm */ + case 123: /* xfullname ::= nm AS nm */ { - yymsp[-2].minor.yy131 = sqlite3SrcListAppend(pParse,0,&yymsp[-2].minor.yy0,0); /*A-overwrites-X*/ - if( yymsp[-2].minor.yy131 ) yymsp[-2].minor.yy131->a[0].zAlias = sqlite3NameFromToken(pParse->db, &yymsp[0].minor.yy0); + yymsp[-2].minor.yy203 = sqlite3SrcListAppend(pParse,0,&yymsp[-2].minor.yy0,0); /*A-overwrites-X*/ + if( yymsp[-2].minor.yy203 ) yymsp[-2].minor.yy203->a[0].zAlias = sqlite3NameFromToken(pParse->db, &yymsp[0].minor.yy0); } break; - case 122: /* joinop ::= COMMA|JOIN */ -{ yymsp[0].minor.yy394 = JT_INNER; } + case 124: /* joinop ::= COMMA|JOIN */ +{ yymsp[0].minor.yy144 = JT_INNER; } break; - case 123: /* joinop ::= JOIN_KW JOIN */ -{yymsp[-1].minor.yy394 = sqlite3JoinType(pParse,&yymsp[-1].minor.yy0,0,0); /*X-overwrites-A*/} + case 125: /* joinop ::= JOIN_KW JOIN */ +{yymsp[-1].minor.yy144 = sqlite3JoinType(pParse,&yymsp[-1].minor.yy0,0,0); /*X-overwrites-A*/} break; - case 124: /* joinop ::= JOIN_KW nm JOIN */ -{yymsp[-2].minor.yy394 = sqlite3JoinType(pParse,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0,0); /*X-overwrites-A*/} + case 126: /* joinop ::= JOIN_KW nm JOIN */ +{yymsp[-2].minor.yy144 = sqlite3JoinType(pParse,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0,0); /*X-overwrites-A*/} break; - case 125: /* joinop ::= JOIN_KW nm nm JOIN */ -{yymsp[-3].minor.yy394 = sqlite3JoinType(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0);/*X-overwrites-A*/} + case 127: /* joinop ::= JOIN_KW nm nm JOIN */ +{yymsp[-3].minor.yy144 = sqlite3JoinType(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0);/*X-overwrites-A*/} break; - case 126: /* on_using ::= ON expr */ -{yymsp[-1].minor.yy561.pOn = yymsp[0].minor.yy528; yymsp[-1].minor.yy561.pUsing = 0;} + case 128: /* on_using ::= ON expr */ +{yymsp[-1].minor.yy269.pOn = yymsp[0].minor.yy454; yymsp[-1].minor.yy269.pUsing = 0;} break; - case 127: /* on_using ::= USING LP idlist RP */ -{yymsp[-3].minor.yy561.pOn = 0; yymsp[-3].minor.yy561.pUsing = yymsp[-1].minor.yy254;} + case 129: /* on_using ::= USING LP idlist RP */ +{yymsp[-3].minor.yy269.pOn = 0; yymsp[-3].minor.yy269.pUsing = yymsp[-1].minor.yy132;} break; - case 128: /* on_using ::= */ -{yymsp[1].minor.yy561.pOn = 0; yymsp[1].minor.yy561.pUsing = 0;} + case 130: /* on_using ::= */ +{yymsp[1].minor.yy269.pOn = 0; yymsp[1].minor.yy269.pUsing = 0;} break; - case 130: /* indexed_by ::= INDEXED BY nm */ + case 132: /* indexed_by ::= INDEXED BY nm */ {yymsp[-2].minor.yy0 = yymsp[0].minor.yy0;} break; - case 131: /* indexed_by ::= NOT INDEXED */ + case 133: /* indexed_by ::= NOT INDEXED */ {yymsp[-1].minor.yy0.z=0; yymsp[-1].minor.yy0.n=1;} break; - case 133: /* orderby_opt ::= ORDER BY sortlist */ - case 143: /* groupby_opt ::= GROUP BY nexprlist */ yytestcase(yyruleno==143); -{yymsp[-2].minor.yy322 = yymsp[0].minor.yy322;} + case 135: /* orderby_opt ::= ORDER BY sortlist */ + case 145: /* groupby_opt ::= GROUP BY nexprlist */ yytestcase(yyruleno==145); +{yymsp[-2].minor.yy14 = yymsp[0].minor.yy14;} break; - case 134: /* sortlist ::= sortlist COMMA expr sortorder nulls */ + case 136: /* sortlist ::= sortlist COMMA expr sortorder nulls */ { - yymsp[-4].minor.yy322 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy322,yymsp[-2].minor.yy528); - sqlite3ExprListSetSortOrder(yymsp[-4].minor.yy322,yymsp[-1].minor.yy394,yymsp[0].minor.yy394); + yymsp[-4].minor.yy14 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy14,yymsp[-2].minor.yy454); + sqlite3ExprListSetSortOrder(yymsp[-4].minor.yy14,yymsp[-1].minor.yy144,yymsp[0].minor.yy144); } break; - case 135: /* sortlist ::= expr sortorder nulls */ + case 137: /* sortlist ::= expr sortorder nulls */ { - yymsp[-2].minor.yy322 = sqlite3ExprListAppend(pParse,0,yymsp[-2].minor.yy528); /*A-overwrites-Y*/ - sqlite3ExprListSetSortOrder(yymsp[-2].minor.yy322,yymsp[-1].minor.yy394,yymsp[0].minor.yy394); + yymsp[-2].minor.yy14 = sqlite3ExprListAppend(pParse,0,yymsp[-2].minor.yy454); /*A-overwrites-Y*/ + sqlite3ExprListSetSortOrder(yymsp[-2].minor.yy14,yymsp[-1].minor.yy144,yymsp[0].minor.yy144); } break; - case 136: /* sortorder ::= ASC */ -{yymsp[0].minor.yy394 = SQLITE_SO_ASC;} + case 138: /* sortorder ::= ASC */ +{yymsp[0].minor.yy144 = SQLITE_SO_ASC;} break; - case 137: /* sortorder ::= DESC */ -{yymsp[0].minor.yy394 = SQLITE_SO_DESC;} + case 139: /* sortorder ::= DESC */ +{yymsp[0].minor.yy144 = SQLITE_SO_DESC;} break; - case 138: /* sortorder ::= */ - case 141: /* nulls ::= */ yytestcase(yyruleno==141); -{yymsp[1].minor.yy394 = SQLITE_SO_UNDEFINED;} + case 140: /* sortorder ::= */ + case 143: /* nulls ::= */ yytestcase(yyruleno==143); +{yymsp[1].minor.yy144 = SQLITE_SO_UNDEFINED;} break; - case 139: /* nulls ::= NULLS FIRST */ -{yymsp[-1].minor.yy394 = SQLITE_SO_ASC;} + case 141: /* nulls ::= NULLS FIRST */ +{yymsp[-1].minor.yy144 = SQLITE_SO_ASC;} break; - case 140: /* nulls ::= NULLS LAST */ -{yymsp[-1].minor.yy394 = SQLITE_SO_DESC;} + case 142: /* nulls ::= NULLS LAST */ +{yymsp[-1].minor.yy144 = SQLITE_SO_DESC;} break; - case 144: /* having_opt ::= */ - case 146: /* limit_opt ::= */ yytestcase(yyruleno==146); - case 151: /* where_opt ::= */ yytestcase(yyruleno==151); - case 153: /* where_opt_ret ::= */ yytestcase(yyruleno==153); - case 230: /* case_else ::= */ yytestcase(yyruleno==230); - case 231: /* case_operand ::= */ yytestcase(yyruleno==231); - case 250: /* vinto ::= */ yytestcase(yyruleno==250); -{yymsp[1].minor.yy528 = 0;} + case 146: /* having_opt ::= */ + case 148: /* limit_opt ::= */ yytestcase(yyruleno==148); + case 153: /* where_opt ::= */ yytestcase(yyruleno==153); + case 155: /* where_opt_ret ::= */ yytestcase(yyruleno==155); + case 232: /* case_else ::= */ yytestcase(yyruleno==232); + case 233: /* case_operand ::= */ yytestcase(yyruleno==233); + case 252: /* vinto ::= */ yytestcase(yyruleno==252); +{yymsp[1].minor.yy454 = 0;} break; - case 145: /* having_opt ::= HAVING expr */ - case 152: /* where_opt ::= WHERE expr */ yytestcase(yyruleno==152); - case 154: /* where_opt_ret ::= WHERE expr */ yytestcase(yyruleno==154); - case 229: /* case_else ::= ELSE expr */ yytestcase(yyruleno==229); - case 249: /* vinto ::= INTO expr */ yytestcase(yyruleno==249); -{yymsp[-1].minor.yy528 = yymsp[0].minor.yy528;} + case 147: /* having_opt ::= HAVING expr */ + case 154: /* where_opt ::= WHERE expr */ yytestcase(yyruleno==154); + case 156: /* where_opt_ret ::= WHERE expr */ yytestcase(yyruleno==156); + case 231: /* case_else ::= ELSE expr */ yytestcase(yyruleno==231); + case 251: /* vinto ::= INTO expr */ yytestcase(yyruleno==251); +{yymsp[-1].minor.yy454 = yymsp[0].minor.yy454;} break; - case 147: /* limit_opt ::= LIMIT expr */ -{yymsp[-1].minor.yy528 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[0].minor.yy528,0);} + case 149: /* limit_opt ::= LIMIT expr */ +{yymsp[-1].minor.yy454 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[0].minor.yy454,0);} break; - case 148: /* limit_opt ::= LIMIT expr OFFSET expr */ -{yymsp[-3].minor.yy528 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[-2].minor.yy528,yymsp[0].minor.yy528);} + case 150: /* limit_opt ::= LIMIT expr OFFSET expr */ +{yymsp[-3].minor.yy454 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[-2].minor.yy454,yymsp[0].minor.yy454);} break; - case 149: /* limit_opt ::= LIMIT expr COMMA expr */ -{yymsp[-3].minor.yy528 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[0].minor.yy528,yymsp[-2].minor.yy528);} + case 151: /* limit_opt ::= LIMIT expr COMMA expr */ +{yymsp[-3].minor.yy454 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[0].minor.yy454,yymsp[-2].minor.yy454);} break; - case 150: /* cmd ::= with DELETE FROM xfullname indexed_opt where_opt_ret */ + case 152: /* cmd ::= with DELETE FROM xfullname indexed_opt where_opt_ret */ { - sqlite3SrcListIndexedBy(pParse, yymsp[-2].minor.yy131, &yymsp[-1].minor.yy0); - sqlite3DeleteFrom(pParse,yymsp[-2].minor.yy131,yymsp[0].minor.yy528,0,0); + sqlite3SrcListIndexedBy(pParse, yymsp[-2].minor.yy203, &yymsp[-1].minor.yy0); + sqlite3DeleteFrom(pParse,yymsp[-2].minor.yy203,yymsp[0].minor.yy454,0,0); } break; - case 155: /* where_opt_ret ::= RETURNING selcollist */ -{sqlite3AddReturning(pParse,yymsp[0].minor.yy322); yymsp[-1].minor.yy528 = 0;} + case 157: /* where_opt_ret ::= RETURNING selcollist */ +{sqlite3AddReturning(pParse,yymsp[0].minor.yy14); yymsp[-1].minor.yy454 = 0;} break; - case 156: /* where_opt_ret ::= WHERE expr RETURNING selcollist */ -{sqlite3AddReturning(pParse,yymsp[0].minor.yy322); yymsp[-3].minor.yy528 = yymsp[-2].minor.yy528;} + case 158: /* where_opt_ret ::= WHERE expr RETURNING selcollist */ +{sqlite3AddReturning(pParse,yymsp[0].minor.yy14); yymsp[-3].minor.yy454 = yymsp[-2].minor.yy454;} break; - case 157: /* cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist from where_opt_ret */ + case 159: /* cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist from where_opt_ret */ { - sqlite3SrcListIndexedBy(pParse, yymsp[-5].minor.yy131, &yymsp[-4].minor.yy0); - sqlite3ExprListCheckLength(pParse,yymsp[-2].minor.yy322,"set list"); - if( yymsp[-1].minor.yy131 ){ - SrcList *pFromClause = yymsp[-1].minor.yy131; + sqlite3SrcListIndexedBy(pParse, yymsp[-5].minor.yy203, &yymsp[-4].minor.yy0); + sqlite3ExprListCheckLength(pParse,yymsp[-2].minor.yy14,"set list"); + if( yymsp[-1].minor.yy203 ){ + SrcList *pFromClause = yymsp[-1].minor.yy203; if( pFromClause->nSrc>1 ){ Select *pSubquery; Token as; @@ -175355,92 +176754,92 @@ static YYACTIONTYPE yy_reduce( as.z = 0; pFromClause = sqlite3SrcListAppendFromTerm(pParse,0,0,0,&as,pSubquery,0); } - yymsp[-5].minor.yy131 = sqlite3SrcListAppendList(pParse, yymsp[-5].minor.yy131, pFromClause); + yymsp[-5].minor.yy203 = sqlite3SrcListAppendList(pParse, yymsp[-5].minor.yy203, pFromClause); } - sqlite3Update(pParse,yymsp[-5].minor.yy131,yymsp[-2].minor.yy322,yymsp[0].minor.yy528,yymsp[-6].minor.yy394,0,0,0); + sqlite3Update(pParse,yymsp[-5].minor.yy203,yymsp[-2].minor.yy14,yymsp[0].minor.yy454,yymsp[-6].minor.yy144,0,0,0); } break; - case 158: /* setlist ::= setlist COMMA nm EQ expr */ + case 160: /* setlist ::= setlist COMMA nm EQ expr */ { - yymsp[-4].minor.yy322 = sqlite3ExprListAppend(pParse, yymsp[-4].minor.yy322, yymsp[0].minor.yy528); - sqlite3ExprListSetName(pParse, yymsp[-4].minor.yy322, &yymsp[-2].minor.yy0, 1); + yymsp[-4].minor.yy14 = sqlite3ExprListAppend(pParse, yymsp[-4].minor.yy14, yymsp[0].minor.yy454); + sqlite3ExprListSetName(pParse, yymsp[-4].minor.yy14, &yymsp[-2].minor.yy0, 1); } break; - case 159: /* setlist ::= setlist COMMA LP idlist RP EQ expr */ + case 161: /* setlist ::= setlist COMMA LP idlist RP EQ expr */ { - yymsp[-6].minor.yy322 = sqlite3ExprListAppendVector(pParse, yymsp[-6].minor.yy322, yymsp[-3].minor.yy254, yymsp[0].minor.yy528); + yymsp[-6].minor.yy14 = sqlite3ExprListAppendVector(pParse, yymsp[-6].minor.yy14, yymsp[-3].minor.yy132, yymsp[0].minor.yy454); } break; - case 160: /* setlist ::= nm EQ expr */ + case 162: /* setlist ::= nm EQ expr */ { - yylhsminor.yy322 = sqlite3ExprListAppend(pParse, 0, yymsp[0].minor.yy528); - sqlite3ExprListSetName(pParse, yylhsminor.yy322, &yymsp[-2].minor.yy0, 1); + yylhsminor.yy14 = sqlite3ExprListAppend(pParse, 0, yymsp[0].minor.yy454); + sqlite3ExprListSetName(pParse, yylhsminor.yy14, &yymsp[-2].minor.yy0, 1); } - yymsp[-2].minor.yy322 = yylhsminor.yy322; + yymsp[-2].minor.yy14 = yylhsminor.yy14; break; - case 161: /* setlist ::= LP idlist RP EQ expr */ + case 163: /* setlist ::= LP idlist RP EQ expr */ { - yymsp[-4].minor.yy322 = sqlite3ExprListAppendVector(pParse, 0, yymsp[-3].minor.yy254, yymsp[0].minor.yy528); + yymsp[-4].minor.yy14 = sqlite3ExprListAppendVector(pParse, 0, yymsp[-3].minor.yy132, yymsp[0].minor.yy454); } break; - case 162: /* cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert */ + case 164: /* cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert */ { - sqlite3Insert(pParse, yymsp[-3].minor.yy131, yymsp[-1].minor.yy47, yymsp[-2].minor.yy254, yymsp[-5].minor.yy394, yymsp[0].minor.yy444); + sqlite3Insert(pParse, yymsp[-3].minor.yy203, yymsp[-1].minor.yy555, yymsp[-2].minor.yy132, yymsp[-5].minor.yy144, yymsp[0].minor.yy122); } break; - case 163: /* cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES returning */ + case 165: /* cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES returning */ { - sqlite3Insert(pParse, yymsp[-4].minor.yy131, 0, yymsp[-3].minor.yy254, yymsp[-6].minor.yy394, 0); + sqlite3Insert(pParse, yymsp[-4].minor.yy203, 0, yymsp[-3].minor.yy132, yymsp[-6].minor.yy144, 0); } break; - case 164: /* upsert ::= */ -{ yymsp[1].minor.yy444 = 0; } + case 166: /* upsert ::= */ +{ yymsp[1].minor.yy122 = 0; } break; - case 165: /* upsert ::= RETURNING selcollist */ -{ yymsp[-1].minor.yy444 = 0; sqlite3AddReturning(pParse,yymsp[0].minor.yy322); } + case 167: /* upsert ::= RETURNING selcollist */ +{ yymsp[-1].minor.yy122 = 0; sqlite3AddReturning(pParse,yymsp[0].minor.yy14); } break; - case 166: /* upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt upsert */ -{ yymsp[-11].minor.yy444 = sqlite3UpsertNew(pParse->db,yymsp[-8].minor.yy322,yymsp[-6].minor.yy528,yymsp[-2].minor.yy322,yymsp[-1].minor.yy528,yymsp[0].minor.yy444);} + case 168: /* upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt upsert */ +{ yymsp[-11].minor.yy122 = sqlite3UpsertNew(pParse->db,yymsp[-8].minor.yy14,yymsp[-6].minor.yy454,yymsp[-2].minor.yy14,yymsp[-1].minor.yy454,yymsp[0].minor.yy122);} break; - case 167: /* upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING upsert */ -{ yymsp[-8].minor.yy444 = sqlite3UpsertNew(pParse->db,yymsp[-5].minor.yy322,yymsp[-3].minor.yy528,0,0,yymsp[0].minor.yy444); } + case 169: /* upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING upsert */ +{ yymsp[-8].minor.yy122 = sqlite3UpsertNew(pParse->db,yymsp[-5].minor.yy14,yymsp[-3].minor.yy454,0,0,yymsp[0].minor.yy122); } break; - case 168: /* upsert ::= ON CONFLICT DO NOTHING returning */ -{ yymsp[-4].minor.yy444 = sqlite3UpsertNew(pParse->db,0,0,0,0,0); } + case 170: /* upsert ::= ON CONFLICT DO NOTHING returning */ +{ yymsp[-4].minor.yy122 = sqlite3UpsertNew(pParse->db,0,0,0,0,0); } break; - case 169: /* upsert ::= ON CONFLICT DO UPDATE SET setlist where_opt returning */ -{ yymsp[-7].minor.yy444 = sqlite3UpsertNew(pParse->db,0,0,yymsp[-2].minor.yy322,yymsp[-1].minor.yy528,0);} + case 171: /* upsert ::= ON CONFLICT DO UPDATE SET setlist where_opt returning */ +{ yymsp[-7].minor.yy122 = sqlite3UpsertNew(pParse->db,0,0,yymsp[-2].minor.yy14,yymsp[-1].minor.yy454,0);} break; - case 170: /* returning ::= RETURNING selcollist */ -{sqlite3AddReturning(pParse,yymsp[0].minor.yy322);} + case 172: /* returning ::= RETURNING selcollist */ +{sqlite3AddReturning(pParse,yymsp[0].minor.yy14);} break; - case 173: /* idlist_opt ::= */ -{yymsp[1].minor.yy254 = 0;} + case 175: /* idlist_opt ::= */ +{yymsp[1].minor.yy132 = 0;} break; - case 174: /* idlist_opt ::= LP idlist RP */ -{yymsp[-2].minor.yy254 = yymsp[-1].minor.yy254;} + case 176: /* idlist_opt ::= LP idlist RP */ +{yymsp[-2].minor.yy132 = yymsp[-1].minor.yy132;} break; - case 175: /* idlist ::= idlist COMMA nm */ -{yymsp[-2].minor.yy254 = sqlite3IdListAppend(pParse,yymsp[-2].minor.yy254,&yymsp[0].minor.yy0);} + case 177: /* idlist ::= idlist COMMA nm */ +{yymsp[-2].minor.yy132 = sqlite3IdListAppend(pParse,yymsp[-2].minor.yy132,&yymsp[0].minor.yy0);} break; - case 176: /* idlist ::= nm */ -{yymsp[0].minor.yy254 = sqlite3IdListAppend(pParse,0,&yymsp[0].minor.yy0); /*A-overwrites-Y*/} + case 178: /* idlist ::= nm */ +{yymsp[0].minor.yy132 = sqlite3IdListAppend(pParse,0,&yymsp[0].minor.yy0); /*A-overwrites-Y*/} break; - case 177: /* expr ::= LP expr RP */ -{yymsp[-2].minor.yy528 = yymsp[-1].minor.yy528;} + case 179: /* expr ::= LP expr RP */ +{yymsp[-2].minor.yy454 = yymsp[-1].minor.yy454;} break; - case 178: /* expr ::= ID|INDEXED|JOIN_KW */ -{yymsp[0].minor.yy528=tokenExpr(pParse,TK_ID,yymsp[0].minor.yy0); /*A-overwrites-X*/} + case 180: /* expr ::= ID|INDEXED|JOIN_KW */ +{yymsp[0].minor.yy454=tokenExpr(pParse,TK_ID,yymsp[0].minor.yy0); /*A-overwrites-X*/} break; - case 179: /* expr ::= nm DOT nm */ + case 181: /* expr ::= nm DOT nm */ { Expr *temp1 = tokenExpr(pParse,TK_ID,yymsp[-2].minor.yy0); Expr *temp2 = tokenExpr(pParse,TK_ID,yymsp[0].minor.yy0); - yylhsminor.yy528 = sqlite3PExpr(pParse, TK_DOT, temp1, temp2); + yylhsminor.yy454 = sqlite3PExpr(pParse, TK_DOT, temp1, temp2); } - yymsp[-2].minor.yy528 = yylhsminor.yy528; + yymsp[-2].minor.yy454 = yylhsminor.yy454; break; - case 180: /* expr ::= nm DOT nm DOT nm */ + case 182: /* expr ::= nm DOT nm DOT nm */ { Expr *temp1 = tokenExpr(pParse,TK_ID,yymsp[-4].minor.yy0); Expr *temp2 = tokenExpr(pParse,TK_ID,yymsp[-2].minor.yy0); @@ -175449,27 +176848,27 @@ static YYACTIONTYPE yy_reduce( if( IN_RENAME_OBJECT ){ sqlite3RenameTokenRemap(pParse, 0, temp1); } - yylhsminor.yy528 = sqlite3PExpr(pParse, TK_DOT, temp1, temp4); + yylhsminor.yy454 = sqlite3PExpr(pParse, TK_DOT, temp1, temp4); } - yymsp[-4].minor.yy528 = yylhsminor.yy528; + yymsp[-4].minor.yy454 = yylhsminor.yy454; break; - case 181: /* term ::= NULL|FLOAT|BLOB */ - case 182: /* term ::= STRING */ yytestcase(yyruleno==182); -{yymsp[0].minor.yy528=tokenExpr(pParse,yymsp[0].major,yymsp[0].minor.yy0); /*A-overwrites-X*/} + case 183: /* term ::= NULL|FLOAT|BLOB */ + case 184: /* term ::= STRING */ yytestcase(yyruleno==184); +{yymsp[0].minor.yy454=tokenExpr(pParse,yymsp[0].major,yymsp[0].minor.yy0); /*A-overwrites-X*/} break; - case 183: /* term ::= INTEGER */ + case 185: /* term ::= INTEGER */ { - yylhsminor.yy528 = sqlite3ExprAlloc(pParse->db, TK_INTEGER, &yymsp[0].minor.yy0, 1); - if( yylhsminor.yy528 ) yylhsminor.yy528->w.iOfst = (int)(yymsp[0].minor.yy0.z - pParse->zTail); + yylhsminor.yy454 = sqlite3ExprAlloc(pParse->db, TK_INTEGER, &yymsp[0].minor.yy0, 1); + if( yylhsminor.yy454 ) yylhsminor.yy454->w.iOfst = (int)(yymsp[0].minor.yy0.z - pParse->zTail); } - yymsp[0].minor.yy528 = yylhsminor.yy528; + yymsp[0].minor.yy454 = yylhsminor.yy454; break; - case 184: /* expr ::= VARIABLE */ + case 186: /* expr ::= VARIABLE */ { if( !(yymsp[0].minor.yy0.z[0]=='#' && sqlite3Isdigit(yymsp[0].minor.yy0.z[1])) ){ u32 n = yymsp[0].minor.yy0.n; - yymsp[0].minor.yy528 = tokenExpr(pParse, TK_VARIABLE, yymsp[0].minor.yy0); - sqlite3ExprAssignVarNumber(pParse, yymsp[0].minor.yy528, n); + yymsp[0].minor.yy454 = tokenExpr(pParse, TK_VARIABLE, yymsp[0].minor.yy0); + sqlite3ExprAssignVarNumber(pParse, yymsp[0].minor.yy454, n); }else{ /* When doing a nested parse, one can include terms in an expression ** that look like this: #1 #2 ... These terms refer to registers @@ -175478,194 +176877,203 @@ static YYACTIONTYPE yy_reduce( assert( t.n>=2 ); if( pParse->nested==0 ){ sqlite3ErrorMsg(pParse, "near \"%T\": syntax error", &t); - yymsp[0].minor.yy528 = 0; + yymsp[0].minor.yy454 = 0; }else{ - yymsp[0].minor.yy528 = sqlite3PExpr(pParse, TK_REGISTER, 0, 0); - if( yymsp[0].minor.yy528 ) sqlite3GetInt32(&t.z[1], &yymsp[0].minor.yy528->iTable); + yymsp[0].minor.yy454 = sqlite3PExpr(pParse, TK_REGISTER, 0, 0); + if( yymsp[0].minor.yy454 ) sqlite3GetInt32(&t.z[1], &yymsp[0].minor.yy454->iTable); } } } break; - case 185: /* expr ::= expr COLLATE ID|STRING */ + case 187: /* expr ::= expr COLLATE ID|STRING */ { - yymsp[-2].minor.yy528 = sqlite3ExprAddCollateToken(pParse, yymsp[-2].minor.yy528, &yymsp[0].minor.yy0, 1); + yymsp[-2].minor.yy454 = sqlite3ExprAddCollateToken(pParse, yymsp[-2].minor.yy454, &yymsp[0].minor.yy0, 1); } break; - case 186: /* expr ::= CAST LP expr AS typetoken RP */ + case 188: /* expr ::= CAST LP expr AS typetoken RP */ { - yymsp[-5].minor.yy528 = sqlite3ExprAlloc(pParse->db, TK_CAST, &yymsp[-1].minor.yy0, 1); - sqlite3ExprAttachSubtrees(pParse->db, yymsp[-5].minor.yy528, yymsp[-3].minor.yy528, 0); + yymsp[-5].minor.yy454 = sqlite3ExprAlloc(pParse->db, TK_CAST, &yymsp[-1].minor.yy0, 1); + sqlite3ExprAttachSubtrees(pParse->db, yymsp[-5].minor.yy454, yymsp[-3].minor.yy454, 0); } break; - case 187: /* expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP */ + case 189: /* expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP */ { - yylhsminor.yy528 = sqlite3ExprFunction(pParse, yymsp[-1].minor.yy322, &yymsp[-4].minor.yy0, yymsp[-2].minor.yy394); + yylhsminor.yy454 = sqlite3ExprFunction(pParse, yymsp[-1].minor.yy14, &yymsp[-4].minor.yy0, yymsp[-2].minor.yy144); } - yymsp[-4].minor.yy528 = yylhsminor.yy528; + yymsp[-4].minor.yy454 = yylhsminor.yy454; break; - case 188: /* expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist ORDER BY sortlist RP */ + case 190: /* expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist ORDER BY sortlist RP */ { - yylhsminor.yy528 = sqlite3ExprFunction(pParse, yymsp[-4].minor.yy322, &yymsp[-7].minor.yy0, yymsp[-5].minor.yy394); - sqlite3ExprAddFunctionOrderBy(pParse, yylhsminor.yy528, yymsp[-1].minor.yy322); + yylhsminor.yy454 = sqlite3ExprFunction(pParse, yymsp[-4].minor.yy14, &yymsp[-7].minor.yy0, yymsp[-5].minor.yy144); + sqlite3ExprAddFunctionOrderBy(pParse, yylhsminor.yy454, yymsp[-1].minor.yy14); } - yymsp[-7].minor.yy528 = yylhsminor.yy528; + yymsp[-7].minor.yy454 = yylhsminor.yy454; break; - case 189: /* expr ::= ID|INDEXED|JOIN_KW LP STAR RP */ + case 191: /* expr ::= ID|INDEXED|JOIN_KW LP STAR RP */ { - yylhsminor.yy528 = sqlite3ExprFunction(pParse, 0, &yymsp[-3].minor.yy0, 0); + yylhsminor.yy454 = sqlite3ExprFunction(pParse, 0, &yymsp[-3].minor.yy0, 0); } - yymsp[-3].minor.yy528 = yylhsminor.yy528; + yymsp[-3].minor.yy454 = yylhsminor.yy454; break; - case 190: /* expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP filter_over */ + case 192: /* expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP filter_over */ { - yylhsminor.yy528 = sqlite3ExprFunction(pParse, yymsp[-2].minor.yy322, &yymsp[-5].minor.yy0, yymsp[-3].minor.yy394); - sqlite3WindowAttach(pParse, yylhsminor.yy528, yymsp[0].minor.yy41); + yylhsminor.yy454 = sqlite3ExprFunction(pParse, yymsp[-2].minor.yy14, &yymsp[-5].minor.yy0, yymsp[-3].minor.yy144); + sqlite3WindowAttach(pParse, yylhsminor.yy454, yymsp[0].minor.yy211); } - yymsp[-5].minor.yy528 = yylhsminor.yy528; + yymsp[-5].minor.yy454 = yylhsminor.yy454; break; - case 191: /* expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist ORDER BY sortlist RP filter_over */ + case 193: /* expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist ORDER BY sortlist RP filter_over */ { - yylhsminor.yy528 = sqlite3ExprFunction(pParse, yymsp[-5].minor.yy322, &yymsp[-8].minor.yy0, yymsp[-6].minor.yy394); - sqlite3WindowAttach(pParse, yylhsminor.yy528, yymsp[0].minor.yy41); - sqlite3ExprAddFunctionOrderBy(pParse, yylhsminor.yy528, yymsp[-2].minor.yy322); + yylhsminor.yy454 = sqlite3ExprFunction(pParse, yymsp[-5].minor.yy14, &yymsp[-8].minor.yy0, yymsp[-6].minor.yy144); + sqlite3WindowAttach(pParse, yylhsminor.yy454, yymsp[0].minor.yy211); + sqlite3ExprAddFunctionOrderBy(pParse, yylhsminor.yy454, yymsp[-2].minor.yy14); } - yymsp[-8].minor.yy528 = yylhsminor.yy528; + yymsp[-8].minor.yy454 = yylhsminor.yy454; break; - case 192: /* expr ::= ID|INDEXED|JOIN_KW LP STAR RP filter_over */ + case 194: /* expr ::= ID|INDEXED|JOIN_KW LP STAR RP filter_over */ { - yylhsminor.yy528 = sqlite3ExprFunction(pParse, 0, &yymsp[-4].minor.yy0, 0); - sqlite3WindowAttach(pParse, yylhsminor.yy528, yymsp[0].minor.yy41); + yylhsminor.yy454 = sqlite3ExprFunction(pParse, 0, &yymsp[-4].minor.yy0, 0); + sqlite3WindowAttach(pParse, yylhsminor.yy454, yymsp[0].minor.yy211); } - yymsp[-4].minor.yy528 = yylhsminor.yy528; + yymsp[-4].minor.yy454 = yylhsminor.yy454; break; - case 193: /* term ::= CTIME_KW */ + case 195: /* term ::= CTIME_KW */ { - yylhsminor.yy528 = sqlite3ExprFunction(pParse, 0, &yymsp[0].minor.yy0, 0); + yylhsminor.yy454 = sqlite3ExprFunction(pParse, 0, &yymsp[0].minor.yy0, 0); } - yymsp[0].minor.yy528 = yylhsminor.yy528; + yymsp[0].minor.yy454 = yylhsminor.yy454; break; - case 194: /* expr ::= LP nexprlist COMMA expr RP */ + case 196: /* expr ::= LP nexprlist COMMA expr RP */ { - ExprList *pList = sqlite3ExprListAppend(pParse, yymsp[-3].minor.yy322, yymsp[-1].minor.yy528); - yymsp[-4].minor.yy528 = sqlite3PExpr(pParse, TK_VECTOR, 0, 0); - if( yymsp[-4].minor.yy528 ){ - yymsp[-4].minor.yy528->x.pList = pList; + ExprList *pList = sqlite3ExprListAppend(pParse, yymsp[-3].minor.yy14, yymsp[-1].minor.yy454); + yymsp[-4].minor.yy454 = sqlite3PExpr(pParse, TK_VECTOR, 0, 0); + if( yymsp[-4].minor.yy454 ){ + yymsp[-4].minor.yy454->x.pList = pList; if( ALWAYS(pList->nExpr) ){ - yymsp[-4].minor.yy528->flags |= pList->a[0].pExpr->flags & EP_Propagate; + yymsp[-4].minor.yy454->flags |= pList->a[0].pExpr->flags & EP_Propagate; } }else{ sqlite3ExprListDelete(pParse->db, pList); } } break; - case 195: /* expr ::= expr AND expr */ -{yymsp[-2].minor.yy528=sqlite3ExprAnd(pParse,yymsp[-2].minor.yy528,yymsp[0].minor.yy528);} + case 197: /* expr ::= expr AND expr */ +{yymsp[-2].minor.yy454=sqlite3ExprAnd(pParse,yymsp[-2].minor.yy454,yymsp[0].minor.yy454);} break; - case 196: /* expr ::= expr OR expr */ - case 197: /* expr ::= expr LT|GT|GE|LE expr */ yytestcase(yyruleno==197); - case 198: /* expr ::= expr EQ|NE expr */ yytestcase(yyruleno==198); - case 199: /* expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */ yytestcase(yyruleno==199); - case 200: /* expr ::= expr PLUS|MINUS expr */ yytestcase(yyruleno==200); - case 201: /* expr ::= expr STAR|SLASH|REM expr */ yytestcase(yyruleno==201); - case 202: /* expr ::= expr CONCAT expr */ yytestcase(yyruleno==202); -{yymsp[-2].minor.yy528=sqlite3PExpr(pParse,yymsp[-1].major,yymsp[-2].minor.yy528,yymsp[0].minor.yy528);} + case 198: /* expr ::= expr OR expr */ + case 199: /* expr ::= expr LT|GT|GE|LE expr */ yytestcase(yyruleno==199); + case 200: /* expr ::= expr EQ|NE expr */ yytestcase(yyruleno==200); + case 201: /* expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */ yytestcase(yyruleno==201); + case 202: /* expr ::= expr PLUS|MINUS expr */ yytestcase(yyruleno==202); + case 203: /* expr ::= expr STAR|SLASH|REM expr */ yytestcase(yyruleno==203); + case 204: /* expr ::= expr CONCAT expr */ yytestcase(yyruleno==204); +{yymsp[-2].minor.yy454=sqlite3PExpr(pParse,yymsp[-1].major,yymsp[-2].minor.yy454,yymsp[0].minor.yy454);} break; - case 203: /* likeop ::= NOT LIKE_KW|MATCH */ + case 205: /* likeop ::= NOT LIKE_KW|MATCH */ {yymsp[-1].minor.yy0=yymsp[0].minor.yy0; yymsp[-1].minor.yy0.n|=0x80000000; /*yymsp[-1].minor.yy0-overwrite-yymsp[0].minor.yy0*/} break; - case 204: /* expr ::= expr likeop expr */ + case 206: /* expr ::= expr likeop expr */ { ExprList *pList; int bNot = yymsp[-1].minor.yy0.n & 0x80000000; yymsp[-1].minor.yy0.n &= 0x7fffffff; - pList = sqlite3ExprListAppend(pParse,0, yymsp[0].minor.yy528); - pList = sqlite3ExprListAppend(pParse,pList, yymsp[-2].minor.yy528); - yymsp[-2].minor.yy528 = sqlite3ExprFunction(pParse, pList, &yymsp[-1].minor.yy0, 0); - if( bNot ) yymsp[-2].minor.yy528 = sqlite3PExpr(pParse, TK_NOT, yymsp[-2].minor.yy528, 0); - if( yymsp[-2].minor.yy528 ) yymsp[-2].minor.yy528->flags |= EP_InfixFunc; + pList = sqlite3ExprListAppend(pParse,0, yymsp[0].minor.yy454); + pList = sqlite3ExprListAppend(pParse,pList, yymsp[-2].minor.yy454); + yymsp[-2].minor.yy454 = sqlite3ExprFunction(pParse, pList, &yymsp[-1].minor.yy0, 0); + if( bNot ) yymsp[-2].minor.yy454 = sqlite3PExpr(pParse, TK_NOT, yymsp[-2].minor.yy454, 0); + if( yymsp[-2].minor.yy454 ) yymsp[-2].minor.yy454->flags |= EP_InfixFunc; } break; - case 205: /* expr ::= expr likeop expr ESCAPE expr */ + case 207: /* expr ::= expr likeop expr ESCAPE expr */ { ExprList *pList; int bNot = yymsp[-3].minor.yy0.n & 0x80000000; yymsp[-3].minor.yy0.n &= 0x7fffffff; - pList = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy528); - pList = sqlite3ExprListAppend(pParse,pList, yymsp[-4].minor.yy528); - pList = sqlite3ExprListAppend(pParse,pList, yymsp[0].minor.yy528); - yymsp[-4].minor.yy528 = sqlite3ExprFunction(pParse, pList, &yymsp[-3].minor.yy0, 0); - if( bNot ) yymsp[-4].minor.yy528 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy528, 0); - if( yymsp[-4].minor.yy528 ) yymsp[-4].minor.yy528->flags |= EP_InfixFunc; + pList = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy454); + pList = sqlite3ExprListAppend(pParse,pList, yymsp[-4].minor.yy454); + pList = sqlite3ExprListAppend(pParse,pList, yymsp[0].minor.yy454); + yymsp[-4].minor.yy454 = sqlite3ExprFunction(pParse, pList, &yymsp[-3].minor.yy0, 0); + if( bNot ) yymsp[-4].minor.yy454 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy454, 0); + if( yymsp[-4].minor.yy454 ) yymsp[-4].minor.yy454->flags |= EP_InfixFunc; } break; - case 206: /* expr ::= expr ISNULL|NOTNULL */ -{yymsp[-1].minor.yy528 = sqlite3PExpr(pParse,yymsp[0].major,yymsp[-1].minor.yy528,0);} + case 208: /* expr ::= expr ISNULL|NOTNULL */ +{yymsp[-1].minor.yy454 = sqlite3PExpr(pParse,yymsp[0].major,yymsp[-1].minor.yy454,0);} break; - case 207: /* expr ::= expr NOT NULL */ -{yymsp[-2].minor.yy528 = sqlite3PExpr(pParse,TK_NOTNULL,yymsp[-2].minor.yy528,0);} + case 209: /* expr ::= expr NOT NULL */ +{yymsp[-2].minor.yy454 = sqlite3PExpr(pParse,TK_NOTNULL,yymsp[-2].minor.yy454,0);} break; - case 208: /* expr ::= expr IS expr */ + case 210: /* expr ::= expr IS expr */ { - yymsp[-2].minor.yy528 = sqlite3PExpr(pParse,TK_IS,yymsp[-2].minor.yy528,yymsp[0].minor.yy528); - binaryToUnaryIfNull(pParse, yymsp[0].minor.yy528, yymsp[-2].minor.yy528, TK_ISNULL); + yymsp[-2].minor.yy454 = sqlite3PExpr(pParse,TK_IS,yymsp[-2].minor.yy454,yymsp[0].minor.yy454); + binaryToUnaryIfNull(pParse, yymsp[0].minor.yy454, yymsp[-2].minor.yy454, TK_ISNULL); } break; - case 209: /* expr ::= expr IS NOT expr */ + case 211: /* expr ::= expr IS NOT expr */ { - yymsp[-3].minor.yy528 = sqlite3PExpr(pParse,TK_ISNOT,yymsp[-3].minor.yy528,yymsp[0].minor.yy528); - binaryToUnaryIfNull(pParse, yymsp[0].minor.yy528, yymsp[-3].minor.yy528, TK_NOTNULL); + yymsp[-3].minor.yy454 = sqlite3PExpr(pParse,TK_ISNOT,yymsp[-3].minor.yy454,yymsp[0].minor.yy454); + binaryToUnaryIfNull(pParse, yymsp[0].minor.yy454, yymsp[-3].minor.yy454, TK_NOTNULL); } break; - case 210: /* expr ::= expr IS NOT DISTINCT FROM expr */ + case 212: /* expr ::= expr IS NOT DISTINCT FROM expr */ { - yymsp[-5].minor.yy528 = sqlite3PExpr(pParse,TK_IS,yymsp[-5].minor.yy528,yymsp[0].minor.yy528); - binaryToUnaryIfNull(pParse, yymsp[0].minor.yy528, yymsp[-5].minor.yy528, TK_ISNULL); + yymsp[-5].minor.yy454 = sqlite3PExpr(pParse,TK_IS,yymsp[-5].minor.yy454,yymsp[0].minor.yy454); + binaryToUnaryIfNull(pParse, yymsp[0].minor.yy454, yymsp[-5].minor.yy454, TK_ISNULL); } break; - case 211: /* expr ::= expr IS DISTINCT FROM expr */ + case 213: /* expr ::= expr IS DISTINCT FROM expr */ { - yymsp[-4].minor.yy528 = sqlite3PExpr(pParse,TK_ISNOT,yymsp[-4].minor.yy528,yymsp[0].minor.yy528); - binaryToUnaryIfNull(pParse, yymsp[0].minor.yy528, yymsp[-4].minor.yy528, TK_NOTNULL); + yymsp[-4].minor.yy454 = sqlite3PExpr(pParse,TK_ISNOT,yymsp[-4].minor.yy454,yymsp[0].minor.yy454); + binaryToUnaryIfNull(pParse, yymsp[0].minor.yy454, yymsp[-4].minor.yy454, TK_NOTNULL); } break; - case 212: /* expr ::= NOT expr */ - case 213: /* expr ::= BITNOT expr */ yytestcase(yyruleno==213); -{yymsp[-1].minor.yy528 = sqlite3PExpr(pParse, yymsp[-1].major, yymsp[0].minor.yy528, 0);/*A-overwrites-B*/} + case 214: /* expr ::= NOT expr */ + case 215: /* expr ::= BITNOT expr */ yytestcase(yyruleno==215); +{yymsp[-1].minor.yy454 = sqlite3PExpr(pParse, yymsp[-1].major, yymsp[0].minor.yy454, 0);/*A-overwrites-B*/} break; - case 214: /* expr ::= PLUS|MINUS expr */ + case 216: /* expr ::= PLUS|MINUS expr */ { - yymsp[-1].minor.yy528 = sqlite3PExpr(pParse, yymsp[-1].major==TK_PLUS ? TK_UPLUS : TK_UMINUS, yymsp[0].minor.yy528, 0); - /*A-overwrites-B*/ + Expr *p = yymsp[0].minor.yy454; + u8 op = yymsp[-1].major + (TK_UPLUS-TK_PLUS); + assert( TK_UPLUS>TK_PLUS ); + assert( TK_UMINUS == TK_MINUS + (TK_UPLUS - TK_PLUS) ); + if( p && p->op==TK_UPLUS ){ + p->op = op; + yymsp[-1].minor.yy454 = p; + }else{ + yymsp[-1].minor.yy454 = sqlite3PExpr(pParse, op, p, 0); + /*A-overwrites-B*/ + } } break; - case 215: /* expr ::= expr PTR expr */ + case 217: /* expr ::= expr PTR expr */ { - ExprList *pList = sqlite3ExprListAppend(pParse, 0, yymsp[-2].minor.yy528); - pList = sqlite3ExprListAppend(pParse, pList, yymsp[0].minor.yy528); - yylhsminor.yy528 = sqlite3ExprFunction(pParse, pList, &yymsp[-1].minor.yy0, 0); + ExprList *pList = sqlite3ExprListAppend(pParse, 0, yymsp[-2].minor.yy454); + pList = sqlite3ExprListAppend(pParse, pList, yymsp[0].minor.yy454); + yylhsminor.yy454 = sqlite3ExprFunction(pParse, pList, &yymsp[-1].minor.yy0, 0); } - yymsp[-2].minor.yy528 = yylhsminor.yy528; + yymsp[-2].minor.yy454 = yylhsminor.yy454; break; - case 216: /* between_op ::= BETWEEN */ - case 219: /* in_op ::= IN */ yytestcase(yyruleno==219); -{yymsp[0].minor.yy394 = 0;} + case 218: /* between_op ::= BETWEEN */ + case 221: /* in_op ::= IN */ yytestcase(yyruleno==221); +{yymsp[0].minor.yy144 = 0;} break; - case 218: /* expr ::= expr between_op expr AND expr */ + case 220: /* expr ::= expr between_op expr AND expr */ { - ExprList *pList = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy528); - pList = sqlite3ExprListAppend(pParse,pList, yymsp[0].minor.yy528); - yymsp[-4].minor.yy528 = sqlite3PExpr(pParse, TK_BETWEEN, yymsp[-4].minor.yy528, 0); - if( yymsp[-4].minor.yy528 ){ - yymsp[-4].minor.yy528->x.pList = pList; + ExprList *pList = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy454); + pList = sqlite3ExprListAppend(pParse,pList, yymsp[0].minor.yy454); + yymsp[-4].minor.yy454 = sqlite3PExpr(pParse, TK_BETWEEN, yymsp[-4].minor.yy454, 0); + if( yymsp[-4].minor.yy454 ){ + yymsp[-4].minor.yy454->x.pList = pList; }else{ sqlite3ExprListDelete(pParse->db, pList); } - if( yymsp[-3].minor.yy394 ) yymsp[-4].minor.yy528 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy528, 0); + if( yymsp[-3].minor.yy144 ) yymsp[-4].minor.yy454 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy454, 0); } break; - case 221: /* expr ::= expr in_op LP exprlist RP */ + case 223: /* expr ::= expr in_op LP exprlist RP */ { - if( yymsp[-1].minor.yy322==0 ){ + if( yymsp[-1].minor.yy14==0 ){ /* Expressions of the form ** ** expr1 IN () @@ -175674,208 +177082,208 @@ static YYACTIONTYPE yy_reduce( ** simplify to constants 0 (false) and 1 (true), respectively, ** regardless of the value of expr1. */ - sqlite3ExprUnmapAndDelete(pParse, yymsp[-4].minor.yy528); - yymsp[-4].minor.yy528 = sqlite3Expr(pParse->db, TK_STRING, yymsp[-3].minor.yy394 ? "true" : "false"); - if( yymsp[-4].minor.yy528 ) sqlite3ExprIdToTrueFalse(yymsp[-4].minor.yy528); + sqlite3ExprUnmapAndDelete(pParse, yymsp[-4].minor.yy454); + yymsp[-4].minor.yy454 = sqlite3Expr(pParse->db, TK_STRING, yymsp[-3].minor.yy144 ? "true" : "false"); + if( yymsp[-4].minor.yy454 ) sqlite3ExprIdToTrueFalse(yymsp[-4].minor.yy454); }else{ - Expr *pRHS = yymsp[-1].minor.yy322->a[0].pExpr; - if( yymsp[-1].minor.yy322->nExpr==1 && sqlite3ExprIsConstant(pRHS) && yymsp[-4].minor.yy528->op!=TK_VECTOR ){ - yymsp[-1].minor.yy322->a[0].pExpr = 0; - sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy322); + Expr *pRHS = yymsp[-1].minor.yy14->a[0].pExpr; + if( yymsp[-1].minor.yy14->nExpr==1 && sqlite3ExprIsConstant(pParse,pRHS) && yymsp[-4].minor.yy454->op!=TK_VECTOR ){ + yymsp[-1].minor.yy14->a[0].pExpr = 0; + sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy14); pRHS = sqlite3PExpr(pParse, TK_UPLUS, pRHS, 0); - yymsp[-4].minor.yy528 = sqlite3PExpr(pParse, TK_EQ, yymsp[-4].minor.yy528, pRHS); - }else if( yymsp[-1].minor.yy322->nExpr==1 && pRHS->op==TK_SELECT ){ - yymsp[-4].minor.yy528 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy528, 0); - sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy528, pRHS->x.pSelect); + yymsp[-4].minor.yy454 = sqlite3PExpr(pParse, TK_EQ, yymsp[-4].minor.yy454, pRHS); + }else if( yymsp[-1].minor.yy14->nExpr==1 && pRHS->op==TK_SELECT ){ + yymsp[-4].minor.yy454 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy454, 0); + sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy454, pRHS->x.pSelect); pRHS->x.pSelect = 0; - sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy322); + sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy14); }else{ - yymsp[-4].minor.yy528 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy528, 0); - if( yymsp[-4].minor.yy528==0 ){ - sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy322); - }else if( yymsp[-4].minor.yy528->pLeft->op==TK_VECTOR ){ - int nExpr = yymsp[-4].minor.yy528->pLeft->x.pList->nExpr; - Select *pSelectRHS = sqlite3ExprListToValues(pParse, nExpr, yymsp[-1].minor.yy322); + yymsp[-4].minor.yy454 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy454, 0); + if( yymsp[-4].minor.yy454==0 ){ + sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy14); + }else if( yymsp[-4].minor.yy454->pLeft->op==TK_VECTOR ){ + int nExpr = yymsp[-4].minor.yy454->pLeft->x.pList->nExpr; + Select *pSelectRHS = sqlite3ExprListToValues(pParse, nExpr, yymsp[-1].minor.yy14); if( pSelectRHS ){ parserDoubleLinkSelect(pParse, pSelectRHS); - sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy528, pSelectRHS); + sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy454, pSelectRHS); } }else{ - yymsp[-4].minor.yy528->x.pList = yymsp[-1].minor.yy322; - sqlite3ExprSetHeightAndFlags(pParse, yymsp[-4].minor.yy528); + yymsp[-4].minor.yy454->x.pList = yymsp[-1].minor.yy14; + sqlite3ExprSetHeightAndFlags(pParse, yymsp[-4].minor.yy454); } } - if( yymsp[-3].minor.yy394 ) yymsp[-4].minor.yy528 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy528, 0); + if( yymsp[-3].minor.yy144 ) yymsp[-4].minor.yy454 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy454, 0); } } break; - case 222: /* expr ::= LP select RP */ + case 224: /* expr ::= LP select RP */ { - yymsp[-2].minor.yy528 = sqlite3PExpr(pParse, TK_SELECT, 0, 0); - sqlite3PExprAddSelect(pParse, yymsp[-2].minor.yy528, yymsp[-1].minor.yy47); + yymsp[-2].minor.yy454 = sqlite3PExpr(pParse, TK_SELECT, 0, 0); + sqlite3PExprAddSelect(pParse, yymsp[-2].minor.yy454, yymsp[-1].minor.yy555); } break; - case 223: /* expr ::= expr in_op LP select RP */ + case 225: /* expr ::= expr in_op LP select RP */ { - yymsp[-4].minor.yy528 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy528, 0); - sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy528, yymsp[-1].minor.yy47); - if( yymsp[-3].minor.yy394 ) yymsp[-4].minor.yy528 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy528, 0); + yymsp[-4].minor.yy454 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy454, 0); + sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy454, yymsp[-1].minor.yy555); + if( yymsp[-3].minor.yy144 ) yymsp[-4].minor.yy454 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy454, 0); } break; - case 224: /* expr ::= expr in_op nm dbnm paren_exprlist */ + case 226: /* expr ::= expr in_op nm dbnm paren_exprlist */ { SrcList *pSrc = sqlite3SrcListAppend(pParse, 0,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0); Select *pSelect = sqlite3SelectNew(pParse, 0,pSrc,0,0,0,0,0,0); - if( yymsp[0].minor.yy322 ) sqlite3SrcListFuncArgs(pParse, pSelect ? pSrc : 0, yymsp[0].minor.yy322); - yymsp[-4].minor.yy528 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy528, 0); - sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy528, pSelect); - if( yymsp[-3].minor.yy394 ) yymsp[-4].minor.yy528 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy528, 0); + if( yymsp[0].minor.yy14 ) sqlite3SrcListFuncArgs(pParse, pSelect ? pSrc : 0, yymsp[0].minor.yy14); + yymsp[-4].minor.yy454 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy454, 0); + sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy454, pSelect); + if( yymsp[-3].minor.yy144 ) yymsp[-4].minor.yy454 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy454, 0); } break; - case 225: /* expr ::= EXISTS LP select RP */ + case 227: /* expr ::= EXISTS LP select RP */ { Expr *p; - p = yymsp[-3].minor.yy528 = sqlite3PExpr(pParse, TK_EXISTS, 0, 0); - sqlite3PExprAddSelect(pParse, p, yymsp[-1].minor.yy47); + p = yymsp[-3].minor.yy454 = sqlite3PExpr(pParse, TK_EXISTS, 0, 0); + sqlite3PExprAddSelect(pParse, p, yymsp[-1].minor.yy555); } break; - case 226: /* expr ::= CASE case_operand case_exprlist case_else END */ + case 228: /* expr ::= CASE case_operand case_exprlist case_else END */ { - yymsp[-4].minor.yy528 = sqlite3PExpr(pParse, TK_CASE, yymsp[-3].minor.yy528, 0); - if( yymsp[-4].minor.yy528 ){ - yymsp[-4].minor.yy528->x.pList = yymsp[-1].minor.yy528 ? sqlite3ExprListAppend(pParse,yymsp[-2].minor.yy322,yymsp[-1].minor.yy528) : yymsp[-2].minor.yy322; - sqlite3ExprSetHeightAndFlags(pParse, yymsp[-4].minor.yy528); + yymsp[-4].minor.yy454 = sqlite3PExpr(pParse, TK_CASE, yymsp[-3].minor.yy454, 0); + if( yymsp[-4].minor.yy454 ){ + yymsp[-4].minor.yy454->x.pList = yymsp[-1].minor.yy454 ? sqlite3ExprListAppend(pParse,yymsp[-2].minor.yy14,yymsp[-1].minor.yy454) : yymsp[-2].minor.yy14; + sqlite3ExprSetHeightAndFlags(pParse, yymsp[-4].minor.yy454); }else{ - sqlite3ExprListDelete(pParse->db, yymsp[-2].minor.yy322); - sqlite3ExprDelete(pParse->db, yymsp[-1].minor.yy528); + sqlite3ExprListDelete(pParse->db, yymsp[-2].minor.yy14); + sqlite3ExprDelete(pParse->db, yymsp[-1].minor.yy454); } } break; - case 227: /* case_exprlist ::= case_exprlist WHEN expr THEN expr */ + case 229: /* case_exprlist ::= case_exprlist WHEN expr THEN expr */ { - yymsp[-4].minor.yy322 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy322, yymsp[-2].minor.yy528); - yymsp[-4].minor.yy322 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy322, yymsp[0].minor.yy528); + yymsp[-4].minor.yy14 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy14, yymsp[-2].minor.yy454); + yymsp[-4].minor.yy14 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy14, yymsp[0].minor.yy454); } break; - case 228: /* case_exprlist ::= WHEN expr THEN expr */ + case 230: /* case_exprlist ::= WHEN expr THEN expr */ { - yymsp[-3].minor.yy322 = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy528); - yymsp[-3].minor.yy322 = sqlite3ExprListAppend(pParse,yymsp[-3].minor.yy322, yymsp[0].minor.yy528); + yymsp[-3].minor.yy14 = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy454); + yymsp[-3].minor.yy14 = sqlite3ExprListAppend(pParse,yymsp[-3].minor.yy14, yymsp[0].minor.yy454); } break; - case 233: /* nexprlist ::= nexprlist COMMA expr */ -{yymsp[-2].minor.yy322 = sqlite3ExprListAppend(pParse,yymsp[-2].minor.yy322,yymsp[0].minor.yy528);} + case 235: /* nexprlist ::= nexprlist COMMA expr */ +{yymsp[-2].minor.yy14 = sqlite3ExprListAppend(pParse,yymsp[-2].minor.yy14,yymsp[0].minor.yy454);} break; - case 234: /* nexprlist ::= expr */ -{yymsp[0].minor.yy322 = sqlite3ExprListAppend(pParse,0,yymsp[0].minor.yy528); /*A-overwrites-Y*/} + case 236: /* nexprlist ::= expr */ +{yymsp[0].minor.yy14 = sqlite3ExprListAppend(pParse,0,yymsp[0].minor.yy454); /*A-overwrites-Y*/} break; - case 236: /* paren_exprlist ::= LP exprlist RP */ - case 241: /* eidlist_opt ::= LP eidlist RP */ yytestcase(yyruleno==241); -{yymsp[-2].minor.yy322 = yymsp[-1].minor.yy322;} + case 238: /* paren_exprlist ::= LP exprlist RP */ + case 243: /* eidlist_opt ::= LP eidlist RP */ yytestcase(yyruleno==243); +{yymsp[-2].minor.yy14 = yymsp[-1].minor.yy14;} break; - case 237: /* cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */ + case 239: /* cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */ { sqlite3CreateIndex(pParse, &yymsp[-7].minor.yy0, &yymsp[-6].minor.yy0, - sqlite3SrcListAppend(pParse,0,&yymsp[-4].minor.yy0,0), yymsp[-2].minor.yy322, yymsp[-10].minor.yy394, - &yymsp[-11].minor.yy0, yymsp[0].minor.yy528, SQLITE_SO_ASC, yymsp[-8].minor.yy394, SQLITE_IDXTYPE_APPDEF); + sqlite3SrcListAppend(pParse,0,&yymsp[-4].minor.yy0,0), yymsp[-2].minor.yy14, yymsp[-10].minor.yy144, + &yymsp[-11].minor.yy0, yymsp[0].minor.yy454, SQLITE_SO_ASC, yymsp[-8].minor.yy144, SQLITE_IDXTYPE_APPDEF); if( IN_RENAME_OBJECT && pParse->pNewIndex ){ sqlite3RenameTokenMap(pParse, pParse->pNewIndex->zName, &yymsp[-4].minor.yy0); } } break; - case 238: /* uniqueflag ::= UNIQUE */ - case 280: /* raisetype ::= ABORT */ yytestcase(yyruleno==280); -{yymsp[0].minor.yy394 = OE_Abort;} + case 240: /* uniqueflag ::= UNIQUE */ + case 282: /* raisetype ::= ABORT */ yytestcase(yyruleno==282); +{yymsp[0].minor.yy144 = OE_Abort;} break; - case 239: /* uniqueflag ::= */ -{yymsp[1].minor.yy394 = OE_None;} + case 241: /* uniqueflag ::= */ +{yymsp[1].minor.yy144 = OE_None;} break; - case 242: /* eidlist ::= eidlist COMMA nm collate sortorder */ + case 244: /* eidlist ::= eidlist COMMA nm collate sortorder */ { - yymsp[-4].minor.yy322 = parserAddExprIdListTerm(pParse, yymsp[-4].minor.yy322, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy394, yymsp[0].minor.yy394); + yymsp[-4].minor.yy14 = parserAddExprIdListTerm(pParse, yymsp[-4].minor.yy14, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy144, yymsp[0].minor.yy144); } break; - case 243: /* eidlist ::= nm collate sortorder */ + case 245: /* eidlist ::= nm collate sortorder */ { - yymsp[-2].minor.yy322 = parserAddExprIdListTerm(pParse, 0, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy394, yymsp[0].minor.yy394); /*A-overwrites-Y*/ + yymsp[-2].minor.yy14 = parserAddExprIdListTerm(pParse, 0, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy144, yymsp[0].minor.yy144); /*A-overwrites-Y*/ } break; - case 246: /* cmd ::= DROP INDEX ifexists fullname */ -{sqlite3DropIndex(pParse, yymsp[0].minor.yy131, yymsp[-1].minor.yy394);} + case 248: /* cmd ::= DROP INDEX ifexists fullname */ +{sqlite3DropIndex(pParse, yymsp[0].minor.yy203, yymsp[-1].minor.yy144);} break; - case 247: /* cmd ::= VACUUM vinto */ -{sqlite3Vacuum(pParse,0,yymsp[0].minor.yy528);} + case 249: /* cmd ::= VACUUM vinto */ +{sqlite3Vacuum(pParse,0,yymsp[0].minor.yy454);} break; - case 248: /* cmd ::= VACUUM nm vinto */ -{sqlite3Vacuum(pParse,&yymsp[-1].minor.yy0,yymsp[0].minor.yy528);} + case 250: /* cmd ::= VACUUM nm vinto */ +{sqlite3Vacuum(pParse,&yymsp[-1].minor.yy0,yymsp[0].minor.yy454);} break; - case 251: /* cmd ::= PRAGMA nm dbnm */ + case 253: /* cmd ::= PRAGMA nm dbnm */ {sqlite3Pragma(pParse,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0,0,0);} break; - case 252: /* cmd ::= PRAGMA nm dbnm EQ nmnum */ + case 254: /* cmd ::= PRAGMA nm dbnm EQ nmnum */ {sqlite3Pragma(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0,0);} break; - case 253: /* cmd ::= PRAGMA nm dbnm LP nmnum RP */ + case 255: /* cmd ::= PRAGMA nm dbnm LP nmnum RP */ {sqlite3Pragma(pParse,&yymsp[-4].minor.yy0,&yymsp[-3].minor.yy0,&yymsp[-1].minor.yy0,0);} break; - case 254: /* cmd ::= PRAGMA nm dbnm EQ minus_num */ + case 256: /* cmd ::= PRAGMA nm dbnm EQ minus_num */ {sqlite3Pragma(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0,1);} break; - case 255: /* cmd ::= PRAGMA nm dbnm LP minus_num RP */ + case 257: /* cmd ::= PRAGMA nm dbnm LP minus_num RP */ {sqlite3Pragma(pParse,&yymsp[-4].minor.yy0,&yymsp[-3].minor.yy0,&yymsp[-1].minor.yy0,1);} break; - case 258: /* cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */ + case 260: /* cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */ { Token all; all.z = yymsp[-3].minor.yy0.z; all.n = (int)(yymsp[0].minor.yy0.z - yymsp[-3].minor.yy0.z) + yymsp[0].minor.yy0.n; - sqlite3FinishTrigger(pParse, yymsp[-1].minor.yy33, &all); + sqlite3FinishTrigger(pParse, yymsp[-1].minor.yy427, &all); } break; - case 259: /* trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */ + case 261: /* trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */ { - sqlite3BeginTrigger(pParse, &yymsp[-7].minor.yy0, &yymsp[-6].minor.yy0, yymsp[-5].minor.yy394, yymsp[-4].minor.yy180.a, yymsp[-4].minor.yy180.b, yymsp[-2].minor.yy131, yymsp[0].minor.yy528, yymsp[-10].minor.yy394, yymsp[-8].minor.yy394); + sqlite3BeginTrigger(pParse, &yymsp[-7].minor.yy0, &yymsp[-6].minor.yy0, yymsp[-5].minor.yy144, yymsp[-4].minor.yy286.a, yymsp[-4].minor.yy286.b, yymsp[-2].minor.yy203, yymsp[0].minor.yy454, yymsp[-10].minor.yy144, yymsp[-8].minor.yy144); yymsp[-10].minor.yy0 = (yymsp[-6].minor.yy0.n==0?yymsp[-7].minor.yy0:yymsp[-6].minor.yy0); /*A-overwrites-T*/ } break; - case 260: /* trigger_time ::= BEFORE|AFTER */ -{ yymsp[0].minor.yy394 = yymsp[0].major; /*A-overwrites-X*/ } + case 262: /* trigger_time ::= BEFORE|AFTER */ +{ yymsp[0].minor.yy144 = yymsp[0].major; /*A-overwrites-X*/ } break; - case 261: /* trigger_time ::= INSTEAD OF */ -{ yymsp[-1].minor.yy394 = TK_INSTEAD;} + case 263: /* trigger_time ::= INSTEAD OF */ +{ yymsp[-1].minor.yy144 = TK_INSTEAD;} break; - case 262: /* trigger_time ::= */ -{ yymsp[1].minor.yy394 = TK_BEFORE; } + case 264: /* trigger_time ::= */ +{ yymsp[1].minor.yy144 = TK_BEFORE; } break; - case 263: /* trigger_event ::= DELETE|INSERT */ - case 264: /* trigger_event ::= UPDATE */ yytestcase(yyruleno==264); -{yymsp[0].minor.yy180.a = yymsp[0].major; /*A-overwrites-X*/ yymsp[0].minor.yy180.b = 0;} + case 265: /* trigger_event ::= DELETE|INSERT */ + case 266: /* trigger_event ::= UPDATE */ yytestcase(yyruleno==266); +{yymsp[0].minor.yy286.a = yymsp[0].major; /*A-overwrites-X*/ yymsp[0].minor.yy286.b = 0;} break; - case 265: /* trigger_event ::= UPDATE OF idlist */ -{yymsp[-2].minor.yy180.a = TK_UPDATE; yymsp[-2].minor.yy180.b = yymsp[0].minor.yy254;} + case 267: /* trigger_event ::= UPDATE OF idlist */ +{yymsp[-2].minor.yy286.a = TK_UPDATE; yymsp[-2].minor.yy286.b = yymsp[0].minor.yy132;} break; - case 266: /* when_clause ::= */ - case 285: /* key_opt ::= */ yytestcase(yyruleno==285); -{ yymsp[1].minor.yy528 = 0; } + case 268: /* when_clause ::= */ + case 287: /* key_opt ::= */ yytestcase(yyruleno==287); +{ yymsp[1].minor.yy454 = 0; } break; - case 267: /* when_clause ::= WHEN expr */ - case 286: /* key_opt ::= KEY expr */ yytestcase(yyruleno==286); -{ yymsp[-1].minor.yy528 = yymsp[0].minor.yy528; } + case 269: /* when_clause ::= WHEN expr */ + case 288: /* key_opt ::= KEY expr */ yytestcase(yyruleno==288); +{ yymsp[-1].minor.yy454 = yymsp[0].minor.yy454; } break; - case 268: /* trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */ + case 270: /* trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */ { - assert( yymsp[-2].minor.yy33!=0 ); - yymsp[-2].minor.yy33->pLast->pNext = yymsp[-1].minor.yy33; - yymsp[-2].minor.yy33->pLast = yymsp[-1].minor.yy33; + assert( yymsp[-2].minor.yy427!=0 ); + yymsp[-2].minor.yy427->pLast->pNext = yymsp[-1].minor.yy427; + yymsp[-2].minor.yy427->pLast = yymsp[-1].minor.yy427; } break; - case 269: /* trigger_cmd_list ::= trigger_cmd SEMI */ + case 271: /* trigger_cmd_list ::= trigger_cmd SEMI */ { - assert( yymsp[-1].minor.yy33!=0 ); - yymsp[-1].minor.yy33->pLast = yymsp[-1].minor.yy33; + assert( yymsp[-1].minor.yy427!=0 ); + yymsp[-1].minor.yy427->pLast = yymsp[-1].minor.yy427; } break; - case 270: /* trnm ::= nm DOT nm */ + case 272: /* trnm ::= nm DOT nm */ { yymsp[-2].minor.yy0 = yymsp[0].minor.yy0; sqlite3ErrorMsg(pParse, @@ -175883,367 +177291,377 @@ static YYACTIONTYPE yy_reduce( "statements within triggers"); } break; - case 271: /* tridxby ::= INDEXED BY nm */ + case 273: /* tridxby ::= INDEXED BY nm */ { sqlite3ErrorMsg(pParse, "the INDEXED BY clause is not allowed on UPDATE or DELETE statements " "within triggers"); } break; - case 272: /* tridxby ::= NOT INDEXED */ + case 274: /* tridxby ::= NOT INDEXED */ { sqlite3ErrorMsg(pParse, "the NOT INDEXED clause is not allowed on UPDATE or DELETE statements " "within triggers"); } break; - case 273: /* trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt */ -{yylhsminor.yy33 = sqlite3TriggerUpdateStep(pParse, &yymsp[-6].minor.yy0, yymsp[-2].minor.yy131, yymsp[-3].minor.yy322, yymsp[-1].minor.yy528, yymsp[-7].minor.yy394, yymsp[-8].minor.yy0.z, yymsp[0].minor.yy522);} - yymsp[-8].minor.yy33 = yylhsminor.yy33; + case 275: /* trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt */ +{yylhsminor.yy427 = sqlite3TriggerUpdateStep(pParse, &yymsp[-6].minor.yy0, yymsp[-2].minor.yy203, yymsp[-3].minor.yy14, yymsp[-1].minor.yy454, yymsp[-7].minor.yy144, yymsp[-8].minor.yy0.z, yymsp[0].minor.yy168);} + yymsp[-8].minor.yy427 = yylhsminor.yy427; break; - case 274: /* trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */ + case 276: /* trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */ { - yylhsminor.yy33 = sqlite3TriggerInsertStep(pParse,&yymsp[-4].minor.yy0,yymsp[-3].minor.yy254,yymsp[-2].minor.yy47,yymsp[-6].minor.yy394,yymsp[-1].minor.yy444,yymsp[-7].minor.yy522,yymsp[0].minor.yy522);/*yylhsminor.yy33-overwrites-yymsp[-6].minor.yy394*/ + yylhsminor.yy427 = sqlite3TriggerInsertStep(pParse,&yymsp[-4].minor.yy0,yymsp[-3].minor.yy132,yymsp[-2].minor.yy555,yymsp[-6].minor.yy144,yymsp[-1].minor.yy122,yymsp[-7].minor.yy168,yymsp[0].minor.yy168);/*yylhsminor.yy427-overwrites-yymsp[-6].minor.yy144*/ } - yymsp[-7].minor.yy33 = yylhsminor.yy33; + yymsp[-7].minor.yy427 = yylhsminor.yy427; break; - case 275: /* trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */ -{yylhsminor.yy33 = sqlite3TriggerDeleteStep(pParse, &yymsp[-3].minor.yy0, yymsp[-1].minor.yy528, yymsp[-5].minor.yy0.z, yymsp[0].minor.yy522);} - yymsp[-5].minor.yy33 = yylhsminor.yy33; + case 277: /* trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */ +{yylhsminor.yy427 = sqlite3TriggerDeleteStep(pParse, &yymsp[-3].minor.yy0, yymsp[-1].minor.yy454, yymsp[-5].minor.yy0.z, yymsp[0].minor.yy168);} + yymsp[-5].minor.yy427 = yylhsminor.yy427; break; - case 276: /* trigger_cmd ::= scanpt select scanpt */ -{yylhsminor.yy33 = sqlite3TriggerSelectStep(pParse->db, yymsp[-1].minor.yy47, yymsp[-2].minor.yy522, yymsp[0].minor.yy522); /*yylhsminor.yy33-overwrites-yymsp[-1].minor.yy47*/} - yymsp[-2].minor.yy33 = yylhsminor.yy33; + case 278: /* trigger_cmd ::= scanpt select scanpt */ +{yylhsminor.yy427 = sqlite3TriggerSelectStep(pParse->db, yymsp[-1].minor.yy555, yymsp[-2].minor.yy168, yymsp[0].minor.yy168); /*yylhsminor.yy427-overwrites-yymsp[-1].minor.yy555*/} + yymsp[-2].minor.yy427 = yylhsminor.yy427; break; - case 277: /* expr ::= RAISE LP IGNORE RP */ + case 279: /* expr ::= RAISE LP IGNORE RP */ { - yymsp[-3].minor.yy528 = sqlite3PExpr(pParse, TK_RAISE, 0, 0); - if( yymsp[-3].minor.yy528 ){ - yymsp[-3].minor.yy528->affExpr = OE_Ignore; + yymsp[-3].minor.yy454 = sqlite3PExpr(pParse, TK_RAISE, 0, 0); + if( yymsp[-3].minor.yy454 ){ + yymsp[-3].minor.yy454->affExpr = OE_Ignore; } } break; - case 278: /* expr ::= RAISE LP raisetype COMMA nm RP */ + case 280: /* expr ::= RAISE LP raisetype COMMA nm RP */ { - yymsp[-5].minor.yy528 = sqlite3ExprAlloc(pParse->db, TK_RAISE, &yymsp[-1].minor.yy0, 1); - if( yymsp[-5].minor.yy528 ) { - yymsp[-5].minor.yy528->affExpr = (char)yymsp[-3].minor.yy394; + yymsp[-5].minor.yy454 = sqlite3ExprAlloc(pParse->db, TK_RAISE, &yymsp[-1].minor.yy0, 1); + if( yymsp[-5].minor.yy454 ) { + yymsp[-5].minor.yy454->affExpr = (char)yymsp[-3].minor.yy144; } } break; - case 279: /* raisetype ::= ROLLBACK */ -{yymsp[0].minor.yy394 = OE_Rollback;} + case 281: /* raisetype ::= ROLLBACK */ +{yymsp[0].minor.yy144 = OE_Rollback;} break; - case 281: /* raisetype ::= FAIL */ -{yymsp[0].minor.yy394 = OE_Fail;} + case 283: /* raisetype ::= FAIL */ +{yymsp[0].minor.yy144 = OE_Fail;} break; - case 282: /* cmd ::= DROP TRIGGER ifexists fullname */ + case 284: /* cmd ::= DROP TRIGGER ifexists fullname */ { - sqlite3DropTrigger(pParse,yymsp[0].minor.yy131,yymsp[-1].minor.yy394); + sqlite3DropTrigger(pParse,yymsp[0].minor.yy203,yymsp[-1].minor.yy144); } break; - case 283: /* cmd ::= ATTACH database_kw_opt expr AS expr key_opt */ + case 285: /* cmd ::= ATTACH database_kw_opt expr AS expr key_opt */ { - sqlite3Attach(pParse, yymsp[-3].minor.yy528, yymsp[-1].minor.yy528, yymsp[0].minor.yy528); + sqlite3Attach(pParse, yymsp[-3].minor.yy454, yymsp[-1].minor.yy454, yymsp[0].minor.yy454); } break; - case 284: /* cmd ::= DETACH database_kw_opt expr */ + case 286: /* cmd ::= DETACH database_kw_opt expr */ { - sqlite3Detach(pParse, yymsp[0].minor.yy528); + sqlite3Detach(pParse, yymsp[0].minor.yy454); } break; - case 287: /* cmd ::= REINDEX */ + case 289: /* cmd ::= REINDEX */ {sqlite3Reindex(pParse, 0, 0);} break; - case 288: /* cmd ::= REINDEX nm dbnm */ + case 290: /* cmd ::= REINDEX nm dbnm */ {sqlite3Reindex(pParse, &yymsp[-1].minor.yy0, &yymsp[0].minor.yy0);} break; - case 289: /* cmd ::= ANALYZE */ + case 291: /* cmd ::= ANALYZE */ {sqlite3Analyze(pParse, 0, 0);} break; - case 290: /* cmd ::= ANALYZE nm dbnm */ + case 292: /* cmd ::= ANALYZE nm dbnm */ {sqlite3Analyze(pParse, &yymsp[-1].minor.yy0, &yymsp[0].minor.yy0);} break; - case 291: /* cmd ::= ALTER TABLE fullname RENAME TO nm */ + case 293: /* cmd ::= ALTER TABLE fullname RENAME TO nm */ { - sqlite3AlterRenameTable(pParse,yymsp[-3].minor.yy131,&yymsp[0].minor.yy0); + sqlite3AlterRenameTable(pParse,yymsp[-3].minor.yy203,&yymsp[0].minor.yy0); } break; - case 292: /* cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */ + case 294: /* cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */ { yymsp[-1].minor.yy0.n = (int)(pParse->sLastToken.z-yymsp[-1].minor.yy0.z) + pParse->sLastToken.n; sqlite3AlterFinishAddColumn(pParse, &yymsp[-1].minor.yy0); } break; - case 293: /* cmd ::= ALTER TABLE fullname DROP kwcolumn_opt nm */ + case 295: /* cmd ::= ALTER TABLE fullname DROP kwcolumn_opt nm */ { - sqlite3AlterDropColumn(pParse, yymsp[-3].minor.yy131, &yymsp[0].minor.yy0); + sqlite3AlterDropColumn(pParse, yymsp[-3].minor.yy203, &yymsp[0].minor.yy0); } break; - case 294: /* add_column_fullname ::= fullname */ + case 296: /* add_column_fullname ::= fullname */ { disableLookaside(pParse); - sqlite3AlterBeginAddColumn(pParse, yymsp[0].minor.yy131); + sqlite3AlterBeginAddColumn(pParse, yymsp[0].minor.yy203); } break; - case 295: /* cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */ + case 297: /* cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */ { - sqlite3AlterRenameColumn(pParse, yymsp[-5].minor.yy131, &yymsp[-2].minor.yy0, &yymsp[0].minor.yy0); + sqlite3AlterRenameColumn(pParse, yymsp[-5].minor.yy203, &yymsp[-2].minor.yy0, &yymsp[0].minor.yy0); } break; - case 296: /* cmd ::= create_vtab */ + case 298: /* cmd ::= create_vtab */ {sqlite3VtabFinishParse(pParse,0);} break; - case 297: /* cmd ::= create_vtab LP vtabarglist RP */ + case 299: /* cmd ::= create_vtab LP vtabarglist RP */ {sqlite3VtabFinishParse(pParse,&yymsp[0].minor.yy0);} break; - case 298: /* create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */ + case 300: /* create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */ { - sqlite3VtabBeginParse(pParse, &yymsp[-3].minor.yy0, &yymsp[-2].minor.yy0, &yymsp[0].minor.yy0, yymsp[-4].minor.yy394); + sqlite3VtabBeginParse(pParse, &yymsp[-3].minor.yy0, &yymsp[-2].minor.yy0, &yymsp[0].minor.yy0, yymsp[-4].minor.yy144); } break; - case 299: /* vtabarg ::= */ + case 301: /* vtabarg ::= */ {sqlite3VtabArgInit(pParse);} break; - case 300: /* vtabargtoken ::= ANY */ - case 301: /* vtabargtoken ::= lp anylist RP */ yytestcase(yyruleno==301); - case 302: /* lp ::= LP */ yytestcase(yyruleno==302); + case 302: /* vtabargtoken ::= ANY */ + case 303: /* vtabargtoken ::= lp anylist RP */ yytestcase(yyruleno==303); + case 304: /* lp ::= LP */ yytestcase(yyruleno==304); {sqlite3VtabArgExtend(pParse,&yymsp[0].minor.yy0);} break; - case 303: /* with ::= WITH wqlist */ - case 304: /* with ::= WITH RECURSIVE wqlist */ yytestcase(yyruleno==304); -{ sqlite3WithPush(pParse, yymsp[0].minor.yy521, 1); } + case 305: /* with ::= WITH wqlist */ + case 306: /* with ::= WITH RECURSIVE wqlist */ yytestcase(yyruleno==306); +{ sqlite3WithPush(pParse, yymsp[0].minor.yy59, 1); } break; - case 305: /* wqas ::= AS */ -{yymsp[0].minor.yy516 = M10d_Any;} + case 307: /* wqas ::= AS */ +{yymsp[0].minor.yy462 = M10d_Any;} break; - case 306: /* wqas ::= AS MATERIALIZED */ -{yymsp[-1].minor.yy516 = M10d_Yes;} + case 308: /* wqas ::= AS MATERIALIZED */ +{yymsp[-1].minor.yy462 = M10d_Yes;} break; - case 307: /* wqas ::= AS NOT MATERIALIZED */ -{yymsp[-2].minor.yy516 = M10d_No;} + case 309: /* wqas ::= AS NOT MATERIALIZED */ +{yymsp[-2].minor.yy462 = M10d_No;} break; - case 308: /* wqitem ::= nm eidlist_opt wqas LP select RP */ + case 310: /* wqitem ::= withnm eidlist_opt wqas LP select RP */ { - yymsp[-5].minor.yy385 = sqlite3CteNew(pParse, &yymsp[-5].minor.yy0, yymsp[-4].minor.yy322, yymsp[-1].minor.yy47, yymsp[-3].minor.yy516); /*A-overwrites-X*/ + yymsp[-5].minor.yy67 = sqlite3CteNew(pParse, &yymsp[-5].minor.yy0, yymsp[-4].minor.yy14, yymsp[-1].minor.yy555, yymsp[-3].minor.yy462); /*A-overwrites-X*/ } break; - case 309: /* wqlist ::= wqitem */ + case 311: /* withnm ::= nm */ +{pParse->bHasWith = 1;} + break; + case 312: /* wqlist ::= wqitem */ { - yymsp[0].minor.yy521 = sqlite3WithAdd(pParse, 0, yymsp[0].minor.yy385); /*A-overwrites-X*/ + yymsp[0].minor.yy59 = sqlite3WithAdd(pParse, 0, yymsp[0].minor.yy67); /*A-overwrites-X*/ } break; - case 310: /* wqlist ::= wqlist COMMA wqitem */ + case 313: /* wqlist ::= wqlist COMMA wqitem */ { - yymsp[-2].minor.yy521 = sqlite3WithAdd(pParse, yymsp[-2].minor.yy521, yymsp[0].minor.yy385); + yymsp[-2].minor.yy59 = sqlite3WithAdd(pParse, yymsp[-2].minor.yy59, yymsp[0].minor.yy67); } break; - case 311: /* windowdefn_list ::= windowdefn_list COMMA windowdefn */ + case 314: /* windowdefn_list ::= windowdefn_list COMMA windowdefn */ { - assert( yymsp[0].minor.yy41!=0 ); - sqlite3WindowChain(pParse, yymsp[0].minor.yy41, yymsp[-2].minor.yy41); - yymsp[0].minor.yy41->pNextWin = yymsp[-2].minor.yy41; - yylhsminor.yy41 = yymsp[0].minor.yy41; + assert( yymsp[0].minor.yy211!=0 ); + sqlite3WindowChain(pParse, yymsp[0].minor.yy211, yymsp[-2].minor.yy211); + yymsp[0].minor.yy211->pNextWin = yymsp[-2].minor.yy211; + yylhsminor.yy211 = yymsp[0].minor.yy211; } - yymsp[-2].minor.yy41 = yylhsminor.yy41; + yymsp[-2].minor.yy211 = yylhsminor.yy211; break; - case 312: /* windowdefn ::= nm AS LP window RP */ + case 315: /* windowdefn ::= nm AS LP window RP */ { - if( ALWAYS(yymsp[-1].minor.yy41) ){ - yymsp[-1].minor.yy41->zName = sqlite3DbStrNDup(pParse->db, yymsp[-4].minor.yy0.z, yymsp[-4].minor.yy0.n); + if( ALWAYS(yymsp[-1].minor.yy211) ){ + yymsp[-1].minor.yy211->zName = sqlite3DbStrNDup(pParse->db, yymsp[-4].minor.yy0.z, yymsp[-4].minor.yy0.n); } - yylhsminor.yy41 = yymsp[-1].minor.yy41; + yylhsminor.yy211 = yymsp[-1].minor.yy211; } - yymsp[-4].minor.yy41 = yylhsminor.yy41; + yymsp[-4].minor.yy211 = yylhsminor.yy211; break; - case 313: /* window ::= PARTITION BY nexprlist orderby_opt frame_opt */ + case 316: /* window ::= PARTITION BY nexprlist orderby_opt frame_opt */ { - yymsp[-4].minor.yy41 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy41, yymsp[-2].minor.yy322, yymsp[-1].minor.yy322, 0); + yymsp[-4].minor.yy211 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy211, yymsp[-2].minor.yy14, yymsp[-1].minor.yy14, 0); } break; - case 314: /* window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */ + case 317: /* window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */ { - yylhsminor.yy41 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy41, yymsp[-2].minor.yy322, yymsp[-1].minor.yy322, &yymsp[-5].minor.yy0); + yylhsminor.yy211 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy211, yymsp[-2].minor.yy14, yymsp[-1].minor.yy14, &yymsp[-5].minor.yy0); } - yymsp[-5].minor.yy41 = yylhsminor.yy41; + yymsp[-5].minor.yy211 = yylhsminor.yy211; break; - case 315: /* window ::= ORDER BY sortlist frame_opt */ + case 318: /* window ::= ORDER BY sortlist frame_opt */ { - yymsp[-3].minor.yy41 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy41, 0, yymsp[-1].minor.yy322, 0); + yymsp[-3].minor.yy211 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy211, 0, yymsp[-1].minor.yy14, 0); } break; - case 316: /* window ::= nm ORDER BY sortlist frame_opt */ + case 319: /* window ::= nm ORDER BY sortlist frame_opt */ { - yylhsminor.yy41 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy41, 0, yymsp[-1].minor.yy322, &yymsp[-4].minor.yy0); + yylhsminor.yy211 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy211, 0, yymsp[-1].minor.yy14, &yymsp[-4].minor.yy0); } - yymsp[-4].minor.yy41 = yylhsminor.yy41; + yymsp[-4].minor.yy211 = yylhsminor.yy211; break; - case 317: /* window ::= nm frame_opt */ + case 320: /* window ::= nm frame_opt */ { - yylhsminor.yy41 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy41, 0, 0, &yymsp[-1].minor.yy0); + yylhsminor.yy211 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy211, 0, 0, &yymsp[-1].minor.yy0); } - yymsp[-1].minor.yy41 = yylhsminor.yy41; + yymsp[-1].minor.yy211 = yylhsminor.yy211; break; - case 318: /* frame_opt ::= */ + case 321: /* frame_opt ::= */ { - yymsp[1].minor.yy41 = sqlite3WindowAlloc(pParse, 0, TK_UNBOUNDED, 0, TK_CURRENT, 0, 0); + yymsp[1].minor.yy211 = sqlite3WindowAlloc(pParse, 0, TK_UNBOUNDED, 0, TK_CURRENT, 0, 0); } break; - case 319: /* frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */ + case 322: /* frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */ { - yylhsminor.yy41 = sqlite3WindowAlloc(pParse, yymsp[-2].minor.yy394, yymsp[-1].minor.yy595.eType, yymsp[-1].minor.yy595.pExpr, TK_CURRENT, 0, yymsp[0].minor.yy516); + yylhsminor.yy211 = sqlite3WindowAlloc(pParse, yymsp[-2].minor.yy144, yymsp[-1].minor.yy509.eType, yymsp[-1].minor.yy509.pExpr, TK_CURRENT, 0, yymsp[0].minor.yy462); } - yymsp[-2].minor.yy41 = yylhsminor.yy41; + yymsp[-2].minor.yy211 = yylhsminor.yy211; break; - case 320: /* frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */ + case 323: /* frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */ { - yylhsminor.yy41 = sqlite3WindowAlloc(pParse, yymsp[-5].minor.yy394, yymsp[-3].minor.yy595.eType, yymsp[-3].minor.yy595.pExpr, yymsp[-1].minor.yy595.eType, yymsp[-1].minor.yy595.pExpr, yymsp[0].minor.yy516); + yylhsminor.yy211 = sqlite3WindowAlloc(pParse, yymsp[-5].minor.yy144, yymsp[-3].minor.yy509.eType, yymsp[-3].minor.yy509.pExpr, yymsp[-1].minor.yy509.eType, yymsp[-1].minor.yy509.pExpr, yymsp[0].minor.yy462); } - yymsp[-5].minor.yy41 = yylhsminor.yy41; + yymsp[-5].minor.yy211 = yylhsminor.yy211; break; - case 322: /* frame_bound_s ::= frame_bound */ - case 324: /* frame_bound_e ::= frame_bound */ yytestcase(yyruleno==324); -{yylhsminor.yy595 = yymsp[0].minor.yy595;} - yymsp[0].minor.yy595 = yylhsminor.yy595; + case 325: /* frame_bound_s ::= frame_bound */ + case 327: /* frame_bound_e ::= frame_bound */ yytestcase(yyruleno==327); +{yylhsminor.yy509 = yymsp[0].minor.yy509;} + yymsp[0].minor.yy509 = yylhsminor.yy509; break; - case 323: /* frame_bound_s ::= UNBOUNDED PRECEDING */ - case 325: /* frame_bound_e ::= UNBOUNDED FOLLOWING */ yytestcase(yyruleno==325); - case 327: /* frame_bound ::= CURRENT ROW */ yytestcase(yyruleno==327); -{yylhsminor.yy595.eType = yymsp[-1].major; yylhsminor.yy595.pExpr = 0;} - yymsp[-1].minor.yy595 = yylhsminor.yy595; + case 326: /* frame_bound_s ::= UNBOUNDED PRECEDING */ + case 328: /* frame_bound_e ::= UNBOUNDED FOLLOWING */ yytestcase(yyruleno==328); + case 330: /* frame_bound ::= CURRENT ROW */ yytestcase(yyruleno==330); +{yylhsminor.yy509.eType = yymsp[-1].major; yylhsminor.yy509.pExpr = 0;} + yymsp[-1].minor.yy509 = yylhsminor.yy509; break; - case 326: /* frame_bound ::= expr PRECEDING|FOLLOWING */ -{yylhsminor.yy595.eType = yymsp[0].major; yylhsminor.yy595.pExpr = yymsp[-1].minor.yy528;} - yymsp[-1].minor.yy595 = yylhsminor.yy595; + case 329: /* frame_bound ::= expr PRECEDING|FOLLOWING */ +{yylhsminor.yy509.eType = yymsp[0].major; yylhsminor.yy509.pExpr = yymsp[-1].minor.yy454;} + yymsp[-1].minor.yy509 = yylhsminor.yy509; break; - case 328: /* frame_exclude_opt ::= */ -{yymsp[1].minor.yy516 = 0;} + case 331: /* frame_exclude_opt ::= */ +{yymsp[1].minor.yy462 = 0;} break; - case 329: /* frame_exclude_opt ::= EXCLUDE frame_exclude */ -{yymsp[-1].minor.yy516 = yymsp[0].minor.yy516;} + case 332: /* frame_exclude_opt ::= EXCLUDE frame_exclude */ +{yymsp[-1].minor.yy462 = yymsp[0].minor.yy462;} break; - case 330: /* frame_exclude ::= NO OTHERS */ - case 331: /* frame_exclude ::= CURRENT ROW */ yytestcase(yyruleno==331); -{yymsp[-1].minor.yy516 = yymsp[-1].major; /*A-overwrites-X*/} + case 333: /* frame_exclude ::= NO OTHERS */ + case 334: /* frame_exclude ::= CURRENT ROW */ yytestcase(yyruleno==334); +{yymsp[-1].minor.yy462 = yymsp[-1].major; /*A-overwrites-X*/} break; - case 332: /* frame_exclude ::= GROUP|TIES */ -{yymsp[0].minor.yy516 = yymsp[0].major; /*A-overwrites-X*/} + case 335: /* frame_exclude ::= GROUP|TIES */ +{yymsp[0].minor.yy462 = yymsp[0].major; /*A-overwrites-X*/} break; - case 333: /* window_clause ::= WINDOW windowdefn_list */ -{ yymsp[-1].minor.yy41 = yymsp[0].minor.yy41; } + case 336: /* window_clause ::= WINDOW windowdefn_list */ +{ yymsp[-1].minor.yy211 = yymsp[0].minor.yy211; } break; - case 334: /* filter_over ::= filter_clause over_clause */ + case 337: /* filter_over ::= filter_clause over_clause */ { - if( yymsp[0].minor.yy41 ){ - yymsp[0].minor.yy41->pFilter = yymsp[-1].minor.yy528; + if( yymsp[0].minor.yy211 ){ + yymsp[0].minor.yy211->pFilter = yymsp[-1].minor.yy454; }else{ - sqlite3ExprDelete(pParse->db, yymsp[-1].minor.yy528); + sqlite3ExprDelete(pParse->db, yymsp[-1].minor.yy454); } - yylhsminor.yy41 = yymsp[0].minor.yy41; + yylhsminor.yy211 = yymsp[0].minor.yy211; } - yymsp[-1].minor.yy41 = yylhsminor.yy41; + yymsp[-1].minor.yy211 = yylhsminor.yy211; break; - case 335: /* filter_over ::= over_clause */ + case 338: /* filter_over ::= over_clause */ { - yylhsminor.yy41 = yymsp[0].minor.yy41; + yylhsminor.yy211 = yymsp[0].minor.yy211; } - yymsp[0].minor.yy41 = yylhsminor.yy41; + yymsp[0].minor.yy211 = yylhsminor.yy211; break; - case 336: /* filter_over ::= filter_clause */ + case 339: /* filter_over ::= filter_clause */ { - yylhsminor.yy41 = (Window*)sqlite3DbMallocZero(pParse->db, sizeof(Window)); - if( yylhsminor.yy41 ){ - yylhsminor.yy41->eFrmType = TK_FILTER; - yylhsminor.yy41->pFilter = yymsp[0].minor.yy528; + yylhsminor.yy211 = (Window*)sqlite3DbMallocZero(pParse->db, sizeof(Window)); + if( yylhsminor.yy211 ){ + yylhsminor.yy211->eFrmType = TK_FILTER; + yylhsminor.yy211->pFilter = yymsp[0].minor.yy454; }else{ - sqlite3ExprDelete(pParse->db, yymsp[0].minor.yy528); + sqlite3ExprDelete(pParse->db, yymsp[0].minor.yy454); } } - yymsp[0].minor.yy41 = yylhsminor.yy41; + yymsp[0].minor.yy211 = yylhsminor.yy211; break; - case 337: /* over_clause ::= OVER LP window RP */ + case 340: /* over_clause ::= OVER LP window RP */ { - yymsp[-3].minor.yy41 = yymsp[-1].minor.yy41; - assert( yymsp[-3].minor.yy41!=0 ); + yymsp[-3].minor.yy211 = yymsp[-1].minor.yy211; + assert( yymsp[-3].minor.yy211!=0 ); } break; - case 338: /* over_clause ::= OVER nm */ + case 341: /* over_clause ::= OVER nm */ { - yymsp[-1].minor.yy41 = (Window*)sqlite3DbMallocZero(pParse->db, sizeof(Window)); - if( yymsp[-1].minor.yy41 ){ - yymsp[-1].minor.yy41->zName = sqlite3DbStrNDup(pParse->db, yymsp[0].minor.yy0.z, yymsp[0].minor.yy0.n); + yymsp[-1].minor.yy211 = (Window*)sqlite3DbMallocZero(pParse->db, sizeof(Window)); + if( yymsp[-1].minor.yy211 ){ + yymsp[-1].minor.yy211->zName = sqlite3DbStrNDup(pParse->db, yymsp[0].minor.yy0.z, yymsp[0].minor.yy0.n); } } break; - case 339: /* filter_clause ::= FILTER LP WHERE expr RP */ -{ yymsp[-4].minor.yy528 = yymsp[-1].minor.yy528; } + case 342: /* filter_clause ::= FILTER LP WHERE expr RP */ +{ yymsp[-4].minor.yy454 = yymsp[-1].minor.yy454; } + break; + case 343: /* term ::= QNUMBER */ +{ + yylhsminor.yy454=tokenExpr(pParse,yymsp[0].major,yymsp[0].minor.yy0); + sqlite3DequoteNumber(pParse, yylhsminor.yy454); +} + yymsp[0].minor.yy454 = yylhsminor.yy454; break; default: - /* (340) input ::= cmdlist */ yytestcase(yyruleno==340); - /* (341) cmdlist ::= cmdlist ecmd */ yytestcase(yyruleno==341); - /* (342) cmdlist ::= ecmd (OPTIMIZED OUT) */ assert(yyruleno!=342); - /* (343) ecmd ::= SEMI */ yytestcase(yyruleno==343); - /* (344) ecmd ::= cmdx SEMI */ yytestcase(yyruleno==344); - /* (345) ecmd ::= explain cmdx SEMI (NEVER REDUCES) */ assert(yyruleno!=345); - /* (346) trans_opt ::= */ yytestcase(yyruleno==346); - /* (347) trans_opt ::= TRANSACTION */ yytestcase(yyruleno==347); - /* (348) trans_opt ::= TRANSACTION nm */ yytestcase(yyruleno==348); - /* (349) savepoint_opt ::= SAVEPOINT */ yytestcase(yyruleno==349); - /* (350) savepoint_opt ::= */ yytestcase(yyruleno==350); - /* (351) cmd ::= create_table create_table_args */ yytestcase(yyruleno==351); - /* (352) table_option_set ::= table_option (OPTIMIZED OUT) */ assert(yyruleno!=352); - /* (353) columnlist ::= columnlist COMMA columnname carglist */ yytestcase(yyruleno==353); - /* (354) columnlist ::= columnname carglist */ yytestcase(yyruleno==354); - /* (355) nm ::= ID|INDEXED|JOIN_KW */ yytestcase(yyruleno==355); - /* (356) nm ::= STRING */ yytestcase(yyruleno==356); - /* (357) typetoken ::= typename */ yytestcase(yyruleno==357); - /* (358) typename ::= ID|STRING */ yytestcase(yyruleno==358); - /* (359) signed ::= plus_num (OPTIMIZED OUT) */ assert(yyruleno!=359); - /* (360) signed ::= minus_num (OPTIMIZED OUT) */ assert(yyruleno!=360); - /* (361) carglist ::= carglist ccons */ yytestcase(yyruleno==361); - /* (362) carglist ::= */ yytestcase(yyruleno==362); - /* (363) ccons ::= NULL onconf */ yytestcase(yyruleno==363); - /* (364) ccons ::= GENERATED ALWAYS AS generated */ yytestcase(yyruleno==364); - /* (365) ccons ::= AS generated */ yytestcase(yyruleno==365); - /* (366) conslist_opt ::= COMMA conslist */ yytestcase(yyruleno==366); - /* (367) conslist ::= conslist tconscomma tcons */ yytestcase(yyruleno==367); - /* (368) conslist ::= tcons (OPTIMIZED OUT) */ assert(yyruleno!=368); - /* (369) tconscomma ::= */ yytestcase(yyruleno==369); - /* (370) defer_subclause_opt ::= defer_subclause (OPTIMIZED OUT) */ assert(yyruleno!=370); - /* (371) resolvetype ::= raisetype (OPTIMIZED OUT) */ assert(yyruleno!=371); - /* (372) selectnowith ::= oneselect (OPTIMIZED OUT) */ assert(yyruleno!=372); - /* (373) oneselect ::= values */ yytestcase(yyruleno==373); - /* (374) sclp ::= selcollist COMMA */ yytestcase(yyruleno==374); - /* (375) as ::= ID|STRING */ yytestcase(yyruleno==375); - /* (376) indexed_opt ::= indexed_by (OPTIMIZED OUT) */ assert(yyruleno!=376); - /* (377) returning ::= */ yytestcase(yyruleno==377); - /* (378) expr ::= term (OPTIMIZED OUT) */ assert(yyruleno!=378); - /* (379) likeop ::= LIKE_KW|MATCH */ yytestcase(yyruleno==379); - /* (380) case_operand ::= expr */ yytestcase(yyruleno==380); - /* (381) exprlist ::= nexprlist */ yytestcase(yyruleno==381); - /* (382) nmnum ::= plus_num (OPTIMIZED OUT) */ assert(yyruleno!=382); - /* (383) nmnum ::= nm (OPTIMIZED OUT) */ assert(yyruleno!=383); - /* (384) nmnum ::= ON */ yytestcase(yyruleno==384); - /* (385) nmnum ::= DELETE */ yytestcase(yyruleno==385); - /* (386) nmnum ::= DEFAULT */ yytestcase(yyruleno==386); - /* (387) plus_num ::= INTEGER|FLOAT */ yytestcase(yyruleno==387); - /* (388) foreach_clause ::= */ yytestcase(yyruleno==388); - /* (389) foreach_clause ::= FOR EACH ROW */ yytestcase(yyruleno==389); - /* (390) trnm ::= nm */ yytestcase(yyruleno==390); - /* (391) tridxby ::= */ yytestcase(yyruleno==391); - /* (392) database_kw_opt ::= DATABASE */ yytestcase(yyruleno==392); - /* (393) database_kw_opt ::= */ yytestcase(yyruleno==393); - /* (394) kwcolumn_opt ::= */ yytestcase(yyruleno==394); - /* (395) kwcolumn_opt ::= COLUMNKW */ yytestcase(yyruleno==395); - /* (396) vtabarglist ::= vtabarg */ yytestcase(yyruleno==396); - /* (397) vtabarglist ::= vtabarglist COMMA vtabarg */ yytestcase(yyruleno==397); - /* (398) vtabarg ::= vtabarg vtabargtoken */ yytestcase(yyruleno==398); - /* (399) anylist ::= */ yytestcase(yyruleno==399); - /* (400) anylist ::= anylist LP anylist RP */ yytestcase(yyruleno==400); - /* (401) anylist ::= anylist ANY */ yytestcase(yyruleno==401); - /* (402) with ::= */ yytestcase(yyruleno==402); - /* (403) windowdefn_list ::= windowdefn (OPTIMIZED OUT) */ assert(yyruleno!=403); - /* (404) window ::= frame_opt (OPTIMIZED OUT) */ assert(yyruleno!=404); + /* (344) input ::= cmdlist */ yytestcase(yyruleno==344); + /* (345) cmdlist ::= cmdlist ecmd */ yytestcase(yyruleno==345); + /* (346) cmdlist ::= ecmd (OPTIMIZED OUT) */ assert(yyruleno!=346); + /* (347) ecmd ::= SEMI */ yytestcase(yyruleno==347); + /* (348) ecmd ::= cmdx SEMI */ yytestcase(yyruleno==348); + /* (349) ecmd ::= explain cmdx SEMI (NEVER REDUCES) */ assert(yyruleno!=349); + /* (350) trans_opt ::= */ yytestcase(yyruleno==350); + /* (351) trans_opt ::= TRANSACTION */ yytestcase(yyruleno==351); + /* (352) trans_opt ::= TRANSACTION nm */ yytestcase(yyruleno==352); + /* (353) savepoint_opt ::= SAVEPOINT */ yytestcase(yyruleno==353); + /* (354) savepoint_opt ::= */ yytestcase(yyruleno==354); + /* (355) cmd ::= create_table create_table_args */ yytestcase(yyruleno==355); + /* (356) table_option_set ::= table_option (OPTIMIZED OUT) */ assert(yyruleno!=356); + /* (357) columnlist ::= columnlist COMMA columnname carglist */ yytestcase(yyruleno==357); + /* (358) columnlist ::= columnname carglist */ yytestcase(yyruleno==358); + /* (359) nm ::= ID|INDEXED|JOIN_KW */ yytestcase(yyruleno==359); + /* (360) nm ::= STRING */ yytestcase(yyruleno==360); + /* (361) typetoken ::= typename */ yytestcase(yyruleno==361); + /* (362) typename ::= ID|STRING */ yytestcase(yyruleno==362); + /* (363) signed ::= plus_num (OPTIMIZED OUT) */ assert(yyruleno!=363); + /* (364) signed ::= minus_num (OPTIMIZED OUT) */ assert(yyruleno!=364); + /* (365) carglist ::= carglist ccons */ yytestcase(yyruleno==365); + /* (366) carglist ::= */ yytestcase(yyruleno==366); + /* (367) ccons ::= NULL onconf */ yytestcase(yyruleno==367); + /* (368) ccons ::= GENERATED ALWAYS AS generated */ yytestcase(yyruleno==368); + /* (369) ccons ::= AS generated */ yytestcase(yyruleno==369); + /* (370) conslist_opt ::= COMMA conslist */ yytestcase(yyruleno==370); + /* (371) conslist ::= conslist tconscomma tcons */ yytestcase(yyruleno==371); + /* (372) conslist ::= tcons (OPTIMIZED OUT) */ assert(yyruleno!=372); + /* (373) tconscomma ::= */ yytestcase(yyruleno==373); + /* (374) defer_subclause_opt ::= defer_subclause (OPTIMIZED OUT) */ assert(yyruleno!=374); + /* (375) resolvetype ::= raisetype (OPTIMIZED OUT) */ assert(yyruleno!=375); + /* (376) selectnowith ::= oneselect (OPTIMIZED OUT) */ assert(yyruleno!=376); + /* (377) oneselect ::= values */ yytestcase(yyruleno==377); + /* (378) sclp ::= selcollist COMMA */ yytestcase(yyruleno==378); + /* (379) as ::= ID|STRING */ yytestcase(yyruleno==379); + /* (380) indexed_opt ::= indexed_by (OPTIMIZED OUT) */ assert(yyruleno!=380); + /* (381) returning ::= */ yytestcase(yyruleno==381); + /* (382) expr ::= term (OPTIMIZED OUT) */ assert(yyruleno!=382); + /* (383) likeop ::= LIKE_KW|MATCH */ yytestcase(yyruleno==383); + /* (384) case_operand ::= expr */ yytestcase(yyruleno==384); + /* (385) exprlist ::= nexprlist */ yytestcase(yyruleno==385); + /* (386) nmnum ::= plus_num (OPTIMIZED OUT) */ assert(yyruleno!=386); + /* (387) nmnum ::= nm (OPTIMIZED OUT) */ assert(yyruleno!=387); + /* (388) nmnum ::= ON */ yytestcase(yyruleno==388); + /* (389) nmnum ::= DELETE */ yytestcase(yyruleno==389); + /* (390) nmnum ::= DEFAULT */ yytestcase(yyruleno==390); + /* (391) plus_num ::= INTEGER|FLOAT */ yytestcase(yyruleno==391); + /* (392) foreach_clause ::= */ yytestcase(yyruleno==392); + /* (393) foreach_clause ::= FOR EACH ROW */ yytestcase(yyruleno==393); + /* (394) trnm ::= nm */ yytestcase(yyruleno==394); + /* (395) tridxby ::= */ yytestcase(yyruleno==395); + /* (396) database_kw_opt ::= DATABASE */ yytestcase(yyruleno==396); + /* (397) database_kw_opt ::= */ yytestcase(yyruleno==397); + /* (398) kwcolumn_opt ::= */ yytestcase(yyruleno==398); + /* (399) kwcolumn_opt ::= COLUMNKW */ yytestcase(yyruleno==399); + /* (400) vtabarglist ::= vtabarg */ yytestcase(yyruleno==400); + /* (401) vtabarglist ::= vtabarglist COMMA vtabarg */ yytestcase(yyruleno==401); + /* (402) vtabarg ::= vtabarg vtabargtoken */ yytestcase(yyruleno==402); + /* (403) anylist ::= */ yytestcase(yyruleno==403); + /* (404) anylist ::= anylist LP anylist RP */ yytestcase(yyruleno==404); + /* (405) anylist ::= anylist ANY */ yytestcase(yyruleno==405); + /* (406) with ::= */ yytestcase(yyruleno==406); + /* (407) windowdefn_list ::= windowdefn (OPTIMIZED OUT) */ assert(yyruleno!=407); + /* (408) window ::= frame_opt (OPTIMIZED OUT) */ assert(yyruleno!=408); break; /********** End reduce actions ************************************************/ }; @@ -176430,19 +177848,12 @@ SQLITE_PRIVATE void sqlite3Parser( (int)(yypParser->yytos - yypParser->yystack)); } #endif -#if YYSTACKDEPTH>0 if( yypParser->yytos>=yypParser->yystackEnd ){ - yyStackOverflow(yypParser); - break; - } -#else - if( yypParser->yytos>=&yypParser->yystack[yypParser->yystksz-1] ){ if( yyGrowStack(yypParser) ){ yyStackOverflow(yypParser); break; } } -#endif } yyact = yy_reduce(yypParser,yyruleno,yymajor,yyminor sqlite3ParserCTX_PARAM); }else if( yyact <= YY_MAX_SHIFTREDUCE ){ @@ -177513,27 +178924,58 @@ SQLITE_PRIVATE int sqlite3GetToken(const unsigned char *z, int *tokenType){ *tokenType = TK_INTEGER; #ifndef SQLITE_OMIT_HEX_INTEGER if( z[0]=='0' && (z[1]=='x' || z[1]=='X') && sqlite3Isxdigit(z[2]) ){ - for(i=3; sqlite3Isxdigit(z[i]); i++){} - return i; - } + for(i=3; 1; i++){ + if( sqlite3Isxdigit(z[i])==0 ){ + if( z[i]==SQLITE_DIGIT_SEPARATOR ){ + *tokenType = TK_QNUMBER; + }else{ + break; + } + } + } + }else #endif - for(i=0; sqlite3Isdigit(z[i]); i++){} + { + for(i=0; 1; i++){ + if( sqlite3Isdigit(z[i])==0 ){ + if( z[i]==SQLITE_DIGIT_SEPARATOR ){ + *tokenType = TK_QNUMBER; + }else{ + break; + } + } + } #ifndef SQLITE_OMIT_FLOATING_POINT - if( z[i]=='.' ){ - i++; - while( sqlite3Isdigit(z[i]) ){ i++; } - *tokenType = TK_FLOAT; - } - if( (z[i]=='e' || z[i]=='E') && - ( sqlite3Isdigit(z[i+1]) - || ((z[i+1]=='+' || z[i+1]=='-') && sqlite3Isdigit(z[i+2])) - ) - ){ - i += 2; - while( sqlite3Isdigit(z[i]) ){ i++; } - *tokenType = TK_FLOAT; - } + if( z[i]=='.' ){ + if( *tokenType==TK_INTEGER ) *tokenType = TK_FLOAT; + for(i++; 1; i++){ + if( sqlite3Isdigit(z[i])==0 ){ + if( z[i]==SQLITE_DIGIT_SEPARATOR ){ + *tokenType = TK_QNUMBER; + }else{ + break; + } + } + } + } + if( (z[i]=='e' || z[i]=='E') && + ( sqlite3Isdigit(z[i+1]) + || ((z[i+1]=='+' || z[i+1]=='-') && sqlite3Isdigit(z[i+2])) + ) + ){ + if( *tokenType==TK_INTEGER ) *tokenType = TK_FLOAT; + for(i+=2; 1; i++){ + if( sqlite3Isdigit(z[i])==0 ){ + if( z[i]==SQLITE_DIGIT_SEPARATOR ){ + *tokenType = TK_QNUMBER; + }else{ + break; + } + } + } + } #endif + } while( IdChar(z[i]) ){ *tokenType = TK_ILLEGAL; i++; @@ -177698,10 +179140,13 @@ SQLITE_PRIVATE int sqlite3RunParser(Parse *pParse, const char *zSql){ if( tokenType>=TK_WINDOW ){ assert( tokenType==TK_SPACE || tokenType==TK_OVER || tokenType==TK_FILTER || tokenType==TK_ILLEGAL || tokenType==TK_WINDOW + || tokenType==TK_QNUMBER ); #else if( tokenType>=TK_SPACE ){ - assert( tokenType==TK_SPACE || tokenType==TK_ILLEGAL ); + assert( tokenType==TK_SPACE || tokenType==TK_ILLEGAL + || tokenType==TK_QNUMBER + ); #endif /* SQLITE_OMIT_WINDOWFUNC */ if( AtomicLoad(&db->u1.isInterrupted) ){ pParse->rc = SQLITE_INTERRUPT; @@ -177734,7 +179179,7 @@ SQLITE_PRIVATE int sqlite3RunParser(Parse *pParse, const char *zSql){ assert( n==6 ); tokenType = analyzeFilterKeyword((const u8*)&zSql[6], lastTokenParsed); #endif /* SQLITE_OMIT_WINDOWFUNC */ - }else{ + }else if( tokenType!=TK_QNUMBER ){ Token x; x.z = zSql; x.n = n; @@ -188729,22 +190174,24 @@ static int fts3IntegrityMethod( char **pzErr /* Write error message here */ ){ Fts3Table *p = (Fts3Table*)pVtab; - int rc; + int rc = SQLITE_OK; int bOk = 0; UNUSED_PARAMETER(isQuick); rc = sqlite3Fts3IntegrityCheck(p, &bOk); - assert( rc!=SQLITE_CORRUPT_VTAB || bOk==0 ); - if( rc!=SQLITE_OK && rc!=SQLITE_CORRUPT_VTAB ){ + assert( rc!=SQLITE_CORRUPT_VTAB ); + if( rc==SQLITE_ERROR || (rc&0xFF)==SQLITE_CORRUPT ){ *pzErr = sqlite3_mprintf("unable to validate the inverted index for" " FTS%d table %s.%s: %s", p->bFts4 ? 4 : 3, zSchema, zTabname, sqlite3_errstr(rc)); - }else if( bOk==0 ){ + if( *pzErr ) rc = SQLITE_OK; + }else if( rc==SQLITE_OK && bOk==0 ){ *pzErr = sqlite3_mprintf("malformed inverted index for FTS%d table %s.%s", p->bFts4 ? 4 : 3, zSchema, zTabname); + if( *pzErr==0 ) rc = SQLITE_NOMEM; } sqlite3Fts3SegmentsClose(p); - return SQLITE_OK; + return rc; } @@ -200406,7 +201853,12 @@ SQLITE_PRIVATE int sqlite3Fts3IntegrityCheck(Fts3Table *p, int *pbOk){ sqlite3_finalize(pStmt); } - *pbOk = (rc==SQLITE_OK && cksum1==cksum2); + if( rc==SQLITE_CORRUPT_VTAB ){ + rc = SQLITE_OK; + *pbOk = 0; + }else{ + *pbOk = (rc==SQLITE_OK && cksum1==cksum2); + } return rc; } @@ -201312,7 +202764,7 @@ static void fts3SnippetDetails( } mCover |= mPhrase; - for(j=0; jnToken; j++){ + for(j=0; jnToken && jnSnippet; j++){ mHighlight |= (mPos>>j); } @@ -203973,7 +205425,6 @@ static void jsonAppendRawNZ(JsonString *p, const char *zIn, u32 N){ } } - /* Append formatted text (not to exceed N bytes) to the JsonString. */ static void jsonPrintf(int N, JsonString *p, const char *zFormat, ...){ @@ -204031,6 +205482,40 @@ static void jsonAppendSeparator(JsonString *p){ jsonAppendChar(p, ','); } +/* c is a control character. Append the canonical JSON representation +** of that control character to p. +** +** This routine assumes that the output buffer has already been enlarged +** sufficiently to hold the worst-case encoding plus a nul terminator. +*/ +static void jsonAppendControlChar(JsonString *p, u8 c){ + static const char aSpecial[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 'b', 't', 'n', 0, 'f', 'r', 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + assert( sizeof(aSpecial)==32 ); + assert( aSpecial['\b']=='b' ); + assert( aSpecial['\f']=='f' ); + assert( aSpecial['\n']=='n' ); + assert( aSpecial['\r']=='r' ); + assert( aSpecial['\t']=='t' ); + assert( c>=0 && cnUsed+7 <= p->nAlloc ); + if( aSpecial[c] ){ + p->zBuf[p->nUsed] = '\\'; + p->zBuf[p->nUsed+1] = aSpecial[c]; + p->nUsed += 2; + }else{ + p->zBuf[p->nUsed] = '\\'; + p->zBuf[p->nUsed+1] = 'u'; + p->zBuf[p->nUsed+2] = '0'; + p->zBuf[p->nUsed+3] = '0'; + p->zBuf[p->nUsed+4] = "0123456789abcdef"[c>>4]; + p->zBuf[p->nUsed+5] = "0123456789abcdef"[c&0xf]; + p->nUsed += 6; + } +} + /* Append the N-byte string in zIn to the end of the JsonString string ** under construction. Enclose the string in double-quotes ("...") and ** escape any double-quotes or backslash characters contained within the @@ -204090,35 +205575,14 @@ static void jsonAppendString(JsonString *p, const char *zIn, u32 N){ } c = z[0]; if( c=='"' || c=='\\' ){ - json_simple_escape: if( (p->nUsed+N+3 > p->nAlloc) && jsonStringGrow(p,N+3)!=0 ) return; p->zBuf[p->nUsed++] = '\\'; p->zBuf[p->nUsed++] = c; }else if( c=='\'' ){ p->zBuf[p->nUsed++] = c; }else{ - static const char aSpecial[] = { - 0, 0, 0, 0, 0, 0, 0, 0, 'b', 't', 'n', 0, 'f', 'r', 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 - }; - assert( sizeof(aSpecial)==32 ); - assert( aSpecial['\b']=='b' ); - assert( aSpecial['\f']=='f' ); - assert( aSpecial['\n']=='n' ); - assert( aSpecial['\r']=='r' ); - assert( aSpecial['\t']=='t' ); - assert( c>=0 && cnUsed+N+7 > p->nAlloc) && jsonStringGrow(p,N+7)!=0 ) return; - p->zBuf[p->nUsed++] = '\\'; - p->zBuf[p->nUsed++] = 'u'; - p->zBuf[p->nUsed++] = '0'; - p->zBuf[p->nUsed++] = '0'; - p->zBuf[p->nUsed++] = "0123456789abcdef"[c>>4]; - p->zBuf[p->nUsed++] = "0123456789abcdef"[c&0xf]; + jsonAppendControlChar(p, c); } z++; N--; @@ -204819,7 +206283,10 @@ static u32 jsonbValidityCheck( if( !jsonIsOk[z[j]] && z[j]!='\'' ){ if( z[j]=='"' ){ if( x==JSONB_TEXTJ ) return j+1; - }else if( z[j]!='\\' || j+1>=k ){ + }else if( z[j]<=0x1f ){ + /* Control characters in JSON5 string literals are ok */ + if( x==JSONB_TEXTJ ) return j+1; + }else if( NEVER(z[j]!='\\') || j+1>=k ){ return j+1; }else if( strchr("\"\\/bfnrt",z[j+1])!=0 ){ j++; @@ -205114,9 +206581,14 @@ json_parse_restart: return -1; } }else if( c<=0x1f ){ - /* Control characters are not allowed in strings */ - pParse->iErr = j; - return -1; + if( c==0 ){ + pParse->iErr = j; + return -1; + } + /* Control characters are not allowed in canonical JSON string + ** literals, but are allowed in JSON5 string literals. */ + opcode = JSONB_TEXT5; + pParse->hasNonstd = 1; }else if( c=='"' ){ opcode = JSONB_TEXT5; } @@ -205332,6 +206804,7 @@ json_parse_restart: return i+4; } /* fall-through into the default case that checks for NaN */ + /* no break */ deliberate_fall_through } default: { u32 k; @@ -205600,7 +207073,7 @@ static u32 jsonTranslateBlobToText( zIn = (const char*)&pParse->aBlob[i+n]; jsonAppendChar(pOut, '"'); while( sz2>0 ){ - for(k=0; k0 ){ jsonAppendRawNZ(pOut, zIn, k); if( k>=sz2 ){ @@ -205615,6 +207088,13 @@ static u32 jsonTranslateBlobToText( sz2--; continue; } + if( zIn[0]<=0x1f ){ + if( pOut->nUsed+7>pOut->nAlloc && jsonStringGrow(pOut,7) ) break; + jsonAppendControlChar(pOut, zIn[0]); + zIn++; + sz2--; + continue; + } assert( zIn[0]=='\\' ); assert( sz2>=1 ); if( sz2<2 ){ @@ -205717,6 +207197,112 @@ static u32 jsonTranslateBlobToText( return i+n+sz; } +/* Context for recursion of json_pretty() +*/ +typedef struct JsonPretty JsonPretty; +struct JsonPretty { + JsonParse *pParse; /* The BLOB being rendered */ + JsonString *pOut; /* Generate pretty output into this string */ + const char *zIndent; /* Use this text for indentation */ + u32 szIndent; /* Bytes in zIndent[] */ + u32 nIndent; /* Current level of indentation */ +}; + +/* Append indentation to the pretty JSON under construction */ +static void jsonPrettyIndent(JsonPretty *pPretty){ + u32 jj; + for(jj=0; jjnIndent; jj++){ + jsonAppendRaw(pPretty->pOut, pPretty->zIndent, pPretty->szIndent); + } +} + +/* +** Translate the binary JSONB representation of JSON beginning at +** pParse->aBlob[i] into a JSON text string. Append the JSON +** text onto the end of pOut. Return the index in pParse->aBlob[] +** of the first byte past the end of the element that is translated. +** +** This is a variant of jsonTranslateBlobToText() that "pretty-prints" +** the output. Extra whitespace is inserted to make the JSON easier +** for humans to read. +** +** If an error is detected in the BLOB input, the pOut->eErr flag +** might get set to JSTRING_MALFORMED. But not all BLOB input errors +** are detected. So a malformed JSONB input might either result +** in an error, or in incorrect JSON. +** +** The pOut->eErr JSTRING_OOM flag is set on a OOM. +*/ +static u32 jsonTranslateBlobToPrettyText( + JsonPretty *pPretty, /* Pretty-printing context */ + u32 i /* Start rendering at this index */ +){ + u32 sz, n, j, iEnd; + const JsonParse *pParse = pPretty->pParse; + JsonString *pOut = pPretty->pOut; + n = jsonbPayloadSize(pParse, i, &sz); + if( n==0 ){ + pOut->eErr |= JSTRING_MALFORMED; + return pParse->nBlob+1; + } + switch( pParse->aBlob[i] & 0x0f ){ + case JSONB_ARRAY: { + j = i+n; + iEnd = j+sz; + jsonAppendChar(pOut, '['); + if( jnIndent++; + while( pOut->eErr==0 ){ + jsonPrettyIndent(pPretty); + j = jsonTranslateBlobToPrettyText(pPretty, j); + if( j>=iEnd ) break; + jsonAppendRawNZ(pOut, ",\n", 2); + } + jsonAppendChar(pOut, '\n'); + pPretty->nIndent--; + jsonPrettyIndent(pPretty); + } + jsonAppendChar(pOut, ']'); + i = iEnd; + break; + } + case JSONB_OBJECT: { + j = i+n; + iEnd = j+sz; + jsonAppendChar(pOut, '{'); + if( jnIndent++; + while( pOut->eErr==0 ){ + jsonPrettyIndent(pPretty); + j = jsonTranslateBlobToText(pParse, j, pOut); + if( j>iEnd ){ + pOut->eErr |= JSTRING_MALFORMED; + break; + } + jsonAppendRawNZ(pOut, ": ", 2); + j = jsonTranslateBlobToPrettyText(pPretty, j); + if( j>=iEnd ) break; + jsonAppendRawNZ(pOut, ",\n", 2); + } + jsonAppendChar(pOut, '\n'); + pPretty->nIndent--; + jsonPrettyIndent(pPretty); + } + jsonAppendChar(pOut, '}'); + i = iEnd; + break; + } + default: { + i = jsonTranslateBlobToText(pParse, i, pOut); + break; + } + } + return i; +} + + /* Return true if the input pJson ** ** For performance reasons, this routine does not do a detailed check of the @@ -206967,11 +208553,12 @@ static void jsonParseFunc( if( p==0 ) return; if( argc==1 ){ jsonDebugPrintBlob(p, 0, p->nBlob, 0, &out); - sqlite3_result_text64(ctx, out.zText, out.nChar, SQLITE_DYNAMIC, SQLITE_UTF8); + sqlite3_result_text64(ctx,out.zText,out.nChar,SQLITE_TRANSIENT,SQLITE_UTF8); }else{ jsonShowParse(p); } jsonParseFree(p); + sqlite3_str_reset(&out); } #endif /* SQLITE_DEBUG */ @@ -207070,13 +208657,6 @@ static void jsonArrayLengthFunc( jsonParseFree(p); } -/* True if the string is all digits */ -static int jsonAllDigits(const char *z, int n){ - int i; - for(i=0; i $[NUMBER] // Not PG. Purely for convenience */ jsonStringInit(&jx, ctx); - if( jsonAllDigits(zPath, nPath) ){ + if( sqlite3_value_type(argv[i])==SQLITE_INTEGER ){ jsonAppendRawNZ(&jx, "[", 1); jsonAppendRaw(&jx, zPath, nPath); jsonAppendRawNZ(&jx, "]", 2); @@ -207635,6 +209215,40 @@ json_type_done: jsonParseFree(p); } +/* +** json_pretty(JSON) +** json_pretty(JSON, INDENT) +** +** Return text that is a pretty-printed rendering of the input JSON. +** If the argument is not valid JSON, return NULL. +** +** The INDENT argument is text that is used for indentation. If omitted, +** it defaults to four spaces (the same as PostgreSQL). +*/ +static void jsonPrettyFunc( + sqlite3_context *ctx, + int argc, + sqlite3_value **argv +){ + JsonString s; /* The output string */ + JsonPretty x; /* Pretty printing context */ + + memset(&x, 0, sizeof(x)); + x.pParse = jsonParseFuncArg(ctx, argv[0], 0); + if( x.pParse==0 ) return; + x.pOut = &s; + jsonStringInit(&s, ctx); + if( argc==1 || (x.zIndent = (const char*)sqlite3_value_text(argv[1]))==0 ){ + x.zIndent = " "; + x.szIndent = 4; + }else{ + x.szIndent = (u32)strlen(x.zIndent); + } + jsonTranslateBlobToPrettyText(&x, 0); + jsonReturnString(&s, 0, 0); + jsonParseFree(x.pParse); +} + /* ** json_valid(JSON) ** json_valid(JSON, FLAGS) @@ -208649,6 +210263,8 @@ SQLITE_PRIVATE void sqlite3RegisterJsonFunctions(void){ JFUNCTION(jsonb_object, -1,0,1, 1,1,0, jsonObjectFunc), JFUNCTION(json_patch, 2,1,1, 0,0,0, jsonPatchFunc), JFUNCTION(jsonb_patch, 2,1,0, 0,1,0, jsonPatchFunc), + JFUNCTION(json_pretty, 1,1,0, 0,0,0, jsonPrettyFunc), + JFUNCTION(json_pretty, 2,1,0, 0,0,0, jsonPrettyFunc), JFUNCTION(json_quote, 1,0,1, 1,0,0, jsonQuoteFunc), JFUNCTION(json_remove, -1,1,1, 0,0,0, jsonRemoveFunc), JFUNCTION(jsonb_remove, -1,1,0, 0,1,0, jsonRemoveFunc), @@ -210548,6 +212164,8 @@ static int deserializeGeometry(sqlite3_value *pValue, RtreeConstraint *pCons){ return SQLITE_OK; } +SQLITE_PRIVATE int sqlite3IntFloatCompare(i64,double); + /* ** Rtree virtual table module xFilter method. */ @@ -210577,7 +212195,8 @@ static int rtreeFilter( i64 iNode = 0; int eType = sqlite3_value_numeric_type(argv[0]); if( eType==SQLITE_INTEGER - || (eType==SQLITE_FLOAT && sqlite3_value_double(argv[0])==iRowid) + || (eType==SQLITE_FLOAT + && 0==sqlite3IntFloatCompare(iRowid,sqlite3_value_double(argv[0]))) ){ rc = findLeafNode(pRtree, iRowid, &pLeaf, &iNode); }else{ @@ -211932,6 +213551,7 @@ constraint: */ static int rtreeBeginTransaction(sqlite3_vtab *pVtab){ Rtree *pRtree = (Rtree *)pVtab; + assert( pRtree->inWrTrans==0 ); pRtree->inWrTrans = 1; return SQLITE_OK; } @@ -215486,7 +217106,7 @@ static void icuLoadCollation( UCollator *pUCollator; /* ICU library collation object */ int rc; /* Return code from sqlite3_create_collation_x() */ - assert(nArg==2); + assert(nArg==2 || nArg==3); (void)nArg; /* Unused parameter */ zLocale = (const char *)sqlite3_value_text(apArg[0]); zName = (const char *)sqlite3_value_text(apArg[1]); @@ -215501,7 +217121,39 @@ static void icuLoadCollation( return; } assert(p); - + if(nArg==3){ + const char *zOption = (const char*)sqlite3_value_text(apArg[2]); + static const struct { + const char *zName; + UColAttributeValue val; + } aStrength[] = { + { "PRIMARY", UCOL_PRIMARY }, + { "SECONDARY", UCOL_SECONDARY }, + { "TERTIARY", UCOL_TERTIARY }, + { "DEFAULT", UCOL_DEFAULT_STRENGTH }, + { "QUARTERNARY", UCOL_QUATERNARY }, + { "IDENTICAL", UCOL_IDENTICAL }, + }; + unsigned int i; + for(i=0; i=sizeof(aStrength)/sizeof(aStrength[0]) ){ + sqlite3_str *pStr = sqlite3_str_new(sqlite3_context_db_handle(p)); + sqlite3_str_appendf(pStr, + "unknown collation strength \"%s\" - should be one of:", + zOption); + for(i=0; ipTblIter, &p->zErrmsg); pIter->zTbl = 0; + pIter->zDataTbl = 0; }else{ pIter->zTbl = (const char*)sqlite3_column_text(pIter->pTblIter, 0); pIter->zDataTbl = (const char*)sqlite3_column_text(pIter->pTblIter,1); @@ -219454,7 +221109,7 @@ static i64 rbuShmChecksum(sqlite3rbu *p){ u32 volatile *ptr; p->rc = pDb->pMethods->xShmMap(pDb, 0, 32*1024, 0, (void volatile**)&ptr); if( p->rc==SQLITE_OK ){ - iRet = ((i64)ptr[10] << 32) + ptr[11]; + iRet = (i64)(((u64)ptr[10] << 32) + ptr[11]); } } return iRet; @@ -226925,14 +228580,14 @@ static int sessionChangesetNextOne( p->rc = sessionInputBuffer(&p->in, 2); if( p->rc!=SQLITE_OK ) return p->rc; + sessionDiscardData(&p->in); + p->in.iCurrent = p->in.iNext; + /* If the iterator is already at the end of the changeset, return DONE. */ if( p->in.iNext>=p->in.nData ){ return SQLITE_DONE; } - sessionDiscardData(&p->in); - p->in.iCurrent = p->in.iNext; - op = p->in.aData[p->in.iNext++]; while( op=='T' || op=='P' ){ if( pbNew ) *pbNew = 1; @@ -228667,6 +230322,7 @@ struct sqlite3_changegroup { int rc; /* Error code */ int bPatch; /* True to accumulate patchsets */ SessionTable *pList; /* List of tables in current patch */ + SessionBuffer rec; sqlite3 *db; /* Configured by changegroup_schema() */ char *zDb; /* Configured by changegroup_schema() */ @@ -228965,108 +230621,128 @@ static int sessionChangesetExtendRecord( } /* -** Add all changes in the changeset traversed by the iterator passed as -** the first argument to the changegroup hash tables. +** Locate or create a SessionTable object that may be used to add the +** change currently pointed to by iterator pIter to changegroup pGrp. +** If successful, set output variable (*ppTab) to point to the table +** object and return SQLITE_OK. Otherwise, if some error occurs, return +** an SQLite error code and leave (*ppTab) set to NULL. */ -static int sessionChangesetToHash( - sqlite3_changeset_iter *pIter, /* Iterator to read from */ - sqlite3_changegroup *pGrp, /* Changegroup object to add changeset to */ - int bRebase /* True if hash table is for rebasing */ +static int sessionChangesetFindTable( + sqlite3_changegroup *pGrp, + const char *zTab, + sqlite3_changeset_iter *pIter, + SessionTable **ppTab ){ - u8 *aRec; - int nRec; int rc = SQLITE_OK; SessionTable *pTab = 0; - SessionBuffer rec = {0, 0, 0}; + int nTab = (int)strlen(zTab); + u8 *abPK = 0; + int nCol = 0; - while( SQLITE_ROW==sessionChangesetNext(pIter, &aRec, &nRec, 0) ){ - const char *zNew; - int nCol; - int op; - int iHash; - int bIndirect; - SessionChange *pChange; - SessionChange *pExist = 0; - SessionChange **pp; + *ppTab = 0; + sqlite3changeset_pk(pIter, &abPK, &nCol); - /* Ensure that only changesets, or only patchsets, but not a mixture - ** of both, are being combined. It is an error to try to combine a - ** changeset and a patchset. */ - if( pGrp->pList==0 ){ - pGrp->bPatch = pIter->bPatchset; - }else if( pIter->bPatchset!=pGrp->bPatch ){ - rc = SQLITE_ERROR; - break; + /* Search the list for an existing table */ + for(pTab = pGrp->pList; pTab; pTab=pTab->pNext){ + if( 0==sqlite3_strnicmp(pTab->zName, zTab, nTab+1) ) break; + } + + /* If one was not found above, create a new table now */ + if( !pTab ){ + SessionTable **ppNew; + + pTab = sqlite3_malloc64(sizeof(SessionTable) + nCol + nTab+1); + if( !pTab ){ + return SQLITE_NOMEM; } + memset(pTab, 0, sizeof(SessionTable)); + pTab->nCol = nCol; + pTab->abPK = (u8*)&pTab[1]; + memcpy(pTab->abPK, abPK, nCol); + pTab->zName = (char*)&pTab->abPK[nCol]; + memcpy(pTab->zName, zTab, nTab+1); - sqlite3changeset_op(pIter, &zNew, &nCol, &op, &bIndirect); - if( !pTab || sqlite3_stricmp(zNew, pTab->zName) ){ - /* Search the list for a matching table */ - int nNew = (int)strlen(zNew); - u8 *abPK; - - sqlite3changeset_pk(pIter, &abPK, 0); - for(pTab = pGrp->pList; pTab; pTab=pTab->pNext){ - if( 0==sqlite3_strnicmp(pTab->zName, zNew, nNew+1) ) break; - } - if( !pTab ){ - SessionTable **ppTab; - - pTab = sqlite3_malloc64(sizeof(SessionTable) + nCol + nNew+1); - if( !pTab ){ - rc = SQLITE_NOMEM; - break; - } - memset(pTab, 0, sizeof(SessionTable)); - pTab->nCol = nCol; - pTab->abPK = (u8*)&pTab[1]; - memcpy(pTab->abPK, abPK, nCol); - pTab->zName = (char*)&pTab->abPK[nCol]; - memcpy(pTab->zName, zNew, nNew+1); - - if( pGrp->db ){ - pTab->nCol = 0; - rc = sessionInitTable(0, pTab, pGrp->db, pGrp->zDb); - if( rc ){ - assert( pTab->azCol==0 ); - sqlite3_free(pTab); - break; - } - } - - /* The new object must be linked on to the end of the list, not - ** simply added to the start of it. This is to ensure that the - ** tables within the output of sqlite3changegroup_output() are in - ** the right order. */ - for(ppTab=&pGrp->pList; *ppTab; ppTab=&(*ppTab)->pNext); - *ppTab = pTab; - } - - if( !sessionChangesetCheckCompat(pTab, nCol, abPK) ){ - rc = SQLITE_SCHEMA; - break; + if( pGrp->db ){ + pTab->nCol = 0; + rc = sessionInitTable(0, pTab, pGrp->db, pGrp->zDb); + if( rc ){ + assert( pTab->azCol==0 ); + sqlite3_free(pTab); + return rc; } } - if( nColnCol ){ - assert( pGrp->db ); - rc = sessionChangesetExtendRecord(pGrp, pTab, nCol, op, aRec, nRec, &rec); - if( rc ) break; - aRec = rec.aBuf; - nRec = rec.nBuf; - } + /* The new object must be linked on to the end of the list, not + ** simply added to the start of it. This is to ensure that the + ** tables within the output of sqlite3changegroup_output() are in + ** the right order. */ + for(ppNew=&pGrp->pList; *ppNew; ppNew=&(*ppNew)->pNext); + *ppNew = pTab; + } - if( sessionGrowHash(0, pIter->bPatchset, pTab) ){ - rc = SQLITE_NOMEM; - break; - } + /* Check that the table is compatible. */ + if( !sessionChangesetCheckCompat(pTab, nCol, abPK) ){ + rc = SQLITE_SCHEMA; + } + + *ppTab = pTab; + return rc; +} + +/* +** Add the change currently indicated by iterator pIter to the hash table +** belonging to changegroup pGrp. +*/ +static int sessionOneChangeToHash( + sqlite3_changegroup *pGrp, + sqlite3_changeset_iter *pIter, + int bRebase +){ + int rc = SQLITE_OK; + int nCol = 0; + int op = 0; + int iHash = 0; + int bIndirect = 0; + SessionChange *pChange = 0; + SessionChange *pExist = 0; + SessionChange **pp = 0; + SessionTable *pTab = 0; + u8 *aRec = &pIter->in.aData[pIter->in.iCurrent + 2]; + int nRec = (pIter->in.iNext - pIter->in.iCurrent) - 2; + + /* Ensure that only changesets, or only patchsets, but not a mixture + ** of both, are being combined. It is an error to try to combine a + ** changeset and a patchset. */ + if( pGrp->pList==0 ){ + pGrp->bPatch = pIter->bPatchset; + }else if( pIter->bPatchset!=pGrp->bPatch ){ + rc = SQLITE_ERROR; + } + + if( rc==SQLITE_OK ){ + const char *zTab = 0; + sqlite3changeset_op(pIter, &zTab, &nCol, &op, &bIndirect); + rc = sessionChangesetFindTable(pGrp, zTab, pIter, &pTab); + } + + if( rc==SQLITE_OK && nColnCol ){ + SessionBuffer *pBuf = &pGrp->rec; + rc = sessionChangesetExtendRecord(pGrp, pTab, nCol, op, aRec, nRec, pBuf); + aRec = pBuf->aBuf; + nRec = pBuf->nBuf; + assert( pGrp->db ); + } + + if( rc==SQLITE_OK && sessionGrowHash(0, pIter->bPatchset, pTab) ){ + rc = SQLITE_NOMEM; + } + + if( rc==SQLITE_OK ){ + /* Search for existing entry. If found, remove it from the hash table. + ** Code below may link it back in. */ iHash = sessionChangeHash( pTab, (pIter->bPatchset && op==SQLITE_DELETE), aRec, pTab->nChange ); - - /* Search for existing entry. If found, remove it from the hash table. - ** Code below may link it back in. - */ for(pp=&pTab->apChange[iHash]; *pp; pp=&(*pp)->pNext){ int bPkOnly1 = 0; int bPkOnly2 = 0; @@ -229081,19 +230757,41 @@ static int sessionChangesetToHash( break; } } + } + if( rc==SQLITE_OK ){ rc = sessionChangeMerge(pTab, bRebase, pIter->bPatchset, pExist, op, bIndirect, aRec, nRec, &pChange ); - if( rc ) break; - if( pChange ){ - pChange->pNext = pTab->apChange[iHash]; - pTab->apChange[iHash] = pChange; - pTab->nEntry++; - } + } + if( rc==SQLITE_OK && pChange ){ + pChange->pNext = pTab->apChange[iHash]; + pTab->apChange[iHash] = pChange; + pTab->nEntry++; + } + + if( rc==SQLITE_OK ) rc = pIter->rc; + return rc; +} + +/* +** Add all changes in the changeset traversed by the iterator passed as +** the first argument to the changegroup hash tables. +*/ +static int sessionChangesetToHash( + sqlite3_changeset_iter *pIter, /* Iterator to read from */ + sqlite3_changegroup *pGrp, /* Changegroup object to add changeset to */ + int bRebase /* True if hash table is for rebasing */ +){ + u8 *aRec; + int nRec; + int rc = SQLITE_OK; + + while( SQLITE_ROW==(sessionChangesetNext(pIter, &aRec, &nRec, 0)) ){ + rc = sessionOneChangeToHash(pGrp, pIter, bRebase); + if( rc!=SQLITE_OK ) break; } - sqlite3_free(rec.aBuf); if( rc==SQLITE_OK ) rc = pIter->rc; return rc; } @@ -229221,6 +230919,23 @@ SQLITE_API int sqlite3changegroup_add(sqlite3_changegroup *pGrp, int nData, void return rc; } +/* +** Add a single change to a changeset-group. +*/ +SQLITE_API int sqlite3changegroup_add_change( + sqlite3_changegroup *pGrp, + sqlite3_changeset_iter *pIter +){ + if( pIter->in.iCurrent==pIter->in.iNext + || pIter->rc!=SQLITE_OK + || pIter->bInvert + ){ + /* Iterator does not point to any valid entry or is an INVERT iterator. */ + return SQLITE_ERROR; + } + return sessionOneChangeToHash(pGrp, pIter, 0); +} + /* ** Obtain a buffer containing a changeset representing the concatenation ** of all changesets added to the group so far. @@ -229270,6 +230985,7 @@ SQLITE_API void sqlite3changegroup_delete(sqlite3_changegroup *pGrp){ if( pGrp ){ sqlite3_free(pGrp->zDb); sessionDeleteTable(0, pGrp->pList); + sqlite3_free(pGrp->rec.aBuf); sqlite3_free(pGrp); } } @@ -229671,6 +231387,7 @@ SQLITE_API int sqlite3rebaser_rebase_strm( SQLITE_API void sqlite3rebaser_delete(sqlite3_rebaser *p){ if( p ){ sessionDeleteTable(0, p->grp.pList); + sqlite3_free(p->grp.rec.aBuf); sqlite3_free(p); } } @@ -229768,8 +231485,8 @@ struct Fts5PhraseIter { ** EXTENSION API FUNCTIONS ** ** xUserData(pFts): -** Return a copy of the context pointer the extension function was -** registered with. +** Return a copy of the pUserData pointer passed to the xCreateFunction() +** API when the extension function was registered. ** ** xColumnTotalSize(pFts, iCol, pnToken): ** If parameter iCol is less than zero, set output variable *pnToken @@ -231365,6 +233082,9 @@ static void sqlite3Fts5UnicodeAscii(u8*, u8*); ** sqlite3Fts5ParserARG_STORE Code to store %extra_argument into fts5yypParser ** sqlite3Fts5ParserARG_FETCH Code to extract %extra_argument from fts5yypParser ** sqlite3Fts5ParserCTX_* As sqlite3Fts5ParserARG_ except for %extra_context +** fts5YYREALLOC Name of the realloc() function to use +** fts5YYFREE Name of the free() function to use +** fts5YYDYNSTACK True if stack space should be extended on heap ** fts5YYERRORSYMBOL is the code number of the error symbol. If not ** defined, then do no error processing. ** fts5YYNSTATE the combined number of states. @@ -231378,6 +233098,8 @@ static void sqlite3Fts5UnicodeAscii(u8*, u8*); ** fts5YY_NO_ACTION The fts5yy_action[] code for no-op ** fts5YY_MIN_REDUCE Minimum value for reduce actions ** fts5YY_MAX_REDUCE Maximum value for reduce actions +** fts5YY_MIN_DSTRCTR Minimum symbol value that has a destructor +** fts5YY_MAX_DSTRCTR Maximum symbol value that has a destructor */ #ifndef INTERFACE # define INTERFACE 1 @@ -231404,6 +233126,9 @@ typedef union { #define sqlite3Fts5ParserARG_PARAM ,pParse #define sqlite3Fts5ParserARG_FETCH Fts5Parse *pParse=fts5yypParser->pParse; #define sqlite3Fts5ParserARG_STORE fts5yypParser->pParse=pParse; +#define fts5YYREALLOC realloc +#define fts5YYFREE free +#define fts5YYDYNSTACK 0 #define sqlite3Fts5ParserCTX_SDECL #define sqlite3Fts5ParserCTX_PDECL #define sqlite3Fts5ParserCTX_PARAM @@ -231421,6 +233146,8 @@ typedef union { #define fts5YY_NO_ACTION 82 #define fts5YY_MIN_REDUCE 83 #define fts5YY_MAX_REDUCE 110 +#define fts5YY_MIN_DSTRCTR 16 +#define fts5YY_MAX_DSTRCTR 24 /************* End control #defines *******************************************/ #define fts5YY_NLOOKAHEAD ((int)(sizeof(fts5yy_lookahead)/sizeof(fts5yy_lookahead[0]))) @@ -231436,6 +233163,22 @@ typedef union { # define fts5yytestcase(X) #endif +/* Macro to determine if stack space has the ability to grow using +** heap memory. +*/ +#if fts5YYSTACKDEPTH<=0 || fts5YYDYNSTACK +# define fts5YYGROWABLESTACK 1 +#else +# define fts5YYGROWABLESTACK 0 +#endif + +/* Guarantee a minimum number of initial stack slots. +*/ +#if fts5YYSTACKDEPTH<=0 +# undef fts5YYSTACKDEPTH +# define fts5YYSTACKDEPTH 2 /* Need a minimum stack size */ +#endif + /* Next are the tables used to determine what action to take based on the ** current state and lookahead token. These tables are used to implement @@ -231596,14 +233339,9 @@ struct fts5yyParser { #endif sqlite3Fts5ParserARG_SDECL /* A place to hold %extra_argument */ sqlite3Fts5ParserCTX_SDECL /* A place to hold %extra_context */ -#if fts5YYSTACKDEPTH<=0 - int fts5yystksz; /* Current side of the stack */ - fts5yyStackEntry *fts5yystack; /* The parser's stack */ - fts5yyStackEntry fts5yystk0; /* First stack entry */ -#else - fts5yyStackEntry fts5yystack[fts5YYSTACKDEPTH]; /* The parser's stack */ - fts5yyStackEntry *fts5yystackEnd; /* Last entry in the stack */ -#endif + fts5yyStackEntry *fts5yystackEnd; /* Last entry in the stack */ + fts5yyStackEntry *fts5yystack; /* The parser stack */ + fts5yyStackEntry fts5yystk0[fts5YYSTACKDEPTH]; /* Initial stack space */ }; typedef struct fts5yyParser fts5yyParser; @@ -231710,37 +233448,45 @@ static const char *const fts5yyRuleName[] = { #endif /* NDEBUG */ -#if fts5YYSTACKDEPTH<=0 +#if fts5YYGROWABLESTACK /* ** Try to increase the size of the parser stack. Return the number ** of errors. Return 0 on success. */ static int fts5yyGrowStack(fts5yyParser *p){ + int oldSize = 1 + (int)(p->fts5yystackEnd - p->fts5yystack); int newSize; int idx; fts5yyStackEntry *pNew; - newSize = p->fts5yystksz*2 + 100; - idx = p->fts5yytos ? (int)(p->fts5yytos - p->fts5yystack) : 0; - if( p->fts5yystack==&p->fts5yystk0 ){ - pNew = malloc(newSize*sizeof(pNew[0])); - if( pNew ) pNew[0] = p->fts5yystk0; + newSize = oldSize*2 + 100; + idx = (int)(p->fts5yytos - p->fts5yystack); + if( p->fts5yystack==p->fts5yystk0 ){ + pNew = fts5YYREALLOC(0, newSize*sizeof(pNew[0])); + if( pNew==0 ) return 1; + memcpy(pNew, p->fts5yystack, oldSize*sizeof(pNew[0])); }else{ - pNew = realloc(p->fts5yystack, newSize*sizeof(pNew[0])); + pNew = fts5YYREALLOC(p->fts5yystack, newSize*sizeof(pNew[0])); + if( pNew==0 ) return 1; } - if( pNew ){ - p->fts5yystack = pNew; - p->fts5yytos = &p->fts5yystack[idx]; + p->fts5yystack = pNew; + p->fts5yytos = &p->fts5yystack[idx]; #ifndef NDEBUG - if( fts5yyTraceFILE ){ - fprintf(fts5yyTraceFILE,"%sStack grows from %d to %d entries.\n", - fts5yyTracePrompt, p->fts5yystksz, newSize); - } -#endif - p->fts5yystksz = newSize; + if( fts5yyTraceFILE ){ + fprintf(fts5yyTraceFILE,"%sStack grows from %d to %d entries.\n", + fts5yyTracePrompt, oldSize, newSize); } - return pNew==0; +#endif + p->fts5yystackEnd = &p->fts5yystack[newSize-1]; + return 0; } +#endif /* fts5YYGROWABLESTACK */ + +#if !fts5YYGROWABLESTACK +/* For builds that do no have a growable stack, fts5yyGrowStack always +** returns an error. +*/ +# define fts5yyGrowStack(X) 1 #endif /* Datatype of the argument to the memory allocated passed as the @@ -231760,24 +233506,14 @@ static void sqlite3Fts5ParserInit(void *fts5yypRawParser sqlite3Fts5ParserCTX_PD #ifdef fts5YYTRACKMAXSTACKDEPTH fts5yypParser->fts5yyhwm = 0; #endif -#if fts5YYSTACKDEPTH<=0 - fts5yypParser->fts5yytos = NULL; - fts5yypParser->fts5yystack = NULL; - fts5yypParser->fts5yystksz = 0; - if( fts5yyGrowStack(fts5yypParser) ){ - fts5yypParser->fts5yystack = &fts5yypParser->fts5yystk0; - fts5yypParser->fts5yystksz = 1; - } -#endif + fts5yypParser->fts5yystack = fts5yypParser->fts5yystk0; + fts5yypParser->fts5yystackEnd = &fts5yypParser->fts5yystack[fts5YYSTACKDEPTH-1]; #ifndef fts5YYNOERRORRECOVERY fts5yypParser->fts5yyerrcnt = -1; #endif fts5yypParser->fts5yytos = fts5yypParser->fts5yystack; fts5yypParser->fts5yystack[0].stateno = 0; fts5yypParser->fts5yystack[0].major = 0; -#if fts5YYSTACKDEPTH>0 - fts5yypParser->fts5yystackEnd = &fts5yypParser->fts5yystack[fts5YYSTACKDEPTH-1]; -#endif } #ifndef sqlite3Fts5Parser_ENGINEALWAYSONSTACK @@ -231891,9 +233627,26 @@ static void fts5yy_pop_parser_stack(fts5yyParser *pParser){ */ static void sqlite3Fts5ParserFinalize(void *p){ fts5yyParser *pParser = (fts5yyParser*)p; - while( pParser->fts5yytos>pParser->fts5yystack ) fts5yy_pop_parser_stack(pParser); -#if fts5YYSTACKDEPTH<=0 - if( pParser->fts5yystack!=&pParser->fts5yystk0 ) free(pParser->fts5yystack); + + /* In-lined version of calling fts5yy_pop_parser_stack() for each + ** element left in the stack */ + fts5yyStackEntry *fts5yytos = pParser->fts5yytos; + while( fts5yytos>pParser->fts5yystack ){ +#ifndef NDEBUG + if( fts5yyTraceFILE ){ + fprintf(fts5yyTraceFILE,"%sPopping %s\n", + fts5yyTracePrompt, + fts5yyTokenName[fts5yytos->major]); + } +#endif + if( fts5yytos->major>=fts5YY_MIN_DSTRCTR ){ + fts5yy_destructor(pParser, fts5yytos->major, &fts5yytos->minor); + } + fts5yytos--; + } + +#if fts5YYGROWABLESTACK + if( pParser->fts5yystack!=pParser->fts5yystk0 ) fts5YYFREE(pParser->fts5yystack); #endif } @@ -232120,25 +233873,19 @@ static void fts5yy_shift( assert( fts5yypParser->fts5yyhwm == (int)(fts5yypParser->fts5yytos - fts5yypParser->fts5yystack) ); } #endif -#if fts5YYSTACKDEPTH>0 - if( fts5yypParser->fts5yytos>fts5yypParser->fts5yystackEnd ){ - fts5yypParser->fts5yytos--; - fts5yyStackOverflow(fts5yypParser); - return; - } -#else - if( fts5yypParser->fts5yytos>=&fts5yypParser->fts5yystack[fts5yypParser->fts5yystksz] ){ + fts5yytos = fts5yypParser->fts5yytos; + if( fts5yytos>fts5yypParser->fts5yystackEnd ){ if( fts5yyGrowStack(fts5yypParser) ){ fts5yypParser->fts5yytos--; fts5yyStackOverflow(fts5yypParser); return; } + fts5yytos = fts5yypParser->fts5yytos; + assert( fts5yytos <= fts5yypParser->fts5yystackEnd ); } -#endif if( fts5yyNewState > fts5YY_MAX_SHIFT ){ fts5yyNewState += fts5YY_MIN_REDUCE - fts5YY_MIN_SHIFTREDUCE; } - fts5yytos = fts5yypParser->fts5yytos; fts5yytos->stateno = fts5yyNewState; fts5yytos->major = fts5yyMajor; fts5yytos->minor.fts5yy0 = fts5yyMinor; @@ -232575,19 +234322,12 @@ static void sqlite3Fts5Parser( (int)(fts5yypParser->fts5yytos - fts5yypParser->fts5yystack)); } #endif -#if fts5YYSTACKDEPTH>0 if( fts5yypParser->fts5yytos>=fts5yypParser->fts5yystackEnd ){ - fts5yyStackOverflow(fts5yypParser); - break; - } -#else - if( fts5yypParser->fts5yytos>=&fts5yypParser->fts5yystack[fts5yypParser->fts5yystksz-1] ){ if( fts5yyGrowStack(fts5yypParser) ){ fts5yyStackOverflow(fts5yypParser); break; } } -#endif } fts5yyact = fts5yy_reduce(fts5yypParser,fts5yyruleno,fts5yymajor,fts5yyminor sqlite3Fts5ParserCTX_PARAM); }else if( fts5yyact <= fts5YY_MAX_SHIFTREDUCE ){ @@ -250799,7 +252539,7 @@ static void fts5SourceIdFunc( ){ assert( nArg==0 ); UNUSED_PARAM2(nArg, apUnused); - sqlite3_result_text(pCtx, "fts5: 2024-04-15 13:34:05 8653b758870e6ef0c98d46b3ace27849054af85da891eb121e9aaa537f1e8355", -1, SQLITE_TRANSIENT); + sqlite3_result_text(pCtx, "fts5: 2024-05-23 13:25:27 96c92aba00c8375bc32fafcdf12429c58bd8aabfcadab6683e35bbb9cdebf19e", -1, SQLITE_TRANSIENT); } /* @@ -250838,6 +252578,7 @@ static int fts5IntegrityMethod( if( (rc&0xff)==SQLITE_CORRUPT ){ *pzErr = sqlite3_mprintf("malformed inverted index for FTS5 table %s.%s", zSchema, zTabname); + rc = (*pzErr) ? SQLITE_OK : SQLITE_NOMEM; }else if( rc!=SQLITE_OK ){ *pzErr = sqlite3_mprintf("unable to validate the inverted index for" " FTS5 table %s.%s: %s", @@ -250845,7 +252586,7 @@ static int fts5IntegrityMethod( } sqlite3Fts5IndexCloseReader(pTab->p.pIndex); - return SQLITE_OK; + return rc; } static int fts5Init(sqlite3 *db){ diff --git a/src/libs/3rdparty/sqlite/sqlite3.h b/src/libs/3rdparty/sqlite/sqlite3.h index 2618b37a7b8..57df8dcf202 100644 --- a/src/libs/3rdparty/sqlite/sqlite3.h +++ b/src/libs/3rdparty/sqlite/sqlite3.h @@ -146,9 +146,9 @@ extern "C" { ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ -#define SQLITE_VERSION "3.45.3" -#define SQLITE_VERSION_NUMBER 3045003 -#define SQLITE_SOURCE_ID "2024-04-15 13:34:05 8653b758870e6ef0c98d46b3ace27849054af85da891eb121e9aaa537f1e8355" +#define SQLITE_VERSION "3.46.0" +#define SQLITE_VERSION_NUMBER 3046000 +#define SQLITE_SOURCE_ID "2024-05-23 13:25:27 96c92aba00c8375bc32fafcdf12429c58bd8aabfcadab6683e35bbb9cdebf19e" /* ** CAPI3REF: Run-Time Library Version Numbers @@ -764,11 +764,11 @@ struct sqlite3_file { ** ** xLock() upgrades the database file lock. In other words, xLock() moves the ** database file lock in the direction NONE toward EXCLUSIVE. The argument to -** xLock() is always on of SHARED, RESERVED, PENDING, or EXCLUSIVE, never +** xLock() is always one of SHARED, RESERVED, PENDING, or EXCLUSIVE, never ** SQLITE_LOCK_NONE. If the database file lock is already at or above the ** requested lock, then the call to xLock() is a no-op. ** xUnlock() downgrades the database file lock to either SHARED or NONE. -* If the lock is already at or below the requested lock state, then the call +** If the lock is already at or below the requested lock state, then the call ** to xUnlock() is a no-op. ** The xCheckReservedLock() method checks whether any database connection, ** either in this process or in some other process, is holding a RESERVED, @@ -3305,8 +3305,8 @@ SQLITE_API int sqlite3_set_authorizer( #define SQLITE_RECURSIVE 33 /* NULL NULL */ /* -** CAPI3REF: Tracing And Profiling Functions -** METHOD: sqlite3 +** CAPI3REF: Deprecated Tracing And Profiling Functions +** DEPRECATED ** ** These routines are deprecated. Use the [sqlite3_trace_v2()] interface ** instead of the routines described here. @@ -6887,6 +6887,12 @@ SQLITE_API int sqlite3_autovacuum_pages( ** The exceptions defined in this paragraph might change in a future ** release of SQLite. ** +** Whether the update hook is invoked before or after the +** corresponding change is currently unspecified and may differ +** depending on the type of change. Do not rely on the order of the +** hook call with regards to the final result of the operation which +** triggers the hook. +** ** The update hook implementation must not do anything that will modify ** the database connection that invoked the update hook. Any actions ** to modify the database connection must be deferred until after the @@ -8357,7 +8363,7 @@ SQLITE_API int sqlite3_test_control(int op, ...); ** The sqlite3_keyword_count() interface returns the number of distinct ** keywords understood by SQLite. ** -** The sqlite3_keyword_name(N,Z,L) interface finds the N-th keyword and +** The sqlite3_keyword_name(N,Z,L) interface finds the 0-based N-th keyword and ** makes *Z point to that keyword expressed as UTF8 and writes the number ** of bytes in the keyword into *L. The string that *Z points to is not ** zero-terminated. The sqlite3_keyword_name(N,Z,L) routine returns @@ -9936,24 +9942,45 @@ SQLITE_API const char *sqlite3_vtab_collation(sqlite3_index_info*,int); **

  • ** ^(If the sqlite3_vtab_distinct() interface returns 2, that means ** that the query planner does not need the rows returned in any particular -** order, as long as rows with the same values in all "aOrderBy" columns -** are adjacent.)^ ^(Furthermore, only a single row for each particular -** combination of values in the columns identified by the "aOrderBy" field -** needs to be returned.)^ ^It is always ok for two or more rows with the same -** values in all "aOrderBy" columns to be returned, as long as all such rows -** are adjacent. ^The virtual table may, if it chooses, omit extra rows -** that have the same value for all columns identified by "aOrderBy". -** ^However omitting the extra rows is optional. +** order, as long as rows with the same values in all columns identified +** by "aOrderBy" are adjacent.)^ ^(Furthermore, when two or more rows +** contain the same values for all columns identified by "colUsed", all but +** one such row may optionally be omitted from the result.)^ +** The virtual table is not required to omit rows that are duplicates +** over the "colUsed" columns, but if the virtual table can do that without +** too much extra effort, it could potentially help the query to run faster. ** This mode is used for a DISTINCT query. **

  • -** ^(If the sqlite3_vtab_distinct() interface returns 3, that means -** that the query planner needs only distinct rows but it does need the -** rows to be sorted.)^ ^The virtual table implementation is free to omit -** rows that are identical in all aOrderBy columns, if it wants to, but -** it is not required to omit any rows. This mode is used for queries +** ^(If the sqlite3_vtab_distinct() interface returns 3, that means the +** virtual table must return rows in the order defined by "aOrderBy" as +** if the sqlite3_vtab_distinct() interface had returned 0. However if +** two or more rows in the result have the same values for all columns +** identified by "colUsed", then all but one such row may optionally be +** omitted.)^ Like when the return value is 2, the virtual table +** is not required to omit rows that are duplicates over the "colUsed" +** columns, but if the virtual table can do that without +** too much extra effort, it could potentially help the query to run faster. +** This mode is used for queries ** that have both DISTINCT and ORDER BY clauses. ** ** +**

    The following table summarizes the conditions under which the +** virtual table is allowed to set the "orderByConsumed" flag based on +** the value returned by sqlite3_vtab_distinct(). This table is a +** restatement of the previous four paragraphs: +** +** +** +**
    sqlite3_vtab_distinct() return value +** Rows are returned in aOrderBy order +** Rows with the same value in all aOrderBy columns are adjacent +** Duplicates over all colUsed columns may be omitted +**
    0yesyesno +**
    1noyesno +**
    2noyesyes +**
    3yesyesyes +**
    +** ** ^For the purposes of comparing virtual table output values to see if the ** values are same value for sorting purposes, two NULL values are considered ** to be the same. In other words, the comparison operator is "IS" @@ -11998,6 +12025,30 @@ SQLITE_API int sqlite3changegroup_schema(sqlite3_changegroup*, sqlite3*, const c */ SQLITE_API int sqlite3changegroup_add(sqlite3_changegroup*, int nData, void *pData); +/* +** CAPI3REF: Add A Single Change To A Changegroup +** METHOD: sqlite3_changegroup +** +** This function adds the single change currently indicated by the iterator +** passed as the second argument to the changegroup object. The rules for +** adding the change are just as described for [sqlite3changegroup_add()]. +** +** If the change is successfully added to the changegroup, SQLITE_OK is +** returned. Otherwise, an SQLite error code is returned. +** +** The iterator must point to a valid entry when this function is called. +** If it does not, SQLITE_ERROR is returned and no change is added to the +** changegroup. Additionally, the iterator must not have been opened with +** the SQLITE_CHANGESETAPPLY_INVERT flag. In this case SQLITE_ERROR is also +** returned. +*/ +SQLITE_API int sqlite3changegroup_add_change( + sqlite3_changegroup*, + sqlite3_changeset_iter* +); + + + /* ** CAPI3REF: Obtain A Composite Changeset From A Changegroup ** METHOD: sqlite3_changegroup @@ -12802,8 +12853,8 @@ struct Fts5PhraseIter { ** EXTENSION API FUNCTIONS ** ** xUserData(pFts): -** Return a copy of the context pointer the extension function was -** registered with. +** Return a copy of the pUserData pointer passed to the xCreateFunction() +** API when the extension function was registered. ** ** xColumnTotalSize(pFts, iCol, pnToken): ** If parameter iCol is less than zero, set output variable *pnToken From fa3a6a39d64e38213487798a7445ad3319252252 Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Tue, 28 May 2024 12:27:56 +0300 Subject: [PATCH 151/154] QmlDesigner: Rename user bundles json files to just bundle.json Change-Id: I23440591083146d9630d0beec8a22efbc5339d35 Reviewed-by: Miikka Heikkinen --- .../contentlibrary/contentlibraryusermodel.cpp | 13 +++++++------ .../contentlibrary/contentlibraryview.cpp | 6 +++--- src/plugins/qmldesigner/qmldesignerconstants.h | 1 + 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp index 2af636da6d6..8dcf4575f78 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp @@ -12,6 +12,7 @@ #include #include +#include #include #include @@ -213,7 +214,7 @@ void ContentLibraryUserModel::removeMaterialFromContentLib(ContentLibraryMateria } m_bundleObjMaterial.insert("items", itemsArr); - auto result = bundlePath.pathAppended("user_material_bundle.json") + auto result = bundlePath.pathAppended(Constants::BUNDLE_JSON_FILENAME) .writeFileContents(QJsonDocument(m_bundleObjMaterial).toJson()); if (!result) qWarning() << __FUNCTION__ << result.error(); @@ -265,7 +266,7 @@ void ContentLibraryUserModel::remove3DFromContentLib(ContentLibraryItem *item) } m_bundleObj3D.insert("items", itemsArr); - auto result = m_bundlePath3D.pathAppended("user_3d_bundle.json") + auto result = m_bundlePath3D.pathAppended(Constants::BUNDLE_JSON_FILENAME) .writeFileContents(QJsonDocument(m_bundleObj3D).toJson()); if (!result) qWarning() << __FUNCTION__ << result.error(); @@ -384,7 +385,7 @@ void ContentLibraryUserModel::loadMaterialBundle() m_bundlePathMaterial.ensureWritableDir(); m_bundlePathMaterial.pathAppended("icons").ensureWritableDir(); - auto jsonFilePath = m_bundlePathMaterial.pathAppended("user_materials_bundle.json"); + auto jsonFilePath = m_bundlePathMaterial.pathAppended(Constants::BUNDLE_JSON_FILENAME); if (!jsonFilePath.exists()) { QString jsonContent = "{\n"; jsonContent += " \"id\": \"UserMaterials\",\n"; @@ -407,7 +408,7 @@ void ContentLibraryUserModel::loadMaterialBundle() QJsonDocument bundleJsonDoc = QJsonDocument::fromJson(jsonContents.value()); if (bundleJsonDoc.isNull()) { - qWarning() << __FUNCTION__ << "Invalid user_materials_bundle.json file"; + qWarning() << __FUNCTION__ << "Invalid json file" << jsonFilePath; emit dataChanged(index(MaterialsSectionIdx), index(MaterialsSectionIdx)); return; } @@ -464,7 +465,7 @@ void ContentLibraryUserModel::load3DBundle() m_bundlePath3D.ensureWritableDir(); m_bundlePath3D.pathAppended("icons").ensureWritableDir(); - auto jsonFilePath = m_bundlePath3D.pathAppended("user_3d_bundle.json"); + auto jsonFilePath = m_bundlePath3D.pathAppended(Constants::BUNDLE_JSON_FILENAME); if (!jsonFilePath.exists()) { QByteArray jsonContent = "{\n"; jsonContent += " \"id\": \"User3D\",\n"; @@ -487,7 +488,7 @@ void ContentLibraryUserModel::load3DBundle() QJsonDocument bundleJsonDoc = QJsonDocument::fromJson(jsonContents.value()); if (bundleJsonDoc.isNull()) { - qWarning() << __FUNCTION__ << "Invalid user_3d_bundle.json file"; + qWarning() << __FUNCTION__ << "Invalid json file" << jsonFilePath; emit dataChanged(index(Items3DSectionIdx), index(Items3DSectionIdx)); return; } diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp index 1287e246d13..9258faaf33c 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp @@ -535,7 +535,7 @@ void ContentLibraryView::addLibMaterial(const ModelNode &node, const QPixmap &ic itemsArr.append(itemObj); jsonRef["items"] = itemsArr; - auto result = bundlePath.pathAppended("user_materials_bundle.json") + auto result = bundlePath.pathAppended(Constants::BUNDLE_JSON_FILENAME) .writeFileContents(QJsonDocument(jsonRef).toJson()); if (!result) qWarning() << __FUNCTION__ << result.error(); @@ -731,7 +731,7 @@ void ContentLibraryView::addLib3DComponent(const ModelNode &node) jsonRef["items"] = itemsArr; - auto result = bundlePath.pathAppended("user_3d_bundle.json") + auto result = bundlePath.pathAppended(Constants::BUNDLE_JSON_FILENAME) .writeFileContents(QJsonDocument(jsonRef).toJson()); if (!result) qWarning() << __FUNCTION__ << result.error(); @@ -771,7 +771,7 @@ void ContentLibraryView::addLib3DItem(const ModelNode &node) jsonRef["items"] = itemsArr; - auto result = bundlePath.pathAppended("user_3d_bundle.json") + auto result = bundlePath.pathAppended(Constants::BUNDLE_JSON_FILENAME) .writeFileContents(QJsonDocument(jsonRef).toJson()); if (!result) qWarning() << __FUNCTION__ << result.error(); diff --git a/src/plugins/qmldesigner/qmldesignerconstants.h b/src/plugins/qmldesigner/qmldesignerconstants.h index c0d69bef656..28f3ed6097b 100644 --- a/src/plugins/qmldesigner/qmldesignerconstants.h +++ b/src/plugins/qmldesigner/qmldesignerconstants.h @@ -78,6 +78,7 @@ inline constexpr char EDIT3D_SNAP_CONFIG[] = "QmlDesigner.Editor3D.SnapConfig"; inline constexpr char EDIT3D_CAMERA_SPEED_CONFIG[] = "QmlDesigner.Editor3D.CameraSpeedConfig"; inline constexpr char QML_DESIGNER_SUBFOLDER[] = "/designer/"; +inline constexpr char BUNDLE_JSON_FILENAME[] = "bundle.json"; inline constexpr char COMPONENT_BUNDLES_TYPE[] = "Bundles"; inline constexpr char COMPONENT_BUNDLES_MATERIAL_BUNDLE_TYPE[] = "Materials"; inline constexpr char COMPONENT_BUNDLES_EFFECT_BUNDLE_TYPE[] = "Effects"; From f752178576c7213811ccac9a1b1b1d99683f925b Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Tue, 28 May 2024 13:17:49 +0300 Subject: [PATCH 152/154] QmlDesigner: Fix examples version handling Example version should be <= QDS version to show in the examples. Fixes: QDS-12853 Change-Id: Iaa4e6244a5451e05672f9001a0d74ef84144b1bc Reviewed-by: Miikka Heikkinen --- .../studiowelcome/studiowelcomeplugin.cpp | 35 +++++++++---------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/src/plugins/studiowelcome/studiowelcomeplugin.cpp b/src/plugins/studiowelcome/studiowelcomeplugin.cpp index fae0c83d41d..5cbc0b673cb 100644 --- a/src/plugins/studiowelcome/studiowelcomeplugin.cpp +++ b/src/plugins/studiowelcome/studiowelcomeplugin.cpp @@ -338,30 +338,27 @@ public: if (exampleVersion.isEmpty()) return true; - const QStringList exampleVersionParts = exampleVersion.split('.'); - const QStringList qdsVersionParts = QCoreApplication::applicationVersion().split('.'); + // Split versions into parts (major, minor, patch) + QStringList qdsVersionParts = QCoreApplication::applicationVersion().split('.'); + QStringList exampleVersionParts = exampleVersion.split('.'); - QList exampleVerInts; - QList qdsVerInts; - for (const QString &part : exampleVersionParts) - exampleVerInts.append(part.toInt()); + // Fill missing parts with zeros + while (qdsVersionParts.size() < 3) + qdsVersionParts.append("0"); - for (const QString &part : qdsVersionParts) - qdsVerInts.append(part.toInt()); + while (exampleVersionParts.size() < 3) + exampleVersionParts.append("0"); - // pad zeros so both lists are same size - while (qdsVerInts.size() < exampleVerInts.size()) - qdsVerInts.append(0); + int qdsMajor = qdsVersionParts.at(0).toInt(); + int qdsMinor = qdsVersionParts.at(1).toInt(); + int qdsPatch = qdsVersionParts.at(2).toInt(); - while (exampleVerInts.size() < qdsVerInts.size()) - exampleVerInts.append(0); + int exMajor = exampleVersionParts.at(0).toInt(); + int exMinor = exampleVersionParts.at(1).toInt(); + int exPatch = exampleVersionParts.at(2).toInt(); - for (int i = 0; i < qdsVerInts.size(); ++i) { - if (exampleVerInts[i] < qdsVerInts[i]) - return false; - } - - return true; + return QT_VERSION_CHECK(exMajor, exMinor, exPatch) + <= QT_VERSION_CHECK(qdsMajor, qdsMinor, qdsPatch); } public slots: From 934a15b16e11fb7a37e4b8c29a9e97a184336fc7 Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Tue, 28 May 2024 08:55:14 +0300 Subject: [PATCH 153/154] QmlDesigner: Make material editor toolbar fixed on the view Task-number: QDS-12850 Change-Id: I230478e175e58180fb462943b3eb936f883fcf52 Reviewed-by: Mahmoud Badri --- .../MaterialEditorPane.qml | 82 +++++++++++-------- .../MaterialEditorTopSection.qml | 8 -- 2 files changed, 48 insertions(+), 42 deletions(-) diff --git a/share/qtcreator/qmldesigner/materialEditorQmlSources/MaterialEditorPane.qml b/share/qtcreator/qmldesigner/materialEditorQmlSources/MaterialEditorPane.qml index dde7ecd0dc2..2d7b82a751f 100644 --- a/share/qtcreator/qmldesigner/materialEditorQmlSources/MaterialEditorPane.qml +++ b/share/qtcreator/qmldesigner/materialEditorQmlSources/MaterialEditorPane.qml @@ -4,8 +4,8 @@ import QtQuick import HelperWidgets -PropertyEditorPane { - id: itemPane +Item { + id: root width: 420 height: 420 @@ -34,46 +34,60 @@ PropertyEditorPane { topSection.previewModel = model } - MaterialEditorTopSection { - id: topSection + MaterialEditorToolBar { + id: toolbar - onToolBarAction: (action) => itemPane.toolBarAction(action) - onPreviewEnvChanged: itemPane.previewEnvChanged(previewEnv) - onPreviewModelChanged: itemPane.previewModelChanged(previewModel) + width: parent.width + + onToolBarAction: (action) => root.toolBarAction(action) } - Item { width: 1; height: 10 } + PropertyEditorPane { + id: itemPane - DynamicPropertiesSection { - propertiesModel: MaterialEditorDynamicPropertiesModel {} - } + anchors.top: toolbar.bottom + anchors.bottom: parent.bottom + width: parent.width - Loader { - id: specificsTwo + clip: true - property string theSource: specificQmlData + MaterialEditorTopSection { + id: topSection - anchors.left: parent.left - anchors.right: parent.right - visible: specificsTwo.theSource !== "" - sourceComponent: specificQmlComponent + onPreviewEnvChanged: root.previewEnvChanged(previewEnv) + onPreviewModelChanged: root.previewModelChanged(previewModel) + } - onTheSourceChanged: { - specificsTwo.active = false - specificsTwo.active = true + DynamicPropertiesSection { + propertiesModel: MaterialEditorDynamicPropertiesModel {} + } + + Loader { + id: specificsTwo + + property string theSource: specificQmlData + + width: parent.width + visible: specificsTwo.theSource !== "" + sourceComponent: specificQmlComponent + + onTheSourceChanged: { + specificsTwo.active = false + specificsTwo.active = true + } + } + + Item { // spacer + width: 1 + height: 10 + visible: specificsTwo.visible + } + + Loader { + id: specificsOne + anchors.left: parent.left + anchors.right: parent.right + source: specificsUrl } } - - Item { - width: 1 - height: 10 - visible: specificsTwo.visible - } - - Loader { - id: specificsOne - anchors.left: parent.left - anchors.right: parent.right - source: specificsUrl - } } diff --git a/share/qtcreator/qmldesigner/materialEditorQmlSources/MaterialEditorTopSection.qml b/share/qtcreator/qmldesigner/materialEditorQmlSources/MaterialEditorTopSection.qml index 1879ea177c6..e5bf6a757bf 100644 --- a/share/qtcreator/qmldesigner/materialEditorQmlSources/MaterialEditorTopSection.qml +++ b/share/qtcreator/qmldesigner/materialEditorQmlSources/MaterialEditorTopSection.qml @@ -11,8 +11,6 @@ import StudioTheme as StudioTheme Column { id: root - signal toolBarAction(int action) - property string previewEnv property string previewModel property StudioTheme.ControlStyle buttonStyle: StudioTheme.ViewBarButtonStyle { @@ -37,12 +35,6 @@ Column { anchors.left: parent.left anchors.right: parent.right - MaterialEditorToolBar { - width: root.width - - onToolBarAction: (action) => root.toolBarAction(action) - } - Item { width: 1; height: 10 } // spacer From ed771ace4adb83e4df3b44665f48e11985e6b7c3 Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Tue, 28 May 2024 09:28:21 +0300 Subject: [PATCH 154/154] QmlDesigner: Enable docking a header in PropertyEditorPane A header component is defined in PropertyEditorPane. This header can be docked to the top. A header is defined for MaterialEditorPane Task-number: QDS-12851 Change-Id: Ie5393f917803241da1f286bd05c226fd055b1174 Reviewed-by: Mahmoud Badri --- .../MaterialEditorPane.qml | 12 ++-- .../MaterialEditorTopSection.qml | 21 +++++- .../HelperWidgets/PropertyEditorPane.qml | 71 ++++++++++++++++--- 3 files changed, 85 insertions(+), 19 deletions(-) diff --git a/share/qtcreator/qmldesigner/materialEditorQmlSources/MaterialEditorPane.qml b/share/qtcreator/qmldesigner/materialEditorQmlSources/MaterialEditorPane.qml index 2d7b82a751f..0ab7e59d01f 100644 --- a/share/qtcreator/qmldesigner/materialEditorQmlSources/MaterialEditorPane.qml +++ b/share/qtcreator/qmldesigner/materialEditorQmlSources/MaterialEditorPane.qml @@ -17,21 +17,21 @@ Item { // invoked from C++ to refresh material preview image function refreshPreview() { - topSection.refreshPreview() + itemPane.headerItem.refreshPreview() } // Called from C++ to close context menu on focus out function closeContextMenu() { - topSection.closeContextMenu() + itemPane.headerItem.closeContextMenu() Controller.closeContextMenu() } // Called from C++ to initialize preview menu checkmarks function initPreviewData(env, model) { - topSection.previewEnv = env; - topSection.previewModel = model + itemPane.headerItem.previewEnv = env + itemPane.headerItem.previewModel = model } MaterialEditorToolBar { @@ -51,9 +51,7 @@ Item { clip: true - MaterialEditorTopSection { - id: topSection - + headerComponent: MaterialEditorTopSection { onPreviewEnvChanged: root.previewEnvChanged(previewEnv) onPreviewModelChanged: root.previewModelChanged(previewModel) } diff --git a/share/qtcreator/qmldesigner/materialEditorQmlSources/MaterialEditorTopSection.qml b/share/qtcreator/qmldesigner/materialEditorQmlSources/MaterialEditorTopSection.qml index e5bf6a757bf..d60c98933b6 100644 --- a/share/qtcreator/qmldesigner/materialEditorQmlSources/MaterialEditorTopSection.qml +++ b/share/qtcreator/qmldesigner/materialEditorQmlSources/MaterialEditorTopSection.qml @@ -13,6 +13,9 @@ Column { property string previewEnv property string previewModel + + property real __horizontalSpacing: 5 + property StudioTheme.ControlStyle buttonStyle: StudioTheme.ViewBarButtonStyle { //This is how you can override stuff from the control styles controlSize: Qt.size(previewOptions.width, previewOptions.width) @@ -35,8 +38,7 @@ Column { anchors.left: parent.left anchors.right: parent.right - Item { width: 1; height: 10 } // spacer - + Item { width: 1; height: 5 } // spacer StudioControls.Menu { id: modelMenu @@ -121,6 +123,19 @@ Column { width: parent.width height: previewRect.height + StudioControls.AbstractButton { + id: pinButton + + x: root.__horizontalSpacing + + style: root.buttonStyle + iconSize: StudioTheme.Values.bigFont + buttonIcon: pinButton.checked ? StudioTheme.Constants.pin : StudioTheme.Constants.unpin + checkable: true + checked: itemPane.headerDocked + onCheckedChanged: itemPane.headerDocked = pinButton.checked + } + Rectangle { id: previewRect anchors.horizontalCenter: parent.horizontalCenter @@ -145,6 +160,7 @@ Column { height: previewRect.height anchors.top: previewRect.top anchors.left: previewRect.right + anchors.leftMargin: root.__horizontalSpacing Column { anchors.horizontalCenter: parent.horizontalCenter @@ -164,7 +180,6 @@ Column { } } } - } HelperWidgets.Section { diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/PropertyEditorPane.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/PropertyEditorPane.qml index 7046ca48e14..309d815d7aa 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/PropertyEditorPane.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/PropertyEditorPane.qml @@ -19,30 +19,83 @@ Rectangle { default property alias content: mainColumn.children property alias scrollView: mainScrollView + property bool headerDocked: false + readonly property Item headerItem: headerDocked ? dockedHeaderLoader.item : undockedHeaderLoader.item + + property Component headerComponent: null + // Called from C++ to close context menu on focus out function closeContextMenu() { Controller.closeContextMenu() } + Loader { + id: dockedHeaderLoader + + anchors.top: itemPane.top + z: parent.z + 1 + height: item ? item.implicitHeight : 0 + width: parent.width + + active: itemPane.headerDocked + sourceComponent: itemPane.headerComponent + + HeaderBackground{} + } + MouseArea { anchors.fill: parent - onClicked: forceActiveFocus() + onClicked: itemPane.forceActiveFocus() } HelperWidgets.ScrollView { id: mainScrollView - //clip: true - anchors.fill: parent + + clip: true + anchors { + top: dockedHeaderLoader.bottom + bottom: itemPane.bottom + left: itemPane.left + right: itemPane.right + } interactive: !Controller.contextMenuOpened - Column { - id: mainColumn - y: -1 - width: itemPane.width + ColumnLayout { + spacing: 2 + width: mainScrollView.width - onWidthChanged: StudioTheme.Values.responsiveResize(itemPane.width) - Component.onCompleted: StudioTheme.Values.responsiveResize(itemPane.width) + Loader { + id: undockedHeaderLoader + + Layout.fillWidth: true + Layout.preferredHeight: item ? item.implicitHeight : 0 + + active: !itemPane.headerDocked + sourceComponent: itemPane.headerComponent + + HeaderBackground{} + } + + Column { + id: mainColumn + + Layout.fillWidth: true + + onWidthChanged: StudioTheme.Values.responsiveResize(itemPane.width) + Component.onCompleted: StudioTheme.Values.responsiveResize(itemPane.width) + } } } + + component HeaderBackground: Rectangle { + anchors.fill: parent + anchors.leftMargin: -StudioTheme.Values.border + anchors.rightMargin: -StudioTheme.Values.border + z: parent.z - 1 + + color: StudioTheme.Values.themeToolbarBackground + border.color: StudioTheme.Values.themePanelBackground + border.width: StudioTheme.Values.border + } }

  • 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�Rm#(D1^haUtlLOc@JYyz5 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 literal 0 HcmV?d00001 diff --git a/doc/qtdesignstudio/images/studio-effects-show-shadow-behind.webp b/doc/qtdesignstudio/images/studio-effects-show-shadow-behind.webp new file mode 100644 index 0000000000000000000000000000000000000000..5b7411bd2c080da4b12e7db4c1c68fcd5a3e369d GIT binary patch literal 18410 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 HC=?k0HS81; literal 0 HcmV?d00001 diff --git a/doc/qtdesignstudio/images/studio-effects.webp b/doc/qtdesignstudio/images/studio-effects.webp new file mode 100644 index 0000000000000000000000000000000000000000..1910dc40afba9231040eaeb042aea9e32dbef8cf GIT binary patch literal 8156 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 GiVOgdWM~lp literal 0 HcmV?d00001 diff --git a/doc/qtdesignstudio/src/qtdesignstudio-toc.qdoc b/doc/qtdesignstudio/src/qtdesignstudio-toc.qdoc index 3ca30792af9..b65c116d87c 100644 --- a/doc/qtdesignstudio/src/qtdesignstudio-toc.qdoc +++ b/doc/qtdesignstudio/src/qtdesignstudio-toc.qdoc @@ -74,8 +74,9 @@ \li \l{UI Controls} \li \l{Lists and Other Data Models} \li \l{2D Effects} + \li \l{Design Effects} \li \l{Logic Helpers} - \li \l Animations + \li \l{Animations} \li \l{3D Views} \li \l{Node} \li \l{Group} diff --git a/doc/qtdesignstudio/src/qtquickdesigner-components/qtdesignstudio-logic-helpers.qdoc b/doc/qtdesignstudio/src/qtquickdesigner-components/qtdesignstudio-logic-helpers.qdoc index c928fff58c6..72f57f0c174 100644 --- a/doc/qtdesignstudio/src/qtquickdesigner-components/qtdesignstudio-logic-helpers.qdoc +++ b/doc/qtdesignstudio/src/qtquickdesigner-components/qtdesignstudio-logic-helpers.qdoc @@ -3,7 +3,7 @@ /*! \page quick-logic-helpers.html - \previouspage quick-2d-effects.html + \previouspage quick-design-effects.html \nextpage quick-animations.html \title Logic Helpers diff --git a/doc/qtdesignstudio/src/qtquickdesigner-components/qtdesignstudio-property-visual-effects.qdoc b/doc/qtdesignstudio/src/qtquickdesigner-components/qtdesignstudio-property-visual-effects.qdoc new file mode 100644 index 00000000000..3530d4da645 --- /dev/null +++ b/doc/qtdesignstudio/src/qtquickdesigner-components/qtdesignstudio-property-visual-effects.qdoc @@ -0,0 +1,136 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only + +/*! + \page quick-design-effects.html + \previouspage quick-2d-effects.html + \nextpage quick-logic-helpers.html + + \title Design Effects + + \QDS provides a set of effects in the \uicontrol Properties view that you can apply + to the components. + + \image studio-effects.webp "Design Effects in the Properties view" + + \note This feature is a \e Beta release. + + The available set of Design Effects applies to the \QDS components: + \list + \li \l {Applying Layer Blur on a Component} {Layer Blur} + \li \l {Applying Background Blur on a Component} {Background Blur} + \li \l {Applying Drop Shadow on a Component} {Drop Shadow} + \li \l {Applying Inner Shadow on a Component} {Inner Shadow} + \endlist + + \section1 Applying Layer Blur on a Component + + Use \uicontrol {Layer Blur} to make a component blurry. To apply \uicontrol {Layer Blur} + to a component, follow these steps: + \list 1 + \li Select the component in the \uicontrol 2D or \uicontrol Navigator view. + \li Go to the \uicontrol {Properties} view > \uicontrol Effects + and select \uicontrol {Add Effects}. + \li Go to \uicontrol {Layer Blur} and enter the level of blurring you need + in \uicontrol {Blur}. + + \image studio-effects-layer-blur.webp "Layer Blur Effects in Properties view" + \endlist + \note The level of \uicontrol {Layer Blur} is adjustable between zero and one hundred. \br + To remove the applied \uicontrol {Layer Blur}, select the component, + then go to \uicontrol {Properties} view > \uicontrol {Layer Blur} + > \uicontrol {Remove Effects}. This also removes all the other effects + applied to the component. + + \section1 Applying Background Blur on a Component + + Apply \uicontrol {Background Blur} to a component when you want to blur a selected + component behind it. There are a few essential conditions you should consider. + \list + \li Make the component partially transparent. With solid color, + the background component is not visible. + \li Use a solid color on the background component. On + a transparent background component the \uicontrol {Background Blur} does not + function properly. + \note Currently, the \uicontrol {Background Blur} functions on top of only one selected + background component. All the other components ignore the blurring. + \endlist + + After fulfilling the above conditions, follow the next steps to apply the + \uicontrol {Background Blur} on a component. + + \list 1 + \li Select the component in the \uicontrol 2D or \uicontrol Navigator view. + \li Go to the \uicontrol {Properties} view > \uicontrol Effects + and select \uicontrol {Add Effects}. + \li Go to \uicontrol {Background Blur} and enter the level of blurring you need + in \uicontrol {Blur}. + \li In the \uicontrol Background dropdown menu, select another + components as the background component. + \li Drag the component on top of the background component. The area from the + component covering the component selected as \uicontrol Background gets + blurred. However, any other component behind the component doesn't blur. + \endlist + + \image studio-effects-background-blur.webp "Applying Background Blur" + + \section1 Applying Drop Shadow on a Component + + Shadows can either fall outside or inside the component. + The shadow that falls outside is a drop shadow. To apply + a \uicontrol {Drop Shadow} to a component, follow the instructions below. + + \list 1 + \li Select the component in the \uicontrol 2D or \uicontrol Navigator view. + \li Go to the \uicontrol {Properties} view > \uicontrol Effects + and select \uicontrol {Add Effects}. + \endlist + + This adds the default drop shadow to the component. To adjust this shadow, + follow these instructions. + + \list 1 + \li Select the component and go to the \uicontrol Properties view > \uicontrol Effects. + Then, select \inlineimage icons/particle-play.png next to the shadow type + selector dropdown menu. + \li Adjust \uicontrol Blur, \uicontrol Offset, \uicontrol Spread, and \uicontrol Color + to shape the shadow. + \endlist + + \image studio-effects-drop-shadow.webp "Drop Shadow Effects in Properties view" + + \note To stack multiple shadows, select \uicontrol {Add Shadow Effect} from the + \uicontrol Properties view. After adding multiple shadows, you can adjust them + separately from their expandable menu. + + The \uicontrol {Show behind} feature in \uicontrol {Drop Shadow} only works when + the component is transparent. To use it: + + \list 1 + \li Select the component with a drop shadow. + \li Go to the \uicontrol Properties view > \uicontrol Effects. + Then, select \inlineimage icons/particle-play.png next to the shadow type + selector dropdown menu. + \li Select \uicontrol {Show Behind}. + + \image studio-effects-show-shadow-behind.webp "Show drop shadow behind the component" + \endlist + + \section1 Applying Inner Shadow on a Component + + \uicontrol {Inner shadow} works inside a component. To apply \uicontrol {Inner Shadow}, + do the following: + + \list 1 + \li Select the component in the \uicontrol 2D or \uicontrol Navigator view. + \li Go to the \uicontrol {Properties} view > \uicontrol Effects + and select \uicontrol {Add Effects}. + \li From the dropdown menu, select \uicontrol {Inner Shadow}. + \li Adjust \uicontrol Blur, \uicontrol Offset, \uicontrol Spread, and \uicontrol Color + to shape the shadow. + + \image studio-effects-inner-shadow.webp "Inner shadow of the component" + \endlist + + +*/ diff --git a/doc/qtdesignstudio/src/qtquickdesigner-components/qtdesignstudio-visual-effects.qdoc b/doc/qtdesignstudio/src/qtquickdesigner-components/qtdesignstudio-visual-effects.qdoc index 9cff12750c0..52ef110a626 100644 --- a/doc/qtdesignstudio/src/qtquickdesigner-components/qtdesignstudio-visual-effects.qdoc +++ b/doc/qtdesignstudio/src/qtquickdesigner-components/qtdesignstudio-visual-effects.qdoc @@ -4,7 +4,7 @@ /*! \page quick-2d-effects.html \previouspage quick-data-models.html - \nextpage quick-logic-helpers.html + \nextpage quick-property-effects.html \title 2D Effects From 16d9535c8d164f22db0d54c82395caf15429bd09 Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Tue, 14 May 2024 18:00:03 +0300 Subject: [PATCH 108/154] QmlDesigner: Hide content lib user when a warning message is shown Fixes: QDS-12679 Change-Id: I5cc673eb24f6479a3cb8e876e0f31a4d2d5cb658 Reviewed-by: Shrief Gabr Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Reviewed-by: Miikka Heikkinen --- .../contentLibraryQmlSource/ContentLibraryUserView.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryUserView.qml b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryUserView.qml index f7a210f6afb..cced9e5f095 100644 --- a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryUserView.qml +++ b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryUserView.qml @@ -81,7 +81,7 @@ HelperWidgets.ScrollView { bottomPadding: StudioTheme.Values.sectionPadding caption: categoryName - visible: categoryVisible + visible: categoryVisible && infoText.text === "" category: "ContentLib_User" function expandSection() { From 379933cb3969f4b9500632dd6fa023b0242a8b77 Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Tue, 14 May 2024 19:32:54 +0300 Subject: [PATCH 109/154] QmlDesigner: Hide bundles from the components view Also few relevant tweaks. Fixes: QDS-12680 Change-Id: I3d552d9de2d92c0501c7b05d626cad84e3688dc9 Reviewed-by: Miikka Heikkinen --- .../itemlibrary/itemlibrarymodel.cpp | 76 +++++++++---------- .../designercore/generatedcomponentutils.cpp | 11 +++ .../designercore/generatedcomponentutils.h | 2 + .../metainfo/subcomponentmanager.cpp | 5 +- 4 files changed, 52 insertions(+), 42 deletions(-) diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp index ef06b692468..8ef9512e26a 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp @@ -313,54 +313,54 @@ void ItemLibraryModel::update(Model *model) beginResetModel(); clearSections(); - GeneratedComponentUtils compUtils = QmlDesignerPlugin::instance()->documentManager() - .generatedComponentUtils(); + const QString projectName = DocumentManager::currentProjectName(); + + auto compUtils = QmlDesignerPlugin::instance()->documentManager().generatedComponentUtils(); - const QString compBundlePrefix = compUtils.componentBundlesTypePrefix(); QStringList excludedImports { - compBundlePrefix + '.' + QLatin1String(Constants::COMPONENT_BUNDLES_MATERIAL_BUNDLE_TYPE), - compBundlePrefix + '.' + QLatin1String(Constants::COMPONENT_BUNDLES_EFFECT_BUNDLE_TYPE), - compBundlePrefix + '.' + QLatin1String(Constants::OLD_COMPONENT_BUNDLES_MATERIAL_BUNDLE_TYPE), - compBundlePrefix + '.' + QLatin1String(Constants::OLD_COMPONENT_BUNDLES_EFFECT_BUNDLE_TYPE) + projectName, + compUtils.materialsBundleType(), + compUtils.effectsBundleType(), + compUtils.userMaterialsBundleType(), + compUtils.user3DBundleType(), + compUtils.userEffectsBundleType() }; // create import sections - const QString projectName = DocumentManager::currentProjectName(); const Imports usedImports = model->usedImports(); QHash importHash; for (const Import &import : model->imports()) { - if (import.url() != projectName) { - if (excludedImports.contains(import.url()) - || import.url().startsWith(compUtils.composedEffectsTypePrefix())) { - continue; - } - bool addNew = true; - bool isQuick3DAsset = import.url().startsWith(compUtils.import3dTypePrefix()); - QString importUrl = import.url(); - if (isQuick3DAsset) - importUrl = ItemLibraryImport::quick3DAssetsTitle(); - else if (import.isFileImport()) - importUrl = import.toString(true, true).remove("\""); + if (excludedImports.contains(import.url()) + || import.url().startsWith(compUtils.composedEffectsTypePrefix())) { + continue; + } - ItemLibraryImport *oldImport = importHash.value(importUrl); - if (oldImport && oldImport->sectionType() == ItemLibraryImport::SectionType::Quick3DAssets - && isQuick3DAsset) { - addNew = false; // add only 1 Quick3DAssets import section - } else if (oldImport && oldImport->importEntry().url() == import.url()) { - // Retain the higher version if multiples exist - if (oldImport->importEntry().toVersion() >= import.toVersion() || import.hasVersion()) - addNew = false; - else - delete oldImport; - } + bool addNew = true; + bool isQuick3DAsset = import.url().startsWith(compUtils.import3dTypePrefix()); + QString importUrl = import.url(); + if (isQuick3DAsset) + importUrl = ItemLibraryImport::quick3DAssetsTitle(); + else if (import.isFileImport()) + importUrl = import.toString(true, true).remove("\""); - if (addNew) { - auto sectionType = isQuick3DAsset ? ItemLibraryImport::SectionType::Quick3DAssets - : ItemLibraryImport::SectionType::Default; - ItemLibraryImport *itemLibImport = new ItemLibraryImport(import, this, sectionType); - itemLibImport->setImportUsed(usedImports.contains(import)); - importHash.insert(importUrl, itemLibImport); - } + ItemLibraryImport *oldImport = importHash.value(importUrl); + if (oldImport && oldImport->sectionType() == ItemLibraryImport::SectionType::Quick3DAssets + && isQuick3DAsset) { + addNew = false; // add only 1 Quick3DAssets import section + } else if (oldImport && oldImport->importEntry().url() == import.url()) { + // Retain the higher version if multiples exist + if (oldImport->importEntry().toVersion() >= import.toVersion() || import.hasVersion()) + addNew = false; + else + delete oldImport; + } + + if (addNew) { + auto sectionType = isQuick3DAsset ? ItemLibraryImport::SectionType::Quick3DAssets + : ItemLibraryImport::SectionType::Default; + ItemLibraryImport *itemLibImport = new ItemLibraryImport(import, this, sectionType); + itemLibImport->setImportUsed(usedImports.contains(import)); + importHash.insert(importUrl, itemLibImport); } } diff --git a/src/plugins/qmldesigner/designercore/generatedcomponentutils.cpp b/src/plugins/qmldesigner/designercore/generatedcomponentutils.cpp index d9e4480ef53..a04713a884a 100644 --- a/src/plugins/qmldesigner/designercore/generatedcomponentutils.cpp +++ b/src/plugins/qmldesigner/designercore/generatedcomponentutils.cpp @@ -175,6 +175,17 @@ bool GeneratedComponentUtils::isComposedEffectPath(const QString &path) const + QLatin1String(Constants::COMPOSED_EFFECTS_TYPE)); } +bool GeneratedComponentUtils::isBundlePath(const QString &path) const +{ + return path.contains(componentBundlesTypePrefix().replace('.', '/')); +} + +bool GeneratedComponentUtils::isGeneratedPath(const QString &path) const +{ + return path.startsWith(generatedComponentsPath().toFSPathString()); +} + + QString GeneratedComponentUtils::generatedComponentTypePrefix() const { Utils::FilePath basePath = generatedComponentsPath(); diff --git a/src/plugins/qmldesigner/designercore/generatedcomponentutils.h b/src/plugins/qmldesigner/designercore/generatedcomponentutils.h index cc51b5f8cbc..ceddb405a34 100644 --- a/src/plugins/qmldesigner/designercore/generatedcomponentutils.h +++ b/src/plugins/qmldesigner/designercore/generatedcomponentutils.h @@ -27,6 +27,8 @@ public: bool isImport3dPath(const QString &path) const; bool isComposedEffectPath(const QString &path) const; + bool isBundlePath(const QString &path) const; + bool isGeneratedPath(const QString &path) const; QString generatedComponentTypePrefix() const; QString import3dTypePrefix() const; diff --git a/src/plugins/qmldesigner/designercore/metainfo/subcomponentmanager.cpp b/src/plugins/qmldesigner/designercore/metainfo/subcomponentmanager.cpp index 16d9217f6a6..29a093f199e 100644 --- a/src/plugins/qmldesigner/designercore/metainfo/subcomponentmanager.cpp +++ b/src/plugins/qmldesigner/designercore/metainfo/subcomponentmanager.cpp @@ -345,11 +345,8 @@ void SubComponentManager::unregisterQmlFile(const QFileInfo &fileInfo, const QSt void SubComponentManager::registerQmlFile(const QFileInfo &fileInfo, const QString &qualifier, bool addToLibrary) { - if (!addToLibrary || !model() - || m_componentUtils.isImport3dPath(fileInfo.path()) - || m_componentUtils.isComposedEffectPath(fileInfo.path())) { + if (!addToLibrary || !model() || m_componentUtils.isGeneratedPath(fileInfo.path())) return; - } QString componentName = fileInfo.baseName(); const QString baseComponentName = componentName; From a7913d543028b297191743c38db55995e6aeb5e3 Mon Sep 17 00:00:00 2001 From: Henning Gruendl Date: Wed, 15 May 2024 16:45:34 +0200 Subject: [PATCH 110/154] QmlDesigner: Update EffectsSection tooltips Change-Id: Ib0cf243f84d125ab488ff4f597bbc4dd1637c1d3 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Mats Honkamaa Reviewed-by: --- .../QtQuick/EffectsSection.qml | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/EffectsSection.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/EffectsSection.qml index c7121127d5e..3e9a79f8e04 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/EffectsSection.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/EffectsSection.qml @@ -78,7 +78,6 @@ Section { } PropertyLabel { - text: qsTr("Visible") tooltip: qsTr("Toggles the visibility of visual effects on the component.") visible: root.hasDesignerEffect @@ -260,8 +259,8 @@ Section { PropertyLabel { text: qsTr("Blur") tooltip: qsTr("Sets the intensity of blur on the selected background component.\n" - + "The foreground component should be transparent, and the background " - + "component should be solid.") + + "The foreground component should be transparent, and the background " + + "component should be opaque.") } SecondColumnLayout { @@ -402,7 +401,8 @@ Section { PropertyLabel { text: qsTr("Blur") - tooltip: qsTr("Sets the intensity of the component shadow.") + tooltip: qsTr("Sets the softness of the component shadow. A larger value" + + " causes the edges of the shadow to appear more blurry.") } SecondColumnLayout { @@ -419,7 +419,9 @@ Section { PropertyLabel { text: qsTr("Spread") - tooltip: qsTr("Resizes the base shadow of the component by pixels.") + tooltip: modelNodeBackend.isInstanceOf("Rectangle") + ? qsTr("Resizes the base shadow of the component by pixels.") + : qsTr("Only supported for Rectangles.") enabled: modelNodeBackend.isInstanceOf("Rectangle") } From 92120273b1d9fa609207584cb0e0eeb17b181b1f Mon Sep 17 00:00:00 2001 From: Johanna Vanhatapio Date: Wed, 15 May 2024 17:20:09 +0300 Subject: [PATCH 111/154] Doc: Add a missing qdoc command Change-Id: I36567e0e3da15d99cf8fe9eac887112ad20b032d Reviewed-by: Mats Honkamaa --- .../src/qtquick3d-editor/qtdesignstudio-3d-view.qdoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-view.qdoc b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-view.qdoc index 1ada3625ff3..db9961e31d7 100644 --- a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-view.qdoc +++ b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-view.qdoc @@ -17,7 +17,7 @@ To create a project with many complex effects, use the \uicontrol {Extended 3D} preset that creates a project with an \uicontrol {Extended View3D} component. - The extended 3D view includes an {Extended Scene Environment} + The extended 3D view includes an \uicontrol{Extended Scene Environment} component that enables using various effects by defining them as properties. \note The extended 3D view is available in projects created with Qt 6.5 From 51a27f3a124931b738d13693dafac231a275a5d6 Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Wed, 15 May 2024 16:48:28 +0300 Subject: [PATCH 112/154] QmlDesigner: Close the color editor popup before adding Fixes: QDS-12498 Change-Id: Id3c565ffb9fca7f186ebeb0cf8051194c7944d4d Reviewed-by: Mahmoud Badri Reviewed-by: Reviewed-by: Miikka Heikkinen Reviewed-by: Qt CI Patch Build Bot --- .../CollectionDetailsView.qml | 65 +++- .../ColorViewDelegate.qml | 346 ++++++------------ 2 files changed, 173 insertions(+), 238 deletions(-) diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsView.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsView.qml index 337cad2ed8c..ad721472ed7 100644 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsView.qml +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsView.qml @@ -90,7 +90,7 @@ Rectangle { property int order: Qt.AscendingOrder onClicked: { order = (order == Qt.AscendingOrder) ? Qt.DescendingOrder : Qt.AscendingOrder; - tableView.closeEditor() + tableView.closeEditors() tableView.model.sort(-1, order) } } @@ -173,7 +173,7 @@ Rectangle { StudioControls.MenuItem { text: qsTr("Sort Ascending") onTriggered: { - tableView.closeEditor() + tableView.closeEditors() tableView.model.sort(headerMenu.clickedHeaderIndex, Qt.AscendingOrder) } } @@ -181,7 +181,7 @@ Rectangle { StudioControls.MenuItem { text: qsTr("Sort Descending") onTriggered: { - tableView.closeEditor() + tableView.closeEditors() tableView.model.sort(headerMenu.clickedHeaderIndex, Qt.DescendingOrder) } } @@ -235,6 +235,8 @@ Rectangle { property int targetRow property int targetColumn + property Item popupEditingItem + Layout.alignment: Qt.AlignTop + Qt.AlignLeft Layout.preferredWidth: tableView.contentWidth Layout.preferredHeight: tableView.contentHeight @@ -261,6 +263,24 @@ Rectangle { return Math.max(h, StudioTheme.Values.collectionCellMinimumHeight) } + function closePopupEditor() { + if (tableView.popupEditingItem) + tableView.popupEditingItem.closeEditor() + tableView.popupEditingItem = null + } + + function openNewPopupEditor(item, editor) { + if (tableView.popupEditingItem !== item) { + closePopupEditor() + tableView.popupEditingItem = item + } + } + + function closeEditors() { + closeEditor() + closePopupEditor() + } + function ensureRowIsVisible(row) { let rows = tableView.model.rowCount() let rowIsLoaded = tableView.isRowLoaded(row) @@ -381,7 +401,36 @@ Rectangle { Component { id: colorEditorComponent - ColorViewDelegate {} + ColorViewDelegate { + id: colorEditorItem + + readonly property color editValue: edit + readonly property color displayValue: display + property string _frontColorStr + property string _backendColorStr + + actionIndicatorVisible: false + + onColorChanged: { + _frontColorStr = colorEditorItem.color.toString() + if (_frontColorStr != _backendColorStr) + edit = colorEditorItem.color + } + + Component.onCompleted: { + colorEditorItem.color = display + } + + onDisplayValueChanged: { + _backendColorStr = colorEditorItem.displayValue.toString() + if (_frontColorStr != _backendColorStr) + colorEditorItem.color = colorEditorItem.displayValue + } + + onEditorOpened: (item, editor) => { + tableView.openNewPopupEditor(item, editor) + } + } } function resetSource() { @@ -441,13 +490,13 @@ Rectangle { } function onRowsInserted(parent, first, last) { - tableView.closeEditor() + tableView.closeEditors() tableView.model.selectRow(first) tableView.ensureRowIsVisible(first) } function onColumnsInserted(parent, first, last) { - tableView.closeEditor() + tableView.closeEditors() tableView.model.selectColumn(first) tableView.ensureColumnIsVisible(first) } @@ -503,7 +552,7 @@ Rectangle { tooltip: "Add Column" onClicked: { - tableView.closeEditor() + tableView.closeEditors() toolbar.addNewColumn() } } @@ -521,7 +570,7 @@ Rectangle { tooltip: "Add Row" onClicked: { - tableView.closeEditor() + tableView.closeEditors() toolbar.addNewRow() } } diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/ColorViewDelegate.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/ColorViewDelegate.qml index 16e55acfb43..b563b6734af 100644 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/ColorViewDelegate.qml +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/ColorViewDelegate.qml @@ -10,170 +10,59 @@ import StudioTheme as StudioTheme import StudioControls as StudioControls import QtQuickDesignerTheme import QtQuickDesignerColorPalette +import StudioHelpers -Row { - id: colorEditor +Item { + id: root - property color color - property bool supportGradient: false + property StudioTheme.ControlStyle style: StudioTheme.Values.controlStyle - property QtObject backendValue: QtObject { - property color value: edit - readonly property color editColor: edit + property alias color: colorBackend.color - function resetValue() { - if (value) - value = "" - } + property alias actionIndicatorVisible: actionIndicator.visible + property real __actionIndicatorWidth: root.style.actionIndicatorSize.width + property real __actionIndicatorHeight: root.style.actionIndicatorSize.height - onValueChanged: { - if (editColor !== value) - edit = value - } + property alias showHexTextField: hexTextField.visible + + readonly property real padding: 2 + readonly property real innerItemsHeight: root.height - 2 * root.padding + + width: root.style.controlSize.width + height: root.style.controlSize.height + + clip: true + + signal editorOpened(var item, var editorPopup) + + function closeEditor() { + loader.close() } - property variant value: { - if (!colorEditor.backendValue || !colorEditor.backendValue.value) - return "white" // default color for Rectangle - - if (colorEditor.isVector3D) { - return Qt.rgba(colorEditor.backendValue.value.x, - colorEditor.backendValue.value.y, - colorEditor.backendValue.value.z, 1) - } - - return colorEditor.backendValue.value + ColorBackend { + id: colorBackend } - property alias gradientPropertyName: popupDialog.gradientPropertyName - - property alias gradientThumbnail: gradientThumbnail - property alias shapeGradientThumbnail: shapeGradientThumbnail - - property bool shapeGradients: false - property bool isVector3D: false - property color originalColor - - property bool __block: false - - function resetShapeColor() { - colorEditor.backendValue.resetValue() - } - - function writeColor() { - if (colorEditor.isVector3D) { - colorEditor.backendValue.value = Qt.vector3d(colorEditor.color.r, - colorEditor.color.g, - colorEditor.color.b) - } else { - colorEditor.backendValue.value = colorEditor.color - } - } - - function initEditor() { - colorEditor.syncColor() - } - - // Syncing color from backend to frontend and block reflection - function syncColor() { - colorEditor.__block = true - colorEditor.color = colorEditor.value - hexTextField.syncColor() - colorEditor.__block = false - } - - Connections { - id: backendConnection - - target: colorEditor - - function onValueChanged() { - if (popupDialog.isSolid()) - colorEditor.syncColor() - } - - function onBackendValueChanged() { - if (popupDialog.isSolid()) - colorEditor.syncColor() - } - } - - Timer { - id: colorEditorTimer - - repeat: false - interval: 100 - running: false - onTriggered: { - backendConnection.enabled = false - colorEditor.writeColor() - hexTextField.syncColor() - backendConnection.enabled = true - } - } - - onColorChanged: { - if (colorEditor.__block) - return - - if (!popupDialog.isInValidState) - return - - popupDialog.commitToGradient() - - // Delay setting the color to keep ui responsive - if (popupDialog.isSolid()) - colorEditorTimer.restart() + StudioControls.ActionIndicator { + id: actionIndicator + style: root.style + __parentControl: root + x: root.padding + y: root.padding + width: actionIndicator.visible ? root.__actionIndicatorWidth : 0 + height: actionIndicator.visible ? root.__actionIndicatorHeight : 0 } Rectangle { id: preview - - implicitWidth: StudioTheme.Values.twoControlColumnWidth - implicitHeight: StudioTheme.Values.height - color: colorEditor.color - border.color: StudioTheme.Values.themeControlOutline - border.width: StudioTheme.Values.border - - Rectangle { - id: gradientThumbnail - - anchors.fill: parent - anchors.margins: StudioTheme.Values.border - visible: !popupDialog.isSolid() - && !colorEditor.shapeGradients - && popupDialog.isLinearGradient() - } - - Shape { - id: shape - - anchors.fill: parent - anchors.margins: StudioTheme.Values.border - visible: !popupDialog.isSolid() && colorEditor.shapeGradients - - ShapePath { - id: shapeGradientThumbnail - - startX: shape.x - 1 - startY: shape.y - 1 - strokeWidth: -1 - strokeColor: "green" - - PathLine { - x: shape.x - 1 - y: shape.height - } - PathLine { - x: shape.width - y: shape.height - } - PathLine { - x: shape.width - y: shape.y - 1 - } - } - } + x: root.padding + actionIndicator.width + y: root.padding + z: previewMouseArea.containsMouse ? 10 : 0 + implicitWidth: root.innerItemsHeight + implicitHeight: root.innerItemsHeight + color: root.color + border.color: previewMouseArea.containsMouse ? root.style.border.hover : root.style.border.idle + border.width: root.style.borderWidth Image { anchors.fill: parent @@ -183,114 +72,111 @@ Row { } MouseArea { + id: previewMouseArea anchors.fill: parent + hoverEnabled: true onClicked: { - popupDialog.visibility ? popupDialog.close() : popupDialog.open() - forceActiveFocus() + loader.toggle() + previewMouseArea.forceActiveFocus() } } - StudioControls.PopupDialog { - id: popupDialog - - property bool isInValidState: loader.active ? popupDialog.loaderItem.isInValidState : true - property QtObject loaderItem: loader.item - property string gradientPropertyName - - keepOpen: loader.item?.eyeDropperActive ?? false - - width: 260 - - function commitToGradient() { - if (!loader.active) - return - - if (colorEditor.supportGradient && popupDialog.loaderItem.gradientModel.hasGradient) { - var hexColor = convertColorToString(colorEditor.color) - hexTextField.text = hexColor - colorEditor.backendValue.value = hexColor - popupDialog.loaderItem.commitGradientColor() - } - } - - function isSolid() { - if (!loader.active) - return true - - return popupDialog.loaderItem.isSolid() - } - - function isLinearGradient(){ - if (!loader.active) - return false - - return popupDialog.loaderItem.isLinearGradient() - } + Loader { + id: loader function ensureLoader() { if (!loader.active) loader.active = true + if (loader.sourceComponent === null) + loader.sourceComponent = popupDialogComponent } function open() { - popupDialog.ensureLoader() - popupDialog.loaderItem.initEditor() - popupDialog.show(preview) + loader.ensureLoader() + loader.item.show(preview) + + if (loader.status === Loader.Ready) + loader.item.originalColor = root.color } - function determineActiveColorMode() { - if (loader.active && popupDialog.loaderItem) - popupDialog.loaderItem.determineActiveColorMode() + function close() { + if (loader.item) + loader.item.close() + } + + function toggle() { + if (loader.item) + loader.close() else - colorEditor.syncColor() + loader.open() } - Loader { - id: loader + Component { + id: popupDialogComponent - active: colorEditor.supportGradient - sourceComponent: HelperWidgets.ColorEditorPopup { - shapeGradients: colorEditor.shapeGradients - supportGradient: colorEditor.supportGradient - width: popupDialog.contentWidth + StudioControls.PopupDialog { + id: popupDialog + + property alias color: popup.color + property alias originalColor: popup.originalColor + titleBar: popup.titleBarContent + + width: 260 + + StudioControls.ColorEditorPopup { + id: popup + width: popupDialog.contentWidth + + onActivateColor: function(color) { + colorBackend.activateColor(color) + } + } + + onClosing: { + loader.sourceComponent = null + } } + } - onLoaded: { - popupDialog.loaderItem.initEditor() - popupDialog.titleBar = loader.item.titleBarContent - } + sourceComponent: null + + Binding { + target: loader.item + property: "color" + value: root.color + when: loader.status === Loader.Ready + } + + onLoaded: { + loader.item.originalColor = root.color + root.editorOpened(root, loader.item) } } } - HelperWidgets.LineEdit { + StudioControls.TextField { id: hexTextField - implicitWidth: StudioTheme.Values.twoControlColumnWidth - + StudioTheme.Values.actionIndicatorWidth - width: hexTextField.implicitWidth - enabled: popupDialog.isSolid() - writeValueManually: true + style: root.style + x: root.padding + actionIndicator.width + preview.width - preview.border.width + y: root.padding + width: root.width - hexTextField.x - 2 * root.padding + height: root.innerItemsHeight + 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 } - showTranslateCheckBox: false - showExtendedFunctionButton: false - indicatorVisible: false - onAccepted: colorEditor.color = hexTextField.text - onCommitData: { - colorEditor.color = hexTextField.text - if (popupDialog.isSolid()) - colorEditor.writeColor() - } - - function syncColor() { - hexTextField.text = colorEditor.color - } + onAccepted: colorBackend.activateColor(colorFromString(hexTextField.text)) } - - Component.onCompleted: popupDialog.determineActiveColorMode() - - onBackendValueChanged: popupDialog.determineActiveColorMode() } From ed97f8172a73cc6773bce259c95d86d388ce9131 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Jen=C3=9Fen?= Date: Fri, 17 May 2024 08:55:49 +0200 Subject: [PATCH 113/154] qds: increase version to 4.6.0 Change-Id: I5d99ba1e92399170d9fd828df573f33503ee4b12 Reviewed-by: Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Tim Jenssen --- 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 204cedffdb1..69479620f8d 100644 --- a/dist/branding/qtdesignstudio/QtCreatorIDEBranding.cmake +++ b/dist/branding/qtdesignstudio/QtCreatorIDEBranding.cmake @@ -1,6 +1,6 @@ -set(IDE_VERSION "4.5.0") # The IDE version. -set(IDE_VERSION_COMPAT "4.5.0") # The IDE Compatibility version. -set(IDE_VERSION_DISPLAY "4.5.0") # The IDE display version. +set(IDE_VERSION "4.6.0") # The IDE version. +set(IDE_VERSION_COMPAT "4.6.0") # The IDE Compatibility version. +set(IDE_VERSION_DISPLAY "4.6.0") # The IDE display version. set(IDE_COPYRIGHT_YEAR "2024") # The IDE current copyright year. set(IDE_SETTINGSVARIANT "QtProject") # The IDE settings variation. From fe7b46c28e4c20e63d9a6ab0f685b7d07a620210 Mon Sep 17 00:00:00 2001 From: Tim Jenssen Date: Fri, 17 May 2024 19:49:04 +0200 Subject: [PATCH 114/154] crashpad: fix include Change-Id: Ia5bd19db4bb05de673632fa7790b72a4824313bb Reviewed-by: Tim Jenssen --- src/tools/qml2puppet/qml2puppet/configcrashpad.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/tools/qml2puppet/qml2puppet/configcrashpad.h b/src/tools/qml2puppet/qml2puppet/configcrashpad.h index 70cbfc5f4a5..b802b809575 100644 --- a/src/tools/qml2puppet/qml2puppet/configcrashpad.h +++ b/src/tools/qml2puppet/qml2puppet/configcrashpad.h @@ -5,6 +5,7 @@ #include #if defined(ENABLE_CRASHPAD) && defined(Q_OS_WIN) +#include #define NOMINMAX #include "client/crash_report_database.h" #include "client/crashpad_client.h" From 73ce3a891feea80c2ca815821cbeea31c3f7514e Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Fri, 17 May 2024 15:59:14 +0300 Subject: [PATCH 115/154] Allow scene environment as background for non-View3D scenes For non-View3D scenes, the environment from the previous View3D is used. Fixes: QDS-12398 Change-Id: I6adc82cd0205ebe443b5834d9bb0f4906e2cd009 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Mahmoud Badri Reviewed-by: --- .../interfaces/nodeinstanceglobal.h | 3 +- .../components/edit3d/edit3dview.cpp | 144 ++++++++++++++---- .../components/edit3d/edit3dview.h | 11 ++ .../instances/nodeinstanceview.cpp | 16 +- .../qml2puppet/mockfiles/qt6/EditView3D.qml | 26 ++-- .../qml2puppet/mockfiles/qt6/SceneView3D.qml | 10 ++ .../qml2puppet/editor3d/generalhelper.cpp | 96 +++++++++++- .../qml2puppet/editor3d/generalhelper.h | 9 ++ .../qt5informationnodeinstanceserver.cpp | 8 + 9 files changed, 280 insertions(+), 43 deletions(-) diff --git a/src/libs/qmlpuppetcommunication/interfaces/nodeinstanceglobal.h b/src/libs/qmlpuppetcommunication/interfaces/nodeinstanceglobal.h index a3012c99f53..3fdd7bf6cc5 100644 --- a/src/libs/qmlpuppetcommunication/interfaces/nodeinstanceglobal.h +++ b/src/libs/qmlpuppetcommunication/interfaces/nodeinstanceglobal.h @@ -54,7 +54,8 @@ enum class View3DActionType { FlyModeToggle, EditCameraRotation, EditCameraMove, - EditCameraStopAllMoves + EditCameraStopAllMoves, + SetLastSceneEnvData }; constexpr bool isNanotraceEnabled() diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp index 7b9966d8bae..a78ff758613 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp +++ b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp @@ -20,9 +20,11 @@ #include "nodeinstanceview.h" #include "qmldesignerconstants.h" #include "qmldesignerplugin.h" +#include "qmlitemnode.h" #include "qmlvisualnode.h" #include "seekerslider.h" #include "snapconfiguration.h" +#include "variantproperty.h" #include #include @@ -235,36 +237,10 @@ void Edit3DView::updateActiveScene3D(const QVariantMap &sceneState) state.showWireframe = false; } - // Syncing background color only makes sense for children of View3D instances - bool syncValue = false; - bool syncEnabled = false; - bool desiredSyncValue = false; if (sceneState.contains(syncEnvBgKey)) - desiredSyncValue = sceneState[syncEnvBgKey].toBool(); - ModelNode checkNode = Utils3D::active3DSceneNode(this); - const bool activeSceneValid = checkNode.isValid(); - - while (checkNode.isValid()) { - if (checkNode.metaInfo().isQtQuick3DView3D()) { - syncValue = desiredSyncValue; - syncEnabled = true; - break; - } - if (checkNode.hasParentProperty()) - checkNode = checkNode.parentProperty().parentModelNode(); - else - break; - } - - if (activeSceneValid && syncValue != desiredSyncValue) { - // Update actual toolstate as well if we overrode it. - QTimer::singleShot(0, this, [this, syncValue]() { - emitView3DAction(View3DActionType::SyncEnvBackground, syncValue); - }); - } - - m_syncEnvBackgroundAction->action()->setChecked(syncValue); - m_syncEnvBackgroundAction->action()->setEnabled(syncEnabled); + m_syncEnvBackgroundAction->action()->setChecked(sceneState[syncEnvBgKey].toBool()); + else + m_syncEnvBackgroundAction->action()->setChecked(false); // Selection context change updates visible and enabled states SelectionContext selectionContext(this); @@ -273,6 +249,8 @@ void Edit3DView::updateActiveScene3D(const QVariantMap &sceneState) m_bakeLightsAction->currentContextChanged(selectionContext); syncCameraSpeedToNewView(); + + storeCurrentSceneEnvironment(); } void Edit3DView::modelAttached(Model *model) @@ -542,6 +520,21 @@ void Edit3DView::nodeRemoved(const ModelNode &, updateAlignActionStates(); } +void Edit3DView::propertiesRemoved(const QList &propertyList) +{ + maybeStoreCurrentSceneEnvironment(propertyList); +} + +void Edit3DView::bindingPropertiesChanged(const QList &propertyList, PropertyChangeFlags) +{ + maybeStoreCurrentSceneEnvironment(propertyList); +} + +void Edit3DView::variantPropertiesChanged(const QList &propertyList, PropertyChangeFlags) +{ + maybeStoreCurrentSceneEnvironment(propertyList); +} + void Edit3DView::sendInputEvent(QEvent *e) const { if (nodeInstanceView()) @@ -718,6 +711,30 @@ QPoint Edit3DView::resolveToolbarPopupPos(Edit3DAction *action) const return pos; } +template +void Edit3DView::maybeStoreCurrentSceneEnvironment(const QList &propertyList) +{ + QSet handledNodes; + QmlObjectNode sceneEnv; + for (const AbstractProperty &prop : propertyList) { + ModelNode node = prop.parentModelNode(); + const qint32 id = node.internalId(); + if (handledNodes.contains(id)) + continue; + + handledNodes.insert(id); + if (!node.metaInfo().isQtQuick3DSceneEnvironment()) + continue; + + if (!sceneEnv.isValid()) + sceneEnv = currentSceneEnv(); + if (sceneEnv == node) { + storeCurrentSceneEnvironment(); + break; + } + } +} + void Edit3DView::showContextMenu() { // If request for context menu is still pending, skip for now @@ -807,6 +824,75 @@ void Edit3DView::syncCameraSpeedToNewView() setCameraSpeedAuxData(speed, multiplier); } +QmlObjectNode Edit3DView::currentSceneEnv() +{ + PropertyName envProp{"environment"}; + ModelNode checkNode = Utils3D::active3DSceneNode(this); + while (checkNode.isValid()) { + if (checkNode.metaInfo().isQtQuick3DView3D()) { + QmlObjectNode sceneEnvNode = QmlItemNode(checkNode).bindingProperty(envProp) + .resolveToModelNode(); + if (sceneEnvNode.isValid()) + return sceneEnvNode; + break; + } + if (checkNode.hasParentProperty()) + checkNode = checkNode.parentProperty().parentModelNode(); + else + break; + } + return {}; +} + +void Edit3DView::storeCurrentSceneEnvironment() +{ + // If current active scene has scene environment, store relevant properties + QmlObjectNode sceneEnvNode = currentSceneEnv(); + if (sceneEnvNode.isValid()) { + QVariantMap lastSceneEnvData; + + auto insertPropValue = [](const PropertyName prop, const QmlObjectNode &node, + QVariantMap &map) { + if (!node.hasProperty(prop)) + return; + + map.insert(QString::fromUtf8(prop), node.modelValue(prop)); + }; + + auto insertTextureProps = [&](const PropertyName prop) { + // For now we just grab the absolute path of texture source for simplicity + if (!sceneEnvNode.hasProperty(prop)) + return; + + QmlObjectNode bindNode = QmlItemNode(sceneEnvNode).bindingProperty(prop) + .resolveToModelNode(); + if (bindNode.isValid()) { + QVariantMap props; + const PropertyName sourceProp = "source"; + if (bindNode.hasProperty(sourceProp)) { + Utils::FilePath qmlPath = Utils::FilePath::fromUrl( + model()->fileUrl()).absolutePath(); + Utils::FilePath sourcePath = Utils::FilePath::fromUrl( + bindNode.modelValue(sourceProp).toUrl()); + + sourcePath = qmlPath.resolvePath(sourcePath); + + props.insert(QString::fromUtf8(sourceProp), + sourcePath.absoluteFilePath().toUrl()); + } + lastSceneEnvData.insert(QString::fromUtf8(prop), props); + } + }; + + insertPropValue("backgroundMode", sceneEnvNode, lastSceneEnvData); + insertPropValue("clearColor", sceneEnvNode, lastSceneEnvData); + insertTextureProps("lightProbe"); + insertTextureProps("skyBoxCubeMap"); + + emitView3DAction(View3DActionType::SetLastSceneEnvData, lastSceneEnvData); + } +} + const QList &Edit3DView::splitToolStates() const { return m_splitToolStates; diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dview.h b/src/plugins/qmldesigner/components/edit3d/edit3dview.h index 918df3c6f98..a76607f003e 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dview.h +++ b/src/plugins/qmldesigner/components/edit3d/edit3dview.h @@ -8,6 +8,7 @@ #include #include +#include #include #include @@ -59,6 +60,11 @@ public: PropertyChangeFlags propertyChange) override; void nodeRemoved(const ModelNode &removedNode, const NodeAbstractProperty &parentProperty, PropertyChangeFlags propertyChange) override; + void propertiesRemoved(const QList &propertyList) override; + void bindingPropertiesChanged(const QList &propertyList, + PropertyChangeFlags propertyChange) override; + void variantPropertiesChanged(const QList &propertyList, + PropertyChangeFlags propertyChange) override; void sendInputEvent(QEvent *e) const; void edit3DViewResized(const QSize &size) const; @@ -127,9 +133,14 @@ private: void createSyncEnvBackgroundAction(); void createSeekerSliderAction(); void syncCameraSpeedToNewView(); + QmlObjectNode currentSceneEnv(); + void storeCurrentSceneEnvironment(); QPoint resolveToolbarPopupPos(Edit3DAction *action) const; + template::value>::type> + void maybeStoreCurrentSceneEnvironment(const QList &propertyList); + QPointer m_edit3DWidget; QVector m_leftActions; QVector m_rightActions; diff --git a/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp b/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp index fd8c92d04d3..c033c983316 100644 --- a/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp +++ b/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp @@ -1216,6 +1216,13 @@ CreateSceneCommand NodeInstanceView::createCreateSceneCommand() if (stateNode.isValid() && stateNode.metaInfo().isQtQuickState()) stateInstanceId = stateNode.internalId(); + QHash sceneStates = m_edit3DToolStates[model()->fileUrl()]; + QHash projectStates = m_edit3DToolStates[ + QUrl::fromLocalFile(m_externalDependencies.currentProjectDirPath())]; + const QString ptsId = "@PTS"; + if (projectStates.contains(ptsId)) + sceneStates.insert(ptsId, projectStates[ptsId]); + return CreateSceneCommand(instanceContainerList, reparentContainerList, idContainerList, @@ -1226,7 +1233,7 @@ CreateSceneCommand NodeInstanceView::createCreateSceneCommand() mockupTypesVector, model()->fileUrl(), m_externalDependencies.currentResourcePath(), - m_edit3DToolStates[model()->fileUrl()], + sceneStates, lastUsedLanguage, m_captureImageMinimumSize, m_captureImageMaximumSize, @@ -1744,7 +1751,12 @@ void NodeInstanceView::handlePuppetToCreatorCommand(const PuppetToCreatorCommand auto data = qvariant_cast(command.data()); if (data.size() == 3) { QString qmlId = data[0].toString(); - m_edit3DToolStates[model()->fileUrl()][qmlId].insert(data[1].toString(), data[2]); + QUrl mainKey; + if (qmlId == "@PTS") // Project tool state + mainKey = QUrl::fromLocalFile(m_externalDependencies.currentProjectDirPath()); + else + mainKey = model()->fileUrl(); + m_edit3DToolStates[mainKey][qmlId].insert(data[1].toString(), data[2]); } } } else if (command.type() == PuppetToCreatorCommand::Render3DView) { diff --git a/src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml b/src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml index 0e876e0d7d0..c4dc15ab5eb 100644 --- a/src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml +++ b/src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml @@ -166,7 +166,7 @@ Item { break; } } - showEditLight = !hasSceneLight; + showEditLight = !hasSceneLight && !_generalHelper.sceneHasLightProbe(sceneId); // Don't inherit camera angles from the previous scene for (let i = 0; i < 4; ++i) @@ -265,16 +265,22 @@ Item { 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; + if (_generalHelper.hasSceneEnvironmentData(sceneId)) { + 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 if (activeScene) { + _generalHelper.updateSceneEnvToLast(editViews[i].sceneEnv, + editViews[i].defaultLightProbe, + editViews[i].defaultCubeMap); } - editViews[i].sceneEnv.lightProbe = _generalHelper.sceneEnvironmentLightProbe(sceneId); - editViews[i].sceneEnv.skyBoxCubeMap = _generalHelper.sceneEnvironmentSkyBoxCubeMap(sceneId); - editViews[i].sceneEnv.clearColor = _generalHelper.sceneEnvironmentColor(sceneId); } else { editViews[i].sceneEnv.backgroundMode = SceneEnvironment.Transparent; editViews[i].sceneEnv.lightProbe = null; diff --git a/src/tools/qml2puppet/mockfiles/qt6/SceneView3D.qml b/src/tools/qml2puppet/mockfiles/qt6/SceneView3D.qml index 1de1cebd14a..82c735467ec 100644 --- a/src/tools/qml2puppet/mockfiles/qt6/SceneView3D.qml +++ b/src/tools/qml2puppet/mockfiles/qt6/SceneView3D.qml @@ -15,6 +15,8 @@ View3D { property alias perspectiveCamera: scenePerspectiveCamera property alias orthoCamera: sceneOrthoCamera property alias sceneEnv: sceneEnv + property alias defaultLightProbe: defaultLightProbe + property alias defaultCubeMap: defaultCubeMap property vector3d cameraLookAt property var selectionBoxes: [] property Node selectedNode @@ -61,6 +63,14 @@ View3D { id: sceneEnv antialiasingMode: SceneEnvironment.MSAA antialiasingQuality: SceneEnvironment.High + + Texture { + id: defaultLightProbe + } + + CubeMapTexture { + id: defaultCubeMap + } } Node { diff --git a/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp b/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp index f24bc342b91..d4f79fdabdd 100644 --- a/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp +++ b/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp @@ -6,6 +6,8 @@ #include "selectionboxgeometry.h" +#include + #include #include #include @@ -42,9 +44,17 @@ namespace QmlDesigner { namespace Internal { -const QString _globalStateId = QStringLiteral("@GTS"); // global tool state +const QString _globalStateId = QStringLiteral("@GTS"); // global tool state (within document) +const QString _projectStateId = QStringLiteral("@PTS"); // project wide tool state const QString _lastSceneIdKey = QStringLiteral("lastSceneId"); const QString _rootSizeKey = QStringLiteral("rootSize"); +const QString _lastSceneEnvKey = QStringLiteral("lastSceneEnv"); + +const QString _lightProbeProp = QStringLiteral("lightProbe"); +const QString _sourceProp = QStringLiteral("source"); +const QString _cubeProp = QStringLiteral("skyBoxCubeMap"); +const QString _bgProp = QStringLiteral("backgroundMode"); +const QString _colorProp = QStringLiteral("clearColor"); static const float floatMin = std::numeric_limits::lowest(); static const float floatMax = std::numeric_limits::max(); @@ -739,6 +749,11 @@ void GeneralHelper::setSceneEnvironmentData(const QString &sceneId, } } +bool GeneralHelper::hasSceneEnvironmentData(const QString &sceneId) const +{ + return m_sceneEnvironmentData.contains(sceneId); +} + QQuick3DSceneEnvironment::QQuick3DEnvironmentBackgroundTypes GeneralHelper::sceneEnvironmentBgMode( const QString &sceneId) const { @@ -760,6 +775,69 @@ QQuick3DCubeMapTexture *GeneralHelper::sceneEnvironmentSkyBoxCubeMap(const QStri return m_sceneEnvironmentData[sceneId].skyBoxCubeMap.data(); } +void GeneralHelper::updateSceneEnvToLast(QQuick3DSceneEnvironment *env, QQuick3DTexture *lightProbe, + QQuick3DCubeMapTexture *cubeMap) +{ + if (!env) + return; + + if (m_lastSceneEnvData.contains(_bgProp)) { + Enumeration enumeration = m_lastSceneEnvData[_bgProp].value(); + QMetaEnum me = QMetaEnum::fromType(); + int intValue = me.keyToValue(enumeration.toName()); + env->setBackgroundMode(QQuick3DSceneEnvironment::QQuick3DEnvironmentBackgroundTypes(intValue)); + } else { + env->setBackgroundMode(QQuick3DSceneEnvironment::Transparent); + } + + if (m_lastSceneEnvData.contains(_colorProp)) + env->setClearColor(m_lastSceneEnvData[_colorProp].value()); + else + env->setClearColor(Qt::transparent); + + if (lightProbe) { + if (m_lastSceneEnvData.contains(_lightProbeProp)) { + QVariantMap props = m_lastSceneEnvData[_lightProbeProp].toMap(); + if (props.contains(_sourceProp)) + lightProbe->setSource(props[_sourceProp].toUrl()); + else + lightProbe->setSource({}); + env->setLightProbe(lightProbe); + } else { + env->setLightProbe(nullptr); + } + } + + if (cubeMap) { + if (m_lastSceneEnvData.contains(_cubeProp)) { + QVariantMap props = m_lastSceneEnvData[_cubeProp].toMap(); + if (props.contains(_sourceProp)) + cubeMap->setSource(props[_sourceProp].toUrl()); + else + cubeMap->setSource({}); + env->setSkyBoxCubeMap(cubeMap); + } else { + env->setSkyBoxCubeMap(nullptr); + } + } +} + +bool GeneralHelper::sceneHasLightProbe(const QString &sceneId) +{ + // From editor perspective, a scene is considered to have a light probe if scene itself + // has a light probe or scene has no env data and last scene had a light probe + if (m_sceneEnvironmentData.contains(sceneId)) { + return bool(m_sceneEnvironmentData[sceneId].lightProbe); + } else { + if (m_lastSceneEnvData.contains(_lightProbeProp)) { + QVariantMap props = m_lastSceneEnvData[_sourceProp].toMap(); + if (props.contains(_sourceProp)) + return !props[_sourceProp].toUrl().isEmpty(); + } + } + return false; +} + void GeneralHelper::clearSceneEnvironmentData() { for (const SceneEnvData &data : std::as_const(m_sceneEnvironmentData)) { @@ -773,6 +851,12 @@ void GeneralHelper::clearSceneEnvironmentData() emit sceneEnvDataChanged(); } +void GeneralHelper::setLastSceneEnvironmentData(const QVariantMap &data) +{ + m_lastSceneEnvData = data; + storeToolState(_projectStateId, _lastSceneEnvKey, m_lastSceneEnvData); +} + void GeneralHelper::initToolStates(const QString &sceneId, const QVariantMap &toolStates) { m_toolStates[sceneId] = toolStates; @@ -797,11 +881,21 @@ QString GeneralHelper::globalStateId() const return _globalStateId; } +QString GeneralHelper::projectStateId() const +{ + return _projectStateId; +} + QString GeneralHelper::lastSceneIdKey() const { return _lastSceneIdKey; } +QString GeneralHelper::lastSceneEnvKey() const +{ + return _lastSceneEnvKey; +} + QString GeneralHelper::rootSizeKey() const { return _rootSizeKey; diff --git a/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.h b/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.h index 10ed28aa721..50eaba68ebb 100644 --- a/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.h +++ b/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.h @@ -96,7 +96,9 @@ public: Q_INVOKABLE void enableItemUpdate(QQuickItem *item, bool enable); Q_INVOKABLE QVariantMap getToolStates(const QString &sceneId); QString globalStateId() const; + QString projectStateId() const; QString lastSceneIdKey() const; + QString lastSceneEnvKey() const; QString rootSizeKey() const; Q_INVOKABLE void setMultiSelectionTargets(QQuick3DNode *multiSelectRootNode, @@ -109,12 +111,18 @@ public: Q_INVOKABLE void rotateMultiSelection(bool commit); void setSceneEnvironmentData(const QString &sceneId, QQuick3DSceneEnvironment *env); + Q_INVOKABLE bool hasSceneEnvironmentData(const QString &sceneId) const; Q_INVOKABLE QQuick3DSceneEnvironment::QQuick3DEnvironmentBackgroundTypes sceneEnvironmentBgMode( const QString &sceneId) const; Q_INVOKABLE QColor sceneEnvironmentColor(const QString &sceneId) const; Q_INVOKABLE QQuick3DTexture *sceneEnvironmentLightProbe(const QString &sceneId) const; Q_INVOKABLE QQuick3DCubeMapTexture *sceneEnvironmentSkyBoxCubeMap(const QString &sceneId) const; + Q_INVOKABLE void updateSceneEnvToLast(QQuick3DSceneEnvironment *env, QQuick3DTexture *lightProbe, + QQuick3DCubeMapTexture *cubeMap); + Q_INVOKABLE bool sceneHasLightProbe(const QString &sceneId); + void clearSceneEnvironmentData(); + void setLastSceneEnvironmentData(const QVariantMap &data); bool isMacOS() const; @@ -202,6 +210,7 @@ private: QPointer skyBoxCubeMap; }; QHash m_sceneEnvironmentData; + QVariantMap m_lastSceneEnvData; struct MultiSelData { QVector3D startScenePos; diff --git a/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp b/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp index 705efce2cf9..d4353fc8f23 100644 --- a/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp +++ b/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp @@ -1963,6 +1963,8 @@ void Qt5InformationNodeInstanceServer::setup3DEditView( if (toolStates[helper->globalStateId()].contains(helper->lastSceneIdKey())) lastSceneId = toolStates[helper->globalStateId()][helper->lastSceneIdKey()].toString(); } + if (toolStates.contains(helper->projectStateId())) + helper->setLastSceneEnvironmentData(toolStates[helper->projectStateId()][helper->lastSceneEnvKey()].toMap()); } // Find a scene to show @@ -2607,6 +2609,12 @@ void Qt5InformationNodeInstanceServer::view3DAction(const View3DActionCommand &c case View3DActionType::MaterialOverride: updatedToolState.insert("matOverride", command.value().toList()); break; + case View3DActionType::SetLastSceneEnvData: { + auto helper = qobject_cast(m_3dHelper); + if (helper) + helper->setLastSceneEnvironmentData(command.value().toMap()); + break; + } default: break; From fc79bad535e4e8cc8d6c979d8ebccb6c545700e0 Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Wed, 15 May 2024 19:38:37 +0300 Subject: [PATCH 116/154] QmlDesigner: Remove Model::generateIdFromName() - Added UniqueName::getId() - Cleaned up generateNewId() and made it use UniqueName::getId() - Moved UniqueName to designercore Also reversed how the predicate of UniqueName::get() works. Change-Id: I89c50f7d80610243f56be165b1495ef428da457c Reviewed-by: Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Miikka Heikkinen --- src/plugins/qmldesigner/CMakeLists.txt | 8 +- .../assetslibrary/assetslibrarymodel.cpp | 2 +- .../assetslibrary/assetslibrarywidget.cpp | 2 +- .../collectioneditor/collectionview.cpp | 2 +- .../contentlibraryusermodel.cpp | 6 +- .../contentlibrary/contentlibraryview.cpp | 4 +- .../materialeditor/materialeditorview.cpp | 4 +- .../qmldesigner/designercore/include/model.h | 4 +- .../qmldesigner/designercore/model/model.cpp | 93 ++------------ .../qmldesigner/designercore/uniquename.cpp | 121 ++++++++++++++++++ .../qmldesigner/designercore/uniquename.h | 32 +++++ src/plugins/qmldesigner/utils/uniquename.cpp | 93 -------------- src/plugins/qmldesigner/utils/uniquename.h | 20 --- .../tests/testdesignercore/CMakeLists.txt | 2 + 14 files changed, 181 insertions(+), 212 deletions(-) create mode 100644 src/plugins/qmldesigner/designercore/uniquename.cpp create mode 100644 src/plugins/qmldesigner/designercore/uniquename.h delete mode 100644 src/plugins/qmldesigner/utils/uniquename.cpp delete mode 100644 src/plugins/qmldesigner/utils/uniquename.h diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index 87f6e9c66d1..2d67fa360f7 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -46,7 +46,6 @@ add_qtc_library(QmlDesignerUtils STATIC hdrimage.cpp hdrimage.h ktximage.cpp ktximage.h imageutils.cpp imageutils.h - uniquename.cpp uniquename.h qmldesignerutils_global.h ) @@ -96,10 +95,9 @@ add_qtc_library(QmlDesignerCore STATIC ${CMAKE_CURRENT_LIST_DIR}/designercore/include SOURCES_PREFIX ${CMAKE_CURRENT_LIST_DIR}/designercore SOURCES - rewritertransaction.cpp - rewritertransaction.h - generatedcomponentutils.cpp - generatedcomponentutils.h + rewritertransaction.cpp rewritertransaction.h + generatedcomponentutils.cpp generatedcomponentutils.h + uniquename.cpp uniquename.h ) extend_qtc_library(QmlDesignerCore diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.cpp b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.cpp index d3a701aa5f2..36299e586bf 100644 --- a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.cpp +++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.cpp @@ -5,6 +5,7 @@ #include #include +#include #include @@ -12,7 +13,6 @@ #include #include #include -#include #include #include diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp index e63ac9b0d5a..11439409a71 100644 --- a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp +++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -28,7 +29,6 @@ #include #include #include -#include #include #include diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp index f8382b19897..8a9e6a8276b 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp @@ -242,7 +242,7 @@ void CollectionView::addResource(const QUrl &url, const QString &name) VariantProperty nameProperty = resourceNode.variantProperty("objectName"); sourceProperty.setValue(sourceAddress); nameProperty.setValue(name); - resourceNode.setIdWithoutRefactoring(model()->generateIdFromName(name, "model")); + resourceNode.setIdWithoutRefactoring(model()->generateNewId(name, "model")); rootModelNode().defaultNodeAbstractProperty().reparentHere(resourceNode); }); } diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp index ca90c9c4517..7f664f0388d 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp @@ -13,10 +13,10 @@ #include #include #include +#include #include #include -#include #include #include @@ -318,11 +318,11 @@ QPair ContentLibraryUserModel::getUniqueLibItemNames(const QSt baseQml.prepend("My"); QString uniqueQml = UniqueName::get(baseQml, [&] (const QString &name) { - return !itemQmls.contains(name); + return itemQmls.contains(name); }); QString uniqueIcon = UniqueName::get(defaultName, [&] (const QString &name) { - return !itemIcons.contains(name); + return itemIcons.contains(name); }); return {uniqueQml + ".qml", uniqueIcon + ".png"}; diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp index 8e5b5c4e3b3..0dbc3a4da71 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp @@ -779,7 +779,7 @@ ModelNode ContentLibraryView::createMaterial(const TypeName &typeName) QString newName = QString::fromUtf8(typeName).replace(rgx, " \\1\\2").trimmed(); if (newName.endsWith(" Material")) newName.chop(9); // remove trailing " Material" - QString newId = model()->generateIdFromName(newName, "material"); + QString newId = model()->generateNewId(newName, "material"); newMatNode.setIdWithRefactoring(newId); VariantProperty objNameProp = newMatNode.variantProperty("objectName"); @@ -805,7 +805,7 @@ ModelNode ContentLibraryView::createMaterial(const NodeMetaInfo &metaInfo) QString newName = QString::fromLatin1(metaInfo.simplifiedTypeName()).replace(rgx, " \\1\\2").trimmed(); if (newName.endsWith(" Material")) newName.chop(9); // remove trailing " Material" - QString newId = model()->generateIdFromName(newName, "material"); + QString newId = model()->generateNewId(newName, "material"); newMatNode.setIdWithRefactoring(newId); VariantProperty objNameProp = newMatNode.variantProperty("objectName"); diff --git a/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp b/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp index 02d9333e09c..21114267cbb 100644 --- a/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp +++ b/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp @@ -961,7 +961,7 @@ void MaterialEditorView::renameMaterial(ModelNode &material, const QString &newN return; executeInTransaction(__FUNCTION__, [&] { - material.setIdWithRefactoring(model()->generateIdFromName(newName, "material")); + material.setIdWithRefactoring(model()->generateNewId(newName, "material")); VariantProperty objNameProp = material.variantProperty("objectName"); objNameProp.setValue(newName); @@ -998,7 +998,7 @@ void MaterialEditorView::duplicateMaterial(const ModelNode &material) QString newName = sourceMat.modelNode().variantProperty("objectName").value().toString() + " copy"; VariantProperty objNameProp = duplicateMatNode.variantProperty("objectName"); objNameProp.setValue(newName); - duplicateMatNode.setIdWithoutRefactoring(model()->generateIdFromName(newName, "material")); + duplicateMatNode.setIdWithoutRefactoring(model()->generateNewId(newName, "material")); // sync properties. Only the base state is duplicated. const QList props = material.properties(); diff --git a/src/plugins/qmldesigner/designercore/include/model.h b/src/plugins/qmldesigner/designercore/include/model.h index 74c5697990c..5529064284e 100644 --- a/src/plugins/qmldesigner/designercore/include/model.h +++ b/src/plugins/qmldesigner/designercore/include/model.h @@ -255,10 +255,8 @@ public: bool hasId(const QString &id) const; bool hasImport(const QString &importUrl) const; - QString generateNewId(const QString &prefixName, - const QString &fallbackPrefix = "element", + QString generateNewId(const QString &prefixName, const QString &fallbackPrefix = "element", std::optional> isDuplicate = {}) const; - QString generateIdFromName(const QString &name, const QString &fallbackId = "element") const; void startDrag(QMimeData *mimeData, const QPixmap &icon); void endDrag(); diff --git a/src/plugins/qmldesigner/designercore/model/model.cpp b/src/plugins/qmldesigner/designercore/model/model.cpp index 23871b66699..d95d3e294bb 100644 --- a/src/plugins/qmldesigner/designercore/model/model.cpp +++ b/src/plugins/qmldesigner/designercore/model/model.cpp @@ -9,7 +9,6 @@ #include "../projectstorage/sourcepath.h" #include "../projectstorage/sourcepathcache.h" #include "abstractview.h" -#include "auxiliarydataproperties.h" #include "internalbindingproperty.h" #include "internalnodeabstractproperty.h" #include "internalnodelistproperty.h" @@ -33,6 +32,8 @@ #include "signalhandlerproperty.h" #include "variantproperty.h" +#include + #include #include @@ -46,8 +47,6 @@ #include #include -#include - /*! \defgroup CoreModel */ @@ -1864,90 +1863,22 @@ bool Model::hasImport(const QString &importUrl) const }); } -static QString firstCharToLower(const QString &string) -{ - QString resultString = string; - - if (!resultString.isEmpty()) - resultString[0] = resultString.at(0).toLower(); - - return resultString; -} - -QString Model::generateNewId(const QString &prefixName, - const QString &fallbackPrefix, +QString Model::generateNewId(const QString &prefixName, const QString &fallbackPrefix, std::optional> isDuplicate) const { - // First try just the prefixName without number as postfix, then continue with 2 and further - // as postfix until id does not already exist. - // Properties of the root node are not allowed for ids, because they are available in the - // complete context without qualification. + QString newId = prefixName; - int counter = 0; + if (newId.isEmpty()) + newId = fallbackPrefix; - static const QRegularExpression nonWordCharsRegex("\\W"); - QString newBaseId = firstCharToLower(prefixName); - newBaseId.remove(nonWordCharsRegex); - - if (!newBaseId.isEmpty()) { - QChar firstChar = newBaseId.at(0); - if (firstChar.isDigit()) - newBaseId.prepend('_'); - } else { - newBaseId = fallbackPrefix; - } - - QString newId = newBaseId; - - if (!isDuplicate.has_value()) + if (!isDuplicate.has_value()) // TODO: to be removed separately to not break build isDuplicate = std::bind(&Model::hasId, this, std::placeholders::_1); - while (!ModelNode::isValidId(newId) || isDuplicate.value()(newId) - || d->rootNode()->property(newId.toUtf8())) { - ++counter; - newId = QStringView(u"%1%2").arg(firstCharToLower(newBaseId)).arg(counter); - } - - return newId; -} - -// Generate a unique camelCase id from a name -// note: this methods does the same as generateNewId(). The 2 methods should be merged into one -QString Model::generateIdFromName(const QString &name, const QString &fallbackId) const -{ - QString newId; - if (name.isEmpty()) { - newId = fallbackId; - } else { - // convert to camel case - QStringList nameWords = name.split(" "); - nameWords[0] = nameWords[0].at(0).toLower() + nameWords[0].mid(1); - for (int i = 1; i < nameWords.size(); ++i) - nameWords[i] = nameWords[i].at(0).toUpper() + nameWords[i].mid(1); - newId = nameWords.join(""); - - // if id starts with a number prepend an underscore - if (newId.at(0).isDigit()) - newId.prepend('_'); - } - - // If the new id is not valid (e.g. qml keyword match), try fixing it by prepending underscore - if (!ModelNode::isValidId(newId)) - newId.prepend("_"); - - QRegularExpression rgx("\\d+$"); // matches a number at the end of a string - while (hasId(newId)) { // id exists - QRegularExpressionMatch match = rgx.match(newId); - if (match.hasMatch()) { // ends with a number, increment it - QString numStr = match.captured(); - int num = numStr.toInt() + 1; - newId = newId.mid(0, match.capturedStart()) + QString::number(num); - } else { - newId.append('1'); - } - } - - return newId; + return UniqueName::getId(prefixName, [&] (const QString &id) { + // Properties of the root node are not allowed for ids, because they are available in the + // complete context without qualification. + return isDuplicate.value()(id) || d->rootNode()->property(id.toUtf8()); + }); } void Model::startDrag(QMimeData *mimeData, const QPixmap &icon) diff --git a/src/plugins/qmldesigner/designercore/uniquename.cpp b/src/plugins/qmldesigner/designercore/uniquename.cpp new file mode 100644 index 00000000000..7bfa2c74303 --- /dev/null +++ b/src/plugins/qmldesigner/designercore/uniquename.cpp @@ -0,0 +1,121 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "uniquename.h" + +#include +#include + +namespace QmlDesigner { + +/** + * @brief Generates a unique name based on the provided name. + * + * This method iteratively generates a name by appending suffixes until a unique name is found. + * The uniqueness of the generated name is determined by the provided predicate function. + * + * @param name The original name to be made unique. + * @param predicate A function that checks if a name exists. Returns true if the name exists, + * false if name is unique. + * @return A unique name derived from the provided name. + */ +QString UniqueName::get(const QString &name, std::function predicate) +{ + if (!predicate(name)) + return name; + + // match prefix and number (including zero padding) parts + static QRegularExpression rgx("(\\D*?)(\\d+)$"); + QRegularExpressionMatch match = rgx.match(name); + + QString prefix; + int number = 0; + int padding = 0; + + if (match.hasMatch()) { + // Split the name into prefix and number + prefix = match.captured(1); + QString numberStr = match.captured(2); + number = numberStr.toInt(); + padding = numberStr.size(); + } else { + prefix = name; + } + + QString nameTemplate = "%1%2"; + QString newName; + do { + newName = nameTemplate.arg(prefix).arg(++number, padding, 10, QChar('0')); + } while (predicate(newName)); + + return newName; +} + +/** + * @brief Generates a unique path based on the provided path. If the path belongs to a file, the + * filename or if it's a directory, the directory name will be adjusted to ensure uniqueness. + * + * This method appends a numerical suffix (or increment it if it exists) to the filename or + * directory name if necessary to make it unique. + * + * @param path The original path to be made unique. + * @return A unique path derived from the provided path. + */ +QString UniqueName::getPath(const QString &path) +{ + // Remove the trailing slash if it exists (otherwise QFileInfo::path() returns empty) + QString adjustedPath = path; + if (adjustedPath.endsWith('/')) + adjustedPath.chop(1); + + QFileInfo fileInfo = QFileInfo(adjustedPath); + QString baseName = fileInfo.baseName(); + QString suffix = fileInfo.completeSuffix(); + if (!suffix.isEmpty()) + suffix.prepend('.'); + + QString parentDir = fileInfo.path(); + QString pathTemplate = parentDir + "/%1" + suffix; + + QString uniqueBaseName = UniqueName::get(baseName, [&] (const QString &currName) { + return !QFileInfo::exists(pathTemplate.arg(currName)); + }); + + return pathTemplate.arg(uniqueBaseName); +} + +/** + * @brief Generates a unique ID based on the provided id + * + * This works similar to get() with additional restrictions: + * - Removes non-Latin1 characters + * - Removes spaces + * - Ensures the first letter is lowercase + * - Converts spaces to camel case + * - Prepends an underscore if id starts with a number or is a reserved word + * + * @param id The original id to be made unique. + * @return A unique Id (when predicate() returns false) + */ +QString UniqueName::getId(const QString &id, std::function predicate) +{ + // remove non word (non A-Z, a-z, 0-9) characters + static const QRegularExpression nonWordCharsRgx("\\W"); + QString newId = id.simplified(); + newId.remove(nonWordCharsRgx); + + // convert to camel case + QStringList idParts = newId.split(" "); + idParts[0] = idParts[0].at(0).toLower() + idParts[0].mid(1); + for (int i = 1; i < idParts.size(); ++i) + idParts[i] = idParts[i].at(0).toUpper() + idParts[i].mid(1); + newId = idParts.join(""); + + // prepend _ if starts with a digit or invalid id (such as reserved words) + if (newId.at(0).isDigit() || std::binary_search(keywords.begin(), keywords.end(), newId)) + newId.prepend('_'); + + return UniqueName::get(newId, predicate); +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/uniquename.h b/src/plugins/qmldesigner/designercore/uniquename.h new file mode 100644 index 00000000000..3d6bc6c0e36 --- /dev/null +++ b/src/plugins/qmldesigner/designercore/uniquename.h @@ -0,0 +1,32 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include + +#include +#include + +namespace QmlDesigner { + +class QMLDESIGNERCORE_EXPORT UniqueName +{ +public: + static QString get(const QString &name, std::function predicate); + static QString getPath(const QString &path); + static QString getId(const QString &id, std::function predicate); + +private: + static inline const QStringList keywords { + "anchors", "as", "baseState", "border", "bottom", "break", "case", "catch", "clip", "color", + "continue", "data", "debugger", "default", "delete", "do", "else", "enabled", "finally", + "flow", "focus", "font", "for", "function", "height", "if", "import", "in", "instanceof", + "item", "layer", "left", "margin", "new", "opacity", "padding", "parent", "print", "rect", + "return", "right", "scale", "shaderInfo", "source", "sprite", "spriteSequence", "state", + "switch", "text", "this", "throw", "top", "try", "typeof", "var", "visible", "void", + "while", "with", "x", "y" + }; +}; + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/utils/uniquename.cpp b/src/plugins/qmldesigner/utils/uniquename.cpp deleted file mode 100644 index e77814c97cf..00000000000 --- a/src/plugins/qmldesigner/utils/uniquename.cpp +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright (C) 2024 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "uniquename.h" - -#include -#include - -namespace QmlDesigner { - -/** - * @brief Generates a unique name based on the provided name. - * - * This method iteratively generates a name by appending suffixes until a unique name is found. - * The uniqueness of the generated name is determined by the provided predicate function. - * - * @param oldName The original name to be made unique. - * @param predicate A function that checks if a name is unique. Returns true if the name is unique, - * false otherwise. - * @return A unique name derived from the provided name. - */ -QString UniqueName::get(const QString &oldName, std::function predicate) -{ - QString newName = oldName; - while (!predicate(newName)) - newName = nextName(newName); - - return newName; -} - -/** - * @brief Generates a unique path based on the provided path. If the path belongs to a file, the - * filename or if it's a directory, the directory name will be adjusted to ensure uniqueness. - * - * This method appends a numerical suffix (or increment it if it exists) to the filename or - * directory name if necessary to make it unique. - * - * @param path The original path to be made unique. - * @return A unique path derived from the provided path. - */ -QString UniqueName::getPath(const QString &path) -{ - // Remove the trailing slash if it exists (otherwise QFileInfo::path() returns empty) - QString adjustedPath = path; - if (adjustedPath.endsWith('/')) - adjustedPath.chop(1); - - QFileInfo fileInfo = QFileInfo(adjustedPath); - QString baseName = fileInfo.baseName(); - QString suffix = fileInfo.completeSuffix(); - if (!suffix.isEmpty()) - suffix.prepend('.'); - - QString parentDir = fileInfo.path(); - QString pathTemplate = parentDir + "/%1" + suffix; - - QString uniqueBaseName = UniqueName::get(baseName, [&] (const QString &currName) { - return !QFileInfo::exists(pathTemplate.arg(currName)); - }); - - return pathTemplate.arg(uniqueBaseName); -} - -QString UniqueName::nextName(const QString &oldName) -{ - static QRegularExpression rgx("\\d+$"); // match a number at the end of a string - - QString uniqueName = oldName; - // if the name ends with a number, increment it - QRegularExpressionMatch match = rgx.match(uniqueName); - if (match.hasMatch()) { // ends with a number - QString numStr = match.captured(0); - int num = numStr.toInt() + 1; - - // get number of padding zeros, ex: for "005" = 2 - int nPaddingZeros = 0; - for (; nPaddingZeros < numStr.size() && numStr[nPaddingZeros] == '0'; ++nPaddingZeros); - - // if the incremented number's digits increased, decrease the padding zeros - if (std::fmod(std::log10(num), 1.0) == 0) - --nPaddingZeros; - - uniqueName = oldName.left(match.capturedStart()) - + QString('0').repeated(nPaddingZeros) - + QString::number(num); - } else { - uniqueName = oldName + '1'; - } - - return uniqueName; -} - -} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/utils/uniquename.h b/src/plugins/qmldesigner/utils/uniquename.h deleted file mode 100644 index 9c6765d1aee..00000000000 --- a/src/plugins/qmldesigner/utils/uniquename.h +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright (C) 2024 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 UniqueName -{ -public: - static QString get(const QString &oldName, std::function predicate); - static QString getPath(const QString &oldName); - -private: - static QString nextName(const QString &oldName); -}; - -} // namespace QmlDesigner diff --git a/tests/unit/tests/testdesignercore/CMakeLists.txt b/tests/unit/tests/testdesignercore/CMakeLists.txt index 0bdf3452d6a..ebe7f7df120 100644 --- a/tests/unit/tests/testdesignercore/CMakeLists.txt +++ b/tests/unit/tests/testdesignercore/CMakeLists.txt @@ -147,6 +147,8 @@ add_qtc_library(TestDesignerCore OBJECT tracing/qmldesignertracing.cpp tracing/qmldesignertracing.h rewritertransaction.cpp rewritertransaction.h + uniquename.cpp + uniquename.h ) extend_qtc_library(TestDesignerCore From 46350083deddff9df9d8fb3ff0be0fda93902cbb Mon Sep 17 00:00:00 2001 From: Johanna Vanhatapio Date: Mon, 6 May 2024 12:35:45 +0300 Subject: [PATCH 117/154] Doc: Improve the documentation for 3D Some editions to comply with the MS Style guide and to improve the structure. Task-number: QDS-12670 Change-Id: Iddb0778327880ec65bdb590d1e02e4ca15e72051 Reviewed-by: Mats Honkamaa --- .../qtdesignstudio-3d-editor.qdoc | 320 +++++++++--------- 1 file changed, 158 insertions(+), 162 deletions(-) diff --git a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-editor.qdoc b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-editor.qdoc index 33c5dbfeeef..aba12f38c1e 100644 --- a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-editor.qdoc +++ b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-editor.qdoc @@ -13,54 +13,28 @@ \brief Edit a 3D scene. When editing a 3D scene, you view the scene in the \uicontrol{3D} - view. You can change the projection of the view by switching between - \e {perspective camera} and \e {orthographic camera} modes. When using the - perspective camera mode, components that are far from the camera appear - smaller than those nearby. In the orthographic camera mode, all components - appear at the same scale irrespective of their distance from the camera. - Both of them are free-form camera modes that you can use to orbit around - the scene. + view. When you import 3D scenes from files that you exported from 3D graphics tools, you also import a \l{Cameras}{scene camera}, - \l{Lights}{light}, \l{3D Models}{model}, and - \l {Materials and Shaders}{materials}. If your scene did not contain - them, you can add the corresponding \l {3D Components}{Qt Quick 3D} + \l{Lights}{light}, \l{3D Models}{model}, and \l {Materials and Shaders}{materials}. + If your scene does not contain them, add the corresponding \l {3D Components}{Qt Quick 3D} components from \uicontrol Components > \inlineimage icons/plus.png > \uicontrol {Qt Quick 3D} > \uicontrol {Qt Quick 3D}. - You can use the toolbar buttons - to \e transform 3D components and manipulate the 3D scene. Transformation - refers to moving, rotating, or scaling of a component. The \e pivot of the - component is used as the origin for transformations. You can set a - \l{Managing 3D Transformations}{local pivot offset} for a component in - \uicontrol Properties to transform the component around a point other than - its local origin. A line is drawn in the \uicontrol{3D} view from the pivot - point to the center of the component to provide a visual connection between - them. Especially when working with complex scenes, it may be useful to use - the \l {Showing and Hiding Components}{showing and hiding} or the - \l {Locking Components}{locking} features in \l Navigator to avoid - transforming components by mistake while editing your scene. + Use the \uicontrol 3D toolbar buttons to modify the \uicontrol 3D view, + manipulate the 3D scene, and to access functionalities to \e transform 3D + components. Transformation refers to moving, rotating, or scaling a + component. The \e pivot of the component is used as the origin for + transformations. Set a \l{Managing 3D Transformations}{local pivot offset} + for a component in \uicontrol Properties to transform the component around + a point other than its local origin. A line is drawn in the \uicontrol{3D} + view from the pivot point to the center of the component to provide a visual + connection between them. - Toggle between local and global orientation to determine whether the gizmos - affect only the local transformations of the component or whether they - transform with respect to the global space. - - Another helpful feature when editing 3D scenes is the \e {edit light}, - which is a quick way to light the scene. - - Additionally, you can toggle the visibility of the grid, selection boxes, - icon gizmos, and camera frustums in the 3D scene. - - There is a context menu in the \uicontrol 3D view. To open it, right-click - in the \uicontrol 3D view. From the context menu you can: - \list - \li Create cameras, lights, and models. - \li Open \uicontrol {Material Editor} and edit materials. - \li Delete components - \endlist - - \image 3d-view-context-menu.webp "The context menu in the 3D view" + \note To avoid transforming components by mistake while editing your scene, + use the \l {Showing and Hiding Components}{showing and hiding} or the + \l {Locking Components}{locking} features in \l Navigator. To refresh the contents of the \uicontrol{3D} view, press \key P or select the \inlineimage icons/reset.png @@ -73,53 +47,74 @@ \youtube SsFWyUeAA_4 + \section1 Usingn the Context Menu in the 3D View + + There is a context menu in the \uicontrol 3D view. To open it, right-click + in the \uicontrol 3D view. From the context menu you can, for example: + \list + \li Create cameras, lights, and models. + \li Open \uicontrol {Material Editor} and edit materials. + \li Delete components. + \li Align camera views. + \endlist + + \image 3d-view-context-menu.webp "The context menu in the 3D view" + \section1 Controlling the 3D View Camera \section2 Toggling Camera Mode - To switch to perspective camera mode, select - \inlineimage perspective_camera.png - (\uicontrol {Toggle Perspective/Orthographic Edit Camera}). - To switch to orthographic camera mode, select - \inlineimage orthographic_camera.png - . You can also Toggle the camera mode by using the keyboard shortcut \key T. + Change the projection of the view by switching between \e {perspective camera} + and \e {orthographic camera} modes. When using the perspective camera mode, + components that are far from the camera appear smaller than those nearby. In + the orthographic camera mode, all components appear at the same scale + irrespective of their distance from the camera. Both of them are free-form + camera modes that you can use to orbit around the scene. + + To toggle the camera mode, do one of the following: + \list + \li Select \inlineimage perspective_camera.png + (\uicontrol {Toggle Perspective/Orthographic Camera}) to use the + perspective camera mode. + \li Select \inlineimage orthographic_camera.png to use the orthographic + camera mode. + \endlist + You can also toggle the camera mode by using the keyboard shortcut \key T. \section2 Navigating in the 3D Scene - You can navigate the scene by panning, rotating, and zooming the 3D view + Navigate the scene by panning, rotating, and zooming the 3D view camera: \list \li To pan, press \key Alt (or \key Option on \macos) and use the - middle mouse button to click and drag anywhere in the rendered - view to slide the view around. Alternatively, press and hold \key {right mouse - button} and \key {left mouse button} and drag anywhere in the view to pan. - \li To orbit, press \key Alt and click and drag anywhere in the rendered - view to rotate the view. + wheel button to drag anywhere in the \uicontrol 3D view to + slide the view around. Alternatively, press and hold \key + {right mouse button} and \key {left mouse button} and drag + anywhere in the view to pan. + \li To orbit, press \key Alt and and drag anywhere in the + \uicontrol 3D view to rotate the view. \li To zoom, use the mouse wheel or press \key Alt and right-click - anywhere in the rendered view to zoom the view in or out as you drag + anywhere in the \uicontrol 3D view to zoom the view in or out as you drag up or down. \endlist - To zoom and focus the 3D view camera on a selected component, - select \inlineimage fit_selected.png - (\uicontrol {Fit Selected}) or press \key F. + To zoom and focus the 3D view camera on a selected component, select + \inlineimage fit_selected.png (\uicontrol {Fit Selected}) or press \key F. The world axis helper (1) shows the direction of the world axes in the view. - To point the camera at the currently selected component in the direction of + To point the 3D view camera at the currently selected component in the direction of an axis, click the axis. Clicking the dot at the end of the axis will point the camera at the opposite direction of the axis. If no component is - selected, the camera is pointed at the world origin. This does not affect - the camera zoom level. + selected, the camera is pointed at the world origin. \image studio-3d-editor-axis-helper.webp "Axis helper in the 3D view" - You can use scene cameras (2) to view the \uicontrol View3D component from a - specific angle in the \l {2D} view while editing scenes. Different types of - cameras are available in \uicontrol Components - > \uicontrol {Qt Quick 3D} > \uicontrol {Qt Quick 3D}. For more information - about using cameras in the scene, the available camera types, and their - properties, see \l{Cameras}. + Use scene cameras (2) to view the \uicontrol View3D component from a + specific angle in the \l {2D} view while editing scenes. Drag a camera + component to your scene from \uicontrol Components > \uicontrol {Qt Quick 3D} > + \uicontrol {Qt Quick 3D}. For more information about using cameras in the + scene and the available camera types, see \l{Cameras}. \section2 Using Split View @@ -128,14 +123,14 @@ \image studio-3d-split-view.webp "Split view in the 3D view" - To select one of the four panes, click on it. The selected pane is marked with + To select one of the four panes, click the pane. The selected pane is marked with a blue frame. Use the world axis helper to change the point of view for each pane independently. Navigate each split by panning, rotating, and zooming, as described above. \section2 Using Fly Mode - You can move around freely in the 3D scene with fly mode. To navigate the scene with + Use the fly mode to move around freely in the 3D scene. To navigate the scene with fly mode, right-click and hold in the \uicontrol 3D view, and use the keyboard shortcuts to move around the scene: @@ -177,10 +172,8 @@ \section1 Using Global and Local Orientation - To switch between local and global orientation, select - \inlineimage global.png - (\uicontrol {Toggle Local/Global Orientation}) - or press \key Y. + In \uicontrol 3D view, you view the scene in global or local orientation + mode. In global orientation mode, transformation of a selected component is presented with respect to the global space. For example, while the move tool @@ -194,40 +187,40 @@ the axes of global space. Dragging on the red arrow of the gizmo moves the component in the local x direction in relation to the component. + To switch between local and global orientation, select \inlineimage global.png + (\uicontrol {Toggle Local/Global Orientation}) or press \key Y. + \section1 Using Edit Light The edit light is an extra point light that can be used to illuminate the scene. To toggle the edit light on and off, select \inlineimage edit_light_on.png - or \inlineimage edit_light_off.png - (\uicontrol {Toggle Edit Light}) - or press \key U. + or \inlineimage edit_light_off.png (\uicontrol {Toggle Edit Light}) or + press \key U. - For more information about the available scene light types and their - properties, see \l{Lights}. + For information about the available scene light types and their properties, + see \l{Lights}. \section1 Baking Lights - Bake lights to light static elements in your scene. To bake lights, - select \inlineimage icons/bakelights.png to open the - \uicontrol {Lights Baking Setup} dialog. For more information, see - \l {Baking Lightmaps}. + Bake lights to light static elements in your scene. To bake lights, select + \inlineimage icons/bakelights.png to open the \uicontrol {Lights Baking Setup} + dialog. For more information, see \l {Baking Lightmaps}. \section1 Selecting Components To move, rotate, or scale components in the scene, you need to select them - first. The selection mode buttons determine how components are selected when - you click them in the \uicontrol{3D} view: + first. Toggle the selection mode to determine how components are selected + when you click them in the \uicontrol{3D} view: \list - \li In the \inlineimage select_item.png - (\uicontrol {Single Selection}) mode, a single component is selected. - \li In the \inlineimage select_group.png - (\uicontrol {Group Selection}) mode, the top level parent of the - component is selected. This enables you to move, rotate, or scale a - group of components. + \li Use the \inlineimage select_item.png (\uicontrol {Single Selection}) + mode to select a single component. + \li Use the \inlineimage select_group.png (\uicontrol {Group Selection}) + mode to select the top level parent of the component, so you can move + a group of components simultaneously. \endlist - To toggle the selection mode, press \key Q. + Alternatively, press \key Q to toggle the selection mode. To multiselect, hold \key Ctrl and click the components you wish to select. @@ -244,16 +237,15 @@ y, or z axis or on the top, bottom, left, and right clip planes of the the \uicontrol{3D} view. - To move components, select \inlineimage move_off.png - or press \key W: + To move components, select \inlineimage move_off.png or press \key W: \list \li To move components along the axes of the move gizmo, click the axis, and drag the component along the axis. - \li To move components on a plane, click the plane handle and drag the + \li To move components on a plane, drag the plane handle of the move component on the plane. - \li To move components freely in the 3D view, click and drag the gray - handle at the center of the move gizmo. + \li To move components freely in the 3D view, drag the gray handle at the + center of the move gizmo. \endlist \section1 Rotating Components @@ -264,29 +256,28 @@ or press \key E: \list - \li To rotate a component around its rotation gizmo, click the axis ring - and drag in the direction you want to rotate the component in. - \li To freely rotate the component, click and drag the inner center - circle of the gizmo. + \li To rotate a component around its rotation gizmo, drag the axis ring + in the direction you want to rotate the component in. + \li To freely rotate the component, drag the inner center circle of the + gizmo. \endlist \section1 Scaling Components \image studio-3d-editor-scale.webp "The 3D view in scale mode" - You can use the scale handles to adjust the local x, y, or z scale of a + Úse the scale handles to adjust the local x, y, or z scale of a component. You can adjust the scale across one, two, or three axes, depending on the handle. - To scale components, select \inlineimage scale_off.png - or press \key R: + To scale components, select \inlineimage scale_off.png or press \key R: \list - \li To adjust the scale across one axis, click and drag the scale handle + \li To adjust the scale across one axis, drag the scale handle attached to the axis. - \li To adjust the scale across a plane, click the plane handle and drag - the component on the plane. - \li To uniformly scale a component across all axes, click and drag the + \li To adjust the scale across a plane, drag the plane handle of + the component. + \li To uniformly scale a component across all axes, drag the gray handle at the center of the component. \endlist @@ -295,7 +286,7 @@ With snapping turned on, the objects in the \uicontrol 3D view snap to certain intervals during transformation (move, rotate, scale). - You can toggle snapping in the following ways: + Toggle snapping in the following ways: \list \li Select \inlineimage icons/snapping-3d.png @@ -303,7 +294,7 @@ \li Hold down the \key Ctrl key. \endlist - With snapping turned on, you can press and hold \key Shift to snap objects to one tenth of + With snapping turned on, press and hold \key Shift to snap objects to one tenth of the specified snap interval. \section2 Configuring Snapping @@ -330,78 +321,85 @@ To align a camera to the \uicontrol{3D} view: \list 1 - \li Select a camera in the \uicontrol{3D} or \uicontrol {Navigator} view. + \li Select a scene camera in the \uicontrol{3D} or \uicontrol {Navigator} view. \note If you don't have a camera selected, the most recently selected camera is aligned to the view. - \li In the \uicontrol{3D} view, - select \inlineimage icons/align-camera-on.png - . + \li In the \uicontrol{3D} view, select \inlineimage icons/align-camera-on.png. \endlist - This moves and rotates the camera so that the camera shows the same view - as the current view in the \uicontrol{3D} view. + This moves and rotates the scene camera to show the same view as the current view + in the \uicontrol{3D} view. To align the \uicontrol{3D} view to a camera: - \list 1 - \li Select a camera in the \uicontrol{3D} view or \uicontrol {Navigator}. + \list 1 + \li Select a scene camera in the \uicontrol{3D} view or \uicontrol {Navigator}. \note If you don't have a camera selected, the view is aligned to the most recently - selected camera. - \li In the \uicontrol{3D} view, - select \inlineimage icons/align-view-on.png - . + selected camera. + \li In the \uicontrol{3D} view, select \inlineimage icons/align-view-on.png. \endlist - This copies the position as well as x and y rotation values from the - camera and applies them to the \uicontrol{3D} view. + This moves and rotates the 3D view to show the same view as the selected scene camera. \section1 Toggling Visibility To toggle the visibility of objects in the \uicontrol{3D} view, select - \inlineimage icons/visibilityon.png - in the toolbar. This opens a menu with the following options: + \inlineimage icons/visibilityon.png in the toolbar. This opens a menu with the + following options: \table - \row - \li Show Grid - \li Toggles the visibility of the helper grid. - \row - \li Show Selection Boxes - \li Toggles the visibility of selection boxes for selected 3D objects. - \row - \li Show Icon Gizmos - \li Toggles the visibility of icon gizmos for object such as cameras, - lights, and particle systems. - \row - \li Always Show Camera Frustums - \li Toggles between always showing the camera frustum and showing it - only for cameras selected in the \uicontrol{3D} view. - \row - \li Always Show Particle Emitters and Attractors - \li Toggle between always showing the particle emitter and attractor - visualizations and only showing them when the emitter or attractor is - selected in the \uicontrol{3D} view. + \header + \li Action + \li Description + \li Keyboard Shortcut + \row + \li Show Grid + \li Toggles the visibility of the helper grid. + \li \key G + \row + \li Show Selection Boxes + \li Toggles the visibility of selection boxes for selected 3D objects. + \li \key B + \row + \li Show Icon Gizmos + \li Toggles the visibility of icon gizmos for object such as cameras, + lights, and particle systems. + \li \key I + \row + \li Always Show Camera Frustums + \li Toggles between always showing the camera frustum and showing it + only for cameras selected in the \uicontrol{3D} view. + \li \key C + \row + \li Always Show Particle Emitters and Attractors + \li Toggles between always showing the particle emitter and attractor + visualizations and only showing them when the emitter or attractor + is selected in the \uicontrol{3D} view. + \li \key M \endtable \section1 Changing Colors To change the \uicontrol 3D view background or grid color, select - \inlineimage icons/3d-background-color.png - in the toolbar. This opens a menu with the following options: + \inlineimage icons/3d-background-color.png in the toolbar. This opens a menu + with the following options: \table + \header + \li Action + \li Decription + \row + \li Select Background Color + \li Select a color for the background. \row - \li Select Background Color - \li Select a color for the background. + \li Select Grid Color + \li Select a color for the grid. \row - \li Select Grid Color - \li Select a color for the grid. + \li Use Scene Environment Color + \li Sets the 3D view to use the scene environment color as background + color. \row - \li Use Scene Environment Color - \li Sets the 3D view to use the scene environment color as background - color. - \row - \li Reset Colors - \li Resets the background and grid colors to the default colors. + \li Reset Colors + \li Resets the background and grid colors to the default colors. \endtable \section1 Particle Editor @@ -416,16 +414,14 @@ \li Select a particle system in the \uicontrol Navigator or \uicontrol{3D} view. \li In the \uicontrol{3D} view, select - \inlineimage icons/particle-animation-on.png - to activate particle animation. Now you can see the particle animation in - the \uicontrol{3D} view. + \inlineimage icons/particle-animation-on.png to activate particle animation. + Now you can see the particle animation in the \uicontrol{3D} view. \endlist You can pause the particle animation by selecting - \inlineimage icons/particle-pause.png - . When the animation is paused, you can use - \inlineimage icons/particles-seek.png - to manually seek forward or backward in the particle animation. + \inlineimage icons/particle-pause.png. When the animation is paused, use + \inlineimage icons/particles-seek.png to manually seek forward or backward in + the particle animation. \section1 Using Viewport Shading From 872c4bf092bf5b7e8a563d3c841af0f8c6808739 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Tue, 21 May 2024 11:40:30 +0300 Subject: [PATCH 118/154] QmlDesigner: Fix double deletion of rewriter actions Actions were deleted after being inserted into actionsToRemove, which caused them to get deleted again, leading to crash. Change-Id: If6597f117859ca92cfc5af67c730e2f84e5c0639 Reviewed-by: Thomas Hartmann Reviewed-by: Qt CI Patch Build Bot --- .../qmldesigner/designercore/model/rewriteactioncompressor.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/plugins/qmldesigner/designercore/model/rewriteactioncompressor.cpp b/src/plugins/qmldesigner/designercore/model/rewriteactioncompressor.cpp index c4d96bb2505..edee6840ef6 100644 --- a/src/plugins/qmldesigner/designercore/model/rewriteactioncompressor.cpp +++ b/src/plugins/qmldesigner/designercore/model/rewriteactioncompressor.cpp @@ -58,7 +58,6 @@ void RewriteActionCompressor::compressImports(QList &actions) c actionsToRemove.append(action); actionsToRemove.append(addImportAction); addedImports.remove(import); - delete addImportAction; } else { removedImports.insert(import, action); } @@ -67,13 +66,11 @@ void RewriteActionCompressor::compressImports(QList &actions) c if (RewriteAction *duplicateAction = addedImports.value(import, 0)) { actionsToRemove.append(duplicateAction); addedImports.remove(import); - delete duplicateAction; addedImports.insert(import, action); } else if (RewriteAction *removeAction = removedImports.value(import, 0)) { actionsToRemove.append(action); actionsToRemove.append(removeAction); removedImports.remove(import); - delete removeAction; } else { addedImports.insert(import, action); } From 14e0196442a85ea0b90d4d0769696fff6bf40270 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Tue, 7 May 2024 13:41:14 +0300 Subject: [PATCH 119/154] Show 3D import preview Show preview of 3D import in item library import dialog when importing a single 3D asset. Fixes: QDS-11111 Change-Id: I13135be1e931cbee034ca8a89654c0827b937bdf Reviewed-by: Mahmoud Badri Reviewed-by: Qt CI Patch Build Bot --- .../commands/puppettocreatorcommand.h | 1 + .../interfaces/nodeinstanceglobal.h | 3 +- src/plugins/qmldesigner/CMakeLists.txt | 2 + .../components/itemlibrary/import3dcanvas.cpp | 56 +++++ .../components/itemlibrary/import3dcanvas.h | 32 +++ .../itemlibrary/import3dconnectionmanager.cpp | 47 ++++ .../itemlibrary/import3dconnectionmanager.h | 28 +++ .../itemlibraryassetimportdialog.cpp | 211 ++++++++++++++++-- .../itemlibraryassetimportdialog.h | 29 ++- .../itemlibraryassetimportdialog.ui | 86 ++++++- .../itemlibrary/itemlibraryassetimporter.cpp | 109 +++++++-- .../itemlibrary/itemlibraryassetimporter.h | 36 +-- .../itemlibrary/itemlibraryview.cpp | 4 +- src/plugins/qmldesigner/settingspage.cpp | 3 +- src/tools/qml2puppet/CMakeLists.txt | 1 + .../qml2puppet/editor3d/generalhelper.cpp | 9 +- .../qml2puppet/editor3d/generalhelper.h | 8 +- .../nodeinstanceserverdispatcher.cpp | 3 + .../qt5import3dnodeinstanceserver.cpp | 186 +++++++++++++++ .../instances/qt5import3dnodeinstanceserver.h | 46 ++++ .../instances/qt5nodeinstanceclientproxy.cpp | 4 + 21 files changed, 832 insertions(+), 72 deletions(-) create mode 100644 src/plugins/qmldesigner/components/itemlibrary/import3dcanvas.cpp create mode 100644 src/plugins/qmldesigner/components/itemlibrary/import3dcanvas.h create mode 100644 src/plugins/qmldesigner/components/itemlibrary/import3dconnectionmanager.cpp create mode 100644 src/plugins/qmldesigner/components/itemlibrary/import3dconnectionmanager.h create mode 100644 src/tools/qml2puppet/qml2puppet/instances/qt5import3dnodeinstanceserver.cpp create mode 100644 src/tools/qml2puppet/qml2puppet/instances/qt5import3dnodeinstanceserver.h diff --git a/src/libs/qmlpuppetcommunication/commands/puppettocreatorcommand.h b/src/libs/qmlpuppetcommunication/commands/puppettocreatorcommand.h index 1e8d82e632b..b0012f3eb24 100644 --- a/src/libs/qmlpuppetcommunication/commands/puppettocreatorcommand.h +++ b/src/libs/qmlpuppetcommunication/commands/puppettocreatorcommand.h @@ -18,6 +18,7 @@ public: ActiveSceneChanged, ActiveSplitChanged, RenderModelNodePreviewImage, + Import3DPreviewImage, Import3DSupport, NodeAtPos, BakeLightsProgress, diff --git a/src/libs/qmlpuppetcommunication/interfaces/nodeinstanceglobal.h b/src/libs/qmlpuppetcommunication/interfaces/nodeinstanceglobal.h index 3fdd7bf6cc5..06df0f79754 100644 --- a/src/libs/qmlpuppetcommunication/interfaces/nodeinstanceglobal.h +++ b/src/libs/qmlpuppetcommunication/interfaces/nodeinstanceglobal.h @@ -55,7 +55,8 @@ enum class View3DActionType { EditCameraRotation, EditCameraMove, EditCameraStopAllMoves, - SetLastSceneEnvData + SetLastSceneEnvData, + Import3dUpdatePreviewImage }; constexpr bool isNanotraceEnabled() diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index 2d67fa360f7..039918f91c2 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -741,6 +741,8 @@ extend_qtc_plugin(QmlDesigner assetimportupdatetreeitemdelegate.cpp assetimportupdatetreeitemdelegate.h assetimportupdatetreemodel.cpp assetimportupdatetreemodel.h assetimportupdatetreeview.cpp assetimportupdatetreeview.h + import3dcanvas.cpp import3dcanvas.h + import3dconnectionmanager.cpp import3dconnectionmanager.h itemlibrary.qrc itemlibraryconstants.h itemlibraryimageprovider.cpp itemlibraryimageprovider.h diff --git a/src/plugins/qmldesigner/components/itemlibrary/import3dcanvas.cpp b/src/plugins/qmldesigner/components/itemlibrary/import3dcanvas.cpp new file mode 100644 index 00000000000..87014cbe603 --- /dev/null +++ b/src/plugins/qmldesigner/components/itemlibrary/import3dcanvas.cpp @@ -0,0 +1,56 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "import3dcanvas.h" + +#include +#include +#include + +namespace QmlDesigner { + +static QImage createGradientImage(int width, int height) { + QImage image(width, height, QImage::Format_ARGB32_Premultiplied); + + QLinearGradient gradient(0, 0, 0, height); + gradient.setColorAt(0, QColor(0x999999)); + gradient.setColorAt(1, QColor(0x222222)); + + QPainter painter(&image); + painter.fillRect(0, 0, width, height, gradient); + + return image; +} + +Import3dCanvas::Import3dCanvas(QWidget *parent) + : QWidget(parent) +{ +} + +void Import3dCanvas::updateRenderImage(const QImage &img) +{ + m_image = img; + update(); +} + +void Import3dCanvas::paintEvent([[maybe_unused]] QPaintEvent *e) +{ + QWidget::paintEvent(e); + + QPainter painter(this); + + if (m_image.isNull()) { + QImage image = createGradientImage(width(), height()); + painter.drawImage(rect(), image, QRect(0, 0, image.width(), image.height())); + } else { + painter.drawImage(rect(), m_image, QRect(0, 0, m_image.width(), m_image.height())); + } +} + +void Import3dCanvas::resizeEvent(QResizeEvent *) +{ + emit requestImageUpdate(); +} + + +} diff --git a/src/plugins/qmldesigner/components/itemlibrary/import3dcanvas.h b/src/plugins/qmldesigner/components/itemlibrary/import3dcanvas.h new file mode 100644 index 00000000000..22bcc04d70f --- /dev/null +++ b/src/plugins/qmldesigner/components/itemlibrary/import3dcanvas.h @@ -0,0 +1,32 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +#pragma once + +#include +#include +#include +#include + +namespace QmlDesigner { + +class Import3dCanvas : public QWidget +{ + Q_OBJECT + +public: + Import3dCanvas(QWidget *parent); + + void updateRenderImage(const QImage &img); + +signals: + void requestImageUpdate(); + +protected: + void paintEvent(QPaintEvent *e) override; + void resizeEvent(QResizeEvent *e) override; + +private: + QImage m_image; +}; + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/itemlibrary/import3dconnectionmanager.cpp b/src/plugins/qmldesigner/components/itemlibrary/import3dconnectionmanager.cpp new file mode 100644 index 00000000000..4c455e3c6d2 --- /dev/null +++ b/src/plugins/qmldesigner/components/itemlibrary/import3dconnectionmanager.cpp @@ -0,0 +1,47 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "import3dconnectionmanager.h" + +#include +#include + +#include + +namespace QmlDesigner { + +Import3dConnectionManager::Import3dConnectionManager() +{ + connections().clear(); // Remove default interactive puppets + connections().emplace_back("Import 3D", "import3dmode"); +} + +void Import3dConnectionManager::setPreviewImageCallback(ImageCallback callback) +{ + m_previewImageCallback = std::move(callback); +} + +void Import3dConnectionManager::dispatchCommand(const QVariant &command, + ConnectionManagerInterface::Connection &connection) +{ + static const int commandType = QMetaType::type("PuppetToCreatorCommand"); + + if (command.typeId() == commandType) { + auto cmd = command.value(); + switch (cmd.type()) { + case PuppetToCreatorCommand::Import3DPreviewImage: { + ImageContainer container = qvariant_cast(cmd.data()); + QImage image = container.image(); + if (!image.isNull()) + m_previewImageCallback(image); + break; + } + default: + break; + } + } else { + InteractiveConnectionManager::dispatchCommand(command, connection); + } +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/itemlibrary/import3dconnectionmanager.h b/src/plugins/qmldesigner/components/itemlibrary/import3dconnectionmanager.h new file mode 100644 index 00000000000..458d612ad2d --- /dev/null +++ b/src/plugins/qmldesigner/components/itemlibrary/import3dconnectionmanager.h @@ -0,0 +1,28 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include "interactiveconnectionmanager.h" + +QT_FORWARD_DECLARE_CLASS(QImage) + +namespace QmlDesigner { + +class Import3dConnectionManager : public InteractiveConnectionManager +{ +public: + using ImageCallback = std::function; + + Import3dConnectionManager(); + + void setPreviewImageCallback(ImageCallback callback); + +protected: + void dispatchCommand(const QVariant &command, Connection &connection) override; + +private: + ImageCallback m_previewImageCallback; +}; + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.cpp index 2c690726029..4e49cd2b807 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.cpp @@ -4,15 +4,19 @@ #include "itemlibraryassetimportdialog.h" #include "ui_itemlibraryassetimportdialog.h" +#include "import3dcanvas.h" +#include "import3dconnectionmanager.h" + #include #include +#include #include #include #include +#include #include #include -#include #include #include @@ -74,9 +78,10 @@ ItemLibraryAssetImportDialog::ItemLibraryAssetImportDialog( const QStringList &importFiles, const QString &defaulTargetDirectory, const QVariantMap &supportedExts, const QVariantMap &supportedOpts, const QJsonObject &defaultOpts, const QSet &preselectedFilesForOverwrite, - QWidget *parent) + AbstractView *view, QWidget *parent) : QDialog(parent) , ui(new Ui::ItemLibraryAssetImportDialog) + , m_view(view) , m_importer(this) , m_preselectedFilesForOverwrite(preselectedFilesForOverwrite) { @@ -107,11 +112,9 @@ ItemLibraryAssetImportDialog::ItemLibraryAssetImportDialog( if (m_quick3DFiles.size() != importFiles.size()) addWarning("Cannot import 3D and other assets simultaneously. Skipping non-3D assets."); - ui->buttonBox->button(QDialogButtonBox::Ok)->setText(tr("Import")); - connect(ui->buttonBox->button(QDialogButtonBox::Ok), &QPushButton::clicked, - this, &ItemLibraryAssetImportDialog::onImport); + connect(ui->importButton, &QPushButton::clicked, this, &ItemLibraryAssetImportDialog::onImport); - ui->buttonBox->button(QDialogButtonBox::Ok)->setDefault(true); + ui->importButton->setDefault(true); ui->advancedSettingsButton->setStyleSheet( "QPushButton#advancedSettingsButton {background-color: transparent}"); @@ -210,10 +213,14 @@ ItemLibraryAssetImportDialog::ItemLibraryAssetImportDialog( ui->tabWidget->setCurrentIndex(0); } - connect(ui->buttonBox->button(QDialogButtonBox::Close), &QPushButton::clicked, + connect(ui->closeButton, &QPushButton::clicked, this, &ItemLibraryAssetImportDialog::onClose); + connect(ui->acceptButton, &QPushButton::clicked, + this, &ItemLibraryAssetImportDialog::onAccept); connect(ui->tabWidget, &QTabWidget::currentChanged, this, &ItemLibraryAssetImportDialog::updateUi); + connect(canvas(), &Import3dCanvas::requestImageUpdate, + this, &ItemLibraryAssetImportDialog::onRequestImageUpdate); connect(&m_importer, &ItemLibraryAssetImporter::errorReported, this, &ItemLibraryAssetImportDialog::addError); @@ -227,6 +234,8 @@ ItemLibraryAssetImportDialog::ItemLibraryAssetImportDialog( this, &ItemLibraryAssetImportDialog::onImportFinished); connect(&m_importer, &ItemLibraryAssetImporter::progressChanged, this, &ItemLibraryAssetImportDialog::setImportProgress); + connect(&m_importer, &ItemLibraryAssetImporter::importReadyForPreview, + this, &ItemLibraryAssetImportDialog::onImportReadyForPreview); addInfo(tr("Select import options and press \"Import\" to import the following files:")); for (const auto &file : std::as_const(m_quick3DFiles)) @@ -240,10 +249,12 @@ ItemLibraryAssetImportDialog::ItemLibraryAssetImportDialog( ItemLibraryAssetImportDialog::~ItemLibraryAssetImportDialog() { + cleanupPreviewPuppet(); delete ui; } -void ItemLibraryAssetImportDialog::updateImport(const ModelNode &updateNode, +void ItemLibraryAssetImportDialog::updateImport(AbstractView *view, + const ModelNode &updateNode, const QVariantMap &supportedExts, const QVariantMap &supportedOpts) { @@ -332,7 +343,8 @@ void ItemLibraryAssetImportDialog::updateImport(const ModelNode &updateNode, {sourceInfo.absoluteFilePath()}, node.model()->fileUrl().toLocalFile(), supportedExts, supportedOpts, options, - preselectedFiles, Core::ICore::dialogParent()); + preselectedFiles, view, + Core::ICore::dialogParent()); importDlg->show(); } else { @@ -829,6 +841,140 @@ bool ItemLibraryAssetImportDialog::isHiddenOption(const QString &id) return hiddenOptions.contains(id); } +void ItemLibraryAssetImportDialog::startPreview() +{ + cleanupPreviewPuppet(); + + // Preview is done via custom QML file added into the temporary folder of the preview + QString previewQml = +R"( +import QtQuick +import QtQuick3D + +Rectangle { + id: root + width: %1 + height: %2 + + property string sceneModelName: "%3" + property alias view3d: view3d + property string extents + + gradient: Gradient { + GradientStop { position: 1.0; color: "#222222" } + GradientStop { position: 0.0; color: "#999999" } + } + + View3D { + id: view3d + anchors.fill: parent + camera: viewCamera + + environment: SceneEnvironment { + antialiasingMode: SceneEnvironment.MSAA + antialiasingQuality: SceneEnvironment.VeryHigh + } + + PerspectiveCamera { + id: viewCamera + z: 600 + y: 600 + x: 600 + eulerRotation.x: -45 + eulerRotation.y: -45 + clipFar: 100000 + clipNear: 10 + } + + DirectionalLight { + rotation: viewCamera.rotation + } + } + + Text { + anchors.bottom: parent.bottom + anchors.left: parent.left + color: "white" + text: root.extents + font.pixelSize: 14 + } +} +)"; + + QSize size = canvas()->size(); + previewQml = previewQml.arg(size.width()).arg(size.height()).arg(m_previewCompName); + + m_previewFile.writeFileContents(previewQml.toUtf8()); + + if (!m_previewFile.exists()) { + addWarning("Failed to write preview file."); + return; + } + + m_connectionManager = new Import3dConnectionManager; + m_rewriterView = new RewriterView{m_view->externalDependencies(), RewriterView::Amend}; + m_nodeInstanceView = new NodeInstanceView{*m_connectionManager, m_view->externalDependencies()}; + +#ifdef QDS_USE_PROJECTSTORAGE + m_model = m_view->model()->createModel("Item"); +#else + m_model = QmlDesigner::Model::create("QtQuick/Item", 2, 1); + m_model->setFileUrl(m_previewFile.toUrl()); +#endif + + auto textDocument = std::make_unique(previewQml); + auto modifier = std::make_unique(textDocument.get(), + QTextCursor{textDocument.get()}); + m_rewriterView->setTextModifier(modifier.get()); + m_model->setRewriterView(m_rewriterView); + + if (!m_rewriterView->errors().isEmpty()) { + addWarning("Preview scene creation failed."); + cleanupPreviewPuppet(); + return; + } + + m_nodeInstanceView->setTarget(m_view->nodeInstanceView()->target()); + + auto previewImageCallback = [this](const QImage &image) { + canvas()->updateRenderImage(image); + }; + + auto crashCallback = [&] { + addWarning("Preview process crashed."); + cleanupPreviewPuppet(); + }; + + m_connectionManager->setPreviewImageCallback(std::move(previewImageCallback)); + m_nodeInstanceView->setCrashCallback(std::move(crashCallback)); + + m_model->setNodeInstanceView(m_nodeInstanceView); +} + +void ItemLibraryAssetImportDialog::cleanupPreviewPuppet() +{ + if (m_model) { + m_model->setNodeInstanceView({}); + m_model->setRewriterView({}); + m_model.reset(); + } + + if (m_nodeInstanceView) + m_nodeInstanceView->setCrashCallback({}); + + if (m_connectionManager) + m_connectionManager->setPreviewImageCallback({}); + + delete m_rewriterView; + delete m_nodeInstanceView; + delete m_connectionManager; +} + +Import3dCanvas *ItemLibraryAssetImportDialog::canvas() +{ + return ui->import3dcanvas; +} + void ItemLibraryAssetImportDialog::resizeEvent(QResizeEvent *event) { m_dialogHeight = event->size().height(); @@ -837,8 +983,8 @@ void ItemLibraryAssetImportDialog::resizeEvent(QResizeEvent *event) void ItemLibraryAssetImportDialog::setCloseButtonState(bool importing) { - ui->buttonBox->button(QDialogButtonBox::Close)->setEnabled(true); - ui->buttonBox->button(QDialogButtonBox::Close)->setText(importing ? tr("Cancel") : tr("Close")); + ui->closeButton->setEnabled(true); + ui->closeButton->setText(importing ? tr("Cancel") : tr("Close")); } void ItemLibraryAssetImportDialog::addError(const QString &error, const QString &srcPath) @@ -860,14 +1006,19 @@ void ItemLibraryAssetImportDialog::addInfo(const QString &info, const QString &s void ItemLibraryAssetImportDialog::onImport() { - ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); + ui->acceptButton->setEnabled(false); + ui->importButton->setEnabled(false); setCloseButtonState(true); ui->progressBar->setValue(0); if (!m_quick3DFiles.isEmpty()) { - m_importer.importQuick3D(m_quick3DFiles, m_quick3DImportPath, - m_importOptions, m_extToImportOptionsMap, - m_preselectedFilesForOverwrite); + if (!m_previewCompName.isEmpty()) { + m_importer.reImportQuick3D(m_previewCompName, m_importOptions); + } else { + m_importer.importQuick3D(m_quick3DFiles, m_quick3DImportPath, + m_importOptions, m_extToImportOptionsMap, + m_preselectedFilesForOverwrite); + } } } @@ -881,10 +1032,28 @@ void ItemLibraryAssetImportDialog::setImportProgress(int value, const QString &t ui->progressBar->setValue(value); } +void ItemLibraryAssetImportDialog::onImportReadyForPreview(const QString &path, const QString &compName) +{ + m_previewFile = Utils::FilePath::fromString(path).pathAppended(m_importer.previewFileName()); + m_previewCompName = compName; + QTimer::singleShot(0, this, &ItemLibraryAssetImportDialog::startPreview); + + ui->acceptButton->setEnabled(true); + ui->importButton->setEnabled(true); + + addInfo(tr("Generating import preview for %1.").arg(compName)); +} + +void ItemLibraryAssetImportDialog::onRequestImageUpdate() +{ + if (m_nodeInstanceView) + m_nodeInstanceView->view3DAction(View3DActionType::Import3dUpdatePreviewImage, canvas()->size()); +} + void ItemLibraryAssetImportDialog::onImportNearlyFinished() { // Canceling import is no longer doable - ui->buttonBox->button(QDialogButtonBox::Close)->setEnabled(false); + ui->closeButton->setEnabled(false); } void ItemLibraryAssetImportDialog::onImportFinished() @@ -920,6 +1089,16 @@ void ItemLibraryAssetImportDialog::onClose() } } +void ItemLibraryAssetImportDialog::onAccept() +{ + cleanupPreviewPuppet(); + + ui->importButton->setEnabled(false); + ui->acceptButton->setEnabled(false); + + m_importer.finalizeQuick3DImport(); +} + void ItemLibraryAssetImportDialog::toggleAdvanced() { m_advancedMode = !m_advancedMode; diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.h b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.h index c5da4782325..efbd189c2fd 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.h +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.h @@ -3,14 +3,19 @@ #pragma once #include "itemlibraryassetimporter.h" -#include "modelnode.h" + +#include + +#include #include #include +#include #include QT_BEGIN_NAMESPACE class QGridLayout; +class QPushButton; QT_END_NAMESPACE namespace Utils { @@ -19,6 +24,10 @@ class OutputFormatter; namespace QmlDesigner { class ItemLibraryAssetImporter; +class Import3dCanvas; +class Import3dConnectionManager; +class NodeInstanceView; +class RewriterView; namespace Ui { class ItemLibraryAssetImportDialog; @@ -35,10 +44,12 @@ public: const QVariantMap &supportedOpts, const QJsonObject &defaultOpts, const QSet &preselectedFilesForOverwrite, + AbstractView *view, QWidget *parent = nullptr); ~ItemLibraryAssetImportDialog(); - static void updateImport(const ModelNode &updateNode, + static void updateImport(AbstractView *view, + const ModelNode &updateNode, const QVariantMap &supportedExts, const QVariantMap &supportedOpts); @@ -55,9 +66,12 @@ private: void onImport(); void setImportProgress(int value, const QString &text); + void onImportReadyForPreview(const QString &path, const QString &compName); + void onRequestImageUpdate(); void onImportNearlyFinished(); void onImportFinished(); void onClose(); + void onAccept(); void toggleAdvanced(); void createTab(const QString &tabLabel, int optionsIndex, const QJsonObject &groups); @@ -69,8 +83,19 @@ private: bool isSimpleOption(const QString &id); bool isHiddenOption(const QString &id); + void startPreview(); + void cleanupPreviewPuppet(); + Import3dCanvas *canvas(); + Ui::ItemLibraryAssetImportDialog *ui = nullptr; Utils::OutputFormatter *m_outputFormatter = nullptr; + QPointer m_connectionManager; + QPointer m_nodeInstanceView; + QPointer m_rewriterView; + QPointer m_view; + ModelPointer m_model; + Utils::FilePath m_previewFile; + QString m_previewCompName; struct OptionsData { diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.ui b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.ui index 081bc36a3d3..a35875a5eb7 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.ui +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.ui @@ -6,15 +6,15 @@ 0 0 - 630 + 1100 350 Asset Import - - + + @@ -24,6 +24,12 @@ 2 + + + 550 + 0 + + 0 @@ -98,6 +104,12 @@ 0 + + + 0 + 0 + + @@ -111,16 +123,74 @@ - - - QDialogButtonBox::Close|QDialogButtonBox::Ok - - + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Close + + + + + + + Import + + + + + + + false + + + Accept + + + + + + + + + 1 + 0 + + + + + 300 + 300 + + + + + + + Import3dCanvas + QWidget +