From 21b598805e5b7bfba872cdce4e0bbb4c41f585b0 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Thu, 30 Mar 2023 11:29:20 +0200 Subject: [PATCH 001/192] QmlDesigner: Delete tool bar and status bar with plugin The parent of the tool and status bar are widgets in icore. So they are not deleted at the same time as the rest of the qmldesigner plugin. So the connections are still there and you get dangling pointer. Deleting them with plugin prevents this dangling connections. Change-Id: I489d18c037c67ce34326651e693e74c4d951c261 Reviewed-by: Qt CI Bot Reviewed-by: Thomas Hartmann --- .../components/toolbar/toolbar.cpp | 24 +++++++++++-------- .../qmldesigner/components/toolbar/toolbar.h | 8 +++++-- src/plugins/qmldesigner/qmldesignerplugin.cpp | 6 +++-- 3 files changed, 24 insertions(+), 14 deletions(-) diff --git a/src/plugins/qmldesigner/components/toolbar/toolbar.cpp b/src/plugins/qmldesigner/components/toolbar/toolbar.cpp index 856546c1063..939595a2a7f 100644 --- a/src/plugins/qmldesigner/components/toolbar/toolbar.cpp +++ b/src/plugins/qmldesigner/components/toolbar/toolbar.cpp @@ -47,10 +47,10 @@ Utils::FilePath qmlSourcesPath() return Core::ICore::resourcePath("qmldesigner/toolbar"); } -void ToolBar::create() +std::unique_ptr ToolBar::create() { if (!isVisible()) - return; + return nullptr; ToolBarBackend::registerDeclarativeType(); @@ -58,7 +58,7 @@ void ToolBar::create() //Core::ICore::statusBar()->hide(); - auto toolBar = new QToolBar; + auto toolBar = std::make_unique(); toolBar->setObjectName("QDS-TOOLBAR"); toolBar->setContextMenuPolicy(Qt::PreventContextMenu); @@ -78,24 +78,26 @@ void ToolBar::create() quickWidget->engine()->addImportPath(propertyEditorResourcesPath().toString() + "/imports"); Utils::FilePath qmlFilePath = qmlSourcesPath() / "Main.qml"; - QTC_ASSERT(qmlFilePath.exists(), return); + QTC_ASSERT(qmlFilePath.exists(), return nullptr); Theme::setupTheme(quickWidget->engine()); quickWidget->setSource(QUrl::fromLocalFile(qmlFilePath.toFSPathString())); toolBar->addWidget(quickWidget); - window->addToolBar(toolBar); + window->addToolBar(toolBar.get()); + + return toolBar; } -void ToolBar::createStatusBar() +std::unique_ptr ToolBar::createStatusBar() { if (!isVisible()) - return; + return nullptr; ToolBarBackend::registerDeclarativeType(); - auto quickWidget = new StudioQuickWidget; + auto quickWidget = std::make_unique(); quickWidget->setFixedHeight(Theme::toolbarSize()); quickWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); @@ -107,7 +109,7 @@ void ToolBar::createStatusBar() quickWidget->engine()->addImportPath(propertyEditorResourcesPath().toString() + "/imports"); Utils::FilePath qmlFilePath = qmlSourcesStatusBarPath().pathAppended("/Main.qml"); - QTC_ASSERT(qmlFilePath.exists(), return); + QTC_ASSERT(qmlFilePath.exists(), return nullptr); Theme::setupTheme(quickWidget->engine()); @@ -117,8 +119,10 @@ void ToolBar::createStatusBar() w->hide(); } - Core::ICore::statusBar()->addWidget(quickWidget); + Core::ICore::statusBar()->addWidget(quickWidget.get()); Core::ICore::statusBar()->setFixedHeight(Theme::toolbarSize()); + + return quickWidget; } bool ToolBar::isVisible() diff --git a/src/plugins/qmldesigner/components/toolbar/toolbar.h b/src/plugins/qmldesigner/components/toolbar/toolbar.h index 8537757cdf4..1cdb2121c65 100644 --- a/src/plugins/qmldesigner/components/toolbar/toolbar.h +++ b/src/plugins/qmldesigner/components/toolbar/toolbar.h @@ -3,14 +3,18 @@ #pragma once +#include + +#include + namespace QmlDesigner { class ToolBar { public: - static void create(); - static void createStatusBar(); + static std::unique_ptr create(); + static std::unique_ptr createStatusBar(); static bool isVisible(); }; diff --git a/src/plugins/qmldesigner/qmldesignerplugin.cpp b/src/plugins/qmldesigner/qmldesignerplugin.cpp index 2d274965f83..c41e272ad26 100644 --- a/src/plugins/qmldesigner/qmldesignerplugin.cpp +++ b/src/plugins/qmldesigner/qmldesignerplugin.cpp @@ -144,6 +144,8 @@ public: DesignModeWidget mainWidget; QtQuickDesignerFactory m_qtQuickDesignerFactory; bool blockEditorChange = false; + std::unique_ptr toolBar; + std::unique_ptr statusBar; }; QmlDesignerPlugin *QmlDesignerPlugin::m_instance = nullptr; @@ -273,8 +275,8 @@ bool QmlDesignerPlugin::initialize(const QStringList & /*arguments*/, QString *e }); if (QmlProjectManager::QmlProject::isQtDesignStudio()) { - ToolBar::create(); - ToolBar::createStatusBar(); + d->toolBar = ToolBar::create(); + d->statusBar = ToolBar::createStatusBar(); } return true; From 129153e663b31c62009c618b67b3b7bfec0c3578 Mon Sep 17 00:00:00 2001 From: Samuel Ghinet Date: Tue, 28 Mar 2023 15:32:37 +0300 Subject: [PATCH 002/192] QmlDesigner: Allow user to download multiple textures at once Task-number: QDS-9224 Change-Id: Ia1ec8f8b0951e8dd31d47f64509d39137cdeaa41 Reviewed-by: Miikka Heikkinen Reviewed-by: Mahmoud Badri --- .../ContentLibraryTexture.qml | 9 --------- .../contentlibrary/contentlibrarywidget.cpp | 14 -------------- .../contentlibrary/contentlibrarywidget.h | 3 --- 3 files changed, 26 deletions(-) diff --git a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryTexture.qml b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryTexture.qml index a556aad72ad..924f8ae4e76 100644 --- a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryTexture.qml +++ b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryTexture.qml @@ -147,9 +147,6 @@ Item { if (root.downloadState !== "" && root.downloadState !== "failed") return - if (!ContentLibraryBackend.rootView.markTextureDownloading()) - return - progressBar.visible = true tooltip.visible = false root.progressText = qsTr("Downloading...") @@ -223,14 +220,10 @@ Item { root.progressValue = 0 root.downloadState = "" - - ContentLibraryBackend.rootView.markNoTextureDownloading() } onDownloadFailed: { root.downloadState = "failed" - - ContentLibraryBackend.rootView.markNoTextureDownloading() } } @@ -244,8 +237,6 @@ Item { onFinishedChanged: { modelData.setDownloaded() root.downloadState = modelData.isDownloaded() ? "downloaded" : "failed" - - ContentLibraryBackend.rootView.markNoTextureDownloading() } } } diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.cpp index a1dfff5ef53..f83fc241856 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.cpp @@ -436,18 +436,4 @@ QPointer ContentLibraryWidget::environmentsModel() return m_environmentsModel; } -bool ContentLibraryWidget::markTextureDownloading() -{ - if (m_anyTextureBeingDownloaded) - return false; - - m_anyTextureBeingDownloaded = true; - return true; // let the caller know it can begin download -} - -void ContentLibraryWidget::markNoTextureDownloading() -{ - m_anyTextureBeingDownloaded = false; // allow other textures to be downloaded -} - } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.h index 519d6fcf780..6d4ba51a300 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.h +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.h @@ -61,8 +61,6 @@ public: Q_INVOKABLE void addTexture(QmlDesigner::ContentLibraryTexture *tex); Q_INVOKABLE void addLightProbe(QmlDesigner::ContentLibraryTexture *tex); Q_INVOKABLE void updateSceneEnvState(); - Q_INVOKABLE bool markTextureDownloading(); - Q_INVOKABLE void markNoTextureDownloading(); signals: void bundleMaterialDragStarted(QmlDesigner::ContentLibraryMaterial *bundleMat); @@ -102,7 +100,6 @@ private: bool m_hasMaterialLibrary = false; bool m_hasQuick3DImport = false; bool m_isDragging = false; - bool m_anyTextureBeingDownloaded = false; QString m_baseUrl; QString m_texturesUrl; QString m_environmentsUrl; From b97ea976cdefe4fdca6200cb90eba3aca9f24b47 Mon Sep 17 00:00:00 2001 From: Mats Honkamaa Date: Thu, 30 Mar 2023 13:01:26 +0300 Subject: [PATCH 003/192] Doc: Remove conditional statements from docs Task-number: QDS-9484 Change-Id: Ib494dbf729ba77e64aa1fb1060b6dcc31fbf818c Reviewed-by: Leena Miettinen --- .../components/qtquick-animation-types.qdoc | 7 +--- .../src/components/qtquick-buttons.qdoc | 13 +------ .../qtquick-component-context-menu.qdocinc | 4 -- .../components/qtquick-components-custom.qdoc | 10 ----- .../src/components/qtquick-components.qdoc | 4 -- .../src/components/qtquick-controls.qdoc | 39 ------------------- .../src/components/qtquick-data-models.qdoc | 8 ---- .../src/components/qtquick-images.qdoc | 4 -- .../qtquick-pathview-editor.qdocinc | 2 - .../src/components/qtquick-positioning.qdoc | 16 +------- .../components/qtquick-preset-components.qdoc | 2 - .../src/components/qtquick-shapes.qdoc | 11 ------ .../src/components/qtquick-text.qdoc | 6 --- .../qtquick-user-interaction-methods.qdoc | 5 --- .../overviews/qtquick-animation-overview.qdoc | 4 -- .../src/overviews/qtquick-annotations.qdoc | 5 --- .../overviews/qtquick-creating-ui-logic.qdoc | 6 --- .../src/overviews/qtquick-fonts.qdoc | 5 --- .../src/overviews/qtquick-prototyping.qdoc | 26 ------------- .../src/overviews/qtquick-uis.qdoc | 28 ------------- .../src/qtdesignstudio-advanced.qdoc | 8 +--- .../exporting-3d/exporting-3d-assets.qdoc | 15 ------- .../exporting-3d/exporting-from-maya.qdoc | 4 -- .../exporting-3d/exporting-from-qt3ds.qdoc | 4 -- .../qtdesignstudio-3d-importing.qdoc | 5 +-- .../qtdesignstudio-optimized-3d-scenes.qdoc | 8 ---- .../views/creator-logical-operators.qdocinc | 3 -- .../src/views/qtquick-components-view.qdoc | 2 - .../qtquick-connection-editor-bindings.qdoc | 4 -- .../qtquick-connection-editor-properties.qdoc | 5 +-- .../qtquick-connection-editor-signals.qdoc | 5 --- .../src/views/qtquick-connection-editor.qdoc | 6 --- .../src/views/qtquick-connection-view.qdoc | 12 +----- .../src/views/qtquick-designer.qdoc | 8 +--- .../src/views/qtquick-properties.qdoc | 2 - .../src/views/qtquick-states.qdoc | 13 ------- .../src/views/qtquick-text-editor.qdoc | 4 -- .../src/views/qtquick-timeline-view.qdoc | 3 -- .../src/views/qtquick-timeline.qdoc | 3 +- .../src/views/qtquick-transition-editor.qdoc | 4 -- .../src/views/studio-workspaces.qdoc | 4 -- 41 files changed, 10 insertions(+), 317 deletions(-) diff --git a/doc/qtdesignstudio/src/components/qtquick-animation-types.qdoc b/doc/qtdesignstudio/src/components/qtquick-animation-types.qdoc index f51b3d376ac..a8bddc66ff8 100644 --- a/doc/qtdesignstudio/src/components/qtquick-animation-types.qdoc +++ b/doc/qtdesignstudio/src/components/qtquick-animation-types.qdoc @@ -3,11 +3,8 @@ /*! \page quick-animations.html - \if defined(qtdesignstudio) + \previouspage quick-logic-helpers.html - \else - \previouspage quick-data-models.html - \endif \nextpage studio-3d-view.html \title Animations @@ -72,10 +69,8 @@ \image qtquick-number-animation.gif "Number animation" - \if defined(qtdesignstudio) For an example of using property animation to animate the scale and opacity of components, see the \l{Coffee Machine} example. - \endif \section2 Setting Non-Animated Properties diff --git a/doc/qtdesignstudio/src/components/qtquick-buttons.qdoc b/doc/qtdesignstudio/src/components/qtquick-buttons.qdoc index d2a1a1760dd..071cf04554b 100644 --- a/doc/qtdesignstudio/src/components/qtquick-buttons.qdoc +++ b/doc/qtdesignstudio/src/components/qtquick-buttons.qdoc @@ -19,11 +19,7 @@ \list 1 \li Select \uicontrol File > \uicontrol {New File} > - \if defined(qtcreator) - \uicontrol Qt > \uicontrol {Qt Quick UI File} > - \else \uicontrol {Qt Quick Files} > \uicontrol {Qt Quick UI File} > - \endif \uicontrol Choose to create a \l{UI Files}{UI file} called Button.ui.qml (for example). @@ -105,11 +101,7 @@ /*! \previouspage quick-buttons.html \page quick-scalable-image.html - \if defined(qtdesignstudio) \nextpage qtquick-properties.html - \else - \nextpage studio-optimized-3d-scenes.html - \endif \title Creating Scalable Buttons and Borders @@ -136,11 +128,8 @@ To create a button component, select \uicontrol File > \uicontrol {New File} > - \if defined(qtcreator) - \uicontrol Qt > \uicontrol {Qt Quick UI File} > - \else \uicontrol {Qt Quick Files} > \uicontrol {Qt Quick UI File} > - \endif + \uicontrol Choose to create a \l{UI Files}{UI file} called Button.ui.qml (for example). diff --git a/doc/qtdesignstudio/src/components/qtquick-component-context-menu.qdocinc b/doc/qtdesignstudio/src/components/qtquick-component-context-menu.qdocinc index 1d6b1cd08a7..34140b88947 100644 --- a/doc/qtdesignstudio/src/components/qtquick-component-context-menu.qdocinc +++ b/doc/qtdesignstudio/src/components/qtquick-component-context-menu.qdocinc @@ -22,11 +22,9 @@ \row \li Anchors \li \l{Setting Anchors and Margins} - \if defined(qtdesignstudio) \row \li Group \li \l{Organizing Components} - \endif \row \li Position \li \l{Using Positioners} @@ -39,11 +37,9 @@ \row \li Timeline \li \l{Creating a Timeline} - \if defined(qtdesignstudio) \row \li Event List \li \l{Simulating Events} - \endif \row \li Edit Color \li \l{Editing Properties Inline} diff --git a/doc/qtdesignstudio/src/components/qtquick-components-custom.qdoc b/doc/qtdesignstudio/src/components/qtquick-components-custom.qdoc index 7d394de29e8..fc6ccdcf3cb 100644 --- a/doc/qtdesignstudio/src/components/qtquick-components-custom.qdoc +++ b/doc/qtdesignstudio/src/components/qtquick-components-custom.qdoc @@ -26,11 +26,7 @@ \list 1 \li Select \uicontrol File > \uicontrol {New File} > - \if defined(qtcreator) - \uicontrol Qt > \uicontrol {Qt Quick UI File} > - \else \uicontrol {Qt Quick Files} > \uicontrol {Qt Quick UI File} > - \endif \uicontrol Choose to create a new \c .ui.qml file. \note Components are listed in \uicontrol Components > \uicontrol {My Components} only if the filename begins with a @@ -80,9 +76,7 @@ that screen. For example, \e myButton_myMenu_home, \e myButton_myMenu_profile, and \e myButton_myMenu_settings - \if defined(qtdesignstudio) \include qtdesignstudio-components.qdocinc creating studio components - \endif \section1 Turning Component Instances into Custom Components @@ -107,11 +101,7 @@ additional ways of opening base components, see \l{Moving Within Components}. For an example of creating a reusable custom component, see - \if defined(qtcreator) - \l{Creating a Mobile Application}. - \else \l{Progress Bar}. - \endif Custom components are listed in \uicontrol Components > \uicontrol {My Components}, and you can use instances of them to build diff --git a/doc/qtdesignstudio/src/components/qtquick-components.qdoc b/doc/qtdesignstudio/src/components/qtquick-components.qdoc index 092a8bea3b1..edd28485db3 100644 --- a/doc/qtdesignstudio/src/components/qtquick-components.qdoc +++ b/doc/qtdesignstudio/src/components/qtquick-components.qdoc @@ -3,11 +3,7 @@ /*! \page quick-components.html - \if defined(qtdesignstudio) \previouspage studio-flow-external-events.html - \else - \previouspage creator-using-qt-quick-designer.html - \endif \nextpage quick-preset-components.html \title Using Components diff --git a/doc/qtdesignstudio/src/components/qtquick-controls.qdoc b/doc/qtdesignstudio/src/components/qtquick-controls.qdoc index abcdde5e5b0..15b2189d2ac 100644 --- a/doc/qtdesignstudio/src/components/qtquick-controls.qdoc +++ b/doc/qtdesignstudio/src/components/qtquick-controls.qdoc @@ -94,12 +94,10 @@ \image qtquickcontrols2-button.gif "Qt Quick Controls - Button" - \if defined(qtdesignstudio) Alternatively, you can use a wizard to create a \l{Creating Custom Controls} {custom button}: \image studio-custom-button.gif "Custom button" - \endif A button can be pushed or clicked by users. Typically, buttons are used to perform an action or to answer a question. For example, \e OK, @@ -133,14 +131,12 @@ \image qtquickcontrols2-button-flat.gif "Flat button" - \if defined(qtdesignstudio) \section3 Icon Buttons To create a button that contains an icon, use the wizard template to \l{Creating Custom Controls}{create a custom button} and drag-and-drop the icon to the button background component. For an example of using the wizard template, see \l{Creating a Push Button}. - \endif \section2 Delay Button @@ -162,12 +158,10 @@ \image qtquickcontrols2-checkbox.gif "Check boxes" - \if defined(qtdesignstudio) Alternatively, you can use a wizard to create \l{Creating Custom Controls} {custom check boxes}: \image studio-custom-check-box.gif "Custom check boxes" - \endif A check box presents an option button that can be toggled on (checked) or off (unchecked). Check boxes are typically used to select @@ -255,12 +249,10 @@ \image qtquickcontrols2-switch.gif "Switch" - \if defined(qtdesignstudio) Alternatively, you can use a wizard to create a \l{Creating Custom Controls} {custom switch}: \image studio-custom-switch.gif "Custom switch" - \endif A switch is an option button that can be dragged or toggled on (checked) or off (unchecked). Switches are typically used to select between @@ -441,12 +433,10 @@ \image qtquickcontrols2-slider.gif "Slider" - \if defined(qtdesignstudio) Alternatively, you can use a wizard to create a \l{Creating Custom Controls} {custom slider}: \image studio-custom-slider.gif "Custom slider" - \endif A slider is used to select a value by sliding a handle along a track, whereas \uicontrol {Range Slider} is used to select a range @@ -463,12 +453,10 @@ \image qtquickcontrols2-dial-no-wrap.gif "Dial" - \if defined(qtdesignstudio) Alternatively, you can use a wizard to create a \l{Creating Custom Controls} {custom dial}: \image studio-custom-dial.gif "Custom dial" - \endif In the \uicontrol From and \uicontrol To fields, set the range of the slider or dial. Set the value of the slide handle or dial in the @@ -514,12 +502,10 @@ \image qtquickcontrols2-spinbox.png "Spin Box" - \if defined(qtdesignstudio) Alternatively, you can use a wizard to create a \l{Creating Custom Controls} {custom spin box}: \image studio-custom-spinbox.gif "Custom spin box" - \endif A spin box enables users to choose an integer value by clicking the up or down indicator buttons, or by pressing up or down on the keyboard. @@ -661,31 +647,6 @@ For more information about how to customize a particular control, see \l{Customization Reference}. - \if defined(qtcreator) - \section1 History of Qt Quick Controls - - In Qt 4, ready-made Qt Quick 1 Components were provided for creating - UIs with a native look and feel for a particular target platform. - In Qt 5.1, Qt Quick Controls, Dialogs, and Layouts were added for - creating classic desktop-style user interfaces using Qt Quick 2.1. The - Qt Quick Controls Styles could be used to customize Qt Quick Controls. - - Since Qt 5.7, \l {Qt Quick Controls 2} replace Qt Quick Controls 1 and - Qt Labs Controls. They provide lightweight components for creating performant - user interfaces for \l{glossary-device}{devices}. - - Qt Quick Controls 2 work in conjunction with Qt Quick and Qt Quick Layouts. - - The \QC project wizards create Qt Quick applications that use Qt Quick - 2 types or Qt Quick Controls 2 types. - - Even if you use Qt Quick Controls 2, you can still write cross-platform - applications, by using different sets of QML files for each platform. - - Some ready-made controls, such as a gauge, dial, status indicator, and - tumbler, are provided by the \l {Qt Quick Extras} module. - \endif - \section1 Summary of UI Controls The following table lists preset UI controls with links to their developer diff --git a/doc/qtdesignstudio/src/components/qtquick-data-models.qdoc b/doc/qtdesignstudio/src/components/qtquick-data-models.qdoc index 6ecd695a2e7..400e264d02e 100644 --- a/doc/qtdesignstudio/src/components/qtquick-data-models.qdoc +++ b/doc/qtdesignstudio/src/components/qtquick-data-models.qdoc @@ -4,11 +4,7 @@ /*! \page quick-data-models.html \previouspage quick-controls.html - \if defined(qtdesignstudio) \nextpage quick-2d-effects.html - \else - \nextpage quick-animations.html - \endif \title Lists and Other Data Models @@ -167,9 +163,7 @@ > \uicontrol {Qt Quick Controls}. \include qtquick-pathview-editor.qdocinc pathview - \if defined(qtdesignstudio) \include qtquick-pathview-editor.qdocinc svgpath - \endif \section1 Summary of Model Components @@ -223,14 +217,12 @@ \li Qt Quick Controls \li \li A stack-based navigation model. - \if defined(qtdesignstudio) \row \li \inlineimage icons/item-svg-16px.png \li \l{SVG Path Item} \li Qt Quick Studio Components \li \li An SVG path data string that is used to draw a path as a line. - \endif \row \li \inlineimage icons/itemdelegate-icon16.png \li \l{SwipeDelegate}{Swipe Delegate} diff --git a/doc/qtdesignstudio/src/components/qtquick-images.qdoc b/doc/qtdesignstudio/src/components/qtquick-images.qdoc index 3d6879826c5..176e4409f8a 100644 --- a/doc/qtdesignstudio/src/components/qtquick-images.qdoc +++ b/doc/qtdesignstudio/src/components/qtquick-images.qdoc @@ -187,7 +187,6 @@ \youtube DVWd_xMMgvg - \if defined(qtdesignstudio) \section1 Iso Icon \note The Iso Icon component is not available if you selected @@ -207,7 +206,6 @@ set the value of \uicontrol {Icon color}. \image iso-icon-browser.png - \endif \section1 Summary of Images @@ -243,7 +241,6 @@ \li \inlineimage ok.png \li An image in one of the supported formats, including bitmap formats such as PNG and JPEG and vector graphics formats such as SVG. - \if defined(qtdesignstudio) \row \li \inlineimage icons/iso-icons-16px.png \li \l{Iso Icon} @@ -253,6 +250,5 @@ component. You can select the icon to use and its color. \note This component is not supported on Qt 6. - \endif \endtable */ diff --git a/doc/qtdesignstudio/src/components/qtquick-pathview-editor.qdocinc b/doc/qtdesignstudio/src/components/qtquick-pathview-editor.qdocinc index 73422f6676b..dd50cf67986 100644 --- a/doc/qtdesignstudio/src/components/qtquick-pathview-editor.qdocinc +++ b/doc/qtdesignstudio/src/components/qtquick-pathview-editor.qdocinc @@ -49,10 +49,8 @@ In the \uicontrol {Path View Highlight} section, you can specify properties for \l{View Highlight}{highlighting} path objects. - \if defined(qtdesignstudio) \note You can also use the \l {SVG Path Item} Studio Component to specify an SVG path data string that draws a path. - \endif //! [pathview] diff --git a/doc/qtdesignstudio/src/components/qtquick-positioning.qdoc b/doc/qtdesignstudio/src/components/qtquick-positioning.qdoc index a71831fdd1e..1eb55ebd771 100644 --- a/doc/qtdesignstudio/src/components/qtquick-positioning.qdoc +++ b/doc/qtdesignstudio/src/components/qtquick-positioning.qdoc @@ -4,11 +4,7 @@ /*! \page qtquick-positioning.html \previouspage qtquick-properties.html - \if defined(qtdesignstudio) \nextpage qtquick-annotations.html - \else - \nextpage qtquick-fonts.html - \endif \title Scalable Layouts @@ -386,13 +382,9 @@ \section2 Using Layouts - \if defined(qtcreator) - Since Qt 5.1, you can use QML types in the \l{qtquicklayouts-index.html} - {Qt Quick Layouts} module to arrange components in UIs. - \else You can use the components available in \uicontrol Components > \uicontrol {Qt Quick Layouts} to arrange components in UIs. - \endif + Unlike positioners, layouts manage both the positions and sizes of their child components, and are therefore well suited for dynamic and resizable UIs. However, this means that you should not specify fixed positions and @@ -502,10 +494,8 @@ You can use the \uicontrol Frame and \uicontrol {Group Box} controls to draw frames around groups of controls. - \if defined(qtdesignstudio) - If you don't want a frame, use the \uicontrol Group component instead. - \endif + If you don't want a frame, use the \uicontrol Group component instead. The following table lists the UI controls that you can use to organize components in UIs (since Qt 5.7). The \e Location column indicates the @@ -522,13 +512,11 @@ \li \l [QtQuickControls]{Frame} \li Qt Quick Controls \li A visual frame around a group of controls. - \if defined(qtdesignstudio) \row \li \inlineimage icons/group-16px.png \li Group \li Qt Quick Studio Components \li Enables handling the selected components as a group. - \endif \row \li \inlineimage icons/groupbox-icon16.png \li \l [QtQuickControls]{GroupBox}{Group Box} diff --git a/doc/qtdesignstudio/src/components/qtquick-preset-components.qdoc b/doc/qtdesignstudio/src/components/qtquick-preset-components.qdoc index d05d800546a..875d0f9ea67 100644 --- a/doc/qtdesignstudio/src/components/qtquick-preset-components.qdoc +++ b/doc/qtdesignstudio/src/components/qtquick-preset-components.qdoc @@ -30,10 +30,8 @@ \li \l {User Interaction Methods} \li \l {UI Controls} \li \l {Lists and Other Data Models} - \if defined(qtdesignstudio) \li \l {2D Effects} \li \l {Logic Helpers} - \endif \li \l Animations \endlist diff --git a/doc/qtdesignstudio/src/components/qtquick-shapes.qdoc b/doc/qtdesignstudio/src/components/qtquick-shapes.qdoc index 02ef0e06cf5..73ccc3971a5 100644 --- a/doc/qtdesignstudio/src/components/qtquick-shapes.qdoc +++ b/doc/qtdesignstudio/src/components/qtquick-shapes.qdoc @@ -8,7 +8,6 @@ \title Shapes - \if defined(qtdesignstudio) \QDS is a UI design tool rather than a generic drawing tool, and therefore, the focus is on providing ready-made UI controls that you can modify according to your needs. The values of some properties of the controls are @@ -22,12 +21,6 @@ \image studio-shapes.png "Shapes in the 2D view" - \else - You can use the Rectangle component to draw basic shapes in - the \l {2D} view. - \image qml-shapes.png "Shapes in the 2D view" - \endif - Most visual components in \uicontrol Components are based on the \l [QtQuick] {Item} component. Even though it has no visual appearance itself (similarly to a mouse area, for example), it defines all the properties that are @@ -48,9 +41,7 @@ The basic \l [QtQuick] {Rectangle} component is used for drawing shapes with four sides and corners, as well as a solid border. - \if defined(qtdesignstudio) \image qml-shapes-rectangle.png "A rectangle and its properties" - \endif Rectangles can be filled either with a solid fill color or a linear gradient that you set in the \uicontrol {Fill color} field. You can @@ -74,7 +65,6 @@ check box in the \uicontrol Advanced section to improve the appearance of your shape. - \if defined(qtdesignstudio) \target studio-rectangle \section2 Studio Rectangle @@ -279,5 +269,4 @@ \li A triangle with different dimensions and shapes that is enclosed in an invisible rectangle. \endtable - \endif */ diff --git a/doc/qtdesignstudio/src/components/qtquick-text.qdoc b/doc/qtdesignstudio/src/components/qtquick-text.qdoc index 7f515c7e98f..f47ac82d7bd 100644 --- a/doc/qtdesignstudio/src/components/qtquick-text.qdoc +++ b/doc/qtdesignstudio/src/components/qtquick-text.qdoc @@ -79,12 +79,6 @@ For more information, see \l {Internationalization and Localization with Qt Quick}. - \if defined(qtcreator) - When you \l{Creating Qt Quick Projects}{create a new project}, you can - automatically generate a translation source file (TS) for one language. - You can add other languages later by editing the project file. - \endif - \section1 Character Properties You can set font properties in the \uicontrol Character section diff --git a/doc/qtdesignstudio/src/components/qtquick-user-interaction-methods.qdoc b/doc/qtdesignstudio/src/components/qtquick-user-interaction-methods.qdoc index bb29fb7f36e..71b3fee6305 100644 --- a/doc/qtdesignstudio/src/components/qtquick-user-interaction-methods.qdoc +++ b/doc/qtdesignstudio/src/components/qtquick-user-interaction-methods.qdoc @@ -59,11 +59,6 @@ \li \c released() \endlist - \if defined(qtcreator) - A more modern way of handling events from all pointing devices, including - mouse and touchscreen, is via \l {Qt Quick Input Handlers}. - \endif - \section2 Mouse Area Properties A \uicontrol {Mouse Area} is an invisible component that is typically used diff --git a/doc/qtdesignstudio/src/overviews/qtquick-animation-overview.qdoc b/doc/qtdesignstudio/src/overviews/qtquick-animation-overview.qdoc index ef7fccc3c7d..86132b5ce1a 100644 --- a/doc/qtdesignstudio/src/overviews/qtquick-animation-overview.qdoc +++ b/doc/qtdesignstudio/src/overviews/qtquick-animation-overview.qdoc @@ -87,12 +87,10 @@ \header \li Technique \li Use Case - \if defined(qtdesignstudio) \row \li \l{Designing Application Flows}{Application flows} \li An interactive prototype that can be clicked through to simulate the user experience of the application. - \endif \row \li \l{Transitions}{Transitions between states} \li Transitions between different states of the UI using a transition @@ -100,7 +98,6 @@ to the keyframes. \endtable - \if defined(qtdesignstudio) \section2 Application Flows You can design an application in the form of a \e {schematic diagram} @@ -111,7 +108,6 @@ as the base of the production version of the application. For more information, see \l{Designing Application Flows}. - \endif \section2 Transitions Between States diff --git a/doc/qtdesignstudio/src/overviews/qtquick-annotations.qdoc b/doc/qtdesignstudio/src/overviews/qtquick-annotations.qdoc index d5f19de7a45..aeaf304bfcf 100644 --- a/doc/qtdesignstudio/src/overviews/qtquick-annotations.qdoc +++ b/doc/qtdesignstudio/src/overviews/qtquick-annotations.qdoc @@ -3,13 +3,8 @@ /*! \page qtquick-annotations.html - \if defined(qtdesignstudio) \previouspage qtquick-positioning.html \nextpage qtquick-prototyping.html - \else - \previouspage qtquick-fonts.html - \nextpage creator-quick-ui-forms.html - \endif \title Annotating Designs diff --git a/doc/qtdesignstudio/src/overviews/qtquick-creating-ui-logic.qdoc b/doc/qtdesignstudio/src/overviews/qtquick-creating-ui-logic.qdoc index 226c2a4982d..a5e05f8beb3 100644 --- a/doc/qtdesignstudio/src/overviews/qtquick-creating-ui-logic.qdoc +++ b/doc/qtdesignstudio/src/overviews/qtquick-creating-ui-logic.qdoc @@ -38,13 +38,11 @@ to define how their properties will animate when they change due to a state change. - \if defined(qtdesignstudio) The \l{Log In UI - States} example illustrates using states to create two UI screens and signals emitted by buttons to apply the states. The button components also switch states when they are pressed down. \image loginui3.gif "Clicking buttons to switch between screens" - \endif Using property aliases and states to create the differences in your component instances enables you to reuse components instead of duplicating @@ -57,7 +55,6 @@ instances of the controls into custom components and specify new properties for them. - \if defined(qtdesignstudio) To have your UI perform certain operations, you might need to write JavaScript expressions for conditions or convert numbers to strings. To make this easier, \QDS provides preset components called @@ -74,7 +71,6 @@ to implement the UI logic. \image studio-logic-helper-combining-example.gif "Logic helper example application" - \endif The following table summarizes some typical use cases with links to more information. @@ -107,11 +103,9 @@ \row \li Using preset UI controls that have default properties and states \li \l{UI Controls} - \if defined(qtdesignstudio) \row \li Creating conditional conditions \li \l{Logic Helpers} - \endif \row \li Adding custom properties for a particular component type \li \l{Specifying Custom Properties} diff --git a/doc/qtdesignstudio/src/overviews/qtquick-fonts.qdoc b/doc/qtdesignstudio/src/overviews/qtquick-fonts.qdoc index e2cfd05679a..d2f6745df5e 100644 --- a/doc/qtdesignstudio/src/overviews/qtquick-fonts.qdoc +++ b/doc/qtdesignstudio/src/overviews/qtquick-fonts.qdoc @@ -3,13 +3,8 @@ /*! \page qtquick-fonts.html - \if defined(qtdesignstudio) \previouspage studio-importing-2d.html \nextpage studio-importing-3d.html - \else - \previouspage qtquick-positioning.html - \nextpage qtquick-annotations.html - \endif \title Using Custom Fonts diff --git a/doc/qtdesignstudio/src/overviews/qtquick-prototyping.qdoc b/doc/qtdesignstudio/src/overviews/qtquick-prototyping.qdoc index 414e608a9c7..2cda8239de4 100644 --- a/doc/qtdesignstudio/src/overviews/qtquick-prototyping.qdoc +++ b/doc/qtdesignstudio/src/overviews/qtquick-prototyping.qdoc @@ -3,11 +3,7 @@ /*! \page qtquick-prototyping.html - \if defined(qtdesignstudio) \previouspage qtquick-annotations.html - \else - \previouspage creator-quick-ui-forms.html - \endif \nextpage qtquick-creating-ui-logic.html \title Prototyping @@ -39,10 +35,8 @@ You can connect UIs to different forms of data from various sources, such as QML-based data models, JavaScript files, and backend services. - \if defined(qtdesignstudio) You can also connect your UI to Simulink to load live data from a Simulink simulation. - \endif \li \l {Dynamic Behaviors} @@ -50,7 +44,6 @@ communicate with each other. The connections can be triggered by changes in component property values or in UI states. - \if defined(qtdesignstudio) \li \l {Validating with Target Hardware} You can use the live preview feature to preview a UI file or the @@ -64,24 +57,5 @@ files that you can import to projects in \QDS, how to import them, and how to export them from \QDS back to the metadata format. - \else - \li \l {Exporting 3D Assets} - - You can export assets from 3D graphics applications into several - widely-used formats, such as .blend, .dae, .fbx, .glb, .gltf, .obj, - .uia, or .uip. - - \li \l {Importing 3D Assets} - - You can import exported assets into \QDS. For a list of formats - supported by each \l{Qt Quick 3D} version, see the module - documentation. - - \li \l {Exporting Components} - - You can export components contained in \l{UI Files}{UI files} - (.ui.qml) to JSON metadata format and PNG assets. - \endif - \endlist */ diff --git a/doc/qtdesignstudio/src/overviews/qtquick-uis.qdoc b/doc/qtdesignstudio/src/overviews/qtquick-uis.qdoc index 59eb00ab94b..82a6f2acfa3 100644 --- a/doc/qtdesignstudio/src/overviews/qtquick-uis.qdoc +++ b/doc/qtdesignstudio/src/overviews/qtquick-uis.qdoc @@ -3,13 +3,8 @@ /*! \page quick-uis.html - \if defined(qtdesignstudio) \previouspage {Examples} \nextpage studio-app-flows.html - \else - \previouspage qtquick-text-editor.html - \nextpage quick-components.html - \endif \title Wireframing @@ -44,7 +39,6 @@ \list - \if defined(qtdesignstudio) \li \l {Designing Application Flows} You can design an application in the form of a \e {schematic diagram} @@ -52,7 +46,6 @@ interconnections by means of symbols. This results in an interactive prototype that can be clicked through to simulate the user experience of the application. - \endif \li \l {Using Components} @@ -79,31 +72,10 @@ methods, such as anchors, layouts, positioners, and property bindings, for dynamic UIs. - \if defined(qtcreator) - \li \l {Using Custom Fonts} - - You can load custom fonts to \QC and use them in your designs. - \endif - \li \l {Annotating Designs} You can annotate your designs to provide reviewers or developers with additional information about them. - \if defined(qtcreator) - \li \l {Loading Placeholder Data} - - You can create QML files that contain placeholder data, so that - you can test grid, list, or path views, even though you don't - have access to real data. - - \li \l{UI Files} - - Some of the wizards create projects that contain UI files - (.ui.qml). You should always edit UI files in the \l {2D} - and \l Properties view, to avoid breaking the code. - - \endif - \endlist */ diff --git a/doc/qtdesignstudio/src/qtdesignstudio-advanced.qdoc b/doc/qtdesignstudio/src/qtdesignstudio-advanced.qdoc index 0ff4c90faf9..21733c996ac 100644 --- a/doc/qtdesignstudio/src/qtdesignstudio-advanced.qdoc +++ b/doc/qtdesignstudio/src/qtdesignstudio-advanced.qdoc @@ -25,18 +25,12 @@ and \l Properties view, to avoid breaking the code. \li \l{Managing Data Collection} - \if defined (qtcreator) - You can enable \QC to report crashes automatically. If you agreed to - pseudonymous user statistics collection during the \QC installation, - you can turn it on and determine what type of data is collected and - transmitted to the backend storage. - \else You can enable \QDS to report crashes automatically. If you enable the telemetry plugin, you can turn on the pseudonymous user statistics collection and determine what type of data is collected and transmitted to the backend storage. You can also modify settings for collecting user feedback. - \endif + \li \l{Packaging Applications} When you are ready to deliver your application to users or upload diff --git a/doc/qtdesignstudio/src/qtquick3d-editor/exporting-3d/exporting-3d-assets.qdoc b/doc/qtdesignstudio/src/qtquick3d-editor/exporting-3d/exporting-3d-assets.qdoc index e861133efd5..7af07ae66e4 100644 --- a/doc/qtdesignstudio/src/qtquick3d-editor/exporting-3d/exporting-3d-assets.qdoc +++ b/doc/qtdesignstudio/src/qtquick3d-editor/exporting-3d/exporting-3d-assets.qdoc @@ -4,26 +4,11 @@ /*! \page exporting-3d-assets.html - \if defined(qtdesignstudio) \previouspage qtbridge-figma-template.html - \else - \previouspage quick-states.html - \endif \nextpage exporting-from-blender.html \title Exporting 3D Assets - \if defined(qtcreator) - \table - \row - \li \inlineimage blender-logo.png - \li \inlineimage maya-logo.png - \row - \li \l{Exporting from Blender}{Blender} - \li \l{Exporting from Maya}{Maya} - \endtable - \endif - You can import files you created using 3D graphics applications and exported to several widely-used formats, such as .blend, .dae, .fbx, .glb, .gltf, .obj, .uia, or .uip. For a list of formats supported by each \l{Qt Quick 3D} diff --git a/doc/qtdesignstudio/src/qtquick3d-editor/exporting-3d/exporting-from-maya.qdoc b/doc/qtdesignstudio/src/qtquick3d-editor/exporting-3d/exporting-from-maya.qdoc index 238b6e6dfdb..16494923806 100644 --- a/doc/qtdesignstudio/src/qtquick3d-editor/exporting-3d/exporting-from-maya.qdoc +++ b/doc/qtdesignstudio/src/qtquick3d-editor/exporting-3d/exporting-from-maya.qdoc @@ -4,11 +4,7 @@ /*! \page exporting-from-maya.html \previouspage exporting-from-blender.html - \if defined (qtdesignstudio) \nextpage exporting-from-qt3ds.html - \else - \nextpage studio-importing-3d.html - \endif \title Exporting from Maya diff --git a/doc/qtdesignstudio/src/qtquick3d-editor/exporting-3d/exporting-from-qt3ds.qdoc b/doc/qtdesignstudio/src/qtquick3d-editor/exporting-3d/exporting-from-qt3ds.qdoc index 49bc8894139..360a21183b0 100644 --- a/doc/qtdesignstudio/src/qtquick3d-editor/exporting-3d/exporting-from-qt3ds.qdoc +++ b/doc/qtdesignstudio/src/qtquick3d-editor/exporting-3d/exporting-from-qt3ds.qdoc @@ -4,11 +4,7 @@ /*! \page exporting-from-qt3ds.html \previouspage exporting-from-maya.html - \if defined(qtdesignstudio) \nextpage studio-importing-designs.html - \else - \nextpage studio-importing-3d.html - \endif \title Exporting from Qt 3D Studio diff --git a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-importing.qdoc b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-importing.qdoc index fd31970ef51..45d517a0bf4 100644 --- a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-importing.qdoc +++ b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-importing.qdoc @@ -3,11 +3,8 @@ /*! \page studio-importing-3d.html - \if defined(qtdesignstudio) + \previouspage qtquick-fonts.html - \else - \previouspage exporting-from-maya.html - \endif \nextpage creator-exporting-qml.html \title Importing 3D Assets diff --git a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-optimized-3d-scenes.qdoc b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-optimized-3d-scenes.qdoc index 4032685223c..535bb392cab 100644 --- a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-optimized-3d-scenes.qdoc +++ b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-optimized-3d-scenes.qdoc @@ -4,11 +4,7 @@ /*! \page studio-optimized-3d-scenes.html \previouspage qtquick-optimizing-designs.html - \if defined(qtdesignstudio) \nextpage studio-implementing-applications.html - \else - \nextpage qtquick-iso-icon-browser.html - \endif \title Creating Optimized 3D Scenes @@ -22,11 +18,7 @@ \section1 The Optimal 3D Scene Example - \if defined(qtdesignstudio) The \l {Optimal 3D Scene} - \else - The Optimal 3D Scene - \endif example features four versions of the same kitchen scene that have been created using different strategies: High, Low, Combined, and Vertex Color. The High Scene includes a significantly higher number of diff --git a/doc/qtdesignstudio/src/views/creator-logical-operators.qdocinc b/doc/qtdesignstudio/src/views/creator-logical-operators.qdocinc index e3bb54af62c..307dfbfd36a 100644 --- a/doc/qtdesignstudio/src/views/creator-logical-operators.qdocinc +++ b/doc/qtdesignstudio/src/views/creator-logical-operators.qdocinc @@ -62,13 +62,10 @@ different type. \endtable - \if defined(qtdesignstudio) Alternatively, you can use \uicontrol {And Operator}, \uicontrol {Or Operator}, and \uicontrol {Not Operator} components to bind property values using the boolean AND, OR, and NOT operator. For more information, see \l{Logic Helpers}. - \endif - In addition, you can use arithmetic operators to compare numbers before checks. However, we recommend that you create separate properties for this diff --git a/doc/qtdesignstudio/src/views/qtquick-components-view.qdoc b/doc/qtdesignstudio/src/views/qtquick-components-view.qdoc index 8592db6a046..1e2786bfbc1 100644 --- a/doc/qtdesignstudio/src/views/qtquick-components-view.qdoc +++ b/doc/qtdesignstudio/src/views/qtquick-components-view.qdoc @@ -36,10 +36,8 @@ \li \l {UI Controls} \li \l {User Interaction Methods} \li \l {Lists and Other Data Models} - \if defined(qtdesignstudio) \li \l {2D Effects} \li \l {Logic Helpers} - \endif \li \l Animations \li \l{3D Views} \li \l{Node} diff --git a/doc/qtdesignstudio/src/views/qtquick-connection-editor-bindings.qdoc b/doc/qtdesignstudio/src/views/qtquick-connection-editor-bindings.qdoc index 4221657e6b8..613d8fd02f4 100644 --- a/doc/qtdesignstudio/src/views/qtquick-connection-editor-bindings.qdoc +++ b/doc/qtdesignstudio/src/views/qtquick-connection-editor-bindings.qdoc @@ -54,11 +54,7 @@ \list \li \l{Using States to Change Component Property Values} - \if defined(qtdesignstudio) \li \l{Exporting Properties} - \else - \li \l{Moving the Bubble} in \l{Creating a Mobile Application} - \endif \endlist For more information, watch the following video: diff --git a/doc/qtdesignstudio/src/views/qtquick-connection-editor-properties.qdoc b/doc/qtdesignstudio/src/views/qtquick-connection-editor-properties.qdoc index 0d61823fc66..8accd1df488 100644 --- a/doc/qtdesignstudio/src/views/qtquick-connection-editor-properties.qdoc +++ b/doc/qtdesignstudio/src/views/qtquick-connection-editor-properties.qdoc @@ -4,11 +4,8 @@ /*! \page quick-dynamic-properties.html \previouspage quick-property-bindings.html - \if defined(qtdesignstudio) \nextpage quick-states.html - \else - \nextpage quick-connections-backend.html - \endif + \sa {Specifying Component Properties} \title Specifying Custom Properties diff --git a/doc/qtdesignstudio/src/views/qtquick-connection-editor-signals.qdoc b/doc/qtdesignstudio/src/views/qtquick-connection-editor-signals.qdoc index a37d41c6355..420226750dc 100644 --- a/doc/qtdesignstudio/src/views/qtquick-connection-editor-signals.qdoc +++ b/doc/qtdesignstudio/src/views/qtquick-connection-editor-signals.qdoc @@ -63,17 +63,12 @@ in the context menu to specify the connection in \uicontrol {Connection Editor}. - \if defined(qtcreator) - For an example of using the \uicontrol {Connections} view, see - \l{Connecting Mouse Clicks to State Changes}. - \else For examples of using the \uicontrol {Connections} view, see: \list \li \l{Connecting Buttons to States} in \l{Log In UI - States} \li \l{Connecting Buttons to State Changes} in \l{Washing Machine UI} \endlist - \endif \section1 Adding Signal Handlers diff --git a/doc/qtdesignstudio/src/views/qtquick-connection-editor.qdoc b/doc/qtdesignstudio/src/views/qtquick-connection-editor.qdoc index 0512af58ad1..58831990191 100644 --- a/doc/qtdesignstudio/src/views/qtquick-connection-editor.qdoc +++ b/doc/qtdesignstudio/src/views/qtquick-connection-editor.qdoc @@ -33,12 +33,6 @@ not otherwise exist for a particular \l{Component Types} {component type} or your custom components. - \if defined(qtcreator) - \li \l{Managing C++ Backend Objects} - - Application developers can access QObject objects implemented in C++ - from QML files. - \endif \endlist For an example of using properties, bindings, and connections to create a diff --git a/doc/qtdesignstudio/src/views/qtquick-connection-view.qdoc b/doc/qtdesignstudio/src/views/qtquick-connection-view.qdoc index 69c2a78dc9c..93fbdc7ea7a 100644 --- a/doc/qtdesignstudio/src/views/qtquick-connection-view.qdoc +++ b/doc/qtdesignstudio/src/views/qtquick-connection-view.qdoc @@ -37,11 +37,6 @@ custom properties that would not otherwise exist for a particular \l{Component Types}{component type}. - \if defined(qtcreator) - In addition, application developers can use the \uicontrol Backends view - to access QObject objects implemented in C++ from QML files. - \endif - \section1 Summary of the Connections View Tabs \table @@ -64,11 +59,6 @@ \li Add custom properties that would not otherwise exist for a particular preset component or your own custom component. \li \l{Specifying Custom Properties} - \if defined(qtcreator) - \row - \li \uicontrol Backends - \li Access QObject objects implemented in C++ from QML files. - \li \l{Managing C++ Backend Objects} - \endif + \endtable */ diff --git a/doc/qtdesignstudio/src/views/qtquick-designer.qdoc b/doc/qtdesignstudio/src/views/qtquick-designer.qdoc index a324c6f5626..cc18d924417 100644 --- a/doc/qtdesignstudio/src/views/qtquick-designer.qdoc +++ b/doc/qtdesignstudio/src/views/qtquick-designer.qdoc @@ -9,13 +9,9 @@ /*! \page creator-using-qt-quick-designer.html - \if defined(qtdesignstudio) + \previouspage creator-modes.html \nextpage qtquick-form-editor.html - \else - \previouspage quick-projects.html - \nextpage quick-uis.html - \endif \title Design Views @@ -151,7 +147,6 @@ \li Keyboard Shortcut \li Read More - \if defined(qtdesignstudio) \row \li \inlineimage icons/home.png \li \uicontrol {Home}: opens the welcome page. @@ -168,7 +163,6 @@ make to the UI are instantly visible to you in the preview. \li \key Alt+P (\key Opt+P on \macos) \li \l{Validating with Target Hardware} - \endif \row \li Currently open file diff --git a/doc/qtdesignstudio/src/views/qtquick-properties.qdoc b/doc/qtdesignstudio/src/views/qtquick-properties.qdoc index 8ce14eba6bf..fc8ba92e93f 100644 --- a/doc/qtdesignstudio/src/views/qtquick-properties.qdoc +++ b/doc/qtdesignstudio/src/views/qtquick-properties.qdoc @@ -214,7 +214,6 @@ use of solid color fills or images. Consider using gradients only for static components in a UI. - \if defined(qtdesignstudio) \section2 Setting Gradient Properties \image qtquick-designer-gradient-types.png "Available gradient types" @@ -262,7 +261,6 @@ in the \uicontrol Angle field. \image qtquick-designer-gradient-properties-conical.png "Conical gradient properties" - \endif \section2 Selecting Web Gradients diff --git a/doc/qtdesignstudio/src/views/qtquick-states.qdoc b/doc/qtdesignstudio/src/views/qtquick-states.qdoc index 6b55e5e59f0..60e3c3f86b7 100644 --- a/doc/qtdesignstudio/src/views/qtquick-states.qdoc +++ b/doc/qtdesignstudio/src/views/qtquick-states.qdoc @@ -3,13 +3,8 @@ /*! \page quick-states.html - \if defined(qtdesignstudio) \previouspage quick-dynamic-properties.html \nextpage creator-live-preview.html - \else - \previouspage quick-connections-backend.html - \nextpage exporting-3d-assets.html - \endif \title Working with States @@ -32,13 +27,11 @@ > \uicontrol {Qt Quick Controls} > \uicontrol Controls has predefined \e normal and \e down states. - \if defined(qtdesignstudio) This also applies to the custom button component that you can create by using a \l{Creating Custom Controls}{wizard template}. For more information about editing the states within the button component and hiding and showing buttons to create several screens, see \l{Log In UI - Components} and \l{Log In UI - States}. - \endif To add motion to a screen, you can change the position of a component instance in the \uicontrol {2D} view and then add animation to the change @@ -122,11 +115,9 @@ when: control.pressed || control.checked && !control.hovered \endcode - \if defined(qtdesignstudio) If you are not familiar with writing expressions, you can use preset \l{Logic Helpers}{logic helpers} from \uicontrol Components > \uicontrol {Qt Quick Studio Logic Helper}. - \endif \section1 Using States @@ -169,10 +160,6 @@ select \uicontrol Default. \endlist - \if defined(qtcreator) - \include qtquick-states-scxml.qdocinc scxml state machines - \endif - \section1 State Groups With state groups, you can change the state of certain components diff --git a/doc/qtdesignstudio/src/views/qtquick-text-editor.qdoc b/doc/qtdesignstudio/src/views/qtquick-text-editor.qdoc index aaaf254d1e6..75e84be9c45 100644 --- a/doc/qtdesignstudio/src/views/qtquick-text-editor.qdoc +++ b/doc/qtdesignstudio/src/views/qtquick-text-editor.qdoc @@ -4,11 +4,7 @@ /*! \page qtquick-text-editor.html \previouspage qtquick-curve-editor.html - \if defined(qtdesignstudio) \nextpage creator-projects-view.html - \else - \nextpage quick-uis.html - \endif \title Code diff --git a/doc/qtdesignstudio/src/views/qtquick-timeline-view.qdoc b/doc/qtdesignstudio/src/views/qtquick-timeline-view.qdoc index 1fcf4ef9088..81947261841 100644 --- a/doc/qtdesignstudio/src/views/qtquick-timeline-view.qdoc +++ b/doc/qtdesignstudio/src/views/qtquick-timeline-view.qdoc @@ -46,10 +46,7 @@ \youtube V3Po15bNErw - \if defined(qtdesignstudio) To try it yourself, follow the \l{Log In UI - Timeline} tutorial. - \endif - For more information about creating timeline animations, see \l{Creating Timeline Animations}. diff --git a/doc/qtdesignstudio/src/views/qtquick-timeline.qdoc b/doc/qtdesignstudio/src/views/qtquick-timeline.qdoc index 12b1fc14633..c6a33f62c70 100644 --- a/doc/qtdesignstudio/src/views/qtquick-timeline.qdoc +++ b/doc/qtdesignstudio/src/views/qtquick-timeline.qdoc @@ -250,7 +250,6 @@ create a timeline for the Item, and set the rotation property for the start and end keyframes. - \if defined(qtdesignstudio) \section1 Animating Shapes You can use the \uicontrol {Qt Quick Studio Components} to animate the @@ -263,5 +262,5 @@ \li \l Rectangle \li \l Triangle \endlist - \endif + */ diff --git a/doc/qtdesignstudio/src/views/qtquick-transition-editor.qdoc b/doc/qtdesignstudio/src/views/qtquick-transition-editor.qdoc index 3ee43335d69..b21207259e2 100644 --- a/doc/qtdesignstudio/src/views/qtquick-transition-editor.qdoc +++ b/doc/qtdesignstudio/src/views/qtquick-transition-editor.qdoc @@ -114,8 +114,4 @@ To remove the current transition, select the \inlineimage icons/minus.png (\uicontrol {Remove Transition}) button. - \if defined(qtcreator) - For an example of animating transitions between states, see - \l {Creating a Qt Quick Application}. - \endif */ diff --git a/doc/qtdesignstudio/src/views/studio-workspaces.qdoc b/doc/qtdesignstudio/src/views/studio-workspaces.qdoc index 8d29b1f74f0..b5f7abf0d34 100644 --- a/doc/qtdesignstudio/src/views/studio-workspaces.qdoc +++ b/doc/qtdesignstudio/src/views/studio-workspaces.qdoc @@ -3,11 +3,7 @@ /*! \page creator-project-managing-workspaces.html - \if defined(qtdesignstudio) \previouspage studio-texture-editor.html - \else - \previouspage creator-open-documents-view.html - \endif \nextpage creator-project-managing-sessions.html \title Managing Workspaces From dbf91a8f031d1d6adcbeb63e7503e03d23878779 Mon Sep 17 00:00:00 2001 From: Samuel Ghinet Date: Wed, 29 Mar 2023 21:04:05 +0300 Subject: [PATCH 004/192] QmlDesigner: Fix Material download not showing any progress Also, now delayed the finish part by 200ms so that 100% progress would be visible to the user. Task-number: QDS-9562 Change-Id: Ie8f87ff70770b206256d4dbf0c08227723067eeb Reviewed-by: Mahmoud Badri Reviewed-by: Miikka Heikkinen --- .../ContentLibraryMaterial.qml | 15 ++++++++++++--- .../ContentLibraryTexture.qml | 13 +++++++++++-- .../qmldesigner/utils/multifiledownloader.cpp | 5 ++++- 3 files changed, 27 insertions(+), 6 deletions(-) diff --git a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryMaterial.qml b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryMaterial.qml index 5bafd1d0526..aadd4b0d991 100644 --- a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryMaterial.qml +++ b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryMaterial.qml @@ -178,6 +178,17 @@ Item { } } // Column + Timer { + id: delayedFinish + interval: 200 + + onTriggered: { + downloadPane.endDownload() + + root.downloadState = "downloaded" + } + } + MultiFileDownloader { id: downloader @@ -191,9 +202,7 @@ Item { } onFinishedChanged: { - downloadPane.endDownload() - - root.downloadState = "downloaded" + delayedFinish.restart() } onDownloadCanceled: { diff --git a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryTexture.qml b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryTexture.qml index 924f8ae4e76..3ba94430290 100644 --- a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryTexture.qml +++ b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryTexture.qml @@ -227,6 +227,16 @@ Item { } } + Timer { + id: delayedFinish + interval: 200 + + onTriggered: { + modelData.setDownloaded() + root.downloadState = modelData.isDownloaded() ? "downloaded" : "failed" + } + } + FileExtractor { id: extractor archiveName: downloader.completeBaseName @@ -235,8 +245,7 @@ Item { alwaysCreateDir: false clearTargetPathContents: false onFinishedChanged: { - modelData.setDownloaded() - root.downloadState = modelData.isDownloaded() ? "downloaded" : "failed" + delayedFinish.restart() } } } diff --git a/src/plugins/qmldesigner/utils/multifiledownloader.cpp b/src/plugins/qmldesigner/utils/multifiledownloader.cpp index 346c1675f6e..05c527c8851 100644 --- a/src/plugins/qmldesigner/utils/multifiledownloader.cpp +++ b/src/plugins/qmldesigner/utils/multifiledownloader.cpp @@ -23,7 +23,10 @@ void MultiFileDownloader::setDownloader(FileDownloader *downloader) }); QObject::connect(m_downloader, &FileDownloader::progressChanged, this, [this]() { - m_progress = (m_nextFile + m_downloader->progress()) / m_files.count(); + double curProgress = m_downloader->progress() / 100.0; + double totalProgress = (m_nextFile + curProgress) / m_files.count(); + m_progress = totalProgress * 100; + emit progressChanged(); }); QObject::connect(m_downloader, &FileDownloader::downloadFailed, this, [this]() { From 515b2fb7b8fa5d735708d1dc9d8fc8726cc290aa Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Thu, 30 Mar 2023 23:57:59 +0300 Subject: [PATCH 005/192] QmlDesigner: Fix assets dialogs wrong background color Fixes: QDS-9600 Change-Id: I85faeecbc58561892af3001537fb1aafad6af06c Reviewed-by: Miikka Heikkinen --- .../assetsLibraryQmlSources/ConfirmDeleteFilesDialog.qml | 2 +- .../assetsLibraryQmlSources/ConfirmDeleteFolderDialog.qml | 2 +- .../qmldesigner/assetsLibraryQmlSources/NewEffectDialog.qml | 2 +- .../qmldesigner/assetsLibraryQmlSources/NewFolderDialog.qml | 2 +- .../qmldesigner/assetsLibraryQmlSources/RenameFolderDialog.qml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/share/qtcreator/qmldesigner/assetsLibraryQmlSources/ConfirmDeleteFilesDialog.qml b/share/qtcreator/qmldesigner/assetsLibraryQmlSources/ConfirmDeleteFilesDialog.qml index 756176dccaf..24bc01d1abb 100644 --- a/share/qtcreator/qmldesigner/assetsLibraryQmlSources/ConfirmDeleteFilesDialog.qml +++ b/share/qtcreator/qmldesigner/assetsLibraryQmlSources/ConfirmDeleteFilesDialog.qml @@ -8,7 +8,7 @@ import StudioTheme as StudioTheme import StudioControls as StudioControls import AssetsLibraryBackend -Dialog { +StudioControls.Dialog { id: root title: qsTr("Confirm Delete Files") anchors.centerIn: parent diff --git a/share/qtcreator/qmldesigner/assetsLibraryQmlSources/ConfirmDeleteFolderDialog.qml b/share/qtcreator/qmldesigner/assetsLibraryQmlSources/ConfirmDeleteFolderDialog.qml index 6faef2434a0..7a817560f09 100644 --- a/share/qtcreator/qmldesigner/assetsLibraryQmlSources/ConfirmDeleteFolderDialog.qml +++ b/share/qtcreator/qmldesigner/assetsLibraryQmlSources/ConfirmDeleteFolderDialog.qml @@ -7,7 +7,7 @@ import HelperWidgets as HelperWidgets import StudioTheme as StudioTheme import AssetsLibraryBackend -Dialog { +StudioControls.Dialog { id: root title: qsTr("Folder Not Empty") diff --git a/share/qtcreator/qmldesigner/assetsLibraryQmlSources/NewEffectDialog.qml b/share/qtcreator/qmldesigner/assetsLibraryQmlSources/NewEffectDialog.qml index a5a12355871..a78e038f229 100644 --- a/share/qtcreator/qmldesigner/assetsLibraryQmlSources/NewEffectDialog.qml +++ b/share/qtcreator/qmldesigner/assetsLibraryQmlSources/NewEffectDialog.qml @@ -8,7 +8,7 @@ import StudioControls as StudioControls import StudioTheme as StudioTheme import AssetsLibraryBackend -Dialog { +StudioControls.Dialog { id: root title: qsTr("Create New Effect") diff --git a/share/qtcreator/qmldesigner/assetsLibraryQmlSources/NewFolderDialog.qml b/share/qtcreator/qmldesigner/assetsLibraryQmlSources/NewFolderDialog.qml index a1afe464916..fad7198ac36 100644 --- a/share/qtcreator/qmldesigner/assetsLibraryQmlSources/NewFolderDialog.qml +++ b/share/qtcreator/qmldesigner/assetsLibraryQmlSources/NewFolderDialog.qml @@ -8,7 +8,7 @@ import StudioControls as StudioControls import StudioTheme as StudioTheme import AssetsLibraryBackend -Dialog { +StudioControls.Dialog { id: root title: qsTr("Create New Folder") diff --git a/share/qtcreator/qmldesigner/assetsLibraryQmlSources/RenameFolderDialog.qml b/share/qtcreator/qmldesigner/assetsLibraryQmlSources/RenameFolderDialog.qml index 0b9ae302550..16c36de63d2 100644 --- a/share/qtcreator/qmldesigner/assetsLibraryQmlSources/RenameFolderDialog.qml +++ b/share/qtcreator/qmldesigner/assetsLibraryQmlSources/RenameFolderDialog.qml @@ -8,7 +8,7 @@ import StudioControls as StudioControls import StudioTheme as StudioTheme import AssetsLibraryBackend -Dialog { +StudioControls.Dialog { id: root title: qsTr("Rename Folder") From 81df14a2ed370bb7b9992e1beb51539042477c76 Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Fri, 31 Mar 2023 12:06:23 +0300 Subject: [PATCH 006/192] QmlDesigner: Fix missing import from ConfirmDeleteFolderDialog Change-Id: Ifd3970dab52f2e09c42738354759e188fe39d1ca Reviewed-by: Miikka Heikkinen --- .../assetsLibraryQmlSources/ConfirmDeleteFolderDialog.qml | 1 + 1 file changed, 1 insertion(+) diff --git a/share/qtcreator/qmldesigner/assetsLibraryQmlSources/ConfirmDeleteFolderDialog.qml b/share/qtcreator/qmldesigner/assetsLibraryQmlSources/ConfirmDeleteFolderDialog.qml index 7a817560f09..1f92c57a753 100644 --- a/share/qtcreator/qmldesigner/assetsLibraryQmlSources/ConfirmDeleteFolderDialog.qml +++ b/share/qtcreator/qmldesigner/assetsLibraryQmlSources/ConfirmDeleteFolderDialog.qml @@ -5,6 +5,7 @@ import QtQuick import QtQuick.Controls import HelperWidgets as HelperWidgets import StudioTheme as StudioTheme +import StudioControls as StudioControls import AssetsLibraryBackend StudioControls.Dialog { From 9c7dcea60bcca392f80f3195b7fc911c38067e17 Mon Sep 17 00:00:00 2001 From: Samuel Ghinet Date: Fri, 31 Mar 2023 15:07:28 +0300 Subject: [PATCH 007/192] QmlDesigner: Fix new folders (same name) not automatically expanding When a user created a new folder with the same name as an existing folder in that same location, the folder was not automatically expanded. Task-number: QDS-8866 Change-Id: I82ec4303a30d8f810417deea68b487e1b55833d5 Reviewed-by: Miikka Heikkinen Reviewed-by: Mahmoud Badri --- .../qmldesigner/assetsLibraryQmlSources/NewFolderDialog.qml | 6 ++++-- .../components/assetslibrary/assetslibrarymodel.cpp | 4 ++-- .../components/assetslibrary/assetslibrarymodel.h | 2 +- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/share/qtcreator/qmldesigner/assetsLibraryQmlSources/NewFolderDialog.qml b/share/qtcreator/qmldesigner/assetsLibraryQmlSources/NewFolderDialog.qml index fad7198ac36..3426f7965a6 100644 --- a/share/qtcreator/qmldesigner/assetsLibraryQmlSources/NewFolderDialog.qml +++ b/share/qtcreator/qmldesigner/assetsLibraryQmlSources/NewFolderDialog.qml @@ -86,8 +86,10 @@ StudioControls.Dialog { text: qsTr("Create") enabled: folderName.text !== "" && root.createdDirPath.length <= root.__maxPath onClicked: { - root.createdDirPath = root.dirPath + '/' + folderName.text - if (AssetsLibraryBackend.assetsModel.addNewFolder(root.createdDirPath)) + let dirPathToCreate = root.dirPath + '/' + folderName.text + root.createdDirPath = AssetsLibraryBackend.assetsModel.addNewFolder(root.createdDirPath) + + if (root.createdDirPath) root.accept() else creationFailedDialog.open() diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.cpp b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.cpp index faf4f1eb549..71cde5f0ba8 100644 --- a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.cpp +++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.cpp @@ -136,7 +136,7 @@ bool AssetsLibraryModel::renameFolder(const QString &folderPath, const QString & return dir.rename(oldName, newName); } -bool AssetsLibraryModel::addNewFolder(const QString &folderPath) +QString AssetsLibraryModel::addNewFolder(const QString &folderPath) { QString iterPath = folderPath; QDir dir{folderPath}; @@ -147,7 +147,7 @@ bool AssetsLibraryModel::addNewFolder(const QString &folderPath) dir.setPath(iterPath); } - return dir.mkpath(iterPath); + return dir.mkpath(iterPath) ? iterPath : ""; } bool AssetsLibraryModel::urlPathExistsInModel(const QUrl &url) const diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.h b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.h index f003ef2c541..e37bf8a109a 100644 --- a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.h +++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.h @@ -45,7 +45,7 @@ public: Q_INVOKABLE bool requestDeleteFiles(const QStringList &filePaths); Q_INVOKABLE void deleteFiles(const QStringList &filePaths, bool dontAskAgain); Q_INVOKABLE bool renameFolder(const QString &folderPath, const QString &newName); - Q_INVOKABLE bool addNewFolder(const QString &folderPath); + Q_INVOKABLE QString addNewFolder(const QString &folderPath); Q_INVOKABLE bool deleteFolderRecursively(const QModelIndex &folderIndex); Q_INVOKABLE bool allFilePathsAreTextures(const QStringList &filePaths) const; From 07adb77dd17acfbc10ee4e77ba532663cc81a8e8 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Fri, 31 Mar 2023 15:31:30 +0200 Subject: [PATCH 008/192] QmlDesigner: Add action for taking screenshots Task-numner: QDS-9431 Change-Id: I091e8cef646d08ee67935e2b3cea903b68a37a0c Reviewed-by: Qt CI Bot Reviewed-by: Thomas Hartmann --- .../qmldesigner/qmldesignerconstants.h | 1 + src/plugins/qmldesigner/shortcutmanager.cpp | 42 ++++++++++++++----- src/plugins/qmldesigner/shortcutmanager.h | 1 + 3 files changed, 33 insertions(+), 11 deletions(-) diff --git a/src/plugins/qmldesigner/qmldesignerconstants.h b/src/plugins/qmldesigner/qmldesignerconstants.h index 18039187509..743d70bcbc9 100644 --- a/src/plugins/qmldesigner/qmldesignerconstants.h +++ b/src/plugins/qmldesigner/qmldesignerconstants.h @@ -30,6 +30,7 @@ const char TOGGLE_RIGHT_SIDEBAR[] = "QmlDesigner.ToggleRightSideBar"; const char TOGGLE_STATES_EDITOR[] = "QmlDesigner.ToggleStatesEditor"; const char GO_INTO_COMPONENT[] = "QmlDesigner.GoIntoComponent"; const char EXPORT_AS_IMAGE[] = "QmlDesigner.ExportAsImage"; +const char TAKE_SCREENSHOT[] = "QmlDesigner.TakeScreenshot"; const char FORMEDITOR_REFRESH[] = "QmlDesigner.FormEditor.Refresh"; const char FORMEDITOR_SNAPPING[] = "QmlDesigner.FormEditor.Snapping"; const char FORMEDITOR_NO_SNAPPING[] = "QmlDesigner.FormEditor.NoSnapping"; diff --git a/src/plugins/qmldesigner/shortcutmanager.cpp b/src/plugins/qmldesigner/shortcutmanager.cpp index efb3bf49eee..add971a8c1f 100644 --- a/src/plugins/qmldesigner/shortcutmanager.cpp +++ b/src/plugins/qmldesigner/shortcutmanager.cpp @@ -39,21 +39,24 @@ #include #include +#include +#include namespace QmlDesigner { ShortCutManager::ShortCutManager() - : QObject(), - m_exportAsImageAction(tr("Export as &Image...")), - m_undoAction(tr("&Undo")), - m_redoAction(tr("&Redo")), - m_deleteAction(tr("Delete")), - m_cutAction(tr("Cu&t")), - m_copyAction(tr("&Copy")), - m_pasteAction(tr("&Paste")), - m_duplicateAction(tr("&Duplicate")), - m_selectAllAction(tr("Select &All")), - m_escapeAction(this) + : QObject() + , m_exportAsImageAction(tr("Export as &Image...")) + , m_takeScreenshotAction(tr("Take Screenshot")) + , m_undoAction(tr("&Undo")) + , m_redoAction(tr("&Redo")) + , m_deleteAction(tr("Delete")) + , m_cutAction(tr("Cu&t")) + , m_copyAction(tr("&Copy")) + , m_pasteAction(tr("&Paste")) + , m_duplicateAction(tr("&Duplicate")) + , m_selectAllAction(tr("Select &All")) + , m_escapeAction(this) { } @@ -111,6 +114,23 @@ void ShortCutManager::registerActions(const Core::Context &qmlDesignerMainContex Core::ActionManager::actionContainer(Core::Constants::M_EDIT) ->addAction(command, Core::Constants::G_EDIT_OTHER); + command = Core::ActionManager::registerAction(&m_takeScreenshotAction, + QmlDesigner::Constants::TAKE_SCREENSHOT); + connect(&m_takeScreenshotAction, &QAction::triggered, [] { + const auto folder = Utils::FilePath::fromString( + QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation)) + .pathAppended("QtDesignStudio/screenshots/"); + folder.createDir(); + + const auto file = folder.pathAppended(QDateTime::currentDateTime().toString("dddd-hh-mm-ss") + + ".png"); + + QPixmap pixmap = Core::ICore::mainWindow()->grab(); + + const bool b = pixmap.save(file.toString(), "PNG"); + qWarning() << "screenshot" << file << b << pixmap; + }); + connect(action, &QAction::triggered, this, [] { ToolBarBackend::launchGlobalAnnotations(); }); Core::ActionContainer *exportMenu = Core::ActionManager::actionContainer( diff --git a/src/plugins/qmldesigner/shortcutmanager.h b/src/plugins/qmldesigner/shortcutmanager.h index 9b4762748ef..b97fd834ee7 100644 --- a/src/plugins/qmldesigner/shortcutmanager.h +++ b/src/plugins/qmldesigner/shortcutmanager.h @@ -52,6 +52,7 @@ private: QAction m_saveAction; QAction m_saveAsAction; QAction m_exportAsImageAction; + QAction m_takeScreenshotAction; QAction m_closeCurrentEditorAction; QAction m_closeAllEditorsAction; QAction m_closeOtherEditorsAction; From 60646fa6acd6c6cc007a2b41445d807af91f90df Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Fri, 31 Mar 2023 16:40:22 +0200 Subject: [PATCH 009/192] QmlDesigner: Ensure statusbar spans the complete widget Task-number: QDS-9512 Change-Id: I6b4cb31ea7de7e8735450f327050290f15351b96 Reviewed-by: Qt CI Bot Reviewed-by: Thomas Hartmann --- src/plugins/qmldesigner/components/toolbar/toolbar.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/qmldesigner/components/toolbar/toolbar.cpp b/src/plugins/qmldesigner/components/toolbar/toolbar.cpp index 939595a2a7f..436515caa47 100644 --- a/src/plugins/qmldesigner/components/toolbar/toolbar.cpp +++ b/src/plugins/qmldesigner/components/toolbar/toolbar.cpp @@ -119,7 +119,7 @@ std::unique_ptr ToolBar::createStatusBar() w->hide(); } - Core::ICore::statusBar()->addWidget(quickWidget.get()); + Core::ICore::statusBar()->addPermanentWidget(quickWidget.get(), 100); Core::ICore::statusBar()->setFixedHeight(Theme::toolbarSize()); return quickWidget; From 42d84e012ceda25c12b915f356a6940a942466ba Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Mon, 3 Apr 2023 11:30:12 +0200 Subject: [PATCH 010/192] QmlDesigner: Use simplifiedTypeName to strip "QML" prefix MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Task-number: QDS-9608 Change-Id: I4fb59e603a0ab31727ee60a8af5b2c3ab4dfdf9b Reviewed-by: Henning Gründl --- .../qmldesigner/components/bindingeditor/bindingeditor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/qmldesigner/components/bindingeditor/bindingeditor.cpp b/src/plugins/qmldesigner/components/bindingeditor/bindingeditor.cpp index 5112cc3f860..0c85432464d 100644 --- a/src/plugins/qmldesigner/components/bindingeditor/bindingeditor.cpp +++ b/src/plugins/qmldesigner/components/bindingeditor/bindingeditor.cpp @@ -187,7 +187,7 @@ void BindingEditor::prepareBindings() for (const auto &objnode : allNodes) { BindingEditorDialog::BindingOption binding; for (const auto &property : objnode.metaInfo().properties()) { - const TypeName &propertyTypeName = property.propertyType().typeName(); + const TypeName &propertyTypeName = property.propertyType().simplifiedTypeName(); if (skipTypeFiltering || (m_backendValueTypeName == propertyTypeName) From 6f1657fa06b7a8aff23578b0700cfb3851d659e6 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Fri, 31 Mar 2023 10:54:06 +0200 Subject: [PATCH 011/192] QmlDesigner: Fallback to root context If we cannot resolve an expression in the proper context, we fallback to the root context. The current context should be always a child of the root context, but there are issues reported to be fixed by this. Change-Id: I3f91641ea10fd0fa3bf22a93455e291013f5acc1 Reviewed-by: Reviewed-by: Tim Jenssen --- .../qml2puppet/instances/objectnodeinstance.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/tools/qml2puppet/qml2puppet/instances/objectnodeinstance.cpp b/src/tools/qml2puppet/qml2puppet/instances/objectnodeinstance.cpp index 281e6030a05..017c6bbc3f2 100644 --- a/src/tools/qml2puppet/qml2puppet/instances/objectnodeinstance.cpp +++ b/src/tools/qml2puppet/qml2puppet/instances/objectnodeinstance.cpp @@ -500,7 +500,15 @@ void ObjectNodeInstance::setPropertyBinding(const PropertyName &name, const QStr if (!isSimpleExpression(expression)) return; - QmlPrivateGate::setPropertyBinding(object(), context(), name, expression); + QQmlExpression qmlExpression(context(), object(), expression); + qmlExpression.evaluate(); + if (qmlExpression.hasError()) + QmlPrivateGate::setPropertyBinding(object(), + context()->engine()->rootContext(), + name, + expression); + else + QmlPrivateGate::setPropertyBinding(object(), context(), name, expression); } void ObjectNodeInstance::deleteObjectsInList(const QQmlProperty &property) From 61d89aba67785cfb5e9ffc7f9aa63e7239dfe1e2 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Fri, 31 Mar 2023 10:52:45 +0200 Subject: [PATCH 012/192] QmlDesigner: Disable possible imports in createQmlObjectNodeFromSource In this context this is not required. Change-Id: I6d801eb11af1f60ed67b84bdd3db4d3db41da04e Reviewed-by: Tim Jenssen --- src/plugins/qmldesigner/designercore/model/qmlvisualnode.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/plugins/qmldesigner/designercore/model/qmlvisualnode.cpp b/src/plugins/qmldesigner/designercore/model/qmlvisualnode.cpp index c3ee4b975f7..3831a90d97c 100644 --- a/src/plugins/qmldesigner/designercore/model/qmlvisualnode.cpp +++ b/src/plugins/qmldesigner/designercore/model/qmlvisualnode.cpp @@ -291,6 +291,7 @@ static QmlObjectNode createQmlObjectNodeFromSource(AbstractView *view, rewriterView->setCheckSemanticErrors(false); rewriterView->setTextModifier(&modifier); rewriterView->setAllowComponentRoot(true); + rewriterView->setPossibleImportsEnabled(false); inputModel->setRewriterView(rewriterView.data()); if (rewriterView->errors().isEmpty() && rewriterView->rootModelNode().isValid()) { From a45bc00a0fad29c2a7a0b12d5735ccf77ae684e2 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Fri, 31 Mar 2023 10:51:33 +0200 Subject: [PATCH 013/192] QmlDesigner: Disable possible imports in toText In this context this is not required. Change-Id: I04e74661bafe9dd9a0f72fc17cb4e6cf01e564fe Reviewed-by: Tim Jenssen --- .../qmldesigner/components/integration/designdocumentview.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/plugins/qmldesigner/components/integration/designdocumentview.cpp b/src/plugins/qmldesigner/components/integration/designdocumentview.cpp index a587580590d..4a1c92ad0aa 100644 --- a/src/plugins/qmldesigner/components/integration/designdocumentview.cpp +++ b/src/plugins/qmldesigner/components/integration/designdocumentview.cpp @@ -115,6 +115,7 @@ QString DesignDocumentView::toText() const QScopedPointer rewriterView( new RewriterView(externalDependencies(), RewriterView::Amend)); rewriterView->setCheckSemanticErrors(false); + rewriterView->setPossibleImportsEnabled(false); rewriterView->setTextModifier(&modifier); outputModel->setRewriterView(rewriterView.data()); From 48ad79ee1ef9f850c99b4a3eebd85098ad054128 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Thu, 30 Mar 2023 13:27:34 +0200 Subject: [PATCH 014/192] Utils: Introduce UniqueObjectPtr We very often want to remove a Qobject in a scope but because it has a parent it can be deleted earlier. So using std::unique_ptr can lead to double deletion. UniqueObjectPtr can track if a pointer is already deleted. So no double deletion is possible but we still delete the object in that scope. UniqueObjectPtr is based on std::unique_ptr but uses a QPointer as internal pointer representation. Because QPointer is not convertable you cannot cast from one type to an other(QTBUG-112464). Because of that UniqueObjectInternalPointer derives from QPointer and adds a conversion constructor. Change-Id: I2c7707489f6db836cc5db2463efa8c33932b6455 Reviewed-by: Qt CI Bot Reviewed-by: Reviewed-by: Thomas Hartmann --- src/libs/utils/CMakeLists.txt | 1 + src/libs/utils/uniqueobjectptr.h | 58 +++++++++++++++++++ src/libs/utils/utils.qbs | 1 + .../components/toolbar/toolbar.cpp | 12 ++-- .../qmldesigner/components/toolbar/toolbar.h | 6 +- src/plugins/qmldesigner/qmldesignerplugin.cpp | 6 +- 6 files changed, 72 insertions(+), 12 deletions(-) create mode 100644 src/libs/utils/uniqueobjectptr.h diff --git a/src/libs/utils/CMakeLists.txt b/src/libs/utils/CMakeLists.txt index 59728939cb7..8777a6a881a 100644 --- a/src/libs/utils/CMakeLists.txt +++ b/src/libs/utils/CMakeLists.txt @@ -186,6 +186,7 @@ add_qtc_library(Utils treemodel.cpp treemodel.h treeviewcombobox.cpp treeviewcombobox.h uncommentselection.cpp uncommentselection.h + uniqueobjectptr.h unixutils.cpp unixutils.h url.cpp url.h utils.qrc diff --git a/src/libs/utils/uniqueobjectptr.h b/src/libs/utils/uniqueobjectptr.h new file mode 100644 index 00000000000..e660e459564 --- /dev/null +++ b/src/libs/utils/uniqueobjectptr.h @@ -0,0 +1,58 @@ +// 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 + +namespace Utils { + +namespace Internal { + +template +class UniqueObjectInternalPointer : public QPointer +{ +public: + using QPointer::QPointer; + + template + && !std::is_same_v, std::decay_t>>> + UniqueObjectInternalPointer(const UniqueObjectInternalPointer &p) noexcept + : QPointer{p.data()} + {} +}; + +template +struct UniqueObjectPtrDeleter +{ + using pointer = UniqueObjectInternalPointer; + + constexpr UniqueObjectPtrDeleter() noexcept = default; + template>> + constexpr UniqueObjectPtrDeleter(const UniqueObjectPtrDeleter &) noexcept + {} + + constexpr void operator()(pointer p) const + { + static_assert(!std::is_void_v, "can't delete pointer to incomplete type"); + static_assert(sizeof(Type) > 0, "can't delete pointer to incomplete type"); + + delete p.data(); + } +}; + +} // namespace Internal + +template +using UniqueObjectPtr = std::unique_ptr>; + +template +auto makeUniqueObjectPtr(Arguments &&...arguments) +{ + return UniqueObjectPtr{new Type(std::forward(arguments)...)}; +} +} // namespace Utils diff --git a/src/libs/utils/utils.qbs b/src/libs/utils/utils.qbs index 6fb84c69cc2..451a9f34943 100644 --- a/src/libs/utils/utils.qbs +++ b/src/libs/utils/utils.qbs @@ -329,6 +329,7 @@ Project { "headerviewstretcher.h", "uncommentselection.cpp", "uncommentselection.h", + "uniqueobjectptr.h" "unixutils.cpp", "unixutils.h", "url.cpp", diff --git a/src/plugins/qmldesigner/components/toolbar/toolbar.cpp b/src/plugins/qmldesigner/components/toolbar/toolbar.cpp index 436515caa47..c8e05999ed0 100644 --- a/src/plugins/qmldesigner/components/toolbar/toolbar.cpp +++ b/src/plugins/qmldesigner/components/toolbar/toolbar.cpp @@ -47,7 +47,7 @@ Utils::FilePath qmlSourcesPath() return Core::ICore::resourcePath("qmldesigner/toolbar"); } -std::unique_ptr ToolBar::create() +Utils::UniqueObjectPtr ToolBar::create() { if (!isVisible()) return nullptr; @@ -58,7 +58,7 @@ std::unique_ptr ToolBar::create() //Core::ICore::statusBar()->hide(); - auto toolBar = std::make_unique(); + auto toolBar = Utils::makeUniqueObjectPtr(); toolBar->setObjectName("QDS-TOOLBAR"); toolBar->setContextMenuPolicy(Qt::PreventContextMenu); @@ -66,7 +66,7 @@ std::unique_ptr ToolBar::create() toolBar->setFloatable(false); toolBar->setMovable(false); - auto quickWidget = new StudioQuickWidget; + auto quickWidget = std::make_unique(); quickWidget->setFixedHeight(48); quickWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); @@ -84,20 +84,20 @@ std::unique_ptr ToolBar::create() quickWidget->setSource(QUrl::fromLocalFile(qmlFilePath.toFSPathString())); - toolBar->addWidget(quickWidget); + toolBar->addWidget(quickWidget.release()); window->addToolBar(toolBar.get()); return toolBar; } -std::unique_ptr ToolBar::createStatusBar() +Utils::UniqueObjectPtr ToolBar::createStatusBar() { if (!isVisible()) return nullptr; ToolBarBackend::registerDeclarativeType(); - auto quickWidget = std::make_unique(); + auto quickWidget = Utils::makeUniqueObjectPtr(); quickWidget->setFixedHeight(Theme::toolbarSize()); quickWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); diff --git a/src/plugins/qmldesigner/components/toolbar/toolbar.h b/src/plugins/qmldesigner/components/toolbar/toolbar.h index 1cdb2121c65..d086fdf3ed2 100644 --- a/src/plugins/qmldesigner/components/toolbar/toolbar.h +++ b/src/plugins/qmldesigner/components/toolbar/toolbar.h @@ -5,7 +5,7 @@ #include -#include +#include namespace QmlDesigner { @@ -13,8 +13,8 @@ class ToolBar { public: - static std::unique_ptr create(); - static std::unique_ptr createStatusBar(); + static Utils::UniqueObjectPtr create(); + static Utils::UniqueObjectPtr createStatusBar(); static bool isVisible(); }; diff --git a/src/plugins/qmldesigner/qmldesignerplugin.cpp b/src/plugins/qmldesigner/qmldesignerplugin.cpp index c41e272ad26..47c20047aa6 100644 --- a/src/plugins/qmldesigner/qmldesignerplugin.cpp +++ b/src/plugins/qmldesigner/qmldesignerplugin.cpp @@ -31,7 +31,6 @@ #include #include #include - #include #include #include @@ -66,6 +65,7 @@ #include #include #include +#include #include #include @@ -144,8 +144,8 @@ public: DesignModeWidget mainWidget; QtQuickDesignerFactory m_qtQuickDesignerFactory; bool blockEditorChange = false; - std::unique_ptr toolBar; - std::unique_ptr statusBar; + Utils::UniqueObjectPtr toolBar; + Utils::UniqueObjectPtr statusBar; }; QmlDesignerPlugin *QmlDesignerPlugin::m_instance = nullptr; From f31cd974049a7126b0b179b3afccff3a11d82517 Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Fri, 10 Mar 2023 09:58:18 +0200 Subject: [PATCH 015/192] QmlDesigner: Implement StudioStyle - StudioStyle is implemented as an independent style dedicated to QmlDesigner. - QmlDesigner style part is removed from ManhattanStyle. - Buttons are handled in StudioStyle. - An standard palette is defined for StudioStyle. - StudioStyle is applied to the application by QmlDesignerBasePlugin. Task-number: QDS-9274 Change-Id: Id0a9040d82b6228a8fb215a416a916be116542bc Reviewed-by: Qt CI Bot Reviewed-by: Thomas Hartmann --- src/plugins/coreplugin/manhattanstyle.cpp | 977 +++--------------- src/plugins/coreplugin/manhattanstyle.h | 110 +- .../components/formeditor/toolbox.cpp | 4 +- src/plugins/qmldesigner/designmodewidget.cpp | 4 + src/plugins/qmldesignerbase/CMakeLists.txt | 1 + .../qmldesignerbase/qmldesignerbaseplugin.cpp | 23 +- .../qmldesignerbase/qmldesignerbaseplugin.h | 1 + .../qmldesignerbase/utils/studiostyle.cpp | 953 +++++++++++++++++ .../qmldesignerbase/utils/studiostyle.h | 70 ++ 9 files changed, 1244 insertions(+), 899 deletions(-) create mode 100644 src/plugins/qmldesignerbase/utils/studiostyle.cpp create mode 100644 src/plugins/qmldesignerbase/utils/studiostyle.h diff --git a/src/plugins/coreplugin/manhattanstyle.cpp b/src/plugins/coreplugin/manhattanstyle.cpp index 4b8d78dab03..a67deb4f41a 100644 --- a/src/plugins/coreplugin/manhattanstyle.cpp +++ b/src/plugins/coreplugin/manhattanstyle.cpp @@ -24,7 +24,6 @@ #include #include #include -#include #include #include #include @@ -87,23 +86,6 @@ bool panelWidget(const QWidget *widget) return false; } -// Consider making this a QStyle state -static bool isQmlEditorMenu(const QWidget *widget) -{ - const QMenu *menu = qobject_cast (widget); - if (!menu) - return false; - - const QWidget *p = widget; - while (p) { - if (p->property("qmlEditorMenu").toBool()) - return styleEnabled(widget); - p = p->parentWidget(); - } - - return false; -} - // Consider making this a QStyle state bool lightColored(const QWidget *widget) { @@ -128,251 +110,10 @@ static bool isDarkFusionStyle(const QStyle *style) && strcmp(style->metaObject()->className(), "QFusionStyle") == 0; } -QColor qmlEditorTextColor(bool enabled, - bool active, - bool checked) -{ - Theme::Color themePenColorId = enabled ? (active - ? (checked ? Theme::DSsubPanelBackground - : Theme::DSpanelBackground) - : Theme::DStextColor) - : Theme::DStextColorDisabled; - - return creatorTheme()->color(themePenColorId); -} - -QPixmap getDeletePixmap(bool enabled, - bool active, - const QSize &sizeLimit) -{ - using Utils::Theme; - using Utils::creatorTheme; - using Utils::StyleHelper; - - const double xRatio = 19; - const double yRatio = 9; - double sizeConst = std::min(xRatio * sizeLimit.height(), yRatio * sizeLimit.width()); - sizeConst = std::max(xRatio, sizeConst); - - const int height = sizeConst/xRatio; - const int width = sizeConst/yRatio; - QPixmap retval(width, height); - QPainter p(&retval); - const qreal devicePixelRatio = p.device()->devicePixelRatio(); - - QPixmap pixmap; - QString pixmapName = QLatin1String("StyleHelper::drawDelete") - + "-" + QString::number(sizeConst) - + "-" + QString::number(enabled) - + "-" + QString::number(active) - + "-" + QString::number(devicePixelRatio); - - if (!QPixmapCache::find(pixmapName, &pixmap)) { - QImage image(width * devicePixelRatio, height * devicePixelRatio, QImage::Format_ARGB32_Premultiplied); - image.fill(Qt::transparent); - QPainter painter(&image); - - auto drawDelete = [&painter, yRatio](const QRect &rect, const QColor &color) -> void - { - static const QStyle* const style = QApplication::style(); - if (!style) - return; - - const int height = rect.height(); - const int width = rect.width(); - const int insideW = height / 2; - const int penWidth = std::ceil(height / yRatio); - const int pixelGuard = penWidth / 2; - const QRect xRect = {insideW + (4 * penWidth), - 2 * penWidth, - 4 * penWidth, - 5 * penWidth}; - - // Workaround for QTCREATORBUG-28470 - painter.save(); - painter.setOpacity(color.alphaF()); - - QPen pen(color, penWidth); - pen.setJoinStyle(Qt::MiterJoin); - painter.setPen(pen); - - QPainterPath pp(QPointF(pixelGuard, insideW)); - pp.lineTo(insideW, pixelGuard); - pp.lineTo(width - pixelGuard, pixelGuard); - pp.lineTo(width - pixelGuard, height - pixelGuard); - pp.lineTo(insideW, height - pixelGuard); - pp.lineTo(pixelGuard, insideW); - - painter.drawPath(pp); - - // drawing X - painter.setPen(QPen(color, 1)); - QPoint stepOver(penWidth, 0); - - pp.clear(); - pp.moveTo(xRect.topLeft()); - pp.lineTo(xRect.topLeft() + stepOver); - pp.lineTo(xRect.bottomRight()); - pp.lineTo(xRect.bottomRight() - stepOver); - pp.lineTo(xRect.topLeft()); - painter.fillPath(pp, QBrush(color)); - - pp.clear(); - pp.moveTo(xRect.topRight()); - pp.lineTo(xRect.topRight() - stepOver); - pp.lineTo(xRect.bottomLeft()); - pp.lineTo(xRect.bottomLeft() + stepOver); - pp.lineTo(xRect.topRight()); - painter.fillPath(pp, QBrush(color)); - - painter.restore(); - }; - - if (enabled && creatorTheme()->flag(Theme::ToolBarIconShadow)) - drawDelete(image.rect().translated(0, devicePixelRatio), StyleHelper::toolBarDropShadowColor()); - - drawDelete(image.rect(), qmlEditorTextColor(enabled, active, false)); - - painter.end(); - pixmap = QPixmap::fromImage(image); - pixmap.setDevicePixelRatio(devicePixelRatio); - QPixmapCache::insert(pixmapName, pixmap); - } - return pixmap; -} - -struct ManhattanShortcut { - ManhattanShortcut(const QStyleOptionMenuItem *option, - const QString &shortcutText) - : shortcutText(shortcutText) - , enabled(option->state & QStyle::State_Enabled) - , active(option->state & QStyle::State_Selected) - , font(option->font) - , fm(font) - , defaultHeight(fm.height()) - , palette(option->palette) - , spaceConst(fm.boundingRect(".").width()) - { - reset(); - } - - QSize getSize() - { - if (isFirstParticle) - calcResult(); - return _size; - } - - QPixmap getPixmap() - { - if (!isFirstParticle && !_pixmap.isNull()) - return _pixmap; - - _pixmap = QPixmap(getSize()); - _pixmap.fill(Qt::transparent); - QPainter painter(&_pixmap); - painter.setFont(font); - QPen pPen = painter.pen(); - pPen.setColor(qmlEditorTextColor(enabled, active, false)); - painter.setPen(pPen); - calcResult(&painter); - painter.end(); - - return _pixmap; - } - -private: - void applySize(const QSize &itemSize) { - width += itemSize.width(); - height = std::max(height, itemSize.height()); - if (isFirstParticle) - isFirstParticle = false; - else - width += spaceConst; - }; - - void addText(const QString &txt, QPainter *painter = nullptr) - { - if (txt.size()) { - int textWidth = fm.boundingRect(txt).width(); - QSize itemSize = {textWidth, defaultHeight}; - if (painter) { - QRect placeRect({width, 0}, itemSize); - painter->drawText(placeRect, txt, textOption); - } - applySize(itemSize); - } - }; - - void addPixmap(const QPixmap &pixmap, QPainter *painter = nullptr) - { - if (painter) - painter->drawPixmap(QRect({width, 0}, pixmap.size()), pixmap); - - applySize(pixmap.size()); - }; - - void calcResult(QPainter *painter = nullptr) - { - reset(); -#ifndef QT_NO_SHORTCUT - if (!shortcutText.isEmpty()) { - int fwdIndex = 0; - QRegularExpressionMatch mMatch = backspaceDetect.match(shortcutText); - int matchCount = mMatch.lastCapturedIndex(); - - for (int i = 0; i <= matchCount; ++i) { - QString mStr = mMatch.captured(i); - QPixmap pixmap = getDeletePixmap(enabled, - active, - {defaultHeight * 3, defaultHeight}); - - int lIndex = shortcutText.indexOf(mStr, fwdIndex); - int diffChars = lIndex - fwdIndex; - addText(shortcutText.mid(fwdIndex, diffChars), painter); - addPixmap(pixmap, painter); - fwdIndex = lIndex + mStr.size(); - } - addText(shortcutText.mid(fwdIndex), painter); - } -#endif - _size = {width, height}; - } - - void reset() - { - isFirstParticle = true; - width = 0; - height = 0; - } - - const QString shortcutText; - const bool enabled; - const bool active; - const QFont font; - const QFontMetrics fm; - const int defaultHeight; - const QPalette palette; - const int spaceConst; - static const QTextOption textOption; - static const QRegularExpression backspaceDetect; - bool isFirstParticle = true; - - int width = 0; - int height = 0; - QSize _size; - QPixmap _pixmap; -}; -const QRegularExpression ManhattanShortcut::backspaceDetect("\\+*backspace\\+*", - QRegularExpression::CaseInsensitiveOption); -const QTextOption ManhattanShortcut::textOption(Qt::AlignLeft | Qt::AlignVCenter); - - class ManhattanStylePrivate { public: explicit ManhattanStylePrivate(); - void init(); public: const QIcon extButtonIcon; @@ -380,15 +121,15 @@ public: StyleAnimator animator; }; -ManhattanStylePrivate::ManhattanStylePrivate() : - extButtonIcon(Utils::Icons::TOOLBAR_EXTENSION.icon()), - closeButtonPixmap(Utils::Icons::CLOSE_FOREGROUND.pixmap()) +ManhattanStylePrivate::ManhattanStylePrivate() + : extButtonIcon(Utils::Icons::TOOLBAR_EXTENSION.icon()) + , closeButtonPixmap(Utils::Icons::CLOSE_FOREGROUND.pixmap()) { } ManhattanStyle::ManhattanStyle(const QString &baseStyleName) - : QProxyStyle(QStyleFactory::create(baseStyleName)), - d(new ManhattanStylePrivate()) + : QProxyStyle(QStyleFactory::create(baseStyleName)) + , d(new ManhattanStylePrivate()) { } @@ -398,7 +139,10 @@ ManhattanStyle::~ManhattanStyle() d = nullptr; } -QPixmap ManhattanStyle::generatedIconPixmap(QIcon::Mode iconMode, const QPixmap &pixmap, const QStyleOption *opt) const +QPixmap ManhattanStyle::generatedIconPixmap( + QIcon::Mode iconMode, + const QPixmap &pixmap, + const QStyleOption *opt) const { return QProxyStyle::generatedIconPixmap(iconMode, pixmap, opt); } @@ -417,67 +161,25 @@ QSize ManhattanStyle::sizeFromContents(ContentsType type, const QStyleOption *op if (panelWidget(widget)) newSize += QSize(14, 0); break; - case CT_MenuItem: - if (isQmlEditorMenu(widget)) { - if (const auto mbi = qstyleoption_cast(option)) { - const int leftMargin = pixelMetric(QStyle::PM_LayoutLeftMargin, option, widget); - const int rightMargin = pixelMetric(QStyle::PM_LayoutRightMargin, option, widget); - const int horizontalSpacing = pixelMetric(QStyle::PM_LayoutHorizontalSpacing, option, widget); - const int iconHeight = pixelMetric(QStyle::PM_SmallIconSize, option, widget) + horizontalSpacing; - int width = leftMargin + rightMargin; - if (mbi->menuHasCheckableItems || mbi->maxIconWidth) - width += iconHeight + horizontalSpacing; - - if (!mbi->text.isEmpty()) { - QString itemText = mbi->text; - QString shortcutText; - int tabIndex = itemText.indexOf("\t"); - if (tabIndex > -1) { - shortcutText = itemText.mid(tabIndex + 1); - itemText = itemText.left(tabIndex); - } - - if (itemText.size()) - width += option->fontMetrics.boundingRect(itemText).width() + horizontalSpacing; - - if (shortcutText.size()) { - QSize shortcutSize = ManhattanShortcut(mbi, shortcutText).getSize(); - width += shortcutSize.width() + 2 * horizontalSpacing; - } - } - - if (mbi->menuItemType == QStyleOptionMenuItem::SubMenu) - width += iconHeight + horizontalSpacing; - - newSize.setWidth(width); - - switch (mbi->menuItemType) { - case QStyleOptionMenuItem::Normal: - case QStyleOptionMenuItem::DefaultItem: - case QStyleOptionMenuItem::SubMenu: - newSize.setHeight(19); - break; - default: - newSize += QSize(0, 7); - break; - } - } - } - break; default: break; } - return newSize; } -QRect ManhattanStyle::subElementRect(SubElement element, const QStyleOption *option, const QWidget *widget) const +QRect ManhattanStyle::subElementRect( + SubElement element, + const QStyleOption *option, + const QWidget *widget) const { return QProxyStyle::subElementRect(element, option, widget); } -QRect ManhattanStyle::subControlRect(ComplexControl control, const QStyleOptionComplex *option, - SubControl subControl, const QWidget *widget) const +QRect ManhattanStyle::subControlRect( + ComplexControl control, + const QStyleOptionComplex *option, + SubControl subControl, + const QWidget *widget) const { #if QT_VERSION < QT_VERSION_CHECK(6, 2, 5) // Workaround for QTBUG-101581, can be removed when building with Qt 6.2.5 or higher @@ -487,30 +189,7 @@ QRect ManhattanStyle::subControlRect(ComplexControl control, const QStyleOptionC return QRect(); // breaks the scrollbar, but avoids the crash } #endif - - QRect retval = QProxyStyle::subControlRect(control, option, subControl, widget);; - if (panelWidget(widget)) { - if (const QStyleOptionSlider *slider = qstyleoption_cast(option)) { - switch (subControl) { - case SubControl::SC_SliderGroove: - return option->rect; - case SubControl::SC_SliderHandle: - { - int thickness = 2; - QPoint center = retval.center(); - const QRect &rect = slider->rect; - if (slider->orientation == Qt::Horizontal) - return QRect(center.x() - thickness, rect.top(), (thickness * 2) + 1, rect.height()); - else - return QRect(rect.left(), center.y() - thickness, rect.width(), (thickness * 2) + 1); - } - break; - default: - break; - } - } - } - return retval; + return QProxyStyle::subControlRect(control, option, subControl, widget); } QStyle::SubControl ManhattanStyle::hitTestComplexControl(ComplexControl control, const QStyleOptionComplex *option, @@ -534,61 +213,31 @@ int ManhattanStyle::pixelMetric(PixelMetric metric, const QStyleOption *option, retval = 16; break; case PM_SmallIconSize: - if (isQmlEditorMenu(widget)) - retval = 10; - else - retval = 16; + retval = 16; break; case PM_DockWidgetHandleExtent: case PM_DockWidgetSeparatorExtent: - return 1; - case PM_LayoutLeftMargin: - case PM_LayoutRightMargin: - if (isQmlEditorMenu(widget)) - retval = 7; - break; - case PM_LayoutHorizontalSpacing: - if (isQmlEditorMenu(widget)) - retval = 12; - break; - case PM_MenuHMargin: - if (isQmlEditorMenu(widget)) - retval = 5; - break; - case PM_SubMenuOverlap: - if (isQmlEditorMenu(widget)) - retval = 10; + retval = 1; break; case PM_MenuPanelWidth: case PM_MenuBarHMargin: case PM_MenuBarVMargin: case PM_ToolBarFrameWidth: - if (panelWidget(widget) || isQmlEditorMenu(widget)) + if (panelWidget(widget)) retval = 1; break; case PM_ButtonShiftVertical: case PM_ButtonShiftHorizontal: case PM_MenuBarPanelWidth: case PM_ToolBarItemMargin: - if (StyleHelper::isQDSTheme()) { - retval = 0; - break; - } - [[fallthrough]]; case PM_ToolBarItemSpacing: if (panelWidget(widget)) retval = 0; - if (StyleHelper::isQDSTheme()) - retval = 4; break; case PM_DefaultFrameWidth: if (qobject_cast(widget) && panelWidget(widget)) return 1; break; - case PM_ToolBarExtensionExtent: - if (StyleHelper::isQDSTheme()) - retval = 29; - break; default: break; } @@ -712,16 +361,19 @@ QIcon ManhattanStyle::standardIcon(StandardPixmap standardIcon, const QStyleOpti case QStyle::SP_ToolBarHorizontalExtensionButton: icon = d->extButtonIcon; break; - default: + case QStyle::SP_ComputerIcon: + { icon = QProxyStyle::standardIcon(standardIcon, option, widget); - break; - } - if (standardIcon == QStyle::SP_ComputerIcon) { // Ubuntu has in some versions a 16x16 icon, see QTCREATORBUG-12832 const QList &sizes = icon.availableSizes(); if (Utils::allOf(sizes, [](const QSize &size) { return size.width() < 32;})) icon = QIcon(":/utils/images/Desktop.png"); + } + break; + default: + icon = QProxyStyle::standardIcon(standardIcon, option, widget); + break; } return icon; } @@ -902,8 +554,6 @@ void ManhattanStyle::drawPrimitive(PrimitiveElement element, const QStyleOption { if (panelWidget(widget)) { drawPrimitiveForPanelWidget(element, option, painter, widget); - } else if (isQmlEditorMenu(widget)) { - drawPrimitiveForQmlEditor(element, option, painter, widget); } else { const bool tweakDarkTheme = (element == PE_Frame @@ -917,7 +567,6 @@ void ManhattanStyle::drawPrimitive(PrimitiveElement element, const QStyleOption drawPrimitiveTweakedForDarkTheme(element, option, painter, widget); else QProxyStyle::drawPrimitive(element, option, painter, widget); - return; } } @@ -926,61 +575,8 @@ void ManhattanStyle::drawPrimitiveForPanelWidget(PrimitiveElement element, QPainter *painter, const QWidget *widget) const { - bool animating = (option->state & State_Animating); int state = option->state; QRect rect = option->rect; - QRect oldRect; - QRect newRect; - if (widget && (element == PE_PanelButtonTool) && !animating) { - auto w = const_cast (widget); - int oldState = w->property("_q_stylestate").toInt(); - oldRect = w->property("_q_stylerect").toRect(); - newRect = w->rect(); - w->setProperty("_q_stylestate", (int)option->state); - w->setProperty("_q_stylerect", w->rect()); - - // Determine the animated transition - bool doTransition = ((state & State_On) != (oldState & State_On) || - (state & State_MouseOver) != (oldState & State_MouseOver)); - if (oldRect != newRect) - { - doTransition = false; - d->animator.stopAnimation(widget); - } - - if (doTransition) { - QImage startImage(option->rect.size(), QImage::Format_ARGB32_Premultiplied); - QImage endImage(option->rect.size(), QImage::Format_ARGB32_Premultiplied); - Animation *anim = d->animator.widgetAnimation(widget); - QStyleOption opt = *option; - opt.state = (QStyle::State)oldState; - opt.state |= State_Animating; - startImage.fill(0); - auto t = new Transition; - t->setWidget(w); - QPainter startPainter(&startImage); - if (!anim) { - drawPrimitive(element, &opt, &startPainter, widget); - } else { - anim->paint(&startPainter, &opt); - d->animator.stopAnimation(widget); - } - QStyleOption endOpt = *option; - endOpt.state |= State_Animating; - t->setStartImage(startImage); - d->animator.startAnimation(t); - endImage.fill(0); - QPainter endPainter(&endImage); - drawPrimitive(element, &endOpt, &endPainter, widget); - t->setEndImage(endImage); - if (oldState & State_MouseOver) - t->setDuration(150); - else - t->setDuration(75); - t->setStartTime(QTime::currentTime()); - } - } - switch (element) { case PE_IndicatorDockWidgetResizeHandle: painter->fillRect(option->rect, creatorTheme()->color(Theme::DockWidgetResizeHandleColor)); @@ -1033,39 +629,90 @@ void ManhattanStyle::drawPrimitiveForPanelWidget(PrimitiveElement element, break; case PE_PanelButtonTool: { - Animation *anim = d->animator.widgetAnimation(widget); - if (!animating && anim) { - anim->paint(painter, option); - } else { - bool pressed = option->state & State_Sunken || option->state & State_On; - painter->setPen(StyleHelper::sidebarShadow()); - if (pressed) { - const QColor shade = creatorTheme()->color(Theme::FancyToolButtonSelectedColor); - painter->fillRect(rect, shade); - if (!creatorTheme()->flag(Theme::FlatToolBars)) { - const QRectF borderRect = QRectF(rect).adjusted(0.5, 0.5, -0.5, -0.5); - painter->drawLine(borderRect.topLeft() + QPointF(1, 0), borderRect.topRight() - QPointF(1, 0)); - painter->drawLine(borderRect.topLeft(), borderRect.bottomLeft()); - painter->drawLine(borderRect.topRight(), borderRect.bottomRight()); - } - } else if (option->state & State_Enabled && option->state & State_MouseOver) { - painter->fillRect(rect, creatorTheme()->color(Theme::FancyToolButtonHoverColor)); - } else if (widget && widget->property("highlightWidget").toBool()) { - QColor shade(0, 0, 0, 128); - painter->fillRect(rect, shade); + bool animating = (state & State_Animating); + Animation *anim = d->animator.widgetAnimation(widget); + painter->save(); + if (widget && !animating) { + auto w = const_cast (widget); + int oldState = w->property("_q_stylestate").toInt(); + QRect oldRect = w->property("_q_stylerect").toRect(); + QRect newRect = w->rect(); + w->setProperty("_q_stylestate", (int)option->state); + w->setProperty("_q_stylerect", w->rect()); + + // Determine the animated transition + bool doTransition = ((state & State_On) != (oldState & State_On) || + (state & State_MouseOver) != (oldState & State_MouseOver)); + if (oldRect != newRect) { + doTransition = false; + d->animator.stopAnimation(widget); + } + + if (doTransition) { + QImage startImage(option->rect.size(), QImage::Format_ARGB32_Premultiplied); + QImage endImage(option->rect.size(), QImage::Format_ARGB32_Premultiplied); + QStyleOption opt = *option; + opt.state = (QStyle::State)oldState; + opt.state |= State_Animating; + startImage.fill(0); + auto t = new Transition; + t->setWidget(w); + QPainter startPainter(&startImage); + if (!anim) { + drawPrimitive(element, &opt, &startPainter, widget); + } else { + anim->paint(&startPainter, &opt); + d->animator.stopAnimation(widget); } - if (option->state & State_HasFocus && (option->state & State_KeyboardFocusChange)) { - QColor highlight = option->palette.highlight().color(); - highlight.setAlphaF(0.4f); - painter->setPen(QPen(highlight.lighter(), 1)); - highlight.setAlphaF(0.3f); - painter->setBrush(highlight); - painter->setRenderHint(QPainter::Antialiasing); - const QRectF rect = option->rect; - painter->drawRoundedRect(rect.adjusted(2.5, 2.5, -2.5, -2.5), 2, 2); - } - } + QStyleOption endOpt = *option; + endOpt.state |= State_Animating; + t->setStartImage(startImage); + d->animator.startAnimation(t); + endImage.fill(0); + QPainter endPainter(&endImage); + drawPrimitive(element, &endOpt, &endPainter, widget); + t->setEndImage(endImage); + if (oldState & State_MouseOver) + t->setDuration(150); + else + t->setDuration(75); + t->setStartTime(QTime::currentTime()); + } } + + if (!animating && anim) { + anim->paint(painter, option); + } else { + bool pressed = option->state & State_Sunken || option->state & State_On; + painter->setPen(StyleHelper::sidebarShadow()); + if (pressed) { + const QColor shade = creatorTheme()->color(Theme::FancyToolButtonSelectedColor); + painter->fillRect(rect, shade); + if (!creatorTheme()->flag(Theme::FlatToolBars)) { + const QRectF borderRect = QRectF(rect).adjusted(0.5, 0.5, -0.5, -0.5); + painter->drawLine(borderRect.topLeft() + QPointF(1, 0), borderRect.topRight() - QPointF(1, 0)); + painter->drawLine(borderRect.topLeft(), borderRect.bottomLeft()); + painter->drawLine(borderRect.topRight(), borderRect.bottomRight()); + } + } else if (option->state & State_Enabled && option->state & State_MouseOver) { + painter->fillRect(rect, creatorTheme()->color(Theme::FancyToolButtonHoverColor)); + } else if (widget && widget->property("highlightWidget").toBool()) { + QColor shade(0, 0, 0, 128); + painter->fillRect(rect, shade); + } + if (option->state & State_HasFocus && (option->state & State_KeyboardFocusChange)) { + QColor highlight = option->palette.highlight().color(); + highlight.setAlphaF(0.4f); + painter->setPen(QPen(highlight.lighter(), 1)); + highlight.setAlphaF(0.3f); + painter->setBrush(highlight); + painter->setRenderHint(QPainter::Antialiasing); + const QRectF rect = option->rect; + painter->drawRoundedRect(rect.adjusted(2.5, 2.5, -2.5, -2.5), 2, 2); + } + } + painter->restore(); + } break; case PE_PanelStatusBar: @@ -1157,227 +804,11 @@ void ManhattanStyle::drawPrimitiveForPanelWidget(PrimitiveElement element, } } -void ManhattanStyle::drawPrimitiveForQmlEditor(PrimitiveElement element, - const QStyleOption *option, - QPainter *painter, - const QWidget *widget) const -{ - const auto mbi = qstyleoption_cast(option); - if (!mbi) { - QProxyStyle::drawPrimitive(element, option, painter, widget); - return; - } - - switch (element) { - case PE_IndicatorArrowUp: - case PE_IndicatorArrowDown: - { - QStyleOptionMenuItem item = *mbi; - item.palette = QPalette(Qt::white); - StyleHelper::drawMinimalArrow(element, painter, &item); - } - break; - case PE_IndicatorArrowRight: - drawQmlEditorIcon(element, option, "cascadeIconRight", painter, widget); - break; - case PE_IndicatorArrowLeft: - drawQmlEditorIcon(element, option, "cascadeIconLeft", painter, widget); - break; - case PE_PanelButtonCommand: - break; - case PE_IndicatorMenuCheckMark: - drawQmlEditorIcon(element, option, "tickIcon", painter, widget); - break; - case PE_FrameMenu: - case PE_PanelMenu: - { - painter->save(); - painter->setBrush(creatorTheme()->color(Theme::DSsubPanelBackground)); - painter->setPen(Qt::NoPen); - painter->drawRect(option->rect); - painter->restore(); - } - break; - default: - QProxyStyle::drawPrimitive(element, option, painter, widget); - break; - } -} - -void ManhattanStyle::drawControlForQmlEditor(ControlElement element, - const QStyleOption *option, - QPainter *painter, - const QWidget *widget) const -{ - Q_UNUSED(element) - if (const auto mbi = qstyleoption_cast(option)) { - painter->save(); - const int iconHeight = pixelMetric(QStyle::PM_SmallIconSize, option, widget); - const int horizontalSpacing = pixelMetric(QStyle::PM_LayoutHorizontalSpacing, option, widget); - const int iconWidth = iconHeight; - const bool isActive = mbi->state & State_Selected; - const bool isDisabled = !(mbi->state & State_Enabled); - const bool isCheckable = mbi->checkType != QStyleOptionMenuItem::NotCheckable; - const bool isChecked = isCheckable ? mbi->checked : false; - int startMargin = pixelMetric(QStyle::PM_LayoutLeftMargin, option, widget); - int endMargin = pixelMetric(QStyle::PM_LayoutRightMargin, option, widget); - int forwardX = 0; - - if (option->direction == Qt::RightToLeft) - std::swap(startMargin, endMargin); - - QStyleOptionMenuItem item = *mbi; - - if (isActive) { - painter->fillRect(item.rect, creatorTheme()->color(Theme::DSinteraction)); - } - forwardX += startMargin; - - if (item.menuItemType == QStyleOptionMenuItem::Separator) { - int commonHeight = item.rect.center().y(); - int additionalMargin = forwardX /*hmargin*/; - QLineF separatorLine (item.rect.left() + additionalMargin, - commonHeight, - item.rect.right() - additionalMargin, - commonHeight); - - painter->setPen(creatorTheme()->color(Theme::DSstateSeparatorColor)); - painter->drawLine(separatorLine); - item.text.clear(); - painter->restore(); - return; - } - - QPixmap iconPixmap; - QIcon::Mode mode = isDisabled ? QIcon::Disabled : ((isActive) ? QIcon::Active : QIcon::Normal); - QIcon::State state = isChecked ? QIcon::On : QIcon::Off; - QColor themePenColor = qmlEditorTextColor(!isDisabled, isActive, isChecked); - - if (!item.icon.isNull()) { - iconPixmap = item.icon.pixmap(QSize(iconHeight, iconHeight), mode, state); - } else if (isCheckable) { - iconPixmap = QPixmap(iconHeight, iconHeight); - iconPixmap.fill(Qt::transparent); - - if (item.checked) { - QStyleOptionMenuItem so = item; - so.rect = iconPixmap.rect(); - QPainter dPainter(&iconPixmap); - dPainter.setPen(themePenColor); - drawPrimitive(PE_IndicatorMenuCheckMark, &so, &dPainter, widget); - } - } - - if (!iconPixmap.isNull()) { - QRect vCheckRect = visualRect(item.direction, - item.rect, - QRect(item.rect.x() + forwardX, - item.rect.y(), - iconWidth, - item.rect.height())); - - QRect pmr(QPoint(0, 0), iconPixmap.deviceIndependentSize().toSize()); - pmr.moveCenter(vCheckRect.center()); - painter->setPen(themePenColor); - painter->drawPixmap(pmr.topLeft(), iconPixmap); - - item.checkType = QStyleOptionMenuItem::NotCheckable; - item.checked = false; - item.icon = {}; - } - if (item.menuHasCheckableItems || item.maxIconWidth > 0) { - forwardX += iconWidth + horizontalSpacing; - } - - QString shortcutText; - int tabIndex = item.text.indexOf("\t"); - if (tabIndex > -1) { - shortcutText = item.text.mid(tabIndex + 1); - item.text = item.text.left(tabIndex); - } - - if (item.text.size()) { - painter->save(); - - QRect vTextRect = visualRect(item.direction, - item.rect, - item.rect.adjusted(forwardX, 0 , 0 , 0)); - - Qt::Alignment alignmentFlags = item.direction == Qt::LeftToRight ? Qt::AlignLeft - : Qt::AlignRight; - alignmentFlags |= Qt::AlignVCenter; - - int textFlags = Qt::TextShowMnemonic | Qt::TextDontClip | Qt::TextSingleLine; - if (!proxy()->styleHint(SH_UnderlineShortcut, &item, widget)) - textFlags |= Qt::TextHideMnemonic; - textFlags |= alignmentFlags; - - painter->setPen(themePenColor); - painter->drawText(vTextRect, textFlags, item.text); - painter->restore(); - } - - if (item.menuItemType == QStyleOptionMenuItem::SubMenu) { - PrimitiveElement dropDirElement = item.direction == Qt::LeftToRight ? PE_IndicatorArrowRight - : PE_IndicatorArrowLeft; - - QSize elSize(iconHeight, iconHeight); - int xOffset = iconHeight + endMargin; - int yOffset = (item.rect.height() - iconHeight) / 2; - QRect dropRect(item.rect.topRight(), elSize); - dropRect.adjust(-xOffset, yOffset, -xOffset, yOffset); - - QStyleOptionMenuItem so = item; - so.rect = visualRect(item.direction, - item.rect, - dropRect); - - drawPrimitive(dropDirElement, &so, painter, widget); - } else if (!shortcutText.isEmpty()) { - QPixmap pix = ManhattanShortcut(&item, shortcutText).getPixmap(); - - if (pix.width()) { - int xOffset = pix.width() + (iconHeight / 2) + endMargin; - QRect shortcutRect = item.rect.translated({item.rect.width() - xOffset, 0}); - shortcutRect.setSize({pix.width(), item.rect.height()}); - shortcutRect = visualRect(item.direction, - item.rect, - shortcutRect); - drawItemPixmap(painter, - shortcutRect, - Qt::AlignRight | Qt::AlignVCenter, - pix); - } - } - painter->restore(); - } -} - -void ManhattanStyle::drawQmlEditorIcon(PrimitiveElement element, - const QStyleOption *option, - const char *propertyName, - QPainter *painter, - const QWidget *widget) const -{ - if (option->styleObject && option->styleObject->property(propertyName).isValid()) { - const auto mbi = qstyleoption_cast(option); - if (mbi) { - const bool checkable = mbi->checkType != QStyleOptionMenuItem::NotCheckable; - const bool isDisabled = !(mbi->state & State_Enabled); - const bool isActive = mbi->state & State_Selected; - QIcon icon = mbi->styleObject->property(propertyName).value(); - QIcon::Mode mode = isDisabled ? QIcon::Disabled : ((isActive) ? QIcon::Active : QIcon::Normal); - QIcon::State state = (checkable && mbi->checked) ? QIcon::On : QIcon::Off; - QPixmap pix = icon.pixmap(option->rect.size(), mode, state); - drawItemPixmap(painter, option->rect, Qt::AlignCenter, pix); - return; - } - } - QProxyStyle::drawPrimitive(element, option, painter, widget); -} - -void ManhattanStyle::drawControl(ControlElement element, const QStyleOption *option, - QPainter *painter, const QWidget *widget) const +void ManhattanStyle::drawControl( + ControlElement element, + const QStyleOption *option, + QPainter *painter, + const QWidget *widget) const { if (!panelWidget(widget) && !qobject_cast(widget)) { QProxyStyle::drawControl(element, option, painter, widget); @@ -1400,10 +831,7 @@ void ManhattanStyle::drawControl(ControlElement element, const QStyleOption *opt item.palette = pal; } - if (isQmlEditorMenu(widget)) - drawControlForQmlEditor(element, &item, painter, widget); - else - QProxyStyle::drawControl(element, &item, painter, widget); + QProxyStyle::drawControl(element, &item, painter, widget); } painter->restore(); break; @@ -1562,13 +990,6 @@ void ManhattanStyle::drawControl(ControlElement element, const QStyleOption *opt } break; - case CE_MenuEmptyArea: - if (isQmlEditorMenu(widget)) - drawPrimitive(PE_PanelMenu, option, painter, widget); - else - QProxyStyle::drawControl(element, option, painter, widget); - - break; case CE_ToolBar: { QRect rect = option->rect; @@ -1786,170 +1207,6 @@ void ManhattanStyle::drawComplexControl(ComplexControl control, const QStyleOpti painter->restore(); } break; - case CC_Slider: - if (const auto *slider = qstyleoption_cast(option)) { - QRect groove = proxy()->subControlRect(CC_Slider, option, SC_SliderGroove, widget); - QRect handle = proxy()->subControlRect(CC_Slider, option, SC_SliderHandle, widget); - - bool horizontal = slider->orientation == Qt::Horizontal; - bool ticksAbove = slider->tickPosition & QSlider::TicksAbove; - bool ticksBelow = slider->tickPosition & QSlider::TicksBelow; - bool enabled = option->state & QStyle::State_Enabled; - bool grooveHover = slider->activeSubControls & SC_SliderGroove; - bool handleHover = slider->activeSubControls & SC_SliderHandle; - bool interaction = option->state & State_Sunken; - bool activeFocus = option->state & State_HasFocus && option->state & State_KeyboardFocusChange; - - int sliderPaintingOffset = horizontal - ? handle.center().x() - : handle.center().y(); - - int borderRadius = 4; - - painter->save(); - painter->setRenderHint(QPainter::RenderHint::Antialiasing); - - int lineWidth = pixelMetric(QStyle::PM_DefaultFrameWidth, option, widget); - Theme::Color themeframeColor = enabled - ? interaction - ? Theme::DSstateControlBackgroundColor_hover // Pressed - : grooveHover - ? Theme::DSstateSeparatorColor // GrooveHover - : Theme::DSpopupBackground // Idle - : Theme::DSpopupBackground; // Disabled - - QColor frameColor = creatorTheme()->color(themeframeColor); - - if ((option->subControls & SC_SliderGroove) && groove.isValid()) { - Theme::Color bgPlusColor = enabled - ? interaction - ? Theme::DSstateControlBackgroundColor_hover // Pressed - : grooveHover - ? Theme::DSstateSeparatorColor // GrooveHover - : Theme::DStoolbarBackground // Idle - : Theme::DStoolbarBackground; // Disabled - Theme::Color bgMinusColor = Theme::DSpopupBackground; - - QRect minusRect(groove); - QRect plusRect(groove); - - if (horizontal) { - if (slider->upsideDown) { - minusRect.setLeft(sliderPaintingOffset); - plusRect.setRight(sliderPaintingOffset); - } else { - minusRect.setRight(sliderPaintingOffset); - plusRect.setLeft(sliderPaintingOffset); - } - } else { - if (slider->upsideDown) { - minusRect.setBottom(sliderPaintingOffset); - plusRect.setTop(sliderPaintingOffset); - } else { - minusRect.setTop(sliderPaintingOffset); - plusRect.setBottom(sliderPaintingOffset); - } - } - - painter->save(); - painter->setPen(Qt::NoPen); - painter->setBrush(creatorTheme()->color(bgPlusColor)); - painter->drawRoundedRect(plusRect, borderRadius, borderRadius); - painter->setBrush(creatorTheme()->color(bgMinusColor)); - painter->drawRoundedRect(minusRect, borderRadius, borderRadius); - painter->restore(); - } - - if (option->subControls & SC_SliderTickmarks) { - Theme::Color tickPen = enabled - ? activeFocus - ? Theme::DSstateBackgroundColor_hover - : Theme::DSBackgroundColorAlternate - : Theme::DScontrolBackgroundDisabled; - - painter->setPen(tickPen); - int tickSize = proxy()->pixelMetric(PM_SliderTickmarkOffset, option, widget); - int available = proxy()->pixelMetric(PM_SliderSpaceAvailable, slider, widget); - int interval = slider->tickInterval; - if (interval <= 0) { - interval = slider->singleStep; - if (QStyle::sliderPositionFromValue(slider->minimum, slider->maximum, interval, - available) - - QStyle::sliderPositionFromValue(slider->minimum, slider->maximum, - 0, available) < 3) - interval = slider->pageStep; - } - if (interval <= 0) - interval = 1; - - int v = slider->minimum; - int len = proxy()->pixelMetric(PM_SliderLength, slider, widget); - while (v <= slider->maximum + 1) { - if (v == slider->maximum + 1 && interval == 1) - break; - const int v_ = qMin(v, slider->maximum); - int pos = sliderPositionFromValue(slider->minimum, slider->maximum, - v_, (horizontal - ? slider->rect.width() - : slider->rect.height()) - len, - slider->upsideDown) + len / 2; - int extra = 2 - ((v_ == slider->minimum || v_ == slider->maximum) ? 1 : 0); - - if (horizontal) { - if (ticksAbove) { - painter->drawLine(pos, slider->rect.top() + extra, - pos, slider->rect.top() + tickSize); - } - if (ticksBelow) { - painter->drawLine(pos, slider->rect.bottom() - extra, - pos, slider->rect.bottom() - tickSize); - } - } else { - if (ticksAbove) { - painter->drawLine(slider->rect.left() + extra, pos, - slider->rect.left() + tickSize, pos); - } - if (ticksBelow) { - painter->drawLine(slider->rect.right() - extra, pos, - slider->rect.right() - tickSize, pos); - } - } - // in the case where maximum is max int - int nextInterval = v + interval; - if (nextInterval < v) - break; - v = nextInterval; - } - } - - // draw handle - if ((option->subControls & SC_SliderHandle) ) { - Theme::Color handleColor = enabled - ? interaction - ? Theme::DSinteraction // Interaction - : grooveHover || handleHover - ? Theme::DStabActiveText // Hover - : Theme::PalettePlaceholderText // Idle - : Theme::DStoolbarIcon_blocked; // Disabled - - int halfSliderThickness = horizontal - ? handle.width() / 2 - : handle.height() / 2; - painter->setBrush(creatorTheme()->color(handleColor)); - painter->setPen(Qt::NoPen); - painter->drawRoundedRect(handle, - halfSliderThickness, - halfSliderThickness); - } - - if (groove.isValid()) { - painter->setBrush(Qt::NoBrush); - painter->setPen(QPen(frameColor, lineWidth)); - painter->drawRoundedRect(groove, borderRadius, borderRadius); - } - painter->restore(); - } - break; default: QProxyStyle::drawComplexControl(control, option, painter, widget); break; diff --git a/src/plugins/coreplugin/manhattanstyle.h b/src/plugins/coreplugin/manhattanstyle.h index 07079c7d2dd..ae9cd42a1fa 100644 --- a/src/plugins/coreplugin/manhattanstyle.h +++ b/src/plugins/coreplugin/manhattanstyle.h @@ -16,26 +16,78 @@ class CORE_EXPORT ManhattanStyle : public QProxyStyle public: explicit ManhattanStyle(const QString &baseStyleName); - ~ManhattanStyle() override; + virtual ~ManhattanStyle() override; - void drawPrimitive(PrimitiveElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget = nullptr) const override; - void drawControl(ControlElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget = nullptr) const override; - void drawComplexControl(ComplexControl control, const QStyleOptionComplex *option, QPainter *painter, const QWidget *widget = nullptr) const override; + void drawPrimitive( + PrimitiveElement element, + const QStyleOption *option, + QPainter *painter, + const QWidget *widget = nullptr) const override; - QSize sizeFromContents(ContentsType type, const QStyleOption *option, const QSize &size, const QWidget *widget) const override; - QRect subElementRect(SubElement element, const QStyleOption *option, const QWidget *widget) const override; - QRect subControlRect(ComplexControl cc, const QStyleOptionComplex *opt, SubControl sc, const QWidget *widget) const override; + void drawControl( + ControlElement element, + const QStyleOption *option, + QPainter *painter, + const QWidget *widget = nullptr) const override; - SubControl hitTestComplexControl(ComplexControl control, const QStyleOptionComplex *option, const QPoint &pos, const QWidget *widget = nullptr) const override; - QPixmap standardPixmap(StandardPixmap standardPixmap, const QStyleOption *opt, const QWidget *widget = nullptr) const override; - QIcon standardIcon(StandardPixmap standardIcon, const QStyleOption *option = nullptr, const QWidget *widget = nullptr) const override; - int styleHint(StyleHint hint, const QStyleOption *option = nullptr, const QWidget *widget = nullptr, QStyleHintReturn *returnData = nullptr) const override; - QPixmap generatedIconPixmap(QIcon::Mode iconMode, const QPixmap &pixmap, const QStyleOption *opt) const override; + void drawComplexControl( + ComplexControl control, + const QStyleOptionComplex *option, + QPainter *painter, + const QWidget *widget = nullptr) const override; - int pixelMetric(PixelMetric metric, const QStyleOption *option = nullptr, const QWidget *widget = nullptr) const override; + QSize sizeFromContents( + ContentsType type, + const QStyleOption *option, + const QSize &size, + const QWidget *widget) const override; + + QRect subElementRect( + SubElement element, + const QStyleOption *option, + const QWidget *widget) const override; + + QRect subControlRect( + ComplexControl control, + const QStyleOptionComplex *option, + SubControl subControl, + const QWidget *widget) const override; + + int styleHint( + StyleHint hint, + const QStyleOption *option = nullptr, + const QWidget *widget = nullptr, + QStyleHintReturn *returnData = nullptr) const override; + + int pixelMetric( + PixelMetric metric, + const QStyleOption *option = nullptr, + const QWidget *widget = nullptr) const override; QPalette standardPalette() const override; + + QIcon standardIcon( + StandardPixmap standardIcon, + const QStyleOption *option = nullptr, + const QWidget *widget = nullptr) const override; + + SubControl hitTestComplexControl( + ComplexControl control, + const QStyleOptionComplex *option, + const QPoint &pos, + const QWidget *widget = nullptr) const override; + + QPixmap standardPixmap( + StandardPixmap standardPixmap, + const QStyleOption *opt, + const QWidget *widget = nullptr) const override; + + QPixmap generatedIconPixmap( + QIcon::Mode iconMode, + const QPixmap &pixmap, + const QStyleOption *opt) const override; + void polish(QWidget *widget) override; void polish(QPalette &pal) override; void polish(QApplication *app) override; @@ -44,28 +96,16 @@ public: void unpolish(QApplication *app) override; private: - void drawPrimitiveForPanelWidget(PrimitiveElement element, - const QStyleOption *option, - QPainter *painter, - const QWidget *widget) const; + void drawPrimitiveForPanelWidget( + PrimitiveElement element, + const QStyleOption *option, + QPainter *painter, + const QWidget *widget) const; - void drawPrimitiveForQmlEditor(PrimitiveElement element, - const QStyleOption *option, - QPainter *painter, - const QWidget *widget) const; + static void drawButtonSeparator( + QPainter *painter, + const QRect &rect, + bool reverse); - void drawControlForQmlEditor(ControlElement element, - const QStyleOption *option, - QPainter *painter, - const QWidget *widget = nullptr) const; - - void drawQmlEditorIcon(PrimitiveElement element, - const QStyleOption *option, - const char *propertyName, - QPainter *painter, - const QWidget *widget = nullptr) const; - - static void drawButtonSeparator(QPainter *painter, const QRect &rect, bool reverse); - - ManhattanStylePrivate *d; + ManhattanStylePrivate *d = nullptr; }; diff --git a/src/plugins/qmldesigner/components/formeditor/toolbox.cpp b/src/plugins/qmldesigner/components/formeditor/toolbox.cpp index d10e14e7cb7..6d20f3374b0 100644 --- a/src/plugins/qmldesigner/components/formeditor/toolbox.cpp +++ b/src/plugins/qmldesigner/components/formeditor/toolbox.cpp @@ -18,6 +18,8 @@ ToolBox::ToolBox(QWidget *parentWidget) , m_rightToolBar(new QToolBar(QLatin1String("RightSidebar"), this)) { setProperty("panelwidget", false); + setProperty("panelwidget_singlerow", false); + setFixedHeight(Theme::toolbarSize()); m_leftToolBar->setFloatable(true); m_leftToolBar->setMovable(true); @@ -27,8 +29,6 @@ ToolBox::ToolBox(QWidget *parentWidget) horizontalLayout->setContentsMargins(0, 0, 0, 0); horizontalLayout->setSpacing(0); - setFixedHeight(Theme::toolbarSize()); - m_leftToolBar->setProperty("panelwidget", false); m_leftToolBar->setProperty("panelwidget_singlerow", false); m_leftToolBar->setFixedHeight(Theme::toolbarSize()); diff --git a/src/plugins/qmldesigner/designmodewidget.cpp b/src/plugins/qmldesigner/designmodewidget.cpp index 2acdff18230..c20f72a81a1 100644 --- a/src/plugins/qmldesigner/designmodewidget.cpp +++ b/src/plugins/qmldesigner/designmodewidget.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -39,6 +40,7 @@ #include #include +#include #include #include #include @@ -92,6 +94,8 @@ DesignModeWidget::DesignModeWidget() , m_crumbleBar(new CrumbleBar(this)) { setAcceptDrops(true); + if (Utils::StyleHelper::isQDSTheme()) + qApp->setStyle(QmlDesignerBasePlugin::style()); } DesignModeWidget::~DesignModeWidget() diff --git a/src/plugins/qmldesignerbase/CMakeLists.txt b/src/plugins/qmldesignerbase/CMakeLists.txt index f03498f28d9..8f0bccf5710 100644 --- a/src/plugins/qmldesignerbase/CMakeLists.txt +++ b/src/plugins/qmldesignerbase/CMakeLists.txt @@ -13,5 +13,6 @@ extend_qtc_plugin(QmlDesignerBase SOURCES designersettings.cpp designersettings.h qmlpuppetpaths.cpp qmlpuppetpaths.h + studiostyle.cpp studiostyle.h studioquickwidget.cpp studioquickwidget.h ) diff --git a/src/plugins/qmldesignerbase/qmldesignerbaseplugin.cpp b/src/plugins/qmldesignerbase/qmldesignerbaseplugin.cpp index 0dc9734e8fb..db2ff7b7ca9 100644 --- a/src/plugins/qmldesignerbase/qmldesignerbaseplugin.cpp +++ b/src/plugins/qmldesignerbase/qmldesignerbaseplugin.cpp @@ -4,6 +4,9 @@ #include "qmldesignerbaseplugin.h" #include "utils/designersettings.h" +#include "utils/hostosinfo.h" +#include "utils/studiostyle.h" +#include "utils/theme/theme.h" #include #include @@ -12,6 +15,7 @@ #include #include +#include #include #include #include @@ -19,8 +23,11 @@ #include #include #include +#include +#include #include +using Utils::HostOsInfo; namespace QmlDesigner { const char EXAMPLES_DOWNLOAD_PATH[] = "StudioConfig/ExamplesDownloadPath"; @@ -29,7 +36,12 @@ const char BUNDLES_DOWNLOAD_PATH[] = "StudioConfig/BundlesDownloadPath"; class QmlDesignerBasePlugin::Data { public: - DesignerSettings settings{Core::ICore::instance()->settings()}; + DesignerSettings settings; + QScopedPointer style; + + Data() + : settings(Core::ICore::settings()) + {} }; namespace { @@ -46,7 +58,6 @@ QmlDesignerBasePlugin *QmlDesignerBasePlugin::instance() return global; }; - QmlDesignerBasePlugin::~QmlDesignerBasePlugin() = default; DesignerSettings &QmlDesignerBasePlugin::settings() @@ -54,6 +65,14 @@ DesignerSettings &QmlDesignerBasePlugin::settings() return global->d->settings; } +QStyle *QmlDesignerBasePlugin::style() +{ + if (global->d->style.isNull()) + global->d->style.reset(new StudioStyle(qApp->style())); + + return global->d->style.data(); +} + bool QmlDesignerBasePlugin::initialize(const QStringList &, QString *) { d = std::make_unique(); diff --git a/src/plugins/qmldesignerbase/qmldesignerbaseplugin.h b/src/plugins/qmldesignerbase/qmldesignerbaseplugin.h index de9c9cabe37..92e5ffc71bc 100644 --- a/src/plugins/qmldesignerbase/qmldesignerbaseplugin.h +++ b/src/plugins/qmldesignerbase/qmldesignerbaseplugin.h @@ -58,6 +58,7 @@ public: static QString bundlesPathSetting(); static class DesignerSettings &settings(); + static QStyle *style(); signals: void examplesDownloadPathChanged(const QString &path); diff --git a/src/plugins/qmldesignerbase/utils/studiostyle.cpp b/src/plugins/qmldesignerbase/utils/studiostyle.cpp new file mode 100644 index 00000000000..75cf926a0fa --- /dev/null +++ b/src/plugins/qmldesignerbase/utils/studiostyle.cpp @@ -0,0 +1,953 @@ +// Copyright (C) 2023 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 "studiostyle.h" + +#include +#include + +#include +#include +#include + +using namespace Utils; + +namespace { + +inline QColor studioTextColor(bool enabled, + bool active, + bool checked) +{ + Theme::Color themePenColorId = + enabled ? (active + ? (checked ? Theme::DSsubPanelBackground + : Theme::DSpanelBackground) + : Theme::DStextColor) + : Theme::DStextColorDisabled; + + return creatorTheme()->color(themePenColorId); +} + +inline QColor studioButtonBgColor(bool enabled, + bool hovered, + bool pressed, + bool checked) +{ + Theme::Color themePenColorId = + enabled + ? (pressed + ? (checked + ? Theme::DSinteractionHover + : Theme::DScontrolBackgroundGlobalHover) + : (hovered + ? (checked + ? Theme::DSinteractionHover + : Theme::DScontrolBackgroundHover) + : Theme::DScontrolBackground) + ) + : Theme::DScontrolBackgroundDisabled; + + return creatorTheme()->color(themePenColorId); +} + +inline QColor studioButtonOutlineColor(bool enabled, + [[maybe_unused]] bool hovered, + bool pressed, + [[maybe_unused]] bool checked) +{ + + Theme::Color themePenColorId = + enabled + ? (pressed + ? Theme::DScontrolOutlineInteraction + : Theme::DScontrolOutline) + : Theme::DScontrolOutlineDisabled; + + return creatorTheme()->color(themePenColorId); +} + +bool styleEnabled(const QWidget *widget) +{ + const QWidget *p = widget; + while (p) { + if (p->property("_q_custom_style_disabled").toBool()) + return false; + p = p->parentWidget(); + } + return true; +} + +// Consider making this a QStyle state +bool isQmlEditorMenu(const QWidget *widget) +{ + const QMenu *menu = qobject_cast (widget); + if (!menu) + return false; + + const QWidget *p = widget; + while (p) { + if (p->property("qmlEditorMenu").toBool()) + return styleEnabled(widget); + p = p->parentWidget(); + } + + return false; +} + +QPixmap getPixmapFromIcon(const QIcon &icon, const QSize &size, bool enabled, bool active, bool checked) +{ + QIcon::Mode mode = enabled ? ((active) ? QIcon::Active : QIcon::Normal) : QIcon::Disabled; + QIcon::State state = (checked) ? QIcon::On : QIcon::Off; + return icon.pixmap(size, mode, state); +} + +struct StudioShortcut { + StudioShortcut(const QStyleOptionMenuItem *option, + const QString &shortcutText) + : shortcutText(shortcutText) + , enabled(option->state & QStyle::State_Enabled) + , active(option->state & QStyle::State_Selected) + , font(option->font) + , fm(font) + , defaultHeight(fm.height()) + , spaceConst(fm.boundingRect(".").width()) + { + reset(); + + if (backspaceMatch(shortcutText).hasMatch()) + backspaceIcon = option->styleObject->property("backspaceIcon").value(); + } + + QSize getSize() + { + if (isFirstParticle) + calcResult(); + return _size; + } + + QPixmap getPixmap() + { + if (!isFirstParticle && !_pixmap.isNull()) + return _pixmap; + + _pixmap = QPixmap(getSize()); + _pixmap.fill(Qt::transparent); + QPainter painter(&_pixmap); + painter.setFont(font); + QPen pPen = painter.pen(); + pPen.setColor(studioTextColor(enabled, active, false)); + painter.setPen(pPen); + calcResult(&painter); + painter.end(); + + return _pixmap; + } + +private: + void applySize(const QSize &itemSize) { + width += itemSize.width(); + height = std::max(height, itemSize.height()); + if (isFirstParticle) + isFirstParticle = false; + else + width += spaceConst; + }; + + void addText(const QString &txt, QPainter *painter = nullptr) + { + if (txt.size()) { + int textWidth = fm.boundingRect(txt).width(); + QSize itemSize = {textWidth, defaultHeight}; + if (painter) { + static const QTextOption textOption(Qt::AlignLeft | Qt::AlignVCenter); + QRect placeRect({width, 0}, itemSize); + painter->drawText(placeRect, txt, textOption); + } + applySize(itemSize); + } + }; + + void addPixmap(const QPixmap &pixmap, QPainter *painter = nullptr) + { + if (painter) + painter->drawPixmap(QRect({width, 0}, pixmap.size()), pixmap); + + applySize(pixmap.size()); + }; + + void calcResult(QPainter *painter = nullptr) + { + reset(); +#ifndef QT_NO_SHORTCUT + if (!shortcutText.isEmpty()) { + int fwdIndex = 0; + + QRegularExpressionMatch mMatch = backspaceMatch(shortcutText); + int matchCount = mMatch.lastCapturedIndex(); + + for (int i = 0; i <= matchCount; ++i) { + QString mStr = mMatch.captured(i); + QSize iconSize(defaultHeight * 3, defaultHeight); + const QList iconSizes = backspaceIcon.availableSizes(); + if (iconSizes.size()) + iconSize = iconSizes.last(); + double aspectRatio = (defaultHeight + .0) / iconSize.height(); + int newWidth = iconSize.width() * aspectRatio; + + QPixmap pixmap = getPixmapFromIcon(backspaceIcon, + {newWidth, defaultHeight}, + enabled, active, false); + + int lIndex = shortcutText.indexOf(mStr, fwdIndex); + int diffChars = lIndex - fwdIndex; + addText(shortcutText.mid(fwdIndex, diffChars), painter); + addPixmap(pixmap, painter); + fwdIndex = lIndex + mStr.size(); + } + addText(shortcutText.mid(fwdIndex), painter); + } +#endif + _size = {width, height}; + } + + void reset() + { + isFirstParticle = true; + width = 0; + height = 0; + } + + inline QRegularExpressionMatch backspaceMatch(const QString &str) const + { + static const QRegularExpression backspaceDetect( + "\\+*backspace\\+*", + QRegularExpression::CaseInsensitiveOption); + return backspaceDetect.match(str); + } + + const QString shortcutText; + const bool enabled; + const bool active; + const QFont font; + const QFontMetrics fm; + const int defaultHeight; + const int spaceConst; + QIcon backspaceIcon; + bool isFirstParticle = true; + + int width = 0; + int height = 0; + QSize _size; + QPixmap _pixmap; +}; + +} // blank namespace + +class StudioStylePrivate +{ +public: + explicit StudioStylePrivate(); + +public: + QPalette stdPalette; +}; + +StudioStylePrivate::StudioStylePrivate() +{ + auto color = [] (Theme::Color c) { + return creatorTheme()->color(c); + }; + + { + stdPalette.setColorGroup( + QPalette::Disabled, // group + color(Theme::DStextColorDisabled), // windowText + color(Theme::DScontrolBackgroundDisabled), // button + color(Theme::DScontrolOutlineDisabled), // light + color(Theme::DStextSelectedTextColor), // dark + color(Theme::DSstatusbarBackground), // mid + color(Theme::DStextColorDisabled), // text + color(Theme::DStextColorDisabled), // brightText + color(Theme::DStoolbarIcon_blocked), // base + color(Theme::DStoolbarIcon_blocked) // window + ); + + stdPalette.setColorGroup( + QPalette::Inactive, // group + color(Theme::DStextColor), // windowText + color(Theme::DScontrolBackground), // button + color(Theme::DStoolbarBackground), // light + color(Theme::DSstatusbarBackground), // dark + color(Theme::DScontrolBackground), // mid + color(Theme::DStextColor), // text + color(Theme::DStextColor), // brightText + color(Theme::DStoolbarBackground), // base + color(Theme::DStoolbarBackground) // window + ); + + stdPalette.setColorGroup( + QPalette::Active, // group + color(Theme::DStextSelectedTextColor), // windowText + color(Theme::DSnavigatorItemBackgroundHover), // button + color(Theme::DSstateBackgroundColor_hover), // light + color(Theme::DSpanelBackground), // dark + color(Theme::DSnavigatorItemBackgroundHover), // mid + color(Theme::DStextSelectedTextColor), // text + color(Theme::DStextSelectedTextColor), // brightText + color(Theme::DStoolbarBackground), // base + color(Theme::DStoolbarBackground) // window + ); + } +} + +StudioStyle::StudioStyle(QStyle *style) + : QProxyStyle(style) + , d(new StudioStylePrivate) +{ +} + +StudioStyle::StudioStyle(const QString &key) + : QProxyStyle(key) + , d(new StudioStylePrivate) +{ +} + +StudioStyle::~StudioStyle() +{ + delete d; +} + +void StudioStyle::drawPrimitive( + PrimitiveElement element, + const QStyleOption *option, + QPainter *painter, + const QWidget *widget) const +{ + switch (element) { + case PE_IndicatorArrowUp: + case PE_IndicatorArrowDown: + if (const auto mbi = qstyleoption_cast(option)) { + QStyleOptionMenuItem item = *mbi; + item.palette = QPalette(Qt::white); + StyleHelper::drawMinimalArrow(element, painter, &item); + } else { + Super::drawPrimitive(element, option, painter, widget); + } + break; + + case PE_IndicatorArrowRight: + drawQmlEditorIcon(element, option, "cascadeIconRight", painter, widget); + break; + + case PE_IndicatorArrowLeft: + drawQmlEditorIcon(element, option, "cascadeIconLeft", painter, widget); + break; + + case PE_IndicatorMenuCheckMark: + drawQmlEditorIcon(element, option, "tickIcon", painter, widget); + break; + + case PE_FrameMenu: + case PE_PanelMenu: + if (isQmlEditorMenu(widget)) + painter->fillRect(option->rect, creatorTheme()->color(Theme::DSsubPanelBackground)); + else + Super::drawPrimitive(element, option, painter, widget); + break; + + case PE_PanelButtonCommand: + if (!isQmlEditorMenu(widget)) + Super::drawPrimitive(element, option, painter, widget); + break; + case PE_FrameDefaultButton: + { + if (const auto button = qstyleoption_cast(option)) { + bool enabled = button->state & QStyle::State_Enabled; + bool hovered = enabled && button->state & QStyle::State_MouseOver; + bool checked = button->state & QStyle::State_On; + bool pressed = enabled && button->state & QStyle::State_Sunken; + QColor btnColor = studioButtonBgColor(enabled, hovered, pressed, checked); + QColor penColor = studioButtonOutlineColor(enabled, hovered, pressed, checked); + penColor.setAlpha(50); + + painter->save(); + painter->setPen(QPen(penColor, 1)); + if (pressed) { + painter->setBrush(Qt::NoBrush); + painter->drawRoundedRect(option->rect.adjusted(0, 0, -1, -1), 5, 5); + painter->setPen(Qt::NoPen); + painter->setBrush(btnColor); + painter->drawRoundedRect(option->rect.adjusted(1, 1, 0, 0), 5, 5); + } else { + painter->setPen(Qt::NoPen); + painter->setBrush(btnColor); + painter->drawRoundedRect(option->rect, 5, 5); + } + + painter->restore(); + } + } + break; + + case PE_IndicatorToolBarSeparator: + { + bool horizontal = option->state & State_Horizontal; + int thickness = pixelMetric(PM_ToolBarSeparatorExtent, option, widget); + QRect colorRect; + if (horizontal) { + colorRect = {option->rect.center().x() - thickness / 2 , option->rect.top() + 2, + thickness , option->rect.height() - 4}; + } else { + colorRect = {option->rect.left() + 2, option->rect.center().y() - thickness / 2, + option->rect.width() - 4, thickness}; + } + + // The separator color is currently the same as toolbar bg + painter->fillRect(colorRect, + creatorTheme()->color(Theme::DStoolbarBackground)); + } + break; + + default: + { + Super::drawPrimitive(element, option, painter, widget); + break; + } + } +} + +void StudioStyle::drawControl( + ControlElement element, + const QStyleOption *option, + QPainter *painter, + const QWidget *widget) const +{ + switch (element) { + case CE_MenuItem: + if (const auto mbi = qstyleoption_cast(option)) { + painter->save(); + const int iconHeight = pixelMetric(QStyle::PM_SmallIconSize, option, widget); + const int horizontalSpacing = pixelMetric(QStyle::PM_LayoutHorizontalSpacing, option, widget); + const int iconWidth = iconHeight; + const bool isActive = mbi->state & State_Selected; + const bool isDisabled = !(mbi->state & State_Enabled); + const bool isCheckable = mbi->checkType != QStyleOptionMenuItem::NotCheckable; + const bool isChecked = isCheckable ? mbi->checked : false; + int startMargin = pixelMetric(QStyle::PM_LayoutLeftMargin, option, widget); + int endMargin = pixelMetric(QStyle::PM_LayoutRightMargin, option, widget); + int forwardX = 0; + + if (option->direction == Qt::RightToLeft) + std::swap(startMargin, endMargin); + + QStyleOptionMenuItem item = *mbi; + + if (isActive) { + painter->fillRect(item.rect, creatorTheme()->color(Theme::DSinteraction)); + } + forwardX += startMargin; + + if (item.menuItemType == QStyleOptionMenuItem::Separator) { + int commonHeight = item.rect.center().y(); + int additionalMargin = forwardX /*hmargin*/; + QLineF separatorLine (item.rect.left() + additionalMargin, + commonHeight, + item.rect.right() - additionalMargin, + commonHeight); + + painter->setPen(creatorTheme()->color(Theme::DSstateSeparatorColor)); + painter->drawLine(separatorLine); + item.text.clear(); + painter->restore(); + return; + } + + QPixmap iconPixmap; + QIcon::Mode mode = isDisabled ? QIcon::Disabled : ((isActive) ? QIcon::Active : QIcon::Normal); + QIcon::State state = isChecked ? QIcon::On : QIcon::Off; + QColor themePenColor = studioTextColor(!isDisabled, isActive, isChecked); + + if (!item.icon.isNull()) { + iconPixmap = item.icon.pixmap(QSize(iconHeight, iconHeight), mode, state); + } else if (isCheckable) { + iconPixmap = QPixmap(iconHeight, iconHeight); + iconPixmap.fill(Qt::transparent); + + if (item.checked) { + QStyleOptionMenuItem so = item; + so.rect = iconPixmap.rect(); + QPainter dPainter(&iconPixmap); + dPainter.setPen(themePenColor); + drawPrimitive(PE_IndicatorMenuCheckMark, &so, &dPainter, widget); + } + } + + if (!iconPixmap.isNull()) { + QRect vCheckRect = visualRect(item.direction, + item.rect, + QRect(item.rect.x() + forwardX, + item.rect.y(), + iconWidth, + item.rect.height())); + + QRect pmr(QPoint(0, 0), iconPixmap.deviceIndependentSize().toSize()); + pmr.moveCenter(vCheckRect.center()); + painter->setPen(themePenColor); + painter->drawPixmap(pmr.topLeft(), iconPixmap); + + item.checkType = QStyleOptionMenuItem::NotCheckable; + item.checked = false; + item.icon = {}; + } + if (item.menuHasCheckableItems || item.maxIconWidth > 0) { + forwardX += iconWidth + horizontalSpacing; + } + + QString shortcutText; + int tabIndex = item.text.indexOf("\t"); + if (tabIndex > -1) { + shortcutText = item.text.mid(tabIndex + 1); + item.text = item.text.left(tabIndex); + } + + if (item.text.size()) { + painter->save(); + + QRect vTextRect = visualRect(item.direction, + item.rect, + item.rect.adjusted(forwardX, 0 , 0 , 0)); + + Qt::Alignment alignmentFlags = item.direction == Qt::LeftToRight ? Qt::AlignLeft + : Qt::AlignRight; + alignmentFlags |= Qt::AlignVCenter; + + int textFlags = Qt::TextShowMnemonic | Qt::TextDontClip | Qt::TextSingleLine; + if (!proxy()->styleHint(SH_UnderlineShortcut, &item, widget)) + textFlags |= Qt::TextHideMnemonic; + textFlags |= alignmentFlags; + + painter->setPen(themePenColor); + painter->drawText(vTextRect, textFlags, item.text); + painter->restore(); + } + + if (item.menuItemType == QStyleOptionMenuItem::SubMenu) { + PrimitiveElement dropDirElement = item.direction == Qt::LeftToRight + ? PE_IndicatorArrowRight + : PE_IndicatorArrowLeft; + + QSize elSize(iconHeight, iconHeight); + int xOffset = iconHeight + endMargin; + int yOffset = (item.rect.height() - iconHeight) / 2; + QRect dropRect(item.rect.topRight(), elSize); + dropRect.adjust(-xOffset, yOffset, -xOffset, yOffset); + + QStyleOptionMenuItem so = item; + so.rect = visualRect(item.direction, + item.rect, + dropRect); + + drawPrimitive(dropDirElement, &so, painter, widget); + } else if (!shortcutText.isEmpty()) { + QPixmap pix = StudioShortcut(&item, shortcutText).getPixmap(); + + if (pix.width()) { + int xOffset = pix.width() + (iconHeight / 2) + endMargin; + QRect shortcutRect = item.rect.translated({item.rect.width() - xOffset, 0}); + shortcutRect.setSize({pix.width(), item.rect.height()}); + shortcutRect = visualRect(item.direction, + item.rect, + shortcutRect); + drawItemPixmap(painter, + shortcutRect, + Qt::AlignRight | Qt::AlignVCenter, + pix); + } + } + painter->restore(); + } + break; + + case CE_MenuEmptyArea: + if (isQmlEditorMenu(widget)) + drawPrimitive(PE_PanelMenu, option, painter, widget); + else + Super::drawControl(element, option, painter, widget); + break; + + default: + Super::drawControl(element, option, painter, widget); + break; + } +} + +void StudioStyle::drawComplexControl( + ComplexControl control, + const QStyleOptionComplex *option, + QPainter *painter, + const QWidget *widget) const +{ + switch (control) { + + case CC_Slider: + if (const auto *slider = qstyleoption_cast(option)) { + QRect groove = proxy()->subControlRect(CC_Slider, option, SC_SliderGroove, widget); + QRect handle = proxy()->subControlRect(CC_Slider, option, SC_SliderHandle, widget); + + bool horizontal = slider->orientation == Qt::Horizontal; + bool ticksAbove = slider->tickPosition & QSlider::TicksAbove; + bool ticksBelow = slider->tickPosition & QSlider::TicksBelow; + bool enabled = option->state & QStyle::State_Enabled; + bool grooveHover = slider->activeSubControls & SC_SliderGroove; + bool handleHover = slider->activeSubControls & SC_SliderHandle; + bool interaction = option->state & State_Sunken; + bool activeFocus = option->state & State_HasFocus && option->state & State_KeyboardFocusChange; + + int sliderPaintingOffset = horizontal + ? handle.center().x() + : handle.center().y(); + + int borderRadius = 4; + + painter->save(); + painter->setRenderHint(QPainter::RenderHint::Antialiasing); + + int lineWidth = pixelMetric(QStyle::PM_DefaultFrameWidth, option, widget); + Theme::Color themeframeColor = enabled + ? interaction + ? Theme::DSstateControlBackgroundColor_hover // Pressed + : grooveHover + ? Theme::DSstateSeparatorColor // GrooveHover + : Theme::DSpopupBackground // Idle + : Theme::DSpopupBackground; // Disabled + + QColor frameColor = creatorTheme()->color(themeframeColor); + + if ((option->subControls & SC_SliderGroove) && groove.isValid()) { + Theme::Color bgPlusColor = enabled + ? interaction + ? Theme::DSstateControlBackgroundColor_hover // Pressed + : grooveHover + ? Theme::DSstateSeparatorColor // GrooveHover + : Theme::DStoolbarBackground // Idle + : Theme::DStoolbarBackground; // Disabled + Theme::Color bgMinusColor = Theme::DSpopupBackground; + + QRect minusRect(groove); + QRect plusRect(groove); + + if (horizontal) { + if (slider->upsideDown) { + minusRect.setLeft(sliderPaintingOffset); + plusRect.setRight(sliderPaintingOffset); + } else { + minusRect.setRight(sliderPaintingOffset); + plusRect.setLeft(sliderPaintingOffset); + } + } else { + if (slider->upsideDown) { + minusRect.setBottom(sliderPaintingOffset); + plusRect.setTop(sliderPaintingOffset); + } else { + minusRect.setTop(sliderPaintingOffset); + plusRect.setBottom(sliderPaintingOffset); + } + } + + painter->save(); + painter->setPen(Qt::NoPen); + painter->setBrush(creatorTheme()->color(bgPlusColor)); + painter->drawRoundedRect(plusRect, borderRadius, borderRadius); + painter->setBrush(creatorTheme()->color(bgMinusColor)); + painter->drawRoundedRect(minusRect, borderRadius, borderRadius); + painter->restore(); + } + + if (option->subControls & SC_SliderTickmarks) { + Theme::Color tickPen = enabled + ? activeFocus + ? Theme::DSstateBackgroundColor_hover + : Theme::DSBackgroundColorAlternate + : Theme::DScontrolBackgroundDisabled; + + painter->setPen(tickPen); + int tickSize = proxy()->pixelMetric(PM_SliderTickmarkOffset, option, widget); + int available = proxy()->pixelMetric(PM_SliderSpaceAvailable, slider, widget); + int interval = slider->tickInterval; + if (interval <= 0) { + interval = slider->singleStep; + if (QStyle::sliderPositionFromValue(slider->minimum, slider->maximum, interval, + available) + - QStyle::sliderPositionFromValue(slider->minimum, slider->maximum, + 0, available) < 3) + interval = slider->pageStep; + } + if (interval <= 0) + interval = 1; + + int v = slider->minimum; + int len = proxy()->pixelMetric(PM_SliderLength, slider, widget); + while (v <= slider->maximum + 1) { + if (v == slider->maximum + 1 && interval == 1) + break; + const int v_ = qMin(v, slider->maximum); + int pos = sliderPositionFromValue(slider->minimum, slider->maximum, + v_, (horizontal + ? slider->rect.width() + : slider->rect.height()) - len, + slider->upsideDown) + len / 2; + int extra = 2 - ((v_ == slider->minimum || v_ == slider->maximum) ? 1 : 0); + + if (horizontal) { + if (ticksAbove) { + painter->drawLine(pos, slider->rect.top() + extra, + pos, slider->rect.top() + tickSize); + } + if (ticksBelow) { + painter->drawLine(pos, slider->rect.bottom() - extra, + pos, slider->rect.bottom() - tickSize); + } + } else { + if (ticksAbove) { + painter->drawLine(slider->rect.left() + extra, pos, + slider->rect.left() + tickSize, pos); + } + if (ticksBelow) { + painter->drawLine(slider->rect.right() - extra, pos, + slider->rect.right() - tickSize, pos); + } + } + // in the case where maximum is max int + int nextInterval = v + interval; + if (nextInterval < v) + break; + v = nextInterval; + } + } + + // draw handle + if ((option->subControls & SC_SliderHandle) ) { + Theme::Color handleColor = enabled + ? interaction + ? Theme::DSinteraction // Interaction + : grooveHover || handleHover + ? Theme::DStabActiveText // Hover + : Theme::PalettePlaceholderText // Idle + : Theme::DStoolbarIcon_blocked; // Disabled + + int halfSliderThickness = horizontal + ? handle.width() / 2 + : handle.height() / 2; + painter->setBrush(creatorTheme()->color(handleColor)); + painter->setPen(Qt::NoPen); + painter->drawRoundedRect(handle, + halfSliderThickness, + halfSliderThickness); + } + + if (groove.isValid()) { + painter->setBrush(Qt::NoBrush); + painter->setPen(QPen(frameColor, lineWidth)); + painter->drawRoundedRect(groove, borderRadius, borderRadius); + } + painter->restore(); + } + break; + + default: + Super::drawComplexControl(control, option, painter, widget); + break; + } +} + +QSize StudioStyle::sizeFromContents( + ContentsType type, + const QStyleOption *option, + const QSize &size, + const QWidget *widget) const +{ + QSize newSize; + + switch (type) { + case CT_MenuItem: + if (const auto mbi = qstyleoption_cast(option)) { + const int leftMargin = pixelMetric( + QStyle::PM_LayoutLeftMargin, + option, widget); + const int rightMargin = pixelMetric( + QStyle::PM_LayoutRightMargin, + option, widget); + const int horizontalSpacing = pixelMetric( + QStyle::PM_LayoutHorizontalSpacing, + option, widget); + const int iconHeight = pixelMetric( + QStyle::PM_SmallIconSize, option, widget) + horizontalSpacing; + int width = leftMargin + rightMargin; + if (mbi->menuHasCheckableItems || mbi->maxIconWidth) + width += iconHeight + horizontalSpacing; + + if (!mbi->text.isEmpty()) { + QString itemText = mbi->text; + QString shortcutText; + int tabIndex = itemText.indexOf("\t"); + if (tabIndex > -1) { + shortcutText = itemText.mid(tabIndex + 1); + itemText = itemText.left(tabIndex); + } + + if (itemText.size()) + width += option->fontMetrics.boundingRect(itemText).width() + horizontalSpacing; + + if (shortcutText.size()) { + QSize shortcutSize = StudioShortcut(mbi, shortcutText).getSize(); + width += shortcutSize.width() + 2 * horizontalSpacing; + } + } + + if (mbi->menuItemType == QStyleOptionMenuItem::SubMenu) + width += iconHeight + horizontalSpacing; + + newSize.setWidth(width); + + switch (mbi->menuItemType) { + case QStyleOptionMenuItem::Normal: + case QStyleOptionMenuItem::DefaultItem: + case QStyleOptionMenuItem::SubMenu: + newSize.setHeight(19); + break; + default: + newSize.setHeight(9); + break; + } + } + break; + + default: + newSize = Super::sizeFromContents(type, option, size, widget); + break; + } + + return newSize; +} + +QRect StudioStyle::subControlRect( + ComplexControl control, + const QStyleOptionComplex *option, + SubControl subControl, + const QWidget *widget) const +{ +#if QT_VERSION < QT_VERSION_CHECK(6, 2, 5) + // Workaround for QTBUG-101581, can be removed when building with Qt 6.2.5 or higher + if (control == CC_ScrollBar) { + const auto scrollbar = qstyleoption_cast(option); + if (scrollbar && qint64(scrollbar->maximum) - scrollbar->minimum > INT_MAX) + return QRect(); // breaks the scrollbar, but avoids the crash + } +#endif + + if (const QStyleOptionSlider *slider = qstyleoption_cast(option)) { + switch (subControl) { + case SubControl::SC_SliderGroove: + return option->rect; + case SubControl::SC_SliderHandle: + { + QRect retval = Super::subControlRect(control, option, subControl, widget); + int thickness = 2; + QPoint center = retval.center(); + const QRect &rect = slider->rect; + if (slider->orientation == Qt::Horizontal) + return {center.x() - thickness, rect.top(), (thickness * 2) + 1, rect.height()}; + else + return {rect.left(), center.y() - thickness, rect.width(), (thickness * 2) + 1}; + } + break; + default: + break; + } + } + return Super::subControlRect(control, option, subControl, widget); +} + +int StudioStyle::pixelMetric( + PixelMetric metric, + const QStyleOption *option, + const QWidget *widget) const +{ + switch (metric) { + case PM_SmallIconSize: + case PM_LayoutLeftMargin: + case PM_LayoutRightMargin: + case PM_LayoutHorizontalSpacing: + case PM_MenuHMargin: + case PM_SubMenuOverlap: + case PM_MenuPanelWidth: + case PM_MenuBarHMargin: + case PM_MenuBarVMargin: + case PM_ToolBarSeparatorExtent: + case PM_ToolBarFrameWidth: + if (isQmlEditorMenu(widget)) { + switch (metric) { + case PM_SmallIconSize: + return 10; + case PM_LayoutLeftMargin: + case PM_LayoutRightMargin: + return 7; + case PM_LayoutHorizontalSpacing: + return 12; + case PM_MenuHMargin: + return 5; + case PM_SubMenuOverlap: + return 0; + case PM_MenuPanelWidth: + case PM_MenuBarHMargin: + case PM_MenuBarVMargin: + case PM_ToolBarSeparatorExtent: + case PM_ToolBarFrameWidth: + return 1; + default: + return 0; + } + } + break; + case PM_ButtonShiftVertical: + case PM_ButtonShiftHorizontal: + case PM_MenuBarPanelWidth: + case PM_ToolBarItemMargin: + return 0; + case PM_ToolBarItemSpacing: + return 4; + case PM_ToolBarExtensionExtent: + return 29; + default: + break; + } + return Super::pixelMetric(metric, option, widget); +} + +QPalette StudioStyle::standardPalette() const +{ + return d->stdPalette; +} + +void StudioStyle::drawQmlEditorIcon( + PrimitiveElement element, + const QStyleOption *option, + const char *propertyName, + QPainter *painter, + const QWidget *widget) const +{ + if (option->styleObject && option->styleObject->property(propertyName).isValid()) { + const auto mbi = qstyleoption_cast(option); + if (mbi) { + const bool isCheckable = mbi->checkType != QStyleOptionMenuItem::NotCheckable; + const bool isEnabled = mbi->state & State_Enabled; + const bool isActive = mbi->state & State_Selected; + const bool isChecked = isCheckable && mbi->checked; + QIcon icon = mbi->styleObject->property(propertyName).value(); + QPixmap pix = getPixmapFromIcon(icon, mbi->rect.size(), + isEnabled, isActive, isChecked); + drawItemPixmap(painter, mbi->rect, Qt::AlignCenter, pix); + return; + } + } + Super::drawPrimitive(element, option, painter, widget); +} diff --git a/src/plugins/qmldesignerbase/utils/studiostyle.h b/src/plugins/qmldesignerbase/utils/studiostyle.h new file mode 100644 index 00000000000..a0eae302f54 --- /dev/null +++ b/src/plugins/qmldesignerbase/utils/studiostyle.h @@ -0,0 +1,70 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0 + +#pragma once + +#include "qmldesignerbase_global.h" + +#include + +class StudioStylePrivate; + +class QMLDESIGNERBASE_EXPORT StudioStyle : public QProxyStyle +{ + Q_OBJECT + +public: + using Super = QProxyStyle; + StudioStyle(QStyle *style = nullptr); + StudioStyle(const QString &key); + virtual ~StudioStyle() override; + + // Drawing Methods + void drawPrimitive( + PrimitiveElement element, + const QStyleOption *option, + QPainter *painter, + const QWidget *widget = nullptr) const override; + + void drawControl( + ControlElement element, + const QStyleOption *option, + QPainter *painter, + const QWidget *widget = nullptr) const override; + + void drawComplexControl( + ComplexControl control, + const QStyleOptionComplex *option, + QPainter *painter, + const QWidget *widget = nullptr) const override; + + // Topology + QSize sizeFromContents( + ContentsType type, + const QStyleOption *option, + const QSize &size, + const QWidget *widget) const override; + + QRect subControlRect( + ComplexControl control, + const QStyleOptionComplex *option, + SubControl subControl, + const QWidget *widget) const override; + + int pixelMetric( + PixelMetric metric, + const QStyleOption *option = nullptr, + const QWidget *widget = nullptr) const override; + + QPalette standardPalette() const override; + +private: + void drawQmlEditorIcon( + PrimitiveElement element, + const QStyleOption *option, + const char *propertyName, + QPainter *painter, + const QWidget *widget = nullptr) const; + + StudioStylePrivate *d = nullptr; +}; From ae6c6868364fe895e2cec7d1d8ceb790530613c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esa=20T=C3=B6rm=C3=A4nen?= Date: Mon, 3 Apr 2023 15:03:32 +0300 Subject: [PATCH 016/192] Doc: Add Qt for MCUs macro Added a new Qt for MCUs macro and modified a number of related topics accordingly. Task-number: QDS-9609 Change-Id: I6506a9cb6671c29d5b164d53cb01c827639ecb16 Reviewed-by: Mats Honkamaa --- doc/config/macros.qdocconf | 1 + .../external-resources.qdoc | 6 ++-- .../creator-embedded-platforms.qdoc | 2 +- doc/qtcreator/src/mcu/creator-mcu-dev.qdoc | 36 +++++++++---------- .../creator-projects-creating.qdoc | 4 +-- .../examples/doc/washingMachineUI.qdoc | 16 ++++----- .../components/qtquick-mcu-support.qdocinc | 6 ++-- .../src/qtdesignstudio-projects.qdoc | 4 +-- 8 files changed, 38 insertions(+), 37 deletions(-) diff --git a/doc/config/macros.qdocconf b/doc/config/macros.qdocconf index 97e6fa7593d..99d1730cfe9 100644 --- a/doc/config/macros.qdocconf +++ b/doc/config/macros.qdocconf @@ -29,6 +29,7 @@ macro.QD = "Qt Designer" macro.QDS = "Qt Design Studio" macro.QDV = "Qt Design Viewer" macro.QL = "Qt Linguist" +macro.QMCU = "Qt for MCUs" macro.QMLD = "Qt Quick Designer" macro.QQV = "Qt QML Viewer" macro.QSDK = "Qt" diff --git a/doc/qtcreator/src/external-resources/external-resources.qdoc b/doc/qtcreator/src/external-resources/external-resources.qdoc index 7576aaf05fe..5d21be4257c 100644 --- a/doc/qtcreator/src/external-resources/external-resources.qdoc +++ b/doc/qtcreator/src/external-resources/external-resources.qdoc @@ -3,15 +3,15 @@ /*! \externalpage https://doc.qt.io/QtForMCUs/index.html - \title Qt for MCUs + \title \QMCU */ /*! \externalpage https://doc.qt.io/QtForMCUs/qtul-qmltypes.html - \title Qt for MCUs - All QML Types + \title \QMCU - All QML Types */ /*! \externalpage https://doc.qt.io/QtForMCUs/qtul-supported-platforms.html - \title Qt for MCUs - Supported Target Platforms + \title \QMCU - Supported Target Platforms */ /*! \externalpage https://doc.qt.io/QtForMCUs/qtul-getting-started-renesas.html diff --git a/doc/qtcreator/src/linux-mobile/creator-embedded-platforms.qdoc b/doc/qtcreator/src/linux-mobile/creator-embedded-platforms.qdoc index c441981d3b8..8f7a7378392 100644 --- a/doc/qtcreator/src/linux-mobile/creator-embedded-platforms.qdoc +++ b/doc/qtcreator/src/linux-mobile/creator-embedded-platforms.qdoc @@ -90,7 +90,7 @@ \list \li \l{Connecting MCUs} \li \l{Running Applications on MCUs} - \li \l{https://doc.qt.io/QtForMCUs/index.html}{Qt for MCUs} + \li \l{https://doc.qt.io/QtForMCUs/index.html}{\QMCU} \endlist \section1 QNX diff --git a/doc/qtcreator/src/mcu/creator-mcu-dev.qdoc b/doc/qtcreator/src/mcu/creator-mcu-dev.qdoc index 2719aa3e4c8..012a5d5e2eb 100644 --- a/doc/qtcreator/src/mcu/creator-mcu-dev.qdoc +++ b/doc/qtcreator/src/mcu/creator-mcu-dev.qdoc @@ -8,7 +8,7 @@ \title Connecting MCUs - Qt for MCU enables you to use subsets of QML and Qt Quick Controls + \QMCU enables you to use subsets of QML and Qt Quick Controls to create user interfaces for devices that are powered by microcontroller units (MCU). It includes a new graphics rendering engine that has a low memory footprint and is optimized for MCUs and other resource-constrained @@ -21,18 +21,18 @@ debug them using \QC. The toolchains are available for cross-compilation on Microsoft Windows, - Linux, and macOS. However, the Qt for MCU SDK is currently only available + Linux, and macOS. However, the Qt for \QMCU SDK is currently only available for Windows and Linux. - For a list of Qt for MCU reference implementations, see the - \l{Qt for MCUs - Supported Target Platforms}{Qt for MCUs} documentation. + For a list of \QMCU reference implementations, see the + \l{\QMCU - Supported Target Platforms}{\QMCU} documentation. \section1 Requirements To use \QC to develop QML applications for MCUs, you need the following: \list - \li Qt for MCU SDK (only available for Windows and Linux) + \li \QMCU SDK (only available for Windows and Linux) \li \l{https://developer.arm.com/tools-and-software/open-source-software/developer-tools/gnu-toolchain/gnu-rm} {GNU ARM Embedded Toolchain} \endlist @@ -55,7 +55,7 @@ To be able to develop applications for MCUs, you need the MCU plugin. This plugin is enabled automatically by the Qt Installer when you - install Qt for MCUs. + install \QMCU. \section2 Specifying MCU Settings @@ -66,9 +66,9 @@ \image qtcreator-mcu-options.png "MCU preferences" \list 1 - \li In the \uicontrol {Qt for MCUs SDK} field, specify the path - to the directory where you installed Qt for MCUs SDK. - \li In the \uicontrol {Targets supported by the Qt for MCUs SDK} + \li In the \uicontrol {\QMCU SDK} field, specify the path + to the directory where you installed \QMCU SDK. + \li In the \uicontrol {Targets supported by the \QMCU SDK} field, select your MCU board. \li In the \uicontrol Requirements section, ensure that the platform-specific requirements are met. This varies depending @@ -105,7 +105,7 @@ \li Select \uicontrol Apply to save the settings. \endlist - \note When updating to other versions of the Qt for MCUs SDK, \QC will + \note When updating to other versions of the \QMCU SDK, \QC will ask you if you want to replace the existing kits, or create new ones alongside. This can also be done manually, for each individual target, via \uicontrol {Update Kit} and \uicontrol {Create Kit}, respectively. @@ -145,7 +145,7 @@ \uicontrol Preferences > \uicontrol Kits. However, for adding new kits you should use the \uicontrol {Create Kit} - button in the {Qt for MCUs} settings tab. This method adds the paths to + button in the {\QMCU} settings tab. This method adds the paths to the kit's toolkits and SDKs, and keeps them synchronized when selecting \uicontrol Apply or \uicontrol OK. @@ -156,14 +156,14 @@ You can use a wizard to set up a project for developing an application that you can run on MCUs. The project uses a subset of QML and Qt Quick Controls - that are supported by Qt for MCU. For more information about developing - applications for MCUs, see the Qt for MCU documentation. + that are supported by \QMCU. For more information about developing + applications for MCUs, see the \QMCU documentation. To create an application and run it on a MCU board: \list 1 \li Select \uicontrol File > \uicontrol {New Project} > - \uicontrol {Application (Qt for MCU)} > + \uicontrol {Application (\QMCU)} > \uicontrol {MCU Support Application} > \uicontrol Choose. \li Follow the instructions of the wizard to create the project. \li Select \uicontrol Projects > \uicontrol {Build & Run}, and then @@ -173,17 +173,17 @@ Usually, you can use the default settings. \endlist - \section1 Supported Qt for MCUs SDKs + \section1 Supported \QMCU SDKs - \note The Qt for MCUs SDK 2.3 requires \QC 9.0.0, or later. + \note The \QMCU SDK 2.3 requires \QC 9.0.0, or later. The following table lists the \QC versions you can use to develop - applications with particular Qt for MCUs SDK versions. + applications with particular \QMCU SDK versions. \table \header \li \QC version - \li Qt for MCUs SDK version + \li \QMCU SDK version \row \li 9.0.0 or later \li 2.0 or later diff --git a/doc/qtcreator/src/projects/creator-only/creator-projects-creating.qdoc b/doc/qtcreator/src/projects/creator-only/creator-projects-creating.qdoc index 141e6eab2ad..8dd448e410b 100644 --- a/doc/qtcreator/src/projects/creator-only/creator-projects-creating.qdoc +++ b/doc/qtcreator/src/projects/creator-only/creator-projects-creating.qdoc @@ -79,10 +79,10 @@ \li Wizard Template \li Purpose \row - \li Application (Qt for MCU) + \li Application (\QMCU) \li MCU Support Application \li Creates an application that uses a subset of Qt QML and - Qt Quick Controls types (as supported by Qt for MCUs) that + Qt Quick Controls types (as supported by \QMCU) that you can deploy, run, and debug on MCU boards. For more information, see \l {Connecting MCUs}. \row diff --git a/doc/qtdesignstudio/examples/doc/washingMachineUI.qdoc b/doc/qtdesignstudio/examples/doc/washingMachineUI.qdoc index 45ab960f7d0..a003d922d47 100644 --- a/doc/qtdesignstudio/examples/doc/washingMachineUI.qdoc +++ b/doc/qtdesignstudio/examples/doc/washingMachineUI.qdoc @@ -33,15 +33,15 @@ In addition, all screens contain a small clock component that displays the current time. We implement a \e TimeDate JavaScript object to support this feature on \l{https://doc.qt.io/QtForMCUs/qtul-qmltypes.html} - {Qt for MCUs}, which does not support the \l Date component at the time of + {\QMCU}, which does not support the \l Date component at the time of writing. \section1 Creating an Application for MCUs - We use the \uicontrol {Qt for MCUs Application} project template to create + We use the \uicontrol {\QMCU Application} project template to create an application for MCUs, which support only a subset of the preset \l{glossary-component}{components}. We select \uicontrol File > - \uicontrol {New Project} > \uicontrol {Qt for MCUs Application} > + \uicontrol {New Project} > \uicontrol {\QMCU Application} > \uicontrol Choose, and follow the instructions of the wizard to create our project. @@ -54,16 +54,16 @@ that we can use to configure and build our example application for running it on MCUs. - \note This example has been tested to run using Qt for MCUs versions since + \note This example has been tested to run using \QMCU versions since 1.6. You cannot run it on older versions. Also, at the time of writing, - Qt for MCUs only supports Qt 5. + \QMCU only supports Qt 5. \section1 Creating Screens For this example, we used an external tool to design the UI and then exported and imported our design into \QDS as assets and components using \QB, as instructed in \l{Exporting from Design Tools}. While - exporting, we only picked components supported by Qt for MCUs to use + exporting, we only picked components supported by \QMCU to use for our components. For the button components, we mostly use the \l {basic-image}{Image}, \l Text, and \l {Mouse Area} components. For the screen background, we use the \l {basic-rectangle}{Rectangle} component. @@ -171,7 +171,7 @@ \image washingmachineui-states.png "States view" - In Qt for MCU, states work differently from Qt Quick, and therefore we + In \QMCU, states work differently from Qt Quick, and therefore we sometimes use \c when conditions to determine the state to apply, and sometimes switch states using signals and JavaScript expressions. @@ -217,7 +217,7 @@ \section1 Showing the Current Time - The \l Date component is not supported on Qt for MCUs, and the + The \l Date component is not supported on \QMCU, and the \l{https://doc.qt.io/QtForMCUs/qtul-javascript-environment.html} {implementation of the JavaScript \c Date()} object returns elapsed time since when the application was started instead of the current diff --git a/doc/qtdesignstudio/src/components/qtquick-mcu-support.qdocinc b/doc/qtdesignstudio/src/components/qtquick-mcu-support.qdocinc index e01af788b80..edd75e3a467 100644 --- a/doc/qtdesignstudio/src/components/qtquick-mcu-support.qdocinc +++ b/doc/qtdesignstudio/src/components/qtquick-mcu-support.qdocinc @@ -6,9 +6,9 @@ \section1 Creating UIs for MCUs - \l{Qt for MCUs} enables you to use subsets of components to create UIs for + \l{\QMCU} enables you to use subsets of components to create UIs for devices that are powered by microcontroller units (MCU). The subset of - supported components depends on the Qt for MCUs version that you use for + supported components depends on the \QMCU version that you use for development. In this manual, we indicate which components are supported at the time of writing. @@ -21,7 +21,7 @@ \image qmldesigner-mcu-support.png "Components and Text properties supported for MCUs" For more information about the supported components and their properties, - see \l{Qt for MCUs - All QML Types}. + see \l{\QMCU - All QML Types}. //! [mcu qtquick components] */ diff --git a/doc/qtdesignstudio/src/qtdesignstudio-projects.qdoc b/doc/qtdesignstudio/src/qtdesignstudio-projects.qdoc index ffb23d35f2e..1aab65187ab 100644 --- a/doc/qtdesignstudio/src/qtdesignstudio-projects.qdoc +++ b/doc/qtdesignstudio/src/qtdesignstudio-projects.qdoc @@ -47,10 +47,10 @@ \li Creates a project that uses default and 3D components such as cameras, lights, 3D models, and materials. \row - \li Qt for MCUs + \li \QMCU \li MCU \li Creates an application that uses a subset of default components - (as supported by Qt for MCUs) that you can deploy, run, and debug + (as supported by \QMCU) that you can deploy, run, and debug on MCU boards. \row \li {1,3} Mobile From 53ce5d6dff66250ab63b29f520117e7e4e6ab114 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esa=20T=C3=B6rm=C3=A4nen?= Date: Tue, 4 Apr 2023 10:09:12 +0300 Subject: [PATCH 017/192] Doc: Add Qt Quick Ultralite macro Task-number: QDS-9613 Change-Id: I8b3dc970d2ca335f9efb320eb22b41891d96f498 Reviewed-by: Mats Honkamaa Reviewed-by: Leena Miettinen --- doc/config/macros.qdocconf | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/config/macros.qdocconf b/doc/config/macros.qdocconf index 99d1730cfe9..08398776843 100644 --- a/doc/config/macros.qdocconf +++ b/doc/config/macros.qdocconf @@ -33,6 +33,7 @@ macro.QMCU = "Qt for MCUs" macro.QMLD = "Qt Quick Designer" macro.QQV = "Qt QML Viewer" macro.QSDK = "Qt" +macro.QUL = "Qt Quick Ultralite" macro.qtcversion = $QTC_VERSION macro.param = "\\e" macro.raisedaster.HTML = "*" From c04f0e5d9c19910fc20d2eb48476deb1fe22fcde Mon Sep 17 00:00:00 2001 From: Tim Jenssen Date: Mon, 3 Apr 2023 14:42:08 +0200 Subject: [PATCH 018/192] Increase the minimum macOS version to 10.15 Qt 6.5 doesn't support 10.14 anymore, because it uses std::filesystem. QmlDesigner also wants to use std::filesystem. Change-Id: I36c14c6efa439c9372eb0d8bf90d1024e8b532eb Reviewed-by: Leena Miettinen Reviewed-by: Tim Jenssen --- .github/workflows/build_cmake.yml | 2 +- README.md | 4 ++-- coin/instructions/common_environment.yaml | 2 +- .../src/overview/creator-only/creator-desktop-platforms.qdoc | 2 +- qtcreator.qbs | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build_cmake.yml b/.github/workflows/build_cmake.yml index 6c14dcfb55a..930c8707d86 100644 --- a/.github/workflows/build_cmake.yml +++ b/.github/workflows/build_cmake.yml @@ -8,7 +8,7 @@ on: env: QT_VERSION: 6.4.2 - MACOS_DEPLOYMENT_TARGET: 10.14 + MACOS_DEPLOYMENT_TARGET: 10.15 CLANG_VERSION: 16.0.0-rc2 ELFUTILS_VERSION: 0.175 CMAKE_VERSION: 3.21.1 diff --git a/README.md b/README.md index 590361d1ef6..9c2128010d7 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ The standalone binary packages support the following platforms: * Windows 10 (64-bit) or later * (K)Ubuntu Linux 20.04 (64-bit) or later -* macOS 10.14 or later +* macOS 10.15 or later ## Contributing @@ -37,7 +37,7 @@ Prerequisites: * Qt 6.2 or later. The Qt version that you use to build Qt Creator defines the minimum platform versions that the result supports - (Windows 10, RHEL/CentOS 8.4, Ubuntu 20.04, macOS 10.14 for Qt 6.2). + (Windows 10, RHEL/CentOS 8.4, Ubuntu 20.04, macOS 10.15 for Qt 6.2). * Qt WebEngine module for QtWebEngine based help viewer * On Windows: * MinGW with GCC 9 or Visual Studio 2019 or later diff --git a/coin/instructions/common_environment.yaml b/coin/instructions/common_environment.yaml index 1124788c83b..576515a1229 100644 --- a/coin/instructions/common_environment.yaml +++ b/coin/instructions/common_environment.yaml @@ -16,7 +16,7 @@ instructions: variableValue: "qt5compat qtbase qtdeclarative qtimageformats qtquick3d qtquickcontrols2 qtquicktimeline qtserialport qtshadertools qtsvg qttools qttranslations qtwebengine" - type: EnvironmentVariable variableName: MACOSX_DEPLOYMENT_TARGET - variableValue: 10.14 + variableValue: 10.15 - type: EnvironmentVariable variableName: QTC_SDKTOOL_QT_BASE_URL variableValue: "http://ci-files02-hki.intra.qt.io/packages/jenkins/archive/qt/5.15/5.15.2-final-released/latest/src/submodules/qtbase-everywhere-src-5.15.2" diff --git a/doc/qtcreator/src/overview/creator-only/creator-desktop-platforms.qdoc b/doc/qtcreator/src/overview/creator-only/creator-desktop-platforms.qdoc index 4cdf397d1fb..5776d92e8ba 100644 --- a/doc/qtcreator/src/overview/creator-only/creator-desktop-platforms.qdoc +++ b/doc/qtcreator/src/overview/creator-only/creator-desktop-platforms.qdoc @@ -47,7 +47,7 @@ \section1 macOS - \macos 10.14 or later is supported with the Xcode tools for your \macos + \macos 10.15 or later is supported with the Xcode tools for your \macos version available in the Mac App Store. \section1 Windows diff --git a/qtcreator.qbs b/qtcreator.qbs index 7f70064e84a..c0779f4af10 100644 --- a/qtcreator.qbs +++ b/qtcreator.qbs @@ -5,7 +5,7 @@ import qbs.FileInfo Project { name: "Qt Creator" minimumQbsVersion: "1.19.0" - property string minimumMacosVersion: "10.14" + property string minimumMacosVersion: "10.15" property bool withAutotests: qbs.buildVariant === "debug" property path ide_source_tree: path property pathList additionalPlugins: [] From d371bf18a20981eca54297e700bebfa74e8255ed Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Mon, 3 Apr 2023 11:24:01 +0200 Subject: [PATCH 019/192] QmlDesigner: Optimize copies Use string views instead of strings to avoid copies. Change-Id: I4649a3c87a0b58cc3f87d01e4fa047f459f52603 Reviewed-by: Qt CI Bot Reviewed-by: Thomas Hartmann --- .../itemlibraryiconimageprovider.cpp | 2 +- .../previewtooltip/previewtooltipbackend.cpp | 7 +++-- .../asynchronousexplicitimagecache.cpp | 30 ++++++------------- .../imagecache/asynchronousimagecache.cpp | 12 ++++---- .../imagecache/asynchronousimagefactory.cpp | 10 +++---- .../imagecache/asynchronousimagefactory.h | 8 ++--- .../explicitimagecacheimageprovider.cpp | 2 +- .../imagecache/midsizeimagecacheprovider.cpp | 2 +- .../imagecache/smallimagecacheprovider.cpp | 2 +- .../include/asynchronousexplicitimagecache.h | 12 ++++---- .../include/asynchronousimagecache.h | 12 ++++---- .../include/asynchronousimagecacheinterface.h | 12 ++++---- 12 files changed, 50 insertions(+), 61 deletions(-) diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryiconimageprovider.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryiconimageprovider.cpp index cdaa3e28a9c..304aa5716ff 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryiconimageprovider.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryiconimageprovider.cpp @@ -20,7 +20,7 @@ QQuickImageResponse *ItemLibraryIconImageProvider::requestImageResponse(const QS Utils::StyleHelper::dpiSpecificImageFile(":/ItemLibrary/images/item-default-icon.png")}); m_cache.requestSmallImage( - id, + Utils::PathString{id}, [response = QPointer(response.get())](const QImage &image) { QMetaObject::invokeMethod( response, diff --git a/src/plugins/qmldesigner/components/previewtooltip/previewtooltipbackend.cpp b/src/plugins/qmldesigner/components/previewtooltip/previewtooltipbackend.cpp index 550ff8fcaf9..16328ae532f 100644 --- a/src/plugins/qmldesigner/components/previewtooltip/previewtooltipbackend.cpp +++ b/src/plugins/qmldesigner/components/previewtooltip/previewtooltipbackend.cpp @@ -32,8 +32,9 @@ void PreviewTooltipBackend::showTooltip() m_tooltip->setInfo(m_info); m_cache.requestImage( - m_path, - [tooltip = QPointer(m_tooltip.get()), scaleImage = m_scaleImage](const QImage &image) { + Utils::PathString{m_path}, + [tooltip = QPointer(m_tooltip.get()), + scaleImage = m_scaleImage](const QImage &image) { QMetaObject::invokeMethod(tooltip, [tooltip, image, scaleImage] { if (tooltip) { tooltip->setImage(image, scaleImage); @@ -42,7 +43,7 @@ void PreviewTooltipBackend::showTooltip() }); }, [](auto) {}, - m_extraId, + Utils::PathString{m_extraId}, m_auxiliaryData); reposition(); diff --git a/src/plugins/qmldesigner/designercore/imagecache/asynchronousexplicitimagecache.cpp b/src/plugins/qmldesigner/designercore/imagecache/asynchronousexplicitimagecache.cpp index ed168b8a09b..1c46722d4c4 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/asynchronousexplicitimagecache.cpp +++ b/src/plugins/qmldesigner/designercore/imagecache/asynchronousexplicitimagecache.cpp @@ -78,42 +78,30 @@ void AsynchronousExplicitImageCache::wait() m_backgroundThread.join(); } -void AsynchronousExplicitImageCache::requestImage(Utils::PathString name, +void AsynchronousExplicitImageCache::requestImage(Utils::SmallStringView name, ImageCache::CaptureImageCallback captureCallback, ImageCache::AbortCallback abortCallback, - Utils::SmallString extraId) + Utils::SmallStringView extraId) { - addEntry(std::move(name), - std::move(extraId), - std::move(captureCallback), - std::move(abortCallback), - RequestType::Image); + addEntry(name, extraId, std::move(captureCallback), std::move(abortCallback), RequestType::Image); m_condition.notify_all(); } -void AsynchronousExplicitImageCache::requestMidSizeImage(Utils::PathString name, +void AsynchronousExplicitImageCache::requestMidSizeImage(Utils::SmallStringView name, ImageCache::CaptureImageCallback captureCallback, ImageCache::AbortCallback abortCallback, - Utils::SmallString extraId) + Utils::SmallStringView extraId) { - addEntry(std::move(name), - std::move(extraId), - std::move(captureCallback), - std::move(abortCallback), - RequestType::MidSizeImage); + addEntry(name, extraId, std::move(captureCallback), std::move(abortCallback), RequestType::MidSizeImage); m_condition.notify_all(); } -void AsynchronousExplicitImageCache::requestSmallImage(Utils::PathString name, +void AsynchronousExplicitImageCache::requestSmallImage(Utils::SmallStringView name, ImageCache::CaptureImageCallback captureCallback, ImageCache::AbortCallback abortCallback, - Utils::SmallString extraId) + Utils::SmallStringView extraId) { - addEntry(std::move(name), - std::move(extraId), - std::move(captureCallback), - std::move(abortCallback), - RequestType::SmallImage); + addEntry(name, extraId, std::move(captureCallback), std::move(abortCallback), RequestType::SmallImage); m_condition.notify_all(); } diff --git a/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagecache.cpp b/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagecache.cpp index dec46f87252..00b3ab722ef 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagecache.cpp +++ b/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagecache.cpp @@ -118,10 +118,10 @@ void AsynchronousImageCache::wait() m_backgroundThread.join(); } -void AsynchronousImageCache::requestImage(Utils::PathString name, +void AsynchronousImageCache::requestImage(Utils::SmallStringView name, ImageCache::CaptureImageCallback captureCallback, ImageCache::AbortCallback abortCallback, - Utils::SmallString extraId, + Utils::SmallStringView extraId, ImageCache::AuxiliaryData auxiliaryData) { addEntry(std::move(name), @@ -133,10 +133,10 @@ void AsynchronousImageCache::requestImage(Utils::PathString name, m_condition.notify_all(); } -void AsynchronousImageCache::requestMidSizeImage(Utils::PathString name, +void AsynchronousImageCache::requestMidSizeImage(Utils::SmallStringView name, ImageCache::CaptureImageCallback captureCallback, ImageCache::AbortCallback abortCallback, - Utils::SmallString extraId, + Utils::SmallStringView extraId, ImageCache::AuxiliaryData auxiliaryData) { addEntry(std::move(name), @@ -148,10 +148,10 @@ void AsynchronousImageCache::requestMidSizeImage(Utils::PathString name, m_condition.notify_all(); } -void AsynchronousImageCache::requestSmallImage(Utils::PathString name, +void AsynchronousImageCache::requestSmallImage(Utils::SmallStringView name, ImageCache::CaptureImageCallback captureCallback, ImageCache::AbortCallback abortCallback, - Utils::SmallString extraId, + Utils::SmallStringView extraId, ImageCache::AuxiliaryData auxiliaryData) { addEntry(std::move(name), diff --git a/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagefactory.cpp b/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagefactory.cpp index 500359182ae..5d6ced30601 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagefactory.cpp +++ b/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagefactory.cpp @@ -39,16 +39,16 @@ AsynchronousImageFactory::~AsynchronousImageFactory() wait(); } -void AsynchronousImageFactory::generate(Utils::PathString name, - Utils::SmallString extraId, +void AsynchronousImageFactory::generate(Utils::SmallStringView name, + Utils::SmallStringView extraId, ImageCache::AuxiliaryData auxiliaryData) { - addEntry(std::move(name), std::move(extraId), std::move(auxiliaryData)); + addEntry(name, extraId, std::move(auxiliaryData)); m_condition.notify_all(); } -void AsynchronousImageFactory::addEntry(Utils::PathString &&name, - Utils::SmallString &&extraId, +void AsynchronousImageFactory::addEntry(Utils::SmallStringView name, + Utils::SmallStringView extraId, ImageCache::AuxiliaryData &&auxiliaryData) { std::unique_lock lock{m_mutex}; diff --git a/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagefactory.h b/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagefactory.h index 4aa24d0a769..415646c73cd 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagefactory.h +++ b/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagefactory.h @@ -28,8 +28,8 @@ public: ~AsynchronousImageFactory(); - void generate(Utils::PathString name, - Utils::SmallString extraId = {}, + void generate(Utils::SmallStringView name, + Utils::SmallStringView extraId = {}, ImageCache::AuxiliaryData auxiliaryData = {}); void clean(); @@ -50,8 +50,8 @@ private: ImageCache::AuxiliaryData auxiliaryData; }; - void addEntry(Utils::PathString &&name, - Utils::SmallString &&extraId, + void addEntry(Utils::SmallStringView name, + Utils::SmallStringView extraId, ImageCache::AuxiliaryData &&auxiliaryData); bool isRunning(); void waitForEntries(); diff --git a/src/plugins/qmldesigner/designercore/imagecache/explicitimagecacheimageprovider.cpp b/src/plugins/qmldesigner/designercore/imagecache/explicitimagecacheimageprovider.cpp index 1f28d4eca69..509abb5c70f 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/explicitimagecacheimageprovider.cpp +++ b/src/plugins/qmldesigner/designercore/imagecache/explicitimagecacheimageprovider.cpp @@ -17,7 +17,7 @@ QQuickImageResponse *ExplicitImageCacheImageProvider::requestImageResponse(const auto response = std::make_unique(m_defaultImage); m_cache.requestImage( - id, + Utils::PathString{id}, [response = QPointer(response.get())](const QImage &image) { QMetaObject::invokeMethod( response, diff --git a/src/plugins/qmldesigner/designercore/imagecache/midsizeimagecacheprovider.cpp b/src/plugins/qmldesigner/designercore/imagecache/midsizeimagecacheprovider.cpp index fd37f02f044..9b62ffe1bfa 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/midsizeimagecacheprovider.cpp +++ b/src/plugins/qmldesigner/designercore/imagecache/midsizeimagecacheprovider.cpp @@ -15,7 +15,7 @@ QQuickImageResponse *MidSizeImageCacheProvider::requestImageResponse(const QStri auto response = std::make_unique(m_defaultImage); m_cache.requestMidSizeImage( - id, + Utils::PathString{id}, [response = QPointer(response.get())](const QImage &image) { QMetaObject::invokeMethod( response, diff --git a/src/plugins/qmldesigner/designercore/imagecache/smallimagecacheprovider.cpp b/src/plugins/qmldesigner/designercore/imagecache/smallimagecacheprovider.cpp index 3fae3bb1754..722498dd98a 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/smallimagecacheprovider.cpp +++ b/src/plugins/qmldesigner/designercore/imagecache/smallimagecacheprovider.cpp @@ -16,7 +16,7 @@ QQuickImageResponse *SmallImageCacheProvider::requestImageResponse(const QString auto response = std::make_unique(m_defaultImage); m_cache.requestSmallImage( - id, + Utils::PathString{id}, [response = QPointer(response.get())](const QImage &image) { QMetaObject::invokeMethod( response, diff --git a/src/plugins/qmldesigner/designercore/include/asynchronousexplicitimagecache.h b/src/plugins/qmldesigner/designercore/include/asynchronousexplicitimagecache.h index 7cddfd29d9f..f9a053a9413 100644 --- a/src/plugins/qmldesigner/designercore/include/asynchronousexplicitimagecache.h +++ b/src/plugins/qmldesigner/designercore/include/asynchronousexplicitimagecache.h @@ -23,18 +23,18 @@ public: AsynchronousExplicitImageCache(ImageCacheStorageInterface &storage); - void requestImage(Utils::PathString name, + void requestImage(Utils::SmallStringView name, ImageCache::CaptureImageCallback captureCallback, ImageCache::AbortCallback abortCallback, - Utils::SmallString extraId = {}); - void requestMidSizeImage(Utils::PathString name, + Utils::SmallStringView extraId = {}); + void requestMidSizeImage(Utils::SmallStringView name, ImageCache::CaptureImageCallback captureCallback, ImageCache::AbortCallback abortCallback, - Utils::SmallString extraId = {}); - void requestSmallImage(Utils::PathString name, + Utils::SmallStringView extraId = {}); + void requestSmallImage(Utils::SmallStringView name, ImageCache::CaptureImageCallback captureCallback, ImageCache::AbortCallback abortCallback, - Utils::SmallString extraId = {}); + Utils::SmallStringView extraId = {}); void clean(); diff --git a/src/plugins/qmldesigner/designercore/include/asynchronousimagecache.h b/src/plugins/qmldesigner/designercore/include/asynchronousimagecache.h index 04882d61eb7..3257f9d75c1 100644 --- a/src/plugins/qmldesigner/designercore/include/asynchronousimagecache.h +++ b/src/plugins/qmldesigner/designercore/include/asynchronousimagecache.h @@ -28,20 +28,20 @@ public: ImageCacheGeneratorInterface &generator, TimeStampProviderInterface &timeStampProvider); - void requestImage(Utils::PathString name, + void requestImage(Utils::SmallStringView name, ImageCache::CaptureImageCallback captureCallback, ImageCache::AbortCallback abortCallback, - Utils::SmallString extraId = {}, + Utils::SmallStringView extraId = {}, ImageCache::AuxiliaryData auxiliaryData = {}) override; - void requestMidSizeImage(Utils::PathString name, + void requestMidSizeImage(Utils::SmallStringView name, ImageCache::CaptureImageCallback captureCallback, ImageCache::AbortCallback abortCallback, - Utils::SmallString extraId = {}, + Utils::SmallStringView extraId = {}, ImageCache::AuxiliaryData auxiliaryData = {}) override; - void requestSmallImage(Utils::PathString name, + void requestSmallImage(Utils::SmallStringView name, ImageCache::CaptureImageCallback captureCallback, ImageCache::AbortCallback abortCallback, - Utils::SmallString extraId = {}, + Utils::SmallStringView extraId = {}, ImageCache::AuxiliaryData auxiliaryData = {}) override; void clean(); diff --git a/src/plugins/qmldesigner/designercore/include/asynchronousimagecacheinterface.h b/src/plugins/qmldesigner/designercore/include/asynchronousimagecacheinterface.h index da8bda079e2..43c8e5b6726 100644 --- a/src/plugins/qmldesigner/designercore/include/asynchronousimagecacheinterface.h +++ b/src/plugins/qmldesigner/designercore/include/asynchronousimagecacheinterface.h @@ -14,22 +14,22 @@ namespace QmlDesigner { class AsynchronousImageCacheInterface { public: - virtual void requestImage(Utils::PathString name, + virtual void requestImage(Utils::SmallStringView name, ImageCache::CaptureImageCallback captureCallback, ImageCache::AbortCallback abortCallback, - Utils::SmallString extraId = {}, + Utils::SmallStringView extraId = {}, ImageCache::AuxiliaryData auxiliaryData = {}) = 0; - virtual void requestMidSizeImage(Utils::PathString name, + virtual void requestMidSizeImage(Utils::SmallStringView name, ImageCache::CaptureImageCallback captureCallback, ImageCache::AbortCallback abortCallback, - Utils::SmallString extraId = {}, + Utils::SmallStringView extraId = {}, ImageCache::AuxiliaryData auxiliaryData = {}) = 0; - virtual void requestSmallImage(Utils::PathString name, + virtual void requestSmallImage(Utils::SmallStringView name, ImageCache::CaptureImageCallback captureCallback, ImageCache::AbortCallback abortCallback, - Utils::SmallString extraId = {}, + Utils::SmallStringView extraId = {}, ImageCache::AuxiliaryData auxiliaryData = {}) = 0; From 318bb028016ecf5203f88d4e631635458454c873 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Tue, 21 Mar 2023 16:39:24 +0100 Subject: [PATCH 020/192] QmlDesigner: React on watcher notification The watcher notifies the updater if files or directories are changed. The updater then does minimal updates to the project storage. Common cases like that the qml documents or qmltypes are changed are handled by an extra path. Otherwise we trigger a directory update. If the directory is in 'strange' state like the qmltypes file is removed but the qmldir is not updated the changed file ids are saved and used additionally in the next notification. Task-number: QDS-9479 Change-Id: Ie82fd914b2e229f0315bf60df398c85477ce5a26 Reviewed-by: Thomas Hartmann Reviewed-by: Tim Jenssen Reviewed-by: Qt CI Bot --- .../projectstorage/projectstorage.h | 21 +- .../projectstorageexceptions.cpp | 5 + .../projectstorage/projectstorageexceptions.h | 42 +- .../projectstorage/projectstorageinterface.h | 1 + .../projectstoragepathwatcher.h | 2 +- .../projectstoragepathwatcherinterface.h | 3 + .../projectstorage/projectstorageupdater.cpp | 241 ++- .../projectstorage/projectstorageupdater.h | 75 +- .../qmldesigner/qmldesignerprojectmanager.cpp | 7 +- tests/unit/unittest/projectstorage-test.cpp | 26 +- tests/unit/unittest/projectstoragemock.h | 5 + .../unittest/projectstoragepathwatchermock.h | 11 +- .../unittest/projectstorageupdater-test.cpp | 1580 ++++++++++++++++- 13 files changed, 1864 insertions(+), 155 deletions(-) diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h index ad3e8931af8..a036653f4f8 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h @@ -414,16 +414,22 @@ public: sourceId); } + std::optional fetchProjectData(SourceId sourceId) const override + { + return selectProjectDataForSourceIdStatement + .template optionalValueWithTransaction(sourceId); + } + Storage::Synchronization::ProjectDatas fetchProjectDatas(SourceId projectSourceId) const override { - return selectProjectDatasForModuleIdStatement + return selectProjectDatasForSourceIdStatement .template valuesWithTransaction(64, projectSourceId); } Storage::Synchronization::ProjectDatas fetchProjectDatas(const SourceIds &projectSourceIds) const { - return selectProjectDatasForModuleIdsStatement.template valuesWithTransaction< + return selectProjectDatasForSourceIdsStatement.template valuesWithTransaction< Storage::Synchronization::ProjectData>(64, toIntegers(projectSourceIds)); } @@ -712,7 +718,7 @@ private: < std::tie(second.projectSourceId, second.sourceId); }); - auto range = selectProjectDatasForModuleIdsStatement + auto range = selectProjectDatasForSourceIdsStatement .template range( toIntegers(updatedProjectSourceIds)); @@ -2581,6 +2587,7 @@ private: table.addColumn("fileType", Sqlite::StrictColumnType::Integer); table.addPrimaryKeyContraint({projectSourceIdColumn, sourceIdColumn}); + table.addUniqueIndex({sourceIdColumn}); table.initialize(database); } @@ -3082,7 +3089,7 @@ public: "DELETE FROM exportedTypeNames WHERE exportedTypeNameId=?", database}; WriteStatement<2> updateExportedTypeNameTypeIdStatement{ "UPDATE exportedTypeNames SET typeId=?2 WHERE exportedTypeNameId=?1", database}; - mutable ReadStatement<4, 1> selectProjectDatasForModuleIdsStatement{ + mutable ReadStatement<4, 1> selectProjectDatasForSourceIdsStatement{ "SELECT projectSourceId, sourceId, moduleId, fileType FROM projectDatas WHERE " "projectSourceId IN carray(?1) ORDER BY projectSourceId, sourceId", database}; @@ -3095,10 +3102,14 @@ public: WriteStatement<4> updateProjectDataStatement{ "UPDATE projectDatas SET moduleId=?3, fileType=?4 WHERE projectSourceId=?1 AND sourceId=?2", database}; - mutable ReadStatement<4, 1> selectProjectDatasForModuleIdStatement{ + mutable ReadStatement<4, 1> selectProjectDatasForSourceIdStatement{ "SELECT projectSourceId, sourceId, moduleId, fileType FROM projectDatas WHERE " "projectSourceId=?1", database}; + mutable ReadStatement<4, 1> selectProjectDataForSourceIdStatement{ + "SELECT projectSourceId, sourceId, moduleId, fileType FROM projectDatas WHERE " + "sourceId=?1 LIMIT 1", + database}; mutable ReadStatement<1, 1> selectTypeIdsForSourceIdsStatement{ "SELECT typeId FROM types WHERE sourceId IN carray(?1)", database}; mutable ReadStatement<6, 1> selectModuleExportedImportsForSourceIdStatement{ diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.cpp b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.cpp index d574ef54750..07e925df689 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.cpp +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.cpp @@ -95,4 +95,9 @@ const char *FileStatusHasInvalidSourceId::what() const noexcept return "The source id in file status is invalid!"; } +const char *ProjectStorageError::what() const noexcept +{ + return "Project storage error!"; +} + } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.h index d78283ca180..e5e50e76092 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.h @@ -9,109 +9,115 @@ namespace QmlDesigner { -class QMLDESIGNERCORE_EXPORT NoSourcePathForInvalidSourceId : std::exception +class QMLDESIGNERCORE_EXPORT ProjectStorageError : std::exception { public: const char *what() const noexcept override; }; -class QMLDESIGNERCORE_EXPORT NoSourceContextPathForInvalidSourceContextId : std::exception +class QMLDESIGNERCORE_EXPORT NoSourcePathForInvalidSourceId : ProjectStorageError { public: const char *what() const noexcept override; }; -class QMLDESIGNERCORE_EXPORT SourceContextIdDoesNotExists : std::exception +class QMLDESIGNERCORE_EXPORT NoSourceContextPathForInvalidSourceContextId : ProjectStorageError { public: const char *what() const noexcept override; }; -class QMLDESIGNERCORE_EXPORT SourceIdDoesNotExists : std::exception +class QMLDESIGNERCORE_EXPORT SourceContextIdDoesNotExists : ProjectStorageError { public: const char *what() const noexcept override; }; -class QMLDESIGNERCORE_EXPORT TypeHasInvalidSourceId : std::exception +class QMLDESIGNERCORE_EXPORT SourceIdDoesNotExists : ProjectStorageError { public: const char *what() const noexcept override; }; -class QMLDESIGNERCORE_EXPORT ModuleDoesNotExists : std::exception +class QMLDESIGNERCORE_EXPORT TypeHasInvalidSourceId : ProjectStorageError { public: const char *what() const noexcept override; }; -class QMLDESIGNERCORE_EXPORT ModuleAlreadyExists : std::exception +class QMLDESIGNERCORE_EXPORT ModuleDoesNotExists : ProjectStorageError { public: const char *what() const noexcept override; }; -class QMLDESIGNERCORE_EXPORT ExportedTypeCannotBeInserted : std::exception +class QMLDESIGNERCORE_EXPORT ModuleAlreadyExists : ProjectStorageError { public: const char *what() const noexcept override; }; -class QMLDESIGNERCORE_EXPORT TypeNameDoesNotExists : std::exception +class QMLDESIGNERCORE_EXPORT ExportedTypeCannotBeInserted : ProjectStorageError { public: const char *what() const noexcept override; }; -class QMLDESIGNERCORE_EXPORT PropertyNameDoesNotExists : std::exception +class QMLDESIGNERCORE_EXPORT TypeNameDoesNotExists : ProjectStorageError { public: const char *what() const noexcept override; }; -class QMLDESIGNERCORE_EXPORT PrototypeChainCycle : std::exception +class QMLDESIGNERCORE_EXPORT PropertyNameDoesNotExists : ProjectStorageError { public: const char *what() const noexcept override; }; -class QMLDESIGNERCORE_EXPORT AliasChainCycle : std::exception +class QMLDESIGNERCORE_EXPORT PrototypeChainCycle : ProjectStorageError { public: const char *what() const noexcept override; }; -class QMLDESIGNERCORE_EXPORT CannotParseQmlTypesFile : std::exception +class QMLDESIGNERCORE_EXPORT AliasChainCycle : ProjectStorageError { public: const char *what() const noexcept override; }; -class QMLDESIGNERCORE_EXPORT CannotParseQmlDocumentFile : std::exception +class QMLDESIGNERCORE_EXPORT CannotParseQmlTypesFile : ProjectStorageError { public: const char *what() const noexcept override; }; -class QMLDESIGNERCORE_EXPORT ProjectDataHasInvalidProjectSourceId : std::exception +class QMLDESIGNERCORE_EXPORT CannotParseQmlDocumentFile : ProjectStorageError { public: const char *what() const noexcept override; }; -class QMLDESIGNERCORE_EXPORT ProjectDataHasInvalidSourceId : std::exception +class QMLDESIGNERCORE_EXPORT ProjectDataHasInvalidProjectSourceId : ProjectStorageError { public: const char *what() const noexcept override; }; -class QMLDESIGNERCORE_EXPORT ProjectDataHasInvalidModuleId : std::exception +class QMLDESIGNERCORE_EXPORT ProjectDataHasInvalidSourceId : ProjectStorageError { public: const char *what() const noexcept override; }; -class QMLDESIGNERCORE_EXPORT FileStatusHasInvalidSourceId : std::exception +class QMLDESIGNERCORE_EXPORT ProjectDataHasInvalidModuleId : ProjectStorageError +{ +public: + const char *what() const noexcept override; +}; + +class QMLDESIGNERCORE_EXPORT FileStatusHasInvalidSourceId : ProjectStorageError { public: 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 f3e87e2cf4e..5a89d29dacf 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinterface.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinterface.h @@ -17,6 +17,7 @@ public: virtual FileStatus fetchFileStatus(SourceId sourceId) const = 0; virtual Storage::Synchronization::ProjectDatas fetchProjectDatas(SourceId sourceId) const = 0; + virtual std::optional fetchProjectData(SourceId sourceId) const = 0; protected: ~ProjectStorageInterface() = default; diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstoragepathwatcher.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstoragepathwatcher.h index 0d3bc730f81..d79b87807f8 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstoragepathwatcher.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstoragepathwatcher.h @@ -71,7 +71,7 @@ public: } void updateContextIdPaths(const std::vector &idPaths, - const SourceContextIds &sourceContextIds) + const SourceContextIds &sourceContextIds) override { const auto &[entires, ids] = convertIdPathsToWatcherEntriesAndIds(idPaths); diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstoragepathwatcherinterface.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstoragepathwatcherinterface.h index 110cf07cff0..8e67b80b9a7 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstoragepathwatcherinterface.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstoragepathwatcherinterface.h @@ -19,6 +19,9 @@ public: ProjectStoragePathWatcherInterface &operator=(const ProjectStoragePathWatcherInterface &) = delete; virtual void updateIdPaths(const std::vector &idPaths) = 0; + virtual void updateContextIdPaths(const std::vector &idPaths, + const SourceContextIds &sourceContextIds) + = 0; virtual void removeIds(const ProjectPartIds &ids) = 0; virtual void setNotifier(ProjectStoragePathWatcherNotifierInterface *notifier) = 0; diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.cpp b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.cpp index f98c69734a8..a02a9c47aac 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.cpp +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.cpp @@ -159,41 +159,48 @@ void addModuleExportedImports(Storage::Synchronization::ModuleExportedImports &i } } -} // namespace - -void ProjectStorageUpdater::update(QStringList directories, - QStringList qmlTypesPaths, +std::vector createIdPaths(ProjectStorageUpdater::WatchedSourceIdsIds watchedSourceIds, ProjectPartId projectPartId) { - Storage::Synchronization::SynchronizationPackage package; - SourceIdsData sourceIdsData{static_cast(directories.size())}; std::vector idPaths; idPaths.reserve(4); - updateDirectories(directories, package, sourceIdsData); - updateQmlTypes(qmlTypesPaths, package, sourceIdsData); + idPaths.push_back( + {projectPartId, SourceType::Directory, std::move(watchedSourceIds.directorySourceIds)}); + idPaths.push_back({projectPartId, SourceType::QmlDir, std::move(watchedSourceIds.qmldirSourceIds)}); + idPaths.push_back({projectPartId, SourceType::Qml, std::move(watchedSourceIds.qmlSourceIds)}); + idPaths.push_back( + {projectPartId, SourceType::QmlTypes, std::move(watchedSourceIds.qmltypesSourceIds)}); + + return idPaths; +} + +} // namespace + +void ProjectStorageUpdater::update(QStringList directories, QStringList qmlTypesPaths) +{ + Storage::Synchronization::SynchronizationPackage package; + WatchedSourceIdsIds watchedSourceIds{Utils::span{directories}.size()}; + NotUpdatedSourceIds notUpdatedSourceIds{Utils::span{directories}.size()}; + + updateDirectories(directories, package, notUpdatedSourceIds, watchedSourceIds); + updateQmlTypes(qmlTypesPaths, package, notUpdatedSourceIds, watchedSourceIds); package.updatedSourceIds = filterNotUpdatedSourceIds(std::move(package.updatedSourceIds), - std::move(sourceIdsData.notUpdatedSourceIds)); + std::move(notUpdatedSourceIds.sourceIds)); package.updatedFileStatusSourceIds = filterNotUpdatedSourceIds( std::move(package.updatedFileStatusSourceIds), - std::move(sourceIdsData.notUpdatedFileStatusSourceIds)); + std::move(notUpdatedSourceIds.fileStatusSourceIds)); m_projectStorage.synchronize(std::move(package)); - idPaths.push_back( - {projectPartId, SourceType::Directory, std::move(sourceIdsData.watchedDirectorySourceIds)}); - idPaths.push_back( - {projectPartId, SourceType::QmlDir, std::move(sourceIdsData.watchedQmldirSourceIds)}); - idPaths.push_back({projectPartId, SourceType::Qml, std::move(sourceIdsData.watchedQmlSourceIds)}); - idPaths.push_back( - {projectPartId, SourceType::QmlTypes, std::move(sourceIdsData.watchedQmltypesSourceIds)}); - m_pathWatcher.updateIdPaths(idPaths); + m_pathWatcher.updateIdPaths(createIdPaths(watchedSourceIds, m_projectPartId)); } void ProjectStorageUpdater::updateQmlTypes(const QStringList &qmlTypesPaths, Storage::Synchronization::SynchronizationPackage &package, - SourceIdsData &sourceIdsData) + NotUpdatedSourceIds ¬UpdatedSourceIds, + WatchedSourceIdsIds &watchedSourceIdsIds) { if (qmlTypesPaths.empty()) return; @@ -202,7 +209,7 @@ void ProjectStorageUpdater::updateQmlTypes(const QStringList &qmlTypesPaths, for (const QString &qmlTypesPath : qmlTypesPaths) { SourceId sourceId = m_pathCache.sourceId(SourcePath{qmlTypesPath}); - sourceIdsData.watchedQmltypesSourceIds.push_back(sourceId); + watchedSourceIdsIds.qmltypesSourceIds.push_back(sourceId); Storage::Synchronization::ProjectData projectData{sourceId, sourceId, @@ -212,7 +219,7 @@ void ProjectStorageUpdater::updateQmlTypes(const QStringList &qmlTypesPaths, FileState state = parseTypeInfo(projectData, Utils::PathString{qmlTypesPath}, package, - sourceIdsData); + notUpdatedSourceIds); if (state == FileState::Changed) package.projectDatas.push_back(std::move(projectData)); @@ -235,28 +242,30 @@ ProjectStorageUpdater::FileState combineState(FileStates... fileStates) void ProjectStorageUpdater::updateDirectories(const QStringList &directories, Storage::Synchronization::SynchronizationPackage &package, - SourceIdsData &sourceIdsData) + NotUpdatedSourceIds ¬UpdatedSourceIds, + WatchedSourceIdsIds &watchedSourceIdsIds) { for (const QString &directory : directories) - updateDirectory({directory}, package, sourceIdsData); + updateDirectory({directory}, package, notUpdatedSourceIds, watchedSourceIdsIds); } void ProjectStorageUpdater::updateDirectory(const Utils::PathString &directoryPath, Storage::Synchronization::SynchronizationPackage &package, - SourceIdsData &sourceIdsData) + NotUpdatedSourceIds ¬UpdatedSourceIds, + WatchedSourceIdsIds &watchedSourceIdsIds) { SourcePath qmldirSourcePath{directoryPath + "/qmldir"}; auto [directoryId, qmlDirSourceId] = m_pathCache.sourceContextAndSourceId(qmldirSourcePath); SourcePath directorySourcePath{directoryPath + "/."}; auto directorySourceId = m_pathCache.sourceId(directorySourcePath); - auto directoryState = fileState(directorySourceId, package, sourceIdsData); + auto directoryState = fileState(directorySourceId, package, notUpdatedSourceIds); if (directoryState != FileState::NotExists) - sourceIdsData.watchedDirectorySourceIds.push_back(directorySourceId); + watchedSourceIdsIds.directorySourceIds.push_back(directorySourceId); - auto qmldirState = fileState(qmlDirSourceId, package, sourceIdsData); + auto qmldirState = fileState(qmlDirSourceId, package, notUpdatedSourceIds); if (qmldirState != FileState::NotExists) - sourceIdsData.watchedQmldirSourceIds.push_back(qmlDirSourceId); + watchedSourceIdsIds.qmldirSourceIds.push_back(qmlDirSourceId); switch (combineState(directoryState, qmldirState)) { case FileState::Changed: { @@ -295,20 +304,25 @@ void ProjectStorageUpdater::updateDirectory(const Utils::PathString &directoryPa directoryPath, cppModuleId, package, - sourceIdsData); + notUpdatedSourceIds, + watchedSourceIdsIds); } parseQmlComponents( createComponents(parser.components(), moduleId, pathModuleId, m_fileSystem, directoryPath), directorySourceId, directoryId, package, - sourceIdsData, + notUpdatedSourceIds, + watchedSourceIdsIds, qmldirState); package.updatedProjectSourceIds.push_back(directorySourceId); break; } case FileState::NotChanged: { - parseProjectDatas(m_projectStorage.fetchProjectDatas(directorySourceId), package, sourceIdsData); + parseProjectDatas(m_projectStorage.fetchProjectDatas(directorySourceId), + package, + notUpdatedSourceIds, + watchedSourceIdsIds); break; } case FileState::NotExists: { @@ -327,7 +341,118 @@ void ProjectStorageUpdater::updateDirectory(const Utils::PathString &directoryPa } } -void ProjectStorageUpdater::pathsWithIdsChanged([[maybe_unused]] const std::vector &) {} +namespace { +SourceContextIds filterUniqueSourceContextIds(const SourceIds &sourceIds, + ProjectStorageUpdater::PathCache &pathCache) +{ + auto sourceContextIds = Utils::transform(sourceIds, [&](SourceId sourceId) { + return pathCache.sourceContextId(sourceId); + }); + + std::sort(sourceContextIds.begin(), sourceContextIds.end()); + auto newEnd = std::unique(sourceContextIds.begin(), sourceContextIds.end()); + sourceContextIds.erase(newEnd, sourceContextIds.end()); + + return sourceContextIds; +} + +SourceIds filterUniqueSourceIds(SourceIds sourceIds) +{ + std::sort(sourceIds.begin(), sourceIds.end()); + auto newEnd = std::unique(sourceIds.begin(), sourceIds.end()); + sourceIds.erase(newEnd, sourceIds.end()); + + return sourceIds; +} + +template +bool contains(const Container &container, Id id) +{ + return std::find(container.begin(), container.end(), id) != container.end(); +} +} // namespace + +void ProjectStorageUpdater::pathsWithIdsChanged(const std::vector &changedIdPaths) +{ + m_changedIdPaths.insert(m_changedIdPaths.end(), changedIdPaths.begin(), changedIdPaths.end()); + + Storage::Synchronization::SynchronizationPackage package; + + WatchedSourceIdsIds watchedSourceIds{10}; + NotUpdatedSourceIds notUpdatedSourceIds{10}; + std::vector idPaths; + idPaths.reserve(4); + + SourceIds directorySourceIds; + directorySourceIds.reserve(32); + SourceIds qmlDocumentSourceIds; + qmlDocumentSourceIds.reserve(128); + SourceIds qmltypesSourceIds; + qmltypesSourceIds.reserve(32); + + for (const auto &[projectChunkId, sourceIds] : m_changedIdPaths) { + if (projectChunkId.id != m_projectPartId) + continue; + + switch (projectChunkId.sourceType) { + case SourceType::Directory: + case SourceType::QmlDir: + directorySourceIds.insert(directorySourceIds.end(), sourceIds.begin(), sourceIds.end()); + break; + case SourceType::Qml: + case SourceType::QmlUi: + qmlDocumentSourceIds.insert(qmlDocumentSourceIds.end(), sourceIds.begin(), sourceIds.end()); + break; + case SourceType::QmlTypes: + qmltypesSourceIds.insert(qmltypesSourceIds.end(), sourceIds.begin(), sourceIds.end()); + break; + } + } + + auto directorySourceContextIds = filterUniqueSourceContextIds(directorySourceIds, m_pathCache); + + for (auto sourceContextId : directorySourceContextIds) { + Utils::PathString directory = m_pathCache.sourceContextPath(sourceContextId); + updateDirectory(directory, package, notUpdatedSourceIds, watchedSourceIds); + } + + for (SourceId sourceId : filterUniqueSourceIds(qmlDocumentSourceIds)) { + if (!contains(directorySourceContextIds, m_pathCache.sourceContextId(sourceId))) + parseQmlComponent(sourceId, package, notUpdatedSourceIds); + } + + try { + 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); + } + } + } catch (const QmlDesigner::CannotParseQmlTypesFile &) { + return; + } + + package.updatedSourceIds = filterNotUpdatedSourceIds(std::move(package.updatedSourceIds), + std::move(notUpdatedSourceIds.sourceIds)); + package.updatedFileStatusSourceIds = filterNotUpdatedSourceIds( + std::move(package.updatedFileStatusSourceIds), + std::move(notUpdatedSourceIds.fileStatusSourceIds)); + + try { + m_projectStorage.synchronize(std::move(package)); + } catch (const ProjectStorageError &) { + return; + } + + if (directorySourceContextIds.size()) { + m_pathWatcher.updateContextIdPaths(createIdPaths(watchedSourceIds, m_projectPartId), + directorySourceContextIds); + } + + m_changedIdPaths.clear(); +} void ProjectStorageUpdater::pathsChanged(const SourceIds &) {} @@ -338,14 +463,15 @@ void ProjectStorageUpdater::parseTypeInfos(const QStringList &typeInfos, Utils::SmallStringView directoryPath, ModuleId moduleId, Storage::Synchronization::SynchronizationPackage &package, - SourceIdsData &sourceIdData) + NotUpdatedSourceIds ¬UpdatedSourceIds, + WatchedSourceIdsIds &watchedSourceIds) { for (const QString &typeInfo : typeInfos) { Utils::PathString qmltypesPath = Utils::PathString::join( {directoryPath, "/", Utils::SmallString{typeInfo}}); SourceId sourceId = m_pathCache.sourceId(SourcePathView{qmltypesPath}); - sourceIdData.watchedQmltypesSourceIds.push_back(sourceId); + watchedSourceIds.qmltypesSourceIds.push_back(sourceId); addDependencies(package.moduleDependencies, sourceId, @@ -356,27 +482,28 @@ void ProjectStorageUpdater::parseTypeInfos(const QStringList &typeInfos, auto projectData = package.projectDatas.emplace_back( directorySourceId, sourceId, moduleId, Storage::Synchronization::FileType::QmlTypes); - parseTypeInfo(projectData, qmltypesPath, package, sourceIdData); + parseTypeInfo(projectData, qmltypesPath, package, notUpdatedSourceIds); } } void ProjectStorageUpdater::parseProjectDatas(const Storage::Synchronization::ProjectDatas &projectDatas, Storage::Synchronization::SynchronizationPackage &package, - SourceIdsData &sourceIdData) + NotUpdatedSourceIds ¬UpdatedSourceIds, + WatchedSourceIdsIds &watchedSourceIds) { for (const Storage::Synchronization::ProjectData &projectData : projectDatas) { switch (projectData.fileType) { case Storage::Synchronization::FileType::QmlTypes: { - sourceIdData.watchedQmltypesSourceIds.push_back(projectData.sourceId); + watchedSourceIds.qmltypesSourceIds.push_back(projectData.sourceId); auto qmltypesPath = m_pathCache.sourcePath(projectData.sourceId); - parseTypeInfo(projectData, qmltypesPath, package, sourceIdData); + parseTypeInfo(projectData, qmltypesPath, package, notUpdatedSourceIds); break; } case Storage::Synchronization::FileType::QmlDocument: { - sourceIdData.watchedQmlSourceIds.push_back(projectData.sourceId); + watchedSourceIds.qmlSourceIds.push_back(projectData.sourceId); - parseQmlComponent(projectData.sourceId, package, sourceIdData); + parseQmlComponent(projectData.sourceId, package, notUpdatedSourceIds); } }; } @@ -385,9 +512,9 @@ void ProjectStorageUpdater::parseProjectDatas(const Storage::Synchronization::Pr auto ProjectStorageUpdater::parseTypeInfo(const Storage::Synchronization::ProjectData &projectData, Utils::SmallStringView qmltypesPath, Storage::Synchronization::SynchronizationPackage &package, - SourceIdsData &sourceIdData) -> FileState + NotUpdatedSourceIds ¬UpdatedSourceIds) -> FileState { - auto state = fileState(projectData.sourceId, package, sourceIdData); + auto state = fileState(projectData.sourceId, package, notUpdatedSourceIds); switch (state) { case FileState::Changed: { package.updatedSourceIds.push_back(projectData.sourceId); @@ -397,7 +524,7 @@ auto ProjectStorageUpdater::parseTypeInfo(const Storage::Synchronization::Projec break; } case FileState::NotChanged: { - sourceIdData.notUpdatedSourceIds.push_back(projectData.sourceId); + notUpdatedSourceIds.sourceIds.push_back(projectData.sourceId); break; } case FileState::NotExists: @@ -413,7 +540,8 @@ void ProjectStorageUpdater::parseQmlComponent(Utils::SmallStringView relativeFil Storage::Synchronization::ExportedTypes exportedTypes, SourceId directorySourceId, Storage::Synchronization::SynchronizationPackage &package, - SourceIdsData &sourceIdData, + NotUpdatedSourceIds ¬UpdatedSourceIds, + WatchedSourceIdsIds &watchedSourceIds, FileState qmldirState) { if (std::find(relativeFilePath.begin(), relativeFilePath.end(), '+') != relativeFilePath.end()) @@ -423,14 +551,14 @@ void ProjectStorageUpdater::parseQmlComponent(Utils::SmallStringView relativeFil SourceId sourceId = m_pathCache.sourceId(SourcePathView{qmlFilePath}); Storage::Synchronization::Type type; - auto state = fileState(sourceId, package, sourceIdData); + auto state = fileState(sourceId, package, notUpdatedSourceIds); - sourceIdData.watchedQmlSourceIds.push_back(sourceId); + watchedSourceIds.qmlSourceIds.push_back(sourceId); switch (state) { case FileState::NotChanged: if (qmldirState == FileState::NotExists) { - sourceIdData.notUpdatedSourceIds.emplace_back(sourceId); + notUpdatedSourceIds.sourceIds.emplace_back(sourceId); package.projectDatas.emplace_back(directorySourceId, sourceId, ModuleId{}, @@ -465,14 +593,17 @@ void ProjectStorageUpdater::parseQmlComponent(Utils::SmallStringView relativeFil void ProjectStorageUpdater::parseQmlComponent(SourceId sourceId, Storage::Synchronization::SynchronizationPackage &package, - SourceIdsData &sourceIdData) + NotUpdatedSourceIds ¬UpdatedSourceIds) { - auto state = fileState(sourceId, package, sourceIdData); - if (state != FileState::Changed) + auto state = fileState(sourceId, package, notUpdatedSourceIds); + if (state == FileState::NotChanged) return; package.updatedSourceIds.push_back(sourceId); + if (state == FileState::NotExists) + return; + SourcePath sourcePath = m_pathCache.sourcePath(sourceId); const auto content = m_fileSystem.contentAsQString(QString{sourcePath}); @@ -526,7 +657,8 @@ void ProjectStorageUpdater::parseQmlComponents(Components components, SourceId directorySourceId, SourceContextId directoryId, Storage::Synchronization::SynchronizationPackage &package, - SourceIdsData &sourceIdsData, + NotUpdatedSourceIds ¬UpdatedSourceIds, + WatchedSourceIdsIds &watchedSourceIdsIds, FileState qmldirState) { std::sort(components.begin(), components.end(), [](auto &&first, auto &&second) { @@ -543,7 +675,8 @@ void ProjectStorageUpdater::parseQmlComponents(Components components, createExportedTypes(componentsWithSameFileName), directorySourceId, package, - sourceIdsData, + notUpdatedSourceIds, + watchedSourceIdsIds, qmldirState); }; @@ -553,7 +686,7 @@ void ProjectStorageUpdater::parseQmlComponents(Components components, ProjectStorageUpdater::FileState ProjectStorageUpdater::fileState( SourceId sourceId, Storage::Synchronization::SynchronizationPackage &package, - SourceIdsData &sourceIdData) const + NotUpdatedSourceIds ¬UpdatedSourceIds) const { auto currentFileStatus = m_fileStatusCache.find(sourceId); @@ -570,7 +703,7 @@ ProjectStorageUpdater::FileState ProjectStorageUpdater::fileState( return FileState::Changed; } - sourceIdData.notUpdatedFileStatusSourceIds.push_back(sourceId); + notUpdatedSourceIds.fileStatusSourceIds.push_back(sourceId); return FileState::NotChanged; } diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.h index 90ce4d543d1..22778ac932a 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.h @@ -43,7 +43,8 @@ public: PathCache &pathCache, QmlDocumentParserInterface &qmlDocumentParser, QmlTypesParserInterface &qmlTypesParser, - class ProjectStoragePathWatcherInterface &pathWatcher) + class ProjectStoragePathWatcherInterface &pathWatcher, + ProjectPartId projectPartId) : m_fileSystem{fileSystem} , m_projectStorage{projectStorage} , m_fileStatusCache{fileStatusCache} @@ -51,11 +52,10 @@ public: , m_qmlDocumentParser{qmlDocumentParser} , m_qmlTypesParser{qmlTypesParser} , m_pathWatcher{pathWatcher} + , m_projectPartId{projectPartId} {} - void update(QStringList directories, - QStringList qmlTypesPaths, - ProjectPartId projectPartId = ProjectPartId{}); + void update(QStringList directories, QStringList qmlTypesPaths); void pathsWithIdsChanged(const std::vector &idPaths) override; void pathsChanged(const SourceIds &filePathIds) override; @@ -96,38 +96,49 @@ public: NotExists, }; -private: - struct SourceIdsData + struct WatchedSourceIdsIds { - SourceIdsData(std::size_t reserve) + WatchedSourceIdsIds(std::size_t reserve) { - notUpdatedFileStatusSourceIds.reserve(reserve * 30); - notUpdatedSourceIds.reserve(reserve * 30); - watchedDirectorySourceIds.reserve(reserve); - watchedQmldirSourceIds.reserve(reserve); - watchedQmlSourceIds.reserve(reserve * 30); - watchedQmltypesSourceIds.reserve(reserve * 30); + directorySourceIds.reserve(reserve); + qmldirSourceIds.reserve(reserve); + qmlSourceIds.reserve(reserve * 30); + qmltypesSourceIds.reserve(reserve * 30); } - SourceIds notUpdatedFileStatusSourceIds; - SourceIds notUpdatedSourceIds; - SourceIds watchedDirectorySourceIds; - SourceIds watchedQmldirSourceIds; - SourceIds watchedQmlSourceIds; - SourceIds watchedQmltypesSourceIds; + SourceIds directorySourceIds; + SourceIds qmldirSourceIds; + SourceIds qmlSourceIds; + SourceIds qmltypesSourceIds; }; + struct NotUpdatedSourceIds + { + NotUpdatedSourceIds(std::size_t reserve) + { + fileStatusSourceIds.reserve(reserve * 30); + sourceIds.reserve(reserve * 30); + } + + SourceIds fileStatusSourceIds; + SourceIds sourceIds; + }; + +private: void updateQmlTypes(const QStringList &qmlTypesPaths, Storage::Synchronization::SynchronizationPackage &package, - SourceIdsData &sourceIdData); + NotUpdatedSourceIds ¬UpdatedSourceIds, + WatchedSourceIdsIds &watchedSourceIdsIds); void updateDirectories(const QStringList &directories, Storage::Synchronization::SynchronizationPackage &package, - SourceIdsData &sourceIdData); + NotUpdatedSourceIds ¬UpdatedSourceIds, + WatchedSourceIdsIds &watchedSourceIdsIds); void updateDirectory(const Utils::PathString &directory, Storage::Synchronization::SynchronizationPackage &package, - SourceIdsData &sourceIdData); + NotUpdatedSourceIds ¬UpdatedSourceIds, + WatchedSourceIdsIds &watchedSourceIdsIds); void parseTypeInfos(const QStringList &typeInfos, const QList &qmldirDependencies, @@ -136,36 +147,41 @@ private: Utils::SmallStringView directoryPath, ModuleId moduleId, Storage::Synchronization::SynchronizationPackage &package, - SourceIdsData &sourceIdData); + NotUpdatedSourceIds ¬UpdatedSourceIds, + WatchedSourceIdsIds &watchedSourceIdsIds); void parseProjectDatas(const Storage::Synchronization::ProjectDatas &projectDatas, Storage::Synchronization::SynchronizationPackage &package, - SourceIdsData &sourceIdData); + NotUpdatedSourceIds ¬UpdatedSourceIds, + WatchedSourceIdsIds &watchedSourceIdsIds); FileState parseTypeInfo(const Storage::Synchronization::ProjectData &projectData, Utils::SmallStringView qmltypesPath, Storage::Synchronization::SynchronizationPackage &package, - SourceIdsData &sourceIdData); + NotUpdatedSourceIds ¬UpdatedSourceIds); void parseQmlComponents(Components components, SourceId directorySourceId, SourceContextId directoryId, Storage::Synchronization::SynchronizationPackage &package, - SourceIdsData &sourceIdData, + NotUpdatedSourceIds ¬UpdatedSourceIds, + WatchedSourceIdsIds &watchedSourceIdsIds, FileState qmldirState); void parseQmlComponent(Utils::SmallStringView fileName, Utils::SmallStringView directory, Storage::Synchronization::ExportedTypes exportedTypes, SourceId directorySourceId, Storage::Synchronization::SynchronizationPackage &package, - SourceIdsData &sourceIdData, + NotUpdatedSourceIds ¬UpdatedSourceIds, + WatchedSourceIdsIds &watchedSourceIdsIds, FileState qmldirState); void parseQmlComponent(SourceId sourceId, Storage::Synchronization::SynchronizationPackage &package, - SourceIdsData &sourceIdData); + NotUpdatedSourceIds ¬UpdatedSourceIds); FileState fileState(SourceId sourceId, Storage::Synchronization::SynchronizationPackage &package, - SourceIdsData &sourceIdData) const; + NotUpdatedSourceIds ¬UpdatedSourceIds) const; private: + std::vector m_changedIdPaths; FileSystemInterface &m_fileSystem; ProjectStorageInterface &m_projectStorage; FileStatusCache &m_fileStatusCache; @@ -173,6 +189,7 @@ private: QmlDocumentParserInterface &m_qmlDocumentParser; QmlTypesParserInterface &m_qmlTypesParser; ProjectStoragePathWatcherInterface &m_pathWatcher; + ProjectPartId m_projectPartId; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp b/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp index 8b323aa0c7a..e6a02fecab7 100644 --- a/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp +++ b/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp @@ -175,8 +175,9 @@ class ProjectStorageData public: ProjectStorageData(::ProjectExplorer::Project *project) : database{project->projectDirectory().pathAppended("projectstorage.db").toString()} + , projectPartId{ProjectPartId::create( + pathCache.sourceId(SourcePath{project->projectDirectory().toString() + "/."}).internalId())} {} - Sqlite::Database database; ProjectStorage storage{database, database.isInitialized()}; ProjectStorageUpdater::PathCache pathCache{storage}; @@ -186,13 +187,15 @@ public: QmlTypesParser qmlTypesParser{pathCache, storage}; ProjectStoragePathWatcher pathWatcher{pathCache, fileSystem, &updater}; + ProjectPartId projectPartId; ProjectStorageUpdater updater{fileSystem, storage, fileStatusCache, pathCache, qmlDocumentParser, qmlTypesParser, - pathWatcher}; + pathWatcher, + projectPartId}; }; std::unique_ptr createProjectStorageData(::ProjectExplorer::Project *project) diff --git a/tests/unit/unittest/projectstorage-test.cpp b/tests/unit/unittest/projectstorage-test.cpp index 07745826912..a11b804a5a0 100644 --- a/tests/unit/unittest/projectstorage-test.cpp +++ b/tests/unit/unittest/projectstorage-test.cpp @@ -5190,7 +5190,7 @@ TEST_F(ProjectStorage, ThrowForUpdatingWithInvalidProjectSourceIdInProjectData) QmlDesigner::ProjectDataHasInvalidProjectSourceId); } -TEST_F(ProjectStorage, FetchProjectDatasByModuleIds) +TEST_F(ProjectStorage, FetchProjectDatasByDirectorySourceIds) { Storage::Synchronization::ProjectData projectData1{qmlProjectSourceId, sourceId1, @@ -5212,7 +5212,7 @@ TEST_F(ProjectStorage, FetchProjectDatasByModuleIds) ASSERT_THAT(projectDatas, UnorderedElementsAre(projectData1, projectData2, projectData3)); } -TEST_F(ProjectStorage, FetchProjectDatasByModuleId) +TEST_F(ProjectStorage, FetchProjectDatasByDirectorySourceId) { Storage::Synchronization::ProjectData projectData1{qmlProjectSourceId, sourceId1, @@ -5234,6 +5234,28 @@ TEST_F(ProjectStorage, FetchProjectDatasByModuleId) ASSERT_THAT(projectData, UnorderedElementsAre(projectData1, projectData2)); } +TEST_F(ProjectStorage, FetchProjectDataBySourceIds) +{ + Storage::Synchronization::ProjectData projectData1{qmlProjectSourceId, + sourceId1, + qmlModuleId, + Storage::Synchronization::FileType::QmlDocument}; + Storage::Synchronization::ProjectData projectData2{qmlProjectSourceId, + sourceId2, + qmlModuleId, + Storage::Synchronization::FileType::QmlDocument}; + Storage::Synchronization::ProjectData projectData3{qtQuickProjectSourceId, + sourceId3, + qtQuickModuleId, + Storage::Synchronization::FileType::QmlTypes}; + storage.synchronize(SynchronizationPackage{{qmlProjectSourceId, qtQuickProjectSourceId}, + {projectData1, projectData2, projectData3}}); + + auto projectData = storage.fetchProjectData({sourceId2}); + + ASSERT_THAT(projectData, Eq(projectData2)); +} + TEST_F(ProjectStorage, ExcludeExportedTypes) { auto package{createSimpleSynchronizationPackage()}; diff --git a/tests/unit/unittest/projectstoragemock.h b/tests/unit/unittest/projectstoragemock.h index 6d2caaa811b..93cca00ca08 100644 --- a/tests/unit/unittest/projectstoragemock.h +++ b/tests/unit/unittest/projectstoragemock.h @@ -31,6 +31,11 @@ public: (QmlDesigner::SourceId sourceId), (const, override)); + MOCK_METHOD(std::optional, + fetchProjectData, + (QmlDesigner::SourceId sourceId), + (const, override)); + MOCK_METHOD(QmlDesigner::SourceContextId, fetchSourceContextId, (Utils::SmallStringView SourceContextPath), diff --git a/tests/unit/unittest/projectstoragepathwatchermock.h b/tests/unit/unittest/projectstoragepathwatchermock.h index 2c47291256a..97ebb65e45e 100644 --- a/tests/unit/unittest/projectstoragepathwatchermock.h +++ b/tests/unit/unittest/projectstoragepathwatchermock.h @@ -10,10 +10,15 @@ class ProjectStoragePathWatcherMock : public QmlDesigner::ProjectStoragePathWatcherInterface { public: - MOCK_METHOD(void, updateIdPaths, (const std::vector &idPaths), ()); - MOCK_METHOD(void, removeIds, (const QmlDesigner::ProjectPartIds &ids), ()); + MOCK_METHOD(void, updateIdPaths, (const std::vector &idPaths), (override)); + MOCK_METHOD(void, + updateContextIdPaths, + (const std::vector &idPaths, + const QmlDesigner::SourceContextIds &sourceContextIds), + (override)); + MOCK_METHOD(void, removeIds, (const QmlDesigner::ProjectPartIds &ids), (override)); MOCK_METHOD(void, setNotifier, (QmlDesigner::ProjectStoragePathWatcherNotifierInterface * notifier), - ()); + (override)); }; diff --git a/tests/unit/unittest/projectstorageupdater-test.cpp b/tests/unit/unittest/projectstorageupdater-test.cpp index da7e2f3adba..05b3166bbc5 100644 --- a/tests/unit/unittest/projectstorageupdater-test.cpp +++ b/tests/unit/unittest/projectstorageupdater-test.cpp @@ -160,7 +160,7 @@ public: setContent(u"/path/First2.qml", qmlDocument2); setContent(u"/path/Second.qml", qmlDocument3); setContent(u"/path/example.qmltypes", qmltypes1); - setContent(u"/path/types/example2.qmltypes", qmltypes2); + setContent(u"/path/example2.qmltypes", qmltypes2); setContent(u"/path/one/First.qml", qmlDocument1); setContent(u"/path/one/Second.qml", qmlDocument2); setContent(u"/path/two/Third.qml", qmlDocument3); @@ -252,6 +252,10 @@ public: { 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})); + } } void setContent(QStringView path, const QString &content) @@ -275,15 +279,18 @@ protected: QmlDesigner::SourcePathCache> sourcePathCache{ storage}; NiceMock patchWatcherMock; + QmlDesigner::ProjectPartId projectPartId = QmlDesigner::ProjectPartId::create(1); + QmlDesigner::ProjectPartId otherProjectPartId = QmlDesigner::ProjectPartId::create(0); QmlDesigner::ProjectStorageUpdater updater{fileSystemMock, projectStorageMock, fileStatusCache, sourcePathCache, qmlDocumentParserMock, qmlTypesParserMock, - patchWatcherMock}; + patchWatcherMock, + projectPartId}; SourceId qmltypesPathSourceId = sourcePathCache.sourceId("/path/example.qmltypes"); - SourceId qmltypes2PathSourceId = sourcePathCache.sourceId("/path/types/example2.qmltypes"); + SourceId qmltypes2PathSourceId = sourcePathCache.sourceId("/path/example2.qmltypes"); SourceId qmlDirPathSourceId = sourcePathCache.sourceId("/path/qmldir"); SourceId directoryPathSourceId = sourcePathCache.sourceId("/path/."); SourceId qmlDocumentSourceId1 = sourcePathCache.sourceId("/path/First.qml"); @@ -341,7 +348,20 @@ protected: QStringList directories = {"/path"}; QStringList directories2 = {"/path/one", "/path/two"}; QStringList directories3 = {"/path/one", "/path/two", "/path/three"}; - QmlDesigner::ProjectPartId projectPartId = QmlDesigner::ProjectPartId::create(1); + QmlDesigner::ProjectChunkId directoryProjectChunkId{projectPartId, + QmlDesigner::SourceType::Directory}; + QmlDesigner::ProjectChunkId qmldirProjectChunkId{projectPartId, QmlDesigner::SourceType::QmlDir}; + QmlDesigner::ProjectChunkId qmlDocumentProjectChunkId{projectPartId, QmlDesigner::SourceType::Qml}; + QmlDesigner::ProjectChunkId qmltypesProjectChunkId{projectPartId, + QmlDesigner::SourceType::QmlTypes}; + QmlDesigner::ProjectChunkId otherDirectoryProjectChunkId{otherProjectPartId, + QmlDesigner::SourceType::Directory}; + QmlDesigner::ProjectChunkId otherQmldirProjectChunkId{otherProjectPartId, + QmlDesigner::SourceType::QmlDir}; + QmlDesigner::ProjectChunkId otherQmlDocumentProjectChunkId{otherProjectPartId, + QmlDesigner::SourceType::Qml}; + QmlDesigner::ProjectChunkId otherQmltypesProjectChunkId{otherProjectPartId, + QmlDesigner::SourceType::QmlTypes}; SourceId path1SourceId = sourcePathCache.sourceId("/path/one/."); SourceId path2SourceId = sourcePathCache.sourceId("/path/two/."); SourceId path3SourceId = sourcePathCache.sourceId("/path/three/."); @@ -409,12 +429,12 @@ TEST_F(ProjectStorageUpdater, ParseQmlTypes) { QString qmldir{R"(module Example typeinfo example.qmltypes - typeinfo types/example2.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/types/example2.qmltypes", qmltypes2); + setContent(u"/path/example2.qmltypes", qmltypes2); EXPECT_CALL(qmlTypesParserMock, parse(qmltypes, _, _, Field(&ProjectData::moduleId, exampleCppNativeModuleId))); @@ -974,18 +994,6 @@ TEST_F(ProjectStorageUpdater, SynchronizeQmlDocumentsDontUpdateIfUpToDate) updater.update(directories, {}); } -TEST_F(ProjectStorageUpdater, UpdateQmldirDocuments) -{ - QString qmldir{R"(module Example - FirstType 1.1 First.qml - FirstType 2.2 First2.qml - SecondType 2.2 Second.qml)"}; - setContent(u"/path/qmldir", qmldir); - setFilesDontChanged({qmlDocumentSourceId3}); - - updater.pathsWithIdsChanged({}); -} - TEST_F(ProjectStorageUpdater, SynchronizIfQmldirFileHasNotChanged) { setProjectDatas( @@ -1072,6 +1080,73 @@ TEST_F(ProjectStorageUpdater, SynchronizIfQmldirFileHasNotChangedAndSomeUpdatedF updater.update(directories, {}); } +TEST_F(ProjectStorageUpdater, SynchronizIfQmldirFileNotChangedAndSomeRemovedFiles) +{ + setQmlFileNames(u"/path", {"First2.qml"}); + setProjectDatas( + directoryPathSourceId, + {{directoryPathSourceId, qmltypesPathSourceId, exampleModuleId, FileType::QmlTypes}, + {directoryPathSourceId, qmltypes2PathSourceId, exampleModuleId, FileType::QmlTypes}, + {directoryPathSourceId, qmlDocumentSourceId1, exampleModuleId, FileType::QmlDocument}, + {directoryPathSourceId, qmlDocumentSourceId2, exampleModuleId, FileType::QmlDocument}}); + setFilesDontChanged({qmlDirPathSourceId, qmltypes2PathSourceId, qmlDocumentSourceId2}); + setFilesRemoved({qmltypesPathSourceId, qmlDocumentSourceId1}); + + ASSERT_THROW(updater.update(directories, {}), QmlDesigner::CannotParseQmlTypesFile); +} + +TEST_F(ProjectStorageUpdater, SynchronizIfQmldirFileHasChangedAndSomeRemovedFiles) +{ + QString qmldir{R"(module Example + FirstType 2.2 First2.qml + typeinfo example2.qmltypes)"}; + setContent(u"/path/qmldir", qmldir); + setQmlFileNames(u"/path", {"First2.qml"}); + setProjectDatas( + directoryPathSourceId, + {{directoryPathSourceId, qmltypesPathSourceId, exampleModuleId, FileType::QmlTypes}, + {directoryPathSourceId, qmltypes2PathSourceId, exampleModuleId, FileType::QmlTypes}, + {directoryPathSourceId, qmlDocumentSourceId1, exampleModuleId, FileType::QmlDocument}, + {directoryPathSourceId, qmlDocumentSourceId2, exampleModuleId, FileType::QmlDocument}}); + setFilesDontChanged({qmltypes2PathSourceId, qmlDocumentSourceId2}); + setFilesRemoved({qmltypesPathSourceId, qmlDocumentSourceId1}); + + EXPECT_CALL( + projectStorageMock, + synchronize(AllOf( + Field(&SynchronizationPackage::imports, IsEmpty()), + Field(&SynchronizationPackage::types, + UnorderedElementsAre(AllOf( + IsStorageType("First2.qml", + Storage::Synchronization::ImportedType{}, + TypeTraits::Reference, + qmlDocumentSourceId2, + Storage::Synchronization::ChangeLevel::Minimal), + Field(&Storage::Synchronization::Type::exportedTypes, + UnorderedElementsAre(IsExportedType(pathModuleId, "First2", -1, -1), + IsExportedType(exampleModuleId, "FirstType", 2, 2)))))), + Field(&SynchronizationPackage::updatedSourceIds, + UnorderedElementsAre(qmlDirPathSourceId, + qmltypesPathSourceId, + qmlDocumentSourceId1, + qmlDocumentSourceId2)), + Field(&SynchronizationPackage::fileStatuses, + 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)))))); + + updater.update(directories, {}); +} + TEST_F(ProjectStorageUpdater, UpdateQmlTypesFilesIsEmpty) { EXPECT_CALL(projectStorageMock, @@ -1111,7 +1186,7 @@ TEST_F(ProjectStorageUpdater, UpdateQmlTypesFiles) FileType::QmlTypes))), Field(&SynchronizationPackage::updatedProjectSourceIds, IsEmpty())))); - updater.update({}, {"/path/example.qmltypes", "/path/types/example2.qmltypes"}); + updater.update({}, {"/path/example.qmltypes", "/path/example2.qmltypes"}); } TEST_F(ProjectStorageUpdater, DontUpdateQmlTypesFilesIfUnchanged) @@ -1135,7 +1210,7 @@ TEST_F(ProjectStorageUpdater, DontUpdateQmlTypesFilesIfUnchanged) FileType::QmlTypes))), Field(&SynchronizationPackage::updatedProjectSourceIds, IsEmpty())))); - updater.update({}, {"/path/example.qmltypes", "/path/types/example2.qmltypes"}); + updater.update({}, {"/path/example.qmltypes", "/path/example2.qmltypes"}); } TEST_F(ProjectStorageUpdater, SynchronizeQmlDocumentsWithDifferentVersionButSameTypeNameAndFileName) @@ -1246,7 +1321,7 @@ TEST_F(ProjectStorageUpdater, SynchronizeQmldirDependencies) depends Qml depends QML typeinfo example.qmltypes - typeinfo types/example2.qmltypes + typeinfo example2.qmltypes )"}; setContent(u"/path/qmldir", qmldir); @@ -1278,7 +1353,7 @@ TEST_F(ProjectStorageUpdater, SynchronizeQmldirDependenciesWithDoubleEntries) depends QML depends Qml typeinfo example.qmltypes - typeinfo types/example2.qmltypes + typeinfo example2.qmltypes )"}; setContent(u"/path/qmldir", qmldir); @@ -1310,7 +1385,7 @@ TEST_F(ProjectStorageUpdater, SynchronizeQmldirDependenciesWithCollidingImports) depends QML import Qml typeinfo example.qmltypes - typeinfo types/example2.qmltypes + typeinfo example2.qmltypes )"}; setContent(u"/path/qmldir", qmldir); @@ -1339,7 +1414,7 @@ TEST_F(ProjectStorageUpdater, SynchronizeQmldirWithNoDependencies) { QString qmldir{R"(module Example typeinfo example.qmltypes - typeinfo types/example2.qmltypes + typeinfo example2.qmltypes )"}; setContent(u"/path/qmldir", qmldir); @@ -1500,7 +1575,7 @@ TEST_F(ProjectStorageUpdater, UpdatePathWatcherDirectories) QmlDesigner::SourceType::Directory, {path1SourceId, path2SourceId, path3SourceId}}))); - updater.update(directories3, {}, projectPartId); + updater.update(directories3, {}); } TEST_F(ProjectStorageUpdater, UpdatePathWatcherDirectoryDoesNotExists) @@ -1512,7 +1587,7 @@ TEST_F(ProjectStorageUpdater, UpdatePathWatcherDirectoryDoesNotExists) QmlDesigner::SourceType::Directory, {path1SourceId, path3SourceId}}))); - updater.update(directories3, {}, projectPartId); + updater.update(directories3, {}); } TEST_F(ProjectStorageUpdater, UpdatePathWatcherDirectoryDoesNotChanged) @@ -1524,7 +1599,7 @@ TEST_F(ProjectStorageUpdater, UpdatePathWatcherDirectoryDoesNotChanged) QmlDesigner::SourceType::Directory, {path1SourceId, path2SourceId}}))); - updater.update(directories2, {}, projectPartId); + updater.update(directories2, {}); } TEST_F(ProjectStorageUpdater, UpdatePathWatcherDirectoryRemoved) @@ -1535,7 +1610,7 @@ TEST_F(ProjectStorageUpdater, UpdatePathWatcherDirectoryRemoved) updateIdPaths(Contains( IdPaths{projectPartId, QmlDesigner::SourceType::Directory, {path2SourceId}}))); - updater.update(directories2, {}, projectPartId); + updater.update(directories2, {}); } TEST_F(ProjectStorageUpdater, UpdatePathWatcherQmldirs) @@ -1545,7 +1620,7 @@ TEST_F(ProjectStorageUpdater, UpdatePathWatcherQmldirs) QmlDesigner::SourceType::QmlDir, {qmldir1SourceId, qmldir2SourceId, qmldir3SourceId}}))); - updater.update(directories3, {}, projectPartId); + updater.update(directories3, {}); } TEST_F(ProjectStorageUpdater, UpdatePathWatcherQmldirDoesNotExists) @@ -1557,7 +1632,7 @@ TEST_F(ProjectStorageUpdater, UpdatePathWatcherQmldirDoesNotExists) QmlDesigner::SourceType::QmlDir, {qmldir1SourceId, qmldir3SourceId}}))); - updater.update(directories3, {}, projectPartId); + updater.update(directories3, {}); } TEST_F(ProjectStorageUpdater, UpdatePathWatcherQmldirDoesNotChanged) @@ -1569,7 +1644,7 @@ TEST_F(ProjectStorageUpdater, UpdatePathWatcherQmldirDoesNotChanged) QmlDesigner::SourceType::QmlDir, {qmldir1SourceId, qmldir2SourceId}}))); - updater.update(directories2, {}, projectPartId); + updater.update(directories2, {}); } TEST_F(ProjectStorageUpdater, UpdatePathWatcherQmldirRemoved) @@ -1580,7 +1655,7 @@ TEST_F(ProjectStorageUpdater, UpdatePathWatcherQmldirRemoved) updateIdPaths(Contains( IdPaths{projectPartId, QmlDesigner::SourceType::QmlDir, {qmldir2SourceId}}))); - updater.update(directories2, {}, projectPartId); + updater.update(directories2, {}); } TEST_F(ProjectStorageUpdater, UpdatePathWatcherQmlFiles) @@ -1597,7 +1672,7 @@ TEST_F(ProjectStorageUpdater, UpdatePathWatcherQmlFiles) QmlDesigner::SourceType::Qml, {firstSourceId, secondSourceId, thirdSourceId}}))); - updater.update(directories2, {}, projectPartId); + updater.update(directories2, {}); } TEST_F(ProjectStorageUpdater, UpdatePathWatcherOnlyQmlFilesDontChanged) @@ -1615,7 +1690,7 @@ TEST_F(ProjectStorageUpdater, UpdatePathWatcherOnlyQmlFilesDontChanged) QmlDesigner::SourceType::Qml, {firstSourceId, secondSourceId, thirdSourceId}}))); - updater.update(directories2, {}, projectPartId); + updater.update(directories2, {}); } TEST_F(ProjectStorageUpdater, UpdatePathWatcherOnlyQmlFilesChanged) @@ -1633,7 +1708,7 @@ TEST_F(ProjectStorageUpdater, UpdatePathWatcherOnlyQmlFilesChanged) QmlDesigner::SourceType::Qml, {firstSourceId, secondSourceId, thirdSourceId}}))); - updater.update(directories2, {}, projectPartId); + updater.update(directories2, {}); } TEST_F(ProjectStorageUpdater, UpdatePathWatcherQmlFilesAndDirectoriesDontChanged) @@ -1656,7 +1731,7 @@ TEST_F(ProjectStorageUpdater, UpdatePathWatcherQmlFilesAndDirectoriesDontChanged QmlDesigner::SourceType::Qml, {firstSourceId, secondSourceId, thirdSourceId}}))); - updater.update(directories2, {}, projectPartId); + updater.update(directories2, {}); } TEST_F(ProjectStorageUpdater, UpdatePathWatcherQmltypesFilesInQmldir) @@ -1675,7 +1750,7 @@ TEST_F(ProjectStorageUpdater, UpdatePathWatcherQmltypesFilesInQmldir) QmlDesigner::SourceType::QmlTypes, {qmltypes1SourceId, qmltypes2SourceId}}))); - updater.update(directories2, {}, projectPartId); + updater.update(directories2, {}); } TEST_F(ProjectStorageUpdater, UpdatePathWatcherOnlyQmltypesFilesInQmldirDontChanged) @@ -1693,7 +1768,7 @@ TEST_F(ProjectStorageUpdater, UpdatePathWatcherOnlyQmltypesFilesInQmldirDontChan QmlDesigner::SourceType::QmlTypes, {qmltypes1SourceId, qmltypes2SourceId}}))); - updater.update(directories2, {}, projectPartId); + updater.update(directories2, {}); } TEST_F(ProjectStorageUpdater, UpdatePathWatcherOnlyQmltypesFilesChanged) @@ -1710,7 +1785,7 @@ TEST_F(ProjectStorageUpdater, UpdatePathWatcherOnlyQmltypesFilesChanged) QmlDesigner::SourceType::QmlTypes, {qmltypes1SourceId, qmltypes2SourceId}}))); - updater.update(directories2, {}, projectPartId); + updater.update(directories2, {}); } TEST_F(ProjectStorageUpdater, UpdatePathWatcherQmltypesFilesAndDirectoriesDontChanged) @@ -1731,7 +1806,7 @@ TEST_F(ProjectStorageUpdater, UpdatePathWatcherQmltypesFilesAndDirectoriesDontCh QmlDesigner::SourceType::QmlTypes, {qmltypes1SourceId, qmltypes2SourceId}}))); - updater.update(directories2, {}, projectPartId); + updater.update(directories2, {}); } TEST_F(ProjectStorageUpdater, UpdatePathWatcherBuiltinQmltypesFiles) @@ -1746,7 +1821,7 @@ TEST_F(ProjectStorageUpdater, UpdatePathWatcherBuiltinQmltypesFiles) QmlDesigner::SourceType::QmlTypes, {qmltypes1SourceId, qmltypes2SourceId}}))); - updater.update({}, {builtinQmltyplesPath1, builtinQmltyplesPath2}, projectPartId); + updater.update({}, {builtinQmltyplesPath1, builtinQmltyplesPath2}); } TEST_F(ProjectStorageUpdater, SynchronizeQmlDocumentsWithoutQmldir) @@ -1942,4 +2017,1427 @@ TEST_F(ProjectStorageUpdater, SynchronizeQmlDocumentsWithoutQmldirRemovesQmlDocu updater.update(directories, {}); } +TEST_F(ProjectStorageUpdater, WatcherUpdatesDirectories) +{ + 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); + setFilesChanged({directoryPathSourceId}); + setFilesDontChanged({qmlDirPathSourceId}); + + EXPECT_CALL( + projectStorageMock, + synchronize(AllOf( + Field(&SynchronizationPackage::imports, UnorderedElementsAre(import1, import2, import3)), + Field( + &SynchronizationPackage::types, + UnorderedElementsAre( + AllOf(IsStorageType("First.qml", + Storage::Synchronization::ImportedType{"Object"}, + TypeTraits::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"}, + TypeTraits::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"}, + TypeTraits::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::updatedFileStatusSourceIds, + UnorderedElementsAre(directoryPathSourceId, + qmlDocumentSourceId1, + qmlDocumentSourceId2, + qmlDocumentSourceId3)), + Field(&SynchronizationPackage::fileStatuses, + UnorderedElementsAre(IsFileStatus(directoryPathSourceId, 1, 21), + IsFileStatus(qmlDocumentSourceId1, 1, 21), + IsFileStatus(qmlDocumentSourceId2, 1, 21), + 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)))))); + + updater.pathsWithIdsChanged({{directoryProjectChunkId, {directoryPathSourceId}}}); +} + +TEST_F(ProjectStorageUpdater, WatcherUpdatesRemovedDirectory) +{ + setFilesRemoved({directoryPathSourceId, + qmlDirPathSourceId, + qmlDocumentSourceId1, + qmlDocumentSourceId2, + qmlDocumentSourceId3}); + setProjectDatas(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()), + Field(&SynchronizationPackage::types, IsEmpty()), + Field(&SynchronizationPackage::updatedSourceIds, + UnorderedElementsAre(qmlDirPathSourceId, + qmlDocumentSourceId1, + qmlDocumentSourceId2, + qmlDocumentSourceId3)), + Field(&SynchronizationPackage::updatedFileStatusSourceIds, + UnorderedElementsAre(directoryPathSourceId, + qmlDirPathSourceId, + qmlDocumentSourceId1, + qmlDocumentSourceId2, + qmlDocumentSourceId3)), + Field(&SynchronizationPackage::fileStatuses, UnorderedElementsAre()), + Field(&SynchronizationPackage::updatedProjectSourceIds, + UnorderedElementsAre(directoryPathSourceId)), + Field(&SynchronizationPackage::projectDatas, IsEmpty())))); + + updater.pathsWithIdsChanged({{directoryProjectChunkId, {directoryPathSourceId}}}); +} + +TEST_F(ProjectStorageUpdater, WatcherWatchesDirectoriesAfterDirectoryChanges) +{ + 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); + setFilesChanged({directoryPathSourceId}); + setFilesDontChanged({qmlDirPathSourceId}); + auto directorySourceContextId = sourcePathCache.sourceContextId(directoryPathSourceId); + + EXPECT_CALL(patchWatcherMock, + updateContextIdPaths( + UnorderedElementsAre( + IdPaths{projectPartId, QmlDesigner::SourceType::Directory, {directoryPathSourceId}}, + IdPaths{projectPartId, QmlDesigner::SourceType::QmlDir, {qmlDirPathSourceId}}, + IdPaths{projectPartId, + QmlDesigner::SourceType::Qml, + {qmlDocumentSourceId1, qmlDocumentSourceId2, qmlDocumentSourceId3}}, + IdPaths{projectPartId, QmlDesigner::SourceType::QmlTypes, {}}), + UnorderedElementsAre(directorySourceContextId))); + + updater.pathsWithIdsChanged({{directoryProjectChunkId, {directoryPathSourceId}}}); +} + +TEST_F(ProjectStorageUpdater, WatcherDontUpdatesDirectoriesForOtherProject) +{ + 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); + + EXPECT_CALL(projectStorageMock, synchronize(PackageIsEmpty())); + + updater.pathsWithIdsChanged({{otherDirectoryProjectChunkId, {directoryPathSourceId}}}); +} + +TEST_F(ProjectStorageUpdater, WatcherUpdatesDirectoriesAndQmldir) +{ + 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); + setFilesChanged({directoryPathSourceId, qmlDirPathSourceId}); + + EXPECT_CALL( + projectStorageMock, + synchronize(AllOf( + Field(&SynchronizationPackage::imports, UnorderedElementsAre(import1, import2, import3)), + Field( + &SynchronizationPackage::types, + UnorderedElementsAre( + AllOf(IsStorageType("First.qml", + Storage::Synchronization::ImportedType{"Object"}, + TypeTraits::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"}, + TypeTraits::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"}, + TypeTraits::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(qmlDirPathSourceId, + qmlDocumentSourceId1, + qmlDocumentSourceId2, + qmlDocumentSourceId3)), + Field(&SynchronizationPackage::updatedFileStatusSourceIds, + UnorderedElementsAre(directoryPathSourceId, + qmlDirPathSourceId, + qmlDocumentSourceId1, + qmlDocumentSourceId2, + qmlDocumentSourceId3)), + Field(&SynchronizationPackage::fileStatuses, + UnorderedElementsAre(IsFileStatus(directoryPathSourceId, 1, 21), + IsFileStatus(qmlDirPathSourceId, 1, 21), + IsFileStatus(qmlDocumentSourceId1, 1, 21), + IsFileStatus(qmlDocumentSourceId2, 1, 21), + 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)))))); + + updater.pathsWithIdsChanged({{directoryProjectChunkId, {directoryPathSourceId}}, + {qmldirProjectChunkId, {qmlDirPathSourceId}}}); +} + +TEST_F(ProjectStorageUpdater, WatcherWatchesDirectoriesAfterQmldirChanges) +{ + 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); + auto directorySourceContextId = sourcePathCache.sourceContextId(qmlDirPathSourceId); + + EXPECT_CALL(patchWatcherMock, + updateContextIdPaths( + UnorderedElementsAre( + IdPaths{projectPartId, QmlDesigner::SourceType::Directory, {directoryPathSourceId}}, + IdPaths{projectPartId, QmlDesigner::SourceType::QmlDir, {qmlDirPathSourceId}}, + IdPaths{projectPartId, + QmlDesigner::SourceType::Qml, + {qmlDocumentSourceId1, qmlDocumentSourceId2, qmlDocumentSourceId3}}, + IdPaths{projectPartId, QmlDesigner::SourceType::QmlTypes, {}}), + UnorderedElementsAre(directorySourceContextId))); + + updater.pathsWithIdsChanged({{qmldirProjectChunkId, {qmlDirPathSourceId}}}); +} + +TEST_F(ProjectStorageUpdater, WatcherDontUpdatesQmldirForOtherProject) +{ + 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); + + EXPECT_CALL(projectStorageMock, synchronize(PackageIsEmpty())); + + updater.pathsWithIdsChanged({{otherQmldirProjectChunkId, {qmlDirPathSourceId}}}); +} + +TEST_F(ProjectStorageUpdater, WatcherUpdatesAddOnlyQmlDocumentInDirectory) +{ + QString qmldir{R"(module Example + FirstType 1.0 First.qml)"}; + setContent(u"/path/qmldir", qmldir); + setFilesChanged({directoryPathSourceId}); + setFilesDontChanged({qmlDirPathSourceId, qmlDocumentSourceId1}); + setFilesAdded({qmlDocumentSourceId2}); + setProjectDatas(directoryPathSourceId, + {{directoryPathSourceId, qmlDocumentSourceId1, ModuleId{}, FileType::QmlDocument}}); + setQmlFileNames(u"/path", {"First.qml", "First2.qml"}); + + EXPECT_CALL(projectStorageMock, + synchronize(AllOf( + Field(&SynchronizationPackage::imports, UnorderedElementsAre(import2)), + Field(&SynchronizationPackage::types, + UnorderedElementsAre( + AllOf(IsStorageType("First.qml", + Storage::Synchronization::ImportedType{}, + TypeTraits::Reference, + qmlDocumentSourceId1, + Storage::Synchronization::ChangeLevel::Minimal), + Field(&Storage::Synchronization::Type::exportedTypes, + UnorderedElementsAre( + IsExportedType(exampleModuleId, "FirstType", 1, 0), + IsExportedType(pathModuleId, "First", -1, -1)))), + AllOf(IsStorageType("First2.qml", + Storage::Synchronization::ImportedType{"Object2"}, + TypeTraits::Reference, + qmlDocumentSourceId2, + Storage::Synchronization::ChangeLevel::Full), + Field(&Storage::Synchronization::Type::exportedTypes, + UnorderedElementsAre( + IsExportedType(pathModuleId, "First2", -1, -1)))))), + Field(&SynchronizationPackage::updatedSourceIds, + UnorderedElementsAre(qmlDocumentSourceId1, qmlDocumentSourceId2)), + Field(&SynchronizationPackage::updatedFileStatusSourceIds, + UnorderedElementsAre(directoryPathSourceId, qmlDocumentSourceId2)), + Field(&SynchronizationPackage::fileStatuses, + UnorderedElementsAre(IsFileStatus(qmlDocumentSourceId2, 1, 21), + 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)))))); + + updater.pathsWithIdsChanged({{directoryProjectChunkId, {directoryPathSourceId}}}); +} + +TEST_F(ProjectStorageUpdater, WatcherUpdatesRemovesQmlDocument) +{ + QString qmldir{R"(module Example + FirstType 1.0 First.qml + FirstType 2.2 First2.qml + )"}; + setContent(u"/path/qmldir", qmldir); + setFilesChanged({qmlDirPathSourceId}); + setFilesDontChanged({qmlDocumentSourceId1, qmlDocumentSourceId2}); + setFilesRemoved({qmlDocumentSourceId3}); + setProjectDatas(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, + synchronize(AllOf( + Field(&SynchronizationPackage::imports, IsEmpty()), + Field(&SynchronizationPackage::types, + UnorderedElementsAre( + AllOf(IsStorageType("First.qml", + Storage::Synchronization::ImportedType{}, + TypeTraits::Reference, + qmlDocumentSourceId1, + Storage::Synchronization::ChangeLevel::Minimal), + Field(&Storage::Synchronization::Type::exportedTypes, + UnorderedElementsAre( + IsExportedType(exampleModuleId, "FirstType", 1, 0), + IsExportedType(pathModuleId, "First", -1, -1)))), + AllOf(IsStorageType("First2.qml", + Storage::Synchronization::ImportedType{}, + TypeTraits::Reference, + qmlDocumentSourceId2, + Storage::Synchronization::ChangeLevel::Minimal), + Field(&Storage::Synchronization::Type::exportedTypes, + UnorderedElementsAre( + IsExportedType(exampleModuleId, "FirstType", 2, 2), + IsExportedType(pathModuleId, "First2", -1, -1)))))), + Field(&SynchronizationPackage::updatedSourceIds, + UnorderedElementsAre(qmlDirPathSourceId, + qmlDocumentSourceId1, + qmlDocumentSourceId2, + qmlDocumentSourceId3)), + Field(&SynchronizationPackage::updatedFileStatusSourceIds, + UnorderedElementsAre(qmlDirPathSourceId, qmlDocumentSourceId3)), + Field(&SynchronizationPackage::fileStatuses, + 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)))))); + + updater.pathsWithIdsChanged({{directoryProjectChunkId, {directoryPathSourceId}}}); +} + +TEST_F(ProjectStorageUpdater, WatcherUpdatesRemovesQmlDocumentInQmldirOnly) +{ + QString qmldir{R"(module Example + FirstType 1.0 First.qml + )"}; + setContent(u"/path/qmldir", qmldir); + setFilesChanged({qmlDirPathSourceId}); + setFilesDontChanged({qmlDocumentSourceId1, qmlDocumentSourceId2}); + setProjectDatas(directoryPathSourceId, + {{directoryPathSourceId, qmlDocumentSourceId1, ModuleId{}, FileType::QmlDocument}, + {directoryPathSourceId, qmlDocumentSourceId2, ModuleId{}, FileType::QmlDocument}}); + setQmlFileNames(u"/path", {"First.qml", "First2.qml"}); + + EXPECT_CALL( + projectStorageMock, + synchronize(AllOf( + Field(&SynchronizationPackage::imports, IsEmpty()), + Field(&SynchronizationPackage::types, + UnorderedElementsAre( + AllOf(IsStorageType("First.qml", + Storage::Synchronization::ImportedType{}, + TypeTraits::Reference, + qmlDocumentSourceId1, + Storage::Synchronization::ChangeLevel::Minimal), + Field(&Storage::Synchronization::Type::exportedTypes, + UnorderedElementsAre(IsExportedType(exampleModuleId, "FirstType", 1, 0), + IsExportedType(pathModuleId, "First", -1, -1)))), + AllOf(IsStorageType("First2.qml", + Storage::Synchronization::ImportedType{}, + TypeTraits::Reference, + qmlDocumentSourceId2, + Storage::Synchronization::ChangeLevel::Minimal), + Field(&Storage::Synchronization::Type::exportedTypes, + UnorderedElementsAre(IsExportedType(pathModuleId, "First2", -1, -1)))))), + Field(&SynchronizationPackage::updatedSourceIds, + UnorderedElementsAre(qmlDirPathSourceId, qmlDocumentSourceId1, qmlDocumentSourceId2)), + Field(&SynchronizationPackage::updatedFileStatusSourceIds, + UnorderedElementsAre(qmlDirPathSourceId)), + Field(&SynchronizationPackage::fileStatuses, + 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)))))); + + updater.pathsWithIdsChanged({{qmldirProjectChunkId, {qmlDirPathSourceId}}}); +} + +TEST_F(ProjectStorageUpdater, WatcherUpdatesDirectoriesAddQmlDocumentToQmldir) +{ + QString qmldir{R"(module Example + FirstType 1.0 First.qml + FirstType 2.2 First2.qml + )"}; + setContent(u"/path/qmldir", qmldir); + setFilesChanged({qmlDirPathSourceId}); + setFilesDontChanged({qmlDocumentSourceId1, qmlDocumentSourceId2}); + setProjectDatas(directoryPathSourceId, + {{directoryPathSourceId, qmlDocumentSourceId1, ModuleId{}, FileType::QmlDocument}, + {directoryPathSourceId, qmlDocumentSourceId2, ModuleId{}, FileType::QmlDocument}}); + setQmlFileNames(u"/path", {"First.qml", "First2.qml"}); + + EXPECT_CALL( + projectStorageMock, + synchronize(AllOf( + Field(&SynchronizationPackage::imports, IsEmpty()), + Field( + &SynchronizationPackage::types, + UnorderedElementsAre( + AllOf(IsStorageType("First.qml", + Storage::Synchronization::ImportedType{}, + TypeTraits::Reference, + qmlDocumentSourceId1, + Storage::Synchronization::ChangeLevel::Minimal), + Field(&Storage::Synchronization::Type::exportedTypes, + UnorderedElementsAre(IsExportedType(exampleModuleId, "FirstType", 1, 0), + IsExportedType(pathModuleId, "First", -1, -1)))), + AllOf(IsStorageType("First2.qml", + Storage::Synchronization::ImportedType{}, + TypeTraits::Reference, + qmlDocumentSourceId2, + Storage::Synchronization::ChangeLevel::Minimal), + Field(&Storage::Synchronization::Type::exportedTypes, + UnorderedElementsAre(IsExportedType(exampleModuleId, "FirstType", 2, 2), + IsExportedType(pathModuleId, "First2", -1, -1)))))), + Field(&SynchronizationPackage::updatedSourceIds, + UnorderedElementsAre(qmlDirPathSourceId, qmlDocumentSourceId1, qmlDocumentSourceId2)), + Field(&SynchronizationPackage::updatedFileStatusSourceIds, + UnorderedElementsAre(qmlDirPathSourceId)), + Field(&SynchronizationPackage::fileStatuses, + 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)))))); + + updater.pathsWithIdsChanged({{qmldirProjectChunkId, {qmlDirPathSourceId}}}); +} + +TEST_F(ProjectStorageUpdater, WatcherUpdatesDirectoriesRemoveQmlDocumentFromQmldir) +{ + QString qmldir{R"(module Example + FirstType 1.0 First.qml + )"}; + setContent(u"/path/qmldir", qmldir); + setFilesDontChanged({qmlDocumentSourceId1, qmlDocumentSourceId2}); + setFilesChanged({qmlDirPathSourceId}); + setProjectDatas(directoryPathSourceId, + {{directoryPathSourceId, qmlDocumentSourceId1, ModuleId{}, FileType::QmlDocument}, + {directoryPathSourceId, qmlDocumentSourceId2, ModuleId{}, FileType::QmlDocument}}); + setQmlFileNames(u"/path", {"First.qml", "First2.qml"}); + + EXPECT_CALL( + projectStorageMock, + synchronize(AllOf( + Field(&SynchronizationPackage::imports, IsEmpty()), + Field(&SynchronizationPackage::types, + UnorderedElementsAre( + AllOf(IsStorageType("First.qml", + Storage::Synchronization::ImportedType{}, + TypeTraits::Reference, + qmlDocumentSourceId1, + Storage::Synchronization::ChangeLevel::Minimal), + Field(&Storage::Synchronization::Type::exportedTypes, + UnorderedElementsAre(IsExportedType(exampleModuleId, "FirstType", 1, 0), + IsExportedType(pathModuleId, "First", -1, -1)))), + AllOf(IsStorageType("First2.qml", + Storage::Synchronization::ImportedType{}, + TypeTraits::Reference, + qmlDocumentSourceId2, + Storage::Synchronization::ChangeLevel::Minimal), + Field(&Storage::Synchronization::Type::exportedTypes, + UnorderedElementsAre(IsExportedType(pathModuleId, "First2", -1, -1)))))), + Field(&SynchronizationPackage::updatedSourceIds, + UnorderedElementsAre(qmlDirPathSourceId, qmlDocumentSourceId1, qmlDocumentSourceId2)), + Field(&SynchronizationPackage::updatedFileStatusSourceIds, + UnorderedElementsAre(qmlDirPathSourceId)), + Field(&SynchronizationPackage::fileStatuses, + 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)))))); + + updater.pathsWithIdsChanged({{qmldirProjectChunkId, {qmlDirPathSourceId}}}); +} + +TEST_F(ProjectStorageUpdater, WatcherUpdatesDirectoriesDontUpdateQmlDocumentsIfUpToDate) +{ + 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); + setFilesDontChanged({qmlDocumentSourceId3}); + + EXPECT_CALL( + projectStorageMock, + synchronize(AllOf( + Field(&SynchronizationPackage::imports, UnorderedElementsAre(import1, import2)), + Field( + &SynchronizationPackage::types, + UnorderedElementsAre( + AllOf(IsStorageType("First.qml", + Storage::Synchronization::ImportedType{"Object"}, + TypeTraits::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"}, + TypeTraits::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{}, + TypeTraits::Reference, + qmlDocumentSourceId3, + Storage::Synchronization::ChangeLevel::Minimal), + Field(&Storage::Synchronization::Type::exportedTypes, + UnorderedElementsAre(IsExportedType(exampleModuleId, "SecondType", 2, 2), + IsExportedType(pathModuleId, "Second", -1, -1)))))), + Field(&SynchronizationPackage::updatedSourceIds, + UnorderedElementsAre(qmlDirPathSourceId, + qmlDocumentSourceId1, + qmlDocumentSourceId2, + qmlDocumentSourceId3)), + Field(&SynchronizationPackage::fileStatuses, + UnorderedElementsAre(IsFileStatus(qmlDirPathSourceId, 1, 21), + IsFileStatus(qmlDocumentSourceId1, 1, 21), + IsFileStatus(qmlDocumentSourceId2, 1, 21))), + Field(&SynchronizationPackage::updatedFileStatusSourceIds, + 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)))))); + + updater.pathsWithIdsChanged({{directoryProjectChunkId, {directoryPathSourceId}}}); +} + +TEST_F(ProjectStorageUpdater, WatcherUpdatesQmldirsDontUpdateQmlDocumentsIfUpToDate) +{ + 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); + setFilesDontChanged({qmlDocumentSourceId3}); + + EXPECT_CALL( + projectStorageMock, + synchronize(AllOf( + Field(&SynchronizationPackage::imports, UnorderedElementsAre(import1, import2)), + Field( + &SynchronizationPackage::types, + UnorderedElementsAre( + AllOf(IsStorageType("First.qml", + Storage::Synchronization::ImportedType{"Object"}, + TypeTraits::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"}, + TypeTraits::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{}, + TypeTraits::Reference, + qmlDocumentSourceId3, + Storage::Synchronization::ChangeLevel::Minimal), + Field(&Storage::Synchronization::Type::exportedTypes, + UnorderedElementsAre(IsExportedType(exampleModuleId, "SecondType", 2, 2), + IsExportedType(pathModuleId, "Second", -1, -1)))))), + Field(&SynchronizationPackage::updatedSourceIds, + UnorderedElementsAre(qmlDirPathSourceId, + qmlDocumentSourceId1, + qmlDocumentSourceId2, + qmlDocumentSourceId3)), + Field(&SynchronizationPackage::fileStatuses, + UnorderedElementsAre(IsFileStatus(qmlDirPathSourceId, 1, 21), + IsFileStatus(qmlDocumentSourceId1, 1, 21), + IsFileStatus(qmlDocumentSourceId2, 1, 21))), + Field(&SynchronizationPackage::updatedFileStatusSourceIds, + 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)))))); + + updater.pathsWithIdsChanged({{qmldirProjectChunkId, {qmlDirPathSourceId}}}); +} + +TEST_F(ProjectStorageUpdater, WatcherUpdatesDirectoryButNotQmldir) +{ + setProjectDatas( + directoryPathSourceId, + {{directoryPathSourceId, qmltypesPathSourceId, exampleModuleId, FileType::QmlTypes}, + {directoryPathSourceId, qmltypes2PathSourceId, exampleModuleId, FileType::QmlTypes}, + {directoryPathSourceId, qmlDocumentSourceId1, exampleModuleId, FileType::QmlDocument}, + {directoryPathSourceId, qmlDocumentSourceId2, exampleModuleId, FileType::QmlDocument}}); + setFilesDontChanged({qmlDirPathSourceId}); + + EXPECT_CALL( + projectStorageMock, + synchronize(AllOf( + Field(&SynchronizationPackage::imports, + UnorderedElementsAre(import1, import2, import4, import5)), + Field(&SynchronizationPackage::types, + UnorderedElementsAre( + Eq(objectType), + Eq(itemType), + AllOf(IsStorageType("First.qml", + Storage::Synchronization::ImportedType{"Object"}, + TypeTraits::Reference, + qmlDocumentSourceId1, + Storage::Synchronization::ChangeLevel::ExcludeExportedTypes), + Field(&Storage::Synchronization::Type::exportedTypes, IsEmpty())), + AllOf(IsStorageType("First2.qml", + Storage::Synchronization::ImportedType{"Object2"}, + TypeTraits::Reference, + qmlDocumentSourceId2, + Storage::Synchronization::ChangeLevel::ExcludeExportedTypes), + Field(&Storage::Synchronization::Type::exportedTypes, IsEmpty())))), + Field(&SynchronizationPackage::updatedSourceIds, + UnorderedElementsAre(qmltypesPathSourceId, + qmltypes2PathSourceId, + qmlDocumentSourceId1, + qmlDocumentSourceId2)), + Field(&SynchronizationPackage::fileStatuses, + UnorderedElementsAre(IsFileStatus(qmltypesPathSourceId, 1, 21), + IsFileStatus(qmltypes2PathSourceId, 1, 21), + IsFileStatus(qmlDocumentSourceId1, 1, 21), + IsFileStatus(qmlDocumentSourceId2, 1, 21))), + Field(&SynchronizationPackage::updatedFileStatusSourceIds, + UnorderedElementsAre(qmltypesPathSourceId, + qmltypes2PathSourceId, + qmlDocumentSourceId1, + qmlDocumentSourceId2)), + Field(&SynchronizationPackage::projectDatas, IsEmpty())))); + + updater.pathsWithIdsChanged({{directoryProjectChunkId, {directoryPathSourceId}}}); +} + +TEST_F(ProjectStorageUpdater, WatcherUpdatesQmlDocuments) +{ + EXPECT_CALL( + projectStorageMock, + synchronize(AllOf( + Field(&SynchronizationPackage::imports, UnorderedElementsAre(import1, import2)), + Field(&SynchronizationPackage::types, + UnorderedElementsAre( + AllOf(IsStorageType("First.qml", + Storage::Synchronization::ImportedType{"Object"}, + TypeTraits::Reference, + qmlDocumentSourceId1, + Storage::Synchronization::ChangeLevel::ExcludeExportedTypes), + Field(&Storage::Synchronization::Type::exportedTypes, IsEmpty())), + AllOf(IsStorageType("First2.qml", + Storage::Synchronization::ImportedType{"Object2"}, + TypeTraits::Reference, + qmlDocumentSourceId2, + Storage::Synchronization::ChangeLevel::ExcludeExportedTypes), + Field(&Storage::Synchronization::Type::exportedTypes, IsEmpty())))), + Field(&SynchronizationPackage::updatedSourceIds, + UnorderedElementsAre(qmlDocumentSourceId1, qmlDocumentSourceId2)), + Field(&SynchronizationPackage::fileStatuses, + UnorderedElementsAre(IsFileStatus(qmlDocumentSourceId1, 1, 21), + IsFileStatus(qmlDocumentSourceId2, 1, 21))), + Field(&SynchronizationPackage::updatedFileStatusSourceIds, + UnorderedElementsAre(qmlDocumentSourceId1, qmlDocumentSourceId2)), + Field(&SynchronizationPackage::projectDatas, IsEmpty())))); + + updater.pathsWithIdsChanged( + {{qmlDocumentProjectChunkId, {qmlDocumentSourceId1, qmlDocumentSourceId2}}}); +} + +TEST_F(ProjectStorageUpdater, WatcherUpdatesRemovedQmlDocuments) +{ + setFilesRemoved({qmlDocumentSourceId2}); + + EXPECT_CALL(projectStorageMock, + synchronize(AllOf( + Field(&SynchronizationPackage::imports, UnorderedElementsAre(import1)), + Field(&SynchronizationPackage::types, + UnorderedElementsAre(AllOf( + IsStorageType("First.qml", + Storage::Synchronization::ImportedType{"Object"}, + TypeTraits::Reference, + qmlDocumentSourceId1, + Storage::Synchronization::ChangeLevel::ExcludeExportedTypes), + Field(&Storage::Synchronization::Type::exportedTypes, IsEmpty())))), + Field(&SynchronizationPackage::updatedSourceIds, + UnorderedElementsAre(qmlDocumentSourceId1, qmlDocumentSourceId2)), + Field(&SynchronizationPackage::fileStatuses, + UnorderedElementsAre(IsFileStatus(qmlDocumentSourceId1, 1, 21))), + Field(&SynchronizationPackage::updatedFileStatusSourceIds, + UnorderedElementsAre(qmlDocumentSourceId1, qmlDocumentSourceId2)), + Field(&SynchronizationPackage::projectDatas, IsEmpty())))); + + updater.pathsWithIdsChanged( + {{qmlDocumentProjectChunkId, {qmlDocumentSourceId1, qmlDocumentSourceId2}}}); +} + +TEST_F(ProjectStorageUpdater, WatcherDontWatchesDirectoriesAfterQmlDocumentChanges) +{ + EXPECT_CALL(patchWatcherMock, updateContextIdPaths(_, _)).Times(0); + + updater.pathsWithIdsChanged({{qmlDocumentProjectChunkId, {qmlDirPathSourceId}}}); +} + +TEST_F(ProjectStorageUpdater, WatcherDontUpdatesQmlDocumentsForOtherProjects) +{ + EXPECT_CALL(projectStorageMock, synchronize(PackageIsEmpty())); + + updater.pathsWithIdsChanged( + {{otherQmlDocumentProjectChunkId, {qmlDocumentSourceId1, qmlDocumentSourceId2}}}); +} + +TEST_F(ProjectStorageUpdater, WatcherUpdatesQmltypes) +{ + setProjectDatas( + directoryPathSourceId, + {{directoryPathSourceId, qmltypesPathSourceId, exampleModuleId, FileType::QmlTypes}, + {directoryPathSourceId, qmltypes2PathSourceId, exampleModuleId, FileType::QmlTypes}}); + setFilesDontChanged( + {directoryPathSourceId, qmlDirPathSourceId, qmlDocumentSourceId1, qmlDocumentSourceId2}); + + EXPECT_CALL(projectStorageMock, + synchronize( + AllOf(Field(&SynchronizationPackage::imports, + UnorderedElementsAre(import4, import5)), + Field(&SynchronizationPackage::types, + UnorderedElementsAre(Eq(objectType), Eq(itemType))), + Field(&SynchronizationPackage::updatedSourceIds, + UnorderedElementsAre(qmltypesPathSourceId, qmltypes2PathSourceId)), + Field(&SynchronizationPackage::fileStatuses, + UnorderedElementsAre(IsFileStatus(qmltypesPathSourceId, 1, 21), + IsFileStatus(qmltypes2PathSourceId, 1, 21))), + Field(&SynchronizationPackage::updatedFileStatusSourceIds, + UnorderedElementsAre(qmltypesPathSourceId, qmltypes2PathSourceId)), + Field(&SynchronizationPackage::projectDatas, IsEmpty())))); + + updater.pathsWithIdsChanged( + {{qmltypesProjectChunkId, {qmltypesPathSourceId, qmltypes2PathSourceId}}}); +} + +TEST_F(ProjectStorageUpdater, WatcherUpdatesRemovedQmltypesWithoutUpdatedQmldir) +{ + setProjectDatas( + directoryPathSourceId, + {{directoryPathSourceId, qmltypesPathSourceId, exampleModuleId, FileType::QmlTypes}, + {directoryPathSourceId, qmltypes2PathSourceId, exampleModuleId, FileType::QmlTypes}}); + setFilesDontChanged( + {directoryPathSourceId, qmlDirPathSourceId, qmlDocumentSourceId1, qmlDocumentSourceId2}); + setFilesRemoved({qmltypesPathSourceId}); + + EXPECT_CALL(projectStorageMock, synchronize(_)).Times(0); + + updater.pathsWithIdsChanged( + {{qmltypesProjectChunkId, {qmltypesPathSourceId, qmltypes2PathSourceId}}}); +} + +TEST_F(ProjectStorageUpdater, WatcherUpdatesRemovedQmltypesWithUpdatedQmldir) +{ + setProjectDatas( + directoryPathSourceId, + {{directoryPathSourceId, qmltypesPathSourceId, exampleModuleId, FileType::QmlTypes}, + {directoryPathSourceId, qmltypes2PathSourceId, exampleModuleId, FileType::QmlTypes}}); + setFilesDontChanged( + {directoryPathSourceId, qmlDirPathSourceId, qmlDocumentSourceId1, qmlDocumentSourceId2}); + setFilesRemoved({qmltypesPathSourceId}); + updater.pathsWithIdsChanged( + {{qmltypesProjectChunkId, {qmltypesPathSourceId, qmltypes2PathSourceId}}}); + QString qmldir{R"(module Example + typeinfo example2.qmltypes)"}; + setContent(u"/path/qmldir", qmldir); + setFilesChanged({qmlDirPathSourceId}); + setQmlFileNames(u"/path", {}); + + EXPECT_CALL(projectStorageMock, + synchronize( + AllOf(Field(&SynchronizationPackage::imports, UnorderedElementsAre(import5)), + Field(&SynchronizationPackage::types, UnorderedElementsAre(Eq(itemType))), + Field(&SynchronizationPackage::updatedSourceIds, + UnorderedElementsAre(qmlDirPathSourceId, + qmltypesPathSourceId, + qmltypes2PathSourceId)), + Field(&SynchronizationPackage::fileStatuses, + UnorderedElementsAre(IsFileStatus(qmlDirPathSourceId, 1, 21), + IsFileStatus(qmltypes2PathSourceId, 1, 21))), + Field(&SynchronizationPackage::updatedFileStatusSourceIds, + UnorderedElementsAre(qmlDirPathSourceId, + qmltypesPathSourceId, + qmltypes2PathSourceId)), + Field(&SynchronizationPackage::projectDatas, + UnorderedElementsAre(IsProjectData(directoryPathSourceId, + qmltypes2PathSourceId, + exampleCppNativeModuleId, + FileType::QmlTypes)))))); + + updater.pathsWithIdsChanged({{qmldirProjectChunkId, {qmlDirPathSourceId}}}); +} + +TEST_F(ProjectStorageUpdater, WatcherDontWatchesDirectoriesAfterQmltypesChanges) +{ + setProjectDatas( + directoryPathSourceId, + {{directoryPathSourceId, qmltypesPathSourceId, exampleModuleId, FileType::QmlTypes}, + {directoryPathSourceId, qmltypes2PathSourceId, exampleModuleId, FileType::QmlTypes}}); + setFilesDontChanged( + {directoryPathSourceId, qmlDirPathSourceId, qmlDocumentSourceId1, qmlDocumentSourceId2}); + + EXPECT_CALL(patchWatcherMock, updateContextIdPaths(_, _)).Times(0); + + updater.pathsWithIdsChanged( + {{qmltypesProjectChunkId, {qmltypesPathSourceId, qmltypes2PathSourceId}}}); +} + +TEST_F(ProjectStorageUpdater, WatcherDontUpdatesQmltypesForOtherProjects) +{ + setProjectDatas( + directoryPathSourceId, + {{directoryPathSourceId, qmltypesPathSourceId, exampleModuleId, FileType::QmlTypes}, + {directoryPathSourceId, qmltypes2PathSourceId, exampleModuleId, FileType::QmlTypes}}); + setFilesDontChanged( + {directoryPathSourceId, qmlDirPathSourceId, qmlDocumentSourceId1, qmlDocumentSourceId2}); + + EXPECT_CALL(projectStorageMock, synchronize(PackageIsEmpty())); + + updater.pathsWithIdsChanged( + {{otherQmltypesProjectChunkId, {qmltypesPathSourceId, qmltypes2PathSourceId}}}); +} + +TEST_F(ProjectStorageUpdater, WatcherUpdatesDirectoriesAndButNotIncludedQmlDocument) +{ + 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); + setFilesChanged({directoryPathSourceId}); + setFilesDontChanged({qmlDirPathSourceId}); + + EXPECT_CALL( + projectStorageMock, + synchronize(AllOf( + Field(&SynchronizationPackage::imports, UnorderedElementsAre(import1, import2, import3)), + Field( + &SynchronizationPackage::types, + UnorderedElementsAre( + AllOf(IsStorageType("First.qml", + Storage::Synchronization::ImportedType{"Object"}, + TypeTraits::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"}, + TypeTraits::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"}, + TypeTraits::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::updatedFileStatusSourceIds, + UnorderedElementsAre(directoryPathSourceId, + qmlDocumentSourceId1, + qmlDocumentSourceId2, + qmlDocumentSourceId3)), + Field(&SynchronizationPackage::fileStatuses, + UnorderedElementsAre(IsFileStatus(directoryPathSourceId, 1, 21), + IsFileStatus(qmlDocumentSourceId1, 1, 21), + IsFileStatus(qmlDocumentSourceId2, 1, 21), + 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)))))); + + updater.pathsWithIdsChanged({{directoryProjectChunkId, {directoryPathSourceId}}, + {qmlDocumentProjectChunkId, + {qmlDocumentSourceId1, qmlDocumentSourceId2, qmlDocumentSourceId3}}}); +} + +TEST_F(ProjectStorageUpdater, WatcherUpdatesQmldirAndButNotIncludedQmlDocument) +{ + 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); + setFilesDontChanged({directoryPathSourceId}); + setFilesChanged({qmlDirPathSourceId}); + + EXPECT_CALL( + projectStorageMock, + synchronize(AllOf( + Field(&SynchronizationPackage::imports, UnorderedElementsAre(import1, import2, import3)), + Field( + &SynchronizationPackage::types, + UnorderedElementsAre( + AllOf(IsStorageType("First.qml", + Storage::Synchronization::ImportedType{"Object"}, + TypeTraits::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"}, + TypeTraits::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"}, + TypeTraits::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(qmlDirPathSourceId, + qmlDocumentSourceId1, + qmlDocumentSourceId2, + qmlDocumentSourceId3)), + Field(&SynchronizationPackage::updatedFileStatusSourceIds, + UnorderedElementsAre(qmlDirPathSourceId, + qmlDocumentSourceId1, + qmlDocumentSourceId2, + qmlDocumentSourceId3)), + Field(&SynchronizationPackage::fileStatuses, + UnorderedElementsAre(IsFileStatus(qmlDirPathSourceId, 1, 21), + IsFileStatus(qmlDocumentSourceId1, 1, 21), + IsFileStatus(qmlDocumentSourceId2, 1, 21), + 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)))))); + + updater.pathsWithIdsChanged({{qmldirProjectChunkId, {qmlDirPathSourceId}}, + {qmlDocumentProjectChunkId, + {qmlDocumentSourceId1, qmlDocumentSourceId2, qmlDocumentSourceId3}}}); +} + +TEST_F(ProjectStorageUpdater, WatcherUpdatesQmldirAndButNotIncludedQmltypes) +{ + setProjectDatas( + directoryPathSourceId, + {{directoryPathSourceId, qmltypesPathSourceId, exampleModuleId, FileType::QmlTypes}, + {directoryPathSourceId, qmltypes2PathSourceId, exampleModuleId, FileType::QmlTypes}, + {directoryPathSourceId, qmlDocumentSourceId1, exampleModuleId, FileType::QmlDocument}, + {directoryPathSourceId, qmlDocumentSourceId2, exampleModuleId, FileType::QmlDocument}}); + QString qmldir{R"(module Example + FirstType 1.0 First.qml + FirstType 2.2 First2.qml + SecondType 2.2 Second.qml + typeinfo example.qmltypes + typeinfo example2.qmltypes)"}; + setContent(u"/path/qmldir", qmldir); + setFilesDontChanged({directoryPathSourceId}); + setFilesChanged({qmlDirPathSourceId, + qmltypesPathSourceId, + qmltypes2PathSourceId, + qmlDocumentSourceId1, + qmlDocumentSourceId2, + qmlDocumentSourceId3}); + + EXPECT_CALL( + projectStorageMock, + synchronize(AllOf( + Field(&SynchronizationPackage::imports, + UnorderedElementsAre(import1, import2, import3, import4, import5)), + Field( + &SynchronizationPackage::types, + UnorderedElementsAre( + Eq(objectType), + Eq(itemType), + AllOf(IsStorageType("First.qml", + Storage::Synchronization::ImportedType{"Object"}, + TypeTraits::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"}, + TypeTraits::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"}, + TypeTraits::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(qmlDirPathSourceId, + qmltypesPathSourceId, + qmltypes2PathSourceId, + qmlDocumentSourceId1, + qmlDocumentSourceId2, + qmlDocumentSourceId3)), + Field(&SynchronizationPackage::updatedFileStatusSourceIds, + UnorderedElementsAre(qmlDirPathSourceId, + qmltypesPathSourceId, + qmltypes2PathSourceId, + qmlDocumentSourceId1, + qmlDocumentSourceId2, + qmlDocumentSourceId3)), + Field(&SynchronizationPackage::fileStatuses, + UnorderedElementsAre(IsFileStatus(qmlDirPathSourceId, 1, 21), + IsFileStatus(qmltypesPathSourceId, 1, 21), + IsFileStatus(qmltypes2PathSourceId, 1, 21), + IsFileStatus(qmlDocumentSourceId1, 1, 21), + IsFileStatus(qmlDocumentSourceId2, 1, 21), + 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)))))); + + updater.pathsWithIdsChanged( + {{qmldirProjectChunkId, {qmlDirPathSourceId}}, + {qmltypesProjectChunkId, {qmltypesPathSourceId, qmltypes2PathSourceId}}}); +} + +TEST_F(ProjectStorageUpdater, ErrorsForWatcherUpdatesAreHandled) +{ + 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); + + ON_CALL(projectStorageMock, synchronize(_)).WillByDefault(Throw(QmlDesigner::ProjectStorageError{})); + + ASSERT_NO_THROW(updater.pathsWithIdsChanged({{directoryProjectChunkId, {directoryPathSourceId}}})); +} + +TEST_F(ProjectStorageUpdater, InputIsReusedNextCallIfAnErrorHappens) +{ + 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); + setProjectDatas( + directoryPathSourceId, + {{directoryPathSourceId, qmltypesPathSourceId, exampleModuleId, FileType::QmlTypes}, + {directoryPathSourceId, qmltypes2PathSourceId, exampleModuleId, FileType::QmlTypes}}); + setFilesDontChanged({directoryPathSourceId, qmlDirPathSourceId}); + ON_CALL(projectStorageMock, synchronize(_)).WillByDefault(Throw(QmlDesigner::ProjectStorageError{})); + updater.pathsWithIdsChanged( + {{qmltypesProjectChunkId, {qmltypesPathSourceId, qmltypes2PathSourceId}}}); + ON_CALL(projectStorageMock, synchronize(_)).WillByDefault(Return()); + + EXPECT_CALL( + projectStorageMock, + synchronize(AllOf( + Field(&SynchronizationPackage::imports, + UnorderedElementsAre(import1, import2, import4, import5)), + Field(&SynchronizationPackage::types, + UnorderedElementsAre( + Eq(objectType), + Eq(itemType), + AllOf(IsStorageType("First.qml", + Storage::Synchronization::ImportedType{"Object"}, + TypeTraits::Reference, + qmlDocumentSourceId1, + Storage::Synchronization::ChangeLevel::ExcludeExportedTypes), + Field(&Storage::Synchronization::Type::exportedTypes, IsEmpty())), + AllOf(IsStorageType("First2.qml", + Storage::Synchronization::ImportedType{"Object2"}, + TypeTraits::Reference, + qmlDocumentSourceId2, + Storage::Synchronization::ChangeLevel::ExcludeExportedTypes), + Field(&Storage::Synchronization::Type::exportedTypes, IsEmpty())))), + Field(&SynchronizationPackage::updatedSourceIds, + UnorderedElementsAre(qmlDocumentSourceId1, + qmlDocumentSourceId2, + qmltypesPathSourceId, + qmltypes2PathSourceId)), + Field(&SynchronizationPackage::fileStatuses, + UnorderedElementsAre(IsFileStatus(qmlDocumentSourceId1, 1, 21), + IsFileStatus(qmlDocumentSourceId2, 1, 21), + IsFileStatus(qmltypesPathSourceId, 1, 21), + IsFileStatus(qmltypes2PathSourceId, 1, 21))), + Field(&SynchronizationPackage::updatedFileStatusSourceIds, + UnorderedElementsAre(qmlDocumentSourceId1, + qmlDocumentSourceId2, + qmltypesPathSourceId, + qmltypes2PathSourceId)), + Field(&SynchronizationPackage::projectDatas, IsEmpty())))); + + updater.pathsWithIdsChanged( + {{qmlDocumentProjectChunkId, {qmlDocumentSourceId1, qmlDocumentSourceId2}}}); +} + +TEST_F(ProjectStorageUpdater, InputIsReusedNextCallIfAnErrorHappensAndQmltypesDuplicatesAreRemoved) +{ + 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); + setProjectDatas( + directoryPathSourceId, + {{directoryPathSourceId, qmltypesPathSourceId, exampleModuleId, FileType::QmlTypes}, + {directoryPathSourceId, qmltypes2PathSourceId, exampleModuleId, FileType::QmlTypes}}); + setFilesDontChanged({directoryPathSourceId, qmlDirPathSourceId}); + ON_CALL(projectStorageMock, synchronize(_)).WillByDefault(Throw(QmlDesigner::ProjectStorageError{})); + updater.pathsWithIdsChanged( + {{qmltypesProjectChunkId, {qmltypesPathSourceId, qmltypes2PathSourceId}}}); + ON_CALL(projectStorageMock, synchronize(_)).WillByDefault(Return()); + + EXPECT_CALL( + projectStorageMock, + synchronize(AllOf( + Field(&SynchronizationPackage::imports, + UnorderedElementsAre(import1, import2, import4, import5)), + Field(&SynchronizationPackage::types, + UnorderedElementsAre( + Eq(objectType), + Eq(itemType), + AllOf(IsStorageType("First.qml", + Storage::Synchronization::ImportedType{"Object"}, + TypeTraits::Reference, + qmlDocumentSourceId1, + Storage::Synchronization::ChangeLevel::ExcludeExportedTypes), + Field(&Storage::Synchronization::Type::exportedTypes, IsEmpty())), + AllOf(IsStorageType("First2.qml", + Storage::Synchronization::ImportedType{"Object2"}, + TypeTraits::Reference, + qmlDocumentSourceId2, + Storage::Synchronization::ChangeLevel::ExcludeExportedTypes), + Field(&Storage::Synchronization::Type::exportedTypes, IsEmpty())))), + Field(&SynchronizationPackage::updatedSourceIds, + UnorderedElementsAre(qmlDocumentSourceId1, + qmlDocumentSourceId2, + qmltypesPathSourceId, + qmltypes2PathSourceId)), + Field(&SynchronizationPackage::fileStatuses, + UnorderedElementsAre(IsFileStatus(qmlDocumentSourceId1, 1, 21), + IsFileStatus(qmlDocumentSourceId2, 1, 21), + IsFileStatus(qmltypesPathSourceId, 1, 21), + IsFileStatus(qmltypes2PathSourceId, 1, 21))), + Field(&SynchronizationPackage::updatedFileStatusSourceIds, + UnorderedElementsAre(qmlDocumentSourceId1, + qmlDocumentSourceId2, + qmltypesPathSourceId, + qmltypes2PathSourceId)), + Field(&SynchronizationPackage::projectDatas, IsEmpty())))); + + updater.pathsWithIdsChanged( + {{qmltypesProjectChunkId, {qmltypesPathSourceId, qmltypes2PathSourceId}}, + {qmlDocumentProjectChunkId, {qmlDocumentSourceId1, qmlDocumentSourceId2}}}); +} + +TEST_F(ProjectStorageUpdater, InputIsReusedNextCallIfAnErrorHappensAndQmlDocumentDuplicatesAreRemoved) +{ + 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); + setProjectDatas( + directoryPathSourceId, + {{directoryPathSourceId, qmltypesPathSourceId, exampleModuleId, FileType::QmlTypes}, + {directoryPathSourceId, qmltypes2PathSourceId, exampleModuleId, FileType::QmlTypes}, + {directoryPathSourceId, qmlDocumentSourceId1, QmlDesigner::ModuleId{}, FileType::QmlDocument}, + {directoryPathSourceId, qmlDocumentSourceId1, QmlDesigner::ModuleId{}, FileType::QmlDocument}}); + setFilesDontChanged({directoryPathSourceId, qmlDirPathSourceId}); + ON_CALL(projectStorageMock, synchronize(_)).WillByDefault(Throw(QmlDesigner::ProjectStorageError{})); + updater.pathsWithIdsChanged( + {{qmlDocumentProjectChunkId, {qmlDocumentSourceId1, qmlDocumentSourceId2}}}); + ON_CALL(projectStorageMock, synchronize(_)).WillByDefault(Return()); + + EXPECT_CALL( + projectStorageMock, + synchronize(AllOf( + Field(&SynchronizationPackage::imports, + UnorderedElementsAre(import1, import2, import4, import5)), + Field(&SynchronizationPackage::types, + UnorderedElementsAre( + Eq(objectType), + Eq(itemType), + AllOf(IsStorageType("First.qml", + Storage::Synchronization::ImportedType{"Object"}, + TypeTraits::Reference, + qmlDocumentSourceId1, + Storage::Synchronization::ChangeLevel::ExcludeExportedTypes), + Field(&Storage::Synchronization::Type::exportedTypes, IsEmpty())), + AllOf(IsStorageType("First2.qml", + Storage::Synchronization::ImportedType{"Object2"}, + TypeTraits::Reference, + qmlDocumentSourceId2, + Storage::Synchronization::ChangeLevel::ExcludeExportedTypes), + Field(&Storage::Synchronization::Type::exportedTypes, IsEmpty())))), + Field(&SynchronizationPackage::updatedSourceIds, + UnorderedElementsAre(qmlDocumentSourceId1, + qmlDocumentSourceId2, + qmltypesPathSourceId, + qmltypes2PathSourceId)), + Field(&SynchronizationPackage::fileStatuses, + UnorderedElementsAre(IsFileStatus(qmlDocumentSourceId1, 1, 21), + IsFileStatus(qmlDocumentSourceId2, 1, 21), + IsFileStatus(qmltypesPathSourceId, 1, 21), + IsFileStatus(qmltypes2PathSourceId, 1, 21))), + Field(&SynchronizationPackage::updatedFileStatusSourceIds, + UnorderedElementsAre(qmlDocumentSourceId1, + qmlDocumentSourceId2, + qmltypesPathSourceId, + qmltypes2PathSourceId)), + Field(&SynchronizationPackage::projectDatas, IsEmpty())))); + + updater.pathsWithIdsChanged( + {{qmltypesProjectChunkId, {qmltypesPathSourceId, qmltypes2PathSourceId}}, + {qmlDocumentProjectChunkId, {qmlDocumentSourceId1, qmlDocumentSourceId2}}}); +} + +TEST_F(ProjectStorageUpdater, InputIsClearedAfterSuccessfulUpdate) +{ + 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); + setProjectDatas( + directoryPathSourceId, + {{directoryPathSourceId, qmltypesPathSourceId, exampleModuleId, FileType::QmlTypes}, + {directoryPathSourceId, qmltypes2PathSourceId, exampleModuleId, FileType::QmlTypes}}); + setFilesDontChanged({directoryPathSourceId, qmlDirPathSourceId}); + updater.pathsWithIdsChanged( + {{qmltypesProjectChunkId, {qmltypesPathSourceId, qmltypes2PathSourceId}}}); + + EXPECT_CALL( + projectStorageMock, + synchronize(AllOf( + Field(&SynchronizationPackage::imports, UnorderedElementsAre(import1, import2)), + Field(&SynchronizationPackage::types, + UnorderedElementsAre( + AllOf(IsStorageType("First.qml", + Storage::Synchronization::ImportedType{"Object"}, + TypeTraits::Reference, + qmlDocumentSourceId1, + Storage::Synchronization::ChangeLevel::ExcludeExportedTypes), + Field(&Storage::Synchronization::Type::exportedTypes, IsEmpty())), + AllOf(IsStorageType("First2.qml", + Storage::Synchronization::ImportedType{"Object2"}, + TypeTraits::Reference, + qmlDocumentSourceId2, + Storage::Synchronization::ChangeLevel::ExcludeExportedTypes), + Field(&Storage::Synchronization::Type::exportedTypes, IsEmpty())))), + Field(&SynchronizationPackage::updatedSourceIds, + UnorderedElementsAre(qmlDocumentSourceId1, qmlDocumentSourceId2)), + Field(&SynchronizationPackage::fileStatuses, + UnorderedElementsAre(IsFileStatus(qmlDocumentSourceId1, 1, 21), + IsFileStatus(qmlDocumentSourceId2, 1, 21))), + Field(&SynchronizationPackage::updatedFileStatusSourceIds, + UnorderedElementsAre(qmlDocumentSourceId1, qmlDocumentSourceId2)), + Field(&SynchronizationPackage::projectDatas, IsEmpty())))); + + updater.pathsWithIdsChanged( + {{qmlDocumentProjectChunkId, {qmlDocumentSourceId1, qmlDocumentSourceId2}}}); +} + } // namespace From 502c119d30bff343ae19559228e4c5256df3cb20 Mon Sep 17 00:00:00 2001 From: Alessandro Portale Date: Tue, 4 Apr 2023 11:48:04 +0200 Subject: [PATCH 021/192] QmlDesignerBase: Add CONDITION on Qt::QuickWidgets For the case that Qt Creator is configured with a Qt that misses QuickWidgets. Change-Id: I7683ce091bd9f8fb6d95694484ba23c68efc19f6 Reviewed-by: Marco Bubke Reviewed-by: Qt CI Bot --- src/plugins/qmldesignerbase/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/src/plugins/qmldesignerbase/CMakeLists.txt b/src/plugins/qmldesignerbase/CMakeLists.txt index 8f0bccf5710..314940988f5 100644 --- a/src/plugins/qmldesignerbase/CMakeLists.txt +++ b/src/plugins/qmldesignerbase/CMakeLists.txt @@ -1,5 +1,6 @@ add_qtc_plugin(QmlDesignerBase PROPERTIES COMPILE_WARNING_AS_ERROR ON + CONDITION TARGET Qt::QuickWidgets DEPENDS Qt::Core Qt::QuickWidgets PLUGIN_DEPENDS Core ProjectExplorer QtSupport SOURCES From 9a90c357194b3508d01745712d9d1913d87dcf62 Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Tue, 4 Apr 2023 15:08:16 +0300 Subject: [PATCH 022/192] QmlDesigner: Fix the size of the shortcut text of Menubar items Task-number: QDS-9274 Change-Id: I0c49cdc5761209b0876a9445c2ec108a93f00885 Reviewed-by: Thomas Hartmann --- src/plugins/qmldesignerbase/utils/studiostyle.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/plugins/qmldesignerbase/utils/studiostyle.cpp b/src/plugins/qmldesignerbase/utils/studiostyle.cpp index 75cf926a0fa..bc257c24a80 100644 --- a/src/plugins/qmldesignerbase/utils/studiostyle.cpp +++ b/src/plugins/qmldesignerbase/utils/studiostyle.cpp @@ -424,6 +424,10 @@ void StudioStyle::drawControl( switch (element) { case CE_MenuItem: if (const auto mbi = qstyleoption_cast(option)) { + if (isQmlEditorMenu(widget)) { + Super::drawControl(element, option, painter, widget); + break; + } painter->save(); const int iconHeight = pixelMetric(QStyle::PM_SmallIconSize, option, widget); const int horizontalSpacing = pixelMetric(QStyle::PM_LayoutHorizontalSpacing, option, widget); From fd06bc9eca9b2d3d6117e552243f41501693ba23 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Tue, 4 Apr 2023 15:37:42 +0200 Subject: [PATCH 023/192] QmlDesigner: Fix evaluating the expression can have a side effect When the "target" of the PropertyChange is set, then evaluating the expression has a side=effect that stops the binding from working. Fixing this specific corner case. Task-number: QDS-9612 Change-Id: I0859433ff5b2c10dd1d3a6c02e5c133e5d55fdcd Reviewed-by: Tim Jenssen --- .../instances/objectnodeinstance.cpp | 20 +++++++++++++++---- .../qml2puppet/instances/objectnodeinstance.h | 1 + .../qmlpropertychangesnodeinstance.cpp | 5 +++++ .../qmlpropertychangesnodeinstance.h | 2 ++ 4 files changed, 24 insertions(+), 4 deletions(-) diff --git a/src/tools/qml2puppet/qml2puppet/instances/objectnodeinstance.cpp b/src/tools/qml2puppet/qml2puppet/instances/objectnodeinstance.cpp index 017c6bbc3f2..fc171c87723 100644 --- a/src/tools/qml2puppet/qml2puppet/instances/objectnodeinstance.cpp +++ b/src/tools/qml2puppet/qml2puppet/instances/objectnodeinstance.cpp @@ -167,6 +167,11 @@ bool ObjectNodeInstance::isRenderable() const return false; } +bool ObjectNodeInstance::isPropertyChange() const +{ + return false; +} + bool ObjectNodeInstance::equalGraphicsItem(QGraphicsItem * /*item*/) const { return false; @@ -500,15 +505,22 @@ void ObjectNodeInstance::setPropertyBinding(const PropertyName &name, const QStr if (!isSimpleExpression(expression)) return; - QQmlExpression qmlExpression(context(), object(), expression); - qmlExpression.evaluate(); - if (qmlExpression.hasError()) + bool qmlExpressionHasError = false; + + if (!isPropertyChange()) { + QQmlExpression qmlExpression(context(), object(), expression); + qmlExpression.evaluate(); + qmlExpressionHasError = qmlExpression.hasError(); + } + + if (qmlExpressionHasError) { QmlPrivateGate::setPropertyBinding(object(), context()->engine()->rootContext(), name, expression); - else + } else { QmlPrivateGate::setPropertyBinding(object(), context(), name, expression); + } } void ObjectNodeInstance::deleteObjectsInList(const QQmlProperty &property) diff --git a/src/tools/qml2puppet/qml2puppet/instances/objectnodeinstance.h b/src/tools/qml2puppet/qml2puppet/instances/objectnodeinstance.h index 36abf66494c..3fad24e661f 100644 --- a/src/tools/qml2puppet/qml2puppet/instances/objectnodeinstance.h +++ b/src/tools/qml2puppet/qml2puppet/instances/objectnodeinstance.h @@ -82,6 +82,7 @@ public: virtual bool isQuickWindow() const; virtual bool isLayoutable() const; virtual bool isRenderable() const; + virtual bool isPropertyChange() const; virtual bool equalGraphicsItem(QGraphicsItem *item) const; diff --git a/src/tools/qml2puppet/qml2puppet/instances/qmlpropertychangesnodeinstance.cpp b/src/tools/qml2puppet/qml2puppet/instances/qmlpropertychangesnodeinstance.cpp index 8fa29118d7c..cd3ea427145 100644 --- a/src/tools/qml2puppet/qml2puppet/instances/qmlpropertychangesnodeinstance.cpp +++ b/src/tools/qml2puppet/qml2puppet/instances/qmlpropertychangesnodeinstance.cpp @@ -87,5 +87,10 @@ void QmlPropertyChangesNodeInstance::reparent(const ObjectNodeInstance::Pointer QmlPrivateGate::PropertyChanges::attachToState(object()); } +bool QmlPropertyChangesNodeInstance::isPropertyChange() const +{ + return true; +} + } // namespace Internal } // namespace QmlDesigner diff --git a/src/tools/qml2puppet/qml2puppet/instances/qmlpropertychangesnodeinstance.h b/src/tools/qml2puppet/qml2puppet/instances/qmlpropertychangesnodeinstance.h index fb270b5ab28..6077fbf2fd2 100644 --- a/src/tools/qml2puppet/qml2puppet/instances/qmlpropertychangesnodeinstance.h +++ b/src/tools/qml2puppet/qml2puppet/instances/qmlpropertychangesnodeinstance.h @@ -29,6 +29,8 @@ public: void reparent(const ObjectNodeInstance::Pointer &oldParentInstance, const PropertyName &oldParentProperty, const ObjectNodeInstance::Pointer &newParentInstance, const PropertyName &newParentProperty) override; + bool isPropertyChange() const override; + protected: QmlPropertyChangesNodeInstance(QObject *object); }; From 1ecc500e5d0a07b296de492af18e441367486292 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Tue, 4 Apr 2023 15:31:16 +0200 Subject: [PATCH 024/192] QmlDesigner: Fix crash In doTransition we start and stop animations. Therefore the lookup for anim has to be done separately. Task-number: QDS-9621 Change-Id: I608799a997d48acef7496a985505f896113068d1 Reviewed-by: Tim Jenssen --- src/plugins/coreplugin/manhattanstyle.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/plugins/coreplugin/manhattanstyle.cpp b/src/plugins/coreplugin/manhattanstyle.cpp index a67deb4f41a..3d51642e44f 100644 --- a/src/plugins/coreplugin/manhattanstyle.cpp +++ b/src/plugins/coreplugin/manhattanstyle.cpp @@ -629,9 +629,9 @@ void ManhattanStyle::drawPrimitiveForPanelWidget(PrimitiveElement element, break; case PE_PanelButtonTool: { - bool animating = (state & State_Animating); - Animation *anim = d->animator.widgetAnimation(widget); painter->save(); + const bool animating = (state & State_Animating); + if (widget && !animating) { auto w = const_cast (widget); int oldState = w->property("_q_stylestate").toInt(); @@ -651,6 +651,7 @@ void ManhattanStyle::drawPrimitiveForPanelWidget(PrimitiveElement element, if (doTransition) { QImage startImage(option->rect.size(), QImage::Format_ARGB32_Premultiplied); QImage endImage(option->rect.size(), QImage::Format_ARGB32_Premultiplied); + Animation *anim = d->animator.widgetAnimation(widget); QStyleOption opt = *option; opt.state = (QStyle::State)oldState; opt.state |= State_Animating; @@ -680,6 +681,8 @@ void ManhattanStyle::drawPrimitiveForPanelWidget(PrimitiveElement element, } } + Animation *anim = d->animator.widgetAnimation(widget); + if (!animating && anim) { anim->paint(painter, option); } else { From 592f66ad6cdd170549cb8152bb6be2284085f61b Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Tue, 4 Apr 2023 16:53:24 +0200 Subject: [PATCH 025/192] QmlDesigner: Add Qt 6.5 support in TextToModelMerger Change-Id: Ie7380d50195f04868fe912a75de772087353347e Reviewed-by: Thomas Hartmann --- .../qmldesigner/designercore/model/texttomodelmerger.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp index 4469793db83..236b690bb4f 100644 --- a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp +++ b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp @@ -61,9 +61,9 @@ bool isSupportedAttachedProperties(const QString &propertyName) QStringList supportedVersionsList() { - static const QStringList list = {"2.0", "2.1", "2.2", "2.3", "2.4", "2.5", "2.6", - "2.7", "2.8", "2.9", "2.10", "2.11", "2.12", "2.13", - "2.14", "2.15", "6.0", "6.1", "6.2", "6.3", "6.4"}; + static const QStringList list = {"2.0", "2.1", "2.2", "2.3", "2.4", "2.5", "2.6", "2.7", + "2.8", "2.9", "2.10", "2.11", "2.12", "2.13", "2.14", "2.15", + "6.0", "6.1", "6.2", "6.3", "6.4", "6.5"}; return list; } From 0bc362598e22a59a85e2a5502ff6e9d62271649f Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Tue, 4 Apr 2023 16:52:52 +0200 Subject: [PATCH 026/192] QmlDesigner: Bump QDS version in project templates Change-Id: Iff3570925fcfccb0e907fd2406b0c4474a355796 Reviewed-by: Thomas Hartmann --- .../studio_templates/projects/common/app.qmlproject.tpl | 2 +- .../studio_templates/projects/common/qmlcomponents.tpl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/common/app.qmlproject.tpl b/share/qtcreator/qmldesigner/studio_templates/projects/common/app.qmlproject.tpl index d598f361c10..a6a536d4b2d 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/common/app.qmlproject.tpl +++ b/share/qtcreator/qmldesigner/studio_templates/projects/common/app.qmlproject.tpl @@ -101,7 +101,7 @@ Project { /* Required for deployment */ targetDirectory: "/opt/%{ProjectName}" - qdsVersion: "4.0" + qdsVersion: "4.1" quickVersion: "%{QtQuickVersion}" diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/common/qmlcomponents.tpl b/share/qtcreator/qmldesigner/studio_templates/projects/common/qmlcomponents.tpl index 50f3db973cb..5e2d5923b1e 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/common/qmlcomponents.tpl +++ b/share/qtcreator/qmldesigner/studio_templates/projects/common/qmlcomponents.tpl @@ -8,7 +8,7 @@ set(QT_QML_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/qml") include(FetchContent) FetchContent_Declare( ds - GIT_TAG qds-4.0 + GIT_TAG qds-4.1 GIT_REPOSITORY https://code.qt.io/qt-labs/qtquickdesigner-components.git ) From 4871e472c449a1ba235292da001b9bcfcc664464 Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Wed, 5 Apr 2023 11:45:40 +0000 Subject: [PATCH 027/192] Fix the wrong condition for the menu style Change-Id: I9a3b2602c4baaa7f2d7cba20bcc94b04f093ceef Reviewed-by: Thomas Hartmann --- src/plugins/qmldesignerbase/utils/studiostyle.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/qmldesignerbase/utils/studiostyle.cpp b/src/plugins/qmldesignerbase/utils/studiostyle.cpp index bc257c24a80..226c9ffb731 100644 --- a/src/plugins/qmldesignerbase/utils/studiostyle.cpp +++ b/src/plugins/qmldesignerbase/utils/studiostyle.cpp @@ -424,7 +424,7 @@ void StudioStyle::drawControl( switch (element) { case CE_MenuItem: if (const auto mbi = qstyleoption_cast(option)) { - if (isQmlEditorMenu(widget)) { + if (!isQmlEditorMenu(widget)) { Super::drawControl(element, option, painter, widget); break; } From 66d72433a6a9bb3d0900cf44719736a90ad99906 Mon Sep 17 00:00:00 2001 From: Burak Hancerli Date: Mon, 13 Feb 2023 00:49:09 +0100 Subject: [PATCH 028/192] QmlProject: Refactor QmlProjectManager - feature: JSON based project manager plugin - feature: functionality to write QmlProject file - tests: tests for QmlProjectItem Task-number: QDS-8810 Change-Id: I8989e54577e9cd883bd76346e22774cc7f7ed93f Reviewed-by: Reviewed-by: Thomas Hartmann Reviewed-by: Qt CI Bot --- src/plugins/qmlprojectmanager/CMakeLists.txt | 43 +- .../buildsystem/projectitem/converters.cpp | 362 ++++++++ .../buildsystem/projectitem/converters.h | 19 + .../projectitem}/filefilteritems.cpp | 75 +- .../projectitem}/filefilteritems.h | 33 +- .../projectitem/qmlprojectitem.cpp | 381 ++++++++ .../buildsystem/projectitem/qmlprojectitem.h | 108 +++ .../projectnode}/qmlprojectnodes.cpp | 4 +- .../projectnode}/qmlprojectnodes.h | 0 .../buildsystem/qmlbuildsystem.cpp | 544 +++++++++++ .../buildsystem/qmlbuildsystem.h | 115 +++ .../fileformat/qmlprojectfileformat.cpp | 198 ---- .../fileformat/qmlprojectfileformat.h | 22 - .../fileformat/qmlprojectitem.cpp | 118 --- .../fileformat/qmlprojectitem.h | 103 --- .../qmlprojectmanager/qmlmainfileaspect.cpp | 2 +- src/plugins/qmlprojectmanager/qmlproject.cpp | 851 +++--------------- src/plugins/qmlprojectmanager/qmlproject.h | 165 +--- .../qmlprojectmanager/qmlprojectmanager.qbs | 14 +- .../qmlprojectmanager/qmlprojectplugin.cpp | 1 - .../auto/qml/qmlprojectmanager/CMakeLists.txt | 1 + .../fileformat/CMakeLists.txt | 17 +- .../projectitem/CMakeLists.txt | 25 + .../qmlprojectmanager/projectitem/README.md | 46 + .../qmlprojectmanager/projectitem/common.h | 20 + .../projectitem/data/README.md | 47 + .../converter/test-set-1/testfile.jsontoqml | 98 ++ .../converter/test-set-1/testfile.qmlproject | 110 +++ .../converter/test-set-1/testfile.qmltojson | 176 ++++ .../converter/test-set-2/testfile.jsontoqml | 102 +++ .../converter/test-set-2/testfile.qmlproject | 112 +++ .../converter/test-set-2/testfile.qmltojson | 173 ++++ .../converter/test-set-3/testfile.jsontoqml | 93 ++ .../converter/test-set-3/testfile.qmlproject | 93 ++ .../converter/test-set-3/testfile.qmltojson | 160 ++++ .../converter/test-set-4/testfile.jsontoqml | 65 ++ .../converter/test-set-4/testfile.qmlproject | 55 ++ .../converter/test-set-4/testfile.qmltojson | 113 +++ .../converter/test-set-5/testfile.jsontoqml | 54 ++ .../converter/test-set-5/testfile.qmlproject | 44 + .../converter/test-set-5/testfile.qmltojson | 90 ++ .../file-filters/MaterialBundle.qmlproject | 112 +++ .../MaterialBundle.qmlproject.qtds | 1 + .../data/file-filters/MaterialLibrary.qrc | 1 + .../file-filters/asset_imports/CMakeLists.txt | 1 + .../ComponentBundles/CMakeLists.txt | 1 + .../MaterialBundle/AcrylicPaintMaterial.qml | 1 + .../MaterialBundle/AluminiumMaterial.qml | 1 + .../MaterialBundle/AsphaltMaterial.qml | 1 + .../MaterialBundle/BrickMaterial.qml | 1 + .../MaterialBundle/CMakeLists.txt | 1 + .../CarPaintGlitterMaterial.qml | 1 + .../MaterialBundle/CarPaintMaterial.qml | 1 + .../MaterialBundle/CarbonFiberMaterial.qml | 1 + .../MaterialBundle/CeramicMaterial.qml | 1 + .../MaterialBundle/ChromeMaterial.qml | 1 + .../MaterialBundle/ConcreteMaterial.qml | 1 + .../MaterialBundle/CopperMaterial.qml | 1 + .../MaterialBundle/FabricMaterial.qml | 1 + .../MaterialBundle/FabricRoughMaterial.qml | 1 + .../MaterialBundle/FabricSatinMaterial.qml | 1 + .../MaterialBundle/GlassMaterial.qml | 1 + .../MaterialBundle/GlassTintedMaterial.qml | 1 + .../MaterialBundle/GoldMaterial.qml | 1 + .../MaterialBundle/LeatherMaterial.qml | 1 + .../MaterialBundle/MirrorMaterial.qml | 1 + .../MaterialBundle/PaperMaterial.qml | 1 + .../MaterialBundle/PlasticMatteMaterial.qml | 1 + .../MaterialBundle/PlasticShinyMaterial.qml | 1 + .../PlasticTexturedMaterial.qml | 1 + .../MaterialBundle/RubberMaterial.qml | 1 + .../MaterialBundle/SilverMaterial.qml | 1 + .../MaterialBundle/SteelBrushedMaterial.qml | 1 + .../MaterialBundle/SteelFloorMaterial.qml | 1 + .../MaterialBundle/SteelMaterial.qml | 1 + .../MaterialBundle/StoneMaterial.qml | 1 + .../MaterialBundle/WaxMaterial.qml | 1 + .../MaterialBundle/WoodMaterial.qml | 1 + .../MaterialBundle/WoodParquetMaterial.qml | 1 + .../MaterialBundle/WoodPlanksMaterial.qml | 1 + .../MaterialBundle/_asset_ref.json | 1 + .../designer/acrylicpaint.metainfo | 1 + .../designer/aluminium.metainfo | 1 + .../MaterialBundle/designer/asphalt.metainfo | 1 + .../MaterialBundle/designer/brick.metainfo | 1 + .../designer/carbonfiber.metainfo | 1 + .../MaterialBundle/designer/carpaint.metainfo | 1 + .../designer/carpaintglitter.metainfo | 1 + .../MaterialBundle/designer/ceramic.metainfo | 1 + .../MaterialBundle/designer/chrome.metainfo | 1 + .../MaterialBundle/designer/concrete.metainfo | 1 + .../MaterialBundle/designer/copper.metainfo | 1 + .../MaterialBundle/designer/fabric.metainfo | 1 + .../designer/fabricrough.metainfo | 1 + .../designer/fabricsatin.metainfo | 1 + .../MaterialBundle/designer/glass.metainfo | 1 + .../designer/glasstinted.metainfo | 1 + .../MaterialBundle/designer/gold.metainfo | 1 + .../designer/images/material.png | 1 + .../designer/images/material16.png | 1 + .../designer/images/material@2x.png | 1 + .../MaterialBundle/designer/leather.metainfo | 1 + .../MaterialBundle/designer/mirror.metainfo | 1 + .../MaterialBundle/designer/paper.metainfo | 1 + .../designer/plasticmatte.metainfo | 1 + .../designer/plasticshiny.metainfo | 1 + .../designer/plastictextured.metainfo | 1 + .../MaterialBundle/designer/rubber.metainfo | 1 + .../MaterialBundle/designer/silver.metainfo | 1 + .../MaterialBundle/designer/steel.metainfo | 1 + .../designer/steelbrushed.metainfo | 1 + .../designer/steelfloor.metainfo | 1 + .../MaterialBundle/designer/stone.metainfo | 1 + .../MaterialBundle/designer/wax.metainfo | 1 + .../MaterialBundle/designer/wood.metainfo | 1 + .../designer/woodparquet.metainfo | 1 + .../designer/woodplanks.metainfo | 1 + .../images/Asphalt010_2K_NormalGL.png | 1 + .../images/Asphalt010_2K_Opacity.png | 1 + .../images/Asphalt010_2K_Roughness.png | 1 + .../images/Bricks026_2K_AmbientOcclusion.png | 1 + .../images/Bricks026_2K_Color.png | 1 + .../images/Bricks026_2K_NormalGL.png | 1 + .../images/Bricks026_2K_Roughness.png | 1 + .../images/Concrete032_2K_NormalGL.png | 1 + .../images/Concrete032_2K_Roughness.png | 1 + .../images/DiamondPlate001_2K_NormalGL.png | 1 + .../images/DiamondPlate001_2K_Roughness.png | 1 + .../images/Fabric004_2K_NormalGL.png | 1 + .../images/Fabric030_2K_Displacement.png | 1 + .../images/Fabric030_2K_NormalGL.png | 1 + .../images/Fabric030_2K_Roughness.png | 1 + .../images/Fabric031_2K_Displacement.png | 1 + .../images/Fabric031_2K_NormalGL.png | 1 + .../images/Fabric031_2K_Roughness.png | 1 + .../MaterialBundle/images/LDR_RGB1_3.png | 1 + .../images/Leather037_2K_Color.png | 1 + .../images/Leather037_2K_NormalGL.png | 1 + .../images/Leather037_2K_Roughness.png | 1 + .../images/Metal009_2K_NormalGL.png | 1 + .../images/Metal009_2K_Roughness.png | 1 + .../images/Metal029_2K_Displacement.jpg | 1 + .../images/Metal029_2K_Displacement.png | 1 + .../images/Paint006_2K_AmbientOcclusion.png | 1 + .../images/Paint006_2K_NormalGL.png | 1 + .../images/Paint006_2K_Roughness.png | 1 + .../images/Rock023_2K_AmbientOcclusion.png | 1 + .../images/Rock023_2K_Color.png | 1 + .../images/Rock023_2K_NormalGL.png | 1 + .../images/Rock023_2K_Roughness.png | 1 + .../images/Wood048_2K_Color.png | 1 + .../images/Wood048_2K_NormalGL.png | 1 + .../images/Wood048_2K_Roughness.png | 1 + .../images/WoodFloor044_2K_Color.png | 1 + .../images/WoodFloor044_2K_NormalGL.png | 1 + .../images/WoodFloor044_2K_Roughness.png | 1 + .../WoodFloor054_2K_AmbientOcclusion.png | 1 + .../images/WoodFloor054_2K_Color.png | 1 + .../images/WoodFloor054_2K_NormalGL.png | 1 + .../images/WoodFloor054_2K_Roughness.png | 1 + .../MaterialBundle/images/blurrynoise.tga | 1 + .../MaterialBundle/images/noisenormal.png | 1 + .../ComponentBundles/MaterialBundle/qmldir | 1 + .../MaterialBundle/shaders/CMakeLists.txt | 1 + .../MaterialBundle/shaders/SSS.frag | 1 + .../MaterialBundle/shaders/SSS.vert | 1 + .../MaterialBundle/shaders/carmat_simple.frag | 1 + .../MaterialBundle/shaders/carmat_simple.vert | 1 + .../shaders/carmat_simple_nf.frag | 1 + .../shaders/carmat_simple_nf.vert | 1 + .../MaterialBundle/shaders/glass.frag | 1 + .../MaterialBundle/shaders/glass.vert | 1 + .../MaterialBundle/shaders/satin.frag | 1 + .../MaterialBundle/shaders/satin.vert | 1 + .../data/file-filters/content/App.qml | 1 + .../data/file-filters/content/CMakeLists.txt | 1 + .../content/CustomRoundButton.qml | 1 + .../file-filters/content/MaterialNames.qml | 1 + .../file-filters/content/MouseRotator.qml | 1 + .../data/file-filters/content/Screen01.ui.qml | 1 + .../content/fonts/OpenSans-Bold.ttf | 1 + .../content/fonts/OpenSans-Regular.ttf | 1 + .../data/file-filters/content/fonts/fonts.txt | 1 + .../content/images/Ground_ShadowMap.png | 1 + .../content/images/HDR/dark_mode.png | 1 + .../content/images/HDR/day_mode.png | 1 + .../content/images/LDR_RGB1_3.png | 1 + .../file-filters/content/images/QtLogo_HD.png | 1 + .../content/images/UI/innerMesh.png | 1 + .../content/images/UI/lightToggle.png | 1 + .../content/images/UI/outerMesh.png | 1 + .../content/images/UI/perfhudicon.png | 1 + .../content/images/UI/perfhudicon_on.png | 1 + .../file-filters/content/images/White.png | 1 + .../file-filters/content/images/checkmark.png | 1 + .../content/images/groundAlpha.png | 1 + .../file-filters/content/images/qtlogo.png | 1 + .../content/images/scratchmap.png | 1 + .../file-filters/content/images/shadow.png | 1 + .../content/images/vlkhcah_2K_AO.jpg | 1 + .../content/images/vlkhcah_2K_Albedo.jpg | 1 + .../content/images/vlkhcah_2K_Normal.jpg | 1 + .../content/images/vlkhcah_2K_Roughness.jpg | 1 + .../file-filters/content/meshes/floor.mesh | 1 + .../content/meshes/materialBall.mesh | 1 + .../data/file-filters/filelist.txt | 126 +++ .../data/file-filters/imports/CMakeLists.txt | 1 + .../imports/MaterialLibrary/CMakeLists.txt | 1 + .../imports/MaterialLibrary/Constants.qml | 1 + .../MaterialLibrary/DirectoryFontLoader.qml | 1 + .../MaterialLibrary/EventListModel.qml | 1 + .../MaterialLibrary/EventListSimulator.qml | 1 + .../MaterialLibrary/designer/plugin.metainfo | 1 + .../imports/MaterialLibrary/qmldir | 1 + .../projectitem/data/file-filters/main.qml | 1 + .../data/file-filters/qmlcomponents | 1 + .../projectitem/data/file-filters/qmlmodules | 1 + .../data/file-filters/qtquickcontrols2.conf | 1 + .../projectitem/data/file-filters/share.qrc | 1 + .../data/file-filters/src/app_environment.h | 1 + .../file-filters/src/import_qml_plugins.h | 1 + .../data/file-filters/src/main.cpp | 1 + .../data/file-filters/translations.db | 1 + .../data/getter-setter/testfile-1.qmlproject | 96 ++ .../data/getter-setter/testfile-2.qmlproject | 44 + .../projectitem/test-converters.cpp | 108 +++ .../projectitem/test-filefilters.cpp | 25 + .../projectitem/test-getters.cpp | 150 +++ .../projectitem/test-setters.cpp | 150 +++ .../projectitem/tst_projectitem.cpp | 8 + 230 files changed, 4464 insertions(+), 1387 deletions(-) create mode 100644 src/plugins/qmlprojectmanager/buildsystem/projectitem/converters.cpp create mode 100644 src/plugins/qmlprojectmanager/buildsystem/projectitem/converters.h rename src/plugins/qmlprojectmanager/{fileformat => buildsystem/projectitem}/filefilteritems.cpp (77%) rename src/plugins/qmlprojectmanager/{fileformat => buildsystem/projectitem}/filefilteritems.h (80%) create mode 100644 src/plugins/qmlprojectmanager/buildsystem/projectitem/qmlprojectitem.cpp create mode 100644 src/plugins/qmlprojectmanager/buildsystem/projectitem/qmlprojectitem.h rename src/plugins/qmlprojectmanager/{ => buildsystem/projectnode}/qmlprojectnodes.cpp (89%) rename src/plugins/qmlprojectmanager/{ => buildsystem/projectnode}/qmlprojectnodes.h (100%) create mode 100644 src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.cpp create mode 100644 src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.h delete mode 100644 src/plugins/qmlprojectmanager/fileformat/qmlprojectfileformat.cpp delete mode 100644 src/plugins/qmlprojectmanager/fileformat/qmlprojectfileformat.h delete mode 100644 src/plugins/qmlprojectmanager/fileformat/qmlprojectitem.cpp delete mode 100644 src/plugins/qmlprojectmanager/fileformat/qmlprojectitem.h create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/CMakeLists.txt create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/README.md create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/common.h create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/README.md create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-1/testfile.jsontoqml create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-1/testfile.qmlproject create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-1/testfile.qmltojson create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-2/testfile.jsontoqml create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-2/testfile.qmlproject create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-2/testfile.qmltojson create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-3/testfile.jsontoqml create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-3/testfile.qmlproject create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-3/testfile.qmltojson create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-4/testfile.jsontoqml create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-4/testfile.qmlproject create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-4/testfile.qmltojson create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-5/testfile.jsontoqml create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-5/testfile.qmlproject create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-5/testfile.qmltojson create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/MaterialBundle.qmlproject create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/MaterialBundle.qmlproject.qtds create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/MaterialLibrary.qrc create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/CMakeLists.txt create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/CMakeLists.txt create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/AcrylicPaintMaterial.qml create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/AluminiumMaterial.qml create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/AsphaltMaterial.qml create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/BrickMaterial.qml create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/CMakeLists.txt create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/CarPaintGlitterMaterial.qml create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/CarPaintMaterial.qml create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/CarbonFiberMaterial.qml create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/CeramicMaterial.qml create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/ChromeMaterial.qml create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/ConcreteMaterial.qml create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/CopperMaterial.qml create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/FabricMaterial.qml create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/FabricRoughMaterial.qml create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/FabricSatinMaterial.qml create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/GlassMaterial.qml create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/GlassTintedMaterial.qml create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/GoldMaterial.qml create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/LeatherMaterial.qml create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/MirrorMaterial.qml create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/PaperMaterial.qml create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/PlasticMatteMaterial.qml create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/PlasticShinyMaterial.qml create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/PlasticTexturedMaterial.qml create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/RubberMaterial.qml create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/SilverMaterial.qml create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/SteelBrushedMaterial.qml create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/SteelFloorMaterial.qml create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/SteelMaterial.qml create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/StoneMaterial.qml create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/WaxMaterial.qml create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/WoodMaterial.qml create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/WoodParquetMaterial.qml create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/WoodPlanksMaterial.qml create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/_asset_ref.json create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/acrylicpaint.metainfo create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/aluminium.metainfo create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/asphalt.metainfo create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/brick.metainfo create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/carbonfiber.metainfo create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/carpaint.metainfo create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/carpaintglitter.metainfo create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/ceramic.metainfo create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/chrome.metainfo create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/concrete.metainfo create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/copper.metainfo create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/fabric.metainfo create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/fabricrough.metainfo create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/fabricsatin.metainfo create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/glass.metainfo create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/glasstinted.metainfo create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/gold.metainfo create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/images/material.png create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/images/material16.png create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/images/material@2x.png create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/leather.metainfo create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/mirror.metainfo create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/paper.metainfo create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/plasticmatte.metainfo create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/plasticshiny.metainfo create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/plastictextured.metainfo create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/rubber.metainfo create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/silver.metainfo create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/steel.metainfo create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/steelbrushed.metainfo create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/steelfloor.metainfo create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/stone.metainfo create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/wax.metainfo create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/wood.metainfo create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/woodparquet.metainfo create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/woodplanks.metainfo create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Asphalt010_2K_NormalGL.png create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Asphalt010_2K_Opacity.png create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Asphalt010_2K_Roughness.png create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Bricks026_2K_AmbientOcclusion.png create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Bricks026_2K_Color.png create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Bricks026_2K_NormalGL.png create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Bricks026_2K_Roughness.png create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Concrete032_2K_NormalGL.png create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Concrete032_2K_Roughness.png create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/DiamondPlate001_2K_NormalGL.png create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/DiamondPlate001_2K_Roughness.png create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Fabric004_2K_NormalGL.png create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Fabric030_2K_Displacement.png create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Fabric030_2K_NormalGL.png create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Fabric030_2K_Roughness.png create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Fabric031_2K_Displacement.png create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Fabric031_2K_NormalGL.png create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Fabric031_2K_Roughness.png create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/LDR_RGB1_3.png create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Leather037_2K_Color.png create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Leather037_2K_NormalGL.png create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Leather037_2K_Roughness.png create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Metal009_2K_NormalGL.png create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Metal009_2K_Roughness.png create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Metal029_2K_Displacement.jpg create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Metal029_2K_Displacement.png create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Paint006_2K_AmbientOcclusion.png create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Paint006_2K_NormalGL.png create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Paint006_2K_Roughness.png create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Rock023_2K_AmbientOcclusion.png create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Rock023_2K_Color.png create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Rock023_2K_NormalGL.png create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Rock023_2K_Roughness.png create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Wood048_2K_Color.png create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Wood048_2K_NormalGL.png create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Wood048_2K_Roughness.png create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/WoodFloor044_2K_Color.png create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/WoodFloor044_2K_NormalGL.png create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/WoodFloor044_2K_Roughness.png create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/WoodFloor054_2K_AmbientOcclusion.png create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/WoodFloor054_2K_Color.png create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/WoodFloor054_2K_NormalGL.png create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/WoodFloor054_2K_Roughness.png create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/blurrynoise.tga create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/noisenormal.png create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/qmldir create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/shaders/CMakeLists.txt create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/shaders/SSS.frag create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/shaders/SSS.vert create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/shaders/carmat_simple.frag create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/shaders/carmat_simple.vert create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/shaders/carmat_simple_nf.frag create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/shaders/carmat_simple_nf.vert create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/shaders/glass.frag create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/shaders/glass.vert create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/shaders/satin.frag create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/shaders/satin.vert create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/App.qml create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/CMakeLists.txt create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/CustomRoundButton.qml create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/MaterialNames.qml create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/MouseRotator.qml create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/Screen01.ui.qml create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/fonts/OpenSans-Bold.ttf create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/fonts/OpenSans-Regular.ttf create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/fonts/fonts.txt create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/images/Ground_ShadowMap.png create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/images/HDR/dark_mode.png create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/images/HDR/day_mode.png create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/images/LDR_RGB1_3.png create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/images/QtLogo_HD.png create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/images/UI/innerMesh.png create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/images/UI/lightToggle.png create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/images/UI/outerMesh.png create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/images/UI/perfhudicon.png create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/images/UI/perfhudicon_on.png create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/images/White.png create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/images/checkmark.png create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/images/groundAlpha.png create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/images/qtlogo.png create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/images/scratchmap.png create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/images/shadow.png create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/images/vlkhcah_2K_AO.jpg create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/images/vlkhcah_2K_Albedo.jpg create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/images/vlkhcah_2K_Normal.jpg create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/images/vlkhcah_2K_Roughness.jpg create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/meshes/floor.mesh create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/meshes/materialBall.mesh create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/filelist.txt create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/imports/CMakeLists.txt create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/imports/MaterialLibrary/CMakeLists.txt create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/imports/MaterialLibrary/Constants.qml create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/imports/MaterialLibrary/DirectoryFontLoader.qml create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/imports/MaterialLibrary/EventListModel.qml create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/imports/MaterialLibrary/EventListSimulator.qml create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/imports/MaterialLibrary/designer/plugin.metainfo create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/imports/MaterialLibrary/qmldir create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/main.qml create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/qmlcomponents create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/qmlmodules create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/qtquickcontrols2.conf create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/share.qrc create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/src/app_environment.h create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/src/import_qml_plugins.h create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/src/main.cpp create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/translations.db create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/getter-setter/testfile-1.qmlproject create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/getter-setter/testfile-2.qmlproject create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/test-converters.cpp create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/test-filefilters.cpp create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/test-getters.cpp create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/test-setters.cpp create mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/tst_projectitem.cpp diff --git a/src/plugins/qmlprojectmanager/CMakeLists.txt b/src/plugins/qmlprojectmanager/CMakeLists.txt index 4119bae7347..7e2d30ce85b 100644 --- a/src/plugins/qmlprojectmanager/CMakeLists.txt +++ b/src/plugins/qmlprojectmanager/CMakeLists.txt @@ -1,23 +1,13 @@ add_qtc_plugin(QmlProjectManager - CONDITION TARGET Qt::QuickWidgets + CONDITION TARGET Qt5::QuickWidgets PROPERTIES COMPILE_WARNING_AS_ERROR ON PLUGIN_CLASS QmlProjectPlugin - DEPENDS QmlJS Qt::QuickWidgets + DEPENDS QmlJS Qt5::QuickWidgets Utils PLUGIN_DEPENDS Core ProjectExplorer QtSupport QmlDesignerBase SOURCES - fileformat/filefilteritems.cpp fileformat/filefilteritems.h - fileformat/qmlprojectfileformat.cpp fileformat/qmlprojectfileformat.h - fileformat/qmlprojectitem.cpp fileformat/qmlprojectitem.h - cmakegen/checkablefiletreeitem.cpp cmakegen/checkablefiletreeitem.h - cmakegen/cmakegeneratordialog.cpp cmakegen/cmakegeneratordialog.h - cmakegen/cmakegeneratordialogtreemodel.cpp cmakegen/cmakegeneratordialogtreemodel.h - cmakegen/cmakeprojectconverter.cpp cmakegen/cmakeprojectconverter.h - cmakegen/cmakeprojectconverterdialog.cpp cmakegen/cmakeprojectconverterdialog.h - cmakegen/generatecmakelists.cpp cmakegen/generatecmakelists.h - cmakegen/generatecmakelistsconstants.h - cmakegen/boilerplate.qrc qmlprojectgen/qmlprojectgenerator.cpp qmlprojectgen/qmlprojectgenerator.h qmlprojectgen/templates.qrc + projectfilecontenttools.cpp projectfilecontenttools.h qdslandingpage.cpp qdslandingpage.h qdslandingpagetheme.cpp qdslandingpagetheme.h @@ -29,8 +19,33 @@ add_qtc_plugin(QmlProjectManager qmlprojectmanager_global.h qmlprojectmanagertr.h qmlprojectmanagerconstants.h - qmlprojectnodes.cpp qmlprojectnodes.h qmlprojectplugin.cpp qmlprojectplugin.h qmlprojectrunconfiguration.cpp qmlprojectrunconfiguration.h + buildsystem/qmlbuildsystem.cpp buildsystem/qmlbuildsystem.h + "${PROJECT_SOURCE_DIR}/src/share/3rdparty/studiofonts/studiofonts.qrc" ) + +extend_qtc_plugin(QmlProjectManager + PUBLIC_INCLUDES ${CMAKE_CURRENT_LIST_DIR}/buildsystem + SOURCES_PREFIX ${CMAKE_CURRENT_LIST_DIR}/buildsystem + SOURCES + projectitem/filefilteritems.cpp projectitem/filefilteritems.h + projectitem/qmlprojectitem.cpp projectitem/qmlprojectitem.h + projectitem/converters.h projectitem/converters.cpp + projectnode/qmlprojectnodes.cpp projectnode/qmlprojectnodes.h +) + +extend_qtc_plugin(QmlProjectManager + PUBLIC_INCLUDES ${CMAKE_CURRENT_LIST_DIR}/cmakegen + SOURCES_PREFIX ${CMAKE_CURRENT_LIST_DIR}/cmakegen + SOURCES + checkablefiletreeitem.cpp checkablefiletreeitem.h + cmakegeneratordialog.cpp cmakegeneratordialog.h + cmakegeneratordialogtreemodel.cpp cmakegeneratordialogtreemodel.h + cmakeprojectconverter.cpp cmakeprojectconverter.h + cmakeprojectconverterdialog.cpp cmakeprojectconverterdialog.h + generatecmakelists.cpp generatecmakelists.h + generatecmakelistsconstants.h + boilerplate.qrc +) diff --git a/src/plugins/qmlprojectmanager/buildsystem/projectitem/converters.cpp b/src/plugins/qmlprojectmanager/buildsystem/projectitem/converters.cpp new file mode 100644 index 00000000000..645335f8b9c --- /dev/null +++ b/src/plugins/qmlprojectmanager/buildsystem/projectitem/converters.cpp @@ -0,0 +1,362 @@ +// Copyright (C) 2016 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 "converters.h" + +#include + +namespace QmlProjectManager::Converters { + +using PropsPair = QPair; +struct FileProps +{ + const PropsPair image{"image", QStringList{"*.jpeg", "*.jpg", "*.png", "*.svg", "*.hdr", ".ktx"}}; + const PropsPair qml{"qml", QStringList{"*.qml"}}; + const PropsPair qmlDir{"qmldir", QStringList{"qmldir"}}; + const PropsPair javaScr{"javaScript", QStringList{"*.js", "*.ts"}}; + const PropsPair video{"video", QStringList{"*.mp4"}}; + const PropsPair sound{"sound", QStringList{"*.mp3", "*.wav"}}; + const PropsPair font{"font", QStringList{"*.ttf", "*.otf"}}; + const PropsPair config{"config", QStringList{"*.conf"}}; + const PropsPair styling{"styling", QStringList{"*.css"}}; + const PropsPair mesh{"meshes", QStringList{"*.mesh"}}; + const PropsPair + shader{"shader", + QStringList{"*.glsl", "*.glslv", "*.glslf", "*.vsh", "*.fsh", "*.vert", "*.frag"}}; +}; + +QString jsonToQmlProject(const QJsonObject &rootObject) +{ + QString qmlProjectString; + QTextStream ts{&qmlProjectString}; + + QJsonObject runConfig = rootObject["runConfig"].toObject(); + QJsonObject languageConfig = rootObject["language"].toObject(); + QJsonObject shaderConfig = rootObject["shaderTool"].toObject(); + QJsonObject versionConfig = rootObject["versions"].toObject(); + QJsonObject environmentConfig = rootObject["environment"].toObject(); + QJsonObject deploymentConfig = rootObject["deployment"].toObject(); + QJsonObject filesConfig = rootObject["fileGroups"].toObject(); + + int indentationLevel = 0; + + auto appendBreak = [&ts]() { ts << Qt::endl; }; + + auto appendComment = [&ts, &indentationLevel](const QString &comment) { + ts << QString(" ").repeated(indentationLevel * 4) << "\\\\ " << comment << Qt::endl; + }; + + auto appendItem = + [&ts, &indentationLevel](const QString &key, const QString &value, const bool isEnclosed) { + ts << QString(" ").repeated(indentationLevel * 4) << key << ": " + << (isEnclosed ? "\"" : "") << value << (isEnclosed ? "\"" : "") << Qt::endl; + }; + + auto appendString = [&appendItem](const QString &key, const QString &val) { + appendItem(key, val, true); + }; + + auto appendBool = [&appendItem](const QString &key, const bool &val) { + appendItem(key, QString::fromStdString(val ? "true" : "false"), false); + }; + + auto appendArray = [&appendItem](const QString &key, const QStringList &vals) { + QString finalString; + foreach (const QString &value, vals) { + finalString.append("\"").append(value).append("\"").append(","); + } + finalString.remove(finalString.length() - 1, 1); + finalString.prepend("[ ").append(" ]"); + appendItem(key, finalString, false); + }; + + auto startObject = [&ts, &indentationLevel](const QString &objectName) { + ts << Qt::endl + << QString(" ").repeated(indentationLevel * 4) << objectName << " {" << Qt::endl; + indentationLevel++; + }; + + auto endObject = [&ts, &indentationLevel]() { + indentationLevel--; + ts << QString(" ").repeated(indentationLevel * 4) << "}" << Qt::endl; + }; + + auto appendDirectories = + [&startObject, &endObject, &appendString, &filesConfig](const QString &jsonKey, + const QString &qmlKey) { + QJsonValue dirsObj = filesConfig[jsonKey].toObject()["directories"]; + QStringList dirs = dirsObj.toVariant().toStringList(); + foreach (const QString &directory, dirs) { + startObject(qmlKey); + appendString("directory", directory); + endObject(); + } + }; + + auto appendFiles = [&startObject, + &endObject, + &appendString, + &appendArray, + &filesConfig](const QString &jsonKey, const QString &qmlKey) { + QJsonValue dirsObj = filesConfig[jsonKey].toObject()["directories"]; + QJsonValue filesObj = filesConfig[jsonKey].toObject()["files"]; + QJsonValue filtersObj = filesConfig[jsonKey].toObject()["filters"]; + + foreach (const QString &directory, dirsObj.toVariant().toStringList()) { + startObject(qmlKey); + appendString("directory", directory); + appendString("filters", filtersObj.toVariant().toStringList().join(";")); + + if (!filesObj.toArray().isEmpty()) { + QStringList fileList; + foreach (const QJsonValue &file, filesObj.toArray()) { + fileList.append(file.toObject()["name"].toString()); + } + appendArray("files", fileList); + } + endObject(); + } + }; + + // start creating the file content + appendComment("prop: json-converted"); + appendComment("prop: auto-generated"); + + ts << Qt::endl << "import QmlProject" << Qt::endl; + { + startObject("Project"); + + { // append non-object props + appendString("mainFile", runConfig["mainFile"].toString()); + appendString("mainUiFile", runConfig["mainUiFile"].toString()); + appendString("targetDirectory", deploymentConfig["targetDirectory"].toString()); + appendBool("widgetApp", runConfig["widgetApp"].toBool()); + appendArray("importPaths", rootObject["importPaths"].toVariant().toStringList()); + appendBreak(); + appendString("qdsVersion", versionConfig["designStudio"].toString()); + appendString("quickVersion", versionConfig["qtQuick"].toString()); + appendBool("qt6Project", versionConfig["qtQuick"].toString().startsWith("6.")); + appendBool("qtForMCUs", rootObject["mcuConfig"].toObject().isEmpty()); + appendBreak(); + appendBool("multilanguageSupport", languageConfig["multiLanguageSupport"].toBool()); + appendString("primaryLanguage", languageConfig["primaryLanguage"].toString()); + appendArray("supportedLanguages", + languageConfig["supportedLanguages"].toVariant().toStringList()); + } + + { // append Environment object + startObject("Environment"); + foreach (const QString &key, environmentConfig.keys()) { + appendItem(key, environmentConfig[key].toString(), true); + } + endObject(); + } + + { // append ShaderTool object + startObject("ShaderTool"); + appendString("args", shaderConfig["args"].toVariant().toStringList().join(" ")); + appendArray("files", shaderConfig["files"].toVariant().toStringList()); + endObject(); + } + + { // append files objects + appendDirectories("qml", "QmlFiles"); + appendDirectories("javaScript", "JavaScriptFiles"); + appendDirectories("image", "ImageFiles"); + appendFiles("config", "Files"); + appendFiles("font", "Files"); + appendFiles("meshes", "Files"); + appendFiles("qmldir", "Files"); + appendFiles("shader", "Files"); + appendFiles("sound", "Files"); + appendFiles("video", "Files"); + } + + endObject(); // Closing 'Project' + } + return qmlProjectString; +} + +QJsonObject qmlProjectTojson(const Utils::FilePath &projectFile) +{ + QmlJS::SimpleReader simpleQmlJSReader; + + const QmlJS::SimpleReaderNode::Ptr rootNode = simpleQmlJSReader.readFile(projectFile.toString()); + + if (!simpleQmlJSReader.errors().isEmpty() || !rootNode->isValid()) { + qCritical() << "Unable to parse:" << projectFile; + qCritical() << simpleQmlJSReader.errors(); + return {}; + } + + if (rootNode->name() != QLatin1String("Project")) { + qCritical() << "Cannot find root 'Proejct' item in the project file: " << projectFile; + return {}; + } + + auto nodeToJsonObject = [](const QmlJS::SimpleReaderNode::Ptr &node) { + QJsonObject tObj; + foreach (const QString &childPropName, node->propertyNames()) { + tObj.insert(childPropName, node->property(childPropName).value.toJsonValue()); + } + return tObj; + }; + + auto toCamelCase = [](const QString &s) { return QString(s).replace(0, 1, s[0].toLower()); }; + + QJsonObject rootObject; // root object + QJsonObject fileGroupsObject; + QJsonObject languageObject; + QJsonObject versionObject; + QJsonObject runConfigObject; + QJsonObject deploymentObject; + QJsonObject mcuObject; + QJsonObject shaderToolObject; + + // convert the the non-object props + for (const QString &propName : rootNode->propertyNames()) { + QJsonObject *currentObj = &rootObject; + QString objKey = propName; + + if (propName.contains("language", Qt::CaseInsensitive)) { + currentObj = &languageObject; + if (propName.toLower() == "multilanguagesupport") // fixing the camelcase + objKey = "multiLanguageSupport"; + } else if (propName.contains("version", Qt::CaseInsensitive)) { + currentObj = &versionObject; + if (propName.toLower() == "qdsversion") + objKey = "designStudio"; + else if (propName.toLower() == "quickversion") + objKey = "qtQuick"; + } else if (propName.contains("widgetapp", Qt::CaseInsensitive) + || propName.contains("fileselector", Qt::CaseInsensitive) + || propName.contains("mainfile", Qt::CaseInsensitive) + || propName.contains("mainuifile", Qt::CaseInsensitive) + || propName.contains("forcefreetype", Qt::CaseInsensitive)) { + currentObj = &runConfigObject; + } else if (propName.contains("targetdirectory", Qt::CaseInsensitive)) { + currentObj = &deploymentObject; + } else if (propName.contains("qtformcus", Qt::CaseInsensitive)) { + currentObj = &mcuObject; + objKey = "mcuEnabled"; + } else if (propName.contains("qt6project", Qt::CaseInsensitive)) { + // we are skipping these ones in the new json format + continue; + } + + currentObj->insert(objKey, rootNode->property(propName).value.toJsonValue()); + } + + // add missing non-object props if any + if (!runConfigObject.contains("fileSelectors")) { + runConfigObject.insert("fileSelectors", QJsonArray{}); + } + + // convert the the object props + for (const QmlJS::SimpleReaderNode::Ptr &childNode : rootNode->children()) { + if (childNode->name().contains("files", Qt::CaseInsensitive)) { + PropsPair propsPair; + FileProps fileProps; + const QString childNodeName = childNode->name().toLower(); + const QmlJS::SimpleReaderNode::Property childNodeFilter = childNode->property("filter"); + const QmlJS::SimpleReaderNode::Property childNodeDirectory = childNode->property("directory"); + const QmlJS::SimpleReaderNode::Property childNodeFiles = childNode->property("files"); + const QString childNodeFilterValue = childNodeFilter.value.toString(); + + if (childNodeName == "qmlfiles" || childNodeFilterValue.contains("*.qml")) { + propsPair = fileProps.qml; + } else if (childNodeName == "javascriptfiles") { + propsPair = fileProps.javaScr; + } else if (childNodeName == "imagefiles") { + propsPair = fileProps.image; + } else { + if (childNodeFilter.isValid()) { + if (childNodeFilterValue.contains(".conf")) + propsPair = fileProps.config; + else if (childNodeFilterValue.contains(".ttf")) + propsPair = fileProps.font; + else if (childNodeFilterValue.contains("qmldir")) + propsPair = fileProps.qmlDir; + else if (childNodeFilterValue.contains(".wav")) + propsPair = fileProps.sound; + else if (childNodeFilterValue.contains(".mp4")) + propsPair = fileProps.video; + else if (childNodeFilterValue.contains(".mesh")) + propsPair = fileProps.mesh; + else if (childNodeFilterValue.contains(".glsl")) + propsPair = fileProps.shader; + else if (childNodeFilterValue.contains(".css")) + propsPair = fileProps.styling; + } + } + + // get all objects we'll work on + QJsonObject targetObject = fileGroupsObject[propsPair.first].toObject(); + QJsonArray directories = targetObject["directories"].toArray(); + QJsonArray filters = targetObject["filters"].toArray(); + QJsonArray files = targetObject["files"].toArray(); + + // populate & update filters + if (filters.isEmpty()) { + filters = QJsonArray::fromStringList(propsPair.second); // populate the filters with the predefined ones + } + + if (childNodeFilter.isValid()) { // append filters from qmlproject (merge) + const QStringList filtersFromProjectFile = childNodeFilterValue.split( + ";"); + for (const QString &filter : filtersFromProjectFile) { + if (!filters.contains(QJsonValue(filter))) { + filters.append(QJsonValue(filter)); + } + } + } + + // populate & update directories + if (childNodeDirectory.isValid()) { + directories.append(childNodeDirectory.value.toJsonValue()); + } + if (directories.isEmpty()) + directories.append("."); + + // populate & update files + if (childNodeFiles.isValid()) { + foreach (const QJsonValue &file, childNodeFiles.value.toJsonArray()) { + files.append(QJsonObject{{"name", file.toString()}}); + } + } + + // put everything back into the root object + targetObject.insert("directories", directories); + targetObject.insert("filters", filters); + targetObject.insert("files", files); + fileGroupsObject.insert(propsPair.first, targetObject); + } else if (childNode->name().contains("shadertool", Qt::CaseInsensitive)) { + QStringList quotedArgs = childNode->property("args").value.toString().split('\"'); + QStringList args; + for (int i = 0; i < quotedArgs.size(); ++i) { + // Each odd arg in this list is a single quoted argument, which we should + // not be split further + if (i % 2 == 0) + args.append(quotedArgs[i].trimmed().split(' ')); + else + args.append(quotedArgs[i].prepend("\"").append("\"")); + } + + shaderToolObject.insert("args", QJsonArray::fromStringList(args)); + shaderToolObject.insert("files", childNode->property("files").value.toJsonValue()); + } else { + rootObject.insert(toCamelCase(childNode->name()), nodeToJsonObject(childNode)); + } + } + + rootObject.insert("fileGroups", fileGroupsObject); + rootObject.insert("language", languageObject); + rootObject.insert("versions", versionObject); + rootObject.insert("runConfig", runConfigObject); + rootObject.insert("deployment", deploymentObject); + rootObject.insert("mcuConfig", mcuObject); + rootObject.insert("shaderTool", shaderToolObject); + rootObject.insert("fileVersion", 1); + return rootObject; +} +} // namespace QmlProjectManager::Converters diff --git a/src/plugins/qmlprojectmanager/buildsystem/projectitem/converters.h b/src/plugins/qmlprojectmanager/buildsystem/projectitem/converters.h new file mode 100644 index 00000000000..b0bb98895e8 --- /dev/null +++ b/src/plugins/qmlprojectmanager/buildsystem/projectitem/converters.h @@ -0,0 +1,19 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0 + +#pragma once + +#include +#include + +#include +#include + +#include + +namespace QmlProjectManager::Converters { + +QString jsonToQmlProject(const QJsonObject &rootObject); +QJsonObject qmlProjectTojson(const Utils::FilePath &projectFile); + +} // namespace QmlProjectManager::Converters diff --git a/src/plugins/qmlprojectmanager/fileformat/filefilteritems.cpp b/src/plugins/qmlprojectmanager/buildsystem/projectitem/filefilteritems.cpp similarity index 77% rename from src/plugins/qmlprojectmanager/fileformat/filefilteritems.cpp rename to src/plugins/qmlprojectmanager/buildsystem/projectitem/filefilteritems.cpp index 79b490887e5..9e834891949 100644 --- a/src/plugins/qmlprojectmanager/fileformat/filefilteritems.cpp +++ b/src/plugins/qmlprojectmanager/buildsystem/projectitem/filefilteritems.cpp @@ -15,36 +15,46 @@ namespace QmlProjectManager { -FileFilterBaseItem::FileFilterBaseItem() +FileFilterItem::FileFilterItem(){ + initTimer(); +} + +FileFilterItem::FileFilterItem(const QString &directory, const QStringList &filters) { + setDirectory(directory); + setFilters(filters); + initTimer(); +} + +void FileFilterItem::initTimer(){ m_updateFileListTimer.setSingleShot(true); m_updateFileListTimer.setInterval(50); - connect(&m_updateFileListTimer, &QTimer::timeout, this, &FileFilterBaseItem::updateFileListNow); + connect(&m_updateFileListTimer, &QTimer::timeout, this, &FileFilterItem::updateFileListNow); } -Utils::FileSystemWatcher *FileFilterBaseItem::dirWatcher() +Utils::FileSystemWatcher *FileFilterItem::dirWatcher() { if (!m_dirWatcher) { m_dirWatcher = new Utils::FileSystemWatcher(1, this); // Separate id, might exceed OS limits. m_dirWatcher->setObjectName(QLatin1String("FileFilterBaseItemWatcher")); connect(m_dirWatcher, &Utils::FileSystemWatcher::directoryChanged, - this, &FileFilterBaseItem::updateFileList); + this, &FileFilterItem::updateFileList); } return m_dirWatcher; } -QStringList FileFilterBaseItem::watchedDirectories() const +QStringList FileFilterItem::watchedDirectories() const { return m_dirWatcher ? m_dirWatcher->directories() : QStringList(); } -QString FileFilterBaseItem::directory() const +QString FileFilterItem::directory() const { return m_rootDir; } -void FileFilterBaseItem::setDirectory(const QString &dirPath) +void FileFilterItem::setDirectory(const QString &dirPath) { if (m_rootDir == dirPath) return; @@ -54,7 +64,7 @@ void FileFilterBaseItem::setDirectory(const QString &dirPath) updateFileList(); } -void FileFilterBaseItem::setDefaultDirectory(const QString &dirPath) +void FileFilterItem::setDefaultDirectory(const QString &dirPath) { if (m_defaultDir == dirPath) return; @@ -63,12 +73,12 @@ void FileFilterBaseItem::setDefaultDirectory(const QString &dirPath) updateFileListNow(); } -QString FileFilterBaseItem::filter() const +QStringList FileFilterItem::filters() const { return m_filter; } -void FileFilterBaseItem::setFilter(const QString &filter) +void FileFilterItem::setFilters(const QStringList &filter) { if (filter == m_filter) return; @@ -77,7 +87,7 @@ void FileFilterBaseItem::setFilter(const QString &filter) m_regExpList.clear(); m_fileSuffixes.clear(); - for (const QString &pattern : filter.split(QLatin1Char(';'))) { + for (const QString &pattern : filter) { if (pattern.isEmpty()) continue; // decide if it's a canonical pattern like *.x @@ -96,7 +106,7 @@ void FileFilterBaseItem::setFilter(const QString &filter) updateFileList(); } -bool FileFilterBaseItem::recursive() const +bool FileFilterItem::recursive() const { bool recursive; if (m_recurse == Recurse) { @@ -112,7 +122,7 @@ bool FileFilterBaseItem::recursive() const return recursive; } -void FileFilterBaseItem::setRecursive(bool recurse) +void FileFilterItem::setRecursive(bool recurse) { bool oldRecursive = recursive(); @@ -125,18 +135,18 @@ void FileFilterBaseItem::setRecursive(bool recurse) updateFileList(); } -QStringList FileFilterBaseItem::pathsProperty() const +QStringList FileFilterItem::pathsProperty() const { return m_explicitFiles; } -void FileFilterBaseItem::setPathsProperty(const QStringList &path) +void FileFilterItem::setPathsProperty(const QStringList &path) { m_explicitFiles = path; updateFileList(); } -QStringList FileFilterBaseItem::files() const +QStringList FileFilterItem::files() const { return Utils::toList(m_files); } @@ -146,7 +156,7 @@ QStringList FileFilterBaseItem::files() const @param filePath: absolute file path */ -bool FileFilterBaseItem::matchesFile(const QString &filePath) const +bool FileFilterItem::matchesFile(const QString &filePath) const { for (const QString &explicitFile : m_explicitFiles) { if (absolutePath(explicitFile) == filePath) @@ -167,14 +177,14 @@ bool FileFilterBaseItem::matchesFile(const QString &filePath) const return false; } -QString FileFilterBaseItem::absolutePath(const QString &path) const +QString FileFilterItem::absolutePath(const QString &path) const { if (QFileInfo(path).isAbsolute()) return path; return QDir(absoluteDir()).absoluteFilePath(path); } -QString FileFilterBaseItem::absoluteDir() const +QString FileFilterItem::absoluteDir() const { QString absoluteDir; if (QFileInfo(m_rootDir).isAbsolute()) @@ -185,13 +195,15 @@ QString FileFilterBaseItem::absoluteDir() const return QDir::cleanPath(absoluteDir); } -void FileFilterBaseItem::updateFileList() +void FileFilterItem::updateFileList() { +#ifndef TESTS_ENABLED_QMLPROJECTITEM if (!m_updateFileListTimer.isActive()) m_updateFileListTimer.start(); +#endif } -void FileFilterBaseItem::updateFileListNow() +void FileFilterItem::updateFileListNow() { if (m_updateFileListTimer.isActive()) m_updateFileListTimer.stop(); @@ -233,7 +245,7 @@ void FileFilterBaseItem::updateFileListNow() dirWatcher()->addDirectories(Utils::toList(watchDirs), Utils::FileSystemWatcher::WatchAllChanges); } -bool FileFilterBaseItem::fileMatches(const QString &fileName) const +bool FileFilterItem::fileMatches(const QString &fileName) const { for (const QString &suffix : std::as_const(m_fileSuffixes)) { if (fileName.endsWith(suffix, Qt::CaseInsensitive)) @@ -248,7 +260,7 @@ bool FileFilterBaseItem::fileMatches(const QString &fileName) const return false; } -QSet FileFilterBaseItem::filesInSubTree(const QDir &rootDir, const QDir &dir, QSet *parsedDirs) +QSet FileFilterItem::filesInSubTree(const QDir &rootDir, const QDir &dir, QSet *parsedDirs) { QSet fileSet; @@ -270,22 +282,5 @@ QSet FileFilterBaseItem::filesInSubTree(const QDir &rootDir, const QDir return fileSet; } -ImageFileFilterItem::ImageFileFilterItem() -{ - QString filter; - // supported image formats according to - QList extensions = QImageReader::supportedImageFormats(); - extensions.append("hdr"); - extensions.append("ktx"); - for (const QByteArray &extension : std::as_const(extensions)) - filter.append(QString::fromLatin1("*.%1;").arg(QString::fromLatin1(extension))); - setFilter(filter); -} - -FileFilterItem::FileFilterItem(const QString &fileFilter) -{ - setFilter(fileFilter); -} - } // namespace QmlProjectManager diff --git a/src/plugins/qmlprojectmanager/fileformat/filefilteritems.h b/src/plugins/qmlprojectmanager/buildsystem/projectitem/filefilteritems.h similarity index 80% rename from src/plugins/qmlprojectmanager/fileformat/filefilteritems.h rename to src/plugins/qmlprojectmanager/buildsystem/projectitem/filefilteritems.h index 20f6e83718c..e5e3edb2a32 100644 --- a/src/plugins/qmlprojectmanager/fileformat/filefilteritems.h +++ b/src/plugins/qmlprojectmanager/buildsystem/projectitem/filefilteritems.h @@ -7,6 +7,7 @@ #include #include #include +#include QT_FORWARD_DECLARE_CLASS(QDir) @@ -14,16 +15,10 @@ namespace Utils { class FileSystemWatcher; } namespace QmlProjectManager { -class QmlProjectContentItem : public QObject -{ - // base class for all elements that should be direct children of Project element - Q_OBJECT +using FileFilterItemPtr = std::unique_ptr; +using FileFilterItems = std::vector; -public: - QmlProjectContentItem() {} -}; - -class FileFilterBaseItem : public QmlProjectContentItem { +class FileFilterItem : public QObject { Q_OBJECT Q_PROPERTY(QString directory READ directory WRITE setDirectory NOTIFY directoryChanged) @@ -33,15 +28,16 @@ class FileFilterBaseItem : public QmlProjectContentItem { Q_PROPERTY(QStringList files READ files NOTIFY filesChanged DESIGNABLE false) public: - FileFilterBaseItem(); + FileFilterItem(); + FileFilterItem(const QString &directory, const QStringList &filters); QString directory() const; void setDirectory(const QString &directoryPath); void setDefaultDirectory(const QString &directoryPath); - QString filter() const; - void setFilter(const QString &filter); + QStringList filters() const; + void setFilters(const QStringList &filter); bool recursive() const; void setRecursive(bool recursive); @@ -73,7 +69,7 @@ private: QString m_rootDir; QString m_defaultDir; - QString m_filter; + QStringList m_filter; // simple "*.png" patterns are stored in m_fileSuffixes, otherwise store in m_regExpList QList m_fileSuffixes; QList m_regExpList; @@ -93,16 +89,7 @@ private: QTimer m_updateFileListTimer; friend class ProjectItem; -}; - -class FileFilterItem : public FileFilterBaseItem { -public: - FileFilterItem(const QString &fileFilter); -}; - -class ImageFileFilterItem : public FileFilterBaseItem { -public: - ImageFileFilterItem(); + void initTimer(); }; } // namespace QmlProjectManager diff --git a/src/plugins/qmlprojectmanager/buildsystem/projectitem/qmlprojectitem.cpp b/src/plugins/qmlprojectmanager/buildsystem/projectitem/qmlprojectitem.cpp new file mode 100644 index 00000000000..76ad2e88360 --- /dev/null +++ b/src/plugins/qmlprojectmanager/buildsystem/projectitem/qmlprojectitem.cpp @@ -0,0 +1,381 @@ +// Copyright (C) 2016 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 "qmlprojectitem.h" + +#include +#include + +#include "converters.h" + +#include +#include +#include + +#include + +namespace QmlProjectManager { + +//#define REWRITE_PROJECT_FILE_IN_JSON_FORMAT + +QmlProjectItem::QmlProjectItem(const Utils::FilePath &filePath) + : m_projectFile(filePath) +{ + if (initProjectObject()) + setupFileFilters(); +} + +bool QmlProjectItem::initProjectObject() +{ + auto contents = m_projectFile.fileContents(); + if (!contents) { + qWarning() << "Cannot open project file. Path:" << m_projectFile.fileName(); + return false; + } + + QString fileContent{QString::fromUtf8(contents.value())}; + QJsonObject rootObj; + QJsonParseError parseError; + + if (fileContent.contains("import qmlproject", Qt::CaseInsensitive)) { + rootObj = Converters::qmlProjectTojson(m_projectFile); +#ifdef REWRITE_PROJECT_FILE_IN_JSON_FORMAT + m_projectFile.writeFileContents(QJsonDocument(rootObj).toJson()); +#endif + } else { + rootObj + = QJsonDocument::fromJson(m_projectFile.fileContents()->data(), &parseError).object(); + } + + if (rootObj.isEmpty()) { + if (parseError.error != QJsonParseError::NoError) { + qWarning() << "Cannot parse the json formatted project file. Error:" + << parseError.errorString(); + } else { + qWarning() << "Cannot convert QmlProject to Json."; + } + return false; + } + + m_project = rootObj; + return true; +} + +void QmlProjectItem::setupFileFilters() +{ + auto setupFileFilterItem = [this](const QJsonObject &fileGroup) { + for (const QString &directory : fileGroup["directories"].toVariant().toStringList()) { + std::unique_ptr fileFilterItem{new FileFilterItem}; + + QStringList filesArr; + for (const QJsonValue &file : fileGroup["files"].toArray()) { + filesArr.append(file["name"].toString()); + } + + fileFilterItem->setDirectory(directory); + fileFilterItem->setFilters(fileGroup["filters"].toVariant().toStringList()); + fileFilterItem->setRecursive(fileGroup["recursive"].toBool(true)); + fileFilterItem->setPathsProperty(fileGroup["directories"].toVariant().toStringList()); + fileFilterItem->setPathsProperty(filesArr); + fileFilterItem->setDefaultDirectory(m_projectFile.parentDir().toString()); +#ifndef TESTS_ENABLED_QMLPROJECTITEM + connect(fileFilterItem.get(), + &FileFilterItem::filesChanged, + this, + &QmlProjectItem::qmlFilesChanged); +#endif + m_content.push_back(std::move(fileFilterItem)); + }; + }; + + QJsonObject fileGroups = m_project["fileGroups"].toObject(); + for (const QString &groupName : fileGroups.keys()) { + setupFileFilterItem(fileGroups[groupName].toObject()); + } +} + +Utils::FilePath QmlProjectItem::sourceDirectory() const +{ + return m_projectFile.parentDir(); +} + +QString QmlProjectItem::targetDirectory() const +{ + return m_project["deployment"].toObject()["targetDirectory"].toString(); +} + +bool QmlProjectItem::isQt4McuProject() const +{ + return m_project["mcuConfig"].toObject()["mcuEnabled"].toBool(); +} + +Utils::EnvironmentItems QmlProjectItem::environment() const +{ + Utils::EnvironmentItems envItems; + QJsonObject envVariables = m_project["environment"].toObject(); + foreach (const QString &variableName, envVariables.keys()) { + envItems.append(Utils::EnvironmentItem(variableName, envVariables[variableName].toString())); + } + return envItems; +} + +void QmlProjectItem::addToEnviroment(const QString &key, const QString &value) +{ + QJsonObject envVariables = m_project["environment"].toObject(); + envVariables.insert(key, value); + insertAndUpdateProjectFile("environment", envVariables); +} + +QJsonObject QmlProjectItem::project() const +{ + return m_project; +} + +bool QmlProjectItem::isQt6Project() const +{ + return m_project["versions"].toObject()["qtQuick"].toString().startsWith("6."); +} + +QStringList QmlProjectItem::importPaths() const +{ + return m_project["importPaths"].toVariant().toStringList(); +} + +void QmlProjectItem::setImportPaths(const QStringList &importPaths) +{ + insertAndUpdateProjectFile("importPaths", QJsonArray::fromStringList(importPaths)); +} + +void QmlProjectItem::addImportPath(const QString &importPath) +{ + QJsonArray importPaths = m_project["importPaths"].toArray(); + + if (importPaths.contains(importPath)) + return; + + importPaths.append(importPath); + insertAndUpdateProjectFile("importPaths", importPaths); +} + +QStringList QmlProjectItem::fileSelectors() const +{ + return m_project["runConfig"].toObject()["fileSelectors"].toVariant().toStringList(); +} + +void QmlProjectItem::setFileSelectors(const QStringList &selectors) +{ + QJsonObject targetObj = m_project["runConfig"].toObject(); + targetObj["fileSelectors"] = QJsonArray::fromStringList(selectors); + insertAndUpdateProjectFile("runConfig", targetObj); +} + +void QmlProjectItem::addFileSelector(const QString &selector) +{ + QJsonObject targetObj = m_project["runConfig"].toObject(); + QJsonArray fileSelectors = targetObj["fileSelectors"].toArray(); + + if (fileSelectors.contains(selector)) + return; + + fileSelectors.append(selector); + targetObj["fileSelectors"] = fileSelectors; + insertAndUpdateProjectFile("runConfig", targetObj); +} + +bool QmlProjectItem::multilanguageSupport() const +{ + return m_project["language"].toObject()["multiLanguageSupport"].toBool(); +} + +void QmlProjectItem::setMultilanguageSupport(const bool &isEnabled) +{ + QJsonObject targetObj = m_project["language"].toObject(); + targetObj["multiLanguageSupport"] = isEnabled; + insertAndUpdateProjectFile("language", targetObj); +} + +QStringList QmlProjectItem::supportedLanguages() const +{ + return m_project["language"].toObject()["supportedLanguages"].toVariant().toStringList(); +} + +void QmlProjectItem::setSupportedLanguages(const QStringList &languages) +{ + QJsonObject targetObj = m_project["language"].toObject(); + targetObj["supportedLanguages"] = QJsonArray::fromStringList(languages); + insertAndUpdateProjectFile("language", targetObj); +} + +void QmlProjectItem::addSupportedLanguage(const QString &language) +{ + QJsonObject targetObj = m_project["language"].toObject(); + QJsonArray suppLangs = targetObj["supportedLanguages"].toArray(); + + if (suppLangs.contains(language)) + return; + + suppLangs.append(language); + targetObj["supportedLanguages"] = suppLangs; + insertAndUpdateProjectFile("language", targetObj); +} + +QString QmlProjectItem::primaryLanguage() const +{ + return m_project["language"].toObject()["primaryLanguage"].toString(); +} + +void QmlProjectItem::setPrimaryLanguage(const QString &language) +{ + QJsonObject targetObj = m_project["language"].toObject(); + targetObj["primaryLanguage"] = language; + insertAndUpdateProjectFile("language", targetObj); +} + +Utils::FilePaths QmlProjectItem::files() const +{ + QSet filesSet; + for (const auto &fileFilter : m_content) { + const QStringList fileList = fileFilter->files(); + for (const QString &file : fileList) { + filesSet.insert(file); + } + } + + Utils::FilePaths files; + for (const auto &fileName : filesSet) { + files.append(Utils::FilePath::fromString(fileName)); + } + + return files; +} + +/** + Check whether the project would include a file path + - regardless whether the file already exists or not. + + @param filePath: absolute file path to check + */ +bool QmlProjectItem::matchesFile(const QString &filePath) const +{ + return Utils::contains(m_content, [&filePath](const auto &fileFilter) { + return fileFilter->matchesFile(filePath); + }); +} + +bool QmlProjectItem::forceFreeType() const +{ + return m_project["runConfig"].toObject()["forceFreeType"].toBool(); +} + +void QmlProjectItem::setForceFreeType(const bool &isForced) +{ + QJsonObject runConfig = m_project["runConfig"].toObject(); + runConfig["forceFreeType"] = isForced; + insertAndUpdateProjectFile("runConfig", runConfig); +} + +void QmlProjectItem::setMainFile(const QString &mainFile) +{ + QJsonObject runConfig = m_project["runConfig"].toObject(); + runConfig["mainFile"] = mainFile; + insertAndUpdateProjectFile("runConfig", runConfig); +} + +QString QmlProjectItem::mainFile() const +{ + return m_project["runConfig"].toObject()["mainFile"].toString(); +} + +Utils::FilePath QmlProjectItem::mainFilePath() const +{ + return m_projectFile; +} + +void QmlProjectItem::setMainUiFile(const QString &mainUiFile) +{ + QJsonObject runConfig = m_project["runConfig"].toObject(); + runConfig["mainUiFile"] = mainUiFile; + insertAndUpdateProjectFile("runConfig", runConfig); +} + +QString QmlProjectItem::mainUiFile() const +{ + return m_project["runConfig"].toObject()["mainUiFile"].toString(); +} + +Utils::FilePath QmlProjectItem::mainUiFilePath() const +{ + return Utils::FilePath::fromString(m_project["runConfig"].toObject()["mainUiFile"].toString()); +} + +bool QmlProjectItem::widgetApp() const +{ + return m_project["runConfig"].toObject()["widgetApp"].toBool(); +} + +void QmlProjectItem::setWidgetApp(const bool &widgetApp) +{ + QJsonObject runConfig = m_project["runConfig"].toObject(); + runConfig["widgetApp"] = widgetApp; + insertAndUpdateProjectFile("runConfig", runConfig); +} + +QStringList QmlProjectItem::shaderToolArgs() const +{ + return m_project["shaderTool"].toObject()["args"].toVariant().toStringList(); +} + +void QmlProjectItem::setShaderToolArgs(const QStringList &args) +{ + QJsonObject shaderTool = m_project["shaderTool"].toObject(); + shaderTool["args"] = QJsonArray::fromStringList(args); + insertAndUpdateProjectFile("shaderTool", shaderTool); +} + +void QmlProjectItem::addShaderToolArg(const QString &arg) +{ + QJsonObject targetObj = m_project["shaderTool"].toObject(); + QJsonArray toolArgs = targetObj["args"].toArray(); + + if (toolArgs.contains(arg)) + return; + + toolArgs.append(arg); + targetObj["args"] = toolArgs; + insertAndUpdateProjectFile("shaderTool", targetObj); +} + +QStringList QmlProjectItem::shaderToolFiles() const +{ + return m_project.value("shaderTool").toObject().value("files").toVariant().toStringList(); +} + +void QmlProjectItem::setShaderToolFiles(const QStringList &files) +{ + QJsonObject shaderTool = m_project["shaderTool"].toObject(); + shaderTool["files"] = QJsonArray::fromStringList(files); + insertAndUpdateProjectFile("shaderTool", shaderTool); +} + +void QmlProjectItem::addShaderToolFile(const QString &file) +{ + QJsonObject targetObj = m_project["shaderTool"].toObject(); + QJsonArray toolArgs = targetObj["files"].toArray(); + + if (toolArgs.contains(file)) + return; + + toolArgs.append(file); + targetObj["files"] = toolArgs; + insertAndUpdateProjectFile("shaderTool", targetObj); +} + +void QmlProjectItem::insertAndUpdateProjectFile(const QString &key, const QJsonValue &value) +{ + m_project[key] = value; +#ifndef TESTS_ENABLED_QMLPROJECTITEM + m_projectFile.writeFileContents(Converters::jsonToQmlProject(m_project).toUtf8()); +#endif +} + +} // namespace QmlProjectManager diff --git a/src/plugins/qmlprojectmanager/buildsystem/projectitem/qmlprojectitem.h b/src/plugins/qmlprojectmanager/buildsystem/projectitem/qmlprojectitem.h new file mode 100644 index 00000000000..48e389db08d --- /dev/null +++ b/src/plugins/qmlprojectmanager/buildsystem/projectitem/qmlprojectitem.h @@ -0,0 +1,108 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0 + +#pragma once + +#include "filefilteritems.h" + +#include + +#include +#include +#include +#include +#include + +#include +#include + +namespace QmlJS { +class SimpleReaderNode; +} + +namespace QmlProjectManager { + +class QmlProjectItem : public QObject +{ + Q_OBJECT +public: + explicit QmlProjectItem(const Utils::FilePath &filePath); + + bool isQt4McuProject() const; + bool isQt6Project() const; + + Utils::FilePath sourceDirectory() const; + QString targetDirectory() const; + + QStringList importPaths() const; + void setImportPaths(const QStringList &paths); + void addImportPath(const QString &importPath); + + QStringList fileSelectors() const; + void setFileSelectors(const QStringList &selectors); + void addFileSelector(const QString &selector); + + bool multilanguageSupport() const; + void setMultilanguageSupport(const bool &isEnabled); + + QStringList supportedLanguages() const; + void setSupportedLanguages(const QStringList &languages); + void addSupportedLanguage(const QString &language); + + QString primaryLanguage() const; + void setPrimaryLanguage(const QString &language); + + Utils::FilePaths files() const; + bool matchesFile(const QString &filePath) const; + + bool forceFreeType() const; + void setForceFreeType(const bool &isForced); + + void setMainFile(const QString &mainFile); + QString mainFile() const; + Utils::FilePath mainFilePath() const; + + void setMainUiFile(const QString &mainUiFile); + QString mainUiFile() const; + Utils::FilePath mainUiFilePath() const; + + bool widgetApp() const; + void setWidgetApp(const bool &widgetApp); + + QStringList shaderToolArgs() const; + void setShaderToolArgs(const QStringList &args); + void addShaderToolArg(const QString &arg); + + QStringList shaderToolFiles() const; + void setShaderToolFiles(const QStringList &files); + void addShaderToolFile(const QString &file); + + Utils::EnvironmentItems environment() const; + void addToEnviroment(const QString &key, const QString &value); + + QJsonObject project() const; + +signals: + void qmlFilesChanged(const QSet &, const QSet &); + +private: + typedef QSharedPointer ShrdPtrQPI; + typedef std::unique_ptr UnqPtrFFBI; + typedef std::unique_ptr UnqPtrFFI; + + // files & props + std::vector> m_content; // content property + + // runtime variables + Utils::FilePath m_projectFile; // design studio project file + QJsonObject m_project; // root project object + + // initializing functions + bool initProjectObject(); + void setupFileFilters(); + + // file update functions + void insertAndUpdateProjectFile(const QString &key, const QJsonValue &value); +}; + +} // namespace QmlProjectManager diff --git a/src/plugins/qmlprojectmanager/qmlprojectnodes.cpp b/src/plugins/qmlprojectmanager/buildsystem/projectnode/qmlprojectnodes.cpp similarity index 89% rename from src/plugins/qmlprojectmanager/qmlprojectnodes.cpp rename to src/plugins/qmlprojectmanager/buildsystem/projectnode/qmlprojectnodes.cpp index a561758acfa..78b03ea7bbd 100644 --- a/src/plugins/qmlprojectmanager/qmlprojectnodes.cpp +++ b/src/plugins/qmlprojectmanager/buildsystem/projectnode/qmlprojectnodes.cpp @@ -10,8 +10,7 @@ using namespace ProjectExplorer; -namespace QmlProjectManager { -namespace Internal { +namespace QmlProjectManager::Internal { QmlProjectNode::QmlProjectNode(Project *project) : ProjectNode(project->projectDirectory()) @@ -21,5 +20,4 @@ QmlProjectNode::QmlProjectNode(Project *project) setIcon(DirectoryIcon(":/projectexplorer/images/fileoverlay_qml.png")); } -} // namespace Internal } // namespace QmlProjectManager diff --git a/src/plugins/qmlprojectmanager/qmlprojectnodes.h b/src/plugins/qmlprojectmanager/buildsystem/projectnode/qmlprojectnodes.h similarity index 100% rename from src/plugins/qmlprojectmanager/qmlprojectnodes.h rename to src/plugins/qmlprojectmanager/buildsystem/projectnode/qmlprojectnodes.h diff --git a/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.cpp b/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.cpp new file mode 100644 index 00000000000..6184c9f4816 --- /dev/null +++ b/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.cpp @@ -0,0 +1,544 @@ +// Copyright (C) 2023 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 "qmlbuildsystem.h" +#include "qmlprojectconstants.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "projectitem/qmlprojectitem.h" +#include "projectnode/qmlprojectnodes.h" + +#include "utils/algorithm.h" +#include "utils/qtcassert.h" + +#include "texteditor/textdocument.h" + +using namespace ProjectExplorer; +namespace QmlProjectManager { + +namespace { +Q_LOGGING_CATEGORY(infoLogger, "QmlProjectManager.QmlBuildSystem", QtInfoMsg) +} + +QmlBuildSystem::QmlBuildSystem(Target *target) + : BuildSystem(target) +{ + // refresh first - project information is used e.g. to decide the default RC's + refresh(RefreshOptions::Project); + + updateDeploymentData(); + registerMenuButtons(); + + connect(target->project(), &Project::activeTargetChanged, [this]() { refresh(RefreshOptions::NoFileRefresh); }); + connect(target->project(), &Project::projectFileIsDirty, [this]() { + refresh(RefreshOptions::Project); + }); + + // FIXME: Check. Probably bogus after the BuildSystem move. + // // addedTarget calls updateEnabled on the runconfigurations + // // which needs to happen after refresh + // foreach (Target *t, targets()) + // addedTarget(t); +} + +void QmlBuildSystem::updateDeploymentData() +{ + if (!m_projectItem) + return; + + if (DeviceTypeKitAspect::deviceTypeId(kit()) + == ProjectExplorer::Constants::DESKTOP_DEVICE_TYPE) { + return; + } + + ProjectExplorer::DeploymentData deploymentData; + for (const auto &file : m_projectItem->files()) { + deploymentData.addFile(file, targetFile(file).parentDir().toString()); + } + + setDeploymentData(deploymentData); +} + +void QmlBuildSystem::registerMenuButtons() +{ + Core::ActionContainer *menu = Core::ActionManager::actionContainer(Core::Constants::M_FILE); + + // QML Project file update button + // This button saves the current configuration into the .qmlproject file + auto action = new QAction("Update QmlProject File", this); + Core::Command *cmd = Core::ActionManager::registerAction(action, "QmlProject.ProjectManager"); + menu->addAction(cmd, Core::Constants::G_FILE_SAVE); + QObject::connect(action, &QAction::triggered, this, &QmlBuildSystem::updateProjectFile); +} + +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!"; + return false; + } + + QTextStream ts(&file); + // License + ts << "/* " + "File generated by Qt Creator" + "Copyright (C) 2016 The Qt Company Ltd." + "SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH " + "Qt-GPL-exception-1.0" + "*/" + << Qt::endl + << Qt::endl; + + // Components + ts << "import QmlProject 1.1" << Qt::endl << Qt::endl; + + return true; +} + +void QmlBuildSystem::triggerParsing() +{ + refresh(RefreshOptions::Project); +} + +Utils::FilePath QmlBuildSystem::canonicalProjectDir() const +{ + return BuildSystem::target() + ->project() + ->projectFilePath() + .canonicalPath() + .normalizedPathName() + .parentDir(); +} + +void QmlBuildSystem::refresh(RefreshOptions options) +{ + ParseGuard guard = guardParsingRun(); + switch (options) { + case RefreshOptions::NoFileRefresh: + break; + case RefreshOptions::Project: + initProjectItem(); + case RefreshOptions::Files: + parseProjectFiles(); + } + + auto modelManager = QmlJS::ModelManagerInterface::instance(); + if (!modelManager) + return; + + QmlJS::ModelManagerInterface::ProjectInfo projectInfo + = modelManager->defaultProjectInfoForProject(project(), + project()->files(Project::HiddenRccFolders)); + const QStringList searchPaths = makeAbsolute(canonicalProjectDir(), customImportPaths()); + for (const QString &searchPath : searchPaths) + projectInfo.importPaths.maybeInsert(Utils::FilePath::fromString(searchPath), + QmlJS::Dialect::Qml); + + modelManager->updateProjectInfo(projectInfo, project()); + + guard.markAsSuccess(); + + emit projectChanged(); +} + +void QmlBuildSystem::initProjectItem() +{ + m_projectItem.reset(new QmlProjectItem{projectFilePath()}); + connect(m_projectItem.get(), + &QmlProjectItem::qmlFilesChanged, + this, + &QmlBuildSystem::refreshFiles); +} + +void QmlBuildSystem::parseProjectFiles() +{ + if (auto modelManager = QmlJS::ModelManagerInterface::instance()) { + modelManager->updateSourceFiles(m_projectItem->files(), true); + } + + + Utils::FilePath mainFilePath{Utils::FilePath::fromString(m_projectItem->mainFile())}; + if (!mainFilePath.isEmpty()) { + mainFilePath = canonicalProjectDir().resolvePath(m_projectItem->mainFile()); + Utils::FileReader reader; + QString errorMessage; + if (!reader.fetch(mainFilePath, &errorMessage)) { + Core::MessageManager::writeFlashing( + tr("Warning while loading project file %1.").arg(projectFilePath().toUserOutput())); + Core::MessageManager::writeSilently(errorMessage); + } + } + + generateProjectTree(); +} + +void QmlBuildSystem::generateProjectTree() +{ + auto newRoot = std::make_unique(project()); + + for (const auto &file : m_projectItem->files()) { + 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)); + updateDeploymentData(); +} + +bool QmlBuildSystem::setFileSettingInProjectFile(const QString &setting, + const Utils::FilePath &mainFilePath, + const QString &oldFile) +{ + // make sure to change it also in the qmlproject file + const Utils::FilePath qmlProjectFilePath = project()->projectFilePath(); + Core::FileChangeBlocker fileChangeBlocker(qmlProjectFilePath); + const QList editors = Core::DocumentModel::editorsForFilePath( + qmlProjectFilePath); + TextEditor::TextDocument *document = nullptr; + if (!editors.isEmpty()) { + document = qobject_cast(editors.first()->document()); + if (document && document->isModified()) + if (!Core::DocumentManager::saveDocument(document)) + return false; + } + + QString fileContent; + QString error; + Utils::TextFileFormat textFileFormat; + const QTextCodec *codec = QTextCodec::codecForName("UTF-8"); // qml files are defined to be utf-8 + Utils::TextFileFormat::ReadResult readResult = Utils::TextFileFormat::readFile(qmlProjectFilePath, + codec, + &fileContent, + &textFileFormat, + &error); + if (readResult != Utils::TextFileFormat::ReadSuccess) { + qWarning() << "Failed to read file" << qmlProjectFilePath << ":" << error; + } + + const QString settingQmlCode = setting + ":"; + + const Utils::FilePath projectDir = project()->projectFilePath().parentDir(); + const QString relativePath = mainFilePath.relativeChildPath(projectDir).path(); + + if (fileContent.indexOf(settingQmlCode) < 0) { + QString addedText = QString("\n %1 \"%2\"\n").arg(settingQmlCode).arg(relativePath); + auto index = fileContent.lastIndexOf("}"); + fileContent.insert(index, addedText); + } else { + QString originalFileName = oldFile; + originalFileName.replace(".", "\\."); + const QRegularExpression expression( + QString("%1\\s*\"(%2)\"").arg(settingQmlCode).arg(originalFileName)); + + const QRegularExpressionMatch match = expression.match(fileContent); + + fileContent.replace(match.capturedStart(1), match.capturedLength(1), relativePath); + } + + if (!textFileFormat.writeFile(qmlProjectFilePath, fileContent, &error)) + qWarning() << "Failed to write file" << qmlProjectFilePath << ":" << error; + + refresh(RefreshOptions::Project); + return true; +} + +bool QmlBuildSystem::blockFilesUpdate() const +{ + return m_blockFilesUpdate; +} + +void QmlBuildSystem::setBlockFilesUpdate(bool newBlockFilesUpdate) +{ + m_blockFilesUpdate = newBlockFilesUpdate; +} + +Utils::FilePath QmlBuildSystem::mainFilePath() const +{ + return projectDirectory().pathAppended(mainFile()); +} + +Utils::FilePath QmlBuildSystem::mainUiFilePath() const +{ + return m_projectItem->mainUiFilePath(); +} + +bool QmlBuildSystem::setMainFileInProjectFile(const Utils::FilePath &newMainFilePath) +{ + return setFileSettingInProjectFile("mainFile", newMainFilePath, mainFile()); +} + +bool QmlBuildSystem::setMainUiFileInProjectFile(const Utils::FilePath &newMainUiFilePath) +{ + return setMainUiFileInMainFile(newMainUiFilePath) + && setFileSettingInProjectFile("mainUiFile", newMainUiFilePath, m_projectItem->mainUiFile()); +} + +bool QmlBuildSystem::setMainUiFileInMainFile(const Utils::FilePath &newMainUiFilePath) +{ + Core::FileChangeBlocker fileChangeBlocker(mainFilePath()); + const QList editors = Core::DocumentModel::editorsForFilePath(mainFilePath()); + TextEditor::TextDocument *document = nullptr; + if (!editors.isEmpty()) { + document = qobject_cast(editors.first()->document()); + if (document && document->isModified()) + if (!Core::DocumentManager::saveDocument(document)) + return false; + } + + QString fileContent; + QString error; + Utils::TextFileFormat textFileFormat; + const QTextCodec *codec = QTextCodec::codecForName("UTF-8"); // qml files are defined to be utf-8 + if (Utils::TextFileFormat::readFile(mainFilePath(), codec, &fileContent, &textFileFormat, &error) + != Utils::TextFileFormat::ReadSuccess) { + qWarning() << "Failed to read file" << mainFilePath() << ":" << error; + } + + const QString currentMain = QString("%1 {").arg(mainUiFilePath().baseName()); + const QString newMain = QString("%1 {").arg(newMainUiFilePath.baseName()); + + if (fileContent.contains(currentMain)) + fileContent.replace(currentMain, newMain); + + if (!textFileFormat.writeFile(mainFilePath(), fileContent, &error)) + qWarning() << "Failed to write file" << mainFilePath() << ":" << error; + + return true; +} + +Utils::FilePath QmlBuildSystem::targetDirectory() const +{ + if (DeviceTypeKitAspect::deviceTypeId(kit()) == ProjectExplorer::Constants::DESKTOP_DEVICE_TYPE) + return canonicalProjectDir(); + + return m_projectItem ? Utils::FilePath::fromString(m_projectItem->targetDirectory()) + : Utils::FilePath(); +} + +Utils::FilePath QmlBuildSystem::targetFile(const Utils::FilePath &sourceFile) const +{ + const QDir sourceDir(m_projectItem ? m_projectItem->sourceDirectory().path() + : canonicalProjectDir().toString()); + const QDir targetDir(targetDirectory().toString()); + const QString relative = sourceDir.relativeFilePath(sourceFile.toString()); + return Utils::FilePath::fromString(QDir::cleanPath(targetDir.absoluteFilePath(relative))); +} + +void QmlBuildSystem::setSupportedLanguages(QStringList languages) +{ + m_projectItem->setSupportedLanguages(languages); +} + +void QmlBuildSystem::setPrimaryLanguage(QString language) +{ + m_projectItem->setPrimaryLanguage(language); +} + +QStringList QmlBuildSystem::makeAbsolute(const Utils::FilePath &path, + const QStringList &relativePaths) +{ + if (path.isEmpty()) + return relativePaths; + + const QDir baseDir(path.toString()); + return Utils::transform(relativePaths, [&baseDir](const QString &path) { + return QDir::cleanPath(baseDir.absoluteFilePath(path)); + }); +} + +void QmlBuildSystem::refreshFiles(const QSet & /*added*/, const QSet &removed) +{ + if (m_blockFilesUpdate) { + qCDebug(infoLogger) << "Auto files refresh blocked."; + return; + } + refresh(RefreshOptions::Files); + if (!removed.isEmpty()) { + if (auto modelManager = QmlJS::ModelManagerInterface::instance()) { + modelManager->removeFiles( + Utils::transform>(removed, [](const QString &s) { + return Utils::FilePath::fromString(s); + })); + } + } + updateDeploymentData(); +} + +QVariant QmlBuildSystem::additionalData(Utils::Id id) const +{ + if (id == Constants::customFileSelectorsData) + return customFileSelectors(); + if (id == Constants::supportedLanguagesData) + return supportedLanguages(); + if (id == Constants::primaryLanguageData) + return primaryLanguage(); + if (id == Constants::customForceFreeTypeData) + return forceFreeType(); + if (id == Constants::customQtForMCUs) + return qtForMCUs(); + if (id == Constants::customQt6Project) + return qt6Project(); + if (id == Constants::mainFilePath) + return mainFilePath().toString(); + if (id == Constants::customImportPaths) + return customImportPaths(); + if (id == Constants::canonicalProjectDir) + return canonicalProjectDir().toString(); + return {}; +} + +bool QmlBuildSystem::supportsAction(Node *context, ProjectAction action, const Node *node) const +{ + if (dynamic_cast(context)) { + if (action == AddNewFile || action == EraseFile) + return true; + QTC_ASSERT(node, return false); + + if (action == Rename && node->asFileNode()) { + const FileNode *fileNode = node->asFileNode(); + QTC_ASSERT(fileNode, return false); + return fileNode->fileType() != FileType::Project; + } + return false; + } + return BuildSystem::supportsAction(context, action, node); +} + +bool QmlBuildSystem::addFiles(Node *context, const Utils::FilePaths &filePaths, Utils::FilePaths *) +{ + if (!dynamic_cast(context)) + return false; + + Utils::FilePaths toAdd; + for (const Utils::FilePath &filePath : filePaths) { + if (!m_projectItem->matchesFile(filePath.toString())) + toAdd << filePaths; + } + return toAdd.isEmpty(); +} + +bool QmlBuildSystem::deleteFiles(Node *context, const Utils::FilePaths &filePaths) +{ + if (dynamic_cast(context)) + return true; + + return BuildSystem::deleteFiles(context, filePaths); +} + +bool QmlBuildSystem::renameFile(Node *context, + const Utils::FilePath &oldFilePath, + const Utils::FilePath &newFilePath) +{ + if (dynamic_cast(context)) { + if (oldFilePath.endsWith(mainFile())) + return setMainFileInProjectFile(newFilePath); + if (oldFilePath.endsWith(m_projectItem->mainUiFile())) + return setMainUiFileInProjectFile(newFilePath); + return true; + } + + return BuildSystem::renameFile(context, oldFilePath, newFilePath); +} + +QString QmlBuildSystem::mainFile() const +{ + return m_projectItem->mainFile(); +} + +bool QmlBuildSystem::qtForMCUs() const +{ + return m_projectItem->isQt4McuProject(); +} + +bool QmlBuildSystem::qt6Project() const +{ + return m_projectItem->isQt6Project(); +} + +Utils::EnvironmentItems QmlBuildSystem::environment() const +{ + return m_projectItem->environment(); +} + +QStringList QmlBuildSystem::customImportPaths() const +{ + return m_projectItem->importPaths(); +} + +QStringList QmlBuildSystem::customFileSelectors() const +{ + return m_projectItem->fileSelectors(); +} + +bool QmlBuildSystem::multilanguageSupport() const +{ + return m_projectItem->multilanguageSupport(); +} + +QStringList QmlBuildSystem::supportedLanguages() const +{ + return m_projectItem->supportedLanguages(); +} + +QString QmlBuildSystem::primaryLanguage() const +{ + return m_projectItem->primaryLanguage(); +} + +bool QmlBuildSystem::forceFreeType() const +{ + return m_projectItem->forceFreeType(); +} + +bool QmlBuildSystem::widgetApp() const +{ + return m_projectItem->widgetApp(); +} + +QStringList QmlBuildSystem::shaderToolArgs() const +{ + return m_projectItem->shaderToolArgs(); +} + +QStringList QmlBuildSystem::shaderToolFiles() const +{ + return m_projectItem->shaderToolFiles(); +} + +QStringList QmlBuildSystem::importPaths() const +{ + return m_projectItem->importPaths(); +} + +Utils::FilePaths QmlBuildSystem::files() const +{ + return m_projectItem->files(); +} + +} // namespace QmlProjectManager diff --git a/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.h b/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.h new file mode 100644 index 00000000000..d45658d98ba --- /dev/null +++ b/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.h @@ -0,0 +1,115 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0 +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0 + +#pragma once + +#include "../qmlprojectmanager_global.h" +#include + +namespace QmlProjectManager { + +class QmlProject; +class QmlProjectItem; +class QmlProjectFile; + +class QMLPROJECTMANAGER_EXPORT QmlBuildSystem : public ProjectExplorer::BuildSystem +{ + Q_OBJECT + +public: + explicit QmlBuildSystem(ProjectExplorer::Target *target); + ~QmlBuildSystem() = default; + + void triggerParsing() final; + + bool supportsAction(ProjectExplorer::Node *context, + ProjectExplorer::ProjectAction action, + const ProjectExplorer::Node *node) const override; + bool addFiles(ProjectExplorer::Node *context, + const Utils::FilePaths &filePaths, + Utils::FilePaths *notAdded = nullptr) override; + bool deleteFiles(ProjectExplorer::Node *context, const Utils::FilePaths &filePaths) override; + bool renameFile(ProjectExplorer::Node *context, + const Utils::FilePath &oldFilePath, + const Utils::FilePath &newFilePath) override; + + bool updateProjectFile(); + + QString name() const override { return QLatin1String("qml"); } + + QmlProject *qmlProject() const; + + QVariant additionalData(Utils::Id id) const override; + + enum class RefreshOptions { + NoFileRefresh, + Files, + Project, + }; + + void refresh(RefreshOptions options); + + bool setMainFileInProjectFile(const Utils::FilePath &newMainFilePath); + bool setMainUiFileInProjectFile(const Utils::FilePath &newMainUiFilePath); + bool setMainUiFileInMainFile(const Utils::FilePath &newMainUiFilePath); + + Utils::FilePath canonicalProjectDir() const; + QString mainFile() const; + Utils::FilePath mainFilePath() const; + Utils::FilePath mainUiFilePath() const; + + bool qtForMCUs() const; + bool qt6Project() const; + + Utils::FilePath targetDirectory() const; + Utils::FilePath targetFile(const Utils::FilePath &sourceFile) const; + + Utils::EnvironmentItems environment() const; + QStringList customImportPaths() const; + QStringList customFileSelectors() const; + bool multilanguageSupport() const; + QStringList supportedLanguages() const; + void setSupportedLanguages(QStringList languages); + QString primaryLanguage() const; + void setPrimaryLanguage(QString language); + bool forceFreeType() const; + bool widgetApp() const; + QStringList shaderToolArgs() const; + QStringList shaderToolFiles() const; + QStringList importPaths() const; + Utils::FilePaths files() const; + + bool addFiles(const QStringList &filePaths); + void refreshProjectFile(); + + static Utils::FilePath activeMainFilePath(); + static QStringList makeAbsolute(const Utils::FilePath &path, const QStringList &relativePaths); + + void refreshFiles(const QSet &added, const QSet &removed); + + bool blockFilesUpdate() const; + void setBlockFilesUpdate(bool newBlockFilesUpdate); + +signals: + void projectChanged(); + +private: + bool setFileSettingInProjectFile(const QString &setting, + const Utils::FilePath &mainFilePath, + const QString &oldFile); + + QSharedPointer m_projectItem; + bool m_blockFilesUpdate = false; + + void initProjectItem(); + void parseProjectFiles(); + void generateProjectTree(); + + void registerMenuButtons(); + void updateDeploymentData(); + friend class FilesUpdateBlocker; +}; + +} // namespace QmlProjectManager diff --git a/src/plugins/qmlprojectmanager/fileformat/qmlprojectfileformat.cpp b/src/plugins/qmlprojectmanager/fileformat/qmlprojectfileformat.cpp deleted file mode 100644 index 27e5809f943..00000000000 --- a/src/plugins/qmlprojectmanager/fileformat/qmlprojectfileformat.cpp +++ /dev/null @@ -1,198 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "qmlprojectfileformat.h" - -#include "filefilteritems.h" -#include "qmlprojectitem.h" -#include "../qmlprojectmanagertr.h" - -#include - -#include - -#include -#include - -#include - -using namespace Utils; - -enum { - debug = false -}; - -namespace { - -std::unique_ptr setupFileFilterItem( - std::unique_ptr fileFilterItem, - const QmlJS::SimpleReaderNode::Ptr &node) -{ - const auto directoryProperty = node->property(QLatin1String("directory")); - if (directoryProperty.isValid()) - fileFilterItem->setDirectory(directoryProperty.value.toString()); - - const auto recursiveProperty = node->property(QLatin1String("recursive")); - if (recursiveProperty.isValid()) - fileFilterItem->setRecursive(recursiveProperty.value.toBool()); - - const auto pathsProperty = node->property(QLatin1String("paths")); - if (pathsProperty.isValid()) - fileFilterItem->setPathsProperty(pathsProperty.value.toStringList()); - - // "paths" and "files" have the same functionality - const auto filesProperty = node->property(QLatin1String("files")); - if (filesProperty.isValid()) - fileFilterItem->setPathsProperty(filesProperty.value.toStringList()); - - const auto filterProperty = node->property(QLatin1String("filter")); - if (filterProperty.isValid()) - fileFilterItem->setFilter(filterProperty.value.toString()); - - if (debug) - qDebug() << "directory:" << directoryProperty.value << "recursive" << recursiveProperty.value - << "paths" << pathsProperty.value << "files" << filesProperty.value; - return fileFilterItem; -} - -} //namespace - -namespace QmlProjectManager { - -std::unique_ptr QmlProjectFileFormat::parseProjectFile(const Utils::FilePath &fileName, - QString *errorMessage) -{ - QmlJS::SimpleReader simpleQmlJSReader; - - const QmlJS::SimpleReaderNode::Ptr rootNode = simpleQmlJSReader.readFile(fileName.toString()); - - if (!simpleQmlJSReader.errors().isEmpty() || !rootNode->isValid()) { - qWarning() << "unable to parse:" << fileName; - qWarning() << simpleQmlJSReader.errors(); - if (errorMessage) - *errorMessage = simpleQmlJSReader.errors().join(QLatin1String(", ")); - return nullptr; - } - - if (rootNode->name() == QLatin1String("Project")) { - auto projectItem = std::make_unique(); - - const auto mainFileProperty = rootNode->property(QLatin1String("mainFile")); - if (mainFileProperty.isValid()) - projectItem->setMainFile(mainFileProperty.value.toString()); - - const auto mainUiFileProperty = rootNode->property(QLatin1String("mainUiFile")); - if (mainUiFileProperty.isValid()) - projectItem->setMainUiFile(mainUiFileProperty.value.toString()); - - const auto importPathsProperty = rootNode->property(QLatin1String("importPaths")); - if (importPathsProperty.isValid()) { - QStringList list = importPathsProperty.value.toStringList(); - list.removeAll("."); - projectItem->setImportPaths(list); - } - - const auto fileSelectorsProperty = rootNode->property(QLatin1String("fileSelectors")); - if (fileSelectorsProperty.isValid()) - projectItem->setFileSelectors(fileSelectorsProperty.value.toStringList()); - - const auto multilanguageSupportProperty = rootNode->property( - QLatin1String("multilanguageSupport")); - if (multilanguageSupportProperty.isValid()) - projectItem->setMultilanguageSupport(multilanguageSupportProperty.value.toBool()); - - const auto languagesProperty = rootNode->property(QLatin1String("supportedLanguages")); - if (languagesProperty.isValid()) - projectItem->setSupportedLanguages(languagesProperty.value.toStringList()); - - const auto primaryLanguageProperty = rootNode->property(QLatin1String("primaryLanguage")); - if (primaryLanguageProperty.isValid()) - projectItem->setPrimaryLanguage(primaryLanguageProperty.value.toString()); - - const auto forceFreeTypeProperty = rootNode->property("forceFreeType"); - if (forceFreeTypeProperty.isValid()) - projectItem->setForceFreeType(forceFreeTypeProperty.value.toBool()); - - const auto targetDirectoryPropery = rootNode->property("targetDirectory"); - if (targetDirectoryPropery.isValid()) - projectItem->setTargetDirectory(FilePath::fromSettings(targetDirectoryPropery.value)); - - const auto qtForMCUProperty = rootNode->property("qtForMCUs"); - if (qtForMCUProperty.isValid() && qtForMCUProperty.value.toBool()) - projectItem->setQtForMCUs(qtForMCUProperty.value.toBool()); - - const auto qt6ProjectProperty = rootNode->property("qt6Project"); - if (qt6ProjectProperty.isValid() && qt6ProjectProperty.value.toBool()) - projectItem->setQt6Project(qt6ProjectProperty.value.toBool()); - - const auto widgetAppProperty = rootNode->property("widgetApp"); - if (widgetAppProperty.isValid()) - projectItem->setWidgetApp(widgetAppProperty.value.toBool()); - - if (debug) - qDebug() << "importPath:" << importPathsProperty.value << "mainFile:" << mainFileProperty.value; - - const QList childNodes = rootNode->children(); - for (const QmlJS::SimpleReaderNode::Ptr &childNode : childNodes) { - if (debug) - qDebug() << "reading type:" << childNode->name(); - - if (childNode->name() == QLatin1String("QmlFiles")) { - projectItem->appendContent( - setupFileFilterItem(std::make_unique("*.qml"), childNode)); - } else if (childNode->name() == QLatin1String("JavaScriptFiles")) { - projectItem->appendContent( - setupFileFilterItem(std::make_unique("*.js"), childNode)); - } else if (childNode->name() == QLatin1String("ImageFiles")) { - projectItem->appendContent( - setupFileFilterItem(std::make_unique(), childNode)); - } else if (childNode->name() == QLatin1String("CssFiles")) { - projectItem->appendContent( - setupFileFilterItem(std::make_unique("*.css"), childNode)); - } else if (childNode->name() == QLatin1String("FontFiles")) { - projectItem->appendContent( - setupFileFilterItem(std::make_unique("*.ttf;*.otf"), childNode)); - } else if (childNode->name() == QLatin1String("Files")) { - projectItem->appendContent( - setupFileFilterItem(std::make_unique(), childNode)); - } else if (childNode->name() == "Environment") { - const auto properties = childNode->properties(); - auto i = properties.constBegin(); - while (i != properties.constEnd()) { - projectItem->addToEnviroment(i.key(), i.value().value.toString()); - ++i; - } - } else if (childNode->name() == "ShaderTool") { - QmlJS::SimpleReaderNode::Property commandLine = childNode->property("args"); - if (commandLine.isValid()) { - const QStringList quotedArgs = commandLine.value.toString().split('\"'); - QStringList args; - for (int i = 0; i < quotedArgs.size(); ++i) { - // Each odd arg in this list is a single quoted argument, which we should - // not be split further - if (i % 2 == 0) - args.append(quotedArgs[i].trimmed().split(' ')); - else - args.append(quotedArgs[i]); - } - args.removeAll({}); - args.append("-o"); // Prepare for adding output file as next arg - projectItem->setShaderToolArgs(args); - } - QmlJS::SimpleReaderNode::Property files = childNode->property("files"); - if (files.isValid()) - projectItem->setShaderToolFiles(files.value.toStringList()); - } else { - qWarning() << "Unknown type:" << childNode->name(); - } - } - return projectItem; - } - - if (errorMessage) - *errorMessage = Tr::tr("Invalid root element: %1").arg(rootNode->name()); - - return nullptr; -} - -} // namespace QmlProjectManager diff --git a/src/plugins/qmlprojectmanager/fileformat/qmlprojectfileformat.h b/src/plugins/qmlprojectmanager/fileformat/qmlprojectfileformat.h deleted file mode 100644 index 72c77999711..00000000000 --- a/src/plugins/qmlprojectmanager/fileformat/qmlprojectfileformat.h +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include - -#include -#include - -namespace QmlProjectManager { - -class QmlProjectItem; - -class QmlProjectFileFormat -{ -public: - static std::unique_ptr parseProjectFile(const Utils::FilePath &fileName, - QString *errorMessage = nullptr); -}; - -} // namespace QmlProjectManager diff --git a/src/plugins/qmlprojectmanager/fileformat/qmlprojectitem.cpp b/src/plugins/qmlprojectmanager/fileformat/qmlprojectitem.cpp deleted file mode 100644 index 3cd7c28af24..00000000000 --- a/src/plugins/qmlprojectmanager/fileformat/qmlprojectitem.cpp +++ /dev/null @@ -1,118 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "qmlprojectitem.h" -#include "filefilteritems.h" - -#include - -#include - -using namespace Utils; - -namespace QmlProjectManager { - -// kind of initialization -void QmlProjectItem::setSourceDirectory(const FilePath &directoryPath) -{ - if (m_sourceDirectory == directoryPath) - return; - - m_sourceDirectory = directoryPath; - - for (auto &fileFilter : m_content) { - fileFilter->setDefaultDirectory(directoryPath.toFSPathString()); - connect(fileFilter.get(), - &FileFilterBaseItem::filesChanged, - this, - &QmlProjectItem::qmlFilesChanged); - } -} - -void QmlProjectItem::setTargetDirectory(const FilePath &directoryPath) -{ - m_targetDirectory = directoryPath; -} - -void QmlProjectItem::setQtForMCUs(bool b) -{ - m_qtForMCUs = b; -} - -void QmlProjectItem::setQt6Project(bool qt6Project) -{ - m_qt6Project = qt6Project; -} - -void QmlProjectItem::setImportPaths(const QStringList &importPaths) -{ - if (m_importPaths != importPaths) - m_importPaths = importPaths; -} - -void QmlProjectItem::setFileSelectors(const QStringList &selectors) -{ - if (m_fileSelectors != selectors) - m_fileSelectors = selectors; -} - -void QmlProjectItem::setMultilanguageSupport(const bool isEnabled) -{ - m_multilanguageSupport = isEnabled; -} - -void QmlProjectItem::setSupportedLanguages(const QStringList &languages) -{ - if (m_supportedLanguages != languages) - m_supportedLanguages = languages; -} - -void QmlProjectItem::setPrimaryLanguage(const QString &language) -{ - if (m_primaryLanguage != language) - m_primaryLanguage = language; -} - -/* Returns list of absolute paths */ -QStringList QmlProjectItem::files() const -{ - QSet files; - - for (const auto &fileFilter : m_content) { - const QStringList fileList = fileFilter->files(); - for (const QString &file : fileList) { - files.insert(file); - } - } - return Utils::toList(files); -} - -/** - Check whether the project would include a file path - - regardless whether the file already exists or not. - - @param filePath: absolute file path to check - */ -bool QmlProjectItem::matchesFile(const QString &filePath) const -{ - return Utils::contains(m_content, [&filePath](const auto &fileFilter) { - return fileFilter->matchesFile(filePath); - }); -} - -void QmlProjectItem::setForceFreeType(bool b) -{ - m_forceFreeType = b; -} - -Utils::EnvironmentItems QmlProjectItem::environment() const -{ - return m_environment; -} - -void QmlProjectItem::addToEnviroment(const QString &key, const QString &value) -{ - m_environment.append(Utils::EnvironmentItem(key, value)); -} - -} // namespace QmlProjectManager diff --git a/src/plugins/qmlprojectmanager/fileformat/qmlprojectitem.h b/src/plugins/qmlprojectmanager/fileformat/qmlprojectitem.h deleted file mode 100644 index f5ac618bf62..00000000000 --- a/src/plugins/qmlprojectmanager/fileformat/qmlprojectitem.h +++ /dev/null @@ -1,103 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include "filefilteritems.h" - -#include -#include - -#include -#include -#include - -#include -#include - -namespace QmlProjectManager { - -class QmlProjectItem : public QObject -{ - Q_OBJECT - -public: - const Utils::FilePath &sourceDirectory() const { return m_sourceDirectory; } - void setSourceDirectory(const Utils::FilePath &directoryPath); - const Utils::FilePath &targetDirectory() const { return m_targetDirectory; } - void setTargetDirectory(const Utils::FilePath &directoryPath); - - bool qtForMCUs() const { return m_qtForMCUs; } - void setQtForMCUs(bool qtForMCUs); - - bool qt6Project() const { return m_qt6Project; } - void setQt6Project(bool qt6Project); - - QStringList importPaths() const { return m_importPaths; } - void setImportPaths(const QStringList &paths); - - QStringList fileSelectors() const { return m_fileSelectors; } - void setFileSelectors(const QStringList &selectors); - - bool multilanguageSupport() const { return m_multilanguageSupport; } - void setMultilanguageSupport(const bool isEnabled); - - QStringList supportedLanguages() const { return m_supportedLanguages; } - void setSupportedLanguages(const QStringList &languages); - - QString primaryLanguage() const { return m_primaryLanguage; } - void setPrimaryLanguage(const QString &language); - - QStringList files() const; - bool matchesFile(const QString &filePath) const; - - bool forceFreeType() const { return m_forceFreeType; }; - void setForceFreeType(bool); - - QString mainFile() const { return m_mainFile; } - void setMainFile(const QString &mainFilePath) { m_mainFile = mainFilePath; } - - QString mainUiFile() const { return m_mainUiFile; } - void setMainUiFile(const QString &mainUiFilePath) { m_mainUiFile = mainUiFilePath; } - - bool widgetApp() const { return m_widgetApp; } - void setWidgetApp(bool widgetApp) { m_widgetApp = widgetApp; } - - QStringList shaderToolArgs() const { return m_shaderToolArgs; } - void setShaderToolArgs(const QStringList &args) {m_shaderToolArgs = args; } - - QStringList shaderToolFiles() const { return m_shaderToolFiles; } - void setShaderToolFiles(const QStringList &files) { m_shaderToolFiles = files; } - - void appendContent(std::unique_ptr item) - { - m_content.push_back(std::move(item)); - } - - Utils::EnvironmentItems environment() const; - void addToEnviroment(const QString &key, const QString &value); - -signals: - void qmlFilesChanged(const QSet &, const QSet &); - -protected: - Utils::FilePath m_sourceDirectory; - Utils::FilePath m_targetDirectory; - QStringList m_importPaths; - QStringList m_fileSelectors; - bool m_multilanguageSupport; - QStringList m_supportedLanguages; - QString m_primaryLanguage; - QString m_mainFile; - QString m_mainUiFile; - Utils::EnvironmentItems m_environment; - std::vector> m_content; // content property - bool m_forceFreeType = false; - bool m_qtForMCUs = false; - bool m_qt6Project = false; - bool m_widgetApp = false; - QStringList m_shaderToolArgs; - QStringList m_shaderToolFiles; -}; - -} // namespace QmlProjectManager diff --git a/src/plugins/qmlprojectmanager/qmlmainfileaspect.cpp b/src/plugins/qmlprojectmanager/qmlmainfileaspect.cpp index 279b48ccb1e..6944f8376a4 100644 --- a/src/plugins/qmlprojectmanager/qmlmainfileaspect.cpp +++ b/src/plugins/qmlprojectmanager/qmlmainfileaspect.cpp @@ -181,7 +181,7 @@ void QmlMainFileAspect::setScriptSource(MainScriptSource source, const QString & FilePath QmlMainFileAspect::mainScript() const { if (!qmlBuildSystem()->mainFile().isEmpty()) { - const FilePath pathInProject = qmlBuildSystem()->mainFile(); + const FilePath pathInProject = qmlBuildSystem()->mainFilePath(); return qmlBuildSystem()->canonicalProjectDir().resolvePath(pathInProject); } diff --git a/src/plugins/qmlprojectmanager/qmlproject.cpp b/src/plugins/qmlprojectmanager/qmlproject.cpp index 7c37cf8e7b7..6743c136049 100644 --- a/src/plugins/qmlprojectmanager/qmlproject.cpp +++ b/src/plugins/qmlprojectmanager/qmlproject.cpp @@ -1,554 +1,163 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0 #include "qmlproject.h" -#include "fileformat/qmlprojectfileformat.h" -#include "fileformat/qmlprojectitem.h" -#include "qmlprojectconstants.h" -#include "qmlprojectmanagerconstants.h" -#include "qmlprojectmanagertr.h" -#include "qmlprojectnodes.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 +#include +#include + +#include +#include +#include + +#include "projectexplorer/devicesupport/idevice.h" +#include "qmlprojectconstants.h" +#include "qmlprojectmanagerconstants.h" +#include "qmlprojectmanagertr.h" +#include "utils/algorithm.h" + using namespace Core; using namespace ProjectExplorer; -using namespace QmlProjectManager::Internal; -using namespace Utils; - -namespace { -Q_LOGGING_CATEGORY(infoLogger, "QmlProjectManager.QmlBuildSystem", QtInfoMsg) -} namespace QmlProjectManager { - -static int preferedQtTarget(Target *target) -{ - if (target) { - const QmlBuildSystem *buildSystem = qobject_cast(target->buildSystem()); - if (buildSystem && buildSystem->qt6Project()) - return 6; - } - return 5; -} - -static bool allowOnlySingleProject() -{ - QSettings *settings = Core::ICore::settings(); - const QString qdsAllowMultipleProjects = "QML/Designer/AllowMultipleProjects"; - - return !settings->value(qdsAllowMultipleProjects, false).toBool(); -} - -Utils::FilePaths QmlProject::collectUiQmlFilesForFolder(const Utils::FilePath &folder) const -{ - const Utils::FilePaths uiFiles = files([&](const ProjectExplorer::Node *node) { - return node->filePath().completeSuffix() == "ui.qml" - && node->filePath().parentDir() == folder; - }); - return uiFiles; -} - -FilePaths QmlProject::collectQmlFiles() const -{ - return files([](const Node *node) { return node->filePath().suffix() == "qml"; }); -} - QmlProject::QmlProject(const Utils::FilePath &fileName) : Project(QString::fromLatin1(Constants::QMLPROJECT_MIMETYPE), fileName) { setId(QmlProjectManager::Constants::QML_PROJECT_ID); - setProjectLanguages(Context(ProjectExplorer::Constants::QMLJS_LANGUAGE_ID)); + setProjectLanguages(Core::Context(ProjectExplorer::Constants::QMLJS_LANGUAGE_ID)); setDisplayName(fileName.completeBaseName()); setNeedsBuildConfigurations(false); setBuildSystemCreator([](Target *t) { return new QmlBuildSystem(t); }); - if (QmlProject::isQtDesignStudio()) { - if (allowOnlySingleProject()) { - EditorManager::closeAllDocuments(); - SessionManager::closeAllProjects(); - } - - m_openFileConnection - = connect(this, - &QmlProject::anyParsingFinished, - this, - [this](Target *target, bool success) { - if (m_openFileConnection) - disconnect(m_openFileConnection); - - if (target && success) { - - auto target = activeTarget(); - if (!target) - return; - - auto qmlBuildSystem = qobject_cast( - target->buildSystem()); - - const Utils::FilePath mainUiFile = qmlBuildSystem->mainUiFilePath(); - - if (mainUiFile.completeSuffix() == "ui.qml" && mainUiFile.exists()) { - QTimer::singleShot(1000, [mainUiFile]() { - Core::EditorManager::openEditor(mainUiFile, - Utils::Id()); - }); - } else { - Utils::FilePaths uiFiles = collectUiQmlFilesForFolder( - projectDirectory().pathAppended("content")); - if (uiFiles.isEmpty()) - uiFiles = collectUiQmlFilesForFolder(projectDirectory()); - - if (uiFiles.isEmpty()) - uiFiles = collectQmlFiles(); - - if (!uiFiles.isEmpty()) { - Utils::FilePath currentFile; - if (auto cd = Core::EditorManager::currentDocument()) - currentFile = cd->filePath(); - - if (currentFile.isEmpty() || !isKnownFile(currentFile)) - QTimer::singleShot(1000, [uiFiles]() { - Core::EditorManager::openEditor(uiFiles.first(), - Utils::Id()); - Core::ModeManager::activateMode( - Core::Constants::MODE_DESIGN); - }); - } - } - } - }); - } -} - -QmlBuildSystem::QmlBuildSystem(Target *target) - : BuildSystem(target) -{ - m_canonicalProjectDir = - target->project()->projectFilePath().canonicalPath().normalizedPathName().parentDir(); - - connect(target->project(), &Project::projectFileIsDirty, - this, &QmlBuildSystem::refreshProjectFile); - - // refresh first - project information is used e.g. to decide the default RC's - refresh(Everything); - -// FIXME: Check. Probably bogus after the BuildSystem move. -// // addedTarget calls updateEnabled on the runconfigurations -// // which needs to happen after refresh -// const QLis targetList = targets(); -// for (Target *t : targetList) -// addedTarget(t); - - connect(target->project(), &Project::activeTargetChanged, - this, &QmlBuildSystem::onActiveTargetChanged); - updateDeploymentData(); -} - -QmlBuildSystem::~QmlBuildSystem() = default; - -void QmlBuildSystem::triggerParsing() -{ - refresh(Everything); -} - -void QmlBuildSystem::onActiveTargetChanged(Target *) -{ - // make sure e.g. the default qml imports are adapted - refresh(Configuration); -} - -void QmlBuildSystem::onKitChanged() -{ - // make sure e.g. the default qml imports are adapted - refresh(Configuration); -} - -Utils::FilePath QmlBuildSystem::canonicalProjectDir() const -{ - return m_canonicalProjectDir; -} - -void QmlBuildSystem::parseProject(RefreshOptions options) -{ - if (options & Files) { - if (options & ProjectFile) - m_projectItem.reset(); - if (!m_projectItem) { - QString errorMessage; - m_projectItem = QmlProjectFileFormat::parseProjectFile(projectFilePath(), &errorMessage); - if (m_projectItem) { - connect(m_projectItem.get(), - &QmlProjectItem::qmlFilesChanged, - this, - &QmlBuildSystem::refreshFiles); - - } else { - MessageManager::writeFlashing( - Tr::tr("Error while loading project file %1.").arg(projectFilePath().toUserOutput())); - MessageManager::writeSilently(errorMessage); - } - } - if (m_projectItem) { - m_projectItem->setSourceDirectory(canonicalProjectDir()); - if (m_projectItem->targetDirectory().isEmpty()) - m_projectItem->setTargetDirectory(canonicalProjectDir()); - - if (auto modelManager = QmlJS::ModelManagerInterface::instance()) { - QStringList files = m_projectItem->files(); - modelManager - ->updateSourceFiles(Utils::transform(files, - [](const QString &p) { - return Utils::FilePath::fromString(p); - }), - true); - } - QString mainFilePath = m_projectItem->mainFile(); - if (!mainFilePath.isEmpty()) { - mainFilePath - = QDir(canonicalProjectDir().toString()).absoluteFilePath(mainFilePath); - Utils::FileReader reader; - QString errorMessage; - if (!reader.fetch(Utils::FilePath::fromString(mainFilePath), &errorMessage)) { - MessageManager::writeFlashing(Tr::tr("Warning while loading project file %1.") - .arg(projectFilePath().toUserOutput())); - MessageManager::writeSilently(errorMessage); - } - } - } - generateProjectTree(); - } - - if (options & Configuration) { - // update configuration - } -} - -bool QmlBuildSystem::setFileSettingInProjectFile(const QString &setting, - const FilePath &mainFilePath, - const FilePath &oldFile) -{ - // make sure to change it also in the qmlproject file - const Utils::FilePath qmlProjectFilePath = project()->projectFilePath(); - Core::FileChangeBlocker fileChangeBlocker(qmlProjectFilePath); - const QList editors = Core::DocumentModel::editorsForFilePath(qmlProjectFilePath); - TextEditor::TextDocument *document = nullptr; - if (!editors.isEmpty()) { - document = qobject_cast(editors.first()->document()); - if (document && document->isModified()) - if (!Core::DocumentManager::saveDocument(document)) - return false; - } - - QString fileContent; - QString error; - Utils::TextFileFormat textFileFormat; - const QTextCodec *codec = QTextCodec::codecForName("UTF-8"); // qml files are defined to be utf-8 - if (Utils::TextFileFormat::readFile(qmlProjectFilePath, codec, &fileContent, &textFileFormat, &error) - != Utils::TextFileFormat::ReadSuccess) { - qWarning() << "Failed to read file" << qmlProjectFilePath << ":" << error; - } - - const QString settingQmlCode = setting + ":"; - - const Utils::FilePath projectDir = project()->projectFilePath().parentDir(); - const QString relativePath = mainFilePath.relativeChildPath(projectDir).path(); - - if (fileContent.indexOf(settingQmlCode) < 0) { - QString addedText = QString("\n %1 \"%2\"\n").arg(settingQmlCode).arg(relativePath); - auto index = fileContent.lastIndexOf("}"); - fileContent.insert(index, addedText); - } else { - QString originalFileName = oldFile.path(); - originalFileName.replace(".", "\\."); - const QRegularExpression expression(QString("%1\\s*\"(%2)\"").arg(settingQmlCode).arg(originalFileName)); - - const QRegularExpressionMatch match = expression.match(fileContent); - - fileContent.replace(match.capturedStart(1), - match.capturedLength(1), - relativePath); - } - - if (!textFileFormat.writeFile(qmlProjectFilePath, fileContent, &error)) - qWarning() << "Failed to write file" << qmlProjectFilePath << ":" << error; - - refresh(Everything); - return true; -} - -void QmlBuildSystem::refresh(RefreshOptions options) -{ - ParseGuard guard = guardParsingRun(); - parseProject(options); - - if (options & Files) - generateProjectTree(); - - auto modelManager = QmlJS::ModelManagerInterface::instance(); - if (!modelManager) + // FIXME: why checking this? + // this should not even be the case. if that's possible, then what? + // what are the follow-up actions? + if (!QmlProject::isQtDesignStudio()) return; - QmlJS::ModelManagerInterface::ProjectInfo projectInfo - = modelManager->defaultProjectInfoForProject(project(), - project()->files(Project::HiddenRccFolders)); - const QStringList searchPaths = makeAbsolute(canonicalProjectDir(), customImportPaths()); - for (const QString &searchPath : searchPaths) - projectInfo.importPaths.maybeInsert(Utils::FilePath::fromString(searchPath), - QmlJS::Dialect::Qml); - - modelManager->updateProjectInfo(projectInfo, project()); - - guard.markAsSuccess(); - - emit projectChanged(); -} - -FilePath QmlBuildSystem::mainFile() const -{ - if (m_projectItem) - return FilePath::fromString(m_projectItem->mainFile()); - return {}; -} - -FilePath QmlBuildSystem::mainUiFile() const -{ - if (m_projectItem) - return FilePath::fromString(m_projectItem->mainUiFile()); - return {}; -} - -FilePath QmlBuildSystem::mainFilePath() const -{ - const auto mainFileString = mainFile(); - - if (mainFileString.isEmpty()) - return {}; - - return projectDirectory().resolvePath(mainFileString); -} - -FilePath QmlBuildSystem::mainUiFilePath() const -{ - const auto mainUiFileString = mainUiFile(); - - if (mainUiFileString.isEmpty()) - return {}; - - return projectDirectory().resolvePath(mainUiFileString); -} - -bool QmlBuildSystem::setMainFileInProjectFile(const FilePath &newMainFilePath) -{ - return setFileSettingInProjectFile("mainFile", newMainFilePath, mainFile()); -} - -bool QmlBuildSystem::setMainUiFileInProjectFile(const FilePath &newMainUiFilePath) -{ - return setMainUiFileInMainFile(newMainUiFilePath) - && setFileSettingInProjectFile("mainUiFile", newMainUiFilePath, mainUiFile()); -} - -bool QmlBuildSystem::setMainUiFileInMainFile(const FilePath &newMainUiFilePath) -{ - Core::FileChangeBlocker fileChangeBlocker(mainFilePath()); - const QList editors = Core::DocumentModel::editorsForFilePath(mainFilePath()); - TextEditor::TextDocument *document = nullptr; - if (!editors.isEmpty()) { - document = qobject_cast(editors.first()->document()); - if (document && document->isModified()) - if (!Core::DocumentManager::saveDocument(document)) - return false; + if (allowOnlySingleProject()) { + Core::EditorManager::closeAllDocuments(); + SessionManager::closeAllProjects(); } - QString fileContent; - QString error; - Utils::TextFileFormat textFileFormat; - const QTextCodec *codec = QTextCodec::codecForName("UTF-8"); // qml files are defined to be utf-8 - if (Utils::TextFileFormat::readFile(mainFilePath(), codec, &fileContent, &textFileFormat, &error) - != Utils::TextFileFormat::ReadSuccess) { - qWarning() << "Failed to read file" << mainFilePath() << ":" << error; + connect(this, &QmlProject::anyParsingFinished, this, &QmlProject::parsingFinished); +} + +void QmlProject::parsingFinished(const Target *target, bool success) +{ + // trigger only once + disconnect(this, &QmlProject::anyParsingFinished, this, &QmlProject::parsingFinished); + + // FIXME: what to do in this case? + if (!target || !success || !activeTarget()) + return; + + auto targetActive = activeTarget(); + auto qmlBuildSystem = qobject_cast( + targetActive->buildSystem()); + + const Utils::FilePath mainUiFile = qmlBuildSystem->mainUiFilePath(); + + if (mainUiFile.completeSuffix() == "ui.qml" && mainUiFile.exists()) { + QTimer::singleShot(1000, [mainUiFile]() { + Core::EditorManager::openEditor(mainUiFile, Utils::Id()); + }); + return; } - const QString currentMain = QString("%1 {").arg(mainUiFilePath().baseName()); - const QString newMain = QString("%1 {").arg(newMainUiFilePath.baseName()); + Utils::FilePaths uiFiles = collectUiQmlFilesForFolder( + projectDirectory().pathAppended("content")); + if (uiFiles.isEmpty()) { + uiFiles = collectUiQmlFilesForFolder(projectDirectory()); + if (uiFiles.isEmpty()) + return; + } - if (fileContent.contains(currentMain)) - fileContent.replace(currentMain, newMain); + Utils::FilePath currentFile; + if (auto cd = Core::EditorManager::currentDocument()) + currentFile = cd->filePath(); - if (!textFileFormat.writeFile(mainFilePath(), fileContent, &error)) - qWarning() << "Failed to write file" << mainFilePath() << ":" << error; - - return true; + if (currentFile.isEmpty() || !isKnownFile(currentFile)) { + QTimer::singleShot(1000, [uiFiles]() { + Core::EditorManager::openEditor(uiFiles.first(), Utils::Id()); + }); + } } -bool QmlBuildSystem::qtForMCUs() const +Project::RestoreResult QmlProject::fromMap(const QVariantMap &map, QString *errorMessage) { - if (m_projectItem) - return m_projectItem->qtForMCUs(); - return false; -} + RestoreResult result = Project::fromMap(map, errorMessage); + if (result != RestoreResult::Ok) + return result; -bool QmlBuildSystem::qt6Project() const -{ - if (m_projectItem) - return m_projectItem->qt6Project(); - return false; -} + if (activeTarget()) + return RestoreResult::Ok; -void QmlBuildSystem::setMainFile(const QString &mainFilePath) -{ - if (m_projectItem) - m_projectItem->setMainFile(mainFilePath); -} - -FilePath QmlBuildSystem::targetDirectory() const -{ - if (DeviceTypeKitAspect::deviceTypeId(kit()) - == ProjectExplorer::Constants::DESKTOP_DEVICE_TYPE) - return canonicalProjectDir(); - - return m_projectItem ? m_projectItem->targetDirectory() : FilePath(); -} - -FilePath QmlBuildSystem::targetFile(const FilePath &sourceFile) const -{ - const FilePath sourceDir = m_projectItem ? m_projectItem->sourceDirectory() - : canonicalProjectDir(); - const FilePath targetDir = targetDirectory(); - const FilePath relative = sourceFile.relativePathFrom(sourceDir); - return targetDir.resolvePath(relative); -} - -Utils::EnvironmentItems QmlBuildSystem::environment() const -{ - if (m_projectItem) - return m_projectItem->environment(); - return {}; -} - -QStringList QmlBuildSystem::customImportPaths() const -{ - if (m_projectItem) - return m_projectItem->importPaths(); - return {}; -} - -QStringList QmlBuildSystem::customFileSelectors() const -{ - if (m_projectItem) - return m_projectItem->fileSelectors(); - return {}; -} - -bool QmlBuildSystem::multilanguageSupport() const -{ - if (m_projectItem) - return m_projectItem->multilanguageSupport(); - return false; -} - -QStringList QmlBuildSystem::supportedLanguages() const -{ - if (m_projectItem) - return m_projectItem->supportedLanguages(); - return {}; -} - -void QmlBuildSystem::setSupportedLanguages(QStringList languages) -{ - if (m_projectItem) - m_projectItem->setSupportedLanguages(languages); -} - -QString QmlBuildSystem::primaryLanguage() const -{ - if (m_projectItem) - return m_projectItem->primaryLanguage(); - return {}; -} - -void QmlBuildSystem::setPrimaryLanguage(QString language) -{ - if (m_projectItem) - m_projectItem->setPrimaryLanguage(language); -} - -void QmlBuildSystem::refreshProjectFile() -{ - refresh(QmlBuildSystem::ProjectFile | Files); -} - -QStringList QmlBuildSystem::makeAbsolute(const Utils::FilePath &path, const QStringList &relativePaths) -{ - if (path.isEmpty()) - return relativePaths; - - const QDir baseDir(path.toString()); - return Utils::transform(relativePaths, [&baseDir](const QString &path) { - return QDir::cleanPath(baseDir.absoluteFilePath(path)); + // find a kit that matches prerequisites (prefer default one) + const QList kits = Utils::filtered(KitManager::kits(), [this](const Kit *k) { + return !containsType(projectIssues(k), Task::TaskType::Error) + && DeviceTypeKitAspect::deviceTypeId(k) + == ProjectExplorer::Constants::DESKTOP_DEVICE_TYPE; }); + + if (!kits.isEmpty()) { + if (kits.contains(KitManager::defaultKit())) + addTargetForDefaultKit(); + else + addTargetForKit(kits.first()); + } + + // FIXME: are there any other way? + // What if it's not a Design Studio project? What should we do then? + if (QmlProject::isQtDesignStudio()) { + int preferedVersion = preferedQtTarget(activeTarget()); + + // if (activeTarget()) + // removeTarget(activeTarget()); + setKitWithVersion(preferedVersion, kits); + } + + return RestoreResult::Ok; } -void QmlBuildSystem::refreshFiles(const QSet &/*added*/, const QSet &removed) +bool QmlProject::setKitWithVersion(const int qtMajorVersion, const QList kits) { - if (m_blockFilesUpdate) { - qCDebug(infoLogger) << "Auto files refresh blocked."; - return; + const QList qtVersionkits = Utils::filtered(kits, [qtMajorVersion](const Kit *k) { + if (!k->isAutoDetected()) + return false; + + if (k->isReplacementKit()) + return false; + + QtSupport::QtVersion *version = QtSupport::QtKitAspect::qtVersion(k); + return (version && version->qtVersion().majorVersion() == qtMajorVersion); + }); + + if (!qtVersionkits.isEmpty()) { + if (qtVersionkits.contains(KitManager::defaultKit())) + addTargetForDefaultKit(); + else + addTargetForKit(qtVersionkits.first()); } - refresh(Files); - if (!removed.isEmpty()) { - if (auto modelManager = QmlJS::ModelManagerInterface::instance()) { - modelManager->removeFiles( - Utils::transform>(removed, [](const QString &s) { - return Utils::FilePath::fromString(s); - })); - } - } - refreshTargetDirectory(); + + return true; } -void QmlBuildSystem::refreshTargetDirectory() +Utils::FilePaths QmlProject::collectUiQmlFilesForFolder(const Utils::FilePath &folder) const { - updateDeploymentData(); + const Utils::FilePaths uiFiles = files([&](const Node *node) { + return node->filePath().completeSuffix() == "ui.qml" + && node->filePath().parentDir() == folder; + }); + return uiFiles; } Tasks QmlProject::projectIssues(const Kit *k) const @@ -572,8 +181,8 @@ Tasks QmlProject::projectIssues(const Kit *k) const if (dev->type() == ProjectExplorer::Constants::DESKTOP_DEVICE_TYPE) { if (version->type() == QtSupport::Constants::DESKTOPQT) { if (version->qmlRuntimeFilePath().isEmpty()) { - result.append(createProjectTask(Task::TaskType::Error, - Tr::tr("Qt version has no QML utility."))); + result.append( + createProjectTask(Task::TaskType::Error, tr("Qt version has no QML utility."))); } } else { // Non-desktop Qt on a desktop device? We don't support that. @@ -589,68 +198,10 @@ Tasks QmlProject::projectIssues(const Kit *k) const return result; } -bool QmlProject::isEditModePreferred() const -{ - return !isQtDesignStudio(); -} - -Project::RestoreResult QmlProject::fromMap(const QVariantMap &map, QString *errorMessage) -{ - RestoreResult result = Project::fromMap(map, errorMessage); - if (result != RestoreResult::Ok) - return result; - - if (!activeTarget()) { - // find a kit that matches prerequisites (prefer default one) - const QList kits = Utils::filtered(KitManager::kits(), [this](const Kit *k) { - return !containsType(projectIssues(k), Task::TaskType::Error) - && DeviceTypeKitAspect::deviceTypeId(k) - == ProjectExplorer::Constants::DESKTOP_DEVICE_TYPE; - }); - - if (!kits.isEmpty()) { - if (kits.contains(KitManager::defaultKit())) - addTargetForDefaultKit(); - else - addTargetForKit(kits.first()); - } - - if (QmlProject::isQtDesignStudio()) { - auto setKitWithVersion = [&](int qtMajorVersion) { - const QList qtVersionkits - = Utils::filtered(kits, [qtMajorVersion](const Kit *k) { - if (!k->isAutoDetected()) - return false; - if (k->isReplacementKit()) - return false; - QtSupport::QtVersion *version = QtSupport::QtKitAspect::qtVersion(k); - return (version && version->qtVersion().majorVersion() == qtMajorVersion); - }); - if (!qtVersionkits.isEmpty()) { - if (qtVersionkits.contains(KitManager::defaultKit())) - addTargetForDefaultKit(); - else - addTargetForKit(qtVersionkits.first()); - } - }; - - int preferedVersion = preferedQtTarget(activeTarget()); - - if (activeTarget()) - removeTarget(activeTarget()); - - setKitWithVersion(preferedVersion); - } - } - - return RestoreResult::Ok; -} - bool QmlProject::isQtDesignStudio() { QSettings *settings = Core::ICore::settings(); const QString qdsStandaloneEntry = "QML/Designer/StandAloneMode"; - return settings->value(qdsStandaloneEntry, false).toBool(); } @@ -659,172 +210,30 @@ bool QmlProject::isQtDesignStudioStartedFromQtC() return qEnvironmentVariableIsSet(Constants::enviromentLaunchedQDS); } -ProjectExplorer::DeploymentKnowledge QmlProject::deploymentKnowledge() const +DeploymentKnowledge QmlProject::deploymentKnowledge() const { return DeploymentKnowledge::Perfect; } -void QmlBuildSystem::generateProjectTree() +bool QmlProject::isEditModePreferred() const { - if (!m_projectItem) - return; - - auto newRoot = std::make_unique(project()); - - for (const QString &f : m_projectItem->files()) { - const Utils::FilePath fileName = Utils::FilePath::fromString(f); - const FileType fileType = (fileName == projectFilePath()) - ? FileType::Project : FileNode::fileTypeForFileName(fileName); - newRoot->addNestedNode(std::make_unique(fileName, fileType)); - } - newRoot->addNestedNode(std::make_unique(projectFilePath(), FileType::Project)); - - setRootProjectNode(std::move(newRoot)); - refreshTargetDirectory(); + return !isQtDesignStudio(); } -void QmlBuildSystem::updateDeploymentData() +int QmlProject::preferedQtTarget(Target *target) { - if (!m_projectItem) - return; + if (!target) + return -1; - if (DeviceTypeKitAspect::deviceTypeId(kit()) - == ProjectExplorer::Constants::DESKTOP_DEVICE_TYPE) { - return; - } - - ProjectExplorer::DeploymentData deploymentData; - for (const QString &file : m_projectItem->files()) { - deploymentData.addFile( - FilePath::fromString(file), - targetFile(Utils::FilePath::fromString(file)).parentDir().toString()); - } - - setDeploymentData(deploymentData); + auto buildSystem = qobject_cast(target->buildSystem()); + return (buildSystem && buildSystem->qt6Project()) ? 6 : 5; } -QVariant QmlBuildSystem::additionalData(Id id) const +bool QmlProject::allowOnlySingleProject() { - if (id == Constants::customFileSelectorsData) - return customFileSelectors(); - if (id == Constants::supportedLanguagesData) - return supportedLanguages(); - if (id == Constants::primaryLanguageData) - return primaryLanguage(); - if (id == Constants::customForceFreeTypeData) - return forceFreeType(); - if (id == Constants::customQtForMCUs) - return qtForMCUs(); - if (id == Constants::customQt6Project) - return qt6Project(); - if (id == Constants::mainFilePath) - return mainFilePath().toString(); - if (id == Constants::customImportPaths) - return customImportPaths(); - if (id == Constants::canonicalProjectDir) - return canonicalProjectDir().toString(); - return {}; -} - -bool QmlBuildSystem::supportsAction(Node *context, ProjectAction action, const Node *node) const -{ - if (dynamic_cast(context)) { - if (action == AddNewFile || action == EraseFile) - return true; - QTC_ASSERT(node, return false); - - if (action == Rename && node->asFileNode()) { - const FileNode *fileNode = node->asFileNode(); - QTC_ASSERT(fileNode, return false); - return fileNode->fileType() != FileType::Project; - } - - return false; - } - - return BuildSystem::supportsAction(context, action, node); -} - -QmlProject *QmlBuildSystem::qmlProject() const -{ - return static_cast(BuildSystem::project()); -} - -bool QmlBuildSystem::forceFreeType() const -{ - if (m_projectItem) - return m_projectItem->forceFreeType(); - return false; -} - -bool QmlBuildSystem::widgetApp() const -{ - if (m_projectItem) - return m_projectItem->widgetApp(); - return false; -} - -QStringList QmlBuildSystem::shaderToolArgs() const -{ - if (m_projectItem) - return m_projectItem->shaderToolArgs(); - return {}; -} - -QStringList QmlBuildSystem::shaderToolFiles() const -{ - if (m_projectItem) - return m_projectItem->shaderToolFiles(); - return {}; -} - -QStringList QmlBuildSystem::importPaths() const -{ - if (m_projectItem) - return m_projectItem->importPaths(); - return {}; -} - -QStringList QmlBuildSystem::files() const -{ - if (m_projectItem) - return m_projectItem->files(); - return {}; -} - -bool QmlBuildSystem::addFiles(Node *context, const FilePaths &filePaths, FilePaths *) -{ - if (!dynamic_cast(context)) - return false; - - FilePaths toAdd; - for (const FilePath &filePath : filePaths) { - if (!m_projectItem->matchesFile(filePath.toString())) - toAdd << filePaths; - } - return toAdd.isEmpty(); -} - -bool QmlBuildSystem::deleteFiles(Node *context, const FilePaths &filePaths) -{ - if (dynamic_cast(context)) - return true; - - return BuildSystem::deleteFiles(context, filePaths); -} - -bool QmlBuildSystem::renameFile(Node * context, const FilePath &oldFilePath, const FilePath &newFilePath) -{ - if (dynamic_cast(context)) { - if (oldFilePath.endsWith(mainFile().path())) - return setMainFileInProjectFile(newFilePath); - if (oldFilePath.endsWith(mainUiFile().path())) - return setMainUiFileInProjectFile(newFilePath); - - return true; - } - - return BuildSystem::renameFile(context, oldFilePath, newFilePath); + QSettings *settings = Core::ICore::settings(); + auto key = "QML/Designer/AllowMultipleProjects"; + return !settings->value(key, false).toBool(); } } // namespace QmlProjectManager diff --git a/src/plugins/qmlprojectmanager/qmlproject.h b/src/plugins/qmlprojectmanager/qmlproject.h index f00a4f2b36c..4156801ecfc 100644 --- a/src/plugins/qmlprojectmanager/qmlproject.h +++ b/src/plugins/qmlprojectmanager/qmlproject.h @@ -1,150 +1,31 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0 #pragma once +#include "buildsystem/qmlbuildsystem.h" // IWYU pragma: keep #include "qmlprojectmanager_global.h" - -#include #include -#include - -#include - namespace QmlProjectManager { class QmlProject; -class QmlProjectItem; - -class QMLPROJECTMANAGER_EXPORT QmlBuildSystem : public ProjectExplorer::BuildSystem -{ - Q_OBJECT - -public: - explicit QmlBuildSystem(ProjectExplorer::Target *target); - ~QmlBuildSystem(); - - void triggerParsing() final; - - bool supportsAction(ProjectExplorer::Node *context, - ProjectExplorer::ProjectAction action, - const ProjectExplorer::Node *node) const override; - bool addFiles(ProjectExplorer::Node *context, - const Utils::FilePaths &filePaths, Utils::FilePaths *notAdded = nullptr) override; - bool deleteFiles(ProjectExplorer::Node *context, - const Utils::FilePaths &filePaths) override; - bool renameFile(ProjectExplorer::Node *context, - const Utils::FilePath &oldFilePath, const Utils::FilePath &newFilePath) override; - QString name() const override { return QLatin1String("qml"); } - - QmlProject *qmlProject() const; - - QVariant additionalData(Utils::Id id) const override; - - enum RefreshOption { - ProjectFile = 0x01, - Files = 0x02, - Configuration = 0x04, - Everything = ProjectFile | Files | Configuration - }; - Q_DECLARE_FLAGS(RefreshOptions,RefreshOption) - - void refresh(RefreshOptions options); - - Utils::FilePath canonicalProjectDir() const; - Utils::FilePath mainFile() const; - Utils::FilePath mainUiFile() const; - Utils::FilePath mainFilePath() const; - Utils::FilePath mainUiFilePath() const; - - bool setMainFileInProjectFile(const Utils::FilePath &newMainFilePath); - bool setMainUiFileInProjectFile(const Utils::FilePath &newMainUiFilePath); - bool setMainUiFileInMainFile(const Utils::FilePath &newMainUiFilePath); - - bool qtForMCUs() const; - bool qt6Project() const; - void setMainFile(const QString &mainFilePath); - Utils::FilePath targetDirectory() const; - Utils::FilePath targetFile(const Utils::FilePath &sourceFile) const; - - Utils::EnvironmentItems environment() const; - QStringList customImportPaths() const; - QStringList customFileSelectors() const; - bool multilanguageSupport() const; - QStringList supportedLanguages() const; - void setSupportedLanguages(QStringList languages); - QString primaryLanguage() const; - void setPrimaryLanguage(QString language); - bool forceFreeType() const; - bool widgetApp() const; - QStringList shaderToolArgs() const; - QStringList shaderToolFiles() const; - QStringList importPaths() const; - QStringList files() const; - - bool addFiles(const QStringList &filePaths); - - void refreshProjectFile(); - - static Utils::FilePath activeMainFilePath(); - static QStringList makeAbsolute(const Utils::FilePath &path, const QStringList &relativePaths); - - void generateProjectTree(); - void updateDeploymentData(); - void refreshFiles(const QSet &added, const QSet &removed); - void refreshTargetDirectory(); - void onActiveTargetChanged(ProjectExplorer::Target *target); - void onKitChanged(); - - // plain format - void parseProject(RefreshOptions options); - -signals: - void projectChanged(); - -private: - bool setFileSettingInProjectFile(const QString &setting, - const Utils::FilePath &mainFilePath, - const Utils::FilePath &oldFile); - - std::unique_ptr m_projectItem; - Utils::FilePath m_canonicalProjectDir; - bool m_blockFilesUpdate = false; - friend class FilesUpdateBlocker; -}; - -class FilesUpdateBlocker { -public: - FilesUpdateBlocker(QmlBuildSystem* bs): m_bs(bs) { - if (m_bs) - m_bs->m_blockFilesUpdate = true; - } - - ~FilesUpdateBlocker() { - if (m_bs) { - m_bs->m_blockFilesUpdate = false; - m_bs->refresh(QmlBuildSystem::Everything); - } - } -private: - QPointer m_bs; -}; class QMLPROJECTMANAGER_EXPORT QmlProject : public ProjectExplorer::Project { Q_OBJECT - public: explicit QmlProject(const Utils::FilePath &filename); - - ProjectExplorer::Tasks projectIssues(const ProjectExplorer::Kit *k) const final; + ~QmlProject(){ + qDebug() << "Closing the project"; + }; static bool isQtDesignStudio(); static bool isQtDesignStudioStartedFromQtC(); - bool isEditModePreferred() const override; + ProjectExplorer::Tasks projectIssues(const ProjectExplorer::Kit *k) const final; + protected: RestoreResult fromMap(const QVariantMap &map, QString *errorMessage) override; @@ -153,9 +34,35 @@ private: Utils::FilePaths collectUiQmlFilesForFolder(const Utils::FilePath &folder) const; Utils::FilePaths collectQmlFiles() const; - QMetaObject::Connection m_openFileConnection; + bool setKitWithVersion(const int qtMajorVersion, const QList kits); + + bool allowOnlySingleProject(); + int preferedQtTarget(ProjectExplorer::Target *target); + +private slots: + void parsingFinished(const ProjectExplorer::Target *target, bool success); +}; + +class FilesUpdateBlocker +{ +public: + FilesUpdateBlocker(QmlBuildSystem *bs) + : m_bs(bs) + { + if (m_bs) + m_bs->m_blockFilesUpdate = true; + } + + ~FilesUpdateBlocker() + { + if (m_bs) { + m_bs->m_blockFilesUpdate = false; + m_bs->refresh(QmlBuildSystem::RefreshOptions::Project); + } + } + +private: + QPointer m_bs; }; } // namespace QmlProjectManager - -Q_DECLARE_OPERATORS_FOR_FLAGS(QmlProjectManager::QmlBuildSystem::RefreshOptions) diff --git a/src/plugins/qmlprojectmanager/qmlprojectmanager.qbs b/src/plugins/qmlprojectmanager/qmlprojectmanager.qbs index 9eac76dc6ca..d4f3b32cc37 100644 --- a/src/plugins/qmlprojectmanager/qmlprojectmanager.qbs +++ b/src/plugins/qmlprojectmanager/qmlprojectmanager.qbs @@ -33,12 +33,14 @@ QtcPlugin { } Group { - name: "File Format" - prefix: "fileformat/" + name: "Build System" + prefix: "buildsystem/" files: [ - "filefilteritems.cpp", "filefilteritems.h", - "qmlprojectfileformat.cpp", "qmlprojectfileformat.h", - "qmlprojectitem.cpp", "qmlprojectitem.h", + "qmlbuildsystem.cpp", "qmlbuildsystem.h", + "projectitem/filefilteritems.cpp", "projectitem/filefilteritems.h", + "projectitem/qmlprojectitem.cpp", "projectitem/qmlprojectitem.h", + "projectitem/converters.h", + "projectnode/qmlprojectnodes.cpp", "projectnode/qmlprojectnodes.h" ] } @@ -55,7 +57,7 @@ QtcPlugin { "cmakeprojectconverterdialog.cpp", "cmakeprojectconverterdialog.h", ] } - + Group { name: "QML Project File Generator" prefix: "qmlprojectgen/" diff --git a/src/plugins/qmlprojectmanager/qmlprojectplugin.cpp b/src/plugins/qmlprojectmanager/qmlprojectplugin.cpp index 3c156311b41..0a87ef9bc90 100644 --- a/src/plugins/qmlprojectmanager/qmlprojectplugin.cpp +++ b/src/plugins/qmlprojectmanager/qmlprojectplugin.cpp @@ -10,7 +10,6 @@ #include "projectfilecontenttools.h" #include "cmakegen/cmakeprojectconverter.h" #include "cmakegen/generatecmakelists.h" -#include "qmlprojectgen/qmlprojectgenerator.h" #include #include diff --git a/tests/auto/qml/qmlprojectmanager/CMakeLists.txt b/tests/auto/qml/qmlprojectmanager/CMakeLists.txt index f7c00d44bd5..795b5a74265 100644 --- a/tests/auto/qml/qmlprojectmanager/CMakeLists.txt +++ b/tests/auto/qml/qmlprojectmanager/CMakeLists.txt @@ -1 +1,2 @@ add_subdirectory(fileformat) +add_subdirectory(projectitem) diff --git a/tests/auto/qml/qmlprojectmanager/fileformat/CMakeLists.txt b/tests/auto/qml/qmlprojectmanager/fileformat/CMakeLists.txt index 91800cd75ee..315db12ac18 100644 --- a/tests/auto/qml/qmlprojectmanager/fileformat/CMakeLists.txt +++ b/tests/auto/qml/qmlprojectmanager/fileformat/CMakeLists.txt @@ -9,11 +9,12 @@ foreach(source IN LISTS QmlProjectManagerSources) endif() endforeach() -add_qtc_test(tst_qml_fileformat - DEPENDS QmlJS Utils - INCLUDES "${PROJECT_SOURCE_DIR}/src/plugins/qmlprojectmanager/fileformat" - DEFINES - QT_CREATOR - SRCDIR="${CMAKE_CURRENT_SOURCE_DIR}" - SOURCES tst_fileformat.cpp ${fileformat_sources} -) +#add_qtc_test(tst_qml_fileformat +# DEPENDS QmlJS Utils +# INCLUDES "${PROJECT_SOURCE_DIR}/src/plugins/qmlprojectmanager/fileformat" +# DEFINES +# QT_CREATOR +# SRCDIR="${CMAKE_CURRENT_SOURCE_DIR}" +# SOURCES tst_fileformat.cpp ${fileformat_sources} +#) + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/CMakeLists.txt b/tests/auto/qml/qmlprojectmanager/projectitem/CMakeLists.txt new file mode 100644 index 00000000000..20f1bd0054d --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/CMakeLists.txt @@ -0,0 +1,25 @@ +set(WITH_TESTS ON) + +find_package(Googletest MODULE) + +set(QmlProjectItemDir "${PROJECT_SOURCE_DIR}/src/plugins/qmlprojectmanager/buildsystem/projectitem") + +add_qtc_test(tst_qml_projectitem + DEPENDS QmlProjectManager Utils QmlJS Googletest + INCLUDES "${QmlProjectItemDir}" + DEFINES + QT_CREATOR + SRCDIR="${CMAKE_CURRENT_SOURCE_DIR}" + TESTDATA_DIR="${CMAKE_CURRENT_SOURCE_DIR}/data" + TESTS_ENABLED_QMLPROJECTITEM + SOURCES + tst_projectitem.cpp + test-getters.cpp + test-setters.cpp + test-converters.cpp + test-filefilters.cpp + common.h + "${QmlProjectItemDir}/qmlprojectitem.cpp" + "${QmlProjectItemDir}/converters.cpp" + "${QmlProjectItemDir}/filefilteritems.cpp" +) diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/README.md b/tests/auto/qml/qmlprojectmanager/projectitem/README.md new file mode 100644 index 00000000000..60d308c5b77 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/README.md @@ -0,0 +1,46 @@ +# QmlProject ProjectItem Tests + +## Content + +This test bundle covers following functionalities of QmlProjectItem class; + +* **Getter Functions**: Tests if getter functions are returning correct types with correct values +* **Setter Functions**: Tests if setter functions are updating the internal JSON object as expected +* **Converter Functions:** Tests if QmlProjectToJson and JsonToQmlProject functions are working as expected +* **File Filter Functions:** Tests if file filters are initialized properly + +## Data set folder structure + +The current folder hierarchy is as following; + +```text +| data +| -> converter +| | -> test-set-1 +| | | -> testfile.qmlproject +| | | -> testfile.qmltojson +| | | -> testfile.jsontoqml +| | -> test-set-2 +| | | -> testfile.qmlproject +| | | -> testfile.qmltojson +| | | -> testfile.jsontoqml +| | -> test-set-.. +| | -> test-set-.. +| -> getter-setter +| | -> testfile-1.qmlproject +| | -> testfile-2.qmlproject +| -> file-filters +| | -> test-set-1 +| | -> test-set-... +``` + +## Further information + +Please see [data/README.md](data/README.md) for more information on the test set content. + +## Contribution + +Please update; + +* This README whenever you change the test content +* [data/README.md](data/README.md) whenever you update the `data` folder diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/common.h b/tests/auto/qml/qmlprojectmanager/projectitem/common.h new file mode 100644 index 00000000000..2167a5d1444 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/common.h @@ -0,0 +1,20 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0 + +#pragma once + +#include +#include +#include + +#include +#include + +#include + +static QDir testDataRootDir(QLatin1String(TESTDATA_DIR)); + +inline void PrintTo(const QString &qString, ::std::ostream *os) +{ + *os << qUtf8Printable(qString); +} diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/README.md b/tests/auto/qml/qmlprojectmanager/projectitem/data/README.md new file mode 100644 index 00000000000..309d453e98e --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/README.md @@ -0,0 +1,47 @@ +# Test Set Information + +This document contains information about the purpose of each test sets. + +## Getter/Setter test data + +* **testfile-1.qmlproject**: QmlProject file with properly filled out object +* **testfile-2.qmlproject**: QmlProject file with empty objects + +## Converter test data + +Test functions iterate over the "test-set-*" folders and run the tests by using the files inside them. + +* **testfile.qmlproject**: Original QmlProject file that'll be converted +* **testfile.qmltojson**: JSON-converted version of the .qmlproject file +* **testfile.jsontoqml**: QmlProject-converted version of the .qmltojson file + +### test-set-1 + +* **purpose**: testing complex qmlproject file convertion +* **origin**: custom project + +### test-set-2 + +* **purpose**: testing complex qmlproject file convertion +* **origin**: material bundle example + +### test-set-3 + +* **purpose**: testing complex qmlproject file convertion +* **origin**: robot arm example + +### test-set-4 + +* **purpose**: testing complex qmlproject file convertion +* **origin**: outrun hvac example + +### test-set-5 + +* **purpose**: testing fileselectors +* **origin**: file selectors example from playground + +## File Filters test data + +Test data contains an example project folders that file filters will be initialized and tested. + +* **filelist.txt**: List of the files need to be found by the file filters. diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-1/testfile.jsontoqml b/tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-1/testfile.jsontoqml new file mode 100644 index 00000000000..5207599f994 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-1/testfile.jsontoqml @@ -0,0 +1,98 @@ +\\ prop: json-converted +\\ prop: auto-generated + +import QmlProject + +Project { + mainFile: "content/App.qml" + mainUiFile: "content/Screen01.ui.qml" + targetDirectory: "/opt/UntitledProject13" + widgetApp: true + importPaths: [ "imports","asset_imports" ] + + qdsVersion: "4.0" + quickVersion: "6.2" + qt6Project: true + qtForMCUs: true + + multilanguageSupport: true + primaryLanguage: "en" + supportedLanguages: [ "en" ] + + Environment { + QML_COMPAT_RESOLVE_URLS_ON_ASSIGNMENT: "1" + QT_AUTO_SCREEN_SCALE_FACTOR: "1" + QT_ENABLE_HIGHDPI_SCALING: "0" + QT_LOGGING_RULES: "qt.qml.connections=false" + QT_QUICK_CONTROLS_CONF: "qtquickcontrols2.conf" + } + + ShaderTool { + args: "-s --glsl "100 es,120,150" --hlsl 50 --msl 12" + files: [ "content/shaders/*" ] + } + + QmlFiles { + directory: "content" + } + + QmlFiles { + directory: "imports" + } + + QmlFiles { + directory: "asset_imports" + } + + JavaScriptFiles { + directory: "content" + } + + JavaScriptFiles { + directory: "imports" + } + + ImageFiles { + directory: "content" + } + + ImageFiles { + directory: "asset_imports" + } + + Files { + directory: "." + filters: "*.conf" + files: [ "qtquickcontrols2.conf" ] + } + + Files { + directory: "." + filters: "*.ttf;*.otf;*.ctf" + } + + Files { + directory: "asset_imports" + filters: "*.mesh" + } + + Files { + directory: "." + filters: "qmldir" + } + + Files { + directory: "." + filters: "*.glsl;*.glslv;*.glslf;*.vsh;*.fsh;*.vert;*.frag;*.trag" + } + + Files { + directory: "." + filters: "*.mp3;*.wav" + } + + Files { + directory: "." + filters: "*.mp4" + } +} diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-1/testfile.qmlproject b/tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-1/testfile.qmlproject new file mode 100644 index 00000000000..1ff457cdd87 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-1/testfile.qmlproject @@ -0,0 +1,110 @@ +import QmlProject + +Project { + mainFile: "content/App.qml" + mainUiFile: "content/Screen01.ui.qml" + + /* Include .qml, .js, and image files from current directory and subdirectories */ + QmlFiles { + directory: "content" + } + + QmlFiles { + directory: "imports" + } + + JavaScriptFiles { + directory: "content" + } + + JavaScriptFiles { + directory: "imports" + } + + ImageFiles { + directory: "content" + } + + ImageFiles { + directory: "asset_imports" + } + + Files { + filter: "*.conf" + files: ["qtquickcontrols2.conf"] + } + + Files { + filter: "qmldir" + directory: "." + } + + Files { + filter: "*.ttf;*.otf;*.ctf" + } + + Files { + filter: "*.wav;*.mp3" + } + + Files { + filter: "*.mp4" + } + + Files { + filter: "*.glsl;*.glslv;*.glslf;*.vsh;*.fsh;*.vert;*.frag;*.trag" + } + + Files { + filter: "*.mesh" + directory: "asset_imports" + } + + Files { + filter: "*.qml" + directory: "asset_imports" + } + + Environment { + QT_QUICK_CONTROLS_CONF: "qtquickcontrols2.conf" + QT_AUTO_SCREEN_SCALE_FACTOR: "1" + QML_COMPAT_RESOLVE_URLS_ON_ASSIGNMENT: "1" + QT_LOGGING_RULES: "qt.qml.connections=false" + QT_ENABLE_HIGHDPI_SCALING: "0" + /* Useful for debugging + QSG_VISUALIZE=batches + QSG_VISUALIZE=clip + QSG_VISUALIZE=changes + QSG_VISUALIZE=overdraw + */ + } + + qt6Project: true + + /* List of plugin directories passed to QML runtime */ + importPaths: [ "imports", "asset_imports" ] + + /* Required for deployment */ + targetDirectory: "/opt/UntitledProject13" + + qdsVersion: "4.0" + + quickVersion: "6.2" + + /* If any modules the project imports require widgets (e.g. QtCharts), widgetApp must be true */ + widgetApp: true + + /* args: Specifies command line arguments for qsb tool to generate shaders. + files: Specifies target files for qsb tool. If path is included, it must be relative to this file. + Wildcard '*' can be used in the file name part of the path. + e.g. files: [ "content/shaders/*.vert", "*.frag" ] */ + ShaderTool { + args: "-s --glsl \"100 es,120,150\" --hlsl 50 --msl 12" + files: [ "content/shaders/*" ] + } + + multilanguageSupport: true + supportedLanguages: ["en"] + primaryLanguage: "en" + +} diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-1/testfile.qmltojson b/tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-1/testfile.qmltojson new file mode 100644 index 00000000000..358a381a97e --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-1/testfile.qmltojson @@ -0,0 +1,176 @@ +{ + "deployment": { + "targetDirectory": "/opt/UntitledProject13" + }, + "environment": { + "QML_COMPAT_RESOLVE_URLS_ON_ASSIGNMENT": "1", + "QT_AUTO_SCREEN_SCALE_FACTOR": "1", + "QT_ENABLE_HIGHDPI_SCALING": "0", + "QT_LOGGING_RULES": "qt.qml.connections=false", + "QT_QUICK_CONTROLS_CONF": "qtquickcontrols2.conf" + }, + "fileGroups": { + "config": { + "directories": [ + "." + ], + "files": [ + { + "name": "qtquickcontrols2.conf" + } + ], + "filters": [ + "*.conf" + ] + }, + "font": { + "directories": [ + "." + ], + "files": [ + ], + "filters": [ + "*.ttf", + "*.otf", + "*.ctf" + ] + }, + "image": { + "directories": [ + "content", + "asset_imports" + ], + "files": [ + ], + "filters": [ + "*.jpeg", + "*.jpg", + "*.png", + "*.svg", + "*.hdr", + ".ktx" + ] + }, + "javaScript": { + "directories": [ + "content", + "imports" + ], + "files": [ + ], + "filters": [ + "*.js", + "*.ts" + ] + }, + "meshes": { + "directories": [ + "asset_imports" + ], + "files": [ + ], + "filters": [ + "*.mesh" + ] + }, + "qml": { + "directories": [ + "content", + "imports", + "asset_imports" + ], + "files": [ + ], + "filters": [ + "*.qml" + ] + }, + "qmldir": { + "directories": [ + "." + ], + "files": [ + ], + "filters": [ + "qmldir" + ] + }, + "shader": { + "directories": [ + "." + ], + "files": [ + ], + "filters": [ + "*.glsl", + "*.glslv", + "*.glslf", + "*.vsh", + "*.fsh", + "*.vert", + "*.frag", + "*.trag" + ] + }, + "sound": { + "directories": [ + "." + ], + "files": [ + ], + "filters": [ + "*.mp3", + "*.wav" + ] + }, + "video": { + "directories": [ + "." + ], + "files": [ + ], + "filters": [ + "*.mp4" + ] + } + }, + "fileVersion": 1, + "importPaths": [ + "imports", + "asset_imports" + ], + "language": { + "multiLanguageSupport": true, + "primaryLanguage": "en", + "supportedLanguages": [ + "en" + ] + }, + "mcuConfig": { + }, + "runConfig": { + "fileSelectors": [ + ], + "mainFile": "content/App.qml", + "mainUiFile": "content/Screen01.ui.qml", + "widgetApp": true + }, + "shaderTool": { + "args": [ + "-s", + "--glsl", + "\"100 es,120,150\"", + "--hlsl", + "50", + "--msl", + "12" + ], + "files": [ + "content/shaders/*" + ] + }, + "versions": { + "designStudio": "4.0", + "qtQuick": "6.2" + } +} diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-2/testfile.jsontoqml b/tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-2/testfile.jsontoqml new file mode 100644 index 00000000000..47d4912c86e --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-2/testfile.jsontoqml @@ -0,0 +1,102 @@ +\\ prop: json-converted +\\ prop: auto-generated + +import QmlProject + +Project { + mainFile: "content/App.qml" + mainUiFile: "Screen01.ui.qml" + targetDirectory: "/opt/MaterialLibrary" + widgetApp: true + importPaths: [ "imports","asset_imports" ] + + qdsVersion: "3.9" + quickVersion: "" + qt6Project: false + qtForMCUs: true + + multilanguageSupport: true + primaryLanguage: "en" + supportedLanguages: [ "en" ] + + Environment { + QT_AUTO_SCREEN_SCALE_FACTOR: "1" + QT_ENABLE_HIGHDPI_SCALING: "0" + QT_LOGGING_RULES: "qt.qml.connections=false" + QT_QUICK_CONTROLS_CONF: "qtquickcontrols2.conf" + } + + ShaderTool { + args: "-s --glsl "100 es,120,150" --hlsl 50 --msl 12" + files: [ "content/shaders/*" ] + } + + QmlFiles { + directory: "content" + } + + QmlFiles { + directory: "imports" + } + + QmlFiles { + directory: "asset_imports" + } + + JavaScriptFiles { + directory: "content" + } + + JavaScriptFiles { + directory: "imports" + } + + ImageFiles { + directory: "content" + } + + ImageFiles { + directory: "asset_imports" + } + + Files { + directory: "." + filters: "*.conf" + files: [ "qtquickcontrols2.conf" ] + } + + Files { + directory: "." + filters: "*.ttf;*.otf" + } + + Files { + directory: "asset_imports" + filters: "*.mesh" + } + + Files { + directory: "content" + filters: "*.mesh" + } + + Files { + directory: "." + filters: "qmldir" + } + + Files { + directory: "." + filters: "*.glsl;*.glslv;*.glslf;*.vsh;*.fsh;*.vert;*.frag" + } + + Files { + directory: "." + filters: "*.mp3;*.wav" + } + + Files { + directory: "." + filters: "*.mp4" + } +} diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-2/testfile.qmlproject b/tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-2/testfile.qmlproject new file mode 100644 index 00000000000..479c20456be --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-2/testfile.qmlproject @@ -0,0 +1,112 @@ +import QmlProject + +Project { + mainFile: "content/App.qml" + mainUiFile: "Screen01.ui.qml" + + + /* Include .qml, .js, and image files from current directory and subdirectories */ + QmlFiles { + directory: "content" + } + + QmlFiles { + directory: "imports" + } + + JavaScriptFiles { + directory: "content" + } + + JavaScriptFiles { + directory: "imports" + } + + ImageFiles { + directory: "content" + } + + Files { + filter: "*.conf" + files: ["qtquickcontrols2.conf"] + } + + Files { + filter: "qmldir" + directory: "." + } + + Files { + filter: "*.ttf;*.otf" + } + + Files { + filter: "*.wav;*.mp3" + } + + Files { + filter: "*.mp4" + } + + Files { + filter: "*.glsl;*.glslv;*.glslf;*.vsh;*.fsh;*.vert;*.frag" + } + + Files { + filter: "*.mesh" + directory: "asset_imports" + } + + Files { + filter: "*.mesh" + directory: "content" + } + + Files { + filter: "*.qml" + directory: "asset_imports" + } + + ImageFiles { + directory: "asset_imports" + } + + Environment { + QT_QUICK_CONTROLS_CONF: "qtquickcontrols2.conf" + QT_AUTO_SCREEN_SCALE_FACTOR: "1" + QT_LOGGING_RULES: "qt.qml.connections=false" + QT_ENABLE_HIGHDPI_SCALING: "0" + /* Useful for debugging + QSG_VISUALIZE=batches + QSG_VISUALIZE=clip + QSG_VISUALIZE=changes + QSG_VISUALIZE=overdraw + */ + } + + qt6Project: true + + /* List of plugin directories passed to QML runtime */ + importPaths: [ "imports", "asset_imports" ] + + /* Required for deployment */ + targetDirectory: "/opt/MaterialLibrary" + + qdsVersion: "3.9" + + /* If any modules the project imports require widgets (e.g. QtCharts), widgetApp must be true */ + widgetApp: true + + /* args: Specifies command line arguments for qsb tool to generate shaders. + files: Specifies target files for qsb tool. If path is included, it must be relative to this file. + Wildcard '*' can be used in the file name part of the path. + e.g. files: [ "content/shaders/*.vert", "*.frag" ] */ + ShaderTool { + args: "-s --glsl \"100 es,120,150\" --hlsl 50 --msl 12" + files: [ "content/shaders/*" ] + } + + multilanguageSupport: true + supportedLanguages: ["en"] + primaryLanguage: "en" +} diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-2/testfile.qmltojson b/tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-2/testfile.qmltojson new file mode 100644 index 00000000000..f29227dea84 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-2/testfile.qmltojson @@ -0,0 +1,173 @@ +{ + "deployment": { + "targetDirectory": "/opt/MaterialLibrary" + }, + "environment": { + "QT_AUTO_SCREEN_SCALE_FACTOR": "1", + "QT_ENABLE_HIGHDPI_SCALING": "0", + "QT_LOGGING_RULES": "qt.qml.connections=false", + "QT_QUICK_CONTROLS_CONF": "qtquickcontrols2.conf" + }, + "fileGroups": { + "config": { + "directories": [ + "." + ], + "files": [ + { + "name": "qtquickcontrols2.conf" + } + ], + "filters": [ + "*.conf" + ] + }, + "font": { + "directories": [ + "." + ], + "files": [ + ], + "filters": [ + "*.ttf", + "*.otf" + ] + }, + "image": { + "directories": [ + "content", + "asset_imports" + ], + "files": [ + ], + "filters": [ + "*.jpeg", + "*.jpg", + "*.png", + "*.svg", + "*.hdr", + ".ktx" + ] + }, + "javaScript": { + "directories": [ + "content", + "imports" + ], + "files": [ + ], + "filters": [ + "*.js", + "*.ts" + ] + }, + "meshes": { + "directories": [ + "asset_imports", + "content" + ], + "files": [ + ], + "filters": [ + "*.mesh" + ] + }, + "qml": { + "directories": [ + "content", + "imports", + "asset_imports" + ], + "files": [ + ], + "filters": [ + "*.qml" + ] + }, + "qmldir": { + "directories": [ + "." + ], + "files": [ + ], + "filters": [ + "qmldir" + ] + }, + "shader": { + "directories": [ + "." + ], + "files": [ + ], + "filters": [ + "*.glsl", + "*.glslv", + "*.glslf", + "*.vsh", + "*.fsh", + "*.vert", + "*.frag" + ] + }, + "sound": { + "directories": [ + "." + ], + "files": [ + ], + "filters": [ + "*.mp3", + "*.wav" + ] + }, + "video": { + "directories": [ + "." + ], + "files": [ + ], + "filters": [ + "*.mp4" + ] + } + }, + "fileVersion": 1, + "importPaths": [ + "imports", + "asset_imports" + ], + "language": { + "multiLanguageSupport": true, + "primaryLanguage": "en", + "supportedLanguages": [ + "en" + ] + }, + "mcuConfig": { + }, + "runConfig": { + "fileSelectors": [ + ], + "mainFile": "content/App.qml", + "mainUiFile": "Screen01.ui.qml", + "widgetApp": true + }, + "shaderTool": { + "args": [ + "-s", + "--glsl", + "\"100 es,120,150\"", + "--hlsl", + "50", + "--msl", + "12" + ], + "files": [ + "content/shaders/*" + ] + }, + "versions": { + "designStudio": "3.9" + } +} diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-3/testfile.jsontoqml b/tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-3/testfile.jsontoqml new file mode 100644 index 00000000000..cb11fae9d30 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-3/testfile.jsontoqml @@ -0,0 +1,93 @@ +\\ prop: json-converted +\\ prop: auto-generated + +import QmlProject + +Project { + mainFile: "content/App.qml" + mainUiFile: "content/MainScreen.ui.qml" + targetDirectory: "/opt/RobotArm" + widgetApp: true + importPaths: [ "imports","asset_imports","backend_mock" ] + + qdsVersion: "3.0" + quickVersion: "" + qt6Project: false + qtForMCUs: true + + multilanguageSupport: true + primaryLanguage: "en" + supportedLanguages: [ "en" ] + + Environment { + QT_AUTO_SCREEN_SCALE_FACTOR: "1" + QT_ENABLE_HIGHDPI_SCALING: "0" + QT_LOGGING_RULES: "qt.qml.connections=false" + QT_QUICK_CONTROLS_CONF: "qtquickcontrols2.conf" + } + + ShaderTool { + args: "" + files: [ ] + } + + QmlFiles { + directory: "content" + } + + QmlFiles { + directory: "imports" + } + + QmlFiles { + directory: "backend_mock" + } + + JavaScriptFiles { + directory: "content" + } + + JavaScriptFiles { + directory: "imports" + } + + ImageFiles { + directory: "content" + } + + Files { + directory: "." + filters: "*.conf" + files: [ "qtquickcontrols2.conf" ] + } + + Files { + directory: "." + filters: "*.ttf;*.otf" + } + + Files { + directory: "content" + filters: "*.mesh" + } + + Files { + directory: "." + filters: "qmldir" + } + + Files { + directory: "." + filters: "*.glsl;*.glslv;*.glslf;*.vsh;*.fsh;*.vert;*.frag" + } + + Files { + directory: "." + filters: "*.mp3;*.wav" + } + + Files { + directory: "." + filters: "*.mp4" + } +} diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-3/testfile.qmlproject b/tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-3/testfile.qmlproject new file mode 100644 index 00000000000..a9c59cdb665 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-3/testfile.qmlproject @@ -0,0 +1,93 @@ +import QmlProject + +Project { + mainFile: "content/App.qml" + mainUiFile: "content/MainScreen.ui.qml" + + /* Include .qml, .js, and image files from current directory and subdirectories */ + QmlFiles { + directory: "content" + } + + QmlFiles { + directory: "imports" + } + + QmlFiles { + directory: "backend_mock" + } + + JavaScriptFiles { + directory: "content" + } + + JavaScriptFiles { + directory: "imports" + } + + ImageFiles { + directory: "content" + } + + Files { + filter: "*.conf" + files: ["qtquickcontrols2.conf"] + } + + Files { + filter: "qmldir" + directory: "." + } + + Files { + filter: "*.ttf;*.otf" + } + + Files { + filter: "*.wav;*.mp3" + } + + Files { + filter: "*.mp4" + } + + Files { + filter: "*.glsl;*.glslv;*.glslf;*.vsh;*.fsh;*.vert;*.frag" + } + + Files { + filter: "*.mesh" + directory: "content" + } + + Environment { + QT_QUICK_CONTROLS_CONF: "qtquickcontrols2.conf" + QT_AUTO_SCREEN_SCALE_FACTOR: "1" + QT_LOGGING_RULES: "qt.qml.connections=false" + QT_ENABLE_HIGHDPI_SCALING: "0" + /* Useful for debugging + QSG_VISUALIZE=batches + QSG_VISUALIZE=clip + QSG_VISUALIZE=changes + QSG_VISUALIZE=overdraw + */ + } + + qt6Project: true + + /* List of plugin directories passed to QML runtime */ + importPaths: [ "imports", "asset_imports", "backend_mock" ] + + /* Required for deployment */ + targetDirectory: "/opt/RobotArm" + + qdsVersion: "3.0" + + /* If any modules the project imports require widgets (e.g. QtCharts), widgetApp must be true */ + widgetApp: true + + multilanguageSupport: true + supportedLanguages: ["en"] + primaryLanguage: "en" + +} diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-3/testfile.qmltojson b/tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-3/testfile.qmltojson new file mode 100644 index 00000000000..9bf016a7e23 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-3/testfile.qmltojson @@ -0,0 +1,160 @@ +{ + "deployment": { + "targetDirectory": "/opt/RobotArm" + }, + "environment": { + "QT_AUTO_SCREEN_SCALE_FACTOR": "1", + "QT_ENABLE_HIGHDPI_SCALING": "0", + "QT_LOGGING_RULES": "qt.qml.connections=false", + "QT_QUICK_CONTROLS_CONF": "qtquickcontrols2.conf" + }, + "fileGroups": { + "config": { + "directories": [ + "." + ], + "files": [ + { + "name": "qtquickcontrols2.conf" + } + ], + "filters": [ + "*.conf" + ] + }, + "font": { + "directories": [ + "." + ], + "files": [ + ], + "filters": [ + "*.ttf", + "*.otf" + ] + }, + "image": { + "directories": [ + "content" + ], + "files": [ + ], + "filters": [ + "*.jpeg", + "*.jpg", + "*.png", + "*.svg", + "*.hdr", + ".ktx" + ] + }, + "javaScript": { + "directories": [ + "content", + "imports" + ], + "files": [ + ], + "filters": [ + "*.js", + "*.ts" + ] + }, + "meshes": { + "directories": [ + "content" + ], + "files": [ + ], + "filters": [ + "*.mesh" + ] + }, + "qml": { + "directories": [ + "content", + "imports", + "backend_mock" + ], + "files": [ + ], + "filters": [ + "*.qml" + ] + }, + "qmldir": { + "directories": [ + "." + ], + "files": [ + ], + "filters": [ + "qmldir" + ] + }, + "shader": { + "directories": [ + "." + ], + "files": [ + ], + "filters": [ + "*.glsl", + "*.glslv", + "*.glslf", + "*.vsh", + "*.fsh", + "*.vert", + "*.frag" + ] + }, + "sound": { + "directories": [ + "." + ], + "files": [ + ], + "filters": [ + "*.mp3", + "*.wav" + ] + }, + "video": { + "directories": [ + "." + ], + "files": [ + ], + "filters": [ + "*.mp4" + ] + } + }, + "fileVersion": 1, + "importPaths": [ + "imports", + "asset_imports", + "backend_mock" + ], + "language": { + "multiLanguageSupport": true, + "primaryLanguage": "en", + "supportedLanguages": [ + "en" + ] + }, + "mcuConfig": { + }, + "runConfig": { + "fileSelectors": [ + ], + "mainFile": "content/App.qml", + "mainUiFile": "content/MainScreen.ui.qml", + "widgetApp": true + }, + "shaderTool": { + }, + "versions": { + "designStudio": "3.0" + } +} diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-4/testfile.jsontoqml b/tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-4/testfile.jsontoqml new file mode 100644 index 00000000000..b0819a79a40 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-4/testfile.jsontoqml @@ -0,0 +1,65 @@ +\\ prop: json-converted +\\ prop: auto-generated + +import QmlProject + +Project { + mainFile: "OutrunHVAC.qml" + mainUiFile: "Screen01.ui.qml" + targetDirectory: "/opt/OutrunHVAC" + widgetApp: false + importPaths: [ "imports","asset_imports" ] + + qdsVersion: "" + quickVersion: "" + qt6Project: false + qtForMCUs: true + + multilanguageSupport: false + primaryLanguage: "" + supportedLanguages: [ ] + + Environment { + QMLSCENE_CORE_PROFILE: "true" + QT_AUTO_SCREEN_SCALE_FACTOR: "1" + QT_QUICK_CONTROLS_CONF: "qtquickcontrols2.conf" + } + + ShaderTool { + args: "" + files: [ ] + } + + QmlFiles { + directory: "." + } + + JavaScriptFiles { + directory: "." + } + + ImageFiles { + directory: "." + } + + Files { + directory: "." + filters: "*.conf" + files: [ "qtquickcontrols2.conf" ] + } + + Files { + directory: "." + filters: "*.ttf;*.otf" + } + + Files { + directory: "." + filters: "*.mesh;*.vert;*.frag" + } + + Files { + directory: "." + filters: "qmldir" + } +} diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-4/testfile.qmlproject b/tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-4/testfile.qmlproject new file mode 100644 index 00000000000..9b2e466fda5 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-4/testfile.qmlproject @@ -0,0 +1,55 @@ +/* File generated by Qt Creator */ + +import QmlProject 1.1 + +Project { + mainFile: "OutrunHVAC.qml" + + /* Include .qml, .js, and image files from current directory and subdirectories */ + QmlFiles { + directory: "." + } + + JavaScriptFiles { + directory: "." + } + + ImageFiles { + directory: "." + } + + Files { + filter: "*.conf" + files: ["qtquickcontrols2.conf"] + } + + Files { + filter: "qmldir" + directory: "." + } + + Files { + filter: "*.ttf;*.otf" + } + + Files { + filter: "*.mesh;*.vert;*.frag" + directory: "." + } + + Environment { + QT_QUICK_CONTROLS_CONF: "qtquickcontrols2.conf" + QT_AUTO_SCREEN_SCALE_FACTOR: "1" + QMLSCENE_CORE_PROFILE: "true" + } + + qt6Project: true + + /* List of plugin directories passed to QML runtime */ + importPaths: [ "imports", "asset_imports" ] + + /* Required for deployment */ + targetDirectory: "/opt/OutrunHVAC" + + mainUiFile: "Screen01.ui.qml" +} diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-4/testfile.qmltojson b/tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-4/testfile.qmltojson new file mode 100644 index 00000000000..2f12130de3c --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-4/testfile.qmltojson @@ -0,0 +1,113 @@ +{ + "deployment": { + "targetDirectory": "/opt/OutrunHVAC" + }, + "environment": { + "QMLSCENE_CORE_PROFILE": "true", + "QT_AUTO_SCREEN_SCALE_FACTOR": "1", + "QT_QUICK_CONTROLS_CONF": "qtquickcontrols2.conf" + }, + "fileGroups": { + "config": { + "directories": [ + "." + ], + "files": [ + { + "name": "qtquickcontrols2.conf" + } + ], + "filters": [ + "*.conf" + ] + }, + "font": { + "directories": [ + "." + ], + "files": [ + ], + "filters": [ + "*.ttf", + "*.otf" + ] + }, + "image": { + "directories": [ + "." + ], + "files": [ + ], + "filters": [ + "*.jpeg", + "*.jpg", + "*.png", + "*.svg", + "*.hdr", + ".ktx" + ] + }, + "javaScript": { + "directories": [ + "." + ], + "files": [ + ], + "filters": [ + "*.js", + "*.ts" + ] + }, + "meshes": { + "directories": [ + "." + ], + "files": [ + ], + "filters": [ + "*.mesh", + "*.vert", + "*.frag" + ] + }, + "qml": { + "directories": [ + "." + ], + "files": [ + ], + "filters": [ + "*.qml" + ] + }, + "qmldir": { + "directories": [ + "." + ], + "files": [ + ], + "filters": [ + "qmldir" + ] + } + }, + "fileVersion": 1, + "importPaths": [ + "imports", + "asset_imports" + ], + "language": { + }, + "mcuConfig": { + }, + "runConfig": { + "fileSelectors": [ + ], + "mainFile": "OutrunHVAC.qml", + "mainUiFile": "Screen01.ui.qml" + }, + "shaderTool": { + }, + "versions": { + } +} diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-5/testfile.jsontoqml b/tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-5/testfile.jsontoqml new file mode 100644 index 00000000000..aaf8d0fdc0a --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-5/testfile.jsontoqml @@ -0,0 +1,54 @@ +\\ prop: json-converted +\\ prop: auto-generated + +import QmlProject + +Project { + mainFile: "fileSelectors.qml" + mainUiFile: "" + targetDirectory: "/opt/fileSelectors" + widgetApp: false + importPaths: [ "imports" ] + + qdsVersion: "" + quickVersion: "" + qt6Project: false + qtForMCUs: true + + multilanguageSupport: false + primaryLanguage: "" + supportedLanguages: [ ] + + Environment { + QT_AUTO_SCREEN_SCALE_FACTOR: "1" + QT_QUICK_CONTROLS_CONF: "qtquickcontrols2.conf" + } + + ShaderTool { + args: "" + files: [ ] + } + + QmlFiles { + directory: "." + } + + JavaScriptFiles { + directory: "." + } + + ImageFiles { + directory: "." + } + + Files { + directory: "." + filters: "*.conf" + files: [ "qtquickcontrols2.conf" ] + } + + Files { + directory: "." + filters: "qmldir" + } +} diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-5/testfile.qmlproject b/tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-5/testfile.qmlproject new file mode 100644 index 00000000000..409b46bb7ff --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-5/testfile.qmlproject @@ -0,0 +1,44 @@ +/* File generated by Qt Creator */ + +import QmlProject 1.1 + +Project { + mainFile: "fileSelectors.qml" + + /* Include .qml, .js, and image files from current directory and subdirectories */ + QmlFiles { + directory: "." + } + + JavaScriptFiles { + directory: "." + } + + ImageFiles { + directory: "." + } + + Files { + filter: "*.conf" + files: ["qtquickcontrols2.conf"] + } + + Files { + filter: "qmldir" + directory: "." + } + + Environment { + QT_QUICK_CONTROLS_CONF: "qtquickcontrols2.conf" + QT_AUTO_SCREEN_SCALE_FACTOR: "1" + } + + /* List of plugin directories passed to QML runtime */ + importPaths: [ "imports" ] + + //fileSelectors: [ "WXGA", "darkTheme" ] + fileSelectors: [ "WXGA", "darkTheme", "ShowIndicator"] + + /* Required for deployment */ + targetDirectory: "/opt/fileSelectors" +} diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-5/testfile.qmltojson b/tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-5/testfile.qmltojson new file mode 100644 index 00000000000..b5e7f5f3b7e --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-5/testfile.qmltojson @@ -0,0 +1,90 @@ +{ + "deployment": { + "targetDirectory": "/opt/fileSelectors" + }, + "environment": { + "QT_AUTO_SCREEN_SCALE_FACTOR": "1", + "QT_QUICK_CONTROLS_CONF": "qtquickcontrols2.conf" + }, + "fileGroups": { + "config": { + "directories": [ + "." + ], + "files": [ + { + "name": "qtquickcontrols2.conf" + } + ], + "filters": [ + "*.conf" + ] + }, + "image": { + "directories": [ + "." + ], + "files": [ + ], + "filters": [ + "*.jpeg", + "*.jpg", + "*.png", + "*.svg", + "*.hdr", + ".ktx" + ] + }, + "javaScript": { + "directories": [ + "." + ], + "files": [ + ], + "filters": [ + "*.js", + "*.ts" + ] + }, + "qml": { + "directories": [ + "." + ], + "files": [ + ], + "filters": [ + "*.qml" + ] + }, + "qmldir": { + "directories": [ + "." + ], + "files": [ + ], + "filters": [ + "qmldir" + ] + } + }, + "fileVersion": 1, + "importPaths": [ + "imports" + ], + "language": { + }, + "mcuConfig": { + }, + "runConfig": { + "fileSelectors": [ + "WXGA", + "darkTheme", + "ShowIndicator" + ], + "mainFile": "fileSelectors.qml" + }, + "shaderTool": { + }, + "versions": { + } +} diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/MaterialBundle.qmlproject b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/MaterialBundle.qmlproject new file mode 100644 index 00000000000..479c20456be --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/MaterialBundle.qmlproject @@ -0,0 +1,112 @@ +import QmlProject + +Project { + mainFile: "content/App.qml" + mainUiFile: "Screen01.ui.qml" + + + /* Include .qml, .js, and image files from current directory and subdirectories */ + QmlFiles { + directory: "content" + } + + QmlFiles { + directory: "imports" + } + + JavaScriptFiles { + directory: "content" + } + + JavaScriptFiles { + directory: "imports" + } + + ImageFiles { + directory: "content" + } + + Files { + filter: "*.conf" + files: ["qtquickcontrols2.conf"] + } + + Files { + filter: "qmldir" + directory: "." + } + + Files { + filter: "*.ttf;*.otf" + } + + Files { + filter: "*.wav;*.mp3" + } + + Files { + filter: "*.mp4" + } + + Files { + filter: "*.glsl;*.glslv;*.glslf;*.vsh;*.fsh;*.vert;*.frag" + } + + Files { + filter: "*.mesh" + directory: "asset_imports" + } + + Files { + filter: "*.mesh" + directory: "content" + } + + Files { + filter: "*.qml" + directory: "asset_imports" + } + + ImageFiles { + directory: "asset_imports" + } + + Environment { + QT_QUICK_CONTROLS_CONF: "qtquickcontrols2.conf" + QT_AUTO_SCREEN_SCALE_FACTOR: "1" + QT_LOGGING_RULES: "qt.qml.connections=false" + QT_ENABLE_HIGHDPI_SCALING: "0" + /* Useful for debugging + QSG_VISUALIZE=batches + QSG_VISUALIZE=clip + QSG_VISUALIZE=changes + QSG_VISUALIZE=overdraw + */ + } + + qt6Project: true + + /* List of plugin directories passed to QML runtime */ + importPaths: [ "imports", "asset_imports" ] + + /* Required for deployment */ + targetDirectory: "/opt/MaterialLibrary" + + qdsVersion: "3.9" + + /* If any modules the project imports require widgets (e.g. QtCharts), widgetApp must be true */ + widgetApp: true + + /* args: Specifies command line arguments for qsb tool to generate shaders. + files: Specifies target files for qsb tool. If path is included, it must be relative to this file. + Wildcard '*' can be used in the file name part of the path. + e.g. files: [ "content/shaders/*.vert", "*.frag" ] */ + ShaderTool { + args: "-s --glsl \"100 es,120,150\" --hlsl 50 --msl 12" + files: [ "content/shaders/*" ] + } + + multilanguageSupport: true + supportedLanguages: ["en"] + primaryLanguage: "en" +} diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/MaterialBundle.qmlproject.qtds b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/MaterialBundle.qmlproject.qtds new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/MaterialBundle.qmlproject.qtds @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/MaterialLibrary.qrc b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/MaterialLibrary.qrc new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/MaterialLibrary.qrc @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/CMakeLists.txt b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/CMakeLists.txt new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/CMakeLists.txt @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/CMakeLists.txt b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/CMakeLists.txt new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/CMakeLists.txt @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/AcrylicPaintMaterial.qml b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/AcrylicPaintMaterial.qml new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/AcrylicPaintMaterial.qml @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/AluminiumMaterial.qml b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/AluminiumMaterial.qml new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/AluminiumMaterial.qml @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/AsphaltMaterial.qml b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/AsphaltMaterial.qml new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/AsphaltMaterial.qml @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/BrickMaterial.qml b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/BrickMaterial.qml new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/BrickMaterial.qml @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/CMakeLists.txt b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/CMakeLists.txt new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/CMakeLists.txt @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/CarPaintGlitterMaterial.qml b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/CarPaintGlitterMaterial.qml new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/CarPaintGlitterMaterial.qml @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/CarPaintMaterial.qml b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/CarPaintMaterial.qml new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/CarPaintMaterial.qml @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/CarbonFiberMaterial.qml b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/CarbonFiberMaterial.qml new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/CarbonFiberMaterial.qml @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/CeramicMaterial.qml b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/CeramicMaterial.qml new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/CeramicMaterial.qml @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/ChromeMaterial.qml b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/ChromeMaterial.qml new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/ChromeMaterial.qml @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/ConcreteMaterial.qml b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/ConcreteMaterial.qml new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/ConcreteMaterial.qml @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/CopperMaterial.qml b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/CopperMaterial.qml new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/CopperMaterial.qml @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/FabricMaterial.qml b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/FabricMaterial.qml new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/FabricMaterial.qml @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/FabricRoughMaterial.qml b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/FabricRoughMaterial.qml new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/FabricRoughMaterial.qml @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/FabricSatinMaterial.qml b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/FabricSatinMaterial.qml new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/FabricSatinMaterial.qml @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/GlassMaterial.qml b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/GlassMaterial.qml new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/GlassMaterial.qml @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/GlassTintedMaterial.qml b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/GlassTintedMaterial.qml new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/GlassTintedMaterial.qml @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/GoldMaterial.qml b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/GoldMaterial.qml new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/GoldMaterial.qml @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/LeatherMaterial.qml b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/LeatherMaterial.qml new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/LeatherMaterial.qml @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/MirrorMaterial.qml b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/MirrorMaterial.qml new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/MirrorMaterial.qml @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/PaperMaterial.qml b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/PaperMaterial.qml new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/PaperMaterial.qml @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/PlasticMatteMaterial.qml b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/PlasticMatteMaterial.qml new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/PlasticMatteMaterial.qml @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/PlasticShinyMaterial.qml b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/PlasticShinyMaterial.qml new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/PlasticShinyMaterial.qml @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/PlasticTexturedMaterial.qml b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/PlasticTexturedMaterial.qml new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/PlasticTexturedMaterial.qml @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/RubberMaterial.qml b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/RubberMaterial.qml new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/RubberMaterial.qml @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/SilverMaterial.qml b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/SilverMaterial.qml new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/SilverMaterial.qml @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/SteelBrushedMaterial.qml b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/SteelBrushedMaterial.qml new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/SteelBrushedMaterial.qml @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/SteelFloorMaterial.qml b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/SteelFloorMaterial.qml new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/SteelFloorMaterial.qml @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/SteelMaterial.qml b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/SteelMaterial.qml new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/SteelMaterial.qml @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/StoneMaterial.qml b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/StoneMaterial.qml new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/StoneMaterial.qml @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/WaxMaterial.qml b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/WaxMaterial.qml new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/WaxMaterial.qml @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/WoodMaterial.qml b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/WoodMaterial.qml new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/WoodMaterial.qml @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/WoodParquetMaterial.qml b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/WoodParquetMaterial.qml new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/WoodParquetMaterial.qml @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/WoodPlanksMaterial.qml b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/WoodPlanksMaterial.qml new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/WoodPlanksMaterial.qml @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/_asset_ref.json b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/_asset_ref.json new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/_asset_ref.json @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/acrylicpaint.metainfo b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/acrylicpaint.metainfo new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/acrylicpaint.metainfo @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/aluminium.metainfo b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/aluminium.metainfo new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/aluminium.metainfo @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/asphalt.metainfo b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/asphalt.metainfo new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/asphalt.metainfo @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/brick.metainfo b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/brick.metainfo new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/brick.metainfo @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/carbonfiber.metainfo b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/carbonfiber.metainfo new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/carbonfiber.metainfo @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/carpaint.metainfo b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/carpaint.metainfo new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/carpaint.metainfo @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/carpaintglitter.metainfo b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/carpaintglitter.metainfo new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/carpaintglitter.metainfo @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/ceramic.metainfo b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/ceramic.metainfo new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/ceramic.metainfo @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/chrome.metainfo b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/chrome.metainfo new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/chrome.metainfo @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/concrete.metainfo b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/concrete.metainfo new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/concrete.metainfo @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/copper.metainfo b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/copper.metainfo new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/copper.metainfo @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/fabric.metainfo b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/fabric.metainfo new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/fabric.metainfo @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/fabricrough.metainfo b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/fabricrough.metainfo new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/fabricrough.metainfo @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/fabricsatin.metainfo b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/fabricsatin.metainfo new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/fabricsatin.metainfo @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/glass.metainfo b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/glass.metainfo new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/glass.metainfo @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/glasstinted.metainfo b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/glasstinted.metainfo new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/glasstinted.metainfo @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/gold.metainfo b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/gold.metainfo new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/gold.metainfo @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/images/material.png b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/images/material.png new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/images/material.png @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/images/material16.png b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/images/material16.png new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/images/material16.png @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/images/material@2x.png b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/images/material@2x.png new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/images/material@2x.png @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/leather.metainfo b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/leather.metainfo new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/leather.metainfo @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/mirror.metainfo b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/mirror.metainfo new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/mirror.metainfo @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/paper.metainfo b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/paper.metainfo new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/paper.metainfo @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/plasticmatte.metainfo b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/plasticmatte.metainfo new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/plasticmatte.metainfo @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/plasticshiny.metainfo b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/plasticshiny.metainfo new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/plasticshiny.metainfo @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/plastictextured.metainfo b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/plastictextured.metainfo new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/plastictextured.metainfo @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/rubber.metainfo b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/rubber.metainfo new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/rubber.metainfo @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/silver.metainfo b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/silver.metainfo new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/silver.metainfo @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/steel.metainfo b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/steel.metainfo new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/steel.metainfo @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/steelbrushed.metainfo b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/steelbrushed.metainfo new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/steelbrushed.metainfo @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/steelfloor.metainfo b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/steelfloor.metainfo new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/steelfloor.metainfo @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/stone.metainfo b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/stone.metainfo new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/stone.metainfo @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/wax.metainfo b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/wax.metainfo new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/wax.metainfo @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/wood.metainfo b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/wood.metainfo new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/wood.metainfo @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/woodparquet.metainfo b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/woodparquet.metainfo new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/woodparquet.metainfo @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/woodplanks.metainfo b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/woodplanks.metainfo new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/woodplanks.metainfo @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Asphalt010_2K_NormalGL.png b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Asphalt010_2K_NormalGL.png new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Asphalt010_2K_NormalGL.png @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Asphalt010_2K_Opacity.png b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Asphalt010_2K_Opacity.png new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Asphalt010_2K_Opacity.png @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Asphalt010_2K_Roughness.png b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Asphalt010_2K_Roughness.png new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Asphalt010_2K_Roughness.png @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Bricks026_2K_AmbientOcclusion.png b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Bricks026_2K_AmbientOcclusion.png new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Bricks026_2K_AmbientOcclusion.png @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Bricks026_2K_Color.png b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Bricks026_2K_Color.png new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Bricks026_2K_Color.png @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Bricks026_2K_NormalGL.png b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Bricks026_2K_NormalGL.png new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Bricks026_2K_NormalGL.png @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Bricks026_2K_Roughness.png b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Bricks026_2K_Roughness.png new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Bricks026_2K_Roughness.png @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Concrete032_2K_NormalGL.png b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Concrete032_2K_NormalGL.png new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Concrete032_2K_NormalGL.png @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Concrete032_2K_Roughness.png b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Concrete032_2K_Roughness.png new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Concrete032_2K_Roughness.png @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/DiamondPlate001_2K_NormalGL.png b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/DiamondPlate001_2K_NormalGL.png new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/DiamondPlate001_2K_NormalGL.png @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/DiamondPlate001_2K_Roughness.png b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/DiamondPlate001_2K_Roughness.png new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/DiamondPlate001_2K_Roughness.png @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Fabric004_2K_NormalGL.png b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Fabric004_2K_NormalGL.png new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Fabric004_2K_NormalGL.png @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Fabric030_2K_Displacement.png b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Fabric030_2K_Displacement.png new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Fabric030_2K_Displacement.png @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Fabric030_2K_NormalGL.png b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Fabric030_2K_NormalGL.png new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Fabric030_2K_NormalGL.png @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Fabric030_2K_Roughness.png b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Fabric030_2K_Roughness.png new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Fabric030_2K_Roughness.png @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Fabric031_2K_Displacement.png b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Fabric031_2K_Displacement.png new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Fabric031_2K_Displacement.png @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Fabric031_2K_NormalGL.png b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Fabric031_2K_NormalGL.png new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Fabric031_2K_NormalGL.png @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Fabric031_2K_Roughness.png b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Fabric031_2K_Roughness.png new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Fabric031_2K_Roughness.png @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/LDR_RGB1_3.png b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/LDR_RGB1_3.png new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/LDR_RGB1_3.png @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Leather037_2K_Color.png b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Leather037_2K_Color.png new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Leather037_2K_Color.png @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Leather037_2K_NormalGL.png b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Leather037_2K_NormalGL.png new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Leather037_2K_NormalGL.png @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Leather037_2K_Roughness.png b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Leather037_2K_Roughness.png new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Leather037_2K_Roughness.png @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Metal009_2K_NormalGL.png b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Metal009_2K_NormalGL.png new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Metal009_2K_NormalGL.png @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Metal009_2K_Roughness.png b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Metal009_2K_Roughness.png new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Metal009_2K_Roughness.png @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Metal029_2K_Displacement.jpg b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Metal029_2K_Displacement.jpg new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Metal029_2K_Displacement.jpg @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Metal029_2K_Displacement.png b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Metal029_2K_Displacement.png new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Metal029_2K_Displacement.png @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Paint006_2K_AmbientOcclusion.png b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Paint006_2K_AmbientOcclusion.png new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Paint006_2K_AmbientOcclusion.png @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Paint006_2K_NormalGL.png b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Paint006_2K_NormalGL.png new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Paint006_2K_NormalGL.png @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Paint006_2K_Roughness.png b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Paint006_2K_Roughness.png new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Paint006_2K_Roughness.png @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Rock023_2K_AmbientOcclusion.png b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Rock023_2K_AmbientOcclusion.png new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Rock023_2K_AmbientOcclusion.png @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Rock023_2K_Color.png b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Rock023_2K_Color.png new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Rock023_2K_Color.png @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Rock023_2K_NormalGL.png b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Rock023_2K_NormalGL.png new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Rock023_2K_NormalGL.png @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Rock023_2K_Roughness.png b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Rock023_2K_Roughness.png new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Rock023_2K_Roughness.png @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Wood048_2K_Color.png b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Wood048_2K_Color.png new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Wood048_2K_Color.png @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Wood048_2K_NormalGL.png b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Wood048_2K_NormalGL.png new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Wood048_2K_NormalGL.png @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Wood048_2K_Roughness.png b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Wood048_2K_Roughness.png new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Wood048_2K_Roughness.png @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/WoodFloor044_2K_Color.png b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/WoodFloor044_2K_Color.png new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/WoodFloor044_2K_Color.png @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/WoodFloor044_2K_NormalGL.png b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/WoodFloor044_2K_NormalGL.png new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/WoodFloor044_2K_NormalGL.png @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/WoodFloor044_2K_Roughness.png b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/WoodFloor044_2K_Roughness.png new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/WoodFloor044_2K_Roughness.png @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/WoodFloor054_2K_AmbientOcclusion.png b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/WoodFloor054_2K_AmbientOcclusion.png new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/WoodFloor054_2K_AmbientOcclusion.png @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/WoodFloor054_2K_Color.png b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/WoodFloor054_2K_Color.png new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/WoodFloor054_2K_Color.png @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/WoodFloor054_2K_NormalGL.png b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/WoodFloor054_2K_NormalGL.png new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/WoodFloor054_2K_NormalGL.png @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/WoodFloor054_2K_Roughness.png b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/WoodFloor054_2K_Roughness.png new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/WoodFloor054_2K_Roughness.png @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/blurrynoise.tga b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/blurrynoise.tga new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/blurrynoise.tga @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/noisenormal.png b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/noisenormal.png new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/noisenormal.png @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/qmldir b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/qmldir new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/qmldir @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/shaders/CMakeLists.txt b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/shaders/CMakeLists.txt new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/shaders/CMakeLists.txt @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/shaders/SSS.frag b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/shaders/SSS.frag new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/shaders/SSS.frag @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/shaders/SSS.vert b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/shaders/SSS.vert new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/shaders/SSS.vert @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/shaders/carmat_simple.frag b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/shaders/carmat_simple.frag new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/shaders/carmat_simple.frag @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/shaders/carmat_simple.vert b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/shaders/carmat_simple.vert new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/shaders/carmat_simple.vert @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/shaders/carmat_simple_nf.frag b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/shaders/carmat_simple_nf.frag new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/shaders/carmat_simple_nf.frag @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/shaders/carmat_simple_nf.vert b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/shaders/carmat_simple_nf.vert new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/shaders/carmat_simple_nf.vert @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/shaders/glass.frag b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/shaders/glass.frag new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/shaders/glass.frag @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/shaders/glass.vert b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/shaders/glass.vert new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/shaders/glass.vert @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/shaders/satin.frag b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/shaders/satin.frag new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/shaders/satin.frag @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/shaders/satin.vert b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/shaders/satin.vert new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/shaders/satin.vert @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/App.qml b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/App.qml new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/App.qml @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/CMakeLists.txt b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/CMakeLists.txt new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/CMakeLists.txt @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/CustomRoundButton.qml b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/CustomRoundButton.qml new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/CustomRoundButton.qml @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/MaterialNames.qml b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/MaterialNames.qml new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/MaterialNames.qml @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/MouseRotator.qml b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/MouseRotator.qml new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/MouseRotator.qml @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/Screen01.ui.qml b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/Screen01.ui.qml new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/Screen01.ui.qml @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/fonts/OpenSans-Bold.ttf b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/fonts/OpenSans-Bold.ttf new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/fonts/OpenSans-Bold.ttf @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/fonts/OpenSans-Regular.ttf b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/fonts/OpenSans-Regular.ttf new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/fonts/OpenSans-Regular.ttf @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/fonts/fonts.txt b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/fonts/fonts.txt new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/fonts/fonts.txt @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/images/Ground_ShadowMap.png b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/images/Ground_ShadowMap.png new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/images/Ground_ShadowMap.png @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/images/HDR/dark_mode.png b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/images/HDR/dark_mode.png new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/images/HDR/dark_mode.png @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/images/HDR/day_mode.png b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/images/HDR/day_mode.png new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/images/HDR/day_mode.png @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/images/LDR_RGB1_3.png b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/images/LDR_RGB1_3.png new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/images/LDR_RGB1_3.png @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/images/QtLogo_HD.png b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/images/QtLogo_HD.png new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/images/QtLogo_HD.png @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/images/UI/innerMesh.png b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/images/UI/innerMesh.png new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/images/UI/innerMesh.png @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/images/UI/lightToggle.png b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/images/UI/lightToggle.png new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/images/UI/lightToggle.png @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/images/UI/outerMesh.png b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/images/UI/outerMesh.png new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/images/UI/outerMesh.png @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/images/UI/perfhudicon.png b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/images/UI/perfhudicon.png new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/images/UI/perfhudicon.png @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/images/UI/perfhudicon_on.png b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/images/UI/perfhudicon_on.png new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/images/UI/perfhudicon_on.png @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/images/White.png b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/images/White.png new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/images/White.png @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/images/checkmark.png b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/images/checkmark.png new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/images/checkmark.png @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/images/groundAlpha.png b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/images/groundAlpha.png new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/images/groundAlpha.png @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/images/qtlogo.png b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/images/qtlogo.png new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/images/qtlogo.png @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/images/scratchmap.png b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/images/scratchmap.png new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/images/scratchmap.png @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/images/shadow.png b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/images/shadow.png new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/images/shadow.png @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/images/vlkhcah_2K_AO.jpg b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/images/vlkhcah_2K_AO.jpg new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/images/vlkhcah_2K_AO.jpg @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/images/vlkhcah_2K_Albedo.jpg b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/images/vlkhcah_2K_Albedo.jpg new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/images/vlkhcah_2K_Albedo.jpg @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/images/vlkhcah_2K_Normal.jpg b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/images/vlkhcah_2K_Normal.jpg new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/images/vlkhcah_2K_Normal.jpg @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/images/vlkhcah_2K_Roughness.jpg b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/images/vlkhcah_2K_Roughness.jpg new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/images/vlkhcah_2K_Roughness.jpg @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/meshes/floor.mesh b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/meshes/floor.mesh new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/meshes/floor.mesh @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/meshes/materialBall.mesh b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/meshes/materialBall.mesh new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/meshes/materialBall.mesh @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/filelist.txt b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/filelist.txt new file mode 100644 index 00000000000..81fc1df7fcc --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/filelist.txt @@ -0,0 +1,126 @@ +asset_imports/ComponentBundles/MaterialBundle/images/Paint006_2K_AmbientOcclusion.png +asset_imports/ComponentBundles/MaterialBundle/images/WoodFloor044_2K_Color.png +asset_imports/ComponentBundles/MaterialBundle/images/Rock023_2K_NormalGL.png +asset_imports/ComponentBundles/MaterialBundle/images/Rock023_2K_Roughness.png +asset_imports/ComponentBundles/MaterialBundle/AcrylicPaintMaterial.qml +asset_imports/ComponentBundles/MaterialBundle/GoldMaterial.qml +asset_imports/ComponentBundles/MaterialBundle/shaders/satin.vert +asset_imports/ComponentBundles/MaterialBundle/CarPaintGlitterMaterial.qml +asset_imports/ComponentBundles/MaterialBundle/images/Asphalt010_2K_NormalGL.png +asset_imports/ComponentBundles/MaterialBundle/SilverMaterial.qml +asset_imports/ComponentBundles/MaterialBundle/SteelBrushedMaterial.qml +asset_imports/ComponentBundles/MaterialBundle/images/Rock023_2K_AmbientOcclusion.png +content/images/White.png +asset_imports/ComponentBundles/MaterialBundle/images/Fabric030_2K_NormalGL.png +asset_imports/ComponentBundles/MaterialBundle/images/WoodFloor044_2K_Roughness.png +content/images/UI/perfhudicon_on.png +asset_imports/ComponentBundles/MaterialBundle/shaders/carmat_simple.frag +asset_imports/ComponentBundles/MaterialBundle/images/DiamondPlate001_2K_NormalGL.png +asset_imports/ComponentBundles/MaterialBundle/images/Wood048_2K_Color.png +asset_imports/ComponentBundles/MaterialBundle/images/WoodFloor054_2K_AmbientOcclusion.png +content/images/shadow.png +asset_imports/ComponentBundles/MaterialBundle/images/WoodFloor054_2K_NormalGL.png +asset_imports/ComponentBundles/MaterialBundle/AsphaltMaterial.qml +asset_imports/ComponentBundles/MaterialBundle/images/Bricks026_2K_AmbientOcclusion.png +content/images/LDR_RGB1_3.png +imports/MaterialLibrary/EventListSimulator.qml +asset_imports/ComponentBundles/MaterialBundle/images/Fabric031_2K_Roughness.png +content/meshes/floor.mesh +asset_imports/ComponentBundles/MaterialBundle/shaders/satin.frag +asset_imports/ComponentBundles/MaterialBundle/images/Metal009_2K_Roughness.png +content/images/UI/innerMesh.png +content/images/UI/perfhudicon.png +content/images/qtlogo.png +content/images/vlkhcah_2K_Normal.jpg +asset_imports/ComponentBundles/MaterialBundle/images/Leather037_2K_Roughness.png +asset_imports/ComponentBundles/MaterialBundle/images/Leather037_2K_NormalGL.png +asset_imports/ComponentBundles/MaterialBundle/RubberMaterial.qml +content/App.qml +asset_imports/ComponentBundles/MaterialBundle/AluminiumMaterial.qml +content/images/HDR/dark_mode.png +asset_imports/ComponentBundles/MaterialBundle/SteelFloorMaterial.qml +asset_imports/ComponentBundles/MaterialBundle/images/WoodFloor054_2K_Color.png +asset_imports/ComponentBundles/MaterialBundle/shaders/SSS.frag +content/fonts/OpenSans-Regular.ttf +imports/MaterialLibrary/DirectoryFontLoader.qml +asset_imports/ComponentBundles/MaterialBundle/LeatherMaterial.qml +asset_imports/ComponentBundles/MaterialBundle/FabricRoughMaterial.qml +asset_imports/ComponentBundles/MaterialBundle/images/LDR_RGB1_3.png +asset_imports/ComponentBundles/MaterialBundle/PlasticTexturedMaterial.qml +asset_imports/ComponentBundles/MaterialBundle/CopperMaterial.qml +asset_imports/ComponentBundles/MaterialBundle/PlasticShinyMaterial.qml +content/images/checkmark.png +asset_imports/ComponentBundles/MaterialBundle/SteelMaterial.qml +asset_imports/ComponentBundles/MaterialBundle/images/Bricks026_2K_Roughness.png +asset_imports/ComponentBundles/MaterialBundle/ChromeMaterial.qml +asset_imports/ComponentBundles/MaterialBundle/CarbonFiberMaterial.qml +asset_imports/ComponentBundles/MaterialBundle/images/Fabric030_2K_Displacement.png +asset_imports/ComponentBundles/MaterialBundle/images/Metal029_2K_Displacement.jpg +asset_imports/ComponentBundles/MaterialBundle/FabricSatinMaterial.qml +asset_imports/ComponentBundles/MaterialBundle/shaders/carmat_simple.vert +asset_imports/ComponentBundles/MaterialBundle/designer/images/material@2x.png +asset_imports/ComponentBundles/MaterialBundle/WoodMaterial.qml +asset_imports/ComponentBundles/MaterialBundle/PlasticMatteMaterial.qml +asset_imports/ComponentBundles/MaterialBundle/images/Bricks026_2K_NormalGL.png +asset_imports/ComponentBundles/MaterialBundle/images/Concrete032_2K_Roughness.png +asset_imports/ComponentBundles/MaterialBundle/CarPaintMaterial.qml +content/images/Ground_ShadowMap.png +content/MaterialNames.qml +asset_imports/ComponentBundles/MaterialBundle/shaders/glass.vert +asset_imports/ComponentBundles/MaterialBundle/images/Rock023_2K_Color.png +asset_imports/ComponentBundles/MaterialBundle/BrickMaterial.qml +content/images/UI/lightToggle.png +asset_imports/ComponentBundles/MaterialBundle/images/Concrete032_2K_NormalGL.png +content/MouseRotator.qml +asset_imports/ComponentBundles/MaterialBundle/images/Paint006_2K_Roughness.png +asset_imports/ComponentBundles/MaterialBundle/shaders/glass.frag +asset_imports/ComponentBundles/MaterialBundle/images/Metal029_2K_Displacement.png +asset_imports/ComponentBundles/MaterialBundle/ConcreteMaterial.qml +asset_imports/ComponentBundles/MaterialBundle/GlassMaterial.qml +asset_imports/ComponentBundles/MaterialBundle/WoodParquetMaterial.qml +imports/MaterialLibrary/Constants.qml +content/meshes/materialBall.mesh +asset_imports/ComponentBundles/MaterialBundle/shaders/carmat_simple_nf.vert +asset_imports/ComponentBundles/MaterialBundle/images/WoodFloor044_2K_NormalGL.png +content/images/vlkhcah_2K_AO.jpg +asset_imports/ComponentBundles/MaterialBundle/MirrorMaterial.qml +asset_imports/ComponentBundles/MaterialBundle/images/Wood048_2K_NormalGL.png +imports/MaterialLibrary/qmldir +asset_imports/ComponentBundles/MaterialBundle/images/Leather037_2K_Color.png +content/Screen01.ui.qml +asset_imports/ComponentBundles/MaterialBundle/CeramicMaterial.qml +asset_imports/ComponentBundles/MaterialBundle/images/WoodFloor054_2K_Roughness.png +asset_imports/ComponentBundles/MaterialBundle/images/Fabric004_2K_NormalGL.png +asset_imports/ComponentBundles/MaterialBundle/images/noisenormal.png +asset_imports/ComponentBundles/MaterialBundle/images/Asphalt010_2K_Opacity.png +asset_imports/ComponentBundles/MaterialBundle/FabricMaterial.qml +asset_imports/ComponentBundles/MaterialBundle/qmldir +asset_imports/ComponentBundles/MaterialBundle/PaperMaterial.qml +content/images/UI/outerMesh.png +imports/MaterialLibrary/EventListModel.qml +asset_imports/ComponentBundles/MaterialBundle/images/Bricks026_2K_Color.png +asset_imports/ComponentBundles/MaterialBundle/WaxMaterial.qml +asset_imports/ComponentBundles/MaterialBundle/shaders/carmat_simple_nf.frag +content/images/scratchmap.png +asset_imports/ComponentBundles/MaterialBundle/images/Metal009_2K_NormalGL.png +content/fonts/OpenSans-Bold.ttf +asset_imports/ComponentBundles/MaterialBundle/StoneMaterial.qml +asset_imports/ComponentBundles/MaterialBundle/shaders/SSS.vert +content/images/HDR/day_mode.png +content/images/QtLogo_HD.png +content/images/vlkhcah_2K_Albedo.jpg +asset_imports/ComponentBundles/MaterialBundle/images/Wood048_2K_Roughness.png +qtquickcontrols2.conf +asset_imports/ComponentBundles/MaterialBundle/images/Asphalt010_2K_Roughness.png +asset_imports/ComponentBundles/MaterialBundle/images/Fabric030_2K_Roughness.png +asset_imports/ComponentBundles/MaterialBundle/images/Fabric031_2K_NormalGL.png +asset_imports/ComponentBundles/MaterialBundle/images/Fabric031_2K_Displacement.png +asset_imports/ComponentBundles/MaterialBundle/GlassTintedMaterial.qml +asset_imports/ComponentBundles/MaterialBundle/designer/images/material.png +asset_imports/ComponentBundles/MaterialBundle/images/DiamondPlate001_2K_Roughness.png +asset_imports/ComponentBundles/MaterialBundle/WoodPlanksMaterial.qml +content/CustomRoundButton.qml +content/images/groundAlpha.png +asset_imports/ComponentBundles/MaterialBundle/images/Paint006_2K_NormalGL.png +content/images/vlkhcah_2K_Roughness.jpg +asset_imports/ComponentBundles/MaterialBundle/designer/images/material16.png \ No newline at end of file diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/imports/CMakeLists.txt b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/imports/CMakeLists.txt new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/imports/CMakeLists.txt @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/imports/MaterialLibrary/CMakeLists.txt b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/imports/MaterialLibrary/CMakeLists.txt new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/imports/MaterialLibrary/CMakeLists.txt @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/imports/MaterialLibrary/Constants.qml b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/imports/MaterialLibrary/Constants.qml new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/imports/MaterialLibrary/Constants.qml @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/imports/MaterialLibrary/DirectoryFontLoader.qml b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/imports/MaterialLibrary/DirectoryFontLoader.qml new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/imports/MaterialLibrary/DirectoryFontLoader.qml @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/imports/MaterialLibrary/EventListModel.qml b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/imports/MaterialLibrary/EventListModel.qml new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/imports/MaterialLibrary/EventListModel.qml @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/imports/MaterialLibrary/EventListSimulator.qml b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/imports/MaterialLibrary/EventListSimulator.qml new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/imports/MaterialLibrary/EventListSimulator.qml @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/imports/MaterialLibrary/designer/plugin.metainfo b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/imports/MaterialLibrary/designer/plugin.metainfo new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/imports/MaterialLibrary/designer/plugin.metainfo @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/imports/MaterialLibrary/qmldir b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/imports/MaterialLibrary/qmldir new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/imports/MaterialLibrary/qmldir @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/main.qml b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/main.qml new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/main.qml @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/qmlcomponents b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/qmlcomponents new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/qmlcomponents @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/qmlmodules b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/qmlmodules new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/qmlmodules @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/qtquickcontrols2.conf b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/qtquickcontrols2.conf new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/qtquickcontrols2.conf @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/share.qrc b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/share.qrc new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/share.qrc @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/src/app_environment.h b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/src/app_environment.h new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/src/app_environment.h @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/src/import_qml_plugins.h b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/src/import_qml_plugins.h new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/src/import_qml_plugins.h @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/src/main.cpp b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/src/main.cpp new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/src/main.cpp @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/translations.db b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/translations.db new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/translations.db @@ -0,0 +1 @@ + diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/getter-setter/testfile-1.qmlproject b/tests/auto/qml/qmlprojectmanager/projectitem/data/getter-setter/testfile-1.qmlproject new file mode 100644 index 00000000000..ae866ca3974 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/getter-setter/testfile-1.qmlproject @@ -0,0 +1,96 @@ +import QmlProject + +Project { + mainFile: "content/App.qml" + mainUiFile: "Screen01.ui.qml" + + qt6Project: true + widgetApp: true + qtForMCUs: true + forceFreeType: true + + importPaths: [ "imports", "asset_imports" ] + targetDirectory: "/opt/targetDirectory" + fileSelectors: [ "WXGA", "darkTheme", "ShowIndicator"] + + qdsVersion: "3.9" + quickVersion: "6.2" + + multilanguageSupport: true + supportedLanguages: ["en" , "fr"] + primaryLanguage: "en" + + QmlFiles { + directory: "content" + } + + QmlFiles { + directory: "imports" + } + + JavaScriptFiles { + directory: "content" + } + + JavaScriptFiles { + directory: "imports" + } + + ImageFiles { + directory: "content" + } + + Files { + filter: "*.conf" + files: ["qtquickcontrols2.conf"] + } + + Files { + filter: "qmldir" + directory: "." + } + + Files { + filter: "*.ttf;*.otf" + } + + Files { + filter: "*.wav;*.mp3" + } + + Files { + filter: "*.mp4" + } + + Files { + filter: "*.glsl;*.glslv;*.glslf;*.vsh;*.fsh;*.vert;*.frag" + } + + Files { + filter: "*.mesh" + directory: "asset_imports" + } + + Files { + filter: "*.mesh" + directory: "content" + } + + Files { + filter: "*.qml" + directory: "asset_imports" + } + + ImageFiles { + directory: "asset_imports" + } + + Environment { + QT_QUICK_CONTROLS_CONF: "qtquickcontrols2.conf" + } + + ShaderTool { + args: "-s --glsl \"100 es,120,150\" --hlsl 50 --msl 12" + files: [ "content/shaders/*" ] + } +} diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/getter-setter/testfile-2.qmlproject b/tests/auto/qml/qmlprojectmanager/projectitem/data/getter-setter/testfile-2.qmlproject new file mode 100644 index 00000000000..66adaaa7d91 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/getter-setter/testfile-2.qmlproject @@ -0,0 +1,44 @@ +import QmlProject + +Project { + mainFile: "" + mainUiFile: "" + + qt6Project: false + widgetApp: false + qtForMCUs: false + forceFreeType: false + + importPaths: [ ] + targetDirectory: "" + fileSelectors: [ ] + + qdsVersion: "" + quickVersion: "" + + multilanguageSupport: false + supportedLanguages: [ ] + primaryLanguage: "" + + QmlFiles { + directory: "" + } + + JavaScriptFiles { + directory: "" + } + + ImageFiles { + directory: "" + } + + Files { + filter: "*.testcontent" + files: [ ] + } + + ShaderTool { + args: "" + files: [ ] + } +} diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/test-converters.cpp b/tests/auto/qml/qmlprojectmanager/projectitem/test-converters.cpp new file mode 100644 index 00000000000..d232db3c0f0 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/test-converters.cpp @@ -0,0 +1,108 @@ +// Copyright (C) 2016 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 "common.h" + +#include "projectitem/converters.h" + +//#define REGENERATE_DATA_SETS + +using namespace QmlProjectManager; + +class DataSet +{ +public: + DataSet(const QString &dataSetName) + : m_dataSetDirectory(testDataRootDir.path() + "/converter/" + dataSetName) + , m_qmlProjectFile(Utils::FilePath::fromString( + QString(m_dataSetDirectory.absolutePath()).append("/testfile.qmlproject"))) + , m_jsonToQmlProjectFile(Utils::FilePath::fromString( + QString(m_dataSetDirectory.absolutePath()).append("/testfile.jsontoqml"))) + , m_qmlProjectToJsonFile(Utils::FilePath::fromString( + QString(m_dataSetDirectory.absolutePath()).append("/testfile.qmltojson"))) + {} + + QString qmlProjectContent() const + { + return (m_qmlProjectFile.fileContents() ? m_qmlProjectFile.fileContents().value() : QString{}); + } + QString jsonToQmlProjectContent() const + { + return m_jsonToQmlProjectFile.fileContents() ? m_jsonToQmlProjectFile.fileContents().value() + : QString{}; + } + QString qmlProjectToJsonContent() const + { + return m_qmlProjectToJsonFile.fileContents() ? m_qmlProjectToJsonFile.fileContents().value() + : QString{}; + } + + QString dataSetPath() const { return m_dataSetDirectory.absolutePath(); } + QString dataSetName() const { return m_dataSetDirectory.dirName(); } + Utils::FilePath qmlProjectFile() const { return m_qmlProjectFile; } + Utils::FilePath jsonToQmlProjectFile() const { return m_jsonToQmlProjectFile; } + Utils::FilePath qmlProjectToJsonFile() const { return m_qmlProjectToJsonFile; } + +private: + QDir m_dataSetDirectory; + Utils::FilePath m_qmlProjectFile; + Utils::FilePath m_jsonToQmlProjectFile; + Utils::FilePath m_qmlProjectToJsonFile; +}; + +QVector getDataSets() +{ + QVector dataSets; + QDir testDataDir(testDataRootDir.path().append("/converter")); + testDataDir.setNameFilters({"test-set-*"}); + foreach (const QString &directory, testDataDir.entryList()) { + dataSets.append(DataSet{directory}); + } + return dataSets; +} + +#ifndef REGENERATE_DATA_SETS +TEST(QmlProjectConverterTests, QmlProjectToJson) +{ + foreach (const DataSet &dataSet, getDataSets()) { + qDebug() << "Data set name:" << dataSet.dataSetName(); + + QString targetContent = dataSet.qmlProjectToJsonContent().replace("\r\n", "\n"); + + QJsonObject jsonObject{ + QmlProjectManager::Converters::qmlProjectTojson(dataSet.qmlProjectFile())}; + QString convertedContent{QJsonDocument(jsonObject).toJson()}; + + ASSERT_EQ(convertedContent.toStdString(), targetContent.toStdString()); + } +} + +TEST(QmlProjectConverterTests, JsonToQmlProject) +{ + foreach (const DataSet &dataSet, getDataSets()) { + qDebug() << "Data set name:" << dataSet.dataSetName(); + + QString targetContent = dataSet.jsonToQmlProjectContent().replace("\r\n", "\n"); + + QString jsonContent = dataSet.qmlProjectToJsonContent(); + QJsonObject jsonObject{QJsonDocument::fromJson(jsonContent.toLatin1()).object()}; + QString convertedContent = QmlProjectManager::Converters::jsonToQmlProject(jsonObject); + + ASSERT_EQ(convertedContent.toStdString(), targetContent.toStdString()); + } +} + +#else +TEST(QmlProjectConverterTests, RegenerateDataSets) +{ + foreach (const DataSet &dataSet, getDataSets()) { + qDebug() << "Regenerating data set:" << dataSet.dataSetName(); + QJsonObject qml2json = Converters::qmlProjectTojson(dataSet.qmlProjectFile()); + QString json2qml = Converters::jsonToQmlProject(qml2json); + + dataSet.qmlProjectToJsonFile().writeFileContents(QJsonDocument(qml2json).toJson()); + dataSet.jsonToQmlProjectFile().writeFileContents(json2qml.toUtf8()); + } + SUCCEED(); +} +#endif diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/test-filefilters.cpp b/tests/auto/qml/qmlprojectmanager/projectitem/test-filefilters.cpp new file mode 100644 index 00000000000..bb7a60f8b23 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/test-filefilters.cpp @@ -0,0 +1,25 @@ +// Copyright (C) 2016 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 "common.h" +#include "projectitem/qmlprojectitem.h" + +const Utils::FilePath testDataDir{ + Utils::FilePath::fromString(testDataRootDir.path() + "/file-filters")}; +const Utils::FilePath projectFilePath{testDataDir.pathAppended("/MaterialBundle.qmlproject")}; +const QmlProjectManager::QmlProjectItem projectItem{projectFilePath}; +const Utils::FilePath fileListPath{testDataDir.pathAppended("/filelist.txt")}; + +TEST(QmlProjectItemFileFilterTests, TestFileFilters) +{ + QStringList fileNameList = QString::fromUtf8(fileListPath.fileContents().value()).replace("\r\n", "\n").split("\n"); + + for (const Utils::FilePath &filePath : projectItem.files()) { + const QString fileName{filePath.relativePathFrom(testDataDir).path()}; + const int index = fileNameList.indexOf(fileName); + ASSERT_NE(index, -1) << "file_is_missing_in_the_filelist:: " + fileName.toStdString(); + fileNameList.remove(index); + } + + ASSERT_EQ(fileNameList.size(), 0); +} diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/test-getters.cpp b/tests/auto/qml/qmlprojectmanager/projectitem/test-getters.cpp new file mode 100644 index 00000000000..26be73b9ce0 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/test-getters.cpp @@ -0,0 +1,150 @@ +// Copyright (C) 2016 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 "common.h" +#include "projectitem/qmlprojectitem.h" + +static QString testDataDir{testDataRootDir.path() + "/getter-setter"}; +static QString qmlProjectFilePath1(testDataDir + "/testfile-1.qmlproject"); +static QString qmlProjectFilePath2(testDataDir + "/testfile-2.qmlproject"); + +struct TestDataSet +{ +public: + const QmlProjectManager::QmlProjectItem projectItem1{ + Utils::FilePath::fromString(qmlProjectFilePath1)}; + QmlProjectManager::QmlProjectItem projectItem2{Utils::FilePath::fromString(qmlProjectFilePath2)}; +} dataSet; + +TEST(QmlProjectProjectItemGetterTests, GetMainFileProject) +{ + ASSERT_EQ(dataSet.projectItem1.mainFile(), "content/App.qml"); + ASSERT_EQ(dataSet.projectItem2.mainFile(), ""); +} + +TEST(QmlProjectProjectItemGetterTests, GetMainUIFileProject) +{ + ASSERT_EQ(dataSet.projectItem1.mainUiFile(), "Screen01.ui.qml"); + ASSERT_EQ(dataSet.projectItem2.mainUiFile(), ""); +} + +TEST(QmlProjectProjectItemGetterTests, GetMcuProject) +{ + ASSERT_EQ(dataSet.projectItem1.isQt4McuProject(), true); + ASSERT_EQ(dataSet.projectItem2.isQt4McuProject(), false); +} + +TEST(QmlProjectProjectItemGetterTests, GetQt6Project) +{ + ASSERT_EQ(dataSet.projectItem1.isQt6Project(), true); + ASSERT_EQ(dataSet.projectItem2.isQt6Project(), false); +} + +TEST(QmlProjectProjectItemGetterTests, GetSourceDirectory) +{ + ASSERT_EQ(dataSet.projectItem1.sourceDirectory().path(), testDataDir); +} + +TEST(QmlProjectProjectItemGetterTests, GetTargetDirectory) +{ + ASSERT_EQ(dataSet.projectItem1.targetDirectory(), "/opt/targetDirectory"); + ASSERT_EQ(dataSet.projectItem2.targetDirectory(), ""); +} + +TEST(QmlProjectProjectItemGetterTests, GetImportPaths) +{ + QString valsToCompare1 = dataSet.projectItem1.importPaths().join(";"); + QString valsToCompare2 = dataSet.projectItem2.importPaths().join(";"); + + ASSERT_EQ(valsToCompare1.toStdString(), "imports;asset_imports"); + ASSERT_EQ(valsToCompare2.toStdString(), ""); +} + +TEST(QmlProjectProjectItemGetterTests, GetFileSelectors) +{ + QString valsToCompare1 = dataSet.projectItem1.fileSelectors().join(";"); + QString valsToCompare2 = dataSet.projectItem2.fileSelectors().join(";"); + + ASSERT_EQ(valsToCompare1.toStdString(), "WXGA;darkTheme;ShowIndicator"); + ASSERT_EQ(valsToCompare2.toStdString(), ""); +} + +TEST(QmlProjectProjectItemGetterTests, GetMultiLanguageSupport) +{ + ASSERT_EQ(dataSet.projectItem1.multilanguageSupport(), true); + ASSERT_EQ(dataSet.projectItem2.multilanguageSupport(), false); +} + +TEST(QmlProjectProjectItemGetterTests, GetSupportedLanguages) +{ + QString valsToCompare1 = dataSet.projectItem1.supportedLanguages().join(";"); + QString valsToCompare2 = dataSet.projectItem2.supportedLanguages().join(";"); + + ASSERT_EQ(valsToCompare1.toStdString(), "en;fr"); + ASSERT_EQ(valsToCompare2.toStdString(), ""); +} + +TEST(QmlProjectProjectItemGetterTests, GetPrimaryLanguage) +{ + ASSERT_EQ(dataSet.projectItem1.primaryLanguage(), "en"); + ASSERT_EQ(dataSet.projectItem2.primaryLanguage(), ""); +} + +TEST(QmlProjectProjectItemGetterTests, GetWidgetApp) +{ + ASSERT_EQ(dataSet.projectItem1.widgetApp(), true); + ASSERT_EQ(dataSet.projectItem2.widgetApp(), false); +} + +TEST(QmlProjectProjectItemGetterTests, GetFileList) +{ + QString valsToCompare1, valsToCompare2; + + for (const auto &file : dataSet.projectItem1.files()) { + valsToCompare1.append(file.path()).append(";"); + } + + for (const auto &file : dataSet.projectItem2.files()) { + valsToCompare2.append(file.path()).append(";"); + } + + valsToCompare1.remove(valsToCompare1.length() - 1, 1); + valsToCompare2.remove(valsToCompare2.length() - 1, 1); + + ASSERT_EQ(valsToCompare1.toStdString(), + testDataDir.toStdString() + "/qtquickcontrols2.conf"); + ASSERT_EQ(valsToCompare2.toStdString(), ""); +} + +TEST(QmlProjectProjectItemGetterTests, GetShaderToolArgs) +{ + QString valsToCompare1 = dataSet.projectItem1.shaderToolArgs().join(";"); + QString valsToCompare2 = dataSet.projectItem2.shaderToolArgs().join(";"); + + ASSERT_EQ(valsToCompare1.toStdString(), "-s;--glsl;\"100 es,120,150\";--hlsl;50;--msl;12"); + ASSERT_EQ(valsToCompare2.toStdString(), ""); +} + +TEST(QmlProjectProjectItemGetterTests, GetShaderToolFiles) +{ + QString valsToCompare1 = dataSet.projectItem1.shaderToolFiles().join(";"); + QString valsToCompare2 = dataSet.projectItem2.shaderToolFiles().join(";"); + + ASSERT_EQ(valsToCompare1.toStdString(), "content/shaders/*"); + ASSERT_EQ(valsToCompare2.toStdString(), ""); +} + +TEST(QmlProjectProjectItemGetterTests, GetEnvironment) +{ + Utils::EnvironmentItems env1 = dataSet.projectItem1.environment(); + Utils::EnvironmentItems env2 = dataSet.projectItem2.environment(); + + ASSERT_EQ(env1[0].value.toStdString(), "qtquickcontrols2.conf"); + ASSERT_EQ(env2.isEmpty(), true); +} + +TEST(QmlProjectProjectItemGetterTests, GetForceFreeType) +{ + ASSERT_EQ(dataSet.projectItem1.forceFreeType(), true); + ASSERT_EQ(dataSet.projectItem2.forceFreeType(), false); +} diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/test-setters.cpp b/tests/auto/qml/qmlprojectmanager/projectitem/test-setters.cpp new file mode 100644 index 00000000000..82524aabd06 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/test-setters.cpp @@ -0,0 +1,150 @@ +// Copyright (C) 2016 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 "common.h" +#include "projectitem/qmlprojectitem.h" + +using namespace QmlProjectManager; + +static QString filePath(testDataRootDir.path() + "/getter-setter/testfile-1.qmlproject"); +static QmlProjectItem projectItem{Utils::FilePath::fromString(filePath)}; + +#define call_mem_fn(ptr) ((projectItem).*(ptr)) + +template +void testerTemplate(void (QmlProjectItem::*setterFunc)(const T &), + T (QmlProjectItem::*getterFunc)(void) const, + const T &testingData) +{ + call_mem_fn(setterFunc)({testingData}); + ASSERT_EQ(call_mem_fn(getterFunc)(), testingData); +} + +template +void testerTemplate(void (QmlProjectItem::*setterFunc)(const T &), + T (QmlProjectItem::*getterFunc)(void) const, + void (QmlProjectItem::*adderFunc)(const Y &), + const Y &testingData) +{ + call_mem_fn(setterFunc)({testingData}); + ASSERT_EQ(call_mem_fn(getterFunc)(), T{testingData}); + + call_mem_fn(setterFunc)({}); + call_mem_fn(adderFunc)(testingData); + ASSERT_EQ(call_mem_fn(getterFunc)(), T{testingData}); +} + +TEST(QmlProjectProjectItemSetterTests, SetMainFileProject) +{ + testerTemplate(&QmlProjectItem::setMainFile, &QmlProjectItem::mainFile, "testing"); +} + +TEST(QmlProjectProjectItemSetterTests, SetMainUIFileProject) +{ + testerTemplate(&QmlProjectItem::setMainUiFile, &QmlProjectItem::mainUiFile, "testing"); +} + +TEST(QmlProjectProjectItemSetterTests, SetImportPaths) +{ + testerTemplate(&QmlProjectItem::setImportPaths, + &QmlProjectItem::importPaths, + &QmlProjectItem::addImportPath, + "testing"); +} + +TEST(QmlProjectProjectItemSetterTests, SetFileSelectors) +{ + testerTemplate(&QmlProjectItem::setFileSelectors, + &QmlProjectItem::fileSelectors, + &QmlProjectItem::addFileSelector, + "testing"); +} + +TEST(QmlProjectProjectItemSetterTests, SetMultiLanguageSupport) +{ + testerTemplate(&QmlProjectItem::setMultilanguageSupport, + &QmlProjectItem::multilanguageSupport, + true); + + testerTemplate(&QmlProjectItem::setMultilanguageSupport, + &QmlProjectItem::multilanguageSupport, + false); +} + +TEST(QmlProjectProjectItemSetterTests, SetSupportedLanguages) +{ + testerTemplate(&QmlProjectItem::setSupportedLanguages, + &QmlProjectItem::supportedLanguages, + &QmlProjectItem::addSupportedLanguage, + "testing"); +} + +TEST(QmlProjectProjectItemSetterTests, SetPrimaryLanguage) +{ + testerTemplate(&QmlProjectItem::setPrimaryLanguage, + &QmlProjectItem::primaryLanguage, + "testing"); +} + +TEST(QmlProjectProjectItemSetterTests, SetWidgetApp) +{ + testerTemplate(&QmlProjectItem::setWidgetApp, &QmlProjectItem::widgetApp, true); + testerTemplate(&QmlProjectItem::setWidgetApp, &QmlProjectItem::widgetApp, false); +} + +TEST(QmlProjectProjectItemSetterTests, SetShaderToolArgs) +{ + testerTemplate(&QmlProjectItem::setShaderToolArgs, + &QmlProjectItem::shaderToolArgs, + &QmlProjectItem::addShaderToolArg, + "testing"); +} + +TEST(QmlProjectProjectItemSetterTests, SetShaderToolFiles) +{ + testerTemplate(&QmlProjectItem::setShaderToolFiles, + &QmlProjectItem::shaderToolFiles, + &QmlProjectItem::addShaderToolFile, + "testing"); +} + +TEST(QmlProjectProjectItemSetterTests, SetForceFreeType) +{ + testerTemplate(&QmlProjectItem::setForceFreeType, &QmlProjectItem::forceFreeType, true); + testerTemplate(&QmlProjectItem::setForceFreeType, &QmlProjectItem::forceFreeType, false); +} + +/** +TEST(QmlProjectProjectItemSetterTests, SetEnvironment) +{ + //FIXME: implement this +} + +*/ + +// not available as of now +//TEST(QmlProjectProjectItemSetterTests, SetMcuProject) +//{ +// ASSERT_EQ(dataSet.projectItem1.isQt4McuProject(), true); +// ASSERT_EQ(dataSet.projectItem2.isQt4McuProject(), false); +//} + +// not available as of now +//TEST(QmlProjectProjectItemSetterTests, SetQt6Project) +//{ +// ASSERT_EQ(dataSet.projectItem1.isQt6Project(), true); +// ASSERT_EQ(dataSet.projectItem2.isQt6Project(), false); +//} + +// not available as of now +//TEST(QmlProjectProjectItemSetterTests, SetSourceDirectory) +//{ +// ASSERT_EQ(dataSet.projectItem1.sourceDirectory(), testDataDir.path()); +//} + +// not available as of now +//TEST(QmlProjectProjectItemSetterTests, SetTargetDirectory) +//{ +// ASSERT_EQ(dataSet.projectItem1.targetDirectory(), "/opt/targetDirectory"); +// ASSERT_EQ(dataSet.projectItem2.targetDirectory(), ""); +//} diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/tst_projectitem.cpp b/tests/auto/qml/qmlprojectmanager/projectitem/tst_projectitem.cpp new file mode 100644 index 00000000000..023f8ffdb26 --- /dev/null +++ b/tests/auto/qml/qmlprojectmanager/projectitem/tst_projectitem.cpp @@ -0,0 +1,8 @@ + +#include "common.h" + +int main(int argc, char *argv[]) +{ + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} From 4dfe3d31eb6a68f701694deb2d44294907217281 Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Mon, 27 Mar 2023 14:42:12 +0300 Subject: [PATCH 029/192] QmlDesigner: Use icon-fonts as the source of backspace icon The backspace icon used to be drawn by painter lines, and this method is replaced by an icon-font symbol. Task-number: QDS-9513 Change-Id: Iaa19f286bc961d71ddc182bd2c431bc024c7aa79 Reviewed-by: Thomas Hartmann --- share/qtcreator/qmldesigner/designericons.json | 3 +++ .../qmldesigner/components/componentcore/designericons.h | 1 + .../qmldesigner/components/componentcore/qmleditormenu.cpp | 2 ++ 3 files changed, 6 insertions(+) diff --git a/share/qtcreator/qmldesigner/designericons.json b/share/qtcreator/qmldesigner/designericons.json index f695e63b563..ddbfcf5a850 100644 --- a/share/qtcreator/qmldesigner/designericons.json +++ b/share/qtcreator/qmldesigner/designericons.json @@ -32,6 +32,9 @@ "ArrangeIcon": { "iconName": "arrange_small" }, + "BackspaceIcon": { + "iconName": "backspace_small" + }, "CameraIcon": { "iconName": "camera_small" }, diff --git a/src/plugins/qmldesigner/components/componentcore/designericons.h b/src/plugins/qmldesigner/components/componentcore/designericons.h index f460707b915..749e4e1cce4 100644 --- a/src/plugins/qmldesigner/components/componentcore/designericons.h +++ b/src/plugins/qmldesigner/components/componentcore/designericons.h @@ -53,6 +53,7 @@ public: AnchorsIcon, AnnotationIcon, ArrangeIcon, + BackspaceIcon, CameraIcon, CameraOrthographicIcon, CameraPerspectiveIcon, diff --git a/src/plugins/qmldesigner/components/componentcore/qmleditormenu.cpp b/src/plugins/qmldesigner/components/componentcore/qmleditormenu.cpp index 173838f170d..e59fca7353e 100644 --- a/src/plugins/qmldesigner/components/componentcore/qmleditormenu.cpp +++ b/src/plugins/qmldesigner/components/componentcore/qmleditormenu.cpp @@ -149,4 +149,6 @@ QmlEditorStyleObject::QmlEditorStyleObject() QmlEditorMenuPrivate::cascadeRight = DesignerIcons::rotateIcon(downIcon, -90); QmlEditorMenuPrivate::tick = DesignerActionManager::instance() .contextIcon(DesignerIcons::SimpleCheckIcon); + QmlEditorMenuPrivate::backspaceIcon = DesignerActionManager::instance() + .contextIcon(DesignerIcons::BackspaceIcon); } From ae8ec8f08e421797855dde0bd9789af5a9ab71fd Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Wed, 5 Apr 2023 12:39:30 +0200 Subject: [PATCH 030/192] QmlDesigner: Update MCU template to Qt 6 imports Change-Id: Icd43a5b60b044516da396797363bc7828c1fd7c6 Reviewed-by: Reviewed-by: Aleksei German --- .../qmldesigner/studio_templates/projects/app_mcu.qmlproject | 4 ++++ .../projects/application-mcu/Constants.qml.tpl | 2 +- .../projects/application-mcu/Screen01.ui.qml.tpl | 2 +- .../studio_templates/projects/application-mcu/main.qml.tpl | 2 +- 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/app_mcu.qmlproject b/share/qtcreator/qmldesigner/studio_templates/projects/app_mcu.qmlproject index bbf848151fe..55204175faa 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/app_mcu.qmlproject +++ b/share/qtcreator/qmldesigner/studio_templates/projects/app_mcu.qmlproject @@ -52,6 +52,10 @@ Project { } qtForMCUs: true + qt6Project: true + + qdsVersion: "4.1" + quickVersion: "6.5" /* List of plugin directories passed to QML runtime */ importPaths: [ "imports" ] diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/application-mcu/Constants.qml.tpl b/share/qtcreator/qmldesigner/studio_templates/projects/application-mcu/Constants.qml.tpl index 524cbd0905c..ad50e39e9ac 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/application-mcu/Constants.qml.tpl +++ b/share/qtcreator/qmldesigner/studio_templates/projects/application-mcu/Constants.qml.tpl @@ -1,5 +1,5 @@ pragma Singleton -import QtQuick 2.15 +import QtQuick 6.5 QtObject { readonly property int width: %{ScreenWidth} diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/application-mcu/Screen01.ui.qml.tpl b/share/qtcreator/qmldesigner/studio_templates/projects/application-mcu/Screen01.ui.qml.tpl index 600031122e8..4a70ce692b6 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/application-mcu/Screen01.ui.qml.tpl +++ b/share/qtcreator/qmldesigner/studio_templates/projects/application-mcu/Screen01.ui.qml.tpl @@ -5,7 +5,7 @@ this file manually, you might introduce QML code that is not supported by Qt Des Check out https://doc.qt.io/qtcreator/creator-quick-ui-forms.html for details on .ui.qml files. */ -import QtQuick 2.15 +import QtQuick 6.5 import Constants 1.0 Rectangle { diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/application-mcu/main.qml.tpl b/share/qtcreator/qmldesigner/studio_templates/projects/application-mcu/main.qml.tpl index 4ea0b24a38c..e25113c67f9 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/application-mcu/main.qml.tpl +++ b/share/qtcreator/qmldesigner/studio_templates/projects/application-mcu/main.qml.tpl @@ -1,4 +1,4 @@ -import QtQuick 2.15 +import QtQuick 6.5 import Constants 1.0 Item { From 55deedb1a725bf3cb590e402fefc5c25bde490c9 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Tue, 4 Apr 2023 16:55:25 +0200 Subject: [PATCH 031/192] QmlDesigner: Support Qt 6.5 in wizards Task-number: QDS-9543 Change-Id: I6999843cac01e7402bf30e5b5d9476a03330b796 Reviewed-by: Samuel Ghinet Reviewed-by: Thomas Hartmann --- .../imports/NewProjectDialog/Details.qml | 1 + .../projects/application-3d/wizard.json | 10 +++++++++- .../studio_templates/projects/application/wizard.json | 9 ++++++++- .../projects/desktop-launcher/wizard.json | 9 ++++++++- .../projects/mobile-scroll/wizard.json | 9 ++++++++- .../studio_templates/projects/mobile-stack/wizard.json | 9 ++++++++- .../studio_templates/projects/mobile-swipe/wizard.json | 9 ++++++++- 7 files changed, 50 insertions(+), 6 deletions(-) diff --git a/share/qtcreator/qmldesigner/newprojectdialog/imports/NewProjectDialog/Details.qml b/share/qtcreator/qmldesigner/newprojectdialog/imports/NewProjectDialog/Details.qml index 9b49a036fe0..cdc6f51900b 100644 --- a/share/qtcreator/qmldesigner/newprojectdialog/imports/NewProjectDialog/Details.qml +++ b/share/qtcreator/qmldesigner/newprojectdialog/imports/NewProjectDialog/Details.qml @@ -402,6 +402,7 @@ Item { ListElement { name: "Qt 6.2" } ListElement { name: "Qt 6.3" } ListElement { name: "Qt 6.4" } + ListElement { name: "Qt 6.5" } } onActivated: (index) => { 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 84690258ea3..55bdc8a940a 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/application-3d/wizard.json +++ b/share/qtcreator/qmldesigner/studio_templates/projects/application-3d/wizard.json @@ -238,7 +238,7 @@ "type": "ComboBox", "data": { - "index": 2, + "index": 4, "items": [ { @@ -272,6 +272,14 @@ 'TargetQuickVersion': '6.4', 'TargetQuick3DVersion': '6.4' })" + }, + { + "trKey": "Qt 6.5", + "value": + "({ + 'TargetQuickVersion': '6.5', + 'TargetQuick3DVersion': '6.5' + })" } ] } diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/application/wizard.json b/share/qtcreator/qmldesigner/studio_templates/projects/application/wizard.json index 85f248f957b..82ebe92ff09 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/application/wizard.json +++ b/share/qtcreator/qmldesigner/studio_templates/projects/application/wizard.json @@ -238,7 +238,7 @@ "type": "ComboBox", "data": { - "index": 1, + "index": 4, "items": [ { @@ -268,6 +268,13 @@ "({ 'TargetQuickVersion': '6.4' })" + }, + { + "trKey": "Qt 6.5", + "value": + "({ + 'TargetQuickVersion': '6.5' + })" } ] } 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 92cbd476cdf..d29b1eec730 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/desktop-launcher/wizard.json +++ b/share/qtcreator/qmldesigner/studio_templates/projects/desktop-launcher/wizard.json @@ -236,7 +236,7 @@ "type": "ComboBox", "data": { - "index": 1, + "index": 4, "items": [ { @@ -266,6 +266,13 @@ "({ 'TargetQuickVersion': '6.4' })" + }, + { + "trKey": "Qt 6.5", + "value": + "({ + 'TargetQuickVersion': '6.5' + })" } ] } 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 9b3fe1be77a..ac83550c398 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/mobile-scroll/wizard.json +++ b/share/qtcreator/qmldesigner/studio_templates/projects/mobile-scroll/wizard.json @@ -195,7 +195,7 @@ "type": "ComboBox", "data": { - "index": 1, + "index": 4, "items": [ { @@ -225,6 +225,13 @@ "({ 'TargetQuickVersion': '6.4' })" + }, + { + "trKey": "Qt 6.5", + "value": + "({ + 'TargetQuickVersion': '6.5' + })" } ] } 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 a10fe57da6c..23cc6163c18 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/mobile-stack/wizard.json +++ b/share/qtcreator/qmldesigner/studio_templates/projects/mobile-stack/wizard.json @@ -193,7 +193,7 @@ "type": "ComboBox", "data": { - "index": 1, + "index": 4, "items": [ { @@ -223,6 +223,13 @@ "({ 'TargetQuickVersion': '6.4' })" + }, + { + "trKey": "Qt 6.5", + "value": + "({ + 'TargetQuickVersion': '6.5' + })" } ] } 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 77c31a7d3bf..222b09a1ba0 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/mobile-swipe/wizard.json +++ b/share/qtcreator/qmldesigner/studio_templates/projects/mobile-swipe/wizard.json @@ -193,7 +193,7 @@ "type": "ComboBox", "data": { - "index": 1, + "index": 4, "items": [ { @@ -223,6 +223,13 @@ "({ 'TargetQuickVersion': '6.4' })" + }, + { + "trKey": "Qt 6.5", + "value": + "({ + 'TargetQuickVersion': '6.5' + })" } ] } From 2175b976fbc5c76213dedef4a6e79f5cb64a55b8 Mon Sep 17 00:00:00 2001 From: Samuel Ghinet Date: Wed, 5 Apr 2023 20:04:55 +0300 Subject: [PATCH 032/192] QmlDesigner: Fix material no longer downloadable if once canceled Task-number: QDS-9618 Change-Id: Ic3bb6594611dc51e34e0a6e4456749539b1cf211 Reviewed-by: Mahmoud Badri --- src/plugins/qmldesigner/utils/multifiledownloader.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/plugins/qmldesigner/utils/multifiledownloader.cpp b/src/plugins/qmldesigner/utils/multifiledownloader.cpp index 05c527c8851..de3e8c52516 100644 --- a/src/plugins/qmldesigner/utils/multifiledownloader.cpp +++ b/src/plugins/qmldesigner/utils/multifiledownloader.cpp @@ -46,6 +46,7 @@ void MultiFileDownloader::setDownloader(FileDownloader *downloader) void MultiFileDownloader::start() { + m_canceled = false; emit downloadStarting(); } From e82898a184944e697313d1bd7c58750c2021d3cf Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Thu, 16 Mar 2023 14:31:41 +0200 Subject: [PATCH 033/192] QmlDesigner: Implement basic lights baking support It is expected that user manually specifies all necessary light baking related properties, including exposing them from subcomponents if needed. qlmdenoiser that is used to denoise generated light maps is a third party application. It can be found here: https://git.qt.io/laagocs/qlmdenoiser Fixes: QDS-9403 Change-Id: Ida6fc142440b9ffa8cc97d578f85d8b76cb4b43f Reviewed-by: Qt CI Bot Reviewed-by: Thomas Hartmann Reviewed-by: Mahmoud Badri --- .../edit3dQmlSource/BakeLightsDialog.qml | 90 +++++++ .../commands/puppettocreatorcommand.h | 6 +- .../interfaces/nodeinstanceglobal.h | 3 +- src/plugins/qmldesigner/CMakeLists.txt | 2 + .../components/edit3d/bakelights.cpp | 250 ++++++++++++++++++ .../components/edit3d/bakelights.h | 54 ++++ .../edit3d/bakelightsconnectionmanager.cpp | 48 ++++ .../edit3d/bakelightsconnectionmanager.h | 28 ++ .../components/edit3d/edit3dactions.cpp | 40 ++- .../components/edit3d/edit3dactions.h | 15 ++ .../components/edit3d/edit3dview.cpp | 53 +++- .../components/edit3d/edit3dview.h | 8 + .../components/edit3d/edit3dwidget.cpp | 8 + .../components/edit3d/edit3dwidget.h | 1 + .../qmldesigner/designercore/include/model.h | 1 + .../designercore/include/nodeinstanceview.h | 1 + .../instances/nodeinstanceview.cpp | 5 + .../qmldesigner/designercore/model/model.cpp | 10 + .../qmldesigner/qmldesignerconstants.h | 2 +- src/plugins/qmldesigner/settingspage.cpp | 2 +- src/tools/qml2puppet/CMakeLists.txt | 1 + .../qml2puppet/instances/instances.pri | 2 + .../nodeinstanceserverdispatcher.cpp | 3 + .../qt5bakelightsnodeinstanceserver.cpp | 233 ++++++++++++++++ .../qt5bakelightsnodeinstanceserver.h | 57 ++++ .../instances/qt5nodeinstanceclientproxy.cpp | 4 + .../qml2puppet/instances/servernodeinstance.h | 1 + 27 files changed, 916 insertions(+), 12 deletions(-) create mode 100644 share/qtcreator/qmldesigner/edit3dQmlSource/BakeLightsDialog.qml create mode 100644 src/plugins/qmldesigner/components/edit3d/bakelights.cpp create mode 100644 src/plugins/qmldesigner/components/edit3d/bakelights.h create mode 100644 src/plugins/qmldesigner/components/edit3d/bakelightsconnectionmanager.cpp create mode 100644 src/plugins/qmldesigner/components/edit3d/bakelightsconnectionmanager.h create mode 100644 src/tools/qml2puppet/qml2puppet/instances/qt5bakelightsnodeinstanceserver.cpp create mode 100644 src/tools/qml2puppet/qml2puppet/instances/qt5bakelightsnodeinstanceserver.h diff --git a/share/qtcreator/qmldesigner/edit3dQmlSource/BakeLightsDialog.qml b/share/qtcreator/qmldesigner/edit3dQmlSource/BakeLightsDialog.qml new file mode 100644 index 00000000000..9632c0b4a11 --- /dev/null +++ b/share/qtcreator/qmldesigner/edit3dQmlSource/BakeLightsDialog.qml @@ -0,0 +1,90 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0 WITH Qt-GPL-exception-1.0 + +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import QtQuickDesignerTheme +import HelperWidgets +import StudioControls as StudioControls +import StudioTheme as StudioTheme + +Rectangle { + id: root + + color: StudioTheme.Values.themePanelBackground + + Column { + id: col + padding: 5 + leftPadding: 10 + spacing: 5 + + Text { + id: title + text: qsTr("Baking lights for 3D view: %1").arg(sceneId) + font.bold: true + font.pixelSize: StudioTheme.Values.myFontSize + color: StudioTheme.Values.themeTextColor + } + + Rectangle { + width: root.width - 16 + height: root.height - title.height - button.height - 20 + + color: StudioTheme.Values.themePanelBackground + border.color: StudioTheme.Values.themeControlOutline + border.width: StudioTheme.Values.border + + ScrollView { + id: scrollView + + anchors.fill: parent + anchors.margins: 4 + + clip: true + + Behavior on contentY { + PropertyAnimation { + easing.type: Easing.InOutQuad + } + } + + Text { + id: progressText + width: scrollView.width + font.pixelSize: StudioTheme.Values.myFontSize + color: StudioTheme.Values.themeTextColor + } + + function ensureVisible() + { + let newPos = scrollView.contentHeight - scrollView.height + scrollView.contentY = newPos < 0 ? 0 : newPos + } + } + + } + + Connections { + target: rootView + function onProgress(msg) { + progressText.text += progressText.text === "" ? msg : "\n" + msg + scrollView.ensureVisible() + } + + function onFinished() { + button.text = qsTr("Close") + } + } + + Button { + id: button + text: qsTr("Cancel") + anchors.right: parent.right + anchors.margins: StudioTheme.Values.dialogButtonPadding + + onClicked: rootView.cancel() + } + } +} diff --git a/src/libs/qmlpuppetcommunication/commands/puppettocreatorcommand.h b/src/libs/qmlpuppetcommunication/commands/puppettocreatorcommand.h index 5c8cd3af063..30b0cce9981 100644 --- a/src/libs/qmlpuppetcommunication/commands/puppettocreatorcommand.h +++ b/src/libs/qmlpuppetcommunication/commands/puppettocreatorcommand.h @@ -19,7 +19,11 @@ public: RenderModelNodePreviewImage, Import3DSupport, NodeAtPos, - None }; + BakeLightsProgress, + BakeLightsFinished, + BakeLightsAborted, + None + }; PuppetToCreatorCommand(Type type, const QVariant &data); PuppetToCreatorCommand() = default; diff --git a/src/libs/qmlpuppetcommunication/interfaces/nodeinstanceglobal.h b/src/libs/qmlpuppetcommunication/interfaces/nodeinstanceglobal.h index 57abf115e17..67f0fa4c5c2 100644 --- a/src/libs/qmlpuppetcommunication/interfaces/nodeinstanceglobal.h +++ b/src/libs/qmlpuppetcommunication/interfaces/nodeinstanceglobal.h @@ -48,7 +48,8 @@ enum class View3DActionType { SelectGridColor, ResetBackgroundColor, SyncBackgroundColor, - GetNodeAtPos + GetNodeAtPos, + SetBakeLightsView3D }; constexpr bool isNanotraceEnabled() diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index 00714ca6cc6..9280965ce0d 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -653,6 +653,8 @@ extend_qtc_plugin(QmlDesigner edit3dactions.cpp edit3dactions.h edit3dvisibilitytogglesmenu.cpp edit3dvisibilitytogglesmenu.h backgroundcolorselection.cpp backgroundcolorselection.h + bakelights.cpp bakelights.h + bakelightsconnectionmanager.cpp bakelightsconnectionmanager.h edit3d.qrc ) diff --git a/src/plugins/qmldesigner/components/edit3d/bakelights.cpp b/src/plugins/qmldesigner/components/edit3d/bakelights.cpp new file mode 100644 index 00000000000..c44b49bb5c4 --- /dev/null +++ b/src/plugins/qmldesigner/components/edit3d/bakelights.cpp @@ -0,0 +1,250 @@ +// Copyright (C) 2023 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 "bakelights.h" + +#include "abstractview.h" +#include "bakelightsconnectionmanager.h" +#include "documentmanager.h" +#include "modelnode.h" +#include "nodeabstractproperty.h" +#include "nodeinstanceview.h" +#include "nodemetainfo.h" +#include "plaintexteditmodifier.h" +#include "rewriterview.h" +#include "variantproperty.h" + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace QmlDesigner { + +static QString propertyEditorResourcesPath() +{ +#ifdef SHARE_QML_PATH + if (Utils::qtcEnvironmentVariableIsSet("LOAD_QML_FROM_SOURCE")) + return QLatin1String(SHARE_QML_PATH) + "/propertyEditorQmlSources"; +#endif + return Core::ICore::resourcePath("qmldesigner/propertyEditorQmlSources").toString(); +} + +static QString qmlSourcesPath() +{ +#ifdef SHARE_QML_PATH + if (Utils::qtcEnvironmentVariableIsSet("LOAD_QML_FROM_SOURCE")) + return QLatin1String(SHARE_QML_PATH) + "/edit3dQmlSource"; +#endif + return Core::ICore::resourcePath("qmldesigner/edit3dQmlSource").toString(); +} + +BakeLights::BakeLights(AbstractView *view) + : QObject(view) + , m_view(view) +{ + m_view3dId = resolveView3dId(view); + + if (m_view3dId.isEmpty()) { + // It should never get here, baking controls should be disabled in this case + qWarning() << __FUNCTION__ << "Active scene is not View3D"; + deleteLater(); + return; + } + + // Create folders for lightmaps if they do not exist + PropertyName loadPrefixPropName{"loadPrefix"}; + const QList bakedLightmapNodes = m_view->allModelNodesOfType( + m_view->model()->qtQuick3DBakedLightmapMetaInfo()); + Utils::FilePath currentPath = DocumentManager::currentFilePath().absolutePath(); + QSet pathSet; + for (const ModelNode &node : bakedLightmapNodes) { + if (node.hasVariantProperty(loadPrefixPropName)) { + QString prefix = node.variantProperty(loadPrefixPropName).value().toString(); + Utils::FilePath fp = Utils::FilePath::fromString(prefix); + if (fp.isRelativePath()) { + fp = currentPath.pathAppended(prefix); + if (!fp.exists()) + pathSet.insert(fp); + } + } + } + for (const Utils::FilePath &fp : std::as_const(pathSet)) + fp.createDir(); + + // Show non-modal progress dialog with cancel button + QString path = qmlSourcesPath() + "/BakeLightsDialog.qml"; + + m_dialog = new QQuickView; + m_dialog->setTitle(tr("Bake Lights")); + m_dialog->setResizeMode(QQuickView::SizeRootObjectToView); + m_dialog->setMinimumSize({150, 100}); + m_dialog->setWidth(800); + m_dialog->setHeight(400); + m_dialog->setFlags(Qt::Dialog); + m_dialog->setModality(Qt::NonModal); + m_dialog->engine()->addImportPath(propertyEditorResourcesPath() + "/imports"); + + m_dialog->rootContext()->setContextProperties({ + {"rootView", QVariant::fromValue(this)}, + {"sceneId", QVariant::fromValue(m_view3dId)} + }); + m_dialog->setSource(QUrl::fromLocalFile(path)); + m_dialog->installEventFilter(this); + m_dialog->show(); + + QTimer::singleShot(0, this, &BakeLights::bakeLights); +} + +BakeLights::~BakeLights() +{ + if (m_connectionManager) { + m_connectionManager->setProgressCallback({}); + m_connectionManager->setFinishedCallback({}); + m_connectionManager->setCrashCallback({}); + } + + if (m_model) { + m_model->setNodeInstanceView({}); + m_model->setRewriterView({}); + m_model.reset(); + } + + delete m_dialog; + delete m_rewriterView; + delete m_nodeInstanceView; + delete m_connectionManager; +} + +QString BakeLights::resolveView3dId(AbstractView *view) +{ + if (!view || !view->model()) + return {}; + + QString view3dId; + ModelNode activeView3D; + ModelNode activeScene = view->active3DSceneNode(); + + if (activeScene.isValid()) { + if (activeScene.metaInfo().isQtQuick3DView3D()) { + activeView3D = activeScene; + } else { + ModelNode sceneParent = activeScene.parentProperty().parentModelNode(); + if (sceneParent.metaInfo().isQtQuick3DView3D()) + activeView3D = sceneParent; + } + view3dId = activeView3D.id(); + } + + return view3dId; +} + +void BakeLights::raiseDialog() +{ + if (m_dialog) + m_dialog->raise(); +} + +void BakeLights::bakeLights() +{ + if (!m_view || !m_view->model()) + return; + + // Start baking process + m_connectionManager = new BakeLightsConnectionManager; + m_rewriterView = new RewriterView{m_view->externalDependencies(), RewriterView::Amend}; + m_nodeInstanceView = new NodeInstanceView{*m_connectionManager, m_view->externalDependencies()}; + + m_model = QmlDesigner::Model::create("QtQuick/Item", 2, 1); + m_model->setFileUrl(m_view->model()->fileUrl()); + + // Take the current unsaved state of the main model and apply it to our copy + auto textDocument = std::make_unique( + m_view->model()->rewriterView()->textModifier()->textDocument()->toRawText()); + + auto modifier = std::make_unique(textDocument.get(), + QTextCursor{textDocument.get()}); + + m_rewriterView->setTextModifier(modifier.get()); + m_model->setRewriterView(m_rewriterView); + + auto rootModelNodeMetaInfo = m_rewriterView->rootModelNode().metaInfo(); + bool is3DRoot = m_rewriterView->errors().isEmpty() + && (rootModelNodeMetaInfo.isQtQuick3DNode() + || rootModelNodeMetaInfo.isQtQuick3DMaterial()); + + if (!m_rewriterView->errors().isEmpty() + || (!m_rewriterView->rootModelNode().metaInfo().isGraphicalItem() && !is3DRoot)) { + emit progress(tr("Invalid root node, baking aborted.")); + emit finished(); + m_dialog->raise(); + return; + } + + m_nodeInstanceView->setTarget(m_view->nodeInstanceView()->target()); + + auto progressCallback = [this](const QString &msg) { + emit progress(msg); + }; + + auto finishedCallback = [this](const QString &msg) { + m_dialog->raise(); + emit progress(msg); + emit finished(); + + // Puppet reset is needed to update baking results to current views + m_view->resetPuppet(); + }; + + auto crashCallback = [this]() { + m_dialog->raise(); + emit progress(tr("Baking process crashed, baking aborted.")); + emit finished(); + }; + + m_connectionManager->setProgressCallback(std::move(progressCallback)); + m_connectionManager->setFinishedCallback(std::move(finishedCallback)); + m_connectionManager->setCrashCallback(std::move(crashCallback)); + + m_model->setNodeInstanceView(m_nodeInstanceView); + + // InternalIds are not guaranteed to match between normal model and our copy of it, so + // we identify the View3D by its qml id. + m_nodeInstanceView->view3DAction(View3DActionType::SetBakeLightsView3D, m_view3dId); +} + +void BakeLights::cancel() +{ + if (!m_dialog.isNull() && m_dialog->isVisible()) + m_dialog->close(); + + deleteLater(); +} + +bool BakeLights::eventFilter(QObject *obj, QEvent *event) +{ + if (obj == m_dialog) { + if (event->type() == QEvent::KeyPress) { + QKeyEvent *keyEvent = static_cast(event); + if (keyEvent->key() == Qt::Key_Escape) + cancel(); + } else if (event->type() == QEvent::Close) { + cancel(); + } + } + + return QObject::eventFilter(obj, event); +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/edit3d/bakelights.h b/src/plugins/qmldesigner/components/edit3d/bakelights.h new file mode 100644 index 00000000000..d63ca0b3b82 --- /dev/null +++ b/src/plugins/qmldesigner/components/edit3d/bakelights.h @@ -0,0 +1,54 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0 +#pragma once + +#include "qmldesignercorelib_global.h" + +#include +#include + +QT_BEGIN_NAMESPACE +class QQuickView; +QT_END_NAMESPACE + +namespace QmlDesigner { + +class AbstractView; +class BakeLightsConnectionManager; +class NodeInstanceView; +class RewriterView; + +class BakeLights : public QObject +{ + Q_OBJECT + +public: + BakeLights(AbstractView *view); + ~BakeLights(); + + Q_INVOKABLE void cancel(); + + void raiseDialog(); + + static QString resolveView3dId(AbstractView *view); + +signals: + void finished(); + void progress(const QString &msg); + +protected: + bool eventFilter(QObject *obj, QEvent *event) override; + +private: + void bakeLights(); + + QPointer m_dialog; + QPointer m_connectionManager; + QPointer m_nodeInstanceView; + QPointer m_rewriterView; + QPointer m_view; + ModelPointer m_model; + QString m_view3dId; +}; + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/edit3d/bakelightsconnectionmanager.cpp b/src/plugins/qmldesigner/components/edit3d/bakelightsconnectionmanager.cpp new file mode 100644 index 00000000000..201ca1d2e9b --- /dev/null +++ b/src/plugins/qmldesigner/components/edit3d/bakelightsconnectionmanager.cpp @@ -0,0 +1,48 @@ +// Copyright (C) 2023 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 "bakelightsconnectionmanager.h" + +#include + +namespace QmlDesigner { + +BakeLightsConnectionManager::BakeLightsConnectionManager() +{ + connections().emplace_back("Bake lights", "bakelightsmode"); +} + +void BakeLightsConnectionManager::setProgressCallback(Callback callback) +{ + m_progressCallback = std::move(callback); +} + +void BakeLightsConnectionManager::setFinishedCallback(Callback callback) +{ + m_finishedCallback = std::move(callback); +} + +void BakeLightsConnectionManager::dispatchCommand(const QVariant &command, + ConnectionManagerInterface::Connection &) +{ + static const int commandType = QMetaType::type("PuppetToCreatorCommand"); + + if (command.userType() == commandType) { + auto cmd = command.value(); + switch (cmd.type()) { + case PuppetToCreatorCommand::BakeLightsProgress: + m_progressCallback(cmd.data().toString()); + break; + case PuppetToCreatorCommand::BakeLightsAborted: + m_finishedCallback(tr("Baking aborted!")); + break; + case PuppetToCreatorCommand::BakeLightsFinished: + m_finishedCallback(tr("Baking finished!")); + break; + default: + break; + } + } +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/edit3d/bakelightsconnectionmanager.h b/src/plugins/qmldesigner/components/edit3d/bakelightsconnectionmanager.h new file mode 100644 index 00000000000..2af5e56c114 --- /dev/null +++ b/src/plugins/qmldesigner/components/edit3d/bakelightsconnectionmanager.h @@ -0,0 +1,28 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0 + +#pragma once + +#include "connectionmanager.h" + +namespace QmlDesigner { + +class BakeLightsConnectionManager : public ConnectionManager +{ +public: + using Callback = std::function; + + BakeLightsConnectionManager(); + + void setProgressCallback(Callback callback); + void setFinishedCallback(Callback callback); + +protected: + void dispatchCommand(const QVariant &command, Connection &connection) override; + +private: + Callback m_progressCallback; + Callback m_finishedCallback; +}; + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dactions.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dactions.cpp index 61731c578f8..12d2dd05528 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dactions.cpp +++ b/src/plugins/qmldesigner/components/edit3d/edit3dactions.cpp @@ -2,18 +2,15 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "edit3dactions.h" -#include "edit3dview.h" -#include -#include -#include -#include +#include "bakelights.h" +#include "edit3dview.h" +#include "nodemetainfo.h" +#include "qmldesignerconstants.h" #include "seekerslider.h" #include -#include - namespace QmlDesigner { Edit3DActionTemplate::Edit3DActionTemplate(const QString &description, @@ -147,4 +144,33 @@ bool Edit3DParticleSeekerAction::isEnabled(const SelectionContext &) const return m_seeker->isEnabled(); } +Edit3DBakeLightsAction::Edit3DBakeLightsAction(const QIcon &icon, + Edit3DView *view, + SelectionContextOperation selectionAction) + : Edit3DAction(QmlDesigner::Constants::EDIT3D_BAKE_LIGHTS, + View3DActionType::Empty, + QCoreApplication::translate("BakeLights", "Bake Lights"), + QKeySequence(), + false, + false, + icon, + view, + selectionAction, + QCoreApplication::translate("BakeLights", "Bake lights for the current 3D scene.")) + , m_view(view) +{ + +} + +bool Edit3DBakeLightsAction::isVisible(const SelectionContext &) const +{ + return m_view->isBakingLightsSupported(); +} + +bool Edit3DBakeLightsAction::isEnabled(const SelectionContext &) const +{ + return m_view->isBakingLightsSupported() + && !BakeLights::resolveView3dId(m_view).isEmpty(); +} + } diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dactions.h b/src/plugins/qmldesigner/components/edit3d/edit3dactions.h index bad427a25be..4e8be202b9f 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dactions.h +++ b/src/plugins/qmldesigner/components/edit3d/edit3dactions.h @@ -122,4 +122,19 @@ private: SeekerSliderAction *m_seeker = nullptr; }; +class Edit3DBakeLightsAction : public Edit3DAction +{ +public: + Edit3DBakeLightsAction(const QIcon &icon, + Edit3DView *view, + SelectionContextOperation selectionAction); + +protected: + bool isVisible(const SelectionContext &) const override; + bool isEnabled(const SelectionContext &) const override; + +private: + Edit3DView *m_view = nullptr; +}; + } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp index d92e76362b9..d346e2bf164 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp +++ b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp @@ -4,6 +4,7 @@ #include "edit3dview.h" #include "backgroundcolorselection.h" +#include "bakelights.h" #include "designeractionmanager.h" #include "designericons.h" #include "designersettings.h" @@ -20,11 +21,16 @@ #include "qmldesignerplugin.h" #include "qmlvisualnode.h" #include "seekerslider.h" +#include "theme.h" #include #include -#include +#include +#include + +#include + #include #include #include @@ -204,6 +210,12 @@ void Edit3DView::updateActiveScene3D(const QVariantMap &sceneState) m_particlesPlayAction->action()->setChecked(sceneState[particlesPlayKey].toBool()); else m_particlesPlayAction->action()->setChecked(true); + + // Selection context change updates visible and enabled states + SelectionContext selectionContext(this); + selectionContext.setUpdateMode(SelectionContext::UpdateMode::Fast); + if (m_bakeLightsAction) + m_bakeLightsAction->currentContextChanged(selectionContext); } void Edit3DView::modelAttached(Model *model) @@ -219,6 +231,13 @@ void Edit3DView::modelAttached(Model *model) edit3DWidget()->canvas()->busyIndicator()->show(); + m_isBakingLightsSupported = false; + ProjectExplorer::Target *target = QmlDesignerPlugin::instance()->currentDesignDocument()->currentTarget(); + if (target && target->kit()) { + if (QtSupport::QtVersion *qtVer = QtSupport::QtKitAspect::qtVersion(target->kit())) + m_isBakingLightsSupported = qtVer->qtVersion() >= QVersionNumber(6, 5, 0); + } + connect(model->metaInfo().itemLibraryInfo(), &ItemLibraryInfo::entriesChanged, this, &Edit3DView::onEntriesChanged, Qt::UniqueConnection); } @@ -279,6 +298,11 @@ void Edit3DView::handleEntriesChanged() void Edit3DView::modelAboutToBeDetached(Model *model) { + m_isBakingLightsSupported = false; + + if (m_bakeLights) + m_bakeLights->cancel(); + // Hide the canvas when model is detached (i.e. changing documents) if (edit3DWidget() && edit3DWidget()->canvas()) { m_canvasCache.insert(model, edit3DWidget()->canvas()->renderImage()); @@ -702,6 +726,17 @@ void Edit3DView::createEdit3DActions() m_seekerAction->action()->setEnabled(!m_particlesPlayAction->action()->isChecked()); }; + SelectionContextOperation bakeLightsTrigger = [this](const SelectionContext &) { + if (!m_isBakingLightsSupported) + return; + + // BakeLights cleans itself up when its dialog is closed + if (!m_bakeLights) + m_bakeLights = new BakeLights(this); + else + m_bakeLights->raiseDialog(); + }; + m_particleViewModeAction = new Edit3DAction( QmlDesigner::Constants::EDIT3D_PARTICLE_MODE, View3DActionType::Edit3DParticleModeToggle, @@ -804,6 +839,11 @@ void Edit3DView::createEdit3DActions() m_seekerAction = createSeekerSliderAction(); + m_bakeLightsAction = new Edit3DBakeLightsAction( + toolbarIcon(Theme::editLightOn_medium), //: TODO placeholder icon + this, + bakeLightsTrigger); + m_leftActions << m_selectionModeAction; m_leftActions << nullptr; // Null indicates separator m_leftActions << nullptr; // Second null after separator indicates an exclusive group @@ -830,6 +870,7 @@ void Edit3DView::createEdit3DActions() m_rightActions << nullptr; m_rightActions << m_seekerAction; m_rightActions << nullptr; + m_rightActions << m_bakeLightsAction; m_rightActions << m_resetAction; m_visibilityToggleActions << m_showGridAction; @@ -870,6 +911,11 @@ Edit3DAction *Edit3DView::edit3DAction(View3DActionType type) const return m_edit3DActions.value(type, nullptr).data(); } +Edit3DBakeLightsAction *Edit3DView::bakeLightsAction() const +{ + return m_bakeLightsAction; +} + void Edit3DView::addQuick3DImport() { DesignDocument *document = QmlDesignerPlugin::instance()->currentDesignDocument(); @@ -939,4 +985,9 @@ void Edit3DView::dropAsset(const QString &file, const QPointF &pos) emitView3DAction(View3DActionType::GetNodeAtPos, pos); } +bool Edit3DView::isBakingLightsSupported() const +{ + return m_isBakingLightsSupported; +} + } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dview.h b/src/plugins/qmldesigner/components/edit3d/edit3dview.h index 2b746cf1af7..109555f586b 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dview.h +++ b/src/plugins/qmldesigner/components/edit3d/edit3dview.h @@ -22,8 +22,10 @@ QT_END_NAMESPACE namespace QmlDesigner { +class BakeLights; class Edit3DWidget; class Edit3DAction; +class Edit3DBakeLightsAction; class Edit3DCameraAction; class QMLDESIGNERCOMPONENTS_EXPORT Edit3DView : public AbstractView @@ -57,6 +59,7 @@ public: QVector visibilityToggleActions() const; QVector backgroundColorActions() const; Edit3DAction *edit3DAction(View3DActionType type) const; + Edit3DBakeLightsAction *bakeLightsAction() const; void addQuick3DImport(); void startContextMenu(const QPoint &pos); @@ -66,6 +69,8 @@ public: void dropComponent(const ItemLibraryEntry &entry, const QPointF &pos); void dropAsset(const QString &file, const QPointF &pos); + bool isBakingLightsSupported() const; + private slots: void onEntriesChanged(); @@ -122,6 +127,7 @@ private: Edit3DAction *m_visibilityTogglesAction = nullptr; Edit3DAction *m_backgrondColorMenuAction = nullptr; Edit3DAction *m_seekerAction = nullptr; + Edit3DBakeLightsAction *m_bakeLightsAction = nullptr; int particlemode; ModelCache m_canvasCache; ModelNode m_droppedModelNode; @@ -130,6 +136,8 @@ private: NodeAtPosReqType m_nodeAtPosReqType; QPoint m_contextMenuPos; QTimer m_compressionTimer; + QPointer m_bakeLights; + bool m_isBakingLightsSupported = false; friend class Edit3DAction; }; diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp index 408584e166b..a217e396d9c 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp +++ b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp @@ -261,6 +261,12 @@ void Edit3DWidget::createContextMenu() view()->emitView3DAction(View3DActionType::AlignViewToCamera, true); }); + m_bakeLightsAction = m_contextMenu->addAction( + contextIcon(DesignerIcons::LightIcon), // TODO: placeholder icon + tr("Bake Lights"), [&] { + view()->bakeLightsAction()->action()->trigger(); + }); + m_contextMenu->addSeparator(); m_selectParentAction = m_contextMenu->addAction( @@ -466,6 +472,8 @@ void Edit3DWidget::showContextMenu(const QPoint &pos, const ModelNode &modelNode m_alignViewAction->setEnabled(isCamera); m_selectParentAction->setEnabled(selectionExcludingRoot); m_toggleGroupAction->setEnabled(true); + m_bakeLightsAction->setVisible(view()->bakeLightsAction()->action()->isVisible()); + m_bakeLightsAction->setEnabled(view()->bakeLightsAction()->action()->isEnabled()); m_contextMenu->popup(mapToGlobal(pos)); } diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.h b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.h index 092abd313a5..eecd52345fa 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.h +++ b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.h @@ -75,6 +75,7 @@ private: QPointer m_visibilityTogglesMenu; QPointer m_backgroundColorMenu; QPointer m_contextMenu; + QPointer m_bakeLightsAction; QPointer m_editComponentAction; QPointer m_editMaterialAction; QPointer m_duplicateAction; diff --git a/src/plugins/qmldesigner/designercore/include/model.h b/src/plugins/qmldesigner/designercore/include/model.h index 9e6c7ca36ad..f3227361e68 100644 --- a/src/plugins/qmldesigner/designercore/include/model.h +++ b/src/plugins/qmldesigner/designercore/include/model.h @@ -87,6 +87,7 @@ public: NodeMetaInfo flowViewFlowTransitionMetaInfo() const; NodeMetaInfo flowViewFlowWildcardMetaInfo() const; NodeMetaInfo fontMetaInfo() const; + NodeMetaInfo qtQuick3DBakedLightmapMetaInfo() const; NodeMetaInfo qtQuick3DDefaultMaterialMetaInfo() const; NodeMetaInfo qtQuick3DMaterialMetaInfo() const; NodeMetaInfo qtQuick3DModelMetaInfo() const; diff --git a/src/plugins/qmldesigner/designercore/include/nodeinstanceview.h b/src/plugins/qmldesigner/designercore/include/nodeinstanceview.h index 467cef0ed3d..11017a27b1c 100644 --- a/src/plugins/qmldesigner/designercore/include/nodeinstanceview.h +++ b/src/plugins/qmldesigner/designercore/include/nodeinstanceview.h @@ -122,6 +122,7 @@ public: QImage statePreviewImage(const ModelNode &stateNode) const; void setTarget(ProjectExplorer::Target *newTarget); + ProjectExplorer::Target *target() const; void sendToken(const QString &token, int number, const QVector &nodeVector); diff --git a/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp b/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp index 7aaed70c9dd..ebe5acd9d28 100644 --- a/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp +++ b/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp @@ -1572,6 +1572,11 @@ void NodeInstanceView::setTarget(ProjectExplorer::Target *newTarget) } } +ProjectExplorer::Target *NodeInstanceView::target() const +{ + return m_currentTarget; +} + void NodeInstanceView::statePreviewImagesChanged(const StatePreviewImageChangedCommand &command) { if (!model()) diff --git a/src/plugins/qmldesigner/designercore/model/model.cpp b/src/plugins/qmldesigner/designercore/model/model.cpp index a234c11e1fe..13197666fc4 100644 --- a/src/plugins/qmldesigner/designercore/model/model.cpp +++ b/src/plugins/qmldesigner/designercore/model/model.cpp @@ -1939,6 +1939,16 @@ NodeMetaInfo Model::qtQuick3DTextureMetaInfo() const } } +NodeMetaInfo Model::qtQuick3DBakedLightmapMetaInfo() const +{ + if constexpr (useProjectStorage()) { + using namespace Storage::Info; + return createNodeMetaInfo(); + } else { + return metaInfo("QtQuick3D.BakedLightmap"); + } +} + NodeMetaInfo Model::qtQuick3DMaterialMetaInfo() const { if constexpr (useProjectStorage()) { diff --git a/src/plugins/qmldesigner/qmldesignerconstants.h b/src/plugins/qmldesigner/qmldesignerconstants.h index 743d70bcbc9..d38dad74c7e 100644 --- a/src/plugins/qmldesigner/qmldesignerconstants.h +++ b/src/plugins/qmldesigner/qmldesignerconstants.h @@ -62,7 +62,7 @@ const char EDIT3D_PARTICLES_SEEKER[] = "QmlDesigner.Editor3D.ParticlesSeeker" const char EDIT3D_PARTICLES_RESTART[] = "QmlDesigner.Editor3D.ParticlesRestart"; const char EDIT3D_VISIBILITY_TOGGLES[] = "QmlDesigner.Editor3D.VisibilityToggles"; const char EDIT3D_BACKGROUND_COLOR_ACTIONS[] = "QmlDesigner.Editor3D.BackgroundColorActions"; - +const char EDIT3D_BAKE_LIGHTS[] = "QmlDesigner.Editor3D.BakeLights"; const char QML_DESIGNER_SUBFOLDER[] = "/designer/"; const char COMPONENT_BUNDLES_FOLDER[] = "/ComponentBundles"; diff --git a/src/plugins/qmldesigner/settingspage.cpp b/src/plugins/qmldesigner/settingspage.cpp index 6acb2284217..52a58e01a5e 100644 --- a/src/plugins/qmldesigner/settingspage.cpp +++ b/src/plugins/qmldesigner/settingspage.cpp @@ -41,7 +41,7 @@ namespace Internal { static QStringList puppetModes() { - static QStringList puppetModeList{"", "all", "editormode", "rendermode", "previewmode"}; + static QStringList puppetModeList{"", "all", "editormode", "rendermode", "previewmode", "bakelightsmode"}; return puppetModeList; } diff --git a/src/tools/qml2puppet/CMakeLists.txt b/src/tools/qml2puppet/CMakeLists.txt index 62325819a58..1d8dc8691b4 100644 --- a/src/tools/qml2puppet/CMakeLists.txt +++ b/src/tools/qml2puppet/CMakeLists.txt @@ -175,6 +175,7 @@ extend_qtc_executable(qml2puppet qmlstatenodeinstance.cpp qmlstatenodeinstance.h qmltransitionnodeinstance.cpp qmltransitionnodeinstance.h qt3dpresentationnodeinstance.cpp qt3dpresentationnodeinstance.h + qt5bakelightsnodeinstanceserver.cpp qt5bakelightsnodeinstanceserver.h qt5informationnodeinstanceserver.cpp qt5informationnodeinstanceserver.h qt5nodeinstanceclientproxy.cpp qt5nodeinstanceclientproxy.h qt5nodeinstanceserver.cpp qt5nodeinstanceserver.h diff --git a/src/tools/qml2puppet/qml2puppet/instances/instances.pri b/src/tools/qml2puppet/qml2puppet/instances/instances.pri index 2d3b4ceedfe..505dd748e49 100644 --- a/src/tools/qml2puppet/qml2puppet/instances/instances.pri +++ b/src/tools/qml2puppet/qml2puppet/instances/instances.pri @@ -25,6 +25,7 @@ HEADERS += $$PWD/qt5nodeinstanceserver.h \ $$PWD/qt5captureimagenodeinstanceserver.h \ $$PWD/qt5capturepreviewnodeinstanceserver.h \ $$PWD/qt5testnodeinstanceserver.h \ + $$PWD/qt5bakelightsnodeinstanceserver.h \ $$PWD/qt5informationnodeinstanceserver.h \ $$PWD/qt5rendernodeinstanceserver.h \ $$PWD/qt5previewnodeinstanceserver.h \ @@ -60,6 +61,7 @@ SOURCES += $$PWD/qt5nodeinstanceserver.cpp \ $$PWD/qt5captureimagenodeinstanceserver.cpp \ $$PWD/qt5capturepreviewnodeinstanceserver.cpp \ $$PWD/qt5testnodeinstanceserver.cpp \ + $$PWD/qt5bakelightsnodeinstanceserver.cpp \ $$PWD/qt5informationnodeinstanceserver.cpp \ $$PWD/qt5rendernodeinstanceserver.cpp \ $$PWD/qt5previewnodeinstanceserver.cpp \ diff --git a/src/tools/qml2puppet/qml2puppet/instances/nodeinstanceserverdispatcher.cpp b/src/tools/qml2puppet/qml2puppet/instances/nodeinstanceserverdispatcher.cpp index 09910847a94..9815d21ac47 100644 --- a/src/tools/qml2puppet/qml2puppet/instances/nodeinstanceserverdispatcher.cpp +++ b/src/tools/qml2puppet/qml2puppet/instances/nodeinstanceserverdispatcher.cpp @@ -3,6 +3,7 @@ #include "nodeinstanceserverdispatcher.h" +#include "qt5bakelightsnodeinstanceserver.h" #include "qt5captureimagenodeinstanceserver.h" #include "qt5capturepreviewnodeinstanceserver.h" #include "qt5informationnodeinstanceserver.h" @@ -170,6 +171,8 @@ std::unique_ptr createNodeInstanceServer( return std::make_unique(nodeInstanceClient); else if (serverName == "previewmode") return std::make_unique(nodeInstanceClient); + else if (serverName == "bakelightsmode") + return std::make_unique(nodeInstanceClient); return {}; } diff --git a/src/tools/qml2puppet/qml2puppet/instances/qt5bakelightsnodeinstanceserver.cpp b/src/tools/qml2puppet/qml2puppet/instances/qt5bakelightsnodeinstanceserver.cpp new file mode 100644 index 00000000000..87b125480d8 --- /dev/null +++ b/src/tools/qml2puppet/qml2puppet/instances/qt5bakelightsnodeinstanceserver.cpp @@ -0,0 +1,233 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0 WITH Qt-GPL-exception-1.0 + +#include "qt5bakelightsnodeinstanceserver.h" + +#if BAKE_LIGHTS_SUPPORTED +#include "createscenecommand.h" +#include "view3dactioncommand.h" + +#include "nodeinstanceclientinterface.h" +#include "puppettocreatorcommand.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#endif + +namespace QmlDesigner { + +Qt5BakeLightsNodeInstanceServer::Qt5BakeLightsNodeInstanceServer(NodeInstanceClientInterface *nodeInstanceClient) : + Qt5NodeInstanceServer(nodeInstanceClient) +{ + setSlowRenderTimerInterval(100000000); + setRenderTimerInterval(100); +} + +#if BAKE_LIGHTS_SUPPORTED + +Qt5BakeLightsNodeInstanceServer::~Qt5BakeLightsNodeInstanceServer() +{ + cleanup(); +} + +void Qt5BakeLightsNodeInstanceServer::createScene(const CreateSceneCommand &command) +{ + initializeView(); + registerFonts(command.resourceUrl); + setTranslationLanguage(command.language); + setupScene(command); + startRenderTimer(); + + // Set working directory to a temporary directory, as baking process creates a file there + if (m_workingDir.isValid()) + QDir::setCurrent(m_workingDir.path()); +} + +void Qt5BakeLightsNodeInstanceServer::view3DAction(const View3DActionCommand &command) +{ + switch (command.type()) { + case View3DActionType::SetBakeLightsView3D: { + QString id = command.value().toString(); + const QList allViews = allView3DInstances(); + for (const auto &view : allViews) { + if (view.id() == id) { + m_view3D = qobject_cast(view.internalObject()); + break; + } + } + + if (!m_view3D) + abort(tr("View3D not found: '%1'").arg(id)); + else + startRenderTimer(); + break; + } + default: + break; + } +} + +void Qt5BakeLightsNodeInstanceServer::startRenderTimer() +{ + if (timerId() != 0) + killTimer(timerId()); + + int timerId = startTimer(renderTimerInterval()); + + setTimerId(timerId); +} + +void Qt5BakeLightsNodeInstanceServer::bakeLights() +{ + if (!m_view3D) { + abort(tr("Invalid View3D object set.")); + return; + } + + QQuick3DLightmapBaker::Callback callback = [this](QQuick3DLightmapBaker::BakingStatus status, + std::optional msg, QQuick3DLightmapBaker::BakingControl *) { + switch (status) { + case QQuick3DLightmapBaker::BakingStatus::Progress: + case QQuick3DLightmapBaker::BakingStatus::Warning: + case QQuick3DLightmapBaker::BakingStatus::Error: + nodeInstanceClient()->handlePuppetToCreatorCommand( + {PuppetToCreatorCommand::BakeLightsProgress, msg.value_or("")}); + break; + case QQuick3DLightmapBaker::BakingStatus::Cancelled: + abort(tr("Baking cancelled.")); + break; + case QQuick3DLightmapBaker::BakingStatus::Complete: + runDenoiser(); + break; + default: + qWarning() << __FUNCTION__ << "Unexpected light baking status received:" + << int(status) << msg.value_or(""); + break; + } + }; + + QQuick3DLightmapBaker *baker = m_view3D->lightmapBaker(); + baker->bake(callback); + + m_bakingStarted = true; +} + +void Qt5BakeLightsNodeInstanceServer::cleanup() +{ + m_workingDir.remove(); + if (m_denoiser) { + if (m_denoiser->state() == QProcess::Running) + m_denoiser->terminate(); + m_denoiser->deleteLater(); + } +} + +void Qt5BakeLightsNodeInstanceServer::abort(const QString &msg) +{ + cleanup(); + nodeInstanceClient()->handlePuppetToCreatorCommand( + {PuppetToCreatorCommand::BakeLightsAborted, msg}); +} + +void Qt5BakeLightsNodeInstanceServer::finish() +{ + cleanup(); + nodeInstanceClient()->handlePuppetToCreatorCommand( + {PuppetToCreatorCommand::BakeLightsFinished, {}}); +} + +void Qt5BakeLightsNodeInstanceServer::runDenoiser() +{ + // Check if denoiser exists (as it is third party app) + QString binPath = QLibraryInfo::path(QLibraryInfo::BinariesPath); +#if defined(Q_OS_WIN) + binPath += "/qlmdenoiser.exe"; +#elif defined(Q_OS_MACOS) + // TODO: What is the path in mac? +#else + binPath += "/qlmdenoiser"; +#endif + + QFileInfo fi(binPath); + if (!fi.exists()) { + nodeInstanceClient()->handlePuppetToCreatorCommand( + {PuppetToCreatorCommand::BakeLightsProgress, + tr("Warning: Denoiser executable not found, cannot denoise baked lightmaps (%1).") + .arg(binPath)}); + finish(); + return; + } + + m_denoiser = new QProcess(); + + QObject::connect(m_denoiser, &QProcess::errorOccurred, this, [this](QProcess::ProcessError error) { + m_workingDir.remove(); + nodeInstanceClient()->handlePuppetToCreatorCommand( + {PuppetToCreatorCommand::BakeLightsProgress, + tr("Warning: An error occurred while running denoiser process!")}); + finish(); + }); + + QObject::connect(m_denoiser, &QProcess::finished, this, [this](int exitCode, + QProcess::ExitStatus exitStatus) { + if (exitCode == 0 && exitStatus == QProcess::NormalExit) { + nodeInstanceClient()->handlePuppetToCreatorCommand( + {PuppetToCreatorCommand::BakeLightsProgress, + tr("Denoising finished.")}); + } else { + nodeInstanceClient()->handlePuppetToCreatorCommand( + {PuppetToCreatorCommand::BakeLightsProgress, + tr("Warning: Denoiser process failed with exit code '%1'!").arg(exitCode)}); + } + finish(); + }); + + nodeInstanceClient()->handlePuppetToCreatorCommand( + {PuppetToCreatorCommand::BakeLightsProgress, + tr("Denoising baked lightmaps...")}); + + m_denoiser->setWorkingDirectory(m_workingDir.path()); + m_denoiser->start(binPath, {"qlm_list.txt"}); + +} + +void Qt5BakeLightsNodeInstanceServer::collectItemChangesAndSendChangeCommands() +{ + static bool inFunction = false; + + if (!rootNodeInstance().holdsGraphical()) + return; + + if (!inFunction) { + inFunction = true; + + QQuickDesignerSupport::polishItems(quickWindow()); + + render(); + + inFunction = false; + } +} + +void Qt5BakeLightsNodeInstanceServer::render() +{ + // Render multiple times to make sure everything gets rendered correctly before baking + if (++m_renderCount == 4) { + bakeLights(); + } else { + rootNodeInstance().updateDirtyNodeRecursive(); + renderWindow(); + if (m_bakingStarted) + slowDownRenderTimer(); // No more renders needed + } +} +#endif + +} // namespace QmlDesigner diff --git a/src/tools/qml2puppet/qml2puppet/instances/qt5bakelightsnodeinstanceserver.h b/src/tools/qml2puppet/qml2puppet/instances/qt5bakelightsnodeinstanceserver.h new file mode 100644 index 00000000000..fc99adc9021 --- /dev/null +++ b/src/tools/qml2puppet/qml2puppet/instances/qt5bakelightsnodeinstanceserver.h @@ -0,0 +1,57 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0 WITH Qt-GPL-exception-1.0 + +#pragma once + +#include "qt5nodeinstanceserver.h" + +#if QUICK3D_MODULE && QT_VERSION >= QT_VERSION_CHECK(6, 5, 0) +#include +#define BAKE_LIGHTS_SUPPORTED 1 +#endif + +QT_BEGIN_NAMESPACE +class QProcess; +class QQuick3DViewport; +QT_END_NAMESPACE + +namespace QmlDesigner { + +class Qt5BakeLightsNodeInstanceServer : public Qt5NodeInstanceServer +{ + Q_OBJECT +public: + explicit Qt5BakeLightsNodeInstanceServer(NodeInstanceClientInterface *nodeInstanceClient); + +#if BAKE_LIGHTS_SUPPORTED +public: + virtual ~Qt5BakeLightsNodeInstanceServer(); + + void createScene(const CreateSceneCommand &command) override; + void view3DAction(const View3DActionCommand &command) override; + + void render(); + +protected: + void collectItemChangesAndSendChangeCommands() override; + void startRenderTimer() override; + void bakeLights(); + +private: + void abort(const QString &msg); + void runDenoiser(); + void finish(); + void cleanup(); + + QQuick3DViewport *m_view3D = nullptr; + bool m_bakingStarted = false; + int m_renderCount = 0; + QProcess *m_denoiser = nullptr; + QTemporaryDir m_workingDir; +#else +protected: + void collectItemChangesAndSendChangeCommands() override {}; +#endif +}; + +} // namespace QmlDesigner diff --git a/src/tools/qml2puppet/qml2puppet/instances/qt5nodeinstanceclientproxy.cpp b/src/tools/qml2puppet/qml2puppet/instances/qt5nodeinstanceclientproxy.cpp index c5c6f6dc965..b0726b152a0 100644 --- a/src/tools/qml2puppet/qml2puppet/instances/qt5nodeinstanceclientproxy.cpp +++ b/src/tools/qml2puppet/qml2puppet/instances/qt5nodeinstanceclientproxy.cpp @@ -6,6 +6,7 @@ #include #include "capturenodeinstanceserverdispatcher.h" +#include "qt5bakelightsnodeinstanceserver.h" #include "qt5captureimagenodeinstanceserver.h" #include "qt5capturepreviewnodeinstanceserver.h" #include "qt5informationnodeinstanceserver.h" @@ -74,6 +75,9 @@ Qt5NodeInstanceClientProxy::Qt5NodeInstanceClientProxy(QObject *parent) : } else if (QCoreApplication::arguments().at(2) == QLatin1String("captureiconmode")) { setNodeInstanceServer(std::make_unique(this)); initializeSocket(); + } else if (QCoreApplication::arguments().at(2) == QLatin1String("bakelightsmode")) { + setNodeInstanceServer(std::make_unique(this)); + initializeSocket(); } } diff --git a/src/tools/qml2puppet/qml2puppet/instances/servernodeinstance.h b/src/tools/qml2puppet/qml2puppet/instances/servernodeinstance.h index b8e45491a8a..abb16d74bdf 100644 --- a/src/tools/qml2puppet/qml2puppet/instances/servernodeinstance.h +++ b/src/tools/qml2puppet/qml2puppet/instances/servernodeinstance.h @@ -57,6 +57,7 @@ using QHashValueType = size_t; friend class Qt4PreviewNodeInstanceServer; friend class Qt5InformationNodeInstanceServer; friend class Qt5NodeInstanceServer; + friend class Qt5BakeLightsNodeInstanceServer; friend class Qt5PreviewNodeInstanceServer; friend class Qt5CapturePreviewNodeInstanceServer; friend class Qt5TestNodeInstanceServer; From 69902addf1dd3835aeb95be1fa597b144d271abf Mon Sep 17 00:00:00 2001 From: Burak Hancerli Date: Thu, 6 Apr 2023 13:58:19 +0200 Subject: [PATCH 034/192] QmlProjectManager: Fix the build error coming from -Werror Change-Id: I46601bcbb9ce358d34211fdea76707abcb5ca709 Reviewed-by: Reviewed-by: Thomas Hartmann --- src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.cpp b/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.cpp index 6184c9f4816..869e5b89c74 100644 --- a/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.cpp +++ b/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.cpp @@ -141,6 +141,7 @@ void QmlBuildSystem::refresh(RefreshOptions options) break; case RefreshOptions::Project: initProjectItem(); + [[fallthrough]]; case RefreshOptions::Files: parseProjectFiles(); } From df6d690304dbcfd4ba1389767c72f5ba7aa8095d Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Mon, 17 Apr 2023 15:55:38 +0200 Subject: [PATCH 035/192] QmlDesigner: Fix warning Change-Id: I029356618a748f48d09deba0e7e32fb48b953779 Reviewed-by: Miikka Heikkinen Reviewed-by: Tim Jenssen --- .../qml2puppet/instances/qt5bakelightsnodeinstanceserver.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/qml2puppet/qml2puppet/instances/qt5bakelightsnodeinstanceserver.cpp b/src/tools/qml2puppet/qml2puppet/instances/qt5bakelightsnodeinstanceserver.cpp index 87b125480d8..9a8fc9489ac 100644 --- a/src/tools/qml2puppet/qml2puppet/instances/qt5bakelightsnodeinstanceserver.cpp +++ b/src/tools/qml2puppet/qml2puppet/instances/qt5bakelightsnodeinstanceserver.cpp @@ -167,7 +167,7 @@ void Qt5BakeLightsNodeInstanceServer::runDenoiser() m_denoiser = new QProcess(); - QObject::connect(m_denoiser, &QProcess::errorOccurred, this, [this](QProcess::ProcessError error) { + QObject::connect(m_denoiser, &QProcess::errorOccurred, this, [this](QProcess::ProcessError) { m_workingDir.remove(); nodeInstanceClient()->handlePuppetToCreatorCommand( {PuppetToCreatorCommand::BakeLightsProgress, From 49c85bbf70798adc1112448481dd4a655d7f26e8 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Mon, 3 Apr 2023 13:37:14 +0200 Subject: [PATCH 036/192] QmlDesigner: Let ~LibraryInitializer() throw exception Because LibraryInitializer has no state it should be fine if it is used like intended. The exception is anyway fatal. Change-Id: I397e5b03e63d69f1468b46a8f333522629f1d882 Reviewed-by: Reviewed-by: Tim Jenssen --- src/libs/sqlite/sqlitelibraryinitializer.cpp | 2 +- src/libs/sqlite/sqlitelibraryinitializer.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libs/sqlite/sqlitelibraryinitializer.cpp b/src/libs/sqlite/sqlitelibraryinitializer.cpp index 927157dba76..0e90348c231 100644 --- a/src/libs/sqlite/sqlitelibraryinitializer.cpp +++ b/src/libs/sqlite/sqlitelibraryinitializer.cpp @@ -17,7 +17,7 @@ LibraryInitializer::LibraryInitializer() DatabaseBackend::initializeSqliteLibrary(); } -LibraryInitializer::~LibraryInitializer() +LibraryInitializer::~LibraryInitializer() noexcept(false) { DatabaseBackend::shutdownSqliteLibrary(); } diff --git a/src/libs/sqlite/sqlitelibraryinitializer.h b/src/libs/sqlite/sqlitelibraryinitializer.h index 27c891a1f43..bb42e822f82 100644 --- a/src/libs/sqlite/sqlitelibraryinitializer.h +++ b/src/libs/sqlite/sqlitelibraryinitializer.h @@ -14,7 +14,7 @@ public: private: LibraryInitializer(); - ~LibraryInitializer(); + ~LibraryInitializer() noexcept(false); }; } // namespace Sqlite From 86e2426ac63689f1d53452db60c3c7f5ebd46659 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Thu, 13 Apr 2023 00:40:03 +0200 Subject: [PATCH 037/192] QmlDesigner: Improve ownership of text editor Handling raw pointer can easily lead to mistakes. So it's better to remove unique pointer. In this case a deleteLater pointer is added. The pointer has not be tested for null because of: 23.11.1.2.2 unique_ptr destructor [unique.ptr.single.dtor] 2 Effects: If get() == nullptr there are no effects. Otherwise get_deleter()(get()). Change-Id: I97eeb86b7316fc93fbed89707644ab7dde7f89f6 Reviewed-by: Qt CI Bot Reviewed-by: Thomas Hartmann --- src/libs/utils/uniqueobjectptr.h | 28 +++++++++++++++++++ .../components/texteditor/texteditorview.cpp | 12 ++++---- .../texteditor/texteditorwidget.cpp | 23 +++++++-------- .../components/texteditor/texteditorwidget.h | 5 ++-- 4 files changed, 48 insertions(+), 20 deletions(-) diff --git a/src/libs/utils/uniqueobjectptr.h b/src/libs/utils/uniqueobjectptr.h index e660e459564..1a06394dfa3 100644 --- a/src/libs/utils/uniqueobjectptr.h +++ b/src/libs/utils/uniqueobjectptr.h @@ -45,6 +45,25 @@ struct UniqueObjectPtrDeleter } }; +template +struct UniqueObjectPtrLateDeleter +{ + using pointer = UniqueObjectInternalPointer; + + constexpr UniqueObjectPtrLateDeleter() noexcept = default; + template>> + constexpr UniqueObjectPtrLateDeleter(const UniqueObjectPtrLateDeleter &) noexcept + {} + + constexpr void operator()(pointer p) const + { + static_assert(!std::is_void_v, "can't delete pointer to incomplete type"); + static_assert(sizeof(Type) > 0, "can't delete pointer to incomplete type"); + + p->deleteLater(); + } +}; + } // namespace Internal template @@ -55,4 +74,13 @@ auto makeUniqueObjectPtr(Arguments &&...arguments) { return UniqueObjectPtr{new Type(std::forward(arguments)...)}; } + +template +using UniqueObjectLatePtr = std::unique_ptr>; + +template +auto makeUniqueObjectLatePtr(Arguments &&...arguments) +{ + return UniqueObjectLatePtr{new Type(std::forward(arguments)...)}; +} } // namespace Utils diff --git a/src/plugins/qmldesigner/components/texteditor/texteditorview.cpp b/src/plugins/qmldesigner/components/texteditor/texteditorview.cpp index a7ada80ed73..fb33794ea80 100644 --- a/src/plugins/qmldesigner/components/texteditor/texteditorview.cpp +++ b/src/plugins/qmldesigner/components/texteditor/texteditorview.cpp @@ -27,10 +27,11 @@ #include #include -#include -#include #include #include +#include +#include +#include #include #include @@ -78,15 +79,16 @@ void TextEditorView::modelAttached(Model *model) AbstractView::modelAttached(model); - auto textEditor = qobject_cast( - QmlDesignerPlugin::instance()->currentDesignDocument()->textEditor()->duplicate()); + auto textEditor = Utils::UniqueObjectLatePtr( + static_cast( + QmlDesignerPlugin::instance()->currentDesignDocument()->textEditor()->duplicate())); // Set the context of the text editor, but we add another special context to override shortcuts. Core::Context context = textEditor->context(); context.prepend(TEXTEDITOR_CONTEXT_ID); m_textEditorContext->setContext(context); - m_widget->setTextEditor(textEditor); + m_widget->setTextEditor(std::move(textEditor)); } void TextEditorView::modelAboutToBeDetached(Model *model) diff --git a/src/plugins/qmldesigner/components/texteditor/texteditorwidget.cpp b/src/plugins/qmldesigner/components/texteditor/texteditorwidget.cpp index 1529c0a87c1..a3ac192ecb0 100644 --- a/src/plugins/qmldesigner/components/texteditor/texteditorwidget.cpp +++ b/src/plugins/qmldesigner/components/texteditor/texteditorwidget.cpp @@ -2,6 +2,7 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "texteditorwidget.h" +#include "utils/uniqueobjectptr.h" #include #include @@ -51,34 +52,30 @@ TextEditorWidget::TextEditorWidget(TextEditorView *textEditorView) QmlDesignerPlugin::trackWidgetFocusTime(this, Constants::EVENT_TEXTEDITOR_TIME); } -void TextEditorWidget::setTextEditor(TextEditor::BaseTextEditor *textEditor) +void TextEditorWidget::setTextEditor(Utils::UniqueObjectLatePtr textEditor) { - TextEditor::BaseTextEditor *oldEditor = m_textEditor.release(); - m_textEditor.reset(textEditor); + std::swap(m_textEditor, textEditor); - if (textEditor) { + if (m_textEditor) { m_layout->insertWidget(0, textEditor->editorWidget()); - setFocusProxy(textEditor->editorWidget()); + setFocusProxy(m_textEditor->editorWidget()); - QmlDesignerPlugin::instance()->emitCurrentTextEditorChanged(textEditor); + QmlDesignerPlugin::instance()->emitCurrentTextEditorChanged(m_textEditor.get()); - connect(textEditor->editorWidget(), &QPlainTextEdit::cursorPositionChanged, this, [this] { + connect(m_textEditor->editorWidget(), &QPlainTextEdit::cursorPositionChanged, this, [this] { // Cursor position is changed by rewriter if (!m_blockCursorSelectionSynchronisation) m_updateSelectionTimer.start(); }); - textEditor->editorWidget()->installEventFilter(this); + m_textEditor->editorWidget()->installEventFilter(this); static QString styleSheet = Theme::replaceCssColors( QString::fromUtf8(Utils::FileReader::fetchQrc(":/qmldesigner/scrollbar.css"))); - textEditor->editorWidget()->verticalScrollBar()->setStyleSheet(styleSheet); - textEditor->editorWidget()->horizontalScrollBar()->setStyleSheet(styleSheet); + m_textEditor->editorWidget()->verticalScrollBar()->setStyleSheet(styleSheet); + m_textEditor->editorWidget()->horizontalScrollBar()->setStyleSheet(styleSheet); } - - if (oldEditor) - oldEditor->deleteLater(); } void TextEditorWidget::contextHelp(const Core::IContext::HelpCallback &callback) const diff --git a/src/plugins/qmldesigner/components/texteditor/texteditorwidget.h b/src/plugins/qmldesigner/components/texteditor/texteditorwidget.h index c5e5c4f5bc9..947826ab598 100644 --- a/src/plugins/qmldesigner/components/texteditor/texteditorwidget.h +++ b/src/plugins/qmldesigner/components/texteditor/texteditorwidget.h @@ -3,6 +3,7 @@ #pragma once #include +#include #include #include @@ -25,7 +26,7 @@ class TextEditorWidget : public QWidget { public: TextEditorWidget(TextEditorView *textEditorView); - void setTextEditor(TextEditor::BaseTextEditor *textEditor); + void setTextEditor(Utils::UniqueObjectLatePtr textEditor); TextEditor::BaseTextEditor *textEditor() const { @@ -51,7 +52,7 @@ protected: private: void updateSelectionByCursorPosition(); - std::unique_ptr m_textEditor; + Utils::UniqueObjectLatePtr m_textEditor; QPointer m_textEditorView; QTimer m_updateSelectionTimer; TextEditorStatusBar *m_statusBar = nullptr; From d14c38e8736adc182ebac0a5448684aff90cacb0 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Mon, 17 Apr 2023 14:46:03 +0200 Subject: [PATCH 038/192] QmlDeesigner: Add feature to disable error on warnings Change-Id: Idc0cbf2c6fd4069d84fe18de190d91ed94610a8a Reviewed-by: Tim Jenssen Reviewed-by: Qt CI Bot Reviewed-by: hjk --- src/plugins/qmldesigner/CMakeLists.txt | 36 +++++++++++++++++++- src/plugins/qmldesignerbase/CMakeLists.txt | 10 +++++- src/plugins/qmlprojectmanager/CMakeLists.txt | 6 +++- 3 files changed, 49 insertions(+), 3 deletions(-) diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index 9280965ce0d..ed558082e1e 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -25,7 +25,6 @@ option(USE_PROJECTSTORAGE "Use ProjectStorage" ${ENV_QDS_USE_PROJECTSTORAGE}) add_feature_info("ProjectStorage" ${USE_PROJECTSTORAGE} "") add_qtc_library(QmlDesignerUtils STATIC - PROPERTIES COMPILE_WARNING_AS_ERROR ON DEPENDS Qt::Gui Utils Qt::QmlPrivate DEFINES QMLDESIGNERUTILS_LIBRARY @@ -44,6 +43,11 @@ add_qtc_library(QmlDesignerUtils STATIC qmldesignerutils_global.h ) +extend_qtc_library(QmlDesignerUtils + CONDITION NOT DISABLE_COMPILE_WARNING_AS_ERROR + PROPERTIES COMPILE_WARNING_AS_ERROR ON +) + add_qtc_library(QmlDesignerCore STATIC EXCLUDE_FROM_INSTALL DEPENDS @@ -79,6 +83,11 @@ add_qtc_library(QmlDesignerCore STATIC rewritertransaction.h ) +extend_qtc_library(QmlDesignerCore + CONDITION NOT DISABLE_COMPILE_WARNING_AS_ERROR + PROPERTIES COMPILE_WARNING_AS_ERROR ON +) + extend_qtc_library(QmlDesignerCore CONDITION TARGET Qt6::QmlDomPrivate AND TARGET Qt6::QmlCompilerPrivate AND Qt6_VERSION VERSION_GREATER_EQUAL 6.5.0 @@ -484,6 +493,11 @@ extend_qtc_plugin(QmlDesigner QMLDESIGNER_PLUGIN_PATH "${QmlDesignerPluginInstallPrefix}" ) +extend_qtc_plugin(QmlDesigner + CONDITION NOT DISABLE_COMPILE_WARNING_AS_ERROR + PROPERTIES COMPILE_WARNING_AS_ERROR ON +) + function(get_and_add_as_subdirectory name repository git_tag build_dir source_dir source_subdir) # make the configuration in the build dir file(MAKE_DIRECTORY ${build_dir}/${name}) @@ -553,6 +567,11 @@ add_qtc_plugin(assetexporterplugin PLUGIN_PATH ${QmlDesignerPluginInstallPrefix} ) +extend_qtc_plugin(assetexporterplugin + CONDITION NOT DISABLE_COMPILE_WARNING_AS_ERROR + PROPERTIES COMPILE_WARNING_AS_ERROR ON +) + add_qtc_plugin(componentsplugin PLUGIN_CLASS ComponentsPlugin CONDITION TARGET QmlDesigner @@ -569,6 +588,11 @@ add_qtc_plugin(componentsplugin PLUGIN_PATH ${QmlDesignerPluginInstallPrefix} ) +extend_qtc_plugin(componentsplugin + CONDITION NOT DISABLE_COMPILE_WARNING_AS_ERROR + PROPERTIES COMPILE_WARNING_AS_ERROR ON +) + add_qtc_plugin(qmlpreviewplugin PLUGIN_CLASS QmlPreviewWidgetPlugin CONDITION TARGET QmlDesigner @@ -580,6 +604,11 @@ add_qtc_plugin(qmlpreviewplugin PLUGIN_PATH ${QmlDesignerPluginInstallPrefix} ) +extend_qtc_plugin(qmlpreviewplugin + CONDITION NOT DISABLE_COMPILE_WARNING_AS_ERROR + PROPERTIES COMPILE_WARNING_AS_ERROR ON +) + add_qtc_plugin(qtquickplugin PLUGIN_CLASS QtQuickPlugin CONDITION TARGET QmlDesigner @@ -591,6 +620,11 @@ add_qtc_plugin(qtquickplugin PLUGIN_PATH ${QmlDesignerPluginInstallPrefix} ) +extend_qtc_plugin(qtquickplugin + CONDITION NOT DISABLE_COMPILE_WARNING_AS_ERROR + PROPERTIES COMPILE_WARNING_AS_ERROR ON +) + add_subdirectory(studioplugin) extend_qtc_plugin(QmlDesigner diff --git a/src/plugins/qmldesignerbase/CMakeLists.txt b/src/plugins/qmldesignerbase/CMakeLists.txt index 314940988f5..f02c4861067 100644 --- a/src/plugins/qmldesignerbase/CMakeLists.txt +++ b/src/plugins/qmldesignerbase/CMakeLists.txt @@ -1,5 +1,8 @@ +env_with_default("QDS_DISABLE_COMPILE_WARNING_AS_ERROR" ENV_QDS_DISABLE_COMPILE_WARNING_AS_ERROR OFF) +option(DISABLE_COMPILE_WARNING_AS_ERROR "Dont treat warnings as errors" ${ENV_QDS_DISABLE_COMPILE_WARNING_AS_ERROR}) +add_feature_info("Treat warnings as errors" ${DISABLE_COMPILE_WARNING_AS_ERROR} "") + add_qtc_plugin(QmlDesignerBase - PROPERTIES COMPILE_WARNING_AS_ERROR ON CONDITION TARGET Qt::QuickWidgets DEPENDS Qt::Core Qt::QuickWidgets PLUGIN_DEPENDS Core ProjectExplorer QtSupport @@ -8,6 +11,11 @@ add_qtc_plugin(QmlDesignerBase qmldesignerbaseplugin.cpp qmldesignerbaseplugin.h ) +extend_qtc_plugin(QmlDesignerBase + CONDITION NOT DISABLE_COMPILE_WARNING_AS_ERROR + PROPERTIES COMPILE_WARNING_AS_ERROR ON +) + extend_qtc_plugin(QmlDesignerBase PUBLIC_INCLUDES ${CMAKE_CURRENT_LIST_DIR}/utils SOURCES_PREFIX ${CMAKE_CURRENT_LIST_DIR}/utils diff --git a/src/plugins/qmlprojectmanager/CMakeLists.txt b/src/plugins/qmlprojectmanager/CMakeLists.txt index 7e2d30ce85b..3b7209d05c7 100644 --- a/src/plugins/qmlprojectmanager/CMakeLists.txt +++ b/src/plugins/qmlprojectmanager/CMakeLists.txt @@ -1,6 +1,5 @@ add_qtc_plugin(QmlProjectManager CONDITION TARGET Qt5::QuickWidgets - PROPERTIES COMPILE_WARNING_AS_ERROR ON PLUGIN_CLASS QmlProjectPlugin DEPENDS QmlJS Qt5::QuickWidgets Utils PLUGIN_DEPENDS Core ProjectExplorer QtSupport QmlDesignerBase @@ -26,6 +25,11 @@ add_qtc_plugin(QmlProjectManager "${PROJECT_SOURCE_DIR}/src/share/3rdparty/studiofonts/studiofonts.qrc" ) +extend_qtc_plugin(QmlProjectManager + CONDITION NOT DISABLE_COMPILE_WARNING_AS_ERROR + PROPERTIES COMPILE_WARNING_AS_ERROR ON +) + extend_qtc_plugin(QmlProjectManager PUBLIC_INCLUDES ${CMAKE_CURRENT_LIST_DIR}/buildsystem SOURCES_PREFIX ${CMAKE_CURRENT_LIST_DIR}/buildsystem From c609baf73ba49056ad55baaf0790afe79cde1d98 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Mon, 17 Apr 2023 18:11:21 +0200 Subject: [PATCH 039/192] QmlDesigner: Set QML_DISABLE_INTERNAL_DEFERRED_PROPERTIES Task-number: QDS-8545 Change-Id: I3ef92bba9c488586a85bb81ba9a244645b748dd8 Reviewed-by: Tim Jenssen --- src/plugins/qmldesigner/puppetenvironmentbuilder.cpp | 6 ++++++ src/plugins/qmldesigner/puppetenvironmentbuilder.h | 1 + 2 files changed, 7 insertions(+) diff --git a/src/plugins/qmldesigner/puppetenvironmentbuilder.cpp b/src/plugins/qmldesigner/puppetenvironmentbuilder.cpp index 7d7957753c4..079cc0a326b 100644 --- a/src/plugins/qmldesigner/puppetenvironmentbuilder.cpp +++ b/src/plugins/qmldesigner/puppetenvironmentbuilder.cpp @@ -51,6 +51,7 @@ QProcessEnvironment PuppetEnvironmentBuilder::processEnvironment() const addMultiLanguageDatatbase(); addImportPaths(); addCustomFileSelectors(); + addDisableDeferredProperties(); qCInfo(puppetEnvirmentBuild) << "Puppet environment:" << m_environment.toStringList(); @@ -228,6 +229,11 @@ void PuppetEnvironmentBuilder::addCustomFileSelectors() const qCInfo(puppetEnvirmentBuild) << "Puppet selectors:" << customFileSelectors; } +void PuppetEnvironmentBuilder::addDisableDeferredProperties() const +{ + m_environment.set("QML_DISABLE_INTERNAL_DEFERRED_PROPERTIES", "true"); +} + PuppetType PuppetEnvironmentBuilder::determinePuppetType() const { if (m_target && m_target->kit() && m_target->kit()->isValid()) { diff --git a/src/plugins/qmldesigner/puppetenvironmentbuilder.h b/src/plugins/qmldesigner/puppetenvironmentbuilder.h index e3ef33a7211..3c2ffba271e 100644 --- a/src/plugins/qmldesigner/puppetenvironmentbuilder.h +++ b/src/plugins/qmldesigner/puppetenvironmentbuilder.h @@ -49,6 +49,7 @@ private: void addMultiLanguageDatatbase() const; void addImportPaths() const; void addCustomFileSelectors() const; + void addDisableDeferredProperties() const; private: ProjectExplorer::Target *m_target = nullptr; From c98eb999573b5909c3273094c3140395a9b5dbf6 Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Tue, 18 Apr 2023 10:48:42 +0300 Subject: [PATCH 040/192] QmlDesigner: Update material browser view model after state changes Task-number: QDS-9485 Change-Id: I65e650b6929b9c83b1de968ef1d77ec46c027c88 Reviewed-by: Miikka Heikkinen --- .../components/materialbrowser/materialbrowserview.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.cpp b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.cpp index 5265c70683d..8e799336f85 100644 --- a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.cpp +++ b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.cpp @@ -540,7 +540,7 @@ void MaterialBrowserView::active3DSceneChanged(qint32 sceneId) void MaterialBrowserView::currentStateChanged([[maybe_unused]] const ModelNode &node) { - m_widget->materialBrowserTexturesModel()->updateAllTexturesSources(); + refreshModel(true); } void MaterialBrowserView::instancesCompleted(const QVector &completedNodeList) From fcf33702025cc81758f66cd5f1f1637111418035 Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Tue, 4 Apr 2023 14:31:35 +0300 Subject: [PATCH 041/192] QmlDesigner: Prevent scrolling when a section's context menu is open Fixes: QDS-9619 Change-Id: Ic278cda058b179bfed08b2ce6e2def7ca9131d69 Reviewed-by: Reviewed-by: Miikka Heikkinen --- .../contentLibraryQmlSource/ContentLibraryMaterialsView.qml | 1 + .../contentLibraryQmlSource/ContentLibraryTexturesView.qml | 1 + .../materialBrowserQmlSource/MaterialBrowser.qml | 1 + .../imports/HelperWidgets/Controller.qml | 2 ++ .../imports/HelperWidgets/PropertyEditorPane.qml | 2 ++ .../imports/HelperWidgets/Section.qml | 6 ++++-- 6 files changed, 11 insertions(+), 2 deletions(-) diff --git a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryMaterialsView.qml b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryMaterialsView.qml index 2c6a5256f46..975e65e266b 100644 --- a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryMaterialsView.qml +++ b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryMaterialsView.qml @@ -12,6 +12,7 @@ HelperWidgets.ScrollView { clip: true interactive: !ctxMenu.opened && !ContentLibraryBackend.rootView.isDragging + && !HelperWidgets.Controller.contextMenuOpened readonly property int cellWidth: 100 readonly property int cellHeight: 120 diff --git a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryTexturesView.qml b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryTexturesView.qml index 1c24f9bc43b..7471a22cfb7 100644 --- a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryTexturesView.qml +++ b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryTexturesView.qml @@ -12,6 +12,7 @@ HelperWidgets.ScrollView { clip: true interactive: !ctxMenu.opened && !ContentLibraryBackend.rootView.isDragging + && !HelperWidgets.Controller.contextMenuOpened readonly property int cellWidth: 100 readonly property int cellHeight: 100 diff --git a/share/qtcreator/qmldesigner/materialBrowserQmlSource/MaterialBrowser.qml b/share/qtcreator/qmldesigner/materialBrowserQmlSource/MaterialBrowser.qml index 620feb3b113..d6b5c76ff6a 100644 --- a/share/qtcreator/qmldesigner/materialBrowserQmlSource/MaterialBrowser.qml +++ b/share/qtcreator/qmldesigner/materialBrowserQmlSource/MaterialBrowser.qml @@ -609,6 +609,7 @@ Item { clip: true visible: root.enableUiElements interactive: !ctxMenu.opened && !ctxMenuTextures.opened && !rootView.isDragging + && !HelperWidgets.Controller.contextMenuOpened Behavior on contentY { id: contentYBehavior diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/Controller.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/Controller.qml index 7b9be3890e2..1825a5e8fff 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/Controller.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/Controller.qml @@ -9,6 +9,8 @@ QtObject { property Item mainScrollView + property bool contextMenuOpened: false + signal collapseAll(string category) signal expandAll(string category) signal closeContextMenu() diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/PropertyEditorPane.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/PropertyEditorPane.qml index 5317d909e88..17a62874c25 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/PropertyEditorPane.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/PropertyEditorPane.qml @@ -34,6 +34,8 @@ Rectangle { clip: true anchors.fill: parent + interactive: !Controller.contextMenuOpened + Column { id: mainColumn y: -1 diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/Section.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/Section.qml index b965e8becdb..1cee92847fc 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/Section.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/Section.qml @@ -86,8 +86,8 @@ Item { enabled: section.dropEnabled anchors.fill: parent - onEntered: (drag)=> section.dropEnter(drag) - onDropped: (drag)=> section.drop(drag) + onEntered: (drag) => section.dropEnter(drag) + onDropped: (drag) => section.drop(drag) onExited: section.dropExit() } @@ -114,6 +114,8 @@ Item { text: qsTr("Collapse All") onTriggered: Controller.collapseAll(section.category) } + + onOpenedChanged: Controller.contextMenuOpened = contextMenu.opened } } From 886cbcde1d52e757900aeeac3587f44e402577c9 Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Fri, 14 Apr 2023 13:26:44 +0300 Subject: [PATCH 042/192] QmlDesigner: Fix the height of the menubar items MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Task-number: QDS-9684 Change-Id: Ic9cc89b209440b46bcfa66c0449250e4fef4a267 Reviewed-by: Reviewed-by: Henning Gründl --- src/plugins/qmldesignerbase/utils/studiostyle.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/plugins/qmldesignerbase/utils/studiostyle.cpp b/src/plugins/qmldesignerbase/utils/studiostyle.cpp index 226c9ffb731..4fd375cd105 100644 --- a/src/plugins/qmldesignerbase/utils/studiostyle.cpp +++ b/src/plugins/qmldesignerbase/utils/studiostyle.cpp @@ -774,6 +774,11 @@ QSize StudioStyle::sizeFromContents( switch (type) { case CT_MenuItem: if (const auto mbi = qstyleoption_cast(option)) { + if (!isQmlEditorMenu(widget)) { + newSize = Super::sizeFromContents(type, option, size, widget); + break; + } + const int leftMargin = pixelMetric( QStyle::PM_LayoutLeftMargin, option, widget); From 87e7829eaa8cba61909555b15e773ddb275c90d9 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Tue, 18 Apr 2023 14:49:35 +0200 Subject: [PATCH 043/192] QmlDesigner: Fix code view is empty MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes: QDS-9702 Change-Id: I23591db266e779a83e2b6df4156fae8c79e0d06d Reviewed-by: Tim Jenssen Reviewed-by: Henning Gründl --- .../qmldesigner/components/texteditor/texteditorwidget.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/qmldesigner/components/texteditor/texteditorwidget.cpp b/src/plugins/qmldesigner/components/texteditor/texteditorwidget.cpp index a3ac192ecb0..9f4bb59231e 100644 --- a/src/plugins/qmldesigner/components/texteditor/texteditorwidget.cpp +++ b/src/plugins/qmldesigner/components/texteditor/texteditorwidget.cpp @@ -57,7 +57,7 @@ void TextEditorWidget::setTextEditor(Utils::UniqueObjectLatePtrinsertWidget(0, textEditor->editorWidget()); + m_layout->insertWidget(0, m_textEditor->editorWidget()); setFocusProxy(m_textEditor->editorWidget()); From bcb89dc900b9508ac2a06f4ef14517ce553bbc53 Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Wed, 12 Apr 2023 13:31:19 +0300 Subject: [PATCH 044/192] QmlDesigner: Enable mouse click events for the crumble bread tooltips MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The tooltip of a crumble bread will behave the same as the crumble bread itself when clicked. Task-number: QDS-9572 Change-Id: I7bd29887a08a0e6fea2732198a92ca75ebce0bd2 Reviewed-by: Reviewed-by: Henning Gründl Reviewed-by: Mahmoud Badri --- .../qmldesigner/toolbar/CrumbleBread.qml | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/share/qtcreator/qmldesigner/toolbar/CrumbleBread.qml b/share/qtcreator/qmldesigner/toolbar/CrumbleBread.qml index 538c1356429..19142de882e 100644 --- a/share/qtcreator/qmldesigner/toolbar/CrumbleBread.qml +++ b/share/qtcreator/qmldesigner/toolbar/CrumbleBread.qml @@ -120,9 +120,18 @@ Item { font.pixelSize: 12 font.family: "SF Pro" - ToolTip.text: root.tooltip - ToolTip.visible: mouseArea.containsMouse - ToolTip.delay: 1000 + ToolTip { + id: toolTipBox + text: root.tooltip + visible: mouseArea.containsMouse + delay: 1000 + + MouseArea { + anchors.fill: parent + anchors.margins: -toolTipBox.padding + onClicked: (mouseEvent) => mouseArea.clicked(mouseEvent) + } + } } MouseArea { From f5da6d2eac08d97d3d68e28bf22bd52b633ab02f Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Wed, 12 Apr 2023 16:23:32 +0300 Subject: [PATCH 045/192] QmlDesigner: Fix shortcut text clip for the context menus MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Task-number: QDS-9662 Change-Id: I7f9385e99c65b2201e36625b2094f04e6f6b701f Reviewed-by: Reviewed-by: Henning Gründl --- src/plugins/qmldesignerbase/utils/studiostyle.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/qmldesignerbase/utils/studiostyle.cpp b/src/plugins/qmldesignerbase/utils/studiostyle.cpp index 4fd375cd105..ec0b0f4af52 100644 --- a/src/plugins/qmldesignerbase/utils/studiostyle.cpp +++ b/src/plugins/qmldesignerbase/utils/studiostyle.cpp @@ -155,7 +155,7 @@ private: void addText(const QString &txt, QPainter *painter = nullptr) { if (txt.size()) { - int textWidth = fm.boundingRect(txt).width(); + int textWidth = fm.horizontalAdvance(txt); QSize itemSize = {textWidth, defaultHeight}; if (painter) { static const QTextOption textOption(Qt::AlignLeft | Qt::AlignVCenter); From 17feb6c352014dd96e99e844dd51eab5d890e022 Mon Sep 17 00:00:00 2001 From: Henning Gruendl Date: Tue, 18 Apr 2023 17:37:09 +0200 Subject: [PATCH 046/192] QmlDesigner: Add insight plugin Move insight plugin from qtquickdesigner repository to this in order for it to be available in all QtDS editions. Task-number: QDS-9626 Change-Id: I42ac804636d59930c4534df951455f864282986b Reviewed-by: Thomas Hartmann --- share/qtcreator/qmldesigner/insight/Main.qml | 417 +++++++++ src/plugins/CMakeLists.txt | 1 + src/plugins/insight/CMakeLists.txt | 13 + src/plugins/insight/Insight.json.in | 22 + src/plugins/insight/insightmodel.cpp | 853 +++++++++++++++++++ src/plugins/insight/insightmodel.h | 132 +++ src/plugins/insight/insightplugin.cpp | 31 + src/plugins/insight/insightplugin.h | 22 + src/plugins/insight/insightview.cpp | 57 ++ src/plugins/insight/insightview.h | 40 + src/plugins/insight/insightwidget.cpp | 123 +++ src/plugins/insight/insightwidget.h | 45 + 12 files changed, 1756 insertions(+) create mode 100644 share/qtcreator/qmldesigner/insight/Main.qml create mode 100644 src/plugins/insight/CMakeLists.txt create mode 100644 src/plugins/insight/Insight.json.in create mode 100644 src/plugins/insight/insightmodel.cpp create mode 100644 src/plugins/insight/insightmodel.h create mode 100644 src/plugins/insight/insightplugin.cpp create mode 100644 src/plugins/insight/insightplugin.h create mode 100644 src/plugins/insight/insightview.cpp create mode 100644 src/plugins/insight/insightview.h create mode 100644 src/plugins/insight/insightwidget.cpp create mode 100644 src/plugins/insight/insightwidget.h diff --git a/share/qtcreator/qmldesigner/insight/Main.qml b/share/qtcreator/qmldesigner/insight/Main.qml new file mode 100644 index 00000000000..3d0cd777753 --- /dev/null +++ b/share/qtcreator/qmldesigner/insight/Main.qml @@ -0,0 +1,417 @@ +// 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 +import QtQuick.Controls +import QtQuickDesignerTheme 1.0 +import HelperWidgets 2.0 as HelperWidgets +import StudioControls 1.0 as StudioControls +import StudioTheme as StudioTheme + +Rectangle { + id: root + + color: Theme.qmlDesignerBackgroundColorDarkAlternate() + + component Title : Text { + color: StudioTheme.Values.themeTextColor + font { + pixelSize: StudioTheme.Values.baseFontSize + bold: true + } + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + Layout.fillWidth: true + Layout.preferredHeight: 30 + } + + StudioControls.Dialog { + id: renameDialog + + property string name: "" + + title: qsTr("Failed to rename \"" + renameDialog.name + "\".") + standardButtons: Dialog.Close + x: Math.round((root.width - renameDialog.width) / 2) + y: Math.round((root.height - renameDialog.height) / 2) + closePolicy: Popup.NoAutoClose + + Text { + color: StudioTheme.Values.themeTextColor + font.pixelSize: StudioTheme.Values.baseFontSize + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + wrapMode: Text.WordWrap + text: qsTr("Can't rename category, name already exists.") + anchors.fill: parent + } + } + + HelperWidgets.ScrollView { + clip: true + anchors.fill: parent + + Column { + id: column + width: root.width + + Section { + id: trackingSection + caption: qsTr("Tracking") + anchors.left: parent.left + anchors.right: parent.right + + property int rowSpacing: 16 + + ColumnLayout { + spacing: StudioTheme.Values.sectionRowSpacing + width: parent.width - StudioTheme.Values.sectionLayoutRightPadding + + Text { + wrapMode: Text.WordWrap + color: StudioTheme.Values.themeTextColor + font.pixelSize: StudioTheme.Values.baseFontSize + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + text: qsTr("With tracking turned on, the application tracks user interactions for all component types in the selected predefined categories.") + Layout.fillWidth: true + } + + Title { text: qsTr("Tracking") } + + Row { + spacing: trackingSection.rowSpacing + + StudioControls.Switch { + id: trackingSwitch + actionIndicatorVisible: false + checked: insightModel.enabled + + onToggled: insightModel.setEnabled(trackingSwitch.checked) + } + + Text { + color: StudioTheme.Values.themeTextColor + text: trackingSwitch.checked ? qsTr("Enabled") : qsTr("Disabled") + font.pixelSize: StudioTheme.Values.baseFontSize + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + height: trackingSwitch.height + } + } + + Title { text: qsTr("Token") } + + Text { + wrapMode: Text.WordWrap + color: StudioTheme.Values.themeTextColor + font.pixelSize: StudioTheme.Values.baseFontSize + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + text: qsTr("Tokens are used to match the data your application sends to your Qt Insight Organization.") + Layout.fillWidth: true + } + + StudioControls.TextField { + id: tokenField + + property string previousToken + + enabled: trackingSwitch.checked + actionIndicatorVisible: false + translationIndicatorVisible: false + placeholderText: qsTr("Add token here") + text: insightModel.token + Layout.fillWidth: true + + onEditingFinished: { + if (tokenField.previousToken === tokenField.text) + return + + tokenField.previousToken = tokenField.text + insightModel.setToken(tokenField.text) + } + + Component.onCompleted: tokenField.previousToken = tokenField.text + } + + Title { text: qsTr("Send Cadence") } + + Row { + spacing: trackingSection.rowSpacing + + StudioControls.SpinBox { + id: cadenceSpinBox + enabled: trackingSwitch.checked + actionIndicatorVisible: false + value: insightModel.minutes + onValueModified: insightModel.setMinutes(cadenceSpinBox.value) + + __devicePixelRatio: insightModel.devicePixelRatio() + + onDragStarted: insightModel.hideCursor() + onDragEnded: insightModel.restoreCursor() + onDragging: insightModel.holdCursorInPlace() + } + + Text { + color: trackingSwitch.checked ? StudioTheme.Values.themeTextColor + : StudioTheme.Values.themeTextColorDisabled + text: qsTr("minutes") + font.pixelSize: StudioTheme.Values.baseFontSize + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + height: cadenceSpinBox.height + } + } + } + } + + Section { + id: predefinedSection + caption: qsTr("Predefined Categories") + anchors.left: parent.left + anchors.right: parent.right + + ColumnLayout { + enabled: trackingSwitch.checked + spacing: StudioTheme.Values.sectionRowSpacing + width: parent.width - StudioTheme.Values.sectionLayoutRightPadding + + Text { + wrapMode: Text.WordWrap + color: StudioTheme.Values.themeTextColor + font.pixelSize: StudioTheme.Values.baseFontSize + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + text: qsTr("Select the categories to track") + Layout.fillWidth: true + } + + Column { + id: predefinedTable + + property int centerColumnWidth: predefinedTable.width + - selectAll.width + - 2 * predefinedTable.rowSpacing + + readonly property int columnSpacing: 2 + readonly property int rowSpacing: 8 + + Layout.fillWidth: true + spacing: predefinedTable.columnSpacing + + Row { + spacing: predefinedTable.rowSpacing + + StudioControls.CheckBox { + id: selectAll + actionIndicatorVisible: false + tristate: true + checkState: insightModel.predefinedSelectState + onClicked: insightModel.selectAllPredefined() + } + + Text { + color: trackingSwitch.checked ? StudioTheme.Values.themeTextColor + : StudioTheme.Values.themeTextColorDisabled + font { + pixelSize: StudioTheme.Values.baseFontSize + bold: true + } + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + text: qsTr("Select all") + width: predefinedTable.centerColumnWidth + height: StudioTheme.Values.height + } + } + + Item { width: 1; height: 4} + + Repeater { + model: insightModel + delegate: Row { + id: predefinedDelegate + + required property int index + + required property string categoryName + required property string categoryColor + required property string categoryType + required property bool categoryActive + + visible: predefinedDelegate.categoryType === "predefined" + spacing: predefinedTable.rowSpacing + + StudioControls.CheckBox { + id: predefinedCheckBox + actionIndicatorVisible: false + checked: predefinedDelegate.categoryActive + onToggled: insightModel.setCategoryActive(predefinedDelegate.index, + predefinedCheckBox.checked) + } + + Text { + color: trackingSwitch.checked ? StudioTheme.Values.themeTextColor + : StudioTheme.Values.themeTextColorDisabled + font.pixelSize: StudioTheme.Values.baseFontSize + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + text: predefinedDelegate.categoryName + width: predefinedTable.centerColumnWidth + height: StudioTheme.Values.height + } + } + } + } + } + } + + Section { + id: customSection + caption: qsTr("Custom Categories") + anchors.left: parent.left + anchors.right: parent.right + + ColumnLayout { + enabled: trackingSwitch.checked + spacing: StudioTheme.Values.sectionRowSpacing + width: parent.width - StudioTheme.Values.sectionLayoutRightPadding + + Text { + wrapMode: Text.WordWrap + color: StudioTheme.Values.themeTextColor + font.pixelSize: StudioTheme.Values.baseFontSize + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + text: qsTr("Manage your own categories") + Layout.fillWidth: true + } + + Column { + id: customTable + + property int centerColumnWidth: predefinedTable.centerColumnWidth + - StudioTheme.Values.height + - predefinedTable.rowSpacing + + Layout.fillWidth: true + spacing: predefinedTable.columnSpacing + + Row { + spacing: predefinedTable.rowSpacing + + StudioControls.CheckBox { + id: selectAllCustom + actionIndicatorVisible: false + tristate: true + checkState: insightModel.customSelectState + onClicked: insightModel.selectAllCustom() + } + + Text { + color: trackingSwitch.checked ? StudioTheme.Values.themeTextColor + : StudioTheme.Values.themeTextColorDisabled + font { + pixelSize: StudioTheme.Values.baseFontSize + bold: true + } + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + text: qsTr("Select all") + width: predefinedTable.centerColumnWidth + height: StudioTheme.Values.height + } + } + + Item { width: 1; height: 4} + + Repeater { + id: customRepeater + model: insightModel + + delegate: Row { + id: customDelegate + + required property int index + + required property string categoryName + required property string categoryColor + required property string categoryType + required property bool categoryActive + + visible: customDelegate.categoryType === "custom" + spacing: predefinedTable.rowSpacing + + StudioControls.CheckBox { + id: customCheckBox + actionIndicatorVisible: false + checked: customDelegate.categoryActive + onToggled: insightModel.setCategoryActive(customDelegate.index, + customCheckBox.checked) + } + + StudioControls.TextField { + id: categoryName + + property string previousName + + actionIndicatorVisible: false + translationIndicatorVisible: false + text: customDelegate.categoryName + width: customTable.centerColumnWidth + height: StudioTheme.Values.height + onEditingFinished: { + if (categoryName.text === categoryName.previousName) + return + + var result = insightModel.renameCategory(customDelegate.index, + categoryName.text) + + if (!result) { + renameDialog.name = categoryName.text + categoryName.text = categoryName.previousName + renameDialog.open() + } + } + + Component.onCompleted: categoryName.previousName = categoryName.text + } + + StudioControls.AbstractButton { + id: removeButton + buttonIcon: StudioTheme.Constants.closeCross + onClicked: insightModel.removeCateogry(customDelegate.index) + } + } + } + + Item { width: 1; height: 4} + + Row { + spacing: StudioTheme.Values.checkBoxSpacing + + StudioControls.AbstractButton { + id: plusButton + buttonIcon: StudioTheme.Constants.plus + onClicked: insightModel.addCategory() + } + + Text { + color: trackingSwitch.checked ? StudioTheme.Values.themeTextColor + : StudioTheme.Values.themeTextColorDisabled + font.pixelSize: StudioTheme.Values.baseFontSize + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + text: qsTr("Add new Category") + width: predefinedTable.centerColumnWidth + height: StudioTheme.Values.height + } + } + } + } + } + } + } +} diff --git a/src/plugins/CMakeLists.txt b/src/plugins/CMakeLists.txt index 50b1f69ba71..950d1d8e95e 100644 --- a/src/plugins/CMakeLists.txt +++ b/src/plugins/CMakeLists.txt @@ -98,6 +98,7 @@ if (WIN32 AND CMAKE_CXX_COMPILER_ID MATCHES "Clang") endif() add_subdirectory(qmldesigner ${qmldesigner_builddir}) add_subdirectory(studiowelcome) +add_subdirectory(insight) add_subdirectory(qnx) add_subdirectory(webassembly) add_subdirectory(mcusupport) diff --git a/src/plugins/insight/CMakeLists.txt b/src/plugins/insight/CMakeLists.txt new file mode 100644 index 00000000000..e8a62940e7c --- /dev/null +++ b/src/plugins/insight/CMakeLists.txt @@ -0,0 +1,13 @@ +add_qtc_plugin(Insight + PLUGIN_DEPENDS + QtCreator::Core QtCreator::QtSupport QtCreator::QmlDesigner QtCreator::QmlProjectManager QtCreator::ProjectExplorer + DEPENDS + QtCreator::Utils + Qt6::Core Qt6::CorePrivate Qt6::Widgets + Qt6::Qml Qt6::QmlPrivate Qt6::Quick Qt6::QuickWidgets + SOURCES + insightplugin.cpp insightplugin.h + insightmodel.cpp insightmodel.h + insightview.cpp insightview.h + insightwidget.cpp insightwidget.h +) diff --git a/src/plugins/insight/Insight.json.in b/src/plugins/insight/Insight.json.in new file mode 100644 index 00000000000..c333a0aab2e --- /dev/null +++ b/src/plugins/insight/Insight.json.in @@ -0,0 +1,22 @@ +{ + \"Name\" : \"Insight\", + \"Version\" : \"$$QTCREATOR_VERSION\", + \"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\", + \"Revision\" : \"$$QTC_PLUGIN_REVISION\", + \"Experimental\" : false, + \"Vendor\" : \"The Qt Company Ltd\", + \"Copyright\" : \"(C) $$QTCREATOR_COPYRIGHT_YEAR The Qt Company Ltd\", + \"License\" : [ \"Commercial Usage\", + \"\", + \"Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt Commercial License Agreement provided with the Software or, alternatively, in accordance with the terms contained in a written agreement between you and The Qt Company.\", + \"\", + \"GNU General Public License Usage\", + \"\", + \"Alternatively, this plugin may be used under the terms of the GNU General Public License version 3 as published by the Free Software Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT included in the packaging of this plugin. Please review the following information to ensure the GNU General Public License requirements will be met: https://www.gnu.org/licenses/gpl-3.0.html.\" + ], + \"Category\" : \"Qt Quick\", + \"Description\" : \"Qt Insight Support for Design Studio.\", + \"DisabledByDefault\" : true, + \"Url\" : \"http://www.qt.io\", + $$dependencyList +} diff --git a/src/plugins/insight/insightmodel.cpp b/src/plugins/insight/insightmodel.cpp new file mode 100644 index 00000000000..64cb8f90a1a --- /dev/null +++ b/src/plugins/insight/insightmodel.cpp @@ -0,0 +1,853 @@ +// 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 "insightmodel.h" +#include "insightview.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include +#include + +namespace QmlDesigner { + +namespace { + +constexpr QStringView insightConfFile{u"qtinsight.conf"}; +constexpr QStringView qtdsConfFile{u"qtdsinsight.conf"}; +constexpr QStringView dataFolder{u"qtinsight"}; + +constexpr QStringView insightImport{u"QtInsightTracker"}; +constexpr QStringView signalHandler{u"Component.onCompleted"}; +constexpr QStringView regExp{u"InsightTracker\\.enabled\\s*=\\s*(true|false)"}; + +constexpr std::string_view defaultColor{"#000000"}; +constexpr std::string_view predefinedStr{"predefined"}; +constexpr std::string_view customStr{"custom"}; + +constexpr QStringView defaultCategoryName{u"New Category"}; + +// JSON attribut names +constexpr std::string_view categoriesAtt{"categories"}; +constexpr std::string_view tokenAtt{"token"}; +constexpr std::string_view syncAtt{"sync"}; +constexpr std::string_view intervalAtt{"interval"}; +constexpr std::string_view secondsAtt{"seconds"}; +constexpr std::string_view minutesAtt{"minutes"}; + +constexpr std::string_view nameAtt{"name"}; +constexpr std::string_view typeAtt{"type"}; +constexpr std::string_view colorAtt{"color"}; + +QByteArray fileToByteArray(const QString &filePath) +{ + QFile file(filePath); + + if (!file.exists()) { + qWarning() << "File does not exist" << filePath; + return {}; + } + + if (!file.open(QIODevice::ReadOnly)) { + qWarning() << "Could not open" << filePath << file.error() << file.errorString(); + return {}; + } + + return file.readAll(); +} + +QString fileToString(const QString &filePath) +{ + return QString::fromUtf8(fileToByteArray(filePath)); +} + +bool isNodeEnabled(const ModelNode &node) +{ + SignalHandlerProperty property = node.signalHandlerProperty(signalHandler.toUtf8()); + + QString src = property.source(); + const QRegularExpression re(regExp.toString()); + QRegularExpressionMatch match = re.match(src); + + if (match.hasMatch() && !match.capturedView(1).isEmpty()) + return QVariant(match.captured(1)).toBool(); + + return false; +} + +void setNodeEnabled(const ModelNode &node, bool value) +{ + const QString valueAsStr = QVariant(value).toString(); + + if (node.hasSignalHandlerProperty(signalHandler.toUtf8())) { + SignalHandlerProperty property = node.signalHandlerProperty(signalHandler.toUtf8()); + + QString src = property.source(); + const QRegularExpression re(regExp.toString()); + QRegularExpressionMatch match = re.match(src); + + if (match.hasMatch() && !match.capturedView(1).isEmpty()) + src.replace(match.capturedStart(1), match.capturedLength(1), valueAsStr); + + property.setSource(src); + } else { + SignalHandlerProperty property = node.signalHandlerProperty(signalHandler.toUtf8()); + property.setSource("InsightTracker.enabled = " + valueAsStr); + } +} + +json readJSON(const QString &filePath) +{ + const QByteArray data = fileToByteArray(filePath); + if (data.isEmpty()) { + qWarning() << "File is empty" << filePath; + return {}; + } + + json document; + + try { + document = json::parse(data.data()); + } catch (json::parse_error &e) { + qWarning() << "JSON parse error" << e.what(); + return {}; + } + + return document; +} + +bool writeJSON(const QString &filePath, const json &document) +{ + QFile file(filePath); + + if (!file.open(QIODevice::WriteOnly)) { + qWarning() << "Could not open file" << filePath << file.error() << file.errorString(); + return false; + } + + auto result = file.write(document.dump(4).c_str()); + + if (result == -1) + qWarning() << "Could not write file" << filePath << file.error() << file.errorString(); + + file.close(); + + return true; +} + +json createCategory(std::string_view name, + std::string_view type = customStr, + std::string_view color = defaultColor) +{ + return json::object({{nameAtt, name}, {typeAtt, type}, {colorAtt, color}}); +} + +// Checks if a is fully, partially or not at all contained in b. +Qt::CheckState checkState(const std::vector &a, const std::vector &b) +{ + unsigned count = 0; + std::for_each(a.begin(), a.end(), [&](const std::string &s) { + if (std::find(b.begin(), b.end(), s) != b.end()) + ++count; + }); + + if (count == 0) + return Qt::Unchecked; + else if (count == a.size()) + return Qt::Checked; + + return Qt::PartiallyChecked; +} + +struct ModelBuilder +{ + ModelBuilder(const QString &filePath, ExternalDependenciesInterface &externalDependencies) + { + const QString fileContent = fileToString(filePath); + if (fileContent.isEmpty()) { + qWarning() << "File is empty" << filePath; + return; + } + + document = std::make_unique(fileContent); + modifier = std::make_unique(document.get(), + QTextCursor{document.get()}); + + rewriter = std::make_unique(externalDependencies, RewriterView::Amend); + rewriter->setCheckSemanticErrors(false); + rewriter->setCheckLinkErrors(false); + rewriter->setTextModifier(modifier.get()); + + model = QmlDesigner::Model::create("QtQuick.Item", 2, 1); + model->setRewriterView(rewriter.get()); + } + + std::unique_ptr document; + std::unique_ptr modifier; + std::unique_ptr rewriter; + ModelPointer model; +}; + +} // namespace + +InsightModel::InsightModel(InsightView *view, ExternalDependenciesInterface &externalDependencies) + : m_insightView(view) + , m_externalDependencies(externalDependencies) + , m_fileSystemWatcher(new Utils::FileSystemWatcher(this)) +{ + QObject::connect(ProjectExplorer::SessionManager::instance(), + &ProjectExplorer::SessionManager::startupProjectChanged, + this, + [&](ProjectExplorer::Project *project) { + if (project) + m_initialized = false; + }); + + QObject::connect(m_fileSystemWatcher, + &Utils::FileSystemWatcher::fileChanged, + this, + &InsightModel::handleFileChange); +} + +int InsightModel::rowCount(const QModelIndex &) const +{ + return m_qtdsConfig.empty() ? 0 : m_qtdsConfig.size(); +} + +QVariant InsightModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid() || index.row() >= rowCount() || m_qtdsConfig.empty()) + return QVariant(); + + json::json_pointer ptr; + ptr.push_back(std::to_string(index.row())); + + if (!m_qtdsConfig.contains(ptr)) + return QVariant(); + + auto element = m_qtdsConfig[ptr]; + + switch (role) { + case CategoryName: + return QString::fromStdString(element.value(nameAtt, "")); + case CategoryColor: + return QString::fromStdString(element.value(colorAtt, "")); + case CategoryType: + return QString::fromStdString(element.value(typeAtt, "")); + case CategoryActive: { + auto categories = activeCategories(); + auto category = element.value(nameAtt, ""); + return std::find(std::begin(categories), std::end(categories), category) + != std::end(categories); + } + default: + return QVariant(); + } +} + +QHash InsightModel::roleNames() const +{ + static QHash roleNames{{CategoryName, "categoryName"}, + {CategoryColor, "categoryColor"}, + {CategoryType, "categoryType"}, + {CategoryActive, "categoryActive"}}; + return roleNames; +} + +void InsightModel::setup() +{ + if (m_initialized) + return; + + const QString projectUrl = m_externalDependencies.projectUrl().toLocalFile(); + + m_mainQmlInfo = QFileInfo(projectUrl + "/main.qml"); + m_configInfo = QFileInfo(projectUrl + "/" + insightConfFile); + m_qtdsConfigInfo = QFileInfo(projectUrl + "/" + qtdsConfFile); + + parseMainQml(); + parseDefaultConfig(); + parseConfig(); + parseQtdsConfig(); + + beginResetModel(); + + if (m_qtdsConfig.empty()) + createQtdsConfig(); + else + updateQtdsConfig(); + + endResetModel(); + + updateCheckState(); + + if (m_enabled) { + // Flush config files and start listening to modifications + writeJSON(m_configInfo.absoluteFilePath(), m_config); + writeJSON(m_qtdsConfigInfo.absoluteFilePath(), m_qtdsConfig); + } + + m_fileSystemWatcher->addFile(m_mainQmlInfo.absoluteFilePath(), + Utils::FileSystemWatcher::WatchModifiedDate); + m_fileSystemWatcher->addFile(m_configInfo.absoluteFilePath(), + Utils::FileSystemWatcher::WatchModifiedDate); + m_fileSystemWatcher->addFile(m_qtdsConfigInfo.absoluteFilePath(), + Utils::FileSystemWatcher::WatchModifiedDate); + + m_initialized = true; +} + +void InsightModel::addCategory() +{ + int counter = 0; + QString newCategory = defaultCategoryName.toString(); + + while (hasCategory(newCategory)) { + ++counter; + newCategory = QString(QStringLiteral("%1%2")).arg(defaultCategoryName).arg(counter); + } + + json tmp = m_qtdsConfig; + tmp.push_back(createCategory(newCategory.toStdString())); + writeJSON(m_qtdsConfigInfo.absoluteFilePath(), tmp); +} + +void InsightModel::removeCateogry(int idx) +{ + json::json_pointer ptr; + ptr.push_back(std::to_string(idx)); + ptr.push_back(std::string(nameAtt)); + + // Check if category is active and remove it there as well + auto active = activeCategories(); + auto category = m_qtdsConfig.contains(ptr) ? m_qtdsConfig[ptr].get() : ""; + auto it = std::find(std::begin(active), std::end(active), category); + if (it != std::end(active)) { + active.erase(it); + + json tmp = m_config; + tmp[categoriesAtt] = active; + writeJSON(m_configInfo.absoluteFilePath(), tmp); + } + + json tmp = m_qtdsConfig; + tmp.erase(idx); + writeJSON(m_qtdsConfigInfo.absoluteFilePath(), tmp); +} + +bool InsightModel::renameCategory(int idx, const QString &name) +{ + if (hasCategory(name)) + return false; + + json::json_pointer ptr; + ptr.push_back(std::to_string(idx)); + ptr.push_back(std::string(nameAtt)); + + // Check if category is active and rename it there as well + auto active = activeCategories(); + auto category = m_qtdsConfig.contains(ptr) ? m_qtdsConfig[ptr].get() : ""; + auto it = std::find(std::begin(active), std::end(active), category); + if (it != std::end(active)) { + *it = name.toStdString(); + + json tmp = m_config; + tmp[categoriesAtt] = active; + writeJSON(m_configInfo.absoluteFilePath(), tmp); + } + + json tmp = m_qtdsConfig; + tmp[ptr] = name.toStdString(); + writeJSON(m_qtdsConfigInfo.absoluteFilePath(), tmp); + return true; +} + +void InsightModel::setCategoryActive(int idx, bool value) +{ + json::json_pointer ptr; + ptr.push_back(std::to_string(idx)); + ptr.push_back(std::string(nameAtt)); + + auto categoryName = m_qtdsConfig.contains(ptr) ? m_qtdsConfig[ptr].get() : ""; + auto categories = activeCategories(); + + if (value) { // active = true + if (std::find(std::begin(categories), std::end(categories), categoryName) + == std::end(categories)) + categories.push_back(categoryName); + } else { // active = false + categories.erase(std::remove(categories.begin(), categories.end(), categoryName), + categories.end()); + } + + json tmp = m_config; + tmp[categoriesAtt] = categories; + writeJSON(m_configInfo.absoluteFilePath(), tmp); +} + +bool InsightModel::enabled() const +{ + return m_enabled; +} + +void InsightModel::setEnabled(bool value) +{ + if (!m_mainQmlInfo.exists()) { + qWarning() << "File does not exist" << m_mainQmlInfo.absoluteFilePath(); + return; + } + + ModelBuilder builder(m_mainQmlInfo.absoluteFilePath(), m_externalDependencies); + + if (!builder.model) { + qWarning() << "Could not create model" << m_mainQmlInfo.absoluteFilePath(); + return; + } + + // Add import if it does not exist yet + Import import = Import::createLibraryImport(insightImport.toString(), "1.0"); + if (!builder.model->hasImport(import, true, true) && value) { + builder.model->changeImports({import}, {}); + } + + bool insightEnabled = isNodeEnabled(builder.rewriter->rootModelNode()); + if (insightEnabled == value) + return; + + setNodeEnabled(builder.rewriter->rootModelNode(), value); + + QFile file(m_mainQmlInfo.absoluteFilePath()); + + if (!file.open(QIODevice::WriteOnly)) { + qWarning() << "Could not open" << m_mainQmlInfo.absoluteFilePath() << file.error() + << file.errorString(); + return; + } + + auto result = file.write(builder.rewriter->textModifierContent().toUtf8()); + + if (result == -1) + qWarning() << "Could not write file" << m_mainQmlInfo.absoluteFilePath() << file.error() + << file.errorString(); + + // If enabled and config files do not exist yet, write them + if (value) { + if (!m_configInfo.exists()) + writeJSON(m_configInfo.absoluteFilePath(), m_config); + if (!m_qtdsConfigInfo.exists()) + writeJSON(m_qtdsConfigInfo.absoluteFilePath(), m_qtdsConfig); + } +} + +QString InsightModel::token() const +{ + if (m_config.empty()) + return {}; + + return QString::fromStdString(m_config.value(tokenAtt, "")); +} + +void InsightModel::setToken(const QString &value) +{ + writeConfigValue(json::json_pointer("/" + std::string(tokenAtt)), value.toStdString()); +} + +int InsightModel::minutes() const +{ + if (m_config.empty()) + return {}; + + json::json_pointer ptr; + ptr.push_back(std::string(syncAtt)); + ptr.push_back(std::string(intervalAtt)); + ptr.push_back(std::string(minutesAtt)); + + return m_config.value(ptr, 0); +} + +void InsightModel::setMinutes(int value) +{ + json::json_pointer ptr; + ptr.push_back(std::string(syncAtt)); + ptr.push_back(std::string(intervalAtt)); + ptr.push_back(std::string(minutesAtt)); + + writeConfigValue(ptr, value); +} + +void InsightModel::selectAllPredefined() +{ + selectAll(predefinedCategories(), m_predefinedCheckState); +} + +void InsightModel::selectAllCustom() +{ + selectAll(customCategories(), m_customCheckState); +} + +void InsightModel::handleFileChange(const QString &path) +{ + if (m_mainQmlInfo.absoluteFilePath() == path) + parseMainQml(); + else if (m_configInfo.absoluteFilePath() == path) + parseConfig(); + else if (m_qtdsConfigInfo.absoluteFilePath() == path) { + beginResetModel(); + parseQtdsConfig(); + endResetModel(); + } +} + +void InsightModel::setAuxiliaryEnabled(bool value) +{ + ModelNode root = m_insightView->rootModelNode(); + if (root.isValid()) + root.setAuxiliaryData(insightEnabledProperty, value); +} + +void InsightModel::setAuxiliaryCategories(const std::vector &categories) +{ + ModelNode root = m_insightView->rootModelNode(); + if (root.isValid()) { + QStringList c; + std::for_each(categories.begin(), categories.end(), [&](const std::string &s) { + c.append(QString::fromStdString(s)); + }); + + root.setAuxiliaryData(insightCategoriesProperty, c); + } +} + +void InsightModel::hideCursor() +{ + if (QApplication::overrideCursor()) + return; + + QApplication::setOverrideCursor(QCursor(Qt::BlankCursor)); + + if (QWidget *w = QApplication::activeWindow()) + m_lastPos = QCursor::pos(w->screen()); +} + +void InsightModel::restoreCursor() +{ + if (!QApplication::overrideCursor()) + return; + + QApplication::restoreOverrideCursor(); + + if (QWidget *w = QApplication::activeWindow()) + QCursor::setPos(w->screen(), m_lastPos); +} + +void InsightModel::holdCursorInPlace() +{ + if (!QApplication::overrideCursor()) + return; + + if (QWidget *w = QApplication::activeWindow()) + QCursor::setPos(w->screen(), m_lastPos); +} + +int InsightModel::devicePixelRatio() +{ + if (QWidget *w = QApplication::activeWindow()) + return w->devicePixelRatio(); + + return 1; +} + +void InsightModel::parseMainQml() +{ + ModelBuilder builder(m_mainQmlInfo.absoluteFilePath(), m_externalDependencies); + + if (!builder.model) + return; + + Import import = Import::createLibraryImport(insightImport.toString(), "1.0"); + if (!builder.model->hasImport(import, true, true)) + return; + + bool insightEnabled = isNodeEnabled(builder.rewriter->rootModelNode()); + + if (m_enabled != insightEnabled) { + m_enabled = insightEnabled; + emit enabledChanged(); + + setAuxiliaryEnabled(m_enabled); + } +} + +void InsightModel::parseDefaultConfig() +{ + // Load default insight config from plugin + const ProjectExplorer::Target *target = ProjectExplorer::ProjectTree::currentTarget(); + if (target) { + const QtSupport::QtVersion *qtVersion = QtSupport::QtKitAspect::qtVersion(target->kit()); + m_defaultConfig = readJSON(qtVersion->dataPath().toString() + "/" + dataFolder + "/" + + insightConfFile); + } +} + +void InsightModel::parseConfig() +{ + json target = readJSON(m_configInfo.absoluteFilePath()); + + if (target.empty()) { + if (m_defaultConfig.empty()) { + qWarning() << "Could not find default or user insight config."; + return; + } + // Copy default config + m_config = m_defaultConfig; + + // Try to overwrite seconds entry in config as it is set in the default config to 60, + // but currently not supported in QtDS. + json::json_pointer ptr; + ptr.push_back(std::string(syncAtt)); + ptr.push_back(std::string(intervalAtt)); + + if (m_config.contains(ptr)) + m_config[ptr][secondsAtt] = 0; + } else { + bool resetModel = false; + + if (m_config.empty()) { + m_config = target; + + emit tokenChanged(); + emit minutesChanged(); + resetModel = true; + } else { + json patch = json::diff(m_config, target); + + m_config = target; + for (auto it : patch) { + if (!it.contains("path")) + continue; + + json::json_pointer tmp(it["path"].get()); + + if (tmp.back() == tokenAtt) + emit tokenChanged(); + + if (tmp.back() == minutesAtt) + emit minutesChanged(); + + if (!tmp.to_string().compare(1, categoriesAtt.size(), categoriesAtt)) + resetModel = true; + } + } + + if (resetModel) { + updateCheckState(); + beginResetModel(); + endResetModel(); + } + } +} + +void InsightModel::parseQtdsConfig() +{ + m_qtdsConfig = readJSON(m_qtdsConfigInfo.absoluteFilePath()); + updateCheckState(); + setAuxiliaryCategories(customCategories()); +} + +// Create new QtDS Insight configuration +void InsightModel::createQtdsConfig() +{ + json categories = json::array(); + + auto active = activeCategories(); + auto predefined = predefinedCategories(); + + std::vector custom; + + std::set_difference(std::make_move_iterator(active.begin()), + std::make_move_iterator(active.end()), + std::make_move_iterator(predefined.begin()), + std::make_move_iterator(predefined.end()), + std::back_inserter(custom)); + + for (const auto &c : predefined) + categories.push_back(createCategory(c, predefinedStr)); + + for (const auto &c : custom) + categories.push_back(createCategory(c)); + + m_qtdsConfig = categories; +} + +// Update existing QtDS Insight configuration +void InsightModel::updateQtdsConfig() +{ + auto contains = [&](const json &arr, const std::string &val) { + for (auto it : arr) { + if (val == it[nameAtt].get()) + return true; + } + return false; + }; + + auto active = activeCategories(); + auto predefined = predefinedCategories(); + std::vector custom; + + std::set_difference(std::make_move_iterator(active.begin()), + std::make_move_iterator(active.end()), + std::make_move_iterator(predefined.begin()), + std::make_move_iterator(predefined.end()), + std::back_inserter(custom)); + + for (const auto &c : predefined) { + if (!contains(m_qtdsConfig, c)) + m_qtdsConfig.push_back(createCategory(c, predefinedStr)); + } + + for (const auto &c : custom) { + if (!contains(m_qtdsConfig, c)) + m_qtdsConfig.push_back(createCategory(c)); + } +} + +void InsightModel::selectAll(const std::vector &categories, Qt::CheckState checkState) +{ + auto active = activeCategories(); + + if (checkState == Qt::Unchecked || checkState == Qt::PartiallyChecked) { + // Select all + std::for_each(categories.begin(), categories.end(), [&](const std::string &s) { + if (std::find(active.begin(), active.end(), s) == active.end()) + active.push_back(s); + }); + } else { + // Unselect all + std::vector diff; + + std::set_difference(active.begin(), + active.end(), + categories.begin(), + categories.end(), + std::inserter(diff, diff.begin())); + active = diff; + } + + json tmp = m_config; + tmp[categoriesAtt] = active; + writeJSON(m_configInfo.absoluteFilePath(), tmp); +} + +std::vector InsightModel::predefinedCategories() const +{ + std::vector categories; + if (!m_defaultConfig.empty() && m_defaultConfig.contains(categoriesAtt)) + categories = m_defaultConfig[categoriesAtt].get>(); + + std::sort(categories.begin(), categories.end()); + categories.erase(std::unique(categories.begin(), categories.end()), categories.end()); + + return categories; +} + +std::vector InsightModel::activeCategories() const +{ + std::vector categories; + if (!m_config.empty() && m_config.contains(categoriesAtt)) + categories = m_config[categoriesAtt].get>(); + + std::sort(categories.begin(), categories.end()); + categories.erase(std::unique(categories.begin(), categories.end()), categories.end()); + + return categories; +} + +std::vector InsightModel::customCategories() const +{ + std::vector categories; + if (!m_qtdsConfig.empty()) { + for (auto it : m_qtdsConfig) { + if (it.contains(typeAtt) && it.contains(nameAtt) + && it[typeAtt].get() == customStr) + categories.push_back(it[nameAtt].get()); + } + } + + std::sort(categories.begin(), categories.end()); + categories.erase(std::unique(categories.begin(), categories.end()), categories.end()); + + return categories; +} + +std::vector InsightModel::categories() const +{ + std::vector categories; + if (!m_qtdsConfig.empty()) { + for (auto it : m_qtdsConfig) { + if (it.contains(nameAtt)) + categories.push_back(it[nameAtt].get()); + } + } + + return categories; +} + +bool InsightModel::hasCategory(const QString &name) const +{ + auto c = categories(); + return std::find(std::begin(c), std::end(c), name.toStdString()) != std::end(c); +} + +void InsightModel::updateCheckState() +{ + auto active = activeCategories(); + auto predefined = predefinedCategories(); + auto custom = customCategories(); + + Qt::CheckState predefinedCheckState = checkState(predefined, active); + Qt::CheckState customCheckState = checkState(custom, active); + + if (m_predefinedCheckState != predefinedCheckState) { + m_predefinedCheckState = predefinedCheckState; + emit predefinedSelectStateChanged(); + } + + if (m_customCheckState != customCheckState) { + m_customCheckState = customCheckState; + emit customSelectStateChanged(); + } +} + +template +void InsightModel::writeConfigValue(const json::json_pointer &ptr, T value) +{ + T configValue{}; + + if (!m_config.empty()) + configValue = m_config.value(ptr, configValue); + + if (configValue == value) + return; + + json tmp = m_config; + tmp[ptr] = value; + + writeJSON(m_configInfo.absoluteFilePath(), tmp); +} + +} // namespace QmlDesigner diff --git a/src/plugins/insight/insightmodel.h b/src/plugins/insight/insightmodel.h new file mode 100644 index 00000000000..b21d7e36205 --- /dev/null +++ b/src/plugins/insight/insightmodel.h @@ -0,0 +1,132 @@ +// 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 + +#pragma once + +#include + +#include +#include +#include +#include + +#include + +#include <3rdparty/json/json.hpp> + +namespace QmlDesigner { + +class InsightView; + +typedef nlohmann::json json; + +class InsightModel : public QAbstractListModel +{ + Q_OBJECT + + Q_PROPERTY(bool enabled READ enabled NOTIFY enabledChanged) + Q_PROPERTY(QString token READ token NOTIFY tokenChanged) + Q_PROPERTY(int minutes READ minutes NOTIFY minutesChanged) + Q_PROPERTY(Qt::CheckState predefinedSelectState MEMBER m_predefinedCheckState NOTIFY + predefinedSelectStateChanged) + Q_PROPERTY(Qt::CheckState customSelectState MEMBER m_customCheckState NOTIFY customSelectStateChanged) + + enum { + CategoryName = Qt::DisplayRole, + CategoryColor = Qt::UserRole, + CategoryType, + CategoryActive + }; + +public: + InsightModel(InsightView *view, class ExternalDependenciesInterface &externalDependencies); + + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + QHash roleNames() const override; + + void setup(); + + Q_INVOKABLE void addCategory(); + Q_INVOKABLE void removeCateogry(int idx); + Q_INVOKABLE bool renameCategory(int idx, const QString &name); + + Q_INVOKABLE void setCategoryActive(int idx, bool value); + + bool enabled() const; + Q_INVOKABLE void setEnabled(bool value); + + QString token() const; + Q_INVOKABLE void setToken(const QString &value); + + int minutes() const; + Q_INVOKABLE void setMinutes(int value); + + Q_INVOKABLE void selectAllPredefined(); + Q_INVOKABLE void selectAllCustom(); + + void handleFileChange(const QString &path); + + void setAuxiliaryEnabled(bool value); + void setAuxiliaryCategories(const std::vector &categories); + + Q_INVOKABLE void hideCursor(); + Q_INVOKABLE void restoreCursor(); + Q_INVOKABLE void holdCursorInPlace(); + + Q_INVOKABLE int devicePixelRatio(); + +signals: + void enabledChanged(); + void tokenChanged(); + void minutesChanged(); + + void predefinedSelectStateChanged(); + void customSelectStateChanged(); + +private: + void parseMainQml(); + void parseDefaultConfig(); + void parseConfig(); + void parseQtdsConfig(); + + void createQtdsConfig(); + void updateQtdsConfig(); + + void selectAll(const std::vector &categories, Qt::CheckState checkState); + + std::vector predefinedCategories() const; + std::vector activeCategories() const; + std::vector customCategories() const; + std::vector categories() const; + + bool hasCategory(const QString &name) const; + void updateCheckState(); + + template + void writeConfigValue(const json::json_pointer &ptr, T value); + +private: + QPointer m_insightView; + ExternalDependenciesInterface &m_externalDependencies; + + Utils::FileSystemWatcher *m_fileSystemWatcher; + + bool m_enabled = false; + bool m_initialized = false; + + QFileInfo m_mainQmlInfo; + QFileInfo m_configInfo; + QFileInfo m_qtdsConfigInfo; + + json m_defaultConfig; + json m_config; + json m_qtdsConfig; + + Qt::CheckState m_predefinedCheckState; + Qt::CheckState m_customCheckState; + + QPoint m_lastPos; +}; + +} // namespace QmlDesigner diff --git a/src/plugins/insight/insightplugin.cpp b/src/plugins/insight/insightplugin.cpp new file mode 100644 index 00000000000..4ad151e80da --- /dev/null +++ b/src/plugins/insight/insightplugin.cpp @@ -0,0 +1,31 @@ +// 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 "insightplugin.h" + +#include "insightview.h" + +#include +#include + +namespace QmlDesigner { + +InsightPlugin::InsightPlugin() = default; +InsightPlugin::~InsightPlugin() = default; + +bool InsightPlugin::initialize(const QStringList & /*arguments*/, QString * /*errorMessage*/) +{ + return true; +} + +bool InsightPlugin::delayedInitialize() +{ + auto *designerPlugin = QmlDesignerPlugin::instance(); + auto &viewManager = designerPlugin->viewManager(); + viewManager.registerView(std::make_unique( + QmlDesignerPlugin::externalDependenciesForPluginInitializationOnly())); + + return true; +} + +} // namespace QmlDesigner diff --git a/src/plugins/insight/insightplugin.h b/src/plugins/insight/insightplugin.h new file mode 100644 index 00000000000..c090e09bb76 --- /dev/null +++ b/src/plugins/insight/insightplugin.h @@ -0,0 +1,22 @@ +// 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 + +#pragma once + +#include + +namespace QmlDesigner { + +class InsightPlugin : public ExtensionSystem::IPlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "Insight.json") +public: + InsightPlugin(); + ~InsightPlugin(); + + bool initialize(const QStringList &arguments, QString *errorMessage); + bool delayedInitialize(); +}; + +} // namespace QmlDesigner diff --git a/src/plugins/insight/insightview.cpp b/src/plugins/insight/insightview.cpp new file mode 100644 index 00000000000..a06cb5e8736 --- /dev/null +++ b/src/plugins/insight/insightview.cpp @@ -0,0 +1,57 @@ +// 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 "insightview.h" +#include "insightmodel.h" +#include "insightwidget.h" + +#include + +#include +#include +#include +#include + +namespace QmlDesigner { + +InsightView::InsightView(ExternalDependenciesInterface &externalDependencies) + : AbstractView(externalDependencies) + , m_insightModel(std::make_unique(this, externalDependencies)) +{ + Q_ASSERT(m_insightModel); +} + +InsightView::~InsightView() +{ + delete m_insightWidget.data(); +} + +void InsightView::modelAttached(Model *model) +{ + if (model == AbstractView::model()) + return; + + QTC_ASSERT(model, return ); + AbstractView::modelAttached(model); + + m_insightModel->setup(); +} + +WidgetInfo InsightView::widgetInfo() +{ + if (!m_insightWidget) + m_insightWidget = new InsightWidget(this, m_insightModel.get()); + + return createWidgetInfo(m_insightWidget.data(), + "QtInsight", + WidgetInfo::RightPane, + 0, + tr("Qt Insight")); +} + +bool InsightView::hasWidget() const +{ + return true; +} + +} // namespace QmlDesigner diff --git a/src/plugins/insight/insightview.h b/src/plugins/insight/insightview.h new file mode 100644 index 00000000000..e39f34239c2 --- /dev/null +++ b/src/plugins/insight/insightview.h @@ -0,0 +1,40 @@ +// 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 + +#pragma once + +#include + +#include +#include +#include + +#include <3rdparty/json/json.hpp> + +namespace QmlDesigner { + +class InsightModel; +class InsightWidget; + +class InsightView : public AbstractView +{ + Q_OBJECT + +public: + explicit InsightView(ExternalDependenciesInterface &externalDependencies); + ~InsightView() override; + + // AbstractView + void modelAttached(Model *model) override; + + WidgetInfo widgetInfo() override; + bool hasWidget() const override; + +public slots: + +private: + std::unique_ptr m_insightModel; + QPointer m_insightWidget; +}; + +} // namespace QmlDesigner diff --git a/src/plugins/insight/insightwidget.cpp b/src/plugins/insight/insightwidget.cpp new file mode 100644 index 00000000000..81286f87eb2 --- /dev/null +++ b/src/plugins/insight/insightwidget.cpp @@ -0,0 +1,123 @@ +// 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 "insightwidget.h" +#include "insightmodel.h" +#include "insightview.h" + +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +enum { + debug = false +}; + +namespace QmlDesigner { + +static QString propertyEditorResourcesPath() +{ +#ifdef SHARE_QML_PATH + if (qEnvironmentVariableIsSet("LOAD_QML_FROM_SOURCE")) + return QLatin1String(SHARE_QML_PATH) + "/propertyEditorQmlSources"; +#endif + return Core::ICore::resourcePath("qmldesigner/propertyEditorQmlSources").toString(); +} + +InsightWidget::InsightWidget(InsightView *insightView, InsightModel *insightModel) + : m_insightView(insightView) + , m_qmlSourceUpdateShortcut(nullptr) +{ + engine()->addImportPath(qmlSourcesPath()); + engine()->addImportPath(propertyEditorResourcesPath() + "/imports"); + engine()->addImportPath(qmlSourcesPath() + "/imports"); + + m_qmlSourceUpdateShortcut = new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_F11), this); + connect(m_qmlSourceUpdateShortcut, + &QShortcut::activated, + this, + &InsightWidget::reloadQmlSource); + + setResizeMode(QQuickWidget::SizeRootObjectToView); + setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + + rootContext()->setContextProperties({{"insightModel", QVariant::fromValue(insightModel)}}); + + Theme::setupTheme(engine()); + + setWindowTitle(tr("Qt Insight", "Title of the widget")); + setMinimumWidth(195); + setMinimumHeight(195); + + // init the first load of the QML UI elements + reloadQmlSource(); +} + +InsightWidget::~InsightWidget() = default; + +QString InsightWidget::qmlSourcesPath() +{ +#ifdef SHARE_QML_PATH + if (qEnvironmentVariableIsSet("LOAD_QML_FROM_SOURCE")) + return QLatin1String(SHARE_QML_PATH) + "/insight"; +#endif + return Core::ICore::resourcePath("qmldesigner/insight").toString(); +} + +void InsightWidget::showEvent(QShowEvent *event) +{ + QQuickWidget::showEvent(event); + update(); +} + +void InsightWidget::focusOutEvent(QFocusEvent *focusEvent) +{ + QmlDesignerPlugin::emitUsageStatisticsTime(Constants::EVENT_INSIGHT_TIME, m_usageTimer.elapsed()); + QQuickWidget::focusOutEvent(focusEvent); +} + +void InsightWidget::focusInEvent(QFocusEvent *focusEvent) +{ + m_usageTimer.restart(); + QQuickWidget::focusInEvent(focusEvent); +} + +void InsightWidget::reloadQmlSource() +{ + QString statesListQmlFilePath = qmlSourcesPath() + QStringLiteral("/Main.qml"); + QTC_ASSERT(QFileInfo::exists(statesListQmlFilePath), return ); + engine()->clearComponentCache(); + setSource(QUrl::fromLocalFile(statesListQmlFilePath)); + + if (!rootObject()) { + QString errorString; + for (const QQmlError &error : errors()) + errorString += "\n" + error.toString(); + + Core::AsynchronousMessageBox::warning(tr("Cannot Create QtQuick View"), + tr("InsightWidget: %1 cannot be created.%2") + .arg(qmlSourcesPath(), errorString)); + return; + } +} + +} // namespace QmlDesigner diff --git a/src/plugins/insight/insightwidget.h b/src/plugins/insight/insightwidget.h new file mode 100644 index 00000000000..aba18f81969 --- /dev/null +++ b/src/plugins/insight/insightwidget.h @@ -0,0 +1,45 @@ +// 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 + +#pragma once + +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE +class QShortcut; +QT_END_NAMESPACE + +namespace QmlDesigner { + +class NodeInstanceView; +class InsightModel; +class InsightView; + +class InsightWidget : public QQuickWidget +{ + Q_OBJECT + +public: + InsightWidget(InsightView *insightView, InsightModel *insightModel); + ~InsightWidget() override; + + static QString qmlSourcesPath(); + +protected: + void showEvent(QShowEvent *) override; + void focusOutEvent(QFocusEvent *focusEvent) override; + void focusInEvent(QFocusEvent *focusEvent) override; + +private: + void reloadQmlSource(); + +private: + QPointer m_insightView; + QShortcut *m_qmlSourceUpdateShortcut; + QElapsedTimer m_usageTimer; +}; + +} // namespace QmlDesigner From 795aae8e7c33a05a03b92a68b781b5918b63475b Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Wed, 15 Mar 2023 12:15:38 +0200 Subject: [PATCH 047/192] QmlDesigner: Enable transient ScrollBars for StudioStyle MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Transient scroll bar is available for StudioStyle. - Some changes are applied to slider style, because scrollBar and sliders have some common usages within the style. Task-number: QDS-9283 Change-Id: I7a8b7997cf4d20142a0524c4a071b93dfd06321d Reviewed-by: Henning Gründl --- src/libs/utils/CMakeLists.txt | 1 + src/libs/utils/transientscroll.cpp | 215 ++++++++++++++++++ src/libs/utils/transientscroll.h | 51 +++++ .../formeditor/formeditorgraphicsview.cpp | 2 + .../components/resources/dockwidgets.css | 34 --- .../texteditor/texteditorwidget.cpp | 5 - .../qmldesignerbase/utils/studiostyle.cpp | 90 +++++--- .../qmldesignerbase/utils/studiostyle.h | 6 + src/plugins/texteditor/texteditor.cpp | 2 + 9 files changed, 340 insertions(+), 66 deletions(-) create mode 100644 src/libs/utils/transientscroll.cpp create mode 100644 src/libs/utils/transientscroll.h diff --git a/src/libs/utils/CMakeLists.txt b/src/libs/utils/CMakeLists.txt index 8777a6a881a..4362294341c 100644 --- a/src/libs/utils/CMakeLists.txt +++ b/src/libs/utils/CMakeLists.txt @@ -183,6 +183,7 @@ add_qtc_library(Utils tooltip/tips.cpp tooltip/tips.h tooltip/tooltip.cpp tooltip/tooltip.h touchbar/touchbar.h + transientscroll.cpp transientscroll.h treemodel.cpp treemodel.h treeviewcombobox.cpp treeviewcombobox.h uncommentselection.cpp uncommentselection.h diff --git a/src/libs/utils/transientscroll.cpp b/src/libs/utils/transientscroll.cpp new file mode 100644 index 00000000000..e056eac7d0e --- /dev/null +++ b/src/libs/utils/transientscroll.cpp @@ -0,0 +1,215 @@ +// Copyright (C) 2023 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 "transientscroll.h" + +#include +#include +#include +#include +#include + +using namespace Utils; + +static constexpr char transientScrollAreaSupportName[] = "transientScrollAreSupport"; + +class Utils::ScrollAreaPrivate +{ +public: + ScrollAreaPrivate(QAbstractScrollArea *area) + : area(area) + { + verticalScrollBar = new ScrollBar(area); + area->setVerticalScrollBar(verticalScrollBar); + + horizontalScrollBar = new ScrollBar(area); + area->setHorizontalScrollBar(horizontalScrollBar); + + area->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); + area->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); + } + + inline QRect scrollBarRect(ScrollBar *scrollBar) + { + QRect rect = viewPort ? viewPort->rect() : area->rect(); + if (scrollBar->orientation() == Qt::Vertical) { + int mDiff = rect.width() - scrollBar->sizeHint().width(); + return rect.adjusted(mDiff, 0, mDiff, 0); + } else { + int mDiff = rect.height() - scrollBar->sizeHint().height(); + return rect.adjusted(0, mDiff, 0, mDiff); + } + } + + inline void checkToFlashScroll(QPointer scrollBar, const QPoint &pos) + { + if (scrollBar.isNull()) + return; + + if (!scrollBar->style()->styleHint( + QStyle::SH_ScrollBar_Transient, + nullptr, scrollBar)) + return; + + if (scrollBarRect(scrollBar).contains(pos)) + scrollBar->flash(); + } + + inline void checkToFlashScroll(const QPoint &pos) + { + checkToFlashScroll(verticalScrollBar, pos); + checkToFlashScroll(horizontalScrollBar, pos); + } + + inline void installViewPort(QObject *eventHandler) { + QWidget *viewPort = area->viewport(); + if (viewPort + && viewPort != this->viewPort + && viewPort->style()->styleHint(QStyle::SH_ScrollBar_Transient, nullptr, viewPort) + && (area->verticalScrollBarPolicy() != Qt::ScrollBarAlwaysOff + || area->horizontalScrollBarPolicy() != Qt::ScrollBarAlwaysOff)) { + viewPort->installEventFilter(eventHandler); + this->viewPort = viewPort; + } + } + + inline void uninstallViewPort(QObject *eventHandler) { + if (viewPort) { + viewPort->removeEventFilter(eventHandler); + this->viewPort = nullptr; + } + } + + QAbstractScrollArea *area = nullptr; + QPointer viewPort = nullptr; + QPointer verticalScrollBar; + QPointer horizontalScrollBar; +}; + +TransientScrollAreaSupport::TransientScrollAreaSupport(QAbstractScrollArea *scrollArea) + : QObject(scrollArea) + , d(new ScrollAreaPrivate(scrollArea)) +{ + scrollArea->installEventFilter(this); +} + +void TransientScrollAreaSupport::support(QAbstractScrollArea *scrollArea) +{ + QObject *prevSupport = scrollArea->property(transientScrollAreaSupportName) + .value(); + if (!prevSupport) + scrollArea->setProperty(transientScrollAreaSupportName, + QVariant::fromValue( + new TransientScrollAreaSupport(scrollArea)) + ); +} + +TransientScrollAreaSupport::~TransientScrollAreaSupport() +{ + delete d; +} + +bool TransientScrollAreaSupport::eventFilter(QObject *watched, QEvent *event) +{ + switch (event->type()) { + case QEvent::Enter: { + if (watched == d->area) + d->installViewPort(this); + } + break; + case QEvent::Leave: { + if (watched == d->area) + d->uninstallViewPort(this); + } + break; + case QEvent::MouseMove: { + if (watched == d->viewPort){ + QMouseEvent *mouseEvent = static_cast(event); + if (mouseEvent) { + d->checkToFlashScroll(mouseEvent->pos()); + return true; + } + } + } + break; + default: + break; + } + return QObject::eventFilter(watched, event); +} + +class Utils::ScrollBarPrivate { +public: + bool flashed = false; + int flashTimer = 0; +}; + +ScrollBar::ScrollBar(QWidget *parent) + : QScrollBar(parent) + , d(new ScrollBarPrivate) +{ +} + +ScrollBar::~ScrollBar() +{ + delete d; +} + +QSize ScrollBar::sizeHint() const +{ + QSize sh = QScrollBar::sizeHint(); + if (style()->styleHint(QStyle::SH_ScrollBar_Transient, nullptr, this)) { + constexpr int thickness = 10; + if (orientation() == Qt::Horizontal) + sh.setHeight(thickness); + else + sh.setWidth(thickness); + } else { + constexpr int thickness = 12; + if (orientation() == Qt::Horizontal) + sh.setHeight(thickness); + else + sh.setWidth(thickness); + } + return sh; +} + +void ScrollBar::flash() +{ + if (!d->flashed && style()->styleHint(QStyle::SH_ScrollBar_Transient, nullptr, this)) { + d->flashed = true; + if (!isVisible()) + show(); + else + update(); + } + if (!d->flashTimer) + d->flashTimer = startTimer(0); +} + +void ScrollBar::initStyleOption(QStyleOptionSlider *option) const +{ + QScrollBar::initStyleOption(option); + + if (d->flashed && style()->styleHint(QStyle::SH_ScrollBar_Transient, nullptr, this)) + option->state |= QStyle::State_On; +} + +bool ScrollBar::event(QEvent *event) +{ + switch (event->type()) { + case QEvent::Timer: + if (static_cast(event)->timerId() == d->flashTimer) { + if (d->flashed && style()->styleHint(QStyle::SH_ScrollBar_Transient, nullptr, this)) { + d->flashed = false; + update(); + } + killTimer(d->flashTimer); + d->flashTimer = 0; + } + break; + default: + break; + } + return QScrollBar::event(event); +} diff --git a/src/libs/utils/transientscroll.h b/src/libs/utils/transientscroll.h new file mode 100644 index 00000000000..2042bbf0fb0 --- /dev/null +++ b/src/libs/utils/transientscroll.h @@ -0,0 +1,51 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0 + +#pragma once + +#include "utils_global.h" + +#include + +class QAbstractScrollArea; + +namespace Utils { +class ScrollAreaPrivate; +class ScrollBarPrivate; + +class QTCREATOR_UTILS_EXPORT TransientScrollAreaSupport : public QObject +{ + Q_OBJECT +public: + static void support(QAbstractScrollArea *scrollArea); + virtual ~TransientScrollAreaSupport(); + +protected: + virtual bool eventFilter(QObject *watched, QEvent *event) override; + +private: + explicit TransientScrollAreaSupport(QAbstractScrollArea *scrollArea); + + ScrollAreaPrivate *d = nullptr; +}; + +class QTCREATOR_UTILS_EXPORT ScrollBar : public QScrollBar +{ + Q_OBJECT +public: + explicit ScrollBar(QWidget *parent = nullptr); + virtual ~ScrollBar(); + + QSize sizeHint() const override; + + virtual void flash(); + +protected: + virtual void initStyleOption(QStyleOptionSlider *option) const override; + bool event(QEvent *event) override; + +private: + ScrollBarPrivate *d = nullptr; +}; + +} diff --git a/src/plugins/qmldesigner/components/formeditor/formeditorgraphicsview.cpp b/src/plugins/qmldesigner/components/formeditor/formeditorgraphicsview.cpp index 1e67884ad8d..a9401480f3a 100644 --- a/src/plugins/qmldesigner/components/formeditor/formeditorgraphicsview.cpp +++ b/src/plugins/qmldesigner/components/formeditor/formeditorgraphicsview.cpp @@ -6,6 +6,7 @@ #include "formeditorwidget.h" #include "navigation2d.h" #include +#include #include #include @@ -36,6 +37,7 @@ FormEditorGraphicsView::FormEditorGraphicsView(QWidget *parent) setBackgroundRole(QPalette::Window); activateCheckboardBackground(); + Utils::TransientScrollAreaSupport::support(this); // as mousetracking only works for mouse key it is better to handle it in the // eventFilter method so it works also for the space scrolling case as expected diff --git a/src/plugins/qmldesigner/components/resources/dockwidgets.css b/src/plugins/qmldesigner/components/resources/dockwidgets.css index 3404065521e..4ff1b46f52d 100644 --- a/src/plugins/qmldesigner/components/resources/dockwidgets.css +++ b/src/plugins/qmldesigner/components/resources/dockwidgets.css @@ -113,40 +113,6 @@ QScrollArea#dockWidgetScrollArea { background: creatorTheme.DStabInactiveButtonPress; } -QScrollBar { - background: creatorTheme.DSscrollBarTrack; -} - -QScrollBar:vertical { - width: 10px; -} - -QScrollBar:horizontal { - height: 10px; -} - -QScrollBar::handle { - background: creatorTheme.DSscrollBarHandle; -} - -QScrollBar::handle:vertical { - min-height: 30px; -} - -QScrollBar::handle:horizontal { - min-width: 30px; -} - -QScrollBar::add-line, -QScrollBar::sub-line, -QScrollBar::left-arrow, -QScrollBar::right-arrow, -QScrollBar::add-page, -QScrollBar::sub-page { - height: 0px; - width: 0px; -} - /* Focus related styling */ ADS--DockWidgetTab[focused="true"] { background: creatorTheme.DStabFocusBackground; diff --git a/src/plugins/qmldesigner/components/texteditor/texteditorwidget.cpp b/src/plugins/qmldesigner/components/texteditor/texteditorwidget.cpp index 9f4bb59231e..122228f2086 100644 --- a/src/plugins/qmldesigner/components/texteditor/texteditorwidget.cpp +++ b/src/plugins/qmldesigner/components/texteditor/texteditorwidget.cpp @@ -70,11 +70,6 @@ void TextEditorWidget::setTextEditor(Utils::UniqueObjectLatePtreditorWidget()->installEventFilter(this); - - static QString styleSheet = Theme::replaceCssColors( - QString::fromUtf8(Utils::FileReader::fetchQrc(":/qmldesigner/scrollbar.css"))); - m_textEditor->editorWidget()->verticalScrollBar()->setStyleSheet(styleSheet); - m_textEditor->editorWidget()->horizontalScrollBar()->setStyleSheet(styleSheet); } } diff --git a/src/plugins/qmldesignerbase/utils/studiostyle.cpp b/src/plugins/qmldesignerbase/utils/studiostyle.cpp index ec0b0f4af52..4b2f1132274 100644 --- a/src/plugins/qmldesignerbase/utils/studiostyle.cpp +++ b/src/plugins/qmldesignerbase/utils/studiostyle.cpp @@ -615,7 +615,6 @@ void StudioStyle::drawComplexControl( painter->save(); painter->setRenderHint(QPainter::RenderHint::Antialiasing); - int lineWidth = pixelMetric(QStyle::PM_DefaultFrameWidth, option, widget); Theme::Color themeframeColor = enabled ? interaction ? Theme::DSstateControlBackgroundColor_hover // Pressed @@ -627,14 +626,15 @@ void StudioStyle::drawComplexControl( QColor frameColor = creatorTheme()->color(themeframeColor); if ((option->subControls & SC_SliderGroove) && groove.isValid()) { - Theme::Color bgPlusColor = enabled + Theme::Color themeBgPlusColor = enabled ? interaction ? Theme::DSstateControlBackgroundColor_hover // Pressed : grooveHover ? Theme::DSstateSeparatorColor // GrooveHover - : Theme::DStoolbarBackground // Idle + : Theme::DSstateControlBackgroundColor_hover // Idle should be the same as pressed : Theme::DStoolbarBackground; // Disabled - Theme::Color bgMinusColor = Theme::DSpopupBackground; + + Theme::Color themeBgMinusColor = Theme::DSpopupBackground; QRect minusRect(groove); QRect plusRect(groove); @@ -659,9 +659,9 @@ void StudioStyle::drawComplexControl( painter->save(); painter->setPen(Qt::NoPen); - painter->setBrush(creatorTheme()->color(bgPlusColor)); + painter->setBrush(creatorTheme()->color(themeBgPlusColor)); painter->drawRoundedRect(plusRect, borderRadius, borderRadius); - painter->setBrush(creatorTheme()->color(bgMinusColor)); + painter->setBrush(creatorTheme()->color(themeBgMinusColor)); painter->drawRoundedRect(minusRect, borderRadius, borderRadius); painter->restore(); } @@ -673,7 +673,8 @@ void StudioStyle::drawComplexControl( : Theme::DSBackgroundColorAlternate : Theme::DScontrolBackgroundDisabled; - painter->setPen(tickPen); + painter->setBrush(Qt::NoBrush); + painter->setPen(creatorTheme()->color(tickPen)); int tickSize = proxy()->pixelMetric(PM_SliderTickmarkOffset, option, widget); int available = proxy()->pixelMetric(PM_SliderSpaceAvailable, slider, widget); int interval = slider->tickInterval; @@ -729,7 +730,7 @@ void StudioStyle::drawComplexControl( } // draw handle - if ((option->subControls & SC_SliderHandle) ) { + if (option->subControls & SC_SliderHandle) { Theme::Color handleColor = enabled ? interaction ? Theme::DSinteraction // Interaction @@ -749,8 +750,9 @@ void StudioStyle::drawComplexControl( } if (groove.isValid()) { + int borderWidth = pixelMetric(QStyle::PM_DefaultFrameWidth, option, widget); painter->setBrush(Qt::NoBrush); - painter->setPen(QPen(frameColor, lineWidth)); + painter->setPen(QPen(frameColor, borderWidth)); painter->drawRoundedRect(groove, borderRadius, borderRadius); } painter->restore(); @@ -853,29 +855,48 @@ QRect StudioStyle::subControlRect( } #endif - if (const QStyleOptionSlider *slider = qstyleoption_cast(option)) { - switch (subControl) { - case SubControl::SC_SliderGroove: - return option->rect; - case SubControl::SC_SliderHandle: - { - QRect retval = Super::subControlRect(control, option, subControl, widget); - int thickness = 2; - QPoint center = retval.center(); - const QRect &rect = slider->rect; - if (slider->orientation == Qt::Horizontal) - return {center.x() - thickness, rect.top(), (thickness * 2) + 1, rect.height()}; - else - return {rect.left(), center.y() - thickness, rect.width(), (thickness * 2) + 1}; - } - break; - default: - break; + switch (control) + { + case CC_Slider: + if (const auto slider = qstyleoption_cast(option)) { + switch (subControl) { + case SubControl::SC_SliderGroove: + return slider->rect; + case SubControl::SC_SliderHandle: + { + QRect retval = Super::subControlRect(control, option, subControl, widget); + return (slider->orientation == Qt::Horizontal) + ? retval.adjusted(0, 1, 0, 0) + : retval.adjusted(1, 0, 0, 0); + } + break; + default: + break; + } } + break; + default: + break; } + return Super::subControlRect(control, option, subControl, widget); } +int StudioStyle::styleHint( + StyleHint hint, + const QStyleOption *option, + const QWidget *widget, + QStyleHintReturn *returnData) const +{ + switch (hint) { + case SH_ScrollBar_Transient: + return true; + default: + break; + } + return Super::styleHint(hint, option, widget, returnData); +} + int StudioStyle::pixelMetric( PixelMetric metric, const QStyleOption *option, @@ -926,6 +947,21 @@ int StudioStyle::pixelMetric( return 4; case PM_ToolBarExtensionExtent: return 29; + case PM_ScrollBarExtent: + return 20; + case PM_ScrollBarSliderMin: + return 30; + case PM_SliderLength: + return 5; + case PM_SliderThickness: + if (const auto *slider = qstyleoption_cast(option)) { + return (slider->orientation == Qt::Horizontal + ? slider->rect.height() + : slider->rect.width()) - 1; + } + break; + case PM_SliderControlThickness: + return 2; default: break; } diff --git a/src/plugins/qmldesignerbase/utils/studiostyle.h b/src/plugins/qmldesignerbase/utils/studiostyle.h index a0eae302f54..4d6424cbef1 100644 --- a/src/plugins/qmldesignerbase/utils/studiostyle.h +++ b/src/plugins/qmldesignerbase/utils/studiostyle.h @@ -51,6 +51,12 @@ public: SubControl subControl, const QWidget *widget) const override; + int styleHint( + StyleHint hint, + const QStyleOption *option, + const QWidget *widget, + QStyleHintReturn *returnData) const override; + int pixelMetric( PixelMetric metric, const QStyleOption *option = nullptr, diff --git a/src/plugins/texteditor/texteditor.cpp b/src/plugins/texteditor/texteditor.cpp index 326be45d7fc..92c27723b99 100644 --- a/src/plugins/texteditor/texteditor.cpp +++ b/src/plugins/texteditor/texteditor.cpp @@ -66,6 +66,7 @@ #include #include #include +#include #include #include @@ -1144,6 +1145,7 @@ TextEditorWidget::TextEditorWidget(QWidget *parent) setLayoutDirection(Qt::LeftToRight); viewport()->setMouseTracking(true); setFrameStyle(QFrame::NoFrame); + TransientScrollAreaSupport::support(this); } void TextEditorWidget::setTextDocument(const QSharedPointer &doc) From cf61025af64485b8ec1fdaa0405541810cc7e813 Mon Sep 17 00:00:00 2001 From: Henning Gruendl Date: Wed, 19 Apr 2023 10:35:27 +0200 Subject: [PATCH 048/192] QmlDesigner: Remove experimental key insight Remove the experimental key from the insight plugin meta data template. Change-Id: I88ea837a1ff296a940e12b5935433947b2e2b7f2 Reviewed-by: Eike Ziller --- src/plugins/insight/Insight.json.in | 1 - 1 file changed, 1 deletion(-) diff --git a/src/plugins/insight/Insight.json.in b/src/plugins/insight/Insight.json.in index c333a0aab2e..0ac25523677 100644 --- a/src/plugins/insight/Insight.json.in +++ b/src/plugins/insight/Insight.json.in @@ -3,7 +3,6 @@ \"Version\" : \"$$QTCREATOR_VERSION\", \"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\", \"Revision\" : \"$$QTC_PLUGIN_REVISION\", - \"Experimental\" : false, \"Vendor\" : \"The Qt Company Ltd\", \"Copyright\" : \"(C) $$QTCREATOR_COPYRIGHT_YEAR The Qt Company Ltd\", \"License\" : [ \"Commercial Usage\", From 79b5a9f03e01712613581e630e51a5143fd9f647 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Tue, 18 Apr 2023 15:59:19 +0200 Subject: [PATCH 049/192] TextEditor: Return BaseTextEditor for duplicate Because the type is always a BaseTextEditor we can return a BaseTextEditor type. C++ is allowing to change the overload return type so long it is compatible. Change-Id: Ib4c88faaa6fdfb97fd03c51a120de0fa0c2d00cd Reviewed-by: David Schulz Reviewed-by: Qt CI Bot --- .../qmldesigner/components/texteditor/texteditorview.cpp | 3 +-- src/plugins/texteditor/texteditor.cpp | 4 ++-- src/plugins/texteditor/texteditor.h | 2 +- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/plugins/qmldesigner/components/texteditor/texteditorview.cpp b/src/plugins/qmldesigner/components/texteditor/texteditorview.cpp index fb33794ea80..1921487aa0b 100644 --- a/src/plugins/qmldesigner/components/texteditor/texteditorview.cpp +++ b/src/plugins/qmldesigner/components/texteditor/texteditorview.cpp @@ -80,8 +80,7 @@ void TextEditorView::modelAttached(Model *model) AbstractView::modelAttached(model); auto textEditor = Utils::UniqueObjectLatePtr( - static_cast( - QmlDesignerPlugin::instance()->currentDesignDocument()->textEditor()->duplicate())); + QmlDesignerPlugin::instance()->currentDesignDocument()->textEditor()->duplicate()); // Set the context of the text editor, but we add another special context to override shortcuts. Core::Context context = textEditor->context(); diff --git a/src/plugins/texteditor/texteditor.cpp b/src/plugins/texteditor/texteditor.cpp index 92c27723b99..f348aa7ce61 100644 --- a/src/plugins/texteditor/texteditor.cpp +++ b/src/plugins/texteditor/texteditor.cpp @@ -8940,11 +8940,11 @@ BaseTextEditor *TextEditorFactoryPrivate::createEditorHelper(const TextDocumentP return editor; } -IEditor *BaseTextEditor::duplicate() +BaseTextEditor *BaseTextEditor::duplicate() { // Use new standard setup if that's available. if (d->m_origin) { - IEditor *dup = d->m_origin->duplicateTextEditor(this); + BaseTextEditor *dup = d->m_origin->duplicateTextEditor(this); emit editorDuplicated(dup); return dup; } diff --git a/src/plugins/texteditor/texteditor.h b/src/plugins/texteditor/texteditor.h index dc492eca21b..98bddb84b91 100644 --- a/src/plugins/texteditor/texteditor.h +++ b/src/plugins/texteditor/texteditor.h @@ -110,7 +110,7 @@ public: // IEditor Core::IDocument *document() const override; - IEditor *duplicate() override; + BaseTextEditor *duplicate() override; QByteArray saveState() const override; void restoreState(const QByteArray &state) override; From 6035ff939d636c9027d87f95f9b70096dfc04d7e Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Thu, 30 Mar 2023 13:14:17 +0200 Subject: [PATCH 050/192] QmlDesigner: Add module scanner For performance reason we want to get the qml modules directly from the file system. When the project storage is finished we can get the modules from there. Task-number: QDS-9542 Change-Id: I26d4b028fbf5ebc541fcd8e34d285ded1fb14935 Reviewed-by: Reviewed-by: Thomas Hartmann --- src/plugins/qmldesigner/CMakeLists.txt | 4 +- .../designeractionmanagerview.cpp | 2 +- .../componentcore/designeractionmanagerview.h | 2 +- .../connectioneditor/connectionview.cpp | 2 +- .../connectioneditor/connectionview.h | 2 +- .../contentlibrary/contentlibraryview.cpp | 2 +- .../contentlibrary/contentlibraryview.h | 2 +- .../components/debugview/debugview.cpp | 2 +- .../components/debugview/debugview.h | 2 +- .../components/edit3d/edit3dview.cpp | 6 +- .../components/edit3d/edit3dview.h | 2 +- .../components/formeditor/formeditorview.cpp | 2 +- .../components/formeditor/formeditorview.h | 2 +- .../itemlibrary/itemlibraryaddimportmodel.cpp | 4 +- .../itemlibrary/itemlibraryaddimportmodel.h | 4 +- .../itemlibrary/itemlibraryassetimporter.cpp | 6 +- .../itemlibrary/itemlibraryassetimporter.h | 2 +- .../itemlibrary/itemlibrarymodel.cpp | 4 +- .../components/itemlibrary/itemlibrarymodel.h | 2 +- .../itemlibrary/itemlibraryview.cpp | 6 +- .../components/itemlibrary/itemlibraryview.h | 6 +- .../itemlibrary/itemlibrarywidget.cpp | 8 +- .../itemlibrary/itemlibrarywidget.h | 4 +- .../materialbrowser/materialbrowserview.cpp | 4 +- .../materialbrowser/materialbrowserview.h | 2 +- .../materialeditor/materialeditorview.cpp | 4 +- .../materialeditor/materialeditorview.h | 2 +- .../navigator/navigatortreemodel.cpp | 2 +- .../components/navigator/navigatorview.cpp | 2 +- .../components/navigator/navigatorview.h | 2 +- .../components/texteditor/texteditorview.cpp | 2 +- .../components/texteditor/texteditorview.h | 2 +- .../textureeditor/textureeditorview.cpp | 4 +- .../textureeditor/textureeditorview.h | 2 +- .../designercore/include/abstractview.h | 6 +- .../include/externaldependenciesinterface.h | 1 + .../designercore/include/forwardview.h | 4 +- .../qmldesigner/designercore/include/import.h | 2 + .../qmldesigner/designercore/include/model.h | 12 +- .../designercore/include/nodeinstanceview.h | 2 +- .../designercore/include/rewriterview.h | 2 +- .../include/subcomponentmanager.h | 4 +- .../instances/nodeinstanceview.cpp | 2 +- .../designercore/metainfo/nodemetainfo.cpp | 8 +- .../metainfo/subcomponentmanager.cpp | 2 +- .../designercore/model/abstractview.cpp | 10 +- .../qmldesigner/designercore/model/model.cpp | 28 +- .../qmldesigner/designercore/model/model_p.h | 16 +- .../designercore/model/modelmerger.cpp | 2 +- .../designercore/model/rewriterview.cpp | 2 +- .../designercore/model/texttomodelmerger.cpp | 292 +++++++----------- .../designercore/model/texttomodelmerger.h | 8 +- .../projectstorage/modulescanner.cpp | 73 +++++ .../projectstorage/modulescanner.h | 37 +++ .../projectstorage/qmldocumentparser.cpp | 4 +- .../projectstorage/qmldocumentparser.h | 4 +- .../projectstorage/qmltypesparser.cpp | 4 +- .../projectstorage/qmltypesparser.h | 4 +- .../qmldesignerexternaldependencies.cpp | 48 +++ .../qmldesignerexternaldependencies.h | 1 + .../qmldesigner/qmldesignerprojectmanager.cpp | 36 ++- .../qmldesigner/coretests/tst_testcore.cpp | 11 +- tests/unit/unittest/CMakeLists.txt | 7 +- .../unit/unittest/google-using-declarations.h | 3 +- tests/unit/unittest/modulescanner-test.cpp | 53 ++++ tests/unit/unittest/smallstring-test.cpp | 2 - tests/unit/unittest/unittest-matchers.h | 68 +++- .../unittest/unittest-utility-functions.h | 5 +- 68 files changed, 540 insertions(+), 329 deletions(-) create mode 100644 src/plugins/qmldesigner/designercore/projectstorage/modulescanner.cpp create mode 100644 src/plugins/qmldesigner/designercore/projectstorage/modulescanner.h create mode 100644 tests/unit/unittest/modulescanner-test.cpp diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index ed558082e1e..59a517a529d 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -57,6 +57,7 @@ add_qtc_library(QmlDesignerCore STATIC Utils Qt::Widgets Qt::Qml + Qt::QmlPrivate Core ProjectExplorer QmakeProjectManager @@ -92,7 +93,7 @@ extend_qtc_library(QmlDesignerCore CONDITION TARGET Qt6::QmlDomPrivate AND TARGET Qt6::QmlCompilerPrivate AND Qt6_VERSION VERSION_GREATER_EQUAL 6.5.0 DEPENDS Qt6::QmlDomPrivate Qt6::QmlCompilerPrivate - DEFINES QDS_HAS_QMLDOM + PUBLIC_DEFINES QDS_HAS_QMLPRIVATE ) extend_qtc_library(QmlDesignerCore @@ -400,6 +401,7 @@ extend_qtc_library(QmlDesignerCore filesystem.cpp filesystem.h filestatus.h filestatuscache.cpp filestatuscache.h + modulescanner.cpp modulescanner.h nonlockingmutex.h projectstorageexceptions.cpp projectstorageexceptions.h projectstorageinterface.h diff --git a/src/plugins/qmldesigner/components/componentcore/designeractionmanagerview.cpp b/src/plugins/qmldesigner/components/componentcore/designeractionmanagerview.cpp index 14f55efa3f8..90bcc54fddc 100644 --- a/src/plugins/qmldesigner/components/componentcore/designeractionmanagerview.cpp +++ b/src/plugins/qmldesigner/components/componentcore/designeractionmanagerview.cpp @@ -95,7 +95,7 @@ void DesignerActionManagerView::nodeOrderChanged(const NodeListProperty &) setupContext(SelectionContext::UpdateMode::NodeHierachy); } -void DesignerActionManagerView::importsChanged(const QList &, const QList &) +void DesignerActionManagerView::importsChanged(const Imports &, const Imports &) { setupContext(); } diff --git a/src/plugins/qmldesigner/components/componentcore/designeractionmanagerview.h b/src/plugins/qmldesigner/components/componentcore/designeractionmanagerview.h index 41210602b38..9ccfe4e7379 100644 --- a/src/plugins/qmldesigner/components/componentcore/designeractionmanagerview.h +++ b/src/plugins/qmldesigner/components/componentcore/designeractionmanagerview.h @@ -37,7 +37,7 @@ public: void selectedNodesChanged(const QList &, const QList &) override; void nodeOrderChanged(const NodeListProperty &) override; - void importsChanged(const QList &, const QList &) override; + void importsChanged(const Imports &, const Imports &) override; void signalHandlerPropertiesChanged(const QVector &/*propertyList*/, PropertyChangeFlags /*propertyChange*/) override; void variantPropertiesChanged(const QList& propertyList, PropertyChangeFlags propertyChangeFlag) override; void bindingPropertiesChanged(const QList& propertyList, PropertyChangeFlags propertyChangeFlag) override; diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionview.cpp b/src/plugins/qmldesigner/components/connectioneditor/connectionview.cpp index 39e05a898ca..56fcc7ef6bf 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectionview.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/connectionview.cpp @@ -183,7 +183,7 @@ void ConnectionView::auxiliaryDataChanged([[maybe_unused]] const ModelNode &node selectionModel->clearSelection(); } -void ConnectionView::importsChanged(const QList & /*addedImports*/, const QList & /*removedImports*/) +void ConnectionView::importsChanged(const Imports & /*addedImports*/, const Imports & /*removedImports*/) { backendModel()->resetModel(); } diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionview.h b/src/plugins/qmldesigner/components/connectioneditor/connectionview.h index dcf61ad79d9..89c6c489105 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectionview.h +++ b/src/plugins/qmldesigner/components/connectioneditor/connectionview.h @@ -50,7 +50,7 @@ public: AuxiliaryDataKeyView key, const QVariant &data) override; - void importsChanged(const QList &addedImports, const QList &removedImports) override; + void importsChanged(const Imports &addedImports, const Imports &removedImports) override; void currentStateChanged(const ModelNode &node) override; diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp index 01932900e9e..105bfed89dd 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp @@ -144,7 +144,7 @@ void ContentLibraryView::modelAboutToBeDetached(Model *model) AbstractView::modelAboutToBeDetached(model); } -void ContentLibraryView::importsChanged(const QList &addedImports, const QList &removedImports) +void ContentLibraryView::importsChanged(const Imports &addedImports, const Imports &removedImports) { Q_UNUSED(addedImports) Q_UNUSED(removedImports) diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.h index 1d27f6e260c..137034dd955 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.h +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.h @@ -31,7 +31,7 @@ public: // AbstractView void modelAttached(Model *model) override; void modelAboutToBeDetached(Model *model) override; - void importsChanged(const QList &addedImports, const QList &removedImports) override; + void importsChanged(const Imports &addedImports, const Imports &removedImports) override; void active3DSceneChanged(qint32 sceneId) override; void selectedNodesChanged(const QList &selectedNodeList, const QList &lastSelectedNodeList) override; diff --git a/src/plugins/qmldesigner/components/debugview/debugview.cpp b/src/plugins/qmldesigner/components/debugview/debugview.cpp index 2888575e44c..2a62d7a6b2b 100644 --- a/src/plugins/qmldesigner/components/debugview/debugview.cpp +++ b/src/plugins/qmldesigner/components/debugview/debugview.cpp @@ -65,7 +65,7 @@ void DebugView::modelAboutToBeDetached(Model *model) AbstractView::modelAboutToBeDetached(model); } -void DebugView::importsChanged(const QList &addedImports, const QList &removedImports) +void DebugView::importsChanged(const Imports &addedImports, const Imports &removedImports) { if (isDebugViewEnabled()) { QString message; diff --git a/src/plugins/qmldesigner/components/debugview/debugview.h b/src/plugins/qmldesigner/components/debugview/debugview.h index 885030545cf..e80c619c818 100644 --- a/src/plugins/qmldesigner/components/debugview/debugview.h +++ b/src/plugins/qmldesigner/components/debugview/debugview.h @@ -24,7 +24,7 @@ public: void modelAttached(Model *model) override; void modelAboutToBeDetached(Model *model) override; - void importsChanged(const QList &addedImports, const QList &removedImports) override; + void importsChanged(const Imports &addedImports, const Imports &removedImports) override; void nodeCreated(const ModelNode &createdNode) override; void nodeAboutToBeRemoved(const ModelNode &removedNode) override; diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp index d346e2bf164..39125c8f698 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp +++ b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp @@ -312,8 +312,8 @@ void Edit3DView::modelAboutToBeDetached(Model *model) AbstractView::modelAboutToBeDetached(model); } -void Edit3DView::importsChanged([[maybe_unused]] const QList &addedImports, - [[maybe_unused]] const QList &removedImports) +void Edit3DView::importsChanged([[maybe_unused]] const Imports &addedImports, + [[maybe_unused]] const Imports &removedImports) { checkImports(); } @@ -920,7 +920,7 @@ void Edit3DView::addQuick3DImport() { DesignDocument *document = QmlDesignerPlugin::instance()->currentDesignDocument(); if (document && !document->inFileComponentModelActive() && model()) { - const QList imports = model()->possibleImports(); + const Imports imports = model()->possibleImports(); for (const auto &import : imports) { if (import.url() == "QtQuick3D") { if (!import.version().isEmpty() && import.majorVersion() >= 6) { diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dview.h b/src/plugins/qmldesigner/components/edit3d/edit3dview.h index 109555f586b..cd3e686d3bb 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dview.h +++ b/src/plugins/qmldesigner/components/edit3d/edit3dview.h @@ -44,7 +44,7 @@ public: void updateActiveScene3D(const QVariantMap &sceneState) override; void modelAttached(Model *model) override; void modelAboutToBeDetached(Model *model) override; - void importsChanged(const QList &addedImports, const QList &removedImports) 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 nodeAtPosReady(const ModelNode &modelNode, const QVector3D &pos3d) override; diff --git a/src/plugins/qmldesigner/components/formeditor/formeditorview.cpp b/src/plugins/qmldesigner/components/formeditor/formeditorview.cpp index 1f4d5262852..5661e4ff793 100644 --- a/src/plugins/qmldesigner/components/formeditor/formeditorview.cpp +++ b/src/plugins/qmldesigner/components/formeditor/formeditorview.cpp @@ -253,7 +253,7 @@ void FormEditorView::modelAboutToBeDetached(Model *model) AbstractView::modelAboutToBeDetached(model); } -void FormEditorView::importsChanged(const QList &/*addedImports*/, const QList &/*removedImports*/) +void FormEditorView::importsChanged(const Imports &/*addedImports*/, const Imports &/*removedImports*/) { reset(); } diff --git a/src/plugins/qmldesigner/components/formeditor/formeditorview.h b/src/plugins/qmldesigner/components/formeditor/formeditorview.h index fde20427628..f97959acb37 100644 --- a/src/plugins/qmldesigner/components/formeditor/formeditorview.h +++ b/src/plugins/qmldesigner/components/formeditor/formeditorview.h @@ -48,7 +48,7 @@ public: void modelAttached(Model *model) override; void modelAboutToBeDetached(Model *model) override; - void importsChanged(const QList &addedImports, const QList &removedImports) override; + void importsChanged(const Imports &addedImports, const Imports &removedImports) override; void nodeCreated(const ModelNode &createdNode) override; void nodeAboutToBeRemoved(const ModelNode &removedNode) override; diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryaddimportmodel.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryaddimportmodel.cpp index 5b46c1d12ef..41a9c6b5f4a 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryaddimportmodel.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryaddimportmodel.cpp @@ -56,14 +56,14 @@ QHash ItemLibraryAddImportModel::roleNames() const return m_roleNames; } -void ItemLibraryAddImportModel::update(const QList &possibleImports) +void ItemLibraryAddImportModel::update(const Imports &possibleImports) { beginResetModel(); m_importList.clear(); const DesignerMcuManager &mcuManager = DesignerMcuManager::instance(); const bool isQtForMCUs = mcuManager.isMCUProject(); - QList filteredImports; + Imports filteredImports; if (isQtForMCUs) { const QStringList mcuAllowedList = mcuManager.allowedImports(); const QStringList mcuBannedList = mcuManager.bannedImports(); diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryaddimportmodel.h b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryaddimportmodel.h index 5785b67301a..2989fd51642 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryaddimportmodel.h +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryaddimportmodel.h @@ -24,7 +24,7 @@ public: QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; QHash roleNames() const override; - void update(const QList &possibleImports); + void update(const Imports &possibleImports); void setSearchText(const QString &searchText); Import getImportAt(int index) const; @@ -33,7 +33,7 @@ public: private: QString m_searchText; - QList m_importList; + Imports m_importList; QSet m_importFilterList; QHash m_roleNames; QSet m_priorityImports; diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.cpp index d9b82419433..e7002ff2503 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.cpp @@ -709,9 +709,9 @@ void ItemLibraryAssetImporter::finalizeQuick3DImport() model->rewriterView()->textModifier()->replace(0, 0, {}); } else if (counter < 100) { try { - const QList posImports = model->possibleImports(); - const QList currentImports = model->imports(); - QList newImportsToAdd; + const Imports posImports = model->possibleImports(); + const Imports currentImports = model->imports(); + Imports newImportsToAdd; for (auto &imp : std::as_const(m_requiredImports)) { const bool isPos = Utils::contains(posImports, [imp](const Import &posImp) { diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.h b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.h index c7952e9b8f6..94413af392e 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.h +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.h @@ -107,7 +107,7 @@ private: int m_currentImportId = 0; QHash m_parseData; QString m_progressTitle; - QList m_requiredImports; + Imports m_requiredImports; QList m_puppetQueue; }; } // QmlDesigner diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp index 556d0c978fe..18430ca0179 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp @@ -343,7 +343,7 @@ void ItemLibraryModel::update(ItemLibraryInfo *itemLibraryInfo, Model *model) materialBundlePrefix.append(".MaterialBundle"); // create import sections - const QList usedImports = model->usedImports(); + const Imports usedImports = model->usedImports(); QHash importHash; for (const Import &import : model->imports()) { if (import.url() != projectName) { @@ -550,7 +550,7 @@ ItemLibraryImport *ItemLibraryModel::importByUrl(const QString &importUrl) const return nullptr; } -void ItemLibraryModel::updateUsedImports(const QList &usedImports) +void ItemLibraryModel::updateUsedImports(const Imports &usedImports) { // imports in the excludeList are not marked used and thus can always be removed even when in use. const QList excludeList = {"SimulinkConnector"}; diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.h b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.h index 4e9c5809c12..212ddf8e040 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.h +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.h @@ -38,7 +38,7 @@ public: ItemLibraryImport *importByUrl(const QString &importName) const; void update(ItemLibraryInfo *itemLibraryInfo, Model *model); - void updateUsedImports(const QList &usedImports); + void updateUsedImports(const Imports &usedImports); QMimeData *getMimeData(const ItemLibraryEntry &itemLibraryEntry); diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp index e8d1b584bfd..89b16fcd8e5 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp @@ -74,7 +74,7 @@ void ItemLibraryView::modelAboutToBeDetached(Model *model) m_widget->setModel(nullptr); } -void ItemLibraryView::importsChanged(const QList &addedImports, const QList &removedImports) +void ItemLibraryView::importsChanged(const Imports &addedImports, const Imports &removedImports) { DesignDocument *document = QmlDesignerPlugin::instance()->currentDesignDocument(); for (const auto &import : addedImports) @@ -111,7 +111,7 @@ void ItemLibraryView::importsChanged(const QList &addedImports, const QL } } -void ItemLibraryView::possibleImportsChanged(const QList &possibleImports) +void ItemLibraryView::possibleImportsChanged(const Imports &possibleImports) { DesignDocument *document = QmlDesignerPlugin::instance()->currentDesignDocument(); for (const auto &import : possibleImports) @@ -120,7 +120,7 @@ void ItemLibraryView::possibleImportsChanged(const QList &possibleImport m_widget->updatePossibleImports(possibleImports); } -void ItemLibraryView::usedImportsChanged(const QList &usedImports) +void ItemLibraryView::usedImportsChanged(const Imports &usedImports) { m_widget->updateUsedImports(usedImports); } diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.h b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.h index e28f39318c0..43287ae0e5a 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.h +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.h @@ -26,9 +26,9 @@ public: // AbstractView void modelAttached(Model *model) override; void modelAboutToBeDetached(Model *model) override; - void importsChanged(const QList &addedImports, const QList &removedImports) override; - void possibleImportsChanged(const QList &possibleImports) override; - void usedImportsChanged(const QList &usedImports) override; + void importsChanged(const Imports &addedImports, const Imports &removedImports) override; + void possibleImportsChanged(const Imports &possibleImports) override; + void usedImportsChanged(const Imports &usedImports) override; void documentMessagesChanged(const QList &errors, const QList &warnings) override; void updateImport3DSupport(const QVariantMap &supportMap) override; void customNotification(const AbstractView *view, const QString &identifier, diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp index 549f9e69c10..1fafc91ea6a 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp @@ -86,7 +86,7 @@ bool ItemLibraryWidget::eventFilter(QObject *obj, QEvent *event) Import fileImport = Import::createFileImport(entry.requiredImport()); if (!m_model->hasImport(libImport, true, true) && !m_model->hasImport(fileImport, true, true)) { - const QList possImports = m_model->possibleImports(); + const Imports possImports = m_model->possibleImports(); for (const auto &possImport : possImports) { if ((!possImport.url().isEmpty() && possImport.url() == libImport.url()) || (!possImport.file().isEmpty() && possImport.file() == fileImport.file())) { @@ -246,7 +246,7 @@ void ItemLibraryWidget::handleAddImport(int index) + importStr); } - QList imports; + Imports imports; const QString dependency = getDependencyImport(import); auto document = QmlDesignerPlugin::instance()->currentDesignDocument(); @@ -346,13 +346,13 @@ void ItemLibraryWidget::updateModel() updateSearch(); } -void ItemLibraryWidget::updatePossibleImports(const QList &possibleImports) +void ItemLibraryWidget::updatePossibleImports(const Imports &possibleImports) { m_addModuleModel->update(possibleImports); delayedUpdateModel(); } -void ItemLibraryWidget::updateUsedImports(const QList &usedImports) +void ItemLibraryWidget::updateUsedImports(const Imports &usedImports) { m_itemLibraryModel->updateUsedImports(usedImports); } diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.h b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.h index 394b3ea6103..38cd782d097 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.h +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.h @@ -60,8 +60,8 @@ public: void switchToComponentsView(); void delayedUpdateModel(); void updateModel(); - void updatePossibleImports(const QList &possibleImports); - void updateUsedImports(const QList &usedImports); + void updatePossibleImports(const Imports &possibleImports); + void updateUsedImports(const Imports &usedImports); void setModel(Model *model); void setFlowMode(bool b); diff --git a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.cpp b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.cpp index 8e799336f85..5caf0169eec 100644 --- a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.cpp +++ b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.cpp @@ -469,8 +469,8 @@ ModelNode MaterialBrowserView::getMaterialOfModel(const ModelNode &model, int id return mat; } -void MaterialBrowserView::importsChanged([[maybe_unused]] const QList &addedImports, - [[maybe_unused]] const QList &removedImports) +void MaterialBrowserView::importsChanged([[maybe_unused]] const Imports &addedImports, + [[maybe_unused]] const Imports &removedImports) { bool hasQuick3DImport = model()->hasImport("QtQuick3D"); diff --git a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.h b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.h index 811d6679b63..2e0227be413 100644 --- a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.h +++ b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.h @@ -44,7 +44,7 @@ public: void nodeAboutToBeRemoved(const ModelNode &removedNode) override; void nodeRemoved(const ModelNode &removedNode, const NodeAbstractProperty &parentProperty, PropertyChangeFlags propertyChange) override; - void importsChanged(const QList &addedImports, const QList &removedImports) 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 instancesCompleted(const QVector &completedNodeList) override; diff --git a/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp b/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp index 47d47985ebd..9cb4959e132 100644 --- a/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp +++ b/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp @@ -974,8 +974,8 @@ void MaterialEditorView::modelNodePreviewPixmapChanged(const ModelNode &node, co m_qmlBackEnd->updateMaterialPreview(pixmap); } -void MaterialEditorView::importsChanged([[maybe_unused]] const QList &addedImports, - [[maybe_unused]] const QList &removedImports) +void MaterialEditorView::importsChanged([[maybe_unused]] const Imports &addedImports, + [[maybe_unused]] const Imports &removedImports) { m_hasQuick3DImport = model()->hasImport("QtQuick3D"); m_qmlBackEnd->contextObject()->setHasQuick3DImport(m_hasQuick3DImport); diff --git a/src/plugins/qmldesigner/components/materialeditor/materialeditorview.h b/src/plugins/qmldesigner/components/materialeditor/materialeditorview.h index d47d030e57b..c201742bd51 100644 --- a/src/plugins/qmldesigner/components/materialeditor/materialeditorview.h +++ b/src/plugins/qmldesigner/components/materialeditor/materialeditorview.h @@ -55,7 +55,7 @@ public: void nodeTypeChanged(const ModelNode& node, const TypeName &type, int majorVersion, int minorVersion) override; void rootNodeTypeChanged(const QString &type, int majorVersion, int minorVersion) override; void modelNodePreviewPixmapChanged(const ModelNode &node, const QPixmap &pixmap) override; - void importsChanged(const QList &addedImports, const QList &removedImports) 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 nodeReparented(const ModelNode &node, const NodeAbstractProperty &newPropertyParent, diff --git a/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp b/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp index 62868290285..9dcf81a1738 100644 --- a/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp +++ b/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp @@ -901,7 +901,7 @@ void NavigatorTreeModel::addImport(const QString &importName) { Import import = Import::createLibraryImport(importName); if (!m_view->model()->hasImport(import, true, true)) { - const QList possImports = m_view->model()->possibleImports(); + const Imports possImports = m_view->model()->possibleImports(); for (const auto &possImport : possImports) { if (possImport.url() == import.url()) { import = possImport; diff --git a/src/plugins/qmldesigner/components/navigator/navigatorview.cpp b/src/plugins/qmldesigner/components/navigator/navigatorview.cpp index 28481aae798..97c7285d49d 100644 --- a/src/plugins/qmldesigner/components/navigator/navigatorview.cpp +++ b/src/plugins/qmldesigner/components/navigator/navigatorview.cpp @@ -228,7 +228,7 @@ void NavigatorView::modelAboutToBeDetached(Model *model) AbstractView::modelAboutToBeDetached(model); } -void NavigatorView::importsChanged(const QList &/*addedImports*/, const QList &/*removedImports*/) +void NavigatorView::importsChanged(const Imports &/*addedImports*/, const Imports &/*removedImports*/) { treeWidget()->update(); } diff --git a/src/plugins/qmldesigner/components/navigator/navigatorview.h b/src/plugins/qmldesigner/components/navigator/navigatorview.h index f5fa81591d9..662b749ecec 100644 --- a/src/plugins/qmldesigner/components/navigator/navigatorview.h +++ b/src/plugins/qmldesigner/components/navigator/navigatorview.h @@ -50,7 +50,7 @@ public: void modelAttached(Model *model) override; void modelAboutToBeDetached(Model *model) override; - void importsChanged(const QList &addedImports, const QList &removedImports) override; + void importsChanged(const Imports &addedImports, const Imports &removedImports) override; void nodeAboutToBeRemoved(const ModelNode &removedNode) override; void nodeRemoved(const ModelNode &removedNode, const NodeAbstractProperty &parentProperty, PropertyChangeFlags propertyChange) override; diff --git a/src/plugins/qmldesigner/components/texteditor/texteditorview.cpp b/src/plugins/qmldesigner/components/texteditor/texteditorview.cpp index 1921487aa0b..6c6102036ac 100644 --- a/src/plugins/qmldesigner/components/texteditor/texteditorview.cpp +++ b/src/plugins/qmldesigner/components/texteditor/texteditorview.cpp @@ -103,7 +103,7 @@ void TextEditorView::modelAboutToBeDetached(Model *model) } } -void TextEditorView::importsChanged(const QList &/*addedImports*/, const QList &/*removedImports*/) +void TextEditorView::importsChanged(const Imports &/*addedImports*/, const Imports &/*removedImports*/) { } diff --git a/src/plugins/qmldesigner/components/texteditor/texteditorview.h b/src/plugins/qmldesigner/components/texteditor/texteditorview.h index b1865e78f9f..e50372279d9 100644 --- a/src/plugins/qmldesigner/components/texteditor/texteditorview.h +++ b/src/plugins/qmldesigner/components/texteditor/texteditorview.h @@ -34,7 +34,7 @@ public: void modelAttached(Model *model) override; void modelAboutToBeDetached(Model *model) override; - void importsChanged(const QList &addedImports, const QList &removedImports) override; + void importsChanged(const Imports &addedImports, const Imports &removedImports) override; void nodeAboutToBeRemoved(const ModelNode &removedNode) override; void nodeReparented(const ModelNode &node, const NodeAbstractProperty &newPropertyParent, const NodeAbstractProperty &oldPropertyParent, AbstractView::PropertyChangeFlags propertyChange) override; diff --git a/src/plugins/qmldesigner/components/textureeditor/textureeditorview.cpp b/src/plugins/qmldesigner/components/textureeditor/textureeditorview.cpp index 38664c146e4..a4132d0eb7a 100644 --- a/src/plugins/qmldesigner/components/textureeditor/textureeditorview.cpp +++ b/src/plugins/qmldesigner/components/textureeditor/textureeditorview.cpp @@ -720,8 +720,8 @@ void TextureEditorView::instancePropertyChanged(const QList &addedImports, - [[maybe_unused]] const QList &removedImports) +void TextureEditorView::importsChanged([[maybe_unused]] const Imports &addedImports, + [[maybe_unused]] const Imports &removedImports) { m_hasQuick3DImport = model()->hasImport("QtQuick3D"); m_qmlBackEnd->contextObject()->setHasQuick3DImport(m_hasQuick3DImport); diff --git a/src/plugins/qmldesigner/components/textureeditor/textureeditorview.h b/src/plugins/qmldesigner/components/textureeditor/textureeditorview.h index b299a1e99ae..7baa07e9d30 100644 --- a/src/plugins/qmldesigner/components/textureeditor/textureeditorview.h +++ b/src/plugins/qmldesigner/components/textureeditor/textureeditorview.h @@ -58,7 +58,7 @@ public: void currentStateChanged(const ModelNode &node) override; void instancePropertyChanged(const QList > &propertyList) override; - void importsChanged(const QList &addedImports, const QList &removedImports) 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; diff --git a/src/plugins/qmldesigner/designercore/include/abstractview.h b/src/plugins/qmldesigner/designercore/include/abstractview.h index 2d886d959be..0c3df94030c 100644 --- a/src/plugins/qmldesigner/designercore/include/abstractview.h +++ b/src/plugins/qmldesigner/designercore/include/abstractview.h @@ -195,9 +195,9 @@ public: const ModelNode &movedNode, int oldIndex); - virtual void importsChanged(const QList &addedImports, const QList &removedImports); - virtual void possibleImportsChanged(const QList &possibleImports); - virtual void usedImportsChanged(const QList &usedImports); + virtual void importsChanged(const Imports &addedImports, const Imports &removedImports); + virtual void possibleImportsChanged(const Imports &possibleImports); + virtual void usedImportsChanged(const Imports &usedImports); virtual void auxiliaryDataChanged(const ModelNode &node, AuxiliaryDataKeyView type, diff --git a/src/plugins/qmldesigner/designercore/include/externaldependenciesinterface.h b/src/plugins/qmldesigner/designercore/include/externaldependenciesinterface.h index 7b2b8a2c55f..d6af071a30a 100644 --- a/src/plugins/qmldesigner/designercore/include/externaldependenciesinterface.h +++ b/src/plugins/qmldesigner/designercore/include/externaldependenciesinterface.h @@ -40,6 +40,7 @@ public: virtual PuppetStartData puppetStartData(const class Model &model) const = 0; virtual bool instantQmlTextUpdate() const = 0; virtual Utils::FilePath qmlPuppetPath() const = 0; + virtual QStringList modulePaths() const = 0; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/include/forwardview.h b/src/plugins/qmldesigner/designercore/include/forwardview.h index 7041e851096..386ac83141c 100644 --- a/src/plugins/qmldesigner/designercore/include/forwardview.h +++ b/src/plugins/qmldesigner/designercore/include/forwardview.h @@ -44,7 +44,7 @@ public: void fileUrlChanged(const QUrl &oldUrl, const QUrl &newUrl) override; void nodeOrderChanged(const NodeListProperty &listProperty) override; - void importsChanged(const QList &addedImports, const QList &removedImports) override; + void importsChanged(const Imports &addedImports, const Imports &removedImports) override; void auxiliaryDataChanged(const ModelNode &node, const PropertyName &name, const QVariant &data) override; @@ -197,7 +197,7 @@ void ForwardView::nodeOrderChanged(const NodeListProperty &listPropert } template -void ForwardView::importChanged(const QList &addedImports, const QList &removedImports) +void ForwardView::importChanged(const Imports &addedImports, const Imports &removedImports) { for (const ViewTypePointer &view : std::as_const(m_targetViewList)) view->importChanged(addedImport, removedImport); diff --git a/src/plugins/qmldesigner/designercore/include/import.h b/src/plugins/qmldesigner/designercore/include/import.h index b221b861732..ff9e90f162f 100644 --- a/src/plugins/qmldesigner/designercore/include/import.h +++ b/src/plugins/qmldesigner/designercore/include/import.h @@ -56,6 +56,8 @@ private: QMLDESIGNERCORE_EXPORT size_t qHash(const Import &import); +using Imports = QList; + } // namespace QmlDesigner Q_DECLARE_METATYPE(QmlDesigner::Import) diff --git a/src/plugins/qmldesigner/designercore/include/model.h b/src/plugins/qmldesigner/designercore/include/model.h index f3227361e68..d36086777d8 100644 --- a/src/plugins/qmldesigner/designercore/include/model.h +++ b/src/plugins/qmldesigner/designercore/include/model.h @@ -115,12 +115,12 @@ public: // Editing sub-components: // Imports: - const QList &imports() const; - const QList &possibleImports() const; - const QList &usedImports() const; - void changeImports(const QList &importsToBeAdded, const QList &importsToBeRemoved); - void setPossibleImports(const QList &possibleImports); - void setUsedImports(const QList &usedImports); + const Imports &imports() const; + const Imports &possibleImports() const; + const Imports &usedImports() const; + void changeImports(const Imports &importsToBeAdded, const Imports &importsToBeRemoved); + void setPossibleImports(const Imports &possibleImports); + void setUsedImports(const Imports &usedImports); bool hasImport(const Import &import, bool ignoreAlias = true, bool allowHigherVersion = false) const; bool isImportPossible(const Import &import, bool ignoreAlias = true, bool allowHigherVersion = false) const; QString pathForImport(const Import &import); diff --git a/src/plugins/qmldesigner/designercore/include/nodeinstanceview.h b/src/plugins/qmldesigner/designercore/include/nodeinstanceview.h index 11017a27b1c..0a9af39fc5e 100644 --- a/src/plugins/qmldesigner/designercore/include/nodeinstanceview.h +++ b/src/plugins/qmldesigner/designercore/include/nodeinstanceview.h @@ -85,7 +85,7 @@ public: void fileUrlChanged(const QUrl &oldUrl, const QUrl &newUrl) override; void nodeIdChanged(const ModelNode& node, const QString& newId, const QString& oldId) override; void nodeOrderChanged(const NodeListProperty &listProperty) override; - void importsChanged(const QList &addedImports, const QList &removedImports) override; + void importsChanged(const Imports &addedImports, const Imports &removedImports) override; void auxiliaryDataChanged(const ModelNode &node, AuxiliaryDataKeyView key, const QVariant &data) override; diff --git a/src/plugins/qmldesigner/designercore/include/rewriterview.h b/src/plugins/qmldesigner/designercore/include/rewriterview.h index 9387fdb345e..95e2e51d8a1 100644 --- a/src/plugins/qmldesigner/designercore/include/rewriterview.h +++ b/src/plugins/qmldesigner/designercore/include/rewriterview.h @@ -84,7 +84,7 @@ public: void rewriterBeginTransaction() override; void rewriterEndTransaction() override; - void importsChanged(const QList &addedImports, const QList &removedImports) override; + void importsChanged(const Imports &addedImports, const Imports &removedImports) override; TextModifier *textModifier() const; void setTextModifier(TextModifier *textModifier); diff --git a/src/plugins/qmldesigner/designercore/include/subcomponentmanager.h b/src/plugins/qmldesigner/designercore/include/subcomponentmanager.h index 207f1b7104b..dbc89c1cccd 100644 --- a/src/plugins/qmldesigner/designercore/include/subcomponentmanager.h +++ b/src/plugins/qmldesigner/designercore/include/subcomponentmanager.h @@ -27,7 +27,7 @@ public: explicit SubComponentManager(Model *model, class ExternalDependenciesInterface &externalDependencies); - void update(const QUrl &fileUrl, const QList &imports); + void update(const QUrl &fileUrl, const Imports &imports); void addAndParseImport(const Import &import); QStringList qmlFiles() const; @@ -53,7 +53,7 @@ private: // functions private: // variables QFileSystemWatcher m_watcher; - QList m_imports; + Imports m_imports; // key: canonical directory path QMultiHash m_dirToQualifier; QUrl m_filePath; diff --git a/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp b/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp index ebe5acd9d28..bd28ce2069e 100644 --- a/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp +++ b/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp @@ -625,7 +625,7 @@ void NodeInstanceView::nodeOrderChanged(const NodeListProperty &listProperty) m_nodeInstanceServer->reparentInstances(ReparentInstancesCommand(containerList)); } -void NodeInstanceView::importsChanged(const QList &/*addedImports*/, const QList &/*removedImports*/) +void NodeInstanceView::importsChanged(const Imports &/*addedImports*/, const Imports &/*removedImports*/) { restartProcess(); } diff --git a/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp b/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp index f4b505c4e70..bebd01b422f 100644 --- a/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp +++ b/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp @@ -768,7 +768,7 @@ NodeMetaInfoPrivate::NodeMetaInfoPrivate(Model *model, TypeName type, int maj, i } } else { m_isFileComponent = true; - const Imports *imports = context()->imports(document()); + const auto *imports = context()->imports(document()); const ImportInfo importInfo = imports->info(lookupNameComponent().constLast(), context().data()); @@ -791,7 +791,7 @@ NodeMetaInfoPrivate::NodeMetaInfoPrivate(Model *model, TypeName type, int maj, i } else { // Special case for aliased types for the rewriter - const Imports *imports = context()->imports(document()); + const auto *imports = context()->imports(document()); const ImportInfo importInfo = imports->info(QString::fromUtf8(m_qualfiedTypeName), context().data()); if (importInfo.isValid()) { @@ -1198,7 +1198,7 @@ QString NodeMetaInfoPrivate::importDirectoryPath() const ModelManagerInterface *modelManager = ModelManagerInterface::instance(); if (isValid()) { - const Imports *imports = context()->imports(document()); + const auto *imports = context()->imports(document()); ImportInfo importInfo = imports->info(lookupNameComponent().constLast(), context().data()); if (importInfo.type() == ImportType::Directory) { @@ -1333,7 +1333,7 @@ void NodeMetaInfoPrivate::setupPrototypes() m_prototypes.append(description); } else { if (context()->lookupType(document(), {ov->className()})) { - const Imports *allImports = context()->imports(document()); + const auto *allImports = context()->imports(document()); ImportInfo importInfo = allImports->info(description.className, context().data()); if (importInfo.isValid()) { diff --git a/src/plugins/qmldesigner/designercore/metainfo/subcomponentmanager.cpp b/src/plugins/qmldesigner/designercore/metainfo/subcomponentmanager.cpp index e7feb0d4454..0afd0d735c7 100644 --- a/src/plugins/qmldesigner/designercore/metainfo/subcomponentmanager.cpp +++ b/src/plugins/qmldesigner/designercore/metainfo/subcomponentmanager.cpp @@ -491,7 +491,7 @@ QStringList SubComponentManager::qmlFiles() const return m_watcher.files(); } -void SubComponentManager::update(const QUrl &filePath, const QList &imports) +void SubComponentManager::update(const QUrl &filePath, const Imports &imports) { if (debug) qDebug() << Q_FUNC_INFO << filePath << imports.size(); diff --git a/src/plugins/qmldesigner/designercore/model/abstractview.cpp b/src/plugins/qmldesigner/designercore/model/abstractview.cpp index b9f0315c196..ec6ddb6aa43 100644 --- a/src/plugins/qmldesigner/designercore/model/abstractview.cpp +++ b/src/plugins/qmldesigner/designercore/model/abstractview.cpp @@ -315,15 +315,15 @@ void AbstractView::nodeTypeChanged(const ModelNode & /*node*/, const TypeName & } -void AbstractView::importsChanged(const QList &/*addedImports*/, const QList &/*removedImports*/) +void AbstractView::importsChanged(const Imports &/*addedImports*/, const Imports &/*removedImports*/) { } -void AbstractView::possibleImportsChanged(const QList &/*possibleImports*/) +void AbstractView::possibleImportsChanged(const Imports &/*possibleImports*/) { } -void AbstractView::usedImportsChanged(const QList &/*usedImports*/) +void AbstractView::usedImportsChanged(const Imports &/*usedImports*/) { } @@ -893,7 +893,7 @@ QmlTimeline AbstractView::currentTimeline() const static int getMinorVersionFromImport(const Model *model) { - const QList imports = model->imports(); + const Imports imports = model->imports(); for (const Import &import : imports) { if (import.isLibraryImport() && import.url() == "QtQuick") { const QString versionString = import.version(); @@ -909,7 +909,7 @@ static int getMinorVersionFromImport(const Model *model) static int getMajorVersionFromImport(const Model *model) { - const QList imports = model->imports(); + const Imports imports = model->imports(); for (const Import &import : imports) { if (import.isLibraryImport() && import.url() == QStringLiteral("QtQuick")) { const QString versionString = import.version(); diff --git a/src/plugins/qmldesigner/designercore/model/model.cpp b/src/plugins/qmldesigner/designercore/model/model.cpp index 13197666fc4..33571cc3ce1 100644 --- a/src/plugins/qmldesigner/designercore/model/model.cpp +++ b/src/plugins/qmldesigner/designercore/model/model.cpp @@ -108,10 +108,10 @@ void ModelPrivate::detachAllViews() } } -void ModelPrivate::changeImports(const QList &toBeAddedImportList, - const QList &toBeRemovedImportList) +void ModelPrivate::changeImports(const Imports &toBeAddedImportList, + const Imports &toBeRemovedImportList) { - QList removedImportList; + Imports removedImportList; for (const Import &import : toBeRemovedImportList) { if (m_imports.contains(import)) { removedImportList.append(import); @@ -119,7 +119,7 @@ void ModelPrivate::changeImports(const QList &toBeAddedImportList, } } - QList addedImportList; + Imports addedImportList; for (const Import &import : toBeAddedImportList) { if (!m_imports.contains(import)) { addedImportList.append(import); @@ -131,8 +131,7 @@ void ModelPrivate::changeImports(const QList &toBeAddedImportList, notifyImportsChanged(addedImportList, removedImportList); } -void ModelPrivate::notifyImportsChanged(const QList &addedImports, - const QList &removedImports) +void ModelPrivate::notifyImportsChanged(const Imports &addedImports, const Imports &removedImports) { bool resetModel = false; QString description; @@ -157,7 +156,7 @@ void ModelPrivate::notifyImportsChanged(const QList &addedImports, resetModelByRewriter(description); } -void ModelPrivate::notifyPossibleImportsChanged(const QList &possibleImports) +void ModelPrivate::notifyPossibleImportsChanged(const Imports &possibleImports) { for (const QPointer &view : enabledViews()) { Q_ASSERT(view != nullptr); @@ -165,7 +164,7 @@ void ModelPrivate::notifyPossibleImportsChanged(const QList &possibleImp } } -void ModelPrivate::notifyUsedImportsChanged(const QList &usedImports) +void ModelPrivate::notifyUsedImportsChanged(const Imports &usedImports) { for (const QPointer &view : enabledViews()) { Q_ASSERT(view != nullptr); @@ -1400,28 +1399,27 @@ Model::Model(const TypeName &typeName, int major, int minor, Model *metaInfoProx Model::~Model() = default; -const QList &Model::imports() const +const Imports &Model::imports() const { return d->imports(); } -const QList &Model::possibleImports() const +const Imports &Model::possibleImports() const { return d->m_possibleImportList; } -const QList &Model::usedImports() const +const Imports &Model::usedImports() const { return d->m_usedImportList; } -void Model::changeImports(const QList &importsToBeAdded, - const QList &importsToBeRemoved) +void Model::changeImports(const Imports &importsToBeAdded, const Imports &importsToBeRemoved) { d->changeImports(importsToBeAdded, importsToBeRemoved); } -void Model::setPossibleImports(const QList &possibleImports) +void Model::setPossibleImports(const Imports &possibleImports) { if (d->m_possibleImportList != possibleImports) { d->m_possibleImportList = possibleImports; @@ -1429,7 +1427,7 @@ void Model::setPossibleImports(const QList &possibleImports) } } -void Model::setUsedImports(const QList &usedImports) +void Model::setUsedImports(const Imports &usedImports) { if (d->m_usedImportList != usedImports) { d->m_usedImportList = usedImports; diff --git a/src/plugins/qmldesigner/designercore/model/model_p.h b/src/plugins/qmldesigner/designercore/model/model_p.h index b08b7388518..95985482548 100644 --- a/src/plugins/qmldesigner/designercore/model/model_p.h +++ b/src/plugins/qmldesigner/designercore/model/model_p.h @@ -202,13 +202,13 @@ public: void resetModelByRewriter(const QString &description); // Imports: - const QList &imports() const { return m_imports; } + const Imports &imports() const { return m_imports; } void addImport(const Import &import); void removeImport(const Import &import); - void changeImports(const QList &importsToBeAdded, const QList &importToBeRemoved); - void notifyImportsChanged(const QList &addedImports, const QList &removedImports); - void notifyPossibleImportsChanged(const QList &possibleImports); - void notifyUsedImportsChanged(const QList &usedImportsChanged); + void changeImports(const Imports &importsToBeAdded, const Imports &importToBeRemoved); + void notifyImportsChanged(const Imports &addedImports, const Imports &removedImports); + void notifyPossibleImportsChanged(const Imports &possibleImports); + void notifyUsedImportsChanged(const Imports &usedImportsChanged); //node state property manipulation void addProperty(const InternalNodePointer &node, const PropertyName &name); @@ -269,9 +269,9 @@ private: private: Model *m_model = nullptr; MetaInfo m_metaInfo; - QList m_imports; - QList m_possibleImportList; - QList m_usedImportList; + Imports m_imports; + Imports m_possibleImportList; + Imports m_usedImportList; QList> m_viewList; QList> m_enabledViewList; QList m_selectedInternalNodeList; diff --git a/src/plugins/qmldesigner/designercore/model/modelmerger.cpp b/src/plugins/qmldesigner/designercore/model/modelmerger.cpp index 75cdcf83582..30d100f3503 100644 --- a/src/plugins/qmldesigner/designercore/model/modelmerger.cpp +++ b/src/plugins/qmldesigner/designercore/model/modelmerger.cpp @@ -173,7 +173,7 @@ ModelNode ModelMerger::insertModel(const ModelNode &modelNode, const MergePredic return {}; RewriterTransaction transaction(view()->beginRewriterTransaction(QByteArrayLiteral("ModelMerger::insertModel"))); - QList newImports; + Imports newImports; for (const Import &import : modelNode.model()->imports()) { if (!view()->model()->hasImport(import, true, true)) diff --git a/src/plugins/qmldesigner/designercore/model/rewriterview.cpp b/src/plugins/qmldesigner/designercore/model/rewriterview.cpp index d8088de64be..53a0f4de596 100644 --- a/src/plugins/qmldesigner/designercore/model/rewriterview.cpp +++ b/src/plugins/qmldesigner/designercore/model/rewriterview.cpp @@ -261,7 +261,7 @@ void RewriterView::nodeReparented(const ModelNode &node, const NodeAbstractPrope applyChanges(); } -void RewriterView::importsChanged(const QList &addedImports, const QList &removedImports) +void RewriterView::importsChanged(const Imports &addedImports, const Imports &removedImports) { for (const Import &import : addedImports) importAdded(import); diff --git a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp index 236b690bb4f..bb09d664a77 100644 --- a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp +++ b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp @@ -19,6 +19,7 @@ #include "signalhandlerproperty.h" #include "variantproperty.h" #include +#include #include #include @@ -44,6 +45,7 @@ #include #include +#include using namespace LanguageUtils; using namespace QmlJS; @@ -716,7 +718,7 @@ bool TextToModelMerger::isActive() const void TextToModelMerger::setupImports(const Document::Ptr &doc, DifferenceHandler &differenceHandler) { - QList existingImports = m_rewriterView->model()->imports(); + Imports existingImports = m_rewriterView->model()->imports(); m_hasVersionlessImport = false; @@ -756,162 +758,125 @@ void TextToModelMerger::setupImports(const Document::Ptr &doc, differenceHandler.importAbsentInQMl(import); } -static bool isLatestImportVersion(const ImportKey &importKey, const QHash &filteredPossibleImportKeys) +namespace { + +bool skipByMetaInfo(QStringView moduleName, const QStringList &skipModuleNames) { - return !filteredPossibleImportKeys.contains(importKey.path()) - || filteredPossibleImportKeys.value(importKey.path()).majorVersion < importKey.majorVersion - || (filteredPossibleImportKeys.value(importKey.path()).majorVersion == importKey.majorVersion - && filteredPossibleImportKeys.value(importKey.path()).minorVersion < importKey.minorVersion); + return std::any_of(skipModuleNames.begin(), + skipModuleNames.end(), + [&](const QString &skipModuleName) { + return moduleName.contains(skipModuleName); + }); } -static bool filterByMetaInfo(const ImportKey &importKey, Model *model) +class StartsWith : public QStringView { - if (model) { - for (const QString &filter : model->metaInfo().itemLibraryInfo()->blacklistImports()) { - if (importKey.libraryQualifiedPath().contains(filter)) - return true; +public: + using QStringView::QStringView; + bool operator()(QStringView moduleName) const { return moduleName.startsWith(*this); } +}; + +class EndsWith : public QStringView +{ +public: + using QStringView::QStringView; + bool operator()(QStringView moduleName) const { return moduleName.endsWith(*this); } +}; + +class StartsAndEndsWith : public std::pair +{ +public: + using Base = std::pair; + using Base::Base; + bool operator()(QStringView moduleName) const + { + return moduleName.startsWith(first) && moduleName.endsWith(second); + } +}; + +class Equals : public QStringView +{ +public: + using QStringView::QStringView; + bool operator()(QStringView moduleName) const { return moduleName == *this; } +}; + +constexpr auto skipModules = std::make_tuple(EndsWith(u".impl"), + StartsWith(u"QML"), + StartsWith(u"QtQml"), + StartsAndEndsWith(u"QtQuick", u".PrivateWidgets"), + EndsWith(u".private"), + EndsWith(u".Private"), + Equals(u"QtQuick.Particles"), + Equals(u"QtQuick.Dialogs"), + Equals(u"QtQuick.Controls.Styles"), + Equals(u"QtNfc"), + Equals(u"Qt.WebSockets"), + Equals(u"QtWebkit"), + Equals(u"QtLocation"), + Equals(u"QtWebChannel"), + Equals(u"QtWinExtras"), + Equals(u"QtPurchasing"), + Equals(u"QtBluetooth"), + Equals(u"Enginio")); + +bool skipModule(QStringView moduleName) +{ + return std::apply([=](const auto &...skipModule) { return (skipModule(moduleName) || ...); }, + skipModules); +} + +bool skipModule(QStringView moduleName, const QStringList &skipModuleNames) +{ + return skipModule(moduleName) || skipByMetaInfo(moduleName, skipModuleNames); +} + +void collectPossibleFileImports(const QString &checkPath, + QSet usedImportsSet, + QList &possibleImports) +{ + const QStringList qmlList("*.qml"); + const QStringList qmldirList("qmldir"); + const QChar delimeter('/'); + + if (QFileInfo(checkPath).isRoot()) + return; + + const QStringList entries = QDir(checkPath).entryList(QDir::Dirs | QDir::NoDot | QDir::NoDotDot); + const QString checkPathDelim = checkPath + delimeter; + for (const QString &entry : entries) { + QDir dir(checkPathDelim + entry); + const QString dirPath = dir.path(); + if (!dir.entryInfoList(qmlList, QDir::Files).isEmpty() + && dir.entryInfoList(qmldirList, QDir::Files).isEmpty() + && !usedImportsSet.contains(dirPath)) { + const QString importName = dir.path().mid(checkPath.size() + 1); + QmlDesigner::Import import = QmlDesigner::Import::createFileImport(importName); + possibleImports.append(import); } - + collectPossibleFileImports(dirPath, usedImportsSet, possibleImports); } - - return false; } -static bool isBlacklistImport(const ImportKey &importKey, Model *model) -{ - const QString &importPathFirst = importKey.splitPath.constFirst(); - const QString &importPathLast = importKey.splitPath.constLast(); - return importPathFirst == QStringLiteral("") - || importPathFirst == QStringLiteral("QML") - || importPathFirst == QStringLiteral("QtQml") - || (importPathFirst == QStringLiteral("QtQuick") && importPathLast == QStringLiteral("PrivateWidgets")) - || importPathLast == QStringLiteral("Private") - || importPathLast == QStringLiteral("private") - || importKey.libraryQualifiedPath() == QStringLiteral("QtQuick.Particles") //Unsupported - || importKey.libraryQualifiedPath() == QStringLiteral("QtQuick.Dialogs") //Unsupported - || importKey.libraryQualifiedPath() == QStringLiteral("QtQuick.Controls.Styles") //Unsupported - || importKey.libraryQualifiedPath() == QStringLiteral("QtNfc") //Unsupported - || importKey.libraryQualifiedPath() == QStringLiteral("Qt.WebSockets") - || importKey.libraryQualifiedPath() == QStringLiteral("QtWebkit") - || importKey.libraryQualifiedPath() == QStringLiteral("QtLocation") - || importKey.libraryQualifiedPath() == QStringLiteral("QtWebChannel") - || importKey.libraryQualifiedPath() == QStringLiteral("QtWinExtras") - || importKey.libraryQualifiedPath() == QStringLiteral("QtPurchasing") - || importKey.libraryQualifiedPath() == QStringLiteral("QtBluetooth") - || importKey.libraryQualifiedPath() == QStringLiteral("Enginio") - - || filterByMetaInfo(importKey, model); -} - -static QHash filterPossibleImportKeys(const QSet &possibleImportKeys, Model *model) -{ - QHash filteredPossibleImportKeys; - for (const ImportKey &importKey : possibleImportKeys) { - if (isLatestImportVersion(importKey, filteredPossibleImportKeys) && !isBlacklistImport(importKey, model)) - filteredPossibleImportKeys.insert(importKey.path(), importKey); - } - - return filteredPossibleImportKeys; -} - -static void removeUsedImports(QHash &filteredPossibleImportKeys, const QList &usedImports) -{ - for (const QmlJS::Import &import : usedImports) - filteredPossibleImportKeys.remove(import.info.path()); -} - -static QList generatePossibleFileImports(const QString &path, - const QList &usedImports) +QList generatePossibleFileImports(const QString &path, + const QList &usedImports) { QSet usedImportsSet; for (const QmlJS::Import &i : usedImports) usedImportsSet.insert(i.info.path()); QList possibleImports; - const QStringList qmlList("*.qml"); - const QStringList qmldirList("qmldir"); QStringList fileImportPaths; - const QChar delimeter('/'); - std::function checkDir; - checkDir = [&](const QString &checkPath) { - - if (QFileInfo(checkPath).isRoot()) - return; - - const QStringList entries = QDir(checkPath).entryList(QDir::Dirs | QDir::NoDot | QDir::NoDotDot); - const QString checkPathDelim = checkPath + delimeter; - for (const QString &entry : entries) { - QDir dir(checkPathDelim + entry); - const QString dirPath = dir.path(); - if (!dir.entryInfoList(qmlList, QDir::Files).isEmpty() - && dir.entryInfoList(qmldirList, QDir::Files).isEmpty() - && !usedImportsSet.contains(dirPath)) { - const QString importName = dir.path().mid(path.size() + 1); - QmlDesigner::Import import = QmlDesigner::Import::createFileImport(importName); - possibleImports.append(import); - } - checkDir(dirPath); - } - }; - checkDir(path); + collectPossibleFileImports(path, usedImportsSet, possibleImports); return possibleImports; } -static QList generatePossibleLibraryImports(const QHash &filteredPossibleImportKeys) -{ - QList possibleImports; - QSet controlsImplVersions; - bool hasVersionedControls = false; - bool hasVersionlessControls = false; - const QString controlsName = "QtQuick.Controls"; - const QString controlsImplName = "QtQuick.Controls.impl"; +} // namespace - for (const ImportKey &importKey : filteredPossibleImportKeys) { - QString libraryName = importKey.splitPath.join(QLatin1Char('.')); - int majorVersion = importKey.majorVersion; - if (majorVersion >= 0) { - int minorVersion = (importKey.minorVersion == LanguageUtils::ComponentVersion::NoVersion) ? 0 : importKey.minorVersion; - - if (libraryName.contains("QtQuick.Studio")) { - majorVersion = 1; - minorVersion = 0; - } - - QString version = QStringLiteral("%1.%2").arg(majorVersion).arg(minorVersion); - if (!libraryName.endsWith(".impl")) - possibleImports.append(QmlDesigner::Import::createLibraryImport(libraryName, version)); - - // In Qt6, QtQuick.Controls itself doesn't have any version as it has no types, - // so it never gets added normally to possible imports. - // We work around this by injecting corresponding QtQuick.Controls version for each - // found impl version, if no valid QtQuick.Controls versions are found. - if (!hasVersionedControls) { - if (libraryName == controlsImplName) - controlsImplVersions.insert(version); - else if (libraryName == controlsName) - hasVersionedControls = true; - } - } else if (!hasVersionlessControls && libraryName == controlsName) { - // If QtQuick.Controls module is not included even in non-versioned, it means - // QtQuick.Controls is either in use or not available at all, - // so we shouldn't inject it. - hasVersionlessControls = true; - } - } - - if (hasVersionlessControls && !hasVersionedControls && !controlsImplVersions.isEmpty()) { - for (const auto &version : std::as_const(controlsImplVersions)) - possibleImports.append(QmlDesigner::Import::createLibraryImport(controlsName, version)); - } - - - return possibleImports; -} - -void TextToModelMerger::setupPossibleImports(const QmlJS::Snapshot &snapshot, const QmlJS::ViewerContext &viewContext) +void TextToModelMerger::setupPossibleImports() { if (!m_rewriterView->possibleImportsEnabled()) return; @@ -919,27 +884,25 @@ void TextToModelMerger::setupPossibleImports(const QmlJS::Snapshot &snapshot, co static QUrl lastProjectUrl; auto &externalDependencies = m_rewriterView->externalDependencies(); auto projectUrl = externalDependencies.projectUrl(); + auto allUsedImports = m_scopeChain->context()->imports(m_document.data())->all(); - if (m_possibleImportKeys.isEmpty() || projectUrl != lastProjectUrl) - m_possibleImportKeys = snapshot.importDependencies()->libraryImports(viewContext); + if (m_possibleModules.isEmpty() || projectUrl != lastProjectUrl) { + const auto skipModuleNames = m_rewriterView->model()->metaInfo().itemLibraryInfo()->blacklistImports(); + ModuleScanner moduleScanner{ + [&](QStringView moduleName) { return skipModule(moduleName, skipModuleNames); }}; + moduleScanner.scan(m_rewriterView->externalDependencies().modulePaths()); + m_possibleModules = moduleScanner.modules(); + } lastProjectUrl = projectUrl; - QHash filteredPossibleImportKeys = filterPossibleImportKeys( - m_possibleImportKeys, m_rewriterView->model()); - - const QmlJS::Imports *imports = m_scopeChain->context()->imports(m_document.data()); - if (imports) - removeUsedImports(filteredPossibleImportKeys, imports->all()); - - QList possibleImports = generatePossibleLibraryImports(filteredPossibleImportKeys); + auto modules = m_possibleModules; if (document()->fileName() != "") - possibleImports.append( - generatePossibleFileImports(document()->path().toString(), imports->all())); + modules.append(generatePossibleFileImports(document()->path().toString(), allUsedImports)); if (m_rewriterView->isAttached()) - m_rewriterView->model()->setPossibleImports(possibleImports); + m_rewriterView->model()->setPossibleImports(modules); } void TextToModelMerger::setupUsedImports() @@ -951,7 +914,7 @@ void TextToModelMerger::setupUsedImports() const QList allImports = imports->all(); QSet usedImportsSet; - QList usedImports; + Imports usedImports; // populate usedImportsSet from current model nodes const QList allModelNodes = m_rewriterView->allModelNodes(); @@ -964,9 +927,6 @@ void TextToModelMerger::setupUsedImports() for (const QmlJS::Import &import : allImports) { QString version = import.info.version().toString(); - if (!import.info.version().isValid()) - version = getHighestPossibleImport(import.info.name()); - if (!import.info.name().isEmpty() && usedImportsSet.contains(import.info.name())) { if (import.info.type() == ImportType::Library) usedImports.append( @@ -1077,7 +1037,7 @@ bool TextToModelMerger::load(const QString &data, DifferenceHandler &differenceH collectLinkErrors(&errors, ctxt); } - setupPossibleImports(snapshot, m_vContext); + setupPossibleImports(); qCInfo(rewriterBenchmark) << "possible imports:" << time.elapsed(); @@ -1115,12 +1075,6 @@ bool TextToModelMerger::load(const QString &data, DifferenceHandler &differenceH setActive(false); - // Clear possible imports cache if code model hasn't settled yet - const int importKeysSize = m_possibleImportKeys.size(); - if (m_previousPossibleImportsSize != importKeysSize) - m_possibleImportKeys.clear(); - m_previousPossibleImportsSize = importKeysSize; - return true; } catch (Exception &e) { DocumentMessage error(&e); @@ -2387,8 +2341,8 @@ QList TextToModelMerger::getQMLSingletons() const void TextToModelMerger::clearPossibleImportKeys() { - m_possibleImportKeys.clear(); - m_previousPossibleImportsSize = -1; + m_possibleModules.clear(); + m_previousPossibleModulesSize = -1; } QString TextToModelMerger::textAt(const Document::Ptr &doc, @@ -2403,19 +2357,3 @@ QString TextToModelMerger::textAt(const Document::Ptr &doc, { return doc->source().mid(from.offset, to.end() - from.begin()); } - -QString TextToModelMerger::getHighestPossibleImport(const QString &importName) const -{ - QString version = "2.15"; - int maj = -1; - const auto imports = m_possibleImportKeys.values(); - for (const ImportKey &import : imports) { - if (importName == import.libraryQualifiedPath()) { - if (import.majorVersion > maj) { - version = QString("%1.%2").arg(import.majorVersion).arg(import.minorVersion); - maj = import.majorVersion; - } - } - } - return version; -} diff --git a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.h b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.h index 5ed2e24c7a4..50b3a423678 100644 --- a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.h +++ b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.h @@ -37,7 +37,7 @@ public: bool isActive() const; void setupImports(const QmlJS::Document::Ptr &doc, DifferenceHandler &differenceHandler); - void setupPossibleImports(const QmlJS::Snapshot &snapshot, const QmlJS::ViewerContext &viewContext); + void setupPossibleImports(); void setupUsedImports(); bool load(const QString &data, DifferenceHandler &differenceHandler); @@ -137,8 +137,6 @@ private: const QmlJS::SourceLocation &from, const QmlJS::SourceLocation &to); - QString getHighestPossibleImport(const QString &importName) const; - private: RewriterView *m_rewriterView; bool m_isActive; @@ -150,8 +148,8 @@ private: QSet m_clearImplicitComponentList; QmlJS::ViewerContext m_vContext; QSet > m_qrcMapping; - QSet m_possibleImportKeys; - int m_previousPossibleImportsSize = -1; + Imports m_possibleModules; + int m_previousPossibleModulesSize = -1; bool m_hasVersionlessImport = false; }; diff --git a/src/plugins/qmldesigner/designercore/projectstorage/modulescanner.cpp b/src/plugins/qmldesigner/designercore/projectstorage/modulescanner.cpp new file mode 100644 index 00000000000..5fd70bfdd35 --- /dev/null +++ b/src/plugins/qmldesigner/designercore/projectstorage/modulescanner.cpp @@ -0,0 +1,73 @@ +// 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 "modulescanner.h" + +#ifdef QDS_HAS_QMLPRIVATE +#include +#endif + +#include + +#include + +namespace QmlDesigner { + +namespace { + +std::optional contentAsQString(const QString &filePath) +{ + QFile file{filePath}; + if (file.open(QIODevice::ReadOnly)) + return {QString::fromUtf8(file.readAll())}; + + return {}; +} + +} // namespace + +void ModuleScanner::scan(const QStringList &modulePaths) +{ + for (const QString &modulePath : modulePaths) + scan(modulePath.toStdString()); +} + +void ModuleScanner::scan(std::string_view modulePath) +{ +#ifdef QDS_HAS_QMLPRIVATE + try { + const std::filesystem::path installDirectoryPath{modulePath}; + + auto current = std::filesystem::recursive_directory_iterator{installDirectoryPath}; + auto end = std::filesystem::end(current); + + for (; current != end; ++current) { + const auto &entry = *current; + auto path = entry.path(); + + if (path.filename() == "qmldir") { + QQmlDirParser parser; + + auto content = contentAsQString(QString::fromStdU16String(path.u16string())); + if (!content) + continue; + + bool hasError = parser.parse(*content); + if (hasError) + continue; + + auto moduleName = parser.typeNamespace(); + + if (moduleName.isEmpty() || m_skip(moduleName)) + continue; + + m_modules.push_back(Import::createLibraryImport(moduleName)); + } + } + } catch (const std::filesystem::filesystem_error &) { + return; + } +#endif +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/projectstorage/modulescanner.h b/src/plugins/qmldesigner/designercore/projectstorage/modulescanner.h new file mode 100644 index 00000000000..3c03f9cd36d --- /dev/null +++ b/src/plugins/qmldesigner/designercore/projectstorage/modulescanner.h @@ -0,0 +1,37 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include + +#include + +#include + +namespace QmlDesigner { + +class QMLDESIGNERCORE_EXPORT ModuleScanner +{ +public: + using SkipFunction = std::function; + + ModuleScanner(SkipFunction skip) + : m_skip{std::move(skip)} + { + m_modules.reserve(128); + } + + void scan(const QStringList &modulePaths); + + const Imports &modules() const { return m_modules; } + +private: + void scan(std::string_view modulePaths); + +private: + SkipFunction m_skip; + Imports m_modules; +}; + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.cpp b/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.cpp index 97aac6a8577..894299e7958 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.cpp +++ b/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.cpp @@ -10,7 +10,7 @@ #include -#ifdef QDS_HAS_QMLDOM +#ifdef QDS_HAS_QMLPRIVATE #include #endif @@ -19,7 +19,7 @@ namespace QmlDesigner { -#ifdef QDS_HAS_QMLDOM +#ifdef QDS_HAS_QMLPRIVATE namespace QmlDom = QQmlJS::Dom; diff --git a/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.h b/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.h index 744825aadc5..87fb03ddaba 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.h @@ -18,7 +18,7 @@ public: using ProjectStorage = QmlDesigner::ProjectStorage; using PathCache = QmlDesigner::SourcePathCache; -#ifdef QDS_HAS_QMLDOM +#ifdef QDS_HAS_QMLPRIVATE QmlDocumentParser(ProjectStorage &storage, PathCache &pathCache) : m_storage{storage} , m_pathCache{pathCache} @@ -35,7 +35,7 @@ public: private: // m_pathCache and m_storage are only used when compiled for QDS -#ifdef QDS_HAS_QMLDOM +#ifdef QDS_HAS_QMLPRIVATE ProjectStorage &m_storage; PathCache &m_pathCache; #endif diff --git a/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.cpp b/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.cpp index bca7b3279d8..3ed15639982 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.cpp +++ b/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.cpp @@ -8,7 +8,7 @@ #include -#ifdef QDS_HAS_QMLDOM +#ifdef QDS_HAS_QMLPRIVATE #include #include #endif @@ -20,7 +20,7 @@ namespace QmlDesigner { -#ifdef QDS_HAS_QMLDOM +#ifdef QDS_HAS_QMLPRIVATE namespace QmlDom = QQmlJS::Dom; diff --git a/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.h b/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.h index 902d3c5a44f..522e5d3292e 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.h @@ -24,7 +24,7 @@ public: using ProjectStorage = QmlDesigner::ProjectStorage; using PathCache = QmlDesigner::SourcePathCache; -#ifdef QDS_HAS_QMLDOM +#ifdef QDS_HAS_QMLPRIVATE QmlTypesParser(PathCache &pathCache, ProjectStorage &storage) : m_pathCache{pathCache} , m_storage{storage} @@ -41,7 +41,7 @@ public: private: // m_pathCache and m_storage are only used when compiled for QDS -#ifdef QDS_HAS_QMLDOM +#ifdef QDS_HAS_QMLPRIVATE PathCache &m_pathCache; ProjectStorage &m_storage; #endif diff --git a/src/plugins/qmldesigner/qmldesignerexternaldependencies.cpp b/src/plugins/qmldesigner/qmldesignerexternaldependencies.cpp index a3fdc762448..71c5379cda6 100644 --- a/src/plugins/qmldesigner/qmldesignerexternaldependencies.cpp +++ b/src/plugins/qmldesigner/qmldesignerexternaldependencies.cpp @@ -9,9 +9,11 @@ #include #include #include +#include #include #include #include +#include #include #include #include @@ -186,4 +188,50 @@ Utils::FilePath ExternalDependencies::qmlPuppetPath() const return puppetPath; } +namespace { + +QString qmlPath(ProjectExplorer::Target *target) +{ + auto kit = target->kit(); + + if (!kit) + return {}; + + auto qtVersion = QtSupport::QtKitAspect::qtVersion(kit); + if (!qtVersion) + return {}; + + return qtVersion->qmlPath().toString(); +} +} // namespace + +QStringList ExternalDependencies::modulePaths() const +{ + QStringList modulePaths; + + auto project = ProjectExplorer::SessionManager::startupProject(); + + if (!project) + return modulePaths; + + auto target = project->activeTarget(); + + if (!target) + return modulePaths; + + if (auto path = qmlPath(target); !path.isEmpty()) + modulePaths.push_back(path); + + const auto qmlBuildSystem = qobject_cast( + target->buildSystem()); + + if (!qmlBuildSystem) + return modulePaths; + + for (const QString &modulePath : qmlBuildSystem->customImportPaths()) + modulePaths.append(project->projectDirectory().pathAppended(modulePath).toString()); + + return modulePaths; +} + } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/qmldesignerexternaldependencies.h b/src/plugins/qmldesigner/qmldesignerexternaldependencies.h index 3e57847ce9d..b19ad0ae111 100644 --- a/src/plugins/qmldesigner/qmldesignerexternaldependencies.h +++ b/src/plugins/qmldesigner/qmldesignerexternaldependencies.h @@ -36,6 +36,7 @@ public: PuppetStartData puppetStartData(const class Model &model) const override; bool instantQmlTextUpdate() const override; Utils::FilePath qmlPuppetPath() const override; + QStringList modulePaths() const override; private: const DesignerSettings &m_designerSettings; diff --git a/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp b/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp index e6a02fecab7..67144a813b4 100644 --- a/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp +++ b/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp @@ -342,7 +342,7 @@ void projectQmldirPaths(::ProjectExplorer::Target *target, QStringList &qmldirPa qmldirPaths.push_back(QDir::cleanPath(pojectDirectory.absoluteFilePath(importPath)) + "/qmldir"); } -#ifdef QDS_HAS_QMLDOM +#ifdef QDS_HAS_QMLPRIVATE bool skipPath(const std::filesystem::path &path) { auto directory = path.filename(); @@ -359,25 +359,27 @@ bool skipPath(const std::filesystem::path &path) } #endif -void qtQmldirPaths([[maybe_unused]] ::ProjectExplorer::Target *target, - [[maybe_unused]] QStringList &qmldirPaths) +void qtQmldirPaths(::ProjectExplorer::Target *target, QStringList &qmldirPaths) { -#ifdef QDS_HAS_QMLDOM - const QString installDirectory = qmlPath(target).toString(); +#ifdef QDS_HAS_QMLPRIVATE - const std::filesystem::path installDirectoryPath{installDirectory.toStdString()}; + if (useProjectStorage()) { + const QString installDirectory = qmlPath(target).toString(); - auto current = std::filesystem::recursive_directory_iterator{installDirectoryPath}; - auto end = std::filesystem::end(current); - for (; current != end; ++current) { - const auto &entry = *current; - auto path = entry.path(); - if (current.depth() < 3 && !current->is_regular_file() && skipPath(path)) { - current.disable_recursion_pending(); - continue; - } - if (path.filename() == "qmldir") { - qmldirPaths.push_back(QString::fromStdU16String(path.generic_u16string())); + const std::filesystem::path installDirectoryPath{installDirectory.toStdString()}; + + auto current = std::filesystem::recursive_directory_iterator{installDirectoryPath}; + auto end = std::filesystem::end(current); + for (; current != end; ++current) { + const auto &entry = *current; + auto path = entry.path(); + if (current.depth() < 3 && !current->is_regular_file() && skipPath(path)) { + current.disable_recursion_pending(); + continue; + } + if (path.filename() == "qmldir") { + qmldirPaths.push_back(QString::fromStdU16String(path.generic_u16string())); + } } } #endif diff --git a/tests/auto/qml/qmldesigner/coretests/tst_testcore.cpp b/tests/auto/qml/qmldesigner/coretests/tst_testcore.cpp index 7850d4512a0..0efa20ff8a2 100644 --- a/tests/auto/qml/qmldesigner/coretests/tst_testcore.cpp +++ b/tests/auto/qml/qmldesigner/coretests/tst_testcore.cpp @@ -160,6 +160,7 @@ public: PuppetStartData puppetStartData(const class Model &) const override { return {}; } bool instantQmlTextUpdate() const override { return true; } Utils::FilePath qmlPuppetPath() const override { return {}; } + QStringList modulePaths() const override { return {}; } public: QSettings qsettings; @@ -1074,10 +1075,10 @@ void tst_TestCore::testRewriterChangeImports() // Import webkitImport = Import::createLibraryImport("QtWebKit", "1.0"); - QList importList; + Imports importList; importList << webkitImport; - model->changeImports(importList, QList()); + model->changeImports(importList, Imports()); const QLatin1String qmlWithImport("\n" "import QtQuick 2.1\n" @@ -1086,7 +1087,7 @@ void tst_TestCore::testRewriterChangeImports() "Rectangle {}\n"); QCOMPARE(textEdit.toPlainText(), qmlWithImport); - model->changeImports(QList(), importList); + model->changeImports(Imports(), importList); QCOMPARE(model->imports().size(), 1); QCOMPARE(model->imports().first(), Import::createLibraryImport("QtQuick", "2.1")); @@ -1100,7 +1101,7 @@ void tst_TestCore::testRewriterChangeImports() Import webkitImportAlias = Import::createLibraryImport("QtWebKit", "1.0", "Web"); - model->changeImports(QList() << webkitImportAlias, QList() << webkitImport); + model->changeImports(Imports() << webkitImportAlias, Imports() << webkitImport); const QLatin1String qmlWithAliasImport("\n" "import QtQuick 2.1\n" @@ -1109,7 +1110,7 @@ void tst_TestCore::testRewriterChangeImports() "Rectangle {}\n"); QCOMPARE(textEdit.toPlainText(), qmlWithAliasImport); - model->changeImports(QList(), QList() << webkitImportAlias); + model->changeImports(Imports(), Imports() << webkitImportAlias); QCOMPARE(model->imports().first(), Import::createLibraryImport("QtQuick", "2.1")); QCOMPARE(textEdit.toPlainText(), qmlString); diff --git a/tests/unit/unittest/CMakeLists.txt b/tests/unit/unittest/CMakeLists.txt index 3df9686e906..8d3898d7f7c 100644 --- a/tests/unit/unittest/CMakeLists.txt +++ b/tests/unit/unittest/CMakeLists.txt @@ -25,7 +25,7 @@ add_qtc_test(unittest GTEST BEFORE "../mockup/qmldesigner/designercore/include" DEPENDS Qt::Core Qt::Network Qt::Widgets - Qt::Xml Qt::Concurrent Qt::Qml Qt::Gui + Qt::Xml Qt::Concurrent Qt::QmlPrivate Qt::Gui Qt6Core5Compat QmlJS Sqlite SqliteC Googletest DEFINES @@ -36,6 +36,7 @@ add_qtc_test(unittest GTEST QTC_RESOURCE_DIR="${CMAKE_CURRENT_LIST_DIR}/../../../share/qtcreator" TESTDATA_DIR="${CMAKE_CURRENT_BINARY_DIR}/data" TEST_RELATIVE_LIBEXEC_PATH="${TEST_RELATIVE_LIBEXEC_PATH}" + QT6_INSTALL_PREFIX="${QT6_INSTALL_PREFIX}" SOURCES abstractviewmock.h compare-operators.h @@ -97,6 +98,7 @@ add_qtc_test(unittest GTEST mockimagecachestorage.h asynchronousexplicitimagecache-test.cpp asynchronousimagefactory-test.cpp + modulescanner-test.cpp ) if (NOT TARGET unittest) @@ -250,6 +252,7 @@ extend_qtc_test(unittest projectstorage/filesystem.cpp projectstorage/filesystem.h projectstorage/filestatus.h projectstorage/filestatuscache.cpp projectstorage/filestatuscache.h + projectstorage/modulescanner.cpp projectstorage/modulescanner.h projectstorage/nonlockingmutex.h projectstorage/projectstorageexceptions.cpp projectstorage/projectstorageexceptions.h projectstorage/projectstorageinterface.h @@ -335,7 +338,7 @@ extend_qtc_test(unittest CONDITION TARGET Qt6::QmlDomPrivate AND TARGET Qt6::QmlCompilerPrivate AND Qt6_VERSION VERSION_GREATER_EQUAL 6.5.0 SOURCES_PREFIX "${QmlDesignerDir}/designercore" DEPENDS Qt6::QmlDomPrivate Qt6::QmlCompilerPrivate - DEFINES QDS_HAS_QMLDOM + DEFINES QDS_HAS_QMLPRIVATE SOURCES projectstorage/qmldocumentparser.cpp projectstorage/qmldocumentparser.h projectstorage/qmltypesparser.cpp projectstorage/qmltypesparser.h diff --git a/tests/unit/unittest/google-using-declarations.h b/tests/unit/unittest/google-using-declarations.h index 67e887c696d..85fc82282f6 100644 --- a/tests/unit/unittest/google-using-declarations.h +++ b/tests/unit/unittest/google-using-declarations.h @@ -5,7 +5,6 @@ #include - using testing::_; using testing::A; using testing::AllOf; @@ -21,6 +20,7 @@ using testing::ByRef; using testing::ContainerEq; using testing::Contains; using testing::ElementsAre; +using testing::EndsWith; using testing::Eq; using testing::Exactly; using testing::Field; @@ -29,7 +29,6 @@ using testing::Gt; using testing::HasSubstr; using testing::InSequence; using testing::Invoke; -using testing::IsEmpty; using testing::IsNull; using testing::Le; using testing::Lt; diff --git a/tests/unit/unittest/modulescanner-test.cpp b/tests/unit/unittest/modulescanner-test.cpp new file mode 100644 index 00000000000..bd72d3db901 --- /dev/null +++ b/tests/unit/unittest/modulescanner-test.cpp @@ -0,0 +1,53 @@ +// 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 "googletest.h" + +#include + +#include + +namespace { + +template +auto UrlProperty(const Matcher &matcher) +{ + return Property(&QmlDesigner::Import::url, matcher); +} + +class ModuleScanner : public testing::Test +{ +protected: + QmlDesigner::ModuleScanner scanner{ + [](QStringView moduleName) { return moduleName.endsWith(u"impl"); }}; +}; + +TEST_F(ModuleScanner, ReturnEmptyOptionalForWrongPath) +{ + scanner.scan(QStringList{""}); + + ASSERT_THAT(scanner.modules(), IsEmpty()); +} + +TEST_F(ModuleScanner, GetQtQuick) +{ + scanner.scan(QStringList{QT6_INSTALL_PREFIX}); + + ASSERT_THAT(scanner.modules(), Contains(UrlProperty("QtQuick"))); +} + +TEST_F(ModuleScanner, SkipEmptyModules) +{ + scanner.scan(QStringList{QT6_INSTALL_PREFIX}); + + ASSERT_THAT(scanner.modules(), Not(Contains(UrlProperty(IsEmpty())))); +} + +TEST_F(ModuleScanner, UseSkipFunction) +{ + scanner.scan(QStringList{QT6_INSTALL_PREFIX}); + + ASSERT_THAT(scanner.modules(), Not(Contains(UrlProperty(EndsWith(QStringView{u"impl"}))))); +} + +} // namespace diff --git a/tests/unit/unittest/smallstring-test.cpp b/tests/unit/unittest/smallstring-test.cpp index d8321ec98f4..6364bd9c646 100644 --- a/tests/unit/unittest/smallstring-test.cpp +++ b/tests/unit/unittest/smallstring-test.cpp @@ -9,8 +9,6 @@ #include #include -using namespace ::testing; - using Utils::PathString; using Utils::SmallString; using Utils::SmallStringLiteral; diff --git a/tests/unit/unittest/unittest-matchers.h b/tests/unit/unittest/unittest-matchers.h index 12f47074835..0f59a5e79a8 100644 --- a/tests/unit/unittest/unittest-matchers.h +++ b/tests/unit/unittest/unittest-matchers.h @@ -7,7 +7,7 @@ #include -namespace UnitTests { +namespace Internal { template class EndsWithMatcher @@ -46,10 +46,68 @@ private: const StringType m_suffix; }; -inline -testing::PolymorphicMatcher > -EndsWith(const Utils::SmallString &suffix) +class QStringEndsWithMatcher { - return testing::MakePolymorphicMatcher(EndsWithMatcher(suffix)); +public: + explicit QStringEndsWithMatcher(const QString &suffix) + : m_suffix(suffix) + {} + + template + bool MatchAndExplain(const MatcheeStringType &s, testing::MatchResultListener * /* listener */) const + { + return s.endsWith(m_suffix); + } + + void DescribeTo(::std::ostream *os) const + { + *os << "ends with " << testing::PrintToString(m_suffix); + } + + void DescribeNegationTo(::std::ostream *os) const + { + *os << "doesn't end with " << testing::PrintToString(m_suffix); + } + +private: + const QString m_suffix; +}; + +class IsEmptyMatcher : public testing::internal::IsEmptyMatcher +{ +public: + using Base = testing::internal::IsEmptyMatcher; + + using Base::MatchAndExplain; + + bool MatchAndExplain(const QString &s, testing::MatchResultListener *listener) const + { + if (s.isEmpty()) { + return true; + } + + *listener << "whose size is " << s.size(); + return false; + } + + void DescribeTo(std::ostream *os) const { *os << "is empty"; } + + void DescribeNegationTo(std::ostream *os) const { *os << "isn't empty"; } +}; + +} // namespace Internal + +inline auto EndsWith(const Utils::SmallString &suffix) +{ + return Internal::EndsWithMatcher(suffix); } + +inline auto EndsWith(const QStringView &suffix) +{ + return ::testing::PolymorphicMatcher(Internal::QStringEndsWithMatcher(suffix.toString())); +} + +inline auto IsEmpty() +{ + return ::testing::PolymorphicMatcher(Internal::IsEmptyMatcher()); } diff --git a/tests/unit/unittest/unittest-utility-functions.h b/tests/unit/unittest/unittest-utility-functions.h index a4866cfa889..090360e8a70 100644 --- a/tests/unit/unittest/unittest-utility-functions.h +++ b/tests/unit/unittest/unittest-utility-functions.h @@ -15,10 +15,9 @@ bool operator==(const QString &first, const char *second) namespace UnitTest { -inline -Utils::PathString temporaryDirPath() +inline ::Utils::PathString temporaryDirPath() { - return Utils::PathString::fromQString(Utils::TemporaryDirectory::masterDirectoryPath()); + return ::Utils::PathString::fromQString(Utils::TemporaryDirectory::masterDirectoryPath()); } } // namespace UnitTest From ca300e9eb8ccb07bca0eebc1e22131c0757fadeb Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Wed, 19 Apr 2023 16:49:14 +0300 Subject: [PATCH 051/192] QmlDesigner: Fix text selection bug for code editor The event is returned to the parent hierarchy when the position is not in the scrollbars area. Task-number: QDS-9726 Change-Id: Id781650e15035a26f282990d7ee387b4417bc6eb Reviewed-by: Mahmoud Badri Reviewed-by: Thomas Hartmann --- src/libs/utils/transientscroll.cpp | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/src/libs/utils/transientscroll.cpp b/src/libs/utils/transientscroll.cpp index e056eac7d0e..844d48de3ad 100644 --- a/src/libs/utils/transientscroll.cpp +++ b/src/libs/utils/transientscroll.cpp @@ -41,24 +41,31 @@ public: } } - inline void checkToFlashScroll(QPointer scrollBar, const QPoint &pos) + inline bool checkToFlashScroll(QPointer scrollBar, const QPoint &pos) { if (scrollBar.isNull()) - return; + return false; if (!scrollBar->style()->styleHint( QStyle::SH_ScrollBar_Transient, nullptr, scrollBar)) - return; + return false; - if (scrollBarRect(scrollBar).contains(pos)) + if (scrollBarRect(scrollBar).contains(pos)) { scrollBar->flash(); + return true; + } + + return false; } - inline void checkToFlashScroll(const QPoint &pos) + inline bool checkToFlashScroll(const QPoint &pos) { - checkToFlashScroll(verticalScrollBar, pos); - checkToFlashScroll(horizontalScrollBar, pos); + bool coversScroll = checkToFlashScroll(verticalScrollBar, pos); + if (!coversScroll) + coversScroll |= checkToFlashScroll(horizontalScrollBar, pos); + + return coversScroll; } inline void installViewPort(QObject *eventHandler) { @@ -126,8 +133,8 @@ bool TransientScrollAreaSupport::eventFilter(QObject *watched, QEvent *event) if (watched == d->viewPort){ QMouseEvent *mouseEvent = static_cast(event); if (mouseEvent) { - d->checkToFlashScroll(mouseEvent->pos()); - return true; + if (d->checkToFlashScroll(mouseEvent->pos())) + return true; } } } From f0b2cc0836178e95e096713c54a3d809696378fb Mon Sep 17 00:00:00 2001 From: Henning Gruendl Date: Wed, 19 Apr 2023 15:03:25 +0200 Subject: [PATCH 052/192] QmlDesigner: Add insight tracker linking - Add proper insight tracker library linking to the QtDS CMake template. - Add CMake option LINK_INSIGHT to turn insight linking off - Add check for Qt version 6.5 and insight configuration available Task-number: QDS-9354 Change-Id: I606364743cce61c281d3faae929df50ba95892b7 Reviewed-by: Cristian Adam Reviewed-by: Thomas Hartmann --- .../projects/application-3d/wizard.json | 4 ++++ .../projects/application/wizard.json | 4 ++++ .../projects/common/CMakeLists.main.txt.tpl | 5 +++++ .../projects/common/insight.tpl | 19 +++++++++++++++++++ .../projects/desktop-launcher/wizard.json | 4 ++++ .../projects/mobile-scroll/wizard.json | 4 ++++ .../projects/mobile-stack/wizard.json | 4 ++++ .../projects/mobile-swipe/wizard.json | 4 ++++ 8 files changed, 48 insertions(+) create mode 100644 share/qtcreator/qmldesigner/studio_templates/projects/common/insight.tpl 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 55bdc8a940a..3e26a5f0f20 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/application-3d/wizard.json +++ b/share/qtcreator/qmldesigner/studio_templates/projects/application-3d/wizard.json @@ -311,6 +311,10 @@ "source": "../common/qmlcomponents.tpl", "target": "%{ProjectDirectory}/qmlcomponents" }, + { + "source": "../common/insight.tpl", + "target": "%{ProjectDirectory}/insight" + }, { "source": "../common/main.qml", "target": "%{ProjectDirectory}/main.qml" diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/application/wizard.json b/share/qtcreator/qmldesigner/studio_templates/projects/application/wizard.json index 82ebe92ff09..ea5f9ef671b 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/application/wizard.json +++ b/share/qtcreator/qmldesigner/studio_templates/projects/application/wizard.json @@ -306,6 +306,10 @@ "source": "../common/qmlcomponents.tpl", "target": "%{ProjectDirectory}/qmlcomponents" }, + { + "source": "../common/insight.tpl", + "target": "%{ProjectDirectory}/insight" + }, { "source": "../common/main.qml", "target": "%{ProjectDirectory}/main.qml" diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/common/CMakeLists.main.txt.tpl b/share/qtcreator/qmldesigner/studio_templates/projects/common/CMakeLists.main.txt.tpl index a76765daee9..086b6bc2e9d 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/common/CMakeLists.main.txt.tpl +++ b/share/qtcreator/qmldesigner/studio_templates/projects/common/CMakeLists.main.txt.tpl @@ -1,6 +1,7 @@ cmake_minimum_required(VERSION 3.21.1) set(BUILD_QDS_COMPONENTS ON CACHE BOOL "Build design studio components") +option(LINK_INSIGHT "Link Qt Insight Tracker library" ON) project(%{ProjectName}App LANGUAGES CXX) @@ -41,6 +42,10 @@ endif () include(${CMAKE_CURRENT_SOURCE_DIR}/qmlmodules) +if (LINK_INSIGHT) + include(${CMAKE_CURRENT_SOURCE_DIR}/insight) +endif () + install(TARGETS ${CMAKE_PROJECT_NAME} BUNDLE DESTINATION . LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}) diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/common/insight.tpl b/share/qtcreator/qmldesigner/studio_templates/projects/common/insight.tpl new file mode 100644 index 00000000000..8245e31f0d9 --- /dev/null +++ b/share/qtcreator/qmldesigner/studio_templates/projects/common/insight.tpl @@ -0,0 +1,19 @@ +### This file is automatically generated by Qt Design Studio. +### Do not change + +if (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/qtinsight.conf) + if (QT_VERSION GREATER_EQUAL 6.5.0) + find_package(Qt6 REQUIRED COMPONENTS InsightTracker) + + qt_add_resources(${CMAKE_PROJECT_NAME} "configuration" + PREFIX "/" + FILES + qtinsight.conf + ) + target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE + Qt6::InsightTracker + ) + else() + message(WARNING "You need Qt 6.5.0 or newer to build the application.") + endif() +endif() 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 d29b1eec730..58fb3f30ef7 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/desktop-launcher/wizard.json +++ b/share/qtcreator/qmldesigner/studio_templates/projects/desktop-launcher/wizard.json @@ -304,6 +304,10 @@ "source": "../common/qmlcomponents.tpl", "target": "%{ProjectDirectory}/qmlcomponents" }, + { + "source": "../common/insight.tpl", + "target": "%{ProjectDirectory}/insight" + }, { "source": "../common/main.qml", "target": "%{ProjectDirectory}/main.qml" 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 ac83550c398..d79059a6a5a 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/mobile-scroll/wizard.json +++ b/share/qtcreator/qmldesigner/studio_templates/projects/mobile-scroll/wizard.json @@ -263,6 +263,10 @@ "source": "../common/qmlcomponents.tpl", "target": "%{ProjectDirectory}/qmlcomponents" }, + { + "source": "../common/insight.tpl", + "target": "%{ProjectDirectory}/insight" + }, { "source": "../common/main.qml", "target": "%{ProjectDirectory}/main.qml" 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 23cc6163c18..cd837cf5688 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/mobile-stack/wizard.json +++ b/share/qtcreator/qmldesigner/studio_templates/projects/mobile-stack/wizard.json @@ -260,6 +260,10 @@ "source": "../common/qmlcomponents.tpl", "target": "%{ProjectDirectory}/qmlcomponents" }, + { + "source": "../common/insight.tpl", + "target": "%{ProjectDirectory}/insight" + }, { "source": "../common/main.qml", "target": "%{ProjectDirectory}/main.qml" 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 222b09a1ba0..abef03752e2 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/mobile-swipe/wizard.json +++ b/share/qtcreator/qmldesigner/studio_templates/projects/mobile-swipe/wizard.json @@ -260,6 +260,10 @@ "source": "../common/qmlcomponents.tpl", "target": "%{ProjectDirectory}/qmlcomponents" }, + { + "source": "../common/insight.tpl", + "target": "%{ProjectDirectory}/insight" + }, { "source": "../common/main.qml", "target": "%{ProjectDirectory}/main.qml" From 062c7745416dfce32665b4c2b899a292b250f44c Mon Sep 17 00:00:00 2001 From: Henning Gruendl Date: Wed, 29 Mar 2023 19:56:43 +0200 Subject: [PATCH 053/192] QmlDesigner: Update workspace management - Add QML attribute displayName to the workspace presets - Add workspace order - Replace QStringList workspaces with proper model in order to use text and value role - Clean up workspace management, remove redundant functions, move warning messages out of backend - Utilizing expected_str to forward error message - Change output format of generated XML workspace files - Remove workspace combo box from C++ based toolbar Task-number: QDS-9184 Change-Id: I19455db75e6b07aea9342eb5276c6191a1456a2d Reviewed-by: Thomas Hartmann --- .../StudioControls/TopLevelComboBox.qml | 5 +- share/qtcreator/qmldesigner/toolbar/Main.qml | 27 +- .../workspacePresets/Advanced-3D.wrk | 75 +- .../workspacePresets/Animation-2D.wrk | 76 +- .../workspacePresets/Animation-3D.wrk | 61 +- .../qmldesigner/workspacePresets/Basic.wrk | 71 +- .../qmldesigner/workspacePresets/Code.wrk | 71 +- .../workspacePresets/Essentials-3D.wrk | 63 +- .../workspacePresets/Essentials.wrk | 90 +- .../workspacePresets/UX-Design.wrk | 76 +- .../workspacePresets/Views-All.wrk | 71 +- .../qmldesigner/workspacePresets/order.json | 11 + src/libs/advanceddockingsystem/CMakeLists.txt | 4 +- .../dockcontainerwidget.cpp | 8 +- .../advanceddockingsystem/dockmanager.cpp | 2087 +++++++++-------- src/libs/advanceddockingsystem/dockmanager.h | 436 ++-- src/libs/advanceddockingsystem/workspace.cpp | 95 + src/libs/advanceddockingsystem/workspace.h | 55 + .../advanceddockingsystem/workspacedialog.cpp | 195 +- .../advanceddockingsystem/workspacedialog.h | 28 +- .../workspaceinputdialog.cpp | 103 + .../workspaceinputdialog.h | 38 + .../advanceddockingsystem/workspacemodel.cpp | 218 +- .../advanceddockingsystem/workspacemodel.h | 28 +- .../advanceddockingsystem/workspaceview.cpp | 285 ++- .../advanceddockingsystem/workspaceview.h | 24 +- .../components/toolbar/toolbarbackend.cpp | 539 +++-- .../components/toolbar/toolbarbackend.h | 21 +- src/plugins/qmldesigner/designmodewidget.cpp | 46 +- 29 files changed, 3009 insertions(+), 1898 deletions(-) create mode 100644 share/qtcreator/qmldesigner/workspacePresets/order.json create mode 100644 src/libs/advanceddockingsystem/workspace.cpp create mode 100644 src/libs/advanceddockingsystem/workspace.h create mode 100644 src/libs/advanceddockingsystem/workspaceinputdialog.cpp create mode 100644 src/libs/advanceddockingsystem/workspaceinputdialog.h diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/TopLevelComboBox.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/TopLevelComboBox.qml index 1dddfec0b4f..3210559a688 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/TopLevelComboBox.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/TopLevelComboBox.qml @@ -152,7 +152,10 @@ T.ComboBox { contentItem: Text { leftPadding: itemDelegateIconArea.width - text: modelData + text: control.textRole ? (Array.isArray(control.model) + ? modelData[control.textRole] + : model[control.textRole]) + : modelData color: { if (!itemDelegate.enabled) return control.style.text.disabled diff --git a/share/qtcreator/qmldesigner/toolbar/Main.qml b/share/qtcreator/qmldesigner/toolbar/Main.qml index 3daf50c14b8..7f6a8a311c8 100644 --- a/share/qtcreator/qmldesigner/toolbar/Main.qml +++ b/share/qtcreator/qmldesigner/toolbar/Main.qml @@ -237,14 +237,18 @@ Rectangle { anchors.verticalCenter: parent.verticalCenter anchors.right: annotations.left anchors.rightMargin: 10 - model: backend.workspaces - suffix: qsTr(" Workspace") - property int currentWorkspaceIndex: workspaces.find(backend.currentWorkspace) - onCurrentWorkspaceIndexChanged: workspaces.currentIndex = workspaces.currentWorkspaceIndex - visible: !root.flyoutEnabled + model: WorkspaceModel { + id: workspaceModel + } + textRole: "displayName" + valueRole: "fileName" + suffix: qsTr(" Workspace") - onActivated: backend.setCurrentWorkspace(workspaces.currentText) + property int currentWorkspaceIndex: workspaces.indexOfValue(backend.currentWorkspace) + + onCurrentWorkspaceIndexChanged: workspaces.currentIndex = workspaces.currentWorkspaceIndex + onActivated: backend.setCurrentWorkspace(workspaces.currentValue) } ToolbarButton { @@ -253,12 +257,11 @@ Rectangle { anchors.verticalCenter: parent.verticalCenter anchors.right: shareButton.left anchors.rightMargin: 10 + width: 0 tooltip: qsTr("Edit Annotations") buttonIcon: StudioTheme.Constants.annotations_large - //visible: !root.flyoutEnabled onClicked: backend.editGlobalAnnoation() - width: 0 } ToolbarButton { @@ -391,10 +394,12 @@ Rectangle { style: StudioTheme.Values.statusbarControlStyle width: row.width maximumPopupHeight: 400 - model: backend.workspaces - currentIndex: workspacesFlyout.find(backend.currentWorkspace) + model: workspaceModel + textRole: "displayName" + valueRole: "fileName" + currentIndex: workspacesFlyout.indexOfValue(backend.currentWorkspace) - onCompressedActivated: backend.setCurrentWorkspace(workspacesFlyout.currentText) + onCompressedActivated: backend.setCurrentWorkspace(workspacesFlyout.currentValue) } } } diff --git a/share/qtcreator/qmldesigner/workspacePresets/Advanced-3D.wrk b/share/qtcreator/qmldesigner/workspacePresets/Advanced-3D.wrk index 2336c94ffae..24eace89b8c 100644 --- a/share/qtcreator/qmldesigner/workspacePresets/Advanced-3D.wrk +++ b/share/qtcreator/qmldesigner/workspacePresets/Advanced-3D.wrk @@ -1 +1,74 @@ -622 527 0620 529575 5741233 0 5330 01150 0536 1767 01150 0 + + + + + + + + + + + + + + + + + + + + + 622 527 0 + + + + + + + + + + + 620 529 + + + + + + + + + + + + + + 575 574 + + 1233 0 533 + + + + + + + + + + + 0 0 + + 1150 0 + + + + + 536 1767 0 + + + + + 1150 0 + + + diff --git a/share/qtcreator/qmldesigner/workspacePresets/Animation-2D.wrk b/share/qtcreator/qmldesigner/workspacePresets/Animation-2D.wrk index e5c5d40bc03..346856dd507 100644 --- a/share/qtcreator/qmldesigner/workspacePresets/Animation-2D.wrk +++ b/share/qtcreator/qmldesigner/workspacePresets/Animation-2D.wrk @@ -1 +1,75 @@ -346 0 365712 00 0914 00 0712 0712 0 0310 914 406712 255 + + + + + + + + + + + + + + + + + + + 346 0 365 + + + + + + + + + + + 712 0 + + + + + + + + + 0 0 + + 914 0 + + + + + + + + + 0 0 + + 712 0 + + + + + + + + + + + + 712 0 0 + + 310 914 406 + + + + + + 712 255 + + + diff --git a/share/qtcreator/qmldesigner/workspacePresets/Animation-3D.wrk b/share/qtcreator/qmldesigner/workspacePresets/Animation-3D.wrk index 7282eb1a637..8f1d59119cd 100644 --- a/share/qtcreator/qmldesigner/workspacePresets/Animation-3D.wrk +++ b/share/qtcreator/qmldesigner/workspacePresets/Animation-3D.wrk @@ -1 +1,60 @@ -688 2515 01604 1599544 421426 587994 10193204 635 + + + + + + + + + + + + + + + + + + + + 688 2515 0 + + + + + + + + + + + + 1604 1599 + + + + + + + 544 42 + + 1426 587 + + + + + + + + + + + + + + 994 1019 + + 3204 635 + + + diff --git a/share/qtcreator/qmldesigner/workspacePresets/Basic.wrk b/share/qtcreator/qmldesigner/workspacePresets/Basic.wrk index 82556f540a6..b1edb18677f 100644 --- a/share/qtcreator/qmldesigner/workspacePresets/Basic.wrk +++ b/share/qtcreator/qmldesigner/workspacePresets/Basic.wrk @@ -1 +1,70 @@ -481 0 486968 0968 0919 00 919968 0968 0 0312 919 408 + + + + + + + + + + + + + + + + + + 481 0 486 + + + + + + + + + + + 968 0 + + + + + + + + + 968 0 + + 919 0 + + + + + + + + + + + 0 919 + + 968 0 + + + + + + + + + + + + 968 0 0 + + 312 919 408 + + + diff --git a/share/qtcreator/qmldesigner/workspacePresets/Code.wrk b/share/qtcreator/qmldesigner/workspacePresets/Code.wrk index c2510cb7dc2..82f734432fc 100644 --- a/share/qtcreator/qmldesigner/workspacePresets/Code.wrk +++ b/share/qtcreator/qmldesigner/workspacePresets/Code.wrk @@ -1 +1,70 @@ -541 0 426707 0488 218601 4481050 0707 260541 0 426274 1050 315 + + + + + + + + + + + + + + + + + + 541 0 426 + + + + + + + + + + + 707 0 + + + + + + + + + 488 218 + + 601 448 + + + + + + + + + + + 1050 0 + + 707 260 + + + + + + + + + + + + 541 0 426 + + 274 1050 315 + + + diff --git a/share/qtcreator/qmldesigner/workspacePresets/Essentials-3D.wrk b/share/qtcreator/qmldesigner/workspacePresets/Essentials-3D.wrk index 4ed1c599c3c..74215ffd456 100644 --- a/share/qtcreator/qmldesigner/workspacePresets/Essentials-3D.wrk +++ b/share/qtcreator/qmldesigner/workspacePresets/Essentials-3D.wrk @@ -1 +1,62 @@ -355 428 404678 678678 678641 547 0 0831 357453 1357 453 + + + + + + + + + + + + + + + + + + + 355 428 404 + + + + + + + + + + + 678 678 + + + + + + + + + 678 678 + + + + + + + + + 641 547 0 0 + + + + + + + + + 831 357 + + 453 1357 453 + + + diff --git a/share/qtcreator/qmldesigner/workspacePresets/Essentials.wrk b/share/qtcreator/qmldesigner/workspacePresets/Essentials.wrk index 2bdbc74f22c..e11f39ec940 100644 --- a/share/qtcreator/qmldesigner/workspacePresets/Essentials.wrk +++ b/share/qtcreator/qmldesigner/workspacePresets/Essentials.wrk @@ -1,47 +1,47 @@ - - - - - - - - - - - - - - - - - - 6000 1500 2500 - - - - - - - - - - - - - - 7000 1500 1500 - - - - - - - - - 7000 3000 - - 2000 6000 2000 - - + + + + + + + + + + + + + + + + + + 6000 1500 2500 + + + + + + + + + + + + + + 7000 1500 1500 + + + + + + + + + 7000 3000 + + 2000 6000 2000 + + diff --git a/share/qtcreator/qmldesigner/workspacePresets/UX-Design.wrk b/share/qtcreator/qmldesigner/workspacePresets/UX-Design.wrk index e7ac0763b17..5e973366629 100644 --- a/share/qtcreator/qmldesigner/workspacePresets/UX-Design.wrk +++ b/share/qtcreator/qmldesigner/workspacePresets/UX-Design.wrk @@ -1 +1,75 @@ -439 0 433873 00 01291 01291 0873 0637 0 235438 1291 573873 276 + + + + + + + + + + + + + + + + + + + 439 0 433 + + + + + + + + + + + 873 0 + + + + + + + + + 0 0 + + 1291 0 + + + + + + + + + + 1291 0 + + 873 0 + + + + + + + + + + + + 637 0 235 + + 438 1291 573 + + + + + 873 276 + + + diff --git a/share/qtcreator/qmldesigner/workspacePresets/Views-All.wrk b/share/qtcreator/qmldesigner/workspacePresets/Views-All.wrk index 7a2639e9550..47b47b213b1 100644 --- a/share/qtcreator/qmldesigner/workspacePresets/Views-All.wrk +++ b/share/qtcreator/qmldesigner/workspacePresets/Views-All.wrk @@ -1 +1,70 @@ -343 353 270353 353353 353448 465457 456707 260343 353 270310 914 406 + + + + + + + + + + + + + + + + + + 343 353 270 + + + + + + + + + + + 353 353 + + + + + + + + + 353 353 + + 448 465 + + + + + + + + + + + 457 456 + + 707 260 + + + + + + + + + + + + 343 353 270 + + 310 914 406 + + + diff --git a/share/qtcreator/qmldesigner/workspacePresets/order.json b/share/qtcreator/qmldesigner/workspacePresets/order.json new file mode 100644 index 00000000000..d22d9d3fcc3 --- /dev/null +++ b/share/qtcreator/qmldesigner/workspacePresets/order.json @@ -0,0 +1,11 @@ +[ + "Basic.wrk", + "UX-Design.wrk", + "Essentials.wrk", + "Animation-2D.wrk", + "Essentials-3D.wrk", + "Animation-3D.wrk", + "Advanced-3D.wrk", + "Code.wrk", + "Views-All.wrk" +] diff --git a/src/libs/advanceddockingsystem/CMakeLists.txt b/src/libs/advanceddockingsystem/CMakeLists.txt index f22d3befe8c..19d89f41d6c 100644 --- a/src/libs/advanceddockingsystem/CMakeLists.txt +++ b/src/libs/advanceddockingsystem/CMakeLists.txt @@ -1,5 +1,5 @@ add_qtc_library(AdvancedDockingSystem - DEPENDS Qt::Widgets Qt::Core Qt::Gui Utils + DEPENDS Qt::Widgets Qt::Core Qt::Gui Qt::Xml Utils SOURCES ads_globals.cpp ads_globals.h advanceddockingsystemtr.h @@ -19,7 +19,9 @@ add_qtc_library(AdvancedDockingSystem floatingdockcontainer.cpp floatingdockcontainer.h floatingdragpreview.cpp floatingdragpreview.h iconprovider.cpp iconprovider.h + workspace.cpp workspace.h workspacedialog.cpp workspacedialog.h + workspaceinputdialog.cpp workspaceinputdialog.h workspacemodel.cpp workspacemodel.h workspaceview.cpp workspaceview.h ) diff --git a/src/libs/advanceddockingsystem/dockcontainerwidget.cpp b/src/libs/advanceddockingsystem/dockcontainerwidget.cpp index 80393a46713..2cf073a94fe 100644 --- a/src/libs/advanceddockingsystem/dockcontainerwidget.cpp +++ b/src/libs/advanceddockingsystem/dockcontainerwidget.cpp @@ -642,8 +642,8 @@ namespace ADS stream.writeAttribute("orientation", QVariant::fromValue(splitter->orientation()).toString()); stream.writeAttribute("count", QString::number(splitter->count())); - qCInfo(adsLog) << "NodeSplitter orient: " << splitter->orientation() - << " WidgetCont: " << splitter->count(); + qCInfo(adsLog) << "NodeSplitter orientation:" << splitter->orientation() + << "WidgetCount:" << splitter->count(); for (int i = 0; i < splitter->count(); ++i) saveChildNodesState(stream, splitter->widget(i)); @@ -679,8 +679,8 @@ namespace ADS if (!ok) return false; - qCInfo(adsLog) << "Restore NodeSplitter Orientation: " << orientation - << " WidgetCount: " << widgetCount; + qCInfo(adsLog) << "Restore NodeSplitter Orientation:" << orientation + << "WidgetCount:" << widgetCount; QSplitter *splitter = nullptr; if (!testing) splitter = createSplitter(orientation); diff --git a/src/libs/advanceddockingsystem/dockmanager.cpp b/src/libs/advanceddockingsystem/dockmanager.cpp index 4c2c1345ea2..44d3ce89f9f 100644 --- a/src/libs/advanceddockingsystem/dockmanager.cpp +++ b/src/libs/advanceddockingsystem/dockmanager.cpp @@ -4,6 +4,7 @@ #include "dockmanager.h" #include "ads_globals.h" +#include "advanceddockingsystemtr.h" #include "dockareawidget.h" #include "dockfocuscontroller.h" #include "dockingstatereader.h" @@ -17,7 +18,6 @@ #include #include #include -#include #include #include @@ -26,8 +26,11 @@ #include #include #include +#include #include #include +#include +#include #include #include #include @@ -42,1005 +45,1245 @@ static Q_LOGGING_CATEGORY(adsLog, "qtc.qmldesigner.advanceddockingsystem", QtWar using namespace Utils; -namespace ADS +namespace ADS { +/** + * Internal file version in case the structure changes internally + */ +enum eStateFileVersion { + InitialVersion = 0, //!< InitialVersion + Version1 = 1, //!< Version1 + CurrentVersion = Version1 //!< CurrentVersion +}; + +static DockManager::ConfigFlags g_staticConfigFlags = DockManager::DefaultNonOpaqueConfig; + +/** + * Private data class of DockManager class (pimpl) + */ +class DockManagerPrivate { - /** - * Internal file version in case the structure changes internally - */ - enum eStateFileVersion { - InitialVersion = 0, //!< InitialVersion - Version1 = 1, //!< Version1 - CurrentVersion = Version1 //!< CurrentVersion - }; +public: + DockManager *q; + QList> m_floatingWidgets; + QList m_containers; + DockOverlay *m_containerOverlay = nullptr; + DockOverlay *m_dockAreaOverlay = nullptr; + QMap m_dockWidgetsMap; + bool m_restoringState = false; + QVector m_uninitializedFloatingWidgets; + DockFocusController *m_focusController = nullptr; - static DockManager::ConfigFlags g_staticConfigFlags = DockManager::DefaultNonOpaqueConfig; + QString m_workspacePresetsPath; + QList m_workspaces; + Workspace m_workspace; + + QSettings *m_settings = nullptr; + bool m_modeChangeState = false; + bool m_workspaceOrderDirty = false; /** - * Private data class of DockManager class (pimpl) + * Private data constructor */ - class DockManagerPrivate + DockManagerPrivate(DockManager *parent); + + /** + * Restores the state. If testing is set to true it will check if + * the given data stream is a valid docking system state file. + */ + bool restoreStateFromXml(const QByteArray &state, int version, bool testing = false); + + /** + * Restore state + */ + bool restoreState(const QByteArray &state, int version); + + void restoreDockWidgetsOpenState(); + void restoreDockAreasIndices(); + void emitTopLevelEvents(); + + void hideFloatingWidgets() { - public: - DockManager *q; - QList> m_floatingWidgets; - QList m_containers; - DockOverlay *m_containerOverlay = nullptr; - DockOverlay *m_dockAreaOverlay = nullptr; - QMap m_dockWidgetsMap; - bool m_restoringState = false; - QVector m_uninitializedFloatingWidgets; - DockFocusController *m_focusController = nullptr; - - QString m_workspaceName; - bool m_workspaceListDirty = true; - QStringList m_workspaces; - QSet m_workspacePresets; - QHash m_workspaceDateTimes; - QString m_workspaceToRestoreAtStartup; - bool m_autorestoreLastWorkspace; // This option is set in the Workspace Manager! - QSettings *m_settings = nullptr; - QString m_workspacePresetsPath; - bool m_modeChangeState = false; - - /** - * Private data constructor - */ - DockManagerPrivate(DockManager *parent); - - /** - * Restores the state. If testing is set to true it will check if - * the given data stream is a valid docking system state file. - */ - bool restoreStateFromXml(const QByteArray &state, - int version, - bool testing = false); - - /** - * Restore state - */ - bool restoreState(const QByteArray &state, int version); - - void restoreDockWidgetsOpenState(); - void restoreDockAreasIndices(); - void emitTopLevelEvents(); - - void hideFloatingWidgets() - { - // Hide updates of floating widgets from user - for (const auto &floatingWidget : std::as_const(m_floatingWidgets)) { - if (floatingWidget) - floatingWidget->hide(); - } - } - - void markDockWidgetsDirty() - { - for (const auto &dockWidget : std::as_const(m_dockWidgetsMap)) - dockWidget->setProperty("dirty", true); - } - - /** - * Restores the container with the given index - */ - bool restoreContainer(int index, DockingStateReader &stream, bool testing); - - void workspaceLoadingProgress(); - }; // class DockManagerPrivate - - DockManagerPrivate::DockManagerPrivate(DockManager *parent) - : q(parent) - {} - - bool DockManagerPrivate::restoreContainer(int index, DockingStateReader &stream, bool testing) - { - if (testing) - index = 0; - - bool result = false; - if (index >= m_containers.count()) { - FloatingDockContainer *floatingWidget = new FloatingDockContainer(q); - result = floatingWidget->restoreState(stream, testing); - } else { - qCInfo(adsLog) << "d->m_containers[i]->restoreState "; - auto container = m_containers[index]; - if (container->isFloating()) - result = container->floatingWidget()->restoreState(stream, testing); - else - result = container->restoreState(stream, testing); - } - - return result; - } - - bool DockManagerPrivate::restoreStateFromXml(const QByteArray &state, int version, bool testing) - { - Q_UNUSED(version) // TODO version is not needed, why is it in here in the first place? - - if (state.isEmpty()) - return false; - - DockingStateReader stateReader(state); - if (!stateReader.readNextStartElement()) - return false; - - if (stateReader.name() != QLatin1String("QtAdvancedDockingSystem")) - return false; - - qCInfo(adsLog) << stateReader.attributes().value("version"); - bool ok; - int v = stateReader.attributes().value("version").toInt(&ok); - if (!ok || v > CurrentVersion) - return false; - - stateReader.setFileVersion(v); - - qCInfo(adsLog) << stateReader.attributes().value("userVersion"); - // Older files do not support UserVersion but we still want to load them so - // we first test if the attribute exists - if (!stateReader.attributes().value("userVersion").isEmpty()) - { - v = stateReader.attributes().value("userVersion").toInt(&ok); - if (!ok || v != version) - return false; - } - - bool result = true; -#ifdef ADS_DEBUG_PRINT - int dockContainers = stateReader.attributes().value("containers").toInt(); - qCInfo(adsLog) << dockContainers; -#endif - int dockContainerCount = 0; - while (stateReader.readNextStartElement()) { - if (stateReader.name() == QLatin1String("container")) { - result = restoreContainer(dockContainerCount, stateReader, testing); - if (!result) - break; - - dockContainerCount++; - } - } - - if (!testing) { - // Delete remaining empty floating widgets - int floatingWidgetIndex = dockContainerCount - 1; - int deleteCount = m_floatingWidgets.count() - floatingWidgetIndex; - for (int i = 0; i < deleteCount; ++i) { - m_floatingWidgets[floatingWidgetIndex + i]->deleteLater(); - q->removeDockContainer(m_floatingWidgets[floatingWidgetIndex + i]->dockContainer()); - } - } - - return result; - } - - void DockManagerPrivate::restoreDockWidgetsOpenState() - { - // All dock widgets, that have not been processed in the restore state - // function are invisible to the user now and have no assigned dock area - // They do not belong to any dock container, until the user toggles the - // toggle view action the next time - for (auto dockWidget : std::as_const(m_dockWidgetsMap)) { - if (dockWidget->property(internal::dirtyProperty).toBool()) { - dockWidget->flagAsUnassigned(); - emit dockWidget->viewToggled(false); - } else { - dockWidget->toggleViewInternal( - !dockWidget->property(internal::closedProperty).toBool()); - } - } - } - - void DockManagerPrivate::restoreDockAreasIndices() - { - // Now all dock areas are properly restored and we setup the index of - // The dock areas because the previous toggleView() action has changed - // the dock area index - int count = 0; - for (auto dockContainer : std::as_const(m_containers)) { - count++; - for (int i = 0; i < dockContainer->dockAreaCount(); ++i) { - DockAreaWidget *dockArea = dockContainer->dockArea(i); - QString dockWidgetName = dockArea->property("currentDockWidget").toString(); - DockWidget *dockWidget = nullptr; - if (!dockWidgetName.isEmpty()) - dockWidget = q->findDockWidget(dockWidgetName); - - if (!dockWidget || dockWidget->isClosed()) { - int index = dockArea->indexOfFirstOpenDockWidget(); - if (index < 0) - continue; - - dockArea->setCurrentIndex(index); - } else { - dockArea->internalSetCurrentDockWidget(dockWidget); - } - } - } - } - - void DockManagerPrivate::emitTopLevelEvents() - { - // Finally we need to send the topLevelChanged() signals for all dock - // widgets if top level changed - for (auto dockContainer : std::as_const(m_containers)) { - DockWidget *topLevelDockWidget = dockContainer->topLevelDockWidget(); - if (topLevelDockWidget) { - topLevelDockWidget->emitTopLevelChanged(true); - } else { - for (int i = 0; i < dockContainer->dockAreaCount(); ++i) { - auto dockArea = dockContainer->dockArea(i); - for (auto dockWidget : dockArea->dockWidgets()) - dockWidget->emitTopLevelChanged(false); - } - } - } - } - - bool DockManagerPrivate::restoreState(const QByteArray &state, int version) - { - QByteArray currentState = state.startsWith("m_workspaceListDirty = true; - }); - - createRootSplitter(); - QMainWindow *mainWindow = qobject_cast(parent); - if (mainWindow) { - mainWindow->setCentralWidget(this); - } - - d->m_dockAreaOverlay = new DockOverlay(this, DockOverlay::ModeDockAreaOverlay); - d->m_containerOverlay = new DockOverlay(this, DockOverlay::ModeContainerOverlay); - d->m_containers.append(this); - - if (DockManager::configFlags().testFlag(DockManager::FocusHighlighting)) - d->m_focusController = new DockFocusController(this); - } - - DockManager::~DockManager() - { - emit aboutToUnloadWorkspace(d->m_workspaceName); - save(); - saveStartupWorkspace(); - - // Using a temporal vector since the destructor of - // FloatingDockWidgetContainer alters d->m_floatingWidgets. - const auto copy = d->m_floatingWidgets; - for (const auto &floatingWidget : copy) { + // Hide updates of floating widgets from user + for (const auto &floatingWidget : std::as_const(m_floatingWidgets)) { if (floatingWidget) - delete floatingWidget.get(); + floatingWidget->hide(); } - delete d; } - DockManager::ConfigFlags DockManager::configFlags() { return g_staticConfigFlags; } - - void DockManager::setConfigFlags(const ConfigFlags flags) { g_staticConfigFlags = flags; } - - void DockManager::setConfigFlag(eConfigFlag flag, bool on) + void markDockWidgetsDirty() { - internal::setFlag(g_staticConfigFlags, flag, on); - } - - bool DockManager::testConfigFlag(eConfigFlag flag) - { - return configFlags().testFlag(flag); - } - - IconProvider &DockManager::iconProvider() - { - static IconProvider instance; - return instance; - } - - int DockManager::startDragDistance() - { - return static_cast(QApplication::startDragDistance() * 1.5); - } - - void DockManager::setSettings(QSettings *settings) { d->m_settings = settings; } - - void DockManager::setWorkspacePresetsPath(const QString &path) { d->m_workspacePresetsPath = path; } - - DockAreaWidget *DockManager::addDockWidget(DockWidgetArea area, - DockWidget *dockWidget, - DockAreaWidget *dockAreaWidget) - { - d->m_dockWidgetsMap.insert(dockWidget->objectName(), dockWidget); - return DockContainerWidget::addDockWidget(area, dockWidget, dockAreaWidget); - } - - void DockManager::initialize() - { - syncWorkspacePresets(); - - QString workspace = ADS::Constants::DEFAULT_WORKSPACE; - - // Determine workspace to restore at startup - if (autoRestorLastWorkspace()) { - QString lastWS = lastWorkspace(); - if (!lastWS.isEmpty() && workspaces().contains(lastWS)) - workspace = lastWS; - else - qDebug() << "Couldn't restore last workspace!"; - } - - openWorkspace(workspace); - } - - DockAreaWidget *DockManager::addDockWidgetTab(DockWidgetArea area, DockWidget *dockWidget) - { - DockAreaWidget *areaWidget = lastAddedDockAreaWidget(area); - if (areaWidget) - return addDockWidget(ADS::CenterDockWidgetArea, dockWidget, areaWidget); - else if (!openedDockAreas().isEmpty()) - return addDockWidget(area, dockWidget, openedDockAreas().constLast()); - else - return addDockWidget(area, dockWidget, nullptr); - } - - DockAreaWidget *DockManager::addDockWidgetTabToArea(DockWidget *dockWidget, - DockAreaWidget *dockAreaWidget) - { - return addDockWidget(ADS::CenterDockWidgetArea, dockWidget, dockAreaWidget); - } - - FloatingDockContainer *DockManager::addDockWidgetFloating(DockWidget *dockWidget) - { - d->m_dockWidgetsMap.insert(dockWidget->objectName(), dockWidget); - DockAreaWidget *oldDockArea = dockWidget->dockAreaWidget(); - if (oldDockArea) - oldDockArea->removeDockWidget(dockWidget); - - dockWidget->setDockManager(this); - FloatingDockContainer *floatingWidget = new FloatingDockContainer(dockWidget); - floatingWidget->resize(dockWidget->size()); - if (isVisible()) - floatingWidget->show(); - else - d->m_uninitializedFloatingWidgets.append(floatingWidget); - - return floatingWidget; - } - - void DockManager::registerFloatingWidget(FloatingDockContainer *floatingWidget) - { - d->m_floatingWidgets.append(floatingWidget); - emit floatingWidgetCreated(floatingWidget); - qCInfo(adsLog) << "d->FloatingWidgets.count() " << d->m_floatingWidgets.count(); - } - - void DockManager::removeFloatingWidget(FloatingDockContainer *floatingWidget) - { - d->m_floatingWidgets.removeAll(floatingWidget); - } - - void DockManager::registerDockContainer(DockContainerWidget *dockContainer) - { - d->m_containers.append(dockContainer); - } - - void DockManager::removeDockContainer(DockContainerWidget *dockContainer) - { - if (this != dockContainer) - d->m_containers.removeAll(dockContainer); - } - - DockOverlay *DockManager::containerOverlay() const { return d->m_containerOverlay; } - - DockOverlay *DockManager::dockAreaOverlay() const { return d->m_dockAreaOverlay; } - - const QList DockManager::dockContainers() const - { - return d->m_containers; - } - - const QList> DockManager::floatingWidgets() const - { - return d->m_floatingWidgets; - } - - unsigned int DockManager::zOrderIndex() const { return 0; } - - QByteArray DockManager::saveState(int version) const - { - QByteArray xmlData; - QXmlStreamWriter stream(&xmlData); - auto configFlags = DockManager::configFlags(); - stream.setAutoFormatting(configFlags.testFlag(XmlAutoFormattingEnabled)); - stream.writeStartDocument(); - stream.writeStartElement("QtAdvancedDockingSystem"); - stream.writeAttribute("version", QString::number(CurrentVersion)); - stream.writeAttribute("userVersion", QString::number(version)); - stream.writeAttribute("containers", QString::number(d->m_containers.count())); - for (auto container : std::as_const(d->m_containers)) - container->saveState(stream); - - stream.writeEndElement(); - stream.writeEndDocument(); - return xmlData; - } - - bool DockManager::restoreState(const QByteArray &state, int version) - { - // Prevent multiple calls as long as state is not restore. This may - // happen, if QApplication::processEvents() is called somewhere - if (d->m_restoringState) - return false; - - // We hide the complete dock manager here. Restoring the state means - // that DockWidgets are removed from the DockArea internal stack layout - // which in turn means, that each time a widget is removed the stack - // will show and raise the next available widget which in turn - // triggers show events for the dock widgets. To avoid this we hide the - // dock manager. Because there will be no processing of application - // events until this function is finished, the user will not see this - // hiding - bool isHidden = this->isHidden(); - if (!isHidden) - hide(); - - d->m_restoringState = true; - emit restoringState(); - bool result = d->restoreState(state, version); - d->m_restoringState = false; - if (!isHidden) - show(); - - emit stateRestored(); - return result; - } - - void DockManager::showEvent(QShowEvent *event) - { - Super::showEvent(event); - if (d->m_uninitializedFloatingWidgets.empty()) - return; - - for (auto floatingWidget : std::as_const(d->m_uninitializedFloatingWidgets)) - floatingWidget->show(); - - d->m_uninitializedFloatingWidgets.clear(); - } - - DockWidget *DockManager::findDockWidget(const QString &objectName) const - { - return d->m_dockWidgetsMap.value(objectName, nullptr); - } - - void DockManager::removeDockWidget(DockWidget *dockWidget) - { - emit dockWidgetAboutToBeRemoved(dockWidget); - d->m_dockWidgetsMap.remove(dockWidget->objectName()); - DockContainerWidget::removeDockWidget(dockWidget); - emit dockWidgetRemoved(dockWidget); - } - - QMap DockManager::dockWidgetsMap() const { return d->m_dockWidgetsMap; } - - bool DockManager::isRestoringState() const { return d->m_restoringState; } - - void DockManager::showWorkspaceMananger() - { - save(); // Save current workspace - - WorkspaceDialog workspaceDialog(this, parentWidget()); - workspaceDialog.setAutoLoadWorkspace(autoRestorLastWorkspace()); - workspaceDialog.exec(); - - QTC_ASSERT(d->m_settings, return ); - d->m_settings->setValue(Constants::AUTO_RESTORE_WORKSPACE_SETTINGS_KEY, - workspaceDialog.autoLoadWorkspace()); - } - - bool DockManager::isWorkspacePreset(const QString &workspace) const - { - return d->m_workspacePresets.contains(workspace); - } - - bool DockManager::save() - { - if (isModeChangeState()) - return false; - - emit aboutToSaveWorkspace(); - - bool result = write(activeWorkspace(), saveState(), parentWidget()); - if (result) - d->m_workspaceDateTimes.insert(activeWorkspace(), QDateTime::currentDateTime()); - else - QMessageBox::warning(parentWidget(), - Tr::tr("Cannot Save Workspace"), - Tr::tr("Could not save workspace to file %1") - .arg(workspaceNameToFilePath(d->m_workspaceName) - .toUserOutput())); - - return result; - } - - QString DockManager::activeWorkspace() const { return d->m_workspaceName; } - - QString DockManager::lastWorkspace() const - { - QTC_ASSERT(d->m_settings, return {}); - return d->m_settings->value(Constants::STARTUP_WORKSPACE_SETTINGS_KEY).toString(); - } - - bool DockManager::autoRestorLastWorkspace() const - { - QTC_ASSERT(d->m_settings, return false); - return d->m_settings->value(Constants::AUTO_RESTORE_WORKSPACE_SETTINGS_KEY).toBool(); - } - - constexpr QStringView m_dirName{u"workspaces"}; - constexpr QStringView m_fileExt{u".wrk"}; // TODO - - QStringView DockManager::workspaceFileExtension() const - { - return m_fileExt; - } - - QStringList DockManager::workspaces() - { - if (d->m_workspaces.isEmpty() || d->m_workspaceListDirty) { - auto tmp = Utils::toSet(d->m_workspaces); - - QTC_ASSERT(d->m_settings, return {}); - QDir workspaceDir(QFileInfo(d->m_settings->fileName()).path() + QLatin1Char('/') - + m_dirName); - QFileInfoList workspaceFiles - = workspaceDir.entryInfoList(QStringList() << QLatin1Char('*') + m_fileExt, - QDir::NoFilter, - QDir::Time); - for (const QFileInfo &fileInfo : workspaceFiles) { - QString workspaceName = fileNameToWorkspaceName(fileInfo.completeBaseName()); - d->m_workspaceDateTimes.insert(workspaceName, fileInfo.lastModified()); - tmp.insert(workspaceName); - } - - d->m_workspaceListDirty = false; - d->m_workspaces = Utils::toList(tmp); - } - return d->m_workspaces; - } - - QSet DockManager::workspacePresets() const - { - if (d->m_workspacePresets.isEmpty()) { - QDir workspacePresetsDir(d->m_workspacePresetsPath); - QFileInfoList workspacePresetsFiles - = workspacePresetsDir.entryInfoList(QStringList() << QLatin1Char('*') + m_fileExt, - QDir::NoFilter, - QDir::Time); - for (const QFileInfo &fileInfo : workspacePresetsFiles) - d->m_workspacePresets.insert(fileNameToWorkspaceName(fileInfo.completeBaseName())); - } - return d->m_workspacePresets; - } - - QDateTime DockManager::workspaceDateTime(const QString &workspace) const - { - return d->m_workspaceDateTimes.value(workspace); - } - - FilePath DockManager::workspaceNameToFilePath(const QString &workspaceName) const - { - QTC_ASSERT(d->m_settings, return {}); - return FilePath::fromString(QFileInfo(d->m_settings->fileName()).path() + QLatin1Char('/') - + m_dirName + QLatin1Char('/') - + workspaceNameToFileName(workspaceName)); - } - - QString DockManager::fileNameToWorkspaceName(const QString &fileName) const - { - QString copy = QFileInfo(fileName).baseName(); - copy.replace("_", " "); - return copy; - } - - QString DockManager::workspaceNameToFileName(const QString &workspaceName) const - { - QString copy = workspaceName; - copy.replace(" ", "_"); - copy.append(m_fileExt); - return copy; + for (const auto &dockWidget : std::as_const(m_dockWidgetsMap)) + dockWidget->setProperty("dirty", true); } /** - * Creates \a workspace, but does not actually create the file. + * Restores the container with the given index */ - bool DockManager::createWorkspace(const QString &workspace) - { - if (workspaces().contains(workspace)) - return false; + bool restoreContainer(int index, DockingStateReader &stream, bool testing); - bool result = write(workspace, saveState(), parentWidget()); - if (result) { - d->m_workspaces.insert(1, workspace); - d->m_workspaceDateTimes.insert(workspace, QDateTime::currentDateTime()); - emit workspaceListChanged(); + void workspaceLoadingProgress(); +}; // class DockManagerPrivate + +DockManagerPrivate::DockManagerPrivate(DockManager *parent) + : q(parent) +{} + +bool DockManagerPrivate::restoreContainer(int index, DockingStateReader &stream, bool testing) +{ + if (testing) + index = 0; + + bool result = false; + if (index >= m_containers.count()) { + FloatingDockContainer *floatingWidget = new FloatingDockContainer(q); + result = floatingWidget->restoreState(stream, testing); + } else { + auto container = m_containers[index]; + if (container->isFloating()) + result = container->floatingWidget()->restoreState(stream, testing); + else + result = container->restoreState(stream, testing); + } + + return result; +} + +bool DockManagerPrivate::restoreStateFromXml(const QByteArray &state, int version, bool testing) +{ + Q_UNUSED(version) // TODO version is not needed, why is it in here in the first place? + + if (state.isEmpty()) + return false; + + DockingStateReader stateReader(state); + if (!stateReader.readNextStartElement()) + return false; + + if (stateReader.name() != QLatin1String("QtAdvancedDockingSystem")) + return false; + + qCInfo(adsLog) << "Version" << stateReader.attributes().value("version"); + bool ok; + int v = stateReader.attributes().value("version").toInt(&ok); + if (!ok || v > CurrentVersion) + return false; + + stateReader.setFileVersion(v); + + qCInfo(adsLog) << "User version" << stateReader.attributes().value("userVersion"); + // Older files do not support userVersion, but we still want to load them so we first test + // if the attribute exists. + if (!stateReader.attributes().value("userVersion").isEmpty()) { + v = stateReader.attributes().value("userVersion").toInt(&ok); + if (!ok || v != version) + return false; + } + + bool result = true; +#ifdef ADS_DEBUG_PRINT + int dockContainers = stateReader.attributes().value("containers").toInt(); + qCInfo(adsLog) << dockContainers; +#endif + int dockContainerCount = 0; + while (stateReader.readNextStartElement()) { + if (stateReader.name() == QLatin1String("container")) { + result = restoreContainer(dockContainerCount, stateReader, testing); + if (!result) + break; + + dockContainerCount++; + } + } + + if (!testing) { + // Delete remaining empty floating widgets + int floatingWidgetIndex = dockContainerCount - 1; + int deleteCount = m_floatingWidgets.count() - floatingWidgetIndex; + for (int i = 0; i < deleteCount; ++i) { + m_floatingWidgets[floatingWidgetIndex + i]->deleteLater(); + q->removeDockContainer(m_floatingWidgets[floatingWidgetIndex + i]->dockContainer()); + } + } + + return result; +} + +void DockManagerPrivate::restoreDockWidgetsOpenState() +{ + // All dock widgets, that have not been processed in the restore state function are + // invisible to the user now and have no assigned dock area. They do not belong to any dock + // container, until the user toggles the toggle view action the next time. + for (auto dockWidget : std::as_const(m_dockWidgetsMap)) { + if (dockWidget->property(internal::dirtyProperty).toBool()) { + dockWidget->flagAsUnassigned(); + emit dockWidget->viewToggled(false); } else { - QMessageBox::warning(parentWidget(), - Tr::tr("Cannot Save Workspace"), - Tr::tr("Could not save workspace to file %1") - .arg(workspaceNameToFilePath(d->m_workspaceName) - .toUserOutput())); + dockWidget->toggleViewInternal(!dockWidget->property(internal::closedProperty).toBool()); } - - return result; } +} - bool DockManager::openWorkspace(const QString &workspace) - { - // Do nothing if we have that workspace already loaded, exception if it is - // a preset workspace. In this case we still want to be able to load the - // default workspace to undo potential user changes. - if (workspace == d->m_workspaceName && !isWorkspacePreset(workspace)) - return true; +void DockManagerPrivate::restoreDockAreasIndices() +{ + // Now all dock areas are properly restored and we setup the index of the dock areas, + // because the previous toggleView() action has changed the dock area index. + for (auto dockContainer : std::as_const(m_containers)) { + for (int i = 0; i < dockContainer->dockAreaCount(); ++i) { + DockAreaWidget *dockArea = dockContainer->dockArea(i); + const QString dockWidgetName = dockArea->property("currentDockWidget").toString(); + DockWidget *dockWidget = nullptr; + if (!dockWidgetName.isEmpty()) + dockWidget = q->findDockWidget(dockWidgetName); - if (!workspaces().contains(workspace)) - return false; + if (!dockWidget || dockWidget->isClosed()) { + int index = dockArea->indexOfFirstOpenDockWidget(); + if (index < 0) + continue; - // Check if the currently active workspace isn't empty and try to save it - if (!d->m_workspaceName.isEmpty()) { - // Allow everyone to set something in the workspace and before saving - emit aboutToUnloadWorkspace(d->m_workspaceName); - if (!save()) - return false; - } - - // Try loading the file - QByteArray data = loadWorkspace(workspace); - if (data.isEmpty()) - return false; - - emit openingWorkspace(workspace); - // If data was loaded from file try to restore its state - if (!data.isNull() && !restoreState(data)) - return false; - - d->m_workspaceName = workspace; - emit workspaceLoaded(workspace); - - return true; - } - - bool DockManager::reloadActiveWorkspace() - { - if (!workspaces().contains(activeWorkspace())) - return false; - - // Try loading the file - QByteArray data = loadWorkspace(activeWorkspace()); - if (data.isEmpty()) - return false; - - // If data was loaded from file try to restore its state - if (!data.isNull() && !restoreState(data)) - return false; - - emit workspaceReloaded(activeWorkspace()); - - return true; - } - - /** - * \brief Shows a dialog asking the user to confirm deleting the workspace \p workspace - */ - bool DockManager::confirmWorkspaceDelete(const QStringList &workspace) - { - const QString title = workspace.size() == 1 ? Tr::tr("Delete Workspace") - : Tr::tr("Delete Workspaces"); - const QString question = workspace.size() == 1 - ? Tr::tr("Delete workspace %1?").arg(workspace.first()) - : Tr::tr("Delete these workspaces?\n %1") - .arg(workspace.join("\n ")); - return QMessageBox::question(parentWidget(), - title, - question, - QMessageBox::Yes | QMessageBox::No) - == QMessageBox::Yes; - } - - /** - * Deletes \a workspace name from workspace list and the file from disk. - */ - bool DockManager::deleteWorkspace(const QString &workspace) - { - // Remove workspace from internal list - if (!d->m_workspaces.contains(workspace)) - return false; - - // Remove corresponding workspace file - const FilePath file = workspaceNameToFilePath(workspace); - if (file.exists()) { - if (file.removeFile()) { - d->m_workspaces.removeOne(workspace); - emit workspacesRemoved(); - emit workspaceListChanged(); - return true; + dockArea->setCurrentIndex(index); + } else { + dockArea->internalSetCurrentDockWidget(dockWidget); } } + } +} +void DockManagerPrivate::emitTopLevelEvents() +{ + // Finally we need to send the topLevelChanged() signals for all dock widgets if top + // level changed. + for (auto dockContainer : std::as_const(m_containers)) { + DockWidget *topLevelDockWidget = dockContainer->topLevelDockWidget(); + if (topLevelDockWidget) { + topLevelDockWidget->emitTopLevelChanged(true); + } else { + for (int i = 0; i < dockContainer->dockAreaCount(); ++i) { + auto dockArea = dockContainer->dockArea(i); + for (auto dockWidget : dockArea->dockWidgets()) + dockWidget->emitTopLevelChanged(false); + } + } + } +} + +bool DockManagerPrivate::restoreState(const QByteArray &state, int version) +{ + QByteArray currentState = state.startsWith("m_workspaces.contains(original)) - return false; - - const FilePath originalPath = workspaceNameToFilePath(original); - const FilePath clonePath = workspaceNameToFilePath(clone); - - // If the file does not exist, we can still clone - if (!originalPath.exists() || originalPath.copyFile(clonePath)) { - d->m_workspaces.insert(1, clone); - d->m_workspaceDateTimes.insert(clone, clonePath.lastModified()); - emit workspaceListChanged(); - return true; - } + if (!restoreStateFromXml(currentState, version)) { + qWarning() << "RestoreState: Error restoring state!"; return false; } - bool DockManager::renameWorkspace(const QString &original, const QString &newName) - { - if (!cloneWorkspace(original, newName)) - return false; + restoreDockWidgetsOpenState(); + restoreDockAreasIndices(); + emitTopLevelEvents(); - if (original == activeWorkspace()) - openWorkspace(newName); + return true; +} - return deleteWorkspace(original); +DockManager::DockManager(QWidget *parent) + : DockContainerWidget(this, parent) + , d(new DockManagerPrivate(this)) +{ + connect(this, &DockManager::workspaceListChanged, this, [=] { + d->m_workspaceOrderDirty = true; + }); + + createRootSplitter(); + QMainWindow *mainWindow = qobject_cast(parent); + if (mainWindow) + mainWindow->setCentralWidget(this); + + d->m_dockAreaOverlay = new DockOverlay(this, DockOverlay::ModeDockAreaOverlay); + d->m_containerOverlay = new DockOverlay(this, DockOverlay::ModeContainerOverlay); + d->m_containers.append(this); + + if (DockManager::configFlags().testFlag(DockManager::FocusHighlighting)) + d->m_focusController = new DockFocusController(this); +} + +DockManager::~DockManager() +{ + emit aboutToUnloadWorkspace(d->m_workspace.fileName()); + save(); + saveStartupWorkspace(); + + // Using a temporary vector since the destructor of FloatingDockWidgetContainer + // alters d->m_floatingWidgets. + const auto copy = d->m_floatingWidgets; + for (const auto &floatingWidget : copy) { + if (floatingWidget) + delete floatingWidget.get(); + } + delete d; +} + +DockManager::ConfigFlags DockManager::configFlags() +{ + return g_staticConfigFlags; +} + +void DockManager::setConfigFlags(const ConfigFlags flags) +{ + g_staticConfigFlags = flags; +} + +void DockManager::setConfigFlag(eConfigFlag flag, bool on) +{ + internal::setFlag(g_staticConfigFlags, flag, on); +} + +bool DockManager::testConfigFlag(eConfigFlag flag) +{ + return configFlags().testFlag(flag); +} + +IconProvider &DockManager::iconProvider() +{ + static IconProvider instance; + return instance; +} + +int DockManager::startDragDistance() +{ + return static_cast(QApplication::startDragDistance() * 1.5); +} + +void DockManager::setSettings(QSettings *settings) +{ + d->m_settings = settings; +} + +void DockManager::setWorkspacePresetsPath(const QString &path) +{ + d->m_workspacePresetsPath = path; +} + +void DockManager::initialize() +{ + qCInfo(adsLog) << "Initialize DockManager"; + + // Can't continue if settings are not set yet + QTC_ASSERT(d->m_settings, return); + + syncWorkspacePresets(); + prepareWorkspaces(); + + QString workspace = ADS::Constants::DEFAULT_WORKSPACE; + + // Determine workspace to restore at startup + if (autoRestoreWorkspace()) { + const QString lastWorkspace = startupWorkspace(); + if (!lastWorkspace.isEmpty()) { + if (!workspaceExists(lastWorkspace)) { + // This is a fallback mechanism for pre 4.1 settings which stored the workspace name + // instead of the file name. + QString minusVariant = lastWorkspace; + minusVariant.replace(" ", "-"); + minusVariant.append("." + workspaceFileExtension); + + if (workspaceExists(minusVariant)) + workspace = minusVariant; + + QString underscoreVariant = lastWorkspace; + underscoreVariant.replace(" ", "_"); + underscoreVariant.append("." + workspaceFileExtension); + + if (workspaceExists(underscoreVariant)) + workspace = underscoreVariant; + } else { + workspace = lastWorkspace; + } + } else + qWarning() << "Could not restore workspace:" << lastWorkspace; } - bool DockManager::resetWorkspacePreset(const QString &workspace) - { - if (!isWorkspacePreset(workspace)) - return false; + openWorkspace(workspace); +} - const FilePath fileName = workspaceNameToFilePath(workspace); +DockAreaWidget *DockManager::addDockWidget(DockWidgetArea area, + DockWidget *dockWidget, + DockAreaWidget *dockAreaWidget) +{ + d->m_dockWidgetsMap.insert(dockWidget->objectName(), dockWidget); + return DockContainerWidget::addDockWidget(area, dockWidget, dockAreaWidget); +} - if (!fileName.removeFile()) - return false; +DockAreaWidget *DockManager::addDockWidgetTab(DockWidgetArea area, DockWidget *dockWidget) +{ + DockAreaWidget *areaWidget = lastAddedDockAreaWidget(area); + if (areaWidget) + return addDockWidget(ADS::CenterDockWidgetArea, dockWidget, areaWidget); + else if (!openedDockAreas().isEmpty()) + return addDockWidget(area, dockWidget, openedDockAreas().constLast()); + else + return addDockWidget(area, dockWidget, nullptr); +} - QDir presetsDir(d->m_workspacePresetsPath); - bool result = QFile::copy(presetsDir.filePath(workspaceNameToFileName(workspace)), - fileName.toFSPathString()); - if (result) - d->m_workspaceDateTimes.insert(workspace, QDateTime::currentDateTime()); +DockAreaWidget *DockManager::addDockWidgetTabToArea(DockWidget *dockWidget, + DockAreaWidget *dockAreaWidget) +{ + return addDockWidget(ADS::CenterDockWidgetArea, dockWidget, dockAreaWidget); +} - return result; +FloatingDockContainer *DockManager::addDockWidgetFloating(DockWidget *dockWidget) +{ + d->m_dockWidgetsMap.insert(dockWidget->objectName(), dockWidget); + DockAreaWidget *oldDockArea = dockWidget->dockAreaWidget(); + if (oldDockArea) + oldDockArea->removeDockWidget(dockWidget); + + dockWidget->setDockManager(this); + FloatingDockContainer *floatingWidget = new FloatingDockContainer(dockWidget); + floatingWidget->resize(dockWidget->size()); + if (isVisible()) + floatingWidget->show(); + else + d->m_uninitializedFloatingWidgets.append(floatingWidget); + + return floatingWidget; +} + +DockWidget *DockManager::findDockWidget(const QString &objectName) const +{ + return d->m_dockWidgetsMap.value(objectName, nullptr); +} + +void DockManager::removeDockWidget(DockWidget *dockWidget) +{ + emit dockWidgetAboutToBeRemoved(dockWidget); + d->m_dockWidgetsMap.remove(dockWidget->objectName()); + DockContainerWidget::removeDockWidget(dockWidget); + emit dockWidgetRemoved(dockWidget); +} + +QMap DockManager::dockWidgetsMap() const +{ + return d->m_dockWidgetsMap; +} + +const QList DockManager::dockContainers() const +{ + return d->m_containers; +} + +const QList> DockManager::floatingWidgets() const +{ + return d->m_floatingWidgets; +} + +unsigned int DockManager::zOrderIndex() const +{ + return 0; +} + +QByteArray DockManager::saveState(const QString &displayName, int version) const +{ + qCInfo(adsLog) << "Save state" << displayName; + + QByteArray xmlData; + QXmlStreamWriter stream(&xmlData); + auto configFlags = DockManager::configFlags(); + stream.setAutoFormatting(configFlags.testFlag(XmlAutoFormattingEnabled)); + stream.setAutoFormattingIndent(workspaceXmlFormattingIndent); + stream.writeStartDocument(); + stream.writeStartElement("QtAdvancedDockingSystem"); + stream.writeAttribute("version", QString::number(CurrentVersion)); + stream.writeAttribute("userVersion", QString::number(version)); + stream.writeAttribute("containers", QString::number(d->m_containers.count())); + stream.writeAttribute(workspaceDisplayNameAttribute.toString(), displayName); + for (auto container : std::as_const(d->m_containers)) + container->saveState(stream); + + stream.writeEndElement(); + stream.writeEndDocument(); + return xmlData; +} + +bool DockManager::restoreState(const QByteArray &state, int version) +{ + // Prevent multiple calls as long as state is not restore. This may happen, if + // QApplication::processEvents() is called somewhere. + if (d->m_restoringState) + return false; + + // We hide the complete dock manager here. Restoring the state means that DockWidgets are + // removed from the DockArea internal stack layout which in turn means, that each time a + // widget is removed the stack will show and raise the next available widget which in turn + // triggers show events for the dock widgets. To avoid this we hide the dock manager. + // Because there will be no processing of application events until this function is + // finished, the user will not see this hiding. + bool isHidden = this->isHidden(); + if (!isHidden) + hide(); + + d->m_restoringState = true; + emit restoringState(); + bool result = d->restoreState(state, version); + d->m_restoringState = false; + if (!isHidden) + show(); + + emit stateRestored(); + return result; +} + +bool DockManager::isRestoringState() const +{ + return d->m_restoringState; +} + +void DockManager::setDockWidgetFocused(DockWidget *dockWidget) +{ + if (d->m_focusController) + d->m_focusController->setDockWidgetFocused(dockWidget); +} + +void DockManager::registerFloatingWidget(FloatingDockContainer *floatingWidget) +{ + d->m_floatingWidgets.append(floatingWidget); + emit floatingWidgetCreated(floatingWidget); + qCInfo(adsLog) << "d->FloatingWidgets.count() " << d->m_floatingWidgets.count(); +} + +void DockManager::removeFloatingWidget(FloatingDockContainer *floatingWidget) +{ + d->m_floatingWidgets.removeAll(floatingWidget); +} + +void DockManager::registerDockContainer(DockContainerWidget *dockContainer) +{ + d->m_containers.append(dockContainer); +} + +void DockManager::removeDockContainer(DockContainerWidget *dockContainer) +{ + if (this != dockContainer) + d->m_containers.removeAll(dockContainer); +} + +DockOverlay *DockManager::containerOverlay() const +{ + return d->m_containerOverlay; +} + +DockOverlay *DockManager::dockAreaOverlay() const +{ + return d->m_dockAreaOverlay; +} + +void DockManager::notifyWidgetOrAreaRelocation(QWidget *droppedWidget) +{ + if (d->m_focusController) + d->m_focusController->notifyWidgetOrAreaRelocation(droppedWidget); +} + +void DockManager::notifyFloatingWidgetDrop(FloatingDockContainer *floatingWidget) +{ + if (d->m_focusController) + d->m_focusController->notifyFloatingWidgetDrop(floatingWidget); +} + +void DockManager::notifyDockWidgetRelocation(DockWidget *, DockContainerWidget *) {} + +void DockManager::notifyDockAreaRelocation(DockAreaWidget *, DockContainerWidget *) {} + +void DockManager::showEvent(QShowEvent *event) +{ + Super::showEvent(event); + if (d->m_uninitializedFloatingWidgets.empty()) + return; + + for (auto floatingWidget : std::as_const(d->m_uninitializedFloatingWidgets)) + floatingWidget->show(); + + d->m_uninitializedFloatingWidgets.clear(); +} + +Workspace *DockManager::activeWorkspace() const +{ + return &d->m_workspace; +} + +QString DockManager::startupWorkspace() const +{ + QTC_ASSERT(d->m_settings, return {}); + return d->m_settings->value(Constants::STARTUP_WORKSPACE_SETTINGS_KEY).toString(); +} + +bool DockManager::autoRestoreWorkspace() const +{ + QTC_ASSERT(d->m_settings, return false); + return d->m_settings->value(Constants::AUTO_RESTORE_WORKSPACE_SETTINGS_KEY).toBool(); +} + +const QList &DockManager::workspaces() const +{ + return d->m_workspaces; +} + +int DockManager::workspaceIndex(const QString &fileName) const +{ + return Utils::indexOf(d->m_workspaces, [&fileName](const Workspace &workspace) { + return workspace == fileName; + }); +} + +bool DockManager::workspaceExists(const QString &fileName) const +{ + return workspaceIndex(fileName) >= 0; +} + +Workspace *DockManager::workspace(const QString &fileName) const +{ + auto result = std::find_if(std::begin(d->m_workspaces), + std::end(d->m_workspaces), + [&fileName](const Workspace &workspace) { + return workspace == fileName; + }); + + if (result != std::end(d->m_workspaces)) + return &*result; + + return nullptr; +} + +Workspace *DockManager::workspace(int index) const +{ + if (index < 0 || index >= d->m_workspaces.count()) + return nullptr; + + return &d->m_workspaces[index]; +} + +QDateTime DockManager::workspaceDateTime(const QString &fileName) const +{ + Workspace *w = workspace(fileName); + + if (w) + return w->lastModified(); + + return QDateTime(); +} + +bool DockManager::moveWorkspace(int from, int to) +{ + qCInfo(adsLog) << "Move workspaces" << from << ">" << to; + + int c = d->m_workspaces.count(); + + from = std::clamp(from, 0, c); + to = std::clamp(to, 0, c); + + if (from == to) + return false; + + d->m_workspaces.move(from, to); + emit workspaceListChanged(); + + return true; +} + +bool DockManager::moveWorkspaceUp(const QString &fileName) +{ + qCInfo(adsLog) << "Move workspace up" << fileName; + + int i = workspaceIndex(fileName); + + // If workspace does not exist or is first item + if (i <= 0) + return false; + + d->m_workspaces.swapItemsAt(i, i - 1); + emit workspaceListChanged(); + + return true; +} + +bool DockManager::moveWorkspaceDown(const QString &fileName) +{ + qCInfo(adsLog) << "Move workspace down" << fileName; + + int i = workspaceIndex(fileName); + int c = d->m_workspaces.count(); + + // If workspace does not exist or is last item + if (i < 0 || i >= (c - 1)) + return false; + + d->m_workspaces.swapItemsAt(i, i + 1); + emit workspaceListChanged(); + + return true; +} + +FilePath DockManager::workspaceNameToFilePath(const QString &workspaceName) const +{ + QTC_ASSERT(d->m_settings, return {}); + return userDirectory().pathAppended(workspaceNameToFileName(workspaceName)); +} + +QString DockManager::workspaceNameToFileName(const QString &workspaceName) const +{ + QString copy = workspaceName; + copy.replace(" ", "-"); + copy.append("." + workspaceFileExtension); + return copy; +} + +void DockManager::uniqueWorkspaceFileName(QString &fileName) const +{ + auto filePath = FilePath::fromString(fileName); + + if (workspaceExists(fileName)) { + int i = 1; + QString copy; + do { + copy = filePath.baseName() + QString::number(i) + "." + filePath.completeSuffix(); + ++i; + } while (workspaceExists(copy)); + fileName = copy; + } +} + +void DockManager::showWorkspaceMananger() +{ + save(); // Save current workspace + + WorkspaceDialog workspaceDialog(this, parentWidget()); + workspaceDialog.setAutoLoadWorkspace(autoRestoreWorkspace()); + workspaceDialog.exec(); + + if (d->m_workspaceOrderDirty) { + writeOrder(); + d->m_workspaceOrderDirty = false; } - void DockManager::setModeChangeState(bool value) - { - d->m_modeChangeState = value; + QTC_ASSERT(d->m_settings, return); + d->m_settings->setValue(Constants::AUTO_RESTORE_WORKSPACE_SETTINGS_KEY, + workspaceDialog.autoLoadWorkspace()); +} + +expected_str DockManager::createWorkspace(const QString &workspaceName) +{ + qCInfo(adsLog) << "Create workspace" << workspaceName; + + QString fileName = workspaceNameToFileName(workspaceName); + uniqueWorkspaceFileName(fileName); + const FilePath filePath = userDirectory().pathAppended(fileName); + + expected_str result = write(filePath, saveState(workspaceName)); + if (!result) + return make_unexpected(result.error()); + + Workspace workspace(filePath, false); + + d->m_workspaces.append(workspace); + emit workspaceListChanged(); + return workspace.fileName(); +} + +expected_str DockManager::openWorkspace(const QString &fileName) +{ + qCInfo(adsLog) << "Open workspace" << fileName; + + Workspace *wrk = workspace(fileName); + if (!wrk) + return make_unexpected(Tr::tr("Workspace \"%1\" does not exist.").arg(fileName)); + + // Do nothing if workspace is already loaded, exception if it is a preset workspace. In this + // case we still want to be able to load the default workspace to undo potential user changes. + if (*wrk == *activeWorkspace() && !wrk->isPreset()) + return {}; // true + + if (activeWorkspace()->isValid()) { + // Allow everyone to set something in the workspace and before saving + emit aboutToUnloadWorkspace(activeWorkspace()->fileName()); + expected_str saveResult = save(); + if (!saveResult) + return saveResult; } - bool DockManager::isModeChangeState() const - { - return d->m_modeChangeState; + // Try loading the file + const expected_str data = loadWorkspace(*wrk); + if (!data) + return make_unexpected(data.error()); + + emit openingWorkspace(wrk->fileName()); + // If data was loaded from file try to restore its state + if (!data->isNull() && !restoreState(*data)) + return make_unexpected(Tr::tr("Cannot restore \"%1\".").arg(wrk->filePath().toUserOutput())); + + d->m_workspace = *wrk; + emit workspaceLoaded(wrk->fileName()); + + return {}; +} + +expected_str DockManager::reloadActiveWorkspace() +{ + qCInfo(adsLog) << "Reload active workspace"; + + Workspace *wrk = activeWorkspace(); + + if (!workspaces().contains(*wrk)) + return make_unexpected( + Tr::tr("Cannot reload \"%1\", it is not contained in the list of workspaces") + .arg(wrk->filePath().toUserOutput())); + + const expected_str data = loadWorkspace(*wrk); + if (!data) + return make_unexpected(data.error()); + + if (!data->isNull() && !restoreState(*data)) + return make_unexpected(Tr::tr("Cannot restore \"%1\".").arg(wrk->filePath().toUserOutput())); + + emit workspaceReloaded(wrk->fileName()); + + return {}; +} + +bool DockManager::deleteWorkspace(const QString &fileName) +{ + qCInfo(adsLog) << "Delete workspace" << fileName; + + Workspace *w = workspace(fileName); + if (!w) { + qWarning() << "Workspace" << fileName << "does not exist"; + return false; } - void DockManager::importWorkspace(const QString &workspace) - { - // Extract workspace name - QString workspaceName = fileNameToWorkspaceName(workspace); - - // Check if the workspace is already contained in the list of workspaces. If that is the case - // add a counter to the workspace name. - if (workspaces().contains(workspaceName)) { - int i = 2; - QString copy; - do { - copy = workspaceName + QLatin1String(" (") + QString::number(i) + QLatin1Char(')'); - ++i; - } while (workspaces().contains(copy)); - workspaceName = copy; - } - - QString fileName = workspaceNameToFileName(workspaceName); - QFile file(workspace); - if (!file.exists()) { - qCInfo(adsLog) << QString("File doesn't exist '%1'").arg(workspace); - return; - } - - QDir workspaceDir(QFileInfo(d->m_settings->fileName()).path() + QLatin1Char('/') + m_dirName); - - if (!file.copy(workspaceDir.filePath(fileName))) { - qCInfo(adsLog) << QString("Could not copy '%1' to '%2' error: %3").arg( - workspace, workspaceDir.filePath(fileName), file.errorString()); - } else { - d->m_workspaces.insert(1, workspaceName); - d->m_workspaceDateTimes.insert(workspaceName, - workspaceNameToFilePath(workspaceName).lastModified()); - d->m_workspaceListDirty = true; - // After importing the workspace, update the workspace list - workspaces(); + // Remove corresponding workspace file + if (w->exists()) { + const FilePath filePath = w->filePath(); + if (filePath.removeFile()) { + d->m_workspaces.removeOne(*w); + emit workspaceRemoved(); emit workspaceListChanged(); + return true; } } - void DockManager::exportWorkspace(const QString &target, const QString &workspace) - { - // If we came this far the user decided that in case the target already exists to overwrite it. - // We first need to remove the existing file, otherwise QFile::copy() will fail. - const FilePath targetFile = FilePath::fromUserInput(target); + return false; +} - // Remove the file which supposed to be overwritten - if (targetFile.exists()) { - if (!targetFile.removeFile()) { - qCInfo(adsLog) << QString("Couldn't remove '%1'").arg(targetFile.toUserOutput()); - return; - } - } +void DockManager::deleteWorkspaces(const QStringList &fileNames) +{ + for (const QString &fileName : fileNames) + deleteWorkspace(fileName); +} - // Check if the target directory exists - if (!targetFile.parentDir().exists()) { - qCInfo(adsLog) << QString("Directory doesn't exist '%1'") - .arg(targetFile.parentDir().toUserOutput()); - return; - } +expected_str DockManager::cloneWorkspace(const QString &originalFileName, + const QString &cloneName) +{ + qCInfo(adsLog) << "Clone workspace" << originalFileName << cloneName; - // Check if the workspace exists - FilePath workspaceFile = workspaceNameToFilePath(workspace); - if (!workspaceFile.exists()) { - qCInfo(adsLog) << QString("Workspace doesn't exist '%1'") - .arg(workspaceFile.toUserOutput()); - return; - } + Workspace *w = workspace(originalFileName); + if (!w) + return make_unexpected(Tr::tr("Workspace \"%1\" does not exist.").arg(originalFileName)); - // Finally copy the workspace to the target - const expected_str copyResult = workspaceFile.copyFile(targetFile); - if (!copyResult) { - qCInfo(adsLog) << QString("Could not copy '%1' to '%2' error: %3") - .arg(workspace, workspaceFile.toUserOutput(), copyResult.error()); + const FilePath originalPath = w->filePath(); + + if (!originalPath.exists()) + return make_unexpected( + Tr::tr("Workspace \"%1\" does not exist.").arg(originalPath.toUserOutput())); + + const FilePath clonePath = workspaceNameToFilePath(cloneName); + + const expected_str copyResult = originalPath.copyFile(clonePath); + if (!copyResult) + return make_unexpected(Tr::tr("Could not clone '%1' due to: %2") + .arg(originalPath.toUserOutput(), copyResult.error())); + + writeDisplayName(clonePath, cloneName); + d->m_workspaces.append(Workspace(clonePath)); + emit workspaceListChanged(); + return clonePath.fileName(); +} + +expected_str DockManager::renameWorkspace(const QString &originalFileName, + const QString &newName) +{ + qCInfo(adsLog) << "Rename workspace" << originalFileName << newName; + + Workspace *w = workspace(originalFileName); + if (!w) + return make_unexpected(Tr::tr("Workspace \"%1\" does not exist.").arg(originalFileName)); + + w->setName(newName); + + emit workspaceListChanged(); + return originalFileName; +} + +expected_str DockManager::resetWorkspacePreset(const QString &fileName) +{ + qCInfo(adsLog) << "Reset workspace" << fileName; + + Workspace *w = workspace(fileName); + if (!w) + return make_unexpected(Tr::tr("Workspace \"%1\" does not exist.").arg(fileName)); + + if (!w->isPreset()) + return make_unexpected(Tr::tr("Workspace \"%1\" is not a preset.").arg(fileName)); + + const FilePath filePath = w->filePath(); + + if (!filePath.removeFile()) + return make_unexpected(Tr::tr("Cannot remove \"%1\".").arg(filePath.toUserOutput())); + + return presetDirectory().pathAppended(fileName).copyFile(filePath); +} + +expected_str DockManager::save() +{ + if (isModeChangeState()) + return make_unexpected(Tr::tr("Cannot save workspace while in mode change state.")); + + emit aboutToSaveWorkspace(); + + Workspace *w = activeWorkspace(); + + return write(w->filePath(), saveState(w->name())); +} + +void DockManager::setModeChangeState(bool value) +{ + d->m_modeChangeState = value; +} + +bool DockManager::isModeChangeState() const +{ + return d->m_modeChangeState; +} + +expected_str DockManager::importWorkspace(const QString &filePath) +{ + qCInfo(adsLog) << "Import workspace" << filePath; + + const FilePath sourceFilePath = FilePath::fromUserInput(filePath); + + if (!sourceFilePath.exists()) + return make_unexpected( + Tr::tr("File \"%1\" does not exist.").arg(sourceFilePath.toUserOutput())); + + // Extract workspace file name. Check if the workspace is already contained in the list of + // workspaces. If that is the case add a counter to the workspace file name. + QString fileName = sourceFilePath.fileName(); + uniqueWorkspaceFileName(fileName); + + const FilePath targetFilePath = userDirectory().pathAppended(fileName); + + const expected_str copyResult = sourceFilePath.copyFile(targetFilePath); + if (!copyResult) + return make_unexpected( + Tr::tr("Could not copy \"%1\" to \"%2\" due to: %3") + .arg(filePath, targetFilePath.toUserOutput(), copyResult.error())); + + d->m_workspaces.append(Workspace(targetFilePath)); + emit workspaceListChanged(); + + return targetFilePath.fileName(); +} + +expected_str DockManager::exportWorkspace(const QString &targetFilePath, + const QString &sourceFileName) +{ + qCInfo(adsLog) << "Export workspace" << targetFilePath << sourceFileName; + + // If we came this far the user decided that in case the target already exists to overwrite it. + // We first need to remove the existing file, otherwise QFile::copy() will fail. + const FilePath targetFile = FilePath::fromUserInput(targetFilePath); + + // Remove the file which supposed to be overwritten + if (targetFile.exists()) { + if (!targetFile.removeFile()) { + return make_unexpected( + Tr::tr("Could not remove \"%1\".").arg(targetFile.toUserOutput())); } } - bool DockManager::write(const QString &workspace, const QByteArray &data, QString *errorString) const - { - const FilePath fileName = workspaceNameToFilePath(workspace); + // Check if the target directory exists + if (!targetFile.parentDir().exists()) + return make_unexpected( + Tr::tr("Directory does not exist\"%1\".").arg(targetFile.parentDir().toUserOutput())); - QDir tmp; - tmp.mkpath(fileName.toFileInfo().path()); - FileSaver fileSaver(fileName, QIODevice::Text); - if (!fileSaver.hasError()) - fileSaver.write(data); + // Check if the workspace exists + const FilePath workspaceFile = userDirectory().pathAppended(sourceFileName); + if (!workspaceFile.exists()) + return make_unexpected( + Tr::tr("Workspace does not exist '%1'").arg(workspaceFile.toUserOutput())); - bool ok = fileSaver.finalize(); + // Finally copy the workspace to the target + const expected_str copyResult = workspaceFile.copyFile(targetFile); + if (!copyResult) + return make_unexpected( + Tr::tr("Could not copy \"%1\" to \"%2\" due to: %3") + .arg(sourceFileName, workspaceFile.toUserOutput(), copyResult.error())); - if (!ok && errorString) - *errorString = fileSaver.errorString(); + return workspaceFile.fileName(); +} - return ok; - } +QStringList DockManager::loadOrder(const FilePath &dir) const +{ + qCInfo(adsLog) << "Load workspace order" << dir.toUserOutput(); - bool DockManager::write(const QString &workspace, const QByteArray &data, QWidget *parent) const - { - QString errorString; - const bool success = write(workspace, data, &errorString); - if (!success) - QMessageBox::critical(parent, ::Utils::Tr::tr("File Error"), errorString); - return success; - } + if (dir.isEmpty()) + return {}; - QByteArray DockManager::loadWorkspace(const QString &workspace) const - { - const FilePath fileName = workspaceNameToFilePath(workspace); - if (fileName.exists()) { - const expected_str data = fileName.fileContents(); + auto filePath = dir.pathAppended(workspaceOrderFileName.toString()); + QByteArray data = loadFile(filePath); - if (!data) { - QMessageBox::warning(parentWidget(), - Tr::tr("Cannot Restore Workspace"), - Tr::tr("Could not restore workspace %1") - .arg(fileName.toUserOutput())); - - qCWarning(adsLog) << QString("Could not restore workspace %1: %2") - .arg(fileName.toUserOutput()) - .arg(data.error()); - - return {}; - } - return data.value(); - } + if (data.isEmpty()) { + qWarning() << "Order data is empty" << filePath.toUserOutput(); return {}; } - void DockManager::syncWorkspacePresets() - { - // Get a list of all workspace presets - QSet presets = workspacePresets(); + QJsonParseError parseError; + QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &parseError); + if (parseError.error != QJsonParseError::NoError) { + qWarning() << "Failed to parse JSON file" << filePath << ":" << parseError.errorString(); + return {}; + } - // Get a list of all available workspaces - QSet availableWorkspaces = Utils::toSet(workspaces()); - presets.subtract(availableWorkspaces); + return jsonDoc.toVariant().toStringList(); +} - // Copy all missing workspace presets over to the local workspace folder - QDir presetsDir(d->m_workspacePresetsPath); - QDir workspaceDir(QFileInfo(d->m_settings->fileName()).path() + QLatin1Char('/') + m_dirName); - // Try do create the 'workspaces' directory if it doesn't exist already - workspaceDir.mkpath(workspaceDir.absolutePath()); - if (!workspaceDir.exists()) { - qCInfo(adsLog) << QString("Could not make directory '%1')").arg(workspaceDir.absolutePath()); - return; +bool DockManager::writeOrder() const +{ + qCInfo(adsLog) << "Write workspace order"; + + QJsonArray order; + for (const Workspace &workspace : d->m_workspaces) + order.append(QJsonValue(workspace.fileName())); + + const FilePath orderFilePath = userDirectory().pathAppended(workspaceOrderFileName.toString()); + + if (!orderFilePath.writeFileContents(QJsonDocument{order}.toJson())) { + qWarning() << "Faild to write order" << orderFilePath.toUserOutput(); + return false; + } + + return true; +} + +QList DockManager::loadWorkspaces(const FilePath &dir) const +{ + if (dir.isEmpty()) + return {}; + + QList workspaces; + + dir.iterateDirectory( + [&workspaces](const FilePath &filePath, const FilePathInfo &) { + Workspace workspace(filePath); + //workspace.setName(displayName(filePath)); TODO + workspaces.append(workspace); + return IterationPolicy::Continue; + }, + FileFilter(QStringList{"*." + workspaceFileExtension}, QDir::Files)); + + return workspaces; +} + +FilePath DockManager::presetDirectory() const +{ + return FilePath::fromString(d->m_workspacePresetsPath); +} + +FilePath DockManager::userDirectory() const +{ + QTC_ASSERT(d->m_settings, return {}); + return FilePath::fromString(QFileInfo(d->m_settings->fileName()).path()) + .pathAppended(workspaceFolderName.toString()); +} + +QByteArray DockManager::loadFile(const FilePath &filePath) +{ + qCInfo(adsLog) << "Load" << filePath; + + if (!filePath.exists()) { + qWarning() << "File does not exist" << filePath.toUserOutput(); + return {}; + } + + const expected_str data = filePath.fileContents(); + + if (!data) { + qWarning() << "Could not open" << filePath.toUserOutput() << data.error(); + return {}; + } + + return data.value(); +} + +QString DockManager::readDisplayName(const FilePath &filePath) +{ + if (!filePath.exists()) + return {}; + + auto data = loadFile(filePath); + + if (data.isEmpty()) + return {}; + + auto tmp = data.startsWith(" content = filePath.fileContents(); + + QTC_ASSERT_EXPECTED(content, return false); + + QDomDocument doc; + QString error_msg; + int error_line, error_col; + if (!doc.setContent(*content, &error_msg, &error_line, &error_col)) { + qWarning() << QString("XML error on line %1, col %2: %3") + .arg(error_line) + .arg(error_col) + .arg(error_msg); + return false; + } + + QDomElement docElem = doc.documentElement(); + docElem.setAttribute(workspaceDisplayNameAttribute.toString(), displayName); + + const expected_str result = write(filePath, doc.toByteArray(workspaceXmlFormattingIndent)); + if (!result) { + qWarning() << "Could not write display name" << displayName << "to" << filePath << ":" + << result.error(); + return false; + } + + return true; +} + +expected_str DockManager::write(const FilePath &filePath, const QByteArray &data) +{ + qCInfo(adsLog) << "Write" << filePath; + + if (!filePath.parentDir().ensureWritableDir()) + return make_unexpected(Tr::tr("Cannot write to \"%1\".").arg(filePath.toUserOutput())); + + FileSaver fileSaver(filePath, QIODevice::Text); + if (!fileSaver.hasError()) + fileSaver.write(data); + + if (!fileSaver.finalize()) + return make_unexpected(Tr::tr("Cannot write to \"%1\" due to: %2") + .arg(filePath.toUserOutput(), fileSaver.errorString())); + + return {}; +} + +expected_str DockManager::loadWorkspace(const Workspace &workspace) const +{ + qCInfo(adsLog) << "Load workspace" << workspace.fileName(); + + if (!workspace.exists()) + return make_unexpected( + Tr::tr("Workspace \"%1\" does not exist.").arg(workspace.filePath().toUserOutput())); + + return workspace.filePath().fileContents(); +} + +void DockManager::syncWorkspacePresets() +{ + qCInfo(adsLog) << "Sync workspaces"; + + auto fileFilter = FileFilter(QStringList{"*." + workspaceFileExtension, "*.json"}, QDir::Files); + + // All files in the workspace preset directory with the file extension wrk or json + auto presetFiles = presetDirectory().dirEntries(fileFilter); + auto userFiles = userDirectory().dirEntries(fileFilter); + + // Try do create the 'workspaces' directory if it doesn't exist already + if (!userDirectory().ensureWritableDir()) { + qWarning() << QString("Could not make directory '%1')").arg(userDirectory().toString()); + return; + } + + for (const auto &filePath : presetFiles) { + const int index = Utils::indexOf(userFiles, [&filePath](const FilePath &other) { + return other.fileName() == filePath.fileName(); + }); + + // File already contained in user directory + if (index >= 0) { + auto userFile = userFiles[index]; + + // If *.wrk file and displayName attribute is empty set the displayName. This + // should fix old workspace files which don't have the displayName attribute. + if (userFile.suffix() == workspaceFileExtension && readDisplayName(userFile).isEmpty()) + writeDisplayName(userFile, readDisplayName(filePath)); + + continue; } - for (const auto &preset : presets) { - QString fileName = workspaceNameToFileName(preset); - QString filePath = presetsDir.filePath(fileName); - QFile file(filePath); + const expected_str copyResult = filePath.copyFile( + userDirectory().pathAppended(filePath.fileName())); + if (!copyResult) + qWarning() << QString("Could not copy '%1' to '%2' due to %3") + .arg(filePath.toString(), + userDirectory().toString(), + copyResult.error()); + } +} - if (file.exists()) { - if (!file.copy(workspaceDir.filePath(fileName))) - qCInfo(adsLog) << QString("Could not copy '%1' to '%2' error: %3").arg( - filePath, workspaceDir.filePath(fileName), file.errorString()); +void DockManager::prepareWorkspaces() +{ + qCInfo(adsLog) << "Prepare workspace"; - d->m_workspaceListDirty = true; - } - } + auto presetWorkspaces = loadWorkspaces(presetDirectory()); + auto userWorkspaces = loadWorkspaces(userDirectory()); + auto userOrder = loadOrder(userDirectory()); - // After copying over missing workspace presets, update the workspace list - workspaces(); + std::vector> tmp; + + for (Workspace &userWorkspace : userWorkspaces) { + userWorkspace.setPreset(presetWorkspaces.contains(userWorkspace)); + qsizetype index = userOrder.indexOf(userWorkspace.fileName()); + if (index == -1) + index = userWorkspaces.count(); + tmp.push_back(std::make_pair(index, userWorkspace)); } - void DockManager::saveStartupWorkspace() - { - QTC_ASSERT(d->m_settings, return ); - d->m_settings->setValue(Constants::STARTUP_WORKSPACE_SETTINGS_KEY, activeWorkspace()); - } + Utils::sort(tmp, [](auto const &a, auto const &b) { return a.first < b.first; }); - void DockManager::notifyWidgetOrAreaRelocation(QWidget *droppedWidget) - { - if (d->m_focusController) - d->m_focusController->notifyWidgetOrAreaRelocation(droppedWidget); - } + d->m_workspaces = Utils::transform>(tmp, + [](const std::pair &p) { + return p.second; + }); - void DockManager::notifyFloatingWidgetDrop(FloatingDockContainer *floatingWidget) - { - if (d->m_focusController) - d->m_focusController->notifyFloatingWidgetDrop(floatingWidget); - } + emit workspaceListChanged(); +} - void DockManager::setDockWidgetFocused(DockWidget *dockWidget) - { - if (d->m_focusController) - d->m_focusController->setDockWidgetFocused(dockWidget); - } +void DockManager::saveStartupWorkspace() +{ + QTC_ASSERT(d->m_settings, return); + d->m_settings->setValue(Constants::STARTUP_WORKSPACE_SETTINGS_KEY, + activeWorkspace()->fileName()); +} } // namespace ADS diff --git a/src/libs/advanceddockingsystem/dockmanager.h b/src/libs/advanceddockingsystem/dockmanager.h index 9ef2a6f3f10..62148d1e234 100644 --- a/src/libs/advanceddockingsystem/dockmanager.h +++ b/src/libs/advanceddockingsystem/dockmanager.h @@ -7,6 +7,7 @@ #include "dockcontainerwidget.h" #include "dockwidget.h" #include "floatingdockcontainer.h" +#include "workspace.h" #include @@ -28,7 +29,7 @@ QT_END_NAMESPACE namespace ADS { namespace Constants { -const char DEFAULT_WORKSPACE[] = "Basic"; // This needs to align with a name of the shipped presets +const char DEFAULT_WORKSPACE[] = "Basic.wrk"; // Needs to align with a shipped preset const char STARTUP_WORKSPACE_SETTINGS_KEY[] = "QML/Designer/StartupWorkspace"; const char AUTO_RESTORE_WORKSPACE_SETTINGS_KEY[] = "QML/Designer/AutoRestoreLastWorkspace"; } // namespace Constants @@ -46,14 +47,18 @@ class DockWidgetTabPrivate; struct DockAreaWidgetPrivate; class IconProvider; +inline constexpr QStringView workspaceFolderName{u"workspaces"}; +inline constexpr QStringView workspaceFileExtension{u"wrk"}; +inline constexpr QStringView workspaceOrderFileName{u"order.json"}; +inline constexpr QStringView workspaceDisplayNameAttribute{u"displayName"}; +inline const int workspaceXmlFormattingIndent = 2; + /** - * The central dock manager that maintains the complete docking system. - * With the configuration flags you can globally control the functionality - * of the docking system. The dock manager uses an internal stylesheet to - * style its components like splitters, tabs and buttons. If you want to - * disable this stylesheet because your application uses its own, - * just call the function for settings the stylesheet with an empty - * string. + * The central dock manager that maintains the complete docking system. With the configuration flags + * you can globally control the functionality of the docking system. The dock manager uses an + * internal stylesheet to style its components like splitters, tabs and buttons. If you want to + * disable this stylesheet because your application uses its own, just call the function for + * settings the stylesheet with an empty string. * \code * dockManager->setStyleSheet(""); * \endcode @@ -76,71 +81,6 @@ private: friend class FloatingDragPreviewPrivate; friend class DockAreaTitleBar; -protected: - /** - * Registers the given floating widget in the internal list of - * floating widgets - */ - void registerFloatingWidget(FloatingDockContainer *floatingWidget); - - /** - * Remove the given floating widget from the list of registered floating - * widgets - */ - void removeFloatingWidget(FloatingDockContainer *floatingWidget); - - /** - * Registers the given dock container widget - */ - void registerDockContainer(DockContainerWidget *dockContainer); - - /** - * Remove dock container from the internal list of registered dock - * containers - */ - void removeDockContainer(DockContainerWidget *dockContainer); - - /** - * Overlay for containers - */ - DockOverlay *containerOverlay() const; - - /** - * Overlay for dock areas - */ - DockOverlay *dockAreaOverlay() const; - - /** - * A container needs to call this function if a widget has been dropped - * into it - */ - void notifyWidgetOrAreaRelocation(QWidget *droppedWidget); - - /** - * This function is called, if a floating widget has been dropped into - * an new position. - * When this function is called, all dock widgets of the FloatingWidget - * are already inserted into its new position - */ - void notifyFloatingWidgetDrop(FloatingDockContainer *floatingWidget); - - /** - * This function is called, if the given DockWidget has been relocated from - * the old container ContainerOld to the new container DockWidget->dockContainer() - */ - void notifyDockWidgetRelocation(DockWidget *dockWidget, DockContainerWidget *containerOld); - - /** - * This function is called, if the given DockAreahas been relocated from - * the old container ContainerOld to the new container DockArea->dockContainer() - */ - void notifyDockAreaRelocation(DockAreaWidget *dockArea, DockContainerWidget *containerOld); - - /** - * Show the floating widgets that has been created floating - */ - void showEvent(QShowEvent *event) override; - public: using Super = DockContainerWidget; @@ -151,12 +91,10 @@ public: enum eConfigFlag { ActiveTabHasCloseButton = 0x0001, //!< If this flag is set, the active tab in a tab area has a close button - DockAreaHasCloseButton - = 0x0002, //!< If the flag is set each dock area has a close button + DockAreaHasCloseButton = 0x0002, //!< If the flag is set each dock area has a close button DockAreaCloseButtonClosesTab = 0x0004, //!< If the flag is set, the dock area close button closes the active tab, if not set, it closes the complete dock area - OpaqueSplitterResize - = 0x0008, //!< See QSplitter::setOpaqueResize() documentation + OpaqueSplitterResize = 0x0008, //!< See QSplitter::setOpaqueResize() documentation XmlAutoFormattingEnabled = 0x0010, //!< If enabled, the XML writer automatically adds line-breaks and indentation to empty sections between elements (ignorable whitespace). XmlCompressionEnabled @@ -177,8 +115,7 @@ public: = 0x1000, ///< If opaque undocking is disabled, then this flag configures if the drag preview is frameless or looks like a real window AlwaysShowTabs = 0x2000, ///< If this option is enabled, the tab of a dock widget is always displayed - even if it is the only visible dock widget in a floating widget. - DockAreaHasUndockButton - = 0x4000, //!< If the flag is set each dock area has an undock button + DockAreaHasUndockButton = 0x4000, //!< If the flag is set each dock area has an undock button DockAreaHasTabsMenuButton = 0x8000, //!< If the flag is set each dock area has a tabs menu button DockAreaHideDisabledButtons @@ -191,39 +128,39 @@ public: = 0x80000, //!< If set, the Floating Widget icon reflects the icon of the current dock widget otherwise it displays application icon HideSingleCentralWidgetTitleBar = 0x100000, //!< If there is only one single visible dock widget in the main dock container (the dock manager) and if this flag is set, then the titlebar of this dock widget will be hidden - //!< this only makes sense for non draggable and non floatable widgets and enables the creation of some kind of "central" widget + //!< this only makes sense for non draggable and non floatable widgets and enables the creation of some kind of "central" widget FocusHighlighting = 0x200000, //!< enables styling of focused dock widget tabs or floating widget titlebar EqualSplitOnInsertion = 0x400000, ///!< if enabled, the space is equally distributed to all widgets in a splitter - DefaultDockAreaButtons = DockAreaHasCloseButton - | DockAreaHasUndockButton - | DockAreaHasTabsMenuButton,///< default configuration of dock area title bar buttons + DefaultDockAreaButtons + = DockAreaHasCloseButton | DockAreaHasUndockButton + | DockAreaHasTabsMenuButton, ///< default configuration of dock area title bar buttons - DefaultBaseConfig = DefaultDockAreaButtons - | ActiveTabHasCloseButton - | XmlCompressionEnabled - | FloatingContainerHasWidgetTitle,///< default base configuration settings + DefaultBaseConfig = DefaultDockAreaButtons | ActiveTabHasCloseButton + | XmlAutoFormattingEnabled + | FloatingContainerHasWidgetTitle, ///< default base configuration settings - DefaultOpaqueConfig = DefaultBaseConfig - | OpaqueSplitterResize - | OpaqueUndocking, ///< the default configuration with opaque operations - this may cause issues if ActiveX or Qt 3D windows are involved + DefaultOpaqueConfig + = DefaultBaseConfig | OpaqueSplitterResize + | OpaqueUndocking, ///< the default configuration with opaque operations - this may cause issues if ActiveX or Qt 3D windows are involved - DefaultNonOpaqueConfig = DefaultBaseConfig - | DragPreviewShowsContentPixmap, ///< the default configuration for non opaque operations + DefaultNonOpaqueConfig + = DefaultBaseConfig + | DragPreviewShowsContentPixmap, ///< the default configuration for non opaque operations - NonOpaqueWithWindowFrame = DefaultNonOpaqueConfig - | DragPreviewHasWindowFrame ///< the default configuration for non opaque operations that show a real window with frame + NonOpaqueWithWindowFrame + = DefaultNonOpaqueConfig + | DragPreviewHasWindowFrame ///< the default configuration for non opaque operations that show a real window with frame }; Q_DECLARE_FLAGS(ConfigFlags, eConfigFlag) /** * Default Constructor. - * If the given parent is a QMainWindow, the dock manager sets itself as the - * central widget. - * Before you create any dock widgets, you should properly setup the - * configuration flags via setConfigFlags(). + * If the given parent is a QMainWindow, the dock manager sets itself as the central widget. + * Before you create any dock widgets, you should properly setup the configuration flags + * via setConfigFlags(). */ DockManager(QWidget *parent = nullptr); @@ -238,9 +175,8 @@ public: static ConfigFlags configFlags(); /** - * Sets the global configuration flags for the whole docking system. - * Call this function before you create the dock manager and before - * your create the first dock widget. + * Sets the global configuration flags for the whole docking system. Call this function before + * you create the dock manager and before your create the first dock widget. */ static void setConfigFlags(const ConfigFlags flags); @@ -257,20 +193,19 @@ public: /** * Returns the global icon provider. - * The icon provider enables the use of custom icons in case using - * styleheets for icons is not an option. + * The icon provider enables the use of custom icons in case using styleheets for icons is not + * an option. */ static IconProvider &iconProvider(); /** - * The distance the user needs to move the mouse with the left button - * hold down before a dock widget start floating. + * The distance the user needs to move the mouse with the left button hold down before a dock + * widget start floating. */ static int startDragDistance(); /** - * Helper function to set focus depending on the configuration of the - * FocusStyling flag + * Helper function to set focus depending on the configuration of the FocusStyling flag */ template static void setWidgetFocus(QWidgetPtr widget) @@ -295,11 +230,10 @@ public: /** * Adds dockwidget into the given area. - * If DockAreaWidget is not null, then the area parameter indicates the area - * into the DockAreaWidget. If DockAreaWidget is null, the Dockwidget will - * be dropped into the container. If you would like to add a dock widget - * tabified, then you need to add it to an existing dock area object - * into the CenterDockWidgetArea. The following code shows this: + * If DockAreaWidget is not null, then the area parameter indicates the area into the + * DockAreaWidget. If DockAreaWidget is null, the Dockwidget will be dropped into the container. + * If you would like to add a dock widget tabified, then you need to add it to an existing + * dock area object into the CenterDockWidgetArea. The following code shows this: * \code * DockManager->addDockWidget(ads::CenterDockWidgetArea, NewDockWidget, * ExisitingDockArea); @@ -311,22 +245,18 @@ public: DockAreaWidget *dockAreaWidget = nullptr); /** - * This function will add the given Dockwidget to the given dock area as - * a new tab. - * If no dock area widget exists for the given area identifier, a new - * dock area widget is created. + * This function will add the given Dockwidget to the given dock area as a new tab. If no dock + * area widget exists for the given area identifier, a new dock area widget is created. */ DockAreaWidget *addDockWidgetTab(DockWidgetArea area, DockWidget *dockWidget); /** - * This function will add the given Dockwidget to the given DockAreaWidget - * as a new tab. + * This function will add the given Dockwidget to the given DockAreaWidget as a new tab. */ DockAreaWidget *addDockWidgetTabToArea(DockWidget *dockWidget, DockAreaWidget *dockAreaWidget); /** - * Adds the given DockWidget floating and returns the created - * CFloatingDockContainer instance. + * Adds the given DockWidget floating and returns the created FloatingDockContainer instance. */ FloatingDockContainer *addDockWidgetFloating(DockWidget *dockWidget); @@ -343,8 +273,8 @@ public: void removeDockWidget(DockWidget *dockWidget); /** - * This function returns a readable reference to the internal dock - * widgets map so that it is possible to iterate over all dock widgets. + * This function returns a readable reference to the internal dock widgets map so that it is + * possible to iterate over all dock widgets. */ QMap dockWidgetsMap() const; @@ -366,39 +296,34 @@ public: unsigned int zOrderIndex() const override; /** - * Saves the current state of the dockmanger and all its dock widgets - * into the returned QByteArray. - * The XmlMode enables / disables the auto formatting for the XmlStreamWriter. + * Saves the current state of the dockmanger and all its dock widgets into the returned + * QByteArray. The XmlMode enables / disables the auto formatting for the XmlStreamWriter. * If auto formatting is enabled, the output is intended and line wrapped. - * The XmlMode XmlAutoFormattingDisabled is better if you would like to have - * a more compact XML output - i.e. for storage in ini files. + * The XmlMode XmlAutoFormattingDisabled is better if you would like to have a more compact + * XML output - i.e. for storage in ini files. * The version number is stored as part of the data. - * To restore the saved state, pass the return value and version number - * to restoreState(). + * To restore the saved state, pass the return value and version number to restoreState(). * \see restoreState() */ - QByteArray saveState(int version = 0) const; + QByteArray saveState(const QString &displayName, int version = 0) const; /** * Restores the state of this dockmanagers dockwidgets. - * The version number is compared with that stored in state. If they do - * not match, the dockmanager's state is left unchanged, and this function - * returns false; otherwise, the state is restored, and this function - * returns true. + * The version number is compared with that stored in state. If they do not match, the + * dockmanager's state is left unchanged, and this function returns false; otherwise, the state + * is restored, and this function returns true. * \see saveState() */ bool restoreState(const QByteArray &state, int version = 0); /** - * This function returns true between the restoringState() and - * stateRestored() signals. + * This function returns true between the restoringState() and stateRestored() signals. */ bool isRestoringState() const; /** * Request a focus change to the given dock widget. - * This function only has an effect, if the flag CDockManager::FocusStyling - * is enabled + * This function only has an effect, if the flag CDockManager::FocusStyling is enabled. */ void setDockWidgetFocused(DockWidget *dockWidget); @@ -411,125 +336,234 @@ signals: /** * This signal is emitted if workspaces have been removed. */ - void workspacesRemoved(); + void workspaceRemoved(); /** - * This signal is emitted, if the restore function is called, just before - * the dock manager starts restoring the state. - * If this function is called, nothing has changed yet. + * This signal is emitted, if the restore function is called, just before the dock manager + * starts restoring the state. If this function is called, nothing has changed yet. */ void restoringState(); /** * This signal is emitted if the state changed in restoreState. - * The signal is emitted if the restoreState() function is called or - * if the openWorkspace() function is called. + * The signal is emitted if the restoreState() function is called or if the openWorkspace() + * function is called. */ void stateRestored(); /** - * This signal is emitted, if the dock manager starts opening a - * workspace. - * Opening a workspace may take more than a second if there are - * many complex widgets. The application may use this signal - * to show some progress indicator or to change the mouse cursor - * into a busy cursor. + * This signal is emitted, if the dock manager starts opening a workspace. + * Opening a workspace may take more than a second if there are many complex widgets. The + * application may use this signal to show some progress indicator or to change the mouse + * cursor into a busy cursor. */ - void openingWorkspace(const QString &workspaceName); + void openingWorkspace(const QString &fileName); /** - * This signal is emitted if the dock manager finished opening a - * workspace. + * This signal is emitted if the dock manager finished opening a workspace. */ - void workspaceOpened(const QString &workspaceName); + void workspaceOpened(const QString &fileName); /** - * This signal is emitted, if a new floating widget has been created. - * An application can use this signal to e.g. subscribe to events of - * the newly created window. + * This signal is emitted, if a new floating widget has been created. An application can use + * this signal to e.g. subscribe to events of the newly created window. */ - void floatingWidgetCreated(FloatingDockContainer *floatingWidget); + void floatingWidgetCreated(ADS::FloatingDockContainer *floatingWidget); /** - * This signal is emitted, if a new DockArea has been created. - * An application can use this signal to set custom icons or custom - * tooltips for the DockArea buttons. + * This signal is emitted, if a new DockArea has been created. An application can use this + * signal to set custom icons or custom tooltips for the DockArea buttons. */ - void dockAreaCreated(DockAreaWidget *dockArea); + void dockAreaCreated(ADS::DockAreaWidget *dockArea); /** * This signal is emitted just before removal of the given DockWidget. */ - void dockWidgetAboutToBeRemoved(DockWidget *dockWidget); + void dockWidgetAboutToBeRemoved(ADS::DockWidget *dockWidget); /** - * This signal is emitted if a dock widget has been removed with the remove - * removeDockWidget() function. - * If this signal is emitted, the dock widget has been removed from the - * docking system but it is not deleted yet. + * This signal is emitted if a dock widget has been removed with the remove removeDockWidget() + * function. If this signal is emitted, the dock widget has been removed from the docking + * system but it is not deleted yet. */ - void dockWidgetRemoved(DockWidget *dockWidget); + void dockWidgetRemoved(ADS::DockWidget *dockWidget); /** - * This signal is emitted if the focused dock widget changed. - * Both old and now can be nullptr. + * This signal is emitted if the focused dock widget changed. Both old and now can be nullptr. * The focused dock widget is the one that is highlighted in the GUI */ - void focusedDockWidgetChanged(DockWidget *old, DockWidget *now); + void focusedDockWidgetChanged(ADS::DockWidget *old, ADS::DockWidget *now); + +protected: + /** + * Registers the given floating widget in the internal list of floating widgets + */ + void registerFloatingWidget(FloatingDockContainer *floatingWidget); + + /** + * Remove the given floating widget from the list of registered floating widgets + */ + void removeFloatingWidget(FloatingDockContainer *floatingWidget); + + /** + * Registers the given dock container widget + */ + void registerDockContainer(DockContainerWidget *dockContainer); + + /** + * Remove dock container from the internal list of registered dock containers + */ + void removeDockContainer(DockContainerWidget *dockContainer); + + /** + * Overlay for containers + */ + DockOverlay *containerOverlay() const; + + /** + * Overlay for dock areas + */ + DockOverlay *dockAreaOverlay() const; + + /** + * A container needs to call this function if a widget has been dropped into it + */ + void notifyWidgetOrAreaRelocation(QWidget *droppedWidget); + + /** + * This function is called, if a floating widget has been dropped into an new position. When + * this function is called, all dock widgets of the FloatingWidget are already inserted into + * its new position + */ + void notifyFloatingWidgetDrop(FloatingDockContainer *floatingWidget); + + /** + * This function is called, if the given DockWidget has been relocated from the old container + * ContainerOld to the new container DockWidget->dockContainer() + */ + void notifyDockWidgetRelocation(DockWidget *dockWidget, DockContainerWidget *containerOld); + + /** + * This function is called, if the given DockArea has been relocated from the old container + * ContainerOld to the new container DockArea->dockContainer() + */ + void notifyDockAreaRelocation(DockAreaWidget *dockArea, DockContainerWidget *containerOld); + + /** + * Show the floating widgets that has been created floating + */ + void showEvent(QShowEvent *event) override; public: - void showWorkspaceMananger(); + // Workspace state + Workspace *activeWorkspace() const; + QString startupWorkspace() const; + bool autoRestoreWorkspace() const; + const QList &workspaces() const; + + // Workspace convenience functions + int workspaceIndex(const QString &fileName) const; + bool workspaceExists(const QString &fileName) const; + Workspace *workspace(const QString &fileName) const; + Workspace *workspace(int index) const; + QDateTime workspaceDateTime(const QString &fileName) const; + + bool moveWorkspace(int from, int to); + bool moveWorkspaceUp(const QString &fileName); + bool moveWorkspaceDown(const QString &fileName); - // higher level workspace management - QString activeWorkspace() const; - QString lastWorkspace() const; - bool autoRestorLastWorkspace() const; - QStringView workspaceFileExtension() const; - QStringList workspaces(); - QSet workspacePresets() const; - QDateTime workspaceDateTime(const QString &workspace) const; Utils::FilePath workspaceNameToFilePath(const QString &workspaceName) const; - QString fileNameToWorkspaceName(const QString &fileName) const; + QString workspaceNameToFileName(const QString &workspaceName) const; - bool createWorkspace(const QString &workspace); + void uniqueWorkspaceFileName(QString &fileName) const; - bool openWorkspace(const QString &workspace); - bool reloadActiveWorkspace(); + // Workspace management functionality + void showWorkspaceMananger(); - bool confirmWorkspaceDelete(const QStringList &workspaces); - bool deleteWorkspace(const QString &workspace); - void deleteWorkspaces(const QStringList &workspaces); + /** + * \brief Create a workspace. + * + * The display name does not need to be unique, but the file name must be. So this function will + * generate a file name from the display name so it will not collide with an existing workspace. + * + * \param workspace display name of the workspace that will be created + * \return file name of the created workspace or unexpected + */ + Utils::expected_str createWorkspace(const QString &workspace); - bool cloneWorkspace(const QString &original, const QString &clone); - bool renameWorkspace(const QString &original, const QString &newName); + Utils::expected_str openWorkspace(const QString &fileName); + Utils::expected_str reloadActiveWorkspace(); - bool resetWorkspacePreset(const QString &workspace); + /** + * \brief Deletes a workspace from workspace list and the file from disk. + */ + bool deleteWorkspace(const QString &fileName); + void deleteWorkspaces(const QStringList &fileNames); - bool save(); + /** + * \brief Clone a workspace. + * + * \param originalFileName file name of the workspace that will be cloned + * \param cloneName display name of cloned workspace + * \return file name of the cloned workspace or unexpected + */ + Utils::expected_str cloneWorkspace(const QString &originalFileName, + const QString &cloneName); - bool isWorkspacePreset(const QString &workspace) const; + /** + * \brief Rename a workspace. + * + * \param originalFileName file name of the workspace that will be renamed + * \param newName new display name + * \return file name of the renamed workspace or unexpected if rename failed + */ + Utils::expected_str renameWorkspace(const QString &originalFileName, + const QString &newName); + + Utils::expected_str resetWorkspacePreset(const QString &fileName); + + /** + * \brief Save the currently active workspace. + */ + Utils::expected_str save(); void setModeChangeState(bool value); bool isModeChangeState() const; - void importWorkspace(const QString &workspace); - void exportWorkspace(const QString &target, const QString &workspace); + Utils::expected_str importWorkspace(const QString &filePath); + Utils::expected_str exportWorkspace(const QString &targetFilePath, + const QString &sourceFileName); + + // Workspace convenience functions + QStringList loadOrder(const Utils::FilePath &dir) const; + bool writeOrder() const; + QList loadWorkspaces(const Utils::FilePath &dir) const; + + Utils::FilePath presetDirectory() const; + Utils::FilePath userDirectory() const; + + static QByteArray loadFile(const Utils::FilePath &filePath); + static QString readDisplayName(const Utils::FilePath &filePath); + static bool writeDisplayName(const Utils::FilePath &filePath, const QString &displayName); signals: - void aboutToUnloadWorkspace(QString workspaceName); - void aboutToLoadWorkspace(QString workspaceName); - void workspaceLoaded(QString workspaceName); - void workspaceReloaded(QString workspaceName); + void aboutToUnloadWorkspace(QString fileName); + void aboutToLoadWorkspace(QString fileName); + void workspaceLoaded(QString fileName); + void workspaceReloaded(QString fileName); void aboutToSaveWorkspace(); private: - bool write(const QString &workspace, const QByteArray &data, QString *errorString) const; - bool write(const QString &workspace, const QByteArray &data, QWidget *parent) const; + static Utils::expected_str write(const Utils::FilePath &filePath, const QByteArray &data); - QByteArray loadWorkspace(const QString &workspace) const; + Utils::expected_str loadWorkspace(const Workspace &workspace) const; + /** + * \brief Copy all missing workspace presets over to the local workspace folder. + */ void syncWorkspacePresets(); + void prepareWorkspaces(); void saveStartupWorkspace(); }; // class DockManager diff --git a/src/libs/advanceddockingsystem/workspace.cpp b/src/libs/advanceddockingsystem/workspace.cpp new file mode 100644 index 00000000000..d3b0785d90b --- /dev/null +++ b/src/libs/advanceddockingsystem/workspace.cpp @@ -0,0 +1,95 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-2.1-or-later OR GPL-3.0-or-later + +#include "workspace.h" + +#include "dockmanager.h" + +namespace ADS { + +Workspace::Workspace() {} + +Workspace::Workspace(const Utils::FilePath &filePath, bool isPreset) + : m_filePath(filePath) + , m_preset(isPreset) +{ + if (m_filePath.isEmpty()) + return; + + QString name = DockManager::readDisplayName(m_filePath); + + if (name.isEmpty()) { + name = baseName(); + // Remove "-" and "_" remove + name.replace("-", " "); + name.replace("_", " "); + + setName(name); + } else { + m_name = name; + } +} + +void Workspace::setName(const QString &name) +{ + if (DockManager::writeDisplayName(filePath(), name)) + m_name = name; +} + +const QString &Workspace::name() const +{ + return m_name; +} + +const Utils::FilePath &Workspace::filePath() const +{ + return m_filePath; +} + +QString Workspace::fileName() const +{ + return m_filePath.fileName(); +} + +QString Workspace::baseName() const +{ + return m_filePath.baseName(); +} + +QDateTime Workspace::lastModified() const +{ + return m_filePath.lastModified(); +} + +bool Workspace::exists() const +{ + return m_filePath.exists(); +} + +bool Workspace::isValid() const +{ + if (m_filePath.isEmpty()) + return false; + + return exists(); +} + +void Workspace::setPreset(bool value) +{ + m_preset = value; +} + +bool Workspace::isPreset() const +{ + return m_preset; +} + +Workspace::operator QString() const +{ + return QString("Workspace %1 Preset[%2] %3") + .arg(name()) + .arg(isPreset()) + .arg(filePath().toUserOutput()); +} + +} // namespace ADS diff --git a/src/libs/advanceddockingsystem/workspace.h b/src/libs/advanceddockingsystem/workspace.h new file mode 100644 index 00000000000..c23db55d6b6 --- /dev/null +++ b/src/libs/advanceddockingsystem/workspace.h @@ -0,0 +1,55 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-2.1-or-later OR GPL-3.0-or-later + +#pragma once + +#include "ads_globals.h" + +#include + +namespace ADS { + +class ADS_EXPORT Workspace +{ +public: + Workspace(); + Workspace(const Utils::FilePath &filePath, bool isPreset = false); + + void setName(const QString &name); + const QString &name() const; + + const Utils::FilePath &filePath() const; + + QString fileName() const; + QString baseName() const; + QDateTime lastModified() const; + bool exists() const; + + bool isValid() const; + + void setPreset(bool value); + bool isPreset() const; + + friend bool operator==(const Workspace &a, const Workspace &b) + { + return a.fileName() == b.fileName(); + } + + friend bool operator==(const QString &fileName, const Workspace &workspace) + { + return fileName == workspace.fileName(); + } + friend bool operator==(const Workspace &workspace, const QString &fileName) + { + return workspace.fileName() == fileName; + } + + explicit operator QString() const; + +private: + QString m_name; + Utils::FilePath m_filePath; + bool m_preset = false; +}; + +} // namespace ADS diff --git a/src/libs/advanceddockingsystem/workspacedialog.cpp b/src/libs/advanceddockingsystem/workspacedialog.cpp index e4f92d92bae..fb636887dbe 100644 --- a/src/libs/advanceddockingsystem/workspacedialog.cpp +++ b/src/libs/advanceddockingsystem/workspacedialog.cpp @@ -13,103 +13,10 @@ #include #include #include -#include #include -#include -#include namespace ADS { -class WorkspaceValidator : public QValidator -{ -public: - WorkspaceValidator(QObject *parent, const QStringList &workspaces); - void fixup(QString &input) const override; - QValidator::State validate(QString &input, int &pos) const override; - -private: - QStringList m_workspaces; -}; - -WorkspaceValidator::WorkspaceValidator(QObject *parent, const QStringList &workspaces) - : QValidator(parent) - , m_workspaces(workspaces) -{} - -QValidator::State WorkspaceValidator::validate(QString &input, int &pos) const -{ - Q_UNUSED(pos) - - static const QRegularExpression rx("^[a-zA-Z0-9 ()\\-]*$"); - - if (!rx.match(input).hasMatch()) - return QValidator::Invalid; - - if (m_workspaces.contains(input)) - return QValidator::Intermediate; - else - return QValidator::Acceptable; -} - -void WorkspaceValidator::fixup(QString &input) const -{ - int i = 2; - QString copy; - do { - copy = input + QLatin1String(" (") + QString::number(i) + QLatin1Char(')'); - ++i; - } while (m_workspaces.contains(copy)); - input = copy; -} - -WorkspaceNameInputDialog::WorkspaceNameInputDialog(DockManager *manager, QWidget *parent) - : QDialog(parent) - , m_manager(manager) -{ - auto label = new QLabel(Tr::tr("Enter the name of the workspace:"), this); - m_newWorkspaceLineEdit = new QLineEdit(this); - m_newWorkspaceLineEdit->setValidator(new WorkspaceValidator(this, m_manager->workspaces())); - auto buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, - Qt::Horizontal, - this); - m_okButton = buttons->button(QDialogButtonBox::Ok); - m_switchToButton = new QPushButton; - buttons->addButton(m_switchToButton, QDialogButtonBox::AcceptRole); - connect(m_switchToButton, &QPushButton::clicked, this, [this] { m_usedSwitchTo = true; }); - connect(buttons, &QDialogButtonBox::accepted, this, &QDialog::accept); - connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject); - - using namespace Utils::Layouting; - - Column { - label, - m_newWorkspaceLineEdit, - buttons - }.attachTo(this); -} - -void WorkspaceNameInputDialog::setActionText(const QString &actionText, - const QString &openActionText) -{ - m_okButton->setText(actionText); - m_switchToButton->setText(openActionText); -} - -void WorkspaceNameInputDialog::setValue(const QString &value) -{ - m_newWorkspaceLineEdit->setText(value); -} - -QString WorkspaceNameInputDialog::value() const -{ - return m_newWorkspaceLineEdit->text(); -} - -bool WorkspaceNameInputDialog::isSwitchToRequested() const -{ - return m_usedSwitchTo; -} - WorkspaceDialog::WorkspaceDialog(DockManager *manager, QWidget *parent) : QDialog(parent) , m_manager(manager) @@ -122,9 +29,12 @@ WorkspaceDialog::WorkspaceDialog(DockManager *manager, QWidget *parent) , m_btSwitch(new QPushButton(Tr::tr("&Switch To"))) , m_btImport(new QPushButton(Tr::tr("Import"))) , m_btExport(new QPushButton(Tr::tr("Export"))) + , m_btUp(new QPushButton(Tr::tr("Move Up"))) + , m_btDown(new QPushButton(Tr::tr("Move Down"))) , m_autoLoadCheckBox(new QCheckBox(Tr::tr("Restore last workspace on startup"))) { setWindowTitle(Tr::tr("Workspace Manager")); + resize(550, 380); m_workspaceView->setActivationMode(Utils::DoubleClickActivation); @@ -139,34 +49,27 @@ WorkspaceDialog::WorkspaceDialog(DockManager *manager, QWidget *parent) using namespace Utils::Layouting; - Column { - Row { - Column { - m_workspaceView, - m_autoLoadCheckBox - }, - Column { - m_btCreateNew, - m_btRename, - m_btClone, - m_btDelete, - m_btReset, - m_btSwitch, - st, - m_btImport, - m_btExport - } - }, - hr, - Row { - whatsAWorkspaceLabel, - buttonBox - } - }.attachTo(this); + Column{Row{Column{m_workspaceView, m_autoLoadCheckBox}, + Column{m_btCreateNew, + m_btRename, + m_btClone, + m_btDelete, + m_btReset, + m_btSwitch, + st, + m_btUp, + m_btDown, + st, + m_btImport, + m_btExport}}, + hr, + Row{whatsAWorkspaceLabel, buttonBox}} + .attachTo(this); - - connect(m_btCreateNew, &QAbstractButton::clicked, - m_workspaceView, &WorkspaceView::createNewWorkspace); + connect(m_btCreateNew, + &QAbstractButton::clicked, + m_workspaceView, + &WorkspaceView::createNewWorkspace); connect(m_btClone, &QAbstractButton::clicked, m_workspaceView, &WorkspaceView::cloneCurrentWorkspace); connect(m_btDelete, &QAbstractButton::clicked, @@ -183,10 +86,15 @@ WorkspaceDialog::WorkspaceDialog(DockManager *manager, QWidget *parent) this, &WorkspaceDialog::updateActions); connect(m_btImport, &QAbstractButton::clicked, m_workspaceView, &WorkspaceView::importWorkspace); - connect(m_btExport, &QAbstractButton::clicked, - m_workspaceView, &WorkspaceView::exportCurrentWorkspace); + connect(m_btExport, + &QAbstractButton::clicked, + m_workspaceView, + &WorkspaceView::exportCurrentWorkspace); - updateActions(m_workspaceView->selectedWorkspaces()); + connect(m_btUp, &QAbstractButton::clicked, m_workspaceView, &WorkspaceView::moveWorkspaceUp); + connect(m_btDown, &QAbstractButton::clicked, m_workspaceView, &WorkspaceView::moveWorkspaceDown); + + updateActions(m_workspaceView->selectedWorkspaces()); } void WorkspaceDialog::setAutoLoadWorkspace(bool check) @@ -204,29 +112,50 @@ DockManager *WorkspaceDialog::dockManager() const return m_manager; } -void WorkspaceDialog::updateActions(const QStringList &workspaces) +void WorkspaceDialog::updateActions(const QStringList &fileNames) { - if (workspaces.isEmpty()) { + if (fileNames.isEmpty()) { m_btDelete->setEnabled(false); m_btRename->setEnabled(false); m_btClone->setEnabled(false); m_btReset->setEnabled(false); m_btSwitch->setEnabled(false); + m_btUp->setEnabled(false); + m_btDown->setEnabled(false); m_btExport->setEnabled(false); return; } - const bool presetIsSelected = Utils::anyOf(workspaces, [this](const QString &workspace) { - return m_manager->isWorkspacePreset(workspace); + const bool presetIsSelected = Utils::anyOf(fileNames, [this](const QString &fileName) { + Workspace *workspace = m_manager->workspace(fileName); + if (!workspace) + return false; + + return workspace->isPreset(); }); - const bool activeIsSelected = Utils::anyOf(workspaces, [this](const QString &workspace) { - return workspace == m_manager->activeWorkspace(); + const bool activeIsSelected = Utils::anyOf(fileNames, [this](const QString &fileName) { + Workspace *workspace = m_manager->workspace(fileName); + if (!workspace) + return false; + + return *workspace == *m_manager->activeWorkspace(); }); + const bool canMoveUp = Utils::anyOf(fileNames, [this](const QString &fileName) { + return m_manager->workspaceIndex(fileName) > 0; + }); + const int count = m_manager->workspaces().count(); + const bool canMoveDown = Utils::anyOf(fileNames, [this, &count](const QString &fileName) { + const int i = m_manager->workspaceIndex(fileName); + return i < (count - 1); + }); + m_btDelete->setEnabled(!activeIsSelected && !presetIsSelected); - m_btRename->setEnabled(workspaces.size() == 1 && !presetIsSelected); - m_btClone->setEnabled(workspaces.size() == 1); + m_btRename->setEnabled(fileNames.size() == 1 && !presetIsSelected); + m_btClone->setEnabled(fileNames.size() == 1); m_btReset->setEnabled(presetIsSelected); - m_btSwitch->setEnabled(workspaces.size() == 1); - m_btExport->setEnabled(workspaces.size() == 1); + m_btSwitch->setEnabled(fileNames.size() == 1 && !activeIsSelected); + m_btUp->setEnabled(fileNames.size() == 1 && canMoveUp); + m_btDown->setEnabled(fileNames.size() == 1 && canMoveDown); + m_btExport->setEnabled(fileNames.size() == 1); } } // namespace ADS diff --git a/src/libs/advanceddockingsystem/workspacedialog.h b/src/libs/advanceddockingsystem/workspacedialog.h index 10ad1af624c..7918c8e8250 100644 --- a/src/libs/advanceddockingsystem/workspacedialog.h +++ b/src/libs/advanceddockingsystem/workspacedialog.h @@ -1,4 +1,4 @@ -// Copyright (C) 2020 The Qt Company Ltd. +// Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-2.1-or-later OR GPL-3.0-or-later #pragma once @@ -7,7 +7,6 @@ QT_BEGIN_NAMESPACE class QCheckBox; -class QLineEdit; class QPushButton; QT_END_NAMESPACE @@ -29,7 +28,7 @@ public: DockManager *dockManager() const; private: - void updateActions(const QStringList &workspaces); + void updateActions(const QStringList &fileNames); DockManager *m_manager = nullptr; @@ -42,28 +41,9 @@ private: QPushButton *m_btSwitch = nullptr; QPushButton *m_btImport = nullptr; QPushButton *m_btExport = nullptr; + QPushButton *m_btUp = nullptr; + QPushButton *m_btDown = nullptr; QCheckBox *m_autoLoadCheckBox = nullptr; }; -class WorkspaceNameInputDialog : public QDialog -{ - Q_OBJECT - -public: - explicit WorkspaceNameInputDialog(DockManager *manager, QWidget *parent); - - void setActionText(const QString &actionText, const QString &openActionText); - void setValue(const QString &value); - QString value() const; - bool isSwitchToRequested() const; - -private: - QLineEdit *m_newWorkspaceLineEdit = nullptr; - QPushButton *m_switchToButton = nullptr; - QPushButton *m_okButton = nullptr; - bool m_usedSwitchTo = false; - - DockManager *m_manager; -}; - } // namespace ADS diff --git a/src/libs/advanceddockingsystem/workspaceinputdialog.cpp b/src/libs/advanceddockingsystem/workspaceinputdialog.cpp new file mode 100644 index 00000000000..56e75433231 --- /dev/null +++ b/src/libs/advanceddockingsystem/workspaceinputdialog.cpp @@ -0,0 +1,103 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-2.1-or-later OR GPL-3.0-or-later + +#include "workspaceinputdialog.h" + +#include "advanceddockingsystemtr.h" +#include "dockmanager.h" + +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace ADS { + +class WorkspaceValidator : public QValidator +{ +public: + WorkspaceValidator(QObject *parent = nullptr); + QValidator::State validate(QString &input, int &pos) const override; +}; + +WorkspaceValidator::WorkspaceValidator(QObject *parent) + : QValidator(parent) +{} + +QValidator::State WorkspaceValidator::validate(QString &input, int &pos) const +{ + Q_UNUSED(pos) + + static const QRegularExpression rx("^[a-zA-Z0-9 ()]*$"); + + if (!rx.match(input).hasMatch()) + return QValidator::Invalid; + + if (input.isEmpty()) + return QValidator::Intermediate; + + return QValidator::Acceptable; +} + +WorkspaceNameInputDialog::WorkspaceNameInputDialog(DockManager *manager, QWidget *parent) + : QDialog(parent) + , m_manager(manager) +{ + auto label = new QLabel(Tr::tr("Enter the name of the workspace:"), this); + m_newWorkspaceLineEdit = new QLineEdit(this); + m_newWorkspaceLineEdit->setValidator(new WorkspaceValidator(this)); + auto buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, + Qt::Horizontal, + this); + m_okButton = buttons->button(QDialogButtonBox::Ok); + m_switchToButton = new QPushButton; + buttons->addButton(m_switchToButton, QDialogButtonBox::AcceptRole); + connect(m_switchToButton, &QPushButton::clicked, this, [this] { m_usedSwitchTo = true; }); + connect(buttons, &QDialogButtonBox::accepted, this, &QDialog::accept); + connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject); + + m_okButton->setEnabled(false); + m_switchToButton->setEnabled(false); + + connect(m_newWorkspaceLineEdit, &QLineEdit::textChanged, this, [this](const QString &) { + m_okButton->setEnabled(m_newWorkspaceLineEdit->hasAcceptableInput()); + m_switchToButton->setEnabled(m_newWorkspaceLineEdit->hasAcceptableInput()); + }); + + using namespace Utils::Layouting; + + Column { + label, + m_newWorkspaceLineEdit, + buttons + }.attachTo(this); +} + +void WorkspaceNameInputDialog::setActionText(const QString &actionText, + const QString &openActionText) +{ + m_okButton->setText(actionText); + m_switchToButton->setText(openActionText); +} + +void WorkspaceNameInputDialog::setValue(const QString &value) +{ + m_newWorkspaceLineEdit->setText(value); +} + +QString WorkspaceNameInputDialog::value() const +{ + return m_newWorkspaceLineEdit->text(); +} + +bool WorkspaceNameInputDialog::isSwitchToRequested() const +{ + return m_usedSwitchTo; +} + +} // namespace ADS diff --git a/src/libs/advanceddockingsystem/workspaceinputdialog.h b/src/libs/advanceddockingsystem/workspaceinputdialog.h new file mode 100644 index 00000000000..0568e466b4a --- /dev/null +++ b/src/libs/advanceddockingsystem/workspaceinputdialog.h @@ -0,0 +1,38 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-2.1-or-later OR GPL-3.0-or-later + +#pragma once + +#include + +QT_BEGIN_NAMESPACE +class QLineEdit; +class QPushButton; +QT_END_NAMESPACE + +namespace ADS { + +class DockManager; + +class WorkspaceNameInputDialog : public QDialog +{ + Q_OBJECT + +public: + explicit WorkspaceNameInputDialog(DockManager *manager, QWidget *parent); + + void setActionText(const QString &actionText, const QString &openActionText); + void setValue(const QString &value); + QString value() const; + bool isSwitchToRequested() const; + +private: + QLineEdit *m_newWorkspaceLineEdit = nullptr; + QPushButton *m_switchToButton = nullptr; + QPushButton *m_okButton = nullptr; + bool m_usedSwitchTo = false; + + DockManager *m_manager; +}; + +} // namespace ADS diff --git a/src/libs/advanceddockingsystem/workspacemodel.cpp b/src/libs/advanceddockingsystem/workspacemodel.cpp index 2f73e747607..99d3bd1473e 100644 --- a/src/libs/advanceddockingsystem/workspacemodel.cpp +++ b/src/libs/advanceddockingsystem/workspacemodel.cpp @@ -18,21 +18,21 @@ namespace ADS { WorkspaceModel::WorkspaceModel(DockManager *manager, QObject *parent) : QAbstractTableModel(parent) , m_manager(manager) - , m_currentSortColumn(0) { - m_sortedWorkspaces = m_manager->workspaces(); - sort(m_currentSortColumn, m_currentSortOrder); connect(m_manager, &DockManager::workspaceLoaded, this, &WorkspaceModel::resetWorkspaces); } -int WorkspaceModel::indexOfWorkspace(const QString &workspace) +int WorkspaceModel::indexOfWorkspace(const QString &fileName) { - return m_sortedWorkspaces.indexOf(workspace); + return m_manager->workspaceIndex(fileName); } QString WorkspaceModel::workspaceAt(int row) const { - return m_sortedWorkspaces.value(row, QString()); + if (row >= rowCount() || row < 0) + return {}; + + return m_manager->workspaces()[row].fileName(); } QVariant WorkspaceModel::headerData(int section, Qt::Orientation orientation, int role) const @@ -46,6 +46,9 @@ QVariant WorkspaceModel::headerData(int section, Qt::Orientation orientation, in result = Tr::tr("Workspace"); break; case 1: + result = Tr::tr("File Name"); + break; + case 2: result = Tr::tr("Last Modified"); break; } // switch (section) @@ -59,7 +62,6 @@ int WorkspaceModel::columnCount(const QModelIndex &) const { static int sectionCount = 0; if (sectionCount == 0) { - // headers sections defining possible columns while (!headerData(sectionCount, Qt::Horizontal, Qt::DisplayRole).isNull()) sectionCount++; } @@ -69,183 +71,79 @@ int WorkspaceModel::columnCount(const QModelIndex &) const int WorkspaceModel::rowCount(const QModelIndex &) const { - return m_sortedWorkspaces.count(); -} - -QStringList pathsToBaseNames(const QStringList &paths) -{ - return Utils::transform(paths, - [](const QString &path) { return QFileInfo(path).completeBaseName(); }); + return m_manager->workspaces().count(); } QVariant WorkspaceModel::data(const QModelIndex &index, int role) const { - QVariant result; - if (index.isValid()) { - QString workspaceName = m_sortedWorkspaces.at(index.row()); + if (!index.isValid() || index.row() >= rowCount()) + return QVariant(); - switch (role) { - case Qt::DisplayRole: - switch (index.column()) { - case 0: - result = workspaceName; - break; - case 1: - result = m_manager->workspaceDateTime(workspaceName); - break; - } // switch (section) + Workspace *workspace = m_manager->workspace(index.row()); + if (!workspace) + return QVariant(); + + switch (role) { + case Qt::DisplayRole: + switch (index.column()) { + case 0: + return workspace->name(); + case 1: + return workspace->fileName(); + case 2: + return workspace->lastModified(); + default: + qWarning("data: invalid display value column %d", index.column()); break; - case Qt::FontRole: { - QFont font; - if (m_manager->isWorkspacePreset(workspaceName)) - font.setItalic(true); - else - font.setItalic(false); - if (m_manager->activeWorkspace() == workspaceName) - font.setBold(true); - else - font.setBold(false); - result = font; - } break; - case PresetWorkspaceRole: - result = m_manager->isWorkspacePreset(workspaceName); - break; - case LastWorkspaceRole: - result = m_manager->lastWorkspace() == workspaceName; - break; - case ActiveWorkspaceRole: - result = m_manager->activeWorkspace() == workspaceName; - break; - } // switch (role) + } + break; + case Qt::FontRole: { + QFont font; + font.setItalic(workspace->isPreset()); + font.setBold(*m_manager->activeWorkspace() == *workspace); + return font; } - - return result; + case WorkspacePreset: + return workspace->isPreset(); + case WorkspaceStartup: + return m_manager->startupWorkspace() == workspace->fileName(); + case WorkspaceActive: + return *m_manager->activeWorkspace() == *workspace; + } + return QVariant(); } QHash WorkspaceModel::roleNames() const { static QHash extraRoles{{Qt::DisplayRole, "workspaceName"}, - {PresetWorkspaceRole, "presetWorkspace"}, - {LastWorkspaceRole, "activeWorkspace"}, - {ActiveWorkspaceRole, "lastWorkspace"}}; + {WorkspacePreset, "presetWorkspace"}, + {WorkspaceStartup, "lastWorkspace"}, + {WorkspaceActive, "activeWorkspace"}}; auto defaultRoles = QAbstractTableModel::roleNames(); defaultRoles.insert(extraRoles); return defaultRoles; } -void WorkspaceModel::sort(int column, Qt::SortOrder order) +Qt::DropActions WorkspaceModel::supportedDropActions() const { - m_currentSortColumn = column; - m_currentSortOrder = order; + return Qt::MoveAction; +} - beginResetModel(); - const auto cmp = [this, column, order](const QString &s1, const QString &s2) { - bool isLess; - if (column == 0) - isLess = s1 < s2; - else - isLess = m_manager->workspaceDateTime(s1) < m_manager->workspaceDateTime(s2); - if (order == Qt::DescendingOrder) - isLess = !isLess; - return isLess; - }; - Utils::sort(m_sortedWorkspaces, cmp); - endResetModel(); +Qt::ItemFlags WorkspaceModel::flags(const QModelIndex &index) const +{ + Qt::ItemFlags defaultFlags = QAbstractTableModel::flags(index); + + if (index.isValid()) + return Qt::ItemIsDragEnabled | defaultFlags; + + return Qt::ItemIsDropEnabled | defaultFlags; } void WorkspaceModel::resetWorkspaces() { - m_sortedWorkspaces = m_manager->workspaces(); - sort(m_currentSortColumn, m_currentSortOrder); -} - -void WorkspaceModel::newWorkspace(QWidget *parent) -{ - WorkspaceNameInputDialog workspaceInputDialog(m_manager, parent); - workspaceInputDialog.setWindowTitle(Tr::tr("New Workspace Name")); - workspaceInputDialog.setActionText(Tr::tr("&Create"), Tr::tr("Create and &Open")); - - runWorkspaceNameInputDialog(&workspaceInputDialog, [this](const QString &newName) { - m_manager->createWorkspace(newName); - }); -} - -void WorkspaceModel::cloneWorkspace(QWidget *parent, const QString &workspace) -{ - WorkspaceNameInputDialog workspaceInputDialog(m_manager, parent); - workspaceInputDialog.setWindowTitle(Tr::tr("New Workspace Name")); - workspaceInputDialog.setActionText(Tr::tr("&Clone"), Tr::tr("Clone and &Open")); - workspaceInputDialog.setValue(workspace + " (2)"); - - runWorkspaceNameInputDialog(&workspaceInputDialog, [this, workspace](const QString &newName) { - m_manager->cloneWorkspace(workspace, newName); - }); -} - -void WorkspaceModel::deleteWorkspaces(const QStringList &workspaces) -{ - if (!m_manager->confirmWorkspaceDelete(workspaces)) - return; - - m_manager->deleteWorkspaces(workspaces); - m_sortedWorkspaces = m_manager->workspaces(); - sort(m_currentSortColumn, m_currentSortOrder); -} - -void WorkspaceModel::renameWorkspace(QWidget *parent, const QString &workspace) -{ - WorkspaceNameInputDialog workspaceInputDialog(m_manager, parent); - workspaceInputDialog.setWindowTitle(Tr::tr("Rename Workspace")); - workspaceInputDialog.setActionText(Tr::tr("&Rename"), Tr::tr("Rename and &Open")); - workspaceInputDialog.setValue(workspace); - - runWorkspaceNameInputDialog(&workspaceInputDialog, [this, workspace](const QString &newName) { - m_manager->renameWorkspace(workspace, newName); - }); -} - -void WorkspaceModel::resetWorkspace(const QString &workspace) -{ - if (m_manager->resetWorkspacePreset(workspace) && workspace == m_manager->activeWorkspace()) - m_manager->reloadActiveWorkspace(); -} - -void WorkspaceModel::switchToWorkspace(const QString &workspace) -{ - m_manager->openWorkspace(workspace); - emit workspaceSwitched(); -} - -void WorkspaceModel::importWorkspace(const QString &workspace) -{ - m_manager->importWorkspace(workspace); - m_sortedWorkspaces = m_manager->workspaces(); - sort(m_currentSortColumn, m_currentSortOrder); -} - -void WorkspaceModel::exportWorkspace(const QString &target, const QString &workspace) -{ - m_manager->exportWorkspace(target, workspace); -} - -void WorkspaceModel::runWorkspaceNameInputDialog(WorkspaceNameInputDialog *workspaceInputDialog, - std::function createWorkspace) -{ - if (workspaceInputDialog->exec() == QDialog::Accepted) { - QString newWorkspace = workspaceInputDialog->value(); - if (newWorkspace.isEmpty() || m_manager->workspaces().contains(newWorkspace)) - return; - - createWorkspace(newWorkspace); - m_sortedWorkspaces = m_manager->workspaces(); - sort(m_currentSortColumn, m_currentSortOrder); - - if (workspaceInputDialog->isSwitchToRequested()) - switchToWorkspace(newWorkspace); - - emit workspaceCreated(newWorkspace); - } + beginResetModel(); + endResetModel(); } } // namespace ADS diff --git a/src/libs/advanceddockingsystem/workspacemodel.h b/src/libs/advanceddockingsystem/workspacemodel.h index 38386ad348b..fe868f71f5b 100644 --- a/src/libs/advanceddockingsystem/workspacemodel.h +++ b/src/libs/advanceddockingsystem/workspacemodel.h @@ -17,45 +17,27 @@ class WorkspaceModel final : public QAbstractTableModel Q_OBJECT public: - enum { PresetWorkspaceRole = Qt::UserRole + 1, LastWorkspaceRole, ActiveWorkspaceRole }; + enum { WorkspaceFileName = Qt::UserRole, WorkspacePreset, WorkspaceStartup, WorkspaceActive }; explicit WorkspaceModel(DockManager *manager, QObject *parent = nullptr); - int indexOfWorkspace(const QString &workspace); + int indexOfWorkspace(const QString &fileName); QString workspaceAt(int row) const; int rowCount(const QModelIndex &parent = QModelIndex()) const override; int columnCount(const QModelIndex &parent = QModelIndex()) const override; QVariant headerData(int section, Qt::Orientation orientation, int role) const override; - QVariant data(const QModelIndex &index, int role) const override; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; QHash roleNames() const override; - void sort(int column, Qt::SortOrder order = Qt::AscendingOrder) override; -signals: - void workspaceSwitched(); - void workspaceCreated(const QString &workspaceName); + Qt::DropActions supportedDropActions() const override; + Qt::ItemFlags flags(const QModelIndex &index) const override; -public: void resetWorkspaces(); - void newWorkspace(QWidget *parent); - void cloneWorkspace(QWidget *parent, const QString &workspace); - void deleteWorkspaces(const QStringList &workspaces); - void renameWorkspace(QWidget *parent, const QString &workspace); - void resetWorkspace(const QString &workspace); - void switchToWorkspace(const QString &workspace); - - void importWorkspace(const QString &workspace); - void exportWorkspace(const QString &target, const QString &workspace); private: - void runWorkspaceNameInputDialog(WorkspaceNameInputDialog *workspaceInputDialog, - std::function createWorkspace); - - QStringList m_sortedWorkspaces; DockManager *m_manager; - int m_currentSortColumn; - Qt::SortOrder m_currentSortOrder = Qt::AscendingOrder; }; } // namespace ADS diff --git a/src/libs/advanceddockingsystem/workspaceview.cpp b/src/libs/advanceddockingsystem/workspaceview.cpp index 430bb097efc..bab54e5e927 100644 --- a/src/libs/advanceddockingsystem/workspaceview.cpp +++ b/src/libs/advanceddockingsystem/workspaceview.cpp @@ -3,14 +3,16 @@ #include "workspaceview.h" -#include "dockmanager.h" #include "advanceddockingsystemtr.h" +#include "dockmanager.h" #include #include #include #include +#include +#include #include #include @@ -49,13 +51,15 @@ WorkspaceView::WorkspaceView(DockManager *manager, QWidget *parent) setSelectionMode(QAbstractItemView::SingleSelection); setWordWrap(false); setRootIsDecorated(false); - setSortingEnabled(true); + setSortingEnabled(false); + setDragEnabled(true); + setAcceptDrops(true); + setDropIndicatorShown(true); + setDragDropMode(QAbstractItemView::InternalMove); setModel(&m_workspaceModel); - sortByColumn(0, Qt::AscendingOrder); - // Ensure that the full workspace name is visible. - header()->setSectionResizeMode(0, QHeaderView::ResizeToContents); + header()->setDefaultSectionSize(150); QItemSelection firstRow(m_workspaceModel.index(0, 0), m_workspaceModel.index(0, m_workspaceModel.columnCount() - 1)); @@ -68,17 +72,94 @@ WorkspaceView::WorkspaceView(DockManager *manager, QWidget *parent) emit workspacesSelected(selectedWorkspaces()); }); - connect(&m_workspaceModel, &WorkspaceModel::workspaceSwitched, - this, &WorkspaceView::workspaceSwitched); - connect(&m_workspaceModel, &WorkspaceModel::modelReset, - this, &WorkspaceView::selectActiveWorkspace); - connect(&m_workspaceModel, &WorkspaceModel::workspaceCreated, - this, &WorkspaceView::selectWorkspace); + connect(&m_workspaceModel, + &WorkspaceModel::modelReset, + this, + &WorkspaceView::selectActiveWorkspace); } void WorkspaceView::createNewWorkspace() { - m_workspaceModel.newWorkspace(this); + WorkspaceNameInputDialog workspaceInputDialog(m_manager, this); + workspaceInputDialog.setWindowTitle(Tr::tr("New Workspace Name")); + workspaceInputDialog.setActionText(Tr::tr("&Create"), Tr::tr("Create and &Open")); + + runWorkspaceNameInputDialog(&workspaceInputDialog, [this](const QString &newName) { + Utils::expected_str result = m_manager->createWorkspace(newName); + + if (!result) + QMessageBox::warning(this, Tr::tr("Cannot Create Workspace"), result.error()); + + return result; + }); +} + +void WorkspaceView::cloneCurrentWorkspace() +{ + const QString fileName = currentWorkspace(); + + QString displayName = "Unknown"; + Workspace *workspace = m_manager->workspace(fileName); + if (workspace) + displayName = workspace->name(); + + WorkspaceNameInputDialog workspaceInputDialog(m_manager, this); + workspaceInputDialog.setWindowTitle(Tr::tr("New Workspace Name")); + workspaceInputDialog.setActionText(Tr::tr("&Clone"), Tr::tr("Clone and &Open")); + workspaceInputDialog.setValue(Tr::tr("%1 Copy").arg(displayName)); + + runWorkspaceNameInputDialog(&workspaceInputDialog, [this, fileName](const QString &newName) { + Utils::expected_str result = m_manager->cloneWorkspace(fileName, newName); + + if (!result) + QMessageBox::warning(this, Tr::tr("Cannot Clone Workspace"), result.error()); + + return result; + }); +} + +void WorkspaceView::renameCurrentWorkspace() +{ + const QString fileName = currentWorkspace(); + + QString displayName = "Unknown"; + Workspace *workspace = m_manager->workspace(fileName); + if (workspace) + displayName = workspace->name(); + + WorkspaceNameInputDialog workspaceInputDialog(m_manager, this); + workspaceInputDialog.setWindowTitle(Tr::tr("Rename Workspace")); + workspaceInputDialog.setActionText(Tr::tr("&Rename"), Tr::tr("Rename and &Open")); + workspaceInputDialog.setValue(displayName); + + runWorkspaceNameInputDialog(&workspaceInputDialog, [this, fileName](const QString &newName) { + Utils::expected_str result = m_manager->renameWorkspace(fileName, newName); + + if (!result) + QMessageBox::warning(this, Tr::tr("Cannot Rename Workspace"), result.error()); + + return result; + }); +} + +void WorkspaceView::resetCurrentWorkspace() +{ + const QString fileName = currentWorkspace(); + + if (m_manager->resetWorkspacePreset(fileName) && fileName == *m_manager->activeWorkspace()) { + if (m_manager->reloadActiveWorkspace()) + m_workspaceModel.resetWorkspaces(); + } +} + +void WorkspaceView::switchToCurrentWorkspace() +{ + Utils::expected_str result = m_manager->openWorkspace(currentWorkspace()); + + if (!result) + QMessageBox::warning(this, Tr::tr("Cannot Switch Workspace"), result.error()); + + emit workspaceSwitched(); } void WorkspaceView::deleteSelectedWorkspaces() @@ -86,61 +167,72 @@ void WorkspaceView::deleteSelectedWorkspaces() deleteWorkspaces(selectedWorkspaces()); } -void WorkspaceView::deleteWorkspaces(const QStringList &workspaces) -{ - m_workspaceModel.deleteWorkspaces(workspaces); -} - void WorkspaceView::importWorkspace() { - static QString lastDir; - const QString currentDir = lastDir.isEmpty() ? "" : lastDir; - const auto fileName = QFileDialog::getOpenFileName(this, - Tr::tr("Import Workspace"), - currentDir, - "Workspaces (*" + m_manager->workspaceFileExtension() + ")"); + static QString previousDirectory; + const QString currentDirectory = previousDirectory.isEmpty() ? "" : previousDirectory; + const auto filePath + = QFileDialog::getOpenFileName(this, + Tr::tr("Import Workspace"), + currentDirectory, + QString("Workspaces (*.%1)").arg(workspaceFileExtension)); - if (!fileName.isEmpty()) - lastDir = QFileInfo(fileName).absolutePath(); + // If the user presses Cancel, it returns a null string + if (filePath.isEmpty()) + return; - m_workspaceModel.importWorkspace(fileName); + previousDirectory = QFileInfo(filePath).absolutePath(); + + const Utils::expected_str newFileName = m_manager->importWorkspace(filePath); + if (newFileName) + m_workspaceModel.resetWorkspaces(); + else + QMessageBox::warning(this, Tr::tr("Cannot Import Workspace"), newFileName.error()); } void WorkspaceView::exportCurrentWorkspace() { - static QString lastDir; - const QString currentDir = lastDir.isEmpty() ? "" : lastDir; - QFileInfo fileInfo(currentDir, m_manager->workspaceNameToFileName(currentWorkspace())); + static QString previousDirectory; + const QString currentDirectory = previousDirectory.isEmpty() ? "" : previousDirectory; + QFileInfo fileInfo(currentDirectory, currentWorkspace()); - const auto fileName = QFileDialog::getSaveFileName(this, - Tr::tr("Export Workspace"), - fileInfo.absoluteFilePath(), - "Workspaces (*" + m_manager->workspaceFileExtension() + ")"); + const auto filePath + = QFileDialog::getSaveFileName(this, + Tr::tr("Export Workspace"), + fileInfo.absoluteFilePath(), + QString("Workspaces (*.%1)").arg(workspaceFileExtension)); - if (!fileName.isEmpty()) - lastDir = QFileInfo(fileName).absolutePath(); + // If the user presses Cancel, it returns a null string + if (filePath.isEmpty()) + return; - m_workspaceModel.exportWorkspace(fileName, currentWorkspace()); + previousDirectory = QFileInfo(filePath).absolutePath(); + + const Utils::expected_str result = m_manager->exportWorkspace(filePath, + currentWorkspace()); + + if (!result) + QMessageBox::warning(this, Tr::tr("Cannot Export Workspace"), result.error()); } -void WorkspaceView::cloneCurrentWorkspace() +void WorkspaceView::moveWorkspaceUp() { - m_workspaceModel.cloneWorkspace(this, currentWorkspace()); + const QString w = currentWorkspace(); + bool hasMoved = m_manager->moveWorkspaceUp(w); + if (hasMoved) { + m_workspaceModel.resetWorkspaces(); + selectWorkspace(w); + } } -void WorkspaceView::renameCurrentWorkspace() +void WorkspaceView::moveWorkspaceDown() { - m_workspaceModel.renameWorkspace(this, currentWorkspace()); -} - -void WorkspaceView::resetCurrentWorkspace() -{ - m_workspaceModel.resetWorkspace(currentWorkspace()); -} - -void WorkspaceView::switchToCurrentWorkspace() -{ - m_workspaceModel.switchToWorkspace(currentWorkspace()); + const QString w = currentWorkspace(); + bool hasMoved = m_manager->moveWorkspaceDown(w); + if (hasMoved) { + m_workspaceModel.resetWorkspaces(); + selectWorkspace(w); + } } QString WorkspaceView::currentWorkspace() @@ -155,17 +247,24 @@ WorkspaceModel *WorkspaceView::workspaceModel() void WorkspaceView::selectActiveWorkspace() { - selectWorkspace(m_manager->activeWorkspace()); + selectWorkspace(m_manager->activeWorkspace()->fileName()); } -void WorkspaceView::selectWorkspace(const QString &workspaceName) +void WorkspaceView::selectWorkspace(const QString &fileName) { - int row = m_workspaceModel.indexOfWorkspace(workspaceName); + int row = m_workspaceModel.indexOfWorkspace(fileName); selectionModel()->setCurrentIndex(model()->index(row, 0), QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows); } +QStringList WorkspaceView::selectedWorkspaces() const +{ + return Utils::transform(selectionModel()->selectedRows(), [this](const QModelIndex &index) { + return m_workspaceModel.workspaceAt(index.row()); + }); +} + void WorkspaceView::showEvent(QShowEvent *event) { Utils::TreeView::showEvent(event); @@ -179,19 +278,83 @@ void WorkspaceView::keyPressEvent(QKeyEvent *event) TreeView::keyPressEvent(event); return; } - const QStringList workspaces = selectedWorkspaces(); - if (!Utils::anyOf(workspaces, [this](const QString &workspace) { - return workspace == m_manager->activeWorkspace(); + const QStringList fileNames = selectedWorkspaces(); + if (!Utils::anyOf(fileNames, [this](const QString &fileName) { + return fileName == *m_manager->activeWorkspace(); })) { - deleteWorkspaces(workspaces); + deleteWorkspaces(fileNames); } } -QStringList WorkspaceView::selectedWorkspaces() const +void WorkspaceView::dropEvent(QDropEvent *event) { - return Utils::transform(selectionModel()->selectedRows(), [this](const QModelIndex &index) { - return m_workspaceModel.workspaceAt(index.row()); - }); + const QModelIndex dropIndex = indexAt(event->pos()); + const DropIndicatorPosition dropIndicator = dropIndicatorPosition(); + + const auto droppedWorkspaces = selectedWorkspaces(); + int from = m_manager->workspaceIndex(droppedWorkspaces.first()); + int to = dropIndex.row(); + + if (dropIndicator == QAbstractItemView::AboveItem && from < to) + --to; + if (dropIndicator == QAbstractItemView::BelowItem && from > to) + ++to; + + bool hasMoved = m_manager->moveWorkspace(from, to); + + if (hasMoved) { + m_workspaceModel.resetWorkspaces(); + selectionModel()->setCurrentIndex(model()->index(to, 0), + QItemSelectionModel::ClearAndSelect + | QItemSelectionModel::Rows); + } + + event->acceptProposedAction(); +} + +void WorkspaceView::deleteWorkspaces(const QStringList &fileNames) +{ + if (!confirmWorkspaceDelete(fileNames)) + return; + + m_manager->deleteWorkspaces(fileNames); + m_workspaceModel.resetWorkspaces(); +} + +bool WorkspaceView::confirmWorkspaceDelete(const QStringList &fileNames) +{ + const QString title = fileNames.size() == 1 ? Tr::tr("Delete Workspace") + : Tr::tr("Delete Workspaces"); + const QString question + = fileNames.size() == 1 + ? Tr::tr("Delete workspace %1?").arg(fileNames.first()) + : Tr::tr("Delete these workspaces?\n %1").arg(fileNames.join("\n ")); + return QMessageBox::question(parentWidget(), title, question, QMessageBox::Yes | QMessageBox::No) + == QMessageBox::Yes; +} + +void WorkspaceView::runWorkspaceNameInputDialog( + WorkspaceNameInputDialog *workspaceInputDialog, + std::function(const QString &)> callback) +{ + if (workspaceInputDialog->exec() == QDialog::Accepted) { + const QString newWorkspace = workspaceInputDialog->value(); + if (newWorkspace.isEmpty() || m_manager->workspaces().contains(newWorkspace)) + return; + + const Utils::expected_str fileName = callback(newWorkspace); + if (!fileName) + return; + + m_workspaceModel.resetWorkspaces(); + + if (workspaceInputDialog->isSwitchToRequested()) { + m_manager->openWorkspace(*fileName); + emit workspaceSwitched(); + } + + selectWorkspace(*fileName); + } } } // namespace ADS diff --git a/src/libs/advanceddockingsystem/workspaceview.h b/src/libs/advanceddockingsystem/workspaceview.h index cb507794dbb..79ecd364bd1 100644 --- a/src/libs/advanceddockingsystem/workspaceview.h +++ b/src/libs/advanceddockingsystem/workspaceview.h @@ -3,12 +3,12 @@ #pragma once +#include "workspaceinputdialog.h" #include "workspacemodel.h" +#include #include -#include - namespace ADS { class DockManager; @@ -22,32 +22,42 @@ public: explicit WorkspaceView(DockManager *manager, QWidget *parent = nullptr); void createNewWorkspace(); - void deleteSelectedWorkspaces(); void cloneCurrentWorkspace(); void renameCurrentWorkspace(); void resetCurrentWorkspace(); void switchToCurrentWorkspace(); + void deleteSelectedWorkspaces(); void importWorkspace(); void exportCurrentWorkspace(); + void moveWorkspaceUp(); + void moveWorkspaceDown(); + QString currentWorkspace(); WorkspaceModel *workspaceModel(); void selectActiveWorkspace(); - void selectWorkspace(const QString &workspaceName); + void selectWorkspace(const QString &fileName); QStringList selectedWorkspaces() const; signals: - void workspaceActivated(const QString &workspace); - void workspacesSelected(const QStringList &workspaces); + void workspaceActivated(const QString &fileName); + void workspacesSelected(const QStringList &fileNames); void workspaceSwitched(); private: void showEvent(QShowEvent *event) override; void keyPressEvent(QKeyEvent *event) override; + void dropEvent(QDropEvent *event) override; - void deleteWorkspaces(const QStringList &workspaces); + void deleteWorkspaces(const QStringList &fileNames); + + bool confirmWorkspaceDelete(const QStringList &fileNames); + + void runWorkspaceNameInputDialog( + WorkspaceNameInputDialog *workspaceInputDialog, + std::function(const QString &)> callback); DockManager *m_manager; WorkspaceModel m_workspaceModel; diff --git a/src/plugins/qmldesigner/components/toolbar/toolbarbackend.cpp b/src/plugins/qmldesigner/components/toolbar/toolbarbackend.cpp index 2c682537271..f563e5eeca7 100644 --- a/src/plugins/qmldesigner/components/toolbar/toolbarbackend.cpp +++ b/src/plugins/qmldesigner/components/toolbar/toolbarbackend.cpp @@ -79,10 +79,288 @@ static void openUiFile() Core::EditorManager::openEditor(mainUiFile, Utils::Id()); } +CrumbleBarModel::CrumbleBarModel(QObject *) +{ + connect(crumbleBar(), &CrumbleBar::pathChanged, this, &CrumbleBarModel::handleCrumblePathChanged); +} + +int CrumbleBarModel::rowCount(const QModelIndex &) const +{ + return crumbleBar()->path().count(); +} + +QHash CrumbleBarModel::roleNames() const +{ + static QHash roleNames{{Qt::UserRole + 1, "fileName"}, + {Qt::UserRole + 2, "fileAddress"}}; + + return roleNames; +} + +QVariant CrumbleBarModel::data(const QModelIndex &index, int role) const +{ + if (index.isValid() && index.row() < rowCount()) { + auto info = crumbleBar()->infos().at(index.row()); + + if (role == Qt::UserRole + 1) { + return info.displayName; + } else if (role == Qt::UserRole + 2) { + return info.fileName.displayName(); + } else { + qWarning() << Q_FUNC_INFO << "invalid role"; + } + } else { + qWarning() << Q_FUNC_INFO << "invalid index"; + } + + return QVariant(); +} + +void CrumbleBarModel::handleCrumblePathChanged() +{ + beginResetModel(); + endResetModel(); +} + +void CrumbleBarModel::onCrumblePathElementClicked(int i) +{ + if (i < rowCount()) { + auto info = crumbleBar()->infos().at(i); + crumbleBar()->onCrumblePathElementClicked(QVariant::fromValue(info)); + } +} + +WorkspaceModel::WorkspaceModel(QObject *) +{ + connect(designModeWidget(), &Internal::DesignModeWidget::initialized, this, [this]() { + const auto dockManager = designModeWidget()->dockManager(); + + connect(dockManager, &ADS::DockManager::workspaceListChanged, this, [this]() { + beginResetModel(); + endResetModel(); + }); + + beginResetModel(); + endResetModel(); + }); +} + +int WorkspaceModel::rowCount(const QModelIndex &) const +{ + if (designModeWidget() && designModeWidget()->dockManager()) + return designModeWidget()->dockManager()->workspaces().count(); + + return 0; +} + +QHash WorkspaceModel::roleNames() const +{ + static QHash roleNames{{DisplayNameRole, "displayName"}, + {FileNameRole, "fileName"}}; + + return roleNames; +} + +QVariant WorkspaceModel::data(const QModelIndex &index, int role) const +{ + if (index.isValid() && index.row() < rowCount()) { + auto workspace = designModeWidget()->dockManager()->workspaces().at(index.row()); + + if (role == DisplayNameRole) { + return workspace.name(); + } else if (role == FileNameRole) { + return workspace.fileName(); + } else { + qWarning() << Q_FUNC_INFO << "invalid role"; + } + } else { + qWarning() << Q_FUNC_INFO << "invalid index"; + } + + return QVariant(); +} + +ActionSubscriber::ActionSubscriber(QObject *parent) + : QObject(parent) +{ + ActionAddedInterface callback = [this](ActionInterface *interface) { + if (interface->menuId() == m_actionId.toLatin1()) { + m_interface = interface; + setupNotifier(); + } + }; + + QmlDesignerPlugin::instance()->viewManager().designerActionManager().addAddActionCallback(callback); +} + +void ActionSubscriber::trigger() +{ + if (m_interface) + m_interface->action()->trigger(); +} + +bool ActionSubscriber::available() const +{ + if (m_interface) + return m_interface->action()->isEnabled(); + return false; +} + +bool ActionSubscriber::checked() const +{ + if (m_interface) + return m_interface->action()->isChecked(); + + return false; +} + +QString ActionSubscriber::actionId() const +{ + return m_actionId; +} + +void ActionSubscriber::setActionId(const QString &id) +{ + if (id == m_actionId) + return; + + m_actionId = id; + emit actionIdChanged(); + emit tooltipChanged(); +} + +QString ActionSubscriber::tooltip() const +{ + if (m_interface) + return m_interface->action()->text(); + return {}; +} + +void ActionSubscriber::setupNotifier() +{ + if (!m_interface) + return; + + connect(m_interface->action(), &QAction::enabledChanged, this, &ActionSubscriber::availableChanged); + + emit tooltipChanged(); +} + +ToolBarBackend::ToolBarBackend(QObject *parent) + : QObject(parent) +{ + ActionAddedInterface callback = [this](ActionInterface *interface) { + if (interface->menuId() == "PreviewZoom") + m_zoomAction = interface; + }; + + QmlDesignerPlugin::instance()->viewManager().designerActionManager().addAddActionCallback(callback); + + connect(designModeWidget(), + &Internal::DesignModeWidget::navigationHistoryChanged, + this, + &ToolBarBackend::navigationHistoryChanged); + + connect(Core::DocumentModel::model(), + &QAbstractItemModel::rowsInserted, + this, + &ToolBarBackend::updateDocumentModel); + connect(Core::DocumentModel::model(), + &QAbstractItemModel::rowsRemoved, + this, + &ToolBarBackend::updateDocumentModel); + connect(Core::DocumentModel::model(), + &QAbstractItemModel::dataChanged, + this, + &ToolBarBackend::updateDocumentModel); + connect(Core::DocumentModel::model(), + &QAbstractItemModel::modelReset, + this, + &ToolBarBackend::updateDocumentModel); + + connect(Core::EditorManager::instance(), + &Core::EditorManager::currentEditorChanged, + this, + &ToolBarBackend::documentIndexChanged); + + connect(Core::EditorManager::instance(), + &Core::EditorManager::currentEditorChanged, + this, + &ToolBarBackend::currentStyleChanged); + + connect(designModeWidget(), &Internal::DesignModeWidget::initialized, this, [this]() { + const auto dockManager = designModeWidget()->dockManager(); + + connect(dockManager, &ADS::DockManager::workspaceLoaded, this, [this](const QString &) { + emit currentWorkspaceChanged(); + }); + + connect(dockManager, &ADS::DockManager::workspaceListChanged, this, [this]() { + emit currentWorkspaceChanged(); + }); + + emit currentWorkspaceChanged(); + }); + + auto editorManager = Core::EditorManager::instance(); + + connect(editorManager, &Core::EditorManager::documentClosed, this, [this]() { + if (isInDesignMode() && Core::DocumentModel::entryCount() == 0) { + QTimer::singleShot(0, + Core::ModeManager::instance(), + []() { /* The mode change has to happen from event loop. + Otherwise we and up in an invalid state */ + Core::ModeManager::activateMode(Core::Constants::MODE_WELCOME); + }); + } + }); + + connect(Core::ICore::instance(), &Core::ICore::coreAboutToOpen, this, [this] { + connect(Core::DesignMode::instance(), &Core::DesignMode::enabledStateChanged, this, [this] { + emit isDesignModeEnabledChanged(); + }); + }); + + connect(Core::ModeManager::instance(), &Core::ModeManager::currentModeChanged, this, [this]() { + emit isInDesignModeChanged(); + emit isInEditModeChanged(); + emit isDesignModeEnabledChanged(); + }); + + connect(ProjectExplorer::SessionManager::instance(), + &ProjectExplorer::SessionManager::startupProjectChanged, + this, + [this](ProjectExplorer::Project *project) { + disconnect(m_kitConnection); + emit isQt6Changed(); + emit projectOpenedChanged(); + if (project) { + m_kitConnection = connect(project, + &ProjectExplorer::Project::activeTargetChanged, + this, + &ToolBarBackend::currentKitChanged); + emit currentKitChanged(); + } + }); + + connect(ProjectExplorer::KitManager::instance(), + &ProjectExplorer::KitManager::kitsChanged, + this, + &ToolBarBackend::kitsChanged); +} + +void ToolBarBackend::registerDeclarativeType() +{ + qmlRegisterType("ToolBar", 1, 0, "ToolBarBackend"); + qmlRegisterType("ToolBar", 1, 0, "ActionSubscriber"); + qmlRegisterType("ToolBar", 1, 0, "CrumbleBarModel"); + qmlRegisterType("ToolBar", 1, 0, "WorkspaceModel"); +} + void ToolBarBackend::triggerModeChange() { QmlDesignerPlugin::emitUsageStatistics(Constants::EVENT_TOOLBAR_MODE_CHANGE); - QTimer::singleShot(0, [this]() { //Do not trigger mode change directly from QML + QTimer::singleShot(0, this, [this]() { //Do not trigger mode change directly from QML bool qmlFileOpen = false; if (!projectOpened()) { @@ -109,7 +387,7 @@ void ToolBarBackend::triggerModeChange() void ToolBarBackend::triggerProjectSettings() { QmlDesignerPlugin::emitUsageStatistics(Constants::EVENT_TOOLBAR_PROJECT_SETTINGS); - QTimer::singleShot(0, []() { //Do not trigger mode change directly from QML + QTimer::singleShot(0, Core::ModeManager::instance(), []() { // Do not trigger mode change directly from QML Core::ModeManager::activateMode(ProjectExplorer::Constants::MODE_SESSION); }); } @@ -249,113 +527,6 @@ bool ToolBarBackend::canGoForward() const return designModeWidget()->canGoForward(); } -ToolBarBackend::ToolBarBackend(QObject *parent) - : QObject(parent) -{ - ActionAddedInterface callback = [this](ActionInterface *interface) { - if (interface->menuId() == "PreviewZoom") - m_zoomAction = interface; - }; - - QmlDesignerPlugin::instance()->viewManager().designerActionManager().addAddActionCallback(callback); - - connect(designModeWidget(), - &Internal::DesignModeWidget::navigationHistoryChanged, - this, - &ToolBarBackend::navigationHistoryChanged); - - connect(Core::DocumentModel::model(), - &QAbstractItemModel::rowsInserted, - this, - &ToolBarBackend::updateDocumentModel); - connect(Core::DocumentModel::model(), - &QAbstractItemModel::rowsRemoved, - this, - &ToolBarBackend::updateDocumentModel); - connect(Core::DocumentModel::model(), - &QAbstractItemModel::dataChanged, - this, - &ToolBarBackend::updateDocumentModel); - connect(Core::DocumentModel::model(), - &QAbstractItemModel::modelReset, - this, - &ToolBarBackend::updateDocumentModel); - - connect(Core::EditorManager::instance(), - &Core::EditorManager::currentEditorChanged, - this, - &ToolBarBackend::documentIndexChanged); - - connect(Core::EditorManager::instance(), - &Core::EditorManager::currentEditorChanged, - this, - &ToolBarBackend::currentStyleChanged); - - connect(designModeWidget(), &Internal::DesignModeWidget::initialized, this, [this]() { - const auto dockManager = designModeWidget()->dockManager(); - - connect(dockManager, - &ADS::DockManager::workspaceListChanged, - this, - &ToolBarBackend::setupWorkspaces); - connect(dockManager, &ADS::DockManager::workspaceLoaded, this, [this](const QString &) { - emit currentWorkspaceChanged(); - }); - - setupWorkspaces(); - }); - - auto editorManager = Core::EditorManager::instance(); - - connect(editorManager, &Core::EditorManager::documentClosed, this, [this]() { - if (isInDesignMode() && Core::DocumentModel::entryCount() == 0) { - QTimer::singleShot(0, []() { /* The mode change has to happen from event loop. - Otherwise we and up in an invalid state */ - Core::ModeManager::activateMode(Core::Constants::MODE_WELCOME); - }); - } - }); - - connect(Core::ICore::instance(), &Core::ICore::coreAboutToOpen, this, [this] { - connect(Core::DesignMode::instance(), &Core::DesignMode::enabledStateChanged, this, [this] { - emit isDesignModeEnabledChanged(); - }); - }); - - connect(Core::ModeManager::instance(), &Core::ModeManager::currentModeChanged, this, [this]() { - emit isInDesignModeChanged(); - emit isInEditModeChanged(); - emit isDesignModeEnabledChanged(); - }); - - connect(ProjectExplorer::SessionManager::instance(), - &ProjectExplorer::SessionManager::startupProjectChanged, - [this](ProjectExplorer::Project *project) { - disconnect(m_kitConnection); - emit isQt6Changed(); - emit projectOpenedChanged(); - if (project) { - m_kitConnection = connect(project, - &ProjectExplorer::Project::activeTargetChanged, - this, - &ToolBarBackend::currentKitChanged); - emit currentKitChanged(); - } - }); - - connect(ProjectExplorer::KitManager::instance(), - &ProjectExplorer::KitManager::kitsChanged, - this, - &ToolBarBackend::kitsChanged); -} - -void ToolBarBackend::registerDeclarativeType() -{ - qmlRegisterType("ToolBar", 1, 0, "ToolBarBackend"); - qmlRegisterType("ToolBar", 1, 0, "ActionSubscriber"); - qmlRegisterType("ToolBar", 1, 0, "CrumbleBarModel"); -} - QStringList ToolBarBackend::documentModel() const { return m_openDocuments; @@ -384,13 +555,9 @@ int ToolBarBackend::documentIndex() const QString ToolBarBackend::currentWorkspace() const { if (designModeWidget() && designModeWidget()->dockManager()) - return designModeWidget()->dockManager()->activeWorkspace(); - return {}; -} + return designModeWidget()->dockManager()->activeWorkspace()->fileName(); -QStringList ToolBarBackend::workspaces() const -{ - return m_workspaces; + return {}; } QStringList ToolBarBackend::styles() const @@ -419,17 +586,6 @@ bool ToolBarBackend::isInEditMode() const return Core::ModeManager::currentModeId() == Core::Constants::MODE_EDIT; } -void ToolBarBackend::launchGlobalAnnotations() -{ - QmlDesignerPlugin::emitUsageStatistics(Constants::EVENT_TOOLBAR_EDIT_GLOBAL_ANNOTATION); - ModelNode node = currentDesignDocument()->rewriterView()->rootModelNode(); - - if (node.isValid()) { - designModeWidget()->globalAnnotationEditor().setModelNode(node); - designModeWidget()->globalAnnotationEditor().showWidget(); - } -} - bool ToolBarBackend::isDesignModeEnabled() const { if (Core::DesignMode::instance()) @@ -447,9 +603,7 @@ int ToolBarBackend::currentStyle() const const QString qmlFile = view->model()->fileUrl().toLocalFile(); - const int index = ChangeStyleWidgetAction::getCurrentStyle(qmlFile); - - return index; + return ChangeStyleWidgetAction::getCurrentStyle(qmlFile); } QStringList ToolBarBackend::kits() const @@ -487,129 +641,14 @@ bool ToolBarBackend::projectOpened() const return ProjectExplorer::SessionManager::instance()->startupProject(); } -void ToolBarBackend::setupWorkspaces() +void ToolBarBackend::launchGlobalAnnotations() { - m_workspaces.clear(); - m_workspaces = designModeWidget()->dockManager()->workspaces(); - Utils::sort(m_workspaces); - emit workspacesChanged(); - emit currentWorkspaceChanged(); -} + QmlDesignerPlugin::emitUsageStatistics(Constants::EVENT_TOOLBAR_EDIT_GLOBAL_ANNOTATION); + ModelNode node = currentDesignDocument()->rewriterView()->rootModelNode(); -ActionSubscriber::ActionSubscriber(QObject *parent) - : QObject(parent) -{ - ActionAddedInterface callback = [this](ActionInterface *interface) { - if (interface->menuId() == m_actionId.toLatin1()) { - m_interface = interface; - setupNotifier(); - } - }; - - QmlDesignerPlugin::instance()->viewManager().designerActionManager().addAddActionCallback(callback); -} - -void ActionSubscriber::trigger() -{ - if (m_interface) - m_interface->action()->trigger(); -} - -bool ActionSubscriber::available() const -{ - if (m_interface) - return m_interface->action()->isEnabled(); - return false; -} - -bool ActionSubscriber::checked() const -{ - if (m_interface) - return m_interface->action()->isChecked(); - - return false; -} - -QString ActionSubscriber::actionId() const -{ - return m_actionId; -} - -void ActionSubscriber::setActionId(const QString &id) -{ - if (id == m_actionId) - return; - - m_actionId = id; - emit actionIdChanged(); - emit tooltipChanged(); -} - -QString ActionSubscriber::tooltip() const -{ - if (m_interface) - return m_interface->action()->text(); - return {}; -} - -void ActionSubscriber::setupNotifier() -{ - if (!m_interface) - return; - - connect(m_interface->action(), &QAction::enabledChanged, this, &ActionSubscriber::availableChanged); - - emit tooltipChanged(); -} - -CrumbleBarModel::CrumbleBarModel(QObject *) -{ - connect(crumbleBar(), &CrumbleBar::pathChanged, this, &CrumbleBarModel::handleCrumblePathChanged); -} - -int CrumbleBarModel::rowCount(const QModelIndex &) const -{ - return crumbleBar()->path().count(); -} - -QHash CrumbleBarModel::roleNames() const -{ - static QHash roleNames{{Qt::UserRole + 1, "fileName"}, - {Qt::UserRole + 2, "fileAddress"}}; - - return roleNames; -} - -QVariant CrumbleBarModel::data(const QModelIndex &index, int role) const -{ - if (index.isValid() && index.row() < rowCount()) { - auto info = crumbleBar()->infos().at(index.row()); - - if (role == Qt::UserRole + 1) { - return info.displayName; - } else if (role == Qt::UserRole + 2) { - return info.fileName.displayName(); - } else { - qWarning() << Q_FUNC_INFO << "invalid role"; - } - } else { - qWarning() << Q_FUNC_INFO << "invalid index"; - } - - return QVariant(); -} - -void CrumbleBarModel::handleCrumblePathChanged() -{ - beginResetModel(); - endResetModel(); -} - -void CrumbleBarModel::onCrumblePathElementClicked(int i) -{ - if (i < rowCount()) { - auto info = crumbleBar()->infos().at(i); - crumbleBar()->onCrumblePathElementClicked(QVariant::fromValue(info)); + if (node.isValid()) { + designModeWidget()->globalAnnotationEditor().setModelNode(node); + designModeWidget()->globalAnnotationEditor().showWidget(); } } diff --git a/src/plugins/qmldesigner/components/toolbar/toolbarbackend.h b/src/plugins/qmldesigner/components/toolbar/toolbarbackend.h index 1aed5aeeaf4..0091ab8a7f1 100644 --- a/src/plugins/qmldesigner/components/toolbar/toolbarbackend.h +++ b/src/plugins/qmldesigner/components/toolbar/toolbarbackend.h @@ -18,15 +18,25 @@ public: explicit CrumbleBarModel(QObject *parent = nullptr); int rowCount(const QModelIndex &parent = QModelIndex()) const override; - QHash roleNames() const override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; void handleCrumblePathChanged(); Q_INVOKABLE void onCrumblePathElementClicked(int i); +}; -private: +class WorkspaceModel : public QAbstractListModel +{ + Q_OBJECT + enum { DisplayNameRole = Qt::DisplayRole, FileNameRole = Qt::UserRole }; + +public: + explicit WorkspaceModel(QObject *parent = nullptr); + + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + QHash roleNames() const override; + QVariant data(const QModelIndex &index, int role = DisplayNameRole) const override; }; class ActionSubscriber : public QObject @@ -73,7 +83,6 @@ class ToolBarBackend : public QObject Q_PROPERTY(QStringList documentModel READ documentModel NOTIFY openDocumentsChanged) Q_PROPERTY(int documentIndex READ documentIndex NOTIFY documentIndexChanged) Q_PROPERTY(QString currentWorkspace READ currentWorkspace NOTIFY currentWorkspaceChanged) - Q_PROPERTY(QStringList workspaces READ workspaces NOTIFY workspacesChanged) Q_PROPERTY(QStringList styles READ styles CONSTANT) Q_PROPERTY(bool isInDesignMode READ isInDesignMode NOTIFY isInDesignModeChanged) Q_PROPERTY(bool isInEditMode READ isInEditMode NOTIFY isInEditModeChanged) @@ -111,11 +120,11 @@ public: int documentIndex() const; QString currentWorkspace() const; - QStringList workspaces() const; QStringList styles() const; bool isInDesignMode() const; + bool isInEditMode() const; bool isDesignModeEnabled() const; int currentStyle() const; @@ -127,8 +136,6 @@ public: bool projectOpened() const; - bool isInEditMode() const; - static void launchGlobalAnnotations(); signals: @@ -136,7 +143,6 @@ signals: void openDocumentsChanged(); void documentIndexChanged(); void currentWorkspaceChanged(); - void workspacesChanged(); void isInDesignModeChanged(); void isInEditModeChanged(); void isDesignModeEnabledChanged(); @@ -152,7 +158,6 @@ private: ActionInterface *m_zoomAction; QStringList m_openDocuments; - QStringList m_workspaces; QMetaObject::Connection m_kitConnection; }; diff --git a/src/plugins/qmldesigner/designmodewidget.cpp b/src/plugins/qmldesigner/designmodewidget.cpp index c20f72a81a1..40e455f40c2 100644 --- a/src/plugins/qmldesigner/designmodewidget.cpp +++ b/src/plugins/qmldesigner/designmodewidget.cpp @@ -372,39 +372,14 @@ void DesignModeWidget::setup() connect(m_toolBar, &Core::EditorToolBar::goForwardClicked, this, &DesignModeWidget::toolBarOnGoForwardClicked); connect(m_toolBar, &Core::EditorToolBar::goBackClicked, this, &DesignModeWidget::toolBarOnGoBackClicked); - QToolBar* toolBarWrapper = new QToolBar(); toolBarWrapper->addWidget(m_toolBar); toolBarWrapper->addWidget(createCrumbleBarFrame()); toolBarWrapper->setMovable(false); addToolBar(Qt::TopToolBarArea, toolBarWrapper); - addSpacerToToolBar(toolBar); - auto workspaceComboBox = new QComboBox(); - workspaceComboBox->setMinimumWidth(120); - workspaceComboBox->setToolTip(tr("Switch the active workspace.")); - auto sortedWorkspaces = m_dockManager->workspaces(); - Utils::sort(sortedWorkspaces); - workspaceComboBox->addItems(sortedWorkspaces); - workspaceComboBox->setCurrentText(m_dockManager->activeWorkspace()); - toolBar->addWidget(workspaceComboBox); - - connect(m_dockManager, &ADS::DockManager::workspaceListChanged, - workspaceComboBox, [this, workspaceComboBox]() { - workspaceComboBox->clear(); - auto sortedWorkspaces = m_dockManager->workspaces(); - Utils::sort(sortedWorkspaces); - workspaceComboBox->addItems(sortedWorkspaces); - workspaceComboBox->setCurrentText(m_dockManager->activeWorkspace()); - }); - connect(m_dockManager, &ADS::DockManager::workspaceLoaded, workspaceComboBox, &QComboBox::setCurrentText); - connect(workspaceComboBox, &QComboBox::activated, - m_dockManager, [this, workspaceComboBox]([[maybe_unused]] int index) { - m_dockManager->openWorkspace(workspaceComboBox->currentText()); - }); - const QIcon gaIcon = Utils::StyleHelper::getIconFromIconFont( fontName, Theme::getIconUnicode(Theme::Icon::annotationBubble), 36, 36, Theme::getColor(Theme::IconsBaseColor)); @@ -416,7 +391,6 @@ void DesignModeWidget::setup() m_globalAnnotationEditor.showWidget(); } }); - } if (currentDesignDocument()) @@ -448,8 +422,6 @@ void DesignModeWidget::setup() } }); - - viewManager().enableWidgets(); readSettings(); show(); @@ -464,8 +436,7 @@ void DesignModeWidget::aboutToShowWorkspaces() auto *ag = new QActionGroup(menu); connect(ag, &QActionGroup::triggered, this, [this](QAction *action) { - QString workspace = action->data().toString(); - m_dockManager->openWorkspace(workspace); + m_dockManager->openWorkspace(action->data().toString()); }); QAction *action = menu->addAction(tr("Manage...")); @@ -473,21 +444,18 @@ void DesignModeWidget::aboutToShowWorkspaces() QAction *resetWorkspace = menu->addAction(tr("Reset Active")); connect(resetWorkspace, &QAction::triggered, this, [this]() { - if (m_dockManager->resetWorkspacePreset(m_dockManager->activeWorkspace())) + if (m_dockManager->resetWorkspacePreset(m_dockManager->activeWorkspace()->fileName())) m_dockManager->reloadActiveWorkspace(); }); menu->addSeparator(); - // Sort the list of workspaces - auto sortedWorkspaces = m_dockManager->workspaces(); - Utils::sort(sortedWorkspaces); - - for (const auto &workspace : std::as_const(sortedWorkspaces)) { - QAction *action = ag->addAction(workspace); - action->setData(workspace); + auto workspaces = m_dockManager->workspaces(); + for (const auto &workspace : std::as_const(workspaces)) { + QAction *action = ag->addAction(workspace.name()); + action->setData(workspace.fileName()); action->setCheckable(true); - if (workspace == m_dockManager->activeWorkspace()) + if (workspace == *m_dockManager->activeWorkspace()) action->setChecked(true); } menu->addActions(ag->actions()); From 34c69766dd0948bf7c15b4b9534cc21d073afd31 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Tue, 18 Apr 2023 11:51:09 +0200 Subject: [PATCH 054/192] QmlDesigner: Add version scanning for Qt5 Add some hardwired code for lagacy Qt5 support. Task-number: QDS-9542 Change-Id: I505685042f53b7e653e76c8d883c8d7bb30605d7 Reviewed-by: Thomas Hartmann Reviewed-by: Qt CI Bot --- .../include/externaldependenciesinterface.h | 2 + .../designercore/model/texttomodelmerger.cpp | 42 +++++++++++-- .../projectstorage/modulescanner.cpp | 17 +++++- .../projectstorage/modulescanner.h | 6 +- .../qmldesignerexternaldependencies.cpp | 59 ++++++++++++++----- .../qmldesignerexternaldependencies.h | 2 + .../qmldesigner/coretests/tst_testcore.cpp | 2 + tests/unit/unittest/data/modulescanner/qmldir | 5 ++ .../unit/unittest/gtest-creator-printing.cpp | 6 ++ tests/unit/unittest/gtest-creator-printing.h | 2 + tests/unit/unittest/modulescanner-test.cpp | 24 +++++++- 11 files changed, 144 insertions(+), 23 deletions(-) create mode 100644 tests/unit/unittest/data/modulescanner/qmldir diff --git a/src/plugins/qmldesigner/designercore/include/externaldependenciesinterface.h b/src/plugins/qmldesigner/designercore/include/externaldependenciesinterface.h index d6af071a30a..887f24ddceb 100644 --- a/src/plugins/qmldesigner/designercore/include/externaldependenciesinterface.h +++ b/src/plugins/qmldesigner/designercore/include/externaldependenciesinterface.h @@ -41,6 +41,8 @@ public: virtual bool instantQmlTextUpdate() const = 0; virtual Utils::FilePath qmlPuppetPath() const = 0; virtual QStringList modulePaths() const = 0; + virtual QStringList projectModulePaths() const = 0; + virtual bool isQt6Project() const = 0; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp index bb09d664a77..5bcaa4969f9 100644 --- a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp +++ b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp @@ -874,6 +874,24 @@ QList generatePossibleFileImports(const QString &path, return possibleImports; } +QmlDesigner::Imports createQt5Modules() +{ + return {QmlDesigner::Import::createLibraryImport("QtQuick", "5.15"), + QmlDesigner::Import::createLibraryImport("QtQuick3D", "5.15"), + QmlDesigner::Import::createLibraryImport("QtQuick.Controls", "5.15"), + QmlDesigner::Import::createLibraryImport("QtQuick.Window", "5.15"), + QmlDesigner::Import::createLibraryImport("QtQuick.Layouts", "5.15"), + QmlDesigner::Import::createLibraryImport("QtQuick.Timeline", "5.15"), + QmlDesigner::Import::createLibraryImport("QtCharts", "5.15"), + QmlDesigner::Import::createLibraryImport("QtDataVisulaization", "5.15"), + QmlDesigner::Import::createLibraryImport("QtQuick.Studio.Controls", "1.0"), + QmlDesigner::Import::createLibraryImport("QtQuick.Studio.Effects", "1.0"), + QmlDesigner::Import::createLibraryImport("FlowView", "1.0"), + QmlDesigner::Import::createLibraryImport("QtQuick.Studio.LogicHelper", "1.0"), + QmlDesigner::Import::createLibraryImport("QtQuick.Studio.MultiText", "1.0"), + QmlDesigner::Import::createLibraryImport("Qt.SafeRenderer", "2.0")}; +} + } // namespace void TextToModelMerger::setupPossibleImports() @@ -887,11 +905,25 @@ void TextToModelMerger::setupPossibleImports() auto allUsedImports = m_scopeChain->context()->imports(m_document.data())->all(); if (m_possibleModules.isEmpty() || projectUrl != lastProjectUrl) { - const auto skipModuleNames = m_rewriterView->model()->metaInfo().itemLibraryInfo()->blacklistImports(); - ModuleScanner moduleScanner{ - [&](QStringView moduleName) { return skipModule(moduleName, skipModuleNames); }}; - moduleScanner.scan(m_rewriterView->externalDependencies().modulePaths()); - m_possibleModules = moduleScanner.modules(); + + auto &externalDependencies = m_rewriterView->externalDependencies(); + if (externalDependencies.isQt6Project()) { + const auto skipModuleNames = m_rewriterView->model() + ->metaInfo() + .itemLibraryInfo() + ->blacklistImports(); + ModuleScanner moduleScanner{[&](QStringView moduleName) { + return skipModule(moduleName, skipModuleNames); + }, + VersionScanning::No}; + moduleScanner.scan(m_rewriterView->externalDependencies().modulePaths()); + m_possibleModules = moduleScanner.modules(); + } else { + ModuleScanner moduleScanner{[&](QStringView) { return false; }, VersionScanning::Yes}; + m_possibleModules = createQt5Modules(); + moduleScanner.scan(externalDependencies.projectModulePaths()); + m_possibleModules.append(moduleScanner.modules()); + } } lastProjectUrl = projectUrl; diff --git a/src/plugins/qmldesigner/designercore/projectstorage/modulescanner.cpp b/src/plugins/qmldesigner/designercore/projectstorage/modulescanner.cpp index 5fd70bfdd35..f2c248182df 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/modulescanner.cpp +++ b/src/plugins/qmldesigner/designercore/projectstorage/modulescanner.cpp @@ -24,6 +24,20 @@ std::optional contentAsQString(const QString &filePath) return {}; } +#ifdef QDS_HAS_QMLPRIVATE +QString createVersion(const QMultiHash &components) +{ + auto found = std::max_element(components.begin(), components.end(), [](auto &&first, auto &&second) { + return first.version < second.version; + }); + + if (found != components.end() && found->version.isValid()) + return QString{"%1.%2"}.arg(found->version.majorVersion()).arg(found->version.minorVersion()); + + return {}; +} +#endif + } // namespace void ModuleScanner::scan(const QStringList &modulePaths) @@ -61,7 +75,8 @@ void ModuleScanner::scan(std::string_view modulePath) if (moduleName.isEmpty() || m_skip(moduleName)) continue; - m_modules.push_back(Import::createLibraryImport(moduleName)); + m_modules.push_back( + Import::createLibraryImport(moduleName, createVersion(parser.components()))); } } } catch (const std::filesystem::filesystem_error &) { diff --git a/src/plugins/qmldesigner/designercore/projectstorage/modulescanner.h b/src/plugins/qmldesigner/designercore/projectstorage/modulescanner.h index 3c03f9cd36d..c44d7aa81ff 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/modulescanner.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/modulescanner.h @@ -11,13 +11,16 @@ namespace QmlDesigner { +enum class VersionScanning { No, Yes }; + class QMLDESIGNERCORE_EXPORT ModuleScanner { public: using SkipFunction = std::function; - ModuleScanner(SkipFunction skip) + ModuleScanner(SkipFunction skip, VersionScanning versionScanning) : m_skip{std::move(skip)} + , m_versionScanning{versionScanning} { m_modules.reserve(128); } @@ -32,6 +35,7 @@ private: private: SkipFunction m_skip; Imports m_modules; + VersionScanning m_versionScanning; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/qmldesignerexternaldependencies.cpp b/src/plugins/qmldesigner/qmldesignerexternaldependencies.cpp index 71c5379cda6..107d021d64f 100644 --- a/src/plugins/qmldesigner/qmldesignerexternaldependencies.cpp +++ b/src/plugins/qmldesigner/qmldesignerexternaldependencies.cpp @@ -203,35 +203,66 @@ QString qmlPath(ProjectExplorer::Target *target) return qtVersion->qmlPath().toString(); } -} // namespace -QStringList ExternalDependencies::modulePaths() const +std::tuple +activeProjectEntries() { - QStringList modulePaths; - auto project = ProjectExplorer::SessionManager::startupProject(); if (!project) - return modulePaths; + return {}; auto target = project->activeTarget(); if (!target) - return modulePaths; - - if (auto path = qmlPath(target); !path.isEmpty()) - modulePaths.push_back(path); + return {}; const auto qmlBuildSystem = qobject_cast( target->buildSystem()); - if (!qmlBuildSystem) - return modulePaths; + if (qmlBuildSystem) + return std::make_tuple(project, target, qmlBuildSystem); - for (const QString &modulePath : qmlBuildSystem->customImportPaths()) - modulePaths.append(project->projectDirectory().pathAppended(modulePath).toString()); + return {}; +} +} // namespace - return modulePaths; +QStringList ExternalDependencies::modulePaths() const +{ + auto [project, target, qmlBuildSystem] = activeProjectEntries(); + + if (project && target && qmlBuildSystem) { + QStringList modulePaths; + + if (auto path = qmlPath(target); !path.isEmpty()) + modulePaths.push_back(path); + + for (const QString &modulePath : qmlBuildSystem->customImportPaths()) + modulePaths.append(project->projectDirectory().pathAppended(modulePath).toString()); + } + + return {}; +} + +QStringList ExternalDependencies::projectModulePaths() const +{ + auto [project, target, qmlBuildSystem] = activeProjectEntries(); + + if (project && target && qmlBuildSystem) { + QStringList modulePaths; + + for (const QString &modulePath : qmlBuildSystem->customImportPaths()) + modulePaths.append(project->projectDirectory().pathAppended(modulePath).toString()); + } + + return {}; +} + +bool ExternalDependencies::isQt6Project() const +{ + auto [project, target, qmlBuildSystem] = activeProjectEntries(); + + return qmlBuildSystem && qmlBuildSystem->qt6Project(); } } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/qmldesignerexternaldependencies.h b/src/plugins/qmldesigner/qmldesignerexternaldependencies.h index b19ad0ae111..3c89dd40474 100644 --- a/src/plugins/qmldesigner/qmldesignerexternaldependencies.h +++ b/src/plugins/qmldesigner/qmldesignerexternaldependencies.h @@ -37,6 +37,8 @@ public: bool instantQmlTextUpdate() const override; Utils::FilePath qmlPuppetPath() const override; QStringList modulePaths() const override; + QStringList projectModulePaths() const override; + bool isQt6Project() const override; private: const DesignerSettings &m_designerSettings; diff --git a/tests/auto/qml/qmldesigner/coretests/tst_testcore.cpp b/tests/auto/qml/qmldesigner/coretests/tst_testcore.cpp index 0efa20ff8a2..e0f5ebda421 100644 --- a/tests/auto/qml/qmldesigner/coretests/tst_testcore.cpp +++ b/tests/auto/qml/qmldesigner/coretests/tst_testcore.cpp @@ -161,6 +161,8 @@ public: bool instantQmlTextUpdate() const override { return true; } Utils::FilePath qmlPuppetPath() const override { return {}; } QStringList modulePaths() const override { return {}; } + QStringList projectModulePaths() const override { return {}; } + bool isQt6Project() const override { return {}; } public: QSettings qsettings; diff --git a/tests/unit/unittest/data/modulescanner/qmldir b/tests/unit/unittest/data/modulescanner/qmldir new file mode 100644 index 00000000000..88203422f01 --- /dev/null +++ b/tests/unit/unittest/data/modulescanner/qmldir @@ -0,0 +1,5 @@ +module Example +MyButton 1.0 MyButton.qml +MyButton 1.1 MyButton11.qml +MyButton 1.3 MyButton13.qml +MyRectangle 1.2 MyRectangle12.qml diff --git a/tests/unit/unittest/gtest-creator-printing.cpp b/tests/unit/unittest/gtest-creator-printing.cpp index 4da2cee485b..3dbba3f50dc 100644 --- a/tests/unit/unittest/gtest-creator-printing.cpp +++ b/tests/unit/unittest/gtest-creator-printing.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -469,6 +470,11 @@ std::ostream &operator<<(std::ostream &out, const FileStatus &fileStatus) << fileStatus.lastModified << ")"; } +std::ostream &operator<<(std::ostream &out, const Import &import) +{ + return out << "(" << import.url() << ", " << import.version() << ")"; +} + std::ostream &operator<<(std::ostream &out, SourceType sourceType) { return out << sourceTypeToText(sourceType); diff --git a/tests/unit/unittest/gtest-creator-printing.h b/tests/unit/unittest/gtest-creator-printing.h index d9d3cba5592..5aad4c40c39 100644 --- a/tests/unit/unittest/gtest-creator-printing.h +++ b/tests/unit/unittest/gtest-creator-printing.h @@ -121,6 +121,7 @@ class IdPaths; class ProjectChunkId; enum class SourceType : int; class FileStatus; +class Import; std::ostream &operator<<(std::ostream &out, const ModelNode &node); std::ostream &operator<<(std::ostream &out, const VariantProperty &property); @@ -129,6 +130,7 @@ std::ostream &operator<<(std::ostream &out, const IdPaths &idPaths); std::ostream &operator<<(std::ostream &out, const ProjectChunkId &id); std::ostream &operator<<(std::ostream &out, SourceType sourceType); std::ostream &operator<<(std::ostream &out, const FileStatus &fileStatus); +std::ostream &operator<<(std::ostream &out, const Import &import); namespace Cache { class SourceContext; diff --git a/tests/unit/unittest/modulescanner-test.cpp b/tests/unit/unittest/modulescanner-test.cpp index bd72d3db901..b98fc3cdb3b 100644 --- a/tests/unit/unittest/modulescanner-test.cpp +++ b/tests/unit/unittest/modulescanner-test.cpp @@ -15,11 +15,19 @@ auto UrlProperty(const Matcher &matcher) return Property(&QmlDesigner::Import::url, matcher); } +template +auto VersionProperty(const Matcher &matcher) +{ + return Property(&QmlDesigner::Import::version, matcher); +} + class ModuleScanner : public testing::Test { protected: - QmlDesigner::ModuleScanner scanner{ - [](QStringView moduleName) { return moduleName.endsWith(u"impl"); }}; + QmlDesigner::ModuleScanner scanner{[](QStringView moduleName) { + return moduleName.endsWith(u"impl"); + }, + QmlDesigner::VersionScanning::No}; }; TEST_F(ModuleScanner, ReturnEmptyOptionalForWrongPath) @@ -50,4 +58,16 @@ TEST_F(ModuleScanner, UseSkipFunction) ASSERT_THAT(scanner.modules(), Not(Contains(UrlProperty(EndsWith(QStringView{u"impl"}))))); } +TEST_F(ModuleScanner, Version) +{ + QmlDesigner::ModuleScanner scanner{[](QStringView moduleName) { + return moduleName.endsWith(u"impl"); + }, + QmlDesigner::VersionScanning::Yes}; + + scanner.scan(QStringList{TESTDATA_DIR "/modulescanner"}); + + ASSERT_THAT(scanner.modules(), ElementsAre(AllOf(UrlProperty("Example"), VersionProperty("1.3")))); +} + } // namespace From 66288ef1138e04161819c77f8186a69e82f29469 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Tue, 18 Apr 2023 13:38:07 +0200 Subject: [PATCH 055/192] QmlDesigner: Cleanup QmlDesignerBasePlugin Please don't put unrelated code into a plugin source file. It gets really cluttered. Use UniqueObjectPtr if the QObject has a parent. Change-Id: I6535163be1f67528a0cf66bf050f48601e3fa09c Reviewed-by: Qt CI Bot Reviewed-by: Thomas Hartmann Reviewed-by: --- .../contentlibrarytexturesmodel.cpp | 8 +- src/plugins/qmldesigner/qmldesignerplugin.h | 1 - src/plugins/qmldesignerbase/CMakeLists.txt | 8 + .../qmldesignerbase/qmldesignerbaseplugin.cpp | 254 ++---------------- .../qmldesignerbase/qmldesignerbaseplugin.h | 43 +-- .../{utils => studio}/studioquickwidget.cpp | 0 .../{utils => studio}/studioquickwidget.h | 0 .../studio/studiosettingspage.cpp | 217 +++++++++++++++ .../studio/studiosettingspage.h | 49 ++++ .../{utils => studio}/studiostyle.cpp | 0 .../{utils => studio}/studiostyle.h | 0 .../qmldesignerbase/utils/designerpaths.cpp | 47 ++++ .../qmldesignerbase/utils/designerpaths.h | 20 ++ src/plugins/studiowelcome/examplecheckout.cpp | 8 +- 14 files changed, 370 insertions(+), 285 deletions(-) rename src/plugins/qmldesignerbase/{utils => studio}/studioquickwidget.cpp (100%) rename src/plugins/qmldesignerbase/{utils => studio}/studioquickwidget.h (100%) create mode 100644 src/plugins/qmldesignerbase/studio/studiosettingspage.cpp create mode 100644 src/plugins/qmldesignerbase/studio/studiosettingspage.h rename src/plugins/qmldesignerbase/{utils => studio}/studiostyle.cpp (100%) rename src/plugins/qmldesignerbase/{utils => studio}/studiostyle.h (100%) create mode 100644 src/plugins/qmldesignerbase/utils/designerpaths.cpp create mode 100644 src/plugins/qmldesignerbase/utils/designerpaths.h diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexturesmodel.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexturesmodel.cpp index 0d74be596f5..5cf88ab6fcd 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexturesmodel.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexturesmodel.cpp @@ -4,8 +4,8 @@ #include "contentlibrarytexturesmodel.h" #include "contentlibrarytexturescategory.h" -#include "qmldesignerplugin.h" +#include #include #include @@ -119,8 +119,10 @@ void ContentLibraryTexturesModel::loadTextureBundle(const QString &remoteUrl, co for (const QFileInfo &tex : texFiles) { QString fullRemoteUrl = QString("%1/%2/%3.zip").arg(remoteUrl, dir.fileName(), tex.baseName()); - QString localDownloadPath = QString("%1/%2/%3").arg(QmlDesignerBasePlugin::bundlesPathSetting(), - m_category, dir.fileName()); + QString localDownloadPath = QString("%1/%2/%3") + .arg(Paths::bundlesPathSetting(), + m_category, + dir.fileName()); QString key = QString("%1/%2/%3").arg(m_category, dir.fileName(), tex.baseName()); QString fileExt; QSize dimensions; diff --git a/src/plugins/qmldesigner/qmldesignerplugin.h b/src/plugins/qmldesigner/qmldesignerplugin.h index 78bf02ac042..6251317bc42 100644 --- a/src/plugins/qmldesigner/qmldesignerplugin.h +++ b/src/plugins/qmldesigner/qmldesignerplugin.h @@ -114,7 +114,6 @@ private: // variables QmlDesignerPluginPrivate *d = nullptr; static QmlDesignerPlugin *m_instance; QElapsedTimer m_usageTimer; - StudioConfigSettingsPage m_settingsPage; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesignerbase/CMakeLists.txt b/src/plugins/qmldesignerbase/CMakeLists.txt index f02c4861067..628403874c0 100644 --- a/src/plugins/qmldesignerbase/CMakeLists.txt +++ b/src/plugins/qmldesignerbase/CMakeLists.txt @@ -20,8 +20,16 @@ extend_qtc_plugin(QmlDesignerBase PUBLIC_INCLUDES ${CMAKE_CURRENT_LIST_DIR}/utils SOURCES_PREFIX ${CMAKE_CURRENT_LIST_DIR}/utils SOURCES + designerpaths.cpp designerpaths.h designersettings.cpp designersettings.h qmlpuppetpaths.cpp qmlpuppetpaths.h +) + +extend_qtc_plugin(QmlDesignerBase + PUBLIC_INCLUDES ${CMAKE_CURRENT_LIST_DIR}/studio + SOURCES_PREFIX ${CMAKE_CURRENT_LIST_DIR}/studio + SOURCES studiostyle.cpp studiostyle.h studioquickwidget.cpp studioquickwidget.h + studiosettingspage.cpp studiosettingspage.h ) diff --git a/src/plugins/qmldesignerbase/qmldesignerbaseplugin.cpp b/src/plugins/qmldesignerbase/qmldesignerbaseplugin.cpp index db2ff7b7ca9..2a2759c3007 100644 --- a/src/plugins/qmldesignerbase/qmldesignerbaseplugin.cpp +++ b/src/plugins/qmldesignerbase/qmldesignerbaseplugin.cpp @@ -3,41 +3,25 @@ #include "qmldesignerbaseplugin.h" -#include "utils/designersettings.h" -#include "utils/hostosinfo.h" -#include "utils/studiostyle.h" -#include "utils/theme/theme.h" +#include "studiosettingspage.h" + +#include "studio/studiostyle.h" +#include "utils/designersettings.h" -#include -#include #include -#include -#include -#include +#include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -using Utils::HostOsInfo; namespace QmlDesigner { -const char EXAMPLES_DOWNLOAD_PATH[] = "StudioConfig/ExamplesDownloadPath"; -const char BUNDLES_DOWNLOAD_PATH[] = "StudioConfig/BundlesDownloadPath"; class QmlDesignerBasePlugin::Data { public: DesignerSettings settings; - QScopedPointer style; + Utils::UniqueObjectPtr style; + StudioConfigSettingsPage studioConfigSettingsPage; Data() : settings(Core::ICore::settings()) @@ -53,11 +37,6 @@ QmlDesignerBasePlugin::QmlDesignerBasePlugin() global = this; }; -QmlDesignerBasePlugin *QmlDesignerBasePlugin::instance() -{ - return global; -}; - QmlDesignerBasePlugin::~QmlDesignerBasePlugin() = default; DesignerSettings &QmlDesignerBasePlugin::settings() @@ -67,10 +46,15 @@ DesignerSettings &QmlDesignerBasePlugin::settings() QStyle *QmlDesignerBasePlugin::style() { - if (global->d->style.isNull()) - global->d->style.reset(new StudioStyle(qApp->style())); + if (!global->d->style) + global->d->style = Utils::makeUniqueObjectPtr(QApplication::style()); - return global->d->style.data(); + return global->d->style.get(); +} + +StudioConfigSettingsPage *QmlDesignerBasePlugin::studioConfigSettingsPage() +{ + return &global->d->studioConfigSettingsPage; } bool QmlDesignerBasePlugin::initialize(const QStringList &, QString *) @@ -80,212 +64,4 @@ bool QmlDesignerBasePlugin::initialize(const QStringList &, QString *) return true; } -Utils::FilePath QmlDesignerBasePlugin::defaultExamplesPath() -{ - QStandardPaths::StandardLocation location = Utils::HostOsInfo::isMacHost() - ? QStandardPaths::HomeLocation - : QStandardPaths::DocumentsLocation; - - return Utils::FilePath::fromString(QStandardPaths::writableLocation(location)) - .pathAppended("QtDesignStudio/examples"); -} - -Utils::FilePath QmlDesignerBasePlugin::defaultBundlesPath() -{ - QStandardPaths::StandardLocation location = Utils::HostOsInfo::isMacHost() - ? QStandardPaths::HomeLocation - : QStandardPaths::DocumentsLocation; - - return Utils::FilePath::fromString(QStandardPaths::writableLocation(location)) - .pathAppended("QtDesignStudio/bundles"); -} - -QString QmlDesignerBasePlugin::examplesPathSetting() -{ - return Core::ICore::settings() - ->value(EXAMPLES_DOWNLOAD_PATH, defaultExamplesPath().toString()) - .toString(); -} - -QString QmlDesignerBasePlugin::bundlesPathSetting() -{ - return Core::ICore::settings() - ->value(BUNDLES_DOWNLOAD_PATH, defaultBundlesPath().toString()) - .toString(); -} - -static bool hideBuildMenuSetting() -{ - return Core::ICore::settings() - ->value(ProjectExplorer::Constants::SETTINGS_MENU_HIDE_BUILD, false) - .toBool(); -} - -static bool hideDebugMenuSetting() -{ - return Core::ICore::settings() - ->value(ProjectExplorer::Constants::SETTINGS_MENU_HIDE_DEBUG, false) - .toBool(); -} - -static bool hideAnalyzeMenuSetting() -{ - return Core::ICore::settings() - ->value(ProjectExplorer::Constants::SETTINGS_MENU_HIDE_ANALYZE, false) - .toBool(); -} - -static bool hideToolsMenuSetting() -{ - return Core::ICore::settings()->value(Core::Constants::SETTINGS_MENU_HIDE_TOOLS, false).toBool(); -} - -void setSettingIfDifferent(const QString &key, bool value, bool &dirty) -{ - QSettings *s = Core::ICore::settings(); - if (s->value(key, false).toBool() != value) { - dirty = true; - s->setValue(key, value); - } -} - -StudioSettingsPage::StudioSettingsPage() - : m_buildCheckBox(new QCheckBox(tr("Build"))) - , m_debugCheckBox(new QCheckBox(tr("Debug"))) - , m_analyzeCheckBox(new QCheckBox(tr("Analyze"))) - , m_toolsCheckBox(new QCheckBox(tr("Tools"))) - , m_pathChooserExamples(new Utils::PathChooser()) - , m_pathChooserBundles(new Utils::PathChooser()) -{ - const QString toolTip = tr( - "Hide top-level menus with advanced functionality to simplify the UI. Build is " - "generally not required in the context of Qt Design Studio. Debug and " - "Analyze " - "are only required for debugging and profiling. Tools can be useful for bookmarks " - "and git integration."); - - QVBoxLayout *boxLayout = new QVBoxLayout(); - setLayout(boxLayout); - auto groupBox = new QGroupBox(tr("Hide Menu")); - groupBox->setToolTip(toolTip); - boxLayout->addWidget(groupBox); - - auto verticalLayout = new QVBoxLayout(); - groupBox->setLayout(verticalLayout); - - m_buildCheckBox->setToolTip(toolTip); - m_debugCheckBox->setToolTip(toolTip); - m_analyzeCheckBox->setToolTip(toolTip); - m_toolsCheckBox->setToolTip(toolTip); - - verticalLayout->addWidget(m_buildCheckBox); - verticalLayout->addWidget(m_debugCheckBox); - verticalLayout->addWidget(m_analyzeCheckBox); - verticalLayout->addWidget(m_toolsCheckBox); - - verticalLayout->addSpacerItem( - new QSpacerItem(10, 10, QSizePolicy::Expanding, QSizePolicy::Minimum)); - - m_buildCheckBox->setChecked(hideBuildMenuSetting()); - m_debugCheckBox->setChecked(hideDebugMenuSetting()); - m_analyzeCheckBox->setChecked(hideAnalyzeMenuSetting()); - m_toolsCheckBox->setChecked(hideToolsMenuSetting()); - - // Examples path setting - auto examplesGroupBox = new QGroupBox(tr("Examples")); - boxLayout->addWidget(examplesGroupBox); - - auto examplesLayout = new QHBoxLayout(this); - examplesGroupBox->setLayout(examplesLayout); - - auto examplesLabel = new QLabel(tr("Examples path:")); - m_pathChooserExamples->setFilePath( - Utils::FilePath::fromString(QmlDesignerBasePlugin::examplesPathSetting())); - auto examplesResetButton = new QPushButton(tr("Reset Path")); - - connect(examplesResetButton, &QPushButton::clicked, this, [this]() { - m_pathChooserExamples->setFilePath(QmlDesignerBasePlugin::defaultExamplesPath()); - }); - - examplesLayout->addWidget(examplesLabel); - examplesLayout->addWidget(m_pathChooserExamples); - examplesLayout->addWidget(examplesResetButton); - - // Bundles path setting - auto bundlesGroupBox = new QGroupBox(tr("Bundles")); - boxLayout->addWidget(bundlesGroupBox); - - auto bundlesLayout = new QHBoxLayout(this); - bundlesGroupBox->setLayout(bundlesLayout); - - QLabel *bundlesLabel = new QLabel(tr("Bundles path:")); - m_pathChooserBundles->setFilePath( - Utils::FilePath::fromString(QmlDesignerBasePlugin::bundlesPathSetting())); - QPushButton *bundlesResetButton = new QPushButton(tr("Reset Path")); - - connect(bundlesResetButton, &QPushButton::clicked, this, [this]() { - m_pathChooserBundles->setFilePath(QmlDesignerBasePlugin::defaultBundlesPath()); - }); - - bundlesLayout->addWidget(bundlesLabel); - bundlesLayout->addWidget(m_pathChooserBundles); - bundlesLayout->addWidget(bundlesResetButton); - - boxLayout->addSpacerItem(new QSpacerItem(10, 10, QSizePolicy::Expanding, QSizePolicy::Expanding)); -} - -void StudioSettingsPage::apply() -{ - bool dirty = false; - - setSettingIfDifferent(ProjectExplorer::Constants::SETTINGS_MENU_HIDE_BUILD, - m_buildCheckBox->isChecked(), - dirty); - - setSettingIfDifferent(ProjectExplorer::Constants::SETTINGS_MENU_HIDE_DEBUG, - m_debugCheckBox->isChecked(), - dirty); - - setSettingIfDifferent(ProjectExplorer::Constants::SETTINGS_MENU_HIDE_ANALYZE, - m_analyzeCheckBox->isChecked(), - dirty); - - setSettingIfDifferent(Core::Constants::SETTINGS_MENU_HIDE_TOOLS, - m_toolsCheckBox->isChecked(), - dirty); - - if (dirty) { - const QString restartText = tr("The menu visibility change will take effect after restart."); - Core::RestartDialog restartDialog(Core::ICore::dialogParent(), restartText); - restartDialog.exec(); - } - - QSettings *s = Core::ICore::settings(); - const QString value = m_pathChooserExamples->filePath().toString(); - - if (s->value(EXAMPLES_DOWNLOAD_PATH, false).toString() != value) { - s->setValue(EXAMPLES_DOWNLOAD_PATH, value); - emit global->examplesDownloadPathChanged(value); - } - - const QString bundlesPath = m_pathChooserBundles->filePath().toString(); - - if (s->value(BUNDLES_DOWNLOAD_PATH).toString() != bundlesPath) { - s->setValue(BUNDLES_DOWNLOAD_PATH, bundlesPath); - emit global->bundlesDownloadPathChanged(bundlesPath); - - const QString restartText = tr("Changing bundle path will take effect after restart."); - Core::RestartDialog restartDialog(Core::ICore::dialogParent(), restartText); - restartDialog.exec(); - } -} - -StudioConfigSettingsPage::StudioConfigSettingsPage() -{ - setId("Z.StudioConfig.Settings"); - setDisplayName(tr("Qt Design Studio Configuration")); - setCategory(Core::Constants::SETTINGS_CATEGORY_CORE); - setWidgetCreator([] { return new StudioSettingsPage; }); -} - } // namespace QmlDesigner diff --git a/src/plugins/qmldesignerbase/qmldesignerbaseplugin.h b/src/plugins/qmldesignerbase/qmldesignerbaseplugin.h index 92e5ffc71bc..1418a6421b5 100644 --- a/src/plugins/qmldesignerbase/qmldesignerbaseplugin.h +++ b/src/plugins/qmldesignerbase/qmldesignerbaseplugin.h @@ -5,41 +5,16 @@ #include "qmldesignerbase_global.h" -#include #include -#include #include -QT_FORWARD_DECLARE_CLASS(QCheckBox) +QT_BEGIN_NAMESPACE +class QStyle; +QT_END_NAMESPACE namespace QmlDesigner { -class StudioSettingsPage : public Core::IOptionsPageWidget -{ - Q_OBJECT - -public: - void apply() final; - - StudioSettingsPage(); - -private: - QCheckBox *m_buildCheckBox; - QCheckBox *m_debugCheckBox; - QCheckBox *m_analyzeCheckBox; - QCheckBox *m_toolsCheckBox; - Utils::PathChooser *m_pathChooserExamples; - Utils::PathChooser *m_pathChooserBundles; -}; - -class QMLDESIGNERBASE_EXPORT StudioConfigSettingsPage : public Core::IOptionsPage -{ - Q_OBJECT - -public: - StudioConfigSettingsPage(); -}; class QMLDESIGNERBASE_EXPORT QmlDesignerBasePlugin final : public ExtensionSystem::IPlugin { @@ -50,19 +25,9 @@ public: QmlDesignerBasePlugin(); ~QmlDesignerBasePlugin(); - static QmlDesignerBasePlugin *instance(); - - static Utils::FilePath defaultExamplesPath(); - static Utils::FilePath defaultBundlesPath(); - static QString examplesPathSetting(); - static QString bundlesPathSetting(); - static class DesignerSettings &settings(); static QStyle *style(); - -signals: - void examplesDownloadPathChanged(const QString &path); - void bundlesDownloadPathChanged(const QString &path); + static class StudioConfigSettingsPage *studioConfigSettingsPage(); private: bool initialize(const QStringList &arguments, QString *errorMessage) override; diff --git a/src/plugins/qmldesignerbase/utils/studioquickwidget.cpp b/src/plugins/qmldesignerbase/studio/studioquickwidget.cpp similarity index 100% rename from src/plugins/qmldesignerbase/utils/studioquickwidget.cpp rename to src/plugins/qmldesignerbase/studio/studioquickwidget.cpp diff --git a/src/plugins/qmldesignerbase/utils/studioquickwidget.h b/src/plugins/qmldesignerbase/studio/studioquickwidget.h similarity index 100% rename from src/plugins/qmldesignerbase/utils/studioquickwidget.h rename to src/plugins/qmldesignerbase/studio/studioquickwidget.h diff --git a/src/plugins/qmldesignerbase/studio/studiosettingspage.cpp b/src/plugins/qmldesignerbase/studio/studiosettingspage.cpp new file mode 100644 index 00000000000..affe25e7da2 --- /dev/null +++ b/src/plugins/qmldesignerbase/studio/studiosettingspage.cpp @@ -0,0 +1,217 @@ +// Copyright (C) 2023 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 "studiosettingspage.h" + +#include "../utils/designerpaths.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace QmlDesigner { + +namespace { + +bool hideBuildMenuSetting() +{ + return Core::ICore::settings() + ->value(ProjectExplorer::Constants::SETTINGS_MENU_HIDE_BUILD, false) + .toBool(); +} + +bool hideDebugMenuSetting() +{ + return Core::ICore::settings() + ->value(ProjectExplorer::Constants::SETTINGS_MENU_HIDE_DEBUG, false) + .toBool(); +} + +bool hideAnalyzeMenuSetting() +{ + return Core::ICore::settings() + ->value(ProjectExplorer::Constants::SETTINGS_MENU_HIDE_ANALYZE, false) + .toBool(); +} + +bool hideToolsMenuSetting() +{ + return Core::ICore::settings()->value(Core::Constants::SETTINGS_MENU_HIDE_TOOLS, false).toBool(); +} + +void setSettingIfDifferent(const QString &key, bool value, bool &dirty) +{ + QSettings *s = Core::ICore::settings(); + if (s->value(key, false).toBool() != value) { + dirty = true; + s->setValue(key, value); + } +} + +} // namespace + +StudioSettingsPage::StudioSettingsPage() + : m_buildCheckBox(new QCheckBox(tr("Build"))) + , m_debugCheckBox(new QCheckBox(tr("Debug"))) + , m_analyzeCheckBox(new QCheckBox(tr("Analyze"))) + , m_toolsCheckBox(new QCheckBox(tr("Tools"))) + , m_pathChooserExamples(new Utils::PathChooser()) + , m_pathChooserBundles(new Utils::PathChooser()) +{ + const QString toolTip = tr( + "Hide top-level menus with advanced functionality to simplify the UI. Build is " + "generally not required in the context of Qt Design Studio. Debug and " + "Analyze " + "are only required for debugging and profiling. Tools can be useful for bookmarks " + "and git integration."); + + QVBoxLayout *boxLayout = new QVBoxLayout(); + setLayout(boxLayout); + auto groupBox = new QGroupBox(tr("Hide Menu")); + groupBox->setToolTip(toolTip); + boxLayout->addWidget(groupBox); + + auto verticalLayout = new QVBoxLayout(); + groupBox->setLayout(verticalLayout); + + m_buildCheckBox->setToolTip(toolTip); + m_debugCheckBox->setToolTip(toolTip); + m_analyzeCheckBox->setToolTip(toolTip); + m_toolsCheckBox->setToolTip(toolTip); + + verticalLayout->addWidget(m_buildCheckBox); + verticalLayout->addWidget(m_debugCheckBox); + verticalLayout->addWidget(m_analyzeCheckBox); + verticalLayout->addWidget(m_toolsCheckBox); + + verticalLayout->addSpacerItem( + new QSpacerItem(10, 10, QSizePolicy::Expanding, QSizePolicy::Minimum)); + + m_buildCheckBox->setChecked(hideBuildMenuSetting()); + m_debugCheckBox->setChecked(hideDebugMenuSetting()); + m_analyzeCheckBox->setChecked(hideAnalyzeMenuSetting()); + m_toolsCheckBox->setChecked(hideToolsMenuSetting()); + + // Examples path setting + auto examplesGroupBox = new QGroupBox(tr("Examples")); + boxLayout->addWidget(examplesGroupBox); + + auto examplesLayout = new QHBoxLayout(this); + examplesGroupBox->setLayout(examplesLayout); + + auto examplesLabel = new QLabel(tr("Examples path:")); + m_pathChooserExamples->setFilePath(Utils::FilePath::fromString(Paths::examplesPathSetting())); + auto examplesResetButton = new QPushButton(tr("Reset Path")); + + connect(examplesResetButton, &QPushButton::clicked, this, [this]() { + m_pathChooserExamples->setFilePath(Paths::defaultExamplesPath()); + }); + + examplesLayout->addWidget(examplesLabel); + examplesLayout->addWidget(m_pathChooserExamples); + examplesLayout->addWidget(examplesResetButton); + + // Bundles path setting + auto bundlesGroupBox = new QGroupBox(tr("Bundles")); + boxLayout->addWidget(bundlesGroupBox); + + auto bundlesLayout = new QHBoxLayout(this); + bundlesGroupBox->setLayout(bundlesLayout); + + QLabel *bundlesLabel = new QLabel(tr("Bundles path:")); + m_pathChooserBundles->setFilePath(Utils::FilePath::fromString(Paths::bundlesPathSetting())); + QPushButton *bundlesResetButton = new QPushButton(tr("Reset Path")); + + connect(bundlesResetButton, &QPushButton::clicked, this, [this]() { + m_pathChooserBundles->setFilePath(Paths::defaultBundlesPath()); + }); + + bundlesLayout->addWidget(bundlesLabel); + bundlesLayout->addWidget(m_pathChooserBundles); + bundlesLayout->addWidget(bundlesResetButton); + + boxLayout->addSpacerItem( + new QSpacerItem(10, 10, QSizePolicy::Expanding, QSizePolicy::Expanding)); +} + +void StudioSettingsPage::apply() +{ + bool dirty = false; + + setSettingIfDifferent(ProjectExplorer::Constants::SETTINGS_MENU_HIDE_BUILD, + m_buildCheckBox->isChecked(), + dirty); + + setSettingIfDifferent(ProjectExplorer::Constants::SETTINGS_MENU_HIDE_DEBUG, + m_debugCheckBox->isChecked(), + dirty); + + setSettingIfDifferent(ProjectExplorer::Constants::SETTINGS_MENU_HIDE_ANALYZE, + m_analyzeCheckBox->isChecked(), + dirty); + + setSettingIfDifferent(Core::Constants::SETTINGS_MENU_HIDE_TOOLS, + m_toolsCheckBox->isChecked(), + dirty); + + if (dirty) { + const QString restartText = tr( + "The menu visibility change will take effect after restart."); + Core::RestartDialog restartDialog(Core::ICore::dialogParent(), restartText); + restartDialog.exec(); + } + + QSettings *s = Core::ICore::settings(); + const QString value = m_pathChooserExamples->filePath().toString(); + + if (s->value(Paths::exampleDownloadPath, false).toString() != value) { + s->setValue(Paths::exampleDownloadPath, value); + emit examplesDownloadPathChanged(value); + } + + const QString bundlesPath = m_pathChooserBundles->filePath().toString(); + + if (s->value(Paths::bundlesDownloadPath).toString() != bundlesPath) { + s->setValue(Paths::bundlesDownloadPath, bundlesPath); + emit bundlesDownloadPathChanged(bundlesPath); + + const QString restartText = tr("Changing bundle path will take effect after restart."); + Core::RestartDialog restartDialog(Core::ICore::dialogParent(), restartText); + restartDialog.exec(); + } +} + +StudioConfigSettingsPage::StudioConfigSettingsPage() +{ + setId("Z.StudioConfig.Settings"); + setDisplayName(tr("Qt Design Studio Configuration")); + setCategory(Core::Constants::SETTINGS_CATEGORY_CORE); + setWidgetCreator([&] { + auto page = new StudioSettingsPage; + connect(page, + &StudioSettingsPage::examplesDownloadPathChanged, + this, + &StudioConfigSettingsPage::examplesDownloadPathChanged); + connect(page, + &StudioSettingsPage::bundlesDownloadPathChanged, + this, + &StudioConfigSettingsPage::bundlesDownloadPathChanged); + return page; + }); +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesignerbase/studio/studiosettingspage.h b/src/plugins/qmldesignerbase/studio/studiosettingspage.h new file mode 100644 index 00000000000..0d0149f1167 --- /dev/null +++ b/src/plugins/qmldesignerbase/studio/studiosettingspage.h @@ -0,0 +1,49 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0 + +#pragma once + +#include "../qmldesignerbase_global.h" + +#include +#include + +QT_FORWARD_DECLARE_CLASS(QCheckBox) + +namespace QmlDesigner { + +class StudioSettingsPage : public Core::IOptionsPageWidget +{ + Q_OBJECT + +public: + void apply() final; + + StudioSettingsPage(); + +signals: + void examplesDownloadPathChanged(const QString &path); + void bundlesDownloadPathChanged(const QString &path); + +private: + QCheckBox *m_buildCheckBox; + QCheckBox *m_debugCheckBox; + QCheckBox *m_analyzeCheckBox; + QCheckBox *m_toolsCheckBox; + Utils::PathChooser *m_pathChooserExamples; + Utils::PathChooser *m_pathChooserBundles; +}; + +class QMLDESIGNERBASE_EXPORT StudioConfigSettingsPage : public Core::IOptionsPage +{ + Q_OBJECT + +public: + StudioConfigSettingsPage(); + +signals: + void examplesDownloadPathChanged(const QString &path); + void bundlesDownloadPathChanged(const QString &path); +}; + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesignerbase/utils/studiostyle.cpp b/src/plugins/qmldesignerbase/studio/studiostyle.cpp similarity index 100% rename from src/plugins/qmldesignerbase/utils/studiostyle.cpp rename to src/plugins/qmldesignerbase/studio/studiostyle.cpp diff --git a/src/plugins/qmldesignerbase/utils/studiostyle.h b/src/plugins/qmldesignerbase/studio/studiostyle.h similarity index 100% rename from src/plugins/qmldesignerbase/utils/studiostyle.h rename to src/plugins/qmldesignerbase/studio/studiostyle.h diff --git a/src/plugins/qmldesignerbase/utils/designerpaths.cpp b/src/plugins/qmldesignerbase/utils/designerpaths.cpp new file mode 100644 index 00000000000..b4ad03ba716 --- /dev/null +++ b/src/plugins/qmldesignerbase/utils/designerpaths.cpp @@ -0,0 +1,47 @@ +// Copyright (C) 2023 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 "designerpaths.h" + +#include +#include + +#include + +namespace QmlDesigner::Paths { + +Utils::FilePath defaultExamplesPath() +{ + QStandardPaths::StandardLocation location = Utils::HostOsInfo::isMacHost() + ? QStandardPaths::HomeLocation + : QStandardPaths::DocumentsLocation; + + return Utils::FilePath::fromString(QStandardPaths::writableLocation(location)) + .pathAppended("QtDesignStudio/examples"); +} + +Utils::FilePath defaultBundlesPath() +{ + QStandardPaths::StandardLocation location = Utils::HostOsInfo::isMacHost() + ? QStandardPaths::HomeLocation + : QStandardPaths::DocumentsLocation; + + return Utils::FilePath::fromString(QStandardPaths::writableLocation(location)) + .pathAppended("QtDesignStudio/bundles"); +} + +QString examplesPathSetting() +{ + return Core::ICore::settings() + ->value(exampleDownloadPath, defaultExamplesPath().toString()) + .toString(); +} + +QString bundlesPathSetting() +{ + return Core::ICore::settings() + ->value(bundlesDownloadPath, defaultBundlesPath().toString()) + .toString(); +} + +} // namespace QmlDesigner::Paths diff --git a/src/plugins/qmldesignerbase/utils/designerpaths.h b/src/plugins/qmldesignerbase/utils/designerpaths.h new file mode 100644 index 00000000000..df53997457f --- /dev/null +++ b/src/plugins/qmldesignerbase/utils/designerpaths.h @@ -0,0 +1,20 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0 + +#pragma once + +#include "../qmldesignerbase_global.h" + +#include + +namespace QmlDesigner::Paths { + +inline constexpr QStringView exampleDownloadPath = u"StudioConfig/ExamplesDownloadPath"; +inline constexpr QStringView bundlesDownloadPath = u"StudioConfig/BundlesDownloadPath"; + +QMLDESIGNERBASE_EXPORT Utils::FilePath defaultExamplesPath(); +QMLDESIGNERBASE_EXPORT Utils::FilePath defaultBundlesPath(); +QMLDESIGNERBASE_EXPORT QString examplesPathSetting(); +QMLDESIGNERBASE_EXPORT QString bundlesPathSetting(); + +} // namespace QmlDesigner::Paths diff --git a/src/plugins/studiowelcome/examplecheckout.cpp b/src/plugins/studiowelcome/examplecheckout.cpp index a0e5d07bab6..61a8e429d68 100644 --- a/src/plugins/studiowelcome/examplecheckout.cpp +++ b/src/plugins/studiowelcome/examplecheckout.cpp @@ -7,6 +7,8 @@ #include #include +#include +#include #include #include @@ -61,7 +63,7 @@ bool DataModelDownloader::downloadEnabled() const QString DataModelDownloader::targetPath() const { - return QmlDesigner::QmlDesignerBasePlugin::examplesPathSetting(); + return QmlDesigner::Paths::examplesPathSetting(); } static Utils::FilePath tempFilePath() @@ -105,8 +107,8 @@ DataModelDownloader::DataModelDownloader(QObject * /* parent */) auto studioWelcomePlugin = qobject_cast(plugin); if (studioWelcomePlugin) { - QObject::connect(QmlDesigner::QmlDesignerBasePlugin::instance(), - &QmlDesigner::QmlDesignerBasePlugin::examplesDownloadPathChanged, + QObject::connect(QmlDesigner::QmlDesignerBasePlugin::studioConfigSettingsPage(), + &QmlDesigner::StudioConfigSettingsPage::examplesDownloadPathChanged, this, &DataModelDownloader::targetPathMustChange); } From df56f95128c03a35a02b06144a6a8e3679e5cc8f Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Wed, 19 Apr 2023 16:31:13 +0200 Subject: [PATCH 056/192] QmlDesigner: Fix build for Qt < 6.5 Change-Id: I943b42e3536a2705dfcee5620e84ea5ddf4fc828 Reviewed-by: Tim Jenssen Reviewed-by: Qt CI Bot Reviewed-by: Thomas Hartmann --- .../qmldesigner/designercore/projectstorage/modulescanner.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/qmldesigner/designercore/projectstorage/modulescanner.cpp b/src/plugins/qmldesigner/designercore/projectstorage/modulescanner.cpp index f2c248182df..f6c0f2449c1 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/modulescanner.cpp +++ b/src/plugins/qmldesigner/designercore/projectstorage/modulescanner.cpp @@ -15,6 +15,7 @@ namespace QmlDesigner { namespace { +#ifdef QDS_HAS_QMLPRIVATE std::optional contentAsQString(const QString &filePath) { QFile file{filePath}; @@ -24,7 +25,6 @@ std::optional contentAsQString(const QString &filePath) return {}; } -#ifdef QDS_HAS_QMLPRIVATE QString createVersion(const QMultiHash &components) { auto found = std::max_element(components.begin(), components.end(), [](auto &&first, auto &&second) { @@ -46,7 +46,7 @@ void ModuleScanner::scan(const QStringList &modulePaths) scan(modulePath.toStdString()); } -void ModuleScanner::scan(std::string_view modulePath) +void ModuleScanner::scan([[maybe_unused]] std::string_view modulePath) { #ifdef QDS_HAS_QMLPRIVATE try { From 111e71238cfc704a3b0ad3d67457b684c3990446 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Wed, 19 Apr 2023 17:39:41 +0200 Subject: [PATCH 057/192] QmlDesigner: Fix warning Change-Id: I91cc82b907c3ddfaf194094217a66379c3c41eb8 Reviewed-by: Tim Jenssen --- src/plugins/qmldesigner/qmldesignerprojectmanager.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp b/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp index 67144a813b4..8f680ac3544 100644 --- a/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp +++ b/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp @@ -342,6 +342,7 @@ void projectQmldirPaths(::ProjectExplorer::Target *target, QStringList &qmldirPa qmldirPaths.push_back(QDir::cleanPath(pojectDirectory.absoluteFilePath(importPath)) + "/qmldir"); } + #ifdef QDS_HAS_QMLPRIVATE bool skipPath(const std::filesystem::path &path) { @@ -359,10 +360,10 @@ bool skipPath(const std::filesystem::path &path) } #endif -void qtQmldirPaths(::ProjectExplorer::Target *target, QStringList &qmldirPaths) +void qtQmldirPaths([[maybe_unused]] ::ProjectExplorer::Target *target, + [[maybe_unused]] QStringList &qmldirPaths) { #ifdef QDS_HAS_QMLPRIVATE - if (useProjectStorage()) { const QString installDirectory = qmlPath(target).toString(); From ccf64cf6424bb1106d4d6894a086959d33608ca5 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Wed, 19 Apr 2023 09:52:59 +0200 Subject: [PATCH 058/192] QmlDesigner: Disbable maybe-uninitialized warning Change-Id: I683bacc47b6f98a7adb64c3391c3df65c42fe945 Reviewed-by: Reviewed-by: Tim Jenssen Reviewed-by: Qt CI Bot --- src/libs/sqlite/sqliteids.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/libs/sqlite/sqliteids.h b/src/libs/sqlite/sqliteids.h index febc094242c..a0562b4df44 100644 --- a/src/libs/sqlite/sqliteids.h +++ b/src/libs/sqlite/sqliteids.h @@ -51,10 +51,17 @@ public: return first.id >= second.id; } +#if defined(__GNUC__) && !defined(__clang__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wmaybe-uninitialized" +#endif constexpr friend InternalIntegerType operator-(BasicId first, BasicId second) { return first.id - second.id; } +#if defined(__GNUC__) && !defined(__clang__) +#pragma GCC diagnostic pop +#endif constexpr bool isValid() const { return id >= 0; } From 4393df48be6ba2c2f2ad43982256f100a6e2d978 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Wed, 19 Apr 2023 17:08:03 +0200 Subject: [PATCH 059/192] QmlDesigner: Reduce version demands We now expect Qt 6.4.3 and MacOS 10.15 or higher. Change-Id: I339e3ca7f668d9451859c51472712c831a6caf8c Reviewed-by: Marco Bubke Reviewed-by: Qt CI Bot --- src/plugins/qmldesigner/CMakeLists.txt | 3 ++- .../qmldesigner/designercore/projectstorage/qmltypesparser.cpp | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index 59a517a529d..bac868378ee 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -90,7 +90,8 @@ extend_qtc_library(QmlDesignerCore ) extend_qtc_library(QmlDesignerCore - CONDITION TARGET Qt6::QmlDomPrivate AND TARGET Qt6::QmlCompilerPrivate AND Qt6_VERSION VERSION_GREATER_EQUAL 6.5.0 + CONDITION TARGET Qt6::QmlDomPrivate AND TARGET Qt6::QmlCompilerPrivate AND Qt6_VERSION VERSION_GREATER_EQUAL 6.4.3 + AND NOT (CMAKE_HOST_SYSTEM_NAME STREQUAL "Darwin" AND CMAKE_SYSTEM_VERSION VERSION_LESS 19) DEPENDS Qt6::QmlDomPrivate Qt6::QmlCompilerPrivate PUBLIC_DEFINES QDS_HAS_QMLPRIVATE diff --git a/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.cpp b/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.cpp index 3ed15639982..ac1761318d0 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.cpp +++ b/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.cpp @@ -20,7 +20,7 @@ namespace QmlDesigner { -#ifdef QDS_HAS_QMLPRIVATE +#if defined(QDS_HAS_QMLPRIVATE) && QT_VERSION >= QT_VERSION_CHECK(6, 5, 0) namespace QmlDom = QQmlJS::Dom; From 2694d6a1f576e2731b5b9f5d11d4fc2c567cba2b Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Wed, 19 Apr 2023 10:11:38 +0200 Subject: [PATCH 060/192] Add feature to disable QmlDesigner and related files QTC_WITH_QMLDESIGNER or WITH_QMLDESIGNER has to be set to OFF to disable QmlDesigner related code. Change-Id: I7e25200fe856fcc7de3493cfa394cdd4f923e0bf Reviewed-by: Cristian Adam Reviewed-by: Qt CI Bot --- CMakeLists.txt | 3 + CMakePresets.json | 3 +- src/libs/CMakeLists.txt | 6 +- src/plugins/CMakeLists.txt | 27 ++-- src/plugins/insight/insightwidget.cpp | 2 +- src/plugins/qmldesigner/CMakeLists.txt | 216 +++++++++++++------------ src/tools/CMakeLists.txt | 4 +- tests/CMakeLists.txt | 4 +- 8 files changed, 146 insertions(+), 119 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d70dab026ce..09dc721201a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,6 +6,7 @@ include(Utils) env_with_default("QTC_BUILD_WITH_PCH" ENV_QTC_BUILD_WITH_PCH ON) env_with_default("QTC_WITH_TESTS" ENV_QTC_WITH_TESTS OFF) +env_with_default("QTC_WITH_QMLDESIGNER" ENV_QTC_WITH_QMLDESIGNER ON) option(BUILD_WITH_PCH "Build with precompiled headers" "${ENV_QTC_BUILD_WITH_PCH}") @@ -35,6 +36,8 @@ qtc_link_with_qt() option(WITH_TESTS "Build Tests" ${ENV_QTC_WITH_TESTS}) add_feature_info("Build tests" ${WITH_TESTS} "") +option(WITH_QMLDESIGNER "Build QmlDesigner" ${ENV_QTC_WITH_QMLDESIGNER}) +add_feature_info("Build QmlDesigner and related code" ${WITH_QMLDESIGNER} "") option(WITH_DEBUG_CMAKE "Enabled CMake project debugging functionality" OFF) option(SHOW_BUILD_DATE "Show build date in about dialog" OFF) option(WITH_SANITIZE "Build with sanitizer enabled" OFF) diff --git a/CMakePresets.json b/CMakePresets.json index 2886bc7c125..b4b859933cd 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -7,7 +7,8 @@ "generator": "Ninja", "cacheVariables": { "BUILD_PLUGINS": "Core;Designer;DiffEditor;TextEditor;ProjectExplorer;CppEditor;CodePaster;Docker;Git;Help;QmakeProjectManager;CMakeProjectManager;ClangCodeModel;ClangTools;ClangFormat;Debugger;QtSupport;ResourceEditor;VcsBase;Welcome;LanguageClient;RemoteLinux", - "BUILD_EXECUTABLES": "QtCreator;qtcreator_ctrlc_stub;qtcreator_process_stub;win64interrupt;qtcreator_processlauncher" + "BUILD_EXECUTABLES": "QtCreator;qtcreator_ctrlc_stub;qtcreator_process_stub;win64interrupt;qtcreator_processlauncher", + "WITH_QMLDESIGNER": "OFF" } } ] diff --git a/src/libs/CMakeLists.txt b/src/libs/CMakeLists.txt index fc6b53d81c4..b5410655f20 100644 --- a/src/libs/CMakeLists.txt +++ b/src/libs/CMakeLists.txt @@ -13,9 +13,11 @@ add_subdirectory(qmldebug) add_subdirectory(qmleditorwidgets) add_subdirectory(glsl) add_subdirectory(languageserverprotocol) -add_subdirectory(sqlite) +if (WITH_QMLDESIGNER) + add_subdirectory(sqlite) + add_subdirectory(qmlpuppetcommunication) +endif() add_subdirectory(tracing) -add_subdirectory(qmlpuppetcommunication) add_subdirectory(qtcreatorcdbext) diff --git a/src/plugins/CMakeLists.txt b/src/plugins/CMakeLists.txt index 950d1d8e95e..0ec8eb2de76 100644 --- a/src/plugins/CMakeLists.txt +++ b/src/plugins/CMakeLists.txt @@ -57,15 +57,18 @@ add_subdirectory(scxmleditor) add_subdirectory(subversion) add_subdirectory(compilationdatabaseprojectmanager) add_subdirectory(languageclient) -add_subdirectory(qmldesignerbase) - +if (WITH_QMLDESIGNER) + add_subdirectory(qmldesignerbase) +endif() # Level 6: add_subdirectory(cmakeprojectmanager) add_subdirectory(debugger) add_subdirectory(coco) add_subdirectory(gitlab) -add_subdirectory(qmlprojectmanager) +if (WITH_QMLDESIGNER) + add_subdirectory(qmlprojectmanager) +endif() # Level 7: add_subdirectory(android) @@ -90,15 +93,17 @@ add_subdirectory(squish) # Level 8: add_subdirectory(boot2qt) -unset(qmldesigner_builddir) -if (WIN32 AND CMAKE_CXX_COMPILER_ID MATCHES "Clang") - # Workaround for @CMakeFiles\QmlDesigner.rsp ld.lld.exe: The filename or extension is too long. - # Clang on Windows is having problems with QmlDesigner.rsp which is bigger than 32KiB - set(qmldesigner_builddir ${PROJECT_BINARY_DIR}/qmldsgnr) +if (WITH_QMLDESIGNER) + unset(qmldesigner_builddir) + if (WIN32 AND CMAKE_CXX_COMPILER_ID MATCHES "Clang") + # Workaround for @CMakeFiles\QmlDesigner.rsp ld.lld.exe: The filename or extension is too long. + # Clang on Windows is having problems with QmlDesigner.rsp which is bigger than 32KiB + set(qmldesigner_builddir ${PROJECT_BINARY_DIR}/qmldsgnr) + endif() + add_subdirectory(qmldesigner ${qmldesigner_builddir}) + add_subdirectory(studiowelcome) + add_subdirectory(insight) endif() -add_subdirectory(qmldesigner ${qmldesigner_builddir}) -add_subdirectory(studiowelcome) -add_subdirectory(insight) add_subdirectory(qnx) add_subdirectory(webassembly) add_subdirectory(mcusupport) diff --git a/src/plugins/insight/insightwidget.cpp b/src/plugins/insight/insightwidget.cpp index 81286f87eb2..aaf152bea31 100644 --- a/src/plugins/insight/insightwidget.cpp +++ b/src/plugins/insight/insightwidget.cpp @@ -5,8 +5,8 @@ #include "insightmodel.h" #include "insightview.h" +#include #include -#include #include #include diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index bac868378ee..c8dc6675735 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -1,16 +1,5 @@ #only if the plugin is requested by qtc_plugin_enabled continue if not stop as early as possible -add_qtc_plugin(QmlDesigner - PLUGIN_RECOMMENDS QmlPreview - CONDITION Qt6_VERSION VERSION_GREATER_EQUAL 6.2.0 AND TARGET Qt::QuickWidgets AND TARGET Qt::Svg - PROPERTIES COMPILE_WARNING_AS_ERROR ON - PLUGIN_DEPENDS - Core ProjectExplorer QmlDesignerBase QmlJSEditor QmakeProjectManager QmlProjectManager - QtSupport -) -qtc_plugin_enabled(_qmlDesignerEnabled QmlDesigner) -if (NOT _qmlDesignerEnabled) - return() -endif() + find_package(Qt6 COMPONENTS QmlDomPrivate QmlCompilerPrivate) @@ -19,7 +8,6 @@ if (APPLE) set(QmlDesignerPluginInstallPrefix "${IDE_PLUGIN_PATH}/QmlDesigner") endif() - env_with_default("QDS_USE_PROJECTSTORAGE" ENV_QDS_USE_PROJECTSTORAGE OFF) option(USE_PROJECTSTORAGE "Use ProjectStorage" ${ENV_QDS_USE_PROJECTSTORAGE}) add_feature_info("ProjectStorage" ${USE_PROJECTSTORAGE} "") @@ -434,15 +422,17 @@ extend_qtc_library(QmlDesignerCore file(GLOB PROJECTSTORAGE_EXCLUDED_SOURCES designercore/projectstorage/*.cpp) set_property(SOURCE ${PROJECTSTORAGE_EXCLUDED_SOURCES} PROPERTY SKIP_AUTOMOC ON) -extend_qtc_plugin(QmlDesigner - PUBLIC_DEPENDS - QmlDesignerUtils - QmlDesignerBase - QmlPuppetCommunication +add_qtc_plugin(QmlDesigner + PLUGIN_RECOMMENDS QmlPreview + CONDITION Qt6_VERSION VERSION_GREATER_EQUAL 6.2.0 AND TARGET Qt::QuickWidgets AND TARGET Qt::Svg + PLUGIN_DEPENDS + Core ProjectExplorer QmlDesignerBase QmlJSEditor QmakeProjectManager QmlProjectManager + QtSupport DEPENDS - QmlDesignerCore - QmlJS LanguageUtils QmlEditorWidgets AdvancedDockingSystem Sqlite - Qt::QuickWidgets Qt::CorePrivate Qt::Xml Qt::Svg + QmlJS LanguageUtils QmlEditorWidgets AdvancedDockingSystem + Qt::QuickWidgets Qt::CorePrivate Qt::Xml Qt::Svg QmlDesignerCore Sqlite + PUBLIC_DEPENDS + QmlDesignerUtils QmlPuppetCommunication QmlDesignerBase DEFINES IDE_LIBRARY_BASENAME=\"${IDE_LIBRARY_BASE_PATH}\" SHARE_QML_PATH="${CMAKE_CURRENT_SOURCE_DIR}/../../../share/qtcreator/qmldesigner" @@ -548,87 +538,6 @@ if (QTC_STATIC_BUILD AND TARGET QmlDesigner) extend_qtc_target(QmlDesigner PUBLIC_DEPENDS TextEditor) endif() -add_qtc_plugin(assetexporterplugin - PLUGIN_CLASS AssetExporterPlugin - CONDITION TARGET QmlDesigner - DEPENDS Core ProjectExplorer QmlDesigner Utils Qt::Qml Qt::QuickPrivate - PUBLIC_INCLUDES assetexporterplugin - SOURCES - assetexporterplugin/assetexportdialog.h assetexporterplugin/assetexportdialog.cpp assetexporterplugin/assetexportdialog.ui - assetexporterplugin/assetexporter.h assetexporterplugin/assetexporter.cpp - assetexporterplugin/assetexporterplugin.h assetexporterplugin/assetexporterplugin.cpp - assetexporterplugin/assetexporterview.h assetexporterplugin/assetexporterview.cpp - assetexporterplugin/assetexportpluginconstants.h - assetexporterplugin/componentexporter.h assetexporterplugin/componentexporter.cpp - assetexporterplugin/exportnotification.h assetexporterplugin/exportnotification.cpp - assetexporterplugin/filepathmodel.h assetexporterplugin/filepathmodel.cpp - assetexporterplugin/dumpers/assetnodedumper.h assetexporterplugin/dumpers/assetnodedumper.cpp - assetexporterplugin/dumpers/itemnodedumper.h assetexporterplugin/dumpers/itemnodedumper.cpp - assetexporterplugin/dumpers/nodedumper.h assetexporterplugin/dumpers/nodedumper.cpp - assetexporterplugin/dumpers/textnodedumper.h assetexporterplugin/dumpers/textnodedumper.cpp - assetexporterplugin/assetexporterplugin.qrc - PLUGIN_PATH ${QmlDesignerPluginInstallPrefix} -) - -extend_qtc_plugin(assetexporterplugin - CONDITION NOT DISABLE_COMPILE_WARNING_AS_ERROR - PROPERTIES COMPILE_WARNING_AS_ERROR ON -) - -add_qtc_plugin(componentsplugin - PLUGIN_CLASS ComponentsPlugin - CONDITION TARGET QmlDesigner - DEPENDS Core QmlDesigner Utils Qt::Qml - DEFINES COMPONENTS_LIBRARY - SOURCES - componentsplugin/addtabdesigneraction.cpp componentsplugin/addtabdesigneraction.h - componentsplugin/addtabtotabviewdialog.cpp componentsplugin/addtabtotabviewdialog.h - componentsplugin/addtabtotabviewdialog.ui - componentsplugin/componentsplugin.cpp componentsplugin/componentsplugin.h - componentsplugin/componentsplugin.qrc - componentsplugin/entertabdesigneraction.cpp componentsplugin/entertabdesigneraction.h - componentsplugin/tabviewindexmodel.cpp componentsplugin/tabviewindexmodel.h - PLUGIN_PATH ${QmlDesignerPluginInstallPrefix} -) - -extend_qtc_plugin(componentsplugin - CONDITION NOT DISABLE_COMPILE_WARNING_AS_ERROR - PROPERTIES COMPILE_WARNING_AS_ERROR ON -) - -add_qtc_plugin(qmlpreviewplugin - PLUGIN_CLASS QmlPreviewWidgetPlugin - CONDITION TARGET QmlDesigner - DEPENDS Core ProjectExplorer QmlDesigner Utils Qt::Qml - SOURCES - qmlpreviewplugin/qmlpreviewactions.cpp qmlpreviewplugin/qmlpreviewactions.h - qmlpreviewplugin/qmlpreviewplugin.cpp qmlpreviewplugin/qmlpreviewplugin.h - qmlpreviewplugin/qmlpreviewplugin.qrc - PLUGIN_PATH ${QmlDesignerPluginInstallPrefix} -) - -extend_qtc_plugin(qmlpreviewplugin - CONDITION NOT DISABLE_COMPILE_WARNING_AS_ERROR - PROPERTIES COMPILE_WARNING_AS_ERROR ON -) - -add_qtc_plugin(qtquickplugin - PLUGIN_CLASS QtQuickPlugin - CONDITION TARGET QmlDesigner - DEPENDS Core QmlDesigner Utils Qt::Qml - DEFINES QTQUICK_LIBRARY - SOURCES - qtquickplugin/qtquickplugin.cpp qtquickplugin/qtquickplugin.h - qtquickplugin/qtquickplugin.qrc - PLUGIN_PATH ${QmlDesignerPluginInstallPrefix} -) - -extend_qtc_plugin(qtquickplugin - CONDITION NOT DISABLE_COMPILE_WARNING_AS_ERROR - PROPERTIES COMPILE_WARNING_AS_ERROR ON -) - -add_subdirectory(studioplugin) extend_qtc_plugin(QmlDesigner SOURCES_PREFIX components @@ -1178,3 +1087,106 @@ extend_qtc_plugin(QmlDesigner CONDITION TARGET Nanotrace DEPENDS Nanotrace ) + +add_qtc_plugin(assetexporterplugin + PLUGIN_CLASS AssetExporterPlugin + CONDITION TARGET QmlDesigner + PLUGIN_DEPENDS + Core ProjectExplorer QmlDesigner + DEPENDS Utils Qt::Qml Qt::QuickPrivate + PUBLIC_INCLUDES assetexporterplugin + PLUGIN_PATH ${QmlDesignerPluginInstallPrefix} +) + +extend_qtc_plugin(assetexporterplugin + CONDITION NOT DISABLE_COMPILE_WARNING_AS_ERROR + PROPERTIES COMPILE_WARNING_AS_ERROR ON +) + +extend_qtc_plugin(assetexporterplugin + SOURCES_PREFIX assetexporterplugin + SOURCES + assetexportdialog.h assetexportdialog.cpp assetexportdialog.ui + assetexporter.h assetexporter.cpp + assetexporterplugin.h assetexporterplugin.cpp + assetexporterview.h assetexporterview.cpp + assetexportpluginconstants.h + componentexporter.h componentexporter.cpp + exportnotification.h exportnotification.cpp + filepathmodel.h filepathmodel.cpp + dumpers/assetnodedumper.h dumpers/assetnodedumper.cpp + dumpers/itemnodedumper.h dumpers/itemnodedumper.cpp + dumpers/nodedumper.h dumpers/nodedumper.cpp + dumpers/textnodedumper.h dumpers/textnodedumper.cpp + assetexporterplugin.qrc +) + +add_qtc_plugin(componentsplugin + PLUGIN_CLASS ComponentsPlugin + CONDITION TARGET QmlDesigner + PLUGIN_DEPENDS Core QmlDesigner + DEPENDS Utils Qt::Qml + DEFINES COMPONENTS_LIBRARY + + PLUGIN_PATH ${QmlDesignerPluginInstallPrefix} +) + +extend_qtc_plugin(componentsplugin + CONDITION NOT DISABLE_COMPILE_WARNING_AS_ERROR + PROPERTIES COMPILE_WARNING_AS_ERROR ON +) + +extend_qtc_plugin(componentsplugin + SOURCES_PREFIX componentsplugin + SOURCES + addtabdesigneraction.cpp addtabdesigneraction.h + addtabtotabviewdialog.cpp addtabtotabviewdialog.h + addtabtotabviewdialog.ui + componentsplugin.cpp componentsplugin.h + componentsplugin.qrc + entertabdesigneraction.cpp entertabdesigneraction.h + tabviewindexmodel.cpp tabviewindexmodel.h +) + +add_qtc_plugin(qmlpreviewplugin + PLUGIN_CLASS QmlPreviewWidgetPlugin + CONDITION TARGET QmlDesigner + PLUGIN_DEPENDS Core ProjectExplorer QmlDesigner + DEPENDS Utils Qt::Qml + PLUGIN_PATH ${QmlDesignerPluginInstallPrefix} +) + +extend_qtc_plugin(qmlpreviewplugin + CONDITION NOT DISABLE_COMPILE_WARNING_AS_ERROR + PROPERTIES COMPILE_WARNING_AS_ERROR ON +) + +extend_qtc_plugin(qmlpreviewplugin + SOURCES_PREFIX qmlpreviewplugin + SOURCES + qmlpreviewactions.cpp qmlpreviewactions.h + qmlpreviewplugin.cpp qmlpreviewplugin.h + qmlpreviewplugin.qrc +) + +add_qtc_plugin(qtquickplugin + PLUGIN_CLASS QtQuickPlugin + CONDITION TARGET QmlDesigner + PLUGIN_DEPENDS Core QmlDesigner + DEPENDS Utils Qt::Qml + DEFINES QTQUICK_LIBRARY + PLUGIN_PATH ${QmlDesignerPluginInstallPrefix} +) + +extend_qtc_plugin(qtquickplugin + CONDITION NOT DISABLE_COMPILE_WARNING_AS_ERROR + PROPERTIES COMPILE_WARNING_AS_ERROR ON +) + +extend_qtc_plugin(qtquickplugin + SOURCES_PREFIX qtquickplugin + SOURCES + qtquickplugin.cpp qtquickplugin.h + qtquickplugin.qrc +) +add_subdirectory(studioplugin) diff --git a/src/tools/CMakeLists.txt b/src/tools/CMakeLists.txt index 1bee7cccfbc..0eac791aee5 100644 --- a/src/tools/CMakeLists.txt +++ b/src/tools/CMakeLists.txt @@ -30,7 +30,9 @@ if (APPLE) endif() add_subdirectory(processlauncher) -add_subdirectory(qml2puppet) +if (WITH_QMLDESIGNER) + add_subdirectory(qml2puppet) +endif() add_subdirectory(qtcdebugger) ## windows only # add_subdirectory(qtcrashhandler) add_subdirectory(qtc-askpass) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 3e961231ca5..864ce4f416b 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,4 +1,6 @@ add_subdirectory(auto) add_subdirectory(manual) add_subdirectory(tools/qml-ast2dot) -add_subdirectory(unit) +if (WITH_QMLDESIGNER) + add_subdirectory(unit) +endif() From 6e5ffb997d377f535617b8c8dcb7fec18a151794 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Thu, 20 Apr 2023 08:59:12 +0200 Subject: [PATCH 061/192] Sqlite: Update to 3.41.2 Previous version was 3.41.0. So it is only a bug fix update. Change-Id: Ibb3373eff19b740d9bbc0ee5786f8054f1d80553 Reviewed-by: Thomas Hartmann --- src/libs/3rdparty/sqlite/sqlite3.c | 209 +++++++++++++++++++++-------- src/libs/3rdparty/sqlite/sqlite3.h | 6 +- 2 files changed, 153 insertions(+), 62 deletions(-) diff --git a/src/libs/3rdparty/sqlite/sqlite3.c b/src/libs/3rdparty/sqlite/sqlite3.c index b47891c3817..947a1545517 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.41.0. By combining all the individual C code files into this +** version 3.41.2. 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 @@ -452,9 +452,9 @@ extern "C" { ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ -#define SQLITE_VERSION "3.41.0" -#define SQLITE_VERSION_NUMBER 3041000 -#define SQLITE_SOURCE_ID "2023-02-21 18:09:37 05941c2a04037fc3ed2ffae11f5d2260706f89431f463518740f72ada350866d" +#define SQLITE_VERSION "3.41.2" +#define SQLITE_VERSION_NUMBER 3041002 +#define SQLITE_SOURCE_ID "2023-03-22 11:56:21 0d1fc92f94cb6b76bffe3ec34d69cffde2924203304e8ffc4155597af0c191da" /* ** CAPI3REF: Run-Time Library Version Numbers @@ -16592,7 +16592,7 @@ struct PgHdr { ** private to pcache.c and should not be accessed by other modules. ** pCache is grouped with the public elements for efficiency. */ - i16 nRef; /* Number of users of this page */ + i64 nRef; /* Number of users of this page */ PgHdr *pDirtyNext; /* Next element in list of dirty pages */ PgHdr *pDirtyPrev; /* Previous element in list of dirty pages */ /* NB: pDirtyNext and pDirtyPrev are undefined if the @@ -16673,12 +16673,12 @@ SQLITE_PRIVATE void sqlite3PcacheClearSyncFlags(PCache *); SQLITE_PRIVATE void sqlite3PcacheClear(PCache*); /* Return the total number of outstanding page references */ -SQLITE_PRIVATE int sqlite3PcacheRefCount(PCache*); +SQLITE_PRIVATE i64 sqlite3PcacheRefCount(PCache*); /* Increment the reference count of an existing page */ SQLITE_PRIVATE void sqlite3PcacheRef(PgHdr*); -SQLITE_PRIVATE int sqlite3PcachePageRefcount(PgHdr*); +SQLITE_PRIVATE i64 sqlite3PcachePageRefcount(PgHdr*); /* Return the total number of pages stored in the cache */ SQLITE_PRIVATE int sqlite3PcachePagecount(PCache*); @@ -18837,7 +18837,7 @@ struct NameContext { #define NC_HasAgg 0x000010 /* One or more aggregate functions seen */ #define NC_IdxExpr 0x000020 /* True if resolving columns of CREATE INDEX */ #define NC_SelfRef 0x00002e /* Combo: PartIdx, isCheck, GenCol, and IdxExpr */ -#define NC_VarSelect 0x000040 /* A correlated subquery has been seen */ +#define NC_Subquery 0x000040 /* A subquery has been seen */ #define NC_UEList 0x000080 /* True if uNC.pEList is used */ #define NC_UAggInfo 0x000100 /* True if uNC.pAggInfo is used */ #define NC_UUpsert 0x000200 /* True if uNC.pUpsert is used */ @@ -19156,6 +19156,7 @@ struct IndexedExpr { int iIdxCur; /* The index cursor */ int iIdxCol; /* The index column that contains value of pExpr */ u8 bMaybeNullRow; /* True if we need an OP_IfNullRow check */ + u8 aff; /* Affinity of the pExpr expression */ IndexedExpr *pIENext; /* Next in a list of all indexed expressions */ #ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS const char *zIdxName; /* Name of index, used only for bytecode comments */ @@ -19207,6 +19208,9 @@ struct Parse { u8 withinRJSubrtn; /* Nesting level for RIGHT JOIN body subroutines */ #if defined(SQLITE_DEBUG) || defined(SQLITE_COVERAGE_TEST) u8 earlyCleanup; /* OOM inside sqlite3ParserAddCleanup() */ +#endif +#ifdef SQLITE_DEBUG + u8 ifNotExists; /* Might be true if IF NOT EXISTS. Assert()s only */ #endif int nRangeReg; /* Size of the temporary register block */ int iRangeReg; /* First register in temporary register block */ @@ -52653,7 +52657,7 @@ bitvec_end: struct PCache { PgHdr *pDirty, *pDirtyTail; /* List of dirty pages in LRU order */ PgHdr *pSynced; /* Last synced page in dirty page list */ - int nRefSum; /* Sum of ref counts over all pages */ + i64 nRefSum; /* Sum of ref counts over all pages */ int szCache; /* Configured cache size */ int szSpill; /* Size before spilling occurs */ int szPage; /* Size of every page in this cache */ @@ -52683,7 +52687,7 @@ struct PCache { unsigned char *a; int j; pPg = (PgHdr*)pLower->pExtra; - printf("%3d: nRef %2d flgs %02x data ", i, pPg->nRef, pPg->flags); + printf("%3lld: nRef %2d flgs %02x data ", i, pPg->nRef, pPg->flags); a = (unsigned char *)pLower->pBuf; for(j=0; j<12; j++) printf("%02x", a[j]); printf(" ptr %p\n", pPg); @@ -53427,14 +53431,14 @@ SQLITE_PRIVATE PgHdr *sqlite3PcacheDirtyList(PCache *pCache){ ** This is not the total number of pages referenced, but the sum of the ** reference count for all pages. */ -SQLITE_PRIVATE int sqlite3PcacheRefCount(PCache *pCache){ +SQLITE_PRIVATE i64 sqlite3PcacheRefCount(PCache *pCache){ return pCache->nRefSum; } /* ** Return the number of references to the page supplied as an argument. */ -SQLITE_PRIVATE int sqlite3PcachePageRefcount(PgHdr *p){ +SQLITE_PRIVATE i64 sqlite3PcachePageRefcount(PgHdr *p){ return p->nRef; } @@ -74504,7 +74508,7 @@ static SQLITE_NOINLINE int btreeNext(BtCursor *pCur){ pPage = pCur->pPage; idx = ++pCur->ix; - if( NEVER(!pPage->isInit) || sqlite3FaultSim(412) ){ + if( !pPage->isInit || sqlite3FaultSim(412) ){ return SQLITE_CORRUPT_BKPT; } @@ -77633,6 +77637,7 @@ SQLITE_PRIVATE int sqlite3BtreeInsert( assert( szNew==pPage->xCellSize(pPage, newCell) ); assert( szNew <= MX_CELL_SIZE(p->pBt) ); idx = pCur->ix; + pCur->info.nSize = 0; if( loc==0 ){ CellInfo info; assert( idx>=0 ); @@ -77705,7 +77710,6 @@ SQLITE_PRIVATE int sqlite3BtreeInsert( ** larger than the largest existing key, it is possible to insert the ** row without seeking the cursor. This can be a big performance boost. */ - pCur->info.nSize = 0; if( pPage->nOverflow ){ assert( rc==SQLITE_OK ); pCur->curFlags &= ~(BTCF_ValidNKey); @@ -90915,8 +90919,7 @@ static u64 filterHash(const Mem *aMem, const Op *pOp){ }else if( p->flags & MEM_Real ){ h += sqlite3VdbeIntValue(p); }else if( p->flags & (MEM_Str|MEM_Blob) ){ - h += p->n; - if( p->flags & MEM_Zero ) h += p->u.nZero; + /* no-op */ } } return h; @@ -104495,14 +104498,10 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ if( 0==sqlite3ExprCanBeNull(pExpr->pLeft) && !IN_RENAME_OBJECT ){ testcase( ExprHasProperty(pExpr, EP_OuterON) ); assert( !ExprHasProperty(pExpr, EP_IntValue) ); - if( pExpr->op==TK_NOTNULL ){ - pExpr->u.zToken = "true"; - ExprSetProperty(pExpr, EP_IsTrue); - }else{ - pExpr->u.zToken = "false"; - ExprSetProperty(pExpr, EP_IsFalse); - } - pExpr->op = TK_TRUEFALSE; + pExpr->u.iValue = (pExpr->op==TK_NOTNULL); + pExpr->flags |= EP_IntValue; + pExpr->op = TK_INTEGER; + for(i=0, p=pNC; p && ipNext, i++){ p->nRef = anRef[i]; } @@ -104804,8 +104803,8 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ assert( pNC->nRef>=nRef ); if( nRef!=pNC->nRef ){ ExprSetProperty(pExpr, EP_VarSelect); - pNC->ncFlags |= NC_VarSelect; } + pNC->ncFlags |= NC_Subquery; } break; } @@ -109554,6 +109553,7 @@ SQLITE_PRIVATE void sqlite3ExprCodeGeneratedColumn( ){ int iAddr; Vdbe *v = pParse->pVdbe; + int nErr = pParse->nErr; assert( v!=0 ); assert( pParse->iSelfTab!=0 ); if( pParse->iSelfTab>0 ){ @@ -109566,6 +109566,7 @@ SQLITE_PRIVATE void sqlite3ExprCodeGeneratedColumn( sqlite3VdbeAddOp4(v, OP_Affinity, regOut, 1, 0, &pCol->affinity, 1); } if( iAddr ) sqlite3VdbeJumpHere(v, iAddr); + if( pParse->nErr>nErr ) pParse->db->errByteOffset = -1; } #endif /* SQLITE_OMIT_GENERATED_COLUMNS */ @@ -109582,6 +109583,7 @@ SQLITE_PRIVATE void sqlite3ExprCodeGetColumnOfTable( Column *pCol; assert( v!=0 ); assert( pTab!=0 ); + assert( iCol!=XN_EXPR ); if( iCol<0 || iCol==pTab->iPKey ){ sqlite3VdbeAddOp2(v, OP_Rowid, iTabCur, regOut); VdbeComment((v, "%s.rowid", pTab->zName)); @@ -109848,6 +109850,7 @@ static SQLITE_NOINLINE int sqlite3IndexedExprLookup( IndexedExpr *p; Vdbe *v; for(p=pParse->pIdxEpr; p; p=p->pIENext){ + u8 exprAff; int iDataCur = p->iDataCur; if( iDataCur<0 ) continue; if( pParse->iSelfTab ){ @@ -109855,6 +109858,16 @@ static SQLITE_NOINLINE int sqlite3IndexedExprLookup( iDataCur = -1; } if( sqlite3ExprCompare(0, pExpr, p->pExpr, iDataCur)!=0 ) continue; + assert( p->aff>=SQLITE_AFF_BLOB && p->aff<=SQLITE_AFF_NUMERIC ); + exprAff = sqlite3ExprAffinity(pExpr); + if( (exprAff<=SQLITE_AFF_BLOB && p->aff!=SQLITE_AFF_BLOB) + || (exprAff==SQLITE_AFF_TEXT && p->aff!=SQLITE_AFF_TEXT) + || (exprAff>=SQLITE_AFF_NUMERIC && p->aff!=SQLITE_AFF_NUMERIC) + ){ + /* Affinity mismatch on a generated column */ + continue; + } + v = pParse->pVdbe; assert( v!=0 ); if( p->bMaybeNullRow ){ @@ -110434,10 +110447,13 @@ expr_code_doover: return target; } case TK_COLLATE: { - if( !ExprHasProperty(pExpr, EP_Collate) - && ALWAYS(pExpr->pLeft) - && pExpr->pLeft->op==TK_FUNCTION - ){ + 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. + */ + assert( pExpr->pLeft ); inReg = sqlite3ExprCodeTarget(pParse, pExpr->pLeft, target); if( inReg!=target ){ sqlite3VdbeAddOp2(v, OP_SCopy, inReg, target); @@ -110545,16 +110561,22 @@ expr_code_doover: break; } } - addrINR = sqlite3VdbeAddOp1(v, OP_IfNullRow, pExpr->iTable); - /* Temporarily disable factoring of constant expressions, since - ** even though expressions may appear to be constant, they are not - ** really constant because they originate from the right-hand side - ** of a LEFT JOIN. */ - pParse->okConstFactor = 0; + addrINR = sqlite3VdbeAddOp3(v, OP_IfNullRow, pExpr->iTable, 0, target); + /* The OP_IfNullRow opcode above can overwrite the result register with + ** NULL. So we have to ensure that the result register is not a value + ** that is suppose to be a constant. Two defenses are needed: + ** (1) Temporarily disable factoring of constant expressions + ** (2) Make sure the computed value really is stored in register + ** "target" and not someplace else. + */ + pParse->okConstFactor = 0; /* note (1) above */ inReg = sqlite3ExprCodeTarget(pParse, pExpr->pLeft, target); pParse->okConstFactor = okConstFactor; + if( inReg!=target ){ /* note (2) above */ + sqlite3VdbeAddOp2(v, OP_SCopy, inReg, target); + inReg = target; + } sqlite3VdbeJumpHere(v, addrINR); - sqlite3VdbeChangeP3(v, addrINR, inReg); break; } @@ -118917,7 +118939,7 @@ SQLITE_PRIVATE void sqlite3AddReturning(Parse *pParse, ExprList *pList){ if( pParse->pNewTrigger ){ sqlite3ErrorMsg(pParse, "cannot use RETURNING in a trigger"); }else{ - assert( pParse->bReturning==0 ); + assert( pParse->bReturning==0 || pParse->ifNotExists ); } pParse->bReturning = 1; pRet = sqlite3DbMallocZero(db, sizeof(*pRet)); @@ -118943,7 +118965,8 @@ SQLITE_PRIVATE void sqlite3AddReturning(Parse *pParse, ExprList *pList){ pRet->retTStep.pTrig = &pRet->retTrig; pRet->retTStep.pExprList = pList; pHash = &(db->aDb[1].pSchema->trigHash); - assert( sqlite3HashFind(pHash, RETURNING_TRIGGER_NAME)==0 || pParse->nErr ); + assert( sqlite3HashFind(pHash, RETURNING_TRIGGER_NAME)==0 + || pParse->nErr || pParse->ifNotExists ); if( sqlite3HashInsert(pHash, RETURNING_TRIGGER_NAME, &pRet->retTrig) ==&pRet->retTrig ){ sqlite3OomFault(db); @@ -119478,6 +119501,7 @@ SQLITE_PRIVATE void sqlite3AddGenerated(Parse *pParse, Expr *pExpr, Token *pType ** turn it into one by adding a unary "+" operator. */ pExpr = sqlite3PExpr(pParse, TK_UPLUS, pExpr, 0); } + if( pExpr && pExpr->op!=TK_RAISE ) pExpr->affExpr = pCol->affinity; sqlite3ColumnSetExpr(pParse, pTab, pCol, pExpr); pExpr = 0; goto generated_done; @@ -124184,7 +124208,7 @@ SQLITE_PRIVATE void sqlite3DeleteFrom( #endif /* SQLITE_OMIT_TRUNCATE_OPTIMIZATION */ { u16 wcf = WHERE_ONEPASS_DESIRED|WHERE_DUPLICATES_OK; - if( sNC.ncFlags & NC_VarSelect ) bComplex = 1; + if( sNC.ncFlags & NC_Subquery ) bComplex = 1; wcf |= (bComplex ? 0 : WHERE_ONEPASS_MULTIROW); if( HasRowid(pTab) ){ /* For a rowid table, initialize the RowSet to an empty set */ @@ -126881,6 +126905,18 @@ static void ceilingFunc( static double xCeil(double x){ return ceil(x); } static double xFloor(double x){ return floor(x); } +/* +** Some systems do not have log2() and log10() in their standard math +** libraries. +*/ +#if defined(HAVE_LOG10) && HAVE_LOG10==0 +# define log10(X) (0.4342944819032517867*log(X)) +#endif +#if defined(HAVE_LOG2) && HAVE_LOG2==0 +# define log2(X) (1.442695040888963456*log(X)) +#endif + + /* ** Implementation of SQL functions: ** @@ -136260,6 +136296,23 @@ SQLITE_PRIVATE void sqlite3Pragma( jmp4 = integrityCheckResultRow(v); sqlite3VdbeJumpHere(v, jmp2); + /* The OP_IdxRowid opcode is an optimized version of OP_Column + ** that extracts the rowid off the end of the index record. + ** But it only works correctly if index record does not have + ** any extra bytes at the end. Verify that this is the case. */ + if( HasRowid(pTab) ){ + int jmp7; + sqlite3VdbeAddOp2(v, OP_IdxRowid, iIdxCur+j, 3); + jmp7 = sqlite3VdbeAddOp3(v, OP_Eq, 3, 0, r1+pIdx->nColumn-1); + VdbeCoverage(v); + sqlite3VdbeLoadString(v, 3, + "rowid not at end-of-record for row "); + sqlite3VdbeAddOp3(v, OP_Concat, 7, 3, 3); + sqlite3VdbeLoadString(v, 4, " of index "); + sqlite3VdbeGoto(v, jmp5-1); + sqlite3VdbeJumpHere(v, jmp7); + } + /* Any indexed columns with non-BINARY collations must still hold ** the exact same text value as the table. */ label6 = 0; @@ -140555,8 +140608,6 @@ SQLITE_PRIVATE void sqlite3SubqueryColumnTypes( pCol->affinity = sqlite3ExprAffinity(p); if( pCol->affinity<=SQLITE_AFF_NONE ){ pCol->affinity = aff; - }else if( pCol->affinity>=SQLITE_AFF_NUMERIC && p->op==TK_CAST ){ - pCol->affinity = SQLITE_AFF_FLEXNUM; } if( pCol->affinity>=SQLITE_AFF_TEXT && pSelect->pNext ){ int m = 0; @@ -140570,6 +140621,9 @@ SQLITE_PRIVATE void sqlite3SubqueryColumnTypes( if( pCol->affinity>=SQLITE_AFF_NUMERIC && (m&0x02)!=0 ){ pCol->affinity = SQLITE_AFF_BLOB; } + if( pCol->affinity>=SQLITE_AFF_NUMERIC && p->op==TK_CAST ){ + pCol->affinity = SQLITE_AFF_FLEXNUM; + } } zType = columnType(&sNC, p, 0, 0, 0); if( zType==0 || pCol->affinity!=sqlite3AffinityType(zType, 0) ){ @@ -142084,7 +142138,9 @@ static Expr *substExpr( sqlite3VectorErrorMsg(pSubst->pParse, pCopy); }else{ sqlite3 *db = pSubst->pParse->db; - if( pSubst->isOuterJoin && pCopy->op!=TK_COLUMN ){ + if( pSubst->isOuterJoin + && (pCopy->op!=TK_COLUMN || pCopy->iTable!=pSubst->iNewTable) + ){ memset(&ifNullRow, 0, sizeof(ifNullRow)); ifNullRow.op = TK_IF_NULL_ROW; ifNullRow.pLeft = pCopy; @@ -144600,10 +144656,12 @@ static void optimizeAggregateUseOfIndexedExpr( NameContext *pNC /* Name context used to resolve agg-func args */ ){ assert( pAggInfo->iFirstReg==0 ); + assert( pSelect!=0 ); + assert( pSelect->pGroupBy!=0 ); pAggInfo->nColumn = pAggInfo->nAccumulator; if( ALWAYS(pAggInfo->nSortingColumn>0) ){ if( pAggInfo->nColumn==0 ){ - pAggInfo->nSortingColumn = 0; + pAggInfo->nSortingColumn = pSelect->pGroupBy->nExpr; }else{ pAggInfo->nSortingColumn = pAggInfo->aCol[pAggInfo->nColumn-1].iSorterColumn+1; @@ -145028,6 +145086,7 @@ static int countOfViewOptimization(Parse *pParse, Select *p){ if( p->pEList->nExpr!=1 ) return 0; /* Single result column */ if( p->pWhere ) return 0; if( p->pGroupBy ) return 0; + if( p->pOrderBy ) return 0; pExpr = p->pEList->a[0].pExpr; if( pExpr->op!=TK_AGG_FUNCTION ) return 0; /* Result is an aggregate */ assert( ExprUseUToken(pExpr) ); @@ -145038,7 +145097,8 @@ static int countOfViewOptimization(Parse *pParse, Select *p){ if( ExprHasProperty(pExpr, EP_WinFunc) ) return 0;/* Not a window function */ pSub = p->pSrc->a[0].pSelect; if( pSub==0 ) return 0; /* The FROM is a subquery */ - if( pSub->pPrior==0 ) return 0; /* Must be a compound ry */ + if( pSub->pPrior==0 ) return 0; /* Must be a compound */ + if( pSub->selFlags & SF_CopyCte ) return 0; /* Not a CTE */ do{ if( pSub->op!=TK_ALL && pSub->pPrior ) return 0; /* Must be UNION ALL */ if( pSub->pWhere ) return 0; /* No WHERE clause */ @@ -145481,7 +145541,6 @@ SQLITE_PRIVATE int sqlite3Select( && countOfViewOptimization(pParse, p) ){ if( db->mallocFailed ) goto select_end; - pEList = p->pEList; pTabList = p->pSrc; } #endif @@ -146843,6 +146902,7 @@ SQLITE_PRIVATE void sqlite3BeginTrigger( }else{ assert( !db->init.busy ); sqlite3CodeVerifySchema(pParse, iDb); + VVA_ONLY( pParse->ifNotExists = 1; ) } goto trigger_cleanup; } @@ -147624,7 +147684,7 @@ static void codeReturningTrigger( } sqlite3ExprListDelete(db, sSelect.pEList); pNew = sqlite3ExpandReturning(pParse, pReturning->pReturnEL, pTab); - if( !db->mallocFailed ){ + if( pParse->nErr==0 ){ NameContext sNC; memset(&sNC, 0, sizeof(sNC)); if( pReturning->nRetCol==0 ){ @@ -148846,12 +148906,22 @@ SQLITE_PRIVATE void sqlite3Update( /* Begin the database scan. ** ** Do not consider a single-pass strategy for a multi-row update if - ** there are any triggers or foreign keys to process, or rows may - ** be deleted as a result of REPLACE conflict handling. Any of these - ** things might disturb a cursor being used to scan through the table - ** or index, causing a single-pass approach to malfunction. */ + ** there is anything that might disrupt the cursor being used to do + ** the UPDATE: + ** (1) This is a nested UPDATE + ** (2) There are triggers + ** (3) There are FOREIGN KEY constraints + ** (4) There are REPLACE conflict handlers + ** (5) There are subqueries in the WHERE clause + */ flags = WHERE_ONEPASS_DESIRED; - if( !pParse->nested && !pTrigger && !hasFK && !chngKey && !bReplace ){ + if( !pParse->nested + && !pTrigger + && !hasFK + && !chngKey + && !bReplace + && (sNC.ncFlags & NC_Subquery)==0 + ){ flags |= WHERE_ONEPASS_MULTIROW; } pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere,0,0,0,flags,iIdxCur); @@ -150816,7 +150886,9 @@ static int vtabCallConstructor( sCtx.pPrior = db->pVtabCtx; sCtx.bDeclared = 0; db->pVtabCtx = &sCtx; + pTab->nTabRef++; rc = xConstruct(db, pMod->pAux, nArg, azArg, &pVTable->pVtab, &zErr); + sqlite3DeleteTable(db, pTab); db->pVtabCtx = sCtx.pPrior; if( rc==SQLITE_NOMEM ) sqlite3OomFault(db); assert( sCtx.pTab==pTab ); @@ -156848,7 +156920,7 @@ SQLITE_PRIVATE void sqlite3WhereTabFuncArgs( pRhs = sqlite3PExpr(pParse, TK_UPLUS, sqlite3ExprDup(pParse->db, pArgs->a[j].pExpr, 0), 0); pTerm = sqlite3PExpr(pParse, TK_EQ, pColRef, pRhs); - if( pItem->fg.jointype & (JT_LEFT|JT_LTORJ) ){ + if( pItem->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT) ){ joinType = EP_OuterON; }else{ joinType = EP_InnerON; @@ -157985,6 +158057,10 @@ static SQLITE_NOINLINE void sqlite3ConstructBloomFilter( Vdbe *v = pParse->pVdbe; /* VDBE under construction */ WhereLoop *pLoop = pLevel->pWLoop; /* The loop being coded */ int iCur; /* Cursor for table getting the filter */ + IndexedExpr *saved_pIdxEpr; /* saved copy of Parse.pIdxEpr */ + + saved_pIdxEpr = pParse->pIdxEpr; + pParse->pIdxEpr = 0; assert( pLoop!=0 ); assert( v!=0 ); @@ -158041,9 +158117,8 @@ static SQLITE_NOINLINE void sqlite3ConstructBloomFilter( int r1 = sqlite3GetTempRange(pParse, n); int jj; for(jj=0; jjaiColumn[jj]; assert( pIdx->pTable==pItem->pTab ); - sqlite3ExprCodeGetColumnOfTable(v, pIdx->pTable, iCur, iCol,r1+jj); + sqlite3ExprCodeLoadIndexColumn(pParse, pIdx, iCur, jj, r1+jj); } sqlite3VdbeAddOp4Int(v, OP_FilterAdd, pLevel->regFilter, 0, r1, n); sqlite3ReleaseTempRange(pParse, r1, n); @@ -158074,6 +158149,7 @@ static SQLITE_NOINLINE void sqlite3ConstructBloomFilter( } }while( iLevel < pWInfo->nLevel ); sqlite3VdbeJumpHere(v, addrOnce); + pParse->pIdxEpr = saved_pIdxEpr; } @@ -158373,6 +158449,7 @@ static int whereKeyStats( assert( pIdx->nSample>0 ); assert( pRec->nField>0 ); + /* Do a binary search to find the first sample greater than or equal ** to pRec. If pRec contains a single field, the set of samples to search ** is simply the aSample[] array. If the samples in aSample[] contain more @@ -158417,7 +158494,12 @@ static int whereKeyStats( ** it is extended to two fields. The duplicates that this creates do not ** cause any problems. */ - nField = MIN(pRec->nField, pIdx->nSample); + if( !HasRowid(pIdx->pTable) && IsPrimaryKeyIndex(pIdx) ){ + nField = pIdx->nKeyCol; + }else{ + nField = pIdx->nColumn; + } + nField = MIN(pRec->nField, nField); iCol = 0; iSample = pIdx->nSample * nField; do{ @@ -162145,6 +162227,10 @@ 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; + } }else{ pWInfo->revMask = pFrom->revLoop; if( pWInfo->nOBSat<=0 ){ @@ -162556,6 +162642,9 @@ static SQLITE_NOINLINE void whereAddIndexedExpr( p->iIdxCur = iIdxCur; p->iIdxCol = i; p->bMaybeNullRow = bMaybeNullRow; + if( sqlite3IndexAffinityStr(pParse->db, pIdx) ){ + p->aff = pIdx->zColAff[i]; + } #ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS p->zIdxName = pIdx->zName; #endif @@ -193018,16 +193107,18 @@ static int fts3MsrBufferData( char *pList, i64 nList ){ - if( nList>pMsr->nBuffer ){ + if( (nList+FTS3_NODE_PADDING)>pMsr->nBuffer ){ char *pNew; - pMsr->nBuffer = nList*2; - pNew = (char *)sqlite3_realloc64(pMsr->aBuffer, pMsr->nBuffer); + int nNew = nList*2 + FTS3_NODE_PADDING; + pNew = (char *)sqlite3_realloc64(pMsr->aBuffer, nNew); if( !pNew ) return SQLITE_NOMEM; pMsr->aBuffer = pNew; + pMsr->nBuffer = nNew; } assert( nList>0 ); memcpy(pMsr->aBuffer, pList, nList); + memset(&pMsr->aBuffer[nList], 0, FTS3_NODE_PADDING); return SQLITE_OK; } @@ -240171,7 +240262,7 @@ static void fts5SourceIdFunc( ){ assert( nArg==0 ); UNUSED_PARAM2(nArg, apUnused); - sqlite3_result_text(pCtx, "fts5: 2023-02-21 18:09:37 05941c2a04037fc3ed2ffae11f5d2260706f89431f463518740f72ada350866d", -1, SQLITE_TRANSIENT); + sqlite3_result_text(pCtx, "fts5: 2023-03-22 11:56:21 0d1fc92f94cb6b76bffe3ec34d69cffde2924203304e8ffc4155597af0c191da", -1, SQLITE_TRANSIENT); } /* diff --git a/src/libs/3rdparty/sqlite/sqlite3.h b/src/libs/3rdparty/sqlite/sqlite3.h index 4c6addac26c..7e43e1f1b4d 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.41.0" -#define SQLITE_VERSION_NUMBER 3041000 -#define SQLITE_SOURCE_ID "2023-02-21 18:09:37 05941c2a04037fc3ed2ffae11f5d2260706f89431f463518740f72ada350866d" +#define SQLITE_VERSION "3.41.2" +#define SQLITE_VERSION_NUMBER 3041002 +#define SQLITE_SOURCE_ID "2023-03-22 11:56:21 0d1fc92f94cb6b76bffe3ec34d69cffde2924203304e8ffc4155597af0c191da" /* ** CAPI3REF: Run-Time Library Version Numbers From 852c0b7fce22673b52fd7632226304a6377f4745 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Thu, 20 Apr 2023 08:40:01 +0200 Subject: [PATCH 062/192] QmlDesigner: Silence more warnings Change-Id: I8982ce6fe2860950e92b95e1bf02d22e857939d7 Reviewed-by: Tim Jenssen --- .../qmldesigner/designercore/projectstorage/modulescanner.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/qmldesigner/designercore/projectstorage/modulescanner.h b/src/plugins/qmldesigner/designercore/projectstorage/modulescanner.h index c44d7aa81ff..fb1012030e0 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/modulescanner.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/modulescanner.h @@ -33,9 +33,9 @@ private: void scan(std::string_view modulePaths); private: - SkipFunction m_skip; + [[maybe_unused]] SkipFunction m_skip; Imports m_modules; - VersionScanning m_versionScanning; + [[maybe_unused]] VersionScanning m_versionScanning; }; } // namespace QmlDesigner From 4132b308f5bffe1cb1d1261cd7c91c606023f331 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Thu, 20 Apr 2023 10:32:52 +0200 Subject: [PATCH 063/192] QmlDesigner: Fix warnings Some compilers still don't support [[maybe_unused]] for members Change-Id: I1a653b9e76eb40313c4f4dd9201d78a24b3d543d Reviewed-by: Qt CI Bot Reviewed-by: Tim Jenssen --- .../designercore/projectstorage/modulescanner.h | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/plugins/qmldesigner/designercore/projectstorage/modulescanner.h b/src/plugins/qmldesigner/designercore/projectstorage/modulescanner.h index fb1012030e0..617428b0f59 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/modulescanner.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/modulescanner.h @@ -18,9 +18,11 @@ class QMLDESIGNERCORE_EXPORT ModuleScanner public: using SkipFunction = std::function; - ModuleScanner(SkipFunction skip, VersionScanning versionScanning) + ModuleScanner([[maybe_unused]] SkipFunction skip, [[maybe_unused]] VersionScanning versionScanning) +#ifdef QDS_HAS_QMLPRIVATE : m_skip{std::move(skip)} , m_versionScanning{versionScanning} +#endif { m_modules.reserve(128); } @@ -33,9 +35,11 @@ private: void scan(std::string_view modulePaths); private: - [[maybe_unused]] SkipFunction m_skip; Imports m_modules; - [[maybe_unused]] VersionScanning m_versionScanning; +#ifdef QDS_HAS_QMLPRIVATE + SkipFunction m_skip; + VersionScanning m_versionScanning; +#endif }; } // namespace QmlDesigner From d530d39dd31e4aa016156b5248a93ac7d85110b8 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Thu, 20 Apr 2023 11:57:23 +0200 Subject: [PATCH 064/192] QmlDesigner: Remove std::filesystem usage Change-Id: I2282052a92e13744bceec3e5597670a68ba6db4c Reviewed-by: Tim Jenssen --- src/plugins/qmldesigner/CMakeLists.txt | 3 +- .../projectstorage/modulescanner.cpp | 45 ++++++++----------- .../qmldesigner/qmldesignerprojectmanager.cpp | 44 +++++++++--------- 3 files changed, 41 insertions(+), 51 deletions(-) diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index c8dc6675735..be735401b82 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -78,8 +78,7 @@ extend_qtc_library(QmlDesignerCore ) extend_qtc_library(QmlDesignerCore - CONDITION TARGET Qt6::QmlDomPrivate AND TARGET Qt6::QmlCompilerPrivate AND Qt6_VERSION VERSION_GREATER_EQUAL 6.4.3 - AND NOT (CMAKE_HOST_SYSTEM_NAME STREQUAL "Darwin" AND CMAKE_SYSTEM_VERSION VERSION_LESS 19) + CONDITION TARGET Qt6::QmlDomPrivate AND TARGET Qt6::QmlCompilerPrivate AND Qt6_VERSION VERSION_GREATER_EQUAL 6.4.3 DEPENDS Qt6::QmlDomPrivate Qt6::QmlCompilerPrivate PUBLIC_DEFINES QDS_HAS_QMLPRIVATE diff --git a/src/plugins/qmldesigner/designercore/projectstorage/modulescanner.cpp b/src/plugins/qmldesigner/designercore/projectstorage/modulescanner.cpp index f6c0f2449c1..ffcf28d7658 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/modulescanner.cpp +++ b/src/plugins/qmldesigner/designercore/projectstorage/modulescanner.cpp @@ -7,10 +7,9 @@ #include #endif +#include #include -#include - namespace QmlDesigner { namespace { @@ -49,38 +48,30 @@ void ModuleScanner::scan(const QStringList &modulePaths) void ModuleScanner::scan([[maybe_unused]] std::string_view modulePath) { #ifdef QDS_HAS_QMLPRIVATE - try { - const std::filesystem::path installDirectoryPath{modulePath}; + QDirIterator dirIterator{QString::fromUtf8(modulePath), QDir::Dirs, QDirIterator::Subdirectories}; - auto current = std::filesystem::recursive_directory_iterator{installDirectoryPath}; - auto end = std::filesystem::end(current); + while (dirIterator.hasNext()) { + auto directoryPath = dirIterator.next(); + QString qmldirPath = directoryPath + "/qmldir"; + if (QFileInfo::exists(qmldirPath)) { + QQmlDirParser parser; - for (; current != end; ++current) { - const auto &entry = *current; - auto path = entry.path(); + auto content = contentAsQString(qmldirPath); + if (!content) + continue; - if (path.filename() == "qmldir") { - QQmlDirParser parser; + bool hasError = parser.parse(*content); + if (hasError) + continue; - auto content = contentAsQString(QString::fromStdU16String(path.u16string())); - if (!content) - continue; + auto moduleName = parser.typeNamespace(); - bool hasError = parser.parse(*content); - if (hasError) - continue; + if (moduleName.isEmpty() || m_skip(moduleName)) + continue; - auto moduleName = parser.typeNamespace(); - - if (moduleName.isEmpty() || m_skip(moduleName)) - continue; - - m_modules.push_back( - Import::createLibraryImport(moduleName, createVersion(parser.components()))); - } + m_modules.push_back( + Import::createLibraryImport(moduleName, createVersion(parser.components()))); } - } catch (const std::filesystem::filesystem_error &) { - return; } #endif } diff --git a/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp b/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp index 8f680ac3544..c1003915813 100644 --- a/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp +++ b/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp @@ -39,6 +39,7 @@ #include +#include #include #include @@ -344,17 +345,23 @@ void projectQmldirPaths(::ProjectExplorer::Target *target, QStringList &qmldirPa } #ifdef QDS_HAS_QMLPRIVATE -bool skipPath(const std::filesystem::path &path) +QStringView currentDirectoryName(const QString &path) { - auto directory = path.filename(); - qDebug() << path.string().data(); + auto found = std::find(path.rbegin(), path.rend(), u'/'); - bool skip = directory == "QtApplicationManager" || directory == "QtInterfaceFramework" - || directory == "QtOpcUa" || directory == "Qt3D" || directory == "Qt3D" - || directory == "Scene2D" || directory == "Scene3D" || directory == "QtWayland" - || directory == "Qt5Compat"; - if (skip) - qDebug() << "skip" << path.string().data(); + if (found == path.rend()) + return {}; + + return QStringView{found.base(), path.end()}; +} +bool skipPath(const QString &path) +{ + auto directory = currentDirectoryName(path); + + bool skip = directory == u"QtApplicationManager" || directory == u"QtInterfaceFramework" + || directory == u"QtOpcUa" || directory == u"Qt3D" || directory == u"Qt3D" + || directory == u"Scene2D" || directory == u"Scene3D" || directory == u"QtWayland" + || directory == u"Qt5Compat"; return skip; } @@ -366,21 +373,14 @@ void qtQmldirPaths([[maybe_unused]] ::ProjectExplorer::Target *target, #ifdef QDS_HAS_QMLPRIVATE if (useProjectStorage()) { const QString installDirectory = qmlPath(target).toString(); + QDirIterator dirIterator{installDirectory, QDir::Dirs, QDirIterator::Subdirectories}; - const std::filesystem::path installDirectoryPath{installDirectory.toStdString()}; + while (dirIterator.hasNext()) { + auto directoryPath = dirIterator.next(); - auto current = std::filesystem::recursive_directory_iterator{installDirectoryPath}; - auto end = std::filesystem::end(current); - for (; current != end; ++current) { - const auto &entry = *current; - auto path = entry.path(); - if (current.depth() < 3 && !current->is_regular_file() && skipPath(path)) { - current.disable_recursion_pending(); - continue; - } - if (path.filename() == "qmldir") { - qmldirPaths.push_back(QString::fromStdU16String(path.generic_u16string())); - } + QString qmldirPath = directoryPath + "/qmldir"; + if (!skipPath(directoryPath) && QFileInfo::exists(qmldirPath)) + qmldirPaths.push_back(directoryPath); } } #endif From 017c57c7a16cd21a9cef04fe6ceb71c2ed7f348e Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Thu, 20 Apr 2023 13:14:37 +0200 Subject: [PATCH 065/192] QmlDesigner: Disable unsupported QmlPrivate versions We can fix that later. Task-number: QDS-9734 Change-Id: I04f8ec055505ec187127f0a41927f0343cc72a2f Reviewed-by: Alessandro Portale Reviewed-by: Qt CI Bot --- src/plugins/qmldesigner/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index be735401b82..a5d767e332a 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -79,7 +79,7 @@ extend_qtc_library(QmlDesignerCore extend_qtc_library(QmlDesignerCore CONDITION TARGET Qt6::QmlDomPrivate AND TARGET Qt6::QmlCompilerPrivate AND Qt6_VERSION VERSION_GREATER_EQUAL 6.4.3 - + AND Qt6_VERSION VERSION_LESS 6.6.0 DEPENDS Qt6::QmlDomPrivate Qt6::QmlCompilerPrivate PUBLIC_DEFINES QDS_HAS_QMLPRIVATE ) From 9817df63fb9eae342d5bf6f28f526aa09b17e8de Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Thu, 20 Apr 2023 15:26:09 +0300 Subject: [PATCH 066/192] QmlDesigner: Fix puppet build after quick3d private API changes Change-Id: I3668853886306fe00d5da916aa458559964a9af0 Reviewed-by: Mahmoud Badri Reviewed-by: Qt CI Bot --- .../qml2puppet/editor3d/generalhelper.cpp | 16 ++++++++++++++-- .../qml2puppet/editor3d/selectionboxgeometry.cpp | 10 ++++++++-- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp b/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp index 829a6f0edbc..52a3f6d74aa 100644 --- a/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp +++ b/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp @@ -215,6 +215,7 @@ QVector4D GeneralHelper::focusNodesToCamera(QQuick3DCamera *camera, float defaul if (auto renderModel = static_cast(targetPriv->spatialNode)) { QWindow *window = static_cast(viewPort->window()); if (window) { +#if QT_VERSION < QT_VERSION_CHECK(6, 5, 1) QSSGRef context; #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) context = QSSGRenderContextInterface::getRenderContextInterface(quintptr(window)); @@ -222,12 +223,17 @@ QVector4D GeneralHelper::focusNodesToCamera(QQuick3DCamera *camera, float defaul context = targetPriv->sceneManager->rci; #endif if (!context.isNull()) { +#else + const auto &sm = targetPriv->sceneManager; + auto context = sm->wattached ? sm->wattached->rci().get() : nullptr; + if (context) { +#endif QSSGBounds3 bounds; auto geometry = qobject_cast(model->geometry()); if (geometry) { bounds = geometry->bounds(); } else { - auto bufferManager = context->bufferManager(); + const auto &bufferManager(context->bufferManager()); #if QT_VERSION < QT_VERSION_CHECK(6, 3, 0) bounds = renderModel->getModelBounds(bufferManager); #else @@ -889,6 +895,7 @@ bool GeneralHelper::getBounds(QQuick3DViewport *view3D, QQuick3DNode *node, QVec if (auto renderModel = static_cast(renderNode)) { QWindow *window = static_cast(view3D->window()); if (window) { +#if QT_VERSION < QT_VERSION_CHECK(6, 5, 1) QSSGRef context; #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) context = QSSGRenderContextInterface::getRenderContextInterface(quintptr(window)); @@ -896,7 +903,12 @@ bool GeneralHelper::getBounds(QQuick3DViewport *view3D, QQuick3DNode *node, QVec context = QQuick3DObjectPrivate::get(node)->sceneManager->rci; #endif if (!context.isNull()) { - auto bufferManager = context->bufferManager(); +#else + const auto &sm = QQuick3DObjectPrivate::get(node)->sceneManager; + auto context = sm->wattached ? sm->wattached->rci().get() : nullptr; + if (context) { +#endif + const auto &bufferManager(context->bufferManager()); #if QT_VERSION < QT_VERSION_CHECK(6, 3, 0) QSSGBounds3 bounds = renderModel->getModelBounds(bufferManager); #else diff --git a/src/tools/qml2puppet/qml2puppet/editor3d/selectionboxgeometry.cpp b/src/tools/qml2puppet/qml2puppet/editor3d/selectionboxgeometry.cpp index 8a2a2449968..1157b1c5f84 100644 --- a/src/tools/qml2puppet/qml2puppet/editor3d/selectionboxgeometry.cpp +++ b/src/tools/qml2puppet/qml2puppet/editor3d/selectionboxgeometry.cpp @@ -312,14 +312,20 @@ void SelectionBoxGeometry::getBounds( if (auto renderModel = static_cast(renderNode)) { QWindow *window = static_cast(m_view3D->window()); if (window) { +#if QT_VERSION < QT_VERSION_CHECK(6, 5, 1) QSSGRef context; #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) context = QSSGRenderContextInterface::getRenderContextInterface(quintptr(window)); -#else +#elif QT_VERSION < QT_VERSION_CHECK(6, 5, 1) context = QQuick3DObjectPrivate::get(this)->sceneManager->rci; #endif if (!context.isNull()) { - auto bufferManager = context->bufferManager(); +#else + const auto &sm = QQuick3DObjectPrivate::get(this)->sceneManager; + auto context = sm->wattached ? sm->wattached->rci().get() : nullptr; + if (context) { +#endif + const auto &bufferManager(context->bufferManager()); #if QT_VERSION < QT_VERSION_CHECK(6, 3, 0) QSSGBounds3 bounds = renderModel->getModelBounds(bufferManager); #else From ece03f55ac3bc3346b591ed9b9bd1797b6a5b026 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Wed, 19 Apr 2023 14:33:07 +0200 Subject: [PATCH 067/192] QmlDesigner: Add import difference In some cases we want to use only a sub set of imports. In that case we compute the differences on demand. To compute the differences the imports have to be sorted. Task-number: QDS-9542 Change-Id: I8b3e501b7657522317847b0b788cd3ff5b47b003 Reviewed-by: Tim Jenssen --- .../components/edit3d/edit3dview.cpp | 28 +++++++------ .../itemlibrary/itemlibraryview.cpp | 2 +- .../itemlibrary/itemlibrarywidget.cpp | 2 +- .../navigator/navigatortreemodel.cpp | 3 +- .../qmldesigner/designercore/include/import.h | 40 ++++++++++++++++--- .../qmldesigner/designercore/include/model.h | 4 +- .../qmldesigner/designercore/model/import.cpp | 18 ++++++--- .../qmldesigner/designercore/model/model.cpp | 18 ++++++--- .../qmldesigner/designercore/model/model_p.h | 2 - 9 files changed, 81 insertions(+), 36 deletions(-) diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp index 39125c8f698..9dcc2d7abc8 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp +++ b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp @@ -920,19 +920,23 @@ void Edit3DView::addQuick3DImport() { DesignDocument *document = QmlDesignerPlugin::instance()->currentDesignDocument(); if (document && !document->inFileComponentModelActive() && model()) { - const Imports imports = model()->possibleImports(); - for (const auto &import : imports) { - if (import.url() == "QtQuick3D") { - if (!import.version().isEmpty() && import.majorVersion() >= 6) { - // Prefer empty version number in Qt6 and beyond - model()->changeImports({Import::createLibraryImport( - import.url(), {}, import.alias(), - import.importPaths())}, {}); - } else { - model()->changeImports({import}, {}); - } - return; + Import qtQuick3DImport; + differenceCall(model()->possibleImports(), model()->imports(), [&](const auto &import) { + if (import.url() == "QtQuick3D") + qtQuick3DImport = import; + }); + if (!qtQuick3DImport.isEmpty()) { + if (!qtQuick3DImport.version().isEmpty() && qtQuick3DImport.majorVersion() >= 6) { + // Prefer empty version number in Qt6 and beyond + model()->changeImports({Import::createLibraryImport(qtQuick3DImport.url(), + {}, + qtQuick3DImport.alias(), + qtQuick3DImport.importPaths())}, + {}); + } else { + model()->changeImports({qtQuick3DImport}, {}); } + return; } } Core::AsynchronousMessageBox::warning(tr("Failed to Add Import"), diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp index 89b16fcd8e5..4dd5be177f5 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp @@ -62,7 +62,7 @@ void ItemLibraryView::modelAttached(Model *model) m_widget->setModel(model); updateImports(); if (model) - m_widget->updatePossibleImports(model->possibleImports()); + m_widget->updatePossibleImports(difference(model->possibleImports(), model->imports())); m_hasErrors = !rewriterView()->errors().isEmpty(); m_widget->setFlowMode(QmlItemNode(rootModelNode()).isFlowView()); } diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp index 1fafc91ea6a..112453fb8d4 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp @@ -368,7 +368,7 @@ void ItemLibraryWidget::handlePriorityImportsChanged() { if (!m_itemLibraryInfo.isNull()) { m_addModuleModel->setPriorityImports(m_itemLibraryInfo->priorityImports()); - m_addModuleModel->update(m_model->possibleImports()); + m_addModuleModel->update(difference(m_model->possibleImports(), m_model->imports())); } } diff --git a/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp b/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp index 9dcf81a1738..9f664429039 100644 --- a/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp +++ b/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp @@ -901,7 +901,8 @@ void NavigatorTreeModel::addImport(const QString &importName) { Import import = Import::createLibraryImport(importName); if (!m_view->model()->hasImport(import, true, true)) { - const Imports possImports = m_view->model()->possibleImports(); + const Imports possImports = difference(m_view->model()->possibleImports(), + m_view->model()->imports()); for (const auto &possImport : possImports) { if (possImport.url() == import.url()) { import = possImport; diff --git a/src/plugins/qmldesigner/designercore/include/import.h b/src/plugins/qmldesigner/designercore/include/import.h index ff9e90f162f..be7c76f492d 100644 --- a/src/plugins/qmldesigner/designercore/include/import.h +++ b/src/plugins/qmldesigner/designercore/include/import.h @@ -3,6 +3,8 @@ #pragma once +#include + #include #include #include @@ -26,16 +28,15 @@ public: bool hasVersion() const { return !m_version.isEmpty(); } bool hasAlias() const { return !m_alias.isEmpty(); } - QString url() const { return m_url; } - QString file() const { return m_file; } - QString version() const { return m_version; } - QString alias() const { return m_alias; } - QStringList importPaths() const { return m_importPathList; } + const QString &url() const { return m_url; } + const QString &file() const { return m_file; } + const QString &version() const { return m_version; } + const QString &alias() const { return m_alias; } + const QStringList &importPaths() const { return m_importPathList; } QString toString(bool skipAlias = false, bool skipVersion = false) const; QString toImportString() const; - bool operator==(const Import &other) const; bool isSameModule(const Import &other) const; int majorVersion() const; @@ -43,6 +44,18 @@ public: static int majorFromVersion(const QString &version); static int minorFromVersion(const QString &version); + friend bool operator==(const Import &first, const Import &second) + { + return first.m_url == second.m_url && first.m_file == second.m_file + && (first.m_version == second.m_version || first.m_version.isEmpty() + || second.m_version.isEmpty()); + } + + friend bool operator<(const Import &first, const Import &second) + { + return std::tie(first.m_url, first.m_file) < std::tie(second.m_url, second.m_file); + } + private: Import(const QString &url, const QString &file, const QString &version, const QString &alias, const QStringList &importPaths); @@ -58,6 +71,21 @@ QMLDESIGNERCORE_EXPORT size_t qHash(const Import &import); using Imports = QList; +QMLDESIGNERCORE_EXPORT Imports difference(const Imports &first, const Imports &second); + +template +void differenceCall(const Imports &first, const Imports &second, Callable callable) +{ + Imports difference; + difference.reserve(first.size()); + + std::set_difference(first.begin(), + first.end(), + second.begin(), + second.end(), + Utils::make_iterator(callable)); +} + } // namespace QmlDesigner Q_DECLARE_METATYPE(QmlDesigner::Import) diff --git a/src/plugins/qmldesigner/designercore/include/model.h b/src/plugins/qmldesigner/designercore/include/model.h index d36086777d8..89391dfe9ed 100644 --- a/src/plugins/qmldesigner/designercore/include/model.h +++ b/src/plugins/qmldesigner/designercore/include/model.h @@ -119,8 +119,8 @@ public: const Imports &possibleImports() const; const Imports &usedImports() const; void changeImports(const Imports &importsToBeAdded, const Imports &importsToBeRemoved); - void setPossibleImports(const Imports &possibleImports); - void setUsedImports(const Imports &usedImports); + void setPossibleImports(Imports possibleImports); + void setUsedImports(Imports usedImports); bool hasImport(const Import &import, bool ignoreAlias = true, bool allowHigherVersion = false) const; bool isImportPossible(const Import &import, bool ignoreAlias = true, bool allowHigherVersion = false) const; QString pathForImport(const Import &import); diff --git a/src/plugins/qmldesigner/designercore/model/import.cpp b/src/plugins/qmldesigner/designercore/model/import.cpp index 3a6914d3258..b9e7a7a9e24 100644 --- a/src/plugins/qmldesigner/designercore/model/import.cpp +++ b/src/plugins/qmldesigner/designercore/model/import.cpp @@ -62,11 +62,6 @@ QString Import::toString(bool skipAlias, bool skipVersion) const return result; } -bool Import::operator==(const Import &other) const -{ - return url() == other.url() && file() == other.file() && (version() == other.version() || version().isEmpty() || other.version().isEmpty()); -} - bool Import::isSameModule(const Import &other) const { if (isLibraryImport()) @@ -107,4 +102,17 @@ size_t qHash(const Import &import) return ::qHash(import.url()) ^ ::qHash(import.file()) ^ ::qHash(import.version()) ^ ::qHash(import.alias()); } +Imports difference(const Imports &first, const Imports &second) +{ + Imports difference; + difference.reserve(first.size()); + + std::set_difference(first.begin(), + first.end(), + second.begin(), + second.end(), + std::back_inserter(difference)); + + return difference; +} } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/model/model.cpp b/src/plugins/qmldesigner/designercore/model/model.cpp index 33571cc3ce1..d410a3c87aa 100644 --- a/src/plugins/qmldesigner/designercore/model/model.cpp +++ b/src/plugins/qmldesigner/designercore/model/model.cpp @@ -127,6 +127,8 @@ void ModelPrivate::changeImports(const Imports &toBeAddedImportList, } } + std::sort(m_imports.begin(), m_imports.end()); + if (!removedImportList.isEmpty() || !addedImportList.isEmpty()) notifyImportsChanged(addedImportList, removedImportList); } @@ -1419,19 +1421,23 @@ void Model::changeImports(const Imports &importsToBeAdded, const Imports &import d->changeImports(importsToBeAdded, importsToBeRemoved); } -void Model::setPossibleImports(const Imports &possibleImports) +void Model::setPossibleImports(Imports possibleImports) { + std::sort(possibleImports.begin(), possibleImports.end()); + if (d->m_possibleImportList != possibleImports) { - d->m_possibleImportList = possibleImports; - d->notifyPossibleImportsChanged(possibleImports); + d->m_possibleImportList = std::move(possibleImports); + d->notifyPossibleImportsChanged(d->m_possibleImportList); } } -void Model::setUsedImports(const Imports &usedImports) +void Model::setUsedImports(Imports usedImports) { + std::sort(usedImports.begin(), usedImports.end()); + if (d->m_usedImportList != usedImports) { - d->m_usedImportList = usedImports; - d->notifyUsedImportsChanged(usedImports); + d->m_usedImportList = std::move(usedImports); + d->notifyUsedImportsChanged(d->m_usedImportList); } } diff --git a/src/plugins/qmldesigner/designercore/model/model_p.h b/src/plugins/qmldesigner/designercore/model/model_p.h index 95985482548..8cdaaac3ecb 100644 --- a/src/plugins/qmldesigner/designercore/model/model_p.h +++ b/src/plugins/qmldesigner/designercore/model/model_p.h @@ -203,8 +203,6 @@ public: // Imports: const Imports &imports() const { return m_imports; } - void addImport(const Import &import); - void removeImport(const Import &import); void changeImports(const Imports &importsToBeAdded, const Imports &importToBeRemoved); void notifyImportsChanged(const Imports &addedImports, const Imports &removedImports); void notifyPossibleImportsChanged(const Imports &possibleImports); From b1f55d5a6a23f3dd6b02691ee4d0ddf68cc119b4 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Thu, 20 Apr 2023 15:06:16 +0300 Subject: [PATCH 068/192] Fix binding assignment failing sometimes Change-Id: Ic94cc911411d3162cd840feba86a445a4bc76c8e Reviewed-by: Qt CI Bot Reviewed-by: Thomas Hartmann --- .../qml2puppet/instances/objectnodeinstance.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/tools/qml2puppet/qml2puppet/instances/objectnodeinstance.cpp b/src/tools/qml2puppet/qml2puppet/instances/objectnodeinstance.cpp index fc171c87723..8a982f5cdd5 100644 --- a/src/tools/qml2puppet/qml2puppet/instances/objectnodeinstance.cpp +++ b/src/tools/qml2puppet/qml2puppet/instances/objectnodeinstance.cpp @@ -507,6 +507,17 @@ void ObjectNodeInstance::setPropertyBinding(const PropertyName &name, const QStr bool qmlExpressionHasError = false; + QStringList idlist; + for (const auto &instance : nodeInstanceServer()->nodeInstances()) + idlist.append(instance.id()); + + // Always set ids using the root context, since they are defined there anyway + if (idlist.contains(expression)) { + QmlPrivateGate::setPropertyBinding(object(), + context()->engine()->rootContext(), name, expression); + return; + } + if (!isPropertyChange()) { QQmlExpression qmlExpression(context(), object(), expression); qmlExpression.evaluate(); From 35582ffcb57cf5f022d7b00dd724776eef2c9600 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Thu, 20 Apr 2023 16:30:24 +0300 Subject: [PATCH 069/192] QmlDesigner: Render main scene in information puppet Upcoming changes in quick3d require rendering of the main scene at least once to resolve rendering context corretly. Task-number: QDS-9699 Change-Id: I77520309029730f306adf24694ee357d29d27209 Reviewed-by: Mahmoud Badri --- .../instances/qt5informationnodeinstanceserver.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp b/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp index 36c24d85733..87936392972 100644 --- a/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp +++ b/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp @@ -1071,6 +1071,13 @@ void Qt5InformationNodeInstanceServer::doRender3DEditView() m_editView3DData.window->afterRendering(); } #else +#if QT_VERSION >= QT_VERSION_CHECK(6, 5, 1) + static bool justOnce = true; + if (justOnce) { + justOnce = false; + renderWindow(); // Need to make sure all View3Ds have context + } +#endif renderImage = grabRenderControl(m_editView3DData); #endif From 50aa8fedd246a33ac53ccc78adefb757dad0de23 Mon Sep 17 00:00:00 2001 From: Fabian Kosmale Date: Thu, 20 Apr 2023 14:51:21 +0200 Subject: [PATCH 070/192] FileDownloader: Do not assume existence of QQmlData MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit QQmlData::wasDeleted returns a sensible result, even when the QQmlData has not been created (for instance because the object was not instantiated by the QML engine). Checking the result of QQmlData::get is counterproductive, as FileDownloader objects are for instance created by ContentLibraryMaterialsModel::fetchBundleIcons, without going through the QML engine. Moreover, try to log some information about why we ended up with a network error. Task-number: QDS-9687 Change-Id: Icf7fba94a8a0552305ac39f2caa32426e58972a0 Reviewed-by: Henning Gründl Reviewed-by: Qt CI Bot --- src/plugins/qmldesigner/utils/filedownloader.cpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/plugins/qmldesigner/utils/filedownloader.cpp b/src/plugins/qmldesigner/utils/filedownloader.cpp index b5c735d87a8..20ba2b3eec9 100644 --- a/src/plugins/qmldesigner/utils/filedownloader.cpp +++ b/src/plugins/qmldesigner/utils/filedownloader.cpp @@ -258,18 +258,16 @@ void FileDownloader::doProbeUrl() QNetworkReply::connect(reply, &QNetworkReply::errorOccurred, this, - [this](QNetworkReply::NetworkError) { - QQmlData *data = QQmlData::get(this, false); - if (!data) { - qDebug() << Q_FUNC_INFO << "FileDownloader is nullptr."; - return; - } + [this](QNetworkReply::NetworkError code) { if (QQmlData::wasDeleted(this)) { qDebug() << Q_FUNC_INFO << "FileDownloader was deleted."; return; } + qDebug() << Q_FUNC_INFO << "Network error:" << code + << qobject_cast(sender())->errorString(); + m_available = false; emit availableChanged(); }); From 43bf597611b68fb37d200086a66f88ef69ac618c Mon Sep 17 00:00:00 2001 From: Henning Gruendl Date: Thu, 20 Apr 2023 17:40:13 +0200 Subject: [PATCH 071/192] QmlDesigner: Fix leak in FileDownloader Fix QNetworkReply in FileDownloader not being deleted. Change-Id: I037a00b4351029e28b887ecbd4205b743284a6f7 Reviewed-by: Fabian Kosmale --- src/plugins/qmldesigner/utils/filedownloader.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/plugins/qmldesigner/utils/filedownloader.cpp b/src/plugins/qmldesigner/utils/filedownloader.cpp index 20ba2b3eec9..7825be5081b 100644 --- a/src/plugins/qmldesigner/utils/filedownloader.cpp +++ b/src/plugins/qmldesigner/utils/filedownloader.cpp @@ -253,6 +253,8 @@ void FileDownloader::doProbeUrl() m_available = true; emit availableChanged(); + + reply->deleteLater(); }); QNetworkReply::connect(reply, From 3387c68ff2daa1ff6aa78f9536feb0554d4fd878 Mon Sep 17 00:00:00 2001 From: Knud Dollereder Date: Thu, 23 Feb 2023 10:34:53 +0100 Subject: [PATCH 072/192] Add MCU build step to the QmlBuildSystem If a qml project is marked as qtForMCU the buildstep will run the qmlprojectexporter on the currently active qmlproject when pressing the "Run" button. The output messages of the tool will then show up in the "Compile Output" panel. The build step itself can be seen when switching to "Projects" mode under "Deployment". Change-Id: I5ac31d5655e3b4b6137aaf541839776f144a09c4 Reviewed-by: Thomas Hartmann Reviewed-by: Burak Hancerli --- src/plugins/qmlprojectmanager/CMakeLists.txt | 3 +- .../buildsystem/qmlbuildsystem.cpp | 7 +- .../qmlprojectmanager/mcubuildstep.cpp | 197 ++++++++++++++++++ src/plugins/qmlprojectmanager/mcubuildstep.h | 42 ++++ .../qmlprojectmanager/qmlprojectplugin.cpp | 2 + 5 files changed, 249 insertions(+), 2 deletions(-) create mode 100644 src/plugins/qmlprojectmanager/mcubuildstep.cpp create mode 100644 src/plugins/qmlprojectmanager/mcubuildstep.h diff --git a/src/plugins/qmlprojectmanager/CMakeLists.txt b/src/plugins/qmlprojectmanager/CMakeLists.txt index 3b7209d05c7..9b6e7609176 100644 --- a/src/plugins/qmlprojectmanager/CMakeLists.txt +++ b/src/plugins/qmlprojectmanager/CMakeLists.txt @@ -1,7 +1,7 @@ add_qtc_plugin(QmlProjectManager CONDITION TARGET Qt5::QuickWidgets PLUGIN_CLASS QmlProjectPlugin - DEPENDS QmlJS Qt5::QuickWidgets Utils + DEPENDS QmlJS Qt5::QuickWidgets Utils McuSupport CMakeProjectManager PLUGIN_DEPENDS Core ProjectExplorer QtSupport QmlDesignerBase SOURCES qmlprojectgen/qmlprojectgenerator.cpp qmlprojectgen/qmlprojectgenerator.h @@ -21,6 +21,7 @@ add_qtc_plugin(QmlProjectManager qmlprojectplugin.cpp qmlprojectplugin.h qmlprojectrunconfiguration.cpp qmlprojectrunconfiguration.h buildsystem/qmlbuildsystem.cpp buildsystem/qmlbuildsystem.h + mcubuildstep.cpp mcubuildstep.h "${PROJECT_SOURCE_DIR}/src/share/3rdparty/studiofonts/studiofonts.qrc" ) diff --git a/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.cpp b/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.cpp index 869e5b89c74..0872f24ede5 100644 --- a/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.cpp +++ b/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.cpp @@ -3,6 +3,7 @@ #include "qmlbuildsystem.h" #include "qmlprojectconstants.h" +#include "mcubuildstep.h" #include #include @@ -49,7 +50,11 @@ QmlBuildSystem::QmlBuildSystem(Target *target) updateDeploymentData(); registerMenuButtons(); - connect(target->project(), &Project::activeTargetChanged, [this]() { refresh(RefreshOptions::NoFileRefresh); }); + connect(target->project(), &Project::activeTargetChanged, [this](Target *target) { + refresh(RefreshOptions::NoFileRefresh); + if (qtForMCUs()) + MCUBuildStepFactory::attachToTarget(target); + }); connect(target->project(), &Project::projectFileIsDirty, [this]() { refresh(RefreshOptions::Project); }); diff --git a/src/plugins/qmlprojectmanager/mcubuildstep.cpp b/src/plugins/qmlprojectmanager/mcubuildstep.cpp new file mode 100644 index 00000000000..7ee5bc83939 --- /dev/null +++ b/src/plugins/qmlprojectmanager/mcubuildstep.cpp @@ -0,0 +1,197 @@ +// Copyright (C) 2023 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 "mcubuildstep.h" + +#include "projectexplorer/buildstep.h" +#include "projectexplorer/buildsystem.h" +#include "projectexplorer/buildsteplist.h" +#include "projectexplorer/deployconfiguration.h" +#include "projectexplorer/kit.h" +#include "projectexplorer/target.h" +#include "projectexplorer/kitmanager.h" + +#include +#include + +#include "qtsupport/qtsupportconstants.h" +#include "mcusupport/mcusupportconstants.h" +#include "mcusupport/mculegacyconstants.h" + +#include "utils/aspects.h" +#include "utils/filepath.h" + +#include + +namespace QmlProjectManager { + +const Utils::Id DeployMcuProcessStep::id = "QmlProject.Mcu.DeployStep"; +const QString DeployMcuProcessStep::processCommandKey = "QmlProject.Mcu.ProcessStep.Command"; +const QString DeployMcuProcessStep::processArgumentsKey = "QmlProject.Mcu.ProcessStep.Arguments"; +const QString DeployMcuProcessStep::processWorkingDirectoryKey = "QmlProject.Mcu.ProcessStep.BuildDirectory"; + +void DeployMcuProcessStep::showError(const QString& text) { + Core::AsynchronousMessageBox::critical(tr("Qt4MCU Deploy Step"), text); +} + +// TODO: +// - Grabbing *a* kit might not be the best todo. +// Would be better to specify a specific version of Qt4MCU in the qmlproject file. +// Currently we use the kit with the greatest version number. +// +// - Do not compare to *legacy* constants. +// Sounds like they will stop existing at some point. +// Also: Find Constant for QUL_PLATFORM + +DeployMcuProcessStep::DeployMcuProcessStep(ProjectExplorer::BuildStepList *bc, Utils::Id id) + : AbstractProcessStep(bc, id) + , m_tmpDir() +{ + if (not buildSystem()) { + showError(QObject::tr("Failed to find valid build system")); + return; + } + + if (not m_tmpDir.isValid()) { + showError(QObject::tr("Failed to create valid build directory")); + return; + } + + auto fixPath = [](const QString& path) -> QString { + return "\"" + QDir::toNativeSeparators(path) + "\""; + }; + + ProjectExplorer::Kit* kit = MCUBuildStepFactory::findMostRecentQulKit(); + if (not kit) + return; + + QString root = findKitInformation(kit, McuSupport::Internal::Legacy::Constants::QUL_CMAKE_VAR); + + auto* cmd = addAspect(); + cmd->setSettingsKey(processCommandKey); + cmd->setDisplayStyle(Utils::StringAspect::PathChooserDisplay); + cmd->setExpectedKind(Utils::PathChooser::Command); + cmd->setLabelText(tr("Command:")); + cmd->setValue(QDir::toNativeSeparators(root + "/bin/qmlprojectexporter")); + + const char* importPathConstant = QtSupport::Constants::KIT_QML_IMPORT_PATH; + QString projectDir = buildSystem()->projectDirectory().toString(); + QString qulIncludeDir = kit->value(importPathConstant).toString( ); + QStringList includeDirs { + fixPath(qulIncludeDir), + fixPath(qulIncludeDir + "/Timeline"), + fixPath(projectDir + "/imports") + }; + + const char* toolChainConstant = McuSupport::Internal::Constants::KIT_MCUTARGET_TOOLCHAIN_KEY; + QStringList arguments = { + fixPath(buildSystem()->projectFilePath().toString()), + "--platform", findKitInformation(kit, "QUL_PLATFORM"), + "--toolchain", kit->value(toolChainConstant).toString( ), + "--include-dirs", includeDirs.join(","), + }; + + auto* args = addAspect(); + args->setSettingsKey(processArgumentsKey); + args->setDisplayStyle(Utils::StringAspect::LineEditDisplay); + args->setLabelText(tr("Arguments:")); + args->setValue(arguments.join(" ")); + + auto* outDir = addAspect(); + outDir->setSettingsKey(processWorkingDirectoryKey); + outDir->setDisplayStyle(Utils::StringAspect::PathChooserDisplay); + outDir->setExpectedKind(Utils::PathChooser::Directory); + outDir->setLabelText(tr("Build directory:")); + outDir->setPlaceHolderText(fixPath(m_tmpDir.path())); + + setCommandLineProvider([this, cmd, args, outDir, fixPath]() -> Utils::CommandLine { + auto directory = outDir->value(); + if (directory.isEmpty()) + directory = fixPath(m_tmpDir.path()); + + QString outArg = " --outdir " + directory; + return {cmd->filePath(), args->value() + outArg, Utils::CommandLine::Raw}; + }); +} + +bool DeployMcuProcessStep::init() +{ + if (!AbstractProcessStep::init()) + return false; + return true; +} + +void DeployMcuProcessStep::doRun() +{ + AbstractProcessStep::doRun(); +} + +QString DeployMcuProcessStep::findKitInformation(ProjectExplorer::Kit* kit, const QString& key) +{ + // This is (kind of) stolen from mcukitmanager.cpp. Might make sense to unify. + using namespace CMakeProjectManager; + const auto config = CMakeConfigurationKitAspect::configuration(kit).toList(); + const auto keyName = key.toUtf8(); + for (const CMakeProjectManager::CMakeConfigItem &configItem : config) { + if (configItem.key == keyName) + return QString::fromUtf8(configItem.value); + } + return {}; +} + + +MCUBuildStepFactory::MCUBuildStepFactory() + : BuildStepFactory() +{ + setDisplayName("Qt4MCU Deploy Step"); + registerStep< DeployMcuProcessStep >(DeployMcuProcessStep::id); +} + +void MCUBuildStepFactory::attachToTarget(ProjectExplorer::Target *target) +{ + if (not target) + return; + + ProjectExplorer::DeployConfiguration* deployConfiguration = target->activeDeployConfiguration(); + ProjectExplorer::BuildStepList* stepList = deployConfiguration->stepList(); + if (stepList->contains(DeployMcuProcessStep::id)) + return; + + if (not findMostRecentQulKit()) { + DeployMcuProcessStep::showError(QObject::tr("Failed to find valid Qt4MCU kit")); + return; + } + + for (BuildStepFactory *factory : BuildStepFactory::allBuildStepFactories()) { + if (factory->stepId() == DeployMcuProcessStep::id) { + ProjectExplorer::BuildStep* deployConfig = factory->create(stepList); + stepList->appendStep(deployConfig); + } + } +} + +ProjectExplorer::Kit* MCUBuildStepFactory::findMostRecentQulKit() +{ + // Stolen from mcukitmanager.cpp + auto kitQulVersion = [](const ProjectExplorer::Kit *kit) -> QVersionNumber { + const char* sdkVersion = McuSupport::Internal::Constants::KIT_MCUTARGET_SDKVERSION_KEY; + return QVersionNumber::fromString(kit->value(sdkVersion).toString()); + }; + + ProjectExplorer::Kit* kit = nullptr; + for (auto k : ProjectExplorer::KitManager::kits()) + { + auto qulVersion = kitQulVersion(k); + if (qulVersion.isNull( )) + continue; + + if (not kit) + kit = k; + + if (qulVersion > kitQulVersion(kit)) + kit = k; + } + return kit; +} + +} // namespace QmlProjectManager diff --git a/src/plugins/qmlprojectmanager/mcubuildstep.h b/src/plugins/qmlprojectmanager/mcubuildstep.h new file mode 100644 index 00000000000..e1167e1917a --- /dev/null +++ b/src/plugins/qmlprojectmanager/mcubuildstep.h @@ -0,0 +1,42 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0 +#pragma once + +#include "projectexplorer/kit.h" +#include "projectexplorer/project.h" +#include +#include +#include + +#include + +namespace QmlProjectManager { + +class DeployMcuProcessStep : public ProjectExplorer::AbstractProcessStep +{ +public: + static const Utils::Id id; + static void showError(const QString& text); + + DeployMcuProcessStep(ProjectExplorer::BuildStepList *bc, Utils::Id id); + +private: + bool init() override; + void doRun() override; + QString findKitInformation(ProjectExplorer::Kit* kit, const QString& key); + + static const QString processCommandKey; + static const QString processArgumentsKey; + static const QString processWorkingDirectoryKey; + QTemporaryDir m_tmpDir; +}; + +class MCUBuildStepFactory : public ProjectExplorer::BuildStepFactory +{ +public: + MCUBuildStepFactory(); + static void attachToTarget(ProjectExplorer::Target *target); + static ProjectExplorer::Kit* findMostRecentQulKit( ); +}; + +} // namespace QmlProjectManager diff --git a/src/plugins/qmlprojectmanager/qmlprojectplugin.cpp b/src/plugins/qmlprojectmanager/qmlprojectplugin.cpp index 0a87ef9bc90..dd33508636c 100644 --- a/src/plugins/qmlprojectmanager/qmlprojectplugin.cpp +++ b/src/plugins/qmlprojectmanager/qmlprojectplugin.cpp @@ -1,6 +1,7 @@ // Copyright (C) 2016 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +#include "mcubuildstep.h" #include "qdslandingpage.h" #include "qmlprojectplugin.h" #include "qmlproject.h" @@ -93,6 +94,7 @@ public: QPointer lastMessageBox; QdsLandingPage *landingPage = nullptr; QdsLandingPageWidget *landingPageWidget = nullptr; + MCUBuildStepFactory mcuBuildStepFactory; }; QmlProjectPlugin::~QmlProjectPlugin() From 22f08280f5224c57928b24712fa30be845293a55 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Thu, 20 Apr 2023 18:49:46 +0200 Subject: [PATCH 073/192] QmlDesigner: Launch feedback dialog only from design mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Task-number: QDS-9641 Change-Id: I1d2523c8cd019092b4c64fdefeb326e0751bbb37 Reviewed-by: Henning Gründl --- src/plugins/qmldesigner/qmldesignerplugin.cpp | 20 ++++++++++++------- src/plugins/qmldesigner/qmldesignerplugin.h | 1 + 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/src/plugins/qmldesigner/qmldesignerplugin.cpp b/src/plugins/qmldesigner/qmldesignerplugin.cpp index 47c20047aa6..b7d7000be4b 100644 --- a/src/plugins/qmldesigner/qmldesignerplugin.cpp +++ b/src/plugins/qmldesigner/qmldesignerplugin.cpp @@ -243,7 +243,7 @@ bool QmlDesignerPlugin::initialize(const QStringList & /*arguments*/, QString *e ->addAction(cmd, Core::Constants::G_HELP_SUPPORT); connect(action, &QAction::triggered, this, [this] { - lauchFeedbackPopup(Core::Constants::IDE_DISPLAY_NAME); + lauchFeedbackPopupInternal(Core::Constants::IDE_DISPLAY_NAME); }); if (!Utils::HostOsInfo::canCreateOpenGLContext(errorMessage)) @@ -732,6 +732,18 @@ void QmlDesignerPlugin::trackWidgetFocusTime(QWidget *widget, const QString &ide } void QmlDesignerPlugin::lauchFeedbackPopup(const QString &identifier) +{ + if (Core::ModeManager::currentModeId() == Core::Constants::MODE_DESIGN) + lauchFeedbackPopupInternal(identifier); +} + +void QmlDesignerPlugin::handleFeedback(const QString &feedback, int rating) +{ + const QString identifier = sender()->property("identifier").toString(); + emit usageStatisticsInsertFeedback(identifier, feedback, rating); +} + +void QmlDesignerPlugin::lauchFeedbackPopupInternal(const QString &identifier) { m_feedbackWidget = new QQuickWidget(Core::ICore::dialogParent()); m_feedbackWidget->setObjectName(Constants::OBJECT_NAME_TOP_FEEDBACK); @@ -769,12 +781,6 @@ void QmlDesignerPlugin::lauchFeedbackPopup(const QString &identifier) m_feedbackWidget->show(); } -void QmlDesignerPlugin::handleFeedback(const QString &feedback, int rating) -{ - const QString identifier = sender()->property("identifier").toString(); - emit usageStatisticsInsertFeedback(identifier, feedback, rating); -} - void QmlDesignerPlugin::closeFeedbackPopup() { if (m_feedbackWidget) { diff --git a/src/plugins/qmldesigner/qmldesignerplugin.h b/src/plugins/qmldesigner/qmldesignerplugin.h index 6251317bc42..ee078b273ab 100644 --- a/src/plugins/qmldesigner/qmldesignerplugin.h +++ b/src/plugins/qmldesigner/qmldesignerplugin.h @@ -95,6 +95,7 @@ private slots: void handleFeedback(const QString &feedback, int rating); private: // functions + void lauchFeedbackPopupInternal(const QString &identifier); void integrateIntoQtCreator(QWidget *modeWidget); void showDesigner(); void hideDesigner(); From 9ac4cd4e4a9f2411aba3aedf3fb1c56303c676ca Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Wed, 5 Apr 2023 19:37:59 +0200 Subject: [PATCH 074/192] QmlDesigner: Add more checks for kit when adding import paths Especially if developing we do not want to parse all kits to slow down startup time. Change-Id: If49624941b611b7e8b19864bdd3f31b884c04514 Reviewed-by: Qt CI Bot Reviewed-by: Tim Jenssen Reviewed-by: --- src/plugins/studiowelcome/studiowelcomeplugin.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/plugins/studiowelcome/studiowelcomeplugin.cpp b/src/plugins/studiowelcome/studiowelcomeplugin.cpp index a3a5421cf0d..5fc4dd7875d 100644 --- a/src/plugins/studiowelcome/studiowelcomeplugin.cpp +++ b/src/plugins/studiowelcome/studiowelcomeplugin.cpp @@ -651,9 +651,11 @@ bool StudioWelcomePlugin::delayedInitialize() const QList kits = Utils::filtered(KitManager::kits(), [](const Kit *k) { const QtSupport::QtVersion *version = QtSupport::QtKitAspect::qtVersion(k); - const bool isQt6 = version && version->qtVersion().majorVersion() == 6; + const bool valid = version && version->isValid(); + const bool isQt6 = valid && version->qtVersion().majorVersion() == 6; + const bool autoDetected = valid && version->isAutodetected(); - return isQt6 + return isQt6 && autoDetected && ProjectExplorer::DeviceTypeKitAspect::deviceTypeId(k) == ProjectExplorer::Constants::DESKTOP_DEVICE_TYPE; }); From 924149e5159fdaf212ea3c9b866a419844b31cbb Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Thu, 20 Apr 2023 18:46:56 +0200 Subject: [PATCH 075/192] QmlDesigner: Use simplified name for Connections MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Task-number: QDS-9737 Change-Id: I1883632cf3029263b28095cc481e45a41c0d89d1 Reviewed-by: Henning Gründl --- .../components/connectioneditor/connectionmodel.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp index b3eb7542f8a..25a5756c28e 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp @@ -38,10 +38,7 @@ QStringList propertyNameListToStringList(const QmlDesigner::PropertyNameList &pr bool isConnection(const QmlDesigner::ModelNode &modelNode) { - return (modelNode.type() == "Connections" - || modelNode.type() == "QtQuick.Connections" - || modelNode.type() == "Qt.Connections" - || modelNode.type() == "QtQml.Connections"); + return (modelNode.metaInfo().simplifiedTypeName() == "Connections"); } } //namespace From 151423ab16b9bbd5c7bca2f82946306816f155d0 Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Thu, 20 Apr 2023 14:15:51 +0300 Subject: [PATCH 076/192] QmlDesigner: Remove transient scrollbars The concept of the transient scrollbar still exists, but all used cases are removed. Task-number: QDS-9735 Change-Id: I16c57635a5eeb114b906ab74bbf24a8693897557 Reviewed-by: Thomas Hartmann --- .../components/formeditor/formeditorgraphicsview.cpp | 2 -- .../qmldesigner/components/texteditor/texteditorwidget.cpp | 5 +++++ src/plugins/qmldesigner/designmodewidget.cpp | 1 + src/plugins/qmldesignerbase/studio/studiostyle.cpp | 6 ------ src/plugins/texteditor/texteditor.cpp | 2 -- 5 files changed, 6 insertions(+), 10 deletions(-) diff --git a/src/plugins/qmldesigner/components/formeditor/formeditorgraphicsview.cpp b/src/plugins/qmldesigner/components/formeditor/formeditorgraphicsview.cpp index a9401480f3a..1e67884ad8d 100644 --- a/src/plugins/qmldesigner/components/formeditor/formeditorgraphicsview.cpp +++ b/src/plugins/qmldesigner/components/formeditor/formeditorgraphicsview.cpp @@ -6,7 +6,6 @@ #include "formeditorwidget.h" #include "navigation2d.h" #include -#include #include #include @@ -37,7 +36,6 @@ FormEditorGraphicsView::FormEditorGraphicsView(QWidget *parent) setBackgroundRole(QPalette::Window); activateCheckboardBackground(); - Utils::TransientScrollAreaSupport::support(this); // as mousetracking only works for mouse key it is better to handle it in the // eventFilter method so it works also for the space scrolling case as expected diff --git a/src/plugins/qmldesigner/components/texteditor/texteditorwidget.cpp b/src/plugins/qmldesigner/components/texteditor/texteditorwidget.cpp index 122228f2086..9f4bb59231e 100644 --- a/src/plugins/qmldesigner/components/texteditor/texteditorwidget.cpp +++ b/src/plugins/qmldesigner/components/texteditor/texteditorwidget.cpp @@ -70,6 +70,11 @@ void TextEditorWidget::setTextEditor(Utils::UniqueObjectLatePtreditorWidget()->installEventFilter(this); + + static QString styleSheet = Theme::replaceCssColors( + QString::fromUtf8(Utils::FileReader::fetchQrc(":/qmldesigner/scrollbar.css"))); + m_textEditor->editorWidget()->verticalScrollBar()->setStyleSheet(styleSheet); + m_textEditor->editorWidget()->horizontalScrollBar()->setStyleSheet(styleSheet); } } diff --git a/src/plugins/qmldesigner/designmodewidget.cpp b/src/plugins/qmldesigner/designmodewidget.cpp index 40e455f40c2..913fb05f0ba 100644 --- a/src/plugins/qmldesigner/designmodewidget.cpp +++ b/src/plugins/qmldesigner/designmodewidget.cpp @@ -186,6 +186,7 @@ void DesignModeWidget::setup() Core::ICore::resourcePath("qmldesigner/workspacePresets/").toString()); QString sheet = QString::fromUtf8(Utils::FileReader::fetchQrc(":/qmldesigner/dockwidgets.css")); + sheet += QString::fromUtf8(Utils::FileReader::fetchQrc(":/qmldesigner/scrollbar.css")); m_dockManager->setStyleSheet(Theme::replaceCssColors(sheet)); // Setup icons diff --git a/src/plugins/qmldesignerbase/studio/studiostyle.cpp b/src/plugins/qmldesignerbase/studio/studiostyle.cpp index 4b2f1132274..ffe652e363e 100644 --- a/src/plugins/qmldesignerbase/studio/studiostyle.cpp +++ b/src/plugins/qmldesignerbase/studio/studiostyle.cpp @@ -888,12 +888,6 @@ int StudioStyle::styleHint( const QWidget *widget, QStyleHintReturn *returnData) const { - switch (hint) { - case SH_ScrollBar_Transient: - return true; - default: - break; - } return Super::styleHint(hint, option, widget, returnData); } diff --git a/src/plugins/texteditor/texteditor.cpp b/src/plugins/texteditor/texteditor.cpp index f348aa7ce61..7e50d469271 100644 --- a/src/plugins/texteditor/texteditor.cpp +++ b/src/plugins/texteditor/texteditor.cpp @@ -66,7 +66,6 @@ #include #include #include -#include #include #include @@ -1145,7 +1144,6 @@ TextEditorWidget::TextEditorWidget(QWidget *parent) setLayoutDirection(Qt::LeftToRight); viewport()->setMouseTracking(true); setFrameStyle(QFrame::NoFrame); - TransientScrollAreaSupport::support(this); } void TextEditorWidget::setTextDocument(const QSharedPointer &doc) From 3abf63b92120f09befb6a1f06b95be82acbb7557 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Fri, 21 Apr 2023 13:59:00 +0200 Subject: [PATCH 077/192] QmlDesigner: Add missing return Task-number: QDS-9739 Change-Id: I4b48aaa72a906954ceb96b71e2b20efb76ac3d42 Reviewed-by: Tim Jenssen --- src/plugins/qmldesigner/qmldesignerexternaldependencies.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/plugins/qmldesigner/qmldesignerexternaldependencies.cpp b/src/plugins/qmldesigner/qmldesignerexternaldependencies.cpp index 107d021d64f..d1eaae8d7e2 100644 --- a/src/plugins/qmldesigner/qmldesignerexternaldependencies.cpp +++ b/src/plugins/qmldesigner/qmldesignerexternaldependencies.cpp @@ -239,6 +239,8 @@ QStringList ExternalDependencies::modulePaths() const for (const QString &modulePath : qmlBuildSystem->customImportPaths()) modulePaths.append(project->projectDirectory().pathAppended(modulePath).toString()); + + return modulePaths; } return {}; From 81cb581e4ac11ae3e2a7d730fc26592f667736c3 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Fri, 21 Apr 2023 14:01:33 +0200 Subject: [PATCH 078/192] QmlDesigner: Avoid duplicate module names We get every qmldir file twice and some files report QtQuick multiple times. This needs to be revisited. Change-Id: I2271f71ee7da2c182fed468fa3ef8aca63785bb4 Reviewed-by: Tim Jenssen --- .../designercore/projectstorage/modulescanner.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/plugins/qmldesigner/designercore/projectstorage/modulescanner.cpp b/src/plugins/qmldesigner/designercore/projectstorage/modulescanner.cpp index ffcf28d7658..4a6adfa10d5 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/modulescanner.cpp +++ b/src/plugins/qmldesigner/designercore/projectstorage/modulescanner.cpp @@ -9,6 +9,7 @@ #include #include +#include namespace QmlDesigner { @@ -50,6 +51,8 @@ void ModuleScanner::scan([[maybe_unused]] std::string_view modulePath) #ifdef QDS_HAS_QMLPRIVATE QDirIterator dirIterator{QString::fromUtf8(modulePath), QDir::Dirs, QDirIterator::Subdirectories}; + QMap moduleNames; + while (dirIterator.hasNext()) { auto directoryPath = dirIterator.next(); QString qmldirPath = directoryPath + "/qmldir"; @@ -69,6 +72,10 @@ void ModuleScanner::scan([[maybe_unused]] std::string_view modulePath) if (moduleName.isEmpty() || m_skip(moduleName)) continue; + if (moduleNames.contains(moduleName)) + continue; + + moduleNames.insert(moduleName, true); m_modules.push_back( Import::createLibraryImport(moduleName, createVersion(parser.components()))); } From 94256c52f218cd92ec50e432be50f11b65303733 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Fri, 21 Apr 2023 14:14:48 +0200 Subject: [PATCH 079/192] QmlDesigner: Fix crash on shutdown The style is owned by QApplication and QApplication does not expect the style to be deleted by QmlDesignerBasePlugin. Change-Id: Ia6526cf9920646a8a61d8c9847ab495fad382ea7 Reviewed-by: Tim Jenssen --- src/plugins/qmldesignerbase/qmldesignerbaseplugin.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/plugins/qmldesignerbase/qmldesignerbaseplugin.cpp b/src/plugins/qmldesignerbase/qmldesignerbaseplugin.cpp index 2a2759c3007..c9af4f22836 100644 --- a/src/plugins/qmldesignerbase/qmldesignerbaseplugin.cpp +++ b/src/plugins/qmldesignerbase/qmldesignerbaseplugin.cpp @@ -20,7 +20,7 @@ class QmlDesignerBasePlugin::Data { public: DesignerSettings settings; - Utils::UniqueObjectPtr style; + StudioStyle *style = nullptr; StudioConfigSettingsPage studioConfigSettingsPage; Data() @@ -47,9 +47,9 @@ DesignerSettings &QmlDesignerBasePlugin::settings() QStyle *QmlDesignerBasePlugin::style() { if (!global->d->style) - global->d->style = Utils::makeUniqueObjectPtr(QApplication::style()); + global->d->style = new StudioStyle(QApplication::style()); - return global->d->style.get(); + return global->d->style; } StudioConfigSettingsPage *QmlDesignerBasePlugin::studioConfigSettingsPage() From 94ab3edb37601f83897749b61f78c38807d46bd0 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Thu, 20 Apr 2023 19:12:47 +0200 Subject: [PATCH 080/192] QmlDesigner: Add .qsb files to project Since they are compiled shaders I moved them to a separate filter. Task-number: QDS-9718 Change-Id: I23346ef180419feabedd018ed0c79123f6a3d7f7 Reviewed-by: Miikka Heikkinen --- .../studio_templates/projects/common/app.qmlproject.tpl | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/common/app.qmlproject.tpl b/share/qtcreator/qmldesigner/studio_templates/projects/common/app.qmlproject.tpl index a6a536d4b2d..9cfbab9228b 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/common/app.qmlproject.tpl +++ b/share/qtcreator/qmldesigner/studio_templates/projects/common/app.qmlproject.tpl @@ -59,6 +59,10 @@ Project { filter: "*.glsl;*.glslv;*.glslf;*.vsh;*.fsh;*.vert;*.frag" } + Files { + filter: "*.qsb" + } + Files { filter: "*.mesh" directory: "asset_imports" From b0c3377f1daaee0cdaf9a7b695e9391226d50c66 Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Thu, 20 Apr 2023 11:11:52 +0300 Subject: [PATCH 081/192] QmlDesigner: Fix the crash caused by calling GlobalAnnotations - Calling global annotations does nothing if we are not in the design mode - Related actions and buttons are blocked while we are not in the design mode Task-number: QDS-9729 Change-Id: Ib4d5e8c5adcbb080f42a2344cd76ce7c36532240 Reviewed-by: Qt CI Bot Reviewed-by: Tim Jenssen --- share/qtcreator/qmldesigner/toolbar/Main.qml | 1 + .../components/toolbar/toolbarbackend.cpp | 2 ++ src/plugins/qmldesigner/shortcutmanager.cpp | 13 +++++++++---- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/share/qtcreator/qmldesigner/toolbar/Main.qml b/share/qtcreator/qmldesigner/toolbar/Main.qml index 7f6a8a311c8..ebed433e724 100644 --- a/share/qtcreator/qmldesigner/toolbar/Main.qml +++ b/share/qtcreator/qmldesigner/toolbar/Main.qml @@ -254,6 +254,7 @@ Rectangle { ToolbarButton { id: annotations visible: false + enabled: backend.isInDesignMode anchors.verticalCenter: parent.verticalCenter anchors.right: shareButton.left anchors.rightMargin: 10 diff --git a/src/plugins/qmldesigner/components/toolbar/toolbarbackend.cpp b/src/plugins/qmldesigner/components/toolbar/toolbarbackend.cpp index f563e5eeca7..1919aeedad6 100644 --- a/src/plugins/qmldesigner/components/toolbar/toolbarbackend.cpp +++ b/src/plugins/qmldesigner/components/toolbar/toolbarbackend.cpp @@ -644,6 +644,8 @@ bool ToolBarBackend::projectOpened() const void ToolBarBackend::launchGlobalAnnotations() { QmlDesignerPlugin::emitUsageStatistics(Constants::EVENT_TOOLBAR_EDIT_GLOBAL_ANNOTATION); + + QTC_ASSERT(currentDesignDocument(), return); ModelNode node = currentDesignDocument()->rewriterView()->rootModelNode(); if (node.isValid()) { diff --git a/src/plugins/qmldesigner/shortcutmanager.cpp b/src/plugins/qmldesigner/shortcutmanager.cpp index add971a8c1f..87a873339df 100644 --- a/src/plugins/qmldesigner/shortcutmanager.cpp +++ b/src/plugins/qmldesigner/shortcutmanager.cpp @@ -14,11 +14,12 @@ #include #include #include -#include -#include #include #include #include +#include +#include +#include #include #include @@ -109,10 +110,16 @@ void ShortCutManager::registerActions(const Core::Context &qmlDesignerMainContex QmlDesignerPlugin::instance()->viewManager().exportAsImage(); }); + // Edit Global Annotations QAction *action = new QAction(tr("Edit Global Annotations..."), this); command = Core::ActionManager::registerAction(action, "Edit.Annotations", qmlDesignerMainContext); Core::ActionManager::actionContainer(Core::Constants::M_EDIT) ->addAction(command, Core::Constants::G_EDIT_OTHER); + connect(action, &QAction::triggered, this, [] { ToolBarBackend::launchGlobalAnnotations(); }); + connect(Core::ModeManager::instance(), &Core::ModeManager::currentModeChanged, this, [action] { + action->setEnabled(Core::ModeManager::currentModeId() == Core::Constants::MODE_DESIGN); + }); + action->setEnabled(false); command = Core::ActionManager::registerAction(&m_takeScreenshotAction, QmlDesigner::Constants::TAKE_SCREENSHOT); @@ -131,8 +138,6 @@ void ShortCutManager::registerActions(const Core::Context &qmlDesignerMainContex qWarning() << "screenshot" << file << b << pixmap; }); - connect(action, &QAction::triggered, this, [] { ToolBarBackend::launchGlobalAnnotations(); }); - Core::ActionContainer *exportMenu = Core::ActionManager::actionContainer( QmlProjectManager::Constants::EXPORT_MENU); From 0c7c7272ea11fe4e4b55c9f98774cfc19d5764df Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Fri, 21 Apr 2023 15:57:29 +0200 Subject: [PATCH 082/192] StudioWelcome: Use DocumentsLocation for examples also on macOS This was a workaround for macOS (QDS-6305). Task-number: QDS-9232 Change-Id: If85d2f950765d8114d8de60ab1dd5126845bb599 Reviewed-by: Tim Jenssen --- src/plugins/qmldesignerbase/utils/designerpaths.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/plugins/qmldesignerbase/utils/designerpaths.cpp b/src/plugins/qmldesignerbase/utils/designerpaths.cpp index b4ad03ba716..d4ae2a46456 100644 --- a/src/plugins/qmldesignerbase/utils/designerpaths.cpp +++ b/src/plugins/qmldesignerbase/utils/designerpaths.cpp @@ -12,9 +12,7 @@ namespace QmlDesigner::Paths { Utils::FilePath defaultExamplesPath() { - QStandardPaths::StandardLocation location = Utils::HostOsInfo::isMacHost() - ? QStandardPaths::HomeLocation - : QStandardPaths::DocumentsLocation; + QStandardPaths::StandardLocation location = QStandardPaths::DocumentsLocation; return Utils::FilePath::fromString(QStandardPaths::writableLocation(location)) .pathAppended("QtDesignStudio/examples"); From 7af8c225d11ac2dcfcebdecdc91f01b944c580c0 Mon Sep 17 00:00:00 2001 From: Henning Gruendl Date: Thu, 20 Apr 2023 18:47:08 +0200 Subject: [PATCH 083/192] QmlDesigner: Expose objectName for Qt Insight - Expose objectName in Property Editor - Rearrange Annotation button in Component Section - Fix enabled state of insight view - Add reset of Property Editor when enabled state is toggled Task-number: QDS-9355 Change-Id: I873994580d8135f97236b6a98462c513169ac8c1 Reviewed-by: Thomas Hartmann --- .../HelperWidgets/ComponentSection.qml | 47 ++++++++++--------- .../imports/HelperWidgets/InsightSection.qml | 21 ++++++++- src/plugins/insight/insightmodel.cpp | 6 +++ 3 files changed, 50 insertions(+), 24 deletions(-) diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ComponentSection.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ComponentSection.qml index 839c656d8a1..49809cb9d97 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ComponentSection.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ComponentSection.qml @@ -174,7 +174,29 @@ Section { } PropertyLabel { - text: qsTr("Name") + visible: root.showState + text: qsTr("State") + tooltip: qsTr("Sets the state of the component.") + } + + SecondColumnLayout { + visible: root.showState + + ComboBox { + implicitWidth: StudioTheme.Values.singleControlColumnWidth + + StudioTheme.Values.actionIndicatorWidth + width: implicitWidth + editable: true + backendValue: backendValues.state + model: allStateNames + valueType: ComboBox.String + } + + ExpandingSpacer {} + } + + PropertyLabel { + text: annotationEditor.hasAuxData ? qsTr("Annotation") : "" tooltip: qsTr("Adds a note with a title to explain the component.") } @@ -226,6 +248,7 @@ Section { visible: !annotationEditor.hasAuxData buttonIcon: qsTr("Add Annotation") iconFont: StudioTheme.Constants.font + tooltip: qsTr("Adds a note with a title to explain the component.") onClicked: annotationEditor.showWidget() onHoveredChanged: annotationEditor.checkAux() } @@ -252,27 +275,5 @@ Section { onCanceled: hideWidget() } } - - PropertyLabel { - visible: root.showState - text: qsTr("State") - tooltip: qsTr("Sets the state of the component.") - } - - SecondColumnLayout { - visible: root.showState - - ComboBox { - implicitWidth: StudioTheme.Values.singleControlColumnWidth - + StudioTheme.Values.actionIndicatorWidth - width: implicitWidth - editable: true - backendValue: backendValues.state - model: allStateNames - valueType: ComboBox.String - } - - ExpandingSpacer {} - } } } diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/InsightSection.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/InsightSection.qml index 5a4c6f48a84..b23f8ff95d3 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/InsightSection.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/InsightSection.qml @@ -8,7 +8,7 @@ import StudioTheme 1.0 as StudioTheme Section { id: root - caption: qsTr("Analytics") + caption: qsTr("Insight") anchors.left: parent.left anchors.right: parent.right @@ -84,5 +84,24 @@ Section { ExpandingSpacer {} } + + PropertyLabel { + text: qsTr("Object name") + tooltip: qsTr("Sets the object name of the component.") + } + + SecondColumnLayout { + LineEdit { + implicitWidth: StudioTheme.Values.singleControlColumnWidth + + StudioTheme.Values.actionIndicatorWidth + width: StudioTheme.Values.singleControlColumnWidth + backendValue: backendValues.objectName + text: backendValues.objectName.value + showTranslateCheckBox: false + enabled: !modelNodeBackend.multiSelection + } + + ExpandingSpacer {} + } } } diff --git a/src/plugins/insight/insightmodel.cpp b/src/plugins/insight/insightmodel.cpp index 64cb8f90a1a..4fe987a5977 100644 --- a/src/plugins/insight/insightmodel.cpp +++ b/src/plugins/insight/insightmodel.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -452,6 +453,11 @@ void InsightModel::setEnabled(bool value) if (!m_qtdsConfigInfo.exists()) writeJSON(m_qtdsConfigInfo.absoluteFilePath(), m_qtdsConfig); } + + m_enabled = value; + setAuxiliaryEnabled(m_enabled); + + QmlDesignerPlugin::instance()->viewManager().resetPropertyEditorView(); } QString InsightModel::token() const From c95ade272b88af4835446f085440066bd5e34ccf Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Fri, 21 Apr 2023 17:28:43 +0300 Subject: [PATCH 084/192] QmlDesigner: Abort lights baking if no response from quick3d side Fixes: QDS-9666 Change-Id: I8721e1076bda7113dca9afde9998548e232a2186 Reviewed-by: Mahmoud Badri --- .../components/edit3d/bakelightsconnectionmanager.cpp | 2 +- .../instances/qt5bakelightsnodeinstanceserver.cpp | 6 +++++- .../qml2puppet/instances/qt5bakelightsnodeinstanceserver.h | 1 + 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/plugins/qmldesigner/components/edit3d/bakelightsconnectionmanager.cpp b/src/plugins/qmldesigner/components/edit3d/bakelightsconnectionmanager.cpp index 201ca1d2e9b..d2a6f3d332d 100644 --- a/src/plugins/qmldesigner/components/edit3d/bakelightsconnectionmanager.cpp +++ b/src/plugins/qmldesigner/components/edit3d/bakelightsconnectionmanager.cpp @@ -34,7 +34,7 @@ void BakeLightsConnectionManager::dispatchCommand(const QVariant &command, m_progressCallback(cmd.data().toString()); break; case PuppetToCreatorCommand::BakeLightsAborted: - m_finishedCallback(tr("Baking aborted!")); + m_finishedCallback(tr("Baking aborted: %1").arg(cmd.data().toString())); break; case PuppetToCreatorCommand::BakeLightsFinished: m_finishedCallback(tr("Baking finished!")); diff --git a/src/tools/qml2puppet/qml2puppet/instances/qt5bakelightsnodeinstanceserver.cpp b/src/tools/qml2puppet/qml2puppet/instances/qt5bakelightsnodeinstanceserver.cpp index 9a8fc9489ac..d7a8b313acc 100644 --- a/src/tools/qml2puppet/qml2puppet/instances/qt5bakelightsnodeinstanceserver.cpp +++ b/src/tools/qml2puppet/qml2puppet/instances/qt5bakelightsnodeinstanceserver.cpp @@ -93,6 +93,7 @@ void Qt5BakeLightsNodeInstanceServer::bakeLights() QQuick3DLightmapBaker::Callback callback = [this](QQuick3DLightmapBaker::BakingStatus status, std::optional msg, QQuick3DLightmapBaker::BakingControl *) { + m_callbackReceived = true; switch (status) { case QQuick3DLightmapBaker::BakingStatus::Progress: case QQuick3DLightmapBaker::BakingStatus::Warning: @@ -224,8 +225,11 @@ void Qt5BakeLightsNodeInstanceServer::render() } else { rootNodeInstance().updateDirtyNodeRecursive(); renderWindow(); - if (m_bakingStarted) + if (m_bakingStarted) { slowDownRenderTimer(); // No more renders needed + if (!m_callbackReceived) + abort(tr("No bakeable models detected.")); + } } } #endif diff --git a/src/tools/qml2puppet/qml2puppet/instances/qt5bakelightsnodeinstanceserver.h b/src/tools/qml2puppet/qml2puppet/instances/qt5bakelightsnodeinstanceserver.h index fc99adc9021..0a92ccdf8f8 100644 --- a/src/tools/qml2puppet/qml2puppet/instances/qt5bakelightsnodeinstanceserver.h +++ b/src/tools/qml2puppet/qml2puppet/instances/qt5bakelightsnodeinstanceserver.h @@ -45,6 +45,7 @@ private: QQuick3DViewport *m_view3D = nullptr; bool m_bakingStarted = false; + bool m_callbackReceived = false; int m_renderCount = 0; QProcess *m_denoiser = nullptr; QTemporaryDir m_workingDir; From 24fe244ea6d8cac66db5f5354b8242cb53b1abb4 Mon Sep 17 00:00:00 2001 From: Eike Ziller Date: Fri, 21 Apr 2023 12:02:16 +0200 Subject: [PATCH 085/192] Build: Add SOURCES_PROPERTIES to extend_qtc_target This way the check if the target is enabled and the condition is met doesn't have to be done manually. Change-Id: If490a84fd5f1ede2d032c3800f69ede4f755bc2c Reviewed-by: Marco Bubke (cherry picked from commit 30bd12d8dc668631ab21831ac32588a5a304f533) Reviewed-by: Eike Ziller --- cmake/QtCreatorAPIInternal.cmake | 6 +++++- src/plugins/projectexplorer/CMakeLists.txt | 15 ++++++--------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/cmake/QtCreatorAPIInternal.cmake b/cmake/QtCreatorAPIInternal.cmake index efe7efd9ce7..224ae4b2b14 100644 --- a/cmake/QtCreatorAPIInternal.cmake +++ b/cmake/QtCreatorAPIInternal.cmake @@ -464,7 +464,7 @@ function(extend_qtc_target target_name) cmake_parse_arguments(_arg "" "SOURCES_PREFIX;SOURCES_PREFIX_FROM_TARGET;FEATURE_INFO" - "CONDITION;DEPENDS;PUBLIC_DEPENDS;DEFINES;PUBLIC_DEFINES;INCLUDES;PUBLIC_INCLUDES;SOURCES;EXPLICIT_MOC;SKIP_AUTOMOC;EXTRA_TRANSLATIONS;PROPERTIES" + "CONDITION;DEPENDS;PUBLIC_DEPENDS;DEFINES;PUBLIC_DEFINES;INCLUDES;PUBLIC_INCLUDES;SOURCES;EXPLICIT_MOC;SKIP_AUTOMOC;EXTRA_TRANSLATIONS;PROPERTIES;SOURCES_PROPERTIES" ${ARGN} ) @@ -546,4 +546,8 @@ function(extend_qtc_target target_name) if (_arg_PROPERTIES) set_target_properties(${target_name} PROPERTIES ${_arg_PROPERTIES}) endif() + + if (_arg_SOURCES_PROPERTIES) + set_source_files_properties(${_arg_SOURCES} PROPERTIES ${_arg_SOURCES_PROPERTIES}) + endif() endfunction() diff --git a/src/plugins/projectexplorer/CMakeLists.txt b/src/plugins/projectexplorer/CMakeLists.txt index 58f8c73d1d6..d05509636ec 100644 --- a/src/plugins/projectexplorer/CMakeLists.txt +++ b/src/plugins/projectexplorer/CMakeLists.txt @@ -216,9 +216,14 @@ extend_qtc_plugin(ProjectExplorer extend_qtc_plugin(ProjectExplorer CONDITION WITH_TESTS SOURCES - jsonwizard/jsonwizard_test.cpp outputparser_test.cpp outputparser_test.h ) +extend_qtc_plugin(ProjectExplorer + CONDITION WITH_TESTS + SOURCES + jsonwizard/jsonwizard_test.cpp + SOURCES_PROPERTIES HEADER_FILE_ONLY ON +) file(GLOB_RECURSE test_resources RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} testdata/*) qtc_add_resources(ProjectExplorer "testdata" @@ -227,11 +232,3 @@ qtc_add_resources(ProjectExplorer "testdata" BASE "." FILES ${test_resources} ) - -qtc_plugin_enabled(_projectexplorer_enabled ProjectExplorer) -if (WITH_TESTS AND _projectexplorer_enabled) - set_source_files_properties(jsonwizard/jsonwizard_test.cpp - PROPERTIES HEADER_FILE_ONLY ON - ) -endif() - From 79fee70ed22822cb3b4f1178671b0faa253f543e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esa=20T=C3=B6rm=C3=A4nen?= Date: Thu, 6 Apr 2023 09:18:15 +0300 Subject: [PATCH 086/192] Doc: Add Qt Design Studio on MCUs intro topic Added the Add Qt Design Studio on MCUs intro topic and modified the related topics (index, toc, and external resources) accordingly. Task-number: QDS-9284 Change-Id: Ibdf27111170b2931511e2ab349ec07210a02e70b Reviewed-by: Mats Honkamaa --- .../external-resources-qds.qdoc | 40 ++++++++ .../src/howto/creator-external-tools.qdoc | 2 +- .../images/qds-mcu-target-deployment.png | Bin 0 -> 45811 bytes .../src/mcus/qtdesignstudio-on-mcus.qdoc | 92 ++++++++++++++++++ .../src/qtdesignstudio-help-overview.qdoc | 2 +- .../src/qtdesignstudio-toc.qdoc | 1 + doc/qtdesignstudio/src/qtdesignstudio.qdoc | 1 + 7 files changed, 136 insertions(+), 2 deletions(-) create mode 100644 doc/qtdesignstudio/images/qds-mcu-target-deployment.png create mode 100644 doc/qtdesignstudio/src/mcus/qtdesignstudio-on-mcus.qdoc diff --git a/doc/qtcreator/src/external-resources/external-resources-qds.qdoc b/doc/qtcreator/src/external-resources/external-resources-qds.qdoc index 8f9eead2d9d..b1c64e785d1 100644 --- a/doc/qtcreator/src/external-resources/external-resources-qds.qdoc +++ b/doc/qtcreator/src/external-resources/external-resources-qds.qdoc @@ -17,3 +17,43 @@ \externalpage https://doc.qt.io/qtdesignstudio/studio-optimized-3d-scenes.html \title Creating Optimized 3D Scenes */ +/*! + \externalpage https://doc.qt.io/QtForMCUs/ + \title \QMCU documentation +*/ +/*! + \externalpage https://doc.qt.io/QtForMCUs/qtul-qsg-traveoii-designui.html + \title Designing a UI for Infineon Traveo II +*/ +/*! + \externalpage https://doc.qt.io/QtForMCUs/qtul-qsg-miimxrt1170-designui.html + \title Designing a UI for NXP i.MX RT1170 +*/ +/*! + \externalpage https://doc.qt.io/QtForMCUs/qtul-qsg-ra6m3g-designui.html + \title Designing a UI for Renesas RA6M3G +*/ +/*! + \externalpage https://doc.qt.io/QtForMCUs/qtul-qsg-rh850-designui.html + \title Designing a UI for Renesas RH850-D1M1A +*/ +/*! + \externalpage https://doc.qt.io/qtcreator/creator-developing-mcu.html + \title Connecting MCUs +*/ +/*! + \externalpage https://doc.qt.io/QtForMCUs/qtul-infineon-traevoii-qsg.html + \title Infineon Traveo II quick start guide +*/ +/*! + \externalpage https://doc.qt.io/QtForMCUs/qtul-nxp-rt1170-qsg.html + \title NXP i.MX RT1170 quick start guide +*/ +/*! + \externalpage https://doc.qt.io/QtForMCUs/qtul-renesas-ra6-qsg.html + \title Renesas EK-RA6M3G quick start guide +*/ +/*! + \externalpage https://doc.qt.io/QtForMCUs/qtul-renesas-rh850-qsg.html + \title Renesas RH850-D1M1A quick start guide +*/ diff --git a/doc/qtcreator/src/howto/creator-external-tools.qdoc b/doc/qtcreator/src/howto/creator-external-tools.qdoc index dee3f838abe..219856879c5 100644 --- a/doc/qtcreator/src/howto/creator-external-tools.qdoc +++ b/doc/qtcreator/src/howto/creator-external-tools.qdoc @@ -11,7 +11,7 @@ \page creator-editor-external.html \if defined(qtdesignstudio) \previouspage quick-converting-ui-projects.html - \nextpage studio-help.html + \nextpage studio-on-mcus.html \else \previouspage creator-keyboard-shortcuts.html \nextpage creator-task-lists.html diff --git a/doc/qtdesignstudio/images/qds-mcu-target-deployment.png b/doc/qtdesignstudio/images/qds-mcu-target-deployment.png new file mode 100644 index 0000000000000000000000000000000000000000..456a75560a7e20299b632ed4bd11ef24fd763abd GIT binary patch literal 45811 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%0D< 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

i%LG$% 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 literal 0 HcmV?d00001 diff --git a/dist/branding/qtdesignstudio/images/logo/64/QtProject-qtcreator.png b/dist/branding/qtdesignstudio/images/logo/64/QtProject-qtcreator.png new file mode 100644 index 0000000000000000000000000000000000000000..91419443a7117da7b9ec30fc4431c9d95d68a0b1 GIT binary patch literal 2656 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 literal 0 HcmV?d00001 diff --git a/dist/branding/qtdesignstudio/qtcreator.ico b/dist/branding/qtdesignstudio/qtcreator.ico new file mode 100644 index 0000000000000000000000000000000000000000..a6f5f84a07bc50bbc6b41233de03e906dfe59295 GIT binary patch literal 120874 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 AZvX%Q literal 0 HcmV?d00001 diff --git a/dist/branding/qtdesignstudio/qtcreator.xcassets/qtcreator-project.iconset/icon_128x128.png b/dist/branding/qtdesignstudio/qtcreator.xcassets/qtcreator-project.iconset/icon_128x128.png new file mode 100644 index 0000000000000000000000000000000000000000..a481d7e8a5dc4764dfa53e74d0ceeaeb0193c8ac GIT binary patch literal 5947 zcmeAS@N?(olHy`uVBq!ia0y~yU}ykg4mJh`hQoG=rx_T;nLJ$_Ln>~)oy%Dx;=1d= z{&_Kzf2*(`Q*mHmyeTSpzo}>qqra0wh|BcLPX(@UtbX)qkWNIn%HcewPD)3_b%PJbLYLJjUrdCUzfKmdvjx5E4TQ& zljqN?*QZ-vV`F2kruUExkB*BUqwYl(d_H%=FXcv z`?qA>nkfn!Dx_JSb2~Jo{8rwuOp#&tUQq_El3z2q=B#CDS+-2A(9@x#vvXzr)2ZQU zI#F9P*yn{k>=meBVz~5c@rE*IhO(;FA0intPA_*_{@Ue-%ZDYCN`od(nj{szKF&7v z>8YvKsvNWSo|KuR&(J8ua8ZaszEYcE4QB#lz_z-uAH2N00wN+ToSdA~LRW{~Z560_ zmn%@g)L@|bN15Tx2Ug()EDgUHvKT_b!=0K-{fTe|D~k++tb9W$ z!^*qM8BEN~x)`opyXI6-V6bHA(ut*~ca^^0!^Onc&?gx%i{ToN0K=Gc@pJoQ^CSs5oI`vHfD6K&0F*c8_8U$x5X z3u91F&=ZZu0|#d@b2iz&)r@@N$*@a_LB6S9Wb2a&+6*2}E7H%;+ca~+97jo}V=`N$ zIc&HN>@0p>!^f~~dB9A8MTeF$l)SqWd4_$%LnYfbo!iPRG7RR53C7K*auhx?dh~X6 zH90o3bw1y4e5GT<$wjy0D;~D$-=cx**|zT!q-hzKRbRc5pImeXYZgV>W@wAmxOBB!jZRo?ow4Z`Y09_GWcA zZ=*Y-hK|k`Plgvk+zd%f91KTTCX^U(HwZDkvJc>hn7=?*;Q_-ICI&4Jv&ZgR7%noX za0M_OaB^_=;9~sBx#acMgiRT5x1nw7LD{ z$&-(&A|ht&@Mk*II(~xyts&_D1rG<{2|)a2#&qT|Lt{{oL2xWp95mmS3+BV&ZeqW(au3^x}cQ zBE}nyi=G%;_J#h{afvz<{>@70xskH+<0o6M$5kuK1vPB-YdTQ1PoP4mA%rDC{YOIJ zX|_!h8D`&DXvg^1hcUr=@)Z5WTGwxSG_RYyVqx|7chc^CGCLXgwV7|PetA}=!HkRb zrk~<9`2fACf(^H3pNL}Ez#Mqe_JZM>+nZ8P``+G``}~!*WSOszn?#FxPBtT0zt>y%nh0sxS0-gz5S`g;PvnZYv%1l z@pTJ+C`$^==?iUmRKWkN>Ptl6f~Gxs0iU%GGPwK*V-tw{@oo9pbBm{c{}AEZyZDT_ z$6u~%`)Yp+G4y#~beNmMRwLZ--0p-p;|6Z`3oMP>bbjx8JpYA4O{V={m23|6ht0wf zn=TwZZ^o;@F{vwIOH=5+-B%0G**x2wU;o7{cJkscvM26eU+ms5xTA_E$9U@1A36-D z@1JjfzU_T@?$T9FA;HhJq8#7WTz}@caf>FC=ZU5l?HjZ1r9D2FCU@k0Ug-{V-TBY% zyc7{ywY2@E)yq&HU*ElQ;q}L&UTtR3sjs)!IlM|shW+6x?x55D1xyEhKR;k;y0hu6 zwQJ((WrrNk&AMNE{qbYd#k^Zx1U)$(?r{-eNMc%bo10;}62tr|ryr;P?UXONe}DO^ zbEd72mz*p8!{R2O_m_qD^8zlW4N;9Zm;ZSe_FH~q`oY*;3zttm@FB@U{o>ai{s3K9 zrd)o8vc`Gl4Br2DiZh-l7vj@kS^NCR%3FCL&0@^$o5KR{Nte`cJ+Hofe)~_>`#Q58 z1ig4uX4Jtl*>NA|fl1%LIq7YyTh9KX?d`R&pH_zg4f(w`9GGixY1Kn@i-wmxOq##v z&w3DcI^C4voc|3w#shU9!k9ng+=~-rP|uHd-rKBxhWSPN#8*uvSN@-qIMpGGaEyuy?F7WjMKr0=|g0;-Y1`X<{#edt~R~8aRED1_VZZP<}HUjF7PYI{$yL% zca{0|?R2%Bx)o6$WL@qvCOo{Rzjbe|@=U9_$#?mb9qoHUm@jMIPx!D~z9wzc?OH)D zhPsJ|4t!%~=xa6RaoGN`-(jkzh|a-_7g|29WBB#TM%PYgQ~n$NgN=*|JAQEY&CzOm z?E3V>`gpE|OIDpLknqfUzQSO~s%)W{o2qh3PEAK1uX-i*@WM)^jEpS@9^UU-xQ8uX zAUER*kMh?8R$Ee5J#6ojb=t@5aOQ*Nwl_27_XS8wF*k0U?hxDkfP+bS4&%$t4POsl z6p)k2cwx7^OWSbE9scO=a!051hFxUA(O=prO1X)15uxNXSa%)@PS4_8PS}#D_3NoSB%gF-kT;V!}L@{PTu<47%3* za~pn})$>f<+PkXGNhPlQ>J!0&yH7RS*4KV;QS^!YZFj}Q?6r$lpMuQEz3t2i=4GGi zCxv}zp0*)^qsOnQYBh_)_H!F@^IkJ5JYBoj{?MIp`)0Mm1#OE@)tCJ-*zx<;y|ugE zeQQ4cu<@+*-FLkz|LdQ7Gd%E!ILzSE`(yJWciuO)*(GyRuAdQH6t-hgxOv5M{p3^z zu2b0x$rr^en!T@`G@i-g(Z$!!dHqY9YH>51koe?0v349;gn>I=9@MC4T7X5yn0Q3L( zb2&5TDn0#OSNg@ICw?aTS6{K8nrBYzU=q7mx3=a?=OV8i#ZRjXHZ5_o?-7ZTcl)=a zCW@W?LEB5ewebw@3pXo#c{Piv{`5SqgExLDZ4`~#_}@1BrkO=_;A$baTi4b3cFIii z-@)fI$>dbltAvtwW91#?@`Yc@uA85=bH*KA>YP$KtB_(p9OXQy-M zWzK(9w*zL_-?D3TmizbS{t4$I_wYh{_84S!^QzE;a>K z{9XM%uvw-x$kHU_I@db3)pMe1^V!)SJo~*R+Q#BHOZbxI2ikrbyE2>*cw?;kv_PDZ z!ErRycNZJ9u&}+TD__*e zH&>@=j(OSPeaWnS{a4Q3J8?g5##JeX6ALcH*D^Om%M}EDu2P7ZIFD_1ahel{T2lv` zMQ{w?6_oJ}E|D)R%SZOf%9Gv**wdt3}BdxO^ zUJR@kIDE*Uc!9S-+|5!Jx$0u`G67TT8BF1IhhCc6u`uit%av#_U~#yx;d_?mqq@7n z54IkE-x_d|T|p}0%%0SMh_IJNJ#(z8SbR=ODKZ!QcrI*^f0J#++V#x)8yqrt-m)-R z^@iD3`!AN~S`}U-<8#c$>Dkn~2D>GeWy*CN{=wpuz$)GxDq;Wd%&nWCfwsF|H1kA%wsQhH`*~cJd5ewao>>9g;DZ?Ht)T-6ATS%58BwL zCzu4Rly+-dbbf=}+~cPWC$?-7XROG4%@?t~w7qRx;TPMMj*2T>5`JuLCzyDT?c=yq zX|iU!?anVeeYrKi=H|zgvv@X$tlho8nc3i#{w@A{j@lj!3)m{kKjv`o8+JRXNIlr~ zCS9-pyy*-O=q9GZ6Yrhu$m)@6o>=WDBY?QRqwD9u-4_HZcRxYDk^AWXr2dQQ{z zce`1S@w`y{Z|lGRcP~rBzXf5lM0MicJ2O1EqS404A^CN}s$F5_9BT_o&zqd6&NAaW zZ`2*l6~N51v!2tcId#|b-t!VH-RXDq{vBnR^JtaSj4wyBEUG?jso7z@cVbS7pv-)p z*Yb0@BE!EF@pjq0^|At|{^{TT;Cwv%NG)eW``6ZW6(7Rmr*Tzwr&}_8 zI1pqgb-_yC)KDzW{kWm3U6_d7&97G{&VMlPsJ>IIbMvCFm)RFT@440RcGe^o|8)Om z3z_l>5~5mt$`bu|kH`Nh@TyH%;bpX%!Am zy~gQsvv^*f_=dSME?T}P5WzL#~AOBx(uWQ;ZzK`Q-=~uq%e!e}ze_LjB z1Z-sAuwo6DRN=1a^8RxgPtA&Y*&8qX#lySFyC5rk^%pUNoq5K6MpYksZEpN}nD;U2 zS%=07UhgA<%NCj)_s=ch_!ocGiov7*s^@)%gfsD_(R+flW9}NWGR{zBevr42;ZMyn zj=Z`1x$11A->^jPf0y}T(~J}@kmcE<5k0!63)S!E5*U?iPb1teadAk4W$LSl2yyccP&MsLjSsbFi zg1ehtTXJ>6CdsB-)1OP#y|D1@(PuQczm`S7){Q0Mp~-dzjg@N}C%2|f_z0*8=d34-YyUTv z+u!g0EATY0ed3e-40A+obww#E>2dn4oq6{l;||l~FB3Uu@dw=34(C@sSUT;ZL;jL^ z(~QL2q8?7{On%UrSq}2A^|C~Uro37o)6dJKyLK+r{Bk~+*-`(`mR;w>w9XZax}BSE zBv!PnFZZr9KhGCMhGTZ?elbkd4KPqDS!qCQ>$mHQWu>VmmJu-3{J7&zS$#-igPy@^aM+3D${KtWsdO{_xS1dLN_4(n^g+Fk$zSX~(}Q!%d$v|EXYE^i_{5yfnMq$uK58;B9kb7^?>sF0 zYS-4jyOkDGv+A7A&39Y$!TnIHh8^1hV=3M1t^N<%c7@djpO~;Mb8h8{oxh)PtNkhC zW%!qHNc_NQR(*?j1NH{y1B@?DF#Hh|-(SdJ!SI%=L4_qjT-=|XjiLT#7juCi*S{DpG7Wv42Ml87JED7e~{Ok?l zd<^xUbTVS* zykz8bOYXqYrxTs(`nK?8FJ1Dj^Mm)2^QF0*4ffX*Jh=lnls2{{sD9r0yYOb^k_q#* z)$Q|xy08k&7=4S2!I9}e zywHxwBA&mh4~rRW*d4l6-ne;6n|RLm+`3JbL2cPD@2G<+Z-sZfu?c!nQ>Gh~ou9TK zBh#DVMz%)lxz_iU98(Mq{N$L=-6_uJ;I99l^~)oy$HaIP}|( z^Rw@1@8xJ}NJi;``fmrEBTHEe!eH-*< z(Sl3ucFwc!P5Qr6|GjTs-aGA;lh4fg{Bw`zr%!5?p7(yz?qvGu$12+O|wcfPy2LYL55M`lUvR5Wgo8Y2z?l~^6snXwG1pxjEoLda)!62wM5UpySx0qx}Kii)Vf6m z*;_BK%zI=PD8tA8uI$>PDdAyZ)22+HKL60#7bhkv|Ni#={{Gs#yGm!9<=?B};MYC2 zc20AnbCT=?lOl#A1;6U5UH-1!71V8D5fv1)Y5jr) z4^Fpoi?93r@ArHD2bY)5Y7IEMlRHJwq=-Rd&Zo_~vfGWnzq|WeGm1g;i?FL4!;i-o zggyk+ZF~9q#y*OWU9|uc`;FVfUO-&v)RqL`hHwwO94L^T$d4J|QN8WT^0TwAuhDTd^ zXa3P)xFN{6p^-r#cJY2bhJS|p<#nwxE-m4dv9GIn-XW-bEbsol+WF7KLb$Incq%hB zC+LRnE!v9oT`2hII6O%E_Th%tzm%Q6@VY`DEG_x7e6 z4j&Km+uKx~on?Ce;pu4tEK=qSH(tzN!??hh>47Pe!(RpmnYX=D;!M6C^$jokTlv>; z_fa3lk~cR5Pn|mD_593C<6l$5<9@a&T&Q3aIM1lyzM0{gAcN9zrU{z+Cq8-|a6s_h zW5eJYIVK(Ff@_iKj)8%JFaP}f%*n*kV8vj-{UDUdA%*FH*UzsPk|oB#sL;fa@btg&L+08&`_D5w-dVMJ^}|1(&xiki z|K#LkRqts!lFb4?#A3EGfpmDPFgbkrf0p5Z{WbHuObOdxzI<75&hoiKVxnS5Xy{a( z*j-Z;92gWBT&j2+co<$yn4oFDzhBdK|Hi+uyURkmTm9x(9F!Ag;b5qaJE|basNlt* z5XJCj-waLxU50Ij;tt!H^maU85)-&GUpsPJj-+D)gM&~W!w0qfJY0*c71&r(7u(6R zJc(s6`T5%6!rtogj^5tZM~{-e))&N|>=M=HU}9mI^oy-w#fLxtPu^j$<5q}b*z!QGdnN;J1vNFcb-P|^tyr-l zK;HY`_9q{Y%d2w;FnBCF$R%*P>8uXJvJW};Q>VwWtz5aXA@}I3J1iGv6+jA6LGkMva_Sl;iYl;t-V@Zo!{GDtX4~8a z3;gHX$t|{@Zz^z?aT5y%gUZ(jTqhJ1PP25Jbto}+2+y+PKG0O3BX7nR`kVE`R|c?m zMUF7{Fenr=S1^3YbLf||y)s3Cxyho_qrJgke}lq>2@DOsj+_jELi_^EjeE2eyxC7Z zV}3DPj^E<@o#Lx`j=NYF{Qh6_bHlyDj%*Vra8x+iID`qLm?;OaFq~$Jsd(5bXUJgJ zd&1cv!`uy|;L3Dug)$DIbdD~D2ic4j3>$n7s7{Z0l;}IdSN+;r=C=``xLUF=hVkZ= zgpGzP1R1{cer11gb+vdxu*CWsjPlMC9386ly#$3AJPvU(MqUnBT*~-kUPF!Ggn4`^ zwTuy36G}OaRx3Zy=K5K?OBI|vS}uwTtYz3I;SnZw=J)>^TZZbq0}YH>o*QzKCf#k=b0Nc?N9&T{7hku_k^3XnE$-)c-Jsb>B{LgO#=@G1w>=}+xup9UQcjcbe_^cY_A9qDTP@!g=9{f$Gz z9Y!VwPY%X}EiE%F#2t9(@bv-C7Rc?ee2r`_BVUS~ZvrmpWq1RD@MS|hT_YbU0H3b11+@x$l ziN%qhZ5HE!94CR`n!2ch<$iOoTx}|0_`%IsC^WIaPE|X6UCtIz^s&e?7)f+!vwmK> zdlC17`{|4hf(!?n8VdY+x7{{iY*1O!=&;zQL6qUfwS~^?q6}+-H(ac~-O3POdBVAo zpU2G2TOo;YL;e3cCWk4^4f+B?vzxlMG9>vkJY$-{cp#kP#3RN-CXd`QIRzLznqD!z zIP{(E31h+*w+2Cm2(1eo3biqA)%^VA#taIb6O0PG7#_XIz9QOT%DR!ML6YUo zIi;L4AeXir;T2#!z<$?-L1HTN{K{uDUl}EaF>tUjC7qfu```@?P|?R}%}~KA!gwH^ zaR&E+>x?YSOpmHRzt<1bGGhZrwiw40T~W2y>sA3Og1bP z0WWVhw(&G}usS$8GBPd@WN0uySjn&Htebx%yoDXykUDV(E zFQ~}tU~G872C6@-%HPRcs64q#xWK{iW@FCM1T`Jy17@|;7W~`8$9K&l(PqoQWr3OBH{jJ$hZZu9TL!a_N*rWu_XI$+Cq7-~!xL!nHliluz#xlQz}$^zv%Dwl;coA$wT!0jWuw zzCHORlKQu{ltU+SQ_7KBTeIsg=rq2{XaSYdOBg0>Wf#9(nA98)>B|s&;M1p1RuUa& z?T>z$zv9WGpC67(?9@L~njqU5q1M#cs2hDQnzh2`sd^2340o9lPOWnPch;@)=A^Vg|3BVs zPr1fiZGUZL$J*!cN6!{*Pz-jjOmnmHIgn*R8o{{DOtt8MkSkn4s=if^}HJGk}76~jBH7O{fD zcLPI1X9i=(sr(ey4+kA&nf`owu;}Kw{ugfh_D|fI&%$vts{Pls-R1dLZ8dat|9)H4 zt+y+#*}?MniCfQlLLljwWy;d$Ev3KrJYv}8t&qn2BZ7gSZDzdwyndZe`6j<+f3*~e zWt8K}xp!!4cpRs?-<%5WaMO8>?P3qZ7BU_XTnS1nEW8Y9%Z&fu;9%M`?_W(W!+nzw zez8x=o2LHEXOA#?I^EIbP2q{W$bt>^|Notf-Cd?DXO?p#{^NbWjCBsW&-nxxI2d}5 z^G;xNn3J*~c3lodJvgO&!<@4p5 zZks>w;=jf*je+q17Xv$=i~@tn1&0MUq#eu*k89{DM03T2eNonCGb(0WvxcS6ZQ;LN zHL^|rYBr~xeQ?QJ-?ry&&#woG?k3?2)Px>0vM@~Az{ZrC=^)6kDYX9UPNpRH_&~-~ z<-JpX>Ti~M*x1vF+$<(Sw`zklR; zrR$X5Z)uC|^S?>I6<16UW1sLV698(VXD-9degXoaaj0H1XaGnIE+h>=ru<9v<5B_4W1l4B@#Br7Dpi?^TsD zr!3wXxVK@=oQSZ;`A`3!7Lztz#kiqq1`pGpzsr1QTXl7Ji}%S`ezL4tdHKOCt268@ zoD3`tZSUD`1j^n0vYn~<;)(3gm+G^b6WF+vvz?xmbp%|VpWN#iwKeN&=J$7Z-(R@X zz;%#IfkA*lYP$4+XvRhQx@^}{o*rK0sWi<#NH9SIv|>1KSfOtBRxJ-AGH z|KleM7e3GuT*pxEwqP1V0z*SHJHHTv)C}o_kT7j6LHkp$kDpxh&rNxMPTKr6*XD0$ zs1P`jkYsW?AnjsD!{L$#g*yE2+ROQ~3md!&{Kbld7&sVs@9|DJ%9J7?5X-QcVfM`{ z@fWX{&t_1`w#o{#R*-&~dXuSE{+yW3`-_W@Pu=laU`HC)r#uG628Ih2YZ(?RNk`6` z8*Vpqk)-Cjd(Y1YaI!Gmcw3~hi)T|0%kHE5#HMmItp4!OvEVB63x*Yn3?=~#YwG@I zN9_HWy{%BhDAK<@gePutb-1f^B=3Fs z_n%L(-rDH*Hz0iKn%4r$Uq>wSDLwvi*P_rXR(GF2E3r~}zU+j({=J{aw|I&HTE3&dv}#{p1yk2$Uy-9pOX{nSaRh50 zTcLD8V@Bo^tp%#VoyE)%JP#V!7{u5wTyA7xkYwPKx09KwdvIPw{c0|TqYjCC927Ek ztoYw`(tpW}#sep`1RN50B#gZe|Gwj5K1;sRb+v=-wU=@yLzjk{bT9CGc!RHGWjCLAo^%gAtM4b$pQ_eHxGuo`IZE`43Lm7&3l$$^=i^dn*K1^%nteIK!;X=mB z_4?WZt2U?n*}$>kz)GR$wy)D87_PE3urV~)e>|lxS=sxyviu4RCI$?DUhZAHp-XeWl4NDCo4@yWg(HPb zhp+Q)u70pIqDWvz-&E&QZ}mPLbX_j9PiXtA`~CM4s<)WW)=u~)Bh14T_8=&khe3xo z!igb+fg$4SJ+bMZeoLo*H}n(8eAiehb#YnsgOa9l!Fh}v6&{wo4iCP(wEoY}bRc|V z@LLV8GNV-)_xIT{E?CIWp!xst`&>ailgEOmuGn%U&T;s6O0j^SiLvo=X2MhsRvS4- ztL5AGhO+Y>mOt=%_s^Plz1ybTiMM#s6xq;K_V$(_Lk;7BiOTLuLJZpGIc`tyXJt11 z=3!E1<#&|2xZzZQ;SL8TKV>ba4f+wZ$H9=L@#*tPDg1My`tH|;m9`$7 zQ~Gquq3`>-^FC#7*?!yCDmtMR)lR@{#yGEvC z6S(?96Q0Ux%uCxV$Z=rLsS9pP4h!s?EygO&;CB2`P#Q}^2HSxy-q}|s#&7(wi%0*A z!cO)y1&%Ye9LyX&4QdSs;=eo88P0A_xV&g~^P(qVhZq`S8#x&4m=bi)v3jk0KRJ3| z-DIm@zm1$eTIVjk?KIV`<-nzmeg009f|Xum9@%yGT*HIil@}{A^%>+C4l*z@Es)vG z6zj~;VZ>nYG`?@~{_7l>#Vt(@cbV@yB)pJw_;80g{!+)krUT2hoOg%KVA&Tf*3Uod zX8t!DMg=a$4LzFwchCMaTVH=Gm(vru7A_XMUF`pw4jB0z==~C=>BpLL%x%l(Td$ZI zm>5}^9lFJIIT$W7F@*jsL^ghwgo$ojxE8RkJQeWk-$761HZ{M5oYqd| z{Zp$y^zt)ssE9c*bO?d`^X;5kGxv$CeS7NPZei*dzS3O%AX&*H?RWjV#A`_WQOUeYcnwT?|7$LW`(clRnvgL(C)4fPImukhYkv>7Cflvy#0qKS<2AZl#?y?j$l=J zChzlYnM}DwmmD`%x=sxbJFHmtZ-=Z^;F`L&*yDZ&*D0$f=3msD%5Nn4G;)$UukT#LMG)EMlR3tm#pDDkizSPq(ri)V06UoO!*p-QDdQ*Ug{P7aG-W z|E`hq>#sn~hrfA03h!@Ru#5fCi^{y$F*B-s&0mzqS$lMJ^Zt6i^5C~9R@b8(&A|rZ zYd0q^_x63Q!lR#Y^uVl(@BZwlDT>f5n8tR*)rpf~A}_Vp3VJ}^0mr}^Fg|J^_F^tr=~$2n)EsvO?+yUpqNv&cm2`ZUft=ASFePczKUmYWg# zq-M?obtZ==Oba$_Fvwvz@$NX|0>AIAp&2)9-cQl-IN|!;!SjQr-vI`L@|5>gKZ|Z2 zkLW7pt*@(JeLu9#*Es0G*WYaiB6T+%u6oyQXjgFbkEvo-*rEg5444*pPF7=S=wNNg zV=T~J`c&b$UcLwpf1sfJ5`gzB^(ei?qWdwxmU{C_WhV7kep1Kn1KHgAs?o**eNF8uiV|GF=a<#xRO$-no! zu7|I0y&8i6LkPo{{jPSGuHUuT`!=ip@daBCuhto7o;`3dUwG}|&9`a44tV=821Lek z@0ZcpWaGcgW9Q406DCh%>8x|Pzd9@H;it;%g#3q^(S|(|S5~dxkbO?l*GMOR_pWP< z@j3bp{&lyy=Pwl~leKN#`9JHc?c>Ku3d*X2n+i_*__grd*%TyP5I^acg|RAQGLPm3 zC2i4J>ASW)X|bqy+CBf_#q0Y&RGTddo%3M&aZUyn23ZEVs`n-gjjnSVJac3$F3sLy z#KCv{x^?XZRfGF147&p(_3D18t{1$i({Qvauc|zcxp`MwbNcRt+*cV{Z=#(%z?`#a%X_WZ;14qZOfp~HFeW=`0l2Epc;Z2J;4CTKZJ zKB%kw6UM~A$e^(L|Jpm#6Bj<%c9;3j>?y1pZ|Wwz+Mq4SFnu=D10|M*$aA-J_Rnn- zE$==S$7mj3IB)HO2d3Wf&F?qnIn){M_-|*kNBvIqS$UuM{JZtr51;w}dwSFok#(m0 zOf!2=*l({DWo%$Lz+mveOkXu@4O3&8V?)v32mcJx4;7eBRnwjm$oS!c_pz=-VV1-H zE^yA<`#9=q$#$uY`4NBlB&NQ7S#3YdR_vVp;*}3yzd4>X*IqBCdupetPzLLUXO14} z%De8*WpH4)z)-Mzp;C0yfwpRcZ@X3}Jlc?bHQ$=u_p8`U%~hSJnI9Z#k25j}J?oqB zVa5dGY!Cm6f&zZ;`oC{po_8vAotk-m|NXGL&kE-)T2oQJd7DnukLxxZ$8wIyuFg|t z5MU5sxPB+ka*_73M#o}-9@!i0d6IR#*?%nxu6|KhKcdVLWO#fY&u^~ohfR_Cw|Y`$ zALmRm4EUj_S5;ZKJ8jwg%1_4f2io%Mh4t9JORF9^cIcn_d!yRkK>t0=@$WP^{^hu+ z+H4lA|0~R>ki-}e5+Y*8kg`gwp|$C>LqeKp!T0 z$K!m5sm9l8zHG7NeEIA2xyI%BvI)OtUa$MgedqI|<3g?_d;Lz&G5JyVQswgX9YWnD zb+#po?p<^Ha@+R8zklCcelSd!J=>e1BZ8qKf+0=e!(m&8oga7EAIeo;U)=le)~#>5 zOb_Jo%i8iy{xrSW*iiQD#AmN;SlTcDcbdNBhlT0UsdKaLgznw2gJ+jd{W$}t^ImoT zw|_{x{Qm8&rRxuGukMSQAH86+VDk&P57qVR3<3->3_A`xOnmG5&(`rl?VTNtwGYd# zG8O11Xx@`^Vepx;_sZYJM^qei&)IjJ&0)HFPQuUdjrguTHmdw>e-pDYc<#36;+_Uj%`1*;WJR%q{Gz#wB^XOqKlV#hbWXyv`B za~gI(T-)@qvi9M+KxWg;@#Ty$0to>L3=-cj7(M=;Bs9N=)y-Ft?YaCF3(?De%y!@Z z{pQ+-e`_u-oBw-Ty7?jdf7=fiw4~&*tUhHtnbUtS`~N?A(=+uCKWJ94$UJg)^?MF! zhN7Y(4u*-`4c5FO_w+c<+->Hp{F`uUM&rH3`^DHT3YG}0o4@tUG}c8MezLFJxgf=~ zdDaaVXMrszeVgOMX3hTKeea+CH^)0UH?DkHZ^54MWofKWeDl;_=OVvb?)mf6|3QJp zTE_?7)dG(!4!*lu4(fICFqF?^`<=6sT}@$z$qsp+s|*h={EPQz-!xszfuU#a)+_&V zT>dVdeCa^r#-iiDyK8{Es(#m(8y}JAMC($+Q)eVRh>9CRz0l$8B$X7H48$bYMLovePnu=>s!$0bjX0;>-GE zL#F={)@^7~b~VU)CbVC7f<(`+@a2je1$<7qJ~4M+RDL+AxvbSjdf!dX$Nx4R+;Gak zR#R@@;eC}mGr!fhJyw1C!KzH@){nz-Ki;orU$E_(VqjFZ0Ly7x1LlKqpt|wFkxt=6 zW`|u|470i3xzBkpZQhk^6sEfj73(}Pr*-W+PJ(qJk=&k$(p~XLER=nP5c5j_>!PO>H zp}(KLEWi1uwf|X9qn~3A=Qrzb+A-}{JJ#22mF1hS^|6RwXLkLsM{*K1>zE*sd0@Bi znYRa+8_O4P8^p3ROlP|i+w`}cJMqHb{>neAAGxn_wE3q0^Si@y<6Bqus!N}Zyxg0{ z(Ohxn^Z%Ftmg!IS=VUf*KCUBsB2DVw$2r>^|Gta-Jca+FR>a1{&8z2H{@7-=s9vGK zHu{4IV*|qiri4|zM?c-S-BDa&U-96WjUw+0J(gSD3=$RhlP_HlVA|jic7C7qxtAB# zwxK8~5HERJ2u4RkT~NpV4|f@BM##{cCc;;##~W*a;cv&AkwQZCzF>(6*+cgEIV_8UKT2DIK}W)1H2(=pqW zEc#^0vI8r;tq=1?=pVS%&U#sD$LF8DAC5hXXO5D-c*+(u)@8zQhx^&ZKL^${?RNjt z9Ql~7T6^KQ3jQdDaR1m8VViI1%IhNkC~V2SdF8QfsdZC;Zy9-1fd$xGPhO zgKt)FfKX7({yyIC3xd0KPP{G?m?QTVu1yC*pJeBoDQVliy8M;rMZdv|dPnPwnNy!# zfB)(5z2pn&7o#rEt8d)F-aa3k_x$GBNSZM$;oEiId!q=$hR}v&L$A%&-+N>jHXnap z`Q(3N@DbYxj*_Rb+=@3g3;zFnN5~_=Zr7534YFU4n%f_Ib5*}7RB>`sY@3XQ@RR{lmbdb*e%n81u54gb z(AMr|5ZK6YVs@HDL~zZjBa^kC*Dl_m`QpbxIpvMc#wTw!-_2l*DZJja;GCO}Ur+P- z`SEEl{%^K^U%Qc2%zpQ^<>$p_PwEU~+{M0Q%C6wp4~Ms>souS1-rn98*R}SH^^IS- z_YT!Duc@86M|W1uul0LPlnzYX4=UTW7~XAP8u4!z`(*)!pTca_nhV#>sW#xa@%kKV z(CdS89DDk6r_N@U4tu`aFYtub3x|q_Z+k!7nJrzr&ZvZy_pu<$3|o(*3ld}IoLAT( z;ke=3Je!QUlki`c7MUq&Urnz1?q0*SXe|XAANRn-$B6^1ynX8DYd>4xD*sJp;`txU1}{_> zL>%Mfn!i_FN4z35G_=yqjg3*^5#xj9_j5(nXBd_-oSAJBa!;UzkwLfN-S#tEdF8B6 z?fi7_qrr<$d2);QH-G*g8F=YIW`CmbBqeF(z4`ANSG`!%E4fTqQ8Dm$=Tn6pYl5?X zNZg(=+w;X+1CXSsV1M4cQ6~E^*iOZg|VaG~H*X+`9MIgXb|_kz4=g_wEmOe)aG5 zSL@g;*t>83j-DJIsiez7JW}(nZ z^cc?YWHI>dxe&`Bk;BZ;*t_A(o!IROT3@X82gm-qzxaoT_4P%URWBrelF6O5LGp>h zhpmFT`{ri~yB%t{XM5$-!>}8x8+?PGg`1}pGtIj5Z~89HvzykrmN_ce)qi4TVqk1w zOnA|(wygHSv-dq0!=srKKJ97HoiOt;ThghIyLM?gN8U87{_#Xw{!mxA*$>~>2oRZ)hm3xqmptObPK;3eiN42^oe(+Wd60&7yRZlFgP$gU`Qxs zY@T1pl*ag_nN@fVgTa0O4;!Yo2|qahy|4LUc$x8>AB)pvy$TgC&D(yqdGb-!x*f*< zj&bf;C?&l3z@6R4o!@t#Gt~({cP#J7`PxsWf8NI1H&))xHx`hHW?ddP!wobtB_b}~ z&dYF;hhbUT+TN9i)E3JXGm810F=jkqXi@%&Y5r=SXw#Do((B)Gn#UGPZpprPWW%|J zeCKaZX3HN=*5Ch@{Yj5|N&MaBD@#;n*wv|gd0n}2`XlRCQOkF~yFYC|Q{S`@C)RUb zDX$eCoKcAmZ43JD;K3=fU&4-I$@1mG3`$ZAZH;;yH*6F18YCII{H7l|b=_{#AA@=8 z=Qpg}GR=3>0^Yo}hR5sc+ONHG)zz~;=Wlw=y?$Ao>Qd1QkH77F_4_98quLKg4nH^| zuAyPW$KF4;#kr#T!3$1{sz)j!^Q?GX)w?gw(wI7b^M}7%PMmGZt}1PQSX=FwS2iuN z+vM-RY4Y0-zfr$)d1`XuxklsFq9MzlJZP@Cy;*RtUddW(0ZGT>H&^?ZDd8@eSMUf4kNFn{-~RpX>YSS)zF-INmRg-x+%KY5~u}pdJMc-SsV- zUTJ9@Hf7Y>@H`(GP zPE+SxIZu2O|2xN%-rt28I2e8`J1nv8Z0CoY!TnWYf|5U8%dI`vvZncRg4rUAN1y-yKY!lr@FwwVk^jU#zVHi4 z-_~?%iB4E<%08Q&1xKd~F(`>MEM2-Z`|;z)hnX6dB=XO8S;Kt3`Mk_selb4@k!$)> z!_S4T_|w1d!?yGD-+toWb>P;|;IpFlO`at%aeT<}Kc+wT-`Vr~4>N23Z{zO2_CsJn zh{DE9Z4K{3r`7c?hRs><=D#oh>HNUh*w}fqW_f|qoC;rk#Ow>q0xx11BspF&Gx+#T z4?S>2J+ATk|Gih&pD%O%e>&mmUGrI5b~mIxwyIuiQN46`{o5AV-TVh)`)xbdxv<{Q zZ~Ig;XUa6br!UtYJlNExcC-1@ba_wV%X|#p?qP3l6-Nuj5=D zUn{PqwOZkVmUH+T|HdE7-Y&G!ylkAYq7jriFEAJ!_tq4d?_QPsJ>l63J4JSe;3e|q z@-5f@Z56#%vx!sm{}jUoIZk=6I#xfNwr91$-5I%(8?QfRQQ#Dl+QohAU#>OxgU+q* z4?N4=&suAi6gx-VnElkHs3vB8O z)aMVQ+*Yqy#noN8>Oo1SgunX<1))htoC7O7DiR@5a;lSwbZTP1(-d zEfy`wye?z)_^;X7;sv38+iv~4&8cu!sDVY`SK)ELd2RLen|ZH2T(Lj*x!t=Y9$(9C zdWD4*D@`q#jb?3YXA)ROc-f$~JFYTPfgmLb85az!|qWwv{SR z{N+TqanE585M|g<$SB=(ApX08#J{zdpMT|Q-ucXN!_>>~X0iWkD*C3f?2q!{szzZ3 zC0PdL+MHt#!{;1`KgzZ5=WAmQCMN%awG#jDGOM4NCsFrQoxz2b;n}a{a|DIeIea!w zF^E>!H+OyP_X9O%kKTAHE68vECBWnm!l2;rlqYkJf=HL>98-R#_Lv9Pny=;@xheeG z|0gFy9n*p}YjkoLJS0rb89raPIwW`Vo#TqfEZ=;qSHHj5$Ep;?TCB#qKK$u*#tD-q zIWcs^Fq~noFaEPnLw%`W?M5ypW>w~cb36|#)IaK|+JAtPVIp6{#m`MS*JkZ^D_JV| z`nK(d_nV*kr<}5vQF7#95ae(8$ry9s-!Arvlib$$e?Kt&eA@r*CyuAyP;&jr$q>lc zz$$P`W}EK!1JeVovhFkoED=0=mpSyOI#08Q5L1IZgND97zcj-mcYTg-PT5%&jsL|w z9?aL32yHv)t!(tPJ|!tdb3b3hlP4))pXdC`wRp_Zs2TR;IA_6=`3I)dYq%^D0EKO` zm=ybbCf)GimWCCK4Hu7!ui(5p;c~~dQo;#Xwf?A3jSaN&~I8UZb3pmbsD7mpk z*>6Jq=LIW*>zEu&7#_5#ZWHnTsA(m)&Z3cb^#aBvf}v0MR|>c+7GO}4WALus*C5?q zEg%UplHG0XCt-#^EDo-&t~VGQoP@F%R8&<#O&k>#1||l^h870U;@1hF76}W35J-f< z!HEG>RC$2A{ll`PgtT^JF9H=lDGZp3=>=g8CIFP zuD>uPuK!c) zXS?p&y!EOKJI*l9 zg8xOF`_kXJ;j@~e;MDkXW|=vQ!nqgM>6Y^dFx&}a*ngC>OWwK5U&vLVfJ0%0;J2&4 zU&v=NJTQ-YnB4N*!(-7=-h!g8bK1811sPh3168=zPk8As)Vi)An2F`1B~!!E%88dI zo3Kc@^ju=r6z2pbS5Ufg0;O0*MMZ`Qpj7MO;laV6qN2hZ`On_lTl!$*iCSp}1_lOC LS3j3^P6oz|Q^~=Any*K^)ybtzi4LZAz|K8!R{pH-X*I%Pz zf|dp?4AByOq##w*-I35E8MOTJ!Owf70wh=+LOoT4HWtaSpZ~ndT2YXFmDe1XmJ}n& zwmBWM8^7%4y%}<}^zK`x(@YZ_12yuuUCh{%z5TW`Lo(0VG+~C@+fF|JTzufxgl6sX zX%TDJZB{q-J1rsRH}}b{Lze6G*9F}E;Z}AhF-u82Q--laV>!2bd^C zAf@m8Z2O4nlIhLAi*9W5{ncsB;BtLM(~6`>G2_eafuTmp$xG&nJkQ^0Q}>bWeRy^Y zGmFbV-}nnHMy4C5_xs1%)%-p8H+_BOhZ<8R4(k9Hft&kUd(X}f_-%BMrIJ6@q9%kVvyJZHK8 zbN#&HcN}J`$;agDXZmWzuQ2Fj=nFW1oDt=x|>)G2~&6y&f4A6(u1d@j_BY zM&@k89g_x@4!6aXj5?>4=K3vuZS}BV$DYlbKbsylxUz8J!tZmPJZ8^$m9j`6sgzMO zFm!5!&aqFQHeI`Z{rc5Qmx?6XGq;pFa5PRS%bBg{b9u>%6&%}d`x;0r*|qD|)ytQQ zoj4MC`T3uhD*e~vQxI`I`1qqhr;C77Kw#j*SFc{Z%FE5Qb&*rgVQdlTn>EGg_MSNX zl_6Tv-+%x0a(6$zy3rxwbjgjS49hhet|o~%1;oVs5qH-)y=c`cuWj46mq$-a?d|Eg zu}oS0K-9#wEB~LV{B`{N^92lROpZ8RC=_}mD8OW5u0G#Zg}L#->60l%sU;;}9`_1Z z_V2v+>fcB1Fa7ZhEw#c-EiB*tH10EpZVf18ukh-0TkPZSUo9mib!&sCYaDyk@qJHw zJG*C|-8LcMKqyZf?-`RN!3ioWSFcu<>P?!JW*P8`BY~GG;mKsqu9&4;y7zoK7#=f^KQJ<~^Zaw=sa}j4eGJ#M zq!ntCUnoV#&!{zs9OZ?wKI)S7hk2-nIjt*t4B6Ax|B znZPw`Szu65)A7fQvu&R3XU(2_;>h&UjuwMcF&aTi0Y8e;rXAS3)n>!`HvMJmihkWO z*{^$yv2j`E&F8oL`0g=I?DTAzlA>K7=^mxt`MNB{k3H ztK|MWKV(Bg14US;pWb(;XkW`ss|ed)^VfBRTIcOfWq0y&I;c>T*A`ztXHoqd>9~q= zq3zEW9gF93T9hfZYSpt6h4;aG^kztMpYp2RVrD9Iv9#%-!S;6_9vKT~9F+VPE~eO` zAQazt#&KJb!h6#g`*ruD58n*lyTo^LcJb#gymco&d3C)@?+e(SZf?C-ZqY~8dy>EB z%wMMe@cVlwubH*;WSMVVb+|oydGW@gHQ#e@{|M%nKe2A|>)Vfhh>5Hc?>G5;>Eg3D zI}#dt`9u~y?l-?&_GP1QwiR21y`tIAxPuI>Cq6#^@qU#8JJ(O=PyLL4mh70R=4@|k%9!{0?4KLOlr+~)oy$HaIP}|( z^Rw@1@8xJ}NJi;``fmrEBTHEe!eH-*< z(Sl3ucFwc!P5Qr6|GjTs-aGA;lh4fg{Bw`zr%!5?p7(yz?qvGu$12+O|wcfPy2LYL55M`lUvR5Wgo8Y2z?l~^6snXwG1pxjEoLda)!62wM5UpySx0qx}Kii)Vf6m z*;_BK%zI=PD8tA8uI$>PDdAyZ)22+HKL60#7bhkv|Ni#={{Gs#yGm!9<=?B};MYC2 zc20AnbCT=?lOl#A1;6U5UH-1!71V8D5fv1)Y5jr) z4^Fpoi?93r@ArHD2bY)5Y7IEMlRHJwq=-Rd&Zo_~vfGWnzq|WeGm1g;i?FL4!;i-o zggyk+ZF~9q#y*OWU9|uc`;FVfUO-&v)RqL`hHwwO94L^T$d4J|QN8WT^0TwAuhDTd^ zXa3P)xFN{6p^-r#cJY2bhJS|p<#nwxE-m4dv9GIn-XW-bEbsol+WF7KLb$Incq%hB zC+LRnE!v9oT`2hII6O%E_Th%tzm%Q6@VY`DEG_x7e6 z4j&Km+uKx~on?Ce;pu4tEK=qSH(tzN!??hh>47Pe!(RpmnYX=D;!M6C^$jokTlv>; z_fa3lk~cR5Pn|mD_593C<6l$5<9@a&T&Q3aIM1lyzM0{gAcN9zrU{z+Cq8-|a6s_h zW5eJYIVK(Ff@_iKj)8%JFaP}f%*n*kV8vj-{UDUdA%*FH*UzsPk|oB#sL;fa@btg&L+08&`_D5w-dVMJ^}|1(&xiki z|K#LkRqts!lFb4?#A3EGfpmDPFgbkrf0p5Z{WbHuObOdxzI<75&hoiKVxnS5Xy{a( z*j-Z;92gWBT&j2+co<$yn4oFDzhBdK|Hi+uyURkmTm9x(9F!Ag;b5qaJE|basNlt* z5XJCj-waLxU50Ij;tt!H^maU85)-&GUpsPJj-+D)gM&~W!w0qfJY0*c71&r(7u(6R zJc(s6`T5%6!rtogj^5tZM~{-e))&N|>=M=HU}9mI^oy-w#fLxtPu^j$<5q}b*z!QGdnN;J1vNFcb-P|^tyr-l zK;HY`_9q{Y%d2w;FnBCF$R%*P>8uXJvJW};Q>VwWtz5aXA@}I3J1iGv6+jA6LGkMva_Sl;iYl;t-V@Zo!{GDtX4~8a z3;gHX$t|{@Zz^z?aT5y%gUZ(jTqhJ1PP25Jbto}+2+y+PKG0O3BX7nR`kVE`R|c?m zMUF7{Fenr=S1^3YbLf||y)s3Cxyho_qrJgke}lq>2@DOsj+_jELi_^EjeE2eyxC7Z zV}3DPj^E<@o#Lx`j=NYF{Qh6_bHlyDj%*Vra8x+iID`qLm?;OaFq~$Jsd(5bXUJgJ zd&1cv!`uy|;L3Dug)$DIbdD~D2ic4j3>$n7s7{Z0l;}IdSN+;r=C=``xLUF=hVkZ= zgpGzP1R1{cer11gb+vdxu*CWsjPlMC9386ly#$3AJPvU(MqUnBT*~-kUPF!Ggn4`^ zwTuy36G}OaRx3Zy=K5K?OBI|vS}uwTtYz3I;SnZw=J)>^TZZbq0}YH>o*QzKCf#k=b0Nc?N9&T{7hku_k^3XnE$-)c-Jsb>B{LgO#=@G1w>=}+xup9UQcjcbe_^cY_A9qDTP@!g=9{f$Gz z9Y!VwPY%X}EiE%F#2t9(@bv-C7Rc?ee2r`_BVUS~ZvrmpWq1RD@MS|hT_YbU0H3b11+@x$l ziN%qhZ5HE!94CR`n!2ch<$iOoTx}|0_`%IsC^WIaPE|X6UCtIz^s&e?7)f+!vwmK> zdlC17`{|4hf(!?n8VdY+x7{{iY*1O!=&;zQL6qUfwS~^?q6}+-H(ac~-O3POdBVAo zpU2G2TOo;YL;e3cCWk4^4f+B?vzxlMG9>vkJY$-{cp#kP#3RN-CXd`QIRzLznqD!z zIP{(E31h+*w+2Cm2(1eo3biqA)%^VA#taIb6O0PG7#_XIz9QOT%DR!ML6YUo zIi;L4AeXir;T2#!z<$?-L1HTN{K{uDUl}EaF>tUjC7qfu```@?P|?R}%}~KA!gwH^ zaR&E+>x?YSOpmHRzt<1bGGhZrwiw40T~W2y>sA3Og1bP z0WWVhw(&G}usS$8GBPd@WN0uySjn&Htebx%yoDXykUDV(E zFQ~}tU~G872C6@-%HPRcs64q#xWK{iW@FCM1T`Jy17@|;7W~`8$9K&l(PqoQWr3OBH{jJ$hZZu9TL!a_N*rWu_XI$+Cq7-~!xL!nHliluz#xlQz}$^zv%Dwl;coA$wT!0jWuw zzCHORlKQu{ltU+SQ_7KBTeIsg=rq2{XaSYdOBg0>Wf#9(nA98)>B|s&;M1p1RuUa& z?T>z$zv9WGpC67(?9@L~njqU5q1M#cs2hDQnzh2`sd^2340o9lPOWnPch;@)=A^Vg|3BVs zPr1fiZGUZL$J*!cN6!{*Pz-jjOmnmHIgn*R8o{{DOtt8MkSkn4s=if^}HJGk}76~jBH7O{fD zcLPI1X9i=(sr(ey4+kA&nf`owu;}Kw{ugfh_D|fI&%$vts{Pls-R1dLZ8dat|9)H4 zt+y+#*}?MniCfQlLLljwWy;d$Ev3KrJYv}8t&qn2BZ7gSZDzdwyndZe`6j<+f3*~e zWt8K}xp!!4cpRs?-<%5WaMO8>?P3qZ7BU_XTnS1nEW8Y9%Z&fu;9%M`?_W(W!+nzw zez8x=o2LHEXOA#?I^EIbP2q{W$bt>^|Notf-Cd?DXO?p#{^NbWjCBsW&-nxxI2d}5 z^G;xNn3J*~c3lodJvgO&!<@4p5 zZks>w;=jf*je+q17Xv$=i~@tn1&0MUq#eu*k89{DM03T2eNonCGb(0WvxcS6ZQ;LN zHL^|rYBr~xeQ?QJ-?ry&&#woG?k3?2)Px>0vM@~Az{ZrC=^)6kDYX9UPNpRH_&~-~ z<-JpX>Ti~M*x1vF+$<(Sw`zklR; zrR$X5Z)uC|^S?>I6<16UW1sLV698(VXD-9degXoaaj0H1XaGnIE+h>=ru<9v<5B_4W1l4B@#Br7Dpi?^TsD zr!3wXxVK@=oQSZ;`A`3!7Lztz#kiqq1`pGpzsr1QTXl7Ji}%S`ezL4tdHKOCt268@ zoD3`tZSUD`1j^n0vYn~<;)(3gm+G^b6WF+vvz?xmbp%|VpWN#iwKeN&=J$7Z-(R@X zz;%#IfkA*lYP$4+XvRhQx@^}{o*rK0sWi<#NH9SIv|>1KSfOtBRxJ-AGH z|KleM7e3GuT*pxEwqP1V0z*SHJHHTv)C}o_kT7j6LHkp$kDpxh&rNxMPTKr6*XD0$ zs1P`jkYsW?AnjsD!{L$#g*yE2+ROQ~3md!&{Kbld7&sVs@9|DJ%9J7?5X-QcVfM`{ z@fWX{&t_1`w#o{#R*-&~dXuSE{+yW3`-_W@Pu=laU`HC)r#uG628Ih2YZ(?RNk`6` z8*Vpqk)-Cjd(Y1YaI!Gmcw3~hi)T|0%kHE5#HMmItp4!OvEVB63x*Yn3?=~#YwG@I zN9_HWy{%BhDAK<@gePutb-1f^B=3Fs z_n%L(-rDH*Hz0iKn%4r$Uq>wSDLwvi*P_rXR(GF2E3r~}zU+j({=J{aw|I&HTE3&dv}#{p1yk2$Uy-9pOX{nSaRh50 zTcLD8V@Bo^tp%#VoyE)%JP#V!7{u5wTyA7xkYwPKx09KwdvIPw{c0|TqYjCC927Ek ztoYw`(tpW}#sep`1RN50B#gZe|Gwj5K1;sRb+v=-wU=@yLzjk{bT9CGc!RHGWjCLAo^%gAtM4b$pQ_eHxGuo`IZE`43Lm7&3l$$^=i^dn*K1^%nteIK!;X=mB z_4?WZt2U?n*}$>kz)GR$wy)D87_PE3urV~)e>|lxS=sxyviu4RCI$?DUhZAHp-XeWl4NDCo4@yWg(HPb zhp+Q)u70pIqDWvz-&E&QZ}mPLbX_j9PiXtA`~CM4s<)WW)=u~)Bh14T_8=&khe3xo z!igb+fg$4SJ+bMZeoLo*H}n(8eAiehb#YnsgOa9l!Fh}v6&{wo4iCP(wEoY}bRc|V z@LLV8GNV-)_xIT{E?CIWp!xst`&>ailgEOmuGn%U&T;s6O0j^SiLvo=X2MhsRvS4- ztL5AGhO+Y>mOt=%_s^Plz1ybTiMM#s6xq;K_V$(_Lk;7BiOTLuLJZpGIc`tyXJt11 z=3!E1<#&|2xZzZQ;SL8TKV>ba4f+wZ$H9=L@#*tPDg1My`tH|;m9`$7 zQ~Gquq3`>-^FC#7*?!yCDmtMR)lR@{#yGEvC z6S(?96Q0Ux%uCxV$Z=rLsS9pP4h!s?EygO&;CB2`P#Q}^2HSxy-q}|s#&7(wi%0*A z!cO)y1&%Ye9LyX&4QdSs;=eo88P0A_xV&g~^P(qVhZq`S8#x&4m=bi)v3jk0KRJ3| z-DIm@zm1$eTIVjk?KIV`<-nzmeg009f|Xum9@%yGT*HIil@}{A^%>+C4l*z@Es)vG z6zj~;VZ>nYG`?@~{_7l>#Vt(@cbV@yB)pJw_;80g{!+)krUT2hoOg%KVA&Tf*3Uod zX8t!DMg=a$4LzFwchCMaTVH=Gm(vru7A_XMUF`pw4jB0z==~C=>BpLL%x%l(Td$ZI zm>5}^9lFJIIT$W7F@*jsL^ghwgo$ojxE8RkJQeWk-$761HZ{M5oYqd| z{Zp$y^zt)ssE9c*bO?d`^X;5kGxv$CeS7NPZei*dzS3O%AX&*H?RWjV#A`_WQOUeYcnwT?|7$LW`(clRnvgL(C)4fPImukhYkv>7Cflvy#0qKS<2AZl#?y?j$l=J zChzlYnM}DwmmD`%x=sxbJFHmtZ-=Z^;F`L&*yDZ&*D0$f=3msD%5Nn4G;)$UukT#LMG)EMlR3tm#pDDkizSPq(ri)V06UoO!*p-QDdQ*Ug{P7aG-W z|E`hq>#sn~hrfA03h!@Ru#5fCi^{y$F*B-s&0mzqS$lMJ^Zt6i^5C~9R@b8(&A|rZ zYd0q^_x63Q!lR#Y^uVl(@BZwlDT>f5n8tR*)rpf~A}_Vp3VJ}^0mr}^Fg|J^_F^tr=~$2n)EsvO?+yUpqNv&cm2`ZUft=ASFePczKUmYWg# zq-M?obtZ==Oba$_Fvwvz@$NX|0>AIAp&2)9-cQl-IN|!;!SjQr-vI`L@|5>gKZ|Z2 zkLW7pt*@(JeLu9#*Es0G*WYaiB6T+%u6oyQXjgFbkEvo-*rEg5444*pPF7=S=wNNg zV=T~J`c&b$UcLwpf1sfJ5`gzB^(ei?qWdwxmU{C_WhV7kep1Kn1KHgAs?o**eNF8uiV|GF=a<#xRO$-no! zu7|I0y&8i6LkPo{{jPSGuHUuT`!=ip@daBCuhto7o;`3dUwG}|&9`a44tV=821Lek z@0ZcpWaGcgW9Q406DCh%>8x|Pzd9@H;it;%g#3q^(S|(|S5~dxkbO?l*GMOR_pWP< z@j3bp{&lyy=Pwl~leKN#`9JHc?c>Ku3d*X2n+i_*__grd*%TyP5I^acg|RAQGLPm3 zC2i4J>ASW)X|bqy+CBf_#q0Y&RGTddo%3M&aZUyn23ZEVs`n-gjjnSVJac3$F3sLy z#KCv{x^?XZRfGF147&p(_3D18t{1$i({Qvauc|zcxp`MwbNcRt+*cV{Z=#(%z?`#a%X_WZ;14qZOfp~HFeW=`0l2Epc;Z2J;4CTKZJ zKB%kw6UM~A$e^(L|Jpm#6Bj<%c9;3j>?y1pZ|Wwz+Mq4SFnu=D10|M*$aA-J_Rnn- zE$==S$7mj3IB)HO2d3Wf&F?qnIn){M_-|*kNBvIqS$UuM{JZtr51;w}dwSFok#(m0 zOf!2=*l({DWo%$Lz+mveOkXu@4O3&8V?)v32mcJx4;7eBRnwjm$oS!c_pz=-VV1-H zE^yA<`#9=q$#$uY`4NBlB&NQ7S#3YdR_vVp;*}3yzd4>X*IqBCdupetPzLLUXO14} z%De8*WpH4)z)-Mzp;C0yfwpRcZ@X3}Jlc?bHQ$=u_p8`U%~hSJnI9Z#k25j}J?oqB zVa5dGY!Cm6f&zZ;`oC{po_8vAotk-m|NXGL&kE-)T2oQJd7DnukLxxZ$8wIyuFg|t z5MU5sxPB+ka*_73M#o}-9@!i0d6IR#*?%nxu6|KhKcdVLWO#fY&u^~ohfR_Cw|Y`$ zALmRm4EUj_S5;ZKJ8jwg%1_4f2io%Mh4t9JORF9^cIcn_d!yRkK>t0=@$WP^{^hu+ z+H4lA|0~R>ki-}e5+Y*8kg`gwp|$C>LqeKp!T0 z$K!m5sm9l8zHG7NeEIA2xyI%BvI)OtUa$MgedqI|<3g?_d;Lz&G5JyVQswgX9YWnD zb+#po?p<^Ha@+R8zklCcelSd!J=>e1BZ8qKf+0=e!(m&8oga7EAIeo;U)=le)~#>5 zOb_Jo%i8iy{xrSW*iiQD#AmN;SlTcDcbdNBhlT0UsdKaLgznw2gJ+jd{W$}t^ImoT zw|_{x{Qm8&rRxuGukMSQAH86+VDk&P57qVR3<3->3_A`xOnmG5&(`rl?VTNtwGYd# zG8O11Xx@`^Vepx;_sZYJM^qei&)IjJ&0)HFPQuUdjrguTHmdw>e-pDYc<#36;+_Uj%`1*;WJR%q{Gz#wB^XOqKlV#hbWXyv`B za~gI(T-)@qvi9M+KxWg;@#Ty$0to>L3=-cj7(M=;Bs9N=)y-Ft?YaCF3(?De%y!@Z z{pQ+-e`_u-oBw-Ty7?jdf7=fiw4~&*tUhHtnbUtS`~N?A(=+uCKWJ94$UJg)^?MF! zhN7Y(4u*-`4c5FO_w+c<+->Hp{F`uUM&rH3`^DHT3YG}0o4@tUG}c8MezLFJxgf=~ zdDaaVXMrszeVgOMX3hTKeea+CH^)0UH?DkHZ^54MWofKWeDl;_=OVvb?)mf6|3QJp zTE_?7)dG(!4!*lu4(fICFqF?^`<=6sT}@$z$qsp+s|*h={EPQz-!xszfuU#a)+_&V zT>dVdeCa^r#-iiDyK8{Es(#m(8y}JAMC($+Q)eVRh>9CRz0l$8B$X7H48$bYMLovePnu=>s!$0bjX0;>-GE zL#F={)@^7~b~VU)CbVC7f<(`+@a2je1$<7qJ~4M+RDL+AxvbSjdf!dX$Nx4R+;Gak zR#R@@;eC}mGr!fhJyw1C!KzH@){nz-Ki;orU$E_(VqjFZ0Ly7x1LlKqpt|wFkxt=6 zW`|u|470i3xzBkpZQhk^6sEfj73(}Pr*-W+PJ(qJk=&k$(p~XLER=nP5c5j_>!PO>H zp}(KLEWi1uwf|X9qn~3A=Qrzb+A-}{JJ#22mF1hS^|6RwXLkLsM{*K1>zE*sd0@Bi znYRa+8_O4P8^p3ROlP|i+w`}cJMqHb{>neAAGxn_wE3q0^Si@y<6Bqus!N}Zyxg0{ z(Ohxn^Z%Ftmg!IS=VUf*KCUBsB2DVw$2r>^|Gta-Jca+FR>a1{&8z2H{@7-=s9vGK zHu{4IV*|qiri4|zM?c-S-BDa&U-96WjUw+0J(gSD3=$RhlP_HlVA|jic7C7qxtAB# zwxK8~5HERJ2u4RkT~NpV4|f@BM##{cCc;;##~W*a;cv&AkwQZCzF>(6*+cgEIV_8UKT2DIK}W)1H2(=pqW zEc#^0vI8r;tq=1?=pVS%&U#sD$LF8DAC5hXXO5D-c*+(u)@8zQhx^&ZKL^${?RNjt z9Ql~7T6^KQ3jQdDaR1m8VViI1%IhNkC~V2SdF8QfsdZC;Zy9-1fd$xGPhO zgKt)FfKX7({yyIC3xd0KPP{G?m?QTVu1yC*pJeBoDQVliy8M;rMZdv|dPnPwnNy!# zfB)(5z2pn&7o#rEt8d)F-aa3k_x$GBNSZM$;oEiId!q=$hR}v&L$A%&-+N>jHXnap z`Q(3N@DbYxj*_Rb+=@3g3;zFnN5~_=Zr7534YFU4n%f_Ib5*}7RB>`sY@3XQ@RR{lmbdb*e%n81u54gb z(AMr|5ZK6YVs@HDL~zZjBa^kC*Dl_m`QpbxIpvMc#wTw!-_2l*DZJja;GCO}Ur+P- z`SEEl{%^K^U%Qc2%zpQ^<>$p_PwEU~+{M0Q%C6wp4~Ms>souS1-rn98*R}SH^^IS- z_YT!Duc@86M|W1uul0LPlnzYX4=UTW7~XAP8u4!z`(*)!pTca_nhV#>sW#xa@%kKV z(CdS89DDk6r_N@U4tu`aFYtub3x|q_Z+k!7nJrzr&ZvZy_pu<$3|o(*3ld}IoLAT( z;ke=3Je!QUlki`c7MUq&Urnz1?q0*SXe|XAANRn-$B6^1ynX8DYd>4xD*sJp;`txU1}{_> zL>%Mfn!i_FN4z35G_=yqjg3*^5#xj9_j5(nXBd_-oSAJBa!;UzkwLfN-S#tEdF8B6 z?fi7_qrr<$d2);QH-G*g8F=YIW`CmbBqeF(z4`ANSG`!%E4fTqQ8Dm$=Tn6pYl5?X zNZg(=+w;X+1CXSsV1M4cQ6~E^*iOZg|VaG~H*X+`9MIgXb|_kz4=g_wEmOe)aG5 zSL@g;*t>83j-DJIsiez7JW}(nZ z^cc?YWHI>dxe&`Bk;BZ;*t_A(o!IROT3@X82gm-qzxaoT_4P%URWBrelF6O5LGp>h zhpmFT`{ri~yB%t{XM5$-!>}8x8+?PGg`1}pGtIj5Z~89HvzykrmN_ce)qi4TVqk1w zOnA|(wygHSv-dq0!=srKKJ97HoiOt;ThghIyLM?gN8U87{_#Xw{!mxA*$>~>2oRZ)hm3xqmptObPK;3eiN42^oe(+Wd60&7yRZlFgP$gU`Qxs zY@T1pl*ag_nN@fVgTa0O4;!Yo2|qahy|4LUc$x8>AB)pvy$TgC&D(yqdGb-!x*f*< zj&bf;C?&l3z@6R4o!@t#Gt~({cP#J7`PxsWf8NI1H&))xHx`hHW?ddP!wobtB_b}~ z&dYF;hhbUT+TN9i)E3JXGm810F=jkqXi@%&Y5r=SXw#Do((B)Gn#UGPZpprPWW%|J zeCKaZX3HN=*5Ch@{Yj5|N&MaBD@#;n*wv|gd0n}2`XlRCQOkF~yFYC|Q{S`@C)RUb zDX$eCoKcAmZ43JD;K3=fU&4-I$@1mG3`$ZAZH;;yH*6F18YCII{H7l|b=_{#AA@=8 z=Qpg}GR=3>0^Yo}hR5sc+ONHG)zz~;=Wlw=y?$Ao>Qd1QkH77F_4_98quLKg4nH^| zuAyPW$KF4;#kr#T!3$1{sz)j!^Q?GX)w?gw(wI7b^M}7%PMmGZt}1PQSX=FwS2iuN z+vM-RY4Y0-zfr$)d1`XuxklsFq9MzlJZP@Cy;*RtUddW(0ZGT>H&^?ZDd8@eSMUf4kNFn{-~RpX>YSS)zF-INmRg-x+%KY5~u}pdJMc-SsV- zUTJ9@Hf7Y>@H`(GP zPE+SxIZu2O|2xN%-rt28I2e8`J1nv8Z0CoY!TnWYf|5U8%dI`vvZncRg4rUAN1y-yKY!lr@FwwVk^jU#zVHi4 z-_~?%iB4E<%08Q&1xKd~F(`>MEM2-Z`|;z)hnX6dB=XO8S;Kt3`Mk_selb4@k!$)> z!_S4T_|w1d!?yGD-+toWb>P;|;IpFlO`at%aeT<}Kc+wT-`Vr~4>N23Z{zO2_CsJn zh{DE9Z4K{3r`7c?hRs><=D#oh>HNUh*w}fqW_f|qoC;rk#Ow>q0xx11BspF&Gx+#T z4?S>2J+ATk|Gih&pD%O%e>&mmUGrI5b~mIxwyIuiQN46`{o5AV-TVh)`)xbdxv<{Q zZ~Ig;XUa6br!UtYJlNExcC-1@ba_wV%X|#p?qP3l6-Nuj5=D zUn{PqwOZkVmUH+T|HdE7-Y&G!ylkAYq7jriFEAJ!_tq4d?_QPsJ>l63J4JSe;3e|q z@-5f@Z56#%vx!sm{}jUoIZk=6I#xfNwr91$-5I%(8?QfRQQ#Dl+QohAU#>OxgU+q* z4?N4=&suAi6gx-VnElkHs3vB8O z)aMVQ+*Yqy#noN8>Oo1SgunX<1))htoC7O7DiR@5a;lSwbZTP1(-d zEfy`wye?z)_^;X7;sv38+iv~4&8cu!sDVY`SK)ELd2RLen|ZH2T(Lj*x!t=Y9$(9C zdWD4*D@`q#jb?3YXA)ROc-f$~JFYTPfgmLb85az!|qWwv{SR z{N+TqanE585M|g<$SB=(ApX08#J{zdpMT|Q-ucXN!_>>~X0iWkD*C3f?2q!{szzZ3 zC0PdL+MHt#!{;1`KgzZ5=WAmQCMN%awG#jDGOM4NCsFrQoxz2b;n}a{a|DIeIea!w zF^E>!H+OyP_X9O%kKTAHE68vECBWnm!l2;rlqYkJf=HL>98-R#_Lv9Pny=;@xheeG z|0gFy9n*p}YjkoLJS0rb89raPIwW`Vo#TqfEZ=;qSHHj5$Ep;?TCB#qKK$u*#tD-q zIWcs^Fq~noFaEPnLw%`W?M5ypW>w~cb36|#)IaK|+JAtPVIp6{#m`MS*JkZ^D_JV| z`nK(d_nV*kr<}5vQF7#95ae(8$ry9s-!Arvlib$$e?Kt&eA@r*CyuAyP;&jr$q>lc zz$$P`W}EK!1JeVovhFkoED=0=mpSyOI#08Q5L1IZgND97zcj-mcYTg-PT5%&jsL|w z9?aL32yHv)t!(tPJ|!tdb3b3hlP4))pXdC`wRp_Zs2TR;IA_6=`3I)dYq%^D0EKO` zm=ybbCf)GimWCCK4Hu7!ui(5p;c~~dQo;#Xwf?A3jSaN&~I8UZb3pmbsD7mpk z*>6Jq=LIW*>zEu&7#_5#ZWHnTsA(m)&Z3cb^#aBvf}v0MR|>c+7GO}4WALus*C5?q zEg%UplHG0XCt-#^EDo-&t~VGQoP@F%R8&<#O&k>#1||l^h870U;@1hF76}W35J-f< z!HEG>RC$2A{ll`PgtT^JF9H=lDGZp3=>=g8CIFP zuD>uPuK!c) zXS?p&y!EOKJI*l9 zg8xOF`_kXJ;j@~e;MDkXW|=vQ!nqgM>6Y^dFx&}a*ngC>OWwK5U&vLVfJ0%0;J2&4 zU&v=NJTQ-YnB4N*!(-7=-h!g8bK1811sPh3168=zPk8As)Vi)An2F`1B~!!E%88dI zo3Kc@^ju=r6z2pbS5Ufg0;O0*MMZ`Qpj7MO;laV6qN2hZ`On_lTl!$*iCSp}1_lOC LS3j3^P6J+m$EJPTRF|nKM(1zzKH^zPT5ic^9^bJ-WcOti0fbs>6%D+b^u%c<|V=)(eJt zJFdU}efGtceHSy*%C>o0y{xR`SUq>k?n&SNo{tu0Il7XCk1?dBsDML(gKd>WpKzZd z%TaC~1}+XJh9_TcsOZQyD`?m-STN3Gb`)Sx*pxi!!w+!>2FHtQzWK7L4SFqe0ldp8XjQyl*Tu`MSQ^dLaW7N7EYx2a`*& zm;UyBk-1!Y^)(}#0tdr`*AXjv>XlO8oW8MCsZ`VdU;`&ZL5`xw-b2%S%fesXKX;|{ z<0Va-cOKVexg{7l90V8+RDE4<^zY7%@QvQKyG@p?n?BLfgu!t_F(z=%d|8o8R z-qwtLAJV|&nUL^Ff`LVWqs?pQuW-r#ldg2_?~DEAJKwI>h)+DkWZJF5N#eJk-&(PH z_3Fv7x2&zLZ%0K(PcJVi(MkNT7qzS8<)PHm)Bf(M`1mOD@-pAqAFqbT`|f|aZ1%p? z`b~3Uqi!x$Wo6Fg(s*H1!Op-Wz{0SBsX^%EkKGIxj_5T@GVEzS%qrSFV`1NwAFmsn z_RRl3+e4+J(ed^qRqr&Nhz$>p-`t#DZ=Zf{j=X%`kB1-E@BjD9pGjoL^fFGil7~zU z3@nTcMtzQ*Nl)La{SDpZEzieb|M$z~*A^)PVgC;@I@ImU`4hVS7(@wv)o+tk8S$`77#luV4> zq`<<+(8a+JbNGFnR*+o7K23%SnRcE-ujbd-A6H{{NWW8hd&RB~6BybXmMv3@^W5-f z^Le}KR0(&5T)`~SZ>q1p& zgCRgf==HzTA`Af&nHuV@++}~DC}Xr@Lht_G#_gv6teDqF-CVutU#Mr(riJ3~iyIHr zuGzC^&aW>oCx2l(GuOKO-GBDg{na)BW#3OPP^e z`R!J=`i6G#fbx?IVpJ8*%Q3JxFf=GJGn9XgpUBjZ!qLEZV6R0=z($4!U6CJ=e}9|) z`&7GWV!CQW)MNc`d=D5d?60rqW8}*={4>4yyzTd15f+`rU*<73fPA}zAz&RN!*%8l z`7%Z;W-vJHv*%#=QTX@uoqwOQ;!Qqm`dA+CUZeM&<;&~q{cL2T=WDm$+x2IHGhgL|szZ#qU}x-M2+*r! z$Xcn+!obkLz#z7MVbtB#^OBhvR;f5#e5)^A^WE<1=O0TKE^G{oExr1rR)#5}^1)Pg z`I--Z!TMu<&o^LjNabS6V&y-u|HIbCEYNIVqEE?Xkd-tZG-6axbTg9Af@07HWG`|n!6g>40j*6u&` zV|5XWOThziyTF1I6BJJtFi0EvaEt4e*!_6WygI^yL4bh~q>+K+m^~lEh1lnAk*QaF zKi1zZP6>GMwCPiFO~VDL>@^c}M2@7f2UJ&Ee`kp~e2DQo6O#g18v`SQMg-FW-=O&~ zE6q06i?W;MMqcKF|-RfFtC6%C@?hWGO+aiziq;5boBet z>*Bq;IT`N!zPVw+ll#3AhD~j}(r%m6&MsQ&JzXt+Qm+Ubivw660~3P_BSW1Q^OGC< zvp&sVsX2j*<&HrE2lJnOzkXFk)&KoES(f=`{++Xe9#u>cehdoW>Xm^(fI(pmyF=W@ z>R#qBMuzDbXO8aw%*3$K=K?FkKmG3vYxaJ-Wm5m|&lAl(V_0H1%tyEhK_j*+nF5p z^`Ga6$lv?*#q;^~e&1LFDkSE3PF4fuBL)VBCI*H?h65$BZA`(B=2r;z?$Qzr@L`%F z%v`B@;Mwf_c{#o|#m{`6PI27F2}!G<>V=)dbjtuZD$$hi_kGyI~J6)O`yUF*d9?&A6D0d4*i~)PFm*Ys48!6dCH~`g1-Jw^QA) zbm`F>IUPY)8QW#cBp{Khu)<<5L!BD)tFS+Di3}GXthQpgwdKEPF+)ga;~rxLea==qP7jUp7xjPv0FW9qZ&bFIr zv!85nh~r&Q_<#9cc9=yk7!~yN8?rbioZ?RS#IWMgKO=?@zbow->{KUwXUWLTHGQUd zLbVCzm8Rwf1=YsDD#kx;j3LeqJD%7ud^Z&52#d;8y_GWAcha%?i;G+{4>y-Ix5M1^ zfhpk2+Q!uB`(NDK&dJ=t79aDoyWqjS_~78lJj@}Xp%?G$EWZ3NEcMUMm>mVbSwM9? zC>eC!<6h9rzJD&`)0K^j5~tZRFEeP+6Abvmle6t++LlvVt5sGrKACpNiof9nC#YIs zU|?Vooh$C}b&rM=^Gu=tA(FpV2{drB9Xb7K_4;`-!h2FqPD)8uD4)9iG&@ZFL2iMG z;s;n*H*LIL$q?e)kkY)~i{;Jz{q;p=4C3t!8}k{yi*zt39qMFJId@#X9-eySI5tFn zJpPh(0{g#?r}bLR`uCs3GH$B>QO*zcCsNG?*$_t;FMy${*dgb?ChC;`NH>?wE`8K_dXJ@uz z5GZC!VJOJ|{KNh|dq8aL+%HTg6xl^tPINXdN>^fKho%JuhXxt93#RNs;osLCG2Fjw zW#cMIhNNHx!+-irO!YPGvSkbQ)&9Qp=xF!UVD?-^2R?RK)N(N~<$Pw$*~75xnS++b zg|$0MnHx*u7_PRqGr09gOw8e2)BSbzGCpX{(8Rzf7snvXGDC=|w3fj~j^W~i)5Q!S zQyH_@ZVfXM+^}@1>P!xe{mXMc_zEbrtHA=1m65^GjqUXVM^UZ~o@^$J4L434zrq$# zQ}g4)!;=RcVv? zJ`L$AS(Y8V?LGnufA$)ez6t?_{Rwl18J5Lr3=B;=iVCk*F-#Ueu#CyUR5HNqS5eV| z7pyDRu3frz`@LCGQc__I4S5C=IGLVIJk)jVAT$UU9QwvIL%|{R-}28)FOvSNF$7Hg z|MC>$-Fg3S=kK@Wl`v44$-W@!D|;h@?+qqse1*se2(*hI&<>8j`a7PLWnFl}Y2EEU zeQeJd78L(whE%3a42)e@Yh>aWHkvn_KKyz8nQRs{e3MWxi>{~!KoX&Z!tDHcENw_lP1TmWzBfKZU_zBFD4ZA=90c|- zSm`(T8ZCIiy5S_VfHor=pUjLm91+36ldq{+3mOd{g**vcDZ8qz%CGRga_NGi_!35Kxohj)p7wB3#d=Rof=kZ9XQRsAiDB&z=9~x*HVm~ZZ}vM z7Q6K>GQA#C+}ilte_?X%THCdEH5_wEA4-&#hCMnA*3xloNL7=rX36hNk=+T%pLCW zc(AG#IyAib#|{lGQ5J?73I~pHFF4Kofr-KLrGKcb!Z*eT|JUt$waSxW#flXd@`9%b zC`{vfbxanP0;@zBDjXdq7&J_u_+=Ky3Zs9Iz9t@SyLe1G-{R7WeEGk>zpJbC zi+C9)m9s!I`;}@22a_3Q3<}Z#KP?@d7?SiE;{I(vDi*Dx&U7oMdXB(>rL&b*ROQ0l za?jBrfFWS#pU`7hmWelnOElQWMMio?=j}|DVu1N@feu5%kBTV_3eF8&JU@6E)^59{ zb(8zlB1Bx4AX6r7tk$JHrB_ zUs{V-R^3@Gbby=T$jXLYCmY^9j*($#iVja@86iCZ298%>84_NxCs+$bJY}5V*bvURX3wWnjo(be z1roCQc|e(%fq_9FM1bLel;>pj1OB27H4keUau^k&1s?qCoxOZPGsF6A2f=Q1U}V^w zex8lNAwZ1LVUNFApVs4*S^t!uv!8IgU+W|DPkH^XRD%a{4G#~ux8HicLeatGS)yV? zD0@upi-qm?*kN`}F=n_R=_brDgJ;hCMu$%-4&TkbGyGs}sA**QXty(W!|(4aUok*C zBn=DRFf_b0EG*6EX0TE)n96wS9z$Ws1_>q(CWfCpjGqjd;r=XTP>^<=`fy70t5q@o zHl-if+t1I;#l_&L;K0t5sOVsBe6ZH{)j3XR7m0!6R6aw%&dsHk3|jdOnk))UjuQM1 zAp#2T`C-k!aA$`Yh6P-VD~>UBcQzEYJJi)zS65$Rez2TNL0mvVsz#J$M?-QG!%8a# zaCz$>Fr}7Zg7x2Xi(6wW=N1`GC0e~!Nl+`c^&(LB}-b&Zl+8=!KjeO@j;po z;dN<-3B2=P8qO7*a{7NrXG0NR!wv2QTeeIwyI1iz0@R;lWcVb)Q1|a=Is*gCQ$>aj zIi}$4)}jn2?lIgoaS&(PQNfXLVuE6%wb`!Qh0YD(<*tzUatv7iiD5+=V~4{6E!Gl! z8>(`(ng$VxE97%Z~5NOdyYOG*#&`bZE!_jQ<%F80h$V{eC-M zIU9@D$33>+U-dfBq2b*RMwmidh7<3K=KhXnYjA94d-}w}m6vf<;GsIj1G<7N;4~n> za+HZ-QQ~JN2E%Ll#ylRZ3|jgQm6es7_#KLii_i6iMsQ5%@Bghoorf`QGV{{cCj$?$ z%hwpd!cL08ppr3%!6EMDZ$^fXb@P}Fik^5#S(Rkmld-?N@dG#GqHqUx#!rS*^I&e@ z#LVzWDkWVUvoQRVF#oql++kgxWWL)H>#(?of_F7iWLZuuon*BE z5&;4%Pgxj3*gRPoHa!eiWqMV_5VE-OsG8%W2MQY}Ib1C`QG5Q?t5;jPmjt|0OOchBA`O=WQtiZv=%1|Vo z5GvOY&k&%xb7jxI9F?a*j)Gh+&RX9^0;bA$wFU3fd%ykE)%UAbuIyyvk#P9?`}^`+ zw{E3OQu2>@vXe_jA}cG)s89F1%7^YZvI5_m5BIJ@q%n1d1x6iK&MMM6q3jOP3<0qz zhxMx&o?M@3{3><9#72kG9G~2Yq2I&9!qnU)_@&KsI9dN3+RH8@b7w*mH_IO-IdMM5 z8vCf|=*u=zZCwqb=OHDP081+q!$pHvJ&X6O3Y)s2u#+v4jVV-U!3^mIQi3|tHRVs& zd}27E$UZeN{O+|%R-Xk+UeD67FTWZZzEn`zO{H48K)*>k?t#JqXZ}A*haIA$qlGw1 z6u^xX1_h2&*$e@yjHiql?i@cVc+hJ4ehvmL{)Sh!q1yHK_ZaRL23KVk8RYziw*aX13~YF!1`}#m2}FZoq(g`ajy3WB2aP_>!s+V7hVQ z-H)qWY-1%HOx|c+-_ONUa_qkLw*1Y%wsSJC=$kJ6|NF1#yf2lX7l(#Ls!F=8Sij!? z-f>}}*!K<6N*S4%7pL)lnl5W!_vfj*T;-GIH@}$Xg9{V}289(j7#*hbCm2h(?^!3! zyTeVkH>brw=a0$5S2c+bkfracdQT0 zRxXq%C}xjYf8f*8)5do4a(orP-)`5p`F0~&-oKLR$KtRty(Z zS-RZf)<|8bWccA~^6OFQMP`LI;gZ$$ldm7nKDyq@ZJV6v`tvU8a(WLQPYjQ*{hBgU ziC=U_XUiXp9dV3?`VGzpve)k|d&j9h=Y!8F;e@6JJuiM{Xt-&GF_~f)|1rsJH`sl4IGUAwqLKzOg`3A8To-_3#Y&ZO^N?{Cx1Y*Clf=;x|-6rS)t$# zROq95*;nqeSOjw}kYSq;AN=&}1{;^)^@B1Eb7AP)IPW{0M9?N49@9 zPP}2I$5pj-Zy8qz72i?Q(kqldE|up zkl$~&-B+;B6{&BZP0oY1V+POxHpgSM;p8?sYf& z==-VJ3yr^+&F@_K%fWQD%TG3n;f((0t_~ zgD3~Mu3-^XX4ui@F8W^XOz&)UOKlE8C;mJx@RA*Z=g6%3L~i5^w7d!>DaJH%q1&Z_uy46;l@J zbN3;`O<{#+ECSH#p@JbyB0rQn;c|dNXwrfhCXH<@({8h_Rybgi7Vg7xW!@4V)$&jO zP8~mfZNcxKH9nkOS^^i&a!mODG?oq2)=jyoaDTdMw^;2F8;eH(D4&dK;gxIK;{`+*YxTl{j$a+jTJppZU!v ztshKf$Y|29-2GD``5o_rV6LCF_iMl3Ro?&eY`&hiYE&fFEC;M{PBEkZ}%r1$gG-gT#hAFwjTJ*}_%&2pne-o$p^jW`~A>(vp`{$eK^KE}Wne1OT%}AQHR6vUvT!Aw&ykKxhuL#**n#k~R*PCPI z`K$LZJUSI`&h3!8CU-S+PTP+g5>@)!_r2U(nmWVEzh%$)P3_0_)~{Z>ZPC_iQQ9{( zI(YeR{XT1c|Bw9AaJC)u8vLAsz+vvlz%ch>peoa@^Iwbpu6ASHeUCBputMmi`4!Pm z|6S+(C%AsL6!XkBF4vDUSJ<#G`^UUX?Ch%AYmNt21TLPG+j{f&wza$8?b@Guyp(OJ zI_oSc)`Q$27dr&xw>E5;3K{^^<985ex-gaVRBm%=Fmvel)wdXCZ~BoN_Dikf!zYa$ zi)%`c-(3B7{mIXZcEx|)x#GaVz2|Ooer9p}w&%SBi_8hRSoG7ied1uq;hFEt;V^aXdsfDE;tuOXCh+dd`Qf{nq2%{E?bpVYPB}I@%j6ig z-IJWXtG=>yJ$K`ZcaMFWD?Tq+8NB@7j_>!X@4Mao$#B|3mMfr@85|rkdbv!5KV z@H?;}@$icki~DY!Hj-yp2nqy-rY>0q7iMLq20nq1-l``T53Z79Qd_)!wb=)$r`rAR zO=Y=1{&W=PES<+VJCbGD6MNByl?rE{9Q}E9eVuFVuP>2@CNY;Ue5dBvtzj$jz|TH~ z_55WK#~!OEFMF21+cp1jTJjCwng5FTnqGbNiQ~PzDqdVlV6S=1>b2XXdSxsxJzUse zey`$jy{3MHjXsjcec~7{zetGIZxCmxsFeLvWs<|-z{B41mL+5Juj{hu>kH2=%VG>I zTCn=8`TaRr+1dN=798gN?_e9s_;!k&zC)-e$bF!?c{_8%xfeGW8cr3QzQ=T#>%JIM zN{4}@gY@F{)ptz4)P?^wYm1HhdAmaON%GoV{&6o?Phu)v9~vL;ADy>zDQLoNo?+UV z8PiuagH5N5$HIt_2r-zZJ(HPy3qx%jd}Pl0@-f&m~$q2X?c3dwo6sn%V7~&F3E-?M@H& z&u7@F#SjNFo1uy8EO&yJ#ph2G}of>hh%sT?n^?vCGje%C_BI`@B7 zwt3ZSeCro8G3oO&iRULA@nW{VSNr|$MjiPFoUP&qM7Y3R6OLDB86Kn-9N90Le4=hI zgPQpMw`&+Miyeb3gH->?0ioNf^4 z@7ge%`+_24Lv;YGT9sm8`N*^)inaPH#{}M;=Z>z|E;p8R-QRYP#qN`ck&;I(npIrUx_s{6rSNCU1Ok8IYz@Gf5_^uq+ zuD21@Z@1oF?mPS2hk4cSDtUKj%YAKO+;fYCnPJ0!1_q8M@rD}bhI7daKJm?%bid?w zdiIRJ(|-T7TxWKED=+H{FS)1BD@=QeHP9`EW>%ytTQew=##Ji^YLcwx0}zC%C0kct!Fdfe_!qUPbG#Kafh!&UU;IqVXpsz*9jTLHX*k|V`J}zMnzeb zg4PN&?uusq?BWnB0dgh>8^h;kXPFrs0`$K!y-+xiZPA;{w*S@7IPLn&UAMPwIsd&= zf8vL~k`=1btA2@9H1@wV4!Zic`*`>r_G=<15>GZ5yY)(y3he%Ayj#b&lHnG|RgkF+ zEC;z2;=gmT-f(L85#?xod*(OYQ#|!^Kblvv{ORE|lPr-I2(A@d7Rj=XjYncb!Qx)C zTh^=$-YWbJ9N>h7x7z5yHS5I5PnI(=g`R87m0WOxUxAgO#^8byFGGod5IA3PXvOF| z$Ot^R_kQY&bY>y5IIi2ZQ#Sv8e1h`>v+6cE-q`_-;pbW^>gU_l%3KqxFz01Z5CkR7 zh6PDqm|pZuoL~3#)8Dw**Df!GnD_E5o&Is^-7k(dOj;ENcb9venS9^7e1Dc-1Fyo2 zdGGgyJ3BM8H{7*lSO9J!FnkG${r;!DYg~`g};th zSBvC7*t(YUa5LxO+3H`|&+XXzEO|k_@%!Wnl^en&6b>KLkT1T|K6l=2&VT)r_sM4- zQ{DKe?0dMok%Qm)ZuR)Y$Y zRhXI@+B?6RtmBjwuE<$0sD0vq)J=9qdxHrZ{_JT{UpDv4-v))D`A9USVL2+Y}{GS5{7B7Dz+1-9D?W)`o z?nT$Dr%#=FQEhh4rsXUQd8!SX;815^c(VKB{&%gNRhuU{3w%hAVXk0(#K5S{@3w5> z$!*~WYQvvu-G1?NV(-q$W!8>O>IxT`88g?vJ|TAZ3x zZ+Ua~{P(*g{e-{#Sh?7icek&`fwZT2t3)oex;**5jj`F+{O%Oy25F{@zzT*CS#WZ( zW|(1LZ^xi;de^g`opmlRvG( z<8?cE_>V9%g|Yd5yw&&SSa@#styT9tKXa_9kkz!AkhwwguJD8tUkuhT@CdRA@o5SM zU0vh;TG3<0`|Y1DEU8cMcjaK%c8?);PyE$q5l>4C;+J}LRuFyzBbZ(6j&R8Q}|+ zqAvW7$G%xyDlUzF>0z*-s$aeX$CrB_9P;&)m&}b}izq0@w%xwA1o6f}x zvK5pZclaK=orhmzyPa&*Jp1DLFZ`C+xryE0#w{ie-*Tq3jD+~0+fX;QcYXJFstcbNe>%jBwLj~W)}EC~PGTb+4kU*`SPxevc4 zUpQ=|SItyXpa06)An6EXfcL`hp8qW+hbMjbxKwx32ZmD|wbKMwz4~doe(r^}*OD0- zFPGo_*b=u_H1)j3#oVOx6RUr||6L^&vY*XHN1)^8-#F&J5QcrXvQ~qOriOdze@xp{ z*#r#s>z(3oZf?k&D$Bsabg*I1+CJxRM;2r;seIuvP}9qp_UTk58|Uds`vvZQZK>NI z|MPdQMfrUzW>^uw%3{TGeu(zJLuBxu-_;kaEl|^5o zG#B<<;OB@?@XXg$Hj&!Uvh-rviFY2B$7Ij32!EV9=gYr;b&d)XPkgCU=}rChdT-IS ze-kCX7$hu+`g?ody6iu$91?al7T`?$LiAXC%+Km?mHZDx9V8dl3tWh}*`%rb#U!k2 zu2Exya{lUH9w}dh18k#b*$PO1SteKW_6@U=>4RlY_A10N-M#~{+6?4O28Jh_ z*G=lFKa{pqgpH*{TeV*}W!oMm4dpXMl8Tno+)W-bGWFdx+s@U37d#N>N#T{X5&;|ZWcA1WlVXkh zcO^O~DDW)kxnR$cu_kA|o^lTZi_u;tr~CW1WVR$aC{(@Rl)U)BkDGu4=-(TOFF1Tu+b>Gj78GVh(U!4u=uHU!% zeqfEno1A@Sph+HW2Azlv3=9vP!)-n@#{N9x^1C?H=V4=l zr&6AxP`4E~tEaBETb9}KDdN$A2TI?h3tEh){+N|{vF%8gb379`$+6&bi0;% z;mh{Ul4f!DrOwY7lztl>DraJH2wDBZ!Xe?ofktouDa7~e|2E|vGfU2^HJ@2o&a9su z|B&%_XxX9flFId`ICLEk@bg5dE6rLMe)sOVZ`JQI`c@~`p1E)Pf58df>In}Wb}e*r zgye6AClS-t@BS0k*;mmyVdK3Yr#b#IxAlgD9JX0l?nLUmh7I3$scdS9Yuupl;D5+l z4IAFRXToBSIbO;w{eJHIdqxi9pZe1E#t8?&4r7>b{+Rt2PNVgwI1V>B{F^FxjEhN1 zd-h$ULw6b+Zr%UID4tzEt^WP>-{LYN**1337ZmLH!5W{O`O*L0?+5o+S09&Y>H^uot| z48BbW*#EbtrQzALG>}0I6SyDCfA>21<;cB2H;Kb0PVw{F{%9)mTu!~fE`N-VDKWv} zf5+^n^RwPP=B}!iVv@9XEK5Gr`j64!-P!wBuR^NJu8Tk7kH!3#EWH*uS4Mc6eEhjn z9IVZ9*YA`ym!31@h^S;`YGE&8V+xbw`N{qA$miJaHD?(rnZzzwKV0OjWUtRMlLJyF zS1tZ{-r`}zyeCDnavY3I6DwIC&2h{4`S{#&qeD9mF;AQ>xXkVei>2K*(?#dCJ?1qq zvZx(nVAwWcLAHd57^pqWz~Ioc%Ov%Wu-rAR+npiH)W7u^9g;zR}|^MCKvd-uQPyp$88>wW9fo}LS*pDpOQz|_gL ztH*%nOHjx8)k~HiEj+}dCLhVvpqVy5AExt)>X{$$->y`=pE_y&3!IE;xvbHmieDR9=V1XXQ$v`VXzxJPW_1g^`zo=(GCf}2^z{Sl8JRsIB(RR|9br3 zr_WsyRlW)an#UCzEE2%UkijMMkFuR38!!KpP)Wt7j?*2d2>t@Mskc9De6dW>@6=V>2N$B*QGJAstqeB4-56(AF+^-<8J6u8kG{-be*?%-&?R=ZK zmq|nU3oEmRG7lq@SiNBQ^n*IhVDlIl7*Zta?X{QYw6hn5yRE2GI>phwqvt|@xPt#D zmLv8M-!L#({l8fL;#9i&joQdZvu?j*XZvK}uVA#E;S|T@RV%fYUu847_@VBr$%2J# zzph;k*#Apw)#_CXx;_Q1($HPH>5`CH@41EF&go^neX-!$H@~dR_X|?j%9_+?a_dGM z)ANqLxwQ4!#`<)<2&e98+gU$wiKIzBJPviA!J`Al8HJ0cS*?;@x%l+)>BrLNIoQNC z$~@`gKhtY+m~;?o%!ZhN!i}bn@@99{cUWx`#q-q#A1H_ni@^8ogY{ky2W(0EEqEG z8BXPm3G(vGjbHV`x@DSmCqw*#J8kUyc6`!UwN~eJck-##Gj3V^?AkIp>b6yeW%SIl z^?tWApB+j)^FwD@=4Y|XUn;#{xR?Db`{tDO`o$NP#uIy(3a1H1?YQ$=>vZOF-ms-} z%_Catc76+3wNB@J_xZKmwFNR9`(|!!*f(=;!@jiX`9Dj`X6{a@bh#k?WV@*C7r&iv zJ~UY`G&eB;yOg1xL17)soF|HfTCZ2^d9RSAy*_=v(DCQun&t{09^3aXo@HaGQg`)K z&;RF_pIvaje|Fi)H=iDEH=fBDbz}Ew4pFy)wFl3Ze9*tP_~Puf7hl`0Zap0(xk7ir zdI_F_gx|CNTEF}qdGdF2?e)zpOtZviR^1eYSj$i_Ba3(TC$0^L1Mcf{GM~0?u35M# z-0j9}MhDfrgn2(-WLKqKy8h*5e=*ZqgU^e4Cfwcl>wub7@pj!CtLmpV{dzm+$NKOW zOIO}+X}R}7A%Qjj(qyQ?4YTTNXFODYu*M?9&zG5-pJ&;$Nm=LPd#-!x@vT3wg^8IV z@c!RD6W&$6zWgNB(|fln2TMyM$7hzT2#>Tga?zI*o7i%U?WxpO(2c zN8-iT+DG3wAuVhMh64-_KFB4%=VW>l70Ie9#U7A(m?7)!srerq6qy^e-{tLl{B>o? z>(%n=#&=5DjpPq8&t7LXVVzxt`?Qny12)V*zW$hc?+8c85%p^8pQO12a7pDpO?OynS@qnnm+MXF(bN2n+y7J}n^){Ny zHZ4bgp1)mvyD8>tw^ZdX*)=>HFEe+15@xZ3Wjs}ej$`+ZX$J)QE91A@xu2#4(g?@ z6%Z%>U}-qoEiNv@@a5*mf~A4{OiWV}&PKkPy>e1D>l~xq`Ux(vY|HO|SiXpvVcpX; zXFi-^zqf$>{@+6zs`_teFJNrETz-F!-<`^kK$-9Q>1US5>Q^5*r@d&u(5fDTjOmW+ z{J8}fUcG(|c6)<9!vi^n;|W*RIy(HZo@Npe>YUokymIq0{%nIMNBEeIaxtvatAED4 z>(fk5F*j=`0Rg)`_8VOGd-LB}m(lm#`u|+F_Pam!EZwfk_kx$5X@1;CcZeStEbb)y z=H1V`+J>>N<@Acx3-h;!FF2jAUtc{vb0agu^}YG^Ug>wfTa}jGlz98XAz^VS+Xdm} zf9IX@^6GjPVQ==(``7Ee>$7!B&p9}JX5xezdV-}v`N}anq4f?`ZL@-7-BW$n91L*X z`!QTxWpUN9UIvDB)^YQllI1EczuVy&;1&@X=B3|zbGjSPtKg91KbQUYS*AVXx@+|m zWeF#dUFB^DwV~!RFfnBCls{28&|k#5S|NeAZ5Df=ce;tTDuYA+zpDGvCD&&MpUj)< zzwi)q`kQ^MbGMvcvB>(^bNg?4cmF<9Z+kp#L(7^67o^o}K1f1MT*26&vxiBrg?$ol zo0KF6&$4TqvL4T`ObuaXsJe16iGjVRde*%^f870ph1ghrZT75vKet{WH2g^#8}s|S z=hHPprl$Y@`@iP-=`V+#zA4cGfY?(ne*bpw&(Y}yQ89Ved-r{IAH(nW?SE{t8E+K9##s=GXHox^2>h7 z&k_9pety|M^YhE+t*0|3<<}gsQu@T`9izJDK0~{}zn|Ba{d>Fb+`qM2i|tH9uf)Xr z{(ZG-^8asZlecH@UT=OqEXXx-<@?;*&u^)}T%4aK8GiF(Q^T9}9~# z>%SL|zlt@wQ`o{@(Y)}MUCuv-Z0k1{6Fh7j&+Ym6BY@$9^6MAh=4;;nDaD-s%QDo^ zd5Vcc&g@xP+8L?W1lPyc`i9xx-aVsxmO5*cq$;<7K>oGx@C#48`TA?Vp6~s)dGh{+ z&(&%tpV{*KUeAiHQjFj)}iN zzh5r@T<@E~4Z)^{W7+~+dOj3GL-zzjgURlkm!chedNxGIPBje)x<6lt_iw~;)8juJ z&+XBaVc7HIWBHoxzbCg%UU!sVeW9th=T!rRh*}5bT)QNW+`ZSlzHpmyO_!dx<49!Q zcWpUeGoDX&oIg$rTNKPLeQ~O}PW0xsZJREhv}r87<2`wjz=OR~-y9h2H-2x4eLdIi zOz{E7gn(e#y{8{6v5m}|xWV9t*m0ZF4;#;~w-Ew6QN)_THb*0~$Bq$gTWDvPR5)$(-tKY7lt z!{@)e?w0#fY4qV6m(8!~wwbR@!@E+Xe@5J0oL@UJ?M!@D`j%oIrmVxWHW=IxtY&or z&#r*1U&GMwMZZ<}R>pB*xdhMVsar!XnKbN*XkO^I@L+%l!v=k3hW*K*d-pPx72m!d zy;8pS?tk$!+6FqG&R6_@J#X1{yWe&PJRNIScOE@!zx$C#S5tW0?>FpLZIbnN_Z>&>MqsY8tomi`BBUltJNh^Q@pq^tbrkrveEA7=w!x?KyD zUBTg?@HoxnA>%(ztE;^xU)OPGW)$!JxFnaA;n(N&KiNeRD-sSrWPWi^xNPx3->nmC z&+U%;W#IcKZJD={qMrz-Rr>n1Fa51^WwNR(e%?DLZa?MC%J-FbYW$S9xHT&AL3(81 z4P{))*{?-UG#edqUvE2KDroL#s*T1AWEj5f{N_1dd-n;59Ch4qi6 zd?gpwuXot;uu-8BsanslW?oZiF;TsesXxBnZuMS$f7{cMAFp^_Q|-H6THKs!v_!w| zPc9e3pj|SUw*l3*8gO)-Irs}U;I27FTL%!wN1d;zUSrF<)yQ%?fz%# zy|wtp_&1DYhMNQ%sCzP@*XYpyxKhZ-h(;B|jh`8d*@6`NJ@c@ML5?(w(leHU()t2%x94=Wp^|KeU7#$TVZz)9x;H^VfY$e;p- z3mWYVe~vu=+t6?*=Q{ME5H%;j!pbw&b0&WlGb^_&WFWKfgu8 zf+GB5`+6sK%*d2SRq3!y~NB_;@=Kd4i#D3tR zx&8b%U*DJVY~p2lc3rpOLqh?0Wd#ER!-|Q|8E%+8=+w3mVC1m*etzR!h6l6$iypfh zDOhRH%6`oMXVW?9@A}sgRw&&}Sh1y$!8&FfhH_bXVi+RE*Yj1TVA-@EylKY5d{zC4$FxRRmb@Bgj( zK3lBw3M*Ix+#mxA3=B#P6IeM|zH%Lrm#cW#wsED(fs*U{oQs4OSlN7FWNs)vc6VjX zY?n&W(oLN$OTV7cejq8=A;HojeSG7)YVr5~PhXh-^H=&E$IH+4HyURMalF~`Ppd@! zb&!PH94x;JYG|w$KJ4c{r<~uJ|?JVwfn02nD8v<+mzKUod3gy@#C!%5C6V+`}v>1FXQJ= z`_|We+MBp_g}vzq!DIdNt*oy6t~btR%q=~)f9>?Y|9Y|urwT?@vWoE6@`4jIIE=H? zcNEW6f551sf7ixLk||*QCt0uajAvLHzuSv3Hk?s75E|$k%gem7h0(D+nV%^!p+Qvl zg86k>;a#>LZt4hrn7@aoKSxOL^81+L0{N=%f0~`@-*2;hANOm%4ZA{+00#%NYY;Se zYnTpPTI$Ws#~@W^skW`N_Tx0cWv}hc<26c{mhcEfZ2ae6{P(-f^Szq$FGpU#_p(<$yC@nJ)~kzh43|dhr5}7ZW@1I6u-vQAu3h9X70%Vw->c479L`=va++mIjSL_;liz*9ctf>&Qiay zHau^qfh5y}*xz?&Yc(@0ZD+qR^}8fP&(3##|9`9HRfWnHujEOX+cs;(<&Q=})83`M z{PvVR*Z=sN9f2#(|7l*Ie&L+B?VG>zq;4O5e7yG6p-RPFHw^DR?mGVB?|)x&`Q{e> z94EKgA$x6kHs4LdE9rK*_mZS4Z^<6ZY*7=a__XDsj(9@E zi}&}+i^VSHO}F(j7T5V`$5!WX;M5MWFv*3oJdiOc1_lRKhO*TkUmRw>*l-|Qli`Ko zyG^_A3x@rlsc^uVp?>$XzK-v`)vQt{5+5?I%JW!P_0{drxjlc=4u`(DIQcuz*O}YP z%Pux7UjM$ZMCD9p{in2fo3CFh`IK+o`{cKIv*iEgW47MGbu)5ni%wNmEMPbVn>=@5 zW)L}~Jz=)dA@jVwJFGbaR{fv;nc>nNrbCb4Z&Ep69M0h2@>{Kn^@hmab5_$#ZrqqZ zx7CZO`M@!4eh%B7_UyI$?|5w%(+iH$)^FZs+&tm8^EPI&t!vgcpG)rSEZ_fCb9V5B z)cQ$YKR(P~&|~oS-1~Lb+b;N}g46i{dj^^G{X*YanWkJdIN;S|9(*_V)!DK0Jsot}AuEKTd? zEIuh*@q1ghaS_9tRgAkFAKdM?Uv7Lvz~RkQLB6RQf)0KA^)KYqES^#ePHh2=^vm4O z@0Kk}e{;+GckFkOy#_y{y&uo|`9k*iziDpy+uU^SpFF?ptY;OoT-E#iTPiA7AAM!Z z2g+!c=Y(I}3w!?o>}duD1`P%UE{PL$k`JwU7*+)xZb;F0@Yh$Yt!Q9A@G|`V#L2bZ z8<_r|yH$5Lu5puA^TZeXJ{|sgJfJwXbjA6`905-rHkRkVXKeU#ZNKg6 zKl8SqWoaz8V2CMoH@Vlj;2~q((p7Dt0z8W*MTL23?4A;At{zvRu~xRo+@;^{-_p>o>)+gED->suIY*DRR?;to;5YjN3&#)oq z=B6g*hF_u#3CT;67#cecF+YC4r$tM&x8X@&{mX}q^R8D^e@ZWTHFdi?o65sIOn#dV z2B^=rosbaMcy7|CE&6YZ)89GYUMswg=e>OY9_MhI-;6i?ltVJF`?_7It^XH$ZU5%E zIdPv^uAOc;e4smc|I+_{^TnlDn93!4Huo5~3H(TmdC2JC>kBDcKd>_F|2x@U`RUF> zqW|=|Q(GSYFYUDAyr9f}p!S^gWc#JTJJ=7LH#wx!Y9|xtbRu!Vwuw`Pglx-B&D(Oi zZFTFfJL_x8nC5;zaa(dZ^L~X7A5KAuc&A1_6EcuX~hvAB6L*GJihx3o$8}J{9 zcj0%ido!EGO7*Gel{oW;cUGSZZdIS&>RkJ7`(CToe?{lDZ(VNs-qVoB`~La~HW68m zUel?SA8X?-U21Iqqjt$HQPKMtR%eybK`n$$AU$(~ma(~%x<=#GoJyVpp9yy~h6g@0$(_7U$I}KF$#kVe$L9-}mL7 zz1z>ee*f$GX7NkPkM}QNzq#-KiCc=txtSOf71r4>zBFcoTF=ICyn1~YbEAVy9OK@N ze?H`iF`e*WI>W*kTk&9Rzui*d^*dsJ6+TIS@%W1K_l5EKdo$TCm~1vYD8us0V12{& z{@)9~?_Z#tS9LE#Am-?-tYhXXTsjENBc|u@1Czdn{Vru@|1Tg-FcR5 zZ(%Q~WV#Hse8Io}ebe7`AAPU*ypn0=>W}^JcABs<1U=ht@^89_tXxHRD}&#TKie2C zb8MEEvv~R7{^k7ojjh)<2S$c@HAhq=1SIQ*PMN9||8yf~{H25cYr^W4HZU-X^2ltv z>#^%g!`qwbf!DP6{qK&be3&f%r$6t{d6i#nrAj~JQvW%9e7J*^CeZH~?}!WV2b+k5aruzZ~>d(3~4$k3C&zi_SD92EcS`o{m$|7_ph z@toV{$35|qqn96VmXJ5R=6whOm^nJ=}FUtmR=LD|Q;+p5RKzpHKCD0Ha$-mji@NvV@Ii|dBI&t#k; z_^;&fd}xe3IByrW=BKpWk019}sW?2r}IAZs&vY<*zI6XI3J%o zJMi9%cgr(Hia2jSs_VUK^7VIQ$%)?c-{)|cxasO}{FxTYF=zk#ZCkEAcoz3{cf`3{ z0`vdBur9fs9o&;X;a(K9nZ>{$#n8dc;xn(U;8@YCy*BI%l#*4~_%WT~;oozP@xi6r zd&3{idV3@9&pSIv%Mz1iyD z_ky;i+{^AZeYt*jzGpVyyUVNVCn%#InY|F0$ z%i=yuKb*_fzvpM=tUrHqK6h>wpDO4FnV4f}5M>ZJ;_>q%*O3^vADYY!v%0$E4lkd< zyuf7A%H#JF6ZC@8FNIr`ziYx%l!3=gge$o0?o%+fMDUH&)w zudmOfrEI13K3E(|*R=b;^t;-nspWP{{=V_j&RWrXUVg>$^^LVf_z0`gvxA=tqdjCIj`gVR1J6*fkcvfA zxBmV4{jz;$w{CA4gTRJOUmu?0NQXMdA(o3l$gE8_{}A)c@BhF5J00ZN@RH4+jq#@T z1G`^R%ngh6cd9%#%j<#LYE>-PKovXh+5VC=5^|M$%B7w3-0^RQTTPP+7@;?cTa`<_e* zJr?D5A~Y<_DSD0FjItz$4VO=ZW<1&O&r;r|#uhsUA;4F__c&vSXb&Q7QH^Ayj9CUY02#@X_f)8s1NZM`7Z@8YXmYO*1waTVLQ z4m%rxWjz6PJD=^+`gJtQIdgYu`{lCNJf}S~AKp27I&-q&|3ybn-@Y0>=R@J4UK^VQ zpG=lyKl82E+aX+4T51XoF%C9{DrM2i3WXHyn5*|||2}x?$=|R?$nU;MZAC*h!@r7) zXBTc)mQHQykNsONb!U6zO%4xsCL@N}+VhXUJjs3^G$FHP)p4)Wrzd+hhurR;bLmsW zVdhVh*-WphtmZ7cn^G#KC-E*@D@o$---Fv<9Gk9QnO}JB6U!F^u+wsrpiQ1R%&0g=h zyEjVNQqNMa_G}w_ZrQcF#~zEB*a+lGCO&%&i7^HS=7z~{!&lciC~OE)|?dh65J*5&ksY_czF1Jq?P7!5c+Z@xV&3CJ@EPY|)3X^}U zUv4>lv_C@XEr&zHB1DzL#!%mR%wy@gXRCTXw6}xK&3on*5iY6FbYQix{bJ{1_ny4` z$dRtoT*X>tRTk8WQqv9=<9~ds%PXdTI1#e|E%-9)8#fz-!yY# zrAX2FjS(9vB>#TtV>|Yp| z{LV{_<=Y9K7mkvI^FH#|uSg^jqp1J1VM)nKyZs(?*U{v_4@#MTpoE z6421pUF`Qv^u?Oj-kqDeA`UY@RaO0RkCpxME$3Oz|J%Op{^nVg_-VV;V%cNb0`c<> zoZmh#pg{f0s#RLxboqdv;h)gY&O5iY1@?AV{z?v4b7puIy3a~Zuj#;PVf&@c=k{xg zyf~7sQ=Mm1_44JGXQ!M+R{!xi-*!jphON+g|2baiF}3Mp-*p*yu9$AxcDOmBdEu?R z^$t7=AH1)*yx6(_o$N33l1H)YFYJ#9cL@IHxa}}=Txtt!9%_M_GRp&FMm;@&51h=# zGuRhwTXiUcDdW_P`pQS@U)t;Mh;$xr&iSU_aD4HiH9cx~N;#))y0&A}?)ZEDrVJ(b z_x*nP|H4;(8}SOM#2A6)Jp5Cp23KEJD9W1kv-iYlj{SRHc|@(;X?G?6#Ub^3=~Efr zr|;V(FR^=GH7}EuyMM2jT8I7$Im>zpFI`v z%i`GU_4|x$D=wZ4o5w53a^&~LI0(%@emL-7jJH%ov*Nwn4V&EN_z0ZR z{&4ol6xzUyU7j4t+78$><53X|7S zCd|X2b+qunOoauell-=`KWO-zx5+rCZ+~s~sfc&qU$1?+XYu#58jMqn|89NQctzHI zj(MM2{mZ z4Z^=4=_rdZD*V+IU0f)t@Ii^2@y+}%3`h3oKPkQ+v$yVXrOA@`$!f>epC}Ko`FHvJ z=NH@6`*=-z4TJj7c%y?*i&Q9S<{)>I(c8inqsxMxf z!_LmwwXR11YOMsroLSAnm0v2EHp}lfVrH;$bhuH&$MAb@b@Gnd&zlc@m6Vg%d8?r6 z&!k|@wHNyjZC=RDzhm~-JJyq5tiOJLZS8572esYz9t*OtR5UBTGVb72PMXVhdi9j$ zRv)kZI{fSMkB#i@zj*KdyCq`v^IbTP`mC0_o)+B?q4fdi{J#BRC28v$9&}sCMCzUUP zH?$AzVO(3TaF27~G@qCW-;=9{BdQvMRHTPCtt~A-O(oyj7bg92H4l%!( zUwb^pb$;F7=C4loUWmTntz`Pc9v9^m+uI!3toTe+W3|Wa)fZ>4bNoJcL-dOK7nb_h zI*8l+>+2WJ`F5Du2sUERaP>{ytpAhq4s_O*seej%*m$!){vkI*ia4cZ(w-vs*;c4`5l2X)`FSy9DeOny|L-t%+vmhzlKR`?V0<%#W}R%!O`!_FL;LE zefW9vXKtp4wsIF68~&M|KBcv?H&kFw^^bWuJ|CtntaGlH_-wlGgzYv`7%fnG95Af?`;ZP{u%^~;0M=Yfls?A}@wnc2i< zdiBx-yRRX!foXs5N`6`{o$+v!*jr7r8Q}`|Y>-m&qpRW54m01CYv}4=R5<-xP4@Ba zb2gL8ZWb{=)N?jgZVh)R`M=-xE4S4fd2_cHl^;2o7Vhy{mdnY|Z*I%~(%yQy{ey!~YKzVG zN5_i&7KMgM%_+Q7#n3&M<;H`hjbb}E{+Rgf z%`4MhgK*>m;>@e=qByr7pBEc4IPiUY`=qwsx!yX%Tq$^2JsZc8{qtnxcKm95{_NtaqN^3wXrxv$mFmPV=-X!fzi!r- zyE>meP0iLj6bM9A96t#S*#fr)(d>V%?1z{SEZ1Z>5y!S`M}G6~Zwp`h*-hAXKO^^G z$7D&CpxY|nnFRPFv#YIhOTSc_q#u0baO_g`{hU26bq)vG!xb1fJ}mQ^KCen7cgM4= zV~$=%948Vb>Ti4O+?Xz!2F1hpj$*jdTj8~ye_4y1JZrzG1UU3&ENbW?5K#uj#&`nbjphlC>=6idz-vS0czl`XP5_=%%eW*pu32o_&bw%9JO856a7iEQyghonIqS^7y}91luv^sZVWV+=O~bpohqAA) z?zsQqz*Tj5FXcAA1uu)AUH&p({KA)=pEKD0m!GpKtCstFW)i>k?p=!*8yn)5I{uxV z`DOjKMYUzWRA2uKpOtsS(^kpKc+Divr1>>JP3KnK>^*kbyYeza*2>rC(?z4#-FwWy zxYZw;su&~~{!Bgd@x}s!39$k*KEFs^$gp7R-exhqNU^ix-ID*Lw&|Kq6+QX-gYBK$=b^1Z2S*Q1ie4ehOxcqeBABW~v_Frb#F0*B?@!Eeo zulQEYlPGPw<};6df`k&Pdg9qr|LotqZueZf8J}8@zHgrEvMPJU)&eOn$f z&R0-a1oO%gh69hE$cuCQXk_fa&cn@cbDB->1QG2De}M}P%CY1RB`TG{G7tIh zzW)EaPD-}?pQ&BF{N41~pPo%!v2>Zf_2rN@wnhho2~3TuP@N&ADchtlrj1yRyB|J-^PDFw)Wf4SXV|QAIeQWds{a*JgPd$sM@UNywwFK z!AwOjzS%o&CvdGUDJ{F$IlX7``ThUw6g0|Lui7?onmNysb*tF6I`-ex-tZ*89$GIn z7<)$SbLC*lOn*1Sv4Q!((!D|qDFQDd|E>;ODy6I`(_YyBul?NK$DH+S_6$dIcN{zQ z<;cZne;YAz{C7q-}*@Q%*7L?CJKLg=^I!cU-Rnf_ZP3K<+gsAQTg9ZXid_B zE%yVm)~3ZZUfQ$3;Lv@*&v5J3EiOKWRYnX73ZR`4kGT2I z)GjGtaQMbG!{}NpbJ^?7`Ilar+Z@R&uaxmqpLG81jf>Wo?(^L|oZ;fR)oJ@l;et0p z%9kG=G^`K^SzTj!ET4~GCp&ZbW4#@H`(~QmJ)A5i$8ze_sgp0Qdv2|o6*fi4E!J(v zn{~ll?^@yl10mu4hw;GuH(^bcd`!jiJC{6QI3Uat#2@$h#p#l7x&6O?-0_(|ckc7l zHy3|jQrw+0JMngic1e-PYt{+2(wnmFFNgXEbsYToL7~Hrakrbj5l6|!mc#XTe&koB z{(17@_>J>RTlyP$QZ=*v_Yz<#x-y4P% zi|a0ZyzZB|?eBZXMgB%4eNHbpA9?%|-{Naq=ec=EIj{emlXC3G*@um1yqK9yCa$~K z|3K8k_FV7(8_{x0#P7{~@p11L^WF>kJRu>W!PSogUOn(^UKw=AKw-n1JaCu?d|?Q% z{FQGT-!?=2+tEn32pxt8uipCaTXa}|-{fUyXFmTg;VvLo@%Gn#_1V%t-z839WJ-y5 z%V1zst(oxl4@+sqg1Nr_dmcRd{bK2NwN|z(FZM8Tu6kwPGA(SAU-Q$@M#lt8ussY6 zA$@X_%RYJju+x0%UXk$bu(&uYgHN(=sosv?lDod!KMs!l_Gtc=6-GJtZ7Nu$_XTBM zKiqIdx5xAHp&P4uE_@7h^ROzozAkT__TaJ=n@T(#mQ6B7q+~=T9J2}gIkti$L{}K>~TwG&#P;Wx^-*u*F8U2 z8uyiM{ql0}^oZ17%fA|2Sog{FA>(W#r{nIDiZA1xdHDC(eH50ee*Je#!pBV=45r6C zyRXE0MO>_x&|RJ4*_`@xi7bao1IQ*2)?jd8X0DWz({N#U(AK^C*ZbN^8{2Q!RLrg{ zS4~#UOAVQmVZWW%ooD&qU5cvHZRj z3JeXqzb>!&ENuJvp2)8E_QEfAM;2?Gq6(79FI%e`Il`Clk;)@{XMeK zs?f;cz~Xksr~7&ej@@A`Id8oFV)FWZvtPz`ah zZH)Vqy|Lv$X&e8-tv8MwE)A1(%3f#F^2_{6|R%l?t;NQm2y`Q82Lm%pDX zu28uweCvl-QKhzY+~r&MY_GKa-QDu{_vJaPU(WVeev#e3Aj0Ehm9*vaoo1Loq4Lheu24qz1#VH zp970>HfG)ziu6tC-QF`{^`1$pxn}&!+v=DzZ}rwgM+6!+pRkfVe_H!O_i2vB)n|Wv z*A@J_&y|U3+27N9_BZ;zm$qtfc9fU8wqKuD_KMG8Ng1BRhpqd1w_aJkukX|hY3mHh z#Oi4FTI0{@zp`%m{QC4;^2^8n`xblt|EtECTGv~4==r|ZZ+ni+QU7q=x!(V|+*W;P zl9(f!@id=>FC?bqqia?i~V42xIh2t{fKW}HjJ0MOwaeGHq@TZtWbEQ*Zea$OPqz}Q~7CU~aVNNqy%mKe0OQ5f}dxN6CjX4*xt= zzrM+SA;hW+3<3u|ud}fzS>F8mZE9*+PwIOveiq)>6>A>MmH2j+pQGiI7VG`(NrwNx zwkQZN%yHg$wtPO58k)G<%EaLfyybHb>8my#IlCh3+%7%^*shnZ#xY9AG~ij zeq1lnV-I#A2Q$O(IpGtQmT4&W2($RaH9F}l_k5Cwu3}x%e%?#!|HTbI!0Hs37*2FK za=sGfkk5CMO#Jq3EyyeP%&i3)K0-oRkm-QE#_qfu5`ItW_AqHEFUdK2=`lCINV??1 z$o994P?s%YYuK>b!AUaFuF>Jmy*Wgj%L^9t7;F-?ImMy;>)#R9 zLdU+1b@zAm{-3u2v;fILk8#1`#fu*>EU20Pxq7~%qvXPRi4%)2xPP1?*mmDM@5lMJ zkK2PHBO%*8KJYSxsqePo;a|{W;Fc^Y@+iPf;;M<$zEAB?H{WDzxS$~LWl6Wup`3To zah1hJdzfapaX{T##Llqy=7I%17q0IMocC6GnV`|*`WNqDZsliC=l~_V7NbKSeuv4V zw)Ca^`FX0IKLRpTJb{(r#`l`}zJ4_g4l3GVQv}mwS(3g#ThJ5m_4!@xdI?CTn_$YY zqk(Cv$xp+ejnbzVfYiDD+WGTi1!%RI7Br4d2r}GQqoe$ZYeCP1uI)VrE5a9SIL$Hn z*#7{SDZvaUlsF>hmvE-GDE6dYk?YU+rYj}jVc+@noYA4Y$NR0sGQ2UrcHhP5ejMWz!9_g=)xWPrpW--gm0}LFik;!X z>y+!=pIQD?s0X-7EZ)e&10K{$=YbK$8zuX zo(;Euyl2aXOly2%WvKFtSm$^{LoVTh7hX%?8X$FNitJLEUF>5G)d42X|y+Fs| z*~0mficp`tkhP!1toN;6N&gghPjzkC(MY$7YmWcfp((A3n;}N1#8_>XT;pWZA0)~KH|5Z0ipMG(gqj|@=KsSlGpfp%i@!*Yf z!k^+gJ#Agc0FVF+eASYp0Bi{b8G=m;9FWmS0TxG)I~W)m6hJqRfr5?&X0ifDlLAP9 zfx%IL1yrI{ax=un#)3}2T3{36=?NK?Xn+|75{Ie>nFcYRctMg}0yY*hsOtdtJ^@cb zyu#oB@e7Ct^E>Dqey|4#3KA1ad&L=Kjx&PndHR^sb|T131_rnz!GQ@8BS(;cA0bHs zBjSh;E>dF+Y%QM*XeTf@s4+}g+OwaV z!L$j~ba51zvY+8W6+;v&Lx#5`s9tv9WKeDTs6W|;L5bl`uQQvYz?A6>22somZ6YV8WnrnZe=m z|Lw)*yM37xMI4U*)Lg}4ZDzx7EjQIRzx7Dd|7D%87tQe5cg9Djz&YUjM7O-9Te<_6 zR{yQMlekxs>p*ohTftP;2X_m8TCCBZ%2cqPL4zTf>p%tHgN+<__+DS#5XaB@pjnrB z$0?={Wjku5u5B-|_&eYJe>j6k!(=9deOx zhwHy`-d=hraNkVU@B9B7?f;%9*2-S{e(di53bqB|3@?KI`ZoNqdhoXHZp!7x&@h=# znN3U#2Jwt1-rw3Dx#E7R>VxNYf4tZKpL&ns#r)65xK=!7x=_VaAbicLW?cva!;IxD zSGW#T?0B!YM)Pq!s{^A2*SjAY3iFwsTrd4`N2!v9;X{aU!ymx|db0gr%p)J<2mPG? z|H$?KA`JD>?*I8L_&*xnxxSEN&E=Ob>Lt|}=Kf`!v6kV8rb8Xy1G^~=Hb2Z2|2+N? z<c^C*GjP{i#AOVXEKpe;?1qGcr{CViHhi{8;E9^E-d@{FkB(jH28Qt66lo5AM%t z+5gf0z#i2F`BNHl7jopNx9n$H!0>02$JgVGm7E&Af4m>&=QBRjXV}OxMUL~xgZ||I zu`~2E7?v@(2RBH(GyE~V^*ncd!)YJJPp`KHuz0cmyYp9@;VjbyQ8mW3qKEBO(-|0^ z@%*dkRef;hm9{tI1C0&4IfWZ9$=0j?VAKm@`E|@?LFw`P;#{pv2Rs|DcRA?(d0clZ zx}oy${!o^(sq4KD-oG8B7&@D2iPsK&Db<5@On-KJI&eLlFIE1RtKq<`5T>lljX$>9 zvhKK}qG0;&P4q(jKdo1n*uFd$bNR@7#u@g1vL`o2J>af){OVqPnX{(h|001o+T0xL zBEOxKV~TO-6!^u}@RzlMO|id$i9v*c!GVzhL6@q9%kVvyJZHK8 zbN#&HcN}J`$;agDXZmWzuQ2Fj=nFW1oDt=x|>)G2~&6y&f4A6(u1d@j_BY zM&@k89g_x@4!6aXj5?>4=K3vuZS}BV$DYlbKbsylxUz8J!tZmPJZ8^$m9j`6sgzMO zFm!5!&aqFQHeI`Z{rc5Qmx?6XGq;pFa5PRS%bBg{b9u>%6&%}d`x;0r*|qD|)ytQQ zoj4MC`T3uhD*e~vQxI`I`1qqhr;C77Kw#j*SFc{Z%FE5Qb&*rgVQdlTn>EGg_MSNX zl_6Tv-+%x0a(6$zy3rxwbjgjS49hhet|o~%1;oVs5qH-)y=c`cuWj46mq$-a?d|Eg zu}oS0K-9#wEB~LV{B`{N^92lROpZ8RC=_}mD8OW5u0G#Zg}L#->60l%sU;;}9`_1Z z_V2v+>fcB1Fa7ZhEw#c-EiB*tH10EpZVf18ukh-0TkPZSUo9mib!&sCYaDyk@qJHw zJG*C|-8LcMKqyZf?-`RN!3ioWSFcu<>P?!JW*P8`BY~GG;mKsqu9&4;y7zoK7#=f^KQJ<~^Zaw=sa}j4eGJ#M zq!ntCUnoV#&!{zs9OZ?wKI)S7hk2-nIjt*t4B6Ax|B znZPw`Szu65)A7fQvu&R3XU(2_;>h&UjuwMcF&aTi0Y8e;rXAS3)n>!`HvMJmihkWO z*{^$yv2j`E&F8oL`0g=I?DTAzlA>K7=^mxt`MNB{k3H ztK|MWKV(Bg14US;pWb(;XkW`ss|ed)^VfBRTIcOfWq0y&I;c>T*A`ztXHoqd>9~q= zq3zEW9gF93T9hfZYSpt6h4;aG^kztMpYp2RVrD9Iv9#%-!S;6_9vKT~9F+VPE~eO` zAQazt#&KJb!h6#g`*ruD58n*lyTo^LcJb#gymco&d3C)@?+e(SZf?C-ZqY~8dy>EB z%wMMe@cVlwubH*;WSMVVb+|oydGW@gHQ#e@{|M%nKe2A|>)Vfhh>5Hc?>G5;>Eg3D zI}#dt`9u~y?l-?&_GP1QwiR21y`tIAxPuI>Cq6#^@qU#8JJ(O=PyLL4mh70R=4@|k%9!{0?4KLOlr+~)jn2-I2^amB z_APb&Ni=JNOSjoi5l_kR}z@fQp%9KSN zUL7a=WCT0^9VnU{Fl&hyXTLzOc#o^lcE6zfw-(ve*W+%#j(yMb{LW5e>#~^Y%kT28 zosD~$@$Sq$$@4$omG3KlcXw;k(%D|mRJ7GsPxu?MT_bYB8>z?^pUKlEP3v&(+gttp zU&8ZqbI&h%#99)&-RH8&nX_ko`+Ix8&b6)nc6HjcY2wS5E&F5R6Lo3!t~sAm;xaX} z%NS1VSsk|a)|DGKcI;lg`gMu3v-8?H8c)L*4%A;+tI;K$nZ@IxBX;=B8=V-v>ut>J ze0#PPK0da!$Y@Flb3$KW&`T4>JsJWcd=iq9EA6Vjytx1R)vIeum{>$5FG;YNg@!&X zuy{~n@&4Vo& z?2U&T9W(Ot{|6|f_;Bc4X>QfL$a0+J;OnnytSvoB4Ur5txs`=GK7K6x@wYBfSXlUx zG220&_P)Np;;&!7UNz@#`keN1<~oB7+)DD+*47U{{}kYAb!j~@ZSv&iNt1*aSqv9^ zIWbZB`K(#9?5$Xe#k+Uw?(MJt z|9;l&+44(|`*;c1nJ`rCnmaw4L6AwO>gUg&i_Wh;(t2(-LqUX#lHj&uvAfH1b8~ZR zUGtW`&{ktv)5}pbP4@9kbLZyMNoAAfZslIa*>EQyk>N_WxPINtwmDOx1fm0#GR|o= zC!{kQH_AReFLqjvhoPNOzWLySZoTRg%P#XB`1knv$3q|Ul>0$=N**?Etb6zbV4e-HUsCvw!ON>lm7K^nk;i`e>j0x5E?OZZ9XwBVQUCJ4? zZ-?afYaxo)-?q5*^|T(6;PZP}-R}3e%RXb{ai;u<<(t-PTt3$D@nT?=w$W2Ak$lcO zOG7yhx1N=c?|nP_+I@q6D|Nr_sIl!>|7VqO_}|G9;h`Pd@@-o-{gH{0l5(G)AJP+D z?{4_1_>RW8&j|@Gll`Q7Ji~(gxR^_>YW`UL!esH~yz{vHk!jV}9^8C8`{S4V|1G*(-o{71a7{0_-*>l6=wgJ=tD}cZq|Qs6 zY6^X6k}zHzVoFUL>!1;>=8%x~P@e*B~Jr5=UWL(8wP zcdKO7&D@*!an8#VGHHtpkCgmuRS2)R(*IHLSfKE8x75u~tW_s7o?hk{dTNp$QwY~Y zHvXyWcFKt-84F*yugdo1iR0<@2OebT)jST4hz@JGdi3WDxgBn|7Czp1wp`gn`e?WM zE<>k{{gs&_=F2XY7$sy@S@bOOKDUpd<(J%Y<~Z~B@1(9zJ;5XM?{o*xp4Phir7g+D z1-oS|u2!?YO|NQgzJKRd!~ZRIt=r=(r0i?oFg|&@qr>tDL)^bl6K^hf`6%jHjs60! z2;-xtJp>9u?2sq9ilV>3?-H2YIAFQMW$MzFze} zn)_m{UQP3nPwVaLn6giCwH|KEbj!$yV(U~8R|#TqO=VUVx#K5p>2&|y1t+#3jhmGM z3US-n^V+*+p5(M}V|y`4_&;~ZquW66&&8(4rqSWCG>+?af@s*F*T(({ zW)KT`mOl)HcgRre7`roJLi?Nw|w!1o{bkw&Xs(Bv~;?E!iy>g zn{eJAnXE6`!oqqFZCb@6|gnh9&F5G?s|N~W9I)|PYyq}uj0Js z-^kMZ=&@*fR%iBA{=V?N>3RI6o-kZ|v7$ z7V>S56gGRuz!n?ySGB7YOc)I$ztaD0e0stvTa{B-P literal 0 HcmV?d00001 diff --git a/dist/branding/qtdesignstudio/qtcreator.xcassets/qtcreator-project.iconset/icon_512x512.png b/dist/branding/qtdesignstudio/qtcreator.xcassets/qtcreator-project.iconset/icon_512x512.png new file mode 100644 index 0000000000000000000000000000000000000000..41808164d199a543fb9fa4d57f04e8aaf5627355 GIT binary patch literal 30033 zcmeAS@N?(olHy`uVBq!ia0y~yU}6Aa4mJh`hA$OYelakVN_e_BhE&{oGnaji^wFEg z;$z>J+m$EJPTRF|nKM(1zzKH^zPT5ic^9^bJ-WcOti0fbs>6%D+b^u%c<|V=)(eJt zJFdU}efGtceHSy*%C>o0y{xR`SUq>k?n&SNo{tu0Il7XCk1?dBsDML(gKd>WpKzZd z%TaC~1}+XJh9_TcsOZQyD`?m-STN3Gb`)Sx*pxi!!w+!>2FHtQzWK7L4SFqe0ldp8XjQyl*Tu`MSQ^dLaW7N7EYx2a`*& zm;UyBk-1!Y^)(}#0tdr`*AXjv>XlO8oW8MCsZ`VdU;`&ZL5`xw-b2%S%fesXKX;|{ z<0Va-cOKVexg{7l90V8+RDE4<^zY7%@QvQKyG@p?n?BLfgu!t_F(z=%d|8o8R z-qwtLAJV|&nUL^Ff`LVWqs?pQuW-r#ldg2_?~DEAJKwI>h)+DkWZJF5N#eJk-&(PH z_3Fv7x2&zLZ%0K(PcJVi(MkNT7qzS8<)PHm)Bf(M`1mOD@-pAqAFqbT`|f|aZ1%p? z`b~3Uqi!x$Wo6Fg(s*H1!Op-Wz{0SBsX^%EkKGIxj_5T@GVEzS%qrSFV`1NwAFmsn z_RRl3+e4+J(ed^qRqr&Nhz$>p-`t#DZ=Zf{j=X%`kB1-E@BjD9pGjoL^fFGil7~zU z3@nTcMtzQ*Nl)La{SDpZEzieb|M$z~*A^)PVgC;@I@ImU`4hVS7(@wv)o+tk8S$`77#luV4> zq`<<+(8a+JbNGFnR*+o7K23%SnRcE-ujbd-A6H{{NWW8hd&RB~6BybXmMv3@^W5-f z^Le}KR0(&5T)`~SZ>q1p& zgCRgf==HzTA`Af&nHuV@++}~DC}Xr@Lht_G#_gv6teDqF-CVutU#Mr(riJ3~iyIHr zuGzC^&aW>oCx2l(GuOKO-GBDg{na)BW#3OPP^e z`R!J=`i6G#fbx?IVpJ8*%Q3JxFf=GJGn9XgpUBjZ!qLEZV6R0=z($4!U6CJ=e}9|) z`&7GWV!CQW)MNc`d=D5d?60rqW8}*={4>4yyzTd15f+`rU*<73fPA}zAz&RN!*%8l z`7%Z;W-vJHv*%#=QTX@uoqwOQ;!Qqm`dA+CUZeM&<;&~q{cL2T=WDm$+x2IHGhgL|szZ#qU}x-M2+*r! z$Xcn+!obkLz#z7MVbtB#^OBhvR;f5#e5)^A^WE<1=O0TKE^G{oExr1rR)#5}^1)Pg z`I--Z!TMu<&o^LjNabS6V&y-u|HIbCEYNIVqEE?Xkd-tZG-6axbTg9Af@07HWG`|n!6g>40j*6u&` zV|5XWOThziyTF1I6BJJtFi0EvaEt4e*!_6WygI^yL4bh~q>+K+m^~lEh1lnAk*QaF zKi1zZP6>GMwCPiFO~VDL>@^c}M2@7f2UJ&Ee`kp~e2DQo6O#g18v`SQMg-FW-=O&~ zE6q06i?W;MMqcKF|-RfFtC6%C@?hWGO+aiziq;5boBet z>*Bq;IT`N!zPVw+ll#3AhD~j}(r%m6&MsQ&JzXt+Qm+Ubivw660~3P_BSW1Q^OGC< zvp&sVsX2j*<&HrE2lJnOzkXFk)&KoES(f=`{++Xe9#u>cehdoW>Xm^(fI(pmyF=W@ z>R#qBMuzDbXO8aw%*3$K=K?FkKmG3vYxaJ-Wm5m|&lAl(V_0H1%tyEhK_j*+nF5p z^`Ga6$lv?*#q;^~e&1LFDkSE3PF4fuBL)VBCI*H?h65$BZA`(B=2r;z?$Qzr@L`%F z%v`B@;Mwf_c{#o|#m{`6PI27F2}!G<>V=)dbjtuZD$$hi_kGyI~J6)O`yUF*d9?&A6D0d4*i~)PFm*Ys48!6dCH~`g1-Jw^QA) zbm`F>IUPY)8QW#cBp{Khu)<<5L!BD)tFS+Di3}GXthQpgwdKEPF+)ga;~rxLea==qP7jUp7xjPv0FW9qZ&bFIr zv!85nh~r&Q_<#9cc9=yk7!~yN8?rbioZ?RS#IWMgKO=?@zbow->{KUwXUWLTHGQUd zLbVCzm8Rwf1=YsDD#kx;j3LeqJD%7ud^Z&52#d;8y_GWAcha%?i;G+{4>y-Ix5M1^ zfhpk2+Q!uB`(NDK&dJ=t79aDoyWqjS_~78lJj@}Xp%?G$EWZ3NEcMUMm>mVbSwM9? zC>eC!<6h9rzJD&`)0K^j5~tZRFEeP+6Abvmle6t++LlvVt5sGrKACpNiof9nC#YIs zU|?Vooh$C}b&rM=^Gu=tA(FpV2{drB9Xb7K_4;`-!h2FqPD)8uD4)9iG&@ZFL2iMG z;s;n*H*LIL$q?e)kkY)~i{;Jz{q;p=4C3t!8}k{yi*zt39qMFJId@#X9-eySI5tFn zJpPh(0{g#?r}bLR`uCs3GH$B>QO*zcCsNG?*$_t;FMy${*dgb?ChC;`NH>?wE`8K_dXJ@uz z5GZC!VJOJ|{KNh|dq8aL+%HTg6xl^tPINXdN>^fKho%JuhXxt93#RNs;osLCG2Fjw zW#cMIhNNHx!+-irO!YPGvSkbQ)&9Qp=xF!UVD?-^2R?RK)N(N~<$Pw$*~75xnS++b zg|$0MnHx*u7_PRqGr09gOw8e2)BSbzGCpX{(8Rzf7snvXGDC=|w3fj~j^W~i)5Q!S zQyH_@ZVfXM+^}@1>P!xe{mXMc_zEbrtHA=1m65^GjqUXVM^UZ~o@^$J4L434zrq$# zQ}g4)!;=RcVv? zJ`L$AS(Y8V?LGnufA$)ez6t?_{Rwl18J5Lr3=B;=iVCk*F-#Ueu#CyUR5HNqS5eV| z7pyDRu3frz`@LCGQc__I4S5C=IGLVIJk)jVAT$UU9QwvIL%|{R-}28)FOvSNF$7Hg z|MC>$-Fg3S=kK@Wl`v44$-W@!D|;h@?+qqse1*se2(*hI&<>8j`a7PLWnFl}Y2EEU zeQeJd78L(whE%3a42)e@Yh>aWHkvn_KKyz8nQRs{e3MWxi>{~!KoX&Z!tDHcENw_lP1TmWzBfKZU_zBFD4ZA=90c|- zSm`(T8ZCIiy5S_VfHor=pUjLm91+36ldq{+3mOd{g**vcDZ8qz%CGRga_NGi_!35Kxohj)p7wB3#d=Rof=kZ9XQRsAiDB&z=9~x*HVm~ZZ}vM z7Q6K>GQA#C+}ilte_?X%THCdEH5_wEA4-&#hCMnA*3xloNL7=rX36hNk=+T%pLCW zc(AG#IyAib#|{lGQ5J?73I~pHFF4Kofr-KLrGKcb!Z*eT|JUt$waSxW#flXd@`9%b zC`{vfbxanP0;@zBDjXdq7&J_u_+=Ky3Zs9Iz9t@SyLe1G-{R7WeEGk>zpJbC zi+C9)m9s!I`;}@22a_3Q3<}Z#KP?@d7?SiE;{I(vDi*Dx&U7oMdXB(>rL&b*ROQ0l za?jBrfFWS#pU`7hmWelnOElQWMMio?=j}|DVu1N@feu5%kBTV_3eF8&JU@6E)^59{ zb(8zlB1Bx4AX6r7tk$JHrB_ zUs{V-R^3@Gbby=T$jXLYCmY^9j*($#iVja@86iCZ298%>84_NxCs+$bJY}5V*bvURX3wWnjo(be z1roCQc|e(%fq_9FM1bLel;>pj1OB27H4keUau^k&1s?qCoxOZPGsF6A2f=Q1U}V^w zex8lNAwZ1LVUNFApVs4*S^t!uv!8IgU+W|DPkH^XRD%a{4G#~ux8HicLeatGS)yV? zD0@upi-qm?*kN`}F=n_R=_brDgJ;hCMu$%-4&TkbGyGs}sA**QXty(W!|(4aUok*C zBn=DRFf_b0EG*6EX0TE)n96wS9z$Ws1_>q(CWfCpjGqjd;r=XTP>^<=`fy70t5q@o zHl-if+t1I;#l_&L;K0t5sOVsBe6ZH{)j3XR7m0!6R6aw%&dsHk3|jdOnk))UjuQM1 zAp#2T`C-k!aA$`Yh6P-VD~>UBcQzEYJJi)zS65$Rez2TNL0mvVsz#J$M?-QG!%8a# zaCz$>Fr}7Zg7x2Xi(6wW=N1`GC0e~!Nl+`c^&(LB}-b&Zl+8=!KjeO@j;po z;dN<-3B2=P8qO7*a{7NrXG0NR!wv2QTeeIwyI1iz0@R;lWcVb)Q1|a=Is*gCQ$>aj zIi}$4)}jn2?lIgoaS&(PQNfXLVuE6%wb`!Qh0YD(<*tzUatv7iiD5+=V~4{6E!Gl! z8>(`(ng$VxE97%Z~5NOdyYOG*#&`bZE!_jQ<%F80h$V{eC-M zIU9@D$33>+U-dfBq2b*RMwmidh7<3K=KhXnYjA94d-}w}m6vf<;GsIj1G<7N;4~n> za+HZ-QQ~JN2E%Ll#ylRZ3|jgQm6es7_#KLii_i6iMsQ5%@Bghoorf`QGV{{cCj$?$ z%hwpd!cL08ppr3%!6EMDZ$^fXb@P}Fik^5#S(Rkmld-?N@dG#GqHqUx#!rS*^I&e@ z#LVzWDkWVUvoQRVF#oql++kgxWWL)H>#(?of_F7iWLZuuon*BE z5&;4%Pgxj3*gRPoHa!eiWqMV_5VE-OsG8%W2MQY}Ib1C`QG5Q?t5;jPmjt|0OOchBA`O=WQtiZv=%1|Vo z5GvOY&k&%xb7jxI9F?a*j)Gh+&RX9^0;bA$wFU3fd%ykE)%UAbuIyyvk#P9?`}^`+ zw{E3OQu2>@vXe_jA}cG)s89F1%7^YZvI5_m5BIJ@q%n1d1x6iK&MMM6q3jOP3<0qz zhxMx&o?M@3{3><9#72kG9G~2Yq2I&9!qnU)_@&KsI9dN3+RH8@b7w*mH_IO-IdMM5 z8vCf|=*u=zZCwqb=OHDP081+q!$pHvJ&X6O3Y)s2u#+v4jVV-U!3^mIQi3|tHRVs& zd}27E$UZeN{O+|%R-Xk+UeD67FTWZZzEn`zO{H48K)*>k?t#JqXZ}A*haIA$qlGw1 z6u^xX1_h2&*$e@yjHiql?i@cVc+hJ4ehvmL{)Sh!q1yHK_ZaRL23KVk8RYziw*aX13~YF!1`}#m2}FZoq(g`ajy3WB2aP_>!s+V7hVQ z-H)qWY-1%HOx|c+-_ONUa_qkLw*1Y%wsSJC=$kJ6|NF1#yf2lX7l(#Ls!F=8Sij!? z-f>}}*!K<6N*S4%7pL)lnl5W!_vfj*T;-GIH@}$Xg9{V}289(j7#*hbCm2h(?^!3! zyTeVkH>brw=a0$5S2c+bkfracdQT0 zRxXq%C}xjYf8f*8)5do4a(orP-)`5p`F0~&-oKLR$KtRty(Z zS-RZf)<|8bWccA~^6OFQMP`LI;gZ$$ldm7nKDyq@ZJV6v`tvU8a(WLQPYjQ*{hBgU ziC=U_XUiXp9dV3?`VGzpve)k|d&j9h=Y!8F;e@6JJuiM{Xt-&GF_~f)|1rsJH`sl4IGUAwqLKzOg`3A8To-_3#Y&ZO^N?{Cx1Y*Clf=;x|-6rS)t$# zROq95*;nqeSOjw}kYSq;AN=&}1{;^)^@B1Eb7AP)IPW{0M9?N49@9 zPP}2I$5pj-Zy8qz72i?Q(kqldE|up zkl$~&-B+;B6{&BZP0oY1V+POxHpgSM;p8?sYf& z==-VJ3yr^+&F@_K%fWQD%TG3n;f((0t_~ zgD3~Mu3-^XX4ui@F8W^XOz&)UOKlE8C;mJx@RA*Z=g6%3L~i5^w7d!>DaJH%q1&Z_uy46;l@J zbN3;`O<{#+ECSH#p@JbyB0rQn;c|dNXwrfhCXH<@({8h_Rybgi7Vg7xW!@4V)$&jO zP8~mfZNcxKH9nkOS^^i&a!mODG?oq2)=jyoaDTdMw^;2F8;eH(D4&dK;gxIK;{`+*YxTl{j$a+jTJppZU!v ztshKf$Y|29-2GD``5o_rV6LCF_iMl3Ro?&eY`&hiYE&fFEC;M{PBEkZ}%r1$gG-gT#hAFwjTJ*}_%&2pne-o$p^jW`~A>(vp`{$eK^KE}Wne1OT%}AQHR6vUvT!Aw&ykKxhuL#**n#k~R*PCPI z`K$LZJUSI`&h3!8CU-S+PTP+g5>@)!_r2U(nmWVEzh%$)P3_0_)~{Z>ZPC_iQQ9{( zI(YeR{XT1c|Bw9AaJC)u8vLAsz+vvlz%ch>peoa@^Iwbpu6ASHeUCBputMmi`4!Pm z|6S+(C%AsL6!XkBF4vDUSJ<#G`^UUX?Ch%AYmNt21TLPG+j{f&wza$8?b@Guyp(OJ zI_oSc)`Q$27dr&xw>E5;3K{^^<985ex-gaVRBm%=Fmvel)wdXCZ~BoN_Dikf!zYa$ zi)%`c-(3B7{mIXZcEx|)x#GaVz2|Ooer9p}w&%SBi_8hRSoG7ied1uq;hFEt;V^aXdsfDE;tuOXCh+dd`Qf{nq2%{E?bpVYPB}I@%j6ig z-IJWXtG=>yJ$K`ZcaMFWD?Tq+8NB@7j_>!X@4Mao$#B|3mMfr@85|rkdbv!5KV z@H?;}@$icki~DY!Hj-yp2nqy-rY>0q7iMLq20nq1-l``T53Z79Qd_)!wb=)$r`rAR zO=Y=1{&W=PES<+VJCbGD6MNByl?rE{9Q}E9eVuFVuP>2@CNY;Ue5dBvtzj$jz|TH~ z_55WK#~!OEFMF21+cp1jTJjCwng5FTnqGbNiQ~PzDqdVlV6S=1>b2XXdSxsxJzUse zey`$jy{3MHjXsjcec~7{zetGIZxCmxsFeLvWs<|-z{B41mL+5Juj{hu>kH2=%VG>I zTCn=8`TaRr+1dN=798gN?_e9s_;!k&zC)-e$bF!?c{_8%xfeGW8cr3QzQ=T#>%JIM zN{4}@gY@F{)ptz4)P?^wYm1HhdAmaON%GoV{&6o?Phu)v9~vL;ADy>zDQLoNo?+UV z8PiuagH5N5$HIt_2r-zZJ(HPy3qx%jd}Pl0@-f&m~$q2X?c3dwo6sn%V7~&F3E-?M@H& z&u7@F#SjNFo1uy8EO&yJ#ph2G}of>hh%sT?n^?vCGje%C_BI`@B7 zwt3ZSeCro8G3oO&iRULA@nW{VSNr|$MjiPFoUP&qM7Y3R6OLDB86Kn-9N90Le4=hI zgPQpMw`&+Miyeb3gH->?0ioNf^4 z@7ge%`+_24Lv;YGT9sm8`N*^)inaPH#{}M;=Z>z|E;p8R-QRYP#qN`ck&;I(npIrUx_s{6rSNCU1Ok8IYz@Gf5_^uq+ zuD21@Z@1oF?mPS2hk4cSDtUKj%YAKO+;fYCnPJ0!1_q8M@rD}bhI7daKJm?%bid?w zdiIRJ(|-T7TxWKED=+H{FS)1BD@=QeHP9`EW>%ytTQew=##Ji^YLcwx0}zC%C0kct!Fdfe_!qUPbG#Kafh!&UU;IqVXpsz*9jTLHX*k|V`J}zMnzeb zg4PN&?uusq?BWnB0dgh>8^h;kXPFrs0`$K!y-+xiZPA;{w*S@7IPLn&UAMPwIsd&= zf8vL~k`=1btA2@9H1@wV4!Zic`*`>r_G=<15>GZ5yY)(y3he%Ayj#b&lHnG|RgkF+ zEC;z2;=gmT-f(L85#?xod*(OYQ#|!^Kblvv{ORE|lPr-I2(A@d7Rj=XjYncb!Qx)C zTh^=$-YWbJ9N>h7x7z5yHS5I5PnI(=g`R87m0WOxUxAgO#^8byFGGod5IA3PXvOF| z$Ot^R_kQY&bY>y5IIi2ZQ#Sv8e1h`>v+6cE-q`_-;pbW^>gU_l%3KqxFz01Z5CkR7 zh6PDqm|pZuoL~3#)8Dw**Df!GnD_E5o&Is^-7k(dOj;ENcb9venS9^7e1Dc-1Fyo2 zdGGgyJ3BM8H{7*lSO9J!FnkG${r;!DYg~`g};th zSBvC7*t(YUa5LxO+3H`|&+XXzEO|k_@%!Wnl^en&6b>KLkT1T|K6l=2&VT)r_sM4- zQ{DKe?0dMok%Qm)ZuR)Y$Y zRhXI@+B?6RtmBjwuE<$0sD0vq)J=9qdxHrZ{_JT{UpDv4-v))D`A9USVL2+Y}{GS5{7B7Dz+1-9D?W)`o z?nT$Dr%#=FQEhh4rsXUQd8!SX;815^c(VKB{&%gNRhuU{3w%hAVXk0(#K5S{@3w5> z$!*~WYQvvu-G1?NV(-q$W!8>O>IxT`88g?vJ|TAZ3x zZ+Ua~{P(*g{e-{#Sh?7icek&`fwZT2t3)oex;**5jj`F+{O%Oy25F{@zzT*CS#WZ( zW|(1LZ^xi;de^g`opmlRvG( z<8?cE_>V9%g|Yd5yw&&SSa@#styT9tKXa_9kkz!AkhwwguJD8tUkuhT@CdRA@o5SM zU0vh;TG3<0`|Y1DEU8cMcjaK%c8?);PyE$q5l>4C;+J}LRuFyzBbZ(6j&R8Q}|+ zqAvW7$G%xyDlUzF>0z*-s$aeX$CrB_9P;&)m&}b}izq0@w%xwA1o6f}x zvK5pZclaK=orhmzyPa&*Jp1DLFZ`C+xryE0#w{ie-*Tq3jD+~0+fX;QcYXJFstcbNe>%jBwLj~W)}EC~PGTb+4kU*`SPxevc4 zUpQ=|SItyXpa06)An6EXfcL`hp8qW+hbMjbxKwx32ZmD|wbKMwz4~doe(r^}*OD0- zFPGo_*b=u_H1)j3#oVOx6RUr||6L^&vY*XHN1)^8-#F&J5QcrXvQ~qOriOdze@xp{ z*#r#s>z(3oZf?k&D$Bsabg*I1+CJxRM;2r;seIuvP}9qp_UTk58|Uds`vvZQZK>NI z|MPdQMfrUzW>^uw%3{TGeu(zJLuBxu-_;kaEl|^5o zG#B<<;OB@?@XXg$Hj&!Uvh-rviFY2B$7Ij32!EV9=gYr;b&d)XPkgCU=}rChdT-IS ze-kCX7$hu+`g?ody6iu$91?al7T`?$LiAXC%+Km?mHZDx9V8dl3tWh}*`%rb#U!k2 zu2Exya{lUH9w}dh18k#b*$PO1SteKW_6@U=>4RlY_A10N-M#~{+6?4O28Jh_ z*G=lFKa{pqgpH*{TeV*}W!oMm4dpXMl8Tno+)W-bGWFdx+s@U37d#N>N#T{X5&;|ZWcA1WlVXkh zcO^O~DDW)kxnR$cu_kA|o^lTZi_u;tr~CW1WVR$aC{(@Rl)U)BkDGu4=-(TOFF1Tu+b>Gj78GVh(U!4u=uHU!% zeqfEno1A@Sph+HW2Azlv3=9vP!)-n@#{N9x^1C?H=V4=l zr&6AxP`4E~tEaBETb9}KDdN$A2TI?h3tEh){+N|{vF%8gb379`$+6&bi0;% z;mh{Ul4f!DrOwY7lztl>DraJH2wDBZ!Xe?ofktouDa7~e|2E|vGfU2^HJ@2o&a9su z|B&%_XxX9flFId`ICLEk@bg5dE6rLMe)sOVZ`JQI`c@~`p1E)Pf58df>In}Wb}e*r zgye6AClS-t@BS0k*;mmyVdK3Yr#b#IxAlgD9JX0l?nLUmh7I3$scdS9Yuupl;D5+l z4IAFRXToBSIbO;w{eJHIdqxi9pZe1E#t8?&4r7>b{+Rt2PNVgwI1V>B{F^FxjEhN1 zd-h$ULw6b+Zr%UID4tzEt^WP>-{LYN**1337ZmLH!5W{O`O*L0?+5o+S09&Y>H^uot| z48BbW*#EbtrQzALG>}0I6SyDCfA>21<;cB2H;Kb0PVw{F{%9)mTu!~fE`N-VDKWv} zf5+^n^RwPP=B}!iVv@9XEK5Gr`j64!-P!wBuR^NJu8Tk7kH!3#EWH*uS4Mc6eEhjn z9IVZ9*YA`ym!31@h^S;`YGE&8V+xbw`N{qA$miJaHD?(rnZzzwKV0OjWUtRMlLJyF zS1tZ{-r`}zyeCDnavY3I6DwIC&2h{4`S{#&qeD9mF;AQ>xXkVei>2K*(?#dCJ?1qq zvZx(nVAwWcLAHd57^pqWz~Ioc%Ov%Wu-rAR+npiH)W7u^9g;zR}|^MCKvd-uQPyp$88>wW9fo}LS*pDpOQz|_gL ztH*%nOHjx8)k~HiEj+}dCLhVvpqVy5AExt)>X{$$->y`=pE_y&3!IE;xvbHmieDR9=V1XXQ$v`VXzxJPW_1g^`zo=(GCf}2^z{Sl8JRsIB(RR|9br3 zr_WsyRlW)an#UCzEE2%UkijMMkFuR38!!KpP)Wt7j?*2d2>t@Mskc9De6dW>@6=V>2N$B*QGJAstqeB4-56(AF+^-<8J6u8kG{-be*?%-&?R=ZK zmq|nU3oEmRG7lq@SiNBQ^n*IhVDlIl7*Zta?X{QYw6hn5yRE2GI>phwqvt|@xPt#D zmLv8M-!L#({l8fL;#9i&joQdZvu?j*XZvK}uVA#E;S|T@RV%fYUu847_@VBr$%2J# zzph;k*#Apw)#_CXx;_Q1($HPH>5`CH@41EF&go^neX-!$H@~dR_X|?j%9_+?a_dGM z)ANqLxwQ4!#`<)<2&e98+gU$wiKIzBJPviA!J`Al8HJ0cS*?;@x%l+)>BrLNIoQNC z$~@`gKhtY+m~;?o%!ZhN!i}bn@@99{cUWx`#q-q#A1H_ni@^8ogY{ky2W(0EEqEG z8BXPm3G(vGjbHV`x@DSmCqw*#J8kUyc6`!UwN~eJck-##Gj3V^?AkIp>b6yeW%SIl z^?tWApB+j)^FwD@=4Y|XUn;#{xR?Db`{tDO`o$NP#uIy(3a1H1?YQ$=>vZOF-ms-} z%_Catc76+3wNB@J_xZKmwFNR9`(|!!*f(=;!@jiX`9Dj`X6{a@bh#k?WV@*C7r&iv zJ~UY`G&eB;yOg1xL17)soF|HfTCZ2^d9RSAy*_=v(DCQun&t{09^3aXo@HaGQg`)K z&;RF_pIvaje|Fi)H=iDEH=fBDbz}Ew4pFy)wFl3Ze9*tP_~Puf7hl`0Zap0(xk7ir zdI_F_gx|CNTEF}qdGdF2?e)zpOtZviR^1eYSj$i_Ba3(TC$0^L1Mcf{GM~0?u35M# z-0j9}MhDfrgn2(-WLKqKy8h*5e=*ZqgU^e4Cfwcl>wub7@pj!CtLmpV{dzm+$NKOW zOIO}+X}R}7A%Qjj(qyQ?4YTTNXFODYu*M?9&zG5-pJ&;$Nm=LPd#-!x@vT3wg^8IV z@c!RD6W&$6zWgNB(|fln2TMyM$7hzT2#>Tga?zI*o7i%U?WxpO(2c zN8-iT+DG3wAuVhMh64-_KFB4%=VW>l70Ie9#U7A(m?7)!srerq6qy^e-{tLl{B>o? z>(%n=#&=5DjpPq8&t7LXVVzxt`?Qny12)V*zW$hc?+8c85%p^8pQO12a7pDpO?OynS@qnnm+MXF(bN2n+y7J}n^){Ny zHZ4bgp1)mvyD8>tw^ZdX*)=>HFEe+15@xZ3Wjs}ej$`+ZX$J)QE91A@xu2#4(g?@ z6%Z%>U}-qoEiNv@@a5*mf~A4{OiWV}&PKkPy>e1D>l~xq`Ux(vY|HO|SiXpvVcpX; zXFi-^zqf$>{@+6zs`_teFJNrETz-F!-<`^kK$-9Q>1US5>Q^5*r@d&u(5fDTjOmW+ z{J8}fUcG(|c6)<9!vi^n;|W*RIy(HZo@Npe>YUokymIq0{%nIMNBEeIaxtvatAED4 z>(fk5F*j=`0Rg)`_8VOGd-LB}m(lm#`u|+F_Pam!EZwfk_kx$5X@1;CcZeStEbb)y z=H1V`+J>>N<@Acx3-h;!FF2jAUtc{vb0agu^}YG^Ug>wfTa}jGlz98XAz^VS+Xdm} zf9IX@^6GjPVQ==(``7Ee>$7!B&p9}JX5xezdV-}v`N}anq4f?`ZL@-7-BW$n91L*X z`!QTxWpUN9UIvDB)^YQllI1EczuVy&;1&@X=B3|zbGjSPtKg91KbQUYS*AVXx@+|m zWeF#dUFB^DwV~!RFfnBCls{28&|k#5S|NeAZ5Df=ce;tTDuYA+zpDGvCD&&MpUj)< zzwi)q`kQ^MbGMvcvB>(^bNg?4cmF<9Z+kp#L(7^67o^o}K1f1MT*26&vxiBrg?$ol zo0KF6&$4TqvL4T`ObuaXsJe16iGjVRde*%^f870ph1ghrZT75vKet{WH2g^#8}s|S z=hHPprl$Y@`@iP-=`V+#zA4cGfY?(ne*bpw&(Y}yQ89Ved-r{IAH(nW?SE{t8E+K9##s=GXHox^2>h7 z&k_9pety|M^YhE+t*0|3<<}gsQu@T`9izJDK0~{}zn|Ba{d>Fb+`qM2i|tH9uf)Xr z{(ZG-^8asZlecH@UT=OqEXXx-<@?;*&u^)}T%4aK8GiF(Q^T9}9~# z>%SL|zlt@wQ`o{@(Y)}MUCuv-Z0k1{6Fh7j&+Ym6BY@$9^6MAh=4;;nDaD-s%QDo^ zd5Vcc&g@xP+8L?W1lPyc`i9xx-aVsxmO5*cq$;<7K>oGx@C#48`TA?Vp6~s)dGh{+ z&(&%tpV{*KUeAiHQjFj)}iN zzh5r@T<@E~4Z)^{W7+~+dOj3GL-zzjgURlkm!chedNxGIPBje)x<6lt_iw~;)8juJ z&+XBaVc7HIWBHoxzbCg%UU!sVeW9th=T!rRh*}5bT)QNW+`ZSlzHpmyO_!dx<49!Q zcWpUeGoDX&oIg$rTNKPLeQ~O}PW0xsZJREhv}r87<2`wjz=OR~-y9h2H-2x4eLdIi zOz{E7gn(e#y{8{6v5m}|xWV9t*m0ZF4;#;~w-Ew6QN)_THb*0~$Bq$gTWDvPR5)$(-tKY7lt z!{@)e?w0#fY4qV6m(8!~wwbR@!@E+Xe@5J0oL@UJ?M!@D`j%oIrmVxWHW=IxtY&or z&#r*1U&GMwMZZ<}R>pB*xdhMVsar!XnKbN*XkO^I@L+%l!v=k3hW*K*d-pPx72m!d zy;8pS?tk$!+6FqG&R6_@J#X1{yWe&PJRNIScOE@!zx$C#S5tW0?>FpLZIbnN_Z>&>MqsY8tomi`BBUltJNh^Q@pq^tbrkrveEA7=w!x?KyD zUBTg?@HoxnA>%(ztE;^xU)OPGW)$!JxFnaA;n(N&KiNeRD-sSrWPWi^xNPx3->nmC z&+U%;W#IcKZJD={qMrz-Rr>n1Fa51^WwNR(e%?DLZa?MC%J-FbYW$S9xHT&AL3(81 z4P{))*{?-UG#edqUvE2KDroL#s*T1AWEj5f{N_1dd-n;59Ch4qi6 zd?gpwuXot;uu-8BsanslW?oZiF;TsesXxBnZuMS$f7{cMAFp^_Q|-H6THKs!v_!w| zPc9e3pj|SUw*l3*8gO)-Irs}U;I27FTL%!wN1d;zUSrF<)yQ%?fz%# zy|wtp_&1DYhMNQ%sCzP@*XYpyxKhZ-h(;B|jh`8d*@6`NJ@c@ML5?(w(leHU()t2%x94=Wp^|KeU7#$TVZz)9x;H^VfY$e;p- z3mWYVe~vu=+t6?*=Q{ME5H%;j!pbw&b0&WlGb^_&WFWKfgu8 zf+GB5`+6sK%*d2SRq3!y~NB_;@=Kd4i#D3tR zx&8b%U*DJVY~p2lc3rpOLqh?0Wd#ER!-|Q|8E%+8=+w3mVC1m*etzR!h6l6$iypfh zDOhRH%6`oMXVW?9@A}sgRw&&}Sh1y$!8&FfhH_bXVi+RE*Yj1TVA-@EylKY5d{zC4$FxRRmb@Bgj( zK3lBw3M*Ix+#mxA3=B#P6IeM|zH%Lrm#cW#wsED(fs*U{oQs4OSlN7FWNs)vc6VjX zY?n&W(oLN$OTV7cejq8=A;HojeSG7)YVr5~PhXh-^H=&E$IH+4HyURMalF~`Ppd@! zb&!PH94x;JYG|w$KJ4c{r<~uJ|?JVwfn02nD8v<+mzKUod3gy@#C!%5C6V+`}v>1FXQJ= z`_|We+MBp_g}vzq!DIdNt*oy6t~btR%q=~)f9>?Y|9Y|urwT?@vWoE6@`4jIIE=H? zcNEW6f551sf7ixLk||*QCt0uajAvLHzuSv3Hk?s75E|$k%gem7h0(D+nV%^!p+Qvl zg86k>;a#>LZt4hrn7@aoKSxOL^81+L0{N=%f0~`@-*2;hANOm%4ZA{+00#%NYY;Se zYnTpPTI$Ws#~@W^skW`N_Tx0cWv}hc<26c{mhcEfZ2ae6{P(-f^Szq$FGpU#_p(<$yC@nJ)~kzh43|dhr5}7ZW@1I6u-vQAu3h9X70%Vw->c479L`=va++mIjSL_;liz*9ctf>&Qiay zHau^qfh5y}*xz?&Yc(@0ZD+qR^}8fP&(3##|9`9HRfWnHujEOX+cs;(<&Q=})83`M z{PvVR*Z=sN9f2#(|7l*Ie&L+B?VG>zq;4O5e7yG6p-RPFHw^DR?mGVB?|)x&`Q{e> z94EKgA$x6kHs4LdE9rK*_mZS4Z^<6ZY*7=a__XDsj(9@E zi}&}+i^VSHO}F(j7T5V`$5!WX;M5MWFv*3oJdiOc1_lRKhO*TkUmRw>*l-|Qli`Ko zyG^_A3x@rlsc^uVp?>$XzK-v`)vQt{5+5?I%JW!P_0{drxjlc=4u`(DIQcuz*O}YP z%Pux7UjM$ZMCD9p{in2fo3CFh`IK+o`{cKIv*iEgW47MGbu)5ni%wNmEMPbVn>=@5 zW)L}~Jz=)dA@jVwJFGbaR{fv;nc>nNrbCb4Z&Ep69M0h2@>{Kn^@hmab5_$#ZrqqZ zx7CZO`M@!4eh%B7_UyI$?|5w%(+iH$)^FZs+&tm8^EPI&t!vgcpG)rSEZ_fCb9V5B z)cQ$YKR(P~&|~oS-1~Lb+b;N}g46i{dj^^G{X*YanWkJdIN;S|9(*_V)!DK0Jsot}AuEKTd? zEIuh*@q1ghaS_9tRgAkFAKdM?Uv7Lvz~RkQLB6RQf)0KA^)KYqES^#ePHh2=^vm4O z@0Kk}e{;+GckFkOy#_y{y&uo|`9k*iziDpy+uU^SpFF?ptY;OoT-E#iTPiA7AAM!Z z2g+!c=Y(I}3w!?o>}duD1`P%UE{PL$k`JwU7*+)xZb;F0@Yh$Yt!Q9A@G|`V#L2bZ z8<_r|yH$5Lu5puA^TZeXJ{|sgJfJwXbjA6`905-rHkRkVXKeU#ZNKg6 zKl8SqWoaz8V2CMoH@Vlj;2~q((p7Dt0z8W*MTL23?4A;At{zvRu~xRo+@;^{-_p>o>)+gED->suIY*DRR?;to;5YjN3&#)oq z=B6g*hF_u#3CT;67#cecF+YC4r$tM&x8X@&{mX}q^R8D^e@ZWTHFdi?o65sIOn#dV z2B^=rosbaMcy7|CE&6YZ)89GYUMswg=e>OY9_MhI-;6i?ltVJF`?_7It^XH$ZU5%E zIdPv^uAOc;e4smc|I+_{^TnlDn93!4Huo5~3H(TmdC2JC>kBDcKd>_F|2x@U`RUF> zqW|=|Q(GSYFYUDAyr9f}p!S^gWc#JTJJ=7LH#wx!Y9|xtbRu!Vwuw`Pglx-B&D(Oi zZFTFfJL_x8nC5;zaa(dZ^L~X7A5KAuc&A1_6EcuX~hvAB6L*GJihx3o$8}J{9 zcj0%ido!EGO7*Gel{oW;cUGSZZdIS&>RkJ7`(CToe?{lDZ(VNs-qVoB`~La~HW68m zUel?SA8X?-U21Iqqjt$HQPKMtR%eybK`n$$AU$(~ma(~%x<=#GoJyVpp9yy~h6g@0$(_7U$I}KF$#kVe$L9-}mL7 zz1z>ee*f$GX7NkPkM}QNzq#-KiCc=txtSOf71r4>zBFcoTF=ICyn1~YbEAVy9OK@N ze?H`iF`e*WI>W*kTk&9Rzui*d^*dsJ6+TIS@%W1K_l5EKdo$TCm~1vYD8us0V12{& z{@)9~?_Z#tS9LE#Am-?-tYhXXTsjENBc|u@1Czdn{Vru@|1Tg-FcR5 zZ(%Q~WV#Hse8Io}ebe7`AAPU*ypn0=>W}^JcABs<1U=ht@^89_tXxHRD}&#TKie2C zb8MEEvv~R7{^k7ojjh)<2S$c@HAhq=1SIQ*PMN9||8yf~{H25cYr^W4HZU-X^2ltv z>#^%g!`qwbf!DP6{qK&be3&f%r$6t{d6i#nrAj~JQvW%9e7J*^CeZH~?}!WV2b+k5aruzZ~>d(3~4$k3C&zi_SD92EcS`o{m$|7_ph z@toV{$35|qqn96VmXJ5R=6whOm^nJ=}FUtmR=LD|Q;+p5RKzpHKCD0Ha$-mji@NvV@Ii|dBI&t#k; z_^;&fd}xe3IByrW=BKpWk019}sW?2r}IAZs&vY<*zI6XI3J%o zJMi9%cgr(Hia2jSs_VUK^7VIQ$%)?c-{)|cxasO}{FxTYF=zk#ZCkEAcoz3{cf`3{ z0`vdBur9fs9o&;X;a(K9nZ>{$#n8dc;xn(U;8@YCy*BI%l#*4~_%WT~;oozP@xi6r zd&3{idV3@9&pSIv%Mz1iyD z_ky;i+{^AZeYt*jzGpVyyUVNVCn%#InY|F0$ z%i=yuKb*_fzvpM=tUrHqK6h>wpDO4FnV4f}5M>ZJ;_>q%*O3^vADYY!v%0$E4lkd< zyuf7A%H#JF6ZC@8FNIr`ziYx%l!3=gge$o0?o%+fMDUH&)w zudmOfrEI13K3E(|*R=b;^t;-nspWP{{=V_j&RWrXUVg>$^^LVf_z0`gvxA=tqdjCIj`gVR1J6*fkcvfA zxBmV4{jz;$w{CA4gTRJOUmu?0NQXMdA(o3l$gE8_{}A)c@BhF5J00ZN@RH4+jq#@T z1G`^R%ngh6cd9%#%j<#LYE>-PKovXh+5VC=5^|M$%B7w3-0^RQTTPP+7@;?cTa`<_e* zJr?D5A~Y<_DSD0FjItz$4VO=ZW<1&O&r;r|#uhsUA;4F__c&vSXb&Q7QH^Ayj9CUY02#@X_f)8s1NZM`7Z@8YXmYO*1waTVLQ z4m%rxWjz6PJD=^+`gJtQIdgYu`{lCNJf}S~AKp27I&-q&|3ybn-@Y0>=R@J4UK^VQ zpG=lyKl82E+aX+4T51XoF%C9{DrM2i3WXHyn5*|||2}x?$=|R?$nU;MZAC*h!@r7) zXBTc)mQHQykNsONb!U6zO%4xsCL@N}+VhXUJjs3^G$FHP)p4)Wrzd+hhurR;bLmsW zVdhVh*-WphtmZ7cn^G#KC-E*@D@o$---Fv<9Gk9QnO}JB6U!F^u+wsrpiQ1R%&0g=h zyEjVNQqNMa_G}w_ZrQcF#~zEB*a+lGCO&%&i7^HS=7z~{!&lciC~OE)|?dh65J*5&ksY_czF1Jq?P7!5c+Z@xV&3CJ@EPY|)3X^}U zUv4>lv_C@XEr&zHB1DzL#!%mR%wy@gXRCTXw6}xK&3on*5iY6FbYQix{bJ{1_ny4` z$dRtoT*X>tRTk8WQqv9=<9~ds%PXdTI1#e|E%-9)8#fz-!yY# zrAX2FjS(9vB>#TtV>|Yp| z{LV{_<=Y9K7mkvI^FH#|uSg^jqp1J1VM)nKyZs(?*U{v_4@#MTpoE z6421pUF`Qv^u?Oj-kqDeA`UY@RaO0RkCpxME$3Oz|J%Op{^nVg_-VV;V%cNb0`c<> zoZmh#pg{f0s#RLxboqdv;h)gY&O5iY1@?AV{z?v4b7puIy3a~Zuj#;PVf&@c=k{xg zyf~7sQ=Mm1_44JGXQ!M+R{!xi-*!jphON+g|2baiF}3Mp-*p*yu9$AxcDOmBdEu?R z^$t7=AH1)*yx6(_o$N33l1H)YFYJ#9cL@IHxa}}=Txtt!9%_M_GRp&FMm;@&51h=# zGuRhwTXiUcDdW_P`pQS@U)t;Mh;$xr&iSU_aD4HiH9cx~N;#))y0&A}?)ZEDrVJ(b z_x*nP|H4;(8}SOM#2A6)Jp5Cp23KEJD9W1kv-iYlj{SRHc|@(;X?G?6#Ub^3=~Efr zr|;V(FR^=GH7}EuyMM2jT8I7$Im>zpFI`v z%i`GU_4|x$D=wZ4o5w53a^&~LI0(%@emL-7jJH%ov*Nwn4V&EN_z0ZR z{&4ol6xzUyU7j4t+78$><53X|7S zCd|X2b+qunOoauell-=`KWO-zx5+rCZ+~s~sfc&qU$1?+XYu#58jMqn|89NQctzHI zj(MM2{mZ z4Z^=4=_rdZD*V+IU0f)t@Ii^2@y+}%3`h3oKPkQ+v$yVXrOA@`$!f>epC}Ko`FHvJ z=NH@6`*=-z4TJj7c%y?*i&Q9S<{)>I(c8inqsxMxf z!_LmwwXR11YOMsroLSAnm0v2EHp}lfVrH;$bhuH&$MAb@b@Gnd&zlc@m6Vg%d8?r6 z&!k|@wHNyjZC=RDzhm~-JJyq5tiOJLZS8572esYz9t*OtR5UBTGVb72PMXVhdi9j$ zRv)kZI{fSMkB#i@zj*KdyCq`v^IbTP`mC0_o)+B?q4fdi{J#BRC28v$9&}sCMCzUUP zH?$AzVO(3TaF27~G@qCW-;=9{BdQvMRHTPCtt~A-O(oyj7bg92H4l%!( zUwb^pb$;F7=C4loUWmTntz`Pc9v9^m+uI!3toTe+W3|Wa)fZ>4bNoJcL-dOK7nb_h zI*8l+>+2WJ`F5Du2sUERaP>{ytpAhq4s_O*seej%*m$!){vkI*ia4cZ(w-vs*;c4`5l2X)`FSy9DeOny|L-t%+vmhzlKR`?V0<%#W}R%!O`!_FL;LE zefW9vXKtp4wsIF68~&M|KBcv?H&kFw^^bWuJ|CtntaGlH_-wlGgzYv`7%fnG95Af?`;ZP{u%^~;0M=Yfls?A}@wnc2i< zdiBx-yRRX!foXs5N`6`{o$+v!*jr7r8Q}`|Y>-m&qpRW54m01CYv}4=R5<-xP4@Ba zb2gL8ZWb{=)N?jgZVh)R`M=-xE4S4fd2_cHl^;2o7Vhy{mdnY|Z*I%~(%yQy{ey!~YKzVG zN5_i&7KMgM%_+Q7#n3&M<;H`hjbb}E{+Rgf z%`4MhgK*>m;>@e=qByr7pBEc4IPiUY`=qwsx!yX%Tq$^2JsZc8{qtnxcKm95{_NtaqN^3wXrxv$mFmPV=-X!fzi!r- zyE>meP0iLj6bM9A96t#S*#fr)(d>V%?1z{SEZ1Z>5y!S`M}G6~Zwp`h*-hAXKO^^G z$7D&CpxY|nnFRPFv#YIhOTSc_q#u0baO_g`{hU26bq)vG!xb1fJ}mQ^KCen7cgM4= zV~$=%948Vb>Ti4O+?Xz!2F1hpj$*jdTj8~ye_4y1JZrzG1UU3&ENbW?5K#uj#&`nbjphlC>=6idz-vS0czl`XP5_=%%eW*pu32o_&bw%9JO856a7iEQyghonIqS^7y}91luv^sZVWV+=O~bpohqAA) z?zsQqz*Tj5FXcAA1uu)AUH&p({KA)=pEKD0m!GpKtCstFW)i>k?p=!*8yn)5I{uxV z`DOjKMYUzWRA2uKpOtsS(^kpKc+Divr1>>JP3KnK>^*kbyYeza*2>rC(?z4#-FwWy zxYZw;su&~~{!Bgd@x}s!39$k*KEFs^$gp7R-exhqNU^ix-ID*Lw&|Kq6+QX-gYBK$=b^1Z2S*Q1ie4ehOxcqeBABW~v_Frb#F0*B?@!Eeo zulQEYlPGPw<};6df`k&Pdg9qr|LotqZueZf8J}8@zHgrEvMPJU)&eOn$f z&R0-a1oO%gh69hE$cuCQXk_fa&cn@cbDB->1QG2De}M}P%CY1RB`TG{G7tIh zzW)EaPD-}?pQ&BF{N41~pPo%!v2>Zf_2rN@wnhho2~3TuP@N&ADchtlrj1yRyB|J-^PDFw)Wf4SXV|QAIeQWds{a*JgPd$sM@UNywwFK z!AwOjzS%o&CvdGUDJ{F$IlX7``ThUw6g0|Lui7?onmNysb*tF6I`-ex-tZ*89$GIn z7<)$SbLC*lOn*1Sv4Q!((!D|qDFQDd|E>;ODy6I`(_YyBul?NK$DH+S_6$dIcN{zQ z<;cZne;YAz{C7q-}*@Q%*7L?CJKLg=^I!cU-Rnf_ZP3K<+gsAQTg9ZXid_B zE%yVm)~3ZZUfQ$3;Lv@*&v5J3EiOKWRYnX73ZR`4kGT2I z)GjGtaQMbG!{}NpbJ^?7`Ilar+Z@R&uaxmqpLG81jf>Wo?(^L|oZ;fR)oJ@l;et0p z%9kG=G^`K^SzTj!ET4~GCp&ZbW4#@H`(~QmJ)A5i$8ze_sgp0Qdv2|o6*fi4E!J(v zn{~ll?^@yl10mu4hw;GuH(^bcd`!jiJC{6QI3Uat#2@$h#p#l7x&6O?-0_(|ckc7l zHy3|jQrw+0JMngic1e-PYt{+2(wnmFFNgXEbsYToL7~Hrakrbj5l6|!mc#XTe&koB z{(17@_>J>RTlyP$QZ=*v_Yz<#x-y4P% zi|a0ZyzZB|?eBZXMgB%4eNHbpA9?%|-{Naq=ec=EIj{emlXC3G*@um1yqK9yCa$~K z|3K8k_FV7(8_{x0#P7{~@p11L^WF>kJRu>W!PSogUOn(^UKw=AKw-n1JaCu?d|?Q% z{FQGT-!?=2+tEn32pxt8uipCaTXa}|-{fUyXFmTg;VvLo@%Gn#_1V%t-z839WJ-y5 z%V1zst(oxl4@+sqg1Nr_dmcRd{bK2NwN|z(FZM8Tu6kwPGA(SAU-Q$@M#lt8ussY6 zA$@X_%RYJju+x0%UXk$bu(&uYgHN(=sosv?lDod!KMs!l_Gtc=6-GJtZ7Nu$_XTBM zKiqIdx5xAHp&P4uE_@7h^ROzozAkT__TaJ=n@T(#mQ6B7q+~=T9J2}gIkti$L{}K>~TwG&#P;Wx^-*u*F8U2 z8uyiM{ql0}^oZ17%fA|2Sog{FA>(W#r{nIDiZA1xdHDC(eH50ee*Je#!pBV=45r6C zyRXE0MO>_x&|RJ4*_`@xi7bao1IQ*2)?jd8X0DWz({N#U(AK^C*ZbN^8{2Q!RLrg{ zS4~#UOAVQmVZWW%ooD&qU5cvHZRj z3JeXqzb>!&ENuJvp2)8E_QEfAM;2?Gq6(79FI%e`Il`Clk;)@{XMeK zs?f;cz~Xksr~7&ej@@A`Id8oFV)FWZvtPz`ah zZH)Vqy|Lv$X&e8-tv8MwE)A1(%3f#F^2_{6|R%l?t;NQm2y`Q82Lm%pDX zu28uweCvl-QKhzY+~r&MY_GKa-QDu{_vJaPU(WVeev#e3Aj0Ehm9*vaoo1Loq4Lheu24qz1#VH zp970>HfG)ziu6tC-QF`{^`1$pxn}&!+v=DzZ}rwgM+6!+pRkfVe_H!O_i2vB)n|Wv z*A@J_&y|U3+27N9_BZ;zm$qtfc9fU8wqKuD_KMG8Ng1BRhpqd1w_aJkukX|hY3mHh z#Oi4FTI0{@zp`%m{QC4;^2^8n`xblt|EtECTGv~4==r|ZZ+ni+QU7q=x!(V|+*W;P zl9(f!@id=>FC?bqqia?i~V42xIh2t{fKW}HjJ0MOwaeGHq@TZtWbEQ*Zea$OPqz}Q~7CU~aVNNqy%mKe0OQ5f}dxN6CjX4*xt= zzrM+SA;hW+3<3u|ud}fzS>F8mZE9*+PwIOveiq)>6>A>MmH2j+pQGiI7VG`(NrwNx zwkQZN%yHg$wtPO58k)G<%EaLfyybHb>8my#IlCh3+%7%^*shnZ#xY9AG~ij zeq1lnV-I#A2Q$O(IpGtQmT4&W2($RaH9F}l_k5Cwu3}x%e%?#!|HTbI!0Hs37*2FK za=sGfkk5CMO#Jq3EyyeP%&i3)K0-oRkm-QE#_qfu5`ItW_AqHEFUdK2=`lCINV??1 z$o994P?s%YYuK>b!AUaFuF>Jmy*Wgj%L^9t7;F-?ImMy;>)#R9 zLdU+1b@zAm{-3u2v;fILk8#1`#fu*>EU20Pxq7~%qvXPRi4%)2xPP1?*mmDM@5lMJ zkK2PHBO%*8KJYSxsqePo;a|{W;Fc^Y@+iPf;;M<$zEAB?H{WDzxS$~LWl6Wup`3To zah1hJdzfapaX{T##Llqy=7I%17q0IMocC6GnV`|*`WNqDZsliC=l~_V7NbKSeuv4V zw)Ca^`FX0IKLRpTJb{(r#`l`}zJ4_g4l3GVQv}mwS(3g#ThJ5m_4!@xdI?CTn_$YY zqk(Cv$xp+ejnbzVfYiDD+WGTi1!%RI7Br4d2r}GQqoe$ZYeCP1uI)VrE5a9SIL$Hn z*#7{SDZvaUlsF>hmvE-GDE6dYk?YU+rYj}jVc+@noYA4Y$NR0sGQ2UrcHhP5ejMWz!9_g=)xWPrpW--gm0}LFik;!X z>y+!=pIQD?s0X-7EZ)e&10K{$=YbK$8zuX zo(;Euyl2aXOly2%WvKFtSm$^{LoVTh7hX%?8X$FNitJLEUF>5G)d42X|y+Fs| z*~0mficp`tkhP!1toN;6N&gghPjzkC(MY$7YmWcfp((A3n;}N1#8_>XT;pWZA0)~KH|5Z0ipMG(gqj|@=KsSlGpfp%i@!*Yf z!k^+gJ#Agc0FVF+eASYp0Bi{b8G=m;9FWmS0TxG)I~W)m6hJqRfr5?&X0ifDlLAP9 zfx%IL1yrI{ax=un#)3}2T3{36=?NK?Xn+|75{Ie>nFcYRctMg}0yY*hsOtdtJ^@cb zyu#oB@e7Ct^E>Dqey|4#3KA1ad&L=Kjx&PndHR^sb|T131_rnz!GQ@8BS(;cA0bHs zBjSh;E>dF+Y%QM*XeTf@s4+}g+OwaV z!L$j~ba51zvY+8W6+;v&Lx#5`s9tv9WKeDTs6W|;L5bl`uQQvYz?A6>22somZ6YV8WnrnZe=m z|Lw)*yM37xMI4U*)Lg}4ZDzx7EjQIRzx7Dd|7D%87tQe5cg9Djz&YUjM7O-9Te<_6 zR{yQMlekxs>p*ohTftP;2X_m8TCCBZ%2cqPL4zTf>p%tHgN+<__+DS#5XaB@pjnrB z$0?={Wjku5u5B-|_&eYJe>j6k!(=9deOx zhwHy`-d=hraNkVU@B9B7?f;%9*2-S{e(di53bqB|3@?KI`ZoNqdhoXHZp!7x&@h=# znN3U#2Jwt1-rw3Dx#E7R>VxNYf4tZKpL&ns#r)65xK=!7x=_VaAbicLW?cva!;IxD zSGW#T?0B!YM)Pq!s{^A2*SjAY3iFwsTrd4`N2!v9;X{aU!ymx|db0gr%p)J<2mPG? z|H$?KA`JD>?*I8L_&*xnxxSEN&E=Ob>Lt|}=Kf`!v6kV8rb8Xy1G^~=Hb2Z2|2+N? z<c^C*GjP{i#AOVXEKpe;?1qGcr{CViHhi{8;E9^E-d@{FkB(jH28Qt66lo5AM%t z+5gf0z#i2F`BNHl7jopNx9n$H!0>02$JgVGm7E&Af4m>&=QBRjXV}OxMUL~xgZ||I zu`~2E7?v@(2RBH(GyE~V^*ncd!)YJJPp`KHuz0cmyYp9@;VjbyQ8mW3qKEBO(-|0^ z@%*dkRef;hm9{tI1C0&4IfWZ9$=0j?VAKm@`E|@?LFw`P;#{pv2Rs|DcRA?(d0clZ zx}oy${!o^(sq4KD-oG8B7&@D2iPsK&Db<5@On-KJI&eLlFIE1RtKq<`5T>lljX$>9 zvhKK}qG0;&P4q(jKdo1n*uFd$bNR@7#u@g1vL`o2J>af){OVqPnX{(h|001o+T0xL zBEOxKV~TO-6!^u}@RzlMO|id$i9v*c!GVzhLZE1`irr{E;@`_rChYo)$uOhJaAR{vr<;_rKeI{TF+^_N|ezb@8(^mS=w7c^+>i>%^hh zA^;(zPMl|CVCc~R^Ov+X|69z!!0?M>Q3FVR$wTJ)ldd=Q7#J9IFXU%BfV3HHYyY>5 z@9S;KBj2y@lt=2YIU>?Rc{N%e-{+*?-96tIO#mgHztOLjcMnn-8}5`>OqRKVCckzjyWM)w-^~qU<3u5rq?u9%O5ekl|ur2sok0_vO9x zUu%#5d6)LD{TyB0s* zhsS!HJbE>;OIgHSt8dkeV8iW6^A6VVb2Bh77!~~0o0-0_p8xF1I(ym3LYJ?R#}7HTP2GRo$yvyHvZ>wfX{W zRz5n(c05cb{ljOzc}xrp0uP=u&)A>*<-P3R>KC3@-)la6@#_1vMX$j{j-DWS2u!NNj-Y`ifnTT#kBnP1c^b z)MDwQn{3P3EO;3h6d13+pO79sC+M$URoK^R&pLnG;Ey~&$>_5E4recyJ$CL6q zOzc#=2_451wzHP7n z)SF-P*j_aL|EKBu{~Xn?dC0x}ZsBoR`CWg%-M(Lc+4p_*@0H8v{W?9>z+$Hmi~ss3 z4IwffqKzTjJ&!i56Pppt$r!7+Af4;Y>j{kO%oxK%9r`#D+`vKJ#rJ<3!w27A_jm8w zVYy}356fxX$qWrAEq-h-U69XY`Q`VIhy3*x%&IJDAOF6u-~WBz`2?|GgZ(@6*)%K}8SB&)fZeQ*0F+%Ay_?zBX zWOkY6eRNI;+neh%EsYgIM4<^*nMon+SN`$Jo^Pz#6Us zUVa@>#*IH2Kv~O?;Y`KX`)_`mSO5F^FkUa{%ai)pfBH}R&p%RHgg^Glmo$9k^6QS@76jnNAyA) z#{wx(Q8)9$;s57LFU9}8zV!dE5BlpvPH2k-9n)B!qO(db$M30 zag(pZc`gfuV3r%v%tf~$X;+cyK={A;RX?WxJNokF|E}d)^A5KfF)%PCNW}j=ulxJ^ z-e380zs?@sZ}(&Oj_32Lck$bPxuEAZ^S^z_h%kn^@116XdnW?sn#E-xlA z6zu(#{~a9s8WW3-IO8d6>BFC$D3YF*?i)3Jkv#8H!H*yDh{}cC-G)2mKX08El_?%fGW@ZZ^{^xf7|JxXTfbyaoC|^{+kKS8V>d&&` z_pgWjbzc_OFQ}V8@6ybywUh6!H%fP3&u?;@>CDwe%c~4qz-6Iu#gG1}|8KAVclu$y z{o@b%shyXWYe+LJNeN_=`l}>_qA(n2#fX)(xzwt2t&uXt5ku-Ga%yQlBH53f;`AjqygzwYNVKCu4to5kVflls~p=imP3m;JYu zLEy>zEIo(v)6;bK+F$*3V7+|lmB9TsZr&7A*kAm-^i??X1tGBUGZ+fi{@;B1|JNt= z*H`~{4-r>%4`1&$XH^BK_*n)9j)p{=Kg+oqmc@ZGks+f3`~Uwx`v1>(_B#H5)%$H- zED`(b_Wr4Teb)T`o<+u?nafu*ewpAP_-Um!^F&Y*IMKq*u=&4z*uVAF|DPs3-tg*B za$KpRQf=4yz&%sC(-;`|{{Nlx=l-#W@$!FSnG{NX@PGX`yS?6V|Eqj12FpL$Pm)to z7vym7c)#!WuK$0||Npa}U8djw_m%gz(!;JYRGBsW@v}P-$UEV(Py-7nzo>aI%=r6% z-KYBEPy6@$(qG!{9=hJaW~K2~rhqVszeeKcKwdIqahQ9!UY2pf=U4Ts7z|GTJKOw! z|Nr0n&pX^(&d##Rm$ArNuI}^f`+ugLyOP6@xqtH1#;V|kJkAZP8DDK-Q2-^l35H?} z>ps=%GL-!MpLn(P*qn1$92TjEuXmfXu0j%I%j3cy^4mWB-{$>)wg|(*lmC}7Y>5B4 zo%O)}zqj{){WoR)(QUc6tBf4>eYbwM<8k-gmDhGJ4^^s5RDJfgw6V;aK?qcSIWknR zHbnl*=lehTQ@y73luyo)fhCI`@f$~1pLo4_j0^${i@&|v|N6oE+5hG%G1S!lw_{A$ z|LMIDL;UYs*Q@5os~p%>`nt+q`m5=-Js*4X_pI5xS@pq-bLVs!*+YdA?)TNhJ!A3v z|Jj%SKXEmGkvV0xXn6?JgP&dD1s{1B4D8zfTL~pxS@!QY-+y1OhAa2#gBe~N{=fgv z>HS|OoL_Cua^k$(-FdR|p!)sJ%&6nlPU%?|>n+6}Kpfx1V8D7HPmnR;+yB>}{jHR=X1NSzJGYxD!93F{uR$?(TGiokQ~@EfuSPAf#C)-!`rX*Y5({0$V_^9c+=z` zBCTuGeu;u?fP_*0hxxBRA=yNNqrqL8NuiWs!Mp#{ySX%tZ59eK>|bHg&7Hx}@X`9u zbFYRv?glmf{|oPdvgt~u1ExRpznt2COy~dq<@LXoOP+W>`7MKyu*19ZowcvtX7}6u z$|$y2FpZ%-O6Y(bUk4;}I4~SBW0=9raQ0vQ&IJ>MoFg4oYTNZg=A3mkVL0%Et09E* z!ue1CC$0Ryuetus2YDfeZ(l&RX=eZTVO((fe<^E2<&XWp-|gNFDg$o(wz|DN|NXky z-P_9dpUe%C-~F@4_?*S|Neo77wy)%6^k8t}P)uQAb$G>f-~xlg&KVU&sthUOAG-w@ z7#JSwGb9NmOj!T#b@;#A5`U&&`kl|k5dX5?hhazD&+DoGe?3^w|Nl_<{$E$`no0G>%H;G<(Sa8*JwxiIz`5B`5sI*T;Rg8XC1=@ZHBkJ4DF#z z4*U!;L7WVdW+k&89qlT9xAXZtJqCszx#l|(0z#bYycp(-d^pK4U&7!vgO6fD7eh2x ziy6a=T!xKT>-Q-(l>Ogt%Y0xOgAxPF&v{E_A2hPdz4#lO*Zyn!?YO_M!uM}8HeF-x z%Csbp!+{CZKu%U*C^BO>;o8C@G0D;SeBd6JLOt<|3=ESM8ES+UoIG6b!O*gbv8MgM z2xGwW5Awnczdp>r#g-7xIHSEjnxW>YcKpkG<@am%iZ*J8uX|HlUu1pd`Hi}hs?%el zJR2VKaw-(p@;QNRnyk)H6wYvptzoe&(*qm5MT`t;k3YPh%y3wT!7Ki+e-yezpUz#maBq3o{=>0C4>-Y* z#ll!nkp7L!VQ0mJmxs=W*o1!!Vqsu7@Pv~gn8~E}$9X{p%cuWUm;TpeIxvU%fiT0q zOaJFcf_hdTp6b`{{Qj?9zHZ0w3(ovs>(q*VKQ&u%{^yD8_}@{{OlLN$9M}i7B4Itl z7tV(8LPcJN28J0Tj1Q{6-WOr`1qzaNybNVO_iHkId3axpq5N)X`1{YcpH3(nn=zlb z9zOr`MDslldGlStfxy8qo#DoPWrh^i2g26Dt6NDU)T5l%WY$+P=c1iZ=z#aWJClR)Wet_Ob4aj$=VFJEdDI#V)zVlw(8#h zE14A1f7P$~6K~-C-}&kPUmxW`}nl?Bjp#|M_aaF2j@kUu_tETAuu_ zxc#&uqu&OV0|%iY*}%X6YJg8=*uekal1U-#-~A~J9+rQe`~2^p`v3I9_#e0T|Gjw)`!@818)azK3lkE8egZUNOH%=|VNcH2KGJC*AQjRDYz2UCIutAp3i{kjZ6eE;n@ zCulLmtpE3%tKq_z{{ipoUtF!X{KKupyo5L5@v+{$^LN&)FZykyasW~`urPvrkieRt z&9q?4|NVUo8&n;-7z~#GpWXgnjm6>R2m8-g>w9bXGFc zaQ)%_a|{mi84Oe%E;A`?VY(3h@BQn)+gTgHVYlx?tA5Gc^Mh1ok28k$82RGlF;l*J{3KRh)_I2U^otYeNY)CxZ6UMOf z>@HCU?*?!EypK}HY(J^PaFa*D-u|atsdj@d)f4DjhQvP#*1^^GVcs10WWqh%mp=0l_ zcqN7!U51Uk48Gh9e_!nvV`!>5zeyBSk<2g@xNs){(qM%o36=v6w;0+28D_Nqmtjl@ zW0+yg;MC0U^uv5DhBM*+T^TYs7qB+$iM_>OmUH96@0q9mD%=nDVcPTaY<`*=BnCjE zj|X0GHCz&FP-At-5pJ;ibDX6i_Fw+D|JjTmrq}rAKqYTsXclk34xJ6P%m zhoLe1g0)zFh|8NPwKIuj1ppA&1CBF69;RER9M|NC-YPsNjo?z>ipuYY&<`ls4gVX!6}D9LQ# zOjyFwFpVLXgWJ0!IL4F;m0b5St1N&q7Jc)4^}Yk z<>zPbYQNvTzI*?Z-&^*tpTJUa`}VzWe&9ZI69Xvh9dsE3Zm}hBHB2#UP-Ag;vtbQu zL$*Ld9&3Zlzg)%zs-VQg_kS6~j;3%!~l0hgI2@I43N$;X@_Vgg)OWHL>LbJ`uci# zI^zp1hPzx7A~`>lA%(($h;58BR)4N(W{71vki+<4E8`OmhP`Y77nl@GSOU@*I#_l* zoff^!oau!Y!_N38)n7aNCa_eTj=t9fZlHpSQU~b+AGjOtm^Dmb@VLu1!HQi$jiE%e zK@*gjmN87wYUq_VFEh`*WfIG(@ZEXEEkOtpC%L3M8QyX=^wv45~~A zwh9?+<2|sB=>pS&mzE667zARP8GIQPLK$9PU;p0j&xga{R#W-@(|;Gg7e-E8F54J) zuryQ(tW#zOn&=i4C8OC;mROu5X1h=CfruiX1u;O`ucCl z)3qz!iz5Y(L;P(9FI9)zxeWV2F?&HsAyv6Sj3G_b!Gpm=xQ+LomY)A%$yf0zwSCxExp- z)-hekWms3*kS2Wq)FjMij@VmO`u@4%_t};IO*9$n{$8*D8w7V&1Ls<%51tK)YnV8; zGPbN@c+<Y#Jj^L=j-tuy7iKZz$uEZH!CSu!xjM zJ7mZ%SjW_n%T&P0a9zPckl~q@gCK+a&lB#uq|Ni*fRfwZT!v4ZC;s-opAM}N7#J8r z7{AmiJTM4heBr!amoaEHL(naTH@OU&vl(h$GkjHWxUa@Ak)h~*4yaL7_V(6GGlt@+ zNzp*;f7Kh713wozE?9*bXR$UOu`XGzh<1SkQI4#d(UclNQ$08xj zu#3rI0vCfI?{3)|&p8nOiza4=NvPPx3ycdr@4&zrBb8tOb6o}Qk*A5lIgoJnWga+B*qim1cfsSW!q z88mM*yh&_WXU0%1?C?&(VInvK=H1=3b$8m?S@+IgU&HjGon^<1Mcr}z@Z#CPaV>*y z7~>441uL~0rc7;Ed6;1r{NZdlKCU@Kn$OT$jy1F8&XLK~jRFl=UZP-#s0 z{Os)AtE_;xPpW|2spwPz($UxE4e( zwXih2cbIJ~u%Sd^ff&Ow77k8^-CPQ(0tp@rCvtCYOm@%bYgofPW&YZAOcE1VZakaF z&<-wFAw{!;wnO|X2B)5uUOdSR_t{G1O+{F6KHgkNLqY z)&y>bVu^%R3?FitJGL_JU};#u+Hfbl-{zACBsk0%KBrq{a2DLFeEwGooL9hoIfgAv zCbzgIM6(KHF>6F}YA`u`Sj+5_%e>(_<10r{YTs#V#1Rn2bV`OHlX=5YKCn(lP$Nz# zAtjpeRT$$FxvbZG%q44?uP`0RVJ_fg*vxf6j6q36h2Q>9K|Wu@7Df+I21qN3g^_`Q z!J}c~eXa>XtOB5f&@e@-;ex?|2@Dr<8J@{7L^CWo$9N%^;gj9TYipyciy2a67jQ8A z-2v-0fV=Q}L>J_8UN~ZK;DSy=gs4OCYld1Thj7LdtN(*W*zCSsaNcXlaEYyf1*{9| z))lK5f>av38Cs$kgVGq6gfO<4G5uJ@u!X_kIm3KitN<*OEbrAw>)|#`fj1W$YGRkSE%p%aEka7|h1VwT!`FHA4)O z!*<3QObY1&3uOMK3OCGSR>);otipKC^7)*nucDrbfJ-CD$c)mGB3TuR8A_xZyctfsy#h9XgJC6;gezykwa9c`aNz?9#fC73 z^(-1|nLO^YJ&1!dmvZH!8g zW;3X@-oPO7Gpu2f7Q=Lw4Xh2XFT9dwkq}~7XUSm5_yEidVpI@isSV12`^L+YzSfe!MA4T zW>8q}a13GG zqRQ~a>c9+!3%4137!~ZL1sRMu0-iCPuyRm=Oz?mzk_imTs*G)0xei=s6A)&2X60bZ zb3u%uN*2_GHDmZW?~59PExQBD0SnKD&FAfYDdgE>MA| z$N1nDi^6>8^-Lm+3vM$U3uW5#WvM=R$QE2`eTil=31tm%XP6etC)+!{-UF zW-~8Pb?{)A0q!=Ma$E>!T)^5;(<8=E#n-To$pNX@P+P>cV69oh1+#`Lz6?qXL17?! zLE0St=}q(nspnvr2}@;QD|zO!ya*R{$dOo}%kajm!K#?yiyA|*=mI%Vma}9(aF1mH zYr{@XhcL!d+W(_KUiX~9pkH~?`e6UI;O)3vJlOFyVYQ7Xzy2dG@kMlt?>FE@miVZMetsK#1WPXyD<={cOes&lz55 zHF!>CNS{~vY_%?@fjFr74z5TqEMSo+(QXi9SeeVPue4!d^9s9$U$+?Ea4{4MJ6vXR zFk|%C+R(?ra2KU2?8y~v@MZut;N%^O!x(?DG{~`Ja4}R1EbwPIal7P<8H37!>)UPK zSDybJ3e7zX37jQ6KS7VsZAm9T^!xI>6<)HNr z0|SFkfT%;3v_mHYM=p~9=K^z3X&KJABMj77yv>lrLzBgo)BdyUBz&wIqg)U@iwjtstj)&8}|7^lY(NyGKLAaQ@K$R&y!UQzpk=f zm;$O`b3h|19BY|)I2mFY6=pNIFr2t<+Kkk)`4Y=CgGnKr=?NFZd=`$i%szq)zU!EL zL>Y|O52!NO{XFS^PKe=?=^F*cM?wyN*O);Yvt6|x5hAA8kS3(;!nH<6yo^Ubj z1^I3X(}Vo~-@0Stk%OyoE8`Nc1|h}-uZBC04ciz_M6%9^W_%#c^dbv1T(p_h!Ol!X z#UYooKmbxNFo4EcYbSxmKd!S)&}Lw{e(=(Et^n_bI!=bldL54j6V8HLnak&PgWAJJ zQz*0U zpE-|Nf$6|K76oGl7p^5NoDwjM)rMOb6C5aBwnM&Ug-Xm!HkY zBb%Y!2k_*}Ml*(Li3PPCJ~!DC!a%L5SjK|ycgx*b8+L+{tjEv&91NBv&)JX*zg1=p zYOD>bxfIf67w9s4F=J2!S6l(C4Y~{|m-fdq)GR6ApvNhs5Xm&B6&?mE4yQvI_FZi- zWL&`Co6ErvF6yv`^?)iv3LmHfTFtPR$zd(il=<$N6Bs;&5+Gf9P&F;U;B3XNpaN=R ztz6Aeo6B&lvG5vbDB+pHfe_H(l^)}VPXC&uD25Z?TD%=mhtAJ=-G4y&h)NwIv=2CD4%}h6o zF>K;IpvsW9`|Y+!CWpLYVMitv&j#M(^7VhT5se>)3`d3=><0=(8J>wT%x9Q!m1)5? zh7)hIv^*Miumsr0!^=Pg1~yfOq+*67CIu6Y4W5h(!WpxmSyBeld2nYq@ixNprOz& zpf3KC*INWt9JaAkfP)9pg>+!};>fT=rC~4Eh7g7yd|i5s52Bb~2r+Epx}XlKvZEPp zI5W61oOqtAjqDz6P&Z@27On%^xi)w*D%=1yUT+C8?BaFMVsK$P@jS;EN%esnoDDbB z8m@3P%wQ16VN6)hWWss?G)o~V3L1x;d7*3qgQqr=yv?&2$^ZC49)Y=y#{tx*iD5b* z#}X0F@B-Y%4{cZps*+k*8rBHCaYrg092iX07=u_GJeYp4G`u&Mv72jy3&RU720>8t zi7^y4lyfo^i-J1$`(KAoV8|3o`1j}M{vJ>og>)DLPhcqITu}GzwJzrd7Es*@o_k_YV2Bc9_-5sBU5z1_VaF|oH=zyR1RQE5 z&WJK>;tF7C*un@d(E{wY-FIZVGKrxbRJ%ZC3K&=%&RQ~PG93tI1PlnALLJE3}SI*bRsxtb4YeYyVMp%>4iAiB8SHcZehp$`($-0ms zHY0WiZidSL3z}FwCNmU2J5#BN5}qCmJ5(I@vo_piTAWm7_48<%BXBagE84hh=fs{0m z=n!B?Rs{`Dm9jMOFqwb`>@v6+_JYE6C#S;%W^f)4yZD^ z`GSUCIT#ef8BTzj6dSY|o)p;nI5I8qY+&ZM5kUB{XFG$&S|$lLPy-q=)XLqkzV7Sl z%S;N}xB|EtBEO2SVwlkUWdkec1Qo_GP^AZpy9Q^bo?Q$(e!tr-Ey^HeX~UlkNsEpQ zCJGEsrb{v~9N2JSo5_aM2D?2+TN-rP&M?o@Wcrfm&sX&<@x0B8)@zKrR2n!Kf|-__ zW4s{bzyj*_b1-BwUpUcN8PUwr;mz>%N5tO$|95;NZX{1D;g}4PhK` zIm0NS$_VPiaxu(gPT*#k$vi=c;fulnuoWeo8?MJy??no3pFau*0)-fwbQ!*Y28F8x z7Kkwfu_kaaNP?P>~{j1&eU;E_mGhM4x9%UB#vsxb&MtaEHwti>>a;p9|??;4yR{&+E+D1Dd& zw?}}1my;n}+QA+idHJFbR&X<@ILu-R=$}3R-LBW`G=&oKkreYWCdfVdrOM#68Z=QS z$C8oHG{O1p3U+YFKJH+-!4L5?cxJPS!GOQuuK>fo&<4xd3{TD%3NV1`w*_+6_954s z(o;MdY-NNV7=p%D!96aYHK2ChFI|S3*$ihuqaX_;#Tgq|Jf<<&gU7Ex)oFqfOM@=s zFI9#&hiBIoXbUo|oDCYe3TNzF%;3PZ#FOFckDu!l8T(F8VCdhha$qq$a3s7KAFN_p zp$*E9hcy^tz|FeDuXlj6#lBe224QW^4d4j@P_veS#lagihIax~^eU);N*O_hZ(0t) z3@7G4)?fgYy$-f*|J-Ax=fycQy=j9K!3+!s0=OA|3rtgHEMj#y)wp&YsQH)+sxL$s z7nn0<91CSoV7#Q#Q1$1JIHXi#0Ie&X#dM%NqL`QI3unXn+PB$$APqi(49^q}ctV3r z%VS@pXG6MHUv30+$r{v^he0(-24liHCK1Mje5MJ9_y5sszh1}>E^;3~f6d2PAcAC* z9VbIPw?iL8K`zq;pC)*SnIlCpGpG}y;L6}3?cl=z>Tn4$cr>JNemD=0Cj|yJ9fl=b z2Sh-_65I?oSwI7>px#fhzylR7X!4m`zh<2*JoFtHc)UP~-v?Bz=`y_WWtim&%EKm{ z3*@Zh%~TFdUY&hf8A*8v`=%QU&)CeE%Ehpf$$+&%f$6|jP6w8TY~cnszSWZ@R2*WM zGz!0{dNb*qnZVHR3hHu$)5ij)1JgiN;J1ZqI6+;;A7DL^tP1T{J$Z!`JemIZmdgI* z0eQG*0mFoNGX@5R!*2Gijq9Zjv}-b*X`Z3U{KVl|Hfx46(~nyWHK7c&^%U5PDlDJ|wkYF+D0T@UhN28rj|LG=h5EXfrIQ%^!);b7a&F)jY7l^| zu3=zW5XgAJMf$)7E{5wMgJ*yzP1(bnSWZl2kpKI_-G0$J&H2e54Xm6(4?ZGAG8b#Z z8AcCKhj9T1L$T}v1@?$0mJ?GMywHdA1%~sQe!Om z{dW8J0O@;GFZSOryPbRaPSxwRccb%mF8$rMBV=8@8I(a7n(VqZaevQ%I!6`rJnqr_S^BDux`+kf79pB z{B!54VpYIeZc8PH(+q-)QEW4m66UBJIBS0Y&pt0E8(js)dEh}Aa82aE>fj~au$QY~ z2J3<8`@SrdWIC{w>w*ZwGsn9&Z@7yV{6FDtcggz6|D*ALT)#KV|4I1n|No2s(l5Jz zAFuy&{4Qu!;+ua5<^MG9;kxtp?)|@a-?7_$Xngnk{{MgP-v2rG{!7LE-}kQXeYtFQ z)t!pRy?3Sab}ZbTbhPW;*X!~3_rBZpde`;1>fG;BufH-`Q2y_a<5l-(Z?VbPsPKL9 zAD$=aD)!ly1?C(79tf9xkS%y&Dq|E&hU5ZmCLckACZUA(UzKso*O~g8svM{ROef_7|?Ddv8v)k(|KdcUKNSFEXHPQa*g7y60-`9QL zy?g)H=>1n8o7aCf-~GPo`tDutYo1&0eqV9idRgV~+xPF)?*3=T};NILmv%=|GvOUh71~3iA;sDkEr-~q_reTmGxyhB zjrXm2&&lw2)&KYZ|GoE@|M#|e|DHqgR$rFO{8-NSF@N?!(3&K<{L0hOX&;`M@2{M7 zT7UnZ>Yuaq=l?Zh{#*9Bc!%BkJ!kjv7TtdOd&+O8cltZypFUTxx37#j(RkN9{NNg~ z4NDtt>M1xKSjV$rHPaKzWV?Coo=hv|-`%anS>WWt^aj*N`1QHx*Qp#3*5)j*KvY8v-V8T{4yZCL;Z;xrRU~2zRiX=oSi-P@Km1oiDi>%HtBc`7E|UPL=ICIU0Ga@LB9O(!aGj@N8HDOG6%Lu71t;{duAenbr;~w%k``u1Pvj{-XY{^gV;_>-+zGHU4*d3bUOdDDTNU zd9KkfSb6V7C3D*EhCJyDP7bS>OIA1J2~Ie^vF^^RtcGbS2exfKXO$hP$~dnJ-f+3W z4I0Lp!L*=6xFK8M!3OSz7|sdS;tN99TC|vIm=szx*|x}RING4_eP)iqgjal(JSQ9D zkBcw`DJr~W3X**=iSgIQ*V)l4Erkll19rO)jPJ!7wy`>RFzs-02sTm(W!kZdsmQg#pJzsqLw&6-+mfqJb!nd@ zCLCXIhVO!t!#_P;=1r*%DUDmWG9(v7vpgxx>1t3@Y54W|;bnjOzoD<9zKHa*+>l2q z@EaIT#IVlT#HnD$uHepar{?onTTTIChK-U8+ZZ@J8>VIyEhDc-!&&ut6_0LCW0?^hdswc8 zMWF?j&m=M#1*BO(wy7`#u`|vB7ZjeLA&D7E3|=Y@vVsBbY)|-je5Nv#rZ(uZ`Y0-V z-@c{vM6%mF?zGZ|Gkh0}9OmhNx-tFucAIri3w8SAZ4YcJP&;rex!?BN(^s>;M0PFL zn093f!*0+jGD!bOfk6tiE#SUBgac2+S%5j!JaGs6i{Mn%bhTXF$f zj72F9v4RQP_%kFG?zi8wQt0QbjG1lZpv9b$)?j1*v~p28>xty3Zie3AKhF!%Bvm`(>BXO)5Tnvt&{;&XO8H5;U zc-PEQNWqom%2dXs#f(8=?AtV%-kf46zTNO=&k`*cxx-bP8+Zh#^fK+za+t>+#d0F~ z)*3dCsSI8zYiAp$_r=Vfm*df3iEMuuYl9|ZP!vN9Xq}zFtTp$Jse#sU#CbKEasaIsaqu&B=;S$($-f~L6ac3h?r=5ky2`L_C*v+7hq|~=KPEhH zbxC9M=mzgfI&XVdE}@H|Qs?)oNExAoum66(|1Z?eVj+T*tPCbG9oQbV_N`-sW-+L{ zvUE1XEg=RwP6G|WgwqW-796;6%R!elD4cUy7vt}Od~c@f`wK%AW<;|-`8{p^dYgSI za~FiLp7`JpE2?l_zS8c*<9lm3Jf<*cf4`NzUVK&dmC37KhfQa>p{~Rz2O5cmBrpR; zp@a!c2c|GIi-0DIHNguNlXF3AB?hlB&;Y9<;{?HSiQts_-UzZ#J?eaRWU72^{_mjfBaONK>4OKGtH~)UO zdw!-z!_jFhH`Y&LsCHL5uvk{;K_Da^K+8-gfYv91CJI1}u>ei7W(`xq7z7y>CN?|+&7vBlGwuM5hTJ!B5M)@V#ZV2Jg1k@+TKAR9 zn2^t6;M$=3i=a!x_$q{dKkPE7Q9e zY~>HsP$f=M42shSr?vam~xge_p-jFfG}fG z^plSY{n@%qC9VxN{!ex)^jBBTd3}6`CgYXlFS8+W4(YEf6lYi$#t_W#;~L{L9_A8G zhU=i!Ck(})MFTT#Gq^Cc+yYhUG3=n3G_eMM&=`go;{jt;~r`Sq)i(8!W#z#Jk%aS;hM*&-%htHl^+-mC8y9pyhT48$ZpN^t>aF`+_dx zr-*Q+SMRTUeO)Yi-azHR#HlP62b3A*=4&$E-~n}AK&x#S9yNoa)0TY!FXMzuY!7BJ zKgeRf5DJ>8yDr0Um+ir7rh>Cf9i0tUQ@EM}raGL{X`GYGqL*})&6l%X#xlU2?S*L5 zr-(_1|9^XXd$+V%&I{Y=XSXqImU8f1zTZgYz+@iI0uJPUV?NUXa}EPnhAWaRH^QVF z)-efyE10vO<=HbfGsLWA_$$D;bCH5OM@t%KhM>SCmX-3)g}E)8dAzSFUYp2h$Ek4Q zy2wKnri9nm)?WV1SQ6c^j%~v6JykV#7p+U4zqq1Gl~GKi?}JB!?L>wa=&mLKhOMj) zYe0($p5!pSD3^A)n+wWKn?(;yV^9>lptjU$K?-x@G?r~T9e>MDO*kaMwfBVVJ2Cefd2Nf%)7%KBles&FKKJhrtcGy8*0A53M~ z$#md4Tf%y#EvydvpD{ey#%N;76A;e0BZ@Ie=zs|0Hl7nl|JZCiFFA>6>1S3iR!7+& zR>xPzf3-NA`qn(Bl2PrGufF@b&O7$H-R?;Y{%h9NI}0UzZ(zA03NEx67#MmQ7&2D#F5qMo$YTC*mT|*v zh8U&=C(Rme7#x_S#jsgaL4`5qD#JFO0#8QqZ^xHUP-3qMQYc^MaQ7^$?>g5rQw28m za%}v`sG7_4sqCl3L_g5t+p}yF{@b4@_L*n=YUBSCmzH|#uU0t_EIo~({}ZUF0uLtk z%wq6Z$>br(u+Xewk|jf#XoD_;*KGzDrX9B!g4!6{Rxu>4W%y>ppv(B?ErSxHkKh8s zR)^ayERHK$6;jt7P}nB5Wv`^-Hl>SG7}qH_?ek?a`&9Q+@(+#lg6B+hpG3YYoNM7)-NmAGvv>+v| z(O+c=pW~UkRRK!UMNy)+CNN4>?zU;WwkGm&Hs^%rdu@(id0hSZ&d%cab?b`#%~TG^ z98GLvx$v6NmFZ1jY_Pqa0^A+RC03!|qGxi03q7B&+32V3+Ch0O7i95_|eziaR?WWUu`YYDe_ODhs z5GcqQkZ!A>*aD7xwNl{*O-9hlp=Ji}>x|RF7-vW`o(W?Bt+!&DwU)t-Ga-+C!)it! z!Gu!`(*-h`mmO`$ZgadllVzoeu6Rz=VLC7mG^24{ron6*ArtjMN z`1^M^Bs#yZT&;4oKIHno^8q%V8yyN=O84fhV?Q&MVGEOh6Sz)(tOCkmIiQ@rft}GT zkaYp8!#l470iq4I0vB>vCxABo2uxzAGFY&NV@otsj^KjS1~+fUXr3J#QXRE+HZ3@5 z)~uE;eq}1RsnE(RSCW4800;O(O{C4`SBsjg91qA1mQl}$&k2gh2!E!r$WtC-K6YPhh=vGjKIdLu=KJY+A82*HZ|whhi!9;_wLU$fjn^zUXd-US)UZ2EcR7ts5+Np{c_3V zbFa_D@JH;DY^Vg++6)W~ObhN=9k^h|aNp~|vUbL{D8`u8j7vd9`x(*3H17s)mJG=W zS}a9{oGjB_R%o*aJr{ivWOO`Xg0rCWRi?7i27k6E=lC~V6mq!5c%}G(V<~TqTl%-O z24@)|g;WiNP^dK^k4-Ua2m-}zgDua7w^|JG+720}3v?OJyk&@DI^xnWjWI2?L6^m6 zi_C|MEh-!*8|PeW5}7q8`9e#`2~ifyt4s@DHC*}0xY3KTx5lp0?^GGX*NVT(cJbBa zr9TUBxXDvcaNM5X!J8q9kwbB@HbdC@xVx+j3=*urcG^00GfWpSNN4)-jQNJrfpu&d zK`cx7SVR~fOlH`(mSGpy0WrpXWemEYk|dg?MT=<*k3nmLH`k08U5wJfQY{{WT#A+x zF2@NhE@^X=Epm17k!w+D^ym4};*epwFkf_`ZSa9!LWprp<3CY3$%7uj6pM-(V3YsSk)m%u3;L(zA}brrWr~HrZ8wK z9hk_1`cQ?vW7`Ph0%)@WKAV#8Eh1{L>8;ZS|$xi zt_USiEp8|HU^1gx6web5Ch?V%6v8Yg+?o`y-BvN&$i!B{P=zDLN#R=5gGr1FC7D*v zW~g8qUm?wlaFk5}BM3gLxwLr@vKs%IrK_0GU zW-MOK5F>QpDkwvJi)om^@gbDuM@mEg1R0NstShIo?t9fZ=UVd&7m1b_4auGvM$8Ap zbth;sd$n;bQ|LdxHhTN1od^W?&`2oF(?ja%n~zWDzjAOo%Nv2pd~Lad0#F4xLNdg_*#bIRbdUytF#z`nciId zc%FFy7u0SES4M*l&?d$Ws-Sk;Jk|g!fepNz6O=*iG;hYq6J#>HM4p6+EZHJI>Z><>5I1uz z0|&HTObB|$U~-jhf)US#m!O`kuV+Ju;DaoFgXgR_jy!2t-K3zxb!&yvirbR{+zqaX zaw$e9zUac8r3a3M+bW!Roc;FJR_k*Xk9)YUeEq07cja1!?$y~Z53kZ{_$!^o zssQRcFfcesGbPM;&afl|v}^H{PQxXTpVoyn#Be?kW>jK~Q92+amT`(<_5>M&qYkl- z4_3)#teUidca{KWDVuWJEzYDMh3&ZtDy%h=4ybZH$-Vmdp$P)FJDs!#af%xuzbXY^q7QuE6-=OEMg|6kV{3UAxG+o!VLY;p@kt2d6h0Q8NM4Ca z40eJFLI+ftoQw{LFzFczOk&FAYMPOJyx=9aCKOtbwG@1n}~-X^SZ3YAkPU(TtP=1HFG;! zMIt9Cvlm5k2DS73YH>Js;NXp2PL*d?zyII&YSzsd?h2O$TnYV0v=~@mEsieGt~Q2X z29sFUfJ%jrP3SC4+t8tD|Ba=BJOEqDIsKQb% zh2^3Ou1qs-Gdk%q?%T#NpMxWmb%xOaF(%EaOdC%%ZcBB%U9|9QSL+0Y6DuW7WU6v( z72dIm#YvQT<)i~sm_FVARQFY;Dj-&W{{rJ`F$R!_!0i+jt_HK)3_;P1HK7bE*D`FZ zWU%8DSjE<{m9@pi!H4aJlY$DH(^O{FO$ut9zjV283Go>^PS9dY3T57Ss&URwrZQ>A zQ@fnz%LqIvtUW3ke&@!`oq`WK=g!>B0M9BJ%o_2WFN7EthJnIuHE0l_OuNCK>4q_5 zjnV;gX@wBpgsz5w&<0KB7?5>AE{*;IKU5mmcP-!yQgz;+;^HB&WQ){}Cl03!4o&&V zv`nUhSB~eb)CHphBCMZwUj6(q{nf0WK1WY>Jr?1%e%Q-UCVC(l$@UYgm^!wCmL*@~ z3J7O<62`nEjd_Pl!)h%BSGFY`jUj;-QbZj+w3dX(PuXTB%YeBCW!+o8G2yKTo zTngoK4t*>db3vXH0(I|aNMuBbwYWH5KH}mO>y@VR;3fG%jLr3L$9AbA8Zr+G36-J(TR;(48aUG)68Hs#-ZB`MbV72CNZr1%uv(J_;x45 z(#?!stC*aO6jWH9OjLPt94BarzuIDCqN^0d;y8Cz<^Cc9R8U!xIGj2#b zJ4=s&;eduQM`6M`HlLZi0+X1RaTsuhGT$&-;OE_tZLwgr(t+)I7p_VzaBcACII_fH zP3!?x_L@Zsp;9KQf=TS9pOcI8{9ol}Z@zU{XmcQ;pvwZmV^XVmJdCVJbgT`rInKi^_FheW8Un-1ko}l%gYor{!8zyBn z$Z$RoV$=*}+{R+y+OXP6A)IYTi$j-;$5d9;OA2Kk=J(})@3I8NQ}se9v@vN zkyT7JP6wtiDk>%j3$dtfTHvCxgYS=T&E6}wFSyNiSt;Q$)Bi!Xu193EM09jUzS@UG zFQ(7?uO_=bW7rc>xa!f$uEndf-zu**Vx@q6(#Q4c<%{f)6G$M6nzJwM&+qXqX^ikm}&U zapZ&`+tNn|)CFqf&2^X-uYJ|D>^A2aEvES*9Jy*9Gffk?nE&4YRC@pOyzh4wtXnw$ z`punu0qZtDuK@3BU&E{d9qsUC;8_K#C6@bd}MtD8(_haDvmz zwa5S3Tmq_DM1Vd>TdCHEY>raEMNNLW*Jpk{U!>yP*6 zt|%P1ETXQ__wvmg<^{JI_|~jDJ(C$)&;+nv$OFe`io*hbmK|-Zw?Y{W6(=Y&J1yyI zS~k(SVs8EYC*QBRw7Ph)I%Xbq$a&YWGwh-mQ|U9VB^^v(4ei&hTRi{vO-qh~#Plq_ zhCF7Dwai1molbsyt8jU%h^B)w+fM*FXB%HF?$R*Q!@dx7)JA({;~khKyaJ z3*Ol@Y+J?X^qH~9#lbVx;nkmp8x03VlICyz^4Y9b+AAYi?8p%Z;arxLO%5sznruPl zLO)s>f8FU{k^LfEeigJ{_^tpfZ*pv7++r(W5YA+BQ7Ax* zDMplWUt{A=BZbmBhc!_OQ>UDd{3oOVM289sl@G=I8XIc%8TbQ1BG3YYY2q~yB)d($cVR;h9 zHYJ4f2?vw>#`AZtTwnQnt9M4Um`S8&fKjr73hS4H_Mt*AzK1hC2p8GE!osz%rgX0E zY=&oLpV&YLA;4X3!O^fRiZMvlA^0l8&Rj-AL4iq(8&5S{`N^m$%51r$=HIgYX>-@# zK9;*CI&y**s~0=}F%?HY??z4L8u`WR7M?d^IS?#*T!QHUXzSkrySaOO!C?Yge8|B0 z0Cdnm#xB(b>o`7~zL^eX7aoDwCn+gQ{r1rw)GI zjWug_wb?~~bUM0ORGo!|p_zeG5xkVoa|Ua`cF_Z>j835pcAOJ7sVXd0RZw9HN@H0P z#=0Y=!Jnf=i?xWwku$gM?v3YplgwJ&e0WTDUNhX9bzlmU>RYyFw{;mmZ1G~yd9{HP z+<}(JtNW~6fBEkB(D3s~`<=dt*L|Ip!{_nzs^L1{XAYCEKPj9r`E^?HKOeqzCl%Ga zbJu+@%Fq%&^K65L_8ghKqC_kACq)-|FUv%p2sAr=X~yO^$84s(PsvmKyliKtn)vUT zfAfuZ{5baRPs}v`jSG(R20qh|H|%Dem3rMe!tki;_47#&jn0-{*4g1v{MJv;WaqNo z1+!K!-;vP8Djp-6x^TmnQySdo>fiH->FrHYe|~#Uy4m`Z3CH$Ve>dsoidP=xt%hnYBw#;yd}b_xmhacx|P|^bq1xii5^lROb=YUg0&exsPr>zFlSU? z;85gXXlRuEXTOH;|MOVeqpR2D#j)^&u<-=3#904QN_hFe^}xf!3QV^qG1iJP{;R*e zh$ABJk;M=FZtsSlj+_O1!?R`x9l=j72lnJJ8mKZpyUldqfI7o6*M@MWn0VPY>-kSGL^CbvXjuJQi77{NL&3^W zNr5R$@8_mXRAd(WdE~&)v!@#F9jwc=^HzGnyz8#mMU`BxW*N;1+ALn%1x$3gwHQA< z@?x;D4P;odj>&~V0K7)lr#S zbQ!0ysx(-=$Uf!b5NUdV$(!kn5Tm!Uk%BYJ45I`u7M7L+IzEyM3cVX$z0)6wFnX`| zNZa`9)>56q3(pSjmp`Q7&U0jm!#lU82{IqHO0=*v`HQW{eo-vTl+c;fsLJp~4ceD9 zSaJBj-SgPc!mkfp4jkFWZlIo!<8pvw8~c;Lx7%(pGYKtd_h!<8M%&zQrVPOeVZ0lH znKHUu9UM~~`nlI_NonABW(m=;vOg5$dzk$<_ov9DgnTav6W`9JIjb5cNO;_|OW3bl z>g;$*%6Xv>gMOfm`$wLm8`~IWycS^iZOL-rF{B)ExX<@L{Hy@uZw02db+!osN)6u{ z6jyRZ+^eZR5c9RN`b#j&7rtMNoEvh~jaeEz9hlDWdo{e?Ba~p4d6gkhu)uKI~|5@5Xk)Ac~>PNfG@%QCU>-d~VmRC~0_INSam^(APc`V4#@5mtF^h*tV z?i#B^`MsrKf8Q?VyHOdlnC~m+pH;K}a?P~6v-)r;Uu64_>t|A)IsV*^rY12&^v8Qb6?XzQMoDpsujXtzg!%RZM@JnQGTE8h7OI zb@1_iStnSsl|3Tp!J-pu7><89AZMEx;K{gnQi7LK%+xkjCb3w9$*qz)S}BE!-dEnf zFHD@1U;8>**|pPUg*NxBh}s#S4DBKVI2)!*F+JGf#h_Ed(ZH`0eoy_ zz~c}9S-y7t6<%fZUv8#S!hAs?#^3#|96R#9xE{E4u)E=KyCX|aK()gvj*K<732vbd zQPmFHI5@l*=5y>==fDzU+}x@tS+G!(Sx3;|@#FQUgqStkzb6VXUN%bTVQ~;HQrNzC z!OE}g3%|EslZj$_FjJB#;r**ywdL&$H$)*#Y?G27+cSMx_Nj8^(f+cH(*Ibn|SS_lc$LJKwc+j*dP_Ur3Ak5((m!mEdlhFgGV78dM zGk7AN94(!&U#p+{u8v+x=dXLyVoYY|*Z;n)>>Lbg>MvN?%eHhX+bgMGp`1()3Ls-P z1}yFWtCSKByk)EvVQikj$nG0ZeIRiQdrwZagYsr37nU8*7*)=37I-!6%4U%kWxO7x zq~OXH^Y}rQy`o@(+@;BUKf?Vt@A&@w+;`n|_sq_==1mhRJ}V;b`*H63nsk*6C%z}= zWHYKJH`dL6|2Wpj;h&r!!{=4m9}ch1{v^Y=ft#rTyxd{I(!>9C1Pks}oabC%+hEDK zh22D*NlIF3#XkG(91#-@bPS4n6LbX&YIy}GXfW&boNTBud{BM$-gzHEfhC242CB@z zDm(taE8jocH`a2363dtS-swC$QXJnIIER-u{M)U_P`7XyLxm2c3|>+Bp{US=3jQ|3ogE8RAQW?l;Bz`xIssU@l{8|p3o-aREJ*&>z@4< zzbe4o-DKh7aP*OgUeV?y4U?QJWA}aE`(8EJUEoTlq5=QEjgrjI8uz6N4P!1?g&x4NOl$$b9*YW^#aLcqCsgOVrHneUzrEP?`47&l+zFjHh&7s_7T``DoVqUph|2G>@ngg~?4`1C?9 z@z~&K7M{lo_j6i45Mo@I)o8{hbnF&?&~^DJrUx#N2*2?D@c;IwAL^$byxM!RVd89I zgN>n#YXs+Pv1B}aLGHq2hSNp=bDLOZgo-{8U_8yHz_jTZOH8x|vrNzfrODalNgWKc zBcu;5>Mq(@HtVQfT4%#e9nS^5%{%+w*Z=?BJ3*Vx>oy15-n|Rj7;eZzQ+VYM`|qn} zvZR2j6$AB#Zw${^zAU#*h+;5NWr`6>h+^5n_ugD()1pd2gY4FXq)-PJ4jrC{!i>ir z*34>uLA5mUTqQh!O|FMIw8pN!ZHhJXXbFpkNilM3vvgf9ItWo z-IqETW#mwMH&Q7fvRWx2?y^!sWGrZk@%qz~4DAve-3*gor)@l;%2Jck(J+(!=j*B~ zf{c2SADZR=BrM5T<#k{hv*lFgrP@5+s*cMTELQAtyDtv$$`zYG@0arZoaHINxJGb^ zEvO<8WwhliXpUx^62|&N#i3txv+IFPY_1K?Ojp)&&Dd+J;LG(!=ll8TjNMH?mNvZK z@b3Ap%xOm=_6bktkT7Mu(4w)zIC;U!t*w@C*&rh&2?yH$rwA70br&;b@Gr1!Fm0NA zSI)qhi6@F>O~zIw1y7cWRZK1{8Hc7aZDu`qNZ_>3s`V39m<}~9FcnNV$#52QMAX8B z8(JJSn-;u#<+OXNf~y#`VWepJC;emt=X_y@YKFIrYXp5JGk#TZaDL0^$hjey<;-kV z#;mS}RNgb31!1f)_TFquxEq-SH>~O7n4$Fa!h5A~mOn4t?N24e9CheY(U@zP(A_x4 zi-{2uGCU6-&i@9=N_o8xey$Om)A5aAIg7?>t`qCnBo=V2P-e~%d~kRgBX=^3-StHw zX`Kx0r=4T$&rXsMkH7!-V}Jb|mvtH$O^K5@RHZqbniSkspl-JN;8*(N{^vQXtEPA~ zSSdA_Hh}^@p~dBZ!4`HFRfn_R8bmk~PBetMI?P?g&@K?MV~wh$!Q=aJ_RX%2f07hx zUg(Q6{%ZI?dH$axr#Emksx5KYuf;qoW^Mc^t|`j6g)ds*Zog^~~F1)2UP1bkZX(vs=f!?^wS zU*12Jl7a-rmZ~57FC}boWyoeJzQw$5_MC4GOSiD!FiOz5#-ZlfAXUs1l*SyPlyGlT zkYIuHE{+W;2byFgC#W+0XU0K0(LX`3JFAXLg#ens;3wj=C z|1jqJsGYW9hk)e?_0tV<+RIJ~GAz9!%Kle~$I@tlAGhP1$8XlRLINeq>d*U!wtx6N zST4vpR3BK;-LQ0xpo~Yg!!{NT0meX1feikLYX#K@KJDN+A;j3mBcUodL7(}TG6S0; z^Q%*hA(9F5cbwaQO*y=e8(QdGdU!wOypyECzqgFneSE4JvRT~0<@`U>CRd>a3v3s( zalEJwZAfbL?c!MxvBSnGTIs(ZMv`;Kg$-m(QfrSaZWF#WID|TEWI3^h%iy1{XSRc^;D!V0%+sz0vDDu6PQe08`@nJ^~^HE zS?&~{w`ESBvWh+E7F&>tqnC8kzgZj%PA5)qHI%--R?67c8h7*m&G4U=$Is6TTvxW~ z`p&0sWFOhsPc~3bxN?g*P3F;g|K|z%*B;hpZa!o2BR*lX(usvDV=N{gp0p!T%$V=H z_|rlczE=WkEH|;a-_+Uu;+u^%=$f8dW5q!e19_CBEf|J|#K@t5au zWW=$3iDud0wqdVn(_%FyKEAGo*X>>`S0*yLi=1k>=Hg&~onu;8LuzhkgXS{6Z@e80 zXO9ZX7$qzdkdPDHuwofU2WNxhByAQi7RP8~9xs;0_n)D;n~nefWHqJ;Ws90n#-&OR z7K8{IxPVy~1ceygw)hIGk2;kiRhf_l}K)f*0PeYDg4g zSbCKscZ~;>bw;k$72J z=t(iu*LM#8j|_jOqQSH-$Cu@eVGgLb!Z}~KK(?64ZKv&m`PnSeVvJk2v$rg1_`V?C znJp$}yVinA!3!zX4!4;SWKs?+Fjs=c&ZWcqeR)A%%Vx>sm)QTUVPceWhIfNtHowPI z#?M|mejM`%b(q1r?_r&)(t}4Vf*WcIlorfqO1RYG7;FTs7fcF&$b(8KaD}tP*1@~M zv7^CKx1*s_{Qi|bjvYUa%~E=B?AaL`=TJ@RF;CS2EiPD0l^E?cxJp;@@9#Mkx75%cvp<6k@>Ic(HZPd9wf z{$ajVi8)B+zy@xH6zE!r6IOrtgIEmI8%&$dZUyy5C*5ZDay?+NmAxg!VePjDr#7<#kqh%E7x>}Iz4aDcfsRlo_EqFi31k zk(kQZ0ml();xeM*z8WIH<?NK2iiz*HNdb-mlNU$Mh0fPKklvv9G3P-8mvLfL{(=|RFZj%RBHeWoxx-r-ho#q~hbiNyMcs_#E_Y?ZBoGn3CDse=nwb7e%bbTCMma~iBMf+QRUo`VnT zLqLs*+bg&ddKI!+)GK-umT}BzF>P9GtFGY7mceLnPANg+XrtYobb@7{jwE-H>1P>N?nzTK=Kpa$y>Pl0u}}(X!2&W! z#3+=px4R)z@+iF zFW3tju)Sqmywz<(3|o(T^??sv4T&NQe+5`Tn*%{B_nIyK-0oSxA<=Kzw1u5#l~x0Y z_PI8PL6<2{>h-sVAkdb?DP0YLA`HzU9FVNR$lxpcPgd!{MP>m;S0h;WE<6c`i^LVayXA92zV5Uy*naU! zMv{r)N#CTR;Jj03eHYz$(WSt}@kUQ)@ArGNOCqc~8bnxK95*U*d9gcIy-!WNc}Q<} z$+TyC_uJ3A|9#HQnU==I=gzDw-uHaY`Oj6gar=LJ`d`pR$Nd=7_dhHD&2y$ed; zGWPFYWXq7v@-vhFLK|b1BEwro1_t#6Mh1pfCq@Q==O|mrbkQFfbfofCL@`!#uk`+RhEK+nF}F2^50}u$5a) zo31%HXt1sDn4U22(#-psi4Nhl5)Y<42VGzb3HEbFEwcZF7t8%Ky~lN64#OJ3Cl$R4 zx0pA8y!kGh|3|OpcBV(njQ%rO82xJQfzFZuhx@}PAM|@O1r8KeGdyES@ME~eT#?Uz zK+5s_TEQs^2iE82@^^e+%TW84F|NK2w4n`@Di|0p7XR><74GpAn9yR$2=>()!6Tq3 zu&DE6xc`>1qVJ(RGbkjv7#coQ|1f7|x*+%9)HepOsxzD)3aStAY+*0pVyu|HT) z*j{4ZvoMA2vPqfN3(qTu{~ei8sS!0x`*~vMOp%H5TQAIaU!?lvgT19n|En|oe>0b= z1|RvO5*Bs(xgoP4|F<^9(ZjHlw%p>GT}=Oy~D-hd{=mhQv#OxAaPPhMdb=IAhMs(`^c z0hTF+IxId6EHgS7KejcvmpUFuDHTXyWIH0wB==mB>DEkc#*KytngtrZH8_0mNtTyb z@%K6;*)$ZC{@5RmTwGO z*b6SpC5W>uux*$e&8W6c@W^If1_p)(Cs53G{CQZv^2-N%H&IXyDQ3DL*YJ&Dx#NMy zaXHXwsbcCh25{eU3zoXI`h}6w~y-2cRZC|`FwZwil@=>0ejCp4JrMh z^y|Id`wsTNHG(eH4RRa@iuvLGm-GAYdVf~g4|8TvJ`3ZR(O?NGa^B2gcz7UozU;o= zI=|Y|YWFsWZFc^Bat_n3%GcA@-oJk|eBH&Dcbsn38qL1;&i>?vhYHQj?=vs_e!b%B znyDexZ`!|{XZ(D$Sr=UF+gE%8B@%GAh zNEdZ(;(zg;$KrzAgFlvxS%wVf?ckwbVR7=j$%m&qL%dd9oT#;=e;W(a1-S+5Y#ny& z`n{f!+vMr}k7pd>=Utnb?zG8ZhU3QIiLO81|8U=5`Q`e(;+N-b)4JPDA2LS1STDx+ zBQ6${GX4sDkSPtKIsW%xFI<(cm&AV%RH+?)%UGXOab@ma z`LKi#=ZPno+_JM770Wng$TUFn+r-rrAHqc*nORhXX5 z@^K$14}jyGfk8l)Ny}`(60Nl>Y7Q$tI(UcoE#n?128ILwViNZY{`+^Na@PO1a#`lp zqJP(Zw~kPL@!%W7!rUjh-=beTiGTTI{i?X+E!$h`xynq@YRqPd_Z)gBdLJ<@iBQhS zuV%P69~7|Q9LK%V0d60`P22w!)eV|D({7VY0t}F zY}-)G@gsA#=9F`{Y_Dz5ejWGr<#W?RZ@D+lO*ro<$YP+b@b4{Sg*>Pv0oUjZ3>Wwq zMQ^TYSe3h?X0c*~a)uHc1H+t&b8}|>njsp!;?f1z4E_geY#(g>c>0L6OvU_9^LGhP zTWEWr?Hj|M?;!Vq%RL5$3=b7HmAw~c1Z90+vgu_8zX2B`1H<#DH-2`Td|K?=7{<6p za0@$AoW!Sj7FYP+dp>uWA=+Tt^syb;ln+J>Avr6U85iB*T5+#WYe}C&b%P%V1A~Qi zG~fKCX8G$y_}4@zOU(b)U|%+0mv6!01pDW%7ClM534flmJlG3LjPQgInCa8P@G$H^ zw`9n(t9Db84r)1?uNQp7z);|NE>>sJ!_#J6{10acC){1za4gt<>6}AJd!Eb8XnLfr zsLis`&7iKB>BB!z5ebWDF2-f|xH4QiuUZHFyfQIDc?A;_1H-#hPqVLQzM6jfX8zrK zhvqN{D>_(A*}FSW|6VfRxr3W41sN8|F<+2NU_ltxkjhZ-`&+Kl4?h(KlT9ze7_#|S zT$yv~>RWZW5_u2V>?|j~ADIs&m(Ot7>dEgrsnJE|k2L$`hjT+K;&&Mw4PSqGhyV6X z%%^zP2%6lG3n=~)WL3D3;k|Zy`rXFMZx-0wrqwC>9%-y^OR8IGCaWy``-5Gu*?&b} z_1udk6VexCs8sy_D9^ya)yl-6V8+;B`j+ve=w*$SY3ZvjRP;PnRJfSJ!N9P?`sbTl z#m_6_yV)z(2wvf1l)d@qK&kT%Zcept684vuv^!H}j8yVa4%dY(vWO&QC(KzX++Me>5DVgzg z>p2-x3>%6$44N4CZxdif3HbzLmJK$yXWZp_;q`RQ9?=QM>Z=(T4p{Fz`pZVWW?QJu zohn|YEeQ<k*FI}iaPZ;ya6;;!`Ypf3{@Y{^{yx&0dG=cBzd5pZ>hlB` zdmI|VZ=<^JfFjF=Jr`OgWq)0;>E+`&tc+}+B4>}a%7&xS@h%Dt*PpZK+^rFK&p&l{oB6ZFgE7SG?vJW+_LBC$6?e48fYd)9JyiwXbs+}#qu!oYIm zX&A%3dQdz}rRtIsVwbUOUw^Y@=*&YYUe*zm)F;TmM~Ed#ii`C!4YWU8A! z%aS5yaJt>-#;!Hr?AFF?mfOq>0-k+yPA|V-UcFm{@eV)Z>hJT8%-_xa>FvMZHD^4E%X$%ccbNCo_&Mys4I;$6=SKFhk zz`)ROBK7aV%U>pkO->b1xGY-C^zYVh*+NmJe{1$m)YRU7nyDd>_%dJTRZVG>O4K zn?>6Hw@CdqIq%hx@hjLEmTzPKz{6O+cp^&HlLl z-{+T{WgqIDx)}Hx8O+xR9{H{T&uMXN3}!0+J-Gr0Zm(@{n-a2CcDJa(nQ5j?3=A&5 zEzXN&7>*ocs8ev*!T4a={eRr;U)H*B&iJ>>)}-d9Th_;G&qH#L2>nVvfAh@&tGT>| zbA+tdKK(4wUEuKV{JYIZ4;NoG7Z1M6d3xo|p7Sd{?^&Or@OtI>KRMdE5!+^mjy znn^tl4c9o>ukQa^0zTqLA&Xe&ui@%$_Sv+-jU$?oC-xHBzsltbkKGGI! zuXp7s_&JAR_buiRe>oW#7{Jvj1H%C(jsmZ0ha3)%x^050`>u4JTH3cohKYfpW19BU z`b2hXQ;q}4)eQwNN^WJ>O_H5-ymr^rIGJ4@HW%z~U79Yxea@OkALKh3tMXZG(iH4P zFQu(YYB~JW{@2;X;m2wY3)vh!l-0>k>w}3Umtz8=B8`*w|Ps1#kpP_I~%?3 z-yg31dqcSyY__stEW!c@!B|MzU+Z73Jr6!S&nbMzv%P7UH5jBc8hJASH-Bk zNcNYg;)11R96vV33M2?HPf?Qj^8Ze8p9Qxg!!9}Q>j9f*&Y#M3AcRlh!0c?6k8eP2 z9dK+nv`_vLlU%kTUnZTyqnMYaV>OHR_9atx{k;A?@?4Cwc5MuT8BB4+6Lym|EBPs4vtN~ zIQz7DklpN{cU#VXocTxT*Tf3$gd`8SE0dpHG}26y>k>0NoOtk8ZOp#5^qbilXC`yV z{D}56R#obM)$>F8-?b>?sj@FZm-N=iU7mU1;XK)OTb~tf%@m)d?qn2OTBqh)o%?fV zPTG+(li5CVXDadiF&6#oz4fJpWFi~GyxJf9kU_EuJD40)*b1)5Jy78gP}7#OS#9XJ__x_>Z>B^v#G zzk9FjN9lS~i3AR&sLb{Td1j_7$qkwU26Ll1E_5>@cXv-Se5iRmgX4iD$A&LqpbBfP z?bKX@D}f9NRxA;%jJ`ehxj5Pz_J6Hx-*>0>j^X^L&XGSK9kh{Rx>2kBVE$k658rpq zJ-z(?#>4vynjg7)F(fFm+$hw3V7OR9z@4Mw4i86$xID*?ol6B0?yW^hC8Z1#+(cO{ zmfAKrb0m0H?bB4f`p#7|Q-;~$mPNczQ-cb-qk{z-hcQ#vjeEuFHS>aRZfoZ9RsVHJ zk)>qj{#mQ9Yd^n~`iuFt&_nw_-iyVVY_7^BNV6=kJ-~s|q4j5Wn0zUtnV~w5b;+(( zwhFC(Z6~eT9L>nv{+XMhP4R&2TgLkI?Tme!GJo%>pSM5x_jPTPvph--TpTMBtXMRX z4@~l9-&wiV>&Ak$*VT2V+x}VI)-X-XiQyJAXvAcfvLbSyo`FGzjZts5@BuRpm3=pQ zCcXZ$^jondD+4RXhGaj7O}+vOJ~ID{#Q*Cun!48qbTJe*F!XL`@_QQl=vnEkO-0pb z4)2fL&a{VtvEQ=PuYvtGGa{8UFfb^Xu`IAv(0j}HmCZ-+`!4a@HBVy%8GLq%KDcAa z_|(YgI!wj<(9{AfnFZe7Kn-KngevW;xF+)WrXejhY z3xjbUD@xzrq36iMFotU!zZe$$SBa|s&@*GMx?Vt8qT$pjclv(+R&wNJuqgFwIB43$ z#H4e(YR2vJU*6s>KQgtgjwPU_!Qwn;hS}%hytu{Za>NcE)>UH{$_KR_)(CD9M#`?B z8eE>^Lq&B11A{c*+=aYLITTVZKh&|B7V9v5{oM|2h98T?neOay3z(6y|J{MiJp1dl z;_G=C85Rp9u(9pgS)(kzzIQ|0hmB^Jofr;ifX4H(S>~FkqsGG{7RKI`%F4=Q=q}LDMEQgsULEA|G|VIWy&!Yj)J>$8m{bc2Kj@3uBb2{V-U}c8kKIp zCnA@wN}KC4HEitSc<|~QL$APx>TUM>e!SwBwf=gv$>%$L!JjFSkK~ zhhYk6>~MzggJUQqDFXwCw@p54Lhk~TuWUZcdfzNid3NEZ2aknBrFO#mmocZ3ug}V# z&Bu^z*}(AcHgm=8-1p}=^&dIQ*h2det@x$3h^>^c{I%Ge|cXcwn%iupC<+w(0N;7&+Kc)pr388Np znHX}CuKzY~Rc>J85cv21(8KphA48KF9`v(ue0Vjd!T!q(-|6c=U+4OuUv@W^pJ~Qk zw+}BHxp!9B-a7Zsh=IXP@jxGRq#Zr+Hh6J7_z~9NeT{>S;efm)heb`;)j14`xBKQU zH~#5b@;Iz>TRVT*hwtu^Og2{P4vD=F4lI4W^LI`E@08U$wrz+Bj`wM0;4Nl)<^QLn z`}*Qjv%QrV9HiO6(*|?w(UR94W=6hzegOsti4WT&yBL-VBs>Xg`0!r-)T!m(;p;Nl z8aOx#3ac9?S~4DDJaJ+zx3*GFuK?o#`Pm8!Zk{>6v%dShXQ%rLDMMH*9qnv ze?A8#&@ve^qNJoj$)fRFgSiAlj^!-XWBiP>eOnk54|VNW(|xpL^%56hP{UJy3p-Ez zzRuO>w{OtVmCo`q3GBEb*T(95v@u6;Ycdy)3_=Jxtb9%w4~d<(T;aj<2X(Zv{{ zEHTfV%`dh6e=#FVKnsIxF_Tgr>xTXRPWKHAV}ljPhqUU3f(VUWRdZLL{~L?t_jTzhT7Y1{IhyvRjJ6paOueJ zFwn$N?}EE;k?Twbh6FyAgx&>-90Cjvtj%`*x7K7@(ZI0zHuEHH&>&*2#?E^R9}VR7 zISNkCVR&fzB$Kb9AmU;VgW5KBpTz9~Z|Y=Q6lWAMB#1-i0dKLQnl7Nu;ZW_M!;^7< z!6D-Ac3&5UN)Cg~who+3ljdg%cyRqT_NZBEy>yA>9?=I2Eg5AkB&yVXLF4FV$xRY_ z1rk`8)7Ib2{heK)^M!@4Yf@U$7IR|%Ss;$moUVK~rzHe%MVBv!TswhqE9 z9*hbyOjqu3b+9o;<*{;@GaRu|_I37Jb#2~VE{Q{Y-;yuL39vG?wEvgh?zQ9R9REKx z(F_brI>lM`>`j~gcD-ZV$EiUKGcsgA(|W~BGN^HH!_RQZr1$e$hAr$KmlHmO+&ahi z=k{R%1rA2G+sq+O4h#%SCMGfbHuhh0b@E*OOOF3J3m*03z)NS~6B$;FAs z7FOC-O=9*8pZ~l^&DSz9<3h#TSdNB^n2cu1sH>r1F-@ zz3HaT*+fPbr_CEpPKruvJ)fBnBoU<&`rbp6x9u|Xn+I!E72AK8?Cc5mS#(Zi<++v) zmGbLZJTzkV~fh3l-*%|!dEHBAj0bBvkz*B_4mbw-%~FaOE3NgES-7pUK2 zzO%ti;Jn=*c7_HgP{*2=L7}n1>s@n$imiYEJC6WE0y}eqtP)cUM^Xb13ya}{JvwX> zjSVXp6qp&4v`m*D39sIK_@l*S!vlUC66>Xz>Mo~>h0p)G|JyuqVTK;31|c?{=xr0b zuRpl@@$`#>X$SBBvrl1VO8`xrTC^jVH4F?J1ej(qF_}0tFfdr(+-0QbCGcUd9>;{^ z4Fw!GWEdLUmS(PyWDb%@QMvZQMRVoi9qkP+-HH!>v^wS4y85XXic zwh9~!zZ4xjlAb=B@+HdtBZDkIhsOg(CKU;T{I_v=cCVf7?|7sxVGwZVc(EzLRDAux zbn8A>rUqw4hHRFNZUOozWr07l!{N zZNKiuH1Yddj(fK=$ylf_xam{(j6vsakp~My{Uc7svlcVk3pW0kRk0x4g<%W(k#s)? z?KOg1Hrt{$HF}&u6N8NnR}NmzyuNZyLWpBSPX1gM>b;|7P3%~71y~ub#5Ii?7us|NIfL6@l?t%JT2FDjT{4brZIL0DEWc9ZO}o&7zRd}`&<{87@U_~o_E=j zk!3HV4gJ(!qB0=r&C6LB!nUzKfH*S z{m5;Be>1ot)Aak$+RF?A2@*_ac8X3o#*m43c;;gyzw$k47)%{S!Nr1LT|zv zGqe^y+#?{{CUM#}Yt=`IQl;R+1dEPHpOL{Ehr z!;(T{mJj#k6c`!|*2a41a0DD-5LR%IV>o00>Z`wbKRaZxu&Q~*<-lx~*86q$b#|UT z`us1qS--;4k1TBr!jJeE_lmE7`tJAp(|6U6H1C#Sk^oIsw%nHy>a6z*^d+*{mP`fC&OHsf2&d}&XMKURw0(^q2n z5zNJ?S=i8^r97?Rr|ge+zy3aY7{$qWr&60Co8>VlG3 z#_TgsSfEbZ!It61ZMg}bVzi=FZ?sMdb}zUq_u=Y1;h36xpS#w#F*$Is_!x*Y9WrpR z*mC#L6E&9#yBkFjHVns|Ak*`_C_^C(3=XLmwRkzJw!Yi%9Q z&uMs)e%WjCDt_iiw(1TNQV~m4RrlIC{OPsM&t_@8R~={f`Ov~(F-D)JhK%h@E#KAG z-*1)XPrvy7X4L^uZ!}2|T8HmNi7f^OXsN&;$Z(_dBXj!Si)@V5SI=>FT$B@FVNiE; zc)6D0o0ZzNH^=f=d!8+`O*qjmz2oPZ{|k@jq%v&SVVj`mE}%5Mrp$R!-LvD4ECGy+ zch(B#^nrRK63Bxe44~Q4rG6}+(!u;12N#3Fmu~J(R>mM6gK9reG1tPdJC8La*SWKl zeWUY^y#g2htetwN=2>f8O(;VGBa6wbKb^|^#HZJn&-l0bcg|}_L3*JLr62O3k|E=z z0%+{8VV@=A%fo4x76vhJG&ZbJV3Ek>57_Kx@N?%D4UPv}zBOEmW?cVe!ILe9+s<&V zF#A-SXZP86S-soIHwp9Curm5&@eA-ZhUeWWIa2=k>1*@M8=HFHJTRHc=#Ulz%3u-7 z68szokO~V1hRF&H#Y{c-xIE;)iOE`(PgIR>VTj~0;AC2~R#1e|LF%}3!}kdnYlra!(fM7!O?HZeZTtlMAY=Q)$Ithv1oN@&;>6* z%4X?JW|)UOl;`5feTK6`nT3O)UZ~D_57ffX1pWG%=pN|Lf4i`%_z|ufJ&W?bhGL_iN5V zdXp!D(MAsoI2iB6DBq~CU~c#<&djlZi)qu9vz!+$$xZkZv&)F7fR#~g3;Txs`ANNe zQ+^yX@K$_qY^{)<-RJkY77I8Sb{RT2S2q-BJbb$K!tAr|=K^QnjNf$lp2!(ahw6sM zXtVncGN542GEiV(_~+u#P*dG-vzV!dVS=1A)4byIa~KTY)K318C10j};E!pOxL)kD zN5=n8es{n5e;?;6fdg6`5qmx^(B5aSAG_0UbJ+Vu>1=yXJ61D*imR)Rh;U4hWVo}t zykB778?mZD<`zjWD> zw)|OT+73T9KYVDs|J#9?7o6*aOFb{n;!&KLbgJd)d}gs-?{=54OV7Lc|8MD+_`T0I zG5=eVFzfia=BUeu#Gg98Q7#a0vu`?<$#Atj`-5nsu{6yiwo-&SrBMmDU zCcInujX}{fG%Cnx?!PH1AHoi(mF3Q9KflM|v7+a@?SFlB4l%HDMC^S(C3}B(tF-yz zu$+bIZ2Qx7Sxn5?82KZ)7$^I-Hpt&(U<9uYI@{WC+{6J9$e@Z_qPantuM~ zzlo9O0w1F=lR!Z686*FilEL=$Ib!{g3LO3{5KAU`BlCA9ai!7e&p_cDc_${z9#u^XJk@~ zWMiz^&hE2yuk8bM8T5+dRDxf_)nXhK3(`nErUX@t-yyr8(ec9Of}zMoj>i)vajstFD>9VyRg4i zv1!g$c8g5E1N{c-4wq5KD^nQ~_Ma3<1}z|SxIBk}k-={Ys2=h#c(7vKw}$Rnx0om0 z^g8t9`5#7>BX{H!z-u3bUh#^p|9zVAzzqLwMW1)-A6L8P4jNkAz`*>hZnOCPqdL#I z^)EMl{k`&J&-s_>Z_}%)LA_d?xxx+Sr9lH_;KgeU4D1dL-u`De1sD~+Ptv!X?qu*_ zMprar)H4>DE+$4L29IP>mL0jh3ktLw)^_=_!}d~aP0VA z+XtOxmo@uidyjuFG*I3C{z96WNzsi@XTRLJ;QEMBu9)c&Gh~(C~ib zTSi~^Gs`CLbWNI~@L-1=$Aukk222b-jSTu{IX~Rx@>p|kp3p1)R0e}-zjWeGpMCzX z{BGKDhK3*8*tg8v%XR;F;H27@n=Gss{97XWs8JBKBrSu5>BCIW>?CxK^MN79gYRJt z91NANj4T|D4KZ&87Hqe55M*!ymsk0$OG5J8bk*xMzSVyIu=BtBf7U4{_`Y|p5-P`y+ZLa0qc$fUO_k7p){r9kWkpx;m{14Kf2d`#Ml3*}UKVZZmz}R4Wr2C{c z*TII#Re7v0Vw7hbVNmdNX;8amcpy_iK%HmBt-_Wa-*We|Ft+4a%urqAeR1m#*_ze+ ze%1*vh$w)@*BBXJrPcYAzq@zz`{sip@w+`wZ-}_5Jh+$EJ!=BsBQRjXMaU>+M#6<0Q$gtO_Y-YueV^WU!+u0u& zGs0#uLG7JhmAp2FbWnbHkQB(k=%kB*z18 z-`;c9^G#oWf76pd2NzA7e#n6Q1)0Q3 zWms`djfe3KXelQPV}jn?R0ahXV-}6X20o4sHimDG4V#~{*xar%$hDcHD);WH(}7*g z>6zD8#+=gJmGmm{$5u}Hz$Ph%4LfZY{ET*B&{pOV$T9w|{js6r8^ilGf=8-B!?2KL z2kV&|>>C(v=d+r~n}*Bql$%Wm;@GhQl4b-IUbdYKJEGqDT~GPs}tw%VPN?8frrtF|M%Bu)gSw|8BD(s)|DCmcFF$lv-?e( z#}P{47aN;~2F%a~KzyG~s@@bdTDidR?sof!`B@N6jZJ8-%AzNkI>k8|7X?#o#C zZjI>XxW#Pz?H0h$P+Z3_!NFk~3#gy8Vy5r|=7ydh zjt%U$nEzc(V#u{C^6QajdSc+f&5~nT6!&NI-on@0mYuJiEIEJk;eQscEE|}alJ0U< zY&z`o@#}%_Wq)olUy=~vWUp8wSaMy;anF8GaskiHelTI!(%Z+7!QXLsZG!?!2g8A7 z2j*+<&~a*zVRN~|bt3$_+=A5J1P#zQ>K1m6pYxynWoB5w&Lm>^ph5rl`DcL#pJtbY zAAKGtvEey$L*X>X2GO^SPiOlsnHm57P=oceb-9^e!kZX8N#?jRCYU zA9VKiXNC*c;y4aS9dwx0KTmsIqp(m*!|7KF4xG#-XE-OYGq8fD5E!!aSpO`}w7*sK z#pd_gzm~oN3%-UO2>bS3;NPG8X&U#r1o%ogGanxkP_Sb8a78X4`;O5TmLrzGB@Fg1 zT5iga&9XC_|G;laq(a;0mc;_w1GU9WQ3ebr^jI0T3MA~Xbzoy!#N2TC7W0n?Q7Dw8D-2ORwDMtVoV^lt?OE!Om`enHdZ!5O_kki~*t6sqTfU)eu zcXcba4|nB0Tw1|@;%oGmj zVm!3J>_-iw>C9b9EDUq_7!NgWx<9FO({@>b*Gg-6*8X`WBnZm>YXq;fGgiC@RaW4V zfq~(I#1{65t8y2Tc$ga&X)7`P5N>Gjp3b`!G=TAfnIY3Crb=6(?k(g0Jkt)@f18;( z7Kkz}kw_@9sMWZ)M+-!Z6`5gF{CeN5C%81<9ap-VRx&HTy+B_`E*& zDD+RR5>rZhf`bW%L6x=xA5+{bqe+wIXDTc^!`-X7FpS?T$3*MbS_byBoIlRLD}K?u zw*AGqySsnP>wi4q!2ef&{TLY6G%~CaG+AuR@ID-Bc?287_xJbJTi&aL=F9tREwft8 z#J#F*dGEO$(o9i(bKNUluWM|7k)>eiq{mvUIH5phhtu=FntOygnU^|D5anQQWXjy_ zVA#wUKPfIpswY(7!NzY5>9_e7zE#&_Is9!+KDUbXg!d{A8?MS*$|T&ZQJLY(#PdV> zkVDEJjexbq0t`)=C-$-5y2M<{zM1)dMcEI2-zF15_J5pRo*}c3P4ol^k@FI z_XXKm?%vN4i3@N zog1up1Q-1e9wlaIwS$wKWtAaePQ_&|xTGVD!u5uh@9F;8vlLJGjt1z`?R& zujmJ+!yFII{~gpwa5%~~YggqI7#udg;9%gm zz{417)yu)qPz4$=Udwiyd0wvZ4B2MylwtETmKNwLKgH52{}hG^ubmlKIRf^G7Tme>t)brRy*w9#m4d?^g@C21s(Y^4 zKMHG@e2aOK>QC?Ym0zB@N53d2W302AF+=b3^&bKU#|223L+YybQ8l zm+!IhI8-}S{9$0!&}BEMTOcMKDTPE!h7VI%2u{D$eb+(4aWS*u3Gl!ogV$HHDIb10 zlv$Mjo5N5)PjZjP9iQUo(&>W%-;TEeRWoDK)}#Vc!^eZTAcvUl(6ytBW}UpUFS{A00;bJ$zUlYE~i zuc%>QJY>L7%=GAzT*G%IctVe3YmkzyS$R*9i!H&=VUNOsuRnf1T34i)=G1WQTZ4+( z$JjI7XU`mG=-$9>m%(~lhaL0eWf|7AGJIU0c;A-)inNy8r@4FF0yvploO7>E z^v*PyEvvBS-}Z}o0whCtci`{?R|M%s=_eDw!x0u%)MweVKf6HmCGU z2Zn4#hacIK;@-LNUy(m1kTB=*bq+1Z2K)Z)^Zj_2*Eku6zhCD3oZII6=JdA*HybiG z*mD#dmvXd0ESqCs=n!&nu({5$ZRUmvUF+|QEaqXnww-+iGs714j77@{g0{za<%#Oj|aQSK@-gx3vwY&T2l~TK6Wb;rBBZji=&s(}E^T&cD3K zm;K23jpB9xLS#01m@--9`Z1hGo6TOp&y@ASOnt%J$In@|q%suz{H4pruzitp!_l{l zo>QYRnhbnxL}U|DepHP8)sLG4Kiwk>DBFgDm9 z72>%7ouzBwXNZz$X4t~sl5pVFUH8oTYp#Y51vdEWvdp+3XOMq2jFn*nH&e(xt`*n1 zgyQ56cnis8bcr*9dro#E8@ox~=a z&|YKD(Bj-6!S%RCn#X0}g75_|G z>1EHK%y&HWJD7zblLxf={#YAwpg%}us8~5!-_&Ym1E|%`#rW3fzUca+T!HM2Znv4A zC@QQh;uhs-uw=}-%`CV6Y9HS`?S!2g$2eFd<_a4WoO6HUFn^gKqeVV{!uo2bwQ^G~ zWNdkUi@D{`+VeGqvl~9tKG-tf@%y{G%m2SH(iUR$X$8&uHzO@PVqiGe$}oK^I|oC- zS`mgtP6v2P1s0U|F5qCe6v4_UH&gh-^`3o?R&8%~Iw#5k>IF*V@_$fv*v-l9#i(#r zg2g6Vk>$lc(F+rTvbHQ|f5j)r|0^#}S%aOif7?7K=4p8X_P70hSzf97rd7!>!J)zW z8Ow)_X!A2VO`v=gt9&DG5kJG9&XxutS5QHtz{C*Z+z_0{dZfHJp*Jtth2g{V1P(SA zCrJ1B!?6RJi3|zsED`tR0@eiudsS>TS6$Ng_0F7zyxFaM zk@3KiXvPR-(CN0&E+GTM0v3i%5*IFRie~i8;OAgC@HKk-%Uf%O;!_M1WSC0Ma&~NV zGgw*V?RkfX@#9X>3l%?ftmeczw4eXIh=uWE2LFmnl`6NrCM;`U$mGl74`63JDH<8~ z%7g!k{4?t})d{@|=i7?Yw$p$D4VTV4$NuT z;m{z@@TZfFG3*>C2Wx`{Xf`fX;6n1RAgf8SCj=G*h%ueXX}n*sFjtn{$JM zafYyh@9)m{owd7}n0_plW$M|^?!wNn-l3s#JJY?`zp}D>5AgjL=So#r5Fp9q16mjO zfA>uRheXwdIt(kCz^PQ4W!1I?E!WvML^D2)+3)sY|8ISP_L@&WHfNk#HJ6cLf}1{D zLhk}2CdBagG{yt`_J15W?oVWA;J?H5V2ka7zeik4UVppmkausN@QL0@7h=Q<7O2Ru zH|?zYT&BU-!r;i+%vir?W&e4 zPihiX_@+ENuruKsx50yc4(5Y776o0hpQPJWV=d3SdC%%I;90RkUF+Zj$(<%4Q;pu# z=U>vk`=XIe*@^#O?^7d3!_KVBDkh$*CQDB2I9%6OG*5C<#GgB#zirOAdr&8m;Q=$# zovWbL8vmG~Lz-?34%H4e`K%=g3;{*$vj6@DGBPrPifQx3&JAS@2j(y=owD{)kL(NX z7ZvYRt>!)M7GgP(*kHpVa>}Veg!z!7!wL7<-%c?!_zDP=^JJ{BN{zJkJM_9)lA))& zk?~whL-_IfBXy@wTtBar_#?xFp&+T6VLfVrIi2yr({oY+3~w33oIweevz;MyJJTYk z1G%moJCqt5mT0ZLad7WcUV#VuEE(%_?zH=HwAW`IQg|>!j^jiaJ7W;fjLk*CA`A>H zE)ERY4?Wi0?Uu{1(0T`2Ep^4=KuFosdE4i^?|);`eJaf-gcY>gt>83D{rsSm;lp#) zd4>n1-!jUo=`Olm{=QYgfrD+v1vvpe#-g{3N(?`eIhZ!MZ77@XS)#Pw!-b*#kf8%N zi%cgI$_e%lME%fQfPK1-S)+OuPJ} zoz!nSdV_`s4mISw1+BTd#29kg8Pd10 zpNaUm_Q&jNlP4W%VmSPkkxL+8^YMzZjQmByj2GktX1Rqtd+PVa;csHjuKmWKS<-DE z{ylp7Jn;C>>?8GR^?MjVo=2VWtyXxT%Ke<>O2M-^4ShTe@9rhH*Vl(QG+g|~u+x$; z5Yz{mmB(7M^|8R*Q~IA=`9PW0LT0h;gZ|9Dnm>Q4E#*jHXW6mYc0tTj9|ZbcJnG1z(aNxPJ5$d(L7NGo4x(D-pBWoW+CW`9#g#WE z1Z9;iY%y;OcA8~;i@EM@s@dJ->f@C^Dox7Y+C?&K_#Dw!rqlsc1&o zW$C^1FG&k(7QaErx>fjQswW=4nK&NxCH`H&Jb6c)2_Ir}Nj|JO{T>cB2+zQUV zOP<(XukOt7d^^*r@LWgE^}Gph8IyO$EAQxE+wtV#^NU^Y`;W6Q=x=3@*osyS#&9v7 zi&nnT$#|fajp4fr=WzyQfdyrt0nrx?3~K~ME-QYHwK~lA|6fKMgDTGjL-mAp8<-5F z7+M?}f^RYZXm1dCIRE|BcT+ATuxu}8N~_;Jy?efU{9D_#QVer$$Q?L62c_AO$?zg- zRt0Fi)RRZ-4E0G)4NPnjbA=W78Oj_$ zFmV_#GP5uU{EKt)NqR1_ivLsy-Z}=JcFYQObcuk%1~zWKNvAw zd85_Lpyt@{`WEw=b_RQfAI0&eYW$)_CKpW>*&?Vy|tMe&BZX2 zk;8+LWkw%zd-lLN+r{(DK}6Z%)7Yz2aX$8F=XQ==J=Y z|2}(Etk+_8Sf+fNxgtin1Eu|V>SIcELv1nB6^4dc-xwAbGc|!)Wfn_q9VD5wLb7%L zU5jO4s6XdvpdiLFC)8Tq;i=TYm)h4hZEcaM4$$rN4rbUPYX~ z&0N&I?&iPGFAnTv5(VwUT#T|--iDXquF6F&##lr315ctEqZkfcSlb{WAi&9xme5ds zjbq;>?a7l3o;Ndv4N8BSPpN&G zrO9-{x#8hA2HtFz|I42WR5Dzs0j;rmo6Dc!@$_c3&cm&BIa41*1YhUidhfowUT&@R zl%vlCQ@T7aPVEG5d`HPC4a}ew`o;_nD{UQKOF5P@Jec~8A(taynU}ZKjH&M85)2$C zzbzMG+H|(9K|-RyVt2!-&`d#Dh8=C7sVS4L|7#nf?rJPNIQvb3{kaco8xE~D&-q`u zrRe)f28Iewrbo4C3(P`TikTi=luHm}kcd@Y!OM_haNuw;lUBK`x20>i=JQGV;wpjc zjGx{zR$b?)+mg9`*WvxEZ!=$txg&QVk2NIMv2#6t0(<3Ofr7EjB$iC`n$4;pO43!)m zcjXdFdLJw(x4Xp7kilPYMaof8^UZwYrD6PD85TjpEF7yD-rwK;|M|sgX?_i+hPm6B z?rcLVNf{XDY+;Y!WQbBw;9w|oY%spfTz7YYy7irSL2$L1P+0Bo)^2hSBWUZ8xw^w1 z(SW_Ys@G)Kyb3!oS@!;;N6#;=G2N}X({q^?%Yiuzf43u-WDV09A549>FM<)&VU%Qg z#K@3!i+R$jtzTIM7Q75Quz_`cK-@=D!<|R3rzkv_l~e7oZQeJAso5;^YxZ(mA28ST zb&D}zc+2?b4wr`Z9<#00fliA9y97R*nQ%1bkIkt+NxS(O{%n+B+H(uk?gI@4f=4A8 z7}P+0$61Ez3oc*zo?9v3;_Se{;M30VeVt&+h3uXkcJDk>s$(lJVGe zj&-+-eTshY^D7+ie#_`|YVVzSLa%vKw=+2@E_gV_f9H?NE5#S1uJuij>|*e1W?)BM z9Lm7(bAheGyX{PGm>LQ=Iv5ySIX=9Y!yw6%VU?x#G=86)&&SeAo(nhS7Nqw+IIwfg z|Nlpq?YqIjn8E*Gwe13#-I#j-hsbR6gffG`WQ&%ebs?QB%XnM7;WY)GR-QQhFw5ADLrtmvAahcw__-^v^myf1Of7)52%)vMw#X>i6uqj|3cc?=Synn2`ALVSSRzq{EXJ>S;z@4mqKsm8_6@__9yz zsg19H&boYO`X{NTba{AlJ&xgnPql-MqJtds55|UXOFPwBBn~oED=s*{CN-qs>oN&f zmVl;))29R!x*4Z8Nht6$+VR{d)tVi&%A}vAVTMEk6U&|zVoXLQ4i3T&4JQRQRNU3O zRq@4U?g?$7>x{R)?){wcI#bb{;faC5J%I_2oX}S6IWVYiXZpe1@a>i10xkaSOpiDj z?Rb7Xm9~9R<7g;4jj17uV?vU{yhm#rvQKSiTIA3$fm6OFcGuhmk#8#Ag*jCHF>DCt z_z^gjC#u|_`kttJk}5QQRLT-X?vJmg^75v434&r!g}aN~~L0YMg<+l5mq9_%l9D|Xsfo<-sS zgKja?lzi5hrH?IHL-s{wOEP5SeF$T?#eCW%xqyJg*P9{gX!!PJt_zydl)Bf396 zL0e7#^4s0teg2-4{C(3*kYUM36_z86$P3XMrZX-G>(}6D_|{NypQ}KPp(5YU!RYsO zyIUK&yKX*=_n4@X=G1WSTZ5bP{UA<;4E_&A)ecMf4rd;o`1#kMV< zANf<192l6+opwI(Fn~i~)7zB39hHyX?ft!LUw+HBruo}u3$idw1}%LBb$p<8M-jsU zDbS%WqAUgM41G-vKR_$Gv?q7nSQy8^P=5xzJf}_n&m7r*(H@{xzmbOygO%a1z=OVT4bQE56ScqTZDu}~`*DJ@YXbwz9S$dlLJ`n_)d7A+ z%N&je!7KM3W;mb+s>A2qW_H`NeUjO9xqnM9GBJ9+WppZ=b4s*5=-zJm>ZuQ!c-a>* zH?VGJ+Or3v2Fo%$ki_xAYLd)qRspnXCY zxEa0o-CS1igTI*}d^>xBpMws^kNMC39u=)`%xG))yh_pG$9b9L{SWt-z3zMT-FLQS zd@z&s9rYEi4C>q13ry6JhlUs!L>{$#V<P{=9Yx7X19voG|h+*NC>NLm@s$w=OboQ zf7`d*_nD(vV$63x35~krcC%E??{PMI+ zVV2^9HY<(`m*qBm{Sdir?t-bH5s-%K*Eq`N{h7Dp)OmSTP|z#LFqz5P`m2BHdhzV- z?ne*29zKxcV*qVYv}8QV7m3zl{o~l+{+#6lGlT3`#fI)@EPs~xg-89kDz+=I$o)*I z=M~3>ZFMDCm z#n>gWAhGvBefpPFZBFS+%~zuWlkPUYSzzCn$`mf}U>iyU_P}L^3#(1kKs%*NoEh{P zCP*+bs3kNwKVz{8+HLInyF%Cc$9+Nm|Cf%mF*Ltry#94NJ5N=*>y*R$Ikz+I0G-o# zFhk%&$X>lKU*9Ef>6QKS_ots!ufT?zODq{ro4sEyl4zjF%upY6!#@9Jcy*A|;=pN+ z4cAdS)%wg1Z{vhm3OHDH?67T+W>|4C(1oG?O*`n|O1bs7n?d>alV0r7N!H(AT${VQ z@dT(tx}u$7@moem4vUGw4*l0&9m&(%^5y7dfd^rLn@*2uM=Dcm6j*?_U& z^c7dd_WvJ3R@`e7ddsp}`MK4GiU%^P8zQq=cKg=bSN!1b4;5&zWbDh}uh{CZmTOhix6gj6 z_x<^2Qrqk6lbk_=(q6?(|8DIl4`KM=ej_GenP0w_hwS6K-$4BjqlYJxck%G<$zK``6ng&+pZK{Gjh^!LnkP=z>kQ2hNx_v7dVq z)H?k}wxZ8}@x|gybBaJ+jH?a@7GI6Nw7^pSa$j(z&Q8c$qAmBh43hm0Z1VjbD*I14 zk9E(Pea-hI#6E{HT;tGUKaV*1j)5V>f#Dg;k;S$R@(eFRX7vRcDx6e&%lQ9hion;Z zY{|8^i$iP<+}~S2|J}XMPy19J3KT^1v)tKM;OB5-Z9`Rang`$ihYzKh>TYjP=biK0 zaI1vz^sFy49R5!=Y~VG@X^&=PyUm>SbTxLeN!fw9WyOvdbAZSQTp?Jv8}?B z2lEg8yR~Yr`^7`1w==KLw7FG!Yx>;8kIoIB);7%9v4wrjoys1$RdW(K7@9I&MB@_g z&*yC`zAks*HO6SmS~6O`G;G9N;MXaB0f-MX5_OXMQ-CHS6n5wx}&>(zUxP z`PZhhAAR3HXW`+`(8B58WFLJ4y7k1xNVv6BSXpm?6b8^zNN2yj*%R9Hp*g2mzf3s{QPvx(D zdwY!B&MaFMd;HS;OTRB2z8YP<%=W=?)2P5^$ICAk`&MUupZUk3th1996m>if4!1vk zKH6Mu!0YSzQ|rz7FQ9470~TupK@;TA~a&Z7YDT$ zMLI32T<(EuZm3w31-qOsdn+=S|ZZm^sC1Q7!^ghjE zVM*~;a5%e`L6t+G=vPvq!Owkjw9ay*j)b>BMk>Z)_?bB;J zc^Q9p2`soFm+)y0gQ9J^-dEXwOIK#dve`tm?*0CX?@2<2g8f^@AC(|aK$qM#D037z zRyQzmFfbh9WKxkRxPRyy!{+N8Y2qT6*Gqq$CiCy`V;PQ!{ogHOHFiDo)&$K4L<%Uh zGcH%L)KpooF+uo}friDr;?BD|CzkfI`(K`ZoB!gauhtpoPW76+Jbcw8=V1Dk z7&3RO9>@NR=OsRxm8NUUy6^jKx%cbMs;HCy>)US=83aU`&onknNe^E6XSJ=y!iyT`*9#sw4{jVnJI2qP z7_M==I>x}zP%E&Yhw<4tPJ>Hw7cy?F(P*!?R&=mtli45OVEN|5dw&6@b*_yJKfg8T z@V;eSrQp!S_)tJ0$>H5nhd$1eoX=LwaEjLxxg_Yx_j|JLk_Nt$PagbC;G45wqQ?!q z+-u)k#@u6PyX$`RGqZ41uCUte7@Xgs`j*izLb>C8Jt$7VF51Jxu>RY(qeYy04;h_Jg~;JNq*D9-ly>?YkA+8f82M2pE32uts_OHRe?^gB>dM3 z9w`SE>X7xs^Vk~fmcGo@WdpCxVgOAx+*#Y8zwO;(qpPCoDSoTt%@7ZnaTy>`~AM?LE zyvObv6}hu;*$~bN9IT)k8*u zwdsw61V_xIZQL=b1`3@#d~-yL3}*;PoJcoNIH{>vd9-oP6t(N(^|KrJ*_?`fd;~c9 zyoy{-u-2<=XHl`7B74S>k$uxr3k6>ZE)V4@Igr)<6pC_`wu;gS7!;?y5Dxe<0aoU{&QLX z`7PYYxKZK3E-Q|XM#eS;1va)LZ4FAP949!P4hU`)n6OqBv^ZOg>0DMjgFV}to8^=0 zeidm?WjMgk(jmLidXvRc= z3Dqx+{w_T#AlhDkPQihdttWERB)zzuno14!T>=G>j7(iq6(2OQFm3wk;LvEqlCf8G z0V~s%dt4Wozys$_4*Qf?SPT`+S(saFt+~uFl@KqNzc8m4d_1umhoI-!i7_=I~qOC009Fu*|qAw;?*Y+F|v^oLBbJ zTf}`<@k%5-Tif9F<-zJVyLUb0I2y_5m%)GFy9H>92fCeSpTdE|phF?c&u|9p5naH< zRK%r!+5UEKLi@jMi5I&WKkgA-z{h0v#@>;&j-i8%G5pIJP(DpKHito##~|6S!TA>R zvHBfWZ$7+N;Lfaui^+v&x#fAp023GdtKfgZG5}Q)FOx zAj`qAUv$IsBbL8H7w)OGq*ma;hi?rc-_8d;G%eFsDBbdA4a06w7L$T6 zu-9ikko~u&(1c}2x*r3>f#~f_UN=_l&%11w(Ehz_(iT%bMw#vGdv4Wp#qO%pZUCJz zb>thv@^ykXX%FWxXtKG?7XF|c{r&szHd~eU{~wlEJ_vJg{+!Rc=ZxL-uk9Nn8HIOb z@*nta1Wzw=eoWSX)PD#nXn~GYlLPtny~BZr#XCbbUY77$b?@BttfEeHmMt4)n5=xF zd|a=4N90L1F{}}MlHU7ZmaY1WmFByGKYf0=&u03&0uBRt=2JHF-JZVQG=KZb^OMs-3>uuR z46HmIR_Y3Q*(~c5dlxuWAGluKcX|Ema$dQAOE)qx#S~~ico-$Cx$uB!U!;r*%t2mGrKJj&k|{IOTa@S#A$ zm#~Ic-x#j17u4xGEp@PR^|njGTW@VVT$fx4>c_mC*V<5|Z?dfDD_elmtsOg@81{98 zs%mJ1OT)>bX{o>kaRGs8e#Z8PeqU->)ir}kXg_~nDV+**MP=Qav#DE!a4 z>kQxj>E410LdO;* zTg>O4iC=s=d%GmVojyj;Oh;?hZRT}HR@)wUV%j9G8>2iUyT}iS70EO#%hc#-IYM!L;esH4eR>W$J%d<%v&iuV3!S5D%J@ zd+%JjE7v$b*~kY$$b&Vux~TmlOIeNcrJ7@=P`Ng?hzwz)sWITu=o8{|T!I)in>VMZ}{y)*YUx&q{kdI}@lduEM zrcuG4qz~VC82@`MLrp#0XAdeFCa?)4JO`Z$df#%|+~ezhtY21NDshwadTV0YN1^<{1b-<%sZU+4I@qD;MN*^o|D z6B>VJv-Cd`xiqDJ|HA)ShuXq6HSaf>rTCy#ZH91ydo{!9!=|?bi<-@Eu4#B7(D1$t zR5yVetPBid4i5EaI4@k3V_?{zuUh`@(znIwna|RtSN#0({l)&MjjR9am#4A+^E=pF zU$LeEwCK9!`N!+Kf}iF+UU7JdtI5~L`I%?WDVMxUn)vWrgVVcP%x|KVcf9-{`O5ye z@~_gr?*!TFe}e{fzb<>j{W4^s-fzh{W`5to81}t~M>7LM0UP7JSY-}|4R+yqcNWE} zpS$=wyZpk1*7$(Kg6}Wfzty*7)7D+nSe%9epa!1jCn563^gf44@z2`KNCJ=Jw{;mU@;W|MiJ z+^+m1v-WOrPgu@9Z|;5n`qb+8Ok+Fuf6lCp_n(Eox9n9dDIs{D(YgE}>|F-<)-xXo^PhP1!B{XZto9x142i8q~t~p=!%-(%&A9gQ} zG^zY)9LGM4{{kKRtPid z%mVEUZM(*?>YVo7(q*%Yvb2`2K3Br|EOqV6HF|!aWxOXqx4g70=lPvpp|w^&^!-fF zT=hNPi^Xa^y|35#&*q=E=yK)s5NC(R=4%}9tV$cx>UPZo4IA7yeYLjWLH?@*P*UG3 zY4+UN26@@EM20ZvG_IT3EIS(-!q2oZC`&MKJXmIXAdMs7)L~QWOY>**CFQaTuQ+<6 z(xl>^y20Xfy__TZt5rzX%3_t4szyB|Is9Y#cUSl%jFJ_K{XKNYm z3Otw+%{aF-!9o2rc#pw#xdpbm&XZ!cib{LjaCK=Cx9VTIi#a`D^+)cN_uo7<$$J@Y z^8WPMFDIwfZh3h?)9S=EDK7R>Mz&9XcMAE;ldzQ8;b`g9^>?SAc*MCFon>#2Z+cO@ zP0}&qym{3}EwkSVoJ)Du z2TpjfGs5$_ARE*9SDju4D0MKgpS zC~q<)K>o2Yfq60N((E^F7S1)jYp zeAJg+)Ve3(|Gj`V9_AbIx1;}@xB12OyLjalC)Zi-{k;McmasF$8E`SW864mgn9z0D z9^CQ(9e%~gu);ckmFWf}=x&eHY6ssf>>5xpadv`rK-5g?R!OE?IG`NKs_jvCj6uO@Rg^OL!P~xLg^oN;Ft9 z&I2V#@QqBTBpyg{6ntLWa7-_s|G-1Yw%h;)1{YBV5914r&;ywS!51AlR5K_XP%dV2 zJNKXCyg4WffKTAhVm#m}@ZecAr2WEkm33nj5R1&lRWea8_UgJ2$(heCJ09{$d%U}?p&II-<^mxW2a~PB*9(=Q8?A2{& zkogBG3|Sc*N*E3#_`_OD4WJu!5_%OtcU3J&eamR^2U2^0Hi)n{R5dpn@_<&Ptp%ON z=u!>7$fm&(bU6=F{3OXRC@CatI1P%My;ngY6QRrj%EZst3m!p)^pO~b4MI!?>I}t9 zHkRrOxIuTaD1bsKL4w&$?}x;BJ$U$uF*d9eXaL!HWDe|RtOd3X`YcGc3du7-@AFas z-`w&HGzntHffThzR2U?Jr1&8513t0x2Fb8t>(Om(9qJvyEvlXDZdJS8{LPw4j`HGN}Qa8E@WV0BmA2<>Ql zGeE98d&t4?1{_q#%HGWvZa8eu1X)qs5)4{247zsc0?2O}pwkIA-(q&tJMvH-dg0R{ zTLy6IMLCiO1xz75Tw4T}{JYBZR zxM2F}+sqr>F3kDX!1sUu-s<raCL5z!HM^WcS zje8q27AdA4y)cVw>yuEE%wP5TtKYu97~*R_9=(3;&DzSJqwZ`rdvUgqsbtpm4en~J+OK3<%6MxS?&!26=Jp)5_$+21~{vi^3FMOuJs z-s)TWS$bVe?#FLRTzufM(_46rl7P&S z3}=j3_Lv+NFj&2uNlKDw+MAUPD_a^)n>rm>KTDLU&)1se%)^xp+gckslMYlD*Jvfo zQ+)82wWYz_T9oO|pVMCxHvo9y8`!$pr|) z)ooz-z@NB(wIuT%^#z@Z31@wr4~Qxr_{eaaIjY#^KsA#X!pH{<2ALcgr-K>q318Uz zoh3$$iR~zZ;(?D1ZnFFlecxGj{4{4|5MWrN%9zk2@t~sNJB!YfiUX;~nXSIhYw&hD zut4I0Eo1Mmo&})zWMDiHCB-1a|6v}(d(IE@8X}J{%oqG3dGM)?+=FNL!NIA@!N6@N z_rSQRkG;ab;X6x(f5UkWvp*FFw$}eEzb*<1EL(;evow|$hpL%m_&-QGg3NGy&-p>} z;6-O}x?x~+V90Q1u=pF0iU-c_=VWMLP+-_%&7h;h@}noAx}W_A$Q&-l zKK7FL#!Y$ud>I&67#I^OnGWbGF3>&Byhr^&HB*h-g>)8K{){W>EYrV0(lp3z5$71d zK?U|<$qSHYE#wSzV1dcfkjK)nO%CccP*_D=6L?T@Af}%k?&5c>3=lWMeE;G~I?Mfn z&-qLY3Je?!viuBdc^LcH!KvXq$F*)oJLMa9zq7>rK3u;e%>Wc-EDVeXKmnL>9OT3T zP`q3Hp4Y&Mu!Mmz;pRPIa9mi(ZI}TGL5Jr1!qCVPV3?-OFz07ZsWCU>J>dto5c@#B z+4366v+o!TP78qj%K=eQ;oxnj`~ni;0u0kk7-G1ip9wrLZn9H;VavGIMh=vUH#9tE zgoWw?X2t{ujshES5_RigoId?eMZ1;B7x#aWErP?R(A_H>#QX ze)S}5zc2h@8YBfeFubT@h=8bQsH}Ip@DZFA8Qybhl)df$|8mv=P&yP~U};!r2vNl_ zU$6ueI#GY-!LloZ0)xrZl??AWGs+>(0wtP*Z1$q?kg*bDSl4c9!}6nN!FQH3sw^>r zOfvihHVc%GGedJLlS4c6fv+!*GaP45lV|d)^mo{9uUrwy@j=q@ICGW^KRhn)@-U>Y zU!NBS$)S7H52UkXABSX|mu+Adae(8TsR0zqk0DVJ+`)MIpX9;rwRQgBEWyGc07{<= zvVtu^xyw%Z1tK#M28$Gw%zthmU;W5aE(1h5>e#V65N`)4}o&yXY{4b`7fMfE&M{pE4 zzUQ1#$N`G022c@f7RInbnrV;vg1sPTfYYnhZ^?tor$0l|4hKU6L&8>O2N9-oweM^} zQDxlJ`>$ugV^BHr+m`YA|1bCd&6~}j0IC#xi3*GbXa)07`^4d z6(u+d&Z;u#XhAIc&Z6`EW5ek__L6tDjMKAPp*gRCA)%O`VSl+z!cBWnrUIq13lG1u z)Z936o@3Wfb8x;mbLPw$ABY#QFdY~K7(l{jASDk0rhzdCfP4W}NRWxk{0^oA;qkR! zx6TBCUNeH`FoH^u10Y*{K$(*fE7Sf_8VG44`wA+&~Uw z0L20*P8h)L7zQS&00)!>YTSUN7!U$r6`(a144^`b0qkK0&^BBJOaTT^+%hnLEP$v$ z5&-E0m1n3HD}Y8VK*A80V6h*f0&F9=C}99639y4ewn6McavDgGkQ2Zz1o;z`xxpR; zIT=NOQkQ^T334k^5J21t4r<&MBV0n169|V1#V$cg4DiT+MHe{ma7PBj$B=dj!N4FK zCKS5_OJqPC4@!cNRD+TV5Rn0nHBhbqVNfo@5<7^%KuV9WAb{8dk1mQ`g2jG#?120L z&M{c>D3a4gvkE+rKmmm#fkGmV*aSjxwA@w@U;wSn6JP*!5g8a5K-C6J0CYkdNQ430 zh+qI$zaS<9sBUKfjXtt4fU9S)6j+b}T*W}7kOUYY;vj=qKum~jQbG*(J_z$MFeI}! zm?Mcp>}O#}_{31)|D*7xg$lzPUi&b8&|#ec+6;R{)~)=`$H^dl*z}hz6NAB9hJt)o zh8saS(HrI4lC41Dd`ujSja-|C7oK8Ryvn32lxBk#w>n=x#|7;Z%7_-}MR>Ib!N4%hW7)rU;Wq!{Ex7#cEJ z8?2AH(L&HX<1FvT?Y%q-}?iXfwC&189$aG-);r1$hiV{C*fl zPG@O&-zq=9z1ueG&BqUqzbsBVFY-Hc-S{^ zx^nCdr4MKQwPs>4&|#P&m=M7bAzzpB?mSDwzjV)S;zCLMZTYMbjAnoR2c3^vNq&%GB`*w zT;t(jcwEQ1;D6<$f8}1k^|fElkF{5EzU)f4@_;D&y8ZOEJOqjj-w^1EX-zs=s@ z-}QIfjjKuZ;@`f5+E!`25tX%7rZJoAgkq1!abDlHpYQrE`PS_F{D-!FI9iZfqZp$- zpY!^;_QPAhyt|j7&#;DzpjPyLhj{@#9kdSAQy%wP6d>qUOQ zzIXQg>^*^R)+XhPSDXLg*e8Fqk-z(|QWwkLTA4bBv!GU$*xvrdudl?nWnbrCJp0f0 z!=~?cKb-Y_Z2|9}s2yr`sxiv*xvrmaKVmd*n9Ek&D1PYvHf7`VL;QDdepr0H{_@M8ADk-`|7h%!Jla@(sQ+1YqWIzn za8uIk{nYD=pMHJ0`{AsudrsCH$6Su%xW420Vbk~852b$i>`=3lTsLt(_w_Ts51W3O z{BTxPwtoG;`YGZJTnq0`OgesuKlZ=ia`D6c z_g{WUuF&gZTHj_Ab*%AU9KZb%a3FWzdwsF=)z5;rzp_d;*XzYRp2v56kA7SBgU1hN z{m9tiW+$<3=IPo0=RTZe_4iNw)t~i@3^Obkro=O?sN#31;#p85necy3P^HL%|IF9w zTQc)EE9bEsd~n(*ys%-K=+i;KqMzw)Pp6H`Sr4j*`_ zApq_-oX!5Y^~LFnXX`5ORvE>dp2u;0+xhnFyI+pg*Y7a9C%kUz>Dm9^K8&*Ze*gcM z&-J0t|G)28_nMVKL7HJz9Lo$j=7{^dze+g#G;R2z+3+vf+j^2Z<0*~{`g7T<%zjP>FHep6 znH(0AIvMp_Wc$kwfr2uJv3tGe>DO1jEc@^A4CE8Gd(!JVPyhe(p6mJod?XMc$7>NH$TF|{ zPy?v{_kV`-}zquKJUL2UHfJKpZE1YpZ&j6|DWCVpFRVF zv$Df4xrRH24R?YWUWzmBvsYpI6T0#4#mfxUi`1Fs`6V4NPAnABI1DwoOk?Lw{q@n0 zXa8Hi`2Uaho&R3%w~i6!-}Uo<-TT@3|IYvYzW>$7`se=}Co(y_lW=&a+3=;C;bmZh z`y!4D^;1^X&s+IFX!6`Zj*j^p9mxmwsc=jQeBdC$4hoSoVq1UQDyc6zykCFk%l`k5 z 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

i%LG$% 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

i%LG$% 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 literal 0 HcmV?d00001 diff --git a/dist/branding/qtdesignstudio/qtcreator.xcassets/qtcreator.appiconset/icon_32x32.png b/dist/branding/qtdesignstudio/qtcreator.xcassets/qtcreator.appiconset/icon_32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..d495f2256836082d1537c6941d0b00aa8db1455d GIT binary patch literal 735 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 literal 0 HcmV?d00001 diff --git a/dist/branding/qtdesignstudio/qtcreator.xcassets/qtcreator.appiconset/icon_512x512.png b/dist/branding/qtdesignstudio/qtcreator.xcassets/qtcreator.appiconset/icon_512x512.png new file mode 100644 index 0000000000000000000000000000000000000000..0b8100f185d91b60ea405e8696d422c1532d955e GIT binary patch literal 24742 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 literal 0 HcmV?d00001 diff --git a/dist/branding/qtdesignstudio/qtcreator.xcassets/qtcreator.appiconset/icon_512x512@2x.png b/dist/branding/qtdesignstudio/qtcreator.xcassets/qtcreator.appiconset/icon_512x512@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..8292c415c5dafa72c1b68905dafacb2e4b868cc2 GIT binary patch literal 54880 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 Date: Tue, 2 May 2023 15:49:52 +0300 Subject: [PATCH 119/192] QmlDesigner: Assign the correct context to QmlDesigner The context would be updated when switching between different modes. Some wrong conditions had caused to prevent this update before. Before this change: QmlDesignerPlugin ignores hiding the designer when the new editor is raised by the editor manager because it hides the designer only when the current designer is QtQuick. So, the previously opened document is kept by the document manager, since hideDesigner is not called. Then, when the user decides to go back to the designer mode, the QtQuick editor is opened, but the previous document is opened, so the QmlDesignerPlugin will not call showDesigner (Even if a different file is opened now). With this change: QmlDesignerPlugin calls hideDesigner when a non-QtQuick editor is raised by the editor manager. It compares the file paths of the editor and the design document. So, if they are different, it will try to open and show it again. Task-number: QDS-9686 Change-Id: I6b962f22a1f3863128ac6a40780fdceeecaec040 Reviewed-by: Thomas Hartmann --- src/plugins/qmldesigner/qmldesignerplugin.cpp | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/plugins/qmldesigner/qmldesignerplugin.cpp b/src/plugins/qmldesigner/qmldesignerplugin.cpp index b7d7000be4b..254c0ea5125 100644 --- a/src/plugins/qmldesigner/qmldesignerplugin.cpp +++ b/src/plugins/qmldesigner/qmldesignerplugin.cpp @@ -186,8 +186,9 @@ static bool isDesignerMode(Utils::Id mode) static bool documentIsAlreadyOpen(DesignDocument *designDocument, Core::IEditor *editor, Utils::Id newMode) { return designDocument - && editor == designDocument->editor() - && isDesignerMode(newMode); + && editor == designDocument->editor() + && isDesignerMode(newMode) + && designDocument->fileName() == editor->document()->filePath(); } static bool shouldAssertInException() @@ -440,14 +441,12 @@ void QmlDesignerPlugin::integrateIntoQtCreator(QWidget *modeWidget) &Core::ModeManager::currentModeChanged, [this](Utils::Id newMode, Utils::Id oldMode) { Core::IEditor *currentEditor = Core::EditorManager::currentEditor(); - if (d && currentEditor && checkIfEditorIsQtQuick(currentEditor) + if (isDesignerMode(newMode) && checkIfEditorIsQtQuick(currentEditor) && !documentIsAlreadyOpen(currentDesignDocument(), currentEditor, newMode)) { - if (isDesignerMode(newMode)) { - showDesigner(); - } else if (currentDesignDocument() - || (!isDesignerMode(newMode) && isDesignerMode(oldMode))) { - hideDesigner(); - } + showDesigner(); + } else if (currentDesignDocument() + || (!isDesignerMode(newMode) && isDesignerMode(oldMode))) { + hideDesigner(); } }); } From 26acabe625615a5eaff5e3ca78f98695e8afc9d9 Mon Sep 17 00:00:00 2001 From: Aleksei German Date: Wed, 3 May 2023 11:53:52 +0200 Subject: [PATCH 120/192] QmlDesigner: Fix tests compilation availableInVersion() was removed Change-Id: Ic98f585ae9b4a8dcdf83281eb6e272b01f3ae4b7 Reviewed-by: Marco Bubke --- tests/auto/qml/qmldesigner/coretests/tst_testcore.cpp | 5 ----- .../mockup/qmldesigner/designercore/include/nodemetainfo.h | 1 - 2 files changed, 6 deletions(-) diff --git a/tests/auto/qml/qmldesigner/coretests/tst_testcore.cpp b/tests/auto/qml/qmldesigner/coretests/tst_testcore.cpp index edbcd5554be..228d0459f4a 100644 --- a/tests/auto/qml/qmldesigner/coretests/tst_testcore.cpp +++ b/tests/auto/qml/qmldesigner/coretests/tst_testcore.cpp @@ -4834,11 +4834,6 @@ void tst_TestCore::testMetaInfoSimpleType() QCOMPARE(itemMetaInfo.superClasses().size(), 2); // Item, QtQuick.QtObject QVERIFY(itemMetaInfo.isQtQuickItem()); QVERIFY(itemMetaInfo.isQtObject()); - - // availableInVersion - QVERIFY(itemMetaInfo.availableInVersion(2, 2)); - QVERIFY(itemMetaInfo.availableInVersion(2, 0)); - QVERIFY(itemMetaInfo.availableInVersion(-1, -1)); } void tst_TestCore::testMetaInfoUncreatableType() diff --git a/tests/unit/mockup/qmldesigner/designercore/include/nodemetainfo.h b/tests/unit/mockup/qmldesigner/designercore/include/nodemetainfo.h index ac0edfc018c..6d9829508a6 100644 --- a/tests/unit/mockup/qmldesigner/designercore/include/nodemetainfo.h +++ b/tests/unit/mockup/qmldesigner/designercore/include/nodemetainfo.h @@ -60,7 +60,6 @@ public: bool hasCustomParser() const { return {}; } - bool availableInVersion(int, int) const { return {}; } bool isBasedOn(const NodeMetaInfo &) const { return {}; } bool isBasedOn(const NodeMetaInfo &, const NodeMetaInfo &) const { return {}; } bool isBasedOn(const NodeMetaInfo &, const NodeMetaInfo &, const NodeMetaInfo &) const From 84f75a7ffb58c07356a5178146865b59018ecef9 Mon Sep 17 00:00:00 2001 From: Samuel Ghinet Date: Wed, 3 May 2023 15:03:57 +0300 Subject: [PATCH 121/192] QmlDesigner: Fix materials download path not using the configured path In Preferences, the User can configure the download path for bundles. However, this configured the path only for the textures, while the materials kept using the hardcoded path. Task-number: QDS-9622 Change-Id: Id4ac82f899542eac5c2ce9cc51053113327aef2c Reviewed-by: Miikka Heikkinen Reviewed-by: Mahmoud Badri --- .../contentlibrary/contentlibrarymaterialsmodel.cpp | 5 +++-- .../components/contentlibrary/contentlibrarywidget.cpp | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.cpp index 127ecb4225f..f272a4121df 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.cpp @@ -7,6 +7,8 @@ #include "contentlibrarymaterial.h" #include "contentlibrarymaterialscategory.h" #include "contentlibrarywidget.h" + +#include #include "filedownloader.h" #include "fileextractor.h" #include "multifiledownloader.h" @@ -30,8 +32,7 @@ ContentLibraryMaterialsModel::ContentLibraryMaterialsModel(ContentLibraryWidget : QAbstractListModel(parent) , m_widget(parent) { - m_downloadPath = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation) - + "/QtDesignStudio/bundles/Materials"; + m_downloadPath = Paths::bundlesPathSetting() + "/Materials"; m_baseUrl = QmlDesignerPlugin::settings() .value(DesignerSettingsKey::DOWNLOADABLE_BUNDLES_URL) diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.cpp index f83fc241856..b1e7868749c 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.cpp @@ -12,6 +12,7 @@ #include "utils/fileextractor.h" #include +#include #include #include @@ -122,8 +123,7 @@ ContentLibraryWidget::ContentLibraryWidget() m_texturesUrl = m_baseUrl + "/Textures"; m_environmentsUrl = m_baseUrl + "/Environments"; - m_downloadPath = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation) - + "/QtDesignStudio/bundles"; + m_downloadPath = Paths::bundlesPathSetting(); loadTextureBundle(); From 971b35f721750741296c193cd1ad5ad1852e46cb Mon Sep 17 00:00:00 2001 From: Samuel Ghinet Date: Wed, 3 May 2023 12:45:09 +0300 Subject: [PATCH 122/192] QmlDesigner: Remove version from textures URL for the Content Library The URL for textures should not have a version number. Change-Id: Ifb0103d8420f9f3b3269657875ca184533700da5 Reviewed-by: Miikka Heikkinen --- .../components/contentlibrary/contentlibrarywidget.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.cpp index b1e7868749c..9cc9576bc6c 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.cpp @@ -118,7 +118,7 @@ ContentLibraryWidget::ContentLibraryWidget() m_baseUrl = QmlDesignerPlugin::settings() .value(DesignerSettingsKey::DOWNLOADABLE_BUNDLES_URL).toString() - + "/textures/v1"; + + "/textures"; m_texturesUrl = m_baseUrl + "/Textures"; m_environmentsUrl = m_baseUrl + "/Environments"; From c88417cf482e3da8e8f8c7e84ab3103726687f6a Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Mon, 1 May 2023 10:37:30 +0200 Subject: [PATCH 123/192] Utils: Silence warning for GCC too GCC is recognized by clangso we silence both. Change-Id: I377caa344a60636e3c73404d9d02a4dfc2eda794 Reviewed-by: Burak Hancerli Reviewed-by: Thomas Hartmann --- tests/unit/unittest/smallstring-test.cpp | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/tests/unit/unittest/smallstring-test.cpp b/tests/unit/unittest/smallstring-test.cpp index 6364bd9c646..2f12e4413a5 100644 --- a/tests/unit/unittest/smallstring-test.cpp +++ b/tests/unit/unittest/smallstring-test.cpp @@ -1533,10 +1533,9 @@ TEST(SmallString, LongPathStringMoveConstuctor) "text")); } -#if __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wself-move" -#endif +QT_WARNING_PUSH +QT_WARNING_DISABLE_GCC("-Wself-move") +QT_WARNING_DISABLE_CLANG("-Wself-move") TEST(SmallString, ShortSmallStringMoveConstuctorToSelf) { @@ -1583,9 +1582,7 @@ TEST(SmallString, LongPathStringMoveConstuctorToSelf) "text")); } -#if __clang__ -#pragma clang diagnostic pop -#endif +QT_WARNING_POP TEST(SmallString, ShortSmallStringCopyAssignment) { From fe92d2ee5d35e5993673f98d8d5d6865c00fa95f Mon Sep 17 00:00:00 2001 From: Eike Ziller Date: Thu, 13 Apr 2023 16:22:10 +0200 Subject: [PATCH 124/192] COIN/GitHub: Bump to Qt 6.5.0 Change-Id: I8c2122b7f35d049f0249631fc6e045b79dff9fbc Reviewed-by: Tim Jenssen --- .github/workflows/build_cmake.yml | 2 +- coin/instructions/build.yaml | 3 +++ coin/instructions/common_environment.yaml | 5 ++++- coin/product_dependencies.yaml | 4 ++-- 4 files changed, 10 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build_cmake.yml b/.github/workflows/build_cmake.yml index c45ed909ac7..17cb3ea370c 100644 --- a/.github/workflows/build_cmake.yml +++ b/.github/workflows/build_cmake.yml @@ -7,7 +7,7 @@ on: - 'doc/**' env: - QT_VERSION: 6.4.3 + QT_VERSION: 6.5.0 MACOS_DEPLOYMENT_TARGET: 10.15 CLANG_VERSION: 16.0.0 ELFUTILS_VERSION: 0.175 diff --git a/coin/instructions/build.yaml b/coin/instructions/build.yaml index 23461dbcf4e..75818065a40 100644 --- a/coin/instructions/build.yaml +++ b/coin/instructions/build.yaml @@ -66,6 +66,9 @@ instructions: userMessageOnFailure: "Failed to run build.py, check logs." - type: ChangeDirectory directory: "{{.AgentWorkingDir}}/build/qtsdk/packaging-tools" + - type: EnvironmentVariable + variableName: MACOSX_DEPLOYMENT_TARGET + variableValue: "{{.Env.SDKTOOL_MACOSX_DEPLOYMENT_TARGET}}" - type: ExecuteCommand command: "python3 -m pipenv run python -u bld_sdktool.py --qt-url {{.Env.QTC_SDKTOOL_QT_BASE_URL}}{{.Env.QTC_SDKTOOL_QT_EXT}} --qt-build {{.AgentWorkingDir}}/build/sdktool/qt --src {{.AgentWorkingDir}}/qt-creator/qt-creator/src/tools/sdktool --build {{.AgentWorkingDir}}/build/sdktool/build --install {{.AgentWorkingDir}}/build/sdktool/install --make-command make" maxTimeInSeconds: 36000 diff --git a/coin/instructions/common_environment.yaml b/coin/instructions/common_environment.yaml index 933785ac8d6..b8be5f5c4a4 100644 --- a/coin/instructions/common_environment.yaml +++ b/coin/instructions/common_environment.yaml @@ -10,13 +10,16 @@ instructions: variableValue: http://master.qt.io/development_releases/prebuilt/libclang/libclang-release_16.0.0-based - type: EnvironmentVariable variableName: QTC_QT_BASE_URL - variableValue: "http://ci-files02-hki.intra.qt.io/packages/jenkins/archive/qt/6.4/6.4.3-released/Qt" + variableValue: "http://ci-files02-hki.intra.qt.io/packages/jenkins/archive/qt/6.5/6.5.0-released/Qt" - type: EnvironmentVariable variableName: QTC_QT_MODULES variableValue: "qt5compat qtbase qtdeclarative qtimageformats qtquick3d qtquickcontrols2 qtquicktimeline qtserialport qtshadertools qtsvg qttools qttranslations qtwebengine" - type: EnvironmentVariable variableName: MACOSX_DEPLOYMENT_TARGET variableValue: 10.15 + - type: EnvironmentVariable + variableName: SDKTOOL_MACOSX_DEPLOYMENT_TARGET + variableValue: 10.14 - type: EnvironmentVariable variableName: QTC_SDKTOOL_QT_BASE_URL variableValue: "http://ci-files02-hki.intra.qt.io/packages/jenkins/archive/qt/5.15/5.15.2-final-released/latest/src/submodules/qtbase-everywhere-src-5.15.2" diff --git a/coin/product_dependencies.yaml b/coin/product_dependencies.yaml index cbcc2bbf7e1..4212b9b2e24 100644 --- a/coin/product_dependencies.yaml +++ b/coin/product_dependencies.yaml @@ -1,4 +1,4 @@ dependencies: - ../../qt/tqtc-qt5.git: - ref: "tqtc/lts-6.2" + ../../qt/qt5.git: + ref: "6.5" From dce30450a3762748535d1abcbe85ded60a05b6de Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Fri, 28 Apr 2023 17:55:03 +0300 Subject: [PATCH 125/192] QmlDesigner: Add UI for lights baking setup Instead of directly baking, bake lights action now opens baking setup dialog, where user can set up baking related properties for models and lights in the scene, including those exposed from subcomponents. Fixes: QDS-9805 Change-Id: I9e0bae98d8a79be4cf8b09ef989c41083a7fbf5d Reviewed-by: Mahmoud Badri --- ...ialog.qml => BakeLightsProgressDialog.qml} | 46 ++- .../edit3dQmlSource/BakeLightsSetupDialog.qml | 204 ++++++++++ src/plugins/qmldesigner/CMakeLists.txt | 1 + .../components/edit3d/bakelights.cpp | 212 +++++++---- .../components/edit3d/bakelights.h | 16 +- .../components/edit3d/bakelightsdatamodel.cpp | 357 ++++++++++++++++++ .../components/edit3d/bakelightsdatamodel.h | 51 +++ .../designercore/include/nodemetainfo.h | 1 + .../designercore/metainfo/nodemetainfo.cpp | 10 + .../designercore/model/texttomodelmerger.cpp | 3 +- .../projectstorage/commontypecache.h | 2 + 11 files changed, 813 insertions(+), 90 deletions(-) rename share/qtcreator/qmldesigner/edit3dQmlSource/{BakeLightsDialog.qml => BakeLightsProgressDialog.qml} (66%) create mode 100644 share/qtcreator/qmldesigner/edit3dQmlSource/BakeLightsSetupDialog.qml create mode 100644 src/plugins/qmldesigner/components/edit3d/bakelightsdatamodel.cpp create mode 100644 src/plugins/qmldesigner/components/edit3d/bakelightsdatamodel.h diff --git a/share/qtcreator/qmldesigner/edit3dQmlSource/BakeLightsDialog.qml b/share/qtcreator/qmldesigner/edit3dQmlSource/BakeLightsProgressDialog.qml similarity index 66% rename from share/qtcreator/qmldesigner/edit3dQmlSource/BakeLightsDialog.qml rename to share/qtcreator/qmldesigner/edit3dQmlSource/BakeLightsProgressDialog.qml index 9632c0b4a11..18494d5f327 100644 --- a/share/qtcreator/qmldesigner/edit3dQmlSource/BakeLightsDialog.qml +++ b/share/qtcreator/qmldesigner/edit3dQmlSource/BakeLightsProgressDialog.qml @@ -14,6 +14,18 @@ Rectangle { color: StudioTheme.Values.themePanelBackground + Connections { + target: rootView + function onProgress(msg) { + progressText.text += progressText.text === "" ? msg : "\n" + msg + scrollView.ensureVisible() + } + + function onFinished() { + cancelButton.text = qsTr("Close") + } + } + Column { id: col padding: 5 @@ -29,8 +41,9 @@ Rectangle { } Rectangle { + id: rect width: root.width - 16 - height: root.height - title.height - button.height - 20 + height: root.height - title.height - cancelButton.height - 20 color: StudioTheme.Values.themePanelBackground border.color: StudioTheme.Values.themeControlOutline @@ -66,25 +79,24 @@ Rectangle { } - Connections { - target: rootView - function onProgress(msg) { - progressText.text += progressText.text === "" ? msg : "\n" + msg - scrollView.ensureVisible() + Row { + spacing: StudioTheme.Values.dialogButtonSpacing + height: cancelButton.height + anchors.right: rect.right + + Button { + id: bakeAgainButton + text: qsTr("Bake Again") + anchors.margins: StudioTheme.Values.dialogButtonPadding + onClicked: rootView.rebake() } - function onFinished() { - button.text = qsTr("Close") + Button { + id: cancelButton + text: qsTr("Cancel") + anchors.margins: StudioTheme.Values.dialogButtonPadding + onClicked: rootView.cancel() } } - - Button { - id: button - text: qsTr("Cancel") - anchors.right: parent.right - anchors.margins: StudioTheme.Values.dialogButtonPadding - - onClicked: rootView.cancel() - } } } diff --git a/share/qtcreator/qmldesigner/edit3dQmlSource/BakeLightsSetupDialog.qml b/share/qtcreator/qmldesigner/edit3dQmlSource/BakeLightsSetupDialog.qml new file mode 100644 index 00000000000..ab4d89374be --- /dev/null +++ b/share/qtcreator/qmldesigner/edit3dQmlSource/BakeLightsSetupDialog.qml @@ -0,0 +1,204 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0 WITH Qt-GPL-exception-1.0 + +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import QtQuickDesignerTheme +import HelperWidgets +import StudioControls as StudioControls +import StudioTheme as StudioTheme + +Rectangle { + id: root + + color: StudioTheme.Values.themePanelBackground + + Column { + id: col + padding: 5 + leftPadding: 10 + spacing: 5 + + Text { + id: title + text: qsTr("Lights baking setup for 3D view: %1").arg(sceneId) + font.bold: true + font.pixelSize: StudioTheme.Values.myFontSize + color: StudioTheme.Values.themeTextColor + } + + Rectangle { + id: ctrlRect + width: root.width - 16 + height: root.height - title.height - manualCheckBox.height - 20 + + color: StudioTheme.Values.themePanelBackground + border.color: StudioTheme.Values.themeControlOutline + border.width: StudioTheme.Values.border + + ListView { + id: listView + + anchors.fill: parent + anchors.margins: 4 + + clip: true + + model: bakeModel + spacing: 5 + + delegate: Row { + spacing: 12 + + enabled: !manualCheckBox.checked + + Text { + text: nodeId + color: StudioTheme.Values.themeTextColor + width: 200 + clip: true + font.bold: isTitle + verticalAlignment: Qt.AlignVCenter + horizontalAlignment: Qt.AlignLeft + height: bakeModeCombo.height + leftPadding: isTitle ? 0 : 8 + } + + StudioControls.ComboBox { + id: bakeModeCombo + model: ListModel { + ListElement { text: qsTr("Baking Disabled"); value: "Light.BakeModeDisabled" } + ListElement { text: qsTr("Bake Indirect"); value: "Light.BakeModeIndirect" } + ListElement { text: qsTr("Bake All"); value: "Light.BakeModeAll" } + } + + visible: !isModel && !isTitle + textRole: "text" + valueRole: "value" + currentIndex: bakeMode === "Light.BakeModeAll" + ? 2 : bakeMode === "Light.BakeModeIndirect" ? 1 : 0 + actionIndicatorVisible: false + + hoverEnabled: true + ToolTip.visible: hovered + ToolTip.text: qsTr("The baking mode applied to this light.") + + onActivated: bakeMode = currentValue + } + + StudioControls.CheckBox { + visible: isModel && !isTitle + checked: inUse + text: qsTr("In Use") + actionIndicatorVisible: false + + hoverEnabled: true + ToolTip.visible: hovered + ToolTip.text: qsTr("If checked, this model contributes to baked lighting,\nfor example in form of casting shadows or indirect light.") + + onToggled: inUse = checked + } + + StudioControls.CheckBox { + visible: isModel && !isTitle + checked: isEnabled + text: qsTr("Enabled") + actionIndicatorVisible: false + + hoverEnabled: true + ToolTip.visible: hovered + ToolTip.text: qsTr("If checked, baked lightmap texture is generated and rendered for this model.") + + onToggled: isEnabled = checked + } + + Row { + width: resolutionLabel.width + resolutionValue.width + StudioTheme.Values.sectionRowSpacing + spacing: StudioTheme.Values.sectionRowSpacing + visible: isModel && !isTitle + Text { + id: resolutionLabel + text: qsTr("Resolution:") + color: enabled ? StudioTheme.Values.themeTextColor + : StudioTheme.Values.themeTextColorDisabled + verticalAlignment: Qt.AlignVCenter + horizontalAlignment: Qt.AlignRight + height: resolutionValue.height + } + + StudioControls.RealSpinBox { + id: resolutionValue + realFrom: 128 + realTo: 128000 + realValue: resolution + realStepSize: 128 + width: 60 + actionIndicatorVisible: false + + hoverEnabled: true + ToolTip.visible: hovered + ToolTip.text: qsTr("Generated lightmap resolution for this model.") + + onRealValueChanged: resolution = realValue + } + } + } + } + } + + Item { + height: manualCheckBox.height + width: ctrlRect.width + + StudioControls.CheckBox { + id: manualCheckBox + checked: false + text: qsTr("Setup baking manually") + actionIndicatorVisible: false + + hoverEnabled: true + ToolTip.visible: hovered + ToolTip.text: qsTr("If checked, baking settings above are not applied on close or bake.\nInstead, user is expected to set baking properties manually.") + } + + Row { + spacing: StudioTheme.Values.dialogButtonSpacing + height: manualCheckBox.height + anchors.right: parent.right + + Button { + id: cancelButton + text: qsTr("Cancel") + anchors.margins: StudioTheme.Values.dialogButtonPadding + height: manualCheckBox.height + onClicked: rootView.cancel() + } + + Button { + id: applyButton + text: qsTr("Apply & Close") + anchors.margins: StudioTheme.Values.dialogButtonPadding + height: manualCheckBox.height + onClicked: { + if (!manualCheckBox.checked) + rootView.apply() + rootView.cancel() + } + } + + Button { + id: bakeButton + text: qsTr("Bake") + anchors.margins: StudioTheme.Values.dialogButtonPadding + height: manualCheckBox.height + onClicked: { + if (!manualCheckBox.checked) + rootView.apply() + rootView.bakeLights() + } + } + } + } + } +} diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index 89bf1ff4e45..90155e94406 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -600,6 +600,7 @@ extend_qtc_plugin(QmlDesigner edit3dvisibilitytogglesmenu.cpp edit3dvisibilitytogglesmenu.h backgroundcolorselection.cpp backgroundcolorselection.h bakelights.cpp bakelights.h + bakelightsdatamodel.cpp bakelightsdatamodel.h bakelightsconnectionmanager.cpp bakelightsconnectionmanager.h edit3d.qrc ) diff --git a/src/plugins/qmldesigner/components/edit3d/bakelights.cpp b/src/plugins/qmldesigner/components/edit3d/bakelights.cpp index c44b49bb5c4..73e2f315b03 100644 --- a/src/plugins/qmldesigner/components/edit3d/bakelights.cpp +++ b/src/plugins/qmldesigner/components/edit3d/bakelights.cpp @@ -4,6 +4,7 @@ #include "bakelights.h" #include "abstractview.h" +#include "bakelightsdatamodel.h" #include "bakelightsconnectionmanager.h" #include "documentmanager.h" #include "modelnode.h" @@ -63,76 +64,19 @@ BakeLights::BakeLights(AbstractView *view) return; } - // Create folders for lightmaps if they do not exist - PropertyName loadPrefixPropName{"loadPrefix"}; - const QList bakedLightmapNodes = m_view->allModelNodesOfType( - m_view->model()->qtQuick3DBakedLightmapMetaInfo()); - Utils::FilePath currentPath = DocumentManager::currentFilePath().absolutePath(); - QSet pathSet; - for (const ModelNode &node : bakedLightmapNodes) { - if (node.hasVariantProperty(loadPrefixPropName)) { - QString prefix = node.variantProperty(loadPrefixPropName).value().toString(); - Utils::FilePath fp = Utils::FilePath::fromString(prefix); - if (fp.isRelativePath()) { - fp = currentPath.pathAppended(prefix); - if (!fp.exists()) - pathSet.insert(fp); - } - } - } - for (const Utils::FilePath &fp : std::as_const(pathSet)) - fp.createDir(); - - // Show non-modal progress dialog with cancel button - QString path = qmlSourcesPath() + "/BakeLightsDialog.qml"; - - m_dialog = new QQuickView; - m_dialog->setTitle(tr("Bake Lights")); - m_dialog->setResizeMode(QQuickView::SizeRootObjectToView); - m_dialog->setMinimumSize({150, 100}); - m_dialog->setWidth(800); - m_dialog->setHeight(400); - m_dialog->setFlags(Qt::Dialog); - m_dialog->setModality(Qt::NonModal); - m_dialog->engine()->addImportPath(propertyEditorResourcesPath() + "/imports"); - - m_dialog->rootContext()->setContextProperties({ - {"rootView", QVariant::fromValue(this)}, - {"sceneId", QVariant::fromValue(m_view3dId)} - }); - m_dialog->setSource(QUrl::fromLocalFile(path)); - m_dialog->installEventFilter(this); - m_dialog->show(); - - QTimer::singleShot(0, this, &BakeLights::bakeLights); + showSetupDialog(); } BakeLights::~BakeLights() { - if (m_connectionManager) { - m_connectionManager->setProgressCallback({}); - m_connectionManager->setFinishedCallback({}); - m_connectionManager->setCrashCallback({}); - } - - if (m_model) { - m_model->setNodeInstanceView({}); - m_model->setRewriterView({}); - m_model.reset(); - } - - delete m_dialog; - delete m_rewriterView; - delete m_nodeInstanceView; - delete m_connectionManager; + cleanup(); } -QString BakeLights::resolveView3dId(AbstractView *view) +ModelNode BakeLights::resolveView3dNode(AbstractView *view) { if (!view || !view->model()) return {}; - QString view3dId; ModelNode activeView3D; ModelNode activeScene = view->active3DSceneNode(); @@ -144,16 +88,26 @@ QString BakeLights::resolveView3dId(AbstractView *view) if (sceneParent.metaInfo().isQtQuick3DView3D()) activeView3D = sceneParent; } - view3dId = activeView3D.id(); + return activeView3D; } - return view3dId; + return {}; +} + +QString BakeLights::resolveView3dId(AbstractView *view) +{ + ModelNode activeView3D = resolveView3dNode(view); + + if (activeView3D.isValid()) + return activeView3D.id(); + + return {}; } void BakeLights::raiseDialog() { - if (m_dialog) - m_dialog->raise(); + if (m_progressDialog) + m_progressDialog->raise(); } void BakeLights::bakeLights() @@ -161,6 +115,9 @@ void BakeLights::bakeLights() if (!m_view || !m_view->model()) return; + m_setupDialog->hide(); + showProgressDialog(); + // Start baking process m_connectionManager = new BakeLightsConnectionManager; m_rewriterView = new RewriterView{m_view->externalDependencies(), RewriterView::Amend}; @@ -188,7 +145,7 @@ void BakeLights::bakeLights() || (!m_rewriterView->rootModelNode().metaInfo().isGraphicalItem() && !is3DRoot)) { emit progress(tr("Invalid root node, baking aborted.")); emit finished(); - m_dialog->raise(); + m_progressDialog->raise(); return; } @@ -199,7 +156,7 @@ void BakeLights::bakeLights() }; auto finishedCallback = [this](const QString &msg) { - m_dialog->raise(); + m_progressDialog->raise(); emit progress(msg); emit finished(); @@ -208,7 +165,7 @@ void BakeLights::bakeLights() }; auto crashCallback = [this]() { - m_dialog->raise(); + m_progressDialog->raise(); emit progress(tr("Baking process crashed, baking aborted.")); emit finished(); }; @@ -224,17 +181,132 @@ void BakeLights::bakeLights() m_nodeInstanceView->view3DAction(View3DActionType::SetBakeLightsView3D, m_view3dId); } +void BakeLights::apply() +{ + m_dataModel->apply(); + + // Create folders for lightmaps if they do not exist + PropertyName loadPrefixPropName{"loadPrefix"}; + const QList bakedLightmapNodes = m_view->allModelNodesOfType( + m_view->model()->qtQuick3DBakedLightmapMetaInfo()); + Utils::FilePath currentPath = DocumentManager::currentFilePath().absolutePath(); + QSet pathSet; + for (const ModelNode &node : bakedLightmapNodes) { + if (node.hasVariantProperty(loadPrefixPropName)) { + QString prefix = node.variantProperty(loadPrefixPropName).value().toString(); + Utils::FilePath fp = Utils::FilePath::fromString(prefix); + if (fp.isRelativePath()) { + fp = currentPath.pathAppended(prefix); + if (!fp.exists()) + pathSet.insert(fp); + } + } + } + for (const Utils::FilePath &fp : std::as_const(pathSet)) + fp.createDir(); +} + +void BakeLights::rebake() +{ + QTimer::singleShot(0, this, [this]() { + cleanup(); + showSetupDialog(); + }); +} + +void BakeLights::showSetupDialog() +{ + if (!m_dataModel) + m_dataModel = new BakeLightsDataModel(m_view); + + m_dataModel->reset(); + + if (!m_setupDialog) { + // Show non-modal progress dialog with cancel button + QString path = qmlSourcesPath() + "/BakeLightsSetupDialog.qml"; + + m_setupDialog = new QQuickView; + m_setupDialog->setTitle(tr("Lights Baking Setup")); + m_setupDialog->setResizeMode(QQuickView::SizeRootObjectToView); + m_setupDialog->setMinimumSize({550, 200}); + m_setupDialog->setWidth(550); + m_setupDialog->setHeight(400); + m_setupDialog->setFlags(Qt::Dialog); + m_setupDialog->setModality(Qt::ApplicationModal); + m_setupDialog->engine()->addImportPath(propertyEditorResourcesPath() + "/imports"); + + m_setupDialog->rootContext()->setContextProperties({ + {"rootView", QVariant::fromValue(this)}, + {"sceneId", QVariant::fromValue(m_view3dId)}, + {"bakeModel", QVariant::fromValue(m_dataModel.data())} + }); + m_setupDialog->setSource(QUrl::fromLocalFile(path)); + m_setupDialog->installEventFilter(this); + } + m_setupDialog->show(); +} + +void BakeLights::showProgressDialog() +{ + if (!m_progressDialog) { + // Show non-modal progress dialog with cancel button + QString path = qmlSourcesPath() + "/BakeLightsProgressDialog.qml"; + + m_progressDialog = new QQuickView; + m_progressDialog->setTitle(tr("Lights Baking Progress")); + m_progressDialog->setResizeMode(QQuickView::SizeRootObjectToView); + m_progressDialog->setMinimumSize({150, 100}); + m_progressDialog->setWidth(800); + m_progressDialog->setHeight(400); + m_progressDialog->setFlags(Qt::Dialog); + m_progressDialog->setModality(Qt::NonModal); + m_progressDialog->engine()->addImportPath(propertyEditorResourcesPath() + "/imports"); + + m_progressDialog->rootContext()->setContextProperties({ + {"rootView", QVariant::fromValue(this)}, + {"sceneId", QVariant::fromValue(m_view3dId)} + }); + m_progressDialog->setSource(QUrl::fromLocalFile(path)); + m_progressDialog->installEventFilter(this); + } + m_progressDialog->show(); +} + +void BakeLights::cleanup() +{ + if (m_connectionManager) { + m_connectionManager->setProgressCallback({}); + m_connectionManager->setFinishedCallback({}); + m_connectionManager->setCrashCallback({}); + } + + if (m_model) { + m_model->setNodeInstanceView({}); + m_model->setRewriterView({}); + m_model.reset(); + } + + delete m_setupDialog; + delete m_progressDialog; + delete m_rewriterView; + delete m_nodeInstanceView; + delete m_connectionManager; + delete m_dataModel; +} + void BakeLights::cancel() { - if (!m_dialog.isNull() && m_dialog->isVisible()) - m_dialog->close(); + if (!m_setupDialog.isNull() && m_setupDialog->isVisible()) + m_setupDialog->close(); + if (!m_progressDialog.isNull() && m_progressDialog->isVisible()) + m_progressDialog->close(); deleteLater(); } bool BakeLights::eventFilter(QObject *obj, QEvent *event) { - if (obj == m_dialog) { + if (obj == m_progressDialog || obj == m_setupDialog) { if (event->type() == QEvent::KeyPress) { QKeyEvent *keyEvent = static_cast(event); if (keyEvent->key() == Qt::Key_Escape) diff --git a/src/plugins/qmldesigner/components/edit3d/bakelights.h b/src/plugins/qmldesigner/components/edit3d/bakelights.h index d63ca0b3b82..a219454076f 100644 --- a/src/plugins/qmldesigner/components/edit3d/bakelights.h +++ b/src/plugins/qmldesigner/components/edit3d/bakelights.h @@ -2,6 +2,7 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0 #pragma once +#include "modelnode.h" #include "qmldesignercorelib_global.h" #include @@ -17,6 +18,7 @@ class AbstractView; class BakeLightsConnectionManager; class NodeInstanceView; class RewriterView; +class BakeLightsDataModel; class BakeLights : public QObject { @@ -27,9 +29,13 @@ public: ~BakeLights(); Q_INVOKABLE void cancel(); + Q_INVOKABLE void bakeLights(); + Q_INVOKABLE void apply(); + Q_INVOKABLE void rebake(); void raiseDialog(); + static ModelNode resolveView3dNode(AbstractView *view); static QString resolveView3dId(AbstractView *view); signals: @@ -40,13 +46,19 @@ protected: bool eventFilter(QObject *obj, QEvent *event) override; private: - void bakeLights(); + void showSetupDialog(); + void showProgressDialog(); + void cleanup(); + + // Separate dialogs for setup and progress, as setup needs to be modal + QPointer m_setupDialog; + QPointer m_progressDialog; - QPointer m_dialog; QPointer m_connectionManager; QPointer m_nodeInstanceView; QPointer m_rewriterView; QPointer m_view; + QPointer m_dataModel; ModelPointer m_model; QString m_view3dId; }; diff --git a/src/plugins/qmldesigner/components/edit3d/bakelightsdatamodel.cpp b/src/plugins/qmldesigner/components/edit3d/bakelightsdatamodel.cpp new file mode 100644 index 00000000000..e52d4403770 --- /dev/null +++ b/src/plugins/qmldesigner/components/edit3d/bakelightsdatamodel.cpp @@ -0,0 +1,357 @@ +// Copyright (C) 2023 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 "bakelightsdatamodel.h" + +#include "abstractview.h" +#include "bakelights.h" +#include "bindingproperty.h" +#include "enumeration.h" +#include "model.h" +#include "modelnode.h" +#include "nodelistproperty.h" +#include "nodemetainfo.h" +#include "variantproperty.h" + +#include + +#include + +namespace QmlDesigner { + + +BakeLightsDataModel::BakeLightsDataModel(AbstractView *view) + : QAbstractListModel(view) + , m_view(view) +{ +} + +BakeLightsDataModel::~BakeLightsDataModel() +{ +} + +int BakeLightsDataModel::rowCount(const QModelIndex &parent) const +{ + return m_dataList.count(); +} + +QHash BakeLightsDataModel::roleNames() const +{ + static const QHash roles { + {Qt::UserRole + 1, "nodeId"}, + {Qt::UserRole + 2, "isModel"}, + {Qt::UserRole + 3, "isEnabled"}, + {Qt::UserRole + 4, "inUse"}, + {Qt::UserRole + 5, "isTitle"}, + {Qt::UserRole + 6, "resolution"}, + {Qt::UserRole + 7, "bakeMode"} + }; + return roles; +} + +QVariant BakeLightsDataModel::data(const QModelIndex &index, int role) const +{ + QTC_ASSERT(index.isValid() && index.row() < m_dataList.count(), return {}); + QTC_ASSERT(roleNames().contains(role), return {}); + + QByteArray roleName = roleNames().value(role); + const BakeData &bakeData = m_dataList[index.row()]; + + if (roleName == "nodeId") { + const QString id = bakeData.id; + const PropertyName aliasProp = bakeData.aliasProp; + if (aliasProp.isEmpty()) + return id; + else + return QVariant{id + " - " + QString::fromUtf8(aliasProp)}; + return {}; + } + + if (roleName == "isModel") + return bakeData.isModel; + + if (roleName == "isEnabled") + return bakeData.enabled; + + if (roleName == "inUse") + return bakeData.inUse; + + if (roleName == "isTitle") + return bakeData.isTitle; + + if (roleName == "resolution") + return bakeData.resolution; + + if (roleName == "bakeMode") + return bakeData.bakeMode; + + return {}; +} + +bool BakeLightsDataModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ + QTC_ASSERT(index.isValid() && index.row() < m_dataList.count(), return false); + QTC_ASSERT(roleNames().contains(role), return false); + + QByteArray roleName = roleNames().value(role); + BakeData &bakeData = m_dataList[index.row()]; + + bool changed = false; + + if (roleName == "isEnabled") { + changed = bakeData.enabled != value.toBool(); + bakeData.enabled = value.toBool(); + } else if (roleName == "inUse") { + changed = bakeData.inUse != value.toBool(); + bakeData.inUse = value.toBool(); + } else if (roleName == "resolution") { + changed = bakeData.resolution != value.toInt(); + bakeData.resolution = value.toInt(); + } else if (roleName == "bakeMode") { + changed = bakeData.bakeMode != value.toString(); + bakeData.bakeMode = value.toString(); + } + + if (changed) + emit dataChanged(index, index, {role}); + + return changed; +} + +void BakeLightsDataModel::reset() +{ + if (!m_view || !m_view->model()) + return; + + beginResetModel(); + m_dataList.clear(); + + ModelNode view3dNode = BakeLights::resolveView3dNode(m_view); + + // Find all models and lights in active View3D + QList nodes = view3dNode.allSubModelNodes(); + + if (view3dNode.hasBindingProperty("importScene")) + nodes.append(view3dNode.bindingProperty("importScene").resolveToModelNode().allSubModelNodesAndThisNode()); + + QList modelList; + QList lightList; + QList compModelList; + QList compLightList; + + // Note: We are always loading base state values for baking. If users want to bake + // differently for different states, they need to setup things manually for now. + // Same goes if they want to use any unusual bindings in baking properties. + for (const auto &node : std::as_const(nodes)) { + BakeData data; + data.id = node.id(); + if (data.id.isEmpty()) + continue; // Skip nodes without id + + if (node.metaInfo().isQtQuick3DModel()) { + data.isModel = true; + if (node.hasBindingProperty("bakedLightmap")) { + ModelNode blm = node.bindingProperty("bakedLightmap").resolveToModelNode(); + if (blm.isValid()) { + if (blm.hasVariantProperty("enabled")) + data.enabled = blm.variantProperty("enabled").value().toBool(); + else + data.enabled = true; + } + } + if (node.hasVariantProperty("lightmapBaseResolution")) + data.resolution = node.variantProperty("lightmapBaseResolution").value().toInt(); + if (node.hasVariantProperty("usedInBakedLighting")) + data.inUse = node.variantProperty("usedInBakedLighting").value().toBool(); + modelList.append(data); + } else if (node.metaInfo().isQtQuick3DLight()) { + if (node.hasVariantProperty("bakeMode")) { + data.bakeMode = node.variantProperty("bakeMode").value() + .value().toString(); + } else { + data.bakeMode = "Light.BakeModeDisabled"; + } + lightList.append(data); + } + + if (node.isComponent()) { + // Every component can expose multiple aliases + // We ignore baking properties defined inside the component (no visibility there) + const QList props = node.properties(); + PropertyMetaInfos metaInfos = node.metaInfo().properties(); + for (const PropertyMetaInfo &mi : metaInfos) { + if (mi.isValid() && !mi.isPrivate() && mi.isWritable()) { + BakeData propData; + propData.id = data.id; + propData.aliasProp = mi.name(); + if (mi.propertyType().isQtQuick3DModel()) { + propData.isModel = true; + PropertyName dotName = mi.name() + '.'; + for (const AbstractProperty &prop : props) { + if (prop.name().startsWith(dotName)) { + PropertyName subName = prop.name().mid(dotName.size()); + if (subName == "bakedLightmap") { + ModelNode blm = prop.toBindingProperty().resolveToModelNode(); + if (blm.isValid()) { + if (blm.hasVariantProperty("enabled")) + propData.enabled = blm.variantProperty("enabled").value().toBool(); + else + propData.enabled = true; + } + } + if (subName == "lightmapBaseResolution") + propData.resolution = prop.toVariantProperty().value().toInt(); + if (subName == "usedInBakedLighting") + propData.inUse = prop.toVariantProperty().value().toBool(); + } + } + compModelList.append(propData); + } else if (mi.propertyType().isQtQuick3DLight()) { + PropertyName dotName = mi.name() + '.'; + for (const AbstractProperty &prop : props) { + if (prop.name().startsWith(dotName)) { + PropertyName subName = prop.name().mid(dotName.size()); + if (subName == "bakeMode") { + propData.bakeMode = prop.toVariantProperty().value() + .value() + .toString(); + } + } + } + if (propData.bakeMode.isEmpty()) + propData.bakeMode = "Light.BakeModeDisabled"; + compLightList.append(propData); + } + } + } + } + } + + auto sortList = [](QList &list) { + std::sort(list.begin(), list.end(), [](const BakeData &a, const BakeData &b) { + return a.id.compare(b.id) < 0; + }); + }; + + sortList(modelList); + sortList(lightList); + sortList(compModelList); + sortList(compLightList); + + BakeData titleData; + titleData.isTitle = true; + titleData.id = tr("Lights"); + m_dataList.append(titleData); + m_dataList.append(lightList); + m_dataList.append(compLightList); + titleData.id = tr("Models"); + m_dataList.append(titleData); + m_dataList.append(modelList); + m_dataList.append(compModelList); + + endResetModel(); +} + +void BakeLightsDataModel::apply() +{ + if (!m_view || !m_view->model()) + return; + + auto setBakedLightmap = [this](const ModelNode &node, const BakeData &data) { + ModelNode blmNode; + PropertyName propName{"bakedLightmap"}; + if (!data.aliasProp.isEmpty()) + propName.prepend(data.aliasProp + '.'); + if (node.hasBindingProperty(propName)) + blmNode = node.bindingProperty(propName).resolveToModelNode(); + if (!blmNode.isValid() && data.enabled) { + NodeMetaInfo metaInfo = m_view->model()->qtQuick3DBakedLightmapMetaInfo(); + blmNode = m_view->createModelNode("QtQuick3D.BakedLightmap", + metaInfo.majorVersion(), + metaInfo.minorVersion()); + QString idPart; + if (data.aliasProp.isEmpty()) + idPart = data.id; + else + idPart = QStringLiteral("%1_%2").arg(data.id, QString::fromUtf8(data.aliasProp)); + QString newId = m_view->model()->generateNewId(QStringLiteral("blm_%1").arg(idPart)); + blmNode.setIdWithoutRefactoring(newId); + node.defaultNodeListProperty().reparentHere(blmNode); + node.bindingProperty(propName).setExpression(newId); + } + if (blmNode.isValid()) { + VariantProperty enabledProp = blmNode.variantProperty("enabled"); + VariantProperty prefixProp = blmNode.variantProperty("loadPrefix"); + VariantProperty keyProp = blmNode.variantProperty("key"); + enabledProp.setValue(data.enabled); + prefixProp.setValue(commonPrefix()); + keyProp.setValue(blmNode.id()); + } + }; + + auto setVariantProp = [](const ModelNode &node, + const PropertyName &propName, + const PropertyName &aliasProp, + const QVariant &value, + const QVariant &defaultValue) { + PropertyName resolvedName = propName; + if (!aliasProp.isEmpty()) + resolvedName.prepend(aliasProp + '.'); + if (node.hasVariantProperty(resolvedName) || value != defaultValue) + node.variantProperty(resolvedName).setValue(value); + }; + + auto setResolution = [setVariantProp](const ModelNode &node, const BakeData &data) { + setVariantProp(node, "lightmapBaseResolution", data.aliasProp, data.resolution, 1024); + }; + + auto setInUse = [setVariantProp](const ModelNode &node, const BakeData &data) { + setVariantProp(node, "usedInBakedLighting", data.aliasProp, data.inUse, false); + }; + + auto setBakeMode = [setVariantProp](const ModelNode &node, const BakeData &data) { + setVariantProp(node, "bakeMode", data.aliasProp, + QVariant::fromValue(QmlDesigner::Enumeration(data.bakeMode)), + QVariant::fromValue(QmlDesigner::Enumeration("Light", "BakeModeDisabled"))); + }; + + // Commits changes to scene model + m_view->executeInTransaction(__FUNCTION__, [&]() { + for (const BakeData &data : std::as_const(m_dataList)) { + if (data.isTitle) + continue; + ModelNode node = m_view->modelNodeForId(data.id); + if (data.isModel) { + setBakedLightmap(node, data); + setResolution(node, data); + setInUse(node, data); + } else { + setBakeMode(node, data); + } + } + }); +} + +QString BakeLightsDataModel::commonPrefix() +{ + static QString prefix = "lightmaps"; + return prefix; +} + +QDebug operator<<(QDebug debug, const BakeLightsDataModel::BakeData &data) +{ + QDebugStateSaver saver(debug); + debug.space() << "(" + << "id:" << data.id + << "aliasProp:" << data.aliasProp + << "isModel:" << data.isModel + << "enabled:" << data.enabled + << "inUse:" << data.inUse + << "resolution:" << data.resolution + << "bakeMode:" << data.bakeMode + << "isTitle:" << data.isTitle + << ")"; + return debug; +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/edit3d/bakelightsdatamodel.h b/src/plugins/qmldesigner/components/edit3d/bakelightsdatamodel.h new file mode 100644 index 00000000000..9217ae44bbf --- /dev/null +++ b/src/plugins/qmldesigner/components/edit3d/bakelightsdatamodel.h @@ -0,0 +1,51 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0 +#pragma once + +#include "qmldesignercorelib_global.h" + +#include +#include +#include + +namespace QmlDesigner { + +class AbstractView; + +class BakeLightsDataModel : public QAbstractListModel +{ + Q_OBJECT + +public: + struct BakeData { + QString id; // node id. Also used as BakedLightmap.key + PropertyName aliasProp; // property id for component exposed models/lights + bool isModel = false; // false means light + bool enabled = false; + bool inUse = false; + bool isTitle = false; // if true, indicates a title row in UI + int resolution = 1024; + QString bakeMode; + }; + + BakeLightsDataModel(AbstractView *view); + ~BakeLightsDataModel() override; + + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + QHash roleNames() const override; + bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override; + + void reset(); + void apply(); + +private: + QString commonPrefix(); + + QPointer m_view; + QList m_dataList; +}; + +QDebug operator<<(QDebug debug, const BakeLightsDataModel::BakeData &data); + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/include/nodemetainfo.h b/src/plugins/qmldesigner/designercore/include/nodemetainfo.h index 4c692565fd4..8642a03875a 100644 --- a/src/plugins/qmldesigner/designercore/include/nodemetainfo.h +++ b/src/plugins/qmldesigner/designercore/include/nodemetainfo.h @@ -124,6 +124,7 @@ public: bool isQtQuick3DEffect() const; bool isQtQuick3DInstanceList() const; bool isQtQuick3DInstanceListEntry() const; + bool isQtQuick3DLight() 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 aafb355b68e..770d919d71c 100644 --- a/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp +++ b/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp @@ -2265,6 +2265,16 @@ bool NodeMetaInfo::isQtQuick3DInstanceListEntry() const } } +bool NodeMetaInfo::isQtQuick3DLight() const +{ + if constexpr (useProjectStorage()) { + using namespace Storage::Info; + return isBasedOnCommonType(m_projectStorage, m_typeId); + } else { + return isValid() && isSubclassOf("QtQuick3D.Light"); + } +} + bool NodeMetaInfo::isQtQuick3DInstanceList() const { if constexpr (useProjectStorage()) { diff --git a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp index 493c834885a..fe9db55ce63 100644 --- a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp +++ b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp @@ -101,7 +101,8 @@ QStringList knownEnumScopes() "Grid", "ItemLayer", "ImageLayer", - "SpriteLayer"}; + "SpriteLayer", + "Light"}; return list; } diff --git a/src/plugins/qmldesigner/designercore/projectstorage/commontypecache.h b/src/plugins/qmldesigner/designercore/projectstorage/commontypecache.h index 3cd7f3a0167..eda1daba5f5 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/commontypecache.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/commontypecache.h @@ -49,6 +49,7 @@ inline constexpr char GroupItem[] = "GroupItem"; inline constexpr char Image[] = "Image"; inline constexpr char InstanceListEntry[] = "InstanceListEntry"; inline constexpr char InstanceList[] = "InstanceList"; +inline constexpr char Light[] = "Light"; inline constexpr char IntType[] = "int"; inline constexpr char Item[] = "Item"; inline constexpr char KeyframeGroup[] = "KeyframeGroup"; @@ -194,6 +195,7 @@ class CommonTypeCache CacheType, CacheType, CacheType, + CacheType, CacheType, CacheType, CacheType, From 45599845e82cbc2eba14bf63595f58110b08ecde Mon Sep 17 00:00:00 2001 From: Aleksei German Date: Wed, 3 May 2023 16:54:21 +0200 Subject: [PATCH 126/192] QmlDesigner: Fix for Qt for MCUs metadata, sheets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Includes: - adds metadata sheets for newer version - bumps metadata.qml default version - tweaks to allowed properties and types - some resorting and minor reformating of the metadata - property editor sheet mcu-related changes for Timer Task-number: QDS-9823 Change-Id: Iedb27047c533b80ba008afeee1ea28a99c8ab19d Reviewed-by: Thomas Hartmann Reviewed-by: Henning Gründl --- .../QtQml/TimerSpecifics.qml | 3 + .../qtcreator/qmldesigner/qt4mcu/metadata.qml | 14 +- share/qtcreator/qmldesigner/qt4mcu/qul-14.qml | 29 ++- share/qtcreator/qmldesigner/qt4mcu/qul-17.qml | 29 ++- share/qtcreator/qmldesigner/qt4mcu/qul-18.qml | 33 ++- share/qtcreator/qmldesigner/qt4mcu/qul-19.qml | 33 ++- share/qtcreator/qmldesigner/qt4mcu/qul-20.qml | 33 ++- share/qtcreator/qmldesigner/qt4mcu/qul-21.qml | 36 +-- share/qtcreator/qmldesigner/qt4mcu/qul-22.qml | 37 +-- share/qtcreator/qmldesigner/qt4mcu/qul-23.qml | 211 ++++++++++++++++++ share/qtcreator/qmldesigner/qt4mcu/qul-24.qml | 210 +++++++++++++++++ 11 files changed, 580 insertions(+), 88 deletions(-) create mode 100644 share/qtcreator/qmldesigner/qt4mcu/qul-23.qml create mode 100644 share/qtcreator/qmldesigner/qt4mcu/qul-24.qml diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQml/TimerSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQml/TimerSpecifics.qml index 9ff90a2b9fa..8be28295d67 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQml/TimerSpecifics.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQml/TimerSpecifics.qml @@ -92,14 +92,17 @@ Column { PropertyLabel { text: qsTr("Triggered on start") tooltip: qsTr("Sets the timer to trigger when started.") + blockedByTemplate: !triggeredOnStartComboBox.enabled } SecondColumnLayout { CheckBox { + id: triggeredOnStartComboBox text: backendValues.triggeredOnStart.valueToString implicitWidth: StudioTheme.Values.twoControlColumnWidth + StudioTheme.Values.actionIndicatorWidth backendValue: backendValues.triggeredOnStart + enabled: backendValues.triggeredOnStart.isAvailable } ExpandingSpacer {} diff --git a/share/qtcreator/qmldesigner/qt4mcu/metadata.qml b/share/qtcreator/qmldesigner/qt4mcu/metadata.qml index c5ff67fb0b1..d4da42cc0a1 100644 --- a/share/qtcreator/qmldesigner/qt4mcu/metadata.qml +++ b/share/qtcreator/qmldesigner/qt4mcu/metadata.qml @@ -5,7 +5,7 @@ Metadata { id: metadataFile - defaultVersion: v22 + defaultVersion: v24 VersionData { id: v14 @@ -48,4 +48,16 @@ Metadata { name: "Qt for MCUs 2.2" path: "qul-22.qml" } + + VersionData { + id: v23 + name: "Qt for MCUs 2.3" + path: "qul-23.qml" + } + + VersionData { + id: v24 + name: "Qt for MCUs 2.4" + path: "qul-24.qml" + } } diff --git a/share/qtcreator/qmldesigner/qt4mcu/qul-14.qml b/share/qtcreator/qmldesigner/qt4mcu/qul-14.qml index 376f83d2261..2fedac684d1 100644 --- a/share/qtcreator/qmldesigner/qt4mcu/qul-14.qml +++ b/share/qtcreator/qmldesigner/qt4mcu/qul-14.qml @@ -4,21 +4,23 @@ VersionData { name: "Qt for MCUs 1.4" - bannedItems: ["QtQuick.AnimatedImage", - "QtQuick.FocusScope", - "QtQuick.TextInput", - "QtQuick.TextEdit", + bannedItems: [ + "QtQuick.AnimatedImage", + "QtQuick.AnimatedSprite", "QtQuick.Flow", + "QtQuick.FocusScope", "QtQuick.Grid", "QtQuick.GridView", "QtQuick.PathView", + "QtQuick.TextEdit", + "QtQuick.TextInput", "QtQuick.Loader", "QtQuick.Controls", "QtQuick.Controls.BusyIndicator", "QtQuick.Controls.ButtonGroup", "QtQuick.Controls.CheckDelegate", - "QtQuick.Controls.Container", "QtQuick.Controls.ComboBox", + "QtQuick.Controls.Container", "QtQuick.Controls.DelayButton", "QtQuick.Controls.Frame", "QtQuick.Controls.GroupBox", @@ -35,18 +37,21 @@ VersionData { "QtQuick.Controls.StackView", "QtQuick.Controls.SwipeDelegate", "QtQuick.Controls.SwitchDelegate", - "QtQuick.Controls.ToolBar", - "QtQuick.Controls.ToolButton", "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.Controls.Tumbler" + ] - allowedImports: ["QtQuick", + allowedImports: [ + "QtQuick", "QtQuick.Controls", - "QtQuick.Timeline"] + "QtQuick.Timeline" + ] bannedImports: ["FlowView"] @@ -58,6 +63,10 @@ VersionData { "strikeout", "underline", "styleName"] } + QtQml.Timer { + bannedProperties: ["triggeredOnStart"] + } + QtQuick.Item { bannedProperties: ["layer", "opacity", "smooth", "antialiasing", "baselineOffset", "focus", "activeFocusOnTab", diff --git a/share/qtcreator/qmldesigner/qt4mcu/qul-17.qml b/share/qtcreator/qmldesigner/qt4mcu/qul-17.qml index 3a39626e745..95e10ad2e2d 100644 --- a/share/qtcreator/qmldesigner/qt4mcu/qul-17.qml +++ b/share/qtcreator/qmldesigner/qt4mcu/qul-17.qml @@ -4,21 +4,23 @@ VersionData { name: "Qt for MCUs 1.7" - bannedItems: ["QtQuick.AnimatedImage", - "QtQuick.FocusScope", - "QtQuick.TextInput", - "QtQuick.TextEdit", + bannedItems: [ + "QtQuick.AnimatedImage", + "QtQuick.AnimatedSprite", "QtQuick.Flow", + "QtQuick.FocusScope", "QtQuick.Grid", "QtQuick.GridView", "QtQuick.PathView", + "QtQuick.TextEdit", + "QtQuick.TextInput", "QtQuick.Loader", "QtQuick.Controls", "QtQuick.Controls.BusyIndicator", "QtQuick.Controls.ButtonGroup", "QtQuick.Controls.CheckDelegate", - "QtQuick.Controls.Container", "QtQuick.Controls.ComboBox", + "QtQuick.Controls.Container", "QtQuick.Controls.DelayButton", "QtQuick.Controls.Frame", "QtQuick.Controls.GroupBox", @@ -35,20 +37,23 @@ VersionData { "QtQuick.Controls.StackView", "QtQuick.Controls.SwipeDelegate", "QtQuick.Controls.SwitchDelegate", - "QtQuick.Controls.ToolBar", - "QtQuick.Controls.ToolButton", "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.Controls.Tumbler" + ] - allowedImports: ["QtQuick", + allowedImports: [ + "QtQuick", "QtQuick.Controls", "QtQuick.Timeline", "QtQuickUltralite.Extras", - "QtQuickUltralite.Layers"] + "QtQuickUltralite.Layers" + ] bannedImports: ["FlowView"] @@ -60,6 +65,10 @@ VersionData { "strikeout", "underline", "styleName"] } + QtQml.Timer { + bannedProperties: ["triggeredOnStart"] + } + QtQuick.Item { bannedProperties: ["layer", "opacity", "smooth", "antialiasing", "baselineOffset", "focus", "activeFocusOnTab", diff --git a/share/qtcreator/qmldesigner/qt4mcu/qul-18.qml b/share/qtcreator/qmldesigner/qt4mcu/qul-18.qml index 2d501265e2d..5e3a76277cf 100644 --- a/share/qtcreator/qmldesigner/qt4mcu/qul-18.qml +++ b/share/qtcreator/qmldesigner/qt4mcu/qul-18.qml @@ -4,21 +4,23 @@ VersionData { name: "Qt for MCUs 1.8" - bannedItems: ["QtQuick.AnimatedImage", - "QtQuick.FocusScope", - "QtQuick.TextInput", - "QtQuick.TextEdit", + bannedItems: [ + "QtQuick.AnimatedImage", + "QtQuick.AnimatedSprite", "QtQuick.Flow", + "QtQuick.FocusScope", "QtQuick.Grid", "QtQuick.GridView", - "QtQuick.PathView", "QtQuick.Loader", + "QtQuick.PathView", + "QtQuick.TextEdit", + "QtQuick.TextInput", "QtQuick.Controls", "QtQuick.Controls.BusyIndicator", "QtQuick.Controls.ButtonGroup", "QtQuick.Controls.CheckDelegate", - "QtQuick.Controls.Container", "QtQuick.Controls.ComboBox", + "QtQuick.Controls.Container", "QtQuick.Controls.DelayButton", "QtQuick.Controls.Frame", "QtQuick.Controls.GroupBox", @@ -35,25 +37,28 @@ VersionData { "QtQuick.Controls.StackView", "QtQuick.Controls.SwipeDelegate", "QtQuick.Controls.SwitchDelegate", - "QtQuick.Controls.ToolBar", - "QtQuick.Controls.ToolButton", "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.LinearGradient", "QtQuick.Shapes.RadialGradient", - "QtQuick.Shapes.ShapeGradient"] + "QtQuick.Shapes.ShapeGradient" + ] - allowedImports: ["QtQuick", - "QtQuick.Shapes", + allowedImports: [ + "QtQuick", "QtQuick.Controls", + "QtQuick.Shapes", "QtQuick.Timeline", "QtQuickUltralite.Extras", - "QtQuickUltralite.Layers"] + "QtQuickUltralite.Layers" + ] bannedImports: ["FlowView"] @@ -65,6 +70,10 @@ VersionData { "strikeout", "underline", "styleName"] } + QtQml.Timer { + bannedProperties: ["triggeredOnStart"] + } + QtQuick.Item { bannedProperties: ["layer", "opacity", "smooth", "antialiasing", "baselineOffset", "focus", "activeFocusOnTab", diff --git a/share/qtcreator/qmldesigner/qt4mcu/qul-19.qml b/share/qtcreator/qmldesigner/qt4mcu/qul-19.qml index 249e27768c2..8f8cb78921f 100644 --- a/share/qtcreator/qmldesigner/qt4mcu/qul-19.qml +++ b/share/qtcreator/qmldesigner/qt4mcu/qul-19.qml @@ -4,21 +4,23 @@ VersionData { name: "Qt for MCUs 1.9" - bannedItems: ["QtQuick.AnimatedImage", - "QtQuick.FocusScope", - "QtQuick.TextInput", - "QtQuick.TextEdit", + bannedItems: [ + "QtQuick.AnimatedImage", + "QtQuick.AnimatedSprite", "QtQuick.Flow", + "QtQuick.FocusScope", "QtQuick.Grid", "QtQuick.GridView", - "QtQuick.PathView", "QtQuick.Loader", + "QtQuick.PathView", + "QtQuick.TextEdit", + "QtQuick.TextInput", "QtQuick.Controls", "QtQuick.Controls.BusyIndicator", "QtQuick.Controls.ButtonGroup", "QtQuick.Controls.CheckDelegate", - "QtQuick.Controls.Container", "QtQuick.Controls.ComboBox", + "QtQuick.Controls.Container", "QtQuick.Controls.DelayButton", "QtQuick.Controls.Frame", "QtQuick.Controls.GroupBox", @@ -35,25 +37,28 @@ VersionData { "QtQuick.Controls.StackView", "QtQuick.Controls.SwipeDelegate", "QtQuick.Controls.SwitchDelegate", - "QtQuick.Controls.ToolBar", - "QtQuick.Controls.ToolButton", "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.LinearGradient", "QtQuick.Shapes.RadialGradient", - "QtQuick.Shapes.ShapeGradient"] + "QtQuick.Shapes.ShapeGradient" + ] - allowedImports: ["QtQuick", - "QtQuick.Shapes", + allowedImports: [ + "QtQuick", "QtQuick.Controls", + "QtQuick.Shapes", "QtQuick.Timeline", "QtQuickUltralite.Extras", - "QtQuickUltralite.Layers"] + "QtQuickUltralite.Layers" + ] bannedImports: ["FlowView"] @@ -65,6 +70,10 @@ VersionData { "strikeout", "underline", "styleName"] } + QtQml.Timer { + bannedProperties: ["triggeredOnStart"] + } + QtQuick.Item { bannedProperties: ["layer", "opacity", "smooth", "antialiasing", "baselineOffset", "focus", "activeFocusOnTab", diff --git a/share/qtcreator/qmldesigner/qt4mcu/qul-20.qml b/share/qtcreator/qmldesigner/qt4mcu/qul-20.qml index 143d167b930..07bee0e403e 100644 --- a/share/qtcreator/qmldesigner/qt4mcu/qul-20.qml +++ b/share/qtcreator/qmldesigner/qt4mcu/qul-20.qml @@ -4,21 +4,23 @@ VersionData { name: "Qt for MCUs 2.0" - bannedItems: ["QtQuick.AnimatedImage", - "QtQuick.FocusScope", - "QtQuick.TextInput", - "QtQuick.TextEdit", + bannedItems: [ + "QtQuick.AnimatedImage", + "QtQuick.AnimatedSprite", "QtQuick.Flow", + "QtQuick.FocusScope", "QtQuick.Grid", "QtQuick.GridView", - "QtQuick.PathView", "QtQuick.Loader", + "QtQuick.PathView", + "QtQuick.TextEdit", + "QtQuick.TextInput", "QtQuick.Controls", "QtQuick.Controls.BusyIndicator", "QtQuick.Controls.ButtonGroup", "QtQuick.Controls.CheckDelegate", - "QtQuick.Controls.Container", "QtQuick.Controls.ComboBox", + "QtQuick.Controls.Container", "QtQuick.Controls.DelayButton", "QtQuick.Controls.Frame", "QtQuick.Controls.GroupBox", @@ -35,25 +37,28 @@ VersionData { "QtQuick.Controls.StackView", "QtQuick.Controls.SwipeDelegate", "QtQuick.Controls.SwitchDelegate", - "QtQuick.Controls.ToolBar", - "QtQuick.Controls.ToolButton", "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.LinearGradient", "QtQuick.Shapes.RadialGradient", - "QtQuick.Shapes.ShapeGradient"] + "QtQuick.Shapes.ShapeGradient" + ] - allowedImports: ["QtQuick", - "QtQuick.Shapes", + allowedImports: [ + "QtQuick", "QtQuick.Controls", + "QtQuick.Shapes", "QtQuick.Timeline", "QtQuickUltralite.Extras", - "QtQuickUltralite.Layers"] + "QtQuickUltralite.Layers" + ] bannedImports: ["FlowView"] @@ -65,6 +70,10 @@ VersionData { "strikeout", "underline", "styleName"] } + QtQml.Timer { + bannedProperties: ["triggeredOnStart"] + } + QtQuick.Item { bannedProperties: ["layer", "opacity", "smooth", "antialiasing", "baselineOffset", "focus", "activeFocusOnTab", diff --git a/share/qtcreator/qmldesigner/qt4mcu/qul-21.qml b/share/qtcreator/qmldesigner/qt4mcu/qul-21.qml index 1d2663b8a52..5f7de7a71f6 100644 --- a/share/qtcreator/qmldesigner/qt4mcu/qul-21.qml +++ b/share/qtcreator/qmldesigner/qt4mcu/qul-21.qml @@ -1,27 +1,26 @@ // Copyright (C) 2021 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 -//differences from 2.0: -//text.elide is introduced in QUL - VersionData { name: "Qt for MCUs 2.1" - bannedItems: ["QtQuick.AnimatedImage", - "QtQuick.FocusScope", - "QtQuick.TextInput", - "QtQuick.TextEdit", + bannedItems: [ + "QtQuick.AnimatedImage", + "QtQuick.AnimatedSprite", "QtQuick.Flow", + "QtQuick.FocusScope", "QtQuick.Grid", "QtQuick.GridView", - "QtQuick.PathView", "QtQuick.Loader", + "QtQuick.PathView", + "QtQuick.TextEdit", + "QtQuick.TextInput", "QtQuick.Controls", "QtQuick.Controls.BusyIndicator", "QtQuick.Controls.ButtonGroup", "QtQuick.Controls.CheckDelegate", - "QtQuick.Controls.Container", "QtQuick.Controls.ComboBox", + "QtQuick.Controls.Container", "QtQuick.Controls.DelayButton", "QtQuick.Controls.Frame", "QtQuick.Controls.GroupBox", @@ -38,25 +37,28 @@ VersionData { "QtQuick.Controls.StackView", "QtQuick.Controls.SwipeDelegate", "QtQuick.Controls.SwitchDelegate", - "QtQuick.Controls.ToolBar", - "QtQuick.Controls.ToolButton", "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.LinearGradient", "QtQuick.Shapes.RadialGradient", - "QtQuick.Shapes.ShapeGradient"] + "QtQuick.Shapes.ShapeGradient" + ] - allowedImports: ["QtQuick", - "QtQuick.Shapes", + allowedImports: [ + "QtQuick", "QtQuick.Controls", + "QtQuick.Shapes", "QtQuick.Timeline", "QtQuickUltralite.Extras", - "QtQuickUltralite.Layers"] + "QtQuickUltralite.Layers" + ] bannedImports: ["FlowView"] @@ -68,6 +70,10 @@ VersionData { "strikeout", "underline", "styleName"] } + QtQml.Timer { + bannedProperties: ["triggeredOnStart"] + } + QtQuick.Item { bannedProperties: ["layer", "opacity", "smooth", "antialiasing", "baselineOffset", "focus", "activeFocusOnTab", diff --git a/share/qtcreator/qmldesigner/qt4mcu/qul-22.qml b/share/qtcreator/qmldesigner/qt4mcu/qul-22.qml index 8dbf0e0f120..b5c5c924b81 100644 --- a/share/qtcreator/qmldesigner/qt4mcu/qul-22.qml +++ b/share/qtcreator/qmldesigner/qt4mcu/qul-22.qml @@ -1,28 +1,26 @@ // Copyright (C) 2022 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 -//differences from 2.0: -//2.1: + text.elide -//2.2: + text.wrapMode - VersionData { name: "Qt for MCUs 2.2" - bannedItems: ["QtQuick.AnimatedImage", - "QtQuick.FocusScope", - "QtQuick.TextInput", - "QtQuick.TextEdit", + bannedItems: [ + "QtQuick.AnimatedImage", + "QtQuick.AnimatedSprite", "QtQuick.Flow", + "QtQuick.FocusScope", "QtQuick.Grid", "QtQuick.GridView", - "QtQuick.PathView", "QtQuick.Loader", + "QtQuick.PathView", + "QtQuick.TextEdit", + "QtQuick.TextInput", "QtQuick.Controls", "QtQuick.Controls.BusyIndicator", "QtQuick.Controls.ButtonGroup", "QtQuick.Controls.CheckDelegate", - "QtQuick.Controls.Container", "QtQuick.Controls.ComboBox", + "QtQuick.Controls.Container", "QtQuick.Controls.DelayButton", "QtQuick.Controls.Frame", "QtQuick.Controls.GroupBox", @@ -39,25 +37,28 @@ VersionData { "QtQuick.Controls.StackView", "QtQuick.Controls.SwipeDelegate", "QtQuick.Controls.SwitchDelegate", - "QtQuick.Controls.ToolBar", - "QtQuick.Controls.ToolButton", "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.LinearGradient", "QtQuick.Shapes.RadialGradient", - "QtQuick.Shapes.ShapeGradient"] + "QtQuick.Shapes.ShapeGradient" + ] - allowedImports: ["QtQuick", - "QtQuick.Shapes", + allowedImports: [ + "QtQuick", "QtQuick.Controls", + "QtQuick.Shapes", "QtQuick.Timeline", "QtQuickUltralite.Extras", - "QtQuickUltralite.Layers"] + "QtQuickUltralite.Layers" + ] bannedImports: ["FlowView"] @@ -69,6 +70,10 @@ VersionData { "strikeout", "underline", "styleName"] } + QtQml.Timer { + bannedProperties: ["triggeredOnStart"] + } + QtQuick.Item { bannedProperties: ["layer", "opacity", "smooth", "antialiasing", "baselineOffset", "focus", "activeFocusOnTab", diff --git a/share/qtcreator/qmldesigner/qt4mcu/qul-23.qml b/share/qtcreator/qmldesigner/qt4mcu/qul-23.qml new file mode 100644 index 00000000000..0c55d7db3a4 --- /dev/null +++ b/share/qtcreator/qmldesigner/qt4mcu/qul-23.qml @@ -0,0 +1,211 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +VersionData { + name: "Qt for MCUs 2.3" + + bannedItems: [ + "QtQuick.AnimatedImage", + "QtQuick.AnimatedSprite", + "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.LinearGradient", + "QtQuick.Shapes.RadialGradient", + "QtQuick.Shapes.ShapeGradient" + ] + + allowedImports: [ + "QtQuick", + "QtQuick.Controls", + "QtQuick.Shapes", + "QtQuick.Timeline", + "QtQuickUltralite.Extras", + "QtQuickUltralite.Layers" + ] + + bannedImports: ["FlowView"] + + //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: ["gradient", "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", "textFormat", "maximumLineCount"] + } + + QtQuick.Loader { + bannedProperties: ["asynchronous", "progress", "sourceComponent", "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"] + } + + //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", + "fillGradient", "strokeStyle"] + } +} diff --git a/share/qtcreator/qmldesigner/qt4mcu/qul-24.qml b/share/qtcreator/qmldesigner/qt4mcu/qul-24.qml new file mode 100644 index 00000000000..f78b00b8c65 --- /dev/null +++ b/share/qtcreator/qmldesigner/qt4mcu/qul-24.qml @@ -0,0 +1,210 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +VersionData { + name: "Qt for MCUs 2.4" + + 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.LinearGradient", + "QtQuick.Shapes.RadialGradient", + "QtQuick.Shapes.ShapeGradient" + ] + + allowedImports: [ + "QtQuick", + "QtQuick.Controls", + "QtQuick.Shapes", + "QtQuick.Timeline", + "QtQuickUltralite.Extras", + "QtQuickUltralite.Layers" + ] + + bannedImports: ["FlowView"] + + //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: ["gradient", "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", "textFormat", "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"] + } + + //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", + "fillGradient", "strokeStyle"] + } +} From 524f45f56685d2931b247f76281d5065e450d186 Mon Sep 17 00:00:00 2001 From: Samuel Ghinet Date: Sun, 7 May 2023 09:34:59 +0300 Subject: [PATCH 127/192] QmlDesigner: Fix crash at filtering the Style Model in QDS new dialog Task-number: QDS-9731 Change-Id: Iac64d109fa125f67ab3e713e07ebbc3e519d5ac5 Reviewed-by: Tim Jenssen --- src/plugins/studiowelcome/stylemodel.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/plugins/studiowelcome/stylemodel.cpp b/src/plugins/studiowelcome/stylemodel.cpp index d93fc5fded2..79669ff2a5f 100644 --- a/src/plugins/studiowelcome/stylemodel.cpp +++ b/src/plugins/studiowelcome/stylemodel.cpp @@ -63,6 +63,8 @@ int StyleModel::filteredIndex(int actualIndex) if (actualIndex < 0) return actualIndex; + QTC_ASSERT(actualIndex < m_items.size(), return -1); + QStandardItem *item = m_items.at(actualIndex); // TODO: perhaps should add this kind of find to utils/algorithm.h auto it = std::find(std::cbegin(m_filteredItems), std::cend(m_filteredItems), item); From 4f11fae460edc7b26472f43e6f6f716bc6c589c3 Mon Sep 17 00:00:00 2001 From: Samuel Ghinet Date: Fri, 5 May 2023 15:10:00 +0300 Subject: [PATCH 128/192] QmlDesigner: Fix crash in FileExtractor when compressed size is 0 Task-number: QDS-9832 Change-Id: Ia096b5770359ea16171cae0634fea340264194e1 Reviewed-by: Tim Jenssen --- src/plugins/qmldesigner/utils/fileextractor.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/plugins/qmldesigner/utils/fileextractor.cpp b/src/plugins/qmldesigner/utils/fileextractor.cpp index c6aa83fb7ae..7c32381b69c 100644 --- a/src/plugins/qmldesigner/utils/fileextractor.cpp +++ b/src/plugins/qmldesigner/utils/fileextractor.cpp @@ -51,7 +51,11 @@ FileExtractor::FileExtractor(QObject *parent) // We can not get the uncompressed size of the archive yet, that is why we use an // approximation. We assume a 50% compression rate. - int progress = std::min(100ll, currentSize * 100 / m_compressedSize * 2); + + int progress = 0; + if (m_compressedSize > 0) + progress = std::min(100ll, currentSize * 100 / m_compressedSize * 2); + if (progress >= 0) { m_progress = progress; emit progressChanged(); @@ -212,6 +216,8 @@ void FileExtractor::extract() m_timer.start(); m_bytesBefore = QStorageInfo(m_targetPath.toFileInfo().dir()).bytesAvailable(); m_compressedSize = QFileInfo(m_sourceFile.toString()).size(); + if (m_compressedSize <= 0) + qWarning() << "Compressed size for file '" << m_sourceFile << "' is zero or invalid: " << m_compressedSize; QObject::connect(archive, &Utils::Archive::outputReceived, this, [this](const QString &output) { m_detailedText += output; From 2bf16bd692bfa1c17635245e0c21e5f2e696acca Mon Sep 17 00:00:00 2001 From: Samuel Ghinet Date: Fri, 5 May 2023 15:10:39 +0300 Subject: [PATCH 129/192] QmlDesigner: Fix crash upon creation of wizard Task-number: QDS-9532 Change-Id: I2ce4e6e9597798a023c626c6b8e5dee6a1570e03 Reviewed-by: Miikka Heikkinen Reviewed-by: Tim Jenssen --- src/plugins/studiowelcome/qdsnewdialog.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/plugins/studiowelcome/qdsnewdialog.cpp b/src/plugins/studiowelcome/qdsnewdialog.cpp index 455267e901e..c3f8f443729 100644 --- a/src/plugins/studiowelcome/qdsnewdialog.cpp +++ b/src/plugins/studiowelcome/qdsnewdialog.cpp @@ -183,6 +183,11 @@ void QdsNewDialog::onWizardCreated(QStandardItemModel *screenSizeModel, QStandar if (styleModel) m_styleModel->setBackendModel(styleModel); + if (!m_currentPreset) { + qWarning() << "Wizard has been created but there is no Preset selected!"; + return; + } + auto userPreset = m_currentPreset->asUserPreset(); if (m_qmlDetailsLoaded) { From c1c90ebd1c217f0821f51adcb2cf6468b49cdef2 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Wed, 3 May 2023 14:10:16 +0200 Subject: [PATCH 130/192] QmlDesigner: Use fully qualified namespace There can be ambiguities with other Utils namespaces. Change-Id: I84c79fd380115160dd6070f67cb80ccf00c20ca5 Reviewed-by: Tim Jenssen Reviewed-by: --- .../qmldesigner/designercore/include/import.h | 2 +- .../designercore/include/nodemetainfo.h | 2 +- .../projectstorage/projectstorageinfotypes.h | 6 +- .../projectstorage/projectstoragetypes.h | 133 +++++++++--------- 4 files changed, 73 insertions(+), 70 deletions(-) diff --git a/src/plugins/qmldesigner/designercore/include/import.h b/src/plugins/qmldesigner/designercore/include/import.h index be7c76f492d..872721c9a4a 100644 --- a/src/plugins/qmldesigner/designercore/include/import.h +++ b/src/plugins/qmldesigner/designercore/include/import.h @@ -83,7 +83,7 @@ void differenceCall(const Imports &first, const Imports &second, Callable callab first.end(), second.begin(), second.end(), - Utils::make_iterator(callable)); + ::Utils::make_iterator(callable)); } } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/include/nodemetainfo.h b/src/plugins/qmldesigner/designercore/include/nodemetainfo.h index 8642a03875a..028c62ea7ed 100644 --- a/src/plugins/qmldesigner/designercore/include/nodemetainfo.h +++ b/src/plugins/qmldesigner/designercore/include/nodemetainfo.h @@ -44,7 +44,7 @@ public: bool isValid() const; explicit operator bool() const { return isValid(); } bool isFileComponent() const; - bool hasProperty(Utils::SmallStringView propertyName) const; + bool hasProperty(::Utils::SmallStringView propertyName) const; PropertyMetaInfos properties() const; PropertyMetaInfos localProperties() const; PropertyMetaInfo property(const PropertyName &propertyName) const; diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinfotypes.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinfotypes.h index 71823d3ad86..b61a6ec331a 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinfotypes.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinfotypes.h @@ -59,7 +59,7 @@ constexpr TypeTraits operator&(TypeTraits first, TypeTraits second) return static_cast(static_cast(first) & static_cast(second)); } -using TypeNameString = Utils::BasicSmallString<63>; +using TypeNameString = ::Utils::BasicSmallString<63>; } // namespace QmlDesigner::Storage @@ -69,7 +69,7 @@ class PropertyDeclaration { public: PropertyDeclaration(TypeId typeId, - Utils::SmallStringView name, + ::Utils::SmallStringView name, PropertyDeclarationTraits traits, TypeId propertyTypeId) : typeId{typeId} @@ -79,7 +79,7 @@ public: {} TypeId typeId; - Utils::SmallString name; + ::Utils::SmallString name; PropertyDeclarationTraits traits; TypeId propertyTypeId; }; diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstoragetypes.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstoragetypes.h index 1f20eb93713..f26ec622ca8 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstoragetypes.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstoragetypes.h @@ -244,7 +244,7 @@ class ImportedType { public: explicit ImportedType() = default; - explicit ImportedType(Utils::SmallStringView name) + explicit ImportedType(::Utils::SmallStringView name) : name{name} {} @@ -261,7 +261,7 @@ class QualifiedImportedType { public: explicit QualifiedImportedType() = default; - explicit QualifiedImportedType(Utils::SmallStringView name, Import import) + explicit QualifiedImportedType(::Utils::SmallStringView name, Import import) : name{name} , import{std::move(import)} {} @@ -282,25 +282,28 @@ class ExportedType { public: explicit ExportedType() = default; - explicit ExportedType(Utils::SmallStringView name, Version version = Version{}) + explicit ExportedType(::Utils::SmallStringView name, Version version = Version{}) : name{name} , version{version} {} - explicit ExportedType(ModuleId moduleId, Utils::SmallStringView name, Version version = Version{}) + explicit ExportedType(ModuleId moduleId, ::Utils::SmallStringView name, Version version = Version{}) : name{name} , version{version} , moduleId{moduleId} {} - explicit ExportedType(Utils::SmallStringView name, Version version, TypeId typeId, ModuleId moduleId) + explicit ExportedType(::Utils::SmallStringView name, Version version, TypeId typeId, ModuleId moduleId) : name{name} , version{version} , typeId{typeId} , moduleId{moduleId} {} - explicit ExportedType(ModuleId moduleId, Utils::SmallStringView name, int majorVersion, int minorVersion) + explicit ExportedType(ModuleId moduleId, + ::Utils::SmallStringView name, + int majorVersion, + int minorVersion) : name{name} , version{majorVersion, minorVersion} , moduleId{moduleId} @@ -318,7 +321,7 @@ public: } public: - Utils::SmallString name; + ::Utils::SmallString name; Version version; TypeId typeId; ModuleId moduleId; @@ -330,13 +333,13 @@ class ExportedTypeView { public: explicit ExportedTypeView() = default; - explicit ExportedTypeView(ModuleId moduleId, Utils::SmallStringView name, Version version) + explicit ExportedTypeView(ModuleId moduleId, ::Utils::SmallStringView name, Version version) : name{name} , version{version} , moduleId{moduleId} {} explicit ExportedTypeView(ModuleId moduleId, - Utils::SmallStringView name, + ::Utils::SmallStringView name, int majorVersion, int minorVersion, TypeId typeId, @@ -349,7 +352,7 @@ public: {} public: - Utils::SmallStringView name; + ::Utils::SmallStringView name; Version version; TypeId typeId; ModuleId moduleId; @@ -362,13 +365,13 @@ class EnumeratorDeclaration { public: explicit EnumeratorDeclaration() = default; - explicit EnumeratorDeclaration(Utils::SmallStringView name, long long value, int hasValue = true) + explicit EnumeratorDeclaration(::Utils::SmallStringView name, long long value, int hasValue = true) : name{name} , value{value} , hasValue{bool(hasValue)} {} - explicit EnumeratorDeclaration(Utils::SmallStringView name) + explicit EnumeratorDeclaration(::Utils::SmallStringView name) : name{name} {} @@ -379,7 +382,7 @@ public: } public: - Utils::SmallString name; + ::Utils::SmallString name; long long value = 0; bool hasValue = false; }; @@ -390,7 +393,7 @@ class EnumerationDeclaration { public: explicit EnumerationDeclaration() = default; - explicit EnumerationDeclaration(Utils::SmallStringView name, + explicit EnumerationDeclaration(::Utils::SmallStringView name, EnumeratorDeclarations enumeratorDeclarations) : name{name} , enumeratorDeclarations{std::move(enumeratorDeclarations)} @@ -413,8 +416,8 @@ class EnumerationDeclarationView { public: explicit EnumerationDeclarationView() = default; - explicit EnumerationDeclarationView(Utils::SmallStringView name, - Utils::SmallStringView enumeratorDeclarations, + explicit EnumerationDeclarationView(::Utils::SmallStringView name, + ::Utils::SmallStringView enumeratorDeclarations, EnumerationDeclarationId id) : name{name} , enumeratorDeclarations{std::move(enumeratorDeclarations)} @@ -422,8 +425,8 @@ public: {} public: - Utils::SmallStringView name; - Utils::SmallStringView enumeratorDeclarations; + ::Utils::SmallStringView name; + ::Utils::SmallStringView enumeratorDeclarations; EnumerationDeclarationId id; }; @@ -431,8 +434,8 @@ class ParameterDeclaration { public: explicit ParameterDeclaration() = default; - explicit ParameterDeclaration(Utils::SmallStringView name, - Utils::SmallStringView typeName, + explicit ParameterDeclaration(::Utils::SmallStringView name, + ::Utils::SmallStringView typeName, PropertyDeclarationTraits traits = {}) : name{name} , typeName{typeName} @@ -446,7 +449,7 @@ public: } public: - Utils::SmallString name; + ::Utils::SmallString name; TypeNameString typeName; PropertyDeclarationTraits traits = {}; }; @@ -457,12 +460,12 @@ class SignalDeclaration { public: explicit SignalDeclaration() = default; - explicit SignalDeclaration(Utils::SmallString name, ParameterDeclarations parameters) + explicit SignalDeclaration(::Utils::SmallString name, ParameterDeclarations parameters) : name{name} , parameters{std::move(parameters)} {} - explicit SignalDeclaration(Utils::SmallString name) + explicit SignalDeclaration(::Utils::SmallString name) : name{name} {} @@ -472,7 +475,7 @@ public: } public: - Utils::SmallString name; + ::Utils::SmallString name; ParameterDeclarations parameters; }; @@ -482,8 +485,8 @@ class SignalDeclarationView { public: explicit SignalDeclarationView() = default; - explicit SignalDeclarationView(Utils::SmallStringView name, - Utils::SmallStringView signature, + explicit SignalDeclarationView(::Utils::SmallStringView name, + ::Utils::SmallStringView signature, SignalDeclarationId id) : name{name} , signature{signature} @@ -491,8 +494,8 @@ public: {} public: - Utils::SmallStringView name; - Utils::SmallStringView signature; + ::Utils::SmallStringView name; + ::Utils::SmallStringView signature; SignalDeclarationId id; }; @@ -500,16 +503,16 @@ class FunctionDeclaration { public: explicit FunctionDeclaration() = default; - explicit FunctionDeclaration(Utils::SmallStringView name, - Utils::SmallStringView returnTypeName, + explicit FunctionDeclaration(::Utils::SmallStringView name, + ::Utils::SmallStringView returnTypeName, ParameterDeclarations parameters) : name{name} , returnTypeName{returnTypeName} , parameters{std::move(parameters)} {} - explicit FunctionDeclaration(Utils::SmallStringView name, - Utils::SmallStringView returnTypeName = {}) + explicit FunctionDeclaration(::Utils::SmallStringView name, + ::Utils::SmallStringView returnTypeName = {}) : name{name} , returnTypeName{returnTypeName} {} @@ -521,7 +524,7 @@ public: } public: - Utils::SmallString name; + ::Utils::SmallString name; TypeNameString returnTypeName; ParameterDeclarations parameters; }; @@ -532,9 +535,9 @@ class FunctionDeclarationView { public: explicit FunctionDeclarationView() = default; - explicit FunctionDeclarationView(Utils::SmallStringView name, - Utils::SmallStringView returnTypeName, - Utils::SmallStringView signature, + explicit FunctionDeclarationView(::Utils::SmallStringView name, + ::Utils::SmallStringView returnTypeName, + ::Utils::SmallStringView signature, FunctionDeclarationId id) : name{name} , returnTypeName{returnTypeName} @@ -543,9 +546,9 @@ public: {} public: - Utils::SmallStringView name; - Utils::SmallStringView returnTypeName; - Utils::SmallStringView signature; + ::Utils::SmallStringView name; + ::Utils::SmallStringView returnTypeName; + ::Utils::SmallStringView signature; FunctionDeclarationId id; }; @@ -555,7 +558,7 @@ class PropertyDeclaration { public: explicit PropertyDeclaration() = default; - explicit PropertyDeclaration(Utils::SmallStringView name, + explicit PropertyDeclaration(::Utils::SmallStringView name, ImportedTypeName typeName, PropertyDeclarationTraits traits) : name{name} @@ -564,7 +567,7 @@ public: , kind{PropertyKind::Property} {} - explicit PropertyDeclaration(Utils::SmallStringView name, + explicit PropertyDeclaration(::Utils::SmallStringView name, TypeId propertyTypeId, PropertyDeclarationTraits traits) : name{name} @@ -573,11 +576,11 @@ public: , kind{PropertyKind::Property} {} - explicit PropertyDeclaration(Utils::SmallStringView name, + explicit PropertyDeclaration(::Utils::SmallStringView name, ImportedTypeName typeName, PropertyDeclarationTraits traits, - Utils::SmallStringView aliasPropertyName, - Utils::SmallStringView aliasPropertyNameTail = {}) + ::Utils::SmallStringView aliasPropertyName, + ::Utils::SmallStringView aliasPropertyNameTail = {}) : name{name} , typeName{std::move(typeName)} , aliasPropertyName{aliasPropertyName} @@ -587,11 +590,11 @@ public: , kind{PropertyKind::Property} {} - explicit PropertyDeclaration(Utils::SmallStringView name, + explicit PropertyDeclaration(::Utils::SmallStringView name, TypeId propertyTypeId, PropertyDeclarationTraits traits, - Utils::SmallStringView aliasPropertyName, - Utils::SmallStringView aliasPropertyNameTail = {}) + ::Utils::SmallStringView aliasPropertyName, + ::Utils::SmallStringView aliasPropertyNameTail = {}) : name{name} , aliasPropertyName{aliasPropertyName} , aliasPropertyNameTail{aliasPropertyNameTail} @@ -600,10 +603,10 @@ public: , kind{PropertyKind::Property} {} - explicit PropertyDeclaration(Utils::SmallStringView name, + explicit PropertyDeclaration(::Utils::SmallStringView name, ImportedTypeName aliasTypeName, - Utils::SmallStringView aliasPropertyName, - Utils::SmallStringView aliasPropertyNameTail = {}) + ::Utils::SmallStringView aliasPropertyName, + ::Utils::SmallStringView aliasPropertyNameTail = {}) : name{name} , typeName{std::move(aliasTypeName)} , aliasPropertyName{aliasPropertyName} @@ -621,10 +624,10 @@ public: } public: - Utils::SmallString name; + ::Utils::SmallString name; ImportedTypeName typeName; - Utils::SmallString aliasPropertyName; - Utils::SmallString aliasPropertyNameTail; + ::Utils::SmallString aliasPropertyName; + ::Utils::SmallString aliasPropertyNameTail; PropertyDeclarationTraits traits = {}; TypeId propertyTypeId; TypeId typeId; @@ -636,7 +639,7 @@ using PropertyDeclarations = std::vector; class PropertyDeclarationView { public: - explicit PropertyDeclarationView(Utils::SmallStringView name, + explicit PropertyDeclarationView(::Utils::SmallStringView name, PropertyDeclarationTraits traits, TypeId typeId, ImportedTypeNameId typeNameId, @@ -651,7 +654,7 @@ public: {} public: - Utils::SmallStringView name; + ::Utils::SmallStringView name; PropertyDeclarationTraits traits = {}; TypeId typeId; ImportedTypeNameId typeNameId; @@ -665,7 +668,7 @@ class Type { public: explicit Type() = default; - explicit Type(Utils::SmallStringView typeName, + explicit Type(::Utils::SmallStringView typeName, ImportedTypeName prototype, ImportedTypeName extension, TypeTraits traits, @@ -676,7 +679,7 @@ public: SignalDeclarations signalDeclarations = {}, EnumerationDeclarations enumerationDeclarations = {}, ChangeLevel changeLevel = ChangeLevel::Full, - Utils::SmallStringView defaultPropertyName = {}) + ::Utils::SmallStringView defaultPropertyName = {}) : typeName{typeName} , defaultPropertyName{defaultPropertyName} , prototype{std::move(prototype)} @@ -691,7 +694,7 @@ public: , changeLevel{changeLevel} {} - explicit Type(Utils::SmallStringView typeName, + explicit Type(::Utils::SmallStringView typeName, TypeId prototypeId, TypeId extensionId, TypeTraits traits, @@ -703,7 +706,7 @@ public: , extensionId{extensionId} {} - explicit Type(Utils::SmallStringView typeName, + explicit Type(::Utils::SmallStringView typeName, ImportedTypeName prototype, TypeTraits traits, SourceId sourceId, @@ -715,9 +718,9 @@ public: , changeLevel{changeLevel} {} - explicit Type(Utils::SmallStringView typeName, - Utils::SmallStringView prototype, - Utils::SmallStringView extension, + explicit Type(::Utils::SmallStringView typeName, + ::Utils::SmallStringView prototype, + ::Utils::SmallStringView extension, TypeTraits traits, SourceId sourceId) : typeName{typeName} @@ -729,12 +732,12 @@ public: {} explicit Type(SourceId sourceId, - Utils::SmallStringView typeName, + ::Utils::SmallStringView typeName, TypeId typeId, TypeId prototypeId, TypeId extensionId, TypeTraits traits, - Utils::SmallStringView defaultPropertyName) + ::Utils::SmallStringView defaultPropertyName) : typeName{typeName} , defaultPropertyName{defaultPropertyName} , traits{traits} @@ -758,7 +761,7 @@ public: public: TypeNameString typeName; - Utils::SmallString defaultPropertyName; + ::Utils::SmallString defaultPropertyName; ImportedTypeName prototype; ImportedTypeName extension; ExportedTypes exportedTypes; From 32f84fc05adcc71d5b5ae8670323d0a1de159ddf Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Sat, 6 May 2023 14:43:53 +0200 Subject: [PATCH 131/192] QmlDesigner: Move Version to different namespace We want to use it not only for synchronization. Task-number: QDS-9766 Change-Id: I26a2ade8203d9c5131e8dec101885434713f8252 Reviewed-by: Tim Jenssen --- .../qmldesigner/designercore/model/model.cpp | 3 +- .../projectstorage/commontypecache.h | 4 +- .../projectstorage/projectstorage.h | 4 +- .../projectstorage/projectstoragetypes.h | 45 +- .../projectstorage/projectstorageupdater.cpp | 11 +- .../projectstorage/qmldocumentparser.cpp | 10 +- .../projectstorage/qmltypesparser.cpp | 10 +- .../unit/unittest/gtest-creator-printing.cpp | 21 +- tests/unit/unittest/gtest-creator-printing.h | 9 +- tests/unit/unittest/projectstorage-test.cpp | 542 +++++++++--------- .../unittest/projectstorageupdater-test.cpp | 80 +-- .../unit/unittest/qmldocumentparser-test.cpp | 66 ++- tests/unit/unittest/qmltypesparser-test.cpp | 93 +-- 13 files changed, 456 insertions(+), 442 deletions(-) diff --git a/src/plugins/qmldesigner/designercore/model/model.cpp b/src/plugins/qmldesigner/designercore/model/model.cpp index d410a3c87aa..c1d6c078631 100644 --- a/src/plugins/qmldesigner/designercore/model/model.cpp +++ b/src/plugins/qmldesigner/designercore/model/model.cpp @@ -2084,8 +2084,7 @@ NodeMetaInfo Model::metaInfo(const TypeName &typeName, int majorVersion, int min ModuleId moduleId = d->projectStorage->moduleId(module); TypeId typeId = d->projectStorage->typeId(moduleId, componentName, - Storage::Synchronization::Version{majorVersion, - minorVersion}); + Storage::Version{majorVersion, minorVersion}); return NodeMetaInfo(typeId, d->projectStorage); } else { return NodeMetaInfo(metaInfoProxyModel(), typeName, majorVersion, minorVersion); diff --git a/src/plugins/qmldesigner/designercore/projectstorage/commontypecache.h b/src/plugins/qmldesigner/designercore/projectstorage/commontypecache.h index eda1daba5f5..d60dfa3abc7 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/commontypecache.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/commontypecache.h @@ -250,9 +250,7 @@ public: if (!type.moduleId) type.moduleId = m_projectStorage.moduleId(moduleName); - type.typeId = m_projectStorage.typeId(type.moduleId, - typeName, - QmlDesigner::Storage::Synchronization::Version{}); + type.typeId = m_projectStorage.typeId(type.moduleId, typeName, QmlDesigner::Storage::Version{}); return type.typeId; } diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h index a036653f4f8..3ebe87da76e 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h @@ -119,7 +119,7 @@ public: TypeId typeId(ModuleId moduleId, Utils::SmallStringView exportedTypeName, - Storage::Synchronization::Version version) const + Storage::Version version) const { if (version.minor) return selectTypeIdByModuleIdAndExportedNameAndVersionStatement @@ -1533,7 +1533,7 @@ private: ModuleExportedImportId moduleExportedImportId) { Storage::Synchronization::Import additionImport{ exportedModuleId, - Storage::Synchronization::Version{majorVersion, minorVersion}, + Storage::Version{majorVersion, minorVersion}, import.sourceId}; auto exportedImportKind = importKind == Storage::Synchronization::ImportKind::Import diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstoragetypes.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstoragetypes.h index f26ec622ca8..83372893785 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstoragetypes.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstoragetypes.h @@ -13,11 +13,7 @@ #include #include -namespace QmlDesigner::Storage::Synchronization { - -enum class TypeNameKind { Exported = 1, QualifiedExported = 2 }; - -enum class FileType : char { QmlTypes, QmlDocument }; +namespace QmlDesigner::Storage { class VersionNumber { @@ -83,6 +79,12 @@ public: VersionNumber minor; }; +namespace Synchronization { + +enum class TypeNameKind { Exported = 1, QualifiedExported = 2 }; + +enum class FileType : char { QmlTypes, QmlDocument }; + enum class IsQualified : int { No, Yes }; inline int operator-(IsQualified first, IsQualified second) @@ -107,7 +109,7 @@ class Import public: explicit Import() = default; - explicit Import(ModuleId moduleId, Version version, SourceId sourceId) + explicit Import(ModuleId moduleId, Storage::Version version, SourceId sourceId) : version{version} , moduleId{moduleId} , sourceId{sourceId} @@ -132,7 +134,7 @@ public: } public: - Version version; + Storage::Version version; ModuleId moduleId; SourceId sourceId; }; @@ -162,7 +164,7 @@ public: ImportId importId; SourceId sourceId; ModuleId moduleId; - Version version; + Storage::Version version; }; enum class IsAutoVersion : char { No, Yes }; @@ -177,7 +179,7 @@ class ModuleExportedImport public: explicit ModuleExportedImport(ModuleId moduleId, ModuleId exportedModuleId, - Version version, + Storage::Version version, IsAutoVersion isAutoVersion) : version{version} , moduleId{moduleId} @@ -199,7 +201,7 @@ public: } public: - Version version; + Storage::Version version; ModuleId moduleId; ModuleId exportedModuleId; IsAutoVersion isAutoVersion = IsAutoVersion::No; @@ -234,7 +236,7 @@ public: public: ModuleExportedImportId moduleExportedImportId; - Version version; + Storage::Version version; ModuleId moduleId; ModuleId exportedModuleId; IsAutoVersion isAutoVersion = IsAutoVersion::No; @@ -282,18 +284,23 @@ class ExportedType { public: explicit ExportedType() = default; - explicit ExportedType(::Utils::SmallStringView name, Version version = Version{}) + explicit ExportedType(::Utils::SmallStringView name, Storage::Version version = Storage::Version{}) : name{name} , version{version} {} - explicit ExportedType(ModuleId moduleId, ::Utils::SmallStringView name, Version version = Version{}) + explicit ExportedType(ModuleId moduleId, + ::Utils::SmallStringView name, + Storage::Version version = Storage::Version{}) : name{name} , version{version} , moduleId{moduleId} {} - explicit ExportedType(::Utils::SmallStringView name, Version version, TypeId typeId, ModuleId moduleId) + explicit ExportedType(::Utils::SmallStringView name, + Storage::Version version, + TypeId typeId, + ModuleId moduleId) : name{name} , version{version} , typeId{typeId} @@ -322,7 +329,7 @@ public: public: ::Utils::SmallString name; - Version version; + Storage::Version version; TypeId typeId; ModuleId moduleId; }; @@ -333,7 +340,7 @@ class ExportedTypeView { public: explicit ExportedTypeView() = default; - explicit ExportedTypeView(ModuleId moduleId, ::Utils::SmallStringView name, Version version) + explicit ExportedTypeView(ModuleId moduleId, ::Utils::SmallStringView name, Storage::Version version) : name{name} , version{version} , moduleId{moduleId} @@ -353,7 +360,7 @@ public: public: ::Utils::SmallStringView name; - Version version; + Storage::Version version; TypeId typeId; ModuleId moduleId; ExportedTypeNameId exportedTypeNameId; @@ -858,5 +865,5 @@ public: ModuleIds updatedModuleIds; }; -} // namespace QmlDesigner::Storage::Synchronization - +} // namespace Synchronization +} // namespace QmlDesigner::Storage diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.cpp b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.cpp index a02a9c47aac..4713dd0136f 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.cpp +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.cpp @@ -113,9 +113,9 @@ void addSourceIds(SourceIds &sourceIds, const Storage::Synchronization::ProjectD sourceIds.push_back(projectData.sourceId); } -Storage::Synchronization::Version convertVersion(LanguageUtils::ComponentVersion version) +Storage::Version convertVersion(LanguageUtils::ComponentVersion version) { - return Storage::Synchronization::Version{version.majorVersion(), version.minorVersion()}; + return Storage::Version{version.majorVersion(), version.minorVersion()}; } Storage::Synchronization::IsAutoVersion convertToIsAutoVersion(QmlDirParser::Import::Flags flags) @@ -133,7 +133,7 @@ void addDependencies(Storage::Synchronization::Imports &dependencies, for (const QmlDirParser::Import &qmldirDependency : qmldirDependencies) { ModuleId moduleId = projectStorage.moduleId(Utils::PathString{qmldirDependency.module} + "-cppnative"); - dependencies.emplace_back(moduleId, Storage::Synchronization::Version{}, sourceId); + dependencies.emplace_back(moduleId, Storage::Version{}, sourceId); } } @@ -154,7 +154,7 @@ void addModuleExportedImports(Storage::Synchronization::ModuleExportedImports &i Utils::PathString{qmldirImport.module} + "-cppnative"); imports.emplace_back(cppModuleId, exportedCppModuleId, - Storage::Synchronization::Version{}, + Storage::Version{}, Storage::Synchronization::IsAutoVersion::No); } } @@ -644,8 +644,7 @@ Storage::Synchronization::ExportedTypes createExportedTypes(ProjectStorageUpdate for (const ProjectStorageUpdater::Component &component : components) { exportedTypes.emplace_back(component.moduleId, Utils::SmallString{component.typeName}, - Storage::Synchronization::Version{component.majorVersion, - component.minorVersion}); + Storage::Version{component.majorVersion, component.minorVersion}); } return exportedTypes; diff --git a/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.cpp b/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.cpp index 894299e7958..b530dc84e38 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.cpp +++ b/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.cpp @@ -32,9 +32,9 @@ int convertVersionNumber(qint32 versionNumber) return versionNumber < 0 ? -1 : versionNumber; } -Storage::Synchronization::Version convertVersion(QmlDom::Version version) +Storage::Version convertVersion(QmlDom::Version version) { - return Storage::Synchronization::Version{convertVersionNumber(version.majorVersion), + return Storage::Version{convertVersionNumber(version.majorVersion), convertVersionNumber(version.minorVersion)}; } @@ -66,7 +66,7 @@ Storage::Synchronization::Import createImport(const QmlDom::Import &qmlImport, if (uri.kind() == QmlUriKind::RelativePath) { auto path = createNormalizedPath(directoryPath, uri.localPath()); auto moduleId = storage.moduleId(createNormalizedPath(directoryPath, uri.localPath())); - return Storage::Synchronization::Import(moduleId, Storage::Synchronization::Version{}, sourceId); + return Storage::Synchronization::Import(moduleId, Storage::Version{}, sourceId); } if (uri.kind() == QmlUriKind::ModuleUri) { @@ -109,11 +109,11 @@ void addImports(Storage::Synchronization::Imports &imports, } auto localDirectoryModuleId = storage.moduleId(directoryPath); - imports.emplace_back(localDirectoryModuleId, Storage::Synchronization::Version{}, sourceId); + imports.emplace_back(localDirectoryModuleId, Storage::Version{}, sourceId); ++importCount; auto qmlModuleId = storage.moduleId("QML"); - imports.emplace_back(qmlModuleId, Storage::Synchronization::Version{}, sourceId); + imports.emplace_back(qmlModuleId, Storage::Version{}, sourceId); ++importCount; auto end = imports.end(); diff --git a/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.cpp b/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.cpp index aebe57a14b0..46613b015a4 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.cpp +++ b/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.cpp @@ -62,7 +62,7 @@ void appendImports(Storage::Synchronization::Imports &imports, moduleName.append("-cppnative"); ModuleId cppModuleId = storage.moduleId(moduleName); - imports.emplace_back(cppModuleId, Storage::Synchronization::Version{}, sourceId); + imports.emplace_back(cppModuleId, Storage::Version{}, sourceId); } void addImports(Storage::Synchronization::Imports &imports, @@ -74,10 +74,10 @@ void addImports(Storage::Synchronization::Imports &imports, for (const QString &dependency : dependencies) appendImports(imports, dependency, sourceId, storage); - imports.emplace_back(cppModuleId, Storage::Synchronization::Version{}, sourceId); + imports.emplace_back(cppModuleId, Storage::Version{}, sourceId); if (ModuleId qmlCppModuleId = storage.moduleId("QML-cppnative"); cppModuleId != qmlCppModuleId) - imports.emplace_back(qmlCppModuleId, Storage::Synchronization::Version{}, sourceId); + imports.emplace_back(qmlCppModuleId, Storage::Version{}, sourceId); } Storage::TypeTraits createTypeTraits(QQmlJSScope::AccessSemantics accessSematics) @@ -96,9 +96,9 @@ Storage::TypeTraits createTypeTraits(QQmlJSScope::AccessSemantics accessSematics return Storage::TypeTraits::None; } -Storage::Synchronization::Version createVersion(QTypeRevision qmlVersion) +Storage::Version createVersion(QTypeRevision qmlVersion) { - return Storage::Synchronization::Version{qmlVersion.majorVersion(), qmlVersion.minorVersion()}; + return Storage::Version{qmlVersion.majorVersion(), qmlVersion.minorVersion()}; } Storage::Synchronization::ExportedTypes createExports(const QList &qmlExports, diff --git a/tests/unit/unittest/gtest-creator-printing.cpp b/tests/unit/unittest/gtest-creator-printing.cpp index 3dbba3f50dc..cd69504461b 100644 --- a/tests/unit/unittest/gtest-creator-printing.cpp +++ b/tests/unit/unittest/gtest-creator-printing.cpp @@ -583,6 +583,17 @@ std::ostream &operator<<(std::ostream &out, PropertyDeclarationTraits traits) return out << ")"; } + +std::ostream &operator<<(std::ostream &out, VersionNumber versionNumber) +{ + return out << versionNumber.value; +} + +std::ostream &operator<<(std::ostream &out, Version version) +{ + return out << "(" << version.major << ", " << version.minor << ")"; +} + } // namespace Storage namespace Storage::Info { @@ -703,16 +714,6 @@ std::ostream &operator<<(std::ostream &out, IsQualified isQualified) return out << isQualifiedToString(isQualified); } -std::ostream &operator<<(std::ostream &out, VersionNumber versionNumber) -{ - return out << versionNumber.value; -} - -std::ostream &operator<<(std::ostream &out, Version version) -{ - return out << "(" << version.major << ", " << version.minor << ")"; -} - std::ostream &operator<<(std::ostream &out, const ExportedType &exportedType) { return out << "(\"" << exportedType.name << "\"," << exportedType.moduleId << ", " diff --git a/tests/unit/unittest/gtest-creator-printing.h b/tests/unit/unittest/gtest-creator-printing.h index 5aad4c40c39..f63cec1f287 100644 --- a/tests/unit/unittest/gtest-creator-printing.h +++ b/tests/unit/unittest/gtest-creator-printing.h @@ -160,10 +160,13 @@ std::ostream &operator<<(std::ostream &out, TypeTraits traits); namespace Storage::Info { class ProjectDeclaration; class Type; +class Version; +class VersionNumber; std::ostream &operator<<(std::ostream &out, const ProjectDeclaration &declaration); std::ostream &operator<<(std::ostream &out, const Type &type); - +std::ostream &operator<<(std::ostream &out, VersionNumber versionNumber); +std::ostream &operator<<(std::ostream &out, Version version); } // namespace Storage::Info namespace Storage::Synchronization { @@ -171,8 +174,6 @@ class Type; class ExportedType; class ImportedType; class QualifiedImportedType; -class Version; -class VersionNumber; class PropertyDeclaration; class FunctionDeclaration; class ParameterDeclaration; @@ -189,8 +190,6 @@ enum class FileType : char; enum class ChangeLevel : char; class ModuleExportedImport; -std::ostream &operator<<(std::ostream &out, VersionNumber versionNumber); -std::ostream &operator<<(std::ostream &out, Version version); std::ostream &operator<<(std::ostream &out, const Type &type); std::ostream &operator<<(std::ostream &out, const ExportedType &exportedType); std::ostream &operator<<(std::ostream &out, const ImportedType &importedType); diff --git a/tests/unit/unittest/projectstorage-test.cpp b/tests/unit/unittest/projectstorage-test.cpp index a11b804a5a0..7ff1df47c9e 100644 --- a/tests/unit/unittest/projectstorage-test.cpp +++ b/tests/unit/unittest/projectstorage-test.cpp @@ -122,12 +122,12 @@ MATCHER_P3(IsExportedType, minorVersion, std::string(negation ? "isn't " : "is ") + PrintToString(Storage::Synchronization::ExportedType{ - name, Storage::Synchronization::Version{majorVersion, minorVersion}})) + name, Storage::Version{majorVersion, minorVersion}})) { const Storage::Synchronization::ExportedType &type = arg; return type.name == name - && type.version == Storage::Synchronization::Version{majorVersion, minorVersion}; + && type.version == Storage::Version{majorVersion, minorVersion}; } MATCHER_P4(IsExportedType, @@ -137,12 +137,12 @@ MATCHER_P4(IsExportedType, minorVersion, std::string(negation ? "isn't " : "is ") + PrintToString(Storage::Synchronization::ExportedType{ - moduleId, name, Storage::Synchronization::Version{majorVersion, minorVersion}})) + moduleId, name, Storage::Version{majorVersion, minorVersion}})) { const Storage::Synchronization::ExportedType &type = arg; return type.moduleId == moduleId && type.name == name - && type.version == Storage::Synchronization::Version{majorVersion, minorVersion}; + && type.version == Storage::Version{majorVersion, minorVersion}; } MATCHER_P3( @@ -284,33 +284,33 @@ protected: { SynchronizationPackage package; - package.imports.emplace_back(qmlModuleId, Storage::Synchronization::Version{}, sourceId1); - package.imports.emplace_back(qtQuickModuleId, Storage::Synchronization::Version{}, sourceId1); - package.imports.emplace_back(qmlModuleId, Storage::Synchronization::Version{}, sourceId2); + package.imports.emplace_back(qmlModuleId, Storage::Version{}, sourceId1); + package.imports.emplace_back(qtQuickModuleId, Storage::Version{}, sourceId1); + package.imports.emplace_back(qmlModuleId, Storage::Version{}, sourceId2); package.moduleDependencies.emplace_back(qmlNativeModuleId, - Storage::Synchronization::Version{}, + Storage::Version{}, sourceId1); package.moduleDependencies.emplace_back(qtQuickNativeModuleId, - Storage::Synchronization::Version{}, + Storage::Version{}, sourceId1); package.moduleDependencies.emplace_back(qmlNativeModuleId, - Storage::Synchronization::Version{}, + Storage::Version{}, sourceId2); package.updatedModuleDependencySourceIds.push_back(sourceId1); package.updatedModuleDependencySourceIds.push_back(sourceId2); - importsSourceId1.emplace_back(qmlModuleId, Storage::Synchronization::Version{}, sourceId1); - importsSourceId1.emplace_back(qtQuickModuleId, Storage::Synchronization::Version{}, sourceId1); + importsSourceId1.emplace_back(qmlModuleId, Storage::Version{}, sourceId1); + importsSourceId1.emplace_back(qtQuickModuleId, Storage::Version{}, sourceId1); moduleDependenciesSourceId1.emplace_back(qmlNativeModuleId, - Storage::Synchronization::Version{}, + Storage::Version{}, sourceId1); moduleDependenciesSourceId1.emplace_back(qtQuickNativeModuleId, - Storage::Synchronization::Version{}, + Storage::Version{}, sourceId1); - importsSourceId2.emplace_back(qmlModuleId, Storage::Synchronization::Version{}, sourceId2); + importsSourceId2.emplace_back(qmlModuleId, Storage::Version{}, sourceId2); moduleDependenciesSourceId2.emplace_back(qmlNativeModuleId, - Storage::Synchronization::Version{}, + Storage::Version{}, sourceId2); package.types.push_back(Storage::Synchronization::Type{ @@ -365,10 +365,10 @@ protected: sourceId2, {Storage::Synchronization::ExportedType{qmlModuleId, "Object", - Storage::Synchronization::Version{2}}, + Storage::Version{2}}, Storage::Synchronization::ExportedType{qmlModuleId, "Obj", - Storage::Synchronization::Version{2}}, + Storage::Version{2}}, Storage::Synchronization::ExportedType{qmlNativeModuleId, "QObject"}}}); package.updatedSourceIds = {sourceId1, sourceId2}; @@ -380,15 +380,15 @@ protected: { SynchronizationPackage package; - package.imports.emplace_back(QMLModuleId, Storage::Synchronization::Version{}, sourceId1); + package.imports.emplace_back(QMLModuleId, Storage::Version{}, sourceId1); package.moduleDependencies.emplace_back(QMLModuleId, - Storage::Synchronization::Version{}, + Storage::Version{}, sourceId1); package.updatedModuleDependencySourceIds.push_back(sourceId1); - importsSourceId1.emplace_back(QMLModuleId, Storage::Synchronization::Version{}, sourceId1); + importsSourceId1.emplace_back(QMLModuleId, Storage::Version{}, sourceId1); moduleDependenciesSourceId1.emplace_back(QMLModuleId, - Storage::Synchronization::Version{}, + Storage::Version{}, sourceId1); package.types.push_back( @@ -417,36 +417,36 @@ protected: { auto package{createSimpleSynchronizationPackage()}; - package.imports.emplace_back(qmlModuleId, Storage::Synchronization::Version{}, sourceId3); - package.imports.emplace_back(qtQuickModuleId, Storage::Synchronization::Version{}, sourceId3); + package.imports.emplace_back(qmlModuleId, Storage::Version{}, sourceId3); + package.imports.emplace_back(qtQuickModuleId, Storage::Version{}, sourceId3); package.moduleDependencies.emplace_back(qmlNativeModuleId, - Storage::Synchronization::Version{}, + Storage::Version{}, sourceId3); package.moduleDependencies.emplace_back(qtQuickNativeModuleId, - Storage::Synchronization::Version{}, + Storage::Version{}, sourceId3); package.updatedModuleDependencySourceIds.push_back(sourceId3); - package.imports.emplace_back(qmlModuleId, Storage::Synchronization::Version{}, sourceId4); - package.imports.emplace_back(pathToModuleId, Storage::Synchronization::Version{}, sourceId4); + package.imports.emplace_back(qmlModuleId, Storage::Version{}, sourceId4); + package.imports.emplace_back(pathToModuleId, Storage::Version{}, sourceId4); package.moduleDependencies.emplace_back(qmlNativeModuleId, - Storage::Synchronization::Version{}, + Storage::Version{}, sourceId4); package.updatedModuleDependencySourceIds.push_back(sourceId4); - importsSourceId3.emplace_back(qmlModuleId, Storage::Synchronization::Version{}, sourceId3); - importsSourceId3.emplace_back(qtQuickModuleId, Storage::Synchronization::Version{}, sourceId3); + importsSourceId3.emplace_back(qmlModuleId, Storage::Version{}, sourceId3); + importsSourceId3.emplace_back(qtQuickModuleId, Storage::Version{}, sourceId3); moduleDependenciesSourceId3.emplace_back(qmlNativeModuleId, - Storage::Synchronization::Version{}, + Storage::Version{}, sourceId3); moduleDependenciesSourceId3.emplace_back(qtQuickNativeModuleId, - Storage::Synchronization::Version{}, + Storage::Version{}, sourceId3); - importsSourceId4.emplace_back(qmlModuleId, Storage::Synchronization::Version{}, sourceId4); - importsSourceId4.emplace_back(pathToModuleId, Storage::Synchronization::Version{}, sourceId4); + importsSourceId4.emplace_back(qmlModuleId, Storage::Version{}, sourceId4); + importsSourceId4.emplace_back(pathToModuleId, Storage::Version{}, sourceId4); moduleDependenciesSourceId4.emplace_back(qmlNativeModuleId, - Storage::Synchronization::Version{}, + Storage::Version{}, sourceId4); package.types[1].propertyDeclarations.push_back( @@ -498,35 +498,35 @@ protected: { SynchronizationPackage package; - package.imports.emplace_back(qmlModuleId, Storage::Synchronization::Version{}, sourceId1); - package.imports.emplace_back(qtQuickModuleId, Storage::Synchronization::Version{}, sourceId1); - package.imports.emplace_back(qmlModuleId, Storage::Synchronization::Version{}, sourceId2); + package.imports.emplace_back(qmlModuleId, Storage::Version{}, sourceId1); + package.imports.emplace_back(qtQuickModuleId, Storage::Version{}, sourceId1); + package.imports.emplace_back(qmlModuleId, Storage::Version{}, sourceId2); package.moduleDependencies.emplace_back(qmlNativeModuleId, - Storage::Synchronization::Version{}, + Storage::Version{}, sourceId1); package.moduleDependencies.emplace_back(qtQuickNativeModuleId, - Storage::Synchronization::Version{}, + Storage::Version{}, sourceId1); package.moduleDependencies.emplace_back(qmlNativeModuleId, - Storage::Synchronization::Version{}, + Storage::Version{}, sourceId2); package.updatedModuleDependencySourceIds.push_back(sourceId1); package.updatedModuleDependencySourceIds.push_back(sourceId2); - importsSourceId1.emplace_back(qmlModuleId, Storage::Synchronization::Version{}, sourceId1); - importsSourceId1.emplace_back(qtQuickModuleId, Storage::Synchronization::Version{}, sourceId1); + importsSourceId1.emplace_back(qmlModuleId, Storage::Version{}, sourceId1); + importsSourceId1.emplace_back(qtQuickModuleId, Storage::Version{}, sourceId1); moduleDependenciesSourceId1.emplace_back(qmlNativeModuleId, - Storage::Synchronization::Version{}, + Storage::Version{}, sourceId1); moduleDependenciesSourceId1.emplace_back(qtQuickNativeModuleId, - Storage::Synchronization::Version{}, + Storage::Version{}, sourceId1); - importsSourceId2.emplace_back(qmlModuleId, Storage::Synchronization::Version{}, sourceId2); + importsSourceId2.emplace_back(qmlModuleId, Storage::Version{}, sourceId2); moduleDependenciesSourceId2.emplace_back(qmlNativeModuleId, - Storage::Synchronization::Version{}, + Storage::Version{}, sourceId2); package.types.push_back(Storage::Synchronization::Type{ @@ -554,52 +554,52 @@ protected: sourceId2, {Storage::Synchronization::ExportedType{qmlModuleId, "Object", - Storage::Synchronization::Version{2}}, + Storage::Version{2}}, Storage::Synchronization::ExportedType{qmlModuleId, "Obj", - Storage::Synchronization::Version{2}}, + Storage::Version{2}}, Storage::Synchronization::ExportedType{qmlNativeModuleId, "QObject"}}}); package.updatedSourceIds = {sourceId1, sourceId2}; - package.imports.emplace_back(qmlModuleId, Storage::Synchronization::Version{}, sourceId3); - package.imports.emplace_back(qtQuickModuleId, Storage::Synchronization::Version{}, sourceId3); + package.imports.emplace_back(qmlModuleId, Storage::Version{}, sourceId3); + package.imports.emplace_back(qtQuickModuleId, Storage::Version{}, sourceId3); package.moduleDependencies.emplace_back(qmlNativeModuleId, - Storage::Synchronization::Version{}, + Storage::Version{}, sourceId3); package.moduleDependencies.emplace_back(qtQuickNativeModuleId, - Storage::Synchronization::Version{}, + Storage::Version{}, sourceId3); package.updatedModuleDependencySourceIds.push_back(sourceId3); - package.imports.emplace_back(qmlModuleId, Storage::Synchronization::Version{}, sourceId4); - package.imports.emplace_back(pathToModuleId, Storage::Synchronization::Version{}, sourceId4); - package.imports.emplace_back(qtQuickModuleId, Storage::Synchronization::Version{}, sourceId4); + package.imports.emplace_back(qmlModuleId, Storage::Version{}, sourceId4); + package.imports.emplace_back(pathToModuleId, Storage::Version{}, sourceId4); + package.imports.emplace_back(qtQuickModuleId, Storage::Version{}, sourceId4); package.moduleDependencies.emplace_back(qmlNativeModuleId, - Storage::Synchronization::Version{}, + Storage::Version{}, sourceId4); package.moduleDependencies.emplace_back(qtQuickNativeModuleId, - Storage::Synchronization::Version{}, + Storage::Version{}, sourceId4); package.updatedModuleDependencySourceIds.push_back(sourceId4); - importsSourceId3.emplace_back(qmlModuleId, Storage::Synchronization::Version{}, sourceId3); - importsSourceId3.emplace_back(qtQuickModuleId, Storage::Synchronization::Version{}, sourceId3); + importsSourceId3.emplace_back(qmlModuleId, Storage::Version{}, sourceId3); + importsSourceId3.emplace_back(qtQuickModuleId, Storage::Version{}, sourceId3); moduleDependenciesSourceId3.emplace_back(qmlNativeModuleId, - Storage::Synchronization::Version{}, + Storage::Version{}, sourceId3); moduleDependenciesSourceId3.emplace_back(qtQuickNativeModuleId, - Storage::Synchronization::Version{}, + Storage::Version{}, sourceId3); - importsSourceId4.emplace_back(qmlModuleId, Storage::Synchronization::Version{}, sourceId4); - importsSourceId4.emplace_back(pathToModuleId, Storage::Synchronization::Version{}, sourceId4); - importsSourceId4.emplace_back(qtQuickModuleId, Storage::Synchronization::Version{}, sourceId4); + importsSourceId4.emplace_back(qmlModuleId, Storage::Version{}, sourceId4); + importsSourceId4.emplace_back(pathToModuleId, Storage::Version{}, sourceId4); + importsSourceId4.emplace_back(qtQuickModuleId, Storage::Version{}, sourceId4); moduleDependenciesSourceId4.emplace_back(qmlNativeModuleId, - Storage::Synchronization::Version{}, + Storage::Version{}, sourceId4); moduleDependenciesSourceId4.emplace_back(qtQuickNativeModuleId, - Storage::Synchronization::Version{}, + Storage::Version{}, sourceId4); package.types[1].propertyDeclarations.push_back( @@ -646,7 +646,7 @@ protected: sourceId5, {Storage::Synchronization::ExportedType{qtQuickModuleId, "Children", - Storage::Synchronization::Version{2}}, + Storage::Version{2}}, Storage::Synchronization::ExportedType{qtQuickNativeModuleId, "QChildren"}}, {Storage::Synchronization::PropertyDeclaration{"items", Storage::Synchronization::ImportedType{ @@ -658,21 +658,21 @@ protected: Storage::PropertyDeclarationTraits::IsList | Storage::PropertyDeclarationTraits::IsReadOnly}}}); - package.imports.emplace_back(qmlModuleId, Storage::Synchronization::Version{}, sourceId5); - package.imports.emplace_back(qtQuickModuleId, Storage::Synchronization::Version{}, sourceId5); + package.imports.emplace_back(qmlModuleId, Storage::Version{}, sourceId5); + package.imports.emplace_back(qtQuickModuleId, Storage::Version{}, sourceId5); package.moduleDependencies.emplace_back(qmlNativeModuleId, - Storage::Synchronization::Version{}, + Storage::Version{}, sourceId5); package.moduleDependencies.emplace_back(qtQuickNativeModuleId, - Storage::Synchronization::Version{}, + Storage::Version{}, sourceId5); - importsSourceId5.emplace_back(qmlModuleId, Storage::Synchronization::Version{}, sourceId5); - importsSourceId5.emplace_back(qtQuickModuleId, Storage::Synchronization::Version{}, sourceId5); + importsSourceId5.emplace_back(qmlModuleId, Storage::Version{}, sourceId5); + importsSourceId5.emplace_back(qtQuickModuleId, Storage::Version{}, sourceId5); moduleDependenciesSourceId5.emplace_back(qmlNativeModuleId, - Storage::Synchronization::Version{}, + Storage::Version{}, sourceId5); moduleDependenciesSourceId5.emplace_back(qtQuickNativeModuleId, - Storage::Synchronization::Version{}, + Storage::Version{}, sourceId5); package.updatedModuleDependencySourceIds.push_back(sourceId5); package.updatedSourceIds.push_back(sourceId5); @@ -685,7 +685,7 @@ protected: sourceId6, {Storage::Synchronization::ExportedType{qtQuickModuleId, "Children2", - Storage::Synchronization::Version{2}}, + Storage::Version{2}}, Storage::Synchronization::ExportedType{qtQuickNativeModuleId, "QChildren2"}}, {Storage::Synchronization::PropertyDeclaration{"items", Storage::Synchronization::ImportedType{ @@ -697,14 +697,14 @@ protected: Storage::PropertyDeclarationTraits::IsList | Storage::PropertyDeclarationTraits::IsReadOnly}}}); - package.imports.emplace_back(qmlModuleId, Storage::Synchronization::Version{}, sourceId6); - package.imports.emplace_back(pathToModuleId, Storage::Synchronization::Version{}, sourceId6); - package.imports.emplace_back(qtQuickModuleId, Storage::Synchronization::Version{}, sourceId6); + package.imports.emplace_back(qmlModuleId, Storage::Version{}, sourceId6); + package.imports.emplace_back(pathToModuleId, Storage::Version{}, sourceId6); + package.imports.emplace_back(qtQuickModuleId, Storage::Version{}, sourceId6); package.moduleDependencies.emplace_back(qmlNativeModuleId, - Storage::Synchronization::Version{}, + Storage::Version{}, sourceId6); package.moduleDependencies.emplace_back(qtQuickNativeModuleId, - Storage::Synchronization::Version{}, + Storage::Version{}, sourceId6); package.updatedModuleDependencySourceIds.push_back(sourceId6); package.updatedSourceIds.push_back(sourceId6); @@ -727,8 +727,8 @@ protected: { auto package{createSynchronizationPackageWithAliases()}; - package.imports.emplace_back(qmlModuleId, Storage::Synchronization::Version{}, sourceId5); - package.imports.emplace_back(qtQuickModuleId, Storage::Synchronization::Version{}, sourceId5); + package.imports.emplace_back(qmlModuleId, Storage::Version{}, sourceId5); + package.imports.emplace_back(qtQuickModuleId, Storage::Version{}, sourceId5); package.types.push_back(Storage::Synchronization::Type{ "QAliasItem2", @@ -769,10 +769,10 @@ protected: sourceId1, {Storage::Synchronization::ExportedType{qmlModuleId, "Object", - Storage::Synchronization::Version{1}}, + Storage::Version{1}}, Storage::Synchronization::ExportedType{qmlModuleId, "Obj", - Storage::Synchronization::Version{1, 2}}, + Storage::Version{1, 2}}, Storage::Synchronization::ExportedType{qmlModuleId, "BuiltInObj"}, Storage::Synchronization::ExportedType{qmlNativeModuleId, "QObject"}}}); package.types.push_back(Storage::Synchronization::Type{ @@ -783,10 +783,10 @@ protected: sourceId1, {Storage::Synchronization::ExportedType{qmlModuleId, "Object", - Storage::Synchronization::Version{2, 0}}, + Storage::Version{2, 0}}, Storage::Synchronization::ExportedType{qmlModuleId, "Obj", - Storage::Synchronization::Version{2, 3}}, + Storage::Version{2, 3}}, Storage::Synchronization::ExportedType{qmlNativeModuleId, "QObject2"}}}); package.types.push_back(Storage::Synchronization::Type{ "QObject3", @@ -796,10 +796,10 @@ protected: sourceId1, {Storage::Synchronization::ExportedType{qmlModuleId, "Object", - Storage::Synchronization::Version{2, 11}}, + Storage::Version{2, 11}}, Storage::Synchronization::ExportedType{qmlModuleId, "Obj", - Storage::Synchronization::Version{2, 11}}, + Storage::Version{2, 11}}, Storage::Synchronization::ExportedType{qmlNativeModuleId, "QObject3"}}}); package.types.push_back(Storage::Synchronization::Type{ "QObject4", @@ -809,13 +809,13 @@ protected: sourceId1, {Storage::Synchronization::ExportedType{qmlModuleId, "Object", - Storage::Synchronization::Version{3, 4}}, + Storage::Version{3, 4}}, Storage::Synchronization::ExportedType{qmlModuleId, "Obj", - Storage::Synchronization::Version{3, 4}}, + Storage::Version{3, 4}}, Storage::Synchronization::ExportedType{qmlModuleId, "BuiltInObj", - Storage::Synchronization::Version{3, 4}}, + Storage::Version{3, 4}}, Storage::Synchronization::ExportedType{qmlNativeModuleId, "QObject4"}}}); package.updatedSourceIds.push_back(sourceId1); @@ -829,7 +829,7 @@ protected: { SynchronizationPackage package; - package.imports.emplace_back(qmlModuleId, Storage::Synchronization::Version{}, sourceId1); + package.imports.emplace_back(qmlModuleId, Storage::Version{}, sourceId1); package.types.push_back(Storage::Synchronization::Type{ "QObject", @@ -839,7 +839,7 @@ protected: sourceId1, {Storage::Synchronization::ExportedType{qmlModuleId, "Object", - Storage::Synchronization::Version{}}}, + Storage::Version{}}}, {Storage::Synchronization::PropertyDeclaration{"data", Storage::Synchronization::ImportedType{"Object"}, Storage::PropertyDeclarationTraits::IsList}, @@ -858,7 +858,7 @@ protected: sourceId1, {Storage::Synchronization::ExportedType{qmlModuleId, "Object2", - Storage::Synchronization::Version{}}}, + Storage::Version{}}}, {Storage::Synchronization::PropertyDeclaration{ "data2", Storage::Synchronization::ImportedType{"Object3"}, @@ -878,7 +878,7 @@ protected: sourceId1, {Storage::Synchronization::ExportedType{qmlModuleId, "Object3", - Storage::Synchronization::Version{}}}, + Storage::Version{}}}, {Storage::Synchronization::PropertyDeclaration{"data3", Storage::Synchronization::ImportedType{ "Object2"}, @@ -902,7 +902,7 @@ protected: { SynchronizationPackage package; - package.imports.emplace_back(qmlModuleId, Storage::Synchronization::Version{1}, sourceId1); + package.imports.emplace_back(qmlModuleId, Storage::Version{1}, sourceId1); package.updatedModuleIds.push_back(qtQuickModuleId); package.types.push_back(Storage::Synchronization::Type{ "QQuickItem", @@ -912,12 +912,12 @@ protected: sourceId1, {Storage::Synchronization::ExportedType{qtQuickModuleId, "Item", - Storage::Synchronization::Version{1, 0}}}}); + Storage::Version{1, 0}}}}); package.updatedModuleIds.push_back(qmlModuleId); package.moduleExportedImports.emplace_back(qtQuickModuleId, qmlModuleId, - Storage::Synchronization::Version{}, + Storage::Version{}, Storage::Synchronization::IsAutoVersion::Yes); package.types.push_back(Storage::Synchronization::Type{ "QObject", @@ -927,12 +927,12 @@ protected: sourceId2, {Storage::Synchronization::ExportedType{qmlModuleId, "Object", - Storage::Synchronization::Version{1, 0}}}}); + Storage::Version{1, 0}}}}); - package.imports.emplace_back(qtQuickModuleId, Storage::Synchronization::Version{1}, sourceId3); + package.imports.emplace_back(qtQuickModuleId, Storage::Version{1}, sourceId3); package.moduleExportedImports.emplace_back(qtQuick3DModuleId, qtQuickModuleId, - Storage::Synchronization::Version{}, + Storage::Version{}, Storage::Synchronization::IsAutoVersion::Yes); package.updatedModuleIds.push_back(qtQuick3DModuleId); package.types.push_back(Storage::Synchronization::Type{ @@ -943,9 +943,9 @@ protected: sourceId3, {Storage::Synchronization::ExportedType{qtQuick3DModuleId, "Item3D", - Storage::Synchronization::Version{1, 0}}}}); + Storage::Version{1, 0}}}}); - package.imports.emplace_back(qtQuick3DModuleId, Storage::Synchronization::Version{1}, sourceId4); + package.imports.emplace_back(qtQuick3DModuleId, Storage::Version{1}, sourceId4); package.types.push_back(Storage::Synchronization::Type{ "MyItem", Storage::Synchronization::ImportedType{"Object"}, @@ -954,7 +954,7 @@ protected: sourceId4, {Storage::Synchronization::ExportedType{myModuleModuleId, "MyItem", - Storage::Synchronization::Version{1, 0}}}}); + Storage::Version{1, 0}}}}); package.updatedSourceIds = {sourceId1, sourceId2, sourceId3, sourceId4}; @@ -1439,12 +1439,12 @@ TEST_F(ProjectStorage, SynchronizeTypesOverwritesSources) package.types[0].sourceId = sourceId3; package.types[1].sourceId = sourceId4; Storage::Synchronization::Imports newImports; - newImports.emplace_back(qmlModuleId, Storage::Synchronization::Version{}, sourceId3); - newImports.emplace_back(qmlNativeModuleId, Storage::Synchronization::Version{}, sourceId3); - newImports.emplace_back(qtQuickModuleId, Storage::Synchronization::Version{}, sourceId3); - newImports.emplace_back(qtQuickNativeModuleId, Storage::Synchronization::Version{}, sourceId3); - newImports.emplace_back(qmlModuleId, Storage::Synchronization::Version{}, sourceId4); - newImports.emplace_back(qmlNativeModuleId, Storage::Synchronization::Version{}, sourceId4); + newImports.emplace_back(qmlModuleId, Storage::Version{}, sourceId3); + newImports.emplace_back(qmlNativeModuleId, Storage::Version{}, sourceId3); + newImports.emplace_back(qtQuickModuleId, Storage::Version{}, sourceId3); + newImports.emplace_back(qtQuickNativeModuleId, Storage::Version{}, sourceId3); + newImports.emplace_back(qmlModuleId, Storage::Version{}, sourceId4); + newImports.emplace_back(qmlNativeModuleId, Storage::Version{}, sourceId4); storage.synchronize(SynchronizationPackage{newImports, package.types, @@ -1560,7 +1560,7 @@ TEST_F(ProjectStorage, SynchronizeTypesAddQualifiedPrototype) package.types[0].prototype = Storage::Synchronization::QualifiedImportedType{ "Object", Storage::Synchronization::Import{qtQuickModuleId, - Storage::Synchronization::Version{}, + Storage::Version{}, sourceId1}}; package.types.push_back(Storage::Synchronization::Type{ "QQuickObject", @@ -1606,7 +1606,7 @@ TEST_F(ProjectStorage, SynchronizeTypesAddQualifiedExtension) package.types[0].extension = Storage::Synchronization::QualifiedImportedType{ "Object", Storage::Synchronization::Import{qtQuickModuleId, - Storage::Synchronization::Version{}, + Storage::Version{}, sourceId1}}; package.types.push_back(Storage::Synchronization::Type{ "QQuickObject", @@ -1828,7 +1828,7 @@ TEST_F(ProjectStorage, SynchronizeTypesAddPropertyDeclarationQualifiedType) package.types[0].propertyDeclarations[0].typeName = Storage::Synchronization::QualifiedImportedType{ "Object", Storage::Synchronization::Import{qtQuickModuleId, - Storage::Synchronization::Version{}, + Storage::Version{}, sourceId1}}; package.types.push_back( Storage::Synchronization::Type{"QQuickObject", @@ -2018,7 +2018,7 @@ TEST_F(ProjectStorage, UsingNonExistingQualifiedExportedPropertyTypeWithWrongNam package.types[0].propertyDeclarations[0].typeName = Storage::Synchronization::QualifiedImportedType{ "QObject2", Storage::Synchronization::Import{qmlNativeModuleId, - Storage::Synchronization::Version{}, + Storage::Version{}, sourceId1}}; package.types.pop_back(); package.imports = importsSourceId1; @@ -2031,7 +2031,7 @@ TEST_F(ProjectStorage, UsingNonExistingQualifiedExportedPropertyTypeWithWrongMod auto package{createSimpleSynchronizationPackage()}; package.types[0].propertyDeclarations[0].typeName = Storage::Synchronization::QualifiedImportedType{ "QObject", - Storage::Synchronization::Import{qmlModuleId, Storage::Synchronization::Version{}, sourceId1}}; + Storage::Synchronization::Import{qmlModuleId, Storage::Version{}, sourceId1}}; package.types.pop_back(); package.imports = importsSourceId1; @@ -2900,7 +2900,7 @@ TEST_F(ProjectStorage, SynchronizeTypesChangeAliasDeclarationsTypeName) storage.synchronize(package); package.types[2].propertyDeclarations[2].typeName = Storage::Synchronization::ImportedType{ "Obj2"}; - importsSourceId3.emplace_back(pathToModuleId, Storage::Synchronization::Version{}, sourceId3); + importsSourceId3.emplace_back(pathToModuleId, Storage::Version{}, sourceId3); storage.synchronize(SynchronizationPackage{importsSourceId3, {package.types[2]}, {sourceId3}}); @@ -3049,7 +3049,7 @@ TEST_F(ProjectStorage, SynchronizeTypesChangeAliasTargetPropertyDeclarationTypeN storage.synchronize(package); package.types[1].propertyDeclarations[0].typeName = Storage::Synchronization::ImportedType{ "Item"}; - importsSourceId2.emplace_back(qtQuickModuleId, Storage::Synchronization::Version{}, sourceId2); + importsSourceId2.emplace_back(qtQuickModuleId, Storage::Version{}, sourceId2); storage.synchronize(SynchronizationPackage{importsSourceId2, {package.types[1]}, {sourceId2}}); @@ -3117,7 +3117,7 @@ TEST_F(ProjectStorage, SynchronizeTypesRemoveTypeWithAliasTargetPropertyDeclarat auto package{createSynchronizationPackageWithAliases()}; package.types[2].propertyDeclarations[2].typeName = Storage::Synchronization::ImportedType{ "Object2"}; - package.imports.emplace_back(pathToModuleId, Storage::Synchronization::Version{}, sourceId3); + package.imports.emplace_back(pathToModuleId, Storage::Version{}, sourceId3); storage.synchronize(package); ASSERT_THROW(storage.synchronize(SynchronizationPackage{{sourceId4}}), @@ -3129,7 +3129,7 @@ TEST_F(ProjectStorage, SynchronizeTypesRemoveTypeAndAliasPropertyDeclaration) auto package{createSynchronizationPackageWithAliases()}; package.types[2].propertyDeclarations[2].typeName = Storage::Synchronization::ImportedType{ "Object2"}; - package.imports.emplace_back(pathToModuleId, Storage::Synchronization::Version{}, sourceId3); + package.imports.emplace_back(pathToModuleId, Storage::Version{}, sourceId3); storage.synchronize(package); package.types[2].propertyDeclarations.pop_back(); @@ -3250,8 +3250,8 @@ TEST_F(ProjectStorage, RelinkAliasProperty) auto package{createSynchronizationPackageWithAliases()}; package.types[1].propertyDeclarations[0].typeName = Storage::Synchronization::ImportedType{ "Object2"}; - package.imports.emplace_back(pathToModuleId, Storage::Synchronization::Version{}, sourceId2); - package.imports.emplace_back(qtQuickModuleId, Storage::Synchronization::Version{}, sourceId2); + package.imports.emplace_back(pathToModuleId, Storage::Version{}, sourceId2); + package.imports.emplace_back(qtQuickModuleId, Storage::Version{}, sourceId2); storage.synchronize(package); package.types[3].exportedTypes[0].moduleId = qtQuickModuleId; @@ -3282,11 +3282,11 @@ TEST_F(ProjectStorage, DoNotRelinkAliasPropertyForQualifiedImportedTypeName) auto package{createSynchronizationPackageWithAliases()}; package.types[1].propertyDeclarations[0].typeName = Storage::Synchronization::QualifiedImportedType{ "Object2", - Storage::Synchronization::Import{pathToModuleId, Storage::Synchronization::Version{}, sourceId2}}; - package.imports.emplace_back(pathToModuleId, Storage::Synchronization::Version{}, sourceId2); + Storage::Synchronization::Import{pathToModuleId, Storage::Version{}, sourceId2}}; + package.imports.emplace_back(pathToModuleId, Storage::Version{}, sourceId2); storage.synchronize(package); package.types[3].exportedTypes[0].moduleId = qtQuickModuleId; - importsSourceId4.emplace_back(qtQuickModuleId, Storage::Synchronization::Version{}, sourceId4); + importsSourceId4.emplace_back(qtQuickModuleId, Storage::Version{}, sourceId4); ASSERT_THROW(storage.synchronize( SynchronizationPackage{importsSourceId4, {package.types[3]}, {sourceId4}}), @@ -3299,8 +3299,8 @@ TEST_F(ProjectStorage, auto package{createSynchronizationPackageWithAliases()}; package.types[1].propertyDeclarations[0].typeName = Storage::Synchronization::QualifiedImportedType{ "Object2", - Storage::Synchronization::Import{pathToModuleId, Storage::Synchronization::Version{}, sourceId2}}; - package.imports.emplace_back(pathToModuleId, Storage::Synchronization::Version{}, sourceId2); + Storage::Synchronization::Import{pathToModuleId, Storage::Version{}, sourceId2}}; + package.imports.emplace_back(pathToModuleId, Storage::Version{}, sourceId2); package.types.push_back(Storage::Synchronization::Type{ "QObject2", Storage::Synchronization::ImportedType{}, @@ -3366,8 +3366,8 @@ TEST_F(ProjectStorage, DoNotRelinkAliasPropertyForDeletedType) auto package{createSynchronizationPackageWithAliases()}; package.types[1].propertyDeclarations[0].typeName = Storage::Synchronization::ImportedType{ "Object2"}; - package.imports.emplace_back(pathToModuleId, Storage::Synchronization::Version{}, sourceId2); - package.imports.emplace_back(qtQuickModuleId, Storage::Synchronization::Version{}, sourceId2); + package.imports.emplace_back(pathToModuleId, Storage::Version{}, sourceId2); + package.imports.emplace_back(qtQuickModuleId, Storage::Version{}, sourceId2); storage.synchronize(package); package.types[3].exportedTypes[0].moduleId = qtQuickModuleId; @@ -3386,12 +3386,12 @@ TEST_F(ProjectStorage, DoNotRelinkAliasPropertyForDeletedTypeAndPropertyType) auto package{createSynchronizationPackageWithAliases()}; package.types[1].propertyDeclarations[0].typeName = Storage::Synchronization::ImportedType{ "Object2"}; - package.imports.emplace_back(pathToModuleId, Storage::Synchronization::Version{}, sourceId2); - package.imports.emplace_back(qtQuickModuleId, Storage::Synchronization::Version{}, sourceId2); + package.imports.emplace_back(pathToModuleId, Storage::Version{}, sourceId2); + package.imports.emplace_back(qtQuickModuleId, Storage::Version{}, sourceId2); storage.synchronize(package); package.types[0].prototype = Storage::Synchronization::ImportedType{}; - importsSourceId1.emplace_back(pathToModuleId, Storage::Synchronization::Version{}, sourceId1); - importsSourceId4.emplace_back(qtQuickModuleId, Storage::Synchronization::Version{}, sourceId4); + importsSourceId1.emplace_back(pathToModuleId, Storage::Version{}, sourceId1); + importsSourceId4.emplace_back(qtQuickModuleId, Storage::Version{}, sourceId4); package.types[0].propertyDeclarations[0].typeName = Storage::Synchronization::ImportedType{ "Object2"}; package.types[3].propertyDeclarations[0].typeName = Storage::Synchronization::ImportedType{ @@ -3409,12 +3409,12 @@ TEST_F(ProjectStorage, DoNotRelinkAliasPropertyForDeletedTypeAndPropertyTypeName auto package{createSynchronizationPackageWithAliases()}; package.types[1].propertyDeclarations[0].typeName = Storage::Synchronization::ImportedType{ "Object2"}; - package.imports.emplace_back(pathToModuleId, Storage::Synchronization::Version{}, sourceId2); + package.imports.emplace_back(pathToModuleId, Storage::Version{}, sourceId2); storage.synchronize(package); package.types[3].exportedTypes[0].moduleId = qtQuickModuleId; package.types[1].propertyDeclarations[0].typeName = Storage::Synchronization::ImportedType{ "QObject"}; - importsSourceId4.emplace_back(qtQuickModuleId, Storage::Synchronization::Version{}, sourceId4); + importsSourceId4.emplace_back(qtQuickModuleId, Storage::Version{}, sourceId4); storage.synchronize(SynchronizationPackage{importsSourceId2 + importsSourceId4, {package.types[1], package.types[3]}, @@ -3432,8 +3432,8 @@ TEST_F(ProjectStorage, DoNotRelinkPropertyTypeDoesNotExists) auto package{createSynchronizationPackageWithAliases()}; package.types[1].propertyDeclarations[0].typeName = Storage::Synchronization::ImportedType{ "Object2"}; - package.imports.emplace_back(pathToModuleId, Storage::Synchronization::Version{}, sourceId2); - package.imports.emplace_back(qtQuickModuleId, Storage::Synchronization::Version{}, sourceId2); + package.imports.emplace_back(pathToModuleId, Storage::Version{}, sourceId2); + package.imports.emplace_back(qtQuickModuleId, Storage::Version{}, sourceId2); storage.synchronize(package); ASSERT_THROW(storage.synchronize(SynchronizationPackage{{sourceId4}}), @@ -3445,7 +3445,7 @@ TEST_F(ProjectStorage, DoNotRelinkAliasPropertyTypeDoesNotExists) auto package{createSynchronizationPackageWithAliases2()}; package.types[1].propertyDeclarations[0].typeName = Storage::Synchronization::ImportedType{ "Object2"}; - package.imports.emplace_back(pathToModuleId, Storage::Synchronization::Version{}, sourceId2); + package.imports.emplace_back(pathToModuleId, Storage::Version{}, sourceId2); storage.synchronize(package); ASSERT_THROW(storage.synchronize(SynchronizationPackage{{sourceId1}}), @@ -3523,7 +3523,7 @@ TEST_F(ProjectStorage, ChangeQualifiedPrototypeTypeModuleIdThrows) auto package{createSimpleSynchronizationPackage()}; package.types[0].prototype = Storage::Synchronization::QualifiedImportedType{ "Object", - Storage::Synchronization::Import{qmlModuleId, Storage::Synchronization::Version{}, sourceId1}}; + Storage::Synchronization::Import{qmlModuleId, Storage::Version{}, sourceId1}}; storage.synchronize(package); package.types[1].exportedTypes[0].moduleId = qtQuickModuleId; @@ -3538,7 +3538,7 @@ TEST_F(ProjectStorage, ChangeQualifiedExtensionTypeModuleIdThrows) std::swap(package.types.front().extension, package.types.front().prototype); package.types[0].extension = Storage::Synchronization::QualifiedImportedType{ "Object", - Storage::Synchronization::Import{qmlModuleId, Storage::Synchronization::Version{}, sourceId1}}; + Storage::Synchronization::Import{qmlModuleId, Storage::Version{}, sourceId1}}; storage.synchronize(package); package.types[1].exportedTypes[0].moduleId = qtQuickModuleId; @@ -3552,13 +3552,13 @@ TEST_F(ProjectStorage, ChangeQualifiedPrototypeTypeModuleId) auto package{createSimpleSynchronizationPackage()}; package.types[0].prototype = Storage::Synchronization::QualifiedImportedType{ "Object", - Storage::Synchronization::Import{qmlModuleId, Storage::Synchronization::Version{}, sourceId1}}; + Storage::Synchronization::Import{qmlModuleId, Storage::Version{}, sourceId1}}; storage.synchronize(package); package.types[1].exportedTypes[0].moduleId = qtQuickModuleId; package.types[0].prototype = Storage::Synchronization::QualifiedImportedType{ "Object", Storage::Synchronization::Import{qtQuickModuleId, - Storage::Synchronization::Version{}, + Storage::Version{}, sourceId1}}; storage.synchronize(SynchronizationPackage{importsSourceId1 + importsSourceId2, @@ -3579,13 +3579,13 @@ TEST_F(ProjectStorage, ChangeQualifiedExtensionTypeModuleId) std::swap(package.types.front().extension, package.types.front().prototype); package.types[0].extension = Storage::Synchronization::QualifiedImportedType{ "Object", - Storage::Synchronization::Import{qmlModuleId, Storage::Synchronization::Version{}, sourceId1}}; + Storage::Synchronization::Import{qmlModuleId, Storage::Version{}, sourceId1}}; storage.synchronize(package); package.types[1].exportedTypes[0].moduleId = qtQuickModuleId; package.types[0].extension = Storage::Synchronization::QualifiedImportedType{ "Object", Storage::Synchronization::Import{qtQuickModuleId, - Storage::Synchronization::Version{}, + Storage::Version{}, sourceId1}}; storage.synchronize(SynchronizationPackage{importsSourceId1 + importsSourceId2, @@ -3688,9 +3688,9 @@ TEST_F(ProjectStorage, ThrowForPrototypeChainCycles) sourceId3, {Storage::Synchronization::ExportedType{pathToModuleId, "Object2"}, Storage::Synchronization::ExportedType{pathToModuleId, "Obj2"}}}); - package.imports.emplace_back(pathToModuleId, Storage::Synchronization::Version{}, sourceId2); - package.imports.emplace_back(qtQuickModuleId, Storage::Synchronization::Version{}, sourceId3); - package.imports.emplace_back(pathToModuleId, Storage::Synchronization::Version{}, sourceId3); + package.imports.emplace_back(pathToModuleId, Storage::Version{}, sourceId2); + package.imports.emplace_back(qtQuickModuleId, Storage::Version{}, sourceId3); + package.imports.emplace_back(pathToModuleId, Storage::Version{}, sourceId3); ASSERT_THROW(storage.synchronize(SynchronizationPackage{package.imports, package.types, @@ -3713,9 +3713,9 @@ TEST_F(ProjectStorage, ThrowForExtensionChainCycles) sourceId3, {Storage::Synchronization::ExportedType{pathToModuleId, "Object2"}, Storage::Synchronization::ExportedType{pathToModuleId, "Obj2"}}}); - package.imports.emplace_back(pathToModuleId, Storage::Synchronization::Version{}, sourceId2); - package.imports.emplace_back(qtQuickModuleId, Storage::Synchronization::Version{}, sourceId3); - package.imports.emplace_back(pathToModuleId, Storage::Synchronization::Version{}, sourceId3); + package.imports.emplace_back(pathToModuleId, Storage::Version{}, sourceId2); + package.imports.emplace_back(qtQuickModuleId, Storage::Version{}, sourceId3); + package.imports.emplace_back(pathToModuleId, Storage::Version{}, sourceId3); ASSERT_THROW(storage.synchronize(SynchronizationPackage{package.imports, package.types, @@ -3748,7 +3748,7 @@ TEST_F(ProjectStorage, ThrowForTypeIdAndPrototypeIdAreTheSameForRelinking) storage.synchronize(package); package.types[1].prototype = Storage::Synchronization::ImportedType{"Item"}; package.types[1].typeName = "QObject2"; - importsSourceId2.emplace_back(qtQuickModuleId, Storage::Synchronization::Version{}, sourceId2); + importsSourceId2.emplace_back(qtQuickModuleId, Storage::Version{}, sourceId2); ASSERT_THROW(storage.synchronize( SynchronizationPackage{importsSourceId2, {package.types[1]}, {sourceId2}}), @@ -3762,7 +3762,7 @@ TEST_F(ProjectStorage, ThrowForTypeIdAndExtenssionIdAreTheSameForRelinking) storage.synchronize(package); package.types[1].extension = Storage::Synchronization::ImportedType{"Item"}; package.types[1].typeName = "QObject2"; - importsSourceId2.emplace_back(qtQuickModuleId, Storage::Synchronization::Version{}, sourceId2); + importsSourceId2.emplace_back(qtQuickModuleId, Storage::Version{}, sourceId2); ASSERT_THROW(storage.synchronize( SynchronizationPackage{importsSourceId2, {package.types[1]}, {sourceId2}}), @@ -3793,7 +3793,7 @@ TEST_F(ProjectStorage, RecursiveAliasesChangePropertyType) storage.synchronize(package); package.types[1].propertyDeclarations[0].typeName = Storage::Synchronization::ImportedType{ "Object2"}; - importsSourceId2.emplace_back(pathToModuleId, Storage::Synchronization::Version{}, sourceId2); + importsSourceId2.emplace_back(pathToModuleId, Storage::Version{}, sourceId2); storage.synchronize(SynchronizationPackage{importsSourceId2, {package.types[1]}, {sourceId2}}); @@ -3879,7 +3879,7 @@ TEST_F(ProjectStorage, UpdateAliasesAfterChangePropertyToAlias) package.types[1].propertyDeclarations.clear(); package.types[1].propertyDeclarations.push_back(Storage::Synchronization::PropertyDeclaration{ "objects", Storage::Synchronization::ImportedType{"Object2"}, "objects"}); - importsSourceId2.emplace_back(pathToModuleId, Storage::Synchronization::Version{}, sourceId2); + importsSourceId2.emplace_back(pathToModuleId, Storage::Version{}, sourceId2); storage.synchronize(SynchronizationPackage{importsSourceId2, {package.types[1]}, {sourceId2}}); @@ -3903,7 +3903,7 @@ TEST_F(ProjectStorage, CheckForProtoTypeCycleThrows) package.types[1].propertyDeclarations.clear(); package.types[1].propertyDeclarations.push_back(Storage::Synchronization::PropertyDeclaration{ "objects", Storage::Synchronization::ImportedType{"AliasItem2"}, "objects"}); - package.imports.emplace_back(qtQuickModuleId, Storage::Synchronization::Version{}, sourceId2); + package.imports.emplace_back(qtQuickModuleId, Storage::Version{}, sourceId2); ASSERT_THROW(storage.synchronize(package), QmlDesigner::AliasChainCycle); } @@ -3915,7 +3915,7 @@ TEST_F(ProjectStorage, CheckForProtoTypeCycleAfterUpdateThrows) package.types[1].propertyDeclarations.clear(); package.types[1].propertyDeclarations.push_back(Storage::Synchronization::PropertyDeclaration{ "objects", Storage::Synchronization::ImportedType{"AliasItem2"}, "objects"}); - importsSourceId2.emplace_back(qtQuickModuleId, Storage::Synchronization::Version{}, sourceId2); + importsSourceId2.emplace_back(qtQuickModuleId, Storage::Version{}, sourceId2); ASSERT_THROW(storage.synchronize( SynchronizationPackage{importsSourceId2, {package.types[1]}, {sourceId2}}), @@ -3927,7 +3927,7 @@ TEST_F(ProjectStorage, QualifiedPrototype) auto package{createSimpleSynchronizationPackage()}; package.types[0].prototype = Storage::Synchronization::QualifiedImportedType{ "Object", - Storage::Synchronization::Import{qmlModuleId, Storage::Synchronization::Version{}, sourceId1}}; + Storage::Synchronization::Import{qmlModuleId, Storage::Version{}, sourceId1}}; package.types.push_back( Storage::Synchronization::Type{"QQuickObject", Storage::Synchronization::ImportedType{}, @@ -3936,7 +3936,7 @@ TEST_F(ProjectStorage, QualifiedPrototype) sourceId3, {Storage::Synchronization::ExportedType{qtQuickModuleId, "Object"}}}); - package.imports.emplace_back(qtQuickModuleId, Storage::Synchronization::Version{}, sourceId3); + package.imports.emplace_back(qtQuickModuleId, Storage::Version{}, sourceId3); package.updatedSourceIds.push_back(sourceId3); storage.synchronize(package); @@ -3955,7 +3955,7 @@ TEST_F(ProjectStorage, QualifiedExtension) std::swap(package.types.front().extension, package.types.front().prototype); package.types[0].extension = Storage::Synchronization::QualifiedImportedType{ "Object", - Storage::Synchronization::Import{qmlModuleId, Storage::Synchronization::Version{}, sourceId1}}; + Storage::Synchronization::Import{qmlModuleId, Storage::Version{}, sourceId1}}; package.types.push_back( Storage::Synchronization::Type{"QQuickObject", Storage::Synchronization::ImportedType{}, @@ -3964,7 +3964,7 @@ TEST_F(ProjectStorage, QualifiedExtension) sourceId3, {Storage::Synchronization::ExportedType{qtQuickModuleId, "Object"}}}); - package.imports.emplace_back(qtQuickModuleId, Storage::Synchronization::Version{}, sourceId3); + package.imports.emplace_back(qtQuickModuleId, Storage::Version{}, sourceId3); package.updatedSourceIds.push_back(sourceId3); storage.synchronize(package); @@ -3983,7 +3983,7 @@ TEST_F(ProjectStorage, QualifiedPrototypeUpperDownTheModuleChainThrows) package.types[0].prototype = Storage::Synchronization::QualifiedImportedType{ "Object", Storage::Synchronization::Import{qtQuickModuleId, - Storage::Synchronization::Version{}, + Storage::Version{}, sourceId1}}; ASSERT_THROW(storage.synchronize(package), QmlDesigner::TypeNameDoesNotExists); @@ -3996,7 +3996,7 @@ TEST_F(ProjectStorage, QualifiedExtensionUpperDownTheModuleChainThrows) package.types[0].extension = Storage::Synchronization::QualifiedImportedType{ "Object", Storage::Synchronization::Import{qtQuickModuleId, - Storage::Synchronization::Version{}, + Storage::Version{}, sourceId1}}; ASSERT_THROW(storage.synchronize(package), QmlDesigner::TypeNameDoesNotExists); @@ -4008,7 +4008,7 @@ TEST_F(ProjectStorage, QualifiedPrototypeUpperInTheModuleChain) package.types[0].prototype = Storage::Synchronization::QualifiedImportedType{ "Object", Storage::Synchronization::Import{qtQuickModuleId, - Storage::Synchronization::Version{}, + Storage::Version{}, sourceId1}}; package.types.push_back( Storage::Synchronization::Type{"QQuickObject", @@ -4018,7 +4018,7 @@ TEST_F(ProjectStorage, QualifiedPrototypeUpperInTheModuleChain) sourceId3, {Storage::Synchronization::ExportedType{qtQuickModuleId, "Object"}}}); - package.imports.emplace_back(qtQuickModuleId, Storage::Synchronization::Version{}, sourceId3); + package.imports.emplace_back(qtQuickModuleId, Storage::Version{}, sourceId3); package.updatedSourceIds.push_back(sourceId3); storage.synchronize(package); @@ -4038,7 +4038,7 @@ TEST_F(ProjectStorage, QualifiedExtensionUpperInTheModuleChain) package.types[0].extension = Storage::Synchronization::QualifiedImportedType{ "Object", Storage::Synchronization::Import{qtQuickModuleId, - Storage::Synchronization::Version{}, + Storage::Version{}, sourceId1}}; package.types.push_back( Storage::Synchronization::Type{"QQuickObject", @@ -4048,7 +4048,7 @@ TEST_F(ProjectStorage, QualifiedExtensionUpperInTheModuleChain) sourceId3, {Storage::Synchronization::ExportedType{qtQuickModuleId, "Object"}}}); - package.imports.emplace_back(qtQuickModuleId, Storage::Synchronization::Version{}, sourceId3); + package.imports.emplace_back(qtQuickModuleId, Storage::Version{}, sourceId3); package.updatedSourceIds.push_back(sourceId3); storage.synchronize(package); @@ -4066,7 +4066,7 @@ TEST_F(ProjectStorage, QualifiedPrototypeWithWrongVersionThrows) auto package{createSimpleSynchronizationPackage()}; package.types[0].prototype = Storage::Synchronization::QualifiedImportedType{ "Object", - Storage::Synchronization::Import{qmlModuleId, Storage::Synchronization::Version{4}, sourceId1}}; + Storage::Synchronization::Import{qmlModuleId, Storage::Version{4}, sourceId1}}; package.types.push_back( Storage::Synchronization::Type{"QQuickObject", Storage::Synchronization::ImportedType{}, @@ -4075,7 +4075,7 @@ TEST_F(ProjectStorage, QualifiedPrototypeWithWrongVersionThrows) sourceId3, {Storage::Synchronization::ExportedType{qtQuickModuleId, "Object"}}}); - package.imports.emplace_back(qtQuickModuleId, Storage::Synchronization::Version{}, sourceId3); + package.imports.emplace_back(qtQuickModuleId, Storage::Version{}, sourceId3); package.updatedSourceIds.push_back(sourceId3); ASSERT_THROW(storage.synchronize(package), QmlDesigner::TypeNameDoesNotExists); @@ -4087,7 +4087,7 @@ TEST_F(ProjectStorage, QualifiedExtensionWithWrongVersionThrows) std::swap(package.types.front().extension, package.types.front().prototype); package.types[0].extension = Storage::Synchronization::QualifiedImportedType{ "Object", - Storage::Synchronization::Import{qmlModuleId, Storage::Synchronization::Version{4}, sourceId1}}; + Storage::Synchronization::Import{qmlModuleId, Storage::Version{4}, sourceId1}}; package.types.push_back( Storage::Synchronization::Type{"QQuickObject", Storage::Synchronization::ImportedType{}, @@ -4096,7 +4096,7 @@ TEST_F(ProjectStorage, QualifiedExtensionWithWrongVersionThrows) sourceId3, {Storage::Synchronization::ExportedType{qtQuickModuleId, "Object"}}}); - package.imports.emplace_back(qtQuickModuleId, Storage::Synchronization::Version{}, sourceId3); + package.imports.emplace_back(qtQuickModuleId, Storage::Version{}, sourceId3); package.updatedSourceIds.push_back(sourceId3); ASSERT_THROW(storage.synchronize(package), QmlDesigner::TypeNameDoesNotExists); @@ -4105,7 +4105,7 @@ TEST_F(ProjectStorage, QualifiedExtensionWithWrongVersionThrows) TEST_F(ProjectStorage, QualifiedPrototypeWithVersion) { auto package{createSimpleSynchronizationPackage()}; - package.imports[0].version = Storage::Synchronization::Version{2}; + package.imports[0].version = Storage::Version{2}; package.types[0].prototype = Storage::Synchronization::QualifiedImportedType{"Object", package.imports[0]}; package.types.push_back( @@ -4116,7 +4116,7 @@ TEST_F(ProjectStorage, QualifiedPrototypeWithVersion) sourceId3, {Storage::Synchronization::ExportedType{qtQuickModuleId, "Object"}}}); - package.imports.emplace_back(qtQuickModuleId, Storage::Synchronization::Version{}, sourceId3); + package.imports.emplace_back(qtQuickModuleId, Storage::Version{}, sourceId3); package.updatedSourceIds.push_back(sourceId3); storage.synchronize(package); @@ -4133,7 +4133,7 @@ TEST_F(ProjectStorage, QualifiedExtensionWithVersion) { auto package{createSimpleSynchronizationPackage()}; std::swap(package.types.front().extension, package.types.front().prototype); - package.imports[0].version = Storage::Synchronization::Version{2}; + package.imports[0].version = Storage::Version{2}; package.types[0].extension = Storage::Synchronization::QualifiedImportedType{"Object", package.imports[0]}; package.types.push_back( @@ -4144,7 +4144,7 @@ TEST_F(ProjectStorage, QualifiedExtensionWithVersion) sourceId3, {Storage::Synchronization::ExportedType{qtQuickModuleId, "Object"}}}); - package.imports.emplace_back(qtQuickModuleId, Storage::Synchronization::Version{}, sourceId3); + package.imports.emplace_back(qtQuickModuleId, Storage::Version{}, sourceId3); package.updatedSourceIds.push_back(sourceId3); storage.synchronize(package); @@ -4160,10 +4160,10 @@ TEST_F(ProjectStorage, QualifiedExtensionWithVersion) TEST_F(ProjectStorage, QualifiedPrototypeWithVersionInTheProtoTypeChain) { auto package{createSimpleSynchronizationPackage()}; - package.imports[1].version = Storage::Synchronization::Version{2}; + package.imports[1].version = Storage::Version{2}; package.types[0].prototype = Storage::Synchronization::QualifiedImportedType{"Object", package.imports[1]}; - package.types[0].exportedTypes[0].version = Storage::Synchronization::Version{2}; + package.types[0].exportedTypes[0].version = Storage::Version{2}; package.types.push_back(Storage::Synchronization::Type{ "QQuickObject", Storage::Synchronization::ImportedType{}, @@ -4172,8 +4172,8 @@ TEST_F(ProjectStorage, QualifiedPrototypeWithVersionInTheProtoTypeChain) sourceId3, {Storage::Synchronization::ExportedType{qtQuickModuleId, "Object", - Storage::Synchronization::Version{2}}}}); - package.imports.emplace_back(qtQuickModuleId, Storage::Synchronization::Version{}, sourceId3); + Storage::Version{2}}}}); + package.imports.emplace_back(qtQuickModuleId, Storage::Version{}, sourceId3); package.updatedSourceIds.push_back(sourceId3); storage.synchronize(package); @@ -4190,10 +4190,10 @@ TEST_F(ProjectStorage, QualifiedExtensionWithVersionInTheProtoTypeChain) { auto package{createSimpleSynchronizationPackage()}; std::swap(package.types.front().extension, package.types.front().prototype); - package.imports[1].version = Storage::Synchronization::Version{2}; + package.imports[1].version = Storage::Version{2}; package.types[0].extension = Storage::Synchronization::QualifiedImportedType{"Object", package.imports[1]}; - package.types[0].exportedTypes[0].version = Storage::Synchronization::Version{2}; + package.types[0].exportedTypes[0].version = Storage::Version{2}; package.types.push_back(Storage::Synchronization::Type{ "QQuickObject", Storage::Synchronization::ImportedType{}, @@ -4202,8 +4202,8 @@ TEST_F(ProjectStorage, QualifiedExtensionWithVersionInTheProtoTypeChain) sourceId3, {Storage::Synchronization::ExportedType{qtQuickModuleId, "Object", - Storage::Synchronization::Version{2}}}}); - package.imports.emplace_back(qtQuickModuleId, Storage::Synchronization::Version{}, sourceId3); + Storage::Version{2}}}}); + package.imports.emplace_back(qtQuickModuleId, Storage::Version{}, sourceId3); package.updatedSourceIds.push_back(sourceId3); storage.synchronize(package); @@ -4222,7 +4222,7 @@ TEST_F(ProjectStorage, QualifiedPrototypeWithVersionDownTheProtoTypeChainThrows) package.types[0].prototype = Storage::Synchronization::QualifiedImportedType{ "Object", Storage::Synchronization::Import{qtQuickModuleId, - Storage::Synchronization::Version{2}, + Storage::Version{2}, sourceId1}}; ASSERT_THROW(storage.synchronize(package), QmlDesigner::TypeNameDoesNotExists); @@ -4235,7 +4235,7 @@ TEST_F(ProjectStorage, QualifiedExtensionWithVersionDownTheProtoTypeChainThrows) package.types[0].extension = Storage::Synchronization::QualifiedImportedType{ "Object", Storage::Synchronization::Import{qtQuickModuleId, - Storage::Synchronization::Version{2}, + Storage::Version{2}, sourceId1}}; ASSERT_THROW(storage.synchronize(package), QmlDesigner::TypeNameDoesNotExists); @@ -4246,7 +4246,7 @@ TEST_F(ProjectStorage, QualifiedPropertyDeclarationTypeName) auto package{createSimpleSynchronizationPackage()}; package.types[0].propertyDeclarations[0].typeName = Storage::Synchronization::QualifiedImportedType{ "Object", - Storage::Synchronization::Import{qmlModuleId, Storage::Synchronization::Version{}, sourceId1}}; + Storage::Synchronization::Import{qmlModuleId, Storage::Version{}, sourceId1}}; package.types.push_back( Storage::Synchronization::Type{"QQuickObject", Storage::Synchronization::ImportedType{}, @@ -4255,7 +4255,7 @@ TEST_F(ProjectStorage, QualifiedPropertyDeclarationTypeName) sourceId3, {Storage::Synchronization::ExportedType{qtQuickModuleId, "Object"}}}); - package.imports.emplace_back(qtQuickModuleId, Storage::Synchronization::Version{}, sourceId3); + package.imports.emplace_back(qtQuickModuleId, Storage::Version{}, sourceId3); package.updatedSourceIds.push_back(sourceId3); storage.synchronize(package); @@ -4274,7 +4274,7 @@ TEST_F(ProjectStorage, QualifiedPropertyDeclarationTypeNameDownTheModuleChainThr package.types[0].propertyDeclarations[0].typeName = Storage::Synchronization::QualifiedImportedType{ "Object", Storage::Synchronization::Import{qtQuickModuleId, - Storage::Synchronization::Version{}, + Storage::Version{}, sourceId1}}; ASSERT_THROW(storage.synchronize(package), QmlDesigner::TypeNameDoesNotExists); @@ -4286,7 +4286,7 @@ TEST_F(ProjectStorage, QualifiedPropertyDeclarationTypeNameInTheModuleChain) package.types[0].propertyDeclarations[0].typeName = Storage::Synchronization::QualifiedImportedType{ "Object", Storage::Synchronization::Import{qtQuickModuleId, - Storage::Synchronization::Version{}, + Storage::Version{}, sourceId1}}; package.types.push_back( Storage::Synchronization::Type{"QQuickObject", @@ -4296,7 +4296,7 @@ TEST_F(ProjectStorage, QualifiedPropertyDeclarationTypeNameInTheModuleChain) sourceId3, {Storage::Synchronization::ExportedType{qtQuickModuleId, "Object"}}}); - package.imports.emplace_back(qtQuickModuleId, Storage::Synchronization::Version{}, sourceId3); + package.imports.emplace_back(qtQuickModuleId, Storage::Version{}, sourceId3); package.updatedSourceIds.push_back(sourceId3); storage.synchronize(package); @@ -4314,8 +4314,8 @@ TEST_F(ProjectStorage, QualifiedPropertyDeclarationTypeNameWithVersion) auto package{createSimpleSynchronizationPackage()}; package.types[0].propertyDeclarations[0].typeName = Storage::Synchronization::QualifiedImportedType{ "Object", - Storage::Synchronization::Import{qmlModuleId, Storage::Synchronization::Version{2}, sourceId1}}; - package.imports.emplace_back(qmlModuleId, Storage::Synchronization::Version{2}, sourceId1); + Storage::Synchronization::Import{qmlModuleId, Storage::Version{2}, sourceId1}}; + package.imports.emplace_back(qmlModuleId, Storage::Version{2}, sourceId1); storage.synchronize(package); @@ -4332,7 +4332,7 @@ TEST_F(ProjectStorage, ChangePropertyTypeModuleIdWithQualifiedTypeThrows) auto package{createSimpleSynchronizationPackage()}; package.types[0].propertyDeclarations[0].typeName = Storage::Synchronization::QualifiedImportedType{ "Object", - Storage::Synchronization::Import{qmlModuleId, Storage::Synchronization::Version{}, sourceId1}}; + Storage::Synchronization::Import{qmlModuleId, Storage::Version{}, sourceId1}}; storage.synchronize(package); package.types[1].exportedTypes[0].moduleId = qtQuickModuleId; @@ -4346,15 +4346,15 @@ TEST_F(ProjectStorage, ChangePropertyTypeModuleIdWithQualifiedType) auto package{createSimpleSynchronizationPackage()}; package.types[0].propertyDeclarations[0].typeName = Storage::Synchronization::QualifiedImportedType{ "Object", - Storage::Synchronization::Import{qmlModuleId, Storage::Synchronization::Version{}, sourceId1}}; + Storage::Synchronization::Import{qmlModuleId, Storage::Version{}, sourceId1}}; storage.synchronize(package); package.types[0].propertyDeclarations[0].typeName = Storage::Synchronization::QualifiedImportedType{ "Object", Storage::Synchronization::Import{qtQuickModuleId, - Storage::Synchronization::Version{}, + Storage::Version{}, sourceId1}}; package.types[1].exportedTypes[0].moduleId = qtQuickModuleId; - package.imports.emplace_back(qtQuickModuleId, Storage::Synchronization::Version{}, sourceId2); + package.imports.emplace_back(qtQuickModuleId, Storage::Version{}, sourceId2); storage.synchronize(package); @@ -4475,9 +4475,9 @@ TEST_F(ProjectStorage, FetchByMajorVersionForImportedType) sourceId2, {Storage::Synchronization::ExportedType{qtQuickModuleId, "Item", - Storage::Synchronization::Version{}}}}; + Storage::Version{}}}}; Storage::Synchronization::Import import{qmlModuleId, - Storage::Synchronization::Version{1}, + Storage::Version{1}, sourceId2}; storage.synchronize(SynchronizationPackage{{import}, {type}, {sourceId2}}); @@ -4492,7 +4492,7 @@ TEST_F(ProjectStorage, FetchByMajorVersionForQualifiedImportedType) auto package{createSynchronizationPackageWithVersions()}; storage.synchronize(package); Storage::Synchronization::Import import{qmlModuleId, - Storage::Synchronization::Version{1}, + Storage::Version{1}, sourceId2}; Storage::Synchronization::Type type{ "Item", @@ -4502,7 +4502,7 @@ TEST_F(ProjectStorage, FetchByMajorVersionForQualifiedImportedType) sourceId2, {Storage::Synchronization::ExportedType{qtQuickModuleId, "Item", - Storage::Synchronization::Version{}}}}; + Storage::Version{}}}}; storage.synchronize(SynchronizationPackage{{import}, {type}, {sourceId2}}); @@ -4523,9 +4523,9 @@ TEST_F(ProjectStorage, FetchByMajorVersionAndMinorVersionForImportedType) sourceId2, {Storage::Synchronization::ExportedType{qtQuickModuleId, "Item", - Storage::Synchronization::Version{}}}}; + Storage::Version{}}}}; Storage::Synchronization::Import import{qmlModuleId, - Storage::Synchronization::Version{1, 2}, + Storage::Version{1, 2}, sourceId2}; storage.synchronize(SynchronizationPackage{{import}, {type}, {sourceId2}}); @@ -4540,7 +4540,7 @@ TEST_F(ProjectStorage, FetchByMajorVersionAndMinorVersionForQualifiedImportedTyp auto package{createSynchronizationPackageWithVersions()}; storage.synchronize(package); Storage::Synchronization::Import import{qmlModuleId, - Storage::Synchronization::Version{1, 2}, + Storage::Version{1, 2}, sourceId2}; Storage::Synchronization::Type type{ "Item", @@ -4550,7 +4550,7 @@ TEST_F(ProjectStorage, FetchByMajorVersionAndMinorVersionForQualifiedImportedTyp sourceId2, {Storage::Synchronization::ExportedType{qtQuickModuleId, "Item", - Storage::Synchronization::Version{}}}}; + Storage::Version{}}}}; storage.synchronize(SynchronizationPackage{{import}, {type}, {sourceId2}}); @@ -4572,9 +4572,9 @@ TEST_F(ProjectStorage, sourceId2, {Storage::Synchronization::ExportedType{qtQuickModuleId, "Item", - Storage::Synchronization::Version{}}}}; + Storage::Version{}}}}; Storage::Synchronization::Import import{qmlModuleId, - Storage::Synchronization::Version{1, 1}, + Storage::Version{1, 1}, sourceId2}; ASSERT_THROW(storage.synchronize(SynchronizationPackage{{import}, {type}, {sourceId2}}), @@ -4587,7 +4587,7 @@ TEST_F(ProjectStorage, auto package{createSynchronizationPackageWithVersions()}; storage.synchronize(package); Storage::Synchronization::Import import{qmlModuleId, - Storage::Synchronization::Version{1, 1}, + Storage::Version{1, 1}, sourceId2}; Storage::Synchronization::Type type{ "Item", @@ -4597,7 +4597,7 @@ TEST_F(ProjectStorage, sourceId2, {Storage::Synchronization::ExportedType{qtQuickModuleId, "Item", - Storage::Synchronization::Version{}}}}; + Storage::Version{}}}}; ASSERT_THROW(storage.synchronize(SynchronizationPackage{{import}, {type}, {sourceId2}}), QmlDesigner::TypeNameDoesNotExists); @@ -4615,9 +4615,9 @@ TEST_F(ProjectStorage, FetchLowMinorVersionForImportedTypeThrows) sourceId2, {Storage::Synchronization::ExportedType{qtQuickModuleId, "Item", - Storage::Synchronization::Version{}}}}; + Storage::Version{}}}}; Storage::Synchronization::Import import{qmlModuleId, - Storage::Synchronization::Version{1, 1}, + Storage::Version{1, 1}, sourceId2}; ASSERT_THROW(storage.synchronize(SynchronizationPackage{{import}, {type}, {sourceId2}}), @@ -4629,7 +4629,7 @@ TEST_F(ProjectStorage, FetchLowMinorVersionForQualifiedImportedTypeThrows) auto package{createSynchronizationPackageWithVersions()}; storage.synchronize(package); Storage::Synchronization::Import import{qmlModuleId, - Storage::Synchronization::Version{1, 1}, + Storage::Version{1, 1}, sourceId2}; Storage::Synchronization::Type type{ "Item", @@ -4639,7 +4639,7 @@ TEST_F(ProjectStorage, FetchLowMinorVersionForQualifiedImportedTypeThrows) sourceId2, {Storage::Synchronization::ExportedType{qtQuickModuleId, "Item", - Storage::Synchronization::Version{}}}}; + Storage::Version{}}}}; ASSERT_THROW(storage.synchronize(SynchronizationPackage{{import}, {type}, {sourceId2}}), QmlDesigner::TypeNameDoesNotExists); @@ -4657,9 +4657,9 @@ TEST_F(ProjectStorage, FetchHigherMinorVersionForImportedType) sourceId2, {Storage::Synchronization::ExportedType{qtQuickModuleId, "Item", - Storage::Synchronization::Version{}}}}; + Storage::Version{}}}}; Storage::Synchronization::Import import{qmlModuleId, - Storage::Synchronization::Version{1, 3}, + Storage::Version{1, 3}, sourceId2}; storage.synchronize(SynchronizationPackage{{import}, {type}, {sourceId2}}); @@ -4674,7 +4674,7 @@ TEST_F(ProjectStorage, FetchHigherMinorVersionForQualifiedImportedType) auto package{createSynchronizationPackageWithVersions()}; storage.synchronize(package); Storage::Synchronization::Import import{qmlModuleId, - Storage::Synchronization::Version{1, 3}, + Storage::Version{1, 3}, sourceId2}; Storage::Synchronization::Type type{ "Item", @@ -4684,7 +4684,7 @@ TEST_F(ProjectStorage, FetchHigherMinorVersionForQualifiedImportedType) sourceId2, {Storage::Synchronization::ExportedType{qtQuickModuleId, "Item", - Storage::Synchronization::Version{}}}}; + Storage::Version{}}}}; storage.synchronize(SynchronizationPackage{{import}, {type}, {sourceId2}}); @@ -4705,9 +4705,9 @@ TEST_F(ProjectStorage, FetchDifferentMajorVersionForImportedTypeThrows) sourceId2, {Storage::Synchronization::ExportedType{qtQuickModuleId, "Item", - Storage::Synchronization::Version{}}}}; + Storage::Version{}}}}; Storage::Synchronization::Import import{qmlModuleId, - Storage::Synchronization::Version{3, 1}, + Storage::Version{3, 1}, sourceId2}; ASSERT_THROW(storage.synchronize(SynchronizationPackage{{import}, {type}, {sourceId2}}), @@ -4719,7 +4719,7 @@ TEST_F(ProjectStorage, FetchDifferentMajorVersionForQualifiedImportedTypeThrows) auto package{createSynchronizationPackageWithVersions()}; storage.synchronize(package); Storage::Synchronization::Import import{qmlModuleId, - Storage::Synchronization::Version{3, 1}, + Storage::Version{3, 1}, sourceId2}; Storage::Synchronization::Type type{ "Item", @@ -4729,7 +4729,7 @@ TEST_F(ProjectStorage, FetchDifferentMajorVersionForQualifiedImportedTypeThrows) sourceId2, {Storage::Synchronization::ExportedType{qtQuickModuleId, "Item", - Storage::Synchronization::Version{}}}}; + Storage::Version{}}}}; ASSERT_THROW(storage.synchronize(SynchronizationPackage{{import}, {type}, {sourceId2}}), QmlDesigner::TypeNameDoesNotExists); @@ -4747,9 +4747,9 @@ TEST_F(ProjectStorage, FetchOtherTypeByDifferentVersionForImportedType) sourceId2, {Storage::Synchronization::ExportedType{qtQuickModuleId, "Item", - Storage::Synchronization::Version{}}}}; + Storage::Version{}}}}; Storage::Synchronization::Import import{qmlModuleId, - Storage::Synchronization::Version{2, 3}, + Storage::Version{2, 3}, sourceId2}; storage.synchronize(SynchronizationPackage{{import}, {type}, {sourceId2}}); @@ -4764,7 +4764,7 @@ TEST_F(ProjectStorage, FetchOtherTypeByDifferentVersionForQualifiedImportedType) auto package{createSynchronizationPackageWithVersions()}; storage.synchronize(package); Storage::Synchronization::Import import{qmlModuleId, - Storage::Synchronization::Version{2, 3}, + Storage::Version{2, 3}, sourceId2}; Storage::Synchronization::Type type{ "Item", @@ -4774,7 +4774,7 @@ TEST_F(ProjectStorage, FetchOtherTypeByDifferentVersionForQualifiedImportedType) sourceId2, {Storage::Synchronization::ExportedType{qtQuickModuleId, "Item", - Storage::Synchronization::Version{}}}}; + Storage::Version{}}}}; storage.synchronize(SynchronizationPackage{{import}, {type}, {sourceId2}}); @@ -4795,8 +4795,8 @@ TEST_F(ProjectStorage, FetchHighestVersionForImportWithoutVersionForImportedType sourceId2, {Storage::Synchronization::ExportedType{qtQuickModuleId, "Item", - Storage::Synchronization::Version{}}}}; - Storage::Synchronization::Import import{qmlModuleId, Storage::Synchronization::Version{}, sourceId2}; + Storage::Version{}}}}; + Storage::Synchronization::Import import{qmlModuleId, Storage::Version{}, sourceId2}; storage.synchronize(SynchronizationPackage{{import}, {type}, {sourceId2}}); @@ -4809,7 +4809,7 @@ TEST_F(ProjectStorage, FetchHighestVersionForImportWithoutVersionForQualifiedImp { auto package{createSynchronizationPackageWithVersions()}; storage.synchronize(package); - Storage::Synchronization::Import import{qmlModuleId, Storage::Synchronization::Version{}, sourceId2}; + Storage::Synchronization::Import import{qmlModuleId, Storage::Version{}, sourceId2}; Storage::Synchronization::Type type{ "Item", Storage::Synchronization::QualifiedImportedType{"Obj", import}, @@ -4818,7 +4818,7 @@ TEST_F(ProjectStorage, FetchHighestVersionForImportWithoutVersionForQualifiedImp sourceId2, {Storage::Synchronization::ExportedType{qtQuickModuleId, "Item", - Storage::Synchronization::Version{}}}}; + Storage::Version{}}}}; storage.synchronize(SynchronizationPackage{{import}, {type}, {sourceId2}}); @@ -4839,9 +4839,9 @@ TEST_F(ProjectStorage, FetchHighestVersionForImportWithMajorVersionForImportedTy sourceId2, {Storage::Synchronization::ExportedType{qtQuickModuleId, "Item", - Storage::Synchronization::Version{}}}}; + Storage::Version{}}}}; Storage::Synchronization::Import import{qmlModuleId, - Storage::Synchronization::Version{2}, + Storage::Version{2}, sourceId2}; storage.synchronize(SynchronizationPackage{{import}, {type}, {sourceId2}}); @@ -4856,7 +4856,7 @@ TEST_F(ProjectStorage, FetchHighestVersionForImportWithMajorVersionForQualifiedI auto package{createSynchronizationPackageWithVersions()}; storage.synchronize(package); Storage::Synchronization::Import import{qmlModuleId, - Storage::Synchronization::Version{2}, + Storage::Version{2}, sourceId2}; Storage::Synchronization::Type type{ "Item", @@ -4866,7 +4866,7 @@ TEST_F(ProjectStorage, FetchHighestVersionForImportWithMajorVersionForQualifiedI sourceId2, {Storage::Synchronization::ExportedType{qtQuickModuleId, "Item", - Storage::Synchronization::Version{}}}}; + Storage::Version{}}}}; storage.synchronize(SynchronizationPackage{{import}, {type}, {sourceId2}}); @@ -4887,8 +4887,8 @@ TEST_F(ProjectStorage, FetchExportedTypeWithoutVersionFirstForImportedType) sourceId2, {Storage::Synchronization::ExportedType{qtQuickModuleId, "Item", - Storage::Synchronization::Version{}}}}; - Storage::Synchronization::Import import{qmlModuleId, Storage::Synchronization::Version{}, sourceId2}; + Storage::Version{}}}}; + Storage::Synchronization::Import import{qmlModuleId, Storage::Version{}, sourceId2}; storage.synchronize(SynchronizationPackage{{import}, {type}, {sourceId2}}); @@ -4901,7 +4901,7 @@ TEST_F(ProjectStorage, FetchExportedTypeWithoutVersionFirstForQualifiedImportedT { auto package{createSynchronizationPackageWithVersions()}; storage.synchronize(package); - Storage::Synchronization::Import import{qmlModuleId, Storage::Synchronization::Version{}, sourceId2}; + Storage::Synchronization::Import import{qmlModuleId, Storage::Version{}, sourceId2}; Storage::Synchronization::Type type{ "Item", Storage::Synchronization::QualifiedImportedType{"BuiltInObj", import}, @@ -4910,7 +4910,7 @@ TEST_F(ProjectStorage, FetchExportedTypeWithoutVersionFirstForQualifiedImportedT sourceId2, {Storage::Synchronization::ExportedType{qtQuickModuleId, "Item", - Storage::Synchronization::Version{}}}}; + Storage::Version{}}}}; storage.synchronize(SynchronizationPackage{{import}, {type}, {sourceId2}}); @@ -4929,12 +4929,12 @@ TEST_F(ProjectStorage, EnsureThatPropertiesForRemovedTypesAreNotAnymoreRelinked) sourceId1, {Storage::Synchronization::ExportedType{qmlModuleId, "Object", - Storage::Synchronization::Version{}}}, + Storage::Version{}}}, {Storage::Synchronization::PropertyDeclaration{"data", Storage::Synchronization::ImportedType{ "Object"}, Storage::PropertyDeclarationTraits::IsList}}}; - Storage::Synchronization::Import import{qmlModuleId, Storage::Synchronization::Version{}, sourceId1}; + Storage::Synchronization::Import import{qmlModuleId, Storage::Version{}, sourceId1}; storage.synchronize(SynchronizationPackage{{import}, {type}, {sourceId1}}); ASSERT_NO_THROW(storage.synchronize(SynchronizationPackage{{sourceId1}})); @@ -4969,7 +4969,7 @@ TEST_F(ProjectStorage, MinimalUpdates) sourceId1, {Storage::Synchronization::ExportedType{qtQuickModuleId, "Item", - Storage::Synchronization::Version{2, 0}}, + Storage::Version{2, 0}}, Storage::Synchronization::ExportedType{qtQuickNativeModuleId, "QQuickItem"}}, {}, {}, @@ -5319,7 +5319,7 @@ TEST_F(ProjectStorage, ModuleExportedImportWithDifferentVersions) package.imports.back().version.major.value = 2; package.types[2].exportedTypes.front().version.major.value = 2; package.moduleExportedImports.back().isAutoVersion = Storage::Synchronization::IsAutoVersion::No; - package.moduleExportedImports.back().version = Storage::Synchronization::Version{1}; + package.moduleExportedImports.back().version = Storage::Version{1}; storage.synchronize(std::move(package)); @@ -5356,7 +5356,7 @@ TEST_F(ProjectStorage, ModuleExportedImportWithIndirectDifferentVersions) package.types[0].exportedTypes.front().version.major.value = 2; package.types[2].exportedTypes.front().version.major.value = 2; package.moduleExportedImports[0].isAutoVersion = Storage::Synchronization::IsAutoVersion::No; - package.moduleExportedImports[0].version = Storage::Synchronization::Version{1}; + package.moduleExportedImports[0].version = Storage::Version{1}; storage.synchronize(std::move(package)); @@ -5389,14 +5389,14 @@ TEST_F(ProjectStorage, ModuleExportedImportPreventCollisionIfModuleIsIndirectlyR { ModuleId qtQuick4DModuleId{storage.moduleId("QtQuick4D")}; auto package{createModuleExportedImportSynchronizationPackage()}; - package.imports.emplace_back(qtQuickModuleId, Storage::Synchronization::Version{1}, sourceId5); + package.imports.emplace_back(qtQuickModuleId, Storage::Version{1}, sourceId5); package.moduleExportedImports.emplace_back(qtQuick4DModuleId, qtQuickModuleId, - Storage::Synchronization::Version{}, + Storage::Version{}, Storage::Synchronization::IsAutoVersion::Yes); package.moduleExportedImports.emplace_back(qtQuick4DModuleId, qmlModuleId, - Storage::Synchronization::Version{}, + Storage::Version{}, Storage::Synchronization::IsAutoVersion::Yes); package.updatedModuleIds.push_back(qtQuick4DModuleId); package.types.push_back(Storage::Synchronization::Type{ @@ -5407,8 +5407,8 @@ TEST_F(ProjectStorage, ModuleExportedImportPreventCollisionIfModuleIsIndirectlyR sourceId5, {Storage::Synchronization::ExportedType{qtQuick4DModuleId, "Item4D", - Storage::Synchronization::Version{1, 0}}}}); - package.imports.emplace_back(qtQuick4DModuleId, Storage::Synchronization::Version{1}, sourceId4); + Storage::Version{1, 0}}}}); + package.imports.emplace_back(qtQuick4DModuleId, Storage::Version{1}, sourceId4); storage.synchronize(std::move(package)); @@ -5448,15 +5448,15 @@ TEST_F(ProjectStorage, DistinguishBetweenImportKinds) ModuleId qml1ModuleId{storage.moduleId("Qml1")}; ModuleId qml11ModuleId{storage.moduleId("Qml11")}; auto package{createSimpleSynchronizationPackage()}; - package.moduleDependencies.emplace_back(qmlModuleId, Storage::Synchronization::Version{}, sourceId1); + package.moduleDependencies.emplace_back(qmlModuleId, Storage::Version{}, sourceId1); package.moduleDependencies.emplace_back(qml1ModuleId, - Storage::Synchronization::Version{1}, + Storage::Version{1}, sourceId1); - package.imports.emplace_back(qml1ModuleId, Storage::Synchronization::Version{}, sourceId1); + package.imports.emplace_back(qml1ModuleId, Storage::Version{}, sourceId1); package.moduleDependencies.emplace_back(qml11ModuleId, - Storage::Synchronization::Version{1, 1}, + Storage::Version{1, 1}, sourceId1); - package.imports.emplace_back(qml11ModuleId, Storage::Synchronization::Version{1, 1}, sourceId1); + package.imports.emplace_back(qml11ModuleId, Storage::Version{1, 1}, sourceId1); storage.synchronize(std::move(package)); @@ -5481,7 +5481,7 @@ TEST_F(ProjectStorage, ModuleExportedImportDistinguishBetweenDependencyAndImport { auto package{createModuleExportedImportSynchronizationPackage()}; package.moduleDependencies.emplace_back(qtQuick3DModuleId, - Storage::Synchronization::Version{1}, + Storage::Version{1}, sourceId4); storage.synchronize(std::move(package)); @@ -5517,7 +5517,7 @@ TEST_F(ProjectStorage, ModuleExportedImportWithQualifiedImportedType) package.types.back().prototype = Storage::Synchronization::QualifiedImportedType{ "Object", Storage::Synchronization::Import{qtQuick3DModuleId, - Storage::Synchronization::Version{1}, + Storage::Version{1}, sourceId4}}; storage.synchronize(std::move(package)); @@ -5659,7 +5659,7 @@ TEST_F(ProjectStorage, SynchronizeTypesChangeIndirectAliasDeclarationTypeName) storage.synchronize(package); package.types[2].propertyDeclarations[1].typeName = Storage::Synchronization::ImportedType{ "Obj2"}; - importsSourceId3.emplace_back(pathToModuleId, Storage::Synchronization::Version{}, sourceId3); + importsSourceId3.emplace_back(pathToModuleId, Storage::Version{}, sourceId3); storage.synchronize(SynchronizationPackage{ importsSourceId3, {package.types[2]}, {sourceId3}, moduleDependenciesSourceId3, {sourceId3}}); @@ -5685,7 +5685,7 @@ TEST_F(ProjectStorage, SynchronizeTypesChangeIndirectAliasDeclarationTailsTypeNa storage.synchronize(package); package.types[4].propertyDeclarations[1].typeName = Storage::Synchronization::ImportedType{ "Obj2"}; - importsSourceId5.emplace_back(pathToModuleId, Storage::Synchronization::Version{}, sourceId5); + importsSourceId5.emplace_back(pathToModuleId, Storage::Version{}, sourceId5); storage.synchronize(SynchronizationPackage{ importsSourceId5, {package.types[4]}, {sourceId5}, moduleDependenciesSourceId5, {sourceId5}}); @@ -5947,7 +5947,7 @@ TEST_F(ProjectStorage, GetTypeId) auto package{createSynchronizationPackageWithVersions()}; storage.synchronize(package); - auto typeId = storage.typeId(qmlModuleId, "Object", Storage::Synchronization::Version{}); + auto typeId = storage.typeId(qmlModuleId, "Object", Storage::Version{}); ASSERT_THAT(typeId, fetchTypeId(sourceId1, "QObject4")); } @@ -5957,7 +5957,7 @@ TEST_F(ProjectStorage, GetNoTypeIdForNonExistingTypeName) auto package{createSynchronizationPackageWithVersions()}; storage.synchronize(package); - auto typeId = storage.typeId(qmlModuleId, "Object2", Storage::Synchronization::Version{}); + auto typeId = storage.typeId(qmlModuleId, "Object2", Storage::Version{}); ASSERT_FALSE(typeId); } @@ -5967,7 +5967,7 @@ TEST_F(ProjectStorage, GetNoTypeIdForInvalidModuleId) auto package{createSynchronizationPackageWithVersions()}; storage.synchronize(package); - auto typeId = storage.typeId(ModuleId{}, "Object", Storage::Synchronization::Version{}); + auto typeId = storage.typeId(ModuleId{}, "Object", Storage::Version{}); ASSERT_FALSE(typeId); } @@ -5977,7 +5977,7 @@ TEST_F(ProjectStorage, GetNoTypeIdForWrongModuleId) auto package{createSynchronizationPackageWithVersions()}; storage.synchronize(package); - auto typeId = storage.typeId(qtQuick3DModuleId, "Object", Storage::Synchronization::Version{}); + auto typeId = storage.typeId(qtQuick3DModuleId, "Object", Storage::Version{}); ASSERT_FALSE(typeId); } @@ -5987,7 +5987,7 @@ TEST_F(ProjectStorage, GetTypeIdWithMajorVersion) auto package{createSynchronizationPackageWithVersions()}; storage.synchronize(package); - auto typeId = storage.typeId(qmlModuleId, "Object", Storage::Synchronization::Version{2}); + auto typeId = storage.typeId(qmlModuleId, "Object", Storage::Version{2}); ASSERT_THAT(typeId, fetchTypeId(sourceId1, "QObject3")); } @@ -5997,7 +5997,7 @@ TEST_F(ProjectStorage, GetNoTypeIdWithMajorVersionForNonExistingTypeName) auto package{createSynchronizationPackageWithVersions()}; storage.synchronize(package); - auto typeId = storage.typeId(qmlModuleId, "Object2", Storage::Synchronization::Version{2}); + auto typeId = storage.typeId(qmlModuleId, "Object2", Storage::Version{2}); ASSERT_FALSE(typeId); } @@ -6007,7 +6007,7 @@ TEST_F(ProjectStorage, GetNoTypeIdWithMajorVersionForInvalidModuleId) auto package{createSynchronizationPackageWithVersions()}; storage.synchronize(package); - auto typeId = storage.typeId(ModuleId{}, "Object", Storage::Synchronization::Version{2}); + auto typeId = storage.typeId(ModuleId{}, "Object", Storage::Version{2}); ASSERT_FALSE(typeId); } @@ -6017,7 +6017,7 @@ TEST_F(ProjectStorage, GetNoTypeIdWithMajorVersionForWrongModuleId) auto package{createSynchronizationPackageWithVersions()}; storage.synchronize(package); - auto typeId = storage.typeId(qtQuick3DModuleId, "Object", Storage::Synchronization::Version{2}); + auto typeId = storage.typeId(qtQuick3DModuleId, "Object", Storage::Version{2}); ASSERT_FALSE(typeId); } @@ -6027,7 +6027,7 @@ TEST_F(ProjectStorage, GetNoTypeIdWithMajorVersionForWrongVersion) auto package{createSynchronizationPackageWithVersions()}; storage.synchronize(package); - auto typeId = storage.typeId(qmlModuleId, "Object", Storage::Synchronization::Version{4}); + auto typeId = storage.typeId(qmlModuleId, "Object", Storage::Version{4}); ASSERT_FALSE(typeId); } @@ -6037,7 +6037,7 @@ TEST_F(ProjectStorage, GetTypeIdWithCompleteVersion) auto package{createSynchronizationPackageWithVersions()}; storage.synchronize(package); - auto typeId = storage.typeId(qmlModuleId, "Object", Storage::Synchronization::Version{2, 0}); + auto typeId = storage.typeId(qmlModuleId, "Object", Storage::Version{2, 0}); ASSERT_THAT(typeId, fetchTypeId(sourceId1, "QObject2")); } @@ -6047,7 +6047,7 @@ TEST_F(ProjectStorage, GetNoTypeIdWithCompleteVersionWithHigherMinorVersion) auto package{createSynchronizationPackageWithVersions()}; storage.synchronize(package); - auto typeId = storage.typeId(qmlModuleId, "Object", Storage::Synchronization::Version{2, 12}); + auto typeId = storage.typeId(qmlModuleId, "Object", Storage::Version{2, 12}); ASSERT_THAT(typeId, fetchTypeId(sourceId1, "QObject3")); } @@ -6057,7 +6057,7 @@ TEST_F(ProjectStorage, GetNoTypeIdWithCompleteVersionForNonExistingTypeName) auto package{createSynchronizationPackageWithVersions()}; storage.synchronize(package); - auto typeId = storage.typeId(qmlModuleId, "Object2", Storage::Synchronization::Version{2, 0}); + auto typeId = storage.typeId(qmlModuleId, "Object2", Storage::Version{2, 0}); ASSERT_FALSE(typeId); } @@ -6067,7 +6067,7 @@ TEST_F(ProjectStorage, GetNoTypeIdWithCompleteVersionForInvalidModuleId) auto package{createSynchronizationPackageWithVersions()}; storage.synchronize(package); - auto typeId = storage.typeId(ModuleId{}, "Object", Storage::Synchronization::Version{2, 0}); + auto typeId = storage.typeId(ModuleId{}, "Object", Storage::Version{2, 0}); ASSERT_FALSE(typeId); } @@ -6077,7 +6077,7 @@ TEST_F(ProjectStorage, GetNoTypeIdWithCompleteVersionForWrongModuleId) auto package{createSynchronizationPackageWithVersions()}; storage.synchronize(package); - auto typeId = storage.typeId(qtQuick3DModuleId, "Object", Storage::Synchronization::Version{2, 0}); + auto typeId = storage.typeId(qtQuick3DModuleId, "Object", Storage::Version{2, 0}); ASSERT_FALSE(typeId); } @@ -6087,7 +6087,7 @@ TEST_F(ProjectStorage, GetNoTypeIdWithCompleteVersionForWrongMajorVersion) auto package{createSynchronizationPackageWithVersions()}; storage.synchronize(package); - auto typeId = storage.typeId(qmlModuleId, "Object", Storage::Synchronization::Version{4, 0}); + auto typeId = storage.typeId(qmlModuleId, "Object", Storage::Version{4, 0}); ASSERT_FALSE(typeId); } diff --git a/tests/unit/unittest/projectstorageupdater-test.cpp b/tests/unit/unittest/projectstorageupdater-test.cpp index 05b3166bbc5..f2f52d401f9 100644 --- a/tests/unit/unittest/projectstorageupdater-test.cpp +++ b/tests/unit/unittest/projectstorageupdater-test.cpp @@ -31,7 +31,7 @@ using QmlDesigner::Storage::Synchronization::IsAutoVersion; using QmlDesigner::Storage::Synchronization::ModuleExportedImport; using QmlDesigner::Storage::Synchronization::ProjectData; using QmlDesigner::Storage::Synchronization::SynchronizationPackage; -using QmlDesigner::Storage::Synchronization::Version; +using QmlDesigner::Storage::Version; MATCHER_P5(IsStorageType, typeName, @@ -71,12 +71,12 @@ MATCHER_P4(IsExportedType, minorVersion, std::string(negation ? "isn't " : "is ") + PrintToString(Storage::Synchronization::ExportedType{ - moduleId, name, Storage::Synchronization::Version{majorVersion, minorVersion}})) + moduleId, name, Storage::Version{majorVersion, minorVersion}})) { const Storage::Synchronization::ExportedType &type = arg; return type.moduleId == moduleId && type.name == name - && type.version == Storage::Synchronization::Version{majorVersion, minorVersion}; + && type.version == Storage::Version{majorVersion, minorVersion}; } MATCHER_P3(IsFileStatus, @@ -328,19 +328,19 @@ protected: Storage::Synchronization::Type secondType; Storage::Synchronization::Type thirdType; Storage::Synchronization::Import import1{qmlModuleId, - Storage::Synchronization::Version{2, 3}, + Storage::Version{2, 3}, qmlDocumentSourceId1}; Storage::Synchronization::Import import2{qmlModuleId, - Storage::Synchronization::Version{}, + Storage::Version{}, qmlDocumentSourceId2}; Storage::Synchronization::Import import3{qmlModuleId, - Storage::Synchronization::Version{2}, + Storage::Version{2}, qmlDocumentSourceId3}; Storage::Synchronization::Import import4{qmlModuleId, - Storage::Synchronization::Version{2, 3}, + Storage::Version{2, 3}, qmltypesPathSourceId}; Storage::Synchronization::Import import5{qmlModuleId, - Storage::Synchronization::Version{2, 3}, + Storage::Version{2, 3}, qmltypes2PathSourceId}; QString qmldirContent{"module Example\ntypeinfo example.qmltypes\n"}; QString qmltypes1{"Module {\ndependencies: [module1]}"}; @@ -456,7 +456,7 @@ TEST_F(ProjectStorageUpdater, SynchronizeIsEmptyForNoChange) TEST_F(ProjectStorageUpdater, SynchronizeQmlTypes) { Storage::Synchronization::Import import{qmlModuleId, - Storage::Synchronization::Version{2, 3}, + Storage::Version{2, 3}, qmltypesPathSourceId}; QString qmltypes{"Module {\ndependencies: []}"}; setQmlFileNames(u"/path", {}); @@ -493,7 +493,7 @@ TEST_F(ProjectStorageUpdater, SynchronizeQmlTypes) TEST_F(ProjectStorageUpdater, SynchronizeQmlTypesThrowsIfQmltpesDoesNotExists) { Storage::Synchronization::Import import{qmlModuleId, - Storage::Synchronization::Version{2, 3}, + Storage::Version{2, 3}, qmltypesPathSourceId}; setFilesDontExists({qmltypesPathSourceId}); @@ -1329,16 +1329,16 @@ TEST_F(ProjectStorageUpdater, SynchronizeQmldirDependencies) synchronize( AllOf(Field(&SynchronizationPackage::moduleDependencies, UnorderedElementsAre(Import{qmlCppNativeModuleId, - Storage::Synchronization::Version{}, + Storage::Version{}, qmltypesPathSourceId}, Import{builtinCppNativeModuleId, - Storage::Synchronization::Version{}, + Storage::Version{}, qmltypesPathSourceId}, Import{qmlCppNativeModuleId, - Storage::Synchronization::Version{}, + Storage::Version{}, qmltypes2PathSourceId}, Import{builtinCppNativeModuleId, - Storage::Synchronization::Version{}, + Storage::Version{}, qmltypes2PathSourceId})), Field(&SynchronizationPackage::updatedModuleDependencySourceIds, UnorderedElementsAre(qmltypesPathSourceId, qmltypes2PathSourceId))))); @@ -1361,16 +1361,16 @@ TEST_F(ProjectStorageUpdater, SynchronizeQmldirDependenciesWithDoubleEntries) synchronize( AllOf(Field(&SynchronizationPackage::moduleDependencies, UnorderedElementsAre(Import{qmlCppNativeModuleId, - Storage::Synchronization::Version{}, + Storage::Version{}, qmltypesPathSourceId}, Import{builtinCppNativeModuleId, - Storage::Synchronization::Version{}, + Storage::Version{}, qmltypesPathSourceId}, Import{qmlCppNativeModuleId, - Storage::Synchronization::Version{}, + Storage::Version{}, qmltypes2PathSourceId}, Import{builtinCppNativeModuleId, - Storage::Synchronization::Version{}, + Storage::Version{}, qmltypes2PathSourceId})), Field(&SynchronizationPackage::updatedModuleDependencySourceIds, UnorderedElementsAre(qmltypesPathSourceId, qmltypes2PathSourceId))))); @@ -1393,16 +1393,16 @@ TEST_F(ProjectStorageUpdater, SynchronizeQmldirDependenciesWithCollidingImports) synchronize( AllOf(Field(&SynchronizationPackage::moduleDependencies, UnorderedElementsAre(Import{qmlCppNativeModuleId, - Storage::Synchronization::Version{}, + Storage::Version{}, qmltypesPathSourceId}, Import{builtinCppNativeModuleId, - Storage::Synchronization::Version{}, + Storage::Version{}, qmltypesPathSourceId}, Import{qmlCppNativeModuleId, - Storage::Synchronization::Version{}, + Storage::Version{}, qmltypes2PathSourceId}, Import{builtinCppNativeModuleId, - Storage::Synchronization::Version{}, + Storage::Version{}, qmltypes2PathSourceId})), Field(&SynchronizationPackage::updatedModuleDependencySourceIds, UnorderedElementsAre(qmltypesPathSourceId, qmltypes2PathSourceId))))); @@ -1442,27 +1442,27 @@ TEST_F(ProjectStorageUpdater, SynchronizeQmldirImports) Field(&SynchronizationPackage::moduleExportedImports, UnorderedElementsAre(ModuleExportedImport{exampleModuleId, qmlModuleId, - Storage::Synchronization::Version{}, + Storage::Version{}, IsAutoVersion::Yes}, ModuleExportedImport{exampleCppNativeModuleId, qmlCppNativeModuleId, - Storage::Synchronization::Version{}, + Storage::Version{}, IsAutoVersion::No}, ModuleExportedImport{exampleModuleId, builtinModuleId, - Storage::Synchronization::Version{2, 1}, + Storage::Version{2, 1}, IsAutoVersion::No}, ModuleExportedImport{exampleCppNativeModuleId, builtinCppNativeModuleId, - Storage::Synchronization::Version{}, + Storage::Version{}, IsAutoVersion::No}, ModuleExportedImport{exampleModuleId, quickModuleId, - Storage::Synchronization::Version{}, + Storage::Version{}, IsAutoVersion::No}, ModuleExportedImport{exampleCppNativeModuleId, quickCppNativeModuleId, - Storage::Synchronization::Version{}, + Storage::Version{}, IsAutoVersion::No})), Field(&SynchronizationPackage::updatedModuleIds, ElementsAre(exampleModuleId))))); @@ -1499,27 +1499,27 @@ TEST_F(ProjectStorageUpdater, SynchronizeQmldirImportsWithDoubleEntries) Field(&SynchronizationPackage::moduleExportedImports, UnorderedElementsAre(ModuleExportedImport{exampleModuleId, qmlModuleId, - Storage::Synchronization::Version{}, + Storage::Version{}, IsAutoVersion::Yes}, ModuleExportedImport{exampleCppNativeModuleId, qmlCppNativeModuleId, - Storage::Synchronization::Version{}, + Storage::Version{}, IsAutoVersion::No}, ModuleExportedImport{exampleModuleId, builtinModuleId, - Storage::Synchronization::Version{2, 1}, + Storage::Version{2, 1}, IsAutoVersion::No}, ModuleExportedImport{exampleCppNativeModuleId, builtinCppNativeModuleId, - Storage::Synchronization::Version{}, + Storage::Version{}, IsAutoVersion::No}, ModuleExportedImport{exampleModuleId, quickModuleId, - Storage::Synchronization::Version{}, + Storage::Version{}, IsAutoVersion::No}, ModuleExportedImport{exampleCppNativeModuleId, quickCppNativeModuleId, - Storage::Synchronization::Version{}, + Storage::Version{}, IsAutoVersion::No})), Field(&SynchronizationPackage::updatedModuleIds, ElementsAre(exampleModuleId))))); @@ -1541,27 +1541,27 @@ TEST_F(ProjectStorageUpdater, SynchronizeQmldirOptionalImports) Field(&SynchronizationPackage::moduleExportedImports, UnorderedElementsAre(ModuleExportedImport{exampleModuleId, qmlModuleId, - Storage::Synchronization::Version{}, + Storage::Version{}, IsAutoVersion::Yes}, ModuleExportedImport{exampleCppNativeModuleId, qmlCppNativeModuleId, - Storage::Synchronization::Version{}, + Storage::Version{}, IsAutoVersion::No}, ModuleExportedImport{exampleModuleId, builtinModuleId, - Storage::Synchronization::Version{2, 1}, + Storage::Version{2, 1}, IsAutoVersion::No}, ModuleExportedImport{exampleCppNativeModuleId, builtinCppNativeModuleId, - Storage::Synchronization::Version{}, + Storage::Version{}, IsAutoVersion::No}, ModuleExportedImport{exampleModuleId, quickModuleId, - Storage::Synchronization::Version{}, + Storage::Version{}, IsAutoVersion::No}, ModuleExportedImport{exampleCppNativeModuleId, quickCppNativeModuleId, - Storage::Synchronization::Version{}, + Storage::Version{}, IsAutoVersion::No})), Field(&SynchronizationPackage::updatedModuleIds, ElementsAre(exampleModuleId))))); diff --git a/tests/unit/unittest/qmldocumentparser-test.cpp b/tests/unit/unittest/qmldocumentparser-test.cpp index 7ed83ef891e..a9103d06ea4 100644 --- a/tests/unit/unittest/qmldocumentparser-test.cpp +++ b/tests/unit/unittest/qmldocumentparser-test.cpp @@ -166,10 +166,11 @@ TEST_F(QmlDocumentParser, QualifiedPrototype) auto type = parser.parse(text, imports, qmlFileSourceId, directoryPath); ASSERT_THAT(type, - HasPrototype(Storage::QualifiedImportedType("Item", - Storage::Import{exampleModuleId, - Storage::Version{2, 1}, - qmlFileSourceId}))); + HasPrototype( + Storage::QualifiedImportedType("Item", + Storage::Import{exampleModuleId, + QmlDesigner::Storage::Version{2, 1}, + qmlFileSourceId}))); } TEST_F(QmlDocumentParser, Properties) @@ -198,7 +199,7 @@ TEST_F(QmlDocumentParser, QualifiedProperties) "foo", Storage::QualifiedImportedType("Foo", Storage::Import{exampleModuleId, - Storage::Version{2, 1}, + QmlDesigner::Storage::Version{2, 1}, qmlFileSourceId}), QmlDesigner::Storage::PropertyDeclarationTraits::None))); } @@ -233,7 +234,7 @@ TEST_F(QmlDocumentParser, QualifiedEnumerationInProperties) "foo", Storage::QualifiedImportedType("Enumeration.Foo", Storage::Import{exampleModuleId, - Storage::Version{2, 1}, + QmlDesigner::Storage::Version{2, 1}, qmlFileSourceId}), QmlDesigner::Storage::PropertyDeclarationTraits::None))); } @@ -251,12 +252,13 @@ TEST_F(QmlDocumentParser, Imports) qmlFileSourceId, directoryPath); - ASSERT_THAT(imports, - UnorderedElementsAre( - Storage::Import{directoryModuleId, Storage::Version{}, qmlFileSourceId}, - Storage::Import{fooDirectoryModuleId, Storage::Version{}, qmlFileSourceId}, - Storage::Import{qmlModuleId, Storage::Version{}, qmlFileSourceId}, - Storage::Import{qtQuickModuleId, Storage::Version{}, qmlFileSourceId})); + ASSERT_THAT( + imports, + UnorderedElementsAre( + Storage::Import{directoryModuleId, QmlDesigner::Storage::Version{}, qmlFileSourceId}, + Storage::Import{fooDirectoryModuleId, QmlDesigner::Storage::Version{}, qmlFileSourceId}, + Storage::Import{qmlModuleId, QmlDesigner::Storage::Version{}, qmlFileSourceId}, + Storage::Import{qtQuickModuleId, QmlDesigner::Storage::Version{}, qmlFileSourceId})); } TEST_F(QmlDocumentParser, ImportsWithVersion) @@ -272,12 +274,13 @@ TEST_F(QmlDocumentParser, ImportsWithVersion) qmlFileSourceId, directoryPath); - ASSERT_THAT(imports, - UnorderedElementsAre( - Storage::Import{directoryModuleId, Storage::Version{}, qmlFileSourceId}, - Storage::Import{fooDirectoryModuleId, Storage::Version{}, qmlFileSourceId}, - Storage::Import{qmlModuleId, Storage::Version{}, qmlFileSourceId}, - Storage::Import{qtQuickModuleId, Storage::Version{2, 1}, qmlFileSourceId})); + ASSERT_THAT( + imports, + UnorderedElementsAre( + Storage::Import{directoryModuleId, QmlDesigner::Storage::Version{}, qmlFileSourceId}, + Storage::Import{fooDirectoryModuleId, QmlDesigner::Storage::Version{}, qmlFileSourceId}, + Storage::Import{qmlModuleId, QmlDesigner::Storage::Version{}, qmlFileSourceId}, + Storage::Import{qtQuickModuleId, QmlDesigner::Storage::Version{2, 1}, qmlFileSourceId})); } TEST_F(QmlDocumentParser, ImportsWithExplictDirectory) @@ -293,11 +296,11 @@ TEST_F(QmlDocumentParser, ImportsWithExplictDirectory) qmlFileSourceId, directoryPath); - ASSERT_THAT( - imports, - UnorderedElementsAre(Storage::Import{directoryModuleId, Storage::Version{}, qmlFileSourceId}, - Storage::Import{qmlModuleId, Storage::Version{}, qmlFileSourceId}, - Storage::Import{qtQuickModuleId, Storage::Version{}, qmlFileSourceId})); + ASSERT_THAT(imports, + UnorderedElementsAre( + Storage::Import{directoryModuleId, QmlDesigner::Storage::Version{}, qmlFileSourceId}, + Storage::Import{qmlModuleId, QmlDesigner::Storage::Version{}, qmlFileSourceId}, + Storage::Import{qtQuickModuleId, QmlDesigner::Storage::Version{}, qmlFileSourceId})); } TEST_F(QmlDocumentParser, Functions) @@ -373,13 +376,14 @@ TEST_F(QmlDocumentParser, DISABLED_DuplicateImportsAreRemoved) qmlFileSourceId, directoryPath); - ASSERT_THAT(imports, - UnorderedElementsAre( - Storage::Import{directoryModuleId, Storage::Version{}, qmlFileSourceId}, - Storage::Import{fooDirectoryModuleId, Storage::Version{}, qmlFileSourceId}, - Storage::Import{qmlModuleId, Storage::Version{1, 0}, qmlFileSourceId}, - Storage::Import{qtQmlModuleId, Storage::Version{6, 0}, qmlFileSourceId}, - Storage::Import{qtQuickModuleId, Storage::Version{}, qmlFileSourceId})); + ASSERT_THAT( + imports, + UnorderedElementsAre( + Storage::Import{directoryModuleId, QmlDesigner::Storage::Version{}, qmlFileSourceId}, + Storage::Import{fooDirectoryModuleId, QmlDesigner::Storage::Version{}, qmlFileSourceId}, + Storage::Import{qmlModuleId, QmlDesigner::Storage::Version{1, 0}, qmlFileSourceId}, + Storage::Import{qtQmlModuleId, QmlDesigner::Storage::Version{6, 0}, qmlFileSourceId}, + Storage::Import{qtQuickModuleId, QmlDesigner::Storage::Version{}, qmlFileSourceId})); } TEST_F(QmlDocumentParser, AliasItemProperties) @@ -510,7 +514,7 @@ TEST_F(QmlDocumentParser, QualifiedListProperty) "foos", Storage::QualifiedImportedType{"Foo", Storage::Import{exampleModuleId, - Storage::Version{2, 1}, + QmlDesigner::Storage::Version{2, 1}, qmlFileSourceId}}, QmlDesigner::Storage::PropertyDeclarationTraits::IsList))); } diff --git a/tests/unit/unittest/qmltypesparser-test.cpp b/tests/unit/unittest/qmltypesparser-test.cpp index cc4fe6def47..3fbbaa6b8cc 100644 --- a/tests/unit/unittest/qmltypesparser-test.cpp +++ b/tests/unit/unittest/qmltypesparser-test.cpp @@ -171,14 +171,22 @@ TEST_F(QmlTypesParser, Imports) parser.parse(source, imports, types, projectData); - ASSERT_THAT( - imports, - UnorderedElementsAre( - IsImport(storage.moduleId("QML-cppnative"), Storage::Version{}, qmltypesFileSourceId), - IsImport(storage.moduleId("QtQml-cppnative"), Storage::Version{}, qmltypesFileSourceId), - IsImport(storage.moduleId("QtQuick-cppnative"), Storage::Version{}, qmltypesFileSourceId), - IsImport(storage.moduleId("QtQuick.Window-cppnative"), Storage::Version{}, qmltypesFileSourceId), - IsImport(storage.moduleId("QtFoo-cppnative"), Storage::Version{}, qmltypesFileSourceId))); + ASSERT_THAT(imports, + UnorderedElementsAre(IsImport(storage.moduleId("QML-cppnative"), + QmlDesigner::Storage::Version{}, + qmltypesFileSourceId), + IsImport(storage.moduleId("QtQml-cppnative"), + QmlDesigner::Storage::Version{}, + qmltypesFileSourceId), + IsImport(storage.moduleId("QtQuick-cppnative"), + QmlDesigner::Storage::Version{}, + qmltypesFileSourceId), + IsImport(storage.moduleId("QtQuick.Window-cppnative"), + QmlDesigner::Storage::Version{}, + qmltypesFileSourceId), + IsImport(storage.moduleId("QtFoo-cppnative"), + QmlDesigner::Storage::Version{}, + qmltypesFileSourceId))); } TEST_F(QmlTypesParser, Types) @@ -261,13 +269,14 @@ TEST_F(QmlTypesParser, ExportedTypes) parser.parse(source, imports, types, projectData); - ASSERT_THAT(types, - ElementsAre( - Field(&Storage::Type::exportedTypes, - UnorderedElementsAre( - IsExportedType(qmlModuleId, "QtObject", Storage::Version{1, 0}), - IsExportedType(qtQmlModuleId, "QtObject", Storage::Version{2, 1}), - IsExportedType(qtQmlNativeModuleId, "QObject", Storage::Version{}))))); + ASSERT_THAT( + types, + ElementsAre(Field( + &Storage::Type::exportedTypes, + UnorderedElementsAre( + IsExportedType(qmlModuleId, "QtObject", QmlDesigner::Storage::Version{1, 0}), + IsExportedType(qtQmlModuleId, "QtObject", QmlDesigner::Storage::Version{2, 1}), + IsExportedType(qtQmlNativeModuleId, "QObject", QmlDesigner::Storage::Version{}))))); } TEST_F(QmlTypesParser, Properties) @@ -567,30 +576,28 @@ TEST_F(QmlTypesParser, EnumerationIsExportedAsType) parser.parse(source, imports, types, projectData); - ASSERT_THAT(types, - UnorderedElementsAre( - AllOf(IsType("QObject::NamedColorSpace", - Storage::ImportedType{}, - Storage::ImportedType{}, - QmlDesigner::Storage::TypeTraits::Value - | QmlDesigner::Storage::TypeTraits::IsEnum, - qmltypesFileSourceId), - Field(&Storage::Type::exportedTypes, - UnorderedElementsAre(IsExportedType(qtQmlNativeModuleId, - "QObject::NamedColorSpace", - Storage::Version{})))), - AllOf(IsType("QObject::VerticalLayoutDirection", - Storage::ImportedType{}, - Storage::ImportedType{}, - QmlDesigner::Storage::TypeTraits::Value - | QmlDesigner::Storage::TypeTraits::IsEnum, - qmltypesFileSourceId), - Field(&Storage::Type::exportedTypes, - UnorderedElementsAre( - IsExportedType(qtQmlNativeModuleId, - "QObject::VerticalLayoutDirection", - Storage::Version{})))), - _)); + ASSERT_THAT( + types, + UnorderedElementsAre( + AllOf(IsType("QObject::NamedColorSpace", + Storage::ImportedType{}, + Storage::ImportedType{}, + QmlDesigner::Storage::TypeTraits::Value | QmlDesigner::Storage::TypeTraits::IsEnum, + qmltypesFileSourceId), + Field(&Storage::Type::exportedTypes, + UnorderedElementsAre(IsExportedType(qtQmlNativeModuleId, + "QObject::NamedColorSpace", + QmlDesigner::Storage::Version{})))), + AllOf(IsType("QObject::VerticalLayoutDirection", + Storage::ImportedType{}, + Storage::ImportedType{}, + QmlDesigner::Storage::TypeTraits::Value | QmlDesigner::Storage::TypeTraits::IsEnum, + qmltypesFileSourceId), + Field(&Storage::Type::exportedTypes, + UnorderedElementsAre(IsExportedType(qtQmlNativeModuleId, + "QObject::VerticalLayoutDirection", + QmlDesigner::Storage::Version{})))), + _)); } TEST_F(QmlTypesParser, EnumerationIsExportedAsTypeWithAlias) @@ -624,10 +631,10 @@ TEST_F(QmlTypesParser, EnumerationIsExportedAsTypeWithAlias) Field(&Storage::Type::exportedTypes, UnorderedElementsAre(IsExportedType(qtQmlNativeModuleId, "QObject::NamedColorSpace", - Storage::Version{}), + QmlDesigner::Storage::Version{}), IsExportedType(qtQmlNativeModuleId, "QObject::NamedColorSpaces", - Storage::Version{})))), + QmlDesigner::Storage::Version{})))), _)); } @@ -671,10 +678,10 @@ TEST_F(QmlTypesParser, EnumerationIsExportedAsTypeWithAliasToo) Field(&Storage::Type::exportedTypes, UnorderedElementsAre(IsExportedType(qtQmlNativeModuleId, "QObject::NamedColorSpace", - Storage::Version{}), + QmlDesigner::Storage::Version{}), IsExportedType(qtQmlNativeModuleId, "QObject::NamedColorSpaces", - Storage::Version{})))), + QmlDesigner::Storage::Version{})))), _)); } From 1fd124bc27249cb0d472afef2e02a99e6703995f Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Mon, 8 May 2023 13:50:48 +0300 Subject: [PATCH 132/192] QmlDesigner: Fix unused warning Change-Id: I352346308c1c150a8309b25e9d8f74c6aadaa146 Reviewed-by: Burak Hancerli Reviewed-by: Mahmoud Badri --- .../qmldesigner/components/edit3d/bakelightsdatamodel.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/qmldesigner/components/edit3d/bakelightsdatamodel.cpp b/src/plugins/qmldesigner/components/edit3d/bakelightsdatamodel.cpp index e52d4403770..2ab5ba4b017 100644 --- a/src/plugins/qmldesigner/components/edit3d/bakelightsdatamodel.cpp +++ b/src/plugins/qmldesigner/components/edit3d/bakelightsdatamodel.cpp @@ -30,7 +30,7 @@ BakeLightsDataModel::~BakeLightsDataModel() { } -int BakeLightsDataModel::rowCount(const QModelIndex &parent) const +int BakeLightsDataModel::rowCount(const QModelIndex &) const { return m_dataList.count(); } From 2ee225b0379129d6dca401a6d4fd1673a92e6fd0 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Mon, 8 May 2023 13:06:27 +0200 Subject: [PATCH 133/192] QmlDesigner: fix hardcoded types for Qt 6.5.1 Task-number: QDS-9737 Change-Id: I286d706b506904b4d6f22549d1ec294d16859df0 Reviewed-by: Tim Jenssen --- .../qmldesigner/designercore/model/texttomodelmerger.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp index fe9db55ce63..7db323930f2 100644 --- a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp +++ b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp @@ -285,7 +285,7 @@ bool isComponentType(const QmlDesigner::TypeName &type) { return type == "Component" || type == "Qt.Component" || type == "QtQuick.Component" || type == "QtQml.Component" || type == ".QQmlComponent" || type == "QQmlComponent" - || type == "QML.Component"; + || type == "QML.Component" || type == "QtQml.Base.Component"; } bool isCustomParserType(const QmlDesigner::TypeName &type) @@ -305,7 +305,8 @@ bool isPropertyChangesType(const QmlDesigner::TypeName &type) bool isConnectionsType(const QmlDesigner::TypeName &type) { - return type == "Connections" || type == "QtQuick.Connections" || type == "Qt.Connections" || type == "QtQml.Connections"; + return type == "Connections" || type == "QtQuick.Connections" || type == "Qt.Connections" + || type == "QtQml.Connections" || type == "QtQml.Base.Connections"; } bool propertyIsComponentType(const QmlDesigner::NodeAbstractProperty &property, const QmlDesigner::TypeName &type, QmlDesigner::Model *model) From 5b9950c5d0d4d3b8dd7b0a2866ab9d1ad971083d Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Sat, 6 May 2023 15:08:52 +0200 Subject: [PATCH 134/192] QmlDesigner: Make meta info system mockable Using the projectstorage mechanism we introduce a flag for the test, so we can use ProjectStorageInterface instead of ProjectStorage. It has the overhead of a virtual function interface but for the test that is neglectable. In the test we can now use a project storage mock to emulate the meta info system. Later we could use a pregenerated database file to create a fake project storage. Task-number: QDS-9766 Change-Id: I16efa28bc4b12181941adecc132084157156daec Reviewed-by: Reviewed-by: Tim Jenssen --- src/plugins/qmldesigner/CMakeLists.txt | 3 + .../components/formeditor/dragtool.cpp | 6 - .../choosefrompropertylistdialog.cpp | 2 +- .../include/externaldependenciesinterface.h | 1 + .../designercore/include/itemlibraryinfo.h | 3 +- .../designercore/include/metainfo.h | 8 +- .../qmldesigner/designercore/include/model.h | 12 +- .../designercore/include/modelfwd.h | 42 +++++++ .../designercore/include/nodemetainfo.h | 11 +- .../designercore/include/propertymetainfo.h | 34 ++++-- .../include/qmldesignercorelib_exports.h | 12 ++ .../include/qmldesignercorelib_global.h | 41 ++----- .../designercore/metainfo/itemlibraryinfo.cpp | 3 +- .../designercore/metainfo/metainfo.cpp | 66 +++++------ .../designercore/metainfo/nodemetainfo.cpp | 66 ++++++----- .../qmldesigner/designercore/model/model.cpp | 29 ++++- .../qmldesigner/designercore/model/model_p.h | 8 +- .../designercore/model/modelnode.cpp | 2 +- .../designercore/model/qmlchangeset.cpp | 2 +- .../projectstorage/commontypecache.h | 11 +- .../projectstorage/projectstorage.h | 62 ++++++++-- .../projectstorage/projectstoragefwd.h | 4 +- .../projectstorage/projectstorageinterface.h | 52 ++++++++- .../qmldesignerexternaldependencies.cpp | 5 + .../qmldesignerexternaldependencies.h | 1 + src/plugins/qmldesigner/qmldesignerplugin.cpp | 8 +- .../qmldesigner/coretests/tst_testcore.cpp | 30 ++--- .../qml/qmldesigner/coretests/tst_testcore.h | 3 +- .../designercore/include/itemlibraryinfo.h | 34 ------ .../designercore/include/metainfo.h | 41 ------- .../designercore/include/nodemetainfo.h | 88 -------------- .../designercore/include/propertymetainfo.h | 49 -------- tests/unit/unittest/CMakeLists.txt | 17 +++ .../unit/unittest/externaldependenciesmock.h | 1 + tests/unit/unittest/listmodeleditor-test.cpp | 55 +++++++-- tests/unit/unittest/nodelistproperty-test.cpp | 36 +++++- tests/unit/unittest/projectstoragemock.h | 108 +++++++++++++++++- 37 files changed, 549 insertions(+), 407 deletions(-) create mode 100644 src/plugins/qmldesigner/designercore/include/modelfwd.h create mode 100644 src/plugins/qmldesigner/designercore/include/qmldesignercorelib_exports.h delete mode 100644 tests/unit/mockup/qmldesigner/designercore/include/itemlibraryinfo.h delete mode 100644 tests/unit/mockup/qmldesigner/designercore/include/metainfo.h delete mode 100644 tests/unit/mockup/qmldesigner/designercore/include/nodemetainfo.h delete mode 100644 tests/unit/mockup/qmldesigner/designercore/include/propertymetainfo.h diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index 90155e94406..0ef574afec7 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -226,6 +226,7 @@ extend_qtc_library(QmlDesignerCore metainfo.h metainforeader.h model.h + modelfwd.h modelmerger.h modelnode.h modelnodepositionstorage.h @@ -246,6 +247,7 @@ extend_qtc_library(QmlDesignerCore qmlchangeset.h qmlconnections.h qmldesignercorelib_global.h + qmldesignercorelib_exports.h qmlitemnode.h qmlmodelnodefacade.h qmlobjectnode.h @@ -267,6 +269,7 @@ extend_qtc_library(QmlDesignerCore INCLUDES ${CMAKE_CURRENT_LIST_DIR}/designercore/metainfo SOURCES_PREFIX ${CMAKE_CURRENT_LIST_DIR}/designercore/metainfo + DEFINES SHARE_QML_PATH="${CMAKE_CURRENT_SOURCE_DIR}/../../../share/qtcreator/qmldesigner" SOURCES itemlibraryinfo.cpp metainfo.cpp diff --git a/src/plugins/qmldesigner/components/formeditor/dragtool.cpp b/src/plugins/qmldesigner/components/formeditor/dragtool.cpp index 86951866058..9a51c9f3722 100644 --- a/src/plugins/qmldesigner/components/formeditor/dragtool.cpp +++ b/src/plugins/qmldesigner/components/formeditor/dragtool.cpp @@ -77,8 +77,6 @@ void DragTool::createQmlItemNode(const ItemLibraryEntry &itemLibraryEntry, const QmlItemNode &parentNode, const QPointF &scenePosition) { - MetaInfo metaInfo = MetaInfo::global(); - FormEditorItem *parentItem = scene()->itemForQmlItemNode(parentNode); const QPointF positonInItemSpace = parentItem->qmlItemNode().instanceSceneContentItemTransform().inverted().map(scenePosition); QPointF itemPos = positonInItemSpace; @@ -107,8 +105,6 @@ void DragTool::createQmlItemNodeFromImage(const QString &imagePath, const QPointF &scenePosition) { if (parentNode.isValid()) { - MetaInfo metaInfo = MetaInfo::global(); - FormEditorItem *parentItem = scene()->itemForQmlItemNode(parentNode); QPointF positonInItemSpace = parentItem->qmlItemNode().instanceSceneContentItemTransform().inverted().map(scenePosition); @@ -121,8 +117,6 @@ void DragTool::createQmlItemNodeFromFont(const QString &fontPath, const QPointF &scenePos) { if (parentNode.isValid()) { - MetaInfo metaInfo = MetaInfo::global(); - FormEditorItem *parentItem = scene()->itemForQmlItemNode(parentNode); QPointF positonInItemSpace = parentItem->qmlItemNode().instanceSceneContentItemTransform() .inverted().map(scenePos); diff --git a/src/plugins/qmldesigner/components/navigator/choosefrompropertylistdialog.cpp b/src/plugins/qmldesigner/components/navigator/choosefrompropertylistdialog.cpp index 35078859de0..91c300e87a2 100644 --- a/src/plugins/qmldesigner/components/navigator/choosefrompropertylistdialog.cpp +++ b/src/plugins/qmldesigner/components/navigator/choosefrompropertylistdialog.cpp @@ -90,7 +90,7 @@ ChooseFromPropertyListFilter::ChooseFromPropertyListFilter(const NodeMetaInfo &i } else if (insertInfo.isQtQuick3DParticles3DParticle3D()) { if (parentInfo.isQtQuick3DParticles3DParticleEmitter3D()) propertyList.append("particle"); - } else if (insertInfo.isQuick3DParticleAbstractShape()) { + } else if (insertInfo.isQtQuick3DParticleAbstractShape()) { if (parentInfo.isQtQuick3DParticles3DParticleEmitter3D() || parentInfo.isQtQuick3DParticles3DAttractor3D()) propertyList.append("shape"); diff --git a/src/plugins/qmldesigner/designercore/include/externaldependenciesinterface.h b/src/plugins/qmldesigner/designercore/include/externaldependenciesinterface.h index b0409140fe3..94d90aad187 100644 --- a/src/plugins/qmldesigner/designercore/include/externaldependenciesinterface.h +++ b/src/plugins/qmldesigner/designercore/include/externaldependenciesinterface.h @@ -44,6 +44,7 @@ public: virtual QStringList projectModulePaths() const = 0; virtual bool isQt6Project() const = 0; virtual QString qtQuickVersion() const = 0; + virtual Utils::FilePath resourcePath(const QString &relativePath) const = 0; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/include/itemlibraryinfo.h b/src/plugins/qmldesigner/designercore/include/itemlibraryinfo.h index 0571e64a8ce..034af72410f 100644 --- a/src/plugins/qmldesigner/designercore/include/itemlibraryinfo.h +++ b/src/plugins/qmldesigner/designercore/include/itemlibraryinfo.h @@ -6,9 +6,10 @@ #include "qmldesignercorelib_global.h" #include "propertycontainer.h" +#include +#include #include #include -#include namespace QmlDesigner { diff --git a/src/plugins/qmldesigner/designercore/include/metainfo.h b/src/plugins/qmldesigner/designercore/include/metainfo.h index b5ec3c0aa95..914be96db7b 100644 --- a/src/plugins/qmldesigner/designercore/include/metainfo.h +++ b/src/plugins/qmldesigner/designercore/include/metainfo.h @@ -16,6 +16,7 @@ namespace QmlDesigner { class ModelNode; class AbstractProperty; class ItemLibraryInfo; +class ExternalDependenciesInterface; namespace Internal { class MetaInfoPrivate; @@ -44,15 +45,14 @@ public: ItemLibraryInfo *itemLibraryInfo() const; public: - static MetaInfo global(); - static void clearGlobal(); - - static void setPluginPaths(const QStringList &paths); + static void initializeGlobal(const QStringList &pluginPaths, + const ExternalDependenciesInterface &externalDependencies); static void disableParseItemLibraryDescriptionsUgly(); // ugly hack around broken tests private: bool isGlobal() const; + static MetaInfo global(); private: MetaInfo(); diff --git a/src/plugins/qmldesigner/designercore/include/model.h b/src/plugins/qmldesigner/designercore/include/model.h index 89391dfe9ed..95295fed6a0 100644 --- a/src/plugins/qmldesigner/designercore/include/model.h +++ b/src/plugins/qmldesigner/designercore/include/model.h @@ -57,7 +57,7 @@ class QMLDESIGNERCORE_EXPORT Model : public QObject public: enum ViewNotification { NotifyView, DoNotNotifyView }; - Model(ProjectStorage &projectStorage, + Model(ProjectStorageType &projectStorage, const TypeName &type, int major = 1, int minor = 1, @@ -74,6 +74,14 @@ public: return ModelPointer(new Model(typeName, major, minor, metaInfoProxyModel)); } + static ModelPointer create(ProjectStorageType &projectStorage, + const TypeName &typeName, + int major = 1, + int minor = 1) + { + return ModelPointer(new Model(projectStorage, typeName, major, minor)); + } + QUrl fileUrl() const; void setFileUrl(const QUrl &url); @@ -158,7 +166,7 @@ public: void startDrag(QMimeData *mimeData, const QPixmap &icon); void endDrag(); - NotNullPointer> projectStorage() const; + NotNullPointer projectStorage() const; private: template diff --git a/src/plugins/qmldesigner/designercore/include/modelfwd.h b/src/plugins/qmldesigner/designercore/include/modelfwd.h new file mode 100644 index 00000000000..66712499958 --- /dev/null +++ b/src/plugins/qmldesigner/designercore/include/modelfwd.h @@ -0,0 +1,42 @@ +// 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 "../projectstorage/projectstoragefwd.h" +#include "qmldesignercorelib_exports.h" + +#include + +namespace QmlDesigner { +using PropertyName = QByteArray; +using PropertyNameList = QList; +using TypeName = QByteArray; +using PropertyTypeList = QList; +using IdName = QByteArray; +class Model; +class ModelNode; + +struct ModelDeleter +{ + QMLDESIGNERCORE_EXPORT void operator()(class Model *model); +}; + +using ModelPointer = std::unique_ptr; + +constexpr bool useProjectStorage() +{ +#ifdef QDS_USE_PROJECTSTORAGE + return true; +#else + return false; +#endif +} + +#ifdef QDS_MODEL_USE_PROJECTSTORAGEINTERFACE +using ProjectStorageType = ProjectStorageInterface; +#else +using ProjectStorageType = ProjectStorage; +#endif + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/include/nodemetainfo.h b/src/plugins/qmldesigner/designercore/include/nodemetainfo.h index 028c62ea7ed..d7bc414e77e 100644 --- a/src/plugins/qmldesigner/designercore/include/nodemetainfo.h +++ b/src/plugins/qmldesigner/designercore/include/nodemetainfo.h @@ -6,7 +6,7 @@ #include "propertymetainfo.h" #include "qmldesignercorelib_global.h" -#include +#include #include #include @@ -32,11 +32,11 @@ class QMLDESIGNERCORE_EXPORT NodeMetaInfo public: NodeMetaInfo() = default; NodeMetaInfo(Model *model, const TypeName &typeName, int majorVersion, int minorVersion); - NodeMetaInfo(TypeId typeId, NotNullPointer> projectStorage) + NodeMetaInfo(TypeId typeId, NotNullPointer projectStorage) : m_typeId{typeId} , m_projectStorage{projectStorage} {} - NodeMetaInfo(NotNullPointer> projectStorage) + NodeMetaInfo(NotNullPointer projectStorage) : m_projectStorage{projectStorage} {} ~NodeMetaInfo(); @@ -128,6 +128,7 @@ public: bool isQtQuick3DMaterial() const; bool isQtQuick3DModel() const; bool isQtQuick3DNode() const; + bool isQtQuick3DParticleAbstractShape() const; bool isQtQuick3DParticles3DAffector3D() const; bool isQtQuick3DParticles3DAttractor3D() const; bool isQtQuick3DParticles3DParticle3D() const; @@ -170,8 +171,6 @@ public: bool isQtQuickWindowWindow() const; bool isQtSafeRendererSafePicture() const; bool isQtSafeRendererSafeRendererPicture() const; - bool isQuick3DParticleAbstractShape() const; - bool isQuickStateOperation() const; bool isString() const; bool isSuitableForMouseAreaFill() const; bool isUrl() const; @@ -198,7 +197,7 @@ private: private: TypeId m_typeId; - NotNullPointer> m_projectStorage = {}; + NotNullPointer m_projectStorage = {}; mutable std::optional m_typeData; QSharedPointer m_privateData; }; diff --git a/src/plugins/qmldesigner/designercore/include/propertymetainfo.h b/src/plugins/qmldesigner/designercore/include/propertymetainfo.h index 6cda405f615..a3c928bc36f 100644 --- a/src/plugins/qmldesigner/designercore/include/propertymetainfo.h +++ b/src/plugins/qmldesigner/designercore/include/propertymetainfo.h @@ -25,10 +25,12 @@ public: PropertyMetaInfo() = default; PropertyMetaInfo(QSharedPointer nodeMetaInfoPrivateData, const PropertyName &propertyName); - PropertyMetaInfo(PropertyDeclarationId id, - NotNullPointer> projectStorage) - : m_id{id} - , m_projectStorage{projectStorage} + PropertyMetaInfo([[maybe_unused]] PropertyDeclarationId id, + [[maybe_unused]] NotNullPointer projectStorage) +#ifdef QDS_USE_PROJECTSTORAGE + : m_projectStorage{projectStorage} + , m_id{id} +#endif {} ~PropertyMetaInfo(); @@ -36,11 +38,11 @@ public: bool isValid() const { - if (useProjectStorage()) { - return bool(m_id); - } else { - return bool(m_nodeMetaInfoPrivateData); - } +#ifdef QDS_USE_PROJECTSTORAGE + return bool(m_id); +#else + return bool(m_nodeMetaInfoPrivateData); +#endif } PropertyName name() const; NodeMetaInfo propertyType() const; @@ -53,20 +55,28 @@ public: friend bool operator==(const PropertyMetaInfo &first, const PropertyMetaInfo &second) { +#ifdef QDS_USE_PROJECTSTORAGE + return first.m_id == second.m_id; +#else return first.m_nodeMetaInfoPrivateData == second.m_nodeMetaInfoPrivateData && first.name() == second.name(); +#endif } private: const Storage::Info::PropertyDeclaration &propertyData() const; TypeName propertyTypeName() const; + const NodeMetaInfoPrivate *nodeMetaInfoPrivateData() const; + const PropertyName &propertyName() const; private: + NotNullPointer m_projectStorage; + mutable std::optional m_propertyData; + PropertyDeclarationId m_id; +#ifndef QDS_USE_PROJECTSTORAGE QSharedPointer m_nodeMetaInfoPrivateData; PropertyName m_propertyName; - PropertyDeclarationId m_id; - NotNullPointer> m_projectStorage; - mutable std::optional m_propertyData; +#endif }; using PropertyMetaInfos = std::vector; diff --git a/src/plugins/qmldesigner/designercore/include/qmldesignercorelib_exports.h b/src/plugins/qmldesigner/designercore/include/qmldesignercorelib_exports.h new file mode 100644 index 00000000000..f5e1dbf4788 --- /dev/null +++ b/src/plugins/qmldesigner/designercore/include/qmldesignercorelib_exports.h @@ -0,0 +1,12 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#if defined(QMLDESIGNERCORE_LIBRARY) +#define QMLDESIGNERCORE_EXPORT Q_DECL_EXPORT +#elif defined(QMLDESIGNERCORE_STATIC_LIBRARY) +#define QMLDESIGNERCORE_EXPORT +#else +#define QMLDESIGNERCORE_EXPORT Q_DECL_IMPORT +#endif diff --git a/src/plugins/qmldesigner/designercore/include/qmldesignercorelib_global.h b/src/plugins/qmldesigner/designercore/include/qmldesignercorelib_global.h index 05a1c2e20f6..7871b5df3a6 100644 --- a/src/plugins/qmldesigner/designercore/include/qmldesignercorelib_global.h +++ b/src/plugins/qmldesigner/designercore/include/qmldesignercorelib_global.h @@ -3,29 +3,16 @@ #pragma once +#include "modelfwd.h" +#include "qmldesignercorelib_exports.h" + #include #include -#include - #include - -// Unnecessary since core isn't a dll any more. - -#if defined(QMLDESIGNERCORE_LIBRARY) -#define QMLDESIGNERCORE_EXPORT Q_DECL_EXPORT -#elif defined(QMLDESIGNERCORE_STATIC_LIBRARY) -#define QMLDESIGNERCORE_EXPORT -#else -#define QMLDESIGNERCORE_EXPORT Q_DECL_IMPORT -#endif +#include namespace QmlDesigner { -using PropertyName = QByteArray; -using PropertyNameList = QList; -using TypeName = QByteArray; -using PropertyTypeList = QList; -using IdName = QByteArray; enum AnchorLineType { AnchorLineInvalid = 0x0, @@ -38,26 +25,12 @@ enum AnchorLineType { AnchorLineVerticalCenter = 0x20, AnchorLineBaseline = 0x40, - AnchorLineFill = AnchorLineLeft | AnchorLineRight | AnchorLineTop | AnchorLineBottom, + AnchorLineFill = AnchorLineLeft | AnchorLineRight | AnchorLineTop | AnchorLineBottom, AnchorLineCenter = AnchorLineVerticalCenter | AnchorLineHorizontalCenter, AnchorLineHorizontalMask = AnchorLineLeft | AnchorLineRight | AnchorLineHorizontalCenter, - AnchorLineVerticalMask = AnchorLineTop | AnchorLineBottom | AnchorLineVerticalCenter | AnchorLineBaseline, + AnchorLineVerticalMask = AnchorLineTop | AnchorLineBottom | AnchorLineVerticalCenter + | AnchorLineBaseline, AnchorLineAllMask = AnchorLineVerticalMask | AnchorLineHorizontalMask }; -struct ModelDeleter -{ - QMLDESIGNERCORE_EXPORT void operator()(class Model *model); -}; - -using ModelPointer = std::unique_ptr; - -constexpr bool useProjectStorage() -{ -#ifdef QDS_USE_PROJECTSTORAGE - return true; -#else - return false; -#endif -} } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/metainfo/itemlibraryinfo.cpp b/src/plugins/qmldesigner/designercore/metainfo/itemlibraryinfo.cpp index b1473d6a4c0..cbde96e9926 100644 --- a/src/plugins/qmldesigner/designercore/metainfo/itemlibraryinfo.cpp +++ b/src/plugins/qmldesigner/designercore/metainfo/itemlibraryinfo.cpp @@ -1,11 +1,12 @@ // Copyright (C) 2016 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 -#include "itemlibraryinfo.h" +#include "../include/itemlibraryinfo.h" #include "nodemetainfo.h" #include "qregularexpression.h" #include +#include #include diff --git a/src/plugins/qmldesigner/designercore/metainfo/metainfo.cpp b/src/plugins/qmldesigner/designercore/metainfo/metainfo.cpp index 354ccd63c3f..a86a597ea0b 100644 --- a/src/plugins/qmldesigner/designercore/metainfo/metainfo.cpp +++ b/src/plugins/qmldesigner/designercore/metainfo/metainfo.cpp @@ -7,6 +7,7 @@ #include "metainforeader.h" #include "iwidgetplugin.h" +#include #include #include @@ -30,24 +31,26 @@ enum { namespace QmlDesigner { namespace Internal { - -static QString globalMetaInfoPath() +static QString globalMetaInfoPath(const ExternalDependenciesInterface &externalDependecies) { #ifdef SHARE_QML_PATH if (Utils::qtcEnvironmentVariableIsSet("LOAD_QML_FROM_SOURCE")) return QLatin1String(SHARE_QML_PATH) + "/globalMetaInfo"; #endif - return Core::ICore::resourcePath("qmldesigner/globalMetaInfo").toString(); + return externalDependecies.resourcePath("qmldesigner/globalMetaInfo").toString(); } -Utils::FilePaths allGlobalMetaInfoFiles() +Utils::FilePaths allGlobalMetaInfoFiles(const ExternalDependenciesInterface &externalDependecies) { static Utils::FilePaths paths; if (!paths.isEmpty()) return paths; - QDirIterator it(globalMetaInfoPath(), { "*.metainfo" }, QDir::Files, QDirIterator::Subdirectories); + QDirIterator it(globalMetaInfoPath(externalDependecies), + {"*.metainfo"}, + QDir::Files, + QDirIterator::Subdirectories); while (it.hasNext()) paths.append(Utils::FilePath::fromString(it.next())); @@ -61,9 +64,9 @@ public: MetaInfoPrivate(MetaInfo *q); void clear(); - void initialize(); + void initialize(const ExternalDependenciesInterface &externalDependencies); - void parseItemLibraryDescriptions(); + void parseItemLibraryDescriptions(const ExternalDependenciesInterface &externalDependencies); QScopedPointer m_itemLibraryInfo; @@ -90,14 +93,14 @@ namespace { bool enableParseItemLibraryDescriptions = true; } -void MetaInfoPrivate::initialize() +void MetaInfoPrivate::initialize(const ExternalDependenciesInterface &externalDependencies) { if (enableParseItemLibraryDescriptions) - parseItemLibraryDescriptions(); + parseItemLibraryDescriptions(externalDependencies); m_isInitialized = true; } -void MetaInfoPrivate::parseItemLibraryDescriptions() +void MetaInfoPrivate::parseItemLibraryDescriptions(const ExternalDependenciesInterface &externalDependencies) { Internal::WidgetPluginManager pluginManager; for (const QString &pluginDir : std::as_const(m_q->s_pluginDirs)) @@ -108,6 +111,7 @@ void MetaInfoPrivate::parseItemLibraryDescriptions() try { reader.readMetaInfoFile(plugin->metaInfo()); } catch (const InvalidMetaInfoException &e) { +#ifndef UNIT_TESTS qWarning() << e.description(); const QString errorMessage = plugin->metaInfo() + QLatin1Char('\n') + QLatin1Char('\n') + reader.errors().join(QLatin1Char('\n')); @@ -115,15 +119,17 @@ void MetaInfoPrivate::parseItemLibraryDescriptions() QCoreApplication::translate("QmlDesigner::Internal::MetaInfoPrivate", "Invalid meta info"), errorMessage); +#endif } } - const Utils::FilePaths allMetaInfoFiles = allGlobalMetaInfoFiles(); + const Utils::FilePaths allMetaInfoFiles = allGlobalMetaInfoFiles(externalDependencies); for (const Utils::FilePath &path : allMetaInfoFiles) { Internal::MetaInfoReader reader(*m_q); try { reader.readMetaInfoFile(path.toString()); } catch (const InvalidMetaInfoException &e) { +#ifndef UNIT_TESTS qWarning() << e.description(); const QString errorMessage = path.toString() + QLatin1Char('\n') + QLatin1Char('\n') + reader.errors().join(QLatin1Char('\n')); @@ -131,6 +137,7 @@ void MetaInfoPrivate::parseItemLibraryDescriptions() QCoreApplication::translate("QmlDesigner::Internal::MetaInfoPrivate", "Invalid meta info"), errorMessage); +#endif } } } @@ -186,40 +193,16 @@ ItemLibraryInfo *MetaInfo::itemLibraryInfo() const return m_p->m_itemLibraryInfo.data(); } -/*! - Accesses the global meta information object. - You almost always want to use Model::metaInfo() instead. - - Internally, all meta information objects share this \e global object - where static QML type information is stored. - */ -MetaInfo MetaInfo::global() +void MetaInfo::initializeGlobal(const QStringList &pluginPaths, + const ExternalDependenciesInterface &externalDependencies) { QMutexLocker locker(&s_lock); if (!s_global.m_p->m_isInitialized) { + s_pluginDirs = pluginPaths, s_global.m_p = QSharedPointer(new MetaInfoPrivate(&s_global)); - s_global.m_p->initialize(); + s_global.m_p->initialize(externalDependencies); } - return s_global; -} - -/*! - Clears the global meta information object. - - This function should be called once on application shutdown to free static data structures. - */ -void MetaInfo::clearGlobal() -{ - if (s_global.m_p->m_isInitialized) - s_global.m_p->clear(); -} - -void MetaInfo::setPluginPaths(const QStringList &paths) -{ - s_pluginDirs = paths; - global(); - clearGlobal(); } bool MetaInfo::isGlobal() const @@ -227,6 +210,11 @@ bool MetaInfo::isGlobal() const return (this->m_p == s_global.m_p); } +MetaInfo MetaInfo::global() +{ + return s_global; +} + bool operator==(const MetaInfo &first, const MetaInfo &second) { return first.m_p == second.m_p; diff --git a/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp b/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp index 770d919d71c..443abf3f07e 100644 --- a/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp +++ b/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp @@ -1649,7 +1649,6 @@ QString NodeMetaInfo::importDirectoryPath() const return {}; } -#ifdef QDS_USE_PROJECTSTORAGE const Storage::Info::Type &NodeMetaInfo::typeData() const { if (!m_typeData) @@ -1657,7 +1656,6 @@ const Storage::Info::Type &NodeMetaInfo::typeData() const return *m_typeData; } -#endif bool NodeMetaInfo::isSubclassOf(const TypeName &type, int majorVersion, int minorVersion) const { @@ -1886,8 +1884,7 @@ bool NodeMetaInfo::isBasedOn(const NodeMetaInfo &metaInfo1, namespace { template -bool isBasedOnCommonType(NotNullPointer> projectStorage, - TypeId typeId) +bool isBasedOnCommonType(NotNullPointer projectStorage, TypeId typeId) { auto base = projectStorage->commonTypeId(); @@ -2033,16 +2030,6 @@ bool NodeMetaInfo::isQtQuickPropertyChanges() const } } -bool NodeMetaInfo::isQuickStateOperation() const -{ - if constexpr (useProjectStorage()) { - using namespace Storage::Info; - return isBasedOnCommonType(m_projectStorage, m_typeId); - } else { - return isValid() && isSubclassOf(".QQuickStateOperation"); - } -} - bool NodeMetaInfo::isQtSafeRendererSafeRendererPicture() const { if constexpr (useProjectStorage()) { @@ -2316,7 +2303,7 @@ bool NodeMetaInfo::isQtQuick3DParticles3DAttractor3D() const } } -bool NodeMetaInfo::isQuick3DParticleAbstractShape() const +bool NodeMetaInfo::isQtQuick3DParticleAbstractShape() const { if constexpr (useProjectStorage()) { using namespace Storage::Info; @@ -2401,7 +2388,8 @@ bool NodeMetaInfo::isQtQuickStateOperation() const { if constexpr (useProjectStorage()) { using namespace Storage::Info; - return isBasedOnCommonType(m_projectStorage, m_typeId); + return isBasedOnCommonType(m_projectStorage, + m_typeId); } else { return isValid() && isSubclassOf(".QQuickStateOperation"); } @@ -2830,12 +2818,13 @@ bool NodeMetaInfo::isEnumeration() const return false; } -PropertyMetaInfo::PropertyMetaInfo(QSharedPointer nodeMetaInfoPrivateData, - const PropertyName &propertyName) +PropertyMetaInfo::PropertyMetaInfo( + [[maybe_unused]] QSharedPointer nodeMetaInfoPrivateData, + [[maybe_unused]] const PropertyName &propertyName) +#ifndef QDS_USE_PROJECTSTORAGE : m_nodeMetaInfoPrivateData{nodeMetaInfoPrivateData} , m_propertyName{propertyName} - , m_projectStorage{nullptr} - +#endif {} PropertyMetaInfo::~PropertyMetaInfo() = default; @@ -2846,8 +2835,8 @@ NodeMetaInfo PropertyMetaInfo::propertyType() const return {propertyData().typeId, m_projectStorage}; } else { if (isValid()) - return NodeMetaInfo{m_nodeMetaInfoPrivateData->model(), - m_nodeMetaInfoPrivateData->propertyType(m_propertyName), + return NodeMetaInfo{nodeMetaInfoPrivateData()->model(), + nodeMetaInfoPrivateData()->propertyType(propertyName()), -1, -1}; } @@ -2860,7 +2849,7 @@ PropertyName PropertyMetaInfo::name() const if constexpr (useProjectStorage()) return PropertyName(Utils::SmallStringView(propertyData().name)); else - return m_propertyName; + return propertyName(); } bool PropertyMetaInfo::isWritable() const @@ -2868,7 +2857,7 @@ bool PropertyMetaInfo::isWritable() const if constexpr (useProjectStorage()) return !(propertyData().traits & Storage::PropertyDeclarationTraits::IsReadOnly); else - return isValid() && m_nodeMetaInfoPrivateData->isPropertyWritable(m_propertyName); + return isValid() && nodeMetaInfoPrivateData()->isPropertyWritable(propertyName()); } bool PropertyMetaInfo::isListProperty() const @@ -2876,7 +2865,7 @@ bool PropertyMetaInfo::isListProperty() const if constexpr (useProjectStorage()) return propertyData().traits & Storage::PropertyDeclarationTraits::IsList; else - return isValid() && m_nodeMetaInfoPrivateData->isPropertyList(m_propertyName); + return isValid() && nodeMetaInfoPrivateData()->isPropertyList(propertyName()); } bool PropertyMetaInfo::isEnumType() const @@ -2884,7 +2873,7 @@ bool PropertyMetaInfo::isEnumType() const if constexpr (useProjectStorage()) return propertyType().isEnumeration(); else - return isValid() && m_nodeMetaInfoPrivateData->isPropertyEnum(m_propertyName); + return isValid() && nodeMetaInfoPrivateData()->isPropertyEnum(propertyName()); } bool PropertyMetaInfo::isPrivate() const @@ -2892,7 +2881,7 @@ bool PropertyMetaInfo::isPrivate() const if constexpr (useProjectStorage()) return propertyData().name.startsWith("__"); else - return isValid() && m_propertyName.startsWith("__"); + return isValid() && propertyName().startsWith("__"); } bool PropertyMetaInfo::isPointer() const @@ -2900,7 +2889,7 @@ bool PropertyMetaInfo::isPointer() const if constexpr (useProjectStorage()) return propertyData().traits & Storage::PropertyDeclarationTraits::IsPointer; else - return isValid() && m_nodeMetaInfoPrivateData->isPropertyPointer(m_propertyName); + return isValid() && nodeMetaInfoPrivateData()->isPropertyPointer(propertyName()); } QVariant PropertyMetaInfo::castedValue(const QVariant &value) const @@ -2916,7 +2905,7 @@ QVariant PropertyMetaInfo::castedValue(const QVariant &value) const const TypeName &typeName = propertyTypeName(); - QVariant::Type typeId = m_nodeMetaInfoPrivateData->variantTypeId(m_propertyName); + QVariant::Type typeId = nodeMetaInfoPrivateData()->variantTypeId(propertyName()); if (variant.type() == QVariant::UserType && variant.userType() == ModelNode::variantUserType()) { @@ -3008,6 +2997,25 @@ TypeName PropertyMetaInfo::propertyTypeName() const return propertyType().typeName(); } +const NodeMetaInfoPrivate *PropertyMetaInfo::nodeMetaInfoPrivateData() const +{ +#ifndef QDS_USE_PROJECTSTORAGE + return m_nodeMetaInfoPrivateData.data(); +#else + return nullptr; +#endif +} + +const PropertyName &PropertyMetaInfo::propertyName() const +{ +#ifndef QDS_USE_PROJECTSTORAGE + return m_propertyName; +#else + static PropertyName dummy; + return dummy; +#endif +} + NodeMetaInfo NodeMetaInfo::commonBase(const NodeMetaInfo &metaInfo) const { for (const NodeMetaInfo &info : metaInfo.superClasses()) { diff --git a/src/plugins/qmldesigner/designercore/model/model.cpp b/src/plugins/qmldesigner/designercore/model/model.cpp index c1d6c078631..be3aa9d3d23 100644 --- a/src/plugins/qmldesigner/designercore/model/model.cpp +++ b/src/plugins/qmldesigner/designercore/model/model.cpp @@ -57,7 +57,7 @@ namespace QmlDesigner { namespace Internal { ModelPrivate::ModelPrivate(Model *model, - ProjectStorage &projectStorage, + ProjectStorageType &projectStorage, const TypeName &typeName, int major, int minor, @@ -211,6 +211,18 @@ void ModelPrivate::changeNodeType(const InternalNodePointer &node, const TypeNam } } +namespace { +std::pair decomposeTypePath(Utils::SmallStringView typeName) +{ + auto found = std::find(typeName.rbegin(), typeName.rend(), '.'); + + if (found == typeName.rend()) + return {}; + + return {{typeName.begin(), std::prev(found.base())}, {found.base(), typeName.end()}}; +} +} // namespace + InternalNodePointer ModelPrivate::createNode(const TypeName &typeName, int majorVersion, int minorVersion, @@ -230,6 +242,15 @@ InternalNodePointer ModelPrivate::createNode(const TypeName &typeName, internalId = m_internalIdCounter++; auto newNode = std::make_shared(typeName, majorVersion, minorVersion, internalId); + + if constexpr (useProjectStorage()) { + auto [moduleName, shortTypeName] = decomposeTypePath(typeName); + ModuleId moduleId = projectStorage->moduleId(moduleName); + newNode->typeId = projectStorage->typeId(moduleId, + shortTypeName, + Storage::Version{majorVersion, minorVersion}); + } + newNode->nodeSourceType = nodeSourceType; newNode->behaviorPropertyName = behaviorPropertyName; @@ -1386,7 +1407,7 @@ void WriteLocker::lock(Model *model) } // namespace Internal -Model::Model(ProjectStorage &projectStorage, +Model::Model(ProjectStorageType &projectStorage, const TypeName &typeName, int major, int minor, @@ -1631,7 +1652,7 @@ void Model::endDrag() d->notifyDragEnded(); } -NotNullPointer> Model::projectStorage() const +NotNullPointer Model::projectStorage() const { return d->projectStorage; } @@ -1808,7 +1829,7 @@ void Model::setMetaInfo(const MetaInfo &metaInfo) template NodeMetaInfo Model::createNodeMetaInfo() const { - auto typeId = d->projectStorage->commonTypeCache.typeId(); + auto typeId = d->projectStorage->commonTypeCache().typeId(); return {typeId, d->projectStorage}; } diff --git a/src/plugins/qmldesigner/designercore/model/model_p.h b/src/plugins/qmldesigner/designercore/model/model_p.h index 8cdaaac3ecb..d1678192804 100644 --- a/src/plugins/qmldesigner/designercore/model/model_p.h +++ b/src/plugins/qmldesigner/designercore/model/model_p.h @@ -73,7 +73,7 @@ class ModelPrivate : public QObject { public: ModelPrivate(Model *model, - ProjectStorage &projectStorage, + ProjectStorageType &projectStorage, const TypeName &type, int major, int minor, @@ -251,9 +251,6 @@ public: void updateEnabledViews(); -public: - NotNullPointer> projectStorage = nullptr; - private: void removePropertyWithoutNotification(const InternalPropertyPointer &property); void removeAllSubNodes(const InternalNodePointer &node); @@ -264,6 +261,9 @@ private: QVector toInternalNodeVector(const QVector &modelNodeVector) const; const QList> enabledViews() const; +public: + NotNullPointer projectStorage = nullptr; + private: Model *m_model = nullptr; MetaInfo m_metaInfo; diff --git a/src/plugins/qmldesigner/designercore/model/modelnode.cpp b/src/plugins/qmldesigner/designercore/model/modelnode.cpp index aab0d13c249..bbf2c1630ee 100644 --- a/src/plugins/qmldesigner/designercore/model/modelnode.cpp +++ b/src/plugins/qmldesigner/designercore/model/modelnode.cpp @@ -61,7 +61,7 @@ ModelNode::ModelNode(const InternalNodePointer &internalNode, Model *model, cons m_model(model), m_view(const_cast(view)) { - Q_ASSERT(!m_model || m_view); + Q_ASSERT(m_model); } ModelNode::ModelNode(const ModelNode &modelNode, AbstractView *view) diff --git a/src/plugins/qmldesigner/designercore/model/qmlchangeset.cpp b/src/plugins/qmldesigner/designercore/model/qmlchangeset.cpp index 5029fdc79d3..134c4bf1c20 100644 --- a/src/plugins/qmldesigner/designercore/model/qmlchangeset.cpp +++ b/src/plugins/qmldesigner/designercore/model/qmlchangeset.cpp @@ -76,7 +76,7 @@ bool QmlModelStateOperation::isValid() const bool QmlModelStateOperation::isValidQmlModelStateOperation(const ModelNode &modelNode) { - return isValidQmlModelNodeFacade(modelNode) && modelNode.metaInfo().isQuickStateOperation(); + return isValidQmlModelNodeFacade(modelNode) && modelNode.metaInfo().isQtQuickStateOperation(); } void QmlPropertyChanges::removeProperty(const PropertyName &name) diff --git a/src/plugins/qmldesigner/designercore/projectstorage/commontypecache.h b/src/plugins/qmldesigner/designercore/projectstorage/commontypecache.h index d60dfa3abc7..0db44e3df76 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/commontypecache.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/commontypecache.h @@ -84,6 +84,7 @@ inline constexpr char QtQuick3D[] = "QtQuick3D"; inline constexpr char QtQuick3D_Particles3D[] = "QtQuick3D.Particles3D"; inline constexpr char QtQuick3D_Particles3D_cppnative[] = "QtQuick3D.Particles3D-cppnative"; inline constexpr char QtQuick[] = "QtQuick"; +inline constexpr char QtQuick_cppnative[] = "QtQuick-cppnative"; inline constexpr char QtQuick_Controls[] = "QtQuick.Controls"; inline constexpr char QtQuick_Dialogs[] = "QtQuick.Dialogs"; inline constexpr char QtQuick_Extras[] = "QtQuick.Extras"; @@ -91,9 +92,7 @@ inline constexpr char QtQuick_Layouts[] = "QtQuick.Layouts"; inline constexpr char QtQuick_Studio_Components[] = "QtQuick.Studio.Components"; 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 QuickStateOperation[] = "QuickStateOperation"; inline constexpr char Rectangle[] = "Rectangle"; inline constexpr char Repeater[] = "Repeater"; inline constexpr char SafePicture[] = "SafePicture"; @@ -159,7 +158,6 @@ class CommonTypeCache CacheType, CacheType, CacheType, - CacheType, CacheType, CacheType, CacheType, @@ -202,6 +200,7 @@ class CommonTypeCache CacheType, CacheType, CacheType, + CacheType, CacheType, CacheType, CacheType, @@ -228,7 +227,7 @@ class CommonTypeCache CacheType, CacheType, CacheType, - CacheType, + CacheType, CacheType, CacheType, CacheType>; @@ -244,8 +243,8 @@ public: } TypeId refreshTypedId(BaseCacheType &type, - Utils::SmallStringView moduleName, - Utils::SmallStringView typeName) const + ::Utils::SmallStringView moduleName, + ::Utils::SmallStringView typeName) const { if (!type.moduleId) type.moduleId = m_projectStorage.moduleId(moduleName); diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h index 3ebe87da76e..be0c9213a75 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h @@ -99,7 +99,7 @@ public: synchronizeProjectDatas(package.projectDatas, package.updatedProjectSourceIds); - commonTypeCache.resetTypeIds(); + commonTypeCache_.resetTypeIds(); transaction.commit(); } @@ -119,7 +119,7 @@ public: TypeId typeId(ModuleId moduleId, Utils::SmallStringView exportedTypeName, - Storage::Version version) const + Storage::Version version) const override { if (version.minor) return selectTypeIdByModuleIdAndExportedNameAndVersionStatement @@ -162,7 +162,7 @@ public: } std::optional propertyDeclaration( - PropertyDeclarationId propertyDeclarationId) const + PropertyDeclarationId propertyDeclarationId) const override { return selectPropertyDeclarationForPropertyDeclarationIdStatement .template optionalValueWithTransaction( @@ -193,22 +193,27 @@ public: propertyDeclarationId); } + const Storage::Info::CommonTypeCache &commonTypeCache() const override + { + return commonTypeCache_; + } + template TypeId commonTypeId() const { - return commonTypeCache.template typeId(); + return commonTypeCache_.template typeId(); } template TypeId builtinTypeId() const { - return commonTypeCache.template builtinTypeId(); + return commonTypeCache_.template builtinTypeId(); } template TypeId builtinTypeId() const { - return commonTypeCache.template builtinTypeId(); + return commonTypeCache_.template builtinTypeId(); } TypeIds prototypeIds(TypeId type) const @@ -224,7 +229,7 @@ public: } template - bool isBasedOn(TypeId typeId, TypeIds... baseTypeIds) const + bool isBasedOn_(TypeId typeId, TypeIds... baseTypeIds) const { static_assert(((std::is_same_v) &&...), "Parameter must be a TypeId!"); @@ -238,6 +243,47 @@ public: return false; } + bool isBasedOn(TypeId id0) const override { return isBasedOn_(id0); } + + bool isBasedOn(TypeId id0, TypeId id1) const override { return isBasedOn_(id0, id1); } + + bool isBasedOn(TypeId id0, TypeId id1, TypeId id2) const override + { + return isBasedOn_(id0, id1, id2); + } + + bool isBasedOn(TypeId id0, TypeId id1, TypeId id2, TypeId id3) const override + { + return isBasedOn_(id0, id1, id2, id3); + } + + bool isBasedOn(TypeId id0, TypeId id1, TypeId id2, TypeId id3, TypeId id4) const override + { + return isBasedOn_(id0, id1, id2, id3, id4); + } + + bool isBasedOn(TypeId id0, TypeId id1, TypeId id2, TypeId id3, TypeId id4, TypeId id5) const override + { + return isBasedOn_(id0, id1, id2, id3, id4, id5); + } + + bool isBasedOn(TypeId id0, TypeId id1, TypeId id2, TypeId id3, TypeId id4, TypeId id5, TypeId id6) const override + { + return isBasedOn_(id0, id1, id2, id3, id4, id5, id6); + } + + bool isBasedOn(TypeId id0, + TypeId id1, + TypeId id2, + TypeId id3, + TypeId id4, + TypeId id5, + TypeId id6, + TypeId id7) const override + { + return isBasedOn_(id0, id1, id2, id3, id4, id5, id6, id7); + } + TypeId fetchTypeIdByExportedName(Utils::SmallStringView name) const { return selectTypeIdByExportedNameStatement.template valueWithTransaction(name); @@ -2597,7 +2643,7 @@ public: Database &database; Initializer initializer; mutable ModuleCache moduleCache{ModuleStorageAdapter{*this}}; - Storage::Info::CommonTypeCache> commonTypeCache{*this}; + Storage::Info::CommonTypeCache commonTypeCache_{*this}; ReadWriteStatement<1, 3> upsertTypeStatement{ "INSERT INTO types(sourceId, name, traits) VALUES(?1, ?2, ?3) ON CONFLICT DO " "UPDATE SET traits=excluded.traits WHERE traits IS NOT " diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstoragefwd.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstoragefwd.h index 461868ec659..db0cec068bc 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstoragefwd.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstoragefwd.h @@ -5,9 +5,11 @@ namespace Sqlite { class Database; -}; +} namespace QmlDesigner { +class ProjectStorageInterface; + template class ProjectStorage; diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinterface.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinterface.h index 5a89d29dacf..9558bc2e184 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinterface.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinterface.h @@ -3,9 +3,13 @@ #pragma once +#include "commontypecache.h" #include "filestatus.h" +#include "projectstorageids.h" #include "projectstoragetypes.h" +#include + namespace QmlDesigner { class ProjectStorageInterface @@ -13,12 +17,58 @@ class ProjectStorageInterface public: virtual void synchronize(Storage::Synchronization::SynchronizationPackage package) = 0; - virtual ModuleId moduleId(Utils::SmallStringView name) const = 0; + virtual ModuleId moduleId(::Utils::SmallStringView name) const = 0; + virtual std::optional + propertyDeclaration(PropertyDeclarationId propertyDeclarationId) const = 0; + virtual TypeId typeId(ModuleId moduleId, + ::Utils::SmallStringView exportedTypeName, + Storage::Version version) const + = 0; + virtual PropertyDeclarationIds propertyDeclarationIds(TypeId typeId) const = 0; + virtual PropertyDeclarationIds localPropertyDeclarationIds(TypeId typeId) const = 0; + virtual PropertyDeclarationId propertyDeclarationId(TypeId typeId, + ::Utils::SmallStringView propertyName) const + = 0; + virtual std::optional type(TypeId typeId) const = 0; + virtual std::vector<::Utils::SmallString> signalDeclarationNames(TypeId typeId) const = 0; + virtual std::vector<::Utils::SmallString> functionDeclarationNames(TypeId typeId) const = 0; + virtual std::optional<::Utils::SmallString> + propertyName(PropertyDeclarationId propertyDeclarationId) const = 0; + virtual TypeIds prototypeAndSelfIds(TypeId type) const = 0; + virtual TypeIds prototypeIds(TypeId type) const = 0; + virtual bool isBasedOn(TypeId) const = 0; + virtual bool isBasedOn(TypeId, TypeId) const = 0; + virtual bool isBasedOn(TypeId, TypeId, TypeId) const = 0; + virtual bool isBasedOn(TypeId, TypeId, TypeId, TypeId) const = 0; + virtual bool isBasedOn(TypeId, TypeId, TypeId, TypeId, TypeId) const = 0; + virtual bool isBasedOn(TypeId, TypeId, TypeId, TypeId, TypeId, TypeId) const = 0; + virtual bool isBasedOn(TypeId, TypeId, TypeId, TypeId, TypeId, TypeId, TypeId) const = 0; + 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 const Storage::Info::CommonTypeCache &commonTypeCache() const = 0; + + template + TypeId commonTypeId() const + { + return commonTypeCache().template typeId(); + } + + template + TypeId builtinTypeId() const + { + return commonTypeCache().template builtinTypeId(); + } + + template + TypeId builtinTypeId() const + { + return commonTypeCache().template builtinTypeId(); + } + protected: ~ProjectStorageInterface() = default; }; diff --git a/src/plugins/qmldesigner/qmldesignerexternaldependencies.cpp b/src/plugins/qmldesigner/qmldesignerexternaldependencies.cpp index ef844b4eaef..a2ffbdc94bf 100644 --- a/src/plugins/qmldesigner/qmldesignerexternaldependencies.cpp +++ b/src/plugins/qmldesigner/qmldesignerexternaldependencies.cpp @@ -274,4 +274,9 @@ QString ExternalDependencies::qtQuickVersion() const return qmlBuildSystem ? qmlBuildSystem->versionQtQuick() : QString{}; } +Utils::FilePath ExternalDependencies::resourcePath(const QString &relativePath) const +{ + return Core::ICore::resourcePath(relativePath); +} + } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/qmldesignerexternaldependencies.h b/src/plugins/qmldesigner/qmldesignerexternaldependencies.h index b5771919139..0f6acf2c106 100644 --- a/src/plugins/qmldesigner/qmldesignerexternaldependencies.h +++ b/src/plugins/qmldesigner/qmldesignerexternaldependencies.h @@ -40,6 +40,7 @@ public: QStringList projectModulePaths() const override; bool isQt6Project() const override; QString qtQuickVersion() const override; + Utils::FilePath resourcePath(const QString &relativePath) const override; private: const DesignerSettings &m_designerSettings; diff --git a/src/plugins/qmldesigner/qmldesignerplugin.cpp b/src/plugins/qmldesigner/qmldesignerplugin.cpp index 254c0ea5125..4a1744692d4 100644 --- a/src/plugins/qmldesigner/qmldesignerplugin.cpp +++ b/src/plugins/qmldesigner/qmldesignerplugin.cpp @@ -292,10 +292,10 @@ bool QmlDesignerPlugin::delayedInitialize() Utils::transform(ExtensionSystem::PluginManager::pluginPaths(), [postfix](const QString &p) { return QString(p + postfix); }); - MetaInfo::setPluginPaths(pluginPaths); - d->viewManager.registerView( - std::make_unique(d->externalDependencies)); + MetaInfo::initializeGlobal(pluginPaths, d->externalDependencies); + + d->viewManager.registerView(std::make_unique(d->externalDependencies)); auto timelineView = d->viewManager.registerView( std::make_unique(d->externalDependencies)); @@ -337,8 +337,6 @@ bool QmlDesignerPlugin::delayedInitialize() Core::ICore::appendAboutInformation(tr("Licensee: %1").arg(licensee())); } - MetaInfo::global(); - return true; } diff --git a/tests/auto/qml/qmldesigner/coretests/tst_testcore.cpp b/tests/auto/qml/qmldesigner/coretests/tst_testcore.cpp index 228d0459f4a..5702fd3846e 100644 --- a/tests/auto/qml/qmldesigner/coretests/tst_testcore.cpp +++ b/tests/auto/qml/qmldesigner/coretests/tst_testcore.cpp @@ -123,24 +123,23 @@ static void initializeMetaTypeSystem(const QString &resourcePath) qWarning() << qPrintable(errorAndWarning); } -namespace { class ExternalDependenciesFake : public QObject, public ExternalDependenciesInterface { public: - ExternalDependenciesFake(Model &model) + ExternalDependenciesFake(Model *model) : model{model} {} double formEditorDevicePixelRatio() const override { return 1.; } QString currentProjectDirPath() const override { - return QFileInfo(model.fileUrl().toLocalFile()).absolutePath(); + return QFileInfo(model->fileUrl().toLocalFile()).absolutePath(); } QUrl currentResourcePath() const override { - return QUrl::fromLocalFile(QFileInfo(model.fileUrl().toLocalFile()).absolutePath()); + return QUrl::fromLocalFile(QFileInfo(model->fileUrl().toLocalFile()).absolutePath()); } QString defaultPuppetFallbackDirectory() const override { return {}; } @@ -164,13 +163,16 @@ public: QStringList projectModulePaths() const override { return {}; } bool isQt6Project() const override { return {}; } QString qtQuickVersion() const override { return {}; } + Utils::FilePath resourcePath(const QString &) const override { return {}; } public: QSettings qsettings; QmlDesigner::DesignerSettings settings{&qsettings}; - Model &model; + Model *model; }; +namespace { + ModelPointer createModel(const QString &typeName, int major = 2, int minor = 1, @@ -191,7 +193,7 @@ ModelPointer createModel(const QString &typeName, NotIndentingTextEditModifier *modifier = new NotIndentingTextEditModifier(textEdit); modifier->setParent(textEdit); - auto externalDependencies = new ExternalDependenciesFake{*model}; + auto externalDependencies = new ExternalDependenciesFake{model.get()}; externalDependencies->setParent(model.get()); auto rewriterView = new QmlDesigner::RewriterView(*externalDependencies, @@ -208,7 +210,7 @@ ModelPointer createModel(const QString &typeName, auto createTextRewriterView( Model &model, RewriterView::DifferenceHandling differenceHandling = RewriterView::Amend) { - auto externalDependencies = new ExternalDependenciesFake{model}; + auto externalDependencies = new ExternalDependenciesFake{&model}; auto rewriter = std::make_unique(*externalDependencies, differenceHandling); externalDependencies->setParent(rewriter.get()); @@ -218,13 +220,15 @@ auto createTextRewriterView( } // namespace tst_TestCore::tst_TestCore() - : QObject() + : externalDependencies{std::make_unique(nullptr)} { QLoggingCategory::setFilterRules(QStringLiteral("qtc.qmljs.imports=false")); //QLoggingCategory::setFilterRules(QStringLiteral("*.info=false\n*.debug=false\n*.warning=false")); QLoggingCategory::setFilterRules(QStringLiteral("*.warning=false")); } +tst_TestCore::~tst_TestCore() = default; + void tst_TestCore::initTestCase() { QmlModelNodeFacade::enableUglyWorkaroundForIsValidQmlModelNodeFacadeInTests(); @@ -256,7 +260,8 @@ void tst_TestCore::initTestCase() qDebug() << pluginPath; Q_ASSERT(QFileInfo::exists(pluginPath)); - MetaInfo::setPluginPaths(QStringList() << pluginPath); + + MetaInfo::initializeGlobal({pluginPath}, *externalDependencies); QFileInfo builtins(IDE_DATA_PATH "/qml-type-descriptions/builtins.qmltypes"); QStringList errors, warnings; @@ -265,7 +270,6 @@ void tst_TestCore::initTestCase() void tst_TestCore::cleanupTestCase() { - MetaInfo::clearGlobal(); } void tst_TestCore::init() @@ -2553,7 +2557,7 @@ void tst_TestCore::testModelRemoveNode() model->attachView(view.data()); TestConnectionManager connectionManager; - ExternalDependenciesFake externalDependenciesFake{*model}; + ExternalDependenciesFake externalDependenciesFake{model.get()}; NodeInstanceView nodeInstanceView{connectionManager, externalDependenciesFake}; model->attachView(&nodeInstanceView); @@ -4528,7 +4532,7 @@ void tst_TestCore::testSubComponentManager() auto model(createModel("QtQuick.Rectangle", 2, 15)); model->setFileUrl(QUrl::fromLocalFile(fileName)); - ExternalDependenciesFake externalDependenciesFake{*model}; + ExternalDependenciesFake externalDependenciesFake{model.get()}; QScopedPointer subComponentManager( new SubComponentManager(model.get(), externalDependenciesFake)); subComponentManager->update(QUrl::fromLocalFile(fileName), model->imports()); @@ -6715,7 +6719,7 @@ void tst_TestCore::testInstancesAttachToExistingModel() // Attach NodeInstanceView TestConnectionManager connectionManager; - ExternalDependenciesFake externalDependenciesFake{*model}; + ExternalDependenciesFake externalDependenciesFake{model.get()}; NodeInstanceView instanceView{connectionManager, externalDependenciesFake}; model->attachView(&instanceView); diff --git a/tests/auto/qml/qmldesigner/coretests/tst_testcore.h b/tests/auto/qml/qmldesigner/coretests/tst_testcore.h index 5160cf5ca3f..1304f4aa654 100644 --- a/tests/auto/qml/qmldesigner/coretests/tst_testcore.h +++ b/tests/auto/qml/qmldesigner/coretests/tst_testcore.h @@ -15,7 +15,7 @@ class tst_TestCore : public QObject Q_OBJECT public: tst_TestCore(); - + ~tst_TestCore(); private slots: void initTestCase(); void cleanupTestCase(); @@ -225,5 +225,6 @@ private slots: void readAnnotations(); private: + class std::unique_ptr externalDependencies; ExtensionSystem::PluginManager pm; // FIXME remove }; diff --git a/tests/unit/mockup/qmldesigner/designercore/include/itemlibraryinfo.h b/tests/unit/mockup/qmldesigner/designercore/include/itemlibraryinfo.h deleted file mode 100644 index b0f80b18702..00000000000 --- a/tests/unit/mockup/qmldesigner/designercore/include/itemlibraryinfo.h +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include "qmldesignercorelib_global.h" - -#include - -namespace QmlDesigner { - -class ItemLibraryEntry -{ -public: - QString name() const { return {}; } - TypeName typeName() const { return {}; } - QIcon typeIcon() const { return {}; } - QString libraryEntryIconPath() const { return {}; } -}; - -class ItemLibraryInfo -{ -public: - QList entries() const { return {}; } - QList entriesForType([[maybe_unused]] const QByteArray &typeName, - [[maybe_unused]] int majorVersion, - [[maybe_unused]] int minorVersion) const - { - return {}; - } - ItemLibraryEntry entry([[maybe_unused]] const QString &name) const { return {}; } -}; - -} // namespace QmlDesigner diff --git a/tests/unit/mockup/qmldesigner/designercore/include/metainfo.h b/tests/unit/mockup/qmldesigner/designercore/include/metainfo.h deleted file mode 100644 index fdf16dc489b..00000000000 --- a/tests/unit/mockup/qmldesigner/designercore/include/metainfo.h +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include "qmldesignercorelib_global.h" - -#include -#include - -#include "itemlibraryinfo.h" -#include - -namespace QmlDesigner { - -class ModelNode; -class AbstractProperty; -class ItemLibraryInfo; - -inline bool operator==([[maybe_unused]] const MetaInfo &first, [[maybe_unused]] const MetaInfo &second) -{ - return {}; -} -inline bool operator!=([[maybe_unused]] const MetaInfo &first, [[maybe_unused]] const MetaInfo &second) -{ - return {}; -} - -class QMLDESIGNERCORE_EXPORT MetaInfo -{ -public: - ItemLibraryInfo *itemLibraryInfo() const { return {}; } - -public: - static MetaInfo global() { return {}; } - static void clearGlobal() {} - - static void setPluginPaths([[maybe_unused]] const QStringList &paths) {} -}; - -} //namespace QmlDesigner diff --git a/tests/unit/mockup/qmldesigner/designercore/include/nodemetainfo.h b/tests/unit/mockup/qmldesigner/designercore/include/nodemetainfo.h deleted file mode 100644 index 6d9829508a6..00000000000 --- a/tests/unit/mockup/qmldesigner/designercore/include/nodemetainfo.h +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include "propertymetainfo.h" - -#include -#include - -#include -#include -#include -#include - -#include - -QT_BEGIN_NAMESPACE -class QDeclarativeContext; -QT_END_NAMESPACE - -namespace QmlDesigner { - -class MetaInfo; -class Model; -class AbstractProperty; - -class NodeMetaInfo -{ -public: - NodeMetaInfo() {} - NodeMetaInfo(Model *, const TypeName &, int, int) {} - NodeMetaInfo(TypeId, NotNullPointer>) {} - NodeMetaInfo(NotNullPointer>) {} - - bool isValid() const { return {}; } - bool isFileComponent() const { return {}; } - bool hasProperty(const PropertyName &) const { return {}; } - PropertyMetaInfos properties() const { return {}; } - PropertyMetaInfos localProperties() const { return {}; } - PropertyMetaInfo property(const PropertyName &) const { return {}; } - PropertyNameList propertyNames() const { return {}; } - PropertyNameList signalNames() const { return {}; } - PropertyNameList directPropertyNames() const { return {}; } - PropertyName defaultPropertyName() const { return "data"; } - bool hasDefaultProperty() const { return {}; } - - std::vector classHierarchy() const { return {}; } - std::vector superClasses() const { return {}; } - - bool defaultPropertyIsComponent() const { return {}; } - - TypeName typeName() const { return {}; } - TypeName simplifiedTypeName() const { return {}; } - int majorVersion() const { return {}; } - int minorVersion() const { return {}; } - - QString componentSource() const { return {}; } - QString componentFileName() const { return {}; } - - bool hasCustomParser() const { return {}; } - - bool isBasedOn(const NodeMetaInfo &) const { return {}; } - bool isBasedOn(const NodeMetaInfo &, const NodeMetaInfo &) const { return {}; } - bool isBasedOn(const NodeMetaInfo &, const NodeMetaInfo &, const NodeMetaInfo &) const - { - return {}; - } - - bool isGraphicalItem() const { return {}; } - bool isLayoutable() const { return {}; } - bool isView() const { return {}; } - bool isTabView() const { return {}; } - bool isQtQuick3DNode() const { return {}; } - bool isQtQuick3DModel() const { return {}; } - bool isQtQuick3DMaterial() const { return {}; } - bool isQtQuickLoader() const { return {}; } - bool isQtQuickItem() const { return {}; } - bool isQtQuick3DTexture() const { return {}; } - - QString importDirectoryPath() const { return {}; } - - static void clearCache() {} -}; - -using NodeMetaInfos = std::vector; - -} // namespace QmlDesigner diff --git a/tests/unit/mockup/qmldesigner/designercore/include/propertymetainfo.h b/tests/unit/mockup/qmldesigner/designercore/include/propertymetainfo.h deleted file mode 100644 index 624f64e76ba..00000000000 --- a/tests/unit/mockup/qmldesigner/designercore/include/propertymetainfo.h +++ /dev/null @@ -1,49 +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 -#include - -#include - -namespace QmlDesigner { - -class PropertyMetaInfo -{ -public: - PropertyMetaInfo() = default; - PropertyMetaInfo(QSharedPointer, const PropertyName &) {} - ~PropertyMetaInfo() {} - - const TypeName &propertyTypeName() const - { - static TypeName foo; - return foo; - } - class NodeMetaInfo propertyNodeMetaInfo() const; - - bool isWritable() const { return {}; } - bool isListProperty() const { return {}; } - bool isEnumType() const { return {}; } - bool isPrivate() const { return {}; } - bool isPointer() const { return {}; } - QVariant castedValue(const QVariant &) const { return {}; } - PropertyName name() const & { return {}; } - - template - bool hasPropertyTypeName(const TypeName &...typeName) const - { - auto propertyTypeName_ = propertyTypeName(); - return ((propertyTypeName_ == typeName) && ...); - } - - bool propertyTypeNameIsUrl() const { return hasPropertyTypeName("QUrl", "url"); } -}; - -using PropertyMetaInfos = std::vector; - -} // namespace QmlDesigner diff --git a/tests/unit/unittest/CMakeLists.txt b/tests/unit/unittest/CMakeLists.txt index 8d3898d7f7c..7498aa52020 100644 --- a/tests/unit/unittest/CMakeLists.txt +++ b/tests/unit/unittest/CMakeLists.txt @@ -37,6 +37,8 @@ add_qtc_test(unittest GTEST TESTDATA_DIR="${CMAKE_CURRENT_BINARY_DIR}/data" TEST_RELATIVE_LIBEXEC_PATH="${TEST_RELATIVE_LIBEXEC_PATH}" QT6_INSTALL_PREFIX="${QT6_INSTALL_PREFIX}" + QDS_MODEL_USE_PROJECTSTORAGEINTERFACE + QDS_USE_PROJECTSTORAGE SOURCES abstractviewmock.h compare-operators.h @@ -208,16 +210,27 @@ extend_qtc_test(unittest include/bindingproperty.h include/imagecacheauxiliarydata.h include/import.h + include/itemlibraryinfo.h + include/metainfo.h + include/metainforeader.h include/model.h include/modelnode.h include/nodeabstractproperty.h include/nodelistproperty.h + include/nodemetainfo.h include/nodeproperty.h include/projectstorageids.h + include/propertymetainfo.h + include/propertycontainer.h + include/propertyparser.h include/qmldesignercorelib_global.h include/signalhandlerproperty.h include/synchronousimagecache.h include/variantproperty.h + metainfo/itemlibraryinfo.cpp + metainfo/metainfo.cpp + metainfo/metainforeader.cpp + metainfo/nodemetainfo.cpp model/abstractproperty.cpp model/abstractview.cpp model/annotation.cpp @@ -242,11 +255,15 @@ extend_qtc_test(unittest model/model.cpp model/model_p.h model/modelnode.cpp + model/propertycontainer.cpp + model/propertyparser.cpp model/nodeabstractproperty.cpp model/nodelistproperty.cpp model/nodeproperty.cpp model/signalhandlerproperty.cpp model/variantproperty.cpp + pluginmanager/widgetpluginmanager.h pluginmanager/widgetpluginmanager.cpp + pluginmanager/widgetpluginpath.h pluginmanager/widgetpluginpath.cpp projectstorage/directorypathcompressor.h projectstorage/filesysteminterface.h projectstorage/filesystem.cpp projectstorage/filesystem.h diff --git a/tests/unit/unittest/externaldependenciesmock.h b/tests/unit/unittest/externaldependenciesmock.h index 368024edc17..df11dec75f6 100644 --- a/tests/unit/unittest/externaldependenciesmock.h +++ b/tests/unit/unittest/externaldependenciesmock.h @@ -42,4 +42,5 @@ public: MOCK_METHOD(QStringList, projectModulePaths, (), (const, override)); MOCK_METHOD(bool, isQt6Project, (), (const, override)); MOCK_METHOD(QString, qtQuickVersion, (), (const, override)); + MOCK_METHOD(Utils::FilePath, resourcePath, (const QString &relativePath), (const, override)); }; diff --git a/tests/unit/unittest/listmodeleditor-test.cpp b/tests/unit/unittest/listmodeleditor-test.cpp index 6443c8a7224..b3373f1e645 100644 --- a/tests/unit/unittest/listmodeleditor-test.cpp +++ b/tests/unit/unittest/listmodeleditor-test.cpp @@ -4,6 +4,7 @@ #include "googletest.h" #include "mocklistmodeleditorview.h" +#include "projectstoragemock.h" #include #include @@ -19,6 +20,10 @@ using QmlDesigner::AbstractProperty; using QmlDesigner::AbstractView; using QmlDesigner::ListModelEditorModel; using QmlDesigner::ModelNode; +using QmlDesigner::ModuleId; +using QmlDesigner::PropertyDeclarationId; +using QmlDesigner::TypeId; +namespace Info = QmlDesigner::Storage::Info; MATCHER_P2(HasItem, name, @@ -68,10 +73,19 @@ class ListModelEditor : public testing::Test public: ListModelEditor() { + setModuleId("QtQuick", modelId_QtQuick); + setType(modelId_QtQuick, "Item", "data"); + designerModel = QmlDesigner::Model::create(projectStorageMock, "QtQuick.Item", 1, 1); + setModuleId("QtQml.Models", modelId_QtQml_Models); + setType(modelId_QtQml_Models, "ListModel", "children"); + setType(modelId_QtQml_Models, "ListElement", "children"); + componentModel = QmlDesigner::Model::create(projectStorageMock, "QtQml.Models.ListModel", 1, 1); + designerModel->attachView(&mockView); emptyListModelNode = mockView.createModelNode("QtQml.Models.ListModel", 2, 15); + setType(modelId_QtQuick, "ListView", "data"); listViewNode = mockView.createModelNode("QtQuick.ListView", 2, 15); listModelNode = mockView.createModelNode("QtQml.Models.ListModel", 2, 15); mockView.rootModelNode().defaultNodeListProperty().reparentHere(listModelNode); @@ -91,7 +105,30 @@ public: mockComponentView, mockComponentView.rootModelNode()); - ON_CALL(mockGoIntoComponent, Call(_)).WillByDefault([](ModelNode node) { return node; }); + ON_CALL(goIntoComponentMock, Call(_)).WillByDefault([](ModelNode node) { return node; }); + } + + 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) + { + static int typeIdNumber = 0; + TypeId typeId = TypeId::create(++typeIdNumber); + + static int defaultPropertyIdNumber = 0; + PropertyDeclarationId defaultPropertyId = PropertyDeclarationId::create( + ++defaultPropertyIdNumber); + + ON_CALL(projectStorageMock, typeId(Eq(moduleId), Eq(typeName), _)).WillByDefault(Return(typeId)); + ON_CALL(projectStorageMock, type(Eq(typeId))) + .WillByDefault(Return(Info::Type{defaultPropertyId, {}})); + ON_CALL(projectStorageMock, propertyName(Eq(defaultPropertyId))) + .WillByDefault(Return(defaultPeopertyName)); } using Entry = std::pair; @@ -174,23 +211,25 @@ public: } protected: - MockFunction mockGoIntoComponent; - QmlDesigner::ModelPointer designerModel{QmlDesigner::Model::create("QtQuick.Item", 1, 1)}; + NiceMock projectStorageMock; + NiceMock> goIntoComponentMock; + QmlDesigner::ModelPointer designerModel; NiceMock mockView; QmlDesigner::ListModelEditorModel model{ [&] { return mockView.createModelNode("QtQml.Models.ListModel", 2, 15); }, [&] { return mockView.createModelNode("QtQml.Models.ListElement", 2, 15); }, - mockGoIntoComponent.AsStdFunction()}; + goIntoComponentMock.AsStdFunction()}; ModelNode listViewNode; ModelNode listModelNode; ModelNode emptyListModelNode; ModelNode element1; ModelNode element2; ModelNode element3; - QmlDesigner::ModelPointer componentModel{ - QmlDesigner::Model::create("QtQml.Models.ListModel", 1, 1)}; + QmlDesigner::ModelPointer componentModel; NiceMock mockComponentView; ModelNode componentElement; + ModuleId modelId_QtQuick = ModuleId::create(1); + ModuleId modelId_QtQml_Models = ModuleId::create(2); }; TEST_F(ListModelEditor, CreatePropertyNameSet) @@ -1374,7 +1413,7 @@ TEST_F(ListModelEditor, AddFalseAsStringProperties) TEST_F(ListModelEditor, GoIntoComponentForBinding) { - EXPECT_CALL(mockGoIntoComponent, Call(Eq(listModelNode))) + EXPECT_CALL(goIntoComponentMock, Call(Eq(listModelNode))) .WillRepeatedly(Return(mockComponentView.rootModelNode())); listModelNode.setIdWithoutRefactoring("listModel"); listViewNode.bindingProperty("model").setExpression("listModel"); @@ -1386,7 +1425,7 @@ TEST_F(ListModelEditor, GoIntoComponentForBinding) TEST_F(ListModelEditor, GoIntoComponentForModelNode) { - EXPECT_CALL(mockGoIntoComponent, Call(Eq(listModelNode))) + EXPECT_CALL(goIntoComponentMock, Call(Eq(listModelNode))) .WillRepeatedly(Return(mockComponentView.rootModelNode())); listViewNode.nodeProperty("model").reparentHere(listModelNode); diff --git a/tests/unit/unittest/nodelistproperty-test.cpp b/tests/unit/unittest/nodelistproperty-test.cpp index edeeaee9c21..8abb350aa63 100644 --- a/tests/unit/unittest/nodelistproperty-test.cpp +++ b/tests/unit/unittest/nodelistproperty-test.cpp @@ -3,6 +3,7 @@ #include "abstractviewmock.h" #include "googletest.h" +#include "projectstoragemock.h" #include #include @@ -13,6 +14,10 @@ namespace { using QmlDesigner::ModelNode; +using QmlDesigner::ModuleId; +using QmlDesigner::PropertyDeclarationId; +using QmlDesigner::TypeId; +namespace Info = QmlDesigner::Storage::Info; class NodeListProperty : public testing::Test { @@ -20,6 +25,11 @@ protected: using iterator = QmlDesigner::NodeListProperty::iterator; NodeListProperty() { + ModuleId modelId_QtQuick = ModuleId::create(1); + setModuleId("QtQuick", modelId_QtQuick); + setType(modelId_QtQuick, "Item", "data"); + model = std::make_unique(projectStorageMock, "QtQuick.Item"); + model->attachView(&abstractViewMock); nodeListProperty = abstractViewMock.rootModelNode().nodeListProperty("foo"); @@ -38,6 +48,29 @@ 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) + { + static int typeIdNumber = 0; + TypeId typeId = TypeId::create(++typeIdNumber); + + static int defaultPropertyIdNumber = 0; + PropertyDeclarationId defaultPropertyId = PropertyDeclarationId::create( + ++defaultPropertyIdNumber); + + ON_CALL(projectStorageMock, typeId(Eq(moduleId), Eq(typeName), _)).WillByDefault(Return(typeId)); + ON_CALL(projectStorageMock, type(Eq(typeId))) + .WillByDefault(Return(Info::Type{defaultPropertyId, {}})); + ON_CALL(projectStorageMock, propertyName(Eq(defaultPropertyId))) + .WillByDefault(Return(defaultPeopertyName)); + } + std::vector nodes() const { return std::vector{nodeListProperty.begin(), nodeListProperty.end()}; @@ -49,7 +82,8 @@ protected: } protected: - std::unique_ptr model{std::make_unique("QtQuick.Item")}; + NiceMock projectStorageMock; + std::unique_ptr model; NiceMock abstractViewMock; QmlDesigner::NodeListProperty nodeListProperty; ModelNode node1; diff --git a/tests/unit/unittest/projectstoragemock.h b/tests/unit/unittest/projectstoragemock.h index 93cca00ca08..8a3c2efd153 100644 --- a/tests/unit/unittest/projectstoragemock.h +++ b/tests/unit/unittest/projectstoragemock.h @@ -19,7 +19,103 @@ public: (QmlDesigner::Storage::Synchronization::SynchronizationPackage package), (override)); - MOCK_METHOD(QmlDesigner::ModuleId, moduleId, (Utils::SmallStringView), (const, override)); + MOCK_METHOD(QmlDesigner::ModuleId, moduleId, (::Utils::SmallStringView), (const, override)); + + MOCK_METHOD(std::optional, + propertyDeclaration, + (QmlDesigner::PropertyDeclarationId propertyDeclarationId), + (const, override)); + + MOCK_METHOD(QmlDesigner::TypeId, + typeId, + (QmlDesigner::ModuleId moduleId, + ::Utils::SmallStringView exportedTypeName, + QmlDesigner::Storage::Version version), + (const, override)); + + MOCK_METHOD(QmlDesigner::PropertyDeclarationIds, + propertyDeclarationIds, + (QmlDesigner::TypeId typeId), + (const, override)); + MOCK_METHOD(QmlDesigner::PropertyDeclarationIds, + localPropertyDeclarationIds, + (QmlDesigner::TypeId typeId), + (const, override)); + MOCK_METHOD(QmlDesigner::PropertyDeclarationId, + propertyDeclarationId, + (QmlDesigner::TypeId typeId, ::Utils::SmallStringView propertyName), + (const, override)); + MOCK_METHOD(std::optional, + type, + (QmlDesigner::TypeId typeId), + (const, override)); + MOCK_METHOD(std::vector<::Utils::SmallString>, + signalDeclarationNames, + (QmlDesigner::TypeId typeId), + (const, override)); + MOCK_METHOD(std::vector<::Utils::SmallString>, + functionDeclarationNames, + (QmlDesigner::TypeId typeId), + (const, override)); + MOCK_METHOD(std::optional<::Utils::SmallString>, + propertyName, + (QmlDesigner::PropertyDeclarationId propertyDeclarationId), + (const, override)); + MOCK_METHOD(QmlDesigner::TypeIds, prototypeAndSelfIds, (QmlDesigner::TypeId type), (const, override)); + MOCK_METHOD(QmlDesigner::TypeIds, prototypeIds, (QmlDesigner::TypeId type), (const, override)); + MOCK_METHOD(bool, isBasedOn, (QmlDesigner::TypeId typeId), (const, override)); + MOCK_METHOD(bool, isBasedOn, (QmlDesigner::TypeId typeId, QmlDesigner::TypeId), (const, override)); + MOCK_METHOD(bool, + isBasedOn, + (QmlDesigner::TypeId typeId, QmlDesigner::TypeId, QmlDesigner::TypeId), + (const, override)); + MOCK_METHOD(bool, + isBasedOn, + (QmlDesigner::TypeId typeId, QmlDesigner::TypeId, QmlDesigner::TypeId, QmlDesigner::TypeId), + (const, override)); + MOCK_METHOD(bool, + isBasedOn, + (QmlDesigner::TypeId typeId, + QmlDesigner::TypeId, + QmlDesigner::TypeId, + QmlDesigner::TypeId, + QmlDesigner::TypeId), + (const, override)); + MOCK_METHOD(bool, + isBasedOn, + (QmlDesigner::TypeId typeId, + QmlDesigner::TypeId, + QmlDesigner::TypeId, + QmlDesigner::TypeId, + QmlDesigner::TypeId, + QmlDesigner::TypeId), + (const, override)); + MOCK_METHOD(bool, + isBasedOn, + (QmlDesigner::TypeId typeId, + QmlDesigner::TypeId, + QmlDesigner::TypeId, + QmlDesigner::TypeId, + QmlDesigner::TypeId, + QmlDesigner::TypeId, + QmlDesigner::TypeId), + (const, override)); + MOCK_METHOD(bool, + isBasedOn, + (QmlDesigner::TypeId typeId, + QmlDesigner::TypeId, + QmlDesigner::TypeId, + QmlDesigner::TypeId, + QmlDesigner::TypeId, + QmlDesigner::TypeId, + QmlDesigner::TypeId, + QmlDesigner::TypeId), + (const, override)); + + MOCK_METHOD(const QmlDesigner::Storage::Info::CommonTypeCache &, + commonTypeCache, + (), + (const, override)); MOCK_METHOD(QmlDesigner::FileStatus, fetchFileStatus, @@ -38,21 +134,21 @@ public: MOCK_METHOD(QmlDesigner::SourceContextId, fetchSourceContextId, - (Utils::SmallStringView SourceContextPath), + (::Utils::SmallStringView SourceContextPath), ()); MOCK_METHOD(QmlDesigner::SourceId, fetchSourceId, - (QmlDesigner::SourceContextId SourceContextId, Utils::SmallStringView sourceName), + (QmlDesigner::SourceContextId SourceContextId, ::Utils::SmallStringView sourceName), ()); MOCK_METHOD(QmlDesigner::SourceContextId, fetchSourceContextIdUnguarded, - (Utils::SmallStringView SourceContextPath), + (::Utils::SmallStringView SourceContextPath), ()); MOCK_METHOD(QmlDesigner::SourceId, fetchSourceIdUnguarded, - (QmlDesigner::SourceContextId SourceContextId, Utils::SmallStringView sourceName), + (QmlDesigner::SourceContextId SourceContextId, ::Utils::SmallStringView sourceName), ()); - MOCK_METHOD(Utils::PathString, + MOCK_METHOD(::Utils::PathString, fetchSourceContextPath, (QmlDesigner::SourceContextId sourceContextId)); MOCK_METHOD(QmlDesigner::Cache::SourceNameAndSourceContextId, From 1e84065123436e2ca2857cc3205d0f359cb3a875 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Thu, 20 Apr 2023 09:43:56 +0200 Subject: [PATCH 135/192] QmlDesigner: Improve major version conversion It handles now the case that it cannot covert to integer and it is not anymore allocating memory. Change-Id: Ie00a7abb5d3e8c6307c5ae6528dca29ebc47ba45 Reviewed-by: Tim Jenssen Reviewed-by: Qt CI Bot Reviewed-by: Miikka Heikkinen --- .../qmldesigner/designercore/model/import.cpp | 31 ++++++++++++++----- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/src/plugins/qmldesigner/designercore/model/import.cpp b/src/plugins/qmldesigner/designercore/model/import.cpp index b9e7a7a9e24..ea74867f159 100644 --- a/src/plugins/qmldesigner/designercore/model/import.cpp +++ b/src/plugins/qmldesigner/designercore/model/import.cpp @@ -5,6 +5,8 @@ #include +#include + namespace QmlDesigner { Import::Import() = default; @@ -82,19 +84,34 @@ int Import::minorVersion() const int Import::majorFromVersion(const QString &version) { - if (version.isEmpty()) + auto found = std::find(version.begin(), version.end(), u'.'); + if (found == version.end()) return -1; - return version.split('.').first().toInt(); + + QStringView majorVersionToken{version.begin(), found}; + bool canConvert = false; + int majorVersion = majorVersionToken.toInt(&canConvert); + + if (canConvert) + return majorVersion; + + return -1; } int Import::minorFromVersion(const QString &version) { - if (version.isEmpty()) + auto found = std::find(version.begin(), version.end(), u'.'); + if (found == version.end()) return -1; - const QStringList parts = version.split('.'); - if (parts.size() < 2) - return -1; - return parts[1].toInt(); + + QStringView minorVersionToken{std::next(found), version.end()}; + bool canConvert = false; + int minorVersion = minorVersionToken.toInt(&canConvert); + + if (canConvert) + return minorVersion; + + return -1; } size_t qHash(const Import &import) From 4be3f6b5e90509d2580a7e371a861f074461a07f Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Thu, 20 Apr 2023 10:28:41 +0200 Subject: [PATCH 136/192] QmlDesigner: Improve version handling Consolidate version handling in imports. It has drawbacks if we copy code to different places in our code base. Having tests for the version conversion has advantages too. The invalid version has not anymore using -1 but the maximum integer as indicator because an empty version is higher than any other version. Change-Id: I8608b9509660f4253e8e5ba7397d964defe35e51 Reviewed-by: Thomas Hartmann Reviewed-by: Miikka Heikkinen Reviewed-by: Tim Jenssen --- .../itemlibrary/itemlibrarymodel.cpp | 24 +- .../qmldesigner/designercore/include/import.h | 30 ++ .../designercore/model/abstractview.cpp | 36 +- .../qmldesigner/designercore/model/import.cpp | 20 ++ .../qmldesigner/designercore/model/model.cpp | 34 +- .../designercore/model/texttomodelmerger.cpp | 26 +- tests/unit/unittest/CMakeLists.txt | 1 + .../unit/unittest/google-using-declarations.h | 1 + tests/unit/unittest/import-test.cpp | 340 ++++++++++++++++++ 9 files changed, 432 insertions(+), 80 deletions(-) create mode 100644 tests/unit/unittest/import-test.cpp diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp index 18430ca0179..d6b55cd052a 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp @@ -304,28 +304,6 @@ Import ItemLibraryModel::entryToImport(const ItemLibraryEntry &entry) } -// Returns true if first import version is higher or equal to second import version -static bool compareVersions(const QString &version1, const QString &version2) -{ - if (version2.isEmpty() || version1 == version2) - return true; - const QStringList version1List = version1.split(QLatin1Char('.')); - const QStringList version2List = version2.split(QLatin1Char('.')); - if (version1List.count() == 2 && version2List.count() == 2) { - int major1 = version1List.constFirst().toInt(); - int major2 = version2List.constFirst().toInt(); - if (major1 > major2) { - return true; - } else if (major1 == major2) { - int minor1 = version1List.constLast().toInt(); - int minor2 = version2List.constLast().toInt(); - if (minor1 >= minor2) - return true; - } - } - return false; -} - void ItemLibraryModel::update(ItemLibraryInfo *itemLibraryInfo, Model *model) { if (!model) @@ -363,7 +341,7 @@ void ItemLibraryModel::update(ItemLibraryInfo *itemLibraryInfo, Model *model) addNew = false; // add only 1 Quick3DAssets import section } else if (oldImport && oldImport->importEntry().url() == import.url()) { // Retain the higher version if multiples exist - if (compareVersions(oldImport->importEntry().version(), import.version())) + if (oldImport->importEntry().toVersion() >= import.toVersion() || import.hasVersion()) addNew = false; else delete oldImport; diff --git a/src/plugins/qmldesigner/designercore/include/import.h b/src/plugins/qmldesigner/designercore/include/import.h index 872721c9a4a..0e366527f1f 100644 --- a/src/plugins/qmldesigner/designercore/include/import.h +++ b/src/plugins/qmldesigner/designercore/include/import.h @@ -11,8 +11,37 @@ #include "qmldesignercorelib_global.h" +#include + namespace QmlDesigner { +class Version +{ +public: + friend bool operator==(Version first, Version second) + { + return first.major == second.major && first.minor == second.minor; + } + + friend bool operator<(Version first, Version second) + { + return std::tie(first.major, first.minor) < std::tie(second.major, second.minor); + } + + friend bool operator>(Version first, Version second) { return second < first; } + friend bool operator<=(Version first, Version second) { return !(second < first); } + friend bool operator>=(Version first, Version second) { return !(first < second); } + + bool isEmpty() const + { + return major == std::numeric_limits::max() || minor == std::numeric_limits::max(); + } + +public: + int major = std::numeric_limits::max(); + int minor = std::numeric_limits::max(); +}; + class QMLDESIGNERCORE_EXPORT Import { public: @@ -41,6 +70,7 @@ public: int majorVersion() const; int minorVersion() const; + Version toVersion() const; static int majorFromVersion(const QString &version); static int minorFromVersion(const QString &version); diff --git a/src/plugins/qmldesigner/designercore/model/abstractview.cpp b/src/plugins/qmldesigner/designercore/model/abstractview.cpp index ec6ddb6aa43..07692f4a10c 100644 --- a/src/plugins/qmldesigner/designercore/model/abstractview.cpp +++ b/src/plugins/qmldesigner/designercore/model/abstractview.cpp @@ -893,32 +893,28 @@ QmlTimeline AbstractView::currentTimeline() const static int getMinorVersionFromImport(const Model *model) { - const Imports imports = model->imports(); - for (const Import &import : imports) { - if (import.isLibraryImport() && import.url() == "QtQuick") { - const QString versionString = import.version(); - if (versionString.contains(".")) { - const QString minorVersionString = versionString.split(".").constLast(); - return minorVersionString.toInt(); - } - } - } + const Imports &imports = model->imports(); + + auto found = std::find_if(imports.begin(), imports.end(), [](const auto &import) { + return import.url() == "QtQuick"; + }); + + if (found != imports.end()) + return found->minorVersion(); return -1; } static int getMajorVersionFromImport(const Model *model) { - const Imports imports = model->imports(); - for (const Import &import : imports) { - if (import.isLibraryImport() && import.url() == QStringLiteral("QtQuick")) { - const QString versionString = import.version(); - if (versionString.contains(QStringLiteral("."))) { - const QString majorVersionString = versionString.split(QStringLiteral(".")).constFirst(); - return majorVersionString.toInt(); - } - } - } + const Imports &imports = model->imports(); + + auto found = std::find_if(imports.begin(), imports.end(), [](const auto &import) { + return import.url() == "QtQuick"; + }); + + if (found != imports.end()) + return found->majorVersion(); return -1; } diff --git a/src/plugins/qmldesigner/designercore/model/import.cpp b/src/plugins/qmldesigner/designercore/model/import.cpp index ea74867f159..7d8e52c93e1 100644 --- a/src/plugins/qmldesigner/designercore/model/import.cpp +++ b/src/plugins/qmldesigner/designercore/model/import.cpp @@ -82,6 +82,26 @@ int Import::minorVersion() const return minorFromVersion(m_version); } +Version Import::toVersion() const +{ + auto found = std::find(m_version.begin(), m_version.end(), u'.'); + if (found == m_version.end()) + return {}; + + QStringView majorVersionToken{m_version.begin(), found}; + bool canConvertMajor = false; + int majorVersion = majorVersionToken.toInt(&canConvertMajor); + + QStringView minorVersionToken{std::next(found), m_version.end()}; + bool canConvertMinor = false; + int minorVersion = minorVersionToken.toInt(&canConvertMinor); + + if (canConvertMajor && canConvertMinor) + return {majorVersion, minorVersion}; + + return {}; +} + int Import::majorFromVersion(const QString &version) { auto found = std::find(version.begin(), version.end(), u'.'); diff --git a/src/plugins/qmldesigner/designercore/model/model.cpp b/src/plugins/qmldesigner/designercore/model/model.cpp index be3aa9d3d23..f9d23ba4aaf 100644 --- a/src/plugins/qmldesigner/designercore/model/model.cpp +++ b/src/plugins/qmldesigner/designercore/model/model.cpp @@ -1462,37 +1462,19 @@ void Model::setUsedImports(Imports usedImports) } } -static bool compareVersions(const QString &version1, const QString &version2, bool allowHigherVersion) +static bool compareVersions(const Import &import1, const Import &import2, bool allowHigherVersion) { + auto version1 = import1.toVersion(); + auto version2 = import2.toVersion(); + if (version2.isEmpty()) return true; if (version1 == version2) return true; if (!allowHigherVersion) return false; - QStringList version1List = version1.split(QLatin1Char('.')); - QStringList version2List = version2.split(QLatin1Char('.')); - if (version1List.count() == 2 && version2List.count() == 2) { - bool ok; - int major1 = version1List.constFirst().toInt(&ok); - if (!ok) - return false; - int major2 = version2List.constFirst().toInt(&ok); - if (!ok) - return false; - if (major1 >= major2) { - int minor1 = version1List.constLast().toInt(&ok); - if (!ok) - return false; - int minor2 = version2List.constLast().toInt(&ok); - if (!ok) - return false; - if (minor1 >= minor2) - return true; - } - } - return false; + return version1 >= version2; } bool Model::hasImport(const Import &import, bool ignoreAlias, bool allowHigherVersion) const @@ -1510,7 +1492,7 @@ bool Model::hasImport(const Import &import, bool ignoreAlias, bool allowHigherVe } if (existingImport.isLibraryImport() && import.isLibraryImport()) { if (existingImport.url() == import.url() - && compareVersions(existingImport.version(), import.version(), allowHigherVersion)) { + && compareVersions(existingImport, import, allowHigherVersion)) { return true; } } @@ -1684,7 +1666,7 @@ bool Model::isImportPossible(const Import &import, bool ignoreAlias, bool allowH if (possibleImport.isLibraryImport() && import.isLibraryImport()) { if (possibleImport.url() == import.url() - && compareVersions(possibleImport.version(), import.version(), allowHigherVersion)) { + && compareVersions(possibleImport, import, allowHigherVersion)) { return true; } } @@ -1719,7 +1701,7 @@ Import Model::highestPossibleImport(const QString &importPath) for (const Import &import : possibleImports()) { if (import.url() == importPath) { - if (candidate.isEmpty() || compareVersions(import.version(), candidate.version(), true)) + if (candidate.isEmpty() || compareVersions(import, candidate, true)) candidate = import; } } diff --git a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp index 7db323930f2..ac89c54acd6 100644 --- a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp +++ b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp @@ -19,6 +19,7 @@ #include "signalhandlerproperty.h" #include "variantproperty.h" #include +#include #include #include @@ -61,12 +62,15 @@ bool isSupportedAttachedProperties(const QString &propertyName) || propertyName.startsWith(QLatin1String("InsightCategory.")); } -QStringList supportedVersionsList() +bool isSupportedVersion(QmlDesigner::Version version) { - static const QStringList list = {"2.0", "2.1", "2.2", "2.3", "2.4", "2.5", "2.6", "2.7", - "2.8", "2.9", "2.10", "2.11", "2.12", "2.13", "2.14", "2.15", - "6.0", "6.1", "6.2", "6.3", "6.4", "6.5"}; - return list; + if (version.major == 2) + return version.minor <= 15; + + if (version.major == 6) + return version.minor <= 5; + + return false; } QStringList globalQtEnums() @@ -106,9 +110,10 @@ QStringList knownEnumScopes() return list; } -bool supportedQtQuickVersion(const QString &version) +bool supportedQtQuickVersion(const QmlDesigner::Import &import) { - return version.isEmpty() || supportedVersionsList().contains(version); + auto version = import.toVersion(); + return version.isEmpty() || isSupportedVersion(version); } QString stripQuotes(const QString &str) @@ -120,7 +125,7 @@ QString stripQuotes(const QString &str) return str; } -inline QString deEscape(const QString &value) +QString deEscape(const QString &value) { QString result = value; @@ -2165,13 +2170,12 @@ void TextToModelMerger::collectImportErrors(QList *errors) bool hasQtQuick = false; for (const QmlDesigner::Import &import : m_rewriterView->model()->imports()) { if (import.isLibraryImport() && import.url() == QStringLiteral("QtQuick")) { - - if (supportedQtQuickVersion(import.version())) { + if (supportedQtQuickVersion(import)) { hasQtQuick = true; auto &externalDependencies = m_rewriterView->externalDependencies(); if (externalDependencies.hasStartupTarget()) { - const bool qt6import = import.version().startsWith("6"); + const bool qt6import = !import.hasVersion() || import.majorVersion() == 6; if (!externalDependencies.isQt6Import() && (m_hasVersionlessImport || qt6import)) { const QmlJS::DiagnosticMessage diagnosticMessage( diff --git a/tests/unit/unittest/CMakeLists.txt b/tests/unit/unittest/CMakeLists.txt index 7498aa52020..6e694f705c8 100644 --- a/tests/unit/unittest/CMakeLists.txt +++ b/tests/unit/unittest/CMakeLists.txt @@ -53,6 +53,7 @@ add_qtc_test(unittest GTEST gtest-qt-printing.cpp gtest-qt-printing.h gtest-std-printing.h lastchangedrowid-test.cpp + import-test.cpp matchingtext-test.cpp mockfutureinterface.h mockmutex.h diff --git a/tests/unit/unittest/google-using-declarations.h b/tests/unit/unittest/google-using-declarations.h index 85fc82282f6..6b2cd4721dd 100644 --- a/tests/unit/unittest/google-using-declarations.h +++ b/tests/unit/unittest/google-using-declarations.h @@ -24,6 +24,7 @@ using testing::EndsWith; using testing::Eq; using testing::Exactly; using testing::Field; +using testing::FieldsAre; using testing::Ge; using testing::Gt; using testing::HasSubstr; diff --git a/tests/unit/unittest/import-test.cpp b/tests/unit/unittest/import-test.cpp new file mode 100644 index 00000000000..e4adfd7e5bb --- /dev/null +++ b/tests/unit/unittest/import-test.cpp @@ -0,0 +1,340 @@ +// 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 "googletest.h" + +#include + +namespace { + +TEST(Import, ParseVersion) +{ + auto import = QmlDesigner::Import::createLibraryImport("Qml", "6.5"); + + auto version = import.toVersion(); + + ASSERT_THAT(version, FieldsAre(6, 5)); +} + +TEST(Import, ParseMajorVersion) +{ + auto import = QmlDesigner::Import::createLibraryImport("Qml", "6.5"); + + auto version = import.majorVersion(); + + ASSERT_THAT(version, 6); +} + +TEST(Import, MajorVersionIsInvalidForEmptyString) +{ + auto import = QmlDesigner::Import::createLibraryImport("Qml"); + + auto version = import.majorVersion(); + + ASSERT_THAT(version, -1); +} + +TEST(Import, MajorVersionIsInvalidForBrokenString) +{ + auto import = QmlDesigner::Import::createLibraryImport("Qml", "6,5"); + + auto version = import.majorVersion(); + + ASSERT_THAT(version, -1); +} + +TEST(Import, ParseMinorVersion) +{ + auto import = QmlDesigner::Import::createLibraryImport("Qml", "6.5"); + + auto version = import.minorVersion(); + + ASSERT_THAT(version, 5); +} + +TEST(Import, MinorVersionIsInvalidForEmptyString) +{ + auto import = QmlDesigner::Import::createLibraryImport("Qml"); + + auto version = import.minorVersion(); + + ASSERT_THAT(version, -1); +} + +TEST(Import, MinorVersionIsInvalidForBrokenString) +{ + auto import = QmlDesigner::Import::createLibraryImport("Qml", "6,5"); + + auto version = import.minorVersion(); + + ASSERT_THAT(version, -1); +} + +TEST(Import, VersionIsNotEmpty) +{ + auto import = QmlDesigner::Import::createLibraryImport("Qml", "6.5"); + + auto version = import.toVersion(); + + ASSERT_FALSE(version.isEmpty()); +} + +TEST(Import, BrokenVersionStringIsEmptyVersion) +{ + auto import = QmlDesigner::Import::createLibraryImport("Qml", "6"); + + auto version = import.toVersion(); + + ASSERT_TRUE(version.isEmpty()); +} + +TEST(Import, EmptyVersionStringIsEmptyVersion) +{ + auto import = QmlDesigner::Import::createLibraryImport("Qml"); + + auto version = import.toVersion(); + + ASSERT_TRUE(version.isEmpty()); +} + +TEST(Import, SameVersionsAreEqual) +{ + QmlDesigner::Version version1{6, 5}; + QmlDesigner::Version version2{6, 5}; + + bool isEqual = version1 == version2; + + ASSERT_TRUE(isEqual); +} + +TEST(Import, InvalidVersionsAreEqual) +{ + QmlDesigner::Version version1; + QmlDesigner::Version version2; + + bool isEqual = version1 == version2; + + ASSERT_TRUE(isEqual); +} + +TEST(Import, DifferentMinorVersionsAreNotEqual) +{ + QmlDesigner::Version version1{6, 4}; + QmlDesigner::Version version2{6, 5}; + + bool isEqual = version1 == version2; + + ASSERT_FALSE(isEqual); +} + +TEST(Import, DifferentMajorVersionsAreNotEqual) +{ + QmlDesigner::Version version1{5, 5}; + QmlDesigner::Version version2{6, 5}; + + bool isEqual = version1 == version2; + + ASSERT_FALSE(isEqual); +} + +TEST(Import, LessMinorVersionsAreLess) +{ + QmlDesigner::Version version1{6, 4}; + QmlDesigner::Version version2{6, 5}; + + bool isLess = version1 < version2; + + ASSERT_TRUE(isLess); +} + +TEST(Import, LessMajorVersionsAreLess) +{ + QmlDesigner::Version version1{5, 15}; + QmlDesigner::Version version2{6, 5}; + + bool isLess = version1 < version2; + + ASSERT_TRUE(isLess); +} + +TEST(Import, SameVersionsAreNotLess) +{ + QmlDesigner::Version version1{6, 5}; + QmlDesigner::Version version2{6, 5}; + + bool isLess = version1 < version2; + + ASSERT_FALSE(isLess); +} + +TEST(Import, EmptyVersionIsNotLess) +{ + QmlDesigner::Version version1; + QmlDesigner::Version version2{6, 5}; + + bool isLess = version1 < version2; + + ASSERT_FALSE(isLess); +} + +TEST(Import, NonEmptyVersionIsIsLessThanEmptyVersion) +{ + QmlDesigner::Version version1{6, 5}; + QmlDesigner::Version version2; + + bool isLess = version1 < version2; + + ASSERT_TRUE(isLess); +} + +TEST(Import, GreaterMinorVersionsAreGreater) +{ + QmlDesigner::Version version1{6, 6}; + QmlDesigner::Version version2{6, 5}; + + bool isGreater = version1 > version2; + + ASSERT_TRUE(isGreater); +} + +TEST(Import, GreaterMajorVersionsAreGreater) +{ + QmlDesigner::Version version1{6, 5}; + QmlDesigner::Version version2{5, 15}; + + bool isGreater = version1 > version2; + + ASSERT_TRUE(isGreater); +} + +TEST(Import, SameVersionsAreNotGreater) +{ + QmlDesigner::Version version1{6, 5}; + QmlDesigner::Version version2{6, 5}; + + bool isGreater = version1 > version2; + + ASSERT_FALSE(isGreater); +} + +TEST(Import, EmptyVersionIsGreater) +{ + QmlDesigner::Version version1; + QmlDesigner::Version version2{6, 5}; + + bool isGreater = version1 > version2; + + ASSERT_TRUE(isGreater); +} + +TEST(Import, NonEmptyVersionIsIsNotGreaterThanEmptyVersion) +{ + QmlDesigner::Version version1{6, 5}; + QmlDesigner::Version version2; + + bool isGreater = version1 > version2; + + ASSERT_FALSE(isGreater); +} + +TEST(Import, LessEqualMinorVersionsAreLessEqual) +{ + QmlDesigner::Version version1{6, 4}; + QmlDesigner::Version version2{6, 5}; + + bool isLessEqual = version1 <= version2; + + ASSERT_TRUE(isLessEqual); +} + +TEST(Import, LessqualMajorVersionsAreLessqual) +{ + QmlDesigner::Version version1{5, 15}; + QmlDesigner::Version version2{6, 5}; + + bool isLessEqual = version1 <= version2; + + ASSERT_TRUE(isLessEqual); +} + +TEST(Import, SameVersionsAreLessqual) +{ + QmlDesigner::Version version1{6, 5}; + QmlDesigner::Version version2{6, 5}; + + bool isLessEqual = version1 <= version2; + + ASSERT_TRUE(isLessEqual); +} + +TEST(Import, EmptyVersionIsNotLessqual) +{ + QmlDesigner::Version version1; + QmlDesigner::Version version2{6, 5}; + + bool isLessEqual = version1 <= version2; + + ASSERT_FALSE(isLessEqual); +} + +TEST(Import, NonEmptyVersionIsIsLessqualThanEmptyVersion) +{ + QmlDesigner::Version version1{6, 5}; + QmlDesigner::Version version2; + + bool isLessEqual = version1 <= version2; + + ASSERT_TRUE(isLessEqual); +} + +TEST(Import, GreaterEqualMinorVersionsAreGreaterEqual) +{ + QmlDesigner::Version version1{6, 6}; + QmlDesigner::Version version2{6, 5}; + + bool isGreaterEqual = version1 >= version2; + + ASSERT_TRUE(isGreaterEqual); +} + +TEST(Import, GreaterEqualMajorVersionsAreGreaterEqual) +{ + QmlDesigner::Version version1{6, 5}; + QmlDesigner::Version version2{5, 15}; + + bool isGreaterEqual = version1 >= version2; + + ASSERT_TRUE(isGreaterEqual); +} + +TEST(Import, SameVersionsAreGreaterEqual) +{ + QmlDesigner::Version version1{6, 5}; + QmlDesigner::Version version2{6, 5}; + + bool isGreaterEqual = version1 >= version2; + + ASSERT_TRUE(isGreaterEqual); +} + +TEST(Import, EmptyVersionIsGreaterEqual) +{ + QmlDesigner::Version version1; + QmlDesigner::Version version2{6, 5}; + + bool isGreaterEqual = version1 >= version2; + + ASSERT_TRUE(isGreaterEqual); +} + +TEST(Import, NonEmptyVersionIsIsNotGreaterEqualThanEmptyVersion) +{ + QmlDesigner::Version version1{6, 5}; + QmlDesigner::Version version2; + + bool isGreaterEqual = version1 >= version2; + + ASSERT_FALSE(isGreaterEqual); +} + +} // namespace From b91279e8d0692574b50a84c187542fdd80479555 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Fri, 21 Apr 2023 18:39:18 +0200 Subject: [PATCH 137/192] QmlDesigner: Remove constructor implementation in import Change-Id: I4cf5207ac0fd5fd17466f08367061f43c9ed9196 Reviewed-by: Tim Jenssen --- src/plugins/qmldesigner/designercore/include/import.h | 2 +- src/plugins/qmldesigner/designercore/model/import.cpp | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/plugins/qmldesigner/designercore/include/import.h b/src/plugins/qmldesigner/designercore/include/import.h index 0e366527f1f..f0a5b8e7700 100644 --- a/src/plugins/qmldesigner/designercore/include/import.h +++ b/src/plugins/qmldesigner/designercore/include/import.h @@ -45,7 +45,7 @@ public: class QMLDESIGNERCORE_EXPORT Import { public: - Import(); + Import() = default; static Import createLibraryImport(const QString &url, const QString &version = QString(), const QString &alias = QString(), const QStringList &importPaths = QStringList()); static Import createFileImport(const QString &file, const QString &version = QString(), const QString &alias = QString(), const QStringList &importPaths = QStringList()); diff --git a/src/plugins/qmldesigner/designercore/model/import.cpp b/src/plugins/qmldesigner/designercore/model/import.cpp index 7d8e52c93e1..54b18810437 100644 --- a/src/plugins/qmldesigner/designercore/model/import.cpp +++ b/src/plugins/qmldesigner/designercore/model/import.cpp @@ -9,8 +9,6 @@ namespace QmlDesigner { -Import::Import() = default; - Import Import::createLibraryImport(const QString &url, const QString &version, const QString &alias, const QStringList &importPaths) { return Import(url, QString(), version, alias, importPaths); From 4b26269131b646b1d137460910bb9aaa04f7fe6a Mon Sep 17 00:00:00 2001 From: Tim Jenssen Date: Mon, 8 May 2023 13:55:46 +0200 Subject: [PATCH 138/192] QmlDesigner: ModelNode model can be null Change-Id: Ie73bc1346805cf6137edeea5550d0ba5288ecdb4 Reviewed-by: Tim Jenssen --- src/plugins/qmldesigner/designercore/model/modelnode.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/plugins/qmldesigner/designercore/model/modelnode.cpp b/src/plugins/qmldesigner/designercore/model/modelnode.cpp index bbf2c1630ee..fd3c59fa1f1 100644 --- a/src/plugins/qmldesigner/designercore/model/modelnode.cpp +++ b/src/plugins/qmldesigner/designercore/model/modelnode.cpp @@ -61,7 +61,6 @@ ModelNode::ModelNode(const InternalNodePointer &internalNode, Model *model, cons m_model(model), m_view(const_cast(view)) { - Q_ASSERT(m_model); } ModelNode::ModelNode(const ModelNode &modelNode, AbstractView *view) From ee903f806c7051a7e74c12efeae98869df42d4d4 Mon Sep 17 00:00:00 2001 From: Pranta Dastider Date: Mon, 8 May 2023 14:37:14 +0200 Subject: [PATCH 139/192] QmlDesigner: Update broken link in the doc There was a broken link in the doc. This patch fixes the issue. Fixes: QDS-9842 Change-Id: I39f8ad913727d2eec0ce717b7e11a9af93bae05d Reviewed-by: Mats Honkamaa --- doc/qtdesignstudio/src/qtdesignstudio-toc.qdoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/qtdesignstudio/src/qtdesignstudio-toc.qdoc b/doc/qtdesignstudio/src/qtdesignstudio-toc.qdoc index 1b9e906c7f6..6dfdadfcf0d 100644 --- a/doc/qtdesignstudio/src/qtdesignstudio-toc.qdoc +++ b/doc/qtdesignstudio/src/qtdesignstudio-toc.qdoc @@ -162,7 +162,7 @@ \list \li \l{Setting Up Qt Bridge for Figma} \li \l{Using Qt Bridge for Figma} - \li \l{Using Figma Quick Control Template in Qt Design Studio} + \li \l{Using Figma Quick Control Template Components in Qt Design Studio} \endlist \endlist \li \l {Exporting 3D Assets} From ed6008c1c25282e6d5e9b35cf0b18a04ed8e3b14 Mon Sep 17 00:00:00 2001 From: Tim Jenssen Date: Mon, 8 May 2023 16:02:40 +0200 Subject: [PATCH 140/192] QmlDesigner: fix warning Change-Id: I94d99489ff8f5b33df415bdb51fe8171405e744e Reviewed-by: Marco Bubke --- src/plugins/qmldesigner/designercore/metainfo/metainfo.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/qmldesigner/designercore/metainfo/metainfo.cpp b/src/plugins/qmldesigner/designercore/metainfo/metainfo.cpp index a86a597ea0b..53359fb2440 100644 --- a/src/plugins/qmldesigner/designercore/metainfo/metainfo.cpp +++ b/src/plugins/qmldesigner/designercore/metainfo/metainfo.cpp @@ -110,7 +110,7 @@ void MetaInfoPrivate::parseItemLibraryDescriptions(const ExternalDependenciesInt Internal::MetaInfoReader reader(*m_q); try { reader.readMetaInfoFile(plugin->metaInfo()); - } catch (const InvalidMetaInfoException &e) { + } catch ([[maybe_unused]] const InvalidMetaInfoException &e) { #ifndef UNIT_TESTS qWarning() << e.description(); const QString errorMessage = plugin->metaInfo() + QLatin1Char('\n') + QLatin1Char('\n') From 1d183e4de7b7256749f5d3138ab15c948c936118 Mon Sep 17 00:00:00 2001 From: Tim Jenssen Date: Mon, 8 May 2023 16:04:52 +0200 Subject: [PATCH 141/192] QmlDesigner: use GTEST_INTERNAL_HAS_STRING_VIEW for str compare constData() was a workaround for the tests Task-number: QDS-9847 Change-Id: I23fcca794ab50101ae70034079af46303c98c648 Reviewed-by: Marco Bubke --- src/libs/utils/smallstring.h | 7 +------ tests/unit/unittest/CMakeLists.txt | 1 + tests/unit/unittest/smallstring-test.cpp | 20 ++++++++++---------- 3 files changed, 12 insertions(+), 16 deletions(-) diff --git a/src/libs/utils/smallstring.h b/src/libs/utils/smallstring.h index 90118a017ae..f4cd6b749f4 100644 --- a/src/libs/utils/smallstring.h +++ b/src/libs/utils/smallstring.h @@ -278,11 +278,6 @@ public: return Q_LIKELY(isShortString()) ? m_data.shortString : m_data.reference.pointer; } - const char *constData() const noexcept - { - return data(); - } - iterator begin() noexcept { return data(); @@ -315,7 +310,7 @@ public: const_iterator begin() const noexcept { - return constData(); + return data(); } const_iterator end() const noexcept diff --git a/tests/unit/unittest/CMakeLists.txt b/tests/unit/unittest/CMakeLists.txt index 6e694f705c8..5d53959a7b5 100644 --- a/tests/unit/unittest/CMakeLists.txt +++ b/tests/unit/unittest/CMakeLists.txt @@ -29,6 +29,7 @@ add_qtc_test(unittest GTEST Qt6Core5Compat QmlJS Sqlite SqliteC Googletest DEFINES + GTEST_INTERNAL_HAS_STRING_VIEW QT_NO_CAST_TO_ASCII QT_RESTRICTED_CAST_FROM_ASCII UNIT_TESTS diff --git a/tests/unit/unittest/smallstring-test.cpp b/tests/unit/unittest/smallstring-test.cpp index 2f12e4413a5..a77e0bdc2d4 100644 --- a/tests/unit/unittest/smallstring-test.cpp +++ b/tests/unit/unittest/smallstring-test.cpp @@ -123,7 +123,7 @@ TEST(SmallString, MaximumShortSmallString) { SmallString maximumShortText("very very very very short text", 30); - ASSERT_THAT(maximumShortText.constData(), StrEq("very very very very short text")); + ASSERT_THAT(maximumShortText, StrEq("very very very very short text")); } TEST(SmallString, LongConstExpressionSmallStringIsReference) @@ -219,21 +219,21 @@ TEST(SmallString, SmallStringLiteralShortSmallStringDataAccess) { SmallStringLiteral literalText("very very very very very very very very very very very long string"); - ASSERT_THAT(literalText.data(), StrEq("very very very very very very very very very very very long string")); + ASSERT_THAT(literalText, StrEq("very very very very very very very very very very very long string")); } TEST(SmallString, SmallStringLiteralLongSmallStringDataAccess) { SmallStringLiteral literalText("short string"); - ASSERT_THAT(literalText.data(), StrEq("short string")); + ASSERT_THAT(literalText, StrEq("short string")); } TEST(SmallString, ReferenceDataAccess) { SmallString literalText("short string"); - ASSERT_THAT(literalText.constData(), StrEq("short string")); + ASSERT_THAT(literalText, StrEq("short string")); } TEST(SmallString, ShortDataAccess) @@ -241,7 +241,7 @@ TEST(SmallString, ShortDataAccess) const char *shortCString = "short string"; auto shortText = SmallString::fromUtf8(shortCString); - ASSERT_THAT(shortText.constData(), StrEq("short string")); + ASSERT_THAT(shortText, StrEq("short string")); } TEST(SmallString, LongDataAccess) @@ -249,7 +249,7 @@ TEST(SmallString, LongDataAccess) const char *longCString = "very very very very very very very very very very very long string"; auto longText = SmallString::fromUtf8(longCString); - ASSERT_THAT(longText.constData(), StrEq(longCString)); + ASSERT_THAT(longText, StrEq(longCString)); } TEST(SmallString, LongSmallStringHasShortSmallStringSizeZero) @@ -962,8 +962,8 @@ TEST(SmallString, EqualCStringArrayOperator) TEST(SmallString, EqualCStringPointerOperator) { - ASSERT_TRUE(SmallString("text") == SmallString("text").data()); - ASSERT_FALSE(SmallString("text") == SmallString("text2").data()); + ASSERT_TRUE(SmallString("text") == std::string("text").data()); + ASSERT_FALSE(SmallString("text") == std::string("text2").data()); } TEST(SmallString, EqualSmallStringViewOperator) @@ -992,8 +992,8 @@ TEST(SmallString, UnequalCStringArrayOperator) TEST(SmallString, UnequalCStringPointerOperator) { - ASSERT_FALSE(SmallString("text") != SmallString("text").data()); - ASSERT_TRUE(SmallString("text") != SmallString("text2").data()); + ASSERT_FALSE(SmallString("text") != std::string("text").data()); + ASSERT_TRUE(SmallString("text") != std::string("text2").data()); } TEST(SmallString, UnequalSmallStringViewArrayOperator) From a946eebd43f3ee968e9c3402e5287b49c3360874 Mon Sep 17 00:00:00 2001 From: Tim Jenssen Date: Mon, 8 May 2023 16:17:26 +0200 Subject: [PATCH 142/192] QmlDesigner: fix unterminated access to data() SmallString is not null terminated. Change-Id: I92346f1befce8afadcf0d5532c17906a7908fff2 Reviewed-by: Marco Bubke --- src/libs/sqlite/sqlitesessions.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/sqlite/sqlitesessions.cpp b/src/libs/sqlite/sqlitesessions.cpp index 0dea908e5f8..96119cd20fb 100644 --- a/src/libs/sqlite/sqlitesessions.cpp +++ b/src/libs/sqlite/sqlitesessions.cpp @@ -67,7 +67,7 @@ void Sessions::create() { sqlite3_session *newSession = nullptr; int resultCode = sqlite3session_create(database.backend().sqliteDatabaseHandle(), - std::string(databaseName.data()).c_str(), + std::string(databaseName).c_str(), &newSession); session.reset(newSession); From 5a88c3ffc16d242a29350c3e5592597aed8f7e69 Mon Sep 17 00:00:00 2001 From: Tim Jenssen Date: Mon, 8 May 2023 16:30:08 +0200 Subject: [PATCH 143/192] QmlDesigner: improve ModuleScanner handle empty path and paths better Task-number: QDS-9847 Change-Id: I9dc6f7972835a366a046d06641fbb64494c734fc Reviewed-by: Marco Bubke --- .../projectstorage/modulescanner.cpp | 3 +++ tests/unit/unittest/modulescanner-test.cpp | 16 +++++++++++++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/plugins/qmldesigner/designercore/projectstorage/modulescanner.cpp b/src/plugins/qmldesigner/designercore/projectstorage/modulescanner.cpp index 12cded6650d..c9c7fd8d61e 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/modulescanner.cpp +++ b/src/plugins/qmldesigner/designercore/projectstorage/modulescanner.cpp @@ -69,6 +69,9 @@ void ModuleScanner::scan(const QStringList &modulePaths) void ModuleScanner::scan([[maybe_unused]] std::string_view modulePath) { #ifdef QDS_HAS_QMLPRIVATE + if (modulePath.empty()) + return; + QDirIterator dirIterator{QString::fromUtf8(modulePath), QDir::Dirs | QDir::NoDotAndDotDot, QDirIterator::Subdirectories}; diff --git a/tests/unit/unittest/modulescanner-test.cpp b/tests/unit/unittest/modulescanner-test.cpp index f3bbbf23e9e..b4632e0cb41 100644 --- a/tests/unit/unittest/modulescanner-test.cpp +++ b/tests/unit/unittest/modulescanner-test.cpp @@ -65,13 +65,27 @@ protected: externalDependenciesMock}; }; -TEST_F(ModuleScanner, ReturnEmptyOptionalForWrongPath) +TEST_F(ModuleScanner, ReturnEmptyOptionalForEmptyPath) { scanner.scan(QStringList{""}); ASSERT_THAT(scanner.modules(), IsEmpty()); } +TEST_F(ModuleScanner, ReturnEmptyOptionalForNullStringPath) +{ + scanner.scan(QStringList{QString{}}); + + ASSERT_THAT(scanner.modules(), IsEmpty()); +} + +TEST_F(ModuleScanner, ReturnEmptyOptionalForEmptyPaths) +{ + scanner.scan(QStringList{}); + + ASSERT_THAT(scanner.modules(), IsEmpty()); +} + TEST_F(ModuleScanner, GetQtQuick) { scanner.scan(QStringList{qmlModulesPath}); From 7a2b1f724b6d2c60b4f89a09491a9a95d8eee243 Mon Sep 17 00:00:00 2001 From: Burak Hancerli Date: Mon, 8 May 2023 15:56:55 +0200 Subject: [PATCH 144/192] QmlDesigner: Change the raw pointer to uniqeptr and rename the main file Change-Id: I751359d65735b1be9b3e6b27946c23c278a78a6e Reviewed-by: Marco Bubke --- src/tools/qml2puppet/CMakeLists.txt | 2 +- .../qml2puppet/{main.cpp => qml2puppetmain.cpp} | 13 ++++++------- 2 files changed, 7 insertions(+), 8 deletions(-) rename src/tools/qml2puppet/qml2puppet/{main.cpp => qml2puppetmain.cpp} (62%) diff --git a/src/tools/qml2puppet/CMakeLists.txt b/src/tools/qml2puppet/CMakeLists.txt index 98b1ebdf458..4f08b1fe355 100644 --- a/src/tools/qml2puppet/CMakeLists.txt +++ b/src/tools/qml2puppet/CMakeLists.txt @@ -45,7 +45,7 @@ add_qtc_executable(qml2puppet INCLUDES ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR} SOURCES - qml2puppet/main.cpp + qml2puppet/qml2puppetmain.cpp qml2puppet/qmlbase.h qml2puppet/appmetadata.h qml2puppet/qmlpuppet.h qml2puppet/qmlpuppet.cpp qml2puppet/configcrashpad.h qmlpuppet.qrc diff --git a/src/tools/qml2puppet/qml2puppet/main.cpp b/src/tools/qml2puppet/qml2puppet/qml2puppetmain.cpp similarity index 62% rename from src/tools/qml2puppet/qml2puppet/main.cpp rename to src/tools/qml2puppet/qml2puppet/qml2puppetmain.cpp index 763bf77b5dc..5896df39e1f 100644 --- a/src/tools/qml2puppet/qml2puppet/main.cpp +++ b/src/tools/qml2puppet/qml2puppet/qml2puppetmain.cpp @@ -7,25 +7,24 @@ #include "runner/qmlruntime.h" #endif -QmlBase *getQmlRunner(int &argc, char **argv) +auto getQmlRunner(int &argc, char **argv) { #ifdef ENABLE_INTERNAL_QML_RUNTIME + QString qmlRuntime("--qml-runtime"); for (int i = 0; i < argc; i++) { - if (!strcmp(argv[i], "--qml-runtime")){ + if (!qmlRuntime.compare(QString::fromLocal8Bit(argv[i]))) { qInfo() << "Starting QML Runtime"; - return new QmlRuntime(argc, argv); + return std::unique_ptr(new QmlRuntime(argc, argv)); } } #endif qInfo() << "Starting QML Puppet"; - return new QmlPuppet(argc, argv); + return std::unique_ptr(new QmlPuppet(argc, argv)); } int main(int argc, char *argv[]) { QDSMeta::Logging::registerMessageHandler(); QDSMeta::AppInfo::registerAppInfo("Qml2Puppet"); - - QmlBase *qmlRunner = getQmlRunner(argc, argv); - return qmlRunner->run(); + return getQmlRunner(argc, argv)->run(); } From 5ab0b37ba17919f277a28eab1daea64a3cc22512 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Thu, 20 Apr 2023 15:43:34 +0200 Subject: [PATCH 145/192] QmlDesigner: Cleanup cmake file Change-Id: I8cca25367741df3ae265413220a64f0c682bada1 Reviewed-by: Tim Jenssen --- src/plugins/qmldesigner/CMakeLists.txt | 46 +++++++++++++------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index 0ef574afec7..00b8bddfbdb 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -8,9 +8,7 @@ if (APPLE) set(QmlDesignerPluginInstallPrefix "${IDE_PLUGIN_PATH}/QmlDesigner") endif() -env_with_default("QDS_USE_PROJECTSTORAGE" ENV_QDS_USE_PROJECTSTORAGE OFF) -option(USE_PROJECTSTORAGE "Use ProjectStorage" ${ENV_QDS_USE_PROJECTSTORAGE}) -add_feature_info("ProjectStorage" ${USE_PROJECTSTORAGE} "") + add_qtc_library(QmlDesignerUtils STATIC DEPENDS @@ -39,6 +37,7 @@ extend_qtc_library(QmlDesignerUtils add_qtc_library(QmlDesignerCore STATIC CONDITION Qt6_VERSION VERSION_GREATER_EQUAL 6.4.3 EXCLUDE_FROM_INSTALL + PROPERTIES SKIP_AUTOUIC ON DEPENDS Threads::Threads Qt::CorePrivate @@ -73,6 +72,14 @@ add_qtc_library(QmlDesignerCore STATIC rewritertransaction.h ) + +if(TARGET QmlDesignerCore) + env_with_default("QDS_USE_PROJECTSTORAGE" ENV_QDS_USE_PROJECTSTORAGE OFF) + option(USE_PROJECTSTORAGE "Use ProjectStorage" ${ENV_QDS_USE_PROJECTSTORAGE}) + add_feature_info("ProjectStorage" ${USE_PROJECTSTORAGE} "") +endif() + + extend_qtc_library(QmlDesignerCore CONDITION NOT DISABLE_COMPILE_WARNING_AS_ERROR PROPERTIES COMPILE_WARNING_AS_ERROR ON @@ -90,20 +97,6 @@ extend_qtc_library(QmlDesignerCore PUBLIC_DEPENDS rt ) -set(UI_FILES - ${CMAKE_CURRENT_LIST_DIR}/designercore/instances/puppetbuildprogressdialog.ui - ${CMAKE_CURRENT_LIST_DIR}/designercore/instances/puppetdialog.ui -) -qt_wrap_ui(UI_SOURCES ${UI_FILES}) - -extend_qtc_library(QmlDesignerCore - INCLUDES ${CMAKE_CURRENT_BINARY_DIR} - SOURCES - ${UI_SOURCES} - ${UI_FILES} -) -set_source_files_properties(${UI_FILES} PROPERTIES SKIP_AUTOUIC ON) - extend_qtc_library(QmlDesignerCore INCLUDES ${CMAKE_CURRENT_LIST_DIR}/designercore/exceptions SOURCES_PREFIX ${CMAKE_CURRENT_LIST_DIR}/designercore/exceptions @@ -293,14 +286,23 @@ extend_qtc_library(QmlDesignerCore nodeinstanceserverproxy.cpp nodeinstanceserverproxy.h nodeinstanceview.cpp - puppetbuildprogressdialog.cpp - puppetbuildprogressdialog.h puppetstartdata.h puppetstarter.cpp puppetstarter.h + qprocessuniqueptr.h +) + +extend_qtc_library(QmlDesignerCore + PUBLIC_INCLUDES ${CMAKE_CURRENT_LIST_DIR}/designercore/instances + SOURCES_PREFIX ${CMAKE_CURRENT_LIST_DIR}/designercore/instances + SOURCES_PROPERTIES SKIP_AUTOUIC OFF + SOURCES + puppetbuildprogressdialog.ui + puppetdialog.ui + puppetbuildprogressdialog.cpp + puppetbuildprogressdialog.h puppetdialog.cpp puppetdialog.h - qprocessuniqueptr.h ) extend_qtc_library(QmlDesignerCore @@ -386,6 +388,7 @@ extend_qtc_library(QmlDesignerCore extend_qtc_library(QmlDesignerCore SOURCES_PREFIX designercore/projectstorage PUBLIC_INCLUDES designercore/projectstorage + SOURCES_PROPERTIES SKIP_AUTOMOC ON SOURCES commontypecache.h directorypathcompressor.h @@ -422,9 +425,6 @@ extend_qtc_library(QmlDesignerCore qmldocumentparser.cpp qmldocumentparser.h ) -file(GLOB PROJECTSTORAGE_EXCLUDED_SOURCES designercore/projectstorage/*.cpp) -set_property(SOURCE ${PROJECTSTORAGE_EXCLUDED_SOURCES} PROPERTY SKIP_AUTOMOC ON) - add_qtc_plugin(QmlDesigner PLUGIN_RECOMMENDS QmlPreview CONDITION Qt6_VERSION VERSION_GREATER_EQUAL 6.4.3 AND TARGET QmlDesignerCore AND TARGET Qt::QuickWidgets AND TARGET Qt::Svg From 849b8cf000fa00acaf59209ebf4b00c184f5b1b4 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Mon, 1 May 2023 10:23:53 +0200 Subject: [PATCH 146/192] QmlDesigner: Improve sqlite id https://www.sqlite.org/autoinc.html says that "If no negative ROWID values are inserted explicitly, then automatically generated ROWID values will always be greater than zero." So zero is an invalid value. This changes reflect on it and makes it an invalid value too. Null is a special value in SQL and it could cast to zero by accident. To prevent that ambiguty we make zero an invalid value too. You can explicit add negative rowids but that has size overhead because positive values can be compressed. So explicit not positive values are not supported. Change-Id: I417ea9fec1573cfd9f1a98134f8adc567021988c Reviewed-by: Tim Jenssen --- src/libs/sqlite/sqliteids.h | 4 ++-- tests/unit/unittest/storagecache-test.cpp | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/libs/sqlite/sqliteids.h b/src/libs/sqlite/sqliteids.h index a0562b4df44..7613a2df46f 100644 --- a/src/libs/sqlite/sqliteids.h +++ b/src/libs/sqlite/sqliteids.h @@ -63,7 +63,7 @@ public: #pragma GCC diagnostic pop #endif - constexpr bool isValid() const { return id >= 0; } + constexpr bool isValid() const { return id > 0; } explicit operator bool() const { return isValid(); } @@ -74,7 +74,7 @@ public: [[noreturn, deprecated]] InternalIntegerType operator&() const { throw std::exception{}; } private: - InternalIntegerType id = -1; + InternalIntegerType id = 0; }; template diff --git a/tests/unit/unittest/storagecache-test.cpp b/tests/unit/unittest/storagecache-test.cpp index 3e3763dae4b..543b4dc5787 100644 --- a/tests/unit/unittest/storagecache-test.cpp +++ b/tests/unit/unittest/storagecache-test.cpp @@ -90,11 +90,11 @@ protected: Utils::PathString filePath5{"/file/pathFife"}; Utils::PathStringVector filePaths{filePath1, filePath2, filePath3, filePath4, filePath5}; Utils::PathStringVector reverseFilePaths{filePath1, filePath2, filePath3, filePath4, filePath5}; - SourceContextId id1{SourceContextId::create(0)}; - SourceContextId id2{SourceContextId::create(1)}; - SourceContextId id3{SourceContextId::create(2)}; - SourceContextId id4{SourceContextId::create(3)}; - SourceContextId id5{SourceContextId::create(4)}; + SourceContextId id1{SourceContextId::create(1)}; + SourceContextId id2{SourceContextId::create(2)}; + SourceContextId id3{SourceContextId::create(3)}; + SourceContextId id4{SourceContextId::create(4)}; + SourceContextId id5{SourceContextId::create(5)}; SourceContextId id41{SourceContextId::create(41)}; SourceContextId id42{SourceContextId::create(42)}; SourceContextId id43{SourceContextId::create(43)}; From a881be85f8f85fe7ded066ed17f29f80dbf9bbf7 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Wed, 10 May 2023 10:30:56 +0200 Subject: [PATCH 147/192] QmlDesigner: Fix project storage Change-Id: I18c2d49f343edd107581075ab91dd1d324e11dfe Reviewed-by: Burak Hancerli Reviewed-by: Marco Bubke --- .../projectstorage/projectstorage.h | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h index be0c9213a75..47b30463f5e 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h @@ -136,19 +136,20 @@ public: .template valueWithTransaction(moduleId, exportedTypeName); } - PropertyDeclarationIds propertyDeclarationIds(TypeId typeId) const + PropertyDeclarationIds propertyDeclarationIds(TypeId typeId) const override { return selectPropertyDeclarationIdsForTypeStatement .template valuesWithTransaction(32, typeId); } - PropertyDeclarationIds localPropertyDeclarationIds(TypeId typeId) const + PropertyDeclarationIds localPropertyDeclarationIds(TypeId typeId) const override { return selectLocalPropertyDeclarationIdsForTypeStatement .template valuesWithTransaction(16, typeId); } - PropertyDeclarationId propertyDeclarationId(TypeId typeId, Utils::SmallStringView propertyName) const + PropertyDeclarationId propertyDeclarationId(TypeId typeId, + Utils::SmallStringView propertyName) const override { return selectPropertyDeclarationIdForTypeAndPropertyNameStatement .template valueWithTransaction(typeId, propertyName); @@ -169,25 +170,25 @@ public: propertyDeclarationId); } - std::optional type(TypeId typeId) const + std::optional type(TypeId typeId) const override { return selectInfoTypeByTypeIdStatement.template optionalValueWithTransaction( typeId); } - std::vector signalDeclarationNames(TypeId typeId) const + std::vector signalDeclarationNames(TypeId typeId) const override { return selectSignalDeclarationNamesForTypeStatement .template valuesWithTransaction(32, typeId); } - std::vector functionDeclarationNames(TypeId typeId) const + std::vector functionDeclarationNames(TypeId typeId) const override { return selectFuncionDeclarationNamesForTypeStatement .template valuesWithTransaction(32, typeId); } - std::optional propertyName(PropertyDeclarationId propertyDeclarationId) const + std::optional propertyName(PropertyDeclarationId propertyDeclarationId) const override { return selectPropertyNameStatement.template optionalValueWithTransaction( propertyDeclarationId); @@ -216,13 +217,13 @@ public: return commonTypeCache_.template builtinTypeId(); } - TypeIds prototypeIds(TypeId type) const + TypeIds prototypeIds(TypeId type) const override { return selectPrototypeIdsForTypeIdInOrderStatement.template valuesWithTransaction(16, type); } - TypeIds prototypeAndSelfIds(TypeId type) const + TypeIds prototypeAndSelfIds(TypeId type) const override { return selectPrototypeAndSelfIdsForTypeIdInOrderStatement .template valuesWithTransaction(16, type); From bd9f49f026b489e90d5dfe445b717db85025b8e8 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Wed, 10 May 2023 11:03:35 +0200 Subject: [PATCH 148/192] QmlDesigner: Workaround wrong Clang warning The function is cleary needed in a constexpr if later. And removing it would not compileeven if the constexpr if is false. Seems Clang is emitting the warning after the constexpr if branch is removed. Change-Id: Ie1988a28cf19d06148f609204c16c3d7753b4c63 Reviewed-by: Burak Hancerli Reviewed-by: Marco Bubke --- src/plugins/qmldesigner/designercore/model/model.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/plugins/qmldesigner/designercore/model/model.cpp b/src/plugins/qmldesigner/designercore/model/model.cpp index f9d23ba4aaf..e630cc96d84 100644 --- a/src/plugins/qmldesigner/designercore/model/model.cpp +++ b/src/plugins/qmldesigner/designercore/model/model.cpp @@ -37,6 +37,7 @@ #include #include #include +#include /*! \defgroup CoreModel @@ -212,6 +213,9 @@ void ModelPrivate::changeNodeType(const InternalNodePointer &node, const TypeNam } namespace { +QT_WARNING_PUSH +QT_WARNING_DISABLE_CLANG("-Wunneeded-internal-declaration") + std::pair decomposeTypePath(Utils::SmallStringView typeName) { auto found = std::find(typeName.rbegin(), typeName.rend(), '.'); @@ -221,6 +225,8 @@ std::pair decomposeTypePath(Util return {{typeName.begin(), std::prev(found.base())}, {found.base(), typeName.end()}}; } + +QT_WARNING_POP } // namespace InternalNodePointer ModelPrivate::createNode(const TypeName &typeName, From 8129f886bcc50e91f55de15f751df183f0db822c Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Tue, 9 May 2023 13:07:36 +0200 Subject: [PATCH 149/192] QmlDesigner: Improve warning suppression Can be removed with never compiler. Change-Id: I3196acfc82e3ab53f0f72dec3b543f27b9bbe2e1 Reviewed-by: Tim Jenssen --- src/libs/sqlite/sqliteids.h | 7 ------- src/plugins/qmldesigner/CMakeLists.txt | 3 ++- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/src/libs/sqlite/sqliteids.h b/src/libs/sqlite/sqliteids.h index 7613a2df46f..e7a4f9fbae2 100644 --- a/src/libs/sqlite/sqliteids.h +++ b/src/libs/sqlite/sqliteids.h @@ -51,17 +51,10 @@ public: return first.id >= second.id; } -#if defined(__GNUC__) && !defined(__clang__) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wmaybe-uninitialized" -#endif constexpr friend InternalIntegerType operator-(BasicId first, BasicId second) { return first.id - second.id; } -#if defined(__GNUC__) && !defined(__clang__) -#pragma GCC diagnostic pop -#endif constexpr bool isValid() const { return id > 0; } diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index 00b8bddfbdb..be90de8ae2b 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -8,7 +8,8 @@ if (APPLE) set(QmlDesignerPluginInstallPrefix "${IDE_PLUGIN_PATH}/QmlDesigner") endif() - +add_compile_options("$<$:-Wno-error=maybe-uninitialized>") +add_compile_options("$<$:-Wno-error=maybe-uninitialized>") add_qtc_library(QmlDesignerUtils STATIC DEPENDS From 07e3641185c5e5efd45ff0e45d0cdb824bf45ef4 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Sat, 22 Apr 2023 22:29:33 +0200 Subject: [PATCH 150/192] QmlDesigner: Remove unneeded calls in GradientModel::rowCount Change-Id: I13443461ce8a8e1b0d2a13d38f3975d4dd703090 Reviewed-by: Reviewed-by: Tim Jenssen --- .../components/propertyeditor/gradientmodel.cpp | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/plugins/qmldesigner/components/propertyeditor/gradientmodel.cpp b/src/plugins/qmldesigner/components/propertyeditor/gradientmodel.cpp index 75b9f34cefd..a81560f5662 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/gradientmodel.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/gradientmodel.cpp @@ -29,14 +29,13 @@ GradientModel::GradientModel(QObject *parent) : int GradientModel::rowCount(const QModelIndex & /*parent*/) const { - if (m_itemNode.isValid()) { - if (m_itemNode.modelNode().hasNodeProperty(gradientPropertyName().toUtf8())) { - QmlDesigner::ModelNode gradientNode = - m_itemNode.modelNode().nodeProperty(gradientPropertyName().toUtf8()).modelNode(); + if (m_itemNode.modelNode().hasNodeProperty(gradientPropertyName().toUtf8())) { + QmlDesigner::ModelNode gradientNode = m_itemNode.modelNode() + .nodeProperty(gradientPropertyName().toUtf8()) + .modelNode(); - if (gradientNode.hasNodeListProperty("stops")) - return gradientNode.nodeListProperty("stops").toModelNodeList().count(); - } + if (gradientNode.hasNodeListProperty("stops")) + return gradientNode.nodeListProperty("stops").count(); } return 0; From 6a4313b7a8d06a3a6951be9203dbc26be9f0405b Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Wed, 10 May 2023 15:25:48 +0200 Subject: [PATCH 151/192] QmlDesigner: Add setting to force download mirror Add setting QML/Designer/ForceDownloadMirror to set a specific mirror for downloads. Remove private QQmlData usage. This was for debugging and the issue was fixed. Task-number: QDS-9687 Change-Id: Icf9a2e2db7d60b13f37d7f51b857cbe7c0a1c3b4 Reviewed-by: Tim Jenssen --- src/plugins/qmldesigner/utils/filedownloader.cpp | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/plugins/qmldesigner/utils/filedownloader.cpp b/src/plugins/qmldesigner/utils/filedownloader.cpp index 7825be5081b..5f0aabe3091 100644 --- a/src/plugins/qmldesigner/utils/filedownloader.cpp +++ b/src/plugins/qmldesigner/utils/filedownloader.cpp @@ -2,7 +2,8 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0 #include "filedownloader.h" -#include +#include + #include #include @@ -154,6 +155,15 @@ void FileDownloader::setUrl(const QUrl &url) { if (m_url != url) { m_url = url; + + const QString mirror = Core::ICore::settings()->value("QML/Designer/ForceDownloadMirror").toString(); + if (!mirror.isEmpty()) { + qWarning() << Q_FUNC_INFO << "Alternative mirror is used:" << mirror; + QString replaced = url.toString(); + replaced.replace("https://download.qt.io/", mirror); + m_url = QUrl::fromUserInput(replaced); + } + emit urlChanged(); } @@ -262,10 +272,6 @@ void FileDownloader::doProbeUrl() this, [this](QNetworkReply::NetworkError code) { - if (QQmlData::wasDeleted(this)) { - qDebug() << Q_FUNC_INFO << "FileDownloader was deleted."; - return; - } qDebug() << Q_FUNC_INFO << "Network error:" << code << qobject_cast(sender())->errorString(); From 36936196bf0bd000256cd658b6082b63425b8fba Mon Sep 17 00:00:00 2001 From: Tim Jenssen Date: Wed, 10 May 2023 21:00:18 +0000 Subject: [PATCH 152/192] Revert "QmlDesigner: Add setting to force download mirror" This reverts commit 6a4313b7a8d06a3a6951be9203dbc26be9f0405b. Reason for revert: does not compile on linux and it is maybe not a good idea that utils static lib gets a core plugin dependency Change-Id: I2bf153f94aa922a82776b490238ba0ba774d26aa Reviewed-by: Tim Jenssen --- src/plugins/qmldesigner/utils/filedownloader.cpp | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/src/plugins/qmldesigner/utils/filedownloader.cpp b/src/plugins/qmldesigner/utils/filedownloader.cpp index 5f0aabe3091..7825be5081b 100644 --- a/src/plugins/qmldesigner/utils/filedownloader.cpp +++ b/src/plugins/qmldesigner/utils/filedownloader.cpp @@ -2,8 +2,7 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0 #include "filedownloader.h" -#include - +#include #include #include @@ -155,15 +154,6 @@ void FileDownloader::setUrl(const QUrl &url) { if (m_url != url) { m_url = url; - - const QString mirror = Core::ICore::settings()->value("QML/Designer/ForceDownloadMirror").toString(); - if (!mirror.isEmpty()) { - qWarning() << Q_FUNC_INFO << "Alternative mirror is used:" << mirror; - QString replaced = url.toString(); - replaced.replace("https://download.qt.io/", mirror); - m_url = QUrl::fromUserInput(replaced); - } - emit urlChanged(); } @@ -272,6 +262,10 @@ void FileDownloader::doProbeUrl() this, [this](QNetworkReply::NetworkError code) { + if (QQmlData::wasDeleted(this)) { + qDebug() << Q_FUNC_INFO << "FileDownloader was deleted."; + return; + } qDebug() << Q_FUNC_INFO << "Network error:" << code << qobject_cast(sender())->errorString(); From 5bf1b598c153fdab3ab95c66ce132482ec622add Mon Sep 17 00:00:00 2001 From: Knud Dollereder Date: Wed, 3 May 2023 12:17:28 +0200 Subject: [PATCH 153/192] Move mcu deploy step into the mcusupport plugin This deploy step will run the qmlprojectexporter commandline tool on the currently active qmlproject if qtForMCUs is set to true. The output of the tool is shown in the "Compile Output" panel. Possible configuration issues are shown in the "Issues" panel. Note that the step is not removed from the list but disabled when setting qtForMCUs to false in order to retain possible changes made by the user. Change-Id: I03b0d6fbe420b49400d48e7365d4395491b9aa2d Reviewed-by: Tim Jenssen Reviewed-by: hjk --- src/plugins/mcusupport/CMakeLists.txt | 1 + .../mcubuildstep.cpp | 95 ++++++++----------- .../mcubuildstep.h | 6 +- src/plugins/mcusupport/mcusupportplugin.cpp | 7 ++ src/plugins/mcusupport/mcusupportplugin.h | 4 + src/plugins/qmlprojectmanager/CMakeLists.txt | 3 +- .../buildsystem/qmlbuildsystem.cpp | 34 ++++++- .../qmlprojectmanager/qmlprojectplugin.cpp | 2 - 8 files changed, 87 insertions(+), 65 deletions(-) rename src/plugins/{qmlprojectmanager => mcusupport}/mcubuildstep.cpp (68%) rename src/plugins/{qmlprojectmanager => mcusupport}/mcubuildstep.h (85%) diff --git a/src/plugins/mcusupport/CMakeLists.txt b/src/plugins/mcusupport/CMakeLists.txt index 778f0a70394..81fadd4ddfb 100644 --- a/src/plugins/mcusupport/CMakeLists.txt +++ b/src/plugins/mcusupport/CMakeLists.txt @@ -23,6 +23,7 @@ add_qtc_plugin(McuSupport mcuhelpers.cpp mcuhelpers.h settingshandler.cpp settingshandler.h mcuqmlprojectnode.cpp mcuqmlprojectnode.h + mcubuildstep.cpp mcubuildstep.h ) add_subdirectory(test) diff --git a/src/plugins/qmlprojectmanager/mcubuildstep.cpp b/src/plugins/mcusupport/mcubuildstep.cpp similarity index 68% rename from src/plugins/qmlprojectmanager/mcubuildstep.cpp rename to src/plugins/mcusupport/mcubuildstep.cpp index 031ed770354..a26e8ed0d1e 100644 --- a/src/plugins/qmlprojectmanager/mcubuildstep.cpp +++ b/src/plugins/mcusupport/mcubuildstep.cpp @@ -2,7 +2,11 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0 #include "mcubuildstep.h" -#include "qmlprojectmanagertr.h" +#include "mcukitmanager.h" +#include "mculegacyconstants.h" +#include "mcusupportconstants.h" + +#include #include #include @@ -14,10 +18,8 @@ #include #include -#include +#include -#include -#include #include #include @@ -25,7 +27,7 @@ #include -namespace QmlProjectManager { +namespace McuSupport::Internal { const Utils::Id DeployMcuProcessStep::id = "QmlProject.Mcu.DeployStep"; @@ -35,26 +37,17 @@ void DeployMcuProcessStep::showError(const QString &text) ProjectExplorer::TaskHub::addTask(task); } -// TODO: -// - Grabbing *a* kit might not be the best todo. -// Would be better to specify a specific version of Qt4MCU in the qmlproject file. -// Currently we use the kit with the greatest version number. -// -// - Do not compare to *legacy* constants. -// Sounds like they will stop existing at some point. -// Also: Find Constant for QUL_PLATFORM - DeployMcuProcessStep::DeployMcuProcessStep(ProjectExplorer::BuildStepList *bc, Utils::Id id) : AbstractProcessStep(bc, id) , m_tmpDir() { if (!buildSystem()) { - showError(Tr::tr("Failed to find valid build system")); + showError(QmlProjectManager::Tr::tr("Failed to find valid build system")); return; } if (!m_tmpDir.isValid()) { - showError(Tr::tr("Failed to create valid build directory")); + showError(QmlProjectManager::Tr::tr("Failed to create valid build directory")); return; } @@ -62,14 +55,14 @@ DeployMcuProcessStep::DeployMcuProcessStep(ProjectExplorer::BuildStepList *bc, U if (!kit) return; - QString root = findKitInformation(kit, McuSupport::Internal::Legacy::Constants::QUL_CMAKE_VAR); + QString root = findKitInformation(kit, Internal::Legacy::Constants::QUL_CMAKE_VAR); auto rootPath = Utils::FilePath::fromString(root); auto cmd = addAspect(); cmd->setSettingsKey("QmlProject.Mcu.ProcessStep.Command"); cmd->setDisplayStyle(Utils::StringAspect::PathChooserDisplay); cmd->setExpectedKind(Utils::PathChooser::Command); - cmd->setLabelText(Tr::tr("Command:")); + cmd->setLabelText(QmlProjectManager::Tr::tr("Command:")); cmd->setFilePath(rootPath.pathAppended("/bin/qmlprojectexporter")); const char *importPathConstant = QtSupport::Constants::KIT_QML_IMPORT_PATH; @@ -80,7 +73,7 @@ DeployMcuProcessStep::DeployMcuProcessStep(ProjectExplorer::BuildStepList *bc, U Utils::ProcessArgs::quoteArg(qulIncludeDir.pathAppended("Timeline").toString()) }; - const char *toolChainConstant = McuSupport::Internal::Constants::KIT_MCUTARGET_TOOLCHAIN_KEY; + const char *toolChainConstant = Internal::Constants::KIT_MCUTARGET_TOOLCHAIN_KEY; QStringList arguments = { Utils::ProcessArgs::quoteArg(buildSystem()->projectFilePath().toString()), "--platform", findKitInformation(kit, "QUL_PLATFORM"), @@ -91,14 +84,14 @@ DeployMcuProcessStep::DeployMcuProcessStep(ProjectExplorer::BuildStepList *bc, U auto args = addAspect(); args->setSettingsKey("QmlProject.Mcu.ProcessStep.Arguments"); args->setDisplayStyle(Utils::StringAspect::LineEditDisplay); - args->setLabelText(Tr::tr("Arguments:")); + args->setLabelText(QmlProjectManager::Tr::tr("Arguments:")); args->setValue(Utils::ProcessArgs::joinArgs(arguments)); auto outDir = addAspect(); outDir->setSettingsKey("QmlProject.Mcu.ProcessStep.BuildDirectory"); outDir->setDisplayStyle(Utils::StringAspect::PathChooserDisplay); outDir->setExpectedKind(Utils::PathChooser::Directory); - outDir->setLabelText(Tr::tr("Build directory:")); + outDir->setLabelText(QmlProjectManager::Tr::tr("Build directory:")); outDir->setPlaceHolderText(m_tmpDir.path()); setCommandLineProvider([this, cmd, args, outDir]() -> Utils::CommandLine { @@ -116,7 +109,6 @@ DeployMcuProcessStep::DeployMcuProcessStep(ProjectExplorer::BuildStepList *bc, U QString DeployMcuProcessStep::findKitInformation(ProjectExplorer::Kit *kit, const QString &key) { - // This is (kind of) stolen from mcukitmanager.cpp. Might make sense to unify. using namespace CMakeProjectManager; const auto config = CMakeConfigurationKitAspect::configuration(kit).toList(); const auto keyName = key.toUtf8(); @@ -130,57 +122,50 @@ QString DeployMcuProcessStep::findKitInformation(ProjectExplorer::Kit *kit, cons MCUBuildStepFactory::MCUBuildStepFactory() : BuildStepFactory() { - setDisplayName(Tr::tr("Qt4MCU Deploy Step")); + setDisplayName(QmlProjectManager::Tr::tr("Qt for MCUs Deploy Step")); registerStep(DeployMcuProcessStep::id); } -void MCUBuildStepFactory::attachToTarget(ProjectExplorer::Target *target) -{ - if (!target) - return; - - ProjectExplorer::DeployConfiguration *deployConfiguration = target->activeDeployConfiguration(); - ProjectExplorer::BuildStepList *stepList = deployConfiguration->stepList(); - if (stepList->contains(DeployMcuProcessStep::id)) - return; - - if (!findMostRecentQulKit()) { - DeployMcuProcessStep::showError(Tr::tr("Failed to find valid Qt4MCU kit")); - return; - } - - for (BuildStepFactory *factory : BuildStepFactory::allBuildStepFactories()) { - if (factory->stepId() == DeployMcuProcessStep::id) { - ProjectExplorer::BuildStep *deployConfig = factory->create(stepList); - stepList->appendStep(deployConfig); - } - } -} - ProjectExplorer::Kit *MCUBuildStepFactory::findMostRecentQulKit() { - // Stolen from mcukitmanager.cpp - auto kitQulVersion = [](const ProjectExplorer::Kit *kit) -> QVersionNumber { - const char *sdkVersion = McuSupport::Internal::Constants::KIT_MCUTARGET_SDKVERSION_KEY; - return QVersionNumber::fromString(kit->value(sdkVersion).toString()); - }; - ProjectExplorer::Kit *mcuKit = nullptr; for (auto availableKit : ProjectExplorer::KitManager::kits()) { if (!availableKit) continue; - auto qulVersion = kitQulVersion(availableKit); + auto qulVersion = McuKitManager::kitQulVersion(availableKit); if (qulVersion.isNull()) continue; if (!mcuKit) mcuKit = availableKit; - if (qulVersion > kitQulVersion(mcuKit)) + if (qulVersion > McuKitManager::kitQulVersion(mcuKit)) mcuKit = availableKit; } return mcuKit; } -} // namespace QmlProjectManager +void MCUBuildStepFactory::updateDeployStep(ProjectExplorer::Target *target, bool enabled) +{ + if (!target) + return; + + ProjectExplorer::DeployConfiguration *deployConfiguration = target->activeDeployConfiguration(); + ProjectExplorer::BuildStepList *stepList = deployConfiguration->stepList(); + ProjectExplorer::BuildStep *step = stepList->firstStepWithId(DeployMcuProcessStep::id); + if (!step && enabled) { + if (findMostRecentQulKit()) { + stepList->appendStep(DeployMcuProcessStep::id); + } else { + DeployMcuProcessStep::showError( + QmlProjectManager::Tr::tr("Failed to find valid Qt for MCUs kit")); + } + } else { + if (!step) + return; + step->setEnabled(enabled); + } +} + +} // namespace McuSupport::Internal diff --git a/src/plugins/qmlprojectmanager/mcubuildstep.h b/src/plugins/mcusupport/mcubuildstep.h similarity index 85% rename from src/plugins/qmlprojectmanager/mcubuildstep.h rename to src/plugins/mcusupport/mcubuildstep.h index 988284a8905..aebca09eba8 100644 --- a/src/plugins/qmlprojectmanager/mcubuildstep.h +++ b/src/plugins/mcusupport/mcubuildstep.h @@ -11,7 +11,7 @@ #include -namespace QmlProjectManager { +namespace McuSupport::Internal { class DeployMcuProcessStep : public ProjectExplorer::AbstractProcessStep { @@ -30,8 +30,8 @@ class MCUBuildStepFactory : public ProjectExplorer::BuildStepFactory { public: MCUBuildStepFactory(); - static void attachToTarget(ProjectExplorer::Target *target); static ProjectExplorer::Kit *findMostRecentQulKit(); + static void updateDeployStep(ProjectExplorer::Target *target, bool enabled); }; -} // namespace QmlProjectManager +} // namespace McuSupport::Internal diff --git a/src/plugins/mcusupport/mcusupportplugin.cpp b/src/plugins/mcusupport/mcusupportplugin.cpp index 6b4e851c476..274cd738e11 100644 --- a/src/plugins/mcusupport/mcusupportplugin.cpp +++ b/src/plugins/mcusupport/mcusupportplugin.cpp @@ -3,6 +3,7 @@ #include "mcusupportplugin.h" +#include "mcubuildstep.h" #include "mcukitinformation.h" #include "mcukitmanager.h" #include "mcuqmlprojectnode.h" @@ -98,6 +99,7 @@ public: McuSupportOptions m_options{m_settingsHandler}; McuSupportOptionsPage optionsPage{m_options, m_settingsHandler}; McuDependenciesKitAspect environmentPathsKitAspect; + MCUBuildStepFactory mcuBuildStepFactory; }; // class McuSupportPluginPrivate static McuSupportPluginPrivate *dd{nullptr}; @@ -219,4 +221,9 @@ void McuSupportPlugin::askUserAboutRemovingUninstalledTargetsKits() ICore::infoBar()->addInfo(info); } +void McuSupportPlugin::updateDeployStep(ProjectExplorer::Target *target, bool enabled) +{ + MCUBuildStepFactory::updateDeployStep(target, enabled); +} + } // namespace McuSupport::Internal diff --git a/src/plugins/mcusupport/mcusupportplugin.h b/src/plugins/mcusupport/mcusupportplugin.h index cb27b138464..c092f51d00f 100644 --- a/src/plugins/mcusupport/mcusupportplugin.h +++ b/src/plugins/mcusupport/mcusupportplugin.h @@ -7,6 +7,8 @@ #include +#include + namespace McuSupport::Internal { void printMessage(const QString &message, bool important); @@ -25,6 +27,8 @@ public: void askUserAboutMcuSupportKitsSetup(); static void askUserAboutMcuSupportKitsUpgrade(const SettingsHandler::Ptr &settingsHandler); static void askUserAboutRemovingUninstalledTargetsKits(); + + Q_INVOKABLE static void updateDeployStep(ProjectExplorer::Target *target, bool enabled); }; } // McuSupport::Internal diff --git a/src/plugins/qmlprojectmanager/CMakeLists.txt b/src/plugins/qmlprojectmanager/CMakeLists.txt index 9b6e7609176..3b7209d05c7 100644 --- a/src/plugins/qmlprojectmanager/CMakeLists.txt +++ b/src/plugins/qmlprojectmanager/CMakeLists.txt @@ -1,7 +1,7 @@ add_qtc_plugin(QmlProjectManager CONDITION TARGET Qt5::QuickWidgets PLUGIN_CLASS QmlProjectPlugin - DEPENDS QmlJS Qt5::QuickWidgets Utils McuSupport CMakeProjectManager + DEPENDS QmlJS Qt5::QuickWidgets Utils PLUGIN_DEPENDS Core ProjectExplorer QtSupport QmlDesignerBase SOURCES qmlprojectgen/qmlprojectgenerator.cpp qmlprojectgen/qmlprojectgenerator.h @@ -21,7 +21,6 @@ add_qtc_plugin(QmlProjectManager qmlprojectplugin.cpp qmlprojectplugin.h qmlprojectrunconfiguration.cpp qmlprojectrunconfiguration.h buildsystem/qmlbuildsystem.cpp buildsystem/qmlbuildsystem.h - mcubuildstep.cpp mcubuildstep.h "${PROJECT_SOURCE_DIR}/src/share/3rdparty/studiofonts/studiofonts.qrc" ) diff --git a/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.cpp b/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.cpp index 712aef92277..86645dc8c50 100644 --- a/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.cpp +++ b/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.cpp @@ -3,7 +3,6 @@ #include "qmlbuildsystem.h" #include "qmlprojectconstants.h" -#include "mcubuildstep.h" #include #include @@ -18,6 +17,10 @@ #include #include +#include +#include +#include + #include #include #include @@ -41,6 +44,31 @@ namespace { Q_LOGGING_CATEGORY(infoLogger, "QmlProjectManager.QmlBuildSystem", QtInfoMsg) } +ExtensionSystem::IPlugin *findMcuSupportPlugin() +{ + const ExtensionSystem::PluginSpec *pluginSpec = Utils::findOrDefault( + ExtensionSystem::PluginManager::plugins(), + Utils::equal(&ExtensionSystem::PluginSpec::name, QString("McuSupport"))); + + if (pluginSpec) + return pluginSpec->plugin(); + return nullptr; +} + +void updateMcuBuildStep(Target *target, bool mcuEnabled) +{ + if (auto plugin = findMcuSupportPlugin()) { + QMetaObject::invokeMethod( + plugin, + "updateDeployStep", + Qt::DirectConnection, + Q_ARG(ProjectExplorer::Target*, target), + Q_ARG(bool, mcuEnabled)); + } else if (mcuEnabled) { + qWarning() << "Failed to find McuSupport plugin but qtForMCUs is enabled in the project"; + } +} + QmlBuildSystem::QmlBuildSystem(Target *target) : BuildSystem(target) { @@ -52,11 +80,11 @@ QmlBuildSystem::QmlBuildSystem(Target *target) connect(target->project(), &Project::activeTargetChanged, [this](Target *target) { refresh(RefreshOptions::NoFileRefresh); - if (qtForMCUs()) - MCUBuildStepFactory::attachToTarget(target); + updateMcuBuildStep(target, qtForMCUs()); }); connect(target->project(), &Project::projectFileIsDirty, [this]() { refresh(RefreshOptions::Project); + updateMcuBuildStep(project()->activeTarget(), qtForMCUs()); }); // FIXME: Check. Probably bogus after the BuildSystem move. diff --git a/src/plugins/qmlprojectmanager/qmlprojectplugin.cpp b/src/plugins/qmlprojectmanager/qmlprojectplugin.cpp index dd33508636c..0a87ef9bc90 100644 --- a/src/plugins/qmlprojectmanager/qmlprojectplugin.cpp +++ b/src/plugins/qmlprojectmanager/qmlprojectplugin.cpp @@ -1,7 +1,6 @@ // Copyright (C) 2016 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 -#include "mcubuildstep.h" #include "qdslandingpage.h" #include "qmlprojectplugin.h" #include "qmlproject.h" @@ -94,7 +93,6 @@ public: QPointer lastMessageBox; QdsLandingPage *landingPage = nullptr; QdsLandingPageWidget *landingPageWidget = nullptr; - MCUBuildStepFactory mcuBuildStepFactory; }; QmlProjectPlugin::~QmlProjectPlugin() From a6ba2354ccaf5858b7fe681c51ffdd33c33d85c4 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Wed, 26 Apr 2023 17:52:01 +0200 Subject: [PATCH 154/192] QmlDesigner: Cleanup the Qt conditions We have now demand QmlPrivate, QmlDomPrivate and QmlCompilerPrivate. Change-Id: I77fa5b506c827b9ba2f7ea18e3ffdd176c1871ec Reviewed-by: Reviewed-by: Tim Jenssen --- src/plugins/qmldesigner/CMakeLists.txt | 10 +++++----- .../designercore/projectstorage/modulescanner.cpp | 6 ------ .../designercore/projectstorage/modulescanner.h | 4 ---- .../designercore/projectstorage/qmldocumentparser.cpp | 4 ++-- .../designercore/projectstorage/qmldocumentparser.h | 4 ++-- .../designercore/projectstorage/qmltypesparser.cpp | 4 ++-- .../designercore/projectstorage/qmltypesparser.h | 8 ++------ tests/unit/unittest/CMakeLists.txt | 6 +++--- 8 files changed, 16 insertions(+), 30 deletions(-) diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index be90de8ae2b..992f46b68bd 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -36,7 +36,7 @@ extend_qtc_library(QmlDesignerUtils ) add_qtc_library(QmlDesignerCore STATIC - CONDITION Qt6_VERSION VERSION_GREATER_EQUAL 6.4.3 + CONDITION Qt6_VERSION VERSION_GREATER_EQUAL 6.4.3 AND TARGET Qt6::QmlPrivate AND TARGET Qt6::QmlDomPrivate AND TARGET Qt6::QmlCompilerPrivate EXCLUDE_FROM_INSTALL PROPERTIES SKIP_AUTOUIC ON DEPENDS @@ -47,6 +47,8 @@ add_qtc_library(QmlDesignerCore STATIC Qt::Widgets Qt::Qml Qt::QmlPrivate + Qt6::QmlDomPrivate + Qt6::QmlCompilerPrivate Core ProjectExplorer QmakeProjectManager @@ -87,10 +89,8 @@ extend_qtc_library(QmlDesignerCore ) extend_qtc_library(QmlDesignerCore - CONDITION TARGET Qt6::QmlDomPrivate AND TARGET Qt6::QmlCompilerPrivate - AND Qt6_VERSION VERSION_LESS 6.6.0 - DEPENDS Qt6::QmlDomPrivate Qt6::QmlCompilerPrivate - PUBLIC_DEFINES QDS_HAS_QMLPRIVATE + CONDITION Qt6_VERSION VERSION_GREATER_EQUAL 6.5.0 AND Qt6_VERSION VERSION_LESS 6.6.0 + PUBLIC_DEFINES QDS_BUILD_QMLPARSER ) extend_qtc_library(QmlDesignerCore diff --git a/src/plugins/qmldesigner/designercore/projectstorage/modulescanner.cpp b/src/plugins/qmldesigner/designercore/projectstorage/modulescanner.cpp index c9c7fd8d61e..c458d8376b9 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/modulescanner.cpp +++ b/src/plugins/qmldesigner/designercore/projectstorage/modulescanner.cpp @@ -3,9 +3,7 @@ #include "modulescanner.h" -#ifdef QDS_HAS_QMLPRIVATE #include -#endif #include #include @@ -15,7 +13,6 @@ namespace QmlDesigner { namespace { -#ifdef QDS_HAS_QMLPRIVATE std::optional contentAsQString(const QString &filePath) { QFile file{filePath}; @@ -57,7 +54,6 @@ QString createCoreVersion(QStringView moduleName, ExternalDependenciesInterface return {}; } -#endif } // namespace void ModuleScanner::scan(const QStringList &modulePaths) @@ -68,7 +64,6 @@ void ModuleScanner::scan(const QStringList &modulePaths) void ModuleScanner::scan([[maybe_unused]] std::string_view modulePath) { -#ifdef QDS_HAS_QMLPRIVATE if (modulePath.empty()) return; @@ -110,7 +105,6 @@ void ModuleScanner::scan([[maybe_unused]] std::string_view modulePath) std::sort(m_modules.begin(), m_modules.end()); m_modules.erase(std::unique(m_modules.begin(), m_modules.end()), m_modules.end()); -#endif } } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/projectstorage/modulescanner.h b/src/plugins/qmldesigner/designercore/projectstorage/modulescanner.h index f450192087f..2083d1d48c0 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/modulescanner.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/modulescanner.h @@ -22,11 +22,9 @@ public: ModuleScanner([[maybe_unused]] SkipFunction skip, [[maybe_unused]] VersionScanning versionScanning, [[maybe_unused]] ExternalDependenciesInterface &externalDependencies) -#ifdef QDS_HAS_QMLPRIVATE : m_skip{std::move(skip)} , m_versionScanning{versionScanning} , m_externalDependencies{externalDependencies} -#endif { m_modules.reserve(128); } @@ -40,11 +38,9 @@ private: private: Imports m_modules; -#ifdef QDS_HAS_QMLPRIVATE SkipFunction m_skip; VersionScanning m_versionScanning; ExternalDependenciesInterface &m_externalDependencies; -#endif }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.cpp b/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.cpp index b530dc84e38..3e9ac43deda 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.cpp +++ b/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.cpp @@ -10,7 +10,7 @@ #include -#ifdef QDS_HAS_QMLPRIVATE +#ifdef QDS_BUILD_QMLPARSER #include #endif @@ -19,7 +19,7 @@ namespace QmlDesigner { -#ifdef QDS_HAS_QMLPRIVATE +#ifdef QDS_BUILD_QMLPARSER namespace QmlDom = QQmlJS::Dom; diff --git a/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.h b/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.h index 87fb03ddaba..2896d54ebe7 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.h @@ -18,7 +18,7 @@ public: using ProjectStorage = QmlDesigner::ProjectStorage; using PathCache = QmlDesigner::SourcePathCache; -#ifdef QDS_HAS_QMLPRIVATE +#ifdef QDS_BUILD_QMLPARSER QmlDocumentParser(ProjectStorage &storage, PathCache &pathCache) : m_storage{storage} , m_pathCache{pathCache} @@ -35,7 +35,7 @@ public: private: // m_pathCache and m_storage are only used when compiled for QDS -#ifdef QDS_HAS_QMLPRIVATE +#ifdef QDS_BUILD_QMLPARSER ProjectStorage &m_storage; PathCache &m_pathCache; #endif diff --git a/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.cpp b/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.cpp index 46613b015a4..c858076aa19 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.cpp +++ b/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.cpp @@ -7,7 +7,7 @@ #include -#ifdef HasQQmlJSTypeDescriptionReader +#ifdef QDS_BUILD_QMLPARSER #include #include #endif @@ -19,7 +19,7 @@ namespace QmlDesigner { -#ifdef HasQQmlJSTypeDescriptionReader +#ifdef QDS_BUILD_QMLPARSER namespace QmlDom = QQmlJS::Dom; diff --git a/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.h b/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.h index 0af85be7570..c1d9a3a0d45 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.h @@ -10,10 +10,6 @@ namespace Sqlite { class Database; } -#if defined(QDS_HAS_QMLPRIVATE) && QT_VERSION >= QT_VERSION_CHECK(6, 5, 0) -#define HasQQmlJSTypeDescriptionReader -#endif - namespace QmlDesigner { template @@ -27,7 +23,7 @@ class QmlTypesParser : public QmlTypesParserInterface public: using ProjectStorage = QmlDesigner::ProjectStorage; -#ifdef HasQQmlJSTypeDescriptionReader +#ifdef QDS_BUILD_QMLPARSER QmlTypesParser(ProjectStorage &storage) : m_storage{storage} {} @@ -41,7 +37,7 @@ public: const Storage::Synchronization::ProjectData &projectData) override; private: -#ifdef HasQQmlJSTypeDescriptionReader +#ifdef QDS_BUILD_QMLPARSER ProjectStorage &m_storage; #endif }; diff --git a/tests/unit/unittest/CMakeLists.txt b/tests/unit/unittest/CMakeLists.txt index 5d53959a7b5..3c8a5652436 100644 --- a/tests/unit/unittest/CMakeLists.txt +++ b/tests/unit/unittest/CMakeLists.txt @@ -346,7 +346,7 @@ extend_qtc_test(unittest ) extend_qtc_test(unittest - CONDITION TARGET Qt6::QmlDomPrivate AND TARGET Qt6::QmlCompilerPrivate AND Qt6_VERSION VERSION_GREATER_EQUAL 6.5.0 + CONDITION TARGET Qt6::QmlDomPrivate AND TARGET Qt6::QmlCompilerPrivate AND Qt6_VERSION VERSION_GREATER_EQUAL 6.5.0 AND Qt6_VERSION VERSION_LESS 6.6.0 DEPENDS Qt6::QmlDomPrivate Qt6::QmlCompilerPrivate SOURCES qmldocumentparser-test.cpp @@ -354,10 +354,10 @@ extend_qtc_test(unittest ) extend_qtc_test(unittest - CONDITION TARGET Qt6::QmlDomPrivate AND TARGET Qt6::QmlCompilerPrivate AND Qt6_VERSION VERSION_GREATER_EQUAL 6.5.0 + CONDITION TARGET Qt6::QmlDomPrivate AND TARGET Qt6::QmlCompilerPrivate AND Qt6_VERSION VERSION_GREATER_EQUAL 6.5.0 AND Qt6_VERSION VERSION_LESS 6.6.0 SOURCES_PREFIX "${QmlDesignerDir}/designercore" DEPENDS Qt6::QmlDomPrivate Qt6::QmlCompilerPrivate - DEFINES QDS_HAS_QMLPRIVATE + DEFINES QDS_BUILD_QMLPARSER SOURCES projectstorage/qmldocumentparser.cpp projectstorage/qmldocumentparser.h projectstorage/qmltypesparser.cpp projectstorage/qmltypesparser.h From 12d9a31f17a30f47d49757cea5504c7fdbeb8626 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Jen=C3=9Fen?= Date: Thu, 11 May 2023 10:24:15 +0200 Subject: [PATCH 155/192] qds: use QtQuickEffectMaker app bundle path Change-Id: Id267d50007766b2849b71d7ff9393d6de6f9e3c0 Reviewed-by: Thomas Hartmann --- .../components/componentcore/modelnodeoperations.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp index b8b1fa94e84..918b06c6379 100644 --- a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp +++ b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp @@ -1650,7 +1650,11 @@ void openEffectMaker(const QString &filePath) const QtSupport::QtVersion *baseQtVersion = QtSupport::QtKitAspect::qtVersion(target->kit()); if (baseQtVersion) { + Utils::Environment env = Utils::Environment::systemEnvironment(); + auto effectMakerPath = baseQtVersion->binPath().pathAppended("qqem").withExecutableSuffix(); + if (!effectMakerPath.exists() && env.osType() == Utils::OsTypeMac) + effectMakerPath = baseQtVersion->binPath().pathAppended("qqem.app/Contents/MacOS/qqem"); if (!effectMakerPath.exists()) { qWarning() << __FUNCTION__ << "Cannot find EffectMaker app"; return; @@ -1663,7 +1667,6 @@ void openEffectMaker(const QString &filePath) arguments << "--create"; arguments << "--exportpath" << effectResPath.toString(); - Utils::Environment env = Utils::Environment::systemEnvironment(); if (env.osType() == Utils::OsTypeMac) env.appendOrSet("QSG_RHI_BACKEND", "metal"); From f3fcce9f4fbba439aae3a0fa85a6261456367e7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Jen=C3=9Fen?= Date: Thu, 11 May 2023 10:27:31 +0200 Subject: [PATCH 156/192] qds: workaround that appbundle finds the images Task-number: QDS-9856 Change-Id: I4efe17b7a5daca12f29ac2d63bde3b5c8c3a142b Reviewed-by: Burak Hancerli Reviewed-by: Thomas Hartmann --- .../components/componentcore/modelnodeoperations.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp index 918b06c6379..3dbe386ce16 100644 --- a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp +++ b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp @@ -1673,6 +1673,11 @@ void openEffectMaker(const QString &filePath) Utils::QtcProcess *qqemProcess = new Utils::QtcProcess(); qqemProcess->setEnvironment(env); qqemProcess->setCommand({ effectMakerPath, arguments }); + + // workaround that effect maker can find the images QTBUG-113531 + if (env.osType() == Utils::OsTypeMac) + qqemProcess->setWorkingDirectory(baseQtVersion->qmlPath().pathAppended("QtQuickEffectMaker")); + QObject::connect(qqemProcess, &Utils::QtcProcess::done, [qqemProcess]() { qqemProcess->deleteLater(); }); From 239d8882a909349bb0afc44d5334870771f9ddec Mon Sep 17 00:00:00 2001 From: Mats Honkamaa Date: Wed, 10 May 2023 09:46:35 +0300 Subject: [PATCH 157/192] Doc: Update QQEM parts in doc Task-number: QDS-9588 Change-Id: I3ae76bf708549e18e8024122d809bd15cb63de05 Reviewed-by: Leena Miettinen --- doc/config/macros.qdocconf | 1 + .../src/overviews/qtquick-export.qdoc | 2 +- .../src/overviews/qtquick-motion-design.qdoc | 2 +- .../src/qt-quick-effect-maker.qdoc | 93 ------------------- .../qtdesignstudio-effect-maker-files.qdoc | 79 ---------------- .../src/qtdesignstudio-toc.qdoc | 6 +- ...signstudio-using-effect-maker-effects.qdoc | 44 +++++++++ 7 files changed, 48 insertions(+), 179 deletions(-) delete mode 100644 doc/qtdesignstudio/src/qt-quick-effect-maker.qdoc delete mode 100644 doc/qtdesignstudio/src/qtdesignstudio-effect-maker-files.qdoc create mode 100644 doc/qtdesignstudio/src/qtdesignstudio-using-effect-maker-effects.qdoc diff --git a/doc/config/macros.qdocconf b/doc/config/macros.qdocconf index 08398776843..1024ddc0278 100644 --- a/doc/config/macros.qdocconf +++ b/doc/config/macros.qdocconf @@ -27,6 +27,7 @@ macro.QC = "$IDE_DISPLAY_NAME" macro.QCE = "$IDE_DISPLAY_NAME Enterprise" macro.QD = "Qt Designer" macro.QDS = "Qt Design Studio" +macro.QQEM = "Qt Quick Effect Maker" macro.QDV = "Qt Design Viewer" macro.QL = "Qt Linguist" macro.QMCU = "Qt for MCUs" diff --git a/doc/qtdesignstudio/src/overviews/qtquick-export.qdoc b/doc/qtdesignstudio/src/overviews/qtquick-export.qdoc index 1d7661fdd34..f9ae3b8d7b4 100644 --- a/doc/qtdesignstudio/src/overviews/qtquick-export.qdoc +++ b/doc/qtdesignstudio/src/overviews/qtquick-export.qdoc @@ -4,7 +4,7 @@ /*! \page creator-exporting-qml.html \previouspage studio-importing-3d.html - \nextpage qt-effect-maker-files.html + \nextpage qt-using-effect-maker-effects.html \title Exporting Components diff --git a/doc/qtdesignstudio/src/overviews/qtquick-motion-design.qdoc b/doc/qtdesignstudio/src/overviews/qtquick-motion-design.qdoc index aade258132c..70ebb9115dc 100644 --- a/doc/qtdesignstudio/src/overviews/qtquick-motion-design.qdoc +++ b/doc/qtdesignstudio/src/overviews/qtquick-motion-design.qdoc @@ -3,7 +3,7 @@ /*! \page qtquick-motion-design.html - \previouspage qt-effect-maker.html + \previouspage qt-using-effect-maker-effects.html \nextpage quick-animation-overview.html \title Motion Design diff --git a/doc/qtdesignstudio/src/qt-quick-effect-maker.qdoc b/doc/qtdesignstudio/src/qt-quick-effect-maker.qdoc deleted file mode 100644 index 56dfebfe301..00000000000 --- a/doc/qtdesignstudio/src/qt-quick-effect-maker.qdoc +++ /dev/null @@ -1,93 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2022 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the Qt Design Studio documentation. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Free Documentation License Usage -** Alternatively, this file may be used under the terms of the GNU Free -** Documentation License version 1.3 as published by the Free Software -** Foundation and appearing in the file included in the packaging of -** this file. Please review the following information to ensure -** the GNU Free Documentation License version 1.3 requirements -** will be met: https://www.gnu.org/licenses/fdl-1.3.html. -** -****************************************************************************/ - -/*! - - \page qt-effect-maker.html - \previouspage qt-effect-maker-files.html - \nextpage qtquick-motion-design.html - \sa {Creating Qt Quick Effect Maker Files} - - \title Working with Effects in Qt Quick Effect Maker - - The Qt Quick Effect Maker is a tool to create high-performance - shader effects for Qt Quick. - - You can import these effects to \QDS and you can run Qt Quick Effect Maker - from \QDS. - - \section1 Creating Effects - - When you run Qt Quick Effect Maker, it starts with an empty project. To - create your effect, add nodes to the node tree. - - \image qt-quick-effect-maker.webp - - Consider the following things when creating effects: - - \list - \li Some nodes do not function alone, they need a helper node. For - example, the \uicontrol FastBlur node needs a \uicontrol BlurHelper node - and the \uicontrol Noise node needs a \uicontrol NoiseHelper node. If the - node needs another node, it is mentioned in the node description. - \li If your effect appears cropped, you need to go to \uicontrol Edit > - \uicontrol {Project Settings} and increase the item padding. - \endlist - - \section2 Creating a Blur Effect - - To create a blur effect: - - \list 1 - \li Add a \uicontrol FastBlur node to the node tree. - \li Add a \uicontrol BlurHelper node to the node tree. You need the - \uicontrol BlurHelper node for all effects that contains a blur effect. - \endlist - - \image blur-effect-nodes.png - - You can now select the \uicontrol FastBlur node and in the settings, change - the \uicontrol fastBlurAmount value to control the amount of blur. - - \image blur-effect-step-1.webp - - \section3 Adjusting Item Borders - - The effect appears cropped. You need to adjust item borders: - - \list 1 - \li Select \inlineimage effect-item-borders-icon.png - to display item borders. - \image blur-effect-step-2.webp - \li Go to \uicontrol Edit > \uicontrol {Project Settings}. - \li Set the item padding for all sides to 100. - \image effect-item-padding-dialog.png - \endlist - - Now, you can see the complete blur effect. - - \image blur-effect-step-3.webp - -*/ diff --git a/doc/qtdesignstudio/src/qtdesignstudio-effect-maker-files.qdoc b/doc/qtdesignstudio/src/qtdesignstudio-effect-maker-files.qdoc deleted file mode 100644 index 06b3ea15906..00000000000 --- a/doc/qtdesignstudio/src/qtdesignstudio-effect-maker-files.qdoc +++ /dev/null @@ -1,79 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2022 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the Qt Design Studio documentation. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Free Documentation License Usage -** Alternatively, this file may be used under the terms of the GNU Free -** Documentation License version 1.3 as published by the Free Software -** Foundation and appearing in the file included in the packaging of -** this file. Please review the following information to ensure -** the GNU Free Documentation License version 1.3 requirements -** will be met: https://www.gnu.org/licenses/fdl-1.3.html. -** -****************************************************************************/ - -/*! - - \page qt-effect-maker-files.html - \nextpage qt-effect-maker.html - \previouspage creator-exporting-qml.html - \sa {Working with Effects in Qt Quick Effect Maker} - - \title Creating Qt Quick Effect Maker Files - - \section1 Creating an Effect File - - You can create Qt Quick Effect Maker effect (.qep) files in \QDS and - then edit them in Qt Quick Effect Maker. - - To create an effect file: - - \list 1 - \li In \QDS, right-click in the \uicontrol Assets view and - select \uicontrol {New Effect}. - \QDS creates an effect file and opens it in Qt Quick Effect Maker. - \image qt-quick-effect-maker.webp - \li Edit the effect. - \li In Qt Quick Effect Maker, go to \uicontrol File > \uicontrol Save. - \li Select \uicontrol File > \uicontrol Export. - \li With the default settings, select \uicontrol OK. - \image effect-maker-export.png - \endlist - - Now, you can close Qt Quick Effect Maker and return to \QDS and apply the - effect. - - \section1 Applying an Effect - - You can apply effects to components in \QDS. To do so, drag the effect - from the \uicontrol Assets view to the component in the \uicontrol 2D or - \uicontrol Navigator view. - - \image apply-effect-maker-effect.webp - - \section1 Animated Effect - - When you import an animated effect, you need to turn on the animation for it - to see the animation in your \QDS application. - - To turn on animation for an effect, first apply the effect to a component, - and then: - - \list 1 - \li Select the effect in \uicontrol Navigator. - \li In \uicontrol Properties, go to the - \uicontrol {Exposed Custom Properties} section and set - \uicontrol timeRunning to true. - \endlist -*/ diff --git a/doc/qtdesignstudio/src/qtdesignstudio-toc.qdoc b/doc/qtdesignstudio/src/qtdesignstudio-toc.qdoc index 6dfdadfcf0d..6fc2014020f 100644 --- a/doc/qtdesignstudio/src/qtdesignstudio-toc.qdoc +++ b/doc/qtdesignstudio/src/qtdesignstudio-toc.qdoc @@ -180,11 +180,7 @@ \li \l{Importing 3D Assets} \endlist \li \l{Exporting Components} - \li Qt Quick Effect Maker - \list - \li \l{Creating Qt Quick Effect Maker Files} - \li \l{Working with Effects in Qt Quick Effect Maker} - \endlist + \li \l{Using Qt Quick Effect Maker Effects} \endlist \endlist \li \l{Motion Design} diff --git a/doc/qtdesignstudio/src/qtdesignstudio-using-effect-maker-effects.qdoc b/doc/qtdesignstudio/src/qtdesignstudio-using-effect-maker-effects.qdoc new file mode 100644 index 00000000000..1e53c53ba0f --- /dev/null +++ b/doc/qtdesignstudio/src/qtdesignstudio-using-effect-maker-effects.qdoc @@ -0,0 +1,44 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only + +/*! + + \page qt-using-effect-maker-effects.html + \nextpage qtquick-motion-design.html + \previouspage creator-exporting-qml.html + + \title Using Qt Quick Effect Maker Effects + + \QQEM is integrated into \QDS for creating shader effects. To create an + effect, you first create the effect file in \QDS, and then you edit it in \QQEM. + + For more information about \QQEM, see the + \l{https://doc.qt.io/qt-6/qtquickeffectmaker-index.html}{Qt Quick Effect Maker Manual}. + + \section1 Creating an Effect File + + To create an effect file in \QDS: + + \list 1 + \li Right-click in the \uicontrol Assets view and + select \uicontrol {New Effect}. + \QDS creates an effect file and opens it in \QQEM. + \image qt-quick-effect-maker.webp + \li Edit the effect. + \li In \QQEM, go to \uicontrol File > \uicontrol Save. + \li With the default settings, select \uicontrol OK. + \image effect-maker-export.png + \endlist + + Now, you can close \QQEM and return to \QDS and apply the + effect. + + \section1 Applying an Effect + + You can apply effects to components in \QDS. To do so, drag the effect + from the \uicontrol Assets view to the component in the \uicontrol 2D or + \uicontrol Navigator view. + + \image apply-effect-maker-effect.webp + +*/ From dd5730d2a3cec20649563ca1dc777ee32c9882a7 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Fri, 21 Apr 2023 16:36:03 +0200 Subject: [PATCH 158/192] QmlDesigner: Simplify conditional import adding Change-Id: I78e110a81f8117b711968b6be5b4241f96763c41 Reviewed-by: Miikka Heikkinen --- src/plugins/qmldesigner/CMakeLists.txt | 2 + .../components/componentcore/viewmanager.h | 2 +- .../components/edit3d/edit3dview.cpp | 27 ++---- .../itemlibrary/itemlibraryassetimporter.cpp | 82 ++++++---------- .../itemlibrary/itemlibraryassetimporter.h | 2 +- .../itemlibrary/itemlibrarywidget.cpp | 32 +++---- .../navigator/navigatortreemodel.cpp | 41 ++++---- .../designercore/model/modelutils.cpp | 93 +++++++++++++++++++ .../designercore/model/modelutils.h | 24 +++++ 9 files changed, 184 insertions(+), 121 deletions(-) create mode 100644 src/plugins/qmldesigner/designercore/model/modelutils.cpp create mode 100644 src/plugins/qmldesigner/designercore/model/modelutils.h diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index 992f46b68bd..b9459aeb1b5 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -343,6 +343,8 @@ extend_qtc_library(QmlDesignerCore modelnodepositionstorage.cpp modeltotextmerger.cpp modeltotextmerger.h + modelutils.cpp + modelutils.h nodeabstractproperty.cpp nodelistproperty.cpp nodeproperty.cpp diff --git a/src/plugins/qmldesigner/components/componentcore/viewmanager.h b/src/plugins/qmldesigner/components/componentcore/viewmanager.h index c54cf139e78..4201065f99f 100644 --- a/src/plugins/qmldesigner/components/componentcore/viewmanager.h +++ b/src/plugins/qmldesigner/components/componentcore/viewmanager.h @@ -67,7 +67,7 @@ public: void disableWidgets(); void enableWidgets(); - void pushFileOnCrumbleBar(const Utils::FilePath &fileName); + void pushFileOnCrumbleBar(const ::Utils::FilePath &fileName); void pushInFileComponentOnCrumbleBar(const ModelNode &modelNode); void nextFileIsCalledInternally(); diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp index 9dcc2d7abc8..10d62a5dc21 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp +++ b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp @@ -23,6 +23,8 @@ #include "seekerslider.h" #include "theme.h" +#include + #include #include @@ -919,25 +921,12 @@ Edit3DBakeLightsAction *Edit3DView::bakeLightsAction() const void Edit3DView::addQuick3DImport() { DesignDocument *document = QmlDesignerPlugin::instance()->currentDesignDocument(); - if (document && !document->inFileComponentModelActive() && model()) { - Import qtQuick3DImport; - differenceCall(model()->possibleImports(), model()->imports(), [&](const auto &import) { - if (import.url() == "QtQuick3D") - qtQuick3DImport = import; - }); - if (!qtQuick3DImport.isEmpty()) { - if (!qtQuick3DImport.version().isEmpty() && qtQuick3DImport.majorVersion() >= 6) { - // Prefer empty version number in Qt6 and beyond - model()->changeImports({Import::createLibraryImport(qtQuick3DImport.url(), - {}, - qtQuick3DImport.alias(), - qtQuick3DImport.importPaths())}, - {}); - } else { - model()->changeImports({qtQuick3DImport}, {}); - } - return; - } + if (document && !document->inFileComponentModelActive() && model() + && Utils::addImportWithCheck( + "QtQuick3D", + [](const Import &import) { return !import.hasVersion() || import.majorVersion() >= 6; }, + model())) { + return; } Core::AsynchronousMessageBox::warning(tr("Failed to Add Import"), tr("Could not add QtQuick3D import to project.")); diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.cpp index e7002ff2503..3bbe60a0f65 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.cpp @@ -14,6 +14,8 @@ #include "rewritingexception.h" #include "viewmanager.h" +#include + #include #include @@ -300,7 +302,7 @@ bool ItemLibraryAssetImporter::preParseQuick3DAsset(const QString &file, ParseDa if (exitVal == QDialog::Accepted) overwriteFiles = dlg.selectedFiles(); if (!overwriteFiles.isEmpty()) { - overwriteFiles.append(Utils::toList(alwaysOverwrite)); + overwriteFiles.append(::Utils::toList(alwaysOverwrite)); m_overwrittenImports.insert(pd.targetDirPath, overwriteFiles); } else { addWarning(tr("No files selected for overwrite, skipping import: \"%1\".").arg(pd.assetName)); @@ -359,9 +361,8 @@ void ItemLibraryAssetImporter::postParseQuick3DAsset(ParseData &pd) qmlInfo.append("."); qmlInfo.append(pd.assetName); qmlInfo.append('\n'); - m_requiredImports.append(Import::createLibraryImport( - QStringLiteral("%1.%2").arg(pd.targetDir.dirName(), - pd.assetName), version)); + m_requiredImports.append( + QStringLiteral("%1.%2").arg(pd.targetDir.dirName(), pd.assetName)); while (qmlIt.hasNext()) { qmlIt.next(); QFileInfo fi = QFileInfo(qmlIt.filePath()); @@ -417,11 +418,8 @@ void ItemLibraryAssetImporter::postParseQuick3DAsset(ParseData &pd) } // Add quick3D import unless it is already added - if (impVersionMajor > 0 - && m_requiredImports.first().url() != "QtQuick3D") { - m_requiredImports.prepend(Import::createLibraryImport( - "QtQuick3D", impVersionStr)); - } + if (impVersionMajor > 0 && m_requiredImports.first() != "QtQuick3D") + m_requiredImports.prepend("QtQuick3D"); } if (impVersionMajor > 0 && impVersionMajor < 6) { pd.iconFile = iconFileName; @@ -683,66 +681,42 @@ void ItemLibraryAssetImporter::finalizeQuick3DImport() QFuture result; if (modelManager) { QmlJS::PathsAndLanguages pathToScan; - pathToScan.maybeInsert(Utils::FilePath::fromString(m_importPath)); - result = Utils::runAsync(&QmlJS::ModelManagerInterface::importScan, - modelManager->workingCopy(), pathToScan, - modelManager, true, true, true); + pathToScan.maybeInsert(::Utils::FilePath::fromString(m_importPath)); + result = ::Utils::runAsync(&QmlJS::ModelManagerInterface::importScan, + modelManager->workingCopy(), + pathToScan, + modelManager, + true, + true, + true); } // First we have to wait a while to ensure qmljs detects new files and updates its - // internal model. Then we make a non-change to the document to trigger qmljs snapshot - // update. There is an inbuilt delay before rewriter change actually updates the data - // model, so we need to wait for another moment to allow the change to take effect. - // Otherwise subsequent subcomponent manager update won't detect new imports properly. + // internal model. Then we force amend on rewriter to trigger qmljs snapshot update. QTimer *timer = new QTimer(parent()); static int counter; counter = 0; timer->callOnTimeout([this, timer, progressTitle, model, result]() { if (!isCancelled()) { - notifyProgress(++counter, progressTitle); - if (counter < 50) { + notifyProgress(++counter * 2, progressTitle); + if (counter < 49) { if (result.isCanceled() || result.isFinished()) - counter = 49; // skip to next step - } else if (counter == 50) { + counter = 48; // skip to next step + } else if (counter == 49) { QmlDesignerPlugin::instance()->documentManager().resetPossibleImports(); - model->rewriterView()->textModifier()->replace(0, 0, {}); - } else if (counter < 100) { + model->rewriterView()->forceAmend(); try { - const Imports posImports = model->possibleImports(); - const Imports currentImports = model->imports(); - Imports newImportsToAdd; - - for (auto &imp : std::as_const(m_requiredImports)) { - const bool isPos = Utils::contains(posImports, [imp](const Import &posImp) { - return posImp.url() == imp.url(); - }); - const bool isCur = Utils::contains(currentImports, [imp](const Import &curImp) { - return curImp.url() == imp.url(); - }); - if (!(isPos || isCur)) - return; - // Check again with 'contains' to ensure we insert latest version - if (!currentImports.contains(imp)) - newImportsToAdd.append(imp); - } - if (counter == 99) + RewriterTransaction transaction = model->rewriterView()->beginRewriterTransaction( + QByteArrayLiteral("ItemLibraryAssetImporter::finalizeQuick3DImport")); + bool success = Utils::addImportsWithCheck(m_requiredImports, model); + if (!success) addError(tr("Failed to insert import statement into qml document.")); - else - counter = 99; - if (!newImportsToAdd.isEmpty()) { - RewriterTransaction transaction - = model->rewriterView()->beginRewriterTransaction( - QByteArrayLiteral("ItemLibraryAssetImporter::finalizeQuick3DImport")); - - model->changeImports(newImportsToAdd, {}); - transaction.commit(); - } + transaction.commit(); } catch (const RewritingException &e) { addError(tr("Failed to update imports: %1").arg(e.description())); - counter = 99; } - } else if (counter >= 100) { + } else if (counter >= 50) { if (!m_overwrittenImports.isEmpty()) model->rewriterView()->emitCustomNotification("asset_import_update"); timer->stop(); @@ -752,7 +726,7 @@ void ItemLibraryAssetImporter::finalizeQuick3DImport() timer->stop(); } }); - timer->start(50); + timer->start(100); } else { notifyFinished(); } diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.h b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.h index 94413af392e..a53f0c9de1b 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.h +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.h @@ -107,7 +107,7 @@ private: int m_currentImportId = 0; QHash m_parseData; QString m_progressTitle; - Imports m_requiredImports; + QStringList m_requiredImports; QList m_puppetQueue; }; } // QmlDesigner diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp index 127bd4af534..f98284f61d6 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -59,7 +60,7 @@ namespace QmlDesigner { static QString propertyEditorResourcesPath() { #ifdef SHARE_QML_PATH - if (Utils::qtcEnvironmentVariableIsSet("LOAD_QML_FROM_SOURCE")) + if (::Utils::qtcEnvironmentVariableIsSet("LOAD_QML_FROM_SOURCE")) return QLatin1String(SHARE_QML_PATH) + "/propertyEditorQmlSources"; #endif return Core::ICore::resourcePath("qmldesigner/propertyEditorQmlSources").toString(); @@ -80,26 +81,16 @@ bool ItemLibraryWidget::eventFilter(QObject *obj, QEvent *event) ItemLibraryEntry entry = m_itemToDrag.value(); // For drag to be handled correctly, we must have the component properly imported // beforehand, so we import the module immediately when the drag starts - if (!entry.requiredImport().isEmpty()) { - // We don't know if required import is library of file import, so try both. - Import libImport = Import::createLibraryImport(entry.requiredImport()); - Import fileImport = Import::createFileImport(entry.requiredImport()); - if (!m_model->hasImport(libImport, true, true) - && !m_model->hasImport(fileImport, true, true)) { - const Imports possImports = m_model->possibleImports(); - for (const auto &possImport : possImports) { - if ((!possImport.url().isEmpty() && possImport.url() == libImport.url()) - || (!possImport.file().isEmpty() && possImport.file() == fileImport.file())) { - m_model->changeImports({possImport}, {}); - break; - } - } - } + if (!entry.requiredImport().isEmpty() + && !Utils::addImportWithCheck(entry.requiredImport(), m_model)) { + qWarning() << __FUNCTION__ << "Required import adding failed:" + << entry.requiredImport(); } if (model) { model->startDrag(m_itemLibraryModel->getMimeData(entry), - Utils::StyleHelper::dpiSpecificImageFile(entry.libraryEntryIconPath())); + ::Utils::StyleHelper::dpiSpecificImageFile( + entry.libraryEntryIconPath())); } m_itemToDrag = {}; @@ -154,7 +145,7 @@ ItemLibraryWidget::ItemLibraryWidget(AsynchronousImageCache &imageCache) updateSearch(); setStyleSheet(Theme::replaceCssColors( - QString::fromUtf8(Utils::FileReader::fetchQrc(":/qmldesigner/stylesheet.css")))); + QString::fromUtf8(::Utils::FileReader::fetchQrc(":/qmldesigner/stylesheet.css")))); m_qmlSourceUpdateShortcut = new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_F5), this); connect(m_qmlSourceUpdateShortcut, &QShortcut::activated, this, &ItemLibraryWidget::reloadQmlSource); @@ -176,10 +167,9 @@ ItemLibraryWidget::ItemLibraryWidget(AsynchronousImageCache &imageCache) {"itemLibraryIconHeight", m_itemIconSize.height()}, {"rootView", QVariant::fromValue(this)}, {"widthLimit", HORIZONTAL_LAYOUT_WIDTH_LIMIT}, - {"highlightColor", Utils::StyleHelper::notTooBrightHighlightColor()}, + {"highlightColor", ::Utils::StyleHelper::notTooBrightHighlightColor()}, {"tooltipBackend", QVariant::fromValue(m_previewTooltipBackend.get())}}); - reloadQmlSource(); } @@ -303,7 +293,7 @@ void ItemLibraryWidget::setModel(Model *model) QString ItemLibraryWidget::qmlSourcesPath() { #ifdef SHARE_QML_PATH - if (Utils::qtcEnvironmentVariableIsSet("LOAD_QML_FROM_SOURCE")) + if (::Utils::qtcEnvironmentVariableIsSet("LOAD_QML_FROM_SOURCE")) return QLatin1String(SHARE_QML_PATH) + "/itemLibraryQmlSources"; #endif return Core::ICore::resourcePath("qmldesigner/itemLibraryQmlSources").toString(); diff --git a/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp b/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp index 9f664429039..16dd2242863 100644 --- a/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp +++ b/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp @@ -9,23 +9,24 @@ #include "qmldesignerplugin.h" #include "assetslibrarywidget.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 @@ -210,7 +211,7 @@ QVariant NavigatorTreeModel::data(const QModelIndex &index, int role) const return modelNode.displayName(); } else if (role == Qt::DecorationRole) { if (currentQmlObjectNode.hasError()) - return Utils::Icons::WARNING.icon(); + return ::Utils::Icons::WARNING.icon(); return modelNode.typeIcon(); @@ -314,14 +315,14 @@ QList NavigatorTreeModel::filteredList(const NodeListProperty &proper if (m_nameFilter.isEmpty()) { nameFilteredList = propertyNodes; } else { - nameFilteredList.append(Utils::filtered(propertyNodes, [&] (const ModelNode &arg){ + nameFilteredList.append(::Utils::filtered(propertyNodes, [&](const ModelNode &arg) { const bool value = m_nameFilteredList.contains(arg); return value; })); } if (filter) { - list.append(Utils::filtered(nameFilteredList, [] (const ModelNode &arg) { + list.append(::Utils::filtered(nameFilteredList, [](const ModelNode &arg) { const bool value = (QmlItemNode::isValidQmlItemNode(arg) || NodeHints::fromModelNode(arg).visibleInNavigator()) && arg.id() != Constants::MATERIAL_LIB_ID; return value; @@ -899,18 +900,8 @@ ModelNode NavigatorTreeModel::handleItemLibraryFontDrop(const QString &fontFamil void NavigatorTreeModel::addImport(const QString &importName) { - Import import = Import::createLibraryImport(importName); - if (!m_view->model()->hasImport(import, true, true)) { - const Imports possImports = difference(m_view->model()->possibleImports(), - m_view->model()->imports()); - for (const auto &possImport : possImports) { - if (possImport.url() == import.url()) { - import = possImport; - m_view->model()->changeImports({import}, {}); - break; - } - } - } + if (!Utils::addImportWithCheck(importName, m_view->model())) + qWarning() << __FUNCTION__ << "Adding import failed:" << importName; } bool QmlDesigner::NavigatorTreeModel::moveNodeToParent(const NodeAbstractProperty &targetProperty, @@ -1269,12 +1260,12 @@ static QList collectParents(const QList &modelNodes) } } - return Utils::toList(parents); + return ::Utils::toList(parents); } QList NavigatorTreeModel::nodesToPersistentIndex(const QList &modelNodes) { - return Utils::transform(modelNodes, [this](const ModelNode &modelNode) { + return ::Utils::transform(modelNodes, [this](const ModelNode &modelNode) { return QPersistentModelIndex(indexForModelNode(modelNode)); }); } diff --git a/src/plugins/qmldesigner/designercore/model/modelutils.cpp b/src/plugins/qmldesigner/designercore/model/modelutils.cpp new file mode 100644 index 00000000000..2e454527b88 --- /dev/null +++ b/src/plugins/qmldesigner/designercore/model/modelutils.cpp @@ -0,0 +1,93 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "modelutils.h" + +#include + +#include + +namespace QmlDesigner::Utils { + +namespace { + +enum class ImportError { EmptyImportName, HasAlreadyImport, NoModule }; + +::Utils::expected findImport(const QString &importName, + const std::function &predicate, + const Imports &imports, + const Imports &modules) +{ + if (importName.isEmpty()) + return ::Utils::make_unexpected(ImportError::EmptyImportName); + + auto hasName = [&](const auto &import) { + return import.url() == importName || import.file() == importName; + }; + + bool hasImport = std::any_of(imports.begin(), imports.end(), hasName); + + if (hasImport) + return ::Utils::make_unexpected(ImportError::HasAlreadyImport); + + auto foundModule = std::find_if(modules.begin(), modules.end(), [&](const Import &import) { + return hasName(import) && predicate(import); + }); + + if (foundModule == modules.end()) + return ::Utils::make_unexpected(ImportError::NoModule); + + return *foundModule; +} + +} // namespace + +bool addImportWithCheck(const QString &importName, + const std::function &predicate, + Model *model) +{ + return addImportsWithCheck({importName}, predicate, model); +} + +bool addImportWithCheck(const QString &importName, Model *model) +{ + return addImportWithCheck( + importName, [](const Import &) { return true; }, model); +} + +bool addImportsWithCheck(const QStringList &importNames, Model *model) +{ + return addImportsWithCheck( + importNames, [](const Import &) { return true; }, model); +} + +bool addImportsWithCheck(const QStringList &importNames, + const std::function &predicate, + Model *model) +{ + const Imports &imports = model->imports(); + const Imports &modules = model->possibleImports(); + + Imports importsToAdd; + importsToAdd.reserve(importNames.size()); + + for (const QString &importName : importNames) { + auto import = findImport(importName, predicate, imports, modules); + + if (import) { + importsToAdd.push_back(*import); + } else { + if (import.error() == ImportError::NoModule) + return false; + else + continue; + } + } + + if (!importsToAdd.isEmpty()) + model->changeImports(std::move(importsToAdd), {}); + + return true; +} + +} // namespace QmlDesigner::Utils diff --git a/src/plugins/qmldesigner/designercore/model/modelutils.h b/src/plugins/qmldesigner/designercore/model/modelutils.h new file mode 100644 index 00000000000..58055f39065 --- /dev/null +++ b/src/plugins/qmldesigner/designercore/model/modelutils.h @@ -0,0 +1,24 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include "qmldesignercorelib_global.h" + +#include +#include + +#include + +namespace QmlDesigner::Utils { + +QMLDESIGNERCORE_EXPORT bool addImportsWithCheck(const QStringList &importNames, + const std::function &predicate, + Model *model); +QMLDESIGNERCORE_EXPORT bool addImportsWithCheck(const QStringList &importNames, Model *model); +QMLDESIGNERCORE_EXPORT bool addImportWithCheck(const QString &importName, + const std::function &predicate, + Model *model); +QMLDESIGNERCORE_EXPORT bool addImportWithCheck(const QString &importName, Model *model); + +} // namespace QmlDesigner::Utils From 3ff90526f5c84b40864fa467d0159cd14ac8baee Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Sat, 22 Apr 2023 01:44:27 +0200 Subject: [PATCH 159/192] QmlDesigner: Introduce SkipIterator Instead of having a cache using an iterator skipping entries is much easier because the cache has not to be anymore updated. When we get C++ 20 ranges use them instead. Change-Id: If5b45c53bbd0b12138328862ac152788ffd911b2 Reviewed-by: Thomas Hartmann Reviewed-by: Mahmoud Badri Reviewed-by: --- src/plugins/qmldesigner/CMakeLists.txt | 1 + .../designercore/model/abstractview.cpp | 6 -- .../qmldesigner/designercore/model/model.cpp | 13 +--- .../qmldesigner/designercore/model/model_p.h | 41 +++++++--- .../designercore/model/skipiterator.h | 77 +++++++++++++++++++ 5 files changed, 111 insertions(+), 27 deletions(-) create mode 100644 src/plugins/qmldesigner/designercore/model/skipiterator.h diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index b9459aeb1b5..c7989fda36d 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -371,6 +371,7 @@ extend_qtc_library(QmlDesignerCore rewriteactioncompressor.h rewriterview.cpp signalhandlerproperty.cpp + skipiterator.h stylesheetmerger.cpp textmodifier.cpp texttomodelmerger.cpp diff --git a/src/plugins/qmldesigner/designercore/model/abstractview.cpp b/src/plugins/qmldesigner/designercore/model/abstractview.cpp index 07692f4a10c..a09db03dacd 100644 --- a/src/plugins/qmldesigner/designercore/model/abstractview.cpp +++ b/src/plugins/qmldesigner/designercore/model/abstractview.cpp @@ -143,9 +143,6 @@ The default implementation is setting the reference of the model to the view. void AbstractView::modelAttached(Model *model) { setModel(model); - - if (model) - model->d->updateEnabledViews(); } /*! @@ -617,9 +614,6 @@ bool AbstractView::isEnabled() const void AbstractView::setEnabled(bool b) { m_enabled = b; - - if (model()) - model()->d->updateEnabledViews(); } QList AbstractView::allModelNodes() const diff --git a/src/plugins/qmldesigner/designercore/model/model.cpp b/src/plugins/qmldesigner/designercore/model/model.cpp index e630cc96d84..fcdb66b8029 100644 --- a/src/plugins/qmldesigner/designercore/model/model.cpp +++ b/src/plugins/qmldesigner/designercore/model/model.cpp @@ -96,7 +96,6 @@ void ModelPrivate::detachAllViews() detachView(view.data(), true); m_viewList.clear(); - updateEnabledViews(); if (m_nodeInstanceView) { m_nodeInstanceView->modelAboutToBeDetached(m_model); @@ -299,9 +298,9 @@ void ModelPrivate::removeNodeFromModel(const InternalNodePointer &node) m_internalIdNodeHash.remove(node->internalId); } -const QList> ModelPrivate::enabledViews() const +EnabledViewRange ModelPrivate::enabledViews() const { - return m_enabledViewList; + return EnabledViewRange{m_viewList}; } void ModelPrivate::removeAllSubNodes(const InternalNodePointer &node) @@ -754,7 +753,6 @@ void ModelPrivate::detachView(AbstractView *view, bool notifyView) if (notifyView) view->modelAboutToBeDetached(m_model); m_viewList.removeOne(view); - updateEnabledViews(); } void ModelPrivate::notifyNodeCreated(const InternalNodePointer &newInternalNodePointer) @@ -1317,13 +1315,6 @@ InternalNodePointer ModelPrivate::currentTimelineNode() const return m_currentTimelineNode; } -void ModelPrivate::updateEnabledViews() -{ - m_enabledViewList = Utils::filtered(m_viewList, [](QPointer view) { - return view->isEnabled(); - }); -} - InternalNodePointer ModelPrivate::nodeForId(const QString &id) const { return m_idNodeHash.value(id); diff --git a/src/plugins/qmldesigner/designercore/model/model_p.h b/src/plugins/qmldesigner/designercore/model/model_p.h index d1678192804..ce2a49fc42d 100644 --- a/src/plugins/qmldesigner/designercore/model/model_p.h +++ b/src/plugins/qmldesigner/designercore/model/model_p.h @@ -3,17 +3,20 @@ #pragma once +#include "qmldesignercorelib_global.h" + +#include "abstractview.h" +#include "metainfo.h" +#include "modelnode.h" +#include "skipiterator.h" + #include #include #include #include #include -#include "modelnode.h" -#include "abstractview.h" -#include "metainfo.h" - -#include "qmldesignercorelib_global.h" +#include QT_BEGIN_NAMESPACE class QPlainTextEdit; @@ -64,7 +67,28 @@ private: QPointer m_model; }; -class ModelPrivate : public QObject { +struct Increment +{ + using iterator = QList>::const_iterator; + auto operator()(iterator current) { + return std::find_if(std::next(current), + end, + [] (iterator::reference &view) { return view && view->isEnabled(); }); + } + + iterator end; +}; + +class EnabledViewRange : public SkipRange>, Increment> +{ +public: + EnabledViewRange(const container &views) + : base{views, Increment{views.end()}} + {} +}; + +class ModelPrivate : public QObject +{ Q_OBJECT friend Model; @@ -249,8 +273,6 @@ public: InternalNodePointer currentStateNode() const; InternalNodePointer currentTimelineNode() const; - void updateEnabledViews(); - private: void removePropertyWithoutNotification(const InternalPropertyPointer &property); void removeAllSubNodes(const InternalNodePointer &node); @@ -259,7 +281,7 @@ private: QList toModelNodeList(const QList &nodeList, AbstractView *view) const; QVector toModelNodeVector(const QVector &nodeVector, AbstractView *view) const; QVector toInternalNodeVector(const QVector &modelNodeVector) const; - const QList> enabledViews() const; + EnabledViewRange enabledViews() const; public: NotNullPointer projectStorage = nullptr; @@ -271,7 +293,6 @@ private: Imports m_possibleImportList; Imports m_usedImportList; QList> m_viewList; - QList> m_enabledViewList; QList m_selectedInternalNodeList; QHash m_idNodeHash; QHash m_internalIdNodeHash; diff --git a/src/plugins/qmldesigner/designercore/model/skipiterator.h b/src/plugins/qmldesigner/designercore/model/skipiterator.h new file mode 100644 index 00000000000..11efb7d97f0 --- /dev/null +++ b/src/plugins/qmldesigner/designercore/model/skipiterator.h @@ -0,0 +1,77 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include + +namespace QmlDesigner { + +template +class SkipIterator +{ +public: + using iterator_category = std::forward_iterator_tag; + using difference_type = qsizetype; + using value_type = typename BaseIterator::value_type; + using pointer = typename BaseIterator::pointer; + using reference = typename BaseIterator::reference; + + SkipIterator() = default; + SkipIterator(BaseIterator current, Increment increment) + : m_current{current} + , m_increment{std::move(increment)} + {} + + SkipIterator operator++() + { + m_current = m_increment(m_current); + return *this; + } + + SkipIterator operator++(int) + { + auto tmp = *this; + m_current = m_increment(m_current); + return tmp; + } + + reference operator*() const { return *m_current; } + pointer operator->() const { return m_current.operator->(); } + + friend bool operator==(const SkipIterator &first, const SkipIterator &second) + { + return first.m_current == second.m_current; + } + + friend bool operator!=(const SkipIterator &first, const SkipIterator &second) + { + return first.m_current != second.m_current; + } + +private: + BaseIterator m_current = {}; + Increment m_increment; +}; + +template +class SkipRange +{ +public: + using container = Container; + using base = SkipRange; + using iterator = SkipIterator; + + SkipRange(const Container &container, Increment increment) + : m_begin{container.begin(), increment} + , m_end{container.end(), increment} + {} + + iterator begin() const { return m_begin; } + iterator end() const { return m_end; } + +private: + iterator m_begin; + iterator m_end; +}; +} // namespace QmlDesigner From 2ded8a3842ef4d8cf200da604ccec24a9c468984 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Fri, 12 May 2023 10:39:25 +0300 Subject: [PATCH 160/192] QmlDesigner: Fix denoiser path for mac Fixes: QDS-9870 Change-Id: Id847cb6efad0bf1d57e6f159adde68dec1843990 Reviewed-by: Thomas Hartmann --- .../qml2puppet/instances/qt5bakelightsnodeinstanceserver.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/tools/qml2puppet/qml2puppet/instances/qt5bakelightsnodeinstanceserver.cpp b/src/tools/qml2puppet/qml2puppet/instances/qt5bakelightsnodeinstanceserver.cpp index 79b3a4f920e..190ad750bf9 100644 --- a/src/tools/qml2puppet/qml2puppet/instances/qt5bakelightsnodeinstanceserver.cpp +++ b/src/tools/qml2puppet/qml2puppet/instances/qt5bakelightsnodeinstanceserver.cpp @@ -150,9 +150,7 @@ void Qt5BakeLightsNodeInstanceServer::runDenoiser() QString binPath = QLibraryInfo::path(QLibraryInfo::BinariesPath); #if defined(Q_OS_WIN) binPath += "/qlmdenoiser.exe"; -#elif defined(Q_OS_MACOS) - binPath += "/qlmdenoiser.app/Contents/MacOS/qlmdenoiser"; -#else +#elif binPath += "/qlmdenoiser"; #endif From ac4219c5cd0b332e579632313e28e47f6a658a74 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Mon, 8 May 2023 17:20:11 +0300 Subject: [PATCH 161/192] Qmldesigner: Allow exposing nodes as aliases via UI for light baking Model and light nodes from subcomponents can now be exposed with a single button press in light baking setup dialog. Fixes: QDS-9831 Change-Id: Ifcc69955a2dc9378111387694e42f0cdc70173f3 Reviewed-by: Reviewed-by: Thomas Hartmann Reviewed-by: Mahmoud Badri --- .../edit3dQmlSource/BakeLightsSetupDialog.qml | 42 ++++++--- .../components/edit3d/bakelights.cpp | 87 ++++++++++++++++++- .../components/edit3d/bakelights.h | 1 + .../components/edit3d/bakelightsdatamodel.cpp | 82 +++++++++++------ .../components/edit3d/bakelightsdatamodel.h | 9 +- .../include/plaintexteditmodifier.h | 11 ++- .../model/plaintexteditmodifier.cpp | 46 +++++++++- 7 files changed, 232 insertions(+), 46 deletions(-) diff --git a/share/qtcreator/qmldesigner/edit3dQmlSource/BakeLightsSetupDialog.qml b/share/qtcreator/qmldesigner/edit3dQmlSource/BakeLightsSetupDialog.qml index ab4d89374be..1adb1285f81 100644 --- a/share/qtcreator/qmldesigner/edit3dQmlSource/BakeLightsSetupDialog.qml +++ b/share/qtcreator/qmldesigner/edit3dQmlSource/BakeLightsSetupDialog.qml @@ -44,25 +44,42 @@ Rectangle { anchors.margins: 4 clip: true - model: bakeModel spacing: 5 delegate: Row { spacing: 12 - enabled: !manualCheckBox.checked Text { - text: nodeId + text: displayId color: StudioTheme.Values.themeTextColor - width: 200 + width: isTitle ? listView.width : listView.width - 320 + clip: true font.bold: isTitle verticalAlignment: Qt.AlignVCenter horizontalAlignment: Qt.AlignLeft height: bakeModeCombo.height leftPadding: isTitle ? 0 : 8 + + ToolTipArea { + anchors.fill: parent + tooltip: displayId // Useful for long ids + } + } + + Button { + visible: isUnexposed + text: qsTr("Expose models and lights") + leftPadding: StudioTheme.Values.dialogButtonPadding + rightPadding: StudioTheme.Values.dialogButtonPadding + height: bakeModeCombo.height + onClicked: { + if (!manualCheckBox.checked) + rootView.apply() + rootView.exposeModelsAndLights(nodeId) + } } StudioControls.ComboBox { @@ -73,7 +90,7 @@ Rectangle { ListElement { text: qsTr("Bake All"); value: "Light.BakeModeAll" } } - visible: !isModel && !isTitle + visible: !isModel && !isTitle && !isUnexposed textRole: "text" valueRole: "value" currentIndex: bakeMode === "Light.BakeModeAll" @@ -88,7 +105,7 @@ Rectangle { } StudioControls.CheckBox { - visible: isModel && !isTitle + visible: isModel && !isTitle && !isUnexposed checked: inUse text: qsTr("In Use") actionIndicatorVisible: false @@ -101,7 +118,7 @@ Rectangle { } StudioControls.CheckBox { - visible: isModel && !isTitle + visible: isModel && !isTitle && !isUnexposed checked: isEnabled text: qsTr("Enabled") actionIndicatorVisible: false @@ -116,7 +133,7 @@ Rectangle { Row { width: resolutionLabel.width + resolutionValue.width + StudioTheme.Values.sectionRowSpacing spacing: StudioTheme.Values.sectionRowSpacing - visible: isModel && !isTitle + visible: isModel && !isTitle && !isUnexposed Text { id: resolutionLabel text: qsTr("Resolution:") @@ -170,7 +187,8 @@ Rectangle { Button { id: cancelButton text: qsTr("Cancel") - anchors.margins: StudioTheme.Values.dialogButtonPadding + leftPadding: StudioTheme.Values.dialogButtonPadding + rightPadding: StudioTheme.Values.dialogButtonPadding height: manualCheckBox.height onClicked: rootView.cancel() } @@ -178,7 +196,8 @@ Rectangle { Button { id: applyButton text: qsTr("Apply & Close") - anchors.margins: StudioTheme.Values.dialogButtonPadding + leftPadding: StudioTheme.Values.dialogButtonPadding + rightPadding: StudioTheme.Values.dialogButtonPadding height: manualCheckBox.height onClicked: { if (!manualCheckBox.checked) @@ -190,7 +209,8 @@ Rectangle { Button { id: bakeButton text: qsTr("Bake") - anchors.margins: StudioTheme.Values.dialogButtonPadding + leftPadding: StudioTheme.Values.dialogButtonPadding + rightPadding: StudioTheme.Values.dialogButtonPadding height: manualCheckBox.height onClicked: { if (!manualCheckBox.checked) diff --git a/src/plugins/qmldesigner/components/edit3d/bakelights.cpp b/src/plugins/qmldesigner/components/edit3d/bakelights.cpp index 73e2f315b03..1b2ea3e7880 100644 --- a/src/plugins/qmldesigner/components/edit3d/bakelights.cpp +++ b/src/plugins/qmldesigner/components/edit3d/bakelights.cpp @@ -6,6 +6,7 @@ #include "abstractview.h" #include "bakelightsdatamodel.h" #include "bakelightsconnectionmanager.h" +#include "bindingproperty.h" #include "documentmanager.h" #include "modelnode.h" #include "nodeabstractproperty.h" @@ -15,17 +16,20 @@ #include "rewriterview.h" #include "variantproperty.h" +#include + +#include + #include #include #include #include -#include - #include #include #include #include +#include #include #include #include @@ -214,6 +218,85 @@ void BakeLights::rebake() }); } +void BakeLights::exposeModelsAndLights(const QString &nodeId) +{ + ModelNode compNode = m_view->modelNodeForId(nodeId); + if (!compNode.isValid() || !compNode.isComponent() + || compNode.metaInfo().componentFileName().isEmpty()) { + return; + } + + RewriterView rewriter{m_view->externalDependencies(), RewriterView::Amend}; + ModelPointer compModel = QmlDesigner::Model::create("QtQuick/Item", 2, 1); + const QString compFile = compNode.metaInfo().componentFileName(); + const Utils::FilePath compFilePath = Utils::FilePath::fromString(compFile); + QByteArray src = compFilePath.fileContents().value(); + + compModel->setFileUrl(QUrl::fromLocalFile(compFile)); + + auto textDocument = std::make_unique(QString::fromUtf8(src)); + auto modifier = std::make_unique( + textDocument.get(), QTextCursor{textDocument.get()}); + + rewriter.setTextModifier(modifier.get()); + compModel->setRewriterView(&rewriter); + + if (!rewriter.rootModelNode().isValid() || !rewriter.errors().isEmpty()) + return; + + QString originalText = modifier->text(); + QStringList idList; + + rewriter.executeInTransaction(__FUNCTION__, [&]() { + QList nodes = rewriter.rootModelNode().allSubModelNodes(); + for (ModelNode &node : nodes) { + if (node.metaInfo().isQtQuick3DModel() || node.metaInfo().isQtQuick3DLight()) { + QString idStr = node.id(); + if (idStr.isEmpty()) { + const QString type = node.metaInfo().isQtQuick3DModel() ? "model" : "light"; + idStr = compModel->generateNewId(type); + node.setIdWithoutRefactoring(idStr); + } + idList.append(idStr); + } + } + }); + + rewriter.executeInTransaction(__FUNCTION__, [&]() { + for (const QString &id : std::as_const(idList)) { + ModelNode node = rewriter.modelNodeForId(id); + if (!node.isValid()) + continue; + rewriter.rootModelNode().bindingProperty(id.toUtf8()) + .setDynamicTypeNameAndExpression("alias", id); + } + }); + + rewriter.forceAmend(); + + QString newText = modifier->text(); + if (newText != originalText) { + QSaveFile saveFile(compFile); + if (saveFile.open(QIODevice::WriteOnly | QIODevice::Truncate)) { + saveFile.write(newText.toUtf8()); + saveFile.commit(); + } else { + qWarning() << __FUNCTION__ << "Failed to save changes to:" << compFile; + } + } + + QmlJS::ModelManagerInterface *modelManager = QmlJS::ModelManagerInterface::instance(); + QmlJS::Document::Ptr doc = rewriter.document()->ptr(); + modelManager->updateDocument(doc); + + m_view->model()->rewriterView()->forceAmend(); + + compModel->setRewriterView({}); + + // Rebake to relaunch setup dialog with updated properties + rebake(); +} + void BakeLights::showSetupDialog() { if (!m_dataModel) diff --git a/src/plugins/qmldesigner/components/edit3d/bakelights.h b/src/plugins/qmldesigner/components/edit3d/bakelights.h index a219454076f..08dd8dda413 100644 --- a/src/plugins/qmldesigner/components/edit3d/bakelights.h +++ b/src/plugins/qmldesigner/components/edit3d/bakelights.h @@ -32,6 +32,7 @@ public: Q_INVOKABLE void bakeLights(); Q_INVOKABLE void apply(); Q_INVOKABLE void rebake(); + Q_INVOKABLE void exposeModelsAndLights(const QString &nodeId); void raiseDialog(); diff --git a/src/plugins/qmldesigner/components/edit3d/bakelightsdatamodel.cpp b/src/plugins/qmldesigner/components/edit3d/bakelightsdatamodel.cpp index 2ab5ba4b017..75d69df184a 100644 --- a/src/plugins/qmldesigner/components/edit3d/bakelightsdatamodel.cpp +++ b/src/plugins/qmldesigner/components/edit3d/bakelightsdatamodel.cpp @@ -7,12 +7,16 @@ #include "bakelights.h" #include "bindingproperty.h" #include "enumeration.h" +#include "externaldependenciesinterface.h" #include "model.h" #include "modelnode.h" #include "nodelistproperty.h" #include "nodemetainfo.h" +#include "qmlobjectnode.h" #include "variantproperty.h" +#include +#include #include #include @@ -38,13 +42,15 @@ int BakeLightsDataModel::rowCount(const QModelIndex &) const QHash BakeLightsDataModel::roleNames() const { static const QHash roles { - {Qt::UserRole + 1, "nodeId"}, - {Qt::UserRole + 2, "isModel"}, - {Qt::UserRole + 3, "isEnabled"}, - {Qt::UserRole + 4, "inUse"}, - {Qt::UserRole + 5, "isTitle"}, - {Qt::UserRole + 6, "resolution"}, - {Qt::UserRole + 7, "bakeMode"} + {Qt::UserRole + 1, "displayId"}, + {Qt::UserRole + 2, "nodeId"}, + {Qt::UserRole + 3, "isModel"}, + {Qt::UserRole + 4, "isEnabled"}, + {Qt::UserRole + 5, "inUse"}, + {Qt::UserRole + 6, "isTitle"}, + {Qt::UserRole + 7, "isUnexposed"}, + {Qt::UserRole + 8, "resolution"}, + {Qt::UserRole + 9, "bakeMode"} }; return roles; } @@ -57,7 +63,7 @@ QVariant BakeLightsDataModel::data(const QModelIndex &index, int role) const QByteArray roleName = roleNames().value(role); const BakeData &bakeData = m_dataList[index.row()]; - if (roleName == "nodeId") { + if (roleName == "displayId") { const QString id = bakeData.id; const PropertyName aliasProp = bakeData.aliasProp; if (aliasProp.isEmpty()) @@ -67,6 +73,9 @@ QVariant BakeLightsDataModel::data(const QModelIndex &index, int role) const return {}; } + if (roleName == "nodeId") + return bakeData.id; + if (roleName == "isModel") return bakeData.isModel; @@ -79,6 +88,9 @@ QVariant BakeLightsDataModel::data(const QModelIndex &index, int role) const if (roleName == "isTitle") return bakeData.isTitle; + if (roleName == "isUnexposed") + return bakeData.isUnexposed; + if (roleName == "resolution") return bakeData.resolution; @@ -138,11 +150,15 @@ void BakeLightsDataModel::reset() QList lightList; QList compModelList; QList compLightList; + QList unexposedList; // Note: We are always loading base state values for baking. If users want to bake // differently for different states, they need to setup things manually for now. // Same goes if they want to use any unusual bindings in baking properties. for (const auto &node : std::as_const(nodes)) { + if (QmlObjectNode(node).hasError()) + continue; + BakeData data; data.id = node.id(); if (data.id.isEmpty()) @@ -177,6 +193,7 @@ void BakeLightsDataModel::reset() if (node.isComponent()) { // Every component can expose multiple aliases // We ignore baking properties defined inside the component (no visibility there) + bool hasExposedProps = false; const QList props = node.properties(); PropertyMetaInfos metaInfos = node.metaInfo().properties(); for (const PropertyMetaInfo &mi : metaInfos) { @@ -185,6 +202,7 @@ void BakeLightsDataModel::reset() propData.id = data.id; propData.aliasProp = mi.name(); if (mi.propertyType().isQtQuick3DModel()) { + hasExposedProps = true; propData.isModel = true; PropertyName dotName = mi.name() + '.'; for (const AbstractProperty &prop : props) { @@ -207,6 +225,7 @@ void BakeLightsDataModel::reset() } compModelList.append(propData); } else if (mi.propertyType().isQtQuick3DLight()) { + hasExposedProps = true; PropertyName dotName = mi.name() + '.'; for (const AbstractProperty &prop : props) { if (prop.name().startsWith(dotName)) { @@ -224,6 +243,22 @@ void BakeLightsDataModel::reset() } } } + + if (!hasExposedProps && node.metaInfo().isFileComponent() + && node.metaInfo().isQtQuick3DNode()) { + const QString compFile = node.metaInfo().componentFileName(); + const QString projPath = m_view->externalDependencies().currentProjectDirPath(); + if (compFile.startsWith(projPath)) { + // Quick and dirty scan of the component source to check if it potentially has + // models or lights. + QByteArray src = Utils::FilePath::fromString(compFile).fileContents().value(); + src = src.mid(src.indexOf('{')); // Skip root element + if (src.contains("Model {") || src.contains("Light {")) { + data.isUnexposed = true; + unexposedList.append(data); + } + } + } } } @@ -237,6 +272,7 @@ void BakeLightsDataModel::reset() sortList(lightList); sortList(compModelList); sortList(compLightList); + sortList(unexposedList); BakeData titleData; titleData.isTitle = true; @@ -249,6 +285,12 @@ void BakeLightsDataModel::reset() m_dataList.append(modelList); m_dataList.append(compModelList); + if (!unexposedList.isEmpty()) { + titleData.id = tr("Components with unexposed models and/or lights"); + m_dataList.append(titleData); + m_dataList.append(unexposedList); + } + endResetModel(); } @@ -301,32 +343,20 @@ void BakeLightsDataModel::apply() node.variantProperty(resolvedName).setValue(value); }; - auto setResolution = [setVariantProp](const ModelNode &node, const BakeData &data) { - setVariantProp(node, "lightmapBaseResolution", data.aliasProp, data.resolution, 1024); - }; - - auto setInUse = [setVariantProp](const ModelNode &node, const BakeData &data) { - setVariantProp(node, "usedInBakedLighting", data.aliasProp, data.inUse, false); - }; - - auto setBakeMode = [setVariantProp](const ModelNode &node, const BakeData &data) { - setVariantProp(node, "bakeMode", data.aliasProp, - QVariant::fromValue(QmlDesigner::Enumeration(data.bakeMode)), - QVariant::fromValue(QmlDesigner::Enumeration("Light", "BakeModeDisabled"))); - }; - // Commits changes to scene model m_view->executeInTransaction(__FUNCTION__, [&]() { for (const BakeData &data : std::as_const(m_dataList)) { - if (data.isTitle) + if (data.isTitle || data.isUnexposed) continue; ModelNode node = m_view->modelNodeForId(data.id); if (data.isModel) { setBakedLightmap(node, data); - setResolution(node, data); - setInUse(node, data); + setVariantProp(node, "lightmapBaseResolution", data.aliasProp, data.resolution, 1024); + setVariantProp(node, "usedInBakedLighting", data.aliasProp, data.inUse, false); } else { - setBakeMode(node, data); + setVariantProp(node, "bakeMode", data.aliasProp, + QVariant::fromValue(QmlDesigner::Enumeration(data.bakeMode)), + QVariant::fromValue(QmlDesigner::Enumeration("Light", "BakeModeDisabled"))); } } }); diff --git a/src/plugins/qmldesigner/components/edit3d/bakelightsdatamodel.h b/src/plugins/qmldesigner/components/edit3d/bakelightsdatamodel.h index 9217ae44bbf..1de341add61 100644 --- a/src/plugins/qmldesigner/components/edit3d/bakelightsdatamodel.h +++ b/src/plugins/qmldesigner/components/edit3d/bakelightsdatamodel.h @@ -18,12 +18,13 @@ class BakeLightsDataModel : public QAbstractListModel public: struct BakeData { - QString id; // node id. Also used as BakedLightmap.key - PropertyName aliasProp; // property id for component exposed models/lights - bool isModel = false; // false means light + QString id; // node id. Also used as BakedLightmap.key + PropertyName aliasProp; // property id for component exposed models/lights + bool isModel = false; // false means light bool enabled = false; bool inUse = false; - bool isTitle = false; // if true, indicates a title row in UI + bool isTitle = false; // if true, indicates a title row in UI + bool isUnexposed = false; // if true, indicates a component with unexposed models/lights int resolution = 1024; QString bakeMode; }; diff --git a/src/plugins/qmldesigner/designercore/include/plaintexteditmodifier.h b/src/plugins/qmldesigner/designercore/include/plaintexteditmodifier.h index 814137d8a77..4172e8921ee 100644 --- a/src/plugins/qmldesigner/designercore/include/plaintexteditmodifier.h +++ b/src/plugins/qmldesigner/designercore/include/plaintexteditmodifier.h @@ -91,8 +91,17 @@ public: TextEditor::TabSettings tabSettings() const override { return m_tabSettings; } -private: +protected: TextEditor::TabSettings m_tabSettings; }; +class QMLDESIGNERCORE_EXPORT IndentingTextEditModifier : public NotIndentingTextEditModifier +{ +public: + IndentingTextEditModifier(QTextDocument *document, const QTextCursor &textCursor); + + void indent(int offset, int length) override; + void indentLines(int startLine, int endLine) override; +}; + } diff --git a/src/plugins/qmldesigner/designercore/model/plaintexteditmodifier.cpp b/src/plugins/qmldesigner/designercore/model/plaintexteditmodifier.cpp index 2318c3f2cf0..a1f88b3a687 100644 --- a/src/plugins/qmldesigner/designercore/model/plaintexteditmodifier.cpp +++ b/src/plugins/qmldesigner/designercore/model/plaintexteditmodifier.cpp @@ -3,12 +3,16 @@ #include "plaintexteditmodifier.h" -#include #include -#include +#include +#include +#include + +#include #include +#include using namespace Utils; using namespace QmlDesigner; @@ -161,3 +165,41 @@ void PlainTextEditModifier::reactivateChangeSignals() emit textChanged(); } } + +IndentingTextEditModifier::IndentingTextEditModifier(QTextDocument *document, const QTextCursor &textCursor) + : NotIndentingTextEditModifier{document, textCursor} +{ + m_tabSettings = QmlJSTools::QmlJSToolsSettings::globalCodeStyle()->tabSettings(); +} + +void IndentingTextEditModifier::indent(int offset, int length) +{ + if (length == 0 || offset < 0 || offset + length >= text().length()) + return; + + int startLine = getLineInDocument(textDocument(), offset); + int endLine = getLineInDocument(textDocument(), offset + length); + + if (startLine > -1 && endLine > -1) + indentLines(startLine, endLine); +} + +void IndentingTextEditModifier::indentLines(int startLine, int endLine) +{ + if (startLine < 0) + return; + + QTextCursor tc(textDocument()); + + tc.beginEditBlock(); + for (int i = startLine; i <= endLine; i++) { + QTextBlock start = textDocument()->findBlockByNumber(i); + + if (start.isValid()) { + QmlJSEditor::Internal::Indenter indenter(textDocument()); + indenter.indentBlock(start, QChar::Null, m_tabSettings); + } + } + tc.endEditBlock(); +} + From 7a04f03bc0c180e32085383e980dd55483208731 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Thu, 11 May 2023 16:39:46 +0300 Subject: [PATCH 162/192] QmlDesigner: Persist manual baking mode value Manual baking mode is stored separately for each View3D in the scene. Fixes: QDS-9867 Change-Id: I94590c2bded8486c3b526a427930f15b364eb010 Reviewed-by: Thomas Hartmann Reviewed-by: Mahmoud Badri --- .../edit3dQmlSource/BakeLightsSetupDialog.qml | 13 +++++---- .../components/edit3d/bakelights.cpp | 27 ++++++++++++++++++- .../components/edit3d/bakelights.h | 7 +++++ .../components/edit3d/bakelightsdatamodel.cpp | 8 +++--- .../components/edit3d/bakelightsdatamodel.h | 4 +++ .../include/auxiliarydataproperties.h | 2 ++ 6 files changed, 49 insertions(+), 12 deletions(-) diff --git a/share/qtcreator/qmldesigner/edit3dQmlSource/BakeLightsSetupDialog.qml b/share/qtcreator/qmldesigner/edit3dQmlSource/BakeLightsSetupDialog.qml index 1adb1285f81..5924756bedf 100644 --- a/share/qtcreator/qmldesigner/edit3dQmlSource/BakeLightsSetupDialog.qml +++ b/share/qtcreator/qmldesigner/edit3dQmlSource/BakeLightsSetupDialog.qml @@ -76,8 +76,7 @@ Rectangle { rightPadding: StudioTheme.Values.dialogButtonPadding height: bakeModeCombo.height onClicked: { - if (!manualCheckBox.checked) - rootView.apply() + rootView.apply() rootView.exposeModelsAndLights(nodeId) } } @@ -170,13 +169,15 @@ Rectangle { StudioControls.CheckBox { id: manualCheckBox - checked: false + checked: rootView.manualMode text: qsTr("Setup baking manually") actionIndicatorVisible: false hoverEnabled: true ToolTip.visible: hovered ToolTip.text: qsTr("If checked, baking settings above are not applied on close or bake.\nInstead, user is expected to set baking properties manually.") + + onToggled: rootView.manualMode = checked } Row { @@ -200,8 +201,7 @@ Rectangle { rightPadding: StudioTheme.Values.dialogButtonPadding height: manualCheckBox.height onClicked: { - if (!manualCheckBox.checked) - rootView.apply() + rootView.apply() rootView.cancel() } } @@ -213,8 +213,7 @@ Rectangle { rightPadding: StudioTheme.Values.dialogButtonPadding height: manualCheckBox.height onClicked: { - if (!manualCheckBox.checked) - rootView.apply() + rootView.apply() rootView.bakeLights() } } diff --git a/src/plugins/qmldesigner/components/edit3d/bakelights.cpp b/src/plugins/qmldesigner/components/edit3d/bakelights.cpp index 1b2ea3e7880..c833927499b 100644 --- a/src/plugins/qmldesigner/components/edit3d/bakelights.cpp +++ b/src/plugins/qmldesigner/components/edit3d/bakelights.cpp @@ -4,6 +4,7 @@ #include "bakelights.h" #include "abstractview.h" +#include "auxiliarydataproperties.h" #include "bakelightsdatamodel.h" #include "bakelightsconnectionmanager.h" #include "bindingproperty.h" @@ -114,6 +115,19 @@ void BakeLights::raiseDialog() m_progressDialog->raise(); } +bool BakeLights::manualMode() const +{ + return m_manualMode; +} + +void BakeLights::setManualMode(bool enabled) +{ + if (m_manualMode != enabled) { + m_manualMode = enabled; + emit manualModeChanged(); + } +} + void BakeLights::bakeLights() { if (!m_view || !m_view->model()) @@ -187,7 +201,12 @@ void BakeLights::bakeLights() void BakeLights::apply() { - m_dataModel->apply(); + // Uninitialized QVariant stored instead of false to remove the aux property in that case + m_dataModel->view3dNode().setAuxiliaryData(bakeLightsManualProperty, + m_manualMode ? QVariant{true} : QVariant{}); + + if (!m_manualMode) + m_dataModel->apply(); // Create folders for lightmaps if they do not exist PropertyName loadPrefixPropName{"loadPrefix"}; @@ -304,6 +323,10 @@ void BakeLights::showSetupDialog() m_dataModel->reset(); + auto data = m_dataModel->view3dNode().auxiliaryData(bakeLightsManualProperty); + if (data) + m_manualMode = data->toBool(); + if (!m_setupDialog) { // Show non-modal progress dialog with cancel button QString path = qmlSourcesPath() + "/BakeLightsSetupDialog.qml"; @@ -375,6 +398,8 @@ void BakeLights::cleanup() delete m_nodeInstanceView; delete m_connectionManager; delete m_dataModel; + + m_manualMode = false; } void BakeLights::cancel() diff --git a/src/plugins/qmldesigner/components/edit3d/bakelights.h b/src/plugins/qmldesigner/components/edit3d/bakelights.h index 08dd8dda413..65f00acc7b0 100644 --- a/src/plugins/qmldesigner/components/edit3d/bakelights.h +++ b/src/plugins/qmldesigner/components/edit3d/bakelights.h @@ -24,6 +24,8 @@ class BakeLights : public QObject { Q_OBJECT + Q_PROPERTY(bool manualMode READ manualMode WRITE setManualMode NOTIFY manualModeChanged) + public: BakeLights(AbstractView *view); ~BakeLights(); @@ -36,12 +38,16 @@ public: void raiseDialog(); + bool manualMode() const; + void setManualMode(bool enabled); + static ModelNode resolveView3dNode(AbstractView *view); static QString resolveView3dId(AbstractView *view); signals: void finished(); void progress(const QString &msg); + void manualModeChanged(); protected: bool eventFilter(QObject *obj, QEvent *event) override; @@ -62,6 +68,7 @@ private: QPointer m_dataModel; ModelPointer m_model; QString m_view3dId; + bool m_manualMode = false; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/edit3d/bakelightsdatamodel.cpp b/src/plugins/qmldesigner/components/edit3d/bakelightsdatamodel.cpp index 75d69df184a..36d192a9244 100644 --- a/src/plugins/qmldesigner/components/edit3d/bakelightsdatamodel.cpp +++ b/src/plugins/qmldesigner/components/edit3d/bakelightsdatamodel.cpp @@ -138,13 +138,13 @@ void BakeLightsDataModel::reset() beginResetModel(); m_dataList.clear(); - ModelNode view3dNode = BakeLights::resolveView3dNode(m_view); + m_view3dNode = BakeLights::resolveView3dNode(m_view); // Find all models and lights in active View3D - QList nodes = view3dNode.allSubModelNodes(); + QList nodes = m_view3dNode.allSubModelNodes(); - if (view3dNode.hasBindingProperty("importScene")) - nodes.append(view3dNode.bindingProperty("importScene").resolveToModelNode().allSubModelNodesAndThisNode()); + if (m_view3dNode.hasBindingProperty("importScene")) + nodes.append(m_view3dNode.bindingProperty("importScene").resolveToModelNode().allSubModelNodesAndThisNode()); QList modelList; QList lightList; diff --git a/src/plugins/qmldesigner/components/edit3d/bakelightsdatamodel.h b/src/plugins/qmldesigner/components/edit3d/bakelightsdatamodel.h index 1de341add61..313c09cf459 100644 --- a/src/plugins/qmldesigner/components/edit3d/bakelightsdatamodel.h +++ b/src/plugins/qmldesigner/components/edit3d/bakelightsdatamodel.h @@ -2,6 +2,7 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0 #pragma once +#include "modelnode.h" #include "qmldesignercorelib_global.h" #include @@ -40,11 +41,14 @@ public: void reset(); void apply(); + ModelNode view3dNode() const { return m_view3dNode; } + private: QString commonPrefix(); QPointer m_view; QList m_dataList; + ModelNode m_view3dNode; }; QDebug operator<<(QDebug debug, const BakeLightsDataModel::BakeData &data); diff --git a/src/plugins/qmldesigner/designercore/include/auxiliarydataproperties.h b/src/plugins/qmldesigner/designercore/include/auxiliarydataproperties.h index 1a1884ce645..4c12af14fef 100644 --- a/src/plugins/qmldesigner/designercore/include/auxiliarydataproperties.h +++ b/src/plugins/qmldesigner/designercore/include/auxiliarydataproperties.h @@ -106,6 +106,8 @@ inline constexpr AuxiliaryDataKeyView globalAnnotationStatus{AuxiliaryDataType:: inline constexpr AuxiliaryDataKeyView rotBlockProperty{AuxiliaryDataType::NodeInstanceAuxiliary, "rotBlock"}; inline constexpr AuxiliaryDataKeyView languageProperty{AuxiliaryDataType::Temporary, "language"}; +inline constexpr AuxiliaryDataKeyView bakeLightsManualProperty{AuxiliaryDataType::Document, + "bakeLightsManual"}; // Most material preview aux properties are duplicated as document and instance types, as they // are both required to be persistent and used at runtime to control material preview rendering From 8188bd2b3a15eb6b6ecb670690accd98789e736d Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Thu, 11 May 2023 17:28:50 +0300 Subject: [PATCH 163/192] QmlDesigner: Fix crash callback for lights baking NodeInstanceView sets the crash callback to connection manager, which overrode our initial setting. Crash callback must be set to NodeInstanceView instead to work. Fixes: QDS-9869 Change-Id: Id0f5e5cfbdaa978314ff8f8a72f30df7c0a98b61 Reviewed-by: Reviewed-by: Mahmoud Badri Reviewed-by: Thomas Hartmann --- src/plugins/qmldesigner/components/edit3d/bakelights.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/qmldesigner/components/edit3d/bakelights.cpp b/src/plugins/qmldesigner/components/edit3d/bakelights.cpp index c833927499b..53c7e53df9d 100644 --- a/src/plugins/qmldesigner/components/edit3d/bakelights.cpp +++ b/src/plugins/qmldesigner/components/edit3d/bakelights.cpp @@ -190,7 +190,7 @@ void BakeLights::bakeLights() m_connectionManager->setProgressCallback(std::move(progressCallback)); m_connectionManager->setFinishedCallback(std::move(finishedCallback)); - m_connectionManager->setCrashCallback(std::move(crashCallback)); + m_nodeInstanceView->setCrashCallback(std::move(crashCallback)); m_model->setNodeInstanceView(m_nodeInstanceView); @@ -383,7 +383,7 @@ void BakeLights::cleanup() if (m_connectionManager) { m_connectionManager->setProgressCallback({}); m_connectionManager->setFinishedCallback({}); - m_connectionManager->setCrashCallback({}); + m_nodeInstanceView->setCrashCallback({}); } if (m_model) { From c4ff1921ee8f14ecdce1146f936a099ae7973e58 Mon Sep 17 00:00:00 2001 From: Henning Gruendl Date: Wed, 10 May 2023 10:06:39 +0200 Subject: [PATCH 164/192] QmlDesigner: Fix ItemFilterComboBox arbitrary edit - Add onAccepted handler - Fix onAccepted early return when manualMapping active in base - Remove import version - Replace RegExpValidator with RegularExpressionValidator Task-number: QDS-9858 Change-Id: I5a967abb28dd70e4215efcc3993e084366d4e020 Reviewed-by: Mahmoud Badri Reviewed-by: Reviewed-by: Thomas Hartmann --- .../imports/HelperWidgets/ComboBox.qml | 12 +++-------- .../HelperWidgets/ItemFilterComboBox.qml | 21 +++++++++---------- 2 files changed, 13 insertions(+), 20 deletions(-) diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ComboBox.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ComboBox.qml index 813aa1c7594..4db5a98c225 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ComboBox.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ComboBox.qml @@ -1,7 +1,7 @@ // Copyright (C) 2016 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 -import QtQuick 2.15 +import QtQuick import StudioControls 1.0 as StudioControls import StudioTheme 1.0 as StudioTheme @@ -152,7 +152,7 @@ StudioControls.ComboBox { } onAccepted: { - if (!comboBox.__isCompleted) + if (!comboBox.__isCompleted || comboBox.backendValue === undefined || comboBox.manualMapping) return let inputText = comboBox.editText @@ -167,13 +167,7 @@ StudioControls.ComboBox { } onCompressedActivated: { - if (!comboBox.__isCompleted) - return - - if (comboBox.backendValue === undefined) - return - - if (comboBox.manualMapping) + if (!comboBox.__isCompleted || comboBox.backendValue === undefined || comboBox.manualMapping) return if (comboBox.valueRole && comboBox.textRole !== comboBox.valueRole) { diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ItemFilterComboBox.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ItemFilterComboBox.qml index 2f749271d77..316207e5f53 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ItemFilterComboBox.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ItemFilterComboBox.qml @@ -1,7 +1,7 @@ // Copyright (C) 2021 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only -import QtQuick 2.15 +import QtQuick import HelperWidgets 2.0 as HelperWidgets HelperWidgets.ComboBox { @@ -13,7 +13,7 @@ HelperWidgets.ComboBox { editable: true model: comboBox.addDefaultItem(itemFilterModel.itemModel) - validator: RegExpValidator { regExp: /(^$|^[a-z_]\w*)/ } + validator: RegularExpressionValidator { regularExpression: /(^$|^[a-z_]\w*)/ } HelperWidgets.ItemFilterModel { id: itemFilterModel @@ -32,17 +32,18 @@ HelperWidgets.ComboBox { comboBox.setCurrentText(comboBox.textValue) } onModelChanged: comboBox.setCurrentText(comboBox.textValue) - onCompressedActivated: function(index, reason) { comboBox.handleActivate(index) } - Component.onCompleted: comboBox.setCurrentText(comboBox.textValue) - onEditTextChanged: comboBox.dirty = true onFocusChanged: { if (comboBox.dirty) comboBox.handleActivate(comboBox.currentIndex) } - function handleActivate(index) - { + onCompressedActivated: function(index, reason) { comboBox.handleActivate(index) } + onAccepted: comboBox.setCurrentText(comboBox.editText) + + Component.onCompleted: comboBox.setCurrentText(comboBox.textValue) + + function handleActivate(index) { if (!comboBox.__isCompleted || comboBox.backendValue === undefined) return @@ -52,8 +53,7 @@ HelperWidgets.ComboBox { comboBox.block = false } - function setCurrentText(text) - { + function setCurrentText(text) { if (!comboBox.__isCompleted || comboBox.backendValue === undefined) return @@ -78,8 +78,7 @@ HelperWidgets.ComboBox { comboBox.dirty = false } - function addDefaultItem(arr) - { + function addDefaultItem(arr) { var copy = arr.slice() copy.unshift(comboBox.defaultItem) return copy From 94b019dbe05ef7639ffea8e09b5974d89d75adaa Mon Sep 17 00:00:00 2001 From: Henning Gruendl Date: Fri, 5 May 2023 17:08:45 +0200 Subject: [PATCH 165/192] QmlDesigner: Add duplicate control Add a setting/property to the EditableListView in order to control if it allows duplicates or not. If duplicates are not allowed it will disable the items in the ComboBox which are already applied to the assigned backend property. Task-number: QDS-6730 Change-Id: I31776fdfb14858c431b40d264e8e709009c517b3 Reviewed-by: Thomas Hartmann Reviewed-by: Ali Kianian Reviewed-by: Mahmoud Badri Reviewed-by: --- .../HelperWidgets/EditableListView.qml | 22 +++++--- .../HelperWidgets/ListViewComboBox.qml | 14 ++---- .../propertyeditor/itemfiltermodel.cpp | 50 ++++++++++--------- .../propertyeditor/itemfiltermodel.h | 20 ++++---- 4 files changed, 56 insertions(+), 50 deletions(-) diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/EditableListView.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/EditableListView.qml index 1536afdb06e..b8792c71ea6 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/EditableListView.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/EditableListView.qml @@ -1,8 +1,9 @@ // Copyright (C) 2021 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 -import QtQuick 2.15 -import QtQuick.Layouts 1.15 +import QtQuick +import QtQuick.Layouts +import HelperWidgets 2.0 as HelperWidgets import StudioControls 1.0 as StudioControls import StudioTheme 1.0 as StudioTheme @@ -23,11 +24,12 @@ Item { property real __actionIndicatorWidth: StudioTheme.Values.squareComponentWidth property real __actionIndicatorHeight: StudioTheme.Values.height property string typeFilter: "QtQuick3D.Material" - property string textRole: "IdAndNameRole" - property string valueRole: "IdRole" + property string textRole: "idAndName" + property string valueRole: "id" property int activatedReason: ComboBox.ActivatedReason.Other property bool delegateHover: false + property bool allowDuplicates: true property string extraButtonIcon: "" // setting this will show an extra button property string extraButtonToolTip: "" @@ -40,11 +42,19 @@ Item { Layout.preferredWidth: StudioTheme.Values.height * 10 Layout.preferredHeight: myColumn.height + HelperWidgets.ItemFilterModel { + id: itemFilterModel + typeFilter: root.typeFilter + modelNodeBackendProperty: modelNodeBackend + selectedItems: root.allowDuplicates ? [] : root.model + } + Component { id: myDelegate Row { property alias comboBox: itemFilterComboBox + ListViewComboBox { id: itemFilterComboBox @@ -54,7 +64,7 @@ Item { validator: RegExpValidator { regExp: /(^[a-z_]\w*|^[A-Z]\w*\.{1}([a-z_]\w*\.?)+)/ } actionIndicatorVisible: false - typeFilter: root.typeFilter + model: itemFilterModel initialModelData: modelData textRole: root.textRole valueRole: root.valueRole @@ -188,7 +198,7 @@ Item { visible: myRepeater.count === 0 validator: RegExpValidator { regExp: /(^[a-z_]\w*|^[A-Z]\w*\.{1}([a-z_]\w*\.?)+)/ } actionIndicatorVisible: false - typeFilter: root.typeFilter + model: itemFilterModel textRole: root.textRole valueRole: root.valueRole implicitWidth: StudioTheme.Values.singleControlColumnWidth diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ListViewComboBox.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ListViewComboBox.qml index 4be51733016..1f8dbbb3d41 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ListViewComboBox.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ListViewComboBox.qml @@ -1,27 +1,19 @@ // Copyright (C) 2021 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only -import QtQuick 2.15 +import QtQuick import HelperWidgets 2.0 as HelperWidgets import StudioControls 1.0 as StudioControls StudioControls.ComboBox { id: root - property alias typeFilter: itemFilterModel.typeFilter - property var initialModelData property bool __isCompleted: false editable: true - model: itemFilterModel - textRole: "IdRole" - valueRole: "IdRole" - - HelperWidgets.ItemFilterModel { - id: itemFilterModel - modelNodeBackendProperty: modelNodeBackend - } + textRole: "id" + valueRole: "id" Component.onCompleted: { root.__isCompleted = true diff --git a/src/plugins/qmldesigner/components/propertyeditor/itemfiltermodel.cpp b/src/plugins/qmldesigner/components/propertyeditor/itemfiltermodel.cpp index 19a697e7024..c5d9a23d7ea 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/itemfiltermodel.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/itemfiltermodel.cpp @@ -15,20 +15,11 @@ using namespace QmlDesigner; -QHash ItemFilterModel::m_roles; - ItemFilterModel::ItemFilterModel(QObject *parent) : QAbstractListModel(parent) , m_typeFilter("QtQuick.Item") , m_selectionOnly(false) -{ - if (m_roles.empty()) { - m_roles = QAbstractListModel::roleNames(); - const QMetaEnum roleEnum = QMetaEnum::fromType(); - for (int i = 0; i < roleEnum.keyCount(); i++) - m_roles.insert(roleEnum.value(i), roleEnum.key(i)); - } -} +{} void ItemFilterModel::setModelNodeBackend(const QVariant &modelNodeBackend) { @@ -64,6 +55,12 @@ void ItemFilterModel::setSelectionOnly(bool value) emit selectionOnlyChanged(); } +void ItemFilterModel::setSelectedItems(const QStringList &selectedItems) +{ + m_selectedItems = selectedItems; + emit selectedItemsChanged(); +} + QString ItemFilterModel::typeFilter() const { return m_typeFilter; @@ -74,6 +71,11 @@ bool ItemFilterModel::selectionOnly() const return m_selectionOnly; } +QStringList ItemFilterModel::selectedItems() const +{ + return m_selectedItems; +} + void ItemFilterModel::registerDeclarativeType() { qmlRegisterType("HelperWidgets", 2, 0, "ItemFilterModel"); @@ -86,34 +88,34 @@ int ItemFilterModel::rowCount(const QModelIndex &) const QVariant ItemFilterModel::data(const QModelIndex &index, int role) const { - if (!index.isValid()) + if (!index.isValid() || index.row() >= rowCount()) return {}; const ModelNode node = modelNodeForRow(index.row()); - QVariant value; switch (role) { case IdRole: - value = node.id(); - break; + return node.id(); case NameRole: - value = node.variantProperty("objectName").value(); - break; + return node.variantProperty("objectName").value(); case IdAndNameRole: - value = QString("%1 [%2]").arg(node.variantProperty("objectName").value().toString(), - node.id()); - break; + return QString("%1 [%2]").arg(node.variantProperty("objectName").value().toString(), + node.id()); + case EnabledRole: + return !m_selectedItems.contains(node.id()); default: - value = node.id(); - break; + return {}; } - - return value; } QHash ItemFilterModel::roleNames() const { - return m_roles; + static QHash roleNames{{IdRole, "id"}, + {NameRole, "name"}, + {IdAndNameRole, "idAndName"}, + {EnabledRole, "enabled"}}; + + return roleNames; } QVariant ItemFilterModel::modelNodeBackend() const diff --git a/src/plugins/qmldesigner/components/propertyeditor/itemfiltermodel.h b/src/plugins/qmldesigner/components/propertyeditor/itemfiltermodel.h index e69269ddd95..cb7ae131778 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/itemfiltermodel.h +++ b/src/plugins/qmldesigner/components/propertyeditor/itemfiltermodel.h @@ -17,25 +17,26 @@ class ItemFilterModel : public QAbstractListModel Q_OBJECT Q_PROPERTY(QString typeFilter READ typeFilter WRITE setTypeFilter NOTIFY typeFilterChanged) - Q_PROPERTY(QVariant modelNodeBackendProperty READ modelNodeBackend WRITE setModelNodeBackend NOTIFY modelNodeBackendChanged) + Q_PROPERTY(QVariant modelNodeBackendProperty READ modelNodeBackend WRITE setModelNodeBackend + NOTIFY modelNodeBackendChanged) Q_PROPERTY(QStringList itemModel READ itemModel NOTIFY itemModelChanged) - Q_PROPERTY(bool selectionOnly READ selectionOnly WRITE setSelectionOnly NOTIFY selectionOnlyChanged) + Q_PROPERTY( + bool selectionOnly READ selectionOnly WRITE setSelectionOnly NOTIFY selectionOnlyChanged) + Q_PROPERTY(QStringList selectedItems READ selectedItems WRITE setSelectedItems NOTIFY + selectedItemsChanged) public: - enum Roles { - IdRole = Qt::UserRole + 1, - NameRole, - IdAndNameRole - }; - Q_ENUM(Roles) + enum { IdRole = Qt::DisplayRole, NameRole = Qt::UserRole, IdAndNameRole, EnabledRole }; explicit ItemFilterModel(QObject *parent = nullptr); void setModelNodeBackend(const QVariant &modelNodeBackend); void setTypeFilter(const QString &typeFilter); void setSelectionOnly(bool value); + void setSelectedItems(const QStringList &selectedItems); QString typeFilter() const; bool selectionOnly() const; + QStringList selectedItems() const; void setupModel(); QStringList itemModel() const; @@ -51,6 +52,7 @@ signals: void modelNodeBackendChanged(); void itemModelChanged(); void selectionOnlyChanged(); + void selectedItemsChanged(); private: QVariant modelNodeBackend() const; @@ -61,7 +63,7 @@ private: QList m_modelInternalIds; QmlDesigner::ModelNode m_modelNode; bool m_selectionOnly; - static QHash m_roles; + QStringList m_selectedItems; }; QML_DECLARE_TYPE(ItemFilterModel) From 95d424bc3d4e973b8eac9a6b00f48539354e60a0 Mon Sep 17 00:00:00 2001 From: Tim Jenssen Date: Fri, 12 May 2023 10:25:25 +0000 Subject: [PATCH 166/192] Revert "qds: workaround that appbundle finds the images" This reverts commit f3fcce9f4fbba439aae3a0fa85a6261456367e7d. Reason for revert: produces other problems Change-Id: I2225ffc96d30a9dc175a977602f7c8378b5cb31c Reviewed-by: Tim Jenssen --- .../components/componentcore/modelnodeoperations.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp index 3dbe386ce16..918b06c6379 100644 --- a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp +++ b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp @@ -1673,11 +1673,6 @@ void openEffectMaker(const QString &filePath) Utils::QtcProcess *qqemProcess = new Utils::QtcProcess(); qqemProcess->setEnvironment(env); qqemProcess->setCommand({ effectMakerPath, arguments }); - - // workaround that effect maker can find the images QTBUG-113531 - if (env.osType() == Utils::OsTypeMac) - qqemProcess->setWorkingDirectory(baseQtVersion->qmlPath().pathAppended("QtQuickEffectMaker")); - QObject::connect(qqemProcess, &Utils::QtcProcess::done, [qqemProcess]() { qqemProcess->deleteLater(); }); From 392e9cc84cfd371ba0e9ee313a2896ca72163ce6 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Fri, 12 May 2023 13:29:46 +0300 Subject: [PATCH 167/192] QmlDesigner: Fix compile error Change-Id: I6694cd0d48eb00d64be93019d4af418e64ad23a4 Reviewed-by: Tim Jenssen --- .../qml2puppet/instances/qt5bakelightsnodeinstanceserver.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/qml2puppet/qml2puppet/instances/qt5bakelightsnodeinstanceserver.cpp b/src/tools/qml2puppet/qml2puppet/instances/qt5bakelightsnodeinstanceserver.cpp index 190ad750bf9..4287808c8a8 100644 --- a/src/tools/qml2puppet/qml2puppet/instances/qt5bakelightsnodeinstanceserver.cpp +++ b/src/tools/qml2puppet/qml2puppet/instances/qt5bakelightsnodeinstanceserver.cpp @@ -150,7 +150,7 @@ void Qt5BakeLightsNodeInstanceServer::runDenoiser() QString binPath = QLibraryInfo::path(QLibraryInfo::BinariesPath); #if defined(Q_OS_WIN) binPath += "/qlmdenoiser.exe"; -#elif +#else binPath += "/qlmdenoiser"; #endif From c7a4ebcfcb83d3769c4805235eeaec1b5c88e638 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Fri, 12 May 2023 14:41:25 +0200 Subject: [PATCH 168/192] QmlDesigner: Use simplifiedTypeName also for m_backendValueTypeName In some cases the type has a QML prefix. Task-number: QDS-9880 Change-Id: I3752970a6e2006dc8813976bf50a94bf375741fd Reviewed-by: Tim Jenssen --- .../qmldesigner/components/bindingeditor/bindingeditor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/qmldesigner/components/bindingeditor/bindingeditor.cpp b/src/plugins/qmldesigner/components/bindingeditor/bindingeditor.cpp index 0c85432464d..6cd3e814929 100644 --- a/src/plugins/qmldesigner/components/bindingeditor/bindingeditor.cpp +++ b/src/plugins/qmldesigner/components/bindingeditor/bindingeditor.cpp @@ -94,7 +94,7 @@ void BindingEditor::setBackendValue(const QVariant &backendValue) m_backendValueTypeName = node.metaInfo() .property(propertyEditorValue->name()) .propertyType() - .typeName(); + .simplifiedTypeName(); QString nodeId = node.id(); if (nodeId.isEmpty()) From 590f0685da417b0a51c1ddb0947a720323fd0056 Mon Sep 17 00:00:00 2001 From: Pranta Dastider Date: Thu, 11 May 2023 18:25:44 +0200 Subject: [PATCH 169/192] QmlDesigner: Update qtBridge installation text This patch updates the existing QtBridge for Adobe Photoshop installation text to clarify the existing text furthermore. It aims to make the console code instructions slightly flexible to accommodate the future versions of QtBridge into the scope. Fixes: QDS-9720 Change-Id: Icbaa29fdb015921f06f653de1cc26caf442f34f2 Reviewed-by: Mats Honkamaa Reviewed-by: Tim Jenssen --- .../src/qtbridge/qtbridge-ps-setup.qdoc | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/doc/qtdesignstudio/src/qtbridge/qtbridge-ps-setup.qdoc b/doc/qtdesignstudio/src/qtbridge/qtbridge-ps-setup.qdoc index d21e9ab7046..e2e8e8f389e 100644 --- a/doc/qtdesignstudio/src/qtbridge/qtbridge-ps-setup.qdoc +++ b/doc/qtdesignstudio/src/qtbridge/qtbridge-ps-setup.qdoc @@ -31,14 +31,16 @@ \list 1 \li Copy the \QBPS ZXP package from \c {Qt\Tools\QtDesignStudio\photoshop_bridge} to the - \c Documents directory in your user directory. + \c Documents directory in your user directory. The \c {file-name} + should look similar to: \c {qt.QtBridge}. \li Open Windows PowerShell. - \li Enter the following commands: + \li Enter the following commands, replacing + \c {} with the \c {file-name}: \badcode cd "$env:UserProfile\Documents" - mv .\io.qt.QtBridge.zxp .\io.qt.QtBridge.zip - expand-archive .\io.qt.QtBridge.zip - xcopy /E /I .\io.qt.QtBridge "$env:APPDATA\Adobe\CEP\extensions\io.qt.QtBridge" + mv .\.zxp .\.zip + expand-archive .\.zip + xcopy /E /I .\ "$env:APPDATA\Adobe\CEP\extensions\" \endcode \endlist @@ -49,14 +51,16 @@ \list 1 \li Copy the \QBPS ZXP package from \c {Qt/QtDesignStudio/photoshop_bridge} - to your \c Documents directory. + to your \c Documents directory. The \c {file-name} + should look similar to: \c {qt.QtBridge}. \li Open Terminal. - \li Enter the following commands: + \li Enter the following commands, replacing + \c {} with the \c {file-name}: \badcode cd ~/Documents - unzip io.qt.QtBridge.zxp -d io.qt.QtBridge + unzip .zxp -d sudo mkdir -p /Library/Application\ Support/Adobe/CEP/extensions - sudo cp -R ./io.qt.QtBridge /Library/Application\ Support/Adobe/CEP/extensions + sudo cp -R ./ /Library/Application\ Support/Adobe/CEP/extensions \endcode \endlist From 910b467af6ebe4a69a9e8682da2890780727783d Mon Sep 17 00:00:00 2001 From: Burak Hancerli Date: Mon, 15 May 2023 14:49:06 +0200 Subject: [PATCH 170/192] QmlProject: Remove unnecessary destructor Change-Id: I7e5d58a46074cff3ac9e41322a0e93200fea17e0 Reviewed-by: Tim Jenssen --- src/plugins/qmlprojectmanager/qmlproject.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/plugins/qmlprojectmanager/qmlproject.h b/src/plugins/qmlprojectmanager/qmlproject.h index 4156801ecfc..3a6d5badf79 100644 --- a/src/plugins/qmlprojectmanager/qmlproject.h +++ b/src/plugins/qmlprojectmanager/qmlproject.h @@ -16,9 +16,6 @@ class QMLPROJECTMANAGER_EXPORT QmlProject : public ProjectExplorer::Project Q_OBJECT public: explicit QmlProject(const Utils::FilePath &filename); - ~QmlProject(){ - qDebug() << "Closing the project"; - }; static bool isQtDesignStudio(); static bool isQtDesignStudioStartedFromQtC(); From 057b4428d8c479d68efe671fd878c50b29abd43e Mon Sep 17 00:00:00 2001 From: Burak Hancerli Date: Tue, 9 May 2023 11:42:32 +0200 Subject: [PATCH 171/192] QmlDesigner: Refactor QmlProjectManager tests Task-number: QDS-9743 Change-Id: Ia5e788fcebacef59ba12226e2c873f5cbf8954ba Reviewed-by: Marco Bubke --- src/plugins/qmlprojectmanager/CMakeLists.txt | 15 +- .../buildsystem/projectitem/converters.cpp | 2 +- .../projectitem/qmlprojectitem.cpp | 8 +- .../buildsystem/projectitem/qmlprojectitem.h | 3 +- .../auto/qml/qmlprojectmanager/CMakeLists.txt | 1 - .../projectitem/CMakeLists.txt | 25 - .../qmlprojectmanager/projectitem/README.md | 46 -- .../qmlprojectmanager/projectitem/common.h | 20 - .../converter/test-set-2/testfile.jsontoqml | 102 ---- .../converter/test-set-2/testfile.qmlproject | 112 ---- .../converter/test-set-2/testfile.qmltojson | 174 ------ .../converter/test-set-3/testfile.jsontoqml | 93 --- .../converter/test-set-3/testfile.qmlproject | 93 --- .../converter/test-set-3/testfile.qmltojson | 161 ------ .../converter/test-set-4/testfile.jsontoqml | 65 --- .../converter/test-set-4/testfile.qmlproject | 55 -- .../converter/test-set-4/testfile.qmltojson | 114 ---- .../projectitem/test-converters.cpp | 108 ---- .../projectitem/test-filefilters.cpp | 25 - .../projectitem/test-getters.cpp | 162 ------ .../projectitem/test-setters.cpp | 161 ------ .../projectitem/tst_projectitem.cpp | 8 - tests/unit/CMakeLists.txt | 1 + tests/unit/README.md | 49 ++ tests/unit/tools/CMakeLists.txt | 1 + .../tools/qmlprojectmanager/CMakeLists.txt | 16 + tests/unit/tools/qmlprojectmanager/main.cpp | 95 +++ tests/unit/unittest/CMakeLists.txt | 5 +- .../unit/unittest/google-using-declarations.h | 1 + tests/unit/unittest/modulescanner-test.cpp | 1 + .../unittest/qmlprojectmanager/CMakeLists.txt | 7 + .../qmlprojectmanager/converters-test.cpp | 94 +++ .../qmlprojectmanager}/data/README.md | 15 - .../converter/test-set-1/testfile.jsontoqml | 0 .../converter/test-set-1/testfile.qmlproject | 0 .../converter/test-set-1/testfile.qmltojson | 0 .../converter/test-set-2}/testfile.jsontoqml | 0 .../converter/test-set-2}/testfile.qmlproject | 0 .../converter/test-set-2}/testfile.qmltojson | 0 .../file-filters/MaterialBundle.qmlproject | 0 .../MaterialBundle.qmlproject.qtds | 0 .../data/file-filters/MaterialLibrary.qrc | 0 .../file-filters/asset_imports/CMakeLists.txt | 0 .../ComponentBundles/CMakeLists.txt | 0 .../MaterialBundle/AcrylicPaintMaterial.qml | 0 .../MaterialBundle/AluminiumMaterial.qml | 0 .../MaterialBundle/AsphaltMaterial.qml | 0 .../MaterialBundle/BrickMaterial.qml | 0 .../MaterialBundle/CMakeLists.txt | 0 .../CarPaintGlitterMaterial.qml | 0 .../MaterialBundle/CarPaintMaterial.qml | 0 .../MaterialBundle/CarbonFiberMaterial.qml | 0 .../MaterialBundle/CeramicMaterial.qml | 0 .../MaterialBundle/ChromeMaterial.qml | 0 .../MaterialBundle/ConcreteMaterial.qml | 0 .../MaterialBundle/CopperMaterial.qml | 0 .../MaterialBundle/FabricMaterial.qml | 0 .../MaterialBundle/FabricRoughMaterial.qml | 0 .../MaterialBundle/FabricSatinMaterial.qml | 0 .../MaterialBundle/GlassMaterial.qml | 0 .../MaterialBundle/GlassTintedMaterial.qml | 0 .../MaterialBundle/GoldMaterial.qml | 0 .../MaterialBundle/LeatherMaterial.qml | 0 .../MaterialBundle/MirrorMaterial.qml | 0 .../MaterialBundle/PaperMaterial.qml | 0 .../MaterialBundle/PlasticMatteMaterial.qml | 0 .../MaterialBundle/PlasticShinyMaterial.qml | 0 .../PlasticTexturedMaterial.qml | 0 .../MaterialBundle/RubberMaterial.qml | 0 .../MaterialBundle/SilverMaterial.qml | 0 .../MaterialBundle/SteelBrushedMaterial.qml | 0 .../MaterialBundle/SteelFloorMaterial.qml | 0 .../MaterialBundle/SteelMaterial.qml | 0 .../MaterialBundle/StoneMaterial.qml | 0 .../MaterialBundle/WaxMaterial.qml | 0 .../MaterialBundle/WoodMaterial.qml | 0 .../MaterialBundle/WoodParquetMaterial.qml | 0 .../MaterialBundle/WoodPlanksMaterial.qml | 0 .../MaterialBundle/_asset_ref.json | 0 .../designer/acrylicpaint.metainfo | 0 .../designer/aluminium.metainfo | 0 .../MaterialBundle/designer/asphalt.metainfo | 0 .../MaterialBundle/designer/brick.metainfo | 0 .../designer/carbonfiber.metainfo | 0 .../MaterialBundle/designer/carpaint.metainfo | 0 .../designer/carpaintglitter.metainfo | 0 .../MaterialBundle/designer/ceramic.metainfo | 0 .../MaterialBundle/designer/chrome.metainfo | 0 .../MaterialBundle/designer/concrete.metainfo | 0 .../MaterialBundle/designer/copper.metainfo | 0 .../MaterialBundle/designer/fabric.metainfo | 0 .../designer/fabricrough.metainfo | 0 .../designer/fabricsatin.metainfo | 0 .../MaterialBundle/designer/glass.metainfo | 0 .../designer/glasstinted.metainfo | 0 .../MaterialBundle/designer/gold.metainfo | 0 .../designer/images/material.png | 0 .../designer/images/material16.png | 0 .../designer/images/material@2x.png | 0 .../MaterialBundle/designer/leather.metainfo | 0 .../MaterialBundle/designer/mirror.metainfo | 0 .../MaterialBundle/designer/paper.metainfo | 0 .../designer/plasticmatte.metainfo | 0 .../designer/plasticshiny.metainfo | 0 .../designer/plastictextured.metainfo | 0 .../MaterialBundle/designer/rubber.metainfo | 0 .../MaterialBundle/designer/silver.metainfo | 0 .../MaterialBundle/designer/steel.metainfo | 0 .../designer/steelbrushed.metainfo | 0 .../designer/steelfloor.metainfo | 0 .../MaterialBundle/designer/stone.metainfo | 0 .../MaterialBundle/designer/wax.metainfo | 0 .../MaterialBundle/designer/wood.metainfo | 0 .../designer/woodparquet.metainfo | 0 .../designer/woodplanks.metainfo | 0 .../images/Asphalt010_2K_NormalGL.png | 0 .../images/Asphalt010_2K_Opacity.png | 0 .../images/Asphalt010_2K_Roughness.png | 0 .../images/Bricks026_2K_AmbientOcclusion.png | 0 .../images/Bricks026_2K_Color.png | 0 .../images/Bricks026_2K_NormalGL.png | 0 .../images/Bricks026_2K_Roughness.png | 0 .../images/Concrete032_2K_NormalGL.png | 0 .../images/Concrete032_2K_Roughness.png | 0 .../images/DiamondPlate001_2K_NormalGL.png | 0 .../images/DiamondPlate001_2K_Roughness.png | 0 .../images/Fabric004_2K_NormalGL.png | 0 .../images/Fabric030_2K_Displacement.png | 0 .../images/Fabric030_2K_NormalGL.png | 0 .../images/Fabric030_2K_Roughness.png | 0 .../images/Fabric031_2K_Displacement.png | 0 .../images/Fabric031_2K_NormalGL.png | 0 .../images/Fabric031_2K_Roughness.png | 0 .../MaterialBundle/images/LDR_RGB1_3.png | 0 .../images/Leather037_2K_Color.png | 0 .../images/Leather037_2K_NormalGL.png | 0 .../images/Leather037_2K_Roughness.png | 0 .../images/Metal009_2K_NormalGL.png | 0 .../images/Metal009_2K_Roughness.png | 0 .../images/Metal029_2K_Displacement.jpg | 0 .../images/Metal029_2K_Displacement.png | 0 .../images/Paint006_2K_AmbientOcclusion.png | 0 .../images/Paint006_2K_NormalGL.png | 0 .../images/Paint006_2K_Roughness.png | 0 .../images/Rock023_2K_AmbientOcclusion.png | 0 .../images/Rock023_2K_Color.png | 0 .../images/Rock023_2K_NormalGL.png | 0 .../images/Rock023_2K_Roughness.png | 0 .../images/Wood048_2K_Color.png | 0 .../images/Wood048_2K_NormalGL.png | 0 .../images/Wood048_2K_Roughness.png | 0 .../images/WoodFloor044_2K_Color.png | 0 .../images/WoodFloor044_2K_NormalGL.png | 0 .../images/WoodFloor044_2K_Roughness.png | 0 .../WoodFloor054_2K_AmbientOcclusion.png | 0 .../images/WoodFloor054_2K_Color.png | 0 .../images/WoodFloor054_2K_NormalGL.png | 0 .../images/WoodFloor054_2K_Roughness.png | 0 .../MaterialBundle/images/blurrynoise.tga | 0 .../MaterialBundle/images/noisenormal.png | 0 .../ComponentBundles/MaterialBundle/qmldir | 0 .../MaterialBundle/shaders/CMakeLists.txt | 0 .../MaterialBundle/shaders/SSS.frag | 0 .../MaterialBundle/shaders/SSS.vert | 0 .../MaterialBundle/shaders/carmat_simple.frag | 0 .../MaterialBundle/shaders/carmat_simple.vert | 0 .../shaders/carmat_simple_nf.frag | 0 .../shaders/carmat_simple_nf.vert | 0 .../MaterialBundle/shaders/glass.frag | 0 .../MaterialBundle/shaders/glass.vert | 0 .../MaterialBundle/shaders/satin.frag | 0 .../MaterialBundle/shaders/satin.vert | 0 .../data/file-filters/content/App.qml | 0 .../data/file-filters/content/CMakeLists.txt | 0 .../content/CustomRoundButton.qml | 0 .../file-filters/content/MaterialNames.qml | 0 .../file-filters/content/MouseRotator.qml | 0 .../data/file-filters/content/Screen01.ui.qml | 0 .../content/fonts/OpenSans-Bold.ttf | 0 .../content/fonts/OpenSans-Regular.ttf | 0 .../data/file-filters/content/fonts/fonts.txt | 0 .../content/images/Ground_ShadowMap.png | 0 .../content/images/HDR/dark_mode.png | 0 .../content/images/HDR/day_mode.png | 0 .../content/images/LDR_RGB1_3.png | 0 .../file-filters/content/images/QtLogo_HD.png | 0 .../content/images/UI/innerMesh.png | 0 .../content/images/UI/lightToggle.png | 0 .../content/images/UI/outerMesh.png | 0 .../content/images/UI/perfhudicon.png | 0 .../content/images/UI/perfhudicon_on.png | 0 .../file-filters/content/images/White.png | 0 .../file-filters/content/images/checkmark.png | 0 .../content/images/groundAlpha.png | 0 .../file-filters/content/images/qtlogo.png | 0 .../content/images/scratchmap.png | 0 .../file-filters/content/images/shadow.png | 0 .../content/images/vlkhcah_2K_AO.jpg | 0 .../content/images/vlkhcah_2K_Albedo.jpg | 0 .../content/images/vlkhcah_2K_Normal.jpg | 0 .../content/images/vlkhcah_2K_Roughness.jpg | 0 .../file-filters/content/meshes/floor.mesh | 0 .../content/meshes/materialBall.mesh | 0 .../data/file-filters/filelist.txt | 0 .../data/file-filters/imports/CMakeLists.txt | 0 .../imports/MaterialLibrary/CMakeLists.txt | 0 .../imports/MaterialLibrary/Constants.qml | 0 .../MaterialLibrary/DirectoryFontLoader.qml | 0 .../MaterialLibrary/EventListModel.qml | 0 .../MaterialLibrary/EventListSimulator.qml | 0 .../MaterialLibrary/designer/plugin.metainfo | 0 .../imports/MaterialLibrary/qmldir | 0 .../data/file-filters/main.qml | 0 .../data/file-filters/qmlcomponents | 0 .../data/file-filters/qmlmodules | 0 .../data/file-filters/qtquickcontrols2.conf | 0 .../data/file-filters/share.qrc | 0 .../data/file-filters/src/app_environment.h | 0 .../file-filters/src/import_qml_plugins.h | 0 .../data/file-filters/src/main.cpp | 0 .../data/file-filters/translations.db | 0 .../data/getter-setter/empty.qmlproject} | 0 .../data/getter-setter/notEmpty.qmlproject} | 0 .../qmlprojectmanager/projectitem-test.cpp | 539 ++++++++++++++++++ 224 files changed, 829 insertions(+), 1548 deletions(-) delete mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/CMakeLists.txt delete mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/README.md delete mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/common.h delete mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-2/testfile.jsontoqml delete mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-2/testfile.qmlproject delete mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-2/testfile.qmltojson delete mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-3/testfile.jsontoqml delete mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-3/testfile.qmlproject delete mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-3/testfile.qmltojson delete mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-4/testfile.jsontoqml delete mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-4/testfile.qmlproject delete mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-4/testfile.qmltojson delete mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/test-converters.cpp delete mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/test-filefilters.cpp delete mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/test-getters.cpp delete mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/test-setters.cpp delete mode 100644 tests/auto/qml/qmlprojectmanager/projectitem/tst_projectitem.cpp create mode 100644 tests/unit/README.md create mode 100644 tests/unit/tools/CMakeLists.txt create mode 100644 tests/unit/tools/qmlprojectmanager/CMakeLists.txt create mode 100644 tests/unit/tools/qmlprojectmanager/main.cpp create mode 100644 tests/unit/unittest/qmlprojectmanager/CMakeLists.txt create mode 100644 tests/unit/unittest/qmlprojectmanager/converters-test.cpp rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/README.md (75%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/converter/test-set-1/testfile.jsontoqml (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/converter/test-set-1/testfile.qmlproject (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/converter/test-set-1/testfile.qmltojson (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-5 => unit/unittest/qmlprojectmanager/data/converter/test-set-2}/testfile.jsontoqml (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-5 => unit/unittest/qmlprojectmanager/data/converter/test-set-2}/testfile.qmlproject (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-5 => unit/unittest/qmlprojectmanager/data/converter/test-set-2}/testfile.qmltojson (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/MaterialBundle.qmlproject (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/MaterialBundle.qmlproject.qtds (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/MaterialLibrary.qrc (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/CMakeLists.txt (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/CMakeLists.txt (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/AcrylicPaintMaterial.qml (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/AluminiumMaterial.qml (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/AsphaltMaterial.qml (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/BrickMaterial.qml (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/CMakeLists.txt (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/CarPaintGlitterMaterial.qml (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/CarPaintMaterial.qml (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/CarbonFiberMaterial.qml (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/CeramicMaterial.qml (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/ChromeMaterial.qml (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/ConcreteMaterial.qml (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/CopperMaterial.qml (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/FabricMaterial.qml (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/FabricRoughMaterial.qml (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/FabricSatinMaterial.qml (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/GlassMaterial.qml (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/GlassTintedMaterial.qml (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/GoldMaterial.qml (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/LeatherMaterial.qml (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/MirrorMaterial.qml (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/PaperMaterial.qml (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/PlasticMatteMaterial.qml (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/PlasticShinyMaterial.qml (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/PlasticTexturedMaterial.qml (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/RubberMaterial.qml (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/SilverMaterial.qml (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/SteelBrushedMaterial.qml (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/SteelFloorMaterial.qml (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/SteelMaterial.qml (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/StoneMaterial.qml (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/WaxMaterial.qml (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/WoodMaterial.qml (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/WoodParquetMaterial.qml (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/WoodPlanksMaterial.qml (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/_asset_ref.json (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/acrylicpaint.metainfo (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/aluminium.metainfo (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/asphalt.metainfo (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/brick.metainfo (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/carbonfiber.metainfo (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/carpaint.metainfo (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/carpaintglitter.metainfo (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/ceramic.metainfo (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/chrome.metainfo (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/concrete.metainfo (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/copper.metainfo (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/fabric.metainfo (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/fabricrough.metainfo (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/fabricsatin.metainfo (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/glass.metainfo (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/glasstinted.metainfo (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/gold.metainfo (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/images/material.png (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/images/material16.png (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/images/material@2x.png (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/leather.metainfo (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/mirror.metainfo (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/paper.metainfo (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/plasticmatte.metainfo (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/plasticshiny.metainfo (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/plastictextured.metainfo (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/rubber.metainfo (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/silver.metainfo (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/steel.metainfo (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/steelbrushed.metainfo (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/steelfloor.metainfo (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/stone.metainfo (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/wax.metainfo (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/wood.metainfo (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/woodparquet.metainfo (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/woodplanks.metainfo (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Asphalt010_2K_NormalGL.png (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Asphalt010_2K_Opacity.png (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Asphalt010_2K_Roughness.png (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Bricks026_2K_AmbientOcclusion.png (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Bricks026_2K_Color.png (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Bricks026_2K_NormalGL.png (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Bricks026_2K_Roughness.png (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Concrete032_2K_NormalGL.png (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Concrete032_2K_Roughness.png (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/DiamondPlate001_2K_NormalGL.png (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/DiamondPlate001_2K_Roughness.png (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Fabric004_2K_NormalGL.png (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Fabric030_2K_Displacement.png (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Fabric030_2K_NormalGL.png (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Fabric030_2K_Roughness.png (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Fabric031_2K_Displacement.png (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Fabric031_2K_NormalGL.png (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Fabric031_2K_Roughness.png (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/LDR_RGB1_3.png (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Leather037_2K_Color.png (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Leather037_2K_NormalGL.png (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Leather037_2K_Roughness.png (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Metal009_2K_NormalGL.png (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Metal009_2K_Roughness.png (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Metal029_2K_Displacement.jpg (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Metal029_2K_Displacement.png (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Paint006_2K_AmbientOcclusion.png (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Paint006_2K_NormalGL.png (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Paint006_2K_Roughness.png (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Rock023_2K_AmbientOcclusion.png (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Rock023_2K_Color.png (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Rock023_2K_NormalGL.png (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Rock023_2K_Roughness.png (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Wood048_2K_Color.png (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Wood048_2K_NormalGL.png (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Wood048_2K_Roughness.png (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/WoodFloor044_2K_Color.png (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/WoodFloor044_2K_NormalGL.png (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/WoodFloor044_2K_Roughness.png (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/WoodFloor054_2K_AmbientOcclusion.png (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/WoodFloor054_2K_Color.png (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/WoodFloor054_2K_NormalGL.png (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/WoodFloor054_2K_Roughness.png (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/blurrynoise.tga (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/noisenormal.png (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/qmldir (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/shaders/CMakeLists.txt (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/shaders/SSS.frag (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/shaders/SSS.vert (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/shaders/carmat_simple.frag (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/shaders/carmat_simple.vert (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/shaders/carmat_simple_nf.frag (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/shaders/carmat_simple_nf.vert (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/shaders/glass.frag (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/shaders/glass.vert (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/shaders/satin.frag (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/shaders/satin.vert (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/content/App.qml (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/content/CMakeLists.txt (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/content/CustomRoundButton.qml (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/content/MaterialNames.qml (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/content/MouseRotator.qml (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/content/Screen01.ui.qml (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/content/fonts/OpenSans-Bold.ttf (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/content/fonts/OpenSans-Regular.ttf (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/content/fonts/fonts.txt (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/content/images/Ground_ShadowMap.png (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/content/images/HDR/dark_mode.png (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/content/images/HDR/day_mode.png (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/content/images/LDR_RGB1_3.png (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/content/images/QtLogo_HD.png (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/content/images/UI/innerMesh.png (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/content/images/UI/lightToggle.png (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/content/images/UI/outerMesh.png (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/content/images/UI/perfhudicon.png (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/content/images/UI/perfhudicon_on.png (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/content/images/White.png (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/content/images/checkmark.png (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/content/images/groundAlpha.png (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/content/images/qtlogo.png (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/content/images/scratchmap.png (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/content/images/shadow.png (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/content/images/vlkhcah_2K_AO.jpg (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/content/images/vlkhcah_2K_Albedo.jpg (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/content/images/vlkhcah_2K_Normal.jpg (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/content/images/vlkhcah_2K_Roughness.jpg (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/content/meshes/floor.mesh (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/content/meshes/materialBall.mesh (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/filelist.txt (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/imports/CMakeLists.txt (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/imports/MaterialLibrary/CMakeLists.txt (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/imports/MaterialLibrary/Constants.qml (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/imports/MaterialLibrary/DirectoryFontLoader.qml (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/imports/MaterialLibrary/EventListModel.qml (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/imports/MaterialLibrary/EventListSimulator.qml (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/imports/MaterialLibrary/designer/plugin.metainfo (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/imports/MaterialLibrary/qmldir (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/main.qml (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/qmlcomponents (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/qmlmodules (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/qtquickcontrols2.conf (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/share.qrc (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/src/app_environment.h (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/src/import_qml_plugins.h (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/src/main.cpp (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem => unit/unittest/qmlprojectmanager}/data/file-filters/translations.db (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem/data/getter-setter/testfile-2.qmlproject => unit/unittest/qmlprojectmanager/data/getter-setter/empty.qmlproject} (100%) rename tests/{auto/qml/qmlprojectmanager/projectitem/data/getter-setter/testfile-1.qmlproject => unit/unittest/qmlprojectmanager/data/getter-setter/notEmpty.qmlproject} (100%) create mode 100644 tests/unit/unittest/qmlprojectmanager/projectitem-test.cpp diff --git a/src/plugins/qmlprojectmanager/CMakeLists.txt b/src/plugins/qmlprojectmanager/CMakeLists.txt index 3b7209d05c7..21da457c214 100644 --- a/src/plugins/qmlprojectmanager/CMakeLists.txt +++ b/src/plugins/qmlprojectmanager/CMakeLists.txt @@ -6,7 +6,6 @@ add_qtc_plugin(QmlProjectManager SOURCES qmlprojectgen/qmlprojectgenerator.cpp qmlprojectgen/qmlprojectgenerator.h qmlprojectgen/templates.qrc - projectfilecontenttools.cpp projectfilecontenttools.h qdslandingpage.cpp qdslandingpage.h qdslandingpagetheme.cpp qdslandingpagetheme.h @@ -53,3 +52,17 @@ extend_qtc_plugin(QmlProjectManager generatecmakelistsconstants.h boilerplate.qrc ) + +add_qtc_library(QmlProjectManagerLib OBJECT + CONDITION Qt6_VERSION VERSION_GREATER_EQUAL 6.4.3 + EXCLUDE_FROM_INSTALL + DEPENDS + QmlJS Utils + INCLUDES + ${CMAKE_CURRENT_LIST_DIR} + SOURCES_PREFIX ${CMAKE_CURRENT_LIST_DIR}/buildsystem + SOURCES + projectitem/filefilteritems.cpp projectitem/filefilteritems.h + projectitem/qmlprojectitem.cpp projectitem/qmlprojectitem.h + projectitem/converters.cpp projectitem/converters.h +) diff --git a/src/plugins/qmlprojectmanager/buildsystem/projectitem/converters.cpp b/src/plugins/qmlprojectmanager/buildsystem/projectitem/converters.cpp index aa0abf8ffe2..0097611a4dd 100644 --- a/src/plugins/qmlprojectmanager/buildsystem/projectitem/converters.cpp +++ b/src/plugins/qmlprojectmanager/buildsystem/projectitem/converters.cpp @@ -337,7 +337,7 @@ QJsonObject qmlProjectTojson(const Utils::FilePath &projectFile) targetObject.insert("files", files); fileGroupsObject.insert(propsPair.first, targetObject); } else if (childNode->name().contains("shadertool", Qt::CaseInsensitive)) { - QStringList quotedArgs = childNode->property("args").value.toString().split('\"'); + QStringList quotedArgs = childNode->property("args").value.toString().split('\"', Qt::SkipEmptyParts); QStringList args; for (int i = 0; i < quotedArgs.size(); ++i) { // Each odd arg in this list is a single quoted argument, which we should diff --git a/src/plugins/qmlprojectmanager/buildsystem/projectitem/qmlprojectitem.cpp b/src/plugins/qmlprojectmanager/buildsystem/projectitem/qmlprojectitem.cpp index 04bd26476e2..5b56c8f37ca 100644 --- a/src/plugins/qmlprojectmanager/buildsystem/projectitem/qmlprojectitem.cpp +++ b/src/plugins/qmlprojectmanager/buildsystem/projectitem/qmlprojectitem.cpp @@ -18,8 +18,9 @@ namespace QmlProjectManager { //#define REWRITE_PROJECT_FILE_IN_JSON_FORMAT -QmlProjectItem::QmlProjectItem(const Utils::FilePath &filePath) +QmlProjectItem::QmlProjectItem(const Utils::FilePath &filePath, const bool skipRewrite) : m_projectFile(filePath) + , m_skipRewrite(skipRewrite) { if (initProjectObject()) setupFileFilters(); @@ -394,9 +395,8 @@ void QmlProjectItem::addShaderToolFile(const QString &file) void QmlProjectItem::insertAndUpdateProjectFile(const QString &key, const QJsonValue &value) { m_project[key] = value; -#ifndef TESTS_ENABLED_QMLPROJECTITEM - m_projectFile.writeFileContents(Converters::jsonToQmlProject(m_project).toUtf8()); -#endif + if (!m_skipRewrite) + m_projectFile.writeFileContents(Converters::jsonToQmlProject(m_project).toUtf8()); } } // namespace QmlProjectManager diff --git a/src/plugins/qmlprojectmanager/buildsystem/projectitem/qmlprojectitem.h b/src/plugins/qmlprojectmanager/buildsystem/projectitem/qmlprojectitem.h index 78b038b0378..83ef5ca0000 100644 --- a/src/plugins/qmlprojectmanager/buildsystem/projectitem/qmlprojectitem.h +++ b/src/plugins/qmlprojectmanager/buildsystem/projectitem/qmlprojectitem.h @@ -26,7 +26,7 @@ class QmlProjectItem : public QObject { Q_OBJECT public: - explicit QmlProjectItem(const Utils::FilePath &filePath); + explicit QmlProjectItem(const Utils::FilePath &filePath, const bool skipRewrite = false); bool isQt4McuProject() const; @@ -103,6 +103,7 @@ private: // runtime variables Utils::FilePath m_projectFile; // design studio project file QJsonObject m_project; // root project object + const bool m_skipRewrite; // initializing functions bool initProjectObject(); diff --git a/tests/auto/qml/qmlprojectmanager/CMakeLists.txt b/tests/auto/qml/qmlprojectmanager/CMakeLists.txt index 795b5a74265..f7c00d44bd5 100644 --- a/tests/auto/qml/qmlprojectmanager/CMakeLists.txt +++ b/tests/auto/qml/qmlprojectmanager/CMakeLists.txt @@ -1,2 +1 @@ add_subdirectory(fileformat) -add_subdirectory(projectitem) diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/CMakeLists.txt b/tests/auto/qml/qmlprojectmanager/projectitem/CMakeLists.txt deleted file mode 100644 index 20f1bd0054d..00000000000 --- a/tests/auto/qml/qmlprojectmanager/projectitem/CMakeLists.txt +++ /dev/null @@ -1,25 +0,0 @@ -set(WITH_TESTS ON) - -find_package(Googletest MODULE) - -set(QmlProjectItemDir "${PROJECT_SOURCE_DIR}/src/plugins/qmlprojectmanager/buildsystem/projectitem") - -add_qtc_test(tst_qml_projectitem - DEPENDS QmlProjectManager Utils QmlJS Googletest - INCLUDES "${QmlProjectItemDir}" - DEFINES - QT_CREATOR - SRCDIR="${CMAKE_CURRENT_SOURCE_DIR}" - TESTDATA_DIR="${CMAKE_CURRENT_SOURCE_DIR}/data" - TESTS_ENABLED_QMLPROJECTITEM - SOURCES - tst_projectitem.cpp - test-getters.cpp - test-setters.cpp - test-converters.cpp - test-filefilters.cpp - common.h - "${QmlProjectItemDir}/qmlprojectitem.cpp" - "${QmlProjectItemDir}/converters.cpp" - "${QmlProjectItemDir}/filefilteritems.cpp" -) diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/README.md b/tests/auto/qml/qmlprojectmanager/projectitem/README.md deleted file mode 100644 index 60d308c5b77..00000000000 --- a/tests/auto/qml/qmlprojectmanager/projectitem/README.md +++ /dev/null @@ -1,46 +0,0 @@ -# QmlProject ProjectItem Tests - -## Content - -This test bundle covers following functionalities of QmlProjectItem class; - -* **Getter Functions**: Tests if getter functions are returning correct types with correct values -* **Setter Functions**: Tests if setter functions are updating the internal JSON object as expected -* **Converter Functions:** Tests if QmlProjectToJson and JsonToQmlProject functions are working as expected -* **File Filter Functions:** Tests if file filters are initialized properly - -## Data set folder structure - -The current folder hierarchy is as following; - -```text -| data -| -> converter -| | -> test-set-1 -| | | -> testfile.qmlproject -| | | -> testfile.qmltojson -| | | -> testfile.jsontoqml -| | -> test-set-2 -| | | -> testfile.qmlproject -| | | -> testfile.qmltojson -| | | -> testfile.jsontoqml -| | -> test-set-.. -| | -> test-set-.. -| -> getter-setter -| | -> testfile-1.qmlproject -| | -> testfile-2.qmlproject -| -> file-filters -| | -> test-set-1 -| | -> test-set-... -``` - -## Further information - -Please see [data/README.md](data/README.md) for more information on the test set content. - -## Contribution - -Please update; - -* This README whenever you change the test content -* [data/README.md](data/README.md) whenever you update the `data` folder diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/common.h b/tests/auto/qml/qmlprojectmanager/projectitem/common.h deleted file mode 100644 index 2167a5d1444..00000000000 --- a/tests/auto/qml/qmlprojectmanager/projectitem/common.h +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0 - -#pragma once - -#include -#include -#include - -#include -#include - -#include - -static QDir testDataRootDir(QLatin1String(TESTDATA_DIR)); - -inline void PrintTo(const QString &qString, ::std::ostream *os) -{ - *os << qUtf8Printable(qString); -} diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-2/testfile.jsontoqml b/tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-2/testfile.jsontoqml deleted file mode 100644 index 19276581aa3..00000000000 --- a/tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-2/testfile.jsontoqml +++ /dev/null @@ -1,102 +0,0 @@ -\\ prop: json-converted -\\ prop: auto-generated - -import QmlProject - -Project { - mainFile: "content/App.qml" - mainUiFile: "Screen01.ui.qml" - targetDirectory: "/opt/MaterialLibrary" - widgetApp: true - importPaths: [ "imports","asset_imports" ] - - qdsVersion: "3.9" - quickVersion: "" - qt6Project: true - qtForMCUs: true - - multilanguageSupport: true - primaryLanguage: "en" - supportedLanguages: [ "en" ] - - Environment { - QT_AUTO_SCREEN_SCALE_FACTOR: "1" - QT_ENABLE_HIGHDPI_SCALING: "0" - QT_LOGGING_RULES: "qt.qml.connections=false" - QT_QUICK_CONTROLS_CONF: "qtquickcontrols2.conf" - } - - ShaderTool { - args: "-s --glsl "100 es,120,150" --hlsl 50 --msl 12" - files: [ "content/shaders/*" ] - } - - QmlFiles { - directory: "content" - } - - QmlFiles { - directory: "imports" - } - - QmlFiles { - directory: "asset_imports" - } - - JavaScriptFiles { - directory: "content" - } - - JavaScriptFiles { - directory: "imports" - } - - ImageFiles { - directory: "content" - } - - ImageFiles { - directory: "asset_imports" - } - - Files { - directory: "." - filters: "*.conf" - files: [ "qtquickcontrols2.conf" ] - } - - Files { - directory: "." - filters: "*.ttf;*.otf" - } - - Files { - directory: "asset_imports" - filters: "*.mesh" - } - - Files { - directory: "content" - filters: "*.mesh" - } - - Files { - directory: "." - filters: "qmldir" - } - - Files { - directory: "." - filters: "*.glsl;*.glslv;*.glslf;*.vsh;*.fsh;*.vert;*.frag" - } - - Files { - directory: "." - filters: "*.mp3;*.wav" - } - - Files { - directory: "." - filters: "*.mp4" - } -} diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-2/testfile.qmlproject b/tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-2/testfile.qmlproject deleted file mode 100644 index 479c20456be..00000000000 --- a/tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-2/testfile.qmlproject +++ /dev/null @@ -1,112 +0,0 @@ -import QmlProject - -Project { - mainFile: "content/App.qml" - mainUiFile: "Screen01.ui.qml" - - - /* Include .qml, .js, and image files from current directory and subdirectories */ - QmlFiles { - directory: "content" - } - - QmlFiles { - directory: "imports" - } - - JavaScriptFiles { - directory: "content" - } - - JavaScriptFiles { - directory: "imports" - } - - ImageFiles { - directory: "content" - } - - Files { - filter: "*.conf" - files: ["qtquickcontrols2.conf"] - } - - Files { - filter: "qmldir" - directory: "." - } - - Files { - filter: "*.ttf;*.otf" - } - - Files { - filter: "*.wav;*.mp3" - } - - Files { - filter: "*.mp4" - } - - Files { - filter: "*.glsl;*.glslv;*.glslf;*.vsh;*.fsh;*.vert;*.frag" - } - - Files { - filter: "*.mesh" - directory: "asset_imports" - } - - Files { - filter: "*.mesh" - directory: "content" - } - - Files { - filter: "*.qml" - directory: "asset_imports" - } - - ImageFiles { - directory: "asset_imports" - } - - Environment { - QT_QUICK_CONTROLS_CONF: "qtquickcontrols2.conf" - QT_AUTO_SCREEN_SCALE_FACTOR: "1" - QT_LOGGING_RULES: "qt.qml.connections=false" - QT_ENABLE_HIGHDPI_SCALING: "0" - /* Useful for debugging - QSG_VISUALIZE=batches - QSG_VISUALIZE=clip - QSG_VISUALIZE=changes - QSG_VISUALIZE=overdraw - */ - } - - qt6Project: true - - /* List of plugin directories passed to QML runtime */ - importPaths: [ "imports", "asset_imports" ] - - /* Required for deployment */ - targetDirectory: "/opt/MaterialLibrary" - - qdsVersion: "3.9" - - /* If any modules the project imports require widgets (e.g. QtCharts), widgetApp must be true */ - widgetApp: true - - /* args: Specifies command line arguments for qsb tool to generate shaders. - files: Specifies target files for qsb tool. If path is included, it must be relative to this file. - Wildcard '*' can be used in the file name part of the path. - e.g. files: [ "content/shaders/*.vert", "*.frag" ] */ - ShaderTool { - args: "-s --glsl \"100 es,120,150\" --hlsl 50 --msl 12" - files: [ "content/shaders/*" ] - } - - multilanguageSupport: true - supportedLanguages: ["en"] - primaryLanguage: "en" -} diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-2/testfile.qmltojson b/tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-2/testfile.qmltojson deleted file mode 100644 index 64aa062313e..00000000000 --- a/tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-2/testfile.qmltojson +++ /dev/null @@ -1,174 +0,0 @@ -{ - "deployment": { - "targetDirectory": "/opt/MaterialLibrary" - }, - "environment": { - "QT_AUTO_SCREEN_SCALE_FACTOR": "1", - "QT_ENABLE_HIGHDPI_SCALING": "0", - "QT_LOGGING_RULES": "qt.qml.connections=false", - "QT_QUICK_CONTROLS_CONF": "qtquickcontrols2.conf" - }, - "fileGroups": { - "config": { - "directories": [ - "." - ], - "files": [ - { - "name": "qtquickcontrols2.conf" - } - ], - "filters": [ - "*.conf" - ] - }, - "font": { - "directories": [ - "." - ], - "files": [ - ], - "filters": [ - "*.ttf", - "*.otf" - ] - }, - "image": { - "directories": [ - "content", - "asset_imports" - ], - "files": [ - ], - "filters": [ - "*.jpeg", - "*.jpg", - "*.png", - "*.svg", - "*.hdr", - ".ktx" - ] - }, - "javaScript": { - "directories": [ - "content", - "imports" - ], - "files": [ - ], - "filters": [ - "*.js", - "*.ts" - ] - }, - "meshes": { - "directories": [ - "asset_imports", - "content" - ], - "files": [ - ], - "filters": [ - "*.mesh" - ] - }, - "qml": { - "directories": [ - "content", - "imports", - "asset_imports" - ], - "files": [ - ], - "filters": [ - "*.qml" - ] - }, - "qmldir": { - "directories": [ - "." - ], - "files": [ - ], - "filters": [ - "qmldir" - ] - }, - "shader": { - "directories": [ - "." - ], - "files": [ - ], - "filters": [ - "*.glsl", - "*.glslv", - "*.glslf", - "*.vsh", - "*.fsh", - "*.vert", - "*.frag" - ] - }, - "sound": { - "directories": [ - "." - ], - "files": [ - ], - "filters": [ - "*.mp3", - "*.wav" - ] - }, - "video": { - "directories": [ - "." - ], - "files": [ - ], - "filters": [ - "*.mp4" - ] - } - }, - "fileVersion": 1, - "importPaths": [ - "imports", - "asset_imports" - ], - "language": { - "multiLanguageSupport": true, - "primaryLanguage": "en", - "supportedLanguages": [ - "en" - ] - }, - "mcuConfig": { - }, - "runConfig": { - "fileSelectors": [ - ], - "mainFile": "content/App.qml", - "mainUiFile": "Screen01.ui.qml", - "widgetApp": true - }, - "shaderTool": { - "args": [ - "-s", - "--glsl", - "\"100 es,120,150\"", - "--hlsl", - "50", - "--msl", - "12" - ], - "files": [ - "content/shaders/*" - ] - }, - "versions": { - "designStudio": "3.9", - "qt": "6" - } -} diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-3/testfile.jsontoqml b/tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-3/testfile.jsontoqml deleted file mode 100644 index 1964ce018d9..00000000000 --- a/tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-3/testfile.jsontoqml +++ /dev/null @@ -1,93 +0,0 @@ -\\ prop: json-converted -\\ prop: auto-generated - -import QmlProject - -Project { - mainFile: "content/App.qml" - mainUiFile: "content/MainScreen.ui.qml" - targetDirectory: "/opt/RobotArm" - widgetApp: true - importPaths: [ "imports","asset_imports","backend_mock" ] - - qdsVersion: "3.0" - quickVersion: "" - qt6Project: true - qtForMCUs: true - - multilanguageSupport: true - primaryLanguage: "en" - supportedLanguages: [ "en" ] - - Environment { - QT_AUTO_SCREEN_SCALE_FACTOR: "1" - QT_ENABLE_HIGHDPI_SCALING: "0" - QT_LOGGING_RULES: "qt.qml.connections=false" - QT_QUICK_CONTROLS_CONF: "qtquickcontrols2.conf" - } - - ShaderTool { - args: "" - files: [ ] - } - - QmlFiles { - directory: "content" - } - - QmlFiles { - directory: "imports" - } - - QmlFiles { - directory: "backend_mock" - } - - JavaScriptFiles { - directory: "content" - } - - JavaScriptFiles { - directory: "imports" - } - - ImageFiles { - directory: "content" - } - - Files { - directory: "." - filters: "*.conf" - files: [ "qtquickcontrols2.conf" ] - } - - Files { - directory: "." - filters: "*.ttf;*.otf" - } - - Files { - directory: "content" - filters: "*.mesh" - } - - Files { - directory: "." - filters: "qmldir" - } - - Files { - directory: "." - filters: "*.glsl;*.glslv;*.glslf;*.vsh;*.fsh;*.vert;*.frag" - } - - Files { - directory: "." - filters: "*.mp3;*.wav" - } - - Files { - directory: "." - filters: "*.mp4" - } -} diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-3/testfile.qmlproject b/tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-3/testfile.qmlproject deleted file mode 100644 index a9c59cdb665..00000000000 --- a/tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-3/testfile.qmlproject +++ /dev/null @@ -1,93 +0,0 @@ -import QmlProject - -Project { - mainFile: "content/App.qml" - mainUiFile: "content/MainScreen.ui.qml" - - /* Include .qml, .js, and image files from current directory and subdirectories */ - QmlFiles { - directory: "content" - } - - QmlFiles { - directory: "imports" - } - - QmlFiles { - directory: "backend_mock" - } - - JavaScriptFiles { - directory: "content" - } - - JavaScriptFiles { - directory: "imports" - } - - ImageFiles { - directory: "content" - } - - Files { - filter: "*.conf" - files: ["qtquickcontrols2.conf"] - } - - Files { - filter: "qmldir" - directory: "." - } - - Files { - filter: "*.ttf;*.otf" - } - - Files { - filter: "*.wav;*.mp3" - } - - Files { - filter: "*.mp4" - } - - Files { - filter: "*.glsl;*.glslv;*.glslf;*.vsh;*.fsh;*.vert;*.frag" - } - - Files { - filter: "*.mesh" - directory: "content" - } - - Environment { - QT_QUICK_CONTROLS_CONF: "qtquickcontrols2.conf" - QT_AUTO_SCREEN_SCALE_FACTOR: "1" - QT_LOGGING_RULES: "qt.qml.connections=false" - QT_ENABLE_HIGHDPI_SCALING: "0" - /* Useful for debugging - QSG_VISUALIZE=batches - QSG_VISUALIZE=clip - QSG_VISUALIZE=changes - QSG_VISUALIZE=overdraw - */ - } - - qt6Project: true - - /* List of plugin directories passed to QML runtime */ - importPaths: [ "imports", "asset_imports", "backend_mock" ] - - /* Required for deployment */ - targetDirectory: "/opt/RobotArm" - - qdsVersion: "3.0" - - /* If any modules the project imports require widgets (e.g. QtCharts), widgetApp must be true */ - widgetApp: true - - multilanguageSupport: true - supportedLanguages: ["en"] - primaryLanguage: "en" - -} diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-3/testfile.qmltojson b/tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-3/testfile.qmltojson deleted file mode 100644 index 1892b9f3f9c..00000000000 --- a/tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-3/testfile.qmltojson +++ /dev/null @@ -1,161 +0,0 @@ -{ - "deployment": { - "targetDirectory": "/opt/RobotArm" - }, - "environment": { - "QT_AUTO_SCREEN_SCALE_FACTOR": "1", - "QT_ENABLE_HIGHDPI_SCALING": "0", - "QT_LOGGING_RULES": "qt.qml.connections=false", - "QT_QUICK_CONTROLS_CONF": "qtquickcontrols2.conf" - }, - "fileGroups": { - "config": { - "directories": [ - "." - ], - "files": [ - { - "name": "qtquickcontrols2.conf" - } - ], - "filters": [ - "*.conf" - ] - }, - "font": { - "directories": [ - "." - ], - "files": [ - ], - "filters": [ - "*.ttf", - "*.otf" - ] - }, - "image": { - "directories": [ - "content" - ], - "files": [ - ], - "filters": [ - "*.jpeg", - "*.jpg", - "*.png", - "*.svg", - "*.hdr", - ".ktx" - ] - }, - "javaScript": { - "directories": [ - "content", - "imports" - ], - "files": [ - ], - "filters": [ - "*.js", - "*.ts" - ] - }, - "meshes": { - "directories": [ - "content" - ], - "files": [ - ], - "filters": [ - "*.mesh" - ] - }, - "qml": { - "directories": [ - "content", - "imports", - "backend_mock" - ], - "files": [ - ], - "filters": [ - "*.qml" - ] - }, - "qmldir": { - "directories": [ - "." - ], - "files": [ - ], - "filters": [ - "qmldir" - ] - }, - "shader": { - "directories": [ - "." - ], - "files": [ - ], - "filters": [ - "*.glsl", - "*.glslv", - "*.glslf", - "*.vsh", - "*.fsh", - "*.vert", - "*.frag" - ] - }, - "sound": { - "directories": [ - "." - ], - "files": [ - ], - "filters": [ - "*.mp3", - "*.wav" - ] - }, - "video": { - "directories": [ - "." - ], - "files": [ - ], - "filters": [ - "*.mp4" - ] - } - }, - "fileVersion": 1, - "importPaths": [ - "imports", - "asset_imports", - "backend_mock" - ], - "language": { - "multiLanguageSupport": true, - "primaryLanguage": "en", - "supportedLanguages": [ - "en" - ] - }, - "mcuConfig": { - }, - "runConfig": { - "fileSelectors": [ - ], - "mainFile": "content/App.qml", - "mainUiFile": "content/MainScreen.ui.qml", - "widgetApp": true - }, - "shaderTool": { - }, - "versions": { - "designStudio": "3.0", - "qt": "6" - } -} diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-4/testfile.jsontoqml b/tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-4/testfile.jsontoqml deleted file mode 100644 index 10a20bd9914..00000000000 --- a/tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-4/testfile.jsontoqml +++ /dev/null @@ -1,65 +0,0 @@ -\\ prop: json-converted -\\ prop: auto-generated - -import QmlProject - -Project { - mainFile: "OutrunHVAC.qml" - mainUiFile: "Screen01.ui.qml" - targetDirectory: "/opt/OutrunHVAC" - widgetApp: false - importPaths: [ "imports","asset_imports" ] - - qdsVersion: "" - quickVersion: "" - qt6Project: true - qtForMCUs: true - - multilanguageSupport: false - primaryLanguage: "" - supportedLanguages: [ ] - - Environment { - QMLSCENE_CORE_PROFILE: "true" - QT_AUTO_SCREEN_SCALE_FACTOR: "1" - QT_QUICK_CONTROLS_CONF: "qtquickcontrols2.conf" - } - - ShaderTool { - args: "" - files: [ ] - } - - QmlFiles { - directory: "." - } - - JavaScriptFiles { - directory: "." - } - - ImageFiles { - directory: "." - } - - Files { - directory: "." - filters: "*.conf" - files: [ "qtquickcontrols2.conf" ] - } - - Files { - directory: "." - filters: "*.ttf;*.otf" - } - - Files { - directory: "." - filters: "*.mesh;*.vert;*.frag" - } - - Files { - directory: "." - filters: "qmldir" - } -} diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-4/testfile.qmlproject b/tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-4/testfile.qmlproject deleted file mode 100644 index 9b2e466fda5..00000000000 --- a/tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-4/testfile.qmlproject +++ /dev/null @@ -1,55 +0,0 @@ -/* File generated by Qt Creator */ - -import QmlProject 1.1 - -Project { - mainFile: "OutrunHVAC.qml" - - /* Include .qml, .js, and image files from current directory and subdirectories */ - QmlFiles { - directory: "." - } - - JavaScriptFiles { - directory: "." - } - - ImageFiles { - directory: "." - } - - Files { - filter: "*.conf" - files: ["qtquickcontrols2.conf"] - } - - Files { - filter: "qmldir" - directory: "." - } - - Files { - filter: "*.ttf;*.otf" - } - - Files { - filter: "*.mesh;*.vert;*.frag" - directory: "." - } - - Environment { - QT_QUICK_CONTROLS_CONF: "qtquickcontrols2.conf" - QT_AUTO_SCREEN_SCALE_FACTOR: "1" - QMLSCENE_CORE_PROFILE: "true" - } - - qt6Project: true - - /* List of plugin directories passed to QML runtime */ - importPaths: [ "imports", "asset_imports" ] - - /* Required for deployment */ - targetDirectory: "/opt/OutrunHVAC" - - mainUiFile: "Screen01.ui.qml" -} diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-4/testfile.qmltojson b/tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-4/testfile.qmltojson deleted file mode 100644 index 3ccd15a8390..00000000000 --- a/tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-4/testfile.qmltojson +++ /dev/null @@ -1,114 +0,0 @@ -{ - "deployment": { - "targetDirectory": "/opt/OutrunHVAC" - }, - "environment": { - "QMLSCENE_CORE_PROFILE": "true", - "QT_AUTO_SCREEN_SCALE_FACTOR": "1", - "QT_QUICK_CONTROLS_CONF": "qtquickcontrols2.conf" - }, - "fileGroups": { - "config": { - "directories": [ - "." - ], - "files": [ - { - "name": "qtquickcontrols2.conf" - } - ], - "filters": [ - "*.conf" - ] - }, - "font": { - "directories": [ - "." - ], - "files": [ - ], - "filters": [ - "*.ttf", - "*.otf" - ] - }, - "image": { - "directories": [ - "." - ], - "files": [ - ], - "filters": [ - "*.jpeg", - "*.jpg", - "*.png", - "*.svg", - "*.hdr", - ".ktx" - ] - }, - "javaScript": { - "directories": [ - "." - ], - "files": [ - ], - "filters": [ - "*.js", - "*.ts" - ] - }, - "meshes": { - "directories": [ - "." - ], - "files": [ - ], - "filters": [ - "*.mesh", - "*.vert", - "*.frag" - ] - }, - "qml": { - "directories": [ - "." - ], - "files": [ - ], - "filters": [ - "*.qml" - ] - }, - "qmldir": { - "directories": [ - "." - ], - "files": [ - ], - "filters": [ - "qmldir" - ] - } - }, - "fileVersion": 1, - "importPaths": [ - "imports", - "asset_imports" - ], - "language": { - }, - "mcuConfig": { - }, - "runConfig": { - "fileSelectors": [ - ], - "mainFile": "OutrunHVAC.qml", - "mainUiFile": "Screen01.ui.qml" - }, - "shaderTool": { - }, - "versions": { - "qt": "6" - } -} diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/test-converters.cpp b/tests/auto/qml/qmlprojectmanager/projectitem/test-converters.cpp deleted file mode 100644 index d232db3c0f0..00000000000 --- a/tests/auto/qml/qmlprojectmanager/projectitem/test-converters.cpp +++ /dev/null @@ -1,108 +0,0 @@ -// Copyright (C) 2016 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 "common.h" - -#include "projectitem/converters.h" - -//#define REGENERATE_DATA_SETS - -using namespace QmlProjectManager; - -class DataSet -{ -public: - DataSet(const QString &dataSetName) - : m_dataSetDirectory(testDataRootDir.path() + "/converter/" + dataSetName) - , m_qmlProjectFile(Utils::FilePath::fromString( - QString(m_dataSetDirectory.absolutePath()).append("/testfile.qmlproject"))) - , m_jsonToQmlProjectFile(Utils::FilePath::fromString( - QString(m_dataSetDirectory.absolutePath()).append("/testfile.jsontoqml"))) - , m_qmlProjectToJsonFile(Utils::FilePath::fromString( - QString(m_dataSetDirectory.absolutePath()).append("/testfile.qmltojson"))) - {} - - QString qmlProjectContent() const - { - return (m_qmlProjectFile.fileContents() ? m_qmlProjectFile.fileContents().value() : QString{}); - } - QString jsonToQmlProjectContent() const - { - return m_jsonToQmlProjectFile.fileContents() ? m_jsonToQmlProjectFile.fileContents().value() - : QString{}; - } - QString qmlProjectToJsonContent() const - { - return m_qmlProjectToJsonFile.fileContents() ? m_qmlProjectToJsonFile.fileContents().value() - : QString{}; - } - - QString dataSetPath() const { return m_dataSetDirectory.absolutePath(); } - QString dataSetName() const { return m_dataSetDirectory.dirName(); } - Utils::FilePath qmlProjectFile() const { return m_qmlProjectFile; } - Utils::FilePath jsonToQmlProjectFile() const { return m_jsonToQmlProjectFile; } - Utils::FilePath qmlProjectToJsonFile() const { return m_qmlProjectToJsonFile; } - -private: - QDir m_dataSetDirectory; - Utils::FilePath m_qmlProjectFile; - Utils::FilePath m_jsonToQmlProjectFile; - Utils::FilePath m_qmlProjectToJsonFile; -}; - -QVector getDataSets() -{ - QVector dataSets; - QDir testDataDir(testDataRootDir.path().append("/converter")); - testDataDir.setNameFilters({"test-set-*"}); - foreach (const QString &directory, testDataDir.entryList()) { - dataSets.append(DataSet{directory}); - } - return dataSets; -} - -#ifndef REGENERATE_DATA_SETS -TEST(QmlProjectConverterTests, QmlProjectToJson) -{ - foreach (const DataSet &dataSet, getDataSets()) { - qDebug() << "Data set name:" << dataSet.dataSetName(); - - QString targetContent = dataSet.qmlProjectToJsonContent().replace("\r\n", "\n"); - - QJsonObject jsonObject{ - QmlProjectManager::Converters::qmlProjectTojson(dataSet.qmlProjectFile())}; - QString convertedContent{QJsonDocument(jsonObject).toJson()}; - - ASSERT_EQ(convertedContent.toStdString(), targetContent.toStdString()); - } -} - -TEST(QmlProjectConverterTests, JsonToQmlProject) -{ - foreach (const DataSet &dataSet, getDataSets()) { - qDebug() << "Data set name:" << dataSet.dataSetName(); - - QString targetContent = dataSet.jsonToQmlProjectContent().replace("\r\n", "\n"); - - QString jsonContent = dataSet.qmlProjectToJsonContent(); - QJsonObject jsonObject{QJsonDocument::fromJson(jsonContent.toLatin1()).object()}; - QString convertedContent = QmlProjectManager::Converters::jsonToQmlProject(jsonObject); - - ASSERT_EQ(convertedContent.toStdString(), targetContent.toStdString()); - } -} - -#else -TEST(QmlProjectConverterTests, RegenerateDataSets) -{ - foreach (const DataSet &dataSet, getDataSets()) { - qDebug() << "Regenerating data set:" << dataSet.dataSetName(); - QJsonObject qml2json = Converters::qmlProjectTojson(dataSet.qmlProjectFile()); - QString json2qml = Converters::jsonToQmlProject(qml2json); - - dataSet.qmlProjectToJsonFile().writeFileContents(QJsonDocument(qml2json).toJson()); - dataSet.jsonToQmlProjectFile().writeFileContents(json2qml.toUtf8()); - } - SUCCEED(); -} -#endif diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/test-filefilters.cpp b/tests/auto/qml/qmlprojectmanager/projectitem/test-filefilters.cpp deleted file mode 100644 index bb7a60f8b23..00000000000 --- a/tests/auto/qml/qmlprojectmanager/projectitem/test-filefilters.cpp +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright (C) 2016 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 "common.h" -#include "projectitem/qmlprojectitem.h" - -const Utils::FilePath testDataDir{ - Utils::FilePath::fromString(testDataRootDir.path() + "/file-filters")}; -const Utils::FilePath projectFilePath{testDataDir.pathAppended("/MaterialBundle.qmlproject")}; -const QmlProjectManager::QmlProjectItem projectItem{projectFilePath}; -const Utils::FilePath fileListPath{testDataDir.pathAppended("/filelist.txt")}; - -TEST(QmlProjectItemFileFilterTests, TestFileFilters) -{ - QStringList fileNameList = QString::fromUtf8(fileListPath.fileContents().value()).replace("\r\n", "\n").split("\n"); - - for (const Utils::FilePath &filePath : projectItem.files()) { - const QString fileName{filePath.relativePathFrom(testDataDir).path()}; - const int index = fileNameList.indexOf(fileName); - ASSERT_NE(index, -1) << "file_is_missing_in_the_filelist:: " + fileName.toStdString(); - fileNameList.remove(index); - } - - ASSERT_EQ(fileNameList.size(), 0); -} diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/test-getters.cpp b/tests/auto/qml/qmlprojectmanager/projectitem/test-getters.cpp deleted file mode 100644 index 9a3367e3cc7..00000000000 --- a/tests/auto/qml/qmlprojectmanager/projectitem/test-getters.cpp +++ /dev/null @@ -1,162 +0,0 @@ -// Copyright (C) 2016 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 "common.h" -#include "projectitem/qmlprojectitem.h" - -static QString testDataDir{testDataRootDir.path() + "/getter-setter"}; -static QString qmlProjectFilePath1(testDataDir + "/testfile-1.qmlproject"); -static QString qmlProjectFilePath2(testDataDir + "/testfile-2.qmlproject"); - -struct TestDataSet -{ -public: - const QmlProjectManager::QmlProjectItem projectItem1{ - Utils::FilePath::fromString(qmlProjectFilePath1)}; - QmlProjectManager::QmlProjectItem projectItem2{Utils::FilePath::fromString(qmlProjectFilePath2)}; -} dataSet; - -TEST(QmlProjectProjectItemGetterTests, GetMainFileProject) -{ - ASSERT_EQ(dataSet.projectItem1.mainFile(), "content/App.qml"); - ASSERT_EQ(dataSet.projectItem2.mainFile(), ""); -} - -TEST(QmlProjectProjectItemGetterTests, GetMainUIFileProject) -{ - ASSERT_EQ(dataSet.projectItem1.mainUiFile(), "Screen01.ui.qml"); - ASSERT_EQ(dataSet.projectItem2.mainUiFile(), ""); -} - -TEST(QmlProjectProjectItemGetterTests, GetMcuProject) -{ - ASSERT_EQ(dataSet.projectItem1.isQt4McuProject(), true); - ASSERT_EQ(dataSet.projectItem2.isQt4McuProject(), false); -} - -TEST(QmlProjectProjectItemGetterTests, GetQtVersion) -{ - ASSERT_EQ(dataSet.projectItem1.versionQt(), "6"); - ASSERT_EQ(dataSet.projectItem2.versionQt(), "5"); -} - -TEST(QmlProjectProjectItemGetterTests, GetQtQuickVersion) -{ - ASSERT_EQ(dataSet.projectItem1.versionQtQuick(), "6.2"); - ASSERT_EQ(dataSet.projectItem2.versionQtQuick(), QString()); -} - -TEST(QmlProjectProjectItemGetterTests, GetDesignStudioVersion) -{ - ASSERT_EQ(dataSet.projectItem1.versionDesignStudio(), "3.9"); - ASSERT_EQ(dataSet.projectItem2.versionDesignStudio(), QString()); -} - -TEST(QmlProjectProjectItemGetterTests, GetSourceDirectory) -{ - ASSERT_EQ(dataSet.projectItem1.sourceDirectory().path(), testDataDir); -} - -TEST(QmlProjectProjectItemGetterTests, GetTargetDirectory) -{ - ASSERT_EQ(dataSet.projectItem1.targetDirectory(), "/opt/targetDirectory"); - ASSERT_EQ(dataSet.projectItem2.targetDirectory(), ""); -} - -TEST(QmlProjectProjectItemGetterTests, GetImportPaths) -{ - QString valsToCompare1 = dataSet.projectItem1.importPaths().join(";"); - QString valsToCompare2 = dataSet.projectItem2.importPaths().join(";"); - - ASSERT_EQ(valsToCompare1.toStdString(), "imports;asset_imports"); - ASSERT_EQ(valsToCompare2.toStdString(), ""); -} - -TEST(QmlProjectProjectItemGetterTests, GetFileSelectors) -{ - QString valsToCompare1 = dataSet.projectItem1.fileSelectors().join(";"); - QString valsToCompare2 = dataSet.projectItem2.fileSelectors().join(";"); - - ASSERT_EQ(valsToCompare1.toStdString(), "WXGA;darkTheme;ShowIndicator"); - ASSERT_EQ(valsToCompare2.toStdString(), ""); -} - -TEST(QmlProjectProjectItemGetterTests, GetMultiLanguageSupport) -{ - ASSERT_EQ(dataSet.projectItem1.multilanguageSupport(), true); - ASSERT_EQ(dataSet.projectItem2.multilanguageSupport(), false); -} - -TEST(QmlProjectProjectItemGetterTests, GetSupportedLanguages) -{ - QString valsToCompare1 = dataSet.projectItem1.supportedLanguages().join(";"); - QString valsToCompare2 = dataSet.projectItem2.supportedLanguages().join(";"); - - ASSERT_EQ(valsToCompare1.toStdString(), "en;fr"); - ASSERT_EQ(valsToCompare2.toStdString(), ""); -} - -TEST(QmlProjectProjectItemGetterTests, GetPrimaryLanguage) -{ - ASSERT_EQ(dataSet.projectItem1.primaryLanguage(), "en"); - ASSERT_EQ(dataSet.projectItem2.primaryLanguage(), ""); -} - -TEST(QmlProjectProjectItemGetterTests, GetWidgetApp) -{ - ASSERT_EQ(dataSet.projectItem1.widgetApp(), true); - ASSERT_EQ(dataSet.projectItem2.widgetApp(), false); -} - -TEST(QmlProjectProjectItemGetterTests, GetFileList) -{ - QString valsToCompare1, valsToCompare2; - - for (const auto &file : dataSet.projectItem1.files()) { - valsToCompare1.append(file.path()).append(";"); - } - - for (const auto &file : dataSet.projectItem2.files()) { - valsToCompare2.append(file.path()).append(";"); - } - - valsToCompare1.remove(valsToCompare1.length() - 1, 1); - valsToCompare2.remove(valsToCompare2.length() - 1, 1); - - ASSERT_EQ(valsToCompare1.toStdString(), - testDataDir.toStdString() + "/qtquickcontrols2.conf"); - ASSERT_EQ(valsToCompare2.toStdString(), ""); -} - -TEST(QmlProjectProjectItemGetterTests, GetShaderToolArgs) -{ - QString valsToCompare1 = dataSet.projectItem1.shaderToolArgs().join(";"); - QString valsToCompare2 = dataSet.projectItem2.shaderToolArgs().join(";"); - - ASSERT_EQ(valsToCompare1.toStdString(), "-s;--glsl;\"100 es,120,150\";--hlsl;50;--msl;12"); - ASSERT_EQ(valsToCompare2.toStdString(), ""); -} - -TEST(QmlProjectProjectItemGetterTests, GetShaderToolFiles) -{ - QString valsToCompare1 = dataSet.projectItem1.shaderToolFiles().join(";"); - QString valsToCompare2 = dataSet.projectItem2.shaderToolFiles().join(";"); - - ASSERT_EQ(valsToCompare1.toStdString(), "content/shaders/*"); - ASSERT_EQ(valsToCompare2.toStdString(), ""); -} - -TEST(QmlProjectProjectItemGetterTests, GetEnvironment) -{ - Utils::EnvironmentItems env1 = dataSet.projectItem1.environment(); - Utils::EnvironmentItems env2 = dataSet.projectItem2.environment(); - - ASSERT_EQ(env1[0].value.toStdString(), "qtquickcontrols2.conf"); - ASSERT_EQ(env2.isEmpty(), true); -} - -TEST(QmlProjectProjectItemGetterTests, GetForceFreeType) -{ - ASSERT_EQ(dataSet.projectItem1.forceFreeType(), true); - ASSERT_EQ(dataSet.projectItem2.forceFreeType(), false); -} diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/test-setters.cpp b/tests/auto/qml/qmlprojectmanager/projectitem/test-setters.cpp deleted file mode 100644 index 30a99d9e212..00000000000 --- a/tests/auto/qml/qmlprojectmanager/projectitem/test-setters.cpp +++ /dev/null @@ -1,161 +0,0 @@ -// Copyright (C) 2016 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 "common.h" -#include "projectitem/qmlprojectitem.h" - -using namespace QmlProjectManager; - -static QString filePath(testDataRootDir.path() + "/getter-setter/testfile-1.qmlproject"); -static QmlProjectItem projectItem{Utils::FilePath::fromString(filePath)}; - -#define call_mem_fn(ptr) ((projectItem).*(ptr)) - -template -void testerTemplate(void (QmlProjectItem::*setterFunc)(const T &), - T (QmlProjectItem::*getterFunc)(void) const, - const T &testingData) -{ - call_mem_fn(setterFunc)({testingData}); - ASSERT_EQ(call_mem_fn(getterFunc)(), testingData); -} - -template -void testerTemplate(void (QmlProjectItem::*setterFunc)(const T &), - T (QmlProjectItem::*getterFunc)(void) const, - void (QmlProjectItem::*adderFunc)(const Y &), - const Y &testingData) -{ - call_mem_fn(setterFunc)({testingData}); - ASSERT_EQ(call_mem_fn(getterFunc)(), T{testingData}); - - call_mem_fn(setterFunc)({}); - call_mem_fn(adderFunc)(testingData); - ASSERT_EQ(call_mem_fn(getterFunc)(), T{testingData}); -} - -TEST(QmlProjectProjectItemSetterTests, SetMainFileProject) -{ - testerTemplate(&QmlProjectItem::setMainFile, &QmlProjectItem::mainFile, "testing"); -} - -TEST(QmlProjectProjectItemSetterTests, SetMainUIFileProject) -{ - testerTemplate(&QmlProjectItem::setMainUiFile, &QmlProjectItem::mainUiFile, "testing"); -} - -TEST(QmlProjectProjectItemSetterTests, SetImportPaths) -{ - testerTemplate(&QmlProjectItem::setImportPaths, - &QmlProjectItem::importPaths, - &QmlProjectItem::addImportPath, - "testing"); -} - -TEST(QmlProjectProjectItemSetterTests, SetFileSelectors) -{ - testerTemplate(&QmlProjectItem::setFileSelectors, - &QmlProjectItem::fileSelectors, - &QmlProjectItem::addFileSelector, - "testing"); -} - -TEST(QmlProjectProjectItemSetterTests, SetMultiLanguageSupport) -{ - testerTemplate(&QmlProjectItem::setMultilanguageSupport, - &QmlProjectItem::multilanguageSupport, - true); - - testerTemplate(&QmlProjectItem::setMultilanguageSupport, - &QmlProjectItem::multilanguageSupport, - false); -} - -TEST(QmlProjectProjectItemSetterTests, SetSupportedLanguages) -{ - testerTemplate(&QmlProjectItem::setSupportedLanguages, - &QmlProjectItem::supportedLanguages, - &QmlProjectItem::addSupportedLanguage, - "testing"); -} - -TEST(QmlProjectProjectItemSetterTests, SetPrimaryLanguage) -{ - testerTemplate(&QmlProjectItem::setPrimaryLanguage, - &QmlProjectItem::primaryLanguage, - "testing"); -} - -TEST(QmlProjectProjectItemSetterTests, SetWidgetApp) -{ - testerTemplate(&QmlProjectItem::setWidgetApp, &QmlProjectItem::widgetApp, true); - testerTemplate(&QmlProjectItem::setWidgetApp, &QmlProjectItem::widgetApp, false); -} - -TEST(QmlProjectProjectItemSetterTests, SetShaderToolArgs) -{ - testerTemplate(&QmlProjectItem::setShaderToolArgs, - &QmlProjectItem::shaderToolArgs, - &QmlProjectItem::addShaderToolArg, - "testing"); -} - -TEST(QmlProjectProjectItemSetterTests, SetShaderToolFiles) -{ - testerTemplate(&QmlProjectItem::setShaderToolFiles, - &QmlProjectItem::shaderToolFiles, - &QmlProjectItem::addShaderToolFile, - "testing"); -} - -TEST(QmlProjectProjectItemSetterTests, SetForceFreeType) -{ - testerTemplate(&QmlProjectItem::setForceFreeType, &QmlProjectItem::forceFreeType, true); - testerTemplate(&QmlProjectItem::setForceFreeType, &QmlProjectItem::forceFreeType, false); -} - -TEST(QmlProjectProjectItemSetterTests, SetQtVersion) -{ - testerTemplate(&QmlProjectItem::setVersionQt, &QmlProjectItem::versionQt, "6"); - testerTemplate(&QmlProjectItem::setVersionQt, &QmlProjectItem::versionQt, "5.3"); -} - -TEST(QmlProjectProjectItemSetterTests, SetQtQuickVersion) -{ - testerTemplate(&QmlProjectItem::setVersionQtQuick, &QmlProjectItem::versionQtQuick, "6"); - testerTemplate(&QmlProjectItem::setVersionQtQuick, &QmlProjectItem::versionQtQuick, "5.3"); -} - -TEST(QmlProjectProjectItemSetterTests, SetDesignStudio) -{ - testerTemplate(&QmlProjectItem::setVersionDesignStudio, &QmlProjectItem::versionDesignStudio, "6"); - testerTemplate(&QmlProjectItem::setVersionDesignStudio, &QmlProjectItem::versionDesignStudio, "5.3"); -} - -/** -TEST(QmlProjectProjectItemSetterTests, SetEnvironment) -{ - //FIXME: implement this -} - -*/ - -// not available as of now -//TEST(QmlProjectProjectItemSetterTests, SetMcuProject) -//{ -// ASSERT_EQ(dataSet.projectItem1.isQt4McuProject(), true); -// ASSERT_EQ(dataSet.projectItem2.isQt4McuProject(), false); -//} - -// not available as of now -//TEST(QmlProjectProjectItemSetterTests, SetSourceDirectory) -//{ -// ASSERT_EQ(dataSet.projectItem1.sourceDirectory(), testDataDir.path()); -//} - -// not available as of now -//TEST(QmlProjectProjectItemSetterTests, SetTargetDirectory) -//{ -// ASSERT_EQ(dataSet.projectItem1.targetDirectory(), "/opt/targetDirectory"); -// ASSERT_EQ(dataSet.projectItem2.targetDirectory(), ""); -//} diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/tst_projectitem.cpp b/tests/auto/qml/qmlprojectmanager/projectitem/tst_projectitem.cpp deleted file mode 100644 index 023f8ffdb26..00000000000 --- a/tests/auto/qml/qmlprojectmanager/projectitem/tst_projectitem.cpp +++ /dev/null @@ -1,8 +0,0 @@ - -#include "common.h" - -int main(int argc, char *argv[]) -{ - testing::InitGoogleTest(&argc, argv); - return RUN_ALL_TESTS(); -} diff --git a/tests/unit/CMakeLists.txt b/tests/unit/CMakeLists.txt index 3a36658ae4f..8971a289dea 100644 --- a/tests/unit/CMakeLists.txt +++ b/tests/unit/CMakeLists.txt @@ -37,3 +37,4 @@ if (NOT QT_CREATOR_API_DEFINED) endif() add_subdirectory(unittest) +add_subdirectory(tools) diff --git a/tests/unit/README.md b/tests/unit/README.md new file mode 100644 index 00000000000..38911b31ec2 --- /dev/null +++ b/tests/unit/README.md @@ -0,0 +1,49 @@ +# Contribution Guideline + +This document summarizes; + +* Best practices for writing tests +* How to add a new test +* How to build only specific test + +All tests here depend on the [GoogleTest][1] framework. + +## Best Practices + +We're following those patterns/approaches; + +* The Arrange, Act, and Assert (AAA) Pattern +* Given When Then (GWT) Pattern + +## Adding a New Unit Test + +* Please add your tests under `unit/unittest`. No subfolders are needed. +* Name your class as `foo-test.cpp` + +* Always include `googletest.h` header. Without that you may get the printer function can be broken because the are not anymore ODR (because of weak linking to printers for example). It is also necessary for nice printers, also adds Qt known matchers. + +## Building Tests + +> Note: +> When you're building the application from the terminal, you can set environment variables instead of settings CMake flags. +> The corresponding environment variable name is same with CMake variable name but with a 'QTC_' prefix. +> CMake Variable: WITH_TESTS +> Environment Variable: QTC_WITH_TESTS + +You have to enable tests with the following CMake variable otherwise the default configuration skips them. + +```bash +WITH_TESTS=ON +``` + +## Building Specific Tests + +After enabling tests you can use test-specific CMake flags to customize which tests should be built instead of building all of them at once. Please check the relevant CMake file to see which variable is required to enable that specific test. + +```bash +BUILD_TESTS_BY_DEFAULT=OFF +BUILD_TEST_UNITTEST=ON +BUILD_TEST_TST_QML_TESTCORE=ON +``` + +[1]: https://github.com/google/googletest diff --git a/tests/unit/tools/CMakeLists.txt b/tests/unit/tools/CMakeLists.txt new file mode 100644 index 00000000000..05b561491fc --- /dev/null +++ b/tests/unit/tools/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(qmlprojectmanager) diff --git a/tests/unit/tools/qmlprojectmanager/CMakeLists.txt b/tests/unit/tools/qmlprojectmanager/CMakeLists.txt new file mode 100644 index 00000000000..0f5a41c6331 --- /dev/null +++ b/tests/unit/tools/qmlprojectmanager/CMakeLists.txt @@ -0,0 +1,16 @@ +project(QmlProjectManagerConverterDataCreator) + +add_compile_definitions(QT_CREATOR) + +add_executable(${PROJECT_NAME} + main.cpp +) + +set_target_properties(${PROJECT_NAME} + PROPERTIES + OUTPUT_NAME "dataSetGenerator" +) + +target_link_libraries(${PROJECT_NAME} + QmlJS Utils ProjectExplorer QmlProjectManagerLib +) diff --git a/tests/unit/tools/qmlprojectmanager/main.cpp b/tests/unit/tools/qmlprojectmanager/main.cpp new file mode 100644 index 00000000000..688a8e6f540 --- /dev/null +++ b/tests/unit/tools/qmlprojectmanager/main.cpp @@ -0,0 +1,95 @@ +// Copyright (C) 2020 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include +#include + +class DataSet +{ +public: + DataSet(const QString &rootDir) + : m_rootDir(rootDir) + {} + void setDataSource(const QString &dataSetName) + { + m_dataSetDirectory.setPath(m_rootDir.path() + "/" + dataSetName); + + m_qmlProjectFile = Utils::FilePath::fromString( + QString(m_dataSetDirectory.absolutePath()).append("/testfile.qmlproject")); + m_jsonToQmlProjectFile = Utils::FilePath::fromString( + QString(m_dataSetDirectory.absolutePath()).append("/testfile.jsontoqml")); + m_qmlProjectToJsonFile = Utils::FilePath::fromString( + QString(m_dataSetDirectory.absolutePath()).append("/testfile.qmltojson")); + } + + QString qmlProjectContent() const + { + return (m_qmlProjectFile.fileContents() + ? QString::fromLatin1(m_qmlProjectFile.fileContents().value()) + : QString{}); + } + QString jsonToQmlProjectContent() const + { + return m_jsonToQmlProjectFile.fileContents() + ? QString::fromLatin1(m_jsonToQmlProjectFile.fileContents().value()) + : QString{}; + } + QString qmlProjectToJsonContent() const + { + return m_qmlProjectToJsonFile.fileContents() + ? QString::fromLatin1(m_qmlProjectToJsonFile.fileContents().value()) + : QString{}; + } + + QString dataSetPath() const { return m_dataSetDirectory.absolutePath(); } + QString dataSetName() const { return m_dataSetDirectory.dirName(); } + Utils::FilePath qmlProjectFile() const { return m_qmlProjectFile; } + Utils::FilePath jsonToQmlProjectFile() const { return m_jsonToQmlProjectFile; } + Utils::FilePath qmlProjectToJsonFile() const { return m_qmlProjectToJsonFile; } + +private: + QDir m_rootDir; + QDir m_dataSetDirectory; + Utils::FilePath m_qmlProjectFile; + Utils::FilePath m_jsonToQmlProjectFile; + Utils::FilePath m_qmlProjectToJsonFile; +}; + +int main(int argc, char **argv) +{ + const QString helpText{"./dataSetGenerator [path]\n" + "[path]: Path to the data set folders. The default is current dir.\n" + " Folder names should be in the form of test-set-x.\n"}; + + QDir dataSetPath{QDir::currentPath()}; + if (argc >= 2) { + dataSetPath.setPath(argv[1]); + } + + if (!dataSetPath.exists()) { + qDebug() << "Data path does not exist:" << dataSetPath.path() << Qt::endl; + qDebug().noquote() << helpText; + return -1; + } + + QStringList dataSetList{dataSetPath.entryList({"test-set-*"})}; + if (!dataSetList.size()) { + qDebug() << "No test sets are available under" << dataSetPath.path() << Qt::endl; + qDebug().noquote() << helpText; + return -1; + } + + DataSet dataSet(dataSetPath.path()); + for (const auto &dataSetName : dataSetList) { + dataSet.setDataSource(dataSetName); + + qDebug() << "Regenerating data set:" << dataSet.dataSetName(); + QJsonObject qml2json = QmlProjectManager::Converters::qmlProjectTojson( + dataSet.qmlProjectFile()); + QString json2qml = QmlProjectManager::Converters::jsonToQmlProject(qml2json); + + dataSet.qmlProjectToJsonFile().writeFileContents(QJsonDocument(qml2json).toJson()); + dataSet.jsonToQmlProjectFile().writeFileContents(json2qml.toUtf8()); + } + return 0; +} diff --git a/tests/unit/unittest/CMakeLists.txt b/tests/unit/unittest/CMakeLists.txt index 3c8a5652436..d0fde6d5d9a 100644 --- a/tests/unit/unittest/CMakeLists.txt +++ b/tests/unit/unittest/CMakeLists.txt @@ -35,7 +35,8 @@ add_qtc_test(unittest GTEST UNIT_TESTS DONT_CHECK_MESSAGE_COUNTER QTC_RESOURCE_DIR="${CMAKE_CURRENT_LIST_DIR}/../../../share/qtcreator" - TESTDATA_DIR="${CMAKE_CURRENT_BINARY_DIR}/data" + TESTDATA_DIR="${CMAKE_CURRENT_SOURCE_DIR}/data" + UNITTEST_DIR="${CMAKE_CURRENT_SOURCE_DIR}" TEST_RELATIVE_LIBEXEC_PATH="${TEST_RELATIVE_LIBEXEC_PATH}" QT6_INSTALL_PREFIX="${QT6_INSTALL_PREFIX}" QDS_MODEL_USE_PROJECTSTORAGEINTERFACE @@ -368,3 +369,5 @@ set_property(SOURCE ${PROJECTSTORAGE_EXCLUDED_SOURCES} PROPERTY SKIP_AUTOMOC ON) file(GLOB UNITTEST_EXCLUDED_SOURCES *.cpp) set_property(SOURCE ${UNITTEST_EXCLUDED_SOURCES} PROPERTY SKIP_AUTOMOC ON) + +add_subdirectory(qmlprojectmanager) diff --git a/tests/unit/unittest/google-using-declarations.h b/tests/unit/unittest/google-using-declarations.h index 6b2cd4721dd..198bbce7842 100644 --- a/tests/unit/unittest/google-using-declarations.h +++ b/tests/unit/unittest/google-using-declarations.h @@ -56,5 +56,6 @@ using testing::StrEq; using testing::Throw; using testing::TypedEq; using testing::UnorderedElementsAre; +using testing::UnorderedElementsAreArray; using testing::VariantWith; using testing::WithArg; diff --git a/tests/unit/unittest/modulescanner-test.cpp b/tests/unit/unittest/modulescanner-test.cpp index b4632e0cb41..554e3fc8398 100644 --- a/tests/unit/unittest/modulescanner-test.cpp +++ b/tests/unit/unittest/modulescanner-test.cpp @@ -118,6 +118,7 @@ TEST_F(ModuleScanner, Version) scanner.scan(QStringList{TESTDATA_DIR "/modulescanner"}); ASSERT_THAT(scanner.modules(), ElementsAre(AllOf(UrlProperty("Example"), VersionProperty("1.3")))); + } TEST_F(ModuleScanner, NoVersion) diff --git a/tests/unit/unittest/qmlprojectmanager/CMakeLists.txt b/tests/unit/unittest/qmlprojectmanager/CMakeLists.txt new file mode 100644 index 00000000000..9b5037fc527 --- /dev/null +++ b/tests/unit/unittest/qmlprojectmanager/CMakeLists.txt @@ -0,0 +1,7 @@ +extend_qtc_test(unittest + DEPENDS + QmlProjectManagerLib + SOURCES + converters-test.cpp + projectitem-test.cpp +) diff --git a/tests/unit/unittest/qmlprojectmanager/converters-test.cpp b/tests/unit/unittest/qmlprojectmanager/converters-test.cpp new file mode 100644 index 00000000000..efa2b647aeb --- /dev/null +++ b/tests/unit/unittest/qmlprojectmanager/converters-test.cpp @@ -0,0 +1,94 @@ +// 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 "googletest.h" // IWYU pragma: keep + +#include + +#include + +namespace { +constexpr QLatin1String localTestDataDir{UNITTEST_DIR "/qmlprojectmanager/data"}; + +class DataSet : public testing::TestWithParam +{ +public: + void setDataSource(const QString &dataSetName) + { + m_dataSetDirectory.setPath(localTestDataDir + "/converter/" + dataSetName); + + m_qmlProjectFile = Utils::FilePath::fromString( + QString(m_dataSetDirectory.absolutePath()).append("/testfile.qmlproject")); + m_jsonToQmlProjectFile = Utils::FilePath::fromString( + QString(m_dataSetDirectory.absolutePath()).append("/testfile.jsontoqml")); + m_qmlProjectToJsonFile = Utils::FilePath::fromString( + QString(m_dataSetDirectory.absolutePath()).append("/testfile.qmltojson")); + } + + QString qmlProjectContent() const + { + return (m_qmlProjectFile.fileContents() + ? QString::fromLatin1(m_qmlProjectFile.fileContents().value()) + : QString{}); + } + QString jsonToQmlProjectContent() const + { + return m_jsonToQmlProjectFile.fileContents() + ? QString::fromLatin1(m_jsonToQmlProjectFile.fileContents().value()) + : QString{}; + } + QString qmlProjectToJsonContent() const + { + return m_qmlProjectToJsonFile.fileContents() + ? QString::fromLatin1(m_qmlProjectToJsonFile.fileContents().value()) + : QString{}; + } + + QString dataSetPath() const { return m_dataSetDirectory.absolutePath(); } + QString dataSetName() const { return m_dataSetDirectory.dirName(); } + Utils::FilePath qmlProjectFile() const { return m_qmlProjectFile; } + Utils::FilePath jsonToQmlProjectFile() const { return m_jsonToQmlProjectFile; } + Utils::FilePath qmlProjectToJsonFile() const { return m_qmlProjectToJsonFile; } + +private: + QDir m_dataSetDirectory; + Utils::FilePath m_qmlProjectFile; + Utils::FilePath m_jsonToQmlProjectFile; + Utils::FilePath m_qmlProjectToJsonFile; +}; + +INSTANTIATE_TEST_SUITE_P(ConverterTests, + DataSet, + ::testing::Values(QString("test-set-1"), QString("test-set-2"))); + +TEST_P(DataSet, QmlProjectToJson) +{ + // GIVEN + setDataSource(GetParam()); + QString targetContent = qmlProjectToJsonContent().replace("\r\n", "\n"); + auto qmlFile = qmlProjectFile(); + + // WHEN + auto jsonObject = QmlProjectManager::Converters::qmlProjectTojson(qmlFile); + + // THEN + QString convertedContent{QString::fromLatin1(QJsonDocument(jsonObject).toJson())}; + ASSERT_THAT(convertedContent, Eq(targetContent)); +} + +TEST_P(DataSet, JsonToQmlProject) +{ + // GIVEN + setDataSource(GetParam()); + QString targetContent = jsonToQmlProjectContent().replace("\r\n", "\n"); + auto jsonContent = qmlProjectToJsonContent().toLatin1(); + + // WHEN + auto jsonObject{QJsonDocument::fromJson(jsonContent).object()}; + + // THEN + QString convertedContent = QmlProjectManager::Converters::jsonToQmlProject(jsonObject); + ASSERT_THAT(convertedContent, Eq(targetContent)); +} + +} // namespace diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/README.md b/tests/unit/unittest/qmlprojectmanager/data/README.md similarity index 75% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/README.md rename to tests/unit/unittest/qmlprojectmanager/data/README.md index 309d453e98e..0edd94edd93 100644 --- a/tests/auto/qml/qmlprojectmanager/projectitem/data/README.md +++ b/tests/unit/unittest/qmlprojectmanager/data/README.md @@ -22,21 +22,6 @@ Test functions iterate over the "test-set-*" folders and run the tests by using ### test-set-2 -* **purpose**: testing complex qmlproject file convertion -* **origin**: material bundle example - -### test-set-3 - -* **purpose**: testing complex qmlproject file convertion -* **origin**: robot arm example - -### test-set-4 - -* **purpose**: testing complex qmlproject file convertion -* **origin**: outrun hvac example - -### test-set-5 - * **purpose**: testing fileselectors * **origin**: file selectors example from playground diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-1/testfile.jsontoqml b/tests/unit/unittest/qmlprojectmanager/data/converter/test-set-1/testfile.jsontoqml similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-1/testfile.jsontoqml rename to tests/unit/unittest/qmlprojectmanager/data/converter/test-set-1/testfile.jsontoqml diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-1/testfile.qmlproject b/tests/unit/unittest/qmlprojectmanager/data/converter/test-set-1/testfile.qmlproject similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-1/testfile.qmlproject rename to tests/unit/unittest/qmlprojectmanager/data/converter/test-set-1/testfile.qmlproject diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-1/testfile.qmltojson b/tests/unit/unittest/qmlprojectmanager/data/converter/test-set-1/testfile.qmltojson similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-1/testfile.qmltojson rename to tests/unit/unittest/qmlprojectmanager/data/converter/test-set-1/testfile.qmltojson diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-5/testfile.jsontoqml b/tests/unit/unittest/qmlprojectmanager/data/converter/test-set-2/testfile.jsontoqml similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-5/testfile.jsontoqml rename to tests/unit/unittest/qmlprojectmanager/data/converter/test-set-2/testfile.jsontoqml diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-5/testfile.qmlproject b/tests/unit/unittest/qmlprojectmanager/data/converter/test-set-2/testfile.qmlproject similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-5/testfile.qmlproject rename to tests/unit/unittest/qmlprojectmanager/data/converter/test-set-2/testfile.qmlproject diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-5/testfile.qmltojson b/tests/unit/unittest/qmlprojectmanager/data/converter/test-set-2/testfile.qmltojson similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-5/testfile.qmltojson rename to tests/unit/unittest/qmlprojectmanager/data/converter/test-set-2/testfile.qmltojson diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/MaterialBundle.qmlproject b/tests/unit/unittest/qmlprojectmanager/data/file-filters/MaterialBundle.qmlproject similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/MaterialBundle.qmlproject rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/MaterialBundle.qmlproject diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/MaterialBundle.qmlproject.qtds b/tests/unit/unittest/qmlprojectmanager/data/file-filters/MaterialBundle.qmlproject.qtds similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/MaterialBundle.qmlproject.qtds rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/MaterialBundle.qmlproject.qtds diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/MaterialLibrary.qrc b/tests/unit/unittest/qmlprojectmanager/data/file-filters/MaterialLibrary.qrc similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/MaterialLibrary.qrc rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/MaterialLibrary.qrc diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/CMakeLists.txt b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/CMakeLists.txt similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/CMakeLists.txt rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/CMakeLists.txt diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/CMakeLists.txt b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/CMakeLists.txt similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/CMakeLists.txt rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/CMakeLists.txt diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/AcrylicPaintMaterial.qml b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/AcrylicPaintMaterial.qml similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/AcrylicPaintMaterial.qml rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/AcrylicPaintMaterial.qml diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/AluminiumMaterial.qml b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/AluminiumMaterial.qml similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/AluminiumMaterial.qml rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/AluminiumMaterial.qml diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/AsphaltMaterial.qml b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/AsphaltMaterial.qml similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/AsphaltMaterial.qml rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/AsphaltMaterial.qml diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/BrickMaterial.qml b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/BrickMaterial.qml similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/BrickMaterial.qml rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/BrickMaterial.qml diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/CMakeLists.txt b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/CMakeLists.txt similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/CMakeLists.txt rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/CMakeLists.txt diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/CarPaintGlitterMaterial.qml b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/CarPaintGlitterMaterial.qml similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/CarPaintGlitterMaterial.qml rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/CarPaintGlitterMaterial.qml diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/CarPaintMaterial.qml b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/CarPaintMaterial.qml similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/CarPaintMaterial.qml rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/CarPaintMaterial.qml diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/CarbonFiberMaterial.qml b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/CarbonFiberMaterial.qml similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/CarbonFiberMaterial.qml rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/CarbonFiberMaterial.qml diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/CeramicMaterial.qml b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/CeramicMaterial.qml similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/CeramicMaterial.qml rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/CeramicMaterial.qml diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/ChromeMaterial.qml b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/ChromeMaterial.qml similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/ChromeMaterial.qml rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/ChromeMaterial.qml diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/ConcreteMaterial.qml b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/ConcreteMaterial.qml similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/ConcreteMaterial.qml rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/ConcreteMaterial.qml diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/CopperMaterial.qml b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/CopperMaterial.qml similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/CopperMaterial.qml rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/CopperMaterial.qml diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/FabricMaterial.qml b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/FabricMaterial.qml similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/FabricMaterial.qml rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/FabricMaterial.qml diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/FabricRoughMaterial.qml b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/FabricRoughMaterial.qml similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/FabricRoughMaterial.qml rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/FabricRoughMaterial.qml diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/FabricSatinMaterial.qml b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/FabricSatinMaterial.qml similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/FabricSatinMaterial.qml rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/FabricSatinMaterial.qml diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/GlassMaterial.qml b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/GlassMaterial.qml similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/GlassMaterial.qml rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/GlassMaterial.qml diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/GlassTintedMaterial.qml b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/GlassTintedMaterial.qml similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/GlassTintedMaterial.qml rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/GlassTintedMaterial.qml diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/GoldMaterial.qml b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/GoldMaterial.qml similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/GoldMaterial.qml rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/GoldMaterial.qml diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/LeatherMaterial.qml b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/LeatherMaterial.qml similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/LeatherMaterial.qml rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/LeatherMaterial.qml diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/MirrorMaterial.qml b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/MirrorMaterial.qml similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/MirrorMaterial.qml rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/MirrorMaterial.qml diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/PaperMaterial.qml b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/PaperMaterial.qml similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/PaperMaterial.qml rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/PaperMaterial.qml diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/PlasticMatteMaterial.qml b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/PlasticMatteMaterial.qml similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/PlasticMatteMaterial.qml rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/PlasticMatteMaterial.qml diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/PlasticShinyMaterial.qml b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/PlasticShinyMaterial.qml similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/PlasticShinyMaterial.qml rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/PlasticShinyMaterial.qml diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/PlasticTexturedMaterial.qml b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/PlasticTexturedMaterial.qml similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/PlasticTexturedMaterial.qml rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/PlasticTexturedMaterial.qml diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/RubberMaterial.qml b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/RubberMaterial.qml similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/RubberMaterial.qml rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/RubberMaterial.qml diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/SilverMaterial.qml b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/SilverMaterial.qml similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/SilverMaterial.qml rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/SilverMaterial.qml diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/SteelBrushedMaterial.qml b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/SteelBrushedMaterial.qml similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/SteelBrushedMaterial.qml rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/SteelBrushedMaterial.qml diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/SteelFloorMaterial.qml b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/SteelFloorMaterial.qml similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/SteelFloorMaterial.qml rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/SteelFloorMaterial.qml diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/SteelMaterial.qml b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/SteelMaterial.qml similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/SteelMaterial.qml rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/SteelMaterial.qml diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/StoneMaterial.qml b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/StoneMaterial.qml similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/StoneMaterial.qml rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/StoneMaterial.qml diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/WaxMaterial.qml b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/WaxMaterial.qml similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/WaxMaterial.qml rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/WaxMaterial.qml diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/WoodMaterial.qml b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/WoodMaterial.qml similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/WoodMaterial.qml rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/WoodMaterial.qml diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/WoodParquetMaterial.qml b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/WoodParquetMaterial.qml similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/WoodParquetMaterial.qml rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/WoodParquetMaterial.qml diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/WoodPlanksMaterial.qml b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/WoodPlanksMaterial.qml similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/WoodPlanksMaterial.qml rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/WoodPlanksMaterial.qml diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/_asset_ref.json b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/_asset_ref.json similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/_asset_ref.json rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/_asset_ref.json diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/acrylicpaint.metainfo b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/acrylicpaint.metainfo similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/acrylicpaint.metainfo rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/acrylicpaint.metainfo diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/aluminium.metainfo b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/aluminium.metainfo similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/aluminium.metainfo rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/aluminium.metainfo diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/asphalt.metainfo b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/asphalt.metainfo similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/asphalt.metainfo rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/asphalt.metainfo diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/brick.metainfo b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/brick.metainfo similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/brick.metainfo rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/brick.metainfo diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/carbonfiber.metainfo b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/carbonfiber.metainfo similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/carbonfiber.metainfo rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/carbonfiber.metainfo diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/carpaint.metainfo b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/carpaint.metainfo similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/carpaint.metainfo rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/carpaint.metainfo diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/carpaintglitter.metainfo b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/carpaintglitter.metainfo similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/carpaintglitter.metainfo rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/carpaintglitter.metainfo diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/ceramic.metainfo b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/ceramic.metainfo similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/ceramic.metainfo rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/ceramic.metainfo diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/chrome.metainfo b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/chrome.metainfo similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/chrome.metainfo rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/chrome.metainfo diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/concrete.metainfo b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/concrete.metainfo similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/concrete.metainfo rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/concrete.metainfo diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/copper.metainfo b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/copper.metainfo similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/copper.metainfo rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/copper.metainfo diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/fabric.metainfo b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/fabric.metainfo similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/fabric.metainfo rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/fabric.metainfo diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/fabricrough.metainfo b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/fabricrough.metainfo similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/fabricrough.metainfo rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/fabricrough.metainfo diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/fabricsatin.metainfo b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/fabricsatin.metainfo similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/fabricsatin.metainfo rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/fabricsatin.metainfo diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/glass.metainfo b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/glass.metainfo similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/glass.metainfo rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/glass.metainfo diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/glasstinted.metainfo b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/glasstinted.metainfo similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/glasstinted.metainfo rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/glasstinted.metainfo diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/gold.metainfo b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/gold.metainfo similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/gold.metainfo rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/gold.metainfo diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/images/material.png b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/images/material.png similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/images/material.png rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/images/material.png diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/images/material16.png b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/images/material16.png similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/images/material16.png rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/images/material16.png diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/images/material@2x.png b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/images/material@2x.png similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/images/material@2x.png rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/images/material@2x.png diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/leather.metainfo b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/leather.metainfo similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/leather.metainfo rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/leather.metainfo diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/mirror.metainfo b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/mirror.metainfo similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/mirror.metainfo rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/mirror.metainfo diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/paper.metainfo b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/paper.metainfo similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/paper.metainfo rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/paper.metainfo diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/plasticmatte.metainfo b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/plasticmatte.metainfo similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/plasticmatte.metainfo rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/plasticmatte.metainfo diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/plasticshiny.metainfo b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/plasticshiny.metainfo similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/plasticshiny.metainfo rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/plasticshiny.metainfo diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/plastictextured.metainfo b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/plastictextured.metainfo similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/plastictextured.metainfo rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/plastictextured.metainfo diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/rubber.metainfo b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/rubber.metainfo similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/rubber.metainfo rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/rubber.metainfo diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/silver.metainfo b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/silver.metainfo similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/silver.metainfo rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/silver.metainfo diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/steel.metainfo b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/steel.metainfo similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/steel.metainfo rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/steel.metainfo diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/steelbrushed.metainfo b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/steelbrushed.metainfo similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/steelbrushed.metainfo rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/steelbrushed.metainfo diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/steelfloor.metainfo b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/steelfloor.metainfo similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/steelfloor.metainfo rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/steelfloor.metainfo diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/stone.metainfo b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/stone.metainfo similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/stone.metainfo rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/stone.metainfo diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/wax.metainfo b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/wax.metainfo similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/wax.metainfo rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/wax.metainfo diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/wood.metainfo b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/wood.metainfo similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/wood.metainfo rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/wood.metainfo diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/woodparquet.metainfo b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/woodparquet.metainfo similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/woodparquet.metainfo rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/woodparquet.metainfo diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/woodplanks.metainfo b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/woodplanks.metainfo similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/woodplanks.metainfo rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/designer/woodplanks.metainfo diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Asphalt010_2K_NormalGL.png b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Asphalt010_2K_NormalGL.png similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Asphalt010_2K_NormalGL.png rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Asphalt010_2K_NormalGL.png diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Asphalt010_2K_Opacity.png b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Asphalt010_2K_Opacity.png similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Asphalt010_2K_Opacity.png rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Asphalt010_2K_Opacity.png diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Asphalt010_2K_Roughness.png b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Asphalt010_2K_Roughness.png similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Asphalt010_2K_Roughness.png rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Asphalt010_2K_Roughness.png diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Bricks026_2K_AmbientOcclusion.png b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Bricks026_2K_AmbientOcclusion.png similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Bricks026_2K_AmbientOcclusion.png rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Bricks026_2K_AmbientOcclusion.png diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Bricks026_2K_Color.png b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Bricks026_2K_Color.png similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Bricks026_2K_Color.png rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Bricks026_2K_Color.png diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Bricks026_2K_NormalGL.png b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Bricks026_2K_NormalGL.png similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Bricks026_2K_NormalGL.png rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Bricks026_2K_NormalGL.png diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Bricks026_2K_Roughness.png b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Bricks026_2K_Roughness.png similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Bricks026_2K_Roughness.png rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Bricks026_2K_Roughness.png diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Concrete032_2K_NormalGL.png b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Concrete032_2K_NormalGL.png similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Concrete032_2K_NormalGL.png rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Concrete032_2K_NormalGL.png diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Concrete032_2K_Roughness.png b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Concrete032_2K_Roughness.png similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Concrete032_2K_Roughness.png rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Concrete032_2K_Roughness.png diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/DiamondPlate001_2K_NormalGL.png b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/DiamondPlate001_2K_NormalGL.png similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/DiamondPlate001_2K_NormalGL.png rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/DiamondPlate001_2K_NormalGL.png diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/DiamondPlate001_2K_Roughness.png b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/DiamondPlate001_2K_Roughness.png similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/DiamondPlate001_2K_Roughness.png rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/DiamondPlate001_2K_Roughness.png diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Fabric004_2K_NormalGL.png b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Fabric004_2K_NormalGL.png similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Fabric004_2K_NormalGL.png rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Fabric004_2K_NormalGL.png diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Fabric030_2K_Displacement.png b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Fabric030_2K_Displacement.png similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Fabric030_2K_Displacement.png rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Fabric030_2K_Displacement.png diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Fabric030_2K_NormalGL.png b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Fabric030_2K_NormalGL.png similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Fabric030_2K_NormalGL.png rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Fabric030_2K_NormalGL.png diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Fabric030_2K_Roughness.png b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Fabric030_2K_Roughness.png similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Fabric030_2K_Roughness.png rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Fabric030_2K_Roughness.png diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Fabric031_2K_Displacement.png b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Fabric031_2K_Displacement.png similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Fabric031_2K_Displacement.png rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Fabric031_2K_Displacement.png diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Fabric031_2K_NormalGL.png b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Fabric031_2K_NormalGL.png similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Fabric031_2K_NormalGL.png rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Fabric031_2K_NormalGL.png diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Fabric031_2K_Roughness.png b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Fabric031_2K_Roughness.png similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Fabric031_2K_Roughness.png rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Fabric031_2K_Roughness.png diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/LDR_RGB1_3.png b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/LDR_RGB1_3.png similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/LDR_RGB1_3.png rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/LDR_RGB1_3.png diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Leather037_2K_Color.png b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Leather037_2K_Color.png similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Leather037_2K_Color.png rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Leather037_2K_Color.png diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Leather037_2K_NormalGL.png b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Leather037_2K_NormalGL.png similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Leather037_2K_NormalGL.png rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Leather037_2K_NormalGL.png diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Leather037_2K_Roughness.png b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Leather037_2K_Roughness.png similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Leather037_2K_Roughness.png rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Leather037_2K_Roughness.png diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Metal009_2K_NormalGL.png b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Metal009_2K_NormalGL.png similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Metal009_2K_NormalGL.png rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Metal009_2K_NormalGL.png diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Metal009_2K_Roughness.png b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Metal009_2K_Roughness.png similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Metal009_2K_Roughness.png rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Metal009_2K_Roughness.png diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Metal029_2K_Displacement.jpg b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Metal029_2K_Displacement.jpg similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Metal029_2K_Displacement.jpg rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Metal029_2K_Displacement.jpg diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Metal029_2K_Displacement.png b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Metal029_2K_Displacement.png similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Metal029_2K_Displacement.png rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Metal029_2K_Displacement.png diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Paint006_2K_AmbientOcclusion.png b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Paint006_2K_AmbientOcclusion.png similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Paint006_2K_AmbientOcclusion.png rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Paint006_2K_AmbientOcclusion.png diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Paint006_2K_NormalGL.png b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Paint006_2K_NormalGL.png similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Paint006_2K_NormalGL.png rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Paint006_2K_NormalGL.png diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Paint006_2K_Roughness.png b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Paint006_2K_Roughness.png similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Paint006_2K_Roughness.png rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Paint006_2K_Roughness.png diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Rock023_2K_AmbientOcclusion.png b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Rock023_2K_AmbientOcclusion.png similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Rock023_2K_AmbientOcclusion.png rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Rock023_2K_AmbientOcclusion.png diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Rock023_2K_Color.png b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Rock023_2K_Color.png similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Rock023_2K_Color.png rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Rock023_2K_Color.png diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Rock023_2K_NormalGL.png b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Rock023_2K_NormalGL.png similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Rock023_2K_NormalGL.png rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Rock023_2K_NormalGL.png diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Rock023_2K_Roughness.png b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Rock023_2K_Roughness.png similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Rock023_2K_Roughness.png rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Rock023_2K_Roughness.png diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Wood048_2K_Color.png b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Wood048_2K_Color.png similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Wood048_2K_Color.png rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Wood048_2K_Color.png diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Wood048_2K_NormalGL.png b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Wood048_2K_NormalGL.png similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Wood048_2K_NormalGL.png rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Wood048_2K_NormalGL.png diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Wood048_2K_Roughness.png b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Wood048_2K_Roughness.png similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Wood048_2K_Roughness.png rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/Wood048_2K_Roughness.png diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/WoodFloor044_2K_Color.png b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/WoodFloor044_2K_Color.png similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/WoodFloor044_2K_Color.png rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/WoodFloor044_2K_Color.png diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/WoodFloor044_2K_NormalGL.png b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/WoodFloor044_2K_NormalGL.png similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/WoodFloor044_2K_NormalGL.png rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/WoodFloor044_2K_NormalGL.png diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/WoodFloor044_2K_Roughness.png b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/WoodFloor044_2K_Roughness.png similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/WoodFloor044_2K_Roughness.png rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/WoodFloor044_2K_Roughness.png diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/WoodFloor054_2K_AmbientOcclusion.png b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/WoodFloor054_2K_AmbientOcclusion.png similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/WoodFloor054_2K_AmbientOcclusion.png rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/WoodFloor054_2K_AmbientOcclusion.png diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/WoodFloor054_2K_Color.png b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/WoodFloor054_2K_Color.png similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/WoodFloor054_2K_Color.png rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/WoodFloor054_2K_Color.png diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/WoodFloor054_2K_NormalGL.png b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/WoodFloor054_2K_NormalGL.png similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/WoodFloor054_2K_NormalGL.png rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/WoodFloor054_2K_NormalGL.png diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/WoodFloor054_2K_Roughness.png b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/WoodFloor054_2K_Roughness.png similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/WoodFloor054_2K_Roughness.png rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/WoodFloor054_2K_Roughness.png diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/blurrynoise.tga b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/blurrynoise.tga similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/blurrynoise.tga rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/blurrynoise.tga diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/noisenormal.png b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/noisenormal.png similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/noisenormal.png rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/images/noisenormal.png diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/qmldir b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/qmldir similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/qmldir rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/qmldir diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/shaders/CMakeLists.txt b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/shaders/CMakeLists.txt similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/shaders/CMakeLists.txt rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/shaders/CMakeLists.txt diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/shaders/SSS.frag b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/shaders/SSS.frag similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/shaders/SSS.frag rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/shaders/SSS.frag diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/shaders/SSS.vert b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/shaders/SSS.vert similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/shaders/SSS.vert rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/shaders/SSS.vert diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/shaders/carmat_simple.frag b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/shaders/carmat_simple.frag similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/shaders/carmat_simple.frag rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/shaders/carmat_simple.frag diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/shaders/carmat_simple.vert b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/shaders/carmat_simple.vert similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/shaders/carmat_simple.vert rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/shaders/carmat_simple.vert diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/shaders/carmat_simple_nf.frag b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/shaders/carmat_simple_nf.frag similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/shaders/carmat_simple_nf.frag rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/shaders/carmat_simple_nf.frag diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/shaders/carmat_simple_nf.vert b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/shaders/carmat_simple_nf.vert similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/shaders/carmat_simple_nf.vert rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/shaders/carmat_simple_nf.vert diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/shaders/glass.frag b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/shaders/glass.frag similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/shaders/glass.frag rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/shaders/glass.frag diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/shaders/glass.vert b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/shaders/glass.vert similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/shaders/glass.vert rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/shaders/glass.vert diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/shaders/satin.frag b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/shaders/satin.frag similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/shaders/satin.frag rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/shaders/satin.frag diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/shaders/satin.vert b/tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/shaders/satin.vert similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/shaders/satin.vert rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/asset_imports/ComponentBundles/MaterialBundle/shaders/satin.vert diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/App.qml b/tests/unit/unittest/qmlprojectmanager/data/file-filters/content/App.qml similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/App.qml rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/content/App.qml diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/CMakeLists.txt b/tests/unit/unittest/qmlprojectmanager/data/file-filters/content/CMakeLists.txt similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/CMakeLists.txt rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/content/CMakeLists.txt diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/CustomRoundButton.qml b/tests/unit/unittest/qmlprojectmanager/data/file-filters/content/CustomRoundButton.qml similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/CustomRoundButton.qml rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/content/CustomRoundButton.qml diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/MaterialNames.qml b/tests/unit/unittest/qmlprojectmanager/data/file-filters/content/MaterialNames.qml similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/MaterialNames.qml rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/content/MaterialNames.qml diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/MouseRotator.qml b/tests/unit/unittest/qmlprojectmanager/data/file-filters/content/MouseRotator.qml similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/MouseRotator.qml rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/content/MouseRotator.qml diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/Screen01.ui.qml b/tests/unit/unittest/qmlprojectmanager/data/file-filters/content/Screen01.ui.qml similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/Screen01.ui.qml rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/content/Screen01.ui.qml diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/fonts/OpenSans-Bold.ttf b/tests/unit/unittest/qmlprojectmanager/data/file-filters/content/fonts/OpenSans-Bold.ttf similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/fonts/OpenSans-Bold.ttf rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/content/fonts/OpenSans-Bold.ttf diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/fonts/OpenSans-Regular.ttf b/tests/unit/unittest/qmlprojectmanager/data/file-filters/content/fonts/OpenSans-Regular.ttf similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/fonts/OpenSans-Regular.ttf rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/content/fonts/OpenSans-Regular.ttf diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/fonts/fonts.txt b/tests/unit/unittest/qmlprojectmanager/data/file-filters/content/fonts/fonts.txt similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/fonts/fonts.txt rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/content/fonts/fonts.txt diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/images/Ground_ShadowMap.png b/tests/unit/unittest/qmlprojectmanager/data/file-filters/content/images/Ground_ShadowMap.png similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/images/Ground_ShadowMap.png rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/content/images/Ground_ShadowMap.png diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/images/HDR/dark_mode.png b/tests/unit/unittest/qmlprojectmanager/data/file-filters/content/images/HDR/dark_mode.png similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/images/HDR/dark_mode.png rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/content/images/HDR/dark_mode.png diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/images/HDR/day_mode.png b/tests/unit/unittest/qmlprojectmanager/data/file-filters/content/images/HDR/day_mode.png similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/images/HDR/day_mode.png rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/content/images/HDR/day_mode.png diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/images/LDR_RGB1_3.png b/tests/unit/unittest/qmlprojectmanager/data/file-filters/content/images/LDR_RGB1_3.png similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/images/LDR_RGB1_3.png rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/content/images/LDR_RGB1_3.png diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/images/QtLogo_HD.png b/tests/unit/unittest/qmlprojectmanager/data/file-filters/content/images/QtLogo_HD.png similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/images/QtLogo_HD.png rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/content/images/QtLogo_HD.png diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/images/UI/innerMesh.png b/tests/unit/unittest/qmlprojectmanager/data/file-filters/content/images/UI/innerMesh.png similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/images/UI/innerMesh.png rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/content/images/UI/innerMesh.png diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/images/UI/lightToggle.png b/tests/unit/unittest/qmlprojectmanager/data/file-filters/content/images/UI/lightToggle.png similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/images/UI/lightToggle.png rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/content/images/UI/lightToggle.png diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/images/UI/outerMesh.png b/tests/unit/unittest/qmlprojectmanager/data/file-filters/content/images/UI/outerMesh.png similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/images/UI/outerMesh.png rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/content/images/UI/outerMesh.png diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/images/UI/perfhudicon.png b/tests/unit/unittest/qmlprojectmanager/data/file-filters/content/images/UI/perfhudicon.png similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/images/UI/perfhudicon.png rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/content/images/UI/perfhudicon.png diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/images/UI/perfhudicon_on.png b/tests/unit/unittest/qmlprojectmanager/data/file-filters/content/images/UI/perfhudicon_on.png similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/images/UI/perfhudicon_on.png rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/content/images/UI/perfhudicon_on.png diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/images/White.png b/tests/unit/unittest/qmlprojectmanager/data/file-filters/content/images/White.png similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/images/White.png rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/content/images/White.png diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/images/checkmark.png b/tests/unit/unittest/qmlprojectmanager/data/file-filters/content/images/checkmark.png similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/images/checkmark.png rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/content/images/checkmark.png diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/images/groundAlpha.png b/tests/unit/unittest/qmlprojectmanager/data/file-filters/content/images/groundAlpha.png similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/images/groundAlpha.png rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/content/images/groundAlpha.png diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/images/qtlogo.png b/tests/unit/unittest/qmlprojectmanager/data/file-filters/content/images/qtlogo.png similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/images/qtlogo.png rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/content/images/qtlogo.png diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/images/scratchmap.png b/tests/unit/unittest/qmlprojectmanager/data/file-filters/content/images/scratchmap.png similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/images/scratchmap.png rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/content/images/scratchmap.png diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/images/shadow.png b/tests/unit/unittest/qmlprojectmanager/data/file-filters/content/images/shadow.png similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/images/shadow.png rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/content/images/shadow.png diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/images/vlkhcah_2K_AO.jpg b/tests/unit/unittest/qmlprojectmanager/data/file-filters/content/images/vlkhcah_2K_AO.jpg similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/images/vlkhcah_2K_AO.jpg rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/content/images/vlkhcah_2K_AO.jpg diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/images/vlkhcah_2K_Albedo.jpg b/tests/unit/unittest/qmlprojectmanager/data/file-filters/content/images/vlkhcah_2K_Albedo.jpg similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/images/vlkhcah_2K_Albedo.jpg rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/content/images/vlkhcah_2K_Albedo.jpg diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/images/vlkhcah_2K_Normal.jpg b/tests/unit/unittest/qmlprojectmanager/data/file-filters/content/images/vlkhcah_2K_Normal.jpg similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/images/vlkhcah_2K_Normal.jpg rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/content/images/vlkhcah_2K_Normal.jpg diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/images/vlkhcah_2K_Roughness.jpg b/tests/unit/unittest/qmlprojectmanager/data/file-filters/content/images/vlkhcah_2K_Roughness.jpg similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/images/vlkhcah_2K_Roughness.jpg rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/content/images/vlkhcah_2K_Roughness.jpg diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/meshes/floor.mesh b/tests/unit/unittest/qmlprojectmanager/data/file-filters/content/meshes/floor.mesh similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/meshes/floor.mesh rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/content/meshes/floor.mesh diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/meshes/materialBall.mesh b/tests/unit/unittest/qmlprojectmanager/data/file-filters/content/meshes/materialBall.mesh similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/content/meshes/materialBall.mesh rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/content/meshes/materialBall.mesh diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/filelist.txt b/tests/unit/unittest/qmlprojectmanager/data/file-filters/filelist.txt similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/filelist.txt rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/filelist.txt diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/imports/CMakeLists.txt b/tests/unit/unittest/qmlprojectmanager/data/file-filters/imports/CMakeLists.txt similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/imports/CMakeLists.txt rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/imports/CMakeLists.txt diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/imports/MaterialLibrary/CMakeLists.txt b/tests/unit/unittest/qmlprojectmanager/data/file-filters/imports/MaterialLibrary/CMakeLists.txt similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/imports/MaterialLibrary/CMakeLists.txt rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/imports/MaterialLibrary/CMakeLists.txt diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/imports/MaterialLibrary/Constants.qml b/tests/unit/unittest/qmlprojectmanager/data/file-filters/imports/MaterialLibrary/Constants.qml similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/imports/MaterialLibrary/Constants.qml rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/imports/MaterialLibrary/Constants.qml diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/imports/MaterialLibrary/DirectoryFontLoader.qml b/tests/unit/unittest/qmlprojectmanager/data/file-filters/imports/MaterialLibrary/DirectoryFontLoader.qml similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/imports/MaterialLibrary/DirectoryFontLoader.qml rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/imports/MaterialLibrary/DirectoryFontLoader.qml diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/imports/MaterialLibrary/EventListModel.qml b/tests/unit/unittest/qmlprojectmanager/data/file-filters/imports/MaterialLibrary/EventListModel.qml similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/imports/MaterialLibrary/EventListModel.qml rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/imports/MaterialLibrary/EventListModel.qml diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/imports/MaterialLibrary/EventListSimulator.qml b/tests/unit/unittest/qmlprojectmanager/data/file-filters/imports/MaterialLibrary/EventListSimulator.qml similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/imports/MaterialLibrary/EventListSimulator.qml rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/imports/MaterialLibrary/EventListSimulator.qml diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/imports/MaterialLibrary/designer/plugin.metainfo b/tests/unit/unittest/qmlprojectmanager/data/file-filters/imports/MaterialLibrary/designer/plugin.metainfo similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/imports/MaterialLibrary/designer/plugin.metainfo rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/imports/MaterialLibrary/designer/plugin.metainfo diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/imports/MaterialLibrary/qmldir b/tests/unit/unittest/qmlprojectmanager/data/file-filters/imports/MaterialLibrary/qmldir similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/imports/MaterialLibrary/qmldir rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/imports/MaterialLibrary/qmldir diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/main.qml b/tests/unit/unittest/qmlprojectmanager/data/file-filters/main.qml similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/main.qml rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/main.qml diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/qmlcomponents b/tests/unit/unittest/qmlprojectmanager/data/file-filters/qmlcomponents similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/qmlcomponents rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/qmlcomponents diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/qmlmodules b/tests/unit/unittest/qmlprojectmanager/data/file-filters/qmlmodules similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/qmlmodules rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/qmlmodules diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/qtquickcontrols2.conf b/tests/unit/unittest/qmlprojectmanager/data/file-filters/qtquickcontrols2.conf similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/qtquickcontrols2.conf rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/qtquickcontrols2.conf diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/share.qrc b/tests/unit/unittest/qmlprojectmanager/data/file-filters/share.qrc similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/share.qrc rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/share.qrc diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/src/app_environment.h b/tests/unit/unittest/qmlprojectmanager/data/file-filters/src/app_environment.h similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/src/app_environment.h rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/src/app_environment.h diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/src/import_qml_plugins.h b/tests/unit/unittest/qmlprojectmanager/data/file-filters/src/import_qml_plugins.h similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/src/import_qml_plugins.h rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/src/import_qml_plugins.h diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/src/main.cpp b/tests/unit/unittest/qmlprojectmanager/data/file-filters/src/main.cpp similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/src/main.cpp rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/src/main.cpp diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/translations.db b/tests/unit/unittest/qmlprojectmanager/data/file-filters/translations.db similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/file-filters/translations.db rename to tests/unit/unittest/qmlprojectmanager/data/file-filters/translations.db diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/getter-setter/testfile-2.qmlproject b/tests/unit/unittest/qmlprojectmanager/data/getter-setter/empty.qmlproject similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/getter-setter/testfile-2.qmlproject rename to tests/unit/unittest/qmlprojectmanager/data/getter-setter/empty.qmlproject diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/getter-setter/testfile-1.qmlproject b/tests/unit/unittest/qmlprojectmanager/data/getter-setter/notEmpty.qmlproject similarity index 100% rename from tests/auto/qml/qmlprojectmanager/projectitem/data/getter-setter/testfile-1.qmlproject rename to tests/unit/unittest/qmlprojectmanager/data/getter-setter/notEmpty.qmlproject diff --git a/tests/unit/unittest/qmlprojectmanager/projectitem-test.cpp b/tests/unit/unittest/qmlprojectmanager/projectitem-test.cpp new file mode 100644 index 00000000000..3bf214fd81f --- /dev/null +++ b/tests/unit/unittest/qmlprojectmanager/projectitem-test.cpp @@ -0,0 +1,539 @@ +// 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 "googletest.h" // IWYU pragma: keep +#include "google-using-declarations.h" + +#include + +#include +namespace { + +constexpr QLatin1String localTestDataDir{UNITTEST_DIR "/qmlprojectmanager/data"}; + +class QmlProjectItem : public testing::Test +{ +protected: + static void SetUpTestSuite() + { + projectItemEmpty = std::make_unique( + Utils::FilePath::fromString(localTestDataDir + "/getter-setter/empty.qmlproject"), true); + + projectItemNotEmpty = std::make_unique( + Utils::FilePath::fromString(localTestDataDir + "/getter-setter/notEmpty.qmlproject"), + true); + + projectItemFileFilters = std::make_unique( + Utils::FilePath::fromString(localTestDataDir + "/file-filters/MaterialBundle.qmlproject"), + true); + } + + static void TearDownTestSuite() + { + projectItemEmpty.reset(); + projectItemNotEmpty.reset(); + projectItemFileFilters.reset(); + } + +protected: + static inline std::unique_ptr projectItemEmpty; + static inline std::unique_ptr projectItemNotEmpty; + std::unique_ptr + projectItemSetters = std::make_unique( + Utils::FilePath::fromString(localTestDataDir + "/getter-setter/empty.qmlproject"), true); + static inline std::unique_ptr projectItemFileFilters; +}; + +auto createAbsoluteFilePaths(const QStringList &fileList) +{ + return Utils::transform(fileList, [](const QString &fileName) { + return Utils::FilePath::fromString(localTestDataDir + "/file-filters").pathAppended(fileName); + }); +} + +TEST_F(QmlProjectItem, GetNotEmptyMainFileProject) +{ + auto mainFile = projectItemNotEmpty->mainFile(); + + ASSERT_THAT(mainFile, Eq("content/App.qml")); +} + +TEST_F(QmlProjectItem, GetNotEmptyMainUIFileProject) +{ + auto mainUiFile = projectItemNotEmpty->mainUiFile(); + + ASSERT_THAT(mainUiFile, Eq("Screen01.ui.qml")); +} + +TEST_F(QmlProjectItem, GetNotEmptyMcuProject) +{ + auto isMcuProject = projectItemNotEmpty->isQt4McuProject(); + + ASSERT_TRUE(isMcuProject); +} + +TEST_F(QmlProjectItem, GetNotEmptyQtVersion) +{ + auto qtVersion = projectItemNotEmpty->versionQt(); + + ASSERT_THAT(qtVersion, Eq("6")); +} + +TEST_F(QmlProjectItem, GetNotEmptyQtQuickVersion) +{ + auto qtQuickVersion = projectItemNotEmpty->versionQtQuick(); + + ASSERT_THAT(qtQuickVersion, Eq("6.2")); +} + +TEST_F(QmlProjectItem, GetNotEmptyDesignStudioVersion) +{ + auto designStudioVersion = projectItemNotEmpty->versionDesignStudio(); + + ASSERT_THAT(designStudioVersion, Eq("3.9")); +} + +TEST_F(QmlProjectItem, GetNotEmptySourceDirectory) +{ + auto sourceDirectory = projectItemNotEmpty->sourceDirectory().path(); + + auto expectedSourceDir = localTestDataDir + "/getter-setter"; + + ASSERT_THAT(sourceDirectory, Eq(expectedSourceDir)); +} + +TEST_F(QmlProjectItem, GetNotEmptyTarGetNotEmptyDirectory) +{ + auto targetDirectory = projectItemNotEmpty->targetDirectory(); + + ASSERT_THAT(targetDirectory, Eq("/opt/targetDirectory")); +} + +TEST_F(QmlProjectItem, GetNotEmptyImportPaths) +{ + auto importPaths = projectItemNotEmpty->importPaths(); + + ASSERT_THAT(importPaths, UnorderedElementsAre("imports", "asset_imports")); +} + +TEST_F(QmlProjectItem, GetNotEmptyFileSelectors) +{ + auto fileSelectors = projectItemNotEmpty->fileSelectors(); + + ASSERT_THAT(fileSelectors, UnorderedElementsAre("WXGA", "darkTheme", "ShowIndicator")); +} + +TEST_F(QmlProjectItem, GetNotEmptyMultiLanguageSupport) +{ + auto multilanguageSupport = projectItemNotEmpty->multilanguageSupport(); + + ASSERT_TRUE(multilanguageSupport); +} + +TEST_F(QmlProjectItem, GetNotEmptySupportedLanguages) +{ + auto supportedLanguages = projectItemNotEmpty->supportedLanguages(); + + ASSERT_THAT(supportedLanguages, UnorderedElementsAre("en", "fr")); +} + +TEST_F(QmlProjectItem, GetNotEmptyPrimaryLanguage) +{ + auto primaryLanguage = projectItemNotEmpty->primaryLanguage(); + ; + + ASSERT_THAT(primaryLanguage, Eq("en")); +} + +TEST_F(QmlProjectItem, GetNotEmptyWidgetApp) +{ + auto widgetApp = projectItemNotEmpty->widgetApp(); + + ASSERT_TRUE(widgetApp); +} + +TEST_F(QmlProjectItem, GetNotEmptyFileList) +{ + QStringList fileList; + for (const auto &file : projectItemNotEmpty->files()) { + fileList.append(file.path()); + } + + auto expectedFileList = localTestDataDir + "/getter-setter/qtquickcontrols2.conf"; + + ASSERT_THAT(fileList, UnorderedElementsAre(expectedFileList)); +} + +TEST_F(QmlProjectItem, GetNotEmptyShaderToolArgs) +{ + auto shaderToolArgs = projectItemNotEmpty->shaderToolArgs(); + + ASSERT_THAT(shaderToolArgs, + UnorderedElementsAre("-s", "--glsl", "\"100 es,120,150\"", "--hlsl", "50", "--msl", "12")); +} + +TEST_F(QmlProjectItem, GetNotEmptyShaderToolFiles) +{ + auto shaderToolFiles = projectItemNotEmpty->shaderToolFiles(); + + ASSERT_THAT(shaderToolFiles, UnorderedElementsAre("content/shaders/*")); +} + +TEST_F(QmlProjectItem, GetNotEmptyEnvironment) +{ + auto env = projectItemNotEmpty->environment(); + + ASSERT_THAT(env, + UnorderedElementsAre( + Utils::EnvironmentItem("QT_QUICK_CONTROLS_CONF", "qtquickcontrols2.conf"))); +} + +TEST_F(QmlProjectItem, GetNotEmptyForceFreeType) +{ + auto forceFreeType = projectItemNotEmpty->forceFreeType(); + + ASSERT_TRUE(forceFreeType); +} + +TEST_F(QmlProjectItem, GetEmptyMainFileProject) +{ + auto mainFile = projectItemEmpty->mainFile(); + + ASSERT_THAT(mainFile, IsEmpty()); +} + +TEST_F(QmlProjectItem, GetEmptyMainUIFileProject) +{ + auto mainUiFile = projectItemEmpty->mainUiFile(); + + ASSERT_THAT(mainUiFile, IsEmpty()); +} + +TEST_F(QmlProjectItem, GetEmptyMcuProject) +{ + auto isMcuProject = projectItemEmpty->isQt4McuProject(); + + ASSERT_FALSE(isMcuProject); +} + +TEST_F(QmlProjectItem, GetEmptyQtVersion) +{ + auto qtVersion = projectItemEmpty->versionQt(); + + // default Qt Version is "5" for Design Studio projects + ASSERT_THAT(qtVersion, Eq("5")); +} + +TEST_F(QmlProjectItem, GetEmptyQtQuickVersion) +{ + auto qtQuickVersion = projectItemEmpty->versionQtQuick(); + + ASSERT_THAT(projectItemEmpty->versionQtQuick(), IsEmpty()); +} + +TEST_F(QmlProjectItem, GetEmptyDesignStudioVersion) +{ + auto designStudioVersion = projectItemEmpty->versionDesignStudio(); + + ASSERT_THAT(projectItemEmpty->versionDesignStudio(), IsEmpty()); +} + +TEST_F(QmlProjectItem, GetEmptySourceDirectory) +{ + auto sourceDirectory = projectItemEmpty->sourceDirectory().path(); + + auto expectedSourceDir = localTestDataDir + "/getter-setter"; + + // default source directory is the project directory + ASSERT_THAT(sourceDirectory, Eq(expectedSourceDir)); +} + +TEST_F(QmlProjectItem, GetEmptyTarGetEmptyDirectory) +{ + auto targetDirectory = projectItemEmpty->targetDirectory(); + + ASSERT_THAT(targetDirectory, IsEmpty()); +} + +TEST_F(QmlProjectItem, GetEmptyImportPaths) +{ + auto importPaths = projectItemEmpty->importPaths(); + + ASSERT_THAT(importPaths, IsEmpty()); +} + +TEST_F(QmlProjectItem, GetEmptyFileSelectors) +{ + auto fileSelectors = projectItemEmpty->fileSelectors(); + + ASSERT_THAT(fileSelectors, IsEmpty()); +} + +TEST_F(QmlProjectItem, GetEmptyMultiLanguageSupport) +{ + auto multilanguageSupport = projectItemEmpty->multilanguageSupport(); + + ASSERT_FALSE(multilanguageSupport); +} + +TEST_F(QmlProjectItem, GetEmptySupportedLanguages) +{ + auto supportedLanguages = projectItemEmpty->supportedLanguages(); + + ASSERT_THAT(supportedLanguages, IsEmpty()); +} + +TEST_F(QmlProjectItem, GetEmptyPrimaryLanguage) +{ + auto primaryLanguage = projectItemEmpty->primaryLanguage(); + + ASSERT_THAT(primaryLanguage, IsEmpty()); +} + +TEST_F(QmlProjectItem, GetEmptyWidgetApp) +{ + auto widgetApp = projectItemEmpty->widgetApp(); + + ASSERT_FALSE(widgetApp); +} + +TEST_F(QmlProjectItem, GetEmptyFileList) +{ + auto fileList = projectItemEmpty->files(); + + ASSERT_THAT(fileList, IsEmpty()); +} + +TEST_F(QmlProjectItem, GetEmptyShaderToolArgs) +{ + auto shaderToolArgs = projectItemEmpty->shaderToolArgs(); + + ASSERT_THAT(shaderToolArgs, IsEmpty()); +} + +TEST_F(QmlProjectItem, GetEmptyShaderToolFiles) +{ + auto shaderToolFiles = projectItemEmpty->shaderToolFiles(); + + ASSERT_THAT(shaderToolFiles, IsEmpty()); +} + +TEST_F(QmlProjectItem, GetEmptyEnvironment) +{ + auto env = projectItemEmpty->environment(); + + ASSERT_THAT(env, IsEmpty()); +} + +TEST_F(QmlProjectItem, GetEmptyForceFreeType) +{ + auto forceFreeType = projectItemEmpty->forceFreeType(); + + ASSERT_FALSE(forceFreeType); +} + +TEST_F(QmlProjectItem, SetMainFileProject) +{ + projectItemSetters->setMainFile("testing"); + + auto mainFile = projectItemSetters->mainFile(); + + ASSERT_THAT(mainFile, Eq("testing")); +} + +TEST_F(QmlProjectItem, SetMainUIFileProject) +{ + projectItemSetters->setMainUiFile("testing"); + + auto mainUiFile = projectItemSetters->mainUiFile(); + + ASSERT_THAT(mainUiFile, Eq("testing")); +} + +TEST_F(QmlProjectItem, SetImportPaths) +{ + projectItemSetters->setImportPaths({"testing"}); + + auto importPaths = projectItemSetters->importPaths(); + + ASSERT_THAT(importPaths, UnorderedElementsAre("testing")); +} + +TEST_F(QmlProjectItem, AddImportPaths) +{ + projectItemSetters->setImportPaths({}); + projectItemSetters->addImportPath("testing"); + + auto importPaths = projectItemSetters->importPaths(); + + ASSERT_THAT(importPaths, UnorderedElementsAre("testing")); +} + +TEST_F(QmlProjectItem, SetFileSelectors) +{ + projectItemSetters->setFileSelectors({"testing"}); + + auto fileSelectors = projectItemSetters->fileSelectors(); + + ASSERT_THAT(fileSelectors, UnorderedElementsAre("testing")); +} + +TEST_F(QmlProjectItem, AddFileSelectors) +{ + projectItemSetters->setFileSelectors({}); + projectItemSetters->addFileSelector("testing"); + + auto fileSelectors = projectItemSetters->fileSelectors(); + + ASSERT_THAT(fileSelectors, UnorderedElementsAre("testing")); +} + +TEST_F(QmlProjectItem, SetMultiLanguageSupport) +{ + projectItemSetters->setMultilanguageSupport(true); + + auto multilanguageSupport = projectItemSetters->multilanguageSupport(); + + ASSERT_TRUE(multilanguageSupport); +} + +TEST_F(QmlProjectItem, SetSupportedLanguages) +{ + projectItemSetters->setSupportedLanguages({"testing"}); + + auto supportedLanguages = projectItemSetters->supportedLanguages(); + + ASSERT_THAT(supportedLanguages, UnorderedElementsAre("testing")); +} + +TEST_F(QmlProjectItem, AddSupportedLanguages) +{ + projectItemSetters->setSupportedLanguages({}); + projectItemSetters->addSupportedLanguage("testing"); + + auto supportedLanguages = projectItemSetters->supportedLanguages(); + + ASSERT_THAT(supportedLanguages, UnorderedElementsAre("testing")); +} + +TEST_F(QmlProjectItem, SetPrimaryLanguage) +{ + projectItemSetters->setPrimaryLanguage("testing"); + + auto primaryLanguage = projectItemSetters->primaryLanguage(); + ; + + ASSERT_THAT(primaryLanguage, Eq("testing")); +} + +TEST_F(QmlProjectItem, SetWidgetApp) +{ + projectItemSetters->setWidgetApp(true); + + auto widgetApp = projectItemSetters->widgetApp(); + + ASSERT_TRUE(widgetApp); +} + +TEST_F(QmlProjectItem, SetShaderToolArgs) +{ + projectItemSetters->setShaderToolArgs({"testing"}); + + auto shaderToolArgs = projectItemSetters->shaderToolArgs(); + + ASSERT_THAT(shaderToolArgs, UnorderedElementsAre("testing")); +} + +TEST_F(QmlProjectItem, AddShaderToolArgs) +{ + projectItemSetters->setShaderToolArgs({}); + projectItemSetters->addShaderToolArg("testing"); + + auto shaderToolArgs = projectItemSetters->shaderToolArgs(); + + ASSERT_THAT(shaderToolArgs, UnorderedElementsAre("testing")); +} + +TEST_F(QmlProjectItem, SetShaderToolFiles) +{ + projectItemSetters->setShaderToolFiles({"testing"}); + + auto shaderToolFiles = projectItemSetters->shaderToolFiles(); + + ASSERT_THAT(shaderToolFiles, UnorderedElementsAre("testing")); +} + +TEST_F(QmlProjectItem, AddShaderToolFiles) +{ + projectItemSetters->setShaderToolFiles({}); + projectItemSetters->addShaderToolFile("testing"); + + auto shaderToolFiles = projectItemSetters->shaderToolFiles(); + + ASSERT_THAT(shaderToolFiles, UnorderedElementsAre("testing")); +} + +TEST_F(QmlProjectItem, AddEnvironment) +{ + projectItemSetters->addToEnviroment("testing", "testing"); + auto envs = projectItemSetters->environment(); + + Utils::EnvironmentItems expectedEnvs; + expectedEnvs.push_back({"testing", "testing"}); + + ASSERT_EQ(envs, expectedEnvs); +} + +TEST_F(QmlProjectItem, SetForceFreeTypeTrue) +{ + projectItemSetters->setForceFreeType(true); + + ASSERT_EQ(projectItemSetters->forceFreeType(), true); +} + +TEST_F(QmlProjectItem, SetForceFreeTypeFalse) +{ + projectItemSetters->setForceFreeType(false); + + ASSERT_EQ(projectItemSetters->forceFreeType(), false); +} + +TEST_F(QmlProjectItem, SetQtVersion) +{ + projectItemSetters->setVersionQt("6"); + + ASSERT_EQ(projectItemSetters->versionQt().toStdString(), "6"); +} + +TEST_F(QmlProjectItem, SetQtQuickVersion) +{ + projectItemSetters->setVersionQtQuick("6"); + + ASSERT_EQ(projectItemSetters->versionQtQuick(), "6"); +} + +TEST_F(QmlProjectItem, SetDesignStudioVersion) +{ + projectItemSetters->setVersionDesignStudio("6"); + + ASSERT_EQ(projectItemSetters->versionDesignStudio(), "6"); +} + +// TODO: We should move this one into the integration tests +TEST_F(QmlProjectItem, TestFileFilters) +{ + // GIVEN + auto fileListPath = Utils::FilePath::fromString(localTestDataDir + "/file-filters/filelist.txt"); + QStringList fileNameList = QString::fromUtf8(fileListPath.fileContents().value()) + .replace("\r\n", "\n") + .split("\n"); + auto expectedAbsoluteFilePaths = createAbsoluteFilePaths(fileNameList); + + // WHEN + auto filePaths = projectItemFileFilters->files(); + + // THEN + ASSERT_THAT(filePaths, UnorderedElementsAreArray(expectedAbsoluteFilePaths)); +} + +} // namespace From 9dbb312d775e6b6339206c1ad5d552d031c09835 Mon Sep 17 00:00:00 2001 From: Brook Cronin Date: Mon, 15 May 2023 11:00:16 +0200 Subject: [PATCH 172/192] QmlDesigner: Align button text with other labels MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: Idd9e91cd6c6726d2fb0c62413f9285968a7db098 Reviewed-by: Tanja Remes Reviewed-by: Henning Gründl --- .../imports/HelperWidgets/ComponentButton.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ComponentButton.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ComponentButton.qml index 6c7b1ceae8b..12c003af638 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ComponentButton.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ComponentButton.qml @@ -27,7 +27,7 @@ Column { AbstractButton { implicitWidth: 180 - buttonIcon: qsTr("Edit Base Component") + buttonIcon: qsTr("Edit Component") iconFont: StudioTheme.Constants.font onClicked: goIntoComponent() From 77b2c82f05955420fd600b68d392bc68156c8982 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Mon, 15 May 2023 14:04:34 +0200 Subject: [PATCH 173/192] Utils: Make CTAD work with std::unexpected Aliases are not working with CTAD before C++ 20. Instead of aliasing some types we simply importing the namespace tl into the namespace Utils. This enables some not aliased things too. Change-Id: Ic61a50bedbbf7253ecb5bb1f6dc0624dcc704aa0 Reviewed-by: Reviewed-by: Marcus Tillmanns Reviewed-by: Qt CI Bot (cherry picked from commit 8b2d7977ca74c09e12d235d16a5c6473e60e9961) Reviewed-by: Marco Bubke --- src/libs/utils/expected.h | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/src/libs/utils/expected.h b/src/libs/utils/expected.h index 943cfe5591f..33231c1246e 100644 --- a/src/libs/utils/expected.h +++ b/src/libs/utils/expected.h @@ -9,24 +9,11 @@ namespace Utils { -template -using expected = tl::expected; +using namespace tl; template using expected_str = tl::expected; -template -using unexpected = tl::unexpected; -using unexpect_t = tl::unexpect_t; - -static constexpr unexpect_t unexpect{}; - -template -constexpr unexpected> make_unexpected(E &&e) -{ - return tl::make_unexpected(e); -} - } // namespace Utils //! If 'expected' has an error the error will be printed and the 'action' will be executed. From 4e2366550c64d491e9ca14f03e1ac27e55bd4c60 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Fri, 12 May 2023 14:40:23 +0200 Subject: [PATCH 174/192] QmlDesigner: Add tooltips to statusbar Task-number: QDS-9851 Change-Id: If71410189422e577e0ea3540fbdef35803268ea4 Reviewed-by: Tim Jenssen Reviewed-by: Pranta Ghosh Dastider --- share/qtcreator/qmldesigner/statusbar/Main.qml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/share/qtcreator/qmldesigner/statusbar/Main.qml b/share/qtcreator/qmldesigner/statusbar/Main.qml index db1a125e10f..16b687abc00 100644 --- a/share/qtcreator/qmldesigner/statusbar/Main.qml +++ b/share/qtcreator/qmldesigner/statusbar/Main.qml @@ -6,6 +6,7 @@ import QtQuick.Controls import StudioControls 1.0 as StudioControls import StudioTheme 1.0 as StudioTheme import "../toolbar" +import HelperWidgets 2.0 import ToolBar 1.0 @@ -35,6 +36,7 @@ Item { buttonIcon: StudioTheme.Constants.settings_medium onClicked: backend.triggerProjectSettings() enabled: backend.isInDesignMode || (backend.isInEditMode && backend.projectOpened) + tooltip: qsTr("Set runtime configuration for the project.") } Text { @@ -45,6 +47,10 @@ Item { horizontalAlignment: Text.AlignRight verticalAlignment: Text.AlignVCenter elide: Text.ElideRight + ToolTipArea { + anchors.fill: parent + tooltip: qsTr("Choose a predefined kit for the runtime configuration of the project.") + } } StudioControls.TopLevelComboBox { @@ -67,6 +73,10 @@ Item { horizontalAlignment: Text.AlignRight verticalAlignment: Text.AlignVCenter elide: Text.ElideRight + ToolTipArea { + anchors.fill: parent + tooltip: qsTr("Choose a style for the Qt Quick Controls of the project.") + } } StudioControls.TopLevelComboBox { From 5b8d79e7a745b6284d8c526dfaa883a984fb3510 Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Fri, 5 May 2023 10:48:23 +0300 Subject: [PATCH 175/192] QmlDesigner: Consider DockWidget size in floating mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Task-number: QDS-9836 Change-Id: Ic7f95b8feb757c021a88110b7d529b1720fe83d0 Reviewed-by: Reviewed-by: Henning Gründl Reviewed-by: Miikka Heikkinen Reviewed-by: Mahmoud Badri --- src/libs/advanceddockingsystem/dockwidget.cpp | 12 +++++++++- .../materialeditor/materialeditorview.cpp | 23 ++++++++++++++++++- 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/src/libs/advanceddockingsystem/dockwidget.cpp b/src/libs/advanceddockingsystem/dockwidget.cpp index b39c58f4c2d..34887e91638 100644 --- a/src/libs/advanceddockingsystem/dockwidget.cpp +++ b/src/libs/advanceddockingsystem/dockwidget.cpp @@ -535,7 +535,17 @@ namespace ADS QSize DockWidget::minimumSizeHint() const { - if (d->m_minimumSizeHintMode == DockWidget::MinimumSizeHintFromDockWidget || !d->m_widget) + if (!d->m_widget) + return QSize(60, 40); + + DockContainerWidget *container = this->dockContainer(); + if (!container || container->isFloating()) { + const QSize sh = d->m_widget->minimumSizeHint(); + const QSize s = d->m_widget->minimumSize(); + return {std::max(s.width(), sh.width()), std::max(s.height(), sh.height())}; + } + + if (d->m_minimumSizeHintMode == DockWidget::MinimumSizeHintFromDockWidget) return QSize(60, 40); else return d->m_widget->minimumSizeHint(); diff --git a/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp b/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp index 9cb4959e132..96e01072904 100644 --- a/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp +++ b/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp @@ -38,6 +38,20 @@ #include #include +namespace { +QSize maxSize(const std::initializer_list &sizeList) +{ + QSize result; + for (const QSize &size : sizeList) { + if (size.width() > result.width()) + result.setWidth(size.width()); + if (size.height() > result.height()) + result.setHeight(size.height()); + } + return result; +} +} + namespace QmlDesigner { MaterialEditorView::MaterialEditorView(ExternalDependenciesInterface &externalDependencies) @@ -64,7 +78,6 @@ MaterialEditorView::MaterialEditorView(ExternalDependenciesInterface &externalDe m_typeUpdateTimer.setInterval(500); connect(&m_typeUpdateTimer, &QTimer::timeout, this, &MaterialEditorView::updatePossibleTypes); - m_stackedWidget->setMinimumWidth(250); QmlDesignerPlugin::trackWidgetFocusTime(m_stackedWidget, Constants::EVENT_MATERIALEDITOR_TIME); MaterialEditorDynamicPropertiesProxyModel::registerDeclarativeType(); @@ -594,6 +607,14 @@ void MaterialEditorView::setupQmlBackend() initPreviewData(); m_stackedWidget->setCurrentWidget(m_qmlBackEnd->widget()); + if (m_qmlBackEnd->widget()) { + m_stackedWidget->setMinimumSize(maxSize({m_qmlBackEnd->widget()->sizeHint(), + m_qmlBackEnd->widget()->initialSize(), + m_qmlBackEnd->widget()->minimumSizeHint(), + m_qmlBackEnd->widget()->minimumSize()})); + } else { + m_stackedWidget->setMinimumSize({400, 300}); + } } void MaterialEditorView::commitVariantValueToModel(const PropertyName &propertyName, const QVariant &value) From c9649513d1b1002aa5c5827bc1a27bfd3c61d588 Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Thu, 4 May 2023 19:31:02 +0300 Subject: [PATCH 176/192] QmlDesigner: Show texture id in the Material Browser Task-number: QDS-9051 Change-Id: I98ba9d9e4dd26d616a0fd3186539ad6aeb23a842 Reviewed-by: Miikka Heikkinen Reviewed-by: Mahmoud Badri --- .../MaterialBrowser.qml | 4 +- .../MaterialBrowserItemName.qml | 68 +++++++++++++++++++ .../materialBrowserQmlSource/MaterialItem.qml | 60 +++------------- .../materialBrowserQmlSource/TextureItem.qml | 51 +++++++++++--- .../materialbrowsertexturesmodel.cpp | 20 ++++++ .../materialbrowsertexturesmodel.h | 2 + 6 files changed, 144 insertions(+), 61 deletions(-) create mode 100644 share/qtcreator/qmldesigner/materialBrowserQmlSource/MaterialBrowserItemName.qml diff --git a/share/qtcreator/qmldesigner/materialBrowserQmlSource/MaterialBrowser.qml b/share/qtcreator/qmldesigner/materialBrowserQmlSource/MaterialBrowser.qml index d6b5c76ff6a..f0031999fa5 100644 --- a/share/qtcreator/qmldesigner/materialBrowserQmlSource/MaterialBrowser.qml +++ b/share/qtcreator/qmldesigner/materialBrowserQmlSource/MaterialBrowser.qml @@ -334,7 +334,7 @@ Item { { // commit rename upon changing selection if (root.currMaterialItem) - root.currMaterialItem.commitRename(); + root.currMaterialItem.forceFinishEditing(); root.currMaterialItem = materialRepeater.itemAt(materialBrowserModel.selectedIndex); @@ -771,7 +771,7 @@ Item { model: materialBrowserTexturesModel delegate: TextureItem { width: root.cellWidth - height: root.cellWidth + height: root.cellHeight onShowContextMenu: { ctxMenuTextures.popupMenu(model) diff --git a/share/qtcreator/qmldesigner/materialBrowserQmlSource/MaterialBrowserItemName.qml b/share/qtcreator/qmldesigner/materialBrowserQmlSource/MaterialBrowserItemName.qml new file mode 100644 index 00000000000..800217bac55 --- /dev/null +++ b/share/qtcreator/qmldesigner/materialBrowserQmlSource/MaterialBrowserItemName.qml @@ -0,0 +1,68 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import QtQuick 2.15 +import QtQuick.Layouts 1.15 +import QtQuickDesignerTheme 1.0 +import HelperWidgets 2.0 +import StudioTheme 1.0 as StudioTheme +import MaterialBrowserBackend + +TextInput { + id: root + + clip: true + readOnly: true + selectByMouse: !root.readOnly + + horizontalAlignment: TextInput.AlignHCenter + + font.pixelSize: StudioTheme.Values.myFontSize + + color: StudioTheme.Values.themeTextColor + selectionColor: StudioTheme.Values.themeTextSelectionColor + selectedTextColor: StudioTheme.Values.themeTextSelectedTextColor + + // allow only alphanumeric characters, underscores, no space at start, and 1 space between words + validator: RegExpValidator { regExp: /^(\w+\s)*\w+$/ } + + signal renamed(string newName) + signal clicked() + + function startRename() + { + root.readOnly = false + root.selectAll() + root.forceActiveFocus() + root.ensureVisible(root.text.length) + mouseArea.enabled = false + } + + function commitRename() + { + if (root.readOnly) + return; + + root.renamed(root.text) + } + + onEditingFinished: root.commitRename() + + onActiveFocusChanged: { + if (!activeFocus) { + root.readOnly = true + mouseArea.enabled = true + ensureVisible(0) + } + } + + Component.onCompleted: ensureVisible(0) + + MouseArea { + id: mouseArea + anchors.fill: parent + + onClicked: root.clicked() + onDoubleClicked: root.startRename() + } +} diff --git a/share/qtcreator/qmldesigner/materialBrowserQmlSource/MaterialItem.qml b/share/qtcreator/qmldesigner/materialBrowserQmlSource/MaterialItem.qml index abf3717e20a..86fec0936da 100644 --- a/share/qtcreator/qmldesigner/materialBrowserQmlSource/MaterialItem.qml +++ b/share/qtcreator/qmldesigner/materialBrowserQmlSource/MaterialItem.qml @@ -19,22 +19,9 @@ Rectangle { img.source = "image://materialBrowser/" + materialInternalId } - function startRename() + function forceFinishEditing() { - matName.readOnly = false - matName.selectAll() - matName.forceActiveFocus() - matName.ensureVisible(matName.text.length) - nameMouseArea.enabled = false - } - - function commitRename() - { - if (matName.readOnly) - return; - - MaterialBrowserBackend.materialBrowserModel.renameMaterial(index, matName.text); - mouseArea.forceActiveFocus() + matName.commitRename() } border.width: MaterialBrowserBackend.materialBrowserModel.selectedIndex === index ? MaterialBrowserBackend.rootView.materialSectionFocused ? 3 : 1 : 0 @@ -106,50 +93,21 @@ Rectangle { e.accepted = true; } - TextInput { + MaterialBrowserItemName { id: matName text: materialName - width: img.width - clip: true anchors.horizontalCenter: parent.horizontalCenter - horizontalAlignment: TextInput.AlignHCenter - font.pixelSize: StudioTheme.Values.myFontSize - - readOnly: true - selectByMouse: !matName.readOnly - - color: StudioTheme.Values.themeTextColor - selectionColor: StudioTheme.Values.themeTextSelectionColor - selectedTextColor: StudioTheme.Values.themeTextSelectedTextColor - - // allow only alphanumeric characters, underscores, no space at start, and 1 space between words - validator: RegExpValidator { regExp: /^(\w+\s)*\w+$/ } - - onEditingFinished: root.commitRename() - - onActiveFocusChanged: { - if (!activeFocus) { - matName.readOnly = true - nameMouseArea.enabled = true - ensureVisible(0) - } + onRenamed: (newName) => { + MaterialBrowserBackend.materialBrowserModel.renameMaterial(index, newName); + mouseArea.forceActiveFocus() } - Component.onCompleted: ensureVisible(0) - - MouseArea { - id: nameMouseArea - - anchors.fill: parent - - onClicked: { - MaterialBrowserBackend.materialBrowserModel.selectMaterial(index) - MaterialBrowserBackend.rootView.focusMaterialSection(true) - } - onDoubleClicked: root.startRename() + onClicked: { + MaterialBrowserBackend.materialBrowserModel.selectMaterial(index) + MaterialBrowserBackend.rootView.focusMaterialSection(true) } } } diff --git a/share/qtcreator/qmldesigner/materialBrowserQmlSource/TextureItem.qml b/share/qtcreator/qmldesigner/materialBrowserQmlSource/TextureItem.qml index 99970bc2661..b0a5e138099 100644 --- a/share/qtcreator/qmldesigner/materialBrowserQmlSource/TextureItem.qml +++ b/share/qtcreator/qmldesigner/materialBrowserQmlSource/TextureItem.qml @@ -23,6 +23,11 @@ Rectangle { signal showContextMenu() + function forceFinishEditing() + { + txtId.commitRename() + } + MouseArea { id: mouseArea @@ -59,13 +64,43 @@ Rectangle { } } - Image { - source: "image://materialBrowserTex/" + textureSource - asynchronous: true - width: root.width - 10 - height: root.height - 10 - anchors.centerIn: parent - smooth: true - fillMode: Image.PreserveAspectFit + Column { + anchors.fill: parent + spacing: 1 + + Item { width: 1; height: 5 } // spacer + Image { + id: img + source: "image://materialBrowserTex/" + textureSource + asynchronous: true + width: root.width - 10 + height: img.width + anchors.horizontalCenter: parent.horizontalCenter + smooth: true + fillMode: Image.PreserveAspectFit + } + + // Eat keys so they are not passed to parent while editing name + Keys.onPressed: (e) => { + e.accepted = true; + } + + MaterialBrowserItemName { + id: txtId + + text: textureId + width: img.width + anchors.horizontalCenter: parent.horizontalCenter + + onRenamed: (newId) => { + MaterialBrowserBackend.materialBrowserTexturesModel.setTextureId(index, newId); + mouseArea.forceActiveFocus() + } + + onClicked: { + MaterialBrowserBackend.materialBrowserTexturesModel.selectTexture(index) + MaterialBrowserBackend.rootView.focusMaterialSection(false) + } + } } } diff --git a/src/plugins/qmldesigner/components/materialbrowser/materialbrowsertexturesmodel.cpp b/src/plugins/qmldesigner/components/materialbrowser/materialbrowsertexturesmodel.cpp index 10b6660db57..4ed241abf98 100644 --- a/src/plugins/qmldesigner/components/materialbrowser/materialbrowsertexturesmodel.cpp +++ b/src/plugins/qmldesigner/components/materialbrowser/materialbrowsertexturesmodel.cpp @@ -51,6 +51,10 @@ QVariant MaterialBrowserTexturesModel::data(const QModelIndex &index, int role) if (role == RoleTexInternalId) return m_textureList.at(index.row()).internalId(); + if (role == RoleTexId) { + return m_textureList.at(index.row()).id(); + } + if (role == RoleTexToolTip) { QString source = data(index, RoleTexSource).toString(); // absolute path if (source.isEmpty()) @@ -88,6 +92,7 @@ QHash MaterialBrowserTexturesModel::roleNames() const static const QHash roles { {RoleTexHasDynamicProps, "hasDynamicProperties"}, {RoleTexInternalId, "textureInternalId"}, + {RoleTexId, "textureId"}, {RoleTexSource, "textureSource"}, {RoleTexToolTip, "textureToolTip"}, {RoleTexVisible, "textureVisible"} @@ -294,6 +299,21 @@ void MaterialBrowserTexturesModel::deleteTexture(int idx) } } +void MaterialBrowserTexturesModel::setTextureId(int idx, const QString &newId) +{ + if (!isValidIndex(idx)) + return; + + ModelNode node = m_textureList[idx]; + if (!node.isValid()) + return; + + if (node.id() != newId) { + node.setIdWithRefactoring(newId); + emit dataChanged(index(idx, 0), index(idx, 0), {RoleTexId}); + } +} + void MaterialBrowserTexturesModel::applyToSelectedMaterial(qint64 internalId) { int idx = m_textureIndexHash.value(internalId); diff --git a/src/plugins/qmldesigner/components/materialbrowser/materialbrowsertexturesmodel.h b/src/plugins/qmldesigner/components/materialbrowser/materialbrowsertexturesmodel.h index 6d66ad13ec5..9cb7c5ac18f 100644 --- a/src/plugins/qmldesigner/components/materialbrowser/materialbrowsertexturesmodel.h +++ b/src/plugins/qmldesigner/components/materialbrowser/materialbrowsertexturesmodel.h @@ -55,6 +55,7 @@ public: Q_INVOKABLE void addNewTexture(); Q_INVOKABLE void duplicateTexture(int idx); Q_INVOKABLE void deleteTexture(int idx); + Q_INVOKABLE void setTextureId(int idx, const QString &newId); Q_INVOKABLE void applyToSelectedMaterial(qint64 internalId); Q_INVOKABLE void applyToSelectedModel(qint64 internalId); Q_INVOKABLE void openTextureEditor(); @@ -91,6 +92,7 @@ private: enum { RoleTexHasDynamicProps = Qt::UserRole + 1, RoleTexInternalId, + RoleTexId, RoleTexSource, RoleTexToolTip, RoleTexVisible From be824148beb545e8447aa530db35a2d03277fd13 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Sun, 21 May 2023 13:23:26 +0200 Subject: [PATCH 177/192] Sqlite: Improve transaction by inversion of control Using a callable makes it possible to omit the commit call. It is now called by the withDeferredTransaction and withImmediateTransaction function. Change-Id: I9b7bfa7e32f269fe8fcba2fe5e1218e73f5846d1 Reviewed-by: Reviewed-by: Vikas Pachdha --- src/libs/sqlite/sqlitereadstatement.h | 44 ++-- src/libs/sqlite/sqlitereadwritestatement.h | 54 ++--- src/libs/sqlite/sqlitetransaction.h | 47 +++- .../imagecache/imagecachestorage.h | 57 ++--- .../projectstorage/projectstorage.h | 206 ++++++++---------- .../unit/unittest/sqlitereadstatementmock.cpp | 1 + tests/unit/unittest/sqlitereadstatementmock.h | 6 +- .../unit/unittest/sqlitetransaction-test.cpp | 124 +++++++++-- 8 files changed, 302 insertions(+), 237 deletions(-) diff --git a/src/libs/sqlite/sqlitereadstatement.h b/src/libs/sqlite/sqlitereadstatement.h index ac764166ce7..3df47efc741 100644 --- a/src/libs/sqlite/sqlitereadstatement.h +++ b/src/libs/sqlite/sqlitereadstatement.h @@ -34,57 +34,39 @@ public: template auto valueWithTransaction(const QueryTypes &...queryValues) { - DeferredTransaction transaction{Base::database()}; - - auto resultValue = Base::template value(queryValues...); - - transaction.commit(); - - return resultValue; + return withDeferredTransaction(Base::database(), [&] { + return Base::template value(queryValues...); + }); } template auto optionalValueWithTransaction(const QueryTypes &...queryValues) { - DeferredTransaction transaction{Base::database()}; - - auto resultValue = Base::template optionalValue(queryValues...); - - transaction.commit(); - - return resultValue; + return withDeferredTransaction(Base::database(), [&] { + return Base::template optionalValue(queryValues...); + }); } template auto valuesWithTransaction(std::size_t reserveSize, const QueryTypes &...queryValues) { - DeferredTransaction transaction{Base::database()}; - - auto resultValues = Base::template values(reserveSize, queryValues...); - - transaction.commit(); - - return resultValues; + return withDeferredTransaction(Base::database(), [&] { + return Base::template values(reserveSize, queryValues...); + }); } template void readCallbackWithTransaction(Callable &&callable, const QueryTypes &...queryValues) { - DeferredTransaction transaction{Base::database()}; - - Base::readCallback(std::forward(callable), queryValues...); - - transaction.commit(); + withDeferredTransaction(Base::database(), [&] { + Base::readCallback(std::forward(callable), queryValues...); + }); } template void readToWithTransaction(Container &container, const QueryTypes &...queryValues) { - DeferredTransaction transaction{Base::database()}; - - Base::readTo(container, queryValues...); - - transaction.commit(); + withDeferredTransaction(Base::database(), [&] { Base::readTo(container, queryValues...); }); } protected: diff --git a/src/libs/sqlite/sqlitereadwritestatement.h b/src/libs/sqlite/sqlitereadwritestatement.h index 69d4865d6c7..08f1aeda04c 100644 --- a/src/libs/sqlite/sqlitereadwritestatement.h +++ b/src/libs/sqlite/sqlitereadwritestatement.h @@ -34,66 +34,48 @@ public: template auto valueWithTransaction(const QueryTypes &...queryValues) { - ImmediateTransaction transaction{Base::database()}; - - auto resultValue = Base::template value(queryValues...); - - transaction.commit(); - - return resultValue; + return withImmediateTransaction(Base::database(), [&] { + return Base::template value(queryValues...); + }); } template auto optionalValueWithTransaction(const QueryTypes &...queryValues) { - ImmediateTransaction transaction{Base::database()}; - - auto resultValue = Base::template optionalValue(queryValues...); - - transaction.commit(); - - return resultValue; + return withImmediateTransaction(Base::database(), [&] { + return Base::template optionalValue(queryValues...); + }); } template auto valuesWithTransaction(std::size_t reserveSize, const QueryTypes &...queryValues) { - ImmediateTransaction transaction{Base::database()}; - - auto resultValues = Base::template values(reserveSize, queryValues...); - - transaction.commit(); - - return resultValues; + return withImmediateTransaction(Base::database(), [&] { + return Base::template values(reserveSize, queryValues...); + }); } template void readCallbackWithTransaction(Callable &&callable, const QueryTypes &...queryValues) { - ImmediateTransaction transaction{Base::database()}; - - Base::readCallback(std::forward(callable), queryValues...); - - transaction.commit(); + withImmediateTransaction(Base::database(), [&] { + Base::readCallback(std::forward(callable), queryValues...); + }); } template void readToWithTransaction(Container &container, const QueryTypes &...queryValues) { - ImmediateTransaction transaction{Base::database()}; - - Base::readTo(container, queryValues...); - - transaction.commit(); + withImmediateTransaction(Base::database(), [&] { + Base::readTo(container, queryValues...); + }); } void executeWithTransaction() { - ImmediateTransaction transaction{Base::database()}; - - Base::execute(); - - transaction.commit(); + withImmediateTransaction(Base::database(), [&] { + Base::execute(); + }); } }; diff --git a/src/libs/sqlite/sqlitetransaction.h b/src/libs/sqlite/sqlitetransaction.h index 45f8eebc337..2cc4a7bf5fe 100644 --- a/src/libs/sqlite/sqlitetransaction.h +++ b/src/libs/sqlite/sqlitetransaction.h @@ -60,7 +60,6 @@ protected: { } - protected: TransactionInterface &m_interface; std::unique_lock m_locker{m_interface}; @@ -183,6 +182,38 @@ public: using Base::Base; }; +template +auto withTransaction(TransactionInterface &transactionInterface, Callable &&callable) + -> std::invoke_result_t +{ + Transaction transaction{transactionInterface}; + + if constexpr (std::is_void_v>) { + callable(); + + transaction.commit(); + } else { + auto results = callable(); + + transaction.commit(); + + return results; + } +} + +template +auto withDeferredTransaction(TransactionInterface &transactionInterface, Callable &&callable) +{ + if constexpr (std::is_void_v>) { + withTransaction>(transactionInterface, + std::forward(callable)); + } else { + return withTransaction>(transactionInterface, + std::forward( + callable)); + } +} + template DeferredTransaction(TransactionInterface &) -> DeferredTransaction; @@ -226,6 +257,20 @@ public: using Base::Base; }; +template +auto withImmediateTransaction(TransactionInterface &transactionInterface, Callable &&callable) +{ + if constexpr (std::is_void_v>) { + withTransaction>(transactionInterface, + std::forward( + callable)); + } else { + return withTransaction>(transactionInterface, + std::forward( + callable)); + } +} + template ImmediateTransaction(TransactionInterface &) -> ImmediateTransaction; diff --git a/src/plugins/qmldesigner/designercore/imagecache/imagecachestorage.h b/src/plugins/qmldesigner/designercore/imagecache/imagecachestorage.h index 6b0df6f1c1e..37131012d69 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/imagecachestorage.h +++ b/src/plugins/qmldesigner/designercore/imagecache/imagecachestorage.h @@ -35,12 +35,8 @@ public: ImageEntry fetchImage(Utils::SmallStringView name, Sqlite::TimeStamp minimumTimeStamp) const override { try { - Sqlite::DeferredTransaction transaction{database}; - - auto optionalBlob = selectImageStatement.template optionalValue( - name, minimumTimeStamp.value); - - transaction.commit(); + auto optionalBlob = selectImageStatement.template optionalValueWithTransaction< + Sqlite::ByteArrayBlob>(name, minimumTimeStamp.value); if (optionalBlob) return {readImage(optionalBlob->byteArray)}; @@ -55,12 +51,8 @@ public: Sqlite::TimeStamp minimumTimeStamp) const override { try { - Sqlite::DeferredTransaction transaction{database}; - - auto optionalBlob = selectMidSizeImageStatement.template optionalValue( - name, minimumTimeStamp.value); - - transaction.commit(); + auto optionalBlob = selectMidSizeImageStatement.template optionalValueWithTransaction< + Sqlite::ByteArrayBlob>(name, minimumTimeStamp.value); if (optionalBlob) return {readImage(optionalBlob->byteArray)}; @@ -75,12 +67,8 @@ public: Sqlite::TimeStamp minimumTimeStamp) const override { try { - Sqlite::DeferredTransaction transaction{database}; - - auto optionalBlob = selectSmallImageStatement.template optionalValue( - name, minimumTimeStamp.value); - - transaction.commit(); + auto optionalBlob = selectSmallImageStatement.template optionalValueWithTransaction< + Sqlite::ByteArrayBlob>(name, minimumTimeStamp.value); if (optionalBlob) return ImageEntry{readImage(optionalBlob->byteArray)}; @@ -95,12 +83,8 @@ public: IconEntry fetchIcon(Utils::SmallStringView name, Sqlite::TimeStamp minimumTimeStamp) const override { try { - Sqlite::DeferredTransaction transaction{database}; - - auto optionalBlob = selectIconStatement.template optionalValue( - name, minimumTimeStamp.value); - - transaction.commit(); + auto optionalBlob = selectIconStatement.template optionalValueWithTransaction< + Sqlite::ByteArrayBlob>(name, minimumTimeStamp.value); if (optionalBlob) return {readIcon(optionalBlob->byteArray)}; @@ -119,19 +103,16 @@ public: const QImage &smallImage) override { try { - Sqlite::ImmediateTransaction transaction{database}; - auto imageBuffer = createBuffer(image); auto midSizeImageBuffer = createBuffer(midSizeImage); auto smallImageBuffer = createBuffer(smallImage); - upsertImageStatement.write(name, - newTimeStamp.value, - createBlobView(imageBuffer.get()), - createBlobView(midSizeImageBuffer.get()), - createBlobView(smallImageBuffer.get())); - - transaction.commit(); - + Sqlite::withImmediateTransaction(database, [&] { + upsertImageStatement.write(name, + newTimeStamp.value, + createBlobView(imageBuffer.get()), + createBlobView(midSizeImageBuffer.get()), + createBlobView(smallImageBuffer.get())); + }); } catch (const Sqlite::StatementIsBusy &) { return storeImage(name, newTimeStamp, image, midSizeImage, smallImage); } @@ -140,12 +121,10 @@ public: void storeIcon(Utils::SmallStringView name, Sqlite::TimeStamp newTimeStamp, const QIcon &icon) override { try { - Sqlite::ImmediateTransaction transaction{database}; - auto iconBuffer = createBuffer(icon); - upsertIconStatement.write(name, newTimeStamp.value, createBlobView(iconBuffer.get())); - - transaction.commit(); + Sqlite::withImmediateTransaction(database, [&] { + upsertIconStatement.write(name, newTimeStamp.value, createBlobView(iconBuffer.get())); + }); } catch (const Sqlite::StatementIsBusy &) { return storeIcon(name, newTimeStamp, icon); diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h index 47b30463f5e..4665159d154 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h @@ -45,63 +45,61 @@ public: void synchronize(Storage::Synchronization::SynchronizationPackage package) override { - Sqlite::ImmediateTransaction transaction{database}; + Sqlite::withImmediateTransaction(database, [&] { + AliasPropertyDeclarations insertedAliasPropertyDeclarations; + AliasPropertyDeclarations updatedAliasPropertyDeclarations; - AliasPropertyDeclarations insertedAliasPropertyDeclarations; - AliasPropertyDeclarations updatedAliasPropertyDeclarations; + AliasPropertyDeclarations relinkableAliasPropertyDeclarations; + PropertyDeclarations relinkablePropertyDeclarations; + Prototypes relinkablePrototypes; + Prototypes relinkableExtensions; + TypeIds deletedTypeIds; - AliasPropertyDeclarations relinkableAliasPropertyDeclarations; - PropertyDeclarations relinkablePropertyDeclarations; - Prototypes relinkablePrototypes; - Prototypes relinkableExtensions; - TypeIds deletedTypeIds; + TypeIds updatedTypeIds; + updatedTypeIds.reserve(package.types.size()); - TypeIds updatedTypeIds; - updatedTypeIds.reserve(package.types.size()); + TypeIds typeIdsToBeDeleted; - TypeIds typeIdsToBeDeleted; + std::sort(package.updatedSourceIds.begin(), package.updatedSourceIds.end()); - std::sort(package.updatedSourceIds.begin(), package.updatedSourceIds.end()); + synchronizeFileStatuses(package.fileStatuses, package.updatedFileStatusSourceIds); + synchronizeImports(package.imports, + package.updatedSourceIds, + package.moduleDependencies, + package.updatedModuleDependencySourceIds, + package.moduleExportedImports, + package.updatedModuleIds); + synchronizeTypes(package.types, + updatedTypeIds, + insertedAliasPropertyDeclarations, + updatedAliasPropertyDeclarations, + relinkableAliasPropertyDeclarations, + relinkablePropertyDeclarations, + relinkablePrototypes, + relinkableExtensions, + package.updatedSourceIds); - synchronizeFileStatuses(package.fileStatuses, package.updatedFileStatusSourceIds); - synchronizeImports(package.imports, - package.updatedSourceIds, - package.moduleDependencies, - package.updatedModuleDependencySourceIds, - package.moduleExportedImports, - package.updatedModuleIds); - synchronizeTypes(package.types, - updatedTypeIds, - insertedAliasPropertyDeclarations, - updatedAliasPropertyDeclarations, - relinkableAliasPropertyDeclarations, - relinkablePropertyDeclarations, - relinkablePrototypes, - relinkableExtensions, - package.updatedSourceIds); + deleteNotUpdatedTypes(updatedTypeIds, + package.updatedSourceIds, + typeIdsToBeDeleted, + relinkableAliasPropertyDeclarations, + relinkablePropertyDeclarations, + relinkablePrototypes, + relinkableExtensions, + deletedTypeIds); - deleteNotUpdatedTypes(updatedTypeIds, - package.updatedSourceIds, - typeIdsToBeDeleted, - relinkableAliasPropertyDeclarations, - relinkablePropertyDeclarations, - relinkablePrototypes, - relinkableExtensions, - deletedTypeIds); + relink(relinkableAliasPropertyDeclarations, + relinkablePropertyDeclarations, + relinkablePrototypes, + relinkableExtensions, + deletedTypeIds); - relink(relinkableAliasPropertyDeclarations, - relinkablePropertyDeclarations, - relinkablePrototypes, - relinkableExtensions, - deletedTypeIds); + linkAliases(insertedAliasPropertyDeclarations, updatedAliasPropertyDeclarations); - linkAliases(insertedAliasPropertyDeclarations, updatedAliasPropertyDeclarations); + synchronizeProjectDatas(package.projectDatas, package.updatedProjectSourceIds); - synchronizeProjectDatas(package.projectDatas, package.updatedProjectSourceIds); - - commonTypeCache_.resetTypeIds(); - - transaction.commit(); + commonTypeCache_.resetTypeIds(); + }); } ModuleId moduleId(Utils::SmallStringView moduleName) const override @@ -304,38 +302,35 @@ public: Storage::Synchronization::Type fetchTypeByTypeId(TypeId typeId) { - Sqlite::DeferredTransaction transaction{database}; + return Sqlite::withDeferredTransaction(database, [&] { + auto type = selectTypeByTypeIdStatement.template value( + typeId); - auto type = selectTypeByTypeIdStatement.template value(typeId); - - type.exportedTypes = fetchExportedTypes(typeId); - type.propertyDeclarations = fetchPropertyDeclarations(type.typeId); - type.functionDeclarations = fetchFunctionDeclarations(type.typeId); - type.signalDeclarations = fetchSignalDeclarations(type.typeId); - type.enumerationDeclarations = fetchEnumerationDeclarations(type.typeId); - - transaction.commit(); - - return type; - } - - Storage::Synchronization::Types fetchTypes() - { - Sqlite::DeferredTransaction transaction{database}; - - auto types = selectTypesStatement.template values(64); - - for (Storage::Synchronization::Type &type : types) { - type.exportedTypes = fetchExportedTypes(type.typeId); + type.exportedTypes = fetchExportedTypes(typeId); type.propertyDeclarations = fetchPropertyDeclarations(type.typeId); type.functionDeclarations = fetchFunctionDeclarations(type.typeId); type.signalDeclarations = fetchSignalDeclarations(type.typeId); type.enumerationDeclarations = fetchEnumerationDeclarations(type.typeId); - } - transaction.commit(); + return type; + }); + } - return types; + Storage::Synchronization::Types fetchTypes() + { + return Sqlite::withDeferredTransaction(database, [&] { + auto types = selectTypesStatement.template values(64); + + for (Storage::Synchronization::Type &type : types) { + type.exportedTypes = fetchExportedTypes(type.typeId); + type.propertyDeclarations = fetchPropertyDeclarations(type.typeId); + type.functionDeclarations = fetchFunctionDeclarations(type.typeId); + type.signalDeclarations = fetchSignalDeclarations(type.typeId); + type.enumerationDeclarations = fetchEnumerationDeclarations(type.typeId); + } + + return types; + }); } bool fetchIsProtype(TypeId type, TypeId prototype) @@ -358,13 +353,9 @@ public: SourceContextId fetchSourceContextId(Utils::SmallStringView sourceContextPath) { try { - Sqlite::DeferredTransaction transaction{database}; - - auto sourceContextId = fetchSourceContextIdUnguarded(sourceContextPath); - - transaction.commit(); - - return sourceContextId; + return Sqlite::withDeferredTransaction(database, [&] { + return fetchSourceContextIdUnguarded(sourceContextPath); + }); } catch (const Sqlite::ConstraintPreventsModification &) { return fetchSourceContextId(sourceContextPath); } @@ -372,18 +363,16 @@ public: Utils::PathString fetchSourceContextPath(SourceContextId sourceContextId) const { - Sqlite::DeferredTransaction transaction{database}; + return Sqlite::withDeferredTransaction(database, [&] { + auto optionalSourceContextPath = selectSourceContextPathFromSourceContextsBySourceContextIdStatement + .template optionalValue( + sourceContextId); - auto optionalSourceContextPath = selectSourceContextPathFromSourceContextsBySourceContextIdStatement - .template optionalValue( - sourceContextId); + if (!optionalSourceContextPath) + throw SourceContextIdDoesNotExists(); - if (!optionalSourceContextPath) - throw SourceContextIdDoesNotExists(); - - transaction.commit(); - - return std::move(*optionalSourceContextPath); + return std::move(*optionalSourceContextPath); + }); } auto fetchAllSourceContexts() const @@ -394,13 +383,9 @@ public: SourceId fetchSourceId(SourceContextId sourceContextId, Utils::SmallStringView sourceName) { - Sqlite::DeferredTransaction transaction{database}; - - auto sourceId = fetchSourceIdUnguarded(sourceContextId, sourceName); - - transaction.commit(); - - return sourceId; + return Sqlite::withDeferredTransaction(database, [&] { + return fetchSourceIdUnguarded(sourceContextId, sourceName); + }); } auto fetchSourceNameAndSourceContextId(SourceId sourceId) const @@ -416,12 +401,10 @@ public: void clearSources() { - Sqlite::ImmediateTransaction transaction{database}; - - deleteAllSourceContextsStatement.execute(); - deleteAllSourcesStatement.execute(); - - transaction.commit(); + Sqlite::withImmediateTransaction(database, [&] { + deleteAllSourceContextsStatement.execute(); + deleteAllSourcesStatement.execute(); + }); } SourceContextId fetchSourceContextId(SourceId sourceId) const @@ -523,24 +506,15 @@ private: ModuleId fetchModuleId(Utils::SmallStringView moduleName) { - Sqlite::DeferredTransaction transaction{database}; - - ModuleId moduleId = fetchModuleIdUnguarded(moduleName); - - transaction.commit(); - - return moduleId; + return Sqlite::withDeferredTransaction(database, + [&] { return fetchModuleIdUnguarded(moduleName); }); } auto fetchModuleName(ModuleId id) { - Sqlite::DeferredTransaction transaction{database}; - - auto moduleName = fetchModuleNameUnguarded(id); - - transaction.commit(); - - return moduleName; + return Sqlite::withDeferredTransaction(database, [&] { + return fetchModuleNameUnguarded(id); + }); } auto fetchAllModules() const diff --git a/tests/unit/unittest/sqlitereadstatementmock.cpp b/tests/unit/unittest/sqlitereadstatementmock.cpp index b90053e9f13..cad99a8bcff 100644 --- a/tests/unit/unittest/sqlitereadstatementmock.cpp +++ b/tests/unit/unittest/sqlitereadstatementmock.cpp @@ -8,6 +8,7 @@ SqliteReadStatementMockBase::SqliteReadStatementMockBase(Utils::SmallStringView sqlStatement, SqliteDatabaseMock &databaseMock) : sqlStatement(sqlStatement) + , databaseMock(databaseMock) { databaseMock.prepare(sqlStatement); } diff --git a/tests/unit/unittest/sqlitereadstatementmock.h b/tests/unit/unittest/sqlitereadstatementmock.h index 9ab3b451220..38d7e41ecfb 100644 --- a/tests/unit/unittest/sqlitereadstatementmock.h +++ b/tests/unit/unittest/sqlitereadstatementmock.h @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -198,7 +199,9 @@ public: template auto optionalValueWithTransaction(const QueryTypes &...queryValues) { - return optionalValue(queryValues...); + return Sqlite::withDeferredTransaction(databaseMock, [&] { + return optionalValue(queryValues...); + }); } template @@ -261,6 +264,7 @@ public: public: Utils::SmallString sqlStatement; + SqliteDatabaseMock &databaseMock; }; template diff --git a/tests/unit/unittest/sqlitetransaction-test.cpp b/tests/unit/unittest/sqlitetransaction-test.cpp index fa502218c77..ca52dd0ae11 100644 --- a/tests/unit/unittest/sqlitetransaction-test.cpp +++ b/tests/unit/unittest/sqlitetransaction-test.cpp @@ -21,8 +21,13 @@ using Sqlite::ImmediateTransaction; class SqliteTransaction : public testing::Test { +protected: + SqliteTransaction() { ON_CALL(callableWithReturnMock, Call()).WillByDefault(Return(212)); } + protected: NiceMock mockTransactionBackend; + NiceMock> callableMock; + NiceMock> callableWithReturnMock; }; TEST_F(SqliteTransaction, DeferredTransactionCommit) @@ -38,19 +43,6 @@ TEST_F(SqliteTransaction, DeferredTransactionCommit) transaction.commit(); } -TEST_F(SqliteTransaction, DeferredTransactionCommitCallsInterface) -{ - InSequence s; - - EXPECT_CALL(mockTransactionBackend, lock()); - EXPECT_CALL(mockTransactionBackend, deferredBegin()); - EXPECT_CALL(mockTransactionBackend, commit()); - EXPECT_CALL(mockTransactionBackend, unlock()); - - DeferredTransaction transaction{mockTransactionBackend}; - transaction.commit(); -} - TEST_F(SqliteTransaction, DeferredTransactionRollBack) { InSequence s; @@ -337,4 +329,110 @@ TEST_F(SqliteTransaction, ImmediateSessionTransactionBeginThrowsAndNotRollback) ASSERT_ANY_THROW(ImmediateSessionTransaction{mockTransactionBackend}); } +TEST_F(SqliteTransaction, WithDeferredTransactionNoReturnCommit) +{ + InSequence s; + + EXPECT_CALL(mockTransactionBackend, lock()); + EXPECT_CALL(mockTransactionBackend, deferredBegin()); + EXPECT_CALL(callableMock, Call()); + EXPECT_CALL(mockTransactionBackend, commit()); + EXPECT_CALL(mockTransactionBackend, unlock()); + + Sqlite::withDeferredTransaction(mockTransactionBackend, callableMock.AsStdFunction()); +} + +TEST_F(SqliteTransaction, WithDeferredTransactionWithReturnCommit) +{ + InSequence s; + + EXPECT_CALL(mockTransactionBackend, lock()); + EXPECT_CALL(mockTransactionBackend, deferredBegin()); + EXPECT_CALL(callableWithReturnMock, Call()); + EXPECT_CALL(mockTransactionBackend, commit()); + EXPECT_CALL(mockTransactionBackend, unlock()); + + Sqlite::withDeferredTransaction(mockTransactionBackend, callableWithReturnMock.AsStdFunction()); +} + +TEST_F(SqliteTransaction, WithDeferredTransactionReturnsValue) +{ + auto callable = callableWithReturnMock.AsStdFunction(); + + auto value = Sqlite::withDeferredTransaction(mockTransactionBackend, + callableWithReturnMock.AsStdFunction()); + + ASSERT_THAT(value, Eq(212)); +} + +TEST_F(SqliteTransaction, WithDeferredTransactionRollsbackForException) +{ + InSequence s; + ON_CALL(callableMock, Call()).WillByDefault(Throw(std::exception{})); + + EXPECT_CALL(mockTransactionBackend, lock()); + EXPECT_CALL(mockTransactionBackend, deferredBegin()); + EXPECT_CALL(callableMock, Call()); + EXPECT_CALL(mockTransactionBackend, rollback()); + EXPECT_CALL(mockTransactionBackend, unlock()); + + try { + Sqlite::withDeferredTransaction(mockTransactionBackend, callableMock.AsStdFunction()); + } catch (...) { + } +} + +TEST_F(SqliteTransaction, WithImmediateTransactionNoReturnCommit) +{ + InSequence s; + + EXPECT_CALL(mockTransactionBackend, lock()); + EXPECT_CALL(mockTransactionBackend, immediateBegin()); + EXPECT_CALL(callableMock, Call()); + EXPECT_CALL(mockTransactionBackend, commit()); + EXPECT_CALL(mockTransactionBackend, unlock()); + + Sqlite::withImmediateTransaction(mockTransactionBackend, callableMock.AsStdFunction()); +} + +TEST_F(SqliteTransaction, WithImmediateTransactionWithReturnCommit) +{ + InSequence s; + + EXPECT_CALL(mockTransactionBackend, lock()); + EXPECT_CALL(mockTransactionBackend, immediateBegin()); + EXPECT_CALL(callableWithReturnMock, Call()); + EXPECT_CALL(mockTransactionBackend, commit()); + EXPECT_CALL(mockTransactionBackend, unlock()); + + Sqlite::withImmediateTransaction(mockTransactionBackend, callableWithReturnMock.AsStdFunction()); +} + +TEST_F(SqliteTransaction, WithImmediateTransactionReturnsValue) +{ + auto callable = callableWithReturnMock.AsStdFunction(); + + auto value = Sqlite::withImmediateTransaction(mockTransactionBackend, + callableWithReturnMock.AsStdFunction()); + + ASSERT_THAT(value, Eq(212)); +} + +TEST_F(SqliteTransaction, WithImmediateTransactionRollsbackForException) +{ + InSequence s; + ON_CALL(callableMock, Call()).WillByDefault(Throw(std::exception{})); + + EXPECT_CALL(mockTransactionBackend, lock()); + EXPECT_CALL(mockTransactionBackend, immediateBegin()); + EXPECT_CALL(callableMock, Call()); + EXPECT_CALL(mockTransactionBackend, rollback()); + EXPECT_CALL(mockTransactionBackend, unlock()); + + try { + Sqlite::withImmediateTransaction(mockTransactionBackend, callableMock.AsStdFunction()); + } catch (...) { + } +} + } // namespace From f68421a53f9040b71c4b17f52e3d14a5425a52bf Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Sat, 20 May 2023 00:42:28 +0200 Subject: [PATCH 178/192] QmlDesigner: Add move contructors Because of adding a default destructor is removing the move construtor we add default move contructor. Change-Id: Ic253e3173956389b11c0aa04204ea27f00c0e9b3 Reviewed-by: Thomas Hartmann Reviewed-by: --- .../qmldesigner/designercore/include/abstractproperty.h | 4 ++++ src/plugins/qmldesigner/designercore/include/modelnode.h | 4 ++++ .../qmldesigner/designercore/include/qmlmodelnodefacade.h | 1 - .../qmldesigner/designercore/model/qmlmodelnodefacade.cpp | 2 -- 4 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/plugins/qmldesigner/designercore/include/abstractproperty.h b/src/plugins/qmldesigner/designercore/include/abstractproperty.h index 7b5c6ccf68d..c22402202d8 100644 --- a/src/plugins/qmldesigner/designercore/include/abstractproperty.h +++ b/src/plugins/qmldesigner/designercore/include/abstractproperty.h @@ -50,6 +50,10 @@ class QMLDESIGNERCORE_EXPORT AbstractProperty public: AbstractProperty() = default; + AbstractProperty(const AbstractProperty &) = default; + AbstractProperty &operator=(const AbstractProperty &) = default; + AbstractProperty(AbstractProperty &&) = default; + AbstractProperty &operator=(AbstractProperty &&) noexcept = default; ~AbstractProperty(); AbstractProperty(const AbstractProperty &property, AbstractView *view); diff --git a/src/plugins/qmldesigner/designercore/include/modelnode.h b/src/plugins/qmldesigner/designercore/include/modelnode.h index a48713ee864..685b900cb0b 100644 --- a/src/plugins/qmldesigner/designercore/include/modelnode.h +++ b/src/plugins/qmldesigner/designercore/include/modelnode.h @@ -80,6 +80,10 @@ public: ModelNode(); ModelNode(const Internal::InternalNodePointer &internalNode, Model *model, const AbstractView *view); ModelNode(const ModelNode &modelNode, AbstractView *view); + ModelNode(const ModelNode &) = default; + ModelNode &operator=(const ModelNode &) = default; + ModelNode(ModelNode &&) = default; + ModelNode &operator=(ModelNode &&) noexcept = default; ~ModelNode(); TypeName type() const; diff --git a/src/plugins/qmldesigner/designercore/include/qmlmodelnodefacade.h b/src/plugins/qmldesigner/designercore/include/qmlmodelnodefacade.h index 9c63fd040a2..e76b5893792 100644 --- a/src/plugins/qmldesigner/designercore/include/qmlmodelnodefacade.h +++ b/src/plugins/qmldesigner/designercore/include/qmlmodelnodefacade.h @@ -20,7 +20,6 @@ public: static bool isValidQmlModelNodeFacade(const ModelNode &modelNode); virtual bool isValid() const; explicit operator bool() const { return isValid(); } - virtual ~QmlModelNodeFacade(); QmlModelNodeFacade() = default; AbstractView *view() const; diff --git a/src/plugins/qmldesigner/designercore/model/qmlmodelnodefacade.cpp b/src/plugins/qmldesigner/designercore/model/qmlmodelnodefacade.cpp index 47699eae468..7e3bc793247 100644 --- a/src/plugins/qmldesigner/designercore/model/qmlmodelnodefacade.cpp +++ b/src/plugins/qmldesigner/designercore/model/qmlmodelnodefacade.cpp @@ -29,8 +29,6 @@ const NodeInstanceView *QmlModelNodeFacade::nodeInstanceView() const return nodeInstanceView(m_modelNode); } -QmlModelNodeFacade::~QmlModelNodeFacade() = default; - bool QmlModelNodeFacade::hasModelNode() const { return m_modelNode.isValid(); From 90ea280b8e6fe3642872979d90455b8e86677854 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Thu, 18 May 2023 12:11:50 +0200 Subject: [PATCH 179/192] Sqlite: Update to 3.42 Change-Id: I7e4587b662d7623d084e020e20fcbb65bbeb54f0 Reviewed-by: Burak Hancerli Reviewed-by: Reviewed-by: Tim Jenssen --- src/libs/3rdparty/sqlite/sqlite3.c | 6963 +++++++++++++++++++--------- src/libs/3rdparty/sqlite/sqlite3.h | 202 +- 2 files changed, 4818 insertions(+), 2347 deletions(-) diff --git a/src/libs/3rdparty/sqlite/sqlite3.c b/src/libs/3rdparty/sqlite/sqlite3.c index 947a1545517..dd3b5c57570 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.41.2. By combining all the individual C code files into this +** version 3.42.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 @@ -123,6 +123,10 @@ #define SQLITE_4_BYTE_ALIGNED_MALLOC #endif /* defined(_MSC_VER) && !defined(_WIN64) */ +#if !defined(HAVE_LOG2) && defined(_MSC_VER) && _MSC_VER<1800 +#define HAVE_LOG2 0 +#endif /* !defined(HAVE_LOG2) && defined(_MSC_VER) && _MSC_VER<1800 */ + #endif /* SQLITE_MSVC_H */ /************** End of msvc.h ************************************************/ @@ -452,9 +456,9 @@ extern "C" { ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ -#define SQLITE_VERSION "3.41.2" -#define SQLITE_VERSION_NUMBER 3041002 -#define SQLITE_SOURCE_ID "2023-03-22 11:56:21 0d1fc92f94cb6b76bffe3ec34d69cffde2924203304e8ffc4155597af0c191da" +#define SQLITE_VERSION "3.42.0" +#define SQLITE_VERSION_NUMBER 3042000 +#define SQLITE_SOURCE_ID "2023-05-16 12:36:15 831d0fb2836b71c9bc51067c49fee4b8f18047814f2ff22d817d25195cf350b0" /* ** CAPI3REF: Run-Time Library Version Numbers @@ -1961,20 +1965,23 @@ SQLITE_API int sqlite3_os_end(void); ** must ensure that no other SQLite interfaces are invoked by other ** threads while sqlite3_config() is running. ** -** The sqlite3_config() interface -** may only be invoked prior to library initialization using -** [sqlite3_initialize()] or after shutdown by [sqlite3_shutdown()]. -** ^If sqlite3_config() is called after [sqlite3_initialize()] and before -** [sqlite3_shutdown()] then it will return SQLITE_MISUSE. -** Note, however, that ^sqlite3_config() can be called as part of the -** implementation of an application-defined [sqlite3_os_init()]. -** ** The first argument to sqlite3_config() is an integer ** [configuration option] that determines ** what property of SQLite is to be configured. Subsequent arguments ** vary depending on the [configuration option] ** in the first argument. ** +** For most configuration options, the sqlite3_config() interface +** may only be invoked prior to library initialization using +** [sqlite3_initialize()] or after shutdown by [sqlite3_shutdown()]. +** The exceptional configuration options that may be invoked at any time +** are called "anytime configuration options". +** ^If sqlite3_config() is called after [sqlite3_initialize()] and before +** [sqlite3_shutdown()] with a first argument that is not an anytime +** configuration option, then the sqlite3_config() call will return SQLITE_MISUSE. +** Note, however, that ^sqlite3_config() can be called as part of the +** implementation of an application-defined [sqlite3_os_init()]. +** ** ^When a configuration option is set, sqlite3_config() returns [SQLITE_OK]. ** ^If the option is unknown or SQLite is unable to set the option ** then this routine returns a non-zero [error code]. @@ -2082,6 +2089,23 @@ struct sqlite3_mem_methods { ** These constants are the available integer configuration options that ** can be passed as the first argument to the [sqlite3_config()] interface. ** +** Most of the configuration options for sqlite3_config() +** will only work if invoked prior to [sqlite3_initialize()] or after +** [sqlite3_shutdown()]. The few exceptions to this rule are called +** "anytime configuration options". +** ^Calling [sqlite3_config()] with a first argument that is not an +** anytime configuration option in between calls to [sqlite3_initialize()] and +** [sqlite3_shutdown()] is a no-op that returns SQLITE_MISUSE. +** +** The set of anytime configuration options can change (by insertions +** and/or deletions) from one release of SQLite to the next. +** As of SQLite version 3.42.0, the complete set of anytime configuration +** options is: +**

+** ** New configuration options may be added in future releases of SQLite. ** Existing configuration options might be discontinued. Applications ** should check the return code from [sqlite3_config()] to make sure that @@ -2428,28 +2452,28 @@ struct sqlite3_mem_methods { ** compile-time option is not set, then the default maximum is 1073741824. ** */ -#define SQLITE_CONFIG_SINGLETHREAD 1 /* nil */ -#define SQLITE_CONFIG_MULTITHREAD 2 /* nil */ -#define SQLITE_CONFIG_SERIALIZED 3 /* nil */ -#define SQLITE_CONFIG_MALLOC 4 /* sqlite3_mem_methods* */ -#define SQLITE_CONFIG_GETMALLOC 5 /* sqlite3_mem_methods* */ -#define SQLITE_CONFIG_SCRATCH 6 /* No longer used */ -#define SQLITE_CONFIG_PAGECACHE 7 /* void*, int sz, int N */ -#define SQLITE_CONFIG_HEAP 8 /* void*, int nByte, int min */ -#define SQLITE_CONFIG_MEMSTATUS 9 /* boolean */ -#define SQLITE_CONFIG_MUTEX 10 /* sqlite3_mutex_methods* */ -#define SQLITE_CONFIG_GETMUTEX 11 /* sqlite3_mutex_methods* */ -/* previously SQLITE_CONFIG_CHUNKALLOC 12 which is now unused. */ -#define SQLITE_CONFIG_LOOKASIDE 13 /* int int */ -#define SQLITE_CONFIG_PCACHE 14 /* no-op */ -#define SQLITE_CONFIG_GETPCACHE 15 /* no-op */ -#define SQLITE_CONFIG_LOG 16 /* xFunc, void* */ -#define SQLITE_CONFIG_URI 17 /* int */ -#define SQLITE_CONFIG_PCACHE2 18 /* sqlite3_pcache_methods2* */ -#define SQLITE_CONFIG_GETPCACHE2 19 /* sqlite3_pcache_methods2* */ +#define SQLITE_CONFIG_SINGLETHREAD 1 /* nil */ +#define SQLITE_CONFIG_MULTITHREAD 2 /* nil */ +#define SQLITE_CONFIG_SERIALIZED 3 /* nil */ +#define SQLITE_CONFIG_MALLOC 4 /* sqlite3_mem_methods* */ +#define SQLITE_CONFIG_GETMALLOC 5 /* sqlite3_mem_methods* */ +#define SQLITE_CONFIG_SCRATCH 6 /* No longer used */ +#define SQLITE_CONFIG_PAGECACHE 7 /* void*, int sz, int N */ +#define SQLITE_CONFIG_HEAP 8 /* void*, int nByte, int min */ +#define SQLITE_CONFIG_MEMSTATUS 9 /* boolean */ +#define SQLITE_CONFIG_MUTEX 10 /* sqlite3_mutex_methods* */ +#define SQLITE_CONFIG_GETMUTEX 11 /* sqlite3_mutex_methods* */ +/* previously SQLITE_CONFIG_CHUNKALLOC 12 which is now unused. */ +#define SQLITE_CONFIG_LOOKASIDE 13 /* int int */ +#define SQLITE_CONFIG_PCACHE 14 /* no-op */ +#define SQLITE_CONFIG_GETPCACHE 15 /* no-op */ +#define SQLITE_CONFIG_LOG 16 /* xFunc, void* */ +#define SQLITE_CONFIG_URI 17 /* int */ +#define SQLITE_CONFIG_PCACHE2 18 /* sqlite3_pcache_methods2* */ +#define SQLITE_CONFIG_GETPCACHE2 19 /* sqlite3_pcache_methods2* */ #define SQLITE_CONFIG_COVERING_INDEX_SCAN 20 /* int */ -#define SQLITE_CONFIG_SQLLOG 21 /* xSqllog, void* */ -#define SQLITE_CONFIG_MMAP_SIZE 22 /* sqlite3_int64, sqlite3_int64 */ +#define SQLITE_CONFIG_SQLLOG 21 /* xSqllog, void* */ +#define SQLITE_CONFIG_MMAP_SIZE 22 /* sqlite3_int64, sqlite3_int64 */ #define SQLITE_CONFIG_WIN32_HEAPSIZE 23 /* int nByte */ #define SQLITE_CONFIG_PCACHE_HDRSZ 24 /* int *psz */ #define SQLITE_CONFIG_PMASZ 25 /* unsigned int szPma */ @@ -2684,7 +2708,7 @@ struct sqlite3_mem_methods { ** ** ** [[SQLITE_DBCONFIG_DQS_DML]] -**
SQLITE_DBCONFIG_DQS_DML +**
SQLITE_DBCONFIG_DQS_DML
**
The SQLITE_DBCONFIG_DQS_DML option activates or deactivates ** the legacy [double-quoted string literal] misfeature for DML statements ** only, that is DELETE, INSERT, SELECT, and UPDATE statements. The @@ -2693,7 +2717,7 @@ struct sqlite3_mem_methods { **
** ** [[SQLITE_DBCONFIG_DQS_DDL]] -**
SQLITE_DBCONFIG_DQS_DDL +**
SQLITE_DBCONFIG_DQS_DDL
**
The SQLITE_DBCONFIG_DQS option activates or deactivates ** the legacy [double-quoted string literal] misfeature for DDL statements, ** such as CREATE TABLE and CREATE INDEX. The @@ -2702,7 +2726,7 @@ struct sqlite3_mem_methods { **
** ** [[SQLITE_DBCONFIG_TRUSTED_SCHEMA]] -**
SQLITE_DBCONFIG_TRUSTED_SCHEMA +**
SQLITE_DBCONFIG_TRUSTED_SCHEMA
**
The SQLITE_DBCONFIG_TRUSTED_SCHEMA option tells SQLite to ** assume that database schemas are untainted by malicious content. ** When the SQLITE_DBCONFIG_TRUSTED_SCHEMA option is disabled, SQLite @@ -2722,7 +2746,7 @@ struct sqlite3_mem_methods { **
** ** [[SQLITE_DBCONFIG_LEGACY_FILE_FORMAT]] -**
SQLITE_DBCONFIG_LEGACY_FILE_FORMAT +**
SQLITE_DBCONFIG_LEGACY_FILE_FORMAT
**
The SQLITE_DBCONFIG_LEGACY_FILE_FORMAT option activates or deactivates ** the legacy file format flag. When activated, this flag causes all newly ** created database file to have a schema format version number (the 4-byte @@ -2731,7 +2755,7 @@ struct sqlite3_mem_methods { ** any SQLite version back to 3.0.0 ([dateof:3.0.0]). Without this setting, ** newly created databases are generally not understandable by SQLite versions ** prior to 3.3.0 ([dateof:3.3.0]). As these words are written, there -** is now scarcely any need to generated database files that are compatible +** is now scarcely any need to generate database files that are compatible ** all the way back to version 3.0.0, and so this setting is of little ** practical use, but is provided so that SQLite can continue to claim the ** ability to generate new database files that are compatible with version @@ -2742,6 +2766,38 @@ struct sqlite3_mem_methods { ** not considered a bug since SQLite versions 3.3.0 and earlier do not support ** either generated columns or decending indexes. **
+** +** [[SQLITE_DBCONFIG_STMT_SCANSTATUS]] +**
SQLITE_DBCONFIG_STMT_SCANSTATUS
+**
The SQLITE_DBCONFIG_STMT_SCANSTATUS option is only useful in +** SQLITE_ENABLE_STMT_SCANSTATUS builds. In this case, it sets or clears +** a flag that enables collection of the sqlite3_stmt_scanstatus_v2() +** statistics. For statistics to be collected, the flag must be set on +** the database handle both when the SQL statement is prepared and when it +** is stepped. The flag is set (collection of statistics is enabled) +** by default. This option takes two arguments: an integer and a pointer to +** an integer.. The first argument is 1, 0, or -1 to enable, disable, or +** leave unchanged the statement scanstatus option. If the second argument +** is not NULL, then the value of the statement scanstatus setting after +** processing the first argument is written into the integer that the second +** argument points to. +**
+** +** [[SQLITE_DBCONFIG_REVERSE_SCANORDER]] +**
SQLITE_DBCONFIG_REVERSE_SCANORDER
+**
The SQLITE_DBCONFIG_REVERSE_SCANORDER option changes the default order +** in which tables and indexes are scanned so that the scans start at the end +** and work toward the beginning rather than starting at the beginning and +** working toward the end. Setting SQLITE_DBCONFIG_REVERSE_SCANORDER is the +** same as setting [PRAGMA reverse_unordered_selects]. This option takes +** two arguments which are an integer and a pointer to an integer. The first +** argument is 1, 0, or -1 to enable, disable, or leave unchanged the +** reverse scan order flag, respectively. If the second argument is not NULL, +** then 0 or 1 is written into the integer that the second argument points to +** depending on if the reverse scan order flag is set after processing the +** first argument. +**
+** ** */ #define SQLITE_DBCONFIG_MAINDBNAME 1000 /* const char* */ @@ -2762,7 +2818,9 @@ struct sqlite3_mem_methods { #define SQLITE_DBCONFIG_ENABLE_VIEW 1015 /* int int* */ #define SQLITE_DBCONFIG_LEGACY_FILE_FORMAT 1016 /* int int* */ #define SQLITE_DBCONFIG_TRUSTED_SCHEMA 1017 /* int int* */ -#define SQLITE_DBCONFIG_MAX 1017 /* Largest DBCONFIG */ +#define SQLITE_DBCONFIG_STMT_SCANSTATUS 1018 /* int int* */ +#define SQLITE_DBCONFIG_REVERSE_SCANORDER 1019 /* int int* */ +#define SQLITE_DBCONFIG_MAX 1019 /* Largest DBCONFIG */ /* ** CAPI3REF: Enable Or Disable Extended Result Codes @@ -6507,6 +6565,13 @@ SQLITE_API void sqlite3_activate_cerod( ** of the default VFS is not implemented correctly, or not implemented at ** all, then the behavior of sqlite3_sleep() may deviate from the description ** in the previous paragraphs. +** +** If a negative argument is passed to sqlite3_sleep() the results vary by +** VFS and operating system. Some system treat a negative argument as an +** instruction to sleep forever. Others understand it to mean do not sleep +** at all. ^In SQLite version 3.42.0 and later, a negative +** argument passed into sqlite3_sleep() is changed to zero before it is relayed +** down into the xSleep method of the VFS. */ SQLITE_API int sqlite3_sleep(int); @@ -8134,9 +8199,9 @@ SQLITE_API int sqlite3_vfs_unregister(sqlite3_vfs*); ** is undefined if the mutex is not currently entered by the ** calling thread or is not currently allocated. ** -** ^If the argument to sqlite3_mutex_enter(), sqlite3_mutex_try(), or -** sqlite3_mutex_leave() is a NULL pointer, then all three routines -** behave as no-ops. +** ^If the argument to sqlite3_mutex_enter(), sqlite3_mutex_try(), +** sqlite3_mutex_leave(), or sqlite3_mutex_free() is a NULL pointer, +** then any of the four routines behaves as a no-op. ** ** See also: [sqlite3_mutex_held()] and [sqlite3_mutex_notheld()]. */ @@ -9870,18 +9935,28 @@ SQLITE_API int sqlite3_vtab_config(sqlite3*, int op, ...); ** [[SQLITE_VTAB_INNOCUOUS]]
SQLITE_VTAB_INNOCUOUS
**
Calls of the form ** [sqlite3_vtab_config](db,SQLITE_VTAB_INNOCUOUS) from within the -** the [xConnect] or [xCreate] methods of a [virtual table] implmentation +** the [xConnect] or [xCreate] methods of a [virtual table] implementation ** identify that virtual table as being safe to use from within triggers ** and views. Conceptually, the SQLITE_VTAB_INNOCUOUS tag means that the ** virtual table can do no serious harm even if it is controlled by a ** malicious hacker. Developers should avoid setting the SQLITE_VTAB_INNOCUOUS ** flag unless absolutely necessary. **
+** +** [[SQLITE_VTAB_USES_ALL_SCHEMAS]]
SQLITE_VTAB_USES_ALL_SCHEMAS
+**
Calls of the form +** [sqlite3_vtab_config](db,SQLITE_VTAB_USES_ALL_SCHEMA) from within the +** the [xConnect] or [xCreate] methods of a [virtual table] implementation +** instruct the query planner to begin at least a read transaction on +** all schemas ("main", "temp", and any ATTACH-ed databases) whenever the +** virtual table is used. +**
** */ #define SQLITE_VTAB_CONSTRAINT_SUPPORT 1 #define SQLITE_VTAB_INNOCUOUS 2 #define SQLITE_VTAB_DIRECTONLY 3 +#define SQLITE_VTAB_USES_ALL_SCHEMAS 4 /* ** CAPI3REF: Determine The Virtual Table Conflict Policy @@ -11056,16 +11131,20 @@ SQLITE_API int sqlite3session_create( SQLITE_API void sqlite3session_delete(sqlite3_session *pSession); /* -** CAPIREF: Conigure a Session Object +** CAPI3REF: Configure a Session Object ** METHOD: sqlite3_session ** ** This method is used to configure a session object after it has been -** created. At present the only valid value for the second parameter is -** [SQLITE_SESSION_OBJCONFIG_SIZE]. +** created. At present the only valid values for the second parameter are +** [SQLITE_SESSION_OBJCONFIG_SIZE] and [SQLITE_SESSION_OBJCONFIG_ROWID]. ** -** Arguments for sqlite3session_object_config() +*/ +SQLITE_API int sqlite3session_object_config(sqlite3_session*, int op, void *pArg); + +/* +** CAPI3REF: Options for sqlite3session_object_config ** -** The following values may passed as the the 4th parameter to +** The following values may passed as the the 2nd parameter to ** sqlite3session_object_config(). ** **
SQLITE_SESSION_OBJCONFIG_SIZE
@@ -11081,12 +11160,21 @@ SQLITE_API void sqlite3session_delete(sqlite3_session *pSession); ** ** It is an error (SQLITE_MISUSE) to attempt to modify this setting after ** the first table has been attached to the session object. +** +**
SQLITE_SESSION_OBJCONFIG_ROWID
+** This option is used to set, clear or query the flag that enables +** collection of data for tables with no explicit PRIMARY KEY. +** +** Normally, tables with no explicit PRIMARY KEY are simply ignored +** by the sessions module. However, if this flag is set, it behaves +** as if such tables have a column "_rowid_ INTEGER PRIMARY KEY" inserted +** as their leftmost columns. +** +** It is an error (SQLITE_MISUSE) to attempt to modify this setting after +** the first table has been attached to the session object. */ -SQLITE_API int sqlite3session_object_config(sqlite3_session*, int op, void *pArg); - -/* -*/ -#define SQLITE_SESSION_OBJCONFIG_SIZE 1 +#define SQLITE_SESSION_OBJCONFIG_SIZE 1 +#define SQLITE_SESSION_OBJCONFIG_ROWID 2 /* ** CAPI3REF: Enable Or Disable A Session Object @@ -12219,9 +12307,23 @@ SQLITE_API int sqlite3changeset_apply_v2( ** Invert the changeset before applying it. This is equivalent to inverting ** a changeset using sqlite3changeset_invert() before applying it. It is ** an error to specify this flag with a patchset. +** +**
SQLITE_CHANGESETAPPLY_IGNORENOOP
+** Do not invoke the conflict handler callback for any changes that +** would not actually modify the database even if they were applied. +** Specifically, this means that the conflict handler is not invoked +** for: +**
    +**
  • a delete change if the row being deleted cannot be found, +**
  • an update change if the modified fields are already set to +** their new values in the conflicting row, or +**
  • an insert change if all fields of the conflicting row match +** the row being inserted. +**
*/ #define SQLITE_CHANGESETAPPLY_NOSAVEPOINT 0x0001 #define SQLITE_CHANGESETAPPLY_INVERT 0x0002 +#define SQLITE_CHANGESETAPPLY_IGNORENOOP 0x0004 /* ** CAPI3REF: Constants Passed To The Conflict Handler @@ -13518,8 +13620,8 @@ struct fts5_api { #endif /* -** WAL mode depends on atomic aligned 32-bit loads and stores in a few -** places. The following macros try to make this explicit. +** A few places in the code require atomic load/store of aligned +** integer values. */ #ifndef __has_extension # define __has_extension(x) 0 /* compatibility with non-clang compilers */ @@ -13575,15 +13677,22 @@ struct fts5_api { #endif /* -** A macro to hint to the compiler that a function should not be +** Macros to hint to the compiler that a function should or should not be ** inlined. */ #if defined(__GNUC__) # define SQLITE_NOINLINE __attribute__((noinline)) +# define SQLITE_INLINE __attribute__((always_inline)) inline #elif defined(_MSC_VER) && _MSC_VER>=1310 # define SQLITE_NOINLINE __declspec(noinline) +# define SQLITE_INLINE __forceinline #else # define SQLITE_NOINLINE +# define SQLITE_INLINE +#endif +#if defined(SQLITE_COVERAGE_TEST) || defined(__STRICT_ANSI__) +# undef SQLITE_INLINE +# define SQLITE_INLINE #endif /* @@ -16544,6 +16653,10 @@ SQLITE_PRIVATE void sqlite3VdbeScanStatusCounters(Vdbe*, int, int, int); SQLITE_PRIVATE void sqlite3VdbePrintOp(FILE*, int, VdbeOp*); #endif +#if defined(SQLITE_ENABLE_CURSOR_HINTS) && defined(SQLITE_DEBUG) +SQLITE_PRIVATE int sqlite3CursorRangeHintExprCheck(Walker *pWalker, Expr *pExpr); +#endif + #endif /* SQLITE_VDBE_H */ /************** End of vdbe.h ************************************************/ @@ -17253,7 +17366,7 @@ struct sqlite3 { #define SQLITE_NullCallback 0x00000100 /* Invoke the callback once if the */ /* result set is empty */ #define SQLITE_IgnoreChecks 0x00000200 /* Do not enforce check constraints */ -#define SQLITE_ReadUncommit 0x00000400 /* READ UNCOMMITTED in shared-cache */ +#define SQLITE_StmtScanStatus 0x00000400 /* Enable stmt_scanstats() counters */ #define SQLITE_NoCkptOnClose 0x00000800 /* No checkpoint on close()/DETACH */ #define SQLITE_ReverseOrder 0x00001000 /* Reverse unordered SELECTs */ #define SQLITE_RecTriggers 0x00002000 /* Enable recursive triggers */ @@ -17279,6 +17392,7 @@ struct sqlite3 { /* DELETE, or UPDATE and return */ /* the count using a callback. */ #define SQLITE_CorruptRdOnly HI(0x00002) /* Prohibit writes due to error */ +#define SQLITE_ReadUncommit HI(0x00004) /* READ UNCOMMITTED in shared-cache */ /* Flags used only if debugging */ #ifdef SQLITE_DEBUG @@ -17335,6 +17449,7 @@ struct sqlite3 { /* TH3 expects this value ^^^^^^^^^^ See flatten04.test */ #define SQLITE_IndexedExpr 0x01000000 /* Pull exprs from index when able */ #define SQLITE_Coroutines 0x02000000 /* Co-routines for subqueries */ +#define SQLITE_NullUnusedCols 0x04000000 /* NULL unused columns in subqueries */ #define SQLITE_AllOpts 0xffffffff /* All optimizations */ /* @@ -17806,6 +17921,7 @@ struct VTable { sqlite3_vtab *pVtab; /* Pointer to vtab instance */ int nRef; /* Number of pointers to this structure */ u8 bConstraint; /* True if constraints are supported */ + u8 bAllSchemas; /* True if might use any attached schema */ u8 eVtabRisk; /* Riskiness of allowing hacker access */ int iSavepoint; /* Depth of the SAVEPOINT stack */ VTable *pNext; /* Next in linked list (see above) */ @@ -18186,6 +18302,7 @@ struct Index { ** expression, or a reference to a VIRTUAL column */ #ifdef SQLITE_ENABLE_STAT4 int nSample; /* Number of elements in aSample[] */ + int mxSample; /* Number of slots allocated to aSample[] */ int nSampleCol; /* Size of IndexSample.anEq[] and so on */ tRowcnt *aAvgEq; /* Average nEq values for keys not in aSample */ IndexSample *aSample; /* Samples of the left-most key */ @@ -19672,6 +19789,7 @@ struct Walker { struct CoveringIndexCheck *pCovIdxCk; /* Check for covering index */ SrcItem *pSrcItem; /* A single FROM clause item */ DbFixer *pFix; /* See sqlite3FixSelect() */ + Mem *aMem; /* See sqlite3BtreeCursorHint() */ } u; }; @@ -19941,6 +20059,8 @@ SQLITE_PRIVATE int sqlite3CorruptPgnoError(int,Pgno); # define sqlite3Isxdigit(x) (sqlite3CtypeMap[(unsigned char)(x)]&0x08) # define sqlite3Tolower(x) (sqlite3UpperToLower[(unsigned char)(x)]) # define sqlite3Isquote(x) (sqlite3CtypeMap[(unsigned char)(x)]&0x80) +# define sqlite3JsonId1(x) (sqlite3CtypeMap[(unsigned char)(x)]&0x42) +# define sqlite3JsonId2(x) (sqlite3CtypeMap[(unsigned char)(x)]&0x46) #else # define sqlite3Toupper(x) toupper((unsigned char)(x)) # define sqlite3Isspace(x) isspace((unsigned char)(x)) @@ -19950,6 +20070,8 @@ SQLITE_PRIVATE int sqlite3CorruptPgnoError(int,Pgno); # define sqlite3Isxdigit(x) isxdigit((unsigned char)(x)) # define sqlite3Tolower(x) tolower((unsigned char)(x)) # define sqlite3Isquote(x) ((x)=='"'||(x)=='\''||(x)=='['||(x)=='`') +# define sqlite3JsonId1(x) (sqlite3IsIdChar(x)&&(x)<'0') +# define sqlite3JsonId2(x) sqlite3IsIdChar(x) #endif SQLITE_PRIVATE int sqlite3IsIdChar(u8); @@ -20143,6 +20265,10 @@ SQLITE_PRIVATE void sqlite3ReleaseTempReg(Parse*,int); SQLITE_PRIVATE int sqlite3GetTempRange(Parse*,int); SQLITE_PRIVATE void sqlite3ReleaseTempRange(Parse*,int,int); SQLITE_PRIVATE void sqlite3ClearTempRegCache(Parse*); +SQLITE_PRIVATE void sqlite3TouchRegister(Parse*,int); +#if defined(SQLITE_ENABLE_STAT4) || defined(SQLITE_DEBUG) +SQLITE_PRIVATE int sqlite3FirstAvailableRegister(Parse*,int); +#endif #ifdef SQLITE_DEBUG SQLITE_PRIVATE int sqlite3NoTempsInRange(Parse*,int,int); #endif @@ -20293,7 +20419,7 @@ SQLITE_PRIVATE Select *sqlite3SelectNew(Parse*,ExprList*,SrcList*,Expr*,ExprList Expr*,ExprList*,u32,Expr*); SQLITE_PRIVATE void sqlite3SelectDelete(sqlite3*, Select*); SQLITE_PRIVATE Table *sqlite3SrcListLookup(Parse*, SrcList*); -SQLITE_PRIVATE int sqlite3IsReadOnly(Parse*, Table*, int); +SQLITE_PRIVATE int sqlite3IsReadOnly(Parse*, Table*, Trigger*); SQLITE_PRIVATE void sqlite3OpenTable(Parse*, int iCur, int iDb, Table*, int); #if defined(SQLITE_ENABLE_UPDATE_DELETE_LIMIT) && !defined(SQLITE_OMIT_SUBQUERY) SQLITE_PRIVATE Expr *sqlite3LimitWhere(Parse*,SrcList*,Expr*,ExprList*,Expr*,char*); @@ -20382,7 +20508,7 @@ SQLITE_PRIVATE int sqlite3ExprIsConstantNotJoin(Expr*); SQLITE_PRIVATE int sqlite3ExprIsConstantOrFunction(Expr*, u8); SQLITE_PRIVATE int sqlite3ExprIsConstantOrGroupBy(Parse*, Expr*, ExprList*); SQLITE_PRIVATE int sqlite3ExprIsTableConstant(Expr*,int); -SQLITE_PRIVATE int sqlite3ExprIsTableConstraint(Expr*,const SrcItem*); +SQLITE_PRIVATE int sqlite3ExprIsSingleTableConstraint(Expr*,const SrcList*,int); #ifdef SQLITE_ENABLE_CURSOR_HINTS SQLITE_PRIVATE int sqlite3ExprContainsSubquery(Expr*); #endif @@ -20830,10 +20956,7 @@ SQLITE_PRIVATE int sqlite3VtabCallDestroy(sqlite3*, int, const char *); SQLITE_PRIVATE int sqlite3VtabBegin(sqlite3 *, VTable *); SQLITE_PRIVATE FuncDef *sqlite3VtabOverloadFunction(sqlite3 *,FuncDef*, int nArg, Expr*); -#if (defined(SQLITE_ENABLE_DBPAGE_VTAB) || defined(SQLITE_TEST)) \ - && !defined(SQLITE_OMIT_VIRTUALTABLE) -SQLITE_PRIVATE void sqlite3VtabUsesAllSchemas(sqlite3_index_info*); -#endif +SQLITE_PRIVATE void sqlite3VtabUsesAllSchemas(Parse*); SQLITE_PRIVATE sqlite3_int64 sqlite3StmtCurrentTime(sqlite3_context*); SQLITE_PRIVATE int sqlite3VdbeParameterIndex(Vdbe*, const char*, int); SQLITE_PRIVATE int sqlite3TransferBindings(sqlite3_stmt *, sqlite3_stmt *); @@ -21080,6 +21203,12 @@ SQLITE_PRIVATE int sqlite3KvvfsInit(void); SQLITE_PRIVATE sqlite3_uint64 sqlite3Hwtime(void); #endif +#ifdef SQLITE_ENABLE_STMT_SCANSTATUS +# define IS_STMT_SCANSTATUS(db) (db->flags & SQLITE_StmtScanStatus) +#else +# define IS_STMT_SCANSTATUS(db) 0 +#endif + #endif /* SQLITEINT_H */ /************** End of sqliteInt.h *******************************************/ @@ -22075,7 +22204,7 @@ SQLITE_PRIVATE const unsigned char *sqlite3aGTb = &sqlite3UpperToLower[256+12-OP ** isalnum() 0x06 ** isxdigit() 0x08 ** toupper() 0x20 -** SQLite identifier character 0x40 +** SQLite identifier character 0x40 $, _, or non-ascii ** Quote character 0x80 ** ** Bit 0x20 is set if the mapped character requires translation to upper @@ -22269,7 +22398,7 @@ SQLITE_PRIVATE SQLITE_WSD struct Sqlite3Config sqlite3Config = { SQLITE_DEFAULT_SORTERREF_SIZE, /* szSorterRef */ 0, /* iPrngSeed */ #ifdef SQLITE_DEBUG - {0,0,0,0,0,0} /* aTune */ + {0,0,0,0,0,0}, /* aTune */ #endif }; @@ -23568,6 +23697,7 @@ struct DateTime { 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 */ }; @@ -23882,6 +24012,11 @@ static int parseDateOrTime( }else if( sqlite3AtoF(zDate, &r, sqlite3Strlen30(zDate), SQLITE_UTF8)>0 ){ setRawDateNumber(p, r); return 0; + }else if( (sqlite3StrICmp(zDate,"subsec")==0 + || sqlite3StrICmp(zDate,"subsecond")==0) + && sqlite3NotPureFunc(context) ){ + p->useSubsec = 1; + return setDateTimeToCurrent(context, p); } return 1; } @@ -24296,8 +24431,22 @@ static int parseModifier( ** ** Move the date backwards to the beginning of the current day, ** or month or year. + ** + ** subsecond + ** subsec + ** + ** Show subsecond precision in the output of datetime() and + ** unixepoch() and strftime('%s'). */ - if( sqlite3_strnicmp(z, "start of ", 9)!=0 ) break; + if( sqlite3_strnicmp(z, "start of ", 9)!=0 ){ + if( sqlite3_stricmp(z, "subsec")==0 + || sqlite3_stricmp(z, "subsecond")==0 + ){ + p->useSubsec = 1; + rc = 0; + } + break; + } if( !p->validJD && !p->validYMD && !p->validHMS ) break; z += 9; computeYMD(p); @@ -24495,7 +24644,11 @@ static void unixepochFunc( DateTime x; if( isDate(context, argc, argv, &x)==0 ){ computeJD(&x); - sqlite3_result_int64(context, x.iJD/1000 - 21086676*(i64)10000); + if( x.useSubsec ){ + sqlite3_result_double(context, (x.iJD - 21086676*(i64)10000000)/1000.0); + }else{ + sqlite3_result_int64(context, x.iJD/1000 - 21086676*(i64)10000); + } } } @@ -24511,8 +24664,8 @@ static void datetimeFunc( ){ DateTime x; if( isDate(context, argc, argv, &x)==0 ){ - int Y, s; - char zBuf[24]; + int Y, s, n; + char zBuf[32]; computeYMD_HMS(&x); Y = x.Y; if( Y<0 ) Y = -Y; @@ -24533,15 +24686,28 @@ static void datetimeFunc( zBuf[15] = '0' + (x.m/10)%10; zBuf[16] = '0' + (x.m)%10; zBuf[17] = ':'; - s = (int)x.s; - zBuf[18] = '0' + (s/10)%10; - zBuf[19] = '0' + (s)%10; - zBuf[20] = 0; + if( x.useSubsec ){ + s = (int)1000.0*x.s; + zBuf[18] = '0' + (s/10000)%10; + zBuf[19] = '0' + (s/1000)%10; + zBuf[20] = '.'; + zBuf[21] = '0' + (s/100)%10; + zBuf[22] = '0' + (s/10)%10; + zBuf[23] = '0' + (s)%10; + zBuf[24] = 0; + n = 24; + }else{ + s = (int)x.s; + zBuf[18] = '0' + (s/10)%10; + zBuf[19] = '0' + (s)%10; + zBuf[20] = 0; + n = 20; + } if( x.Y<0 ){ zBuf[0] = '-'; - sqlite3_result_text(context, zBuf, 20, SQLITE_TRANSIENT); + sqlite3_result_text(context, zBuf, n, SQLITE_TRANSIENT); }else{ - sqlite3_result_text(context, &zBuf[1], 19, SQLITE_TRANSIENT); + sqlite3_result_text(context, &zBuf[1], n-1, SQLITE_TRANSIENT); } } } @@ -24558,7 +24724,7 @@ static void timeFunc( ){ DateTime x; if( isDate(context, argc, argv, &x)==0 ){ - int s; + int s, n; char zBuf[16]; computeHMS(&x); zBuf[0] = '0' + (x.h/10)%10; @@ -24567,11 +24733,24 @@ static void timeFunc( zBuf[3] = '0' + (x.m/10)%10; zBuf[4] = '0' + (x.m)%10; zBuf[5] = ':'; - s = (int)x.s; - zBuf[6] = '0' + (s/10)%10; - zBuf[7] = '0' + (s)%10; - zBuf[8] = 0; - sqlite3_result_text(context, zBuf, 8, SQLITE_TRANSIENT); + if( x.useSubsec ){ + s = (int)1000.0*x.s; + zBuf[6] = '0' + (s/10000)%10; + zBuf[7] = '0' + (s/1000)%10; + zBuf[8] = '.'; + zBuf[9] = '0' + (s/100)%10; + zBuf[10] = '0' + (s/10)%10; + zBuf[11] = '0' + (s)%10; + zBuf[12] = 0; + n = 12; + }else{ + s = (int)x.s; + zBuf[6] = '0' + (s/10)%10; + zBuf[7] = '0' + (s)%10; + zBuf[8] = 0; + n = 8; + } + sqlite3_result_text(context, zBuf, n, SQLITE_TRANSIENT); } } @@ -24702,8 +24881,13 @@ static void strftimeFunc( break; } case 's': { - i64 iS = (i64)(x.iJD/1000 - 21086676*(i64)10000); - sqlite3_str_appendf(&sRes,"%lld",iS); + if( x.useSubsec ){ + sqlite3_str_appendf(&sRes,"%.3f", + (x.iJD - 21086676*(i64)10000000)/1000.0); + }else{ + i64 iS = (i64)(x.iJD/1000 - 21086676*(i64)10000); + sqlite3_str_appendf(&sRes,"%lld",iS); + } break; } case 'S': { @@ -30074,6 +30258,20 @@ static char et_getdigit(LONGDOUBLE_TYPE *val, int *cnt){ } #endif /* SQLITE_OMIT_FLOATING_POINT */ +#ifndef SQLITE_OMIT_FLOATING_POINT +/* +** "*val" is a u64. *msd is a divisor used to extract the +** most significant digit of *val. Extract that most significant +** digit and return it. +*/ +static char et_getdigit_int(u64 *val, u64 *msd){ + u64 x = (*val)/(*msd); + *val -= x*(*msd); + if( *msd>=10 ) *msd /= 10; + return '0' + (char)(x & 15); +} +#endif /* SQLITE_OMIT_FLOATING_POINT */ + /* ** Set the StrAccum object to an error mode. */ @@ -30166,6 +30364,8 @@ SQLITE_API void sqlite3_str_vappendf( char prefix; /* Prefix character. "+" or "-" or " " or '\0'. */ sqlite_uint64 longvalue; /* Value for integer types */ LONGDOUBLE_TYPE realvalue; /* Value for real types */ + sqlite_uint64 msd; /* Divisor to get most-significant-digit + ** of longvalue */ const et_info *infop; /* Pointer to the appropriate info structure */ char *zOut; /* Rendering buffer */ int nOut; /* Size of the rendering buffer */ @@ -30472,52 +30672,78 @@ SQLITE_API void sqlite3_str_vappendf( }else{ prefix = flag_prefix; } + exp = 0; if( xtype==etGENERIC && precision>0 ) precision--; testcase( precision>0xfff ); - idx = precision & 0xfff; - rounder = arRound[idx%10]; - while( idx>=10 ){ rounder *= 1.0e-10; idx -= 10; } - if( xtype==etFLOAT ){ - double rx = (double)realvalue; - sqlite3_uint64 u; - int ex; - memcpy(&u, &rx, sizeof(u)); - ex = -1023 + (int)((u>>52)&0x7ff); - if( precision+(ex/3) < 15 ) rounder += realvalue*3e-16; - realvalue += rounder; - } - /* Normalize realvalue to within 10.0 > realvalue >= 1.0 */ - exp = 0; - if( sqlite3IsNaN((double)realvalue) ){ - bufpt = "NaN"; - length = 3; - break; - } - if( realvalue>0.0 ){ - LONGDOUBLE_TYPE scale = 1.0; - while( realvalue>=1e100*scale && exp<=350 ){ scale *= 1e100;exp+=100;} - while( realvalue>=1e10*scale && exp<=350 ){ scale *= 1e10; exp+=10; } - while( realvalue>=10.0*scale && exp<=350 ){ scale *= 10.0; exp++; } - realvalue /= scale; - while( realvalue<1e-8 ){ realvalue *= 1e8; exp-=8; } - while( realvalue<1.0 ){ realvalue *= 10.0; exp--; } - if( exp>350 ){ - bufpt = buf; - buf[0] = prefix; - memcpy(buf+(prefix!=0),"Inf",4); - length = 3+(prefix!=0); + if( realvalue<1.0e+16 + && realvalue==(LONGDOUBLE_TYPE)(longvalue = (u64)realvalue) + ){ + /* Number is a pure integer that can be represented as u64 */ + for(msd=1; msd*10<=longvalue; msd *= 10, exp++){} + if( exp>precision && xtype!=etFLOAT ){ + u64 rnd = msd/2; + int kk = precision; + while( kk-- > 0 ){ rnd /= 10; } + longvalue += rnd; + } + }else{ + msd = 0; + longvalue = 0; /* To prevent a compiler warning */ + idx = precision & 0xfff; + rounder = arRound[idx%10]; + while( idx>=10 ){ rounder *= 1.0e-10; idx -= 10; } + if( xtype==etFLOAT ){ + double rx = (double)realvalue; + sqlite3_uint64 u; + int ex; + memcpy(&u, &rx, sizeof(u)); + ex = -1023 + (int)((u>>52)&0x7ff); + if( precision+(ex/3) < 15 ) rounder += realvalue*3e-16; + realvalue += rounder; + } + if( sqlite3IsNaN((double)realvalue) ){ + if( flag_zeropad ){ + bufpt = "null"; + length = 4; + }else{ + bufpt = "NaN"; + length = 3; + } break; } + + /* Normalize realvalue to within 10.0 > realvalue >= 1.0 */ + if( ALWAYS(realvalue>0.0) ){ + LONGDOUBLE_TYPE scale = 1.0; + while( realvalue>=1e100*scale && exp<=350){ scale*=1e100;exp+=100;} + while( realvalue>=1e10*scale && exp<=350 ){ scale*=1e10; exp+=10; } + while( realvalue>=10.0*scale && exp<=350 ){ scale *= 10.0; exp++; } + realvalue /= scale; + while( realvalue<1e-8 ){ realvalue *= 1e8; exp-=8; } + while( realvalue<1.0 ){ realvalue *= 10.0; exp--; } + if( exp>350 ){ + if( flag_zeropad ){ + realvalue = 9.0; + exp = 999; + }else{ + bufpt = buf; + buf[0] = prefix; + memcpy(buf+(prefix!=0),"Inf",4); + length = 3+(prefix!=0); + break; + } + } + if( xtype!=etFLOAT ){ + realvalue += rounder; + if( realvalue>=10.0 ){ realvalue *= 0.1; exp++; } + } + } } - bufpt = buf; + /* ** If the field type is etGENERIC, then convert to either etEXP ** or etFLOAT, as appropriate. */ - if( xtype!=etFLOAT ){ - realvalue += rounder; - if( realvalue>=10.0 ){ realvalue *= 0.1; exp++; } - } if( xtype==etGENERIC ){ flag_rtz = !flag_alternateform; if( exp<-4 || exp>precision ){ @@ -30534,16 +30760,18 @@ SQLITE_API void sqlite3_str_vappendf( }else{ e2 = exp; } + nsd = 16 + flag_altform2*10; + bufpt = buf; { i64 szBufNeeded; /* Size of a temporary buffer needed */ szBufNeeded = MAX(e2,0)+(i64)precision+(i64)width+15; + if( cThousand && e2>0 ) szBufNeeded += (e2+2)/3; if( szBufNeeded > etBUFSIZE ){ bufpt = zExtra = printfTempBuf(pAccum, szBufNeeded); if( bufpt==0 ) return; } } zOut = bufpt; - nsd = 16 + flag_altform2*10; flag_dp = (precision>0 ?1:0) | flag_alternateform | flag_altform2; /* The sign in front of the number */ if( prefix ){ @@ -30552,9 +30780,15 @@ SQLITE_API void sqlite3_str_vappendf( /* Digits prior to the decimal point */ if( e2<0 ){ *(bufpt++) = '0'; + }else if( msd>0 ){ + for(; e2>=0; e2--){ + *(bufpt++) = et_getdigit_int(&longvalue,&msd); + if( cThousand && (e2%3)==0 && e2>1 ) *(bufpt++) = ','; + } }else{ for(; e2>=0; e2--){ *(bufpt++) = et_getdigit(&realvalue,&nsd); + if( cThousand && (e2%3)==0 && e2>1 ) *(bufpt++) = ','; } } /* The decimal point */ @@ -30568,8 +30802,14 @@ SQLITE_API void sqlite3_str_vappendf( *(bufpt++) = '0'; } /* Significant digits after the decimal point */ - while( (precision--)>0 ){ - *(bufpt++) = et_getdigit(&realvalue,&nsd); + if( msd>0 ){ + while( (precision--)>0 ){ + *(bufpt++) = et_getdigit_int(&longvalue,&msd); + } + }else{ + while( (precision--)>0 ){ + *(bufpt++) = et_getdigit(&realvalue,&nsd); + } } /* Remove trailing zeros and the "." if no digits follow the "." */ if( flag_rtz && flag_dp ){ @@ -31250,12 +31490,22 @@ SQLITE_API char *sqlite3_vsnprintf(int n, char *zBuf, const char *zFormat, va_li return zBuf; } SQLITE_API char *sqlite3_snprintf(int n, char *zBuf, const char *zFormat, ...){ - char *z; + StrAccum acc; va_list ap; + if( n<=0 ) return zBuf; +#ifdef SQLITE_ENABLE_API_ARMOR + if( zBuf==0 || zFormat==0 ) { + (void)SQLITE_MISUSE_BKPT; + if( zBuf ) zBuf[0] = 0; + return zBuf; + } +#endif + sqlite3StrAccumInit(&acc, 0, zBuf, n, 0); va_start(ap,zFormat); - z = sqlite3_vsnprintf(n, zBuf, zFormat, ap); + sqlite3_str_vappendf(&acc, zFormat, ap); va_end(ap); - return z; + zBuf[acc.nChar] = 0; + return zBuf; } /* @@ -34285,13 +34535,15 @@ SQLITE_PRIVATE int sqlite3Int64ToText(i64 v, char *zOut){ } i = sizeof(zTemp)-2; zTemp[sizeof(zTemp)-1] = 0; - do{ - zTemp[i--] = (x%10) + '0'; + while( 1 /*exit-by-break*/ ){ + zTemp[i] = (x%10) + '0'; x = x/10; - }while( x ); - if( v<0 ) zTemp[i--] = '-'; - memcpy(zOut, &zTemp[i+1], sizeof(zTemp)-1-i); - return sizeof(zTemp)-2-i; + if( x==0 ) break; + i--; + }; + if( v<0 ) zTemp[--i] = '-'; + memcpy(zOut, &zTemp[i], sizeof(zTemp)-i); + return sizeof(zTemp)-1-i; } /* @@ -34456,7 +34708,9 @@ SQLITE_PRIVATE int sqlite3DecOrHexToI64(const char *z, i64 *pOut){ u = u*16 + sqlite3HexToInt(z[k]); } memcpy(pOut, &u, 8); - return (z[k]==0 && k-i<=16) ? 0 : 2; + if( k-i>16 ) return 2; + if( z[k]!=0 ) return 1; + return 0; }else #endif /* SQLITE_OMIT_HEX_INTEGER */ { @@ -34492,7 +34746,7 @@ SQLITE_PRIVATE int sqlite3GetInt32(const char *zNum, int *pValue){ u32 u = 0; zNum += 2; while( zNum[0]=='0' ) zNum++; - for(i=0; sqlite3Isxdigit(zNum[i]) && i<8; i++){ + for(i=0; i<8 && sqlite3Isxdigit(zNum[i]); i++){ u = u*16 + sqlite3HexToInt(zNum[i]); } if( (u&0x80000000)==0 && sqlite3Isxdigit(zNum[i])==0 ){ @@ -36988,7 +37242,7 @@ SQLITE_PRIVATE int sqlite3KvvfsInit(void){ #endif /* Use pread() and pwrite() if they are available */ -#if defined(__APPLE__) +#if defined(__APPLE__) || defined(__linux__) # define HAVE_PREAD 1 # define HAVE_PWRITE 1 #endif @@ -40238,12 +40492,6 @@ static int nfsUnlock(sqlite3_file *id, int eFileLock){ ** Seek to the offset passed as the second argument, then read cnt ** bytes into pBuf. Return the number of bytes actually read. ** -** NB: If you define USE_PREAD or USE_PREAD64, then it might also -** be necessary to define _XOPEN_SOURCE to be 500. This varies from -** one system to another. Since SQLite does not define USE_PREAD -** in any form by default, we will not attempt to define _XOPEN_SOURCE. -** See tickets #2741 and #2681. -** ** To avoid stomping the errno value on a failed read the lastErrno value ** is set before returning. */ @@ -50270,7 +50518,7 @@ static int winOpen( if( isReadWrite ){ int rc2, isRO = 0; sqlite3BeginBenignMalloc(); - rc2 = winAccess(pVfs, zName, SQLITE_ACCESS_READ, &isRO); + rc2 = winAccess(pVfs, zUtf8Name, SQLITE_ACCESS_READ, &isRO); sqlite3EndBenignMalloc(); if( rc2==SQLITE_OK && isRO ) break; } @@ -50287,7 +50535,7 @@ static int winOpen( if( isReadWrite ){ int rc2, isRO = 0; sqlite3BeginBenignMalloc(); - rc2 = winAccess(pVfs, zName, SQLITE_ACCESS_READ, &isRO); + rc2 = winAccess(pVfs, zUtf8Name, SQLITE_ACCESS_READ, &isRO); sqlite3EndBenignMalloc(); if( rc2==SQLITE_OK && isRO ) break; } @@ -50307,7 +50555,7 @@ static int winOpen( if( isReadWrite ){ int rc2, isRO = 0; sqlite3BeginBenignMalloc(); - rc2 = winAccess(pVfs, zName, SQLITE_ACCESS_READ, &isRO); + rc2 = winAccess(pVfs, zUtf8Name, SQLITE_ACCESS_READ, &isRO); sqlite3EndBenignMalloc(); if( rc2==SQLITE_OK && isRO ) break; } @@ -50530,6 +50778,13 @@ static int winAccess( OSTRACE(("ACCESS name=%s, flags=%x, pResOut=%p\n", zFilename, flags, pResOut)); + if( zFilename==0 ){ + *pResOut = 0; + OSTRACE(("ACCESS name=%s, pResOut=%p, *pResOut=%d, rc=SQLITE_OK\n", + zFilename, pResOut, *pResOut)); + return SQLITE_OK; + } + zConverted = winConvertFromUtf8Filename(zFilename); if( zConverted==0 ){ OSTRACE(("ACCESS name=%s, rc=SQLITE_IOERR_NOMEM\n", zFilename)); @@ -52686,11 +52941,15 @@ struct PCache { PgHdr *pPg; unsigned char *a; int j; - pPg = (PgHdr*)pLower->pExtra; - printf("%3lld: nRef %2d flgs %02x data ", i, pPg->nRef, pPg->flags); - a = (unsigned char *)pLower->pBuf; - for(j=0; j<12; j++) printf("%02x", a[j]); - printf(" ptr %p\n", pPg); + if( pLower==0 ){ + printf("%3d: NULL\n", i); + }else{ + pPg = (PgHdr*)pLower->pExtra; + printf("%3d: nRef %2lld flgs %02x data ", i, pPg->nRef, pPg->flags); + a = (unsigned char *)pLower->pBuf; + for(j=0; j<12; j++) printf("%02x", a[j]); + printf(" ptr %p\n", pPg); + } } static void pcacheDump(PCache *pCache){ int N; @@ -52703,9 +52962,8 @@ struct PCache { if( N>sqlite3PcacheMxDump ) N = sqlite3PcacheMxDump; for(i=1; i<=N; i++){ pLower = sqlite3GlobalConfig.pcache2.xFetch(pCache->pCache, i, 0); - if( pLower==0 ) continue; pcachePageTrace(i, pLower); - if( ((PgHdr*)pLower)->pPage==0 ){ + if( pLower && ((PgHdr*)pLower)->pPage==0 ){ sqlite3GlobalConfig.pcache2.xUnpin(pCache->pCache, pLower, 0); } } @@ -58093,6 +58351,8 @@ static int pager_truncate(Pager *pPager, Pgno nPage){ int rc = SQLITE_OK; assert( pPager->eState!=PAGER_ERROR ); assert( pPager->eState!=PAGER_READER ); + PAGERTRACE(("Truncate %d npage %u\n", PAGERID(pPager), nPage)); + if( isOpen(pPager->fd) && (pPager->eState>=PAGER_WRITER_DBMOD || pPager->eState==PAGER_OPEN) @@ -61010,6 +61270,10 @@ static int getPageNormal( if( !isOpen(pPager->fd) || pPager->dbSizepPager->mxPgno ){ rc = SQLITE_FULL; + if( pgno<=pPager->dbSize ){ + sqlite3PcacheRelease(pPg); + pPg = 0; + } goto pager_acquire_err; } if( noContent ){ @@ -61174,10 +61438,12 @@ SQLITE_PRIVATE DbPage *sqlite3PagerLookup(Pager *pPager, Pgno pgno){ /* ** Release a page reference. ** -** The sqlite3PagerUnref() and sqlite3PagerUnrefNotNull() may only be -** used if we know that the page being released is not the last page. +** The sqlite3PagerUnref() and sqlite3PagerUnrefNotNull() may only be used +** if we know that the page being released is not the last reference to page1. ** The btree layer always holds page1 open until the end, so these first -** to routines can be used to release any page other than BtShared.pPage1. +** two routines can be used to release any page other than BtShared.pPage1. +** The assert() at tag-20230419-2 proves that this constraint is always +** honored. ** ** Use sqlite3PagerUnrefPageOne() to release page1. This latter routine ** checks the total number of outstanding pages and if the number of @@ -61193,7 +61459,7 @@ SQLITE_PRIVATE void sqlite3PagerUnrefNotNull(DbPage *pPg){ sqlite3PcacheRelease(pPg); } /* Do not use this routine to release the last reference to page1 */ - assert( sqlite3PcacheRefCount(pPager->pPCache)>0 ); + assert( sqlite3PcacheRefCount(pPager->pPCache)>0 ); /* tag-20230419-2 */ } SQLITE_PRIVATE void sqlite3PagerUnref(DbPage *pPg){ if( pPg ) sqlite3PagerUnrefNotNull(pPg); @@ -62953,13 +63219,15 @@ SQLITE_PRIVATE int sqlite3PagerWalSupported(Pager *pPager){ */ static int pagerExclusiveLock(Pager *pPager){ int rc; /* Return code */ + u8 eOrigLock; /* Original lock */ - assert( pPager->eLock==SHARED_LOCK || pPager->eLock==EXCLUSIVE_LOCK ); + assert( pPager->eLock>=SHARED_LOCK ); + eOrigLock = pPager->eLock; rc = pagerLockDb(pPager, EXCLUSIVE_LOCK); if( rc!=SQLITE_OK ){ /* If the attempt to grab the exclusive lock failed, release the ** pending lock that may have been obtained instead. */ - pagerUnlockDb(pPager, SHARED_LOCK); + pagerUnlockDb(pPager, eOrigLock); } return rc; @@ -63964,19 +64232,40 @@ static void walChecksumBytes( assert( nByte>=8 ); assert( (nByte&0x00000007)==0 ); assert( nByte<=65536 ); + assert( nByte%4==0 ); - if( nativeCksum ){ - do { - s1 += *aData++ + s2; - s2 += *aData++ + s1; - }while( aDataszPage==szPage ); + if( (int)pWal->szPage!=szPage ){ + return SQLITE_CORRUPT_BKPT; /* TH3 test case: cov1/corrupt155.test */ + } /* Setup information needed to write frames into the WAL */ w.pWal = pWal; @@ -67567,7 +67858,7 @@ SQLITE_PRIVATE sqlite3_file *sqlite3WalFile(Wal *pWal){ ** byte are used. The integer consists of all bytes that have bit 8 set and ** the first byte with bit 8 clear. The most significant byte of the integer ** appears first. A variable-length integer may not be more than 9 bytes long. -** As a special case, all 8 bytes of the 9th byte are used as data. This +** As a special case, all 8 bits of the 9th byte are used as data. This ** allows a 64-bit integer to be encoded in 9 bytes. ** ** 0x00 becomes 0x00000000 @@ -67951,7 +68242,7 @@ struct BtCursor { #define BTCF_WriteFlag 0x01 /* True if a write cursor */ #define BTCF_ValidNKey 0x02 /* True if info.nKey is valid */ #define BTCF_ValidOvfl 0x04 /* True if aOverflow is valid */ -#define BTCF_AtLast 0x08 /* Cursor is pointing ot the last entry */ +#define BTCF_AtLast 0x08 /* Cursor is pointing to the last entry */ #define BTCF_Incrblob 0x10 /* True if an incremental I/O handle */ #define BTCF_Multiple 0x20 /* Maybe another cursor on the same btree */ #define BTCF_Pinned 0x40 /* Cursor is busy and cannot be moved */ @@ -68096,8 +68387,9 @@ struct IntegrityCk { int rc; /* SQLITE_OK, SQLITE_NOMEM, or SQLITE_INTERRUPT */ u32 nStep; /* Number of steps into the integrity_check process */ const char *zPfx; /* Error message prefix */ - Pgno v1; /* Value for first %u substitution in zPfx */ - int v2; /* Value for second %d substitution in zPfx */ + Pgno v0; /* Value for first %u substitution in zPfx (root page) */ + Pgno v1; /* Value for second %u substitution in zPfx (current pg) */ + int v2; /* Value for third %d substitution in zPfx */ StrAccum errMsg; /* Accumulate the error message text here */ u32 *heap; /* Min-heap used for analyzing cell coverage */ sqlite3 *db; /* Database connection running the check */ @@ -68560,8 +68852,8 @@ SQLITE_PRIVATE sqlite3_uint64 sqlite3BtreeSeekCount(Btree *pBt){ int corruptPageError(int lineno, MemPage *p){ char *zMsg; sqlite3BeginBenignMalloc(); - zMsg = sqlite3_mprintf("database corruption page %d of %s", - (int)p->pgno, sqlite3PagerFilename(p->pBt->pPager, 0) + zMsg = sqlite3_mprintf("database corruption page %u of %s", + p->pgno, sqlite3PagerFilename(p->pBt->pPager, 0) ); sqlite3EndBenignMalloc(); if( zMsg ){ @@ -69370,8 +69662,25 @@ SQLITE_PRIVATE int sqlite3BtreeCursorRestore(BtCursor *pCur, int *pDifferentRow) */ SQLITE_PRIVATE void sqlite3BtreeCursorHint(BtCursor *pCur, int eHintType, ...){ /* Used only by system that substitute their own storage engine */ +#ifdef SQLITE_DEBUG + if( ALWAYS(eHintType==BTREE_HINT_RANGE) ){ + va_list ap; + Expr *pExpr; + Walker w; + memset(&w, 0, sizeof(w)); + w.xExprCallback = sqlite3CursorRangeHintExprCheck; + va_start(ap, eHintType); + pExpr = va_arg(ap, Expr*); + w.u.aMem = va_arg(ap, Mem*); + va_end(ap); + assert( pExpr!=0 ); + assert( w.u.aMem!=0 ); + sqlite3WalkExpr(&w, pExpr); + } +#endif /* SQLITE_DEBUG */ } -#endif +#endif /* SQLITE_ENABLE_CURSOR_HINTS */ + /* ** Provide flag hints to the cursor. @@ -69456,7 +69765,7 @@ static void ptrmapPut(BtShared *pBt, Pgno key, u8 eType, Pgno parent, int *pRC){ pPtrmap = (u8 *)sqlite3PagerGetData(pDbPage); if( eType!=pPtrmap[offset] || get4byte(&pPtrmap[offset+1])!=parent ){ - TRACE(("PTRMAP_UPDATE: %d->(%d,%d)\n", key, eType, parent)); + TRACE(("PTRMAP_UPDATE: %u->(%u,%u)\n", key, eType, parent)); *pRC= rc = sqlite3PagerWrite(pDbPage); if( rc==SQLITE_OK ){ pPtrmap[offset] = eType; @@ -69655,27 +69964,31 @@ static void btreeParseCellPtr( iKey = *pIter; if( iKey>=0x80 ){ u8 x; - iKey = ((iKey&0x7f)<<7) | ((x = *++pIter) & 0x7f); + iKey = (iKey<<7) ^ (x = *++pIter); if( x>=0x80 ){ - iKey = (iKey<<7) | ((x =*++pIter) & 0x7f); + iKey = (iKey<<7) ^ (x = *++pIter); if( x>=0x80 ){ - iKey = (iKey<<7) | ((x = *++pIter) & 0x7f); + iKey = (iKey<<7) ^ 0x10204000 ^ (x = *++pIter); if( x>=0x80 ){ - iKey = (iKey<<7) | ((x = *++pIter) & 0x7f); + iKey = (iKey<<7) ^ 0x4000 ^ (x = *++pIter); if( x>=0x80 ){ - iKey = (iKey<<7) | ((x = *++pIter) & 0x7f); + iKey = (iKey<<7) ^ 0x4000 ^ (x = *++pIter); if( x>=0x80 ){ - iKey = (iKey<<7) | ((x = *++pIter) & 0x7f); + iKey = (iKey<<7) ^ 0x4000 ^ (x = *++pIter); if( x>=0x80 ){ - iKey = (iKey<<7) | ((x = *++pIter) & 0x7f); + iKey = (iKey<<7) ^ 0x4000 ^ (x = *++pIter); if( x>=0x80 ){ - iKey = (iKey<<8) | (*++pIter); + iKey = (iKey<<8) ^ 0x8000 ^ (*++pIter); } } } } } + }else{ + iKey ^= 0x204000; } + }else{ + iKey ^= 0x4000; } } pIter++; @@ -69752,10 +70065,11 @@ static void btreeParseCell( ** ** cellSizePtrNoPayload() => table internal nodes ** cellSizePtrTableLeaf() => table leaf nodes -** cellSizePtr() => all index nodes & table leaf nodes +** cellSizePtr() => index internal nodes +** cellSizeIdxLeaf() => index leaf nodes */ static u16 cellSizePtr(MemPage *pPage, u8 *pCell){ - u8 *pIter = pCell + pPage->childPtrSize; /* For looping over bytes of pCell */ + u8 *pIter = pCell + 4; /* For looping over bytes of pCell */ u8 *pEnd; /* End mark for a varint */ u32 nSize; /* Size value to return */ @@ -69768,6 +70082,49 @@ static u16 cellSizePtr(MemPage *pPage, u8 *pCell){ pPage->xParseCell(pPage, pCell, &debuginfo); #endif + assert( pPage->childPtrSize==4 ); + nSize = *pIter; + if( nSize>=0x80 ){ + pEnd = &pIter[8]; + nSize &= 0x7f; + do{ + nSize = (nSize<<7) | (*++pIter & 0x7f); + }while( *(pIter)>=0x80 && pItermaxLocal ); + testcase( nSize==(u32)pPage->maxLocal+1 ); + if( nSize<=pPage->maxLocal ){ + nSize += (u32)(pIter - pCell); + assert( nSize>4 ); + }else{ + int minLocal = pPage->minLocal; + nSize = minLocal + (nSize - minLocal) % (pPage->pBt->usableSize - 4); + testcase( nSize==pPage->maxLocal ); + testcase( nSize==(u32)pPage->maxLocal+1 ); + if( nSize>pPage->maxLocal ){ + nSize = minLocal; + } + nSize += 4 + (u16)(pIter - pCell); + } + assert( nSize==debuginfo.nSize || CORRUPT_DB ); + return (u16)nSize; +} +static u16 cellSizePtrIdxLeaf(MemPage *pPage, u8 *pCell){ + u8 *pIter = pCell; /* For looping over bytes of pCell */ + u8 *pEnd; /* End mark for a varint */ + u32 nSize; /* Size value to return */ + +#ifdef SQLITE_DEBUG + /* The value returned by this function should always be the same as + ** the (CellInfo.nSize) value found by doing a full parse of the + ** cell. If SQLITE_DEBUG is defined, an assert() at the bottom of + ** this function verifies that this invariant is not violated. */ + CellInfo debuginfo; + pPage->xParseCell(pPage, pCell, &debuginfo); +#endif + + assert( pPage->childPtrSize==0 ); nSize = *pIter; if( nSize>=0x80 ){ pEnd = &pIter[8]; @@ -70004,10 +70361,10 @@ static int defragmentPage(MemPage *pPage, int nMaxFrag){ /* These conditions have already been verified in btreeInitPage() ** if PRAGMA cell_size_check=ON. */ - if( pciCellLast ){ + if( pc>iCellLast ){ return SQLITE_CORRUPT_PAGE(pPage); } - assert( pc>=iCellStart && pc<=iCellLast ); + assert( pc>=0 && pc<=iCellLast ); size = pPage->xCellSize(pPage, &src[pc]); cbrk -= size; if( cbrkusableSize ){ @@ -70122,7 +70479,7 @@ static u8 *pageFindSlot(MemPage *pPg, int nByte, int *pRc){ ** allocation is being made in order to insert a new cell, so we will ** also end up needing a new cell pointer. */ -static int allocateSpace(MemPage *pPage, int nByte, int *pIdx){ +static SQLITE_INLINE int allocateSpace(MemPage *pPage, int nByte, int *pIdx){ const int hdr = pPage->hdrOffset; /* Local cache of pPage->hdrOffset */ u8 * const data = pPage->aData; /* Local cache of pPage->aData */ int top; /* First byte of cell content area */ @@ -70148,13 +70505,14 @@ static int allocateSpace(MemPage *pPage, int nByte, int *pIdx){ ** integer, so a value of 0 is used in its place. */ pTmp = &data[hdr+5]; top = get2byte(pTmp); - assert( top<=(int)pPage->pBt->usableSize ); /* by btreeComputeFreeSpace() */ if( gap>top ){ if( top==0 && pPage->pBt->usableSize==65536 ){ top = 65536; }else{ return SQLITE_CORRUPT_PAGE(pPage); } + }else if( top>(int)pPage->pBt->usableSize ){ + return SQLITE_CORRUPT_PAGE(pPage); } /* If there is enough space between gap and top for one more cell pointer, @@ -70237,7 +70595,7 @@ static int freeSpace(MemPage *pPage, u16 iStart, u16 iSize){ assert( CORRUPT_DB || iEnd <= pPage->pBt->usableSize ); assert( sqlite3_mutex_held(pPage->pBt->mutex) ); assert( iSize>=4 ); /* Minimum cell size is 4 */ - assert( iStart<=pPage->pBt->usableSize-4 ); + assert( CORRUPT_DB || iStart<=pPage->pBt->usableSize-4 ); /* The list of freeblocks must be in ascending order. Find the ** spot on the list where iStart should be inserted. @@ -70294,6 +70652,11 @@ static int freeSpace(MemPage *pPage, u16 iStart, u16 iSize){ } pTmp = &data[hdr+5]; x = get2byte(pTmp); + if( pPage->pBt->btsFlags & BTS_FAST_SECURE ){ + /* Overwrite deleted information with zeros when the secure_delete + ** option is enabled */ + memset(&data[iStart], 0, iSize); + } if( iStart<=x ){ /* The new freeblock is at the beginning of the cell content area, ** so just extend the cell content area rather than create another @@ -70305,14 +70668,9 @@ static int freeSpace(MemPage *pPage, u16 iStart, u16 iSize){ }else{ /* Insert the new freeblock into the freelist */ put2byte(&data[iPtr], iStart); + put2byte(&data[iStart], iFreeBlk); + put2byte(&data[iStart+2], iSize); } - if( pPage->pBt->btsFlags & BTS_FAST_SECURE ){ - /* Overwrite deleted information with zeros when the secure_delete - ** option is enabled */ - memset(&data[iStart], 0, iSize); - } - put2byte(&data[iStart], iFreeBlk); - put2byte(&data[iStart+2], iSize); pPage->nFree += iOrigSize; return SQLITE_OK; } @@ -70349,14 +70707,14 @@ static int decodeFlags(MemPage *pPage, int flagByte){ }else if( flagByte==(PTF_ZERODATA | PTF_LEAF) ){ pPage->intKey = 0; pPage->intKeyLeaf = 0; - pPage->xCellSize = cellSizePtr; + pPage->xCellSize = cellSizePtrIdxLeaf; pPage->xParseCell = btreeParseCellPtrIndex; pPage->maxLocal = pBt->maxLocal; pPage->minLocal = pBt->minLocal; }else{ pPage->intKey = 0; pPage->intKeyLeaf = 0; - pPage->xCellSize = cellSizePtr; + pPage->xCellSize = cellSizePtrIdxLeaf; pPage->xParseCell = btreeParseCellPtrIndex; return SQLITE_CORRUPT_PAGE(pPage); } @@ -72222,7 +72580,7 @@ static int relocatePage( if( iDbPage<3 ) return SQLITE_CORRUPT_BKPT; /* Move page iDbPage from its current location to page number iFreePage */ - TRACE(("AUTOVACUUM: Moving %d to free page %d (ptr page %d type %d)\n", + TRACE(("AUTOVACUUM: Moving %u to free page %u (ptr page %u type %u)\n", iDbPage, iFreePage, iPtrPage, eType)); rc = sqlite3PagerMovepage(pPager, pDbPage->pDbPage, iFreePage, isCommit); if( rc!=SQLITE_OK ){ @@ -74508,7 +74866,8 @@ static SQLITE_NOINLINE int btreeNext(BtCursor *pCur){ pPage = pCur->pPage; idx = ++pCur->ix; - if( !pPage->isInit || sqlite3FaultSim(412) ){ + if( sqlite3FaultSim(412) ) pPage->isInit = 0; + if( !pPage->isInit ){ return SQLITE_CORRUPT_BKPT; } @@ -74771,7 +75130,7 @@ static int allocateBtreePage( memcpy(&pPage1->aData[32], &pTrunk->aData[0], 4); *ppPage = pTrunk; pTrunk = 0; - TRACE(("ALLOCATE: %d trunk - %d free pages left\n", *pPgno, n-1)); + TRACE(("ALLOCATE: %u trunk - %u free pages left\n", *pPgno, n-1)); }else if( k>(u32)(pBt->usableSize/4 - 2) ){ /* Value of k is out of range. Database corruption */ rc = SQLITE_CORRUPT_PGNO(iTrunk); @@ -74837,7 +75196,7 @@ static int allocateBtreePage( } } pTrunk = 0; - TRACE(("ALLOCATE: %d trunk - %d free pages left\n", *pPgno, n-1)); + TRACE(("ALLOCATE: %u trunk - %u free pages left\n", *pPgno, n-1)); #endif }else if( k>0 ){ /* Extract a leaf from the trunk */ @@ -74882,8 +75241,8 @@ static int allocateBtreePage( ){ int noContent; *pPgno = iPage; - TRACE(("ALLOCATE: %d was leaf %d of %d on trunk %d" - ": %d more free pages\n", + TRACE(("ALLOCATE: %u was leaf %u of %u on trunk %u" + ": %u more free pages\n", *pPgno, closest+1, k, pTrunk->pgno, n-1)); rc = sqlite3PagerWrite(pTrunk->pDbPage); if( rc ) goto end_allocate_page; @@ -74939,7 +75298,7 @@ static int allocateBtreePage( ** becomes a new pointer-map page, the second is used by the caller. */ MemPage *pPg = 0; - TRACE(("ALLOCATE: %d from end of file (pointer-map page)\n", pBt->nPage)); + TRACE(("ALLOCATE: %u from end of file (pointer-map page)\n", pBt->nPage)); assert( pBt->nPage!=PENDING_BYTE_PAGE(pBt) ); rc = btreeGetUnusedPage(pBt, pBt->nPage, &pPg, bNoContent); if( rc==SQLITE_OK ){ @@ -74962,7 +75321,7 @@ static int allocateBtreePage( releasePage(*ppPage); *ppPage = 0; } - TRACE(("ALLOCATE: %d from end of file\n", *pPgno)); + TRACE(("ALLOCATE: %u from end of file\n", *pPgno)); } assert( CORRUPT_DB || *pPgno!=PENDING_BYTE_PAGE(pBt) ); @@ -75090,7 +75449,7 @@ static int freePage2(BtShared *pBt, MemPage *pMemPage, Pgno iPage){ } rc = btreeSetHasContent(pBt, iPage); } - TRACE(("FREE-PAGE: %d leaf on trunk page %d\n",pPage->pgno,pTrunk->pgno)); + TRACE(("FREE-PAGE: %u leaf on trunk page %u\n",pPage->pgno,pTrunk->pgno)); goto freepage_out; } } @@ -75111,7 +75470,7 @@ static int freePage2(BtShared *pBt, MemPage *pMemPage, Pgno iPage){ put4byte(pPage->aData, iTrunk); put4byte(&pPage->aData[4], 0); put4byte(&pPage1->aData[32], iPage); - TRACE(("FREE-PAGE: %d new trunk page replacing %d\n", pPage->pgno, iTrunk)); + TRACE(("FREE-PAGE: %u new trunk page replacing %u\n", pPage->pgno, iTrunk)); freepage_out: if( pPage ){ @@ -75470,6 +75829,14 @@ static void dropCell(MemPage *pPage, int idx, int sz, int *pRC){ ** in pTemp or the original pCell) and also record its index. ** Allocating a new entry in pPage->aCell[] implies that ** pPage->nOverflow is incremented. +** +** The insertCellFast() routine below works exactly the same as +** insertCell() except that it lacks the pTemp and iChild parameters +** which are assumed zero. Other than that, the two routines are the +** same. +** +** Fixes or enhancements to this routine should be reflected in +** insertCellFast()! */ static int insertCell( MemPage *pPage, /* Page into which we are copying */ @@ -75492,14 +75859,103 @@ static int insertCell( assert( sqlite3_mutex_held(pPage->pBt->mutex) ); assert( sz==pPage->xCellSize(pPage, pCell) || CORRUPT_DB ); assert( pPage->nFree>=0 ); + assert( iChild>0 ); if( pPage->nOverflow || sz+2>pPage->nFree ){ if( pTemp ){ memcpy(pTemp, pCell, sz); pCell = pTemp; } - if( iChild ){ - put4byte(pCell, iChild); + put4byte(pCell, iChild); + j = pPage->nOverflow++; + /* Comparison against ArraySize-1 since we hold back one extra slot + ** as a contingency. In other words, never need more than 3 overflow + ** slots but 4 are allocated, just to be safe. */ + assert( j < ArraySize(pPage->apOvfl)-1 ); + pPage->apOvfl[j] = pCell; + pPage->aiOvfl[j] = (u16)i; + + /* When multiple overflows occur, they are always sequential and in + ** sorted order. This invariants arise because multiple overflows can + ** only occur when inserting divider cells into the parent page during + ** balancing, and the dividers are adjacent and sorted. + */ + assert( j==0 || pPage->aiOvfl[j-1]<(u16)i ); /* Overflows in sorted order */ + assert( j==0 || i==pPage->aiOvfl[j-1]+1 ); /* Overflows are sequential */ + }else{ + int rc = sqlite3PagerWrite(pPage->pDbPage); + if( NEVER(rc!=SQLITE_OK) ){ + return rc; } + assert( sqlite3PagerIswriteable(pPage->pDbPage) ); + data = pPage->aData; + assert( &data[pPage->cellOffset]==pPage->aCellIdx ); + rc = allocateSpace(pPage, sz, &idx); + if( rc ){ return rc; } + /* The allocateSpace() routine guarantees the following properties + ** if it returns successfully */ + assert( idx >= 0 ); + assert( idx >= pPage->cellOffset+2*pPage->nCell+2 || CORRUPT_DB ); + assert( idx+sz <= (int)pPage->pBt->usableSize ); + pPage->nFree -= (u16)(2 + sz); + /* In a corrupt database where an entry in the cell index section of + ** a btree page has a value of 3 or less, the pCell value might point + ** as many as 4 bytes in front of the start of the aData buffer for + ** the source page. Make sure this does not cause problems by not + ** reading the first 4 bytes */ + memcpy(&data[idx+4], pCell+4, sz-4); + put4byte(&data[idx], iChild); + pIns = pPage->aCellIdx + i*2; + memmove(pIns+2, pIns, 2*(pPage->nCell - i)); + put2byte(pIns, idx); + pPage->nCell++; + /* increment the cell count */ + if( (++data[pPage->hdrOffset+4])==0 ) data[pPage->hdrOffset+3]++; + assert( get2byte(&data[pPage->hdrOffset+3])==pPage->nCell || CORRUPT_DB ); +#ifndef SQLITE_OMIT_AUTOVACUUM + if( pPage->pBt->autoVacuum ){ + int rc2 = SQLITE_OK; + /* The cell may contain a pointer to an overflow page. If so, write + ** the entry for the overflow page into the pointer map. + */ + ptrmapPutOvflPtr(pPage, pPage, pCell, &rc2); + if( rc2 ) return rc2; + } +#endif + } + return SQLITE_OK; +} + +/* +** This variant of insertCell() assumes that the pTemp and iChild +** parameters are both zero. Use this variant in sqlite3BtreeInsert() +** for performance improvement, and also so that this variant is only +** called from that one place, and is thus inlined, and thus runs must +** faster. +** +** Fixes or enhancements to this routine should be reflected into +** the insertCell() routine. +*/ +static int insertCellFast( + MemPage *pPage, /* Page into which we are copying */ + int i, /* New cell becomes the i-th cell of the page */ + u8 *pCell, /* Content of the new cell */ + int sz /* Bytes of content in pCell */ +){ + int idx = 0; /* Where to write new cell content in data[] */ + int j; /* Loop counter */ + u8 *data; /* The content of the whole page */ + u8 *pIns; /* The point in pPage->aCellIdx[] where no cell inserted */ + + assert( i>=0 && i<=pPage->nCell+pPage->nOverflow ); + assert( MX_CELL(pPage->pBt)<=10921 ); + assert( pPage->nCell<=MX_CELL(pPage->pBt) || CORRUPT_DB ); + assert( pPage->nOverflow<=ArraySize(pPage->apOvfl) ); + assert( ArraySize(pPage->apOvfl)==ArraySize(pPage->aiOvfl) ); + assert( sqlite3_mutex_held(pPage->pBt->mutex) ); + assert( sz==pPage->xCellSize(pPage, pCell) || CORRUPT_DB ); + assert( pPage->nFree>=0 ); + assert( pPage->nOverflow==0 ); + if( sz+2>pPage->nFree ){ j = pPage->nOverflow++; /* Comparison against ArraySize-1 since we hold back one extra slot ** as a contingency. In other words, never need more than 3 overflow @@ -75531,17 +75987,7 @@ static int insertCell( assert( idx >= pPage->cellOffset+2*pPage->nCell+2 || CORRUPT_DB ); assert( idx+sz <= (int)pPage->pBt->usableSize ); pPage->nFree -= (u16)(2 + sz); - if( iChild ){ - /* In a corrupt database where an entry in the cell index section of - ** a btree page has a value of 3 or less, the pCell value might point - ** as many as 4 bytes in front of the start of the aData buffer for - ** the source page. Make sure this does not cause problems by not - ** reading the first 4 bytes */ - memcpy(&data[idx+4], pCell+4, sz-4); - put4byte(&data[idx], iChild); - }else{ - memcpy(&data[idx], pCell, sz); - } + memcpy(&data[idx], pCell, sz); pIns = pPage->aCellIdx + i*2; memmove(pIns+2, pIns, 2*(pPage->nCell - i)); put2byte(pIns, idx); @@ -75726,7 +76172,7 @@ static int rebuildPage( assert( i(u32)usableSize ){ j = 0; } + if( NEVER(j>(u32)usableSize) ){ j = 0; } memcpy(&pTmp[j], &aData[j], usableSize - j); for(k=0; pCArray->ixNx[k]<=i && ALWAYS(kpBt->usableSize]; u8 * const pStart = &aData[pPg->hdrOffset + 8 + pPg->childPtrSize]; int nRet = 0; - int i; + int i, j; int iEnd = iFirst + nCell; - u8 *pFree = 0; /* \__ Parameters for pending call to */ - int szFree = 0; /* / freeSpace() */ + int nFree = 0; + int aOfst[10]; + int aAfter[10]; for(i=iFirst; iapCell[i]; if( SQLITE_WITHIN(pCell, pStart, pEnd) ){ int sz; + int iAfter; + int iOfst; /* No need to use cachedCellSize() here. The sizes of all cells that ** are to be freed have already been computing while deciding which ** cells need freeing */ sz = pCArray->szCell[i]; assert( sz>0 ); - if( pFree!=(pCell + sz) ){ - if( pFree ){ - assert( pFree>aData && (pFree - aData)<65536 ); - freeSpace(pPg, (u16)(pFree - aData), szFree); + iOfst = (u16)(pCell - aData); + iAfter = iOfst+sz; + for(j=0; jpEnd ){ - return 0; + } + if( j>=nFree ){ + if( nFree>=(int)(sizeof(aOfst)/sizeof(aOfst[0])) ){ + for(j=0; jpEnd ) return 0; + nFree++; } nRet++; } } - if( pFree ){ - assert( pFree>aData && (pFree - aData)<65536 ); - freeSpace(pPg, (u16)(pFree - aData), szFree); + for(j=0; jpPg->aDataEnd ) goto editpage_fail; + if( NEVER(pData>pPg->aDataEnd) ) goto editpage_fail; /* Add cells to the start of the page */ if( iNew0 || (pParent->pgno==1 && pParent->nCell==0) || CORRUPT_DB); - TRACE(("BALANCE: old: %d(nc=%d) %d(nc=%d) %d(nc=%d)\n", + TRACE(("BALANCE: old: %u(nc=%u) %u(nc=%u) %u(nc=%u)\n", apOld[0]->pgno, apOld[0]->nCell, nOld>=2 ? apOld[1]->pgno : 0, nOld>=2 ? apOld[1]->nCell : 0, nOld>=3 ? apOld[2]->pgno : 0, nOld>=3 ? apOld[2]->nCell : 0 @@ -76781,8 +77235,8 @@ static int balance_nonroot( } } - TRACE(("BALANCE: new: %d(%d nc=%d) %d(%d nc=%d) %d(%d nc=%d) " - "%d(%d nc=%d) %d(%d nc=%d)\n", + TRACE(("BALANCE: new: %u(%u nc=%u) %u(%u nc=%u) %u(%u nc=%u) " + "%u(%u nc=%u) %u(%u nc=%u)\n", apNew[0]->pgno, szNew[0], cntNew[0], nNew>=2 ? apNew[1]->pgno : 0, nNew>=2 ? szNew[1] : 0, nNew>=2 ? cntNew[1] - cntNew[0] - !leafData : 0, @@ -77027,7 +77481,7 @@ static int balance_nonroot( } assert( pParent->isInit ); - TRACE(("BALANCE: finished: old=%d new=%d cells=%d\n", + TRACE(("BALANCE: finished: old=%u new=%u cells=%u\n", nOld, nNew, b.nCell)); /* Free any old pages that were not reused as new pages. @@ -77112,7 +77566,7 @@ static int balance_deeper(MemPage *pRoot, MemPage **ppChild){ assert( sqlite3PagerIswriteable(pRoot->pDbPage) ); assert( pChild->nCell==pRoot->nCell || CORRUPT_DB ); - TRACE(("BALANCE: copy root %d into %d\n", pRoot->pgno, pChild->pgno)); + TRACE(("BALANCE: copy root %u into %u\n", pRoot->pgno, pChild->pgno)); /* Copy the overflow cells from pRoot to pChild */ memcpy(pChild->aiOvfl, pRoot->aiOvfl, @@ -77595,7 +78049,7 @@ SQLITE_PRIVATE int sqlite3BtreeInsert( } } assert( pCur->eState==CURSOR_VALID - || (pCur->eState==CURSOR_INVALID && loc) ); + || (pCur->eState==CURSOR_INVALID && loc) || CORRUPT_DB ); pPage = pCur->pPage; assert( pPage->intKey || pX->nKey>=0 || (flags & BTREE_PREFORMAT) ); @@ -77610,7 +78064,7 @@ SQLITE_PRIVATE int sqlite3BtreeInsert( if( rc ) return rc; } - TRACE(("INSERT: table=%d nkey=%lld ndata=%d page=%d %s\n", + TRACE(("INSERT: table=%u nkey=%lld ndata=%u page=%u %s\n", pCur->pgnoRoot, pX->nKey, pX->nData, pPage->pgno, loc==0 ? "overwrite" : "new entry")); assert( pPage->isInit || CORRUPT_DB ); @@ -77686,7 +78140,7 @@ SQLITE_PRIVATE int sqlite3BtreeInsert( }else{ assert( pPage->leaf ); } - rc = insertCell(pPage, idx, newCell, szNew, 0, 0); + rc = insertCellFast(pPage, idx, newCell, szNew); assert( pPage->nOverflow==0 || rc==SQLITE_OK ); assert( rc!=SQLITE_OK || pPage->nCell>0 || pPage->nOverflow>0 ); @@ -77910,6 +78364,9 @@ SQLITE_PRIVATE int sqlite3BtreeDelete(BtCursor *pCur, u8 flags){ if( pPage->nFree<0 && btreeComputeFreeSpace(pPage) ){ return SQLITE_CORRUPT_BKPT; } + if( pCell<&pPage->aCellIdx[pPage->nCell] ){ + return SQLITE_CORRUPT_BKPT; + } /* If the BTREE_SAVEPOSITION bit is on, then the cursor position must ** be preserved following this delete operation. If the current delete @@ -78658,7 +79115,8 @@ static void checkAppendMsg( sqlite3_str_append(&pCheck->errMsg, "\n", 1); } if( pCheck->zPfx ){ - sqlite3_str_appendf(&pCheck->errMsg, pCheck->zPfx, pCheck->v1, pCheck->v2); + sqlite3_str_appendf(&pCheck->errMsg, pCheck->zPfx, + pCheck->v0, pCheck->v1, pCheck->v2); } sqlite3_str_vappendf(&pCheck->errMsg, zFormat, ap); va_end(ap); @@ -78698,11 +79156,11 @@ static void setPageReferenced(IntegrityCk *pCheck, Pgno iPg){ */ static int checkRef(IntegrityCk *pCheck, Pgno iPage){ if( iPage>pCheck->nPage || iPage==0 ){ - checkAppendMsg(pCheck, "invalid page number %d", iPage); + checkAppendMsg(pCheck, "invalid page number %u", iPage); return 1; } if( getPageReferenced(pCheck, iPage) ){ - checkAppendMsg(pCheck, "2nd reference to page %d", iPage); + checkAppendMsg(pCheck, "2nd reference to page %u", iPage); return 1; } setPageReferenced(pCheck, iPage); @@ -78728,13 +79186,13 @@ static void checkPtrmap( rc = ptrmapGet(pCheck->pBt, iChild, &ePtrmapType, &iPtrmapParent); if( rc!=SQLITE_OK ){ if( rc==SQLITE_NOMEM || rc==SQLITE_IOERR_NOMEM ) checkOom(pCheck); - checkAppendMsg(pCheck, "Failed to read ptrmap key=%d", iChild); + checkAppendMsg(pCheck, "Failed to read ptrmap key=%u", iChild); return; } if( ePtrmapType!=eType || iPtrmapParent!=iParent ){ checkAppendMsg(pCheck, - "Bad ptr map entry key=%d expected=(%d,%d) got=(%d,%d)", + "Bad ptr map entry key=%u expected=(%u,%u) got=(%u,%u)", iChild, eType, iParent, ePtrmapType, iPtrmapParent); } } @@ -78759,7 +79217,7 @@ static void checkList( if( checkRef(pCheck, iPage) ) break; N--; if( sqlite3PagerGet(pCheck->pPager, (Pgno)iPage, &pOvflPage, 0) ){ - checkAppendMsg(pCheck, "failed to get page %d", iPage); + checkAppendMsg(pCheck, "failed to get page %u", iPage); break; } pOvflData = (unsigned char *)sqlite3PagerGetData(pOvflPage); @@ -78772,7 +79230,7 @@ static void checkList( #endif if( n>pCheck->pBt->usableSize/4-2 ){ checkAppendMsg(pCheck, - "freelist leaf count too big on page %d", iPage); + "freelist leaf count too big on page %u", iPage); N--; }else{ for(i=0; i<(int)n; i++){ @@ -78804,7 +79262,7 @@ static void checkList( } if( N && nErrAtStart==pCheck->nErr ){ checkAppendMsg(pCheck, - "%s is %d but should be %d", + "%s is %u but should be %u", isFreeList ? "size" : "overflow list length", expected-N, expected); } @@ -78919,8 +79377,8 @@ static int checkTreePage( usableSize = pBt->usableSize; if( iPage==0 ) return 0; if( checkRef(pCheck, iPage) ) return 0; - pCheck->zPfx = "Page %u: "; - pCheck->v1 = iPage; + pCheck->zPfx = "Tree %u page %u: "; + pCheck->v0 = pCheck->v1 = iPage; if( (rc = btreeGetPage(pBt, iPage, &pPage, 0))!=0 ){ checkAppendMsg(pCheck, "unable to get the page. error code=%d", rc); @@ -78946,7 +79404,7 @@ static int checkTreePage( hdr = pPage->hdrOffset; /* Set up for cell analysis */ - pCheck->zPfx = "On tree page %u cell %d: "; + pCheck->zPfx = "Tree %u page %u cell %u: "; contentOffset = get2byteNotZero(&data[hdr+5]); assert( contentOffset<=usableSize ); /* Enforced by btreeInitPage() */ @@ -78966,7 +79424,7 @@ static int checkTreePage( pgno = get4byte(&data[hdr+8]); #ifndef SQLITE_OMIT_AUTOVACUUM if( pBt->autoVacuum ){ - pCheck->zPfx = "On page %u at right child: "; + pCheck->zPfx = "Tree %u page %u right child: "; checkPtrmap(pCheck, pgno, PTRMAP_BTREE, iPage); } #endif @@ -78990,7 +79448,7 @@ static int checkTreePage( pc = get2byteAligned(pCellIdx); pCellIdx -= 2; if( pcusableSize-4 ){ - checkAppendMsg(pCheck, "Offset %d out of range %d..%d", + checkAppendMsg(pCheck, "Offset %u out of range %u..%u", pc, contentOffset, usableSize-4); doCoverageCheck = 0; continue; @@ -79122,7 +79580,7 @@ static int checkTreePage( */ if( heap[0]==0 && nFrag!=data[hdr+7] ){ checkAppendMsg(pCheck, - "Fragmentation of %d bytes reported as %d on page %u", + "Fragmentation of %u bytes reported as %u on page %u", nFrag, data[hdr+7], iPage); } } @@ -79219,7 +79677,7 @@ SQLITE_PRIVATE int sqlite3BtreeIntegrityCheck( /* Check the integrity of the freelist */ if( bCkFreelist ){ - sCheck.zPfx = "Main freelist: "; + sCheck.zPfx = "Freelist: "; checkList(&sCheck, 1, get4byte(&pBt->pPage1->aData[32]), get4byte(&pBt->pPage1->aData[36])); sCheck.zPfx = 0; @@ -79236,7 +79694,7 @@ SQLITE_PRIVATE int sqlite3BtreeIntegrityCheck( mxInHdr = get4byte(&pBt->pPage1->aData[52]); if( mx!=mxInHdr ){ checkAppendMsg(&sCheck, - "max rootpage (%d) disagrees with header (%d)", + "max rootpage (%u) disagrees with header (%u)", mx, mxInHdr ); } @@ -79267,7 +79725,7 @@ SQLITE_PRIVATE int sqlite3BtreeIntegrityCheck( for(i=1; i<=sCheck.nPage && sCheck.mxErr; i++){ #ifdef SQLITE_OMIT_AUTOVACUUM if( getPageReferenced(&sCheck, i)==0 ){ - checkAppendMsg(&sCheck, "Page %d is never used", i); + checkAppendMsg(&sCheck, "Page %u: never used", i); } #else /* If the database supports auto-vacuum, make sure no tables contain @@ -79275,11 +79733,11 @@ SQLITE_PRIVATE int sqlite3BtreeIntegrityCheck( */ if( getPageReferenced(&sCheck, i)==0 && (PTRMAP_PAGENO(pBt, i)!=i || !pBt->autoVacuum) ){ - checkAppendMsg(&sCheck, "Page %d is never used", i); + checkAppendMsg(&sCheck, "Page %u: never used", i); } if( getPageReferenced(&sCheck, i)!=0 && (PTRMAP_PAGENO(pBt, i)==i && pBt->autoVacuum) ){ - checkAppendMsg(&sCheck, "Pointer map page %d is referenced", i); + checkAppendMsg(&sCheck, "Page %u: pointer map referenced", i); } #endif } @@ -79841,13 +80299,7 @@ static int backupOnePage( assert( !isFatalError(p->rc) ); assert( iSrcPg!=PENDING_BYTE_PAGE(p->pSrc->pBt) ); assert( zSrcData ); - - /* Catch the case where the destination is an in-memory database and the - ** page sizes of the source and destination differ. - */ - if( nSrcPgsz!=nDestPgsz && sqlite3PagerIsMemdb(pDestPager) ){ - rc = SQLITE_READONLY; - } + assert( nSrcPgsz==nDestPgsz || sqlite3PagerIsMemdb(pDestPager)==0 ); /* This loop runs once for each destination page spanned by the source ** page. For each iteration, variable iOff is set to the byte offset @@ -79980,7 +80432,10 @@ SQLITE_API int sqlite3_backup_step(sqlite3_backup *p, int nPage){ pgszSrc = sqlite3BtreeGetPageSize(p->pSrc); pgszDest = sqlite3BtreeGetPageSize(p->pDest); destMode = sqlite3PagerGetJournalMode(sqlite3BtreePager(p->pDest)); - if( SQLITE_OK==rc && destMode==PAGER_JOURNALMODE_WAL && pgszSrc!=pgszDest ){ + if( SQLITE_OK==rc + && (destMode==PAGER_JOURNALMODE_WAL || sqlite3PagerIsMemdb(pDestPager)) + && pgszSrc!=pgszDest + ){ rc = SQLITE_READONLY; } @@ -80529,6 +80984,7 @@ SQLITE_PRIVATE int sqlite3VdbeMemValidStrRep(Mem *p){ char *z; int i, j, incr; if( (p->flags & MEM_Str)==0 ) return 1; + if( p->db && p->db->mallocFailed ) return 1; if( p->flags & MEM_Term ){ /* Insure that the string is properly zero-terminated. Pay particular ** attention to the case where p->n is odd */ @@ -80811,7 +81267,7 @@ SQLITE_PRIVATE int sqlite3VdbeMemStringify(Mem *pMem, u8 enc, u8 bForce){ vdbeMemRenderNum(nByte, pMem->z, pMem); assert( pMem->z!=0 ); - assert( pMem->n==sqlite3Strlen30NN(pMem->z) ); + assert( pMem->n==(int)sqlite3Strlen30NN(pMem->z) ); pMem->enc = SQLITE_UTF8; pMem->flags |= MEM_Str|MEM_Term; if( bForce ) pMem->flags &= ~(MEM_Int|MEM_Real|MEM_IntReal); @@ -81855,6 +82311,9 @@ static int valueFromFunction( if( pList ) nVal = pList->nExpr; assert( !ExprHasProperty(p, EP_IntValue) ); pFunc = sqlite3FindFunction(db, p->u.zToken, nVal, enc, 0); +#ifdef SQLITE_ENABLE_UNKNOWN_SQL_FUNCTION + if( pFunc==0 ) return SQLITE_OK; +#endif assert( pFunc ); if( (pFunc->funcFlags & (SQLITE_FUNC_CONSTANT|SQLITE_FUNC_SLOCHNG))==0 || (pFunc->funcFlags & SQLITE_FUNC_NEEDCOLL) @@ -81891,16 +82350,11 @@ static int valueFromFunction( }else{ sqlite3ValueApplyAffinity(pVal, aff, SQLITE_UTF8); assert( rc==SQLITE_OK ); - assert( enc==pVal->enc - || (pVal->flags & MEM_Str)==0 - || db->mallocFailed ); -#if 0 /* Not reachable except after a prior failure */ rc = sqlite3VdbeChangeEncoding(pVal, enc); - if( rc==SQLITE_OK && sqlite3VdbeMemTooBig(pVal) ){ + if( NEVER(rc==SQLITE_OK && sqlite3VdbeMemTooBig(pVal)) ){ rc = SQLITE_TOOBIG; pCtx->pParse->nErr++; } -#endif } value_from_function_out: @@ -81964,6 +82418,13 @@ static int valueFromExpr( rc = valueFromExpr(db, pExpr->pLeft, enc, aff, ppVal, pCtx); testcase( rc!=SQLITE_OK ); if( *ppVal ){ +#ifdef SQLITE_ENABLE_STAT4 + rc = ExpandBlob(*ppVal); +#else + /* zero-blobs only come from functions, not literal values. And + ** functions are only processed under STAT4 */ + assert( (ppVal[0][0].flags & MEM_Zero)==0 ); +#endif sqlite3VdbeMemCast(*ppVal, aff, enc); sqlite3ValueApplyAffinity(*ppVal, affinity, enc); } @@ -82810,10 +83271,10 @@ SQLITE_PRIVATE void sqlite3ExplainBreakpoint(const char *z1, const char *z2){ */ SQLITE_PRIVATE int sqlite3VdbeExplain(Parse *pParse, u8 bPush, const char *zFmt, ...){ int addr = 0; -#if !defined(SQLITE_DEBUG) && !defined(SQLITE_ENABLE_STMT_SCANSTATUS) +#if !defined(SQLITE_DEBUG) /* Always include the OP_Explain opcodes if SQLITE_DEBUG is defined. ** But omit them (for performance) during production builds */ - if( pParse->explain==2 ) + if( pParse->explain==2 || IS_STMT_SCANSTATUS(pParse->db) ) #endif { char *zMsg; @@ -83187,6 +83648,8 @@ static void resolveP2Values(Vdbe *p, int *pMaxFuncArgs){ Op *pOp; Parse *pParse = p->pParse; int *aLabel = pParse->aLabel; + + assert( pParse->db->mallocFailed==0 ); /* tag-20230419-1 */ p->readOnly = 1; p->bIsReader = 0; pOp = &p->aOp[p->nOp-1]; @@ -83246,6 +83709,7 @@ static void resolveP2Values(Vdbe *p, int *pMaxFuncArgs){ ** have non-negative values for P2. */ assert( (sqlite3OpcodeProperty[pOp->opcode] & OPFLG_JUMP)!=0 ); assert( ADDR(pOp->p2)<-pParse->nLabel ); + assert( aLabel!=0 ); /* True because of tag-20230419-1 */ pOp->p2 = aLabel[ADDR(pOp->p2)]; } break; @@ -83489,18 +83953,20 @@ SQLITE_PRIVATE void sqlite3VdbeScanStatus( LogEst nEst, /* Estimated number of output rows */ const char *zName /* Name of table or index being scanned */ ){ - sqlite3_int64 nByte = (p->nScan+1) * sizeof(ScanStatus); - ScanStatus *aNew; - aNew = (ScanStatus*)sqlite3DbRealloc(p->db, p->aScan, nByte); - if( aNew ){ - ScanStatus *pNew = &aNew[p->nScan++]; - memset(pNew, 0, sizeof(ScanStatus)); - pNew->addrExplain = addrExplain; - pNew->addrLoop = addrLoop; - pNew->addrVisit = addrVisit; - pNew->nEst = nEst; - pNew->zName = sqlite3DbStrDup(p->db, zName); - p->aScan = aNew; + if( IS_STMT_SCANSTATUS(p->db) ){ + sqlite3_int64 nByte = (p->nScan+1) * sizeof(ScanStatus); + ScanStatus *aNew; + aNew = (ScanStatus*)sqlite3DbRealloc(p->db, p->aScan, nByte); + if( aNew ){ + ScanStatus *pNew = &aNew[p->nScan++]; + memset(pNew, 0, sizeof(ScanStatus)); + pNew->addrExplain = addrExplain; + pNew->addrLoop = addrLoop; + pNew->addrVisit = addrVisit; + pNew->nEst = nEst; + pNew->zName = sqlite3DbStrDup(p->db, zName); + p->aScan = aNew; + } } } @@ -83517,20 +83983,22 @@ SQLITE_PRIVATE void sqlite3VdbeScanStatusRange( int addrStart, int addrEnd ){ - ScanStatus *pScan = 0; - int ii; - for(ii=p->nScan-1; ii>=0; ii--){ - pScan = &p->aScan[ii]; - if( pScan->addrExplain==addrExplain ) break; - pScan = 0; - } - if( pScan ){ - if( addrEnd<0 ) addrEnd = sqlite3VdbeCurrentAddr(p)-1; - for(ii=0; iiaAddrRange); ii+=2){ - if( pScan->aAddrRange[ii]==0 ){ - pScan->aAddrRange[ii] = addrStart; - pScan->aAddrRange[ii+1] = addrEnd; - break; + if( IS_STMT_SCANSTATUS(p->db) ){ + ScanStatus *pScan = 0; + int ii; + for(ii=p->nScan-1; ii>=0; ii--){ + pScan = &p->aScan[ii]; + if( pScan->addrExplain==addrExplain ) break; + pScan = 0; + } + if( pScan ){ + if( addrEnd<0 ) addrEnd = sqlite3VdbeCurrentAddr(p)-1; + for(ii=0; iiaAddrRange); ii+=2){ + if( pScan->aAddrRange[ii]==0 ){ + pScan->aAddrRange[ii] = addrStart; + pScan->aAddrRange[ii+1] = addrEnd; + break; + } } } } @@ -83547,19 +84015,21 @@ SQLITE_PRIVATE void sqlite3VdbeScanStatusCounters( int addrLoop, int addrVisit ){ - ScanStatus *pScan = 0; - int ii; - for(ii=p->nScan-1; ii>=0; ii--){ - pScan = &p->aScan[ii]; - if( pScan->addrExplain==addrExplain ) break; - pScan = 0; - } - if( pScan ){ - pScan->addrLoop = addrLoop; - pScan->addrVisit = addrVisit; + if( IS_STMT_SCANSTATUS(p->db) ){ + ScanStatus *pScan = 0; + int ii; + for(ii=p->nScan-1; ii>=0; ii--){ + pScan = &p->aScan[ii]; + if( pScan->addrExplain==addrExplain ) break; + pScan = 0; + } + if( pScan ){ + pScan->addrLoop = addrLoop; + pScan->addrVisit = addrVisit; + } } } -#endif +#endif /* defined(SQLITE_ENABLE_STMT_SCANSTATUS) */ /* @@ -83983,7 +84453,7 @@ SQLITE_PRIVATE VdbeOp *sqlite3VdbeGetOp(Vdbe *p, int addr){ /* Return the most recently added opcode */ -VdbeOp * sqlite3VdbeGetLastOp(Vdbe *p){ +SQLITE_PRIVATE VdbeOp *sqlite3VdbeGetLastOp(Vdbe *p){ return sqlite3VdbeGetOp(p, p->nOp - 1); } @@ -85687,6 +86157,8 @@ SQLITE_PRIVATE int sqlite3VdbeHalt(Vdbe *p){ db->flags &= ~(u64)SQLITE_DeferFKs; sqlite3CommitInternalChanges(db); } + }else if( p->rc==SQLITE_SCHEMA && db->nVdbeActive>1 ){ + p->nChange = 0; }else{ sqlite3RollbackAll(db, SQLITE_OK); p->nChange = 0; @@ -86005,9 +86477,9 @@ static void sqlite3VdbeClearObject(sqlite3 *db, Vdbe *p){ #ifdef SQLITE_ENABLE_NORMALIZE sqlite3DbFree(db, p->zNormSql); { - DblquoteStr *pThis, *pNext; - for(pThis=p->pDblStr; pThis; pThis=pNext){ - pNext = pThis->pNextStr; + DblquoteStr *pThis, *pNxt; + for(pThis=p->pDblStr; pThis; pThis=pNxt){ + pNxt = pThis->pNextStr; sqlite3DbFree(db, pThis); } } @@ -87634,6 +88106,20 @@ SQLITE_PRIVATE int sqlite3NotPureFunc(sqlite3_context *pCtx){ return 1; } +#if defined(SQLITE_ENABLE_CURSOR_HINTS) && defined(SQLITE_DEBUG) +/* +** This Walker callback is used to help verify that calls to +** sqlite3BtreeCursorHint() with opcode BTREE_HINT_RANGE have +** byte-code register values correctly initialized. +*/ +SQLITE_PRIVATE int sqlite3CursorRangeHintExprCheck(Walker *pWalker, Expr *pExpr){ + if( pExpr->op==TK_REGISTER ){ + assert( (pWalker->u.aMem[pExpr->iTable].flags & MEM_Undefined)==0 ); + } + return WRC_Continue; +} +#endif /* SQLITE_ENABLE_CURSOR_HINTS && SQLITE_DEBUG */ + #ifndef SQLITE_OMIT_VIRTUALTABLE /* ** Transfer error message text from an sqlite3_vtab.zErrMsg (text stored @@ -87696,6 +88182,16 @@ SQLITE_PRIVATE void sqlite3VdbePreUpdateHook( PreUpdate preupdate; const char *zTbl = pTab->zName; static const u8 fakeSortOrder = 0; +#ifdef SQLITE_DEBUG + int nRealCol; + if( pTab->tabFlags & TF_WithoutRowid ){ + nRealCol = sqlite3PrimaryKeyIndex(pTab)->nColumn; + }else if( pTab->tabFlags & TF_HasVirtual ){ + nRealCol = pTab->nNVCol; + }else{ + nRealCol = pTab->nCol; + } +#endif assert( db->pPreUpdate==0 ); memset(&preupdate, 0, sizeof(PreUpdate)); @@ -87712,8 +88208,8 @@ SQLITE_PRIVATE void sqlite3VdbePreUpdateHook( assert( pCsr!=0 ); assert( pCsr->eCurType==CURTYPE_BTREE ); - assert( pCsr->nField==pTab->nCol - || (pCsr->nField==pTab->nCol+1 && op==SQLITE_DELETE && iReg==-1) + assert( pCsr->nField==nRealCol + || (pCsr->nField==nRealCol+1 && op==SQLITE_DELETE && iReg==-1) ); preupdate.v = v; @@ -88020,7 +88516,7 @@ SQLITE_API int sqlite3_value_type(sqlite3_value* pVal){ SQLITE_NULL, /* 0x1f (not possible) */ SQLITE_FLOAT, /* 0x20 INTREAL */ SQLITE_NULL, /* 0x21 (not possible) */ - SQLITE_TEXT, /* 0x22 INTREAL + TEXT */ + SQLITE_FLOAT, /* 0x22 INTREAL + TEXT */ SQLITE_NULL, /* 0x23 (not possible) */ SQLITE_FLOAT, /* 0x24 (not possible) */ SQLITE_NULL, /* 0x25 (not possible) */ @@ -89086,9 +89582,9 @@ static const void *columnName( assert( db!=0 ); n = sqlite3_column_count(pStmt); if( N=0 ){ + u8 prior_mallocFailed = db->mallocFailed; N += useType*n; sqlite3_mutex_enter(db->mutex); - assert( db->mallocFailed==0 ); #ifndef SQLITE_OMIT_UTF16 if( useUtf16 ){ ret = sqlite3_value_text16((sqlite3_value*)&p->aColName[N]); @@ -89100,7 +89596,8 @@ static const void *columnName( /* A malloc may have failed inside of the _text() call. If this ** is the case, clear the mallocFailed flag and return NULL. */ - if( db->mallocFailed ){ + assert( db->mallocFailed==0 || db->mallocFailed==1 ); + if( db->mallocFailed > prior_mallocFailed ){ sqlite3OomClear(db); ret = 0; } @@ -89887,15 +90384,24 @@ SQLITE_API int sqlite3_stmt_scanstatus_v2( void *pOut /* OUT: Write the answer here */ ){ Vdbe *p = (Vdbe*)pStmt; - ScanStatus *pScan; + VdbeOp *aOp = p->aOp; + int nOp = p->nOp; + ScanStatus *pScan = 0; int idx; + if( p->pFrame ){ + VdbeFrame *pFrame; + for(pFrame=p->pFrame; pFrame->pParent; pFrame=pFrame->pParent); + aOp = pFrame->aOp; + nOp = pFrame->nOp; + } + if( iScan<0 ){ int ii; if( iScanStatusOp==SQLITE_SCANSTAT_NCYCLE ){ i64 res = 0; - for(ii=0; iinOp; ii++){ - res += p->aOp[ii].nCycle; + for(ii=0; iiaddrLoop>0 ){ - *(sqlite3_int64*)pOut = p->aOp[pScan->addrLoop].nExec; + *(sqlite3_int64*)pOut = aOp[pScan->addrLoop].nExec; }else{ *(sqlite3_int64*)pOut = -1; } @@ -89929,7 +90435,7 @@ SQLITE_API int sqlite3_stmt_scanstatus_v2( } case SQLITE_SCANSTAT_NVISIT: { if( pScan->addrVisit>0 ){ - *(sqlite3_int64*)pOut = p->aOp[pScan->addrVisit].nExec; + *(sqlite3_int64*)pOut = aOp[pScan->addrVisit].nExec; }else{ *(sqlite3_int64*)pOut = -1; } @@ -89951,7 +90457,7 @@ SQLITE_API int sqlite3_stmt_scanstatus_v2( } case SQLITE_SCANSTAT_EXPLAIN: { if( pScan->addrExplain ){ - *(const char**)pOut = p->aOp[ pScan->addrExplain ].p4.z; + *(const char**)pOut = aOp[ pScan->addrExplain ].p4.z; }else{ *(const char**)pOut = 0; } @@ -89959,7 +90465,7 @@ SQLITE_API int sqlite3_stmt_scanstatus_v2( } case SQLITE_SCANSTAT_SELECTID: { if( pScan->addrExplain ){ - *(int*)pOut = p->aOp[ pScan->addrExplain ].p1; + *(int*)pOut = aOp[ pScan->addrExplain ].p1; }else{ *(int*)pOut = -1; } @@ -89967,7 +90473,7 @@ SQLITE_API int sqlite3_stmt_scanstatus_v2( } case SQLITE_SCANSTAT_PARENTID: { if( pScan->addrExplain ){ - *(int*)pOut = p->aOp[ pScan->addrExplain ].p2; + *(int*)pOut = aOp[ pScan->addrExplain ].p2; }else{ *(int*)pOut = -1; } @@ -89985,18 +90491,18 @@ SQLITE_API int sqlite3_stmt_scanstatus_v2( if( iIns==0 ) break; if( iIns>0 ){ while( iIns<=iEnd ){ - res += p->aOp[iIns].nCycle; + res += aOp[iIns].nCycle; iIns++; } }else{ int iOp; - for(iOp=0; iOpnOp; iOp++){ - Op *pOp = &p->aOp[iOp]; + for(iOp=0; iOpp1!=iEnd ) continue; if( (sqlite3OpcodeProperty[pOp->opcode] & OPFLG_NCYCLE)==0 ){ continue; } - res += p->aOp[iOp].nCycle; + res += aOp[iOp].nCycle; } } } @@ -90919,7 +91425,10 @@ static u64 filterHash(const Mem *aMem, const Op *pOp){ }else if( p->flags & MEM_Real ){ h += sqlite3VdbeIntValue(p); }else if( p->flags & (MEM_Str|MEM_Blob) ){ - /* no-op */ + /* All strings have the same hash and all blobs have the same hash, + ** though, at least, those hashes are different from each other and + ** from NULL. */ + h += 4093 + (p->flags & (MEM_Str|MEM_Blob)); } } return h; @@ -90969,6 +91478,7 @@ SQLITE_PRIVATE int sqlite3VdbeExec( Mem *pOut = 0; /* Output operand */ #if defined(SQLITE_ENABLE_STMT_SCANSTATUS) || defined(VDBE_PROFILE) u64 *pnCycle = 0; + int bStmtScanStatus = IS_STMT_SCANSTATUS(db)!=0; #endif /*** INSERT STACK UNION HERE ***/ @@ -91033,13 +91543,17 @@ SQLITE_PRIVATE int sqlite3VdbeExec( assert( pOp>=aOp && pOp<&aOp[p->nOp]); nVmStep++; -#if defined(SQLITE_ENABLE_STMT_SCANSTATUS) || defined(VDBE_PROFILE) + +#if defined(VDBE_PROFILE) pOp->nExec++; pnCycle = &pOp->nCycle; -# ifdef VDBE_PROFILE - if( sqlite3NProfileCnt==0 ) -# endif + if( sqlite3NProfileCnt==0 ) *pnCycle -= sqlite3Hwtime(); +#elif defined(SQLITE_ENABLE_STMT_SCANSTATUS) + if( bStmtScanStatus ){ + pOp->nExec++; + pnCycle = &pOp->nCycle; *pnCycle -= sqlite3Hwtime(); + } #endif /* Only allow tracing if SQLITE_DEBUG is defined. @@ -92627,7 +93141,7 @@ case OP_Compare: { /* Opcode: Jump P1 P2 P3 * * ** ** Jump to the instruction at address P1, P2, or P3 depending on whether -** in the most recent OP_Compare instruction the P1 vector was less than +** in the most recent OP_Compare instruction the P1 vector was less than, ** equal to, or greater than the P2 vector, respectively. ** ** This opcode must immediately follow an OP_Compare opcode. @@ -92854,6 +93368,12 @@ case OP_IsNull: { /* same as TK_ISNULL, jump, in1 */ ** (0x01) bit. SQLITE_FLOAT is the 0x02 bit. SQLITE_TEXT is 0x04. ** SQLITE_BLOB is 0x08. SQLITE_NULL is 0x10. ** +** WARNING: This opcode does not reliably distinguish between NULL and REAL +** when P1>=0. If the database contains a NaN value, this opcode will think +** that the datatype is REAL when it should be NULL. When P1<0 and the value +** is already stored in register P3, then this opcode does reliably +** distinguish between NULL and REAL. The problem only arises then P1>=0. +** ** Take the jump to address P2 if and only if the datatype of the ** value determined by P1 and P3 corresponds to one of the bits in the ** P5 bitmask. @@ -92967,7 +93487,7 @@ case OP_IfNullRow: { /* jump */ VdbeCursor *pC; assert( pOp->p1>=0 && pOp->p1nCursor ); pC = p->apCsr[pOp->p1]; - if( ALWAYS(pC) && pC->nullRow ){ + if( pC && pC->nullRow ){ sqlite3VdbeMemSetNull(aMem + pOp->p3); goto jump_to_p2; } @@ -93462,7 +93982,7 @@ case OP_Affinity: { }else{ pIn1->u.r = (double)pIn1->u.i; pIn1->flags |= MEM_Real; - pIn1->flags &= ~MEM_Int; + pIn1->flags &= ~(MEM_Int|MEM_Str); } } REGISTER_TRACE((int)(pIn1-aMem), pIn1); @@ -95201,6 +95721,7 @@ case OP_SeekScan: { /* ncycle */ break; } nStep--; + pC->cacheStatus = CACHE_STALE; rc = sqlite3BtreeNext(pC->uc.pCursor, 0); if( rc ){ if( rc==SQLITE_DONE ){ @@ -97853,6 +98374,7 @@ case OP_AggFinal: { } sqlite3VdbeChangeEncoding(pMem, encoding); UPDATE_MAX_BLOBSIZE(pMem); + REGISTER_TRACE((int)(pMem-aMem), pMem); break; } @@ -98991,8 +99513,10 @@ default: { /* This is really OP_Noop, OP_Explain */ *pnCycle += sqlite3NProfileCnt ? sqlite3NProfileCnt : sqlite3Hwtime(); pnCycle = 0; #elif defined(SQLITE_ENABLE_STMT_SCANSTATUS) - *pnCycle += sqlite3Hwtime(); - pnCycle = 0; + if( pnCycle ){ + *pnCycle += sqlite3Hwtime(); + pnCycle = 0; + } #endif /* The following code adds nothing to the actual functionality @@ -99471,7 +99995,7 @@ blob_open_out: if( pBlob && pBlob->pStmt ) sqlite3VdbeFinalize((Vdbe *)pBlob->pStmt); sqlite3DbFree(db, pBlob); } - sqlite3ErrorWithMsg(db, rc, (zErr ? "%s" : 0), zErr); + sqlite3ErrorWithMsg(db, rc, (zErr ? "%s" : (char*)0), zErr); sqlite3DbFree(db, zErr); sqlite3ParseObjectReset(&sParse); rc = sqlite3ApiExit(db, rc); @@ -99630,7 +100154,7 @@ SQLITE_API int sqlite3_blob_reopen(sqlite3_blob *pBlob, sqlite3_int64 iRow){ ((Vdbe*)p->pStmt)->rc = SQLITE_OK; rc = blobSeekToRow(p, iRow, &zErr); if( rc!=SQLITE_OK ){ - sqlite3ErrorWithMsg(db, rc, (zErr ? "%s" : 0), zErr); + sqlite3ErrorWithMsg(db, rc, (zErr ? "%s" : (char*)0), zErr); sqlite3DbFree(db, zErr); } assert( rc!=SQLITE_SCHEMA ); @@ -104018,7 +104542,8 @@ static int lookupName( assert( op==TK_DELETE || op==TK_UPDATE || op==TK_INSERT ); if( pParse->bReturning ){ if( (pNC->ncFlags & NC_UBaseReg)!=0 - && (zTab==0 || sqlite3StrICmp(zTab,pParse->pTriggerTab->zName)==0) + && ALWAYS(zTab==0 + || sqlite3StrICmp(zTab,pParse->pTriggerTab->zName)==0) ){ pExpr->iTable = op!=TK_DELETE; pTab = pParse->pTriggerTab; @@ -105992,11 +106517,10 @@ SQLITE_PRIVATE CollSeq *sqlite3ExprCollSeq(Parse *pParse, const Expr *pExpr){ }else{ Expr *pNext = p->pRight; /* The Expr.x union is never used at the same time as Expr.pRight */ - assert( ExprUseXList(p) ); - assert( p->x.pList==0 || p->pRight==0 ); - if( p->x.pList!=0 && !db->mallocFailed ){ + assert( !ExprUseXList(p) || p->x.pList==0 || p->pRight==0 ); + if( ExprUseXList(p) && p->x.pList!=0 && !db->mallocFailed ){ int i; - for(i=0; ALWAYS(ix.pList->nExpr); i++){ + for(i=0; ix.pList->nExpr; i++){ if( ExprHasProperty(p->x.pList->a[i].pExpr, EP_Collate) ){ pNext = p->x.pList->a[i].pExpr; break; @@ -106828,9 +107352,9 @@ SQLITE_PRIVATE Select *sqlite3ExprListToValues(Parse *pParse, int nElem, ExprLis ** Join two expressions using an AND operator. If either expression is ** NULL, then just return the other expression. ** -** If one side or the other of the AND is known to be false, then instead -** of returning an AND expression, just return a constant expression with -** a value of false. +** If one side or the other of the AND is known to be false, and neither side +** is part of an ON clause, then instead of returning an AND expression, +** just return a constant expression with a value of false. */ SQLITE_PRIVATE Expr *sqlite3ExprAnd(Parse *pParse, Expr *pLeft, Expr *pRight){ sqlite3 *db = pParse->db; @@ -106838,14 +107362,17 @@ SQLITE_PRIVATE Expr *sqlite3ExprAnd(Parse *pParse, Expr *pLeft, Expr *pRight){ return pRight; }else if( pRight==0 ){ return pLeft; - }else if( (ExprAlwaysFalse(pLeft) || ExprAlwaysFalse(pRight)) - && !IN_RENAME_OBJECT - ){ - sqlite3ExprDeferredDelete(pParse, pLeft); - sqlite3ExprDeferredDelete(pParse, pRight); - return sqlite3Expr(db, TK_INTEGER, "0"); }else{ - return sqlite3PExpr(pParse, TK_AND, pLeft, pRight); + u32 f = pLeft->flags | pRight->flags; + if( (f&(EP_OuterON|EP_InnerON|EP_IsFalse))==EP_IsFalse + && !IN_RENAME_OBJECT + ){ + sqlite3ExprDeferredDelete(pParse, pLeft); + sqlite3ExprDeferredDelete(pParse, pRight); + return sqlite3Expr(db, TK_INTEGER, "0"); + }else{ + return sqlite3PExpr(pParse, TK_AND, pLeft, pRight); + } } } @@ -108090,12 +108617,17 @@ SQLITE_PRIVATE int sqlite3ExprIsTableConstant(Expr *p, int iCur){ } /* -** Check pExpr to see if it is an invariant constraint on data source pSrc. +** Check pExpr to see if it is an constraint on the single data source +** pSrc = &pSrcList->a[iSrc]. In other words, check to see if pExpr +** constrains pSrc but does not depend on any other tables or data +** sources anywhere else in the query. Return true (non-zero) if pExpr +** is a constraint on pSrc only. +** ** This is an optimization. False negatives will perhaps cause slower ** queries, but false positives will yield incorrect answers. So when in ** doubt, return 0. ** -** To be an invariant constraint, the following must be true: +** To be an single-source constraint, the following must be true: ** ** (1) pExpr cannot refer to any table other than pSrc->iCursor. ** @@ -108106,13 +108638,31 @@ SQLITE_PRIVATE int sqlite3ExprIsTableConstant(Expr *p, int iCur){ ** ** (4) If pSrc is the right operand of a LEFT JOIN, then... ** (4a) pExpr must come from an ON clause.. - (4b) and specifically the ON clause associated with the LEFT JOIN. +** (4b) and specifically the ON clause associated with the LEFT JOIN. ** ** (5) If pSrc is not the right operand of a LEFT JOIN or the left ** operand of a RIGHT JOIN, then pExpr must be from the WHERE ** clause, not an ON clause. +** +** (6) Either: +** +** (6a) pExpr does not originate in an ON or USING clause, or +** +** (6b) The ON or USING clause from which pExpr is derived is +** not to the left of a RIGHT JOIN (or FULL JOIN). +** +** Without this restriction, accepting pExpr as a single-table +** constraint might move the 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 (9) +** on push-down. */ -SQLITE_PRIVATE int sqlite3ExprIsTableConstraint(Expr *pExpr, const SrcItem *pSrc){ +SQLITE_PRIVATE int sqlite3ExprIsSingleTableConstraint( + Expr *pExpr, /* The constraint */ + const SrcList *pSrcList, /* Complete FROM clause */ + int iSrc /* Which element of pSrcList to use */ +){ + const SrcItem *pSrc = &pSrcList->a[iSrc]; if( pSrc->fg.jointype & JT_LTORJ ){ return 0; /* rule (3) */ } @@ -108122,6 +108672,19 @@ SQLITE_PRIVATE int sqlite3ExprIsTableConstraint(Expr *pExpr, const SrcItem *pSrc }else{ if( ExprHasProperty(pExpr, EP_OuterON) ) return 0; /* rule (5) */ } + if( ExprHasProperty(pExpr, EP_OuterON|EP_InnerON) /* (6a) */ + && (pSrcList->a[0].fg.jointype & JT_LTORJ)!=0 /* Fast pre-test of (6b) */ + ){ + int jj; + for(jj=0; jjw.iJoin==pSrcList->a[jj].iCursor ){ + if( (pSrcList->a[jj].fg.jointype & JT_LTORJ)!=0 ){ + return 0; /* restriction (6) */ + } + break; + } + } + } return sqlite3ExprIsTableConstant(pExpr, pSrc->iCursor); /* rules (1), (2) */ } @@ -108364,7 +108927,7 @@ SQLITE_PRIVATE int sqlite3IsRowid(const char *z){ ** pX is the RHS of an IN operator. If pX is a SELECT statement ** that can be simplified to a direct table access, then return ** a pointer to the SELECT statement. If pX is not a SELECT statement, -** or if the SELECT statement needs to be manifested into a transient +** or if the SELECT statement needs to be materialized into a transient ** table, then return NULL. */ #ifndef SQLITE_OMIT_SUBQUERY @@ -108650,7 +109213,6 @@ SQLITE_PRIVATE int sqlite3FindInIndex( CollSeq *pReq = sqlite3BinaryCompareCollSeq(pParse, pLhs, pRhs); int j; - assert( pReq!=0 || pRhs->iColumn==XN_ROWID || pParse->nErr ); for(j=0; jaiColumn[j]!=pRhs->iColumn ) continue; assert( pIdx->azColl[j] ); @@ -109936,7 +110498,19 @@ expr_code_doover: AggInfo *pAggInfo = pExpr->pAggInfo; struct AggInfo_col *pCol; assert( pAggInfo!=0 ); - assert( pExpr->iAgg>=0 && pExpr->iAggnColumn ); + assert( pExpr->iAgg>=0 ); + if( pExpr->iAgg>=pAggInfo->nColumn ){ + /* Happens when the left table of a RIGHT JOIN is null and + ** is using an expression index */ + sqlite3VdbeAddOp2(v, OP_Null, 0, target); +#ifdef SQLITE_VDBE_COVERAGE + /* Verify that the OP_Null above is exercised by tests + ** tag-20230325-2 */ + sqlite3VdbeAddOp2(v, OP_NotNull, target, 1); + VdbeCoverageNeverTaken(v); +#endif + break; + } pCol = &pAggInfo->aCol[pExpr->iAgg]; if( !pAggInfo->directMode ){ return AggInfoColumnReg(pAggInfo, pExpr->iAgg); @@ -110111,11 +110685,8 @@ expr_code_doover: #ifndef SQLITE_OMIT_CAST case TK_CAST: { /* Expressions of the form: CAST(pLeft AS token) */ - inReg = sqlite3ExprCodeTarget(pParse, pExpr->pLeft, target); - if( inReg!=target ){ - sqlite3VdbeAddOp2(v, OP_SCopy, inReg, target); - inReg = target; - } + sqlite3ExprCode(pParse, pExpr->pLeft, target); + assert( inReg==target ); assert( !ExprHasProperty(pExpr, EP_IntValue) ); sqlite3VdbeAddOp2(v, OP_Cast, target, sqlite3AffinityType(pExpr->u.zToken, 0)); @@ -110454,13 +111025,9 @@ expr_code_doover: ** Clear subtypes as subtypes may not cross a subquery boundary. */ assert( pExpr->pLeft ); - inReg = sqlite3ExprCodeTarget(pParse, pExpr->pLeft, target); - if( inReg!=target ){ - sqlite3VdbeAddOp2(v, OP_SCopy, inReg, target); - inReg = target; - } - sqlite3VdbeAddOp1(v, OP_ClrSubtype, inReg); - return inReg; + sqlite3ExprCode(pParse, pExpr->pLeft, target); + sqlite3VdbeAddOp1(v, OP_ClrSubtype, target); + return target; }else{ pExpr = pExpr->pLeft; goto expr_code_doover; /* 2018-04-28: Prevent deep recursion. */ @@ -110570,12 +111137,9 @@ expr_code_doover: ** "target" and not someplace else. */ pParse->okConstFactor = 0; /* note (1) above */ - inReg = sqlite3ExprCodeTarget(pParse, pExpr->pLeft, target); + sqlite3ExprCode(pParse, pExpr->pLeft, target); + assert( target==inReg ); pParse->okConstFactor = okConstFactor; - if( inReg!=target ){ /* note (2) above */ - sqlite3VdbeAddOp2(v, OP_SCopy, inReg, target); - inReg = target; - } sqlite3VdbeJumpHere(v, addrINR); break; } @@ -110813,7 +111377,9 @@ SQLITE_PRIVATE void sqlite3ExprCode(Parse *pParse, Expr *pExpr, int target){ inReg = sqlite3ExprCodeTarget(pParse, pExpr, target); if( inReg!=target ){ u8 op; - if( ALWAYS(pExpr) && ExprHasProperty(pExpr,EP_Subquery) ){ + if( ALWAYS(pExpr) + && (ExprHasProperty(pExpr,EP_Subquery) || pExpr->op==TK_REGISTER) + ){ op = OP_Copy; }else{ op = OP_SCopy; @@ -111998,9 +112564,11 @@ static int agginfoPersistExprCb(Walker *pWalker, Expr *pExpr){ int iAgg = pExpr->iAgg; Parse *pParse = pWalker->pParse; sqlite3 *db = pParse->db; + assert( iAgg>=0 ); if( pExpr->op!=TK_AGG_FUNCTION ){ - assert( iAgg>=0 && iAggnColumn ); - if( pAggInfo->aCol[iAgg].pCExpr==pExpr ){ + if( iAggnColumn + && pAggInfo->aCol[iAgg].pCExpr==pExpr + ){ pExpr = sqlite3ExprDup(db, pExpr, 0); if( pExpr ){ pAggInfo->aCol[iAgg].pCExpr = pExpr; @@ -112009,8 +112577,9 @@ static int agginfoPersistExprCb(Walker *pWalker, Expr *pExpr){ } }else{ assert( pExpr->op==TK_AGG_FUNCTION ); - assert( iAgg>=0 && iAggnFunc ); - if( pAggInfo->aFunc[iAgg].pFExpr==pExpr ){ + if( ALWAYS(iAggnFunc) + && pAggInfo->aFunc[iAgg].pFExpr==pExpr + ){ pExpr = sqlite3ExprDup(db, pExpr, 0); if( pExpr ){ pAggInfo->aFunc[iAgg].pFExpr = pExpr; @@ -112160,7 +112729,12 @@ static int analyzeAggregate(Walker *pWalker, Expr *pExpr){ } if( pIEpr==0 ) break; if( NEVER(!ExprUseYTab(pExpr)) ) break; - if( pExpr->pAggInfo!=0 ) break; /* Already resolved by outer context */ + for(i=0; inSrc; i++){ + if( pSrcList->a[0].iCursor==pIEpr->iDataCur ) break; + } + if( i>=pSrcList->nSrc ) break; + if( NEVER(pExpr->pAggInfo!=0) ) break; /* Resolved by outer context */ + if( pParse->nErr ){ return WRC_Abort; } /* If we reach this point, it means that expression pExpr can be ** translated into a reference to an index column as described by @@ -112171,6 +112745,9 @@ static int analyzeAggregate(Walker *pWalker, Expr *pExpr){ tmp.iTable = pIEpr->iIdxCur; tmp.iColumn = pIEpr->iIdxCol; findOrCreateAggInfoColumn(pParse, pAggInfo, &tmp); + if( pParse->nErr ){ return WRC_Abort; } + assert( pAggInfo->aCol!=0 ); + assert( tmp.iAggnColumn ); pAggInfo->aCol[tmp.iAgg].pCExpr = pExpr; pExpr->pAggInfo = pAggInfo; pExpr->iAgg = tmp.iAgg; @@ -112194,7 +112771,7 @@ static int analyzeAggregate(Walker *pWalker, Expr *pExpr){ } /* endif pExpr->iTable==pItem->iCursor */ } /* end loop over pSrcList */ } - return WRC_Prune; + return WRC_Continue; } case TK_AGG_FUNCTION: { if( (pNC->ncFlags & NC_InAggFunc)==0 @@ -112347,6 +112924,37 @@ SQLITE_PRIVATE void sqlite3ClearTempRegCache(Parse *pParse){ pParse->nRangeReg = 0; } +/* +** Make sure sufficient registers have been allocated so that +** iReg is a valid register number. +*/ +SQLITE_PRIVATE void sqlite3TouchRegister(Parse *pParse, int iReg){ + if( pParse->nMemnMem = iReg; +} + +#if defined(SQLITE_ENABLE_STAT4) || defined(SQLITE_DEBUG) +/* +** Return the latest reusable register in the set of all registers. +** The value returned is no less than iMin. If any register iMin or +** greater is in permanent use, then return one more than that last +** permanent register. +*/ +SQLITE_PRIVATE int sqlite3FirstAvailableRegister(Parse *pParse, int iMin){ + const ExprList *pList = pParse->pConstExpr; + if( pList ){ + int i; + for(i=0; inExpr; i++){ + if( pList->a[i].u.iConstExprReg>=iMin ){ + iMin = pList->a[i].u.iConstExprReg + 1; + } + } + } + pParse->nTempReg = 0; + pParse->nRangeReg = 0; + return iMin; +} +#endif /* SQLITE_ENABLE_STAT4 || SQLITE_DEBUG */ + /* ** Validate that no temporary register falls within the range of ** iFirst..iLast, inclusive. This routine is only call from within assert() @@ -112366,6 +112974,14 @@ SQLITE_PRIVATE int sqlite3NoTempsInRange(Parse *pParse, int iFirst, int iLast){ return 0; } } + if( pParse->pConstExpr ){ + ExprList *pList = pParse->pConstExpr; + for(i=0; inExpr; i++){ + int iReg = pList->a[i].u.iConstExprReg; + if( iReg==0 ) continue; + if( iReg>=iFirst && iReg<=iLast ) return 0; + } + } return 1; } #endif /* SQLITE_DEBUG */ @@ -113653,6 +114269,19 @@ static int renameEditSql( return rc; } +/* +** Set all pEList->a[].fg.eEName fields in the expression-list to val. +*/ +static void renameSetENames(ExprList *pEList, int val){ + if( pEList ){ + int i; + for(i=0; inExpr; i++){ + assert( val==ENAME_NAME || pEList->a[i].fg.eEName==ENAME_NAME ); + pEList->a[i].fg.eEName = val; + } + } +} + /* ** Resolve all symbols in the trigger at pParse->pNewTrigger, assuming ** it was read from the schema of database zDb. Return SQLITE_OK if @@ -113700,7 +114329,17 @@ static int renameResolveTrigger(Parse *pParse){ pSrc = 0; rc = SQLITE_NOMEM; }else{ + /* pStep->pExprList contains an expression-list used for an UPDATE + ** statement. So the a[].zEName values are the RHS of the + ** " = " clauses of the UPDATE statement. So, before + ** running SelectPrep(), change all the eEName values in + ** pStep->pExprList to ENAME_SPAN (from their current value of + ** ENAME_NAME). This is to prevent any ids in ON() clauses that are + ** part of pSrc from being incorrectly resolved against the + ** a[].zEName values as if they were column aliases. */ + renameSetENames(pStep->pExprList, ENAME_SPAN); sqlite3SelectPrep(pParse, pSel, 0); + renameSetENames(pStep->pExprList, ENAME_NAME); rc = pParse->nErr ? SQLITE_ERROR : SQLITE_OK; assert( pStep->pExprList==0 || pStep->pExprList==pSel->pEList ); assert( pSrc==pSel->pSrc ); @@ -115649,11 +116288,15 @@ static void analyzeOneTable( int regIdxname = iMem++; /* Register containing index name */ int regStat1 = iMem++; /* Value for the stat column of sqlite_stat1 */ int regPrev = iMem; /* MUST BE LAST (see below) */ +#ifdef SQLITE_ENABLE_STAT4 + int doOnce = 1; /* Flag for a one-time computation */ +#endif #ifdef SQLITE_ENABLE_PREUPDATE_HOOK Table *pStat1 = 0; #endif - pParse->nMem = MAX(pParse->nMem, iMem); + sqlite3TouchRegister(pParse, iMem); + assert( sqlite3NoTempsInRange(pParse, regNewRowid, iMem) ); v = sqlite3GetVdbe(pParse); if( v==0 || NEVER(pTab==0) ){ return; @@ -115759,7 +116402,7 @@ static void analyzeOneTable( ** the regPrev array and a trailing rowid (the rowid slot is required ** when building a record to insert into the sample column of ** the sqlite_stat4 table. */ - pParse->nMem = MAX(pParse->nMem, regPrev+nColTest); + sqlite3TouchRegister(pParse, regPrev+nColTest); /* Open a read-only cursor on the index being analyzed. */ assert( iDb==sqlite3SchemaToIndex(db, pIdx->pSchema) ); @@ -115931,7 +116574,35 @@ static void analyzeOneTable( int addrIsNull; u8 seekOp = HasRowid(pTab) ? OP_NotExists : OP_NotFound; - pParse->nMem = MAX(pParse->nMem, regCol+nCol); + if( doOnce ){ + int mxCol = nCol; + Index *pX; + + /* Compute the maximum number of columns in any index */ + for(pX=pTab->pIndex; pX; pX=pX->pNext){ + int nColX; /* Number of columns in pX */ + if( !HasRowid(pTab) && IsPrimaryKeyIndex(pX) ){ + nColX = pX->nKeyCol; + }else{ + nColX = pX->nColumn; + } + if( nColX>mxCol ) mxCol = nColX; + } + + /* Allocate space to compute results for the largest index */ + sqlite3TouchRegister(pParse, regCol+mxCol); + doOnce = 0; +#ifdef SQLITE_DEBUG + /* Verify that the call to sqlite3ClearTempRegCache() below + ** really is needed. + ** https://sqlite.org/forum/forumpost/83cb4a95a0 (2023-03-25) + */ + testcase( !sqlite3NoTempsInRange(pParse, regEq, regCol+mxCol) ); +#endif + sqlite3ClearTempRegCache(pParse); /* tag-20230325-1 */ + assert( sqlite3NoTempsInRange(pParse, regEq, regCol+mxCol) ); + } + assert( sqlite3NoTempsInRange(pParse, regEq, regCol+nCol) ); addrNext = sqlite3VdbeCurrentAddr(v); callStatGet(pParse, regStat, STAT_GET_ROWID, regSampleRowid); @@ -116012,6 +116683,11 @@ static void analyzeDatabase(Parse *pParse, int iDb){ for(k=sqliteHashFirst(&pSchema->tblHash); k; k=sqliteHashNext(k)){ Table *pTab = (Table*)sqliteHashData(k); analyzeOneTable(pParse, pTab, 0, iStatCur, iMem, iTab); +#ifdef SQLITE_ENABLE_STAT4 + iMem = sqlite3FirstAvailableRegister(pParse, iMem); +#else + assert( iMem==sqlite3FirstAvailableRegister(pParse,iMem) ); +#endif } loadAnalysis(pParse, iDb); } @@ -116399,6 +117075,10 @@ static int loadStatTbl( pIdx = findIndexOrPrimaryKey(db, zIndex, zDb); assert( pIdx==0 || pIdx->nSample==0 ); if( pIdx==0 ) continue; + if( pIdx->aSample!=0 ){ + /* The same index appears in sqlite_stat4 under multiple names */ + continue; + } assert( !HasRowid(pIdx->pTable) || pIdx->nColumn==pIdx->nKeyCol+1 ); if( !HasRowid(pIdx->pTable) && IsPrimaryKeyIndex(pIdx) ){ nIdxCol = pIdx->nKeyCol; @@ -116406,6 +117086,7 @@ static int loadStatTbl( nIdxCol = pIdx->nColumn; } pIdx->nSampleCol = nIdxCol; + pIdx->mxSample = nSample; nByte = sizeof(IndexSample) * nSample; nByte += sizeof(tRowcnt) * nIdxCol * 3 * nSample; nByte += nIdxCol * sizeof(tRowcnt); /* Space for Index.aAvgEq[] */ @@ -116445,6 +117126,11 @@ static int loadStatTbl( if( zIndex==0 ) continue; pIdx = findIndexOrPrimaryKey(db, zIndex, zDb); if( pIdx==0 ) continue; + if( pIdx->nSample>=pIdx->mxSample ){ + /* Too many slots used because the same index appears in + ** sqlite_stat4 using multiple names */ + continue; + } /* This next condition is true if data has already been loaded from ** the sqlite_stat4 table. */ nCol = pIdx->nSampleCol; @@ -116488,11 +117174,12 @@ static int loadStat4(sqlite3 *db, const char *zDb){ const Table *pStat4; assert( db->lookaside.bDisable ); - if( (pStat4 = sqlite3FindTable(db, "sqlite_stat4", zDb))!=0 + if( OptimizationEnabled(db, SQLITE_Stat4) + && (pStat4 = sqlite3FindTable(db, "sqlite_stat4", zDb))!=0 && IsOrdinaryTable(pStat4) ){ rc = loadStatTbl(db, - "SELECT idx,count(*) FROM %Q.sqlite_stat4 GROUP BY idx", + "SELECT idx,count(*) FROM %Q.sqlite_stat4 GROUP BY idx COLLATE nocase", "SELECT idx,neq,nlt,ndlt,sample FROM %Q.sqlite_stat4", zDb ); @@ -118336,7 +119023,7 @@ static void SQLITE_NOINLINE deleteTable(sqlite3 *db, Table *pTable){ if( IsOrdinaryTable(pTable) ){ sqlite3FkDelete(db, pTable); } -#ifndef SQLITE_OMIT_VIRTUAL_TABLE +#ifndef SQLITE_OMIT_VIRTUALTABLE else if( IsVirtual(pTable) ){ sqlite3VtabClear(db, pTable); } @@ -123368,6 +124055,7 @@ SQLITE_PRIVATE void sqlite3SetTextEncoding(sqlite3 *db, u8 enc){ ** strings is BINARY. */ db->pDfltColl = sqlite3FindCollSeq(db, enc, sqlite3StrBINARY, 0); + sqlite3ExpirePreparedStatements(db, 1); } /* @@ -123839,13 +124527,15 @@ static int tabIsReadOnly(Parse *pParse, Table *pTab){ ** If pTab is writable but other errors have occurred -> return 1. ** If pTab is writable and no prior errors -> return 0; */ -SQLITE_PRIVATE int sqlite3IsReadOnly(Parse *pParse, Table *pTab, int viewOk){ +SQLITE_PRIVATE int sqlite3IsReadOnly(Parse *pParse, Table *pTab, Trigger *pTrigger){ if( tabIsReadOnly(pParse, pTab) ){ sqlite3ErrorMsg(pParse, "table %s may not be modified", pTab->zName); return 1; } #ifndef SQLITE_OMIT_VIEW - if( !viewOk && IsView(pTab) ){ + if( IsView(pTab) + && (pTrigger==0 || (pTrigger->bReturning && pTrigger->pNext==0)) + ){ sqlite3ErrorMsg(pParse,"cannot modify %s because it is a view",pTab->zName); return 1; } @@ -124099,7 +124789,7 @@ SQLITE_PRIVATE void sqlite3DeleteFrom( goto delete_from_cleanup; } - if( sqlite3IsReadOnly(pParse, pTab, (pTrigger?1:0)) ){ + if( sqlite3IsReadOnly(pParse, pTab, pTrigger) ){ goto delete_from_cleanup; } iDb = sqlite3SchemaToIndex(db, pTab->pSchema); @@ -126262,7 +126952,7 @@ static void trimFunc( /* ** The "unknown" function is automatically substituted in place of ** any unrecognized function name when doing an EXPLAIN or EXPLAIN QUERY PLAN -** when the SQLITE_ENABLE_UNKNOWN_FUNCTION compile-time option is used. +** when the SQLITE_ENABLE_UNKNOWN_SQL_FUNCTION compile-time option is used. ** When the "sqlite3" command-line shell is built using this functionality, ** that allows an EXPLAIN or EXPLAIN QUERY PLAN for complex queries ** involving application-defined functions to be examined in a generic @@ -128565,22 +129255,22 @@ static Trigger *fkActionTrigger( if( action==OE_Restrict ){ int iDb = sqlite3SchemaToIndex(db, pTab->pSchema); - Token tFrom; - Token tDb; + SrcList *pSrc; Expr *pRaise; - tFrom.z = zFrom; - tFrom.n = nFrom; - tDb.z = db->aDb[iDb].zDbSName; - tDb.n = sqlite3Strlen30(tDb.z); - pRaise = sqlite3Expr(db, TK_RAISE, "FOREIGN KEY constraint failed"); if( pRaise ){ pRaise->affExpr = OE_Abort; } + pSrc = sqlite3SrcListAppend(pParse, 0, 0, 0); + if( pSrc ){ + assert( pSrc->nSrc==1 ); + pSrc->a[0].zName = sqlite3DbStrDup(db, zFrom); + pSrc->a[0].zDatabase = sqlite3DbStrDup(db, db->aDb[iDb].zDbSName); + } pSelect = sqlite3SelectNew(pParse, sqlite3ExprListAppend(pParse, 0, pRaise), - sqlite3SrcListAppend(pParse, 0, &tDb, &tFrom), + pSrc, pWhere, 0, 0, 0, 0, 0 ); @@ -128796,45 +129486,47 @@ SQLITE_PRIVATE void sqlite3OpenTable( ** is managed along with the rest of the Index structure. It will be ** released when sqlite3DeleteIndex() is called. */ -SQLITE_PRIVATE const char *sqlite3IndexAffinityStr(sqlite3 *db, Index *pIdx){ +static SQLITE_NOINLINE const char *computeIndexAffStr(sqlite3 *db, Index *pIdx){ + /* The first time a column affinity string for a particular index is + ** required, it is allocated and populated here. It is then stored as + ** a member of the Index structure for subsequent use. + ** + ** The column affinity string will eventually be deleted by + ** sqliteDeleteIndex() when the Index structure itself is cleaned + ** up. + */ + int n; + Table *pTab = pIdx->pTable; + pIdx->zColAff = (char *)sqlite3DbMallocRaw(0, pIdx->nColumn+1); if( !pIdx->zColAff ){ - /* The first time a column affinity string for a particular index is - ** required, it is allocated and populated here. It is then stored as - ** a member of the Index structure for subsequent use. - ** - ** The column affinity string will eventually be deleted by - ** sqliteDeleteIndex() when the Index structure itself is cleaned - ** up. - */ - int n; - Table *pTab = pIdx->pTable; - pIdx->zColAff = (char *)sqlite3DbMallocRaw(0, pIdx->nColumn+1); - if( !pIdx->zColAff ){ - sqlite3OomFault(db); - return 0; - } - for(n=0; nnColumn; n++){ - i16 x = pIdx->aiColumn[n]; - char aff; - if( x>=0 ){ - aff = pTab->aCol[x].affinity; - }else if( x==XN_ROWID ){ - aff = SQLITE_AFF_INTEGER; - }else{ - assert( x==XN_EXPR ); - assert( pIdx->bHasExpr ); - assert( pIdx->aColExpr!=0 ); - aff = sqlite3ExprAffinity(pIdx->aColExpr->a[n].pExpr); - } - if( affSQLITE_AFF_NUMERIC) aff = SQLITE_AFF_NUMERIC; - pIdx->zColAff[n] = aff; - } - pIdx->zColAff[n] = 0; + sqlite3OomFault(db); + return 0; } - + for(n=0; nnColumn; n++){ + i16 x = pIdx->aiColumn[n]; + char aff; + if( x>=0 ){ + aff = pTab->aCol[x].affinity; + }else if( x==XN_ROWID ){ + aff = SQLITE_AFF_INTEGER; + }else{ + assert( x==XN_EXPR ); + assert( pIdx->bHasExpr ); + assert( pIdx->aColExpr!=0 ); + aff = sqlite3ExprAffinity(pIdx->aColExpr->a[n].pExpr); + } + if( affSQLITE_AFF_NUMERIC) aff = SQLITE_AFF_NUMERIC; + pIdx->zColAff[n] = aff; + } + pIdx->zColAff[n] = 0; return pIdx->zColAff; } +SQLITE_PRIVATE const char *sqlite3IndexAffinityStr(sqlite3 *db, Index *pIdx){ + if( !pIdx->zColAff ) return computeIndexAffStr(db, pIdx); + return pIdx->zColAff; +} + /* ** Compute an affinity string for a table. Space is obtained @@ -129520,7 +130212,7 @@ SQLITE_PRIVATE void sqlite3Insert( /* Cannot insert into a read-only table. */ - if( sqlite3IsReadOnly(pParse, pTab, tmask) ){ + if( sqlite3IsReadOnly(pParse, pTab, pTrigger) ){ goto insert_cleanup; } @@ -129967,7 +130659,7 @@ SQLITE_PRIVATE void sqlite3Insert( } /* Copy the new data already generated. */ - assert( pTab->nNVCol>0 ); + assert( pTab->nNVCol>0 || pParse->nErr>0 ); sqlite3VdbeAddOp3(v, OP_Copy, regRowid+1, regCols+1, pTab->nNVCol-1); #ifndef SQLITE_OMIT_GENERATED_COLUMNS @@ -133330,7 +134022,11 @@ static int sqlite3LoadExtension( /* tag-20210611-1. Some dlopen() implementations will segfault if given ** an oversize filename. Most filesystems have a pathname limit of 4K, ** so limit the extension filename length to about twice that. - ** https://sqlite.org/forum/forumpost/08a0d6d9bf */ + ** https://sqlite.org/forum/forumpost/08a0d6d9bf + ** + ** Later (2023-03-25): Save an extra 6 bytes for the filename suffix. + ** See https://sqlite.org/forum/forumpost/24083b579d. + */ if( nMsg>SQLITE_MAX_PATHLEN ) goto extension_not_found; handle = sqlite3OsDlOpen(pVfs, zFile); @@ -133338,7 +134034,9 @@ static int sqlite3LoadExtension( for(ii=0; iiaDb[iDb].zDbSName; sqlite3CodeVerifySchema(pParse, iDb); sqlite3TableLock(pParse, iDb, pTab->tnum, 0, pTab->zName); - if( pTab->nCol+regRow>pParse->nMem ) pParse->nMem = pTab->nCol + regRow; + sqlite3TouchRegister(pParse, pTab->nCol+regRow); sqlite3OpenTable(pParse, 0, iDb, pTab, OP_OpenRead); sqlite3VdbeLoadString(v, regResult, pTab->zName); assert( IsOrdinaryTable(pTab) ); @@ -135874,7 +136572,7 @@ SQLITE_PRIVATE void sqlite3Pragma( ** regRow..regRow+n. If any of the child key values are NULL, this ** row cannot cause an FK violation. Jump directly to addrOk in ** this case. */ - if( regRow+pFK->nCol>pParse->nMem ) pParse->nMem = regRow+pFK->nCol; + sqlite3TouchRegister(pParse, regRow + pFK->nCol); for(j=0; jnCol; j++){ int iCol = aiCols ? aiCols[j] : pFK->aCol[j].iFrom; sqlite3ExprCodeGetColumnOfTable(v, pTab, 0, iCol, regRow+j); @@ -136003,6 +136701,7 @@ SQLITE_PRIVATE void sqlite3Pragma( if( iDb>=0 && i!=iDb ) continue; sqlite3CodeVerifySchema(pParse, i); + pParse->okConstFactor = 0; /* tag-20230327-1 */ /* Do an integrity check of the B-Tree ** @@ -136038,7 +136737,7 @@ SQLITE_PRIVATE void sqlite3Pragma( aRoot[0] = cnt; /* Make sure sufficient number of registers have been allocated */ - pParse->nMem = MAX( pParse->nMem, 8+mxIdx ); + sqlite3TouchRegister(pParse, 8+mxIdx); sqlite3ClearTempRegCache(pParse); /* Do the b-tree integrity checks */ @@ -136188,15 +136887,29 @@ SQLITE_PRIVATE void sqlite3Pragma( labelOk = sqlite3VdbeMakeLabel(pParse); if( pCol->notNull ){ /* (1) NOT NULL columns may not contain a NULL */ + int jmp3; int jmp2 = sqlite3VdbeAddOp4Int(v, OP_IsType, p1, labelOk, p3, p4); - sqlite3VdbeChangeP5(v, 0x0f); VdbeCoverage(v); + if( p1<0 ){ + sqlite3VdbeChangeP5(v, 0x0f); /* INT, REAL, TEXT, or BLOB */ + jmp3 = jmp2; + }else{ + sqlite3VdbeChangeP5(v, 0x0d); /* INT, TEXT, or BLOB */ + /* OP_IsType does not detect NaN values in the database file + ** which should be treated as a NULL. So if the header type + ** is REAL, we have to load the actual data using OP_Column + ** to reliably determine if the value is a NULL. */ + sqlite3VdbeAddOp3(v, OP_Column, p1, p3, 3); + jmp3 = sqlite3VdbeAddOp2(v, OP_NotNull, 3, labelOk); + VdbeCoverage(v); + } zErr = sqlite3MPrintf(db, "NULL value in %s.%s", pTab->zName, pCol->zCnName); sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0, zErr, P4_DYNAMIC); if( doTypeCheck ){ sqlite3VdbeGoto(v, labelError); sqlite3VdbeJumpHere(v, jmp2); + sqlite3VdbeJumpHere(v, jmp3); }else{ /* VDBE byte code will fall thru */ } @@ -136304,7 +137017,7 @@ SQLITE_PRIVATE void sqlite3Pragma( int jmp7; sqlite3VdbeAddOp2(v, OP_IdxRowid, iIdxCur+j, 3); jmp7 = sqlite3VdbeAddOp3(v, OP_Eq, 3, 0, r1+pIdx->nColumn-1); - VdbeCoverage(v); + VdbeCoverageNeverNull(v); sqlite3VdbeLoadString(v, 3, "rowid not at end-of-record for row "); sqlite3VdbeAddOp3(v, OP_Concat, 7, 3, 3); @@ -137510,7 +138223,9 @@ SQLITE_PRIVATE int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg, u32 mFl #else encoding = SQLITE_UTF8; #endif - if( db->nVdbeActive>0 && encoding!=ENC(db) ){ + if( db->nVdbeActive>0 && encoding!=ENC(db) + && (db->mDbFlags & DBFLAG_Vacuum)==0 + ){ rc = SQLITE_LOCKED; goto initone_error_out; }else{ @@ -137904,7 +138619,11 @@ static int sqlite3Prepare( sParse.db = db; sParse.pReprepare = pReprepare; assert( ppStmt && *ppStmt==0 ); - if( db->mallocFailed ) sqlite3ErrorMsg(&sParse, "out of memory"); + if( db->mallocFailed ){ + sqlite3ErrorMsg(&sParse, "out of memory"); + db->errCode = rc = SQLITE_NOMEM; + goto end_prepare; + } assert( sqlite3_mutex_held(db->mutex) ); /* For a long-term use prepared statement avoid the use of @@ -138993,7 +139712,7 @@ static void pushOntoSorter( ** (2) All output columns are included in the sort record. In that ** case regData==regOrigData. ** (3) Some output columns are omitted from the sort record due to - ** the SQLITE_ENABLE_SORTER_REFERENCE optimization, or due to the + ** the SQLITE_ENABLE_SORTER_REFERENCES optimization, or due to the ** SQLITE_ECEL_OMITREF optimization, or due to the ** SortCtx.pDeferredRowLoad optimiation. In any of these cases ** regOrigData is 0 to prevent this routine from trying to copy @@ -140594,7 +141313,7 @@ SQLITE_PRIVATE void sqlite3SubqueryColumnTypes( 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 ) return; + if( db->mallocFailed || IN_RENAME_OBJECT ) return; while( pSelect->pPrior ) pSelect = pSelect->pPrior; a = pSelect->pEList->a; memset(&sNC, 0, sizeof(sNC)); @@ -140639,18 +141358,16 @@ SQLITE_PRIVATE void sqlite3SubqueryColumnTypes( break; } } - } - } - if( zType ){ - i64 m = sqlite3Strlen30(zType); - n = sqlite3Strlen30(pCol->zCnName); - pCol->zCnName = sqlite3DbReallocOrFree(db, pCol->zCnName, n+m+2); - if( pCol->zCnName ){ - memcpy(&pCol->zCnName[n+1], zType, m+1); - pCol->colFlags |= COLFLAG_HASTYPE; - }else{ - testcase( pCol->colFlags & COLFLAG_HASTYPE ); - pCol->colFlags &= ~(COLFLAG_HASTYPE|COLFLAG_HASCOLL); + } + } + if( zType ){ + i64 m = sqlite3Strlen30(zType); + n = sqlite3Strlen30(pCol->zCnName); + pCol->zCnName = sqlite3DbReallocOrFree(db, pCol->zCnName, n+m+2); + pCol->colFlags &= ~(COLFLAG_HASTYPE|COLFLAG_HASCOLL); + if( pCol->zCnName ){ + memcpy(&pCol->zCnName[n+1], zType, m+1); + pCol->colFlags |= COLFLAG_HASTYPE; } } pColl = sqlite3ExprCollSeq(pParse, p); @@ -142517,8 +143234,7 @@ static int compoundHasDifferentAffinities(Select *p){ ** query or there are no RIGHT or FULL JOINs in any arm ** of the subquery. (This is a duplicate of condition (27b).) ** (17h) The corresponding result set expressions in all arms of the -** compound must have the same affinity. (See restriction (9) -** on the push-down optimization.) +** compound must have the same affinity. ** ** The parent and sub-query may contain WHERE clauses. Subject to ** rules (11), (13) and (14), they may also contain ORDER BY, @@ -143386,10 +144102,24 @@ static int pushDownWindowCheck(Parse *pParse, Select *pSubq, Expr *pExpr){ ** or EXCEPT, then all of the result set columns for all arms of ** the compound must use the BINARY collating sequence. ** -** (9) If the subquery is a compound, then all arms of the compound must -** have the same affinity. (This is the same as restriction (17h) -** for query flattening.) +** (9) All three of the following are true: ** +** (9a) The WHERE clause expression originates in the ON or USING clause +** of a join (either an INNER or an OUTER join), and +** +** (9b) The subquery is to the right of the ON/USING clause +** +** (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(). +** +** (10) The inner query is not the right-hand table of a RIGHT JOIN. +** +** (11) The subquery is not a VALUES clause ** ** Return 0 if no changes are made and non-zero if one or more WHERE clause ** terms are duplicated into the subquery. @@ -143398,13 +144128,20 @@ static int pushDownWhereTerms( Parse *pParse, /* Parse context (for malloc() and error reporting) */ Select *pSubq, /* The subquery whose WHERE clause is to be augmented */ Expr *pWhere, /* The WHERE clause of the outer query */ - SrcItem *pSrc /* The subquery term of the outer FROM clause */ + SrcList *pSrcList, /* The complete from clause of the outer query */ + int iSrc /* Which FROM clause term to try to push into */ ){ Expr *pNew; + SrcItem *pSrc; /* The subquery FROM term into which WHERE is pushed */ int nChng = 0; + pSrc = &pSrcList->a[iSrc]; if( pWhere==0 ) return 0; - if( pSubq->selFlags & (SF_Recursive|SF_MultiPart) ) return 0; - if( pSrc->fg.jointype & (JT_LTORJ|JT_RIGHT) ) return 0; + if( pSubq->selFlags & (SF_Recursive|SF_MultiPart) ){ + return 0; /* restrictions (2) and (11) */ + } + if( pSrc->fg.jointype & (JT_LTORJ|JT_RIGHT) ){ + return 0; /* restrictions (10) */ + } if( pSubq->pPrior ){ Select *pSel; @@ -143420,9 +144157,6 @@ static int pushDownWhereTerms( if( pSel->pWin ) return 0; /* restriction (6b) */ #endif } - if( compoundHasDifferentAffinities(pSubq) ){ - return 0; /* restriction (9) */ - } if( notUnionAll ){ /* If any of the compound arms are connected using UNION, INTERSECT, ** or EXCEPT, then we must ensure that none of the columns use a @@ -143462,11 +144196,28 @@ static int pushDownWhereTerms( return 0; /* restriction (3) */ } while( pWhere->op==TK_AND ){ - nChng += pushDownWhereTerms(pParse, pSubq, pWhere->pRight, pSrc); + nChng += pushDownWhereTerms(pParse, pSubq, pWhere->pRight, pSrcList, iSrc); pWhere = pWhere->pLeft; } -#if 0 /* Legacy code. Checks now done by sqlite3ExprIsTableConstraint() */ +#if 0 /* These checks now done by sqlite3ExprIsSingleTableConstraint() */ + if( ExprHasProperty(pWhere, EP_OuterON|EP_InnerON) /* (9a) */ + && (pSrcList->a[0].fg.jointype & JT_LTORJ)!=0 /* Fast pre-test of (9c) */ + ){ + int jj; + for(jj=0; jjw.iJoin==pSrcList->a[jj].iCursor ){ + /* If we reach this point, both (9a) and (9b) are satisfied. + ** The following loop checks (9c): + */ + for(jj++; jja[jj].fg.jointype & JT_RIGHT)!=0 ){ + return 0; /* restriction (9) */ + } + } + } + } + } if( isLeftJoin && (ExprHasProperty(pWhere,EP_OuterON)==0 || pWhere->w.iJoin!=iCursor) @@ -143480,7 +144231,7 @@ static int pushDownWhereTerms( } #endif - if( sqlite3ExprIsTableConstraint(pWhere, pSrc) ){ + if( sqlite3ExprIsSingleTableConstraint(pWhere, pSrcList, iSrc) ){ nChng++; pSubq->selFlags |= SF_PushDown; while( pSubq ){ @@ -143514,6 +144265,78 @@ static int pushDownWhereTerms( } #endif /* !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) */ +/* +** Check to see if a subquery contains result-set columns that are +** never used. If it does, change the value of those result-set columns +** to NULL so that they do not cause unnecessary work to compute. +** +** Return the number of column that were changed to NULL. +*/ +static int disableUnusedSubqueryResultColumns(SrcItem *pItem){ + int nCol; + Select *pSub; /* The subquery to be simplified */ + Select *pX; /* For looping over compound elements of pSub */ + Table *pTab; /* The table that describes the subquery */ + int j; /* Column number */ + int nChng = 0; /* Number of columns converted to NULL */ + Bitmask colUsed; /* Columns that may not be NULLed out */ + + assert( pItem!=0 ); + if( pItem->fg.isCorrelated || pItem->fg.isCte ){ + return 0; + } + assert( pItem->pTab!=0 ); + pTab = pItem->pTab; + assert( pItem->pSelect!=0 ); + pSub = pItem->pSelect; + assert( pSub->pEList->nExpr==pTab->nCol ); + if( (pSub->selFlags & (SF_Distinct|SF_Aggregate))!=0 ){ + testcase( pSub->selFlags & SF_Distinct ); + testcase( pSub->selFlags & SF_Aggregate ); + return 0; + } + for(pX=pSub; pX; pX=pX->pPrior){ + if( pX->pPrior && pX->op!=TK_ALL ){ + /* This optimization does not work for compound subqueries that + ** use UNION, INTERSECT, or EXCEPT. Only UNION ALL is allowed. */ + return 0; + } +#ifndef SQLITE_OMIT_WINDOWFUNC + if( pX->pWin ){ + /* This optimization does not work for subqueries that use window + ** functions. */ + return 0; + } +#endif + } + colUsed = pItem->colUsed; + if( pSub->pOrderBy ){ + ExprList *pList = pSub->pOrderBy; + for(j=0; jnExpr; j++){ + u16 iCol = pList->a[j].u.x.iOrderByCol; + if( iCol>0 ){ + iCol--; + colUsed |= ((Bitmask)1)<<(iCol>=BMS ? BMS-1 : iCol); + } + } + } + nCol = pTab->nCol; + for(j=0; jpPrior) { + Expr *pY = pX->pEList->a[j].pExpr; + if( pY->op==TK_NULL ) continue; + pY->op = TK_NULL; + ExprClearProperty(pY, EP_Skip|EP_Unlikely); + pX->selFlags |= SF_PushDown; + nChng++; + } + } + return nChng; +} + + /* ** The pFunc is the only aggregate function in the query. Check to see ** if the query is a candidate for the min/max optimization. @@ -144660,12 +145483,13 @@ static void optimizeAggregateUseOfIndexedExpr( assert( pSelect->pGroupBy!=0 ); pAggInfo->nColumn = pAggInfo->nAccumulator; if( ALWAYS(pAggInfo->nSortingColumn>0) ){ - if( pAggInfo->nColumn==0 ){ - pAggInfo->nSortingColumn = pSelect->pGroupBy->nExpr; - }else{ - pAggInfo->nSortingColumn = - pAggInfo->aCol[pAggInfo->nColumn-1].iSorterColumn+1; + int mx = pSelect->pGroupBy->nExpr - 1; + int j, k; + for(j=0; jnColumn; j++){ + k = pAggInfo->aCol[j].iSorterColumn; + if( k>mx ) mx = k; } + pAggInfo->nSortingColumn = mx+1; } analyzeAggFuncArgs(pAggInfo, pNC); #if TREETRACE_ENABLED @@ -144699,11 +145523,13 @@ static int aggregateIdxEprRefToColCallback(Walker *pWalker, Expr *pExpr){ if( pExpr->op==TK_AGG_FUNCTION ) return WRC_Continue; if( pExpr->op==TK_IF_NULL_ROW ) return WRC_Continue; pAggInfo = pExpr->pAggInfo; - assert( pExpr->iAgg>=0 && pExpr->iAggnColumn ); + if( NEVER(pExpr->iAgg>=pAggInfo->nColumn) ) return WRC_Continue; + assert( pExpr->iAgg>=0 ); pCol = &pAggInfo->aCol[pExpr->iAgg]; pExpr->op = TK_AGG_COLUMN; pExpr->iTable = pCol->iTable; pExpr->iColumn = pCol->iColumn; + ExprClearProperty(pExpr, EP_Skip|EP_Collate); return WRC_Prune; } @@ -145057,7 +145883,6 @@ static void agginfoFree(sqlite3 *db, AggInfo *p){ sqlite3DbFreeNN(db, p); } -#ifdef SQLITE_COUNTOFVIEW_OPTIMIZATION /* ** Attempt to transform a query of the form ** @@ -145085,6 +145910,7 @@ static int countOfViewOptimization(Parse *pParse, Select *p){ if( (p->selFlags & SF_Aggregate)==0 ) return 0; /* This is an aggregate */ if( p->pEList->nExpr!=1 ) return 0; /* Single result column */ if( p->pWhere ) return 0; + if( p->pHaving ) return 0; if( p->pGroupBy ) return 0; if( p->pOrderBy ) return 0; pExpr = p->pEList->a[0].pExpr; @@ -145104,7 +145930,8 @@ static int countOfViewOptimization(Parse *pParse, Select *p){ if( pSub->pWhere ) return 0; /* No WHERE clause */ if( pSub->pLimit ) return 0; /* No LIMIT clause */ if( pSub->selFlags & SF_Aggregate ) return 0; /* Not an aggregate */ - pSub = pSub->pPrior; /* Repeat over compound */ + assert( pSub->pHaving==0 ); /* Due to the previous */ + pSub = pSub->pPrior; /* Repeat over compound */ }while( pSub ); /* If we reach this point then it is OK to perform the transformation */ @@ -145147,7 +145974,6 @@ static int countOfViewOptimization(Parse *pParse, Select *p){ #endif return 1; } -#endif /* SQLITE_COUNTOFVIEW_OPTIMIZATION */ /* ** If any term of pSrc, or any SF_NestedFrom sub-query, is not the same @@ -145403,7 +146229,7 @@ SQLITE_PRIVATE int sqlite3Select( pTabList->a[0].fg.jointype & JT_LTORJ); } - /* No futher action if this term of the FROM clause is no a subquery */ + /* No futher action if this term of the FROM clause is not a subquery */ if( pSub==0 ) continue; /* Catch mismatch in the declared columns of a view and the number of @@ -145536,14 +146362,12 @@ SQLITE_PRIVATE int sqlite3Select( TREETRACE(0x2000,pParse,p,("Constant propagation not helpful\n")); } -#ifdef SQLITE_COUNTOFVIEW_OPTIMIZATION if( OptimizationEnabled(db, SQLITE_QueryFlattener|SQLITE_CountOfView) && countOfViewOptimization(pParse, p) ){ if( db->mallocFailed ) goto select_end; pTabList = p->pSrc; } -#endif /* For each term in the FROM clause, do two things: ** (1) Authorized unreferenced tables @@ -145602,7 +146426,7 @@ SQLITE_PRIVATE int sqlite3Select( if( OptimizationEnabled(db, SQLITE_PushDown) && (pItem->fg.isCte==0 || (pItem->u2.pCteUse->eM10d!=M10d_Yes && pItem->u2.pCteUse->nUse<2)) - && pushDownWhereTerms(pParse, pSub, p->pWhere, pItem) + && pushDownWhereTerms(pParse, pSub, p->pWhere, pTabList, i) ){ #if TREETRACE_ENABLED if( sqlite3TreeTrace & 0x4000 ){ @@ -145616,6 +146440,22 @@ SQLITE_PRIVATE int sqlite3Select( TREETRACE(0x4000,pParse,p,("Push-down not possible\n")); } + /* Convert unused result columns of the subquery into simple NULL + ** expressions, to avoid unneeded searching and computation. + */ + if( OptimizationEnabled(db, SQLITE_NullUnusedCols) + && disableUnusedSubqueryResultColumns(pItem) + ){ +#if TREETRACE_ENABLED + if( sqlite3TreeTrace & 0x4000 ){ + TREETRACE(0x4000,pParse,p, + ("Change unused result columns to NULL for subquery %d:\n", + pSub->selId)); + sqlite3TreeViewSelect(0, p, 0); + } +#endif + } + zSavedAuthContext = pParse->zAuthContext; pParse->zAuthContext = pItem->zName; @@ -148153,6 +148993,9 @@ SQLITE_PRIVATE u32 sqlite3TriggerColmask( Trigger *p; assert( isNew==1 || isNew==0 ); + if( IsView(pTab) ){ + return 0xffffffff; + } for(p=pTrigger; p; p=p->pNext){ if( p->op==op && (tr_tm&p->tr_tm) @@ -148587,7 +149430,7 @@ SQLITE_PRIVATE void sqlite3Update( if( sqlite3ViewGetColumnNames(pParse, pTab) ){ goto update_cleanup; } - if( sqlite3IsReadOnly(pParse, pTab, tmask) ){ + if( sqlite3IsReadOnly(pParse, pTab, pTrigger) ){ goto update_cleanup; } @@ -151378,7 +152221,10 @@ SQLITE_PRIVATE int sqlite3VtabSavepoint(sqlite3 *db, int op, int iSavepoint){ break; } if( xMethod && pVTab->iSavepoint>iSavepoint ){ + u64 savedFlags = (db->flags & SQLITE_Defensive); + db->flags &= ~(u64)SQLITE_Defensive; rc = xMethod(pVTab->pVtab, iSavepoint); + db->flags |= savedFlags; } sqlite3VtabUnlock(pVTab); } @@ -151607,6 +152453,10 @@ SQLITE_API int sqlite3_vtab_config(sqlite3 *db, int op, ...){ p->pVTable->eVtabRisk = SQLITE_VTABRISK_High; break; } + case SQLITE_VTAB_USES_ALL_SCHEMAS: { + p->pVTable->bAllSchemas = 1; + break; + } default: { rc = SQLITE_MISUSE_BKPT; break; @@ -152380,9 +153230,9 @@ static void explainIndexRange(StrAccum *pStr, WhereLoop *pLoop){ /* ** This function is a no-op unless currently processing an EXPLAIN QUERY PLAN -** command, or if either SQLITE_DEBUG or SQLITE_ENABLE_STMT_SCANSTATUS was -** defined at compile-time. If it is not a no-op, a single OP_Explain opcode -** is added to the output to describe the table scan strategy in pLevel. +** command, or if stmt_scanstatus_v2() stats are enabled, or if SQLITE_DEBUG +** was defined at compile-time. If it is not a no-op, a single OP_Explain +** opcode is added to the output to describe the table scan strategy in pLevel. ** ** If an OP_Explain opcode is added to the VM, its address is returned. ** Otherwise, if no OP_Explain is coded, zero is returned. @@ -152394,8 +153244,8 @@ SQLITE_PRIVATE int sqlite3WhereExplainOneScan( u16 wctrlFlags /* Flags passed to sqlite3WhereBegin() */ ){ int ret = 0; -#if !defined(SQLITE_DEBUG) && !defined(SQLITE_ENABLE_STMT_SCANSTATUS) - if( sqlite3ParseToplevel(pParse)->explain==2 ) +#if !defined(SQLITE_DEBUG) + if( sqlite3ParseToplevel(pParse)->explain==2 || IS_STMT_SCANSTATUS(pParse->db) ) #endif { SrcItem *pItem = &pTabList->a[pLevel->iFrom]; @@ -152561,27 +153411,29 @@ SQLITE_PRIVATE void sqlite3WhereAddScanStatus( WhereLevel *pLvl, /* Level to add scanstatus() entry for */ int addrExplain /* Address of OP_Explain (or 0) */ ){ - const char *zObj = 0; - WhereLoop *pLoop = pLvl->pWLoop; - int wsFlags = pLoop->wsFlags; - int viaCoroutine = 0; + if( IS_STMT_SCANSTATUS( sqlite3VdbeDb(v) ) ){ + const char *zObj = 0; + WhereLoop *pLoop = pLvl->pWLoop; + int wsFlags = pLoop->wsFlags; + int viaCoroutine = 0; - if( (wsFlags & WHERE_VIRTUALTABLE)==0 && pLoop->u.btree.pIndex!=0 ){ - zObj = pLoop->u.btree.pIndex->zName; - }else{ - zObj = pSrclist->a[pLvl->iFrom].zName; - viaCoroutine = pSrclist->a[pLvl->iFrom].fg.viaCoroutine; - } - sqlite3VdbeScanStatus( - v, addrExplain, pLvl->addrBody, pLvl->addrVisit, pLoop->nOut, zObj - ); - - if( viaCoroutine==0 ){ - if( (wsFlags & (WHERE_MULTI_OR|WHERE_AUTO_INDEX))==0 ){ - sqlite3VdbeScanStatusRange(v, addrExplain, -1, pLvl->iTabCur); + if( (wsFlags & WHERE_VIRTUALTABLE)==0 && pLoop->u.btree.pIndex!=0 ){ + zObj = pLoop->u.btree.pIndex->zName; + }else{ + zObj = pSrclist->a[pLvl->iFrom].zName; + viaCoroutine = pSrclist->a[pLvl->iFrom].fg.viaCoroutine; } - if( wsFlags & WHERE_INDEXED ){ - sqlite3VdbeScanStatusRange(v, addrExplain, -1, pLvl->iIdxCur); + sqlite3VdbeScanStatus( + v, addrExplain, pLvl->addrBody, pLvl->addrVisit, pLoop->nOut, zObj + ); + + if( viaCoroutine==0 ){ + if( (wsFlags & (WHERE_MULTI_OR|WHERE_AUTO_INDEX))==0 ){ + sqlite3VdbeScanStatusRange(v, addrExplain, -1, pLvl->iTabCur); + } + if( wsFlags & WHERE_INDEXED ){ + sqlite3VdbeScanStatusRange(v, addrExplain, -1, pLvl->iIdxCur); + } } } } @@ -153278,11 +154130,12 @@ static int codeCursorHintIsOrFunction(Walker *pWalker, Expr *pExpr){ */ static int codeCursorHintFixExpr(Walker *pWalker, Expr *pExpr){ int rc = WRC_Continue; + int reg; struct CCurHint *pHint = pWalker->u.pCCurHint; if( pExpr->op==TK_COLUMN ){ if( pExpr->iTable!=pHint->iTabCur ){ - int reg = ++pWalker->pParse->nMem; /* Register for column value */ - sqlite3ExprCode(pWalker->pParse, pExpr, reg); + reg = ++pWalker->pParse->nMem; /* Register for column value */ + reg = sqlite3ExprCodeTarget(pWalker->pParse, pExpr, reg); pExpr->op = TK_REGISTER; pExpr->iTable = reg; }else if( pHint->pIdx!=0 ){ @@ -153290,15 +154143,15 @@ static int codeCursorHintFixExpr(Walker *pWalker, Expr *pExpr){ pExpr->iColumn = sqlite3TableColumnToIndex(pHint->pIdx, pExpr->iColumn); assert( pExpr->iColumn>=0 ); } - }else if( pExpr->op==TK_AGG_FUNCTION ){ - /* An aggregate function in the WHERE clause of a query means this must - ** be a correlated sub-query, and expression pExpr is an aggregate from - ** the parent context. Do not walk the function arguments in this case. - ** - ** todo: It should be possible to replace this node with a TK_REGISTER - ** expression, as the result of the expression must be stored in a - ** register at this point. The same holds for TK_AGG_COLUMN nodes. */ + }else if( pExpr->pAggInfo ){ rc = WRC_Prune; + reg = ++pWalker->pParse->nMem; /* Register for column value */ + reg = sqlite3ExprCodeTarget(pWalker->pParse, pExpr, reg); + pExpr->op = TK_REGISTER; + pExpr->iTable = reg; + }else if( pExpr->op==TK_TRUEFALSE ){ + /* Do not walk disabled expressions. tag-20230504-1 */ + return WRC_Prune; } return rc; } @@ -153400,7 +154253,7 @@ static void codeCursorHint( } if( pExpr!=0 ){ sWalker.xExprCallback = codeCursorHintFixExpr; - sqlite3WalkExpr(&sWalker, pExpr); + if( pParse->nErr==0 ) sqlite3WalkExpr(&sWalker, pExpr); sqlite3VdbeAddOp4(v, OP_CursorHint, (sHint.pIdx ? sHint.iIdxCur : sHint.iTabCur), 0, 0, (const char*)pExpr, P4_EXPR); @@ -154194,7 +155047,7 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( ** guess. */ addrSeekScan = sqlite3VdbeAddOp1(v, OP_SeekScan, (pIdx->aiRowLogEst[0]+9)/10); - if( pRangeStart ){ + if( pRangeStart || pRangeEnd ){ sqlite3VdbeChangeP5(v, 1); sqlite3VdbeChangeP2(v, addrSeekScan, sqlite3VdbeCurrentAddr(v)+1); addrSeekScan = 0; @@ -154235,16 +155088,7 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( assert( pLevel->p2==0 ); if( pRangeEnd ){ Expr *pRight = pRangeEnd->pExpr->pRight; - if( addrSeekScan ){ - /* For a seek-scan that has a range on the lowest term of the index, - ** we have to make the top of the loop be code that sets the end - ** condition of the range. Otherwise, the OP_SeekScan might jump - ** over that initialization, leaving the range-end value set to the - ** range-start value, resulting in a wrong answer. - ** See ticket 5981a8c041a3c2f3 (2021-11-02). - */ - pLevel->p2 = sqlite3VdbeCurrentAddr(v); - } + assert( addrSeekScan==0 ); codeExprOrVector(pParse, pRight, regBase+nEq, nTop); whereLikeOptimizationStringFixup(v, pLevel, pRangeEnd); if( (pRangeEnd->wtFlags & TERM_VNULL)==0 @@ -154278,7 +155122,7 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( if( zEndAff ) sqlite3DbNNFreeNN(db, zEndAff); /* Top of the loop body */ - if( pLevel->p2==0 ) pLevel->p2 = sqlite3VdbeCurrentAddr(v); + pLevel->p2 = sqlite3VdbeCurrentAddr(v); /* Check if the index cursor is past the end of the range. */ if( nConstraint ){ @@ -156275,7 +157119,7 @@ static void exprAnalyze( && 0==sqlite3ExprCanBeNull(pLeft) ){ assert( !ExprHasProperty(pExpr, EP_IntValue) ); - pExpr->op = TK_TRUEFALSE; + pExpr->op = TK_TRUEFALSE; /* See tag-20230504-1 */ pExpr->u.zToken = "false"; ExprSetProperty(pExpr, EP_IsFalse); pTerm->prereqAll = 0; @@ -156920,9 +157764,12 @@ SQLITE_PRIVATE void sqlite3WhereTabFuncArgs( pRhs = sqlite3PExpr(pParse, TK_UPLUS, sqlite3ExprDup(pParse->db, pArgs->a[j].pExpr, 0), 0); pTerm = sqlite3PExpr(pParse, TK_EQ, pColRef, pRhs); - if( pItem->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT) ){ + if( pItem->fg.jointype & (JT_LEFT|JT_RIGHT) ){ + testcase( pItem->fg.jointype & JT_LEFT ); /* testtag-20230227a */ + testcase( pItem->fg.jointype & JT_RIGHT ); /* testtag-20230227b */ joinType = EP_OuterON; }else{ + testcase( pItem->fg.jointype & JT_LTORJ ); /* testtag-20230227c */ joinType = EP_InnerON; } sqlite3SetJoinExpr(pTerm, pItem->iCursor, joinType); @@ -157765,7 +158612,7 @@ static void explainAutomaticIndex( int bPartial, /* True if pIdx is a partial index */ int *pAddrExplain /* OUT: Address of OP_Explain */ ){ - if( pParse->explain!=2 ){ + if( IS_STMT_SCANSTATUS(pParse->db) && pParse->explain!=2 ){ Table *pTab = pIdx->pTable; const char *zSep = ""; char *zText = 0; @@ -157804,8 +158651,7 @@ static void explainAutomaticIndex( */ static SQLITE_NOINLINE void constructAutomaticIndex( Parse *pParse, /* The parsing context */ - const WhereClause *pWC, /* The WHERE clause */ - const SrcItem *pSrc, /* The FROM clause term to get the next index */ + WhereClause *pWC, /* The WHERE clause */ const Bitmask notReady, /* Mask of cursors that are not available */ WhereLevel *pLevel /* Write new index here */ ){ @@ -157826,10 +158672,12 @@ static SQLITE_NOINLINE void constructAutomaticIndex( char *zNotUsed; /* Extra space on the end of pIdx */ Bitmask idxCols; /* Bitmap of columns used for indexing */ Bitmask extraCols; /* Bitmap of additional columns */ - u8 sentWarning = 0; /* True if a warnning has been issued */ + u8 sentWarning = 0; /* True if a warning has been issued */ + u8 useBloomFilter = 0; /* True to also add a Bloom filter */ Expr *pPartial = 0; /* Partial Index Expression */ int iContinue = 0; /* Jump here to skip excluded rows */ - SrcItem *pTabItem; /* FROM clause term being indexed */ + SrcList *pTabList; /* The complete FROM clause */ + SrcItem *pSrc; /* The FROM clause term to get the next index */ int addrCounter = 0; /* Address where integer counter is initialized */ int regBase; /* Array of registers where record is assembled */ #ifdef SQLITE_ENABLE_STMT_SCANSTATUS @@ -157845,6 +158693,8 @@ static SQLITE_NOINLINE void constructAutomaticIndex( /* Count the number of columns that will be added to the index ** and used to match WHERE clause constraints */ nKeyCol = 0; + pTabList = pWC->pWInfo->pTabList; + pSrc = &pTabList->a[pLevel->iFrom]; pTable = pSrc->pTab; pWCEnd = &pWC->a[pWC->nTerm]; pLoop = pLevel->pWLoop; @@ -157855,7 +158705,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 - && sqlite3ExprIsTableConstraint(pExpr, pSrc) + && sqlite3ExprIsSingleTableConstraint(pExpr, pTabList, pLevel->iFrom) ){ pPartial = sqlite3ExprAnd(pParse, pPartial, sqlite3ExprDup(pParse->db, pExpr, 0)); @@ -157896,7 +158746,11 @@ static SQLITE_NOINLINE void constructAutomaticIndex( ** original table changes and the index and table cannot both be used ** if they go out of sync. */ - extraCols = pSrc->colUsed & (~idxCols | MASKBIT(BMS-1)); + if( IsView(pTable) ){ + extraCols = ALLBITS; + }else{ + extraCols = pSrc->colUsed & (~idxCols | MASKBIT(BMS-1)); + } mxBitCol = MIN(BMS-1,pTable->nCol); testcase( pTable->nCol==BMS-1 ); testcase( pTable->nCol==BMS-2 ); @@ -157932,6 +158786,16 @@ static SQLITE_NOINLINE void constructAutomaticIndex( assert( pColl!=0 || pParse->nErr>0 ); /* TH3 collate01.800 */ pIdx->azColl[n] = pColl ? pColl->zName : sqlite3StrBINARY; n++; + if( ALWAYS(pX->pLeft!=0) + && sqlite3ExprAffinity(pX->pLeft)!=SQLITE_AFF_TEXT + ){ + /* TUNING: only use a Bloom filter on an automatic index + ** if one or more key columns has the ability to hold numeric + ** values, since strings all have the same hash in the Bloom + ** filter implementation and hence a Bloom filter on a text column + ** is not usually helpful. */ + useBloomFilter = 1; + } } } } @@ -157964,20 +158828,21 @@ static SQLITE_NOINLINE void constructAutomaticIndex( sqlite3VdbeAddOp2(v, OP_OpenAutoindex, pLevel->iIdxCur, nKeyCol+1); sqlite3VdbeSetP4KeyInfo(pParse, pIdx); VdbeComment((v, "for %s", pTable->zName)); - if( OptimizationEnabled(pParse->db, SQLITE_BloomFilter) ){ + if( OptimizationEnabled(pParse->db, SQLITE_BloomFilter) && useBloomFilter ){ + sqlite3WhereExplainBloomFilter(pParse, pWC->pWInfo, pLevel); pLevel->regFilter = ++pParse->nMem; sqlite3VdbeAddOp2(v, OP_Blob, 10000, pLevel->regFilter); } /* Fill the automatic index with content */ - pTabItem = &pWC->pWInfo->pTabList->a[pLevel->iFrom]; - if( pTabItem->fg.viaCoroutine ){ - int regYield = pTabItem->regReturn; + assert( pSrc == &pWC->pWInfo->pTabList->a[pLevel->iFrom] ); + if( pSrc->fg.viaCoroutine ){ + int regYield = pSrc->regReturn; addrCounter = sqlite3VdbeAddOp2(v, OP_Integer, 0, 0); - sqlite3VdbeAddOp3(v, OP_InitCoroutine, regYield, 0, pTabItem->addrFillSub); + sqlite3VdbeAddOp3(v, OP_InitCoroutine, regYield, 0, pSrc->addrFillSub); addrTop = sqlite3VdbeAddOp1(v, OP_Yield, regYield); VdbeCoverage(v); - VdbeComment((v, "next row of %s", pTabItem->pTab->zName)); + VdbeComment((v, "next row of %s", pSrc->pTab->zName)); }else{ addrTop = sqlite3VdbeAddOp1(v, OP_Rewind, pLevel->iTabCur); VdbeCoverage(v); } @@ -157998,14 +158863,14 @@ static SQLITE_NOINLINE void constructAutomaticIndex( sqlite3VdbeAddOp2(v, OP_IdxInsert, pLevel->iIdxCur, regRecord); sqlite3VdbeChangeP5(v, OPFLAG_USESEEKRESULT); if( pPartial ) sqlite3VdbeResolveLabel(v, iContinue); - if( pTabItem->fg.viaCoroutine ){ + if( pSrc->fg.viaCoroutine ){ sqlite3VdbeChangeP2(v, addrCounter, regBase+n); testcase( pParse->db->mallocFailed ); assert( pLevel->iIdxCur>0 ); translateColumnToCopy(pParse, addrTop, pLevel->iTabCur, - pTabItem->regResult, pLevel->iIdxCur); + pSrc->regResult, pLevel->iIdxCur); sqlite3VdbeGoto(v, addrTop); - pTabItem->fg.viaCoroutine = 0; + pSrc->fg.viaCoroutine = 0; }else{ sqlite3VdbeAddOp2(v, OP_Next, pLevel->iTabCur, addrTop+1); VdbeCoverage(v); sqlite3VdbeChangeP5(v, SQLITE_STMTSTATUS_AUTOINDEX); @@ -158068,9 +158933,11 @@ static SQLITE_NOINLINE void sqlite3ConstructBloomFilter( addrOnce = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v); do{ + const SrcList *pTabList; const SrcItem *pItem; const Table *pTab; u64 sz; + int iSrc; sqlite3WhereExplainBloomFilter(pParse, pWInfo, pLevel); addrCont = sqlite3VdbeMakeLabel(pParse); iCur = pLevel->iTabCur; @@ -158084,7 +158951,9 @@ static SQLITE_NOINLINE void sqlite3ConstructBloomFilter( ** testing complicated. By basing the blob size on the value in the ** sqlite_stat1 table, testing is much easier. */ - pItem = &pWInfo->pTabList->a[pLevel->iFrom]; + pTabList = pWInfo->pTabList; + iSrc = pLevel->iFrom; + pItem = &pTabList->a[iSrc]; assert( pItem!=0 ); pTab = pItem->pTab; assert( pTab!=0 ); @@ -158101,7 +158970,7 @@ static SQLITE_NOINLINE void sqlite3ConstructBloomFilter( for(pTerm=pWInfo->sWC.a; pTermpExpr; if( (pTerm->wtFlags & TERM_VIRTUAL)==0 - && sqlite3ExprIsTableConstraint(pExpr, pItem) + && sqlite3ExprIsSingleTableConstraint(pExpr, pTabList, iSrc) ){ sqlite3ExprIfFalse(pParse, pTerm->pExpr, addrCont, SQLITE_JUMPIFNULL); } @@ -158405,6 +159274,9 @@ static int vtabBestIndex(Parse *pParse, Table *pTab, sqlite3_index_info *p){ sqlite3ErrorMsg(pParse, "%s", pVtab->zErrMsg); } } + if( pTab->u.vtab.p->bAllSchemas ){ + sqlite3VtabUsesAllSchemas(pParse); + } sqlite3_free(pVtab->zErrMsg); pVtab->zErrMsg = 0; return rc; @@ -158935,7 +159807,7 @@ static int whereRangeScanEst( UNUSED_PARAMETER(pBuilder); assert( pLower || pUpper ); #endif - assert( pUpper==0 || (pUpper->wtFlags & TERM_VNULL)==0 ); + assert( pUpper==0 || (pUpper->wtFlags & TERM_VNULL)==0 || pParse->nErr>0 ); nNew = whereRangeAdjust(pLower, nOut); nNew = whereRangeAdjust(pUpper, nNew); @@ -161036,8 +161908,6 @@ SQLITE_API int sqlite3_vtab_distinct(sqlite3_index_info *pIdxInfo){ return pHidden->eDistinct; } -#if (defined(SQLITE_ENABLE_DBPAGE_VTAB) || defined(SQLITE_TEST)) \ - && !defined(SQLITE_OMIT_VIRTUALTABLE) /* ** Cause the prepared statement that is associated with a call to ** xBestIndex to potentially use all schemas. If the statement being @@ -161047,9 +161917,7 @@ SQLITE_API int sqlite3_vtab_distinct(sqlite3_index_info *pIdxInfo){ ** ** This is used by the (built-in) sqlite_dbpage virtual table. */ -SQLITE_PRIVATE void sqlite3VtabUsesAllSchemas(sqlite3_index_info *pIdxInfo){ - HiddenIndexInfo *pHidden = (HiddenIndexInfo*)&pIdxInfo[1]; - Parse *pParse = pHidden->pParse; +SQLITE_PRIVATE void sqlite3VtabUsesAllSchemas(Parse *pParse){ int nDb = pParse->db->nDb; int i; for(i=0; inLevel>=2 ); @@ -162481,6 +163356,7 @@ static SQLITE_NOINLINE Bitmask whereOmitNoopJoin( if( pWInfo->pOrderBy ){ tabUsed |= sqlite3WhereExprListUsage(&pWInfo->sMaskSet, pWInfo->pOrderBy); } + hasRightJoin = (pWInfo->pTabList->a[0].fg.jointype & JT_LTORJ)!=0; for(i=pWInfo->nLevel-1; i>=1; i--){ WhereTerm *pTerm, *pEnd; SrcItem *pItem; @@ -162503,6 +163379,12 @@ static SQLITE_NOINLINE Bitmask whereOmitNoopJoin( break; } } + if( hasRightJoin + && ExprHasProperty(pTerm->pExpr, EP_InnerON) + && pTerm->pExpr->w.iJoin==pItem->iCursor + ){ + break; /* restriction (5) */ + } } if( pTerm drop loop %c not used\n", pLoop->cId)); @@ -162902,22 +163784,45 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin( } if( pParse->nErr ) goto whereBeginError; - /* Special case: WHERE terms that do not refer to any tables in the join - ** (constant expressions). Evaluate each such term, and jump over all the - ** generated code if the result is not true. + /* The False-WHERE-Term-Bypass optimization: ** - ** Do not do this if the expression contains non-deterministic functions - ** that are not within a sub-select. This is not strictly required, but - ** preserves SQLite's legacy behaviour in the following two cases: + ** If there are WHERE terms that are false, then no rows will be output, + ** so skip over all of the code generated here. ** - ** FROM ... WHERE random()>0; -- eval random() once per row - ** FROM ... WHERE (SELECT random())>0; -- eval random() once overall + ** Conditions: + ** + ** (1) The WHERE term must not refer to any tables in the join. + ** (2) The term must not come from an ON clause on the + ** right-hand side of a LEFT or FULL JOIN. + ** (3) The term must not come from an ON clause, or there must be + ** no RIGHT or FULL OUTER joins in pTabList. + ** (4) If the expression contains non-deterministic functions + ** that are not within a sub-select. This is not required + ** for correctness but rather to preserves SQLite's legacy + ** behaviour in the following two cases: + ** + ** WHERE random()>0; -- eval random() once per row + ** WHERE (SELECT random())>0; -- eval random() just once overall + ** + ** Note that the Where term need not be a constant in order for this + ** optimization to apply, though it does need to be constant relative to + ** the current subquery (condition 1). The term might include variables + ** from outer queries so that the value of the term changes from one + ** invocation of the current subquery to the next. */ for(ii=0; iinBase; ii++){ - WhereTerm *pT = &sWLB.pWC->a[ii]; + WhereTerm *pT = &sWLB.pWC->a[ii]; /* A term of the WHERE clause */ + Expr *pX; /* The expression of pT */ if( pT->wtFlags & TERM_VIRTUAL ) continue; - if( pT->prereqAll==0 && (nTabList==0 || exprIsDeterministic(pT->pExpr)) ){ - sqlite3ExprIfFalse(pParse, pT->pExpr, pWInfo->iBreak, SQLITE_JUMPIFNULL); + pX = pT->pExpr; + assert( pX!=0 ); + assert( pT->prereqAll!=0 || !ExprHasProperty(pX, EP_OuterON) ); + if( pT->prereqAll==0 /* Conditions (1) and (2) */ + && (nTabList==0 || exprIsDeterministic(pX)) /* Condition (4) */ + && !(ExprHasProperty(pX, EP_InnerON) /* Condition (3) */ + && (pTabList->a[0].fg.jointype & JT_LTORJ)!=0 ) + ){ + sqlite3ExprIfFalse(pParse, pX, pWInfo->iBreak, SQLITE_JUMPIFNULL); pT->wtFlags |= TERM_CODED; } } @@ -163160,7 +164065,7 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin( assert( n<=pTab->nCol ); } #ifdef SQLITE_ENABLE_CURSOR_HINTS - if( pLoop->u.btree.pIndex!=0 ){ + if( pLoop->u.btree.pIndex!=0 && (pTab->tabFlags & TF_WithoutRowid)==0 ){ sqlite3VdbeChangeP5(v, OPFLAG_SEEKEQ|bFordelete); }else #endif @@ -163297,11 +164202,11 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin( sqlite3VdbeJumpHere(v, iOnce); } } + assert( pTabList == pWInfo->pTabList ); if( (wsFlags & (WHERE_AUTO_INDEX|WHERE_BLOOMFILTER))!=0 ){ if( (wsFlags & WHERE_AUTO_INDEX)!=0 ){ #ifndef SQLITE_OMIT_AUTOMATIC_INDEX - constructAutomaticIndex(pParse, &pWInfo->sWC, - &pTabList->a[pLevel->iFrom], notReady, pLevel); + constructAutomaticIndex(pParse, &pWInfo->sWC, notReady, pLevel); #endif }else{ sqlite3ConstructBloomFilter(pWInfo, ii, pLevel, notReady); @@ -163618,7 +164523,8 @@ SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo *pWInfo){ k = pLevel->addrBody + 1; #ifdef SQLITE_DEBUG if( db->flags & SQLITE_VdbeAddopTrace ){ - printf("TRANSLATE opcodes in range %d..%d\n", k, last-1); + printf("TRANSLATE cursor %d->%d in opcode range %d..%d\n", + pLevel->iTabCur, pLevel->iIdxCur, k, last-1); } /* Proof that the "+1" on the k value above is safe */ pOp = sqlite3VdbeGetOp(v, k - 1); @@ -164493,6 +165399,7 @@ static int selectWindowRewriteExprCb(Walker *pWalker, Expr *pExpr){ } /* no break */ deliberate_fall_through + case TK_IF_NULL_ROW: case TK_AGG_FUNCTION: case TK_COLUMN: { int iCol = -1; @@ -167321,18 +168228,18 @@ typedef union { #define sqlite3ParserCTX_FETCH Parse *pParse=yypParser->pParse; #define sqlite3ParserCTX_STORE yypParser->pParse=pParse; #define YYFALLBACK 1 -#define YYNSTATE 576 -#define YYNRULE 405 -#define YYNRULE_WITH_ACTION 342 +#define YYNSTATE 575 +#define YYNRULE 403 +#define YYNRULE_WITH_ACTION 340 #define YYNTOKEN 185 -#define YY_MAX_SHIFT 575 -#define YY_MIN_SHIFTREDUCE 835 -#define YY_MAX_SHIFTREDUCE 1239 -#define YY_ERROR_ACTION 1240 -#define YY_ACCEPT_ACTION 1241 -#define YY_NO_ACTION 1242 -#define YY_MIN_REDUCE 1243 -#define YY_MAX_REDUCE 1647 +#define YY_MAX_SHIFT 574 +#define YY_MIN_SHIFTREDUCE 833 +#define YY_MAX_SHIFTREDUCE 1235 +#define YY_ERROR_ACTION 1236 +#define YY_ACCEPT_ACTION 1237 +#define YY_NO_ACTION 1238 +#define YY_MIN_REDUCE 1239 +#define YY_MAX_REDUCE 1641 /************* End control #defines *******************************************/ #define YY_NLOOKAHEAD ((int)(sizeof(yy_lookahead)/sizeof(yy_lookahead[0]))) @@ -167399,218 +168306,218 @@ typedef union { ** yy_default[] Default action for each state. ** *********** Begin parsing tables **********************************************/ -#define YY_ACTTAB_COUNT (2098) +#define YY_ACTTAB_COUNT (2096) static const YYACTIONTYPE yy_action[] = { /* 0 */ 568, 208, 568, 118, 115, 229, 568, 118, 115, 229, - /* 10 */ 568, 1314, 377, 1293, 408, 562, 562, 562, 568, 409, - /* 20 */ 378, 1314, 1276, 41, 41, 41, 41, 208, 1526, 71, - /* 30 */ 71, 971, 419, 41, 41, 491, 303, 279, 303, 972, - /* 40 */ 397, 71, 71, 125, 126, 80, 1217, 1217, 1050, 1053, - /* 50 */ 1040, 1040, 123, 123, 124, 124, 124, 124, 476, 409, - /* 60 */ 1241, 1, 1, 575, 2, 1245, 550, 118, 115, 229, - /* 70 */ 317, 480, 146, 480, 524, 118, 115, 229, 529, 1327, - /* 80 */ 417, 523, 142, 125, 126, 80, 1217, 1217, 1050, 1053, - /* 90 */ 1040, 1040, 123, 123, 124, 124, 124, 124, 118, 115, + /* 10 */ 568, 1310, 377, 1289, 408, 562, 562, 562, 568, 409, + /* 20 */ 378, 1310, 1272, 41, 41, 41, 41, 208, 1520, 71, + /* 30 */ 71, 969, 419, 41, 41, 491, 303, 279, 303, 970, + /* 40 */ 397, 71, 71, 125, 126, 80, 1212, 1212, 1047, 1050, + /* 50 */ 1037, 1037, 123, 123, 124, 124, 124, 124, 476, 409, + /* 60 */ 1237, 1, 1, 574, 2, 1241, 550, 118, 115, 229, + /* 70 */ 317, 480, 146, 480, 524, 118, 115, 229, 529, 1323, + /* 80 */ 417, 523, 142, 125, 126, 80, 1212, 1212, 1047, 1050, + /* 90 */ 1037, 1037, 123, 123, 124, 124, 124, 124, 118, 115, /* 100 */ 229, 327, 122, 122, 122, 122, 121, 121, 120, 120, /* 110 */ 120, 119, 116, 444, 284, 284, 284, 284, 442, 442, - /* 120 */ 442, 1567, 376, 1569, 1192, 375, 1163, 565, 1163, 565, - /* 130 */ 409, 1567, 537, 259, 226, 444, 101, 145, 449, 316, + /* 120 */ 442, 1561, 376, 1563, 1188, 375, 1159, 565, 1159, 565, + /* 130 */ 409, 1561, 537, 259, 226, 444, 101, 145, 449, 316, /* 140 */ 559, 240, 122, 122, 122, 122, 121, 121, 120, 120, - /* 150 */ 120, 119, 116, 444, 125, 126, 80, 1217, 1217, 1050, - /* 160 */ 1053, 1040, 1040, 123, 123, 124, 124, 124, 124, 142, - /* 170 */ 294, 1192, 339, 448, 120, 120, 120, 119, 116, 444, - /* 180 */ 127, 1192, 1193, 1194, 148, 441, 440, 568, 119, 116, + /* 150 */ 120, 119, 116, 444, 125, 126, 80, 1212, 1212, 1047, + /* 160 */ 1050, 1037, 1037, 123, 123, 124, 124, 124, 124, 142, + /* 170 */ 294, 1188, 339, 448, 120, 120, 120, 119, 116, 444, + /* 180 */ 127, 1188, 1189, 1188, 148, 441, 440, 568, 119, 116, /* 190 */ 444, 124, 124, 124, 124, 117, 122, 122, 122, 122, /* 200 */ 121, 121, 120, 120, 120, 119, 116, 444, 454, 113, /* 210 */ 13, 13, 546, 122, 122, 122, 122, 121, 121, 120, - /* 220 */ 120, 120, 119, 116, 444, 422, 316, 559, 1192, 1193, - /* 230 */ 1194, 149, 1224, 409, 1224, 124, 124, 124, 124, 122, + /* 220 */ 120, 120, 119, 116, 444, 422, 316, 559, 1188, 1189, + /* 230 */ 1188, 149, 1220, 409, 1220, 124, 124, 124, 124, 122, /* 240 */ 122, 122, 122, 121, 121, 120, 120, 120, 119, 116, - /* 250 */ 444, 465, 342, 1037, 1037, 1051, 1054, 125, 126, 80, - /* 260 */ 1217, 1217, 1050, 1053, 1040, 1040, 123, 123, 124, 124, - /* 270 */ 124, 124, 1279, 522, 222, 1192, 568, 409, 224, 514, + /* 250 */ 444, 465, 342, 1034, 1034, 1048, 1051, 125, 126, 80, + /* 260 */ 1212, 1212, 1047, 1050, 1037, 1037, 123, 123, 124, 124, + /* 270 */ 124, 124, 1275, 522, 222, 1188, 568, 409, 224, 514, /* 280 */ 175, 82, 83, 122, 122, 122, 122, 121, 121, 120, - /* 290 */ 120, 120, 119, 116, 444, 1007, 16, 16, 1192, 133, - /* 300 */ 133, 125, 126, 80, 1217, 1217, 1050, 1053, 1040, 1040, + /* 290 */ 120, 120, 119, 116, 444, 1005, 16, 16, 1188, 133, + /* 300 */ 133, 125, 126, 80, 1212, 1212, 1047, 1050, 1037, 1037, /* 310 */ 123, 123, 124, 124, 124, 124, 122, 122, 122, 122, - /* 320 */ 121, 121, 120, 120, 120, 119, 116, 444, 1041, 546, - /* 330 */ 1192, 373, 1192, 1193, 1194, 252, 1434, 399, 504, 501, - /* 340 */ 500, 111, 560, 566, 4, 926, 926, 433, 499, 340, - /* 350 */ 460, 328, 360, 394, 1237, 1192, 1193, 1194, 563, 568, + /* 320 */ 121, 121, 120, 120, 120, 119, 116, 444, 1038, 546, + /* 330 */ 1188, 373, 1188, 1189, 1188, 252, 1429, 399, 504, 501, + /* 340 */ 500, 111, 560, 566, 4, 924, 924, 433, 499, 340, + /* 350 */ 460, 328, 360, 394, 1233, 1188, 1189, 1188, 563, 568, /* 360 */ 122, 122, 122, 122, 121, 121, 120, 120, 120, 119, - /* 370 */ 116, 444, 284, 284, 369, 1580, 1607, 441, 440, 154, - /* 380 */ 409, 445, 71, 71, 1286, 565, 1221, 1192, 1193, 1194, - /* 390 */ 85, 1223, 271, 557, 543, 515, 1561, 568, 98, 1222, - /* 400 */ 6, 1278, 472, 142, 125, 126, 80, 1217, 1217, 1050, - /* 410 */ 1053, 1040, 1040, 123, 123, 124, 124, 124, 124, 550, - /* 420 */ 13, 13, 1027, 507, 1224, 1192, 1224, 549, 109, 109, - /* 430 */ 222, 568, 1238, 175, 568, 427, 110, 197, 445, 570, - /* 440 */ 569, 430, 1552, 1017, 325, 551, 1192, 270, 287, 368, + /* 370 */ 116, 444, 284, 284, 369, 1574, 1600, 441, 440, 154, + /* 380 */ 409, 445, 71, 71, 1282, 565, 1217, 1188, 1189, 1188, + /* 390 */ 85, 1219, 271, 557, 543, 515, 1555, 568, 98, 1218, + /* 400 */ 6, 1274, 472, 142, 125, 126, 80, 1212, 1212, 1047, + /* 410 */ 1050, 1037, 1037, 123, 123, 124, 124, 124, 124, 550, + /* 420 */ 13, 13, 1024, 507, 1220, 1188, 1220, 549, 109, 109, + /* 430 */ 222, 568, 1234, 175, 568, 427, 110, 197, 445, 569, + /* 440 */ 445, 430, 1546, 1014, 325, 551, 1188, 270, 287, 368, /* 450 */ 510, 363, 509, 257, 71, 71, 543, 71, 71, 359, - /* 460 */ 316, 559, 1613, 122, 122, 122, 122, 121, 121, 120, - /* 470 */ 120, 120, 119, 116, 444, 1017, 1017, 1019, 1020, 27, - /* 480 */ 284, 284, 1192, 1193, 1194, 1158, 568, 1612, 409, 901, - /* 490 */ 190, 550, 356, 565, 550, 937, 533, 517, 1158, 516, - /* 500 */ 413, 1158, 552, 1192, 1193, 1194, 568, 544, 1554, 51, - /* 510 */ 51, 214, 125, 126, 80, 1217, 1217, 1050, 1053, 1040, - /* 520 */ 1040, 123, 123, 124, 124, 124, 124, 1192, 474, 135, - /* 530 */ 135, 409, 284, 284, 1490, 505, 121, 121, 120, 120, - /* 540 */ 120, 119, 116, 444, 1007, 565, 518, 217, 541, 1561, - /* 550 */ 316, 559, 142, 6, 532, 125, 126, 80, 1217, 1217, - /* 560 */ 1050, 1053, 1040, 1040, 123, 123, 124, 124, 124, 124, - /* 570 */ 1555, 122, 122, 122, 122, 121, 121, 120, 120, 120, - /* 580 */ 119, 116, 444, 485, 1192, 1193, 1194, 482, 281, 1267, - /* 590 */ 957, 252, 1192, 373, 504, 501, 500, 1192, 340, 571, - /* 600 */ 1192, 571, 409, 292, 499, 957, 876, 191, 480, 316, + /* 460 */ 316, 559, 1606, 122, 122, 122, 122, 121, 121, 120, + /* 470 */ 120, 120, 119, 116, 444, 1014, 1014, 1016, 1017, 27, + /* 480 */ 284, 284, 1188, 1189, 1188, 1154, 568, 1605, 409, 899, + /* 490 */ 190, 550, 356, 565, 550, 935, 533, 517, 1154, 516, + /* 500 */ 413, 1154, 552, 1188, 1189, 1188, 568, 544, 1548, 51, + /* 510 */ 51, 214, 125, 126, 80, 1212, 1212, 1047, 1050, 1037, + /* 520 */ 1037, 123, 123, 124, 124, 124, 124, 1188, 474, 135, + /* 530 */ 135, 409, 284, 284, 1484, 505, 121, 121, 120, 120, + /* 540 */ 120, 119, 116, 444, 1005, 565, 518, 217, 541, 1555, + /* 550 */ 316, 559, 142, 6, 532, 125, 126, 80, 1212, 1212, + /* 560 */ 1047, 1050, 1037, 1037, 123, 123, 124, 124, 124, 124, + /* 570 */ 1549, 122, 122, 122, 122, 121, 121, 120, 120, 120, + /* 580 */ 119, 116, 444, 485, 1188, 1189, 1188, 482, 281, 1263, + /* 590 */ 955, 252, 1188, 373, 504, 501, 500, 1188, 340, 570, + /* 600 */ 1188, 570, 409, 292, 499, 955, 874, 191, 480, 316, /* 610 */ 559, 384, 290, 380, 122, 122, 122, 122, 121, 121, - /* 620 */ 120, 120, 120, 119, 116, 444, 125, 126, 80, 1217, - /* 630 */ 1217, 1050, 1053, 1040, 1040, 123, 123, 124, 124, 124, - /* 640 */ 124, 409, 394, 1136, 1192, 869, 100, 284, 284, 1192, - /* 650 */ 1193, 1194, 373, 1093, 1192, 1193, 1194, 1192, 1193, 1194, - /* 660 */ 565, 455, 32, 373, 233, 125, 126, 80, 1217, 1217, - /* 670 */ 1050, 1053, 1040, 1040, 123, 123, 124, 124, 124, 124, - /* 680 */ 1433, 959, 568, 228, 958, 122, 122, 122, 122, 121, - /* 690 */ 121, 120, 120, 120, 119, 116, 444, 1158, 228, 1192, - /* 700 */ 157, 1192, 1193, 1194, 1553, 13, 13, 301, 957, 1232, - /* 710 */ 1158, 153, 409, 1158, 373, 1583, 1176, 5, 369, 1580, - /* 720 */ 429, 1238, 3, 957, 122, 122, 122, 122, 121, 121, - /* 730 */ 120, 120, 120, 119, 116, 444, 125, 126, 80, 1217, - /* 740 */ 1217, 1050, 1053, 1040, 1040, 123, 123, 124, 124, 124, - /* 750 */ 124, 409, 208, 567, 1192, 1028, 1192, 1193, 1194, 1192, - /* 760 */ 388, 852, 155, 1552, 286, 402, 1098, 1098, 488, 568, - /* 770 */ 465, 342, 1319, 1319, 1552, 125, 126, 80, 1217, 1217, - /* 780 */ 1050, 1053, 1040, 1040, 123, 123, 124, 124, 124, 124, + /* 620 */ 120, 120, 120, 119, 116, 444, 125, 126, 80, 1212, + /* 630 */ 1212, 1047, 1050, 1037, 1037, 123, 123, 124, 124, 124, + /* 640 */ 124, 409, 394, 1132, 1188, 867, 100, 284, 284, 1188, + /* 650 */ 1189, 1188, 373, 1089, 1188, 1189, 1188, 1188, 1189, 1188, + /* 660 */ 565, 455, 32, 373, 233, 125, 126, 80, 1212, 1212, + /* 670 */ 1047, 1050, 1037, 1037, 123, 123, 124, 124, 124, 124, + /* 680 */ 1428, 957, 568, 228, 956, 122, 122, 122, 122, 121, + /* 690 */ 121, 120, 120, 120, 119, 116, 444, 1154, 228, 1188, + /* 700 */ 157, 1188, 1189, 1188, 1547, 13, 13, 301, 955, 1228, + /* 710 */ 1154, 153, 409, 1154, 373, 1577, 1172, 5, 369, 1574, + /* 720 */ 429, 1234, 3, 955, 122, 122, 122, 122, 121, 121, + /* 730 */ 120, 120, 120, 119, 116, 444, 125, 126, 80, 1212, + /* 740 */ 1212, 1047, 1050, 1037, 1037, 123, 123, 124, 124, 124, + /* 750 */ 124, 409, 208, 567, 1188, 1025, 1188, 1189, 1188, 1188, + /* 760 */ 388, 850, 155, 1546, 286, 402, 1094, 1094, 488, 568, + /* 770 */ 465, 342, 1315, 1315, 1546, 125, 126, 80, 1212, 1212, + /* 780 */ 1047, 1050, 1037, 1037, 123, 123, 124, 124, 124, 124, /* 790 */ 129, 568, 13, 13, 374, 122, 122, 122, 122, 121, /* 800 */ 121, 120, 120, 120, 119, 116, 444, 302, 568, 453, - /* 810 */ 528, 1192, 1193, 1194, 13, 13, 1192, 1193, 1194, 1297, - /* 820 */ 463, 1267, 409, 1317, 1317, 1552, 1012, 453, 452, 200, - /* 830 */ 299, 71, 71, 1265, 122, 122, 122, 122, 121, 121, - /* 840 */ 120, 120, 120, 119, 116, 444, 125, 126, 80, 1217, - /* 850 */ 1217, 1050, 1053, 1040, 1040, 123, 123, 124, 124, 124, - /* 860 */ 124, 409, 227, 1073, 1158, 284, 284, 419, 312, 278, - /* 870 */ 278, 285, 285, 1419, 406, 405, 382, 1158, 565, 568, - /* 880 */ 1158, 1196, 565, 1600, 565, 125, 126, 80, 1217, 1217, - /* 890 */ 1050, 1053, 1040, 1040, 123, 123, 124, 124, 124, 124, - /* 900 */ 453, 1482, 13, 13, 1536, 122, 122, 122, 122, 121, + /* 810 */ 528, 1188, 1189, 1188, 13, 13, 1188, 1189, 1188, 1293, + /* 820 */ 463, 1263, 409, 1313, 1313, 1546, 1010, 453, 452, 200, + /* 830 */ 299, 71, 71, 1261, 122, 122, 122, 122, 121, 121, + /* 840 */ 120, 120, 120, 119, 116, 444, 125, 126, 80, 1212, + /* 850 */ 1212, 1047, 1050, 1037, 1037, 123, 123, 124, 124, 124, + /* 860 */ 124, 409, 227, 1069, 1154, 284, 284, 419, 312, 278, + /* 870 */ 278, 285, 285, 1415, 406, 405, 382, 1154, 565, 568, + /* 880 */ 1154, 1191, 565, 1594, 565, 125, 126, 80, 1212, 1212, + /* 890 */ 1047, 1050, 1037, 1037, 123, 123, 124, 124, 124, 124, + /* 900 */ 453, 1476, 13, 13, 1530, 122, 122, 122, 122, 121, /* 910 */ 121, 120, 120, 120, 119, 116, 444, 201, 568, 354, - /* 920 */ 1586, 575, 2, 1245, 840, 841, 842, 1562, 317, 1212, - /* 930 */ 146, 6, 409, 255, 254, 253, 206, 1327, 9, 1196, + /* 920 */ 1580, 574, 2, 1241, 838, 839, 840, 1556, 317, 1207, + /* 930 */ 146, 6, 409, 255, 254, 253, 206, 1323, 9, 1191, /* 940 */ 262, 71, 71, 424, 122, 122, 122, 122, 121, 121, - /* 950 */ 120, 120, 120, 119, 116, 444, 125, 126, 80, 1217, - /* 960 */ 1217, 1050, 1053, 1040, 1040, 123, 123, 124, 124, 124, - /* 970 */ 124, 568, 284, 284, 568, 1213, 409, 574, 313, 1245, - /* 980 */ 349, 1296, 352, 419, 317, 565, 146, 491, 525, 1643, - /* 990 */ 395, 371, 491, 1327, 70, 70, 1295, 71, 71, 240, - /* 1000 */ 1325, 104, 80, 1217, 1217, 1050, 1053, 1040, 1040, 123, + /* 950 */ 120, 120, 120, 119, 116, 444, 125, 126, 80, 1212, + /* 960 */ 1212, 1047, 1050, 1037, 1037, 123, 123, 124, 124, 124, + /* 970 */ 124, 568, 284, 284, 568, 1208, 409, 573, 313, 1241, + /* 980 */ 349, 1292, 352, 419, 317, 565, 146, 491, 525, 1637, + /* 990 */ 395, 371, 491, 1323, 70, 70, 1291, 71, 71, 240, + /* 1000 */ 1321, 104, 80, 1212, 1212, 1047, 1050, 1037, 1037, 123, /* 1010 */ 123, 124, 124, 124, 124, 122, 122, 122, 122, 121, - /* 1020 */ 121, 120, 120, 120, 119, 116, 444, 1114, 284, 284, - /* 1030 */ 428, 448, 1525, 1213, 439, 284, 284, 1489, 1352, 311, - /* 1040 */ 474, 565, 1115, 971, 491, 491, 217, 1263, 565, 1538, - /* 1050 */ 568, 972, 207, 568, 1027, 240, 383, 1116, 519, 122, + /* 1020 */ 121, 120, 120, 120, 119, 116, 444, 1110, 284, 284, + /* 1030 */ 428, 448, 1519, 1208, 439, 284, 284, 1483, 1348, 311, + /* 1040 */ 474, 565, 1111, 969, 491, 491, 217, 1259, 565, 1532, + /* 1050 */ 568, 970, 207, 568, 1024, 240, 383, 1112, 519, 122, /* 1060 */ 122, 122, 122, 121, 121, 120, 120, 120, 119, 116, - /* 1070 */ 444, 1018, 107, 71, 71, 1017, 13, 13, 912, 568, - /* 1080 */ 1495, 568, 284, 284, 97, 526, 491, 448, 913, 1326, - /* 1090 */ 1322, 545, 409, 284, 284, 565, 151, 209, 1495, 1497, - /* 1100 */ 262, 450, 55, 55, 56, 56, 565, 1017, 1017, 1019, - /* 1110 */ 443, 332, 409, 527, 12, 295, 125, 126, 80, 1217, - /* 1120 */ 1217, 1050, 1053, 1040, 1040, 123, 123, 124, 124, 124, - /* 1130 */ 124, 347, 409, 864, 1534, 1213, 125, 126, 80, 1217, - /* 1140 */ 1217, 1050, 1053, 1040, 1040, 123, 123, 124, 124, 124, - /* 1150 */ 124, 1137, 1641, 474, 1641, 371, 125, 114, 80, 1217, - /* 1160 */ 1217, 1050, 1053, 1040, 1040, 123, 123, 124, 124, 124, - /* 1170 */ 124, 1495, 329, 474, 331, 122, 122, 122, 122, 121, - /* 1180 */ 121, 120, 120, 120, 119, 116, 444, 203, 1419, 568, - /* 1190 */ 1294, 864, 464, 1213, 436, 122, 122, 122, 122, 121, - /* 1200 */ 121, 120, 120, 120, 119, 116, 444, 553, 1137, 1642, - /* 1210 */ 539, 1642, 15, 15, 892, 122, 122, 122, 122, 121, + /* 1070 */ 444, 1015, 107, 71, 71, 1014, 13, 13, 910, 568, + /* 1080 */ 1489, 568, 284, 284, 97, 526, 491, 448, 911, 1322, + /* 1090 */ 1318, 545, 409, 284, 284, 565, 151, 209, 1489, 1491, + /* 1100 */ 262, 450, 55, 55, 56, 56, 565, 1014, 1014, 1016, + /* 1110 */ 443, 332, 409, 527, 12, 295, 125, 126, 80, 1212, + /* 1120 */ 1212, 1047, 1050, 1037, 1037, 123, 123, 124, 124, 124, + /* 1130 */ 124, 347, 409, 862, 1528, 1208, 125, 126, 80, 1212, + /* 1140 */ 1212, 1047, 1050, 1037, 1037, 123, 123, 124, 124, 124, + /* 1150 */ 124, 1133, 1635, 474, 1635, 371, 125, 114, 80, 1212, + /* 1160 */ 1212, 1047, 1050, 1037, 1037, 123, 123, 124, 124, 124, + /* 1170 */ 124, 1489, 329, 474, 331, 122, 122, 122, 122, 121, + /* 1180 */ 121, 120, 120, 120, 119, 116, 444, 203, 1415, 568, + /* 1190 */ 1290, 862, 464, 1208, 436, 122, 122, 122, 122, 121, + /* 1200 */ 121, 120, 120, 120, 119, 116, 444, 553, 1133, 1636, + /* 1210 */ 539, 1636, 15, 15, 890, 122, 122, 122, 122, 121, /* 1220 */ 121, 120, 120, 120, 119, 116, 444, 568, 298, 538, - /* 1230 */ 1135, 1419, 1559, 1560, 1331, 409, 6, 6, 1169, 1268, - /* 1240 */ 415, 320, 284, 284, 1419, 508, 565, 525, 300, 457, - /* 1250 */ 43, 43, 568, 893, 12, 565, 330, 478, 425, 407, - /* 1260 */ 126, 80, 1217, 1217, 1050, 1053, 1040, 1040, 123, 123, - /* 1270 */ 124, 124, 124, 124, 568, 57, 57, 288, 1192, 1419, - /* 1280 */ 496, 458, 392, 392, 391, 273, 389, 1135, 1558, 849, - /* 1290 */ 1169, 407, 6, 568, 321, 1158, 470, 44, 44, 1557, - /* 1300 */ 1114, 426, 234, 6, 323, 256, 540, 256, 1158, 431, - /* 1310 */ 568, 1158, 322, 17, 487, 1115, 58, 58, 122, 122, + /* 1230 */ 1131, 1415, 1553, 1554, 1327, 409, 6, 6, 1165, 1264, + /* 1240 */ 415, 320, 284, 284, 1415, 508, 565, 525, 300, 457, + /* 1250 */ 43, 43, 568, 891, 12, 565, 330, 478, 425, 407, + /* 1260 */ 126, 80, 1212, 1212, 1047, 1050, 1037, 1037, 123, 123, + /* 1270 */ 124, 124, 124, 124, 568, 57, 57, 288, 1188, 1415, + /* 1280 */ 496, 458, 392, 392, 391, 273, 389, 1131, 1552, 847, + /* 1290 */ 1165, 407, 6, 568, 321, 1154, 470, 44, 44, 1551, + /* 1300 */ 1110, 426, 234, 6, 323, 256, 540, 256, 1154, 431, + /* 1310 */ 568, 1154, 322, 17, 487, 1111, 58, 58, 122, 122, /* 1320 */ 122, 122, 121, 121, 120, 120, 120, 119, 116, 444, - /* 1330 */ 1116, 216, 481, 59, 59, 1192, 1193, 1194, 111, 560, + /* 1330 */ 1112, 216, 481, 59, 59, 1188, 1189, 1188, 111, 560, /* 1340 */ 324, 4, 236, 456, 526, 568, 237, 456, 568, 437, - /* 1350 */ 168, 556, 420, 141, 479, 563, 568, 293, 568, 1095, - /* 1360 */ 568, 293, 568, 1095, 531, 568, 872, 8, 60, 60, + /* 1350 */ 168, 556, 420, 141, 479, 563, 568, 293, 568, 1091, + /* 1360 */ 568, 293, 568, 1091, 531, 568, 870, 8, 60, 60, /* 1370 */ 235, 61, 61, 568, 414, 568, 414, 568, 445, 62, /* 1380 */ 62, 45, 45, 46, 46, 47, 47, 199, 49, 49, /* 1390 */ 557, 568, 359, 568, 100, 486, 50, 50, 63, 63, - /* 1400 */ 64, 64, 561, 415, 535, 410, 568, 1027, 568, 534, - /* 1410 */ 316, 559, 316, 559, 65, 65, 14, 14, 568, 1027, - /* 1420 */ 568, 512, 932, 872, 1018, 109, 109, 931, 1017, 66, - /* 1430 */ 66, 131, 131, 110, 451, 445, 570, 569, 416, 177, - /* 1440 */ 1017, 132, 132, 67, 67, 568, 467, 568, 932, 471, - /* 1450 */ 1364, 283, 226, 931, 315, 1363, 407, 568, 459, 407, - /* 1460 */ 1017, 1017, 1019, 239, 407, 86, 213, 1350, 52, 52, - /* 1470 */ 68, 68, 1017, 1017, 1019, 1020, 27, 1585, 1180, 447, - /* 1480 */ 69, 69, 288, 97, 108, 1541, 106, 392, 392, 391, - /* 1490 */ 273, 389, 568, 879, 849, 883, 568, 111, 560, 466, - /* 1500 */ 4, 568, 152, 30, 38, 568, 1132, 234, 396, 323, + /* 1400 */ 64, 64, 561, 415, 535, 410, 568, 1024, 568, 534, + /* 1410 */ 316, 559, 316, 559, 65, 65, 14, 14, 568, 1024, + /* 1420 */ 568, 512, 930, 870, 1015, 109, 109, 929, 1014, 66, + /* 1430 */ 66, 131, 131, 110, 451, 445, 569, 445, 416, 177, + /* 1440 */ 1014, 132, 132, 67, 67, 568, 467, 568, 930, 471, + /* 1450 */ 1360, 283, 226, 929, 315, 1359, 407, 568, 459, 407, + /* 1460 */ 1014, 1014, 1016, 239, 407, 86, 213, 1346, 52, 52, + /* 1470 */ 68, 68, 1014, 1014, 1016, 1017, 27, 1579, 1176, 447, + /* 1480 */ 69, 69, 288, 97, 108, 1535, 106, 392, 392, 391, + /* 1490 */ 273, 389, 568, 877, 847, 881, 568, 111, 560, 466, + /* 1500 */ 4, 568, 152, 30, 38, 568, 1128, 234, 396, 323, /* 1510 */ 111, 560, 527, 4, 563, 53, 53, 322, 568, 163, /* 1520 */ 163, 568, 337, 468, 164, 164, 333, 563, 76, 76, - /* 1530 */ 568, 289, 1514, 568, 31, 1513, 568, 445, 338, 483, - /* 1540 */ 100, 54, 54, 344, 72, 72, 296, 236, 1080, 557, - /* 1550 */ 445, 879, 1360, 134, 134, 168, 73, 73, 141, 161, - /* 1560 */ 161, 1574, 557, 535, 568, 319, 568, 348, 536, 1009, - /* 1570 */ 473, 261, 261, 891, 890, 235, 535, 568, 1027, 568, + /* 1530 */ 568, 289, 1508, 568, 31, 1507, 568, 445, 338, 483, + /* 1540 */ 100, 54, 54, 344, 72, 72, 296, 236, 1076, 557, + /* 1550 */ 445, 877, 1356, 134, 134, 168, 73, 73, 141, 161, + /* 1560 */ 161, 1568, 557, 535, 568, 319, 568, 348, 536, 1007, + /* 1570 */ 473, 261, 261, 889, 888, 235, 535, 568, 1024, 568, /* 1580 */ 475, 534, 261, 367, 109, 109, 521, 136, 136, 130, - /* 1590 */ 130, 1027, 110, 366, 445, 570, 569, 109, 109, 1017, - /* 1600 */ 162, 162, 156, 156, 568, 110, 1080, 445, 570, 569, - /* 1610 */ 410, 351, 1017, 568, 353, 316, 559, 568, 343, 568, - /* 1620 */ 100, 497, 357, 258, 100, 898, 899, 140, 140, 355, - /* 1630 */ 1310, 1017, 1017, 1019, 1020, 27, 139, 139, 362, 451, - /* 1640 */ 137, 137, 138, 138, 1017, 1017, 1019, 1020, 27, 1180, - /* 1650 */ 447, 568, 372, 288, 111, 560, 1021, 4, 392, 392, - /* 1660 */ 391, 273, 389, 568, 1141, 849, 568, 1076, 568, 258, - /* 1670 */ 492, 563, 568, 211, 75, 75, 555, 962, 234, 261, - /* 1680 */ 323, 111, 560, 929, 4, 113, 77, 77, 322, 74, - /* 1690 */ 74, 42, 42, 1373, 445, 48, 48, 1418, 563, 974, - /* 1700 */ 975, 1092, 1091, 1092, 1091, 862, 557, 150, 930, 1346, - /* 1710 */ 113, 1358, 554, 1424, 1021, 1275, 1266, 1254, 236, 1253, - /* 1720 */ 1255, 445, 1593, 1343, 308, 276, 168, 309, 11, 141, - /* 1730 */ 393, 310, 232, 557, 1405, 1027, 335, 291, 1400, 219, - /* 1740 */ 336, 109, 109, 936, 297, 1410, 235, 341, 477, 110, - /* 1750 */ 502, 445, 570, 569, 1393, 1409, 1017, 400, 1293, 365, - /* 1760 */ 223, 1486, 1027, 1485, 1355, 1356, 1354, 1353, 109, 109, - /* 1770 */ 204, 1596, 1232, 558, 265, 218, 110, 205, 445, 570, - /* 1780 */ 569, 410, 387, 1017, 1533, 179, 316, 559, 1017, 1017, - /* 1790 */ 1019, 1020, 27, 230, 1531, 1229, 79, 560, 85, 4, - /* 1800 */ 418, 215, 548, 81, 84, 188, 1406, 173, 181, 461, - /* 1810 */ 451, 35, 462, 563, 183, 1017, 1017, 1019, 1020, 27, - /* 1820 */ 184, 1491, 185, 186, 495, 242, 98, 398, 1412, 36, - /* 1830 */ 1411, 484, 91, 469, 401, 1414, 445, 192, 1480, 246, - /* 1840 */ 1502, 490, 346, 277, 248, 196, 493, 511, 557, 350, - /* 1850 */ 1256, 249, 250, 403, 1313, 1312, 111, 560, 432, 4, - /* 1860 */ 1311, 1304, 93, 1611, 883, 1610, 224, 404, 434, 520, - /* 1870 */ 263, 435, 1579, 563, 1283, 1282, 364, 1027, 306, 1281, - /* 1880 */ 264, 1609, 1565, 109, 109, 370, 1303, 307, 1564, 438, - /* 1890 */ 128, 110, 1378, 445, 570, 569, 445, 546, 1017, 10, - /* 1900 */ 1466, 105, 381, 1377, 34, 572, 99, 1336, 557, 314, - /* 1910 */ 1186, 530, 272, 274, 379, 210, 1335, 547, 385, 386, - /* 1920 */ 275, 573, 1251, 1246, 411, 412, 1518, 165, 178, 1519, - /* 1930 */ 1017, 1017, 1019, 1020, 27, 1517, 1516, 1027, 78, 147, - /* 1940 */ 166, 220, 221, 109, 109, 836, 304, 167, 446, 212, - /* 1950 */ 318, 110, 231, 445, 570, 569, 144, 1090, 1017, 1088, - /* 1960 */ 326, 180, 169, 1212, 182, 334, 238, 915, 241, 1104, + /* 1590 */ 130, 1024, 110, 366, 445, 569, 445, 109, 109, 1014, + /* 1600 */ 162, 162, 156, 156, 568, 110, 1076, 445, 569, 445, + /* 1610 */ 410, 351, 1014, 568, 353, 316, 559, 568, 343, 568, + /* 1620 */ 100, 497, 357, 258, 100, 896, 897, 140, 140, 355, + /* 1630 */ 1306, 1014, 1014, 1016, 1017, 27, 139, 139, 362, 451, + /* 1640 */ 137, 137, 138, 138, 1014, 1014, 1016, 1017, 27, 1176, + /* 1650 */ 447, 568, 372, 288, 111, 560, 1018, 4, 392, 392, + /* 1660 */ 391, 273, 389, 568, 1137, 847, 568, 1072, 568, 258, + /* 1670 */ 492, 563, 568, 211, 75, 75, 555, 960, 234, 261, + /* 1680 */ 323, 111, 560, 927, 4, 113, 77, 77, 322, 74, + /* 1690 */ 74, 42, 42, 1369, 445, 48, 48, 1414, 563, 972, + /* 1700 */ 973, 1088, 1087, 1088, 1087, 860, 557, 150, 928, 1342, + /* 1710 */ 113, 1354, 554, 1419, 1018, 1271, 1262, 1250, 236, 1249, + /* 1720 */ 1251, 445, 1587, 1339, 308, 276, 168, 309, 11, 141, + /* 1730 */ 393, 310, 232, 557, 1401, 1024, 335, 291, 1396, 219, + /* 1740 */ 336, 109, 109, 934, 297, 1406, 235, 341, 477, 110, + /* 1750 */ 502, 445, 569, 445, 1389, 1405, 1014, 400, 1289, 365, + /* 1760 */ 223, 1480, 1024, 1479, 1351, 1352, 1350, 1349, 109, 109, + /* 1770 */ 204, 1590, 1228, 558, 265, 218, 110, 205, 445, 569, + /* 1780 */ 445, 410, 387, 1014, 1527, 179, 316, 559, 1014, 1014, + /* 1790 */ 1016, 1017, 27, 230, 1525, 1225, 79, 560, 85, 4, + /* 1800 */ 418, 215, 548, 81, 84, 188, 1402, 173, 181, 461, + /* 1810 */ 451, 35, 462, 563, 183, 1014, 1014, 1016, 1017, 27, + /* 1820 */ 184, 1485, 185, 186, 495, 242, 98, 398, 1408, 36, + /* 1830 */ 1407, 484, 91, 469, 401, 1410, 445, 192, 1474, 246, + /* 1840 */ 1496, 490, 346, 277, 248, 196, 493, 511, 557, 350, + /* 1850 */ 1252, 249, 250, 403, 1309, 1308, 111, 560, 432, 4, + /* 1860 */ 1307, 1300, 93, 1604, 881, 1603, 224, 404, 434, 520, + /* 1870 */ 263, 435, 1573, 563, 1279, 1278, 364, 1024, 306, 1277, + /* 1880 */ 264, 1602, 1559, 109, 109, 370, 1299, 307, 1558, 438, + /* 1890 */ 128, 110, 1374, 445, 569, 445, 445, 546, 1014, 10, + /* 1900 */ 1461, 105, 381, 1373, 34, 571, 99, 1332, 557, 314, + /* 1910 */ 1182, 530, 272, 274, 379, 210, 1331, 547, 385, 386, + /* 1920 */ 275, 572, 1247, 1242, 411, 412, 1512, 165, 178, 1513, + /* 1930 */ 1014, 1014, 1016, 1017, 27, 1511, 1510, 1024, 78, 147, + /* 1940 */ 166, 220, 221, 109, 109, 834, 304, 167, 446, 212, + /* 1950 */ 318, 110, 231, 445, 569, 445, 144, 1086, 1014, 1084, + /* 1960 */ 326, 180, 169, 1207, 182, 334, 238, 913, 241, 1100, /* 1970 */ 187, 170, 171, 421, 87, 88, 423, 189, 89, 90, - /* 1980 */ 172, 1107, 243, 1103, 244, 158, 18, 245, 345, 247, - /* 1990 */ 1017, 1017, 1019, 1020, 27, 261, 1096, 193, 1226, 489, - /* 2000 */ 194, 37, 366, 851, 494, 251, 195, 506, 92, 19, - /* 2010 */ 498, 358, 20, 503, 881, 361, 94, 894, 305, 159, - /* 2020 */ 513, 39, 95, 1174, 160, 1056, 966, 1143, 96, 174, - /* 2030 */ 1142, 225, 280, 282, 198, 960, 113, 1164, 1160, 260, - /* 2040 */ 21, 22, 23, 1162, 1168, 1167, 1148, 24, 33, 25, - /* 2050 */ 202, 542, 26, 100, 1071, 102, 1057, 103, 7, 1055, - /* 2060 */ 1059, 1113, 1060, 1112, 266, 267, 28, 40, 390, 1022, - /* 2070 */ 863, 112, 29, 564, 1182, 1181, 268, 176, 143, 925, - /* 2080 */ 1242, 1242, 1242, 1242, 1242, 1242, 1242, 1242, 1242, 1242, - /* 2090 */ 1242, 1242, 1242, 1242, 269, 1602, 1242, 1601, + /* 1980 */ 172, 1103, 243, 1099, 244, 158, 18, 245, 345, 247, + /* 1990 */ 1014, 1014, 1016, 1017, 27, 261, 1092, 193, 1222, 489, + /* 2000 */ 194, 37, 366, 849, 494, 251, 195, 506, 92, 19, + /* 2010 */ 498, 358, 20, 503, 879, 361, 94, 892, 305, 159, + /* 2020 */ 513, 39, 95, 1170, 160, 1053, 964, 1139, 96, 174, + /* 2030 */ 1138, 225, 280, 282, 198, 958, 113, 1160, 1156, 260, + /* 2040 */ 21, 22, 23, 1158, 1164, 1163, 1144, 24, 33, 25, + /* 2050 */ 202, 542, 26, 100, 1067, 102, 1054, 103, 7, 1052, + /* 2060 */ 1056, 1109, 1057, 1108, 266, 267, 28, 40, 390, 1019, + /* 2070 */ 861, 112, 29, 564, 1178, 1177, 268, 176, 143, 923, + /* 2080 */ 1238, 1238, 1238, 1238, 1238, 1238, 1238, 1238, 1238, 1238, + /* 2090 */ 1238, 1238, 1238, 1238, 269, 1595, }; static const YYCODETYPE yy_lookahead[] = { /* 0 */ 193, 193, 193, 274, 275, 276, 193, 274, 275, 276, @@ -167822,7 +168729,7 @@ static const YYCODETYPE yy_lookahead[] = { /* 2060 */ 23, 23, 11, 23, 25, 22, 22, 22, 15, 23, /* 2070 */ 23, 22, 22, 25, 1, 1, 141, 25, 23, 135, /* 2080 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, - /* 2090 */ 319, 319, 319, 319, 141, 141, 319, 141, 319, 319, + /* 2090 */ 319, 319, 319, 319, 141, 141, 319, 319, 319, 319, /* 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, @@ -167841,9 +168748,9 @@ static const YYCODETYPE yy_lookahead[] = { /* 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, + /* 2280 */ 319, }; -#define YY_SHIFT_COUNT (575) +#define YY_SHIFT_COUNT (574) #define YY_SHIFT_MIN (0) #define YY_SHIFT_MAX (2074) static const unsigned short int yy_shift_ofst[] = { @@ -167863,12 +168770,12 @@ static const unsigned short int yy_shift_ofst[] = { /* 130 */ 137, 181, 181, 181, 181, 181, 181, 181, 94, 430, /* 140 */ 66, 65, 112, 366, 533, 533, 740, 1261, 533, 533, /* 150 */ 79, 79, 533, 412, 412, 412, 77, 412, 123, 113, - /* 160 */ 113, 22, 22, 2098, 2098, 328, 328, 328, 239, 468, + /* 160 */ 113, 22, 22, 2096, 2096, 328, 328, 328, 239, 468, /* 170 */ 468, 468, 468, 1015, 1015, 409, 366, 1129, 1186, 533, /* 180 */ 533, 533, 533, 533, 533, 533, 533, 533, 533, 533, /* 190 */ 533, 533, 533, 533, 533, 533, 533, 533, 533, 969, /* 200 */ 621, 621, 533, 642, 788, 788, 1228, 1228, 822, 822, - /* 210 */ 67, 1274, 2098, 2098, 2098, 2098, 2098, 2098, 2098, 1307, + /* 210 */ 67, 1274, 2096, 2096, 2096, 2096, 2096, 2096, 2096, 1307, /* 220 */ 954, 954, 585, 472, 640, 387, 695, 538, 541, 700, /* 230 */ 533, 533, 533, 533, 533, 533, 533, 533, 533, 533, /* 240 */ 222, 533, 533, 533, 533, 533, 533, 533, 533, 533, @@ -167886,8 +168793,8 @@ static const unsigned short int yy_shift_ofst[] = { /* 360 */ 1840, 1840, 1823, 1732, 1738, 1732, 1794, 1732, 1732, 1701, /* 370 */ 1844, 1758, 1758, 1823, 1633, 1789, 1789, 1807, 1807, 1742, /* 380 */ 1752, 1877, 1633, 1743, 1742, 1759, 1765, 1677, 1879, 1897, - /* 390 */ 1897, 1914, 1914, 1914, 2098, 2098, 2098, 2098, 2098, 2098, - /* 400 */ 2098, 2098, 2098, 2098, 2098, 2098, 2098, 2098, 2098, 207, + /* 390 */ 1897, 1914, 1914, 1914, 2096, 2096, 2096, 2096, 2096, 2096, + /* 400 */ 2096, 2096, 2096, 2096, 2096, 2096, 2096, 2096, 2096, 207, /* 410 */ 1095, 331, 620, 903, 806, 1074, 1483, 1432, 1481, 1322, /* 420 */ 1370, 1394, 1515, 1291, 1546, 1547, 1557, 1595, 1598, 1599, /* 430 */ 1434, 1453, 1618, 1462, 1567, 1489, 1644, 1654, 1616, 1660, @@ -167904,7 +168811,7 @@ static const unsigned short int yy_shift_ofst[] = { /* 540 */ 2015, 2023, 2026, 2027, 2025, 2028, 2018, 1913, 1915, 2031, /* 550 */ 2011, 2033, 2036, 2037, 2038, 2039, 2040, 2043, 2051, 2044, /* 560 */ 2045, 2046, 2047, 2049, 2050, 2048, 1944, 1935, 1953, 1954, - /* 570 */ 1956, 2052, 2055, 2053, 2073, 2074, + /* 570 */ 2052, 2055, 2053, 2073, 2074, }; #define YY_REDUCE_COUNT (408) #define YY_REDUCE_MIN (-271) @@ -167953,64 +168860,64 @@ static const short yy_reduce_ofst[] = { /* 400 */ 1722, 1723, 1733, 1717, 1724, 1727, 1728, 1725, 1740, }; static const YYACTIONTYPE yy_default[] = { - /* 0 */ 1647, 1647, 1647, 1475, 1240, 1351, 1240, 1240, 1240, 1475, - /* 10 */ 1475, 1475, 1240, 1381, 1381, 1528, 1273, 1240, 1240, 1240, - /* 20 */ 1240, 1240, 1240, 1240, 1240, 1240, 1240, 1474, 1240, 1240, - /* 30 */ 1240, 1240, 1563, 1563, 1240, 1240, 1240, 1240, 1240, 1240, - /* 40 */ 1240, 1240, 1390, 1240, 1397, 1240, 1240, 1240, 1240, 1240, - /* 50 */ 1476, 1477, 1240, 1240, 1240, 1527, 1529, 1492, 1404, 1403, - /* 60 */ 1402, 1401, 1510, 1369, 1395, 1388, 1392, 1470, 1471, 1469, - /* 70 */ 1473, 1477, 1476, 1240, 1391, 1438, 1454, 1437, 1240, 1240, - /* 80 */ 1240, 1240, 1240, 1240, 1240, 1240, 1240, 1240, 1240, 1240, - /* 90 */ 1240, 1240, 1240, 1240, 1240, 1240, 1240, 1240, 1240, 1240, - /* 100 */ 1240, 1240, 1240, 1240, 1240, 1240, 1240, 1240, 1240, 1240, - /* 110 */ 1240, 1240, 1240, 1240, 1240, 1240, 1240, 1240, 1240, 1240, - /* 120 */ 1240, 1240, 1240, 1240, 1240, 1240, 1240, 1240, 1240, 1240, - /* 130 */ 1446, 1453, 1452, 1451, 1460, 1450, 1447, 1440, 1439, 1441, - /* 140 */ 1442, 1240, 1240, 1264, 1240, 1240, 1261, 1315, 1240, 1240, - /* 150 */ 1240, 1240, 1240, 1547, 1546, 1240, 1443, 1240, 1273, 1432, - /* 160 */ 1431, 1457, 1444, 1456, 1455, 1535, 1599, 1598, 1493, 1240, - /* 170 */ 1240, 1240, 1240, 1240, 1240, 1563, 1240, 1240, 1240, 1240, - /* 180 */ 1240, 1240, 1240, 1240, 1240, 1240, 1240, 1240, 1240, 1240, - /* 190 */ 1240, 1240, 1240, 1240, 1240, 1240, 1240, 1240, 1240, 1371, - /* 200 */ 1563, 1563, 1240, 1273, 1563, 1563, 1372, 1372, 1269, 1269, - /* 210 */ 1375, 1240, 1542, 1342, 1342, 1342, 1342, 1351, 1342, 1240, - /* 220 */ 1240, 1240, 1240, 1240, 1240, 1240, 1240, 1240, 1240, 1240, - /* 230 */ 1240, 1240, 1240, 1240, 1532, 1530, 1240, 1240, 1240, 1240, - /* 240 */ 1240, 1240, 1240, 1240, 1240, 1240, 1240, 1240, 1240, 1240, - /* 250 */ 1240, 1240, 1240, 1240, 1240, 1240, 1240, 1240, 1240, 1240, - /* 260 */ 1240, 1240, 1240, 1347, 1240, 1240, 1240, 1240, 1240, 1240, - /* 270 */ 1240, 1240, 1240, 1240, 1240, 1592, 1240, 1505, 1329, 1347, - /* 280 */ 1347, 1347, 1347, 1349, 1330, 1328, 1341, 1274, 1247, 1639, - /* 290 */ 1407, 1396, 1348, 1396, 1636, 1394, 1407, 1407, 1394, 1407, - /* 300 */ 1348, 1636, 1290, 1615, 1285, 1381, 1381, 1381, 1371, 1371, - /* 310 */ 1371, 1371, 1375, 1375, 1472, 1348, 1341, 1240, 1639, 1639, - /* 320 */ 1357, 1357, 1638, 1638, 1357, 1493, 1623, 1416, 1318, 1324, - /* 330 */ 1324, 1324, 1324, 1357, 1258, 1394, 1623, 1623, 1394, 1416, - /* 340 */ 1318, 1394, 1318, 1394, 1357, 1258, 1509, 1633, 1357, 1258, - /* 350 */ 1483, 1357, 1258, 1357, 1258, 1483, 1316, 1316, 1316, 1305, - /* 360 */ 1240, 1240, 1483, 1316, 1290, 1316, 1305, 1316, 1316, 1581, - /* 370 */ 1240, 1487, 1487, 1483, 1357, 1573, 1573, 1384, 1384, 1389, - /* 380 */ 1375, 1478, 1357, 1240, 1389, 1387, 1385, 1394, 1308, 1595, - /* 390 */ 1595, 1591, 1591, 1591, 1644, 1644, 1542, 1608, 1273, 1273, - /* 400 */ 1273, 1273, 1608, 1292, 1292, 1274, 1274, 1273, 1608, 1240, - /* 410 */ 1240, 1240, 1240, 1240, 1240, 1603, 1240, 1537, 1494, 1361, - /* 420 */ 1240, 1240, 1240, 1240, 1240, 1240, 1240, 1240, 1240, 1240, - /* 430 */ 1240, 1240, 1240, 1240, 1548, 1240, 1240, 1240, 1240, 1240, - /* 440 */ 1240, 1240, 1240, 1240, 1240, 1421, 1240, 1243, 1539, 1240, - /* 450 */ 1240, 1240, 1240, 1240, 1240, 1240, 1240, 1398, 1399, 1362, - /* 460 */ 1240, 1240, 1240, 1240, 1240, 1240, 1240, 1413, 1240, 1240, - /* 470 */ 1240, 1408, 1240, 1240, 1240, 1240, 1240, 1240, 1240, 1240, - /* 480 */ 1635, 1240, 1240, 1240, 1240, 1240, 1240, 1508, 1507, 1240, - /* 490 */ 1240, 1359, 1240, 1240, 1240, 1240, 1240, 1240, 1240, 1240, - /* 500 */ 1240, 1240, 1240, 1240, 1240, 1288, 1240, 1240, 1240, 1240, - /* 510 */ 1240, 1240, 1240, 1240, 1240, 1240, 1240, 1240, 1240, 1240, - /* 520 */ 1240, 1240, 1240, 1240, 1240, 1240, 1240, 1240, 1240, 1386, - /* 530 */ 1240, 1240, 1240, 1240, 1240, 1240, 1240, 1240, 1240, 1240, - /* 540 */ 1240, 1240, 1240, 1240, 1578, 1376, 1240, 1240, 1240, 1240, - /* 550 */ 1626, 1240, 1240, 1240, 1240, 1240, 1240, 1240, 1240, 1240, - /* 560 */ 1240, 1240, 1240, 1240, 1240, 1619, 1332, 1423, 1240, 1422, - /* 570 */ 1426, 1262, 1240, 1252, 1240, 1240, + /* 0 */ 1641, 1641, 1641, 1469, 1236, 1347, 1236, 1236, 1236, 1469, + /* 10 */ 1469, 1469, 1236, 1377, 1377, 1522, 1269, 1236, 1236, 1236, + /* 20 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1468, 1236, 1236, + /* 30 */ 1236, 1236, 1557, 1557, 1236, 1236, 1236, 1236, 1236, 1236, + /* 40 */ 1236, 1236, 1386, 1236, 1393, 1236, 1236, 1236, 1236, 1236, + /* 50 */ 1470, 1471, 1236, 1236, 1236, 1521, 1523, 1486, 1400, 1399, + /* 60 */ 1398, 1397, 1504, 1365, 1391, 1384, 1388, 1465, 1466, 1464, + /* 70 */ 1619, 1471, 1470, 1236, 1387, 1433, 1449, 1432, 1236, 1236, + /* 80 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, + /* 90 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, + /* 100 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, + /* 110 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, + /* 120 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, + /* 130 */ 1441, 1448, 1447, 1446, 1455, 1445, 1442, 1435, 1434, 1436, + /* 140 */ 1437, 1236, 1236, 1260, 1236, 1236, 1257, 1311, 1236, 1236, + /* 150 */ 1236, 1236, 1236, 1541, 1540, 1236, 1438, 1236, 1269, 1427, + /* 160 */ 1426, 1452, 1439, 1451, 1450, 1529, 1593, 1592, 1487, 1236, + /* 170 */ 1236, 1236, 1236, 1236, 1236, 1557, 1236, 1236, 1236, 1236, + /* 180 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, + /* 190 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1367, + /* 200 */ 1557, 1557, 1236, 1269, 1557, 1557, 1368, 1368, 1265, 1265, + /* 210 */ 1371, 1236, 1536, 1338, 1338, 1338, 1338, 1347, 1338, 1236, + /* 220 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, + /* 230 */ 1236, 1236, 1236, 1236, 1526, 1524, 1236, 1236, 1236, 1236, + /* 240 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, + /* 250 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, + /* 260 */ 1236, 1236, 1236, 1343, 1236, 1236, 1236, 1236, 1236, 1236, + /* 270 */ 1236, 1236, 1236, 1236, 1236, 1586, 1236, 1499, 1325, 1343, + /* 280 */ 1343, 1343, 1343, 1345, 1326, 1324, 1337, 1270, 1243, 1633, + /* 290 */ 1403, 1392, 1344, 1392, 1630, 1390, 1403, 1403, 1390, 1403, + /* 300 */ 1344, 1630, 1286, 1608, 1281, 1377, 1377, 1377, 1367, 1367, + /* 310 */ 1367, 1367, 1371, 1371, 1467, 1344, 1337, 1236, 1633, 1633, + /* 320 */ 1353, 1353, 1632, 1632, 1353, 1487, 1616, 1412, 1314, 1320, + /* 330 */ 1320, 1320, 1320, 1353, 1254, 1390, 1616, 1616, 1390, 1412, + /* 340 */ 1314, 1390, 1314, 1390, 1353, 1254, 1503, 1627, 1353, 1254, + /* 350 */ 1477, 1353, 1254, 1353, 1254, 1477, 1312, 1312, 1312, 1301, + /* 360 */ 1236, 1236, 1477, 1312, 1286, 1312, 1301, 1312, 1312, 1575, + /* 370 */ 1236, 1481, 1481, 1477, 1353, 1567, 1567, 1380, 1380, 1385, + /* 380 */ 1371, 1472, 1353, 1236, 1385, 1383, 1381, 1390, 1304, 1589, + /* 390 */ 1589, 1585, 1585, 1585, 1638, 1638, 1536, 1601, 1269, 1269, + /* 400 */ 1269, 1269, 1601, 1288, 1288, 1270, 1270, 1269, 1601, 1236, + /* 410 */ 1236, 1236, 1236, 1236, 1236, 1596, 1236, 1531, 1488, 1357, + /* 420 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, + /* 430 */ 1236, 1236, 1236, 1236, 1542, 1236, 1236, 1236, 1236, 1236, + /* 440 */ 1236, 1236, 1236, 1236, 1236, 1417, 1236, 1239, 1533, 1236, + /* 450 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1394, 1395, 1358, + /* 460 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1409, 1236, 1236, + /* 470 */ 1236, 1404, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, + /* 480 */ 1629, 1236, 1236, 1236, 1236, 1236, 1236, 1502, 1501, 1236, + /* 490 */ 1236, 1355, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, + /* 500 */ 1236, 1236, 1236, 1236, 1236, 1284, 1236, 1236, 1236, 1236, + /* 510 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, + /* 520 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1382, + /* 530 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, + /* 540 */ 1236, 1236, 1236, 1236, 1572, 1372, 1236, 1236, 1236, 1236, + /* 550 */ 1620, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, + /* 560 */ 1236, 1236, 1236, 1236, 1236, 1612, 1328, 1418, 1236, 1421, + /* 570 */ 1258, 1236, 1248, 1236, 1236, }; /********** End of lemon-generated parsing tables *****************************/ @@ -168807,233 +169714,231 @@ static const char *const yyRuleName[] = { /* 175 */ "idlist ::= idlist COMMA nm", /* 176 */ "idlist ::= nm", /* 177 */ "expr ::= LP expr RP", - /* 178 */ "expr ::= ID|INDEXED", - /* 179 */ "expr ::= JOIN_KW", - /* 180 */ "expr ::= nm DOT nm", - /* 181 */ "expr ::= nm DOT nm DOT nm", - /* 182 */ "term ::= NULL|FLOAT|BLOB", - /* 183 */ "term ::= STRING", - /* 184 */ "term ::= INTEGER", - /* 185 */ "expr ::= VARIABLE", - /* 186 */ "expr ::= expr COLLATE ID|STRING", - /* 187 */ "expr ::= CAST LP expr AS typetoken RP", - /* 188 */ "expr ::= ID|INDEXED LP distinct exprlist RP", - /* 189 */ "expr ::= ID|INDEXED LP STAR RP", - /* 190 */ "expr ::= ID|INDEXED LP distinct exprlist RP filter_over", - /* 191 */ "expr ::= ID|INDEXED LP STAR RP filter_over", - /* 192 */ "term ::= CTIME_KW", - /* 193 */ "expr ::= LP nexprlist COMMA expr RP", - /* 194 */ "expr ::= expr AND expr", - /* 195 */ "expr ::= expr OR expr", - /* 196 */ "expr ::= expr LT|GT|GE|LE expr", - /* 197 */ "expr ::= expr EQ|NE expr", - /* 198 */ "expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr", - /* 199 */ "expr ::= expr PLUS|MINUS expr", - /* 200 */ "expr ::= expr STAR|SLASH|REM expr", - /* 201 */ "expr ::= expr CONCAT expr", - /* 202 */ "likeop ::= NOT LIKE_KW|MATCH", - /* 203 */ "expr ::= expr likeop expr", - /* 204 */ "expr ::= expr likeop expr ESCAPE expr", - /* 205 */ "expr ::= expr ISNULL|NOTNULL", - /* 206 */ "expr ::= expr NOT NULL", - /* 207 */ "expr ::= expr IS expr", - /* 208 */ "expr ::= expr IS NOT expr", - /* 209 */ "expr ::= expr IS NOT DISTINCT FROM expr", - /* 210 */ "expr ::= expr IS DISTINCT FROM expr", - /* 211 */ "expr ::= NOT expr", - /* 212 */ "expr ::= BITNOT expr", - /* 213 */ "expr ::= PLUS|MINUS expr", - /* 214 */ "expr ::= expr PTR expr", - /* 215 */ "between_op ::= BETWEEN", - /* 216 */ "between_op ::= NOT BETWEEN", - /* 217 */ "expr ::= expr between_op expr AND expr", - /* 218 */ "in_op ::= IN", - /* 219 */ "in_op ::= NOT IN", - /* 220 */ "expr ::= expr in_op LP exprlist RP", - /* 221 */ "expr ::= LP select RP", - /* 222 */ "expr ::= expr in_op LP select RP", - /* 223 */ "expr ::= expr in_op nm dbnm paren_exprlist", - /* 224 */ "expr ::= EXISTS LP select RP", - /* 225 */ "expr ::= CASE case_operand case_exprlist case_else END", - /* 226 */ "case_exprlist ::= case_exprlist WHEN expr THEN expr", - /* 227 */ "case_exprlist ::= WHEN expr THEN expr", - /* 228 */ "case_else ::= ELSE expr", - /* 229 */ "case_else ::=", - /* 230 */ "case_operand ::= expr", - /* 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", - /* 312 */ "windowdefn_list ::= windowdefn_list COMMA windowdefn", - /* 313 */ "windowdefn ::= nm AS LP window RP", - /* 314 */ "window ::= PARTITION BY nexprlist orderby_opt frame_opt", - /* 315 */ "window ::= nm PARTITION BY nexprlist orderby_opt frame_opt", - /* 316 */ "window ::= ORDER BY sortlist frame_opt", - /* 317 */ "window ::= nm ORDER BY sortlist frame_opt", - /* 318 */ "window ::= frame_opt", - /* 319 */ "window ::= nm frame_opt", - /* 320 */ "frame_opt ::=", - /* 321 */ "frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt", - /* 322 */ "frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt", - /* 323 */ "range_or_rows ::= RANGE|ROWS|GROUPS", - /* 324 */ "frame_bound_s ::= frame_bound", - /* 325 */ "frame_bound_s ::= UNBOUNDED PRECEDING", - /* 326 */ "frame_bound_e ::= frame_bound", - /* 327 */ "frame_bound_e ::= UNBOUNDED FOLLOWING", - /* 328 */ "frame_bound ::= expr PRECEDING|FOLLOWING", - /* 329 */ "frame_bound ::= CURRENT ROW", - /* 330 */ "frame_exclude_opt ::=", - /* 331 */ "frame_exclude_opt ::= EXCLUDE frame_exclude", - /* 332 */ "frame_exclude ::= NO OTHERS", - /* 333 */ "frame_exclude ::= CURRENT ROW", - /* 334 */ "frame_exclude ::= GROUP|TIES", - /* 335 */ "window_clause ::= WINDOW windowdefn_list", - /* 336 */ "filter_over ::= filter_clause over_clause", - /* 337 */ "filter_over ::= over_clause", - /* 338 */ "filter_over ::= filter_clause", - /* 339 */ "over_clause ::= OVER LP window RP", - /* 340 */ "over_clause ::= OVER nm", - /* 341 */ "filter_clause ::= FILTER LP WHERE expr RP", - /* 342 */ "input ::= cmdlist", - /* 343 */ "cmdlist ::= cmdlist ecmd", - /* 344 */ "cmdlist ::= ecmd", - /* 345 */ "ecmd ::= SEMI", - /* 346 */ "ecmd ::= cmdx SEMI", - /* 347 */ "ecmd ::= explain cmdx SEMI", - /* 348 */ "trans_opt ::=", - /* 349 */ "trans_opt ::= TRANSACTION", - /* 350 */ "trans_opt ::= TRANSACTION nm", - /* 351 */ "savepoint_opt ::= SAVEPOINT", - /* 352 */ "savepoint_opt ::=", - /* 353 */ "cmd ::= create_table create_table_args", - /* 354 */ "table_option_set ::= table_option", - /* 355 */ "columnlist ::= columnlist COMMA columnname carglist", - /* 356 */ "columnlist ::= columnname carglist", - /* 357 */ "nm ::= ID|INDEXED", - /* 358 */ "nm ::= STRING", - /* 359 */ "nm ::= JOIN_KW", - /* 360 */ "typetoken ::= typename", - /* 361 */ "typename ::= ID|STRING", - /* 362 */ "signed ::= plus_num", - /* 363 */ "signed ::= minus_num", - /* 364 */ "carglist ::= carglist ccons", - /* 365 */ "carglist ::=", - /* 366 */ "ccons ::= NULL onconf", - /* 367 */ "ccons ::= GENERATED ALWAYS AS generated", - /* 368 */ "ccons ::= AS generated", - /* 369 */ "conslist_opt ::= COMMA conslist", - /* 370 */ "conslist ::= conslist tconscomma tcons", - /* 371 */ "conslist ::= tcons", - /* 372 */ "tconscomma ::=", - /* 373 */ "defer_subclause_opt ::= defer_subclause", - /* 374 */ "resolvetype ::= raisetype", - /* 375 */ "selectnowith ::= oneselect", - /* 376 */ "oneselect ::= values", - /* 377 */ "sclp ::= selcollist COMMA", - /* 378 */ "as ::= ID|STRING", - /* 379 */ "indexed_opt ::= indexed_by", - /* 380 */ "returning ::=", - /* 381 */ "expr ::= term", - /* 382 */ "likeop ::= LIKE_KW|MATCH", - /* 383 */ "exprlist ::= nexprlist", - /* 384 */ "nmnum ::= plus_num", - /* 385 */ "nmnum ::= nm", - /* 386 */ "nmnum ::= ON", - /* 387 */ "nmnum ::= DELETE", - /* 388 */ "nmnum ::= DEFAULT", - /* 389 */ "plus_num ::= INTEGER|FLOAT", - /* 390 */ "foreach_clause ::=", - /* 391 */ "foreach_clause ::= FOR EACH ROW", - /* 392 */ "trnm ::= nm", - /* 393 */ "tridxby ::=", - /* 394 */ "database_kw_opt ::= DATABASE", - /* 395 */ "database_kw_opt ::=", - /* 396 */ "kwcolumn_opt ::=", - /* 397 */ "kwcolumn_opt ::= COLUMNKW", - /* 398 */ "vtabarglist ::= vtabarg", - /* 399 */ "vtabarglist ::= vtabarglist COMMA vtabarg", - /* 400 */ "vtabarg ::= vtabarg vtabargtoken", - /* 401 */ "anylist ::=", - /* 402 */ "anylist ::= anylist LP anylist RP", - /* 403 */ "anylist ::= anylist ANY", - /* 404 */ "with ::=", + /* 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 STAR RP", + /* 189 */ "expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP filter_over", + /* 190 */ "expr ::= ID|INDEXED|JOIN_KW LP STAR RP filter_over", + /* 191 */ "term ::= CTIME_KW", + /* 192 */ "expr ::= LP nexprlist COMMA expr RP", + /* 193 */ "expr ::= expr AND expr", + /* 194 */ "expr ::= expr OR expr", + /* 195 */ "expr ::= expr LT|GT|GE|LE expr", + /* 196 */ "expr ::= expr EQ|NE expr", + /* 197 */ "expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr", + /* 198 */ "expr ::= expr PLUS|MINUS expr", + /* 199 */ "expr ::= expr STAR|SLASH|REM expr", + /* 200 */ "expr ::= expr CONCAT expr", + /* 201 */ "likeop ::= NOT LIKE_KW|MATCH", + /* 202 */ "expr ::= expr likeop expr", + /* 203 */ "expr ::= expr likeop expr ESCAPE expr", + /* 204 */ "expr ::= expr ISNULL|NOTNULL", + /* 205 */ "expr ::= expr NOT NULL", + /* 206 */ "expr ::= expr IS expr", + /* 207 */ "expr ::= expr IS NOT expr", + /* 208 */ "expr ::= expr IS NOT DISTINCT FROM expr", + /* 209 */ "expr ::= expr IS DISTINCT FROM expr", + /* 210 */ "expr ::= NOT expr", + /* 211 */ "expr ::= BITNOT expr", + /* 212 */ "expr ::= PLUS|MINUS expr", + /* 213 */ "expr ::= expr PTR expr", + /* 214 */ "between_op ::= BETWEEN", + /* 215 */ "between_op ::= NOT BETWEEN", + /* 216 */ "expr ::= expr between_op expr AND expr", + /* 217 */ "in_op ::= IN", + /* 218 */ "in_op ::= NOT IN", + /* 219 */ "expr ::= expr in_op LP exprlist RP", + /* 220 */ "expr ::= LP select RP", + /* 221 */ "expr ::= expr in_op LP select RP", + /* 222 */ "expr ::= expr in_op nm dbnm paren_exprlist", + /* 223 */ "expr ::= EXISTS LP select RP", + /* 224 */ "expr ::= CASE case_operand case_exprlist case_else END", + /* 225 */ "case_exprlist ::= case_exprlist WHEN expr THEN expr", + /* 226 */ "case_exprlist ::= WHEN expr THEN expr", + /* 227 */ "case_else ::= ELSE expr", + /* 228 */ "case_else ::=", + /* 229 */ "case_operand ::=", + /* 230 */ "exprlist ::=", + /* 231 */ "nexprlist ::= nexprlist COMMA expr", + /* 232 */ "nexprlist ::= expr", + /* 233 */ "paren_exprlist ::=", + /* 234 */ "paren_exprlist ::= LP exprlist RP", + /* 235 */ "cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt", + /* 236 */ "uniqueflag ::= UNIQUE", + /* 237 */ "uniqueflag ::=", + /* 238 */ "eidlist_opt ::=", + /* 239 */ "eidlist_opt ::= LP eidlist RP", + /* 240 */ "eidlist ::= eidlist COMMA nm collate sortorder", + /* 241 */ "eidlist ::= nm collate sortorder", + /* 242 */ "collate ::=", + /* 243 */ "collate ::= COLLATE ID|STRING", + /* 244 */ "cmd ::= DROP INDEX ifexists fullname", + /* 245 */ "cmd ::= VACUUM vinto", + /* 246 */ "cmd ::= VACUUM nm vinto", + /* 247 */ "vinto ::= INTO expr", + /* 248 */ "vinto ::=", + /* 249 */ "cmd ::= PRAGMA nm dbnm", + /* 250 */ "cmd ::= PRAGMA nm dbnm EQ nmnum", + /* 251 */ "cmd ::= PRAGMA nm dbnm LP nmnum RP", + /* 252 */ "cmd ::= PRAGMA nm dbnm EQ minus_num", + /* 253 */ "cmd ::= PRAGMA nm dbnm LP minus_num RP", + /* 254 */ "plus_num ::= PLUS INTEGER|FLOAT", + /* 255 */ "minus_num ::= MINUS INTEGER|FLOAT", + /* 256 */ "cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END", + /* 257 */ "trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause", + /* 258 */ "trigger_time ::= BEFORE|AFTER", + /* 259 */ "trigger_time ::= INSTEAD OF", + /* 260 */ "trigger_time ::=", + /* 261 */ "trigger_event ::= DELETE|INSERT", + /* 262 */ "trigger_event ::= UPDATE", + /* 263 */ "trigger_event ::= UPDATE OF idlist", + /* 264 */ "when_clause ::=", + /* 265 */ "when_clause ::= WHEN expr", + /* 266 */ "trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI", + /* 267 */ "trigger_cmd_list ::= trigger_cmd SEMI", + /* 268 */ "trnm ::= nm DOT nm", + /* 269 */ "tridxby ::= INDEXED BY nm", + /* 270 */ "tridxby ::= NOT INDEXED", + /* 271 */ "trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt", + /* 272 */ "trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt", + /* 273 */ "trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt", + /* 274 */ "trigger_cmd ::= scanpt select scanpt", + /* 275 */ "expr ::= RAISE LP IGNORE RP", + /* 276 */ "expr ::= RAISE LP raisetype COMMA nm RP", + /* 277 */ "raisetype ::= ROLLBACK", + /* 278 */ "raisetype ::= ABORT", + /* 279 */ "raisetype ::= FAIL", + /* 280 */ "cmd ::= DROP TRIGGER ifexists fullname", + /* 281 */ "cmd ::= ATTACH database_kw_opt expr AS expr key_opt", + /* 282 */ "cmd ::= DETACH database_kw_opt expr", + /* 283 */ "key_opt ::=", + /* 284 */ "key_opt ::= KEY expr", + /* 285 */ "cmd ::= REINDEX", + /* 286 */ "cmd ::= REINDEX nm dbnm", + /* 287 */ "cmd ::= ANALYZE", + /* 288 */ "cmd ::= ANALYZE nm dbnm", + /* 289 */ "cmd ::= ALTER TABLE fullname RENAME TO nm", + /* 290 */ "cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist", + /* 291 */ "cmd ::= ALTER TABLE fullname DROP kwcolumn_opt nm", + /* 292 */ "add_column_fullname ::= fullname", + /* 293 */ "cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm", + /* 294 */ "cmd ::= create_vtab", + /* 295 */ "cmd ::= create_vtab LP vtabarglist RP", + /* 296 */ "create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm", + /* 297 */ "vtabarg ::=", + /* 298 */ "vtabargtoken ::= ANY", + /* 299 */ "vtabargtoken ::= lp anylist RP", + /* 300 */ "lp ::= LP", + /* 301 */ "with ::= WITH wqlist", + /* 302 */ "with ::= WITH RECURSIVE wqlist", + /* 303 */ "wqas ::= AS", + /* 304 */ "wqas ::= AS MATERIALIZED", + /* 305 */ "wqas ::= AS NOT MATERIALIZED", + /* 306 */ "wqitem ::= nm eidlist_opt wqas LP select RP", + /* 307 */ "wqlist ::= wqitem", + /* 308 */ "wqlist ::= wqlist COMMA wqitem", + /* 309 */ "windowdefn_list ::= windowdefn", + /* 310 */ "windowdefn_list ::= windowdefn_list COMMA windowdefn", + /* 311 */ "windowdefn ::= nm AS LP window RP", + /* 312 */ "window ::= PARTITION BY nexprlist orderby_opt frame_opt", + /* 313 */ "window ::= nm PARTITION BY nexprlist orderby_opt frame_opt", + /* 314 */ "window ::= ORDER BY sortlist frame_opt", + /* 315 */ "window ::= nm ORDER BY sortlist frame_opt", + /* 316 */ "window ::= 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 ::=", }; #endif /* NDEBUG */ @@ -169718,233 +170623,231 @@ static const YYCODETYPE yyRuleInfoLhs[] = { 263, /* (175) idlist ::= idlist COMMA nm */ 263, /* (176) idlist ::= nm */ 217, /* (177) expr ::= LP expr RP */ - 217, /* (178) expr ::= ID|INDEXED */ - 217, /* (179) expr ::= JOIN_KW */ - 217, /* (180) expr ::= nm DOT nm */ - 217, /* (181) expr ::= nm DOT nm DOT nm */ - 216, /* (182) term ::= NULL|FLOAT|BLOB */ - 216, /* (183) term ::= STRING */ - 216, /* (184) term ::= INTEGER */ - 217, /* (185) expr ::= VARIABLE */ - 217, /* (186) expr ::= expr COLLATE ID|STRING */ - 217, /* (187) expr ::= CAST LP expr AS typetoken RP */ - 217, /* (188) expr ::= ID|INDEXED LP distinct exprlist RP */ - 217, /* (189) expr ::= ID|INDEXED LP STAR RP */ - 217, /* (190) expr ::= ID|INDEXED LP distinct exprlist RP filter_over */ - 217, /* (191) expr ::= ID|INDEXED LP STAR RP filter_over */ - 216, /* (192) term ::= CTIME_KW */ - 217, /* (193) expr ::= LP nexprlist COMMA expr RP */ - 217, /* (194) expr ::= expr AND expr */ - 217, /* (195) expr ::= expr OR expr */ - 217, /* (196) expr ::= expr LT|GT|GE|LE expr */ - 217, /* (197) expr ::= expr EQ|NE expr */ - 217, /* (198) expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */ - 217, /* (199) expr ::= expr PLUS|MINUS expr */ - 217, /* (200) expr ::= expr STAR|SLASH|REM expr */ - 217, /* (201) expr ::= expr CONCAT expr */ - 274, /* (202) likeop ::= NOT LIKE_KW|MATCH */ - 217, /* (203) expr ::= expr likeop expr */ - 217, /* (204) expr ::= expr likeop expr ESCAPE expr */ - 217, /* (205) expr ::= expr ISNULL|NOTNULL */ - 217, /* (206) expr ::= expr NOT NULL */ - 217, /* (207) expr ::= expr IS expr */ - 217, /* (208) expr ::= expr IS NOT expr */ - 217, /* (209) expr ::= expr IS NOT DISTINCT FROM expr */ - 217, /* (210) expr ::= expr IS DISTINCT FROM expr */ - 217, /* (211) expr ::= NOT expr */ - 217, /* (212) expr ::= BITNOT expr */ - 217, /* (213) expr ::= PLUS|MINUS expr */ - 217, /* (214) expr ::= expr PTR expr */ - 275, /* (215) between_op ::= BETWEEN */ - 275, /* (216) between_op ::= NOT BETWEEN */ - 217, /* (217) expr ::= expr between_op expr AND expr */ - 276, /* (218) in_op ::= IN */ - 276, /* (219) in_op ::= NOT IN */ - 217, /* (220) expr ::= expr in_op LP exprlist RP */ - 217, /* (221) expr ::= LP select RP */ - 217, /* (222) expr ::= expr in_op LP select RP */ - 217, /* (223) expr ::= expr in_op nm dbnm paren_exprlist */ - 217, /* (224) expr ::= EXISTS LP select RP */ - 217, /* (225) expr ::= CASE case_operand case_exprlist case_else END */ - 279, /* (226) case_exprlist ::= case_exprlist WHEN expr THEN expr */ - 279, /* (227) case_exprlist ::= WHEN expr THEN expr */ - 280, /* (228) case_else ::= ELSE expr */ - 280, /* (229) case_else ::= */ - 278, /* (230) case_operand ::= expr */ - 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 */ - 306, /* (312) windowdefn_list ::= windowdefn_list COMMA windowdefn */ - 307, /* (313) windowdefn ::= nm AS LP window RP */ - 308, /* (314) window ::= PARTITION BY nexprlist orderby_opt frame_opt */ - 308, /* (315) window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */ - 308, /* (316) window ::= ORDER BY sortlist frame_opt */ - 308, /* (317) window ::= nm ORDER BY sortlist frame_opt */ - 308, /* (318) window ::= frame_opt */ - 308, /* (319) window ::= nm frame_opt */ - 309, /* (320) frame_opt ::= */ - 309, /* (321) frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */ - 309, /* (322) frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */ - 313, /* (323) range_or_rows ::= RANGE|ROWS|GROUPS */ - 315, /* (324) frame_bound_s ::= frame_bound */ - 315, /* (325) frame_bound_s ::= UNBOUNDED PRECEDING */ - 316, /* (326) frame_bound_e ::= frame_bound */ - 316, /* (327) frame_bound_e ::= UNBOUNDED FOLLOWING */ - 314, /* (328) frame_bound ::= expr PRECEDING|FOLLOWING */ - 314, /* (329) frame_bound ::= CURRENT ROW */ - 317, /* (330) frame_exclude_opt ::= */ - 317, /* (331) frame_exclude_opt ::= EXCLUDE frame_exclude */ - 318, /* (332) frame_exclude ::= NO OTHERS */ - 318, /* (333) frame_exclude ::= CURRENT ROW */ - 318, /* (334) frame_exclude ::= GROUP|TIES */ - 251, /* (335) window_clause ::= WINDOW windowdefn_list */ - 273, /* (336) filter_over ::= filter_clause over_clause */ - 273, /* (337) filter_over ::= over_clause */ - 273, /* (338) filter_over ::= filter_clause */ - 312, /* (339) over_clause ::= OVER LP window RP */ - 312, /* (340) over_clause ::= OVER nm */ - 311, /* (341) filter_clause ::= FILTER LP WHERE expr RP */ - 185, /* (342) input ::= cmdlist */ - 186, /* (343) cmdlist ::= cmdlist ecmd */ - 186, /* (344) cmdlist ::= ecmd */ - 187, /* (345) ecmd ::= SEMI */ - 187, /* (346) ecmd ::= cmdx SEMI */ - 187, /* (347) ecmd ::= explain cmdx SEMI */ - 192, /* (348) trans_opt ::= */ - 192, /* (349) trans_opt ::= TRANSACTION */ - 192, /* (350) trans_opt ::= TRANSACTION nm */ - 194, /* (351) savepoint_opt ::= SAVEPOINT */ - 194, /* (352) savepoint_opt ::= */ - 190, /* (353) cmd ::= create_table create_table_args */ - 203, /* (354) table_option_set ::= table_option */ - 201, /* (355) columnlist ::= columnlist COMMA columnname carglist */ - 201, /* (356) columnlist ::= columnname carglist */ - 193, /* (357) nm ::= ID|INDEXED */ - 193, /* (358) nm ::= STRING */ - 193, /* (359) nm ::= JOIN_KW */ - 208, /* (360) typetoken ::= typename */ - 209, /* (361) typename ::= ID|STRING */ - 210, /* (362) signed ::= plus_num */ - 210, /* (363) signed ::= minus_num */ - 207, /* (364) carglist ::= carglist ccons */ - 207, /* (365) carglist ::= */ - 215, /* (366) ccons ::= NULL onconf */ - 215, /* (367) ccons ::= GENERATED ALWAYS AS generated */ - 215, /* (368) ccons ::= AS generated */ - 202, /* (369) conslist_opt ::= COMMA conslist */ - 228, /* (370) conslist ::= conslist tconscomma tcons */ - 228, /* (371) conslist ::= tcons */ - 229, /* (372) tconscomma ::= */ - 233, /* (373) defer_subclause_opt ::= defer_subclause */ - 235, /* (374) resolvetype ::= raisetype */ - 239, /* (375) selectnowith ::= oneselect */ - 240, /* (376) oneselect ::= values */ - 254, /* (377) sclp ::= selcollist COMMA */ - 255, /* (378) as ::= ID|STRING */ - 264, /* (379) indexed_opt ::= indexed_by */ - 272, /* (380) returning ::= */ - 217, /* (381) expr ::= term */ - 274, /* (382) likeop ::= LIKE_KW|MATCH */ - 261, /* (383) exprlist ::= nexprlist */ - 284, /* (384) nmnum ::= plus_num */ - 284, /* (385) nmnum ::= nm */ - 284, /* (386) nmnum ::= ON */ - 284, /* (387) nmnum ::= DELETE */ - 284, /* (388) nmnum ::= DEFAULT */ - 211, /* (389) plus_num ::= INTEGER|FLOAT */ - 289, /* (390) foreach_clause ::= */ - 289, /* (391) foreach_clause ::= FOR EACH ROW */ - 292, /* (392) trnm ::= nm */ - 293, /* (393) tridxby ::= */ - 294, /* (394) database_kw_opt ::= DATABASE */ - 294, /* (395) database_kw_opt ::= */ - 297, /* (396) kwcolumn_opt ::= */ - 297, /* (397) kwcolumn_opt ::= COLUMNKW */ - 299, /* (398) vtabarglist ::= vtabarg */ - 299, /* (399) vtabarglist ::= vtabarglist COMMA vtabarg */ - 300, /* (400) vtabarg ::= vtabarg vtabargtoken */ - 303, /* (401) anylist ::= */ - 303, /* (402) anylist ::= anylist LP anylist RP */ - 303, /* (403) anylist ::= anylist ANY */ - 266, /* (404) with ::= */ + 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 STAR RP */ + 217, /* (189) expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP filter_over */ + 217, /* (190) expr ::= ID|INDEXED|JOIN_KW LP STAR RP filter_over */ + 216, /* (191) term ::= CTIME_KW */ + 217, /* (192) expr ::= LP nexprlist COMMA expr RP */ + 217, /* (193) expr ::= expr AND expr */ + 217, /* (194) expr ::= expr OR expr */ + 217, /* (195) expr ::= expr LT|GT|GE|LE expr */ + 217, /* (196) expr ::= expr EQ|NE expr */ + 217, /* (197) expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */ + 217, /* (198) expr ::= expr PLUS|MINUS expr */ + 217, /* (199) expr ::= expr STAR|SLASH|REM expr */ + 217, /* (200) expr ::= expr CONCAT expr */ + 274, /* (201) likeop ::= NOT LIKE_KW|MATCH */ + 217, /* (202) expr ::= expr likeop expr */ + 217, /* (203) expr ::= expr likeop expr ESCAPE expr */ + 217, /* (204) expr ::= expr ISNULL|NOTNULL */ + 217, /* (205) expr ::= expr NOT NULL */ + 217, /* (206) expr ::= expr IS expr */ + 217, /* (207) expr ::= expr IS NOT expr */ + 217, /* (208) expr ::= expr IS NOT DISTINCT FROM expr */ + 217, /* (209) expr ::= expr IS DISTINCT FROM expr */ + 217, /* (210) expr ::= NOT expr */ + 217, /* (211) expr ::= BITNOT expr */ + 217, /* (212) expr ::= PLUS|MINUS expr */ + 217, /* (213) expr ::= expr PTR expr */ + 275, /* (214) between_op ::= BETWEEN */ + 275, /* (215) between_op ::= NOT BETWEEN */ + 217, /* (216) expr ::= expr between_op expr AND expr */ + 276, /* (217) in_op ::= IN */ + 276, /* (218) in_op ::= NOT IN */ + 217, /* (219) expr ::= expr in_op LP exprlist RP */ + 217, /* (220) expr ::= LP select RP */ + 217, /* (221) expr ::= expr in_op LP select RP */ + 217, /* (222) expr ::= expr in_op nm dbnm paren_exprlist */ + 217, /* (223) expr ::= EXISTS LP select RP */ + 217, /* (224) expr ::= CASE case_operand case_exprlist case_else END */ + 279, /* (225) case_exprlist ::= case_exprlist WHEN expr THEN expr */ + 279, /* (226) case_exprlist ::= WHEN expr THEN expr */ + 280, /* (227) case_else ::= ELSE expr */ + 280, /* (228) case_else ::= */ + 278, /* (229) case_operand ::= */ + 261, /* (230) exprlist ::= */ + 253, /* (231) nexprlist ::= nexprlist COMMA expr */ + 253, /* (232) nexprlist ::= expr */ + 277, /* (233) paren_exprlist ::= */ + 277, /* (234) paren_exprlist ::= LP exprlist RP */ + 190, /* (235) cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */ + 281, /* (236) uniqueflag ::= UNIQUE */ + 281, /* (237) uniqueflag ::= */ + 221, /* (238) eidlist_opt ::= */ + 221, /* (239) eidlist_opt ::= LP eidlist RP */ + 232, /* (240) eidlist ::= eidlist COMMA nm collate sortorder */ + 232, /* (241) eidlist ::= nm collate sortorder */ + 282, /* (242) collate ::= */ + 282, /* (243) collate ::= COLLATE ID|STRING */ + 190, /* (244) cmd ::= DROP INDEX ifexists fullname */ + 190, /* (245) cmd ::= VACUUM vinto */ + 190, /* (246) cmd ::= VACUUM nm vinto */ + 283, /* (247) vinto ::= INTO expr */ + 283, /* (248) vinto ::= */ + 190, /* (249) cmd ::= PRAGMA nm dbnm */ + 190, /* (250) cmd ::= PRAGMA nm dbnm EQ nmnum */ + 190, /* (251) cmd ::= PRAGMA nm dbnm LP nmnum RP */ + 190, /* (252) cmd ::= PRAGMA nm dbnm EQ minus_num */ + 190, /* (253) cmd ::= PRAGMA nm dbnm LP minus_num RP */ + 211, /* (254) plus_num ::= PLUS INTEGER|FLOAT */ + 212, /* (255) minus_num ::= MINUS INTEGER|FLOAT */ + 190, /* (256) cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */ + 285, /* (257) trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */ + 287, /* (258) trigger_time ::= BEFORE|AFTER */ + 287, /* (259) trigger_time ::= INSTEAD OF */ + 287, /* (260) trigger_time ::= */ + 288, /* (261) trigger_event ::= DELETE|INSERT */ + 288, /* (262) trigger_event ::= UPDATE */ + 288, /* (263) trigger_event ::= UPDATE OF idlist */ + 290, /* (264) when_clause ::= */ + 290, /* (265) when_clause ::= WHEN expr */ + 286, /* (266) trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */ + 286, /* (267) trigger_cmd_list ::= trigger_cmd SEMI */ + 292, /* (268) trnm ::= nm DOT nm */ + 293, /* (269) tridxby ::= INDEXED BY nm */ + 293, /* (270) tridxby ::= NOT INDEXED */ + 291, /* (271) trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt */ + 291, /* (272) trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */ + 291, /* (273) trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */ + 291, /* (274) trigger_cmd ::= scanpt select scanpt */ + 217, /* (275) expr ::= RAISE LP IGNORE RP */ + 217, /* (276) expr ::= RAISE LP raisetype COMMA nm RP */ + 236, /* (277) raisetype ::= ROLLBACK */ + 236, /* (278) raisetype ::= ABORT */ + 236, /* (279) raisetype ::= FAIL */ + 190, /* (280) cmd ::= DROP TRIGGER ifexists fullname */ + 190, /* (281) cmd ::= ATTACH database_kw_opt expr AS expr key_opt */ + 190, /* (282) cmd ::= DETACH database_kw_opt expr */ + 295, /* (283) key_opt ::= */ + 295, /* (284) key_opt ::= KEY expr */ + 190, /* (285) cmd ::= REINDEX */ + 190, /* (286) cmd ::= REINDEX nm dbnm */ + 190, /* (287) cmd ::= ANALYZE */ + 190, /* (288) cmd ::= ANALYZE nm dbnm */ + 190, /* (289) cmd ::= ALTER TABLE fullname RENAME TO nm */ + 190, /* (290) cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */ + 190, /* (291) cmd ::= ALTER TABLE fullname DROP kwcolumn_opt nm */ + 296, /* (292) add_column_fullname ::= fullname */ + 190, /* (293) cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */ + 190, /* (294) cmd ::= create_vtab */ + 190, /* (295) cmd ::= create_vtab LP vtabarglist RP */ + 298, /* (296) create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */ + 300, /* (297) vtabarg ::= */ + 301, /* (298) vtabargtoken ::= ANY */ + 301, /* (299) vtabargtoken ::= lp anylist RP */ + 302, /* (300) lp ::= LP */ + 266, /* (301) with ::= WITH wqlist */ + 266, /* (302) with ::= WITH RECURSIVE wqlist */ + 305, /* (303) wqas ::= AS */ + 305, /* (304) wqas ::= AS MATERIALIZED */ + 305, /* (305) wqas ::= AS NOT MATERIALIZED */ + 304, /* (306) wqitem ::= nm eidlist_opt wqas LP select RP */ + 241, /* (307) wqlist ::= wqitem */ + 241, /* (308) wqlist ::= wqlist COMMA wqitem */ + 306, /* (309) windowdefn_list ::= windowdefn */ + 306, /* (310) windowdefn_list ::= windowdefn_list COMMA windowdefn */ + 307, /* (311) windowdefn ::= nm AS LP window RP */ + 308, /* (312) window ::= PARTITION BY nexprlist orderby_opt frame_opt */ + 308, /* (313) window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */ + 308, /* (314) window ::= ORDER BY sortlist frame_opt */ + 308, /* (315) window ::= nm ORDER BY sortlist frame_opt */ + 308, /* (316) window ::= 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 ::= */ }; /* For rule J, yyRuleInfoNRhs[J] contains the negative of the number @@ -170128,233 +171031,231 @@ static const signed char yyRuleInfoNRhs[] = { -3, /* (175) idlist ::= idlist COMMA nm */ -1, /* (176) idlist ::= nm */ -3, /* (177) expr ::= LP expr RP */ - -1, /* (178) expr ::= ID|INDEXED */ - -1, /* (179) expr ::= JOIN_KW */ - -3, /* (180) expr ::= nm DOT nm */ - -5, /* (181) expr ::= nm DOT nm DOT nm */ - -1, /* (182) term ::= NULL|FLOAT|BLOB */ - -1, /* (183) term ::= STRING */ - -1, /* (184) term ::= INTEGER */ - -1, /* (185) expr ::= VARIABLE */ - -3, /* (186) expr ::= expr COLLATE ID|STRING */ - -6, /* (187) expr ::= CAST LP expr AS typetoken RP */ - -5, /* (188) expr ::= ID|INDEXED LP distinct exprlist RP */ - -4, /* (189) expr ::= ID|INDEXED LP STAR RP */ - -6, /* (190) expr ::= ID|INDEXED LP distinct exprlist RP filter_over */ - -5, /* (191) expr ::= ID|INDEXED LP STAR RP filter_over */ - -1, /* (192) term ::= CTIME_KW */ - -5, /* (193) expr ::= LP nexprlist COMMA expr RP */ - -3, /* (194) expr ::= expr AND expr */ - -3, /* (195) expr ::= expr OR expr */ - -3, /* (196) expr ::= expr LT|GT|GE|LE expr */ - -3, /* (197) expr ::= expr EQ|NE expr */ - -3, /* (198) expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */ - -3, /* (199) expr ::= expr PLUS|MINUS expr */ - -3, /* (200) expr ::= expr STAR|SLASH|REM expr */ - -3, /* (201) expr ::= expr CONCAT expr */ - -2, /* (202) likeop ::= NOT LIKE_KW|MATCH */ - -3, /* (203) expr ::= expr likeop expr */ - -5, /* (204) expr ::= expr likeop expr ESCAPE expr */ - -2, /* (205) expr ::= expr ISNULL|NOTNULL */ - -3, /* (206) expr ::= expr NOT NULL */ - -3, /* (207) expr ::= expr IS expr */ - -4, /* (208) expr ::= expr IS NOT expr */ - -6, /* (209) expr ::= expr IS NOT DISTINCT FROM expr */ - -5, /* (210) expr ::= expr IS DISTINCT FROM expr */ - -2, /* (211) expr ::= NOT expr */ - -2, /* (212) expr ::= BITNOT expr */ - -2, /* (213) expr ::= PLUS|MINUS expr */ - -3, /* (214) expr ::= expr PTR expr */ - -1, /* (215) between_op ::= BETWEEN */ - -2, /* (216) between_op ::= NOT BETWEEN */ - -5, /* (217) expr ::= expr between_op expr AND expr */ - -1, /* (218) in_op ::= IN */ - -2, /* (219) in_op ::= NOT IN */ - -5, /* (220) expr ::= expr in_op LP exprlist RP */ - -3, /* (221) expr ::= LP select RP */ - -5, /* (222) expr ::= expr in_op LP select RP */ - -5, /* (223) expr ::= expr in_op nm dbnm paren_exprlist */ - -4, /* (224) expr ::= EXISTS LP select RP */ - -5, /* (225) expr ::= CASE case_operand case_exprlist case_else END */ - -5, /* (226) case_exprlist ::= case_exprlist WHEN expr THEN expr */ - -4, /* (227) case_exprlist ::= WHEN expr THEN expr */ - -2, /* (228) case_else ::= ELSE expr */ - 0, /* (229) case_else ::= */ - -1, /* (230) case_operand ::= expr */ - 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 */ - -1, /* (311) windowdefn_list ::= windowdefn */ - -3, /* (312) windowdefn_list ::= windowdefn_list COMMA windowdefn */ - -5, /* (313) windowdefn ::= nm AS LP window RP */ - -5, /* (314) window ::= PARTITION BY nexprlist orderby_opt frame_opt */ - -6, /* (315) window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */ - -4, /* (316) window ::= ORDER BY sortlist frame_opt */ - -5, /* (317) window ::= nm ORDER BY sortlist frame_opt */ - -1, /* (318) window ::= frame_opt */ - -2, /* (319) window ::= nm frame_opt */ - 0, /* (320) frame_opt ::= */ - -3, /* (321) frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */ - -6, /* (322) frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */ - -1, /* (323) range_or_rows ::= RANGE|ROWS|GROUPS */ - -1, /* (324) frame_bound_s ::= frame_bound */ - -2, /* (325) frame_bound_s ::= UNBOUNDED PRECEDING */ - -1, /* (326) frame_bound_e ::= frame_bound */ - -2, /* (327) frame_bound_e ::= UNBOUNDED FOLLOWING */ - -2, /* (328) frame_bound ::= expr PRECEDING|FOLLOWING */ - -2, /* (329) frame_bound ::= CURRENT ROW */ - 0, /* (330) frame_exclude_opt ::= */ - -2, /* (331) frame_exclude_opt ::= EXCLUDE frame_exclude */ - -2, /* (332) frame_exclude ::= NO OTHERS */ - -2, /* (333) frame_exclude ::= CURRENT ROW */ - -1, /* (334) frame_exclude ::= GROUP|TIES */ - -2, /* (335) window_clause ::= WINDOW windowdefn_list */ - -2, /* (336) filter_over ::= filter_clause over_clause */ - -1, /* (337) filter_over ::= over_clause */ - -1, /* (338) filter_over ::= filter_clause */ - -4, /* (339) over_clause ::= OVER LP window RP */ - -2, /* (340) over_clause ::= OVER nm */ - -5, /* (341) filter_clause ::= FILTER LP WHERE expr RP */ - -1, /* (342) input ::= cmdlist */ - -2, /* (343) cmdlist ::= cmdlist ecmd */ - -1, /* (344) cmdlist ::= ecmd */ - -1, /* (345) ecmd ::= SEMI */ - -2, /* (346) ecmd ::= cmdx SEMI */ - -3, /* (347) ecmd ::= explain cmdx SEMI */ - 0, /* (348) trans_opt ::= */ - -1, /* (349) trans_opt ::= TRANSACTION */ - -2, /* (350) trans_opt ::= TRANSACTION nm */ - -1, /* (351) savepoint_opt ::= SAVEPOINT */ - 0, /* (352) savepoint_opt ::= */ - -2, /* (353) cmd ::= create_table create_table_args */ - -1, /* (354) table_option_set ::= table_option */ - -4, /* (355) columnlist ::= columnlist COMMA columnname carglist */ - -2, /* (356) columnlist ::= columnname carglist */ - -1, /* (357) nm ::= ID|INDEXED */ - -1, /* (358) nm ::= STRING */ - -1, /* (359) nm ::= JOIN_KW */ - -1, /* (360) typetoken ::= typename */ - -1, /* (361) typename ::= ID|STRING */ - -1, /* (362) signed ::= plus_num */ - -1, /* (363) signed ::= minus_num */ - -2, /* (364) carglist ::= carglist ccons */ - 0, /* (365) carglist ::= */ - -2, /* (366) ccons ::= NULL onconf */ - -4, /* (367) ccons ::= GENERATED ALWAYS AS generated */ - -2, /* (368) ccons ::= AS generated */ - -2, /* (369) conslist_opt ::= COMMA conslist */ - -3, /* (370) conslist ::= conslist tconscomma tcons */ - -1, /* (371) conslist ::= tcons */ - 0, /* (372) tconscomma ::= */ - -1, /* (373) defer_subclause_opt ::= defer_subclause */ - -1, /* (374) resolvetype ::= raisetype */ - -1, /* (375) selectnowith ::= oneselect */ - -1, /* (376) oneselect ::= values */ - -2, /* (377) sclp ::= selcollist COMMA */ - -1, /* (378) as ::= ID|STRING */ - -1, /* (379) indexed_opt ::= indexed_by */ - 0, /* (380) returning ::= */ - -1, /* (381) expr ::= term */ - -1, /* (382) likeop ::= LIKE_KW|MATCH */ - -1, /* (383) exprlist ::= nexprlist */ - -1, /* (384) nmnum ::= plus_num */ - -1, /* (385) nmnum ::= nm */ - -1, /* (386) nmnum ::= ON */ - -1, /* (387) nmnum ::= DELETE */ - -1, /* (388) nmnum ::= DEFAULT */ - -1, /* (389) plus_num ::= INTEGER|FLOAT */ - 0, /* (390) foreach_clause ::= */ - -3, /* (391) foreach_clause ::= FOR EACH ROW */ - -1, /* (392) trnm ::= nm */ - 0, /* (393) tridxby ::= */ - -1, /* (394) database_kw_opt ::= DATABASE */ - 0, /* (395) database_kw_opt ::= */ - 0, /* (396) kwcolumn_opt ::= */ - -1, /* (397) kwcolumn_opt ::= COLUMNKW */ - -1, /* (398) vtabarglist ::= vtabarg */ - -3, /* (399) vtabarglist ::= vtabarglist COMMA vtabarg */ - -2, /* (400) vtabarg ::= vtabarg vtabargtoken */ - 0, /* (401) anylist ::= */ - -4, /* (402) anylist ::= anylist LP anylist RP */ - -2, /* (403) anylist ::= anylist ANY */ - 0, /* (404) with ::= */ + -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 */ + -4, /* (188) expr ::= ID|INDEXED|JOIN_KW LP STAR RP */ + -6, /* (189) expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP filter_over */ + -5, /* (190) expr ::= ID|INDEXED|JOIN_KW LP STAR RP filter_over */ + -1, /* (191) term ::= CTIME_KW */ + -5, /* (192) expr ::= LP nexprlist COMMA expr RP */ + -3, /* (193) expr ::= expr AND expr */ + -3, /* (194) expr ::= expr OR expr */ + -3, /* (195) expr ::= expr LT|GT|GE|LE expr */ + -3, /* (196) expr ::= expr EQ|NE expr */ + -3, /* (197) expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */ + -3, /* (198) expr ::= expr PLUS|MINUS expr */ + -3, /* (199) expr ::= expr STAR|SLASH|REM expr */ + -3, /* (200) expr ::= expr CONCAT expr */ + -2, /* (201) likeop ::= NOT LIKE_KW|MATCH */ + -3, /* (202) expr ::= expr likeop expr */ + -5, /* (203) expr ::= expr likeop expr ESCAPE expr */ + -2, /* (204) expr ::= expr ISNULL|NOTNULL */ + -3, /* (205) expr ::= expr NOT NULL */ + -3, /* (206) expr ::= expr IS expr */ + -4, /* (207) expr ::= expr IS NOT expr */ + -6, /* (208) expr ::= expr IS NOT DISTINCT FROM expr */ + -5, /* (209) expr ::= expr IS DISTINCT FROM expr */ + -2, /* (210) expr ::= NOT expr */ + -2, /* (211) expr ::= BITNOT expr */ + -2, /* (212) expr ::= PLUS|MINUS expr */ + -3, /* (213) expr ::= expr PTR expr */ + -1, /* (214) between_op ::= BETWEEN */ + -2, /* (215) between_op ::= NOT BETWEEN */ + -5, /* (216) expr ::= expr between_op expr AND expr */ + -1, /* (217) in_op ::= IN */ + -2, /* (218) in_op ::= NOT IN */ + -5, /* (219) expr ::= expr in_op LP exprlist RP */ + -3, /* (220) expr ::= LP select RP */ + -5, /* (221) expr ::= expr in_op LP select RP */ + -5, /* (222) expr ::= expr in_op nm dbnm paren_exprlist */ + -4, /* (223) expr ::= EXISTS LP select RP */ + -5, /* (224) expr ::= CASE case_operand case_exprlist case_else END */ + -5, /* (225) case_exprlist ::= case_exprlist WHEN expr THEN expr */ + -4, /* (226) case_exprlist ::= WHEN expr THEN expr */ + -2, /* (227) case_else ::= ELSE expr */ + 0, /* (228) case_else ::= */ + 0, /* (229) case_operand ::= */ + 0, /* (230) exprlist ::= */ + -3, /* (231) nexprlist ::= nexprlist COMMA expr */ + -1, /* (232) nexprlist ::= expr */ + 0, /* (233) paren_exprlist ::= */ + -3, /* (234) paren_exprlist ::= LP exprlist RP */ + -12, /* (235) cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */ + -1, /* (236) uniqueflag ::= UNIQUE */ + 0, /* (237) uniqueflag ::= */ + 0, /* (238) eidlist_opt ::= */ + -3, /* (239) eidlist_opt ::= LP eidlist RP */ + -5, /* (240) eidlist ::= eidlist COMMA nm collate sortorder */ + -3, /* (241) eidlist ::= nm collate sortorder */ + 0, /* (242) collate ::= */ + -2, /* (243) collate ::= COLLATE ID|STRING */ + -4, /* (244) cmd ::= DROP INDEX ifexists fullname */ + -2, /* (245) cmd ::= VACUUM vinto */ + -3, /* (246) cmd ::= VACUUM nm vinto */ + -2, /* (247) vinto ::= INTO expr */ + 0, /* (248) vinto ::= */ + -3, /* (249) cmd ::= PRAGMA nm dbnm */ + -5, /* (250) cmd ::= PRAGMA nm dbnm EQ nmnum */ + -6, /* (251) cmd ::= PRAGMA nm dbnm LP nmnum RP */ + -5, /* (252) cmd ::= PRAGMA nm dbnm EQ minus_num */ + -6, /* (253) cmd ::= PRAGMA nm dbnm LP minus_num RP */ + -2, /* (254) plus_num ::= PLUS INTEGER|FLOAT */ + -2, /* (255) minus_num ::= MINUS INTEGER|FLOAT */ + -5, /* (256) cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */ + -11, /* (257) trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */ + -1, /* (258) trigger_time ::= BEFORE|AFTER */ + -2, /* (259) trigger_time ::= INSTEAD OF */ + 0, /* (260) trigger_time ::= */ + -1, /* (261) trigger_event ::= DELETE|INSERT */ + -1, /* (262) trigger_event ::= UPDATE */ + -3, /* (263) trigger_event ::= UPDATE OF idlist */ + 0, /* (264) when_clause ::= */ + -2, /* (265) when_clause ::= WHEN expr */ + -3, /* (266) trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */ + -2, /* (267) trigger_cmd_list ::= trigger_cmd SEMI */ + -3, /* (268) trnm ::= nm DOT nm */ + -3, /* (269) tridxby ::= INDEXED BY nm */ + -2, /* (270) tridxby ::= NOT INDEXED */ + -9, /* (271) trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt */ + -8, /* (272) trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */ + -6, /* (273) trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */ + -3, /* (274) trigger_cmd ::= scanpt select scanpt */ + -4, /* (275) expr ::= RAISE LP IGNORE RP */ + -6, /* (276) expr ::= RAISE LP raisetype COMMA nm RP */ + -1, /* (277) raisetype ::= ROLLBACK */ + -1, /* (278) raisetype ::= ABORT */ + -1, /* (279) raisetype ::= FAIL */ + -4, /* (280) cmd ::= DROP TRIGGER ifexists fullname */ + -6, /* (281) cmd ::= ATTACH database_kw_opt expr AS expr key_opt */ + -3, /* (282) cmd ::= DETACH database_kw_opt expr */ + 0, /* (283) key_opt ::= */ + -2, /* (284) key_opt ::= KEY expr */ + -1, /* (285) cmd ::= REINDEX */ + -3, /* (286) cmd ::= REINDEX nm dbnm */ + -1, /* (287) cmd ::= ANALYZE */ + -3, /* (288) cmd ::= ANALYZE nm dbnm */ + -6, /* (289) cmd ::= ALTER TABLE fullname RENAME TO nm */ + -7, /* (290) cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */ + -6, /* (291) cmd ::= ALTER TABLE fullname DROP kwcolumn_opt nm */ + -1, /* (292) add_column_fullname ::= fullname */ + -8, /* (293) cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */ + -1, /* (294) cmd ::= create_vtab */ + -4, /* (295) cmd ::= create_vtab LP vtabarglist RP */ + -8, /* (296) create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */ + 0, /* (297) vtabarg ::= */ + -1, /* (298) vtabargtoken ::= ANY */ + -3, /* (299) vtabargtoken ::= lp anylist RP */ + -1, /* (300) lp ::= LP */ + -2, /* (301) with ::= WITH wqlist */ + -3, /* (302) with ::= WITH RECURSIVE wqlist */ + -1, /* (303) wqas ::= AS */ + -2, /* (304) wqas ::= AS MATERIALIZED */ + -3, /* (305) wqas ::= AS NOT MATERIALIZED */ + -6, /* (306) wqitem ::= nm eidlist_opt wqas LP select RP */ + -1, /* (307) wqlist ::= wqitem */ + -3, /* (308) wqlist ::= wqlist COMMA wqitem */ + -1, /* (309) windowdefn_list ::= windowdefn */ + -3, /* (310) windowdefn_list ::= windowdefn_list COMMA windowdefn */ + -5, /* (311) windowdefn ::= nm AS LP window RP */ + -5, /* (312) window ::= PARTITION BY nexprlist orderby_opt frame_opt */ + -6, /* (313) window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */ + -4, /* (314) window ::= ORDER BY sortlist frame_opt */ + -5, /* (315) window ::= nm ORDER BY sortlist frame_opt */ + -1, /* (316) window ::= 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 ::= */ }; static void yy_accept(yyParser*); /* Forward Declaration */ @@ -170414,7 +171315,7 @@ static YYACTIONTYPE yy_reduce( case 5: /* transtype ::= DEFERRED */ case 6: /* transtype ::= IMMEDIATE */ yytestcase(yyruleno==6); case 7: /* transtype ::= EXCLUSIVE */ yytestcase(yyruleno==7); - case 323: /* range_or_rows ::= RANGE|ROWS|GROUPS */ yytestcase(yyruleno==323); + case 321: /* range_or_rows ::= RANGE|ROWS|GROUPS */ yytestcase(yyruleno==321); {yymsp[0].minor.yy394 = yymsp[0].major; /*A-overwrites-X*/} break; case 8: /* cmd ::= COMMIT|END trans_opt */ @@ -170451,7 +171352,7 @@ static YYACTIONTYPE yy_reduce( 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); + case 242: /* collate ::= */ yytestcase(yyruleno==242); {yymsp[1].minor.yy394 = 0;} break; case 16: /* ifnotexists ::= IF NOT EXISTS */ @@ -170635,9 +171536,9 @@ static YYACTIONTYPE yy_reduce( break; case 63: /* init_deferred_pred_opt ::= INITIALLY DEFERRED */ case 80: /* ifexists ::= IF EXISTS */ yytestcase(yyruleno==80); - case 216: /* between_op ::= NOT BETWEEN */ yytestcase(yyruleno==216); - case 219: /* in_op ::= NOT IN */ yytestcase(yyruleno==219); - case 245: /* collate ::= COLLATE ID|STRING */ yytestcase(yyruleno==245); + case 215: /* between_op ::= NOT BETWEEN */ yytestcase(yyruleno==215); + case 218: /* in_op ::= NOT IN */ yytestcase(yyruleno==218); + case 243: /* collate ::= COLLATE ID|STRING */ yytestcase(yyruleno==243); {yymsp[-1].minor.yy394 = 1;} break; case 64: /* init_deferred_pred_opt ::= INITIALLY IMMEDIATE */ @@ -170787,9 +171688,9 @@ static YYACTIONTYPE yy_reduce( 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); + case 230: /* exprlist ::= */ yytestcase(yyruleno==230); + case 233: /* paren_exprlist ::= */ yytestcase(yyruleno==233); + case 238: /* eidlist_opt ::= */ yytestcase(yyruleno==238); {yymsp[1].minor.yy322 = 0;} break; case 100: /* selcollist ::= sclp scanpt expr scanpt as */ @@ -170815,8 +171716,8 @@ static YYACTIONTYPE yy_reduce( 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 254: /* plus_num ::= PLUS INTEGER|FLOAT */ yytestcase(yyruleno==254); + case 255: /* minus_num ::= MINUS INTEGER|FLOAT */ yytestcase(yyruleno==255); {yymsp[-1].minor.yy0 = yymsp[0].minor.yy0;} break; case 105: /* from ::= */ @@ -170860,7 +171761,7 @@ static YYACTIONTYPE yy_reduce( { 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( yymsp[-3].minor.yy131->nSrc==1 ){ + }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]; @@ -170988,16 +171889,16 @@ static YYACTIONTYPE yy_reduce( case 146: /* limit_opt ::= */ yytestcase(yyruleno==146); case 151: /* where_opt ::= */ yytestcase(yyruleno==151); case 153: /* where_opt_ret ::= */ yytestcase(yyruleno==153); - case 229: /* case_else ::= */ yytestcase(yyruleno==229); - case 231: /* case_operand ::= */ yytestcase(yyruleno==231); - case 250: /* vinto ::= */ yytestcase(yyruleno==250); + case 228: /* case_else ::= */ yytestcase(yyruleno==228); + case 229: /* case_operand ::= */ yytestcase(yyruleno==229); + case 248: /* vinto ::= */ yytestcase(yyruleno==248); {yymsp[1].minor.yy528 = 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 228: /* case_else ::= ELSE expr */ yytestcase(yyruleno==228); - case 249: /* vinto ::= INTO expr */ yytestcase(yyruleno==249); + case 227: /* case_else ::= ELSE expr */ yytestcase(yyruleno==227); + case 247: /* vinto ::= INTO expr */ yytestcase(yyruleno==247); {yymsp[-1].minor.yy528 = yymsp[0].minor.yy528;} break; case 147: /* limit_opt ::= LIMIT expr */ @@ -171109,11 +172010,10 @@ static YYACTIONTYPE yy_reduce( case 177: /* expr ::= LP expr RP */ {yymsp[-2].minor.yy528 = yymsp[-1].minor.yy528;} break; - case 178: /* expr ::= ID|INDEXED */ - case 179: /* expr ::= JOIN_KW */ yytestcase(yyruleno==179); + case 178: /* expr ::= ID|INDEXED|JOIN_KW */ {yymsp[0].minor.yy528=tokenExpr(pParse,TK_ID,yymsp[0].minor.yy0); /*A-overwrites-X*/} break; - case 180: /* expr ::= nm DOT nm */ + case 179: /* expr ::= nm DOT nm */ { Expr *temp1 = tokenExpr(pParse,TK_ID,yymsp[-2].minor.yy0); Expr *temp2 = tokenExpr(pParse,TK_ID,yymsp[0].minor.yy0); @@ -171121,7 +172021,7 @@ static YYACTIONTYPE yy_reduce( } yymsp[-2].minor.yy528 = yylhsminor.yy528; break; - case 181: /* expr ::= nm DOT nm DOT nm */ + case 180: /* 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); @@ -171134,18 +172034,18 @@ static YYACTIONTYPE yy_reduce( } yymsp[-4].minor.yy528 = yylhsminor.yy528; break; - case 182: /* term ::= NULL|FLOAT|BLOB */ - case 183: /* term ::= STRING */ yytestcase(yyruleno==183); + 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*/} break; - case 184: /* term ::= INTEGER */ + case 183: /* 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); } yymsp[0].minor.yy528 = yylhsminor.yy528; break; - case 185: /* expr ::= VARIABLE */ + case 184: /* expr ::= VARIABLE */ { if( !(yymsp[0].minor.yy0.z[0]=='#' && sqlite3Isdigit(yymsp[0].minor.yy0.z[1])) ){ u32 n = yymsp[0].minor.yy0.n; @@ -171167,50 +172067,50 @@ static YYACTIONTYPE yy_reduce( } } break; - case 186: /* expr ::= expr COLLATE ID|STRING */ + case 185: /* expr ::= expr COLLATE ID|STRING */ { yymsp[-2].minor.yy528 = sqlite3ExprAddCollateToken(pParse, yymsp[-2].minor.yy528, &yymsp[0].minor.yy0, 1); } break; - case 187: /* expr ::= CAST LP expr AS typetoken RP */ + case 186: /* 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); } break; - case 188: /* expr ::= ID|INDEXED LP distinct exprlist RP */ + case 187: /* 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); } yymsp[-4].minor.yy528 = yylhsminor.yy528; break; - case 189: /* expr ::= ID|INDEXED LP STAR RP */ + case 188: /* expr ::= ID|INDEXED|JOIN_KW LP STAR RP */ { yylhsminor.yy528 = sqlite3ExprFunction(pParse, 0, &yymsp[-3].minor.yy0, 0); } yymsp[-3].minor.yy528 = yylhsminor.yy528; break; - case 190: /* expr ::= ID|INDEXED LP distinct exprlist RP filter_over */ + case 189: /* 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); } yymsp[-5].minor.yy528 = yylhsminor.yy528; break; - case 191: /* expr ::= ID|INDEXED LP STAR RP filter_over */ + case 190: /* 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); } yymsp[-4].minor.yy528 = yylhsminor.yy528; break; - case 192: /* term ::= CTIME_KW */ + case 191: /* term ::= CTIME_KW */ { yylhsminor.yy528 = sqlite3ExprFunction(pParse, 0, &yymsp[0].minor.yy0, 0); } yymsp[0].minor.yy528 = yylhsminor.yy528; break; - case 193: /* expr ::= LP nexprlist COMMA expr RP */ + case 192: /* 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); @@ -171224,22 +172124,22 @@ static YYACTIONTYPE yy_reduce( } } break; - case 194: /* expr ::= expr AND expr */ + case 193: /* expr ::= expr AND expr */ {yymsp[-2].minor.yy528=sqlite3ExprAnd(pParse,yymsp[-2].minor.yy528,yymsp[0].minor.yy528);} break; - case 195: /* expr ::= expr OR expr */ - case 196: /* expr ::= expr LT|GT|GE|LE expr */ yytestcase(yyruleno==196); - case 197: /* expr ::= expr EQ|NE expr */ yytestcase(yyruleno==197); - case 198: /* expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */ yytestcase(yyruleno==198); - case 199: /* expr ::= expr PLUS|MINUS expr */ yytestcase(yyruleno==199); - case 200: /* expr ::= expr STAR|SLASH|REM expr */ yytestcase(yyruleno==200); - case 201: /* expr ::= expr CONCAT expr */ yytestcase(yyruleno==201); + case 194: /* expr ::= expr OR expr */ + case 195: /* expr ::= expr LT|GT|GE|LE expr */ yytestcase(yyruleno==195); + case 196: /* expr ::= expr EQ|NE expr */ yytestcase(yyruleno==196); + case 197: /* expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */ yytestcase(yyruleno==197); + case 198: /* expr ::= expr PLUS|MINUS expr */ yytestcase(yyruleno==198); + case 199: /* expr ::= expr STAR|SLASH|REM expr */ yytestcase(yyruleno==199); + case 200: /* expr ::= expr CONCAT expr */ yytestcase(yyruleno==200); {yymsp[-2].minor.yy528=sqlite3PExpr(pParse,yymsp[-1].major,yymsp[-2].minor.yy528,yymsp[0].minor.yy528);} break; - case 202: /* likeop ::= NOT LIKE_KW|MATCH */ + case 201: /* 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 203: /* expr ::= expr likeop expr */ + case 202: /* expr ::= expr likeop expr */ { ExprList *pList; int bNot = yymsp[-1].minor.yy0.n & 0x80000000; @@ -171251,7 +172151,7 @@ static YYACTIONTYPE yy_reduce( if( yymsp[-2].minor.yy528 ) yymsp[-2].minor.yy528->flags |= EP_InfixFunc; } break; - case 204: /* expr ::= expr likeop expr ESCAPE expr */ + case 203: /* expr ::= expr likeop expr ESCAPE expr */ { ExprList *pList; int bNot = yymsp[-3].minor.yy0.n & 0x80000000; @@ -171264,47 +172164,47 @@ static YYACTIONTYPE yy_reduce( if( yymsp[-4].minor.yy528 ) yymsp[-4].minor.yy528->flags |= EP_InfixFunc; } break; - case 205: /* expr ::= expr ISNULL|NOTNULL */ + case 204: /* expr ::= expr ISNULL|NOTNULL */ {yymsp[-1].minor.yy528 = sqlite3PExpr(pParse,yymsp[0].major,yymsp[-1].minor.yy528,0);} break; - case 206: /* expr ::= expr NOT NULL */ + case 205: /* expr ::= expr NOT NULL */ {yymsp[-2].minor.yy528 = sqlite3PExpr(pParse,TK_NOTNULL,yymsp[-2].minor.yy528,0);} break; - case 207: /* expr ::= expr IS expr */ + case 206: /* 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); } break; - case 208: /* expr ::= expr IS NOT expr */ + case 207: /* 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); } break; - case 209: /* expr ::= expr IS NOT DISTINCT FROM expr */ + case 208: /* 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); } break; - case 210: /* expr ::= expr IS DISTINCT FROM expr */ + case 209: /* 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); } break; - case 211: /* expr ::= NOT expr */ - case 212: /* expr ::= BITNOT expr */ yytestcase(yyruleno==212); + case 210: /* expr ::= NOT expr */ + case 211: /* expr ::= BITNOT expr */ yytestcase(yyruleno==211); {yymsp[-1].minor.yy528 = sqlite3PExpr(pParse, yymsp[-1].major, yymsp[0].minor.yy528, 0);/*A-overwrites-B*/} break; - case 213: /* expr ::= PLUS|MINUS expr */ + case 212: /* 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*/ } break; - case 214: /* expr ::= expr PTR expr */ + case 213: /* expr ::= expr PTR expr */ { ExprList *pList = sqlite3ExprListAppend(pParse, 0, yymsp[-2].minor.yy528); pList = sqlite3ExprListAppend(pParse, pList, yymsp[0].minor.yy528); @@ -171312,11 +172212,11 @@ static YYACTIONTYPE yy_reduce( } yymsp[-2].minor.yy528 = yylhsminor.yy528; break; - case 215: /* between_op ::= BETWEEN */ - case 218: /* in_op ::= IN */ yytestcase(yyruleno==218); + case 214: /* between_op ::= BETWEEN */ + case 217: /* in_op ::= IN */ yytestcase(yyruleno==217); {yymsp[0].minor.yy394 = 0;} break; - case 217: /* expr ::= expr between_op expr AND expr */ + case 216: /* expr ::= expr between_op expr AND expr */ { ExprList *pList = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy528); pList = sqlite3ExprListAppend(pParse,pList, yymsp[0].minor.yy528); @@ -171329,7 +172229,7 @@ static YYACTIONTYPE yy_reduce( if( yymsp[-3].minor.yy394 ) yymsp[-4].minor.yy528 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy528, 0); } break; - case 220: /* expr ::= expr in_op LP exprlist RP */ + case 219: /* expr ::= expr in_op LP exprlist RP */ { if( yymsp[-1].minor.yy322==0 ){ /* Expressions of the form @@ -171375,20 +172275,20 @@ static YYACTIONTYPE yy_reduce( } } break; - case 221: /* expr ::= LP select RP */ + case 220: /* expr ::= LP select RP */ { yymsp[-2].minor.yy528 = sqlite3PExpr(pParse, TK_SELECT, 0, 0); sqlite3PExprAddSelect(pParse, yymsp[-2].minor.yy528, yymsp[-1].minor.yy47); } break; - case 222: /* expr ::= expr in_op LP select RP */ + case 221: /* 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); } break; - case 223: /* expr ::= expr in_op nm dbnm paren_exprlist */ + case 222: /* 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); @@ -171398,14 +172298,14 @@ static YYACTIONTYPE yy_reduce( if( yymsp[-3].minor.yy394 ) yymsp[-4].minor.yy528 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy528, 0); } break; - case 224: /* expr ::= EXISTS LP select RP */ + case 223: /* 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); } break; - case 225: /* expr ::= CASE case_operand case_exprlist case_else END */ + case 224: /* 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 ){ @@ -171417,32 +172317,29 @@ static YYACTIONTYPE yy_reduce( } } break; - case 226: /* case_exprlist ::= case_exprlist WHEN expr THEN expr */ + case 225: /* 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); } break; - case 227: /* case_exprlist ::= WHEN expr THEN expr */ + case 226: /* 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); } break; - case 230: /* case_operand ::= expr */ -{yymsp[0].minor.yy528 = yymsp[0].minor.yy528; /*A-overwrites-X*/} - break; - case 233: /* nexprlist ::= nexprlist COMMA expr */ + case 231: /* nexprlist ::= nexprlist COMMA expr */ {yymsp[-2].minor.yy322 = sqlite3ExprListAppend(pParse,yymsp[-2].minor.yy322,yymsp[0].minor.yy528);} break; - case 234: /* nexprlist ::= expr */ + case 232: /* nexprlist ::= expr */ {yymsp[0].minor.yy322 = sqlite3ExprListAppend(pParse,0,yymsp[0].minor.yy528); /*A-overwrites-Y*/} break; - case 236: /* paren_exprlist ::= LP exprlist RP */ - case 241: /* eidlist_opt ::= LP eidlist RP */ yytestcase(yyruleno==241); + case 234: /* paren_exprlist ::= LP exprlist RP */ + case 239: /* eidlist_opt ::= LP eidlist RP */ yytestcase(yyruleno==239); {yymsp[-2].minor.yy322 = yymsp[-1].minor.yy322;} break; - case 237: /* cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */ + case 235: /* 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, @@ -171452,48 +172349,48 @@ static YYACTIONTYPE yy_reduce( } } break; - case 238: /* uniqueflag ::= UNIQUE */ - case 280: /* raisetype ::= ABORT */ yytestcase(yyruleno==280); + case 236: /* uniqueflag ::= UNIQUE */ + case 278: /* raisetype ::= ABORT */ yytestcase(yyruleno==278); {yymsp[0].minor.yy394 = OE_Abort;} break; - case 239: /* uniqueflag ::= */ + case 237: /* uniqueflag ::= */ {yymsp[1].minor.yy394 = OE_None;} break; - case 242: /* eidlist ::= eidlist COMMA nm collate sortorder */ + case 240: /* 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); } break; - case 243: /* eidlist ::= nm collate sortorder */ + case 241: /* 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*/ } break; - case 246: /* cmd ::= DROP INDEX ifexists fullname */ + case 244: /* cmd ::= DROP INDEX ifexists fullname */ {sqlite3DropIndex(pParse, yymsp[0].minor.yy131, yymsp[-1].minor.yy394);} break; - case 247: /* cmd ::= VACUUM vinto */ + case 245: /* cmd ::= VACUUM vinto */ {sqlite3Vacuum(pParse,0,yymsp[0].minor.yy528);} break; - case 248: /* cmd ::= VACUUM nm vinto */ + case 246: /* cmd ::= VACUUM nm vinto */ {sqlite3Vacuum(pParse,&yymsp[-1].minor.yy0,yymsp[0].minor.yy528);} break; - case 251: /* cmd ::= PRAGMA nm dbnm */ + case 249: /* 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 250: /* 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 251: /* 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 252: /* 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 253: /* 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 256: /* cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */ { Token all; all.z = yymsp[-3].minor.yy0.z; @@ -171501,50 +172398,50 @@ static YYACTIONTYPE yy_reduce( sqlite3FinishTrigger(pParse, yymsp[-1].minor.yy33, &all); } break; - case 259: /* trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */ + case 257: /* 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); 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 */ + case 258: /* trigger_time ::= BEFORE|AFTER */ { yymsp[0].minor.yy394 = yymsp[0].major; /*A-overwrites-X*/ } break; - case 261: /* trigger_time ::= INSTEAD OF */ + case 259: /* trigger_time ::= INSTEAD OF */ { yymsp[-1].minor.yy394 = TK_INSTEAD;} break; - case 262: /* trigger_time ::= */ + case 260: /* trigger_time ::= */ { yymsp[1].minor.yy394 = TK_BEFORE; } break; - case 263: /* trigger_event ::= DELETE|INSERT */ - case 264: /* trigger_event ::= UPDATE */ yytestcase(yyruleno==264); + case 261: /* trigger_event ::= DELETE|INSERT */ + case 262: /* trigger_event ::= UPDATE */ yytestcase(yyruleno==262); {yymsp[0].minor.yy180.a = yymsp[0].major; /*A-overwrites-X*/ yymsp[0].minor.yy180.b = 0;} break; - case 265: /* trigger_event ::= UPDATE OF idlist */ + case 263: /* trigger_event ::= UPDATE OF idlist */ {yymsp[-2].minor.yy180.a = TK_UPDATE; yymsp[-2].minor.yy180.b = yymsp[0].minor.yy254;} break; - case 266: /* when_clause ::= */ - case 285: /* key_opt ::= */ yytestcase(yyruleno==285); + case 264: /* when_clause ::= */ + case 283: /* key_opt ::= */ yytestcase(yyruleno==283); { yymsp[1].minor.yy528 = 0; } break; - case 267: /* when_clause ::= WHEN expr */ - case 286: /* key_opt ::= KEY expr */ yytestcase(yyruleno==286); + case 265: /* when_clause ::= WHEN expr */ + case 284: /* key_opt ::= KEY expr */ yytestcase(yyruleno==284); { yymsp[-1].minor.yy528 = yymsp[0].minor.yy528; } break; - case 268: /* trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */ + case 266: /* 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; } break; - case 269: /* trigger_cmd_list ::= trigger_cmd SEMI */ + case 267: /* trigger_cmd_list ::= trigger_cmd SEMI */ { assert( yymsp[-1].minor.yy33!=0 ); yymsp[-1].minor.yy33->pLast = yymsp[-1].minor.yy33; } break; - case 270: /* trnm ::= nm DOT nm */ + case 268: /* trnm ::= nm DOT nm */ { yymsp[-2].minor.yy0 = yymsp[0].minor.yy0; sqlite3ErrorMsg(pParse, @@ -171552,39 +172449,39 @@ static YYACTIONTYPE yy_reduce( "statements within triggers"); } break; - case 271: /* tridxby ::= INDEXED BY nm */ + case 269: /* 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 270: /* 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 */ + case 271: /* 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; break; - case 274: /* trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */ + case 272: /* 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*/ } yymsp[-7].minor.yy33 = yylhsminor.yy33; break; - case 275: /* trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */ + case 273: /* 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; break; - case 276: /* trigger_cmd ::= scanpt select scanpt */ + case 274: /* 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; break; - case 277: /* expr ::= RAISE LP IGNORE RP */ + case 275: /* expr ::= RAISE LP IGNORE RP */ { yymsp[-3].minor.yy528 = sqlite3PExpr(pParse, TK_RAISE, 0, 0); if( yymsp[-3].minor.yy528 ){ @@ -171592,7 +172489,7 @@ static YYACTIONTYPE yy_reduce( } } break; - case 278: /* expr ::= RAISE LP raisetype COMMA nm RP */ + case 276: /* 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 ) { @@ -171600,118 +172497,118 @@ static YYACTIONTYPE yy_reduce( } } break; - case 279: /* raisetype ::= ROLLBACK */ + case 277: /* raisetype ::= ROLLBACK */ {yymsp[0].minor.yy394 = OE_Rollback;} break; - case 281: /* raisetype ::= FAIL */ + case 279: /* raisetype ::= FAIL */ {yymsp[0].minor.yy394 = OE_Fail;} break; - case 282: /* cmd ::= DROP TRIGGER ifexists fullname */ + case 280: /* cmd ::= DROP TRIGGER ifexists fullname */ { sqlite3DropTrigger(pParse,yymsp[0].minor.yy131,yymsp[-1].minor.yy394); } break; - case 283: /* cmd ::= ATTACH database_kw_opt expr AS expr key_opt */ + case 281: /* cmd ::= ATTACH database_kw_opt expr AS expr key_opt */ { sqlite3Attach(pParse, yymsp[-3].minor.yy528, yymsp[-1].minor.yy528, yymsp[0].minor.yy528); } break; - case 284: /* cmd ::= DETACH database_kw_opt expr */ + case 282: /* cmd ::= DETACH database_kw_opt expr */ { sqlite3Detach(pParse, yymsp[0].minor.yy528); } break; - case 287: /* cmd ::= REINDEX */ + case 285: /* cmd ::= REINDEX */ {sqlite3Reindex(pParse, 0, 0);} break; - case 288: /* cmd ::= REINDEX nm dbnm */ + case 286: /* cmd ::= REINDEX nm dbnm */ {sqlite3Reindex(pParse, &yymsp[-1].minor.yy0, &yymsp[0].minor.yy0);} break; - case 289: /* cmd ::= ANALYZE */ + case 287: /* cmd ::= ANALYZE */ {sqlite3Analyze(pParse, 0, 0);} break; - case 290: /* cmd ::= ANALYZE nm dbnm */ + case 288: /* 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 289: /* cmd ::= ALTER TABLE fullname RENAME TO nm */ { sqlite3AlterRenameTable(pParse,yymsp[-3].minor.yy131,&yymsp[0].minor.yy0); } break; - case 292: /* cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */ + case 290: /* 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 291: /* cmd ::= ALTER TABLE fullname DROP kwcolumn_opt nm */ { sqlite3AlterDropColumn(pParse, yymsp[-3].minor.yy131, &yymsp[0].minor.yy0); } break; - case 294: /* add_column_fullname ::= fullname */ + case 292: /* add_column_fullname ::= fullname */ { disableLookaside(pParse); sqlite3AlterBeginAddColumn(pParse, yymsp[0].minor.yy131); } break; - case 295: /* cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */ + case 293: /* cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */ { sqlite3AlterRenameColumn(pParse, yymsp[-5].minor.yy131, &yymsp[-2].minor.yy0, &yymsp[0].minor.yy0); } break; - case 296: /* cmd ::= create_vtab */ + case 294: /* cmd ::= create_vtab */ {sqlite3VtabFinishParse(pParse,0);} break; - case 297: /* cmd ::= create_vtab LP vtabarglist RP */ + case 295: /* 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 296: /* 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); } break; - case 299: /* vtabarg ::= */ + case 297: /* 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 298: /* vtabargtoken ::= ANY */ + case 299: /* vtabargtoken ::= lp anylist RP */ yytestcase(yyruleno==299); + case 300: /* lp ::= LP */ yytestcase(yyruleno==300); {sqlite3VtabArgExtend(pParse,&yymsp[0].minor.yy0);} break; - case 303: /* with ::= WITH wqlist */ - case 304: /* with ::= WITH RECURSIVE wqlist */ yytestcase(yyruleno==304); + case 301: /* with ::= WITH wqlist */ + case 302: /* with ::= WITH RECURSIVE wqlist */ yytestcase(yyruleno==302); { sqlite3WithPush(pParse, yymsp[0].minor.yy521, 1); } break; - case 305: /* wqas ::= AS */ + case 303: /* wqas ::= AS */ {yymsp[0].minor.yy516 = M10d_Any;} break; - case 306: /* wqas ::= AS MATERIALIZED */ + case 304: /* wqas ::= AS MATERIALIZED */ {yymsp[-1].minor.yy516 = M10d_Yes;} break; - case 307: /* wqas ::= AS NOT MATERIALIZED */ + case 305: /* wqas ::= AS NOT MATERIALIZED */ {yymsp[-2].minor.yy516 = M10d_No;} break; - case 308: /* wqitem ::= nm eidlist_opt wqas LP select RP */ + case 306: /* wqitem ::= nm 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*/ } break; - case 309: /* wqlist ::= wqitem */ + case 307: /* wqlist ::= wqitem */ { yymsp[0].minor.yy521 = sqlite3WithAdd(pParse, 0, yymsp[0].minor.yy385); /*A-overwrites-X*/ } break; - case 310: /* wqlist ::= wqlist COMMA wqitem */ + case 308: /* wqlist ::= wqlist COMMA wqitem */ { yymsp[-2].minor.yy521 = sqlite3WithAdd(pParse, yymsp[-2].minor.yy521, yymsp[0].minor.yy385); } break; - case 311: /* windowdefn_list ::= windowdefn */ + case 309: /* windowdefn_list ::= windowdefn */ { yylhsminor.yy41 = yymsp[0].minor.yy41; } yymsp[0].minor.yy41 = yylhsminor.yy41; break; - case 312: /* windowdefn_list ::= windowdefn_list COMMA windowdefn */ + case 310: /* windowdefn_list ::= windowdefn_list COMMA windowdefn */ { assert( yymsp[0].minor.yy41!=0 ); sqlite3WindowChain(pParse, yymsp[0].minor.yy41, yymsp[-2].minor.yy41); @@ -171720,7 +172617,7 @@ static YYACTIONTYPE yy_reduce( } yymsp[-2].minor.yy41 = yylhsminor.yy41; break; - case 313: /* windowdefn ::= nm AS LP window RP */ + case 311: /* 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); @@ -171729,90 +172626,90 @@ static YYACTIONTYPE yy_reduce( } yymsp[-4].minor.yy41 = yylhsminor.yy41; break; - case 314: /* window ::= PARTITION BY nexprlist orderby_opt frame_opt */ + case 312: /* 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); } break; - case 315: /* window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */ + case 313: /* 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); } yymsp[-5].minor.yy41 = yylhsminor.yy41; break; - case 316: /* window ::= ORDER BY sortlist frame_opt */ + case 314: /* window ::= ORDER BY sortlist frame_opt */ { yymsp[-3].minor.yy41 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy41, 0, yymsp[-1].minor.yy322, 0); } break; - case 317: /* window ::= nm ORDER BY sortlist frame_opt */ + case 315: /* window ::= nm ORDER BY sortlist frame_opt */ { yylhsminor.yy41 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy41, 0, yymsp[-1].minor.yy322, &yymsp[-4].minor.yy0); } yymsp[-4].minor.yy41 = yylhsminor.yy41; break; - case 318: /* window ::= frame_opt */ - case 337: /* filter_over ::= over_clause */ yytestcase(yyruleno==337); + case 316: /* window ::= frame_opt */ + case 335: /* filter_over ::= over_clause */ yytestcase(yyruleno==335); { yylhsminor.yy41 = yymsp[0].minor.yy41; } yymsp[0].minor.yy41 = yylhsminor.yy41; break; - case 319: /* window ::= nm frame_opt */ + case 317: /* window ::= nm frame_opt */ { yylhsminor.yy41 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy41, 0, 0, &yymsp[-1].minor.yy0); } yymsp[-1].minor.yy41 = yylhsminor.yy41; break; - case 320: /* frame_opt ::= */ + case 318: /* frame_opt ::= */ { yymsp[1].minor.yy41 = sqlite3WindowAlloc(pParse, 0, TK_UNBOUNDED, 0, TK_CURRENT, 0, 0); } break; - case 321: /* frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */ + case 319: /* 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); } yymsp[-2].minor.yy41 = yylhsminor.yy41; break; - case 322: /* frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */ + case 320: /* 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); } yymsp[-5].minor.yy41 = yylhsminor.yy41; break; - case 324: /* frame_bound_s ::= frame_bound */ - case 326: /* frame_bound_e ::= frame_bound */ yytestcase(yyruleno==326); + 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; break; - case 325: /* frame_bound_s ::= UNBOUNDED PRECEDING */ - case 327: /* frame_bound_e ::= UNBOUNDED FOLLOWING */ yytestcase(yyruleno==327); - case 329: /* frame_bound ::= CURRENT ROW */ yytestcase(yyruleno==329); + 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; break; - case 328: /* frame_bound ::= expr PRECEDING|FOLLOWING */ + 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; break; - case 330: /* frame_exclude_opt ::= */ + case 328: /* frame_exclude_opt ::= */ {yymsp[1].minor.yy516 = 0;} break; - case 331: /* frame_exclude_opt ::= EXCLUDE frame_exclude */ + case 329: /* frame_exclude_opt ::= EXCLUDE frame_exclude */ {yymsp[-1].minor.yy516 = yymsp[0].minor.yy516;} break; - case 332: /* frame_exclude ::= NO OTHERS */ - case 333: /* frame_exclude ::= CURRENT ROW */ yytestcase(yyruleno==333); + 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*/} break; - case 334: /* frame_exclude ::= GROUP|TIES */ + case 332: /* frame_exclude ::= GROUP|TIES */ {yymsp[0].minor.yy516 = yymsp[0].major; /*A-overwrites-X*/} break; - case 335: /* window_clause ::= WINDOW windowdefn_list */ + case 333: /* window_clause ::= WINDOW windowdefn_list */ { yymsp[-1].minor.yy41 = yymsp[0].minor.yy41; } break; - case 336: /* filter_over ::= filter_clause over_clause */ + case 334: /* filter_over ::= filter_clause over_clause */ { if( yymsp[0].minor.yy41 ){ yymsp[0].minor.yy41->pFilter = yymsp[-1].minor.yy528; @@ -171823,7 +172720,7 @@ static YYACTIONTYPE yy_reduce( } yymsp[-1].minor.yy41 = yylhsminor.yy41; break; - case 338: /* filter_over ::= filter_clause */ + case 336: /* filter_over ::= filter_clause */ { yylhsminor.yy41 = (Window*)sqlite3DbMallocZero(pParse->db, sizeof(Window)); if( yylhsminor.yy41 ){ @@ -171835,13 +172732,13 @@ static YYACTIONTYPE yy_reduce( } yymsp[0].minor.yy41 = yylhsminor.yy41; break; - case 339: /* over_clause ::= OVER LP window RP */ + case 337: /* over_clause ::= OVER LP window RP */ { yymsp[-3].minor.yy41 = yymsp[-1].minor.yy41; assert( yymsp[-3].minor.yy41!=0 ); } break; - case 340: /* over_clause ::= OVER nm */ + case 338: /* over_clause ::= OVER nm */ { yymsp[-1].minor.yy41 = (Window*)sqlite3DbMallocZero(pParse->db, sizeof(Window)); if( yymsp[-1].minor.yy41 ){ @@ -171849,73 +172746,73 @@ static YYACTIONTYPE yy_reduce( } } break; - case 341: /* filter_clause ::= FILTER LP WHERE expr RP */ + case 339: /* filter_clause ::= FILTER LP WHERE expr RP */ { yymsp[-4].minor.yy528 = yymsp[-1].minor.yy528; } break; default: - /* (342) input ::= cmdlist */ yytestcase(yyruleno==342); - /* (343) cmdlist ::= cmdlist ecmd */ yytestcase(yyruleno==343); - /* (344) cmdlist ::= ecmd (OPTIMIZED OUT) */ assert(yyruleno!=344); - /* (345) ecmd ::= SEMI */ yytestcase(yyruleno==345); - /* (346) ecmd ::= cmdx SEMI */ yytestcase(yyruleno==346); - /* (347) ecmd ::= explain cmdx SEMI (NEVER REDUCES) */ assert(yyruleno!=347); - /* (348) trans_opt ::= */ yytestcase(yyruleno==348); - /* (349) trans_opt ::= TRANSACTION */ yytestcase(yyruleno==349); - /* (350) trans_opt ::= TRANSACTION nm */ yytestcase(yyruleno==350); - /* (351) savepoint_opt ::= SAVEPOINT */ yytestcase(yyruleno==351); - /* (352) savepoint_opt ::= */ yytestcase(yyruleno==352); - /* (353) cmd ::= create_table create_table_args */ yytestcase(yyruleno==353); - /* (354) table_option_set ::= table_option (OPTIMIZED OUT) */ assert(yyruleno!=354); - /* (355) columnlist ::= columnlist COMMA columnname carglist */ yytestcase(yyruleno==355); - /* (356) columnlist ::= columnname carglist */ yytestcase(yyruleno==356); - /* (357) nm ::= ID|INDEXED */ yytestcase(yyruleno==357); - /* (358) nm ::= STRING */ yytestcase(yyruleno==358); - /* (359) nm ::= JOIN_KW */ yytestcase(yyruleno==359); - /* (360) typetoken ::= typename */ yytestcase(yyruleno==360); - /* (361) typename ::= ID|STRING */ yytestcase(yyruleno==361); - /* (362) signed ::= plus_num (OPTIMIZED OUT) */ assert(yyruleno!=362); - /* (363) signed ::= minus_num (OPTIMIZED OUT) */ assert(yyruleno!=363); - /* (364) carglist ::= carglist ccons */ yytestcase(yyruleno==364); - /* (365) carglist ::= */ yytestcase(yyruleno==365); - /* (366) ccons ::= NULL onconf */ yytestcase(yyruleno==366); - /* (367) ccons ::= GENERATED ALWAYS AS generated */ yytestcase(yyruleno==367); - /* (368) ccons ::= AS generated */ yytestcase(yyruleno==368); - /* (369) conslist_opt ::= COMMA conslist */ yytestcase(yyruleno==369); - /* (370) conslist ::= conslist tconscomma tcons */ yytestcase(yyruleno==370); - /* (371) conslist ::= tcons (OPTIMIZED OUT) */ assert(yyruleno!=371); - /* (372) tconscomma ::= */ yytestcase(yyruleno==372); - /* (373) defer_subclause_opt ::= defer_subclause (OPTIMIZED OUT) */ assert(yyruleno!=373); - /* (374) resolvetype ::= raisetype (OPTIMIZED OUT) */ assert(yyruleno!=374); - /* (375) selectnowith ::= oneselect (OPTIMIZED OUT) */ assert(yyruleno!=375); - /* (376) oneselect ::= values */ yytestcase(yyruleno==376); - /* (377) sclp ::= selcollist COMMA */ yytestcase(yyruleno==377); - /* (378) as ::= ID|STRING */ yytestcase(yyruleno==378); - /* (379) indexed_opt ::= indexed_by (OPTIMIZED OUT) */ assert(yyruleno!=379); - /* (380) returning ::= */ yytestcase(yyruleno==380); - /* (381) expr ::= term (OPTIMIZED OUT) */ assert(yyruleno!=381); - /* (382) likeop ::= LIKE_KW|MATCH */ yytestcase(yyruleno==382); - /* (383) exprlist ::= nexprlist */ yytestcase(yyruleno==383); - /* (384) nmnum ::= plus_num (OPTIMIZED OUT) */ assert(yyruleno!=384); - /* (385) nmnum ::= nm (OPTIMIZED OUT) */ assert(yyruleno!=385); - /* (386) nmnum ::= ON */ yytestcase(yyruleno==386); - /* (387) nmnum ::= DELETE */ yytestcase(yyruleno==387); - /* (388) nmnum ::= DEFAULT */ yytestcase(yyruleno==388); - /* (389) plus_num ::= INTEGER|FLOAT */ yytestcase(yyruleno==389); - /* (390) foreach_clause ::= */ yytestcase(yyruleno==390); - /* (391) foreach_clause ::= FOR EACH ROW */ yytestcase(yyruleno==391); - /* (392) trnm ::= nm */ yytestcase(yyruleno==392); - /* (393) tridxby ::= */ yytestcase(yyruleno==393); - /* (394) database_kw_opt ::= DATABASE */ yytestcase(yyruleno==394); - /* (395) database_kw_opt ::= */ yytestcase(yyruleno==395); - /* (396) kwcolumn_opt ::= */ yytestcase(yyruleno==396); - /* (397) kwcolumn_opt ::= COLUMNKW */ yytestcase(yyruleno==397); - /* (398) vtabarglist ::= vtabarg */ yytestcase(yyruleno==398); - /* (399) vtabarglist ::= vtabarglist COMMA vtabarg */ yytestcase(yyruleno==399); - /* (400) vtabarg ::= vtabarg vtabargtoken */ yytestcase(yyruleno==400); - /* (401) anylist ::= */ yytestcase(yyruleno==401); - /* (402) anylist ::= anylist LP anylist RP */ yytestcase(yyruleno==402); - /* (403) anylist ::= anylist ANY */ yytestcase(yyruleno==403); - /* (404) with ::= */ yytestcase(yyruleno==404); + /* (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); break; /********** End reduce actions ************************************************/ }; @@ -172491,7 +173388,7 @@ static const unsigned char aKWHash[127] = { /* aKWNext[] forms the hash collision chain. If aKWHash[i]==0 ** then the i-th keyword has no more hash collisions. Otherwise, ** the next keyword with the same hash is aKWHash[i]-1. */ -static const unsigned char aKWNext[147] = { +static const unsigned char aKWNext[148] = {0, 0, 0, 0, 0, 4, 0, 43, 0, 0, 106, 114, 0, 0, 0, 2, 0, 0, 143, 0, 0, 0, 13, 0, 0, 0, 0, 141, 0, 0, 119, 52, 0, 0, 137, 12, 0, 0, 62, 0, @@ -172506,7 +173403,7 @@ static const unsigned char aKWNext[147] = { 102, 0, 0, 87, }; /* aKWLen[i] is the length (in bytes) of the i-th keyword */ -static const unsigned char aKWLen[147] = { +static const unsigned char aKWLen[148] = {0, 7, 7, 5, 4, 6, 4, 5, 3, 6, 7, 3, 6, 6, 7, 7, 3, 8, 2, 6, 5, 4, 4, 3, 10, 4, 7, 6, 9, 4, 2, 6, 5, 9, 9, 4, 7, 3, 2, 4, @@ -172522,7 +173419,7 @@ static const unsigned char aKWLen[147] = { }; /* aKWOffset[i] is the index into zKWText[] of the start of ** the text for the i-th keyword. */ -static const unsigned short int aKWOffset[147] = { +static const unsigned short int aKWOffset[148] = {0, 0, 2, 2, 8, 9, 14, 16, 20, 23, 25, 25, 29, 33, 36, 41, 46, 48, 53, 54, 59, 62, 65, 67, 69, 78, 81, 86, 90, 90, 94, 99, 101, 105, 111, 119, 123, 123, 123, 126, @@ -172537,7 +173434,7 @@ static const unsigned short int aKWOffset[147] = { 648, 650, 655, 659, }; /* aKWCode[i] is the parser symbol code for the i-th keyword */ -static const unsigned char aKWCode[147] = { +static const unsigned char aKWCode[148] = {0, TK_REINDEX, TK_INDEXED, TK_INDEX, TK_DESC, TK_ESCAPE, TK_EACH, TK_CHECK, TK_KEY, TK_BEFORE, TK_FOREIGN, TK_FOR, TK_IGNORE, TK_LIKE_KW, TK_EXPLAIN, TK_INSTEAD, @@ -172706,7 +173603,7 @@ static int keywordCode(const char *z, int n, int *pType){ const char *zKW; if( n>=2 ){ i = ((charMap(z[0])*4) ^ (charMap(z[n-1])*3) ^ n*1) % 127; - for(i=((int)aKWHash[i])-1; i>=0; i=((int)aKWNext[i])-1){ + for(i=(int)aKWHash[i]; i>0; i=aKWNext[i]){ if( aKWLen[i]!=n ) continue; zKW = &zKWText[aKWOffset[i]]; #ifdef SQLITE_ASCII @@ -172722,153 +173619,153 @@ static int keywordCode(const char *z, int n, int *pType){ while( j=SQLITE_N_KEYWORD ) return SQLITE_ERROR; + i++; *pzName = zKWText + aKWOffset[i]; *pnName = aKWLen[i]; return SQLITE_OK; @@ -174421,9 +175319,21 @@ SQLITE_API int sqlite3_config(int op, ...){ va_list ap; int rc = SQLITE_OK; - /* sqlite3_config() shall return SQLITE_MISUSE if it is invoked while - ** the SQLite library is in use. */ - if( sqlite3GlobalConfig.isInit ) return SQLITE_MISUSE_BKPT; + /* sqlite3_config() normally returns SQLITE_MISUSE if it is invoked while + ** the SQLite library is in use. Except, a few selected opcodes + ** are allowed. + */ + if( sqlite3GlobalConfig.isInit ){ + static const u64 mAnytimeConfigOption = 0 + | MASKBIT64( SQLITE_CONFIG_LOG ) + | MASKBIT64( SQLITE_CONFIG_PCACHE_HDRSZ ) + ; + if( op<0 || op>63 || (MASKBIT64(op) & mAnytimeConfigOption)==0 ){ + return SQLITE_MISUSE_BKPT; + } + testcase( op==SQLITE_CONFIG_LOG ); + testcase( op==SQLITE_CONFIG_PCACHE_HDRSZ ); + } va_start(ap, op); switch( op ){ @@ -174492,6 +175402,7 @@ SQLITE_API int sqlite3_config(int op, ...){ break; } case SQLITE_CONFIG_MEMSTATUS: { + assert( !sqlite3GlobalConfig.isInit ); /* Cannot change at runtime */ /* EVIDENCE-OF: R-61275-35157 The SQLITE_CONFIG_MEMSTATUS option takes ** single argument of type int, interpreted as a boolean, which enables ** or disables the collection of memory allocation statistics. */ @@ -174615,8 +175526,10 @@ SQLITE_API int sqlite3_config(int op, ...){ ** sqlite3GlobalConfig.xLog = va_arg(ap, void(*)(void*,int,const char*)); */ typedef void(*LOGFUNC_t)(void*,int,const char*); - sqlite3GlobalConfig.xLog = va_arg(ap, LOGFUNC_t); - sqlite3GlobalConfig.pLogArg = va_arg(ap, void*); + LOGFUNC_t xLog = va_arg(ap, LOGFUNC_t); + void *pLogArg = va_arg(ap, void*); + AtomicStore(&sqlite3GlobalConfig.xLog, xLog); + AtomicStore(&sqlite3GlobalConfig.pLogArg, pLogArg); break; } @@ -174630,7 +175543,8 @@ SQLITE_API int sqlite3_config(int op, ...){ ** argument of type int. If non-zero, then URI handling is globally ** enabled. If the parameter is zero, then URI handling is globally ** disabled. */ - sqlite3GlobalConfig.bOpenUri = va_arg(ap, int); + int bOpenUri = va_arg(ap, int); + AtomicStore(&sqlite3GlobalConfig.bOpenUri, bOpenUri); break; } @@ -174945,6 +175859,8 @@ SQLITE_API int sqlite3_db_config(sqlite3 *db, int op, ...){ { SQLITE_DBCONFIG_DQS_DML, SQLITE_DqsDML }, { SQLITE_DBCONFIG_LEGACY_FILE_FORMAT, SQLITE_LegacyFileFmt }, { SQLITE_DBCONFIG_TRUSTED_SCHEMA, SQLITE_TrustedSchema }, + { SQLITE_DBCONFIG_STMT_SCANSTATUS, SQLITE_StmtScanStatus }, + { SQLITE_DBCONFIG_REVERSE_SCANORDER, SQLITE_ReverseOrder }, }; unsigned int i; rc = SQLITE_ERROR; /* IMP: R-42790-23372 */ @@ -176930,9 +177846,9 @@ SQLITE_PRIVATE int sqlite3ParseUri( assert( *pzErrMsg==0 ); - if( ((flags & SQLITE_OPEN_URI) /* IMP: R-48725-32206 */ - || sqlite3GlobalConfig.bOpenUri) /* IMP: R-51689-46548 */ - && nUri>=5 && memcmp(zUri, "file:", 5)==0 /* IMP: R-57884-37496 */ + if( ((flags & SQLITE_OPEN_URI) /* IMP: R-48725-32206 */ + || AtomicLoad(&sqlite3GlobalConfig.bOpenUri)) /* IMP: R-51689-46548 */ + && nUri>=5 && memcmp(zUri, "file:", 5)==0 /* IMP: R-57884-37496 */ ){ char *zOpt; int eState; /* Parser state when parsing URI */ @@ -177338,6 +178254,9 @@ static int openDatabase( #endif #if defined(SQLITE_DEFAULT_LEGACY_ALTER_TABLE) | SQLITE_LegacyAlter +#endif +#if defined(SQLITE_ENABLE_STMT_SCANSTATUS) + | SQLITE_StmtScanStatus #endif ; sqlite3HashInit(&db->aCollSeq); @@ -177903,7 +178822,7 @@ SQLITE_API int sqlite3_sleep(int ms){ /* This function works in milliseconds, but the underlying OsSleep() ** API uses microseconds. Hence the 1000's. */ - rc = (sqlite3OsSleep(pVfs, 1000*ms)/1000); + rc = (sqlite3OsSleep(pVfs, ms<0 ? 0 : 1000*ms)/1000); return rc; } @@ -198907,6 +199826,7 @@ static const char * const jsonType[] = { #define JNODE_PATCH 0x10 /* Patch with JsonNode.u.pPatch */ #define JNODE_APPEND 0x20 /* More ARRAY/OBJECT entries at u.iAppend */ #define JNODE_LABEL 0x40 /* Is a label of an object */ +#define JNODE_JSON5 0x80 /* Node contains JSON5 enhancements */ /* A single node of parsed JSON @@ -198933,10 +199853,12 @@ struct JsonParse { JsonNode *aNode; /* Array of nodes containing the parse */ const char *zJson; /* Original JSON string */ u32 *aUp; /* Index of parent of each node */ - u8 oom; /* Set to true if out of memory */ - u8 nErr; /* Number of errors seen */ u16 iDepth; /* Nesting depth */ + u8 nErr; /* Number of errors seen */ + u8 oom; /* Set to true if out of memory */ + u8 hasNonstd; /* True if input uses non-standard features like JSON5 */ int nJson; /* Length of the zJson string in bytes */ + u32 iErr; /* Error location in zJson[] */ u32 iHold; /* Replace cache line with the lowest iHold value */ }; @@ -198944,10 +199866,10 @@ struct JsonParse { ** Maximum nesting depth of JSON for this implementation. ** ** This limit is needed to avoid a stack overflow in the recursive -** descent parser. A depth of 2000 is far deeper than any sane JSON -** should go. +** descent parser. A depth of 1000 is far deeper than any sane JSON +** should go. Historical note: This limit was 2000 prior to version 3.42.0 */ -#define JSON_MAX_DEPTH 2000 +#define JSON_MAX_DEPTH 1000 /************************************************************************** ** Utility routines for dealing with JsonString objects @@ -199097,6 +200019,129 @@ static void jsonAppendString(JsonString *p, const char *zIn, u32 N){ assert( p->nUsednAlloc ); } +/* +** The zIn[0..N] string is a JSON5 string literal. Append to p a translation +** of the string literal that standard JSON and that omits all JSON5 +** features. +*/ +static void jsonAppendNormalizedString(JsonString *p, const char *zIn, u32 N){ + u32 i; + jsonAppendChar(p, '"'); + zIn++; + N -= 2; + while( N>0 ){ + for(i=0; i0 ){ + jsonAppendRaw(p, zIn, i); + zIn += i; + N -= i; + if( N==0 ) break; + } + assert( zIn[0]=='\\' ); + switch( (u8)zIn[1] ){ + case '\'': + jsonAppendChar(p, '\''); + break; + case 'v': + jsonAppendRaw(p, "\\u0009", 6); + break; + case 'x': + jsonAppendRaw(p, "\\u00", 4); + jsonAppendRaw(p, &zIn[2], 2); + zIn += 2; + N -= 2; + break; + case '0': + jsonAppendRaw(p, "\\u0000", 6); + break; + case '\r': + if( zIn[2]=='\n' ){ + zIn++; + N--; + } + break; + case '\n': + break; + case 0xe2: + assert( N>=4 ); + assert( 0x80==(u8)zIn[2] ); + assert( 0xa8==(u8)zIn[3] || 0xa9==(u8)zIn[3] ); + zIn += 2; + N -= 2; + break; + default: + jsonAppendRaw(p, zIn, 2); + break; + } + zIn += 2; + N -= 2; + } + jsonAppendChar(p, '"'); +} + +/* +** The zIn[0..N] string is a JSON5 integer literal. Append to p a translation +** of the string literal that standard JSON and that omits all JSON5 +** features. +*/ +static void jsonAppendNormalizedInt(JsonString *p, const char *zIn, u32 N){ + if( zIn[0]=='+' ){ + zIn++; + N--; + }else if( zIn[0]=='-' ){ + jsonAppendChar(p, '-'); + zIn++; + N--; + } + if( zIn[0]=='0' && (zIn[1]=='x' || zIn[1]=='X') ){ + sqlite3_int64 i = 0; + int rc = sqlite3DecOrHexToI64(zIn, &i); + if( rc<=1 ){ + jsonPrintf(100,p,"%lld",i); + }else{ + assert( rc==2 ); + jsonAppendRaw(p, "9.0e999", 7); + } + return; + } + jsonAppendRaw(p, zIn, N); +} + +/* +** The zIn[0..N] string is a JSON5 real literal. Append to p a translation +** of the string literal that standard JSON and that omits all JSON5 +** features. +*/ +static void jsonAppendNormalizedReal(JsonString *p, const char *zIn, u32 N){ + u32 i; + if( zIn[0]=='+' ){ + zIn++; + N--; + }else if( zIn[0]=='-' ){ + jsonAppendChar(p, '-'); + zIn++; + N--; + } + if( zIn[0]=='.' ){ + jsonAppendChar(p, '0'); + } + for(i=0; i0 ){ + jsonAppendRaw(p, zIn, N); + } +} + + + /* ** Append a function parameter value to the JSON string under ** construction. @@ -199110,8 +200155,11 @@ static void jsonAppendValue( jsonAppendRaw(p, "null", 4); break; } - case SQLITE_INTEGER: case SQLITE_FLOAT: { + jsonPrintf(100, p, "%!0.15g", sqlite3_value_double(pValue)); + break; + } + case SQLITE_INTEGER: { const char *z = (const char*)sqlite3_value_text(pValue); u32 n = (u32)sqlite3_value_bytes(pValue); jsonAppendRaw(p, z, n); @@ -199224,17 +200272,38 @@ static void jsonRenderNode( break; } case JSON_STRING: { + assert( pNode->eU==1 ); if( pNode->jnFlags & JNODE_RAW ){ - assert( pNode->eU==1 ); - jsonAppendString(pOut, pNode->u.zJContent, pNode->n); - break; + if( pNode->jnFlags & JNODE_LABEL ){ + jsonAppendChar(pOut, '"'); + jsonAppendRaw(pOut, pNode->u.zJContent, pNode->n); + jsonAppendChar(pOut, '"'); + }else{ + jsonAppendString(pOut, pNode->u.zJContent, pNode->n); + } + }else if( pNode->jnFlags & JNODE_JSON5 ){ + jsonAppendNormalizedString(pOut, pNode->u.zJContent, pNode->n); + }else{ + jsonAppendRaw(pOut, pNode->u.zJContent, pNode->n); } - /* no break */ deliberate_fall_through + break; + } + case JSON_REAL: { + assert( pNode->eU==1 ); + if( pNode->jnFlags & JNODE_JSON5 ){ + jsonAppendNormalizedReal(pOut, pNode->u.zJContent, pNode->n); + }else{ + jsonAppendRaw(pOut, pNode->u.zJContent, pNode->n); + } + break; } - case JSON_REAL: case JSON_INT: { assert( pNode->eU==1 ); - jsonAppendRaw(pOut, pNode->u.zJContent, pNode->n); + if( pNode->jnFlags & JNODE_JSON5 ){ + jsonAppendNormalizedInt(pOut, pNode->u.zJContent, pNode->n); + }else{ + jsonAppendRaw(pOut, pNode->u.zJContent, pNode->n); + } break; } case JSON_ARRAY: { @@ -199350,59 +200419,41 @@ static void jsonReturn( } case JSON_INT: { sqlite3_int64 i = 0; + int rc; + int bNeg = 0; const char *z; + + assert( pNode->eU==1 ); z = pNode->u.zJContent; - if( z[0]=='-' ){ z++; } - while( z[0]>='0' && z[0]<='9' ){ - unsigned v = *(z++) - '0'; - if( i>=LARGEST_INT64/10 ){ - if( i>LARGEST_INT64/10 ) goto int_as_real; - if( z[0]>='0' && z[0]<='9' ) goto int_as_real; - if( v==9 ) goto int_as_real; - if( v==8 ){ - if( pNode->u.zJContent[0]=='-' ){ - sqlite3_result_int64(pCtx, SMALLEST_INT64); - goto int_done; - }else{ - goto int_as_real; - } - } - } - i = i*10 + v; + if( z[0]=='-' ){ z++; bNeg = 1; } + else if( z[0]=='+' ){ z++; } + rc = sqlite3DecOrHexToI64(z, &i); + if( rc<=1 ){ + sqlite3_result_int64(pCtx, bNeg ? -i : i); + }else if( rc==3 && bNeg ){ + sqlite3_result_int64(pCtx, SMALLEST_INT64); + }else{ + goto to_double; } - if( pNode->u.zJContent[0]=='-' ){ i = -i; } - sqlite3_result_int64(pCtx, i); - int_done: break; - int_as_real: ; /* no break */ deliberate_fall_through } case JSON_REAL: { double r; -#ifdef SQLITE_AMALGAMATION const char *z; assert( pNode->eU==1 ); + to_double: z = pNode->u.zJContent; sqlite3AtoF(z, &r, sqlite3Strlen30(z), SQLITE_UTF8); -#else - assert( pNode->eU==1 ); - r = strtod(pNode->u.zJContent, 0); -#endif sqlite3_result_double(pCtx, r); break; } case JSON_STRING: { -#if 0 /* Never happens because JNODE_RAW is only set by json_set(), - ** json_insert() and json_replace() and those routines do not - ** call jsonReturn() */ if( pNode->jnFlags & JNODE_RAW ){ assert( pNode->eU==1 ); sqlite3_result_text(pCtx, pNode->u.zJContent, pNode->n, SQLITE_TRANSIENT); - }else -#endif - assert( (pNode->jnFlags & JNODE_RAW)==0 ); - if( (pNode->jnFlags & JNODE_ESCAPE)==0 ){ + }else if( (pNode->jnFlags & JNODE_ESCAPE)==0 ){ /* JSON formatted without any backslash-escapes */ assert( pNode->eU==1 ); sqlite3_result_text(pCtx, pNode->u.zJContent+1, pNode->n-2, @@ -199414,18 +200465,17 @@ static void jsonReturn( const char *z; char *zOut; u32 j; + u32 nOut = n; assert( pNode->eU==1 ); z = pNode->u.zJContent; - zOut = sqlite3_malloc( n+1 ); + zOut = sqlite3_malloc( nOut+1 ); if( zOut==0 ){ sqlite3_result_error_nomem(pCtx); break; } for(i=1, j=0; iaNode[pParse->nNode]; - p->eType = (u8)eType; - p->jnFlags = 0; + p->eType = (u8)(eType & 0xff); + p->jnFlags = (u8)(eType >> 8); VVA( p->eU = zContent ? 1 : 0 ); p->n = n; p->u.zJContent = zContent; return pParse->nNode++; } +/* +** Return true if z[] begins with 2 (or more) hexadecimal digits +*/ +static int jsonIs2Hex(const char *z){ + return sqlite3Isxdigit(z[0]) && sqlite3Isxdigit(z[1]); +} + /* ** Return true if z[] begins with 4 (or more) hexadecimal digits */ static int jsonIs4Hex(const char *z){ - int i; - for(i=0; i<4; i++) if( !sqlite3Isxdigit(z[i]) ) return 0; - return 1; + return jsonIs2Hex(z) && jsonIs2Hex(&z[2]); } +/* +** Return the number of bytes of JSON5 whitespace at the beginning of +** the input string z[]. +** +** JSON5 whitespace consists of any of the following characters: +** +** Unicode UTF-8 Name +** U+0009 09 horizontal tab +** U+000a 0a line feed +** U+000b 0b vertical tab +** U+000c 0c form feed +** U+000d 0d carriage return +** U+0020 20 space +** U+00a0 c2 a0 non-breaking space +** U+1680 e1 9a 80 ogham space mark +** U+2000 e2 80 80 en quad +** U+2001 e2 80 81 em quad +** U+2002 e2 80 82 en space +** U+2003 e2 80 83 em space +** U+2004 e2 80 84 three-per-em space +** U+2005 e2 80 85 four-per-em space +** U+2006 e2 80 86 six-per-em space +** U+2007 e2 80 87 figure space +** U+2008 e2 80 88 punctuation space +** U+2009 e2 80 89 thin space +** U+200a e2 80 8a hair space +** U+2028 e2 80 a8 line separator +** U+2029 e2 80 a9 paragraph separator +** U+202f e2 80 af narrow no-break space (NNBSP) +** U+205f e2 81 9f medium mathematical space (MMSP) +** U+3000 e3 80 80 ideographical space +** U+FEFF ef bb bf byte order mark +** +** In addition, comments between '/', '*' and '*', '/' and +** from '/', '/' to end-of-line are also considered to be whitespace. +*/ +static int json5Whitespace(const char *zIn){ + int n = 0; + const u8 *z = (u8*)zIn; + while( 1 /*exit by "goto whitespace_done"*/ ){ + switch( z[n] ){ + case 0x09: + case 0x0a: + case 0x0b: + case 0x0c: + case 0x0d: + case 0x20: { + n++; + break; + } + case '/': { + if( z[n+1]=='*' && z[n+2]!=0 ){ + int j; + for(j=n+3; z[j]!='/' || z[j-1]!='*'; j++){ + if( z[j]==0 ) goto whitespace_done; + } + n = j+1; + break; + }else if( z[n+1]=='/' ){ + int j; + char c; + for(j=n+2; (c = z[j])!=0; j++){ + if( c=='\n' || c=='\r' ) break; + if( 0xe2==(u8)c && 0x80==(u8)z[j+1] + && (0xa8==(u8)z[j+2] || 0xa9==(u8)z[j+2]) + ){ + j += 2; + break; + } + } + n = j; + if( z[n] ) n++; + break; + } + goto whitespace_done; + } + case 0xc2: { + if( z[n+1]==0xa0 ){ + n += 2; + break; + } + goto whitespace_done; + } + case 0xe1: { + if( z[n+1]==0x9a && z[n+2]==0x80 ){ + n += 3; + break; + } + goto whitespace_done; + } + case 0xe2: { + if( z[n+1]==0x80 ){ + u8 c = z[n+2]; + if( c<0x80 ) goto whitespace_done; + if( c<=0x8a || c==0xa8 || c==0xa9 || c==0xaf ){ + n += 3; + break; + } + }else if( z[n+1]==0x81 && z[n+2]==0x9f ){ + n += 3; + break; + } + goto whitespace_done; + } + case 0xe3: { + if( z[n+1]==0x80 && z[n+2]==0x80 ){ + n += 3; + break; + } + goto whitespace_done; + } + case 0xef: { + if( z[n+1]==0xbb && z[n+2]==0xbf ){ + n += 3; + break; + } + goto whitespace_done; + } + default: { + goto whitespace_done; + } + } + } + whitespace_done: + return n; +} + +/* +** Extra floating-point literals to allow in JSON. +*/ +static const struct NanInfName { + char c1; + char c2; + char n; + char eType; + char nRepl; + char *zMatch; + char *zRepl; +} aNanInfName[] = { + { 'i', 'I', 3, JSON_REAL, 7, "inf", "9.0e999" }, + { 'i', 'I', 8, JSON_REAL, 7, "infinity", "9.0e999" }, + { 'n', 'N', 3, JSON_NULL, 4, "NaN", "null" }, + { 'q', 'Q', 4, JSON_NULL, 4, "QNaN", "null" }, + { 's', 'S', 4, JSON_NULL, 4, "SNaN", "null" }, +}; + /* ** Parse a single JSON value which begins at pParse->zJson[i]. Return the ** index of the first character past the end of the value parsed. ** -** Return negative for a syntax error. Special cases: return -2 if the -** first non-whitespace character is '}' and return -3 if the first -** non-whitespace character is ']'. +** Special return values: +** +** 0 End if input +** -1 Syntax error +** -2 '}' seen +** -3 ']' seen +** -4 ',' seen +** -5 ':' seen */ static int jsonParseValue(JsonParse *pParse, u32 i){ char c; @@ -199572,151 +200796,430 @@ static int jsonParseValue(JsonParse *pParse, u32 i){ int x; JsonNode *pNode; const char *z = pParse->zJson; - while( fast_isspace(z[i]) ){ i++; } - if( (c = z[i])=='{' ){ +json_parse_restart: + switch( (u8)z[i] ){ + case '{': { /* Parse object */ iThis = jsonParseAddNode(pParse, JSON_OBJECT, 0, 0); if( iThis<0 ) return -1; + if( ++pParse->iDepth > JSON_MAX_DEPTH ){ + pParse->iErr = i; + return -1; + } for(j=i+1;;j++){ - while( fast_isspace(z[j]) ){ j++; } - if( ++pParse->iDepth > JSON_MAX_DEPTH ) return -1; + u32 nNode = pParse->nNode; x = jsonParseValue(pParse, j); - if( x<0 ){ - pParse->iDepth--; - if( x==(-2) && pParse->nNode==(u32)iThis+1 ) return j+1; - return -1; + if( x<=0 ){ + if( x==(-2) ){ + j = pParse->iErr; + if( pParse->nNode!=(u32)iThis+1 ) pParse->hasNonstd = 1; + break; + } + j += json5Whitespace(&z[j]); + if( sqlite3JsonId1(z[j]) + || (z[j]=='\\' && z[j+1]=='u' && jsonIs4Hex(&z[j+2])) + ){ + int k = j+1; + while( (sqlite3JsonId2(z[k]) && json5Whitespace(&z[k])==0) + || (z[k]=='\\' && z[k+1]=='u' && jsonIs4Hex(&z[k+2])) + ){ + k++; + } + jsonParseAddNode(pParse, JSON_STRING | (JNODE_RAW<<8), k-j, &z[j]); + pParse->hasNonstd = 1; + x = k; + }else{ + if( x!=-1 ) pParse->iErr = j; + return -1; + } } if( pParse->oom ) return -1; - pNode = &pParse->aNode[pParse->nNode-1]; - if( pNode->eType!=JSON_STRING ) return -1; + pNode = &pParse->aNode[nNode]; + if( pNode->eType!=JSON_STRING ){ + pParse->iErr = j; + return -1; + } pNode->jnFlags |= JNODE_LABEL; j = x; - while( fast_isspace(z[j]) ){ j++; } - if( z[j]!=':' ) return -1; - j++; + if( z[j]==':' ){ + j++; + }else{ + if( fast_isspace(z[j]) ){ + do{ j++; }while( fast_isspace(z[j]) ); + if( z[j]==':' ){ + j++; + goto parse_object_value; + } + } + x = jsonParseValue(pParse, j); + if( x!=(-5) ){ + if( x!=(-1) ) pParse->iErr = j; + return -1; + } + j = pParse->iErr+1; + } + parse_object_value: x = jsonParseValue(pParse, j); - pParse->iDepth--; - if( x<0 ) return -1; + if( x<=0 ){ + if( x!=(-1) ) pParse->iErr = j; + return -1; + } j = x; - while( fast_isspace(z[j]) ){ j++; } - c = z[j]; - if( c==',' ) continue; - if( c!='}' ) return -1; - break; + if( z[j]==',' ){ + continue; + }else if( z[j]=='}' ){ + break; + }else{ + if( fast_isspace(z[j]) ){ + do{ j++; }while( fast_isspace(z[j]) ); + if( z[j]==',' ){ + continue; + }else if( z[j]=='}' ){ + break; + } + } + x = jsonParseValue(pParse, j); + if( x==(-4) ){ + j = pParse->iErr; + continue; + } + if( x==(-2) ){ + j = pParse->iErr; + break; + } + } + pParse->iErr = j; + return -1; } pParse->aNode[iThis].n = pParse->nNode - (u32)iThis - 1; + pParse->iDepth--; return j+1; - }else if( c=='[' ){ + } + case '[': { /* Parse array */ iThis = jsonParseAddNode(pParse, JSON_ARRAY, 0, 0); if( iThis<0 ) return -1; + if( ++pParse->iDepth > JSON_MAX_DEPTH ){ + pParse->iErr = i; + return -1; + } memset(&pParse->aNode[iThis].u, 0, sizeof(pParse->aNode[iThis].u)); for(j=i+1;;j++){ - while( fast_isspace(z[j]) ){ j++; } - if( ++pParse->iDepth > JSON_MAX_DEPTH ) return -1; x = jsonParseValue(pParse, j); - pParse->iDepth--; - if( x<0 ){ - if( x==(-3) && pParse->nNode==(u32)iThis+1 ) return j+1; + if( x<=0 ){ + if( x==(-3) ){ + j = pParse->iErr; + if( pParse->nNode!=(u32)iThis+1 ) pParse->hasNonstd = 1; + break; + } + if( x!=(-1) ) pParse->iErr = j; return -1; } j = x; - while( fast_isspace(z[j]) ){ j++; } - c = z[j]; - if( c==',' ) continue; - if( c!=']' ) return -1; - break; + if( z[j]==',' ){ + continue; + }else if( z[j]==']' ){ + break; + }else{ + if( fast_isspace(z[j]) ){ + do{ j++; }while( fast_isspace(z[j]) ); + if( z[j]==',' ){ + continue; + }else if( z[j]==']' ){ + break; + } + } + x = jsonParseValue(pParse, j); + if( x==(-4) ){ + j = pParse->iErr; + continue; + } + if( x==(-3) ){ + j = pParse->iErr; + break; + } + } + pParse->iErr = j; + return -1; } pParse->aNode[iThis].n = pParse->nNode - (u32)iThis - 1; + pParse->iDepth--; return j+1; - }else if( c=='"' ){ + } + case '\'': { + u8 jnFlags; + char cDelim; + pParse->hasNonstd = 1; + jnFlags = JNODE_JSON5; + goto parse_string; + case '"': /* Parse string */ - u8 jnFlags = 0; + jnFlags = 0; + parse_string: + cDelim = z[i]; j = i+1; for(;;){ c = z[j]; if( (c & ~0x1f)==0 ){ /* Control characters are not allowed in strings */ + pParse->iErr = j; return -1; } if( c=='\\' ){ c = z[++j]; if( c=='"' || c=='\\' || c=='/' || c=='b' || c=='f' || c=='n' || c=='r' || c=='t' - || (c=='u' && jsonIs4Hex(z+j+1)) ){ - jnFlags = JNODE_ESCAPE; + || (c=='u' && jsonIs4Hex(&z[j+1])) ){ + jnFlags |= JNODE_ESCAPE; + }else if( c=='\'' || c=='0' || c=='v' || c=='\n' + || (0xe2==(u8)c && 0x80==(u8)z[j+1] + && (0xa8==(u8)z[j+2] || 0xa9==(u8)z[j+2])) + || (c=='x' && jsonIs2Hex(&z[j+1])) ){ + jnFlags |= (JNODE_ESCAPE|JNODE_JSON5); + pParse->hasNonstd = 1; + }else if( c=='\r' ){ + if( z[j+1]=='\n' ) j++; + jnFlags |= (JNODE_ESCAPE|JNODE_JSON5); + pParse->hasNonstd = 1; }else{ + pParse->iErr = j; return -1; } - }else if( c=='"' ){ + }else if( c==cDelim ){ break; } j++; } - jsonParseAddNode(pParse, JSON_STRING, j+1-i, &z[i]); - if( !pParse->oom ) pParse->aNode[pParse->nNode-1].jnFlags = jnFlags; + jsonParseAddNode(pParse, JSON_STRING | (jnFlags<<8), j+1-i, &z[i]); return j+1; - }else if( c=='n' - && strncmp(z+i,"null",4)==0 - && !sqlite3Isalnum(z[i+4]) ){ - jsonParseAddNode(pParse, JSON_NULL, 0, 0); - return i+4; - }else if( c=='t' - && strncmp(z+i,"true",4)==0 - && !sqlite3Isalnum(z[i+4]) ){ - jsonParseAddNode(pParse, JSON_TRUE, 0, 0); - return i+4; - }else if( c=='f' - && strncmp(z+i,"false",5)==0 - && !sqlite3Isalnum(z[i+5]) ){ - jsonParseAddNode(pParse, JSON_FALSE, 0, 0); - return i+5; - }else if( c=='-' || (c>='0' && c<='9') ){ - /* Parse number */ - u8 seenDP = 0; - u8 seenE = 0; - assert( '-' < '0' ); - if( c<='0' ){ - j = c=='-' ? i+1 : i; - if( z[j]=='0' && z[j+1]>='0' && z[j+1]<='9' ) return -1; + } + case 't': { + if( strncmp(z+i,"true",4)==0 && !sqlite3Isalnum(z[i+4]) ){ + jsonParseAddNode(pParse, JSON_TRUE, 0, 0); + return i+4; } - j = i+1; - for(;; j++){ + pParse->iErr = i; + return -1; + } + case 'f': { + if( strncmp(z+i,"false",5)==0 && !sqlite3Isalnum(z[i+5]) ){ + jsonParseAddNode(pParse, JSON_FALSE, 0, 0); + return i+5; + } + pParse->iErr = i; + return -1; + } + case '+': { + u8 seenDP, seenE, jnFlags; + pParse->hasNonstd = 1; + jnFlags = JNODE_JSON5; + goto parse_number; + case '.': + if( sqlite3Isdigit(z[i+1]) ){ + pParse->hasNonstd = 1; + jnFlags = JNODE_JSON5; + seenE = 0; + seenDP = JSON_REAL; + goto parse_number_2; + } + pParse->iErr = i; + return -1; + case '-': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + /* Parse number */ + jnFlags = 0; + parse_number: + seenDP = JSON_INT; + seenE = 0; + assert( '-' < '0' ); + assert( '+' < '0' ); + assert( '.' < '0' ); + c = z[i]; + + if( c<='0' ){ + if( c=='0' ){ + if( (z[i+1]=='x' || z[i+1]=='X') && sqlite3Isxdigit(z[i+2]) ){ + assert( seenDP==JSON_INT ); + pParse->hasNonstd = 1; + jnFlags |= JNODE_JSON5; + for(j=i+3; sqlite3Isxdigit(z[j]); j++){} + goto parse_number_finish; + }else if( sqlite3Isdigit(z[i+1]) ){ + pParse->iErr = i+1; + return -1; + } + }else{ + if( !sqlite3Isdigit(z[i+1]) ){ + /* JSON5 allows for "+Infinity" and "-Infinity" using exactly + ** that case. SQLite also allows these in any case and it allows + ** "+inf" and "-inf". */ + if( (z[i+1]=='I' || z[i+1]=='i') + && sqlite3StrNICmp(&z[i+1], "inf",3)==0 + ){ + pParse->hasNonstd = 1; + if( z[i]=='-' ){ + jsonParseAddNode(pParse, JSON_REAL, 8, "-9.0e999"); + }else{ + jsonParseAddNode(pParse, JSON_REAL, 7, "9.0e999"); + } + return i + (sqlite3StrNICmp(&z[i+4],"inity",5)==0 ? 9 : 4); + } + if( z[i+1]=='.' ){ + pParse->hasNonstd = 1; + jnFlags |= JNODE_JSON5; + goto parse_number_2; + } + pParse->iErr = i; + return -1; + } + if( z[i+1]=='0' ){ + if( sqlite3Isdigit(z[i+2]) ){ + pParse->iErr = i+1; + return -1; + }else if( (z[i+2]=='x' || z[i+2]=='X') && sqlite3Isxdigit(z[i+3]) ){ + pParse->hasNonstd = 1; + jnFlags |= JNODE_JSON5; + for(j=i+4; sqlite3Isxdigit(z[j]); j++){} + goto parse_number_finish; + } + } + } + } + parse_number_2: + for(j=i+1;; j++){ c = z[j]; - if( c>='0' && c<='9' ) continue; + if( sqlite3Isdigit(c) ) continue; if( c=='.' ){ - if( z[j-1]=='-' ) return -1; - if( seenDP ) return -1; - seenDP = 1; + if( seenDP==JSON_REAL ){ + pParse->iErr = j; + return -1; + } + seenDP = JSON_REAL; continue; } if( c=='e' || c=='E' ){ - if( z[j-1]<'0' ) return -1; - if( seenE ) return -1; - seenDP = seenE = 1; + if( z[j-1]<'0' ){ + if( ALWAYS(z[j-1]=='.') && ALWAYS(j-2>=i) && sqlite3Isdigit(z[j-2]) ){ + pParse->hasNonstd = 1; + jnFlags |= JNODE_JSON5; + }else{ + pParse->iErr = j; + return -1; + } + } + if( seenE ){ + pParse->iErr = j; + return -1; + } + seenDP = JSON_REAL; + seenE = 1; c = z[j+1]; if( c=='+' || c=='-' ){ j++; c = z[j+1]; } - if( c<'0' || c>'9' ) return -1; + if( c<'0' || c>'9' ){ + pParse->iErr = j; + return -1; + } continue; } break; } - if( z[j-1]<'0' ) return -1; - jsonParseAddNode(pParse, seenDP ? JSON_REAL : JSON_INT, - j - i, &z[i]); + if( z[j-1]<'0' ){ + if( ALWAYS(z[j-1]=='.') && ALWAYS(j-2>=i) && sqlite3Isdigit(z[j-2]) ){ + pParse->hasNonstd = 1; + jnFlags |= JNODE_JSON5; + }else{ + pParse->iErr = j; + return -1; + } + } + parse_number_finish: + jsonParseAddNode(pParse, seenDP | (jnFlags<<8), j - i, &z[i]); return j; - }else if( c=='}' ){ + } + case '}': { + pParse->iErr = i; return -2; /* End of {...} */ - }else if( c==']' ){ + } + case ']': { + pParse->iErr = i; return -3; /* End of [...] */ - }else if( c==0 ){ + } + case ',': { + pParse->iErr = i; + return -4; /* List separator */ + } + case ':': { + pParse->iErr = i; + return -5; /* Object label/value separator */ + } + case 0: { return 0; /* End of file */ - }else{ + } + case 0x09: + case 0x0a: + case 0x0d: + case 0x20: { + do{ + i++; + }while( fast_isspace(z[i]) ); + goto json_parse_restart; + } + case 0x0b: + case 0x0c: + case '/': + case 0xc2: + case 0xe1: + case 0xe2: + case 0xe3: + case 0xef: { + j = json5Whitespace(&z[i]); + if( j>0 ){ + i += j; + pParse->hasNonstd = 1; + goto json_parse_restart; + } + pParse->iErr = i; + return -1; + } + case 'n': { + if( strncmp(z+i,"null",4)==0 && !sqlite3Isalnum(z[i+4]) ){ + jsonParseAddNode(pParse, JSON_NULL, 0, 0); + return i+4; + } + /* fall-through into the default case that checks for NaN */ + } + default: { + u32 k; + int nn; + c = z[i]; + for(k=0; khasNonstd = 1; + return i + nn; + } + pParse->iErr = i; return -1; /* Syntax error */ } + } /* End switch(z[i]) */ } /* @@ -199740,7 +201243,14 @@ static int jsonParse( if( i>0 ){ assert( pParse->iDepth==0 ); while( fast_isspace(zJson[i]) ) i++; - if( zJson[i] ) i = -1; + if( zJson[i] ){ + i += json5Whitespace(&zJson[i]); + if( zJson[i] ){ + jsonParseReset(pParse); + return 1; + } + pParse->hasNonstd = 1; + } } if( i<=0 ){ if( pCtx!=0 ){ @@ -199811,6 +201321,15 @@ static int jsonParseFindParents(JsonParse *pParse){ ** is no longer valid, parse the JSON again and return the new parse, ** and also register the new parse so that it will be available for ** future sqlite3_get_auxdata() calls. +** +** If an error occurs and pErrCtx!=0 then report the error on pErrCtx +** and return NULL. +** +** If an error occurs and pErrCtx==0 then return the Parse object with +** JsonParse.nErr non-zero. If the caller invokes this routine with +** pErrCtx==0 and it gets back a JsonParse with nErr!=0, then the caller +** is responsible for invoking jsonParseFree() on the returned value. +** But the caller may invoke jsonParseFree() *only* if pParse->nErr!=0. */ static JsonParse *jsonParseCached( sqlite3_context *pCtx, @@ -199860,6 +201379,10 @@ static JsonParse *jsonParseCached( p->zJson = (char*)&p[1]; memcpy((char*)p->zJson, zJson, nJson+1); if( jsonParse(p, pErrCtx, p->zJson) ){ + if( pErrCtx==0 ){ + p->nErr = 1; + return p; + } sqlite3_free(p); return 0; } @@ -199874,7 +201397,7 @@ static JsonParse *jsonParseCached( ** Compare the OBJECT label at pNode against zKey,nKey. Return true on ** a match. */ -static int jsonLabelCompare(JsonNode *pNode, const char *zKey, u32 nKey){ +static int jsonLabelCompare(const JsonNode *pNode, const char *zKey, u32 nKey){ assert( pNode->eU==1 ); if( pNode->jnFlags & JNODE_RAW ){ if( pNode->n!=nKey ) return 0; @@ -199884,6 +201407,15 @@ static int jsonLabelCompare(JsonNode *pNode, const char *zKey, u32 nKey){ return strncmp(pNode->u.zJContent+1, zKey, nKey)==0; } } +static int jsonSameLabel(const JsonNode *p1, const JsonNode *p2){ + if( p1->jnFlags & JNODE_RAW ){ + return jsonLabelCompare(p2, p1->u.zJContent, p1->n); + }else if( p2->jnFlags & JNODE_RAW ){ + return jsonLabelCompare(p1, p2->u.zJContent, p2->n); + }else{ + return p1->n==p2->n && strncmp(p1->u.zJContent,p2->u.zJContent,p1->n)==0; + } +} /* forward declaration */ static JsonNode *jsonLookupAppend(JsonParse*,const char*,int*,const char**); @@ -200354,7 +201886,7 @@ static void jsonExtractFunc( zPath = (const char*)sqlite3_value_text(argv[1]); if( zPath==0 ) return; if( flags & JSON_ABPATH ){ - if( zPath[0]!='$' ){ + if( zPath[0]!='$' || (zPath[1]!='.' && zPath[1]!='[' && zPath[1]!=0) ){ /* The -> and ->> operators accept abbreviated PATH arguments. This ** is mostly for compatibility with PostgreSQL, but also for ** convenience. @@ -200445,12 +201977,10 @@ static JsonNode *jsonMergePatch( assert( pPatch[i].eU==1 ); nKey = pPatch[i].n; zKey = pPatch[i].u.zJContent; - assert( (pPatch[i].jnFlags & JNODE_RAW)==0 ); for(j=1; jn; j += jsonNodeSize(&pTarget[j+1])+1 ){ assert( pTarget[j].eType==JSON_STRING ); assert( pTarget[j].jnFlags & JNODE_LABEL ); - assert( (pPatch[i].jnFlags & JNODE_RAW)==0 ); - if( pTarget[j].n==nKey && strncmp(pTarget[j].u.zJContent,zKey,nKey)==0 ){ + if( jsonSameLabel(&pPatch[i], &pTarget[j]) ){ if( pTarget[j+1].jnFlags & (JNODE_REMOVE|JNODE_PATCH) ) break; if( pPatch[i+1].eType==JSON_NULL ){ pTarget[j+1].jnFlags |= JNODE_REMOVE; @@ -200737,8 +202267,8 @@ static void jsonTypeFunc( /* ** json_valid(JSON) ** -** Return 1 if JSON is a well-formed JSON string according to RFC-7159. -** Return 0 otherwise. +** Return 1 if JSON is a well-formed canonical JSON string according +** to RFC-7159. Return 0 otherwise. */ static void jsonValidFunc( sqlite3_context *ctx, @@ -200747,8 +202277,69 @@ static void jsonValidFunc( ){ JsonParse *p; /* The parse */ UNUSED_PARAMETER(argc); + if( sqlite3_value_type(argv[0])==SQLITE_NULL ) return; p = jsonParseCached(ctx, argv, 0); - sqlite3_result_int(ctx, p!=0); + if( p==0 || p->oom ){ + sqlite3_result_error_nomem(ctx); + sqlite3_free(p); + }else{ + sqlite3_result_int(ctx, p->nErr==0 && p->hasNonstd==0); + if( p->nErr ) jsonParseFree(p); + } +} + +/* +** json_error_position(JSON) +** +** If the argument is not an interpretable JSON string, then return the 1-based +** character position at which the parser first recognized that the input +** was in error. The left-most character is 1. If the string is valid +** JSON, then return 0. +** +** Note that json_valid() is only true for strictly conforming canonical JSON. +** But this routine returns zero if the input contains extension. Thus: +** +** (1) If the input X is strictly conforming canonical JSON: +** +** json_valid(X) returns true +** json_error_position(X) returns 0 +** +** (2) If the input X is JSON but it includes extension (such as JSON5) that +** are not part of RFC-8259: +** +** json_valid(X) returns false +** json_error_position(X) return 0 +** +** (3) If the input X cannot be interpreted as JSON even taking extensions +** into account: +** +** json_valid(X) return false +** json_error_position(X) returns 1 or more +*/ +static void jsonErrorFunc( + sqlite3_context *ctx, + int argc, + sqlite3_value **argv +){ + JsonParse *p; /* The parse */ + UNUSED_PARAMETER(argc); + if( sqlite3_value_type(argv[0])==SQLITE_NULL ) return; + p = jsonParseCached(ctx, argv, 0); + if( p==0 || p->oom ){ + sqlite3_result_error_nomem(ctx); + sqlite3_free(p); + }else if( p->nErr==0 ){ + sqlite3_result_int(ctx, 0); + }else{ + int n = 1; + u32 i; + const char *z = p->zJson; + for(i=0; iiErr && ALWAYS(z[i]); i++){ + if( (z[i]&0xc0)!=0x80 ) n++; + } + sqlite3_result_int(ctx, n); + jsonParseFree(p); + } } @@ -201092,14 +202683,16 @@ static void jsonAppendObjectPathElement( assert( pNode->eU==1 ); z = pNode->u.zJContent; nn = pNode->n; - assert( nn>=2 ); - assert( z[0]=='"' ); - assert( z[nn-1]=='"' ); - if( nn>2 && sqlite3Isalpha(z[1]) ){ - for(jj=2; jjjnFlags & JNODE_RAW)==0 ){ + assert( nn>=2 ); + assert( z[0]=='"' || z[0]=='\'' ); + assert( z[nn-1]=='"' || z[0]=='\'' ); + if( nn>2 && sqlite3Isalpha(z[1]) ){ + for(jj=2; jj, 2, JSON_JSON, jsonExtractFunc), JFUNCTION(->>, 2, JSON_SQL, jsonExtractFunc), @@ -201983,16 +203577,17 @@ struct RtreeMatchArg { ** at run-time. */ #ifndef SQLITE_BYTEORDER -#if defined(i386) || defined(__i386__) || defined(_M_IX86) || \ - defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) || \ - defined(_M_AMD64) || defined(_M_ARM) || defined(__x86) || \ - defined(__arm__) -# define SQLITE_BYTEORDER 1234 -#elif defined(sparc) || defined(__ppc__) -# define SQLITE_BYTEORDER 4321 -#else -# define SQLITE_BYTEORDER 0 /* 0 means "unknown at compile-time" */ -#endif +# if defined(i386) || defined(__i386__) || defined(_M_IX86) || \ + defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) || \ + defined(_M_AMD64) || defined(_M_ARM) || defined(__x86) || \ + defined(__ARMEL__) || defined(__AARCH64EL__) || defined(_M_ARM64) +# define SQLITE_BYTEORDER 1234 +# elif defined(sparc) || defined(__ppc__) || \ + defined(__ARMEB__) || defined(__AARCH64EB__) +# define SQLITE_BYTEORDER 4321 +# else +# define SQLITE_BYTEORDER 0 +# endif #endif @@ -212537,6 +214132,11 @@ static void rbuCheckpointFrame(sqlite3rbu *p, RbuFrame *pFrame){ p->rc = pDb->pMethods->xWrite(pDb, p->aBuf, p->pgsz, iOff); } +/* +** This value is copied from the definition of ZIPVFS_CTRL_FILE_POINTER +** in zipvfs.h. +*/ +#define RBU_ZIPVFS_CTRL_FILE_POINTER 230439 /* ** Take an EXCLUSIVE lock on the database file. Return SQLITE_OK if @@ -212545,9 +214145,20 @@ static void rbuCheckpointFrame(sqlite3rbu *p, RbuFrame *pFrame){ static int rbuLockDatabase(sqlite3 *db){ int rc = SQLITE_OK; sqlite3_file *fd = 0; - sqlite3_file_control(db, "main", SQLITE_FCNTL_FILE_POINTER, &fd); - if( fd->pMethods ){ + sqlite3_file_control(db, "main", RBU_ZIPVFS_CTRL_FILE_POINTER, &fd); + if( fd ){ + sqlite3_file_control(db, "main", SQLITE_FCNTL_FILE_POINTER, &fd); + rc = fd->pMethods->xLock(fd, SQLITE_LOCK_SHARED); + if( rc==SQLITE_OK ){ + rc = fd->pMethods->xUnlock(fd, SQLITE_LOCK_NONE); + } + sqlite3_file_control(db, "main", RBU_ZIPVFS_CTRL_FILE_POINTER, &fd); + }else{ + sqlite3_file_control(db, "main", SQLITE_FCNTL_FILE_POINTER, &fd); + } + + if( rc==SQLITE_OK && fd->pMethods ){ rc = fd->pMethods->xLock(fd, SQLITE_LOCK_SHARED); if( rc==SQLITE_OK ){ rc = fd->pMethods->xLock(fd, SQLITE_LOCK_EXCLUSIVE); @@ -215784,6 +217395,7 @@ static int dbpageConnect( (void)pzErr; sqlite3_vtab_config(db, SQLITE_VTAB_DIRECTONLY); + sqlite3_vtab_config(db, SQLITE_VTAB_USES_ALL_SCHEMAS); rc = sqlite3_declare_vtab(db, "CREATE TABLE x(pgno INTEGER PRIMARY KEY, data BLOB, schema HIDDEN)"); if( rc==SQLITE_OK ){ @@ -215867,7 +217479,6 @@ static int dbpageBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ ){ pIdxInfo->orderByConsumed = 1; } - sqlite3VtabUsesAllSchemas(pIdxInfo); return SQLITE_OK; } @@ -216168,6 +217779,8 @@ typedef struct SessionInput SessionInput; # endif #endif +#define SESSIONS_ROWID "_rowid_" + static int sessions_strm_chunk_size = SESSIONS_STRM_CHUNK_SIZE; typedef struct SessionHook SessionHook; @@ -216189,6 +217802,7 @@ struct sqlite3_session { int bEnable; /* True if currently recording */ int bIndirect; /* True if all changes are indirect */ int bAutoAttach; /* True to auto-attach tables */ + int bImplicitPK; /* True to handle tables with implicit PK */ int rc; /* Non-zero if an error has occurred */ void *pFilterCtx; /* First argument to pass to xTableFilter */ int (*xTableFilter)(void *pCtx, const char *zTab); @@ -216265,6 +217879,7 @@ struct SessionTable { char *zName; /* Local name of table */ int nCol; /* Number of columns in table zName */ int bStat1; /* True if this is sqlite_stat1 */ + int bRowid; /* True if this table uses rowid for PK */ const char **azCol; /* Column names */ u8 *abPK; /* Array of primary key flags */ int nEntry; /* Total number of entries in hash table */ @@ -216657,6 +218272,7 @@ static unsigned int sessionHashAppendType(unsigned int h, int eType){ */ static int sessionPreupdateHash( sqlite3_session *pSession, /* Session object that owns pTab */ + i64 iRowid, SessionTable *pTab, /* Session table handle */ int bNew, /* True to hash the new.* PK */ int *piHash, /* OUT: Hash value */ @@ -216665,48 +218281,53 @@ static int sessionPreupdateHash( unsigned int h = 0; /* Hash value to return */ int i; /* Used to iterate through columns */ - assert( *pbNullPK==0 ); - assert( pTab->nCol==pSession->hook.xCount(pSession->hook.pCtx) ); - for(i=0; inCol; i++){ - if( pTab->abPK[i] ){ - int rc; - int eType; - sqlite3_value *pVal; + if( pTab->bRowid ){ + assert( pTab->nCol-1==pSession->hook.xCount(pSession->hook.pCtx) ); + h = sessionHashAppendI64(h, iRowid); + }else{ + assert( *pbNullPK==0 ); + assert( pTab->nCol==pSession->hook.xCount(pSession->hook.pCtx) ); + for(i=0; inCol; i++){ + if( pTab->abPK[i] ){ + int rc; + int eType; + sqlite3_value *pVal; - if( bNew ){ - rc = pSession->hook.xNew(pSession->hook.pCtx, i, &pVal); - }else{ - rc = pSession->hook.xOld(pSession->hook.pCtx, i, &pVal); - } - if( rc!=SQLITE_OK ) return rc; + if( bNew ){ + rc = pSession->hook.xNew(pSession->hook.pCtx, i, &pVal); + }else{ + rc = pSession->hook.xOld(pSession->hook.pCtx, i, &pVal); + } + if( rc!=SQLITE_OK ) return rc; - eType = sqlite3_value_type(pVal); - h = sessionHashAppendType(h, eType); - if( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT ){ - i64 iVal; - if( eType==SQLITE_INTEGER ){ - iVal = sqlite3_value_int64(pVal); + eType = sqlite3_value_type(pVal); + h = sessionHashAppendType(h, eType); + if( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT ){ + i64 iVal; + if( eType==SQLITE_INTEGER ){ + iVal = sqlite3_value_int64(pVal); + }else{ + double rVal = sqlite3_value_double(pVal); + assert( sizeof(iVal)==8 && sizeof(rVal)==8 ); + memcpy(&iVal, &rVal, 8); + } + h = sessionHashAppendI64(h, iVal); + }else if( eType==SQLITE_TEXT || eType==SQLITE_BLOB ){ + const u8 *z; + int n; + if( eType==SQLITE_TEXT ){ + z = (const u8 *)sqlite3_value_text(pVal); + }else{ + z = (const u8 *)sqlite3_value_blob(pVal); + } + n = sqlite3_value_bytes(pVal); + if( !z && (eType!=SQLITE_BLOB || n>0) ) return SQLITE_NOMEM; + h = sessionHashAppendBlob(h, n, z); }else{ - double rVal = sqlite3_value_double(pVal); - assert( sizeof(iVal)==8 && sizeof(rVal)==8 ); - memcpy(&iVal, &rVal, 8); + assert( eType==SQLITE_NULL ); + assert( pTab->bStat1==0 || i!=1 ); + *pbNullPK = 1; } - h = sessionHashAppendI64(h, iVal); - }else if( eType==SQLITE_TEXT || eType==SQLITE_BLOB ){ - const u8 *z; - int n; - if( eType==SQLITE_TEXT ){ - z = (const u8 *)sqlite3_value_text(pVal); - }else{ - z = (const u8 *)sqlite3_value_blob(pVal); - } - n = sqlite3_value_bytes(pVal); - if( !z && (eType!=SQLITE_BLOB || n>0) ) return SQLITE_NOMEM; - h = sessionHashAppendBlob(h, n, z); - }else{ - assert( eType==SQLITE_NULL ); - assert( pTab->bStat1==0 || i!=1 ); - *pbNullPK = 1; } } } @@ -216989,6 +218610,7 @@ static int sessionMergeUpdate( */ static int sessionPreupdateEqual( sqlite3_session *pSession, /* Session object that owns SessionTable */ + i64 iRowid, /* Rowid value if pTab->bRowid */ SessionTable *pTab, /* Table associated with change */ SessionChange *pChange, /* Change to compare to */ int op /* Current pre-update operation */ @@ -216996,6 +218618,11 @@ static int sessionPreupdateEqual( int iCol; /* Used to iterate through columns */ u8 *a = pChange->aRecord; /* Cursor used to scan change record */ + if( pTab->bRowid ){ + if( a[0]!=SQLITE_INTEGER ) return 0; + return sessionGetI64(&a[1])==iRowid; + } + assert( op==SQLITE_INSERT || op==SQLITE_UPDATE || op==SQLITE_DELETE ); for(iCol=0; iColnCol; iCol++){ if( !pTab->abPK[iCol] ){ @@ -217140,7 +218767,8 @@ static int sessionTableInfo( int *pnCol, /* OUT: number of columns */ const char **pzTab, /* OUT: Copy of zThis */ const char ***pazCol, /* OUT: Array of column names for table */ - u8 **pabPK /* OUT: Array of booleans - true for PK col */ + u8 **pabPK, /* OUT: Array of booleans - true for PK col */ + int *pbRowid /* OUT: True if only PK is a rowid */ ){ char *zPragma; sqlite3_stmt *pStmt; @@ -217152,6 +218780,7 @@ static int sessionTableInfo( u8 *pAlloc = 0; char **azCol = 0; u8 *abPK = 0; + int bRowid = 0; /* Set to true to use rowid as PK */ assert( pazCol && pabPK ); @@ -217196,10 +218825,15 @@ static int sessionTableInfo( } nByte = nThis + 1; + bRowid = (pbRowid!=0); while( SQLITE_ROW==sqlite3_step(pStmt) ){ nByte += sqlite3_column_bytes(pStmt, 1); nDbCol++; + if( sqlite3_column_int(pStmt, 5) ) bRowid = 0; } + if( nDbCol==0 ) bRowid = 0; + nDbCol += bRowid; + nByte += strlen(SESSIONS_ROWID); rc = sqlite3_reset(pStmt); if( rc==SQLITE_OK ){ @@ -217221,6 +218855,14 @@ static int sessionTableInfo( } i = 0; + if( bRowid ){ + size_t nName = strlen(SESSIONS_ROWID); + memcpy(pAlloc, SESSIONS_ROWID, nName+1); + azCol[i] = (char*)pAlloc; + pAlloc += nName+1; + abPK[i] = 1; + i++; + } while( SQLITE_ROW==sqlite3_step(pStmt) ){ int nName = sqlite3_column_bytes(pStmt, 1); const unsigned char *zName = sqlite3_column_text(pStmt, 1); @@ -217232,7 +218874,6 @@ static int sessionTableInfo( i++; } rc = sqlite3_reset(pStmt); - } /* If successful, populate the output variables. Otherwise, zero them and @@ -217249,6 +218890,7 @@ static int sessionTableInfo( if( pzTab ) *pzTab = 0; sessionFree(pSession, azCol); } + if( pbRowid ) *pbRowid = bRowid; sqlite3_finalize(pStmt); return rc; } @@ -217270,7 +218912,8 @@ static int sessionInitTable(sqlite3_session *pSession, SessionTable *pTab){ u8 *abPK; assert( pTab->azCol==0 || pTab->abPK==0 ); pSession->rc = sessionTableInfo(pSession, pSession->db, pSession->zDb, - pTab->zName, &pTab->nCol, 0, &pTab->azCol, &abPK + pTab->zName, &pTab->nCol, 0, &pTab->azCol, &abPK, + (pSession->bImplicitPK ? &pTab->bRowid : 0) ); if( pSession->rc==SQLITE_OK ){ int i; @@ -217342,6 +218985,7 @@ static int sessionUpdateMaxSize( ){ i64 nNew = 2; if( pC->op==SQLITE_INSERT ){ + if( pTab->bRowid ) nNew += 9; if( op!=SQLITE_DELETE ){ int ii; for(ii=0; iinCol; ii++){ @@ -217358,12 +219002,16 @@ static int sessionUpdateMaxSize( }else{ int ii; u8 *pCsr = pC->aRecord; - for(ii=0; iinCol; ii++){ + if( pTab->bRowid ){ + nNew += 9 + 1; + pCsr += 9; + } + for(ii=pTab->bRowid; iinCol; ii++){ int bChanged = 1; int nOld = 0; int eType; sqlite3_value *p = 0; - pSession->hook.xNew(pSession->hook.pCtx, ii, &p); + pSession->hook.xNew(pSession->hook.pCtx, ii-pTab->bRowid, &p); if( p==0 ){ return SQLITE_NOMEM; } @@ -217442,6 +219090,7 @@ static int sessionUpdateMaxSize( */ static void sessionPreupdateOneChange( int op, /* One of SQLITE_UPDATE, INSERT, DELETE */ + i64 iRowid, sqlite3_session *pSession, /* Session object pTab is attached to */ SessionTable *pTab /* Table that change applies to */ ){ @@ -217457,7 +219106,7 @@ static void sessionPreupdateOneChange( /* Check the number of columns in this xPreUpdate call matches the ** number of columns in the table. */ - if( pTab->nCol!=pSession->hook.xCount(pSession->hook.pCtx) ){ + if( (pTab->nCol-pTab->bRowid)!=pSession->hook.xCount(pSession->hook.pCtx) ){ pSession->rc = SQLITE_SCHEMA; return; } @@ -217490,14 +219139,16 @@ static void sessionPreupdateOneChange( /* Calculate the hash-key for this change. If the primary key of the row ** includes a NULL value, exit early. Such changes are ignored by the ** session module. */ - rc = sessionPreupdateHash(pSession, pTab, op==SQLITE_INSERT, &iHash, &bNull); + rc = sessionPreupdateHash( + pSession, iRowid, pTab, op==SQLITE_INSERT, &iHash, &bNull + ); if( rc!=SQLITE_OK ) goto error_out; if( bNull==0 ){ /* Search the hash table for an existing record for this row. */ SessionChange *pC; for(pC=pTab->apChange[iHash]; pC; pC=pC->pNext){ - if( sessionPreupdateEqual(pSession, pTab, pC, op) ) break; + if( sessionPreupdateEqual(pSession, iRowid, pTab, pC, op) ) break; } if( pC==0 ){ @@ -217512,7 +219163,7 @@ static void sessionPreupdateOneChange( /* Figure out how large an allocation is required */ nByte = sizeof(SessionChange); - for(i=0; inCol; i++){ + for(i=0; i<(pTab->nCol-pTab->bRowid); i++){ sqlite3_value *p = 0; if( op!=SQLITE_INSERT ){ TESTONLY(int trc = ) pSession->hook.xOld(pSession->hook.pCtx, i, &p); @@ -217527,6 +219178,9 @@ static void sessionPreupdateOneChange( rc = sessionSerializeValue(0, p, &nByte); if( rc!=SQLITE_OK ) goto error_out; } + if( pTab->bRowid ){ + nByte += 9; /* Size of rowid field - an integer */ + } /* Allocate the change object */ pC = (SessionChange *)sessionMalloc64(pSession, nByte); @@ -217543,7 +219197,12 @@ static void sessionPreupdateOneChange( ** required values and encodings have already been cached in memory. ** It is not possible for an OOM to occur in this block. */ nByte = 0; - for(i=0; inCol; i++){ + if( pTab->bRowid ){ + pC->aRecord[0] = SQLITE_INTEGER; + sessionPutI64(&pC->aRecord[1], iRowid); + nByte = 9; + } + for(i=0; i<(pTab->nCol-pTab->bRowid); i++){ sqlite3_value *p = 0; if( op!=SQLITE_INSERT ){ pSession->hook.xOld(pSession->hook.pCtx, i, &p); @@ -217658,9 +219317,10 @@ static void xPreUpdate( pSession->rc = sessionFindTable(pSession, zName, &pTab); if( pTab ){ assert( pSession->rc==SQLITE_OK ); - sessionPreupdateOneChange(op, pSession, pTab); + assert( op==SQLITE_UPDATE || iKey1==iKey2 ); + sessionPreupdateOneChange(op, iKey1, pSession, pTab); if( op==SQLITE_UPDATE ){ - sessionPreupdateOneChange(SQLITE_INSERT, pSession, pTab); + sessionPreupdateOneChange(SQLITE_INSERT, iKey2, pSession, pTab); } } } @@ -217699,6 +219359,7 @@ static void sessionPreupdateHooks( typedef struct SessionDiffCtx SessionDiffCtx; struct SessionDiffCtx { sqlite3_stmt *pStmt; + int bRowid; int nOldOff; }; @@ -217707,17 +219368,17 @@ struct SessionDiffCtx { */ static int sessionDiffOld(void *pCtx, int iVal, sqlite3_value **ppVal){ SessionDiffCtx *p = (SessionDiffCtx*)pCtx; - *ppVal = sqlite3_column_value(p->pStmt, iVal+p->nOldOff); + *ppVal = sqlite3_column_value(p->pStmt, iVal+p->nOldOff+p->bRowid); return SQLITE_OK; } static int sessionDiffNew(void *pCtx, int iVal, sqlite3_value **ppVal){ SessionDiffCtx *p = (SessionDiffCtx*)pCtx; - *ppVal = sqlite3_column_value(p->pStmt, iVal); + *ppVal = sqlite3_column_value(p->pStmt, iVal+p->bRowid); return SQLITE_OK; } static int sessionDiffCount(void *pCtx){ SessionDiffCtx *p = (SessionDiffCtx*)pCtx; - return p->nOldOff ? p->nOldOff : sqlite3_column_count(p->pStmt); + return (p->nOldOff ? p->nOldOff : sqlite3_column_count(p->pStmt)) - p->bRowid; } static int sessionDiffDepth(void *pCtx){ (void)pCtx; @@ -217796,14 +219457,16 @@ static char *sessionExprCompareOther( static char *sessionSelectFindNew( const char *zDb1, /* Pick rows in this db only */ const char *zDb2, /* But not in this one */ + int bRowid, const char *zTbl, /* Table name */ const char *zExpr ){ + const char *zSel = (bRowid ? SESSIONS_ROWID ", *" : "*"); char *zRet = sqlite3_mprintf( - "SELECT * FROM \"%w\".\"%w\" WHERE NOT EXISTS (" + "SELECT %s FROM \"%w\".\"%w\" WHERE NOT EXISTS (" " SELECT 1 FROM \"%w\".\"%w\" WHERE %s" ")", - zDb1, zTbl, zDb2, zTbl, zExpr + zSel, zDb1, zTbl, zDb2, zTbl, zExpr ); return zRet; } @@ -217817,7 +219480,9 @@ static int sessionDiffFindNew( char *zExpr ){ int rc = SQLITE_OK; - char *zStmt = sessionSelectFindNew(zDb1, zDb2, pTab->zName,zExpr); + char *zStmt = sessionSelectFindNew( + zDb1, zDb2, pTab->bRowid, pTab->zName, zExpr + ); if( zStmt==0 ){ rc = SQLITE_NOMEM; @@ -217828,8 +219493,10 @@ static int sessionDiffFindNew( SessionDiffCtx *pDiffCtx = (SessionDiffCtx*)pSession->hook.pCtx; pDiffCtx->pStmt = pStmt; pDiffCtx->nOldOff = 0; + pDiffCtx->bRowid = pTab->bRowid; while( SQLITE_ROW==sqlite3_step(pStmt) ){ - sessionPreupdateOneChange(op, pSession, pTab); + i64 iRowid = (pTab->bRowid ? sqlite3_column_int64(pStmt, 0) : 0); + sessionPreupdateOneChange(op, iRowid, pSession, pTab); } rc = sqlite3_finalize(pStmt); } @@ -217839,6 +219506,27 @@ static int sessionDiffFindNew( return rc; } +/* +** Return a comma-separated list of the fully-qualified (with both database +** and table name) column names from table pTab. e.g. +** +** "main"."t1"."a", "main"."t1"."b", "main"."t1"."c" +*/ +static char *sessionAllCols( + const char *zDb, + SessionTable *pTab +){ + int ii; + char *zRet = 0; + for(ii=0; iinCol; ii++){ + zRet = sqlite3_mprintf("%z%s\"%w\".\"%w\".\"%w\"", + zRet, (zRet ? ", " : ""), zDb, pTab->zName, pTab->azCol[ii] + ); + if( !zRet ) break; + } + return zRet; +} + static int sessionDiffFindModified( sqlite3_session *pSession, SessionTable *pTab, @@ -217853,11 +219541,13 @@ static int sessionDiffFindModified( if( zExpr2==0 ){ rc = SQLITE_NOMEM; }else{ + char *z1 = sessionAllCols(pSession->zDb, pTab); + char *z2 = sessionAllCols(zFrom, pTab); char *zStmt = sqlite3_mprintf( - "SELECT * FROM \"%w\".\"%w\", \"%w\".\"%w\" WHERE %s AND (%z)", - pSession->zDb, pTab->zName, zFrom, pTab->zName, zExpr, zExpr2 + "SELECT %s,%s FROM \"%w\".\"%w\", \"%w\".\"%w\" WHERE %s AND (%z)", + z1, z2, pSession->zDb, pTab->zName, zFrom, pTab->zName, zExpr, zExpr2 ); - if( zStmt==0 ){ + if( zStmt==0 || z1==0 || z2==0 ){ rc = SQLITE_NOMEM; }else{ sqlite3_stmt *pStmt; @@ -217868,12 +219558,15 @@ static int sessionDiffFindModified( pDiffCtx->pStmt = pStmt; pDiffCtx->nOldOff = pTab->nCol; while( SQLITE_ROW==sqlite3_step(pStmt) ){ - sessionPreupdateOneChange(SQLITE_UPDATE, pSession, pTab); + i64 iRowid = (pTab->bRowid ? sqlite3_column_int64(pStmt, 0) : 0); + sessionPreupdateOneChange(SQLITE_UPDATE, iRowid, pSession, pTab); } rc = sqlite3_finalize(pStmt); } - sqlite3_free(zStmt); } + sqlite3_free(zStmt); + sqlite3_free(z1); + sqlite3_free(z2); } return rc; @@ -217912,9 +219605,12 @@ SQLITE_API int sqlite3session_diff( int bHasPk = 0; int bMismatch = 0; int nCol; /* Columns in zFrom.zTbl */ + int bRowid = 0; u8 *abPK; const char **azCol = 0; - rc = sessionTableInfo(0, db, zFrom, zTbl, &nCol, 0, &azCol, &abPK); + rc = sessionTableInfo(0, db, zFrom, zTbl, &nCol, 0, &azCol, &abPK, + pSession->bImplicitPK ? &bRowid : 0 + ); if( rc==SQLITE_OK ){ if( pTo->nCol!=nCol ){ bMismatch = 1; @@ -218256,9 +219952,10 @@ static void sessionAppendStr( int *pRc ){ int nStr = sqlite3Strlen30(zStr); - if( 0==sessionBufferGrow(p, nStr, pRc) ){ + if( 0==sessionBufferGrow(p, nStr+1, pRc) ){ memcpy(&p->aBuf[p->nBuf], zStr, nStr); p->nBuf += nStr; + p->aBuf[p->nBuf] = 0x00; } } @@ -218280,6 +219977,27 @@ static void sessionAppendInteger( sessionAppendStr(p, aBuf, pRc); } +static void sessionAppendPrintf( + SessionBuffer *p, /* Buffer to append to */ + int *pRc, + const char *zFmt, + ... +){ + if( *pRc==SQLITE_OK ){ + char *zApp = 0; + va_list ap; + va_start(ap, zFmt); + zApp = sqlite3_vmprintf(zFmt, ap); + if( zApp==0 ){ + *pRc = SQLITE_NOMEM; + }else{ + sessionAppendStr(p, zApp, pRc); + } + va_end(ap); + sqlite3_free(zApp); + } +} + /* ** This function is a no-op if *pRc is other than SQLITE_OK when it is ** called. Otherwise, append the string zStr enclosed in quotes (") and @@ -218294,7 +220012,7 @@ static void sessionAppendIdent( const char *zStr, /* String to quote, escape and append */ int *pRc /* IN/OUT: Error code */ ){ - int nStr = sqlite3Strlen30(zStr)*2 + 2 + 1; + int nStr = sqlite3Strlen30(zStr)*2 + 2 + 2; if( 0==sessionBufferGrow(p, nStr, pRc) ){ char *zOut = (char *)&p->aBuf[p->nBuf]; const char *zIn = zStr; @@ -218305,6 +220023,7 @@ static void sessionAppendIdent( } *zOut++ = '"'; p->nBuf = (int)((u8 *)zOut - p->aBuf); + p->aBuf[p->nBuf] = 0x00; } } @@ -218440,7 +220159,7 @@ static int sessionAppendUpdate( /* If at least one field has been modified, this is not a no-op. */ if( bChanged ) bNoop = 0; - /* Add a field to the old.* record. This is omitted if this modules is + /* Add a field to the old.* record. This is omitted if this module is ** currently generating a patchset. */ if( bPatchset==0 ){ if( bChanged || abPK[i] ){ @@ -218529,12 +220248,20 @@ static int sessionAppendDelete( ** Formulate and prepare a SELECT statement to retrieve a row from table ** zTab in database zDb based on its primary key. i.e. ** -** SELECT * FROM zDb.zTab WHERE pk1 = ? AND pk2 = ? AND ... +** SELECT *, FROM zDb.zTab WHERE (pk1, pk2,...) IS (?1, ?2,...) +** +** where is: +** +** 1 AND (?A OR ?1 IS ) AND ... +** +** for each non-pk . */ static int sessionSelectStmt( sqlite3 *db, /* Database handle */ + int bIgnoreNoop, const char *zDb, /* Database name */ const char *zTab, /* Table name */ + int bRowid, int nCol, /* Number of columns in table */ const char **azCol, /* Names of table columns */ u8 *abPK, /* PRIMARY KEY array */ @@ -218542,8 +220269,50 @@ static int sessionSelectStmt( ){ int rc = SQLITE_OK; char *zSql = 0; + const char *zSep = ""; + const char *zCols = bRowid ? SESSIONS_ROWID ", *" : "*"; int nSql = -1; + int i; + SessionBuffer nooptest = {0, 0, 0}; + SessionBuffer pkfield = {0, 0, 0}; + SessionBuffer pkvar = {0, 0, 0}; + + sessionAppendStr(&nooptest, ", 1", &rc); + + if( 0==sqlite3_stricmp("sqlite_stat1", zTab) ){ + sessionAppendStr(&nooptest, " AND (?6 OR ?3 IS stat)", &rc); + sessionAppendStr(&pkfield, "tbl, idx", &rc); + sessionAppendStr(&pkvar, + "?1, (CASE WHEN ?2=X'' THEN NULL ELSE ?2 END)", &rc + ); + zCols = "tbl, ?2, stat"; + }else{ + for(i=0; izDb, zName, &nCol, 0,&azCol,&abPK); - if( !rc && (pTab->nCol!=nCol || memcmp(abPK, pTab->abPK, nCol)) ){ + rc = sessionTableInfo( + 0, db, pSession->zDb, zName, &nCol, 0, &azCol, &abPK, + (pSession->bImplicitPK ? &bRowid : 0) + ); + if( rc==SQLITE_OK && ( + pTab->nCol!=nCol + || pTab->bRowid!=bRowid + || memcmp(abPK, pTab->abPK, nCol) + )){ rc = SQLITE_SCHEMA; } @@ -218736,7 +220516,8 @@ static int sessionGenerateChangeset( /* Build and compile a statement to execute: */ if( rc==SQLITE_OK ){ rc = sessionSelectStmt( - db, pSession->zDb, zName, nCol, azCol, abPK, &pSel); + db, 0, pSession->zDb, zName, bRowid, nCol, azCol, abPK, &pSel + ); } nNoop = buf.nBuf; @@ -218819,7 +220600,7 @@ SQLITE_API int sqlite3session_changeset( int rc; if( pnChangeset==0 || ppChangeset==0 ) return SQLITE_MISUSE; - rc = sessionGenerateChangeset(pSession, 0, 0, 0, pnChangeset,ppChangeset); + rc = sessionGenerateChangeset(pSession, 0, 0, 0, pnChangeset, ppChangeset); assert( rc || pnChangeset==0 || pSession->bEnableSize==0 || *pnChangeset<=pSession->nMaxChangesetSize ); @@ -218937,6 +220718,19 @@ SQLITE_API int sqlite3session_object_config(sqlite3_session *pSession, int op, v break; } + case SQLITE_SESSION_OBJCONFIG_ROWID: { + int iArg = *(int*)pArg; + if( iArg>=0 ){ + if( pSession->pTable ){ + rc = SQLITE_MISUSE; + }else{ + pSession->bImplicitPK = (iArg!=0); + } + } + *(int*)pArg = pSession->bImplicitPK; + break; + } + default: rc = SQLITE_MISUSE; } @@ -219925,6 +221719,8 @@ struct SessionApplyCtx { SessionBuffer rebase; /* Rebase information (if any) here */ u8 bRebaseStarted; /* If table header is already in rebase */ u8 bRebase; /* True to collect rebase information */ + u8 bIgnoreNoop; /* True to ignore no-op conflicts */ + int bRowid; }; /* Number of prepared UPDATE statements to cache. */ @@ -220175,8 +221971,10 @@ static int sessionSelectRow( const char *zTab, /* Table name */ SessionApplyCtx *p /* Session changeset-apply context */ ){ - return sessionSelectStmt( - db, "main", zTab, p->nCol, p->azCol, p->abPK, &p->pSelect); + /* TODO */ + return sessionSelectStmt(db, p->bIgnoreNoop, + "main", zTab, p->bRowid, p->nCol, p->azCol, p->abPK, &p->pSelect + ); } /* @@ -220335,20 +222133,33 @@ static int sessionBindRow( */ static int sessionSeekToRow( sqlite3_changeset_iter *pIter, /* Changeset iterator */ - u8 *abPK, /* Primary key flags array */ - sqlite3_stmt *pSelect /* SELECT statement from sessionSelectRow() */ + SessionApplyCtx *p ){ + sqlite3_stmt *pSelect = p->pSelect; int rc; /* Return code */ int nCol; /* Number of columns in table */ int op; /* Changset operation (SQLITE_UPDATE etc.) */ const char *zDummy; /* Unused */ + sqlite3_clear_bindings(pSelect); sqlite3changeset_op(pIter, &zDummy, &nCol, &op, 0); rc = sessionBindRow(pIter, op==SQLITE_INSERT ? sqlite3changeset_new : sqlite3changeset_old, - nCol, abPK, pSelect + nCol, p->abPK, pSelect ); + if( op!=SQLITE_DELETE && p->bIgnoreNoop ){ + int ii; + for(ii=0; rc==SQLITE_OK && iiabPK[ii]==0 ){ + sqlite3_value *pVal = 0; + sqlite3changeset_new(pIter, ii, &pVal); + sqlite3_bind_int(pSelect, ii+1+nCol, (pVal==0)); + if( pVal ) rc = sessionBindValue(pSelect, ii+1, pVal); + } + } + } + if( rc==SQLITE_OK ){ rc = sqlite3_step(pSelect); if( rc!=SQLITE_ROW ) rc = sqlite3_reset(pSelect); @@ -220463,16 +222274,22 @@ static int sessionConflictHandler( /* Bind the new.* PRIMARY KEY values to the SELECT statement. */ if( pbReplace ){ - rc = sessionSeekToRow(pIter, p->abPK, p->pSelect); + rc = sessionSeekToRow(pIter, p); }else{ rc = SQLITE_OK; } if( rc==SQLITE_ROW ){ /* There exists another row with the new.* primary key. */ - pIter->pConflict = p->pSelect; - res = xConflict(pCtx, eType, pIter); - pIter->pConflict = 0; + if( p->bIgnoreNoop + && sqlite3_column_int(p->pSelect, sqlite3_column_count(p->pSelect)-1) + ){ + res = SQLITE_CHANGESET_OMIT; + }else{ + pIter->pConflict = p->pSelect; + res = xConflict(pCtx, eType, pIter); + pIter->pConflict = 0; + } rc = sqlite3_reset(p->pSelect); }else if( rc==SQLITE_OK ){ if( p->bDeferConstraints && eType==SQLITE_CHANGESET_CONFLICT ){ @@ -220580,7 +222397,7 @@ static int sessionApplyOneOp( sqlite3_step(p->pDelete); rc = sqlite3_reset(p->pDelete); - if( rc==SQLITE_OK && sqlite3_changes(p->db)==0 ){ + if( rc==SQLITE_OK && sqlite3_changes(p->db)==0 && p->bIgnoreNoop==0 ){ rc = sessionConflictHandler( SQLITE_CHANGESET_DATA, p, pIter, xConflict, pCtx, pbRetry ); @@ -220637,7 +222454,7 @@ static int sessionApplyOneOp( /* Check if there is a conflicting row. For sqlite_stat1, this needs ** to be done using a SELECT, as there is no PRIMARY KEY in the ** database schema to throw an exception if a duplicate is inserted. */ - rc = sessionSeekToRow(pIter, p->abPK, p->pSelect); + rc = sessionSeekToRow(pIter, p); if( rc==SQLITE_ROW ){ rc = SQLITE_CONSTRAINT; sqlite3_reset(p->pSelect); @@ -220814,6 +222631,7 @@ static int sessionChangesetApply( memset(&sApply, 0, sizeof(sApply)); sApply.bRebase = (ppRebase && pnRebase); sApply.bInvertConstraints = !!(flags & SQLITE_CHANGESETAPPLY_INVERT); + sApply.bIgnoreNoop = !!(flags & SQLITE_CHANGESETAPPLY_IGNORENOOP); sqlite3_mutex_enter(sqlite3_db_mutex(db)); if( (flags & SQLITE_CHANGESETAPPLY_NOSAVEPOINT)==0 ){ rc = sqlite3_exec(db, "SAVEPOINT changeset_apply", 0, 0, 0); @@ -220851,6 +222669,7 @@ static int sessionChangesetApply( sApply.bStat1 = 0; sApply.bDeferConstraints = 1; sApply.bRebaseStarted = 0; + sApply.bRowid = 0; memset(&sApply.constraints, 0, sizeof(SessionBuffer)); /* If an xFilter() callback was specified, invoke it now. If the @@ -220870,8 +222689,8 @@ static int sessionChangesetApply( int i; sqlite3changeset_pk(pIter, &abPK, 0); - rc = sessionTableInfo(0, - db, "main", zNew, &sApply.nCol, &zTab, &sApply.azCol, &sApply.abPK + rc = sessionTableInfo(0, db, "main", zNew, + &sApply.nCol, &zTab, &sApply.azCol, &sApply.abPK, &sApply.bRowid ); if( rc!=SQLITE_OK ) break; for(i=0; iiPos++; - if( p->iRangeEnd>0 ){ + if( p->iRangeEnd>=0 ){ if( iPosiRangeStart || iPos>p->iRangeEnd ) return SQLITE_OK; if( p->iRangeStart && iPos==p->iRangeStart ) p->iOff = iStartOff; } @@ -225082,7 +226907,7 @@ static int fts5HighlightCb( } if( iPos==p->iter.iEnd ){ - if( p->iRangeEnd && p->iter.iStartiRangeStart ){ + if( p->iRangeEnd>=0 && p->iter.iStartiRangeStart ){ fts5HighlightAppend(&rc, p, p->zOpen, -1); } fts5HighlightAppend(&rc, p, &p->zIn[p->iOff], iEndOff - p->iOff); @@ -225093,7 +226918,7 @@ static int fts5HighlightCb( } } - if( p->iRangeEnd>0 && iPos==p->iRangeEnd ){ + if( p->iRangeEnd>=0 && iPos==p->iRangeEnd ){ fts5HighlightAppend(&rc, p, &p->zIn[p->iOff], iEndOff - p->iOff); p->iOff = iEndOff; if( iPos>=p->iter.iStart && iPositer.iEnd ){ @@ -225128,6 +226953,7 @@ static void fts5HighlightFunction( memset(&ctx, 0, sizeof(HighlightContext)); ctx.zOpen = (const char*)sqlite3_value_text(apVal[1]); ctx.zClose = (const char*)sqlite3_value_text(apVal[2]); + ctx.iRangeEnd = -1; rc = pApi->xColumnText(pFts, iCol, &ctx.zIn, &ctx.nIn); if( ctx.zIn ){ @@ -225313,6 +227139,7 @@ static void fts5SnippetFunction( iCol = sqlite3_value_int(apVal[0]); ctx.zOpen = fts5ValueToText(apVal[1]); ctx.zClose = fts5ValueToText(apVal[2]); + ctx.iRangeEnd = -1; zEllips = fts5ValueToText(apVal[3]); nToken = sqlite3_value_int(apVal[4]); @@ -226581,6 +228408,7 @@ static int sqlite3Fts5ConfigParse( rc = SQLITE_ERROR; } + assert( (pRet->abUnindexed && pRet->azCol) || rc!=SQLITE_OK ); for(i=3; rc==SQLITE_OK && ibSecureDelete = (bVal ? 1 : 0); + } }else{ *pbBadkey = 1; } @@ -226978,15 +228818,20 @@ static int sqlite3Fts5ConfigLoad(Fts5Config *pConfig, int iCookie){ rc = sqlite3_finalize(p); } - if( rc==SQLITE_OK && iVersion!=FTS5_CURRENT_VERSION ){ + if( rc==SQLITE_OK + && iVersion!=FTS5_CURRENT_VERSION + && iVersion!=FTS5_CURRENT_VERSION_SECUREDELETE + ){ rc = SQLITE_ERROR; if( pConfig->pzErrmsg ){ assert( 0==*pConfig->pzErrmsg ); - *pConfig->pzErrmsg = sqlite3_mprintf( - "invalid fts5 file format (found %d, expected %d) - run 'rebuild'", - iVersion, FTS5_CURRENT_VERSION + *pConfig->pzErrmsg = sqlite3_mprintf("invalid fts5 file format " + "(found %d, expected %d or %d) - run 'rebuild'", + iVersion, FTS5_CURRENT_VERSION, FTS5_CURRENT_VERSION_SECUREDELETE ); } + }else{ + pConfig->iVersion = iVersion; } if( rc==SQLITE_OK ){ @@ -227014,6 +228859,10 @@ static int sqlite3Fts5ConfigLoad(Fts5Config *pConfig, int iCookie){ /* #include "fts5Int.h" */ /* #include "fts5parse.h" */ +#ifndef SQLITE_FTS5_MAX_EXPR_DEPTH +# define SQLITE_FTS5_MAX_EXPR_DEPTH 256 +#endif + /* ** All token types in the generated fts5parse.h file are greater than 0. */ @@ -227054,11 +228903,17 @@ struct Fts5Expr { ** FTS5_NOT (nChild, apChild valid) ** FTS5_STRING (pNear valid) ** FTS5_TERM (pNear valid) +** +** iHeight: +** Distance from this node to furthest leaf. This is always 0 for nodes +** of type FTS5_STRING and FTS5_TERM. For all other nodes it is one +** greater than the largest child value. */ struct Fts5ExprNode { int eType; /* Node type */ int bEof; /* True at EOF */ int bNomatch; /* True if entry is not a match */ + int iHeight; /* Distance to tree leaf nodes */ /* Next method for this node. */ int (*xNext)(Fts5Expr*, Fts5ExprNode*, int, i64); @@ -227128,6 +228983,31 @@ struct Fts5Parse { int bPhraseToAnd; /* Convert "a+b" to "a AND b" */ }; +/* +** Check that the Fts5ExprNode.iHeight variables are set correctly in +** the expression tree passed as the only argument. +*/ +#ifndef NDEBUG +static void assert_expr_depth_ok(int rc, Fts5ExprNode *p){ + if( rc==SQLITE_OK ){ + if( p->eType==FTS5_TERM || p->eType==FTS5_STRING || p->eType==0 ){ + assert( p->iHeight==0 ); + }else{ + int ii; + int iMaxChild = 0; + for(ii=0; iinChild; ii++){ + Fts5ExprNode *pChild = p->apChild[ii]; + iMaxChild = MAX(iMaxChild, pChild->iHeight); + assert_expr_depth_ok(SQLITE_OK, pChild); + } + assert( p->iHeight==iMaxChild+1 ); + } + } +} +#else +# define assert_expr_depth_ok(rc, p) +#endif + static void sqlite3Fts5ParseError(Fts5Parse *pParse, const char *zFmt, ...){ va_list ap; va_start(ap, zFmt); @@ -227242,6 +229122,8 @@ static int sqlite3Fts5ExprNew( }while( sParse.rc==SQLITE_OK && t!=FTS5_EOF ); sqlite3Fts5ParserFree(pEngine, fts5ParseFree); + assert_expr_depth_ok(sParse.rc, sParse.pExpr); + /* If the LHS of the MATCH expression was a user column, apply the ** implicit column-filter. */ if( iColnCol && sParse.pExpr && sParse.rc==SQLITE_OK ){ @@ -227404,7 +229286,7 @@ static int sqlite3Fts5ExprAnd(Fts5Expr **pp1, Fts5Expr *p2){ Fts5Parse sParse; memset(&sParse, 0, sizeof(sParse)); - if( *pp1 ){ + if( *pp1 && p2 ){ Fts5Expr *p1 = *pp1; int nPhrase = p1->nPhrase + p2->nPhrase; @@ -227429,7 +229311,7 @@ static int sqlite3Fts5ExprAnd(Fts5Expr **pp1, Fts5Expr *p2){ } sqlite3_free(p2->apExprPhrase); sqlite3_free(p2); - }else{ + }else if( p2 ){ *pp1 = p2; } @@ -229203,6 +231085,7 @@ static void fts5ExprAssignXNext(Fts5ExprNode *pNode){ } static void fts5ExprAddChildren(Fts5ExprNode *p, Fts5ExprNode *pSub){ + int ii = p->nChild; if( p->eType!=FTS5_NOT && pSub->eType==p->eType ){ int nByte = sizeof(Fts5ExprNode*) * pSub->nChild; memcpy(&p->apChild[p->nChild], pSub->apChild, nByte); @@ -229211,6 +231094,9 @@ static void fts5ExprAddChildren(Fts5ExprNode *p, Fts5ExprNode *pSub){ }else{ p->apChild[p->nChild++] = pSub; } + for( ; iinChild; ii++){ + p->iHeight = MAX(p->iHeight, p->apChild[ii]->iHeight + 1); + } } /* @@ -229241,6 +231127,7 @@ static Fts5ExprNode *fts5ParsePhraseToAnd( if( pRet ){ pRet->eType = FTS5_AND; pRet->nChild = nTerm; + pRet->iHeight = 1; fts5ExprAssignXNext(pRet); pParse->nPhrase--; for(ii=0; iiiHeight>SQLITE_FTS5_MAX_EXPR_DEPTH ){ + sqlite3Fts5ParseError(pParse, + "fts5 expression tree is too large (maximum depth %d)", + SQLITE_FTS5_MAX_EXPR_DEPTH + ); + sqlite3_free(pRet); + pRet = 0; + } } } } @@ -230946,6 +232841,8 @@ struct Fts5Index { sqlite3_stmt *pIdxSelect; int nRead; /* Total number of blocks read */ + sqlite3_stmt *pDeleteFromIdx; + sqlite3_stmt *pDataVersion; i64 iStructVersion; /* data_version when pStruct read */ Fts5Structure *pStruct; /* Current db structure (or NULL) */ @@ -231038,9 +232935,6 @@ struct Fts5CResult { ** iLeafOffset: ** Byte offset within the current leaf that is the first byte of the ** position list data (one byte passed the position-list size field). -** rowid field of the current entry. Usually this is the size field of the -** position list data. The exception is if the rowid for the current entry -** is the last thing on the leaf page. ** ** pLeaf: ** Buffer containing current leaf page data. Set to NULL at EOF. @@ -231599,6 +233493,7 @@ static int fts5StructureDecode( rc = FTS5_CORRUPT; break; } + assert( pSeg!=0 ); i += fts5GetVarint32(&pData[i], pSeg->iSegid); i += fts5GetVarint32(&pData[i], pSeg->pgnoFirst); i += fts5GetVarint32(&pData[i], pSeg->pgnoLast); @@ -231629,6 +233524,7 @@ static int fts5StructureDecode( */ static void fts5StructureAddLevel(int *pRc, Fts5Structure **ppStruct){ fts5StructureMakeWritable(pRc, ppStruct); + assert( (ppStruct!=0 && (*ppStruct)!=0) || (*pRc)!=SQLITE_OK ); if( *pRc==SQLITE_OK ){ Fts5Structure *pStruct = *ppStruct; int nLevel = pStruct->nLevel; @@ -232087,42 +233983,25 @@ static int fts5DlidxLvlPrev(Fts5DlidxLvl *pLvl){ pLvl->bEof = 1; }else{ u8 *a = pLvl->pData->p; - i64 iVal; - int iLimit; - int ii; - int nZero = 0; - /* Currently iOff points to the first byte of a varint. This block - ** decrements iOff until it points to the first byte of the previous - ** varint. Taking care not to read any memory locations that occur - ** before the buffer in memory. */ - iLimit = (iOff>9 ? iOff-9 : 0); - for(iOff--; iOff>iLimit; iOff--){ - if( (a[iOff-1] & 0x80)==0 ) break; - } + pLvl->iOff = 0; + fts5DlidxLvlNext(pLvl); + while( 1 ){ + int nZero = 0; + int ii = pLvl->iOff; + u64 delta = 0; - fts5GetVarint(&a[iOff], (u64*)&iVal); - pLvl->iRowid -= iVal; - pLvl->iLeafPgno--; - - /* Skip backwards past any 0x00 varints. */ - for(ii=iOff-1; ii>=pLvl->iFirstOff && a[ii]==0x00; ii--){ - nZero++; - } - if( ii>=pLvl->iFirstOff && (a[ii] & 0x80) ){ - /* The byte immediately before the last 0x00 byte has the 0x80 bit - ** set. So the last 0x00 is only a varint 0 if there are 8 more 0x80 - ** bytes before a[ii]. */ - int bZero = 0; /* True if last 0x00 counts */ - if( (ii-8)>=pLvl->iFirstOff ){ - int j; - for(j=1; j<=8 && (a[ii-j] & 0x80); j++); - bZero = (j>8); + while( a[ii]==0 ){ + nZero++; + ii++; } - if( bZero==0 ) nZero--; + ii += sqlite3Fts5GetVarint(&a[ii], &delta); + + if( ii>=iOff ) break; + pLvl->iLeafPgno += nZero+1; + pLvl->iRowid += delta; + pLvl->iOff = ii; } - pLvl->iLeafPgno -= nZero; - pLvl->iOff = iOff - nZero; } return pLvl->bEof; @@ -232318,7 +234197,7 @@ static void fts5SegIterLoadRowid(Fts5Index *p, Fts5SegIter *pIter){ i64 iOff = pIter->iLeafOffset; ASSERT_SZLEAF_OK(pIter->pLeaf); - if( iOff>=pIter->pLeaf->szLeaf ){ + while( iOff>=pIter->pLeaf->szLeaf ){ fts5SegIterNextPage(p, pIter); if( pIter->pLeaf==0 ){ if( p->rc==SQLITE_OK ) p->rc = FTS5_CORRUPT; @@ -232417,10 +234296,12 @@ static void fts5SegIterInit( fts5SegIterSetNext(p, pIter); pIter->pSeg = pSeg; pIter->iLeafPgno = pSeg->pgnoFirst-1; - fts5SegIterNextPage(p, pIter); + do { + fts5SegIterNextPage(p, pIter); + }while( p->rc==SQLITE_OK && pIter->pLeaf && pIter->pLeaf->nn==4 ); } - if( p->rc==SQLITE_OK ){ + if( p->rc==SQLITE_OK && pIter->pLeaf ){ pIter->iLeafOffset = 4; assert( pIter->pLeaf!=0 ); assert_nc( pIter->pLeaf->nn>4 ); @@ -232614,7 +234495,7 @@ static void fts5SegIterNext_None( iOff = pIter->iLeafOffset; /* Next entry is on the next page */ - if( pIter->pSeg && iOff>=pIter->pLeaf->szLeaf ){ + while( pIter->pSeg && iOff>=pIter->pLeaf->szLeaf ){ fts5SegIterNextPage(p, pIter); if( p->rc || pIter->pLeaf==0 ) return; pIter->iRowid = 0; @@ -232807,7 +234688,7 @@ static void fts5SegIterReverse(Fts5Index *p, Fts5SegIter *pIter){ Fts5Data *pLast = 0; int pgnoLast = 0; - if( pDlidx ){ + if( pDlidx && p->pConfig->iVersion==FTS5_CURRENT_VERSION ){ int iSegid = pIter->pSeg->iSegid; pgnoLast = fts5DlidxIterPgno(pDlidx); pLast = fts5LeafRead(p, FTS5_SEGMENT_ROWID(iSegid, pgnoLast)); @@ -233368,7 +235249,8 @@ static int fts5MultiIterDoCompare(Fts5Iter *pIter, int iOut){ /* ** Move the seg-iter so that it points to the first rowid on page iLeafPgno. -** It is an error if leaf iLeafPgno does not exist or contains no rowids. +** It is an error if leaf iLeafPgno does not exist. Unless the db is +** a 'secure-delete' db, if it contains no rowids then this is also an error. */ static void fts5SegIterGotoPage( Fts5Index *p, /* FTS5 backend object */ @@ -233383,21 +235265,23 @@ static void fts5SegIterGotoPage( fts5DataRelease(pIter->pNextLeaf); pIter->pNextLeaf = 0; pIter->iLeafPgno = iLeafPgno-1; - fts5SegIterNextPage(p, pIter); - assert( p->rc!=SQLITE_OK || pIter->iLeafPgno==iLeafPgno ); - if( p->rc==SQLITE_OK && ALWAYS(pIter->pLeaf!=0) ){ + while( p->rc==SQLITE_OK ){ int iOff; - u8 *a = pIter->pLeaf->p; - int n = pIter->pLeaf->szLeaf; - + fts5SegIterNextPage(p, pIter); + if( pIter->pLeaf==0 ) break; iOff = fts5LeafFirstRowidOff(pIter->pLeaf); - if( iOff<4 || iOff>=n ){ - p->rc = FTS5_CORRUPT; - }else{ - iOff += fts5GetVarint(&a[iOff], (u64*)&pIter->iRowid); - pIter->iLeafOffset = iOff; - fts5SegIterLoadNPos(p, pIter); + if( iOff>0 ){ + u8 *a = pIter->pLeaf->p; + int n = pIter->pLeaf->szLeaf; + if( iOff<4 || iOff>=n ){ + p->rc = FTS5_CORRUPT; + }else{ + iOff += fts5GetVarint(&a[iOff], (u64*)&pIter->iRowid); + pIter->iLeafOffset = iOff; + fts5SegIterLoadNPos(p, pIter); + } + break; } } } @@ -234112,7 +235996,7 @@ static void fts5MultiIterNew( if( iLevel<0 ){ assert( pStruct->nSegment==fts5StructureCountSegments(pStruct) ); nSeg = pStruct->nSegment; - nSeg += (p->pHash ? 1 : 0); + nSeg += (p->pHash && 0==(flags & FTS5INDEX_QUERY_SKIPHASH)); }else{ nSeg = MIN(pStruct->aLevel[iLevel].nSeg, nSegment); } @@ -234133,7 +236017,7 @@ static void fts5MultiIterNew( if( p->rc==SQLITE_OK ){ if( iLevel<0 ){ Fts5StructureLevel *pEnd = &pStruct->aLevel[pStruct->nLevel]; - if( p->pHash ){ + if( p->pHash && 0==(flags & FTS5INDEX_QUERY_SKIPHASH) ){ /* Add a segment iterator for the current contents of the hash table. */ Fts5SegIter *pIter = &pNew->aSeg[iIter++]; fts5SegIterHashInit(p, pTerm, nTerm, flags, pIter); @@ -234888,7 +236772,7 @@ static void fts5TrimSegments(Fts5Index *p, Fts5Iter *pIter){ fts5BufferAppendBlob(&p->rc, &buf, sizeof(aHdr), aHdr); fts5BufferAppendVarint(&p->rc, &buf, pSeg->term.n); fts5BufferAppendBlob(&p->rc, &buf, pSeg->term.n, pSeg->term.p); - fts5BufferAppendBlob(&p->rc, &buf, pData->szLeaf-iOff,&pData->p[iOff]); + fts5BufferAppendBlob(&p->rc, &buf,pData->szLeaf-iOff,&pData->p[iOff]); if( p->rc==SQLITE_OK ){ /* Set the szLeaf field */ fts5PutU16(&buf.p[2], (u16)buf.n); @@ -235166,16 +237050,16 @@ static void fts5IndexCrisismerge( ){ const int nCrisis = p->pConfig->nCrisisMerge; Fts5Structure *pStruct = *ppStruct; - int iLvl = 0; - - assert( p->rc!=SQLITE_OK || pStruct->nLevel>0 ); - while( p->rc==SQLITE_OK && pStruct->aLevel[iLvl].nSeg>=nCrisis ){ - fts5IndexMergeLevel(p, &pStruct, iLvl, 0); - assert( p->rc!=SQLITE_OK || pStruct->nLevel>(iLvl+1) ); - fts5StructurePromote(p, iLvl+1, pStruct); - iLvl++; + if( pStruct && pStruct->nLevel>0 ){ + int iLvl = 0; + while( p->rc==SQLITE_OK && pStruct->aLevel[iLvl].nSeg>=nCrisis ){ + fts5IndexMergeLevel(p, &pStruct, iLvl, 0); + assert( p->rc!=SQLITE_OK || pStruct->nLevel>(iLvl+1) ); + fts5StructurePromote(p, iLvl+1, pStruct); + iLvl++; + } + *ppStruct = pStruct; } - *ppStruct = pStruct; } static int fts5IndexReturn(Fts5Index *p){ @@ -235209,6 +237093,413 @@ static int fts5PoslistPrefix(const u8 *aBuf, int nMax){ return ret; } +/* +** Execute the SQL statement: +** +** DELETE FROM %_idx WHERE (segid, (pgno/2)) = ($iSegid, $iPgno); +** +** This is used when a secure-delete operation removes the last term +** from a segment leaf page. In that case the %_idx entry is removed +** too. This is done to ensure that if all instances of a token are +** removed from an fts5 database in secure-delete mode, no trace of +** the token itself remains in the database. +*/ +static void fts5SecureDeleteIdxEntry( + Fts5Index *p, /* FTS5 backend object */ + int iSegid, /* Id of segment to delete entry for */ + int iPgno /* Page number within segment */ +){ + if( iPgno!=1 ){ + assert( p->pConfig->iVersion==FTS5_CURRENT_VERSION_SECUREDELETE ); + if( p->pDeleteFromIdx==0 ){ + fts5IndexPrepareStmt(p, &p->pDeleteFromIdx, sqlite3_mprintf( + "DELETE FROM '%q'.'%q_idx' WHERE (segid, (pgno/2)) = (?1, ?2)", + p->pConfig->zDb, p->pConfig->zName + )); + } + if( p->rc==SQLITE_OK ){ + sqlite3_bind_int(p->pDeleteFromIdx, 1, iSegid); + sqlite3_bind_int(p->pDeleteFromIdx, 2, iPgno); + sqlite3_step(p->pDeleteFromIdx); + p->rc = sqlite3_reset(p->pDeleteFromIdx); + } + } +} + +/* +** This is called when a secure-delete operation removes a position-list +** that overflows onto segment page iPgno of segment pSeg. This function +** rewrites node iPgno, and possibly one or more of its right-hand peers, +** to remove this portion of the position list. +** +** Output variable (*pbLastInDoclist) is set to true if the position-list +** removed is followed by a new term or the end-of-segment, or false if +** it is followed by another rowid/position list. +*/ +static void fts5SecureDeleteOverflow( + Fts5Index *p, + Fts5StructureSegment *pSeg, + int iPgno, + int *pbLastInDoclist +){ + const int bDetailNone = (p->pConfig->eDetail==FTS5_DETAIL_NONE); + int pgno; + Fts5Data *pLeaf = 0; + assert( iPgno!=1 ); + + *pbLastInDoclist = 1; + for(pgno=iPgno; p->rc==SQLITE_OK && pgno<=pSeg->pgnoLast; pgno++){ + i64 iRowid = FTS5_SEGMENT_ROWID(pSeg->iSegid, pgno); + int iNext = 0; + u8 *aPg = 0; + + pLeaf = fts5DataRead(p, iRowid); + if( pLeaf==0 ) break; + aPg = pLeaf->p; + + iNext = fts5GetU16(&aPg[0]); + if( iNext!=0 ){ + *pbLastInDoclist = 0; + } + if( iNext==0 && pLeaf->szLeaf!=pLeaf->nn ){ + fts5GetVarint32(&aPg[pLeaf->szLeaf], iNext); + } + + if( iNext==0 ){ + /* The page contains no terms or rowids. Replace it with an empty + ** page and move on to the right-hand peer. */ + const u8 aEmpty[] = {0x00, 0x00, 0x00, 0x04}; + assert_nc( bDetailNone==0 || pLeaf->nn==4 ); + if( bDetailNone==0 ) fts5DataWrite(p, iRowid, aEmpty, sizeof(aEmpty)); + fts5DataRelease(pLeaf); + pLeaf = 0; + }else if( bDetailNone ){ + break; + }else if( iNext>=pLeaf->szLeaf || iNext<4 ){ + p->rc = FTS5_CORRUPT; + break; + }else{ + int nShift = iNext - 4; + int nPg; + + int nIdx = 0; + u8 *aIdx = 0; + + /* Unless the current page footer is 0 bytes in size (in which case + ** the new page footer will be as well), allocate and populate a + ** buffer containing the new page footer. Set stack variables aIdx + ** and nIdx accordingly. */ + if( pLeaf->nn>pLeaf->szLeaf ){ + int iFirst = 0; + int i1 = pLeaf->szLeaf; + int i2 = 0; + + aIdx = sqlite3Fts5MallocZero(&p->rc, (pLeaf->nn-pLeaf->szLeaf)+2); + if( aIdx==0 ) break; + i1 += fts5GetVarint32(&aPg[i1], iFirst); + i2 = sqlite3Fts5PutVarint(aIdx, iFirst-nShift); + if( i1nn ){ + memcpy(&aIdx[i2], &aPg[i1], pLeaf->nn-i1); + i2 += (pLeaf->nn-i1); + } + nIdx = i2; + } + + /* Modify the contents of buffer aPg[]. Set nPg to the new size + ** in bytes. The new page is always smaller than the old. */ + nPg = pLeaf->szLeaf - nShift; + memmove(&aPg[4], &aPg[4+nShift], nPg-4); + fts5PutU16(&aPg[2], nPg); + if( fts5GetU16(&aPg[0]) ) fts5PutU16(&aPg[0], 4); + if( nIdx>0 ){ + memcpy(&aPg[nPg], aIdx, nIdx); + nPg += nIdx; + } + sqlite3_free(aIdx); + + /* Write the new page to disk and exit the loop */ + assert( nPg>4 || fts5GetU16(aPg)==0 ); + fts5DataWrite(p, iRowid, aPg, nPg); + break; + } + } + fts5DataRelease(pLeaf); +} + +/* +** Completely remove the entry that pSeg currently points to from +** the database. +*/ +static void fts5DoSecureDelete( + Fts5Index *p, + Fts5SegIter *pSeg +){ + const int bDetailNone = (p->pConfig->eDetail==FTS5_DETAIL_NONE); + int iSegid = pSeg->pSeg->iSegid; + u8 *aPg = pSeg->pLeaf->p; + int nPg = pSeg->pLeaf->nn; + int iPgIdx = pSeg->pLeaf->szLeaf; + + u64 iDelta = 0; + u64 iNextDelta = 0; + int iNextOff = 0; + int iOff = 0; + int nIdx = 0; + u8 *aIdx = 0; + int bLastInDoclist = 0; + int iIdx = 0; + int iStart = 0; + int iKeyOff = 0; + int iPrevKeyOff = 0; + int iDelKeyOff = 0; /* Offset of deleted key, if any */ + + nIdx = nPg-iPgIdx; + aIdx = sqlite3Fts5MallocZero(&p->rc, nIdx+16); + if( p->rc ) return; + memcpy(aIdx, &aPg[iPgIdx], nIdx); + + /* At this point segment iterator pSeg points to the entry + ** this function should remove from the b-tree segment. + ** + ** In detail=full or detail=column mode, pSeg->iLeafOffset is the + ** offset of the first byte in the position-list for the entry to + ** remove. Immediately before this comes two varints that will also + ** need to be removed: + ** + ** + the rowid or delta rowid value for the entry, and + ** + the size of the position list in bytes. + ** + ** Or, in detail=none mode, there is a single varint prior to + ** pSeg->iLeafOffset - the rowid or delta rowid value. + ** + ** This block sets the following variables: + ** + ** iStart: + ** iDelta: + */ + { + int iSOP; + if( pSeg->iLeafPgno==pSeg->iTermLeafPgno ){ + iStart = pSeg->iTermLeafOffset; + }else{ + iStart = fts5GetU16(&aPg[0]); + } + + iSOP = iStart + fts5GetVarint(&aPg[iStart], &iDelta); + assert_nc( iSOP<=pSeg->iLeafOffset ); + + if( bDetailNone ){ + while( iSOPiLeafOffset ){ + if( aPg[iSOP]==0x00 ) iSOP++; + if( aPg[iSOP]==0x00 ) iSOP++; + iStart = iSOP; + iSOP = iStart + fts5GetVarint(&aPg[iStart], &iDelta); + } + + iNextOff = iSOP; + if( iNextOffiEndofDoclist && aPg[iNextOff]==0x00 ) iNextOff++; + if( iNextOffiEndofDoclist && aPg[iNextOff]==0x00 ) iNextOff++; + + }else{ + int nPos = 0; + iSOP += fts5GetVarint32(&aPg[iSOP], nPos); + while( iSOPiLeafOffset ){ + iStart = iSOP + (nPos/2); + iSOP = iStart + fts5GetVarint(&aPg[iStart], &iDelta); + iSOP += fts5GetVarint32(&aPg[iSOP], nPos); + } + assert_nc( iSOP==pSeg->iLeafOffset ); + iNextOff = pSeg->iLeafOffset + pSeg->nPos; + } + } + + iOff = iStart; + if( iNextOff>=iPgIdx ){ + int pgno = pSeg->iLeafPgno+1; + fts5SecureDeleteOverflow(p, pSeg->pSeg, pgno, &bLastInDoclist); + iNextOff = iPgIdx; + }else{ + /* Set bLastInDoclist to true if the entry being removed is the last + ** in its doclist. */ + for(iIdx=0, iKeyOff=0; iIdxiTermLeafOffset && pSeg->iLeafPgno==pSeg->iTermLeafPgno + ){ + /* The entry being removed was the only position list in its + ** doclist. Therefore the term needs to be removed as well. */ + int iKey = 0; + for(iIdx=0, iKeyOff=0; iIdx(u32)iStart ) break; + iKeyOff += iVal; + } + + iDelKeyOff = iOff = iKeyOff; + if( iNextOff!=iPgIdx ){ + int nPrefix = 0; + int nSuffix = 0; + int nPrefix2 = 0; + int nSuffix2 = 0; + + iDelKeyOff = iNextOff; + iNextOff += fts5GetVarint32(&aPg[iNextOff], nPrefix2); + iNextOff += fts5GetVarint32(&aPg[iNextOff], nSuffix2); + + if( iKey!=1 ){ + iKeyOff += fts5GetVarint32(&aPg[iKeyOff], nPrefix); + } + iKeyOff += fts5GetVarint32(&aPg[iKeyOff], nSuffix); + + nPrefix = MIN(nPrefix, nPrefix2); + nSuffix = (nPrefix2 + nSuffix2) - nPrefix; + + if( (iKeyOff+nSuffix)>iPgIdx || (iNextOff+nSuffix2)>iPgIdx ){ + p->rc = FTS5_CORRUPT; + }else{ + if( iKey!=1 ){ + iOff += sqlite3Fts5PutVarint(&aPg[iOff], nPrefix); + } + iOff += sqlite3Fts5PutVarint(&aPg[iOff], nSuffix); + if( nPrefix2>nPrefix ){ + memcpy(&aPg[iOff], &pSeg->term.p[nPrefix], nPrefix2-nPrefix); + iOff += (nPrefix2-nPrefix); + } + memmove(&aPg[iOff], &aPg[iNextOff], nSuffix2); + iOff += nSuffix2; + iNextOff += nSuffix2; + } + } + }else if( iStart==4 ){ + int iPgno; + + assert_nc( pSeg->iLeafPgno>pSeg->iTermLeafPgno ); + /* The entry being removed may be the only position list in + ** its doclist. */ + for(iPgno=pSeg->iLeafPgno-1; iPgno>pSeg->iTermLeafPgno; iPgno-- ){ + Fts5Data *pPg = fts5DataRead(p, FTS5_SEGMENT_ROWID(iSegid, iPgno)); + int bEmpty = (pPg && pPg->nn==4); + fts5DataRelease(pPg); + if( bEmpty==0 ) break; + } + + if( iPgno==pSeg->iTermLeafPgno ){ + i64 iId = FTS5_SEGMENT_ROWID(iSegid, pSeg->iTermLeafPgno); + Fts5Data *pTerm = fts5DataRead(p, iId); + if( pTerm && pTerm->szLeaf==pSeg->iTermLeafOffset ){ + u8 *aTermIdx = &pTerm->p[pTerm->szLeaf]; + int nTermIdx = pTerm->nn - pTerm->szLeaf; + int iTermIdx = 0; + int iTermOff = 0; + + while( 1 ){ + u32 iVal = 0; + int nByte = fts5GetVarint32(&aTermIdx[iTermIdx], iVal); + iTermOff += iVal; + if( (iTermIdx+nByte)>=nTermIdx ) break; + iTermIdx += nByte; + } + nTermIdx = iTermIdx; + + memmove(&pTerm->p[iTermOff], &pTerm->p[pTerm->szLeaf], nTermIdx); + fts5PutU16(&pTerm->p[2], iTermOff); + + fts5DataWrite(p, iId, pTerm->p, iTermOff+nTermIdx); + if( nTermIdx==0 ){ + fts5SecureDeleteIdxEntry(p, iSegid, pSeg->iTermLeafPgno); + } + } + fts5DataRelease(pTerm); + } + } + + if( p->rc==SQLITE_OK ){ + const int nMove = nPg - iNextOff; + int nShift = 0; + + memmove(&aPg[iOff], &aPg[iNextOff], nMove); + iPgIdx -= (iNextOff - iOff); + nPg = iPgIdx; + fts5PutU16(&aPg[2], iPgIdx); + + nShift = iNextOff - iOff; + for(iIdx=0, iKeyOff=0, iPrevKeyOff=0; iIdxiOff ){ + iKeyOff -= nShift; + nShift = 0; + } + nPg += sqlite3Fts5PutVarint(&aPg[nPg], iKeyOff - iPrevKeyOff); + iPrevKeyOff = iKeyOff; + } + } + + if( iPgIdx==nPg && nIdx>0 && pSeg->iLeafPgno!=1 ){ + fts5SecureDeleteIdxEntry(p, iSegid, pSeg->iLeafPgno); + } + + assert_nc( nPg>4 || fts5GetU16(aPg)==0 ); + fts5DataWrite(p, FTS5_SEGMENT_ROWID(iSegid,pSeg->iLeafPgno), aPg,nPg); + } + sqlite3_free(aIdx); +} + +/* +** This is called as part of flushing a delete to disk in 'secure-delete' +** mode. It edits the segments within the database described by argument +** pStruct to remove the entries for term zTerm, rowid iRowid. +*/ +static void fts5FlushSecureDelete( + Fts5Index *p, + Fts5Structure *pStruct, + const char *zTerm, + i64 iRowid +){ + const int f = FTS5INDEX_QUERY_SKIPHASH; + int nTerm = (int)strlen(zTerm); + Fts5Iter *pIter = 0; /* Used to find term instance */ + + fts5MultiIterNew(p, pStruct, f, 0, (const u8*)zTerm, nTerm, -1, 0, &pIter); + if( fts5MultiIterEof(p, pIter)==0 ){ + i64 iThis = fts5MultiIterRowid(pIter); + if( iThisrc==SQLITE_OK + && fts5MultiIterEof(p, pIter)==0 + && iRowid==fts5MultiIterRowid(pIter) + ){ + Fts5SegIter *pSeg = &pIter->aSeg[pIter->aFirst[1].iFirst]; + fts5DoSecureDelete(p, pSeg); + } + } + + fts5MultiIterFree(pIter); +} + + /* ** Flush the contents of in-memory hash table iHash to a new level-0 ** segment on disk. Also update the corresponding structure record. @@ -235231,6 +237522,7 @@ static void fts5FlushOneHash(Fts5Index *p){ if( iSegid ){ const int pgsz = p->pConfig->pgsz; int eDetail = p->pConfig->eDetail; + int bSecureDelete = p->pConfig->bSecureDelete; Fts5StructureSegment *pSeg; /* New segment within pStruct */ Fts5Buffer *pBuf; /* Buffer in which to assemble leaf page */ Fts5Buffer *pPgidx; /* Buffer in which to assemble pgidx */ @@ -235253,40 +237545,77 @@ static void fts5FlushOneHash(Fts5Index *p){ } while( p->rc==SQLITE_OK && 0==sqlite3Fts5HashScanEof(pHash) ){ const char *zTerm; /* Buffer containing term */ + int nTerm; /* Size of zTerm in bytes */ const u8 *pDoclist; /* Pointer to doclist for this term */ int nDoclist; /* Size of doclist in bytes */ - /* Write the term for this entry to disk. */ + /* Get the term and doclist for this entry. */ sqlite3Fts5HashScanEntry(pHash, &zTerm, &pDoclist, &nDoclist); - fts5WriteAppendTerm(p, &writer, (int)strlen(zTerm), (const u8*)zTerm); - if( p->rc!=SQLITE_OK ) break; + nTerm = (int)strlen(zTerm); + if( bSecureDelete==0 ){ + fts5WriteAppendTerm(p, &writer, nTerm, (const u8*)zTerm); + if( p->rc!=SQLITE_OK ) break; + assert( writer.bFirstRowidInPage==0 ); + } - assert( writer.bFirstRowidInPage==0 ); - if( pgsz>=(pBuf->n + pPgidx->n + nDoclist + 1) ){ + if( !bSecureDelete && pgsz>=(pBuf->n + pPgidx->n + nDoclist + 1) ){ /* The entire doclist will fit on the current leaf. */ fts5BufferSafeAppendBlob(pBuf, pDoclist, nDoclist); }else{ + int bTermWritten = !bSecureDelete; i64 iRowid = 0; - u64 iDelta = 0; + i64 iPrev = 0; int iOff = 0; /* The entire doclist will not fit on this leaf. The following ** loop iterates through the poslists that make up the current ** doclist. */ while( p->rc==SQLITE_OK && iOffrc!=SQLITE_OK || pDoclist[iOff]==0x01 ){ + iOff++; + continue; + } + } + } + + if( p->rc==SQLITE_OK && bTermWritten==0 ){ + fts5WriteAppendTerm(p, &writer, nTerm, (const u8*)zTerm); + bTermWritten = 1; + assert( p->rc!=SQLITE_OK || writer.bFirstRowidInPage==0 ); + } + if( writer.bFirstRowidInPage ){ fts5PutU16(&pBuf->p[0], (u16)pBuf->n); /* first rowid on page */ pBuf->n += sqlite3Fts5PutVarint(&pBuf->p[pBuf->n], iRowid); writer.bFirstRowidInPage = 0; fts5WriteDlidxAppend(p, &writer, iRowid); - if( p->rc!=SQLITE_OK ) break; }else{ - pBuf->n += sqlite3Fts5PutVarint(&pBuf->p[pBuf->n], iDelta); + pBuf->n += sqlite3Fts5PutVarint(&pBuf->p[pBuf->n], iRowid-iPrev); } + if( p->rc!=SQLITE_OK ) break; assert( pBuf->n<=pBuf->nSpace ); + iPrev = iRowid; if( eDetail==FTS5_DETAIL_NONE ){ if( iOffnLevel==0 ){ - fts5StructureAddLevel(&p->rc, &pStruct); + assert( p->rc!=SQLITE_OK || bSecureDelete || pgnoLast>0 ); + if( pgnoLast>0 ){ + /* Update the Fts5Structure. It is written back to the database by the + ** fts5StructureRelease() call below. */ + if( pStruct->nLevel==0 ){ + fts5StructureAddLevel(&p->rc, &pStruct); + } + fts5StructureExtendLevel(&p->rc, pStruct, 0, 1, 0); + if( p->rc==SQLITE_OK ){ + pSeg = &pStruct->aLevel[0].aSeg[ pStruct->aLevel[0].nSeg++ ]; + pSeg->iSegid = iSegid; + pSeg->pgnoFirst = 1; + pSeg->pgnoLast = pgnoLast; + pStruct->nSegment++; + } + fts5StructurePromote(p, 0, pStruct); } - fts5StructureExtendLevel(&p->rc, pStruct, 0, 1, 0); - if( p->rc==SQLITE_OK ){ - pSeg = &pStruct->aLevel[0].aSeg[ pStruct->aLevel[0].nSeg++ ]; - pSeg->iSegid = iSegid; - pSeg->pgnoFirst = 1; - pSeg->pgnoLast = pgnoLast; - pStruct->nSegment++; - } - fts5StructurePromote(p, 0, pStruct); } fts5IndexAutomerge(p, &pStruct, pgnoLast); @@ -236099,6 +238431,7 @@ static int sqlite3Fts5IndexClose(Fts5Index *p){ sqlite3_finalize(p->pIdxDeleter); sqlite3_finalize(p->pIdxSelect); sqlite3_finalize(p->pDataVersion); + sqlite3_finalize(p->pDeleteFromIdx); sqlite3Fts5HashFree(p->pHash); sqlite3_free(p->zDataTbl); sqlite3_free(p); @@ -236729,6 +239062,7 @@ static void fts5IndexIntegrityCheckSegment( Fts5StructureSegment *pSeg /* Segment to check internal consistency */ ){ Fts5Config *pConfig = p->pConfig; + int bSecureDelete = (pConfig->iVersion==FTS5_CURRENT_VERSION_SECUREDELETE); sqlite3_stmt *pStmt = 0; int rc2; int iIdxPrevLeaf = pSeg->pgnoFirst-1; @@ -236764,7 +239098,19 @@ static void fts5IndexIntegrityCheckSegment( ** is also a rowid pointer within the leaf page header, it points to a ** location before the term. */ if( pLeaf->nn<=pLeaf->szLeaf ){ - p->rc = FTS5_CORRUPT; + + if( nIdxTerm==0 + && pConfig->iVersion==FTS5_CURRENT_VERSION_SECUREDELETE + && pLeaf->nn==pLeaf->szLeaf + && pLeaf->nn==4 + ){ + /* special case - the very first page in a segment keeps its %_idx + ** entry even if all the terms are removed from it by secure-delete + ** operations. */ + }else{ + p->rc = FTS5_CORRUPT; + } + }else{ int iOff; /* Offset of first term on leaf */ int iRowidOff; /* Offset of first rowid on leaf */ @@ -236828,9 +239174,12 @@ static void fts5IndexIntegrityCheckSegment( ASSERT_SZLEAF_OK(pLeaf); if( iRowidOff>=pLeaf->szLeaf ){ p->rc = FTS5_CORRUPT; - }else{ + }else if( bSecureDelete==0 || iRowidOff>0 ){ + i64 iDlRowid = fts5DlidxIterRowid(pDlidx); fts5GetVarint(&pLeaf->p[iRowidOff], (u64*)&iRowid); - if( iRowid!=fts5DlidxIterRowid(pDlidx) ) p->rc = FTS5_CORRUPT; + if( iRowidrc = FTS5_CORRUPT; + } } fts5DataRelease(pLeaf); } @@ -239092,6 +241441,8 @@ static int fts5UpdateMethod( Fts5Config *pConfig = pTab->p.pConfig; int eType0; /* value_type() of apVal[0] */ int rc = SQLITE_OK; /* Return code */ + int bUpdateOrDelete = 0; + /* A transaction must be open when this is called. */ assert( pTab->ts.eState==1 || pTab->ts.eState==2 ); @@ -239102,6 +241453,11 @@ static int fts5UpdateMethod( || sqlite3_value_type(apVal[0])==SQLITE_NULL ); assert( pTab->p.pConfig->pzErrmsg==0 ); + if( pConfig->pgsz==0 ){ + rc = sqlite3Fts5IndexLoadConfig(pTab->p.pIndex); + if( rc!=SQLITE_OK ) return rc; + } + pTab->p.pConfig->pzErrmsg = &pTab->p.base.zErrMsg; /* Put any active cursors into REQUIRE_SEEK state. */ @@ -239154,6 +241510,7 @@ static int fts5UpdateMethod( else if( nArg==1 ){ i64 iDel = sqlite3_value_int64(apVal[0]); /* Rowid to delete */ rc = sqlite3Fts5StorageDelete(pTab->pStorage, iDel, 0); + bUpdateOrDelete = 1; } /* INSERT or UPDATE */ @@ -239169,6 +241526,7 @@ static int fts5UpdateMethod( if( eConflict==SQLITE_REPLACE && eType1==SQLITE_INTEGER ){ i64 iNew = sqlite3_value_int64(apVal[1]); /* Rowid to delete */ rc = sqlite3Fts5StorageDelete(pTab->pStorage, iNew, 0); + bUpdateOrDelete = 1; } fts5StorageInsert(&rc, pTab, apVal, pRowid); } @@ -239197,10 +241555,24 @@ static int fts5UpdateMethod( rc = sqlite3Fts5StorageDelete(pTab->pStorage, iOld, 0); fts5StorageInsert(&rc, pTab, apVal, pRowid); } + bUpdateOrDelete = 1; } } } + if( rc==SQLITE_OK + && bUpdateOrDelete + && pConfig->bSecureDelete + && pConfig->iVersion==FTS5_CURRENT_VERSION + ){ + rc = sqlite3Fts5StorageConfigValue( + pTab->pStorage, "version", 0, FTS5_CURRENT_VERSION_SECUREDELETE + ); + if( rc==SQLITE_OK ){ + pConfig->iVersion = FTS5_CURRENT_VERSION_SECUREDELETE; + } + } + pTab->p.pConfig->pzErrmsg = 0; return rc; } @@ -240060,6 +242432,7 @@ static int fts5RollbackToMethod(sqlite3_vtab *pVtab, int iSavepoint){ UNUSED_PARAM(iSavepoint); /* Call below is a no-op for NDEBUG builds */ fts5CheckTransactionState(pTab, FTS5_ROLLBACKTO, iSavepoint); fts5TripCursors(pTab); + pTab->p.pConfig->pgsz = 0; return sqlite3Fts5StorageRollback(pTab->pStorage); } @@ -240262,7 +242635,7 @@ static void fts5SourceIdFunc( ){ assert( nArg==0 ); UNUSED_PARAM2(nArg, apUnused); - sqlite3_result_text(pCtx, "fts5: 2023-03-22 11:56:21 0d1fc92f94cb6b76bffe3ec34d69cffde2924203304e8ffc4155597af0c191da", -1, SQLITE_TRANSIENT); + sqlite3_result_text(pCtx, "fts5: 2023-05-16 12:36:15 831d0fb2836b71c9bc51067c49fee4b8f18047814f2ff22d817d25195cf350b0", -1, SQLITE_TRANSIENT); } /* diff --git a/src/libs/3rdparty/sqlite/sqlite3.h b/src/libs/3rdparty/sqlite/sqlite3.h index 7e43e1f1b4d..48effe20216 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.41.2" -#define SQLITE_VERSION_NUMBER 3041002 -#define SQLITE_SOURCE_ID "2023-03-22 11:56:21 0d1fc92f94cb6b76bffe3ec34d69cffde2924203304e8ffc4155597af0c191da" +#define SQLITE_VERSION "3.42.0" +#define SQLITE_VERSION_NUMBER 3042000 +#define SQLITE_SOURCE_ID "2023-05-16 12:36:15 831d0fb2836b71c9bc51067c49fee4b8f18047814f2ff22d817d25195cf350b0" /* ** CAPI3REF: Run-Time Library Version Numbers @@ -1655,20 +1655,23 @@ SQLITE_API int sqlite3_os_end(void); ** must ensure that no other SQLite interfaces are invoked by other ** threads while sqlite3_config() is running.
** -** The sqlite3_config() interface -** may only be invoked prior to library initialization using -** [sqlite3_initialize()] or after shutdown by [sqlite3_shutdown()]. -** ^If sqlite3_config() is called after [sqlite3_initialize()] and before -** [sqlite3_shutdown()] then it will return SQLITE_MISUSE. -** Note, however, that ^sqlite3_config() can be called as part of the -** implementation of an application-defined [sqlite3_os_init()]. -** ** The first argument to sqlite3_config() is an integer ** [configuration option] that determines ** what property of SQLite is to be configured. Subsequent arguments ** vary depending on the [configuration option] ** in the first argument. ** +** For most configuration options, the sqlite3_config() interface +** may only be invoked prior to library initialization using +** [sqlite3_initialize()] or after shutdown by [sqlite3_shutdown()]. +** The exceptional configuration options that may be invoked at any time +** are called "anytime configuration options". +** ^If sqlite3_config() is called after [sqlite3_initialize()] and before +** [sqlite3_shutdown()] with a first argument that is not an anytime +** configuration option, then the sqlite3_config() call will return SQLITE_MISUSE. +** Note, however, that ^sqlite3_config() can be called as part of the +** implementation of an application-defined [sqlite3_os_init()]. +** ** ^When a configuration option is set, sqlite3_config() returns [SQLITE_OK]. ** ^If the option is unknown or SQLite is unable to set the option ** then this routine returns a non-zero [error code]. @@ -1776,6 +1779,23 @@ struct sqlite3_mem_methods { ** These constants are the available integer configuration options that ** can be passed as the first argument to the [sqlite3_config()] interface. ** +** Most of the configuration options for sqlite3_config() +** will only work if invoked prior to [sqlite3_initialize()] or after +** [sqlite3_shutdown()]. The few exceptions to this rule are called +** "anytime configuration options". +** ^Calling [sqlite3_config()] with a first argument that is not an +** anytime configuration option in between calls to [sqlite3_initialize()] and +** [sqlite3_shutdown()] is a no-op that returns SQLITE_MISUSE. +** +** The set of anytime configuration options can change (by insertions +** and/or deletions) from one release of SQLite to the next. +** As of SQLite version 3.42.0, the complete set of anytime configuration +** options is: +**
    +**
  • SQLITE_CONFIG_LOG +**
  • SQLITE_CONFIG_PCACHE_HDRSZ +**
+** ** New configuration options may be added in future releases of SQLite. ** Existing configuration options might be discontinued. Applications ** should check the return code from [sqlite3_config()] to make sure that @@ -2122,28 +2142,28 @@ struct sqlite3_mem_methods { ** compile-time option is not set, then the default maximum is 1073741824. ** */ -#define SQLITE_CONFIG_SINGLETHREAD 1 /* nil */ -#define SQLITE_CONFIG_MULTITHREAD 2 /* nil */ -#define SQLITE_CONFIG_SERIALIZED 3 /* nil */ -#define SQLITE_CONFIG_MALLOC 4 /* sqlite3_mem_methods* */ -#define SQLITE_CONFIG_GETMALLOC 5 /* sqlite3_mem_methods* */ -#define SQLITE_CONFIG_SCRATCH 6 /* No longer used */ -#define SQLITE_CONFIG_PAGECACHE 7 /* void*, int sz, int N */ -#define SQLITE_CONFIG_HEAP 8 /* void*, int nByte, int min */ -#define SQLITE_CONFIG_MEMSTATUS 9 /* boolean */ -#define SQLITE_CONFIG_MUTEX 10 /* sqlite3_mutex_methods* */ -#define SQLITE_CONFIG_GETMUTEX 11 /* sqlite3_mutex_methods* */ -/* previously SQLITE_CONFIG_CHUNKALLOC 12 which is now unused. */ -#define SQLITE_CONFIG_LOOKASIDE 13 /* int int */ -#define SQLITE_CONFIG_PCACHE 14 /* no-op */ -#define SQLITE_CONFIG_GETPCACHE 15 /* no-op */ -#define SQLITE_CONFIG_LOG 16 /* xFunc, void* */ -#define SQLITE_CONFIG_URI 17 /* int */ -#define SQLITE_CONFIG_PCACHE2 18 /* sqlite3_pcache_methods2* */ -#define SQLITE_CONFIG_GETPCACHE2 19 /* sqlite3_pcache_methods2* */ +#define SQLITE_CONFIG_SINGLETHREAD 1 /* nil */ +#define SQLITE_CONFIG_MULTITHREAD 2 /* nil */ +#define SQLITE_CONFIG_SERIALIZED 3 /* nil */ +#define SQLITE_CONFIG_MALLOC 4 /* sqlite3_mem_methods* */ +#define SQLITE_CONFIG_GETMALLOC 5 /* sqlite3_mem_methods* */ +#define SQLITE_CONFIG_SCRATCH 6 /* No longer used */ +#define SQLITE_CONFIG_PAGECACHE 7 /* void*, int sz, int N */ +#define SQLITE_CONFIG_HEAP 8 /* void*, int nByte, int min */ +#define SQLITE_CONFIG_MEMSTATUS 9 /* boolean */ +#define SQLITE_CONFIG_MUTEX 10 /* sqlite3_mutex_methods* */ +#define SQLITE_CONFIG_GETMUTEX 11 /* sqlite3_mutex_methods* */ +/* previously SQLITE_CONFIG_CHUNKALLOC 12 which is now unused. */ +#define SQLITE_CONFIG_LOOKASIDE 13 /* int int */ +#define SQLITE_CONFIG_PCACHE 14 /* no-op */ +#define SQLITE_CONFIG_GETPCACHE 15 /* no-op */ +#define SQLITE_CONFIG_LOG 16 /* xFunc, void* */ +#define SQLITE_CONFIG_URI 17 /* int */ +#define SQLITE_CONFIG_PCACHE2 18 /* sqlite3_pcache_methods2* */ +#define SQLITE_CONFIG_GETPCACHE2 19 /* sqlite3_pcache_methods2* */ #define SQLITE_CONFIG_COVERING_INDEX_SCAN 20 /* int */ -#define SQLITE_CONFIG_SQLLOG 21 /* xSqllog, void* */ -#define SQLITE_CONFIG_MMAP_SIZE 22 /* sqlite3_int64, sqlite3_int64 */ +#define SQLITE_CONFIG_SQLLOG 21 /* xSqllog, void* */ +#define SQLITE_CONFIG_MMAP_SIZE 22 /* sqlite3_int64, sqlite3_int64 */ #define SQLITE_CONFIG_WIN32_HEAPSIZE 23 /* int nByte */ #define SQLITE_CONFIG_PCACHE_HDRSZ 24 /* int *psz */ #define SQLITE_CONFIG_PMASZ 25 /* unsigned int szPma */ @@ -2378,7 +2398,7 @@ struct sqlite3_mem_methods { **
** ** [[SQLITE_DBCONFIG_DQS_DML]] -**
SQLITE_DBCONFIG_DQS_DML +**
SQLITE_DBCONFIG_DQS_DML
**
The SQLITE_DBCONFIG_DQS_DML option activates or deactivates ** the legacy [double-quoted string literal] misfeature for DML statements ** only, that is DELETE, INSERT, SELECT, and UPDATE statements. The @@ -2387,7 +2407,7 @@ struct sqlite3_mem_methods { **
** ** [[SQLITE_DBCONFIG_DQS_DDL]] -**
SQLITE_DBCONFIG_DQS_DDL +**
SQLITE_DBCONFIG_DQS_DDL
**
The SQLITE_DBCONFIG_DQS option activates or deactivates ** the legacy [double-quoted string literal] misfeature for DDL statements, ** such as CREATE TABLE and CREATE INDEX. The @@ -2396,7 +2416,7 @@ struct sqlite3_mem_methods { **
** ** [[SQLITE_DBCONFIG_TRUSTED_SCHEMA]] -**
SQLITE_DBCONFIG_TRUSTED_SCHEMA +**
SQLITE_DBCONFIG_TRUSTED_SCHEMA
**
The SQLITE_DBCONFIG_TRUSTED_SCHEMA option tells SQLite to ** assume that database schemas are untainted by malicious content. ** When the SQLITE_DBCONFIG_TRUSTED_SCHEMA option is disabled, SQLite @@ -2416,7 +2436,7 @@ struct sqlite3_mem_methods { **
** ** [[SQLITE_DBCONFIG_LEGACY_FILE_FORMAT]] -**
SQLITE_DBCONFIG_LEGACY_FILE_FORMAT +**
SQLITE_DBCONFIG_LEGACY_FILE_FORMAT
**
The SQLITE_DBCONFIG_LEGACY_FILE_FORMAT option activates or deactivates ** the legacy file format flag. When activated, this flag causes all newly ** created database file to have a schema format version number (the 4-byte @@ -2425,7 +2445,7 @@ struct sqlite3_mem_methods { ** any SQLite version back to 3.0.0 ([dateof:3.0.0]). Without this setting, ** newly created databases are generally not understandable by SQLite versions ** prior to 3.3.0 ([dateof:3.3.0]). As these words are written, there -** is now scarcely any need to generated database files that are compatible +** is now scarcely any need to generate database files that are compatible ** all the way back to version 3.0.0, and so this setting is of little ** practical use, but is provided so that SQLite can continue to claim the ** ability to generate new database files that are compatible with version @@ -2436,6 +2456,38 @@ struct sqlite3_mem_methods { ** not considered a bug since SQLite versions 3.3.0 and earlier do not support ** either generated columns or decending indexes. **
+** +** [[SQLITE_DBCONFIG_STMT_SCANSTATUS]] +**
SQLITE_DBCONFIG_STMT_SCANSTATUS
+**
The SQLITE_DBCONFIG_STMT_SCANSTATUS option is only useful in +** SQLITE_ENABLE_STMT_SCANSTATUS builds. In this case, it sets or clears +** a flag that enables collection of the sqlite3_stmt_scanstatus_v2() +** statistics. For statistics to be collected, the flag must be set on +** the database handle both when the SQL statement is prepared and when it +** is stepped. The flag is set (collection of statistics is enabled) +** by default. This option takes two arguments: an integer and a pointer to +** an integer.. The first argument is 1, 0, or -1 to enable, disable, or +** leave unchanged the statement scanstatus option. If the second argument +** is not NULL, then the value of the statement scanstatus setting after +** processing the first argument is written into the integer that the second +** argument points to. +**
+** +** [[SQLITE_DBCONFIG_REVERSE_SCANORDER]] +**
SQLITE_DBCONFIG_REVERSE_SCANORDER
+**
The SQLITE_DBCONFIG_REVERSE_SCANORDER option changes the default order +** in which tables and indexes are scanned so that the scans start at the end +** and work toward the beginning rather than starting at the beginning and +** working toward the end. Setting SQLITE_DBCONFIG_REVERSE_SCANORDER is the +** same as setting [PRAGMA reverse_unordered_selects]. This option takes +** two arguments which are an integer and a pointer to an integer. The first +** argument is 1, 0, or -1 to enable, disable, or leave unchanged the +** reverse scan order flag, respectively. If the second argument is not NULL, +** then 0 or 1 is written into the integer that the second argument points to +** depending on if the reverse scan order flag is set after processing the +** first argument. +**
+** ** */ #define SQLITE_DBCONFIG_MAINDBNAME 1000 /* const char* */ @@ -2456,7 +2508,9 @@ struct sqlite3_mem_methods { #define SQLITE_DBCONFIG_ENABLE_VIEW 1015 /* int int* */ #define SQLITE_DBCONFIG_LEGACY_FILE_FORMAT 1016 /* int int* */ #define SQLITE_DBCONFIG_TRUSTED_SCHEMA 1017 /* int int* */ -#define SQLITE_DBCONFIG_MAX 1017 /* Largest DBCONFIG */ +#define SQLITE_DBCONFIG_STMT_SCANSTATUS 1018 /* int int* */ +#define SQLITE_DBCONFIG_REVERSE_SCANORDER 1019 /* int int* */ +#define SQLITE_DBCONFIG_MAX 1019 /* Largest DBCONFIG */ /* ** CAPI3REF: Enable Or Disable Extended Result Codes @@ -6201,6 +6255,13 @@ SQLITE_API void sqlite3_activate_cerod( ** of the default VFS is not implemented correctly, or not implemented at ** all, then the behavior of sqlite3_sleep() may deviate from the description ** in the previous paragraphs. +** +** If a negative argument is passed to sqlite3_sleep() the results vary by +** VFS and operating system. Some system treat a negative argument as an +** instruction to sleep forever. Others understand it to mean do not sleep +** at all. ^In SQLite version 3.42.0 and later, a negative +** argument passed into sqlite3_sleep() is changed to zero before it is relayed +** down into the xSleep method of the VFS. */ SQLITE_API int sqlite3_sleep(int); @@ -7828,9 +7889,9 @@ SQLITE_API int sqlite3_vfs_unregister(sqlite3_vfs*); ** is undefined if the mutex is not currently entered by the ** calling thread or is not currently allocated. ** -** ^If the argument to sqlite3_mutex_enter(), sqlite3_mutex_try(), or -** sqlite3_mutex_leave() is a NULL pointer, then all three routines -** behave as no-ops. +** ^If the argument to sqlite3_mutex_enter(), sqlite3_mutex_try(), +** sqlite3_mutex_leave(), or sqlite3_mutex_free() is a NULL pointer, +** then any of the four routines behaves as a no-op. ** ** See also: [sqlite3_mutex_held()] and [sqlite3_mutex_notheld()]. */ @@ -9564,18 +9625,28 @@ SQLITE_API int sqlite3_vtab_config(sqlite3*, int op, ...); ** [[SQLITE_VTAB_INNOCUOUS]]
SQLITE_VTAB_INNOCUOUS
**
Calls of the form ** [sqlite3_vtab_config](db,SQLITE_VTAB_INNOCUOUS) from within the -** the [xConnect] or [xCreate] methods of a [virtual table] implmentation +** the [xConnect] or [xCreate] methods of a [virtual table] implementation ** identify that virtual table as being safe to use from within triggers ** and views. Conceptually, the SQLITE_VTAB_INNOCUOUS tag means that the ** virtual table can do no serious harm even if it is controlled by a ** malicious hacker. Developers should avoid setting the SQLITE_VTAB_INNOCUOUS ** flag unless absolutely necessary. **
+** +** [[SQLITE_VTAB_USES_ALL_SCHEMAS]]
SQLITE_VTAB_USES_ALL_SCHEMAS
+**
Calls of the form +** [sqlite3_vtab_config](db,SQLITE_VTAB_USES_ALL_SCHEMA) from within the +** the [xConnect] or [xCreate] methods of a [virtual table] implementation +** instruct the query planner to begin at least a read transaction on +** all schemas ("main", "temp", and any ATTACH-ed databases) whenever the +** virtual table is used. +**
** */ #define SQLITE_VTAB_CONSTRAINT_SUPPORT 1 #define SQLITE_VTAB_INNOCUOUS 2 #define SQLITE_VTAB_DIRECTONLY 3 +#define SQLITE_VTAB_USES_ALL_SCHEMAS 4 /* ** CAPI3REF: Determine The Virtual Table Conflict Policy @@ -10750,16 +10821,20 @@ SQLITE_API int sqlite3session_create( SQLITE_API void sqlite3session_delete(sqlite3_session *pSession); /* -** CAPIREF: Conigure a Session Object +** CAPI3REF: Configure a Session Object ** METHOD: sqlite3_session ** ** This method is used to configure a session object after it has been -** created. At present the only valid value for the second parameter is -** [SQLITE_SESSION_OBJCONFIG_SIZE]. +** created. At present the only valid values for the second parameter are +** [SQLITE_SESSION_OBJCONFIG_SIZE] and [SQLITE_SESSION_OBJCONFIG_ROWID]. ** -** Arguments for sqlite3session_object_config() +*/ +SQLITE_API int sqlite3session_object_config(sqlite3_session*, int op, void *pArg); + +/* +** CAPI3REF: Options for sqlite3session_object_config ** -** The following values may passed as the the 4th parameter to +** The following values may passed as the the 2nd parameter to ** sqlite3session_object_config(). ** **
SQLITE_SESSION_OBJCONFIG_SIZE
@@ -10775,12 +10850,21 @@ SQLITE_API void sqlite3session_delete(sqlite3_session *pSession); ** ** It is an error (SQLITE_MISUSE) to attempt to modify this setting after ** the first table has been attached to the session object. +** +**
SQLITE_SESSION_OBJCONFIG_ROWID
+** This option is used to set, clear or query the flag that enables +** collection of data for tables with no explicit PRIMARY KEY. +** +** Normally, tables with no explicit PRIMARY KEY are simply ignored +** by the sessions module. However, if this flag is set, it behaves +** as if such tables have a column "_rowid_ INTEGER PRIMARY KEY" inserted +** as their leftmost columns. +** +** It is an error (SQLITE_MISUSE) to attempt to modify this setting after +** the first table has been attached to the session object. */ -SQLITE_API int sqlite3session_object_config(sqlite3_session*, int op, void *pArg); - -/* -*/ -#define SQLITE_SESSION_OBJCONFIG_SIZE 1 +#define SQLITE_SESSION_OBJCONFIG_SIZE 1 +#define SQLITE_SESSION_OBJCONFIG_ROWID 2 /* ** CAPI3REF: Enable Or Disable A Session Object @@ -11913,9 +11997,23 @@ SQLITE_API int sqlite3changeset_apply_v2( ** Invert the changeset before applying it. This is equivalent to inverting ** a changeset using sqlite3changeset_invert() before applying it. It is ** an error to specify this flag with a patchset. +** +**
SQLITE_CHANGESETAPPLY_IGNORENOOP
+** Do not invoke the conflict handler callback for any changes that +** would not actually modify the database even if they were applied. +** Specifically, this means that the conflict handler is not invoked +** for: +**
    +**
  • a delete change if the row being deleted cannot be found, +**
  • an update change if the modified fields are already set to +** their new values in the conflicting row, or +**
  • an insert change if all fields of the conflicting row match +** the row being inserted. +**
*/ #define SQLITE_CHANGESETAPPLY_NOSAVEPOINT 0x0001 #define SQLITE_CHANGESETAPPLY_INVERT 0x0002 +#define SQLITE_CHANGESETAPPLY_IGNORENOOP 0x0004 /* ** CAPI3REF: Constants Passed To The Conflict Handler From c5818cfe6d272b8f485acbfac7c93120003f1dee Mon Sep 17 00:00:00 2001 From: Christian Stenger Date: Mon, 22 May 2023 11:42:32 +0200 Subject: [PATCH 180/192] Utils: Fix pathchooser handling for aspects ..especially when entering a path manually. Fixes some soft asserts regarding the call guard and re-allows to type a backslash directly to separate path from sub-path instead of using wild workarounds like adding a dummy character and adding the backslash before this. Change-Id: I8cc8aaccf414d0fd9acc03d7c69e10ddd88dbfd9 Reviewed-by: hjk --- src/libs/utils/aspects.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libs/utils/aspects.cpp b/src/libs/utils/aspects.cpp index 613a5561ea9..e2dda5e31cb 100644 --- a/src/libs/utils/aspects.cpp +++ b/src/libs/utils/aspects.cpp @@ -1134,7 +1134,7 @@ void StringAspect::addToLayout(LayoutItem &parent) if (d->m_blockAutoApply) return; d->m_blockAutoApply = true; - setValue(d->m_pathChooserDisplay->filePath().toString()); + setValueQuietly(d->m_pathChooserDisplay->filePath().toString()); d->m_blockAutoApply = false; }; connect(d->m_pathChooserDisplay, &PathChooser::editingFinished, this, setPathChooserValue); @@ -1142,7 +1142,7 @@ void StringAspect::addToLayout(LayoutItem &parent) } else { connect(d->m_pathChooserDisplay, &PathChooser::textChanged, this, [this](const QString &path) { - setValue(path); + setValueQuietly(path); }); } } From cae4dd01a8a5445d1b2815a22ff318eb2976ca48 Mon Sep 17 00:00:00 2001 From: Assam Boudjelthia Date: Fri, 12 May 2023 01:22:07 +0300 Subject: [PATCH 181/192] Android: Skip "Build APK" for non-app builds For projects using Qt 5.15 and Qt 6, the deployment settings file is generated by CMake/qmake and not Qt Creator, so if such file doesn't exist or it's been generated by Qt Creator, we can assume the project is not an android app. This is mainly a workaround for now to avoid a more involved fix which would need to potentially remove the method AndroidManager::deploymentSettings() (and it's use cases) which is used to generate a minimal version of deployment settings file, which was added some time ago where the build system CMake and Qt wasn't handling that generation, but now with Qt 5.15 and Qt 6, that shouldn't be a concern of Qt Creator. Fixes: QTCREATORBUG-27167 Fixes: QTBUG-111334 Task-number: QTCREATORBUG-26888 Change-Id: I15657f3b67acc52c28c92e6af24668f778432a19 Reviewed-by: Alessandro Portale --- src/plugins/android/androidmanager.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/plugins/android/androidmanager.cpp b/src/plugins/android/androidmanager.cpp index 0ccca7c8f7b..78b6202a65c 100644 --- a/src/plugins/android/androidmanager.cpp +++ b/src/plugins/android/androidmanager.cpp @@ -361,6 +361,14 @@ Abi AndroidManager::androidAbi2Abi(const QString &androidAbi) bool AndroidManager::skipInstallationAndPackageSteps(const Target *target) { + // For projects using Qt 5.15 and Qt 6, the deployment settings file + // is generated by CMake/qmake and not Qt Creator, so if such file doesn't exist + // or it's been generated by Qt Creator, we can assume the project is not + // an android app. + const FilePath inputFile = AndroidQtVersion::androidDeploymentSettings(target); + if (!inputFile.exists() || AndroidManager::isQtCreatorGenerated(inputFile)) + return true; + const Project *p = target->project(); const Core::Context cmakeCtx = Core::Context(CMakeProjectManager::Constants::CMAKE_PROJECT_ID); From cb053048583771ff9c60c548a6335c18e08f2050 Mon Sep 17 00:00:00 2001 From: Alexander Pershin Date: Fri, 19 May 2023 23:16:54 +0300 Subject: [PATCH 182/192] MiniProjectTargetSelector: Ensure selected item is visible on show event Is useful when project contains large amount of targets and you have to switch between them more or less often. Change also applies to other tree views in selector but they are less likely to contain large row counts. Change-Id: Ic2cd920335adeef618d85202b3347a4cd042871c Reviewed-by: Christian Kandeler --- src/plugins/projectexplorer/miniprojecttargetselector.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/plugins/projectexplorer/miniprojecttargetselector.cpp b/src/plugins/projectexplorer/miniprojecttargetselector.cpp index 3da81e61542..96f85e94bea 100644 --- a/src/plugins/projectexplorer/miniprojecttargetselector.cpp +++ b/src/plugins/projectexplorer/miniprojecttargetselector.cpp @@ -389,6 +389,12 @@ private: TreeView::mouseReleaseEvent(event); } + void showEvent(QShowEvent* event) override + { + scrollTo(currentIndex()); + TreeView::showEvent(event); + } + QObject *objectAt(const QModelIndex &index) const { return theModel()->itemForIndex(index)->object(); From 5e059db065b1680c4f08b901b64f102300eda6e6 Mon Sep 17 00:00:00 2001 From: hjk Date: Mon, 22 May 2023 12:01:30 +0200 Subject: [PATCH 183/192] CMakeProjectManager: Robustify buildAndRunOnSameDevice helper Change-Id: Ib3559f81f2ff71ad5fc04d982ad6286df0d4390e Reviewed-by: David Schulz Reviewed-by: Cristian Adam --- src/plugins/cmakeprojectmanager/cmakebuildstep.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/plugins/cmakeprojectmanager/cmakebuildstep.cpp b/src/plugins/cmakeprojectmanager/cmakebuildstep.cpp index d4e50a0c27e..885cdea9f8f 100644 --- a/src/plugins/cmakeprojectmanager/cmakebuildstep.cpp +++ b/src/plugins/cmakeprojectmanager/cmakebuildstep.cpp @@ -174,6 +174,8 @@ static bool buildAndRunOnSameDevice(Kit *kit) { IDeviceConstPtr runDevice = DeviceKitAspect::device(kit); IDeviceConstPtr buildDevice = BuildDeviceKitAspect::device(kit); + QTC_ASSERT(runDevice, return false); + QTC_ASSERT(buildDevice, return false); return runDevice->id() == buildDevice->id(); } From 4de56e2683010c8b22848191405150b4d0e69c82 Mon Sep 17 00:00:00 2001 From: hjk Date: Tue, 16 May 2023 15:00:32 +0200 Subject: [PATCH 184/192] Beautifier: Use Aspects for part of settings Change-Id: Icff60f5f1292ae6b2de2ce757d645d5fec47bcc6 Reviewed-by: Reviewed-by: Christian Stenger --- src/plugins/beautifier/CMakeLists.txt | 3 - src/plugins/beautifier/abstractsettings.cpp | 118 ++++------ src/plugins/beautifier/abstractsettings.h | 17 +- .../artisticstyle/artisticstyle.cpp | 2 +- .../beautifier/artisticstyle/artisticstyle.h | 1 - .../artisticstyleoptionspage.cpp | 134 ----------- .../artisticstyle/artisticstyleoptionspage.h | 18 -- .../artisticstyle/artisticstylesettings.cpp | 189 ++++++++------- .../artisticstyle/artisticstylesettings.h | 32 +-- src/plugins/beautifier/beautifier.qbs | 6 - .../beautifier/clangformat/clangformat.cpp | 6 +- .../beautifier/clangformat/clangformat.h | 1 - .../clangformat/clangformatoptionspage.cpp | 144 ------------ .../clangformat/clangformatoptionspage.h | 18 -- .../clangformat/clangformatsettings.cpp | 219 ++++++++++++------ .../clangformat/clangformatsettings.h | 26 +-- .../beautifier/uncrustify/uncrustify.cpp | 2 +- .../beautifier/uncrustify/uncrustify.h | 1 - .../uncrustify/uncrustifyoptionspage.cpp | 137 ----------- .../uncrustify/uncrustifyoptionspage.h | 18 -- .../uncrustify/uncrustifysettings.cpp | 214 +++++++++-------- .../uncrustify/uncrustifysettings.h | 43 ++-- 22 files changed, 453 insertions(+), 896 deletions(-) delete mode 100644 src/plugins/beautifier/artisticstyle/artisticstyleoptionspage.cpp delete mode 100644 src/plugins/beautifier/artisticstyle/artisticstyleoptionspage.h delete mode 100644 src/plugins/beautifier/clangformat/clangformatoptionspage.cpp delete mode 100644 src/plugins/beautifier/clangformat/clangformatoptionspage.h delete mode 100644 src/plugins/beautifier/uncrustify/uncrustifyoptionspage.cpp delete mode 100644 src/plugins/beautifier/uncrustify/uncrustifyoptionspage.h diff --git a/src/plugins/beautifier/CMakeLists.txt b/src/plugins/beautifier/CMakeLists.txt index cdfacccce1a..4e921342b7a 100644 --- a/src/plugins/beautifier/CMakeLists.txt +++ b/src/plugins/beautifier/CMakeLists.txt @@ -5,7 +5,6 @@ add_qtc_plugin(Beautifier abstractsettings.cpp abstractsettings.h artisticstyle/artisticstyle.cpp artisticstyle/artisticstyle.h artisticstyle/artisticstyleconstants.h - artisticstyle/artisticstyleoptionspage.cpp artisticstyle/artisticstyleoptionspage.h artisticstyle/artisticstylesettings.cpp artisticstyle/artisticstylesettings.h beautifier.qrc beautifierabstracttool.h @@ -14,7 +13,6 @@ add_qtc_plugin(Beautifier beautifiertr.h clangformat/clangformat.cpp clangformat/clangformat.h clangformat/clangformatconstants.h - clangformat/clangformatoptionspage.cpp clangformat/clangformatoptionspage.h clangformat/clangformatsettings.cpp clangformat/clangformatsettings.h configurationdialog.cpp configurationdialog.h configurationeditor.cpp configurationeditor.h @@ -22,6 +20,5 @@ add_qtc_plugin(Beautifier generalsettings.cpp generalsettings.h uncrustify/uncrustify.cpp uncrustify/uncrustify.h uncrustify/uncrustifyconstants.h - uncrustify/uncrustifyoptionspage.cpp uncrustify/uncrustifyoptionspage.h uncrustify/uncrustifysettings.cpp uncrustify/uncrustifysettings.h ) diff --git a/src/plugins/beautifier/abstractsettings.cpp b/src/plugins/beautifier/abstractsettings.cpp index fa621fdebcb..c80d4f6ac7b 100644 --- a/src/plugins/beautifier/abstractsettings.cpp +++ b/src/plugins/beautifier/abstractsettings.cpp @@ -26,9 +26,6 @@ using namespace Utils; namespace Beautifier::Internal { -const char COMMAND[] = "command"; -const char SUPPORTED_MIME[] = "supportedMime"; - class VersionUpdater { public: @@ -86,9 +83,40 @@ AbstractSettings::AbstractSettings(const QString &name, const QString &ending) , m_styleDir(Core::ICore::userResourcePath(Beautifier::Constants::SETTINGS_DIRNAME) .pathAppended(name) .toString()) - , m_name(name) , m_versionUpdater(new VersionUpdater) { + setSettingsGroups(Utils::Constants::BEAUTIFIER_SETTINGS_GROUP, name); + + registerAspect(&command); + command.setSettingsKey("command"); + command.setExpectedKind(Utils::PathChooser::ExistingCommand); + command.setCommandVersionArguments({"--version"}); + command.setPromptDialogTitle(BeautifierPlugin::msgCommandPromptDialogTitle("Clang Format")); + + registerAspect(&supportedMimeTypes); + supportedMimeTypes.setDisplayStyle(StringAspect::LineEditDisplay); + supportedMimeTypes.setSettingsKey("supportedMime"); + supportedMimeTypes.setLabelText(Tr::tr("Restrict to MIME types:")); + supportedMimeTypes.setDefaultValue("text/x-c++src; text/x-c++hdr; text/x-csrc;text/x-chdr; " + "text/x-objcsrc; text/x-objc++src"); + + supportedMimeTypes.setValueAcceptor([](const QString &, const QString &value) -> std::optional { + const QStringList stringTypes = value.split(';'); + QStringList types; + for (const QString &type : stringTypes) { + const MimeType mime = mimeTypeForName(type.trimmed()); + if (!mime.isValid()) + continue; + const QString canonicalName = mime.name(); + if (!types.contains(canonicalName)) + types << canonicalName; + } + return types.join("; "); + }); + + connect(&command, &BaseAspect::changed, this, [this] { + m_versionUpdater->update(command()); + }); } AbstractSettings::~AbstractSettings() = default; @@ -157,20 +185,6 @@ QString AbstractSettings::styleFileName(const QString &key) const return m_styleDir.absoluteFilePath(key + m_ending); } -FilePath AbstractSettings::command() const -{ - return m_command; -} - -void AbstractSettings::setCommand(const FilePath &cmd) -{ - if (cmd == m_command) - return; - - m_command = cmd; - m_versionUpdater->update(command()); -} - QVersionNumber AbstractSettings::version() const { return m_versionUpdater->version(); @@ -181,30 +195,6 @@ void AbstractSettings::setVersionRegExp(const QRegularExpression &versionRegExp) m_versionUpdater->setVersionRegExp(versionRegExp); } -QString AbstractSettings::supportedMimeTypesAsString() const -{ - return m_supportedMimeTypes.join("; "); -} - -void AbstractSettings::setSupportedMimeTypes(const QString &mimes) -{ - const QStringList stringTypes = mimes.split(';'); - QStringList types; - for (const QString &type : stringTypes) { - const MimeType mime = mimeTypeForName(type.trimmed()); - if (!mime.isValid()) - continue; - const QString canonicalName = mime.name(); - if (!types.contains(canonicalName)) - types << canonicalName; - } - - if (m_supportedMimeTypes != types) { - m_supportedMimeTypes = types; - emit supportedMimeTypesChanged(); - } -} - bool AbstractSettings::isApplicable(const Core::IDocument *document) const { if (!document) @@ -240,17 +230,8 @@ void AbstractSettings::save() { // Save settings, except styles QSettings *s = Core::ICore::settings(); - s->beginGroup(Utils::Constants::BEAUTIFIER_SETTINGS_GROUP); - s->beginGroup(m_name); - QMap::const_iterator iSettings = m_settings.constBegin(); - while (iSettings != m_settings.constEnd()) { - s->setValue(iSettings.key(), iSettings.value()); - ++iSettings; - } - s->setValue(COMMAND, m_command.toSettings()); - s->setValue(SUPPORTED_MIME, supportedMimeTypesAsString()); - s->endGroup(); - s->endGroup(); + + AspectContainer::writeSettings(s); // Save styles if (m_stylesToRemove.isEmpty() && m_styles.isEmpty()) @@ -306,27 +287,9 @@ void AbstractSettings::createDocumentationFile() const void AbstractSettings::read() { - // Set default values - setSupportedMimeTypes("text/x-c++src;text/x-c++hdr;text/x-csrc;text/x-chdr;text/x-objcsrc;" - "text/x-objc++src"); - // Read settings, except styles QSettings *s = Core::ICore::settings(); - s->beginGroup(Utils::Constants::BEAUTIFIER_SETTINGS_GROUP); - s->beginGroup(m_name); - const QStringList keys = s->allKeys(); - for (const QString &key : keys) { - if (key == COMMAND) - setCommand(FilePath::fromSettings(s->value(key))); - else if (key == SUPPORTED_MIME) - setSupportedMimeTypes(s->value(key).toString()); - else if (m_settings.contains(key)) - m_settings[key] = s->value(key); - else - s->remove(key); - } - s->endGroup(); - s->endGroup(); + AspectContainer::readSettings(s); m_styles.clear(); m_changedStyles.clear(); @@ -336,18 +299,19 @@ void AbstractSettings::read() void AbstractSettings::readDocumentation() { - const QString filename = documentationFilePath(); + const FilePath filename = documentationFilePath(); if (filename.isEmpty()) { BeautifierPlugin::showError(Tr::tr("No documentation file specified.")); return; } - QFile file(filename); + QFile file(filename.toFSPathString()); if (!file.exists()) createDocumentationFile(); if (!file.open(QIODevice::ReadOnly)) { - BeautifierPlugin::showError(Tr::tr("Cannot open documentation file \"%1\".").arg(filename)); + BeautifierPlugin::showError(Tr::tr("Cannot open documentation file \"%1\".") + .arg(filename.toUserOutput())); return; } @@ -356,7 +320,7 @@ void AbstractSettings::readDocumentation() return; if (xml.name() != QLatin1String(Constants::DOCUMENTATION_XMLROOT)) { BeautifierPlugin::showError(Tr::tr("The file \"%1\" is not a valid documentation file.") - .arg(filename)); + .arg(filename.toUserOutput())); return; } @@ -387,7 +351,7 @@ void AbstractSettings::readDocumentation() if (xml.hasError()) { BeautifierPlugin::showError(Tr::tr("Cannot read documentation file \"%1\": %2.") - .arg(filename).arg(xml.errorString())); + .arg(filename.toUserOutput()).arg(xml.errorString())); } } diff --git a/src/plugins/beautifier/abstractsettings.h b/src/plugins/beautifier/abstractsettings.h index 25baa838858..5eb930396ee 100644 --- a/src/plugins/beautifier/abstractsettings.h +++ b/src/plugins/beautifier/abstractsettings.h @@ -3,6 +3,8 @@ #pragma once +#include + #include #include @@ -40,7 +42,6 @@ public: void read(); void save(); - virtual QString documentationFilePath() const = 0; virtual void createDocumentationFile() const; virtual QStringList completerWords(); @@ -53,25 +54,21 @@ public: void replaceStyle(const QString &oldKey, const QString &newKey, const QString &value); virtual QString styleFileName(const QString &key) const; - Utils::FilePath command() const; - void setCommand(const Utils::FilePath &cmd); + Utils::FilePathAspect command; + Utils::StringAspect supportedMimeTypes; + Utils::FilePathAspect documentationFilePath; + QVersionNumber version() const; - QString supportedMimeTypesAsString() const; - void setSupportedMimeTypes(const QString &mimes); bool isApplicable(const Core::IDocument *document) const; QStringList options(); QString documentation(const QString &option) const; -signals: - void supportedMimeTypesChanged(); - protected: void setVersionRegExp(const QRegularExpression &versionRegExp); QMap m_styles; - QMap m_settings; QString m_ending; QDir m_styleDir; @@ -79,11 +76,9 @@ protected: virtual void readStyles(); private: - QString m_name; std::unique_ptr m_versionUpdater; QStringList m_stylesToRemove; QSet m_changedStyles; - Utils::FilePath m_command; QHash m_options; QStringList m_docu; QStringList m_supportedMimeTypes; diff --git a/src/plugins/beautifier/artisticstyle/artisticstyle.cpp b/src/plugins/beautifier/artisticstyle/artisticstyle.cpp index 610f636d743..1f063829944 100644 --- a/src/plugins/beautifier/artisticstyle/artisticstyle.cpp +++ b/src/plugins/beautifier/artisticstyle/artisticstyle.cpp @@ -48,7 +48,7 @@ ArtisticStyle::ArtisticStyle() Core::ActionManager::actionContainer(Constants::MENU_ID)->addMenu(menu); - connect(&m_settings, &ArtisticStyleSettings::supportedMimeTypesChanged, + connect(&m_settings.supportedMimeTypes, &Utils::BaseAspect::changed, this, [this] { updateActions(Core::EditorManager::currentEditor()); }); } diff --git a/src/plugins/beautifier/artisticstyle/artisticstyle.h b/src/plugins/beautifier/artisticstyle/artisticstyle.h index ef394d068aa..3f3f15946af 100644 --- a/src/plugins/beautifier/artisticstyle/artisticstyle.h +++ b/src/plugins/beautifier/artisticstyle/artisticstyle.h @@ -5,7 +5,6 @@ #include "../beautifierabstracttool.h" -#include "artisticstyleoptionspage.h" #include "artisticstylesettings.h" namespace Beautifier::Internal { diff --git a/src/plugins/beautifier/artisticstyle/artisticstyleoptionspage.cpp b/src/plugins/beautifier/artisticstyle/artisticstyleoptionspage.cpp deleted file mode 100644 index e34fe18349d..00000000000 --- a/src/plugins/beautifier/artisticstyle/artisticstyleoptionspage.cpp +++ /dev/null @@ -1,134 +0,0 @@ -// Copyright (C) 2016 Lorenz Haas -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "artisticstyleoptionspage.h" - -#include "artisticstyleconstants.h" -#include "artisticstylesettings.h" - -#include "../beautifierconstants.h" -#include "../beautifierplugin.h" -#include "../beautifiertr.h" -#include "../configurationpanel.h" - -#include -#include - -#include -#include -#include -#include -#include - -namespace Beautifier::Internal { - -class ArtisticStyleOptionsPageWidget : public Core::IOptionsPageWidget -{ -public: - explicit ArtisticStyleOptionsPageWidget(ArtisticStyleSettings *settings); - - void apply() final; - -private: - ArtisticStyleSettings *m_settings; - - Utils::PathChooser *m_command; - QLineEdit *m_mime; - QCheckBox *m_useOtherFiles; - QCheckBox *m_useSpecificConfigFile; - Utils::PathChooser *m_specificConfigFile; - QCheckBox *m_useHomeFile; - QCheckBox *m_useCustomStyle; - Beautifier::Internal::ConfigurationPanel *m_configurations; -}; - -ArtisticStyleOptionsPageWidget::ArtisticStyleOptionsPageWidget(ArtisticStyleSettings *settings) - : m_settings(settings) -{ - m_command = new Utils::PathChooser; - - m_mime = new QLineEdit(m_settings->supportedMimeTypesAsString()); - - auto options = new QGroupBox(Tr::tr("Options")); - - m_useOtherFiles = new QCheckBox(Tr::tr("Use file *.astylerc defined in project files")); - m_useOtherFiles->setChecked(m_settings->useOtherFiles()); - - m_useSpecificConfigFile = new QCheckBox(Tr::tr("Use specific config file:")); - m_useSpecificConfigFile->setChecked(m_settings->useSpecificConfigFile()); - - m_specificConfigFile = new Utils::PathChooser; - m_specificConfigFile->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); - m_specificConfigFile->setExpectedKind(Utils::PathChooser::File); - m_specificConfigFile->setPromptDialogFilter(Tr::tr("AStyle (*.astylerc)")); - m_specificConfigFile->setFilePath(m_settings->specificConfigFile()); - - m_useHomeFile = new QCheckBox( - Tr::tr("Use file .astylerc or astylerc in HOME"). - replace("HOME", QDir::toNativeSeparators(QDir::home().absolutePath()))); - m_useHomeFile->setChecked(m_settings->useHomeFile()); - - m_useCustomStyle = new QCheckBox(Tr::tr("Use customized style:")); - m_useCustomStyle->setChecked(m_settings->useCustomStyle()); - - m_configurations = new ConfigurationPanel(options); - m_configurations->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); - m_configurations->setSettings(m_settings); - m_configurations->setCurrentConfiguration(m_settings->customStyle()); - - m_command->setExpectedKind(Utils::PathChooser::ExistingCommand); - m_command->setCommandVersionArguments({"--version"}); - m_command->setPromptDialogTitle(BeautifierPlugin::msgCommandPromptDialogTitle( - Tr::tr(Constants::ARTISTICSTYLE_DISPLAY_NAME))); - m_command->setFilePath(m_settings->command()); - - using namespace Layouting; - - Column { - m_useOtherFiles, - Row { m_useSpecificConfigFile, m_specificConfigFile }, - m_useHomeFile, - Row { m_useCustomStyle, m_configurations }, - noMargin, - }.attachTo(options); - - Column { - Group { - title(Tr::tr("Configuration")), - Form { - Tr::tr("Artistic Style command:"), m_command, br, - Tr::tr("Restrict to MIME types:"), m_mime - } - }, - options, - st - }.attachTo(this); - - connect(m_command, &Utils::PathChooser::validChanged, options, &QWidget::setEnabled); -} - -void ArtisticStyleOptionsPageWidget::apply() -{ - m_settings->setCommand(m_command->filePath()); - m_settings->setSupportedMimeTypes(m_mime->text()); - m_settings->setUseOtherFiles(m_useOtherFiles->isChecked()); - m_settings->setUseSpecificConfigFile(m_useSpecificConfigFile->isChecked()); - m_settings->setSpecificConfigFile(m_specificConfigFile->filePath()); - m_settings->setUseHomeFile(m_useHomeFile->isChecked()); - m_settings->setUseCustomStyle(m_useCustomStyle->isChecked()); - m_settings->setCustomStyle(m_configurations->currentConfiguration()); - m_settings->save(); - - // update since not all MIME types are accepted (invalids or duplicates) - m_mime->setText(m_settings->supportedMimeTypesAsString()); -} - -ArtisticStyleOptionsPage::ArtisticStyleOptionsPage(ArtisticStyleSettings *settings) -{ - setId("ArtisticStyle"); - setDisplayName(Tr::tr("Artistic Style")); - setCategory(Constants::OPTION_CATEGORY); - setWidgetCreator([settings] { return new ArtisticStyleOptionsPageWidget(settings); }); -} - -} // Beautifier::Internal diff --git a/src/plugins/beautifier/artisticstyle/artisticstyleoptionspage.h b/src/plugins/beautifier/artisticstyle/artisticstyleoptionspage.h deleted file mode 100644 index 161e20f7067..00000000000 --- a/src/plugins/beautifier/artisticstyle/artisticstyleoptionspage.h +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright (C) 2016 Lorenz Haas -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include - -namespace Beautifier::Internal { - -class ArtisticStyleSettings; - -class ArtisticStyleOptionsPage final : public Core::IOptionsPage -{ -public: - explicit ArtisticStyleOptionsPage(ArtisticStyleSettings *settings); -}; - -} // Beautifier::Internal diff --git a/src/plugins/beautifier/artisticstyle/artisticstylesettings.cpp b/src/plugins/beautifier/artisticstyle/artisticstylesettings.cpp index 42b91a785fe..78537df1970 100644 --- a/src/plugins/beautifier/artisticstyle/artisticstylesettings.cpp +++ b/src/plugins/beautifier/artisticstyle/artisticstylesettings.cpp @@ -3,16 +3,24 @@ #include "artisticstylesettings.h" +#include "artisticstyleconstants.h" #include "../beautifierconstants.h" +#include "../beautifierplugin.h" +#include "../beautifiertr.h" +#include "../configurationpanel.h" #include +#include +#include #include #include +#include #include #include #include +#include #include #include @@ -20,96 +28,52 @@ using namespace Utils; namespace Beautifier::Internal { -const char USE_OTHER_FILES[] = "useOtherFiles"; -const char USE_SPECIFIC_CONFIG_FILE[] = "useSpecificConfigFile"; -const char SPECIFIC_CONFIG_FILE[] = "specificConfigFile"; -const char USE_HOME_FILE[] = "useHomeFile"; -const char USE_CUSTOM_STYLE[] = "useCustomStyle"; -const char CUSTOM_STYLE[] = "customStyle"; const char SETTINGS_NAME[] = "artisticstyle"; -ArtisticStyleSettings::ArtisticStyleSettings() : - AbstractSettings(SETTINGS_NAME, ".astyle") +ArtisticStyleSettings::ArtisticStyleSettings() + : AbstractSettings(SETTINGS_NAME, ".astyle") { setVersionRegExp(QRegularExpression("([2-9]{1})\\.([0-9]{1,2})(\\.[1-9]{1})?$")); - setCommand("astyle"); - m_settings.insert(USE_OTHER_FILES, QVariant(true)); - m_settings.insert(USE_SPECIFIC_CONFIG_FILE, QVariant(false)); - m_settings.insert(SPECIFIC_CONFIG_FILE, QVariant()); - m_settings.insert(USE_HOME_FILE, QVariant(false)); - m_settings.insert(USE_CUSTOM_STYLE, QVariant(false)); - m_settings.insert(CUSTOM_STYLE, QVariant()); + command.setLabelText(Tr::tr("Artistic Style command:")); + command.setDefaultValue("astyle"); + command.setPromptDialogTitle(BeautifierPlugin::msgCommandPromptDialogTitle( + Tr::tr(Constants::ARTISTICSTYLE_DISPLAY_NAME))); + + registerAspect(&useOtherFiles); + useOtherFiles.setSettingsKey("useOtherFiles"); + useOtherFiles.setLabelText(Tr::tr("Use file *.astylerc defined in project files")); + useOtherFiles.setDefaultValue(true); + + registerAspect(&useSpecificConfigFile); + useSpecificConfigFile.setSettingsKey("useSpecificConfigFile"); + useSpecificConfigFile.setLabelText(Tr::tr("Use specific config file:")); + + registerAspect(&specificConfigFile); + specificConfigFile.setSettingsKey("specificConfigFile"); + specificConfigFile.setExpectedKind(PathChooser::File); + specificConfigFile.setPromptDialogFilter(Tr::tr("AStyle (*.astylerc)")); + + registerAspect(&useHomeFile); + useHomeFile.setSettingsKey("useHomeFile"); + useHomeFile.setLabelText(Tr::tr("Use file .astylerc or astylerc in HOME"). + replace("HOME", QDir::toNativeSeparators(QDir::home().absolutePath()))); + + registerAspect(&useCustomStyle); + useCustomStyle.setSettingsKey("useCustomStyle"); + useCustomStyle.setLabelText(Tr::tr("Use customized style:")); + + registerAspect(&customStyle); + customStyle.setSettingsKey("customStyle"); + + documentationFilePath.setFilePath( + Core::ICore::userResourcePath(Beautifier::Constants::SETTINGS_DIRNAME) + .pathAppended(Beautifier::Constants::DOCUMENTATION_DIRNAME) + .pathAppended(SETTINGS_NAME) + .stringAppended(".xml")); + read(); } -bool ArtisticStyleSettings::useOtherFiles() const -{ - return m_settings.value(USE_OTHER_FILES).toBool(); -} - -void ArtisticStyleSettings::setUseOtherFiles(bool useOtherFiles) -{ - m_settings.insert(USE_OTHER_FILES, QVariant(useOtherFiles)); -} - -bool ArtisticStyleSettings::useSpecificConfigFile() const -{ - return m_settings.value(USE_SPECIFIC_CONFIG_FILE).toBool(); -} - -void ArtisticStyleSettings::setUseSpecificConfigFile(bool useSpecificConfigFile) -{ - m_settings.insert(USE_SPECIFIC_CONFIG_FILE, QVariant(useSpecificConfigFile)); -} - -FilePath ArtisticStyleSettings::specificConfigFile() const -{ - return FilePath::fromString(m_settings.value(SPECIFIC_CONFIG_FILE).toString()); -} - -void ArtisticStyleSettings::setSpecificConfigFile(const FilePath &specificConfigFile) -{ - m_settings.insert(SPECIFIC_CONFIG_FILE, QVariant(specificConfigFile.toString())); -} - -bool ArtisticStyleSettings::useHomeFile() const -{ - return m_settings.value(USE_HOME_FILE).toBool(); -} - -void ArtisticStyleSettings::setUseHomeFile(bool useHomeFile) -{ - m_settings.insert(USE_HOME_FILE, QVariant(useHomeFile)); -} - -bool ArtisticStyleSettings::useCustomStyle() const -{ - return m_settings.value(USE_CUSTOM_STYLE).toBool(); -} - -void ArtisticStyleSettings::setUseCustomStyle(bool useCustomStyle) -{ - m_settings.insert(USE_CUSTOM_STYLE, QVariant(useCustomStyle)); -} - -QString ArtisticStyleSettings::customStyle() const -{ - return m_settings.value(CUSTOM_STYLE).toString(); -} - -void ArtisticStyleSettings::setCustomStyle(const QString &customStyle) -{ - m_settings.insert(CUSTOM_STYLE, QVariant(customStyle)); -} - -QString ArtisticStyleSettings::documentationFilePath() const -{ - return (Core::ICore::userResourcePath(Beautifier::Constants::SETTINGS_DIRNAME) - / Beautifier::Constants::DOCUMENTATION_DIRNAME / SETTINGS_NAME) - .stringAppended(".xml") - .toString(); -} - void ArtisticStyleSettings::createDocumentationFile() const { Process process; @@ -119,7 +83,7 @@ void ArtisticStyleSettings::createDocumentationFile() const if (process.result() != ProcessResult::FinishedWithSuccess) return; - QFile file(documentationFilePath()); + QFile file(documentationFilePath().toFSPathString()); const QFileInfo fi(file); if (!fi.exists()) fi.dir().mkpath(fi.absolutePath()); @@ -183,4 +147,61 @@ void ArtisticStyleSettings::createDocumentationFile() const } } +class ArtisticStyleOptionsPageWidget : public Core::IOptionsPageWidget +{ +public: + explicit ArtisticStyleOptionsPageWidget(ArtisticStyleSettings *settings) + { + QGroupBox *options = nullptr; + + auto configurations = new ConfigurationPanel(this); + configurations->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); + configurations->setSettings(settings); + configurations->setCurrentConfiguration(settings->customStyle()); + + using namespace Layouting; + + ArtisticStyleSettings &s = *settings; + + Column { + Group { + title(Tr::tr("Configuration")), + Form { + s.command, br, + s.supportedMimeTypes + } + }, + Group { + title(Tr::tr("Options")), + bindTo(&options), + Column { + s.useOtherFiles, + Row { s.useSpecificConfigFile, s.specificConfigFile }, + s.useHomeFile, + Row { s.useCustomStyle, configurations }, + } + }, + st + }.attachTo(this); + + setOnApply([&s, configurations] { + s.customStyle.setValue(configurations->currentConfiguration()); + s.save(); + }); + + s.read(); + + connect(s.command.pathChooser(), &PathChooser::validChanged, options, &QWidget::setEnabled); + options->setEnabled(s.command.pathChooser()->isValid()); + } +}; + +ArtisticStyleOptionsPage::ArtisticStyleOptionsPage(ArtisticStyleSettings *settings) +{ + setId("ArtisticStyle"); + setDisplayName(Tr::tr("Artistic Style")); + setCategory(Constants::OPTION_CATEGORY); + setWidgetCreator([settings] { return new ArtisticStyleOptionsPageWidget(settings); }); +} + } // Beautifier::Internal diff --git a/src/plugins/beautifier/artisticstyle/artisticstylesettings.h b/src/plugins/beautifier/artisticstyle/artisticstylesettings.h index de5402a8ee5..5b74f8d1a03 100644 --- a/src/plugins/beautifier/artisticstyle/artisticstylesettings.h +++ b/src/plugins/beautifier/artisticstyle/artisticstylesettings.h @@ -5,8 +5,6 @@ #include "../abstractsettings.h" -#include - namespace Beautifier::Internal { class ArtisticStyleSettings : public AbstractSettings @@ -14,26 +12,20 @@ class ArtisticStyleSettings : public AbstractSettings public: ArtisticStyleSettings(); - bool useOtherFiles() const; - void setUseOtherFiles(bool useOtherFiles); + Utils::BoolAspect useOtherFiles; + Utils::BoolAspect useSpecificConfigFile; + Utils::FilePathAspect specificConfigFile; + Utils::BoolAspect useHomeFile; + Utils::BoolAspect useCustomStyle; + Utils::StringAspect customStyle; - bool useSpecificConfigFile() const; - void setUseSpecificConfigFile(bool useSpecificConfigFile); - - Utils::FilePath specificConfigFile() const; - void setSpecificConfigFile(const Utils::FilePath &specificConfigFile); - - bool useHomeFile() const; - void setUseHomeFile(bool useHomeFile); - - bool useCustomStyle() const; - void setUseCustomStyle(bool useCustomStyle); - - QString customStyle() const; - void setCustomStyle(const QString &customStyle); - - QString documentationFilePath() const override; void createDocumentationFile() const override; }; +class ArtisticStyleOptionsPage final : public Core::IOptionsPage +{ +public: + explicit ArtisticStyleOptionsPage(ArtisticStyleSettings *settings); +}; + } // Beautifier::Internal diff --git a/src/plugins/beautifier/beautifier.qbs b/src/plugins/beautifier/beautifier.qbs index 0f85ca29b57..24fecd671f1 100644 --- a/src/plugins/beautifier/beautifier.qbs +++ b/src/plugins/beautifier/beautifier.qbs @@ -36,8 +36,6 @@ QtcPlugin { "artisticstyle.cpp", "artisticstyle.h", "artisticstyleconstants.h", - "artisticstyleoptionspage.cpp", - "artisticstyleoptionspage.h", "artisticstylesettings.cpp", "artisticstylesettings.h" ] @@ -50,8 +48,6 @@ QtcPlugin { "clangformat.cpp", "clangformat.h", "clangformatconstants.h", - "clangformatoptionspage.cpp", - "clangformatoptionspage.h", "clangformatsettings.cpp", "clangformatsettings.h" ] @@ -64,8 +60,6 @@ QtcPlugin { "uncrustify.cpp", "uncrustify.h", "uncrustifyconstants.h", - "uncrustifyoptionspage.cpp", - "uncrustifyoptionspage.h", "uncrustifysettings.cpp", "uncrustifysettings.h" ] diff --git a/src/plugins/beautifier/clangformat/clangformat.cpp b/src/plugins/beautifier/clangformat/clangformat.cpp index c1833a77f11..92c8c7f454d 100644 --- a/src/plugins/beautifier/clangformat/clangformat.cpp +++ b/src/plugins/beautifier/clangformat/clangformat.cpp @@ -64,7 +64,7 @@ ClangFormat::ClangFormat() Core::ActionManager::actionContainer(Constants::MENU_ID)->addMenu(menu); - connect(&m_settings, &ClangFormatSettings::supportedMimeTypesChanged, + connect(&m_settings.supportedMimeTypes, &Utils::BaseAspect::changed, this, [this] { updateActions(Core::EditorManager::currentEditor()); }); } @@ -191,10 +191,10 @@ Command ClangFormat::command() const command.setProcessing(Command::PipeProcessing); if (m_settings.usePredefinedStyle()) { - const QString predefinedStyle = m_settings.predefinedStyle(); + const QString predefinedStyle = m_settings.predefinedStyle.stringValue(); command.addOption("-style=" + predefinedStyle); if (predefinedStyle == "File") { - const QString fallbackStyle = m_settings.fallbackStyle(); + const QString fallbackStyle = m_settings.fallbackStyle.stringValue(); if (fallbackStyle != "Default") command.addOption("-fallback-style=" + fallbackStyle); } diff --git a/src/plugins/beautifier/clangformat/clangformat.h b/src/plugins/beautifier/clangformat/clangformat.h index 8563a782efc..65fd23a43f1 100644 --- a/src/plugins/beautifier/clangformat/clangformat.h +++ b/src/plugins/beautifier/clangformat/clangformat.h @@ -5,7 +5,6 @@ #include "../beautifierabstracttool.h" -#include "clangformatoptionspage.h" #include "clangformatsettings.h" namespace Beautifier::Internal { diff --git a/src/plugins/beautifier/clangformat/clangformatoptionspage.cpp b/src/plugins/beautifier/clangformat/clangformatoptionspage.cpp deleted file mode 100644 index fd79826846a..00000000000 --- a/src/plugins/beautifier/clangformat/clangformatoptionspage.cpp +++ /dev/null @@ -1,144 +0,0 @@ -// Copyright (C) 2016 Lorenz Haas -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "clangformatoptionspage.h" - -#include "clangformatsettings.h" - -#include "../beautifierconstants.h" -#include "../beautifierplugin.h" -#include "../beautifiertr.h" -#include "../configurationpanel.h" - -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -namespace Beautifier::Internal { - -class ClangFormatOptionsPageWidget : public Core::IOptionsPageWidget -{ -public: - explicit ClangFormatOptionsPageWidget(ClangFormatSettings *settings); - - void apply() final; - -private: - ClangFormatSettings *m_settings; - ConfigurationPanel *m_configurations; - QRadioButton *m_usePredefinedStyle; - QComboBox *m_predefinedStyle; - QComboBox *m_fallbackStyle; - Utils::PathChooser *m_command; - QLineEdit *m_mime; -}; - -ClangFormatOptionsPageWidget::ClangFormatOptionsPageWidget(ClangFormatSettings *settings) - : m_settings(settings) -{ - auto options = new QGroupBox(Tr::tr("Options")); - options->setEnabled(false); - - auto styleButtonGroup = new QButtonGroup(this); - - auto useCustomizedStyle = new QRadioButton(Tr::tr("Use customized style:")); - styleButtonGroup->addButton(useCustomizedStyle); - - m_configurations = new ConfigurationPanel; - m_configurations->setSettings(m_settings); - m_configurations->setCurrentConfiguration(m_settings->customStyle()); - - m_usePredefinedStyle = new QRadioButton(Tr::tr("Use predefined style:")); - - m_usePredefinedStyle->setChecked(true); - styleButtonGroup->addButton(m_usePredefinedStyle); - - m_predefinedStyle = new QComboBox; - m_predefinedStyle->addItems(m_settings->predefinedStyles()); - const int predefinedStyleIndex = m_predefinedStyle->findText(m_settings->predefinedStyle()); - if (predefinedStyleIndex != -1) - m_predefinedStyle->setCurrentIndex(predefinedStyleIndex); - - m_fallbackStyle = new QComboBox; - m_fallbackStyle->addItems(m_settings->fallbackStyles()); - m_fallbackStyle->setEnabled(false); - const int fallbackStyleIndex = m_fallbackStyle->findText(m_settings->fallbackStyle()); - if (fallbackStyleIndex != -1) - m_fallbackStyle->setCurrentIndex(fallbackStyleIndex); - - m_mime = new QLineEdit(m_settings->supportedMimeTypesAsString()); - - m_command = new Utils::PathChooser; - m_command->setExpectedKind(Utils::PathChooser::ExistingCommand); - m_command->setCommandVersionArguments({"--version"}); - m_command->setPromptDialogTitle( - BeautifierPlugin::msgCommandPromptDialogTitle("Clang Format")); - - if (m_settings->usePredefinedStyle()) - m_usePredefinedStyle->setChecked(true); - else - useCustomizedStyle->setChecked(true); - - using namespace Layouting; - - Form { - m_usePredefinedStyle, m_predefinedStyle, br, - empty, Row { Tr::tr("Fallback style:"), m_fallbackStyle }, br, - useCustomizedStyle, m_configurations, br, - }.attachTo(options); - - Column { - Group { - title(Tr::tr("Configuration")), - Form { - Tr::tr("Clang Format command:"), m_command, br, - Tr::tr("Restrict to MIME types:"), m_mime - } - }, - options, - st - }.attachTo(this); - - connect(m_command, &Utils::PathChooser::validChanged, options, &QWidget::setEnabled); - connect(m_predefinedStyle, &QComboBox::currentTextChanged, this, [this](const QString &item) { - m_fallbackStyle->setEnabled(item == "File"); - }); - connect(m_usePredefinedStyle, &QRadioButton::toggled, this, [this](bool checked) { - m_fallbackStyle->setEnabled(checked && m_predefinedStyle->currentText() == "File"); - m_predefinedStyle->setEnabled(checked); - }); - - // might trigger PathChooser::validChanged, so so after the connect above - m_command->setFilePath(m_settings->command()); -} - -void ClangFormatOptionsPageWidget::apply() -{ - m_settings->setCommand(m_command->filePath()); - m_settings->setSupportedMimeTypes(m_mime->text()); - m_settings->setUsePredefinedStyle(m_usePredefinedStyle->isChecked()); - m_settings->setPredefinedStyle(m_predefinedStyle->currentText()); - m_settings->setFallbackStyle(m_fallbackStyle->currentText()); - m_settings->setCustomStyle(m_configurations->currentConfiguration()); - m_settings->save(); - - // update since not all MIME types are accepted (invalids or duplicates) - m_mime->setText(m_settings->supportedMimeTypesAsString()); -} - -ClangFormatOptionsPage::ClangFormatOptionsPage(ClangFormatSettings *settings) -{ - setId("ClangFormat"); - setDisplayName(Tr::tr("Clang Format")); - setCategory(Constants::OPTION_CATEGORY); - setWidgetCreator([settings] { return new ClangFormatOptionsPageWidget(settings); }); -} - -} // Beautifier::Internal diff --git a/src/plugins/beautifier/clangformat/clangformatoptionspage.h b/src/plugins/beautifier/clangformat/clangformatoptionspage.h deleted file mode 100644 index e9a845af267..00000000000 --- a/src/plugins/beautifier/clangformat/clangformatoptionspage.h +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright (C) 2016 Lorenz Haas -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include - -namespace Beautifier::Internal { - -class ClangFormatSettings; - -class ClangFormatOptionsPage final : public Core::IOptionsPage -{ -public: - explicit ClangFormatOptionsPage(ClangFormatSettings *settings); -}; - -} // Beautifier::Internal diff --git a/src/plugins/beautifier/clangformat/clangformatsettings.cpp b/src/plugins/beautifier/clangformat/clangformatsettings.cpp index 16a2225cd97..dc061ed9658 100644 --- a/src/plugins/beautifier/clangformat/clangformatsettings.cpp +++ b/src/plugins/beautifier/clangformat/clangformatsettings.cpp @@ -4,43 +4,80 @@ #include "clangformatsettings.h" #include "../beautifierconstants.h" +#include "../beautifierplugin.h" #include "../beautifiertr.h" - -#include -#include +#include "../configurationpanel.h" #include +#include +#include + +#include +#include +#include +#include +#include +#include + +using namespace Utils; + namespace Beautifier::Internal { -const char USE_PREDEFINED_STYLE[] = "usePredefinedStyle"; -const char PREDEFINED_STYLE[] = "predefinedStyle"; -const char FALLBACK_STYLE[] = "fallbackStyle"; -const char CUSTOM_STYLE[] = "customStyle"; const char SETTINGS_NAME[] = "clangformat"; -ClangFormatSettings::ClangFormatSettings() : - AbstractSettings(SETTINGS_NAME, ".clang-format") +ClangFormatSettings::ClangFormatSettings() + : AbstractSettings(SETTINGS_NAME, ".clang-format") { - setCommand("clang-format"); - m_settings.insert(USE_PREDEFINED_STYLE, QVariant(true)); - m_settings.insert(PREDEFINED_STYLE, "LLVM"); - m_settings.insert(FALLBACK_STYLE, "Default"); - m_settings.insert(CUSTOM_STYLE, QVariant()); - read(); -} + command.setDefaultValue("clang-format"); + command.setPromptDialogTitle(BeautifierPlugin::msgCommandPromptDialogTitle("Clang Format")); + command.setLabelText(Tr::tr("Clang Format command:")); -QString ClangFormatSettings::documentationFilePath() const -{ - return (Core::ICore::userResourcePath() / Beautifier::Constants::SETTINGS_DIRNAME - / Beautifier::Constants::DOCUMENTATION_DIRNAME / SETTINGS_NAME) - .stringAppended(".xml") - .toString(); + registerAspect(&usePredefinedStyle); + usePredefinedStyle.setSettingsKey("usePredefinedStyle"); + usePredefinedStyle.setLabelText(Tr::tr("Use predefined style:")); + usePredefinedStyle.setDefaultValue(true); + + registerAspect(&predefinedStyle); + predefinedStyle.setSettingsKey("predefinedStyle"); + predefinedStyle.setDisplayStyle(SelectionAspect::DisplayStyle::ComboBox); + predefinedStyle.addOption("LLVM"); + predefinedStyle.addOption("Google"); + predefinedStyle.addOption("Chromium"); + predefinedStyle.addOption("Mozilla"); + predefinedStyle.addOption("WebKit"); + predefinedStyle.addOption("File"); + predefinedStyle.setDefaultValue("LLVM"); + + registerAspect(&fallbackStyle); + fallbackStyle.setSettingsKey("fallbackStyle"); + fallbackStyle.setDisplayStyle(SelectionAspect::DisplayStyle::ComboBox); + fallbackStyle.addOption("Default"); + fallbackStyle.addOption("None"); + fallbackStyle.addOption("LLVM"); + fallbackStyle.addOption("Google"); + fallbackStyle.addOption("Chromium"); + fallbackStyle.addOption("Mozilla"); + fallbackStyle.addOption("WebKit"); + fallbackStyle.setDefaultValue("Default"); + + registerAspect(&predefinedStyle); + predefinedStyle.setSettingsKey("predefinedStyle"); + predefinedStyle.setDefaultValue("LLVM"); + + registerAspect(&customStyle); + customStyle.setSettingsKey("customStyle"); + + documentationFilePath.setFilePath(Core::ICore::userResourcePath(Constants::SETTINGS_DIRNAME) + .pathAppended(Constants::DOCUMENTATION_DIRNAME) + .pathAppended(SETTINGS_NAME).stringAppended(".xml")); + + read(); } void ClangFormatSettings::createDocumentationFile() const { - QFile file(documentationFilePath()); + QFile file(documentationFilePath().toFSPathString()); const QFileInfo fi(file); if (!fi.exists()) fi.dir().mkpath(fi.absolutePath()); @@ -144,60 +181,6 @@ QStringList ClangFormatSettings::completerWords() }; } -bool ClangFormatSettings::usePredefinedStyle() const -{ - return m_settings.value(USE_PREDEFINED_STYLE).toBool(); -} - -void ClangFormatSettings::setUsePredefinedStyle(bool usePredefinedStyle) -{ - m_settings.insert(USE_PREDEFINED_STYLE, QVariant(usePredefinedStyle)); -} - -QString ClangFormatSettings::predefinedStyle() const -{ - return m_settings.value(PREDEFINED_STYLE).toString(); -} - -void ClangFormatSettings::setPredefinedStyle(const QString &predefinedStyle) -{ - const QStringList test = predefinedStyles(); - if (test.contains(predefinedStyle)) - m_settings.insert(PREDEFINED_STYLE, QVariant(predefinedStyle)); -} - -QString ClangFormatSettings::fallbackStyle() const -{ - return m_settings.value(FALLBACK_STYLE).toString(); -} - -void ClangFormatSettings::setFallbackStyle(const QString &fallbackStyle) -{ - const QStringList test = fallbackStyles(); - if (test.contains(fallbackStyle)) - m_settings.insert(FALLBACK_STYLE, QVariant(fallbackStyle)); -} - -QString ClangFormatSettings::customStyle() const -{ - return m_settings.value(CUSTOM_STYLE).toString(); -} - -void ClangFormatSettings::setCustomStyle(const QString &customStyle) -{ - m_settings.insert(CUSTOM_STYLE, QVariant(customStyle)); -} - -QStringList ClangFormatSettings::predefinedStyles() const -{ - return {"LLVM", "Google", "Chromium", "Mozilla", "WebKit", "File"}; -} - -QStringList ClangFormatSettings::fallbackStyles() const -{ - return {"Default", "None", "LLVM", "Google", "Chromium", "Mozilla", "WebKit"}; -} - QString ClangFormatSettings::styleFileName(const QString &key) const { return m_styleDir.absolutePath() + '/' + key + '/' + m_ending; @@ -213,4 +196,86 @@ void ClangFormatSettings::readStyles() } } +class ClangFormatOptionsPageWidget : public Core::IOptionsPageWidget +{ +public: + explicit ClangFormatOptionsPageWidget(ClangFormatSettings *settings) + { + ClangFormatSettings &s = *settings; + QGroupBox *options = nullptr; + + auto predefinedStyleButton = new QRadioButton; + s.usePredefinedStyle.adoptButton(predefinedStyleButton); + + auto customizedStyleButton = new QRadioButton(Tr::tr("Use customized style:")); + + auto styleButtonGroup = new QButtonGroup; + styleButtonGroup->addButton(predefinedStyleButton); + styleButtonGroup->addButton(customizedStyleButton); + + auto configurations = new ConfigurationPanel(this); + configurations->setSettings(&s); + configurations->setCurrentConfiguration(s.customStyle()); + + using namespace Layouting; + + auto fallbackBlob = Row { noMargin, Tr::tr("Fallback style:"), s.fallbackStyle }.emerge(); + + auto predefinedBlob = Column { noMargin, s.predefinedStyle, fallbackBlob }.emerge(); + + Column { + Group { + title(Tr::tr("Configuration")), + Form { + s.command, br, + s.supportedMimeTypes + } + }, + Group { + title(Tr::tr("Options")), + bindTo(&options), + Form { + s.usePredefinedStyle, predefinedBlob, br, + customizedStyleButton, configurations, + }, + }, + st + }.attachTo(this); + + if (s.usePredefinedStyle.value()) + predefinedStyleButton->click(); + else + customizedStyleButton->click(); + + const auto updateEnabled = [&s, styleButtonGroup, predefinedBlob, fallbackBlob, + configurations, predefinedStyleButton] { + const bool predefSelected = styleButtonGroup->checkedButton() == predefinedStyleButton; + predefinedBlob->setEnabled(predefSelected); + fallbackBlob->setEnabled(predefSelected && s.predefinedStyle.volatileValue().toInt() == 5); // File + configurations->setEnabled(!predefSelected); + }; + updateEnabled(); + connect(styleButtonGroup, &QButtonGroup::buttonClicked, this, updateEnabled); + connect(&s.predefinedStyle, &SelectionAspect::volatileValueChanged, this, updateEnabled); + + setOnApply([settings, configurations] { + settings->customStyle.setValue(configurations->currentConfiguration()); + settings->save(); + }); + + s.read(); + + connect(s.command.pathChooser(), &PathChooser::validChanged, options, &QWidget::setEnabled); + options->setEnabled(s.command.pathChooser()->isValid()); + } +}; + +ClangFormatOptionsPage::ClangFormatOptionsPage(ClangFormatSettings *settings) +{ + setId("ClangFormat"); + setDisplayName(Tr::tr("Clang Format")); + setCategory(Constants::OPTION_CATEGORY); + setWidgetCreator([settings] { return new ClangFormatOptionsPageWidget(settings); }); +} + } // Beautifier::Internal diff --git a/src/plugins/beautifier/clangformat/clangformatsettings.h b/src/plugins/beautifier/clangformat/clangformatsettings.h index 310eeaf8926..22c16eafc3d 100644 --- a/src/plugins/beautifier/clangformat/clangformatsettings.h +++ b/src/plugins/beautifier/clangformat/clangformatsettings.h @@ -12,24 +12,14 @@ class ClangFormatSettings : public AbstractSettings public: explicit ClangFormatSettings(); - QString documentationFilePath() const override; void createDocumentationFile() const override; + QStringList completerWords() override; - bool usePredefinedStyle() const; - void setUsePredefinedStyle(bool usePredefinedStyle); - - QString predefinedStyle() const; - void setPredefinedStyle(const QString &predefinedStyle); - - QString fallbackStyle() const; - void setFallbackStyle(const QString &fallbackStyle); - - QString customStyle() const; - void setCustomStyle(const QString &customStyle); - - QStringList predefinedStyles() const; - QStringList fallbackStyles() const; + Utils::BoolAspect usePredefinedStyle; + Utils::SelectionAspect predefinedStyle; + Utils::SelectionAspect fallbackStyle; + Utils::StringAspect customStyle; QString styleFileName(const QString &key) const override; @@ -37,4 +27,10 @@ private: void readStyles() override; }; +class ClangFormatOptionsPage final : public Core::IOptionsPage +{ +public: + explicit ClangFormatOptionsPage(ClangFormatSettings *settings); +}; + } // Beautifier::Internal diff --git a/src/plugins/beautifier/uncrustify/uncrustify.cpp b/src/plugins/beautifier/uncrustify/uncrustify.cpp index 2e6f6d67d59..1439dc315f2 100644 --- a/src/plugins/beautifier/uncrustify/uncrustify.cpp +++ b/src/plugins/beautifier/uncrustify/uncrustify.cpp @@ -54,7 +54,7 @@ Uncrustify::Uncrustify() Core::ActionManager::actionContainer(Constants::MENU_ID)->addMenu(menu); - connect(&m_settings, &UncrustifySettings::supportedMimeTypesChanged, + connect(&m_settings.supportedMimeTypes, &Utils::BaseAspect::changed, this, [this] { updateActions(Core::EditorManager::currentEditor()); }); } diff --git a/src/plugins/beautifier/uncrustify/uncrustify.h b/src/plugins/beautifier/uncrustify/uncrustify.h index 1261576274c..685a29c25a1 100644 --- a/src/plugins/beautifier/uncrustify/uncrustify.h +++ b/src/plugins/beautifier/uncrustify/uncrustify.h @@ -5,7 +5,6 @@ #include "../beautifierabstracttool.h" -#include "uncrustifyoptionspage.h" #include "uncrustifysettings.h" namespace Beautifier::Internal { diff --git a/src/plugins/beautifier/uncrustify/uncrustifyoptionspage.cpp b/src/plugins/beautifier/uncrustify/uncrustifyoptionspage.cpp deleted file mode 100644 index 8da74a71973..00000000000 --- a/src/plugins/beautifier/uncrustify/uncrustifyoptionspage.cpp +++ /dev/null @@ -1,137 +0,0 @@ -// Copyright (C) 2016 Lorenz Haas -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "uncrustifyoptionspage.h" - -#include "uncrustifyconstants.h" -#include "uncrustifysettings.h" - -#include "../beautifierconstants.h" -#include "../beautifierplugin.h" -#include "../beautifiertr.h" -#include "../configurationpanel.h" - -#include -#include - -#include -#include -#include -#include - -namespace Beautifier::Internal { - -class UncrustifyOptionsPageWidget : public Core::IOptionsPageWidget -{ -public: - explicit UncrustifyOptionsPageWidget(UncrustifySettings *settings); - - void apply() final; - -private: - UncrustifySettings *m_settings; - - Utils::PathChooser *m_command; - QLineEdit *m_mime; - QCheckBox *m_useOtherFiles; - QCheckBox *m_useSpecificFile; - Utils::PathChooser *m_uncrusifyFilePath; - QCheckBox *m_useHomeFile; - QCheckBox *m_useCustomStyle; - ConfigurationPanel *m_configurations; - QCheckBox *m_formatEntireFileFallback; -}; - -UncrustifyOptionsPageWidget::UncrustifyOptionsPageWidget(UncrustifySettings *settings) - : m_settings(settings) -{ - m_command = new Utils::PathChooser; - - m_mime = new QLineEdit(m_settings->supportedMimeTypesAsString()); - - m_useOtherFiles = new QCheckBox(Tr::tr("Use file uncrustify.cfg defined in project files")); - m_useOtherFiles->setChecked(m_settings->useOtherFiles()); - - m_useSpecificFile = new QCheckBox(Tr::tr("Use file specific uncrustify.cfg")); - m_useSpecificFile->setChecked(m_settings->useSpecificConfigFile()); - - m_uncrusifyFilePath = new Utils::PathChooser; - m_uncrusifyFilePath->setExpectedKind(Utils::PathChooser::File); - m_uncrusifyFilePath->setPromptDialogFilter(Tr::tr("Uncrustify file (*.cfg)")); - m_uncrusifyFilePath->setFilePath(m_settings->specificConfigFile()); - - m_useHomeFile = new QCheckBox(Tr::tr("Use file uncrustify.cfg in HOME") - .replace( "HOME", QDir::toNativeSeparators(QDir::home().absolutePath()))); - m_useHomeFile->setChecked(m_settings->useHomeFile()); - - m_useCustomStyle = new QCheckBox(Tr::tr("Use customized style:")); - m_useCustomStyle->setChecked(m_settings->useCustomStyle()); - - m_configurations = new ConfigurationPanel; - m_configurations->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); - m_configurations->setSettings(m_settings); - m_configurations->setCurrentConfiguration(m_settings->customStyle()); - - m_formatEntireFileFallback = new QCheckBox(Tr::tr("Format entire file if no text was selected")); - m_formatEntireFileFallback->setToolTip(Tr::tr("For action Format Selected Text")); - m_formatEntireFileFallback->setChecked(m_settings->formatEntireFileFallback()); - - m_command->setExpectedKind(Utils::PathChooser::ExistingCommand); - m_command->setCommandVersionArguments({"--version"}); - m_command->setPromptDialogTitle(BeautifierPlugin::msgCommandPromptDialogTitle( - Tr::tr(Constants::UNCRUSTIFY_DISPLAY_NAME))); - m_command->setFilePath(m_settings->command()); - - auto options = new QGroupBox(Tr::tr("Options")); - - using namespace Layouting; - - Column { - m_useOtherFiles, - Row { m_useSpecificFile, m_uncrusifyFilePath }, - m_useHomeFile, - Row { m_useCustomStyle, m_configurations }, - m_formatEntireFileFallback - }.attachTo(options); - - Column { - Group { - title(Tr::tr("Configuration")), - Form { - Tr::tr("Uncrustify command:"), m_command, br, - Tr::tr("Restrict to MIME types:"), m_mime - } - }, - options, - st - }.attachTo(this); - - connect(m_command, &Utils::PathChooser::validChanged, options, &QWidget::setEnabled); -} - -void UncrustifyOptionsPageWidget::apply() -{ - m_settings->setCommand(m_command->filePath()); - m_settings->setSupportedMimeTypes(m_mime->text()); - m_settings->setUseOtherFiles(m_useOtherFiles->isChecked()); - m_settings->setUseHomeFile(m_useHomeFile->isChecked()); - m_settings->setUseSpecificConfigFile(m_useSpecificFile->isChecked()); - m_settings->setSpecificConfigFile(m_uncrusifyFilePath->filePath()); - m_settings->setUseCustomStyle(m_useCustomStyle->isChecked()); - m_settings->setCustomStyle(m_configurations->currentConfiguration()); - m_settings->setFormatEntireFileFallback(m_formatEntireFileFallback->isChecked()); - m_settings->save(); - - // update since not all MIME types are accepted (invalids or duplicates) - m_mime->setText(m_settings->supportedMimeTypesAsString()); -} - -UncrustifyOptionsPage::UncrustifyOptionsPage(UncrustifySettings *settings) -{ - setId("Uncrustify"); - setDisplayName(Tr::tr("Uncrustify")); - setCategory(Constants::OPTION_CATEGORY); - setWidgetCreator([settings] { return new UncrustifyOptionsPageWidget(settings); }); -} - -} // Beautifier::Internal diff --git a/src/plugins/beautifier/uncrustify/uncrustifyoptionspage.h b/src/plugins/beautifier/uncrustify/uncrustifyoptionspage.h deleted file mode 100644 index a8512d0da62..00000000000 --- a/src/plugins/beautifier/uncrustify/uncrustifyoptionspage.h +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright (C) 2016 Lorenz Haas -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include - -namespace Beautifier::Internal { - -class UncrustifySettings; - -class UncrustifyOptionsPage final : public Core::IOptionsPage -{ -public: - explicit UncrustifyOptionsPage(UncrustifySettings *settings); -}; - -} // Beautifier::Internal diff --git a/src/plugins/beautifier/uncrustify/uncrustifysettings.cpp b/src/plugins/beautifier/uncrustify/uncrustifysettings.cpp index a284d780b6c..8fefa851d0c 100644 --- a/src/plugins/beautifier/uncrustify/uncrustifysettings.cpp +++ b/src/plugins/beautifier/uncrustify/uncrustifysettings.cpp @@ -3,14 +3,25 @@ #include "uncrustifysettings.h" +#include "uncrustifyconstants.h" #include "../beautifierconstants.h" +#include "../beautifierplugin.h" +#include "../beautifiertr.h" +#include "../configurationpanel.h" #include + +#include +#include #include +#include #include #include #include +#include +#include +#include #include #include @@ -18,110 +29,57 @@ using namespace Utils; namespace Beautifier::Internal { -const char USE_OTHER_FILES[] = "useOtherFiles"; -const char USE_HOME_FILE[] = "useHomeFile"; -const char USE_SPECIFIC_CONFIG_FILE_PATH[] = "useSpecificConfigFile"; -const char SPECIFIC_CONFIG_FILE_PATH[] = "specificConfigFile"; -const char USE_CUSTOM_STYLE[] = "useCustomStyle"; -const char CUSTOM_STYLE[] = "customStyle"; -const char FORMAT_ENTIRE_FILE_FALLBACK[] = "formatEntireFileFallback"; -const char SETTINGS_NAME[] = "uncrustify"; +const char SETTINGS_NAME[] = "uncrustify"; -UncrustifySettings::UncrustifySettings() : - AbstractSettings(SETTINGS_NAME, ".cfg") +UncrustifySettings::UncrustifySettings() + : AbstractSettings(SETTINGS_NAME, ".cfg") { setVersionRegExp(QRegularExpression("([0-9]{1})\\.([0-9]{2})")); - setCommand("uncrustify"); - m_settings.insert(USE_OTHER_FILES, QVariant(true)); - m_settings.insert(USE_HOME_FILE, QVariant(false)); - m_settings.insert(USE_CUSTOM_STYLE, QVariant(false)); - m_settings.insert(USE_SPECIFIC_CONFIG_FILE_PATH, QVariant(false)); - m_settings.insert(CUSTOM_STYLE, QVariant()); - m_settings.insert(FORMAT_ENTIRE_FILE_FALLBACK, QVariant(true)); - m_settings.insert(SPECIFIC_CONFIG_FILE_PATH, QVariant()); + + command.setDefaultValue("uncrustify"); + command.setLabelText(Tr::tr("Uncrustify command:")); + command.setPromptDialogTitle(BeautifierPlugin::msgCommandPromptDialogTitle( + Tr::tr(Constants::UNCRUSTIFY_DISPLAY_NAME))); + + registerAspect(&useOtherFiles); + useOtherFiles.setSettingsKey("useOtherFiles"); + useOtherFiles.setDefaultValue(true); + useOtherFiles.setLabelText(Tr::tr("Use file uncrustify.cfg defined in project files")); + + registerAspect(&useHomeFile); + useHomeFile.setSettingsKey("useHomeFile"); + useHomeFile.setLabelText(Tr::tr("Use file uncrustify.cfg in HOME") + .replace( "HOME", QDir::toNativeSeparators(QDir::home().absolutePath()))); + + registerAspect(&useCustomStyle); + useCustomStyle.setSettingsKey("useCustomStyle"); + useCustomStyle.setLabelText(Tr::tr("Use customized style:")); + + registerAspect(&useSpecificConfigFile); + useSpecificConfigFile.setSettingsKey("useSpecificConfigFile"); + useSpecificConfigFile.setLabelText(Tr::tr("Use file specific uncrustify.cfg")); + + registerAspect(&customStyle); + customStyle.setSettingsKey("customStyle"); + + registerAspect(&formatEntireFileFallback); + formatEntireFileFallback.setSettingsKey("formatEntireFileFallback"); + formatEntireFileFallback.setDefaultValue(true); + formatEntireFileFallback.setLabelText(Tr::tr("Format entire file if no text was selected")); + formatEntireFileFallback.setToolTip(Tr::tr("For action Format Selected Text")); + + registerAspect(&specificConfigFile); + specificConfigFile.setSettingsKey("specificConfigFile"); + specificConfigFile.setExpectedKind(Utils::PathChooser::File); + specificConfigFile.setPromptDialogFilter(Tr::tr("Uncrustify file (*.cfg)")); + + documentationFilePath.setFilePath(Core::ICore::userResourcePath(Constants::SETTINGS_DIRNAME) + .pathAppended(Constants::DOCUMENTATION_DIRNAME) + .pathAppended(SETTINGS_NAME).stringAppended(".xml")); + read(); } -UncrustifySettings::~UncrustifySettings() = default; - -bool UncrustifySettings::useOtherFiles() const -{ - return m_settings.value(USE_OTHER_FILES).toBool(); -} - -void UncrustifySettings::setUseOtherFiles(bool useOtherFiles) -{ - m_settings.insert(USE_OTHER_FILES, QVariant(useOtherFiles)); -} - -bool UncrustifySettings::useHomeFile() const -{ - return m_settings.value(USE_HOME_FILE).toBool(); -} - -void UncrustifySettings::setUseHomeFile(bool useHomeFile) -{ - m_settings.insert(USE_HOME_FILE, QVariant(useHomeFile)); -} - -FilePath UncrustifySettings::specificConfigFile() const -{ - return FilePath::fromString(m_settings.value(SPECIFIC_CONFIG_FILE_PATH).toString()); -} - -void UncrustifySettings::setSpecificConfigFile(const FilePath &filePath) -{ - m_settings.insert(SPECIFIC_CONFIG_FILE_PATH, QVariant(filePath.toString())); -} - -bool UncrustifySettings::useSpecificConfigFile() const -{ - return m_settings.value(USE_SPECIFIC_CONFIG_FILE_PATH).toBool(); -} - -void UncrustifySettings::setUseSpecificConfigFile(bool useConfigFile) -{ - m_settings.insert(USE_SPECIFIC_CONFIG_FILE_PATH, QVariant(useConfigFile)); -} - -bool UncrustifySettings::useCustomStyle() const -{ - return m_settings.value(USE_CUSTOM_STYLE).toBool(); -} - -void UncrustifySettings::setUseCustomStyle(bool useCustomStyle) -{ - m_settings.insert(USE_CUSTOM_STYLE, QVariant(useCustomStyle)); -} - -QString UncrustifySettings::customStyle() const -{ - return m_settings.value(CUSTOM_STYLE).toString(); -} - -void UncrustifySettings::setCustomStyle(const QString &customStyle) -{ - m_settings.insert(CUSTOM_STYLE, QVariant(customStyle)); -} - -bool UncrustifySettings::formatEntireFileFallback() const -{ - return m_settings.value(FORMAT_ENTIRE_FILE_FALLBACK).toBool(); -} - -void UncrustifySettings::setFormatEntireFileFallback(bool formatEntireFileFallback) -{ - m_settings.insert(FORMAT_ENTIRE_FILE_FALLBACK, QVariant(formatEntireFileFallback)); -} - -QString UncrustifySettings::documentationFilePath() const -{ - return (Core::ICore::userResourcePath() / Beautifier::Constants::SETTINGS_DIRNAME - / Beautifier::Constants::DOCUMENTATION_DIRNAME / SETTINGS_NAME) - .stringAppended(".xml") - .toString(); -} - void UncrustifySettings::createDocumentationFile() const { Process process; @@ -131,7 +89,7 @@ void UncrustifySettings::createDocumentationFile() const if (process.result() != ProcessResult::FinishedWithSuccess) return; - QFile file(documentationFilePath()); + QFile file(documentationFilePath().toFSPathString()); const QFileInfo fi(file); if (!fi.exists()) fi.dir().mkpath(fi.absolutePath()); @@ -185,4 +143,62 @@ void UncrustifySettings::createDocumentationFile() const } } +class UncrustifyOptionsPageWidget : public Core::IOptionsPageWidget +{ +public: + explicit UncrustifyOptionsPageWidget(UncrustifySettings *settings) + { + UncrustifySettings &s = *settings; + + auto configurations = new ConfigurationPanel(this); + configurations->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); + configurations->setSettings(settings); + configurations->setCurrentConfiguration(settings->customStyle()); + + QGroupBox *options = nullptr; + + using namespace Layouting; + + Column { + Group { + title(Tr::tr("Configuration")), + Form { + s.command, br, + s.supportedMimeTypes, + } + }, + Group { + title(Tr::tr("Options")), + bindTo(&options), + Column { + s.useOtherFiles, + Row { s.useSpecificConfigFile, s.specificConfigFile }, + s.useHomeFile, + Row { s.useCustomStyle, configurations }, + s.formatEntireFileFallback + }, + }, + st + }.attachTo(this); + + s.read(); + + connect(s.command.pathChooser(), &PathChooser::validChanged, options, &QWidget::setEnabled); + options->setEnabled(s.command.pathChooser()->isValid()); + + setOnApply([&s, configurations] { + s.customStyle.setValue(configurations->currentConfiguration()); + s.save(); + }); + } +}; + +UncrustifyOptionsPage::UncrustifyOptionsPage(UncrustifySettings *settings) +{ + setId("Uncrustify"); + setDisplayName(Tr::tr("Uncrustify")); + setCategory(Constants::OPTION_CATEGORY); + setWidgetCreator([settings] { return new UncrustifyOptionsPageWidget(settings); }); +} + } // Beautifier::Internal diff --git a/src/plugins/beautifier/uncrustify/uncrustifysettings.h b/src/plugins/beautifier/uncrustify/uncrustifysettings.h index d57bfe6d39e..c0de1b891b2 100644 --- a/src/plugins/beautifier/uncrustify/uncrustifysettings.h +++ b/src/plugins/beautifier/uncrustify/uncrustifysettings.h @@ -5,41 +5,30 @@ #include "../abstractsettings.h" -namespace Beautifier { -namespace Internal { +namespace Beautifier::Internal { class UncrustifySettings : public AbstractSettings { - Q_OBJECT - public: UncrustifySettings(); - ~UncrustifySettings() override; - bool useOtherFiles() const; - void setUseOtherFiles(bool useOtherFiles); - - bool useHomeFile() const; - void setUseHomeFile(bool useHomeFile); - - bool useCustomStyle() const; - void setUseCustomStyle(bool useCustomStyle); - - QString customStyle() const; - void setCustomStyle(const QString &customStyle); - - bool formatEntireFileFallback() const; - void setFormatEntireFileFallback(bool formatEntireFileFallback); - - QString documentationFilePath() const override; void createDocumentationFile() const override; - Utils::FilePath specificConfigFile() const; - void setSpecificConfigFile(const Utils::FilePath &filePath); + Utils::BoolAspect useOtherFiles; + Utils::BoolAspect useHomeFile; + Utils::BoolAspect useCustomStyle; - bool useSpecificConfigFile() const; - void setUseSpecificConfigFile(bool useConfigFile); + Utils::StringAspect customStyle; + Utils::BoolAspect formatEntireFileFallback; + + Utils::FilePathAspect specificConfigFile; + Utils::BoolAspect useSpecificConfigFile; }; -} // namespace Internal -} // namespace Beautifier +class UncrustifyOptionsPage final : public Core::IOptionsPage +{ +public: + explicit UncrustifyOptionsPage(UncrustifySettings *settings); +}; + +} // Beautifier::Internal From 5eba056b7311d463c07c645ad8b81b54d4add166 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Thu, 4 May 2023 19:01:58 +0200 Subject: [PATCH 185/192] QmlDesigner: Add callbacks for model resource management As a node or property is removed there are now callbacks to generate node, properties and expressions which should removed or adapt too. Task-number: QDS-9766 Change-Id: I6d842006a6282af00ff644ffaa0f3102e14f13fa Reviewed-by: Thomas Hartmann Reviewed-by: --- src/plugins/qmldesigner/CMakeLists.txt | 1 + .../qmldesigner/designercore/include/model.h | 28 +- .../designercore/include/modelnode.h | 2 + .../designercore/metainfo/nodemetainfo.cpp | 12 +- .../designercore/model/abstractproperty.cpp | 1 - .../designercore/model/abstractview.cpp | 8 +- .../designercore/model/bindingproperty.cpp | 4 +- .../qmldesigner/designercore/model/model.cpp | 73 ++- .../qmldesigner/designercore/model/model_p.h | 14 +- .../designercore/model/modelnode.cpp | 6 +- .../model/modelresourcemanagementinterface.h | 39 ++ .../model/nodeabstractproperty.cpp | 2 +- .../designercore/model/nodeproperty.cpp | 2 +- .../model/signalhandlerproperty.cpp | 4 +- .../designercore/model/variantproperty.cpp | 4 +- .../projectstorage/projectstorage.h | 29 +- .../projectstorage/projectstorageinterface.h | 1 - tests/unit/unittest/CMakeLists.txt | 4 + tests/unit/unittest/listmodeleditor-test.cpp | 42 +- tests/unit/unittest/mocklistmodeleditorview.h | 16 + tests/unit/unittest/model-test.cpp | 491 ++++++++++++++++++ .../unittest/modelresourcemanagementmock.h | 42 ++ tests/unit/unittest/nodelistproperty-test.cpp | 10 +- tests/unit/unittest/projectstoragemock.cpp | 89 ++++ tests/unit/unittest/projectstoragemock.h | 10 +- 25 files changed, 835 insertions(+), 99 deletions(-) create mode 100644 src/plugins/qmldesigner/designercore/model/modelresourcemanagementinterface.h create mode 100644 tests/unit/unittest/model-test.cpp create mode 100644 tests/unit/unittest/modelresourcemanagementmock.h create mode 100644 tests/unit/unittest/projectstoragemock.cpp diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index c7989fda36d..340cbd77a1a 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -341,6 +341,7 @@ extend_qtc_library(QmlDesignerCore modelnodepositionrecalculator.cpp modelnodepositionrecalculator.h modelnodepositionstorage.cpp + modelresourcemanagementinterface.h modeltotextmerger.cpp modeltotextmerger.h modelutils.cpp diff --git a/src/plugins/qmldesigner/designercore/include/model.h b/src/plugins/qmldesigner/designercore/include/model.h index 95295fed6a0..c2cd807a8a2 100644 --- a/src/plugins/qmldesigner/designercore/include/model.h +++ b/src/plugins/qmldesigner/designercore/include/model.h @@ -6,6 +6,7 @@ #include #include +#include #include #include @@ -40,7 +41,7 @@ class RewriterView; class NodeInstanceView; class TextModifier; -using PropertyListType = QList >; +using PropertyListType = QList>; class QMLDESIGNERCORE_EXPORT Model : public QObject { @@ -61,25 +62,34 @@ public: const TypeName &type, int major = 1, int minor = 1, - Model *metaInfoProxyModel = nullptr); - Model(const TypeName &typeName, int major = 1, int minor = 1, Model *metaInfoProxyModel = nullptr); + Model *metaInfoProxyModel = nullptr, + std::unique_ptr resourceManagement = {}); + Model(const TypeName &typeName, + int major = 1, + int minor = 1, + Model *metaInfoProxyModel = nullptr, + std::unique_ptr resourceManagement = {}); ~Model(); static ModelPointer create(const TypeName &typeName, int major = 1, int minor = 1, - Model *metaInfoProxyModel = nullptr) + Model *metaInfoProxyModel = nullptr, + std::unique_ptr resourceManagement = {}) { - return ModelPointer(new Model(typeName, major, minor, metaInfoProxyModel)); + return ModelPointer( + new Model(typeName, major, minor, metaInfoProxyModel, std::move(resourceManagement))); } static ModelPointer create(ProjectStorageType &projectStorage, const TypeName &typeName, int major = 1, - int minor = 1) + int minor = 1, + std::unique_ptr resourceManagement = {}) { - return ModelPointer(new Model(projectStorage, typeName, major, minor)); + return ModelPointer( + new Model(projectStorage, typeName, major, minor, nullptr, std::move(resourceManagement))); } QUrl fileUrl() const; @@ -120,6 +130,8 @@ public: void attachView(AbstractView *view); void detachView(AbstractView *view, ViewNotification emitDetachNotify = NotifyView); + QList allModelNodes() const; + // Editing sub-components: // Imports: @@ -177,4 +189,4 @@ private: std::unique_ptr d; }; -} +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/include/modelnode.h b/src/plugins/qmldesigner/designercore/include/modelnode.h index 685b900cb0b..e9891da620c 100644 --- a/src/plugins/qmldesigner/designercore/include/modelnode.h +++ b/src/plugins/qmldesigner/designercore/include/modelnode.h @@ -270,6 +270,8 @@ QMLDESIGNERCORE_EXPORT bool operator !=(const ModelNode &firstNode, const ModelN QMLDESIGNERCORE_EXPORT bool operator <(const ModelNode &firstNode, const ModelNode &secondNode); QMLDESIGNERCORE_EXPORT QDebug operator<<(QDebug debug, const ModelNode &modelNode); QMLDESIGNERCORE_EXPORT QTextStream& operator<<(QTextStream &stream, const ModelNode &modelNode); + +using ModelNodes = QList; } Q_DECLARE_METATYPE(QmlDesigner::ModelNode) diff --git a/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp b/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp index 443abf3f07e..bdb7b63392e 100644 --- a/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp +++ b/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp @@ -1619,16 +1619,20 @@ TypeName NodeMetaInfo::simplifiedTypeName() const int NodeMetaInfo::majorVersion() const { - if (isValid()) - return m_privateData->majorVersion(); + if constexpr (!useProjectStorage()) { + if (isValid()) + return m_privateData->majorVersion(); + } return -1; } int NodeMetaInfo::minorVersion() const { - if (isValid()) - return m_privateData->minorVersion(); + if constexpr (!useProjectStorage()) { + if (isValid()) + return m_privateData->minorVersion(); + } return -1; } diff --git a/src/plugins/qmldesigner/designercore/model/abstractproperty.cpp b/src/plugins/qmldesigner/designercore/model/abstractproperty.cpp index 3bccf54660c..00541e12e2f 100644 --- a/src/plugins/qmldesigner/designercore/model/abstractproperty.cpp +++ b/src/plugins/qmldesigner/designercore/model/abstractproperty.cpp @@ -39,7 +39,6 @@ AbstractProperty::AbstractProperty(const Internal::InternalPropertyPointer &prop m_model(model), m_view(view) { - Q_ASSERT(!m_model || m_view); } AbstractProperty::AbstractProperty(const AbstractProperty &property, AbstractView *view) diff --git a/src/plugins/qmldesigner/designercore/model/abstractview.cpp b/src/plugins/qmldesigner/designercore/model/abstractview.cpp index a09db03dacd..9967d49ee7b 100644 --- a/src/plugins/qmldesigner/designercore/model/abstractview.cpp +++ b/src/plugins/qmldesigner/designercore/model/abstractview.cpp @@ -64,8 +64,12 @@ RewriterTransaction AbstractView::beginRewriterTransaction(const QByteArray &ide ModelNode AbstractView::createModelNode(const TypeName &typeName) { - const NodeMetaInfo metaInfo = model()->metaInfo(typeName); - return createModelNode(typeName, metaInfo.majorVersion(), metaInfo.minorVersion()); + if constexpr (useProjectStorage()) { + return createModelNode(typeName, -1, -1); + } else { + const NodeMetaInfo metaInfo = model()->metaInfo(typeName); + return createModelNode(typeName, metaInfo.majorVersion(), metaInfo.minorVersion()); + } } ModelNode AbstractView::createModelNode(const TypeName &typeName, diff --git a/src/plugins/qmldesigner/designercore/model/bindingproperty.cpp b/src/plugins/qmldesigner/designercore/model/bindingproperty.cpp index 77dbec5e25a..45d4e4cf20e 100644 --- a/src/plugins/qmldesigner/designercore/model/bindingproperty.cpp +++ b/src/plugins/qmldesigner/designercore/model/bindingproperty.cpp @@ -56,7 +56,7 @@ void BindingProperty::setExpression(const QString &expression) } if (internalNode()->hasProperty(name()) && !internalNode()->property(name())->isBindingProperty()) - privateModel()->removeProperty(internalNode()->property(name())); + privateModel()->removePropertyAndRelatedResources(internalNode()->property(name())); privateModel()->setBindingProperty(internalNode(), name(), expression); } @@ -341,7 +341,7 @@ void BindingProperty::setDynamicTypeNameAndExpression(const TypeName &typeName, } if (internalNode()->hasProperty(name()) && !internalNode()->property(name())->isBindingProperty()) - privateModel()->removeProperty(internalNode()->property(name())); + privateModel()->removePropertyAndRelatedResources(internalNode()->property(name())); privateModel()->setDynamicBindingProperty(internalNode(), name(), typeName, expression); } diff --git a/src/plugins/qmldesigner/designercore/model/model.cpp b/src/plugins/qmldesigner/designercore/model/model.cpp index fcdb66b8029..844454ae55e 100644 --- a/src/plugins/qmldesigner/designercore/model/model.cpp +++ b/src/plugins/qmldesigner/designercore/model/model.cpp @@ -62,9 +62,11 @@ ModelPrivate::ModelPrivate(Model *model, const TypeName &typeName, int major, int minor, - Model *metaInfoProxyModel) + Model *metaInfoProxyModel, + std::unique_ptr resourceManagement) : projectStorage{&projectStorage} , m_model{model} + , m_resourceManagement{std::move(resourceManagement)} { m_metaInfoProxyModel = metaInfoProxyModel; @@ -75,9 +77,14 @@ ModelPrivate::ModelPrivate(Model *model, m_currentTimelineNode = m_rootInternalNode; } -ModelPrivate::ModelPrivate( - Model *model, const TypeName &typeName, int major, int minor, Model *metaInfoProxyModel) +ModelPrivate::ModelPrivate(Model *model, + const TypeName &typeName, + int major, + int minor, + Model *metaInfoProxyModel, + std::unique_ptr resourceManagement) : m_model(model) + , m_resourceManagement{std::move(resourceManagement)} { m_metaInfoProxyModel = metaInfoProxyModel; @@ -279,7 +286,9 @@ InternalNodePointer ModelPrivate::createNode(const TypeName &typeName, notifyNodeCreated(newNode); if (!newNode->propertyNameList().isEmpty()) - notifyVariantPropertiesChanged(newNode, newNode->propertyNameList(), AbstractView::PropertiesAdded); + notifyVariantPropertiesChanged(newNode, + newNode->propertyNameList(), + AbstractView::PropertiesAdded); return newNode; } @@ -303,16 +312,40 @@ EnabledViewRange ModelPrivate::enabledViews() const return EnabledViewRange{m_viewList}; } +void ModelPrivate::handleResourceSet(const ModelResourceSet &resourceSet) +{ + for (const ModelNode &node : resourceSet.removeModelNodes) { + if (node) + removeNode(node.m_internalNode); + } + + for (const AbstractProperty &property : resourceSet.removeProperties) { + if (property) + removeProperty(property.m_internalNode->property(property.m_propertyName)); + } + + for (const auto &[property, expression] : resourceSet.setExpressions) { + if (property) + setBindingProperty(property.m_internalNode, property.m_propertyName, expression); + } +} + void ModelPrivate::removeAllSubNodes(const InternalNodePointer &node) { for (const InternalNodePointer &subNode : node->allSubNodes()) removeNodeFromModel(subNode); } +void ModelPrivate::removeNodeAndRelatedResources(const InternalNodePointer &node) +{ + if (m_resourceManagement) + handleResourceSet(m_resourceManagement->removeNode(ModelNode{node, m_model, nullptr})); + else + removeNode(node); +} + void ModelPrivate::removeNode(const InternalNodePointer &node) { - Q_ASSERT(node); - AbstractView::PropertyChangeFlags propertyChangeFlags = AbstractView::NoAdditionalChanges; notifyNodeAboutToBeRemoved(node); @@ -1088,6 +1121,15 @@ static QList toPropertyPairList(const QListremoveProperty(AbstractProperty{property, m_model, nullptr})); + else + removeProperty(property); +} + void ModelPrivate::removeProperty(const InternalPropertyPointer &property) { notifyPropertiesAboutToBeRemoved({property}); @@ -1408,13 +1450,19 @@ Model::Model(ProjectStorageType &projectStorage, const TypeName &typeName, int major, int minor, - Model *metaInfoProxyModel) + Model *metaInfoProxyModel, + std::unique_ptr resourceManagement) : d(std::make_unique( - this, projectStorage, typeName, major, minor, metaInfoProxyModel)) + this, projectStorage, typeName, major, minor, metaInfoProxyModel, std::move(resourceManagement))) {} -Model::Model(const TypeName &typeName, int major, int minor, Model *metaInfoProxyModel) - : d(std::make_unique(this, typeName, major, minor, metaInfoProxyModel)) +Model::Model(const TypeName &typeName, + int major, + int minor, + Model *metaInfoProxyModel, + std::unique_ptr resourceManagement) + : d(std::make_unique( + this, typeName, major, minor, metaInfoProxyModel, std::move(resourceManagement))) {} Model::~Model() = default; @@ -2156,4 +2204,9 @@ void Model::detachView(AbstractView *view, ViewNotification emitDetachNotify) d->detachView(view, emitNotify); } +QList Model::allModelNodes() const +{ + return QmlDesigner::toModelNodeList(d->allNodes(), nullptr); +} + } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/model/model_p.h b/src/plugins/qmldesigner/designercore/model/model_p.h index ce2a49fc42d..b802d703f25 100644 --- a/src/plugins/qmldesigner/designercore/model/model_p.h +++ b/src/plugins/qmldesigner/designercore/model/model_p.h @@ -101,8 +101,14 @@ public: const TypeName &type, int major, int minor, - Model *metaInfoProxyModel); - ModelPrivate(Model *model, const TypeName &type, int major, int minor, Model *metaInfoProxyModel); + Model *metaInfoProxyModel, + std::unique_ptr resourceManagement); + ModelPrivate(Model *model, + const TypeName &type, + int major, + int minor, + Model *metaInfoProxyModel, + std::unique_ptr resourceManagement); ~ModelPrivate() override; @@ -120,6 +126,7 @@ public: bool isRootNode = false); /*factory methods for internal use in model and rewriter*/ + void removeNodeAndRelatedResources(const InternalNodePointer &node); void removeNode(const InternalNodePointer &node); void changeNodeId(const InternalNodePointer &node, const QString &id); void changeNodeType(const InternalNodePointer &node, const TypeName &typeName, int majorVersion, int minorVersion); @@ -235,6 +242,7 @@ public: //node state property manipulation void addProperty(const InternalNodePointer &node, const PropertyName &name); void setPropertyValue(const InternalNodePointer &node,const PropertyName &name, const QVariant &value); + void removePropertyAndRelatedResources(const InternalPropertyPointer &property); void removeProperty(const InternalPropertyPointer &property); void setBindingProperty(const InternalNodePointer &node, const PropertyName &name, const QString &expression); @@ -282,6 +290,7 @@ private: QVector toModelNodeVector(const QVector &nodeVector, AbstractView *view) const; QVector toInternalNodeVector(const QVector &modelNodeVector) const; EnabledViewRange enabledViews() const; + void handleResourceSet(const ModelResourceSet &resourceSet); public: NotNullPointer projectStorage = nullptr; @@ -300,6 +309,7 @@ private: InternalNodePointer m_currentStateNode; InternalNodePointer m_rootInternalNode; InternalNodePointer m_currentTimelineNode; + std::unique_ptr m_resourceManagement; QUrl m_fileUrl; QPointer m_rewriterView; QPointer m_nodeInstanceView; diff --git a/src/plugins/qmldesigner/designercore/model/modelnode.cpp b/src/plugins/qmldesigner/designercore/model/modelnode.cpp index fd3c59fa1f1..7e64f4c4bf4 100644 --- a/src/plugins/qmldesigner/designercore/model/modelnode.cpp +++ b/src/plugins/qmldesigner/designercore/model/modelnode.cpp @@ -290,7 +290,7 @@ A node might become invalid if e.g. it or one of its ancestors is deleted. */ bool ModelNode::isValid() const { - return !m_model.isNull() && !m_view.isNull() && m_internalNode && m_internalNode->isValid; + return !m_model.isNull() && m_internalNode && m_internalNode->isValid; } /*! @@ -652,7 +652,7 @@ void ModelNode::removeProperty(const PropertyName &name) const return; if (m_internalNode->hasProperty(name)) - model()->d->removeProperty(m_internalNode->property(name)); + model()->d->removePropertyAndRelatedResources(m_internalNode->property(name)); } /*! \brief removes this node from the node tree @@ -692,7 +692,7 @@ void ModelNode::destroy() return; removeModelNodeFromSelection(*this); - model()->d->removeNode(m_internalNode); + model()->d->removeNodeAndRelatedResources(m_internalNode); } //\} diff --git a/src/plugins/qmldesigner/designercore/model/modelresourcemanagementinterface.h b/src/plugins/qmldesigner/designercore/model/modelresourcemanagementinterface.h new file mode 100644 index 00000000000..b2188132d6a --- /dev/null +++ b/src/plugins/qmldesigner/designercore/model/modelresourcemanagementinterface.h @@ -0,0 +1,39 @@ +// 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 { + +struct ModelResourceSet +{ + struct SetExpression + { + BindingProperty property; + QString expression; + }; + + QList removeModelNodes; + QList removeProperties; + QList setExpressions; +}; + +class QMLDESIGNERCORE_EXPORT ModelResourceManagementInterface +{ +public: + ModelResourceManagementInterface() = default; + virtual ~ModelResourceManagementInterface() = default; + + ModelResourceManagementInterface(const ModelResourceManagementInterface &) = delete; + ModelResourceManagementInterface &operator=(const ModelResourceManagementInterface &) = delete; + ModelResourceManagementInterface(ModelResourceManagementInterface &&) = default; + ModelResourceManagementInterface &operator=(ModelResourceManagementInterface &&) = default; + + virtual ModelResourceSet removeNode(const ModelNode &node) const = 0; + virtual ModelResourceSet removeProperty(const AbstractProperty &property) const = 0; +}; +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/model/nodeabstractproperty.cpp b/src/plugins/qmldesigner/designercore/model/nodeabstractproperty.cpp index e2ef9d76f06..60b4e77dc6d 100644 --- a/src/plugins/qmldesigner/designercore/model/nodeabstractproperty.cpp +++ b/src/plugins/qmldesigner/designercore/model/nodeabstractproperty.cpp @@ -71,7 +71,7 @@ void NodeAbstractProperty::reparentHere(const ModelNode &modelNode, bool isNode return; if (internalNode()->hasProperty(name()) && !internalNode()->property(name())->isNodeAbstractProperty()) - privateModel()->removeProperty(internalNode()->property(name())); + privateModel()->removePropertyAndRelatedResources(internalNode()->property(name())); if (modelNode.hasParentProperty()) { Internal::InternalNodeAbstractProperty::Pointer oldParentProperty = modelNode.internalNode()->parentProperty(); diff --git a/src/plugins/qmldesigner/designercore/model/nodeproperty.cpp b/src/plugins/qmldesigner/designercore/model/nodeproperty.cpp index a9cc7a34f54..4af8428e6be 100644 --- a/src/plugins/qmldesigner/designercore/model/nodeproperty.cpp +++ b/src/plugins/qmldesigner/designercore/model/nodeproperty.cpp @@ -32,7 +32,7 @@ void NodeProperty::setModelNode(const ModelNode &modelNode) } if (internalNode()->hasProperty(name()) && !internalNode()->property(name())->isNodeProperty()) - privateModel()->removeProperty(internalNode()->property(name())); + privateModel()->removePropertyAndRelatedResources(internalNode()->property(name())); privateModel()->reparentNode(internalNode(), name(), modelNode.internalNode(), false); //### we have to add a flag that this is not a list } diff --git a/src/plugins/qmldesigner/designercore/model/signalhandlerproperty.cpp b/src/plugins/qmldesigner/designercore/model/signalhandlerproperty.cpp index 205105187d1..4690044fa61 100644 --- a/src/plugins/qmldesigner/designercore/model/signalhandlerproperty.cpp +++ b/src/plugins/qmldesigner/designercore/model/signalhandlerproperty.cpp @@ -41,7 +41,7 @@ void SignalHandlerProperty::setSource(const QString &source) } if (internalNode()->hasProperty(name()) && !internalNode()->property(name())->isSignalHandlerProperty()) - privateModel()->removeProperty(internalNode()->property(name())); + privateModel()->removePropertyAndRelatedResources(internalNode()->property(name())); privateModel()->setSignalHandlerProperty(internalNode(), name(), source); } @@ -118,7 +118,7 @@ void SignalDeclarationProperty::setSignature(const QString &signature) } if (internalNode()->hasProperty(name()) && !internalNode()->property(name())->isSignalDeclarationProperty()) - privateModel()->removeProperty(internalNode()->property(name())); + privateModel()->removePropertyAndRelatedResources(internalNode()->property(name())); privateModel()->setSignalDeclarationProperty(internalNode(), name(), signature); } diff --git a/src/plugins/qmldesigner/designercore/model/variantproperty.cpp b/src/plugins/qmldesigner/designercore/model/variantproperty.cpp index 48d0fa86300..c879ec7a347 100644 --- a/src/plugins/qmldesigner/designercore/model/variantproperty.cpp +++ b/src/plugins/qmldesigner/designercore/model/variantproperty.cpp @@ -47,7 +47,7 @@ void VariantProperty::setValue(const QVariant &value) } if (internalNode()->hasProperty(name()) && !internalNode()->property(name())->isVariantProperty()) - privateModel()->removeProperty(internalNode()->property(name())); + privateModel()->removePropertyAndRelatedResources(internalNode()->property(name())); privateModel()->setVariantProperty(internalNode(), name(), value); } @@ -96,7 +96,7 @@ void VariantProperty::setDynamicTypeNameAndValue(const TypeName &type, const QVa } if (internalNode()->hasProperty(name()) && !internalNode()->property(name())->isVariantProperty()) - privateModel()->removeProperty(internalNode()->property(name())); + privateModel()->removePropertyAndRelatedResources(internalNode()->property(name())); privateModel()->setDynamicVariantProperty(internalNode(), name(), type, value); } diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h index 4665159d154..974ebdf6aab 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h @@ -242,36 +242,37 @@ public: return false; } - bool isBasedOn(TypeId id0) const override { return isBasedOn_(id0); } + bool isBasedOn(TypeId typeId) const { return isBasedOn_(typeId); } - bool isBasedOn(TypeId id0, TypeId id1) const override { return isBasedOn_(id0, id1); } + bool isBasedOn(TypeId typeId, TypeId id1) const override { return isBasedOn_(typeId, id1); } - bool isBasedOn(TypeId id0, TypeId id1, TypeId id2) const override + bool isBasedOn(TypeId typeId, TypeId id1, TypeId id2) const override { - return isBasedOn_(id0, id1, id2); + return isBasedOn_(typeId, id1, id2); } - bool isBasedOn(TypeId id0, TypeId id1, TypeId id2, TypeId id3) const override + bool isBasedOn(TypeId typeId, TypeId id1, TypeId id2, TypeId id3) const override { - return isBasedOn_(id0, id1, id2, id3); + return isBasedOn_(typeId, id1, id2, id3); } - bool isBasedOn(TypeId id0, TypeId id1, TypeId id2, TypeId id3, TypeId id4) const override + bool isBasedOn(TypeId typeId, TypeId id1, TypeId id2, TypeId id3, TypeId id4) const override { - return isBasedOn_(id0, id1, id2, id3, id4); + return isBasedOn_(typeId, id1, id2, id3, id4); } - bool isBasedOn(TypeId id0, TypeId id1, TypeId id2, TypeId id3, TypeId id4, TypeId id5) const override + bool isBasedOn(TypeId typeId, TypeId id1, TypeId id2, TypeId id3, TypeId id4, TypeId id5) const override { - return isBasedOn_(id0, id1, id2, id3, id4, id5); + return isBasedOn_(typeId, id1, id2, id3, id4, id5); } - bool isBasedOn(TypeId id0, TypeId id1, TypeId id2, TypeId id3, TypeId id4, TypeId id5, TypeId id6) const override + bool isBasedOn( + TypeId typeId, TypeId id1, TypeId id2, TypeId id3, TypeId id4, TypeId id5, TypeId id6) const override { - return isBasedOn_(id0, id1, id2, id3, id4, id5, id6); + return isBasedOn_(typeId, id1, id2, id3, id4, id5, id6); } - bool isBasedOn(TypeId id0, + bool isBasedOn(TypeId typeId, TypeId id1, TypeId id2, TypeId id3, @@ -280,7 +281,7 @@ public: TypeId id6, TypeId id7) const override { - return isBasedOn_(id0, id1, id2, id3, id4, id5, id6, id7); + return isBasedOn_(typeId, id1, id2, id3, id4, id5, id6, id7); } TypeId fetchTypeIdByExportedName(Utils::SmallStringView name) const diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinterface.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinterface.h index 9558bc2e184..bc74172ecd7 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinterface.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinterface.h @@ -36,7 +36,6 @@ public: propertyName(PropertyDeclarationId propertyDeclarationId) const = 0; virtual TypeIds prototypeAndSelfIds(TypeId type) const = 0; virtual TypeIds prototypeIds(TypeId type) const = 0; - virtual bool isBasedOn(TypeId) const = 0; virtual bool isBasedOn(TypeId, TypeId) const = 0; virtual bool isBasedOn(TypeId, TypeId, TypeId) const = 0; virtual bool isBasedOn(TypeId, TypeId, TypeId, TypeId) const = 0; diff --git a/tests/unit/unittest/CMakeLists.txt b/tests/unit/unittest/CMakeLists.txt index d0fde6d5d9a..834c43ae3fd 100644 --- a/tests/unit/unittest/CMakeLists.txt +++ b/tests/unit/unittest/CMakeLists.txt @@ -56,6 +56,8 @@ add_qtc_test(unittest GTEST gtest-std-printing.h lastchangedrowid-test.cpp import-test.cpp + model-test.cpp + modelresourcemanagementmock.h matchingtext-test.cpp mockfutureinterface.h mockmutex.h @@ -66,6 +68,7 @@ add_qtc_test(unittest GTEST mocktimer.cpp mocktimer.h nodelistproperty-test.cpp processevents-utilities.cpp processevents-utilities.h + projectstoragemock.cpp projectstoragemock.h sizedarray-test.cpp smallstring-test.cpp spydummy.cpp spydummy.h @@ -258,6 +261,7 @@ extend_qtc_test(unittest model/model.cpp model/model_p.h model/modelnode.cpp + model/modelresourcemanagementinterface.h model/propertycontainer.cpp model/propertyparser.cpp model/nodeabstractproperty.cpp diff --git a/tests/unit/unittest/listmodeleditor-test.cpp b/tests/unit/unittest/listmodeleditor-test.cpp index b3373f1e645..146ba65ad5d 100644 --- a/tests/unit/unittest/listmodeleditor-test.cpp +++ b/tests/unit/unittest/listmodeleditor-test.cpp @@ -73,19 +73,10 @@ class ListModelEditor : public testing::Test public: ListModelEditor() { - setModuleId("QtQuick", modelId_QtQuick); - setType(modelId_QtQuick, "Item", "data"); - designerModel = QmlDesigner::Model::create(projectStorageMock, "QtQuick.Item", 1, 1); - setModuleId("QtQml.Models", modelId_QtQml_Models); - setType(modelId_QtQml_Models, "ListModel", "children"); - setType(modelId_QtQml_Models, "ListElement", "children"); - componentModel = QmlDesigner::Model::create(projectStorageMock, "QtQml.Models.ListModel", 1, 1); - designerModel->attachView(&mockView); emptyListModelNode = mockView.createModelNode("QtQml.Models.ListModel", 2, 15); - setType(modelId_QtQuick, "ListView", "data"); listViewNode = mockView.createModelNode("QtQuick.ListView", 2, 15); listModelNode = mockView.createModelNode("QtQml.Models.ListModel", 2, 15); mockView.rootModelNode().defaultNodeListProperty().reparentHere(listModelNode); @@ -108,29 +99,6 @@ public: ON_CALL(goIntoComponentMock, Call(_)).WillByDefault([](ModelNode node) { return node; }); } - 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) - { - static int typeIdNumber = 0; - TypeId typeId = TypeId::create(++typeIdNumber); - - static int defaultPropertyIdNumber = 0; - PropertyDeclarationId defaultPropertyId = PropertyDeclarationId::create( - ++defaultPropertyIdNumber); - - ON_CALL(projectStorageMock, typeId(Eq(moduleId), Eq(typeName), _)).WillByDefault(Return(typeId)); - ON_CALL(projectStorageMock, type(Eq(typeId))) - .WillByDefault(Return(Info::Type{defaultPropertyId, {}})); - ON_CALL(projectStorageMock, propertyName(Eq(defaultPropertyId))) - .WillByDefault(Return(defaultPeopertyName)); - } - using Entry = std::pair; ModelNode createElement(std::initializer_list entries, AbstractView &view, ModelNode listModel) @@ -211,9 +179,10 @@ public: } protected: - NiceMock projectStorageMock; + NiceMock projectStorageMock; NiceMock> goIntoComponentMock; - QmlDesigner::ModelPointer designerModel; + QmlDesigner::ModelPointer designerModel{ + QmlDesigner::Model::create(projectStorageMock, "QtQuick.Item", 1, 1)}; NiceMock mockView; QmlDesigner::ListModelEditorModel model{ [&] { return mockView.createModelNode("QtQml.Models.ListModel", 2, 15); }, @@ -225,11 +194,10 @@ protected: ModelNode element1; ModelNode element2; ModelNode element3; - QmlDesigner::ModelPointer componentModel; + QmlDesigner::ModelPointer componentModel{ + QmlDesigner::Model::create(projectStorageMock, "QtQml.Models.ListModel", 1, 1)}; NiceMock mockComponentView; ModelNode componentElement; - ModuleId modelId_QtQuick = ModuleId::create(1); - ModuleId modelId_QtQml_Models = ModuleId::create(2); }; TEST_F(ListModelEditor, CreatePropertyNameSet) diff --git a/tests/unit/unittest/mocklistmodeleditorview.h b/tests/unit/unittest/mocklistmodeleditorview.h index 1de8e1edea0..0f6ff38742e 100644 --- a/tests/unit/unittest/mocklistmodeleditorview.h +++ b/tests/unit/unittest/mocklistmodeleditorview.h @@ -26,10 +26,25 @@ public: const QmlDesigner::NodeAbstractProperty &oldPropertyParent, AbstractView::PropertyChangeFlags propertyChange), (override)); + MOCK_METHOD(void, propertiesRemoved, (const QList &propertyList), (override)); + MOCK_METHOD(void, + propertiesAboutToBeRemoved, + (const QList &propertyList), + (override)); + + MOCK_METHOD(void, + bindingPropertiesChanged, + (const QList &propertyList, + PropertyChangeFlags propertyChange), + (override)); + MOCK_METHOD(void, + bindingPropertiesAboutToBeChanged, + (const QList &propertyList), + (override)); MOCK_METHOD(void, nodeRemoved, @@ -37,4 +52,5 @@ public: const QmlDesigner::NodeAbstractProperty &parentProperty, AbstractView::PropertyChangeFlags propertyChange), (override)); + MOCK_METHOD(void, nodeAboutToBeRemoved, (const QmlDesigner::ModelNode &removedNode), (override)); }; diff --git a/tests/unit/unittest/model-test.cpp b/tests/unit/unittest/model-test.cpp new file mode 100644 index 00000000000..7de2618aa93 --- /dev/null +++ b/tests/unit/unittest/model-test.cpp @@ -0,0 +1,491 @@ +// 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 "googletest.h" + +#include "mocklistmodeleditorview.h" +#include "modelresourcemanagementmock.h" +#include "projectstoragemock.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace { +using QmlDesigner::AbstractProperty; +using QmlDesigner::ModelNode; +using QmlDesigner::ModelNodes; +using QmlDesigner::ModelResourceSet; + +template +auto HasPropertyName(const Matcher &matcher) +{ + return Property(&AbstractProperty::name, matcher); +} + +class Model : public ::testing::Test +{ +protected: + Model() + { + model.attachView(&viewMock); + rootNode = viewMock.rootModelNode(); + ON_CALL(resourceManagementMock, removeNode(_)).WillByDefault([](const auto &node) { + return ModelResourceSet{{node}, {}, {}}; + }); + ON_CALL(resourceManagementMock, removeProperty(_)).WillByDefault([](const auto &property) { + return ModelResourceSet{{}, {property}, {}}; + }); + } + + ~Model() { model.detachView(&viewMock); } + + auto createNodeWithParent(const ModelNode &parentNode) + { + auto node = viewMock.createModelNode("QtQuick.Item"); + parentNode.defaultNodeAbstractProperty().reparentHere(node); + + return node; + } + + auto createProperty(const ModelNode &parentNode, QmlDesigner::PropertyName name) + { + auto property = parentNode.variantProperty(name); + property.setValue(4); + return property; + } + +protected: + NiceMock viewMock; + NiceMock projectStorageMock; + NiceMock resourceManagementMock; + QmlDesigner::Model model{projectStorageMock, + "QtQuick.Item", + -1, + -1, + nullptr, + std::make_unique( + resourceManagementMock)}; + ModelNode rootNode; +}; + +TEST_F(Model, ModelNodeDestroyIsCallingModelResourceManagementRemoveNode) +{ + auto node = createNodeWithParent(rootNode); + + EXPECT_CALL(resourceManagementMock, removeNode(node)); + + node.destroy(); +} + +TEST_F(Model, ModelNodeRemoveProperyIsCallingModelResourceManagementRemoveProperty) +{ + auto property = rootNode.variantProperty("foo"); + property.setValue(4); + + EXPECT_CALL(resourceManagementMock, removeProperty(Eq(property))); + + rootNode.removeProperty("foo"); +} + +TEST_F(Model, NodeAbstractPropertyReparentHereIsCallingModelResourceManagementRemoveProperty) +{ + auto node = createNodeWithParent(rootNode); + auto property = rootNode.variantProperty("foo"); + property.setValue(4); + + EXPECT_CALL(resourceManagementMock, removeProperty(Eq(property))); + + rootNode.nodeListProperty("foo").reparentHere(node); +} + +TEST_F(Model, NodePropertySetModelNodeIsCallingModelResourceManagementRemoveProperty) +{ + auto node = createNodeWithParent(rootNode); + auto property = rootNode.variantProperty("foo"); + property.setValue(4); + + EXPECT_CALL(resourceManagementMock, removeProperty(Eq(property))); + + rootNode.nodeProperty("foo").setModelNode(node); +} + +TEST_F(Model, VariantPropertySetValueIsCallingModelResourceManagementRemoveProperty) +{ + auto property = rootNode.bindingProperty("foo"); + property.setExpression("blah"); + + EXPECT_CALL(resourceManagementMock, removeProperty(Eq(property))); + + rootNode.variantProperty("foo").setValue(7); +} + +TEST_F(Model, + VariantPropertySetDynamicTypeNameAndEnumerationIsCallingModelResourceManagementRemoveProperty) +{ + auto property = rootNode.bindingProperty("foo"); + property.setExpression("blah"); + + EXPECT_CALL(resourceManagementMock, removeProperty(Eq(property))); + + rootNode.variantProperty("foo").setDynamicTypeNameAndEnumeration("int", "Ha"); +} + +TEST_F(Model, VariantPropertySetDynamicTypeNameAndValueIsCallingModelResourceManagementRemoveProperty) +{ + auto property = rootNode.bindingProperty("foo"); + property.setExpression("blah"); + + EXPECT_CALL(resourceManagementMock, removeProperty(Eq(property))); + + rootNode.variantProperty("foo").setDynamicTypeNameAndValue("int", 7); +} + +TEST_F(Model, BindingPropertySetExpressionIsCallingModelResourceManagementRemoveProperty) +{ + auto property = rootNode.variantProperty("foo"); + property.setValue(4); + + EXPECT_CALL(resourceManagementMock, removeProperty(Eq(property))); + + rootNode.bindingProperty("foo").setExpression("blah"); +} + +TEST_F(Model, + BindingPropertySetDynamicTypeNameAndExpressionIsCallingModelResourceManagementRemoveProperty) +{ + auto property = rootNode.variantProperty("foo"); + property.setValue(4); + + EXPECT_CALL(resourceManagementMock, removeProperty(Eq(property))); + + rootNode.bindingProperty("foo").setDynamicTypeNameAndExpression("int", "blah"); +} + +TEST_F(Model, SignalHandlerPropertySetSourceIsCallingModelResourceManagementRemoveProperty) +{ + auto property = rootNode.bindingProperty("foo"); + property.setExpression("blah"); + + EXPECT_CALL(resourceManagementMock, removeProperty(Eq(property))); + + rootNode.signalHandlerProperty("foo").setSource("blah"); +} + +TEST_F(Model, SignalDeclarationPropertySetSignatureIsCallingModelResourceManagementRemoveProperty) +{ + auto property = rootNode.bindingProperty("foo"); + property.setExpression("blah"); + + EXPECT_CALL(resourceManagementMock, removeProperty(Eq(property))); + + rootNode.signalDeclarationProperty("foo").setSignature("blah"); +} + +TEST_F(Model, ModelNodeDestroyIsCallingAbstractViewNodeAboutToBeRemoved) +{ + auto node = createNodeWithParent(rootNode); + auto node2 = createNodeWithParent(rootNode); + ON_CALL(resourceManagementMock, removeNode(node)) + .WillByDefault(Return(ModelResourceSet{{node, node2}, {}, {}})); + + EXPECT_CALL(viewMock, nodeAboutToBeRemoved(Eq(node))); + EXPECT_CALL(viewMock, nodeAboutToBeRemoved(Eq(node2))); + + node.destroy(); +} + +TEST_F(Model, ModelNodeDestroyIsCallingAbstractViewNodeRemoved) +{ + auto node = createNodeWithParent(rootNode); + auto node2 = createNodeWithParent(rootNode); + ON_CALL(resourceManagementMock, removeNode(node)) + .WillByDefault(Return(ModelResourceSet{{node, node2}, {}, {}})); + + EXPECT_CALL(viewMock, nodeRemoved(Eq(node), _, _)); + EXPECT_CALL(viewMock, nodeRemoved(Eq(node2), _, _)); + + node.destroy(); +} + +TEST_F(Model, ModelNodeDestroyIsCallingAbstractViewNodeRemovedWithValidNodes) +{ + auto node = createNodeWithParent(rootNode); + auto node2 = createNodeWithParent(rootNode); + ON_CALL(resourceManagementMock, removeNode(node)) + .WillByDefault(Return(ModelResourceSet{{node, node2, ModelNode{}}, {}, {}})); + + EXPECT_CALL(viewMock, nodeRemoved(Eq(node), _, _)); + EXPECT_CALL(viewMock, nodeRemoved(Eq(node2), _, _)); + + node.destroy(); +} + +TEST_F(Model, ModelNodeDestroyIsCallingAbstractViewPropertiesAboutToBeRemoved) +{ + auto node = createNodeWithParent(rootNode); + auto property = createProperty(rootNode, "foo"); + auto property2 = createProperty(rootNode, "bar"); + ON_CALL(resourceManagementMock, removeNode(node)) + .WillByDefault(Return(ModelResourceSet{{node}, {property, property2}, {}})); + + EXPECT_CALL(viewMock, propertiesAboutToBeRemoved(ElementsAre(Eq(property)))); + EXPECT_CALL(viewMock, propertiesAboutToBeRemoved(ElementsAre(Eq(property2)))); + + node.destroy(); +} + +TEST_F(Model, ModelNodeDestroyIsCallingAbstractViewPropertiesRemoved) +{ + auto node = createNodeWithParent(rootNode); + auto property = createProperty(rootNode, "foo"); + auto property2 = createProperty(rootNode, "bar"); + ON_CALL(resourceManagementMock, removeNode(node)) + .WillByDefault(Return(ModelResourceSet{{node}, {property, property2}, {}})); + + EXPECT_CALL(viewMock, propertiesRemoved(ElementsAre(Eq(property)))); + EXPECT_CALL(viewMock, propertiesRemoved(ElementsAre(Eq(property2)))); + + node.destroy(); +} + +TEST_F(Model, ModelNodeDestroyIsCallingAbstractViewPropertiesRemovedOnlyWithValidProperties) +{ + auto node = createNodeWithParent(rootNode); + auto property = createProperty(rootNode, "foo"); + auto property2 = createProperty(rootNode, "bar"); + ON_CALL(resourceManagementMock, removeNode(node)) + .WillByDefault(Return(ModelResourceSet{{node}, {property, property2, {}}, {}})); + + EXPECT_CALL(viewMock, propertiesRemoved(ElementsAre(Eq(property)))); + EXPECT_CALL(viewMock, propertiesRemoved(ElementsAre(Eq(property2)))); + + node.destroy(); +} + +TEST_F(Model, ModelNodeDestroyIsCallingAbstractViewBindingPropertiesAboutToBeChanged) +{ + auto node = createNodeWithParent(rootNode); + auto property = rootNode.bindingProperty("foo"); + auto property2 = rootNode.bindingProperty("bar"); + ON_CALL(resourceManagementMock, removeNode(node)) + .WillByDefault(Return(ModelResourceSet{{node}, {}, {{property, "yi"}, {property2, "er"}}})); + + EXPECT_CALL(viewMock, bindingPropertiesAboutToBeChanged(ElementsAre(Eq(property)))); + EXPECT_CALL(viewMock, bindingPropertiesAboutToBeChanged(ElementsAre(Eq(property2)))); + + node.destroy(); +} + +TEST_F(Model, ModelNodeDestroyIsCallingAbstractViewBindingPropertiesChanged) +{ + auto node = createNodeWithParent(rootNode); + auto property = rootNode.bindingProperty("foo"); + auto property2 = rootNode.bindingProperty("bar"); + ON_CALL(resourceManagementMock, removeNode(node)) + .WillByDefault(Return(ModelResourceSet{{node}, {}, {{property, "yi"}, {property2, "er"}}})); + + EXPECT_CALL(viewMock, bindingPropertiesChanged(ElementsAre(Eq(property)), _)); + EXPECT_CALL(viewMock, bindingPropertiesChanged(ElementsAre(Eq(property2)), _)); + + node.destroy(); +} + +TEST_F(Model, ModelNodeDestroyIsCallingAbstractViewBindingPropertiesChangedOnlyWithValidProperties) +{ + auto node = createNodeWithParent(rootNode); + auto property = rootNode.bindingProperty("foo"); + auto property2 = rootNode.bindingProperty("bar"); + ON_CALL(resourceManagementMock, removeNode(node)) + .WillByDefault(Return( + ModelResourceSet{{node}, {}, {{property, "yi"}, {property2, "er"}, {{}, "san"}}})); + + EXPECT_CALL(viewMock, bindingPropertiesChanged(ElementsAre(Eq(property)), _)); + EXPECT_CALL(viewMock, bindingPropertiesChanged(ElementsAre(Eq(property2)), _)); + + node.destroy(); +} + +TEST_F(Model, ModelNodeRemovePropertyIsCallingAbstractViewNodeAboutToBeRemoved) +{ + auto property = createProperty(rootNode, "foo"); + auto node = createNodeWithParent(rootNode); + auto node2 = createNodeWithParent(rootNode); + ON_CALL(resourceManagementMock, removeProperty(property)) + .WillByDefault(Return(ModelResourceSet{{node, node2}, {property}, {}})); + + EXPECT_CALL(viewMock, nodeAboutToBeRemoved(Eq(node))); + EXPECT_CALL(viewMock, nodeAboutToBeRemoved(Eq(node2))); + + rootNode.removeProperty("foo"); +} + +TEST_F(Model, ModelNodeRemovePropertyIsCallingAbstractViewNodeRemoved) +{ + auto property = createProperty(rootNode, "foo"); + auto node = createNodeWithParent(rootNode); + auto node2 = createNodeWithParent(rootNode); + ON_CALL(resourceManagementMock, removeProperty(property)) + .WillByDefault(Return(ModelResourceSet{{node, node2}, {property}, {}})); + + EXPECT_CALL(viewMock, nodeRemoved(Eq(node), _, _)); + EXPECT_CALL(viewMock, nodeRemoved(Eq(node2), _, _)); + + rootNode.removeProperty("foo"); +} + +TEST_F(Model, ModelNodeRemovePropertyIsCallingAbstractViewNodeRemovedWithValidNodes) +{ + auto property = createProperty(rootNode, "foo"); + auto node = createNodeWithParent(rootNode); + auto node2 = createNodeWithParent(rootNode); + ON_CALL(resourceManagementMock, removeProperty(property)) + .WillByDefault(Return(ModelResourceSet{{node, node2, ModelNode{}}, {property}, {}})); + + EXPECT_CALL(viewMock, nodeRemoved(Eq(node), _, _)); + EXPECT_CALL(viewMock, nodeRemoved(Eq(node2), _, _)); + + rootNode.removeProperty("foo"); +} + +TEST_F(Model, ModelNodeRemovePropertyIsCallingAbstractViewPropertiesAboutToBeRemoved) +{ + auto property = createProperty(rootNode, "yi"); + auto property2 = createProperty(rootNode, "er"); + ON_CALL(resourceManagementMock, removeProperty(property)) + .WillByDefault(Return(ModelResourceSet{{}, {property, property2}, {}})); + + EXPECT_CALL(viewMock, propertiesAboutToBeRemoved(ElementsAre(Eq(property)))); + EXPECT_CALL(viewMock, propertiesAboutToBeRemoved(ElementsAre(Eq(property2)))); + + rootNode.removeProperty("yi"); +} + +TEST_F(Model, ModelNodeRemovePropertyIsCallingAbstractViewPropertiesRemoved) +{ + auto property = createProperty(rootNode, "yi"); + auto property2 = createProperty(rootNode, "er"); + ON_CALL(resourceManagementMock, removeProperty(property)) + .WillByDefault(Return(ModelResourceSet{{}, {property, property2}, {}})); + + EXPECT_CALL(viewMock, propertiesRemoved(ElementsAre(Eq(property)))); + EXPECT_CALL(viewMock, propertiesRemoved(ElementsAre(Eq(property2)))); + + rootNode.removeProperty("yi"); +} + +TEST_F(Model, ModelNodeRemovePropertyIsCallingAbstractViewPropertiesRemovedOnlyWithValidProperties) +{ + auto property = createProperty(rootNode, "yi"); + auto property2 = createProperty(rootNode, "er"); + ON_CALL(resourceManagementMock, removeProperty(property)) + .WillByDefault(Return(ModelResourceSet{{}, {property, property2, {}}, {}})); + + EXPECT_CALL(viewMock, propertiesRemoved(ElementsAre(Eq(property)))); + EXPECT_CALL(viewMock, propertiesRemoved(ElementsAre(Eq(property2)))); + + rootNode.removeProperty("yi"); +} + +TEST_F(Model, ModelNodeRemovePropertyIsCallingAbstractViewBindingPropertiesAboutToBeChanged) +{ + auto property = createProperty(rootNode, "yi"); + auto property1 = rootNode.bindingProperty("foo"); + auto property2 = rootNode.bindingProperty("bar"); + ON_CALL(resourceManagementMock, removeProperty(property)) + .WillByDefault( + Return(ModelResourceSet{{}, {property}, {{property1, "yi"}, {property2, "er"}}})); + + EXPECT_CALL(viewMock, bindingPropertiesAboutToBeChanged(ElementsAre(Eq(property1)))); + EXPECT_CALL(viewMock, bindingPropertiesAboutToBeChanged(ElementsAre(Eq(property2)))); + + rootNode.removeProperty("yi"); +} + +TEST_F(Model, ModelNodeRemovePropertyIsCallingAbstractViewBindingPropertiesChanged) +{ + auto property = createProperty(rootNode, "yi"); + auto property1 = rootNode.bindingProperty("foo"); + auto property2 = rootNode.bindingProperty("bar"); + ON_CALL(resourceManagementMock, removeProperty(property)) + .WillByDefault( + Return(ModelResourceSet{{}, {property}, {{property1, "yi"}, {property2, "er"}}})); + + EXPECT_CALL(viewMock, bindingPropertiesChanged(ElementsAre(Eq(property1)), _)); + EXPECT_CALL(viewMock, bindingPropertiesChanged(ElementsAre(Eq(property2)), _)); + + rootNode.removeProperty("yi"); +} + +TEST_F(Model, + ModelNodeRemovePropertyIsCallingAbstractViewBindingPropertiesChangedOnlyWithValidProperties) +{ + auto property = createProperty(rootNode, "yi"); + auto property1 = rootNode.bindingProperty("foo"); + auto property2 = rootNode.bindingProperty("bar"); + ON_CALL(resourceManagementMock, removeProperty(property)) + .WillByDefault( + Return(ModelResourceSet{{}, {property}, {{property1, "yi"}, {property2, "er"}, {}}})); + + EXPECT_CALL(viewMock, bindingPropertiesChanged(ElementsAre(Eq(property1)), _)); + EXPECT_CALL(viewMock, bindingPropertiesChanged(ElementsAre(Eq(property2)), _)); + + rootNode.removeProperty("yi"); +} + +TEST_F(Model, ByDefaultRemoveModelNodeRemovesNode) +{ + model.detachView(&viewMock); + QmlDesigner::Model newModel{projectStorageMock, "QtQuick.Item"}; + newModel.attachView(&viewMock); + auto node = createNodeWithParent(viewMock.rootModelNode()); + + EXPECT_CALL(viewMock, nodeAboutToBeRemoved(Eq(node))); + + node.destroy(); +} + +TEST_F(Model, ByDefaultRemovePropertiesRemovesProperty) +{ + model.detachView(&viewMock); + QmlDesigner::Model newModel{projectStorageMock, "QtQuick.Item"}; + newModel.attachView(&viewMock); + rootNode = viewMock.rootModelNode(); + auto property = createProperty(rootNode, "yi"); + + EXPECT_CALL(viewMock, propertiesAboutToBeRemoved(ElementsAre(Eq(property)))); + + rootNode.removeProperty("yi"); +} + +TEST_F(Model, ByDefaultRemoveModelNodeInFactoryMethodCallsRemovesNode) +{ + model.detachView(&viewMock); + auto newModel = QmlDesigner::Model::create(projectStorageMock, "QtQuick.Item"); + newModel->attachView(&viewMock); + auto node = createNodeWithParent(viewMock.rootModelNode()); + + EXPECT_CALL(viewMock, nodeAboutToBeRemoved(Eq(node))); + + node.destroy(); +} + +TEST_F(Model, ByDefaultRemovePropertiesInFactoryMethodCallsRemoveProperty) +{ + model.detachView(&viewMock); + auto newModel = QmlDesigner::Model::create(projectStorageMock, "QtQuick.Item"); + newModel->attachView(&viewMock); + rootNode = viewMock.rootModelNode(); + auto property = createProperty(rootNode, "yi"); + + EXPECT_CALL(viewMock, propertiesAboutToBeRemoved(ElementsAre(Eq(property)))); + + rootNode.removeProperty("yi"); +} + +} // namespace diff --git a/tests/unit/unittest/modelresourcemanagementmock.h b/tests/unit/unittest/modelresourcemanagementmock.h new file mode 100644 index 00000000000..0b98adfcff0 --- /dev/null +++ b/tests/unit/unittest/modelresourcemanagementmock.h @@ -0,0 +1,42 @@ +// 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 "googletest.h" + +#include +#include + +class ModelResourceManagementMock : public QmlDesigner::ModelResourceManagementInterface +{ +public: + MOCK_METHOD(QmlDesigner::ModelResourceSet, + removeNode, + (const QmlDesigner::ModelNode &), + (const, override)); + MOCK_METHOD(QmlDesigner::ModelResourceSet, + removeProperty, + (const QmlDesigner::AbstractProperty &), + (const, override)); +}; + +class ModelResourceManagementMockWrapper : public QmlDesigner::ModelResourceManagementInterface +{ +public: + ModelResourceManagementMockWrapper(ModelResourceManagementMock &mock) + : mock{mock} + {} + + QmlDesigner::ModelResourceSet removeNode(const QmlDesigner::ModelNode &node) const override + { + return mock.removeNode(node); + } + + QmlDesigner::ModelResourceSet removeProperty(const QmlDesigner::AbstractProperty &property) const override + { + return mock.removeProperty(property); + } + + ModelResourceManagementMock &mock; +}; diff --git a/tests/unit/unittest/nodelistproperty-test.cpp b/tests/unit/unittest/nodelistproperty-test.cpp index 8abb350aa63..36a8dcb6df7 100644 --- a/tests/unit/unittest/nodelistproperty-test.cpp +++ b/tests/unit/unittest/nodelistproperty-test.cpp @@ -25,11 +25,6 @@ protected: using iterator = QmlDesigner::NodeListProperty::iterator; NodeListProperty() { - ModuleId modelId_QtQuick = ModuleId::create(1); - setModuleId("QtQuick", modelId_QtQuick); - setType(modelId_QtQuick, "Item", "data"); - model = std::make_unique(projectStorageMock, "QtQuick.Item"); - model->attachView(&abstractViewMock); nodeListProperty = abstractViewMock.rootModelNode().nodeListProperty("foo"); @@ -82,8 +77,9 @@ protected: } protected: - NiceMock projectStorageMock; - std::unique_ptr model; + NiceMock projectStorageMock; + std::unique_ptr model{ + std::make_unique(projectStorageMock, "QtQuick.Item")}; NiceMock abstractViewMock; QmlDesigner::NodeListProperty nodeListProperty; ModelNode node1; diff --git a/tests/unit/unittest/projectstoragemock.cpp b/tests/unit/unittest/projectstoragemock.cpp new file mode 100644 index 00000000000..8e4928377f4 --- /dev/null +++ b/tests/unit/unittest/projectstoragemock.cpp @@ -0,0 +1,89 @@ +// 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 "projectstoragemock.h" + +namespace QmlDesigner { +namespace { + +template +void incrementBasicId(BasicId &id) +{ + id = BasicId::create(id.internalId() + 1); +} + +ModuleId createModule(ProjectStorageMock &mock, Utils::SmallStringView moduleName) +{ + static ModuleId moduleId; + incrementBasicId(moduleId); + + ON_CALL(mock, moduleId(Eq(moduleName))).WillByDefault(Return(moduleId)); + + return moduleId; +} + +TypeId createType(ProjectStorageMock &mock, + ModuleId moduleId, + Utils::SmallStringView typeName, + Utils::SmallString defaultPropertyName, + Storage::TypeTraits typeTraits, + TypeId baseTypeId = TypeId{}) +{ + static TypeId typeId; + incrementBasicId(typeId); + + static PropertyDeclarationId defaultPropertyId; + incrementBasicId(defaultPropertyId); + + ON_CALL(mock, typeId(Eq(moduleId), Eq(typeName), _)).WillByDefault(Return(typeId)); + ON_CALL(mock, type(Eq(typeId))) + .WillByDefault(Return(Storage::Info::Type{defaultPropertyId, typeTraits})); + ON_CALL(mock, propertyName(Eq(defaultPropertyId))).WillByDefault(Return(defaultPropertyName)); + + if (baseTypeId) + ON_CALL(mock, isBasedOn(Eq(typeId), Eq(baseTypeId))).WillByDefault(Return(true)); + + return typeId; +} + +TypeId createObject(ProjectStorageMock &mock, + ModuleId moduleId, + Utils::SmallStringView typeName, + Utils::SmallString defaultPropertyName, + TypeId baseTypeId = TypeId{}) +{ + return createType( + mock, moduleId, typeName, defaultPropertyName, Storage::TypeTraits::Reference, baseTypeId); +} +void setupIsBasedOn(ProjectStorageMock &mock) +{ + auto call = [&](TypeId typeId, auto... ids) -> bool { + return (mock.isBasedOn(typeId, ids) || ...); + }; + ON_CALL(mock, isBasedOn(_, _, _)).WillByDefault(call); + ON_CALL(mock, isBasedOn(_, _, _, _)).WillByDefault(call); + ON_CALL(mock, isBasedOn(_, _, _, _, _)).WillByDefault(call); + ON_CALL(mock, isBasedOn(_, _, _, _, _, _)).WillByDefault(call); + ON_CALL(mock, isBasedOn(_, _, _, _, _, _, _)).WillByDefault(call); + ON_CALL(mock, isBasedOn(_, _, _, _, _, _, _, _)).WillByDefault(call); +} + +} // namespace +} // namespace QmlDesigner + +void ProjectStorageMock::setupQtQtuick() +{ + QmlDesigner::setupIsBasedOn(*this); + + auto qmlModuleId = QmlDesigner::createModule(*this, "QML"); + auto qtQmlModelsModuleId = QmlDesigner::createModule(*this, "QtQml.Models"); + auto qtQuickModuleId = QmlDesigner::createModule(*this, "QtQuick"); + + auto qtObjectId = QmlDesigner::createObject(*this, qmlModuleId, "QtObject", "children"); + + QmlDesigner::createObject(*this, qtQmlModelsModuleId, "ListModel", "children", qtObjectId); + QmlDesigner::createObject(*this, qtQmlModelsModuleId, "ListElement", "children", qtObjectId); + + auto itemId = QmlDesigner::createObject(*this, qtQuickModuleId, "Item", "data", qtObjectId); + QmlDesigner::createObject(*this, qtQuickModuleId, "ListView", "data", itemId); +} diff --git a/tests/unit/unittest/projectstoragemock.h b/tests/unit/unittest/projectstoragemock.h index 8a3c2efd153..cb30521c959 100644 --- a/tests/unit/unittest/projectstoragemock.h +++ b/tests/unit/unittest/projectstoragemock.h @@ -1,4 +1,4 @@ -// Copyright (C) 2017 The Qt Company Ltd. +// Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #pragma once @@ -14,6 +14,8 @@ class ProjectStorageMock : public QmlDesigner::ProjectStorageInterface { public: + void setupQtQtuick(); + MOCK_METHOD(void, synchronize, (QmlDesigner::Storage::Synchronization::SynchronizationPackage package), @@ -63,7 +65,6 @@ public: (const, override)); MOCK_METHOD(QmlDesigner::TypeIds, prototypeAndSelfIds, (QmlDesigner::TypeId type), (const, override)); MOCK_METHOD(QmlDesigner::TypeIds, prototypeIds, (QmlDesigner::TypeId type), (const, override)); - MOCK_METHOD(bool, isBasedOn, (QmlDesigner::TypeId typeId), (const, override)); MOCK_METHOD(bool, isBasedOn, (QmlDesigner::TypeId typeId, QmlDesigner::TypeId), (const, override)); MOCK_METHOD(bool, isBasedOn, @@ -158,3 +159,8 @@ public: MOCK_METHOD(std::vector, fetchAllSources, (), ()); }; +class ProjectStorageMockWithQtQtuick : public ProjectStorageMock +{ +public: + ProjectStorageMockWithQtQtuick() { setupQtQtuick(); } +}; From 3dcdbe9069c452e2f0eacb925aa7412e63dc4762 Mon Sep 17 00:00:00 2001 From: hjk Date: Mon, 22 May 2023 10:32:24 +0200 Subject: [PATCH 186/192] FancyLineEdit: Mark placeholder text that doesn't pass validation ... also for place holder text. While this omission was apparently intentional, the opposite behavior was used in the Beautifier settings, arguably being a better standard as this makes clear to the user that the pre-selected placeholder value won't be ok to use. Change-Id: Iaf15b1351de929dee57329efdf18d7e831b4f8bc Reviewed-by: Reviewed-by: Eike Ziller --- src/libs/utils/fancylineedit.cpp | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/libs/utils/fancylineedit.cpp b/src/libs/utils/fancylineedit.cpp index 03e33c67b67..443845e7801 100644 --- a/src/libs/utils/fancylineedit.cpp +++ b/src/libs/utils/fancylineedit.cpp @@ -115,6 +115,7 @@ public: const QColor m_okTextColor; const QColor m_errorTextColor; + const QColor m_placeholderTextColor; QString m_errorMessage; }; @@ -123,7 +124,9 @@ FancyLineEditPrivate::FancyLineEditPrivate(FancyLineEdit *parent) : m_lineEdit(parent), m_completionShortcut(completionShortcut()->key(), parent), m_okTextColor(creatorTheme()->color(Theme::TextColorNormal)), - m_errorTextColor(creatorTheme()->color(Theme::TextColorError)) + m_errorTextColor(creatorTheme()->color(Theme::TextColorError)), + m_placeholderTextColor(creatorTheme()->color(Theme::PalettePlaceholderText)) + { m_completionShortcut.setContext(Qt::WidgetShortcut); connect(completionShortcut(), &CompletionShortcut::keyChanged, @@ -485,15 +488,18 @@ void FancyLineEdit::validate() setToolTip(d->m_errorMessage); d->m_toolTipSet = true; } - // Changed..figure out if valid changed. DisplayingPlaceholderText is not valid, - // but should not show error color. Also trigger on the first change. + // Changed..figure out if valid changed. Also trigger on the first change. + // Invalid DisplayingPlaceholderText shows also error color. if (newState != d->m_state || d->m_firstChange) { const bool validHasChanged = (d->m_state == Valid) != (newState == Valid); d->m_state = newState; d->m_firstChange = false; QPalette p = palette(); - p.setColor(QPalette::Active, QPalette::Text, newState == Invalid ? d->m_errorTextColor : d->m_okTextColor); + p.setColor(QPalette::Active, QPalette::Text, + newState == Invalid ? d->m_errorTextColor : d->m_okTextColor); + p.setColor(QPalette::Active, QPalette::PlaceholderText, + validates ? d->m_placeholderTextColor : d->m_errorTextColor); setPalette(p); if (validHasChanged) From df7398e2c5f3c1595f32c7484ac1e804d83a01ca Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Mon, 22 May 2023 16:25:32 +0200 Subject: [PATCH 187/192] QmlDesigner: Add virtual destructor to silence warning Add copy and move constructor ... to adhere to rule of five. Change-Id: Ib56c229b45df2304694b51b4aa51aea8a20073d9 Reviewed-by: Tim Jenssen --- .../qmldesigner/designercore/include/qmlmodelnodefacade.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/plugins/qmldesigner/designercore/include/qmlmodelnodefacade.h b/src/plugins/qmldesigner/designercore/include/qmlmodelnodefacade.h index e76b5893792..27c4c86cc0b 100644 --- a/src/plugins/qmldesigner/designercore/include/qmlmodelnodefacade.h +++ b/src/plugins/qmldesigner/designercore/include/qmlmodelnodefacade.h @@ -14,6 +14,11 @@ class NodeInstanceView; class QMLDESIGNERCORE_EXPORT QmlModelNodeFacade { public: + QmlModelNodeFacade(const QmlModelNodeFacade &) = default; + QmlModelNodeFacade &operator=(const QmlModelNodeFacade &) = default; + QmlModelNodeFacade(QmlModelNodeFacade &&) noexcept = default; + QmlModelNodeFacade &operator=(QmlModelNodeFacade &&) noexcept = default; + virtual ~QmlModelNodeFacade() = default; operator ModelNode() const { return m_modelNode; } ModelNode modelNode() const { return m_modelNode; } bool hasModelNode() const; From db1d12f69abd77219a17ffe93e4ddc6ab4d65874 Mon Sep 17 00:00:00 2001 From: Alexander Pershin Date: Sat, 20 May 2023 01:25:41 +0300 Subject: [PATCH 188/192] MiniProjectTargetSelector: Natural sorting of entries Change-Id: Ibad929d1423d964204b4b236e1cc4df7e0614889 Reviewed-by: Christian Kandeler Reviewed-by: --- .../projectexplorer/miniprojecttargetselector.cpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/plugins/projectexplorer/miniprojecttargetselector.cpp b/src/plugins/projectexplorer/miniprojecttargetselector.cpp index 96f85e94bea..3a1b01b724b 100644 --- a/src/plugins/projectexplorer/miniprojecttargetselector.cpp +++ b/src/plugins/projectexplorer/miniprojecttargetselector.cpp @@ -32,6 +32,7 @@ #include #include +#include #include #include #include @@ -141,8 +142,15 @@ private: static bool compareItems(const TreeItem *ti1, const TreeItem *ti2) { - const int result = caseFriendlyCompare(static_cast(ti1)->rawDisplayName(), - static_cast(ti2)->rawDisplayName()); + static const QCollator collator = [] { + QCollator collator; + collator.setNumericMode(true); + collator.setCaseSensitivity(Qt::CaseInsensitive); + return collator; + }(); + + const int result = collator.compare(static_cast(ti1)->rawDisplayName(), + static_cast(ti2)->rawDisplayName()); if (result != 0) return result < 0; return ti1 < ti2; From a4c962aa33ac74e0a357f30f9b2f90111f8d865a Mon Sep 17 00:00:00 2001 From: hjk Date: Mon, 22 May 2023 11:35:52 +0200 Subject: [PATCH 189/192] Utils: Introduce aspect ctors referring to an "wrapping" AspectContainer This removes the need to manual 'registerAspect' calls in most cases. Whether the containers owns the registered aspects or just references them is still determined by AspectContainer::setOwnsSubAspects() Change-Id: Iadd17c919287f625bf5eb4964de4149d4da5a0f9 Reviewed-by: Alessandro Portale --- src/libs/utils/aspects.cpp | 36 +++++++++++++++++++----------------- src/libs/utils/aspects.h | 18 +++++++++--------- 2 files changed, 28 insertions(+), 26 deletions(-) diff --git a/src/libs/utils/aspects.cpp b/src/libs/utils/aspects.cpp index e2dda5e31cb..7baa9ba38ce 100644 --- a/src/libs/utils/aspects.cpp +++ b/src/libs/utils/aspects.cpp @@ -92,9 +92,11 @@ public: /*! Constructs a BaseAspect. */ -BaseAspect::BaseAspect() +BaseAspect::BaseAspect(AspectContainer *container) : d(new Internal::BaseAspectPrivate) { + if (container) + container->registerAspect(this); addDataExtractor(this, &BaseAspect::value, &Data::value); } @@ -766,8 +768,8 @@ public: Constructs a StringAspect. */ -StringAspect::StringAspect() - : d(new Internal::StringAspectPrivate) +StringAspect::StringAspect(AspectContainer *container) + : BaseAspect(container), d(new Internal::StringAspectPrivate) { setDefaultValue(QString()); setSpan(2, 1); // Default: Label + something @@ -1359,8 +1361,8 @@ FilePathAspect::FilePathAspect() The color aspect is displayed using a QtColorButton. */ -ColorAspect::ColorAspect() - : d(new Internal::ColorAspectPrivate) +ColorAspect::ColorAspect(AspectContainer *container) + : BaseAspect(container), d(new Internal::ColorAspectPrivate) { setDefaultValue(QColor::fromRgb(0, 0, 0)); setSpan(1, 1); @@ -1426,8 +1428,8 @@ void ColorAspect::setVolatileValue(const QVariant &val) */ -BoolAspect::BoolAspect() - : d(new Internal::BoolAspectPrivate) +BoolAspect::BoolAspect(AspectContainer *container) + : BaseAspect(container), d(new Internal::BoolAspectPrivate) { setDefaultValue(false); setSpan(2, 1); @@ -1605,8 +1607,8 @@ CheckableDecider BoolAspect::checkableDecider() QRadioButtons in a QButtonGroup. */ -SelectionAspect::SelectionAspect() - : d(new Internal::SelectionAspectPrivate) +SelectionAspect::SelectionAspect(AspectContainer *container) + : BaseAspect(container), d(new Internal::SelectionAspectPrivate) { setSpan(2, 1); } @@ -1808,8 +1810,8 @@ QVariant SelectionAspect::itemValueForIndex(int index) const checkable items. */ -MultiSelectionAspect::MultiSelectionAspect() - : d(new Internal::MultiSelectionAspectPrivate(this)) +MultiSelectionAspect::MultiSelectionAspect(AspectContainer *container) + : BaseAspect(container), d(new Internal::MultiSelectionAspectPrivate(this)) { setDefaultValue(QStringList()); setSpan(2, 1); @@ -1915,8 +1917,8 @@ void MultiSelectionAspect::setValue(const QStringList &value) // IntegerAspect -IntegerAspect::IntegerAspect() - : d(new Internal::IntegerAspectPrivate) +IntegerAspect::IntegerAspect(AspectContainer *container) + : BaseAspect(container), d(new Internal::IntegerAspectPrivate) { setDefaultValue(qint64(0)); setSpan(2, 1); @@ -2051,8 +2053,8 @@ void IntegerAspect::setSingleStep(qint64 step) the display of the spin box. */ -DoubleAspect::DoubleAspect() - : d(new Internal::DoubleAspectPrivate) +DoubleAspect::DoubleAspect(AspectContainer *container) + : BaseAspect(container), d(new Internal::DoubleAspectPrivate) { setDefaultValue(double(0)); setSpan(2, 1); @@ -2206,8 +2208,8 @@ TriState TriState::fromVariant(const QVariant &variant) that is a list of strings. */ -StringListAspect::StringListAspect() - : d(new Internal::StringListAspectPrivate) +StringListAspect::StringListAspect(AspectContainer *container) + : BaseAspect(container), d(new Internal::StringListAspectPrivate) { setDefaultValue(QStringList()); } diff --git a/src/libs/utils/aspects.h b/src/libs/utils/aspects.h index ecfc9ee86bf..5fb16356ce3 100644 --- a/src/libs/utils/aspects.h +++ b/src/libs/utils/aspects.h @@ -45,7 +45,7 @@ class QTCREATOR_UTILS_EXPORT BaseAspect : public QObject Q_OBJECT public: - BaseAspect(); + BaseAspect(AspectContainer *container = nullptr); ~BaseAspect() override; Id id() const; @@ -213,7 +213,7 @@ class QTCREATOR_UTILS_EXPORT BoolAspect : public BaseAspect Q_OBJECT public: - BoolAspect(); + BoolAspect(AspectContainer *container = nullptr); ~BoolAspect() override; struct Data : BaseAspect::Data @@ -257,7 +257,7 @@ class QTCREATOR_UTILS_EXPORT ColorAspect : public BaseAspect Q_OBJECT public: - ColorAspect(); + ColorAspect(AspectContainer *container = nullptr); ~ColorAspect() override; struct Data : BaseAspect::Data @@ -282,7 +282,7 @@ class QTCREATOR_UTILS_EXPORT SelectionAspect : public BaseAspect Q_OBJECT public: - SelectionAspect(); + SelectionAspect(AspectContainer *container = nullptr); ~SelectionAspect() override; void addToLayout(Layouting::LayoutItem &parent) override; @@ -336,7 +336,7 @@ class QTCREATOR_UTILS_EXPORT MultiSelectionAspect : public BaseAspect Q_OBJECT public: - MultiSelectionAspect(); + MultiSelectionAspect(AspectContainer *container = nullptr); ~MultiSelectionAspect() override; void addToLayout(Layouting::LayoutItem &parent) override; @@ -359,7 +359,7 @@ class QTCREATOR_UTILS_EXPORT StringAspect : public BaseAspect Q_OBJECT public: - StringAspect(); + StringAspect(AspectContainer *container = nullptr); ~StringAspect() override; struct Data : BaseAspect::Data @@ -456,7 +456,7 @@ class QTCREATOR_UTILS_EXPORT IntegerAspect : public BaseAspect Q_OBJECT public: - IntegerAspect(); + IntegerAspect(AspectContainer *container = nullptr); ~IntegerAspect() override; void addToLayout(Layouting::LayoutItem &parent) override; @@ -494,7 +494,7 @@ class QTCREATOR_UTILS_EXPORT DoubleAspect : public BaseAspect Q_OBJECT public: - DoubleAspect(); + DoubleAspect(AspectContainer *container = nullptr); ~DoubleAspect() override; void addToLayout(Layouting::LayoutItem &parent) override; @@ -563,7 +563,7 @@ class QTCREATOR_UTILS_EXPORT StringListAspect : public BaseAspect Q_OBJECT public: - StringListAspect(); + StringListAspect(AspectContainer *container = nullptr); ~StringListAspect() override; void addToLayout(Layouting::LayoutItem &parent) override; From eebe7f86f3d676b2f14291fa25fc65a472d70aa2 Mon Sep 17 00:00:00 2001 From: hjk Date: Mon, 22 May 2023 14:55:24 +0200 Subject: [PATCH 190/192] Fossil: Use a bit more FilePath{Aspect} Change-Id: Ie7c995585aafe03428dc5e93b2904b189f0319c0 Reviewed-by: Orgad Shaneh Reviewed-by: --- src/plugins/fossil/fossilclient.cpp | 5 ++--- src/plugins/fossil/fossilplugin.cpp | 15 +++++---------- src/plugins/fossil/fossilsettings.cpp | 2 -- src/plugins/fossil/fossilsettings.h | 4 ++-- src/plugins/fossil/pullorpushdialog.cpp | 10 ++++------ src/plugins/fossil/pullorpushdialog.h | 12 +++++------- 6 files changed, 18 insertions(+), 30 deletions(-) diff --git a/src/plugins/fossil/fossilclient.cpp b/src/plugins/fossil/fossilclient.cpp index 49ded9c7740..915121710aa 100644 --- a/src/plugins/fossil/fossilclient.cpp +++ b/src/plugins/fossil/fossilclient.cpp @@ -592,7 +592,7 @@ bool FossilClient::synchronousCreateRepository(const FilePath &workingDirectory, // use the configured default user for admin const QString repoName = workingDirectory.fileName().simplified(); - const QString repoPath = settings().defaultRepoPath.value(); + const FilePath repoPath = settings().defaultRepoPath(); const QString adminUser = settings().userName.value(); if (repoName.isEmpty() || repoPath.isEmpty()) @@ -602,8 +602,7 @@ bool FossilClient::synchronousCreateRepository(const FilePath &workingDirectory, // @TODO: what about --template options? const FilePath fullRepoName = FilePath::fromStringWithExtension(repoName, Constants::FOSSIL_FILE_SUFFIX); - const FilePath repoFilePath = FilePath::fromString(repoPath) - .pathAppended(fullRepoName.toString()); + const FilePath repoFilePath = repoPath.pathAppended(fullRepoName.toString()); QStringList args(vcsCommandString(CreateRepositoryCommand)); if (!adminUser.isEmpty()) args << "--admin-user" << adminUser; diff --git a/src/plugins/fossil/fossilplugin.cpp b/src/plugins/fossil/fossilplugin.cpp index 6792c6e42b8..a316daf3aec 100644 --- a/src/plugins/fossil/fossilplugin.cpp +++ b/src/plugins/fossil/fossilplugin.cpp @@ -592,7 +592,7 @@ bool FossilPluginPrivate::pullOrPush(FossilPluginPrivate::SyncMode mode) QTC_ASSERT(state.hasTopLevel(), return false); PullOrPushDialog dialog(pullOrPushMode, Core::ICore::dialogParent()); - dialog.setLocalBaseDirectory(m_client.settings().defaultRepoPath.value()); + dialog.setLocalBaseDirectory(m_client.settings().defaultRepoPath()); const QString defaultURL(m_client.synchronousGetRepositoryURL(state.topLevel())); dialog.setDefaultRemoteLocation(defaultURL); if (dialog.exec() != QDialog::Accepted) @@ -868,24 +868,19 @@ bool FossilPluginPrivate::managesFile(const FilePath &workingDirectory, const QS bool FossilPluginPrivate::isConfigured() const { - const Utils::FilePath binary = m_client.vcsBinary(); + const FilePath binary = m_client.vcsBinary(); if (binary.isEmpty()) return false; - const QFileInfo fi = binary.toFileInfo(); - if ( !(fi.exists() && fi.isFile() && fi.isExecutable()) ) + if (!binary.isExecutableFile()) return false; // Local repositories default path must be set and exist - const QString repoPath = m_client.settings().defaultRepoPath.value(); + const FilePath repoPath = m_client.settings().defaultRepoPath(); if (repoPath.isEmpty()) return false; - const QDir dir(repoPath); - if (!dir.exists()) - return false; - - return true; + return repoPath.isReadableDir(); } bool FossilPluginPrivate::supportsOperation(Operation operation) const diff --git a/src/plugins/fossil/fossilsettings.cpp b/src/plugins/fossil/fossilsettings.cpp index b72f6b76234..22adad3920d 100644 --- a/src/plugins/fossil/fossilsettings.cpp +++ b/src/plugins/fossil/fossilsettings.cpp @@ -42,7 +42,6 @@ FossilSettings::FossilSettings() registerAspect(&defaultRepoPath); defaultRepoPath.setSettingsKey("defaultRepoPath"); - defaultRepoPath.setDisplayStyle(StringAspect::PathChooserDisplay); defaultRepoPath.setExpectedKind(PathChooser::Directory); defaultRepoPath.setDisplayName(Tr::tr("Fossil Repositories")); defaultRepoPath.setLabelText(Tr::tr("Default path:")); @@ -55,7 +54,6 @@ FossilSettings::FossilSettings() registerAspect(&sslIdentityFile); sslIdentityFile.setSettingsKey("sslIdentityFile"); - sslIdentityFile.setDisplayStyle(StringAspect::PathChooserDisplay); sslIdentityFile.setExpectedKind(PathChooser::File); sslIdentityFile.setDisplayName(Tr::tr("SSL/TLS Identity Key")); sslIdentityFile.setLabelText(Tr::tr("SSL/TLS identity:")); diff --git a/src/plugins/fossil/fossilsettings.h b/src/plugins/fossil/fossilsettings.h index eeea6041bdc..6b925357f06 100644 --- a/src/plugins/fossil/fossilsettings.h +++ b/src/plugins/fossil/fossilsettings.h @@ -12,8 +12,8 @@ class FossilSettings : public VcsBase::VcsBaseSettings public: FossilSettings(); - Utils::StringAspect defaultRepoPath; - Utils::StringAspect sslIdentityFile; + Utils::FilePathAspect defaultRepoPath; + Utils::FilePathAspect sslIdentityFile; Utils::BoolAspect diffIgnoreAllWhiteSpace; Utils::BoolAspect diffStripTrailingCR; Utils::BoolAspect annotateShowCommitters; diff --git a/src/plugins/fossil/pullorpushdialog.cpp b/src/plugins/fossil/pullorpushdialog.cpp index cd72f2bd01b..5ebf9869d3f 100644 --- a/src/plugins/fossil/pullorpushdialog.cpp +++ b/src/plugins/fossil/pullorpushdialog.cpp @@ -15,8 +15,7 @@ #include #include -namespace Fossil { -namespace Internal { +namespace Fossil::Internal { PullOrPushDialog::PullOrPushDialog(Mode mode, QWidget *parent) : QDialog(parent) @@ -106,10 +105,9 @@ void PullOrPushDialog::setDefaultRemoteLocation(const QString &url) m_urlLineEdit->setText(url); } -void PullOrPushDialog::setLocalBaseDirectory(const QString &dir) +void PullOrPushDialog::setLocalBaseDirectory(const Utils::FilePath &dir) { - m_localPathChooser->setBaseDirectory(Utils::FilePath::fromString(dir)); + m_localPathChooser->setBaseDirectory(dir); } -} // namespace Internal -} // namespace Fossil +} // Fossil::Internal diff --git a/src/plugins/fossil/pullorpushdialog.h b/src/plugins/fossil/pullorpushdialog.h index 1d921733b69..5594947cb0d 100644 --- a/src/plugins/fossil/pullorpushdialog.h +++ b/src/plugins/fossil/pullorpushdialog.h @@ -3,6 +3,8 @@ #pragma once +#include + #include QT_BEGIN_NAMESPACE @@ -13,13 +15,10 @@ QT_END_NAMESPACE namespace Utils { class PathChooser; } -namespace Fossil { -namespace Internal { +namespace Fossil::Internal { class PullOrPushDialog : public QDialog { - Q_OBJECT - public: enum Mode { PullMode, @@ -33,7 +32,7 @@ public: bool isRememberOptionEnabled() const; bool isPrivateOptionEnabled() const; void setDefaultRemoteLocation(const QString &url); - void setLocalBaseDirectory(const QString &dir); + void setLocalBaseDirectory(const Utils::FilePath &dir); // Pull-specific options // Push-specific options @@ -47,5 +46,4 @@ private: QCheckBox *m_privateCheckBox; }; -} // namespace Internal -} // namespace Fossil +} // Fossil::Internal From 8e7ad13ad2f58469d9c11a8d60e5a4aed724cdfd Mon Sep 17 00:00:00 2001 From: Christian Kandeler Date: Tue, 23 May 2023 08:49:27 +0200 Subject: [PATCH 191/192] Fix qbs build Was broken in an impressive number of ways by latest Design Studio merge. Change-Id: I25f56827074a8c16a1a9c18884e1f63e8eaf6ef1 Reviewed-by: Christian Stenger --- .../advanceddockingsystem.qbs | 3 ++- src/libs/utils/utils.qbs | 2 +- .../qmldesignerbase/qmldesignerbase.qbs | 21 ++++++++++++++----- .../qmldesignerbase/qmldesignerbaseplugin.cpp | 2 +- .../qmldesignerbase/studio/studiostyle.h | 2 +- .../buildsystem/qmlbuildsystem.cpp | 2 +- .../qmlprojectmanager/qmlprojectmanager.qbs | 1 - tests/auto/qml/qml.qbs | 2 +- .../fileformat/fileformat.qbs | 4 +--- 9 files changed, 24 insertions(+), 15 deletions(-) diff --git a/src/libs/advanceddockingsystem/advanceddockingsystem.qbs b/src/libs/advanceddockingsystem/advanceddockingsystem.qbs index 9778f629492..751449cf15d 100644 --- a/src/libs/advanceddockingsystem/advanceddockingsystem.qbs +++ b/src/libs/advanceddockingsystem/advanceddockingsystem.qbs @@ -7,7 +7,7 @@ QtcLibrary { cpp.defines: base.concat("ADVANCEDDOCKINGSYSTEM_LIBRARY") cpp.includePaths: base.concat([".", linux.prefix]) - Depends { name: "Qt"; submodules: ["widgets", "core", "gui"] } + Depends { name: "Qt"; submodules: ["widgets", "xml"] } Depends { name: "Utils" } Group { @@ -31,6 +31,7 @@ QtcLibrary { "floatingdockcontainer.cpp", "floatingdockcontainer.h", "floatingdragpreview.cpp", "floatingdragpreview.h", "iconprovider.cpp", "iconprovider.h", + "workspace.cpp", "workspace.h", "workspacedialog.cpp", "workspacedialog.h", "workspacemodel.cpp", "workspacemodel.h", "workspaceview.cpp", "workspaceview.h", diff --git a/src/libs/utils/utils.qbs b/src/libs/utils/utils.qbs index 3196d9b34b0..298b23c1e9f 100644 --- a/src/libs/utils/utils.qbs +++ b/src/libs/utils/utils.qbs @@ -337,7 +337,7 @@ Project { "headerviewstretcher.h", "uncommentselection.cpp", "uncommentselection.h", - "uniqueobjectptr.h" + "uniqueobjectptr.h", "unixutils.cpp", "unixutils.h", "url.cpp", diff --git a/src/plugins/qmldesignerbase/qmldesignerbase.qbs b/src/plugins/qmldesignerbase/qmldesignerbase.qbs index 937f180c32c..98f4cdf5562 100644 --- a/src/plugins/qmldesignerbase/qmldesignerbase.qbs +++ b/src/plugins/qmldesignerbase/qmldesignerbase.qbs @@ -16,14 +16,25 @@ QtcPlugin { ] Group { - prefix: "utils/" + prefix: "studio/" files: [ - "designersettings.cpp", - "designersettings.h", - "qmlpuppetpaths.cpp", - "qmlpuppetpaths.h", + "studiosettingspage.cpp", + "studiosettingspage.h", + "studiostyle.cpp", + "studiostyle.h", "studioquickwidget.cpp", "studioquickwidget.h", ] } + Group { + prefix: "utils/" + files: [ + "designerpaths.cpp", + "designerpaths.h", + "designersettings.cpp", + "designersettings.h", + "qmlpuppetpaths.cpp", + "qmlpuppetpaths.h", + ] + } } diff --git a/src/plugins/qmldesignerbase/qmldesignerbaseplugin.cpp b/src/plugins/qmldesignerbase/qmldesignerbaseplugin.cpp index c9af4f22836..c59fcbb2ea1 100644 --- a/src/plugins/qmldesignerbase/qmldesignerbaseplugin.cpp +++ b/src/plugins/qmldesignerbase/qmldesignerbaseplugin.cpp @@ -3,7 +3,7 @@ #include "qmldesignerbaseplugin.h" -#include "studiosettingspage.h" +#include "studio/studiosettingspage.h" #include "studio/studiostyle.h" #include "utils/designersettings.h" diff --git a/src/plugins/qmldesignerbase/studio/studiostyle.h b/src/plugins/qmldesignerbase/studio/studiostyle.h index 4d6424cbef1..c55797354b3 100644 --- a/src/plugins/qmldesignerbase/studio/studiostyle.h +++ b/src/plugins/qmldesignerbase/studio/studiostyle.h @@ -3,7 +3,7 @@ #pragma once -#include "qmldesignerbase_global.h" +#include "../qmldesignerbase_global.h" #include diff --git a/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.cpp b/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.cpp index 86645dc8c50..2b600210736 100644 --- a/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.cpp +++ b/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.cpp @@ -2,7 +2,7 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0 #include "qmlbuildsystem.h" -#include "qmlprojectconstants.h" +#include "../qmlprojectconstants.h" #include #include diff --git a/src/plugins/qmlprojectmanager/qmlprojectmanager.qbs b/src/plugins/qmlprojectmanager/qmlprojectmanager.qbs index e4e4d0bc247..2ab9417704e 100644 --- a/src/plugins/qmlprojectmanager/qmlprojectmanager.qbs +++ b/src/plugins/qmlprojectmanager/qmlprojectmanager.qbs @@ -26,7 +26,6 @@ QtcPlugin { "qmlprojectconstants.h", "qmlprojectmanager_global.h", "qmlprojectmanagertr.h", "qmlprojectmanagerconstants.h", - "qmlprojectnodes.cpp", "qmlprojectnodes.h", "qmlprojectplugin.cpp", "qmlprojectplugin.h", "qmlprojectrunconfiguration.cpp", "qmlprojectrunconfiguration.h", project.ide_source_tree + "/src/share/3rdparty/studiofonts/studiofonts.qrc" diff --git a/tests/auto/qml/qml.qbs b/tests/auto/qml/qml.qbs index 76659a5256b..9f887bfcacd 100644 --- a/tests/auto/qml/qml.qbs +++ b/tests/auto/qml/qml.qbs @@ -7,7 +7,7 @@ Project { // "qmldesigner/qmldesigner.qbs", "qmleditor/qmleditor.qbs", "qmljssimplereader/qmljssimplereader.qbs", - "qmlprojectmanager/qmlprojectmanager.qbs", +// "qmlprojectmanager/qmlprojectmanager.qbs", "qrcparser/qrcparser.qbs", "reformatter/reformatter.qbs", "persistenttrie/persistenttrie.qbs" diff --git a/tests/auto/qml/qmlprojectmanager/fileformat/fileformat.qbs b/tests/auto/qml/qmlprojectmanager/fileformat/fileformat.qbs index 07ed3a924e6..7abe08bf40b 100644 --- a/tests/auto/qml/qmlprojectmanager/fileformat/fileformat.qbs +++ b/tests/auto/qml/qmlprojectmanager/fileformat/fileformat.qbs @@ -4,7 +4,7 @@ QtcAutotest { name: "QmlProjectManager file format autotest" Depends { name: "QmlJS" } Depends { name: "Utils" } - property path fileFormatDir: project.ide_source_tree + "/src/plugins/qmlprojectmanager/fileformat" + property path fileFormatDir: project.ide_source_tree + "/src/plugins/qmlprojectmanager/buildsystem/projectitem" files: "tst_fileformat.cpp" Group { name: "Files from QmlProjectManager" @@ -12,8 +12,6 @@ QtcAutotest { files: [ "filefilteritems.cpp", "filefilteritems.h", - "qmlprojectfileformat.cpp", - "qmlprojectfileformat.h", "qmlprojectitem.cpp", "qmlprojectitem.h", ] From 590a6904b10ddd3538733c927b7a2a1b49c7e8a2 Mon Sep 17 00:00:00 2001 From: Christian Stenger Date: Tue, 23 May 2023 09:26:30 +0200 Subject: [PATCH 192/192] QmlDesigner: Fix qbs build and building with Qt6.2 Change-Id: Ic114c9eb830eed5cf0bef890007408694453791e Reviewed-by: Christian Kandeler --- .../studio/studiosettingspage.cpp | 8 +- .../qmldesignerbase/utils/designerpaths.cpp | 4 +- .../buildsystem/qmlbuildsystem.cpp | 2 + src/plugins/qmlprojectmanager/qmlproject.cpp | 2 +- .../fileformat/fileformat.qbs | 2 + .../fileformat/tst_fileformat.cpp | 107 +++++++----------- 6 files changed, 52 insertions(+), 73 deletions(-) diff --git a/src/plugins/qmldesignerbase/studio/studiosettingspage.cpp b/src/plugins/qmldesignerbase/studio/studiosettingspage.cpp index d88c4cf409b..d94efbd666b 100644 --- a/src/plugins/qmldesignerbase/studio/studiosettingspage.cpp +++ b/src/plugins/qmldesignerbase/studio/studiosettingspage.cpp @@ -178,15 +178,15 @@ void StudioSettingsPage::apply() QSettings *s = Core::ICore::settings(); const QString value = m_pathChooserExamples->filePath().toString(); - if (s->value(Paths::exampleDownloadPath, false).toString() != value) { - s->setValue(Paths::exampleDownloadPath, value); + if (s->value(Paths::exampleDownloadPath.toString(), false).toString() != value) { + s->setValue(Paths::exampleDownloadPath.toString(), value); emit examplesDownloadPathChanged(value); } const QString bundlesPath = m_pathChooserBundles->filePath().toString(); - if (s->value(Paths::bundlesDownloadPath).toString() != bundlesPath) { - s->setValue(Paths::bundlesDownloadPath, bundlesPath); + if (s->value(Paths::bundlesDownloadPath.toString()).toString() != bundlesPath) { + s->setValue(Paths::bundlesDownloadPath.toString(), bundlesPath); emit bundlesDownloadPathChanged(bundlesPath); const QString restartText = tr("Changing bundle path will take effect after restart."); diff --git a/src/plugins/qmldesignerbase/utils/designerpaths.cpp b/src/plugins/qmldesignerbase/utils/designerpaths.cpp index d4ae2a46456..53b4b7ea37e 100644 --- a/src/plugins/qmldesignerbase/utils/designerpaths.cpp +++ b/src/plugins/qmldesignerbase/utils/designerpaths.cpp @@ -31,14 +31,14 @@ Utils::FilePath defaultBundlesPath() QString examplesPathSetting() { return Core::ICore::settings() - ->value(exampleDownloadPath, defaultExamplesPath().toString()) + ->value(exampleDownloadPath.toString(), defaultExamplesPath().toString()) .toString(); } QString bundlesPathSetting() { return Core::ICore::settings() - ->value(bundlesDownloadPath, defaultBundlesPath().toString()) + ->value(bundlesDownloadPath.toString(), defaultBundlesPath().toString()) .toString(); } diff --git a/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.cpp b/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.cpp index 2b600210736..81a157f1b6f 100644 --- a/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.cpp +++ b/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.cpp @@ -37,6 +37,8 @@ #include "texteditor/textdocument.h" +#include + using namespace ProjectExplorer; namespace QmlProjectManager { diff --git a/src/plugins/qmlprojectmanager/qmlproject.cpp b/src/plugins/qmlprojectmanager/qmlproject.cpp index ce2f363cd41..31fa3429d21 100644 --- a/src/plugins/qmlprojectmanager/qmlproject.cpp +++ b/src/plugins/qmlprojectmanager/qmlproject.cpp @@ -248,7 +248,7 @@ bool QmlProject::allowOnlySingleProject() { QSettings *settings = Core::ICore::settings(); auto key = "QML/Designer/AllowMultipleProjects"; - return !settings->value(key, false).toBool(); + return !settings->value(QString::fromUtf8(key), false).toBool(); } } // namespace QmlProjectManager diff --git a/tests/auto/qml/qmlprojectmanager/fileformat/fileformat.qbs b/tests/auto/qml/qmlprojectmanager/fileformat/fileformat.qbs index 7abe08bf40b..f32bd21c05d 100644 --- a/tests/auto/qml/qmlprojectmanager/fileformat/fileformat.qbs +++ b/tests/auto/qml/qmlprojectmanager/fileformat/fileformat.qbs @@ -10,6 +10,8 @@ QtcAutotest { name: "Files from QmlProjectManager" prefix: product.fileFormatDir + '/' files: [ + "converters.cpp", + "converters.h", "filefilteritems.cpp", "filefilteritems.h", "qmlprojectitem.cpp", diff --git a/tests/auto/qml/qmlprojectmanager/fileformat/tst_fileformat.cpp b/tests/auto/qml/qmlprojectmanager/fileformat/tst_fileformat.cpp index bd7695b53db..b8b5f11dee7 100644 --- a/tests/auto/qml/qmlprojectmanager/fileformat/tst_fileformat.cpp +++ b/tests/auto/qml/qmlprojectmanager/fileformat/tst_fileformat.cpp @@ -2,8 +2,6 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "qmlprojectitem.h" -#include "filefilteritems.h" -#include "qmlprojectfileformat.h" #include @@ -43,29 +41,27 @@ tst_FileFormat::tst_FileFormat() const QString testDataDir = QLatin1String(SRCDIR "/data"); const FilePath testDataDirPath = FilePath::fromString(testDataDir); -static std::unique_ptr loadQmlProject(QString name, QString *error) +static std::unique_ptr loadQmlProject(QString name) { - return QmlProjectFileFormat::parseProjectFile( - Utils::FilePath::fromString(testDataDir).pathAppended(name + ".qmlproject"), error); + return std::unique_ptr(new QmlProjectItem( + testDataDirPath.pathAppended(name + ".qmlproject"))); } void tst_FileFormat::testFileFilter() { - QString error; - // // Search for qml files in directory + subdirectories // { - auto project = loadQmlProject(QLatin1String("testFileFilter1"), &error); + auto project = loadQmlProject(QLatin1String("testFileFilter1")); QVERIFY(project); - QVERIFY(error.isEmpty()); + QVERIFY(!project->project().isEmpty()); - project->setSourceDirectory(testDataDirPath); - - QStringList expectedFiles(QStringList() << testDataDir + "/file1.qml" - << testDataDir + "/file2.qml" - << testDataDir + "/subdir/file3.qml"); + FilePaths expectedFiles{ + testDataDirPath / "file1.qml", + testDataDirPath / "file2.qml", + testDataDirPath / "subdir/file3.qml" + }; COMPARE_AS_SETS(project->files(), expectedFiles); } @@ -73,14 +69,11 @@ void tst_FileFormat::testFileFilter() // search for all qml files in directory // { - auto project = loadQmlProject(QLatin1String("testFileFilter2"), &error); + auto project = loadQmlProject(QLatin1String("testFileFilter2")); QVERIFY(project); - QVERIFY(error.isEmpty()); + QVERIFY(!project->project().isEmpty()); - project->setSourceDirectory(testDataDirPath); - - QStringList expectedFiles(QStringList() << testDataDir + "/file1.qml" - << testDataDir + "/file2.qml"); + FilePaths expectedFiles{ testDataDirPath / "file1.qml", testDataDirPath / "file2.qml" }; COMPARE_AS_SETS(project->files(), expectedFiles); } @@ -88,13 +81,11 @@ void tst_FileFormat::testFileFilter() // search for all qml files in subdirectory // { - auto project = loadQmlProject(QLatin1String("testFileFilter3"), &error); + auto project = loadQmlProject(QLatin1String("testFileFilter3")); QVERIFY(project); - QVERIFY(error.isEmpty()); + QVERIFY(!project->project().isEmpty()); - project->setSourceDirectory(testDataDirPath); - - QStringList expectedFiles(QStringList() << testDataDir + "/subdir/file3.qml"); + FilePaths expectedFiles{ testDataDirPath / "subdir/file3.qml" }; COMPARE_AS_SETS(project->files(), expectedFiles); } @@ -102,15 +93,15 @@ void tst_FileFormat::testFileFilter() // multiple entries // { - auto project = loadQmlProject(QLatin1String("testFileFilter4"), &error); + auto project = loadQmlProject(QLatin1String("testFileFilter4")); QVERIFY(project); - QVERIFY(error.isEmpty()); + QVERIFY(!project->project().isEmpty()); - project->setSourceDirectory(testDataDirPath); - - QStringList expectedFiles(QStringList() << testDataDir + "/file1.qml" - << testDataDir + "/file2.qml" - << testDataDir + "/subdir/file3.qml"); + FilePaths expectedFiles{ + testDataDirPath / "file1.qml", + testDataDirPath / "file2.qml", + testDataDirPath / "/subdir/file3.qml" + }; QCOMPARE(project->files().size(), 3); COMPARE_AS_SETS(project->files(), expectedFiles); } @@ -119,14 +110,11 @@ void tst_FileFormat::testFileFilter() // include specific list // { - auto project = loadQmlProject(QLatin1String("testFileFilter5"), &error); + auto project = loadQmlProject(QLatin1String("testFileFilter5")); QVERIFY(project); - QVERIFY(error.isEmpty()); + QVERIFY(!project->project().isEmpty()); - project->setSourceDirectory(testDataDirPath); - - QStringList expectedFiles(QStringList() << testDataDir + "/file1.qml" - << testDataDir + "/file2.qml"); + FilePaths expectedFiles{ testDataDirPath / "file1.qml", testDataDirPath / "file2.qml" }; COMPARE_AS_SETS(project->files(), expectedFiles); } @@ -134,13 +122,11 @@ void tst_FileFormat::testFileFilter() // include specific list // { - auto project = loadQmlProject(QLatin1String("testFileFilter6"), &error); + auto project = loadQmlProject(QLatin1String("testFileFilter6")); QVERIFY(project); - QVERIFY(error.isEmpty()); + QVERIFY(!project->project().isEmpty()); - project->setSourceDirectory(testDataDirPath); - - QStringList expectedFiles(QStringList() << testDataDir + "/image.gif"); + FilePaths expectedFiles{ testDataDirPath / "image.gif" }; COMPARE_AS_SETS(project->files(), expectedFiles); } @@ -148,13 +134,11 @@ void tst_FileFormat::testFileFilter() // use wildcards // { - auto project = loadQmlProject(QLatin1String("testFileFilter7"), &error); + auto project = loadQmlProject(QLatin1String("testFileFilter7")); QVERIFY(project); - QVERIFY(error.isEmpty()); + QVERIFY(!project->project().isEmpty()); - project->setSourceDirectory(testDataDirPath); - - QStringList expectedFiles(QStringList() << testDataDir + "/image.gif"); + FilePaths expectedFiles{ testDataDirPath / "image.gif" }; COMPARE_AS_SETS(project->files(), expectedFiles); } @@ -162,28 +146,23 @@ void tst_FileFormat::testFileFilter() // use Files element (1.1) // { - auto project = loadQmlProject(QLatin1String("testFileFilter8"), &error); + auto project = loadQmlProject(QLatin1String("testFileFilter8")); QVERIFY(project); - QVERIFY(error.isEmpty()); + QVERIFY(!project->project().isEmpty()); - project->setSourceDirectory(testDataDirPath); - - QStringList expectedFiles(QStringList() << testDataDir + "/image.gif"); + FilePaths expectedFiles{ testDataDirPath / "image.gif" }; COMPARE_AS_SETS(project->files(), expectedFiles); } } void tst_FileFormat::testMatchesFile() { - QString error; // // search for qml files in local directory // - auto project = loadQmlProject(QLatin1String("testMatchesFile"), &error); + auto project = loadQmlProject(QLatin1String("testMatchesFile")); QVERIFY(project); - QVERIFY(error.isEmpty()); - - project->setSourceDirectory(testDataDirPath); + QVERIFY(!project->project().isEmpty()); QVERIFY(project->matchesFile(testDataDir + "/file1.qml")); QVERIFY(project->matchesFile(testDataDir + "/notyetexistingfile.qml")); @@ -194,15 +173,12 @@ void tst_FileFormat::testMatchesFile() void tst_FileFormat::testLibraryPaths() { - QString error; // // search for qml files in local directory // - auto project = loadQmlProject(QLatin1String("testLibraryPaths"), &error); + auto project = loadQmlProject(QLatin1String("testLibraryPaths")); QVERIFY(project); - QVERIFY(error.isEmpty()); - - project->setSourceDirectory(testDataDirPath); + QVERIFY(!project->project().isEmpty()); const QDir base(testDataDir); const QStringList expectedPaths({base.relativeFilePath(SRCDIR "/otherLibrary"), @@ -212,13 +188,12 @@ void tst_FileFormat::testLibraryPaths() void tst_FileFormat::testMainFile() { - QString error; // // search for qml files in local directory // - auto project = loadQmlProject(QLatin1String("testMainFile"), &error); + auto project = loadQmlProject(QLatin1String("testMainFile")); QVERIFY(project); - QVERIFY(error.isEmpty()); + QVERIFY(!project->project().isEmpty()); QCOMPARE(project->mainFile(), QString("file1.qml")); }

bY>~ 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 literal 0 HcmV?d00001 diff --git a/doc/qtdesignstudio/src/mcus/qtdesignstudio-on-mcus.qdoc b/doc/qtdesignstudio/src/mcus/qtdesignstudio-on-mcus.qdoc new file mode 100644 index 00000000000..b8409d9e442 --- /dev/null +++ b/doc/qtdesignstudio/src/mcus/qtdesignstudio-on-mcus.qdoc @@ -0,0 +1,92 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only + +/*! + \previouspage creator-editor-external.html + \page studio-on-mcus.html + \nextpage studio-help.html + + \title \QDS on MCUs + + \QMCU is a comprehensive framework that supports various hardware ecosystems + and platforms. One of the most important libraries provided by the \QMCU + framework is \QUL (QUL), a lightweight implementation of the Qt Quick + framework. \QUL provides a QML API and an efficient graphics rendering engine + that has a low memory footprint and is optimized for MCUs and other + resource-constrained devices. + + In addition to a lightweight graphics framework, \QMCU offers a toolkit that + enables you to design, develop, and deploy graphical user interfaces (GUI) + on microcontrollers (MCU). Also, it lets you run the applications either + on BareMetal or a real-time operating system (RTOS). + + For more information on \QMCU, see \l {\QMCU documentation}. + + \section1 Designing application UIs for MCU devices with \QDS + + As a technical artist or a designer you can use specialized UI design tools, + such as Adobe Photoshop, Sketch, Figma, Blender, or Maya to create the + original UI design files for your MCU application. After the initial design + work, export your design from the design tools, and import your 2D and 3D UI + design assets into \QDS, which can convert them into code for developers. + For more information on managing the original assets created with + specialized UI design tools, see \l {Asset Creation with Other Tools}. + + Once your UI design assets are in \QDS, use it to \l {Wireframing} {wireframe} + your MCU application, to visualize its structure. To modify the look and feel + of your UI further, utilize the preset UI components available in \QDS. + + For an example on how to create a UI that runs both on the desktop and + on MCUs, see \l {Washing Machine UI}. For step-by-step instructions on how + to use \QDS to design a UI for a specific MCU target device, see: + + \list + \li \l {Designing a UI for Infineon Traveo II} + \li \l {Designing a UI for NXP i.MX RT1170} + \li \l {Designing a UI for Renesas RA6M3G} + \li \l {Designing a UI for Renesas RH850-D1M1A} + \endlist + + \section1 Developing applications for MCU devices with \QDS + + As a GUI/application developer, use \QDS to bring your designs to life. Add + further functionality to your applications and utilize the \l {Prototyping} + {prototyping} features of \QDS to simulate and validate interactions and + their dynamic behavior. + + You can also test, preview, and fine-tune your designs to pixel-perfection + live on the desktop or on an actual MCU target device. For more information, + see \l {Validating with Target Hardware}. + + \image qds-mcu-target-deployment.png + + \QDS enables designers and developers to work together on common projects to + develop applications. Designers can use the views in the Design mode to modify + UI files (.ui.qml), whereas developers can use Qt Creator to work on the Qt + Quick (.qml) and other files that are needed to implement the application + logic and to prepare the application for production. For more information, + see \l {Implementing Applications}. + + \section1 Connecting MCUs with Qt Creator + + \l {Connecting MCUs} {Connect MCU boards} to a development host to + build applications for them using the GNU Arm Embedded GCC compiler, libraries, + and other GNU tools necessary for BareMetal software development on devices + based on the Arm Cortex-M processors. Deploy the applications on MCUs to run + and debug them using Qt Creator. + + The toolchains are available for cross-compilation on Microsoft Windows, + Linux, and macOS. However, the \QMCU SDK is currently only available for + Windows and Linux. + + For more information on how to manage the complete cycle of developing \QMCU + applications using Qt tools, see: + + \list + \li \l {Infineon Traveo II quick start guide} + \li \l {NXP i.MX RT1170 quick start guide} + \li \l {Renesas EK-RA6M3G quick start guide} + \li \l {Renesas RH850-D1M1A quick start guide} + \endlist + +*/ diff --git a/doc/qtdesignstudio/src/qtdesignstudio-help-overview.qdoc b/doc/qtdesignstudio/src/qtdesignstudio-help-overview.qdoc index 772885ca385..d63f049659f 100644 --- a/doc/qtdesignstudio/src/qtdesignstudio-help-overview.qdoc +++ b/doc/qtdesignstudio/src/qtdesignstudio-help-overview.qdoc @@ -3,7 +3,7 @@ /*! \page studio-help.html - \previouspage creator-editor-external.html + \previouspage studio-on-mcus.html \nextpage creator-help.html \title Help diff --git a/doc/qtdesignstudio/src/qtdesignstudio-toc.qdoc b/doc/qtdesignstudio/src/qtdesignstudio-toc.qdoc index 89a93a4d435..4a5bb3c3f21 100644 --- a/doc/qtdesignstudio/src/qtdesignstudio-toc.qdoc +++ b/doc/qtdesignstudio/src/qtdesignstudio-toc.qdoc @@ -263,6 +263,7 @@ \li \l{Converting Qt 5 Projects into Qt 6 Projects} \li \l{Converting UI Projects to Applications} \li \l{Using External Tools} + \li \l{\QDS on MCUs} \endlist \li \l Help \list diff --git a/doc/qtdesignstudio/src/qtdesignstudio.qdoc b/doc/qtdesignstudio/src/qtdesignstudio.qdoc index e9f302ce859..adf772987d4 100644 --- a/doc/qtdesignstudio/src/qtdesignstudio.qdoc +++ b/doc/qtdesignstudio/src/qtdesignstudio.qdoc @@ -81,6 +81,7 @@ \li \l{Converting Qt 5 Projects into Qt 6 Projects} \li \l{Converting UI Projects to Applications} \li \l{Using External Tools} + \li \l{\QDS on MCUs} \endlist \li \b {\l Help} \list From eafe43399d29502ecd06be6976a4f2509b20c9ee Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Fri, 21 Apr 2023 15:47:50 +0200 Subject: [PATCH 087/192] QmlDesigner: Remove duplicates Change-Id: I5b2d8433c0aca286ec530f237750ba660c409a48 Reviewed-by: Aleksei German Reviewed-by: Marco Bubke --- .../projectstorage/modulescanner.cpp | 9 +++---- tests/unit/unittest/modulescanner-test.cpp | 25 +++++++++++++++++++ 2 files changed, 28 insertions(+), 6 deletions(-) diff --git a/src/plugins/qmldesigner/designercore/projectstorage/modulescanner.cpp b/src/plugins/qmldesigner/designercore/projectstorage/modulescanner.cpp index 4a6adfa10d5..2210f0732b3 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/modulescanner.cpp +++ b/src/plugins/qmldesigner/designercore/projectstorage/modulescanner.cpp @@ -51,8 +51,6 @@ void ModuleScanner::scan([[maybe_unused]] std::string_view modulePath) #ifdef QDS_HAS_QMLPRIVATE QDirIterator dirIterator{QString::fromUtf8(modulePath), QDir::Dirs, QDirIterator::Subdirectories}; - QMap moduleNames; - while (dirIterator.hasNext()) { auto directoryPath = dirIterator.next(); QString qmldirPath = directoryPath + "/qmldir"; @@ -72,14 +70,13 @@ void ModuleScanner::scan([[maybe_unused]] std::string_view modulePath) if (moduleName.isEmpty() || m_skip(moduleName)) continue; - if (moduleNames.contains(moduleName)) - continue; - - moduleNames.insert(moduleName, true); m_modules.push_back( Import::createLibraryImport(moduleName, createVersion(parser.components()))); } } + + std::sort(m_modules.begin(), m_modules.end()); + m_modules.erase(std::unique(m_modules.begin(), m_modules.end()), m_modules.end()); #endif } diff --git a/tests/unit/unittest/modulescanner-test.cpp b/tests/unit/unittest/modulescanner-test.cpp index b98fc3cdb3b..92d7959916a 100644 --- a/tests/unit/unittest/modulescanner-test.cpp +++ b/tests/unit/unittest/modulescanner-test.cpp @@ -21,6 +21,15 @@ auto VersionProperty(const Matcher &matcher) return Property(&QmlDesigner::Import::version, matcher); } +MATCHER(HasDuplicates, std::string(negation ? "hasn't duplicates" : "has dublicates")) +{ + auto values = arg; + std::sort(values.begin(), values.begin()); + auto found = std::adjacent_find(values.begin(), values.end()); + + return found != values.end(); +} + class ModuleScanner : public testing::Test { protected: @@ -70,4 +79,20 @@ TEST_F(ModuleScanner, Version) ASSERT_THAT(scanner.modules(), ElementsAre(AllOf(UrlProperty("Example"), VersionProperty("1.3")))); } +TEST_F(ModuleScanner, Duplicates) +{ + scanner.scan(QStringList{QT6_INSTALL_PREFIX}); + + ASSERT_THAT(scanner.modules(), Not(HasDuplicates())); +} + +TEST_F(ModuleScanner, DontAddModulesAgain) +{ + scanner.scan(QStringList{QT6_INSTALL_PREFIX}); + + scanner.scan(QStringList{QT6_INSTALL_PREFIX}); + + ASSERT_THAT(scanner.modules(), Not(HasDuplicates())); +} + } // namespace From 45cf905b295b311c614e8c62e0f9a42f97c98fe5 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Tue, 25 Apr 2023 11:30:58 +0200 Subject: [PATCH 088/192] QmlDesigner: Fix version scanning Switch was missing. Change-Id: I5d87e624f698b9f59fe57cf49cafef4e6d08eca2 Reviewed-by: Marco Bubke Reviewed-by: Aleksei German --- .../designercore/projectstorage/modulescanner.cpp | 7 +++++-- tests/unit/unittest/modulescanner-test.cpp | 13 +++++++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/plugins/qmldesigner/designercore/projectstorage/modulescanner.cpp b/src/plugins/qmldesigner/designercore/projectstorage/modulescanner.cpp index 2210f0732b3..56822dce06d 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/modulescanner.cpp +++ b/src/plugins/qmldesigner/designercore/projectstorage/modulescanner.cpp @@ -70,8 +70,11 @@ void ModuleScanner::scan([[maybe_unused]] std::string_view modulePath) if (moduleName.isEmpty() || m_skip(moduleName)) continue; - m_modules.push_back( - Import::createLibraryImport(moduleName, createVersion(parser.components()))); + QString version = m_versionScanning == VersionScanning::Yes + ? createVersion(parser.components()) + : QString{}; + + m_modules.push_back(Import::createLibraryImport(moduleName, version)); } } diff --git a/tests/unit/unittest/modulescanner-test.cpp b/tests/unit/unittest/modulescanner-test.cpp index 92d7959916a..062256408e8 100644 --- a/tests/unit/unittest/modulescanner-test.cpp +++ b/tests/unit/unittest/modulescanner-test.cpp @@ -79,6 +79,19 @@ TEST_F(ModuleScanner, Version) ASSERT_THAT(scanner.modules(), ElementsAre(AllOf(UrlProperty("Example"), VersionProperty("1.3")))); } +TEST_F(ModuleScanner, NoVersion) +{ + QmlDesigner::ModuleScanner scanner{[](QStringView moduleName) { + return moduleName.endsWith(u"impl"); + }, + QmlDesigner::VersionScanning::No}; + + scanner.scan(QStringList{TESTDATA_DIR "/modulescanner"}); + + ASSERT_THAT(scanner.modules(), + ElementsAre(AllOf(UrlProperty("Example"), VersionProperty(QString{})))); +} + TEST_F(ModuleScanner, Duplicates) { scanner.scan(QStringList{QT6_INSTALL_PREFIX}); From 5cd83aa2a7ff997d0ef263c7ace806f198305409 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Tue, 25 Apr 2023 17:27:31 +0300 Subject: [PATCH 089/192] QmlDesigner: Fix macOS path for qlmdenoiser Change-Id: I6f8931dc59615d736c42014e048301b4d1c58d74 Reviewed-by: Mahmoud Badri --- .../qml2puppet/instances/qt5bakelightsnodeinstanceserver.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/qml2puppet/qml2puppet/instances/qt5bakelightsnodeinstanceserver.cpp b/src/tools/qml2puppet/qml2puppet/instances/qt5bakelightsnodeinstanceserver.cpp index d7a8b313acc..79b3a4f920e 100644 --- a/src/tools/qml2puppet/qml2puppet/instances/qt5bakelightsnodeinstanceserver.cpp +++ b/src/tools/qml2puppet/qml2puppet/instances/qt5bakelightsnodeinstanceserver.cpp @@ -151,7 +151,7 @@ void Qt5BakeLightsNodeInstanceServer::runDenoiser() #if defined(Q_OS_WIN) binPath += "/qlmdenoiser.exe"; #elif defined(Q_OS_MACOS) - // TODO: What is the path in mac? + binPath += "/qlmdenoiser.app/Contents/MacOS/qlmdenoiser"; #else binPath += "/qlmdenoiser"; #endif From b929cbfd64bba1950b2252576e2c620ad03485d8 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Tue, 25 Apr 2023 18:57:07 +0200 Subject: [PATCH 090/192] QmlDesigner: Fix QmlTypesParser Change-Id: I826e1a37747a51602f3f7a8ea675320787080272 Reviewed-by: Tim Jenssen --- .../projectstorage/qmltypesparser.cpp | 5 ++--- .../projectstorage/qmltypesparser.h | 19 +++++++++---------- .../qmldesigner/qmldesignerprojectmanager.cpp | 2 +- tests/unit/unittest/qmltypesparser-test.cpp | 2 +- 4 files changed, 13 insertions(+), 15 deletions(-) diff --git a/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.cpp b/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.cpp index ac1761318d0..aebe57a14b0 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.cpp +++ b/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.cpp @@ -4,11 +4,10 @@ #include "qmltypesparser.h" #include "projectstorage.h" -#include "sourcepathcache.h" #include -#ifdef QDS_HAS_QMLPRIVATE +#ifdef HasQQmlJSTypeDescriptionReader #include #include #endif @@ -20,7 +19,7 @@ namespace QmlDesigner { -#if defined(QDS_HAS_QMLPRIVATE) && QT_VERSION >= QT_VERSION_CHECK(6, 5, 0) +#ifdef HasQQmlJSTypeDescriptionReader namespace QmlDom = QQmlJS::Dom; diff --git a/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.h b/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.h index 522e5d3292e..0af85be7570 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.h @@ -10,6 +10,10 @@ namespace Sqlite { class Database; } +#if defined(QDS_HAS_QMLPRIVATE) && QT_VERSION >= QT_VERSION_CHECK(6, 5, 0) +#define HasQQmlJSTypeDescriptionReader +#endif + namespace QmlDesigner { template @@ -22,16 +26,13 @@ class QmlTypesParser : public QmlTypesParserInterface { public: using ProjectStorage = QmlDesigner::ProjectStorage; - using PathCache = QmlDesigner::SourcePathCache; -#ifdef QDS_HAS_QMLPRIVATE - QmlTypesParser(PathCache &pathCache, ProjectStorage &storage) - : m_pathCache{pathCache} - , m_storage{storage} +#ifdef HasQQmlJSTypeDescriptionReader + QmlTypesParser(ProjectStorage &storage) + : m_storage{storage} {} #else - QmlTypesParser(PathCache &, ProjectStorage &) - {} + QmlTypesParser(ProjectStorage &) {} #endif void parse(const QString &sourceContent, @@ -40,9 +41,7 @@ public: const Storage::Synchronization::ProjectData &projectData) override; private: - // m_pathCache and m_storage are only used when compiled for QDS -#ifdef QDS_HAS_QMLPRIVATE - PathCache &m_pathCache; +#ifdef HasQQmlJSTypeDescriptionReader ProjectStorage &m_storage; #endif }; diff --git a/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp b/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp index c1003915813..ef36b3957a1 100644 --- a/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp +++ b/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp @@ -185,7 +185,7 @@ public: FileSystem fileSystem{pathCache}; FileStatusCache fileStatusCache{fileSystem}; QmlDocumentParser qmlDocumentParser{storage, pathCache}; - QmlTypesParser qmlTypesParser{pathCache, storage}; + QmlTypesParser qmlTypesParser{storage}; ProjectStoragePathWatcher pathWatcher{pathCache, fileSystem, &updater}; ProjectPartId projectPartId; diff --git a/tests/unit/unittest/qmltypesparser-test.cpp b/tests/unit/unittest/qmltypesparser-test.cpp index 405eec57df6..cc4fe6def47 100644 --- a/tests/unit/unittest/qmltypesparser-test.cpp +++ b/tests/unit/unittest/qmltypesparser-test.cpp @@ -149,7 +149,7 @@ protected: QmlDesigner::ProjectStorage storage{database, database.isInitialized()}; QmlDesigner::SourcePathCache> sourcePathCache{ storage}; - QmlDesigner::QmlTypesParser parser{sourcePathCache, storage}; + QmlDesigner::QmlTypesParser parser{storage}; Storage::Imports imports; Storage::Types types; SourceId qmltypesFileSourceId{sourcePathCache.sourceId("path/to/types.qmltypes")}; From f640e83a0448534eaad1f50e58d77ed811e31470 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Tue, 25 Apr 2023 11:00:36 +0200 Subject: [PATCH 091/192] QmlDesigner: Use RESOURCE_PREFIX for QmlRuntime.QmlConfiguration See: QTBUG-104554 Task-number: QDS-9342 Change-Id: I3e097a2c4dd27673ec2a9a7d3e3e9b1e82a722ff Reviewed-by: Tim Jenssen --- src/tools/qml2puppet/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/src/tools/qml2puppet/CMakeLists.txt b/src/tools/qml2puppet/CMakeLists.txt index 1d8dc8691b4..9f46e49ef66 100644 --- a/src/tools/qml2puppet/CMakeLists.txt +++ b/src/tools/qml2puppet/CMakeLists.txt @@ -236,6 +236,7 @@ if (Qt6_VERSION VERSION_GREATER_EQUAL 6.4.0) qt_add_qml_module(qml2puppet URI QmlRuntime.QmlConfiguration VERSION 1.0 + RESOURCE_PREFIX "/qt-project.org" ) if (QTC_STATIC_BUILD) qt_import_qml_plugins(qml2puppet PATH_TO_SCAN ${SRCDIR}) From 463d0e03ae92184fad18bf7b313d6f44b132e7e7 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Tue, 25 Apr 2023 14:38:22 +0200 Subject: [PATCH 092/192] QmlDesigner: Update possible modules for import changes Maybe we should rename possibleImports in the module to modules() to make it clear. Task-number: QDS-9749 Change-Id: I46675d7eaca1f86ea4fe6e5841273e78a8ea2508 Reviewed-by: Thomas Hartmann Reviewed-by: Reviewed-by: Miikka Heikkinen --- .../qmldesigner/components/itemlibrary/itemlibraryview.cpp | 1 + .../qmldesigner/components/itemlibrary/itemlibrarywidget.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp index 4dd5be177f5..5afc95564a3 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp @@ -81,6 +81,7 @@ void ItemLibraryView::importsChanged(const Imports &addedImports, const Imports document->addSubcomponentManagerImport(import); updateImports(); + m_widget->updatePossibleImports(model()->possibleImports()); // TODO: generalize the logic below to allow adding/removing any Qml component when its import is added/removed bool simulinkImportAdded = std::any_of(addedImports.cbegin(), addedImports.cend(), [](const Import &import) { diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp index 112453fb8d4..127bd4af534 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp @@ -348,7 +348,7 @@ void ItemLibraryWidget::updateModel() void ItemLibraryWidget::updatePossibleImports(const Imports &possibleImports) { - m_addModuleModel->update(possibleImports); + m_addModuleModel->update(difference(possibleImports, m_model->imports())); delayedUpdateModel(); } From ccf17b02c8a0dbe9338da2c939d432f1f9e47eea Mon Sep 17 00:00:00 2001 From: Burak Hancerli Date: Tue, 25 Apr 2023 15:47:52 +0200 Subject: [PATCH 093/192] QmlDesigner: Fix project file getter/setter issues Task-number: QDS-9781 Change-Id: I2df8cca1c4e5cd10563621b849fb54b29c0d155d Reviewed-by: Marco Bubke Reviewed-by: Reviewed-by: Thomas Hartmann --- .../buildsystem/projectitem/converters.cpp | 14 +++++--- .../projectitem/qmlprojectitem.cpp | 35 +++++++++++++++++-- .../buildsystem/projectitem/qmlprojectitem.h | 11 +++++- .../buildsystem/qmlbuildsystem.cpp | 17 ++++++++- .../buildsystem/qmlbuildsystem.h | 4 +++ .../converter/test-set-1/testfile.qmltojson | 1 + .../converter/test-set-2/testfile.jsontoqml | 2 +- .../converter/test-set-2/testfile.qmltojson | 3 +- .../converter/test-set-3/testfile.jsontoqml | 2 +- .../converter/test-set-3/testfile.qmltojson | 3 +- .../converter/test-set-4/testfile.jsontoqml | 2 +- .../converter/test-set-4/testfile.qmltojson | 1 + .../converter/test-set-5/testfile.qmltojson | 1 + .../projectitem/test-getters.cpp | 18 ++++++++-- .../projectitem/test-setters.cpp | 25 +++++++++---- 15 files changed, 116 insertions(+), 23 deletions(-) diff --git a/src/plugins/qmlprojectmanager/buildsystem/projectitem/converters.cpp b/src/plugins/qmlprojectmanager/buildsystem/projectitem/converters.cpp index 645335f8b9c..aa0abf8ffe2 100644 --- a/src/plugins/qmlprojectmanager/buildsystem/projectitem/converters.cpp +++ b/src/plugins/qmlprojectmanager/buildsystem/projectitem/converters.cpp @@ -135,7 +135,7 @@ QString jsonToQmlProject(const QJsonObject &rootObject) appendBreak(); appendString("qdsVersion", versionConfig["designStudio"].toString()); appendString("quickVersion", versionConfig["qtQuick"].toString()); - appendBool("qt6Project", versionConfig["qtQuick"].toString().startsWith("6.")); + appendBool("qt6Project", versionConfig["qt"].toString() == "6"); appendBool("qtForMCUs", rootObject["mcuConfig"].toObject().isEmpty()); appendBreak(); appendBool("multilanguageSupport", languageConfig["multiLanguageSupport"].toBool()); @@ -217,6 +217,7 @@ QJsonObject qmlProjectTojson(const Utils::FilePath &projectFile) for (const QString &propName : rootNode->propertyNames()) { QJsonObject *currentObj = &rootObject; QString objKey = propName; + QJsonValue value = rootNode->property(propName).value.toJsonValue(); if (propName.contains("language", Qt::CaseInsensitive)) { currentObj = &languageObject; @@ -240,11 +241,12 @@ QJsonObject qmlProjectTojson(const Utils::FilePath &projectFile) currentObj = &mcuObject; objKey = "mcuEnabled"; } else if (propName.contains("qt6project", Qt::CaseInsensitive)) { - // we are skipping these ones in the new json format - continue; + currentObj = &versionObject; + objKey = "qt"; + value = rootNode->property(propName).value.toBool() ? "6" : "5"; } - currentObj->insert(objKey, rootNode->property(propName).value.toJsonValue()); + currentObj->insert(objKey, value); } // add missing non-object props if any @@ -252,6 +254,10 @@ QJsonObject qmlProjectTojson(const Utils::FilePath &projectFile) runConfigObject.insert("fileSelectors", QJsonArray{}); } + if (!versionObject.contains("qt")) { + versionObject.insert("qt", "5"); + } + // convert the the object props for (const QmlJS::SimpleReaderNode::Ptr &childNode : rootNode->children()) { if (childNode->name().contains("files", Qt::CaseInsensitive)) { diff --git a/src/plugins/qmlprojectmanager/buildsystem/projectitem/qmlprojectitem.cpp b/src/plugins/qmlprojectmanager/buildsystem/projectitem/qmlprojectitem.cpp index 76ad2e88360..c216ee320ba 100644 --- a/src/plugins/qmlprojectmanager/buildsystem/projectitem/qmlprojectitem.cpp +++ b/src/plugins/qmlprojectmanager/buildsystem/projectitem/qmlprojectitem.cpp @@ -131,9 +131,40 @@ QJsonObject QmlProjectItem::project() const return m_project; } -bool QmlProjectItem::isQt6Project() const +QString QmlProjectItem::versionQt() const { - return m_project["versions"].toObject()["qtQuick"].toString().startsWith("6."); + return m_project["versions"].toObject()["qt"].toString(); +} + +void QmlProjectItem::setVersionQt(const QString &version) +{ + QJsonObject targetObj = m_project["versions"].toObject(); + targetObj["qt"] = version; + insertAndUpdateProjectFile("versions", targetObj); +} + +QString QmlProjectItem::versionQtQuick() const +{ + return m_project["versions"].toObject()["qtQuick"].toString(); +} + +void QmlProjectItem::setVersionQtQuick(const QString &version) +{ + QJsonObject targetObj = m_project["versions"].toObject(); + targetObj["qtQuick"] = version; + insertAndUpdateProjectFile("versions", targetObj); +} + +QString QmlProjectItem::versionDesignStudio() const +{ + return m_project["versions"].toObject()["designStudio"].toString(); +} + +void QmlProjectItem::setVersionDesignStudio(const QString &version) +{ + QJsonObject targetObj = m_project["versions"].toObject(); + targetObj["designStudio"] = version; + insertAndUpdateProjectFile("versions", targetObj); } QStringList QmlProjectItem::importPaths() const diff --git a/src/plugins/qmlprojectmanager/buildsystem/projectitem/qmlprojectitem.h b/src/plugins/qmlprojectmanager/buildsystem/projectitem/qmlprojectitem.h index 48e389db08d..30e85d53dcd 100644 --- a/src/plugins/qmlprojectmanager/buildsystem/projectitem/qmlprojectitem.h +++ b/src/plugins/qmlprojectmanager/buildsystem/projectitem/qmlprojectitem.h @@ -29,7 +29,16 @@ public: explicit QmlProjectItem(const Utils::FilePath &filePath); bool isQt4McuProject() const; - bool isQt6Project() const; + + + QString versionQt() const; + void setVersionQt(const QString &version); + + QString versionQtQuick() const; + void setVersionQtQuick(const QString &version); + + QString versionDesignStudio() const; + void setVersionDesignStudio(const QString &version); Utils::FilePath sourceDirectory() const; QString targetDirectory() const; diff --git a/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.cpp b/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.cpp index 0872f24ede5..e06ac211dfb 100644 --- a/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.cpp +++ b/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.cpp @@ -484,7 +484,7 @@ bool QmlBuildSystem::qtForMCUs() const bool QmlBuildSystem::qt6Project() const { - return m_projectItem->isQt6Project(); + return m_projectItem->versionQt() == "6"; } Utils::EnvironmentItems QmlBuildSystem::environment() const @@ -547,4 +547,19 @@ Utils::FilePaths QmlBuildSystem::files() const return m_projectItem->files(); } +QString QmlBuildSystem::versionQt() const +{ + return m_projectItem->versionQt(); +} + +QString QmlBuildSystem::versionQtQuick() const +{ + return m_projectItem->versionQtQuick(); +} + +QString QmlBuildSystem::versionDesignStudio() const +{ + return m_projectItem->versionDesignStudio(); +} + } // namespace QmlProjectManager diff --git a/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.h b/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.h index d45658d98ba..914b8d4c0fe 100644 --- a/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.h +++ b/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.h @@ -81,6 +81,10 @@ public: QStringList importPaths() const; Utils::FilePaths files() const; + QString versionQt() const; + QString versionQtQuick() const; + QString versionDesignStudio() const; + bool addFiles(const QStringList &filePaths); void refreshProjectFile(); diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-1/testfile.qmltojson b/tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-1/testfile.qmltojson index 358a381a97e..9abc7a76c3c 100644 --- a/tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-1/testfile.qmltojson +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-1/testfile.qmltojson @@ -171,6 +171,7 @@ }, "versions": { "designStudio": "4.0", + "qt": "6", "qtQuick": "6.2" } } diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-2/testfile.jsontoqml b/tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-2/testfile.jsontoqml index 47d4912c86e..19276581aa3 100644 --- a/tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-2/testfile.jsontoqml +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-2/testfile.jsontoqml @@ -12,7 +12,7 @@ Project { qdsVersion: "3.9" quickVersion: "" - qt6Project: false + qt6Project: true qtForMCUs: true multilanguageSupport: true diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-2/testfile.qmltojson b/tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-2/testfile.qmltojson index f29227dea84..64aa062313e 100644 --- a/tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-2/testfile.qmltojson +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-2/testfile.qmltojson @@ -168,6 +168,7 @@ ] }, "versions": { - "designStudio": "3.9" + "designStudio": "3.9", + "qt": "6" } } diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-3/testfile.jsontoqml b/tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-3/testfile.jsontoqml index cb11fae9d30..1964ce018d9 100644 --- a/tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-3/testfile.jsontoqml +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-3/testfile.jsontoqml @@ -12,7 +12,7 @@ Project { qdsVersion: "3.0" quickVersion: "" - qt6Project: false + qt6Project: true qtForMCUs: true multilanguageSupport: true diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-3/testfile.qmltojson b/tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-3/testfile.qmltojson index 9bf016a7e23..1892b9f3f9c 100644 --- a/tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-3/testfile.qmltojson +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-3/testfile.qmltojson @@ -155,6 +155,7 @@ "shaderTool": { }, "versions": { - "designStudio": "3.0" + "designStudio": "3.0", + "qt": "6" } } diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-4/testfile.jsontoqml b/tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-4/testfile.jsontoqml index b0819a79a40..10a20bd9914 100644 --- a/tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-4/testfile.jsontoqml +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-4/testfile.jsontoqml @@ -12,7 +12,7 @@ Project { qdsVersion: "" quickVersion: "" - qt6Project: false + qt6Project: true qtForMCUs: true multilanguageSupport: false diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-4/testfile.qmltojson b/tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-4/testfile.qmltojson index 2f12130de3c..3ccd15a8390 100644 --- a/tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-4/testfile.qmltojson +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-4/testfile.qmltojson @@ -109,5 +109,6 @@ "shaderTool": { }, "versions": { + "qt": "6" } } diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-5/testfile.qmltojson b/tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-5/testfile.qmltojson index b5e7f5f3b7e..5635cf1f638 100644 --- a/tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-5/testfile.qmltojson +++ b/tests/auto/qml/qmlprojectmanager/projectitem/data/converter/test-set-5/testfile.qmltojson @@ -86,5 +86,6 @@ "shaderTool": { }, "versions": { + "qt": "5" } } diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/test-getters.cpp b/tests/auto/qml/qmlprojectmanager/projectitem/test-getters.cpp index 26be73b9ce0..9a3367e3cc7 100644 --- a/tests/auto/qml/qmlprojectmanager/projectitem/test-getters.cpp +++ b/tests/auto/qml/qmlprojectmanager/projectitem/test-getters.cpp @@ -34,10 +34,22 @@ TEST(QmlProjectProjectItemGetterTests, GetMcuProject) ASSERT_EQ(dataSet.projectItem2.isQt4McuProject(), false); } -TEST(QmlProjectProjectItemGetterTests, GetQt6Project) +TEST(QmlProjectProjectItemGetterTests, GetQtVersion) { - ASSERT_EQ(dataSet.projectItem1.isQt6Project(), true); - ASSERT_EQ(dataSet.projectItem2.isQt6Project(), false); + ASSERT_EQ(dataSet.projectItem1.versionQt(), "6"); + ASSERT_EQ(dataSet.projectItem2.versionQt(), "5"); +} + +TEST(QmlProjectProjectItemGetterTests, GetQtQuickVersion) +{ + ASSERT_EQ(dataSet.projectItem1.versionQtQuick(), "6.2"); + ASSERT_EQ(dataSet.projectItem2.versionQtQuick(), QString()); +} + +TEST(QmlProjectProjectItemGetterTests, GetDesignStudioVersion) +{ + ASSERT_EQ(dataSet.projectItem1.versionDesignStudio(), "3.9"); + ASSERT_EQ(dataSet.projectItem2.versionDesignStudio(), QString()); } TEST(QmlProjectProjectItemGetterTests, GetSourceDirectory) diff --git a/tests/auto/qml/qmlprojectmanager/projectitem/test-setters.cpp b/tests/auto/qml/qmlprojectmanager/projectitem/test-setters.cpp index 82524aabd06..30a99d9e212 100644 --- a/tests/auto/qml/qmlprojectmanager/projectitem/test-setters.cpp +++ b/tests/auto/qml/qmlprojectmanager/projectitem/test-setters.cpp @@ -114,6 +114,24 @@ TEST(QmlProjectProjectItemSetterTests, SetForceFreeType) testerTemplate(&QmlProjectItem::setForceFreeType, &QmlProjectItem::forceFreeType, false); } +TEST(QmlProjectProjectItemSetterTests, SetQtVersion) +{ + testerTemplate(&QmlProjectItem::setVersionQt, &QmlProjectItem::versionQt, "6"); + testerTemplate(&QmlProjectItem::setVersionQt, &QmlProjectItem::versionQt, "5.3"); +} + +TEST(QmlProjectProjectItemSetterTests, SetQtQuickVersion) +{ + testerTemplate(&QmlProjectItem::setVersionQtQuick, &QmlProjectItem::versionQtQuick, "6"); + testerTemplate(&QmlProjectItem::setVersionQtQuick, &QmlProjectItem::versionQtQuick, "5.3"); +} + +TEST(QmlProjectProjectItemSetterTests, SetDesignStudio) +{ + testerTemplate(&QmlProjectItem::setVersionDesignStudio, &QmlProjectItem::versionDesignStudio, "6"); + testerTemplate(&QmlProjectItem::setVersionDesignStudio, &QmlProjectItem::versionDesignStudio, "5.3"); +} + /** TEST(QmlProjectProjectItemSetterTests, SetEnvironment) { @@ -129,13 +147,6 @@ TEST(QmlProjectProjectItemSetterTests, SetEnvironment) // ASSERT_EQ(dataSet.projectItem2.isQt4McuProject(), false); //} -// not available as of now -//TEST(QmlProjectProjectItemSetterTests, SetQt6Project) -//{ -// ASSERT_EQ(dataSet.projectItem1.isQt6Project(), true); -// ASSERT_EQ(dataSet.projectItem2.isQt6Project(), false); -//} - // not available as of now //TEST(QmlProjectProjectItemSetterTests, SetSourceDirectory) //{ From de2154f1c94cc13219ad720c0db23cec691cc2f7 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Wed, 26 Apr 2023 14:07:54 +0200 Subject: [PATCH 094/192] QmlDesigner: Add versioning for core modules Task-number: QDS-9786 Change-Id: I56c6e9ca06ffcfcaf35cbbee061cb99e7297fca5 Reviewed-by: Thomas Hartmann Reviewed-by: Burak Hancerli --- .../include/externaldependenciesinterface.h | 1 + .../designercore/model/texttomodelmerger.cpp | 7 +- .../projectstorage/modulescanner.cpp | 29 +++- .../projectstorage/modulescanner.h | 7 +- .../qmldesignerexternaldependencies.cpp | 7 + .../qmldesignerexternaldependencies.h | 1 + .../qmldesigner/coretests/tst_testcore.cpp | 1 + .../data/modulescanner/{ => Example}/qmldir | 0 tests/unit/unittest/data/qml/QmlTime/qmldir | 4 + .../data/qml/Qt/labs/animation/qmldir | 8 ++ .../data/qml/Qt/labs/folderlistmodel/qmldir | 8 ++ .../unittest/data/qml/Qt/labs/lottieqt/qmldir | 8 ++ .../unittest/data/qml/Qt/labs/platform/qmldir | 8 ++ .../data/qml/Qt/labs/qmlmodels/qmldir | 8 ++ .../unittest/data/qml/Qt/labs/settings/qmldir | 7 + .../data/qml/Qt/labs/sharedimage/qmldir | 8 ++ .../data/qml/Qt/labs/wavefrontmesh/qmldir | 8 ++ .../unittest/data/qml/Qt/test/controls/qmldir | 7 + .../unittest/data/qml/Qt3D/Animation/qmldir | 9 ++ tests/unit/unittest/data/qml/Qt3D/Core/qmldir | 8 ++ .../unit/unittest/data/qml/Qt3D/Extras/qmldir | 9 ++ .../unit/unittest/data/qml/Qt3D/Input/qmldir | 8 ++ .../unit/unittest/data/qml/Qt3D/Logic/qmldir | 7 + .../unit/unittest/data/qml/Qt3D/Render/qmldir | 8 ++ .../Qt5Compat/GraphicalEffects/private/qmldir | 21 +++ .../qml/Qt5Compat/GraphicalEffects/qmldir | 60 ++++++++ .../QtApplicationManager/Application/qmldir | 1 + .../qml/QtApplicationManager/SystemUI/qmldir | 1 + .../data/qml/QtApplicationManager/qmldir | 1 + tests/unit/unittest/data/qml/QtCharts/qmldir | 10 ++ tests/unit/unittest/data/qml/QtCore/qmldir | 9 ++ .../data/qml/QtDataVisualization/qmldir | 8 ++ .../unittest/data/qml/QtInsightTracker/qmldir | 7 + .../qml/QtInterfaceFramework/Media/qmldir | 8 ++ .../VehicleFunctions/qmldir | 8 ++ .../data/qml/QtInterfaceFramework/qmldir | 7 + .../unit/unittest/data/qml/QtLocation/qmldir | 8 ++ .../unittest/data/qml/QtMultimedia/qmldir | 10 ++ tests/unit/unittest/data/qml/QtOpcUa/qmldir | 8 ++ .../unittest/data/qml/QtPositioning/qmldir | 8 ++ .../unit/unittest/data/qml/QtQml/Base/qmldir | 9 ++ .../unittest/data/qml/QtQml/Models/qmldir | 9 ++ .../data/qml/QtQml/StateMachine/qmldir | 8 ++ .../data/qml/QtQml/WorkerScript/qmldir | 9 ++ .../data/qml/QtQml/XmlListModel/qmldir | 8 ++ tests/unit/unittest/data/qml/QtQml/qmldir | 10 ++ .../qml/QtQuick/Controls/Basic/impl/qmldir | 7 + .../data/qml/QtQuick/Controls/Basic/qmldir | 131 ++++++++++++++++++ .../qml/QtQuick/Controls/Fusion/impl/qmldir | 20 +++ .../data/qml/QtQuick/Controls/Fusion/qmldir | 111 +++++++++++++++ .../qml/QtQuick/Controls/Imagine/impl/qmldir | 9 ++ .../data/qml/QtQuick/Controls/Imagine/qmldir | 108 +++++++++++++++ .../qml/QtQuick/Controls/Material/impl/qmldir | 26 ++++ .../data/qml/QtQuick/Controls/Material/qmldir | 115 +++++++++++++++ .../QtQuick/Controls/Universal/impl/qmldir | 14 ++ .../qml/QtQuick/Controls/Universal/qmldir | 111 +++++++++++++++ .../data/qml/QtQuick/Controls/impl/qmldir | 9 ++ .../unittest/data/qml/QtQuick/Controls/qmldir | 16 +++ .../unittest/data/qml/QtQuick/Dialogs/qmldir | 8 ++ .../data/qml/QtQuick/Dialogs/quickimpl/qmldir | 57 ++++++++ .../unittest/data/qml/QtQuick/Effects/qmldir | 8 ++ .../unittest/data/qml/QtQuick/Layouts/qmldir | 9 ++ .../data/qml/QtQuick/LocalStorage/qmldir | 7 + .../data/qml/QtQuick/NativeStyle/qmldir | 38 +++++ .../data/qml/QtQuick/Particles/qmldir | 8 ++ .../unit/unittest/data/qml/QtQuick/Pdf/qmldir | 22 +++ .../unittest/data/qml/QtQuick/Scene2D/qmldir | 7 + .../unittest/data/qml/QtQuick/Scene3D/qmldir | 7 + .../unittest/data/qml/QtQuick/Shapes/qmldir | 8 ++ .../data/qml/QtQuick/Templates/qmldir | 8 ++ .../unittest/data/qml/QtQuick/Timeline/qmldir | 9 ++ .../QtQuick/VirtualKeyboard/Components/qmldir | 103 ++++++++++++++ .../QtQuick/VirtualKeyboard/Layouts/qmldir | 7 + .../VirtualKeyboard/Plugins/Hangul/qmldir | 8 ++ .../VirtualKeyboard/Plugins/OpenWNN/qmldir | 8 ++ .../VirtualKeyboard/Plugins/Pinyin/qmldir | 8 ++ .../VirtualKeyboard/Plugins/TCIme/qmldir | 8 ++ .../VirtualKeyboard/Plugins/Thai/qmldir | 8 ++ .../QtQuick/VirtualKeyboard/Plugins/qmldir | 12 ++ .../QtQuick/VirtualKeyboard/Settings/qmldir | 7 + .../VirtualKeyboard/Styles/Builtin/qmldir | 7 + .../qml/QtQuick/VirtualKeyboard/Styles/qmldir | 30 ++++ .../data/qml/QtQuick/VirtualKeyboard/qmldir | 25 ++++ .../unittest/data/qml/QtQuick/Window/qmldir | 8 ++ tests/unit/unittest/data/qml/QtQuick/qmldir | 9 ++ .../unittest/data/qml/QtQuick/tooling/qmldir | 23 +++ .../data/qml/QtQuick3D/AssetUtils/qmldir | 9 ++ .../data/qml/QtQuick3D/Effects/qmldir | 31 +++++ .../data/qml/QtQuick3D/Helpers/impl/qmldir | 12 ++ .../data/qml/QtQuick3D/Helpers/qmldir | 21 +++ .../data/qml/QtQuick3D/MaterialEditor/qmldir | 13 ++ .../data/qml/QtQuick3D/ParticleEffects/qmldir | 10 ++ .../data/qml/QtQuick3D/Particles3D/qmldir | 9 ++ .../data/qml/QtQuick3D/Physics/Helpers/qmldir | 8 ++ .../data/qml/QtQuick3D/Physics/qmldir | 8 ++ .../data/qml/QtQuick3D/SpatialAudio/qmldir | 10 ++ tests/unit/unittest/data/qml/QtQuick3D/qmldir | 9 ++ .../unittest/data/qml/QtRemoteObjects/qmldir | 7 + tests/unit/unittest/data/qml/QtScxml/qmldir | 8 ++ tests/unit/unittest/data/qml/QtSensors/qmldir | 8 ++ tests/unit/unittest/data/qml/QtTest/qmldir | 14 ++ .../unittest/data/qml/QtTextToSpeech/qmldir | 7 + .../unit/unittest/data/qml/QtVncServer/qmldir | 8 ++ .../QtWayland/Client/TextureSharing/qmldir | 7 + .../Compositor/IviApplication/qmldir | 7 + .../Compositor/PresentationTime/qmldir | 7 + .../qml/QtWayland/Compositor/QtShell/qmldir | 7 + .../Compositor/TextureSharingExtension/qmldir | 7 + .../qml/QtWayland/Compositor/WlShell/qmldir | 7 + .../qml/QtWayland/Compositor/XdgShell/qmldir | 7 + .../data/qml/QtWayland/Compositor/qmldir | 12 ++ .../unittest/data/qml/QtWebChannel/qmldir | 7 + .../qml/QtWebEngine/ControlsDelegates/qmldir | 36 +++++ .../unit/unittest/data/qml/QtWebEngine/qmldir | 9 ++ .../unittest/data/qml/QtWebSockets/qmldir | 7 + tests/unit/unittest/data/qml/QtWebView/qmldir | 8 ++ .../unit/unittest/externaldependenciesmock.h | 45 ++++++ tests/unit/unittest/modulescanner-test.cpp | 71 ++++++++-- 118 files changed, 1971 insertions(+), 13 deletions(-) rename tests/unit/unittest/data/modulescanner/{ => Example}/qmldir (100%) create mode 100644 tests/unit/unittest/data/qml/QmlTime/qmldir create mode 100644 tests/unit/unittest/data/qml/Qt/labs/animation/qmldir create mode 100644 tests/unit/unittest/data/qml/Qt/labs/folderlistmodel/qmldir create mode 100644 tests/unit/unittest/data/qml/Qt/labs/lottieqt/qmldir create mode 100644 tests/unit/unittest/data/qml/Qt/labs/platform/qmldir create mode 100644 tests/unit/unittest/data/qml/Qt/labs/qmlmodels/qmldir create mode 100644 tests/unit/unittest/data/qml/Qt/labs/settings/qmldir create mode 100644 tests/unit/unittest/data/qml/Qt/labs/sharedimage/qmldir create mode 100644 tests/unit/unittest/data/qml/Qt/labs/wavefrontmesh/qmldir create mode 100644 tests/unit/unittest/data/qml/Qt/test/controls/qmldir create mode 100644 tests/unit/unittest/data/qml/Qt3D/Animation/qmldir create mode 100644 tests/unit/unittest/data/qml/Qt3D/Core/qmldir create mode 100644 tests/unit/unittest/data/qml/Qt3D/Extras/qmldir create mode 100644 tests/unit/unittest/data/qml/Qt3D/Input/qmldir create mode 100644 tests/unit/unittest/data/qml/Qt3D/Logic/qmldir create mode 100644 tests/unit/unittest/data/qml/Qt3D/Render/qmldir create mode 100644 tests/unit/unittest/data/qml/Qt5Compat/GraphicalEffects/private/qmldir create mode 100644 tests/unit/unittest/data/qml/Qt5Compat/GraphicalEffects/qmldir create mode 100644 tests/unit/unittest/data/qml/QtApplicationManager/Application/qmldir create mode 100644 tests/unit/unittest/data/qml/QtApplicationManager/SystemUI/qmldir create mode 100644 tests/unit/unittest/data/qml/QtApplicationManager/qmldir create mode 100644 tests/unit/unittest/data/qml/QtCharts/qmldir create mode 100644 tests/unit/unittest/data/qml/QtCore/qmldir create mode 100644 tests/unit/unittest/data/qml/QtDataVisualization/qmldir create mode 100644 tests/unit/unittest/data/qml/QtInsightTracker/qmldir create mode 100644 tests/unit/unittest/data/qml/QtInterfaceFramework/Media/qmldir create mode 100644 tests/unit/unittest/data/qml/QtInterfaceFramework/VehicleFunctions/qmldir create mode 100644 tests/unit/unittest/data/qml/QtInterfaceFramework/qmldir create mode 100644 tests/unit/unittest/data/qml/QtLocation/qmldir create mode 100644 tests/unit/unittest/data/qml/QtMultimedia/qmldir create mode 100644 tests/unit/unittest/data/qml/QtOpcUa/qmldir create mode 100644 tests/unit/unittest/data/qml/QtPositioning/qmldir create mode 100644 tests/unit/unittest/data/qml/QtQml/Base/qmldir create mode 100644 tests/unit/unittest/data/qml/QtQml/Models/qmldir create mode 100644 tests/unit/unittest/data/qml/QtQml/StateMachine/qmldir create mode 100644 tests/unit/unittest/data/qml/QtQml/WorkerScript/qmldir create mode 100644 tests/unit/unittest/data/qml/QtQml/XmlListModel/qmldir create mode 100644 tests/unit/unittest/data/qml/QtQml/qmldir create mode 100644 tests/unit/unittest/data/qml/QtQuick/Controls/Basic/impl/qmldir create mode 100644 tests/unit/unittest/data/qml/QtQuick/Controls/Basic/qmldir create mode 100644 tests/unit/unittest/data/qml/QtQuick/Controls/Fusion/impl/qmldir create mode 100644 tests/unit/unittest/data/qml/QtQuick/Controls/Fusion/qmldir create mode 100644 tests/unit/unittest/data/qml/QtQuick/Controls/Imagine/impl/qmldir create mode 100644 tests/unit/unittest/data/qml/QtQuick/Controls/Imagine/qmldir create mode 100644 tests/unit/unittest/data/qml/QtQuick/Controls/Material/impl/qmldir create mode 100644 tests/unit/unittest/data/qml/QtQuick/Controls/Material/qmldir create mode 100644 tests/unit/unittest/data/qml/QtQuick/Controls/Universal/impl/qmldir create mode 100644 tests/unit/unittest/data/qml/QtQuick/Controls/Universal/qmldir create mode 100644 tests/unit/unittest/data/qml/QtQuick/Controls/impl/qmldir create mode 100644 tests/unit/unittest/data/qml/QtQuick/Controls/qmldir create mode 100644 tests/unit/unittest/data/qml/QtQuick/Dialogs/qmldir create mode 100644 tests/unit/unittest/data/qml/QtQuick/Dialogs/quickimpl/qmldir create mode 100644 tests/unit/unittest/data/qml/QtQuick/Effects/qmldir create mode 100644 tests/unit/unittest/data/qml/QtQuick/Layouts/qmldir create mode 100644 tests/unit/unittest/data/qml/QtQuick/LocalStorage/qmldir create mode 100644 tests/unit/unittest/data/qml/QtQuick/NativeStyle/qmldir create mode 100644 tests/unit/unittest/data/qml/QtQuick/Particles/qmldir create mode 100644 tests/unit/unittest/data/qml/QtQuick/Pdf/qmldir create mode 100644 tests/unit/unittest/data/qml/QtQuick/Scene2D/qmldir create mode 100644 tests/unit/unittest/data/qml/QtQuick/Scene3D/qmldir create mode 100644 tests/unit/unittest/data/qml/QtQuick/Shapes/qmldir create mode 100644 tests/unit/unittest/data/qml/QtQuick/Templates/qmldir create mode 100644 tests/unit/unittest/data/qml/QtQuick/Timeline/qmldir create mode 100644 tests/unit/unittest/data/qml/QtQuick/VirtualKeyboard/Components/qmldir create mode 100644 tests/unit/unittest/data/qml/QtQuick/VirtualKeyboard/Layouts/qmldir create mode 100644 tests/unit/unittest/data/qml/QtQuick/VirtualKeyboard/Plugins/Hangul/qmldir create mode 100644 tests/unit/unittest/data/qml/QtQuick/VirtualKeyboard/Plugins/OpenWNN/qmldir create mode 100644 tests/unit/unittest/data/qml/QtQuick/VirtualKeyboard/Plugins/Pinyin/qmldir create mode 100644 tests/unit/unittest/data/qml/QtQuick/VirtualKeyboard/Plugins/TCIme/qmldir create mode 100644 tests/unit/unittest/data/qml/QtQuick/VirtualKeyboard/Plugins/Thai/qmldir create mode 100644 tests/unit/unittest/data/qml/QtQuick/VirtualKeyboard/Plugins/qmldir create mode 100644 tests/unit/unittest/data/qml/QtQuick/VirtualKeyboard/Settings/qmldir create mode 100644 tests/unit/unittest/data/qml/QtQuick/VirtualKeyboard/Styles/Builtin/qmldir create mode 100644 tests/unit/unittest/data/qml/QtQuick/VirtualKeyboard/Styles/qmldir create mode 100644 tests/unit/unittest/data/qml/QtQuick/VirtualKeyboard/qmldir create mode 100644 tests/unit/unittest/data/qml/QtQuick/Window/qmldir create mode 100644 tests/unit/unittest/data/qml/QtQuick/qmldir create mode 100644 tests/unit/unittest/data/qml/QtQuick/tooling/qmldir create mode 100644 tests/unit/unittest/data/qml/QtQuick3D/AssetUtils/qmldir create mode 100644 tests/unit/unittest/data/qml/QtQuick3D/Effects/qmldir create mode 100644 tests/unit/unittest/data/qml/QtQuick3D/Helpers/impl/qmldir create mode 100644 tests/unit/unittest/data/qml/QtQuick3D/Helpers/qmldir create mode 100644 tests/unit/unittest/data/qml/QtQuick3D/MaterialEditor/qmldir create mode 100644 tests/unit/unittest/data/qml/QtQuick3D/ParticleEffects/qmldir create mode 100644 tests/unit/unittest/data/qml/QtQuick3D/Particles3D/qmldir create mode 100644 tests/unit/unittest/data/qml/QtQuick3D/Physics/Helpers/qmldir create mode 100644 tests/unit/unittest/data/qml/QtQuick3D/Physics/qmldir create mode 100644 tests/unit/unittest/data/qml/QtQuick3D/SpatialAudio/qmldir create mode 100644 tests/unit/unittest/data/qml/QtQuick3D/qmldir create mode 100644 tests/unit/unittest/data/qml/QtRemoteObjects/qmldir create mode 100644 tests/unit/unittest/data/qml/QtScxml/qmldir create mode 100644 tests/unit/unittest/data/qml/QtSensors/qmldir create mode 100644 tests/unit/unittest/data/qml/QtTest/qmldir create mode 100644 tests/unit/unittest/data/qml/QtTextToSpeech/qmldir create mode 100644 tests/unit/unittest/data/qml/QtVncServer/qmldir create mode 100644 tests/unit/unittest/data/qml/QtWayland/Client/TextureSharing/qmldir create mode 100644 tests/unit/unittest/data/qml/QtWayland/Compositor/IviApplication/qmldir create mode 100644 tests/unit/unittest/data/qml/QtWayland/Compositor/PresentationTime/qmldir create mode 100644 tests/unit/unittest/data/qml/QtWayland/Compositor/QtShell/qmldir create mode 100644 tests/unit/unittest/data/qml/QtWayland/Compositor/TextureSharingExtension/qmldir create mode 100644 tests/unit/unittest/data/qml/QtWayland/Compositor/WlShell/qmldir create mode 100644 tests/unit/unittest/data/qml/QtWayland/Compositor/XdgShell/qmldir create mode 100644 tests/unit/unittest/data/qml/QtWayland/Compositor/qmldir create mode 100644 tests/unit/unittest/data/qml/QtWebChannel/qmldir create mode 100644 tests/unit/unittest/data/qml/QtWebEngine/ControlsDelegates/qmldir create mode 100644 tests/unit/unittest/data/qml/QtWebEngine/qmldir create mode 100644 tests/unit/unittest/data/qml/QtWebSockets/qmldir create mode 100644 tests/unit/unittest/data/qml/QtWebView/qmldir create mode 100644 tests/unit/unittest/externaldependenciesmock.h diff --git a/src/plugins/qmldesigner/designercore/include/externaldependenciesinterface.h b/src/plugins/qmldesigner/designercore/include/externaldependenciesinterface.h index 887f24ddceb..b0409140fe3 100644 --- a/src/plugins/qmldesigner/designercore/include/externaldependenciesinterface.h +++ b/src/plugins/qmldesigner/designercore/include/externaldependenciesinterface.h @@ -43,6 +43,7 @@ public: virtual QStringList modulePaths() const = 0; virtual QStringList projectModulePaths() const = 0; virtual bool isQt6Project() const = 0; + virtual QString qtQuickVersion() const = 0; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp index 5bcaa4969f9..493c834885a 100644 --- a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp +++ b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp @@ -915,11 +915,14 @@ void TextToModelMerger::setupPossibleImports() ModuleScanner moduleScanner{[&](QStringView moduleName) { return skipModule(moduleName, skipModuleNames); }, - VersionScanning::No}; + VersionScanning::No, + m_rewriterView->externalDependencies()}; moduleScanner.scan(m_rewriterView->externalDependencies().modulePaths()); m_possibleModules = moduleScanner.modules(); } else { - ModuleScanner moduleScanner{[&](QStringView) { return false; }, VersionScanning::Yes}; + ModuleScanner moduleScanner{[&](QStringView) { return false; }, + VersionScanning::Yes, + m_rewriterView->externalDependencies()}; m_possibleModules = createQt5Modules(); moduleScanner.scan(externalDependencies.projectModulePaths()); m_possibleModules.append(moduleScanner.modules()); diff --git a/src/plugins/qmldesigner/designercore/projectstorage/modulescanner.cpp b/src/plugins/qmldesigner/designercore/projectstorage/modulescanner.cpp index 56822dce06d..7a283389581 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/modulescanner.cpp +++ b/src/plugins/qmldesigner/designercore/projectstorage/modulescanner.cpp @@ -38,6 +38,26 @@ QString createVersion(const QMultiHash &compo } #endif +constexpr auto coreModules = std::make_tuple(QStringView{u"QtQuick"}, + QStringView{u"QtQuick.Controls"}, + QStringView{u"QtQuick3D"}, + QStringView{u"QtQuick3D.Helpers"}, + QStringView{u"QtQuick3D.Particles3D"}); + +bool isCoreVersion(QStringView moduleName) +{ + return std::apply([=](auto... coreModuleName) { return ((moduleName == coreModuleName) || ...); }, + coreModules); +} + +QString createCoreVersion(QStringView moduleName, ExternalDependenciesInterface &externalDependencies) +{ + if (isCoreVersion(moduleName)) + return externalDependencies.qtQuickVersion(); + + return {}; +} + } // namespace void ModuleScanner::scan(const QStringList &modulePaths) @@ -49,7 +69,9 @@ void ModuleScanner::scan(const QStringList &modulePaths) void ModuleScanner::scan([[maybe_unused]] std::string_view modulePath) { #ifdef QDS_HAS_QMLPRIVATE - QDirIterator dirIterator{QString::fromUtf8(modulePath), QDir::Dirs, QDirIterator::Subdirectories}; + QDirIterator dirIterator{QString::fromUtf8(modulePath), + QDir::Dirs | QDir::NoDotAndDotDot, + QDirIterator::Subdirectories}; while (dirIterator.hasNext()) { auto directoryPath = dirIterator.next(); @@ -74,6 +96,11 @@ void ModuleScanner::scan([[maybe_unused]] std::string_view modulePath) ? createVersion(parser.components()) : QString{}; + QString coreModuleVersion = createCoreVersion(moduleName, m_externalDependencies); + + if (!coreModuleVersion.isEmpty()) + version = coreModuleVersion; + m_modules.push_back(Import::createLibraryImport(moduleName, version)); } } diff --git a/src/plugins/qmldesigner/designercore/projectstorage/modulescanner.h b/src/plugins/qmldesigner/designercore/projectstorage/modulescanner.h index 617428b0f59..64b672de8e3 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/modulescanner.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/modulescanner.h @@ -5,6 +5,7 @@ #include +#include #include #include @@ -18,10 +19,13 @@ class QMLDESIGNERCORE_EXPORT ModuleScanner public: using SkipFunction = std::function; - ModuleScanner([[maybe_unused]] SkipFunction skip, [[maybe_unused]] VersionScanning versionScanning) + ModuleScanner([[maybe_unused]] SkipFunction skip, + [[maybe_unused]] VersionScanning versionScanning, + ExternalDependenciesInterface &externalDependencies) #ifdef QDS_HAS_QMLPRIVATE : m_skip{std::move(skip)} , m_versionScanning{versionScanning} + , m_externalDependencies{externalDependencies} #endif { m_modules.reserve(128); @@ -39,6 +43,7 @@ private: #ifdef QDS_HAS_QMLPRIVATE SkipFunction m_skip; VersionScanning m_versionScanning; + ExternalDependenciesInterface &m_externalDependencies; #endif }; diff --git a/src/plugins/qmldesigner/qmldesignerexternaldependencies.cpp b/src/plugins/qmldesigner/qmldesignerexternaldependencies.cpp index d1eaae8d7e2..ef844b4eaef 100644 --- a/src/plugins/qmldesigner/qmldesignerexternaldependencies.cpp +++ b/src/plugins/qmldesigner/qmldesignerexternaldependencies.cpp @@ -267,4 +267,11 @@ bool ExternalDependencies::isQt6Project() const return qmlBuildSystem && qmlBuildSystem->qt6Project(); } +QString ExternalDependencies::qtQuickVersion() const +{ + auto [project, target, qmlBuildSystem] = activeProjectEntries(); + + return qmlBuildSystem ? qmlBuildSystem->versionQtQuick() : QString{}; +} + } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/qmldesignerexternaldependencies.h b/src/plugins/qmldesigner/qmldesignerexternaldependencies.h index 3c89dd40474..b5771919139 100644 --- a/src/plugins/qmldesigner/qmldesignerexternaldependencies.h +++ b/src/plugins/qmldesigner/qmldesignerexternaldependencies.h @@ -39,6 +39,7 @@ public: QStringList modulePaths() const override; QStringList projectModulePaths() const override; bool isQt6Project() const override; + QString qtQuickVersion() const override; private: const DesignerSettings &m_designerSettings; diff --git a/tests/auto/qml/qmldesigner/coretests/tst_testcore.cpp b/tests/auto/qml/qmldesigner/coretests/tst_testcore.cpp index e0f5ebda421..edbcd5554be 100644 --- a/tests/auto/qml/qmldesigner/coretests/tst_testcore.cpp +++ b/tests/auto/qml/qmldesigner/coretests/tst_testcore.cpp @@ -163,6 +163,7 @@ public: QStringList modulePaths() const override { return {}; } QStringList projectModulePaths() const override { return {}; } bool isQt6Project() const override { return {}; } + QString qtQuickVersion() const override { return {}; } public: QSettings qsettings; diff --git a/tests/unit/unittest/data/modulescanner/qmldir b/tests/unit/unittest/data/modulescanner/Example/qmldir similarity index 100% rename from tests/unit/unittest/data/modulescanner/qmldir rename to tests/unit/unittest/data/modulescanner/Example/qmldir diff --git a/tests/unit/unittest/data/qml/QmlTime/qmldir b/tests/unit/unittest/data/qml/QmlTime/qmldir new file mode 100644 index 00000000000..6610b421041 --- /dev/null +++ b/tests/unit/unittest/data/qml/QmlTime/qmldir @@ -0,0 +1,4 @@ +module QmlTime +typeinfo qmltime.qmltypes +prefer :/qt-project.org/imports/QmlTime/ + diff --git a/tests/unit/unittest/data/qml/Qt/labs/animation/qmldir b/tests/unit/unittest/data/qml/Qt/labs/animation/qmldir new file mode 100644 index 00000000000..5f58f4bb3c2 --- /dev/null +++ b/tests/unit/unittest/data/qml/Qt/labs/animation/qmldir @@ -0,0 +1,8 @@ +module Qt.labs.animation +linktarget Qt6::labsanimationplugin +optional plugin labsanimationplugin +classname QtLabsAnimationPlugin +typeinfo plugins.qmltypes +depends QtQml +prefer :/qt-project.org/imports/Qt/labs/animation/ + diff --git a/tests/unit/unittest/data/qml/Qt/labs/folderlistmodel/qmldir b/tests/unit/unittest/data/qml/Qt/labs/folderlistmodel/qmldir new file mode 100644 index 00000000000..5a61a7149d3 --- /dev/null +++ b/tests/unit/unittest/data/qml/Qt/labs/folderlistmodel/qmldir @@ -0,0 +1,8 @@ +module Qt.labs.folderlistmodel +linktarget Qt6::qmlfolderlistmodelplugin +optional plugin qmlfolderlistmodelplugin +classname QmlFolderListModelPlugin +typeinfo plugins.qmltypes +depends QtQml.Models auto +prefer :/qt-project.org/imports/Qt/labs/folderlistmodel/ + diff --git a/tests/unit/unittest/data/qml/Qt/labs/lottieqt/qmldir b/tests/unit/unittest/data/qml/Qt/labs/lottieqt/qmldir new file mode 100644 index 00000000000..5d5eb586b36 --- /dev/null +++ b/tests/unit/unittest/data/qml/Qt/labs/lottieqt/qmldir @@ -0,0 +1,8 @@ +module Qt.labs.lottieqt +linktarget Qt6::lottieqtplugin +optional plugin lottieqtplugin +classname BodymovinPlugin +typeinfo plugins.qmltypes +depends QtQuick +prefer :/qt-project.org/imports/Qt/labs/lottieqt/ + diff --git a/tests/unit/unittest/data/qml/Qt/labs/platform/qmldir b/tests/unit/unittest/data/qml/Qt/labs/platform/qmldir new file mode 100644 index 00000000000..01800b393b9 --- /dev/null +++ b/tests/unit/unittest/data/qml/Qt/labs/platform/qmldir @@ -0,0 +1,8 @@ +module Qt.labs.platform +linktarget Qt6::qtlabsplatformplugin +optional plugin qtlabsplatformplugin +classname QtLabsPlatformPlugin +typeinfo plugins.qmltypes +depends QtQuick +prefer :/qt-project.org/imports/Qt/labs/platform/ + diff --git a/tests/unit/unittest/data/qml/Qt/labs/qmlmodels/qmldir b/tests/unit/unittest/data/qml/Qt/labs/qmlmodels/qmldir new file mode 100644 index 00000000000..6b928f6776f --- /dev/null +++ b/tests/unit/unittest/data/qml/Qt/labs/qmlmodels/qmldir @@ -0,0 +1,8 @@ +module Qt.labs.qmlmodels +linktarget Qt6::labsmodelsplugin +optional plugin labsmodelsplugin +classname QtQmlLabsModelsPlugin +typeinfo plugins.qmltypes +depends QtQml.Models auto +prefer :/qt-project.org/imports/Qt/labs/qmlmodels/ + diff --git a/tests/unit/unittest/data/qml/Qt/labs/settings/qmldir b/tests/unit/unittest/data/qml/Qt/labs/settings/qmldir new file mode 100644 index 00000000000..86b0864288d --- /dev/null +++ b/tests/unit/unittest/data/qml/Qt/labs/settings/qmldir @@ -0,0 +1,7 @@ +module Qt.labs.settings +linktarget Qt6::qmlsettingsplugin +optional plugin qmlsettingsplugin +classname QmlSettingsPlugin +typeinfo plugins.qmltypes +prefer :/qt-project.org/imports/Qt/labs/settings/ + diff --git a/tests/unit/unittest/data/qml/Qt/labs/sharedimage/qmldir b/tests/unit/unittest/data/qml/Qt/labs/sharedimage/qmldir new file mode 100644 index 00000000000..089730519ce --- /dev/null +++ b/tests/unit/unittest/data/qml/Qt/labs/sharedimage/qmldir @@ -0,0 +1,8 @@ +module Qt.labs.sharedimage +linktarget Qt6::sharedimageplugin +plugin sharedimageplugin +classname QtQuickSharedImagePlugin +static +typeinfo plugins.qmltypes +prefer :/qt-project.org/imports/Qt/labs/sharedimage/ + diff --git a/tests/unit/unittest/data/qml/Qt/labs/wavefrontmesh/qmldir b/tests/unit/unittest/data/qml/Qt/labs/wavefrontmesh/qmldir new file mode 100644 index 00000000000..35e8425429c --- /dev/null +++ b/tests/unit/unittest/data/qml/Qt/labs/wavefrontmesh/qmldir @@ -0,0 +1,8 @@ +module Qt.labs.wavefrontmesh +linktarget Qt6::qmlwavefrontmeshplugin +optional plugin qmlwavefrontmeshplugin +classname QmlWavefrontMeshPlugin +typeinfo plugins.qmltypes +depends QtQuick auto +prefer :/qt-project.org/imports/Qt/labs/wavefrontmesh/ + diff --git a/tests/unit/unittest/data/qml/Qt/test/controls/qmldir b/tests/unit/unittest/data/qml/Qt/test/controls/qmldir new file mode 100644 index 00000000000..e2ee693d15c --- /dev/null +++ b/tests/unit/unittest/data/qml/Qt/test/controls/qmldir @@ -0,0 +1,7 @@ +module Qt.test.controls +linktarget Qt6::QuickControlsTestUtilsPrivateplugin +optional plugin quickcontrolstestutilsprivateplugin +classname Qt_test_controlsPlugin +typeinfo QuickControlsTestUtilsPrivate.qmltypes +prefer :/qt-project.org/imports/Qt/test/controls/ + diff --git a/tests/unit/unittest/data/qml/Qt3D/Animation/qmldir b/tests/unit/unittest/data/qml/Qt3D/Animation/qmldir new file mode 100644 index 00000000000..e122e29243e --- /dev/null +++ b/tests/unit/unittest/data/qml/Qt3D/Animation/qmldir @@ -0,0 +1,9 @@ +module Qt3D.Animation +linktarget Qt6::quick3danimationplugin +plugin quick3danimationplugin +classname Qt3DQuick3DAnimationPlugin +typeinfo plugins.qmltypes +depends QtQml auto +depends Qt3D.Render auto +prefer :/qt-project.org/imports/Qt3D/Animation/ + diff --git a/tests/unit/unittest/data/qml/Qt3D/Core/qmldir b/tests/unit/unittest/data/qml/Qt3D/Core/qmldir new file mode 100644 index 00000000000..7c8712f946c --- /dev/null +++ b/tests/unit/unittest/data/qml/Qt3D/Core/qmldir @@ -0,0 +1,8 @@ +module Qt3D.Core +linktarget Qt6::quick3dcoreplugin +plugin quick3dcoreplugin +classname Qt3DQuick3DCorePlugin +typeinfo plugins.qmltypes +depends QtQuick auto +prefer :/qt-project.org/imports/Qt3D/Core/ + diff --git a/tests/unit/unittest/data/qml/Qt3D/Extras/qmldir b/tests/unit/unittest/data/qml/Qt3D/Extras/qmldir new file mode 100644 index 00000000000..c3e41f097f3 --- /dev/null +++ b/tests/unit/unittest/data/qml/Qt3D/Extras/qmldir @@ -0,0 +1,9 @@ +module Qt3D.Extras +linktarget Qt6::quick3dextrasplugin +plugin quick3dextrasplugin +classname Qt3DQuick3DExtrasPlugin +typeinfo plugins.qmltypes +depends QtQuick auto +depends Qt3D.Logic auto +prefer :/qt-project.org/imports/Qt3D/Extras/ + diff --git a/tests/unit/unittest/data/qml/Qt3D/Input/qmldir b/tests/unit/unittest/data/qml/Qt3D/Input/qmldir new file mode 100644 index 00000000000..b34bf8caeac --- /dev/null +++ b/tests/unit/unittest/data/qml/Qt3D/Input/qmldir @@ -0,0 +1,8 @@ +module Qt3D.Input +linktarget Qt6::quick3dinputplugin +plugin quick3dinputplugin +classname Qt3DQuick3DInputPlugin +typeinfo plugins.qmltypes +depends QtQml auto +prefer :/qt-project.org/imports/Qt3D/Input/ + diff --git a/tests/unit/unittest/data/qml/Qt3D/Logic/qmldir b/tests/unit/unittest/data/qml/Qt3D/Logic/qmldir new file mode 100644 index 00000000000..c61405b9da4 --- /dev/null +++ b/tests/unit/unittest/data/qml/Qt3D/Logic/qmldir @@ -0,0 +1,7 @@ +module Qt3D.Logic +linktarget Qt6::quick3dlogicplugin +plugin quick3dlogicplugin +classname Qt3DQuick3DLogicPlugin +typeinfo plugins.qmltypes +prefer :/qt-project.org/imports/Qt3D/Logic/ + diff --git a/tests/unit/unittest/data/qml/Qt3D/Render/qmldir b/tests/unit/unittest/data/qml/Qt3D/Render/qmldir new file mode 100644 index 00000000000..fe3e762347e --- /dev/null +++ b/tests/unit/unittest/data/qml/Qt3D/Render/qmldir @@ -0,0 +1,8 @@ +module Qt3D.Render +linktarget Qt6::quick3drenderplugin +plugin quick3drenderplugin +classname Qt3DQuick3DRenderPlugin +typeinfo plugins.qmltypes +depends QtQml auto +prefer :/qt-project.org/imports/Qt3D/Render/ + diff --git a/tests/unit/unittest/data/qml/Qt5Compat/GraphicalEffects/private/qmldir b/tests/unit/unittest/data/qml/Qt5Compat/GraphicalEffects/private/qmldir new file mode 100644 index 00000000000..b7d43299006 --- /dev/null +++ b/tests/unit/unittest/data/qml/Qt5Compat/GraphicalEffects/private/qmldir @@ -0,0 +1,21 @@ +module Qt5Compat.GraphicalEffects.private +linktarget Qt6::qtgraphicaleffectsprivate +optional plugin qtgraphicaleffectsprivateplugin +classname QtGraphicalEffectsPrivatePlugin +typeinfo plugins.qmltypes +prefer :/qt-project.org/imports/Qt5Compat/GraphicalEffects/private/ +DropShadowBase 6.0 DropShadowBase.qml +DropShadowBase 1.0 DropShadowBase.qml +FastGlow 6.0 FastGlow.qml +FastGlow 1.0 FastGlow.qml +FastInnerShadow 6.0 FastInnerShadow.qml +FastInnerShadow 1.0 FastInnerShadow.qml +GaussianDirectionalBlur 6.0 GaussianDirectionalBlur.qml +GaussianDirectionalBlur 1.0 GaussianDirectionalBlur.qml +GaussianGlow 6.0 GaussianGlow.qml +GaussianGlow 1.0 GaussianGlow.qml +GaussianInnerShadow 6.0 GaussianInnerShadow.qml +GaussianInnerShadow 1.0 GaussianInnerShadow.qml +GaussianMaskedBlur 6.0 GaussianMaskedBlur.qml +GaussianMaskedBlur 1.0 GaussianMaskedBlur.qml + diff --git a/tests/unit/unittest/data/qml/Qt5Compat/GraphicalEffects/qmldir b/tests/unit/unittest/data/qml/Qt5Compat/GraphicalEffects/qmldir new file mode 100644 index 00000000000..60238eaa0b6 --- /dev/null +++ b/tests/unit/unittest/data/qml/Qt5Compat/GraphicalEffects/qmldir @@ -0,0 +1,60 @@ +module Qt5Compat.GraphicalEffects +linktarget Qt6::qtgraphicaleffectsplugin +plugin qtgraphicaleffectsplugin +classname QtGraphicalEffectsPlugin +designersupported +typeinfo plugins.qmltypes +depends Qt5Compat.GraphicalEffects.private +depends QtQuick.Window +prefer :/qt-project.org/imports/Qt5Compat/GraphicalEffects/ +Blend 6.0 Blend.qml +Blend 1.0 Blend.qml +BrightnessContrast 6.0 BrightnessContrast.qml +BrightnessContrast 1.0 BrightnessContrast.qml +ColorOverlay 6.0 ColorOverlay.qml +ColorOverlay 1.0 ColorOverlay.qml +Colorize 6.0 Colorize.qml +Colorize 1.0 Colorize.qml +ConicalGradient 6.0 ConicalGradient.qml +ConicalGradient 1.0 ConicalGradient.qml +Desaturate 6.0 Desaturate.qml +Desaturate 1.0 Desaturate.qml +DirectionalBlur 6.0 DirectionalBlur.qml +DirectionalBlur 1.0 DirectionalBlur.qml +Displace 6.0 Displace.qml +Displace 1.0 Displace.qml +DropShadow 6.0 DropShadow.qml +DropShadow 1.0 DropShadow.qml +FastBlur 6.0 FastBlur.qml +FastBlur 1.0 FastBlur.qml +GammaAdjust 6.0 GammaAdjust.qml +GammaAdjust 1.0 GammaAdjust.qml +GaussianBlur 6.0 GaussianBlur.qml +GaussianBlur 1.0 GaussianBlur.qml +Glow 6.0 Glow.qml +Glow 1.0 Glow.qml +HueSaturation 6.0 HueSaturation.qml +HueSaturation 1.0 HueSaturation.qml +InnerShadow 6.0 InnerShadow.qml +InnerShadow 1.0 InnerShadow.qml +LevelAdjust 6.0 LevelAdjust.qml +LevelAdjust 1.0 LevelAdjust.qml +LinearGradient 6.0 LinearGradient.qml +LinearGradient 1.0 LinearGradient.qml +MaskedBlur 6.0 MaskedBlur.qml +MaskedBlur 1.0 MaskedBlur.qml +OpacityMask 6.0 OpacityMask.qml +OpacityMask 1.0 OpacityMask.qml +RadialBlur 6.0 RadialBlur.qml +RadialBlur 1.0 RadialBlur.qml +RadialGradient 6.0 RadialGradient.qml +RadialGradient 1.0 RadialGradient.qml +RectangularGlow 6.0 RectangularGlow.qml +RectangularGlow 1.0 RectangularGlow.qml +RecursiveBlur 6.0 RecursiveBlur.qml +RecursiveBlur 1.0 RecursiveBlur.qml +ThresholdMask 6.0 ThresholdMask.qml +ThresholdMask 1.0 ThresholdMask.qml +ZoomBlur 6.0 ZoomBlur.qml +ZoomBlur 1.0 ZoomBlur.qml + diff --git a/tests/unit/unittest/data/qml/QtApplicationManager/Application/qmldir b/tests/unit/unittest/data/qml/QtApplicationManager/Application/qmldir new file mode 100644 index 00000000000..4ed2cd6755f --- /dev/null +++ b/tests/unit/unittest/data/qml/QtApplicationManager/Application/qmldir @@ -0,0 +1 @@ +typeinfo plugins.qmltypes diff --git a/tests/unit/unittest/data/qml/QtApplicationManager/SystemUI/qmldir b/tests/unit/unittest/data/qml/QtApplicationManager/SystemUI/qmldir new file mode 100644 index 00000000000..4ed2cd6755f --- /dev/null +++ b/tests/unit/unittest/data/qml/QtApplicationManager/SystemUI/qmldir @@ -0,0 +1 @@ +typeinfo plugins.qmltypes diff --git a/tests/unit/unittest/data/qml/QtApplicationManager/qmldir b/tests/unit/unittest/data/qml/QtApplicationManager/qmldir new file mode 100644 index 00000000000..4ed2cd6755f --- /dev/null +++ b/tests/unit/unittest/data/qml/QtApplicationManager/qmldir @@ -0,0 +1 @@ +typeinfo plugins.qmltypes diff --git a/tests/unit/unittest/data/qml/QtCharts/qmldir b/tests/unit/unittest/data/qml/QtCharts/qmldir new file mode 100644 index 00000000000..42d00490467 --- /dev/null +++ b/tests/unit/unittest/data/qml/QtCharts/qmldir @@ -0,0 +1,10 @@ +module QtCharts +linktarget Qt6::qtchartsqml2 +optional plugin qtchartsqml2plugin +classname QtChartsQml2Plugin +designersupported +typeinfo plugins.qmltypes +depends QtQuick +depends QtCharts +prefer :/qt-project.org/imports/QtCharts/ + diff --git a/tests/unit/unittest/data/qml/QtCore/qmldir b/tests/unit/unittest/data/qml/QtCore/qmldir new file mode 100644 index 00000000000..d652bb201b4 --- /dev/null +++ b/tests/unit/unittest/data/qml/QtCore/qmldir @@ -0,0 +1,9 @@ +module QtCore +linktarget Qt6::qtqmlcoreplugin +optional plugin qtqmlcoreplugin +classname QtQmlCorePlugin +designersupported +typeinfo plugins.qmltypes +depends QtQml auto +prefer :/qt-project.org/imports/QtCore/ + diff --git a/tests/unit/unittest/data/qml/QtDataVisualization/qmldir b/tests/unit/unittest/data/qml/QtDataVisualization/qmldir new file mode 100644 index 00000000000..714749cb3c6 --- /dev/null +++ b/tests/unit/unittest/data/qml/QtDataVisualization/qmldir @@ -0,0 +1,8 @@ +module QtDataVisualization +linktarget Qt6::DataVisualizationQmlplugin +optional plugin datavisualizationqmlplugin +classname QtDataVisualizationPlugin +typeinfo plugins.qmltypes +depends QtQuick +prefer :/qt-project.org/imports/QtDataVisualization/ + diff --git a/tests/unit/unittest/data/qml/QtInsightTracker/qmldir b/tests/unit/unittest/data/qml/QtInsightTracker/qmldir new file mode 100644 index 00000000000..e9126a4513a --- /dev/null +++ b/tests/unit/unittest/data/qml/QtInsightTracker/qmldir @@ -0,0 +1,7 @@ +module QtInsightTracker +linktarget Qt6::InsightTrackerQmlplugin +optional plugin insighttrackerqmlplugin +classname QtInsightTrackerPlugin +typeinfo plugins.qmltypes +prefer :/qt-project.org/imports/QtInsightTracker/ + diff --git a/tests/unit/unittest/data/qml/QtInterfaceFramework/Media/qmldir b/tests/unit/unittest/data/qml/QtInterfaceFramework/Media/qmldir new file mode 100644 index 00000000000..bcd1c145e04 --- /dev/null +++ b/tests/unit/unittest/data/qml/QtInterfaceFramework/Media/qmldir @@ -0,0 +1,8 @@ +module QtInterfaceFramework.Media +linktarget Qt6::IfMediaplugin +optional plugin ifmediaplugin +classname QIfMediaPlugin +typeinfo plugins.qmltypes +import QtInterfaceFramework auto +prefer :/qt-project.org/imports/QtInterfaceFramework/Media/ + diff --git a/tests/unit/unittest/data/qml/QtInterfaceFramework/VehicleFunctions/qmldir b/tests/unit/unittest/data/qml/QtInterfaceFramework/VehicleFunctions/qmldir new file mode 100644 index 00000000000..009e335aea4 --- /dev/null +++ b/tests/unit/unittest/data/qml/QtInterfaceFramework/VehicleFunctions/qmldir @@ -0,0 +1,8 @@ +module QtInterfaceFramework.VehicleFunctions +linktarget Qt6::qtifvehiclefunctionsplugin +optional plugin qtifvehiclefunctionsplugin +classname QtIfVehicleFunctionsPlugin +typeinfo IfVehicleFunctions.qmltypes +import QtInterfaceFramework auto +prefer :/qt-project.org/imports/QtInterfaceFramework/VehicleFunctions/ + diff --git a/tests/unit/unittest/data/qml/QtInterfaceFramework/qmldir b/tests/unit/unittest/data/qml/QtInterfaceFramework/qmldir new file mode 100644 index 00000000000..727eaf99b77 --- /dev/null +++ b/tests/unit/unittest/data/qml/QtInterfaceFramework/qmldir @@ -0,0 +1,7 @@ +module QtInterfaceFramework +linktarget Qt6::InterfaceFrameworkplugin +optional plugin interfaceframeworkplugin +classname QtInterfaceFrameworkPlugin +typeinfo plugins.qmltypes +prefer :/qt-project.org/imports/QtInterfaceFramework/ + diff --git a/tests/unit/unittest/data/qml/QtLocation/qmldir b/tests/unit/unittest/data/qml/QtLocation/qmldir new file mode 100644 index 00000000000..207cd8959b4 --- /dev/null +++ b/tests/unit/unittest/data/qml/QtLocation/qmldir @@ -0,0 +1,8 @@ +module QtLocation +linktarget Qt6::declarative_location +plugin declarative_locationplugin +classname QtLocationDeclarativeModule +typeinfo plugins.qmltypes +prefer :/qt-project.org/imports/QtLocation/ +MapView 6.0 MapView.qml + diff --git a/tests/unit/unittest/data/qml/QtMultimedia/qmldir b/tests/unit/unittest/data/qml/QtMultimedia/qmldir new file mode 100644 index 00000000000..82f64177ebf --- /dev/null +++ b/tests/unit/unittest/data/qml/QtMultimedia/qmldir @@ -0,0 +1,10 @@ +module QtMultimedia +linktarget Qt6::quickmultimedia +plugin quickmultimediaplugin +classname QMultimediaQuickModule +typeinfo plugins.qmltypes +depends QtQuick +prefer :/qt-project.org/imports/QtMultimedia/ +Video 6.0 Video.qml +Video 5.0 Video.qml + diff --git a/tests/unit/unittest/data/qml/QtOpcUa/qmldir b/tests/unit/unittest/data/qml/QtOpcUa/qmldir new file mode 100644 index 00000000000..4d745231296 --- /dev/null +++ b/tests/unit/unittest/data/qml/QtOpcUa/qmldir @@ -0,0 +1,8 @@ +module QtOpcUa +linktarget Qt6::DeclarativeOpcuaplugin +optional plugin declarativeopcuaplugin +classname QtOpcUaPlugin +typeinfo plugins.qmltypes +depends QtQuick +prefer :/qt-project.org/imports/QtOpcUa/ + diff --git a/tests/unit/unittest/data/qml/QtPositioning/qmldir b/tests/unit/unittest/data/qml/QtPositioning/qmldir new file mode 100644 index 00000000000..9ae0891d083 --- /dev/null +++ b/tests/unit/unittest/data/qml/QtPositioning/qmldir @@ -0,0 +1,8 @@ +module QtPositioning +linktarget Qt6::positioningquickplugin +plugin positioningquickplugin +classname QtPositioningDeclarativeModule +typeinfo plugins.qmltypes +depends QtQuick auto +prefer :/qt-project.org/imports/QtPositioning/ + diff --git a/tests/unit/unittest/data/qml/QtQml/Base/qmldir b/tests/unit/unittest/data/qml/QtQml/Base/qmldir new file mode 100644 index 00000000000..7f2da0f0d81 --- /dev/null +++ b/tests/unit/unittest/data/qml/QtQml/Base/qmldir @@ -0,0 +1,9 @@ +module QtQml.Base +linktarget Qt6::qmlplugin +optional plugin qmlplugin +classname QtQmlPlugin +designersupported +system +typeinfo plugins.qmltypes +prefer :/qt-project.org/imports/QtQml/Base/ + diff --git a/tests/unit/unittest/data/qml/QtQml/Models/qmldir b/tests/unit/unittest/data/qml/QtQml/Models/qmldir new file mode 100644 index 00000000000..60eac9bf196 --- /dev/null +++ b/tests/unit/unittest/data/qml/QtQml/Models/qmldir @@ -0,0 +1,9 @@ +module QtQml.Models +linktarget Qt6::modelsplugin +optional plugin modelsplugin +classname QtQmlModelsPlugin +designersupported +typeinfo plugins.qmltypes +depends QtQml.Base auto +prefer :/qt-project.org/imports/QtQml/Models/ + diff --git a/tests/unit/unittest/data/qml/QtQml/StateMachine/qmldir b/tests/unit/unittest/data/qml/QtQml/StateMachine/qmldir new file mode 100644 index 00000000000..943791ea154 --- /dev/null +++ b/tests/unit/unittest/data/qml/QtQml/StateMachine/qmldir @@ -0,0 +1,8 @@ +module QtQml.StateMachine +linktarget Qt6::qtqmlstatemachine +optional plugin qtqmlstatemachineplugin +classname QtQmlStateMachinePlugin +typeinfo plugins.qmltypes +depends QtQml +prefer :/qt-project.org/imports/QtQml/StateMachine/ + diff --git a/tests/unit/unittest/data/qml/QtQml/WorkerScript/qmldir b/tests/unit/unittest/data/qml/QtQml/WorkerScript/qmldir new file mode 100644 index 00000000000..a4de5f38b28 --- /dev/null +++ b/tests/unit/unittest/data/qml/QtQml/WorkerScript/qmldir @@ -0,0 +1,9 @@ +module QtQml.WorkerScript +linktarget Qt6::workerscriptplugin +optional plugin workerscriptplugin +classname QtQmlWorkerScriptPlugin +designersupported +typeinfo plugins.qmltypes +depends QtQml.Base auto +prefer :/qt-project.org/imports/QtQml/WorkerScript/ + diff --git a/tests/unit/unittest/data/qml/QtQml/XmlListModel/qmldir b/tests/unit/unittest/data/qml/QtQml/XmlListModel/qmldir new file mode 100644 index 00000000000..f04f990ef12 --- /dev/null +++ b/tests/unit/unittest/data/qml/QtQml/XmlListModel/qmldir @@ -0,0 +1,8 @@ +module QtQml.XmlListModel +linktarget Qt6::qmlxmllistmodelplugin +optional plugin qmlxmllistmodelplugin +classname QtQmlXmlListModelPlugin +typeinfo plugins.qmltypes +depends QtQml auto +prefer :/qt-project.org/imports/QtQml/XmlListModel/ + diff --git a/tests/unit/unittest/data/qml/QtQml/qmldir b/tests/unit/unittest/data/qml/QtQml/qmldir new file mode 100644 index 00000000000..ae6977b9d97 --- /dev/null +++ b/tests/unit/unittest/data/qml/QtQml/qmldir @@ -0,0 +1,10 @@ +module QtQml +linktarget Qt6::QmlMeta +optional plugin qmlmetaplugin +classname QtQmlMetaPlugin +designersupported +import QtQml.Base auto +import QtQml.Models auto +import QtQml.WorkerScript auto +prefer :/qt-project.org/imports/QtQml/ + diff --git a/tests/unit/unittest/data/qml/QtQuick/Controls/Basic/impl/qmldir b/tests/unit/unittest/data/qml/QtQuick/Controls/Basic/impl/qmldir new file mode 100644 index 00000000000..d09bc6a1938 --- /dev/null +++ b/tests/unit/unittest/data/qml/QtQuick/Controls/Basic/impl/qmldir @@ -0,0 +1,7 @@ +module QtQuick.Controls.Basic.impl +linktarget Qt6::qtquickcontrols2basicstyleimplplugin +plugin qtquickcontrols2basicstyleimplplugin +classname QtQuickControls2BasicStyleImplPlugin +typeinfo plugins.qmltypes +prefer :/qt-project.org/imports/QtQuick/Controls/Basic/impl/ + diff --git a/tests/unit/unittest/data/qml/QtQuick/Controls/Basic/qmldir b/tests/unit/unittest/data/qml/QtQuick/Controls/Basic/qmldir new file mode 100644 index 00000000000..8460bd3bf21 --- /dev/null +++ b/tests/unit/unittest/data/qml/QtQuick/Controls/Basic/qmldir @@ -0,0 +1,131 @@ +module QtQuick.Controls.Basic +linktarget Qt6::qtquickcontrols2basicstyleplugin +plugin qtquickcontrols2basicstyleplugin +classname QtQuickControls2BasicStylePlugin +typeinfo plugins.qmltypes +import QtQuick.Controls.impl auto +depends QtQuick auto +prefer :/qt-project.org/imports/QtQuick/Controls/Basic/ +AbstractButton 6.0 AbstractButton.qml +AbstractButton 2.0 AbstractButton.qml +Action 2.3 Action.qml +Action 6.0 Action.qml +ActionGroup 2.3 ActionGroup.qml +ActionGroup 6.0 ActionGroup.qml +ApplicationWindow 6.0 ApplicationWindow.qml +ApplicationWindow 2.0 ApplicationWindow.qml +BusyIndicator 6.0 BusyIndicator.qml +BusyIndicator 2.0 BusyIndicator.qml +Button 6.0 Button.qml +Button 2.0 Button.qml +ButtonGroup 6.0 ButtonGroup.qml +ButtonGroup 2.0 ButtonGroup.qml +CheckBox 6.0 CheckBox.qml +CheckBox 2.0 CheckBox.qml +CheckDelegate 6.0 CheckDelegate.qml +CheckDelegate 2.0 CheckDelegate.qml +ComboBox 6.0 ComboBox.qml +ComboBox 2.0 ComboBox.qml +Container 6.0 Container.qml +Container 2.0 Container.qml +Control 6.0 Control.qml +Control 2.0 Control.qml +DelayButton 2.2 DelayButton.qml +DelayButton 6.0 DelayButton.qml +Dial 6.0 Dial.qml +Dial 2.0 Dial.qml +Dialog 2.1 Dialog.qml +Dialog 6.0 Dialog.qml +DialogButtonBox 2.1 DialogButtonBox.qml +DialogButtonBox 6.0 DialogButtonBox.qml +Drawer 6.0 Drawer.qml +Drawer 2.0 Drawer.qml +Frame 6.0 Frame.qml +Frame 2.0 Frame.qml +GroupBox 6.0 GroupBox.qml +GroupBox 2.0 GroupBox.qml +HorizontalHeaderView 2.15 HorizontalHeaderView.qml +HorizontalHeaderView 6.0 HorizontalHeaderView.qml +ItemDelegate 6.0 ItemDelegate.qml +ItemDelegate 2.0 ItemDelegate.qml +Label 6.0 Label.qml +Label 2.0 Label.qml +Menu 6.0 Menu.qml +Menu 2.0 Menu.qml +MenuBar 2.3 MenuBar.qml +MenuBar 6.0 MenuBar.qml +MenuBarItem 2.3 MenuBarItem.qml +MenuBarItem 6.0 MenuBarItem.qml +MenuItem 6.0 MenuItem.qml +MenuItem 2.0 MenuItem.qml +MenuSeparator 2.1 MenuSeparator.qml +MenuSeparator 6.0 MenuSeparator.qml +Page 6.0 Page.qml +Page 2.0 Page.qml +PageIndicator 6.0 PageIndicator.qml +PageIndicator 2.0 PageIndicator.qml +Pane 6.0 Pane.qml +Pane 2.0 Pane.qml +Popup 6.0 Popup.qml +Popup 2.0 Popup.qml +ProgressBar 6.0 ProgressBar.qml +ProgressBar 2.0 ProgressBar.qml +RadioButton 6.0 RadioButton.qml +RadioButton 2.0 RadioButton.qml +RadioDelegate 6.0 RadioDelegate.qml +RadioDelegate 2.0 RadioDelegate.qml +RangeSlider 6.0 RangeSlider.qml +RangeSlider 2.0 RangeSlider.qml +RoundButton 2.1 RoundButton.qml +RoundButton 6.0 RoundButton.qml +ScrollBar 6.0 ScrollBar.qml +ScrollBar 2.0 ScrollBar.qml +ScrollIndicator 6.0 ScrollIndicator.qml +ScrollIndicator 2.0 ScrollIndicator.qml +ScrollView 2.2 ScrollView.qml +ScrollView 6.0 ScrollView.qml +SelectionRectangle 6.2 SelectionRectangle.qml +Slider 6.0 Slider.qml +Slider 2.0 Slider.qml +SpinBox 6.0 SpinBox.qml +SpinBox 2.0 SpinBox.qml +SplitView 2.13 SplitView.qml +SplitView 6.0 SplitView.qml +StackView 6.0 StackView.qml +StackView 2.0 StackView.qml +SwipeDelegate 6.0 SwipeDelegate.qml +SwipeDelegate 2.0 SwipeDelegate.qml +Switch 6.0 Switch.qml +Switch 2.0 Switch.qml +SwitchDelegate 6.0 SwitchDelegate.qml +SwitchDelegate 2.0 SwitchDelegate.qml +SwipeView 6.0 SwipeView.qml +SwipeView 2.0 SwipeView.qml +TabBar 6.0 TabBar.qml +TabBar 2.0 TabBar.qml +TabButton 6.0 TabButton.qml +TabButton 2.0 TabButton.qml +TextArea 6.0 TextArea.qml +TextArea 2.0 TextArea.qml +TextField 6.0 TextField.qml +TextField 2.0 TextField.qml +ToolBar 6.0 ToolBar.qml +ToolBar 2.0 ToolBar.qml +ToolButton 6.0 ToolButton.qml +ToolButton 2.0 ToolButton.qml +ToolSeparator 2.1 ToolSeparator.qml +ToolSeparator 6.0 ToolSeparator.qml +ToolTip 6.0 ToolTip.qml +ToolTip 2.0 ToolTip.qml +Tumbler 6.0 Tumbler.qml +Tumbler 2.0 Tumbler.qml +VerticalHeaderView 2.15 VerticalHeaderView.qml +VerticalHeaderView 6.0 VerticalHeaderView.qml +singleton Calendar 6.3 Calendar.qml +CalendarModel 6.3 CalendarModel.qml +DayOfWeekRow 6.3 DayOfWeekRow.qml +MonthGrid 6.3 MonthGrid.qml +WeekNumberColumn 6.3 WeekNumberColumn.qml +TreeViewDelegate 6.0 TreeViewDelegate.qml +TreeViewDelegate 2.0 TreeViewDelegate.qml + diff --git a/tests/unit/unittest/data/qml/QtQuick/Controls/Fusion/impl/qmldir b/tests/unit/unittest/data/qml/QtQuick/Controls/Fusion/impl/qmldir new file mode 100644 index 00000000000..69e0bec723b --- /dev/null +++ b/tests/unit/unittest/data/qml/QtQuick/Controls/Fusion/impl/qmldir @@ -0,0 +1,20 @@ +module QtQuick.Controls.Fusion.impl +linktarget Qt6::qtquickcontrols2fusionstyleimplplugin +plugin qtquickcontrols2fusionstyleimplplugin +classname QtQuickControls2FusionStyleImplPlugin +typeinfo plugins.qmltypes +depends QtQuick auto +prefer :/qt-project.org/imports/QtQuick/Controls/Fusion/impl/ +ButtonPanel 6.0 ButtonPanel.qml +ButtonPanel 2.0 ButtonPanel.qml +CheckIndicator 6.0 CheckIndicator.qml +CheckIndicator 2.0 CheckIndicator.qml +RadioIndicator 6.0 RadioIndicator.qml +RadioIndicator 2.0 RadioIndicator.qml +SliderGroove 6.0 SliderGroove.qml +SliderGroove 2.0 SliderGroove.qml +SliderHandle 6.0 SliderHandle.qml +SliderHandle 2.0 SliderHandle.qml +SwitchIndicator 6.0 SwitchIndicator.qml +SwitchIndicator 2.0 SwitchIndicator.qml + diff --git a/tests/unit/unittest/data/qml/QtQuick/Controls/Fusion/qmldir b/tests/unit/unittest/data/qml/QtQuick/Controls/Fusion/qmldir new file mode 100644 index 00000000000..37e8a1407a6 --- /dev/null +++ b/tests/unit/unittest/data/qml/QtQuick/Controls/Fusion/qmldir @@ -0,0 +1,111 @@ +module QtQuick.Controls.Fusion +linktarget Qt6::qtquickcontrols2fusionstyleplugin +plugin qtquickcontrols2fusionstyleplugin +classname QtQuickControls2FusionStylePlugin +typeinfo plugins.qmltypes +import QtQuick.Controls.Basic auto +depends QtQuick auto +prefer :/qt-project.org/imports/QtQuick/Controls/Fusion/ +ApplicationWindow 6.0 ApplicationWindow.qml +ApplicationWindow 2.0 ApplicationWindow.qml +BusyIndicator 6.0 BusyIndicator.qml +BusyIndicator 2.0 BusyIndicator.qml +Button 6.0 Button.qml +Button 2.0 Button.qml +CheckBox 6.0 CheckBox.qml +CheckBox 2.0 CheckBox.qml +CheckDelegate 6.0 CheckDelegate.qml +CheckDelegate 2.0 CheckDelegate.qml +ComboBox 6.0 ComboBox.qml +ComboBox 2.0 ComboBox.qml +DelayButton 2.2 DelayButton.qml +DelayButton 6.0 DelayButton.qml +Dial 6.0 Dial.qml +Dial 2.0 Dial.qml +Dialog 2.1 Dialog.qml +Dialog 6.0 Dialog.qml +DialogButtonBox 2.1 DialogButtonBox.qml +DialogButtonBox 6.0 DialogButtonBox.qml +Drawer 6.0 Drawer.qml +Drawer 2.0 Drawer.qml +Frame 6.0 Frame.qml +Frame 2.0 Frame.qml +GroupBox 6.0 GroupBox.qml +GroupBox 2.0 GroupBox.qml +HorizontalHeaderView 2.15 HorizontalHeaderView.qml +HorizontalHeaderView 6.0 HorizontalHeaderView.qml +ItemDelegate 6.0 ItemDelegate.qml +ItemDelegate 2.0 ItemDelegate.qml +Label 6.0 Label.qml +Label 2.0 Label.qml +Menu 6.0 Menu.qml +Menu 2.0 Menu.qml +MenuBar 2.3 MenuBar.qml +MenuBar 6.0 MenuBar.qml +MenuBarItem 2.3 MenuBarItem.qml +MenuBarItem 6.0 MenuBarItem.qml +MenuItem 6.0 MenuItem.qml +MenuItem 2.0 MenuItem.qml +MenuSeparator 2.1 MenuSeparator.qml +MenuSeparator 6.0 MenuSeparator.qml +Page 6.0 Page.qml +Page 2.0 Page.qml +PageIndicator 6.0 PageIndicator.qml +PageIndicator 2.0 PageIndicator.qml +Pane 6.0 Pane.qml +Pane 2.0 Pane.qml +Popup 6.0 Popup.qml +Popup 2.0 Popup.qml +ProgressBar 6.0 ProgressBar.qml +ProgressBar 2.0 ProgressBar.qml +RadioButton 6.0 RadioButton.qml +RadioButton 2.0 RadioButton.qml +RadioDelegate 6.0 RadioDelegate.qml +RadioDelegate 2.0 RadioDelegate.qml +RangeSlider 6.0 RangeSlider.qml +RangeSlider 2.0 RangeSlider.qml +RoundButton 2.1 RoundButton.qml +RoundButton 6.0 RoundButton.qml +ScrollBar 6.0 ScrollBar.qml +ScrollBar 2.0 ScrollBar.qml +ScrollView 6.0 ScrollView.qml +ScrollView 2.0 ScrollView.qml +ScrollIndicator 6.0 ScrollIndicator.qml +ScrollIndicator 2.0 ScrollIndicator.qml +SelectionRectangle 6.0 SelectionRectangle.qml +SelectionRectangle 2.0 SelectionRectangle.qml +Slider 6.0 Slider.qml +Slider 2.0 Slider.qml +SpinBox 6.0 SpinBox.qml +SpinBox 2.0 SpinBox.qml +SplitView 2.13 SplitView.qml +SplitView 6.0 SplitView.qml +SwipeDelegate 6.0 SwipeDelegate.qml +SwipeDelegate 2.0 SwipeDelegate.qml +SwitchDelegate 6.0 SwitchDelegate.qml +SwitchDelegate 2.0 SwitchDelegate.qml +Switch 6.0 Switch.qml +Switch 2.0 Switch.qml +TabBar 6.0 TabBar.qml +TabBar 2.0 TabBar.qml +TabButton 6.0 TabButton.qml +TabButton 2.0 TabButton.qml +TextArea 6.0 TextArea.qml +TextArea 2.0 TextArea.qml +TextField 6.0 TextField.qml +TextField 2.0 TextField.qml +ToolBar 6.0 ToolBar.qml +ToolBar 2.0 ToolBar.qml +ToolButton 6.0 ToolButton.qml +ToolButton 2.0 ToolButton.qml +ToolSeparator 2.1 ToolSeparator.qml +ToolSeparator 6.0 ToolSeparator.qml +ToolTip 6.0 ToolTip.qml +ToolTip 2.0 ToolTip.qml +TreeViewDelegate 6.0 TreeViewDelegate.qml +TreeViewDelegate 2.0 TreeViewDelegate.qml +Tumbler 6.0 Tumbler.qml +Tumbler 2.0 Tumbler.qml +VerticalHeaderView 2.15 VerticalHeaderView.qml +VerticalHeaderView 6.0 VerticalHeaderView.qml + diff --git a/tests/unit/unittest/data/qml/QtQuick/Controls/Imagine/impl/qmldir b/tests/unit/unittest/data/qml/QtQuick/Controls/Imagine/impl/qmldir new file mode 100644 index 00000000000..7cdf5d634be --- /dev/null +++ b/tests/unit/unittest/data/qml/QtQuick/Controls/Imagine/impl/qmldir @@ -0,0 +1,9 @@ +module QtQuick.Controls.Imagine.impl +linktarget Qt6::qtquickcontrols2imaginestyleimplplugin +plugin qtquickcontrols2imaginestyleimplplugin +classname QtQuickControls2ImagineStyleImplPlugin +typeinfo qtquickcontrols2imaginestyleimplplugin.qmltypes +import QtQuick.Controls.impl auto +prefer :/qt-project.org/imports/QtQuick/Controls/Imagine/impl/ +OpacityMask 6.0 OpacityMask.qml + diff --git a/tests/unit/unittest/data/qml/QtQuick/Controls/Imagine/qmldir b/tests/unit/unittest/data/qml/QtQuick/Controls/Imagine/qmldir new file mode 100644 index 00000000000..adce0e5148e --- /dev/null +++ b/tests/unit/unittest/data/qml/QtQuick/Controls/Imagine/qmldir @@ -0,0 +1,108 @@ +module QtQuick.Controls.Imagine +linktarget Qt6::qtquickcontrols2imaginestyleplugin +plugin qtquickcontrols2imaginestyleplugin +classname QtQuickControls2ImagineStylePlugin +typeinfo plugins.qmltypes +import QtQuick.Controls.Basic auto +prefer :/qt-project.org/imports/QtQuick/Controls/Imagine/ +ApplicationWindow 6.0 ApplicationWindow.qml +ApplicationWindow 2.0 ApplicationWindow.qml +BusyIndicator 6.0 BusyIndicator.qml +BusyIndicator 2.0 BusyIndicator.qml +Button 6.0 Button.qml +Button 2.0 Button.qml +CheckBox 6.0 CheckBox.qml +CheckBox 2.0 CheckBox.qml +CheckDelegate 6.0 CheckDelegate.qml +CheckDelegate 2.0 CheckDelegate.qml +ComboBox 6.0 ComboBox.qml +ComboBox 2.0 ComboBox.qml +DelayButton 2.2 DelayButton.qml +DelayButton 6.0 DelayButton.qml +Dial 6.0 Dial.qml +Dial 2.0 Dial.qml +Dialog 2.1 Dialog.qml +Dialog 6.0 Dialog.qml +DialogButtonBox 2.1 DialogButtonBox.qml +DialogButtonBox 6.0 DialogButtonBox.qml +Drawer 6.0 Drawer.qml +Drawer 2.0 Drawer.qml +Frame 6.0 Frame.qml +Frame 2.0 Frame.qml +GroupBox 6.0 GroupBox.qml +GroupBox 2.0 GroupBox.qml +HorizontalHeaderView 2.15 HorizontalHeaderView.qml +HorizontalHeaderView 6.0 HorizontalHeaderView.qml +ItemDelegate 6.0 ItemDelegate.qml +ItemDelegate 2.0 ItemDelegate.qml +Label 6.0 Label.qml +Label 2.0 Label.qml +Menu 6.0 Menu.qml +Menu 2.0 Menu.qml +MenuItem 6.0 MenuItem.qml +MenuItem 2.0 MenuItem.qml +MenuSeparator 2.1 MenuSeparator.qml +MenuSeparator 6.0 MenuSeparator.qml +PageIndicator 6.0 PageIndicator.qml +PageIndicator 2.0 PageIndicator.qml +Page 6.0 Page.qml +Page 2.0 Page.qml +Pane 6.0 Pane.qml +Pane 2.0 Pane.qml +Popup 6.0 Popup.qml +Popup 2.0 Popup.qml +ProgressBar 6.0 ProgressBar.qml +ProgressBar 2.0 ProgressBar.qml +RadioButton 6.0 RadioButton.qml +RadioButton 2.0 RadioButton.qml +RadioDelegate 6.0 RadioDelegate.qml +RadioDelegate 2.0 RadioDelegate.qml +RangeSlider 6.0 RangeSlider.qml +RangeSlider 2.0 RangeSlider.qml +RoundButton 2.1 RoundButton.qml +RoundButton 6.0 RoundButton.qml +ScrollView 6.0 ScrollView.qml +ScrollView 2.0 ScrollView.qml +ScrollBar 6.0 ScrollBar.qml +ScrollBar 2.0 ScrollBar.qml +ScrollIndicator 6.0 ScrollIndicator.qml +ScrollIndicator 2.0 ScrollIndicator.qml +SelectionRectangle 6.0 SelectionRectangle.qml +SelectionRectangle 2.0 SelectionRectangle.qml +Slider 6.0 Slider.qml +Slider 2.0 Slider.qml +SpinBox 6.0 SpinBox.qml +SpinBox 2.0 SpinBox.qml +SplitView 2.13 SplitView.qml +SplitView 6.0 SplitView.qml +StackView 6.0 StackView.qml +StackView 2.0 StackView.qml +SwipeDelegate 6.0 SwipeDelegate.qml +SwipeDelegate 2.0 SwipeDelegate.qml +SwipeView 6.0 SwipeView.qml +SwipeView 2.0 SwipeView.qml +Switch 6.0 Switch.qml +Switch 2.0 Switch.qml +SwitchDelegate 6.0 SwitchDelegate.qml +SwitchDelegate 2.0 SwitchDelegate.qml +TextField 6.0 TextField.qml +TextField 2.0 TextField.qml +TextArea 6.0 TextArea.qml +TextArea 2.0 TextArea.qml +TabBar 6.0 TabBar.qml +TabBar 2.0 TabBar.qml +TabButton 6.0 TabButton.qml +TabButton 2.0 TabButton.qml +ToolBar 6.0 ToolBar.qml +ToolBar 2.0 ToolBar.qml +ToolButton 6.0 ToolButton.qml +ToolButton 2.0 ToolButton.qml +ToolSeparator 2.1 ToolSeparator.qml +ToolSeparator 6.0 ToolSeparator.qml +ToolTip 6.0 ToolTip.qml +ToolTip 2.0 ToolTip.qml +Tumbler 6.0 Tumbler.qml +Tumbler 2.0 Tumbler.qml +VerticalHeaderView 2.15 VerticalHeaderView.qml +VerticalHeaderView 6.0 VerticalHeaderView.qml + diff --git a/tests/unit/unittest/data/qml/QtQuick/Controls/Material/impl/qmldir b/tests/unit/unittest/data/qml/QtQuick/Controls/Material/impl/qmldir new file mode 100644 index 00000000000..3608a16f083 --- /dev/null +++ b/tests/unit/unittest/data/qml/QtQuick/Controls/Material/impl/qmldir @@ -0,0 +1,26 @@ +module QtQuick.Controls.Material.impl +linktarget Qt6::qtquickcontrols2materialstyleimplplugin +plugin qtquickcontrols2materialstyleimplplugin +classname QtQuickControls2MaterialStyleImplPlugin +typeinfo plugins.qmltypes +depends QtQuick auto +prefer :/qt-project.org/imports/QtQuick/Controls/Material/impl/ +BoxShadow 6.0 BoxShadow.qml +BoxShadow 2.0 BoxShadow.qml +CheckIndicator 6.0 CheckIndicator.qml +CheckIndicator 2.0 CheckIndicator.qml +CursorDelegate 6.0 CursorDelegate.qml +CursorDelegate 2.0 CursorDelegate.qml +ElevationEffect 6.0 ElevationEffect.qml +ElevationEffect 2.0 ElevationEffect.qml +RadioIndicator 6.0 RadioIndicator.qml +RadioIndicator 2.0 RadioIndicator.qml +RectangularGlow 6.0 RectangularGlow.qml +RectangularGlow 2.0 RectangularGlow.qml +RoundedElevationEffect 6.0 RoundedElevationEffect.qml +RoundedElevationEffect 2.0 RoundedElevationEffect.qml +SliderHandle 6.0 SliderHandle.qml +SliderHandle 2.0 SliderHandle.qml +SwitchIndicator 6.0 SwitchIndicator.qml +SwitchIndicator 2.0 SwitchIndicator.qml + diff --git a/tests/unit/unittest/data/qml/QtQuick/Controls/Material/qmldir b/tests/unit/unittest/data/qml/QtQuick/Controls/Material/qmldir new file mode 100644 index 00000000000..9965cfe2ad7 --- /dev/null +++ b/tests/unit/unittest/data/qml/QtQuick/Controls/Material/qmldir @@ -0,0 +1,115 @@ +module QtQuick.Controls.Material +linktarget Qt6::qtquickcontrols2materialstyleplugin +plugin qtquickcontrols2materialstyleplugin +classname QtQuickControls2MaterialStylePlugin +typeinfo plugins.qmltypes +import QtQuick.Controls.Basic auto +depends QtQuick auto +prefer :/qt-project.org/imports/QtQuick/Controls/Material/ +ApplicationWindow 6.0 ApplicationWindow.qml +ApplicationWindow 2.0 ApplicationWindow.qml +BusyIndicator 6.0 BusyIndicator.qml +BusyIndicator 2.0 BusyIndicator.qml +Button 6.0 Button.qml +Button 2.0 Button.qml +CheckBox 6.0 CheckBox.qml +CheckBox 2.0 CheckBox.qml +CheckDelegate 6.0 CheckDelegate.qml +CheckDelegate 2.0 CheckDelegate.qml +ComboBox 6.0 ComboBox.qml +ComboBox 2.0 ComboBox.qml +DelayButton 2.2 DelayButton.qml +DelayButton 6.0 DelayButton.qml +Dial 6.0 Dial.qml +Dial 2.0 Dial.qml +Dialog 2.1 Dialog.qml +Dialog 6.0 Dialog.qml +DialogButtonBox 2.1 DialogButtonBox.qml +DialogButtonBox 6.0 DialogButtonBox.qml +Drawer 6.0 Drawer.qml +Drawer 2.0 Drawer.qml +Frame 6.0 Frame.qml +Frame 2.0 Frame.qml +GroupBox 6.0 GroupBox.qml +GroupBox 2.0 GroupBox.qml +HorizontalHeaderView 2.15 HorizontalHeaderView.qml +HorizontalHeaderView 6.0 HorizontalHeaderView.qml +ItemDelegate 6.0 ItemDelegate.qml +ItemDelegate 2.0 ItemDelegate.qml +Label 6.0 Label.qml +Label 2.0 Label.qml +Menu 6.0 Menu.qml +Menu 2.0 Menu.qml +MenuBar 2.3 MenuBar.qml +MenuBar 6.0 MenuBar.qml +MenuBarItem 2.3 MenuBarItem.qml +MenuBarItem 6.0 MenuBarItem.qml +MenuItem 6.0 MenuItem.qml +MenuItem 2.0 MenuItem.qml +MenuSeparator 2.1 MenuSeparator.qml +MenuSeparator 6.0 MenuSeparator.qml +Page 6.0 Page.qml +Page 2.0 Page.qml +PageIndicator 6.0 PageIndicator.qml +PageIndicator 2.0 PageIndicator.qml +Pane 6.0 Pane.qml +Pane 2.0 Pane.qml +Popup 6.0 Popup.qml +Popup 2.0 Popup.qml +ProgressBar 6.0 ProgressBar.qml +ProgressBar 2.0 ProgressBar.qml +RadioButton 6.0 RadioButton.qml +RadioButton 2.0 RadioButton.qml +RadioDelegate 6.0 RadioDelegate.qml +RadioDelegate 2.0 RadioDelegate.qml +RangeSlider 6.0 RangeSlider.qml +RangeSlider 2.0 RangeSlider.qml +RoundButton 2.1 RoundButton.qml +RoundButton 6.0 RoundButton.qml +ScrollView 6.0 ScrollView.qml +ScrollView 2.0 ScrollView.qml +ScrollBar 6.0 ScrollBar.qml +ScrollBar 2.0 ScrollBar.qml +ScrollIndicator 6.0 ScrollIndicator.qml +ScrollIndicator 2.0 ScrollIndicator.qml +SelectionRectangle 6.0 SelectionRectangle.qml +SelectionRectangle 2.0 SelectionRectangle.qml +Slider 6.0 Slider.qml +Slider 2.0 Slider.qml +SpinBox 6.0 SpinBox.qml +SpinBox 2.0 SpinBox.qml +SplitView 2.13 SplitView.qml +SplitView 6.0 SplitView.qml +StackView 6.0 StackView.qml +StackView 2.0 StackView.qml +SwipeDelegate 6.0 SwipeDelegate.qml +SwipeDelegate 2.0 SwipeDelegate.qml +SwipeView 6.0 SwipeView.qml +SwipeView 2.0 SwipeView.qml +Switch 6.0 Switch.qml +Switch 2.0 Switch.qml +SwitchDelegate 6.0 SwitchDelegate.qml +SwitchDelegate 2.0 SwitchDelegate.qml +TabBar 6.0 TabBar.qml +TabBar 2.0 TabBar.qml +TabButton 6.0 TabButton.qml +TabButton 2.0 TabButton.qml +TextArea 6.0 TextArea.qml +TextArea 2.0 TextArea.qml +TextField 6.0 TextField.qml +TextField 2.0 TextField.qml +ToolBar 6.0 ToolBar.qml +ToolBar 2.0 ToolBar.qml +ToolButton 6.0 ToolButton.qml +ToolButton 2.0 ToolButton.qml +ToolSeparator 2.1 ToolSeparator.qml +ToolSeparator 6.0 ToolSeparator.qml +ToolTip 6.0 ToolTip.qml +ToolTip 2.0 ToolTip.qml +TreeViewDelegate 6.0 TreeViewDelegate.qml +TreeViewDelegate 2.0 TreeViewDelegate.qml +Tumbler 6.0 Tumbler.qml +Tumbler 2.0 Tumbler.qml +VerticalHeaderView 2.15 VerticalHeaderView.qml +VerticalHeaderView 6.0 VerticalHeaderView.qml + diff --git a/tests/unit/unittest/data/qml/QtQuick/Controls/Universal/impl/qmldir b/tests/unit/unittest/data/qml/QtQuick/Controls/Universal/impl/qmldir new file mode 100644 index 00000000000..e3297d34122 --- /dev/null +++ b/tests/unit/unittest/data/qml/QtQuick/Controls/Universal/impl/qmldir @@ -0,0 +1,14 @@ +module QtQuick.Controls.Universal.impl +linktarget Qt6::qtquickcontrols2universalstyleimplplugin +plugin qtquickcontrols2universalstyleimplplugin +classname QtQuickControls2UniversalStyleImplPlugin +typeinfo plugins.qmltypes +depends QtQuick auto +prefer :/qt-project.org/imports/QtQuick/Controls/Universal/impl/ +CheckIndicator 6.0 CheckIndicator.qml +CheckIndicator 2.0 CheckIndicator.qml +RadioIndicator 6.0 RadioIndicator.qml +RadioIndicator 2.0 RadioIndicator.qml +SwitchIndicator 6.0 SwitchIndicator.qml +SwitchIndicator 2.0 SwitchIndicator.qml + diff --git a/tests/unit/unittest/data/qml/QtQuick/Controls/Universal/qmldir b/tests/unit/unittest/data/qml/QtQuick/Controls/Universal/qmldir new file mode 100644 index 00000000000..05fc6f15c50 --- /dev/null +++ b/tests/unit/unittest/data/qml/QtQuick/Controls/Universal/qmldir @@ -0,0 +1,111 @@ +module QtQuick.Controls.Universal +linktarget Qt6::qtquickcontrols2universalstyleplugin +plugin qtquickcontrols2universalstyleplugin +classname QtQuickControls2UniversalStylePlugin +typeinfo plugins.qmltypes +import QtQuick.Controls.Basic auto +depends QtQuick auto +prefer :/qt-project.org/imports/QtQuick/Controls/Universal/ +ApplicationWindow 6.0 ApplicationWindow.qml +ApplicationWindow 2.0 ApplicationWindow.qml +BusyIndicator 6.0 BusyIndicator.qml +BusyIndicator 2.0 BusyIndicator.qml +Button 6.0 Button.qml +Button 2.0 Button.qml +CheckBox 6.0 CheckBox.qml +CheckBox 2.0 CheckBox.qml +CheckDelegate 6.0 CheckDelegate.qml +CheckDelegate 2.0 CheckDelegate.qml +ComboBox 6.0 ComboBox.qml +ComboBox 2.0 ComboBox.qml +DelayButton 2.2 DelayButton.qml +DelayButton 6.0 DelayButton.qml +Dial 6.0 Dial.qml +Dial 2.0 Dial.qml +Dialog 2.1 Dialog.qml +Dialog 6.0 Dialog.qml +DialogButtonBox 2.1 DialogButtonBox.qml +DialogButtonBox 6.0 DialogButtonBox.qml +Drawer 6.0 Drawer.qml +Drawer 2.0 Drawer.qml +Frame 6.0 Frame.qml +Frame 2.0 Frame.qml +GroupBox 6.0 GroupBox.qml +GroupBox 2.0 GroupBox.qml +HorizontalHeaderView 2.15 HorizontalHeaderView.qml +HorizontalHeaderView 6.0 HorizontalHeaderView.qml +ItemDelegate 6.0 ItemDelegate.qml +ItemDelegate 2.0 ItemDelegate.qml +Label 6.0 Label.qml +Label 2.0 Label.qml +Menu 6.0 Menu.qml +Menu 2.0 Menu.qml +MenuBar 2.3 MenuBar.qml +MenuBar 6.0 MenuBar.qml +MenuBarItem 2.3 MenuBarItem.qml +MenuBarItem 6.0 MenuBarItem.qml +MenuItem 6.0 MenuItem.qml +MenuItem 2.0 MenuItem.qml +MenuSeparator 2.1 MenuSeparator.qml +MenuSeparator 6.0 MenuSeparator.qml +Page 6.0 Page.qml +Page 2.0 Page.qml +PageIndicator 6.0 PageIndicator.qml +PageIndicator 2.0 PageIndicator.qml +Pane 6.0 Pane.qml +Pane 2.0 Pane.qml +Popup 6.0 Popup.qml +Popup 2.0 Popup.qml +ProgressBar 6.0 ProgressBar.qml +ProgressBar 2.0 ProgressBar.qml +RadioButton 6.0 RadioButton.qml +RadioButton 2.0 RadioButton.qml +RadioDelegate 6.0 RadioDelegate.qml +RadioDelegate 2.0 RadioDelegate.qml +RangeSlider 6.0 RangeSlider.qml +RangeSlider 2.0 RangeSlider.qml +RoundButton 2.1 RoundButton.qml +RoundButton 6.0 RoundButton.qml +ScrollView 6.0 ScrollView.qml +ScrollView 2.0 ScrollView.qml +ScrollBar 6.0 ScrollBar.qml +ScrollBar 2.0 ScrollBar.qml +ScrollIndicator 6.0 ScrollIndicator.qml +ScrollIndicator 2.0 ScrollIndicator.qml +SelectionRectangle 6.0 SelectionRectangle.qml +SelectionRectangle 2.0 SelectionRectangle.qml +Slider 6.0 Slider.qml +Slider 2.0 Slider.qml +SpinBox 6.0 SpinBox.qml +SpinBox 2.0 SpinBox.qml +SplitView 2.13 SplitView.qml +SplitView 6.0 SplitView.qml +StackView 6.0 StackView.qml +StackView 2.0 StackView.qml +SwipeDelegate 6.0 SwipeDelegate.qml +SwipeDelegate 2.0 SwipeDelegate.qml +SwitchDelegate 6.0 SwitchDelegate.qml +SwitchDelegate 2.0 SwitchDelegate.qml +Switch 6.0 Switch.qml +Switch 2.0 Switch.qml +TabBar 6.0 TabBar.qml +TabBar 2.0 TabBar.qml +TabButton 6.0 TabButton.qml +TabButton 2.0 TabButton.qml +TextArea 6.0 TextArea.qml +TextArea 2.0 TextArea.qml +TextField 6.0 TextField.qml +TextField 2.0 TextField.qml +ToolBar 6.0 ToolBar.qml +ToolBar 2.0 ToolBar.qml +ToolButton 6.0 ToolButton.qml +ToolButton 2.0 ToolButton.qml +ToolSeparator 2.1 ToolSeparator.qml +ToolSeparator 6.0 ToolSeparator.qml +ToolTip 6.0 ToolTip.qml +ToolTip 2.0 ToolTip.qml +Tumbler 6.0 Tumbler.qml +Tumbler 2.0 Tumbler.qml +VerticalHeaderView 2.15 VerticalHeaderView.qml +VerticalHeaderView 6.0 VerticalHeaderView.qml + diff --git a/tests/unit/unittest/data/qml/QtQuick/Controls/impl/qmldir b/tests/unit/unittest/data/qml/QtQuick/Controls/impl/qmldir new file mode 100644 index 00000000000..84355c3b9f9 --- /dev/null +++ b/tests/unit/unittest/data/qml/QtQuick/Controls/impl/qmldir @@ -0,0 +1,9 @@ +module QtQuick.Controls.impl +linktarget Qt6::qtquickcontrols2implplugin +optional plugin qtquickcontrols2implplugin +classname QtQuickControls2ImplPlugin +typeinfo plugins.qmltypes +depends QtQuick auto +depends QtQuick.Templates auto +prefer :/qt-project.org/imports/QtQuick/Controls/impl/ + diff --git a/tests/unit/unittest/data/qml/QtQuick/Controls/qmldir b/tests/unit/unittest/data/qml/QtQuick/Controls/qmldir new file mode 100644 index 00000000000..86f42c2274d --- /dev/null +++ b/tests/unit/unittest/data/qml/QtQuick/Controls/qmldir @@ -0,0 +1,16 @@ +module QtQuick.Controls +linktarget Qt6::qtquickcontrols2plugin +plugin qtquickcontrols2plugin +classname QtQuickControls2Plugin +designersupported +typeinfo plugins.qmltypes +optional import QtQuick.Controls.Fusion auto +optional import QtQuick.Controls.Material auto +optional import QtQuick.Controls.Imagine auto +optional import QtQuick.Controls.Universal auto +optional import QtQuick.Controls.Windows auto +optional import QtQuick.Controls.macOS auto +optional import QtQuick.Controls.iOS auto +default import QtQuick.Controls.Basic auto +prefer :/qt-project.org/imports/QtQuick/Controls/ + diff --git a/tests/unit/unittest/data/qml/QtQuick/Dialogs/qmldir b/tests/unit/unittest/data/qml/QtQuick/Dialogs/qmldir new file mode 100644 index 00000000000..9468d95530c --- /dev/null +++ b/tests/unit/unittest/data/qml/QtQuick/Dialogs/qmldir @@ -0,0 +1,8 @@ +module QtQuick.Dialogs +linktarget Qt6::qtquickdialogsplugin +optional plugin qtquickdialogsplugin +classname QtQuickDialogsPlugin +typeinfo plugins.qmltypes +depends QtQuick auto +prefer :/qt-project.org/imports/QtQuick/Dialogs/ + diff --git a/tests/unit/unittest/data/qml/QtQuick/Dialogs/quickimpl/qmldir b/tests/unit/unittest/data/qml/QtQuick/Dialogs/quickimpl/qmldir new file mode 100644 index 00000000000..85a331e3067 --- /dev/null +++ b/tests/unit/unittest/data/qml/QtQuick/Dialogs/quickimpl/qmldir @@ -0,0 +1,57 @@ +module QtQuick.Dialogs.quickimpl +linktarget Qt6::qtquickdialogs2quickimplplugin +optional plugin qtquickdialogs2quickimplplugin +classname QtQuickDialogs2QuickImplPlugin +typeinfo plugins.qmltypes +depends QtQuick auto +depends QtQuick.Templates auto +depends QtQuick.Layouts auto +prefer :/qt-project.org/imports/QtQuick/Dialogs/quickimpl/ +ColorDialog 6.0 qml/ColorDialog.qml +ColorInputs 6.0 qml/ColorInputs.qml +FileDialog 6.0 qml/FileDialog.qml +FileDialogDelegate 6.0 qml/FileDialogDelegate.qml +FileDialogDelegateLabel 6.0 qml/FileDialogDelegateLabel.qml +FolderBreadcrumbBar 6.0 qml/FolderBreadcrumbBar.qml +FolderDialog 6.0 qml/FolderDialog.qml +FolderDialogDelegate 6.0 qml/FolderDialogDelegate.qml +FolderDialogDelegateLabel 6.0 qml/FolderDialogDelegateLabel.qml +FontDialog 6.0 qml/FontDialog.qml +FontDialogContent 6.0 qml/FontDialogContent.qml +HueGradient 6.0 qml/HueGradient.qml +MessageDialog 6.0 qml/MessageDialog.qml +PickerHandle 6.0 qml/PickerHandle.qml +SaturationLightnessPicker 6.0 qml/SaturationLightnessPicker.qml +ColorDialog 6.0 qml/+Fusion/ColorDialog.qml +FileDialog 6.0 qml/+Fusion/FileDialog.qml +FileDialogDelegate 6.0 qml/+Fusion/FileDialogDelegate.qml +FolderBreadcrumbBar 6.0 qml/+Fusion/FolderBreadcrumbBar.qml +FolderDialog 6.0 qml/+Fusion/FolderDialog.qml +FolderDialogDelegate 6.0 qml/+Fusion/FolderDialogDelegate.qml +FontDialog 6.0 qml/+Fusion/FontDialog.qml +MessageDialog 6.0 qml/+Fusion/MessageDialog.qml +ColorDialog 6.0 qml/+Imagine/ColorDialog.qml +FileDialog 6.0 qml/+Imagine/FileDialog.qml +FileDialogDelegate 6.0 qml/+Imagine/FileDialogDelegate.qml +FolderBreadcrumbBar 6.0 qml/+Imagine/FolderBreadcrumbBar.qml +FolderDialog 6.0 qml/+Imagine/FolderDialog.qml +FolderDialogDelegate 6.0 qml/+Imagine/FolderDialogDelegate.qml +FontDialog 6.0 qml/+Imagine/FontDialog.qml +MessageDialog 6.0 qml/+Imagine/MessageDialog.qml +ColorDialog 6.0 qml/+Material/ColorDialog.qml +FileDialog 6.0 qml/+Material/FileDialog.qml +FileDialogDelegate 6.0 qml/+Material/FileDialogDelegate.qml +FolderBreadcrumbBar 6.0 qml/+Material/FolderBreadcrumbBar.qml +FolderDialog 6.0 qml/+Material/FolderDialog.qml +FolderDialogDelegate 6.0 qml/+Material/FolderDialogDelegate.qml +FontDialog 6.0 qml/+Material/FontDialog.qml +MessageDialog 6.0 qml/+Material/MessageDialog.qml +ColorDialog 6.0 qml/+Universal/ColorDialog.qml +FileDialog 6.0 qml/+Universal/FileDialog.qml +FileDialogDelegate 6.0 qml/+Universal/FileDialogDelegate.qml +FolderBreadcrumbBar 6.0 qml/+Universal/FolderBreadcrumbBar.qml +FolderDialog 6.0 qml/+Universal/FolderDialog.qml +FolderDialogDelegate 6.0 qml/+Universal/FolderDialogDelegate.qml +FontDialog 6.0 qml/+Universal/FontDialog.qml +MessageDialog 6.0 qml/+Universal/MessageDialog.qml + diff --git a/tests/unit/unittest/data/qml/QtQuick/Effects/qmldir b/tests/unit/unittest/data/qml/QtQuick/Effects/qmldir new file mode 100644 index 00000000000..4fbc513d21e --- /dev/null +++ b/tests/unit/unittest/data/qml/QtQuick/Effects/qmldir @@ -0,0 +1,8 @@ +module QtQuick.Effects +linktarget Qt6::effectsplugin +optional plugin effectsplugin +classname QtQuickEffectsPlugin +typeinfo plugins.qmltypes +depends QtQuick auto +prefer :/qt-project.org/imports/QtQuick/Effects/ + diff --git a/tests/unit/unittest/data/qml/QtQuick/Layouts/qmldir b/tests/unit/unittest/data/qml/QtQuick/Layouts/qmldir new file mode 100644 index 00000000000..9fa0f0d84c5 --- /dev/null +++ b/tests/unit/unittest/data/qml/QtQuick/Layouts/qmldir @@ -0,0 +1,9 @@ +module QtQuick.Layouts +linktarget Qt6::qquicklayoutsplugin +optional plugin qquicklayoutsplugin +classname QtQuickLayoutsPlugin +designersupported +typeinfo plugins.qmltypes +depends QtQuick auto +prefer :/qt-project.org/imports/QtQuick/Layouts/ + diff --git a/tests/unit/unittest/data/qml/QtQuick/LocalStorage/qmldir b/tests/unit/unittest/data/qml/QtQuick/LocalStorage/qmldir new file mode 100644 index 00000000000..de9d480b6cd --- /dev/null +++ b/tests/unit/unittest/data/qml/QtQuick/LocalStorage/qmldir @@ -0,0 +1,7 @@ +module QtQuick.LocalStorage +linktarget Qt6::qmllocalstorageplugin +optional plugin qmllocalstorageplugin +classname QQmlLocalStoragePlugin +typeinfo plugins.qmltypes +prefer :/qt-project.org/imports/QtQuick/LocalStorage/ + diff --git a/tests/unit/unittest/data/qml/QtQuick/NativeStyle/qmldir b/tests/unit/unittest/data/qml/QtQuick/NativeStyle/qmldir new file mode 100644 index 00000000000..4ab0813a4d7 --- /dev/null +++ b/tests/unit/unittest/data/qml/QtQuick/NativeStyle/qmldir @@ -0,0 +1,38 @@ +module QtQuick.NativeStyle +linktarget Qt6::qtquickcontrols2nativestyleplugin +plugin qtquickcontrols2nativestyleplugin +classname QtQuickControls2NativeStylePlugin +typeinfo plugins.qmltypes +depends QtQuick.Controls auto +depends QtQuick.Layouts auto +depends QtQuick auto +prefer :/qt-project.org/imports/QtQuick/NativeStyle/ +DefaultButton 6.0 controls/DefaultButton.qml +DefaultButton 2.0 controls/DefaultButton.qml +DefaultSlider 6.0 controls/DefaultSlider.qml +DefaultSlider 2.0 controls/DefaultSlider.qml +DefaultGroupBox 6.0 controls/DefaultGroupBox.qml +DefaultGroupBox 2.0 controls/DefaultGroupBox.qml +DefaultCheckBox 6.0 controls/DefaultCheckBox.qml +DefaultCheckBox 2.0 controls/DefaultCheckBox.qml +DefaultRadioButton 6.0 controls/DefaultRadioButton.qml +DefaultRadioButton 2.0 controls/DefaultRadioButton.qml +DefaultSpinBox 6.0 controls/DefaultSpinBox.qml +DefaultSpinBox 2.0 controls/DefaultSpinBox.qml +DefaultTextField 6.0 controls/DefaultTextField.qml +DefaultTextField 2.0 controls/DefaultTextField.qml +DefaultFrame 6.0 controls/DefaultFrame.qml +DefaultFrame 2.0 controls/DefaultFrame.qml +DefaultTextArea 6.0 controls/DefaultTextArea.qml +DefaultTextArea 2.0 controls/DefaultTextArea.qml +DefaultComboBox 6.0 controls/DefaultComboBox.qml +DefaultComboBox 2.0 controls/DefaultComboBox.qml +DefaultScrollBar 6.0 controls/DefaultScrollBar.qml +DefaultScrollBar 2.0 controls/DefaultScrollBar.qml +DefaultProgressBar 6.0 controls/DefaultProgressBar.qml +DefaultProgressBar 2.0 controls/DefaultProgressBar.qml +DefaultDial 6.0 controls/DefaultDial.qml +DefaultDial 2.0 controls/DefaultDial.qml +DefaultTreeViewDelegate 6.0 controls/DefaultTreeViewDelegate.qml +DefaultTreeViewDelegate 2.0 controls/DefaultTreeViewDelegate.qml + diff --git a/tests/unit/unittest/data/qml/QtQuick/Particles/qmldir b/tests/unit/unittest/data/qml/QtQuick/Particles/qmldir new file mode 100644 index 00000000000..163fb28bdf8 --- /dev/null +++ b/tests/unit/unittest/data/qml/QtQuick/Particles/qmldir @@ -0,0 +1,8 @@ +module QtQuick.Particles +linktarget Qt6::particlesplugin +optional plugin particlesplugin +classname QtQuick2ParticlesPlugin +typeinfo plugins.qmltypes +depends QtQuick auto +prefer :/qt-project.org/imports/QtQuick/Particles/ + diff --git a/tests/unit/unittest/data/qml/QtQuick/Pdf/qmldir b/tests/unit/unittest/data/qml/QtQuick/Pdf/qmldir new file mode 100644 index 00000000000..9004a9ed71e --- /dev/null +++ b/tests/unit/unittest/data/qml/QtQuick/Pdf/qmldir @@ -0,0 +1,22 @@ +module QtQuick.Pdf +linktarget Qt6::PdfQuickplugin +optional plugin pdfquickplugin +classname QtQuick_PdfPlugin +typeinfo plugins.qmltypes +depends QtQuick auto +prefer :/qt-project.org/imports/QtQuick/Pdf/ +PdfStyle 6.0 +Material/PdfStyle.qml +PdfStyle 5.0 +Material/PdfStyle.qml +PdfStyle 6.0 +Universal/PdfStyle.qml +PdfStyle 5.0 +Universal/PdfStyle.qml +PdfLinkDelegate 6.0 PdfLinkDelegate.qml +PdfLinkDelegate 5.0 PdfLinkDelegate.qml +PdfMultiPageView 6.0 PdfMultiPageView.qml +PdfMultiPageView 5.0 PdfMultiPageView.qml +PdfPageView 6.0 PdfPageView.qml +PdfPageView 5.0 PdfPageView.qml +PdfScrollablePageView 6.0 PdfScrollablePageView.qml +PdfScrollablePageView 5.0 PdfScrollablePageView.qml +PdfStyle 6.0 PdfStyle.qml +PdfStyle 5.0 PdfStyle.qml + diff --git a/tests/unit/unittest/data/qml/QtQuick/Scene2D/qmldir b/tests/unit/unittest/data/qml/QtQuick/Scene2D/qmldir new file mode 100644 index 00000000000..8a222334339 --- /dev/null +++ b/tests/unit/unittest/data/qml/QtQuick/Scene2D/qmldir @@ -0,0 +1,7 @@ +module QtQuick.Scene2D +linktarget Qt6::qtquickscene2dplugin +plugin qtquickscene2dplugin +classname QtQuickScene2DPlugin +typeinfo plugins.qmltypes +prefer :/qt-project.org/imports/QtQuick/Scene2D/ + diff --git a/tests/unit/unittest/data/qml/QtQuick/Scene3D/qmldir b/tests/unit/unittest/data/qml/QtQuick/Scene3D/qmldir new file mode 100644 index 00000000000..6dcd613d391 --- /dev/null +++ b/tests/unit/unittest/data/qml/QtQuick/Scene3D/qmldir @@ -0,0 +1,7 @@ +module QtQuick.Scene3D +linktarget Qt6::qtquickscene3dplugin +plugin qtquickscene3dplugin +classname QtQuickScene3DPlugin +typeinfo plugins.qmltypes +prefer :/qt-project.org/imports/QtQuick/Scene3D/ + diff --git a/tests/unit/unittest/data/qml/QtQuick/Shapes/qmldir b/tests/unit/unittest/data/qml/QtQuick/Shapes/qmldir new file mode 100644 index 00000000000..428ff391a4a --- /dev/null +++ b/tests/unit/unittest/data/qml/QtQuick/Shapes/qmldir @@ -0,0 +1,8 @@ +module QtQuick.Shapes +linktarget Qt6::qmlshapesplugin +plugin qmlshapesplugin +classname QmlShapesPlugin +typeinfo plugins.qmltypes +depends QtQuick auto +prefer :/qt-project.org/imports/QtQuick/Shapes/ + diff --git a/tests/unit/unittest/data/qml/QtQuick/Templates/qmldir b/tests/unit/unittest/data/qml/QtQuick/Templates/qmldir new file mode 100644 index 00000000000..bd768665387 --- /dev/null +++ b/tests/unit/unittest/data/qml/QtQuick/Templates/qmldir @@ -0,0 +1,8 @@ +module QtQuick.Templates +linktarget Qt6::qtquicktemplates2plugin +plugin qtquicktemplates2plugin +classname QtQuickTemplates2Plugin +typeinfo plugins.qmltypes +depends QtQuick auto +prefer :/qt-project.org/imports/QtQuick/Templates/ + diff --git a/tests/unit/unittest/data/qml/QtQuick/Timeline/qmldir b/tests/unit/unittest/data/qml/QtQuick/Timeline/qmldir new file mode 100644 index 00000000000..f4e953c568e --- /dev/null +++ b/tests/unit/unittest/data/qml/QtQuick/Timeline/qmldir @@ -0,0 +1,9 @@ +module QtQuick.Timeline +linktarget Qt6::qtquicktimelineplugin +optional plugin qtquicktimelineplugin +classname QtQuickTimelinePlugin +designersupported +typeinfo plugins.qmltypes +depends QtQuick +prefer :/qt-project.org/imports/QtQuick/Timeline/ + diff --git a/tests/unit/unittest/data/qml/QtQuick/VirtualKeyboard/Components/qmldir b/tests/unit/unittest/data/qml/QtQuick/VirtualKeyboard/Components/qmldir new file mode 100644 index 00000000000..d1f487261ae --- /dev/null +++ b/tests/unit/unittest/data/qml/QtQuick/VirtualKeyboard/Components/qmldir @@ -0,0 +1,103 @@ +module QtQuick.VirtualKeyboard.Components +linktarget Qt6::qtvkbcomponentsplugin +optional plugin qtvkbcomponentsplugin +classname QtQuick_VirtualKeyboard_ComponentsPlugin +typeinfo qtvkbcomponentsplugin.qmltypes +depends QtQuick auto +depends QtQuick.Layouts auto +depends QtQuick.VirtualKeyboard.Settings auto +prefer :/qt-project.org/imports/QtQuick/VirtualKeyboard/Components/ +AlternativeKeys 6.0 AlternativeKeys.qml +AlternativeKeys 2.0 AlternativeKeys.qml +AlternativeKeys 1.0 AlternativeKeys.qml +BackspaceKey 6.0 BackspaceKey.qml +BackspaceKey 2.0 BackspaceKey.qml +BackspaceKey 1.0 BackspaceKey.qml +BaseKey 6.0 BaseKey.qml +BaseKey 2.0 BaseKey.qml +BaseKey 1.0 BaseKey.qml +ChangeLanguageKey 6.0 ChangeLanguageKey.qml +ChangeLanguageKey 2.0 ChangeLanguageKey.qml +ChangeLanguageKey 1.0 ChangeLanguageKey.qml +CharacterPreviewBubble 6.0 CharacterPreviewBubble.qml +CharacterPreviewBubble 2.0 CharacterPreviewBubble.qml +CharacterPreviewBubble 1.0 CharacterPreviewBubble.qml +EnterKey 6.0 EnterKey.qml +EnterKey 2.0 EnterKey.qml +EnterKey 1.0 EnterKey.qml +FillerKey 6.0 FillerKey.qml +FillerKey 2.0 FillerKey.qml +FillerKey 1.0 FillerKey.qml +FlickKey 6.0 FlickKey.qml +FlickKey 2.0 FlickKey.qml +FlickKey 1.0 FlickKey.qml +FunctionPopupList 6.0 FunctionPopupList.qml +FunctionPopupList 2.0 FunctionPopupList.qml +FunctionPopupList 1.0 FunctionPopupList.qml +HandwritingModeKey 6.0 HandwritingModeKey.qml +HandwritingModeKey 2.0 HandwritingModeKey.qml +HandwritingModeKey 1.0 HandwritingModeKey.qml +HideKeyboardKey 6.0 HideKeyboardKey.qml +HideKeyboardKey 2.0 HideKeyboardKey.qml +HideKeyboardKey 1.0 HideKeyboardKey.qml +InputModeKey 6.0 InputModeKey.qml +InputModeKey 2.0 InputModeKey.qml +InputModeKey 1.0 InputModeKey.qml +Key 6.0 Key.qml +Key 2.0 Key.qml +Key 1.0 Key.qml +Keyboard 6.0 Keyboard.qml +Keyboard 2.0 Keyboard.qml +Keyboard 1.0 Keyboard.qml +KeyboardColumn 6.0 KeyboardColumn.qml +KeyboardColumn 2.0 KeyboardColumn.qml +KeyboardColumn 1.0 KeyboardColumn.qml +KeyboardLayout 6.0 KeyboardLayout.qml +KeyboardLayout 2.0 KeyboardLayout.qml +KeyboardLayout 1.0 KeyboardLayout.qml +KeyboardLayoutLoader 6.0 KeyboardLayoutLoader.qml +KeyboardLayoutLoader 2.0 KeyboardLayoutLoader.qml +KeyboardLayoutLoader 1.0 KeyboardLayoutLoader.qml +KeyboardRow 6.0 KeyboardRow.qml +KeyboardRow 2.0 KeyboardRow.qml +KeyboardRow 1.0 KeyboardRow.qml +ModeKey 6.0 ModeKey.qml +ModeKey 2.0 ModeKey.qml +ModeKey 1.0 ModeKey.qml +MultiSoundEffect 6.0 MultiSoundEffect.qml +MultiSoundEffect 2.0 MultiSoundEffect.qml +MultiSoundEffect 1.0 MultiSoundEffect.qml +MultitapInputMethod 6.0 MultitapInputMethod.qml +MultitapInputMethod 2.0 MultitapInputMethod.qml +MultitapInputMethod 1.0 MultitapInputMethod.qml +NumberKey 6.0 NumberKey.qml +NumberKey 2.0 NumberKey.qml +NumberKey 1.0 NumberKey.qml +PopupList 6.0 PopupList.qml +PopupList 2.0 PopupList.qml +PopupList 1.0 PopupList.qml +SelectionControl 6.0 SelectionControl.qml +SelectionControl 2.0 SelectionControl.qml +SelectionControl 1.0 SelectionControl.qml +ShadowInputControl 6.0 ShadowInputControl.qml +ShadowInputControl 2.0 ShadowInputControl.qml +ShadowInputControl 1.0 ShadowInputControl.qml +ShiftKey 6.0 ShiftKey.qml +ShiftKey 2.0 ShiftKey.qml +ShiftKey 1.0 ShiftKey.qml +SpaceKey 6.0 SpaceKey.qml +SpaceKey 2.0 SpaceKey.qml +SpaceKey 1.0 SpaceKey.qml +SymbolModeKey 6.0 SymbolModeKey.qml +SymbolModeKey 2.0 SymbolModeKey.qml +SymbolModeKey 1.0 SymbolModeKey.qml +TraceInputArea 6.0 TraceInputArea.qml +TraceInputArea 2.0 TraceInputArea.qml +TraceInputArea 1.0 TraceInputArea.qml +TraceInputKey 6.0 TraceInputKey.qml +TraceInputKey 2.0 TraceInputKey.qml +TraceInputKey 1.0 TraceInputKey.qml +WordCandidatePopupList 6.0 WordCandidatePopupList.qml +WordCandidatePopupList 2.0 WordCandidatePopupList.qml +WordCandidatePopupList 1.0 WordCandidatePopupList.qml + diff --git a/tests/unit/unittest/data/qml/QtQuick/VirtualKeyboard/Layouts/qmldir b/tests/unit/unittest/data/qml/QtQuick/VirtualKeyboard/Layouts/qmldir new file mode 100644 index 00000000000..626958542b1 --- /dev/null +++ b/tests/unit/unittest/data/qml/QtQuick/VirtualKeyboard/Layouts/qmldir @@ -0,0 +1,7 @@ +module QtQuick.VirtualKeyboard.Layouts +linktarget Qt6::qtvkblayoutsplugin +plugin qtvkblayoutsplugin +classname QtQuick_VirtualKeyboard_LayoutsPlugin +typeinfo qtvkblayoutsplugin.qmltypes +prefer :/qt-project.org/imports/QtQuick/VirtualKeyboard/Layouts/ + diff --git a/tests/unit/unittest/data/qml/QtQuick/VirtualKeyboard/Plugins/Hangul/qmldir b/tests/unit/unittest/data/qml/QtQuick/VirtualKeyboard/Plugins/Hangul/qmldir new file mode 100644 index 00000000000..d884562c66d --- /dev/null +++ b/tests/unit/unittest/data/qml/QtQuick/VirtualKeyboard/Plugins/Hangul/qmldir @@ -0,0 +1,8 @@ +module QtQuick.VirtualKeyboard.Plugins.Hangul +linktarget Qt6::qtvkbhangulplugin +plugin qtvkbhangulplugin +classname QtQuick_VirtualKeyboard_Plugins_HangulPlugin +typeinfo plugins.qmltypes +depends QtQuick.VirtualKeyboard auto +prefer :/qt-project.org/imports/QtQuick/VirtualKeyboard/Plugins/Hangul/ + diff --git a/tests/unit/unittest/data/qml/QtQuick/VirtualKeyboard/Plugins/OpenWNN/qmldir b/tests/unit/unittest/data/qml/QtQuick/VirtualKeyboard/Plugins/OpenWNN/qmldir new file mode 100644 index 00000000000..d356a13a061 --- /dev/null +++ b/tests/unit/unittest/data/qml/QtQuick/VirtualKeyboard/Plugins/OpenWNN/qmldir @@ -0,0 +1,8 @@ +module QtQuick.VirtualKeyboard.Plugins.OpenWNN +linktarget Qt6::qtvkbopenwnnplugin +plugin qtvkbopenwnnplugin +classname QtQuick_VirtualKeyboard_Plugins_OpenWNNPlugin +typeinfo plugins.qmltypes +depends QtQuick.VirtualKeyboard auto +prefer :/qt-project.org/imports/QtQuick/VirtualKeyboard/Plugins/OpenWNN/ + diff --git a/tests/unit/unittest/data/qml/QtQuick/VirtualKeyboard/Plugins/Pinyin/qmldir b/tests/unit/unittest/data/qml/QtQuick/VirtualKeyboard/Plugins/Pinyin/qmldir new file mode 100644 index 00000000000..cdf07320229 --- /dev/null +++ b/tests/unit/unittest/data/qml/QtQuick/VirtualKeyboard/Plugins/Pinyin/qmldir @@ -0,0 +1,8 @@ +module QtQuick.VirtualKeyboard.Plugins.Pinyin +linktarget Qt6::qtvkbpinyinplugin +plugin qtvkbpinyinplugin +classname QtQuick_VirtualKeyboard_Plugins_PinyinPlugin +typeinfo plugins.qmltypes +depends QtQuick.VirtualKeyboard auto +prefer :/qt-project.org/imports/QtQuick/VirtualKeyboard/Plugins/Pinyin/ + diff --git a/tests/unit/unittest/data/qml/QtQuick/VirtualKeyboard/Plugins/TCIme/qmldir b/tests/unit/unittest/data/qml/QtQuick/VirtualKeyboard/Plugins/TCIme/qmldir new file mode 100644 index 00000000000..f3d7c580539 --- /dev/null +++ b/tests/unit/unittest/data/qml/QtQuick/VirtualKeyboard/Plugins/TCIme/qmldir @@ -0,0 +1,8 @@ +module QtQuick.VirtualKeyboard.Plugins.TCIme +linktarget Qt6::qtvkbtcimeplugin +plugin qtvkbtcimeplugin +classname QtQuick_VirtualKeyboard_Plugins_TCImePlugin +typeinfo plugins.qmltypes +depends QtQuick.VirtualKeyboard auto +prefer :/qt-project.org/imports/QtQuick/VirtualKeyboard/Plugins/TCIme/ + diff --git a/tests/unit/unittest/data/qml/QtQuick/VirtualKeyboard/Plugins/Thai/qmldir b/tests/unit/unittest/data/qml/QtQuick/VirtualKeyboard/Plugins/Thai/qmldir new file mode 100644 index 00000000000..79164dda638 --- /dev/null +++ b/tests/unit/unittest/data/qml/QtQuick/VirtualKeyboard/Plugins/Thai/qmldir @@ -0,0 +1,8 @@ +module QtQuick.VirtualKeyboard.Plugins.Thai +linktarget Qt6::qtvkbthaiplugin +plugin qtvkbthaiplugin +classname QtQuick_VirtualKeyboard_Plugins_ThaiPlugin +typeinfo plugins.qmltypes +depends QtQuick.VirtualKeyboard auto +prefer :/qt-project.org/imports/QtQuick/VirtualKeyboard/Plugins/Thai/ + diff --git a/tests/unit/unittest/data/qml/QtQuick/VirtualKeyboard/Plugins/qmldir b/tests/unit/unittest/data/qml/QtQuick/VirtualKeyboard/Plugins/qmldir new file mode 100644 index 00000000000..d07a1a1c2d7 --- /dev/null +++ b/tests/unit/unittest/data/qml/QtQuick/VirtualKeyboard/Plugins/qmldir @@ -0,0 +1,12 @@ +module QtQuick.VirtualKeyboard.Plugins +linktarget Qt6::qtvkbpluginsplugin +optional plugin qtvkbpluginsplugin +classname QtQuick_VirtualKeyboard_PluginsPlugin +typeinfo qtvkbpluginsplugin.qmltypes +import QtQuick.VirtualKeyboard.Plugins.Hangul auto +import QtQuick.VirtualKeyboard.Plugins.OpenWNN auto +import QtQuick.VirtualKeyboard.Plugins.Pinyin auto +import QtQuick.VirtualKeyboard.Plugins.TCIme auto +import QtQuick.VirtualKeyboard.Plugins.Thai auto +prefer :/qt-project.org/imports/QtQuick/VirtualKeyboard/Plugins/ + diff --git a/tests/unit/unittest/data/qml/QtQuick/VirtualKeyboard/Settings/qmldir b/tests/unit/unittest/data/qml/QtQuick/VirtualKeyboard/Settings/qmldir new file mode 100644 index 00000000000..a3043cf3f69 --- /dev/null +++ b/tests/unit/unittest/data/qml/QtQuick/VirtualKeyboard/Settings/qmldir @@ -0,0 +1,7 @@ +module QtQuick.VirtualKeyboard.Settings +linktarget Qt6::qtvkbsettingsplugin +plugin qtvkbsettingsplugin +classname QtQuick_VirtualKeyboard_SettingsPlugin +typeinfo plugins.qmltypes +prefer :/qt-project.org/imports/QtQuick/VirtualKeyboard/Settings/ + diff --git a/tests/unit/unittest/data/qml/QtQuick/VirtualKeyboard/Styles/Builtin/qmldir b/tests/unit/unittest/data/qml/QtQuick/VirtualKeyboard/Styles/Builtin/qmldir new file mode 100644 index 00000000000..fdf9f086abb --- /dev/null +++ b/tests/unit/unittest/data/qml/QtQuick/VirtualKeyboard/Styles/Builtin/qmldir @@ -0,0 +1,7 @@ +module QtQuick.VirtualKeyboard.Styles.Builtin +linktarget Qt6::qtvkbbuiltinstylesplugin +plugin qtvkbbuiltinstylesplugin +classname QtQuickVirtualKeyboardStylesBuiltinPlugin +typeinfo plugins.qmltypes +prefer :/qt-project.org/imports/QtQuick/VirtualKeyboard/Styles/Builtin/ + diff --git a/tests/unit/unittest/data/qml/QtQuick/VirtualKeyboard/Styles/qmldir b/tests/unit/unittest/data/qml/QtQuick/VirtualKeyboard/Styles/qmldir new file mode 100644 index 00000000000..8804cd8a731 --- /dev/null +++ b/tests/unit/unittest/data/qml/QtQuick/VirtualKeyboard/Styles/qmldir @@ -0,0 +1,30 @@ +module QtQuick.VirtualKeyboard.Styles +linktarget Qt6::qtvkbstylesplugin +plugin qtvkbstylesplugin +classname QtQuickVirtualKeyboardStylesPlugin +typeinfo plugins.qmltypes +import QtQuick.VirtualKeyboard.Styles.Builtin auto +depends QtQuick auto +prefer :/qt-project.org/imports/QtQuick/VirtualKeyboard/Styles/ +KeyboardStyle 6.0 KeyboardStyle.qml +KeyboardStyle 2.0 KeyboardStyle.qml +KeyboardStyle 1.0 KeyboardStyle.qml +KeyIcon 6.0 KeyIcon.qml +KeyIcon 2.0 KeyIcon.qml +KeyIcon 1.0 KeyIcon.qml +KeyPanel 6.0 KeyPanel.qml +KeyPanel 2.0 KeyPanel.qml +KeyPanel 1.0 KeyPanel.qml +SelectionListItem 6.0 SelectionListItem.qml +SelectionListItem 2.0 SelectionListItem.qml +SelectionListItem 1.0 SelectionListItem.qml +TraceInputKeyPanel 6.0 TraceInputKeyPanel.qml +TraceInputKeyPanel 2.0 TraceInputKeyPanel.qml +TraceInputKeyPanel 1.0 TraceInputKeyPanel.qml +TraceCanvas 6.0 TraceCanvas.qml +TraceCanvas 2.0 TraceCanvas.qml +TraceCanvas 1.0 TraceCanvas.qml +TraceUtils 6.0 TraceUtils.js +TraceUtils 2.0 TraceUtils.js +TraceUtils 1.0 TraceUtils.js + diff --git a/tests/unit/unittest/data/qml/QtQuick/VirtualKeyboard/qmldir b/tests/unit/unittest/data/qml/QtQuick/VirtualKeyboard/qmldir new file mode 100644 index 00000000000..754cc754490 --- /dev/null +++ b/tests/unit/unittest/data/qml/QtQuick/VirtualKeyboard/qmldir @@ -0,0 +1,25 @@ +module QtQuick.VirtualKeyboard +linktarget Qt6::qtvkbplugin +optional plugin qtvkbplugin +classname QtQuick_VirtualKeyboardPlugin +typeinfo plugins.qmltypes +import QtQuick.VirtualKeyboard.Layouts auto +import QtQuick.VirtualKeyboard.Components auto +depends QtQuick auto +depends QtQuick.Window auto +depends QtQuick.Layouts auto +depends Qt.labs.folderlistmodel auto +depends QtQuick.VirtualKeyboard.Settings auto +depends QtQuick.VirtualKeyboard.Styles auto +depends QtQuick.VirtualKeyboard.Plugins auto +prefer :/qt-project.org/imports/QtQuick/VirtualKeyboard/ +HandwritingInputPanel 6.0 HandwritingInputPanel.qml +HandwritingInputPanel 2.0 HandwritingInputPanel.qml +HandwritingInputPanel 1.0 HandwritingInputPanel.qml +InputPanel 6.0 InputPanel.qml +InputPanel 2.0 InputPanel.qml +InputPanel 1.0 InputPanel.qml +EnterKey 6.0 EnterKey.qml +EnterKey 2.0 EnterKey.qml +EnterKey 1.0 EnterKey.qml + diff --git a/tests/unit/unittest/data/qml/QtQuick/Window/qmldir b/tests/unit/unittest/data/qml/QtQuick/Window/qmldir new file mode 100644 index 00000000000..5ff5ce84ddb --- /dev/null +++ b/tests/unit/unittest/data/qml/QtQuick/Window/qmldir @@ -0,0 +1,8 @@ +module QtQuick.Window +linktarget Qt6::quickwindow +plugin quickwindowplugin +classname QtQuick_WindowPlugin +typeinfo quickwindow.qmltypes +import QtQuick auto +prefer :/qt-project.org/imports/QtQuick/Window/ + diff --git a/tests/unit/unittest/data/qml/QtQuick/qmldir b/tests/unit/unittest/data/qml/QtQuick/qmldir new file mode 100644 index 00000000000..7d68a105aa2 --- /dev/null +++ b/tests/unit/unittest/data/qml/QtQuick/qmldir @@ -0,0 +1,9 @@ +module QtQuick +linktarget Qt6::qtquick2plugin +optional plugin qtquick2plugin +classname QtQuick2Plugin +designersupported +typeinfo plugins.qmltypes +import QtQml auto +prefer :/qt-project.org/imports/QtQuick/ + diff --git a/tests/unit/unittest/data/qml/QtQuick/tooling/qmldir b/tests/unit/unittest/data/qml/QtQuick/tooling/qmldir new file mode 100644 index 00000000000..99798d19a69 --- /dev/null +++ b/tests/unit/unittest/data/qml/QtQuick/tooling/qmldir @@ -0,0 +1,23 @@ +module QtQuick.tooling +linktarget Qt6::quicktooling +plugin quicktoolingplugin +classname QtQuick_toolingPlugin +typeinfo quicktooling.qmltypes +prefer :/qt-project.org/imports/QtQuick/tooling/ +Component 1.2 Component.qml +Component 6.0 Component.qml +Enum 1.2 Enum.qml +Enum 6.0 Enum.qml +Member 1.2 Member.qml +Member 6.0 Member.qml +Method 1.2 Method.qml +Method 6.0 Method.qml +Module 1.2 Module.qml +Module 6.0 Module.qml +Parameter 1.2 Parameter.qml +Parameter 6.0 Parameter.qml +Property 1.2 Property.qml +Property 6.0 Property.qml +Signal 1.2 Signal.qml +Signal 6.0 Signal.qml + diff --git a/tests/unit/unittest/data/qml/QtQuick3D/AssetUtils/qmldir b/tests/unit/unittest/data/qml/QtQuick3D/AssetUtils/qmldir new file mode 100644 index 00000000000..5c759031714 --- /dev/null +++ b/tests/unit/unittest/data/qml/QtQuick3D/AssetUtils/qmldir @@ -0,0 +1,9 @@ +module QtQuick3D.AssetUtils +linktarget Qt6::qtquick3dassetutilsplugin +optional plugin qtquick3dassetutilsplugin +classname QtQuick3DAssetUtilsPlugin +designersupported +typeinfo plugins.qmltypes +depends QtQuick3D auto +prefer :/qt-project.org/imports/QtQuick3D/AssetUtils/ + diff --git a/tests/unit/unittest/data/qml/QtQuick3D/Effects/qmldir b/tests/unit/unittest/data/qml/QtQuick3D/Effects/qmldir new file mode 100644 index 00000000000..cec26d9c2a4 --- /dev/null +++ b/tests/unit/unittest/data/qml/QtQuick3D/Effects/qmldir @@ -0,0 +1,31 @@ +module QtQuick3D.Effects +linktarget Qt6::qtquick3deffectplugin +optional plugin qtquick3deffectplugin +classname QtQuick3DEffectPlugin +designersupported +typeinfo Quick3DEffects.qmltypes +depends QtQuick3D auto +depends QtQuick.Window auto +prefer :/qt-project.org/imports/QtQuick3D/Effects/ +Vignette 6.0 Vignette.qml +TiltShift 6.0 TiltShift.qml +SCurveTonemap 6.0 SCurveTonemap.qml +Scatter 6.0 Scatter.qml +MotionBlur 6.0 MotionBlur.qml +HDRBloomTonemap 6.0 HDRBloomTonemap.qml +GaussianBlur 6.0 GaussianBlur.qml +Fxaa 6.0 Fxaa.qml +Flip 6.0 Flip.qml +Emboss 6.0 Emboss.qml +EdgeDetect 6.0 EdgeDetect.qml +DistortionSpiral 6.0 DistortionSpiral.qml +DistortionSphere 6.0 DistortionSphere.qml +DistortionRipple 6.0 DistortionRipple.qml +Desaturate 6.0 Desaturate.qml +DepthOfFieldHQBlur 6.0 DepthOfFieldHQBlur.qml +ColorMaster 6.0 ColorMaster.qml +ChromaticAberration 6.0 ChromaticAberration.qml +BrushStrokes 6.0 BrushStrokes.qml +Blur 6.0 Blur.qml +AdditiveColorGradient 6.0 AdditiveColorGradient.qml + diff --git a/tests/unit/unittest/data/qml/QtQuick3D/Helpers/impl/qmldir b/tests/unit/unittest/data/qml/QtQuick3D/Helpers/impl/qmldir new file mode 100644 index 00000000000..013c950285b --- /dev/null +++ b/tests/unit/unittest/data/qml/QtQuick3D/Helpers/impl/qmldir @@ -0,0 +1,12 @@ +module QtQuick3D.Helpers.impl +linktarget Qt6::qtquick3dhelpersimplplugin +plugin qtquick3dhelpersimplplugin +classname QtQuick3DHelpersImplPlugin +typeinfo plugins.qmltypes +depends QtQuick3D auto +depends Quick3DHelpers auto +prefer :/qt-project.org/imports/QtQuick3D/Helpers/impl/ +DepthOfFieldBlur 6.0 DepthOfFieldBlur.qml +SceneEffect 6.0 SceneEffect.qml +LightmapperOutputWindow 6.0 LightmapperOutputWindow.qml + diff --git a/tests/unit/unittest/data/qml/QtQuick3D/Helpers/qmldir b/tests/unit/unittest/data/qml/QtQuick3D/Helpers/qmldir new file mode 100644 index 00000000000..f70948ab74e --- /dev/null +++ b/tests/unit/unittest/data/qml/QtQuick3D/Helpers/qmldir @@ -0,0 +1,21 @@ +module QtQuick3D.Helpers +linktarget Qt6::qtquick3dhelpersplugin +optional plugin qtquick3dhelpersplugin +classname QtQuick3DHelpersPlugin +designersupported +typeinfo plugins.qmltypes +depends QtQuick3D auto +prefer :/qt-project.org/imports/QtQuick3D/Helpers/ +AxisHelper 6.0 AxisHelper.qml +AxisHelper 1.0 AxisHelper.qml +DebugView 6.0 DebugView.qml +DebugView 1.0 DebugView.qml +WasdController 6.0 WasdController.qml +WasdController 1.0 WasdController.qml +OrbitCameraController 6.0 OrbitCameraController.qml +OrbitCameraController 1.0 OrbitCameraController.qml +LodManager 6.0 LodManager.qml +LodManager 1.0 LodManager.qml +ExtendedSceneEnvironment 6.0 ExtendedSceneEnvironment.qml +ExtendedSceneEnvironment 1.0 ExtendedSceneEnvironment.qml + diff --git a/tests/unit/unittest/data/qml/QtQuick3D/MaterialEditor/qmldir b/tests/unit/unittest/data/qml/QtQuick3D/MaterialEditor/qmldir new file mode 100644 index 00000000000..0286f76d757 --- /dev/null +++ b/tests/unit/unittest/data/qml/QtQuick3D/MaterialEditor/qmldir @@ -0,0 +1,13 @@ +module QtQuick3D.MaterialEditor +typeinfo plugins.qmltypes +prefer :/qt-project.org/imports/QtQuick3D/MaterialEditor/ +ShaderEditor 1.0 ShaderEditor.qml +EditorView 1.0 EditorView.qml +Preview 1.0 Preview.qml +PreviewControls 1.0 PreviewControls.qml +FrostedGlass 1.0 FrostedGlass.qml +AboutDialog 1.0 AboutDialog.qml +MaterialPropertiesPane 1.0 MaterialPropertiesPane.qml +SaveChangesDialog 1.0 SaveChangesDialog.qml +UniformManagerPane 1.0 UniformManagerPane.qml + diff --git a/tests/unit/unittest/data/qml/QtQuick3D/ParticleEffects/qmldir b/tests/unit/unittest/data/qml/QtQuick3D/ParticleEffects/qmldir new file mode 100644 index 00000000000..6abbe020127 --- /dev/null +++ b/tests/unit/unittest/data/qml/QtQuick3D/ParticleEffects/qmldir @@ -0,0 +1,10 @@ +module QtQuick3D.ParticleEffects +linktarget Qt6::qtquick3dparticleeffectsplugin +optional plugin qtquick3dparticleeffectsplugin +classname QtQuick3DParticleEffectsPlugin +designersupported +typeinfo Quick3DParticleEffects.qmltypes +depends QtQuick3D auto +depends QtQuick3DParticles3D auto +prefer :/qt-project.org/imports/QtQuick3D/ParticleEffects/ + diff --git a/tests/unit/unittest/data/qml/QtQuick3D/Particles3D/qmldir b/tests/unit/unittest/data/qml/QtQuick3D/Particles3D/qmldir new file mode 100644 index 00000000000..840899a28f3 --- /dev/null +++ b/tests/unit/unittest/data/qml/QtQuick3D/Particles3D/qmldir @@ -0,0 +1,9 @@ +module QtQuick3D.Particles3D +linktarget Qt6::qtquick3dparticles3dplugin +optional plugin qtquick3dparticles3dplugin +classname QtQuick3DParticles3DPlugin +designersupported +typeinfo plugins.qmltypes +depends QtQuick3D auto +prefer :/qt-project.org/imports/QtQuick3D/Particles3D/ + diff --git a/tests/unit/unittest/data/qml/QtQuick3D/Physics/Helpers/qmldir b/tests/unit/unittest/data/qml/QtQuick3D/Physics/Helpers/qmldir new file mode 100644 index 00000000000..01802761653 --- /dev/null +++ b/tests/unit/unittest/data/qml/QtQuick3D/Physics/Helpers/qmldir @@ -0,0 +1,8 @@ +module QtQuick3D.Physics.Helpers +linktarget Qt6::qtquick3dphysicshelpersplugin +optional plugin qtquick3dphysicshelpersplugin +classname QtQuick3DPhysicsHelpersPlugin +typeinfo plugins.qmltypes +depends QtQuick3DPhysics auto +prefer :/qt-project.org/imports/QtQuick3D/Physics/Helpers/ + diff --git a/tests/unit/unittest/data/qml/QtQuick3D/Physics/qmldir b/tests/unit/unittest/data/qml/QtQuick3D/Physics/qmldir new file mode 100644 index 00000000000..eb263336d85 --- /dev/null +++ b/tests/unit/unittest/data/qml/QtQuick3D/Physics/qmldir @@ -0,0 +1,8 @@ +module QtQuick3D.Physics +linktarget Qt6::qquick3dphysicsplugin +plugin qquick3dphysicsplugin +classname QtQuick3DPhysicsPlugin +designersupported +typeinfo plugins.qmltypes +prefer :/qt-project.org/imports/QtQuick3D/Physics/ + diff --git a/tests/unit/unittest/data/qml/QtQuick3D/SpatialAudio/qmldir b/tests/unit/unittest/data/qml/QtQuick3D/SpatialAudio/qmldir new file mode 100644 index 00000000000..e913a041384 --- /dev/null +++ b/tests/unit/unittest/data/qml/QtQuick3D/SpatialAudio/qmldir @@ -0,0 +1,10 @@ +module QtQuick3D.SpatialAudio +linktarget Qt6::quick3dspatialaudio +plugin quick3dspatialaudioplugin +classname QQuick3DAudioModule +typeinfo plugins.qmltypes +depends QtQuick +depends QtQuick3DPrivate +depends QtMultimedia +prefer :/qt-project.org/imports/QtQuick3D/SpatialAudio/ + diff --git a/tests/unit/unittest/data/qml/QtQuick3D/qmldir b/tests/unit/unittest/data/qml/QtQuick3D/qmldir new file mode 100644 index 00000000000..4de7691565a --- /dev/null +++ b/tests/unit/unittest/data/qml/QtQuick3D/qmldir @@ -0,0 +1,9 @@ +module QtQuick3D +linktarget Qt6::qquick3dplugin +plugin qquick3dplugin +classname QQuick3DPlugin +designersupported +typeinfo plugins.qmltypes +depends QtQuick auto +prefer :/qt-project.org/imports/QtQuick3D/ + diff --git a/tests/unit/unittest/data/qml/QtRemoteObjects/qmldir b/tests/unit/unittest/data/qml/QtRemoteObjects/qmldir new file mode 100644 index 00000000000..3f52d6267ec --- /dev/null +++ b/tests/unit/unittest/data/qml/QtRemoteObjects/qmldir @@ -0,0 +1,7 @@ +module QtRemoteObjects +linktarget Qt6::declarative_remoteobjects +optional plugin declarative_remoteobjectsplugin +classname QtRemoteObjectsPlugin +typeinfo plugins.qmltypes +prefer :/qt-project.org/imports/QtRemoteObjects/ + diff --git a/tests/unit/unittest/data/qml/QtScxml/qmldir b/tests/unit/unittest/data/qml/QtScxml/qmldir new file mode 100644 index 00000000000..d72487dcb16 --- /dev/null +++ b/tests/unit/unittest/data/qml/QtScxml/qmldir @@ -0,0 +1,8 @@ +module QtScxml +linktarget Qt6::declarative_scxml +optional plugin declarative_scxmlplugin +classname QScxmlStateMachinePlugin +typeinfo plugins.qmltypes +depends QtQml +prefer :/qt-project.org/imports/QtScxml/ + diff --git a/tests/unit/unittest/data/qml/QtSensors/qmldir b/tests/unit/unittest/data/qml/QtSensors/qmldir new file mode 100644 index 00000000000..228471522be --- /dev/null +++ b/tests/unit/unittest/data/qml/QtSensors/qmldir @@ -0,0 +1,8 @@ +module QtSensors +linktarget Qt6::SensorsQuickplugin +optional plugin sensorsquickplugin +classname QtSensorsPlugin +typeinfo plugins.qmltypes +depends QtQml +prefer :/qt-project.org/imports/QtSensors/ + diff --git a/tests/unit/unittest/data/qml/QtTest/qmldir b/tests/unit/unittest/data/qml/QtTest/qmldir new file mode 100644 index 00000000000..9ce4da24313 --- /dev/null +++ b/tests/unit/unittest/data/qml/QtTest/qmldir @@ -0,0 +1,14 @@ +module QtTest +linktarget Qt6::QuickTestplugin +optional plugin quicktestplugin +classname QtTestPlugin +typeinfo plugins.qmltypes +depends QtQuick.Window auto +prefer :/qt-project.org/imports/QtTest/ +SignalSpy 6.0 SignalSpy.qml +SignalSpy 1.0 SignalSpy.qml +TestCase 6.0 TestCase.qml +TestCase 1.0 TestCase.qml +singleton TestSchedule 6.0 TestSchedule.qml +singleton TestSchedule 1.0 TestSchedule.qml + diff --git a/tests/unit/unittest/data/qml/QtTextToSpeech/qmldir b/tests/unit/unittest/data/qml/QtTextToSpeech/qmldir new file mode 100644 index 00000000000..3ffacdb981b --- /dev/null +++ b/tests/unit/unittest/data/qml/QtTextToSpeech/qmldir @@ -0,0 +1,7 @@ +module QtTextToSpeech +linktarget Qt6::TextToSpeechQml +optional plugin texttospeechqmlplugin +classname QtTextToSpeechPlugin +typeinfo plugins.qmltypes +prefer :/qt-project.org/imports/QtTextToSpeech/ + diff --git a/tests/unit/unittest/data/qml/QtVncServer/qmldir b/tests/unit/unittest/data/qml/QtVncServer/qmldir new file mode 100644 index 00000000000..ccef9dfd6e1 --- /dev/null +++ b/tests/unit/unittest/data/qml/QtVncServer/qmldir @@ -0,0 +1,8 @@ +module QtVncServer +linktarget Qt6::qquickvncplugin +plugin qquickvncplugin +classname QQuickVncPlugin +typeinfo plugins.qmltypes +depends QtQuick +prefer :/qt-project.org/imports/QtVncServer/ + diff --git a/tests/unit/unittest/data/qml/QtWayland/Client/TextureSharing/qmldir b/tests/unit/unittest/data/qml/QtWayland/Client/TextureSharing/qmldir new file mode 100644 index 00000000000..c9517aa7db7 --- /dev/null +++ b/tests/unit/unittest/data/qml/QtWayland/Client/TextureSharing/qmldir @@ -0,0 +1,7 @@ +module QtWayland.Client.TextureSharing +linktarget Qt6::WaylandTextureSharing +plugin waylandtexturesharingplugin +classname QWaylandTextureSharingPlugin +typeinfo plugins.qmltypes +prefer :/qt-project.org/imports/QtWayland/Client/TextureSharing/ + diff --git a/tests/unit/unittest/data/qml/QtWayland/Compositor/IviApplication/qmldir b/tests/unit/unittest/data/qml/QtWayland/Compositor/IviApplication/qmldir new file mode 100644 index 00000000000..2a4e19ea91c --- /dev/null +++ b/tests/unit/unittest/data/qml/QtWayland/Compositor/IviApplication/qmldir @@ -0,0 +1,7 @@ +module QtWayland.Compositor.IviApplication +linktarget Qt6::WaylandCompositorIviapplication +plugin waylandcompositoriviapplicationplugin +classname QWaylandCompositorIviApplicationPlugin +typeinfo plugins.qmltypes +prefer :/qt-project.org/imports/QtWayland/Compositor/IviApplication/ + diff --git a/tests/unit/unittest/data/qml/QtWayland/Compositor/PresentationTime/qmldir b/tests/unit/unittest/data/qml/QtWayland/Compositor/PresentationTime/qmldir new file mode 100644 index 00000000000..40e25556307 --- /dev/null +++ b/tests/unit/unittest/data/qml/QtWayland/Compositor/PresentationTime/qmldir @@ -0,0 +1,7 @@ +module QtWayland.Compositor.PresentationTime +linktarget Qt6::WaylandCompositorPresentationTime +plugin waylandcompositorpresentationtimeplugin +classname QWaylandCompositorPresentationTimePlugin +typeinfo plugins.qmltypes +prefer :/qt-project.org/imports/QtWayland/Compositor/PresentationTime/ + diff --git a/tests/unit/unittest/data/qml/QtWayland/Compositor/QtShell/qmldir b/tests/unit/unittest/data/qml/QtWayland/Compositor/QtShell/qmldir new file mode 100644 index 00000000000..851feb14c97 --- /dev/null +++ b/tests/unit/unittest/data/qml/QtWayland/Compositor/QtShell/qmldir @@ -0,0 +1,7 @@ +module QtWayland.Compositor.QtShell +linktarget Qt6::WaylandCompositorQtShell +plugin waylandcompositorqtshellplugin +classname QWaylandQtShellPlugin +typeinfo plugins.qmltypes +prefer :/qt-project.org/imports/QtWayland/Compositor/QtShell/ + diff --git a/tests/unit/unittest/data/qml/QtWayland/Compositor/TextureSharingExtension/qmldir b/tests/unit/unittest/data/qml/QtWayland/Compositor/TextureSharingExtension/qmldir new file mode 100644 index 00000000000..fd11b6052c2 --- /dev/null +++ b/tests/unit/unittest/data/qml/QtWayland/Compositor/TextureSharingExtension/qmldir @@ -0,0 +1,7 @@ +module QtWayland.Compositor.TextureSharingExtension +linktarget Qt6::WaylandTextureSharingExtension +plugin waylandtexturesharingextensionplugin +classname QWaylandTextureSharingExtensionPlugin +typeinfo plugins.qmltypes +prefer :/qt-project.org/imports/QtWayland/Compositor/TextureSharingExtension/ + diff --git a/tests/unit/unittest/data/qml/QtWayland/Compositor/WlShell/qmldir b/tests/unit/unittest/data/qml/QtWayland/Compositor/WlShell/qmldir new file mode 100644 index 00000000000..19a44ce6b58 --- /dev/null +++ b/tests/unit/unittest/data/qml/QtWayland/Compositor/WlShell/qmldir @@ -0,0 +1,7 @@ +module QtWayland.Compositor.WlShell +linktarget Qt6::WaylandCompositorWLShell +plugin waylandcompositorwlshellplugin +classname QWaylandCompositorWlShellPlugin +typeinfo plugins.qmltypes +prefer :/qt-project.org/imports/QtWayland/Compositor/WlShell/ + diff --git a/tests/unit/unittest/data/qml/QtWayland/Compositor/XdgShell/qmldir b/tests/unit/unittest/data/qml/QtWayland/Compositor/XdgShell/qmldir new file mode 100644 index 00000000000..8e04c730e49 --- /dev/null +++ b/tests/unit/unittest/data/qml/QtWayland/Compositor/XdgShell/qmldir @@ -0,0 +1,7 @@ +module QtWayland.Compositor.XdgShell +linktarget Qt6::WaylandCompositorXdgShell +plugin waylandcompositorxdgshellplugin +classname QWaylandCompositorXdgShellPlugin +typeinfo plugins.qmltypes +prefer :/qt-project.org/imports/QtWayland/Compositor/XdgShell/ + diff --git a/tests/unit/unittest/data/qml/QtWayland/Compositor/qmldir b/tests/unit/unittest/data/qml/QtWayland/Compositor/qmldir new file mode 100644 index 00000000000..aba6cc89dce --- /dev/null +++ b/tests/unit/unittest/data/qml/QtWayland/Compositor/qmldir @@ -0,0 +1,12 @@ +module QtWayland.Compositor +linktarget Qt6::qwaylandcompositorplugin +optional plugin qwaylandcompositorplugin +classname QWaylandCompositorPlugin +typeinfo WaylandCompositor.qmltypes +depends QtQuick +prefer :/qt-project.org/imports/QtWayland/Compositor/ +WaylandCursorItem 6.0 qmlfiles/WaylandCursorItem.qml +WaylandCursorItem 1.0 qmlfiles/WaylandCursorItem.qml +WaylandOutputWindow 6.0 qmlfiles/WaylandOutputWindow.qml +WaylandOutputWindow 1.0 qmlfiles/WaylandOutputWindow.qml + diff --git a/tests/unit/unittest/data/qml/QtWebChannel/qmldir b/tests/unit/unittest/data/qml/QtWebChannel/qmldir new file mode 100644 index 00000000000..4fb6b7116d2 --- /dev/null +++ b/tests/unit/unittest/data/qml/QtWebChannel/qmldir @@ -0,0 +1,7 @@ +module QtWebChannel +linktarget Qt6::webchannel +plugin webchannelplugin +classname QWebChannelPlugin +typeinfo plugins.qmltypes +prefer :/qt-project.org/imports/QtWebChannel/ + diff --git a/tests/unit/unittest/data/qml/QtWebEngine/ControlsDelegates/qmldir b/tests/unit/unittest/data/qml/QtWebEngine/ControlsDelegates/qmldir new file mode 100644 index 00000000000..6c361034764 --- /dev/null +++ b/tests/unit/unittest/data/qml/QtWebEngine/ControlsDelegates/qmldir @@ -0,0 +1,36 @@ +module QtWebEngine.ControlsDelegates +linktarget Qt6::qtwebenginequickdelegatesplugin +optional plugin qtwebenginequickdelegatesplugin +classname QtWebEngine_ControlsDelegatesPlugin +typeinfo WebEngineQuickDelegatesQml.qmltypes +depends QtQuickControls2 +prefer :/qt-project.org/imports/QtWebEngine/ControlsDelegates/ +AlertDialog 6.0 AlertDialog.qml +AlertDialog 1.0 AlertDialog.qml +AuthenticationDialog 6.0 AuthenticationDialog.qml +AuthenticationDialog 1.0 AuthenticationDialog.qml +AutofillPopup 6.0 AutofillPopup.qml +AutofillPopup 1.0 AutofillPopup.qml +ColorDialog 6.0 ColorDialog.qml +ColorDialog 1.0 ColorDialog.qml +ConfirmDialog 6.0 ConfirmDialog.qml +ConfirmDialog 1.0 ConfirmDialog.qml +DirectoryPicker 6.0 DirectoryPicker.qml +DirectoryPicker 1.0 DirectoryPicker.qml +FilePicker 6.0 FilePicker.qml +FilePicker 1.0 FilePicker.qml +Menu 6.0 Menu.qml +Menu 1.0 Menu.qml +MenuItem 6.0 MenuItem.qml +MenuItem 1.0 MenuItem.qml +MenuSeparator 6.0 MenuSeparator.qml +MenuSeparator 1.0 MenuSeparator.qml +PromptDialog 6.0 PromptDialog.qml +PromptDialog 1.0 PromptDialog.qml +ToolTip 6.0 ToolTip.qml +ToolTip 1.0 ToolTip.qml +TouchHandle 6.0 TouchHandle.qml +TouchHandle 1.0 TouchHandle.qml +TouchSelectionMenu 6.0 TouchSelectionMenu.qml +TouchSelectionMenu 1.0 TouchSelectionMenu.qml + diff --git a/tests/unit/unittest/data/qml/QtWebEngine/qmldir b/tests/unit/unittest/data/qml/QtWebEngine/qmldir new file mode 100644 index 00000000000..0709c060c48 --- /dev/null +++ b/tests/unit/unittest/data/qml/QtWebEngine/qmldir @@ -0,0 +1,9 @@ +module QtWebEngine +linktarget Qt6::qtwebenginequickplugin +plugin qtwebenginequickplugin +classname QtWebEnginePlugin +typeinfo plugins.qmltypes +depends QtQuick auto +depends QtWebChannel auto +prefer :/qt-project.org/imports/QtWebEngine/ + diff --git a/tests/unit/unittest/data/qml/QtWebSockets/qmldir b/tests/unit/unittest/data/qml/QtWebSockets/qmldir new file mode 100644 index 00000000000..24224f087ca --- /dev/null +++ b/tests/unit/unittest/data/qml/QtWebSockets/qmldir @@ -0,0 +1,7 @@ +module QtWebSockets +linktarget Qt6::qmlwebsockets +plugin qmlwebsocketsplugin +classname QtWebSocketsDeclarativeModule +typeinfo plugins.qmltypes +prefer :/qt-project.org/imports/QtWebSockets/ + diff --git a/tests/unit/unittest/data/qml/QtWebView/qmldir b/tests/unit/unittest/data/qml/QtWebView/qmldir new file mode 100644 index 00000000000..3174233870a --- /dev/null +++ b/tests/unit/unittest/data/qml/QtWebView/qmldir @@ -0,0 +1,8 @@ +module QtWebView +linktarget Qt6::qtwebviewquickplugin +plugin qtwebviewquickplugin +classname QWebViewQuickPlugin +typeinfo plugins.qmltypes +depends QtWebEngine 2.0 +prefer :/qt-project.org/imports/QtWebView/ + diff --git a/tests/unit/unittest/externaldependenciesmock.h b/tests/unit/unittest/externaldependenciesmock.h new file mode 100644 index 00000000000..368024edc17 --- /dev/null +++ b/tests/unit/unittest/externaldependenciesmock.h @@ -0,0 +1,45 @@ +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include "googletest.h" + +#include + +#include + +class ExternalDependenciesMock : public QmlDesigner::ExternalDependenciesInterface +{ +public: + MOCK_METHOD(double, formEditorDevicePixelRatio, (), (const, override)); + MOCK_METHOD(QString, defaultPuppetFallbackDirectory, (), (const, override)); + MOCK_METHOD(QString, qmlPuppetFallbackDirectory, (), (const, override)); + MOCK_METHOD(QString, defaultPuppetToplevelBuildDirectory, (), (const, override)); + MOCK_METHOD(QUrl, projectUrl, (), (const, override)); + MOCK_METHOD(QString, currentProjectDirPath, (), (const, override)); + MOCK_METHOD(QList, designerSettingsEdit3DViewBackgroundColor, (), (const, override)); + MOCK_METHOD(QColor, designerSettingsEdit3DViewGridColor, (), (const, override)); + MOCK_METHOD(QUrl, currentResourcePath, (), (const, override)); + MOCK_METHOD(void, parseItemLibraryDescriptions, (), (override)); + MOCK_METHOD(const QmlDesigner::DesignerSettings &, designerSettings, (), (const, override)); + MOCK_METHOD(void, undoOnCurrentDesignDocument, (), (override)); + MOCK_METHOD(bool, + viewManagerUsesRewriterView, + (class QmlDesigner::RewriterView * view), + (const, override)); + MOCK_METHOD(void, viewManagerDiableWidgets, (), (override)); + MOCK_METHOD(QString, itemLibraryImportUserComponentsTitle, (), (const, override)); + MOCK_METHOD(bool, isQt6Import, (), (const, override)); + MOCK_METHOD(bool, hasStartupTarget, (), (const, override)); + MOCK_METHOD(QmlDesigner::PuppetStartData, + puppetStartData, + (const class QmlDesigner::Model &model), + (const, override)); + MOCK_METHOD(bool, instantQmlTextUpdate, (), (const, override)); + MOCK_METHOD(Utils::FilePath, qmlPuppetPath, (), (const, override)); + MOCK_METHOD(QStringList, modulePaths, (), (const, override)); + MOCK_METHOD(QStringList, projectModulePaths, (), (const, override)); + MOCK_METHOD(bool, isQt6Project, (), (const, override)); + MOCK_METHOD(QString, qtQuickVersion, (), (const, override)); +}; diff --git a/tests/unit/unittest/modulescanner-test.cpp b/tests/unit/unittest/modulescanner-test.cpp index 062256408e8..f3bbbf23e9e 100644 --- a/tests/unit/unittest/modulescanner-test.cpp +++ b/tests/unit/unittest/modulescanner-test.cpp @@ -1,6 +1,7 @@ // Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +#include "externaldependenciesmock.h" #include "googletest.h" #include @@ -9,6 +10,8 @@ namespace { +QLatin1String qmlModulesPath(TESTDATA_DIR "/qml"); + template auto UrlProperty(const Matcher &matcher) { @@ -21,6 +24,27 @@ auto VersionProperty(const Matcher &matcher) return Property(&QmlDesigner::Import::version, matcher); } +template +auto CorePropertiesHave(const Matcher &matcher) +{ + return AllOf(Contains(AllOf(UrlProperty("QtQuick"), matcher)), + Contains(AllOf(UrlProperty("QtQuick.Controls"), matcher)), + Contains(AllOf(UrlProperty("QtQuick3D"), matcher)), + Contains(AllOf(UrlProperty("QtQuick3D.Helpers"), matcher)), + Contains(AllOf(UrlProperty("QtQuick3D.Particles3D"), matcher))); +} + +template +auto NonCorePropertiesHave(const Matcher &matcher) +{ + return Not(Contains(AllOf(UrlProperty(AnyOf(Eq("QtQuick"), + Eq("QtQuick.Controls"), + Eq("QtQuick3D"), + Eq("QtQuick3D.Helpers"), + Eq("QtQuick3D.Particles3D"))), + matcher))); +} + MATCHER(HasDuplicates, std::string(negation ? "hasn't duplicates" : "has dublicates")) { auto values = arg; @@ -33,10 +57,12 @@ MATCHER(HasDuplicates, std::string(negation ? "hasn't duplicates" : "has dublica class ModuleScanner : public testing::Test { protected: + NiceMock externalDependenciesMock; QmlDesigner::ModuleScanner scanner{[](QStringView moduleName) { return moduleName.endsWith(u"impl"); }, - QmlDesigner::VersionScanning::No}; + QmlDesigner::VersionScanning::No, + externalDependenciesMock}; }; TEST_F(ModuleScanner, ReturnEmptyOptionalForWrongPath) @@ -48,21 +74,21 @@ TEST_F(ModuleScanner, ReturnEmptyOptionalForWrongPath) TEST_F(ModuleScanner, GetQtQuick) { - scanner.scan(QStringList{QT6_INSTALL_PREFIX}); + scanner.scan(QStringList{qmlModulesPath}); ASSERT_THAT(scanner.modules(), Contains(UrlProperty("QtQuick"))); } TEST_F(ModuleScanner, SkipEmptyModules) { - scanner.scan(QStringList{QT6_INSTALL_PREFIX}); + scanner.scan(QStringList{qmlModulesPath}); ASSERT_THAT(scanner.modules(), Not(Contains(UrlProperty(IsEmpty())))); } TEST_F(ModuleScanner, UseSkipFunction) { - scanner.scan(QStringList{QT6_INSTALL_PREFIX}); + scanner.scan(QStringList{qmlModulesPath}); ASSERT_THAT(scanner.modules(), Not(Contains(UrlProperty(EndsWith(QStringView{u"impl"}))))); } @@ -72,7 +98,8 @@ TEST_F(ModuleScanner, Version) QmlDesigner::ModuleScanner scanner{[](QStringView moduleName) { return moduleName.endsWith(u"impl"); }, - QmlDesigner::VersionScanning::Yes}; + QmlDesigner::VersionScanning::Yes, + externalDependenciesMock}; scanner.scan(QStringList{TESTDATA_DIR "/modulescanner"}); @@ -84,7 +111,8 @@ TEST_F(ModuleScanner, NoVersion) QmlDesigner::ModuleScanner scanner{[](QStringView moduleName) { return moduleName.endsWith(u"impl"); }, - QmlDesigner::VersionScanning::No}; + QmlDesigner::VersionScanning::No, + externalDependenciesMock}; scanner.scan(QStringList{TESTDATA_DIR "/modulescanner"}); @@ -94,18 +122,43 @@ TEST_F(ModuleScanner, NoVersion) TEST_F(ModuleScanner, Duplicates) { - scanner.scan(QStringList{QT6_INSTALL_PREFIX}); + scanner.scan(QStringList{qmlModulesPath}); ASSERT_THAT(scanner.modules(), Not(HasDuplicates())); } TEST_F(ModuleScanner, DontAddModulesAgain) { - scanner.scan(QStringList{QT6_INSTALL_PREFIX}); + scanner.scan(QStringList{qmlModulesPath}); - scanner.scan(QStringList{QT6_INSTALL_PREFIX}); + scanner.scan(QStringList{qmlModulesPath}); ASSERT_THAT(scanner.modules(), Not(HasDuplicates())); } +TEST_F(ModuleScanner, SetNoVersionForQtQuickVersion) +{ + scanner.scan(QStringList{qmlModulesPath}); + + ASSERT_THAT(scanner.modules(), CorePropertiesHave(VersionProperty(QString{}))); +} + +TEST_F(ModuleScanner, SetVersionForQtQuickVersion) +{ + ON_CALL(externalDependenciesMock, qtQuickVersion()).WillByDefault(Return(QString{"6.4"})); + + scanner.scan(QStringList{qmlModulesPath}); + + ASSERT_THAT(scanner.modules(), CorePropertiesHave(VersionProperty(u"6.4"))); +} + +TEST_F(ModuleScanner, DontSetVersionForNonQtQuickVersion) +{ + ON_CALL(externalDependenciesMock, qtQuickVersion()).WillByDefault(Return(QString{"6.4"})); + + scanner.scan(QStringList{qmlModulesPath}); + + ASSERT_THAT(scanner.modules(), NonCorePropertiesHave(VersionProperty(QString{}))); +} + } // namespace From 55687264fc36337017d6985b21029a48bb20bdbd Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Wed, 26 Apr 2023 14:51:00 +0200 Subject: [PATCH 095/192] QmlDesigner: Block more imports Change-Id: Ie6892d32594cc4f742ff48bebd82584e1ae2f82c Reviewed-by: Marco Bubke --- .../qmldesigner/studioplugin/studioplugin.metainfo | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/plugins/qmldesigner/studioplugin/studioplugin.metainfo b/src/plugins/qmldesigner/studioplugin/studioplugin.metainfo index 94f14c296dd..b24fdafc107 100644 --- a/src/plugins/qmldesigner/studioplugin/studioplugin.metainfo +++ b/src/plugins/qmldesigner/studioplugin/studioplugin.metainfo @@ -34,7 +34,13 @@ MetaInfo { "QtQuick.Templates", "QtQuick.Shapes", "QtQuick.Studio.EventSystem", - "QtQuick.Studio.EventSimulator" + "QtQuick.Studio.EventSimulator", + "QtQuick.Pdf", + "QmlTime", + "Qt.test.controls", + "QtOpcUa", + "QtVncServer", + "QtTextToSpeech" ] showTagsForImports: [ From 128ac85f79ee42c66cd622af2da4e9ba6dc676dc Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Wed, 26 Apr 2023 17:21:27 +0200 Subject: [PATCH 096/192] QmlDesigner: Fix unused warnings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I7791f6c6c9553a5a746b62b37e8846c5f479987d Reviewed-by: Henning Gründl --- .../qmldesigner/designercore/projectstorage/modulescanner.cpp | 2 +- .../qmldesigner/designercore/projectstorage/modulescanner.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/qmldesigner/designercore/projectstorage/modulescanner.cpp b/src/plugins/qmldesigner/designercore/projectstorage/modulescanner.cpp index 7a283389581..12cded6650d 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/modulescanner.cpp +++ b/src/plugins/qmldesigner/designercore/projectstorage/modulescanner.cpp @@ -36,7 +36,6 @@ QString createVersion(const QMultiHash &compo return {}; } -#endif constexpr auto coreModules = std::make_tuple(QStringView{u"QtQuick"}, QStringView{u"QtQuick.Controls"}, @@ -58,6 +57,7 @@ QString createCoreVersion(QStringView moduleName, ExternalDependenciesInterface return {}; } +#endif } // namespace void ModuleScanner::scan(const QStringList &modulePaths) diff --git a/src/plugins/qmldesigner/designercore/projectstorage/modulescanner.h b/src/plugins/qmldesigner/designercore/projectstorage/modulescanner.h index 64b672de8e3..f450192087f 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/modulescanner.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/modulescanner.h @@ -21,7 +21,7 @@ public: ModuleScanner([[maybe_unused]] SkipFunction skip, [[maybe_unused]] VersionScanning versionScanning, - ExternalDependenciesInterface &externalDependencies) + [[maybe_unused]] ExternalDependenciesInterface &externalDependencies) #ifdef QDS_HAS_QMLPRIVATE : m_skip{std::move(skip)} , m_versionScanning{versionScanning} From f9619ef381e450c84b9d697d9085065b43f9defe Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Wed, 26 Apr 2023 13:37:03 +0200 Subject: [PATCH 097/192] QmlDesigner: Place qmlpuppet configuration in system import path The system import path is called "/qt-project.org/imports". The fact that the qml tool gets this wrong doesn't mean that qmlpuppet has to get it wrong, too. Task-number: QDS-9342 Change-Id: If6e2c636b840d112e566ee4b67e916f97a8d52e5 Reviewed-by: Tim Jenssen --- src/tools/qml2puppet/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/qml2puppet/CMakeLists.txt b/src/tools/qml2puppet/CMakeLists.txt index 9f46e49ef66..98b1ebdf458 100644 --- a/src/tools/qml2puppet/CMakeLists.txt +++ b/src/tools/qml2puppet/CMakeLists.txt @@ -236,7 +236,7 @@ if (Qt6_VERSION VERSION_GREATER_EQUAL 6.4.0) qt_add_qml_module(qml2puppet URI QmlRuntime.QmlConfiguration VERSION 1.0 - RESOURCE_PREFIX "/qt-project.org" + RESOURCE_PREFIX "/qt-project.org/imports" ) if (QTC_STATIC_BUILD) qt_import_qml_plugins(qml2puppet PATH_TO_SCAN ${SRCDIR}) From 2c55b246c99ccb119f8129de1c83d0004736a7c8 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Wed, 26 Apr 2023 17:27:43 +0200 Subject: [PATCH 098/192] QmlDesigner: Increase Qt version condition We need Qt >= 6.4.3 for the module scanner. Change-Id: I496051e1ce6cbcb8100ede2e5026414799cc50ba Reviewed-by: Tim Jenssen Reviewed-by: Reviewed-by: Thomas Hartmann --- src/plugins/insight/CMakeLists.txt | 1 + src/plugins/qmldesigner/CMakeLists.txt | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/plugins/insight/CMakeLists.txt b/src/plugins/insight/CMakeLists.txt index e8a62940e7c..ec262638eef 100644 --- a/src/plugins/insight/CMakeLists.txt +++ b/src/plugins/insight/CMakeLists.txt @@ -1,4 +1,5 @@ add_qtc_plugin(Insight + CONDITION TARGET QtCreator::QmlDesigner PLUGIN_DEPENDS QtCreator::Core QtCreator::QtSupport QtCreator::QmlDesigner QtCreator::QmlProjectManager QtCreator::ProjectExplorer DEPENDS diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index a5d767e332a..89bf1ff4e45 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -37,6 +37,7 @@ extend_qtc_library(QmlDesignerUtils ) add_qtc_library(QmlDesignerCore STATIC + CONDITION Qt6_VERSION VERSION_GREATER_EQUAL 6.4.3 EXCLUDE_FROM_INSTALL DEPENDS Threads::Threads @@ -78,7 +79,7 @@ extend_qtc_library(QmlDesignerCore ) extend_qtc_library(QmlDesignerCore - CONDITION TARGET Qt6::QmlDomPrivate AND TARGET Qt6::QmlCompilerPrivate AND Qt6_VERSION VERSION_GREATER_EQUAL 6.4.3 + CONDITION TARGET Qt6::QmlDomPrivate AND TARGET Qt6::QmlCompilerPrivate AND Qt6_VERSION VERSION_LESS 6.6.0 DEPENDS Qt6::QmlDomPrivate Qt6::QmlCompilerPrivate PUBLIC_DEFINES QDS_HAS_QMLPRIVATE @@ -423,7 +424,7 @@ set_property(SOURCE ${PROJECTSTORAGE_EXCLUDED_SOURCES} PROPERTY SKIP_AUTOMOC ON) add_qtc_plugin(QmlDesigner PLUGIN_RECOMMENDS QmlPreview - CONDITION Qt6_VERSION VERSION_GREATER_EQUAL 6.2.0 AND TARGET Qt::QuickWidgets AND TARGET Qt::Svg + CONDITION Qt6_VERSION VERSION_GREATER_EQUAL 6.4.3 AND TARGET QmlDesignerCore AND TARGET Qt::QuickWidgets AND TARGET Qt::Svg PLUGIN_DEPENDS Core ProjectExplorer QmlDesignerBase QmlJSEditor QmakeProjectManager QmlProjectManager QtSupport From 3f32409748f6e52eb2268851b578edac31eaf82a Mon Sep 17 00:00:00 2001 From: Knud Dollereder Date: Fri, 21 Apr 2023 15:30:28 +0200 Subject: [PATCH 099/192] QmlDesigner: Cleanup mcu deploy step Change-Id: I4f4fa4f9067613cb31c7408a89a25cf27f2093e3 Reviewed-by: Aleksei German --- .../qmlprojectmanager/mcubuildstep.cpp | 165 ++++++++---------- src/plugins/qmlprojectmanager/mcubuildstep.h | 17 +- 2 files changed, 83 insertions(+), 99 deletions(-) diff --git a/src/plugins/qmlprojectmanager/mcubuildstep.cpp b/src/plugins/qmlprojectmanager/mcubuildstep.cpp index 7ee5bc83939..031ed770354 100644 --- a/src/plugins/qmlprojectmanager/mcubuildstep.cpp +++ b/src/plugins/qmlprojectmanager/mcubuildstep.cpp @@ -2,36 +2,37 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0 #include "mcubuildstep.h" +#include "qmlprojectmanagertr.h" -#include "projectexplorer/buildstep.h" -#include "projectexplorer/buildsystem.h" -#include "projectexplorer/buildsteplist.h" -#include "projectexplorer/deployconfiguration.h" -#include "projectexplorer/kit.h" -#include "projectexplorer/target.h" -#include "projectexplorer/kitmanager.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include -#include #include -#include "qtsupport/qtsupportconstants.h" -#include "mcusupport/mcusupportconstants.h" -#include "mcusupport/mculegacyconstants.h" +#include +#include +#include -#include "utils/aspects.h" -#include "utils/filepath.h" +#include +#include #include namespace QmlProjectManager { const Utils::Id DeployMcuProcessStep::id = "QmlProject.Mcu.DeployStep"; -const QString DeployMcuProcessStep::processCommandKey = "QmlProject.Mcu.ProcessStep.Command"; -const QString DeployMcuProcessStep::processArgumentsKey = "QmlProject.Mcu.ProcessStep.Arguments"; -const QString DeployMcuProcessStep::processWorkingDirectoryKey = "QmlProject.Mcu.ProcessStep.BuildDirectory"; -void DeployMcuProcessStep::showError(const QString& text) { - Core::AsynchronousMessageBox::critical(tr("Qt4MCU Deploy Step"), text); +void DeployMcuProcessStep::showError(const QString &text) +{ + ProjectExplorer::DeploymentTask task(ProjectExplorer::Task::Error, text); + ProjectExplorer::TaskHub::addTask(task); } // TODO: @@ -47,86 +48,73 @@ DeployMcuProcessStep::DeployMcuProcessStep(ProjectExplorer::BuildStepList *bc, U : AbstractProcessStep(bc, id) , m_tmpDir() { - if (not buildSystem()) { - showError(QObject::tr("Failed to find valid build system")); + if (!buildSystem()) { + showError(Tr::tr("Failed to find valid build system")); return; } - if (not m_tmpDir.isValid()) { - showError(QObject::tr("Failed to create valid build directory")); + if (!m_tmpDir.isValid()) { + showError(Tr::tr("Failed to create valid build directory")); return; } - auto fixPath = [](const QString& path) -> QString { - return "\"" + QDir::toNativeSeparators(path) + "\""; - }; - - ProjectExplorer::Kit* kit = MCUBuildStepFactory::findMostRecentQulKit(); - if (not kit) + ProjectExplorer::Kit *kit = MCUBuildStepFactory::findMostRecentQulKit(); + if (!kit) return; QString root = findKitInformation(kit, McuSupport::Internal::Legacy::Constants::QUL_CMAKE_VAR); + auto rootPath = Utils::FilePath::fromString(root); - auto* cmd = addAspect(); - cmd->setSettingsKey(processCommandKey); + auto cmd = addAspect(); + cmd->setSettingsKey("QmlProject.Mcu.ProcessStep.Command"); cmd->setDisplayStyle(Utils::StringAspect::PathChooserDisplay); cmd->setExpectedKind(Utils::PathChooser::Command); - cmd->setLabelText(tr("Command:")); - cmd->setValue(QDir::toNativeSeparators(root + "/bin/qmlprojectexporter")); + cmd->setLabelText(Tr::tr("Command:")); + cmd->setFilePath(rootPath.pathAppended("/bin/qmlprojectexporter")); - const char* importPathConstant = QtSupport::Constants::KIT_QML_IMPORT_PATH; - QString projectDir = buildSystem()->projectDirectory().toString(); - QString qulIncludeDir = kit->value(importPathConstant).toString( ); + const char *importPathConstant = QtSupport::Constants::KIT_QML_IMPORT_PATH; + Utils::FilePath projectDir = buildSystem()->projectDirectory(); + Utils::FilePath qulIncludeDir = Utils::FilePath::fromVariant(kit->value(importPathConstant)); QStringList includeDirs { - fixPath(qulIncludeDir), - fixPath(qulIncludeDir + "/Timeline"), - fixPath(projectDir + "/imports") + Utils::ProcessArgs::quoteArg(qulIncludeDir.toString()), + Utils::ProcessArgs::quoteArg(qulIncludeDir.pathAppended("Timeline").toString()) }; - const char* toolChainConstant = McuSupport::Internal::Constants::KIT_MCUTARGET_TOOLCHAIN_KEY; + const char *toolChainConstant = McuSupport::Internal::Constants::KIT_MCUTARGET_TOOLCHAIN_KEY; QStringList arguments = { - fixPath(buildSystem()->projectFilePath().toString()), + Utils::ProcessArgs::quoteArg(buildSystem()->projectFilePath().toString()), "--platform", findKitInformation(kit, "QUL_PLATFORM"), - "--toolchain", kit->value(toolChainConstant).toString( ), + "--toolchain", kit->value(toolChainConstant).toString(), "--include-dirs", includeDirs.join(","), }; - auto* args = addAspect(); - args->setSettingsKey(processArgumentsKey); + auto args = addAspect(); + args->setSettingsKey("QmlProject.Mcu.ProcessStep.Arguments"); args->setDisplayStyle(Utils::StringAspect::LineEditDisplay); - args->setLabelText(tr("Arguments:")); - args->setValue(arguments.join(" ")); + args->setLabelText(Tr::tr("Arguments:")); + args->setValue(Utils::ProcessArgs::joinArgs(arguments)); - auto* outDir = addAspect(); - outDir->setSettingsKey(processWorkingDirectoryKey); + auto outDir = addAspect(); + outDir->setSettingsKey("QmlProject.Mcu.ProcessStep.BuildDirectory"); outDir->setDisplayStyle(Utils::StringAspect::PathChooserDisplay); outDir->setExpectedKind(Utils::PathChooser::Directory); - outDir->setLabelText(tr("Build directory:")); - outDir->setPlaceHolderText(fixPath(m_tmpDir.path())); + outDir->setLabelText(Tr::tr("Build directory:")); + outDir->setPlaceHolderText(m_tmpDir.path()); - setCommandLineProvider([this, cmd, args, outDir, fixPath]() -> Utils::CommandLine { + setCommandLineProvider([this, cmd, args, outDir]() -> Utils::CommandLine { auto directory = outDir->value(); if (directory.isEmpty()) - directory = fixPath(m_tmpDir.path()); + directory = m_tmpDir.path(); - QString outArg = " --outdir " + directory; - return {cmd->filePath(), args->value() + outArg, Utils::CommandLine::Raw}; + Utils::CommandLine cmdLine(cmd->filePath()); + cmdLine.addArgs(args->value(), Utils::CommandLine::Raw); + cmdLine.addArg("--outdir"); + cmdLine.addArg(directory); + return cmdLine; }); } -bool DeployMcuProcessStep::init() -{ - if (!AbstractProcessStep::init()) - return false; - return true; -} - -void DeployMcuProcessStep::doRun() -{ - AbstractProcessStep::doRun(); -} - -QString DeployMcuProcessStep::findKitInformation(ProjectExplorer::Kit* kit, const QString& key) +QString DeployMcuProcessStep::findKitInformation(ProjectExplorer::Kit *kit, const QString &key) { // This is (kind of) stolen from mcukitmanager.cpp. Might make sense to unify. using namespace CMakeProjectManager; @@ -139,59 +127,60 @@ QString DeployMcuProcessStep::findKitInformation(ProjectExplorer::Kit* kit, cons return {}; } - MCUBuildStepFactory::MCUBuildStepFactory() : BuildStepFactory() { - setDisplayName("Qt4MCU Deploy Step"); - registerStep< DeployMcuProcessStep >(DeployMcuProcessStep::id); + setDisplayName(Tr::tr("Qt4MCU Deploy Step")); + registerStep(DeployMcuProcessStep::id); } void MCUBuildStepFactory::attachToTarget(ProjectExplorer::Target *target) { - if (not target) + if (!target) return; - ProjectExplorer::DeployConfiguration* deployConfiguration = target->activeDeployConfiguration(); - ProjectExplorer::BuildStepList* stepList = deployConfiguration->stepList(); + ProjectExplorer::DeployConfiguration *deployConfiguration = target->activeDeployConfiguration(); + ProjectExplorer::BuildStepList *stepList = deployConfiguration->stepList(); if (stepList->contains(DeployMcuProcessStep::id)) return; - if (not findMostRecentQulKit()) { - DeployMcuProcessStep::showError(QObject::tr("Failed to find valid Qt4MCU kit")); + if (!findMostRecentQulKit()) { + DeployMcuProcessStep::showError(Tr::tr("Failed to find valid Qt4MCU kit")); return; } for (BuildStepFactory *factory : BuildStepFactory::allBuildStepFactories()) { if (factory->stepId() == DeployMcuProcessStep::id) { - ProjectExplorer::BuildStep* deployConfig = factory->create(stepList); - stepList->appendStep(deployConfig); + ProjectExplorer::BuildStep *deployConfig = factory->create(stepList); + stepList->appendStep(deployConfig); } } } -ProjectExplorer::Kit* MCUBuildStepFactory::findMostRecentQulKit() +ProjectExplorer::Kit *MCUBuildStepFactory::findMostRecentQulKit() { // Stolen from mcukitmanager.cpp auto kitQulVersion = [](const ProjectExplorer::Kit *kit) -> QVersionNumber { - const char* sdkVersion = McuSupport::Internal::Constants::KIT_MCUTARGET_SDKVERSION_KEY; + const char *sdkVersion = McuSupport::Internal::Constants::KIT_MCUTARGET_SDKVERSION_KEY; return QVersionNumber::fromString(kit->value(sdkVersion).toString()); }; - ProjectExplorer::Kit* kit = nullptr; - for (auto k : ProjectExplorer::KitManager::kits()) - { - auto qulVersion = kitQulVersion(k); - if (qulVersion.isNull( )) + ProjectExplorer::Kit *mcuKit = nullptr; + for (auto availableKit : ProjectExplorer::KitManager::kits()) { + if (!availableKit) continue; - if (not kit) - kit = k; + auto qulVersion = kitQulVersion(availableKit); + if (qulVersion.isNull()) + continue; - if (qulVersion > kitQulVersion(kit)) - kit = k; + if (!mcuKit) + mcuKit = availableKit; + + if (qulVersion > kitQulVersion(mcuKit)) + mcuKit = availableKit; } - return kit; + return mcuKit; } } // namespace QmlProjectManager diff --git a/src/plugins/qmlprojectmanager/mcubuildstep.h b/src/plugins/qmlprojectmanager/mcubuildstep.h index e1167e1917a..988284a8905 100644 --- a/src/plugins/qmlprojectmanager/mcubuildstep.h +++ b/src/plugins/qmlprojectmanager/mcubuildstep.h @@ -2,10 +2,11 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0 #pragma once -#include "projectexplorer/kit.h" -#include "projectexplorer/project.h" #include #include +#include +#include + #include #include @@ -16,18 +17,12 @@ class DeployMcuProcessStep : public ProjectExplorer::AbstractProcessStep { public: static const Utils::Id id; - static void showError(const QString& text); + static void showError(const QString &text); DeployMcuProcessStep(ProjectExplorer::BuildStepList *bc, Utils::Id id); private: - bool init() override; - void doRun() override; - QString findKitInformation(ProjectExplorer::Kit* kit, const QString& key); - - static const QString processCommandKey; - static const QString processArgumentsKey; - static const QString processWorkingDirectoryKey; + QString findKitInformation(ProjectExplorer::Kit *kit, const QString &key); QTemporaryDir m_tmpDir; }; @@ -36,7 +31,7 @@ class MCUBuildStepFactory : public ProjectExplorer::BuildStepFactory public: MCUBuildStepFactory(); static void attachToTarget(ProjectExplorer::Target *target); - static ProjectExplorer::Kit* findMostRecentQulKit( ); + static ProjectExplorer::Kit *findMostRecentQulKit(); }; } // namespace QmlProjectManager From 932ae0411c840f6d1bc0825c3d1f6b59d9906757 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Thu, 27 Apr 2023 15:27:48 +0200 Subject: [PATCH 100/192] QmlDesigner: Block more imports Block QtQuick3D.MaterialEditor, QtDataVisualization and QtQuick3D.ParticleEffects Task-number: QDS-9799 Change-Id: I68e2f7a467f4e6c7e6ba1ebfedf7880425b2fdda Reviewed-by: Thomas Hartmann --- src/plugins/qmldesigner/studioplugin/studioplugin.metainfo | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/plugins/qmldesigner/studioplugin/studioplugin.metainfo b/src/plugins/qmldesigner/studioplugin/studioplugin.metainfo index b24fdafc107..2998ea2f67b 100644 --- a/src/plugins/qmldesigner/studioplugin/studioplugin.metainfo +++ b/src/plugins/qmldesigner/studioplugin/studioplugin.metainfo @@ -40,7 +40,10 @@ MetaInfo { "Qt.test.controls", "QtOpcUa", "QtVncServer", - "QtTextToSpeech" + "QtTextToSpeech", + "QtQuick3D MaterialEditor", + "QtDataVisualization", + "QtQuick3D.ParticleEffects" ] showTagsForImports: [ From 3be61f0be9b2093ce834df7eb337cd0d16df4a60 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Thu, 27 Apr 2023 15:30:07 +0200 Subject: [PATCH 101/192] QmlDesigner: Activate kit after adding We do not remove other targets anymore and instead activate the added one. Task-number: QDS-9652 Change-Id: I29d5b9e878b928611a1e854fd212682153b89d63 Reviewed-by: Burak Hancerli Reviewed-by: Tim Jenssen --- src/plugins/qmlprojectmanager/qmlproject.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/plugins/qmlprojectmanager/qmlproject.cpp b/src/plugins/qmlprojectmanager/qmlproject.cpp index 6743c136049..4e919005c08 100644 --- a/src/plugins/qmlprojectmanager/qmlproject.cpp +++ b/src/plugins/qmlprojectmanager/qmlproject.cpp @@ -120,8 +120,6 @@ Project::RestoreResult QmlProject::fromMap(const QVariantMap &map, QString *erro if (QmlProject::isQtDesignStudio()) { int preferedVersion = preferedQtTarget(activeTarget()); - // if (activeTarget()) - // removeTarget(activeTarget()); setKitWithVersion(preferedVersion, kits); } @@ -141,13 +139,19 @@ bool QmlProject::setKitWithVersion(const int qtMajorVersion, const QList return (version && version->qtVersion().majorVersion() == qtMajorVersion); }); + + Target *target = nullptr; + if (!qtVersionkits.isEmpty()) { if (qtVersionkits.contains(KitManager::defaultKit())) - addTargetForDefaultKit(); + target = addTargetForDefaultKit(); else - addTargetForKit(qtVersionkits.first()); + target = addTargetForKit(qtVersionkits.first()); } + if (target) + SessionManager::setActiveTarget(this, target, SetActive::NoCascade); + return true; } From 351e11a73d39ac87a6d71f5ab5fd9466d33157e2 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Thu, 27 Apr 2023 14:09:09 +0200 Subject: [PATCH 102/192] QmlProject: Fix mainUiFilePath() The method mainUiFilePath() is supposed to return the actual file path that is relative to the project file. Removing mainUiFilePath() from QmlProjectItem, since QmlProjectItem is supposed to contain only the pure data. Task-number: QDS-9650 Change-Id: Iad474586cad8f8cef745aadb63d421573d7d9c83 Reviewed-by: Thomas Hartmann --- .../buildsystem/projectitem/qmlprojectitem.cpp | 10 ---------- .../buildsystem/projectitem/qmlprojectitem.h | 2 -- .../qmlprojectmanager/buildsystem/qmlbuildsystem.cpp | 7 ++++++- .../qmlprojectmanager/buildsystem/qmlbuildsystem.h | 1 + 4 files changed, 7 insertions(+), 13 deletions(-) diff --git a/src/plugins/qmlprojectmanager/buildsystem/projectitem/qmlprojectitem.cpp b/src/plugins/qmlprojectmanager/buildsystem/projectitem/qmlprojectitem.cpp index c216ee320ba..04bd26476e2 100644 --- a/src/plugins/qmlprojectmanager/buildsystem/projectitem/qmlprojectitem.cpp +++ b/src/plugins/qmlprojectmanager/buildsystem/projectitem/qmlprojectitem.cpp @@ -317,11 +317,6 @@ QString QmlProjectItem::mainFile() const return m_project["runConfig"].toObject()["mainFile"].toString(); } -Utils::FilePath QmlProjectItem::mainFilePath() const -{ - return m_projectFile; -} - void QmlProjectItem::setMainUiFile(const QString &mainUiFile) { QJsonObject runConfig = m_project["runConfig"].toObject(); @@ -334,11 +329,6 @@ QString QmlProjectItem::mainUiFile() const return m_project["runConfig"].toObject()["mainUiFile"].toString(); } -Utils::FilePath QmlProjectItem::mainUiFilePath() const -{ - return Utils::FilePath::fromString(m_project["runConfig"].toObject()["mainUiFile"].toString()); -} - bool QmlProjectItem::widgetApp() const { return m_project["runConfig"].toObject()["widgetApp"].toBool(); diff --git a/src/plugins/qmlprojectmanager/buildsystem/projectitem/qmlprojectitem.h b/src/plugins/qmlprojectmanager/buildsystem/projectitem/qmlprojectitem.h index 30e85d53dcd..78b038b0378 100644 --- a/src/plugins/qmlprojectmanager/buildsystem/projectitem/qmlprojectitem.h +++ b/src/plugins/qmlprojectmanager/buildsystem/projectitem/qmlprojectitem.h @@ -69,11 +69,9 @@ public: void setMainFile(const QString &mainFile); QString mainFile() const; - Utils::FilePath mainFilePath() const; void setMainUiFile(const QString &mainUiFile); QString mainUiFile() const; - Utils::FilePath mainUiFilePath() const; bool widgetApp() const; void setWidgetApp(const bool &widgetApp); diff --git a/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.cpp b/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.cpp index e06ac211dfb..712aef92277 100644 --- a/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.cpp +++ b/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.cpp @@ -291,7 +291,7 @@ Utils::FilePath QmlBuildSystem::mainFilePath() const Utils::FilePath QmlBuildSystem::mainUiFilePath() const { - return m_projectItem->mainUiFilePath(); + return projectDirectory().pathAppended(mainUiFile()); } bool QmlBuildSystem::setMainFileInProjectFile(const Utils::FilePath &newMainFilePath) @@ -477,6 +477,11 @@ QString QmlBuildSystem::mainFile() const return m_projectItem->mainFile(); } +QString QmlBuildSystem::mainUiFile() const +{ + return m_projectItem->mainUiFile(); +} + bool QmlBuildSystem::qtForMCUs() const { return m_projectItem->isQt4McuProject(); diff --git a/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.h b/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.h index 914b8d4c0fe..d275481f537 100644 --- a/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.h +++ b/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.h @@ -57,6 +57,7 @@ public: Utils::FilePath canonicalProjectDir() const; QString mainFile() const; + QString mainUiFile() const; Utils::FilePath mainFilePath() const; Utils::FilePath mainUiFilePath() const; From 6eccd825151182455dbe0def70473da67e1a41b8 Mon Sep 17 00:00:00 2001 From: Henning Gruendl Date: Wed, 26 Apr 2023 17:07:32 +0200 Subject: [PATCH 103/192] QmlDesigner: Fix SpinBox value reset on focus loss Task-number: QDS-9770 Change-Id: I8a4d73e7abe8dbb5c62c571fc9980a0f78a5f819 Reviewed-by: Miikka Heikkinen Reviewed-by: --- .../imports/StudioControls/RealSpinBox.qml | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/RealSpinBox.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/RealSpinBox.qml index aba99f75ffa..2f219a411da 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/RealSpinBox.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/RealSpinBox.qml @@ -121,7 +121,7 @@ T.SpinBox { height: control.spinBoxIndicatorVisible ? control.__spinBoxIndicatorHeight : 0 realEnabled: (control.realFrom < control.realTo) ? (control.realValue < control.realTo) - : (control.realValue > control.realTo) + : (control.realValue > control.realTo) } down.indicator: RealSpinBoxIndicator { @@ -138,7 +138,7 @@ T.SpinBox { height: control.spinBoxIndicatorVisible ? control.__spinBoxIndicatorHeight : 0 realEnabled: (control.realFrom < control.realTo) ? (control.realValue > control.realFrom) - : (control.realValue < control.realFrom) + : (control.realValue < control.realFrom) } contentItem: RealSpinBoxInput { @@ -284,26 +284,26 @@ T.SpinBox { control.setRealValue(control.realValue) // sanitize and clamp realValue spinBoxInput.text = control.textFromValue(control.realValue, control.locale) control.value = 0 // Without setting value back to 0, it can happen that one of - // the indicator will be disabled due to range logic. + // the indicator will be disabled due to range logic. } onRealValueModified: myTimer.restart() onFocusChanged: { - if (control.focus) { + if (control.focus) control.dirty = false - } else { - // Make sure displayed value is correct after focus loss, as onEditingFinished - // doesn't trigger when value is something validator doesn't accept. - spinBoxInput.text = control.textFromValue(control.realValue, control.locale) - - if (control.dirty) - spinBoxInput.handleEditingFinished() - } } onDisplayTextChanged: spinBoxInput.text = control.displayText onActiveFocusChanged: { if (control.activeFocus) { // QTBUG-75862 && mySpinBox.focusReason === Qt.TabFocusReason) control.preFocusText = spinBoxInput.text spinBoxInput.selectAll() + } else { + // Make sure displayed value is correct after focus loss, as onEditingFinished + // doesn't trigger when value is something validator doesn't accept. + if (spinBoxInput.text === "") + spinBoxInput.text = control.textFromValue(control.realValue, control.locale) + + if (control.dirty) + spinBoxInput.handleEditingFinished() } } @@ -373,8 +373,8 @@ T.SpinBox { value = Math.round(value) control.realValue = control.clamp(value, - control.validator.bottom, - control.validator.top) + control.validator.bottom, + control.validator.top) } function realDecrease() { From 3b8c38c7f0d68887a820d2aba3576c1adbfedec2 Mon Sep 17 00:00:00 2001 From: Henning Gruendl Date: Tue, 25 Apr 2023 15:43:24 +0200 Subject: [PATCH 104/192] QmlDesigner: Add support for valueRole in ComboBox - Add support for valueRole in HelperWidget.ComboBox in order to allow for custom JS arrays that define value and text role - Replace specialized invokables in item filter model which are only available in that particular model with general purpose functionality provided by QML ComboBox - Remove unnecessary functions from item filter model Task-number: QDS-8737 Change-Id: I58bd3fed395961ac4495a0c6e9ff47c9b87e0de2 Reviewed-by: Ali Kianian Reviewed-by: Reviewed-by: Mahmoud Badri --- .../imports/HelperWidgets/ComboBox.qml | 66 +++++++++++-------- .../HelperWidgets/EditableListView.qml | 2 +- .../HelperWidgets/ListViewComboBox.qml | 12 +--- .../propertyeditor/itemfiltermodel.cpp | 45 +++++-------- .../propertyeditor/itemfiltermodel.h | 12 ++-- 5 files changed, 64 insertions(+), 73 deletions(-) diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ComboBox.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ComboBox.qml index 1820af7958b..813aa1c7594 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ComboBox.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ComboBox.qml @@ -10,8 +10,8 @@ StudioControls.ComboBox { property variant backendValue - labelColor: edit && !colorLogic.errorState ? StudioTheme.Values.themeTextColor - : colorLogic.textColor + labelColor: comboBox.edit && !colorLogic.errorState ? StudioTheme.Values.themeTextColor + : colorLogic.textColor property string scope: "Qt" enum ValueType { String, Integer, Enum } @@ -74,7 +74,6 @@ StudioControls.ComboBox { comboBox.backendValue.commitDrop(dropArea.dropData) comboBox.hasActiveHoverDrag = false } - } ExtendedFunctionLogic { @@ -102,6 +101,16 @@ StudioControls.ComboBox { if (comboBox.manualMapping) { comboBox.valueFromBackendChanged() + } else if (comboBox.valueRole && comboBox.textRole !== comboBox.valueRole) { + switch (comboBox.valueType) { + case ComboBox.ValueType.Enum: + comboBox.currentIndex = comboBox.indexOfValue(comboBox.backendValue.enumeration) + break + case ComboBox.ValueType.String: + case ComboBox.ValueType.Integer: + default: + comboBox.currentIndex = comboBox.indexOfValue(comboBox.backendValue.value) + } } else { switch (comboBox.valueType) { case ComboBox.ValueType.String: @@ -147,14 +156,10 @@ StudioControls.ComboBox { return let inputText = comboBox.editText - let inputValue = inputText; + let inputValue = inputText let index = comboBox.find(inputText) - if (index !== -1) { - let modelIdx = comboBox.model.index(index) - inputValue = comboBox.valueRole - ? comboBox.model.data(modelIdx, comboBox.valueRole) - : comboBox.textAt(index) - } + if (index !== -1) + inputValue = comboBox.valueRole ? comboBox.valueAt(index) : comboBox.textAt(index) comboBox.backendValue.value = inputValue @@ -175,24 +180,31 @@ StudioControls.ComboBox { let inputText = comboBox.currentText let inputValue = comboBox.currentValue let index = comboBox.find(inputText) - if (index !== -1) { - let modelIdx = comboBox.model.index(index) - inputValue = comboBox.model.data(modelIdx, comboBox.valueRole) - } - comboBox.backendValue.value = inputValue - return - } - switch (comboBox.valueType) { - case ComboBox.ValueType.String: - comboBox.backendValue.value = comboBox.currentText - break - case ComboBox.ValueType.Integer: - comboBox.backendValue.value = comboBox.currentIndex - break - case ComboBox.ValueType.Enum: - default: - comboBox.backendValue.setEnumeration(comboBox.scope, comboBox.currentText) + if (index !== -1) + inputValue = comboBox.valueAt(index) + + switch (comboBox.valueType) { + case ComboBox.ValueType.Enum: + comboBox.backendValue.setEnumeration(comboBox.scope, inputValue) + break + case ComboBox.ValueType.String: + case ComboBox.ValueType.Integer: + default: + comboBox.backendValue.value = inputValue + } + } else { + switch (comboBox.valueType) { + case ComboBox.ValueType.String: + comboBox.backendValue.value = comboBox.currentText + break + case ComboBox.ValueType.Integer: + comboBox.backendValue.value = comboBox.currentIndex + break + case ComboBox.ValueType.Enum: + default: + comboBox.backendValue.setEnumeration(comboBox.scope, comboBox.currentText) + } } } diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/EditableListView.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/EditableListView.qml index 7b0ed94191a..1536afdb06e 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/EditableListView.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/EditableListView.qml @@ -96,7 +96,7 @@ Item { tooltip: root.extraButtonToolTip onClicked: root.extraButtonClicked(index) visible: root.extraButtonIcon !== "" - enabled: root.model[index] + enabled: root.model[index] ?? false } IconIndicator { diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ListViewComboBox.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ListViewComboBox.qml index 0512b0861be..4be51733016 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ListViewComboBox.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ListViewComboBox.qml @@ -42,9 +42,7 @@ StudioControls.ComboBox { currentSelectedDataIndex = root.find(root.initialModelData) } else { for (let i = 0; i < root.count; ++i) { - let movingModelIndex = root.model.index(i) - let movingModelValueData = root.model.data(movingModelIndex, root.valueRole) - if (movingModelValueData === root.initialModelData) { + if (root.valueAt(i) === root.initialModelData) { currentSelectedDataIndex = i break } @@ -55,14 +53,6 @@ StudioControls.ComboBox { root.editText = root.initialModelData } - function currentData(role = root.valueRole) { - if (root.currentIndex !== -1) { - let currentModelIndex = root.model.index(root.currentIndex) - return root.model.data(currentModelIndex, role) - } - return root.editText - } - function availableValue() { if (root.currentIndex !== -1 && root.currentValue !== "") return root.currentValue diff --git a/src/plugins/qmldesigner/components/propertyeditor/itemfiltermodel.cpp b/src/plugins/qmldesigner/components/propertyeditor/itemfiltermodel.cpp index d52e47c3678..19a697e7024 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/itemfiltermodel.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/itemfiltermodel.cpp @@ -7,7 +7,7 @@ #include #include #include -#include "variantproperty.h" +#include #include #include @@ -24,7 +24,7 @@ ItemFilterModel::ItemFilterModel(QObject *parent) { if (m_roles.empty()) { m_roles = QAbstractListModel::roleNames(); - QMetaEnum roleEnum = QMetaEnum::fromType(); + const QMetaEnum roleEnum = QMetaEnum::fromType(); for (int i = 0; i < roleEnum.keyCount(); i++) m_roles.insert(roleEnum.value(i), roleEnum.key(i)); } @@ -32,7 +32,6 @@ ItemFilterModel::ItemFilterModel(QObject *parent) void ItemFilterModel::setModelNodeBackend(const QVariant &modelNodeBackend) { - auto modelNodeBackendObject = modelNodeBackend.value(); const auto backendObjectCasted = @@ -47,18 +46,22 @@ void ItemFilterModel::setModelNodeBackend(const QVariant &modelNodeBackend) void ItemFilterModel::setTypeFilter(const QString &filter) { - if (m_typeFilter != filter) { - m_typeFilter = filter; - setupModel(); - } + if (m_typeFilter == filter) + return; + + m_typeFilter = filter; + setupModel(); + emit typeFilterChanged(); } void ItemFilterModel::setSelectionOnly(bool value) { - if (m_selectionOnly != value) { - m_selectionOnly = value; - setupModel(); - } + if (m_selectionOnly == value) + return; + + m_selectionOnly = value; + setupModel(); + emit selectionOnlyChanged(); } QString ItemFilterModel::typeFilter() const @@ -73,12 +76,7 @@ bool ItemFilterModel::selectionOnly() const void ItemFilterModel::registerDeclarativeType() { - qmlRegisterType("HelperWidgets",2,0,"ItemFilterModel"); -} - -QModelIndex ItemFilterModel::index(int row, int column, const QModelIndex &parent) const -{ - return QAbstractListModel::index(row, column, parent); + qmlRegisterType("HelperWidgets", 2, 0, "ItemFilterModel"); } int ItemFilterModel::rowCount(const QModelIndex &) const @@ -91,7 +89,7 @@ QVariant ItemFilterModel::data(const QModelIndex &index, int role) const if (!index.isValid()) return {}; - ModelNode node = modelNodeForRow(index.row()); + const ModelNode node = modelNodeForRow(index.row()); QVariant value; switch (role) { @@ -102,9 +100,8 @@ QVariant ItemFilterModel::data(const QModelIndex &index, int role) const value = node.variantProperty("objectName").value(); break; case IdAndNameRole: - value = QString("%1 [%2]").arg( - node.variantProperty("objectName").value().toString() - ,node.id()); + value = QString("%1 [%2]").arg(node.variantProperty("objectName").value().toString(), + node.id()); break; default: value = node.id(); @@ -114,12 +111,6 @@ QVariant ItemFilterModel::data(const QModelIndex &index, int role) const return value; } -// TODO: Handle model data manipulation here. -bool ItemFilterModel::setData(const QModelIndex &index, const QVariant &value, int role) -{ - return QAbstractListModel::setData(index, value, role); -} - QHash ItemFilterModel::roleNames() const { return m_roles; diff --git a/src/plugins/qmldesigner/components/propertyeditor/itemfiltermodel.h b/src/plugins/qmldesigner/components/propertyeditor/itemfiltermodel.h index 6876f1edffd..e69269ddd95 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/itemfiltermodel.h +++ b/src/plugins/qmldesigner/components/propertyeditor/itemfiltermodel.h @@ -16,7 +16,7 @@ class ItemFilterModel : public QAbstractListModel { Q_OBJECT - Q_PROPERTY(QString typeFilter READ typeFilter WRITE setTypeFilter) + Q_PROPERTY(QString typeFilter READ typeFilter WRITE setTypeFilter NOTIFY typeFilterChanged) Q_PROPERTY(QVariant modelNodeBackendProperty READ modelNodeBackend WRITE setModelNodeBackend NOTIFY modelNodeBackendChanged) Q_PROPERTY(QStringList itemModel READ itemModel NOTIFY itemModelChanged) Q_PROPERTY(bool selectionOnly READ selectionOnly WRITE setSelectionOnly NOTIFY selectionOnlyChanged) @@ -41,15 +41,13 @@ public: static void registerDeclarativeType(); - // Make index accessible for Qml side since it's not accessible by default in QAbstractListModel - Q_INVOKABLE QModelIndex index(int row, int column = 0, const QModelIndex &parent = QModelIndex()) const override; - Q_INVOKABLE virtual int rowCount(const QModelIndex &parent = QModelIndex()) const override; - Q_INVOKABLE virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; - Q_INVOKABLE virtual bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override; + virtual int rowCount(const QModelIndex &parent = QModelIndex()) const override; + virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; - virtual QHash roleNames() const override; + virtual QHash roleNames() const override; signals: + void typeFilterChanged(); void modelNodeBackendChanged(); void itemModelChanged(); void selectionOnlyChanged(); From 38eaba63de22a74cc50a2a68eb54f3a3656a982a Mon Sep 17 00:00:00 2001 From: Mats Honkamaa Date: Wed, 19 Apr 2023 12:00:36 +0300 Subject: [PATCH 105/192] Doc: Add documentation about baking lightmaps MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Task-number: QDS-9519 Change-Id: Ibe91546a14f074eb7ff489e21c36676f23b74cbb Reviewed-by: Esa Törmänen Reviewed-by: Mats Honkamaa --- .../images/baked-lightmaps-add-property.png | Bin 0 -> 6288 bytes .../images/baked-lightmaps-edit-component.png | Bin 0 -> 10204 bytes .../images/baked-lightmaps-exit-component.png | Bin 0 -> 8767 bytes .../images/baked-lightmaps-navigator-blm.png | Bin 0 -> 26047 bytes .../images/baked-lightmaps-navigator.png | Bin 0 -> 24344 bytes .../images/baked-lightmaps-property-value.png | Bin 0 -> 7471 bytes .../qtdesignstudio-3d-lights.qdoc | 93 ++++++++++++++++++ 7 files changed, 93 insertions(+) create mode 100644 doc/qtdesignstudio/images/baked-lightmaps-add-property.png create mode 100644 doc/qtdesignstudio/images/baked-lightmaps-edit-component.png create mode 100644 doc/qtdesignstudio/images/baked-lightmaps-exit-component.png create mode 100644 doc/qtdesignstudio/images/baked-lightmaps-navigator-blm.png create mode 100644 doc/qtdesignstudio/images/baked-lightmaps-navigator.png create mode 100644 doc/qtdesignstudio/images/baked-lightmaps-property-value.png diff --git a/doc/qtdesignstudio/images/baked-lightmaps-add-property.png b/doc/qtdesignstudio/images/baked-lightmaps-add-property.png new file mode 100644 index 0000000000000000000000000000000000000000..96d4e3e327623622d9e776b63356fe718f720b6d GIT binary patch literal 6288 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 literal 0 HcmV?d00001 diff --git a/doc/qtdesignstudio/images/baked-lightmaps-navigator.png b/doc/qtdesignstudio/images/baked-lightmaps-navigator.png new file mode 100644 index 0000000000000000000000000000000000000000..a6fac65a00dc87f6539bb6cbc7420e1a2da10b93 GIT binary patch literal 24344 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 g?<_gZ0CO@BjR=@0Q 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≻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 literal 0 HcmV?d00001 diff --git a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-lights.qdoc b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-lights.qdoc index 47e39f32118..8c798a31a4f 100644 --- a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-lights.qdoc +++ b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-lights.qdoc @@ -208,4 +208,97 @@ To specify the maximum distance for the shadow map, set the \uicontrol {Shadow map far} property value. Using smaller values may improve the precision and effects of the map. + + \section1 Baking Lightmaps + + \note Lightmaps baking is released as technical preview in \QDS 4.1. + + \section2 Baking Lightmaps for a 3D Model + Baked lightmap components are not visible in the \uicontrol Navigator view by default. To make + them visible, select \inlineimage icons/visibilityon.png + in the \uicontrol Navigator view. + + To bake lightmaps for a 3D model: + \list 1 + \li From \uicontrol Components, drag a \uicontrol {Baked Lightmap} component to + the 3D model in the \uicontrol Navigator view. + \image baked-lightmaps-navigator.png + \li In the \uicontrol Navigator view, select \e bakedLightmap and in the \uicontrol Properties + view: + \list + \li Select \uicontrol Enabled. + \li In \uicontrol Key, set the filename base for the generated light maps. This must be + a unique name. + \li In \uicontrol {Load Prefix}, set the relative path to the folder where the generated + light map files are saved. + \endlist + \li In the \uicontrol Navigator view, select the 3D model and in the \uicontrol Properties + view, select \uicontrol {Used in Baked Lighting}. + \li Optional. Adjust \uicontrol Resolution to set the light map resoution. This effects how + accurate and time-consuming the lightmap baking is. + \li In the \uicontrol Navigator view, select the light component that you want to bake + lightmaps for, and in the \uicontrol Properties view, set \uicontrol {Bake Mode} to BakeModeAll. + \li Right-click anywhere in the \uicontrol 3D view and select \uicontrol {Bake Lights}. + \endlist + + \section2 Baking Lightmaps for a 3D Model Inside a Sub Component + + To bake lightmaps for a 3D model inside a sub component, first add a local custom property to + expose the model: + + \list 1 + \li In the \uicontrol Navigator view, right-click the sub component and select + \uicontrol {Edit Component}. + \image baked-lightmaps-edit-component.png + \li In the \uicontrol Navigator view, select the root component. + \li In the \uicontrol Properties view, select \inlineimage icons/plus.png + in the \uicontrol {Local Custom Properties} section. + \li Add a new property, set \uicontrol Type to alias. + \image baked-lightmaps-add-property.png + \li For the property, set the value to the ID of the 3D model that you want + to bake lightmaps for. + \image baked-lightmaps-property-value.png + \li In the \uicontrol Navigator view, select the 3D model and in the \uicontrol Properties + view: + \list + \li Select \uicontrol {Used in Baked Lighting}. + \li Set \uicontrol Resolution to, for example, 128. + \endlist + \li Save your changes (\key {Ctrl+S}) and return to the main project file. To do this, select + the bread crumb in the top toolbar. + \image baked-lightmaps-exit-component.png + \li From the \uicontrol Components view, drag a \uicontrol {Baked Lightmap} component to the + sub component in the \uicontrol Navigator view. + \image baked-lightmaps-navigator-blm.png + \li In the \uicontrol Navigator view, select the sub component and go to the + \uicontrol Code view. + \li In the \uicontrol Code view, you need to set the properties for the model inside the sub + component by using the exposed property. + + Add the following code inside the sub component. + \code + lmSphere.bakedLightmap: bakedLightmap + \endcode + + It should look something like this: + \code + MyGroup { + id: group + lmSphere.bakedLightmap: bakedLightmap + BakedLightmap { + id: bakedLightmap + loadPrefix: "lightmaps" + } + } + \endcode + \li In the \uicontrol Navigator view, select \e bakedLightMap, and in the \uicontrol + Properties view: + \list + \li Select \uicontrol Enabled. + \li In \uicontrol Key, set the filename base for the generated light maps. This must be + a unique name. + \endlist + \li Right-click anywhere in the \uicontrol 3D view and select \uicontrol {Bake Lights}. + \endlist + */ From 5b090655727485bfcc42646dae694a0ce99e4591 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Thu, 27 Apr 2023 17:54:57 +0300 Subject: [PATCH 106/192] QmlDesigner: Fix puppet crash on View3D removal Fixes: QDS-9591 Change-Id: Idff3e996c66296863b7549b7d8649049497642e0 Reviewed-by: Thomas Hartmann --- .../instances/qt5informationnodeinstanceserver.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp b/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp index 87936392972..0d7b4fa215c 100644 --- a/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp +++ b/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp @@ -827,9 +827,11 @@ void Qt5InformationNodeInstanceServer::handleView3DDestroyed([[maybe_unused]] QO #ifdef QUICK3D_MODULE auto view = qobject_cast(obj); m_view3Ds.remove(obj); - removeNode3D(view->scene()); - if (view && view == m_active3DView) - m_active3DView = nullptr; + if (view) { + removeNode3D(view->scene()); + if (view == m_active3DView) + m_active3DView = nullptr; + } #endif } From b92f741e6098b57d4fb6650ded3b17294dc023ba Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Fri, 28 Apr 2023 13:10:37 +0200 Subject: [PATCH 107/192] StudioWelcome: Fix crash We have to if the project actually has a QmlBuildSystem. Task-number: QDS-9804 Change-Id: I1740f1c0772641888a8828b10380f442308bfe46 Reviewed-by: Tim Jenssen --- 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 5fc4dd7875d..c53ec48c43c 100644 --- a/src/plugins/studiowelcome/studiowelcomeplugin.cpp +++ b/src/plugins/studiowelcome/studiowelcomeplugin.cpp @@ -113,6 +113,9 @@ static Utils::FilePath getMainUiFileWithFallback() auto qmlBuildSystem = qobject_cast( project->activeTarget()->buildSystem()); + if (!qmlBuildSystem) + return {}; + auto mainUiFile = qmlBuildSystem->mainUiFilePath(); if (mainUiFile.exists()) return mainUiFile; From 945832ce9b068a326eb3748e34cd563b057d5ccc Mon Sep 17 00:00:00 2001 From: Aleksei German Date: Fri, 28 Apr 2023 14:04:28 +0200 Subject: [PATCH 108/192] QmlDesigner: Remove isAvailableInVersion Task-numbers: QDS-9420, QDS-9421 Change-Id: Ic93cb6e24c4049d6982344a9d8420b8e567c8265 Reviewed-by: Thomas Hartmann --- .../designercore/include/nodemetainfo.h | 2 -- .../designercore/metainfo/nodemetainfo.cpp | 18 ++---------------- 2 files changed, 2 insertions(+), 18 deletions(-) diff --git a/src/plugins/qmldesigner/designercore/include/nodemetainfo.h b/src/plugins/qmldesigner/designercore/include/nodemetainfo.h index 69ac5f2a977..4c692565fd4 100644 --- a/src/plugins/qmldesigner/designercore/include/nodemetainfo.h +++ b/src/plugins/qmldesigner/designercore/include/nodemetainfo.h @@ -67,8 +67,6 @@ public: QString componentFileName() const; - bool availableInVersion(int majorVersion, int minorVersion) const; - bool isBasedOn(const NodeMetaInfo &metaInfo) const; bool isBasedOn(const NodeMetaInfo &metaInfo1, const NodeMetaInfo &metaInfo2) const; bool isBasedOn(const NodeMetaInfo &metaInfo1, diff --git a/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp b/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp index bebd01b422f..aafb355b68e 100644 --- a/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp +++ b/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp @@ -1659,19 +1659,6 @@ const Storage::Info::Type &NodeMetaInfo::typeData() const } #endif -bool NodeMetaInfo::availableInVersion(int majorVersion, int minorVersion) const -{ - if (!isValid()) - return false; - - if (majorVersion == -1 && minorVersion == -1) - return true; - - return (m_privateData->majorVersion() >= majorVersion) - || (majorVersion == m_privateData->majorVersion() - && m_privateData->minorVersion() >= minorVersion); -} - bool NodeMetaInfo::isSubclassOf(const TypeName &type, int majorVersion, int minorVersion) const { if (!isValid()) { @@ -1682,7 +1669,7 @@ bool NodeMetaInfo::isSubclassOf(const TypeName &type, int majorVersion, int mino if (typeName().isEmpty()) return false; - if (typeName() == type && availableInVersion(majorVersion, minorVersion)) + if (typeName() == type) return true; if (m_privateData->prototypeCachePositives().contains( @@ -1695,8 +1682,7 @@ bool NodeMetaInfo::isSubclassOf(const TypeName &type, int majorVersion, int mino const NodeMetaInfos superClassList = superClasses(); for (const NodeMetaInfo &superClass : superClassList) { - if (superClass.m_privateData->cleverCheckType(type) - && superClass.availableInVersion(majorVersion, minorVersion)) { + if (superClass.m_privateData->cleverCheckType(type)) { m_privateData->prototypeCachePositives().insert( stringIdentifier(type, majorVersion, minorVersion)); return true; From 66af0f4dab3862bed5c4507aa7cb60be2d8d2c32 Mon Sep 17 00:00:00 2001 From: Henning Gruendl Date: Fri, 28 Apr 2023 13:51:34 +0200 Subject: [PATCH 109/192] OutputPane: Add setting to restore previous pane Add a setting to store the previously used output pane index to be able to restore it on startup. Task-number: QDS-9647 Change-Id: I2ef277e8d4c79a6d0b017e5422ad639b56b2140b Reviewed-by: Eike Ziller --- src/plugins/coreplugin/outputpane.cpp | 4 ++++ src/plugins/coreplugin/outputpanemanager.cpp | 11 +++++++++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/plugins/coreplugin/outputpane.cpp b/src/plugins/coreplugin/outputpane.cpp index 67e57448aac..81473320bbd 100644 --- a/src/plugins/coreplugin/outputpane.cpp +++ b/src/plugins/coreplugin/outputpane.cpp @@ -183,6 +183,10 @@ void OutputPanePlaceHolder::showEvent(QShowEvent *) d->m_initialized = true; setHeight(Internal::OutputPaneManager::outputPaneHeightSetting()); } + if (OutputPanePlaceHolderPrivate::m_current == this) { + Internal::OutputPaneManager *om = Internal::OutputPaneManager::instance(); + om->updateStatusButtons(true); + } } OutputPanePlaceHolder *OutputPanePlaceHolder::getCurrent() diff --git a/src/plugins/coreplugin/outputpanemanager.cpp b/src/plugins/coreplugin/outputpanemanager.cpp index a249686f9e8..e9390cb11db 100644 --- a/src/plugins/coreplugin/outputpanemanager.cpp +++ b/src/plugins/coreplugin/outputpanemanager.cpp @@ -575,7 +575,12 @@ void OutputPaneManager::readSettings() } settings->endArray(); - m_outputPaneHeightSetting = settings->value(QLatin1String("OutputPanePlaceHolder/Height"), 0).toInt(); + m_outputPaneHeightSetting + = settings->value(QLatin1String("OutputPanePlaceHolder/Height"), 0).toInt(); + const int currentIdx + = settings->value(QLatin1String("OutputPanePlaceHolder/CurrentIndex"), 0).toInt(); + if (QTC_GUARD(currentIdx >= 0 && currentIdx < g_outputPanes.size())) + setCurrentIndex(currentIdx); } void OutputPaneManager::slotNext() @@ -682,7 +687,8 @@ void OutputPaneManager::setCurrentIndex(int idx) OutputPaneData &data = g_outputPanes[idx]; IOutputPane *pane = data.pane; data.button->show(); - pane->visibilityChanged(true); + if (OutputPanePlaceHolder::isCurrentVisible()) + pane->visibilityChanged(true); bool canNavigate = pane->canNavigate(); m_prevAction->setEnabled(canNavigate && pane->canPrevious()); @@ -737,6 +743,7 @@ void OutputPaneManager::saveSettings() const if (OutputPanePlaceHolder *curr = OutputPanePlaceHolder::getCurrent()) heightSetting = curr->nonMaximizedSize(); settings->setValue(QLatin1String("OutputPanePlaceHolder/Height"), heightSetting); + settings->setValue(QLatin1String("OutputPanePlaceHolder/CurrentIndex"), currentIndex()); } void OutputPaneManager::clearPage() From 4642c5fd7ddf73b4753037c3616c2d60d5899b6e Mon Sep 17 00:00:00 2001 From: Henning Gruendl Date: Fri, 28 Apr 2023 13:57:14 +0200 Subject: [PATCH 110/192] QmlDesigner: Fix view menus intially enabled - Fix views, workspaces and output menus being enabled in welcome mode - Use mode instead of context change to enable/disable menus Change-Id: I68053112d66e100e3bf3b07368310a3c3c50999d Reviewed-by: Thomas Hartmann --- src/plugins/qmldesigner/designmodewidget.cpp | 27 +++++++++++++++----- 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/src/plugins/qmldesigner/designmodewidget.cpp b/src/plugins/qmldesigner/designmodewidget.cpp index 913fb05f0ba..ef22c2bb971 100644 --- a/src/plugins/qmldesigner/designmodewidget.cpp +++ b/src/plugins/qmldesigner/designmodewidget.cpp @@ -221,14 +221,29 @@ void DesignModeWidget::setup() mworkspaces->setOnAllDisabledBehavior(Core::ActionContainer::Show); // Connect opening of the 'workspaces' menu with creation of the workspaces menu connect(mworkspaces->menu(), &QMenu::aboutToShow, this, &DesignModeWidget::aboutToShowWorkspaces); - // Disable workspace menu when context is different to C_DESIGN_MODE - connect(Core::ICore::instance(), &Core::ICore::contextChanged, - this, [mworkspaces](const Core::Context &context){ - if (context.contains(Core::Constants::C_DESIGN_MODE)) + + Core::ActionContainer *mpanes = Core::ActionManager::actionContainer(Core::Constants::M_VIEW_PANES); + + // Initially disable menus + mviews->menu()->setEnabled(false); + mworkspaces->menu()->setEnabled(false); + mpanes->menu()->setEnabled(false); + + // Enable/disable menus when mode is different to MODE_DESIGN + connect(Core::ModeManager::instance(), + &Core::ModeManager::currentModeChanged, + this, + [mviews, mworkspaces, mpanes](Utils::Id mode, Utils::Id) { + if (mode == Core::Constants::MODE_DESIGN) { + mviews->menu()->setEnabled(true); mworkspaces->menu()->setEnabled(true); - else + mpanes->menu()->setEnabled(true); + } else { + mviews->menu()->setEnabled(false); mworkspaces->menu()->setEnabled(false); - }); + mpanes->menu()->setEnabled(false); + } + }); // Create a DockWidget for each QWidget and add them to the DockManager const Core::Context designContext(Core::Constants::C_DESIGN_MODE); From 6bf66e982bc9076f537976c8213141a9ef792de4 Mon Sep 17 00:00:00 2001 From: Henning Gruendl Date: Fri, 28 Apr 2023 14:05:32 +0200 Subject: [PATCH 111/192] OutputPane: Fix initial pane title not set Fix the title of the initially selected pane in the output pane placeholder toolbar not being set by the output pane manager. Change-Id: I94407c76454a632a9b5707edcbde0ce6dc990d61 Reviewed-by: Eike Ziller --- src/plugins/coreplugin/outputpanemanager.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/plugins/coreplugin/outputpanemanager.cpp b/src/plugins/coreplugin/outputpanemanager.cpp index e9390cb11db..97b41776941 100644 --- a/src/plugins/coreplugin/outputpanemanager.cpp +++ b/src/plugins/coreplugin/outputpanemanager.cpp @@ -497,6 +497,9 @@ void OutputPaneManager::initialize() m_instance->m_titleLabel->setMinimumWidth( minTitleWidth + m_instance->m_titleLabel->contentsMargins().left() + m_instance->m_titleLabel->contentsMargins().right()); + const int currentIdx = m_instance->currentIndex(); + if (QTC_GUARD(currentIdx >= 0 && currentIdx < g_outputPanes.size())) + m_instance->m_titleLabel->setText(g_outputPanes[currentIdx].pane->displayName()); m_instance->m_buttonsWidget->layout()->addWidget(m_instance->m_manageButton); connect(m_instance->m_manageButton, &QAbstractButton::clicked, From 3ef30f57a222776a466c609a9d9ca56c35ebacfd Mon Sep 17 00:00:00 2001 From: Henning Gruendl Date: Fri, 28 Apr 2023 16:06:07 +0200 Subject: [PATCH 112/192] QmlDesigner: Remove output pane default workaround Remove the workaround that always opens the Application Output when the Output Pane view opens. This is no longer needed as the last used output pane index is stored in the settings and used whenever the output pane is opened the first time. Change-Id: Ifbe6c60b5331489faff4bb0bce30c3f6fef8fba6 Reviewed-by: Thomas Hartmann --- src/plugins/qmldesigner/designmodewidget.cpp | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/plugins/qmldesigner/designmodewidget.cpp b/src/plugins/qmldesigner/designmodewidget.cpp index ef22c2bb971..27b9b2ff295 100644 --- a/src/plugins/qmldesigner/designmodewidget.cpp +++ b/src/plugins/qmldesigner/designmodewidget.cpp @@ -348,15 +348,6 @@ void DesignModeWidget::setup() command->setAttribute(Core::Command::CA_Hide); viewCommands.append(command); - connect(command->action(), &QAction::triggered, this, [command]() { - if (!command->action()->isChecked()) - return; - - auto cmd = Core::ActionManager::command("QtCreator.Pane.ApplicationOutput"); - QTC_ASSERT(cmd, return); - cmd->action()->trigger(); - }); - connect(outputPanePlaceholder, &Core::OutputPanePlaceHolder::visibilityChangeRequested, m_outputPaneDockWidget, From 949c3800188a40e09f20863ab9be2c545eec791d Mon Sep 17 00:00:00 2001 From: Henning Gruendl Date: Thu, 27 Apr 2023 14:01:38 +0200 Subject: [PATCH 113/192] QmlDesigner: Fix TextFieldSpecifics Add TextInputSection to TextFieldSpecifics as TextField inherits from TextInput. Align the properties of the contained sections to the ones in TextInputSpecifics. Also adjust TextArea specifics to also contain TextInputSection. Task-number: QDS-9792 Change-Id: I3a4a734d965f3834ccd1e5cad23bb125b9ca592c Reviewed-by: Thomas Hartmann Reviewed-by: --- .../QtQuick/Controls/TextAreaSpecifics.qml | 2 ++ .../QtQuick/Controls/TextFieldSpecifics.qml | 16 +++++++-- .../HelperWidgets}/TextInputSection.qml | 36 +++++++++---------- .../imports/HelperWidgets/qmldir | 1 + 4 files changed, 34 insertions(+), 21 deletions(-) rename share/qtcreator/qmldesigner/propertyEditorQmlSources/{QtQuick => imports/HelperWidgets}/TextInputSection.qml (89%) diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/TextAreaSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/TextAreaSpecifics.qml index 766e2b78aa5..d6b0c76c57e 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/TextAreaSpecifics.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/TextAreaSpecifics.qml @@ -16,6 +16,8 @@ Column { showVerticalAlignment: true } + TextInputSection {} + TextExtrasSection { showWrapMode: true showFormatProperty: true diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/TextFieldSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/TextFieldSpecifics.qml index d7286272309..b0aa6fbfc59 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/TextFieldSpecifics.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/TextFieldSpecifics.qml @@ -14,11 +14,21 @@ Column { caption: qsTr("Text Field") } - CharacterSection {} + CharacterSection { + showVerticalAlignment: true + } - TextExtrasSection {} + TextInputSection { + isTextInput: true + } - FontExtrasSection {} + TextExtrasSection { + showWrapMode: true + } + + FontExtrasSection { + showStyle: false + } PaddingSection {} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/TextInputSection.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/TextInputSection.qml similarity index 89% rename from share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/TextInputSection.qml rename to share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/TextInputSection.qml index b76666f7bc5..737c742b33e 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/TextInputSection.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/TextInputSection.qml @@ -7,7 +7,7 @@ import HelperWidgets 2.0 import StudioTheme 1.0 as StudioTheme Section { - id: textInputSection + id: root anchors.left: parent.left anchors.right: parent.right caption: qsTr("Text Input") @@ -46,7 +46,7 @@ Section { + StudioTheme.Values.actionIndicatorWidth width: implicitWidth backendValue: backendValues.mouseSelectionMode - scope: "TextInput" + scope: root.isTextInput ? "TextInput" : "TextEdit" model: ["SelectCharacters", "SelectWords"] } @@ -54,13 +54,13 @@ Section { } PropertyLabel { - visible: textInputSection.isTextInput + visible: root.isTextInput text: qsTr("Input mask") tooltip: qsTr("Sets the allowed characters.") } SecondColumnLayout { - visible: textInputSection.isTextInput + visible: root.isTextInput LineEdit { backendValue: backendValues.inputMask @@ -74,13 +74,13 @@ Section { } PropertyLabel { - visible: textInputSection.isTextInput + visible: root.isTextInput text: qsTr("Echo mode") tooltip: qsTr("Sets the visibility mode.") } SecondColumnLayout { - visible: textInputSection.isTextInput + visible: root.isTextInput ComboBox { implicitWidth: StudioTheme.Values.singleControlColumnWidth @@ -95,13 +95,13 @@ Section { } PropertyLabel { - visible: textInputSection.isTextInput + visible: root.isTextInput text: qsTr("Password character") tooltip: qsTr("Sets which character to display when passwords are entered.") } SecondColumnLayout { - visible: textInputSection.isTextInput + visible: root.isTextInput LineEdit { backendValue: backendValues.passwordCharacter @@ -115,13 +115,13 @@ Section { } PropertyLabel { - visible: !textInputSection.isTextInput + visible: !root.isTextInput text: qsTr("Tab stop distance") tooltip: qsTr("Default distance between tab stops in device units.") } SecondColumnLayout { - visible: !textInputSection.isTextInput + visible: !root.isTextInput SpinBox { implicitWidth: StudioTheme.Values.twoControlColumnWidth @@ -139,13 +139,13 @@ Section { } PropertyLabel { - visible: !textInputSection.isTextInput + visible: !root.isTextInput text: qsTr("Text margin") tooltip: qsTr("Margin around the text in the Text Edit in pixels.") } SecondColumnLayout { - visible: !textInputSection.isTextInput + visible: !root.isTextInput SpinBox { implicitWidth: StudioTheme.Values.twoControlColumnWidth @@ -163,13 +163,13 @@ Section { } PropertyLabel { - visible: textInputSection.isTextInput + visible: root.isTextInput text: qsTr("Maximum length") tooltip: qsTr("Sets the maximum length of the text.") } SecondColumnLayout { - visible: textInputSection.isTextInput + visible: root.isTextInput SpinBox { implicitWidth: StudioTheme.Values.singleControlColumnWidth @@ -216,13 +216,13 @@ Section { FlagItem { backendValue: backendValues.activeFocusOnPress } PropertyLabel { - visible: textInputSection.isTextInput + visible: root.isTextInput text: qsTr("Auto scroll") tooltip: qsTr("Toggles if the text scrolls when it exceeds its boundary.") } FlagItem { - visible: textInputSection.isTextInput + visible: root.isTextInput backendValue: backendValues.autoScroll } @@ -248,12 +248,12 @@ Section { FlagItem { backendValue: backendValues.selectByMouse } PropertyLabel { - visible: !textInputSection.isTextInput + visible: !root.isTextInput text: qsTr("Select by keyboard") } FlagItem { - visible: !textInputSection.isTextInput + visible: !root.isTextInput backendValue: backendValues.selectByKeyboard } } diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/qmldir b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/qmldir index ca1411a4d6e..9c19a45e2ef 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/qmldir +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/qmldir @@ -67,6 +67,7 @@ Spacer 2.0 Spacer.qml SpinBox 2.0 SpinBox.qml StandardTextSection 2.0 StandardTextSection.qml TextExtrasSection 2.0 TextExtrasSection.qml +TextInputSection 2.0 TextInputSection.qml ToolTipArea 2.0 ToolTipArea.qml UrlChooser 2.0 UrlChooser.qml VerticalScrollBar 2.0 VerticalScrollBar.qml From d3b8df68ebfdf207a98398b24bcbd13c65dee39f Mon Sep 17 00:00:00 2001 From: Henning Gruendl Date: Fri, 28 Apr 2023 14:00:28 +0200 Subject: [PATCH 114/192] QmlDesigner: Code cleanup in design mode widget Remove the redundant initialization of navigation views. Change-Id: I26d0c199cfc8be11b364763c0dba37e41dedc64e Reviewed-by: Thomas Hartmann --- src/plugins/qmldesigner/designmodewidget.cpp | 105 +++++++++---------- 1 file changed, 52 insertions(+), 53 deletions(-) diff --git a/src/plugins/qmldesigner/designmodewidget.cpp b/src/plugins/qmldesigner/designmodewidget.cpp index 27b9b2ff295..1bb427bd055 100644 --- a/src/plugins/qmldesigner/designmodewidget.cpp +++ b/src/plugins/qmldesigner/designmodewidget.cpp @@ -251,60 +251,48 @@ void DesignModeWidget::setup() // First get all navigation views QList factories = Core::INavigationWidgetFactory::allNavigationFactories(); - - QList viewCommands; + QList viewCommands; + const QList navigationViewIds = {"Projects", "File System", "Open Documents"}; for (Core::INavigationWidgetFactory *factory : factories) { - Core::NavigationView navigationView; - navigationView.widget = nullptr; - QString uniqueId; - QString title; + Core::NavigationView navigationView = {nullptr, {}}; - if (factory->id() == "Projects") { - navigationView = factory->createWidget(); - hideToolButtons(navigationView.dockToolBarWidgets); - navigationView.widget->setWindowTitle(tr(factory->id().name())); - uniqueId = "Projects"; - title = "Projects"; - } - if (factory->id() == "File System") { - navigationView = factory->createWidget(); - hideToolButtons(navigationView.dockToolBarWidgets); - navigationView.widget->setWindowTitle(tr(factory->id().name())); - uniqueId = "FileSystem"; - title = "File System"; - } - if (factory->id() == "Open Documents") { - navigationView = factory->createWidget(); - hideToolButtons(navigationView.dockToolBarWidgets); - navigationView.widget->setWindowTitle(tr(factory->id().name())); - uniqueId = "OpenDocuments"; - title = "Open Documents"; - } + if (!navigationViewIds.contains(factory->id())) + continue; - if (navigationView.widget) { - // Apply stylesheet to QWidget - QByteArray sheet = Utils::FileReader::fetchQrc(":/qmldesigner/stylesheet.css"); - sheet += Utils::FileReader::fetchQrc(":/qmldesigner/scrollbar.css"); - sheet += "QLabel { background-color: creatorTheme.DSsectionHeadBackground; }"; - navigationView.widget->setStyleSheet(Theme::replaceCssColors(QString::fromUtf8(sheet))); + navigationView = factory->createWidget(); - // Create DockWidget - ADS::DockWidget *dockWidget = new ADS::DockWidget(uniqueId); - dockWidget->setWidget(navigationView.widget); - dockWidget->setWindowTitle(title); - m_dockManager->addDockWidget(ADS::NoDockWidgetArea, dockWidget); + if (!navigationView.widget) + continue; - // Set unique id as object name - navigationView.widget->setObjectName(uniqueId); + hideToolButtons(navigationView.dockToolBarWidgets); + navigationView.widget->setWindowTitle(tr(factory->id().name())); - // Create menu action - auto command = Core::ActionManager::registerAction(dockWidget->toggleViewAction(), - actionToggle.withSuffix(uniqueId + "Widget"), - designContext); - command->setAttribute(Core::Command::CA_Hide); - viewCommands.append(command); - } + QString idString = factory->id().toSetting().toString(); + const QString title = idString; + const QString uniqueId = idString.remove(" "); // title without whitespaces + + // Apply stylesheet to QWidget + QByteArray sheet = Utils::FileReader::fetchQrc(":/qmldesigner/stylesheet.css"); + sheet += Utils::FileReader::fetchQrc(":/qmldesigner/scrollbar.css"); + sheet += "QLabel { background-color: creatorTheme.DSsectionHeadBackground; }"; + navigationView.widget->setStyleSheet(Theme::replaceCssColors(QString::fromUtf8(sheet))); + + // Create DockWidget + ADS::DockWidget *dockWidget = new ADS::DockWidget(uniqueId); + dockWidget->setWidget(navigationView.widget); + dockWidget->setWindowTitle(title); + m_dockManager->addDockWidget(ADS::NoDockWidgetArea, dockWidget); + + // Set unique id as object name + navigationView.widget->setObjectName(uniqueId); + + // Create menu action + auto command = Core::ActionManager::registerAction(dockWidget->toggleViewAction(), + actionToggle.withSuffix(uniqueId + "Widget"), + designContext); + command->setAttribute(Core::Command::CA_Hide); + viewCommands.append(command); } // Afterwards get all the other widgets @@ -323,7 +311,8 @@ void DesignModeWidget::setup() // Create menu action auto command = Core::ActionManager::registerAction(dockWidget->toggleViewAction(), - actionToggle.withSuffix(widgetInfo.uniqueId + "Widget"), + actionToggle.withSuffix( + widgetInfo.uniqueId + "Widget"), designContext); command->setAttribute(Core::Command::CA_Hide); viewCommands.append(command); @@ -354,7 +343,7 @@ void DesignModeWidget::setup() &ADS::DockWidget::toggleView); } - std::sort(viewCommands.begin(), viewCommands.end(), [](Core::Command *first, Core::Command *second){ + std::sort(viewCommands.begin(), viewCommands.end(), [](Core::Command *first, Core::Command *second) { return first->description() < second->description(); }); @@ -367,7 +356,10 @@ void DesignModeWidget::setup() toolBar->addAction(viewManager().componentViewAction()); toolBar->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); - DesignerActionToolBar *designerToolBar = QmlDesignerPlugin::instance()->viewManager().designerActionManager().createToolBar(m_toolBar); + DesignerActionToolBar *designerToolBar = QmlDesignerPlugin::instance() + ->viewManager() + .designerActionManager() + .createToolBar(m_toolBar); designerToolBar->layout()->addWidget(toolBar); @@ -376,8 +368,14 @@ void DesignModeWidget::setup() m_toolBar->setToolbarCreationFlags(Core::EditorToolBar::FlagsStandalone); m_toolBar->setNavigationVisible(true); - connect(m_toolBar, &Core::EditorToolBar::goForwardClicked, this, &DesignModeWidget::toolBarOnGoForwardClicked); - connect(m_toolBar, &Core::EditorToolBar::goBackClicked, this, &DesignModeWidget::toolBarOnGoBackClicked); + connect(m_toolBar, + &Core::EditorToolBar::goForwardClicked, + this, + &DesignModeWidget::toolBarOnGoForwardClicked); + connect(m_toolBar, + &Core::EditorToolBar::goBackClicked, + this, + &DesignModeWidget::toolBarOnGoBackClicked); QToolBar* toolBarWrapper = new QToolBar(); toolBarWrapper->addWidget(m_toolBar); @@ -436,7 +434,8 @@ void DesignModeWidget::setup() void DesignModeWidget::aboutToShowWorkspaces() { - Core::ActionContainer *aci = Core::ActionManager::actionContainer(QmlDesigner::Constants::M_VIEW_WORKSPACES); + Core::ActionContainer *aci = Core::ActionManager::actionContainer( + QmlDesigner::Constants::M_VIEW_WORKSPACES); QMenu *menu = aci->menu(); menu->clear(); From a73fdd8db4829d1b370fecabf73204c1ccc6d1df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esa=20T=C3=B6rm=C3=A4nen?= Date: Tue, 25 Apr 2023 12:40:55 +0300 Subject: [PATCH 115/192] Doc: Add QDS Version Compatibility with Qt for MCUs SDKs topic Added the QDS Version Compatibility with Qt for MCUs SDKs topic and modified the related topics accordingly. Task-number: QDS-9287 Change-Id: I30a89a87152d5db010d074110424b6039ce719cd Reviewed-by: Mats Honkamaa Reviewed-by: Leena Miettinen Reviewed-by: Maija Metso --- ...ignstudio-compatibility-with-mcu-sdks.qdoc | 29 +++++++++++++++++++ .../src/mcus/qtdesignstudio-on-mcus.qdoc | 2 +- .../src/qtdesignstudio-help-overview.qdoc | 2 +- .../src/qtdesignstudio-toc.qdoc | 3 ++ 4 files changed, 34 insertions(+), 2 deletions(-) create mode 100644 doc/qtdesignstudio/src/mcus/qtdesignstudio-compatibility-with-mcu-sdks.qdoc diff --git a/doc/qtdesignstudio/src/mcus/qtdesignstudio-compatibility-with-mcu-sdks.qdoc b/doc/qtdesignstudio/src/mcus/qtdesignstudio-compatibility-with-mcu-sdks.qdoc new file mode 100644 index 00000000000..c6dab76398d --- /dev/null +++ b/doc/qtdesignstudio/src/mcus/qtdesignstudio-compatibility-with-mcu-sdks.qdoc @@ -0,0 +1,29 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only + +/*! + \previouspage studio-on-mcus.html + \page studio-compatibility-with-mcu-sdks.html + \nextpage studio-help.html + + \title \QDS Version Compatibility with \QMCU SDKs + + The following table lists the \QDS versions you can use to develop + applications with particular \QMCU SDK versions. + + \table + \header + \li \QDS Version + \li \QMCU SDK Version + \row + \li 4.0 or later + \li 2.4 or later + \row + \li 3.8 up to 3.9 + \li 2.3 + \row + \li 3.4 up to 3.7 + \li 2.2 (LTS) + \endtable + +*/ diff --git a/doc/qtdesignstudio/src/mcus/qtdesignstudio-on-mcus.qdoc b/doc/qtdesignstudio/src/mcus/qtdesignstudio-on-mcus.qdoc index b8409d9e442..a16df932c91 100644 --- a/doc/qtdesignstudio/src/mcus/qtdesignstudio-on-mcus.qdoc +++ b/doc/qtdesignstudio/src/mcus/qtdesignstudio-on-mcus.qdoc @@ -4,7 +4,7 @@ /*! \previouspage creator-editor-external.html \page studio-on-mcus.html - \nextpage studio-help.html + \nextpage studio-compatibility-with-mcu-sdks.html \title \QDS on MCUs diff --git a/doc/qtdesignstudio/src/qtdesignstudio-help-overview.qdoc b/doc/qtdesignstudio/src/qtdesignstudio-help-overview.qdoc index d63f049659f..e3a00cb8dfb 100644 --- a/doc/qtdesignstudio/src/qtdesignstudio-help-overview.qdoc +++ b/doc/qtdesignstudio/src/qtdesignstudio-help-overview.qdoc @@ -3,7 +3,7 @@ /*! \page studio-help.html - \previouspage studio-on-mcus.html + \previouspage studio-compatibility-with-mcu-sdks.html \nextpage creator-help.html \title Help diff --git a/doc/qtdesignstudio/src/qtdesignstudio-toc.qdoc b/doc/qtdesignstudio/src/qtdesignstudio-toc.qdoc index 4a5bb3c3f21..1b9e906c7f6 100644 --- a/doc/qtdesignstudio/src/qtdesignstudio-toc.qdoc +++ b/doc/qtdesignstudio/src/qtdesignstudio-toc.qdoc @@ -264,6 +264,9 @@ \li \l{Converting UI Projects to Applications} \li \l{Using External Tools} \li \l{\QDS on MCUs} + \list + \li \l {\QDS Version Compatibility with \QMCU SDKs} + \endlist \endlist \li \l Help \list From 5074c424f1bc1a424bea854f0a287dfa6c4c16a2 Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Wed, 19 Apr 2023 13:23:44 +0300 Subject: [PATCH 116/192] QmlDesigner: Update material preview images instead of whole refresh Task-number: QDS-9485 Change-Id: I865c8d39dea4ec0c451f41cbe8c5f603deae4f25 Reviewed-by: Mahmoud Badri Reviewed-by: Miikka Heikkinen --- .../materialbrowser/materialbrowserview.cpp | 20 ++++++++++++------- .../materialbrowser/materialbrowserview.h | 1 + 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.cpp b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.cpp index 5caf0169eec..5349fc26ce7 100644 --- a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.cpp +++ b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.cpp @@ -264,12 +264,17 @@ void MaterialBrowserView::refreshModel(bool updateImages) m_widget->materialBrowserTexturesModel()->setTextures(textures); m_widget->materialBrowserModel()->setHasMaterialLibrary(matLib.isValid()); - if (updateImages) { - for (const ModelNode &node : std::as_const(materials)) - m_previewRequests.insert(node); - if (!m_previewRequests.isEmpty()) - m_previewTimer.start(0); - } + if (updateImages) + updateMaterialsPreview(); +} + +void MaterialBrowserView::updateMaterialsPreview() +{ + const QList materials = m_widget->materialBrowserModel()->materials(); + for (const ModelNode &node : materials) + m_previewRequests.insert(node); + if (!m_previewRequests.isEmpty()) + m_previewTimer.start(0); } bool MaterialBrowserView::isMaterial(const ModelNode &node) const @@ -540,7 +545,8 @@ void MaterialBrowserView::active3DSceneChanged(qint32 sceneId) void MaterialBrowserView::currentStateChanged([[maybe_unused]] const ModelNode &node) { - refreshModel(true); + m_widget->materialBrowserTexturesModel()->updateAllTexturesSources(); + updateMaterialsPreview(); } void MaterialBrowserView::instancesCompleted(const QVector &completedNodeList) diff --git a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.h b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.h index 2e0227be413..297487ae66f 100644 --- a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.h +++ b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.h @@ -66,6 +66,7 @@ protected: private: void refreshModel(bool updateImages); + void updateMaterialsPreview(); bool isMaterial(const ModelNode &node) const; bool isTexture(const ModelNode &node) const; void loadPropertyGroups(); From 89acb035af5330ee218dc4b31710b8a8a5f8b7ba Mon Sep 17 00:00:00 2001 From: Pranta Dastider Date: Wed, 26 Apr 2023 14:58:39 +0200 Subject: [PATCH 117/192] QmlDesigner: Update 3D tooltip for Component 3D component Since this was in another repository, had to put it separately as update. This would let the Component 3D to have tooltip in the 3D components space. Fixes: QDS-9757 Change-Id: I91bbfbcd54776774989a12d4bbefe8c8deb93ab1 Reviewed-by: Miikka Heikkinen Reviewed-by: Mahmoud Badri Reviewed-by: Mats Honkamaa --- src/plugins/qmldesigner/qtquickplugin/quick.metainfo | 1 + 1 file changed, 1 insertion(+) diff --git a/src/plugins/qmldesigner/qtquickplugin/quick.metainfo b/src/plugins/qmldesigner/qtquickplugin/quick.metainfo index 45e01621280..f1aeaa9ebbd 100644 --- a/src/plugins/qmldesigner/qtquickplugin/quick.metainfo +++ b/src/plugins/qmldesigner/qtquickplugin/quick.metainfo @@ -571,6 +571,7 @@ MetaInfo { requiredImport: "QtQuick3D" QmlSource { source: ":/qtquickplugin/source/component3d.qml" } + toolTip: qsTr("Allows you to define 3D components inline, within a QML document.") } } From 6eadb5d357bfab4967ad3224531712c9efa9f255 Mon Sep 17 00:00:00 2001 From: Tim Jenssen Date: Wed, 3 May 2023 06:24:49 +0200 Subject: [PATCH 118/192] qds: move branding into opensource repository Task-number: QDS-8326 Change-Id: I3484a48de30ef394071d1fc59ad8fdff20327af3 Reviewed-by: Burak Hancerli Reviewed-by: Marco Bubke Reviewed-by: Eike Ziller --- dist/branding/qtdesignstudio/CMakeLists.txt | 1 + .../qtdesignstudio/QtCreatorIDEBranding.cmake | 18 +++++ .../QtProject/QtDesignStudio.ini | 64 +++++++++++++++++ .../QtProject/qtdesignstudio/EasingCurves.ini | 2 + .../images/logo/128/QtProject-qtcreator.png | Bin 0 -> 5276 bytes .../images/logo/16/QtProject-qtcreator.png | Bin 0 -> 482 bytes .../images/logo/24/QtProject-qtcreator.png | Bin 0 -> 529 bytes .../images/logo/256/QtProject-qtcreator.png | Bin 0 -> 10212 bytes .../images/logo/32/QtProject-qtcreator.png | Bin 0 -> 735 bytes .../images/logo/48/QtProject-qtcreator.png | Bin 0 -> 1889 bytes .../images/logo/512/QtProject-qtcreator.png | Bin 0 -> 24742 bytes .../images/logo/64/QtProject-qtcreator.png | Bin 0 -> 2656 bytes dist/branding/qtdesignstudio/qtcreator.ico | Bin 0 -> 120874 bytes .../icon_128x128.png | Bin 0 -> 5947 bytes .../icon_128x128@2x.png | Bin 0 -> 12750 bytes .../qtcreator-project.iconset/icon_16x16.png | Bin 0 -> 509 bytes .../icon_16x16@2x.png | Bin 0 -> 1098 bytes .../icon_256x256.png | Bin 0 -> 12750 bytes .../icon_256x256@2x.png | Bin 0 -> 30033 bytes .../qtcreator-project.iconset/icon_32x32.png | Bin 0 -> 1098 bytes .../icon_32x32@2x.png | Bin 0 -> 2410 bytes .../icon_512x512.png | Bin 0 -> 30033 bytes .../icon_512x512@2x.png | Bin 0 -> 66441 bytes .../qtcreator.appiconset/Contents.json | 68 ++++++++++++++++++ .../qtcreator.appiconset/icon_128x128.png | Bin 0 -> 5276 bytes .../qtcreator.appiconset/icon_128x128@2x.png | Bin 0 -> 10212 bytes .../qtcreator.appiconset/icon_16x16.png | Bin 0 -> 482 bytes .../qtcreator.appiconset/icon_16x16@2x.png | Bin 0 -> 735 bytes .../qtcreator.appiconset/icon_256x256.png | Bin 0 -> 10212 bytes .../qtcreator.appiconset/icon_256x256@2x.png | Bin 0 -> 24742 bytes .../qtcreator.appiconset/icon_32x32.png | Bin 0 -> 735 bytes .../qtcreator.appiconset/icon_32x32@2x.png | Bin 0 -> 2656 bytes .../qtcreator.appiconset/icon_512x512.png | Bin 0 -> 24742 bytes .../qtcreator.appiconset/icon_512x512@2x.png | Bin 0 -> 54880 bytes 34 files changed, 153 insertions(+) create mode 100644 dist/branding/qtdesignstudio/CMakeLists.txt create mode 100644 dist/branding/qtdesignstudio/QtCreatorIDEBranding.cmake create mode 100644 dist/branding/qtdesignstudio/QtProject/QtDesignStudio.ini create mode 100644 dist/branding/qtdesignstudio/QtProject/qtdesignstudio/EasingCurves.ini create mode 100644 dist/branding/qtdesignstudio/images/logo/128/QtProject-qtcreator.png create mode 100644 dist/branding/qtdesignstudio/images/logo/16/QtProject-qtcreator.png create mode 100644 dist/branding/qtdesignstudio/images/logo/24/QtProject-qtcreator.png create mode 100644 dist/branding/qtdesignstudio/images/logo/256/QtProject-qtcreator.png create mode 100644 dist/branding/qtdesignstudio/images/logo/32/QtProject-qtcreator.png create mode 100644 dist/branding/qtdesignstudio/images/logo/48/QtProject-qtcreator.png create mode 100644 dist/branding/qtdesignstudio/images/logo/512/QtProject-qtcreator.png create mode 100644 dist/branding/qtdesignstudio/images/logo/64/QtProject-qtcreator.png create mode 100644 dist/branding/qtdesignstudio/qtcreator.ico create mode 100644 dist/branding/qtdesignstudio/qtcreator.xcassets/qtcreator-project.iconset/icon_128x128.png create mode 100644 dist/branding/qtdesignstudio/qtcreator.xcassets/qtcreator-project.iconset/icon_128x128@2x.png create mode 100644 dist/branding/qtdesignstudio/qtcreator.xcassets/qtcreator-project.iconset/icon_16x16.png create mode 100644 dist/branding/qtdesignstudio/qtcreator.xcassets/qtcreator-project.iconset/icon_16x16@2x.png create mode 100644 dist/branding/qtdesignstudio/qtcreator.xcassets/qtcreator-project.iconset/icon_256x256.png create mode 100644 dist/branding/qtdesignstudio/qtcreator.xcassets/qtcreator-project.iconset/icon_256x256@2x.png create mode 100644 dist/branding/qtdesignstudio/qtcreator.xcassets/qtcreator-project.iconset/icon_32x32.png create mode 100644 dist/branding/qtdesignstudio/qtcreator.xcassets/qtcreator-project.iconset/icon_32x32@2x.png create mode 100644 dist/branding/qtdesignstudio/qtcreator.xcassets/qtcreator-project.iconset/icon_512x512.png create mode 100644 dist/branding/qtdesignstudio/qtcreator.xcassets/qtcreator-project.iconset/icon_512x512@2x.png create mode 100644 dist/branding/qtdesignstudio/qtcreator.xcassets/qtcreator.appiconset/Contents.json create mode 100644 dist/branding/qtdesignstudio/qtcreator.xcassets/qtcreator.appiconset/icon_128x128.png create mode 100644 dist/branding/qtdesignstudio/qtcreator.xcassets/qtcreator.appiconset/icon_128x128@2x.png create mode 100644 dist/branding/qtdesignstudio/qtcreator.xcassets/qtcreator.appiconset/icon_16x16.png create mode 100644 dist/branding/qtdesignstudio/qtcreator.xcassets/qtcreator.appiconset/icon_16x16@2x.png create mode 100644 dist/branding/qtdesignstudio/qtcreator.xcassets/qtcreator.appiconset/icon_256x256.png create mode 100644 dist/branding/qtdesignstudio/qtcreator.xcassets/qtcreator.appiconset/icon_256x256@2x.png create mode 100644 dist/branding/qtdesignstudio/qtcreator.xcassets/qtcreator.appiconset/icon_32x32.png create mode 100644 dist/branding/qtdesignstudio/qtcreator.xcassets/qtcreator.appiconset/icon_32x32@2x.png create mode 100644 dist/branding/qtdesignstudio/qtcreator.xcassets/qtcreator.appiconset/icon_512x512.png create mode 100644 dist/branding/qtdesignstudio/qtcreator.xcassets/qtcreator.appiconset/icon_512x512@2x.png diff --git a/dist/branding/qtdesignstudio/CMakeLists.txt b/dist/branding/qtdesignstudio/CMakeLists.txt new file mode 100644 index 00000000000..7886ac3ef6d --- /dev/null +++ b/dist/branding/qtdesignstudio/CMakeLists.txt @@ -0,0 +1 @@ +install(DIRECTORY QtProject DESTINATION "${IDE_DATA_PATH}") diff --git a/dist/branding/qtdesignstudio/QtCreatorIDEBranding.cmake b/dist/branding/qtdesignstudio/QtCreatorIDEBranding.cmake new file mode 100644 index 00000000000..9b805bff125 --- /dev/null +++ b/dist/branding/qtdesignstudio/QtCreatorIDEBranding.cmake @@ -0,0 +1,18 @@ +set(IDE_VERSION "4.2.0") # The IDE version. +set(IDE_VERSION_COMPAT "4.2.0") # The IDE Compatibility version. +set(IDE_VERSION_DISPLAY "4.2.0") # The IDE display version. +set(IDE_COPYRIGHT_YEAR "2023") # The IDE current copyright year. + +set(IDE_SETTINGSVARIANT "QtProject") # The IDE settings variation. +set(IDE_COPY_SETTINGSVARIANT "Nokia") # The IDE settings to initially import. +set(IDE_DISPLAY_NAME "Qt Design Studio") # The IDE display name. +set(IDE_ID "qtdesignstudio") # The IDE id (no spaces, lowercase!) +set(IDE_CASED_ID "QtDesignStudio") # The cased IDE id (no spaces!) +set(IDE_BUNDLE_IDENTIFIER "org.qt-project.${IDE_ID}") # The macOS application bundle identifier. + +set(PROJECT_USER_FILE_EXTENSION .qtds) +set(IDE_DOC_FILE "qtdesignstudio/qtdesignstudio.qdocconf") +set(IDE_DOC_FILE_ONLINE "qtdesignstudio/qtdesignstudio-online.qdocconf") + +set(IDE_ICON_PATH "${CMAKE_CURRENT_LIST_DIR}") +set(IDE_LOGO_PATH "${CMAKE_CURRENT_LIST_DIR}") diff --git a/dist/branding/qtdesignstudio/QtProject/QtDesignStudio.ini b/dist/branding/qtdesignstudio/QtProject/QtDesignStudio.ini new file mode 100644 index 00000000000..4685e29f3bd --- /dev/null +++ b/dist/branding/qtdesignstudio/QtProject/QtDesignStudio.ini @@ -0,0 +1,64 @@ +[Plugins] +Ignored=AutoTest, Bazaar, ClangCodeModel, ClangTools, CMakeProjectManager, CVS, ClassView, CodePaster, CppEditor, CtfVisualizer, Designer, FakeVim, GLSLEditor, GenericProjectManager, IncrediBuild, Ios, Macros, Mercurial, ModelEditor, Perforce, PerfProfiler, ScxmlEditor, QbsProjectManager, Qnx, Subversion, Valgrind, VcsBase, Welcome, WinRt, Python +ForceEnabled=Boot2Qt, StudioWelcome, QmlDesigner, ModuleTools, McuSupport + +[Core] +NewDialog\LastCategory=H.StudioProject +NewDialog\LastPlatform=Desktop +NewDialog\ShowPlatformFilter=false +NewDialog\AllowAllTemplates=false +NewDialog\BlacklistedCategories=U.Java, R.Qt, T.Import, H.Project, U.General +NewDialog\AlternativeWizardStyle=true +CreatorTheme=design + +[Menu] +HideBuild=true +HideDebug=true +HideAnalyze=true +HideTools=true + +[QML] +Designer\StandAloneMode=true +Designer\EnableTimelineView=true +Designer\DisableItemLibraryUpdateTimer=false +Designer\NewWelcomePage=true +Designer\EnableWelcomePageDownload=true +Designer\OldStatesEditor=false +Designer\TopToolBar=true + +[Bauhaus] +LeftSideBar\Views=Library, Navigator +LeftSideBar\Visible=true +RightSideBar\VerticalPosition=@ByteArray(\0\0\0\xff\0\0\0\x1\0\0\0\x2\0\0\x2\xdf\0\0\0n\0\0\0\0\x1\x1\0\0\0\x2\0) +RightSideBar\Views=Properties, ConnectionView +RightSideBar\Visible=true + +[MainWindow] +ModeSelectorLayout=2 + +[OutputPanePlaceHolder] +CurrentIndex=2 + +[OutputPaneVisibility] +size=1 +1\id=QtCreator.Pane.CompileOutput +1\visible=false + +[Boot2Qt] +adbStartEnabled=false +flashActionDisabled=true + +[%General] +OverrideLanguage=C +[General] +HideOptionCategories=C++, Debug, Designer, Kits, BuildAndRun, CPaster, LanguageClient, Version Control + +[Help] +ContextHelpOption=3 +HomePage=qthelp://org.qt-project.qtdesignstudio.200/doc/studio-getting-started.html + +[ProjectExplorer] +Settings\ShowRunOutput=0 + +[KeyboardShortcutsV2] +QtCreator.ReturnToEditor= diff --git a/dist/branding/qtdesignstudio/QtProject/qtdesignstudio/EasingCurves.ini b/dist/branding/qtdesignstudio/QtProject/qtdesignstudio/EasingCurves.ini new file mode 100644 index 00000000000..4e7a5b3ab11 --- /dev/null +++ b/dist/branding/qtdesignstudio/QtProject/qtdesignstudio/EasingCurves.ini @@ -0,0 +1,2 @@ +[General] +EasingCurveList=@Variant(\0\0\0\x7f\0\0\0\x1eQmlDesigner::NamedEasingCurve\0\0\0\0\f\0l\0i\0n\0\x65\0\x61\0r-\0\0\0\0\0\0\0\0\x1?\xd3\x33\x33\x33\x33\x33\x33?\xf0\0\0\0\0\0\0?\xfb\x39\xab\xf3\x38qa\0\0\0\x3?\xc9\x99\x99\x99\x99\x99\x9a?\xc9\x99\x99\x99\x99\x99\x9a?\xe9\x99\x99\x99\x99\x99\x9a?\xe9\x99\x99\x99\x99\x99\x9a?\xf0\0\0\0\0\0\0?\xf0\0\0\0\0\0\0\0\0\0\0\0\0\0\0), "@Variant(\0\0\0\x7f\0\0\0\x1eQmlDesigner::NamedEasingCurve\0\0\0\0\x14\0\x65\0\x61\0s\0\x65\0I\0n\0S\0i\0n\0\x65-\0\0\0\0\0\0\0\0\x1?\xd3\x33\x33\x33\x33\x33\x33?\xf0\0\0\0\0\0\0?\xfb\x39\xab\xf3\x38qa\0\0\0\x3?\xde\x14z\xe1G\xae\x14\0\0\0\0\0\0\0\0?\xe7\xd7\n=p\xa3\xd7?\xe6\xe1G\xae\x14z\xe1?\xf0\0\0\0\0\0\0?\xf0\0\0\0\0\0\0\0\0\0\0\0\0\0\0)", @Variant(\0\0\0\x7f\0\0\0\x1eQmlDesigner::NamedEasingCurve\0\0\0\0\x16\0\x65\0\x61\0s\0\x65\0O\0u\0t\0S\0i\0n\0\x65-\0\0\0\0\0\0\0\0\x1?\xd3\x33\x33\x33\x33\x33\x33?\xf0\0\0\0\0\0\0?\xfb\x39\xab\xf3\x38qa\0\0\0\x3?\xd8\xf5\xc2\x8f\\(\xf6?\xe2\x66\x66\x66\x66\x66\x66?\xe2\x14z\xe1G\xae\x14?\xf0\0\0\0\0\0\0?\xf0\0\0\0\0\0\0?\xf0\0\0\0\0\0\0\0\0\0\0\0\0\0\0), @Variant(\0\0\0\x7f\0\0\0\x1eQmlDesigner::NamedEasingCurve\0\0\0\0\x1a\0\x65\0\x61\0s\0\x65\0I\0n\0O\0u\0t\0S\0i\0n\0\x65-\0\0\0\0\0\0\0\0\x1?\xd3\x33\x33\x33\x33\x33\x33?\xf0\0\0\0\0\0\0?\xfb\x39\xab\xf3\x38qa\0\0\0\x3?\xdcz\xe1G\xae\x14{?\xa9\x99\x99\x99\x99\x99\x9a?\xe1\x99\x99\x99\x99\x99\x9a?\xee\x66\x66\x66\x66\x66\x66?\xf0\0\0\0\0\0\0?\xf0\0\0\0\0\0\0\0\0\0\0\0\0\0\0), @Variant(\0\0\0\x7f\0\0\0\x1eQmlDesigner::NamedEasingCurve\0\0\0\0\x14\0\x65\0\x61\0s\0\x65\0I\0n\0Q\0u\0\x61\0\x64-\0\0\0\0\0\0\0\0\x1?\xd3\x33\x33\x33\x33\x33\x33?\xf0\0\0\0\0\0\0?\xfb\x39\xab\xf3\x38qa\0\0\0\x3?\xe1\x99\x99\x99\x99\x99\x9a?\xb5\xc2\x8f\\(\xf5\xc3?\xe5\xc2\x8f\\(\xf5\xc3?\xe0\xf5\xc2\x8f\\(\xf6?\xf0\0\0\0\0\0\0?\xf0\0\0\0\0\0\0\0\0\0\0\0\0\0\0), "@Variant(\0\0\0\x7f\0\0\0\x1eQmlDesigner::NamedEasingCurve\0\0\0\0\x16\0\x65\0\x61\0s\0\x65\0O\0u\0t\0Q\0u\0\x61\0\x64-\0\0\0\0\0\0\0\0\x1?\xd3\x33\x33\x33\x33\x33\x33?\xf0\0\0\0\0\0\0?\xfb\x39\xab\xf3\x38qa\0\0\0\x3?\xd0\0\0\0\0\0\0?\xddp\xa3\xd7\n=q?\xdc\xcc\xcc\xcc\xcc\xcc\xcd?\xee\x14z\xe1G\xae\x14?\xf0\0\0\0\0\0\0?\xf0\0\0\0\0\0\0\0\0\0\0\0\0\0\0)", @Variant(\0\0\0\x7f\0\0\0\x1eQmlDesigner::NamedEasingCurve\0\0\0\0\x1a\0\x65\0\x61\0s\0\x65\0I\0n\0O\0u\0t\0Q\0u\0\x61\0\x64-\0\0\0\0\0\0\0\0\x1?\xd3\x33\x33\x33\x33\x33\x33?\xf0\0\0\0\0\0\0?\xfb\x39\xab\xf3\x38qa\0\0\0\x3?\xdd\x1e\xb8Q\xeb\x85\x1f?\x9e\xb8Q\xeb\x85\x1e\xb8?\xe0z\xe1G\xae\x14{?\xee\x8f\\(\xf5\xc2\x8f?\xf0\0\0\0\0\0\0?\xf0\0\0\0\0\0\0\0\0\0\0\0\0\0\0), @Variant(\0\0\0\x7f\0\0\0\x1eQmlDesigner::NamedEasingCurve\0\0\0\0\x16\0\x65\0\x61\0s\0\x65\0I\0n\0\x43\0u\0\x62\0i\0\x63-\0\0\0\0\0\0\0\0\x1?\xd3\x33\x33\x33\x33\x33\x33?\xf0\0\0\0\0\0\0?\xfb\x39\xab\xf3\x38qa\0\0\0\x3?\xe1\x99\x99\x99\x99\x99\x9a?\xac(\xf5\xc2\x8f\\)?\xe5\x99\x99\x99\x99\x99\x9a?\xc8Q\xeb\x85\x1e\xb8R?\xf0\0\0\0\0\0\0?\xf0\0\0\0\0\0\0\0\0\0\0\0\0\0\0), @Variant(\0\0\0\x7f\0\0\0\x1eQmlDesigner::NamedEasingCurve\0\0\0\0\x18\0\x65\0\x61\0s\0\x65\0O\0u\0t\0\x43\0u\0\x62\0i\0\x63-\0\0\0\0\0\0\0\0\x1?\xd3\x33\x33\x33\x33\x33\x33?\xf0\0\0\0\0\0\0?\xfb\x39\xab\xf3\x38qa\0\0\0\x3?\xcb\x85\x1e\xb8Q\xeb\x85?\xe3\x85\x1e\xb8Q\xeb\x85?\xd6\xb8Q\xeb\x85\x1e\xb8?\xf0\0\0\0\0\0\0?\xf0\0\0\0\0\0\0?\xf0\0\0\0\0\0\0\0\0\0\0\0\0\0\0), "@Variant(\0\0\0\x7f\0\0\0\x1eQmlDesigner::NamedEasingCurve\0\0\0\0\x1c\0\x65\0\x61\0s\0\x65\0I\0n\0O\0u\0t\0\x43\0u\0\x62\0i\0\x63-\0\0\0\0\0\0\0\0\x1?\xd3\x33\x33\x33\x33\x33\x33?\xf0\0\0\0\0\0\0?\xfb\x39\xab\xf3\x38qa\0\0\0\x3?\xe4\xa3\xd7\n=p\xa4?\xa7\n=p\xa3\xd7\n?\xd6\xb8Q\xeb\x85\x1e\xb8?\xf0\0\0\0\0\0\0?\xf0\0\0\0\0\0\0?\xf0\0\0\0\0\0\0\0\0\0\0\0\0\0\0)", "@Variant(\0\0\0\x7f\0\0\0\x1eQmlDesigner::NamedEasingCurve\0\0\0\0\x16\0\x65\0\x61\0s\0\x65\0I\0n\0Q\0u\0\x61\0r\0t-\0\0\0\0\0\0\0\0\x1?\xd3\x33\x33\x33\x33\x33\x33?\xf0\0\0\0\0\0\0?\xfb\x39\xab\xf3\x38qa\0\0\0\x3?\xec\xa3\xd7\n=p\xa4?\x9e\xb8Q\xeb\x85\x1e\xb8?\xe5\xeb\x85\x1e\xb8Q\xec?\xcc(\xf5\xc2\x8f\\)?\xf0\0\0\0\0\0\0?\xf0\0\0\0\0\0\0\0\0\0\0\0\0\0\0)", @Variant(\0\0\0\x7f\0\0\0\x1eQmlDesigner::NamedEasingCurve\0\0\0\0\x18\0\x65\0\x61\0s\0\x65\0O\0u\0t\0Q\0u\0\x61\0r\0t-\0\0\0\0\0\0\0\0\x1?\xd3\x33\x33\x33\x33\x33\x33?\xf0\0\0\0\0\0\0?\xfb\x39\xab\xf3\x38qa\0\0\0\x3?\xc5\x1e\xb8Q\xeb\x85\x1f?\xea\xe1G\xae\x14z\xe1?\xdc(\xf5\xc2\x8f\\)?\xf0\0\0\0\0\0\0?\xf0\0\0\0\0\0\0?\xf0\0\0\0\0\0\0\0\0\0\0\0\0\0\0), "@Variant(\0\0\0\x7f\0\0\0\x1eQmlDesigner::NamedEasingCurve\0\0\0\0\x1c\0\x65\0\x61\0s\0\x65\0I\0n\0O\0u\0t\0Q\0u\0\x61\0r\0t-\0\0\0\0\0\0\0\0\x1?\xd3\x33\x33\x33\x33\x33\x33?\xf0\0\0\0\0\0\0?\xfb\x39\xab\xf3\x38qa\0\0\0\x3?\xe8\xa3\xd7\n=p\xa4\0\0\0\0\0\0\0\0?\xc6\x66\x66\x66\x66\x66\x66?\xf0\0\0\0\0\0\0?\xf0\0\0\0\0\0\0?\xf0\0\0\0\0\0\0\0\0\0\0\0\0\0\0)", @Variant(\0\0\0\x7f\0\0\0\x1eQmlDesigner::NamedEasingCurve\0\0\0\0\x16\0\x65\0\x61\0s\0\x65\0I\0n\0Q\0u\0i\0n\0t-\0\0\0\0\0\0\0\0\x1?\xd3\x33\x33\x33\x33\x33\x33?\xf0\0\0\0\0\0\0?\xfb\x39\xab\xf3\x38qa\0\0\0\x3?\xe8(\xf5\xc2\x8f\\)?\xa9\x99\x99\x99\x99\x99\x9a?\xeb\\(\xf5\xc2\x8f\\?\xae\xb8Q\xeb\x85\x1e\xb8?\xf0\0\0\0\0\0\0?\xf0\0\0\0\0\0\0\0\0\0\0\0\0\0\0), "@Variant(\0\0\0\x7f\0\0\0\x1eQmlDesigner::NamedEasingCurve\0\0\0\0\x18\0\x65\0\x61\0s\0\x65\0O\0u\0t\0Q\0u\0i\0n\0t-\0\0\0\0\0\0\0\0\x1?\xd3\x33\x33\x33\x33\x33\x33?\xf0\0\0\0\0\0\0?\xfb\x39\xab\xf3\x38qa\0\0\0\x3?\xcdp\xa3\xd7\n=q?\xf0\0\0\0\0\0\0?\xd4z\xe1G\xae\x14{?\xf0\0\0\0\0\0\0?\xf0\0\0\0\0\0\0?\xf0\0\0\0\0\0\0\0\0\0\0\0\0\0\0)", @Variant(\0\0\0\x7f\0\0\0\x1eQmlDesigner::NamedEasingCurve\0\0\0\0\x1c\0\x65\0\x61\0s\0\x65\0I\0n\0O\0u\0t\0Q\0u\0i\0n\0t-\0\0\0\0\0\0\0\0\x1?\xd3\x33\x33\x33\x33\x33\x33?\xf0\0\0\0\0\0\0?\xfb\x39\xab\xf3\x38qa\0\0\0\x3?\xeb\x85\x1e\xb8Q\xeb\x85\0\0\0\0\0\0\0\0?\xb1\xeb\x85\x1e\xb8Q\xec?\xf0\0\0\0\0\0\0?\xf0\0\0\0\0\0\0?\xf0\0\0\0\0\0\0\0\0\0\0\0\0\0\0), "@Variant(\0\0\0\x7f\0\0\0\x1eQmlDesigner::NamedEasingCurve\0\0\0\0\x14\0\x65\0\x61\0s\0\x65\0I\0n\0\x45\0x\0p\0o-\0\0\0\0\0\0\0\0\x1?\xd3\x33\x33\x33\x33\x33\x33?\xf0\0\0\0\0\0\0?\xfb\x39\xab\xf3\x38qa\0\0\0\x3?\xee\x66\x66\x66\x66\x66\x66?\xa9\x99\x99\x99\x99\x99\x9a?\xe9p\xa3\xd7\n=q?\xa1\xeb\x85\x1e\xb8Q\xec?\xf0\0\0\0\0\0\0?\xf0\0\0\0\0\0\0\0\0\0\0\0\0\0\0)", @Variant(\0\0\0\x7f\0\0\0\x1eQmlDesigner::NamedEasingCurve\0\0\0\0\x16\0\x65\0\x61\0s\0\x65\0O\0u\0t\0\x45\0x\0p\0o-\0\0\0\0\0\0\0\0\x1?\xd3\x33\x33\x33\x33\x33\x33?\xf0\0\0\0\0\0\0?\xfb\x39\xab\xf3\x38qa\0\0\0\x3?\xc8Q\xeb\x85\x1e\xb8R?\xf0\0\0\0\0\0\0?\xcc(\xf5\xc2\x8f\\)?\xf0\0\0\0\0\0\0?\xf0\0\0\0\0\0\0?\xf0\0\0\0\0\0\0\0\0\0\0\0\0\0\0), @Variant(\0\0\0\x7f\0\0\0\x1eQmlDesigner::NamedEasingCurve\0\0\0\0\x1a\0\x65\0\x61\0s\0\x65\0I\0n\0O\0u\0t\0\x45\0x\0p\0o-\0\0\0\0\0\0\0\0\x1?\xd3\x33\x33\x33\x33\x33\x33?\xf0\0\0\0\0\0\0?\xfb\x39\xab\xf3\x38qa\0\0\0\x3?\xf0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0?\xf0\0\0\0\0\0\0?\xf0\0\0\0\0\0\0?\xf0\0\0\0\0\0\0\0\0\0\0\0\0\0\0), "@Variant(\0\0\0\x7f\0\0\0\x1eQmlDesigner::NamedEasingCurve\0\0\0\0\x14\0\x65\0\x61\0s\0\x65\0I\0n\0\x43\0i\0r\0\x63-\0\0\0\0\0\0\0\0\x1?\xd3\x33\x33\x33\x33\x33\x33?\xf0\0\0\0\0\0\0?\xfb\x39\xab\xf3\x38qa\0\0\0\x3?\xe3\x33\x33\x33\x33\x33\x33?\xa4z\xe1G\xae\x14{?\xef\\(\xf5\xc2\x8f\\?\xd5p\xa3\xd7\n=q?\xf0\0\0\0\0\0\0?\xf0\0\0\0\0\0\0\0\0\0\0\0\0\0\0)", "@Variant(\0\0\0\x7f\0\0\0\x1eQmlDesigner::NamedEasingCurve\0\0\0\0\x16\0\x65\0\x61\0s\0\x65\0O\0u\0t\0\x43\0i\0r\0\x63-\0\0\0\0\0\0\0\0\x1?\xd3\x33\x33\x33\x33\x33\x33?\xf0\0\0\0\0\0\0?\xfb\x39\xab\xf3\x38qa\0\0\0\x3?\xb3\x33\x33\x33\x33\x33\x33?\xea=p\xa3\xd7\n=?\xc5\x1e\xb8Q\xeb\x85\x1f?\xf0\0\0\0\0\0\0?\xf0\0\0\0\0\0\0?\xf0\0\0\0\0\0\0\0\0\0\0\0\0\0\0)", @Variant(\0\0\0\x7f\0\0\0\x1eQmlDesigner::NamedEasingCurve\0\0\0\0\x1a\0\x65\0\x61\0s\0\x65\0I\0n\0O\0u\0t\0\x43\0i\0r\0\x63-\0\0\0\0\0\0\0\0\x1?\xd3\x33\x33\x33\x33\x33\x33?\xf0\0\0\0\0\0\0?\xfb\x39\xab\xf3\x38qa\0\0\0\x3?\xe9\x1e\xb8Q\xeb\x85\x1f?\xc1G\xae\x14z\xe1H?\xc3\x33\x33\x33\x33\x33\x33?\xeb\x85\x1e\xb8Q\xeb\x85?\xf0\0\0\0\0\0\0?\xf0\0\0\0\0\0\0\0\0\0\0\0\0\0\0), "@Variant(\0\0\0\x7f\0\0\0\x1eQmlDesigner::NamedEasingCurve\0\0\0\0\x14\0\x65\0\x61\0s\0\x65\0I\0n\0\x42\0\x61\0\x63\0k-\0\0\0\0\0\0\0\0\x1?\xd3\x33\x33\x33\x33\x33\x33?\xf0\0\0\0\0\0\0?\xfb\x39\xab\xf3\x38qa\0\0\0\x3?\xe3\x33\x33\x33\x33\x33\x33\xbf\xd1\xeb\x85\x1e\xb8Q\xec?\xe7\x85\x1e\xb8Q\xeb\x85?\xa7\n=p\xa3\xd7\n?\xf0\0\0\0\0\0\0?\xf0\0\0\0\0\0\0\0\0\0\0\0\0\0\0)", @Variant(\0\0\0\x7f\0\0\0\x1eQmlDesigner::NamedEasingCurve\0\0\0\0\x16\0\x65\0\x61\0s\0\x65\0O\0u\0t\0\x42\0\x61\0\x63\0k-\0\0\0\0\0\0\0\0\x1?\xd3\x33\x33\x33\x33\x33\x33?\xf0\0\0\0\0\0\0?\xfb\x39\xab\xf3\x38qa\0\0\0\x3?\xc6\x66\x66\x66\x66\x66\x66?\xecQ\xeb\x85\x1e\xb8R?\xd4z\xe1G\xae\x14{?\xf4Q\xeb\x85\x1e\xb8R?\xf0\0\0\0\0\0\0?\xf0\0\0\0\0\0\0\0\0\0\0\0\0\0\0), @Variant(\0\0\0\x7f\0\0\0\x1eQmlDesigner::NamedEasingCurve\0\0\0\0\x1a\0\x65\0\x61\0s\0\x65\0I\0n\0O\0u\0t\0\x42\0\x61\0\x63\0k-\0\0\0\0\0\0\0\0\x1?\xd3\x33\x33\x33\x33\x33\x33?\xf0\0\0\0\0\0\0?\xfb\x39\xab\xf3\x38qa\0\0\0\x3?\xe5\xc2\x8f\\(\xf5\xc3\xbf\xe1\x99\x99\x99\x99\x99\x9a?\xd0\xf5\xc2\x8f\\(\xf6?\xf8\xcc\xcc\xcc\xcc\xcc\xcd?\xf0\0\0\0\0\0\0?\xf0\0\0\0\0\0\0\0\0\0\0\0\0\0\0), "@Variant(\0\0\0\x7f\0\0\0\x1eQmlDesigner::NamedEasingCurve\0\0\0\0\x1a\0\x65\0\x61\0s\0\x65\0I\0n\0\x45\0l\0\x61\0s\0t\0i\0\x63-\0\0\0\0\0\0\0\0\x1?\xd3\x33\x33\x33\x33\x33\x33?\xf0\0\0\0\0\0\0?\xfb\x39\xab\xf3\x38qa\0\0\0\xf?\xd0r\xb0 \xc4\x9b\xa6\xbf~do\x15\x61\x91\x15?\xd6\x66\x66\x66\x66\x66\x66?\xac\x9e\xec\xbf\xb1[W?\xdcj~\xf9\xdb\"\xd1?\xaaxl\"h\t\xd5?\xe1\xeV\x4\x18\x93u?\xab\x98\xc7\xe2\x82@\xb8?\xe3\\(\xf5\xc2\x8f\\\xbf\x90\x96\xbb\x98\xc7\xe2\x82?\xe5\x6$\xdd/\x1a\xa0\xbf\x9b\xc0\x1a\x36\xe2\xeb\x1c?\xe7\xb6\x45\xa1\xca\xc0\x83\xbf\x88*\x99\x30\xbe\r\xed?\xe7t\xbcj~\xf9\xdb?\xbf\xbev\xc8\xb4\x39X?\xe8\xd4\xfd\xf3\xb6\x45\xa2?\xc5\x81\x6$\xdd/\x1b?\xea=p\xa3\xd7\n=?\xc0\xc4\x9b\xa5\xe3S\xf8?\xea\xf1\xa9\xfb\xe7l\x8b\xbf\xa6\xd5\xcf\xaa\xcd\x9e\x84?\xec(\xf5\xc2\x8f\\)\xbf\xb5\x81\x6$\xdd/\x1b?\xed/\x1a\x9f\xbev\xc9\xbf\xa4\x95\x18*\x99\x30\xbe?\xee\x8f\\(\xf5\xc2\x8f?\xdb\xa5\xe3S\xf7\xce\xd9?\xf0\0\0\0\0\0\0?\xf0\0\0\0\0\0\0\0\0\0\0\0\0\0\0)", "@Variant(\0\0\0\x7f\0\0\0\x1eQmlDesigner::NamedEasingCurve\0\0\0\0\x1c\0\x65\0\x61\0s\0\x65\0O\0u\0t\0\x45\0l\0\x61\0s\0t\0i\0\x63-\0\0\0\0\0\0\0\0\x1?\xd3\x33\x33\x33\x33\x33\x33?\xf0\0\0\0\0\0\0?\xfb\x39\xab\xf3\x38qa\0\0\0\xf?\xa3\xdd\x97\xf6+j\xe8?\xdc\xed\x91hr\xb0!?\xa0\x62M\xd2\xf1\xa9\xfc?\xf2\x66\x66\x66\x66\x66\x66?\xb0'RT`\xaa\x65?\xf3\x85\x1e\xb8Q\xeb\x85?\xbc\xac\b1&\xe9y?\xf2\x66\x66\x66\x66\x66\x66?\xc2\xd7\x93\xac\xbaY\x1e?\xec\x8c ]\x1a\x1\xf0?\xc8\xba\xe7\xa4\x89\x32\x35?\xeb}\xcaY\x1n{?\xce;\xed\xc9\x66\x61O?\xec\xf6\x9fV\xf5$\xc1?\xd3\x43\x95\x81\x6$\xdd?\xf1G\xae\x14z\xe1H?\xd7K\xc6\xa7\xef\x9d\xb2?\xf0\xa3\xd7\n=p\xa4?\xdc\xcc\xcc\xcc\xcc\xcc\xcd?\xee\x14z\xe1G\xae\x14?\xe4Z\x1c\xac\b1'?\xef\xe7l\x8b\x43\x95\x81?\xe5\xd2\xf1\xa9\xfb\xe7m?\xf0Q\xeb\x85\x1e\xb8R?\xe7\xdf;dZ\x1c\xac?\xf0\xa3\xd7\n=p\xa4?\xe8\xcc\xcc\xcc\xcc\xcc\xcd?\xef\xae\x14z\xe1G\xae?\xf0\0\0\0\0\0\0?\xf0\0\0\0\0\0\0\0\0\0\0\0\0\0\0)", "@Variant(\0\0\0\x7f\0\0\0\x1eQmlDesigner::NamedEasingCurve\0\0\0\0 \0\x65\0\x61\0s\0\x65\0I\0n\0O\0u\0t\0\x45\0l\0\x61\0s\0t\0i\0\x63-\0\0\0\0\0\0\0\0\x1?\xd3\x33\x33\x33\x33\x33\x33?\xf0\0\0\0\0\0\0?\xfb\x39\xab\xf3\x38qa\0\0\0\x15?\xacPH\x16\xf0\x6\x8e?\xa3\xb6\x45\xa1\xca\xc0\x83?\xb4`\xaa\x64\xc2\xf8\x38\xbf\xa3\x81\xd7\xdb\xf4\x87\xfd?\xbb\xe7l\x8b\x43\x95\x81\xbf\xa3\x81\xd7\xdb\xf4\x87\xfd?\xc1\xa9\xfb\xe7l\x8b\x44\xbf\xa3\x81\xd7\xdb\xf4\x87\xfd?\xc7\xae\x14z\xe1G\xae?\x9c\x43,\xa5zxl?\xccj~\xf9\xdb\"\xd1?l\xea\xf2Q\xc1\x93\xb4?\xd0\x93t\xbcj~\xfa\xbf\x94\xfd\xf3\xb6\x45\xa1\xcb?\xd5?|\xed\x91hs\xbf\xd0\0\0\0\0\0\0?\xd8\xa3\xd7\n=p\xa4\xbf\xd0 \xc4\x9b\xa5\xe3T?\xdd\x91hr\xb0 \xc5\xbf\xc1\xeb\x85\x1e\xb8Q\xec?\xe1`A\x89\x37K\xc7?\xf2\xe1G\xae\x14z\xe1?\xe3\xbev\xc8\xb4\x39X?\xf3\xd7\n=p\xa3\xd7?\xe5\xa1\xca\xc0\x83\x12o?\xf3\xd7\n=p\xa3\xd7?\xe7\x9d\xb2-\xeV\x4?\xf0\0\0\0\0\0\0?\xe8\xe5`A\x89\x37L?\xef\x43\x95\x81\x6$\xdd?\xea$\xdd/\x1a\x9f\xbe?\xee~\xf9\xdb\"\xd0\xe5?\xeb\\(\xf5\xc2\x8f\\?\xf0Q\xeb\x85\x1e\xb8R?\xecr\xb0 \xc4\x9b\xa6?\xf0z\xe1G\xae\x14{?\xed\x91hr\xb0 \xc5?\xf0z\xe1G\xae\x14{?\xeen\x97\x8dO\xdf;?\xee\xd9\x16\x87+\x2\f?\xf0\0\0\0\0\0\0?\xf0\0\0\0\0\0\0\0\0\0\0\0\0\0\0)", @Variant(\0\0\0\x7f\0\0\0\x1eQmlDesigner::NamedEasingCurve\0\0\0\0\x18\0\x65\0\x61\0s\0\x65\0I\0n\0\x42\0o\0u\0n\0\x63\0\x65-\0\0\0\0\0\0\0\0\x1?\xd3\x33\x33\x33\x33\x33\x33?\xf0\0\0\0\0\0\0?\xfb\x39\xab\xf3\x38qa\0\0\0\xf?\xb1\xe4\xf7\x65\xfd\x8a\xdb?U\xf4^\vN\x11\xdc?\xb5\xa1\xca\xc0\x83\x12o?\xb3\xf1\x41 [\xc0\x1a?\xbc\xac\b1&\xe9y?\xb3\xf1\x41 [\xc0\x1a?\xc1\xca\xc0\x83\x12n\x98?\xb3\xf1\x41 [\xc0\x1a?\xc7l\x8b\x43\x95\x81\x6\xbful\roTK\xb2?\xcc\xac\b1&\xe9y\xbful\roTK\xb2?\xd1\x6$\xdd/\x1a\xa0\xbful\roTK\xb2?\xd4\xdd/\x1a\x9f\xbew?\xd0\x31&\xe9x\xd4\xfe?\xd8\xc4\x9b\xa5\xe3S\xf8?\xd0\x31&\xe9x\xd4\xfe?\xdc\xbcj~\xf9\xdb#?\xd0\x31&\xe9x\xd4\xfe?\xdf|\xed\x91hr\xb0?U\xf4^\vN\x11\xdc?\xe3\x2\fI\xba^5\xbfh\xe7W\x92\x8e\f\x9e?\xe6\x45\xa1\xca\xc0\x83\x12\xbf~do\x15\x61\x91\x15?\xe8\x8b\x43\x95\x81\x6%?\xe9\x99\x99\x99\x99\x99\x9a?\xf0\0\0\0\0\0\0?\xf0\0\0\0\0\0\0\0\0\0\0\0\0\0\0), "@Variant(\0\0\0\x7f\0\0\0\x1eQmlDesigner::NamedEasingCurve\0\0\0\0\x1a\0\x65\0\x61\0s\0\x65\0O\0u\0t\0\x42\0o\0u\0n\0\x63\0\x65-\0\0\0\0\0\0\0\0\x1?\xd3\x33\x33\x33\x33\x33\x33?\xf0\0\0\0\0\0\0?\xfb\x39\xab\xf3\x38qa\0\0\0\xf?\xcd\xd2\xf1\xa9\xfb\xe7m?\xc4\x9b\xa5\xe3S\xf7\xcf?\xd0\xe5`A\x89\x37L?\xef\xe7l\x8b\x43\x95\x81?\xd9&\xe9x\xd4\xfd\xf4?\xef\xe7l\x8b\x43\x95\x81?\xe0\xb4\x39X\x10\x62N?\xef\xe7l\x8b\x43\x95\x81?\xe1\xc2\x8f\\(\xf5\xc3?\xe8\x10\x62M\xd2\xf1\xaa?\xe3\x85\x1e\xb8Q\xeb\x85?\xe8\0\0\0\0\0\0?\xe5?|\xed\x91hs?\xe7\xef\x9d\xb2-\xeV?\xe7\x8dO\xdf;dZ?\xf0\0\0\0\0\0\0?\xe8\xcc\xcc\xcc\xcc\xcc\xcd?\xf0\0\0\0\0\0\0?\xea\fI\xba^5??\xef\xf7\xce\xd9\x16\x87+?\xeb\x8dO\xdf;dZ?\xec\xd4\xfd\xf3\xb6\x45\xa2?\xecj~\xf9\xdb\"\xd1?\xec\xd4\xfd\xf3\xb6\x45\xa2?\xedO\xdf;dZ\x1d?\xec\xd4\xfd\xf3\xb6\x45\xa2?\xed\x89\x37K\xc6\xa7\xf0?\xef\xd7\n=p\xa3\xd7?\xf0\0\0\0\0\0\0?\xf0\0\0\0\0\0\0\0\0\0\0\0\0\0\0)", "@Variant(\0\0\0\x7f\0\0\0\x1eQmlDesigner::NamedEasingCurve\0\0\0\0\x1e\0\x65\0\x61\0s\0\x65\0I\0n\0O\0u\0t\0\x42\0o\0u\0n\0\x63\0\x65-\0\0\0\0\0\0\0\0\x1?\xd3\x33\x33\x33\x33\x33\x33?\xf0\0\0\0\0\0\0?\xfb\x39\xab\xf3\x38qa\0\0\0\x1e?\xa8\xd4\xfd\xf3\xb6\x45\xa2?wc^t)\x9d\x88?\xaeOv_\xd8\xad\xac?\xa5\xf6\xfd!\xff.I?\xb5\xdc\xc6?\x14\x12\x6?\xa7\x17X\xe2\x19\x65,?\xbcj~\xf9\xdb\"\xd1?\xa8\x37\xb4\xa2\x33\x9c\xf?\xbd/\x1a\x9f\xbev\xc9\xbfh\xe7W\x92\x8e\f\x9e?\xc3t\xbcj~\xf9\xdb\xbfh\xe7W\x92\x8e\f\x9e?\xc8Q\xeb\x85\x1e\xb8R\xbfh\xe7W\x92\x8e\f\x9e?\xcb\x64Z\x1c\xac\b1?\xbf|\xed\x91hr\xb0?\xcf|\xed\x91hr\xb0?\xbf\xbev\xc8\xb4\x39X?\xd1\xdb\"\xd0\xe5`B?\xc0\0\0\0\0\0\0?\xd3\x12n\x97\x8dO\xdf?l\xea\xf2Q\xc1\x93\xb4?\xd6\x87+\x2\fI\xba?l\xea\xf2Q\xc1\x93\xb4?\xd9\xfb\xe7l\x8b\x43\x96?l\xea\xf2Q\xc1\x93\xb4?\xdd\xeV\x4\x18\x93u?\xdd`A\x89\x37K\xc7?\xe0(\xf5\xc2\x8f\\)?\xdf\xdf;dZ\x1c\xac?\xe1\xca\xc0\x83\x12n\x98?\xe1/\x1a\x9f\xbev\xc9?\xe1\xb2-\xeV\x4\x19?\xf0\0\0\0\0\0\0?\xe4\0\0\0\0\0\0?\xf0\0\0\0\0\0\0?\xe6M\xd2\xf1\xa9\xfb\xe7?\xf0\0\0\0\0\0\0?\xe6\xf1\xa9\xfb\xe7l\x8b?\xeb\xd7\n=p\xa3\xd7?\xe7\xf7\xce\xd9\x16\x87+?\xeb\xd7\n=p\xa3\xd7?\xe8\xfd\xf3\xb6\x45\xa1\xcb?\xeb\xd7\n=p\xa3\xd7?\xea\x45\xa1\xca\xc0\x83\x12?\xef\xe7l\x8b\x43\x95\x81?\xeb\x85\x1e\xb8Q\xeb\x85?\xef\xe7l\x8b\x43\x95\x81?\xec\xc4\x9b\xa5\xe3S\xf8?\xef\xdf;dZ\x1c\xac?\xec\xc4\x9b\xa5\xe3S\xf8?\xed\xe3S\xf7\xce\xd9\x17?\xed\x89\x37K\xc6\xa7\xf0?\xed\xe3S\xf7\xce\xd9\x17?\xee\x45\xa1\xca\xc0\x83\x12?\xed\xe3S\xf7\xce\xd9\x17?\xee\xb0 \xc4\x9b\xa5\xe3?\xf0\0\0\0\0\0\0?\xf0\0\0\0\0\0\0?\xf0\0\0\0\0\0\0\0\0\0\0\0\0\0\0)" diff --git a/dist/branding/qtdesignstudio/images/logo/128/QtProject-qtcreator.png b/dist/branding/qtdesignstudio/images/logo/128/QtProject-qtcreator.png new file mode 100644 index 0000000000000000000000000000000000000000..c655d17b870331c1d5b226b2b3d34285f39e2185 GIT binary patch literal 5276 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 literal 0 HcmV?d00001 diff --git a/dist/branding/qtdesignstudio/images/logo/256/QtProject-qtcreator.png b/dist/branding/qtdesignstudio/images/logo/256/QtProject-qtcreator.png new file mode 100644 index 0000000000000000000000000000000000000000..d565981cdae7b075f7565da7325e893950f75799 GIT binary patch literal 10212 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